Sync with trunk (r47116), hopefully without breaking anything.
[reactos.git] / lib / 3rdparty / freetype / src / gzip / ftgzip.c
1 /***************************************************************************/
2 /* */
3 /* ftgzip.c */
4 /* */
5 /* FreeType support for .gz compressed files. */
6 /* */
7 /* This optional component relies on zlib. It should mainly be used to */
8 /* parse compressed PCF fonts, as found with many X11 server */
9 /* distributions. */
10 /* */
11 /* Copyright 2002, 2003, 2004, 2005, 2006 by */
12 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
13 /* */
14 /* This file is part of the FreeType project, and may only be used, */
15 /* modified, and distributed under the terms of the FreeType project */
16 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
17 /* this file you indicate that you have read the license and */
18 /* understand and accept it fully. */
19 /* */
20 /***************************************************************************/
21
22
23 #include <ft2build.h>
24 #include FT_INTERNAL_MEMORY_H
25 #include FT_INTERNAL_STREAM_H
26 #include FT_INTERNAL_DEBUG_H
27 #include FT_GZIP_H
28 #include <string.h>
29
30
31 #include FT_MODULE_ERRORS_H
32
33 #undef __FTERRORS_H__
34
35 #define FT_ERR_PREFIX Gzip_Err_
36 #define FT_ERR_BASE FT_Mod_Err_Gzip
37
38 #include FT_ERRORS_H
39
40
41 #ifdef FT_CONFIG_OPTION_USE_ZLIB
42
43 #ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB
44
45 #include <zlib.h>
46
47 #else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
48
49 /* In this case, we include our own modified sources of the ZLib */
50 /* within the "ftgzip" component. The modifications were necessary */
51 /* to #include all files without conflicts, as well as preventing */
52 /* the definition of "extern" functions that may cause linking */
53 /* conflicts when a program is linked with both FreeType and the */
54 /* original ZLib. */
55
56 #define NO_DUMMY_DECL
57 #define MY_ZCALLOC
58
59 #include "zlib.h"
60
61 #undef SLOW
62 #define SLOW 1 /* we can't use asm-optimized sources here! */
63
64 /* Urgh. `inflate_mask' must not be declared twice -- C++ doesn't like
65 this. We temporarily disable it and load all necessary header files. */
66 #define NO_INFLATE_MASK
67 #include "zutil.h"
68 #include "inftrees.h"
69 #include "infblock.h"
70 #include "infcodes.h"
71 #include "infutil.h"
72 #undef NO_INFLATE_MASK
73
74 /* infutil.c must be included before infcodes.c */
75 #include "zutil.c"
76 #include "inftrees.c"
77 #include "infutil.c"
78 #include "infcodes.c"
79 #include "infblock.c"
80 #include "inflate.c"
81 #include "adler32.c"
82
83 #endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
84
85
86 /***************************************************************************/
87 /***************************************************************************/
88 /***** *****/
89 /***** Z L I B M E M O R Y M A N A G E M E N T *****/
90 /***** *****/
91 /***************************************************************************/
92 /***************************************************************************/
93
94 /* it is better to use FreeType memory routines instead of raw
95 'malloc/free' */
96
97 static voidpf
98 ft_gzip_alloc( FT_Memory memory,
99 uInt items,
100 uInt size )
101 {
102 FT_ULong sz = (FT_ULong)size * items;
103 FT_Error error;
104 FT_Pointer p;
105
106
107 (void)FT_ALLOC( p, sz );
108 return p;
109 }
110
111
112 static void
113 ft_gzip_free( FT_Memory memory,
114 voidpf address )
115 {
116 FT_MEM_FREE( address );
117 }
118
119
120 #ifndef FT_CONFIG_OPTION_SYSTEM_ZLIB
121
122 local voidpf
123 zcalloc ( voidpf opaque,
124 unsigned items,
125 unsigned size )
126 {
127 return ft_gzip_alloc( (FT_Memory)opaque, items, size );
128 }
129
130 local void
131 zcfree( voidpf opaque,
132 voidpf ptr )
133 {
134 ft_gzip_free( (FT_Memory)opaque, ptr );
135 }
136
137 #endif /* !SYSTEM_ZLIB */
138
139
140 /***************************************************************************/
141 /***************************************************************************/
142 /***** *****/
143 /***** Z L I B F I L E D E S C R I P T O R *****/
144 /***** *****/
145 /***************************************************************************/
146 /***************************************************************************/
147
148 #define FT_GZIP_BUFFER_SIZE 4096
149
150 typedef struct FT_GZipFileRec_
151 {
152 FT_Stream source; /* parent/source stream */
153 FT_Stream stream; /* embedding stream */
154 FT_Memory memory; /* memory allocator */
155 z_stream zstream; /* zlib input stream */
156
157 FT_ULong start; /* starting position, after .gz header */
158 FT_Byte input[FT_GZIP_BUFFER_SIZE]; /* input read buffer */
159
160 FT_Byte buffer[FT_GZIP_BUFFER_SIZE]; /* output buffer */
161 FT_ULong pos; /* position in output */
162 FT_Byte* cursor;
163 FT_Byte* limit;
164
165 } FT_GZipFileRec, *FT_GZipFile;
166
167
168 /* gzip flag byte */
169 #define FT_GZIP_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
170 #define FT_GZIP_HEAD_CRC 0x02 /* bit 1 set: header CRC present */
171 #define FT_GZIP_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
172 #define FT_GZIP_ORIG_NAME 0x08 /* bit 3 set: original file name present */
173 #define FT_GZIP_COMMENT 0x10 /* bit 4 set: file comment present */
174 #define FT_GZIP_RESERVED 0xE0 /* bits 5..7: reserved */
175
176
177 /* check and skip .gz header - we don't support `transparent' compression */
178 static FT_Error
179 ft_gzip_check_header( FT_Stream stream )
180 {
181 FT_Error error;
182 FT_Byte head[4];
183
184
185 if ( FT_STREAM_SEEK( 0 ) ||
186 FT_STREAM_READ( head, 4 ) )
187 goto Exit;
188
189 /* head[0] && head[1] are the magic numbers; */
190 /* head[2] is the method, and head[3] the flags */
191 if ( head[0] != 0x1f ||
192 head[1] != 0x8b ||
193 head[2] != Z_DEFLATED ||
194 (head[3] & FT_GZIP_RESERVED) )
195 {
196 error = Gzip_Err_Invalid_File_Format;
197 goto Exit;
198 }
199
200 /* skip time, xflags and os code */
201 (void)FT_STREAM_SKIP( 6 );
202
203 /* skip the extra field */
204 if ( head[3] & FT_GZIP_EXTRA_FIELD )
205 {
206 FT_UInt len;
207
208
209 if ( FT_READ_USHORT_LE( len ) ||
210 FT_STREAM_SKIP( len ) )
211 goto Exit;
212 }
213
214 /* skip original file name */
215 if ( head[3] & FT_GZIP_ORIG_NAME )
216 for (;;)
217 {
218 FT_UInt c;
219
220
221 if ( FT_READ_BYTE( c ) )
222 goto Exit;
223
224 if ( c == 0 )
225 break;
226 }
227
228 /* skip .gz comment */
229 if ( head[3] & FT_GZIP_COMMENT )
230 for (;;)
231 {
232 FT_UInt c;
233
234
235 if ( FT_READ_BYTE( c ) )
236 goto Exit;
237
238 if ( c == 0 )
239 break;
240 }
241
242 /* skip CRC */
243 if ( head[3] & FT_GZIP_HEAD_CRC )
244 if ( FT_STREAM_SKIP( 2 ) )
245 goto Exit;
246
247 Exit:
248 return error;
249 }
250
251
252 static FT_Error
253 ft_gzip_file_init( FT_GZipFile zip,
254 FT_Stream stream,
255 FT_Stream source )
256 {
257 z_stream* zstream = &zip->zstream;
258 FT_Error error = Gzip_Err_Ok;
259
260
261 zip->stream = stream;
262 zip->source = source;
263 zip->memory = stream->memory;
264
265 zip->limit = zip->buffer + FT_GZIP_BUFFER_SIZE;
266 zip->cursor = zip->limit;
267 zip->pos = 0;
268
269 /* check and skip .gz header */
270 {
271 stream = source;
272
273 error = ft_gzip_check_header( stream );
274 if ( error )
275 goto Exit;
276
277 zip->start = FT_STREAM_POS();
278 }
279
280 /* initialize zlib -- there is no zlib header in the compressed stream */
281 zstream->zalloc = (alloc_func)ft_gzip_alloc;
282 zstream->zfree = (free_func) ft_gzip_free;
283 zstream->opaque = stream->memory;
284
285 zstream->avail_in = 0;
286 zstream->next_in = zip->buffer;
287
288 if ( inflateInit2( zstream, -MAX_WBITS ) != Z_OK ||
289 zstream->next_in == NULL )
290 error = Gzip_Err_Invalid_File_Format;
291
292 Exit:
293 return error;
294 }
295
296
297 static void
298 ft_gzip_file_done( FT_GZipFile zip )
299 {
300 z_stream* zstream = &zip->zstream;
301
302
303 inflateEnd( zstream );
304
305 /* clear the rest */
306 zstream->zalloc = NULL;
307 zstream->zfree = NULL;
308 zstream->opaque = NULL;
309 zstream->next_in = NULL;
310 zstream->next_out = NULL;
311 zstream->avail_in = 0;
312 zstream->avail_out = 0;
313
314 zip->memory = NULL;
315 zip->source = NULL;
316 zip->stream = NULL;
317 }
318
319
320 static FT_Error
321 ft_gzip_file_reset( FT_GZipFile zip )
322 {
323 FT_Stream stream = zip->source;
324 FT_Error error;
325
326
327 if ( !FT_STREAM_SEEK( zip->start ) )
328 {
329 z_stream* zstream = &zip->zstream;
330
331
332 inflateReset( zstream );
333
334 zstream->avail_in = 0;
335 zstream->next_in = zip->input;
336 zstream->avail_out = 0;
337 zstream->next_out = zip->buffer;
338
339 zip->limit = zip->buffer + FT_GZIP_BUFFER_SIZE;
340 zip->cursor = zip->limit;
341 zip->pos = 0;
342 }
343
344 return error;
345 }
346
347
348 static FT_Error
349 ft_gzip_file_fill_input( FT_GZipFile zip )
350 {
351 z_stream* zstream = &zip->zstream;
352 FT_Stream stream = zip->source;
353 FT_ULong size;
354
355
356 if ( stream->read )
357 {
358 size = stream->read( stream, stream->pos, zip->input,
359 FT_GZIP_BUFFER_SIZE );
360 if ( size == 0 )
361 return Gzip_Err_Invalid_Stream_Operation;
362 }
363 else
364 {
365 size = stream->size - stream->pos;
366 if ( size > FT_GZIP_BUFFER_SIZE )
367 size = FT_GZIP_BUFFER_SIZE;
368
369 if ( size == 0 )
370 return Gzip_Err_Invalid_Stream_Operation;
371
372 FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
373 }
374 stream->pos += size;
375
376 zstream->next_in = zip->input;
377 zstream->avail_in = size;
378
379 return Gzip_Err_Ok;
380 }
381
382
383 static FT_Error
384 ft_gzip_file_fill_output( FT_GZipFile zip )
385 {
386 z_stream* zstream = &zip->zstream;
387 FT_Error error = 0;
388
389
390 zip->cursor = zip->buffer;
391 zstream->next_out = zip->cursor;
392 zstream->avail_out = FT_GZIP_BUFFER_SIZE;
393
394 while ( zstream->avail_out > 0 )
395 {
396 int err;
397
398
399 if ( zstream->avail_in == 0 )
400 {
401 error = ft_gzip_file_fill_input( zip );
402 if ( error )
403 break;
404 }
405
406 err = inflate( zstream, Z_NO_FLUSH );
407
408 if ( err == Z_STREAM_END )
409 {
410 zip->limit = zstream->next_out;
411 if ( zip->limit == zip->cursor )
412 error = Gzip_Err_Invalid_Stream_Operation;
413 break;
414 }
415 else if ( err != Z_OK )
416 {
417 error = Gzip_Err_Invalid_Stream_Operation;
418 break;
419 }
420 }
421
422 return error;
423 }
424
425
426 /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */
427 static FT_Error
428 ft_gzip_file_skip_output( FT_GZipFile zip,
429 FT_ULong count )
430 {
431 FT_Error error = Gzip_Err_Ok;
432 FT_ULong delta;
433
434
435 for (;;)
436 {
437 delta = (FT_ULong)( zip->limit - zip->cursor );
438 if ( delta >= count )
439 delta = count;
440
441 zip->cursor += delta;
442 zip->pos += delta;
443
444 count -= delta;
445 if ( count == 0 )
446 break;
447
448 error = ft_gzip_file_fill_output( zip );
449 if ( error )
450 break;
451 }
452
453 return error;
454 }
455
456
457 static FT_ULong
458 ft_gzip_file_io( FT_GZipFile zip,
459 FT_ULong pos,
460 FT_Byte* buffer,
461 FT_ULong count )
462 {
463 FT_ULong result = 0;
464 FT_Error error;
465
466
467 /* Reset inflate stream if we're seeking backwards. */
468 /* Yes, that is not too efficient, but it saves memory :-) */
469 if ( pos < zip->pos )
470 {
471 error = ft_gzip_file_reset( zip );
472 if ( error )
473 goto Exit;
474 }
475
476 /* skip unwanted bytes */
477 if ( pos > zip->pos )
478 {
479 error = ft_gzip_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
480 if ( error )
481 goto Exit;
482 }
483
484 if ( count == 0 )
485 goto Exit;
486
487 /* now read the data */
488 for (;;)
489 {
490 FT_ULong delta;
491
492
493 delta = (FT_ULong)( zip->limit - zip->cursor );
494 if ( delta >= count )
495 delta = count;
496
497 FT_MEM_COPY( buffer, zip->cursor, delta );
498 buffer += delta;
499 result += delta;
500 zip->cursor += delta;
501 zip->pos += delta;
502
503 count -= delta;
504 if ( count == 0 )
505 break;
506
507 error = ft_gzip_file_fill_output( zip );
508 if ( error )
509 break;
510 }
511
512 Exit:
513 return result;
514 }
515
516
517 /***************************************************************************/
518 /***************************************************************************/
519 /***** *****/
520 /***** G Z E M B E D D I N G S T R E A M *****/
521 /***** *****/
522 /***************************************************************************/
523 /***************************************************************************/
524
525 static void
526 ft_gzip_stream_close( FT_Stream stream )
527 {
528 FT_GZipFile zip = (FT_GZipFile)stream->descriptor.pointer;
529 FT_Memory memory = stream->memory;
530
531
532 if ( zip )
533 {
534 /* finalize gzip file descriptor */
535 ft_gzip_file_done( zip );
536
537 FT_FREE( zip );
538
539 stream->descriptor.pointer = NULL;
540 }
541 }
542
543
544 static FT_ULong
545 ft_gzip_stream_io( FT_Stream stream,
546 FT_ULong pos,
547 FT_Byte* buffer,
548 FT_ULong count )
549 {
550 FT_GZipFile zip = (FT_GZipFile)stream->descriptor.pointer;
551
552
553 return ft_gzip_file_io( zip, pos, buffer, count );
554 }
555
556
557 static FT_ULong
558 ft_gzip_get_uncompressed_size( FT_Stream stream )
559 {
560 FT_Error error;
561 FT_ULong old_pos;
562 FT_ULong result = 0;
563
564
565 old_pos = stream->pos;
566 if ( !FT_Stream_Seek( stream, stream->size - 4 ) )
567 {
568 result = (FT_ULong)FT_Stream_ReadLong( stream, &error );
569 if ( error )
570 result = 0;
571
572 FT_Stream_Seek( stream, old_pos );
573 }
574
575 return result;
576 }
577
578
579 FT_EXPORT_DEF( FT_Error )
580 FT_Stream_OpenGzip( FT_Stream stream,
581 FT_Stream source )
582 {
583 FT_Error error;
584 FT_Memory memory = source->memory;
585 FT_GZipFile zip;
586
587
588 /*
589 * check the header right now; this prevents allocating un-necessary
590 * objects when we don't need them
591 */
592 error = ft_gzip_check_header( source );
593 if ( error )
594 goto Exit;
595
596 FT_ZERO( stream );
597 stream->memory = memory;
598
599 if ( !FT_QNEW( zip ) )
600 {
601 error = ft_gzip_file_init( zip, stream, source );
602 if ( error )
603 {
604 FT_FREE( zip );
605 goto Exit;
606 }
607
608 stream->descriptor.pointer = zip;
609 }
610
611 /*
612 * We use the following trick to try to dramatically improve the
613 * performance while dealing with small files. If the original stream
614 * size is less than a certain threshold, we try to load the whole font
615 * file into memory. This saves us from using the 32KB buffer needed
616 * to inflate the file, plus the two 4KB intermediate input/output
617 * buffers used in the `FT_GZipFile' structure.
618 */
619 {
620 FT_ULong zip_size = ft_gzip_get_uncompressed_size( source );
621
622
623 if ( zip_size != 0 && zip_size < 40 * 1024 )
624 {
625 FT_Byte* zip_buff;
626
627
628 if ( !FT_ALLOC( zip_buff, zip_size ) )
629 {
630 FT_ULong count;
631
632
633 count = ft_gzip_file_io( zip, 0, zip_buff, zip_size );
634 if ( count == zip_size )
635 {
636 ft_gzip_file_done( zip );
637 FT_FREE( zip );
638
639 stream->descriptor.pointer = NULL;
640
641 stream->size = zip_size;
642 stream->pos = 0;
643 stream->base = zip_buff;
644 stream->read = NULL;
645 stream->close = ft_gzip_stream_close;
646
647 goto Exit;
648 }
649
650 ft_gzip_file_io( zip, 0, NULL, 0 );
651 FT_FREE( zip_buff );
652 }
653 error = 0;
654 }
655 }
656
657 stream->size = 0x7FFFFFFFL; /* don't know the real size! */
658 stream->pos = 0;
659 stream->base = 0;
660 stream->read = ft_gzip_stream_io;
661 stream->close = ft_gzip_stream_close;
662
663 Exit:
664 return error;
665 }
666
667 #else /* !FT_CONFIG_OPTION_USE_ZLIB */
668
669 FT_EXPORT_DEF( FT_Error )
670 FT_Stream_OpenGzip( FT_Stream stream,
671 FT_Stream source )
672 {
673 FT_UNUSED( stream );
674 FT_UNUSED( source );
675
676 return Gzip_Err_Unimplemented_Feature;
677 }
678
679 #endif /* !FT_CONFIG_OPTION_USE_ZLIB */
680
681
682 /* END */