xref: /aosp_15_r20/external/freetype/src/base/ftstream.c (revision 63949dbd25bcc50c4e1178497ff9e9574d44fc5a)
1 /****************************************************************************
2  *
3  * ftstream.c
4  *
5  *   I/O stream support (body).
6  *
7  * Copyright (C) 2000-2023 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 )
FT_Stream_OpenMemory(FT_Stream stream,const FT_Byte * base,FT_ULong size)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 )
FT_Stream_Close(FT_Stream stream)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 )
FT_Stream_Seek(FT_Stream stream,FT_ULong pos)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 )
FT_Stream_Skip(FT_Stream stream,FT_Long distance)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 )
FT_Stream_Pos(FT_Stream stream)102   FT_Stream_Pos( FT_Stream  stream )
103   {
104     return stream->pos;
105   }
106 
107 
108   FT_BASE_DEF( FT_Error )
FT_Stream_Read(FT_Stream stream,FT_Byte * buffer,FT_ULong count)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 )
FT_Stream_ReadAt(FT_Stream stream,FT_ULong pos,FT_Byte * buffer,FT_ULong count)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 )
FT_Stream_TryRead(FT_Stream stream,FT_Byte * buffer,FT_ULong count)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 )
FT_Stream_ExtractFrame(FT_Stream stream,FT_ULong count,FT_Byte ** pbytes)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 )
FT_Stream_ReleaseFrame(FT_Stream stream,FT_Byte ** pbytes)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 )
FT_Stream_EnterFrame(FT_Stream stream,FT_ULong count)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: %ld 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 )
FT_Stream_ExitFrame(FT_Stream stream)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 )
FT_Stream_GetByte(FT_Stream stream)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 )
FT_Stream_GetUShort(FT_Stream stream)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 )
FT_Stream_GetUShortLE(FT_Stream stream)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 )
FT_Stream_GetUOffset(FT_Stream stream)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 )
FT_Stream_GetULong(FT_Stream stream)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 )
FT_Stream_GetULongLE(FT_Stream stream)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 )
FT_Stream_ReadByte(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadUShort(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadUShortLE(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadUOffset(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadULong(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadULongLE(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadFields(FT_Stream stream,const FT_Frame_Field * fields,void * structure)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_UInt  len = fields->size;
767 
768 
769           if ( cursor + len > stream->limit )
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 is 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 */
873