/* 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