Skip to content

Commit

Permalink
Move encode/decode to dedicated module
Browse files Browse the repository at this point in the history
  • Loading branch information
dansteren committed Oct 1, 2023
1 parent 6b254ae commit a3fc7a3
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 75 deletions.
69 changes: 69 additions & 0 deletions src/lib_functional/candid/serde.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { IDL } from '@dfinity/candid';

import { AzleVec, AzleOpt, AzleTuple } from '../../lib_new';
import { toIDLType } from '../../lib_new/utils';
import {
DecodeVisitor,
EncodeVisitor
} from '../../lib_new/visitors/encode_decode';
import { CandidType } from '../';

/**
* Encodes the provided value as candid blob of the designated type.
*
* Intended to be a rich replacement for `IDL.encode` from `@dfinity/candid`,
* adding support for complex Azle IDL wrappers such as {@link AzleOpt},
* {@link AzleVec}, and {@link AzleTuple}. It recursively visits all "inner"
* values, converting any Azle values to official IDL values.
*
* @param data the value to encode
* @param fakeIdl either a built-in IDL data type, or an Azle-defined super-type
* @returns candid bytes
*/
export function encode(
data: any,
fakeIdl: IDL.Type<any> | CandidType
): Uint8Array {
// TODO: there is a discrepancy between CandidType and CandidClass that
// needs to be aligned so that this isn't an error. Both are representing
// candid IDLs, either from the @dfinity/candid library or the
// Azle-augmented ones
const realIDL = toIDLType(fakeIdl, []);

const encodeReadyKey = realIDL.accept(new EncodeVisitor(), {
js_class: fakeIdl,
js_data: data
});

return new Uint8Array(IDL.encode([realIDL], [encodeReadyKey]));
}

/**
* Decodes the provided buffer into the designated JS value.
*
* Intended to be a rich replacement for `IDL.decode` from `@dfinity/candid`
* adding support for complex Azle IDL wrappers such as {@link AzleOpt},
* {@link AzleVec}, and {@link AzleTuple}. It recursively visits all "inner"
* values, converting them from their native shape to the shape that Azle expects.
*
* @param data the value to decode
* @param fakeIdl either a built-in IDL data type, or an Azle-defined super-type
* @returns the Azle representation of the data
*/
export function decode(
data: ArrayBuffer,
fakeIdl: IDL.Type<any> | CandidType
): any {
// TODO: there is a discrepancy between CandidType and CandidClass that
// needs to be aligned so that this isn't an error. Both are representing
// candid IDLs, either from the @dfinity/candid library or the
// Azle-augmented ones
const realIDL = toIDLType(fakeIdl, []);

const candidDecodedValue = IDL.decode([realIDL], data)[0] as any;

return realIDL.accept(new DecodeVisitor(), {
js_class: fakeIdl,
js_data: candidDecodedValue
});
}
99 changes: 24 additions & 75 deletions src/lib_new/stable_b_tree_map.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
import { CandidType, None, Some, TypeMapping } from '../lib_functional';
import { IDL, nat8, nat64, Opt } from './index';
import { CandidClass, toIDLType } from './utils';
import { DecodeVisitor, EncodeVisitor } from './visitors/encode_decode';

function visitAndEncode<T>(fakeIdl: CandidClass, data: T): Uint8Array {
const realIDL = toIDLType(fakeIdl, []);

const encodeReadyKey = realIDL.accept(new EncodeVisitor(), {
js_class: fakeIdl,
js_data: data
});

return new Uint8Array(IDL.encode([realIDL], [encodeReadyKey]));
}

function decodeAndVisit<T>(fakeIdl: CandidClass, data: ArrayBuffer): any {
const realIDL = toIDLType(fakeIdl, []);

const candidDecodedValue = IDL.decode([realIDL], data)[0] as any;

return realIDL.accept(new DecodeVisitor(), {
js_class: fakeIdl,
js_data: candidDecodedValue
});
}
import { toIDLType } from './utils';
import { encode, decode } from '../lib_functional/candid/serde';

export function StableBTreeMap<
Key extends CandidType,
Expand All @@ -47,11 +25,8 @@ export function StableBTreeMap<
* @returns `true` if the key exists in the map, `false` otherwise.
*/
containsKey(key: TypeMapping<Key>): boolean {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedKey = visitAndEncode(keyType, key).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;
const candidEncodedKey = encode(key, keyType).buffer;

return (globalThis as any)._azleIc.stableBTreeMapContainsKey(
candidEncodedMemoryId,
Expand All @@ -64,11 +39,8 @@ export function StableBTreeMap<
* @returns the value associated with the given key, if it exists.
*/
get(key: TypeMapping<Key>): Opt<TypeMapping<Value>> {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedKey = visitAndEncode(keyType, key).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;
const candidEncodedKey = encode(key, keyType).buffer;

const candidEncodedValue = (
globalThis as any
Expand All @@ -80,7 +52,7 @@ export function StableBTreeMap<
if (candidEncodedValue === undefined) {
return None;
} else {
return Some(decodeAndVisit(valueType, candidEncodedValue));
return Some(decode(candidEncodedValue, valueType));
}
},
/**
Expand All @@ -93,12 +65,9 @@ export function StableBTreeMap<
key: TypeMapping<Key>,
value: TypeMapping<Value>
): Opt<TypeMapping<Value>> {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedKey = visitAndEncode(keyType, key).buffer;
const candidEncodedValue = visitAndEncode(valueType, value).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;
const candidEncodedKey = encode(key, keyType).buffer;
const candidEncodedValue = encode(value, valueType).buffer;

const candidEncodedResultValue = (
globalThis as any
Expand All @@ -111,20 +80,15 @@ export function StableBTreeMap<
if (candidEncodedResultValue === undefined) {
return None;
} else {
return Some(
decodeAndVisit(valueType, candidEncodedResultValue)
);
return Some(decode(candidEncodedResultValue, valueType));
}
},
/**
* Checks if the map is empty.
* @returns `true` if the map contains no elements, `false` otherwise.
*/
isEmpty(): boolean {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;

return (globalThis as any)._azleIc.stableBTreeMapIsEmpty(
candidEncodedMemoryId
Expand All @@ -135,10 +99,7 @@ export function StableBTreeMap<
* @returns tuples representing key/value pairs.
*/
items(): [TypeMapping<Key>, TypeMapping<Value>][] {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;

const candidEncodedItems = (
globalThis as any
Expand All @@ -147,8 +108,8 @@ export function StableBTreeMap<
// TODO too much copying
return candidEncodedItems.map((candidEncodedItem: any) => {
return [
decodeAndVisit(keyType, candidEncodedItem[0]),
decodeAndVisit(valueType, candidEncodedItem[1])
decode(candidEncodedItem[0], keyType),
decode(candidEncodedItem[1], valueType)
];
});
},
Expand All @@ -157,47 +118,38 @@ export function StableBTreeMap<
* @returns they keys in the map.
*/
keys(): TypeMapping<Key>[] {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;

const candidEncodedKeys = (
globalThis as any
)._azleIc.stableBTreeMapKeys(candidEncodedMemoryId);

// TODO too much copying
return candidEncodedKeys.map((candidEncodedKey: any) => {
return decodeAndVisit(keyType, candidEncodedKey);
return decode(candidEncodedKey, keyType);
});
},
/**
* Checks to see how many elements are in the map.
* @returns the number of elements in the map.
*/
len(): nat64 {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;

const candidEncodedLen = (
globalThis as any
)._azleIc.stableBTreeMapLen(candidEncodedMemoryId);

return decodeAndVisit(IDL.Nat64, candidEncodedLen);
return decode(candidEncodedLen, IDL.Nat64);
},
/**
* Removes a key from the map.
* @param key the location from which to remove.
* @returns the previous value at the key if it exists, `null` otherwise.
*/
remove(key: TypeMapping<Key>): Opt<TypeMapping<Value>> {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedKey = visitAndEncode(keyType, key).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;
const candidEncodedKey = encode(key, keyType).buffer;

const candidEncodedValue = (
globalThis as any
Expand All @@ -209,26 +161,23 @@ export function StableBTreeMap<
if (candidEncodedValue === undefined) {
return None;
} else {
return Some(decodeAndVisit(valueType, candidEncodedValue));
return Some(decode(candidEncodedValue, valueType));
}
},
/**
* The values in the map in sorted order.
* @returns the values in the map.
*/
values(): TypeMapping<Value>[] {
const candidEncodedMemoryId = visitAndEncode(
IDL.Nat8,
memoryId
).buffer;
const candidEncodedMemoryId = encode(memoryId, IDL.Nat8).buffer;

const candidEncodedValues = (
globalThis as any
)._azleIc.stableBTreeMapValues(candidEncodedMemoryId);

// TODO too much copying
return candidEncodedValues.map((candidEncodedValue: any) => {
return decodeAndVisit(valueType, candidEncodedValue);
return decode(candidEncodedValue, valueType);
});
}
};
Expand Down

0 comments on commit a3fc7a3

Please sign in to comment.