/* l1lzw.psm * by pts@fazekas.hu at Sun Sep 22 01:23:57 CEST 2002 * formerly lzwdecode.psm -- a real, effective implementation of PostScript * LanguageLevel2 and PDF /LZWDecode filters (same as the LZW * compression used in TIFF raster image files), in PostScript Level1 * derived from lzwdecode.c by pts@fazekas.hu (at Wed Sep 4 11:11:45 CEST 2002) * by pts@fazekas.hu at Wed Sep 4 11:30:18 CEST 2002 * first run at Wed Sep 4 14:18:43 CEST 2002 * linux-2.2.8 fine at Wed Sep 4 14:46:05 CEST 2002 * rets linux-2.2.8 fine at Wed Sep 4 15:58:24 CEST 2002 * works at Wed Sep 4 16:41:29 CEST 2002 * linux-2.2.8 58388480 uncompressed: * lzwdecode.c: 17s user time (*1) * lzw_codec.c: 6s user time (*0.353) * lzwdecode.psm: 1080s user time (*63.53) * stack-reorg works at Thu Sep 5 12:31:23 CEST 2002 * lzwdecode.psm: 980s user time (*57.65) * lzwdecode.eps works at Thu Sep 5 14:31:23 CEST 2002 * last non-` at Sat Sep 21 23:40:03 CEST 2002 * * Note that the LZW compression (but not uncompression) is patented by * Unisys (patent number #4,558,302), so use this program at your own legal * risk! * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ % OK: test with aaaaaaaaaaaa (stack overflow) #include "psmlib.psm" #if USE_A85D #else #if USE_HEXD #else #define USE_BINARY 1 #endif #endif #if USE_NO_BIND #define BIND_DEF def #else #define BIND_DEF bind def #endif #if USE_PIN /* --- .pin begins */ % %!PS-Adobe-3.0`E %%Inflater: pts@fazekas.hu l1lzw.psm %%Pages: 1 `X%%DocumentData: `B %%LanguageLevel: 1 `I%%EndComments %%Page: 1 1 % % save`s `R % % vvv 50 % begin % % % best to make it first % % best to make it second % K: .toksubs. % % % % % % % % % % % % %C! %D! % % % % %S! % % % % %d! % % % % % % % % % % -- not needed, long enough % #if USE_SHORT_NAMES % % % % % % % % % % % % % % % #endif #if USE_A85D % % % % #endif #if USE_HEXD % % % % #endif #if USE_BINARY % #if USE_PALETTE % #endif % % #endif % % #if USE_BINARY /F /filter where {pop false}{true} ifelse def #else #if USE_A85D {mark currentfile/ASCII85Decode filter/T exch def}stopped #endif #if USE_HEXD {mark currentfile/ASCIIHexDecode filter/T exch def}stopped #endif /F exch def cleartomark #endif `w `h `b[1 0 0 -1 0 `h] #if USE_TRUE true #else F #endif % % 4096 string % #if USE_SHORT_NAMES_OLD #define a85_getc d #define Flzwdecode_init e #define Flzwdecode_do f #define Fclear I #endif #if USE_CURRENTFILE #define STDIN currentfile #endif % % Fmain % ^^^ must call it here, because _now_ `currentfile fileposition` is at the real image data % vvv avoid Fmain here because of the substituted constant 4717 (and also size increase!) Flzwdecode_init % !! Imp: less code here, init as a macro /Fmain 4096 string def { Fmain Flzwdecode_do } `i Fmain Flzwdecode_do pop % make sure that mEOD has been read TE_read_eod % % #if USE_BINARY /F currentfile/LZWDecode filter def #else /F T/LZWDecode filter def #endif F `i F closefile #if !USE_BINARY T closefile #endif % % #else 40 dict begin % LZWDict #endif #if 0 % (a) () 0 1 % DumpStack % TYPE_STACK(-str -int) % ZZZZZZZZZ % (a) 3 % ASSERT_STACK(-str -int,(1)) % pop pop #endif % Defaults: /EarlyChange 1 /UnitLength 8 /LowBitFirst false #if !USE_EARLYCHANGE_1 /EarlyChange 1 def #endif #if !USE_UNITLENGTH_8 /UnitLength 8 def #endif #if !USE_LOWBITFIRST_FALSE /LowBitFirst false def #endif /* OK: test with aaaaaaaaaaaa (stack overflow) */ #define STACKSIZE 4096 #define STACKSIZE_M1 4095 #if USE_SHORT_NAMES_OLD #endif % Uninitialized const-vars /prefix 4096 array def % array[0..4095] of 0..4095 /suffix 4096 string def /stack STACKSIZE string def #if USE_CURRENTFILE #else /STDIN (%stdin) (r) file def /STDOUT (%stdout) (w) file def % Fmain #endif #if USE_HEXD /C(.)def #endif % Uninitialized vars #if USE_UNITLENGTH_8 #define mClear 256 #define mEOD 257 #endif #if !USE_NO_NULLDEF /preread null def /past null def /nentry null def /had_eof null def % 0..2 #if !USE_UNITLENGTH_8 /mEOD null def /mClear null def #endif /nentrybig null def /nentryb null def /gcode null def % Flzwdecode_do /stackslice null def % Flzwdecode_do #if USE_A85D /S null def /D null def /C null def #endif #endif #if USE_PIN { #endif #if USE_A85D /a85_getc{ % Simple getchar() of ASCII85Decode filter :-)), formerly /d PSM_A85_GETC }BIND_DEF #endif %** - Fclear - /Fclear { #if USE_UNITLENGTH_8 /nentry 258 def /nentryb 9 def #if USE_EARLYCHANGE_1 /nentrybig 512 def #else /nentrybig 513 EarlyChange sub def #endif #else /nentry 1 UnitLength bitshift 2 add def /nentryb UnitLength 1 add def /nentrybig 1 nentryb bitshift #if !USE_EARLYCHANGE_1 1 EarlyChange sub add #endif def #endif 0 1 nentry 3 sub { % Stack: code(0..255) dup prefix exch dup % Stack: code prefix code code put suffix exch dup put } for } BIND_DEF %** - Flzwdecode_init - %** The caller should have modified /EarlyChange, /UnitLength and /LowBitFirst /Flzwdecode_init { TE_init /past 8 def /preread 0 def /had_eof 0 def #if !USE_EARLYCHANGE_1 ASSERT_TRUE(EarlyChange 0 INT_EQ EarlyChange 1 INT_EQ BOOL_BIN(or,(ora1)), (lsp->EarlyChange==0 || lsp->EarlyChange==1)) #endif #if USE_UNITLENGTH_8 Fclear #else ASSERT_TRUE(3 UnitLength INT_LE UnitLength 8 INT_LE and, (3<=lsp->UnitLength && lsp->UnitLength<=8)) Fclear /mClear 1 UnitLength bitshift def /mEOD 1 UnitLength bitshift 1 add def #endif /stackslice stack 0 0 getinterval def /gcode {} def } BIND_DEF %** Flzwdecode_do %** The caller should have called Flzwdecode_init. Flzwdecode_do fills the %** entire str.pre unless EOF on input prevents this. str.pre can have any %** length. Doesn`t depend of str.pre passed to it on the previous invocation. /Flzwdecode_do { ASSERT_TRUE(10 nentry INT_LE,(lsp->nentry>=10)) dup % Silently leave on stack stackslice had_eof % Stack: reto rets==reto stackslice had_eof { % W2:WHILE(1) % Stack: len++ stackslice had_eof* dup 0 INT_NE { /had_eof exch def pop /stackslice stack 0 0 getinterval def exit % exit(W2) }if % return(len) ASSERT_STACK(-str -str -str -int,(W2.in)) pop % Stack: len++ stackslice % Stack: reto rets stackslice ASSERT_STACK(-str -str -str,(3)) /* Flush stack. */ 2 copy length exch length ge { % return from the stack (rets.length<=stackslice.length % Stack: reto rets stackslice dup 0 3 index length getinterval % Stack: reto rets stackslice stackslice[0.. rets.length] 2 index copy pop % Stack: reto rets stackslice /stackslice exch 2 index length dup % Stack: reto rets /stackslice stackslice rets.length rets.length 2 index length exch sub % Stack: reto rets /stackslice stackslice rets.length stackslice.length-rets.length getinterval def 0 0 getinterval % Stack: reto rets[0,0] ASSERT_STACK(-str -str,(4)) exit % exit(W2) }if % vvv flush the whole stack into rets 2 copy exch copy pop % stackslice -> rets length dup % Stack: reto rets stackslice.length stackslice.length 2 index length exch sub % Stack: reto rets stackslice.length rets.length-stackslice.length getinterval % Stack: reto rets ASSERT_STACK(-str -str,(5)) ASSERT_TRUE(dup length 1 exch INT_LE,(len>=1)) % Stack: len++ /* Read next code (0..4095) from input to `code` */ nentry nentrybig INT_EQ { /nentryb nentryb 1 add def /nentrybig 1 nentryb bitshift #if !USE_EARLYCHANGE_1 1 EarlyChange sub add #endif def }if ASSERT_TRUE(4 nentryb INT_LE nentryb 12 INT_LE and, (4<=nentryb && nentryb<=12)) ASSERT_STACK(-str -str,(6)) { % W1:WHILE(1) ASSERT_STACK(-str -str,(7)) % Stack: len++ % assert(had_eof==0); -- cannot check this right here... ASSERT_TRUE(1 past INT_LE past 8 INT_LE and,(1<=past && past<=8)) ASSERT_TRUE(1 nentryb INT_LE nentryb 13 INT_LE and,(1<=nentryb && nentryb<=13)) #if !USE_LOWBITFIRST_FALSE LowBitFirst { % Dat: this is untested, even the stack /code 0 def 0 1 netryb 1 sub { % Stack: len++ i past 8 INT_EQ { /preread TE_read(def /past 0 def) #if !USE_NO_EOF { TE_read_pop pop /had_eof 2 def exit % exit from inner `for` }ifelse #endif }if preread 1 and 0 INT_NE { 1 exch bitshift code INT_BIN(add,(ora2)) /code exch def }{pop}ifelse % Stack: len++ /preread preread -1 bitshift def /past past 1 add def }for had_eof 0 INT_NE {0 had_eof exit}if % exit(W1) code }{ % else: !LowBitFirst #endif past nentryb add % Stack: len++ past* ASSERT_STACK(-str -str -int,(8)) dup 7 INT_GT { dup 16 INT_GT { 16 sub preread 8 bitshift % Stack: len++ past* code /preread TE_read(def) #if !USE_NO_EOF { TE_read_pop pop % Stack: len++ past* code pop /past exch def 0 2 % Stack: len++ garbage had_eof* exit % exit(W1) }ifelse #endif }{ 8 sub 0 % /code }ifelse % Stack: len++ past* code ASSERT_STACK(-str -str -int -int,(9)) ASSERT_TRUE(1 index 1 exch INT_LE 2 index 8 INT_LE and,(1<=lsp->past && lsp->past<=8)) preread INT_BIN(add,(or1)) % Stack: len++ past* code|preread 1 index bitshift % Stack: len++ past* code*==(code|preread)<preread&=(1<<(8-lsp->past))-1; /* do PCLEAR */ % Stack: len++ code preread0 past* dup /past exch def 8 sub bitshift INT_BIN(add,(or2)) % Stack: len++ code*==code|(preread>>(8-past)) ASSERT_STACK(-str -str -int,(12)) #if !USE_LOWBITFIRST_FALSE }ifelse % if: /LowBitFirst #endif % Stack: len++ code ASSERT_STACK(-str -str -int,(13)) dup mClear INT_LT{ /* Speed-up: process normal literal data code in `code` */ % Stack: len++ code ASSERT_STACK(-str -str -int,(15b)) ASSERT_TRUE(dup 1 add mClear INT_LE,(code too high2)) /* /ioerror */ ASSERT_TRUE(nentry 4095 INT_LE,(table full2)) /* /ioerror */ prefix nentry 2 index put suffix nentry 1 sub 2 index put stack exch STACKSIZE_M1 exch put % Stack: len++ ASSERT_STACK(-str -str,(15c)) /nentry nentry 1 add def stack STACKSIZE_M1 1 getinterval % Stack: len++ stackslice 0 % /had_eof ASSERT_STACK(-str -str -str -int,(20b)) exit % exit(W1) }if dup mEOD INT_GT{ /* Process normal data code (either literal or above-mEOD) in `code` */ % Stack: len++ code ASSERT_STACK(-str -str -int,(15)) ASSERT_TRUE(dup 1 add nentry INT_LE,(code too high)) /* /ioerror */ ASSERT_TRUE(nentry 4095 INT_LE,(table full)) /* /ioerror */ prefix nentry 2 index put % ASSERT_TRUE(0 stacklen INT_EQ,(lsp->stacklen==0)) % Stack: len++ code stack exch dup STACKSIZE_M1 exch nentry 1 sub % Stack: len++ stack code STACKSIZE_M1 code nentry-1 ASSERT_STACK(-str -str -aryb -int -int -int -int,(16)) INT_EQ { % the rightmost char of this entry will be equal to the leftmost % side, as soon as we calculate the leftmost side /gcode { stack STACKSIZE_M1 2 index put /gcode {} def } def }if %{ % ASSERT_TRUE(1 index nentry 2 sub INT_LE,(codenentry-1)) % % /gcode { } def %}ifelse -1 0 % Stack: len++ -{...} stack code STACKSIZE-1|STACKSIZE-2 -1 0 { % 1 index (code=) print === % Stack: len -{...} stack code putidx ASSERT_STACK(-str -str -aryb -int -int,(17)) exch dup prefix exch get 2 copy % Stack: len -{...} stack putidx code prefix[code] code prefix[code] INT_EQ{pop exit}if % exit from inner for % Stack: len -{...} stack putidx code prefix[code] 4 copy pop suffix exch get % Stack: len -{...} stack putidx code prefix[code] stack putidx suffix[code] put exch pop exch pop % Stack: len -{...} stack prefix[code] }for % Stack: len stack unused_putidx code ASSERT_STACK(-str -str -aryb -int -int,(18)) dup suffix exch get % dup (sufcode=) print === gcode suffix nentry 1 sub 2 index put pop % Stack: len stack unused_putidx suffix[code] 3 copy put pop % Stack: len stack unused_putidx dup STACKSIZE exch sub % Stack: len stack putidx STACKSIZE-putidx ASSERT_STACK(-str -str -aryb -int -int,(19)) getinterval ASSERT_TRUE(1 index length 0 INT_NE,(len!=0)) /nentry nentry 1 add def % Stack: len++ stackslice 0 % /had_eof ASSERT_STACK(-str -str -str -int,(20)) exit % exit(W1) }if dup mEOD INT_EQ{ % Stack: len++ code==mEOD pop 0 1 % Stack: len++ garbage had_eof* ASSERT_STACK(-str -str -int -int,(14)) exit % exit(W1) }if ASSERT_TRUE(dup mClear INT_EQ,(code==mClear)) pop Fclear % Stack: len++ ASSERT_STACK(-str -str,(21)) }loop % /W1:WHILE(1) % Stack: len++ stackslice|garbage had_eof* ASSERT_TRUE(TYPE_STACK(-str -str -str -int) TYPE_STACK(-str -str -int -int -bool) BOOL_BIN(or,(ora4)),(stack leaving W2)) } loop % /W2:WHILE(1) % Stack: len++ % Stack: reto rets(buffer-not-read) ASSERT_STACK(-str -str,(22)) length 1 index length exch sub % Stack: reto reto.length-rets.length 0 exch getinterval % Stack: reto.pre(return-value) } BIND_DEF % Flzwdecode_do #if USE_PIN %/Fmain { %} BIND_DEF } % close the function defs section % % %%BeginData: exec `S %%EndData end restore showpage %%Trailer %%EOF % #else #if !USE_CURRENTFILE % --- Main %** - Fmain - /Fmain { /mys 8192 string def Flzwdecode_init { mys Flzwdecode_do % Stack: mys.pre dup length 0 INT_EQ{exit}if STDOUT exch writestring } loop had_eof 1 INT_EQ { { STDIN read {pop}{exit}ifelse }loop }if } BIND_DEF Fmain #endif end % LZWDict #endif %%EOF