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