| // In-memory structures of records in the LSM tree. |
| |
| use crate::lsm_tree::OrdLowerBound; |
| use crate::lsm_tree::{Item, ItemRef}; |
| use serde::{Deserialize, Serialize}; |
| use std::cmp::{max, min}; |
| use std::ops::Range; |
| |
| #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] |
| pub struct ExtentKey { |
| pub attribute_id: u64, |
| pub range: std::ops::Range<u64>, |
| } |
| |
| impl ExtentKey { |
| pub fn new(attribute_id: u64, range: std::ops::Range<u64>) -> Self { |
| ExtentKey { attribute_id, range } |
| } |
| |
| pub fn overlap(&self, other: &ExtentKey) -> Option<Range<u64>> { |
| if self.attribute_id != other.attribute_id |
| || self.range.end <= other.range.start |
| || self.range.start >= other.range.end |
| { |
| None |
| } else { |
| Some(max(self.range.start, other.range.start)..min(self.range.end, other.range.end)) |
| } |
| } |
| |
| pub fn lower_bound(&self) -> Self { |
| ExtentKey { attribute_id: self.attribute_id, range: 0..self.range.start + 1 } |
| } |
| } |
| |
| // The normal comparison uses the end of the range before the start of the range. This makes |
| // searching for records easier because it's easy to find K.. (where K is the key you are searching |
| // for), but it's harder (with BTreeMap at least) to find the key immediately preceding K that then |
| // extends to the end i.e. K-1.. OrdLowerBound orders by the start of an extent. |
| impl Ord for ExtentKey { |
| fn cmp(&self, other: &ExtentKey) -> std::cmp::Ordering { |
| // The comparison uses the end of the range so that we can more easily do queries. |
| // TODO(csuter): Expand on this. |
| self.attribute_id |
| .cmp(&other.attribute_id) |
| .then(self.range.end.cmp(&other.range.end)) |
| .then(self.range.start.cmp(&other.range.start)) |
| } |
| } |
| |
| impl PartialOrd for ExtentKey { |
| fn partial_cmp(&self, other: &ExtentKey) -> Option<std::cmp::Ordering> { |
| Some(self.cmp(other)) |
| } |
| } |
| |
| impl OrdLowerBound for ExtentKey { |
| // Orders by the start of the range rather than the end. |
| fn cmp_lower_bound(&self, other: &ExtentKey) -> std::cmp::Ordering { |
| return self |
| .attribute_id |
| .cmp(&other.attribute_id) |
| .then(self.range.start.cmp(&other.range.start)); |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy, Serialize, Deserialize)] |
| pub struct ExtentValue { |
| pub device_offset: u64, |
| } |
| |
| #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] |
| pub enum ObjectKeyData { |
| Object, |
| Attribute { attribute_id: u64 }, |
| Extent(ExtentKey), |
| Child { name: String }, // Should this be a string or array of bytes? |
| } |
| |
| #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, Clone)] |
| pub struct ObjectKey { |
| pub object_id: u64, |
| pub data: ObjectKeyData, |
| } |
| |
| impl ObjectKey { |
| pub fn object(object_id: u64) -> Self { |
| Self { object_id: object_id, data: ObjectKeyData::Object } |
| } |
| |
| pub fn attribute(object_id: u64, attribute_id: u64) -> Self { |
| Self { object_id, data: ObjectKeyData::Attribute { attribute_id } } |
| } |
| |
| pub fn extent(object_id: u64, extent_key: ExtentKey) -> Self { |
| Self { object_id, data: ObjectKeyData::Extent(extent_key) } |
| } |
| |
| pub fn child(object_id: u64, name: &str) -> Self { |
| Self { object_id, data: ObjectKeyData::Child { name: name.to_owned() } } |
| } |
| |
| // Returns the lower bound for a key. |
| pub fn lower_bound(&self) -> Self { |
| if let Self { object_id, data: ObjectKeyData::Extent(e) } = self { |
| Self::extent(*object_id, e.lower_bound()) |
| } else { |
| self.clone() |
| } |
| } |
| } |
| |
| impl OrdLowerBound for ObjectKey { |
| fn cmp_lower_bound(&self, other: &ObjectKey) -> std::cmp::Ordering { |
| self.object_id.cmp(&other.object_id).then_with(|| match (&self.data, &other.data) { |
| (ObjectKeyData::Extent(ref left_extent), ObjectKeyData::Extent(ref right_extent)) => { |
| left_extent.cmp_lower_bound(right_extent) |
| } |
| _ => self.data.cmp(&other.data), |
| }) |
| } |
| } |
| |
| #[derive(Clone, Debug, Serialize, Deserialize)] |
| pub enum ObjectType { |
| File, |
| Directory, |
| Volume, |
| } |
| |
| #[derive(Clone, Debug, Serialize, Deserialize)] |
| pub enum ObjectValue { |
| Object { object_type: ObjectType }, |
| Attribute { size: u64 }, |
| Extent(ExtentValue), |
| Child { object_id: u64, object_type: ObjectType }, |
| } |
| |
| impl ObjectValue { |
| pub fn object(object_type: ObjectType) -> ObjectValue { |
| ObjectValue::Object { object_type } |
| } |
| pub fn attribute(size: u64) -> ObjectValue { |
| ObjectValue::Attribute { size } |
| } |
| pub fn extent(device_offset: u64) -> ObjectValue { |
| ObjectValue::Extent(ExtentValue { device_offset }) |
| } |
| |
| pub fn child(object_id: u64, object_type: ObjectType) -> ObjectValue { |
| ObjectValue::Child { object_id, object_type } |
| } |
| } |
| |
| pub type ObjectItem = Item<ObjectKey, ObjectValue>; |
| |
| pub fn decode_extent( |
| item: ItemRef<'_, ObjectKey, ObjectValue>, |
| ) -> Option<(u64, &ExtentKey, &ExtentValue)> { |
| match item { |
| ItemRef { |
| key: ObjectKey { object_id, data: ObjectKeyData::Extent(ref extent_key) }, |
| value: ObjectValue::Extent(ref extent_value), |
| } => Some((*object_id, extent_key, extent_value)), |
| _ => None, |
| } |
| } |
| |
| // TODO: need validation after deserialization. |