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) }