packages/codec/lib/bytes/decode/index.ts
import debugModule from "debug";
const debug = debugModule("codec:bytes:decode");
import read from "@truffle/codec/read";
import * as Conversion from "@truffle/codec/conversion";
import type * as Format from "@truffle/codec/format";
import type * as Pointer from "@truffle/codec/pointer";
import type { DecoderRequest, DecoderOptions } from "@truffle/codec/types";
import type * as Evm from "@truffle/codec/evm";
import { handleDecodingError } from "@truffle/codec/errors";
import utf8 from "utf8";
export function* decodeBytes(
dataType: Format.Types.BytesTypeDynamic | Format.Types.StringType,
pointer: Pointer.DataPointer,
info: Evm.EvmInfo,
options: DecoderOptions = {}
): Generator<DecoderRequest, Format.Values.Result, Uint8Array | null> {
const { state } = info;
const { strictAbiMode: strict } = options; //if this is undefined it'll still be falsy so OK
let bytes: Uint8Array;
try {
bytes = yield* read(pointer, state);
} catch (error) {
debug("segfault, pointer %o, state: %O", pointer, state);
return handleDecodingError(dataType, error, strict);
}
debug("type %O", dataType);
debug("pointer %o", pointer);
//note: this function does not check padding
switch (dataType.typeClass) {
case "bytes":
//we assume this is a dynamic bytestring!
//static ones should go to decodeBasic!
return {
type: dataType,
kind: "value" as const,
value: {
asHex: Conversion.toHexString(bytes)
},
interpretations: {}
};
case "string":
return {
type: dataType,
kind: "value" as const,
value: decodeString(bytes),
interpretations: {}
};
}
}
export function decodeString(bytes: Uint8Array): Format.Values.StringValueInfo {
//the following line takes our UTF-8 string... and interprets each byte
//as a UTF-16 bytepair. Yikes! Fortunately, we have a library to repair that.
let badlyEncodedString = String.fromCharCode.apply(undefined, bytes);
try {
//this will throw an error if we have malformed UTF-8
let correctlyEncodedString = utf8.decode(badlyEncodedString);
//NOTE: we don't use node's builtin Buffer class to do the UTF-8 decoding
//here, because that handles malformed UTF-8 by means of replacement characters
//(U+FFFD). That loses information. So we use the utf8 package instead,
//and... well, see the catch block below.
return {
kind: "valid" as const,
asString: correctlyEncodedString
};
} catch {
//we're going to ignore the precise error and just assume it's because
//the string was malformed (what else could it be?)
let hexString = Conversion.toHexString(bytes);
return {
kind: "malformed" as const,
asHex: hexString
};
}
}