Skip to content

Commit

Permalink
chore(store, collections): replace manual borsh trait impl-s with d…
Browse files Browse the repository at this point in the history
…erives and correct bounds within (#1176)
  • Loading branch information
dj8yfo authored May 1, 2024
1 parent d1702ce commit 53d5305
Show file tree
Hide file tree
Showing 15 changed files with 85 additions and 147 deletions.
2 changes: 1 addition & 1 deletion near-sdk/compilation_tests/schema_derive_invalids.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ error[E0277]: the trait bound `Inner: JsonSchema` is not satisfied
i128
and $N others
note: required by a bound in `SchemaGenerator::subschema_for`
--> $CARGO/schemars-0.8.16/src/gen.rs
--> $CARGO/schemars-0.8.17/src/gen.rs
|
| pub fn subschema_for<T: ?Sized + JsonSchema>(&mut self) -> Schema {
| ^^^^^^^^^^ required by this bound in `SchemaGenerator::subschema_for`
12 changes: 12 additions & 0 deletions near-sdk/src/collections/legacy_tree_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@ use crate::IntoStorageKey;
#[near(inside_nearsdk)]
pub struct LegacyTreeMap<K, V> {
root: u64,
// ser/de is independent of `K`,`V` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
val: UnorderedMap<K, V>,
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
tree: Vector<Node<K>>,
}

Expand Down
12 changes: 12 additions & 0 deletions near-sdk/src/collections/tree_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,19 @@ use near_sdk_macros::near;
#[near(inside_nearsdk)]
pub struct TreeMap<K, V> {
root: u64,
// ser/de is independent of `K`,`V` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
val: LookupMap<K, V>,
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
tree: Vector<Node<K>>,
}

Expand Down
12 changes: 12 additions & 0 deletions near-sdk/src/collections/unordered_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,19 @@ const ERR_VALUE_SERIALIZATION: &str = "Cannot serialize value with Borsh";
#[near(inside_nearsdk)]
pub struct UnorderedMap<K, V> {
key_index_prefix: Vec<u8>,
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
keys: Vector<K>,
// ser/de is independent of `V` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
values: Vector<V>,
}

Expand Down
6 changes: 6 additions & 0 deletions near-sdk/src/collections/unordered_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const ERR_ELEMENT_SERIALIZATION: &str = "Cannot serialize element with Borsh";
#[near(inside_nearsdk)]
pub struct UnorderedSet<T> {
element_index_prefix: Vec<u8>,
// ser/de is independent of `T` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
elements: Vector<T>,
}

Expand Down
35 changes: 7 additions & 28 deletions near-sdk/src/store/free_list/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct FreeListIndex(pub(crate) u32);
/// Unordered container of values. This is similar to [`Vector`] except that values are not
/// re-arranged on removal, keeping the indices consistent. When an element is removed, it will
/// be replaced with an empty cell which will be populated on the next insertion.
#[derive(NearSchema)]
#[derive(NearSchema, BorshSerialize, BorshDeserialize)]
#[inside_nearsdk]
#[abi(borsh)]
pub(crate) struct FreeList<T>
Expand All @@ -26,36 +26,15 @@ where
{
first_free: Option<FreeListIndex>,
occupied_count: u32,
// ser/de is independent of `T` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
elements: Vector<Slot<T>>,
}

//? Manual implementations needed only because borsh derive is leaking field types
// https://github.com/near/borsh-rs/issues/41
impl<T> BorshSerialize for FreeList<T>
where
T: BorshSerialize,
{
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
BorshSerialize::serialize(&self.first_free, writer)?;
BorshSerialize::serialize(&self.occupied_count, writer)?;
BorshSerialize::serialize(&self.elements, writer)?;
Ok(())
}
}

impl<T> BorshDeserialize for FreeList<T>
where
T: BorshSerialize,
{
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
first_free: BorshDeserialize::deserialize_reader(reader)?,
occupied_count: BorshDeserialize::deserialize_reader(reader)?,
elements: BorshDeserialize::deserialize_reader(reader)?,
})
}
}

#[near(inside_nearsdk)]
#[derive(Debug)]
enum Slot<T> {
Expand Down
2 changes: 1 addition & 1 deletion near-sdk/src/store/index_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ where
///
/// Note: u32 indices are used over usize to have consistent functionality across architectures.
/// Some functionality would be different from tests to Wasm if exceeding 32-bit length.
#[borsh(skip, bound(deserialize = ""))]
#[borsh(skip, bound(deserialize = ""))] // removes `core::default::Default` bound from T
pub(crate) cache: StableMap<u32, OnceCell<CacheEntry<T>>>,
}

Expand Down
35 changes: 6 additions & 29 deletions near-sdk/src/store/iterable_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ use super::{LookupMap, ERR_INCONSISTENT_STATE, ERR_NOT_EXIST};
/// ```
///
/// [`with_hasher`]: Self::with_hasher
#[derive(BorshDeserialize, BorshSerialize)]
pub struct IterableMap<K, V, H = Sha256>
where
K: BorshSerialize + Ord,
Expand All @@ -91,7 +92,12 @@ where
// not skipping empty/unoccupied entries white trying to get to the next element.
// See https://github.com/near/near-sdk-rs/issues/1134 to understand the difference between
// `store::UnorderedMap` and `store::IterableMap`.

// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
keys: Vector<K>,
// ser/de is independent of `K`, `V`, `H` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
values: LookupMap<K, ValueAndIndex<V>, H>,
}

Expand All @@ -101,35 +107,6 @@ struct ValueAndIndex<V> {
key_index: u32,
}

//? Manual implementations needed only because borsh derive is leaking field types
// https://github.com/near/borsh-rs/issues/41
impl<K, V, H> BorshSerialize for IterableMap<K, V, H>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
BorshSerialize::serialize(&self.keys, writer)?;
BorshSerialize::serialize(&self.values, writer)?;
Ok(())
}
}

impl<K, V, H> BorshDeserialize for IterableMap<K, V, H>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
keys: BorshDeserialize::deserialize_reader(reader)?,
values: BorshDeserialize::deserialize_reader(reader)?,
})
}
}

impl<K, V, H> Drop for IterableMap<K, V, H>
where
K: BorshSerialize + Ord,
Expand Down
2 changes: 1 addition & 1 deletion near-sdk/src/store/lazy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ where
{
/// Key bytes to index the contract's storage.
storage_key: Box<[u8]>,
#[borsh(skip, bound(deserialize = ""))]
#[borsh(skip, bound(deserialize = ""))] // removes `core::default::Default` bound from T
/// Cached value which is lazily loaded and deserialized from storage.
cache: OnceCell<CacheEntry<T>>,
}
Expand Down
2 changes: 1 addition & 1 deletion near-sdk/src/store/lazy_option/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ where
prefix: Box<[u8]>,

/// Cached value which is lazily loaded and deserialized from storage.
#[borsh(skip, bound(deserialize = ""))]
#[borsh(skip, bound(deserialize = ""))] // removes `core::default::Default` bound from T
cache: OnceCell<CacheEntry<T>>,
}

Expand Down
2 changes: 1 addition & 1 deletion near-sdk/src/store/lookup_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ where
/// Cache for loads and intermediate changes to the underlying vector.
/// The cached entries are wrapped in a [`Box`] to avoid existing pointers from being
/// invalidated.
#[borsh(skip, bound(deserialize = ""))]
#[borsh(skip, bound(deserialize = ""))] // removes `core::default::Default` from `K`/`V`
cache: StableMap<K, EntryAndHash<V, H::KeyType>>,
}

Expand Down
41 changes: 11 additions & 30 deletions near-sdk/src/store/tree_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ fn expect<T>(val: Option<T>) -> T {
/// - `min`/`max`: O(log(N))
/// - `above`/`below`: O(log(N))
/// - `range` of K elements: O(Klog(N))
#[derive(BorshDeserialize, BorshSerialize)]
pub struct TreeMap<K, V, H = Sha256>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
// ser/de is independent of `K`, `V`, `H` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
values: LookupMap<K, V, H>,
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
tree: Tree<K>,
}

Expand Down Expand Up @@ -65,42 +70,18 @@ where
}
}

//? Manual implementations needed only because borsh derive is leaking field types
// https://github.com/near/borsh-rs/issues/41
impl<K, V, H> BorshSerialize for TreeMap<K, V, H>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
BorshSerialize::serialize(&self.values, writer)?;
BorshSerialize::serialize(&self.tree, writer)?;
Ok(())
}
}

impl<K, V, H> BorshDeserialize for TreeMap<K, V, H>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
values: BorshDeserialize::deserialize_reader(reader)?,
tree: BorshDeserialize::deserialize_reader(reader)?,
})
}
}

#[near(inside_nearsdk)]
struct Tree<K>
where
K: BorshSerialize,
{
root: Option<FreeListIndex>,
#[borsh(bound(deserialize = ""))]
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize`/`BorshSchema` bounds removed
#[cfg_attr(not(feature = "abi"), borsh(bound(serialize = "", deserialize = "")))]
#[cfg_attr(
feature = "abi",
borsh(bound(serialize = "", deserialize = ""), schema(params = ""))
)]
nodes: FreeList<Node<K>>,
}

Expand Down
34 changes: 5 additions & 29 deletions near-sdk/src/store/unordered_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,18 @@ use super::{FreeList, LookupMap, ERR_INCONSISTENT_STATE, ERR_NOT_EXIST};
since = "5.0.0",
note = "Suboptimal iteration performance. See performance considerations doc for details."
)]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct UnorderedMap<K, V, H = Sha256>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
// ser/de is independent of `K` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
keys: FreeList<K>,
// ser/de is independent of `K`, `V`, `H` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
values: LookupMap<K, ValueAndIndex<V>, H>,
}

Expand All @@ -106,35 +111,6 @@ struct ValueAndIndex<V> {
key_index: FreeListIndex,
}

//? Manual implementations needed only because borsh derive is leaking field types
// https://github.com/near/borsh-rs/issues/41
impl<K, V, H> BorshSerialize for UnorderedMap<K, V, H>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
BorshSerialize::serialize(&self.keys, writer)?;
BorshSerialize::serialize(&self.values, writer)?;
Ok(())
}
}

impl<K, V, H> BorshDeserialize for UnorderedMap<K, V, H>
where
K: BorshSerialize + Ord,
V: BorshSerialize,
H: ToKey,
{
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> Result<Self, std::io::Error> {
Ok(Self {
keys: BorshDeserialize::deserialize_reader(reader)?,
values: BorshDeserialize::deserialize_reader(reader)?,
})
}
}

impl<K, V, H> Drop for UnorderedMap<K, V, H>
where
K: BorshSerialize + Ord,
Expand Down
2 changes: 2 additions & 0 deletions near-sdk/src/store/unordered_set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ where
T: BorshSerialize + Ord,
H: ToKey,
{
// ser/de is independent of `T` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
elements: FreeList<T>,
// ser/de is independent of `T`, `H` ser/de, `BorshSerialize`/`BorshDeserialize` bounds removed
#[borsh(bound(serialize = "", deserialize = ""))]
index: LookupMap<T, FreeListIndex, H>,
}
Expand Down
Loading

0 comments on commit 53d5305

Please sign in to comment.