Skip to content

Commit

Permalink
[Feature] Simple map (#182)
Browse files Browse the repository at this point in the history
* Add SimpleMap

* Add SimpleMap
  • Loading branch information
WGB5445 authored and welbon committed Apr 10, 2023
1 parent 1b5db0f commit 0d2f61c
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 42 deletions.
3 changes: 1 addition & 2 deletions build/StarcoinFramework/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ This is the root document for the Move StarcoinFramework module documentation. T
- [`0x1::Signature`](Signature.md#0x1_Signature)
- [`0x1::SignedInteger64`](SignedInteger64.md#0x1_SignedInteger64)
- [`0x1::Signer`](Signer.md#0x1_Signer)
- [`0x1::StarcoinVerifier`](StarcoinVerifier.md#0x1_StarcoinVerifier)
- [`0x1::SimpleMap`](SimpleMap.md#0x1_SimpleMap)
- [`0x1::StdlibUpgradeScripts`](StdlibUpgradeScripts.md#0x1_StdlibUpgradeScripts)
- [`0x1::StructuredHash`](StarcoinVerifier.md#0x1_StructuredHash)
- [`0x1::Timestamp`](Timestamp.md#0x1_Timestamp)
Expand All @@ -99,4 +99,3 @@ This is the root document for the Move StarcoinFramework module documentation. T
- [`0x1::Version`](Version.md#0x1_Version)
- [`0x1::YieldFarming`](YieldFarming.md#0x1_YieldFarming)
- [`0x1::YieldFarmingV2`](YieldFarmingV2.md#0x1_YieldFarmingV2)
- [`0x1::simple_map`](SimpleMap.md#0x1_simple_map)
129 changes: 89 additions & 40 deletions sources/SimpleMap.move
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
/// This module provides a solution for sorted maps, that is it has the properties that
/// 1) Keys point to Values
/// 2) Each Key must be unique
/// 3) A Key can be found within O(N) time
/// 4) The keys are unsorted.
/// 3) A Key can be found within O(Log N) time
/// 4) The data is stored as sorted by Key
/// 5) Adds and removals take O(N) time
module StarcoinFramework::simple_map {
module StarcoinFramework::SimpleMap {

use StarcoinFramework::Errors;
use StarcoinFramework::Option;
use StarcoinFramework::Vector;
use StarcoinFramework::Compare;
use StarcoinFramework::BCS;

spec module {
pragma verify = false;
pragma aborts_if_is_strict = true;
}

/// Map key already exists
const EKEY_ALREADY_EXISTS: u64 = 1;
Expand Down Expand Up @@ -37,7 +45,7 @@ module StarcoinFramework::simple_map {
map: &SimpleMap<Key, Value>,
key: &Key,
): &Value {
let maybe_idx = find(map, key);
let (maybe_idx, _) = find(map, key);
assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND));
let idx = Option::extract(&mut maybe_idx);
&Vector::borrow(&map.data, idx).value
Expand All @@ -47,7 +55,7 @@ module StarcoinFramework::simple_map {
map: &mut SimpleMap<Key, Value>,
key: &Key,
): &mut Value {
let maybe_idx = find(map, key);
let (maybe_idx, _) = find(map, key);
assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND));
let idx = Option::extract(&mut maybe_idx);
&mut Vector::borrow_mut(&mut map.data, idx).value
Expand All @@ -57,7 +65,7 @@ module StarcoinFramework::simple_map {
map: &SimpleMap<Key, Value>,
key: &Key,
): bool {
let maybe_idx = find(map, key);
let (maybe_idx, _) = find(map, key);
Option::is_some(&maybe_idx)
}

Expand All @@ -71,60 +79,67 @@ module StarcoinFramework::simple_map {
key: Key,
value: Value,
) {
let maybe_idx = find(map, &key);
let (maybe_idx, maybe_placement) = find(map, &key);
assert!(Option::is_none(&maybe_idx), Errors::invalid_argument(EKEY_ALREADY_EXISTS));

// Append to the end and then swap elements until the list is ordered again
Vector::push_back(&mut map.data, Element { key, value });
}

/// Insert key/value pair or update an existing key to a new value
public fun upsert<Key: store, Value: store>(
map: &mut SimpleMap<Key, Value>,
key: Key,
value: Value
): (Option::Option<Key>, Option::Option<Value>) {
let data = &mut map.data;
let len = Vector::length(data);
let i = 0;
while (i < len) {
let element = Vector::borrow(data, i);
if (&element.key == &key) {
Vector::push_back(data, Element { key, value});
Vector::swap(data, i, len);
let Element { key, value } = Vector::pop_back(data);
return (Option::some(key), Option::some(value))
};
i = i + 1;
let placement = Option::extract(&mut maybe_placement);
let end = Vector::length(&map.data) - 1;
while (placement < end) {
Vector::swap(&mut map.data, placement, end);
placement = placement + 1;
};
Vector::push_back(&mut map.data, Element { key, value });
(Option::none(), Option::none())
}

public fun remove<Key: store, Value: store>(
map: &mut SimpleMap<Key, Value>,
key: &Key,
): (Key, Value) {
let maybe_idx = find(map, key);
let (maybe_idx, _) = find(map, key);
assert!(Option::is_some(&maybe_idx), Errors::invalid_argument(EKEY_NOT_FOUND));

let placement = Option::extract(&mut maybe_idx);
let Element { key, value } = Vector::swap_remove(&mut map.data, placement);
let end = Vector::length(&map.data) - 1;

while (placement < end) {
Vector::swap(&mut map.data, placement, placement + 1);
placement = placement + 1;
};

let Element { key, value } = Vector::pop_back(&mut map.data);
(key, value)
}

fun find<Key: store, Value: store>(
map: &SimpleMap<Key, Value>,
key: &Key,
): Option::Option<u64>{
let leng = Vector::length(&map.data);
let i = 0;
while (i < leng) {
let element = Vector::borrow(&map.data, i);
if (&element.key == key){
return Option::some(i)
): (Option::Option<u64>, Option::Option<u64>) {
let length = Vector::length(&map.data);

if (length == 0) {
return (Option::none(), Option::some(0))
};

let left = 0;
let right = length;

while (left != right) {
let mid = left + (right - left) / 2;
let potential_key = &Vector::borrow(&map.data, mid).key;
if (Compare::is_less_than(Compare::cmp_bytes(&BCS::to_bytes(potential_key), &BCS::to_bytes(key)))) {
left = mid + 1;
} else {
right = mid;
};
i = i + 1;
};
Option::none<u64>()

if (left != length && key == &Vector::borrow(&map.data, left).key) {
(Option::some(left), Option::none())
} else {
(Option::none(), Option::some(left))
}
}

#[test]
Expand Down Expand Up @@ -160,6 +175,36 @@ module StarcoinFramework::simple_map {
destroy_empty(map);
}

#[test]
public fun test_several() {
let map = create<u64, u64>();
add(&mut map, 6, 6);
add(&mut map, 1, 1);
add(&mut map, 5, 5);
add(&mut map, 2, 2);
add(&mut map, 3, 3);
add(&mut map, 0, 0);
add(&mut map, 7, 7);
add(&mut map, 4, 4);

let idx = 0;
while (idx < Vector::length(&map.data)) {
assert!(idx == Vector::borrow(&map.data, idx).key, idx);
idx = idx + 1;
};

remove(&mut map, &0);
remove(&mut map, &1);
remove(&mut map, &2);
remove(&mut map, &3);
remove(&mut map, &4);
remove(&mut map, &5);
remove(&mut map, &6);
remove(&mut map, &7);

destroy_empty(map);
}

#[test]
#[expected_failure]
public fun add_twice() {
Expand All @@ -181,6 +226,7 @@ module StarcoinFramework::simple_map {

destroy_empty(map);
}
<<<<<<< HEAD

#[test]
public fun upsert_test() {
Expand All @@ -205,4 +251,7 @@ module StarcoinFramework::simple_map {
assert!(contains_key(&map, &1), 8);
assert!(borrow(&map, &1) == &4, 9);
}
}
}
=======
}
>>>>>>> ae436d0 ([Feature] Simple map (#182))

0 comments on commit 0d2f61c

Please sign in to comment.