github.com/clem109/go-ethereum@v1.8.3-0.20180316121352-fe6cf00f480a/compression/rle/read_write.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package rle implements the run-length encoding used for Ethereum data. 18 package rle 19 20 import ( 21 "bytes" 22 "errors" 23 24 "github.com/ethereum/go-ethereum/crypto" 25 ) 26 27 const ( 28 token byte = 0xfe 29 emptyShaToken = 0xfd 30 emptyListShaToken = 0xfe 31 tokenToken = 0xff 32 ) 33 34 var empty = crypto.Keccak256([]byte("")) 35 var emptyList = crypto.Keccak256([]byte{0x80}) 36 37 func Decompress(dat []byte) ([]byte, error) { 38 buf := new(bytes.Buffer) 39 40 for i := 0; i < len(dat); i++ { 41 if dat[i] == token { 42 if i+1 < len(dat) { 43 switch dat[i+1] { 44 case emptyShaToken: 45 buf.Write(empty) 46 case emptyListShaToken: 47 buf.Write(emptyList) 48 case tokenToken: 49 buf.WriteByte(token) 50 default: 51 buf.Write(make([]byte, int(dat[i+1]-2))) 52 } 53 i++ 54 } else { 55 return nil, errors.New("error reading bytes. token encountered without proceeding bytes") 56 } 57 } else { 58 buf.WriteByte(dat[i]) 59 } 60 } 61 62 return buf.Bytes(), nil 63 } 64 65 func compressChunk(dat []byte) (ret []byte, n int) { 66 switch { 67 case dat[0] == token: 68 return []byte{token, tokenToken}, 1 69 case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0: 70 j := 0 71 for j <= 254 && j < len(dat) { 72 if dat[j] != 0 { 73 break 74 } 75 j++ 76 } 77 return []byte{token, byte(j + 2)}, j 78 case len(dat) >= 32: 79 if dat[0] == empty[0] && bytes.Equal(dat[:32], empty) { 80 return []byte{token, emptyShaToken}, 32 81 } else if dat[0] == emptyList[0] && bytes.Equal(dat[:32], emptyList) { 82 return []byte{token, emptyListShaToken}, 32 83 } 84 fallthrough 85 default: 86 return dat[:1], 1 87 } 88 } 89 90 func Compress(dat []byte) []byte { 91 buf := new(bytes.Buffer) 92 93 i := 0 94 for i < len(dat) { 95 b, n := compressChunk(dat[i:]) 96 buf.Write(b) 97 i += n 98 } 99 100 return buf.Bytes() 101 }