github.com/la5nta/wl2k-go@v0.11.8/lzhuf/writer.go (about) 1 // Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved. 2 // Use of this source code is governed by the MIT-license that can be 3 // found in the LICENSE file. 4 5 package lzhuf 6 7 import ( 8 "bufio" 9 "bytes" 10 "encoding/binary" 11 "io" 12 ) 13 14 // A Writer is an io.WriteCloser. 15 // Writes to a Writer are compressed and writter to w. 16 type Writer struct { 17 w *bufio.Writer 18 z *lzhuf 19 err error 20 21 crc16 bool 22 23 buf *bytes.Buffer // Encode data here and then write header and copy buf to actual writer 24 putbuf uint 25 putlen uint8 26 len, r, s int 27 lastMatchLength int 28 preFilled bool 29 fileSize int32 30 } 31 32 // NewB2Writer returns a new Writer with the extended FBB B2 format used by Winlink. 33 // 34 // It is the caller's responsibility to call Close on the WriteCloser when done. 35 // Writes may be buffered and not flushed until Close. 36 func NewB2Writer(w io.Writer) *Writer { return NewWriter(w, true) } 37 38 // NewWriter returns a new Writer. Writes to the returned writer are compressed and written to w. 39 // 40 // If crc16 is true, the header will be prepended with a checksum of the compressed data (as per FBB B2). 41 // 42 // It is the caller's responsibility to call Close on the WriteCloser when done. 43 // Writes may be buffered and not flushed until Close. 44 func NewWriter(w io.Writer, crc16 bool) *Writer { 45 wr := &Writer{w: bufio.NewWriter(w), buf: new(bytes.Buffer), crc16: crc16} 46 47 wr.z = newLZHUFF() 48 wr.z.InitTree() 49 50 wr.r = _N - _F 51 for i := 0; i < wr.r; i++ { 52 wr.z.textBuf[i] = ' ' 53 } 54 55 return wr 56 } 57 58 // Write writes a compressed form of p to the underlying io.Writer. The 59 // compressed bytes are not necessarily flushed until the Writer is closed. 60 func (w *Writer) Write(p []byte) (n int, err error) { 61 if w.err != nil { 62 return 0, err 63 } 64 65 for !w.preFilled && n < len(p) { // Pre-fill lookahead buffer 66 w.z.textBuf[w.r+w.len] = p[n] 67 n++ 68 w.fileSize++ 69 w.len++ 70 w.z.InsertNode(w.r - w.len) 71 72 w.lastMatchLength = 1 73 w.preFilled = w.len == _F 74 } 75 76 for n < len(p) { 77 w.advance(&p[n]) 78 n++ 79 w.fileSize++ 80 } 81 82 return n, nil 83 } 84 85 // Close closes the Writer, flushing any unwritten data to the underlying 86 // io.Writer, but does not close the underlying io.Writer. 87 func (w *Writer) Close() error { 88 if w.err != nil { 89 return w.err 90 } 91 92 // Write remaining data from the lookahead buffer 93 for w.len > 0 { 94 w.advance(nil) 95 } 96 w.encode() 97 w.encodeEnd() 98 99 var lengthBytes bytes.Buffer 100 binary.Write(&lengthBytes, binary.LittleEndian, w.fileSize) 101 102 // Write checksum (2 bytes) 103 if w.crc16 { 104 sum := crc(append(lengthBytes.Bytes(), w.buf.Bytes()...)) 105 if err := binary.Write(w.w, binary.LittleEndian, sum); err != nil { 106 return err 107 } 108 } 109 110 // Write filesize (4 bytes) 111 if _, err := io.Copy(w.w, &lengthBytes); err != nil { 112 return err 113 } 114 115 // Write compressed data 116 if _, err := io.Copy(w.w, w.buf); err != nil { 117 return err 118 } 119 120 return w.w.Flush() 121 } 122 123 func (w *Writer) advance(c *byte) { 124 if c != nil { 125 // Add to lookahead buffer 126 w.z.textBuf[w.s] = *c 127 if w.s < _F-1 { 128 w.z.textBuf[w.s+_N] = *c 129 } 130 w.len++ 131 } 132 133 // Process one byte from lookahead buffer 134 w.z.InsertNode(w.r) 135 w.lastMatchLength-- 136 if w.lastMatchLength == 0 { 137 w.encode() 138 } 139 w.z.DeleteNode(w.s) 140 w.s = (w.s + 1) & (_N - 1) 141 w.r = (w.r + 1) & (_N - 1) 142 w.len-- 143 } 144 145 func (w *Writer) encode() { 146 if w.len == 0 { 147 return 148 } 149 150 // Encode from lookahead buffer 151 if w.z.matchLength > w.len { 152 w.z.matchLength = w.len 153 } 154 if w.z.matchLength <= _Threshold { 155 w.z.matchLength = 1 156 w.encodeChar(uint(w.z.textBuf[w.r])) 157 } else { 158 w.encodeChar(uint(255 - _Threshold + w.z.matchLength)) 159 w.encodePosition(uint(w.z.matchPosition)) 160 } 161 162 w.lastMatchLength = w.z.matchLength 163 } 164 165 func (w *Writer) encodeEnd() { 166 if w.putlen == 0 { 167 return 168 } 169 w.err = w.buf.WriteByte(byte(w.putbuf >> 8)) 170 } 171 172 func (w *Writer) encodeChar(c uint) { 173 // travel from leaf to root 174 i, j := uint(0), int(0) 175 k := w.z.prnt[c+_T] 176 for { 177 i >>= 1 178 j++ 179 180 // if node's address is odd-numbered, choose bigger brother node 181 if k&1 != 0 { 182 i += 0x8000 183 } 184 185 if k = w.z.prnt[k]; k == _R { 186 break 187 } 188 } 189 w.putCode(j, i) 190 w.z.update(int(c)) 191 } 192 193 func (w *Writer) encodePosition(c uint) { 194 var i uint 195 196 // output upper 6 bits by table lookup 197 i = c >> 6 198 w.putCode(int(pLen[i]), uint(pCode[i])<<8) 199 200 // output lower 6 bits verbatim 201 w.putCode(6, (c&0x3f)<<10) 202 } 203 204 // Output c bits of code 205 func (w *Writer) putCode(l int, c uint) { 206 if w.err != nil { 207 return 208 } 209 210 w.putbuf |= c >> w.putlen 211 w.putlen += uint8(l) 212 213 if w.putlen < 8 { 214 return 215 } 216 217 w.err = w.buf.WriteByte(byte(w.putbuf >> 8)) 218 w.putlen -= 8 219 220 if w.putlen >= 8 { 221 w.err = w.buf.WriteByte(byte(w.putbuf)) 222 223 w.putlen -= 8 224 w.putbuf = c << uint(l-int(w.putlen)) 225 } else { 226 w.putbuf <<= 8 227 } 228 }