Skip to content

Commit bdba9e3

Browse files
committed
[Docs]: Update readme and remove unused pkgs
1 parent 320f354 commit bdba9e3

File tree

15 files changed

+420
-535
lines changed

15 files changed

+420
-535
lines changed

mops.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@ keywords = ["region", "sorted", "persistent", "map", "encoding"]
77
license = "MIT"
88

99
[dependencies]
10-
base = "0.12.0"
10+
base = "0.12.1"
1111
itertools = "0.2.1"
12-
memory-region = "1.2.3"
13-
lru-cache = "1.0.0"
12+
memory-region = "1.2.4"
1413
buffer-deque = "0.1.0"
15-
fuzz = "0.2.1"
1614

1715
[dev-dependencies]
1816
test = "2.0.0"
1917
bench = "1.0.0"
20-
augmented-btrees = "0.5.1"
18+
augmented-btrees = "0.5.2"
19+
fuzz = "0.2.1"
2120
MotokoStableBTree = "https://github.com/sardariuss/MotokoStableBTree#master@b590ede4489c2d4b2189299c3a2cc35dc4faa3fc"
2221

2322
[toolchain]
2423
wasmtime = "14.0.4"
25-
moc = "0.11.1"
24+
moc = "0.12.0"

readme.md

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## Memory-Collection
22

3-
A collection of data structures in motoko that store their data in stable memory. These data structures address the heap storage limitations by allowing developers to leverage the 400GB capacity available in stable memory.
3+
A collection of data structures in motoko that store their data in stable memory.
4+
These data structures address the heap storage limitations by allowing developers to leverage the 400GB capacity available in stable memory.
45

56
### Motivation
67

@@ -10,28 +11,51 @@ The heap memory in the Internet Computer is limited to 4GB, which can be a bottl
1011

1112
- [MemoryBuffer](./src/MemoryBuffer/readme.md): A persistent buffer with `O(1)` random access.
1213
- [MemoryBTree](./src/MemoryBTree/readme.md): A persistent B-Tree with `O(log n)` search, insertion, and deletion.
13-
- [MemoryBTreeSet](./src/MemoryBTreeSet/readme.md): A persistent set with `O(log n)` search, insertion, and deletion.
14-
> Note: `MemoryBTreeSet` does not have a Stable module version
14+
- MemoryBTreeSet: A persistent set with `O(log n)` search, insertion, and deletion.
1515
- [MemoryQueue](./src/MemoryQueue/readme.md): A persistent queue with `O(1)` add and pop operations.
1616

17-
Each data structure is implemented using the class+ pattern, which creates a mutable stable store that is wrapped around a class object to provide an easy to use interface. This method allows the data persists accross canister upgrades while also being easy to use.
17+
Each data structure is implemented using the class+ pattern, which creates a mutable stable store that is wrapped around by a class object for a more familiar object oriented interface. This method allows the data to persists accross canister upgrades while also being simple and easy to use.
1818
The data structures can be imported using the default path to the library `mo:memory-collection/<data-structure>`.
1919
Each data structure also has a Stable module that can be used to call function on the stable store directly without wrapping it in a class object. The stable pattern can be accessed using the path `mo:memory-collection/<data-structure>/Stable`.
2020

21-
### Utilities
21+
### TypeUtils
2222

23-
The data structures require additional utilities to serialize, deserialize, and compare the data.
24-
These utilities are needed in order to store, retrieve and search for data in stable memory.
25-
We have provided these utilities as a set of modules that can be imported and used in your project.
26-
These utilities are:
23+
For each data structure we have to define a set of functions during initialization for processing the data structure's given data type so that it can be stored in stable memory.
24+
To help with this we have provided a set of modules that can be imported and used in your project to help define those functions.
2725

28-
- [TypeUtils](./src/TypeUtils/readme.md): A module that provides utilities for working with types.
29-
- [Blobify](./src/Blobify/readme.md): A module that provides functions for serializing and deserializing primitive motoko types.
30-
- [MemoryCmp](./src/MemoryCmp/readme.md): A module that provides functions for comparing elements.
26+
**Main Utility Module**
3127

32-
The main module is the `TypeUtils` module, and the other two submodules `Blobify` and `MemoryCmp` are contained within it.
33-
`TypeUtils` provides utilities for most of motoko's primitive types (e.g. Nat, Int, Text, etc.) and exposes a record containing the submodules when a type is selected.
34-
This structure allows the user to easily select from one of the preset options or define their own custom utilities for their required use case. More information on how to create custom utilities can be found in the **Create Custom Utilities** section of each data structure's readme.
28+
- [TypeUtils](./src/TypeUtils/lib.mo)
29+
30+
```motoko
31+
public type TypeUtils<T> = {
32+
blobify : Blobify<T>;
33+
cmp: MemoryCmp<T>;
34+
}
35+
```
36+
37+
**Sub modules within TypeUtils**
38+
39+
- [Blobify](./src/TypeUtils/Blobify.mo): A module that provides functions for converting the given data type to a `Blob` and back using the given interface.
40+
41+
```motoko
42+
public type Blobify<T> = {
43+
to_blob: (T) -> Blob;
44+
from_blob: (Blob) -> T;
45+
}
46+
```
47+
48+
- [MemoryCmp](./src/TypeUtils/MemoryCmp.mo): A module that provides functions for comparing two elements of the same type and retrieving their order. The comparison function can either be a `#BlobCmp` which compares the serialized version of the types, or it could be a `#GenCmp` which compares the types in their given data type, which often involves deserializing the stored `Blob` before comparing it.
49+
50+
```motoko
51+
public type MemoryCmp<T> = {
52+
#GenCmp: (T, T) -> Int8;
53+
#BlobCmp: (Blob, Blob) -> Int8;
54+
}
55+
```
56+
57+
`TypeUtils` are provided for most of motoko's primitive types (e.g. Nat, Int, Text, etc.). However, you can define a custom function for a compound datatype using the interface above.
58+
More information on how to create custom type utilities can be found in the **Create Custom TypeUtils** section of each data structure's readme.
3559

3660
## Getting Started
3761

@@ -50,6 +74,8 @@ This structure allows the user to easily select from one of the preset options o
5074

5175
### Usage Examples
5276

77+
Usage examples using the preset `TypeUtils`
78+
5379
- MemoryBuffer
5480

5581
```motoko
@@ -109,16 +135,20 @@ This structure allows the user to easily select from one of the preset options o
109135
110136
### Migrating between mops versions
111137

112-
The data stored in stable memory is stored in a way that it is unlikely to cause breaking changes between different `mops` versions.
113-
However, some of the data used by the data structure is stored as heap memory. These include cached fields, like the size, head or tail pointers, and others. The most important of the data stored on the heap is the deallocated memory stored in each [MemoryRegion](https://github.com/NatLabs/memory-region) because it is not backed up in stable memory. For efficient utilization of memory it's important to not lose access to this data. Furthermore, the deallocated memory blocks are stored on the heap for a number of reasons. The first one being accessing data on the heap is faster than stable memory which makes the data structure more efficient since this is a module that is used frequently in all of them.
138+
The data structures in the memory-collection are designed with backward compatibility in mind, particularly for data stored in stable memory. This approach helps prevent breaking changes between package updates. To support this compatibility, additional checks are implemented, and space is reserved in each data structure to accommodate potential future additions to stable memory.
114139

115-
In future, the `MemoryRegion` may have updates improving its performance for reallocating memory. In doing so the structure of the `MemoryRegion` may change. So in order to keep it
140+
While stable memory ensures data persistence, certain information is stored on the heap for faster access. This includes cached fields from stable memory and memory marked as deallocated in each MemoryRegion. Unlike stable memory where all data is formatted as `Blobs`, heap data utilizes various Motoko data types. This diversity in data types on the heap presents a challenge for compatibility, as adding new data types or modifying existing ones can potentially break compatibility between versions.
116141

117-
### Serialization Notes
142+
Future updates to this library may introduce new fields to cached data or modify the MemoryRegion to enhance performance. These changes could potentially alter the internal structure of the data, particularly on the heap where data types vary. To prevent data loss during such changes, each `StableStore` (the value returned after a call to `.newStableStore()`) is wrapped with the current version identifier. This versioning helps distinguish between different versions and facilitates the migration when the package is updated.
118143

119-
> should be moved to the individual data structure readme
144+
To upgrade your StableStore, use the following code:
145+
146+
```motoko
147+
stable var sstore = MemoryBTree.newStableStore(null);
148+
sstore := MemoryBTree.upgrade(sstore);
149+
```
120150

121-
- Serialization and deserialization using candid is often much more performant than using Blobify or any other serialization methods. The reason for this is because the to_candid and from_candid functions are system functions in the IC and therefore more efficient than any custom serialization methods. However, the candid contains extra type information included in the serialized data, which can make the serialized data larger than using Blobify. This difference in size can be significant if each of the pieces of data being serialized is small. For example a serialized value of Nat8 value if serialized with Blobify will be 1 bytes, but if serialized into candid will include the magic number (4 bytes), the type (1 byte) and the value (1 byte) for a total of 6 bytes. This difference in size can be significant if each of the pieces of data being serialized is small. However, if the data being serialized is large, the overhead of the extra bytes is negligible. So be mindful of the size of the data being serialized when choosing between Blobify and candid.
151+
You can call `.upgrade()` either immediately after defining the stable store variable or within the `preupgrade()` system function. It's important to note that when `preupgrade()` is set or modified, the defined logic is executed on the next update to the canister, not the current one.
122152

123153
### Notes and Limitations
124154

@@ -127,4 +157,3 @@ In future, the `MemoryRegion` may have updates improving its performance for rea
127157
- Each value stored in the data structure is immutable. To update a value, the data is removed and the new value is serialized and stored in its place or in a new location.
128158
- Currently, stable memory is not garbage collected. Which means that we can't free up a section of memory when it's not longer in use once we have allocated it. We can only mark it as deallocated and re-use it once we need to store more data. So even after one of our data structures is no longer in use and all of its data has been cleared, the `Region` and the memory it allocated will still be reserved.
129159
- Ideally, serializing compound types can be done easily enough using a predefined serialization function for candid, but since motoko's `to_candid()` and `from_candid()` don't yet support generic types, each type need to have its own serialization utility defined.
130-
<!-- - We have provided Utilities for comparing and serializing primitive types. However, you will need to define your own custom functions for compound or complex types. This goes for candid as well since it does not yet support generic types you would need to define serialization functions for each of your types. -->

src/MemoryBTree/Base.mo

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import Nat64 "mo:base/Nat64";
1010
import Blob "mo:base/Blob";
1111

1212
import MemoryRegion "mo:memory-region/MemoryRegion";
13-
import LruCache "mo:lru-cache";
1413
import RevIter "mo:itertools/RevIter";
1514

1615
import MemoryCmp "../TypeUtils/MemoryCmp";
@@ -27,7 +26,6 @@ import TypeUtils "../TypeUtils";
2726
module {
2827
type Address = Nat;
2928
type MemoryRegion = MemoryRegion.MemoryRegion;
30-
type LruCache<K, V> = LruCache.LruCache<K, V>;
3129
type Blobify<A> = Blobify.Blobify<A>;
3230
type RevIter<A> = RevIter.RevIter<A>;
3331
type Iter<A> = Iter.Iter<A>;
@@ -70,8 +68,6 @@ module {
7068
branches = MemoryRegion.new();
7169
data = MemoryRegion.new();
7270

73-
nodes_cache = LruCache.new(0);
74-
key_cache = LruCache.new<Nat, Blob>(cache_size);
7571
};
7672

7773
init_region_header(btree);
@@ -572,8 +568,6 @@ module {
572568
};
573569

574570
public func clear(btree : MemoryBTree) {
575-
// clear cache
576-
LruCache.clear(btree.nodes_cache);
577571

578572
// the first leaf node should be at the address where the header ends
579573
let leaf_address = MC.REGION_HEADER_SIZE;
@@ -707,7 +701,6 @@ module {
707701
// merge leaf with neighbour
708702
Leaf.merge(btree, left, right);
709703
Branch.remove(btree, parent, right_index);
710-
Branch.rm_from_cache(btree, right);
711704

712705
// deallocate right leaf that was merged into left
713706
Leaf.deallocate(btree, right);
@@ -764,7 +757,6 @@ module {
764757
let merged_branch = Branch.merge(btree, branch, neighbour);
765758
let merged_branch_index = Branch.get_index(btree, merged_branch);
766759
Branch.remove(btree, parent, merged_branch_index);
767-
Branch.rm_from_cache(btree, merged_branch);
768760
Branch.deallocate(btree, merged_branch);
769761
update_branch_count(btree, btree.branch_count - 1);
770762

src/MemoryBTree/Migrations/V0.mo

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import Nat "mo:base/Nat";
22

33
import MemoryRegion "mo:memory-region/MemoryRegion";
4-
import LruCache "mo:lru-cache";
54
import RevIter "mo:itertools/RevIter";
65
// import Branch "mo:augmented-btrees/BpTree/Branch";
76

@@ -15,7 +14,6 @@ module {
1514
public type MemoryBlock = (Address, Size);
1615

1716
type MemoryRegionV1 = MemoryRegion.MemoryRegionV1;
18-
type LruCache<K, V> = LruCache.LruCache<K, V>;
1917
type Blobify<A> = Blobify.Blobify<A>;
2018
type RevIter<A> = RevIter.RevIter<A>;
2119

@@ -74,7 +72,6 @@ module {
7472
branches : MemoryRegionV1;
7573
data : MemoryRegionV1;
7674

77-
nodes_cache : LruCache<Address, Node>;
7875
};
7976

8077
};

0 commit comments

Comments
 (0)