aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ai Hoshino <ambiguous404@gmail.com> 2023-07-02 08:01:52 +0800
committerGravatar GitHub <noreply@github.com> 2023-07-01 17:01:52 -0700
commit4720fa1207d374a2447d457ad478f9f8911b959a (patch)
treee8268b3c91ea992842682afd94f32747668a136f
parentdf10252979aa3d87a8d127707a23678b76a15583 (diff)
downloadbun-4720fa1207d374a2447d457ad478f9f8911b959a.tar.gz
bun-4720fa1207d374a2447d457ad478f9f8911b959a.tar.zst
bun-4720fa1207d374a2447d457ad478f9f8911b959a.zip
[WIP]Fix calling `Buffer.toString` with `(offset, length, encoding)` (#3467)
* Allow `toString` to be called with `(offset, length, encoding)`. Close: #3085 * handle undefined value * add tests for buffer.xxxSlice * fix parameters * fix offset and length
Diffstat (limited to '')
-rw-r--r--src/bun.js/bindings/JSBuffer.cpp67
-rw-r--r--src/js/builtins/JSBufferPrototype.ts32
-rw-r--r--test/js/node/buffer.test.js79
3 files changed, 135 insertions, 43 deletions
diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp
index 00965da89..4b0e058dd 100644
--- a/src/bun.js/bindings/JSBuffer.cpp
+++ b/src/bun.js/bindings/JSBuffer.cpp
@@ -1436,43 +1436,56 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
if (length == 0)
return JSC::JSValue::encode(JSC::jsEmptyString(vm));
- switch (callFrame->argumentCount()) {
- case 0: {
- break;
- }
- case 2:
- case 3:
- case 1: {
- EnsureStillAliveScope arg1 = callFrame->uncheckedArgument(0);
- if (!arg1.value().isUndefined()) {
- encoding = parseEncoding(lexicalGlobalObject, scope, arg1.value());
+ size_t argsCount = callFrame->argumentCount();
+
+ JSC::JSValue arg1 = callFrame->argument(0);
+ JSC::JSValue arg2 = callFrame->argument(1);
+ JSC::JSValue arg3 = callFrame->argument(2);
+
+ // This method could be called in following forms:
+ // - toString()
+ // - toString(encoding)
+ // - toString(encoding, start)
+ // - toString(encoding, start, end)
+ // - toString(offset, length)
+ // - toString(offset, length, encoding)
+ if (argsCount == 0)
+ return jsBufferToString(vm, lexicalGlobalObject, castedThis, offset, length, encoding);
+
+ if (arg1.isString()) {
+ encoding = parseEncoding(lexicalGlobalObject, scope, arg1);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined()));
- }
- if (callFrame->argumentCount() == 1)
- break;
- }
- // any
- case 5: {
- JSC::JSValue arg2 = callFrame->uncheckedArgument(1);
- int32_t ioffset = arg2.toInt32(lexicalGlobalObject);
+
+ if (!arg3.isUndefined()) {
+ // length is end
+ length = std::min(byteLength, static_cast<uint32_t>(arg3.toInt32(lexicalGlobalObject)));
+ }
+
+ int32_t istart = arg2.toInt32(lexicalGlobalObject);
+ if (istart < 0) {
+ throwTypeError(lexicalGlobalObject, scope, "Start must be a positive integer"_s);
+ return JSC::JSValue::encode(jsUndefined());
+ }
+ offset = static_cast<uint32_t>(istart);
+ length = (length > offset) ? (length - offset) : 0;
+ } else {
+ int32_t ioffset = arg1.toInt32(lexicalGlobalObject);
if (ioffset < 0) {
throwTypeError(lexicalGlobalObject, scope, "Offset must be a positive integer"_s);
return JSC::JSValue::encode(jsUndefined());
}
offset = static_cast<uint32_t>(ioffset);
+ length = (length > offset) ? (length - offset) : 0;
- if (callFrame->argumentCount() == 2)
- break;
- }
+ if (!arg3.isUndefined()) {
+ encoding = parseEncoding(lexicalGlobalObject, scope, arg3);
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined()));
+ }
- default: {
- length = std::min(byteLength, static_cast<uint32_t>(callFrame->argument(2).toInt32(lexicalGlobalObject)));
- break;
- }
+ if (!arg2.isUndefined())
+ length = std::min(length, static_cast<uint32_t>(arg2.toInt32(lexicalGlobalObject)));
}
- length -= std::min(offset, length);
-
return jsBufferToString(vm, lexicalGlobalObject, castedThis, offset, length, encoding);
}
diff --git a/src/js/builtins/JSBufferPrototype.ts b/src/js/builtins/JSBufferPrototype.ts
index 97b25b9b2..f5d6a7bfb 100644
--- a/src/js/builtins/JSBufferPrototype.ts
+++ b/src/js/builtins/JSBufferPrototype.ts
@@ -427,29 +427,29 @@ export function hexWrite(this: BufferExt, text, offset, length) {
return this.write(text, offset, length, "hex");
}
-export function utf8Slice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "utf8");
+export function utf8Slice(this: BufferExt, start, end) {
+ return this.toString("utf8", start, end);
}
-export function ucs2Slice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "ucs2");
+export function ucs2Slice(this: BufferExt, start, end) {
+ return this.toString("ucs2", start, end);
}
-export function utf16leSlice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "utf16le");
+export function utf16leSlice(this: BufferExt, start, end) {
+ return this.toString("utf16le", start, end);
}
-export function latin1Slice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "latin1");
+export function latin1Slice(this: BufferExt, start, end) {
+ return this.toString("latin1", start, end);
}
-export function asciiSlice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "ascii");
+export function asciiSlice(this: BufferExt, start, end) {
+ return this.toString("ascii", start, end);
}
-export function base64Slice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "base64");
+export function base64Slice(this: BufferExt, start, end) {
+ return this.toString("base64", start, end);
}
-export function base64urlSlice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "base64url");
+export function base64urlSlice(this: BufferExt, start, end) {
+ return this.toString("base64url", start, end);
}
-export function hexSlice(this: BufferExt, offset, length) {
- return this.toString(offset, length, "hex");
+export function hexSlice(this: BufferExt, start, end) {
+ return this.toString("hex", start, end);
}
export function toJSON(this: BufferExt) {
diff --git a/test/js/node/buffer.test.js b/test/js/node/buffer.test.js
index 697774e0a..e0d8f5486 100644
--- a/test/js/node/buffer.test.js
+++ b/test/js/node/buffer.test.js
@@ -2353,6 +2353,85 @@ it("Buffer.byteLength()", () => {
}
});
+it("Buffer.toString(encoding, start, end)", () => {
+ const buf = Buffer.from("0123456789", "utf8");
+
+ expect(buf.toString()).toStrictEqual("0123456789");
+ expect(buf.toString("utf8")).toStrictEqual("0123456789");
+ expect(buf.toString("utf8", 3)).toStrictEqual("3456789");
+ expect(buf.toString("utf8", 3, 4)).toStrictEqual("3");
+
+ expect(buf.toString("utf8", 3, 100)).toStrictEqual("3456789");
+ expect(buf.toString("utf8", 3, 1)).toStrictEqual("");
+ expect(buf.toString("utf8", 100, 200)).toStrictEqual("");
+ expect(buf.toString("utf8", 100, 1)).toStrictEqual("");
+});
+
+it("Buffer.toString(offset, length, encoding)", () => {
+ const buf = Buffer.from("0123456789", "utf8");
+
+ expect(buf.toString(3, 6, "utf8")).toStrictEqual("345678");
+ expect(buf.toString(3, 100, "utf8")).toStrictEqual("3456789");
+ expect(buf.toString(100, 200, "utf8")).toStrictEqual("");
+ expect(buf.toString(100, 50, "utf8")).toStrictEqual("");
+});
+
+it("Buffer.asciiSlice())", () => {
+ const buf = Buffer.from("0123456789", "ascii");
+
+ expect(buf.asciiSlice()).toStrictEqual("0123456789");
+ expect(buf.asciiSlice(3)).toStrictEqual("3456789");
+ expect(buf.asciiSlice(3, 4)).toStrictEqual("3");
+});
+
+it("Buffer.latin1Slice()", () => {
+ const buf = Buffer.from("âéö", "latin1");
+
+ expect(buf.latin1Slice()).toStrictEqual("âéö");
+ expect(buf.latin1Slice(1)).toStrictEqual("éö");
+ expect(buf.latin1Slice(1, 2)).toStrictEqual("é");
+});
+
+it("Buffer.utf8Slice()", () => {
+ const buf = Buffer.from("あいうえお", "utf8");
+
+ expect(buf.utf8Slice()).toStrictEqual("あいうえお");
+ expect(buf.utf8Slice(3)).toStrictEqual("いうえお");
+ expect(buf.utf8Slice(3, 6)).toStrictEqual("い");
+});
+
+it("Buffer.hexSlice()", () => {
+ const buf = Buffer.from("0123456789", "utf8");
+
+ expect(buf.hexSlice()).toStrictEqual("30313233343536373839");
+ expect(buf.hexSlice(3)).toStrictEqual("33343536373839");
+ expect(buf.hexSlice(3, 4)).toStrictEqual("33");
+});
+
+it("Buffer.ucs2Slice()", () => {
+ const buf = Buffer.from("あいうえお", "ucs2");
+
+ expect(buf.ucs2Slice()).toStrictEqual("あいうえお");
+ expect(buf.ucs2Slice(2)).toStrictEqual("いうえお");
+ expect(buf.ucs2Slice(2, 6)).toStrictEqual("いう");
+});
+
+it("Buffer.base64Slice()", () => {
+ const buf = Buffer.from("0123456789", "utf8");
+
+ expect(buf.base64Slice()).toStrictEqual("MDEyMzQ1Njc4OQ==");
+ expect(buf.base64Slice(3)).toStrictEqual("MzQ1Njc4OQ==");
+ expect(buf.base64Slice(3, 4)).toStrictEqual("Mw==");
+});
+
+it("Buffer.base64urlSlice()", () => {
+ const buf = Buffer.from("0123456789", "utf8");
+
+ expect(buf.base64urlSlice()).toStrictEqual("MDEyMzQ1Njc4OQ");
+ expect(buf.base64urlSlice(3)).toStrictEqual("MzQ1Njc4OQ");
+ expect(buf.base64urlSlice(3, 4)).toStrictEqual("Mw");
+});
+
it("should not crash on invalid UTF-8 byte sequence", () => {
const buf = Buffer.from([0xc0, 0xfd]);
expect(buf.length).toBe(2);