( ulz encoder ) |10 @Console &vector $2 &read $1 &pad $5 &write $1 &err $1 |a0 @File &vector $2 &success $2 &stat $2 &delete $1 &append $1 &name $2 &length $2 &read $2 &write $2 |0000 @src $30 @dst $30 @ptr $1 @match-len $2 @output-ptr $2 @match-ctl $2 @dict-best $2 @combine $2 @dict $2 @dict-len $2 |0100 @ready-src ( -> ) ;&await .Console/vector DEO2 BRK &await ( -> ) .Console/read DEI .src skey ?ready-dst BRK @ready-dst ( -> ) ;&await .Console/vector DEO2 BRK &await ( -> ) .Console/read DEI .dst skey ?on-ready BRK @on-ready ( -> ) ( | load raw ) ;src .File/name DEO2 #4000 .File/length DEO2 ;raw .File/read DEO2 ;raw .File/success DEI2 uxn_lz_compress ( | write ) ;dst .File/name DEO2 .output-ptr LDZ2 ;compressed SUB2 .File/length DEO2 ;compressed .File/write DEO2 ( halt ) #800f DEO BRK @ ( byte -- ) .output-ptr LDZ2 INC2k .output-ptr STZ2 STA JMP2r @uxn_lz_compress ( input* length* -- ) ( | fill variables ) ;compressed .output-ptr STZ2 ADD2k NIP2 SWP2 &w ( end* start* -- ) EQU2k ?&end ( | get available dictionary size ) DUP2 ;raw SUB2 #0100 LTH2k ?{ SWP2 } POP2 .dict-len STZ2 ( | size of the string to search for ) SUB2k #3fff #0004 ADD2 LTH2k ?{ SWP2 } POP2 ,&string-len STR2 ( | itterate through the dictionary ) #0000 .match-len STZ2 DUP2 .dict-len LDZ2 SUB2 .dict STZ2 &for1 ( for ; dict_len; dict++, dict_len-- ) .dict-len LDZ2 #0000 EQU2 ?&end-for1 ( Find common prefix length with the string ) #0000 &for2 ( for i = 0;; i++ ) ( | If we reach the end of the string, it's the best possible match. ) DUP2 [ LIT2 &string-len $2 ] NEQ2 ?{ DUP2 .match-len STZ2 .dict LDZ2 .dict-best STZ2 POP2 !&done-search } ( | in[i] != dict[i % dict_len] break; ) ( a ) ADD2k LDA STH ( b ) DUP2 .dict-len LDZ2 DIV2k MUL2 SUB2 .dict LDZ2 ADD2 ( res ) LDA STHr NEQ ?&end-for2 INC2 ORAk ?&for2 &end-for2 ( | i > match_len ) DUP2 .match-len LDZ2 LTH2 ?{ DUP2 .match-len STZ2 .dict LDZ2 .dict-best STZ2 } POP2 .dict LDZ2 INC2 .dict STZ2 .dict-len LDZ2 #0001 SUB2 .dict-len STZ2 !&for1 &end-for1 &done-search ( -- ) ( CPY ) .match-len LDZ2 #0003 GTH2 ?op-cpy ( LIT ) !op-lit &end POP2 POP2 JMP2r ( @|opcodes ) @op-cpy ( in* -- ) ( | More numeric range: treat 0 as 4, 1 as 5, etc. ) .match-len LDZ2 #0004 SUB2 .match-ctl STZ2 ( | CPY2 ) .match-ctl LDZ2 #003f GTH2 ?&cpy2 ( | *output_ptr++ = match_ctl | 0x80; ) .match-ctl LDZ2 NIP #80 ORA !&cpy-resume &cpy2 ( -- ) .match-ctl LDZ2 SWP #c0 ORA &cpy-resume ( -- ) ( | *output_ptr++ = in - dict_best - 1; ) DUP2 .dict-best LDZ2 SUB2 #0001 SUB2 NIP ( | in += match_len; Advance input by size of the match ) .match-len LDZ2 ADD2 ( | Disable combining previous literal, if any ) #0000 .combine STZ2 !uxn_lz_compress/w @op-lit ( in* -- ) .combine LDZ2 ORA ?&combine ( | start a new literal ) ( | Store this address, and later use it to increment the literal size. ) ( | combine = output_ptr++; ) .output-ptr LDZ2 INC2k .output-ptr STZ2 .combine STZ2 ( | *combine = 0; ) #00 .combine LDZ2 LDA2 STA ( | *output_ptr++ = *in++; ) LDAk INC2 !uxn_lz_compress/w &combine ( -- ) ( | if ++*combine == 127 ) .combine LDZ2 LDA INC DUP #7f NEQ ?{ POP #00 } .combine LDZ2 STA LDAk INC2 !uxn_lz_compress/w ( @|stdlib ) @skey ( key buf -- proc ) OVR #21 LTH ?&eval #00 SWP sput #00 JMP2r &eval POP2 #01 JMP2r @scap ( str* -- end* ) &w INC2 & LDAk ?&w JMP2r @sput ( chr str* -- ) scap/ INC2k #00 ROT ROT STA STA JMP2r @getc ( -- b ) #0001 .File/length DEO2 ;&b .File/read DEO2 [ LIT &b $1 ] JMP2r ( @|memory ) @raw $4000 @compressed $4000