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  }