mirror of https://github.com/nextgraph-org/rkv.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
8.9 KiB
252 lines
8.9 KiB
// Copyright 2018-2019 Mozilla
|
|
//
|
|
// 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.
|
|
|
|
use std::fmt;
|
|
|
|
use arrayref::array_ref;
|
|
use bincode::{deserialize, serialize, serialized_size};
|
|
use ordered_float::OrderedFloat;
|
|
use uuid::{Bytes, Uuid};
|
|
|
|
use crate::error::DataError;
|
|
|
|
/// We define a set of types, associated with simple integers, to annotate values stored
|
|
/// in LMDB. This is to avoid an accidental 'cast' from a value of one type to another.
|
|
/// For this reason we don't simply use `deserialize` from the `bincode` crate.
|
|
#[repr(u8)]
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub enum Type {
|
|
Bool = 1,
|
|
U64 = 2,
|
|
I64 = 3,
|
|
F64 = 4,
|
|
Instant = 5, // Millisecond-precision timestamp.
|
|
Uuid = 6,
|
|
Str = 7,
|
|
Json = 8,
|
|
Blob = 9,
|
|
}
|
|
|
|
/// We use manual tagging, because <https://github.com/serde-rs/serde/issues/610>.
|
|
impl Type {
|
|
pub fn from_tag(tag: u8) -> Result<Type, DataError> {
|
|
Type::from_primitive(tag).ok_or(DataError::UnknownType(tag))
|
|
}
|
|
|
|
#[allow(clippy::wrong_self_convention)]
|
|
pub fn to_tag(self) -> u8 {
|
|
self as u8
|
|
}
|
|
|
|
fn from_primitive(p: u8) -> Option<Type> {
|
|
match p {
|
|
1 => Some(Type::Bool),
|
|
2 => Some(Type::U64),
|
|
3 => Some(Type::I64),
|
|
4 => Some(Type::F64),
|
|
5 => Some(Type::Instant),
|
|
6 => Some(Type::Uuid),
|
|
7 => Some(Type::Str),
|
|
8 => Some(Type::Json),
|
|
9 => Some(Type::Blob),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Type {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
f.write_str(match *self {
|
|
Type::Bool => "bool",
|
|
Type::U64 => "u64",
|
|
Type::I64 => "i64",
|
|
Type::F64 => "f64",
|
|
Type::Instant => "instant",
|
|
Type::Uuid => "uuid",
|
|
Type::Str => "str",
|
|
Type::Json => "json",
|
|
Type::Blob => "blob",
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
pub enum Value<'v> {
|
|
Bool(bool),
|
|
U64(u64),
|
|
I64(i64),
|
|
F64(OrderedFloat<f64>),
|
|
Instant(i64), // Millisecond-precision timestamp.
|
|
Uuid(&'v Bytes),
|
|
Str(&'v str),
|
|
Json(&'v str),
|
|
Blob(&'v [u8]),
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum OwnedValue {
|
|
Bool(bool),
|
|
U64(u64),
|
|
I64(i64),
|
|
F64(f64),
|
|
Instant(i64), // Millisecond-precision timestamp.
|
|
Uuid(Uuid),
|
|
Str(String),
|
|
Json(String), // TODO
|
|
Blob(Vec<u8>),
|
|
}
|
|
|
|
fn uuid(bytes: &[u8]) -> Result<Value, DataError> {
|
|
if bytes.len() == 16 {
|
|
Ok(Value::Uuid(array_ref![bytes, 0, 16]))
|
|
} else {
|
|
Err(DataError::InvalidUuid)
|
|
}
|
|
}
|
|
|
|
impl<'v> Value<'v> {
|
|
pub fn from_tagged_slice(slice: &'v [u8]) -> Result<Value<'v>, DataError> {
|
|
//let (tag, data) = slice.split_first().ok_or(DataError::Empty)?;
|
|
//let t = Type::from_tag(*tag)?;
|
|
//Value::from_type_and_data(t, data)
|
|
Ok(Value::Blob(slice))
|
|
}
|
|
|
|
fn from_type_and_data(t: Type, data: &'v [u8]) -> Result<Value<'v>, DataError> {
|
|
if t == Type::Uuid {
|
|
return deserialize(data)
|
|
.map_err(|e| DataError::DecodingError {
|
|
value_type: t,
|
|
err: e,
|
|
})
|
|
.map(uuid)?;
|
|
}
|
|
|
|
match t {
|
|
Type::Bool => deserialize(data).map(Value::Bool),
|
|
Type::U64 => deserialize(data).map(Value::U64),
|
|
Type::I64 => deserialize(data).map(Value::I64),
|
|
Type::F64 => deserialize(data).map(OrderedFloat).map(Value::F64),
|
|
Type::Instant => deserialize(data).map(Value::Instant),
|
|
Type::Str => deserialize(data).map(Value::Str),
|
|
Type::Json => deserialize(data).map(Value::Json),
|
|
Type::Blob => deserialize(data).map(Value::Blob),
|
|
Type::Uuid => {
|
|
// Processed above to avoid verbose duplication of error transforms.
|
|
unreachable!()
|
|
}
|
|
}
|
|
.map_err(|e| DataError::DecodingError {
|
|
value_type: t,
|
|
err: e,
|
|
})
|
|
}
|
|
|
|
pub fn to_bytes(&self) -> Result<Vec<u8>, DataError> {
|
|
match self {
|
|
Value::Bool(v) => serialize(&(Type::Bool.to_tag(), *v)),
|
|
Value::U64(v) => serialize(&(Type::U64.to_tag(), *v)),
|
|
Value::I64(v) => serialize(&(Type::I64.to_tag(), *v)),
|
|
Value::F64(v) => serialize(&(Type::F64.to_tag(), v.0)),
|
|
Value::Instant(v) => serialize(&(Type::Instant.to_tag(), *v)),
|
|
Value::Str(v) => serialize(&(Type::Str.to_tag(), v)),
|
|
Value::Json(v) => serialize(&(Type::Json.to_tag(), v)),
|
|
Value::Blob(v) => Ok(v.to_vec()),
|
|
Value::Uuid(v) => serialize(&(Type::Uuid.to_tag(), v)),
|
|
}
|
|
.map_err(DataError::EncodingError)
|
|
}
|
|
|
|
pub fn serialized_size(&self) -> Result<u64, DataError> {
|
|
match self {
|
|
Value::Bool(v) => serialized_size(&(Type::Bool.to_tag(), *v)),
|
|
Value::U64(v) => serialized_size(&(Type::U64.to_tag(), *v)),
|
|
Value::I64(v) => serialized_size(&(Type::I64.to_tag(), *v)),
|
|
Value::F64(v) => serialized_size(&(Type::F64.to_tag(), v.0)),
|
|
Value::Instant(v) => serialized_size(&(Type::Instant.to_tag(), *v)),
|
|
Value::Str(v) => serialized_size(&(Type::Str.to_tag(), v)),
|
|
Value::Json(v) => serialized_size(&(Type::Json.to_tag(), v)),
|
|
Value::Blob(v) => serialized_size(&(Type::Blob.to_tag(), v)),
|
|
Value::Uuid(v) => serialized_size(&(Type::Uuid.to_tag(), v)),
|
|
}
|
|
.map_err(DataError::EncodingError)
|
|
}
|
|
}
|
|
|
|
impl<'v> From<&'v Value<'v>> for OwnedValue {
|
|
fn from(value: &Value) -> OwnedValue {
|
|
match value {
|
|
Value::Bool(v) => OwnedValue::Bool(*v),
|
|
Value::U64(v) => OwnedValue::U64(*v),
|
|
Value::I64(v) => OwnedValue::I64(*v),
|
|
Value::F64(v) => OwnedValue::F64(**v),
|
|
Value::Instant(v) => OwnedValue::Instant(*v),
|
|
Value::Uuid(v) => OwnedValue::Uuid(Uuid::from_bytes(**v)),
|
|
Value::Str(v) => OwnedValue::Str((*v).to_string()),
|
|
Value::Json(v) => OwnedValue::Json((*v).to_string()),
|
|
Value::Blob(v) => OwnedValue::Blob(v.to_vec()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'v> From<&'v OwnedValue> for Value<'v> {
|
|
fn from(value: &OwnedValue) -> Value {
|
|
match value {
|
|
OwnedValue::Bool(v) => Value::Bool(*v),
|
|
OwnedValue::U64(v) => Value::U64(*v),
|
|
OwnedValue::I64(v) => Value::I64(*v),
|
|
OwnedValue::F64(v) => Value::F64(OrderedFloat::from(*v)),
|
|
OwnedValue::Instant(v) => Value::Instant(*v),
|
|
OwnedValue::Uuid(v) => Value::Uuid(v.as_bytes()),
|
|
OwnedValue::Str(v) => Value::Str(v),
|
|
OwnedValue::Json(v) => Value::Json(v),
|
|
OwnedValue::Blob(v) => Value::Blob(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_value_serialized_size() {
|
|
// | Value enum | tag: 1 byte | value_payload |
|
|
// |----------------------------------------------------------|
|
|
// | I64 | 1 | 8 |
|
|
// | U64 | 1 | 8 |
|
|
// | Bool | 1 | 1 |
|
|
// | Instant | 1 | 8 |
|
|
// | F64 | 1 | 8 |
|
|
// | Uuid | 1 | 16 |
|
|
// | Str/Blob/Json | 1 |(8: len + sizeof(payload))|
|
|
// assert_eq!(Value::I64(-1000).serialized_size().unwrap(), 9);
|
|
// assert_eq!(Value::U64(1000u64).serialized_size().unwrap(), 9);
|
|
// assert_eq!(Value::Bool(true).serialized_size().unwrap(), 2);
|
|
// assert_eq!(
|
|
// Value::Instant(1_558_020_865_224).serialized_size().unwrap(),
|
|
// 9
|
|
// );
|
|
// assert_eq!(
|
|
// Value::F64(OrderedFloat(10000.1)).serialized_size().unwrap(),
|
|
// 9
|
|
// );
|
|
// assert_eq!(Value::Str("hello!").serialized_size().unwrap(), 15);
|
|
// assert_eq!(Value::Str("¡Hola").serialized_size().unwrap(), 15);
|
|
assert_eq!(Value::Blob(b"hello!").serialized_size().unwrap(), 6);
|
|
// assert_eq!(
|
|
// uuid(b"\x9f\xe2\xc4\xe9\x3f\x65\x4f\xdb\xb2\x4c\x02\xb1\x52\x59\x71\x6c")
|
|
// .unwrap()
|
|
// .serialized_size()
|
|
// .unwrap(),
|
|
// 17
|
|
// );
|
|
}
|
|
}
|
|
|