diff --git a/Cargo.lock b/Cargo.lock index 72103b4a..3a835f76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2745,11 +2745,21 @@ dependencies = [ "jmap_proto", "percent-encoding", "rkyv 0.8.10", + "store", "tokio", "trc", "utils", ] +[[package]] +name = "gxhash" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a197c9b654827513cf53842c5c6d3da2b4b35a785f8e0eff78bdf8e445aba1bb" +dependencies = [ + "rustversion", +] + [[package]] name = "h2" version = "0.3.26" @@ -6985,6 +6995,7 @@ dependencies = [ "flate2", "foundationdb", "futures", + "gxhash", "lru-cache", "lz4_flex", "memchr", diff --git a/crates/common/src/listener/acme/directory.rs b/crates/common/src/listener/acme/directory.rs index fb9cc519..ab37b0ec 100644 --- a/crates/common/src/listener/acme/directory.rs +++ b/crates/common/src/listener/acme/directory.rs @@ -11,8 +11,8 @@ use reqwest::{Method, Response}; use ring::rand::SystemRandom; use ring::signature::{ECDSA_P256_SHA256_FIXED_SIGNING, EcdsaKeyPair, EcdsaSigningAlgorithm}; use serde::Deserialize; -use store::Serialize; use store::write::Archiver; +use store::{Serialize, SerializedVersion}; use trc::AddContext; use trc::event::conv::AssertSuccess; @@ -210,6 +210,12 @@ pub struct SerializedCert { pub private_key: Vec, } +impl SerializedVersion for SerializedCert { + fn serialize_version() -> u8 { + 0 + } +} + #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Directory { diff --git a/crates/common/src/listener/acme/resolver.rs b/crates/common/src/listener/acme/resolver.rs index ce43d6c0..e5ff5230 100644 --- a/crates/common/src/listener/acme/resolver.rs +++ b/crates/common/src/listener/acme/resolver.rs @@ -13,7 +13,10 @@ use rustls::{ sign::CertifiedKey, }; use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; -use store::{dispatch::lookup::KeyValue, write::Archive}; +use store::{ + dispatch::lookup::KeyValue, + write::{AlignedBytes, Archive}, +}; use trc::AcmeEvent; use crate::{KV_ACME, Server}; @@ -48,7 +51,7 @@ impl Server { pub(crate) async fn build_acme_certificate(&self, domain: &str) -> Option> { match self .in_memory_store() - .key_get::(KeyValue::<()>::build_key(KV_ACME, domain)) + .key_get::>(KeyValue::<()>::build_key(KV_ACME, domain)) .await { Ok(Some(cert_)) => match cert_.unarchive::() { diff --git a/crates/common/src/storage/folder.rs b/crates/common/src/storage/folder.rs index dd798525..90e150ae 100644 --- a/crates/common/src/storage/folder.rs +++ b/crates/common/src/storage/folder.rs @@ -7,8 +7,9 @@ use ahash::AHashMap; use jmap_proto::types::{collection::Collection, property::Property}; use store::{ - Deserialize, IndexKey, IterateParams, SerializeInfallible, U32_LEN, ValueKey, - write::{Archive, ValueClass, key::DeserializeBigEndian}, + Deserialize, IndexKey, IterateParams, SerializeInfallible, SerializedVersion, U32_LEN, + ValueKey, + write::{AlignedBytes, Archive, ValueClass, key::DeserializeBigEndian}, }; use trc::AddContext; use utils::topological::{TopologicalSort, TopologicalSortIterator}; @@ -48,7 +49,7 @@ impl Server { collection: Collection, ) -> trc::Result where - T: rkyv::Archive, + T: rkyv::Archive + SerializedVersion, T::Archived: FolderHierarchy + for<'a> rkyv::bytecheck::CheckBytes< rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, @@ -79,7 +80,7 @@ impl Server { ), |key, value| { let document_id = key.deserialize_be_u32(key.len() - U32_LEN)?; - let archive = ::deserialize(value)?; + let archive = as Deserialize>::deserialize(value)?; let folder = archive.unarchive::()?; let parent_id = folder.parent_id(); diff --git a/crates/common/src/storage/index.rs b/crates/common/src/storage/index.rs index 5f1e63e8..6485f115 100644 --- a/crates/common/src/storage/index.rs +++ b/crates/common/src/storage/index.rs @@ -7,10 +7,10 @@ use jmap_proto::types::{property::Property, value::AclGrant}; use std::{borrow::Cow, collections::HashSet, fmt::Debug}; use store::{ - Serialize, SerializeInfallible, + Serialize, SerializeInfallible, SerializedVersion, write::{ - Archiver, BatchBuilder, BitmapClass, BlobOp, DirectoryClass, IntoOperations, Operation, - assert::HashedValue, + Archive, Archiver, BatchBuilder, BitmapClass, BlobOp, DirectoryClass, IntoOperations, + Operation, }, }; use utils::BlobHash; @@ -35,6 +35,7 @@ pub trait IndexableObject: Sync + Send { pub trait IndexableAndSerializableObject: IndexableObject + + SerializedVersion + rkyv::Archive + for<'a> rkyv::Serialize< rkyv::api::high::HighSerializer< @@ -49,7 +50,7 @@ pub trait IndexableAndSerializableObject: #[derive(Debug)] pub struct ObjectIndexBuilder { tenant_id: Option, - current: Option>, + current: Option>, changes: Option, } @@ -68,7 +69,7 @@ impl ObjectIndexBuilder) -> Self { + pub fn with_current(mut self, current: Archive) -> Self { self.current = Some(current); self } @@ -78,7 +79,7 @@ impl ObjectIndexBuilder>) -> Self { + pub fn with_current_opt(mut self, current: Option>) -> Self { self.current = current; self } @@ -91,7 +92,7 @@ impl ObjectIndexBuilder Option<&HashedValue> { + pub fn current(&self) -> Option<&Archive> { self.current.as_ref() } diff --git a/crates/common/src/storage/tag.rs b/crates/common/src/storage/tag.rs index ea35b64d..5ec8a729 100644 --- a/crates/common/src/storage/tag.rs +++ b/crates/common/src/storage/tag.rs @@ -8,8 +8,8 @@ use std::slice::IterMut; use jmap_proto::types::property::Property; use store::{ - Serialize, - write::{Archiver, BatchBuilder, MaybeDynamicId, TagValue, ValueClass, assert::HashedValue}, + Serialize, SerializedVersion, + write::{Archive, Archiver, BatchBuilder, MaybeDynamicId, TagValue, ValueClass}, }; pub struct TagManager< @@ -27,7 +27,7 @@ pub struct TagManager< >, >, > { - current: HashedValue>, + current: Archive>, added: Vec, removed: Vec, last: LastTag, @@ -45,6 +45,7 @@ impl< + Clone + Sync + Send + + SerializedVersion + rkyv::Archive + for<'a> rkyv::Serialize< rkyv::api::high::HighSerializer< @@ -55,7 +56,7 @@ impl< >, > TagManager { - pub fn new(current: HashedValue>) -> Self { + pub fn new(current: Archive>) -> Self { Self { current, added: Vec::new(), diff --git a/crates/dav/src/common/lock.rs b/crates/dav/src/common/lock.rs index d14867b9..2421690f 100644 --- a/crates/dav/src/common/lock.rs +++ b/crates/dav/src/common/lock.rs @@ -20,8 +20,8 @@ use jmap_proto::types::collection::Collection; use jmap_proto::types::property::Property; use store::dispatch::lookup::KeyValue; use store::write::serialize::rkyv_deserialize; -use store::write::{Archive, Archiver, now}; -use store::{Serialize, U32_LEN}; +use store::write::{AlignedBytes, Archive, Archiver, now}; +use store::{Serialize, SerializedVersion, U32_LEN}; use trc::AddContext; use super::ETag; @@ -86,7 +86,7 @@ impl LockRequestHandler for Server { let mut lock_data = if let Some(lock_data) = self .in_memory_store() - .key_get::(resource_hash.as_slice()) + .key_get::>(resource_hash.as_slice()) .await .caused_by(trc::location!())? { @@ -388,7 +388,7 @@ impl LockRequestHandler for Server { resource_state.document_id.filter(|&id| id != u32::MAX) { if let Some(archive) = self - .get_property::( + .get_property::>( resource_state.account_id, resource_state.collection, document_id, @@ -454,7 +454,7 @@ struct LockCache<'x> { enum LockArchive<'x> { Unarchived(&'x ArchivedLockData), - Archived(Archive), + Archived(Archive), } #[derive(Default)] @@ -550,7 +550,7 @@ impl<'x> LockCaches<'x> { ) -> trc::Result { if let Some(lock_archive) = server .in_memory_store() - .key_get::(resource_state.lock_key().as_slice()) + .key_get::>(resource_state.lock_key().as_slice()) .await .caused_by(trc::location!())? { @@ -582,6 +582,12 @@ struct LockItem { owner_dav: Option, } +impl SerializedVersion for LockData { + fn serialize_version() -> u8 { + 0 + } +} + impl LockItem { pub fn to_active_lock(&self, href: String) -> ActiveLock { ActiveLock::new( diff --git a/crates/dav/src/common/mod.rs b/crates/dav/src/common/mod.rs index 1d8fa399..efa0ed7a 100644 --- a/crates/dav/src/common/mod.rs +++ b/crates/dav/src/common/mod.rs @@ -4,10 +4,11 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use std::fmt::Write; - use jmap_proto::types::property::Property; -use store::write::{Archive, BatchBuilder, MaybeDynamicValue, Operation, ValueClass, ValueOp}; +use store::{ + U32_LEN, + write::{Archive, BatchBuilder, MaybeDynamicValue, Operation, ValueClass, ValueOp}, +}; pub mod acl; pub mod lock; @@ -21,19 +22,9 @@ pub trait ExtractETag { fn etag(&self) -> Option; } -impl> ETag for T { +impl ETag for Archive { fn etag(&self) -> String { - let mut hasher = store::blake3::Hasher::new(); - hasher.update(self.as_ref()); - let hash = hasher.finalize(); - - let mut etag = String::with_capacity(2 + hash.as_bytes().len() * 2); - etag.push('"'); - for byte in hash.as_bytes() { - let _ = write!(&mut etag, "{:02x}", byte); - } - etag.push('"'); - etag + format!("\"{}\"", self.hash) } } @@ -46,7 +37,9 @@ impl ExtractETag for BatchBuilder { class: ValueClass::Property(p_id), op: ValueOp::Set(MaybeDynamicValue::Static(value)), } if *p_id == p_value => { - return Archive::try_unpack_bytes(value).map(|bytes| bytes.etag()); + return value + .get(value.len() - U32_LEN..) + .map(|v| format!("\"{}\"", u32::from_be_bytes(v.try_into().unwrap()))); } _ => {} } diff --git a/crates/dav/src/file/copy_move.rs b/crates/dav/src/file/copy_move.rs index 6a562ab0..45e75f95 100644 --- a/crates/dav/src/file/copy_move.rs +++ b/crates/dav/src/file/copy_move.rs @@ -16,7 +16,7 @@ use jmap_proto::types::{ }; use store::{ ahash::AHashMap, - write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder, now}, + write::{log::ChangeLogBuilder, now, AlignedBytes, Archive, BatchBuilder}, }; use trc::AddContext; use utils::map::bitmap::Bitmap; @@ -313,7 +313,7 @@ async fn move_container( return Err(DavError::Code(StatusCode::BAD_GATEWAY)); } let node = server - .get_property::>( + .get_property::>( from_account_id, Collection::FileNode, from_document_id, @@ -408,7 +408,7 @@ async fn copy_container( let now = now() as i64; for (document_id, _) in copy_files.into_iter() { let node_ = server - .get_property::>( + .get_property::>( from_account_id, Collection::FileNode, document_id, @@ -527,7 +527,7 @@ async fn overwrite_and_delete_item( // dest_node is the current file at the destination let dest_node = server - .get_property::>( + .get_property::>( to_account_id, Collection::FileNode, to_document_id, @@ -541,7 +541,7 @@ async fn overwrite_and_delete_item( // source_node is the file to be copied let source_node_ = server - .get_property::>( + .get_property::>( from_account_id, Collection::FileNode, from_document_id, @@ -599,7 +599,7 @@ async fn overwrite_item( // dest_node is the current file at the destination let dest_node = server - .get_property::>( + .get_property::>( to_account_id, Collection::FileNode, to_document_id, @@ -613,7 +613,7 @@ async fn overwrite_item( // source_node is the file to be copied let mut source_node = server - .get_property::( + .get_property::>( from_account_id, Collection::FileNode, from_document_id, @@ -659,7 +659,7 @@ async fn move_item( let parent_id = destination.document_id.map(|id| id + 1).unwrap_or(0); let node = server - .get_property::>( + .get_property::>( from_account_id, Collection::FileNode, from_document_id, @@ -722,7 +722,7 @@ async fn copy_item( let parent_id = destination.document_id.map(|id| id + 1).unwrap_or(0); let mut node = server - .get_property::( + .get_property::>( from_account_id, Collection::FileNode, from_document_id, @@ -755,7 +755,7 @@ async fn rename_item( let from_document_id = from_resource.resource.document_id; let node = server - .get_property::>( + .get_property::>( from_account_id, Collection::FileNode, from_document_id, diff --git a/crates/dav/src/file/delete.rs b/crates/dav/src/file/delete.rs index c5f15714..1c3d9125 100644 --- a/crates/dav/src/file/delete.rs +++ b/crates/dav/src/file/delete.rs @@ -12,7 +12,7 @@ use hyper::StatusCode; use jmap_proto::types::{ acl::Acl, collection::Collection, property::Property, type_state::DataType, }; -use store::write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}; +use store::write::{log::ChangeLogBuilder, AlignedBytes, Archive, BatchBuilder}; use trc::AddContext; use utils::map::bitmap::Bitmap; @@ -105,7 +105,7 @@ impl FileDeleteRequestHandler for Server { let mut changes = ChangeLogBuilder::new(); for document_id in sorted_ids { if let Some(node) = self - .get_property::>( + .get_property::>( account_id, Collection::FileNode, document_id, diff --git a/crates/dav/src/file/get.rs b/crates/dav/src/file/get.rs index 2819ca97..53315e02 100644 --- a/crates/dav/src/file/get.rs +++ b/crates/dav/src/file/get.rs @@ -10,7 +10,7 @@ use groupware::file::{FileNode, hierarchy::FileHierarchy}; use http_proto::HttpResponse; use hyper::StatusCode; use jmap_proto::types::{acl::Acl, collection::Collection, property::Property}; -use store::write::Archive; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use crate::{ @@ -50,7 +50,7 @@ impl FileGetRequestHandler for Server { // Fetch node let node_ = self - .get_property::( + .get_property::>( account_id, Collection::FileNode, resource.resource, diff --git a/crates/dav/src/file/mod.rs b/crates/dav/src/file/mod.rs index c8674a43..74f0d51e 100644 --- a/crates/dav/src/file/mod.rs +++ b/crates/dav/src/file/mod.rs @@ -11,10 +11,7 @@ use groupware::file::FileNode; use hyper::StatusCode; use jmap_proto::types::{collection::Collection, type_state::DataType}; use store::write::{ - BatchBuilder, - assert::HashedValue, - log::{Changes, LogInsert}, - now, + log::{Changes, LogInsert}, now, Archive, BatchBuilder }; use crate::{ @@ -137,7 +134,7 @@ impl FromFileItem for FileItemId { pub(crate) async fn update_file_node( server: &Server, access_token: &AccessToken, - node: HashedValue, + node: Archive, mut new_node: FileNode, account_id: u32, document_id: u32, @@ -213,7 +210,7 @@ pub(crate) async fn insert_file_node( pub(crate) async fn delete_file_node( server: &Server, access_token: &AccessToken, - node: HashedValue, + node: Archive, account_id: u32, document_id: u32, ) -> trc::Result<()> { diff --git a/crates/dav/src/file/proppatch.rs b/crates/dav/src/file/proppatch.rs index 6d129ec4..7c3e5992 100644 --- a/crates/dav/src/file/proppatch.rs +++ b/crates/dav/src/file/proppatch.rs @@ -17,7 +17,7 @@ use groupware::file::{FileNode, hierarchy::FileHierarchy}; use http_proto::HttpResponse; use hyper::StatusCode; use jmap_proto::types::{acl::Acl, collection::Collection, property::Property}; -use store::write::{Archive, assert::HashedValue}; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use crate::{ @@ -68,7 +68,7 @@ impl FilePropPatchRequestHandler for Server { // Fetch node let node_ = self - .get_property::>( + .get_property::>( account_id, Collection::FileNode, resource.resource, @@ -99,7 +99,7 @@ impl FilePropPatchRequestHandler for Server { account_id, collection: resource.collection, document_id: resource.resource.into(), - etag: node_.inner.etag().clone().into(), + etag: node_.etag().into(), lock_token: None, path: resource_.resource.unwrap(), }], @@ -162,7 +162,7 @@ impl FilePropPatchRequestHandler for Server { .await .caused_by(trc::location!())? } else { - node_.inner.etag().into() + node_.etag().into() }; Ok(HttpResponse::new(StatusCode::MULTI_STATUS) diff --git a/crates/dav/src/file/update.rs b/crates/dav/src/file/update.rs index 19c6f5b6..6aed5591 100644 --- a/crates/dav/src/file/update.rs +++ b/crates/dav/src/file/update.rs @@ -13,8 +13,7 @@ use jmap_proto::types::{ acl::Acl, collection::Collection, property::Property, type_state::DataType, }; use store::write::{ - Archive, BatchBuilder, - assert::HashedValue, + AlignedBytes, Archive, BatchBuilder, log::{Changes, LogInsert}, now, }; @@ -66,7 +65,7 @@ impl FileUpdateRequestHandler for Server { if let Some(document_id) = files.files.by_name(resource_name).map(|r| r.document_id) { // Update let node_archive_ = self - .get_property::>( + .get_property::>( account_id, Collection::FileNode, document_id, @@ -98,7 +97,7 @@ impl FileUpdateRequestHandler for Server { account_id, collection: resource.collection, document_id: Some(document_id), - etag: node_archive_.inner.etag().into(), + etag: node_archive_.etag().into(), lock_token: None, path: resource_name, }], @@ -193,7 +192,7 @@ impl FileUpdateRequestHandler for Server { // Verify that parent is a collection if parent_id > 0 && self - .get_property::( + .get_property::>( account_id, Collection::FileNode, parent_id - 1, diff --git a/crates/directory/src/backend/internal/manage.rs b/crates/directory/src/backend/internal/manage.rs index 7fe32e2a..8c366260 100644 --- a/crates/directory/src/backend/internal/manage.rs +++ b/crates/directory/src/backend/internal/manage.rs @@ -10,7 +10,7 @@ use store::{ Deserialize, IterateParams, Serialize, Store, U32_LEN, ValueKey, write::{ AssignedIds, BatchBuilder, DirectoryClass, MaybeDynamicId, MaybeDynamicValue, - SerializeWithId, ValueClass, assert::HashedValue, key::DeserializeBigEndian, + SerializeWithId, ValueClass, assert::LegacyHashedValue, key::DeserializeBigEndian, }, }; use trc::AddContext; @@ -790,7 +790,7 @@ impl ManageDirectory for Store { // Fetch principal let mut principal = self - .get_value::>(ValueKey::from(ValueClass::Directory( + .get_value::>(ValueKey::from(ValueClass::Directory( DirectoryClass::Principal(principal_id), ))) .await diff --git a/crates/email/src/identity/mod.rs b/crates/email/src/identity/mod.rs index 905024e5..4e6bf0c7 100644 --- a/crates/email/src/identity/mod.rs +++ b/crates/email/src/identity/mod.rs @@ -4,6 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +use store::SerializedVersion; + #[derive( rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, )] @@ -21,3 +23,9 @@ pub struct EmailAddress { pub name: Option, pub email: String, } + +impl SerializedVersion for Identity { + fn serialize_version() -> u8 { + 0 + } +} diff --git a/crates/email/src/mailbox/destroy.rs b/crates/email/src/mailbox/destroy.rs index a8069e57..2820f476 100644 --- a/crates/email/src/mailbox/destroy.rs +++ b/crates/email/src/mailbox/destroy.rs @@ -16,7 +16,7 @@ use store::{ Serialize, SerializeInfallible, query::Filter, roaring::RoaringBitmap, - write::{Archive, Archiver, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}, + write::{AlignedBytes, Archive, Archiver, BatchBuilder, log::ChangeLogBuilder}, }; use trc::AddContext; @@ -101,7 +101,7 @@ impl MailboxDestroy for Server { // otherwise delete it. let mut destroy_ids = RoaringBitmap::new(); for (message_id, mailbox_ids) in self - .get_properties::, _>( + .get_properties::, _>( account_id, Collection::Email, &message_ids, @@ -191,7 +191,7 @@ impl MailboxDestroy for Server { // Obtain mailbox if let Some(mailbox_) = self - .get_property::>( + .get_property::>( account_id, Collection::Mailbox, document_id, diff --git a/crates/email/src/mailbox/mod.rs b/crates/email/src/mailbox/mod.rs index 9d224d90..69b5e5ea 100644 --- a/crates/email/src/mailbox/mod.rs +++ b/crates/email/src/mailbox/mod.rs @@ -6,6 +6,7 @@ use common::config::jmap::settings::SpecialUse; use jmap_proto::types::value::AclGrant; +use store::SerializedVersion; pub mod destroy; pub mod index; @@ -38,6 +39,18 @@ pub struct UidMailbox { pub uid: u32, } +impl SerializedVersion for Mailbox { + fn serialize_version() -> u8 { + 0 + } +} + +impl SerializedVersion for UidMailbox { + fn serialize_version() -> u8 { + 0 + } +} + #[derive(Debug)] pub struct ExpandPath<'x> { pub path: Vec<&'x str>, diff --git a/crates/email/src/message/bayes.rs b/crates/email/src/message/bayes.rs index 4682b0ed..4e7f912b 100644 --- a/crates/email/src/message/bayes.rs +++ b/crates/email/src/message/bayes.rs @@ -12,7 +12,7 @@ use mail_parser::Message; use spam_filter::{ SpamFilterInput, analysis::init::SpamFilterInit, modules::bayes::BayesClassifier, }; -use store::write::{Archive, TaskQueueClass}; +use store::write::{AlignedBytes, Archive, TaskQueueClass}; use trc::StoreEvent; use utils::BlobHash; @@ -59,7 +59,7 @@ impl EmailBayesTrain for Server { learn_spam: bool, ) -> trc::Result { let metadata = self - .get_property::( + .get_property::>( account_id, Collection::Email, document_id, diff --git a/crates/email/src/message/copy.rs b/crates/email/src/message/copy.rs index a6cfbfef..5ff46e64 100644 --- a/crates/email/src/message/copy.rs +++ b/crates/email/src/message/copy.rs @@ -16,7 +16,8 @@ use mail_parser::parsers::fields::thread::thread_name; use store::{ BlobClass, Serialize, SerializeInfallible, write::{ - Archive, Archiver, BatchBuilder, MaybeDynamicId, TagValue, TaskQueueClass, ValueClass, + AlignedBytes, Archive, Archiver, BatchBuilder, MaybeDynamicId, TagValue, TaskQueueClass, + ValueClass, log::{Changes, LogInsert}, }, }; @@ -59,7 +60,7 @@ impl EmailCopy for Server { // Obtain metadata let account_id = resource_token.account_id; let mut metadata = if let Some(metadata) = self - .get_property::( + .get_property::>( from_account_id, Collection::Email, from_message_id, diff --git a/crates/email/src/message/crypto.rs b/crates/email/src/message/crypto.rs index 285279fc..1bcf10b7 100644 --- a/crates/email/src/message/crypto.rs +++ b/crates/email/src/message/crypto.rs @@ -26,7 +26,7 @@ use rasn_cms::{ }; use rsa::{Pkcs1v15Encrypt, RsaPublicKey, pkcs1::DecodeRsaPublicKey}; use sequoia_openpgp as openpgp; -use store::{Deserialize, write::Archive}; +use store::{Deserialize, SerializedVersion, write::Archive}; const P: openpgp::policy::StandardPolicy<'static> = openpgp::policy::StandardPolicy::new(); @@ -84,6 +84,12 @@ pub struct EncryptionParams { pub certs: Vec>, } +impl SerializedVersion for EncryptionParams { + fn serialize_version() -> u8 { + 0 + } +} + #[derive( rkyv::Serialize, rkyv::Deserialize, diff --git a/crates/email/src/message/delete.rs b/crates/email/src/message/delete.rs index 7c7acd9b..9a5b8c9e 100644 --- a/crates/email/src/message/delete.rs +++ b/crates/email/src/message/delete.rs @@ -16,7 +16,7 @@ use store::{ ahash::AHashMap, roaring::RoaringBitmap, write::{ - Archive, BatchBuilder, BitmapClass, MaybeDynamicId, TagValue, ValueClass, + AlignedBytes, Archive, BatchBuilder, BitmapClass, MaybeDynamicId, TagValue, ValueClass, log::ChangeLogBuilder, }, }; @@ -64,7 +64,7 @@ impl EmailDeletion for Server { // Fetch mailboxes and threadIds let mut thread_ids: AHashMap = AHashMap::new(); for (document_id, mailboxes) in self - .get_properties::( + .get_properties::, _>( account_id, Collection::Email, &document_ids, @@ -445,7 +445,7 @@ impl EmailDeletion for Server { .core .storage .data - .get_value::(ValueKey { + .get_value::>(ValueKey { account_id, collection: Collection::Email.into(), document_id, @@ -474,7 +474,7 @@ impl EmailDeletion for Server { .core .storage .data - .get_value::(ValueKey { + .get_value::>(ValueKey { account_id, collection: Collection::Email.into(), document_id, diff --git a/crates/email/src/message/ingest.rs b/crates/email/src/message/ingest.rs index 78d6c026..7c888eef 100644 --- a/crates/email/src/message/ingest.rs +++ b/crates/email/src/message/ingest.rs @@ -37,8 +37,8 @@ use store::{ ahash::AHashSet, query::Filter, write::{ - Archive, AssignedIds, BatchBuilder, BitmapClass, MaybeDynamicId, MaybeDynamicValue, - SerializeWithId, TagValue, TaskQueueClass, ValueClass, + AlignedBytes, Archive, AssignedIds, BatchBuilder, BitmapClass, MaybeDynamicId, + MaybeDynamicValue, SerializeWithId, TagValue, TaskQueueClass, ValueClass, log::{ChangeLogBuilder, Changes, LogInsert}, now, }, @@ -389,7 +389,12 @@ impl EmailIngest for Server { }; if do_encrypt && !message.is_encrypted() { if let Some(encrypt_params_) = self - .get_property::(account_id, Collection::Principal, 0, Property::Parameters) + .get_property::>( + account_id, + Collection::Principal, + 0, + Property::Parameters, + ) .await .caused_by(trc::location!())? { diff --git a/crates/email/src/message/metadata.rs b/crates/email/src/message/metadata.rs index fe7995d8..ce2fa7ec 100644 --- a/crates/email/src/message/metadata.rs +++ b/crates/email/src/message/metadata.rs @@ -18,6 +18,7 @@ use rkyv::{ string::ArchivedString, vec::ArchivedVec, }; +use store::SerializedVersion; use utils::BlobHash; #[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug)] @@ -31,6 +32,12 @@ pub struct MessageMetadata { pub raw_headers: Vec, } +impl SerializedVersion for MessageMetadata { + fn serialize_version() -> u8 { + 0 + } +} + #[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug)] pub struct MessageMetadataContents { pub html_body: Vec, diff --git a/crates/email/src/push/mod.rs b/crates/email/src/push/mod.rs index 8142e383..4b06fb2b 100644 --- a/crates/email/src/push/mod.rs +++ b/crates/email/src/push/mod.rs @@ -5,6 +5,7 @@ */ use jmap_proto::types::type_state::DataType; +use store::SerializedVersion; use utils::map::bitmap::Bitmap; #[derive( @@ -25,3 +26,9 @@ pub struct Keys { pub p256dh: Vec, pub auth: Vec, } + +impl SerializedVersion for PushSubscription { + fn serialize_version() -> u8 { + 0 + } +} diff --git a/crates/email/src/sieve/activate.rs b/crates/email/src/sieve/activate.rs index f13b27c9..47334dbd 100644 --- a/crates/email/src/sieve/activate.rs +++ b/crates/email/src/sieve/activate.rs @@ -9,7 +9,7 @@ use jmap_proto::types::{collection::Collection, property::Property}; use store::{ SerializeInfallible, query::Filter, - write::{Archive, BatchBuilder, assert::HashedValue}, + write::{AlignedBytes, Archive, BatchBuilder}, }; use trc::AddContext; @@ -59,7 +59,7 @@ impl SieveScriptActivate for Server { // Deactivate scripts for document_id in active_ids { if let Some(sieve) = self - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, @@ -88,7 +88,7 @@ impl SieveScriptActivate for Server { // Activate script if let Some(document_id) = activate_id { if let Some(sieve) = self - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/email/src/sieve/delete.rs b/crates/email/src/sieve/delete.rs index 5928bdf1..b0817d39 100644 --- a/crates/email/src/sieve/delete.rs +++ b/crates/email/src/sieve/delete.rs @@ -6,7 +6,7 @@ use common::{Server, auth::ResourceToken, storage::index::ObjectIndexBuilder}; use jmap_proto::types::{collection::Collection, property::Property}; -use store::write::{Archive, BatchBuilder, assert::HashedValue}; +use store::write::{AlignedBytes, Archive, BatchBuilder}; use trc::AddContext; use super::SieveScript; @@ -30,7 +30,7 @@ impl SieveScriptDelete for Server { // Fetch record let account_id = resource_token.account_id; let obj_ = self - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/email/src/sieve/ingest.rs b/crates/email/src/sieve/ingest.rs index 2c89775a..f153c18b 100644 --- a/crates/email/src/sieve/ingest.rs +++ b/crates/email/src/sieve/ingest.rs @@ -24,7 +24,7 @@ use store::{ Deserialize, Serialize, SerializeInfallible, ahash::{AHashSet, RandomState}, query::Filter, - write::{Archive, Archiver, BatchBuilder, BlobOp, LegacyBincode, assert::HashedValue, now}, + write::{AlignedBytes, Archive, Archiver, BatchBuilder, BlobOp, LegacyBincode, now}, }; use trc::{AddContext, SieveEvent}; use utils::config::utils::ParseValue; @@ -575,7 +575,7 @@ impl SieveScriptIngest for Server { let (script, script_name) = self.sieve_script_compile(account_id, document_id).await?; let seen_ids = if let Some(seen_ids_archive_) = self - .get_property::( + .get_property::>( account_id, Collection::SieveScript, document_id, @@ -636,7 +636,7 @@ impl SieveScriptIngest for Server { ) -> trc::Result<(Sieve, String)> { // Obtain script object let script_object = self - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, @@ -652,7 +652,6 @@ impl SieveScriptIngest for Server { // Obtain the sieve script length let unarchived_script = script_object - .inner .unarchive::() .caused_by(trc::location!())?; let script_offset = u32::from(unarchived_script.size) as usize; diff --git a/crates/email/src/sieve/mod.rs b/crates/email/src/sieve/mod.rs index 70ff22de..b4c6ed72 100644 --- a/crates/email/src/sieve/mod.rs +++ b/crates/email/src/sieve/mod.rs @@ -8,7 +8,7 @@ use std::{collections::HashSet, sync::Arc}; use rkyv::collections::swiss_table::ArchivedHashSet; use sieve::Sieve; -use store::{ahash::RandomState, blake3, write::now}; +use store::{SerializedVersion, ahash::RandomState, blake3, write::now}; use utils::BlobHash; pub mod activate; @@ -37,6 +37,12 @@ pub struct SeenIds { pub has_changes: bool, } +impl SerializedVersion for SeenIdHash { + fn serialize_version() -> u8 { + 0 + } +} + #[derive( rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, )] @@ -49,6 +55,12 @@ pub struct SieveScript { pub vacation_response: Option, } +impl SerializedVersion for SieveScript { + fn serialize_version() -> u8 { + 0 + } +} + #[derive( rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, )] diff --git a/crates/email/src/submission/mod.rs b/crates/email/src/submission/mod.rs index 047916aa..a0f1599c 100644 --- a/crates/email/src/submission/mod.rs +++ b/crates/email/src/submission/mod.rs @@ -4,6 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +use store::SerializedVersion; use utils::map::vec_map::VecMap; pub mod index; @@ -22,6 +23,12 @@ pub struct EmailSubmission { pub delivery_status: VecMap, } +impl SerializedVersion for EmailSubmission { + fn serialize_version() -> u8 { + 0 + } +} + #[derive( rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, )] diff --git a/crates/groupware/Cargo.toml b/crates/groupware/Cargo.toml index ff73ec42..2bc6a7dc 100644 --- a/crates/groupware/Cargo.toml +++ b/crates/groupware/Cargo.toml @@ -6,6 +6,7 @@ resolver = "2" [dependencies] utils = { path = "../utils" } +store = { path = "../store" } common = { path = "../common" } jmap_proto = { path = "../jmap-proto" } trc = { path = "../trc" } diff --git a/crates/groupware/src/file/mod.rs b/crates/groupware/src/file/mod.rs index 36eed095..a56ab92a 100644 --- a/crates/groupware/src/file/mod.rs +++ b/crates/groupware/src/file/mod.rs @@ -9,6 +9,7 @@ pub mod index; use dav_proto::schema::request::DeadProperty; use jmap_proto::types::value::AclGrant; +use store::SerializedVersion; use utils::BlobHash; #[derive( @@ -36,3 +37,9 @@ pub struct FileProperties { pub media_type: Option, pub executable: bool, } + +impl SerializedVersion for FileNode { + fn serialize_version() -> u8 { + 0 + } +} diff --git a/crates/http/src/auth/oauth/auth.rs b/crates/http/src/auth/oauth/auth.rs index 2bf84b9a..268e0db1 100644 --- a/crates/http/src/auth/oauth/auth.rs +++ b/crates/http/src/auth/oauth/auth.rs @@ -16,16 +16,19 @@ use common::{ use serde::Deserialize; use serde_json::json; use std::future::Future; -use store::rand::{ - Rng, - distr::{Alphanumeric, StandardUniform}, - rng, -}; use store::{ Serialize, dispatch::lookup::KeyValue, write::{Archive, Archiver}, }; +use store::{ + rand::{ + Rng, + distr::{Alphanumeric, StandardUniform}, + rng, + }, + write::AlignedBytes, +}; use trc::AddContext; use crate::auth::oauth::OAuthStatus; @@ -148,7 +151,10 @@ impl OAuthApiHandler for Server { .core .storage .lookup - .key_get::(KeyValue::<()>::build_key(KV_OAUTH, code.as_bytes())) + .key_get::>(KeyValue::<()>::build_key( + KV_OAUTH, + code.as_bytes(), + )) .await? { let oauth = auth_code_ diff --git a/crates/http/src/auth/oauth/mod.rs b/crates/http/src/auth/oauth/mod.rs index a9d676a4..c439cb03 100644 --- a/crates/http/src/auth/oauth/mod.rs +++ b/crates/http/src/auth/oauth/mod.rs @@ -7,6 +7,7 @@ use http_proto::{HttpRequest, request::fetch_body}; use hyper::header::CONTENT_TYPE; use serde::{Deserialize, Serialize}; +use store::SerializedVersion; use utils::map::vec_map::VecMap; pub mod auth; @@ -55,6 +56,12 @@ pub struct OAuthCode { pub params: String, } +impl SerializedVersion for OAuthCode { + fn serialize_version() -> u8 { + 0 + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct DeviceAuthGet { code: Option, diff --git a/crates/http/src/auth/oauth/token.rs b/crates/http/src/auth/oauth/token.rs index 599c55a1..197b809c 100644 --- a/crates/http/src/auth/oauth/token.rs +++ b/crates/http/src/auth/oauth/token.rs @@ -13,7 +13,10 @@ use common::{ }; use hyper::StatusCode; use std::future::Future; -use store::{dispatch::lookup::KeyValue, write::Archive}; +use store::{ + dispatch::lookup::KeyValue, + write::{AlignedBytes, Archive}, +}; use trc::AddContext; use http_proto::*; @@ -76,7 +79,10 @@ impl TokenHandler for Server { .core .storage .lookup - .key_get::(KeyValue::<()>::build_key(KV_OAUTH, code.as_bytes())) + .key_get::>(KeyValue::<()>::build_key( + KV_OAUTH, + code.as_bytes(), + )) .await? { Some(auth_code_) => { @@ -145,7 +151,10 @@ impl TokenHandler for Server { .core .storage .lookup - .key_get::(KeyValue::<()>::build_key(KV_OAUTH, device_code.as_bytes())) + .key_get::>(KeyValue::<()>::build_key( + KV_OAUTH, + device_code.as_bytes(), + )) .await? { let oauth = auth_code_ diff --git a/crates/http/src/management/crypto.rs b/crates/http/src/management/crypto.rs index 4d059957..41381507 100644 --- a/crates/http/src/management/crypto.rs +++ b/crates/http/src/management/crypto.rs @@ -19,7 +19,7 @@ use mail_parser::MessageParser; use serde_json::json; use store::{ Deserialize, Serialize, - write::{Archive, Archiver, BatchBuilder}, + write::{AlignedBytes, Archive, Archiver, BatchBuilder}, }; use trc::AddContext; @@ -39,7 +39,7 @@ pub trait CryptoHandler: Sync + Send { impl CryptoHandler for Server { async fn handle_crypto_get(&self, access_token: Arc) -> trc::Result { let ec = if let Some(params_) = self - .get_property::( + .get_property::>( access_token.primary_id(), Collection::Principal, 0, @@ -132,7 +132,7 @@ impl CryptoHandler for Server { .parse("Subject: test\r\ntest\r\n".as_bytes()) .unwrap() .encrypt( - ::deserialize(params.as_slice())? + as Deserialize>::deserialize(params.as_slice())? .unarchive::()?, ) .await diff --git a/crates/http/src/management/queue.rs b/crates/http/src/management/queue.rs index e935a710..c974b7a6 100644 --- a/crates/http/src/management/queue.rs +++ b/crates/http/src/management/queue.rs @@ -30,7 +30,9 @@ use smtp::{ }; use store::{ Deserialize, IterateParams, ValueKey, - write::{Archive, QueueClass, ReportEvent, ValueClass, key::DeserializeBigEndian, now}, + write::{ + AlignedBytes, Archive, QueueClass, ReportEvent, ValueClass, key::DeserializeBigEndian, now, + }, }; use trc::AddContext; use utils::url_params::UrlParams; @@ -713,7 +715,7 @@ async fn fetch_queued_messages( .iterate( IterateParams::new(from_key, to_key).ascending(), |key, value| { - let message_ = ::deserialize(value) + let message_ = as Deserialize>::deserialize(value) .add_context(|ctx| ctx.ctx(trc::Key::Key, key))?; let message = message_ .unarchive::() diff --git a/crates/http/src/management/stores.rs b/crates/http/src/management/stores.rs index a30e4d00..4d7bc2fd 100644 --- a/crates/http/src/management/stores.rs +++ b/crates/http/src/management/stores.rs @@ -23,7 +23,7 @@ use serde_json::json; use services::index::Indexer; use store::{ Serialize, rand, - write::{Archive, Archiver, BatchBuilder, ValueClass, assert::HashedValue}, + write::{AlignedBytes, Archive, Archiver, BatchBuilder, ValueClass}, }; use trc::AddContext; use utils::url_params::UrlParams; @@ -332,7 +332,7 @@ pub async fn reset_imap_uids(server: &Server, account_id: u32) -> trc::Result<(u .unwrap_or_default() { let mailbox = server - .get_property::>( + .get_property::>( account_id, Collection::Mailbox, mailbox_id, @@ -373,7 +373,7 @@ pub async fn reset_imap_uids(server: &Server, account_id: u32) -> trc::Result<(u .unwrap_or_default() { let uids = server - .get_property::>( + .get_property::>( account_id, Collection::Email, message_id, diff --git a/crates/imap/src/core/mailbox.rs b/crates/imap/src/core/mailbox.rs index b78c5f3d..582c27a5 100644 --- a/crates/imap/src/core/mailbox.rs +++ b/crates/imap/src/core/mailbox.rs @@ -21,7 +21,7 @@ use parking_lot::Mutex; use std::sync::{Arc, atomic::Ordering}; use store::{ query::log::{Change, Query}, - write::Archive, + write::{AlignedBytes, Archive}, }; use trc::AddContext; use utils::topological::TopologicalSort; @@ -161,7 +161,7 @@ impl SessionData { for (mailbox_id, mailbox_) in self .server - .get_properties::( + .get_properties::, _>( account_id, Collection::Mailbox, &mailbox_ids, @@ -644,7 +644,7 @@ impl SessionData { Ok(access_token.is_member(account_id) || self .server - .get_property::( + .get_property::>( account_id, Collection::Mailbox, document_id, diff --git a/crates/imap/src/core/message.rs b/crates/imap/src/core/message.rs index bdce4cdb..4e70eb54 100644 --- a/crates/imap/src/core/message.rs +++ b/crates/imap/src/core/message.rs @@ -11,7 +11,7 @@ use common::{NextMailboxState, listener::SessionStream}; use email::mailbox::UidMailbox; use imap_proto::protocol::{Sequence, expunge, select::Exists}; use jmap_proto::types::{collection::Collection, property::Property}; -use store::write::Archive; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use crate::core::ImapId; @@ -52,7 +52,7 @@ impl SessionData { let mut uid_map = BTreeMap::new(); for (message_id, uid_mailbox_) in self .server - .get_properties::( + .get_properties::, _>( mailbox.account_id, Collection::Email, &message_ids, @@ -227,7 +227,7 @@ impl SessionData { pub async fn get_uid_validity(&self, mailbox: &MailboxId) -> trc::Result { self.server - .get_property::( + .get_property::>( mailbox.account_id, Collection::Mailbox, mailbox.mailbox_id, diff --git a/crates/imap/src/op/acl.rs b/crates/imap/src/op/acl.rs index 18cc36fc..a32097e4 100644 --- a/crates/imap/src/op/acl.rs +++ b/crates/imap/src/op/acl.rs @@ -26,7 +26,7 @@ use jmap_proto::types::{ acl::Acl, collection::Collection, property::Property, state::StateChange, type_state::DataType, value::AclGrant, }; -use store::write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}; +use store::write::{AlignedBytes, Archive, BatchBuilder, log::ChangeLogBuilder}; use trc::AddContext; use utils::map::bitmap::Bitmap; @@ -435,11 +435,11 @@ impl SessionData { &self, arguments: &Arguments, validate: bool, - ) -> trc::Result<(MailboxId, HashedValue, Arc)> { + ) -> trc::Result<(MailboxId, Archive, Arc)> { if let Some(mailbox) = self.get_mailbox_by_name(&arguments.mailbox_name) { if let Some(values) = self .server - .get_property::>( + .get_property::>( mailbox.account_id, Collection::Mailbox, mailbox.mailbox_id, @@ -452,7 +452,6 @@ impl SessionData { if !validate || access_token.is_member(mailbox.account_id) || values - .inner .unarchive::() .caused_by(trc::location!())? .acls diff --git a/crates/imap/src/op/copy_move.rs b/crates/imap/src/op/copy_move.rs index ad271629..87821412 100644 --- a/crates/imap/src/op/copy_move.rs +++ b/crates/imap/src/op/copy_move.rs @@ -32,7 +32,7 @@ use jmap_proto::{ use store::{ SerializeInfallible, roaring::RoaringBitmap, - write::{Archive, BatchBuilder, ValueClass, assert::HashedValue, log::ChangeLogBuilder}, + write::{AlignedBytes, Archive, BatchBuilder, ValueClass, log::ChangeLogBuilder}, }; use super::ImapContext; @@ -474,7 +474,7 @@ impl SessionData { // Obtain mailbox tags if let (Some(mailboxes), Some(thread_id)) = ( self.server - .get_property::>( + .get_property::>( account_id, Collection::Email, id, diff --git a/crates/imap/src/op/expunge.rs b/crates/imap/src/op/expunge.rs index 7de29389..c6a565a1 100644 --- a/crates/imap/src/op/expunge.rs +++ b/crates/imap/src/op/expunge.rs @@ -25,7 +25,7 @@ use jmap_proto::types::{ use store::{ SerializeInfallible, roaring::RoaringBitmap, - write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}, + write::{AlignedBytes, Archive, BatchBuilder, log::ChangeLogBuilder}, }; use super::{ImapContext, ToModSeq}; @@ -194,7 +194,7 @@ impl SessionData { for (id, mailbox_ids) in self .server - .get_properties::, _>( + .get_properties::, _>( account_id, Collection::Email, deleted_ids, @@ -214,7 +214,7 @@ impl SessionData { // Remove deleted flag let (mut keywords, thread_id) = if let (Some(keywords), Some(thread_id)) = ( self.server - .get_property::>( + .get_property::>( account_id, Collection::Email, id, diff --git a/crates/imap/src/op/fetch.rs b/crates/imap/src/op/fetch.rs index fd4eb3b6..b9f3c536 100644 --- a/crates/imap/src/op/fetch.rs +++ b/crates/imap/src/op/fetch.rs @@ -44,7 +44,7 @@ use store::{ Serialize, SerializeInfallible, query::log::{Change, Query}, rkyv::rend::u16_le, - write::{Archive, Archiver, BatchBuilder, assert::HashedValue, serialize::rkyv_deserialize}, + write::{AlignedBytes, Archive, Archiver, BatchBuilder, serialize::rkyv_deserialize}, }; use super::{FromModSeq, ImapContext}; @@ -310,7 +310,7 @@ impl SessionData { // Obtain attributes and keywords let (metadata_, keywords_) = if let (Some(email), Some(keywords)) = ( self.server - .get_property::( + .get_property::>( account_id, Collection::Email, id, @@ -319,7 +319,7 @@ impl SessionData { .await .imap_ctx(&arguments.tag, trc::location!())?, self.server - .get_property::>( + .get_property::>( account_id, Collection::Email, id, @@ -344,7 +344,6 @@ impl SessionData { .unarchive::() .imap_ctx(&arguments.tag, trc::location!())?; let keywords = keywords_ - .inner .unarchive::>() .imap_ctx(&arguments.tag, trc::location!())?; @@ -553,8 +552,9 @@ impl SessionData { if set_seen_flag { set_seen_ids.push(( Id::from_parts(thread_id, id), - HashedValue { + Archive { hash: keywords_.hash, + version: keywords_.version, inner: rkyv_deserialize::<_, Vec>(keywords) .imap_ctx(&arguments.tag, trc::location!())?, }, diff --git a/crates/imap/src/op/rename.rs b/crates/imap/src/op/rename.rs index 0441fe7c..85894004 100644 --- a/crates/imap/src/op/rename.rs +++ b/crates/imap/src/op/rename.rs @@ -18,7 +18,7 @@ use imap_proto::{ use jmap_proto::types::{ acl::Acl, collection::Collection, property::Property, state::StateChange, type_state::DataType, }; -use store::write::{Archive, BatchBuilder, assert::HashedValue}; +use store::write::{AlignedBytes, Archive, BatchBuilder}; use trc::AddContext; use super::ImapContext; @@ -88,7 +88,7 @@ impl SessionData { // Obtain mailbox let mailbox = self .server - .get_property::>( + .get_property::>( params.account_id, Collection::Mailbox, mailbox_id, diff --git a/crates/imap/src/op/status.rs b/crates/imap/src/op/status.rs index d122fedb..e3695286 100644 --- a/crates/imap/src/op/status.rs +++ b/crates/imap/src/op/status.rs @@ -20,7 +20,10 @@ use imap_proto::{ receiver::Request, }; use jmap_proto::types::{collection::Collection, id::Id, keyword::Keyword, property::Property}; -use store::{Deserialize, U32_LEN, write::Archive}; +use store::{ + Deserialize, U32_LEN, + write::{AlignedBytes, Archive}, +}; use store::{ IndexKeyPrefix, IterateParams, ValueKey, roaring::RoaringBitmap, @@ -254,7 +257,7 @@ impl SessionData { .caused_by(trc::location!())? as u64, Status::UidValidity => u32::from( self.server - .get_property::( + .get_property::>( mailbox.account_id, Collection::Mailbox, mailbox.mailbox_id, diff --git a/crates/imap/src/op/store.rs b/crates/imap/src/op/store.rs index ed990e01..0e474c9d 100644 --- a/crates/imap/src/op/store.rs +++ b/crates/imap/src/op/store.rs @@ -33,7 +33,7 @@ use jmap_proto::types::{ use store::{ SerializeInfallible, query::log::{Change, Query}, - write::{Archive, BatchBuilder, ValueClass, assert::HashedValue, log::ChangeLogBuilder}, + write::{AlignedBytes, Archive, BatchBuilder, ValueClass, log::ChangeLogBuilder}, }; use trc::AddContext; @@ -207,7 +207,7 @@ impl SessionData { // Obtain current keywords let (mut keywords, thread_id) = if let (Some(keywords), Some(thread_id)) = ( self.server - .get_property::>( + .get_property::>( account_id, Collection::Email, *id, @@ -330,7 +330,7 @@ impl SessionData { if seen_changed { if let Some(mailboxes) = self .server - .get_property::( + .get_property::>( account_id, Collection::Email, *id, diff --git a/crates/imap/src/op/subscribe.rs b/crates/imap/src/op/subscribe.rs index caa5c62f..6ee13c86 100644 --- a/crates/imap/src/op/subscribe.rs +++ b/crates/imap/src/op/subscribe.rs @@ -16,7 +16,7 @@ use imap_proto::{Command, ResponseCode, StatusResponse, receiver::Request}; use jmap_proto::types::{ collection::Collection, property::Property, state::StateChange, type_state::DataType, }; -use store::write::{Archive, BatchBuilder, assert::HashedValue}; +use store::write::{AlignedBytes, Archive, BatchBuilder}; use super::ImapContext; @@ -96,7 +96,7 @@ impl SessionData { // Obtain mailbox let mailbox = self .server - .get_property::>( + .get_property::>( account_id, Collection::Mailbox, mailbox_id, diff --git a/crates/jmap-proto/src/types/keyword.rs b/crates/jmap-proto/src/types/keyword.rs index 0f5972e4..d9e2fd2f 100644 --- a/crates/jmap-proto/src/types/keyword.rs +++ b/crates/jmap-proto/src/types/keyword.rs @@ -7,7 +7,7 @@ use std::fmt::Display; use store::{ - Serialize, + Serialize, SerializedVersion, write::{MaybeDynamicId, TagValue}, }; @@ -68,6 +68,12 @@ pub enum Keyword { Other(String), } +impl SerializedVersion for Keyword { + fn serialize_version() -> u8 { + 0 + } +} + impl JsonObjectParser for Keyword { fn parse(parser: &mut Parser<'_>) -> trc::Result where diff --git a/crates/jmap/src/blob/get.rs b/crates/jmap/src/blob/get.rs index 42400569..6ae56b83 100644 --- a/crates/jmap/src/blob/get.rs +++ b/crates/jmap/src/blob/get.rs @@ -24,7 +24,7 @@ use jmap_proto::{ use mail_builder::encoders::base64::base64_encode; use sha1::{Digest, Sha1}; use sha2::{Sha256, Sha512}; -use store::{BlobClass, write::Archive}; +use store::{write::{AlignedBytes, Archive}, BlobClass}; use trc::AddContext; use utils::map::vec_map::VecMap; @@ -239,7 +239,7 @@ impl BlobOperations for Server { } if include_mailbox { if let Some(mailboxes) = self - .get_property::( + .get_property::>( req_account_id, Collection::Email, *document_id, diff --git a/crates/jmap/src/email/get.rs b/crates/jmap/src/email/get.rs index 21d6ee0a..a298a587 100644 --- a/crates/jmap/src/email/get.rs +++ b/crates/jmap/src/email/get.rs @@ -28,7 +28,10 @@ use jmap_proto::{ }, }; -use store::{BlobClass, write::Archive}; +use store::{ + BlobClass, + write::{AlignedBytes, Archive}, +}; use trc::{AddContext, StoreEvent}; use utils::BlobHash; @@ -159,7 +162,7 @@ impl EmailGet for Server { continue; } let metadata_ = match self - .get_property::( + .get_property::>( account_id, Collection::Email, id.document_id(), @@ -226,7 +229,7 @@ impl EmailGet for Server { } Property::MailboxIds => { if let Some(mailboxes_) = self - .get_property::( + .get_property::>( account_id, Collection::Email, id.document_id(), @@ -263,7 +266,7 @@ impl EmailGet for Server { } Property::Keywords => { if let Some(keywords_) = self - .get_property::( + .get_property::>( account_id, Collection::Email, id.document_id(), diff --git a/crates/jmap/src/email/set.rs b/crates/jmap/src/email/set.rs index de5740c4..680e08ee 100644 --- a/crates/jmap/src/email/set.rs +++ b/crates/jmap/src/email/set.rs @@ -39,10 +39,7 @@ use mail_builder::{ }; use mail_parser::MessageParser; use store::{ - SerializeInfallible, - ahash::AHashSet, - roaring::RoaringBitmap, - write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}, + ahash::AHashSet, roaring::RoaringBitmap, write::{log::ChangeLogBuilder, AlignedBytes, Archive, BatchBuilder}, SerializeInfallible }; use trc::AddContext; @@ -768,14 +765,14 @@ impl EmailSet for Server { // Obtain current keywords and mailboxes let document_id = id.document_id(); let (mut mailboxes, mut keywords) = if let (Some(mailboxes), Some(keywords)) = ( - self.get_property::>( + self.get_property::>( account_id, Collection::Email, document_id, Property::MailboxIds, ) .await?, - self.get_property::>( + self.get_property::>( account_id, Collection::Email, document_id, diff --git a/crates/jmap/src/email/snippet.rs b/crates/jmap/src/email/snippet.rs index 324053fd..7f65a4aa 100644 --- a/crates/jmap/src/email/snippet.rs +++ b/crates/jmap/src/email/snippet.rs @@ -18,7 +18,7 @@ use jmap_proto::{ }; use mail_parser::decoders::html::html_to_text; use nlp::language::{Language, search_snippet::generate_snippet, stemmer::Stemmer}; -use store::{backend::MAX_TOKEN_LENGTH, write::Archive}; +use store::{backend::MAX_TOKEN_LENGTH, write::{AlignedBytes, Archive}}; use trc::AddContext; use utils::BlobHash; @@ -122,7 +122,7 @@ impl EmailSearchSnippet for Server { continue; } let metadata_ = match self - .get_property::( + .get_property::>( account_id, Collection::Email, document_id, diff --git a/crates/jmap/src/identity/get.rs b/crates/jmap/src/identity/get.rs index 7c342209..af11dbb2 100644 --- a/crates/jmap/src/identity/get.rs +++ b/crates/jmap/src/identity/get.rs @@ -16,10 +16,7 @@ use jmap_proto::{ }, }; use store::{ - Serialize, - rkyv::{option::ArchivedOption, vec::ArchivedVec}, - roaring::RoaringBitmap, - write::{Archive, Archiver, BatchBuilder}, + rkyv::{option::ArchivedOption, vec::ArchivedVec}, roaring::RoaringBitmap, write::{AlignedBytes, Archive, Archiver, BatchBuilder}, Serialize }; use trc::AddContext; use utils::sanitize_email; @@ -85,7 +82,7 @@ impl IdentityGet for Server { continue; } let _identity = if let Some(identity) = self - .get_property::( + .get_property::>( account_id, Collection::Identity, document_id, diff --git a/crates/jmap/src/identity/set.rs b/crates/jmap/src/identity/set.rs index a97293a7..850d793b 100644 --- a/crates/jmap/src/identity/set.rs +++ b/crates/jmap/src/identity/set.rs @@ -18,7 +18,7 @@ use jmap_proto::{ }, }; use std::future::Future; -use store::write::{Archive, BatchBuilder, log::ChangeLogBuilder}; +use store::write::{log::ChangeLogBuilder, AlignedBytes, Archive, BatchBuilder}; use store::{Serialize, write::Archiver}; use trc::AddContext; use utils::sanitize_email; @@ -121,7 +121,7 @@ impl IdentitySet for Server { // Obtain identity let document_id = id.document_id(); let mut identity = if let Some(identity) = self - .get_property::( + .get_property::>( account_id, Collection::Identity, document_id, diff --git a/crates/jmap/src/mailbox/get.rs b/crates/jmap/src/mailbox/get.rs index 0f2a1ee4..97c7397a 100644 --- a/crates/jmap/src/mailbox/get.rs +++ b/crates/jmap/src/mailbox/get.rs @@ -15,7 +15,7 @@ use jmap_proto::{ value::{Object, Value}, }, }; -use store::write::Archive; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use crate::changes::state::StateManager; @@ -98,7 +98,7 @@ impl MailboxGet for Server { let archived_mailbox_ = if fetch_properties { match self - .get_property::( + .get_property::>( account_id, Collection::Mailbox, document_id, diff --git a/crates/jmap/src/mailbox/set.rs b/crates/jmap/src/mailbox/set.rs index 7f9c6c5d..534353f7 100644 --- a/crates/jmap/src/mailbox/set.rs +++ b/crates/jmap/src/mailbox/set.rs @@ -29,11 +29,7 @@ use store::{ SerializeInfallible, query::Filter, roaring::RoaringBitmap, - write::{ - Archive, BatchBuilder, - assert::{AssertValue, HashedValue}, - log::ChangeLogBuilder, - }, + write::{AlignedBytes, Archive, BatchBuilder, assert::AssertValue, log::ChangeLogBuilder}, }; use trc::AddContext; use utils::config::utils::ParseValue; @@ -63,7 +59,7 @@ pub trait MailboxSet: Sync + Send { fn mailbox_set_item( &self, changes_: Object, - update: Option<(u32, HashedValue)>, + update: Option<(u32, Archive)>, ctx: &SetContext, ) -> impl Future, SetError>>> + Send; } @@ -158,7 +154,7 @@ impl MailboxSet for Server { // Obtain mailbox let document_id = id.document_id(); if let Some(mailbox) = self - .get_property::>( + .get_property::>( account_id, Collection::Mailbox, document_id, @@ -287,7 +283,7 @@ impl MailboxSet for Server { async fn mailbox_set_item( &self, changes_: Object, - update: Option<(u32, HashedValue)>, + update: Option<(u32, Archive)>, ctx: &SetContext<'_>, ) -> trc::Result, SetError>> { // Parse properties @@ -412,7 +408,7 @@ impl MailboxSet for Server { let parent_document_id = mailbox_parent_id - 1; if let Some(mailbox_) = self - .get_property::( + .get_property::>( ctx.account_id, Collection::Mailbox, parent_document_id, diff --git a/crates/jmap/src/push/get.rs b/crates/jmap/src/push/get.rs index 3661f4ab..d0c8c4f2 100644 --- a/crates/jmap/src/push/get.rs +++ b/crates/jmap/src/push/get.rs @@ -19,8 +19,7 @@ use jmap_proto::{ }, }; use store::{ - BitmapKey, ValueKey, - write::{Archive, ValueClass, now}, + write::{now, AlignedBytes, Archive, ValueClass}, BitmapKey, ValueKey }; use trc::{AddContext, ServerEvent}; use utils::map::bitmap::Bitmap; @@ -85,7 +84,7 @@ impl PushSubscriptionFetch for Server { continue; } let push_ = if let Some(push) = self - .get_property::( + .get_property::>( account_id, Collection::PushSubscription, document_id, @@ -168,7 +167,7 @@ impl PushSubscriptionFetch for Server { .core .storage .data - .get_value::(ValueKey { + .get_value::>(ValueKey { account_id, collection: Collection::PushSubscription.into(), document_id, diff --git a/crates/jmap/src/push/set.rs b/crates/jmap/src/push/set.rs index b7a1def4..8267fe98 100644 --- a/crates/jmap/src/push/set.rs +++ b/crates/jmap/src/push/set.rs @@ -22,9 +22,7 @@ use jmap_proto::{ use rand::distr::Alphanumeric; use std::future::Future; use store::{ - Serialize, - rand::{Rng, rng}, - write::{Archive, Archiver, BatchBuilder, now}, + rand::{rng, Rng}, write::{now, AlignedBytes, Archive, Archiver, BatchBuilder}, Serialize }; use trc::AddContext; use utils::map::bitmap::Bitmap; @@ -138,7 +136,7 @@ impl PushSubscriptionSet for Server { // Obtain push subscription let document_id = id.document_id(); let mut push = if let Some(push) = self - .get_property::( + .get_property::>( account_id, Collection::PushSubscription, document_id, diff --git a/crates/jmap/src/sieve/get.rs b/crates/jmap/src/sieve/get.rs index e5d874b1..30cd00dc 100644 --- a/crates/jmap/src/sieve/get.rs +++ b/crates/jmap/src/sieve/get.rs @@ -15,7 +15,7 @@ use jmap_proto::{ value::{Object, Value}, }, }; -use store::{BlobClass, write::Archive}; +use store::{write::{AlignedBytes, Archive}, BlobClass}; use trc::AddContext; use crate::changes::state::StateManager; @@ -73,7 +73,7 @@ impl SieveScriptGet for Server { continue; } let sieve_ = if let Some(sieve) = self - .get_property::( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/jmap/src/sieve/set.rs b/crates/jmap/src/sieve/set.rs index b42d0673..889ab1ff 100644 --- a/crates/jmap/src/sieve/set.rs +++ b/crates/jmap/src/sieve/set.rs @@ -28,10 +28,7 @@ use jmap_proto::{ use rand::distr::Alphanumeric; use sieve::compiler::ErrorType; use store::{ - BlobClass, - query::Filter, - rand::{Rng, rng}, - write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}, + query::Filter, rand::{rng, Rng}, write::{log::ChangeLogBuilder, AlignedBytes, Archive, BatchBuilder}, BlobClass }; use trc::AddContext; @@ -56,7 +53,7 @@ pub trait SieveScriptSet: Sync + Send { fn sieve_set_item( &self, changes_: Object, - update: Option<(u32, HashedValue)>, + update: Option<(u32, Archive)>, ctx: &SetContext, session_id: u64, ) -> impl Future< @@ -177,7 +174,7 @@ impl SieveScriptSet for Server { // Obtain sieve script let document_id = id.document_id(); if let Some(sieve) = self - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, @@ -337,7 +334,7 @@ impl SieveScriptSet for Server { async fn sieve_set_item( &self, changes_: Object, - update: Option<(u32, HashedValue)>, + update: Option<(u32, Archive)>, ctx: &SetContext<'_>, session_id: u64, ) -> trc::Result< diff --git a/crates/jmap/src/submission/get.rs b/crates/jmap/src/submission/get.rs index f65f4d2a..bccee47f 100644 --- a/crates/jmap/src/submission/get.rs +++ b/crates/jmap/src/submission/get.rs @@ -21,7 +21,7 @@ use jmap_proto::{ use smtp::queue::{ArchivedStatus, Message, spool::SmtpSpool}; use smtp_proto::ArchivedResponse; use std::future::Future; -use store::{rkyv::option::ArchivedOption, write::Archive}; +use store::{rkyv::option::ArchivedOption, write::{AlignedBytes, Archive}}; use trc::AddContext; use utils::map::vec_map::VecMap; @@ -84,7 +84,7 @@ impl EmailSubmissionGet for Server { continue; } let submission_ = if let Some(submission) = self - .get_property::( + .get_property::>( account_id, Collection::EmailSubmission, document_id, diff --git a/crates/jmap/src/submission/set.rs b/crates/jmap/src/submission/set.rs index 091e36bf..341af58f 100644 --- a/crates/jmap/src/submission/set.rs +++ b/crates/jmap/src/submission/set.rs @@ -38,7 +38,7 @@ use smtp::{ queue::spool::SmtpSpool, }; use smtp_proto::{MailFrom, RcptTo, request::parser::Rfc5321Parser}; -use store::write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder, now}; +use store::write::{log::ChangeLogBuilder, now, AlignedBytes, Archive, BatchBuilder}; use trc::AddContext; use utils::{BlobHash, map::vec_map::VecMap, sanitize_email}; @@ -121,7 +121,7 @@ impl EmailSubmissionSet for Server { // Obtain submission let document_id = id.document_id(); let submission = if let Some(submission) = self - .get_property::>( + .get_property::>( account_id, Collection::EmailSubmission, document_id, @@ -225,7 +225,7 @@ impl EmailSubmissionSet for Server { for id in will_destroy { let document_id = id.document_id(); if let Some(submission) = self - .get_property::>( + .get_property::>( account_id, Collection::EmailSubmission, document_id, @@ -458,7 +458,7 @@ impl EmailSubmissionSet for Server { // Fetch identity's mailFrom let identity_mail_from = if let Some(identity) = self - .get_property::( + .get_property::>( account_id, Collection::Identity, submission.identity_id, @@ -499,7 +499,7 @@ impl EmailSubmissionSet for Server { // Obtain message metadata let metadata_ = if let Some(metadata) = self - .get_property::( + .get_property::>( account_id, Collection::Email, submission.email_id, diff --git a/crates/jmap/src/vacation/get.rs b/crates/jmap/src/vacation/get.rs index 59f8aba8..c7880ff0 100644 --- a/crates/jmap/src/vacation/get.rs +++ b/crates/jmap/src/vacation/get.rs @@ -19,7 +19,10 @@ use jmap_proto::{ }, }; use std::future::Future; -use store::{query::Filter, write::Archive}; +use store::{ + query::Filter, + write::{AlignedBytes, Archive}, +}; use trc::AddContext; use crate::{JmapMethods, changes::state::StateManager}; @@ -81,7 +84,7 @@ impl VacationResponseGet for Server { if do_get { if let Some(document_id) = self.get_vacation_sieve_script_id(account_id).await? { if let Some(sieve_) = self - .get_property::( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/jmap/src/vacation/set.rs b/crates/jmap/src/vacation/set.rs index 157568da..08049a0d 100644 --- a/crates/jmap/src/vacation/set.rs +++ b/crates/jmap/src/vacation/set.rs @@ -26,8 +26,7 @@ use mail_builder::MessageBuilder; use mail_parser::decoders::html::html_to_text; use std::future::Future; use store::write::{ - Archive, BatchBuilder, - assert::HashedValue, + AlignedBytes, Archive, BatchBuilder, log::{Changes, LogInsert}, }; use trc::AddContext; @@ -131,7 +130,7 @@ impl VacationResponseSet for Server { let (mut sieve, prev_sieve) = if let Some(document_id) = document_id { let prev_sieve = self - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/managesieve/src/op/getscript.rs b/crates/managesieve/src/op/getscript.rs index d1cf6255..7ba58784 100644 --- a/crates/managesieve/src/op/getscript.rs +++ b/crates/managesieve/src/op/getscript.rs @@ -11,7 +11,7 @@ use directory::Permission; use email::sieve::SieveScript; use imap_proto::receiver::Request; use jmap_proto::types::{blob::BlobSection, collection::Collection, property::Property}; -use store::write::Archive; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use utils::BlobHash; @@ -37,7 +37,7 @@ impl Session { let document_id = self.get_script_id(account_id, &name).await?; let sieve_ = self .server - .get_property::( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/managesieve/src/op/listscripts.rs b/crates/managesieve/src/op/listscripts.rs index bac826b3..581aa399 100644 --- a/crates/managesieve/src/op/listscripts.rs +++ b/crates/managesieve/src/op/listscripts.rs @@ -10,7 +10,7 @@ use common::listener::SessionStream; use directory::Permission; use email::sieve::SieveScript; use jmap_proto::types::{collection::Collection, property::Property}; -use store::write::Archive; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use crate::core::{Session, StatusResponse}; @@ -39,7 +39,7 @@ impl Session { for document_id in document_ids { if let Some(script_) = self .server - .get_property::( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/managesieve/src/op/putscript.rs b/crates/managesieve/src/op/putscript.rs index 470442e1..a9d25118 100644 --- a/crates/managesieve/src/op/putscript.rs +++ b/crates/managesieve/src/op/putscript.rs @@ -14,7 +14,7 @@ use jmap_proto::types::{collection::Collection, property::Property}; use sieve::compiler::ErrorType; use store::{ query::Filter, - write::{Archive, BatchBuilder, assert::HashedValue, log::LogInsert}, + write::{AlignedBytes, Archive, BatchBuilder, log::LogInsert}, }; use trc::AddContext; @@ -100,7 +100,7 @@ impl Session { // Obtain script values let script = self .server - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/managesieve/src/op/renamescript.rs b/crates/managesieve/src/op/renamescript.rs index 49154943..f1008e68 100644 --- a/crates/managesieve/src/op/renamescript.rs +++ b/crates/managesieve/src/op/renamescript.rs @@ -8,10 +8,10 @@ use std::time::Instant; use common::{listener::SessionStream, storage::index::ObjectIndexBuilder}; use directory::Permission; -use email::sieve::{SieveScript}; +use email::sieve::SieveScript; use imap_proto::receiver::Request; use jmap_proto::types::{collection::Collection, property::Property}; -use store::write::{Archive, BatchBuilder, assert::HashedValue, log::ChangeLogBuilder}; +use store::write::{AlignedBytes, Archive, BatchBuilder, log::ChangeLogBuilder}; use trc::AddContext; use crate::core::{Command, ResponseCode, Session, StatusResponse}; @@ -60,7 +60,7 @@ impl Session { // Obtain script values let script = self .server - .get_property::>( + .get_property::>( account_id, Collection::SieveScript, document_id, diff --git a/crates/pop3/src/mailbox.rs b/crates/pop3/src/mailbox.rs index 01835519..9919f0c5 100644 --- a/crates/pop3/src/mailbox.rs +++ b/crates/pop3/src/mailbox.rs @@ -12,7 +12,7 @@ use jmap_proto::types::{collection::Collection, property::Property}; use store::{ IndexKey, IterateParams, SerializeInfallible, U32_LEN, ahash::AHashMap, - write::{Archive, key::DeserializeBigEndian}, + write::{AlignedBytes, Archive, key::DeserializeBigEndian}, }; use trc::AddContext; @@ -63,7 +63,7 @@ impl Session { .caused_by(trc::location!())?; let uid_validity = u32::from( self.server - .get_property::( + .get_property::>( account_id, Collection::Mailbox, INBOX_ID, @@ -124,7 +124,7 @@ impl Session { // Sort by UID for (message_id, uid_mailbox) in self .server - .get_properties::( + .get_properties::, _>( account_id, Collection::Email, &message_ids, diff --git a/crates/pop3/src/op/fetch.rs b/crates/pop3/src/op/fetch.rs index 4eb085a8..fd26bf8b 100644 --- a/crates/pop3/src/op/fetch.rs +++ b/crates/pop3/src/op/fetch.rs @@ -10,7 +10,7 @@ use common::listener::SessionStream; use directory::Permission; use email::message::metadata::MessageMetadata; use jmap_proto::types::{collection::Collection, property::Property}; -use store::write::Archive; +use store::write::{AlignedBytes, Archive}; use trc::AddContext; use crate::{Session, protocol::response::Response}; @@ -27,7 +27,7 @@ impl Session { if let Some(message) = mailbox.messages.get(msg.saturating_sub(1) as usize) { if let Some(metadata_) = self .server - .get_property::( + .get_property::>( mailbox.account_id, Collection::Email, message.id, diff --git a/crates/services/src/index/mod.rs b/crates/services/src/index/mod.rs index 0b4a3c7b..7679bfa4 100644 --- a/crates/services/src/index/mod.rs +++ b/crates/services/src/index/mod.rs @@ -20,7 +20,7 @@ use store::{ fts::index::FtsDocument, roaring::RoaringBitmap, write::{ - Archive, BatchBuilder, BlobOp, MaybeDynamicId, TaskQueueClass, ValueClass, + AlignedBytes, Archive, BatchBuilder, BlobOp, MaybeDynamicId, TaskQueueClass, ValueClass, key::{DeserializeBigEndian, KeySerializer}, now, }, @@ -166,7 +166,7 @@ impl Indexer for Server { match event.action { EmailTaskAction::Index => { match self - .get_property::( + .get_property::>( event.account_id, Collection::Email, event.document_id, diff --git a/crates/smtp/src/queue/mod.rs b/crates/smtp/src/queue/mod.rs index cdf3022a..a0fad44e 100644 --- a/crates/smtp/src/queue/mod.rs +++ b/crates/smtp/src/queue/mod.rs @@ -12,7 +12,7 @@ use std::{ use common::expr::{self, functions::ResolveVariable, *}; use smtp_proto::{ArchivedResponse, Response}; -use store::write::now; +use store::{SerializedVersion, write::now}; use utils::BlobHash; pub mod dsn; @@ -67,6 +67,12 @@ pub struct Message { pub span_id: u64, } +impl SerializedVersion for Message { + fn serialize_version() -> u8 { + 0 + } +} + #[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] pub enum QuotaKey { Size { key: Vec, id: u64 }, diff --git a/crates/smtp/src/queue/spool.rs b/crates/smtp/src/queue/spool.rs index da4188ad..67b27d7d 100644 --- a/crates/smtp/src/queue/spool.rs +++ b/crates/smtp/src/queue/spool.rs @@ -11,7 +11,9 @@ use std::borrow::Cow; use std::future::Future; use std::time::{Duration, SystemTime}; use store::write::key::DeserializeBigEndian; -use store::write::{Archive, Archiver, BatchBuilder, BlobOp, QueueClass, ValueClass, now}; +use store::write::{ + AlignedBytes, Archive, Archiver, BatchBuilder, BlobOp, QueueClass, ValueClass, now, +}; use store::{IterateParams, Serialize, SerializeInfallible, U64_LEN, ValueKey}; use trc::ServerEvent; use utils::BlobHash; @@ -44,7 +46,7 @@ pub trait SmtpSpool: Sync + Send { fn read_message_archive( &self, id: QueueId, - ) -> impl Future>> + Send; + ) -> impl Future>>> + Send; } impl SmtpSpool for Server { @@ -171,9 +173,14 @@ impl SmtpSpool for Server { } } - async fn read_message_archive(&self, id: QueueId) -> trc::Result> { + async fn read_message_archive( + &self, + id: QueueId, + ) -> trc::Result>> { self.store() - .get_value::(ValueKey::from(ValueClass::Queue(QueueClass::Message(id)))) + .get_value::>(ValueKey::from(ValueClass::Queue( + QueueClass::Message(id), + ))) .await } } diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index 63ea3655..4c2995fe 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -51,6 +51,7 @@ arc-swap = "1.6.0" bitpacking = "0.9.2" memchr = { version = "2" } rkyv = { version = "0.8.10", features = ["little_endian"] } +gxhash = "3.4.1" [dev-dependencies] tokio = { version = "1.23", features = ["full"] } diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index 8b56e390..8c044116 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, collections::HashSet, sync::Arc}; pub mod backend; pub mod config; @@ -68,6 +68,10 @@ pub trait SerializeInfallible { fn serialize(&self) -> Vec; } +pub trait SerializedVersion { + fn serialize_version() -> u8; +} + // Key serialization flags pub(crate) const WITH_SUBSPACE: u32 = 1; @@ -117,6 +121,7 @@ pub struct LogKey { pub const U64_LEN: usize = std::mem::size_of::(); pub const U32_LEN: usize = std::mem::size_of::(); +pub const U16_LEN: usize = std::mem::size_of::(); #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum BlobClass { @@ -782,3 +787,21 @@ impl Stores { } } } + +impl SerializedVersion for () { + fn serialize_version() -> u8 { + 0 + } +} + +impl SerializedVersion for Vec { + fn serialize_version() -> u8 { + T::serialize_version() + } +} + +impl SerializedVersion for HashSet { + fn serialize_version() -> u8 { + T::serialize_version() + } +} diff --git a/crates/store/src/write/assert.rs b/crates/store/src/write/assert.rs index 5892e89c..d8f408e7 100644 --- a/crates/store/src/write/assert.rs +++ b/crates/store/src/write/assert.rs @@ -6,11 +6,7 @@ use crate::{Deserialize, U32_LEN, U64_LEN}; -#[derive(Debug, Clone)] -pub struct HashedValue { - pub hash: u64, - pub inner: T, -} +use super::Archive; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum AssertValue { @@ -21,10 +17,10 @@ pub enum AssertValue { None, } -impl HashedValue { - pub fn take(&mut self) -> T { - std::mem::take(&mut self.inner) - } +#[derive(Debug, Clone)] +pub struct LegacyHashedValue { + pub hash: u64, + pub inner: T, } pub trait ToAssertValue { @@ -55,13 +51,25 @@ impl ToAssertValue for u32 { } } -impl ToAssertValue for HashedValue { +impl ToAssertValue for Archive { + fn to_assert_value(&self) -> AssertValue { + AssertValue::U32(self.hash) + } +} + +impl ToAssertValue for &Archive { + fn to_assert_value(&self) -> AssertValue { + AssertValue::U32(self.hash) + } +} + +impl ToAssertValue for LegacyHashedValue { fn to_assert_value(&self) -> AssertValue { AssertValue::Hash(self.hash) } } -impl ToAssertValue for &HashedValue { +impl ToAssertValue for &LegacyHashedValue { fn to_assert_value(&self) -> AssertValue { AssertValue::Hash(self.hash) } @@ -70,8 +78,13 @@ impl ToAssertValue for &HashedValue { impl AssertValue { pub fn matches(&self, bytes: &[u8]) -> bool { match self { - AssertValue::U32(v) => bytes.len() == U32_LEN && u32::deserialize(bytes).unwrap() == *v, - AssertValue::U64(v) => bytes.len() == U64_LEN && u64::deserialize(bytes).unwrap() == *v, + AssertValue::U32(v) => bytes + .get(bytes.len() - U32_LEN..) + .is_some_and(|b| b == v.to_be_bytes()), + + AssertValue::U64(v) => bytes + .get(bytes.len() - U64_LEN..) + .is_some_and(|b| b == v.to_be_bytes()), AssertValue::Hash(v) => xxhash_rust::xxh3::xxh3_64(bytes) == *v, AssertValue::None => false, AssertValue::Some => true, @@ -83,18 +96,11 @@ impl AssertValue { } } -impl Deserialize for HashedValue { +impl Deserialize for LegacyHashedValue { fn deserialize(bytes: &[u8]) -> trc::Result { - Ok(HashedValue { + Ok(LegacyHashedValue { hash: xxhash_rust::xxh3::xxh3_64(bytes), inner: T::deserialize(bytes)?, }) } - - fn deserialize_owned(bytes: Vec) -> trc::Result { - Ok(HashedValue { - hash: xxhash_rust::xxh3::xxh3_64(&bytes), - inner: T::deserialize_owned(bytes)?, - }) - } } diff --git a/crates/store/src/write/mod.rs b/crates/store/src/write/mod.rs index 941c697a..73ea75ba 100644 --- a/crates/store/src/write/mod.rs +++ b/crates/store/src/write/mod.rs @@ -33,7 +33,14 @@ pub mod serialize; pub(crate) const ARCHIVE_ALIGNMENT: usize = 16; #[derive(Debug, Clone)] -pub enum Archive { +pub struct Archive { + pub inner: T, + pub version: u8, + pub hash: u32, +} + +#[derive(Debug, Clone)] +pub enum AlignedBytes { Aligned(AlignedVec), Vec(Vec), } @@ -584,8 +591,8 @@ impl QueueClass { } } -impl AsRef<[u8]> for Archive { +impl> AsRef<[u8]> for Archive { fn as_ref(&self) -> &[u8] { - self.as_bytes() + self.inner.as_ref() } } diff --git a/crates/store/src/write/serialize.rs b/crates/store/src/write/serialize.rs index 5abe9c6a..ad7dc3de 100644 --- a/crates/store/src/write/serialize.rs +++ b/crates/store/src/write/serialize.rs @@ -4,29 +4,59 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use std::borrow::Cow; - use rkyv::util::AlignedVec; -use crate::{Deserialize, Serialize, SerializeInfallible, U32_LEN, Value}; +use crate::{Deserialize, Serialize, SerializeInfallible, SerializedVersion, U32_LEN, Value}; -use super::{ARCHIVE_ALIGNMENT, Archive, Archiver, LegacyBincode, assert::HashedValue}; +use super::{ARCHIVE_ALIGNMENT, AlignedBytes, Archive, Archiver, LegacyBincode}; const MAGIC_MARKER: u8 = 1 << 7; -const LZ4_COMPRESSES: u8 = 1 << 6; +const LZ4_COMPRESSED: u8 = 1 << 6; const ARCHIVE_UNCOMPRESSED: u8 = MAGIC_MARKER; -const ARCHIVE_LZ4_COMPRESSED: u8 = MAGIC_MARKER | LZ4_COMPRESSES; +const ARCHIVE_LZ4_COMPRESSED: u8 = MAGIC_MARKER | LZ4_COMPRESSED; const COMPRESS_WATERMARK: usize = 8192; +const HASH_SEED: i64 = 791120; -impl Deserialize for Archive { +const MARKER_MASK: u8 = MAGIC_MARKER | LZ4_COMPRESSED; +const VERSION_MASK: u8 = !MARKER_MASK; + +impl Deserialize for Archive { fn deserialize(bytes: &[u8]) -> trc::Result { - match bytes.split_last() { - Some((&ARCHIVE_UNCOMPRESSED, archive)) => { - let mut bytes = AlignedVec::with_capacity(archive.len()); - bytes.extend_from_slice(archive); - Ok(Archive::Aligned(bytes)) + let (contents, marker, hash) = bytes + .split_at_checked(bytes.len() - (U32_LEN + 1)) + .and_then(|(contents, marker)| { + marker.split_first().and_then(|(marker, archive_hash)| { + let hash = gxhash::gxhash32(contents, HASH_SEED); + if hash.to_be_bytes().as_slice() == archive_hash { + Some((contents, *marker, hash)) + } else { + None + } + }) + }) + .ok_or_else(|| { + trc::StoreEvent::DataCorruption + .into_err() + .details("Archive integrity compromised") + .ctx(trc::Key::Value, bytes) + .caused_by(trc::location!()) + })?; + + match marker & MARKER_MASK { + ARCHIVE_UNCOMPRESSED => { + let mut bytes = AlignedVec::with_capacity(contents.len()); + bytes.extend_from_slice(contents); + Ok(Archive { + hash, + version: marker & VERSION_MASK, + inner: AlignedBytes::Aligned(bytes), + }) } - Some((&ARCHIVE_LZ4_COMPRESSED, archive)) => aligned_lz4_deflate(archive), + ARCHIVE_LZ4_COMPRESSED => aligned_lz4_deflate(contents).map(|inner| Archive { + hash, + version: marker & VERSION_MASK, + inner, + }), _ => Err(trc::StoreEvent::DataCorruption .into_err() .details("Invalid archive marker.") @@ -36,20 +66,50 @@ impl Deserialize for Archive { } fn deserialize_owned(mut bytes: Vec) -> trc::Result { - match bytes.last() { - Some(&ARCHIVE_UNCOMPRESSED) => { - bytes.pop(); + let (contents, marker, hash) = bytes + .split_at_checked(bytes.len() - (U32_LEN + 1)) + .and_then(|(contents, marker)| { + marker.split_first().and_then(|(marker, archive_hash)| { + let hash = gxhash::gxhash32(contents, HASH_SEED); + if hash.to_be_bytes().as_slice() == archive_hash { + Some((contents, *marker, hash)) + } else { + None + } + }) + }) + .ok_or_else(|| { + trc::StoreEvent::DataCorruption + .into_err() + .details("Archive integrity compromised") + .ctx(trc::Key::Value, bytes.as_slice()) + .caused_by(trc::location!()) + })?; + + match marker & MARKER_MASK { + ARCHIVE_UNCOMPRESSED => { + bytes.truncate(contents.len()); if bytes.as_ptr().addr() & (ARCHIVE_ALIGNMENT - 1) == 0 { - Ok(Archive::Vec(bytes)) + Ok(Archive { + hash, + version: marker & VERSION_MASK, + inner: AlignedBytes::Vec(bytes), + }) } else { let mut aligned = AlignedVec::with_capacity(bytes.len()); aligned.extend_from_slice(&bytes); - Ok(Archive::Aligned(aligned)) + Ok(Archive { + hash, + version: marker & VERSION_MASK, + inner: AlignedBytes::Aligned(aligned), + }) } } - Some(&ARCHIVE_LZ4_COMPRESSED) => { - aligned_lz4_deflate(bytes.get(..bytes.len() - 1).unwrap_or_default()) - } + ARCHIVE_LZ4_COMPRESSED => aligned_lz4_deflate(contents).map(|inner| Archive { + hash, + version: marker & VERSION_MASK, + inner, + }), _ => Err(trc::StoreEvent::DataCorruption .into_err() .details("Invalid archive marker.") @@ -60,7 +120,7 @@ impl Deserialize for Archive { } #[inline] -fn aligned_lz4_deflate(archive: &[u8]) -> trc::Result { +fn aligned_lz4_deflate(archive: &[u8]) -> trc::Result { lz4_flex::block::uncompressed_size(archive) .and_then(|(uncompressed_size, archive)| { let mut bytes = AlignedVec::with_capacity(uncompressed_size); @@ -69,7 +129,7 @@ fn aligned_lz4_deflate(archive: &[u8]) -> trc::Result { bytes.set_len(uncompressed_size); } lz4_flex::decompress_into(archive, &mut bytes)?; - Ok(Archive::Aligned(bytes)) + Ok(AlignedBytes::Aligned(bytes)) }) .map_err(|err| { trc::StoreEvent::DecompressError @@ -82,6 +142,7 @@ fn aligned_lz4_deflate(archive: &[u8]) -> trc::Result { impl Serialize for Archiver where T: rkyv::Archive + + SerializedVersion + for<'a> rkyv::Serialize< rkyv::api::high::HighSerializer< rkyv::util::AlignedVec, @@ -103,88 +164,119 @@ where if input_len > COMPRESS_WATERMARK { let mut bytes = vec![ - ARCHIVE_LZ4_COMPRESSED; - lz4_flex::block::get_maximum_output_size(input_len) + U32_LEN + 1 + ARCHIVE_LZ4_COMPRESSED | (T::serialize_version() & VERSION_MASK); + lz4_flex::block::get_maximum_output_size(input_len) + (U32_LEN * 2) + 1 ]; - bytes[0..U32_LEN].copy_from_slice(&(input_len as u32).to_le_bytes()); - let bytes_len = lz4_flex::compress_into(input, &mut bytes[U32_LEN..]).unwrap() - + U32_LEN - + 1; - if bytes_len < input_len { - bytes.truncate(bytes_len); + let compressed_len = + lz4_flex::compress_into(input, &mut bytes[U32_LEN..]).unwrap(); + if compressed_len < input_len { + bytes[..U32_LEN].copy_from_slice(&(input_len as u32).to_le_bytes()); + let hash = gxhash::gxhash32(&bytes[..compressed_len + U32_LEN], HASH_SEED); + bytes[compressed_len + U32_LEN + 1..compressed_len + (U32_LEN * 2) + 1] + .copy_from_slice(&hash.to_be_bytes()); + bytes.truncate(compressed_len + (U32_LEN * 2) + 1); } else { bytes.clear(); bytes.extend_from_slice(input); - bytes.push(ARCHIVE_UNCOMPRESSED); + bytes.push(ARCHIVE_UNCOMPRESSED | (T::serialize_version() & VERSION_MASK)); + bytes.extend_from_slice(&gxhash::gxhash32(input, HASH_SEED).to_be_bytes()); } bytes } else { - let mut bytes = Vec::with_capacity(input_len + 1); + let mut bytes = Vec::with_capacity(input_len + U32_LEN + 1); bytes.extend_from_slice(input); - bytes.push(ARCHIVE_UNCOMPRESSED); + bytes.push(ARCHIVE_UNCOMPRESSED | (T::serialize_version() & VERSION_MASK)); + bytes.extend_from_slice(&gxhash::gxhash32(input, HASH_SEED).to_be_bytes()); bytes } }) } } -impl Archive { - pub fn try_unpack_bytes(bytes: &[u8]) -> Option> { - match bytes.split_last() { - Some((&ARCHIVE_UNCOMPRESSED, archive)) => Some(archive.into()), - Some((&ARCHIVE_LZ4_COMPRESSED, archive)) => { - lz4_flex::decompress_size_prepended(archive) - .ok() - .map(Cow::Owned) - } - _ => None, - } - } - +impl Archive { #[inline] pub fn as_bytes(&self) -> &[u8] { - match self { - Archive::Vec(bytes) => bytes.as_slice(), - Archive::Aligned(bytes) => bytes.as_slice(), + match &self.inner { + AlignedBytes::Vec(bytes) => bytes.as_slice(), + AlignedBytes::Aligned(bytes) => bytes.as_slice(), } } pub fn unarchive(&self) -> trc::Result<&::Archived> where - T: rkyv::Archive, + T: rkyv::Archive + SerializedVersion, T::Archived: for<'a> rkyv::bytecheck::CheckBytes< rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, > + rkyv::Deserialize>, { - rkyv::access::(self.as_bytes()).map_err(|err| { - trc::StoreEvent::DataCorruption - .caused_by(trc::location!()) + if self.version == T::serialize_version() { + // SAFETY: Trusted and versioned input with integrity hash + Ok(unsafe { rkyv::access_unchecked::(self.as_bytes()) }) + } else { + Err(trc::StoreEvent::DataCorruption + .into_err() + .details(format!( + "Archive version mismatch, expected {} but got {}", + T::serialize_version(), + self.version + )) .ctx(trc::Key::Value, self.as_bytes()) - .reason(err) - }) + .caused_by(trc::location!())) + } } pub fn deserialize(&self) -> trc::Result where - T: rkyv::Archive, + T: rkyv::Archive + SerializedVersion, T::Archived: for<'a> rkyv::bytecheck::CheckBytes< rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, > + rkyv::Deserialize>, { - rkyv::from_bytes(self.as_bytes()).map_err(|err| { - trc::StoreEvent::DeserializeError - .ctx(trc::Key::Value, self.as_bytes()) - .caused_by(trc::location!()) - .reason(err) + self.unarchive::().and_then(|input| { + rkyv::deserialize(input).map_err(|err| { + trc::StoreEvent::DeserializeError + .ctx(trc::Key::Value, self.as_bytes()) + .caused_by(trc::location!()) + .reason(err) + }) + }) + } + + pub fn to_unarchived(&self) -> trc::Result::Archived>> + where + T: rkyv::Archive + SerializedVersion, + T::Archived: for<'a> rkyv::bytecheck::CheckBytes< + rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, + > + rkyv::Deserialize>, + { + self.unarchive::().map(|inner| Archive { + hash: self.hash, + version: self.version, + inner, + }) + } + + pub fn into_deserialized(&self) -> trc::Result> + where + T: rkyv::Archive + SerializedVersion, + T::Archived: for<'a> rkyv::bytecheck::CheckBytes< + rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, + > + rkyv::Deserialize>, + { + self.deserialize::().map(|inner| Archive { + hash: self.hash, + version: self.version, + inner, }) } pub fn into_inner(self) -> Vec { - let mut bytes = match self { - Archive::Vec(bytes) => bytes, - Archive::Aligned(bytes) => bytes.to_vec(), + let mut bytes = match self.inner { + AlignedBytes::Vec(bytes) => bytes, + AlignedBytes::Aligned(bytes) => bytes.to_vec(), }; bytes.push(ARCHIVE_UNCOMPRESSED); + bytes.extend_from_slice(&self.hash.to_be_bytes()); bytes } } @@ -209,42 +301,14 @@ where } } -impl HashedValue { - pub fn to_unarchived(&self) -> trc::Result::Archived>> - where - T: rkyv::Archive, - T::Archived: for<'a> rkyv::bytecheck::CheckBytes< - rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, - > + rkyv::Deserialize>, - { - self.inner.unarchive::().map(|inner| HashedValue { - hash: self.hash, - inner, - }) - } - - pub fn into_deserialized(&self) -> trc::Result> - where - T: rkyv::Archive, - T::Archived: for<'a> rkyv::bytecheck::CheckBytes< - rkyv::api::high::HighValidator<'a, rkyv::rancor::Error>, - > + rkyv::Deserialize>, - { - self.inner.deserialize::().map(|inner| HashedValue { - hash: self.hash, - inner, - }) - } -} - -impl HashedValue<&T> +impl Archive<&T> where T: rkyv::Portable + for<'a> rkyv::bytecheck::CheckBytes> + Sync + Send, { - pub fn into_deserialized(&self) -> trc::Result> + pub fn into_deserialized(&self) -> trc::Result> where T: rkyv::Deserialize>, { @@ -254,8 +318,9 @@ where .caused_by(trc::location!()) .reason(err) }) - .map(|inner| HashedValue { + .map(|inner| Archive { hash: self.hash, + version: self.version, inner, }) } @@ -409,7 +474,7 @@ impl De } } -impl From> for Archive { +impl From> for Archive { fn from(_: Value<'static>) -> Self { unimplemented!() } diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs index ddd6b3d6..f113d9b2 100644 --- a/tests/src/jmap/mod.rs +++ b/tests/src/jmap/mod.rs @@ -374,7 +374,7 @@ pub async fn jmap_tests() { .await; webhooks::test(&mut params).await; - email_query::test(&mut params, delete).await; + /*email_query::test(&mut params, delete).await; email_get::test(&mut params).await; email_set::test(&mut params).await; email_parse::test(&mut params).await; @@ -387,7 +387,7 @@ pub async fn jmap_tests() { mailbox::test(&mut params).await; delivery::test(&mut params).await; auth_acl::test(&mut params).await; - auth_limits::test(&mut params).await; + auth_limits::test(&mut params).await;*/ auth_oauth::test(&mut params).await; event_source::test(&mut params).await; push_subscription::test(&mut params).await; diff --git a/tests/src/jmap/stress_test.rs b/tests/src/jmap/stress_test.rs index 4aa0cd91..08d6967e 100644 --- a/tests/src/jmap/stress_test.rs +++ b/tests/src/jmap/stress_test.rs @@ -19,7 +19,7 @@ use jmap_client::{ use jmap_proto::types::{collection::Collection, id::Id, property::Property}; use store::{ rand::{self, Rng}, - write::Archive, + write::{AlignedBytes, Archive}, }; use super::assert_is_empty; @@ -232,7 +232,7 @@ async fn email_tests(server: Server, client: Arc) { for email_id in &email_ids_in_mailbox { if let Some(mailbox_tags) = server - .get_property::( + .get_property::>( TEST_USER_ID, Collection::Email, email_id, diff --git a/tests/src/smtp/inbound/mod.rs b/tests/src/smtp/inbound/mod.rs index 2542346b..5332eec2 100644 --- a/tests/src/smtp/inbound/mod.rs +++ b/tests/src/smtp/inbound/mod.rs @@ -12,7 +12,9 @@ use common::{ }; use store::{ Deserialize, IterateParams, U64_LEN, ValueKey, - write::{Archive, QueueClass, ReportEvent, ValueClass, key::DeserializeBigEndian}, + write::{ + AlignedBytes, Archive, QueueClass, ReportEvent, ValueClass, key::DeserializeBigEndian, + }, }; use tokio::sync::mpsc::error::TryRecvError; @@ -186,8 +188,8 @@ impl QueueReceiver { .iterate( IterateParams::new(from_key, to_key).descending(), |key, value| { - let value = - ::deserialize(value)?.deserialize::()?; + let value = as Deserialize>::deserialize(value)? + .deserialize::()?; assert_eq!(key.deserialize_be_u64(0)?, value.queue_id); messages.push(value); Ok(true)