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  }