From f6194ef39af5864f792412460c354cc339dde7d1 Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Fri, 9 Sep 2011 23:26:40 -0700 Subject: [PATCH] zlib 1.2.3.4 --- ChangeLog | 63 +- INDEX | 1 + Makefile | 112 +-- Makefile.in | 112 +-- README | 4 +- adler32.c | 38 +- as400/zlib.inc | 6 +- configure | 140 ++-- contrib/README.contrib | 4 + contrib/amd64/amd64-match.S | 357 ++++++++++ contrib/infback9/infback9.c | 13 +- contrib/infback9/inftree9.c | 21 +- contrib/infback9/inftree9.h | 24 +- contrib/nintendods/Makefile | 126 ++++ contrib/nintendods/README | 5 + contrib/puff/puff | Bin 0 -> 17344 bytes contrib/puff/puff.c | 193 ++++-- contrib/puff/puff.h | 4 +- contrib/vstudio/vc7/zlib.rc | 6 +- crc32.c | 32 +- deflate.c | 75 +- deflate.h | 13 +- doc/algorithm.txt | 2 +- examples/README.examples | 21 +- examples/enough.c | 569 +++++++++++++++ examples/gzlog.c | 1303 ++++++++++++++++++++++++++--------- examples/gzlog.h | 93 ++- examples/pigz.c | 452 ++++++++++++ gzio.c | 94 +-- infback.c | 13 +- inffast.c | 5 +- inflate.c | 136 +++- inflate.h | 26 +- inftrees.c | 23 +- inftrees.h | 23 +- make_vms.com | 3 +- qnx/package.qpg | 10 +- trees.c | 10 +- win32/zlib.def | 16 +- zconf.h | 6 +- zlib.3 | 4 +- zlib.h | 169 +++-- zlib.map | 9 + zlib2ansi | 152 ++++ zutil.h | 6 + 45 files changed, 3642 insertions(+), 852 deletions(-) create mode 100644 contrib/amd64/amd64-match.S create mode 100644 contrib/nintendods/Makefile create mode 100644 contrib/nintendods/README create mode 100755 contrib/puff/puff create mode 100644 examples/enough.c create mode 100644 examples/pigz.c create mode 100755 zlib2ansi diff --git a/ChangeLog b/ChangeLog index 1bc7105e..497b757e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,67 @@ ChangeLog file for zlib +Changes in 1.2.3.4 (21 Dec 2009) +- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility +- Update comments in configure and Makefile.in for default --shared +- Fix test -z's in configure [Marquess] +- Build examplesh and minigzipsh when not testing +- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h +- Import LDFLAGS from the environment in configure +- Fix configure to populate SFLAGS with discovered CFLAGS options +- Adapt make_vms.com to the new Makefile.in [Zinser] +- Add zlib2ansi script for C++ compilation [Marquess] +- Add _FILE_OFFSET_BITS=64 test to make test (when applicable) +- Add AMD64 assembler code for longest match to contrib [Teterin] +- Include options from $SFLAGS when doing $LDSHARED +- Simplify 64-bit file support by introducing z_off64_t type +- Make shared object files in objs directory to work around old Sun cc +- Use only three-part version number for Darwin shared compiles +- Add rc option to ar in Makefile.in for when ./configure not run +- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* +- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile +- Protect against _FILE_OFFSET_BITS being defined when compiling zlib +- Rename Makefile.in targets allstatic to static and allshared to shared +- Fix static and shared Makefile.in targets to be independent +- Correct error return bug in gz_open() by setting state [Brown] +- Put spaces before ;;'s in configure for better sh compatibility +- Added pigz.c (parallel implementation of gzip) to examples/ +- Correct constant in crc32.c to UL [Leventhal] +- Reject negative lengths in crc32_combine() +- Add inflateReset2() function to work like inflateEnd()/inflateInit2() +- Include sys/types.h for _LARGEFILE64_SOURCE [Brown] +- Correct typo in doc/algorithm.txt [Janik] +- Fix bug in adler32_combine() [Zhu] +- Catch missing-end-of-block-code error in all inflates and in puff + Assures that random input to inflate eventually results in an error +- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ +- Update ENOUGH and its usage to reflect discovered bounds +- Fix gzerror() error report on empty input file [Brown] +- Add ush casts in trees.c to avoid pedantic runtime errors +- Fix typo in zlib.h uncompress() description [Reiss] +- Correct inflate() comments with regard to automatic header detection +- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) +- Put new version of gzlog (2.0) in examples with interruption recovery +- Add puff compile option to permit invalid distance-too-far streams +- Add puff TEST command options, ability to read piped input +- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but + _LARGEFILE64_SOURCE not defined +- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart +- Fix deflateSetDictionary() to use all 32K for output consistency +- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) +- Clear bytes after deflate lookahead to avoid use of uninitialized data +- Change a limit in inftrees.c to be more transparent to Coverity Prevent +- Update win32/zlib.def with exported symbols from zlib.h +- Correct spelling error in zlib.h [Willem] +- Allow Z_BLOCK for deflate() to force a new block +- Allow negative bits in inflatePrime() to delete existing bit buffer +- Add Z_TREES flush option to inflate() to return at end of trees +- Add inflateMark() to return current state information for random access +- Added Makefile for NintendoDS to contrib [Costa] +- Add -w in configure compile tests to avoid spurious warnings [Beucler] +- Fix typos in zlib.h comments for deflateSetDictionary() +- Fix EOF detection in transparent gzread() [Maier] + Changes in 1.2.3.3 (2 October 2006) - Make --shared the default for configure, add a --static option - Add compile option to permit invalid distance-too-far streams @@ -10,7 +71,7 @@ Changes in 1.2.3.3 (2 October 2006) - Use ftello() and fseeko() if available instead of ftell() and fseek() - Provide two versions of all functions that use the z_off_t type for binary compatibility -- a normal version and a 64-bit offset version, - per the Large File Support Extension when _LARGEFILE64_SUPPORT is + per the Large File Support Extension when _LARGEFILE64_SOURCE is defined; use the 64-bit versions by default when _FILE_OFFSET_BITS is defined to be 64 - Add a --uname= option to configure to perhaps help with cross-compiling diff --git a/INDEX b/INDEX index 722d4eca..2c7a5123 100644 --- a/INDEX +++ b/INDEX @@ -10,6 +10,7 @@ treebuild.xml see http://treebuild.metux.de/ zlib.3 Man page for zlib zlib.map Linux symbol information zlib.pc.in Template for pkg-config descriptor +zlib2ansi perl script to convert source files for C++ compilation amiga/ makefiles for Amiga SAS C as400/ makefiles for IBM AS/400 diff --git a/Makefile b/Makefile index 2fa2e259..6ad7a5ce 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ # To compile and test, type: # ./configure; make test -# The call of configure is optional if you don't have special requirements -# If you wish to build zlib as a shared library, use: ./configure -s +# Normally configure builds both a static and a shared library. +# If you want to build just a static library, use: ./configure --static # To use the asm code, type: # cp contrib/asm?86/match.S ./match.S @@ -26,16 +26,17 @@ CFLAGS=-O SFLAGS=-O -LDFLAGS=libz.a +LDFLAGS=-L. libz.a LDSHARED=$(CC) CPP=$(CC) -E -LIBS=libz.a +STATICLIB=libz.a SHAREDLIB=libz.so -SHAREDLIBV=libz.so.1.2.3.3 +SHAREDLIBV=libz.so.1.2.3.4 SHAREDLIBM=libz.so.1 +LIBS=$(STATICLIB) $(SHAREDLIB) -AR=ar +AR=ar rc RANLIB=ranlib TAR=tar SHELL=/bin/sh @@ -52,22 +53,30 @@ pkgconfigdir = ${libdir}/pkgconfig OBJC = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \ zutil.o inflate.o infback.o inftrees.o inffast.o +PIC_OBJC = adler32.lo compress.lo crc32.lo gzio.lo uncompr.lo deflate.lo trees.lo \ + zutil.lo inflate.lo infback.lo inftrees.lo inffast.lo + +# to use the asm code: make OBJA=match.o, PIC_OBJA=match.lo OBJA = -# to use the asm code: make OBJA=match.o +PIC_OBJA = OBJS = $(OBJC) $(OBJA) -PIC_OBJS = $(OBJS:%.o=%.lo) +PIC_OBJS = $(PIC_OBJC) $(PIC_OBJA) + +all: static shared -TEST_OBJS = example.o minigzip.o +static: example$(EXE) minigzip$(EXE) -allstatic: example$(EXE) minigzip$(EXE) +shared: examplesh$(EXE) minigzipsh$(EXE) -allshared: examplesh$(EXE) minigzipsh$(EXE) +all64: example64$(EXE) minigzip64$(EXE) + +check: test -all: allstatic allshared +test: all teststatic testshared -teststatic: allstatic +teststatic: static @echo hello world | ./minigzip | ./minigzip -d || \ echo ' *** minigzip test FAILED ***' ; \ if ./example; then \ @@ -76,8 +85,9 @@ teststatic: allstatic echo ' *** zlib test FAILED ***'; \ fi -testshared: allshared +testshared: shared @LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ + LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \ DYLD_LIBRARY_PATH=`pwd`:$(DYLD_LIBRARY_PATH) ; export DYLD_LIBRARY_PATH; \ SHLIB_PATH=`pwd`:$(SHLIB_PATH) ; export SHLIB_PATH; \ echo hello world | ./minigzipsh | ./minigzipsh -d || \ @@ -88,9 +98,14 @@ testshared: allshared echo ' *** zlib shared test FAILED ***'; \ fi -test: teststatic testshared - -check: test +test64: all64 + @echo hello world | ./minigzip64 | ./minigzip64 -d || \ + echo ' *** minigzip 64-bit test FAILED ***' ; \ + if ./example64; then \ + echo ' *** zlib 64-bit test OK ***'; \ + else \ + echo ' *** zlib 64-bit test FAILED ***'; \ + fi libz.a: $(OBJS) $(AR) $@ $(OBJS) @@ -108,26 +123,43 @@ match.lo: match.S mv _match.o match.lo rm -f _match.s -%.lo: %.c - $(CC) $(SFLAGS) -DPIC -c $< -o $@ +example64.o: example.c zlib.h zconf.h zlibdefs.h + $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -c -o $@ $< + +minigzip64.o: minigzip.c zlib.h zconf.h zlibdefs.h + $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -c -o $@ $< + +.SUFFIXES: .lo + +.c.lo: + -@if [ ! -d objs ]; then mkdir objs; fi + $(CC) $(SFLAGS) -DPIC -c -o objs/$*.o $< + -@mv objs/$*.o $@ $(SHAREDLIBV): $(PIC_OBJS) - $(LDSHARED) -o $@ $(PIC_OBJS) -lc + $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) -lc rm -f $(SHAREDLIB) $(SHAREDLIBM) ln -s $@ $(SHAREDLIB) ln -s $@ $(SHAREDLIBM) + -@rmdir objs -example$(EXE): example.o $(LIBS) +example$(EXE): example.o $(STATICLIB) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -minigzip$(EXE): minigzip.o $(LIBS) +minigzip$(EXE): minigzip.o $(STATICLIB) $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) -examplesh$(EXE): example.o $(LIBS) - $(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIB) +examplesh$(EXE): example.o $(SHAREDLIBV) + $(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIBV) + +minigzipsh$(EXE): minigzip.o $(SHAREDLIBV) + $(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIBV) + +example64$(EXE): example64.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ example64.o $(LDFLAGS) -minigzipsh$(EXE): minigzip.o $(LIBS) - $(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIB) +minigzip64$(EXE): minigzip64.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ minigzip64.o $(LDFLAGS) install-libs: $(LIBS) -@if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi @@ -168,8 +200,10 @@ mostlyclean: clean clean: rm -f *.o *.lo *~ \ example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \ + example64$(EXE) minigzip64$(EXE) \ libz.* foo.gz so_locations \ _match.s maketree contrib/infback9/*.o + rm -rf objs maintainer-clean: distclean distclean: clean @@ -186,30 +220,20 @@ depend: # DO NOT DELETE THIS LINE -- make depend depends on it. -adler32.o: zlib.h zconf.h zlibdefs.h -compress.o: zlib.h zconf.h zlibdefs.h -crc32.o: crc32.h zlib.h zconf.h zlibdefs.h +adler32.o gzio.o zutil.o: zutil.h zlib.h zconf.h zlibdefs.h +compress.o example.o minigzip.o uncompr.o: zlib.h zconf.h zlibdefs.h +crc32.o: zutil.h zlib.h zconf.h zlibdefs.h crc32.h deflate.o: deflate.h zutil.h zlib.h zconf.h zlibdefs.h -example.o: zlib.h zconf.h zlibdefs.h -gzio.o: zutil.h zlib.h zconf.h zlibdefs.h +infback.o inflate.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inffast.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h -inflate.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h -infback.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inftrees.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h -minigzip.o: zlib.h zconf.h zlibdefs.h trees.o: deflate.h zutil.h zlib.h zconf.h zlibdefs.h trees.h -uncompr.o: zlib.h zconf.h zlibdefs.h -zutil.o: zutil.h zlib.h zconf.h zlibdefs.h -adler32.lo: zlib.h zconf.h zlibdefs.h -compress.lo: zlib.h zconf.h zlibdefs.h -crc32.lo: crc32.h zlib.h zconf.h zlibdefs.h +adler32.lo gzio.lo zutil.lo: zutil.h zlib.h zconf.h zlibdefs.h +compress.lo example.lo minigzip.lo uncompr.lo: zlib.h zconf.h zlibdefs.h +crc32.lo: zutil.h zlib.h zconf.h zlibdefs.h crc32.h deflate.lo: deflate.h zutil.h zlib.h zconf.h zlibdefs.h -gzio.lo: zutil.h zlib.h zconf.h zlibdefs.h +infback.lo inflate.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inffast.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h -inflate.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h -infback.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inftrees.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h trees.lo: deflate.h zutil.h zlib.h zconf.h zlibdefs.h trees.h -uncompr.lo: zlib.h zconf.h zlibdefs.h -zutil.lo: zutil.h zlib.h zconf.h zlibdefs.h diff --git a/Makefile.in b/Makefile.in index 2fa2e259..6ad7a5ce 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,8 +4,8 @@ # To compile and test, type: # ./configure; make test -# The call of configure is optional if you don't have special requirements -# If you wish to build zlib as a shared library, use: ./configure -s +# Normally configure builds both a static and a shared library. +# If you want to build just a static library, use: ./configure --static # To use the asm code, type: # cp contrib/asm?86/match.S ./match.S @@ -26,16 +26,17 @@ CFLAGS=-O SFLAGS=-O -LDFLAGS=libz.a +LDFLAGS=-L. libz.a LDSHARED=$(CC) CPP=$(CC) -E -LIBS=libz.a +STATICLIB=libz.a SHAREDLIB=libz.so -SHAREDLIBV=libz.so.1.2.3.3 +SHAREDLIBV=libz.so.1.2.3.4 SHAREDLIBM=libz.so.1 +LIBS=$(STATICLIB) $(SHAREDLIB) -AR=ar +AR=ar rc RANLIB=ranlib TAR=tar SHELL=/bin/sh @@ -52,22 +53,30 @@ pkgconfigdir = ${libdir}/pkgconfig OBJC = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \ zutil.o inflate.o infback.o inftrees.o inffast.o +PIC_OBJC = adler32.lo compress.lo crc32.lo gzio.lo uncompr.lo deflate.lo trees.lo \ + zutil.lo inflate.lo infback.lo inftrees.lo inffast.lo + +# to use the asm code: make OBJA=match.o, PIC_OBJA=match.lo OBJA = -# to use the asm code: make OBJA=match.o +PIC_OBJA = OBJS = $(OBJC) $(OBJA) -PIC_OBJS = $(OBJS:%.o=%.lo) +PIC_OBJS = $(PIC_OBJC) $(PIC_OBJA) + +all: static shared -TEST_OBJS = example.o minigzip.o +static: example$(EXE) minigzip$(EXE) -allstatic: example$(EXE) minigzip$(EXE) +shared: examplesh$(EXE) minigzipsh$(EXE) -allshared: examplesh$(EXE) minigzipsh$(EXE) +all64: example64$(EXE) minigzip64$(EXE) + +check: test -all: allstatic allshared +test: all teststatic testshared -teststatic: allstatic +teststatic: static @echo hello world | ./minigzip | ./minigzip -d || \ echo ' *** minigzip test FAILED ***' ; \ if ./example; then \ @@ -76,8 +85,9 @@ teststatic: allstatic echo ' *** zlib test FAILED ***'; \ fi -testshared: allshared +testshared: shared @LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ + LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \ DYLD_LIBRARY_PATH=`pwd`:$(DYLD_LIBRARY_PATH) ; export DYLD_LIBRARY_PATH; \ SHLIB_PATH=`pwd`:$(SHLIB_PATH) ; export SHLIB_PATH; \ echo hello world | ./minigzipsh | ./minigzipsh -d || \ @@ -88,9 +98,14 @@ testshared: allshared echo ' *** zlib shared test FAILED ***'; \ fi -test: teststatic testshared - -check: test +test64: all64 + @echo hello world | ./minigzip64 | ./minigzip64 -d || \ + echo ' *** minigzip 64-bit test FAILED ***' ; \ + if ./example64; then \ + echo ' *** zlib 64-bit test OK ***'; \ + else \ + echo ' *** zlib 64-bit test FAILED ***'; \ + fi libz.a: $(OBJS) $(AR) $@ $(OBJS) @@ -108,26 +123,43 @@ match.lo: match.S mv _match.o match.lo rm -f _match.s -%.lo: %.c - $(CC) $(SFLAGS) -DPIC -c $< -o $@ +example64.o: example.c zlib.h zconf.h zlibdefs.h + $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -c -o $@ $< + +minigzip64.o: minigzip.c zlib.h zconf.h zlibdefs.h + $(CC) $(CFLAGS) -D_FILE_OFFSET_BITS=64 -c -o $@ $< + +.SUFFIXES: .lo + +.c.lo: + -@if [ ! -d objs ]; then mkdir objs; fi + $(CC) $(SFLAGS) -DPIC -c -o objs/$*.o $< + -@mv objs/$*.o $@ $(SHAREDLIBV): $(PIC_OBJS) - $(LDSHARED) -o $@ $(PIC_OBJS) -lc + $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) -lc rm -f $(SHAREDLIB) $(SHAREDLIBM) ln -s $@ $(SHAREDLIB) ln -s $@ $(SHAREDLIBM) + -@rmdir objs -example$(EXE): example.o $(LIBS) +example$(EXE): example.o $(STATICLIB) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -minigzip$(EXE): minigzip.o $(LIBS) +minigzip$(EXE): minigzip.o $(STATICLIB) $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) -examplesh$(EXE): example.o $(LIBS) - $(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIB) +examplesh$(EXE): example.o $(SHAREDLIBV) + $(CC) $(CFLAGS) -o $@ example.o -L. $(SHAREDLIBV) + +minigzipsh$(EXE): minigzip.o $(SHAREDLIBV) + $(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIBV) + +example64$(EXE): example64.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ example64.o $(LDFLAGS) -minigzipsh$(EXE): minigzip.o $(LIBS) - $(CC) $(CFLAGS) -o $@ minigzip.o -L. $(SHAREDLIB) +minigzip64$(EXE): minigzip64.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ minigzip64.o $(LDFLAGS) install-libs: $(LIBS) -@if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi @@ -168,8 +200,10 @@ mostlyclean: clean clean: rm -f *.o *.lo *~ \ example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \ + example64$(EXE) minigzip64$(EXE) \ libz.* foo.gz so_locations \ _match.s maketree contrib/infback9/*.o + rm -rf objs maintainer-clean: distclean distclean: clean @@ -186,30 +220,20 @@ depend: # DO NOT DELETE THIS LINE -- make depend depends on it. -adler32.o: zlib.h zconf.h zlibdefs.h -compress.o: zlib.h zconf.h zlibdefs.h -crc32.o: crc32.h zlib.h zconf.h zlibdefs.h +adler32.o gzio.o zutil.o: zutil.h zlib.h zconf.h zlibdefs.h +compress.o example.o minigzip.o uncompr.o: zlib.h zconf.h zlibdefs.h +crc32.o: zutil.h zlib.h zconf.h zlibdefs.h crc32.h deflate.o: deflate.h zutil.h zlib.h zconf.h zlibdefs.h -example.o: zlib.h zconf.h zlibdefs.h -gzio.o: zutil.h zlib.h zconf.h zlibdefs.h +infback.o inflate.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inffast.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h -inflate.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h -infback.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inftrees.o: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h -minigzip.o: zlib.h zconf.h zlibdefs.h trees.o: deflate.h zutil.h zlib.h zconf.h zlibdefs.h trees.h -uncompr.o: zlib.h zconf.h zlibdefs.h -zutil.o: zutil.h zlib.h zconf.h zlibdefs.h -adler32.lo: zlib.h zconf.h zlibdefs.h -compress.lo: zlib.h zconf.h zlibdefs.h -crc32.lo: crc32.h zlib.h zconf.h zlibdefs.h +adler32.lo gzio.lo zutil.lo: zutil.h zlib.h zconf.h zlibdefs.h +compress.lo example.lo minigzip.lo uncompr.lo: zlib.h zconf.h zlibdefs.h +crc32.lo: zutil.h zlib.h zconf.h zlibdefs.h crc32.h deflate.lo: deflate.h zutil.h zlib.h zconf.h zlibdefs.h -gzio.lo: zutil.h zlib.h zconf.h zlibdefs.h +infback.lo inflate.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inffast.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h -inflate.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h -infback.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h inflate.h inffast.h inffixed.h inftrees.lo: zutil.h zlib.h zconf.h zlibdefs.h inftrees.h trees.lo: deflate.h zutil.h zlib.h zconf.h zlibdefs.h trees.h -uncompr.lo: zlib.h zconf.h zlibdefs.h -zutil.lo: zutil.h zlib.h zconf.h zlibdefs.h diff --git a/README b/README index 75cae36a..b1446be8 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ ZLIB DATA COMPRESSION LIBRARY -zlib 1.2.3.3 is a general purpose data compression library. All the code is +zlib 1.2.3.4 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) @@ -33,7 +33,7 @@ Mark Nelson wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available in http://dogma.net/markn/articles/zlibtool/zlibtool.htm -The changes made in version 1.2.3.3 are documented in the file ChangeLog. +The changes made in version 1.2.3.4 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory "contrib". diff --git a/adler32.c b/adler32.c index 8bf7dc42..65ad6a5a 100644 --- a/adler32.c +++ b/adler32.c @@ -1,21 +1,15 @@ /* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2007 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ -#define ZLIB_INTERNAL -#include "zlib.h" +#include "zutil.h" #define local static -#ifdef _LARGEFILE64_SOURCE - local uLong adler32_combine_(uLong adler1, uLong adler2, off64_t len2); -#else - local uLong adler32_combine_(uLong adler1, uLong adler2, z_off_t len2); -#endif - +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); #define BASE 65521UL /* largest prime smaller than 65536 */ #define NMAX 5552 @@ -137,11 +131,7 @@ uLong ZEXPORT adler32(adler, buf, len) local uLong adler32_combine_(adler1, adler2, len2) uLong adler1; uLong adler2; -#ifdef _LARGEFILE64_SOURCE - off64_t len2; -#else - z_off_t len2; -#endif + z_off64_t len2; { unsigned long sum1; unsigned long sum2; @@ -154,10 +144,10 @@ local uLong adler32_combine_(adler1, adler2, len2) MOD(sum2); sum1 += (adler2 & 0xffff) + BASE - 1; sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; - if (sum1 > BASE) sum1 -= BASE; - if (sum1 > BASE) sum1 -= BASE; - if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); - if (sum2 > BASE) sum2 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; return sum1 | (sum2 << 16); } @@ -170,20 +160,10 @@ uLong ZEXPORT adler32_combine(adler1, adler2, len2) return adler32_combine_(adler1, adler2, len2); } -#ifdef _LARGEFILE64_SOURCE uLong ZEXPORT adler32_combine64(adler1, adler2, len2) uLong adler1; uLong adler2; - off64_t len2; + z_off64_t len2; { return adler32_combine_(adler1, adler2, len2); } -#else -uLong ZEXPORT adler32_combine64(adler1, adler2, len2) - uLong adler1; - uLong adler2; - z_off_t len2; -{ - return adler32_combine_(adler1, adler2, len2); -} -#endif diff --git a/as400/zlib.inc b/as400/zlib.inc index 4436477d..9ef0b144 100644 --- a/as400/zlib.inc +++ b/as400/zlib.inc @@ -1,7 +1,7 @@ * ZLIB.INC - Interface to the general purpose compression library * * ILE RPG400 version by Patrick Monnerat, DATASPHERE. - * Version 1.2.3.3 + * Version 1.2.3.4 * * * WARNING: @@ -22,8 +22,8 @@ * * Versioning information. * - D ZLIB_VERSION C '1.2.3.3' - D ZLIB_VERNUM C X'1233' + D ZLIB_VERSION C '1.2.3.4' + D ZLIB_VERNUM C X'1234' * * Other equates. * diff --git a/configure b/configure index cc97872b..b87dddcd 100755 --- a/configure +++ b/configure @@ -1,26 +1,22 @@ #!/bin/sh -# configure script for zlib. This script is needed only if -# you wish to build a shared library and your system supports them, -# of if you need special compiler, flags or install directory. -# Otherwise, you can just use directly "make test; make install" +# configure script for zlib. # -# To create a shared library, use "configure --shared"; by default a static -# library is created. If the primitive shared library support provided here -# does not work, use ftp://prep.ai.mit.edu/pub/gnu/libtool-*.tar.gz +# Normally configure builds both a static and a shared library. +# If you want to build just a static library, use: ./configure --static # # To impose specific compiler or flags or install directory, use for example: # prefix=$HOME CC=cc CFLAGS="-O4" ./configure # or for csh/tcsh users: # (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure) -# LDSHARED is the command to be used to create a shared library # Incorrect settings of CC or CFLAGS may prevent creating a shared library. # If you have problems, try without defining CC and CFLAGS before reporting # an error. -LIBS=libz.a -LDFLAGS="-L. ${LIBS}" +STATICLIB=libz.a +LDFLAGS="${LDFLAGS} -L. ${STATICLIB}" VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h` +VER3=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h` VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h` VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < zlib.h` AR=${AR-"ar"} @@ -45,22 +41,22 @@ case "$1" in echo 'usage:' echo ' configure [--shared] [--prefix=PREFIX] [--exec_prefix=EXPREFIX]' echo ' [--libdir=LIBDIR] [--includedir=INCLUDEDIR] [--zprefix]' - exit 0;; - -p*=* | --prefix=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;; - -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;; - -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift;; - -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift;; - -u*=* | --uname=*) uname=`echo $1 | sed 's/[-a-z_]*=//'`;shift;; - -p* | --prefix) prefix="$2"; shift; shift;; - -e* | --eprefix) exec_prefix="$2"; shift; shift;; - -l* | --libdir) libdir="$2"; shift; shift;; - -i* | --includedir) includedir="$2"; shift; shift;; - -s* | --shared | --enable-shared) shared=1; shift;; - -t | --static) shared=0; shift;; - -z* | --zprefix) zprefix=1; shift;; - --sysconfdir=*) echo "ignored option: --sysconfdir"; shift;; - --localstatedir=*) echo "ignored option: --localstatedir"; shift;; - *) echo "unknown option: $1"; echo "$0 --help for help"; exit 1;; + exit 0 ;; + -p*=* | --prefix=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift ;; + -e*=* | --eprefix=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift ;; + -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift ;; + -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift ;; + -u*=* | --uname=*) uname=`echo $1 | sed 's/[-a-z_]*=//'`;shift ;; + -p* | --prefix) prefix="$2"; shift; shift ;; + -e* | --eprefix) exec_prefix="$2"; shift; shift ;; + -l* | --libdir) libdir="$2"; shift; shift ;; + -i* | --includedir) includedir="$2"; shift; shift ;; + -s* | --shared | --enable-shared) shared=1; shift ;; + -t | --static) shared=0; shift ;; + -z* | --zprefix) zprefix=1; shift ;; + --sysconfdir=*) echo "ignored option: --sysconfdir"; shift ;; + --localstatedir=*) echo "ignored option: --localstatedir"; shift ;; + *) echo "unknown option: $1"; echo "$0 --help for help"; exit 1 ;; esac done @@ -75,44 +71,44 @@ cc=${CC-gcc} cflags=${CFLAGS-"-O3"} # to force the asm version use: CFLAGS="-O3 -DASMV" ./configure case "$cc" in - *gcc*) gcc=1;; + *gcc*) gcc=1 ;; esac if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then CC="$cc" SFLAGS="${CFLAGS-"-O3"} -fPIC" CFLAGS="${CFLAGS-"-O3"}" - if test -z $uname; then + if test -z "$uname"; then uname=`(uname -s || echo unknown) 2>/dev/null` fi case "$uname" in - Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map"};; + Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map"} ;; CYGWIN* | Cygwin* | cygwin* | OS/2* ) - EXE='.exe';; + EXE='.exe' ;; QNX*) # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4 # (alain.bonnefoy@icbt.com) - LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"};; + LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"} ;; HP-UX*) LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"} case `(uname -m || echo unknown) 2>/dev/null` in ia64) shared_ext='.so' - SHAREDLIB='libz.so';; + SHAREDLIB='libz.so' ;; *) shared_ext='.sl' - SHAREDLIB='libz.sl';; - esac;; + SHAREDLIB='libz.sl' ;; + esac ;; Darwin*) shared_ext='.dylib' SHAREDLIB=libz$shared_ext SHAREDLIBV=libz.$VER$shared_ext SHAREDLIBM=libz.$VER1$shared_ext - LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER"};; - *) LDSHARED=${LDSHARED-"$cc -shared"};; + LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER3"} ;; + *) LDSHARED=${LDSHARED-"$cc -shared"} ;; esac else # find system name and corresponding cc options CC=${CC-cc} - if test -z $uname; then + if test -z "$uname"; then uname=`(uname -sr || echo unknown) 2>/dev/null` fi case "$uname" in @@ -123,63 +119,64 @@ else case `(uname -m || echo unknown) 2>/dev/null` in ia64) shared_ext='.so' - SHAREDLIB='libz.so';; + SHAREDLIB='libz.so' ;; *) shared_ext='.sl' - SHAREDLIB='libz.sl';; - esac;; + SHAREDLIB='libz.sl' ;; + esac ;; IRIX*) SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."} CFLAGS=${CFLAGS-"-ansi -O2"} - LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"};; + LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"} CFLAGS=${CFLAGS-"-O -std1"} - LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"};; + LDFLAGS="${LDFLAGS} -Wl,-rpath,." + LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"} ;; OSF1*) SFLAGS=${CFLAGS-"-O -std1"} CFLAGS=${CFLAGS-"-O -std1"} - LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"};; + LDSHARED=${LDSHARED-"cc -shared -Wl,-soname,libz.so.1"} ;; QNX*) SFLAGS=${CFLAGS-"-4 -O"} CFLAGS=${CFLAGS-"-4 -O"} LDSHARED=${LDSHARED-"cc"} RANLIB=${RANLIB-"true"} - AR_RC="cc -A";; + AR_RC="cc -A" ;; SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "} CFLAGS=${CFLAGS-"-O3"} - LDSHARED=${LDSHARED-"cc -dy -KPIC -G"};; + LDSHARED=${LDSHARED-"cc -dy -KPIC -G"} ;; SunOS\ 5*) LDSHARED=${LDSHARED-"cc -G"} case `(uname -m || echo unknown) 2>/dev/null` in i86*) SFLAGS=${CFLAGS-"-xpentium -fast -KPIC -R."} - CFLAGS=${CFLAGS-"-xpentium -fast"};; + CFLAGS=${CFLAGS-"-xpentium -fast"} ;; *) SFLAGS=${CFLAGS-"-fast -xcg92 -KPIC -R."} - CFLAGS=${CFLAGS-"-fast -xcg92"};; - esac;; + CFLAGS=${CFLAGS-"-fast -xcg92"} ;; + esac ;; SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"} CFLAGS=${CFLAGS-"-O2"} - LDSHARED=${LDSHARED-"ld"};; + LDSHARED=${LDSHARED-"ld"} ;; SunStudio\ 9*) SFLAGS=${CFLAGS-"-DUSE_MMAP -fast -xcode=pic32 -xtarget=ultra3 -xarch=v9b"} CFLAGS=${CFLAGS-"-DUSE_MMAP -fast -xtarget=ultra3 -xarch=v9b"} - LDSHARED=${LDSHARED-"cc -xarch=v9b"};; + LDSHARED=${LDSHARED-"cc -xarch=v9b"} ;; UNIX_System_V\ 4.2.0) SFLAGS=${CFLAGS-"-KPIC -O"} CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -G"};; + LDSHARED=${LDSHARED-"cc -G"} ;; UNIX_SV\ 4.2MP) SFLAGS=${CFLAGS-"-Kconform_pic -O"} CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -G"};; + LDSHARED=${LDSHARED-"cc -G"} ;; OpenUNIX\ 5) SFLAGS=${CFLAGS-"-KPIC -O"} CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -G"};; + LDSHARED=${LDSHARED-"cc -G"} ;; AIX*) # Courtesy of dbakker@arrayasolutions.com SFLAGS=${CFLAGS-"-O -qmaxmem=8192"} CFLAGS=${CFLAGS-"-O -qmaxmem=8192"} - LDSHARED=${LDSHARED-"xlc -G"};; + LDSHARED=${LDSHARED-"xlc -G"} ;; # send working options for other systems to support@gzip.org *) SFLAGS=${CFLAGS-"-O"} CFLAGS=${CFLAGS-"-O"} - LDSHARED=${LDSHARED-"cc -shared"};; + LDSHARED=${LDSHARED-"cc -shared"} ;; esac fi @@ -190,16 +187,15 @@ SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"} if test $shared -eq 1; then echo Checking for shared library support... # we must test in two steps (cc then ld), required at least on SunOS 4.x - if test "`($CC -c $SFLAGS $test.c) 2>&1`" = "" && + if test "`($CC -w -c $SFLAGS $test.c) 2>&1`" = "" && test "`($LDSHARED -o $test$shared_ext $test.o) 2>&1`" = ""; then - LIBS="$LIBS $SHAREDLIBV" echo Building shared library $SHAREDLIBV with $CC. elif test -z "$old_cc" -a -z "$old_cflags"; then echo No shared library support. shared=0; else - echo Tested $CC -c $SFLAGS $test.c - $CC -c $SFLAGS $test.c + echo Tested $CC -w -c $SFLAGS $test.c + $CC -w -c $SFLAGS $test.c echo Tested $LDSHARED -o $test$shared_ext $test.o $LDSHARED -o $test$shared_ext $test.o echo 'No shared library support; try without defining CC and CFLAGS' @@ -208,12 +204,12 @@ if test $shared -eq 1; then fi if test $shared -eq 0; then LDSHARED="$CC" - ALL="allstatic" - TEST="teststatic" - echo Building static library $LIBS version $VER with $CC. + ALL="static" + TEST="all teststatic" + echo Building static library $STATICLIB version $VER with $CC. else - ALL="allstatic allshared" - TEST="teststatic testshared" + ALL="static shared" + TEST="all teststatic testshared" fi cat > zlibdefs.h << EOF @@ -231,6 +227,8 @@ EOF if test "`($CC -c $CFLAGS -D_LARGEFILE64_SOURCE=1 $test.c) 2>&1`" = ""; then CFLAGS="${CFLAGS} -D_LARGEFILE64_SOURCE=1" SFLAGS="${SFLAGS} -D_LARGEFILE64_SOURCE=1" + ALL="${ALL} all64" + TEST="${TEST} test64" echo "Checking for off64_t... Yes." echo "Checking for fseeko... Yes." else @@ -345,6 +343,7 @@ EOF echo "Checking for return value of vsnprintf()... Yes." else CFLAGS="$CFLAGS -DHAS_vsnprintf_void" + SFLAGS="$SFLAGS -DHAS_vsnprintf_void" echo "Checking for return value of vsnprintf()... No." echo " WARNING: apparently vsnprintf() does not return a value. zlib" echo " can build but will be open to possible string-format security" @@ -352,6 +351,7 @@ EOF fi else CFLAGS="$CFLAGS -DNO_vsnprintf" + SFLAGS="$SFLAGS -DNO_vsnprintf" echo "Checking for vsnprintf() in stdio.h... No." echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" echo " can build but will be open to possible buffer-overflow security" @@ -383,6 +383,7 @@ EOF echo "Checking for return value of vsprintf()... Yes." else CFLAGS="$CFLAGS -DHAS_vsprintf_void" + SFLAGS="$SFLAGS -DHAS_vsprintf_void" echo "Checking for return value of vsprintf()... No." echo " WARNING: apparently vsprintf() does not return a value. zlib" echo " can build but will be open to possible string-format security" @@ -432,6 +433,7 @@ EOF echo "Checking for return value of snprintf()... Yes." else CFLAGS="$CFLAGS -DHAS_snprintf_void" + SFLAGS="$SFLAGS -DHAS_snprintf_void" echo "Checking for return value of snprintf()... No." echo " WARNING: apparently snprintf() does not return a value. zlib" echo " can build but will be open to possible string-format security" @@ -439,6 +441,7 @@ EOF fi else CFLAGS="$CFLAGS -DNO_snprintf" + SFLAGS="$SFLAGS -DNO_snprintf" echo "Checking for snprintf() in stdio.h... No." echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" echo " can build but will be open to possible buffer-overflow security" @@ -464,6 +467,7 @@ EOF echo "Checking for return value of sprintf()... Yes." else CFLAGS="$CFLAGS -DHAS_sprintf_void" + SFLAGS="$SFLAGS -DHAS_sprintf_void" echo "Checking for return value of sprintf()... No." echo " WARNING: apparently sprintf() does not return a value. zlib" echo " can build but will be open to possible string-format security" @@ -481,6 +485,7 @@ if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then else echo "Checking for errno.h... No." CFLAGS="$CFLAGS -DNO_ERRNO_H" + SFLAGS="$SFLAGS -DNO_ERRNO_H" fi cat > $test.c <&1`" = ""; then CFLAGS="$CFLAGS -DUSE_MMAP" + SFLAGS="$SFLAGS -DUSE_MMAP" echo Checking for mmap support... Yes. else echo Checking for mmap support... No. @@ -506,7 +512,7 @@ case $CFLAGS in echo Checking for underline in external names... No. else echo Checking for underline in external names... Yes. - fi;; + fi ;; esac rm -f $test.[co] $test $test$shared_ext @@ -519,7 +525,7 @@ sed < Makefile.in " /^LDFLAGS *=/s#=.*#=$LDFLAGS# /^LDSHARED *=/s#=.*#=$LDSHARED# /^CPP *=/s#=.*#=$CPP# -/^LIBS *=/s#=.*#=$LIBS# +/^STATICLIB *=/s#=.*#=$STATICLIB# /^SHAREDLIB *=/s#=.*#=$SHAREDLIB# /^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# /^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# @@ -540,7 +546,7 @@ sed < zlib.pc.in " /^CFLAGS *=/s#=.*#=$CFLAGS# /^CPP *=/s#=.*#=$CPP# /^LDSHARED *=/s#=.*#=$LDSHARED# -/^LIBS *=/s#=.*#=$LIBS# +/^STATICLIB *=/s#=.*#=$STATICLIB# /^SHAREDLIB *=/s#=.*#=$SHAREDLIB# /^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV# /^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM# diff --git a/contrib/README.contrib b/contrib/README.contrib index 20afc621..f9c1665e 100644 --- a/contrib/README.contrib +++ b/contrib/README.contrib @@ -8,6 +8,10 @@ ada/ by Dmitriy Anisimkov Support for Ada See http://zlib-ada.sourceforge.net/ +amd64/ by Mikhail Teterin + asm code for AMD64 + See patch at http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/96393 + asm586/ asm686/ by Brian Raiter asm code for Pentium and PPro/PII, using the AT&T (GNU as) syntax diff --git a/contrib/amd64/amd64-match.S b/contrib/amd64/amd64-match.S new file mode 100644 index 00000000..b3bf1ac4 --- /dev/null +++ b/contrib/amd64/amd64-match.S @@ -0,0 +1,357 @@ +/* + * match.S -- optimized version of longest_match() + * based on the similar work by Gilles Vollant, and Brian Raiter, written 1998 + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the BSD License. Use by owners of Che Guevarra + * parafernalia is prohibited, where possible, and highly discouraged + * elsewhere. + */ + +#ifndef NO_UNDERLINE +# define match_init _match_init +# define longest_match _longest_match +#endif + +#define scanend ebx +#define scanendw bx +#define chainlenwmask edx /* high word: current chain len low word: s->wmask */ +#define curmatch rsi +#define curmatchd esi +#define windowbestlen r8 +#define scanalign r9 +#define scanalignd r9d +#define window r10 +#define bestlen r11 +#define bestlend r11d +#define scanstart r12d +#define scanstartw r12w +#define scan r13 +#define nicematch r14d +#define limit r15 +#define limitd r15d +#define prev rcx + +/* + * The 258 is a "magic number, not a parameter -- changing it + * breaks the hell loose + */ +#define MAX_MATCH (258) +#define MIN_MATCH (3) +#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1) +#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7) + +/* stack frame offsets */ +#define LocalVarsSize (112) +#define _chainlenwmask ( 8-LocalVarsSize)(%rsp) +#define _windowbestlen (16-LocalVarsSize)(%rsp) +#define save_r14 (24-LocalVarsSize)(%rsp) +#define save_rsi (32-LocalVarsSize)(%rsp) +#define save_rbx (40-LocalVarsSize)(%rsp) +#define save_r12 (56-LocalVarsSize)(%rsp) +#define save_r13 (64-LocalVarsSize)(%rsp) +#define save_r15 (80-LocalVarsSize)(%rsp) + +/* + * On AMD64 the first argument of a function (in our case -- the pointer to + * deflate_state structure) is passed in %rdi, hence our offsets below are + * all off of that. + */ +#ifndef STRUCT_OFFSET +# define STRUCT_OFFSET (0) +#endif +#define dsWSize ( 56 + STRUCT_OFFSET)(%rdi) +#define dsWMask ( 64 + STRUCT_OFFSET)(%rdi) +#define dsWindow ( 72 + STRUCT_OFFSET)(%rdi) +#define dsPrev ( 88 + STRUCT_OFFSET)(%rdi) +#define dsMatchLen (136 + STRUCT_OFFSET)(%rdi) +#define dsPrevMatch (140 + STRUCT_OFFSET)(%rdi) +#define dsStrStart (148 + STRUCT_OFFSET)(%rdi) +#define dsMatchStart (152 + STRUCT_OFFSET)(%rdi) +#define dsLookahead (156 + STRUCT_OFFSET)(%rdi) +#define dsPrevLen (160 + STRUCT_OFFSET)(%rdi) +#define dsMaxChainLen (164 + STRUCT_OFFSET)(%rdi) +#define dsGoodMatch (180 + STRUCT_OFFSET)(%rdi) +#define dsNiceMatch (184 + STRUCT_OFFSET)(%rdi) + +.globl match_init, longest_match + +.text + +/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */ + +longest_match: +/* + * Retrieve the function arguments. %curmatch will hold cur_match + * throughout the entire function (passed via rsi on amd64). + * rdi will hold the pointer to the deflate_state (first arg on amd64) + */ + mov %rsi, save_rsi + mov %rbx, save_rbx + mov %r12, save_r12 + mov %r13, save_r13 + mov %r14, save_r14 + mov %r15, save_r15 + +/* uInt wmask = s->w_mask; */ +/* unsigned chain_length = s->max_chain_length; */ +/* if (s->prev_length >= s->good_match) { */ +/* chain_length >>= 2; */ +/* } */ + + movl dsPrevLen, %eax + movl dsGoodMatch, %ebx + cmpl %ebx, %eax + movl dsWMask, %eax + movl dsMaxChainLen, %chainlenwmask + jl LastMatchGood + shrl $2, %chainlenwmask +LastMatchGood: + +/* chainlen is decremented once beforehand so that the function can */ +/* use the sign flag instead of the zero flag for the exit test. */ +/* It is then shifted into the high word, to make room for the wmask */ +/* value, which it will always accompany. */ + + decl %chainlenwmask + shll $16, %chainlenwmask + orl %eax, %chainlenwmask + +/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */ + + movl dsNiceMatch, %eax + movl dsLookahead, %ebx + cmpl %eax, %ebx + jl LookaheadLess + movl %eax, %ebx +LookaheadLess: movl %ebx, %nicematch + +/* register Bytef *scan = s->window + s->strstart; */ + + mov dsWindow, %window + movl dsStrStart, %limitd + lea (%limit, %window), %scan + +/* Determine how many bytes the scan ptr is off from being */ +/* dword-aligned. */ + + mov %scan, %scanalign + negl %scanalignd + andl $3, %scanalignd + +/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */ +/* s->strstart - (IPos)MAX_DIST(s) : NIL; */ + + movl dsWSize, %eax + subl $MIN_LOOKAHEAD, %eax + xorl %ecx, %ecx + subl %eax, %limitd + cmovng %ecx, %limitd + +/* int best_len = s->prev_length; */ + + movl dsPrevLen, %bestlend + +/* Store the sum of s->window + best_len in %windowbestlen locally, and in memory. */ + + lea (%window, %bestlen), %windowbestlen + mov %windowbestlen, _windowbestlen + +/* register ush scan_start = *(ushf*)scan; */ +/* register ush scan_end = *(ushf*)(scan+best_len-1); */ +/* Posf *prev = s->prev; */ + + movzwl (%scan), %scanstart + movzwl -1(%scan, %bestlen), %scanend + mov dsPrev, %prev + +/* Jump into the main loop. */ + + movl %chainlenwmask, _chainlenwmask + jmp LoopEntry + +.balign 16 + +/* do { + * match = s->window + cur_match; + * if (*(ushf*)(match+best_len-1) != scan_end || + * *(ushf*)match != scan_start) continue; + * [...] + * } while ((cur_match = prev[cur_match & wmask]) > limit + * && --chain_length != 0); + * + * Here is the inner loop of the function. The function will spend the + * majority of its time in this loop, and majority of that time will + * be spent in the first ten instructions. + */ +LookupLoop: + andl %chainlenwmask, %curmatchd + movzwl (%prev, %curmatch, 2), %curmatchd + cmpl %limitd, %curmatchd + jbe LeaveNow + subl $0x00010000, %chainlenwmask + js LeaveNow +LoopEntry: cmpw -1(%windowbestlen, %curmatch), %scanendw + jne LookupLoop + cmpw %scanstartw, (%window, %curmatch) + jne LookupLoop + +/* Store the current value of chainlen. */ + movl %chainlenwmask, _chainlenwmask + +/* %scan is the string under scrutiny, and %prev to the string we */ +/* are hoping to match it up with. In actuality, %esi and %edi are */ +/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */ +/* initialized to -(MAX_MATCH_8 - scanalign). */ + + mov $(-MAX_MATCH_8), %rdx + lea (%curmatch, %window), %windowbestlen + lea MAX_MATCH_8(%windowbestlen, %scanalign), %windowbestlen + lea MAX_MATCH_8(%scan, %scanalign), %prev + +/* the prefetching below makes very little difference... */ + prefetcht1 (%windowbestlen, %rdx) + prefetcht1 (%prev, %rdx) + +/* + * Test the strings for equality, 8 bytes at a time. At the end, + * adjust %rdx so that it is offset to the exact byte that mismatched. + * + * It should be confessed that this loop usually does not represent + * much of the total running time. Replacing it with a more + * straightforward "rep cmpsb" would not drastically degrade + * performance -- unrolling it, for example, makes no difference. + */ +#undef USE_SSE /* works, but is 6-7% slower, than non-SSE... */ +LoopCmps: +#ifdef USE_SSE + /* Preload the SSE registers */ + movdqu (%windowbestlen, %rdx), %xmm1 + movdqu (%prev, %rdx), %xmm2 + pcmpeqb %xmm2, %xmm1 + movdqu 16(%windowbestlen, %rdx), %xmm3 + movdqu 16(%prev, %rdx), %xmm4 + pcmpeqb %xmm4, %xmm3 + movdqu 32(%windowbestlen, %rdx), %xmm5 + movdqu 32(%prev, %rdx), %xmm6 + pcmpeqb %xmm6, %xmm5 + movdqu 48(%windowbestlen, %rdx), %xmm7 + movdqu 48(%prev, %rdx), %xmm8 + pcmpeqb %xmm8, %xmm7 + + /* Check the comparisions' results */ + pmovmskb %xmm1, %rax + notw %ax + bsfw %ax, %ax + jnz LeaveLoopCmps + add $16, %rdx + pmovmskb %xmm3, %rax + notw %ax + bsfw %ax, %ax + jnz LeaveLoopCmps + add $16, %rdx + pmovmskb %xmm5, %rax + notw %ax + bsfw %ax, %ax + jnz LeaveLoopCmps + add $16, %rdx + pmovmskb %xmm7, %rax + notw %ax + bsfw %ax, %ax + jnz LeaveLoopCmps + add $16, %rdx + jmp LoopCmps +LeaveLoopCmps: add %rax, %rdx +#else + mov (%windowbestlen, %rdx), %rax + xor (%prev, %rdx), %rax + jnz LeaveLoopCmps + add $8, %rdx + jnz LoopCmps + jmp LenMaximum +# if 0 +/* + * This three-liner is tantalizingly simple, but bsf is a slow instruction, + * and the complicated alternative down below is quite a bit faster. Sad... + */ +LeaveLoopCmps: bsf %rax, %rax /* find the first non-zero bit */ + shrl $3, %eax /* divide by 8 to get the byte */ + add %rax, %rdx +# else +LeaveLoopCmps: testl $0xFFFFFFFF, %eax /* Check the first 4 bytes */ + jnz Check16 + add $4, %rdx + shr $32, %rax +Check16: testw $0xFFFF, %ax + jnz LenLower + add $2, %rdx + shrl $16, %eax +LenLower: subb $1, %al + adc $0, %rdx +# endif +#endif + +/* Calculate the length of the match. If it is longer than MAX_MATCH, */ +/* then automatically accept it as the best possible match and leave. */ + + lea (%prev, %rdx), %rax + sub %scan, %rax + cmpl $MAX_MATCH, %eax + jge LenMaximum + +/* If the length of the match is not longer than the best match we */ +/* have so far, then forget it and return to the lookup loop. */ + + cmpl %bestlend, %eax + jg LongerMatch + mov _windowbestlen, %windowbestlen + mov dsPrev, %prev + movl _chainlenwmask, %edx + jmp LookupLoop + +/* s->match_start = cur_match; */ +/* best_len = len; */ +/* if (len >= nice_match) break; */ +/* scan_end = *(ushf*)(scan+best_len-1); */ + +LongerMatch: + movl %eax, %bestlend + movl %curmatchd, dsMatchStart + cmpl %nicematch, %eax + jge LeaveNow + + lea (%window, %bestlen), %windowbestlen + mov %windowbestlen, _windowbestlen + + movzwl -1(%scan, %rax), %scanend + mov dsPrev, %prev + movl _chainlenwmask, %chainlenwmask + jmp LookupLoop + +/* Accept the current string, with the maximum possible length. */ + +LenMaximum: + movl $MAX_MATCH, %bestlend + movl %curmatchd, dsMatchStart + +/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */ +/* return s->lookahead; */ + +LeaveNow: + movl dsLookahead, %eax + cmpl %eax, %bestlend + cmovngl %bestlend, %eax +LookaheadRet: + +/* Restore the registers and return from whence we came. */ + + mov save_rsi, %rsi + mov save_rbx, %rbx + mov save_r12, %r12 + mov save_r13, %r13 + mov save_r14, %r14 + mov save_r15, %r15 + + ret + +match_init: ret diff --git a/contrib/infback9/infback9.c b/contrib/infback9/infback9.c index c5547aea..7bbe90ce 100644 --- a/contrib/infback9/infback9.c +++ b/contrib/infback9/infback9.c @@ -1,5 +1,5 @@ /* infback9.c -- inflate deflate64 data using a call-back interface - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -433,7 +433,16 @@ void FAR *out_desc; /* handle error breaks in while */ if (mode == BAD) break; - /* build code tables */ + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftree9.h + concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; lencode = (code const FAR *)(state->next); lenbits = 9; diff --git a/contrib/infback9/inftree9.c b/contrib/infback9/inftree9.c index c24302b5..18353cbc 100644 --- a/contrib/infback9/inftree9.c +++ b/contrib/infback9/inftree9.c @@ -1,5 +1,5 @@ /* inftree9.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate9_copyright[] = - " inflate9 1.2.3.3 Copyright 1995-2006 Mark Adler "; + " inflate9 1.2.3.4 Copyright 1995-2008 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -64,7 +64,7 @@ unsigned short FAR *work; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132, - 133, 133, 133, 133, 144, 201, 203}; + 133, 133, 133, 133, 144, 72, 200}; static const unsigned short dbase[32] = { /* Distance codes 0..31 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, @@ -160,11 +160,10 @@ unsigned short FAR *work; entered in the tables. used keeps track of how many table entries have been allocated from the - provided *table space. It is checked when a LENS table is being made - against the space in *table, ENOUGH, minus the maximum space needed by - the worst case distance code, MAXD. This should never happen, but the - sufficiency of ENOUGH has not been proven exhaustively, hence the check. - This assumes that when type == LENS, bits == 9. + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftree9.h + for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This @@ -203,7 +202,8 @@ unsigned short FAR *work; mask = used - 1; /* mask for comparing low */ /* check available table space */ - if (type == LENS && used >= ENOUGH - MAXD) + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ @@ -270,7 +270,8 @@ unsigned short FAR *work; /* check for enough space */ used += 1U << curr; - if (type == LENS && used >= ENOUGH - MAXD) + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ diff --git a/contrib/infback9/inftree9.h b/contrib/infback9/inftree9.h index a268084e..5ab21f0c 100644 --- a/contrib/infback9/inftree9.h +++ b/contrib/infback9/inftree9.h @@ -1,5 +1,5 @@ /* inftree9.h -- header to use inftree9.c - * Copyright (C) 1995-2003 Mark Adler + * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -35,15 +35,21 @@ typedef struct { 01000000 - invalid code */ -/* Maximum size of dynamic tree. The maximum found in a long but non- - exhaustive search was 1444 code structures (852 for length/literals - and 592 for distances, the latter actually the result of an - exhaustive search). The true maximum is not known, but the value - below is more than safe. */ -#define ENOUGH 2048 -#define MAXD 592 +/* Maximum size of the dynamic table. The maximum number of code structures is + 1446, which is the sum of 852 for literal/length codes and 594 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 32 6 15" for distance codes returns 594. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in infback9.c. If the root table size is changed, + then these maximum sizes would be need to be recalculated and updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 594 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) -/* Type of code to build for inftable() */ +/* Type of code to build for inflate_table9() */ typedef enum { CODES, LENS, diff --git a/contrib/nintendods/Makefile b/contrib/nintendods/Makefile new file mode 100644 index 00000000..21337d01 --- /dev/null +++ b/contrib/nintendods/Makefile @@ -0,0 +1,126 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(shell basename $(CURDIR)) +BUILD := build +SOURCES := ../../ +DATA := data +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -Wall -O2\ + -march=armv5te -mtune=arm946e-s \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := $(ARCH) -march=armv5te -mtune=arm946e-s +LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/lib/libz.a + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + @[ -d $@ ] || mkdir -p include + @cp ../../*.h include + +lib: + @[ -d $@ ] || mkdir -p $@ + +$(BUILD): lib + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/contrib/nintendods/README b/contrib/nintendods/README new file mode 100644 index 00000000..ba7a37db --- /dev/null +++ b/contrib/nintendods/README @@ -0,0 +1,5 @@ +This Makefile requires devkitARM (http://www.devkitpro.org/category/devkitarm/) and works inside "contrib/nds". It is based on a devkitARM template. + +Eduardo Costa +January 3, 2009 + diff --git a/contrib/puff/puff b/contrib/puff/puff new file mode 100755 index 0000000000000000000000000000000000000000..bedac26972202275bee3937d6a07ce081685dc69 GIT binary patch literal 17344 zcmeHPdw3L8mcP|$pn*t5jfiW8G-GgpI7l2HWP(nDDFSL^e6%vFGi^w^F-+1OySqSC z9Vm?lO#DC303&gc5^X>=l{1!p2Mkf750JGZ(? z2k|j8|Lo`2)v2me_nvd^Ilp`EsauuX{o2V7PK{xV@J7ZCt73{OIdpy+@ zRodTbx8974^p=+-8dh?q4Y6-Cb}4L&eZy)yEth$Qj!-yQ?_ZKCJX1&i zOvc7TNB?9Cv$QPXm)=mF!l1I^_KI{V&a<$malko^{V)b~HQ}0Jevjv~^3$l=IkN3x z3Bs{%MMK?i$(cH)!G;15b?Zc8_O)tNP|! zha;gJg@gfg(3voxJ&m8o(-f%phrPj+0cVq^I3@YC3|)?Oti;s#?Dqh0Rq&IgvI50D0q28{o>10U4Jpk$cDdn zeO&W*L(g3I`qRhvC%^y9!{is)K=xAmUv;Cgceah%z7jjNMSfeFm0^2e(~{a+PiSVz zOqf=`7#wHHC`v%eo%R82x2*_;y^S;Ho)#k2UxRHaAQsj>%rD zOsuJuR~trbZvO6_cibj9?lO)%#I;uLev-R3iSh!jZRERztF>gG(4G_vCUI9x%#LfX zQwwqJpmo|#fnvmMhcEJ}g_USiJDAkAEAH2o9wo-LgJ%BNg~q95c%^W~_O6T>Z!2T> zsvQ?Xf_G2vDb)_H`i;p@Q9*K(l zvWh%4QcJ80a=Ghqb!)CtZk%$EkFHi;k*}`PAon5-zeE`*p|^^O(S6~mVxFUHo?VU1 zfh-?dn`;xx3dG7*n4GL~z=Omqr;IPaqB_5{Ixl>+m`~On&Jkpy{apT`vCFZ) zcWm68s}N>$^DU@K^{!P;-U&fS>HFf9MRb2fXS5nIC$|^%|8yE)+Un}!CsDTlB(I)S ztaovJ4~aJwP))lO&^PVx<$LE(8O+h1xC-xc%OCZmOb8SJA8p!Sk4r^ zH%ISFYTe3luJ@T&elO|TW31ehR3jZ==f5@HQ^uZ9JFYM~ZG3R=(DZ>at#9QkFoMsY zB-|T|-QC7JW4QYOdWu&U2z`@q_ZF}06Z7oK$#jpu0N{Yxga30Ooal%=cmV+8?jHUM z04#O&aBZ`?t_!U}t7ytEZ9>nUgvdzjO5o%HK-X!`Q_z`wUcR9na0u4{zOVpF)O8P& zy}O_T3iMYK?a&~@rdUI->`^0^k;;{=qAI_v3N7?dg9s&*EXbAN!>jV)xkFty1kSj& zN5;+vpm{fXA>Aj`8!EDXhlKHTc=$?^iQj$-Kexi69vHZPkk6YWw4UkO0jq1IrBuDY zcm9-~9Bnh#+NH;iQoVioQVMxnG#0KFt}Uj0LfKsV@{Q5mYUJHdlF5?YsI3q-eze%# zu5QiGTbn=25}n7J9Hp);`df(|;Nyd580xzePP(_iPt!h;S2z;?2(FGlA+QtQBx4Ji zl|Pz~JcOI1&>X^@D_r?jI^ogn1eq-!?ZO#wF&>RkiI&Hs-9#wyXiust9zAfHdObz) z=pImJ^dOD|+j~>V@?DaMe(+ggVpxrBEn~|^?y*a#voC#d2 z#aJJ`v)6%fUSTtK=A;YrNAtlGJv_P@;_6m;;|EYrnZJ_F*903ou_rF>w=hxSvKlLY1HD0m*MABMtw0; zEsP*08S}*>8u{p8<=1Y&wf+aQWg~{-<|bdDmk79PVV}7`wI* zRm>tA?trNxi0B-RHMTIYJ$DI=8#!07s%b!qMKYT+8_0^D?|zF>dA zhvYX!?HCY`9fBuNrIV=I(OvH2b8dhtd0o|}KLk6%Q?VJsk4QV_i8}KlO3iZR{*YeW1Zn7E?FjBVF{5JJuCLH$%B@{<|ajo+V z94cqLI1EDNjF{eUnq(>vc#WHz2!Zj|e5;Kv;QuP}|6=L?o6`SEJvQt-E~<>=WW6VD zP+p)d@!rRjMe-*IlhTouNQ7zSmY0!HqW=K~5v59HuuP6+_0joMW>R9Oo68J5F7Uw| znH9@qa#tVevLg>seagsl}!qJ^&I@KW|+fLWJoXCsTq$a?F~9As@|148U5Z`%mP zhaJ8RqJ^mR_?k@ds#DBfKvOH^|O zg2uX!WBw<0P)Ee018L^AZcxB79zB*S!kpqX6$3?>+-%?$bqnsMxZb;ij%vp^>VZSb z_%>qa1JltUsaXA>kms9~$qTI^yM$&<}c3^gcBHYkZ_w99*=sM(da1ki*l7x(L(L${ko21(cH=*?vw~>v; z+heS8pd1DK)jnfTU3EW>n0x*LxPm%YjZ6k*42`y&R@eVKa2Pp49v(~^_eCRabb7=s zg@TL`R|)BMdX5n-)N@FpZVC|*rQVy%Wnp4AP%sS&d4x{17d4_q9xV^$>`d)A&ls}t zmLsTLebY!2-#YPCOte`hu2!u~gjvO_iKj@?B%L&j$Lt);sj>!fj*o9~k)?*T$5y(7 z#;^sj8vOwrGLq-1>jTIK2^S8-_t$N>N+u@2=O`lZAfzZ<)yV&%#4_9JYGKl(RN`bN+B*tZ;;*w2&Iq}bsS9Xw$6%>iJD6otjgSqJ77~{}t%Rcjt@g+c* z@K$=tB+}}7n%SDcBt1!AV%3%GGk+|z%3M6q_;{(Y)0UJ%r9Ju)tAi3hgEN5LijLK; zOxX4O#1dGrA&sUQSwtRw=~N}v4XffQpe67nNkDJjfq5#;q!Zr*gz5Q$Wh|0G1cj6u?&*i zkn@v?na4n(`DCI9l+V~<2U6(e>j3jvGPH8}WBCvL~^rP3L|X_5ue-Qhy` zOF%}+aBKdcVCsOZnoKR!d;@7%p)E)~5%Lnlq<@EDkCEj6b-8tGGdaC~j^HD|XE?e2HKKd*vLJtq% z%m+*M`8Lem+pm9YoV2Oy=!K(s!>9b@{`X5_#>vs@<1sUwr?dmh4gpl+S3m^UbMmWKPIT@rWADIo;$1nkfIZ! z%_K;jXBtm{Pt~+8H0v;pXVaaS*#Ozm05yD0QNMqrIwJ^21a)-cJos-Kw?e^-WS0yD zsfFK;u9VJvYoxH&U8-#ls=~e9G+v{I!FaeCCZePmFNjZa?SOnr@8j-vj+c$7$54>5 z;@*raJH;io{(t#^QZL^Xe$aB`=mczK6zxjoP_Pw%%8&9Z(-xh9JW$JAuCp{_+w0pPUD+ zE-(uCjffCHfN;bsJvqjAqkYsZ!X@3e& zCxqVVO~K1?8$LPs*zp;K&uDzc;N!q&EI#A#8IMmcKIh;=8%dGGDDaI%*}3c@Hieb2 z3Rc6K*-oY?&5B}EaumBVT5%}3N}e)FaVoQva^)VW;EV&=k+vmmPuixmU1VE&v)U#* z$wsp89Q?2ATy{RYkX_6Q*(I!)eTS8?>)CCrmW7zXe$HNEzh^cYx7hN)3qT-E_K${! zQP5$BhdIz>V-sKv{qL;azpSRAzRnqF3fBkx&Z(hsEZ}cg;SBh_PG5b4*Xgfm^g3%A z8Uo9`b>moYF>CZT27)VOQEiPB^3~Kg=t1u|HYXIWtM^|=?binMhB~J|5OxN=HFeN? znKKY{)`y+TYeG(MV^es=I2QU*eG{th)4idvGvISht#dYbYnFLKkaO1ioAmHF)};G< z)22JA32&Wqd42d^NQCvE-&q@|^Fjpgcv3tey|&ittpl6xuMIRd1-+pV{#osu+MqiZ zuLyhL8jml;(i#KHVO|uxg2RndgF`OmTdA;i$*eUzxqRfHaj-An--LeQ| z5y&EtMIehn7J)1RSp>2OWD&?BkVPPiKo)_oH3G-}itTF+OxC$90$BvI2xJk+B9KKO zi$E5EECN{svIt}m$RhCn2m!1wX0#T5GuA56LL{QQK;tX!Yy#+B&{q)+M*8l2d5R{6 zn^JT+=sQ#N8qkYVbPTkgXe^e>!#aUADLNnY`V?IN`ll(n2sEt_n}F3cwEmzyMQ;L4 zYXYg9)+6js(X>9H7qklfv?hU;&Jj&(69!T=txxy_bPg?Cpg;ITJhXP1N8(kraCJdHKA_o9&+6kGD76%1EhCFRmr zS2EAC`rwj!KdP>T9@)~0Mq>OtrIjc&V;jgu7V93Hn6~QI7sq->8m>@9LHT(IbhS+n3&L%_de VX(LoM(#j-Q4Q`7FdBaq}{tpWjO!@!- literal 0 HcmV?d00001 diff --git a/contrib/puff/puff.c b/contrib/puff/puff.c index ce0cc405..df5b79f8 100644 --- a/contrib/puff/puff.c +++ b/contrib/puff/puff.c @@ -1,8 +1,8 @@ /* * puff.c - * Copyright (C) 2002-2004 Mark Adler + * Copyright (C) 2002-2008 Mark Adler * For conditions of distribution and use, see copyright notice in puff.h - * version 1.8, 9 Jan 2004 + * version 2.0, 25 Jul 2008 * * puff.c is a simple inflate written to be an unambiguous way to specify the * deflate format. It is not written for speed but rather simplicity. As a @@ -61,6 +61,12 @@ * 1.7 3 Mar 2003 - Added test code for distribution * - Added zlib-like license * 1.8 9 Jan 2004 - Added some comments on no distance codes case + * 1.9 21 Feb 2008 - Fix bug on 16-bit integer architectures [Pohland] + * - Catch missing end-of-block symbol error + * 2.0 25 Jul 2008 - Add #define to permit distance too far back + * - Add option in TEST code for puff to write the data + * - Add option in TEST code to skip input bytes + * - Allow TEST code to read from piped stdin */ #include /* for setjmp(), longjmp(), and jmp_buf */ @@ -194,7 +200,7 @@ struct huffman { * Decode a code from the stream s using huffman table h. Return the symbol or * a negative value if there is an error. If all of the lengths are zero, i.e. * an empty code, or if the code is incomplete and an invalid code is received, - * then -9 is returned after reading MAXBITS bits. + * then -10 is returned after reading MAXBITS bits. * * Format notes: * @@ -226,14 +232,14 @@ local int decode(struct state *s, struct huffman *h) for (len = 1; len <= MAXBITS; len++) { code |= bits(s, 1); /* get next bit */ count = h->count[len]; - if (code < first + count) /* if length len, return symbol */ + if (code - count < first) /* if length len, return symbol */ return h->symbol[index + (code - first)]; index += count; /* else update for next length */ first += count; first <<= 1; code <<= 1; } - return -9; /* ran out of codes */ + return -10; /* ran out of codes */ } /* @@ -263,7 +269,7 @@ local int decode(struct state *s, struct huffman *h) code |= bitbuf & 1; bitbuf >>= 1; count = *next++; - if (code < first + count) { /* if length len, return symbol */ + if (code - count < first) { /* if length len, return symbol */ s->bitbuf = bitbuf; s->bitcnt = (s->bitcnt - len) & 7; return h->symbol[index + (code - first)]; @@ -280,7 +286,7 @@ local int decode(struct state *s, struct huffman *h) bitbuf = s->in[s->incnt++]; if (left > 8) left = 8; } - return -9; /* ran out of codes */ + return -10; /* ran out of codes */ } #endif /* SLOW */ @@ -448,21 +454,27 @@ local int codes(struct state *s, else if (symbol > 256) { /* length */ /* get and compute length */ symbol -= 257; - if (symbol >= 29) return -9; /* invalid fixed code */ + if (symbol >= 29) return -10; /* invalid fixed code */ len = lens[symbol] + bits(s, lext[symbol]); /* get and check distance */ symbol = decode(s, distcode); if (symbol < 0) return symbol; /* invalid symbol */ dist = dists[symbol] + bits(s, dext[symbol]); +#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR if (dist > s->outcnt) - return -10; /* distance too far back */ + return -11; /* distance too far back */ +#endif /* copy length bytes from distance bytes back */ if (s->out != NIL) { if (s->outcnt + len > s->outlen) return 1; while (len--) { - s->out[s->outcnt] = s->out[s->outcnt - dist]; + s->out[s->outcnt] = +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + dist > s->outcnt ? 0 : +#endif + s->out[s->outcnt - dist]; s->outcnt++; } } @@ -680,6 +692,10 @@ local int dynamic(struct state *s) } } + /* check for end-of-block code -- there better be one! */ + if (lengths[256] == 0) + return -9; + /* build huffman table for literal/length codes */ err = construct(&lencode, lengths, nlen); if (err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) @@ -724,8 +740,9 @@ local int dynamic(struct state *s) * -6: dynamic block code description: repeat more than specified lengths * -7: dynamic block code description: invalid literal/length code lengths * -8: dynamic block code description: invalid distance code lengths - * -9: invalid literal/length or distance code in fixed or dynamic block - * -10: distance is too far back in fixed or dynamic block + * -9: dynamic block code description: missing end-of-block code + * -10: invalid literal/length or distance code in fixed or dynamic block + * -11: distance is too far back in fixed or dynamic block * * Format notes: * @@ -783,54 +800,142 @@ int puff(unsigned char *dest, /* pointer to destination pointer */ } #ifdef TEST -/* Example of how to use puff() */ +/* Examples of how to use puff(). + + Usage: puff [-w] [-nnn] file + ... | puff [-w] [-nnn] + + where file is the input file with deflate data, nnn is the number of bytes + of input to skip before inflating (e.g. to skip a zlib or gzip header), and + -w is used to write the decompressed data to stdout */ + #include #include -#include -#include -local unsigned char *yank(char *name, unsigned long *len) +/* Return size times approximately the cube root of 2, keeping the result as 1, + 3, or 5 times a power of 2 -- the result is always > size, until the result + is the maximum value of an unsigned long, where it remains. This is useful + to keep reallocations less than ~33% over the actual data. */ +local size_t bythirds(size_t size) { - unsigned long size; - unsigned char *buf; + int n; + size_t m; + + m = size; + for (n = 0; m; n++) + m >>= 1; + if (n < 3) + return size + 1; + n -= 3; + m = size >> n; + m += m == 6 ? 2 : 1; + m <<= n; + return m > size ? m : (size_t)(-1); +} + +/* Read the input file *name, or stdin if name is NULL, into allocated memory. + Reallocate to larger buffers until the entire file is read in. Return a + pointer to the allocated data, or NULL if there was a memory allocation + failure. *len is the number of bytes of data read from the input file (even + if load() returns NULL). If the input file was empty or could not be opened + or read, *len is zero. */ +local void *load(char *name, size_t *len) +{ + size_t size; + void *buf, *swap; FILE *in; - struct stat s; *len = 0; - if (stat(name, &s)) return NULL; - if ((s.st_mode & S_IFMT) != S_IFREG) return NULL; - size = (unsigned long)(s.st_size); - if (size == 0 || (off_t)size != s.st_size) return NULL; - in = fopen(name, "r"); - if (in == NULL) return NULL; - buf = malloc(size); - if (buf != NULL && fread(buf, 1, size, in) != size) { - free(buf); - buf = NULL; + buf = malloc(size = 4096); + if (buf == NULL) + return NULL; + in = name == NULL ? stdin : fopen(name, "rb"); + if (in != NULL) { + for (;;) { + *len += fread((char *)buf + *len, 1, size - *len, in); + if (*len < size) break; + size = bythirds(size); + if (size == *len || (swap = realloc(buf, size)) == NULL) { + free(buf); + buf = NULL; + break; + } + buf = swap; + } + fclose(in); } - fclose(in); - *len = size; return buf; } int main(int argc, char **argv) { - int ret; - unsigned char *source; - unsigned long len, sourcelen, destlen; - - if (argc < 2) return 2; - source = yank(argv[1], &len); - if (source == NULL) return 2; - sourcelen = len; - ret = puff(NIL, &destlen, source, &sourcelen); + int ret, skip = 0, put = 0; + char *arg, *name = NULL; + unsigned char *source = NULL, *dest; + size_t len = 0; + unsigned long sourcelen, destlen; + + /* process arguments */ + while (arg = *++argv, --argc) + if (arg[0] == '-') { + if (arg[1] == 'w' && arg[2] == 0) + put = 1; + else if (arg[1] >= '0' && arg[1] <= '9') + skip = atoi(arg + 1); + else { + fprintf(stderr, "invalid option %s\n", arg); + return 3; + } + } + else if (name != NULL) { + fprintf(stderr, "only one file name allowed\n"); + return 3; + } + else + name = arg; + source = load(name, &len); + if (source == NULL) { + fprintf(stderr, "memory allocation failure\n"); + return 4; + } + if (len == 0) { + fprintf(stderr, "could not read %s, or it was empty\n", + name == NULL ? "" : name); + free(source); + return 3; + } + if (skip >= len) { + fprintf(stderr, "skip request of %d leaves no input\n", skip); + free(source); + return 3; + } + + /* test inflate data with offset skip */ + len -= skip; + sourcelen = (unsigned long)len; + ret = puff(NIL, &destlen, source + skip, &sourcelen); if (ret) - printf("puff() failed with return code %d\n", ret); + fprintf(stderr, "puff() failed with return code %d\n", ret); else { - printf("puff() succeeded uncompressing %lu bytes\n", destlen); - if (sourcelen < len) printf("%lu compressed bytes unused\n", - len - sourcelen); + fprintf(stderr, "puff() succeeded uncompressing %lu bytes\n", destlen); + if (sourcelen < len) fprintf(stderr, "%lu compressed bytes unused\n", + len - sourcelen); } + + /* if requested, inflate again and write decompressd data to stdout */ + if (put) { + dest = malloc(destlen); + if (dest == NULL) { + fprintf(stderr, "memory allocation failure\n"); + free(source); + return 4; + } + puff(dest, &destlen, source + skip, &sourcelen); + fwrite(dest, 1, destlen, stdout); + free(dest); + } + + /* clean up */ free(source); return ret; } diff --git a/contrib/puff/puff.h b/contrib/puff/puff.h index ef612520..8d7f5f87 100644 --- a/contrib/puff/puff.h +++ b/contrib/puff/puff.h @@ -1,6 +1,6 @@ /* puff.h - Copyright (C) 2002, 2003 Mark Adler, all rights reserved - version 1.7, 3 Mar 2002 + Copyright (C) 2002-2008 Mark Adler, all rights reserved + version 1.9, 10 Jan 2008 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages diff --git a/contrib/vstudio/vc7/zlib.rc b/contrib/vstudio/vc7/zlib.rc index 39eca7eb..98ca20b5 100644 --- a/contrib/vstudio/vc7/zlib.rc +++ b/contrib/vstudio/vc7/zlib.rc @@ -2,8 +2,8 @@ #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE - FILEVERSION 1,2,3,3 - PRODUCTVERSION 1,2,3,3 + FILEVERSION 1,2,3,4 + PRODUCTVERSION 1,2,3,4 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS 0 FILEOS VOS_DOS_WINDOWS32 @@ -17,7 +17,7 @@ BEGIN BEGIN VALUE "FileDescription", "zlib data compression library\0" - VALUE "FileVersion", "1.2.3.3\0" + VALUE "FileVersion", "1.2.3.4\0" VALUE "InternalName", "zlib\0" VALUE "OriginalFilename", "zlib.dll\0" VALUE "ProductName", "ZLib.DLL\0" diff --git a/crc32.c b/crc32.c index b34a510f..1acc7ed8 100644 --- a/crc32.c +++ b/crc32.c @@ -53,7 +53,7 @@ /* Definitions for doing the crc four data bytes at a time. */ #ifdef BYFOUR -# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ +# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ (((w)&0xff00)<<8)+(((w)&0xff)<<24)) local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, unsigned)); @@ -68,11 +68,7 @@ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); -#ifdef _LARGEFILE64_SOURCE - local uLong crc32_combine_(uLong crc1, uLong crc2, off64_t len2); -#else - local uLong crc32_combine_(uLong crc1, uLong crc2, z_off_t len2); -#endif +local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); #ifdef DYNAMIC_CRC_TABLE @@ -376,23 +372,19 @@ local void gf2_matrix_square(square, mat) local uLong crc32_combine_(crc1, crc2, len2) uLong crc1; uLong crc2; -#ifdef _LARGEFILE64_SOURCE - off64_t len2; -#else - z_off_t len2; -#endif + z_off64_t len2; { int n; unsigned long row; unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ - /* degenerate case */ - if (len2 == 0) + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) return crc1; /* put operator for one zero bit in odd */ - odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ row = 1; for (n = 1; n < GF2_DIM; n++) { odd[n] = row; @@ -441,20 +433,10 @@ uLong ZEXPORT crc32_combine(crc1, crc2, len2) return crc32_combine_(crc1, crc2, len2); } -#ifdef _LARGEFILE64_SOURCE uLong ZEXPORT crc32_combine64(crc1, crc2, len2) uLong crc1; uLong crc2; - off64_t len2; -{ - return crc32_combine_(crc1, crc2, len2); -} -#else -uLong ZEXPORT crc32_combine64(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off_t len2; + z_off64_t len2; { return crc32_combine_(crc1, crc2, len2); } -#endif diff --git a/deflate.c b/deflate.c index fc41b805..db01adfe 100644 --- a/deflate.c +++ b/deflate.c @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2006 Jean-loup Gailly. + * Copyright (C) 1995-2009 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.3.3 Copyright 1995-2006 Jean-loup Gailly "; + " deflate 1.2.3.4 Copyright 1995-2009 Jean-loup Gailly "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -110,11 +110,6 @@ local void check_match OF((deflate_state *s, IPos start, IPos match, #endif /* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - /* Values for max_lazy_match, good_match and max_chain_length, depending on * the desired pack level (0..9). The values given below have been tuned to * exclude worst case performance for pathological files. Better values may be @@ -288,6 +283,8 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + s->high_water = 0; /* nothing written to s->window yet */ + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); @@ -332,8 +329,8 @@ int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) strm->adler = adler32(strm->adler, dictionary, dictLength); if (length < MIN_MATCH) return Z_OK; - if (length > MAX_DIST(s)) { - length = MAX_DIST(s); + if (length > s->w_size) { + length = s->w_size; dictionary += dictLength - length; /* use the tail of the dictionary */ } zmemcpy(s->window, dictionary, length); @@ -513,16 +510,16 @@ uLong ZEXPORT deflateBound(strm, sourceLen) break; case 2: /* gzip wrapper */ wraplen = 18; - if (s->gzhead != NULL) { /* user-supplied gzip header */ - if (s->gzhead->extra != NULL) + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) wraplen += 2 + s->gzhead->extra_len; str = s->gzhead->name; - if (str != NULL) + if (str != Z_NULL) do { wraplen++; } while (*str++); str = s->gzhead->comment; - if (str != NULL) + if (str != Z_NULL) do { wraplen++; } while (*str++); @@ -589,7 +586,7 @@ int ZEXPORT deflate (strm, flush) deflate_state *s; if (strm == Z_NULL || strm->state == Z_NULL || - flush > Z_FINISH || flush < 0) { + flush > Z_BLOCK || flush < 0) { return Z_STREAM_ERROR; } s = strm->state; @@ -613,7 +610,7 @@ int ZEXPORT deflate (strm, flush) put_byte(s, 31); put_byte(s, 139); put_byte(s, 8); - if (s->gzhead == NULL) { + if (s->gzhead == Z_NULL) { put_byte(s, 0); put_byte(s, 0); put_byte(s, 0); @@ -640,7 +637,7 @@ int ZEXPORT deflate (strm, flush) (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != NULL) { + if (s->gzhead->extra != Z_NULL) { put_byte(s, s->gzhead->extra_len & 0xff); put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } @@ -682,7 +679,7 @@ int ZEXPORT deflate (strm, flush) } #ifdef GZIP if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != NULL) { + if (s->gzhead->extra != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { @@ -710,7 +707,7 @@ int ZEXPORT deflate (strm, flush) s->status = NAME_STATE; } if (s->status == NAME_STATE) { - if (s->gzhead->name != NULL) { + if (s->gzhead->name != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; @@ -741,7 +738,7 @@ int ZEXPORT deflate (strm, flush) s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { - if (s->gzhead->comment != NULL) { + if (s->gzhead->comment != Z_NULL) { uInt beg = s->pending; /* start of bytes to update crc */ int val; @@ -840,13 +837,17 @@ int ZEXPORT deflate (strm, flush) if (bstate == block_done) { if (flush == Z_PARTIAL_FLUSH) { _tr_align(s); - } else { /* FULL_FLUSH or SYNC_FLUSH */ + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ _tr_stored_block(s, (char*)0, 0L, 0); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ if (flush == Z_FULL_FLUSH) { CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + } } } flush_pending(strm); @@ -1387,6 +1388,40 @@ local void fill_window(s) */ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } } /* =========================================================================== diff --git a/deflate.h b/deflate.h index 90077d80..f1df04cd 100644 --- a/deflate.h +++ b/deflate.h @@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2005 Jean-loup Gailly + * Copyright (C) 1995-2009 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -260,6 +260,13 @@ typedef struct internal_state { * are always zero. */ + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + } FAR deflate_state; /* Output a byte on the stream. @@ -278,6 +285,10 @@ typedef struct internal_state { * distances are limited to MAX_DIST instead of WSIZE. */ +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + /* in trees.c */ void _tr_init OF((deflate_state *s)); int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); diff --git a/doc/algorithm.txt b/doc/algorithm.txt index b022dde3..34960bdd 100644 --- a/doc/algorithm.txt +++ b/doc/algorithm.txt @@ -121,7 +121,7 @@ At least for deflate's output that generates new trees every several 10's of kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code would take too long if you're only decoding several thousand symbols. At the other extreme, you could make a new table for every bit in the code. In fact, -that's essentially a Huffman tree. But then you spend two much time +that's essentially a Huffman tree. But then you spend too much time traversing the tree while decoding, even for short symbols. So the number of bits for the first lookup table is a trade of the time to diff --git a/examples/README.examples b/examples/README.examples index 5632d7a4..146919c6 100644 --- a/examples/README.examples +++ b/examples/README.examples @@ -1,4 +1,10 @@ -This directory contains examples of the use of zlib. +This directory contains examples of the use of zlib and other relevant +programs and documentation. + +enough.c + calculation and justification of ENOUGH parameter in inftrees.h + - calculates the maximum table space used in inflate tree + construction over all possible Huffman codes fitblk.c compress just enough input to nearly fill a requested output size @@ -23,9 +29,16 @@ gzjoin.c gzlog.c gzlog.h - efficiently maintain a message log file in gzip format - - illustrates use of raw deflate and Z_SYNC_FLUSH - - illustrates use of gzip header extra field + efficiently and robustly maintain a message log file in gzip format + - illustrates use of raw deflate, Z_PARTIAL_FLUSH, deflatePrime(), + and deflateSetDictionary() + - illustrates use of a gzip header extra field + +pigz.c + parallel implementation of gzip compression + - uses pthreads to speed up compression on multiple core machines + - illustrates the use of deflateSetDictionary() with raw deflate + - illustrates the use of crc32_combine() zlib_how.html painfully comprehensive description of zpipe.c (see below) diff --git a/examples/enough.c b/examples/enough.c new file mode 100644 index 00000000..b570707b --- /dev/null +++ b/examples/enough.c @@ -0,0 +1,569 @@ +/* enough.c -- determine the maximum size of inflate's Huffman code tables over + * all possible valid and complete Huffman codes, subject to a length limit. + * Copyright (C) 2007, 2008 Mark Adler + * Version 1.3 17 February 2008 Mark Adler + */ + +/* Version history: + 1.0 3 Jan 2007 First version (derived from codecount.c version 1.4) + 1.1 4 Jan 2007 Use faster incremental table usage computation + Prune examine() search on previously visited states + 1.2 5 Jan 2007 Comments clean up + As inflate does, decrease root for short codes + Refuse cases where inflate would increase root + 1.3 17 Feb 2008 Add argument for initial root table size + Fix bug for initial root table size == max - 1 + Use a macro to compute the history index + */ + +/* + Examine all possible Huffman codes for a given number of symbols and a + maximum code length in bits to determine the maximum table size for zilb's + inflate. Only complete Huffman codes are counted. + + Two codes are considered distinct if the vectors of the number of codes per + length are not identical. So permutations of the symbol assignments result + in the same code for the counting, as do permutations of the assignments of + the bit values to the codes (i.e. only canonical codes are counted). + + We build a code from shorter to longer lengths, determining how many symbols + are coded at each length. At each step, we have how many symbols remain to + be coded, what the last code length used was, and how many bit patterns of + that length remain unused. Then we add one to the code length and double the + number of unused patterns to graduate to the next code length. We then + assign all portions of the remaining symbols to that code length that + preserve the properties of a correct and eventually complete code. Those + properties are: we cannot use more bit patterns than are available; and when + all the symbols are used, there are exactly zero possible bit patterns + remaining. + + The inflate Huffman decoding algorithm uses two-level lookup tables for + speed. There is a single first-level table to decode codes up to root bits + in length (root == 9 in the current inflate implementation). The table + has 1 << root entries and is indexed by the next root bits of input. Codes + shorter than root bits have replicated table entries, so that the correct + entry is pointed to regardless of the bits that follow the short code. If + the code is longer than root bits, then the table entry points to a second- + level table. The size of that table is determined by the longest code with + that root-bit prefix. If that longest code has length len, then the table + has size 1 << (len - root), to index the remaining bits in that set of + codes. Each subsequent root-bit prefix then has its own sub-table. The + total number of table entries required by the code is calculated + incrementally as the number of codes at each bit length is populated. When + all of the codes are shorter than root bits, then root is reduced to the + longest code length, resulting in a single, smaller, one-level table. + + The inflate algorithm also provides for small values of root (relative to + the log2 of the number of symbols), where the shortest code has more bits + than root. In that case, root is increased to the length of the shortest + code. This program, by design, does not handle that case, so it is verified + that the number of symbols is less than 2^(root + 1). + + In order to speed up the examination (by about ten orders of magnitude for + the default arguments), the intermediate states in the build-up of a code + are remembered and previously visited branches are pruned. The memory + required for this will increase rapidly with the total number of symbols and + the maximum code length in bits. However this is a very small price to pay + for the vast speedup. + + First, all of the possible Huffman codes are counted, and reachable + intermediate states are noted by a non-zero count in a saved-results array. + Second, the intermediate states that lead to (root + 1) bit or longer codes + are used to look at all sub-codes from those junctures for their inflate + memory usage. (The amount of memory used is not affected by the number of + codes of root bits or less in length.) Third, the visited states in the + construction of those sub-codes and the associated calculation of the table + size is recalled in order to avoid recalculating from the same juncture. + Beginning the code examination at (root + 1) bit codes, which is enabled by + identifying the reachable nodes, accounts for about six of the orders of + magnitude of improvement for the default arguments. About another four + orders of magnitude come from not revisiting previous states. Out of + approximately 2x10^16 possible Huffman codes, only about 2x10^6 sub-codes + need to be examined to cover all of the possible table memory usage cases + for the default arguments of 286 symbols limited to 15-bit codes. + + Note that an unsigned long long type is used for counting. It is quite easy + to exceed the capacity of an eight-byte integer with a large number of + symbols and a large maximum code length, so multiple-precision arithmetic + would need to replace the unsigned long long arithmetic in that case. This + program will abort if an overflow occurs. The big_t type identifies where + the counting takes place. + + An unsigned long long type is also used for calculating the number of + possible codes remaining at the maximum length. This limits the maximum + code length to the number of bits in a long long minus the number of bits + needed to represent the symbols in a flat code. The code_t type identifies + where the bit pattern counting takes place. + */ + +#include +#include +#include +#include + +#define local static + +/* special data types */ +typedef unsigned long long big_t; /* type for code counting */ +typedef unsigned long long code_t; /* type for bit pattern counting */ +struct tab { /* type for been here check */ + size_t len; /* length of bit vector in char's */ + char *vec; /* allocated bit vector */ +}; + +/* The array for saving results, num[], is indexed with this triplet: + + syms: number of symbols remaining to code + left: number of available bit patterns at length len + len: number of bits in the codes currently being assigned + + Those indices are constrained thusly when saving results: + + syms: 3..totsym (totsym == total symbols to code) + left: 2..syms - 1, but only the evens (so syms == 8 -> 2, 4, 6) + len: 1..max - 1 (max == maximum code length in bits) + + syms == 2 is not saved since that immediately leads to a single code. left + must be even, since it represents the number of available bit patterns at + the current length, which is double the number at the previous length. + left ends at syms-1 since left == syms immediately results in a single code. + (left > sym is not allowed since that would result in an incomplete code.) + len is less than max, since the code completes immediately when len == max. + + The offset into the array is calculated for the three indices with the + first one (syms) being outermost, and the last one (len) being innermost. + We build the array with length max-1 lists for the len index, with syms-3 + of those for each symbol. There are totsym-2 of those, with each one + varying in length as a function of sym. See the calculation of index in + count() for the index, and the calculation of size in main() for the size + of the array. + + For the deflate example of 286 symbols limited to 15-bit codes, the array + has 284,284 entries, taking up 2.17 MB for an 8-byte big_t. More than + half of the space allocated for saved results is actually used -- not all + possible triplets are reached in the generation of valid Huffman codes. + */ + +/* The array for tracking visited states, done[], is itself indexed identically + to the num[] array as described above for the (syms, left, len) triplet. + Each element in the array is further indexed by the (mem, rem) doublet, + where mem is the amount of inflate table space used so far, and rem is the + remaining unused entries in the current inflate sub-table. Each indexed + element is simply one bit indicating whether the state has been visited or + not. Since the ranges for mem and rem are not known a priori, each bit + vector is of a variable size, and grows as needed to accommodate the visited + states. mem and rem are used to calculate a single index in a triangular + array. Since the range of mem is expected in the default case to be about + ten times larger than the range of rem, the array is skewed to reduce the + memory usage, with eight times the range for mem than for rem. See the + calculations for offset and bit in beenhere() for the details. + + For the deflate example of 286 symbols limited to 15-bit codes, the bit + vectors grow to total approximately 21 MB, in addition to the 4.3 MB done[] + array itself. + */ + +/* Globals to avoid propagating constants or constant pointers recursively */ +local int max; /* maximum allowed bit length for the codes */ +local int root; /* size of base code table in bits */ +local int large; /* largest code table so far */ +local size_t size; /* number of elements in num and done */ +local int *code; /* number of symbols assigned to each bit length */ +local big_t *num; /* saved results array for code counting */ +local struct tab *done; /* states already evaluated array */ + +/* Index function for num[] and done[] */ +#define INDEX(i,j,k) (((size_t)((i-1)>>1)*((i-2)>>1)+(j>>1)-1)*(max-1)+k-1) + +/* Free allocated space. Uses globals code, num, and done. */ +local void cleanup(void) +{ + size_t n; + + if (done != NULL) { + for (n = 0; n < size; n++) + if (done[n].len) + free(done[n].vec); + free(done); + } + if (num != NULL) + free(num); + if (code != NULL) + free(code); +} + +/* Return the number of possible Huffman codes using bit patterns of lengths + len through max inclusive, coding syms symbols, with left bit patterns of + length len unused -- return -1 if there is an overflow in the counting. + Keep a record of previous results in num to prevent repeating the same + calculation. Uses the globals max and num. */ +local big_t count(int syms, int len, int left) +{ + big_t sum; /* number of possible codes from this juncture */ + big_t got; /* value returned from count() */ + int least; /* least number of syms to use at this juncture */ + int most; /* most number of syms to use at this juncture */ + int use; /* number of bit patterns to use in next call */ + size_t index; /* index of this case in *num */ + + /* see if only one possible code */ + if (syms == left) + return 1; + + /* note and verify the expected state */ + assert(syms > left && left > 0 && len < max); + + /* see if we've done this one already */ + index = INDEX(syms, left, len); + got = num[index]; + if (got) + return got; /* we have -- return the saved result */ + + /* we need to use at least this many bit patterns so that the code won't be + incomplete at the next length (more bit patterns than symbols) */ + least = (left << 1) - syms; + if (least < 0) + least = 0; + + /* we can use at most this many bit patterns, lest there not be enough + available for the remaining symbols at the maximum length (if there were + no limit to the code length, this would become: most = left - 1) */ + most = (((code_t)left << (max - len)) - syms) / + (((code_t)1 << (max - len)) - 1); + + /* count all possible codes from this juncture and add them up */ + sum = 0; + for (use = least; use <= most; use++) { + got = count(syms - use, len + 1, (left - use) << 1); + sum += got; + if (got == -1 || sum < got) /* overflow */ + return -1; + } + + /* verify that all recursive calls are productive */ + assert(sum != 0); + + /* save the result and return it */ + num[index] = sum; + return sum; +} + +/* Return true if we've been here before, set to true if not. Set a bit in a + bit vector to indicate visiting this state. Each (syms,len,left) state + has a variable size bit vector indexed by (mem,rem). The bit vector is + lengthened if needed to allow setting the (mem,rem) bit. */ +local int beenhere(int syms, int len, int left, int mem, int rem) +{ + size_t index; /* index for this state's bit vector */ + size_t offset; /* offset in this state's bit vector */ + int bit; /* mask for this state's bit */ + size_t length; /* length of the bit vector in bytes */ + char *vector; /* new or enlarged bit vector */ + + /* point to vector for (syms,left,len), bit in vector for (mem,rem) */ + index = INDEX(syms, left, len); + mem -= 1 << root; + offset = (mem >> 3) + rem; + offset = ((offset * (offset + 1)) >> 1) + rem; + bit = 1 << (mem & 7); + + /* see if we've been here */ + length = done[index].len; + if (offset < length && (done[index].vec[offset] & bit) != 0) + return 1; /* done this! */ + + /* we haven't been here before -- set the bit to show we have now */ + + /* see if we need to lengthen the vector in order to set the bit */ + if (length <= offset) { + /* if we have one already, enlarge it, zero out the appended space */ + if (length) { + do { + length <<= 1; + } while (length <= offset); + vector = realloc(done[index].vec, length); + if (vector != NULL) + memset(vector + done[index].len, 0, length - done[index].len); + } + + /* otherwise we need to make a new vector and zero it out */ + else { + length = 1 << (len - root); + while (length <= offset) + length <<= 1; + vector = calloc(length, sizeof(char)); + } + + /* in either case, bail if we can't get the memory */ + if (vector == NULL) { + fputs("abort: unable to allocate enough memory\n", stderr); + cleanup(); + exit(1); + } + + /* install the new vector */ + done[index].len = length; + done[index].vec = vector; + } + + /* set the bit */ + done[index].vec[offset] |= bit; + return 0; +} + +/* Examine all possible codes from the given node (syms, len, left). Compute + the amount of memory required to build inflate's decoding tables, where the + number of code structures used so far is mem, and the number remaining in + the current sub-table is rem. Uses the globals max, code, root, large, and + done. */ +local void examine(int syms, int len, int left, int mem, int rem) +{ + int least; /* least number of syms to use at this juncture */ + int most; /* most number of syms to use at this juncture */ + int use; /* number of bit patterns to use in next call */ + + /* see if we have a complete code */ + if (syms == left) { + /* set the last code entry */ + code[len] = left; + + /* complete computation of memory used by this code */ + while (rem < left) { + left -= rem; + rem = 1 << (len - root); + mem += rem; + } + assert(rem == left); + + /* if this is a new maximum, show the entries used and the sub-code */ + if (mem > large) { + large = mem; + printf("max %d: ", mem); + for (use = root + 1; use <= max; use++) + if (code[use]) + printf("%d[%d] ", code[use], use); + putchar('\n'); + fflush(stdout); + } + + /* remove entries as we drop back down in the recursion */ + code[len] = 0; + return; + } + + /* prune the tree if we can */ + if (beenhere(syms, len, left, mem, rem)) + return; + + /* we need to use at least this many bit patterns so that the code won't be + incomplete at the next length (more bit patterns than symbols) */ + least = (left << 1) - syms; + if (least < 0) + least = 0; + + /* we can use at most this many bit patterns, lest there not be enough + available for the remaining symbols at the maximum length (if there were + no limit to the code length, this would become: most = left - 1) */ + most = (((code_t)left << (max - len)) - syms) / + (((code_t)1 << (max - len)) - 1); + + /* occupy least table spaces, creating new sub-tables as needed */ + use = least; + while (rem < use) { + use -= rem; + rem = 1 << (len - root); + mem += rem; + } + rem -= use; + + /* examine codes from here, updating table space as we go */ + for (use = least; use <= most; use++) { + code[len] = use; + examine(syms - use, len + 1, (left - use) << 1, + mem + (rem ? 1 << (len - root) : 0), rem << 1); + if (rem == 0) { + rem = 1 << (len - root); + mem += rem; + } + rem--; + } + + /* remove entries as we drop back down in the recursion */ + code[len] = 0; +} + +/* Look at all sub-codes starting with root + 1 bits. Look at only the valid + intermediate code states (syms, left, len). For each completed code, + calculate the amount of memory required by inflate to build the decoding + tables. Find the maximum amount of memory required and show the code that + requires that maximum. Uses the globals max, root, and num. */ +local void enough(int syms) +{ + int n; /* number of remaing symbols for this node */ + int left; /* number of unused bit patterns at this length */ + size_t index; /* index of this case in *num */ + + /* clear code */ + for (n = 0; n <= max; n++) + code[n] = 0; + + /* look at all (root + 1) bit and longer codes */ + large = 1 << root; /* base table */ + if (root < max) /* otherwise, there's only a base table */ + for (n = 3; n <= syms; n++) + for (left = 2; left < n; left += 2) + { + /* look at all reachable (root + 1) bit nodes, and the + resulting codes (complete at root + 2 or more) */ + index = INDEX(n, left, root + 1); + if (root + 1 < max && num[index]) /* reachable node */ + examine(n, root + 1, left, 1 << root, 0); + + /* also look at root bit codes with completions at root + 1 + bits (not saved in num, since complete), just in case */ + if (num[index - 1] && n <= left << 1) + examine((n - left) << 1, root + 1, (n - left) << 1, + 1 << root, 0); + } + + /* done */ + printf("done: maximum of %d table entries\n", large); +} + +/* + Examine and show the total number of possible Huffman codes for a given + maximum number of symbols, initial root table size, and maximum code length + in bits -- those are the command arguments in that order. The default + values are 286, 9, and 15 respectively, for the deflate literal/length code. + The possible codes are counted for each number of coded symbols from two to + the maximum. The counts for each of those and the total number of codes are + shown. The maximum number of inflate table entires is then calculated + across all possible codes. Each new maximum number of table entries and the + associated sub-code (starting at root + 1 == 10 bits) is shown. + + To count and examine Huffman codes that are not length-limited, provide a + maximum length equal to the number of symbols minus one. + + For the deflate literal/length code, use "enough". For the deflate distance + code, use "enough 30 6". + + This uses the %llu printf format to print big_t numbers, which assumes that + big_t is an unsigned long long. If the big_t type is changed (for example + to a multiple precision type), the method of printing will also need to be + updated. + */ +int main(int argc, char **argv) +{ + int syms; /* total number of symbols to code */ + int n; /* number of symbols to code for this run */ + big_t got; /* return value of count() */ + big_t sum; /* accumulated number of codes over n */ + + /* set up globals for cleanup() */ + code = NULL; + num = NULL; + done = NULL; + + /* get arguments -- default to the deflate literal/length code */ + syms = 286; + root = 9; + max = 15; + if (argc > 1) { + syms = atoi(argv[1]); + if (argc > 2) { + root = atoi(argv[2]); + if (argc > 3) + max = atoi(argv[3]); + } + } + if (argc > 4 || syms < 2 || root < 1 || max < 1) { + fputs("invalid arguments, need: [sym >= 2 [root >= 1 [max >= 1]]]\n", + stderr); + return 1; + } + + /* if not restricting the code length, the longest is syms - 1 */ + if (max > syms - 1) + max = syms - 1; + + /* determine the number of bits in a code_t */ + n = 0; + while (((code_t)1 << n) != 0) + n++; + + /* make sure that the calculation of most will not overflow */ + if (max > n || syms - 2 >= (((code_t)0 - 1) >> (max - 1))) { + fputs("abort: code length too long for internal types\n", stderr); + return 1; + } + + /* reject impossible code requests */ + if (syms - 1 > ((code_t)1 << max) - 1) { + fprintf(stderr, "%d symbols cannot be coded in %d bits\n", + syms, max); + return 1; + } + + /* allocate code vector */ + code = calloc(max + 1, sizeof(int)); + if (code == NULL) { + fputs("abort: unable to allocate enough memory\n", stderr); + return 1; + } + + /* determine size of saved results array, checking for overflows, + allocate and clear the array (set all to zero with calloc()) */ + if (syms == 2) /* iff max == 1 */ + num = NULL; /* won't be saving any results */ + else { + size = syms >> 1; + if (size > ((size_t)0 - 1) / (n = (syms - 1) >> 1) || + (size *= n, size > ((size_t)0 - 1) / (n = max - 1)) || + (size *= n, size > ((size_t)0 - 1) / sizeof(big_t)) || + (num = calloc(size, sizeof(big_t))) == NULL) { + fputs("abort: unable to allocate enough memory\n", stderr); + cleanup(); + return 1; + } + } + + /* count possible codes for all numbers of symbols, add up counts */ + sum = 0; + for (n = 2; n <= syms; n++) { + got = count(n, 1, 2); + sum += got; + if (got == -1 || sum < got) { /* overflow */ + fputs("abort: can't count that high!\n", stderr); + cleanup(); + return 1; + } + printf("%llu %d-codes\n", got, n); + } + printf("%llu total codes for 2 to %d symbols", sum, syms); + if (max < syms - 1) + printf(" (%d-bit length limit)\n", max); + else + puts(" (no length limit)"); + + /* allocate and clear done array for beenhere() */ + if (syms == 2) + done = NULL; + else if (size > ((size_t)0 - 1) / sizeof(struct tab) || + (done = calloc(size, sizeof(struct tab))) == NULL) { + fputs("abort: unable to allocate enough memory\n", stderr); + cleanup(); + return 1; + } + + /* find and show maximum inflate table usage */ + if (root > max) /* reduce root to max length */ + root = max; + if (syms < ((code_t)1 << (root + 1))) + enough(syms); + else + puts("cannot handle minimum code lengths > root"); + + /* done */ + cleanup(); + return 0; +} diff --git a/examples/gzlog.c b/examples/gzlog.c index b6acdef4..4daf1c2b 100644 --- a/examples/gzlog.c +++ b/examples/gzlog.c @@ -1,413 +1,1058 @@ /* * gzlog.c - * Copyright (C) 2004 Mark Adler + * Copyright (C) 2004, 2008 Mark Adler, all rights reserved * For conditions of distribution and use, see copyright notice in gzlog.h - * version 1.0, 26 Nov 2004 - * + * version 2.0, 25 Apr 2008 */ -#include /* memcmp() */ -#include /* malloc(), free(), NULL */ -#include /* size_t, off_t */ -#include /* read(), close(), sleep(), ftruncate(), */ - /* lseek() */ -#include /* open() */ -#include /* flock() */ -#include "zlib.h" /* deflateInit2(), deflate(), deflateEnd() */ +/* + gzlog provides a mechanism for frequently appending short strings to a gzip + file that is efficient both in execution time and compression ratio. The + strategy is to write the short strings in an uncompressed form to the end of + the gzip file, only compressing when the amount of uncompressed data has + reached a given threshold. + + gzlog also provides protection against interruptions in the process due to + system crashes. The status of the operation is recorded in an extra field + in the gzip file, and is only updated once the gzip file is brought to a + valid state. The last data to be appended or compressed is saved in an + auxiliary file, so that if the operation is interrupted, it can be completed + the next time an append operation is attempted. + + gzlog maintains another auxiliary file with the last 32K of data from the + compressed portion, which is preloaded for the compression of the subsequent + data. This minimizes the impact to the compression ratio of appending. + */ + +/* + Operations Concept: + + Files (log name "foo"): + foo.gz -- gzip file with the complete log + foo.add -- last message to append or last data to compress + foo.dict -- dictionary of the last 32K of data for next compression + foo.temp -- temporary dictionary file for compression after this one + foo.lock -- lock file for reading and writing the other files + foo.repairs -- log file for log file recovery operations (not compressed) + + gzip file structure: + - fixed-length (no file name) header with extra field (see below) + - compressed data ending initially with empty stored block + - uncompressed data filling out originally empty stored block and + subsequent stored blocks as needed (16K max each) + - gzip trailer + - no junk at end (no other gzip streams) + + When appending data, the information in the first three items above plus the + foo.add file are sufficient to recover an interrupted append operation. The + extra field has the necessary information to restore the start of the last + stored block and determine where to append the data in the foo.add file, as + well as the crc and length of the gzip data before the append operation. + + The foo.add file is created before the gzip file is marked for append, and + deleted after the gzip file is marked as complete. So if the append + operation is interrupted, the data to add will still be there. If due to + some external force, the foo.add file gets deleted between when the append + operation was interrupted and when recovery is attempted, the gzip file will + still be restored, but without the appended data. + + When compressing data, the information in the first two items above plus the + foo.add file are sufficient to recover an interrupted compress operation. + The extra field has the necessary information to find the end of the + compressed data, and contains both the crc and length of just the compressed + data and of the complete set of data including the contents of the foo.add + file. + + Again, the foo.add file is maintained during the compress operation in case + of an interruption. If in the unlikely event the foo.add file with the data + to be compressed is missing due to some external force, a gzip file with + just the previous compressed data will be reconstructed. In this case, all + of the data that was to be compressed is lost (approximately one megabyte). + This will not occur if all that happened was an interruption of the compress + operation. + + The third state that is marked is the replacement of the old dictionary with + the new dictionary after a compress operation. Once compression is + complete, the gzip file is marked as being in the replace state. This + completes the gzip file, so an interrupt after being so marked does not + result in recompression. Then the dictionary file is replaced, and the gzip + file is marked as completed. This state prevents the possibility of + restarting compression with the wrong dictionary file. + + All three operations are wrapped by a lock/unlock procedure. In order to + gain exclusive access to the log files, first a foo.lock file must be + exclusively created. When all operations are complete, the lock is + released by deleting the foo.lock file. If when attempting to create the + lock file, it already exists and the modify time of the lock file is more + than five minutes old (set by the PATIENCE define below), then the old + lock file is considered stale and deleted, and the exclusive creation of + the lock file is retried. To assure that there are no false assessments + of the staleness of the lock file, the operations periodically touch the + lock file to update the modified date. + + Following is the definition of the extra field with all of the information + required to enable the above append and compress operations and their + recovery if interrupted. Multi-byte values are stored little endian + (consistent with the gzip format). File pointers are eight bytes long. + The crc's and lengths for the gzip trailer are four bytes long. (Note that + the length at the end of a gzip file is used for error checking only, and + for large files is actually the length modulo 2^32.) The stored block + length is two bytes long. The gzip extra field two-byte identification is + "ap" for append. It is assumed that writing the extra field to the file is + an "atomic" operation. That is, either all of the extra field is written + to the file, or none of it is, if the operation is interrupted right at the + point of updating the extra field. This is a reasonable assumption, since + the extra field is within the first 52 bytes of the file, which is smaller + than any expected block size for a mass storage device (usually 512 bytes or + larger). + + Extra field (35 bytes): + - Pointer to first stored block length -- this points to the two-byte length + of the first stored block, which is followed by the two-byte, one's + complement of that length. The stored block length is preceded by the + three-bit header of the stored block, which is the actual start of the + stored block in the deflate format. See the bit offset field below. + - Pointer to the last stored block length. This is the same as above, but + for the last stored block of the uncompressed data in the gzip file. + Initially this is the same as the first stored block length pointer. + When the stored block gets to 16K (see the MAX_STORE define), then a new + stored block as added, at which point the last stored block length pointer + is different from the first stored block length pointer. When they are + different, the first bit of the last stored block header is eight bits, or + one byte back from the block length. + - Compressed data crc and length. This is the crc and length of the data + that is in the compressed portion of the deflate stream. These are used + only in the event that the foo.add file containing the data to compress is + lost after a compress operation is interrupted. + - Total data crc and length. This is the crc and length of all of the data + stored in the gzip file, compressed and uncompressed. It is used to + reconstruct the gzip trailer when compressing, as well as when recovering + interrupted operations. + - Final stored block length. This is used to quickly find where to append, + and allows the restoration of the original final stored block state when + an append operation is interrupted. + - First stored block start as the number of bits back from the final stored + block first length byte. This value is in the range of 3..10, and is + stored as the low three bits of the final byte of the extra field after + subtracting three (0..7). This allows the last-block bit of the stored + block header to be updated when a new stored block is added, for the case + when the first stored block and the last stored block are the same. (When + they are different, the numbers of bits back is known to be eight.) This + also allows for new compressed data to be appended to the old compressed + data in the compress operation, overwriting the previous first stored + block, or for the compressed data to be terminated and a valid gzip file + reconstructed on the off chance that a compression operation was + interrupted and the data to compress in the foo.add file was deleted. + - The operation in process. This is the next two bits in the last byte (the + bits under the mask 0x18). The are interpreted as 0: nothing in process, + 1: append in process, 2: compress in process, 3: replace in process. + - The top three bits of the last byte in the extra field are reserved and + are currently set to zero. + + Main procedure: + - Exclusively create the foo.lock file using the O_CREAT and O_EXCL modes of + the system open() call. If the modify time of an existing lock file is + more than PATIENCE seconds old, then the lock file is deleted and the + exclusive create is retried. + - Load the extra field from the foo.gz file, and see if an operation was in + progress but not completed. If so, apply the recovery procedure below. + - Perform the append procedure with the provided data. + - If the uncompressed data in the foo.gz file is 1MB or more, apply the + compress procedure. + - Delete the foo.lock file. + + Append procedure: + - Put what to append in the foo.add file so that the operation can be + restarted if this procedure is interrupted. + - Mark the foo.gz extra field with the append operation in progress. + + Restore the original last-block bit and stored block length of the last + stored block from the information in the extra field, in case a previous + append operation was interrupted. + - Append the provided data to the last stored block, creating new stored + blocks as needed and updating the stored blocks last-block bits and + lengths. + - Update the crc and length with the new data, and write the gzip trailer. + - Write over the extra field (with a single write operation) with the new + pointers, lengths, and crc's, and mark the gzip file as not in process. + Though there is still a foo.add file, it will be ignored since nothing + is in process. If a foo.add file is leftover from a previously + completed operation, it is truncated when writing new data to it. + - Delete the foo.add file. + + Compress and replace procedures: + - Read all of the uncompressed data in the stored blocks in foo.gz and write + it to foo.add. Also write foo.temp with the last 32K of that data to + provide a dictionary for the next invocation of this procedure. + - Rewrite the extra field marking foo.gz with a compression in process. + * If there is no data provided to compress (due to a missing foo.add file + when recovering), reconstruct and truncate the foo.gz file to contain + only the previous compressed data and proceed to the step after the next + one. Otherwise ... + - Compress the data with the dictionary in foo.dict, and write to the + foo.gz file starting at the bit immediately following the last previously + compressed block. If there is no foo.dict, proceed anyway with the + compression at slightly reduced efficiency. (For the foo.dict file to be + missing requires some external failure beyond simply the interruption of + a compress operation.) During this process, the foo.lock file is + periodically touched to assure that that file is not considered stale by + another process before we're done. The deflation is terminated with a + non-last empty static block (10 bits long), that is then located and + written over by a last-bit-set empty stored block. + - Append the crc and length of the data in the gzip file (previously + calculated during the append operations). + - Write over the extra field with the updated stored block offsets, bits + back, crc's, and lengths, and mark foo.gz as in process for a replacement + of the dictionary. + @ Delete the foo.add file. + - Replace foo.dict with foo.temp. + - Write over the extra field, marking foo.gz as complete. + + Recovery procedure: + - If not a replace recovery, read in the foo.add file, and provide that data + to the appropriate recovery below. If there is no foo.add file, provide + a zero data length to the recovery. In that case, the append recovery + restores the foo.gz to the previous compressed + uncompressed data state. + For the the compress recovery, a missing foo.add file results in foo.gz + being restored to the previous compressed-only data state. + - Append recovery: + - Pick up append at + step above + - Compress recovery: + - Pick up compress at * step above + - Replace recovery: + - Pick up compress at @ step above + - Log the repair with a date stamp in foo.repairs + */ + +#include +#include /* rename, fopen, fprintf, fclose */ +#include /* malloc, free */ +#include /* strlen, strrchr, strcpy, strncpy, strcmp */ +#include /* open */ +#include /* lseek, read, write, close, unlink, sleep, */ + /* ftruncate, fsync */ +#include /* errno */ +#include /* time, ctime */ +#include /* stat */ +#include /* utimes */ +#include "zlib.h" /* crc32 */ + +#include "gzlog.h" /* header for external access */ -#include "gzlog.h" /* interface */ #define local static +typedef unsigned int uint; +typedef unsigned long ulong; + +/* Macro for debugging to deterministically force recovery operations */ +#ifdef DEBUG + #include /* longjmp */ + jmp_buf gzlog_jump; /* where to go back to */ + int gzlog_bail = 0; /* which point to bail at (1..8) */ + int gzlog_count = -1; /* number of times through to wait */ +# define BAIL(n) do { if (n == gzlog_bail && gzlog_count-- == 0) \ + longjmp(gzlog_jump, gzlog_bail); } while (0) +#else +# define BAIL(n) +#endif + +/* how old the lock file can be in seconds before considering it stale */ +#define PATIENCE 300 + +/* maximum stored block size in Kbytes -- must be in 1..63 */ +#define MAX_STORE 16 -/* log object structure */ -typedef struct { - int id; /* object identifier */ - int fd; /* log file descriptor */ - off_t extra; /* offset of extra "ap" subfield */ - off_t mark_off; /* offset of marked data */ - off_t last_off; /* offset of last block */ - unsigned long crc; /* uncompressed crc */ - unsigned long len; /* uncompressed length (modulo 2^32) */ - unsigned stored; /* length of current stored block */ -} gz_log; - -#define GZLOGID 19334 /* gz_log object identifier */ - -#define LOCK_RETRY 1 /* retry lock once a second */ -#define LOCK_PATIENCE 1200 /* try about twenty minutes before forcing */ - -/* acquire a lock on a file */ -local int lock(int fd) +/* number of stored Kbytes to trigger compression (must be >= 32 to allow + dictionary construction, and <= 204 * MAX_STORE, in order for >> 10 to + discard the stored block headers contribution of five bytes each) */ +#define TRIGGER 1024 + +/* size of a deflate dictionary (this cannot be changed) */ +#define DICT 32768U + +/* values for the operation (2 bits) */ +#define NO_OP 0 +#define APPEND_OP 1 +#define COMPRESS_OP 2 +#define REPLACE_OP 3 + +/* macros to extract little-endian integers from an unsigned byte buffer */ +#define PULL2(p) ((p)[0]+((uint)((p)[1])<<8)) +#define PULL4(p) (PULL2(p)+((ulong)PULL2(p+2)<<16)) +#define PULL8(p) (PULL4(p)+((off_t)PULL4(p+4)<<32)) + +/* macros to store integers into a byte buffer in little-endian order */ +#define PUT2(p,a) do {(p)[0]=a;(p)[1]=(a)>>8;} while(0) +#define PUT4(p,a) do {PUT2(p,a);PUT2(p+2,a>>16);} while(0) +#define PUT8(p,a) do {PUT4(p,a);PUT4(p+4,a>>32);} while(0) + +/* internal structure for log information */ +#define LOGID "\106\035\172" /* should be three non-zero characters */ +struct log { + char id[4]; /* contains LOGID to detect inadvertent overwrites */ + int fd; /* file descriptor for .gz file, opened read/write */ + char *path; /* allocated path, e.g. "/var/log/foo" or "foo" */ + char *end; /* end of path, for appending suffices such as ".gz" */ + off_t first; /* offset of first stored block first length byte */ + int back; /* location of first block id in bits back from first */ + uint stored; /* bytes currently in last stored block */ + off_t last; /* offset of last stored block first length byte */ + ulong ccrc; /* crc of compressed data */ + ulong clen; /* length (modulo 2^32) of compressed data */ + ulong tcrc; /* crc of total data */ + ulong tlen; /* length (modulo 2^32) of total data */ + time_t lock; /* last modify time of our lock file */ +}; + +/* gzip header for gzlog */ +local unsigned char log_gzhead[] = { + 0x1f, 0x8b, /* magic gzip id */ + 8, /* compression method is deflate */ + 4, /* there is an extra field (no file name) */ + 0, 0, 0, 0, /* no modification time provided */ + 0, 0xff, /* no extra flags, no OS specified */ + 39, 0, 'a', 'p', 35, 0 /* extra field with "ap" subfield */ + /* 35 is EXTRA, 39 is EXTRA + 4 */ +}; + +#define HEAD sizeof(log_gzhead) /* should be 16 */ + +/* initial gzip extra field content (52 == HEAD + EXTRA + 1) */ +local unsigned char log_gzext[] = { + 52, 0, 0, 0, 0, 0, 0, 0, /* offset of first stored block length */ + 52, 0, 0, 0, 0, 0, 0, 0, /* offset of last stored block length */ + 0, 0, 0, 0, 0, 0, 0, 0, /* compressed data crc and length */ + 0, 0, 0, 0, 0, 0, 0, 0, /* total data crc and length */ + 0, 0, /* final stored block data length */ + 5 /* op is NO_OP, last bit 8 bits back */ +}; + +#define EXTRA sizeof(log_gzext) /* should be 35 */ + +/* initial gzip data and trailer */ +local unsigned char log_gzbody[] = { + 1, 0, 0, 0xff, 0xff, /* empty stored block (last) */ + 0, 0, 0, 0, /* crc */ + 0, 0, 0, 0 /* uncompressed length */ +}; + +#define BODY sizeof(log_gzbody) + +/* Exclusively create foo.lock in order to negotiate exclusive access to the + foo.* files. If the modify time of an existing lock file is greater than + PATIENCE seconds in the past, then consider the lock file to have been + abandoned, delete it, and try the exclusive create again. Save the lock + file modify time for verification of ownership. Return 0 on success, or -1 + on failure, usually due to an access restriction or invalid path. Note that + if stat() or unlink() fails, it may be due to another process noticing the + abandoned lock file a smidge sooner and deleting it, so those are not + flagged as an error. */ +local int log_lock(struct log *log) { - int patience; + int fd; + struct stat st; - /* try to lock every LOCK_RETRY seconds for LOCK_PATIENCE seconds */ - patience = LOCK_PATIENCE; - do { - if (flock(fd, LOCK_EX + LOCK_NB) == 0) - return 0; - (void)sleep(LOCK_RETRY); - patience -= LOCK_RETRY; - } while (patience > 0); + strcpy(log->end, ".lock"); + while ((fd = open(log->path, O_CREAT | O_EXCL, 0644)) < 0) { + if (errno != EEXIST) + return -1; + if (stat(log->path, &st) == 0 && time(NULL) - st.st_mtime > PATIENCE) { + unlink(log->path); + continue; + } + sleep(2); /* relinquish the CPU for two seconds while waiting */ + } + close(fd); + if (stat(log->path, &st) == 0) + log->lock = st.st_mtime; + return 0; +} - /* we've run out of patience -- give up */ - return -1; +/* Update the modify time of the lock file to now, in order to prevent another + task from thinking that the lock is stale. Save the lock file modify time + for verification of ownership. */ +local void log_touch(struct log *log) +{ + struct stat st; + + strcpy(log->end, ".lock"); + utimes(log->path, NULL); + if (stat(log->path, &st) == 0) + log->lock = st.st_mtime; } -/* release lock */ -local void unlock(int fd) +/* Check the log file modify time against what is expected. Return true if + this is not our lock. If it is our lock, touch it to keep it. */ +local int log_check(struct log *log) { - (void)flock(fd, LOCK_UN); + struct stat st; + + strcpy(log->end, ".lock"); + if (stat(log->path, &st) || st.st_mtime != log->lock) + return 1; + log_touch(log); + return 0; } -/* release a log object */ -local void log_clean(gz_log *log) +/* Unlock a previously acquired lock, but only if it's ours. */ +local void log_unlock(struct log *log) { - unlock(log->fd); - (void)close(log->fd); - free(log); + if (log_check(log)) + return; + strcpy(log->end, ".lock"); + unlink(log->path); + log->lock = 0; } -/* read an unsigned long from a byte buffer little-endian */ -local unsigned long make_ulg(unsigned char *buf) +/* Check the gzip header and read in the extra field, filling in the values in + the log structure. Return op on success or -1 if the gzip header was not as + expected. op is the current operation in progress last written to the extra + field. This assumes that the gzip file has already been opened, with the + file descriptor log->fd. */ +local int log_head(struct log *log) { - int n; - unsigned long val; + int op; + unsigned char buf[HEAD + EXTRA]; - val = (unsigned long)(*buf++); - for (n = 8; n < 32; n += 8) - val += (unsigned long)(*buf++) << n; - return val; + if (lseek(log->fd, 0, SEEK_SET) < 0 || + read(log->fd, buf, HEAD + EXTRA) != HEAD + EXTRA || + memcmp(buf, log_gzhead, HEAD)) { + return -1; + } + log->first = PULL8(buf + HEAD); + log->last = PULL8(buf + HEAD + 8); + log->ccrc = PULL4(buf + HEAD + 16); + log->clen = PULL4(buf + HEAD + 20); + log->tcrc = PULL4(buf + HEAD + 24); + log->tlen = PULL4(buf + HEAD + 28); + log->stored = PULL2(buf + HEAD + 32); + log->back = 3 + (buf[HEAD + 34] & 7); + op = (buf[HEAD + 34] >> 3) & 3; + return op; } -/* read an off_t from a byte buffer little-endian */ -local off_t make_off(unsigned char *buf) +/* Write over the extra field contents, marking the operation as op. Use fsync + to assure that the device is written to, and in the requested order. This + operation, and only this operation, is assumed to be atomic in order to + assure that the log is recoverable in the event of an interruption at any + point in the process. Return -1 if the write to foo.gz failed. */ +local int log_mark(struct log *log, int op) { - int n; - off_t val; + int ret; + unsigned char ext[EXTRA]; - val = (off_t)(*buf++); - for (n = 8; n < 64; n += 8) - val += (off_t)(*buf++) << n; - return val; + PUT8(ext, log->first); + PUT8(ext + 8, log->last); + PUT4(ext + 16, log->ccrc); + PUT4(ext + 20, log->clen); + PUT4(ext + 24, log->tcrc); + PUT4(ext + 28, log->tlen); + PUT2(ext + 32, log->stored); + ext[34] = log->back - 3 + (op << 3); + fsync(log->fd); + ret = lseek(log->fd, HEAD, SEEK_SET) < 0 || + write(log->fd, ext, EXTRA) != EXTRA ? -1 : 0; + fsync(log->fd); + return ret; } -/* write an unsigned long little-endian to byte buffer */ -local void dice_ulg(unsigned long val, unsigned char *buf) +/* Rewrite the last block header bits and subsequent zero bits to get to a byte + boundary, setting the last block bit if last is true, and then write the + remainder of the stored block header (length and one's complement). Leave + the file pointer after the end of the last stored block data. Return -1 if + there is a read or write failure on the foo.gz file */ +local int log_last(struct log *log, int last) { - int n; + int back, len, mask; + unsigned char buf[6]; + + /* determine the locations of the bytes and bits to modify */ + back = log->last == log->first ? log->back : 8; + len = back > 8 ? 2 : 1; /* bytes back from log->last */ + mask = 0x80 >> ((back - 1) & 7); /* mask for block last-bit */ + + /* get the byte to modify (one or two back) into buf[0] -- don't need to + read the byte if the last-bit is eight bits back, since in that case + the entire byte will be modified */ + buf[0] = 0; + if (back != 8 && (lseek(log->fd, log->last - len, SEEK_SET) < 0 || + read(log->fd, buf, 1) != 1)) + return -1; + + /* change the last-bit of the last stored block as requested -- note + that all bits above the last-bit are set to zero, per the type bits + of a stored block being 00 and per the convention that the bits to + bring the stream to a byte boundary are also zeros */ + buf[1] = 0; + buf[2 - len] = (*buf & (mask - 1)) + (last ? mask : 0); - for (n = 0; n < 4; n++) { - *buf++ = val & 0xff; - val >>= 8; + /* write the modified stored block header and lengths, move the file + pointer to after the last stored block data */ + PUT2(buf + 2, log->stored); + PUT2(buf + 4, log->stored ^ 0xffff); + return lseek(log->fd, log->last - len, SEEK_SET) < 0 || + write(log->fd, buf + 2 - len, len + 4) != len + 4 || + lseek(log->fd, log->stored, SEEK_CUR) < 0 ? -1 : 0; +} + +/* Append len bytes from data to the locked and open log file. len may be zero + if recovering and no .add file was found. In that case, the previous state + of the foo.gz file is restored. The data is appended uncompressed in + deflate stored blocks. Return -1 if there was an error reading or writing + the foo.gz file. */ +local int log_append(struct log *log, unsigned char *data, size_t len) +{ + uint put; + off_t end; + unsigned char buf[8]; + + /* set the last block last-bit and length, in case recovering an + interrupted append, then position the file pointer to append to the + block */ + if (log_last(log, 1)) + return -1; + + /* append, adding stored blocks and updating the offset of the last stored + block as needed, and update the total crc and length */ + while (len) { + /* append as much as we can to the last block */ + put = (MAX_STORE << 10) - log->stored; + if (put > len) + put = (uint)len; + if (put) { + if (write(log->fd, data, put) != put) + return -1; + BAIL(1); + log->tcrc = crc32(log->tcrc, data, put); + log->tlen += put; + log->stored += put; + data += put; + len -= put; + } + + /* if we need to, add a new empty stored block */ + if (len) { + /* mark current block as not last */ + if (log_last(log, 0)) + return -1; + + /* point to new, empty stored block */ + log->last += 4 + log->stored + 1; + log->stored = 0; + } + + /* mark last block as last, update its length */ + if (log_last(log, 1)) + return -1; + BAIL(2); } + + /* write the new crc and length trailer, and truncate just in case (could + be recovering from partial append with a missing foo.add file) */ + PUT4(buf, log->tcrc); + PUT4(buf + 4, log->tlen); + if (write(log->fd, buf, 8) != 8 || + (end = lseek(log->fd, 0, SEEK_CUR)) < 0 || ftruncate(log->fd, end)) + return -1; + + /* write the extra field, marking the log file as done, delete .add file */ + if (log_mark(log, NO_OP)) + return -1; + strcpy(log->end, ".add"); + unlink(log->path); /* ignore error, since may not exist */ + return 0; } -/* write an off_t little-endian to byte buffer */ -local void dice_off(off_t val, unsigned char *buf) +/* Replace the foo.dict file with the foo.temp file. Also delete the foo.add + file, since the compress operation may have been interrupted before that was + done. Returns 1 if memory could not be allocated, or -1 if reading or + writing foo.gz fails, or if the rename fails for some reason other than + foo.temp not existing. foo.temp not existing is a permitted error, since + the replace operation may have been interrupted after the rename is done, + but before foo.gz is marked as complete. */ +local int log_replace(struct log *log) { - int n; + int ret; + char *dest; + + /* delete foo.add file */ + strcpy(log->end, ".add"); + unlink(log->path); /* ignore error, since may not exist */ + BAIL(3); + + /* rename foo.name to foo.dict, replacing foo.dict if it exists */ + strcpy(log->end, ".dict"); + dest = malloc(strlen(log->path) + 1); + if (dest == NULL) + return -2; + strcpy(dest, log->path); + strcpy(log->end, ".temp"); + ret = rename(log->path, dest); + free(dest); + if (ret && errno != ENOENT) + return -1; + BAIL(4); - for (n = 0; n < 8; n++) { - *buf++ = val & 0xff; - val >>= 8; + /* mark the foo.gz file as done */ + return log_mark(log, NO_OP); +} + +/* Compress the len bytes at data and append the compressed data to the + foo.gz deflate data immediately after the previous compressed data. This + overwrites the previous uncompressed data, which was stored in foo.add + and is the data provided in data[0..len-1]. If this operation is + interrupted, it picks up at the start of this routine, with the foo.add + file read in again. If there is no data to compress (len == 0), then we + simply terminate the foo.gz file after the previously compressed data, + appending a final empty stored block and the gzip trailer. Return -1 if + reading or writing the log.gz file failed, or -2 if there was a memory + allocation failure. */ +local int log_compress(struct log *log, unsigned char *data, size_t len) +{ + int fd; + uint got, max; + ssize_t dict; + off_t end; + z_stream strm; + unsigned char buf[DICT]; + + /* compress and append compressed data */ + if (len) { + /* set up for deflate, allocating memory */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, + Z_DEFAULT_STRATEGY) != Z_OK) + return -2; + + /* read in dictionary (last 32K of data that was compressed) */ + strcpy(log->end, ".dict"); + fd = open(log->path, O_RDONLY, 0); + if (fd >= 0) { + dict = read(fd, buf, DICT); + close(fd); + if (dict < 0) { + deflateEnd(&strm); + return -1; + } + if (dict) + deflateSetDictionary(&strm, buf, (uint)dict); + } + log_touch(log); + + /* prime deflate with last bits of previous block, position write + pointer to write those bits and overwrite what follows */ + if (lseek(log->fd, log->first - (log->back > 8 ? 2 : 1), + SEEK_SET) < 0 || + read(log->fd, buf, 1) != 1 || lseek(log->fd, -1, SEEK_CUR) < 0) { + deflateEnd(&strm); + return -1; + } + deflatePrime(&strm, (8 - log->back) & 7, *buf); + + /* compress, finishing with a partial non-last empty static block */ + strm.next_in = data; + max = (((uint)0 - 1) >> 1) + 1; /* in case int smaller than size_t */ + do { + strm.avail_in = len > max ? max : (uint)len; + len -= strm.avail_in; + do { + strm.avail_out = DICT; + strm.next_out = buf; + deflate(&strm, len ? Z_NO_FLUSH : Z_PARTIAL_FLUSH); + got = DICT - strm.avail_out; + if (got && write(log->fd, buf, got) != got) { + deflateEnd(&strm); + return -1; + } + log_touch(log); + } while (strm.avail_out == 0); + } while (len); + deflateEnd(&strm); + BAIL(5); + + /* find start of empty static block -- scanning backwards the first one + bit is the second bit of the block, if the last byte is zero, then + we know the byte before that has a one in the top bit, since an + empty static block is ten bits long */ + if ((log->first = lseek(log->fd, -1, SEEK_CUR)) < 0 || + read(log->fd, buf, 1) != 1) + return -1; + log->first++; + if (*buf) { + log->back = 1; + while ((*buf & ((uint)1 << (8 - log->back++))) == 0) + ; /* guaranteed to terminate, since *buf != 0 */ + } + else + log->back = 10; + + /* update compressed crc and length */ + log->ccrc = log->tcrc; + log->clen = log->tlen; + } + else { + /* no data to compress -- fix up existing gzip stream */ + log->tcrc = log->ccrc; + log->tlen = log->clen; } + + /* complete and truncate gzip stream */ + log->last = log->first; + log->stored = 0; + PUT4(buf, log->tcrc); + PUT4(buf + 4, log->tlen); + if (log_last(log, 1) || write(log->fd, buf, 8) != 8 || + (end = lseek(log->fd, 0, SEEK_CUR)) < 0 || ftruncate(log->fd, end)) + return -1; + BAIL(6); + + /* mark as being in the replace operation */ + if (log_mark(log, REPLACE_OP)) + return -1; + + /* execute the replace operation and mark the file as done */ + return log_replace(log); } -/* initial, empty gzip file for appending */ -local char empty_gz[] = { - 0x1f, 0x8b, /* magic gzip id */ - 8, /* compression method is deflate */ - 4, /* there is an extra field */ - 0, 0, 0, 0, /* no modification time provided */ - 0, 0xff, /* no extra flags, no OS */ - 20, 0, 'a', 'p', 16, 0, /* extra field with "ap" subfield */ - 32, 0, 0, 0, 0, 0, 0, 0, /* offset of uncompressed data */ - 32, 0, 0, 0, 0, 0, 0, 0, /* offset of last block */ - 1, 0, 0, 0xff, 0xff, /* empty stored block (last) */ - 0, 0, 0, 0, /* crc */ - 0, 0, 0, 0 /* uncompressed length */ -}; +/* log a repair record to the .repairs file */ +local void log_log(struct log *log, int op, char *record) +{ + time_t now; + FILE *rec; -/* initialize a log object with locking */ -void *gzlog_open(char *path) + now = time(NULL); + strcpy(log->end, ".repairs"); + rec = fopen(log->path, "a"); + if (rec == NULL) + return; + fprintf(rec, "%.24s %s recovery: %s\n", ctime(&now), op == APPEND_OP ? + "append" : (op == COMPRESS_OP ? "compress" : "replace"), record); + fclose(rec); + return; +} + +/* Recover the interrupted operation op. First read foo.add for recovering an + append or compress operation. Return -1 if there was an error reading or + writing foo.gz or reading an existing foo.add, or -2 if there was a memory + allocation failure. */ +local int log_recover(struct log *log, int op) { - unsigned xlen; - unsigned char temp[20]; - unsigned sub_len; - int good; - gz_log *log; - - /* allocate log structure */ - log = malloc(sizeof(gz_log)); - if (log == NULL) - return NULL; - log->id = GZLOGID; + int fd, ret = 0; + unsigned char *data = NULL; + size_t len = 0; + struct stat st; - /* open file, creating it if necessary, and locking it */ - log->fd = open(path, O_RDWR | O_CREAT, 0600); - if (log->fd < 0) { - free(log); - return NULL; + /* log recovery */ + log_log(log, op, "start"); + + /* load foo.add file if expected and present */ + if (op == APPEND_OP || op == COMPRESS_OP) { + strcpy(log->end, ".add"); + if (stat(log->path, &st) == 0 && st.st_size) { + len = (size_t)(st.st_size); + if (len != st.st_size || (data = malloc(st.st_size)) == NULL) { + log_log(log, op, "allocation failure"); + return -2; + } + if ((fd = open(log->path, O_RDONLY, 0)) < 0) { + log_log(log, op, ".add file read failure"); + return -1; + } + ret = read(fd, data, len) != len; + close(fd); + if (ret) { + log_log(log, op, ".add file read failure"); + return -1; + } + log_log(log, op, "loaded .add file"); + } + else + log_log(log, op, "missing .add file!"); + } + + /* recover the interrupted operation */ + switch (op) { + case APPEND_OP: + ret = log_append(log, data, len); + break; + case COMPRESS_OP: + ret = log_compress(log, data, len); + break; + case REPLACE_OP: + ret = log_replace(log); } - if (lock(log->fd)) { + + /* log status */ + log_log(log, op, ret ? "failure" : "complete"); + + /* clean up */ + if (data != NULL) + free(data); + return ret; +} + +/* Close the foo.gz file (if open) and release the lock. */ +local void log_close(struct log *log) +{ + if (log->fd >= 0) close(log->fd); - free(log); - return NULL; + log->fd = -1; + log_unlock(log); +} + +/* Open foo.gz, verify the header, and load the extra field contents, after + first creating the foo.lock file to gain exclusive access to the foo.* + files. If foo.gz does not exist or is empty, then write the initial header, + extra, and body content of an empty foo.gz log file. If there is an error + creating the lock file due to access restrictions, or an error reading or + writing the foo.gz file, or if the foo.gz file is not a proper log file for + this object (e.g. not a gzip file or does not contain the expected extra + field), then return true. If there is an error, the lock is released. + Otherwise, the lock is left in place. */ +local int log_open(struct log *log) +{ + int op; + + /* release open file resource if left over -- can occur if lock lost + between gzlog_open() and gzlog_write() */ + if (log->fd >= 0) + close(log->fd); + log->fd = -1; + + /* negotiate exclusive access */ + if (log_lock(log) < 0) + return -1; + + /* open the log file, foo.gz */ + strcpy(log->end, ".gz"); + log->fd = open(log->path, O_RDWR | O_CREAT, 0644); + if (log->fd < 0) { + log_close(log); + return -1; } - /* if file is empty, write new gzip stream */ + /* if new, initialize foo.gz with an empty log, delete old dictionary */ if (lseek(log->fd, 0, SEEK_END) == 0) { - if (write(log->fd, empty_gz, sizeof(empty_gz)) != sizeof(empty_gz)) { - log_clean(log); - return NULL; + if (write(log->fd, log_gzhead, HEAD) != HEAD || + write(log->fd, log_gzext, EXTRA) != EXTRA || + write(log->fd, log_gzbody, BODY) != BODY) { + log_close(log); + return -1; } + strcpy(log->end, ".dict"); + unlink(log->path); } - /* check gzip header */ - (void)lseek(log->fd, 0, SEEK_SET); - if (read(log->fd, temp, 12) != 12 || temp[0] != 0x1f || - temp[1] != 0x8b || temp[2] != 8 || (temp[3] & 4) == 0) { - log_clean(log); - return NULL; + /* verify log file and load extra field information */ + if ((op = log_head(log)) < 0) { + log_close(log); + return -1; } - /* process extra field to find "ap" sub-field */ - xlen = temp[10] + (temp[11] << 8); - good = 0; - while (xlen) { - if (xlen < 4 || read(log->fd, temp, 4) != 4) - break; - sub_len = temp[2]; - sub_len += temp[3] << 8; - xlen -= 4; - if (memcmp(temp, "ap", 2) == 0 && sub_len == 16) { - good = 1; - break; - } - if (xlen < sub_len) - break; - (void)lseek(log->fd, sub_len, SEEK_CUR); - xlen -= sub_len; + /* check for interrupted process and if so, recover */ + if (op != NO_OP && log_recover(log, op)) { + log_close(log); + return -1; } - if (!good) { - log_clean(log); + + /* touch the lock file to prevent another process from grabbing it */ + log_touch(log); + return 0; +} + +/* See gzlog.h for the description of the external methods below */ +gzlog *gzlog_open(char *path) +{ + size_t n; + struct log *log; + + /* check arguments */ + if (path == NULL || *path == 0) return NULL; - } - /* read in "ap" sub-field */ - log->extra = lseek(log->fd, 0, SEEK_CUR); - if (read(log->fd, temp, 16) != 16) { - log_clean(log); + /* allocate and initialize log structure */ + log = malloc(sizeof(struct log)); + if (log == NULL) + return NULL; + strcpy(log->id, LOGID); + log->fd = -1; + + /* save path and end of path for name construction */ + n = strlen(path); + log->path = malloc(n + 9); /* allow for ".repairs" */ + if (log->path == NULL) { + free(log); return NULL; } - log->mark_off = make_off(temp); - log->last_off = make_off(temp + 8); - - /* get crc, length of gzip file */ - (void)lseek(log->fd, log->last_off, SEEK_SET); - if (read(log->fd, temp, 13) != 13 || - memcmp(temp, "\001\000\000\377\377", 5) != 0) { - log_clean(log); + strcpy(log->path, path); + log->end = log->path + n; + + /* gain exclusive access and verify log file -- may perform a + recovery operation if needed */ + if (log_open(log)) { + free(log->path); + free(log); return NULL; } - log->crc = make_ulg(temp + 5); - log->len = make_ulg(temp + 9); - /* set up to write over empty last block */ - (void)lseek(log->fd, log->last_off + 5, SEEK_SET); - log->stored = 0; - return (void *)log; + /* return pointer to log structure */ + return log; } -/* maximum amount to put in a stored block before starting a new one */ -#define MAX_BLOCK 16384 - -/* write a block to a log object */ -int gzlog_write(void *obj, char *data, size_t len) +/* gzlog_compress() return values: + 0: all good + -1: file i/o error (usually access issue) + -2: memory allocation failure + -3: invalid log pointer argument */ +int gzlog_compress(gzlog *logd) { - size_t some; - unsigned char temp[5]; - gz_log *log; + int fd, ret; + uint block; + size_t len, next; + unsigned char *data, buf[5]; + struct log *log = logd; - /* check object */ - log = (gz_log *)obj; - if (log == NULL || log->id != GZLOGID) - return 1; + /* check arguments */ + if (log == NULL || strcmp(log->id, LOGID) || len < 0) + return -3; - /* write stored blocks until all of the input is written */ - do { - some = MAX_BLOCK - log->stored; - if (some > len) - some = len; - if (write(log->fd, data, some) != some) - return 1; - log->crc = crc32(log->crc, (unsigned char *)data, some); - log->len += some; - len -= some; - data += some; - log->stored += some; - - /* if the stored block is full, end it and start another */ - if (log->stored == MAX_BLOCK) { - (void)lseek(log->fd, log->last_off, SEEK_SET); - temp[0] = 0; - dice_ulg(log->stored + ((unsigned long)(~log->stored) << 16), - temp + 1); - if (write(log->fd, temp, 5) != 5) - return 1; - log->last_off = lseek(log->fd, log->stored, SEEK_CUR); - (void)lseek(log->fd, 5, SEEK_CUR); - log->stored = 0; - } - } while (len); - return 0; -} + /* see if we lost the lock -- if so get it again and reload the extra + field information (it probably changed), recover last operation if + necessary */ + if (log_check(log) && log_open(log)) + return -1; -/* recompress the remaining stored deflate data in place */ -local int recomp(gz_log *log) -{ - z_stream strm; - size_t len, max; - unsigned char *in; - unsigned char *out; - unsigned char temp[16]; - - /* allocate space and read it all in (it's around 1 MB) */ - len = log->last_off - log->mark_off; - max = len + (len >> 12) + (len >> 14) + 11; - out = malloc(max); - if (out == NULL) - return 1; - in = malloc(len); - if (in == NULL) { - free(out); - return 1; - } - (void)lseek(log->fd, log->mark_off, SEEK_SET); - if (read(log->fd, in, len) != len) { - free(in); - free(out); - return 1; - } + /* create space for uncompressed data */ + len = ((size_t)(log->last - log->first) & ~(((size_t)1 << 10) - 1)) + + log->stored; + if ((data = malloc(len)) == NULL) + return -2; - /* recompress in memory, decoding stored data as we go */ - /* note: this assumes that unsigned is four bytes or more */ - /* consider not making that assumption */ - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - if (deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 8, - Z_DEFAULT_STRATEGY) != Z_OK) { - free(in); - free(out); - return 1; - } - strm.next_in = in; - strm.avail_out = max; - strm.next_out = out; - while (len >= 5) { - if (strm.next_in[0] != 0) + /* do statement here is just a cheap trick for error handling */ + do { + /* read in the uncompressed data */ + if (lseek(log->fd, log->first - 1, SEEK_SET) < 0) break; - strm.avail_in = strm.next_in[1] + (strm.next_in[2] << 8); - strm.next_in += 5; - len -= 5; - if (strm.avail_in != 0) { - if (len < strm.avail_in) + next = 0; + while (next < len) { + if (read(log->fd, buf, 5) != 5) break; - len -= strm.avail_in; - (void)deflate(&strm, Z_NO_FLUSH); - if (strm.avail_in != 0 || strm.avail_out == 0) + block = PULL2(buf + 1); + if (next + block > len || + read(log->fd, (char *)data + next, block) != block) break; + next += block; } - } - (void)deflate(&strm, Z_SYNC_FLUSH); - (void)deflateEnd(&strm); - free(in); - if (len != 0 || strm.avail_out == 0) { - free(out); - return 1; - } + if (lseek(log->fd, 0, SEEK_CUR) != log->last + 4 + log->stored) + break; + log_touch(log); - /* overwrite stored data with compressed data */ - (void)lseek(log->fd, log->mark_off, SEEK_SET); - len = max - strm.avail_out; - if (write(log->fd, out, len) != len) { - free(out); - return 1; - } - free(out); - - /* write last empty block, crc, and length */ - log->mark_off = log->last_off = lseek(log->fd, 0, SEEK_CUR); - temp[0] = 1; - dice_ulg(0xffffL << 16, temp + 1); - dice_ulg(log->crc, temp + 5); - dice_ulg(log->len, temp + 9); - if (write(log->fd, temp, 13) != 13) - return 1; + /* write the uncompressed data to the .add file */ + strcpy(log->end, ".add"); + fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + break; + ret = write(fd, data, len) != len; + if (ret | close(fd)) + break; + log_touch(log); - /* truncate file to discard remaining stored data and old trailer */ - ftruncate(log->fd, lseek(log->fd, 0, SEEK_CUR)); + /* write the dictionary for the next compress to the .temp file */ + strcpy(log->end, ".temp"); + fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + break; + next = DICT > len ? len : DICT; + ret = write(fd, (char *)data + len - next, next) != next; + if (ret | close(fd)) + break; + log_touch(log); - /* update extra field to point to new last empty block */ - (void)lseek(log->fd, log->extra, SEEK_SET); - dice_off(log->mark_off, temp); - dice_off(log->last_off, temp + 8); - if (write(log->fd, temp, 16) != 16) - return 1; - return 0; -} + /* roll back to compressed data, mark the compress in progress */ + log->last = log->first; + log->stored = 0; + if (log_mark(log, COMPRESS_OP)) + break; + BAIL(7); + + /* compress and append the data (clears mark) */ + ret = log_compress(log, data, len); + free(data); + return ret; + } while (0); -/* maximum accumulation of stored blocks before compressing */ -#define MAX_STORED 1048576 + /* broke out of do above on i/o error */ + free(data); + return -1; +} -/* close log object */ -int gzlog_close(void *obj) +/* gzlog_write() return values: + 0: all good + -1: file i/o error (usually access issue) + -2: memory allocation failure + -3: invalid log pointer argument */ +int gzlog_write(gzlog *logd, void *data, size_t len) { - unsigned char temp[8]; - gz_log *log; + int fd, ret; + struct log *log = logd; - /* check object */ - log = (gz_log *)obj; - if (log == NULL || log->id != GZLOGID) - return 1; + /* check arguments */ + if (log == NULL || strcmp(log->id, LOGID) || len < 0) + return -3; + if (data == NULL || len == 0) + return 0; - /* go to start of most recent block being written */ - (void)lseek(log->fd, log->last_off, SEEK_SET); - - /* if some stuff was put there, update block */ - if (log->stored) { - temp[0] = 0; - dice_ulg(log->stored + ((unsigned long)(~log->stored) << 16), - temp + 1); - if (write(log->fd, temp, 5) != 5) - return 1; - log->last_off = lseek(log->fd, log->stored, SEEK_CUR); - } + /* see if we lost the lock -- if so get it again and reload the extra + field information (it probably changed), recover last operation if + necessary */ + if (log_check(log) && log_open(log)) + return -1; - /* write last block (empty) */ - if (write(log->fd, "\001\000\000\377\377", 5) != 5) - return 1; + /* create and write .add file */ + strcpy(log->end, ".add"); + fd = open(log->path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + return -1; + ret = write(fd, data, len) != len; + if (ret | close(fd)) + return -1; + log_touch(log); - /* write updated crc and uncompressed length */ - dice_ulg(log->crc, temp); - dice_ulg(log->len, temp + 4); - if (write(log->fd, temp, 8) != 8) - return 1; + /* mark log file with append in progress */ + if (log_mark(log, APPEND_OP)) + return -1; + BAIL(8); - /* put offset of that last block in gzip extra block */ - (void)lseek(log->fd, log->extra + 8, SEEK_SET); - dice_off(log->last_off, temp); - if (write(log->fd, temp, 8) != 8) - return 1; + /* append data (clears mark) */ + if (log_append(log, data, len)) + return -1; - /* if more than 1 MB stored, then time to compress it */ - if (log->last_off - log->mark_off > MAX_STORED) { - if (recomp(log)) - return 1; - } + /* check to see if it's time to compress -- if not, then done */ + if (((log->last - log->first) >> 10) + (log->stored >> 10) < TRIGGER) + return 0; + + /* time to compress */ + return gzlog_compress(log); +} + +/* gzlog_close() return values: + 0: ok + -3: invalid log pointer argument */ +int gzlog_close(gzlog *logd) +{ + struct log *log = logd; - /* unlock and close file */ - log_clean(log); + /* check arguments */ + if (log == NULL || strcmp(log->id, LOGID)) + return -3; + + /* close the log file and release the lock */ + log_close(log); + + /* free structure and return */ + if (log->path != NULL) + free(log->path); + strcpy(log->id, "bad"); + free(log); return 0; } diff --git a/examples/gzlog.h b/examples/gzlog.h index a800bd53..c4614267 100644 --- a/examples/gzlog.h +++ b/examples/gzlog.h @@ -1,6 +1,6 @@ /* gzlog.h - Copyright (C) 2004 Mark Adler, all rights reserved - version 1.0, 26 Nov 2004 + Copyright (C) 2004, 2008 Mark Adler, all rights reserved + version 2.0, 25 Apr 2008 This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages @@ -21,38 +21,69 @@ Mark Adler madler@alumni.caltech.edu */ +/* Version History: + 1.0 26 Nov 2004 First version + 2.0 25 Apr 2008 Complete redesign for recovery of interrupted operations + Interface changed slightly in that now path is a prefix + Compression now occurs as needed during gzlog_write() + gzlog_write() now always leaves the log file as valid gzip + */ + /* The gzlog object allows writing short messages to a gzipped log file, opening the log file locked for small bursts, and then closing it. The log - object works by appending stored data to the gzip file until 1 MB has been - accumulated. At that time, the stored data is compressed, and replaces the - uncompressed data in the file. The log file is truncated to its new size at - that time. After closing, the log file is always valid gzip file that can - decompressed to recover what was written. - - A gzip header "extra" field contains two file offsets for appending. The - first points to just after the last compressed data. The second points to - the last stored block in the deflate stream, which is empty. All of the - data between those pointers is uncompressed. + object works by appending stored (uncompressed) data to the gzip file until + 1 MB has been accumulated. At that time, the stored data is compressed, and + replaces the uncompressed data in the file. The log file is truncated to + its new size at that time. After each write operation, the log file is a + valid gzip file that can decompressed to recover what was written. + + The gzlog operations can be interupted at any point due to an application or + system crash, and the log file will be recovered the next time the log is + opened with gzlog_open(). */ +#ifndef GZLOG_H +#define GZLOG_H + +/* gzlog object type */ +typedef void gzlog; + /* Open a gzlog object, creating the log file if it does not exist. Return - NULL on error. Note that gzlog_open() could take a long time to return if - there is difficulty in locking the file. */ -void *gzlog_open(char *path); - -/* Write to a gzlog object. Return non-zero on error. This function will - simply write data to the file uncompressed. Compression of the data - will not occur until gzlog_close() is called. It is expected that - gzlog_write() is used for a short message, and then gzlog_close() is - called. If a large amount of data is to be written, then the application - should write no more than 1 MB at a time with gzlog_write() before - calling gzlog_close() and then gzlog_open() again. */ -int gzlog_write(void *log, char *data, size_t len); - -/* Close a gzlog object. Return non-zero on error. The log file is locked - until this function is called. This function will compress stored data - at the end of the gzip file if at least 1 MB has been accumulated. Note - that the file will not be a valid gzip file until this function completes. - */ -int gzlog_close(void *log); + NULL on error. Note that gzlog_open() could take a while to complete if it + has to wait to verify that a lock is stale (possibly for five minutes), or + if there is significant contention with other instantiations of this object + when locking the resource. path is the prefix of the file names created by + this object. If path is "foo", then the log file will be "foo.gz", and + other auxiliary files will be created and destroyed during the process: + "foo.dict" for a compression dictionary, "foo.temp" for a temporary (next) + dictionary, "foo.add" for data being added or compressed, "foo.lock" for the + lock file, and "foo.repairs" to log recovery operations performed due to + interrupted gzlog operations. A gzlog_open() followed by a gzlog_close() + will recover a previously interrupted operation, if any. */ +gzlog *gzlog_open(char *path); + +/* Write to a gzlog object. Return zero on success, -1 if there is a file i/o + error on any of the gzlog files (this should not happen if gzlog_open() + succeeded, unless the device has run out of space or leftover auxiliary + files have permissions or ownership that prevent their use), -2 if there is + a memory allocation failure, or -3 if the log argument is invalid (e.g. if + it was not created by gzlog_open()). This function will write data to the + file uncompressed, until 1 MB has been accumulated, at which time that data + will be compressed. The log file will be a valid gzip file upon successful + return. */ +int gzlog_write(gzlog *log, void *data, size_t len); + +/* Force compression of any uncompressed data in the log. This should be used + sparingly, if at all. The main application would be when a log file will + not be appended to again. If this is used to compress frequently while + appending, it will both significantly increase the execution time and + reduce the compression ratio. The return codes are the same as for + gzlog_write(). */ +int gzlog_compress(gzlog *log); + +/* Close a gzlog object. Return zero on success, -3 if the log argument is + invalid. The log object is freed, and so cannot be referenced again. */ +int gzlog_close(gzlog *log); + +#endif diff --git a/examples/pigz.c b/examples/pigz.c new file mode 100644 index 00000000..42794d0f --- /dev/null +++ b/examples/pigz.c @@ -0,0 +1,452 @@ +/* pigz.c -- parallel implementation of gzip + * Copyright (C) 2007 Mark Adler + * Version 1.1 28 January 2007 Mark Adler + */ + +/* Version history: + 1.0 17 Jan 2007 First version + 1.1 28 Jan 2007 Avoid void * arithmetic (some compilers don't get that) + Add note about requiring zlib 1.2.3 + Allow compression level 0 (no compression) + Completely rewrite parallelism -- add a write thread + Use deflateSetDictionary() to make use of history + Tune argument defaults to best performance on four cores + */ + +/* + pigz compresses from stdin to stdout using threads to make use of multiple + processors and cores. The input is broken up into 128 KB chunks, and each + is compressed separately. The CRC for each chunk is also calculated + separately. The compressed chunks are written in order to the output, + and the overall CRC is calculated from the CRC's of the chunks. + + The compressed data format generated is the gzip format using the deflate + compression method. First a gzip header is written, followed by raw deflate + partial streams. They are partial, in that they do not have a terminating + block. At the end, the deflate stream is terminated with a final empty + static block, and lastly a gzip trailer is written with the CRC and the + number of input bytes. + + Each raw deflate partial stream is terminated by an empty stored block + (using the Z_SYNC_FLUSH option of zlib), in order to end that partial + bit stream at a byte boundary. That allows the partial streams to be + concantenated simply as sequences of bytes. This adds a very small four + or five byte overhead to the output for each input chunk. + + zlib's crc32_combine() routine allows the calcuation of the CRC of the + entire input using the independent CRC's of the chunks. pigz requires zlib + version 1.2.3 or later, since that is the first version that provides the + crc32_combine() function. + + pigz uses the POSIX pthread library for thread control and communication. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "zlib.h" + +#define local static + +/* exit with error */ +local void bail(char *msg) +{ + fprintf(stderr, "pigz abort: %s\n", msg); + exit(1); +} + +/* read up to len bytes into buf, repeating read() calls as needed */ +local size_t readn(int desc, unsigned char *buf, size_t len) +{ + ssize_t ret; + size_t got; + + got = 0; + while (len) { + ret = read(desc, buf, len); + if (ret < 0) + bail("read error"); + if (ret == 0) + break; + buf += ret; + len -= ret; + got += ret; + } + return got; +} + +/* write len bytes, repeating write() calls as needed */ +local void writen(int desc, unsigned char *buf, size_t len) +{ + ssize_t ret; + + while (len) { + ret = write(desc, buf, len); + if (ret < 1) + bail("write error"); + buf += ret; + len -= ret; + } +} + +/* a flag variable for communication between two threads */ +struct flag { + int value; /* value of flag */ + pthread_mutex_t lock; /* lock for checking and changing flag */ + pthread_cond_t cond; /* condition for signaling on flag change */ +}; + +/* initialize a flag for use, starting with value val */ +local void flag_init(struct flag *me, int val) +{ + me->value = val; + pthread_mutex_init(&(me->lock), NULL); + pthread_cond_init(&(me->cond), NULL); +} + +/* set the flag to val, signal another process that may be waiting for it */ +local void flag_set(struct flag *me, int val) +{ + pthread_mutex_lock(&(me->lock)); + me->value = val; + pthread_cond_signal(&(me->cond)); + pthread_mutex_unlock(&(me->lock)); +} + +/* if it isn't already, wait for some other thread to set the flag to val */ +local void flag_wait(struct flag *me, int val) +{ + pthread_mutex_lock(&(me->lock)); + while (me->value != val) + pthread_cond_wait(&(me->cond), &(me->lock)); + pthread_mutex_unlock(&(me->lock)); +} + +/* if flag is equal to val, wait for some other thread to change it */ +local void flag_wait_not(struct flag *me, int val) +{ + pthread_mutex_lock(&(me->lock)); + while (me->value == val) + pthread_cond_wait(&(me->cond), &(me->lock)); + pthread_mutex_unlock(&(me->lock)); +} + +/* clean up the flag when done with it */ +local void flag_done(struct flag *me) +{ + pthread_cond_destroy(&(me->cond)); + pthread_mutex_destroy(&(me->lock)); +} + +/* a unit of work to feed to compress_thread() -- it is assumed that the out + buffer is large enough to hold the maximum size len bytes could deflate to, + plus five bytes for the final sync marker */ +struct work { + size_t len; /* length of input */ + unsigned long crc; /* crc of input */ + unsigned char *buf; /* input */ + unsigned char *out; /* space for output (guaranteed big enough) */ + z_stream strm; /* pre-initialized z_stream */ + struct flag busy; /* busy flag indicating work unit in use */ + pthread_t comp; /* this compression thread */ +}; + +/* busy flag values */ +#define IDLE 0 /* compress and writing done -- can start compress */ +#define COMP 1 /* compress -- input and output buffers in use */ +#define WRITE 2 /* compress done, writing output -- can read input */ + +/* read-only globals (set by main/read thread before others started) */ +local int ind; /* input file descriptor */ +local int outd; /* output file descriptor */ +local int level; /* compression level */ +local int procs; /* number of compression threads (>= 2) */ +local size_t size; /* uncompressed input size per thread (>= 32K) */ +local struct work *jobs; /* work units: jobs[0..procs-1] */ + +/* next and previous jobs[] indices */ +#define NEXT(n) ((n) == procs - 1 ? 0 : (n) + 1) +#define PREV(n) ((n) == 0 ? procs - 1 : (n) - 1) + +/* sliding dictionary size for deflate */ +#define DICT 32768U + +/* largest power of 2 that fits in an unsigned int -- used to limit requests + to zlib functions that use unsigned int lengths */ +#define MAX ((((unsigned)-1) >> 1) + 1) + +/* compress thread: compress the input in the provided work unit and compute + its crc -- assume that the amount of space at job->out is guaranteed to be + enough for the compressed output, as determined by the maximum expansion + of deflate compression -- use the input in the previous work unit (if there + is one) to set the deflate dictionary for better compression */ +local void *compress_thread(void *arg) +{ + size_t len; /* input length for this work unit */ + unsigned long crc; /* crc of input data */ + struct work *prev; /* previous work unit */ + struct work *job = arg; /* work unit for this thread */ + z_stream *strm = &(job->strm); /* zlib stream for this work unit */ + + /* reset state for a new compressed stream */ + (void)deflateReset(strm); + + /* initialize input, output, and crc */ + strm->next_in = job->buf; + strm->next_out = job->out; + len = job->len; + crc = crc32(0L, Z_NULL, 0); + + /* set dictionary if this isn't the first work unit, and if we will be + compressing something (the read thread assures that the dictionary + data in the previous work unit is still there) */ + prev = jobs + PREV(job - jobs); + if (prev->buf != NULL && len != 0) + deflateSetDictionary(strm, prev->buf + (size - DICT), DICT); + + /* run MAX-sized amounts of input through deflate and crc32 -- this loop + is needed for those cases where the integer type is smaller than the + size_t type, or when len is close to the limit of the size_t type */ + while (len > MAX) { + strm->avail_in = MAX; + strm->avail_out = (unsigned)-1; + crc = crc32(crc, strm->next_in, strm->avail_in); + (void)deflate(strm, Z_NO_FLUSH); + len -= MAX; + } + + /* run last piece through deflate and crc32, follow with a sync marker */ + if (len) { + strm->avail_in = len; + strm->avail_out = (unsigned)-1; + crc = crc32(crc, strm->next_in, strm->avail_in); + (void)deflate(strm, Z_SYNC_FLUSH); + } + + /* don't need to Z_FINISH, since we'd delete the last two bytes anyway */ + + /* return result */ + job->crc = crc; + return NULL; +} + +/* put a 4-byte integer into a byte array in LSB order */ +#define PUT4(a,b) (*(a)=(b),(a)[1]=(b)>>8,(a)[2]=(b)>>16,(a)[3]=(b)>>24) + +/* write thread: wait for compression threads to complete, write output in + order, also write gzip header and trailer around the compressed data */ +local void *write_thread(void *arg) +{ + int n; /* compress thread index */ + size_t len; /* length of input processed */ + unsigned long tot; /* total uncompressed size (overflow ok) */ + unsigned long crc; /* CRC-32 of uncompressed data */ + unsigned char wrap[10]; /* gzip header or trailer */ + + /* write simple gzip header */ + memcpy(wrap, "\037\213\10\0\0\0\0\0\0\3", 10); + wrap[8] = level == 9 ? 2 : (level == 1 ? 4 : 0); + writen(outd, wrap, 10); + + /* process output of compress threads until end of input */ + tot = 0; + crc = crc32(0L, Z_NULL, 0); + n = 0; + do { + /* wait for compress thread to start, then wait to complete */ + flag_wait(&(jobs[n].busy), COMP); + pthread_join(jobs[n].comp, NULL); + + /* now that compress is done, allow read thread to use input buffer */ + flag_set(&(jobs[n].busy), WRITE); + + /* write compressed data and update length and crc */ + writen(outd, jobs[n].out, jobs[n].strm.next_out - jobs[n].out); + len = jobs[n].len; + tot += len; + crc = crc32_combine(crc, jobs[n].crc, len); + + /* release this work unit and go to the next work unit */ + flag_set(&(jobs[n].busy), IDLE); + n = NEXT(n); + + /* an input buffer less than size in length indicates end of input */ + } while (len == size); + + /* write final static block and gzip trailer (crc and len mod 2^32) */ + wrap[0] = 3; wrap[1] = 0; + PUT4(wrap + 2, crc); + PUT4(wrap + 6, tot); + writen(outd, wrap, 10); + return NULL; +} + +/* one-time initialization of a work unit -- this is where we set the deflate + compression level and request raw deflate, and also where we set the size + of the output buffer to guarantee enough space for a worst-case deflate + ending with a Z_SYNC_FLUSH */ +local void job_init(struct work *job) +{ + int ret; /* deflateInit2() return value */ + + job->buf = malloc(size); + job->out = malloc(size + (size >> 11) + 10); + job->strm.zfree = Z_NULL; + job->strm.zalloc = Z_NULL; + job->strm.opaque = Z_NULL; + ret = deflateInit2(&(job->strm), level, Z_DEFLATED, -15, 8, + Z_DEFAULT_STRATEGY); + if (job->buf == NULL || job->out == NULL || ret != Z_OK) + bail("not enough memory"); +} + +/* compress ind to outd in the gzip format, using multiple threads for the + compression and crc calculation and another thread for writing the output -- + the read thread is the main thread */ +local void read_thread(void) +{ + int n; /* general index */ + size_t got; /* amount read */ + pthread_attr_t attr; /* thread attributes (left at defaults) */ + pthread_t write; /* write thread */ + + /* set defaults (not all pthread implementations default to joinable) */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + /* allocate and set up work list (individual work units will be initialized + as needed, in case the input is short), assure that allocation size + arithmetic does not overflow */ + if (size + (size >> 11) + 10 < (size >> 11) + 10 || + (ssize_t)(size + (size >> 11) + 10) < 0 || + ((size_t)0 - 1) / procs <= sizeof(struct work) || + (jobs = malloc(procs * sizeof(struct work))) == NULL) + bail("not enough memory"); + for (n = 0; n < procs; n++) { + jobs[n].buf = NULL; + flag_init(&(jobs[n].busy), IDLE); + } + + /* start write thread */ + pthread_create(&write, &attr, write_thread, NULL); + + /* read from input and start compress threads (write thread will pick up + the output of the compress threads) */ + n = 0; + do { + /* initialize this work unit if it's the first time it's used */ + if (jobs[n].buf == NULL) + job_init(jobs + n); + + /* read input data, but wait for last compress on this work unit to be + done, and wait for the dictionary to be used by the last compress on + the next work unit */ + flag_wait_not(&(jobs[n].busy), COMP); + flag_wait_not(&(jobs[NEXT(n)].busy), COMP); + got = readn(ind, jobs[n].buf, size); + + /* start compress thread, but wait for write to be done first */ + flag_wait(&(jobs[n].busy), IDLE); + jobs[n].len = got; + pthread_create(&(jobs[n].comp), &attr, compress_thread, jobs + n); + + /* mark work unit so write thread knows compress was started */ + flag_set(&(jobs[n].busy), COMP); + + /* go to the next work unit */ + n = NEXT(n); + + /* do until end of input, indicated by a read less than size */ + } while (got == size); + + /* wait for the write thread to complete -- the write thread will join with + all of the compress threads, so this waits for all of the threads to + complete */ + pthread_join(write, NULL); + + /* free up all requested resources and return */ + for (n = procs - 1; n >= 0; n--) { + flag_done(&(jobs[n].busy)); + (void)deflateEnd(&(jobs[n].strm)); + free(jobs[n].out); + free(jobs[n].buf); + } + free(jobs); + pthread_attr_destroy(&attr); +} + +/* Process arguments for level, size, and procs, compress from stdin to + stdout in the gzip format. Note that procs must be at least two in + order to provide a dictionary in one work unit for the other work + unit, and that size must be at least 32K to store a full dictionary. */ +int main(int argc, char **argv) +{ + int n; /* general index */ + int get; /* command line parameters to get */ + char *arg; /* command line argument */ + + /* set defaults -- 32 processes and 128K buffers was found to provide + good utilization of four cores (about 97%) and balanced the overall + execution time impact of more threads against more dictionary + processing for a fixed amount of memory -- the memory usage for these + settings and full use of all work units (at least 4 MB of input) is + 16.2 MB + */ + level = Z_DEFAULT_COMPRESSION; + procs = 32; + size = 131072UL; + + /* process command-line arguments */ + get = 0; + for (n = 1; n < argc; n++) { + arg = argv[n]; + if (*arg == '-') { + while (*++arg) + if (*arg >= '0' && *arg <= '9') /* compression level */ + level = *arg - '0'; + else if (*arg == 'b') /* chunk size in K */ + get |= 1; + else if (*arg == 'p') /* number of processes */ + get |= 2; + else if (*arg == 'h') { /* help */ + fputs("usage: pigz [-0..9] [-b blocksizeinK]", stderr); + fputs(" [-p processes] < foo > foo.gz\n", stderr); + return 0; + } + else + bail("invalid option"); + } + else if (get & 1) { + if (get & 2) + bail("you need to separate the -b and -p options"); + size = (size_t)(atol(arg)) << 10; /* chunk size */ + if (size < DICT) + bail("invalid option"); + get = 0; + } + else if (get & 2) { + procs = atoi(arg); /* processes */ + if (procs < 2) + bail("invalid option"); + get = 0; + } + else + bail("invalid option (you need to pipe input and output)"); + } + if (get) + bail("missing option argument"); + + /* do parallel compression from stdin to stdout (the read thread starts up + the write thread and the compression threads, and they all join before + the read thread returns) */ + ind = 0; + outd = 1; + read_thread(); + + /* done */ + return 0; +} diff --git a/gzio.c b/gzio.c index df346207..cf55486d 100644 --- a/gzio.c +++ b/gzio.c @@ -1,5 +1,5 @@ /* gzio.c -- IO on .gz files - * Copyright (C) 1995-2006 Jean-loup Gailly. + * Copyright (C) 1995-2009 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h * * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. @@ -71,43 +71,32 @@ static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ typedef struct gz_stream { z_stream stream; - int z_err; /* error code for last stream operation */ - int z_eof; /* set if end of input file */ - FILE *file; /* .gz file */ - Byte *inbuf; /* input buffer */ - Byte *outbuf; /* output buffer */ - uLong crc; /* crc32 of uncompressed data */ - char *msg; /* error message */ - char *path; /* path name for debugging only */ - int transparent; /* 1 if input file is not a .gz file */ - char mode; /* 'w' or 'r' */ -#ifdef _LARGEFILE64_SOURCE - off64_t start; /* start of compressed data in file (header skipped) */ - off64_t in; /* bytes into deflate or inflate */ - off64_t out; /* bytes out of deflate or inflate */ -#else - z_off_t start; /* start of compressed data in file (header skipped) */ - z_off_t in; /* bytes into deflate or inflate */ - z_off_t out; /* bytes out of deflate or inflate */ -#endif - int back; /* one character push-back */ - int last; /* true if push-back is last character */ + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off64_t start; /* start of compressed data in file */ + z_off64_t in; /* bytes into deflate or inflate */ + z_off64_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ } gz_stream; -local gzFile gz_open OF((const char *path, const char *mode, int fd, - int use64)); -#ifdef _LARGEFILE64_SOURCE -local off64_t gz_seek OF((gzFile file, off64_t offset, int whence, int use64)); -#else -local z_off_t gz_seek OF((gzFile file, z_off_t offset, int whence, int use64)); -#endif -local int do_flush OF((gzFile file, int flush)); -local int get_byte OF((gz_stream *s)); -local void check_header OF((gz_stream *s)); -local int destroy OF((gz_stream *s)); -local void putLong OF((FILE *file, uLong x)); -local uLong getLong OF((gz_stream *s)); +local gzFile gz_open OF((const char *, const char *, int, int)); +local z_off64_t gz_seek OF((gzFile, z_off64_t, int, int)); +local int do_flush OF((gzFile, int)); +local int get_byte OF((gz_stream *)); +local void check_header OF((gz_stream *)); +local int destroy OF((gz_stream *)); +local void putLong OF((FILE *, uLong)); +local uLong getLong OF((gz_stream *)); /* =========================================================================== Opens a gzip (.gz) file for reading or writing. The mode parameter @@ -143,6 +132,7 @@ local gzFile gz_open (path, mode, fd, use64) s->stream.next_in = s->inbuf = Z_NULL; s->stream.next_out = s->outbuf = Z_NULL; s->stream.avail_in = s->stream.avail_out = 0; + s->stream.state = Z_NULL; s->file = NULL; s->z_err = Z_OK; s->z_eof = 0; @@ -442,7 +432,7 @@ int ZEXPORT gzread (file, buf, len) if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; - if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + if (s->z_err == Z_STREAM_END || s->z_eof) return 0; /* EOF */ next_out = (Byte*)buf; s->stream.next_out = (Bytef*)buf; @@ -482,7 +472,7 @@ int ZEXPORT gzread (file, buf, len) len -= s->stream.avail_out; s->in += len; s->out += len; - if (len == 0) s->z_eof = 1; + if (feof(s->file)) s->z_eof = 1; return (int)len; } if (s->stream.avail_in == 0 && !s->z_eof) { @@ -803,15 +793,9 @@ int ZEXPORT gzflush (file, flush) SEEK_END is not implemented, returns error. In this version of the library, gzseek can be extremely slow. */ -#ifdef _LARGEFILE64_SOURCE -local off64_t gz_seek (file, offset, whence, use64) - gzFile file; - off64_t offset; -#else -local z_off_t gz_seek (file, offset, whence, use64) +local z_off64_t gz_seek (file, offset, whence, use64) gzFile file; - z_off_t offset; -#endif + z_off64_t offset; int whence; int use64; { @@ -914,23 +898,17 @@ z_off_t ZEXPORT gzseek (file, offset, whence) return (z_off_t)gz_seek(file, offset, whence, 0); } -#ifdef _LARGEFILE64_SOURCE -off64_t ZEXPORT gzseek64 (file, offset, whence) +z_off64_t ZEXPORT gzseek64 (file, offset, whence) gzFile file; - off64_t offset; + z_off64_t offset; int whence; { +#ifdef _LARGEFILE64_SOURCE return gz_seek(file, offset, whence, 1); -} #else -z_off_t ZEXPORT gzseek64 (file, offset, whence) - gzFile file; - z_off_t offset; - int whence; -{ return gz_seek(file, offset, whence, 0); -} #endif +} /* =========================================================================== Rewinds input file. @@ -968,11 +946,7 @@ z_off_t ZEXPORT gztell (file) /* =========================================================================== 64-bit version */ -#ifdef _LARGEFILE64_SOURCE -off64_t ZEXPORT gztell64 (file) -#else -z_off_t ZEXPORT gztell64 (file) -#endif +z_off64_t ZEXPORT gztell64 (file) gzFile file; { return gzseek64(file, 0L, SEEK_CUR); diff --git a/infback.c b/infback.c index be0b3dc8..e279d8f5 100644 --- a/infback.c +++ b/infback.c @@ -1,5 +1,5 @@ /* infback.c -- inflate using a call-back interface - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -438,7 +438,16 @@ void FAR *out_desc; /* handle error breaks in while */ if (state->mode == BAD) break; - /* build code tables */ + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 9; diff --git a/inffast.c b/inffast.c index 0b919bb4..97f9a842 100644 --- a/inffast.c +++ b/inffast.c @@ -1,5 +1,5 @@ /* inffast.c -- fast decoding - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2008 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -188,7 +188,8 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ op = dist - op; /* distance back in window */ if (op > whave) { if (state->sane) { - strm->msg = (char *)"invalid distance too far back"; + strm->msg = + (char *)"invalid distance too far back"; state->mode = BAD; break; } diff --git a/inflate.c b/inflate.c index d3c718c0..d069bbe4 100644 --- a/inflate.c +++ b/inflate.c @@ -1,5 +1,5 @@ /* inflate.c -- zlib decompression - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -122,24 +122,47 @@ z_streamp strm; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; state->sane = 1; + state->back = -1; Tracev((stderr, "inflate: reset\n")); return Z_OK; } -int ZEXPORT inflatePrime(strm, bits, value) +int ZEXPORT inflateReset2(strm, windowBits) z_streamp strm; -int bits; -int value; +int windowBits; { + int wrap; struct inflate_state FAR *state; + /* get the state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; - if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; - value &= (1L << bits) - 1; - state->hold += value << state->bits; - state->bits += bits; - return Z_OK; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + if (state->wbits != windowBits && state->window != Z_NULL) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); } int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) @@ -148,6 +171,7 @@ int windowBits; const char *version; int stream_size; { + int ret; struct inflate_state FAR *state; if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || @@ -165,24 +189,13 @@ int stream_size; if (state == Z_NULL) return Z_MEM_ERROR; Tracev((stderr, "inflate: allocated\n")); strm->state = (struct internal_state FAR *)state; - if (windowBits < 0) { - state->wrap = 0; - windowBits = -windowBits; - } - else { - state->wrap = (windowBits >> 4) + 1; -#ifdef GUNZIP - if (windowBits < 48) windowBits &= 15; -#endif - } - if (windowBits < 8 || windowBits > 15) { + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { ZFREE(strm, state); strm->state = Z_NULL; - return Z_STREAM_ERROR; } - state->wbits = (unsigned)windowBits; - state->window = Z_NULL; - return inflateReset(strm); + return ret; } int ZEXPORT inflateInit_(strm, version, stream_size) @@ -193,6 +206,27 @@ int stream_size; return inflateInit2_(strm, DEF_WBITS, version, stream_size); } +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. Normally this returns fixed tables from inffixed.h. @@ -772,7 +806,7 @@ int flush; strm->adler = state->check = adler32(0L, Z_NULL, 0); state->mode = TYPE; case TYPE: - if (flush == Z_BLOCK) goto inf_leave; + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; case TYPEDO: if (state->last) { BYTEBITS(); @@ -792,7 +826,11 @@ int flush; fixedtables(state); Tracev((stderr, "inflate: fixed codes block%s\n", state->last ? " (last)" : "")); - state->mode = LEN; /* decode codes */ + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } break; case 2: /* dynamic block */ Tracev((stderr, "inflate: dynamic codes block%s\n", @@ -817,6 +855,9 @@ int flush; Tracev((stderr, "inflate: stored length %u\n", state->length)); INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: state->mode = COPY; case COPY: copy = state->length; @@ -926,7 +967,16 @@ int flush; /* handle error breaks in while */ if (state->mode == BAD) break; - /* build code tables */ + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; state->lencode = (code const FAR *)(state->next); state->lenbits = 9; @@ -947,14 +997,20 @@ int flush; break; } Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: state->mode = LEN; case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); + if (state->mode == TYPE) + state->back = -1; break; } + state->back = 0; for (;;) { here = state->lencode[BITS(state->lenbits)]; if ((unsigned)(here.bits) <= bits) break; @@ -969,8 +1025,10 @@ int flush; PULLBYTE(); } DROPBITS(last.bits); + state->back += last.bits; } DROPBITS(here.bits); + state->back += here.bits; state->length = (unsigned)here.val; if ((int)(here.op) == 0) { Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? @@ -981,6 +1039,7 @@ int flush; } if (here.op & 32) { Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; state->mode = TYPE; break; } @@ -996,8 +1055,10 @@ int flush; NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); + state->back += state->extra; } Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; state->mode = DIST; case DIST: for (;;) { @@ -1014,8 +1075,10 @@ int flush; PULLBYTE(); } DROPBITS(last.bits); + state->back += last.bits; } DROPBITS(here.bits); + state->back += here.bits; if (here.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; @@ -1029,6 +1092,7 @@ int flush; NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); + state->back += state->extra; } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { @@ -1066,6 +1130,9 @@ int flush; } if (copy > state->write) { copy -= state->write; + /* %% problem here if copy > state->wsize -- avoid? */ + /* %% or can (state->window + state->wsize) - copy */ + /* %% but really should detect and reject this case */ from = state->window + (state->wsize - copy); } else @@ -1162,7 +1229,8 @@ int flush; strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = state->bits + (state->last ? 64 : 0) + - (state->mode == TYPE ? 128 : 0); + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; @@ -1399,3 +1467,15 @@ int subvert; return Z_DATA_ERROR; #endif } + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/inflate.h b/inflate.h index d54a8688..ba03e7c4 100644 --- a/inflate.h +++ b/inflate.h @@ -1,5 +1,5 @@ /* inflate.h -- internal inflate state definition - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -32,11 +32,13 @@ typedef enum { TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ - LEN, /* i: waiting for length/lit code */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ @@ -53,19 +55,21 @@ typedef enum { /* State transitions between above modes - - (most modes can go to the BAD or MEM mode -- not shown for clarity) + (most modes can go to BAD or MEM on error -- not shown for clarity) Process header: - HEAD -> (gzip) or (zlib) - (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME - NAME -> COMMENT -> HCRC -> TYPE + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE + (raw) -> TYPEDO Read deflate blocks: - TYPE -> STORED or TABLE or LEN or CHECK - STORED -> COPY -> TYPE - TABLE -> LENLENS -> CODELENS -> LEN - Read deflate codes: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN @@ -113,4 +117,6 @@ struct inflate_state { unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ }; diff --git a/inftrees.c b/inftrees.c index fb8d8431..2b12d708 100644 --- a/inftrees.c +++ b/inftrees.c @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2006 Mark Adler + * Copyright (C) 1995-2009 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.3.3 Copyright 1995-2006 Mark Adler "; + " inflate 1.2.3.4 Copyright 1995-2008 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -62,7 +62,7 @@ unsigned short FAR *work; 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 203}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 200}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, @@ -123,7 +123,7 @@ unsigned short FAR *work; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } - for (min = 1; min <= MAXBITS; min++) + for (min = 1; min < max; min++) if (count[min] != 0) break; if (root < min) root = min; @@ -166,11 +166,10 @@ unsigned short FAR *work; entered in the tables. used keeps track of how many table entries have been allocated from the - provided *table space. It is checked when a LENS table is being made - against the space in *table, ENOUGH, minus the maximum space needed by - the worst case distance code, MAXD. This should never happen, but the - sufficiency of ENOUGH has not been proven exhaustively, hence the check. - This assumes that when type == LENS, bits == 9. + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This @@ -209,7 +208,8 @@ unsigned short FAR *work; mask = used - 1; /* mask for comparing low */ /* check available table space */ - if (type == LENS && used >= ENOUGH - MAXD) + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ @@ -277,7 +277,8 @@ unsigned short FAR *work; /* check for enough space */ used += 1U << curr; - if (type == LENS && used >= ENOUGH - MAXD) + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ diff --git a/inftrees.h b/inftrees.h index b1104c87..67461da0 100644 --- a/inftrees.h +++ b/inftrees.h @@ -35,15 +35,22 @@ typedef struct { 01000000 - invalid code */ -/* Maximum size of dynamic tree. The maximum found in a long but non- - exhaustive search was 1444 code structures (852 for length/literals - and 592 for distances, the latter actually the result of an - exhaustive search). The true maximum is not known, but the value - below is more than safe. */ -#define ENOUGH 2048 -#define MAXD 592 +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) -/* Type of code to build for inftable() */ +/* Type of code to build for inflate_table() */ typedef enum { CODES, LENS, diff --git a/make_vms.com b/make_vms.com index c8fa6a55..87c480aa 100644 --- a/make_vms.com +++ b/make_vms.com @@ -10,6 +10,7 @@ $! $!------------------------------------------------------------------------------ $! Version history $! 0.01 20060120 First version to receive a number +$! 0.02 20061008 Adapt to new Makefile.in $! $ on error then goto err_exit $! @@ -353,7 +354,7 @@ $! $CREA_OLIST: $ open/read min makefile.in $ open/write mod modules.opt -$ src_check = "OBJS =" +$ src_check = "OBJC =" $MRLOOP: $ read/end=mrdone min rec $ if (f$extract(0,6,rec) .nes. src_check) then goto mrloop diff --git a/qnx/package.qpg b/qnx/package.qpg index be5d5c16..c37e91be 100644 --- a/qnx/package.qpg +++ b/qnx/package.qpg @@ -25,10 +25,10 @@ - - - - + + + + @@ -63,7 +63,7 @@ - 1.2.3.3 + 1.2.3.4 Medium Stable diff --git a/trees.c b/trees.c index 94f28bbf..aa599a54 100644 --- a/trees.c +++ b/trees.c @@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2006 Jean-loup Gailly + * Copyright (C) 1995-2009 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -204,12 +204,12 @@ local void send_bits(s, value, length) * unused bits in value. */ if (s->bi_valid > (int)Buf_size - length) { - s->bi_buf |= (value << s->bi_valid); + s->bi_buf |= (ush)value << s->bi_valid; put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); s->bi_valid += length - Buf_size; } else { - s->bi_buf |= value << s->bi_valid; + s->bi_buf |= (ush)value << s->bi_valid; s->bi_valid += length; } } @@ -219,12 +219,12 @@ local void send_bits(s, value, length) { int len = length;\ if (s->bi_valid > (int)Buf_size - len) {\ int val = value;\ - s->bi_buf |= (val << s->bi_valid);\ + s->bi_buf |= (ush)val << s->bi_valid;\ put_short(s, s->bi_buf);\ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ s->bi_valid += len - Buf_size;\ } else {\ - s->bi_buf |= (value) << s->bi_valid;\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ s->bi_valid += len;\ }\ } diff --git a/win32/zlib.def b/win32/zlib.def index dbea9c55..c148ecb7 100644 --- a/win32/zlib.def +++ b/win32/zlib.def @@ -13,12 +13,18 @@ EXPORTS deflateCopy deflateReset deflateParams + deflateTune deflateBound deflatePrime + deflateSetHeader inflateSetDictionary inflateSync inflateCopy inflateReset + inflateReset2 + inflatePrime + inflateMark + inflateGetHeader inflateBack inflateBackEnd zlibCompileFlags @@ -49,8 +55,8 @@ EXPORTS gzclearerr ; checksum functions adler32 - adler32_combine crc32 + adler32_combine crc32_combine ; various hacks, don't look :) deflateInit_ @@ -58,6 +64,12 @@ EXPORTS inflateInit_ inflateInit2_ inflateBackInit_ + gzopen64 + gzseek64 + gztell64 + adler32_combine64 + crc32_combine64 + zError inflateSyncPoint get_crc_table - zError + inflateUndermine diff --git a/zconf.h b/zconf.h index 0a9f5a4c..e8d95531 100644 --- a/zconf.h +++ b/zconf.h @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2006 Jean-loup Gailly. + * Copyright (C) 1995-2007 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -353,6 +353,10 @@ typedef uLong FAR uLongf; #include "zlibdefs.h" /* created by configure */ +#ifdef _LARGEFILE64_SOURCE +# include +#endif + #ifndef SEEK_SET # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ diff --git a/zlib.3 b/zlib.3 index b83f234e..71c166c1 100644 --- a/zlib.3 +++ b/zlib.3 @@ -1,4 +1,4 @@ -.TH ZLIB 3 "2 October 2006" +.TH ZLIB 3 "21 December 2009" .SH NAME zlib \- compression/decompression library .SH SYNOPSIS @@ -133,7 +133,7 @@ before asking for help. Send questions and/or comments to zlib@gzip.org, or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). .SH AUTHORS -Version 1.2.3.3 +Version 1.2.3.4 Copyright (C) 1995-2006 Jean-loup Gailly (jloup@gzip.org) and Mark Adler (madler@alumni.caltech.edu). .LP diff --git a/zlib.h b/zlib.h index bb164c0f..4d13ca18 100644 --- a/zlib.h +++ b/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.3.3, October 2nd, 2006 + version 1.2.3.4, December 21st, 2009 - Copyright (C) 1995-2006 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2009 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,8 +37,8 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.3.3" -#define ZLIB_VERNUM 0x1233 +#define ZLIB_VERSION "1.2.3.4" +#define ZLIB_VERNUM 0x1234 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 #define ZLIB_VER_REVISION 3 @@ -163,11 +163,12 @@ typedef gz_header FAR *gz_headerp; /* constants */ #define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_PARTIAL_FLUSH 1 #define Z_SYNC_FLUSH 2 #define Z_FULL_FLUSH 3 #define Z_FINISH 4 #define Z_BLOCK 5 +#define Z_TREES 6 /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 @@ -273,7 +274,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); output buffer because there might be more output pending. Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumualte before producing output, in order to + decide how much data to accumulate before producing output, in order to maximize compression. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is @@ -281,7 +282,26 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression - algorithms and so it should be used only when necessary. + algorithms and so it should be used only when necessary. This completes the + current deflate block and follows it with an empty stored block that is three + bits plus filler bits to the next byte, followed by four bytes (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can @@ -321,7 +341,7 @@ ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and deflate() can be called again with more input and more output space to continue compressing. @@ -396,8 +416,8 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); must be called again after making room in the output buffer because there might be more output pending. - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, - Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after @@ -415,7 +435,16 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be - less than eight. + less than eight. data_type is set as noted here every time inflate() + returns for all flush options, and so can be used to determine the amount + of currently consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step @@ -432,7 +461,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); possible to the output buffer, and always uses the faster approach on the first call. So the only effect of the flush parameter in this implementation is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK is used. + because Z_BLOCK or Z_TREES is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the adler32 checksum of the dictionary @@ -443,12 +472,12 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. - inflate() will decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically. Any information - contained in the gzip header is not retained, so applications that need that - information should instead use raw inflate, see inflateInit2() below, or - inflateBack() and perform their own processing of the gzip header and - trailer. + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has @@ -456,7 +485,7 @@ ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to @@ -529,12 +558,12 @@ ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between - Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as - Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy - parameter only affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately. Z_FIXED prevents the - use of dynamic Huffman codes, allowing for a simpler decoder for special - applications. + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid @@ -561,11 +590,11 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, Depending on the size of the compression data structures selected by deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. In addition, the - current implementation of deflate will use at most the window size minus - 262 bytes of the provided dictionary. + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. Upon return of this function, strm->adler is set to the adler32 value of the dictionary; the decompressor may later use this value to determine @@ -575,7 +604,7 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, adler32 value is not computed and strm->adler is not set. deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream or if the compression method is bsort). deflateSetDictionary does not perform any compression: this will be done by deflate(). @@ -595,7 +624,7 @@ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and + (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ @@ -607,7 +636,7 @@ ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). + stream state was inconsistent (such as zalloc or state being Z_NULL). */ ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, @@ -760,7 +789,7 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the expected one (incorrect adler32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of @@ -794,7 +823,7 @@ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and + (such as zalloc being Z_NULL). msg is left unchanged in both source and destination. */ @@ -805,7 +834,19 @@ ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is + interpreted the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. */ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, @@ -820,10 +861,43 @@ ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, inflateReset(). bits must be less than or equal to 16, and that many of the least significant bits of value will be inserted in the input. + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, gz_headerp head)); /* @@ -833,9 +907,9 @@ ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, As inflate() processes the gzip stream, head->done is zero until the header is completed, at which time head->done is set to one. If a zlib stream is being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK can be used to - force inflate() to return immediately after header processing is complete - and before any actual data is decompressed. + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC @@ -952,7 +1026,7 @@ ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, nature of the error), or Z_STREAM_ERROR if the stream was not properly initialized. In the case of Z_BUF_ERROR, an input or output error can be distinguished using strm->next_in which will be Z_NULL only if in() returned - an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + an error. If strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning non-zero. (in() will always be called before out(), so strm->next_in is assured to be defined if out() returns non-zero.) Note that inflateBack() cannot return Z_OK. @@ -1065,7 +1139,7 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. + Upon exit, destLen is the actual size of the uncompressed buffer. This function can be used to decompress a whole file at once if the input file is mmap'ed. @@ -1279,7 +1353,7 @@ ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns + return the updated checksum. If buf is Z_NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC32 but can be computed much faster. Usage example: @@ -1305,7 +1379,7 @@ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is NULL, this function returns the required initial + updated CRC-32. If buf is Z_NULL, this function returns the required initial value for the for the crc. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the application. Usage example: @@ -1369,12 +1443,19 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, off64_t)); #endif -#if _FILE_OFFSET_BITS == 64 +#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS == 64 # define gzopen gzopen64 # define gzseek gzseek64 # define gztell gztell64 # define adler32_combine adler32_combine64 # define crc32_combine crc32_combine64 +# ifndef _LARGEFILE64_SOURCE + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN off_t ZEXPORT gzseek64 OF((gzFile, off_t, int)); + ZEXTERN off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, off_t)); +# endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); diff --git a/zlib.map b/zlib.map index da683a82..499213d4 100644 --- a/zlib.map +++ b/zlib.map @@ -42,3 +42,12 @@ ZLIB_1.2.2.3 { ZLIB_1.2.2.4 { inflatePrime; } ZLIB_1.2.2.3; + +ZLIB_1.2.3.3 { + inflateUndermine; +} ZLIB_1.2.2.4; + +ZLIB_1.2.3.4 { + inflateReset2; + inflateMark; +} ZLIB_1.2.3.3; diff --git a/zlib2ansi b/zlib2ansi new file mode 100755 index 00000000..0695f6b1 --- /dev/null +++ b/zlib2ansi @@ -0,0 +1,152 @@ +#!/usr/bin/perl + +# Transform K&R C function definitions into ANSI equivalent. +# +# Author: Paul Marquess +# Version: 1.0 +# Date: 3 October 2006 + +# TODO +# +# Asumes no function pointer parameters. unless they are typedefed. +# Assumes no literal strings that look like function definitions +# Assumes functions start at the beginning of a line + +use strict; +use warnings; + +local $/; +$_ = <>; + +my $sp = qr{ \s* (?: /\* .*? \*/ )? \s* }x; # assume no nested comments + +my $d1 = qr{ $sp (?: [\w\*\s]+ $sp)* $sp \w+ $sp [\[\]\s]* $sp }x ; +my $decl = qr{ $sp (?: \w+ $sp )+ $d1 }xo ; +my $dList = qr{ $sp $decl (?: $sp , $d1 )* $sp ; $sp }xo ; + + +while (s/^ + ( # Start $1 + ( # Start $2 + .*? # Minimal eat content + ( ^ \w [\w\s\*]+ ) # $3 -- function name + \s* # optional whitespace + ) # $2 - Matched up to before parameter list + + \( \s* # Literal "(" + optional whitespace + ( [^\)]+ ) # $4 - one or more anythings except ")" + \s* \) # optional whitespace surrounding a Literal ")" + + ( (?: $dList )+ ) # $5 + + $sp ^ { # literal "{" at start of line + ) # Remember to $1 + //xsom + ) +{ + my $all = $1 ; + my $prefix = $2; + my $param_list = $4 ; + my $params = $5; + + StripComments($params); + StripComments($param_list); + $param_list =~ s/^\s+//; + $param_list =~ s/\s+$//; + + my $i = 0 ; + my %pList = map { $_ => $i++ } + split /\s*,\s*/, $param_list; + my $pMatch = '(\b' . join('|', keys %pList) . '\b)\W*$' ; + + my @params = split /\s*;\s*/, $params; + my @outParams = (); + foreach my $p (@params) + { + if ($p =~ /,/) + { + my @bits = split /\s*,\s*/, $p; + my $first = shift @bits; + $first =~ s/^\s*//; + push @outParams, $first; + $first =~ /^(\w+\s*)/; + my $type = $1 ; + push @outParams, map { $type . $_ } @bits; + } + else + { + $p =~ s/^\s+//; + push @outParams, $p; + } + } + + + my %tmp = map { /$pMatch/; $_ => $pList{$1} } + @outParams ; + + @outParams = map { " $_" } + sort { $tmp{$a} <=> $tmp{$b} } + @outParams ; + + print $prefix ; + print "(\n" . join(",\n", @outParams) . ")\n"; + print "{" ; + +} + +# Output any trailing code. +print ; +exit 0; + + +sub StripComments +{ + + no warnings; + + # Strip C & C++ coments + # From the perlfaq + $_[0] =~ + + s{ + /\* ## Start of /* ... */ comment + [^*]*\*+ ## Non-* followed by 1-or-more *'s + ( + [^/*][^*]*\*+ + )* ## 0-or-more things which don't start with / + ## but do end with '*' + / ## End of /* ... */ comment + + | ## OR C++ Comment + // ## Start of C++ comment // + [^\n]* ## followed by 0-or-more non end of line characters + + | ## OR various things which aren't comments: + + ( + " ## Start of " ... " string + ( + \\. ## Escaped char + | ## OR + [^"\\] ## Non "\ + )* + " ## End of " ... " string + + | ## OR + + ' ## Start of ' ... ' string + ( + \\. ## Escaped char + | ## OR + [^'\\] ## Non '\ + )* + ' ## End of ' ... ' string + + | ## OR + + . ## Anything other char + [^/"'\\]* ## Chars which doesn't start a comment, string or escape + ) + }{$2}gxs; + +} diff --git a/zutil.h b/zutil.h index 32e9c890..bccc5815 100644 --- a/zutil.h +++ b/zutil.h @@ -165,6 +165,12 @@ extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 +#endif + +#ifdef _LARGEFILE64_SOURCE +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t #endif /* common defaults */