github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/compression/rle/read_write.go (about)

     1  package rle
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/neatlab/neatio/utilities/crypto"
     8  )
     9  
    10  const (
    11  	token             byte = 0xfe
    12  	emptyShaToken          = 0xfd
    13  	emptyListShaToken      = 0xfe
    14  	tokenToken             = 0xff
    15  )
    16  
    17  var empty = crypto.Keccak256([]byte(""))
    18  var emptyList = crypto.Keccak256([]byte{0x80})
    19  
    20  func Decompress(dat []byte) ([]byte, error) {
    21  	buf := new(bytes.Buffer)
    22  
    23  	for i := 0; i < len(dat); i++ {
    24  		if dat[i] == token {
    25  			if i+1 < len(dat) {
    26  				switch dat[i+1] {
    27  				case emptyShaToken:
    28  					buf.Write(empty)
    29  				case emptyListShaToken:
    30  					buf.Write(emptyList)
    31  				case tokenToken:
    32  					buf.WriteByte(token)
    33  				default:
    34  					buf.Write(make([]byte, int(dat[i+1]-2)))
    35  				}
    36  				i++
    37  			} else {
    38  				return nil, errors.New("error reading bytes. token encountered without proceeding bytes")
    39  			}
    40  		} else {
    41  			buf.WriteByte(dat[i])
    42  		}
    43  	}
    44  
    45  	return buf.Bytes(), nil
    46  }
    47  
    48  func compressChunk(dat []byte) (ret []byte, n int) {
    49  	switch {
    50  	case dat[0] == token:
    51  		return []byte{token, tokenToken}, 1
    52  	case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0:
    53  		j := 0
    54  		for j <= 254 && j < len(dat) {
    55  			if dat[j] != 0 {
    56  				break
    57  			}
    58  			j++
    59  		}
    60  		return []byte{token, byte(j + 2)}, j
    61  	case len(dat) >= 32:
    62  		if dat[0] == empty[0] && bytes.Equal(dat[:32], empty) {
    63  			return []byte{token, emptyShaToken}, 32
    64  		} else if dat[0] == emptyList[0] && bytes.Equal(dat[:32], emptyList) {
    65  			return []byte{token, emptyListShaToken}, 32
    66  		}
    67  		fallthrough
    68  	default:
    69  		return dat[:1], 1
    70  	}
    71  }
    72  
    73  func Compress(dat []byte) []byte {
    74  	buf := new(bytes.Buffer)
    75  
    76  	i := 0
    77  	for i < len(dat) {
    78  		b, n := compressChunk(dat[i:])
    79  		buf.Write(b)
    80  		i += n
    81  	}
    82  
    83  	return buf.Bytes()
    84  }