tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ftstream.c (19478B)


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