///|
pub(all) enum Direction {
Asc
Desc
}
///|
pub(all) enum Field {
BytesField(Direction)
Int64Field(Direction)
}
///|
#derive(Eq, Debug)
pub(all) enum Value {
Binary(Bytes)
Signed(Int64)
}
///|
pub extend Value with Eq::{equal, not_equal}
///|
pub extend Value with @debug.Debug::{to_repr}
///|
/// Field indexes and byte offsets in errors are zero-based.
#derive(Debug)
pub suberror KeyError {
EmptySpec
WrongArity(Int32, Int32)
InvalidPrefixLength(Int32)
TypeMismatch(Int32)
TruncatedKey(Int32)
InvalidEscape(Int32)
TrailingBytes(Int32)
CursorOutsideRange
InvalidLayout(Int32)
UnsupportedKeyFormat(Byte)
LayoutMismatch
InvalidTupleLength(Int32)
}
///|
pub extend KeyError with @debug.Debug::{to_repr}
///|
/// A fixed layout for one ordered index. Keys carry no schema metadata.
pub struct KeySpec {
priv fields : Array[Field]
}
///|
pub fn KeySpec::new(fields : Array[Field]) -> KeySpec raise KeyError {
if fields.is_empty() {
raise EmptySpec
}
{ fields: fields.copy(), }
}
///|
fn Field::direction(self : Field) -> Direction {
match self {
BytesField(direction) | Int64Field(direction) => direction
}
}
///|
fn Direction::mask(self : Direction) -> Byte {
match self {
Asc => 0
Desc => 0xff
}
}
///|
fn Direction::int64_mask(self : Direction) -> UInt64 {
match self {
Asc => 0x8000000000000000UL
Desc => 0x7fffffffffffffffUL
}
}
///|
fn write_field(
output : @buffer.Buffer,
field : Field,
value : Value,
index : Int32,
) -> Unit raise KeyError {
match (field, value) {
(BytesField(direction), Binary(bytes)) => {
let mask = direction.mask()
for byte in bytes {
output.write_byte(byte ^ mask)
if byte == 0 {
output.write_byte(0xff ^ mask)
}
}
output.write_byte(mask)
output.write_byte(mask)
}
(Int64Field(direction), Signed(number)) =>
output.write_uint64_be(
number.reinterpret_as_uint64() ^ direction.int64_mask(),
)
_ => raise TypeMismatch(index)
}
}
///|
fn KeySpec::encode_prefix(
self : KeySpec,
values : Array[Value],
) -> Bytes raise KeyError {
let output = @buffer.Buffer()
self.write_values(output, 0, values)
output.to_bytes()
}
///|
fn KeySpec::write_values(
self : KeySpec,
output : @buffer.Buffer,
start : Int32,
values : Array[Value],
) -> Unit raise KeyError {
for offset, value in values {
let index = start + offset
write_field(output, self.fields[index], value, index)
}
}
///|
/// Encodes a complete key in unsigned lexicographic byte order.
pub fn KeySpec::encode(
self : KeySpec,
values : Array[Value],
) -> Bytes raise KeyError {
if values.length() != self.fields.length() {
raise WrongArity(self.fields.length(), values.length())
}
self.encode_prefix(values)
}
///|
fn read_binary(
key : Bytes,
start : Int32,
mask : Byte,
) -> (Bytes, Int32) raise KeyError {
let value = @buffer.Buffer()
let mut index = start
while index < key.length() {
let byte = key[index] ^ mask
index += 1
if byte != 0 {
value.write_byte(byte)
} else {
if index == key.length() {
raise TruncatedKey(index)
}
match key[index] ^ mask {
0 => return (value.to_bytes(), index + 1)
0xff => value.write_byte(0)
_ => raise InvalidEscape(index)
}
index += 1
}
}
raise TruncatedKey(index)
}
///|
fn read_int64(
key : Bytes,
start : Int32,
direction : Direction,
) -> (Int64, Int32) raise KeyError {
if key.length() - start < 8 {
raise TruncatedKey(key.length())
}
let mut bits = 0UL
for index = start; index < start + 8; index = index + 1 {
bits = (bits << 8) | key[index].to_int().to_uint64()
}
((bits ^ direction.int64_mask()).reinterpret_as_int64(), start + 8)
}
///|
/// Decodes exactly one complete key and rejects trailing bytes.
pub fn KeySpec::decode(
self : KeySpec,
key : Bytes,
) -> Array[Value] raise KeyError {
let values = []
let mut offset = 0
for field in self.fields {
match field {
BytesField(direction) => {
let (value, end) = read_binary(key, offset, direction.mask())
values.push(Binary(value))
offset = end
}
Int64Field(direction) => {
let (value, end) = read_int64(key, offset, direction)
values.push(Signed(value))
offset = end
}
}
}
if offset != key.length() {
raise TrailingBytes(offset)
}
values
}