Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PersistentUnorderedMap #85

Merged
merged 5 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 98 additions & 1 deletion assembly/__tests__/runtime/runtime.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { context, storage, base58, base64, util, PersistentMap, PersistentVector, PersistentDeque, ContractPromise, ContractPromiseBatch, math, logging, env, u128 } from "../../runtime";
import { context, storage, base58, base64, PersistentUnorderedMap, PersistentMap, PersistentVector, PersistentDeque, ContractPromise, math, logging, env, u128, RNG, ContractPromiseBatch } from "../../runtime";
import { TextMessage } from "./model";
import { _testTextMessage, _testTextMessageTwo, _testBytes, _testBytesTwo } from "./util";
import { Context, VM, Outcome } from "../../vm";


describe("Encodings", () => {
it("base58 round trip", () => {
let array: Uint8Array = _testBytes();
Expand Down Expand Up @@ -370,6 +371,102 @@ describe("Deque should handle", () => {
});
});

let unOrderedMap: PersistentUnorderedMap<string, TextMessage>;
let imap: PersistentUnorderedMap<i32, i32>;
let keys: i32[];
describe("Ordered Map should handle", () => {
beforeEach(() => {
unOrderedMap = new PersistentUnorderedMap<string, TextMessage>("unOrderedMap");
});

describe("empty maps", () => {
it("should not contain key", () => {
expect(!unOrderedMap.contains("nonexistentkey")).toBe(true, "Map contains a non existent key");
});

throws("should throw when retrieving key that doesn't exist", () =>{
expect(unOrderedMap.getSome("nonexistentkey")).toBeNull("Incorrect result on get with nonexistent key");
});
});

it("some entries", () => {
// add some entries to the map
const message = _testTextMessage();
unOrderedMap.set("mapKey1", message);
unOrderedMap.set("mapKey3", _testTextMessageTwo());
expect(unOrderedMap.contains("mapKey1")).toBe(true);
expect(!unOrderedMap.contains("nonexistentkey")).toBe(true, "Map contains a non existent key");
expect(unOrderedMap.contains("mapKey1")).toBe(true, "Map does not contain a key that was added (mapKey1)");
expect(unOrderedMap.contains("mapKey3")).toBe(true, "Map does not contain a key that was added (mapKey3)");
expect(unOrderedMap.getSome("mapKey1")).toStrictEqual(message, "Incorrect result from map get");
expect(unOrderedMap.getSome("mapKey3")).toStrictEqual(_testTextMessageTwo(), "Incorrect result from map get");
// delete an entry and retry api calls
unOrderedMap.pop();
expect(!unOrderedMap.contains("mapKey3")).toBe(true, "Map contains a key that was deleted");
expect(unOrderedMap.contains("mapKey1")).toBe(true, "Map does not contain a key that should be there after deletion of another key");
expect(unOrderedMap.getSome("mapKey1")).toStrictEqual(message, "Incorrect result from map get after delete");
});

it("should handle primitives", () => {
// map with primitives
const map = new PersistentUnorderedMap<i32, i32>("mapPrimitives");
map.set(1, -20);
expect(map.getSome(1)).toBe(-20, "wrong value on map get for i32");
expect(map.values()).toStrictEqual([-20]);
});

describe("order", () => {

beforeEach(() => {
imap = new PersistentUnorderedMap<i32, i32>("mapPrimitives");
keys = new Array<i32>();
let rng = new RNG<i32>(100, 100);
for(let _i=0; _i < 10; _i++) {
let i = rng.next();
if (!imap.contains(i)) {
imap.set(i,i);
keys.push(i);
} else {
_i--;
}
}
})

it("should maintain order if no deletions before penultimate key", () => {
expect(imap.keys()).toStrictEqual(keys, "keys should be in order inserted");
expect(imap.values()).toStrictEqual(keys, "values should be in order inserted");

imap.delete(keys.pop());

expect(imap.keys()).toStrictEqual(keys, "keys should be in order inserted");
expect(imap.values()).toStrictEqual(keys, "values should be in order inserted");

let oldEnd = keys.pop();
imap.delete(keys.pop());
keys.push(oldEnd);

expect(imap.keys()).toStrictEqual(keys, "keys should be in order inserted");
expect(imap.values()).toStrictEqual(keys, "values should be in order inserted");
});

it("should not be in order if key deleted before penultimate key", () => {
imap.delete(keys.shift());
expect(imap.keys()).not.toStrictEqual(keys, "keys should not be in order inserted");
expect(imap.values()).not.toStrictEqual(keys, "values should not be in order inserted");
});
})

it("should handle arrays", () => {
// map with arrays
const map = new PersistentUnorderedMap<i32, Array<string>>("mapArray");
const arr1 = new Array<string>();
arr1.push("123456789");
// return arr1;
map.set(1, arr1);
expect(map.getSome(1)[0]).toBe("123456789");
});
});

describe("context", () => {

it("should read unchanged context", () => {
Expand Down
31 changes: 31 additions & 0 deletions assembly/__tests__/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { math, Context, RNG } from ".."

function test(i: u32): void {
expect(<f64>math.binaryLog(i)).toBe(Math.floor(Math.log2(i)));
}

function isOdd(rng: RNG<u64> ): bool {
return rng.next() % 2 == 1
}

describe("test integer log_2", () => {
it("binary log", () => {
for (let i: u32 = 1; i < 100_000_000;) {
test(i);
i = i + 1;
i *= 2;
}
});
});
describe("RNG", () => {
it("coin flip should be 50/50", () => {
Context.setPrepaid_gas(8446744073709552000);
let rng = new RNG<u64>(10000, 10_000);
let count: u32 = 0;
for (let i: u32 = 0; i < 10_000; i++) {
count += <u32>isOdd(rng);
}
expect(count).toBeGreaterThan(4900);
expect(count).toBeLessThan(5100)
});
});
18 changes: 6 additions & 12 deletions assembly/runtime/collections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,17 @@ export namespace collections {
export const _KEY_ELEMENT_SUFFIX = "::";
/** @internal */
export const _RATING_OFFSET: u64 = 2147483648;

/**
* Helper class to store key->value pairs.
* @internal
*/
export class MapEntry<K, V> {
constructor(
public key: K,
public value: V
) {
}
}
}

/** @internal */
export * from "./persistentDeque";
/** @internal */
export * from "./persistentMap";
/** @internal */
export * from "./persistentVector";
/** @internal */
export * from "./persistentSet";
/** @internal */
export * from "./persistentUnorderedMap";
/** @internal */
export * from "./util";
2 changes: 1 addition & 1 deletion assembly/runtime/collections/persistentSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class PersistentSet<T> {
}

/**
*
* Deletes all items.
*/
clear(): void {
while(this._vector.length > 0) {
Expand Down