Skip to content

Commit

Permalink
Merge pull request #14 from koinos/feat-new-space-helpers
Browse files Browse the repository at this point in the history
Feat new space helpers
  • Loading branch information
roaminro authored Apr 25, 2022
2 parents f3bd46a + 2544709 commit 3b83344
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 16 deletions.
82 changes: 67 additions & 15 deletions __tests__/space.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,19 @@ describe('space', () => {
});

it('should get many', () => {
const testObj = new test_object(42);
const Objects = new Space.Space<String, test_object>(mockContractId, 1, test_object.decode, test_object.encode);

Objects.put('key1', testObj);
Objects.put('key3', testObj);
Objects.put('key2', testObj);
Objects.put('key1', new test_object(1));
Objects.put('key3', new test_object(3));
Objects.put('key2', new test_object(2));

let objs = Objects.getMany('key1');

expect(objs.length).toBe(2);
expect(Arrays.equal(objs[0].key, StringBytes.stringToBytes('key2'))).toBe(true);
expect(Arrays.equal(objs[1].key, StringBytes.stringToBytes('key3'))).toBe(true);

Objects.put('key4', testObj);
Objects.put('key4', new test_object(4));

objs = Objects.getMany('key1', 2);

Expand All @@ -81,21 +80,51 @@ describe('space', () => {
expect(Arrays.equal(objs[0].key, StringBytes.stringToBytes('key3'))).toBe(true);
expect(Arrays.equal(objs[1].key, StringBytes.stringToBytes('key2'))).toBe(true);

let keys = Objects.getManyKeys('key4', 2, Space.Direction.Descending);
expect(keys.length).toBe(2);
expect(keys[0]).toBe('key3');
expect(keys[1]).toBe('key2');

keys = Objects.getManyKeys('key1', 2);
expect(keys.length).toBe(2);
expect(keys[0]).toBe('key2');
expect(keys[1]).toBe('key3');

let values = Objects.getManyValues('key4', 2, Space.Direction.Descending);
expect(values.length).toBe(2);
expect(values[0].value).toBe(3);
expect(values[1].value).toBe(2);

values = Objects.getManyValues('key1', 2);
expect(values.length).toBe(2);
expect(values[0].value).toBe(2);
expect(values[1].value).toBe(3);

const Objects2 = new Space.Space<Uint8Array, test_object>(mockContractId, 1, test_object.decode, test_object.encode);

objs = Objects2.getMany(StringBytes.stringToBytes('key4'), 2, Space.Direction.Descending);

expect(objs.length).toBe(2);
expect(Arrays.equal(objs[0].key, StringBytes.stringToBytes('key3'))).toBe(true);
expect(Arrays.equal(objs[1].key, StringBytes.stringToBytes('key2'))).toBe(true);

const keys2 = Objects2.getManyKeys(StringBytes.stringToBytes('key4'), 2, Space.Direction.Descending);
expect(keys2.length).toBe(2);
expect(Arrays.equal(keys2[0], StringBytes.stringToBytes('key3'))).toBe(true);
expect(Arrays.equal(keys2[1], StringBytes.stringToBytes('key2'))).toBe(true);

const values2 = Objects2.getManyValues(StringBytes.stringToBytes('key4'), 2, Space.Direction.Descending);
expect(values2.length).toBe(2);
expect(values2[0].value).toBe(3);
expect(values2[1].value).toBe(2);
});
});

describe('space with proto key', () => {
it('should put and get an object', () => {
const key = new test_object(1);
const obj = new test_object(42);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2, test_object.encode, test_object.decode, test_object.encode);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2,test_object.decode, test_object.encode, test_object.decode, test_object.encode);

Objects.put(key, obj);

Expand All @@ -106,15 +135,15 @@ describe('space with proto key', () => {

it('should check if space has an object or not', () => {
const key = new test_object(1);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2, test_object.encode, test_object.decode, test_object.encode);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2,test_object.decode, test_object.encode, test_object.decode, test_object.encode);

expect(Objects.has(key)).toBe(true);
expect(Objects.has(new test_object(2))).toBe(false);
});

it('should remove an object', () => {
const key = new test_object(1);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2, test_object.encode, test_object.decode, test_object.encode);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2,test_object.decode, test_object.encode, test_object.decode, test_object.encode);

expect(Objects.has(key)).toBe(true);
Objects.remove(key);
Expand All @@ -123,7 +152,7 @@ describe('space with proto key', () => {

it('should get next and prev object', () => {
const obj = new test_object(42);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2, test_object.encode, test_object.decode, test_object.encode);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2,test_object.decode, test_object.encode, test_object.decode, test_object.encode);

Objects.put(new test_object(1), obj);
Objects.put(new test_object(3), obj);
Expand All @@ -141,20 +170,19 @@ describe('space with proto key', () => {
});

it('should get many', () => {
const testObj = new test_object(42);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2, test_object.encode, test_object.decode, test_object.encode);
const Objects = new Space.SpaceProtoKey<test_object, test_object>(mockContractId, 2,test_object.decode, test_object.encode, test_object.decode, test_object.encode);

Objects.put(new test_object(1), testObj);
Objects.put(new test_object(3), testObj);
Objects.put(new test_object(2), testObj);
Objects.put(new test_object(1), new test_object(10));
Objects.put(new test_object(3), new test_object(30));
Objects.put(new test_object(2), new test_object(20));

let objs = Objects.getManyObj(new test_object(1));

expect(objs.length).toBe(2);
expect(Arrays.equal(objs[0].key, Protobuf.encode(new test_object(2), test_object.encode))).toBe(true);
expect(Arrays.equal(objs[1].key, Protobuf.encode(new test_object(3), test_object.encode))).toBe(true);

Objects.put(new test_object(4), testObj);
Objects.put(new test_object(4), new test_object(40));

objs = Objects.getManyObj(new test_object(1), 2);

Expand All @@ -167,5 +195,29 @@ describe('space with proto key', () => {
expect(objs.length).toBe(2);
expect(Arrays.equal(objs[0].key, Protobuf.encode(new test_object(3), test_object.encode))).toBe(true);
expect(Arrays.equal(objs[1].key, Protobuf.encode(new test_object(2), test_object.encode))).toBe(true);

let values = Objects.getManyObjValues(new test_object(1), 2);

expect(values.length).toBe(2);
expect(values[0].value).toBe(20);
expect(values[1].value).toBe(30);

values = Objects.getManyObjValues(new test_object(4), 2, Space.Direction.Descending);

expect(values.length).toBe(2);
expect(values[0].value).toBe(30);
expect(values[1].value).toBe(20);

let key = Objects.getManyObjKeys(new test_object(1), 2);

expect(key.length).toBe(2);
expect(key[0].value).toBe(2);
expect(key[1].value).toBe(3);

key = Objects.getManyObjKeys(new test_object(4), 2, Space.Direction.Descending);

expect(key.length).toBe(2);
expect(key[0].value).toBe(3);
expect(key[1].value).toBe(2);
});
});
152 changes: 151 additions & 1 deletion assembly/util/space.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,92 @@ export namespace Space {
return result;
}

/**
* Get many keys from the space
* @param offsetKey key used as the offset
* @param limit number of keys to return
* @param direction direction of the get, Ascending or Descending
* @returns an array with the keys retrieved
* @example
* ```ts
* const keys = Objects.getManyKeys('key1', 10, Space.Direction.Descending);
*
* for (let index = 0; index < keys.length; index++) {
* const key = keys[index];
* }
* ```
*/
getManyKeys(offsetKey: TKey, limit: i32 = i32.MAX_VALUE, direction: Direction = Direction.Ascending): TKey[] {
const result: TKey[] = [];

let key: Uint8Array;
if (offsetKey instanceof Uint8Array) {
key = offsetKey;
} else if (typeof offsetKey == 'string') {
key = StringBytes.stringToBytes(offsetKey);
}

let done = false;
do {
// @ts-ignore key is always initialized when reaching this code
const obj = direction == Direction.Ascending ? System.getNextObject<Uint8Array, TValue>(this.space, key, this.valueDecoder) : System.getPrevObject<Uint8Array, TValue>(this.space, key, this.valueDecoder);
if (obj) {
key = obj.key!;
if (offsetKey instanceof Uint8Array) {
// @ts-ignore key here is a Uint8Array
result.push(key);
} else if (typeof offsetKey == 'string') {
// @ts-ignore key here is a string
result.push(StringBytes.bytesToString(key)!);
}
}

done = obj == null || result.length >= limit;
} while (!done);

return result;
}

/**
* Get many values from the space
* @param offsetKey key used as the offset
* @param limit number of values to return
* @param direction direction of the get, Ascending or Descending
* @returns an array with the values retrieved
* @example
* ```ts
* const values = Objects.getManyValues('key1', 10, Space.Direction.Descending);
*
* for (let index = 0; index < values.length; index++) {
* const values = values[index];
* }
* ```
*/
getManyValues(offsetKey: TKey, limit: i32 = i32.MAX_VALUE, direction: Direction = Direction.Ascending): TValue[] {
const result: TValue[] = [];

let key: Uint8Array;
if (offsetKey instanceof Uint8Array) {
key = offsetKey;
} else if (typeof offsetKey == 'string') {
key = StringBytes.stringToBytes(offsetKey);
}

let done = false;
do {
// @ts-ignore key is always initialized when reaching this code
const obj = direction == Direction.Ascending ? System.getNextObject<Uint8Array, TValue>(this.space, key, this.valueDecoder) : System.getPrevObject<Uint8Array, TValue>(this.space, key, this.valueDecoder);
if (obj) {
result.push(obj.value);
key = obj.key!;
}

done = obj == null || result.length >= limit;
} while (!done);

return result;
}

/**
* Get the next object from the space
* @param key key to get next
Expand Down Expand Up @@ -180,6 +266,7 @@ export namespace Space {
}

export class SpaceProtoKey<TKey, TValue> extends Space<Uint8Array, TValue> {
private keyDecoder: (reader: Reader, length: i32) => TKey;
private keyEncoder: (message: TKey, writer: Writer) => void;

/**
Expand All @@ -200,11 +287,13 @@ export namespace Space {
constructor(
contractId: Uint8Array,
spaceId: u32,
keyDecoder: (reader: Reader, length: i32) => TKey,
keyEncoder: (message: TKey, writer: Writer) => void,
valueDecoder: (reader: Reader, length: i32) => TValue,
valueEncoder: (message: TValue, writer: Writer) => void,
system: bool = false) {
super(contractId, spaceId, valueDecoder, valueEncoder, system);
this.keyDecoder = keyDecoder;
this.keyEncoder = keyEncoder;
}

Expand All @@ -220,6 +309,7 @@ export namespace Space {
* }
* ```
*/
// @ts-ignore valid in AS
has(key: TKey): boolean {
const finalKey = Protobuf.encode(key, this.keyEncoder);
const object = super.get(finalKey);
Expand All @@ -240,6 +330,7 @@ export namespace Space {
* }
* ```
*/
// @ts-ignore valid in AS
get(key: TKey): TValue | null {
const finalKey = Protobuf.encode(key, this.keyEncoder);
return super.get(finalKey);
Expand All @@ -253,7 +344,7 @@ export namespace Space {
* @returns an array with the objects retrieved
* @example
* ```ts
* const objs = Objects.getMany('key1', 10, Space.Direction.Descending);
* const objs = Objects.getManyObj(new test_key(1), 10, Space.Direction.Descending);
*
* for (let index = 0; index < objs.length; index++) {
* const obj = objs[index];
Expand All @@ -265,6 +356,61 @@ export namespace Space {
return super.getMany(finalKey, limit, direction);
}

/**
* Get many values from the space
* @param offsetKey key used as the offset
* @param limit number of objects to return
* @param direction direction of the get, Ascending or Descending
* @returns an array with the objects retrieved
* @example
* ```ts
* const values = Objects.getManyObjValues(new test_key(1), 10, Space.Direction.Descending);
*
* for (let index = 0; index < values.length; index++) {
* const value = values[index];
* }
* ```
*/
getManyObjValues(offsetKey: TKey, limit: i32 = i32.MAX_VALUE, direction: Direction = Direction.Ascending): TValue[] {
const finalKey = Protobuf.encode(offsetKey, this.keyEncoder);
return super.getManyValues(finalKey, limit, direction);
}

/**
* Get many keys from the space
* @param offsetKey key used as the offset
* @param limit number of keys to return
* @param direction direction of the get, Ascending or Descending
* @returns an array with the keys retrieved
* @example
* ```ts
* const keys = Objects.getManyObjKeys(new test_key(1), 10, Space.Direction.Descending);
*
* for (let index = 0; index < keys.length; index++) {
* const key = keys[index];
* }
* ```
*/
getManyObjKeys(offsetKey: TKey, limit: i32 = i32.MAX_VALUE, direction: Direction = Direction.Ascending): TKey[] {
const result: TKey[] = [];

let key = Protobuf.encode(offsetKey, this.keyEncoder);

let done = false;
do {
// @ts-ignore key is always initialized when reaching this code
const obj = direction == Direction.Ascending ? System.getNextObject<Uint8Array, TValue>(this.space, key, this.valueDecoder) : System.getPrevObject<Uint8Array, TValue>(this.space, key, this.valueDecoder);
if (obj) {
key = obj.key!;
result.push(Protobuf.decode(key, this.keyDecoder));
}

done = obj == null || result.length >= limit;
} while (!done);

return result;
}

/**
* Get the next object from the space
* @param key key to get next
Expand All @@ -279,6 +425,7 @@ export namespace Space {
* }
* ```
*/
// @ts-ignore valid in AS
getNext(key: TKey): System.ProtoDatabaseObject<TValue> | null {
const finalKey = Protobuf.encode(key, this.keyEncoder);
return super.getNext(finalKey);
Expand All @@ -298,6 +445,7 @@ export namespace Space {
* }
* ```
*/
// @ts-ignore valid in AS
getPrev(key: TKey): System.ProtoDatabaseObject<TValue> | null {
const finalKey = Protobuf.encode(key, this.keyEncoder);
return super.getPrev(finalKey);
Expand All @@ -315,6 +463,7 @@ export namespace Space {
* System.log(nbBytesWritten.toString());
* ```
*/
// @ts-ignore valid in AS
put(key: TKey, object: TValue): i32 {
const finalKey = Protobuf.encode(key, this.keyEncoder);
return super.put(finalKey, object);
Expand All @@ -328,6 +477,7 @@ export namespace Space {
* Objects.remove(new test_key(1));
* ```
*/
// @ts-ignore valid in AS
remove(key: TKey): void {
const finalKey = Protobuf.encode(key, this.keyEncoder);
super.remove(finalKey);
Expand Down

0 comments on commit 3b83344

Please sign in to comment.