use std::cmp::Ordering;
use std::convert::TryInto;
use std::fmt::Debug;
#[derive(Eq, PartialEq, Clone, Debug)]
enum TypeClassification {
Internal,
UserDefined,
}
impl TypeClassification {
fn to_byte(&self) -> u8 {
match self {
TypeClassification::Internal => 1,
TypeClassification::UserDefined => 2,
}
}
fn from_byte(value: u8) -> Self {
match value {
1 => TypeClassification::Internal,
2 => TypeClassification::UserDefined,
_ => unreachable!(),
}
}
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct TypeName {
classification: TypeClassification,
name: String,
}
impl TypeName {
pub fn new(name: &str) -> Self {
Self {
classification: TypeClassification::UserDefined,
name: name.to_string(),
}
}
pub(crate) fn internal(name: &str) -> Self {
Self {
classification: TypeClassification::Internal,
name: name.to_string(),
}
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(self.name.as_bytes().len() + 1);
result.push(self.classification.to_byte());
result.extend_from_slice(self.name.as_bytes());
result
}
pub(crate) fn from_bytes(bytes: &[u8]) -> Self {
let classification = TypeClassification::from_byte(bytes[0]);
let name = std::str::from_utf8(&bytes[1..]).unwrap().to_string();
Self {
classification,
name,
}
}
pub(crate) fn name(&self) -> &str {
&self.name
}
}
pub trait RedbValue: Debug {
type SelfType<'a>: Debug + 'a
where
Self: 'a;
type AsBytes<'a>: AsRef<[u8]> + 'a
where
Self: 'a;
fn fixed_width() -> Option<usize>;
fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
where
Self: 'a;
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
where
Self: 'a,
Self: 'b;
fn type_name() -> TypeName;
}
pub trait RedbValueMutInPlace: RedbValue {
type BaseRefType: Debug + ?Sized;
fn initialize(data: &mut [u8]);
fn from_bytes_mut(data: &mut [u8]) -> &mut Self::BaseRefType;
}
impl RedbValueMutInPlace for &[u8] {
type BaseRefType = [u8];
fn initialize(_data: &mut [u8]) {
}
fn from_bytes_mut(data: &mut [u8]) -> &mut Self::BaseRefType {
data
}
}
pub trait RedbKey: RedbValue {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering;
}
impl RedbValue for () {
type SelfType<'a> = ()
where
Self: 'a;
type AsBytes<'a> = &'a [u8]
where
Self: 'a;
fn fixed_width() -> Option<usize> {
Some(0)
}
#[allow(clippy::unused_unit)]
fn from_bytes<'a>(_data: &'a [u8]) -> ()
where
Self: 'a,
{
()
}
fn as_bytes<'a, 'b: 'a>(_: &'a Self::SelfType<'b>) -> &'a [u8]
where
Self: 'a,
Self: 'b,
{
&[]
}
fn type_name() -> TypeName {
TypeName::internal("()")
}
}
impl RedbKey for () {
fn compare(_data1: &[u8], _data2: &[u8]) -> Ordering {
Ordering::Equal
}
}
impl<T: RedbValue> RedbValue for Option<T> {
type SelfType<'a> = Option<T::SelfType<'a>>
where
Self: 'a;
type AsBytes<'a> = Vec<u8>
where
Self: 'a;
fn fixed_width() -> Option<usize> {
T::fixed_width().map(|x| x + 1)
}
fn from_bytes<'a>(data: &'a [u8]) -> Option<T::SelfType<'a>>
where
Self: 'a,
{
match data[0] {
0 => None,
1 => Some(T::from_bytes(&data[1..])),
_ => unreachable!(),
}
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Vec<u8>
where
Self: 'a,
Self: 'b,
{
let mut result = vec![0];
if let Some(x) = value {
result[0] = 1;
result.extend_from_slice(T::as_bytes(x).as_ref());
}
result
}
fn type_name() -> TypeName {
TypeName::internal(&format!("Option<{}>", T::type_name().name()))
}
}
impl RedbValue for &[u8] {
type SelfType<'a> = &'a [u8]
where
Self: 'a;
type AsBytes<'a> = &'a [u8]
where
Self: 'a;
fn fixed_width() -> Option<usize> {
None
}
fn from_bytes<'a>(data: &'a [u8]) -> &'a [u8]
where
Self: 'a,
{
data
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> &'a [u8]
where
Self: 'a,
Self: 'b,
{
value
}
fn type_name() -> TypeName {
TypeName::internal("&[u8]")
}
}
impl RedbKey for &[u8] {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
data1.cmp(data2)
}
}
impl<const N: usize> RedbValue for &[u8; N] {
type SelfType<'a> = &'a [u8; N]
where
Self: 'a;
type AsBytes<'a> = &'a [u8; N]
where
Self: 'a;
fn fixed_width() -> Option<usize> {
Some(N)
}
fn from_bytes<'a>(data: &'a [u8]) -> &'a [u8; N]
where
Self: 'a,
{
data.try_into().unwrap()
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> &'a [u8; N]
where
Self: 'a,
Self: 'b,
{
value
}
fn type_name() -> TypeName {
TypeName::internal(&format!("[u8;{N}]"))
}
}
impl<const N: usize> RedbKey for &[u8; N] {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
data1.cmp(data2)
}
}
impl RedbValue for &str {
type SelfType<'a> = &'a str
where
Self: 'a;
type AsBytes<'a> = &'a str
where
Self: 'a;
fn fixed_width() -> Option<usize> {
None
}
fn from_bytes<'a>(data: &'a [u8]) -> &'a str
where
Self: 'a,
{
std::str::from_utf8(data).unwrap()
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> &'a str
where
Self: 'a,
Self: 'b,
{
value
}
fn type_name() -> TypeName {
TypeName::internal("&str")
}
}
impl RedbKey for &str {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
let str1 = Self::from_bytes(data1);
let str2 = Self::from_bytes(data2);
str1.cmp(str2)
}
}
macro_rules! be_value {
($t:ty) => {
impl RedbValue for $t {
type SelfType<'a> = $t;
type AsBytes<'a> = [u8; std::mem::size_of::<$t>()] where Self: 'a;
fn fixed_width() -> Option<usize> {
Some(std::mem::size_of::<$t>())
}
fn from_bytes<'a>(data: &'a [u8]) -> $t
where
Self: 'a,
{
<$t>::from_le_bytes(data.try_into().unwrap())
}
fn as_bytes<'a, 'b: 'a>(
value: &'a Self::SelfType<'b>,
) -> [u8; std::mem::size_of::<$t>()]
where
Self: 'a,
Self: 'b,
{
value.to_le_bytes()
}
fn type_name() -> TypeName {
TypeName::internal(stringify!($t))
}
}
};
}
macro_rules! be_impl {
($t:ty) => {
be_value!($t);
impl RedbKey for $t {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
Self::from_bytes(data1).cmp(&Self::from_bytes(data2))
}
}
};
}
be_impl!(u8);
be_impl!(u16);
be_impl!(u32);
be_impl!(u64);
be_impl!(u128);
be_impl!(i8);
be_impl!(i16);
be_impl!(i32);
be_impl!(i64);
be_impl!(i128);
be_value!(f32);
be_value!(f64);