Skip to content

Commit

Permalink
* Changing the .read() method for StorageHandle to unwrap internally
Browse files Browse the repository at this point in the history
* Added `try_read()` that behaves like `read` but returns an `Option`
* Changed `get` in `StorageMap` to return a `StorageHandle` instead of
  the value directly.
  • Loading branch information
mohammadfawaz committed Mar 24, 2023
1 parent 80fff41 commit 648b2f0
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 113 deletions.
68 changes: 49 additions & 19 deletions sway-lib-std/src/experimental/storage.sw
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::experimental::storage::StorageHandle;
///
/// * `key` - The storage slot at which the variable will be stored.
/// * `value` - The value to be stored.
/// * `offset` - An offset, in words, from the beginning of slot at `key`, at which `value` should
/// * `offset` - An offset, in words, from the beginning of slot at `key`, at which `value` should
/// be stored.
///
/// ### Examples
Expand Down Expand Up @@ -46,16 +46,16 @@ pub fn write<T>(key: b256, offset: u64, value: T) {
let _ = __state_store_quad(key, padded_value, number_of_slots);
}

/// Reads a value of type `T` starting at the location specified by `key` and `offset`. If the
/// Reads a value of type `T` starting at the location specified by `key` and `offset`. If the
/// value crosses the boundary of a storage slot, reading continues at the following slot.
///
/// Returns `Option(value)` if a the storage slots read were valid and contain `value`.
/// Returns `Option(value)` if a the storage slots read were valid and contain `value`.
/// Otherwise, return `None`.
///
/// ### Arguments
///
/// * `key` - The storage slot to load the value from.
/// * `offset` - An offset, in words, from the start of slot at `key`, from which the value should
/// * `offset` - An offset, in words, from the start of slot at `key`, from which the value should
/// be read.
///
/// ### Examples
Expand Down Expand Up @@ -113,10 +113,38 @@ pub fn clear<T>(key: b256) -> bool {
}

impl<T> StorageHandle<T> {
/// Reads a value of type `T` starting at the location specified by `self`. If the value
/// Reads a value of type `T` starting at the location specified by `self`. If the value
/// crosses the boundary of a storage slot, reading continues at the following slot.
///
/// Returns `Option(value)` if a the storage slots read were valid and contain `value`.
/// Returns the value previously stored if a the storage slots read were
/// valid and contain `value`. Panics otherwise.
///
/// ### Arguments
///
/// None
///
/// ### Examples
///
/// ```sway
/// fn foo() {
/// let r: StorageHandle<u64> = StorageHandle {
/// key: 0x0000000000000000000000000000000000000000000000000000000000000000,
/// offset: 2,
/// };
///
/// // Reads the third word from storage slot with key 0x000...0
/// let x:u64 = r.read();
/// }
/// ```
#[storage(read)]
pub fn read(self) -> T {
read::<T>(self.key, self.offset).unwrap()
}

/// Reads a value of type `T` starting at the location specified by `self`. If the value
/// crosses the boundary of a storage slot, reading continues at the following slot.
///
/// Returns `Option(value)` if a the storage slots read were valid and contain `value`.
/// Otherwise, return `None`.
///
/// ### Arguments
Expand All @@ -129,17 +157,19 @@ impl<T> StorageHandle<T> {
/// fn foo() {
/// let r: StorageHandle<u64> = StorageHandle {
/// key: 0x0000000000000000000000000000000000000000000000000000000000000000,
/// offset: 2,
/// offset: 2,
/// };
/// let x = r.read(); // Reads the third word from storage slot with key 0x000...0
///
/// // Reads the third word from storage slot with key 0x000...0
/// let x:Option<u64> = r.try_read();
/// }
/// ```
#[storage(read)]
pub fn read(self) -> Option<T> {
pub fn try_read(self) -> Option<T> {
read(self.key, self.offset)
}

/// Writes a value of type `T` starting at the location specified by `self`. If the value
/// Writes a value of type `T` starting at the location specified by `self`. If the value
/// crosses the boundary of a storage slot, writing continues at the following slot.
///
/// ### Arguments
Expand All @@ -152,7 +182,7 @@ impl<T> StorageHandle<T> {
/// fn foo() {
/// let r: StorageHandle<u64> = StorageHandle {
/// key: 0x0000000000000000000000000000000000000000000000000000000000000000,
/// offset: 2,
/// offset: 2,
/// };
/// let x = r.write(42); // Writes 42 at the third word of storage slot with key 0x000...0
/// }
Expand Down Expand Up @@ -195,10 +225,8 @@ impl<K, V> StorageMap<K, V> {
write::<V>(key, 0, value);
}

/// Retrieves a value previously stored using a key.
///
/// If no value was previously stored at `key`, `Option::None` is returned. Otherwise,
/// `Option::Some(value)` is returned, where `value` is the value stored at `key`.
/// Retrieves the `StorageHandle` that describes the raw location in storage of the value
/// stored at `key`, regardless of whether a value is actually stored at that location or not.
///
/// ### Arguments
///
Expand All @@ -215,14 +243,16 @@ impl<K, V> StorageMap<K, V> {
/// let key = 5_u64;
/// let value = true;
/// storage.map.insert(key, value);
/// let retrieved_value = storage.map.get(key).unwrap();
/// let retrieved_value = storage.map.get(key).read();
/// assert(value == retrieved_value);
/// }
/// ```
#[storage(read)]
pub fn get(self: StorageHandle<Self>, key: K) -> Option<V> {
let key = sha256((key, self.key));
read::<V>(key, 0)
pub fn get(self: StorageHandle<Self>, key: K) -> StorageHandle<V> {
StorageHandle {
key: sha256((key, self.key)),
offset: 0,
}
}

/// Clears a value previously stored using a key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,3 @@ async fn maps_in_struct_access() {
(None, None)
);
}

#[tokio::test]
async fn maps_in_map_access() {
let methods = test_experimental_storage_instance().await.methods();

methods.map_in_map_access().call().await.unwrap();
}
71 changes: 24 additions & 47 deletions test/src/sdk-harness/test_projects/experimental_storage/src/main.sw
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ storage {
z: 0,
w: 0,
},
map_in_map: StorageMap<u64, StorageHandle<StorageMap<u64, u64>>> = StorageMap {},
}

abi ExperimentalStorageTest {
Expand All @@ -107,23 +106,20 @@ abi ExperimentalStorageTest {

#[storage(read, write)]
fn map_in_struct_write(key: (u64, u64), value: (u64, u64));

#[storage(read, write)]
fn map_in_map_access();
}

impl ExperimentalStorageTest for Contract {
#[storage(read, write)]
fn write_and_read_u64(input: u64) -> u64 {
let r = storage.x;
r.write(input);
r.read().unwrap()
r.read()
}

#[storage(read, write)]
fn write_and_read_b256(input: b256) -> b256 {
storage.y.write(input);
storage.y.read().unwrap()
storage.y.read()
}

#[storage(read, write)]
Expand All @@ -132,7 +128,7 @@ impl ExperimentalStorageTest for Contract {
// slot where the second half of `simple` is stored
storage.simple.z.write(simple.z);
storage.simple.b.write(simple.b);
storage.simple.read().unwrap()
storage.simple.read()
}

#[storage(read, write)]
Expand All @@ -146,17 +142,17 @@ impl ExperimentalStorageTest for Contract {
storage.s.d.write(s.d);

assert(S {
a: storage.s.a.read().unwrap(),
b: storage.s.b.read().unwrap(),
a: storage.s.a.read(),
b: storage.s.b.read(),
c: T {
x: storage.s.c.x.read().unwrap(),
y: storage.s.c.y.read().unwrap(),
x: storage.s.c.x.read(),
y: storage.s.c.y.read(),
z: M {
u: storage.s.c.z.u.read().unwrap(),
v: storage.s.c.z.v.read().unwrap(),
u: storage.s.c.z.u.read(),
v: storage.s.c.z.v.read(),
},
},
d: storage.s.d.read().unwrap(),
d: storage.s.d.read(),
} == s);

// Semi-granular write, granular read
Expand All @@ -166,17 +162,17 @@ impl ExperimentalStorageTest for Contract {
storage.s.d.write(s.d);

assert(S {
a: storage.s.a.read().unwrap(),
b: storage.s.b.read().unwrap(),
a: storage.s.a.read(),
b: storage.s.b.read(),
c: T {
x: storage.s.c.x.read().unwrap(),
y: storage.s.c.y.read().unwrap(),
x: storage.s.c.x.read(),
y: storage.s.c.y.read(),
z: M {
u: storage.s.c.z.u.read().unwrap(),
v: storage.s.c.z.v.read().unwrap(),
u: storage.s.c.z.u.read(),
v: storage.s.c.z.v.read(),
},
},
d: storage.s.d.read().unwrap(),
d: storage.s.d.read(),
} == s);

// Granular write, semi-granular read
Expand All @@ -188,21 +184,21 @@ impl ExperimentalStorageTest for Contract {
storage.s.d.write(s.d);

assert(S {
a: storage.s.a.read().unwrap(),
b: storage.s.b.read().unwrap(),
c: storage.s.c.read().unwrap(),
d: storage.s.d.read().unwrap(),
a: storage.s.a.read(),
b: storage.s.b.read(),
c: storage.s.c.read(),
d: storage.s.d.read(),
} == s);

// Coarse write and read
storage.s.write(s);

storage.s.read().unwrap()
storage.s.read()
}

#[storage(read)]
fn map_read(key: u64) -> Option<u64> {
storage.map.get(key)
storage.map.get(key).try_read()
}

#[storage(read, write)]
Expand All @@ -212,31 +208,12 @@ impl ExperimentalStorageTest for Contract {

#[storage(read)]
fn map_in_struct_read(key: (u64, u64)) -> (Option<u64>, Option<u64>) {
(storage.s2.map0.get(key.0), storage.s2.map1.get(key.1))
(storage.s2.map0.get(key.0).try_read(), storage.s2.map1.get(key.1).try_read())
}

#[storage(read, write)]
fn map_in_struct_write(key: (u64, u64), value: (u64, u64)) {
storage.s2.map0.insert(key.0, value.0);
storage.s2.map1.insert(key.1, value.1);
}

#[storage(read, write)]
fn map_in_map_access() {
storage.map_in_map.insert(0, StorageHandle {
key: std::hash::sha256((storage.map_in_map, 0)),
offset: 0,
});

storage.map_in_map.insert(1, StorageHandle {
key: std::hash::sha256((storage.map_in_map, 1)),
offset: 0,
});

storage.map_in_map.get(0).unwrap().insert(1, 42);
assert(storage.map_in_map.get(0).unwrap().get(1).unwrap() == 42);

storage.map_in_map.get(1).unwrap().insert(1, 24);
assert(storage.map_in_map.get(1).unwrap().get(1).unwrap() == 24);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,27 +121,27 @@ impl ExperimentalStorageInitTest for Contract {
let e2: E = E::A(777);
let string: str[40] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

assert(storage.x.read().unwrap() == x);
assert(storage.y.read().unwrap() == y);
assert(storage.s.read().unwrap() == s);
assert(storage.boolean.read().unwrap() == boolean);
assert(storage.int8.read().unwrap() == int8);
assert(storage.int16.read().unwrap() == int16);
assert(storage.int32.read().unwrap() == int32);
assert(storage.s.x.read().unwrap() == s.x);
assert(storage.s.y.read().unwrap() == s.y);
assert(storage.s.z.read().unwrap() == s.z);
assert(storage.s.t.read().unwrap() == s.t);
assert(storage.s.t.x.read().unwrap() == s.t.x);
assert(storage.s.t.y.read().unwrap() == s.t.y);
assert(storage.s.t.z.read().unwrap() == s.t.z);
assert(storage.s.t.boolean.read().unwrap() == s.t.boolean);
assert(storage.s.t.int8.read().unwrap() == s.t.int8);
assert(storage.s.t.int16.read().unwrap() == s.t.int16);
assert(storage.s.t.int32.read().unwrap() == s.t.int32);
assert(storage.e.read().unwrap() == e);
assert(storage.e2.read().unwrap() == e2);
assert(std::hash::sha256(storage.string.read().unwrap()) == std::hash::sha256(string));
assert(storage.x.read() == x);
assert(storage.y.read() == y);
assert(storage.s.read() == s);
assert(storage.boolean.read() == boolean);
assert(storage.int8.read() == int8);
assert(storage.int16.read() == int16);
assert(storage.int32.read() == int32);
assert(storage.s.x.read() == s.x);
assert(storage.s.y.read() == s.y);
assert(storage.s.z.read() == s.z);
assert(storage.s.t.read() == s.t);
assert(storage.s.t.x.read() == s.t.x);
assert(storage.s.t.y.read() == s.t.y);
assert(storage.s.t.z.read() == s.t.z);
assert(storage.s.t.boolean.read() == s.t.boolean);
assert(storage.s.t.int8.read() == s.t.int8);
assert(storage.s.t.int16.read() == s.t.int16);
assert(storage.s.t.int32.read() == s.t.int32);
assert(storage.e.read() == e);
assert(storage.e2.read() == e2);
assert(std::hash::sha256(storage.string.read()) == std::hash::sha256(string));
true
}
}
Loading

0 comments on commit 648b2f0

Please sign in to comment.