Home | History | Annotate | Download | only in base
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ftstream.c                                                             */
      4 /*                                                                         */
      5 /*    I/O stream support (body).                                           */
      6 /*                                                                         */
      7 /*  Copyright 2000-2018 by                                                 */
      8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
      9 /*                                                                         */
     10 /*  This file is part of the FreeType project, and may only be used,       */
     11 /*  modified, and distributed under the terms of the FreeType project      */
     12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     13 /*  this file you indicate that you have read the license and              */
     14 /*  understand and accept it fully.                                        */
     15 /*                                                                         */
     16 /***************************************************************************/
     17 
     18 
     19 #include <ft2build.h>
     20 #include FT_INTERNAL_STREAM_H
     21 #include FT_INTERNAL_DEBUG_H
     22 
     23 
     24   /*************************************************************************/
     25   /*                                                                       */
     26   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
     27   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
     28   /* messages during execution.                                            */
     29   /*                                                                       */
     30 #undef  FT_COMPONENT
     31 #define FT_COMPONENT  trace_stream
     32 
     33 
     34   FT_BASE_DEF( void )
     35   FT_Stream_OpenMemory( FT_Stream       stream,
     36                         const FT_Byte*  base,
     37                         FT_ULong        size )
     38   {
     39     stream->base   = (FT_Byte*) base;
     40     stream->size   = size;
     41     stream->pos    = 0;
     42     stream->cursor = NULL;
     43     stream->read   = NULL;
     44     stream->close  = NULL;
     45   }
     46 
     47 
     48   FT_BASE_DEF( void )
     49   FT_Stream_Close( FT_Stream  stream )
     50   {
     51     if ( stream && stream->close )
     52       stream->close( stream );
     53   }
     54 
     55 
     56   FT_BASE_DEF( FT_Error )
     57   FT_Stream_Seek( FT_Stream  stream,
     58                   FT_ULong   pos )
     59   {
     60     FT_Error  error = FT_Err_Ok;
     61 
     62 
     63     if ( stream->read )
     64     {
     65       if ( stream->read( stream, pos, 0, 0 ) )
     66       {
     67         FT_ERROR(( "FT_Stream_Seek:"
     68                    " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     69                    pos, stream->size ));
     70 
     71         error = FT_THROW( Invalid_Stream_Operation );
     72       }
     73     }
     74     /* note that seeking to the first position after the file is valid */
     75     else if ( pos > stream->size )
     76     {
     77       FT_ERROR(( "FT_Stream_Seek:"
     78                  " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     79                  pos, stream->size ));
     80 
     81       error = FT_THROW( Invalid_Stream_Operation );
     82     }
     83 
     84     if ( !error )
     85       stream->pos = pos;
     86 
     87     return error;
     88   }
     89 
     90 
     91   FT_BASE_DEF( FT_Error )
     92   FT_Stream_Skip( FT_Stream  stream,
     93                   FT_Long    distance )
     94   {
     95     if ( distance < 0 )
     96       return FT_THROW( Invalid_Stream_Operation );
     97 
     98     return FT_Stream_Seek( stream, stream->pos + (FT_ULong)distance );
     99   }
    100 
    101 
    102   FT_BASE_DEF( FT_ULong )
    103   FT_Stream_Pos( FT_Stream  stream )
    104   {
    105     return stream->pos;
    106   }
    107 
    108 
    109   FT_BASE_DEF( FT_Error )
    110   FT_Stream_Read( FT_Stream  stream,
    111                   FT_Byte*   buffer,
    112                   FT_ULong   count )
    113   {
    114     return FT_Stream_ReadAt( stream, stream->pos, buffer, count );
    115   }
    116 
    117 
    118   FT_BASE_DEF( FT_Error )
    119   FT_Stream_ReadAt( FT_Stream  stream,
    120                     FT_ULong   pos,
    121                     FT_Byte*   buffer,
    122                     FT_ULong   count )
    123   {
    124     FT_Error  error = FT_Err_Ok;
    125     FT_ULong  read_bytes;
    126 
    127 
    128     if ( pos >= stream->size )
    129     {
    130       FT_ERROR(( "FT_Stream_ReadAt:"
    131                  " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    132                  pos, stream->size ));
    133 
    134       return FT_THROW( Invalid_Stream_Operation );
    135     }
    136 
    137     if ( stream->read )
    138       read_bytes = stream->read( stream, pos, buffer, count );
    139     else
    140     {
    141       read_bytes = stream->size - pos;
    142       if ( read_bytes > count )
    143         read_bytes = count;
    144 
    145       FT_MEM_COPY( buffer, stream->base + pos, read_bytes );
    146     }
    147 
    148     stream->pos = pos + read_bytes;
    149 
    150     if ( read_bytes < count )
    151     {
    152       FT_ERROR(( "FT_Stream_ReadAt:"
    153                  " invalid read; expected %lu bytes, got %lu\n",
    154                  count, read_bytes ));
    155 
    156       error = FT_THROW( Invalid_Stream_Operation );
    157     }
    158 
    159     return error;
    160   }
    161 
    162 
    163   FT_BASE_DEF( FT_ULong )
    164   FT_Stream_TryRead( FT_Stream  stream,
    165                      FT_Byte*   buffer,
    166                      FT_ULong   count )
    167   {
    168     FT_ULong  read_bytes = 0;
    169 
    170 
    171     if ( stream->pos >= stream->size )
    172       goto Exit;
    173 
    174     if ( stream->read )
    175       read_bytes = stream->read( stream, stream->pos, buffer, count );
    176     else
    177     {
    178       read_bytes = stream->size - stream->pos;
    179       if ( read_bytes > count )
    180         read_bytes = count;
    181 
    182       FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes );
    183     }
    184 
    185     stream->pos += read_bytes;
    186 
    187   Exit:
    188     return read_bytes;
    189   }
    190 
    191 
    192   FT_BASE_DEF( FT_Error )
    193   FT_Stream_ExtractFrame( FT_Stream  stream,
    194                           FT_ULong   count,
    195                           FT_Byte**  pbytes )
    196   {
    197     FT_Error  error;
    198 
    199 
    200     error = FT_Stream_EnterFrame( stream, count );
    201     if ( !error )
    202     {
    203       *pbytes = (FT_Byte*)stream->cursor;
    204 
    205       /* equivalent to FT_Stream_ExitFrame(), with no memory block release */
    206       stream->cursor = NULL;
    207       stream->limit  = NULL;
    208     }
    209 
    210     return error;
    211   }
    212 
    213 
    214   FT_BASE_DEF( void )
    215   FT_Stream_ReleaseFrame( FT_Stream  stream,
    216                           FT_Byte**  pbytes )
    217   {
    218     if ( stream && stream->read )
    219     {
    220       FT_Memory  memory = stream->memory;
    221 
    222 #ifdef FT_DEBUG_MEMORY
    223       ft_mem_free( memory, *pbytes );
    224       *pbytes = NULL;
    225 #else
    226       FT_FREE( *pbytes );
    227 #endif
    228     }
    229     *pbytes = NULL;
    230   }
    231 
    232 
    233   FT_BASE_DEF( FT_Error )
    234   FT_Stream_EnterFrame( FT_Stream  stream,
    235                         FT_ULong   count )
    236   {
    237     FT_Error  error = FT_Err_Ok;
    238     FT_ULong  read_bytes;
    239 
    240 
    241     /* check for nested frame access */
    242     FT_ASSERT( stream && stream->cursor == 0 );
    243 
    244     if ( stream->read )
    245     {
    246       /* allocate the frame in memory */
    247       FT_Memory  memory = stream->memory;
    248 
    249 
    250       /* simple sanity check */
    251       if ( count > stream->size )
    252       {
    253         FT_ERROR(( "FT_Stream_EnterFrame:"
    254                    " frame size (%lu) larger than stream size (%lu)\n",
    255                    count, stream->size ));
    256 
    257         error = FT_THROW( Invalid_Stream_Operation );
    258         goto Exit;
    259       }
    260 
    261 #ifdef FT_DEBUG_MEMORY
    262       /* assume _ft_debug_file and _ft_debug_lineno are already set */
    263       stream->base = (unsigned char*)ft_mem_qalloc( memory,
    264                                                     (FT_Long)count,
    265                                                     &error );
    266       if ( error )
    267         goto Exit;
    268 #else
    269       if ( FT_QALLOC( stream->base, count ) )
    270         goto Exit;
    271 #endif
    272       /* read it */
    273       read_bytes = stream->read( stream, stream->pos,
    274                                  stream->base, count );
    275       if ( read_bytes < count )
    276       {
    277         FT_ERROR(( "FT_Stream_EnterFrame:"
    278                    " invalid read; expected %lu bytes, got %lu\n",
    279                    count, read_bytes ));
    280 
    281         FT_FREE( stream->base );
    282         error = FT_THROW( Invalid_Stream_Operation );
    283       }
    284       stream->cursor = stream->base;
    285       stream->limit  = stream->cursor + count;
    286       stream->pos   += read_bytes;
    287     }
    288     else
    289     {
    290       /* check current and new position */
    291       if ( stream->pos >= stream->size        ||
    292            stream->size - stream->pos < count )
    293       {
    294         FT_ERROR(( "FT_Stream_EnterFrame:"
    295                    " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
    296                    stream->pos, count, stream->size ));
    297 
    298         error = FT_THROW( Invalid_Stream_Operation );
    299         goto Exit;
    300       }
    301 
    302       /* set cursor */
    303       stream->cursor = stream->base + stream->pos;
    304       stream->limit  = stream->cursor + count;
    305       stream->pos   += count;
    306     }
    307 
    308   Exit:
    309     return error;
    310   }
    311 
    312 
    313   FT_BASE_DEF( void )
    314   FT_Stream_ExitFrame( FT_Stream  stream )
    315   {
    316     /* IMPORTANT: The assertion stream->cursor != 0 was removed, given    */
    317     /*            that it is possible to access a frame of length 0 in    */
    318     /*            some weird fonts (usually, when accessing an array of   */
    319     /*            0 records, like in some strange kern tables).           */
    320     /*                                                                    */
    321     /*  In this case, the loader code handles the 0-length table          */
    322     /*  gracefully; however, stream.cursor is really set to 0 by the      */
    323     /*  FT_Stream_EnterFrame() call, and this is not an error.            */
    324     /*                                                                    */
    325     FT_ASSERT( stream );
    326 
    327     if ( stream->read )
    328     {
    329       FT_Memory  memory = stream->memory;
    330 
    331 #ifdef FT_DEBUG_MEMORY
    332       ft_mem_free( memory, stream->base );
    333       stream->base = NULL;
    334 #else
    335       FT_FREE( stream->base );
    336 #endif
    337     }
    338     stream->cursor = NULL;
    339     stream->limit  = NULL;
    340   }
    341 
    342 
    343   FT_BASE_DEF( FT_Char )
    344   FT_Stream_GetChar( FT_Stream  stream )
    345   {
    346     FT_Char  result;
    347 
    348 
    349     FT_ASSERT( stream && stream->cursor );
    350 
    351     result = 0;
    352     if ( stream->cursor < stream->limit )
    353       result = (FT_Char)*stream->cursor++;
    354 
    355     return result;
    356   }
    357 
    358 
    359   FT_BASE_DEF( FT_UShort )
    360   FT_Stream_GetUShort( FT_Stream  stream )
    361   {
    362     FT_Byte*   p;
    363     FT_UShort  result;
    364 
    365 
    366     FT_ASSERT( stream && stream->cursor );
    367 
    368     result         = 0;
    369     p              = stream->cursor;
    370     if ( p + 1 < stream->limit )
    371       result       = FT_NEXT_USHORT( p );
    372     stream->cursor = p;
    373 
    374     return result;
    375   }
    376 
    377 
    378   FT_BASE_DEF( FT_UShort )
    379   FT_Stream_GetUShortLE( FT_Stream  stream )
    380   {
    381     FT_Byte*   p;
    382     FT_UShort  result;
    383 
    384 
    385     FT_ASSERT( stream && stream->cursor );
    386 
    387     result         = 0;
    388     p              = stream->cursor;
    389     if ( p + 1 < stream->limit )
    390       result       = FT_NEXT_USHORT_LE( p );
    391     stream->cursor = p;
    392 
    393     return result;
    394   }
    395 
    396 
    397   FT_BASE_DEF( FT_ULong )
    398   FT_Stream_GetUOffset( FT_Stream  stream )
    399   {
    400     FT_Byte*  p;
    401     FT_ULong  result;
    402 
    403 
    404     FT_ASSERT( stream && stream->cursor );
    405 
    406     result         = 0;
    407     p              = stream->cursor;
    408     if ( p + 2 < stream->limit )
    409       result       = FT_NEXT_UOFF3( p );
    410     stream->cursor = p;
    411     return result;
    412   }
    413 
    414 
    415   FT_BASE_DEF( FT_ULong )
    416   FT_Stream_GetULong( FT_Stream  stream )
    417   {
    418     FT_Byte*  p;
    419     FT_ULong  result;
    420 
    421 
    422     FT_ASSERT( stream && stream->cursor );
    423 
    424     result         = 0;
    425     p              = stream->cursor;
    426     if ( p + 3 < stream->limit )
    427       result       = FT_NEXT_ULONG( p );
    428     stream->cursor = p;
    429     return result;
    430   }
    431 
    432 
    433   FT_BASE_DEF( FT_ULong )
    434   FT_Stream_GetULongLE( FT_Stream  stream )
    435   {
    436     FT_Byte*  p;
    437     FT_ULong  result;
    438 
    439 
    440     FT_ASSERT( stream && stream->cursor );
    441 
    442     result         = 0;
    443     p              = stream->cursor;
    444     if ( p + 3 < stream->limit )
    445       result       = FT_NEXT_ULONG_LE( p );
    446     stream->cursor = p;
    447     return result;
    448   }
    449 
    450 
    451   FT_BASE_DEF( FT_Char )
    452   FT_Stream_ReadChar( FT_Stream  stream,
    453                       FT_Error*  error )
    454   {
    455     FT_Byte  result = 0;
    456 
    457 
    458     FT_ASSERT( stream );
    459 
    460     *error = FT_Err_Ok;
    461 
    462     if ( stream->read )
    463     {
    464       if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
    465         goto Fail;
    466     }
    467     else
    468     {
    469       if ( stream->pos < stream->size )
    470         result = stream->base[stream->pos];
    471       else
    472         goto Fail;
    473     }
    474     stream->pos++;
    475 
    476     return (FT_Char)result;
    477 
    478   Fail:
    479     *error = FT_THROW( Invalid_Stream_Operation );
    480     FT_ERROR(( "FT_Stream_ReadChar:"
    481                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    482                stream->pos, stream->size ));
    483 
    484     return 0;
    485   }
    486 
    487 
    488   FT_BASE_DEF( FT_UShort )
    489   FT_Stream_ReadUShort( FT_Stream  stream,
    490                         FT_Error*  error )
    491   {
    492     FT_Byte    reads[2];
    493     FT_Byte*   p      = 0;
    494     FT_UShort  result = 0;
    495 
    496 
    497     FT_ASSERT( stream );
    498 
    499     *error = FT_Err_Ok;
    500 
    501     if ( stream->pos + 1 < stream->size )
    502     {
    503       if ( stream->read )
    504       {
    505         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
    506           goto Fail;
    507 
    508         p = reads;
    509       }
    510       else
    511         p = stream->base + stream->pos;
    512 
    513       if ( p )
    514         result = FT_NEXT_USHORT( p );
    515     }
    516     else
    517       goto Fail;
    518 
    519     stream->pos += 2;
    520 
    521     return result;
    522 
    523   Fail:
    524     *error = FT_THROW( Invalid_Stream_Operation );
    525     FT_ERROR(( "FT_Stream_ReadUShort:"
    526                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    527                stream->pos, stream->size ));
    528 
    529     return 0;
    530   }
    531 
    532 
    533   FT_BASE_DEF( FT_UShort )
    534   FT_Stream_ReadUShortLE( FT_Stream  stream,
    535                           FT_Error*  error )
    536   {
    537     FT_Byte    reads[2];
    538     FT_Byte*   p      = 0;
    539     FT_UShort  result = 0;
    540 
    541 
    542     FT_ASSERT( stream );
    543 
    544     *error = FT_Err_Ok;
    545 
    546     if ( stream->pos + 1 < stream->size )
    547     {
    548       if ( stream->read )
    549       {
    550         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
    551           goto Fail;
    552 
    553         p = reads;
    554       }
    555       else
    556         p = stream->base + stream->pos;
    557 
    558       if ( p )
    559         result = FT_NEXT_USHORT_LE( p );
    560     }
    561     else
    562       goto Fail;
    563 
    564     stream->pos += 2;
    565 
    566     return result;
    567 
    568   Fail:
    569     *error = FT_THROW( Invalid_Stream_Operation );
    570     FT_ERROR(( "FT_Stream_ReadUShortLE:"
    571                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    572                stream->pos, stream->size ));
    573 
    574     return 0;
    575   }
    576 
    577 
    578   FT_BASE_DEF( FT_ULong )
    579   FT_Stream_ReadUOffset( FT_Stream  stream,
    580                          FT_Error*  error )
    581   {
    582     FT_Byte   reads[3];
    583     FT_Byte*  p      = 0;
    584     FT_ULong  result = 0;
    585 
    586 
    587     FT_ASSERT( stream );
    588 
    589     *error = FT_Err_Ok;
    590 
    591     if ( stream->pos + 2 < stream->size )
    592     {
    593       if ( stream->read )
    594       {
    595         if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
    596           goto Fail;
    597 
    598         p = reads;
    599       }
    600       else
    601         p = stream->base + stream->pos;
    602 
    603       if ( p )
    604         result = FT_NEXT_UOFF3( p );
    605     }
    606     else
    607       goto Fail;
    608 
    609     stream->pos += 3;
    610 
    611     return result;
    612 
    613   Fail:
    614     *error = FT_THROW( Invalid_Stream_Operation );
    615     FT_ERROR(( "FT_Stream_ReadUOffset:"
    616                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    617                stream->pos, stream->size ));
    618 
    619     return 0;
    620   }
    621 
    622 
    623   FT_BASE_DEF( FT_ULong )
    624   FT_Stream_ReadULong( FT_Stream  stream,
    625                        FT_Error*  error )
    626   {
    627     FT_Byte   reads[4];
    628     FT_Byte*  p      = 0;
    629     FT_ULong  result = 0;
    630 
    631 
    632     FT_ASSERT( stream );
    633 
    634     *error = FT_Err_Ok;
    635 
    636     if ( stream->pos + 3 < stream->size )
    637     {
    638       if ( stream->read )
    639       {
    640         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
    641           goto Fail;
    642 
    643         p = reads;
    644       }
    645       else
    646         p = stream->base + stream->pos;
    647 
    648       if ( p )
    649         result = FT_NEXT_ULONG( p );
    650     }
    651     else
    652       goto Fail;
    653 
    654     stream->pos += 4;
    655 
    656     return result;
    657 
    658   Fail:
    659     *error = FT_THROW( Invalid_Stream_Operation );
    660     FT_ERROR(( "FT_Stream_ReadULong:"
    661                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    662                stream->pos, stream->size ));
    663 
    664     return 0;
    665   }
    666 
    667 
    668   FT_BASE_DEF( FT_ULong )
    669   FT_Stream_ReadULongLE( FT_Stream  stream,
    670                          FT_Error*  error )
    671   {
    672     FT_Byte   reads[4];
    673     FT_Byte*  p      = 0;
    674     FT_ULong  result = 0;
    675 
    676 
    677     FT_ASSERT( stream );
    678 
    679     *error = FT_Err_Ok;
    680 
    681     if ( stream->pos + 3 < stream->size )
    682     {
    683       if ( stream->read )
    684       {
    685         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
    686           goto Fail;
    687 
    688         p = reads;
    689       }
    690       else
    691         p = stream->base + stream->pos;
    692 
    693       if ( p )
    694         result = FT_NEXT_ULONG_LE( p );
    695     }
    696     else
    697       goto Fail;
    698 
    699     stream->pos += 4;
    700 
    701     return result;
    702 
    703   Fail:
    704     *error = FT_THROW( Invalid_Stream_Operation );
    705     FT_ERROR(( "FT_Stream_ReadULongLE:"
    706                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    707                stream->pos, stream->size ));
    708 
    709     return 0;
    710   }
    711 
    712 
    713   FT_BASE_DEF( FT_Error )
    714   FT_Stream_ReadFields( FT_Stream              stream,
    715                         const FT_Frame_Field*  fields,
    716                         void*                  structure )
    717   {
    718     FT_Error  error;
    719     FT_Bool   frame_accessed = 0;
    720     FT_Byte*  cursor;
    721 
    722 
    723     if ( !fields )
    724       return FT_THROW( Invalid_Argument );
    725 
    726     if ( !stream )
    727       return FT_THROW( Invalid_Stream_Handle );
    728 
    729     cursor = stream->cursor;
    730 
    731     error = FT_Err_Ok;
    732     do
    733     {
    734       FT_ULong  value;
    735       FT_Int    sign_shift;
    736       FT_Byte*  p;
    737 
    738 
    739       switch ( fields->value )
    740       {
    741       case ft_frame_start:  /* access a new frame */
    742         error = FT_Stream_EnterFrame( stream, fields->offset );
    743         if ( error )
    744           goto Exit;
    745 
    746         frame_accessed = 1;
    747         cursor         = stream->cursor;
    748         fields++;
    749         continue;  /* loop! */
    750 
    751       case ft_frame_bytes:  /* read a byte sequence */
    752       case ft_frame_skip:   /* skip some bytes      */
    753         {
    754           FT_UInt  len = fields->size;
    755 
    756 
    757           if ( cursor + len > stream->limit )
    758           {
    759             error = FT_THROW( Invalid_Stream_Operation );
    760             goto Exit;
    761           }
    762 
    763           if ( fields->value == ft_frame_bytes )
    764           {
    765             p = (FT_Byte*)structure + fields->offset;
    766             FT_MEM_COPY( p, cursor, len );
    767           }
    768           cursor += len;
    769           fields++;
    770           continue;
    771         }
    772 
    773       case ft_frame_byte:
    774       case ft_frame_schar:  /* read a single byte */
    775         value = FT_NEXT_BYTE( cursor );
    776         sign_shift = 24;
    777         break;
    778 
    779       case ft_frame_short_be:
    780       case ft_frame_ushort_be:  /* read a 2-byte big-endian short */
    781         value = FT_NEXT_USHORT( cursor );
    782         sign_shift = 16;
    783         break;
    784 
    785       case ft_frame_short_le:
    786       case ft_frame_ushort_le:  /* read a 2-byte little-endian short */
    787         value = FT_NEXT_USHORT_LE( cursor );
    788         sign_shift = 16;
    789         break;
    790 
    791       case ft_frame_long_be:
    792       case ft_frame_ulong_be:  /* read a 4-byte big-endian long */
    793         value = FT_NEXT_ULONG( cursor );
    794         sign_shift = 0;
    795         break;
    796 
    797       case ft_frame_long_le:
    798       case ft_frame_ulong_le:  /* read a 4-byte little-endian long */
    799         value = FT_NEXT_ULONG_LE( cursor );
    800         sign_shift = 0;
    801         break;
    802 
    803       case ft_frame_off3_be:
    804       case ft_frame_uoff3_be:  /* read a 3-byte big-endian long */
    805         value = FT_NEXT_UOFF3( cursor );
    806         sign_shift = 8;
    807         break;
    808 
    809       case ft_frame_off3_le:
    810       case ft_frame_uoff3_le:  /* read a 3-byte little-endian long */
    811         value = FT_NEXT_UOFF3_LE( cursor );
    812         sign_shift = 8;
    813         break;
    814 
    815       default:
    816         /* otherwise, exit the loop */
    817         stream->cursor = cursor;
    818         goto Exit;
    819       }
    820 
    821       /* now, compute the signed value is necessary */
    822       if ( fields->value & FT_FRAME_OP_SIGNED )
    823         value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
    824 
    825       /* finally, store the value in the object */
    826 
    827       p = (FT_Byte*)structure + fields->offset;
    828       switch ( fields->size )
    829       {
    830       case ( 8 / FT_CHAR_BIT ):
    831         *(FT_Byte*)p = (FT_Byte)value;
    832         break;
    833 
    834       case ( 16 / FT_CHAR_BIT ):
    835         *(FT_UShort*)p = (FT_UShort)value;
    836         break;
    837 
    838       case ( 32 / FT_CHAR_BIT ):
    839         *(FT_UInt32*)p = (FT_UInt32)value;
    840         break;
    841 
    842       default:  /* for 64-bit systems */
    843         *(FT_ULong*)p = (FT_ULong)value;
    844       }
    845 
    846       /* go to next field */
    847       fields++;
    848     }
    849     while ( 1 );
    850 
    851   Exit:
    852     /* close the frame if it was opened by this read */
    853     if ( frame_accessed )
    854       FT_Stream_ExitFrame( stream );
    855 
    856     return error;
    857   }
    858 
    859 
    860 /* END */
    861