-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from CosmWasm/map-fixed-length-keys
Map key redesign
- Loading branch information
Showing
9 changed files
with
468 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
/// A key that can be used with a [`Map`](crate::Map). | ||
pub trait Key { | ||
/// The kind of key, meaning either fixed size or dynamic size. | ||
type Kind: KeyKind; | ||
|
||
/// Encode the key into a byte vector. | ||
fn encode(&self) -> Vec<u8>; | ||
} | ||
|
||
/// An owned key that can be used with a [`Map`](crate::Map). | ||
pub trait OwnedKey: Key { | ||
/// The error type that can occur when decoding the key. | ||
type Error; | ||
|
||
/// Decode the key from a byte slice. | ||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized; | ||
} | ||
|
||
impl Key for String { | ||
type Kind = DynamicKey; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.as_bytes().to_vec() | ||
} | ||
} | ||
|
||
impl Key for Box<str> { | ||
type Kind = DynamicKey; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.as_bytes().to_vec() | ||
} | ||
} | ||
|
||
impl Key for str { | ||
type Kind = DynamicKey; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.as_bytes().to_vec() | ||
} | ||
} | ||
|
||
/// An error type representing a failure to decode a UTF-8 string. | ||
#[derive(Debug, PartialEq, Eq, Clone, Copy, thiserror::Error)] | ||
#[error("invalid UTF8")] | ||
pub struct InvalidUtf8; | ||
|
||
impl OwnedKey for String { | ||
type Error = InvalidUtf8; | ||
|
||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized, | ||
{ | ||
std::str::from_utf8(bytes) | ||
.map(String::from) | ||
.map_err(|_| InvalidUtf8) | ||
} | ||
} | ||
|
||
impl OwnedKey for Box<str> { | ||
type Error = InvalidUtf8; | ||
|
||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized, | ||
{ | ||
std::str::from_utf8(bytes) | ||
.map(Box::from) | ||
.map_err(|_| InvalidUtf8) | ||
} | ||
} | ||
|
||
impl Key for Vec<u8> { | ||
type Kind = DynamicKey; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.clone() | ||
} | ||
} | ||
|
||
impl Key for Box<[u8]> { | ||
type Kind = DynamicKey; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.to_vec() | ||
} | ||
} | ||
|
||
impl Key for [u8] { | ||
type Kind = DynamicKey; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.to_vec() | ||
} | ||
} | ||
|
||
impl<const N: usize> Key for [u8; N] { | ||
type Kind = FixedSizeKey<N>; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.to_vec() | ||
} | ||
} | ||
|
||
impl OwnedKey for Vec<u8> { | ||
type Error = (); | ||
|
||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized, | ||
{ | ||
Ok(bytes.to_vec()) | ||
} | ||
} | ||
|
||
impl OwnedKey for Box<[u8]> { | ||
type Error = (); | ||
|
||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized, | ||
{ | ||
Ok(bytes.to_vec().into_boxed_slice()) | ||
} | ||
} | ||
|
||
/// An error type for decoding arrays. | ||
pub enum ArrayDecodeError { | ||
InvalidLength, | ||
} | ||
|
||
impl<const N: usize> OwnedKey for [u8; N] { | ||
type Error = ArrayDecodeError; | ||
|
||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized, | ||
{ | ||
if bytes.len() != N { | ||
return Err(ArrayDecodeError::InvalidLength); | ||
} | ||
|
||
let mut buf = [0; N]; | ||
buf.copy_from_slice(bytes); | ||
Ok(buf) | ||
} | ||
} | ||
|
||
/// A trait specifying the kind of key. | ||
/// | ||
/// There are two kinds of keys: fixed-size keys and dynamic keys, which are | ||
/// represented by the [`FixedSizeKey`] and [`DynamicKey`] types, respectively. | ||
/// | ||
/// This trait is [sealed](https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits) | ||
/// and cannot be implemented outside of this crate. | ||
pub trait KeyKind: sealed::KeyKindSeal {} | ||
|
||
/// A marker type representing a fixed-size key. | ||
pub struct FixedSizeKey<const L: usize>; | ||
|
||
/// A marker type representing a dynamic-size key. | ||
pub struct DynamicKey; | ||
|
||
impl<const L: usize> KeyKind for FixedSizeKey<L> {} | ||
impl KeyKind for DynamicKey {} | ||
|
||
mod sealed { | ||
pub trait KeyKindSeal {} | ||
|
||
impl<const L: usize> KeyKindSeal for super::FixedSizeKey<L> {} | ||
impl KeyKindSeal for super::DynamicKey {} | ||
} | ||
|
||
/// An error type for decoding numeric keys. | ||
pub enum NumericKeyDecodeError { | ||
InvalidLength, | ||
} | ||
|
||
macro_rules! impl_key_for_numeric { | ||
($($t:ty),*) => { | ||
$( | ||
impl Key for $t { | ||
type Kind = FixedSizeKey<{(Self::BITS / 8) as usize}>; | ||
|
||
fn encode(&self) -> Vec<u8> { | ||
self.to_be_bytes().to_vec() | ||
} | ||
} | ||
|
||
impl OwnedKey for $t { | ||
type Error = NumericKeyDecodeError; | ||
|
||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> | ||
where | ||
Self: Sized, | ||
{ | ||
if bytes.len() != std::mem::size_of::<Self>() { | ||
return Err(NumericKeyDecodeError::InvalidLength); | ||
} | ||
|
||
let mut buf = [0; std::mem::size_of::<Self>()]; | ||
buf.copy_from_slice(bytes); | ||
Ok(Self::from_be_bytes(buf)) | ||
} | ||
} | ||
)* | ||
}; | ||
} | ||
|
||
impl_key_for_numeric!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
use crate::containers::{NonTerminal, Terminal}; | ||
|
||
use super::key::{DynamicKey, FixedSizeKey}; | ||
|
||
/// A trait that specifies the encoding strategy for a key. | ||
/// | ||
/// This trait is implemented on tuples of the form `(K, C)` where `K` is the key type (dynamic/fixed) | ||
/// and `C` is the container type (terminal/nonterminal). Once we know these two properties, we can | ||
/// determine the encoding strategy for the key. | ||
/// | ||
/// Scenarios: | ||
/// - If the key is dynamic and the container is nonterminal, then the key needs to be | ||
/// length prefixed - otherwise, we would not know where the key ends and the key for the inner | ||
/// container starts. | ||
/// - If the key is dynamic and the container is terminal, then the key is the rest of the string. | ||
/// - If the key is fixed size, then we statically provide the number of bytes to read/write. | ||
pub trait KeyEncodingT { | ||
const BEHAVIOR: KeyEncoding; | ||
} | ||
|
||
impl KeyEncodingT for (DynamicKey, NonTerminal) { | ||
const BEHAVIOR: KeyEncoding = KeyEncoding::LenPrefix; | ||
} | ||
|
||
impl<const L: usize> KeyEncodingT for (FixedSizeKey<L>, Terminal) { | ||
const BEHAVIOR: KeyEncoding = KeyEncoding::UseRest; | ||
} | ||
|
||
impl KeyEncodingT for (DynamicKey, Terminal) { | ||
const BEHAVIOR: KeyEncoding = KeyEncoding::UseRest; | ||
} | ||
|
||
impl<const L: usize> KeyEncodingT for (FixedSizeKey<L>, NonTerminal) { | ||
const BEHAVIOR: KeyEncoding = KeyEncoding::UseN(L); | ||
} | ||
|
||
/// The encoding strategy for a given key. | ||
pub enum KeyEncoding { | ||
/// The key needs to be length prefixed. | ||
LenPrefix, | ||
/// The key doesn't need to be length prefixed. The rest of the string is the key. | ||
UseRest, | ||
/// The key is of fixed size. | ||
UseN(usize), | ||
} |
Oops, something went wrong.