summaryrefslogtreecommitdiffstats
path: root/third_party/rust/rkv/src/value.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/rkv/src/value.rs')
-rw-r--r--third_party/rust/rkv/src/value.rs252
1 files changed, 252 insertions, 0 deletions
diff --git a/third_party/rust/rkv/src/value.rs b/third_party/rust/rkv/src/value.rs
new file mode 100644
index 0000000000..762607acd3
--- /dev/null
+++ b/third_party/rust/rkv/src/value.rs
@@ -0,0 +1,252 @@
+// 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> {
+ #![allow(clippy::unnecessary_lazy_evaluations)]
+ Type::from_primitive(tag).ok_or_else(|| 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)
+ }
+
+ 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) => serialize(&(Type::Blob.to_tag(), v)),
+ 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(), 15);
+ 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
+ );
+ }
+}