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; }