github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/util/codec/bytes.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package codec 15 16 import ( 17 "encoding/binary" 18 "runtime" 19 "unsafe" 20 21 "github.com/insionng/yougam/libraries/juju/errors" 22 ) 23 24 const ( 25 encGroupSize = 8 26 encMarker = byte(0xFF) 27 encPad = byte(0x0) 28 ) 29 30 var ( 31 pads = make([]byte, encGroupSize) 32 encPads = []byte{encPad} 33 ) 34 35 // EncodeBytes guarantees the encoded value is in ascending order for comparison, 36 // encoding with the following rule: 37 // [group1][marker1]...[groupN][markerN] 38 // group is 8 bytes slice which is padding with 0. 39 // marker is `0xFF - padding 0 count` 40 // For example: 41 // [] -> [0, 0, 0, 0, 0, 0, 0, 0, 247] 42 // [1, 2, 3] -> [1, 2, 3, 0, 0, 0, 0, 0, 250] 43 // [1, 2, 3, 0] -> [1, 2, 3, 0, 0, 0, 0, 0, 251] 44 // [1, 2, 3, 4, 5, 6, 7, 8] -> [1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247] 45 // Refer: https://yougam/libraries/facebook/mysql-5.6/wiki/MyRocks-record-format#memcomparable-format 46 func EncodeBytes(b []byte, data []byte) []byte { 47 // Allocate more space to avoid unnecessary slice growing. 48 // Assume that the byte slice size is about `(len(data) / encGroupSize + 1) * (encGroupSize + 1)` bytes, 49 // that is `(len(data) / 8 + 1) * 9` in our implement. 50 dLen := len(data) 51 reallocSize := (dLen/encGroupSize + 1) * (encGroupSize + 1) 52 result := reallocBytes(b, reallocSize) 53 for idx := 0; idx <= dLen; idx += encGroupSize { 54 remain := dLen - idx 55 padCount := 0 56 if remain >= encGroupSize { 57 result = append(result, data[idx:idx+encGroupSize]...) 58 } else { 59 padCount = encGroupSize - remain 60 result = append(result, data[idx:]...) 61 result = append(result, pads[:padCount]...) 62 } 63 64 marker := encMarker - byte(padCount) 65 result = append(result, marker) 66 } 67 68 return result 69 } 70 71 func decodeBytes(b []byte, reverse bool) ([]byte, []byte, error) { 72 data := make([]byte, 0, len(b)) 73 for { 74 if len(b) < encGroupSize+1 { 75 return nil, nil, errors.New("insufficient bytes to decode value") 76 } 77 78 groupBytes := b[:encGroupSize+1] 79 80 group := groupBytes[:encGroupSize] 81 marker := groupBytes[encGroupSize] 82 83 var padCount byte 84 if reverse { 85 padCount = marker 86 } else { 87 padCount = encMarker - marker 88 } 89 if padCount > encGroupSize { 90 return nil, nil, errors.Errorf("invalid marker byte, group bytes %q", groupBytes) 91 } 92 93 realGroupSize := encGroupSize - padCount 94 data = append(data, group[:realGroupSize]...) 95 b = b[encGroupSize+1:] 96 97 if padCount != 0 { 98 var padByte = encPad 99 if reverse { 100 padByte = encMarker 101 } 102 // Check validity of padding bytes. 103 for _, v := range group[realGroupSize:] { 104 if v != padByte { 105 return nil, nil, errors.Errorf("invalid padding byte, group bytes %q", groupBytes) 106 } 107 } 108 break 109 } 110 } 111 if reverse { 112 reverseBytes(data) 113 } 114 return b, data, nil 115 } 116 117 // DecodeBytes decodes bytes which is encoded by EncodeBytes before, 118 // returns the leftover bytes and decoded value if no error. 119 func DecodeBytes(b []byte) ([]byte, []byte, error) { 120 return decodeBytes(b, false) 121 } 122 123 // EncodeBytesDesc first encodes bytes using EncodeBytes, then bitwise reverses 124 // encoded value to guarantee the encoded value is in descending order for comparison. 125 func EncodeBytesDesc(b []byte, data []byte) []byte { 126 n := len(b) 127 b = EncodeBytes(b, data) 128 reverseBytes(b[n:]) 129 return b 130 } 131 132 // DecodeBytesDesc decodes bytes which is encoded by EncodeBytesDesc before, 133 // returns the leftover bytes and decoded value if no error. 134 func DecodeBytesDesc(b []byte) ([]byte, []byte, error) { 135 return decodeBytes(b, true) 136 } 137 138 // EncodeCompactBytes joins bytes with its length into a byte slice. It is more 139 // efficient in both space and time compare to EncodeBytes. Note that the encoded 140 // result is not memcomparable. 141 func EncodeCompactBytes(b []byte, data []byte) []byte { 142 b = reallocBytes(b, binary.MaxVarintLen64+len(data)) 143 b = EncodeVarint(b, int64(len(data))) 144 return append(b, data...) 145 } 146 147 // DecodeCompactBytes decodes bytes which is encoded by EncodeCompactBytes before. 148 func DecodeCompactBytes(b []byte) ([]byte, []byte, error) { 149 b, n, err := DecodeVarint(b) 150 if err != nil { 151 return nil, nil, errors.Trace(err) 152 } 153 if int64(len(b)) < n { 154 return nil, nil, errors.Errorf("insufficient bytes to decode value, expected length: %v", n) 155 } 156 return b[n:], b[:n], nil 157 } 158 159 // See https://yougam/libraries/src/crypto/cipher/xor.go 160 const wordSize = int(unsafe.Sizeof(uintptr(0))) 161 const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" 162 163 func fastReverseBytes(b []byte) { 164 n := len(b) 165 w := n / wordSize 166 if w > 0 { 167 bw := *(*[]uintptr)(unsafe.Pointer(&b)) 168 for i := 0; i < w; i++ { 169 bw[i] = ^bw[i] 170 } 171 } 172 173 for i := w * wordSize; i < n; i++ { 174 b[i] = ^b[i] 175 } 176 } 177 178 func safeReverseBytes(b []byte) { 179 for i := range b { 180 b[i] = ^b[i] 181 } 182 } 183 184 func reverseBytes(b []byte) { 185 if supportsUnaligned { 186 fastReverseBytes(b) 187 return 188 } 189 190 safeReverseBytes(b) 191 } 192 193 // like realloc. 194 func reallocBytes(b []byte, n int) []byte { 195 newSize := len(b) + n 196 if cap(b) < newSize { 197 bs := make([]byte, len(b), newSize) 198 copy(bs, b) 199 return bs 200 } 201 202 // slice b has capability to store n bytes 203 return b 204 }