From cb684d50c1e8dcfed09ad820825cef70914f9c0b Mon Sep 17 00:00:00 2001 From: Takuro Sato <79583855+takuro-sato@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:37:01 +0000 Subject: [PATCH] Partially support TextEncoder Web API (#5863) --- js/ccf-app/src/textcodec.ts | 61 ++++++++++++++++++++++++++++++++ js/ccf-app/test/polyfill.test.ts | 34 ++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 js/ccf-app/src/textcodec.ts diff --git a/js/ccf-app/src/textcodec.ts b/js/ccf-app/src/textcodec.ts new file mode 100644 index 000000000000..b34d035fbc86 --- /dev/null +++ b/js/ccf-app/src/textcodec.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. + +/** + * The `textcodec` module provides access to TextEncoder Web API class. + * + * Example: + * ``` + * import * as ccftextcodec from '@microsoft/ccf-app/textcodec.js'; + * + * const bytes = new ccftextcodec.TextEncoder().encode("foo") + * ``` + * + * If you need TextEncoder Web API as a globally accessible class: + * ``` + * import * as ccftextcodec from '@microsoft/ccf-app/textcodec.js'; + * + * if (globalThis != undefined && (globalThis as any).TextEncoder == undefined) { + * (globalThis as any).TextEncoder = ccftextcodec.TextEncoder; + * } + * + * ``` + * + * @module + */ + +import { ccf } from "./global.js"; + +export type TextEncoderEncodeIntoResult = { + read?: number; + written?: number; +}; + +/** + * TextEncoder can be used to encode string to Uint8Array. + */ +export class TextEncoder { + /** + * Always returns "utf-8". + */ + public readonly encoding: string = "utf-8"; + + /** + * Returns Uint8Array containing UTF-8 encoded text. + * @param input Input string to encode. + * @returns Encoded bytes. + */ + encode(input: string): Uint8Array { + return new Uint8Array(ccf.strToBuf(input)); + } + + /** + * Not implemented. + * @param input + * @param output + * @throws Always throws an Error object. + */ + encodeInto(input: string, output: Uint8Array): TextEncoderEncodeIntoResult { + throw new Error("Not implemented"); + } +} diff --git a/js/ccf-app/test/polyfill.test.ts b/js/ccf-app/test/polyfill.test.ts index 3f4c7274d59d..ef4ddf430325 100644 --- a/js/ccf-app/test/polyfill.test.ts +++ b/js/ccf-app/test/polyfill.test.ts @@ -8,6 +8,7 @@ import { RsaOaepAesKwpParams, RsaOaepParams, } from "../src/global.js"; +import * as textcodec from "../src/textcodec.js"; import { generateSelfSignedCert, generateCertChain } from "./crypto.js"; beforeEach(function () { @@ -24,6 +25,39 @@ describe("polyfill", function () { assert.equal(ccf.bufToStr(ccf.strToBuf(s)), s); }); }); + describe("TextEncoder", function () { + it("returns utf-8 for encoding field", function () { + const encoder = new textcodec.TextEncoder(); + assert.equal(encoder.encoding, "utf-8"); + const s = encoder.encode("foo"); + }); + it("returns an empty array for default empty input", function () { + assert.deepEqual( + new textcodec.TextEncoder().encode(""), + new Uint8Array(), + ); + }); + it("encodes ascii strings correctly", function () { + const sample = "foo"; + assert.deepEqual( + new textcodec.TextEncoder().encode(sample), + new Uint8Array([0x66, 0x6f, 0x6f]), + ); + }); + it("encodes UTF-8 strings correctly", function () { + // a (U+0061, 0x61 in UTF-8), pound sign (U+00A3, 0xC2 0xA3 in UTF-8) + const sample = "\u0061\u00A3"; + assert.deepEqual( + new textcodec.TextEncoder().encode(sample), + new Uint8Array([0x61, 0xc2, 0xa3]), + ); + }); + it("throws when unsupported method is called", function () { + assert.throws(() => + new textcodec.TextEncoder().encodeInto("test", new Uint8Array([])), + ); + }); + }); describe("jsonCompatibleToBuf/bufToJsonCompatible", function () { it("converts JSON-compatible <--> ArrayBuffer", function () { const s = { foo: "bar" };