Skip to content

Commit

Permalink
feat(json-pack): 🎸 add support for CID decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Mar 12, 2024
1 parent 651e22b commit 41ba07d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 26 deletions.
12 changes: 6 additions & 6 deletions src/json-pack/json/JsonDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ const readShortUtf8StrAndUnescape = (reader: Reader): string => {
export class JsonDecoder implements BinaryJsonDecoder {
public reader = new Reader();

public read(uint8: Uint8Array): PackValue {
public read(uint8: Uint8Array): unknown {
this.reader.reset(uint8);
return this.readAny();
}
Expand All @@ -195,7 +195,7 @@ export class JsonDecoder implements BinaryJsonDecoder {
return this.readAny();
}

public readAny(): PackValue {
public readAny(): unknown {
this.skipWhitespace();
const reader = this.reader;
const x = reader.x;
Expand Down Expand Up @@ -640,10 +640,10 @@ export class JsonDecoder implements BinaryJsonDecoder {
return bin;
}

public readArr(): PackValue[] {
public readArr(): unknown[] {
const reader = this.reader;
if (reader.u8() !== 0x5b) throw new Error('Invalid JSON');
const arr: PackValue[] = [];
const arr: unknown[] = [];
const uint8 = reader.uint8;
while (true) {
this.skipWhitespace();
Expand All @@ -657,10 +657,10 @@ export class JsonDecoder implements BinaryJsonDecoder {
}
}

public readObj(): PackValue | Record<string, PackValue> {
public readObj(): PackValue | Record<string, unknown> | unknown {
const reader = this.reader;
if (reader.u8() !== 0x7b) throw new Error('Invalid JSON');
const obj: Record<string, PackValue> = {};
const obj: Record<string, unknown> = {};
const uint8 = reader.uint8;
while (true) {
this.skipWhitespace();
Expand Down
50 changes: 49 additions & 1 deletion src/json-pack/json/JsonDecoderDag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {createFromBase64Bin} from '../../util/base64/createFromBase64Bin';
export const fromBase64Bin = createFromBase64Bin(undefined, '');

export class JsonDecoderDag extends JsonDecoder {
public readObj(): PackValue | Record<string, PackValue> | Uint8Array {
public readObj(): PackValue | Record<string, PackValue> | Uint8Array | unknown {
const bytes = this.tryReadBytes();
if (bytes) return bytes;
const cid = this.tryReadCid();
if (cid) return cid;
return super.readObj();
}

Expand Down Expand Up @@ -82,4 +84,50 @@ export class JsonDecoderDag extends JsonDecoder {
const bin = fromBase64Bin(reader.view, bufStart, bufEnd - bufStart);
return bin;
}

protected tryReadCid(): undefined | unknown {
const reader = this.reader;
const x = reader.x;
if (reader.u8() !== 0x7b) {
// {
reader.x = x;
return;
}
this.skipWhitespace();
if (reader.u8() !== 0x22 || reader.u8() !== 0x2f || reader.u8() !== 0x22) {
// "/"
reader.x = x;
return;
}
this.skipWhitespace();
if (reader.u8() !== 0x3a) {
// :
reader.x = x;
return;
}
this.skipWhitespace();
if (reader.u8() !== 0x22) {
// "
reader.x = x;
return;
}
const bufStart = reader.x;
const bufEnd = findEndingQuote(reader.uint8, bufStart);
reader.x = 1 + bufEnd;
this.skipWhitespace();
if (reader.u8() !== 0x7d) {
// }
reader.x = x;
return;
}
const finalX = reader.x;
reader.x = bufStart;
const cid = reader.ascii(bufEnd - bufStart);
reader.x = finalX;
return this.readCid(cid);
}

public readCid(cid: string): unknown {
return cid;
}
}
76 changes: 59 additions & 17 deletions src/json-pack/json/__tests__/JsonDecoderDag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,66 @@ const writer = new Writer(16);
const encoder = new JsonEncoderDag(writer);
const decoder = new JsonDecoderDag();

test('can decode a simple buffer in object', () => {
const buf = utf8`hello world`;
const data = {foo: buf};
const encoded = encoder.encode(data);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual(data);
});
describe('Bytes', () => {
test('can decode a simple buffer in object', () => {
const buf = utf8`hello world`;
const data = {foo: buf};
const encoded = encoder.encode(data);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual(data);
});

test('can decode buffers inside an array', () => {
const data = [0, utf8``, utf8`asdf`, 1];
const encoded = encoder.encode(data);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual(data);
});

test('can decode buffers inside an array', () => {
const data = [0, utf8``, utf8`asdf`, 1];
const encoded = encoder.encode(data);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual(data);
test('can decode buffer with whitespace surrounding literals', () => {
const json = ' { "foo" : { "/" : { "bytes" : "aGVsbG8gd29ybGQ" } } } ';
const encoded = Buffer.from(json);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual({foo: utf8`hello world`});
});
});

test('can decode buffer with whitespace surrounding literals', () => {
const json = ' { "foo" : { "/" : { "bytes" : "aGVsbG8gd29ybGQ" } } } ';
const encoded = Buffer.from(json);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual({foo: utf8`hello world`});
describe('Cid', () => {
class CID {
constructor(public readonly value: string) {}
}

class IpfsEncoder extends JsonEncoderDag {
public writeUnknown(value: unknown): void {
if (value instanceof CID) return this.writeCid(value.value);
else super.writeUnknown(value);
}
}

class IpfsDecoder extends JsonDecoderDag {
public readCid(cid: string): unknown {
return new CID(cid);
}
}

const encoder = new IpfsEncoder(writer);
const decoder = new IpfsDecoder();

test('can decode a single CID', () => {
const data = new CID('Qm');
const encoded = encoder.encode(data);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual(data);
});

test('can decode a CID in object and array', () => {
const data = {
foo: 'bar',
baz: new CID('Qm'),
qux: [new CID('bu'), 'quux'],
};
const encoded = encoder.encode(data);
const decoded = decoder.decode(encoded);
expect(decoded).toEqual(data);
});
});
9 changes: 8 additions & 1 deletion src/json-pack/json/__tests__/JsonEncoderDag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,17 @@ describe('Cid', () => {

const encoder = new IpfsEncoder(writer);

test('can encode a simple buffer in array', () => {
test('can encode a CID as object key', () => {
const data = {id: new CID('QmXn5v3z')};
const encoded = encoder.encode(data);
const json = Buffer.from(encoded).toString();
expect(json).toBe('{"id":{"/":"QmXn5v3z"}}');
});

test('can encode a CID in array', () => {
const data = ['a', new CID('b'), 'c'];
const encoded = encoder.encode(data);
const json = Buffer.from(encoded).toString();
expect(json).toBe('["a",{"/":"b"},"c"]');
});
});
2 changes: 1 addition & 1 deletion src/json-pack/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ export interface TlvBinaryJsonEncoder {
export interface BinaryJsonDecoder {
decode(uint8: Uint8Array): unknown;
reader: IReader & IReaderResettable;
read(uint8: Uint8Array): PackValue;
read(uint8: Uint8Array): unknown;
}

0 comments on commit 41ba07d

Please sign in to comment.