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 MutInPlaceValue: RedbValue {
type BaseRefType: Debug + ?Sized;
fn initialize(data: &mut [u8]);
fn from_bytes_mut(data: &mut [u8]) -> &mut Self::BaseRefType;
}
impl MutInPlaceValue 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 RedbValue for bool {
type SelfType<'a> = bool
where
Self: 'a;
type AsBytes<'a> = &'a [u8]
where
Self: 'a;
fn fixed_width() -> Option<usize> {
Some(1)
}
fn from_bytes<'a>(data: &'a [u8]) -> bool
where
Self: 'a,
{
match data[0] {
0 => false,
1 => true,
_ => unreachable!(),
}
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> &'a [u8]
where
Self: 'a,
Self: 'b,
{
match value {
true => &[1],
false => &[0],
}
}
fn type_name() -> TypeName {
TypeName::internal("bool")
}
}
impl RedbKey for bool {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
let value1 = Self::from_bytes(data1);
let value2 = Self::from_bytes(data2);
value1.cmp(&value2)
}
}
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());
} else if let Some(fixed_width) = T::fixed_width() {
result.extend_from_slice(&vec![0; fixed_width]);
}
result
}
fn type_name() -> TypeName {
TypeName::internal(&format!("Option<{}>", T::type_name().name()))
}
}
impl<T: RedbKey> RedbKey for Option<T> {
#[allow(clippy::collapsible_else_if)]
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
if data1[0] == 0 {
if data2[0] == 0 {
Ordering::Equal
} else {
Ordering::Less
}
} else {
if data2[0] == 0 {
Ordering::Greater
} else {
T::compare(&data1[1..], &data2[1..])
}
}
}
}
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)
}
}
impl RedbValue for char {
type SelfType<'a> = char;
type AsBytes<'a> = [u8; 3] where Self: 'a;
fn fixed_width() -> Option<usize> {
Some(3)
}
fn from_bytes<'a>(data: &'a [u8]) -> char
where
Self: 'a,
{
char::from_u32(u32::from_le_bytes([data[0], data[1], data[2], 0])).unwrap()
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> [u8; 3]
where
Self: 'a,
Self: 'b,
{
let bytes = u32::from(*value).to_le_bytes();
[bytes[0], bytes[1], bytes[2]]
}
fn type_name() -> TypeName {
TypeName::internal(stringify!(char))
}
}
impl RedbKey for char {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
Self::from_bytes(data1).cmp(&Self::from_bytes(data2))
}
}
macro_rules! le_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! le_impl {
($t:ty) => {
le_value!($t);
impl RedbKey for $t {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
Self::from_bytes(data1).cmp(&Self::from_bytes(data2))
}
}
};
}
le_impl!(u8);
le_impl!(u16);
le_impl!(u32);
le_impl!(u64);
le_impl!(u128);
le_impl!(i8);
le_impl!(i16);
le_impl!(i32);
le_impl!(i64);
le_impl!(i128);
le_value!(f32);
le_value!(f64);