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 }