\ lzw code variable current-code defer next-code ( -- code ) : (next-code) current-code @ 1+ dup current-code ! ; latestxt is next-code \ dictionary stuff 0 value lzw-wordlist defer dictionary-add : (dictionary-add) ( addr len code -- ) get-current >r >r lzw-wordlist set-current nextname create r> , r> set-current ; latestxt is dictionary-add defer dictionary-contains : (dictionary-contains) ( addr len -- contains? ) dup 1 = if 2drop -1 else lzw-wordlist search-wordlist dup if nip endif endif ; latestxt is dictionary-contains defer dictionary-get : (dictionary-get) ( addr len -- code ) dup 1 = if drop c@ else lzw-wordlist search-wordlist drop execute @ endif ; latestxt is dictionary-get defer dictionary-init : (dictionary-init) 255 current-code ! table to lzw-wordlist ; latestxt is dictionary-init defer dictionary-destroy : (dictionary-destroy) ; latestxt is dictionary-destroy \ the compression algorithm defer current-char : (current-char) ( len addr wlen -- len addr nwlen len<=nwlen ) 1+ >r over r> swap over >= ; latestxt is current-char defer dismiss-old-chars : (dismiss-old-chars) ( len addr wlen -- nlen naddr 1 ) dup chars rot + >r - r> 1 ; latestxt is dismiss-old-chars variable stats defer code-out : (code-out) ( code -- ) stats @ 1+ stats ! dup 256 < if emit else [char] < emit space . [char] > emit endif space ; latestxt is code-out : lzw-compress ( addr len -- [outputs code] ) dictionary-init 0 stats ! swap 0 ( len addr 0 ) begin current-char while 2dup dictionary-contains 0= if ( add code for w+c ) 2dup next-code dictionary-add ( output code for w ) 1- 2dup dictionary-get code-out ( set w to c ) dismiss-old-chars endif repeat ( output code for w ) 1- dictionary-get code-out drop dictionary-destroy ; : lzw-stats ( addr len -- bits lzw-bits ) dup >r lzw-compress r> 8 * stats @ 12 * ; : lzw-compress-file ( addr len -- [outputs code] ) slurp-file lzw-compress ;