Home | History | Annotate | Download | only in base
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ftstream.c                                                             */
      4 /*                                                                         */
      5 /*    I/O stream support (body).                                           */
      6 /*                                                                         */
      7 /*  Copyright 2000-2002, 2004-2006, 2008-2011 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 = 0;
     43     stream->read   = 0;
     44     stream->close  = 0;
     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_Err_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_Err_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_Err_Invalid_Stream_Operation;
     97 
     98     return FT_Stream_Seek( stream, (FT_ULong)( stream->pos + distance ) );
     99   }
    100 
    101 
    102   FT_BASE_DEF( FT_Long )
    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_Err_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_Err_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 = 0;
    207       stream->limit  = 0;
    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 = 0;
    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_Err_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, count, &error );
    264       if ( error )
    265         goto Exit;
    266 #else
    267       if ( FT_QALLOC( stream->base, count ) )
    268         goto Exit;
    269 #endif
    270       /* read it */
    271       read_bytes = stream->read( stream, stream->pos,
    272                                  stream->base, count );
    273       if ( read_bytes < count )
    274       {
    275         FT_ERROR(( "FT_Stream_EnterFrame:"
    276                    " invalid read; expected %lu bytes, got %lu\n",
    277                    count, read_bytes ));
    278 
    279         FT_FREE( stream->base );
    280         error = FT_Err_Invalid_Stream_Operation;
    281       }
    282       stream->cursor = stream->base;
    283       stream->limit  = stream->cursor + count;
    284       stream->pos   += read_bytes;
    285     }
    286     else
    287     {
    288       /* check current and new position */
    289       if ( stream->pos >= stream->size        ||
    290            stream->size - stream->pos < count )
    291       {
    292         FT_ERROR(( "FT_Stream_EnterFrame:"
    293                    " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
    294                    stream->pos, count, stream->size ));
    295 
    296         error = FT_Err_Invalid_Stream_Operation;
    297         goto Exit;
    298       }
    299 
    300       /* set cursor */
    301       stream->cursor = stream->base + stream->pos;
    302       stream->limit  = stream->cursor + count;
    303       stream->pos   += count;
    304     }
    305 
    306   Exit:
    307     return error;
    308   }
    309 
    310 
    311   FT_BASE_DEF( void )
    312   FT_Stream_ExitFrame( FT_Stream  stream )
    313   {
    314     /* IMPORTANT: The assertion stream->cursor != 0 was removed, given    */
    315     /*            that it is possible to access a frame of length 0 in    */
    316     /*            some weird fonts (usually, when accessing an array of   */
    317     /*            0 records, like in some strange kern tables).           */
    318     /*                                                                    */
    319     /*  In this case, the loader code handles the 0-length table          */
    320     /*  gracefully; however, stream.cursor is really set to 0 by the      */
    321     /*  FT_Stream_EnterFrame() call, and this is not an error.            */
    322     /*                                                                    */
    323     FT_ASSERT( stream );
    324 
    325     if ( stream->read )
    326     {
    327       FT_Memory  memory = stream->memory;
    328 
    329 #ifdef FT_DEBUG_MEMORY
    330       ft_mem_free( memory, stream->base );
    331       stream->base = NULL;
    332 #else
    333       FT_FREE( stream->base );
    334 #endif
    335     }
    336     stream->cursor = 0;
    337     stream->limit  = 0;
    338   }
    339 
    340 
    341   FT_BASE_DEF( FT_Char )
    342   FT_Stream_GetChar( FT_Stream  stream )
    343   {
    344     FT_Char  result;
    345 
    346 
    347     FT_ASSERT( stream && stream->cursor );
    348 
    349     result = 0;
    350     if ( stream->cursor < stream->limit )
    351       result = *stream->cursor++;
    352 
    353     return result;
    354   }
    355 
    356 
    357   FT_BASE_DEF( FT_UShort )
    358   FT_Stream_GetUShort( FT_Stream  stream )
    359   {
    360     FT_Byte*  p;
    361     FT_Short  result;
    362 
    363 
    364     FT_ASSERT( stream && stream->cursor );
    365 
    366     result         = 0;
    367     p              = stream->cursor;
    368     if ( p + 1 < stream->limit )
    369       result       = FT_NEXT_USHORT( p );
    370     stream->cursor = p;
    371 
    372     return result;
    373   }
    374 
    375 
    376   FT_BASE_DEF( FT_UShort )
    377   FT_Stream_GetUShortLE( FT_Stream  stream )
    378   {
    379     FT_Byte*  p;
    380     FT_Short  result;
    381 
    382 
    383     FT_ASSERT( stream && stream->cursor );
    384 
    385     result         = 0;
    386     p              = stream->cursor;
    387     if ( p + 1 < stream->limit )
    388       result       = FT_NEXT_USHORT_LE( p );
    389     stream->cursor = p;
    390 
    391     return result;
    392   }
    393 
    394 
    395   FT_BASE_DEF( FT_ULong )
    396   FT_Stream_GetUOffset( FT_Stream  stream )
    397   {
    398     FT_Byte*  p;
    399     FT_Long   result;
    400 
    401 
    402     FT_ASSERT( stream && stream->cursor );
    403 
    404     result         = 0;
    405     p              = stream->cursor;
    406     if ( p + 2 < stream->limit )
    407       result       = FT_NEXT_UOFF3( p );
    408     stream->cursor = p;
    409     return result;
    410   }
    411 
    412 
    413   FT_BASE_DEF( FT_ULong )
    414   FT_Stream_GetULong( FT_Stream  stream )
    415   {
    416     FT_Byte*  p;
    417     FT_Long   result;
    418 
    419 
    420     FT_ASSERT( stream && stream->cursor );
    421 
    422     result         = 0;
    423     p              = stream->cursor;
    424     if ( p + 3 < stream->limit )
    425       result       = FT_NEXT_ULONG( p );
    426     stream->cursor = p;
    427     return result;
    428   }
    429 
    430 
    431   FT_BASE_DEF( FT_ULong )
    432   FT_Stream_GetULongLE( FT_Stream  stream )
    433   {
    434     FT_Byte*  p;
    435     FT_Long   result;
    436 
    437 
    438     FT_ASSERT( stream && stream->cursor );
    439 
    440     result         = 0;
    441     p              = stream->cursor;
    442     if ( p + 3 < stream->limit )
    443       result       = FT_NEXT_ULONG_LE( p );
    444     stream->cursor = p;
    445     return result;
    446   }
    447 
    448 
    449   FT_BASE_DEF( FT_Char )
    450   FT_Stream_ReadChar( FT_Stream  stream,
    451                       FT_Error*  error )
    452   {
    453     FT_Byte  result = 0;
    454 
    455 
    456     FT_ASSERT( stream );
    457 
    458     *error = FT_Err_Ok;
    459 
    460     if ( stream->read )
    461     {
    462       if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
    463         goto Fail;
    464     }
    465     else
    466     {
    467       if ( stream->pos < stream->size )
    468         result = stream->base[stream->pos];
    469       else
    470         goto Fail;
    471     }
    472     stream->pos++;
    473 
    474     return result;
    475 
    476   Fail:
    477     *error = FT_Err_Invalid_Stream_Operation;
    478     FT_ERROR(( "FT_Stream_ReadChar:"
    479                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    480                stream->pos, stream->size ));
    481 
    482     return 0;
    483   }
    484 
    485 
    486   FT_BASE_DEF( FT_UShort )
    487   FT_Stream_ReadUShort( FT_Stream  stream,
    488                        FT_Error*  error )
    489   {
    490     FT_Byte   reads[2];
    491     FT_Byte*  p = 0;
    492     FT_Short  result = 0;
    493 
    494 
    495     FT_ASSERT( stream );
    496 
    497     *error = FT_Err_Ok;
    498 
    499     if ( stream->pos + 1 < stream->size )
    500     {
    501       if ( stream->read )
    502       {
    503         if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
    504           goto Fail;
    505 
    506         p = reads;
    507       }
    508       else
    509       {
    510         p = stream->base + stream->pos;
    511       }
    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_Err_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_Short  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       {
    557         p = stream->base + stream->pos;
    558       }
    559 
    560       if ( p )
    561         result = FT_NEXT_USHORT_LE( p );
    562     }
    563     else
    564       goto Fail;
    565 
    566     stream->pos += 2;
    567 
    568     return result;
    569 
    570   Fail:
    571     *error = FT_Err_Invalid_Stream_Operation;
    572     FT_ERROR(( "FT_Stream_ReadUShortLE:"
    573                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    574                stream->pos, stream->size ));
    575 
    576     return 0;
    577   }
    578 
    579 
    580   FT_BASE_DEF( FT_ULong )
    581   FT_Stream_ReadUOffset( FT_Stream  stream,
    582                         FT_Error*  error )
    583   {
    584     FT_Byte   reads[3];
    585     FT_Byte*  p = 0;
    586     FT_Long   result = 0;
    587 
    588 
    589     FT_ASSERT( stream );
    590 
    591     *error = FT_Err_Ok;
    592 
    593     if ( stream->pos + 2 < stream->size )
    594     {
    595       if ( stream->read )
    596       {
    597         if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
    598           goto Fail;
    599 
    600         p = reads;
    601       }
    602       else
    603       {
    604         p = stream->base + stream->pos;
    605       }
    606 
    607       if ( p )
    608         result = FT_NEXT_UOFF3( p );
    609     }
    610     else
    611       goto Fail;
    612 
    613     stream->pos += 3;
    614 
    615     return result;
    616 
    617   Fail:
    618     *error = FT_Err_Invalid_Stream_Operation;
    619     FT_ERROR(( "FT_Stream_ReadUOffset:"
    620                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    621                stream->pos, stream->size ));
    622 
    623     return 0;
    624   }
    625 
    626 
    627   FT_BASE_DEF( FT_ULong )
    628   FT_Stream_ReadULong( FT_Stream  stream,
    629                       FT_Error*  error )
    630   {
    631     FT_Byte   reads[4];
    632     FT_Byte*  p = 0;
    633     FT_Long   result = 0;
    634 
    635 
    636     FT_ASSERT( stream );
    637 
    638     *error = FT_Err_Ok;
    639 
    640     if ( stream->pos + 3 < stream->size )
    641     {
    642       if ( stream->read )
    643       {
    644         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
    645           goto Fail;
    646 
    647         p = reads;
    648       }
    649       else
    650       {
    651         p = stream->base + stream->pos;
    652       }
    653 
    654       if ( p )
    655         result = FT_NEXT_ULONG( p );
    656     }
    657     else
    658       goto Fail;
    659 
    660     stream->pos += 4;
    661 
    662     return result;
    663 
    664   Fail:
    665     *error = FT_Err_Invalid_Stream_Operation;
    666     FT_ERROR(( "FT_Stream_ReadULong:"
    667                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    668                stream->pos, stream->size ));
    669 
    670     return 0;
    671   }
    672 
    673 
    674   FT_BASE_DEF( FT_ULong )
    675   FT_Stream_ReadULongLE( FT_Stream  stream,
    676                         FT_Error*  error )
    677   {
    678     FT_Byte   reads[4];
    679     FT_Byte*  p = 0;
    680     FT_Long   result = 0;
    681 
    682 
    683     FT_ASSERT( stream );
    684 
    685     *error = FT_Err_Ok;
    686 
    687     if ( stream->pos + 3 < stream->size )
    688     {
    689       if ( stream->read )
    690       {
    691         if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
    692           goto Fail;
    693 
    694         p = reads;
    695       }
    696       else
    697       {
    698         p = stream->base + stream->pos;
    699       }
    700 
    701       if ( p )
    702         result = FT_NEXT_ULONG_LE( p );
    703     }
    704     else
    705       goto Fail;
    706 
    707     stream->pos += 4;
    708 
    709     return result;
    710 
    711   Fail:
    712     *error = FT_Err_Invalid_Stream_Operation;
    713     FT_ERROR(( "FT_Stream_ReadULongLE:"
    714                " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
    715                stream->pos, stream->size ));
    716 
    717     return 0;
    718   }
    719 
    720 
    721   FT_BASE_DEF( FT_Error )
    722   FT_Stream_ReadFields( FT_Stream              stream,
    723                         const FT_Frame_Field*  fields,
    724                         void*                  structure )
    725   {
    726     FT_Error  error;
    727     FT_Bool   frame_accessed = 0;
    728     FT_Byte*  cursor;
    729 
    730     if ( !fields || !stream )
    731       return FT_Err_Invalid_Argument;
    732 
    733     cursor = stream->cursor;
    734 
    735     error = FT_Err_Ok;
    736     do
    737     {
    738       FT_ULong  value;
    739       FT_Int    sign_shift;
    740       FT_Byte*  p;
    741 
    742 
    743       switch ( fields->value )
    744       {
    745       case ft_frame_start:  /* access a new frame */
    746         error = FT_Stream_EnterFrame( stream, fields->offset );
    747         if ( error )
    748           goto Exit;
    749 
    750         frame_accessed = 1;
    751         cursor         = stream->cursor;
    752         fields++;
    753         continue;  /* loop! */
    754 
    755       case ft_frame_bytes:  /* read a byte sequence */
    756       case ft_frame_skip:   /* skip some bytes      */
    757         {
    758           FT_UInt  len = fields->size;
    759 
    760 
    761           if ( cursor + len > stream->limit )
    762           {
    763             error = FT_Err_Invalid_Stream_Operation;
    764             goto Exit;
    765           }
    766 
    767           if ( fields->value == ft_frame_bytes )
    768           {
    769             p = (FT_Byte*)structure + fields->offset;
    770             FT_MEM_COPY( p, cursor, len );
    771           }
    772           cursor += len;
    773           fields++;
    774           continue;
    775         }
    776 
    777       case ft_frame_byte:
    778       case ft_frame_schar:  /* read a single byte */
    779         value = FT_NEXT_BYTE(cursor);
    780         sign_shift = 24;
    781         break;
    782 
    783       case ft_frame_short_be:
    784       case ft_frame_ushort_be:  /* read a 2-byte big-endian short */
    785         value = FT_NEXT_USHORT(cursor);
    786         sign_shift = 16;
    787         break;
    788 
    789       case ft_frame_short_le:
    790       case ft_frame_ushort_le:  /* read a 2-byte little-endian short */
    791         value = FT_NEXT_USHORT_LE(cursor);
    792         sign_shift = 16;
    793         break;
    794 
    795       case ft_frame_long_be:
    796       case ft_frame_ulong_be:  /* read a 4-byte big-endian long */
    797         value = FT_NEXT_ULONG(cursor);
    798         sign_shift = 0;
    799         break;
    800 
    801       case ft_frame_long_le:
    802       case ft_frame_ulong_le:  /* read a 4-byte little-endian long */
    803         value = FT_NEXT_ULONG_LE(cursor);
    804         sign_shift = 0;
    805         break;
    806 
    807       case ft_frame_off3_be:
    808       case ft_frame_uoff3_be:  /* read a 3-byte big-endian long */
    809         value = FT_NEXT_UOFF3(cursor);
    810         sign_shift = 8;
    811         break;
    812 
    813       case ft_frame_off3_le:
    814       case ft_frame_uoff3_le:  /* read a 3-byte little-endian long */
    815         value = FT_NEXT_UOFF3_LE(cursor);
    816         sign_shift = 8;
    817         break;
    818 
    819       default:
    820         /* otherwise, exit the loop */
    821         stream->cursor = cursor;
    822         goto Exit;
    823       }
    824 
    825       /* now, compute the signed value is necessary */
    826       if ( fields->value & FT_FRAME_OP_SIGNED )
    827         value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
    828 
    829       /* finally, store the value in the object */
    830 
    831       p = (FT_Byte*)structure + fields->offset;
    832       switch ( fields->size )
    833       {
    834       case (8 / FT_CHAR_BIT):
    835         *(FT_Byte*)p = (FT_Byte)value;
    836         break;
    837 
    838       case (16 / FT_CHAR_BIT):
    839         *(FT_UShort*)p = (FT_UShort)value;
    840         break;
    841 
    842       case (32 / FT_CHAR_BIT):
    843         *(FT_UInt32*)p = (FT_UInt32)value;
    844         break;
    845 
    846       default:  /* for 64-bit systems */
    847         *(FT_ULong*)p = (FT_ULong)value;
    848       }
    849 
    850       /* go to next field */
    851       fields++;
    852     }
    853     while ( 1 );
    854 
    855   Exit:
    856     /* close the frame if it was opened by this read */
    857     if ( frame_accessed )
    858       FT_Stream_ExitFrame( stream );
    859 
    860     return error;
    861   }
    862 
    863 
    864 /* END */
    865