Skip to content

Commit

Permalink
Explain in detail the intended usage of the StorageEncodedBox
Browse files Browse the repository at this point in the history
  • Loading branch information
ironcev committed Jun 12, 2024
1 parent 9e27146 commit 40c4dba
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ abi Demo {
fn demo();
}

struct Struct {
struct DynStruct { // A struct of dynamic size.
vec: Vec<u64>,
txt: String,
}

impl AbiEncode for Struct {
impl AbiEncode for DynStruct {
fn abi_encode(&self, buffer: Buffer) -> Buffer {
let mut buffer = self.vec.abi_encode(buffer);
buffer = self.txt.abi_encode(buffer);
Expand All @@ -19,12 +19,12 @@ impl AbiEncode for Struct {
}
}

impl AbiDecode for Struct {
impl AbiDecode for DynStruct {
fn abi_decode(ref mut buffer: BufferReader) -> Self {
let vec = Vec::<u64>::abi_decode(buffer);
let txt = String::abi_decode(buffer);

Struct {
DynStruct {
vec,
txt,
}
Expand All @@ -35,45 +35,50 @@ storage {
//--
// For the sake of example, in the below examples we assume that the `Vec` and `String` have
// the presented `const fn from(...)` functions implemented via the `From` trait.
// But in general, any const eval expression that returns `Vec`, or `String`, or `Struct` can
// But in general, any const eval expression that returns `Vec`, or `String`, or `DynStruct` can
// apear on the RHS of `:=`.
//--
//--
// The usage patterns and the capabilities of `StorageEncodedBox` are the same as the
// ones of the `StorageBox`. Essentially, to support types that require encoding, the only
// thing that developers need to do is replace `StorageBox` with `StorageEncodedBox`.
// thing that developers need to do, is to replace the `StorageBox` with the `StorageEncodedBox`.
//--
box_1: StorageEncodedBox<Struct> := Struct::default(),
box_2: StorageEncodedBox<Struct> := Struct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
box_3: StorageEncodedBox<Vec<Struct>> := Vec::from([const_create_struct(1, "abc"), const_create_struct(2, "cdf")]),
box_1: StorageEncodedBox<DynStruct> := Struct::default(),
box_2: StorageEncodedBox<DynStruct> := Struct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
//--
// `box_3` encodes (on write) and decodes (on read) the entire boxed vector of `DynStruct`!
// When boxed as such, it is not possible to access only individual elements of the stored vector.
// This will be explained in the documentation, together with the hint that the inteded storage structure
// is actually very likely the `StorageVec<StorageEncodedBox<DynStruct>>` demonstrated below.
//--
box_3: StorageEncodedBox<Vec<DynStruct>> := Vec::from([const_create_struct(1, "abc"), const_create_struct(2, "cdf")]),

vec_of_encoded_val_1: StorageVec<StorageEncodedBox<Vec<bool>> := [Vec::from([true, false, true]), Vec::default(), Vec::from([true])],
vec_of_encoded_val_2: StorageVec<StorageEncodedBox<String>> := [String::from("abc"), String::from("cdf")],
vec_of_encoded_val_3: StorageVec<StorageEncodedBox<Struct>> := [
Struct::default(),
Struct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
vec_of_encoded_val_1: StorageVec<StorageEncodedBox<String>> := [String::from("abc"), String::from("cdf")],
vec_of_encoded_val_2: StorageVec<StorageEncodedBox<DynStruct>> := [
DynStruct::default(),
DynStruct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
some_const_fn_that_creates_struct(true, "abc"),
],

vec_of_vec_1: StorageVec<StorageVec<StorageEncodedBox<Struct>>> := [
vec_of_vec_1: StorageVec<StorageVec<StorageEncodedBox<DynStruct>>> := [
[],
[Struct::default()],
[DynStruct::default()],
[
Struct::default(),
Struct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
DynStruct::default(),
DynStruct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
some_const_fn_that_creates_struct(true, "abc"),
]
],

map_01: StorageMap<str[3], StorageVec<StorageMap<u64, StorageVec<StorageEncodedBox<Struct>>>>> := [
map_01: StorageMap<str[3], StorageVec<StorageMap<u64, StorageVec<StorageEncodedBox<DynStruct>>>>> := [
("abc", [
(11, [Struct::default(), const_create_struct(1, "abc")]),
(11, [DynStruct::default(), const_create_struct(1, "abc")]),
(22, []),
(33, [some_const_fn_that_creates_struct(true, Struct::default()]),
(33, [some_const_fn_that_creates_struct(true, DynStruct::default()]),
]),
("def", [
(111, [Struct::default(), const_create_struct(1, "abc")]),
(222, [Struct { vec: Vec::from([1, 2, 3]), txt: String::from("text") }]),
(111, [DynStruct::default(), const_create_struct(1, "abc")]),
(222, [DynStruct { vec: Vec::from([1, 2, 3]), txt: String::from("text") }]),
(333, [some_const_fn_that_creates_struct(true, "abc")]),
]),
],
Expand Down
20 changes: 15 additions & 5 deletions rfcs/0013-configurable-and-composable-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,28 @@ storage {
If the `Struct` in the above example would have a dynamic size, to store it we just need to replace `StorageBox` with `StorageEncodedBox`:

```Sway
struct Struct {
struct DynStruct { // A struct of dynamic size.
vec: Vec<u64>,
txt: String,
}
box_1: StorageEncodedBox<Struct> := Struct::default(),
box_2: StorageEncodedBox<Struct> := Struct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
box_1: StorageEncodedBox<DynStruct> := DynStruct::default(),
box_2: StorageEncodedBox<DynStruct> := DynStruct { vec: Vec::from([1, 2, 3]), txt: String::from("text") },
```

The later usage of `storage` elements is completely the same, regardless if they are stored in the `StorageBox` or `StorageEncodedBox`. The only difference is in the background. The `StorageEncodedBox` encodes and decodes the stored values during reading and writing using the ABI encoding and is thus more gas and storage demanding.
The later usage of `storage` elements is completely the same, regardless if they are stored in a `StorageBox` or a `StorageEncodedBox`. The only difference is in the way they store their content in the background. The `StorageEncodedBox` encodes and decodes the stored values during reading and writing, using the ABI encoding, and is thus more gas and storage demanding.

It is possible to arbitrarily compose and configure storage types. As an example, let's consider a `StorageVec<StorageVec<StorageBox<Struct>>>` and an intentionally complex `StorageMap`. Note that the `Struct` _must_ be stored in the `StorageBox`. The `StorageVec`, as well as all other _compound storage types_ can store only other store types.
The possibility of the `StorageEncodedBox` to store `Vec` and other dynamic Sway types opens the question of difference between, e.g., `StorageVec<StorageBox<Struct>>` and the `StorageEncodedBox<Vec<Struct>>`. Does the `StorageEncodedBox<Vec<T>>` actually makes `StorageVec` obsolete?

Not exactly. The `StorageVec` provides access to individual vector elements, thus allowing _pushing and reading individual elements_ in a manner that minimizes the number of storage reads and writes.

The `StorageEncodedBox<Vec<T>>`, on the other hand, always reads and writes back _the entire vector_. It is not possible to get only a particular vector element from the storage, the way `StorageVec` provides it. This is very likely not what developers want in a general case.

The intended usage of the `StorageEncodedBox` is storing instances of dynamic Sway types that are expected to be read and write entirely, as a single unit, like the `DynStruct` used in the above example.

One consequence of having the `StorageEncodedBox` is that the `StorageString` will become obsolete (or just be a type alias for the `StorageEncodedBox<String>`). Because, in the case of the `String` type, we want to store and read the entire string and not having each character be stored and accessible separately.

It is possible to arbitrarily compose and configure storage types. As an example, let's consider a `StorageVec<StorageVec<StorageBox<Struct>>>` and an intentionally complex `StorageMap` shown below. Note that the `Struct` _must_ be stored in the `StorageBox` (or `StorageEncodedBox` if dynamic in size). The `StorageVec`, as well as all other _compound storage types_ can store only other storage types.

The `[<content>]` on the RHSs represent instances of _typed slices_. As explained in the [Reference-level explanation](#reference-level-explanation), typed slices are one of the language prerequisites for the new storage.

Expand Down

0 comments on commit 40c4dba

Please sign in to comment.