Closed jafri closed 2 years ago
Optional
only supports classes that extend Packer
currently. Wrapping u64 in a class can solve this kind of problem.
Here is an alternative that can support Optional<u64>
but for some reasons, I don't think it's a better solution.
import { Packer, Encoder, Decoder } from "./serializer"
import { Utils } from "./utils"
export class OptionalValue<T> {
value: T
constructor(
value: T
) {
this.value = value;
}
}
export class Optional<T> implements Packer {
_value: usize
constructor(
value: T | null = null
) {
if (value) {
this._value = changetype<usize>(new OptionalValue<T>(value));
} else {
this._value = 0;
}
}
get value(): T | null {
if (this._value) {
let value = changetype<OptionalValue<T>>(this._value);
return value.value;
} else {
return null;
}
}
pack(): u8[] {
let enc = new Encoder(this.getSize())
if (this._value) {
enc.packNumber<u8>(1);
let value = changetype<OptionalValue<T>>(this._value);
if (isInteger<T>()) {
enc.packNumber<T>(value.value);
} else if (isString<T>()) {
enc.packString(changetype<string>(value.value));
} else {
enc.pack(changetype<Packer>(value.value));
}
return enc.getBytes();
} else {
enc.packNumber<u8>(0);
return enc.getBytes();
}
}
unpack(data: u8[]): usize {
let dec = new Decoder(data)
let hasValue = dec.unpackNumber<u8>();
if (!hasValue) {
this._value = 0;
return 1;
}
if (isInteger<T>()) {
let obj = dec.unpackNumber<T>();
this._value = changetype<usize>(new OptionalValue<T>(obj));
} else if (isString<T>()) {
let obj = dec.unpackString();
this._value = changetype<usize>(new OptionalValue<string>(obj));
} else {
let obj = instantiate<T>();
dec.unpack(obj);
this._value = changetype<usize>(new OptionalValue<T>(obj));
}
return dec.getPos();
}
getSize(): usize {
if (!this._value) {
return 1;
}
if (isInteger<T>()) {
return 1 + sizeof<T>();
} else if (isString<T>()) {
let value = changetype<OptionalValue<string>>(this._value);
return 1 + Utils.calcPackedStringLength(value.value);
} else if (isReference<T>()) {
let value = changetype<OptionalValue<T>>(this._value);
return 1 + value.value.getSize();
}
return 1 + this._value.getSize();
}
}
Should be fixed by fc91b75d5e3142de33ce37208ca38e131e4a44ba by introducing two new classes OptionalNumber
and OptionalString
.
@learnforpractice I don't think this optional follows correctly
class MyClass {
a1: Optional<Asset>;
a2: OptionalNumber<u64>;
a3: OptionalString;
constructor(a1: Asset | null = null, a2: u64 = 0, a3: string="") {
this.a1 = new Optional<Asset>(a1);
this.a2 = new OptionalNumber<u64>(a2);
this.a3 = new OptionalString(a3);
}
}
Example code
const item = new MyClass(null)
print(item.a2.hasValue) // print true ??
print(item.a3.hasValue) // print true ??
We should also add get hasValue
to Optional
for consistent interface
That's how it suppose to work, see the constructor in OptionalNumber:
export class OptionalNumber<T> implements Packer {
value: T;
hasValue: bool;
constructor(
value: T = 0, hasValue: bool = true
)
the default value of hasValue
is true.
Optional u64 types are supported in normal eosio code and ABI, but not possible in ascdk