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