use std::collections::BTreeMap;
use std::fmt::Write as _;
use std::future::{Future, ready};
use std::sync::{Arc, Mutex, PoisonError};
use crate::error::{WriteError, WriteResult};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Listed {
pub uri: String,
pub size: u64,
}
pub trait Host {
type Upload: Upload;
fn get(&self, uri: &str) -> impl Future<Output = WriteResult<Option<Vec<u8>>>>;
fn get_range(&self, uri: &str, start: u64, len: u64) -> impl Future<Output = WriteResult<Vec<u8>>>;
fn exists(&self, uri: &str) -> impl Future<Output = WriteResult<bool>>;
fn put(&self, uri: &str, bytes: Vec<u8>) -> impl Future<Output = WriteResult<()>>;
fn put_if_absent(&self, uri: &str, bytes: Vec<u8>) -> impl Future<Output = WriteResult<bool>>;
fn upload(&self, uri: &str) -> impl Future<Output = WriteResult<Self::Upload>>;
fn list(&self, prefix: &str) -> impl Future<Output = WriteResult<Vec<Listed>>>;
fn delete(&self, uris: &[String]) -> impl Future<Output = WriteResult<()>>;
fn now_ms(&self) -> i64;
fn random(&self) -> u64;
fn sleep(&self, ms: u64) -> impl Future<Output = ()>;
fn max_row_group_bytes(&self) -> Option<usize> {
None
}
fn data_page_rows(&self) -> usize {
crate::file::DATA_PAGE_ROWS
}
}
pub trait Upload {
fn put_part(&mut self, bytes: Vec<u8>) -> impl Future<Output = WriteResult<()>>;
fn complete(self) -> impl Future<Output = WriteResult<()>>;
fn abort(self) -> impl Future<Output = ()>;
}
#[derive(Clone, Debug, Default)]
pub struct MemoryHost {
objects: Arc<Mutex<BTreeMap<String, Vec<u8>>>>,
clock: Arc<Mutex<i64>>,
seed: Arc<Mutex<u64>>,
}
impl MemoryHost {
pub fn new(now_ms: i64) -> Self {
let host = Self::default();
*host.clock.lock().unwrap_or_else(PoisonError::into_inner) = now_ms;
host
}
pub fn advance(&self, ms: i64) {
*self.clock.lock().unwrap_or_else(PoisonError::into_inner) += ms;
}
pub fn uris(&self) -> Vec<String> {
self.objects().keys().cloned().collect()
}
fn objects(&self) -> std::sync::MutexGuard<'_, BTreeMap<String, Vec<u8>>> {
self.objects.lock().unwrap_or_else(PoisonError::into_inner)
}
}
#[derive(Debug)]
pub struct MemoryUpload {
host: MemoryHost,
uri: String,
bytes: Vec<u8>,
}
impl Upload for MemoryUpload {
fn put_part(&mut self, bytes: Vec<u8>) -> impl Future<Output = WriteResult<()>> {
self.bytes.extend(bytes);
ready(Ok(()))
}
fn complete(self) -> impl Future<Output = WriteResult<()>> {
self.host.objects().insert(self.uri, self.bytes);
ready(Ok(()))
}
fn abort(self) -> impl Future<Output = ()> {
ready(())
}
}
impl Host for MemoryHost {
type Upload = MemoryUpload;
fn get(&self, uri: &str) -> impl Future<Output = WriteResult<Option<Vec<u8>>>> {
ready(Ok(self.objects().get(uri).cloned()))
}
fn get_range(&self, uri: &str, start: u64, len: u64) -> impl Future<Output = WriteResult<Vec<u8>>> {
let objects = self.objects();
ready(objects.get(uri).ok_or_else(|| WriteError::NotFound(uri.to_string())).map(|bytes| {
let start = usize::try_from(start).unwrap_or(usize::MAX).min(bytes.len());
let end = start.saturating_add(usize::try_from(len).unwrap_or(usize::MAX)).min(bytes.len());
bytes[start..end].to_vec()
}))
}
fn exists(&self, uri: &str) -> impl Future<Output = WriteResult<bool>> {
ready(Ok(self.objects().contains_key(uri)))
}
fn put(&self, uri: &str, bytes: Vec<u8>) -> impl Future<Output = WriteResult<()>> {
self.objects().insert(uri.to_string(), bytes);
ready(Ok(()))
}
fn put_if_absent(&self, uri: &str, bytes: Vec<u8>) -> impl Future<Output = WriteResult<bool>> {
let mut objects = self.objects();
let absent = !objects.contains_key(uri);
if absent {
objects.insert(uri.to_string(), bytes);
}
ready(Ok(absent))
}
fn upload(&self, uri: &str) -> impl Future<Output = WriteResult<MemoryUpload>> {
ready(Ok(MemoryUpload { host: self.clone(), uri: uri.to_string(), bytes: vec![] }))
}
fn list(&self, prefix: &str) -> impl Future<Output = WriteResult<Vec<Listed>>> {
let listed = self
.objects()
.range(prefix.to_string()..)
.take_while(|(k, _)| k.starts_with(prefix))
.map(|(k, v)| Listed { uri: k.clone(), size: v.len() as u64 })
.collect();
ready(Ok(listed))
}
fn delete(&self, uris: &[String]) -> impl Future<Output = WriteResult<()>> {
let mut objects = self.objects();
for uri in uris {
objects.remove(uri);
}
ready(Ok(()))
}
fn now_ms(&self) -> i64 {
*self.clock.lock().unwrap_or_else(PoisonError::into_inner)
}
fn random(&self) -> u64 {
let mut seed = self.seed.lock().unwrap_or_else(PoisonError::into_inner);
*seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1_442_695_040_888_963_407);
*seed ^ (*seed >> 29)
}
fn sleep(&self, ms: u64) -> impl Future<Output = ()> {
self.advance(i64::try_from(ms).unwrap_or(i64::MAX));
ready(())
}
}
pub fn uuid_v4(host: &impl Host) -> String {
let mut b = [0u8; 16];
b[..8].copy_from_slice(&host.random().to_be_bytes());
b[8..].copy_from_slice(&host.random().to_be_bytes());
b[6] = (b[6] & 0x0f) | 0x40;
b[8] = (b[8] & 0x3f) | 0x80;
format_uuid(&b)
}
pub fn uuid_v7(host: &impl Host) -> String {
let mut b = [0u8; 16];
let ms = u64::try_from(host.now_ms()).unwrap_or(0);
b[..6].copy_from_slice(&ms.to_be_bytes()[2..]);
b[6..14].copy_from_slice(&host.random().to_be_bytes());
b[14..].copy_from_slice(&host.random().to_be_bytes()[..2]);
b[6] = (b[6] & 0x0f) | 0x70;
b[8] = (b[8] & 0x3f) | 0x80;
format_uuid(&b)
}
fn format_uuid(b: &[u8; 16]) -> String {
let hex = b.iter().fold(String::with_capacity(32), |mut s, x| {
let _ = write!(s, "{x:02x}");
s
});
format!("{}-{}-{}-{}-{}", &hex[..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uuids_are_well_formed() {
let host = MemoryHost::new(1_767_225_600_000);
let v4 = uuid_v4(&host);
let v7 = uuid_v7(&host);
for (u, version) in [(&v4, '4'), (&v7, '7')] {
assert_eq!(u.len(), 36);
assert_eq!(u.chars().nth(14), Some(version));
assert!(matches!(u.chars().nth(19), Some('8' | '9' | 'a' | 'b')));
}
host.advance(1);
assert!(uuid_v7(&host) > v7);
}
}