Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{BlobStore, CompressionAlgo, Store, U32_LEN};
|
||||
use std::{ops::Range, time::Instant};
|
||||
use trc::{AddContext, StoreEvent};
|
||||
|
||||
const MAGIC_MARKER: u8 = 0xa0;
|
||||
const LZ4_MARKER: u8 = MAGIC_MARKER | 0x01;
|
||||
//const ZSTD_MARKER: u8 = MAGIC_MARKER | 0x02;
|
||||
const NONE_MARKER: u8 = 0x00;
|
||||
|
||||
impl BlobStore {
|
||||
pub async fn get_blob(&self, key: &[u8], range: Range<usize>) -> trc::Result<Option<Vec<u8>>> {
|
||||
let start_time = Instant::now();
|
||||
let result = match &self {
|
||||
BlobStore::Store(store) => match store {
|
||||
#[cfg(feature = "sqlite")]
|
||||
Store::SQLite(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
#[cfg(feature = "foundation")]
|
||||
Store::FoundationDb(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
#[cfg(feature = "postgres")]
|
||||
Store::PostgreSQL(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
#[cfg(feature = "mysql")]
|
||||
Store::MySQL(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
#[cfg(feature = "rocks")]
|
||||
Store::RocksDb(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
Store::Ephemeral(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
Store::None => Err(trc::StoreEvent::NotConfigured.into()),
|
||||
},
|
||||
BlobStore::Fs(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
#[cfg(feature = "s3")]
|
||||
BlobStore::S3(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
#[cfg(feature = "azure")]
|
||||
BlobStore::Azure(store) => store.get_blob(key, 0..usize::MAX).await,
|
||||
}
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
trc::event!(
|
||||
Store(StoreEvent::BlobRead),
|
||||
Key = key,
|
||||
Elapsed = start_time.elapsed(),
|
||||
Size = result.as_ref().map_or(0, |data| data.len()),
|
||||
);
|
||||
|
||||
let Some(mut data) = result else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut data = match data.last().copied() {
|
||||
Some(LZ4_MARKER) => {
|
||||
lz4_flex::decompress_size_prepended(data.get(..data.len() - 1).unwrap_or_default())
|
||||
.map_err(|err| {
|
||||
trc::StoreEvent::DecompressError
|
||||
.reason(err)
|
||||
.ctx(trc::Key::Key, key)
|
||||
.ctx(trc::Key::CausedBy, trc::location!())
|
||||
})?
|
||||
}
|
||||
Some(NONE_MARKER) => {
|
||||
if !data.is_empty() {
|
||||
data.truncate(data.len() - 1);
|
||||
}
|
||||
data
|
||||
}
|
||||
Some(_) => {
|
||||
trc::event!(Store(StoreEvent::BlobMissingMarker), Key = key);
|
||||
|
||||
data
|
||||
}
|
||||
None => {
|
||||
return Ok(Some(data));
|
||||
}
|
||||
};
|
||||
|
||||
if range.start == 0 {
|
||||
if range.end > data.len() {
|
||||
Ok(Some(data))
|
||||
} else {
|
||||
data.truncate(range.end);
|
||||
Ok(Some(data))
|
||||
}
|
||||
} else {
|
||||
Ok(Some(
|
||||
data.get(range.start..range.end)
|
||||
.unwrap_or_default()
|
||||
.to_vec(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_blob(
|
||||
&self,
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
compression: CompressionAlgo,
|
||||
) -> trc::Result<()> {
|
||||
let data = match compression {
|
||||
CompressionAlgo::None => {
|
||||
let mut uncompressed = Vec::with_capacity(data.len() + 1);
|
||||
uncompressed.extend_from_slice(data);
|
||||
uncompressed.push(NONE_MARKER);
|
||||
uncompressed
|
||||
}
|
||||
CompressionAlgo::Lz4 => {
|
||||
let mut compressed =
|
||||
vec![
|
||||
LZ4_MARKER;
|
||||
lz4_flex::block::get_maximum_output_size(data.len()) + U32_LEN + 1
|
||||
];
|
||||
|
||||
// Compress the data
|
||||
let compressed_len =
|
||||
lz4_flex::compress_into(data, &mut compressed[U32_LEN..]).unwrap();
|
||||
|
||||
// Prepend the length of the uncompressed data
|
||||
compressed[..U32_LEN].copy_from_slice(&(data.len() as u32).to_le_bytes());
|
||||
|
||||
// Truncate to the actual size
|
||||
compressed.truncate(compressed_len + U32_LEN + 1);
|
||||
compressed
|
||||
}
|
||||
};
|
||||
|
||||
let start_time = Instant::now();
|
||||
let result = match &self {
|
||||
BlobStore::Store(store) => match store {
|
||||
#[cfg(feature = "sqlite")]
|
||||
Store::SQLite(store) => store.put_blob(key, &data).await,
|
||||
#[cfg(feature = "foundation")]
|
||||
Store::FoundationDb(store) => store.put_blob(key, &data).await,
|
||||
#[cfg(feature = "postgres")]
|
||||
Store::PostgreSQL(store) => store.put_blob(key, &data).await,
|
||||
#[cfg(feature = "mysql")]
|
||||
Store::MySQL(store) => store.put_blob(key, &data).await,
|
||||
#[cfg(feature = "rocks")]
|
||||
Store::RocksDb(store) => store.put_blob(key, &data).await,
|
||||
Store::Ephemeral(store) => store.put_blob(key, &data).await,
|
||||
Store::None => Err(trc::StoreEvent::NotConfigured.into()),
|
||||
},
|
||||
BlobStore::Fs(store) => store.put_blob(key, &data).await,
|
||||
#[cfg(feature = "s3")]
|
||||
BlobStore::S3(store) => store.put_blob(key, &data).await,
|
||||
#[cfg(feature = "azure")]
|
||||
BlobStore::Azure(store) => store.put_blob(key, &data).await,
|
||||
}
|
||||
.caused_by(trc::location!());
|
||||
|
||||
trc::event!(
|
||||
Store(StoreEvent::BlobWrite),
|
||||
Key = key,
|
||||
Elapsed = start_time.elapsed(),
|
||||
Size = data.len(),
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn delete_blob(&self, key: &[u8]) -> trc::Result<bool> {
|
||||
let start_time = Instant::now();
|
||||
let result = match &self {
|
||||
BlobStore::Store(store) => match store {
|
||||
#[cfg(feature = "sqlite")]
|
||||
Store::SQLite(store) => store.delete_blob(key).await,
|
||||
#[cfg(feature = "foundation")]
|
||||
Store::FoundationDb(store) => store.delete_blob(key).await,
|
||||
#[cfg(feature = "postgres")]
|
||||
Store::PostgreSQL(store) => store.delete_blob(key).await,
|
||||
#[cfg(feature = "mysql")]
|
||||
Store::MySQL(store) => store.delete_blob(key).await,
|
||||
#[cfg(feature = "rocks")]
|
||||
Store::RocksDb(store) => store.delete_blob(key).await,
|
||||
Store::Ephemeral(store) => store.delete_blob(key).await,
|
||||
Store::None => Err(trc::StoreEvent::NotConfigured.into()),
|
||||
},
|
||||
BlobStore::Fs(store) => store.delete_blob(key).await,
|
||||
#[cfg(feature = "s3")]
|
||||
BlobStore::S3(store) => store.delete_blob(key).await,
|
||||
#[cfg(feature = "azure")]
|
||||
BlobStore::Azure(store) => store.delete_blob(key).await,
|
||||
}
|
||||
.caused_by(trc::location!());
|
||||
|
||||
trc::event!(
|
||||
Store(StoreEvent::BlobWrite),
|
||||
Key = key,
|
||||
Elapsed = start_time.elapsed(),
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user