Skip to content

Commit

Permalink
feat(codec): molecule pack works with BytesLike
Browse files Browse the repository at this point in the history
  • Loading branch information
homura committed Jul 28, 2022
1 parent 1669bf5 commit 31c578e
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 18 deletions.
12 changes: 5 additions & 7 deletions packages/codec/src/blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import {
import { bytify, hexify } from "./bytes";
import { byteVecOf, option, table, vector } from "./molecule";

export const createFixedHexBytesCodec = (
export function createFixedHexBytesCodec(
byteLength: number
): FixedBytesCodec<string> =>
createFixedBytesCodec<string>({
): FixedBytesCodec<string, BytesLike> {
return createFixedBytesCodec({
byteLength,
pack: (hex) => bytify(hex),
unpack: (buf) => hexify(buf),
});
}

/**
* placeholder codec, generally used as a placeholder
Expand All @@ -31,10 +32,7 @@ export const createFixedHexBytesCodec = (
// export const UnusedOpt = option(Unknown);

// vector Bytes <byte>
export const Bytes = byteVecOf<string>({
pack: (hex) => bytify(hex),
unpack: (buf) => hexify(buf),
});
export const Bytes = byteVecOf({ pack: bytify, unpack: hexify });

export const BytesOpt = option(Bytes);
export const BytesVec = vector(Bytes);
Expand Down
10 changes: 7 additions & 3 deletions packages/codec/src/molecule/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ export function byteArrayOf<Packed, Packable = Packed>(
* a helper function to create custom codec of `byte`
* @param codec
*/
export function byteOf<T>(codec: BytesCodec<T>): FixedBytesCodec<T> {
export function byteOf<Packed, Packable = Packed>(
codec: BytesCodec<Packed, Packable>
): FixedBytesCodec<Packed, Packable> {
return byteArrayOf({ ...codec, byteLength: 1 });
}

/**
* a helper function to create custom codec of `vector Bytes <byte>`
* @param codec
*/
export const byteVecOf = <T>(codec: BytesCodec<T>): BytesCodec<T> => {
export function byteVecOf<Packed, Packable = Packed>(
codec: BytesCodec<Packed, Packable>
): BytesCodec<Packed, Packable> {
return createBytesCodec({
pack(unpacked) {
const payload = codec.pack(unpacked);
Expand All @@ -50,4 +54,4 @@ export const byteVecOf = <T>(codec: BytesCodec<T>): BytesCodec<T> => {
return codec.unpack(packed.slice(4));
},
});
};
}
31 changes: 23 additions & 8 deletions packages/codec/src/molecule/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
* | union | item-type-id | item |
*/

import type { BytesCodec, Fixed, FixedBytesCodec, UnpackResult } from "../base";
import type {
BytesCodec,
Fixed,
FixedBytesCodec,
PackParam,
UnpackResult,
} from "../base";
import { createBytesCodec, createFixedBytesCodec, isFixedCodec } from "../base";
import { Uint32LE } from "../number";
import { concat } from "../bytes";
Expand All @@ -24,24 +30,27 @@ type NonNullableKeys<O extends Record<string, unknown>> = {

// prettier-ignore
type PartialNullable<O extends Record<string, unknown>> =
& Partial<Pick<O, NullableKeys<O>>>
& Pick<O, NonNullableKeys<O>>;
& Partial<Pick<O, NullableKeys<O>>>
& Pick<O, NonNullableKeys<O>>;

export type ObjectCodec<T extends Record<string, BytesCodec>> = BytesCodec<
PartialNullable<{ [key in keyof T]: UnpackResult<T[key]> }>
PartialNullable<{ [key in keyof T]: UnpackResult<T[key]> }>,
PartialNullable<{ [key in keyof T]: PackParam<T[key]> }>
>;

export interface OptionCodec<T extends BytesCodec>
extends BytesCodec<UnpackResult<T> | undefined> {
pack: (packable?: UnpackResult<T>) => Uint8Array;
pack: (packable?: PackParam<T>) => Uint8Array;
}

export type ArrayCodec<T extends BytesCodec> = BytesCodec<
Array<UnpackResult<T>>
Array<UnpackResult<T>>,
Array<PackParam<T>>
>;

export type UnionCodec<T extends Record<string, BytesCodec>> = BytesCodec<
{ [key in keyof T]: { type: key; value: UnpackResult<T[key]> } }[keyof T]
{ [key in keyof T]: { type: key; value: UnpackResult<T[key]> } }[keyof T],
{ [key in keyof T]: { type: key; value: PackParam<T[key]> } }[keyof T]
>;

export function array<T extends FixedBytesCodec>(
Expand Down Expand Up @@ -280,9 +289,15 @@ export function union<T extends Record<string, BytesCodec>>(
return createBytesCodec({
pack(obj) {
const type = obj.type;

/* c8 ignore next */
if (typeof type !== "string") {
throw new Error(`Invalid type in union, type must be a string`);
}

const fieldIndex = fields.indexOf(type);
if (fieldIndex === -1) {
throw new Error(`Unknown union type: ${obj.type}`);
throw new Error(`Unknown union type: ${type}`);
}
const packedFieldIndex = Uint32LE.pack(fieldIndex);
const packedBody = itemCodec[type].pack(obj.value);
Expand Down
46 changes: 46 additions & 0 deletions packages/codec/tests/bytes-like.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import test from "ava";
import { blockchain, molecule, number } from "../src";
import { randomBytes } from "crypto";
import { concat, hexify } from "../src/bytes";
import { BI } from "@ckb-lumos/bi";

const { struct } = molecule;
const { Uint32 } = number;
const { Byte32 } = blockchain;

test("pack with BytesLike and BIish", (t) => {
const OutPoint = struct({ tx_hash: Byte32, index: Uint32 }, [
"tx_hash",
"index",
]);

const txHash = randomBytes(32);

const packed1 = OutPoint.pack({ tx_hash: txHash, index: 0 });
const packed2 = OutPoint.pack({ tx_hash: hexify(txHash), index: "0x0" });
const packed3 = OutPoint.pack({ tx_hash: txHash, index: "0x0" });
const packed4 = OutPoint.pack({ tx_hash: txHash, index: BI.from(0) });

t.deepEqual(packed1, concat(txHash, "0x00000000"));
t.deepEqual(packed1, packed2);
t.deepEqual(packed2, packed3);
t.deepEqual(packed3, packed4);
});

test("unpack with BytesLike", (t) => {
const OutPoint = struct({ tx_hash: Byte32, index: Uint32 }, [
"tx_hash",
"index",
]);

const txHash = randomBytes(32);
const index = "0x00000000";

const unpacked1 = OutPoint.unpack(concat(txHash, index));
const unpacked2 = OutPoint.unpack(hexify(concat(txHash, index)));
const unpacked3 = OutPoint.unpack(Array.from(concat(txHash, index)));

t.deepEqual(unpacked1, { tx_hash: hexify(txHash), index: 0 });
t.deepEqual(unpacked1, unpacked2);
t.deepEqual(unpacked2, unpacked3);
});

0 comments on commit 31c578e

Please sign in to comment.