github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/common/bitutil/compress.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:33</date> 10 //</624450072834215936> 11 12 13 package bitutil 14 15 import "errors" 16 17 var ( 18 //如果引用的字节 19 //位集头溢出输入数据。 20 errMissingData = errors.New("missing bytes on input") 21 22 //如果没有使用所有字节,则从解压返回errunreferenceddata。 23 //在对输入数据进行解压缩之后。 24 errUnreferencedData = errors.New("extra bytes on input") 25 26 //如果位集头具有 27 //定义的比特数多于可用的目标缓冲区空间数。 28 errExceededTarget = errors.New("target data size exceeded") 29 30 //如果中引用了数据字节,则从解压缩返回errZeroContent。 31 //位集头实际上是一个零字节。 32 errZeroContent = errors.New("zero byte in input content") 33 ) 34 35 //由compressBytes和compressBytes实现的压缩算法是 36 //针对包含大量零字节的稀疏输入数据进行了优化。减压 37 //需要解压数据长度的知识。 38 // 39 //压缩工程如下: 40 // 41 //如果数据只包含零, 42 //compressbytes(data)==nil 43 //否则,如果len(data)<=1, 44 //compressBytes(data)==数据 45 //否则: 46 //compressbytes(data)==附加(compressbytes(nonzerobitset(data)),nonzerobytes(data)…) 47 //哪里 48 //非零位集(data)是一个带有len(data)位(msb first)的位向量: 49 //非零位集(数据)[I/8]&&(1<(7-I%8))!=0,如果数据[i]!= 0 50 //len(非零位集(数据))==(len(数据)+7)/8 51 //非零字节(数据)包含相同顺序的非零字节数据 52 53 //compressBytes根据稀疏位集压缩输入字节片 54 //表示算法。如果结果大于原始输入,则不 55 //压缩完成。 56 func CompressBytes(data []byte) []byte { 57 if out := bitsetEncodeBytes(data); len(out) < len(data) { 58 return out 59 } 60 cpy := make([]byte, len(data)) 61 copy(cpy, data) 62 return cpy 63 } 64 65 //bitsetEncodeBytes根据稀疏数据压缩输入字节片 66 //位集表示算法。 67 func bitsetEncodeBytes(data []byte) []byte { 68 //空切片压缩为零 69 if len(data) == 0 { 70 return nil 71 } 72 //单字节片压缩为零或保留单字节 73 if len(data) == 1 { 74 if data[0] == 0 { 75 return nil 76 } 77 return data 78 } 79 //计算集合字节的位集,并收集非零字节 80 nonZeroBitset := make([]byte, (len(data)+7)/8) 81 nonZeroBytes := make([]byte, 0, len(data)) 82 83 for i, b := range data { 84 if b != 0 { 85 nonZeroBytes = append(nonZeroBytes, b) 86 nonZeroBitset[i/8] |= 1 << byte(7-i%8) 87 } 88 } 89 if len(nonZeroBytes) == 0 { 90 return nil 91 } 92 return append(bitsetEncodeBytes(nonZeroBitset), nonZeroBytes...) 93 } 94 95 //解压缩字节用已知的目标大小解压缩数据。如果输入数据 96 //匹配目标的大小,这意味着在第一个压缩过程中没有进行压缩 97 //地点。 98 func DecompressBytes(data []byte, target int) ([]byte, error) { 99 if len(data) > target { 100 return nil, errExceededTarget 101 } 102 if len(data) == target { 103 cpy := make([]byte, len(data)) 104 copy(cpy, data) 105 return cpy, nil 106 } 107 return bitsetDecodeBytes(data, target) 108 } 109 110 //bitsetdecodebytes用已知的目标大小解压缩数据。 111 func bitsetDecodeBytes(data []byte, target int) ([]byte, error) { 112 out, size, err := bitsetDecodePartialBytes(data, target) 113 if err != nil { 114 return nil, err 115 } 116 if size != len(data) { 117 return nil, errUnreferencedData 118 } 119 return out, nil 120 } 121 122 //BitsetDecodePartialBytes以已知的目标大小解压缩数据,但确实如此 123 //不强制使用所有输入字节。除了减压 124 //输出,函数返回相应的压缩输入数据的长度 125 //因为输入片可能更长。 126 func bitsetDecodePartialBytes(data []byte, target int) ([]byte, int, error) { 127 //健全性检查0个目标以避免无限递归 128 if target == 0 { 129 return nil, 0, nil 130 } 131 //处理零和单字节角情况 132 decomp := make([]byte, target) 133 if len(data) == 0 { 134 return decomp, 0, nil 135 } 136 if target == 1 { 137 decomp[0] = data[0] //复制以避免引用输入切片 138 if data[0] != 0 { 139 return decomp, 1, nil 140 } 141 return decomp, 0, nil 142 } 143 //解压集合字节的位集并分配非零字节 144 nonZeroBitset, ptr, err := bitsetDecodePartialBytes(data, (target+7)/8) 145 if err != nil { 146 return nil, ptr, err 147 } 148 for i := 0; i < 8*len(nonZeroBitset); i++ { 149 if nonZeroBitset[i/8]&(1<<byte(7-i%8)) != 0 { 150 //确保我们有足够的数据插入正确的插槽 151 if ptr >= len(data) { 152 return nil, 0, errMissingData 153 } 154 if i >= len(decomp) { 155 return nil, 0, errExceededTarget 156 } 157 //确保数据有效并推入插槽 158 if data[ptr] == 0 { 159 return nil, 0, errZeroContent 160 } 161 decomp[i] = data[ptr] 162 ptr++ 163 } 164 } 165 return decomp, ptr, nil 166 } 167