Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cbor/unstable): introduce @std/cbor #5909

Merged
merged 60 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
4f07166
setup(cbor): workspace to look for `cbor/`
BlackAsLight Sep 4, 2024
a410e42
feat(cbor): `new CborEncoder()`
BlackAsLight Sep 4, 2024
953a60d
feat(cbor): `new CborDecoder()`
BlackAsLight Sep 4, 2024
4c295c2
fix(cbor): `@module` being in the wrong file
BlackAsLight Sep 4, 2024
5e96fc0
chore(cbor): `deno fmt`
BlackAsLight Sep 4, 2024
b36ce13
docs(cbor): Updated `CborTag` docs
BlackAsLight Sep 4, 2024
3a330ba
fix(cbor): link:docs
BlackAsLight Sep 4, 2024
0a6eb45
docs(cbor): Updated `CborEncoder` docs
BlackAsLight Sep 4, 2024
b5d5f3c
docs(cbor): Updated `CborDecoder` docs
BlackAsLight Sep 4, 2024
794a4a7
Merge branch 'main' into cbor
BlackAsLight Sep 4, 2024
b544f7f
github(cbor): Update GitHub Action to recognise CBOR
BlackAsLight Sep 4, 2024
6c59514
github(cbor): Update GitHub Action to recognise cbor/
BlackAsLight Sep 4, 2024
5228297
Merge branch 'main' into cbor
kt3k Sep 5, 2024
8c597e7
refactor(cbor): `CborEncoder` to `encodeCbor`
BlackAsLight Sep 8, 2024
29ef8ac
feat(cbor): CborEncoderStreams
BlackAsLight Sep 8, 2024
b479d7d
fix(cbor): random bug _maybe_
BlackAsLight Sep 9, 2024
2a746b9
fix(cbor): bug where `CborTextEncoderStream` was filtering out empty …
BlackAsLight Sep 9, 2024
ace4b9c
chore(cbor): fmt
BlackAsLight Sep 9, 2024
35b5b1e
Merge branch 'main' into cbor
BlackAsLight Sep 9, 2024
b851450
refactor(cbor): `CborDecoder` to `decodeCbor`
BlackAsLight Sep 9, 2024
049931d
feat(cbor): CborDecoderStreams
BlackAsLight Sep 18, 2024
e1780ea
fix(cbor): missing error messages from CborDecoderStreams
BlackAsLight Sep 18, 2024
8d93b05
tests(cbor): added one test
BlackAsLight Sep 18, 2024
df3692c
tests(cbor): added more tests for `encodeCbor()` testing errors
BlackAsLight Sep 18, 2024
ab8bf00
fix(cbor): CborDecoderStreams not handling empty strings correctly
BlackAsLight Sep 18, 2024
0e77bc3
tests(cbor): added more tests for `decodeCbor()` testing errors
BlackAsLight Sep 18, 2024
89e22a5
tests(cbor): finished writing tests for `decodeCbor()`, hopefully
BlackAsLight Sep 19, 2024
baf3281
Merge branch 'main' into cbor
BlackAsLight Sep 19, 2024
60ca9ae
chore(cbor): remove floating `console.log`
BlackAsLight Sep 19, 2024
de12cf1
Merge branch 'main' into cbor
BlackAsLight Sep 20, 2024
bd93905
fix(cbor): new linting error
BlackAsLight Sep 20, 2024
1d2827d
chore(cbor): clean up imports
BlackAsLight Sep 20, 2024
7aec71d
tests(cbor): Completed the last of the tests for `decodeCbor()`
BlackAsLight Sep 20, 2024
60bfa11
tests(cbor): Improved `CborTag()` test for `encodeCbor()`
BlackAsLight Sep 20, 2024
4314b6c
feat(cbor): `encodeCborSequence()`
BlackAsLight Sep 20, 2024
8465276
feat(cbor): `decodeCborSequence()`
BlackAsLight Sep 20, 2024
1b88c9a
tests(cbor): Added more tests for CborEncoderStreams
BlackAsLight Sep 20, 2024
52aa06d
fix(cbor): broken tests
BlackAsLight Sep 20, 2024
ceeece0
tests(cbor): improved tests for decode_stream.ts
BlackAsLight Sep 21, 2024
6557f25
fix(cbor): decode_stream.ts when dealing with empty streams
BlackAsLight Sep 21, 2024
8519017
fix(cbor): docs
BlackAsLight Sep 21, 2024
718fd18
docs(cbor): Filled in all @examples
BlackAsLight Sep 21, 2024
97b0622
docs(cbor): Improved JSDocs for all exports.
BlackAsLight Sep 22, 2024
4cb8742
docs(cbor): fix import statment in examples
BlackAsLight Sep 22, 2024
92e98b5
Merge branch 'main' into cbor
BlackAsLight Sep 22, 2024
ec405c5
fix(cbor): deno.json missing export & bumped minor version
BlackAsLight Sep 23, 2024
89e9b53
fix(cbor): import_map
BlackAsLight Sep 23, 2024
1a0f200
Merge branch 'main' into cbor
BlackAsLight Sep 24, 2024
efe8438
refactor(cbor): so every API has it's own file.
BlackAsLight Sep 24, 2024
a9987c4
fix(cbor): bug where `new Uint8Array(0)` would cause a promise to nev…
BlackAsLight Sep 24, 2024
1cb2a8b
tests(cbor): improved to make them more uniform
BlackAsLight Sep 24, 2024
4ec394e
refactor(cbor): `upgradeStreamFromGen()`
BlackAsLight Sep 24, 2024
8b3afc6
Merge branch 'main' into cbor
BlackAsLight Sep 25, 2024
d2acc77
replace(cbor): upgradeStreamFromGen with toByteStream
BlackAsLight Oct 2, 2024
ff7bb4e
Merge branch 'main' into cbor
BlackAsLight Oct 4, 2024
2502a75
fix(cbor): bug where CborTextEncoderStream filtered out empty strings
BlackAsLight Oct 4, 2024
d7ff661
make decoded streams non-entrypoint
kt3k Oct 8, 2024
de9e89f
rename Cbor(Map){Output,Input}Stream to Cbor(Map)Stream{Output,Input}
kt3k Oct 8, 2024
edc7f82
reduce max string length in testing
kt3k Oct 8, 2024
ec68c5c
add export of decoded streams from cbor/sequence-decoder-stream
kt3k Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions _tools/check_circular_package_dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Mod =
| "async"
| "bytes"
| "cache"
| "cbor"
| "cli"
| "collections"
| "crypto"
Expand Down Expand Up @@ -83,6 +84,7 @@ const ENTRYPOINTS: Record<Mod, string[]> = {
async: ["mod.ts"],
bytes: ["mod.ts"],
cache: ["mod.ts"],
cbor: ["mod.ts"],
cli: ["mod.ts"],
collections: ["mod.ts"],
crypto: ["mod.ts"],
Expand Down
1 change: 1 addition & 0 deletions _tools/check_docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const ENTRY_POINTS = [
"../async/mod.ts",
"../bytes/mod.ts",
"../cache/mod.ts",
"../cbor/mod.ts",
"../cli/mod.ts",
"../cli/unstable_spinner.ts",
"../crypto/mod.ts",
Expand Down
1 change: 1 addition & 0 deletions browser-compat.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"./async",
"./bytes",
"./cache",
"./cbor",
"./cli",
"./collections",
"./crypto",
Expand Down
62 changes: 62 additions & 0 deletions cbor/_array_decoded_stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import type { ReleaseLock } from "./_common.ts";
import type { CborStreamOutput } from "./types.ts";

/**
* A {@link ReadableStream} that wraps the decoded CBOR "Array".
* [RFC 8949 - Concise Binary Object Representation (CBOR)](https://datatracker.ietf.org/doc/html/rfc8949)
*
* Instances of this class is created from {@link CborSequenceDecoderStream}.
* This class is not designed for you to create instances of it yourself. It is
* merely a way for you to validate the type being returned.
*
* @example Usage
* ```ts
* import { assert, assertEquals } from "@std/assert";
* import {
* CborArrayDecodedStream,
* CborArrayEncoderStream,
* CborSequenceDecoderStream,
* } from "@std/cbor";
*
* const rawMessage = ["a".repeat(100), "b".repeat(100), "c".repeat(100)];
*
* for await (
* const value of ReadableStream.from(rawMessage)
* .pipeThrough(new CborArrayEncoderStream())
* .pipeThrough(new CborSequenceDecoderStream())
* ) {
* assert(value instanceof CborArrayDecodedStream);
* let i = 0;
* for await (const text of value) {
* assert(typeof text === "string");
* assertEquals(text, rawMessage[i++]);
* }
* }
* ```
*/
export class CborArrayDecodedStream extends ReadableStream<CborStreamOutput> {
/**
* Constructs a new instance.
*
* @param gen A {@link AsyncGenerator<CborStreamOutput>}.
* @param releaseLock A Function that's called when the stream is finished.
*/
constructor(gen: AsyncGenerator<CborStreamOutput>, releaseLock: ReleaseLock) {
super({
async pull(controller) {
const { done, value } = await gen.next();
if (done) {
releaseLock();
controller.close();
} else controller.enqueue(value);
},
async cancel() {
// deno-lint-ignore no-empty
for await (const _ of gen) {}
releaseLock();
},
});
}
}
48 changes: 48 additions & 0 deletions cbor/_array_decoded_stream_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import { assert, assertEquals } from "@std/assert";
import { random } from "./_common_test.ts";
import { CborArrayDecodedStream } from "./_array_decoded_stream.ts";
import { encodeCbor } from "./encode_cbor.ts";
import { CborSequenceDecoderStream } from "./sequence_decoder_stream.ts";

Deno.test("CborArrayDecodedStream() being consumed", async () => {
const size = random(0, 24);

const reader = ReadableStream.from([encodeCbor(new Array(size).fill(0))])
.pipeThrough(new CborSequenceDecoderStream()).getReader();

const { done, value } = await reader.read();
assert(done === false);
assert(value instanceof CborArrayDecodedStream);
assertEquals(await Array.fromAsync(value), new Array(size).fill(0));

assert((await reader.read()).done === true);
reader.releaseLock();
});

Deno.test("CborArrayDecodedStream() being cancelled", async () => {
const size = random(0, 24);
const reader = ReadableStream.from([
encodeCbor(new Array(size).fill(0)),
encodeCbor(0),
])
.pipeThrough(new CborSequenceDecoderStream()).getReader();

{
const { done, value } = await reader.read();
assert(done === false);
assert(value instanceof CborArrayDecodedStream);
await value.cancel();
}

{
const { done, value } = await reader.read();
assert(done === false);
assert(typeof value === "number");
assertEquals(value, 0);
}

assert((await reader.read()).done === true);
reader.releaseLock();
});
60 changes: 60 additions & 0 deletions cbor/_byte_decoded_stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import type { ReleaseLock } from "./_common.ts";

/**
* A {@link ReadableStream} that wraps the decoded CBOR "Byte String".
* [RFC 8949 - Concise Binary Object Representation (CBOR)](https://datatracker.ietf.org/doc/html/rfc8949)
*
* Instances of this class is created from {@link CborSequenceDecoderStream}.
* This class is not designed for you to create instances of it yourself. It is
* merely a way for you to validate the type being returned.
*
* @example Usage
* ```ts
* import { assert, assertEquals } from "@std/assert";
* import { concat } from "@std/bytes";
* import {
* CborByteDecodedStream,
* CborByteEncoderStream,
* CborSequenceDecoderStream,
* } from "@std/cbor";
*
* const rawMessage = new Uint8Array(100);
*
* for await (
* const value of ReadableStream.from([rawMessage])
* .pipeThrough(new CborByteEncoderStream())
* .pipeThrough(new CborSequenceDecoderStream())
* ) {
* assert(value instanceof Uint8Array || value instanceof CborByteDecodedStream);
* if (value instanceof CborByteDecodedStream) {
* assertEquals(concat(await Array.fromAsync(value)), new Uint8Array(100));
* } else assertEquals(value, new Uint8Array(100));
* }
* ```
*/
export class CborByteDecodedStream extends ReadableStream<Uint8Array> {
/**
* Constructs a new instance.
*
* @param gen A {@link AsyncGenerator<Uint8Array>}.
* @param releaseLock A Function that's called when the stream is finished.
*/
constructor(gen: AsyncGenerator<Uint8Array>, releaseLock: ReleaseLock) {
super({
async pull(controller) {
const { done, value } = await gen.next();
if (done) {
releaseLock();
controller.close();
} else controller.enqueue(value);
},
async cancel() {
// deno-lint-ignore no-empty
for await (const _ of gen) {}
releaseLock();
},
});
}
}
80 changes: 80 additions & 0 deletions cbor/_byte_decoded_stream_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import { assert, assertEquals } from "@std/assert";
import { concat } from "@std/bytes";
import { random } from "./_common_test.ts";
import { CborByteDecodedStream } from "./_byte_decoded_stream.ts";
import { CborByteEncoderStream } from "./byte_encoder_stream.ts";
import { encodeCbor } from "./encode_cbor.ts";
import { CborSequenceDecoderStream } from "./sequence_decoder_stream.ts";
import { CborSequenceEncoderStream } from "./sequence_encoder_stream.ts";

Deno.test("CborByteDecodedStream() consuming indefinite length byte string", async () => {
const size = random(0, 24);

const reader = CborByteEncoderStream.from([
new Uint8Array(size),
new Uint8Array(size * 2),
new Uint8Array(size * 3),
]).readable.pipeThrough(new CborSequenceDecoderStream()).getReader();

const { done, value } = await reader.read();
assert(done === false);
assert(value instanceof CborByteDecodedStream);
assertEquals(await Array.fromAsync(value), [
new Uint8Array(size),
new Uint8Array(size * 2),
new Uint8Array(size * 3),
]);

assert((await reader.read()).done === true);
reader.releaseLock();
});

Deno.test("CborByteDecodedStream() consuming large definite length byte string", async () => {
// Uint8Array needs to be 2 ** 32 bytes+ to be decoded via a CborByteDecodedStream.
const size = random(2 ** 32, 2 ** 33);

const reader = ReadableStream.from([encodeCbor(new Uint8Array(size))])
.pipeThrough(new CborSequenceDecoderStream()).getReader();

const { done, value } = await reader.read();
assert(done === false);
assert(value instanceof CborByteDecodedStream);
assertEquals(concat(await Array.fromAsync(value)).length, size);

assert((await reader.read()).done === true);
reader.releaseLock();
});

Deno.test("CborByteDecodedStream() being cancelled", async () => {
const size = random(0, 24);

const reader = ReadableStream.from([
CborByteEncoderStream.from([
new Uint8Array(size),
new Uint8Array(size * 2),
new Uint8Array(size * 3),
]),
0,
])
.pipeThrough(new CborSequenceEncoderStream())
.pipeThrough(new CborSequenceDecoderStream()).getReader();

{
const { done, value } = await reader.read();
assert(done === false);
assert(value instanceof CborByteDecodedStream);
await value.cancel();
}

{
const { done, value } = await reader.read();
assert(done === false);
assert(typeof value === "number");
assertEquals(value, 0);
}

assert((await reader.read()).done === true);
reader.releaseLock();
});
92 changes: 92 additions & 0 deletions cbor/_common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

export type ReleaseLock = (value?: unknown) => void;

export function numberToArray(bytes: number, x: number | bigint): Uint8Array {
const view = new DataView(new ArrayBuffer(8));
if (typeof x === "bigint" || x % 1 === 0) view.setBigUint64(0, BigInt(x));
else view.setFloat64(0, x);
return new Uint8Array(view.buffer.slice(-bytes));
}

export function arrayToNumber(
buffer: ArrayBufferLike & { BYTES_PER_ELEMENT?: never },
isInteger: true,
): number | bigint;
export function arrayToNumber(
buffer: ArrayBufferLike & { BYTES_PER_ELEMENT?: never },
isInteger: false,
): number;
export function arrayToNumber(
buffer: ArrayBufferLike & { BYTES_PER_ELEMENT?: never },
isInteger: boolean,
): number | bigint {
const view = new DataView(buffer);
if (isInteger) {
switch (buffer.byteLength) {
case 1:
return view.getUint8(0);
case 2:
return view.getUint16(0);
case 4:
return view.getUint32(0);
default:
return view.getBigUint64(0);
}
}
switch (buffer.byteLength) {
case 2:
return view.getFloat16(0);

Check warning on line 39 in cbor/_common.ts

View check run for this annotation

Codecov / codecov/patch

cbor/_common.ts#L39

Added line #L39 was not covered by tests
case 4:
return view.getFloat32(0);

Check warning on line 41 in cbor/_common.ts

View check run for this annotation

Codecov / codecov/patch

cbor/_common.ts#L41

Added line #L41 was not covered by tests
default:
return view.getFloat64(0);
}
}

// To be removed if https://github.com/denoland/std/pull/6046 merges.
export function toByteStream(
readable: ReadableStream<Uint8Array>,
): ReadableStream<Uint8Array> {
try {
const reader = readable.getReader({ mode: "byob" });
reader.releaseLock();
return readable;
} catch {
const reader = readable.getReader();
return new ReadableStream({
type: "bytes",
async pull(controller) {
const value = await async function () {
while (true) {
const { done, value } = await reader.read();
if (done) return undefined;
if (value.length) return value;
}
}();

if (value == undefined) {
controller.close();
return controller.byobRequest?.respond(0);
}

if (controller.byobRequest?.view) {
const buffer = new Uint8Array(controller.byobRequest.view.buffer);
const offset = controller.byobRequest.view.byteOffset;
const size = buffer.length - offset;
if (value.length > size) {
buffer.set(value.slice(0, size), offset);
controller.byobRequest.respond(size);
controller.enqueue(value.slice(size));
} else {
buffer.set(value, offset);
controller.byobRequest.respond(value.length);
}
} else controller.enqueue(value);
},
async cancel(reason) {
await reader.cancel(reason);
},

Check warning on line 89 in cbor/_common.ts

View check run for this annotation

Codecov / codecov/patch

cbor/_common.ts#L87-L89

Added lines #L87 - L89 were not covered by tests
});
}
}
Loading