85 lines
1.7 KiB
Go
85 lines
1.7 KiB
Go
package compression
|
|
|
|
import (
|
|
"io"
|
|
|
|
"github.com/inkyblackness/hacked/ss1/serial"
|
|
)
|
|
|
|
type compressor struct {
|
|
coder serial.Coder
|
|
writer *WordWriter
|
|
|
|
dictBuffer dictEntryBuffer
|
|
overtime int
|
|
dictionary *dictEntry
|
|
dictionarySize int
|
|
curEntry *dictEntry
|
|
}
|
|
|
|
// NewCompressor creates a new compressor instance over a writer.
|
|
func NewCompressor(target io.Writer) io.WriteCloser {
|
|
coder := serial.NewEncoder(target)
|
|
obj := &compressor{
|
|
coder: coder,
|
|
writer: NewWordWriter(coder),
|
|
dictionary: rootDictEntry(),
|
|
dictionarySize: 0,
|
|
overtime: 0}
|
|
|
|
obj.resetDictionary()
|
|
|
|
return obj
|
|
}
|
|
|
|
func (obj *compressor) resetDictionary() {
|
|
obj.dictionarySize = 0
|
|
for i := 0; i < 0x100; i++ {
|
|
obj.dictionary.Add(byte(i), Word(i), obj.dictBuffer.entry(Word(i)))
|
|
}
|
|
obj.curEntry = obj.dictionary
|
|
}
|
|
|
|
func (obj *compressor) Close() error {
|
|
obj.writer.Write(obj.curEntry.key)
|
|
obj.writer.Close()
|
|
|
|
return obj.coder.FirstError()
|
|
}
|
|
|
|
func (obj *compressor) Write(p []byte) (n int, err error) {
|
|
for _, input := range p {
|
|
obj.addByte(input)
|
|
}
|
|
|
|
return len(p), obj.coder.FirstError()
|
|
}
|
|
|
|
func (obj *compressor) addByte(value byte) {
|
|
nextEntry := obj.curEntry.next[int(value)]
|
|
if nextEntry != nil {
|
|
obj.curEntry = nextEntry
|
|
} else {
|
|
obj.writer.Write(obj.curEntry.key)
|
|
|
|
key := Word(int(literalLimit) + obj.dictionarySize)
|
|
if key < Reset {
|
|
obj.curEntry.Add(value, key, obj.dictBuffer.entry(key))
|
|
obj.dictionarySize++
|
|
} else {
|
|
obj.onKeySaturation()
|
|
}
|
|
|
|
obj.curEntry = obj.dictionary.next[value]
|
|
}
|
|
}
|
|
|
|
func (obj *compressor) onKeySaturation() {
|
|
obj.overtime++
|
|
if obj.overtime > 1000 {
|
|
obj.writer.Write(Reset)
|
|
obj.resetDictionary()
|
|
obj.overtime = 0
|
|
}
|
|
}
|