github.com/la5nta/wl2k-go@v0.11.8/lzhuf/reader.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 "bytes" 9 "encoding/binary" 10 "errors" 11 "io" 12 ) 13 14 // ErrChecksum indicates a checksum or file size mismatch on decode. 15 var ErrChecksum = errors.New("lzhuf: invalid checksum") 16 17 // A Reader is an io.Reader that can be read to retrieve 18 // uncompressed data from a lzhuf-compressed file. 19 // 20 // Lzhuf files store a length and optionally a checksum of the uncompressed data. 21 // The Reader will return io.ErrUnexpectedEOF when Read reaches the end of the 22 // uncompressed data. The checksum is verified on Close. 23 // 24 // Clients should treat data returned by Read as tentative until they receive the io.EOF 25 // marking the end of the data. Data consistency should then be verified by calling Close. 26 type Reader struct { 27 r bitReader 28 z *lzhuf 29 err error 30 31 crc16 bool 32 crcw *crcWriter 33 34 header struct { 35 crc crc16 // 2 bytes (only in B2 mode) 36 size int32 // 4 bytes 37 } 38 39 state struct { 40 pos int32 41 r int 42 buf bytes.Buffer // Buffer to hold decoded but not yet Read 43 } 44 } 45 46 // NewB2Reader creates a new Reader expecting the extended FBB B2 format used by Winlink. 47 // 48 // It is the caller's responsibility to call Close on the Reader when done. 49 func NewB2Reader(r io.Reader) (*Reader, error) { return NewReader(r, true) } 50 51 // NewReader creates a new Reader reading the given reader. 52 // 53 // If crc16 is true, the Reader will expect and verify a checksum of the compressed data (as per FBB B2). 54 // 55 // It is the caller's responsibility to call Close on the Reader when done. 56 func NewReader(r io.Reader, crc16 bool) (*Reader, error) { 57 d := &Reader{z: newLZHUFF(), crc16: crc16, crcw: newCRCWriter()} 58 d.state.r = _N - _R 59 for i := 0; i < _N-_F; i++ { 60 d.z.textBuf[i] = ' ' 61 } 62 63 if d.crc16 { 64 err := binary.Read(r, binary.LittleEndian, &d.header.crc) 65 if err != nil { 66 return nil, err 67 } 68 } 69 70 // Copy every byte read into our CRC writer (for checksum) 71 r = io.TeeReader(r, d.crcw) 72 d.r = newBitReader(r) 73 74 return d, binary.Read(r, binary.LittleEndian, &d.header.size) 75 } 76 77 // Close closes the Reader. It does not close the underlying io.Reader. 78 // 79 // If an error was encountered during Read, the error will be returned. 80 // ErrChecksum is returned if the filesize header does not match the 81 // number of bytes read, or a crc16 checksum (B2 format) was expected 82 // but did not match. 83 // 84 // If no error is returned, the file has been successfully decompressed. 85 func (d *Reader) Close() error { 86 switch { 87 case d.err != nil: 88 return d.err 89 case d.r.Err() != nil: 90 return d.r.Err() 91 case d.crc16 && d.header.crc != d.crcw.Sum(): 92 return ErrChecksum 93 case d.header.size != d.state.pos-int32(d.state.buf.Len()): 94 return ErrChecksum 95 default: 96 return nil 97 } 98 } 99 100 // Read reads uncompressed data into p. It returns the number of bytes read into p. 101 // 102 // At EOF, count is 0 and err is io.EOF (unless len(p) is zero). 103 func (d *Reader) Read(p []byte) (n int, err error) { 104 switch { 105 case d.r.Err() == io.EOF && d.state.pos < d.header.size: 106 d.err = io.ErrUnexpectedEOF 107 case d.r.Err() != nil: 108 d.err = d.r.Err() 109 case d.state.pos == d.header.size && d.state.buf.Len() == 0: 110 return 0, io.EOF 111 } 112 113 if d.err != nil { 114 return 0, d.err 115 } 116 117 n, err = d.state.buf.Read(p) 118 119 var i, j, k, c int 120 for n < len(p) && d.r.Err() == nil && d.state.pos < d.header.size { 121 c = int(d.decodeChar()) 122 123 if c < 256 { 124 p[n] = byte(c) 125 n++ 126 d.z.textBuf[d.state.r] = byte(c) 127 d.advanceState() 128 continue 129 } 130 131 i = (d.state.r - d.decodePosition() - 1) & (_N - 1) 132 j = c - 255 + _Threshold 133 for k = 0; k < j; k++ { 134 c = int(d.z.textBuf[(i+k)&(_N-1)]) 135 if n < len(p) { 136 p[n] = byte(c) 137 n++ 138 } else { 139 d.state.buf.WriteByte(byte(c)) 140 } 141 d.z.textBuf[d.state.r] = byte(c) 142 d.advanceState() 143 } 144 } 145 146 return n, nil 147 } 148 149 func (d *Reader) advanceState() { 150 d.state.r++ 151 d.state.r &= (_N - 1) 152 d.state.pos++ 153 } 154 155 func (d *Reader) decodeChar() (c uint) { 156 c = uint(d.z.son[_R]) 157 158 // Travel from root to leaf, 159 // choosing the smaller child node (son[]) if the read bit is 0, 160 // the bigger (son[]+1} if 1 161 for c < _T { 162 c += uint(d.getBit()) 163 c = uint(d.z.son[c]) 164 } 165 c -= _T 166 d.z.update(int(c)) 167 return c 168 } 169 170 func (d *Reader) decodePosition() int { 171 var i, j, c uint 172 173 // Recover upper 6 bits from table 174 i = uint(d.getByte()) 175 c = uint(dCode[i]) << 6 176 j = uint(dLen[i]) 177 178 // Read lower 6 bits verbatim 179 for j -= 2; j > 0; j-- { 180 i = (i << 1) + uint(d.getBit()) 181 } 182 return int(c | (i & 0x3f)) 183 } 184 185 func (d *Reader) getBit() (c int) { return d.r.ReadBits(1) } 186 func (d *Reader) getByte() (c int) { return d.r.ReadBits(8) }