github.com/grailbio/base@v0.0.11/compress/libdeflate/libdeflate_nocgo.go (about)

     1  // Copyright 2018 GRAIL, Inc.  All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !cgo || arm64
     6  // +build !cgo arm64
     7  
     8  package libdeflate
     9  
    10  // Fall back on the pure-go compress/flate package if cgo support is
    11  // unavailable, to make it safe to include this package unconditionally.
    12  
    13  import (
    14  	"bytes"
    15  	"compress/flate"
    16  	"compress/gzip"
    17  	"io"
    18  )
    19  
    20  type Decompressor struct{}
    21  
    22  func (dd *Decompressor) Init() error {
    23  	return nil
    24  }
    25  
    26  // Decompress performs raw DEFLATE decompression on a byte slice.  outData[]
    27  // must be large enough to fit the decompressed data.  Byte count of the
    28  // decompressed data is returned on success (it may be smaller than
    29  // len(outData)).
    30  func (dd *Decompressor) Decompress(outData, inData []byte) (int, error) {
    31  	dataReader := bytes.NewReader(inData)
    32  	actualDecompressor := flate.NewReader(dataReader)
    33  	// Copy of readToEOF() in github.com/biogo/hts/bgzf/cache.go.
    34  	n := 0
    35  	outDataMax := len(outData)
    36  	var err error
    37  	for err == nil && n < outDataMax {
    38  		var nn int
    39  		nn, err = actualDecompressor.Read(outData[n:])
    40  		n += nn
    41  	}
    42  	switch {
    43  	case err == io.EOF:
    44  		return n, nil
    45  	case n == outDataMax && err == nil:
    46  		var dummy [1]byte
    47  		_, err = actualDecompressor.Read(dummy[:])
    48  		if err == nil {
    49  			return 0, io.ErrShortBuffer
    50  		}
    51  		if err == io.EOF {
    52  			err = nil
    53  		}
    54  	}
    55  	return n, err
    56  }
    57  
    58  // GzipDecompress performs gzip decompression on a byte slice.  outData[] must
    59  // be large enough to fit the decompressed data.  Byte count of the
    60  // decompressed data is returned on success (it may be smaller than
    61  // len(outData)).
    62  func (dd *Decompressor) GzipDecompress(outData, inData []byte) (int, error) {
    63  	dataReader := bytes.NewReader(inData)
    64  	actualDecompressor, err := gzip.NewReader(dataReader)
    65  	if err != nil {
    66  		return 0, err
    67  	}
    68  	// Copy of readToEOF() in github.com/biogo/hts/bgzf/cache.go.
    69  	n := 0
    70  	outDataMax := len(outData)
    71  	for err == nil && n < outDataMax {
    72  		var nn int
    73  		nn, err = actualDecompressor.Read(outData[n:])
    74  		n += nn
    75  	}
    76  	switch {
    77  	case err == io.EOF:
    78  		return n, nil
    79  	case n == outDataMax && err == nil:
    80  		var dummy [1]byte
    81  		_, err = actualDecompressor.Read(dummy[:])
    82  		if err == nil {
    83  			return 0, io.ErrShortBuffer
    84  		}
    85  		if err == io.EOF {
    86  			err = nil
    87  		}
    88  	}
    89  	return n, err
    90  }
    91  
    92  func (dd *Decompressor) Cleanup() {
    93  }
    94  
    95  type Compressor struct {
    96  	clvl int
    97  }
    98  
    99  func (cc *Compressor) Init(compressionLevel int) error {
   100  	cc.clvl = compressionLevel
   101  	return nil
   102  }
   103  
   104  // Compress performs raw DEFLATE compression on a byte slice.  outData[] must
   105  // be large enough to fit the compressed data.  Byte count of the compressed
   106  // data is returned on success.
   107  // Zero is currently returned on failure.  A side effect is that inData cannot
   108  // be length zero; this function will panic or crash if it is.
   109  func (cc *Compressor) Compress(outData, inData []byte) int {
   110  	// I suspect this currently makes a few unnecessary allocations and copies;
   111  	// can optimize later.
   112  	if len(inData) == 0 {
   113  		panic("libdeflate.Compress: zero-length inData")
   114  	}
   115  	var buf bytes.Buffer
   116  	actualCompressor, err := flate.NewWriter(&buf, cc.clvl)
   117  	if err != nil {
   118  		return 0
   119  	}
   120  	_, err = actualCompressor.Write(inData)
   121  	if err != nil {
   122  		return 0
   123  	}
   124  	err = actualCompressor.Close()
   125  	if err != nil {
   126  		return 0
   127  	}
   128  	outLen := buf.Len()
   129  	if outLen > len(outData) {
   130  		return 0
   131  	}
   132  	copy(outData, buf.Bytes())
   133  	return outLen
   134  }
   135  
   136  func (cc *Compressor) Cleanup() {
   137  }