xref: /aosp_15_r20/external/freetype/src/bzip2/ftbzip2.c (revision 63949dbd25bcc50c4e1178497ff9e9574d44fc5a)
1 /****************************************************************************
2  *
3  * ftbzip2.c
4  *
5  *   FreeType support for .bz2 compressed files.
6  *
7  * This optional component relies on libbz2.  It should mainly be used to
8  * parse compressed PCF fonts, as found with many X11 server
9  * distributions.
10  *
11  * Copyright (C) 2010-2023 by
12  * Joel Klinghed.
13  *
14  * based on `src/gzip/ftgzip.c'
15  *
16  * This file is part of the FreeType project, and may only be used,
17  * modified, and distributed under the terms of the FreeType project
18  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
19  * this file you indicate that you have read the license and
20  * understand and accept it fully.
21  *
22  */
23 
24 
25 #include <freetype/internal/ftmemory.h>
26 #include <freetype/internal/ftstream.h>
27 #include <freetype/internal/ftdebug.h>
28 #include <freetype/ftbzip2.h>
29 #include FT_CONFIG_STANDARD_LIBRARY_H
30 
31 
32 #include <freetype/ftmoderr.h>
33 
34 #undef FTERRORS_H_
35 
36 #undef  FT_ERR_PREFIX
37 #define FT_ERR_PREFIX  Bzip2_Err_
38 #define FT_ERR_BASE    FT_Mod_Err_Bzip2
39 
40 #include <freetype/fterrors.h>
41 
42 
43 #ifdef FT_CONFIG_OPTION_USE_BZIP2
44 
45 #define BZ_NO_STDIO /* Do not need FILE */
46 #include <bzlib.h>
47 
48 
49 /***************************************************************************/
50 /***************************************************************************/
51 /*****                                                                 *****/
52 /*****           B Z I P 2   M E M O R Y   M A N A G E M E N T         *****/
53 /*****                                                                 *****/
54 /***************************************************************************/
55 /***************************************************************************/
56 
57   /* it is better to use FreeType memory routines instead of raw
58      'malloc/free' */
59 
60   typedef void* (*alloc_func)( void*, int, int );
61   typedef void  (*free_func) ( void*, void* );
62 
63 
64   static void*
ft_bzip2_alloc(void * memory_,int items,int size)65   ft_bzip2_alloc( void*  memory_,  /* FT_Memory */
66                   int    items,
67                   int    size )
68   {
69     FT_Memory  memory = (FT_Memory)memory_;
70 
71     FT_ULong    sz = (FT_ULong)size * (FT_ULong)items;
72     FT_Error    error;
73     FT_Pointer  p  = NULL;
74 
75 
76     FT_MEM_QALLOC( p, sz );
77     return p;
78   }
79 
80 
81   static void
ft_bzip2_free(void * memory_,void * address)82   ft_bzip2_free( void*  memory_,   /* FT_Memory */
83                  void*  address )
84   {
85     FT_Memory  memory = (FT_Memory)memory_;
86 
87 
88     FT_MEM_FREE( address );
89   }
90 
91 
92 /***************************************************************************/
93 /***************************************************************************/
94 /*****                                                                 *****/
95 /*****              B Z I P 2   F I L E   D E S C R I P T O R          *****/
96 /*****                                                                 *****/
97 /***************************************************************************/
98 /***************************************************************************/
99 
100 #define FT_BZIP2_BUFFER_SIZE  4096
101 
102   typedef struct  FT_BZip2FileRec_
103   {
104     FT_Stream  source;         /* parent/source stream        */
105     FT_Stream  stream;         /* embedding stream            */
106     FT_Memory  memory;         /* memory allocator            */
107     bz_stream  bzstream;       /* bzlib input stream          */
108 
109     FT_Byte    input[FT_BZIP2_BUFFER_SIZE];  /* input read buffer  */
110 
111     FT_Byte    buffer[FT_BZIP2_BUFFER_SIZE]; /* output buffer          */
112     FT_ULong   pos;                          /* position in output     */
113     FT_Byte*   cursor;
114     FT_Byte*   limit;
115     FT_Bool    reset;                        /* reset before next read */
116 
117   } FT_BZip2FileRec, *FT_BZip2File;
118 
119 
120   /* check and skip .bz2 header - we don't support `transparent' compression */
121   static FT_Error
ft_bzip2_check_header(FT_Stream stream)122   ft_bzip2_check_header( FT_Stream  stream )
123   {
124     FT_Error  error = FT_Err_Ok;
125     FT_Byte   head[4];
126 
127 
128     if ( FT_STREAM_SEEK( 0 )       ||
129          FT_STREAM_READ( head, 4 ) )
130       goto Exit;
131 
132     /* head[0] && head[1] are the magic numbers;    */
133     /* head[2] is the version, and head[3] the blocksize */
134     if ( head[0] != 0x42  ||
135          head[1] != 0x5A  ||
136          head[2] != 0x68  )  /* only support bzip2 (huffman) */
137     {
138       error = FT_THROW( Invalid_File_Format );
139       goto Exit;
140     }
141 
142   Exit:
143     return error;
144   }
145 
146 
147   static FT_Error
ft_bzip2_file_init(FT_BZip2File zip,FT_Stream stream,FT_Stream source)148   ft_bzip2_file_init( FT_BZip2File  zip,
149                       FT_Stream     stream,
150                       FT_Stream     source )
151   {
152     bz_stream*  bzstream = &zip->bzstream;
153     FT_Error    error    = FT_Err_Ok;
154 
155 
156     zip->stream = stream;
157     zip->source = source;
158     zip->memory = stream->memory;
159 
160     zip->limit  = zip->buffer + FT_BZIP2_BUFFER_SIZE;
161     zip->cursor = zip->limit;
162     zip->pos    = 0;
163     zip->reset  = 0;
164 
165     /* check .bz2 header */
166     {
167       stream = source;
168 
169       error = ft_bzip2_check_header( stream );
170       if ( error )
171         goto Exit;
172 
173       if ( FT_STREAM_SEEK( 0 ) )
174         goto Exit;
175     }
176 
177     /* initialize bzlib */
178     bzstream->bzalloc = ft_bzip2_alloc;
179     bzstream->bzfree  = ft_bzip2_free;
180     bzstream->opaque  = zip->memory;
181 
182     bzstream->avail_in = 0;
183     bzstream->next_in  = (char*)zip->buffer;
184 
185     if ( BZ2_bzDecompressInit( bzstream, 0, 0 ) != BZ_OK ||
186          !bzstream->next_in                              )
187       error = FT_THROW( Invalid_File_Format );
188 
189   Exit:
190     return error;
191   }
192 
193 
194   static void
ft_bzip2_file_done(FT_BZip2File zip)195   ft_bzip2_file_done( FT_BZip2File  zip )
196   {
197     bz_stream*  bzstream = &zip->bzstream;
198 
199 
200     BZ2_bzDecompressEnd( bzstream );
201 
202     /* clear the rest */
203     bzstream->bzalloc   = NULL;
204     bzstream->bzfree    = NULL;
205     bzstream->opaque    = NULL;
206     bzstream->next_in   = NULL;
207     bzstream->next_out  = NULL;
208     bzstream->avail_in  = 0;
209     bzstream->avail_out = 0;
210 
211     zip->memory = NULL;
212     zip->source = NULL;
213     zip->stream = NULL;
214   }
215 
216 
217   static FT_Error
ft_bzip2_file_reset(FT_BZip2File zip)218   ft_bzip2_file_reset( FT_BZip2File  zip )
219   {
220     FT_Stream  stream = zip->source;
221     FT_Error   error;
222 
223 
224     if ( !FT_STREAM_SEEK( 0 ) )
225     {
226       bz_stream*  bzstream = &zip->bzstream;
227 
228 
229       BZ2_bzDecompressEnd( bzstream );
230 
231       bzstream->avail_in  = 0;
232       bzstream->next_in   = (char*)zip->input;
233       bzstream->avail_out = 0;
234       bzstream->next_out  = (char*)zip->buffer;
235 
236       zip->limit  = zip->buffer + FT_BZIP2_BUFFER_SIZE;
237       zip->cursor = zip->limit;
238       zip->pos    = 0;
239       zip->reset  = 0;
240 
241       BZ2_bzDecompressInit( bzstream, 0, 0 );
242     }
243 
244     return error;
245   }
246 
247 
248   static FT_Error
ft_bzip2_file_fill_input(FT_BZip2File zip)249   ft_bzip2_file_fill_input( FT_BZip2File  zip )
250   {
251     bz_stream*  bzstream = &zip->bzstream;
252     FT_Stream   stream    = zip->source;
253     FT_ULong    size;
254 
255 
256     if ( stream->read )
257     {
258       size = stream->read( stream, stream->pos, zip->input,
259                            FT_BZIP2_BUFFER_SIZE );
260       if ( size == 0 )
261       {
262         zip->limit = zip->cursor;
263         return FT_THROW( Invalid_Stream_Operation );
264       }
265     }
266     else
267     {
268       size = stream->size - stream->pos;
269       if ( size > FT_BZIP2_BUFFER_SIZE )
270         size = FT_BZIP2_BUFFER_SIZE;
271 
272       if ( size == 0 )
273       {
274         zip->limit = zip->cursor;
275         return FT_THROW( Invalid_Stream_Operation );
276       }
277 
278       FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
279     }
280     stream->pos += size;
281 
282     bzstream->next_in  = (char*)zip->input;
283     bzstream->avail_in = size;
284 
285     return FT_Err_Ok;
286   }
287 
288 
289   static FT_Error
ft_bzip2_file_fill_output(FT_BZip2File zip)290   ft_bzip2_file_fill_output( FT_BZip2File  zip )
291   {
292     bz_stream*  bzstream = &zip->bzstream;
293     FT_Error    error    = FT_Err_Ok;
294 
295 
296     zip->cursor         = zip->buffer;
297     bzstream->next_out  = (char*)zip->cursor;
298     bzstream->avail_out = FT_BZIP2_BUFFER_SIZE;
299 
300     while ( bzstream->avail_out > 0 )
301     {
302       int  err;
303 
304 
305       if ( bzstream->avail_in == 0 )
306       {
307         error = ft_bzip2_file_fill_input( zip );
308         if ( error )
309           break;
310       }
311 
312       err = BZ2_bzDecompress( bzstream );
313 
314       if ( err != BZ_OK )
315       {
316         zip->reset = 1;
317 
318         if ( err == BZ_STREAM_END )
319         {
320           zip->limit = (FT_Byte*)bzstream->next_out;
321           if ( zip->limit == zip->cursor )
322             error = FT_THROW( Invalid_Stream_Operation );
323           break;
324         }
325         else
326         {
327           zip->limit = zip->cursor;
328           error      = FT_THROW( Invalid_Stream_Operation );
329           break;
330         }
331       }
332     }
333 
334     return error;
335   }
336 
337 
338   /* fill output buffer; `count' must be <= FT_BZIP2_BUFFER_SIZE */
339   static FT_Error
ft_bzip2_file_skip_output(FT_BZip2File zip,FT_ULong count)340   ft_bzip2_file_skip_output( FT_BZip2File  zip,
341                              FT_ULong      count )
342   {
343     FT_Error  error = FT_Err_Ok;
344 
345 
346     for (;;)
347     {
348       FT_ULong  delta = (FT_ULong)( zip->limit - zip->cursor );
349 
350 
351       if ( delta >= count )
352         delta = count;
353 
354       zip->cursor += delta;
355       zip->pos    += delta;
356 
357       count -= delta;
358       if ( count == 0 )
359         break;
360 
361       error = ft_bzip2_file_fill_output( zip );
362       if ( error )
363         break;
364     }
365 
366     return error;
367   }
368 
369 
370   static FT_ULong
ft_bzip2_file_io(FT_BZip2File zip,FT_ULong pos,FT_Byte * buffer,FT_ULong count)371   ft_bzip2_file_io( FT_BZip2File  zip,
372                     FT_ULong      pos,
373                     FT_Byte*      buffer,
374                     FT_ULong      count )
375   {
376     FT_ULong  result = 0;
377     FT_Error  error;
378 
379 
380     /* Reset inflate stream if seeking backwards or bzip reported an error. */
381     /* Yes, that is not too efficient, but it saves memory :-)              */
382     if ( pos < zip->pos || zip->reset )
383     {
384       error = ft_bzip2_file_reset( zip );
385       if ( error )
386         goto Exit;
387     }
388 
389     /* skip unwanted bytes */
390     if ( pos > zip->pos )
391     {
392       error = ft_bzip2_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
393       if ( error )
394         goto Exit;
395     }
396 
397     if ( count == 0 )
398       goto Exit;
399 
400     /* now read the data */
401     for (;;)
402     {
403       FT_ULong  delta;
404 
405 
406       delta = (FT_ULong)( zip->limit - zip->cursor );
407       if ( delta >= count )
408         delta = count;
409 
410       FT_MEM_COPY( buffer, zip->cursor, delta );
411       buffer      += delta;
412       result      += delta;
413       zip->cursor += delta;
414       zip->pos    += delta;
415 
416       count -= delta;
417       if ( count == 0 )
418         break;
419 
420       error = ft_bzip2_file_fill_output( zip );
421       if ( error )
422         break;
423     }
424 
425   Exit:
426     return result;
427   }
428 
429 
430 /***************************************************************************/
431 /***************************************************************************/
432 /*****                                                                 *****/
433 /*****               B Z   E M B E D D I N G   S T R E A M             *****/
434 /*****                                                                 *****/
435 /***************************************************************************/
436 /***************************************************************************/
437 
438   static void
ft_bzip2_stream_close(FT_Stream stream)439   ft_bzip2_stream_close( FT_Stream  stream )
440   {
441     FT_BZip2File  zip    = (FT_BZip2File)stream->descriptor.pointer;
442     FT_Memory     memory = stream->memory;
443 
444 
445     if ( zip )
446     {
447       /* finalize bzip file descriptor */
448       ft_bzip2_file_done( zip );
449 
450       FT_FREE( zip );
451 
452       stream->descriptor.pointer = NULL;
453     }
454   }
455 
456 
457   static unsigned long
ft_bzip2_stream_io(FT_Stream stream,unsigned long offset,unsigned char * buffer,unsigned long count)458   ft_bzip2_stream_io( FT_Stream       stream,
459                       unsigned long   offset,
460                       unsigned char*  buffer,
461                       unsigned long   count )
462   {
463     FT_BZip2File  zip = (FT_BZip2File)stream->descriptor.pointer;
464 
465 
466     return ft_bzip2_file_io( zip, offset, buffer, count );
467   }
468 
469 
470   FT_EXPORT_DEF( FT_Error )
FT_Stream_OpenBzip2(FT_Stream stream,FT_Stream source)471   FT_Stream_OpenBzip2( FT_Stream  stream,
472                        FT_Stream  source )
473   {
474     FT_Error      error;
475     FT_Memory     memory;
476     FT_BZip2File  zip = NULL;
477 
478 
479     if ( !stream || !source )
480     {
481       error = FT_THROW( Invalid_Stream_Handle );
482       goto Exit;
483     }
484 
485     memory = source->memory;
486 
487     /*
488      * check the header right now; this prevents allocating unnecessary
489      * objects when we don't need them
490      */
491     error = ft_bzip2_check_header( source );
492     if ( error )
493       goto Exit;
494 
495     FT_ZERO( stream );
496     stream->memory = memory;
497 
498     if ( !FT_QNEW( zip ) )
499     {
500       error = ft_bzip2_file_init( zip, stream, source );
501       if ( error )
502       {
503         FT_FREE( zip );
504         goto Exit;
505       }
506 
507       stream->descriptor.pointer = zip;
508     }
509 
510     stream->size  = 0x7FFFFFFFL;  /* don't know the real size! */
511     stream->pos   = 0;
512     stream->base  = NULL;
513     stream->read  = ft_bzip2_stream_io;
514     stream->close = ft_bzip2_stream_close;
515 
516   Exit:
517     return error;
518   }
519 
520 #else  /* !FT_CONFIG_OPTION_USE_BZIP2 */
521 
522   FT_EXPORT_DEF( FT_Error )
FT_Stream_OpenBzip2(FT_Stream stream,FT_Stream source)523   FT_Stream_OpenBzip2( FT_Stream  stream,
524                        FT_Stream  source )
525   {
526     FT_UNUSED( stream );
527     FT_UNUSED( source );
528 
529     return FT_THROW( Unimplemented_Feature );
530   }
531 
532 #endif /* !FT_CONFIG_OPTION_USE_BZIP2 */
533 
534 
535 /* END */
536