From 67149e1f64387c4239050435ada2d9f2c6395f70 Mon Sep 17 00:00:00 2001 From: Joshua Send Date: Thu, 30 May 2024 11:12:59 +0100 Subject: [PATCH] Outline of structs as a general 'definition' system (#7069) ## Usage and product changes We introduce the architecture around structs definitions, and definitions in general. The idea of a `definition` is that it is a general schema construct outside the regular `thing` and `type` system - this will initially be used for structs and functions. Definitions are interesting because their interesting payload is in the value, not the key. However, the key contains the ID of the definition. As a result, you can consider the new `DefinitionKey` to be analogous to `TypeVertex`, but the interesting data structure is the `StructDefinition`, which serialises a struct's content. In doing this, we also realise that `ValueType` must encompass the built-in value type category, plus the ID of the struct definition, and create a new `ValueTypeCategory` which can map to a Prefix and doesn't contain the ID of struct definitions. Lastly, we create a the serialised form of `ValueTypeBytes`, which is used as the property of an attribute type's value type, including the definition ID if it exists. ### What isn't implemented yet: 1. The indexing + retrieval by name of value types through the TypeManager 2. Serialisation of `StructDefinition` 3. The data insertion and deletion pattern of struct instantiations --- concept/thing/attribute.rs | 4 - concept/thing/mod.rs | 10 +- concept/thing/thing_manager.rs | 53 ++++-- concept/thing/value.rs | 33 +++- concept/thing/value_struct.rs | 40 +++++ concept/type_/attribute_type.rs | 6 +- concept/type_/type_cache/type_cache.rs | 4 +- concept/type_/type_manager.rs | 9 +- concept/type_/type_reader.rs | 5 +- encoding/BUILD | 1 + encoding/graph/definition/definition_key.rs | 95 +++++++++++ .../definition/definition_key_generator.rs | 35 ++++ encoding/graph/definition/mod.rs | 9 + encoding/graph/definition/struct.rs | 27 +++ encoding/graph/mod.rs | 1 + encoding/graph/thing/edge.rs | 8 +- encoding/graph/thing/vertex_attribute.rs | 113 ++++++++----- encoding/graph/thing/vertex_generator.rs | 11 +- encoding/graph/type_/vertex.rs | 1 + encoding/layout/prefix.rs | 19 ++- encoding/value/mod.rs | 6 + encoding/value/struct_bytes.rs | 51 ++++++ encoding/value/value_type.rs | 154 ++++++++++++------ resource/constants.rs | 7 + 24 files changed, 564 insertions(+), 138 deletions(-) create mode 100644 concept/thing/value_struct.rs create mode 100644 encoding/graph/definition/definition_key.rs create mode 100644 encoding/graph/definition/definition_key_generator.rs create mode 100644 encoding/graph/definition/mod.rs create mode 100644 encoding/graph/definition/struct.rs create mode 100644 encoding/value/struct_bytes.rs diff --git a/concept/thing/attribute.rs b/concept/thing/attribute.rs index 895460e1d6..852d76d048 100644 --- a/concept/thing/attribute.rs +++ b/concept/thing/attribute.rs @@ -43,10 +43,6 @@ impl<'a> Attribute<'a> { Attribute { vertex, value: None } } - pub(crate) fn value_type(&self) -> ValueType { - self.vertex.value_type() - } - pub fn type_(&self) -> AttributeType<'static> { AttributeType::new(build_vertex_attribute_type(self.vertex.type_id_())) } diff --git a/concept/thing/mod.rs b/concept/thing/mod.rs index bfbcc29822..cbc8aa1eac 100644 --- a/concept/thing/mod.rs +++ b/concept/thing/mod.rs @@ -6,13 +6,14 @@ use bytes::byte_array::ByteArray; use encoding::{graph::thing::vertex_attribute::AttributeID, value::value_type::ValueType}; +use encoding::value::value_type::ValueTypeCategory; use resource::constants::snapshot::BUFFER_VALUE_INLINE; use storage::snapshot::{ReadableSnapshot, WritableSnapshot}; use crate::{ + ConceptStatus, error::{ConceptReadError, ConceptWriteError}, thing::thing_manager::ThingManager, - ConceptStatus, }; pub mod attribute; @@ -22,6 +23,7 @@ pub mod relation; pub mod statistics; pub mod thing_manager; pub mod value; +mod value_struct; pub trait ThingAPI<'a> { fn set_modified(&self, snapshot: &mut Snapshot, thing_manager: &ThingManager); @@ -47,11 +49,11 @@ pub trait ThingAPI<'a> { } // TODO: where do these belong? They're encodings of values we store for keys -pub(crate) fn decode_attribute_ids(value_type: ValueType, bytes: &[u8]) -> impl Iterator + '_ { - let chunk_size = AttributeID::value_type_encoding_length(value_type); +pub(crate) fn decode_attribute_ids(value_type_category: ValueTypeCategory, bytes: &[u8]) -> impl Iterator + '_ { + let chunk_size = AttributeID::value_type_encoding_length(value_type_category); let chunks_iter = bytes.chunks_exact(chunk_size); debug_assert!(chunks_iter.remainder().is_empty()); - chunks_iter.map(move |chunk| AttributeID::new(value_type, chunk)) + chunks_iter.map(move |chunk| AttributeID::new(value_type_category, chunk)) } pub(crate) fn encode_attribute_ids(attribute_ids: impl Iterator) -> ByteArray { diff --git a/concept/thing/thing_manager.rs b/concept/thing/thing_manager.rs index 7154b5e911..2657cafced 100644 --- a/concept/thing/thing_manager.rs +++ b/concept/thing/thing_manager.rs @@ -173,11 +173,12 @@ impl ThingManager { snapshot: &'this Snapshot, attribute_type: AttributeType<'_>, ) -> Result, ConceptReadError> { - let Some(value_type) = attribute_type.get_value_type(snapshot, self.type_manager.as_ref())? else { + let attribute_value_type = attribute_type.get_value_type(snapshot, self.type_manager.as_ref())?; + let Some(value_type) = attribute_value_type.as_ref() else { return Ok(AttributeIterator::new_empty()); }; - let attribute_value_type_prefix = AttributeVertex::value_type_to_prefix_type(value_type); + let attribute_value_type_prefix = AttributeVertex::value_type_category_to_prefix_type(value_type.category()); let prefix = AttributeVertex::build_prefix_type(attribute_value_type_prefix, attribute_type.vertex().type_id_()); let attribute_iterator = @@ -191,12 +192,14 @@ impl ThingManager { Ok(AttributeIterator::new(attribute_iterator, has_reverse_iterator, snapshot, self.type_manager())) } - pub(crate) fn get_attribute_value( + pub(crate) fn get_attribute_value<'a>( &self, snapshot: &Snapshot, - attribute: &Attribute<'_>, + attribute: &'a Attribute<'a>, ) -> Result, ConceptReadError> { - match attribute.value_type() { + let attribute_type = attribute.type_(); + let value_type = attribute_type.get_value_type(snapshot, self.type_manager())?; + match value_type.as_ref().unwrap() { ValueType::Boolean => { let attribute_id = attribute.vertex().attribute_id().unwrap_boolean(); Ok(Value::Boolean(BooleanBytes::new(attribute_id.bytes()).as_bool())) @@ -228,6 +231,9 @@ impl ThingManager { .unwrap()) } } + ValueType::Struct(definition_key) => { + todo!() + } } } @@ -237,7 +243,13 @@ impl ThingManager { attribute_type: AttributeType<'static>, value: Value<'_>, ) -> Result>, ConceptReadError> { - let attribute = match value.value_type() { + let value_type = value.value_type(); + let attribute_value_type = attribute_type.get_value_type(snapshot, self.type_manager())?; + if attribute_value_type.is_none() || attribute_value_type.as_ref().unwrap() != &value_type { + return Ok(None); + } + + let attribute = match value_type { ValueType::Boolean | ValueType::Long | ValueType::Double | ValueType::DateTime => { debug_assert!(AttributeID::is_inlineable(value.as_reference())); match self.get_attribute_with_value_inline(snapshot, attribute_type, value) { @@ -263,6 +275,9 @@ impl ThingManager { } } } + ValueType::Struct(definition_key) => { + todo!() + } }; let is_independent = attribute.type_().is_independent(snapshot, self.type_manager())?; @@ -280,8 +295,12 @@ impl ThingManager { value: Value<'_>, ) -> Result>, ConceptReadError> { debug_assert!(AttributeID::is_inlineable(value.as_reference())); + let attribute_value_type = attribute_type.get_value_type(snapshot, self.type_manager())?; + if attribute_value_type.is_none() || attribute_value_type.as_ref().unwrap() == &value.value_type() { + return Ok(None) + } let vertex = AttributeVertex::build( - value.value_type(), + attribute_value_type.as_ref().unwrap().category(), attribute_type.vertex().type_id_(), AttributeID::build_inline(value), ); @@ -297,10 +316,11 @@ impl ThingManager { attribute_type: AttributeType<'static>, value: Value<'_>, ) -> Result { + // TODO: check value type matches attribute value type let value_type = value.value_type(); let vertex = if AttributeID::is_inlineable(value.as_reference()) { // don't need to do an extra lookup to get the attribute vertex - if it exists, it will have this ID - AttributeVertex::build(value_type, attribute_type.vertex().type_id_(), AttributeID::build_inline(value)) + AttributeVertex::build(value_type.category(), attribute_type.vertex().type_id_(), AttributeID::build_inline(value)) } else { // non-inline attributes require an extra lookup before checking for the has edge existence let attribute = self.get_attribute_with_value(snapshot, attribute_type, value)?; @@ -335,13 +355,14 @@ impl ThingManager { owner: &impl ObjectAPI<'a>, attribute_type: AttributeType<'static>, ) -> Result { - let value_type = match attribute_type.get_value_type(snapshot, self.type_manager())? { + let attribute_value_type = attribute_type.get_value_type(snapshot, self.type_manager())?; + let value_type = match attribute_value_type.as_ref() { None => { todo!("Handle missing value type - for abstract attributes. Or assume this will never happen") } Some(value_type) => value_type, }; - let prefix = ThingEdgeHas::prefix_from_object_to_type(owner.vertex(), value_type, attribute_type.into_vertex()); + let prefix = ThingEdgeHas::prefix_from_object_to_type(owner.vertex(), value_type.category(), attribute_type.into_vertex()); Ok(HasAttributeIterator::new( snapshot.iterate_range(KeyRange::new_within(prefix, ThingEdgeHas::FIXED_WIDTH_ENCODING)), )) @@ -354,7 +375,8 @@ impl ThingManager { attribute_type: AttributeType<'static>, ) -> Result>, ConceptReadError> { let key = HAS_ORDER_PROPERTY_FACTORY.build(owner.vertex(), attribute_type.vertex()); - let value_type = match attribute_type.get_value_type(snapshot, self.type_manager())? { + let attribute_value_type = attribute_type.get_value_type(snapshot, self.type_manager())?; + let value_type = match attribute_value_type.as_ref() { None => { todo!("Handle missing value type - for abstract attributes. Or assume this will never happen") } @@ -362,7 +384,7 @@ impl ThingManager { }; let attributes = snapshot .get_mapped(key.as_storage_key().as_reference(), |bytes| { - decode_attribute_ids(value_type, bytes.bytes()) + decode_attribute_ids(value_type.category(), bytes.bytes()) .map(|id| Attribute::new(AttributeVertex::new(Bytes::copy(id.bytes())))) .collect() }) @@ -662,7 +684,7 @@ impl<'txn, Snapshot: WritableSnapshot> ThingManager { value: Value<'_>, ) -> Result, ConceptWriteError> { let value_type = attribute_type.get_value_type(snapshot, self.type_manager.as_ref())?; - if Some(value.value_type()) == value_type { + if Some(&value.value_type()) == value_type.as_ref() { let vertex = match value { Value::Boolean(bool) => { let encoded_boolean = BooleanBytes::build(bool); @@ -719,10 +741,13 @@ impl<'txn, Snapshot: WritableSnapshot> ThingManager { .create_attribute_string(attribute_type.vertex().type_id_(), encoded_string, snapshot) .map_err(|err| ConceptWriteError::SnapshotIterate { source: err })? } + Value::Struct(struct_) => { + todo!() + } }; Ok(Attribute::new(vertex)) } else { - Err(ConceptWriteError::ValueTypeMismatch { expected: value_type, provided: value.value_type() }) + Err(ConceptWriteError::ValueTypeMismatch { expected: value_type.as_ref().cloned(), provided: value.value_type() }) } } diff --git a/concept/thing/value.rs b/concept/thing/value.rs index ee63192c70..1e40394786 100644 --- a/concept/thing/value.rs +++ b/concept/thing/value.rs @@ -7,12 +7,14 @@ use std::borrow::Cow; use chrono::NaiveDateTime; + use encoding::value::{ boolean_bytes::BooleanBytes, date_time_bytes::DateTimeBytes, double_bytes::DoubleBytes, long_bytes::LongBytes, string_bytes::StringBytes, value_type::ValueType, ValueEncodable, }; +use encoding::value::struct_bytes::StructBytes; -// TODO: how do we handle user-created compound structs? +use crate::thing::value_struct::StructValue; #[derive(Debug, Clone, PartialEq)] pub enum Value<'a> { @@ -21,6 +23,7 @@ pub enum Value<'a> { Double(f64), DateTime(NaiveDateTime), String(Cow<'a, str>), + Struct(Cow<'a, StructValue<'static>>), } impl<'a> Value<'a> { @@ -31,6 +34,7 @@ impl<'a> Value<'a> { Value::Double(double) => Value::Double(*double), Value::DateTime(date_time) => Value::DateTime(*date_time), Value::String(string) => Value::String(Cow::Borrowed(string.as_ref())), + Value::Struct(struct_) => Value::Struct(Cow::Borrowed(struct_.as_ref())) } } @@ -68,6 +72,13 @@ impl<'a> Value<'a> { _ => panic!("Cannot unwrap String if not a string value."), } } + + pub fn unwrap_struct(self) -> Cow<'a, StructValue<'static>> { + match self { + Value::Struct(struct_) => struct_, + _ => panic!("Cannot unwrap Struct if not a struct value.") + } + } } impl<'a> ValueEncodable for Value<'a> { @@ -78,41 +89,51 @@ impl<'a> ValueEncodable for Value<'a> { Value::Double(_) => ValueType::Double, Value::DateTime(_) => ValueType::DateTime, Value::String(_) => ValueType::String, + Value::Struct(struct_value) => ValueType::Struct(struct_value.definition_key().into_owned()) } } fn encode_boolean(&self) -> BooleanBytes { match self { Self::Boolean(boolean) => BooleanBytes::build(*boolean), - _ => panic!("Cannot encoded non-boolean as BooleanBytes"), + _ => panic!("Cannot encode non-boolean as BooleanBytes"), } } fn encode_long(&self) -> LongBytes { match self { Self::Long(long) => LongBytes::build(*long), - _ => panic!("Cannot encoded non-long as LongBytes"), + _ => panic!("Cannot encode non-long as LongBytes"), } } fn encode_double(&self) -> DoubleBytes { match self { Self::Double(double) => DoubleBytes::build(*double), - _ => panic!("Cannot encoded non-double as DoubleBytes"), + _ => panic!("Cannot encode non-double as DoubleBytes"), } } fn encode_date_time(&self) -> DateTimeBytes { match self { Self::DateTime(date_time) => DateTimeBytes::build(*date_time), - _ => panic!("Cannot encoded non-datetime as DateTimeBytes"), + _ => panic!("Cannot encode non-datetime as DateTimeBytes"), } } fn encode_string(&self) -> StringBytes<'_, INLINE_LENGTH> { match self { Value::String(str) => StringBytes::build_ref(str), - _ => panic!("Cannot encoded non-String as StringBytes"), + _ => panic!("Cannot encode non-String as StringBytes"), + } + } + + fn encode_struct(&self) -> StructBytes<'static, INLINE_LENGTH> { + match self { + Value::Struct(struct_) => { + todo!() + }, + _ => panic!("Cannot encode non-Struct as StructBytes"), } } } diff --git a/concept/thing/value_struct.rs b/concept/thing/value_struct.rs new file mode 100644 index 0000000000..b90a327144 --- /dev/null +++ b/concept/thing/value_struct.rs @@ -0,0 +1,40 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +use std::collections::HashMap; +use serde::{Deserialize, Serialize}; + +use encoding::graph::definition::definition_key::DefinitionKey; +use encoding::graph::type_::vertex::TypeVertex; +use resource::constants::encoding::StructFieldIDUInt; + +use crate::thing::value::Value; + +#[derive(Debug, Clone, PartialEq)] +pub struct StructValue<'a> { + definition: DefinitionKey<'static>, + + // a map allows empty fields to not be recorded at all + fields: HashMap> +} + +impl<'a> StructValue<'a> { + + pub fn definition_key(&self) -> DefinitionKey<'_> { + self.definition.as_reference() + } +} + + +// TODO: implement serialise/deserialise for the StructValue +// since JSON serialisation seems to be able to handle recursive nesting, it should be able to handle that diff --git a/concept/type_/attribute_type.rs b/concept/type_/attribute_type.rs index d9bdfd6997..647b602f8f 100644 --- a/concept/type_/attribute_type.rs +++ b/concept/type_/attribute_type.rs @@ -102,11 +102,11 @@ impl<'a> AttributeType<'a> { type_manager.storage_set_value_type(snapshot, self.clone().into_owned(), value_type) } - pub fn get_value_type( + pub fn get_value_type<'m, Snapshot: ReadableSnapshot>( &self, snapshot: &Snapshot, - type_manager: &TypeManager, - ) -> Result, ConceptReadError> { + type_manager: &'m TypeManager, + ) -> Result>, ConceptReadError> { type_manager.get_attribute_type_value_type(snapshot, self.clone().into_owned()) } diff --git a/concept/type_/type_cache/type_cache.rs b/concept/type_/type_cache/type_cache.rs index 6c9aae6117..b60ee47b6b 100644 --- a/concept/type_/type_cache/type_cache.rs +++ b/concept/type_/type_cache/type_cache.rs @@ -248,8 +248,8 @@ impl TypeCache { &T::get_cache(self, type_).owner_player_cache().plays_transitive } - pub(crate) fn get_attribute_type_value_type<'a>(&self, attribute_type: AttributeType<'a>) -> Option { - AttributeType::get_cache(&self, attribute_type).value_type + pub(crate) fn get_attribute_type_value_type<'a>(&self, attribute_type: AttributeType<'a>) -> &Option { + &AttributeType::get_cache(&self, attribute_type).value_type } pub(crate) fn get_owns_annotations<'c>(&'c self, owns: Owns<'c>) -> &'c HashSet { diff --git a/concept/type_/type_manager.rs b/concept/type_/type_manager.rs index 75c487d689..c51294b0a8 100644 --- a/concept/type_/type_manager.rs +++ b/concept/type_/type_manager.rs @@ -39,6 +39,7 @@ use encoding::{ value::{label::Label, value_type::ValueType}, AsBytes, Keyable, }; +use encoding::value::value_type::ValueTypeBytes; use primitive::maybe_owns::MaybeOwns; use resource::constants::snapshot::BUFFER_KEY_INLINE; use storage::{ @@ -511,11 +512,11 @@ impl TypeManager { &self, snapshot: &Snapshot, attribute_type: AttributeType<'static>, - ) -> Result, ConceptReadError> { + ) -> Result>, ConceptReadError> { if let Some(cache) = &self.type_cache { - Ok(cache.get_attribute_type_value_type(attribute_type)) + Ok(MaybeOwns::Borrowed(cache.get_attribute_type_value_type(attribute_type))) } else { - TypeReader::get_value_type(snapshot, attribute_type) + Ok(MaybeOwns::Owned(TypeReader::get_value_type(snapshot, attribute_type)?)) } } @@ -865,7 +866,7 @@ impl TypeManager { ) { let property_key = build_property_type_value_type(attribute.into_vertex()).into_storage_key().into_owned_array(); - let property_value = ByteArray::copy(&value_type.value_type_id().bytes()); + let property_value = ByteArray::copy(&ValueTypeBytes::build(&value_type).into_bytes()); snapshot.put_val(property_key, property_value); } diff --git a/concept/type_/type_reader.rs b/concept/type_/type_reader.rs index 32d1d9ae18..a8fb07c090 100644 --- a/concept/type_/type_reader.rs +++ b/concept/type_/type_reader.rs @@ -26,10 +26,11 @@ use encoding::{ value::{ label::Label, string_bytes::StringBytes, - value_type::{ValueType, ValueTypeID}, + value_type::{ValueType}, }, AsBytes, Keyable, }; +use encoding::value::value_type::ValueTypeBytes; use iterator::Collector; use resource::constants::{encoding::LABEL_SCOPED_NAME_STRING_INLINE, snapshot::BUFFER_KEY_INLINE}; use storage::{key_range::KeyRange, snapshot::ReadableSnapshot}; @@ -342,7 +343,7 @@ impl TypeReader { snapshot .get_mapped( build_property_type_value_type(type_.into_vertex()).into_storage_key().as_reference(), - |bytes| ValueType::from_value_type_id(ValueTypeID::new(bytes.bytes().try_into().unwrap())), + |bytes| ValueTypeBytes::new(bytes.bytes().try_into().unwrap()).to_value_type() ) .map_err(|error| ConceptReadError::SnapshotGet { source: error }) } diff --git a/encoding/BUILD b/encoding/BUILD index c8268297a8..a3165c36fc 100644 --- a/encoding/BUILD +++ b/encoding/BUILD @@ -31,6 +31,7 @@ rust_library( "@crates//:chrono", "@crates//:tracing", "@crates//:seahash", + "@crates//:serde", ], ) diff --git a/encoding/graph/definition/definition_key.rs b/encoding/graph/definition/definition_key.rs new file mode 100644 index 0000000000..dbda1fca84 --- /dev/null +++ b/encoding/graph/definition/definition_key.rs @@ -0,0 +1,95 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +use std::ops::Range; + +use bytes::byte_array::ByteArray; +use bytes::byte_reference::ByteReference; +use bytes::Bytes; +use resource::constants::encoding::DefinitionIDUInt; +use resource::constants::snapshot::BUFFER_KEY_INLINE; + +use crate::{AsBytes, EncodingKeyspace, Keyable, Prefixed}; +use crate::layout::prefix::{Prefix, PrefixID}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct DefinitionKey<'a> { + bytes: Bytes<'a, { BUFFER_KEY_INLINE }>, +} + +impl<'a> DefinitionKey<'a> { + pub(crate) const KEYSPACE: EncodingKeyspace = EncodingKeyspace::Schema; + pub const FIXED_WIDTH_ENCODING: bool = true; + + pub(crate) const LENGTH: usize = PrefixID::LENGTH + DefinitionID::LENGTH; + pub(crate) const LENGTH_PREFIX: usize = PrefixID::LENGTH; + pub(crate) const RANGE_DEFINITION_ID: Range = Self::RANGE_PREFIX.end..Self::RANGE_PREFIX.end + DefinitionID::LENGTH; + + pub fn new(bytes: Bytes<'a, BUFFER_KEY_INLINE>) -> Self { + debug_assert_eq!(bytes.length(), Self::LENGTH); + Self { bytes } + } + + pub(crate) fn build(prefix: Prefix, definition_id: DefinitionID) -> Self { + let mut array = ByteArray::zeros(Self::LENGTH); + array.bytes_mut()[Self::RANGE_PREFIX].copy_from_slice(&prefix.prefix_id().bytes()); + array.bytes_mut()[Self::RANGE_DEFINITION_ID].copy_from_slice(&definition_id.bytes()); + Self { bytes: Bytes::Array(array) } + } + + pub fn as_reference<'this: 'a>(&'this self) -> DefinitionKey<'this> { + Self::new(Bytes::Reference(self.bytes.as_reference())) + } + + pub fn into_owned(self) -> DefinitionKey<'static> { + DefinitionKey { bytes: self.bytes.into_owned() } + } +} + +impl<'a> AsBytes<'a, BUFFER_KEY_INLINE> for DefinitionKey<'a> { + fn bytes(&'a self) -> ByteReference<'a> { + self.bytes.as_reference() + } + + fn into_bytes(self) -> Bytes<'a, BUFFER_KEY_INLINE> { + self.bytes + } +} + +impl<'a> Keyable<'a, BUFFER_KEY_INLINE> for DefinitionKey<'a> { + fn keyspace(&self) -> EncodingKeyspace { + Self::KEYSPACE + } +} + +impl<'a> Prefixed<'a, BUFFER_KEY_INLINE> for DefinitionKey<'a> {} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct DefinitionID { + bytes: [u8; DefinitionID::LENGTH], +} + +impl DefinitionID { + pub(crate) const LENGTH: usize = std::mem::size_of::(); + + pub fn new(bytes: [u8; DefinitionID::LENGTH]) -> DefinitionID { + DefinitionID { bytes } + } + + pub fn build(id: DefinitionIDUInt) -> Self { + debug_assert_eq!(std::mem::size_of_val(&id), DefinitionID::LENGTH); + DefinitionID { bytes: id.to_be_bytes() } + } + + pub fn as_uint(&self) -> DefinitionIDUInt { + DefinitionIDUInt::from_be_bytes(self.bytes) + } + + pub fn bytes(&self) -> [u8; DefinitionID::LENGTH] { + self.bytes + } +} diff --git a/encoding/graph/definition/definition_key_generator.rs b/encoding/graph/definition/definition_key_generator.rs new file mode 100644 index 0000000000..ee592ac313 --- /dev/null +++ b/encoding/graph/definition/definition_key_generator.rs @@ -0,0 +1,35 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use resource::constants::encoding::DefinitionIDAtomicUInt; +use storage::snapshot::WritableSnapshot; +use crate::error::EncodingError; +use crate::graph::definition::definition_key::{DefinitionID, DefinitionKey}; +use crate::Keyable; +use crate::layout::prefix::Prefix; + +pub struct DefinitionKeyGenerator { + // TODO: implement full allocator with recycling + next_struct_id: DefinitionIDAtomicUInt, +} + +impl DefinitionKeyGenerator { + + pub fn create_struct( + &self, + snapshot: &mut Snapshot, + ) -> Result, EncodingError> { + + // TODO: implement + let id = 0; + + let definition_key = DefinitionKey::build(Prefix::DefinitionStruct, DefinitionID::build(id)); + snapshot.put(definition_key.as_storage_key().into_owned_array()); + Ok(definition_key) + } +} + + diff --git a/encoding/graph/definition/mod.rs b/encoding/graph/definition/mod.rs new file mode 100644 index 0000000000..0744c10267 --- /dev/null +++ b/encoding/graph/definition/mod.rs @@ -0,0 +1,9 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +pub mod r#struct; +pub mod definition_key; +pub mod definition_key_generator; \ No newline at end of file diff --git a/encoding/graph/definition/struct.rs b/encoding/graph/definition/struct.rs new file mode 100644 index 0000000000..1bd67e8a1a --- /dev/null +++ b/encoding/graph/definition/struct.rs @@ -0,0 +1,27 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use resource::constants::encoding::StructFieldIDUInt; + +use crate::value::value_type::ValueType; + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct StructDefinition { + name: String, + fields: Vec, + field_names: HashMap, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct StructDefinitionField { + optional: bool, + // value_type: ValueType, // TODO + index: StructFieldIDUInt, +} diff --git a/encoding/graph/mod.rs b/encoding/graph/mod.rs index 0ff8f0393f..5f25369390 100644 --- a/encoding/graph/mod.rs +++ b/encoding/graph/mod.rs @@ -10,6 +10,7 @@ use crate::{graph::type_::vertex::TypeID, Prefixed}; pub mod thing; pub mod type_; +pub mod definition; pub trait Typed<'a, const INLINE_SIZE: usize>: Prefixed<'a, INLINE_SIZE> { const RANGE_TYPE_ID: Range = Self::RANGE_PREFIX.end..Self::RANGE_PREFIX.end + TypeID::LENGTH; diff --git a/encoding/graph/thing/edge.rs b/encoding/graph/thing/edge.rs index 07555ee276..771a24bf0a 100644 --- a/encoding/graph/thing/edge.rs +++ b/encoding/graph/thing/edge.rs @@ -26,6 +26,7 @@ use crate::{ value::value_type::ValueType, AsBytes, EncodingKeyspace, Keyable, Prefixed, }; +use crate::value::value_type::ValueTypeCategory; /// /// [has][object][Attribute8|Attribute17] @@ -69,14 +70,14 @@ impl<'a> ThingEdgeHas<'a> { pub fn prefix_from_object_to_type( from: ObjectVertex, - to_value_type: ValueType, + to_value_type_category: ValueTypeCategory, to_type: TypeVertex, ) -> StorageKey<'static, { ThingEdgeHas::LENGTH_PREFIX_FROM_OBJECT_TO_TYPE }> { let mut bytes = ByteArray::zeros(Self::LENGTH_PREFIX_FROM_OBJECT_TO_TYPE); bytes.bytes_mut()[Self::RANGE_PREFIX].copy_from_slice(&Self::PREFIX.prefix_id().bytes()); bytes.bytes_mut()[Self::range_from()].copy_from_slice(from.bytes().bytes()); let to_prefix = AttributeVertex::build_prefix_type( - AttributeVertex::value_type_to_prefix_type(to_value_type), + AttributeVertex::value_type_category_to_prefix_type(to_value_type_category), to_type.type_id_(), ); let to_type_range = Self::range_from().end..Self::range_from().end + to_prefix.length(); @@ -266,8 +267,7 @@ impl<'a> ThingEdgeHasReverse<'a> { fn from_length(&self) -> usize { let byte = self.bytes.bytes()[Self::INDEX_FROM_PREFIX]; let prefix = PrefixID::new([byte]); - let value_type = AttributeVertex::prefix_type_to_value_type(Prefix::from_prefix_id(prefix)); - let id_encoding_length = AttributeID::value_type_encoding_length(value_type); + let id_encoding_length = AttributeVertex::prefix_type_to_value_id_encoding_length(Prefix::from_prefix_id(prefix)); AttributeVertex::LENGTH_PREFIX_TYPE + id_encoding_length } diff --git a/encoding/graph/thing/vertex_attribute.rs b/encoding/graph/thing/vertex_attribute.rs index d12f85f18a..78df7cf47d 100644 --- a/encoding/graph/thing/vertex_attribute.rs +++ b/encoding/graph/thing/vertex_attribute.rs @@ -26,6 +26,7 @@ use crate::{ }, AsBytes, EncodingKeyspace, Keyable, Prefixed, }; +use crate::value::value_type::ValueTypeCategory; #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] pub struct AttributeVertex<'a> { @@ -43,23 +44,23 @@ impl<'a> AttributeVertex<'a> { AttributeVertex { bytes } } - pub fn build(value_type: ValueType, type_id: TypeID, attribute_id: AttributeID) -> Self { + pub fn build(value_type_category: ValueTypeCategory, type_id: TypeID, attribute_id: AttributeID) -> Self { let mut bytes = ByteArray::zeros(Self::LENGTH_PREFIX_TYPE + attribute_id.length()); bytes.bytes_mut()[Self::RANGE_PREFIX] - .copy_from_slice(&Self::value_type_to_prefix_type(value_type).prefix_id().bytes()); + .copy_from_slice(&Self::value_type_category_to_prefix_type(value_type_category).prefix_id().bytes()); bytes.bytes_mut()[Self::RANGE_TYPE_ID].copy_from_slice(&type_id.bytes()); bytes.bytes_mut()[Self::range_for_attribute_id(attribute_id.length())].copy_from_slice(attribute_id.bytes()); Self { bytes: Bytes::Array(bytes) } } pub(crate) fn build_prefix_type_attribute_id( - value_type: ValueType, + value_type_category: ValueTypeCategory, type_id: TypeID, attribute_id_part: &[u8], ) -> StorageKey<'static, BUFFER_KEY_INLINE> { let mut bytes = ByteArray::zeros(Self::LENGTH_PREFIX_TYPE + attribute_id_part.len()); bytes.bytes_mut()[Self::RANGE_PREFIX] - .copy_from_slice(&Self::value_type_to_prefix_type(value_type).prefix_id().bytes()); + .copy_from_slice(&Self::value_type_category_to_prefix_type(value_type_category).prefix_id().bytes()); bytes.bytes_mut()[Self::RANGE_TYPE_ID].copy_from_slice(&type_id.bytes()); bytes.bytes_mut()[Self::range_for_attribute_id(attribute_id_part.len())].copy_from_slice(attribute_id_part); StorageKey::new_owned(Self::KEYSPACE, bytes) @@ -82,23 +83,25 @@ impl<'a> AttributeVertex<'a> { && &storage_key.bytes()[Self::RANGE_PREFIX] <= &Prefix::ATTRIBUTE_MAX.prefix_id().bytes()) } - pub fn value_type_to_prefix_type(value_type: ValueType) -> Prefix { - match value_type { - ValueType::Boolean => Prefix::VertexAttributeBoolean, - ValueType::Long => Prefix::VertexAttributeLong, - ValueType::Double => Prefix::VertexAttributeDouble, - ValueType::String => Prefix::VertexAttributeString, - ValueType::DateTime => Prefix::VertexAttributeDateTime, + pub fn value_type_category_to_prefix_type(value_type_category: ValueTypeCategory) -> Prefix { + match value_type_category { + ValueTypeCategory::Boolean => Prefix::VertexAttributeBoolean, + ValueTypeCategory::Long => Prefix::VertexAttributeLong, + ValueTypeCategory::Double => Prefix::VertexAttributeDouble, + ValueTypeCategory::String => Prefix::VertexAttributeString, + ValueTypeCategory::DateTime => Prefix::VertexAttributeDateTime, + ValueTypeCategory::Struct => Prefix::VertexAttributeStruct, } } - pub fn prefix_type_to_value_type(prefix: Prefix) -> ValueType { + pub fn prefix_type_to_value_id_encoding_length(prefix: Prefix) -> usize { match prefix { - Prefix::VertexAttributeBoolean => ValueType::Boolean, - Prefix::VertexAttributeLong => ValueType::Long, - Prefix::VertexAttributeDouble => ValueType::Double, - Prefix::VertexAttributeString => ValueType::String, - Prefix::VertexAttributeDateTime => ValueType::DateTime, + Prefix::VertexAttributeBoolean => BooleanAttributeID::LENGTH, + Prefix::VertexAttributeLong => LongAttributeID::LENGTH, + Prefix::VertexAttributeDouble => DoubleAttributeID::LENGTH, + Prefix::VertexAttributeString => StringAttributeID::LENGTH, + Prefix::VertexAttributeDateTime => DateTimeAttributeID::LENGTH, + Prefix::VertexAttributeStruct => StructAttributeID::LENGTH, _ => unreachable!("Unrecognised attribute vertex prefix type"), } } @@ -109,19 +112,20 @@ impl<'a> AttributeVertex<'a> { StorageKey::new(Self::KEYSPACE, Bytes::Array(array)) } - pub fn value_type(&self) -> ValueType { + pub fn value_type_category(&self) -> ValueTypeCategory { match self.prefix() { - Prefix::VertexAttributeBoolean => ValueType::Boolean, - Prefix::VertexAttributeLong => ValueType::Long, - Prefix::VertexAttributeDouble => ValueType::Double, - Prefix::VertexAttributeDateTime => ValueType::DateTime, - Prefix::VertexAttributeString => ValueType::String, + Prefix::VertexAttributeBoolean => ValueTypeCategory::Boolean, + Prefix::VertexAttributeLong => ValueTypeCategory::Long, + Prefix::VertexAttributeDouble => ValueTypeCategory::Double, + Prefix::VertexAttributeDateTime => ValueTypeCategory::DateTime, + Prefix::VertexAttributeString => ValueTypeCategory::String, + Prefix::VertexAttributeStruct => ValueTypeCategory::Struct, _ => unreachable!("Unexpected prefix."), } } pub fn attribute_id(&self) -> AttributeID { - AttributeID::new(self.value_type(), &self.bytes.bytes()[self.range_of_attribute_id()]) + AttributeID::new(self.value_type_category(), &self.bytes.bytes()[self.range_of_attribute_id()]) } fn range_of_attribute_id(&self) -> Range { @@ -194,16 +198,18 @@ pub enum AttributeID { Double(DoubleAttributeID), DateTime(DateTimeAttributeID), String(StringAttributeID), + Struct(StructAttributeID), } impl AttributeID { - pub fn new(value_type: ValueType, bytes: &[u8]) -> Self { - match value_type { - ValueType::Boolean => Self::Boolean(BooleanAttributeID::new(bytes.try_into().unwrap())), - ValueType::Long => Self::Long(LongAttributeID::new(bytes.try_into().unwrap())), - ValueType::Double => Self::Double(DoubleAttributeID::new(bytes.try_into().unwrap())), - ValueType::DateTime => Self::DateTime(DateTimeAttributeID::new(bytes.try_into().unwrap())), - ValueType::String => Self::String(StringAttributeID::new(bytes.try_into().unwrap())), + pub fn new(value_type_category: ValueTypeCategory, bytes: &[u8]) -> Self { + match value_type_category { + ValueTypeCategory::Boolean => Self::Boolean(BooleanAttributeID::new(bytes.try_into().unwrap())), + ValueTypeCategory::Long => Self::Long(LongAttributeID::new(bytes.try_into().unwrap())), + ValueTypeCategory::Double => Self::Double(DoubleAttributeID::new(bytes.try_into().unwrap())), + ValueTypeCategory::DateTime => Self::DateTime(DateTimeAttributeID::new(bytes.try_into().unwrap())), + ValueTypeCategory::String => Self::String(StringAttributeID::new(bytes.try_into().unwrap())), + ValueTypeCategory::Struct => Self::Struct(StructAttributeID::new(bytes.try_into().unwrap())) } } @@ -215,16 +221,20 @@ impl AttributeID { ValueType::Double => Self::Double(DoubleAttributeID::build(value.encode_double())), ValueType::DateTime => Self::DateTime(DateTimeAttributeID::build(value.encode_date_time())), ValueType::String => Self::String(StringAttributeID::build_inline_id(value.encode_string::<256>())), + ValueType::Struct(_) => { + todo!() + } } } - pub fn value_type_encoding_length(value_type: ValueType) -> usize { - match value_type { - ValueType::Boolean => BooleanAttributeID::LENGTH, - ValueType::Long => LongAttributeID::LENGTH, - ValueType::Double => DoubleAttributeID::LENGTH, - ValueType::DateTime => DateTimeAttributeID::LENGTH, - ValueType::String => StringAttributeID::LENGTH, + pub fn value_type_encoding_length(value_type_category: ValueTypeCategory) -> usize { + match value_type_category { + ValueTypeCategory::Boolean => BooleanAttributeID::LENGTH, + ValueTypeCategory::Long => LongAttributeID::LENGTH, + ValueTypeCategory::Double => DoubleAttributeID::LENGTH, + ValueTypeCategory::DateTime => DateTimeAttributeID::LENGTH, + ValueTypeCategory::String => StringAttributeID::LENGTH, + ValueTypeCategory::Struct => StructAttributeID::LENGTH, } } @@ -239,6 +249,10 @@ impl AttributeID { ValueType::Double => DoubleAttributeID::is_inlineable(), ValueType::DateTime => DateTimeAttributeID::is_inlineable(), ValueType::String => StringAttributeID::is_inlineable(value.encode_string::<256>()), + ValueType::Struct(definition_key) => { + todo!() + // StructAttributeID::is_inlineable() + } } } @@ -249,6 +263,7 @@ impl AttributeID { AttributeID::Double(double_id) => double_id.bytes_ref(), AttributeID::DateTime(date_time_id) => date_time_id.bytes_ref(), AttributeID::String(string_id) => string_id.bytes_ref(), + AttributeID::Struct(struct_id) => struct_id.bytes_ref(), } } @@ -259,6 +274,7 @@ impl AttributeID { AttributeID::Double(_) => DoubleAttributeID::LENGTH, AttributeID::DateTime(_) => DateTimeAttributeID::LENGTH, AttributeID::String(_) => StringAttributeID::LENGTH, + AttributeID::Struct(_) => StructAttributeID::LENGTH, } } @@ -562,8 +578,8 @@ impl StringAttributeID { { debug_assert!(!Self::is_inlineable(string.as_reference())); let prefix_search = KeyRange::new_within( - AttributeVertex::build_prefix_type_attribute_id(ValueType::String, type_id, &id_without_tail), - AttributeVertex::value_type_to_prefix_type(ValueType::String).fixed_width_keys(), + AttributeVertex::build_prefix_type_attribute_id(ValueTypeCategory::String, type_id, &id_without_tail), + AttributeVertex::value_type_category_to_prefix_type(ValueTypeCategory::String).fixed_width_keys(), ); let mut iter = snapshot.iterate_range(prefix_search); let mut next = iter.next().transpose()?; @@ -647,3 +663,20 @@ impl StringAttributeID { &self.bytes } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct StructAttributeID { + bytes: [u8; Self::LENGTH], +} + +impl StructAttributeID { + const LENGTH: usize = AttributeIDLength::LONG_LENGTH; + + pub fn new(bytes: [u8; Self::LENGTH]) -> Self { + Self { bytes } + } + + pub(crate) fn bytes_ref(&self) -> &[u8] { + &self.bytes + } +} \ No newline at end of file diff --git a/encoding/graph/thing/vertex_generator.rs b/encoding/graph/thing/vertex_generator.rs index e9ef1b8fb2..ac36c0468e 100644 --- a/encoding/graph/thing/vertex_generator.rs +++ b/encoding/graph/thing/vertex_generator.rs @@ -37,6 +37,7 @@ use crate::{ }, AsBytes, Keyable, Prefixed, }; +use crate::value::value_type::ValueTypeCategory; pub struct ThingVertexGenerator { entity_ids: Box<[AtomicU64]>, @@ -157,7 +158,7 @@ impl ThingVertexGenerator { Snapshot: WritableSnapshot, { let boolean_attribute_id = self.create_attribute_id_boolean(value); - let vertex = AttributeVertex::build(ValueType::Boolean, type_id, AttributeID::Boolean(boolean_attribute_id)); + let vertex = AttributeVertex::build(ValueTypeCategory::Boolean, type_id, AttributeID::Boolean(boolean_attribute_id)); snapshot.put(vertex.as_storage_key().into_owned_array()); vertex } @@ -172,7 +173,7 @@ impl ThingVertexGenerator { Snapshot: WritableSnapshot, { let long_attribute_id = self.create_attribute_id_long(value); - let vertex = AttributeVertex::build(ValueType::Long, type_id, AttributeID::Long(long_attribute_id)); + let vertex = AttributeVertex::build(ValueTypeCategory::Long, type_id, AttributeID::Long(long_attribute_id)); snapshot.put(vertex.as_storage_key().into_owned_array()); vertex } @@ -187,7 +188,7 @@ impl ThingVertexGenerator { Snapshot: WritableSnapshot, { let double_attribute_id = self.create_attribute_id_double(value); - let vertex = AttributeVertex::build(ValueType::Double, type_id, AttributeID::Double(double_attribute_id)); + let vertex = AttributeVertex::build(ValueTypeCategory::Double, type_id, AttributeID::Double(double_attribute_id)); snapshot.put(vertex.as_storage_key().into_owned_array()); vertex } @@ -203,7 +204,7 @@ impl ThingVertexGenerator { { let date_time_attribute_id = self.create_attribute_id_date_time(value); let vertex = - AttributeVertex::build(ValueType::DateTime, type_id, AttributeID::DateTime(date_time_attribute_id)); + AttributeVertex::build(ValueTypeCategory::DateTime, type_id, AttributeID::DateTime(date_time_attribute_id)); snapshot.put(vertex.as_storage_key().into_owned_array()); vertex } @@ -244,7 +245,7 @@ impl ThingVertexGenerator { Snapshot: WritableSnapshot, { let string_attribute_id = self.create_attribute_id_string(type_id, value.as_reference(), snapshot)?; - let vertex = AttributeVertex::build(ValueType::String, type_id, AttributeID::String(string_attribute_id)); + let vertex = AttributeVertex::build(ValueTypeCategory::String, type_id, AttributeID::String(string_attribute_id)); snapshot.put_val(vertex.as_storage_key().into_owned_array(), ByteArray::from(value.bytes())); Ok(vertex) } diff --git a/encoding/graph/type_/vertex.rs b/encoding/graph/type_/vertex.rs index 5da135979f..adc8ab2432 100644 --- a/encoding/graph/type_/vertex.rs +++ b/encoding/graph/type_/vertex.rs @@ -137,6 +137,7 @@ pub struct TypeID { bytes: [u8; TypeID::LENGTH], } +// TODO: this type should move into constants.rs, similarly to DefinitionIDUInt pub type TypeIDUInt = u16; impl TypeID { diff --git a/encoding/layout/prefix.rs b/encoding/layout/prefix.rs index e8439c81e0..258c275aa0 100644 --- a/encoding/layout/prefix.rs +++ b/encoding/layout/prefix.rs @@ -40,7 +40,9 @@ impl PrefixID { | Prefix::EdgeRelatesReverse | Prefix::PropertyTypeVertex | Prefix::IndexLabelToType - | Prefix::PropertyTypeEdge => EncodingKeyspace::Schema, + | Prefix::PropertyTypeEdge + | Prefix::DefinitionStruct + | Prefix::IndexLabelToDefinitionStruct => EncodingKeyspace::Schema, Prefix::VertexEntity => todo!(), Prefix::VertexRelation => todo!(), Prefix::VertexAttributeBoolean => todo!(), @@ -48,6 +50,7 @@ impl PrefixID { Prefix::VertexAttributeDouble => todo!(), Prefix::VertexAttributeString => todo!(), Prefix::VertexAttributeDateTime => todo!(), + Prefix::VertexAttributeStruct => todo!(), Prefix::EdgeHas => todo!(), Prefix::EdgeHasReverse => todo!(), Prefix::EdgeRolePlayer => todo!(), @@ -74,6 +77,7 @@ pub enum Prefix { VertexAttributeDouble, VertexAttributeDateTime, VertexAttributeString, + VertexAttributeStruct, _VertexAttributeLast, // marker to indicate reserved range for attribute types EdgeSub, @@ -91,12 +95,18 @@ pub enum Prefix { EdgeRolePlayerReverse, EdgeRolePlayerIndex, + DefinitionStruct, + // DefinitionFunction + PropertyTypeVertex, PropertyTypeEdge, + // PropertyDefinitionFunction, PropertyObjectVertex, IndexLabelToType, + IndexLabelToDefinitionStruct + // IndexLabelToDefinitionFunction } macro_rules! prefix_functions { @@ -153,11 +163,11 @@ impl Prefix { prefix_functions!( // Reserved: 0-9 - VertexEntityType => [10], true; VertexRelationType => [11], true; VertexAttributeType => [12], true; VertexRoleType => [15], true; + DefinitionStruct => [20], true; VertexEntity => [30], true; VertexRelation => [31], true; @@ -168,6 +178,7 @@ impl Prefix { VertexAttributeDouble => [52], true; VertexAttributeString => [53], true; VertexAttributeDateTime => [54], true; + VertexAttributeStruct => [90], true; _VertexAttributeLast => [99], true; EdgeSub => [100], true; @@ -189,8 +200,8 @@ impl Prefix { PropertyTypeEdge => [162], false; PropertyObjectVertex => [163], false; - IndexLabelToType => [182], false - + IndexLabelToType => [182], false; + IndexLabelToDefinitionStruct => [183], false // Reserved: 200-255 ); } diff --git a/encoding/value/mod.rs b/encoding/value/mod.rs index d3d9d62258..4eda4590a7 100644 --- a/encoding/value/mod.rs +++ b/encoding/value/mod.rs @@ -6,6 +6,9 @@ use bytes::{byte_array::ByteArray, byte_reference::ByteReference}; use resource::constants::snapshot::BUFFER_VALUE_INLINE; +use storage::snapshot::ReadableSnapshot; +use crate::graph::definition::r#struct::StructDefinition; +use crate::value::struct_bytes::StructBytes; use self::{ boolean_bytes::BooleanBytes, date_time_bytes::DateTimeBytes, double_bytes::DoubleBytes, long_bytes::LongBytes, @@ -19,6 +22,7 @@ pub mod label; pub mod long_bytes; pub mod string_bytes; pub mod value_type; +pub mod struct_bytes; pub fn encode_value_u64(count: u64) -> ByteArray { // LE is normally platform-native @@ -42,4 +46,6 @@ pub trait ValueEncodable: Clone { fn encode_date_time(&self) -> DateTimeBytes; fn encode_string(&self) -> StringBytes; + + fn encode_struct(&self) -> StructBytes; } diff --git a/encoding/value/struct_bytes.rs b/encoding/value/struct_bytes.rs new file mode 100644 index 0000000000..5768e579b5 --- /dev/null +++ b/encoding/value/struct_bytes.rs @@ -0,0 +1,51 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::fmt; + +use bytes::byte_reference::ByteReference; +use bytes::Bytes; + +use crate::AsBytes; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct StructBytes<'a, const INLINE_LENGTH: usize> { + bytes: Bytes<'a, INLINE_LENGTH>, +} + +impl<'a, const INLINE_LENGTH: usize> StructBytes<'a, INLINE_LENGTH> { + pub fn new(value: Bytes<'a, INLINE_LENGTH>) -> Self { + StructBytes { bytes: value } + } + + pub fn length(&self) -> usize { + self.bytes.length() + } + + pub fn as_reference(&'a self) -> StructBytes<'a, INLINE_LENGTH> { + StructBytes { bytes: Bytes::Reference(self.bytes.as_reference()) } + } + + pub fn into_owned(self) -> StructBytes<'static, INLINE_LENGTH> { + StructBytes { bytes: self.bytes.into_owned() } + } +} + +impl<'a, const INLINE_LENGTH: usize> AsBytes<'a, INLINE_LENGTH> for StructBytes<'a, INLINE_LENGTH> { + fn bytes(&'a self) -> ByteReference<'a> { + self.bytes.as_reference() + } + + fn into_bytes(self) -> Bytes<'a, INLINE_LENGTH> { + self.bytes + } +} + +impl<'a, const INLINE_LENGTH: usize> fmt::Display for StructBytes<'a, INLINE_LENGTH> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "bytes(len={})={:?}", self.length(), self.bytes()) + } +} diff --git a/encoding/value/value_type.rs b/encoding/value/value_type.rs index 764d560762..889f313442 100644 --- a/encoding/value/value_type.rs +++ b/encoding/value/value_type.rs @@ -4,28 +4,16 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::layout::infix::InfixID; +use std::ops::Range; -// A tiny struct will always be more efficient owning its own data and being Copy -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ValueTypeID { - bytes: [u8; ValueTypeID::LENGTH], -} - -impl ValueTypeID { - const LENGTH: usize = 1; +use bytes::byte_array::ByteArray; +use bytes::Bytes; - pub const fn new(bytes: [u8; ValueTypeID::LENGTH]) -> Self { - ValueTypeID { bytes } - } +use crate::AsBytes; +use crate::graph::definition::definition_key::DefinitionKey; - pub fn bytes(&self) -> [u8; InfixID::LENGTH] { - self.bytes - } -} - -// TODO: how do we handle user-created compound structs? -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +/// We can support Prefix::ATTRIBUTE_MAX - Prefix::ATTRIBUTE_MIN different built-in value types +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ValueType { Boolean, Long, @@ -37,38 +25,112 @@ pub enum ValueType { Duration, // Naming: 'interval'? */ String, + + Struct(DefinitionKey<'static>), } -macro_rules! value_type_functions { - ($( - $name:ident => $bytes:tt - ),+ $(,)?) => { - pub const fn value_type_id(&self) -> ValueTypeID { - let bytes = match self { - $( - Self::$name => &$bytes, - )+ - }; - ValueTypeID::new(*bytes) +impl ValueType { + pub fn category(&self) -> ValueTypeCategory { + match self { + ValueType::Boolean => ValueTypeCategory::Boolean, + ValueType::Long => ValueTypeCategory::Long, + ValueType::Double => ValueTypeCategory::Double, + ValueType::DateTime => ValueTypeCategory::DateTime, + ValueType::String => ValueTypeCategory::String, + ValueType::Struct(_) => ValueTypeCategory::Struct, } + } - pub fn from_value_type_id(value_type_id: ValueTypeID) -> Self { - match value_type_id.bytes() { - $( - $bytes => Self::$name, - )+ - _ => unreachable!(), + fn from_category_and_tail(category: ValueTypeCategory, tail: [u8; ValueTypeBytes::TAIL_LENGTH]) -> Self { + match category { + ValueTypeCategory::Boolean => Self::Boolean, + ValueTypeCategory::Long => Self::Long, + ValueTypeCategory::Double => Self::Double, + ValueTypeCategory::DateTime => Self::DateTime, + ValueTypeCategory::String => Self::String, + ValueTypeCategory::Struct => { + let definition_key = DefinitionKey::new(Bytes::Array(ByteArray::copy(&tail))); + Self::Struct(definition_key) } - } - }; + } + } } -impl ValueType { - value_type_functions!( - Boolean => [0], - Long => [1], - Double => [2], - String => [3], - DateTime => [4], - ); +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ValueTypeCategory { + Boolean, + Long, + Double, + DateTime, + String, + Struct, +} + +impl ValueTypeCategory { + + fn to_bytes(&self) -> [u8; ValueTypeBytes::CATEGORY_LENGTH] { + match self { + Self::Boolean => [0], + Self::Long => [1], + Self::Double => [2], + Self::DateTime => [3], + Self::String => [4], + Self::Struct => [40] + } + } + + fn from_bytes(bytes: [u8; ValueTypeBytes::CATEGORY_LENGTH]) -> Self { + let category = match bytes { + [0] => ValueTypeCategory::Boolean, + [1] => ValueTypeCategory::Long, + [2] => ValueTypeCategory::Double, + [3] => ValueTypeCategory::DateTime, + [4] => ValueTypeCategory::String, + [40] => ValueTypeCategory::Struct, + _ => panic!("Unrecognised value type category byte: {:?}", bytes), + }; + debug_assert_eq!(bytes, category.to_bytes()); + category + } +} + +#[derive(Debug, Copy, Clone)] +pub struct ValueTypeBytes { + bytes: [u8; Self::LENGTH], +} + +impl ValueTypeBytes { + const CATEGORY_LENGTH: usize = 1; + const TAIL_LENGTH: usize = DefinitionKey::LENGTH; + const LENGTH: usize = Self::CATEGORY_LENGTH + Self::TAIL_LENGTH; + const RANGE_CATEGORY: Range = 0..Self::CATEGORY_LENGTH; + const RANGE_TAIL: Range = Self::RANGE_CATEGORY.end..Self::RANGE_CATEGORY.end + Self::TAIL_LENGTH; + + pub fn new(bytes: [u8; Self::LENGTH]) -> Self { + Self { bytes } + } + + pub fn build(value_type: &ValueType) -> Self { + let mut array = [0; Self::LENGTH]; + array[Self::RANGE_CATEGORY].copy_from_slice(&value_type.category().to_bytes()); + match value_type { + ValueType::Struct(definition_key) => { + array[Self::RANGE_TAIL].copy_from_slice(&definition_key.bytes().bytes()); + } + _ => {} + } + + Self { bytes: array } + } + + pub fn to_value_type(&self) -> ValueType { + ValueType::from_category_and_tail( + ValueTypeCategory::from_bytes(self.bytes[Self::RANGE_CATEGORY].try_into().unwrap()), + self.bytes[Self::RANGE_TAIL].try_into().unwrap() + ) + } + + pub fn into_bytes(self) -> [u8; Self::LENGTH] { + self.bytes + } } diff --git a/resource/constants.rs b/resource/constants.rs index 7e9a0fadea..fe5ec6b2ce 100644 --- a/resource/constants.rs +++ b/resource/constants.rs @@ -18,7 +18,14 @@ pub mod storage { } pub mod encoding { + use std::sync::atomic::AtomicU32; + pub const LABEL_NAME_STRING_INLINE: usize = 64; pub const LABEL_SCOPE_STRING_INLINE: usize = 64; pub const LABEL_SCOPED_NAME_STRING_INLINE: usize = LABEL_NAME_STRING_INLINE + LABEL_SCOPE_STRING_INLINE; + + pub type DefinitionIDUInt = u32; + pub type DefinitionIDAtomicUInt = AtomicU32; + + pub type StructFieldIDUInt = u16; }