ソースを参照

LibJS: Implement Uint8Array.prototype.toHex

Timothy Flynn 1 年間 前
コミット
c69d6fab8f

+ 1 - 0
Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h

@@ -533,6 +533,7 @@ namespace JS {
     P(toExponential)                         \
     P(toFixed)                               \
     P(toGMTString)                           \
+    P(toHex)                                 \
     P(toInstant)                             \
     P(toISOString)                           \
     P(toJSON)                                \

+ 27 - 0
Userland/Libraries/LibJS/Runtime/Uint8Array.cpp

@@ -5,6 +5,7 @@
  */
 
 #include <AK/Base64.h>
+#include <AK/StringBuilder.h>
 #include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/TypedArray.h>
 #include <LibJS/Runtime/Uint8Array.h>
@@ -19,6 +20,7 @@ void Uint8ArrayPrototypeHelpers::initialize(Realm& realm, Object& prototype)
 
     static constexpr u8 attr = Attribute::Writable | Attribute::Configurable;
     prototype.define_native_function(realm, vm.names.toBase64, to_base64, 0, attr);
+    prototype.define_native_function(realm, vm.names.toHex, to_hex, 0, attr);
 }
 
 static ThrowCompletionOr<Alphabet> parse_alphabet(VM& vm, Object& options)
@@ -85,6 +87,31 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_base64)
     return PrimitiveString::create(vm, move(out_ascii));
 }
 
+// 2 Uint8Array.prototype.toHex ( ), https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64
+JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_hex)
+{
+    // 1. Let O be the this value.
+    // 2. Perform ? ValidateUint8Array(O).
+    auto typed_array = TRY(validate_uint8_array(vm));
+
+    // 3. Let toEncode be ? GetUint8ArrayBytes(O).
+    auto to_encode = TRY(get_uint8_array_bytes(vm, typed_array));
+
+    // 4. Let out be the empty String.
+    StringBuilder out;
+
+    // 5. For each byte byte of toEncode, do
+    for (auto byte : to_encode.bytes()) {
+        // a. Let hex be Number::toString(𝔽(byte), 16).
+        // b. Set hex to StringPad(hex, 2, "0", START).
+        // c. Set out to the string-concatenation of out and hex.
+        out.appendff("{:02x}", byte);
+    }
+
+    // 6. Return out.
+    return PrimitiveString::create(vm, MUST(out.to_string()));
+}
+
 // 7 ValidateUint8Array ( ta ), https://tc39.es/proposal-arraybuffer-base64/spec/#sec-validateuint8array
 ThrowCompletionOr<NonnullGCPtr<TypedArrayBase>> validate_uint8_array(VM& vm)
 {

+ 1 - 0
Userland/Libraries/LibJS/Runtime/Uint8Array.h

@@ -17,6 +17,7 @@ public:
 
 private:
     JS_DECLARE_NATIVE_FUNCTION(to_base64);
+    JS_DECLARE_NATIVE_FUNCTION(to_hex);
 };
 
 enum class Alphabet {

+ 59 - 0
Userland/Libraries/LibJS/Tests/builtins/TypedArray/Uint8Array.prototype.toHex.js

@@ -0,0 +1,59 @@
+describe("errors", () => {
+    test("called on non-Uint8Array object", () => {
+        expect(() => {
+            Uint8Array.prototype.toHex.call(1);
+        }).toThrowWithMessage(TypeError, "Not an object of type Uint8Array");
+
+        expect(() => {
+            Uint8Array.prototype.toHex.call(new Uint16Array());
+        }).toThrowWithMessage(TypeError, "Not an object of type Uint8Array");
+    });
+
+    test("detached ArrayBuffer", () => {
+        let arrayBuffer = new ArrayBuffer(5, { maxByteLength: 10 });
+        let typedArray = new Uint8Array(arrayBuffer, Uint8Array.BYTES_PER_ELEMENT, 1);
+        detachArrayBuffer(arrayBuffer);
+
+        expect(() => {
+            typedArray.toHex();
+        }).toThrowWithMessage(
+            TypeError,
+            "TypedArray contains a property which references a value at an index not contained within its buffer's bounds"
+        );
+    });
+
+    test("ArrayBuffer out of bounds", () => {
+        let arrayBuffer = new ArrayBuffer(Uint8Array.BYTES_PER_ELEMENT * 2, {
+            maxByteLength: Uint8Array.BYTES_PER_ELEMENT * 4,
+        });
+
+        let typedArray = new Uint8Array(arrayBuffer, Uint8Array.BYTES_PER_ELEMENT, 1);
+        arrayBuffer.resize(Uint8Array.BYTES_PER_ELEMENT);
+
+        expect(() => {
+            typedArray.toHex();
+        }).toThrowWithMessage(
+            TypeError,
+            "TypedArray contains a property which references a value at an index not contained within its buffer's bounds"
+        );
+    });
+});
+
+describe("correct behavior", () => {
+    test("length is 0", () => {
+        expect(Uint8Array.prototype.toHex).toHaveLength(0);
+    });
+
+    const encodeEqual = (input, expected) => {
+        const encoded = toUTF8Bytes(input).toHex();
+        expect(encoded).toBe(expected);
+    };
+
+    test("basic functionality", () => {
+        encodeEqual("", "");
+        encodeEqual("a", "61");
+        encodeEqual("abcdef012345", "616263646566303132333435");
+        encodeEqual("🤓", "f09fa493");
+        encodeEqual("🤓foo🖖", "f09fa493666f6ff09f9696");
+    });
+});