1001 lines
32 KiB
Go
1001 lines
32 KiB
Go
// Copyright 2023 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Copyright 2014 The Cockroach Authors.
|
|
//
|
|
// Use of this software is governed by the Business Source License
|
|
// included in the file licenses/BSL.txt.
|
|
//
|
|
// As of the Change Date specified in that file, in accordance with
|
|
// the Business Source License, use of this software will be governed
|
|
// by the Apache License, Version 2.0, included in the file
|
|
// licenses/APL.txt.
|
|
|
|
package encoding
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"unsafe"
|
|
|
|
"github.com/cockroachdb/apd/v3"
|
|
"github.com/cockroachdb/errors"
|
|
|
|
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
|
)
|
|
|
|
const (
|
|
encodedNull = 0x00
|
|
// A marker greater than NULL but lower than any other value.
|
|
// This value is not actually ever present in a stored key, but
|
|
// it's used in keys used as span boundaries for index scans.
|
|
encodedNotNull = 0x01
|
|
|
|
floatNaN = encodedNotNull + 1
|
|
floatNeg = floatNaN + 1
|
|
floatZero = floatNeg + 1
|
|
floatPos = floatZero + 1
|
|
floatNaNDesc = floatPos + 1 // NaN encoded descendingly
|
|
|
|
// The gap between floatNaNDesc and bytesMarker was left for
|
|
// compatibility reasons.
|
|
bytesMarker byte = 0x12
|
|
bytesDescMarker = bytesMarker + 1
|
|
timeMarker = bytesDescMarker + 1
|
|
durationBigNegMarker = timeMarker + 1 // Only used for durations < MinInt64 nanos.
|
|
durationMarker = durationBigNegMarker + 1
|
|
durationBigPosMarker = durationMarker + 1 // Only used for durations > MaxInt64 nanos.
|
|
|
|
decimalNaN = durationBigPosMarker + 1 // 24
|
|
decimalNegativeInfinity = decimalNaN + 1
|
|
decimalNegLarge = decimalNegativeInfinity + 1
|
|
decimalNegMedium = decimalNegLarge + 11
|
|
decimalNegSmall = decimalNegMedium + 1
|
|
decimalZero = decimalNegSmall + 1
|
|
decimalPosSmall = decimalZero + 1
|
|
decimalPosMedium = decimalPosSmall + 1
|
|
decimalPosLarge = decimalPosMedium + 11
|
|
decimalInfinity = decimalPosLarge + 1
|
|
decimalNaNDesc = decimalInfinity + 1 // NaN encoded descendingly
|
|
decimalTerminator = 0x00
|
|
|
|
jsonInvertedIndex = decimalNaNDesc + 1
|
|
jsonEmptyArray = jsonInvertedIndex + 1
|
|
jsonEmptyObject = jsonEmptyArray + 1
|
|
|
|
bitArrayMarker = jsonEmptyObject + 1
|
|
bitArrayDescMarker = bitArrayMarker + 1
|
|
bitArrayDataTerminator = 0x00
|
|
bitArrayDataDescTerminator = 0xff
|
|
|
|
timeTZMarker = bitArrayDescMarker + 1
|
|
geoMarker = timeTZMarker + 1
|
|
geoDescMarker = geoMarker + 1
|
|
|
|
// Markers and terminators for key encoding Datum arrays in sorted order.
|
|
// For the arrayKeyMarker and other types like bytes and bit arrays, it
|
|
// might be unclear why we have a separate marker for the ascending and
|
|
// descending cases. This is necessary because the terminators for these
|
|
// encodings are different depending on the direction the data is encoded
|
|
// in. In order to safely decode a set of bytes without knowing the direction
|
|
// of the encoding, we must store this information in the marker. Otherwise,
|
|
// we would not know what terminator to look for when decoding this format.
|
|
arrayKeyMarker = geoDescMarker + 1
|
|
arrayKeyDescendingMarker = arrayKeyMarker + 1
|
|
|
|
box2DMarker = arrayKeyDescendingMarker + 1
|
|
|
|
arrayKeyTerminator byte = 0x00
|
|
arrayKeyDescendingTerminator byte = 0xFF
|
|
|
|
// IntMin is chosen such that the range of int tags does not overlap the
|
|
// ascii character set that is frequently used in testing.
|
|
IntMin = 0x80 // 128
|
|
intMaxWidth = 8
|
|
intZero = IntMin + intMaxWidth // 136
|
|
intSmall = IntMax - intZero - intMaxWidth // 109
|
|
// IntMax is the maximum int tag value.
|
|
IntMax = 0xfd // 253
|
|
|
|
// Nulls come last when encoded descendingly.
|
|
// This value is not actually ever present in a stored key, but
|
|
// it's used in keys used as span boundaries for index scans.
|
|
encodedNotNullDesc = 0xfe
|
|
encodedNullDesc = 0xff
|
|
)
|
|
|
|
// Direction for ordering results.
|
|
type Direction int
|
|
|
|
// Direction values.
|
|
const (
|
|
_ Direction = iota
|
|
Ascending
|
|
Descending
|
|
)
|
|
|
|
const escapeLength = 2
|
|
|
|
// EncodeUint32Ascending encodes the uint32 value using a big-endian 4 byte
|
|
// representation. The bytes are appended to the supplied buffer and
|
|
// the final buffer is returned.
|
|
func EncodeUint32Ascending(b []byte, v uint32) []byte {
|
|
return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
|
}
|
|
|
|
// PutUint32Ascending encodes the uint32 value using a big-endian 4 byte
|
|
// representation at the specified index, lengthening the input slice if
|
|
// necessary.
|
|
func PutUint32Ascending(b []byte, v uint32, idx int) []byte {
|
|
for len(b) < idx+4 {
|
|
b = append(b, 0)
|
|
}
|
|
b[idx] = byte(v >> 24)
|
|
b[idx+1] = byte(v >> 16)
|
|
b[idx+2] = byte(v >> 8)
|
|
b[idx+3] = byte(v)
|
|
return b
|
|
}
|
|
|
|
// DecodeUint32Ascending decodes a uint32 from the input buffer, treating
|
|
// the input as a big-endian 4 byte uint32 representation. The remainder
|
|
// of the input buffer and the decoded uint32 are returned.
|
|
func DecodeUint32Ascending(b []byte) ([]byte, uint32, error) {
|
|
if len(b) < 4 {
|
|
return nil, 0, errors.Errorf("insufficient bytes to decode uint32 int value")
|
|
}
|
|
v := binary.BigEndian.Uint32(b)
|
|
return b[4:], v, nil
|
|
}
|
|
|
|
// EncodeUint64Ascending encodes the uint64 value using a big-endian 8 byte
|
|
// representation. The bytes are appended to the supplied buffer and
|
|
// the final buffer is returned.
|
|
func EncodeUint64Ascending(b []byte, v uint64) []byte {
|
|
return append(b,
|
|
byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32),
|
|
byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
|
}
|
|
|
|
// DecodeUint64Ascending decodes a uint64 from the input buffer, treating
|
|
// the input as a big-endian 8 byte uint64 representation. The remainder
|
|
// of the input buffer and the decoded uint64 are returned.
|
|
func DecodeUint64Ascending(b []byte) ([]byte, uint64, error) {
|
|
if len(b) < 8 {
|
|
return nil, 0, errors.Errorf("insufficient bytes to decode uint64 int value")
|
|
}
|
|
v := binary.BigEndian.Uint64(b)
|
|
return b[8:], v, nil
|
|
}
|
|
|
|
// MaxVarintLen is the maximum length of a value encoded using any of:
|
|
// - EncodeVarintAscending
|
|
// - EncodeVarintDescending
|
|
// - EncodeUvarintAscending
|
|
// - EncodeUvarintDescending
|
|
const MaxVarintLen = 9
|
|
|
|
// getVarintLen returns the encoded length of an encoded varint. Assumes the
|
|
// slice has at least one byte.
|
|
func getVarintLen(b []byte) (int, error) {
|
|
length := int(b[0]) - intZero
|
|
if length >= 0 {
|
|
if length <= intSmall {
|
|
// just the tag
|
|
return 1, nil
|
|
}
|
|
// tag and length-intSmall bytes
|
|
length = 1 + length - intSmall
|
|
} else {
|
|
// tag and -length bytes
|
|
length = 1 - length
|
|
}
|
|
|
|
if length > len(b) {
|
|
return 0, errors.Errorf("varint length %d exceeds slice length %d", length, len(b))
|
|
}
|
|
return length, nil
|
|
}
|
|
|
|
// EncodeUvarintAscending encodes the uint64 value using a variable length
|
|
// (length-prefixed) representation. The length is encoded as a single
|
|
// byte indicating the number of encoded bytes (-8) to follow. See
|
|
// EncodeVarintAscending for rationale. The encoded bytes are appended to the
|
|
// supplied buffer and the final buffer is returned.
|
|
func EncodeUvarintAscending(b []byte, v uint64) []byte {
|
|
switch {
|
|
case v <= intSmall:
|
|
return append(b, intZero+byte(v))
|
|
case v <= 0xff:
|
|
return append(b, IntMax-7, byte(v))
|
|
case v <= 0xffff:
|
|
return append(b, IntMax-6, byte(v>>8), byte(v))
|
|
case v <= 0xffffff:
|
|
return append(b, IntMax-5, byte(v>>16), byte(v>>8), byte(v))
|
|
case v <= 0xffffffff:
|
|
return append(b, IntMax-4, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
|
case v <= 0xffffffffff:
|
|
return append(b, IntMax-3, byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8),
|
|
byte(v))
|
|
case v <= 0xffffffffffff:
|
|
return append(b, IntMax-2, byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16),
|
|
byte(v>>8), byte(v))
|
|
case v <= 0xffffffffffffff:
|
|
return append(b, IntMax-1, byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24),
|
|
byte(v>>16), byte(v>>8), byte(v))
|
|
default:
|
|
return append(b, IntMax, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32),
|
|
byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
|
}
|
|
}
|
|
|
|
// EncodeUvarintDescending encodes the uint64 value so that it sorts in
|
|
// reverse order, from largest to smallest.
|
|
func EncodeUvarintDescending(b []byte, v uint64) []byte {
|
|
switch {
|
|
case v == 0:
|
|
return append(b, IntMin+8)
|
|
case v <= 0xff:
|
|
v = ^v
|
|
return append(b, IntMin+7, byte(v))
|
|
case v <= 0xffff:
|
|
v = ^v
|
|
return append(b, IntMin+6, byte(v>>8), byte(v))
|
|
case v <= 0xffffff:
|
|
v = ^v
|
|
return append(b, IntMin+5, byte(v>>16), byte(v>>8), byte(v))
|
|
case v <= 0xffffffff:
|
|
v = ^v
|
|
return append(b, IntMin+4, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
|
case v <= 0xffffffffff:
|
|
v = ^v
|
|
return append(b, IntMin+3, byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8),
|
|
byte(v))
|
|
case v <= 0xffffffffffff:
|
|
v = ^v
|
|
return append(b, IntMin+2, byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16),
|
|
byte(v>>8), byte(v))
|
|
case v <= 0xffffffffffffff:
|
|
v = ^v
|
|
return append(b, IntMin+1, byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24),
|
|
byte(v>>16), byte(v>>8), byte(v))
|
|
default:
|
|
v = ^v
|
|
return append(b, IntMin, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32),
|
|
byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
|
}
|
|
}
|
|
|
|
// highestByteIndex returns the index (0 to 7) of the highest nonzero byte in v.
|
|
func highestByteIndex(v uint64) int {
|
|
l := 0
|
|
if v > 0xffffffff {
|
|
v >>= 32
|
|
l += 4
|
|
}
|
|
if v > 0xffff {
|
|
v >>= 16
|
|
l += 2
|
|
}
|
|
if v > 0xff {
|
|
l++
|
|
}
|
|
return l
|
|
}
|
|
|
|
// EncLenUvarintAscending returns the encoding length for EncodeUvarintAscending
|
|
// without actually encoding.
|
|
func EncLenUvarintAscending(v uint64) int {
|
|
if v <= intSmall {
|
|
return 1
|
|
}
|
|
return 2 + highestByteIndex(v)
|
|
}
|
|
|
|
// EncLenUvarintDescending returns the encoding length for
|
|
// EncodeUvarintDescending without actually encoding.
|
|
func EncLenUvarintDescending(v uint64) int {
|
|
if v == 0 {
|
|
return 1
|
|
}
|
|
return 2 + highestByteIndex(v)
|
|
}
|
|
|
|
// DecodeUvarintAscending decodes a varint encoded uint64 from the input
|
|
// buffer. The remainder of the input buffer and the decoded uint64
|
|
// are returned.
|
|
func DecodeUvarintAscending(b []byte) ([]byte, uint64, error) {
|
|
if len(b) == 0 {
|
|
return nil, 0, errors.Errorf("insufficient bytes to decode uvarint value")
|
|
}
|
|
length := int(b[0]) - intZero
|
|
b = b[1:] // skip length byte
|
|
if length <= intSmall {
|
|
return b, uint64(length), nil
|
|
}
|
|
length -= intSmall
|
|
if length < 0 || length > 8 {
|
|
return nil, 0, errors.Errorf("invalid uvarint length of %d", length)
|
|
} else if len(b) < length {
|
|
return nil, 0, errors.Errorf("insufficient bytes to decode uvarint value: %q", b)
|
|
}
|
|
var v uint64
|
|
// It is faster to range over the elements in a slice than to index
|
|
// into the slice on each loop iteration.
|
|
for _, t := range b[:length] {
|
|
v = (v << 8) | uint64(t)
|
|
}
|
|
return b[length:], v, nil
|
|
}
|
|
|
|
// DecodeUvarintDescending decodes a uint64 value which was encoded
|
|
// using EncodeUvarintDescending.
|
|
func DecodeUvarintDescending(b []byte) ([]byte, uint64, error) {
|
|
if len(b) == 0 {
|
|
return nil, 0, errors.Errorf("insufficient bytes to decode uvarint value")
|
|
}
|
|
length := intZero - int(b[0])
|
|
b = b[1:] // skip length byte
|
|
if length < 0 || length > 8 {
|
|
return nil, 0, errors.Errorf("invalid uvarint length of %d", length)
|
|
} else if len(b) < length {
|
|
return nil, 0, errors.Errorf("insufficient bytes to decode uvarint value: %q", b)
|
|
}
|
|
var x uint64
|
|
for _, t := range b[:length] {
|
|
x = (x << 8) | uint64(^t)
|
|
}
|
|
return b[length:], x, nil
|
|
}
|
|
|
|
const (
|
|
// <term> -> \x00\x01
|
|
// \x00 -> \x00\xff
|
|
escape byte = 0x00
|
|
escapedTerm byte = 0x01
|
|
escapedJSONObjectKeyTerm byte = 0x02
|
|
escapedJSONArray byte = 0x03
|
|
escaped00 byte = 0xff
|
|
escapedFF byte = 0x00
|
|
)
|
|
|
|
type escapes struct {
|
|
escape byte
|
|
escapedTerm byte
|
|
escaped00 byte
|
|
escapedFF byte
|
|
marker byte
|
|
}
|
|
|
|
var (
|
|
ascendingBytesEscapes = escapes{escape, escapedTerm, escaped00, escapedFF, bytesMarker}
|
|
descendingBytesEscapes = escapes{^escape, ^escapedTerm, ^escaped00, ^escapedFF, bytesDescMarker}
|
|
|
|
ascendingGeoEscapes = escapes{escape, escapedTerm, escaped00, escapedFF, geoMarker}
|
|
descendingGeoEscapes = escapes{^escape, ^escapedTerm, ^escaped00, ^escapedFF, geoDescMarker}
|
|
)
|
|
|
|
// encodeBytesAscendingWithTerminatorAndPrefix encodes the []byte value using an escape-based
|
|
// encoding. The encoded value is terminated with the sequence
|
|
// "\x00\terminator". The encoded bytes are append to the supplied buffer
|
|
// and the resulting buffer is returned. The terminator allows us to pass
|
|
// different terminators for things such as JSON key encoding.
|
|
func encodeBytesAscendingWithTerminatorAndPrefix(
|
|
b []byte, data []byte, terminator byte, prefix byte,
|
|
) []byte {
|
|
b = append(b, prefix)
|
|
return encodeBytesAscendingWithTerminator(b, data, terminator)
|
|
}
|
|
|
|
// encodeBytesAscendingWithTerminator encodes the []byte value using an escape-based
|
|
// encoding. The encoded value is terminated with the sequence
|
|
// "\x00\terminator". The encoded bytes are append to the supplied buffer
|
|
// and the resulting buffer is returned. The terminator allows us to pass
|
|
// different terminators for things such as JSON key encoding.
|
|
func encodeBytesAscendingWithTerminator(b []byte, data []byte, terminator byte) []byte {
|
|
bs := encodeBytesAscendingWithoutTerminatorOrPrefix(b, data)
|
|
return append(bs, escape, terminator)
|
|
}
|
|
|
|
// encodeBytesAscendingWithoutTerminatorOrPrefix encodes the []byte value using an escape-based
|
|
// encoding.
|
|
func encodeBytesAscendingWithoutTerminatorOrPrefix(b []byte, data []byte) []byte {
|
|
for {
|
|
// IndexByte is implemented by the go runtime in assembly and is
|
|
// much faster than looping over the bytes in the slice.
|
|
i := bytes.IndexByte(data, escape)
|
|
if i == -1 {
|
|
break
|
|
}
|
|
b = append(b, data[:i]...)
|
|
b = append(b, escape, escaped00)
|
|
data = data[i+1:]
|
|
}
|
|
return append(b, data...)
|
|
}
|
|
|
|
// getBytesLength finds the length of a bytes encoding.
|
|
func getBytesLength(b []byte, e escapes) (int, error) {
|
|
// Skip the tag.
|
|
skipped := 1
|
|
for {
|
|
i := bytes.IndexByte(b[skipped:], e.escape)
|
|
if i == -1 {
|
|
return 0, errors.Errorf("did not find terminator %#x in buffer %#x", e.escape, b)
|
|
}
|
|
if i+1 >= len(b) {
|
|
return 0, errors.Errorf("malformed escape in buffer %#x", b)
|
|
}
|
|
skipped += i + escapeLength
|
|
if b[skipped-1] == e.escapedTerm {
|
|
return skipped, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// UnsafeConvertStringToBytes converts a string to a byte array to be used with
|
|
// string encoding functions. Note that the output byte array should not be
|
|
// modified if the input string is expected to be used again - doing so could
|
|
// violate Go semantics.
|
|
func UnsafeConvertStringToBytes(s string) []byte {
|
|
if len(s) == 0 {
|
|
return nil
|
|
}
|
|
// We unsafely convert the string to a []byte to avoid the
|
|
// usual allocation when converting to a []byte. This is
|
|
// kosher because we know that EncodeBytes{,Descending} does
|
|
// not keep a reference to the value it encodes. The first
|
|
// step is getting access to the string internals.
|
|
data := unsafe.StringData(s)
|
|
// Next we treat the string data as a maximally sized array which we
|
|
// slice. This usage is safe because the pointer value remains in the string.
|
|
return (*[0x7fffffff]byte)(unsafe.Pointer(data))[:len(s):len(s)]
|
|
}
|
|
|
|
// EncodeStringAscending encodes the string value using an escape-based encoding. See
|
|
// EncodeBytes for details. The encoded bytes are append to the supplied buffer
|
|
// and the resulting buffer is returned.
|
|
func EncodeStringAscending(b []byte, s string) []byte {
|
|
return encodeStringAscendingWithTerminatorAndPrefix(b, s, ascendingBytesEscapes.escapedTerm, bytesMarker)
|
|
}
|
|
|
|
// encodeStringAscendingWithTerminatorAndPrefix encodes the string value using an escape-based encoding. See
|
|
// EncodeBytes for details. The encoded bytes are append to the supplied buffer
|
|
// and the resulting buffer is returned. We can also pass a terminator byte to be used with
|
|
// JSON key encoding.
|
|
func encodeStringAscendingWithTerminatorAndPrefix(
|
|
b []byte, s string, terminator byte, prefix byte,
|
|
) []byte {
|
|
unsafeString := UnsafeConvertStringToBytes(s)
|
|
return encodeBytesAscendingWithTerminatorAndPrefix(b, unsafeString, terminator, prefix)
|
|
}
|
|
|
|
// EncodeJSONKeyStringAscending encodes the JSON key string value with a JSON specific escaped
|
|
// terminator. This allows us to encode keys in the same number of bytes as a string,
|
|
// while at the same time giving us a sentinel to identify JSON keys. The end parameter is used
|
|
// to determine if this is the last key in a a JSON path. If it is we don't add a separator after it.
|
|
func EncodeJSONKeyStringAscending(b []byte, s string, end bool) []byte {
|
|
str := UnsafeConvertStringToBytes(s)
|
|
|
|
if end {
|
|
return encodeBytesAscendingWithoutTerminatorOrPrefix(b, str)
|
|
}
|
|
return encodeBytesAscendingWithTerminator(b, str, escapedJSONObjectKeyTerm)
|
|
}
|
|
|
|
// EncodeJSONEmptyArray returns a byte array b with a byte to signify an empty JSON array.
|
|
func EncodeJSONEmptyArray(b []byte) []byte {
|
|
return append(b, escape, escapedTerm, jsonEmptyArray)
|
|
}
|
|
|
|
// AddJSONPathTerminator adds a json path terminator to a byte array.
|
|
func AddJSONPathTerminator(b []byte) []byte {
|
|
return append(b, escape, escapedTerm)
|
|
}
|
|
|
|
// EncodeJSONEmptyObject returns a byte array b with a byte to signify an empty JSON object.
|
|
func EncodeJSONEmptyObject(b []byte) []byte {
|
|
return append(b, escape, escapedTerm, jsonEmptyObject)
|
|
}
|
|
|
|
// EncodeNullAscending encodes a NULL value. The encodes bytes are appended to the
|
|
// supplied buffer and the final buffer is returned. The encoded value for a
|
|
// NULL is guaranteed to not be a prefix for the EncodeVarint, EncodeFloat,
|
|
// EncodeBytes and EncodeString encodings.
|
|
func EncodeNullAscending(b []byte) []byte {
|
|
return append(b, encodedNull)
|
|
}
|
|
|
|
// EncodeJSONAscending encodes a JSON Type. The encoded bytes are appended to the
|
|
// supplied buffer and the final buffer is returned.
|
|
func EncodeJSONAscending(b []byte) []byte {
|
|
return append(b, jsonInvertedIndex)
|
|
}
|
|
|
|
// EncodeArrayAscending encodes a value used to signify membership of an array for JSON objects.
|
|
func EncodeArrayAscending(b []byte) []byte {
|
|
return append(b, escape, escapedJSONArray)
|
|
}
|
|
|
|
// EncodeTrueAscending encodes the boolean value true for use with JSON inverted indexes.
|
|
func EncodeTrueAscending(b []byte) []byte {
|
|
return append(b, byte(True))
|
|
}
|
|
|
|
// EncodeFalseAscending encodes the boolean value false for use with JSON inverted indexes.
|
|
func EncodeFalseAscending(b []byte) []byte {
|
|
return append(b, byte(False))
|
|
}
|
|
|
|
// getBitArrayWordsLen returns the number of bit array words in the
|
|
// encoded bytes and the size in bytes of the encoded word array
|
|
// (excluding the terminator byte).
|
|
func getBitArrayWordsLen(b []byte, term byte) (int, int, error) {
|
|
bSearch := b
|
|
numWords := 0
|
|
sz := 0
|
|
for {
|
|
if len(bSearch) == 0 {
|
|
return 0, 0, errors.Errorf("slice too short for bit array (%d)", len(b))
|
|
}
|
|
if bSearch[0] == term {
|
|
break
|
|
}
|
|
vLen, err := getVarintLen(bSearch)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
bSearch = bSearch[vLen:]
|
|
numWords++
|
|
sz += vLen
|
|
}
|
|
return numWords, sz, nil
|
|
}
|
|
|
|
// Type represents the type of a value encoded by
|
|
// Encode{Null,NotNull,Varint,Uvarint,Float,Bytes}.
|
|
//
|
|
//go:generate stringer -type=Type
|
|
type Type int
|
|
|
|
// Type values.
|
|
// TODO(dan, arjun): Make this into a proto enum.
|
|
// The 'Type' annotations are necessary for producing stringer-generated values.
|
|
const (
|
|
Unknown Type = 0
|
|
Null Type = 1
|
|
NotNull Type = 2
|
|
Int Type = 3
|
|
Float Type = 4
|
|
Decimal Type = 5
|
|
Bytes Type = 6
|
|
BytesDesc Type = 7 // Bytes encoded descendingly
|
|
Time Type = 8
|
|
Duration Type = 9
|
|
True Type = 10
|
|
False Type = 11
|
|
UUID Type = 12
|
|
Array Type = 13
|
|
IPAddr Type = 14
|
|
// SentinelType is used for bit manipulation to check if the encoded type
|
|
// value requires more than 4 bits, and thus will be encoded in two bytes. It
|
|
// is not used as a type value, and thus intentionally overlaps with the
|
|
// subsequent type value. The 'Type' annotation is intentionally omitted here.
|
|
SentinelType = 15
|
|
JSON Type = 15
|
|
Tuple Type = 16
|
|
BitArray Type = 17
|
|
BitArrayDesc Type = 18 // BitArray encoded descendingly
|
|
TimeTZ Type = 19
|
|
Geo Type = 20
|
|
GeoDesc Type = 21
|
|
ArrayKeyAsc Type = 22 // Array key encoding
|
|
ArrayKeyDesc Type = 23 // Array key encoded descendingly
|
|
Box2D Type = 24
|
|
)
|
|
|
|
// typMap maps an encoded type byte to a decoded Type. It's got 256 slots, one
|
|
// for every possible byte value.
|
|
var typMap [256]Type
|
|
|
|
func init() {
|
|
buf := []byte{0}
|
|
for i := range typMap {
|
|
buf[0] = byte(i)
|
|
typMap[i] = slowPeekType(buf)
|
|
}
|
|
}
|
|
|
|
// PeekType peeks at the type of the value encoded at the start of b.
|
|
func PeekType(b []byte) Type {
|
|
if len(b) >= 1 {
|
|
return typMap[b[0]]
|
|
}
|
|
return Unknown
|
|
}
|
|
|
|
// slowPeekType is the old implementation of PeekType. It's used to generate
|
|
// the lookup table for PeekType.
|
|
func slowPeekType(b []byte) Type {
|
|
if len(b) >= 1 {
|
|
m := b[0]
|
|
switch {
|
|
case m == encodedNull, m == encodedNullDesc:
|
|
return Null
|
|
case m == encodedNotNull, m == encodedNotNullDesc:
|
|
return NotNull
|
|
case m == arrayKeyMarker:
|
|
return ArrayKeyAsc
|
|
case m == arrayKeyDescendingMarker:
|
|
return ArrayKeyDesc
|
|
case m == bytesMarker:
|
|
return Bytes
|
|
case m == bytesDescMarker:
|
|
return BytesDesc
|
|
case m == bitArrayMarker:
|
|
return BitArray
|
|
case m == bitArrayDescMarker:
|
|
return BitArrayDesc
|
|
case m == timeMarker:
|
|
return Time
|
|
case m == timeTZMarker:
|
|
return TimeTZ
|
|
case m == geoMarker:
|
|
return Geo
|
|
case m == box2DMarker:
|
|
return Box2D
|
|
case m == geoDescMarker:
|
|
return GeoDesc
|
|
case m == byte(Array):
|
|
return Array
|
|
case m == byte(True):
|
|
return True
|
|
case m == byte(False):
|
|
return False
|
|
case m == durationBigNegMarker, m == durationMarker, m == durationBigPosMarker:
|
|
return Duration
|
|
case m >= IntMin && m <= IntMax:
|
|
return Int
|
|
case m >= floatNaN && m <= floatNaNDesc:
|
|
return Float
|
|
case m >= decimalNaN && m <= decimalNaNDesc:
|
|
return Decimal
|
|
}
|
|
}
|
|
return Unknown
|
|
}
|
|
|
|
// GetMultiVarintLen find the length of <num> encoded varints that follow a
|
|
// 1-byte tag.
|
|
func GetMultiVarintLen(b []byte, num int) (int, error) {
|
|
p := 1
|
|
for i := 0; i < num && p < len(b); i++ {
|
|
len, err := getVarintLen(b[p:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
p += len
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// getArrayLength returns the length of a key encoded array. The input
|
|
// must have had the array type marker stripped from the front.
|
|
func getArrayLength(buf []byte, dir Direction) (int, error) {
|
|
result := 0
|
|
for {
|
|
if len(buf) == 0 {
|
|
return 0, errors.AssertionFailedf("invalid array encoding (unterminated)")
|
|
}
|
|
if IsArrayKeyDone(buf, dir) {
|
|
// Increment to include the terminator byte.
|
|
result++
|
|
break
|
|
}
|
|
next, err := PeekLength(buf)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// Shift buf over by the encoded data amount.
|
|
buf = buf[next:]
|
|
result += next
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// peekBox2DLength peeks to look at the length of a box2d encoding.
|
|
func peekBox2DLength(b []byte) (int, error) {
|
|
length := 0
|
|
curr := b
|
|
for i := 0; i < 4; i++ {
|
|
if len(curr) == 0 {
|
|
return 0, errors.Newf("slice too short for box2d")
|
|
}
|
|
switch curr[0] {
|
|
case floatNaN, floatNaNDesc, floatZero:
|
|
length++
|
|
curr = curr[1:]
|
|
case floatNeg, floatPos:
|
|
length += 9
|
|
curr = curr[9:]
|
|
default:
|
|
return 0, errors.Newf("unexpected marker for box2d: %x", curr[0])
|
|
}
|
|
}
|
|
return length, nil
|
|
}
|
|
|
|
// PeekLength returns the length of the encoded value at the start of b. Note:
|
|
// if this function succeeds, it's not a guarantee that decoding the value will
|
|
// succeed. PeekLength is meant to be used on key encoded data only.
|
|
func PeekLength(b []byte) (int, error) {
|
|
if len(b) == 0 {
|
|
return 0, errors.Errorf("empty slice")
|
|
}
|
|
m := b[0]
|
|
switch m {
|
|
case encodedNull, encodedNullDesc, encodedNotNull, encodedNotNullDesc,
|
|
floatNaN, floatNaNDesc, floatZero, decimalZero, byte(True), byte(False):
|
|
// interleavedSentinel also falls into this path. Since it
|
|
// contains the same byte value as encodedNotNullDesc, it
|
|
// cannot be included explicitly in the case statement.
|
|
// ascendingNullWithinArrayKey and descendingNullWithinArrayKey also
|
|
// contain the same byte values as encodedNotNull and encodedNotNullDesc
|
|
// respectively.
|
|
return 1, nil
|
|
case bitArrayMarker, bitArrayDescMarker:
|
|
terminator := byte(bitArrayDataTerminator)
|
|
if m == bitArrayDescMarker {
|
|
terminator = bitArrayDataDescTerminator
|
|
}
|
|
_, n, err := getBitArrayWordsLen(b[1:], terminator)
|
|
if err != nil {
|
|
return 1 + n, err
|
|
}
|
|
m, err := getVarintLen(b[n+2:])
|
|
if err != nil {
|
|
return 1 + n + m + 1, err
|
|
}
|
|
return 1 + n + m + 1, nil
|
|
case arrayKeyMarker, arrayKeyDescendingMarker:
|
|
dir := Ascending
|
|
if m == arrayKeyDescendingMarker {
|
|
dir = Descending
|
|
}
|
|
length, err := getArrayLength(b[1:], dir)
|
|
return 1 + length, err
|
|
case bytesMarker:
|
|
return getBytesLength(b, ascendingBytesEscapes)
|
|
case box2DMarker:
|
|
if len(b) == 0 {
|
|
return 0, errors.Newf("slice too short for box2d")
|
|
}
|
|
length, err := peekBox2DLength(b[1:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return 1 + length, nil
|
|
case geoMarker:
|
|
// Expect to reserve at least 8 bytes for int64.
|
|
if len(b) < 8 {
|
|
return 0, errors.Errorf("slice too short for spatial object (%d)", len(b))
|
|
}
|
|
ret, err := getBytesLength(b[8:], ascendingGeoEscapes)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return 8 + ret, nil
|
|
case jsonInvertedIndex:
|
|
return getJSONInvertedIndexKeyLength(b)
|
|
case bytesDescMarker:
|
|
return getBytesLength(b, descendingBytesEscapes)
|
|
case geoDescMarker:
|
|
// Expect to reserve at least 8 bytes for int64.
|
|
if len(b) < 8 {
|
|
return 0, errors.Errorf("slice too short for spatial object (%d)", len(b))
|
|
}
|
|
ret, err := getBytesLength(b[8:], descendingGeoEscapes)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return 8 + ret, nil
|
|
case timeMarker, timeTZMarker:
|
|
return GetMultiVarintLen(b, 2)
|
|
case durationBigNegMarker, durationMarker, durationBigPosMarker:
|
|
return GetMultiVarintLen(b, 3)
|
|
case floatNeg, floatPos:
|
|
// the marker is followed by 8 bytes
|
|
if len(b) < 9 {
|
|
return 0, errors.Errorf("slice too short for float (%d)", len(b))
|
|
}
|
|
return 9, nil
|
|
}
|
|
if m >= IntMin && m <= IntMax {
|
|
return getVarintLen(b)
|
|
}
|
|
if m >= decimalNaN && m <= decimalNaNDesc {
|
|
return getDecimalLen(b)
|
|
}
|
|
return 0, errors.Errorf("unknown tag %d", m)
|
|
}
|
|
|
|
// DecodeNonsortingStdlibVarint decodes a value encoded by EncodeNonsortingVarint. It
|
|
// returns the length of the encoded varint and value.
|
|
func DecodeNonsortingStdlibVarint(b []byte) (remaining []byte, length int, value int64, err error) {
|
|
value, length = binary.Varint(b)
|
|
if length <= 0 {
|
|
return nil, 0, 0, fmt.Errorf("int64 varint decoding failed: %d", length)
|
|
}
|
|
return b[length:], length, value, nil
|
|
}
|
|
|
|
// DecodeNonsortingUvarint decodes a value encoded by EncodeNonsortingUvarint. It
|
|
// returns the length of the encoded varint and value.
|
|
func DecodeNonsortingUvarint(buf []byte) (remaining []byte, length int, value uint64, err error) {
|
|
// TODO(dan): Handle overflow.
|
|
for i, b := range buf {
|
|
value += uint64(b & 0x7f)
|
|
if b < 0x80 {
|
|
return buf[i+1:], i + 1, value, nil
|
|
}
|
|
value <<= 7
|
|
}
|
|
return buf, 0, 0, nil
|
|
}
|
|
|
|
// DecodeNonsortingStdlibUvarint decodes a value encoded with binary.PutUvarint. It
|
|
// returns the length of the encoded varint and value.
|
|
func DecodeNonsortingStdlibUvarint(
|
|
buf []byte,
|
|
) (remaining []byte, length int, value uint64, err error) {
|
|
i, n := binary.Uvarint(buf)
|
|
if n <= 0 {
|
|
return buf, 0, 0, errors.New("buffer too small")
|
|
}
|
|
return buf[n:], n, i, nil
|
|
}
|
|
|
|
// EncodeUntaggedDecimalValue encodes an apd.Decimal value, appends it to the supplied
|
|
// buffer, and returns the final buffer.
|
|
func EncodeUntaggedDecimalValue(appendTo []byte, d *apd.Decimal) []byte {
|
|
// To avoid the allocation, leave space for the varint, encode the decimal,
|
|
// encode the varint, and shift the encoded decimal to the end of the
|
|
// varint.
|
|
varintPos := len(appendTo)
|
|
// Manually append 10 (binary.MaxVarintLen64) 0s to avoid the allocation.
|
|
appendTo = append(appendTo, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
|
decOffset := len(appendTo)
|
|
appendTo = EncodeNonsortingDecimal(appendTo, d)
|
|
decLen := len(appendTo) - decOffset
|
|
varintLen := binary.PutUvarint(appendTo[varintPos:decOffset], uint64(decLen))
|
|
copy(appendTo[varintPos+varintLen:varintPos+varintLen+decLen], appendTo[decOffset:decOffset+decLen])
|
|
return appendTo[:varintPos+varintLen+decLen]
|
|
}
|
|
|
|
// DecodeValueTag decodes a value encoded by EncodeValueTag, used as a prefix in
|
|
// each of the other EncodeFooValue methods.
|
|
//
|
|
// The tag is structured such that the encoded column id can be dropped from the
|
|
// front by removing the first `typeOffset` bytes. DecodeValueTag,
|
|
// PeekValueLength and each of the DecodeFooValue methods will still work as
|
|
// expected with `b[typeOffset:]`. (Except, obviously, the column id is no
|
|
// longer encoded so if this suffix is passed back to DecodeValueTag, the
|
|
// returned colID should be discarded.)
|
|
//
|
|
// Concretely:
|
|
//
|
|
// b := ...
|
|
// typeOffset, _, colID, typ, err := DecodeValueTag(b)
|
|
// _, _, _, typ, err := DecodeValueTag(b[typeOffset:])
|
|
//
|
|
// will return the same typ and err and
|
|
//
|
|
// DecodeFooValue(b)
|
|
// DecodeFooValue(b[typeOffset:])
|
|
//
|
|
// will return the same thing. PeekValueLength works as expected with either of
|
|
// `b` or `b[typeOffset:]`.
|
|
func DecodeValueTag(b []byte) (typeOffset int, dataOffset int, colID uint32, typ Type, err error) {
|
|
// TODO(dan): This can be made faster by special casing the single byte
|
|
// version and skipping the column id extraction when it's not needed.
|
|
if len(b) == 0 {
|
|
return 0, 0, 0, Unknown, fmt.Errorf("empty array")
|
|
}
|
|
var n int
|
|
var tag uint64
|
|
b, n, tag, err = DecodeNonsortingUvarint(b)
|
|
if err != nil {
|
|
return 0, 0, 0, Unknown, err
|
|
}
|
|
colID = uint32(tag >> 4)
|
|
|
|
typ = Type(tag & 0xf)
|
|
typeOffset = n - 1
|
|
dataOffset = n
|
|
if typ == SentinelType {
|
|
_, n, tag, err = DecodeNonsortingUvarint(b)
|
|
if err != nil {
|
|
return 0, 0, 0, Unknown, err
|
|
}
|
|
typ = Type(tag)
|
|
dataOffset += n
|
|
}
|
|
return typeOffset, dataOffset, colID, typ, nil
|
|
}
|
|
|
|
// DecodeUntaggedDecimalValue decodes a value encoded by EncodeUntaggedDecimalValue.
|
|
func DecodeUntaggedDecimalValue(b []byte) (remaining []byte, d apd.Decimal, err error) {
|
|
var i uint64
|
|
b, _, i, err = DecodeNonsortingStdlibUvarint(b)
|
|
if err != nil {
|
|
return b, apd.Decimal{}, err
|
|
}
|
|
d, err = DecodeNonsortingDecimal(b[:int(i)], nil)
|
|
return b[int(i):], d, err
|
|
}
|
|
|
|
const uuidValueEncodedLength = 16
|
|
|
|
var _ [uuidValueEncodedLength]byte = uuid.UUID{} // Assert that uuid.UUID is length 16.
|
|
|
|
// getInvertedIndexKeyLength finds the length of an inverted index key
|
|
// encoded as a byte array.
|
|
func getInvertedIndexKeyLength(b []byte) (int, error) {
|
|
skipped := 0
|
|
for {
|
|
i := bytes.IndexByte(b[skipped:], escape)
|
|
if i == -1 {
|
|
return 0, errors.Errorf("malformed inverted index key in buffer %#x", b)
|
|
}
|
|
skipped += i + escapeLength
|
|
switch b[skipped-1] {
|
|
case escapedTerm, jsonEmptyObject, jsonEmptyArray:
|
|
return skipped, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// getJSONInvertedIndexKeyLength returns the length of encoded JSON inverted index
|
|
// key at the start of b.
|
|
func getJSONInvertedIndexKeyLength(buf []byte) (int, error) {
|
|
len, err := getInvertedIndexKeyLength(buf)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
switch buf[len] {
|
|
case jsonEmptyArray, jsonEmptyObject:
|
|
return len + 1, nil
|
|
|
|
default:
|
|
valLen, err := PeekLength(buf[len:])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return len + valLen, nil
|
|
}
|
|
}
|
|
|
|
// IsArrayKeyDone returns if the first byte in the input is the array
|
|
// terminator for the input direction.
|
|
func IsArrayKeyDone(buf []byte, dir Direction) bool {
|
|
expected := arrayKeyTerminator
|
|
if dir == Descending {
|
|
expected = arrayKeyDescendingTerminator
|
|
}
|
|
return buf[0] == expected
|
|
}
|