Compare commits

...

51 Commits

Author SHA1 Message Date
Meghan Denny
3a22eb7b24 Merge remote-tracking branch 'origin/main' into nektro-patch-52129 2025-01-22 16:08:46 -08:00
Meghan Denny
c052ebfddf Merge branch 'main' into nektro-patch-52129 2024-12-23 09:41:48 -08:00
Jarred Sumner
ab1e149cc7 Merge branch 'main' into nektro-patch-52129 2024-12-14 23:54:08 -08:00
Meghan Denny
8b17029b06 dont create JSString for ASCIILiteral 2024-12-13 22:43:03 -08:00
Meghan Denny
9201b88dcf address comments 2024-12-13 22:38:23 -08:00
Meghan Denny
fc3a95d834 dont create JSString for ASCIILiteral 2024-12-13 22:33:43 -08:00
Meghan Denny
4d49408ea3 only read getters once 2024-12-13 22:33:08 -08:00
Meghan Denny
37d3472b27 these commented tests are fixed 2024-12-13 22:32:30 -08:00
Meghan Denny
48eff0f334 share implementation 2024-12-13 18:38:49 -08:00
Meghan Denny
befcab8421 cleaner name 2024-12-13 18:25:37 -08:00
Meghan Denny
4a332e6317 fix linux x64 2024-12-13 18:25:25 -08:00
Meghan Denny
b692263c6f fix windows build 2024-12-13 16:56:38 -08:00
Meghan Denny
eeb2803a54 fix includes on linux, memmem on macos isn't posix compliant 2024-12-13 16:54:20 -08:00
Meghan Denny
15c43040df fix test-buffer-includes.js 2024-12-13 02:36:09 -08:00
Meghan Denny
e49cc4dcbf fix test-buffer-indexof.js 2024-12-13 02:35:49 -08:00
Meghan Denny
1cf31b8488 webkit upgrade for test-buffer-alloc.js 2024-12-12 21:38:55 -08:00
Meghan Denny
18b29695f5 fix test-stream-unshift-empty-chunk.js regression 2024-12-12 21:38:24 -08:00
Meghan Denny
9dff935580 Merge remote-tracking branch 'origin/main' into nektro-patch-52129 2024-12-12 20:54:22 -08:00
Meghan Denny
7a911c12cb Merge remote-tracking branch 'origin/main' into nektro-patch-52129 2024-12-12 20:28:38 -08:00
Meghan Denny
3778be8605 misc fixes 2024-12-12 19:51:57 -08:00
Meghan Denny
58b7515f3e move main impl of Buffer.from back to JSBufferConstructor 2024-12-12 17:10:28 -08:00
Meghan Denny
d0eb4a6a6a fix test-buffer-sharedarraybuffer.js 2024-12-12 02:20:54 -08:00
Meghan Denny
d45c4d8bfb fix test-buffer-from.js 2024-12-12 02:19:48 -08:00
Meghan Denny
d56586eec6 fix test-buffer-badhex.js 2024-12-11 23:47:53 -08:00
Meghan Denny
d13a47f3be fix regressions 2024-12-11 22:18:37 -08:00
Meghan Denny
3b3c29e0d2 add scripts/check-node-all.sh 2024-12-11 19:36:09 -08:00
Meghan Denny
8fd46d40d3 fix regression 2024-12-11 19:35:47 -08:00
Meghan Denny
72e5b7e60f fixed correctness issue in ERR_INVALID_ARG_TYPE 2024-12-11 19:35:33 -08:00
Meghan Denny
3f810a3b2e fix test-buffer-arraybuffer.js 2024-12-11 18:57:48 -08:00
Meghan Denny
af0fd9d318 fix test-buffer-backing-arraybuffer.js 2024-12-11 18:14:46 -08:00
Meghan Denny
db74f9c224 fix test-buffer-bigint64.js 2024-12-11 18:09:48 -08:00
Meghan Denny
ba86ff19ed fix test-buffer-new.js 2024-12-11 17:08:24 -08:00
Meghan Denny
f108c3c6b6 fix test-buffer-read.js 2024-12-11 16:17:31 -08:00
Meghan Denny
5b650bc2e5 fix test-buffer-readdouble.js 2024-12-11 15:50:44 -08:00
Meghan Denny
237e4df38c fix test-buffer-readfloat.js 2024-12-11 15:50:17 -08:00
Meghan Denny
89795bfe35 fix test-buffer-readint.js 2024-12-11 15:30:46 -08:00
Meghan Denny
eef6fc8496 fix test-buffer-readuint.js 2024-12-11 15:15:53 -08:00
Meghan Denny
28a7c40e29 fix test-buffer-write.js 2024-12-11 01:07:41 -08:00
Meghan Denny
8a9b9f5eb2 fix test-buffer-writedouble.js 2024-12-11 01:06:54 -08:00
Meghan Denny
bdb97724aa fix test-buffer-writefloat.js 2024-12-11 01:05:31 -08:00
Meghan Denny
ba777b8b76 fix test-buffer-writeint.js 2024-12-11 01:01:30 -08:00
Meghan Denny
814ac87c8a fix test-buffer-writeuint.js 2024-12-11 00:53:50 -08:00
Meghan Denny
7535c58462 fix test-buffer-fill.js 2024-12-10 23:17:26 -08:00
Meghan Denny
158c581be8 fix test-buffer-equals.js 2024-12-10 20:46:48 -08:00
Meghan Denny
0168fa8593 fix test-buffer-copy.js 2024-12-10 20:41:41 -08:00
Meghan Denny
fde51e72fd fix test-buffer-constants.js 2024-12-10 19:49:09 -08:00
Meghan Denny
b2077dd113 fix test-buffer-concat.js 2024-12-10 19:47:56 -08:00
Meghan Denny
975a339830 fix test-buffer-compare.js 2024-12-10 19:28:24 -08:00
Meghan Denny
5d00fcde4c fix test-buffer-bytelength.js 2024-12-10 19:27:18 -08:00
Meghan Denny
2699eda438 fix test-buffer-alloc.js 2024-12-10 19:24:29 -08:00
Meghan Denny
2a1a431c86 fix test-buffer-zero-fill-cli.js 2024-12-10 16:32:39 -08:00
47 changed files with 6130 additions and 499 deletions

View File

@@ -789,7 +789,6 @@ pub const FormatOptions = struct {
if (try arg1.getBooleanLoose(globalThis, "sorted")) |opt| {
formatOptions.ordered_properties = opt;
}
if (try arg1.getBooleanLoose(globalThis, "compact")) |opt| {
formatOptions.single_line = opt;
}
@@ -2262,7 +2261,7 @@ pub const Formatter = struct {
this.addForNewLine(description.len + "()".len);
writer.print(comptime Output.prettyFmt("<r><blue>Symbol({any})<r>", enable_ansi_colors), .{description});
} else {
writer.print(comptime Output.prettyFmt("<r><blue>Symbol<r>", enable_ansi_colors), .{});
writer.print(comptime Output.prettyFmt("<r><blue>Symbol()<r>", enable_ansi_colors), .{});
}
},
.Error => {

View File

@@ -205,7 +205,6 @@ JSObject* createError(Zig::JSGlobalObject* globalObject, ErrorCode code, JSC::JS
// export fn Bun__inspect(globalThis: *JSGlobalObject, value: JSValue) ZigString
extern "C" ZigString Bun__inspect(JSC::JSGlobalObject* globalObject, JSValue value);
//
WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg)
{
ASSERT(!arg.isEmpty());
@@ -379,13 +378,17 @@ WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* gl
unsigned length = expected_types.size();
if (length == 1) {
result.append(expected_types.at(0).toWTFString(globalObject));
} else if (length == 2) {
result.append(expected_types.at(0).toWTFString(globalObject));
result.append(", or "_s);
result.append(expected_types.at(1).toWTFString(globalObject));
} else {
for (unsigned i = 0; i < length - 1; i++) {
JSValue expected_type = expected_types.at(i);
if (i > 0) result.append(", "_s);
result.append(expected_type.toWTFString(globalObject));
}
result.append(" or "_s);
result.append(", or "_s);
result.append(expected_types.at(length - 1).toWTFString(globalObject));
}
@@ -434,6 +437,12 @@ WTF::String ERR_OUT_OF_RANGE(JSC::ThrowScope& scope, JSC::JSGlobalObject* global
namespace ERR {
JSC::EncodedJSValue throwCode(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ErrorCode code, const WTF::String& message)
{
throwScope.throwException(globalObject, createError(globalObject, code, message));
return {};
}
JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& arg_name, const WTF::String& expected_type, JSC::JSValue val_actual_value)
{
auto arg_kind = arg_name.contains('.') ? "property"_s : "argument"_s;
@@ -529,7 +538,7 @@ JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObjec
return {};
}
JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, WTF::ASCIILiteral name, JSC::JSValue value, const WTF::String& reason)
JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& name, JSC::JSValue value, const WTF::String& reason)
{
ASCIILiteral type = String(name).contains('.') ? "property"_s : "argument"_s;
@@ -614,8 +623,12 @@ JSC::EncodedJSValue STRING_TOO_LONG(JSC::ThrowScope& throwScope, JSC::JSGlobalOb
return {};
}
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject)
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& name)
{
if (!name.isEmpty()) {
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_BUFFER_OUT_OF_BOUNDS, makeString("\""_s, name, "\" is outside of buffer bounds"_s)));
return {};
}
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_BUFFER_OUT_OF_BOUNDS, "Attempt to access memory outside buffer bounds"_s));
return {};
}

View File

@@ -65,11 +65,13 @@ enum Bound {
namespace ERR {
JSC::EncodedJSValue throwCode(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ErrorCode code, const WTF::String& message);
JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& arg_name, const WTF::String& expected_type, JSC::JSValue val_actual_value);
JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name, const WTF::String& expected_type, JSC::JSValue val_actual_value);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& arg_name, double lower, double upper, JSC::JSValue actual);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name, double lower, double upper, JSC::JSValue actual);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name_val, double bound_num, Bound bound, JSC::JSValue actual);
JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue val_arg_name, const WTF::String& val_expected_type, JSC::JSValue val_actual_value);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& arg_name, size_t lower, size_t upper, JSC::JSValue actual);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name, size_t lower, size_t upper, JSC::JSValue actual);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name_val, size_t bound_num, Bound bound, JSC::JSValue actual);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name_val, const WTF::String& msg, JSC::JSValue actual);
JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& arg_name_val, const WTF::String& msg, JSC::JSValue actual);
JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, WTF::ASCIILiteral name, JSC::JSValue value, const WTF::String& reason = "is invalid"_s);
@@ -78,7 +80,7 @@ JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobal
JSC::EncodedJSValue UNKNOWN_ENCODING(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::StringView encoding);
JSC::EncodedJSValue INVALID_STATE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& statemsg);
JSC::EncodedJSValue STRING_TOO_LONG(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& name);
JSC::EncodedJSValue UNKNOWN_SIGNAL(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue signal, bool triedUppercase = false);
JSC::EncodedJSValue SOCKET_BAD_PORT(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue name, JSC::JSValue port, bool allowZero);
JSC::EncodedJSValue UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);

View File

@@ -78,6 +78,8 @@
// #include <JavaScriptCore/JSTypedArrayViewPrototype.h>
#include <JavaScriptCore/JSArrayBufferViewInlines.h>
extern "C" bool Bun__Node__ZeroFillBuffers;
using namespace JSC;
using namespace WebCore;
@@ -229,6 +231,7 @@ static JSUint8Array* allocBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_
return uint8Array;
}
static JSUint8Array* allocBufferUnsafe(JSC::JSGlobalObject* lexicalGlobalObject, size_t byteLength)
{
@@ -267,7 +270,7 @@ static int normalizeCompareVal(int val, size_t a_length, size_t b_length)
return val;
}
static inline WebCore::BufferEncodingType parseEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ThrowScope& scope, JSValue arg)
static WebCore::BufferEncodingType parseEncoding(JSC::ThrowScope& scope, JSC::JSGlobalObject* lexicalGlobalObject, JSValue arg, bool validateUnknown)
{
auto arg_ = arg.toStringOrNull(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
@@ -275,6 +278,10 @@ static inline WebCore::BufferEncodingType parseEncoding(JSC::JSGlobalObject* lex
std::optional<BufferEncodingType> encoded = parseEnumeration2(*lexicalGlobalObject, view);
if (UNLIKELY(!encoded)) {
if (validateUnknown) {
Bun::V::validateString(scope, lexicalGlobalObject, arg, "encoding"_s);
RETURN_IF_EXCEPTION(scope, WebCore::BufferEncodingType::utf8);
}
Bun::ERR::UNKNOWN_ENCODING(scope, lexicalGlobalObject, view);
return WebCore::BufferEncodingType::utf8;
}
@@ -282,6 +289,19 @@ static inline WebCore::BufferEncodingType parseEncoding(JSC::JSGlobalObject* lex
return encoded.value();
}
JSC::EncodedJSValue validateOffset(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, JSC::JSValue name, JSC::JSValue min, JSC::JSValue max)
{
if (min.isUndefined()) min = jsDoubleNumber(0);
if (max.isUndefined()) max = jsDoubleNumber(Bun::Buffer::kMaxLength);
return Bun::V::validateInteger(scope, globalObject, value, name, min, max);
}
JSC::EncodedJSValue validateOffset(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, const WTF::String& name, JSC::JSValue min, JSC::JSValue max)
{
if (min.isUndefined()) min = jsDoubleNumber(0);
if (max.isUndefined()) max = jsDoubleNumber(Bun::Buffer::kMaxLength);
return Bun::V::validateInteger(scope, globalObject, value, name, min, max);
}
namespace WebCore {
using namespace JSC;
@@ -456,6 +476,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocUnsafeBody(JS
size_t length = lengthValue.toLength(lexicalGlobalObject);
auto result = allocBufferUnsafe(lexicalGlobalObject, length);
RETURN_IF_EXCEPTION(throwScope, {});
if (Bun__Node__ZeroFillBuffers) memset(result->typedVector(), 0, length);
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
}
@@ -467,7 +488,7 @@ static inline JSC::EncodedJSValue constructBufferEmpty(JSGlobalObject* lexicalGl
static JSC::EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalObject, JSString* str, WebCore::BufferEncodingType encoding)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// Use ->view() here instead of ->value() as that will avoid flattening ropestrings from .slice()
@@ -540,7 +561,7 @@ static JSC::EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalOb
static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSValue arg0, JSValue arg1)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
auto scope = DECLARE_THROW_SCOPE(vm);
@@ -580,6 +601,12 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG
RETURN_IF_EXCEPTION(scope, {});
size_t length = lengthValue.toLength(lexicalGlobalObject);
if (length == 0) {
auto* uint8Array = createEmptyBuffer(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(uint8Array));
}
// fill argument
if (UNLIKELY(callFrame->argumentCount() > 1)) {
auto* uint8Array = createUninitializedBuffer(lexicalGlobalObject, length);
@@ -595,7 +622,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG
if (callFrame->argumentCount() > 2) {
EnsureStillAliveScope arg2 = callFrame->uncheckedArgument(2);
if (!arg2.value().isUndefined()) {
encoding = parseEncoding(lexicalGlobalObject, scope, arg2.value());
encoding = parseEncoding(scope, lexicalGlobalObject, arg2.value(), true);
RETURN_IF_EXCEPTION(scope, {});
}
}
@@ -611,8 +638,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG
ZigString str = Zig::toZigString(view);
if (UNLIKELY(!Bun__Buffer_fill(&str, startPtr, end - start, encoding))) {
throwTypeError(lexicalGlobalObject, scope, "Failed to decode value"_s);
return {};
return Bun::ERR::throwCode(scope, lexicalGlobalObject, Bun::ErrorCode::ERR_INVALID_ARG_VALUE, "Failed to decode value"_s);
}
} else if (auto* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(value)) {
if (UNLIKELY(view->isDetached())) {
@@ -622,8 +648,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG
size_t length = view->byteLength();
if (UNLIKELY(length == 0)) {
throwTypeError(lexicalGlobalObject, scope, "Buffer cannot be empty"_s);
return {};
return Bun::ERR::throwCode(scope, lexicalGlobalObject, Bun::ErrorCode::ERR_INVALID_ARG_VALUE, "Buffer cannot be empty"_s);
}
auto* start = uint8Array->typedVector();
@@ -692,9 +717,10 @@ static inline JSC::EncodedJSValue jsBufferByteLengthFromStringAndEncoding(JSC::J
return {};
}
static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
@@ -732,7 +758,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC
static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto castedThisValue = callFrame->argument(0);
@@ -780,20 +806,15 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J
static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
if (callFrame->argumentCount() < 1) {
return constructBufferEmpty(lexicalGlobalObject);
}
auto arrayValue = callFrame->uncheckedArgument(0);
auto array = JSC::jsDynamicCast<JSC::JSArray*>(arrayValue);
if (!array) {
throwTypeError(lexicalGlobalObject, throwScope, "Argument must be an array"_s);
return {};
}
auto listValue = callFrame->argument(0);
Bun::V::validateArray(throwScope, lexicalGlobalObject, listValue, "list"_s, jsUndefined());
RETURN_IF_EXCEPTION(throwScope, {});
auto array = JSC::jsDynamicCast<JSC::JSArray*>(listValue);
size_t arrayLength = array->length();
if (arrayLength < 1) {
RELEASE_AND_RETURN(throwScope, constructBufferEmpty(lexicalGlobalObject));
@@ -816,22 +837,20 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JS
JSValue element = array->getIndex(lexicalGlobalObject, i);
RETURN_IF_EXCEPTION(throwScope, {});
if (auto* bufferView = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(element)) {
if (UNLIKELY(bufferView->isDetached())) {
throwVMTypeError(lexicalGlobalObject, throwScope, "ArrayBufferView is detached"_s);
return {};
}
auto length = bufferView->byteLength();
if (length > 0)
args.append(element);
byteLength += length;
} else {
throwTypeError(lexicalGlobalObject, throwScope, "Buffer.concat expects Buffer or Uint8Array"_s);
return {};
auto* typedArray = JSC::jsDynamicCast<JSC::JSUint8Array*>(element);
if (!typedArray) {
return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, makeString("list["_s, i, "]"_s), "Buffer or Uint8Array"_s, element);
}
if (UNLIKELY(typedArray->isDetached())) {
return throwVMTypeError(lexicalGlobalObject, throwScope, "ArrayBufferView is detached"_s);
}
auto length = typedArray->byteLength();
if (length > 0)
args.append(element);
byteLength += length;
}
size_t availableLength = byteLength;
@@ -866,7 +885,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JS
size_t remain = byteLength;
auto* head = outBuffer->typedVector();
const int arrayLengthI = arrayLength;
const int arrayLengthI = args.size();
for (int i = 0; i < arrayLengthI && remain > 0; i++) {
auto* bufferView = JSC::jsCast<JSC::JSArrayBufferView*>(args.at(i));
size_t length = std::min(remain, bufferView->byteLength());
@@ -887,7 +906,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JS
// https://github.com/nodejs/node/blob/v22.9.0/lib/buffer.js#L337
static inline JSC::EncodedJSValue jsBufferConstructorFunction_copyBytesFromBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto viewValue = callFrame->argument(0);
@@ -991,7 +1010,7 @@ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSBufferPrototype, JSBufferPrototype::Base);
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_compareBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto arg0 = callFrame->argument(0);
@@ -1103,7 +1122,7 @@ static double toInteger(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObjec
// https://github.com/nodejs/node/blob/v22.9.0/lib/buffer.js#L205
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_copyBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto targetValue = callFrame->argument(0);
@@ -1169,7 +1188,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_copyBody(JSC::JSGlob
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_equalsBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
if (callFrame->argumentCount() < 1) {
throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
@@ -1201,9 +1220,10 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_equalsBody(JSC::JSGl
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsBoolean(normalizeCompareVal(result, a_length, b_length) == 0)));
}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (callFrame->argumentCount() < 1) {
@@ -1242,8 +1262,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob
}
if (!encodingValue.isUndefined() && value.isString()) {
if (!encodingValue.isString()) return Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, "encoding"_s, "string"_s, encodingValue);
encoding = parseEncoding(lexicalGlobalObject, scope, encodingValue);
encoding = parseEncoding(scope, lexicalGlobalObject, encodingValue, true);
RETURN_IF_EXCEPTION(scope, {});
}
@@ -1286,8 +1305,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob
size_t length = view->byteLength();
if (UNLIKELY(length == 0)) {
throwTypeError(lexicalGlobalObject, scope, "Buffer cannot be empty"_s);
return {};
return Bun::ERR::throwCode(scope, lexicalGlobalObject, Bun::ErrorCode::ERR_INVALID_ARG_VALUE, "Buffer cannot be empty"_s);
}
length = std::min(length, remain);
@@ -1339,6 +1357,28 @@ static int64_t indexOf(const uint8_t* thisPtr, int64_t thisLength, const uint8_t
return -1;
}
static int64_t indexOf16(const uint8_t* thisPtr, int64_t thisLength, const uint8_t* valuePtr, int64_t valueLength, int64_t byteOffset)
{
size_t finalresult = 0;
if (thisLength == 1) return -1;
thisLength = thisLength / 2 * 2;
if (valueLength == 1) return -1;
valueLength = valueLength / 2 * 2;
byteOffset = byteOffset / 2 * 2;
while (true) {
auto res = indexOf(thisPtr, thisLength, valuePtr, valueLength, byteOffset);
if (res == -1) return -1;
if (res % 2 == 1) {
thisPtr += res + 1;
thisLength -= res + 1;
finalresult += res + 1;
continue;
}
finalresult += res;
return finalresult;
}
}
static int64_t lastIndexOf(const uint8_t* thisPtr, int64_t thisLength, const uint8_t* valuePtr, int64_t valueLength, int64_t byteOffset)
{
auto start = thisPtr;
@@ -1350,108 +1390,128 @@ static int64_t lastIndexOf(const uint8_t* thisPtr, int64_t thisLength, const uin
return -1;
}
static int64_t indexOf(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis, bool last)
static ssize_t indexOfOffset(size_t length, ssize_t offset_i64, ssize_t needle_length, bool is_forward)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
ssize_t length_i64 = static_cast<ssize_t>(length);
if (offset_i64 < 0) {
if (offset_i64 + length_i64 >= 0) {
// Negative offsets count backwards from the end of the buffer.
return length_i64 + offset_i64;
} else if (is_forward || needle_length == 0) {
// indexOf from before the start of the buffer: search the whole buffer.
return 0;
} else {
// lastIndexOf from before the start of the buffer: no match.
return -1;
}
} else {
if (offset_i64 + needle_length <= length_i64) {
// Valid positive offset.
return offset_i64;
} else if (needle_length == 0) {
// Out of buffer bounds, but empty needle: point to end of buffer.
return length_i64;
} else if (is_forward) {
// indexOf from past the end of the buffer: no match.
return -1;
} else {
// lastIndexOf from past the end of the buffer: search the whole buffer.
return length_i64 - 1;
}
}
}
static int64_t indexOf(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter buffer, bool last)
{
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (callFrame->argumentCount() < 1) {
throwVMError(lexicalGlobalObject, scope, createNotEnoughArgumentsError(lexicalGlobalObject));
bool dir = !last;
const uint8_t* typedVector = buffer->typedVector();
size_t byteLength = buffer->byteLength();
std::optional<BufferEncodingType> encoding = std::nullopt;
if (byteLength == 0) return -1;
auto valueValue = callFrame->argument(0);
auto byteOffsetValue = callFrame->argument(1);
auto encodingValue = callFrame->argument(2);
if (byteOffsetValue.isString()) {
encodingValue = byteOffsetValue;
byteOffsetValue = jsUndefined();
} else {
double byteOffset = byteOffsetValue.toNumber(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, -1);
if (byteOffset > 0x7fffffffp0f) byteOffsetValue = jsDoubleNumber(0x7fffffffp0f);
if (byteOffset < -0x80000000p0f) byteOffsetValue = jsDoubleNumber(-0x80000000p0f);
}
byteOffsetValue = jsDoubleNumber(byteOffsetValue.toNumber(lexicalGlobalObject));
RETURN_IF_EXCEPTION(scope, -1);
if (std::isnan(byteOffsetValue.asNumber())) byteOffsetValue = jsNumber(dir ? 0 : byteLength);
if (valueValue.isNumber()) {
ssize_t byteOffset = indexOfOffset(byteLength, byteOffsetValue.asNumber(), 1, dir);
if (byteOffset == -1) return -1;
uint8_t byteValue = (valueValue.toInt32(lexicalGlobalObject)) % 256;
RETURN_IF_EXCEPTION(scope, -1);
if (last) {
for (int64_t i = byteOffset; i >= 0; --i) {
if (byteValue == typedVector[i]) return i;
}
} else {
const void* offset = memchr(reinterpret_cast<const void*>(typedVector + byteOffset), byteValue, byteLength - byteOffset);
if (offset != NULL) return static_cast<const uint8_t*>(offset) - typedVector;
}
return -1;
}
auto value = callFrame->uncheckedArgument(0);
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
int64_t length = static_cast<int64_t>(castedThis->byteLength());
const uint8_t* typedVector = castedThis->typedVector();
int64_t byteOffset = last ? length - 1 : 0;
if (callFrame->argumentCount() > 1) {
EnsureStillAliveScope arg1 = callFrame->uncheckedArgument(1);
if (arg1.value().isString()) {
encoding = parseEncoding(lexicalGlobalObject, scope, arg1.value());
RETURN_IF_EXCEPTION(scope, -1);
} else {
auto byteOffset_ = arg1.value().toNumber(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, -1);
if (std::isnan(byteOffset_) || std::isinf(byteOffset_)) {
byteOffset = last ? length - 1 : 0;
} else if (byteOffset_ < 0) {
byteOffset = length + static_cast<int64_t>(byteOffset_);
} else {
byteOffset = static_cast<int64_t>(byteOffset_);
}
if (last) {
if (byteOffset < 0) {
return -1;
} else if (byteOffset > length - 1) {
byteOffset = length - 1;
}
} else {
if (byteOffset <= 0) {
byteOffset = 0;
} else if (byteOffset > length - 1) {
return -1;
}
}
if (callFrame->argumentCount() > 2) {
EnsureStillAliveScope encodingValue = callFrame->uncheckedArgument(2);
if (!encodingValue.value().isUndefined()) {
encoding = parseEncoding(lexicalGlobalObject, scope, encodingValue.value());
RETURN_IF_EXCEPTION(scope, -1);
}
}
}
WTF::String encodingString;
if (!encodingValue.isUndefined()) {
encodingString = encodingValue.toWTFString(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
encoding = parseEnumeration2(*lexicalGlobalObject, encodingString);
} else {
encoding = BufferEncodingType::utf8;
}
if (value.isString()) {
auto* str = value.toStringOrNull(lexicalGlobalObject);
if (valueValue.isString()) {
if (!encoding.has_value()) {
return Bun::ERR::UNKNOWN_ENCODING(scope, lexicalGlobalObject, encodingString);
}
auto* str = valueValue.toStringOrNull(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, -1);
JSC::EncodedJSValue encodedBuffer = constructFromEncoding(lexicalGlobalObject, str, encoding);
ssize_t byteOffset = indexOfOffset(byteLength, byteOffsetValue.asNumber(), str->length(), dir);
if (byteOffset == -1) return -1;
if (str->length() == 0) return byteOffset;
JSC::EncodedJSValue encodedBuffer = constructFromEncoding(lexicalGlobalObject, str, encoding.value());
auto* arrayValue = JSC::jsDynamicCast<JSC::JSUint8Array*>(JSC::JSValue::decode(encodedBuffer));
int64_t lengthValue = static_cast<int64_t>(arrayValue->byteLength());
const uint8_t* typedVectorValue = arrayValue->typedVector();
if (last) {
return lastIndexOf(typedVector, length, typedVectorValue, lengthValue, byteOffset);
return lastIndexOf(typedVector, byteLength, typedVectorValue, lengthValue, byteOffset);
} else {
return indexOf(typedVector, length, typedVectorValue, lengthValue, byteOffset);
if (encoding.value() == BufferEncodingType::ucs2) return indexOf16(typedVector, byteLength, typedVectorValue, lengthValue, byteOffset);
return indexOf(typedVector, byteLength, typedVectorValue, lengthValue, byteOffset);
}
} else if (value.isNumber()) {
uint8_t byteValue = static_cast<uint8_t>((value.toInt32(lexicalGlobalObject)) % 256);
RETURN_IF_EXCEPTION(scope, -1);
if (last) {
for (int64_t i = byteOffset; i >= 0; --i) {
if (byteValue == typedVector[i]) {
return i;
}
}
} else {
const void* offset = memchr(reinterpret_cast<const void*>(typedVector + byteOffset), byteValue, length - byteOffset);
if (offset != NULL) {
return static_cast<int64_t>(static_cast<const uint8_t*>(offset) - typedVector);
}
}
return -1;
} else if (auto* arrayValue = JSC::jsDynamicCast<JSC::JSUint8Array*>(value)) {
size_t lengthValue = arrayValue->byteLength();
const uint8_t* typedVectorValue = arrayValue->typedVector();
if (last) {
return lastIndexOf(typedVector, length, typedVectorValue, lengthValue, byteOffset);
} else {
return indexOf(typedVector, length, typedVectorValue, lengthValue, byteOffset);
}
} else {
throwTypeError(lexicalGlobalObject, scope, "Invalid value type"_s);
return -1;
}
if (auto* array = JSC::jsDynamicCast<JSC::JSUint8Array*>(valueValue)) {
if (!encoding.has_value()) encoding = BufferEncodingType::utf8;
size_t lengthValue = array->byteLength();
ssize_t byteOffset = indexOfOffset(byteLength, byteOffsetValue.asNumber(), lengthValue, dir);
if (byteOffset == -1) return -1;
if (lengthValue == 0) return byteOffset;
const uint8_t* typedVectorValue = array->typedVector();
if (last) {
return lastIndexOf(typedVector, byteLength, typedVectorValue, lengthValue, byteOffset);
} else {
if (encoding.value() == BufferEncodingType::ucs2) return indexOf16(typedVector, byteLength, typedVectorValue, lengthValue, byteOffset);
return indexOf(typedVector, byteLength, typedVectorValue, lengthValue, byteOffset);
}
}
Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, "value"_s, "number, string, Buffer, or Uint8Array"_s, valueValue);
return -1;
}
@@ -1460,19 +1520,22 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_includesBody(JSC::JS
auto index = indexOf(lexicalGlobalObject, callFrame, castedThis, false);
return JSC::JSValue::encode(jsBoolean(index != -1));
}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_indexOfBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto index = indexOf(lexicalGlobalObject, callFrame, castedThis, false);
return JSC::JSValue::encode(jsNumber(index));
}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_lastIndexOfBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto index = indexOf(lexicalGlobalObject, callFrame, castedThis, true);
return JSC::JSValue::encode(jsNumber(index));
}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap16Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
constexpr int elemSize = 2;
@@ -1499,9 +1562,10 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap16Body(JSC::JSGl
return JSC::JSValue::encode(castedThis);
}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap32Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
constexpr int elemSize = 4;
@@ -1533,9 +1597,10 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap32Body(JSC::JSGl
return JSC::JSValue::encode(castedThis);
}
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
constexpr size_t elemSize = 8;
@@ -1662,7 +1727,7 @@ bool inline parseArrayIndex(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalO
// using byteLength and byte offsets here is intentional
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
uint32_t start = 0;
uint32_t end = castedThis->byteLength();
@@ -1682,7 +1747,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
return jsBufferToString(vm, lexicalGlobalObject, castedThis, start, end, encoding);
if (!arg1.isUndefined()) {
encoding = parseEncoding(lexicalGlobalObject, scope, arg1);
encoding = parseEncoding(scope, lexicalGlobalObject, arg1, false);
RETURN_IF_EXCEPTION(scope, {});
}
@@ -1716,7 +1781,7 @@ lstart:
template<BufferEncodingType encoding>
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_SliceWithEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* castedThis = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(callFrame->thisValue());
const JSValue startValue = callFrame->argument(0);
@@ -1808,7 +1873,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeEncodingBody(JS
template<BufferEncodingType encoding>
static inline JSC::EncodedJSValue jsBufferPrototypeFunctionWriteWithEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* castedThis = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(callFrame->thisValue());
@@ -1833,93 +1898,64 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunctionWriteWithEncoding(JSC
static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis)
{
auto& vm = JSC::getVM(lexicalGlobalObject);
uint32_t offset = 0;
uint32_t length = castedThis->byteLength();
uint32_t max = length;
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (UNLIKELY(callFrame->argumentCount() == 0)) {
throwTypeError(lexicalGlobalObject, scope, "Not enough arguments"_s);
return {};
}
EnsureStillAliveScope arg0 = callFrame->argument(0);
auto* str = arg0.value().toStringOrNull(lexicalGlobalObject);
if (!str) {
throwTypeError(lexicalGlobalObject, scope, "write() expects a string"_s);
return {};
}
JSValue offsetValue = jsUndefined();
JSValue lengthValue = jsUndefined();
JSValue encodingValue = jsUndefined();
switch (callFrame->argumentCount()) {
case 4:
encodingValue = callFrame->uncheckedArgument(3);
FALLTHROUGH;
case 3:
lengthValue = callFrame->uncheckedArgument(2);
FALLTHROUGH;
case 2:
offsetValue = callFrame->uncheckedArgument(1);
break;
default:
break;
}
auto setEncoding = [&]() {
if (!encodingValue.isUndefined()) {
encoding = parseEncoding(lexicalGlobalObject, scope, encodingValue);
}
};
auto stringValue = callFrame->argument(0);
auto offsetValue = callFrame->argument(1);
auto lengthValue = callFrame->argument(2);
auto encodingValue = callFrame->argument(3);
if (offsetValue.isUndefined()) {
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L1053
RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
Bun::V::validateString(scope, lexicalGlobalObject, stringValue, "string"_s);
RETURN_IF_EXCEPTION(scope, {});
auto* str = stringValue.toString(lexicalGlobalObject);
uint32_t offset = 0;
uint32_t length = castedThis->byteLength();
RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, WebCore::BufferEncodingType::utf8));
}
if (lengthValue.isUndefined() && offsetValue.isString()) {
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L1056
encodingValue = offsetValue;
setEncoding();
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
}
if (UNLIKELY(!offsetValue.isNumber())) {
throwTypeError(lexicalGlobalObject, scope, "Invalid offset"_s);
return {};
}
int32_t userOffset = offsetValue.toInt32(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
if (userOffset < 0 || userOffset > max) {
throwNodeRangeError(lexicalGlobalObject, scope, "Offset is out of bounds"_s);
return {};
}
offset = static_cast<uint32_t>(userOffset);
uint32_t remaining = max - static_cast<uint32_t>(userOffset);
// https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L1062-L1077
if (lengthValue.isUndefined()) {
length = remaining;
} else if (lengthValue.isString()) {
encodingValue = lengthValue;
setEncoding();
RETURN_IF_EXCEPTION(scope, {});
length = remaining;
lengthValue = jsNumber(castedThis->byteLength());
offsetValue = jsNumber(0);
} else {
setEncoding();
int32_t userLength = lengthValue.toInt32(lexicalGlobalObject);
uint32_t length = castedThis->byteLength();
validateOffset(scope, lexicalGlobalObject, offsetValue, "offset"_s, jsNumber(0), jsNumber(length));
RETURN_IF_EXCEPTION(scope, {});
length = std::min(static_cast<uint32_t>(userLength), remaining);
uint32_t offset = offsetValue.toUInt32(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
uint32_t remaining = castedThis->byteLength() - offset;
if (lengthValue.isUndefined()) {
lengthValue = jsNumber(remaining);
} else if (lengthValue.isString()) {
encodingValue = lengthValue;
lengthValue = jsNumber(remaining);
} else {
validateOffset(scope, lexicalGlobalObject, lengthValue, "length"_s, jsNumber(0), jsNumber(length));
RETURN_IF_EXCEPTION(scope, {});
length = lengthValue.toUInt32(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
if (length > remaining) lengthValue = jsNumber(remaining);
}
}
Bun::V::validateString(scope, lexicalGlobalObject, stringValue, "string"_s);
RETURN_IF_EXCEPTION(scope, {});
auto* str = stringValue.toString(lexicalGlobalObject);
uint32_t offset = offsetValue.toUInt32(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
uint32_t length = lengthValue.toUInt32(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, {});
if (!encodingValue.toBoolean(lexicalGlobalObject)) {
RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, WebCore::BufferEncodingType::utf8));
}
auto encoding = parseEncoding(scope, lexicalGlobalObject, encodingValue, false);
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
}
@@ -2249,7 +2285,6 @@ static const HashTableValue JSBufferPrototypeTableValues[]
{ "swap32"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap32, 0 } },
{ "swap64"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap64, 0 } },
{ "toJSON"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeToJSONCodeGenerator, 1 } },
{ "toLocaleString"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } },
{ "toString"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } },
{ "ucs2Slice"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf16leSlice, 2 } },
{ "ucs2Write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_utf16leWrite, 3 } },
@@ -2301,6 +2336,8 @@ void JSBufferPrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalThis)
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
reifyStaticProperties(vm, JSBuffer::info(), JSBufferPrototypeTableValues, *this);
ALIAS("toLocaleString", "toString");
ALIAS("readUintBE", "readUIntBE");
ALIAS("readUintLE", "readUIntLE");
ALIAS("readUint8", "readUInt8");
@@ -2378,6 +2415,128 @@ JSC::JSObject* createBufferConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalO
} // namespace WebCore
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_Array, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto arrayValue = callFrame->argument(0);
return constructBufferFromArray(throwScope, lexicalGlobalObject, arrayValue);
}
EncodedJSValue constructBufferFromArray(JSC::ThrowScope& throwScope, JSGlobalObject* lexicalGlobalObject, JSValue arrayValue)
{
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
auto* constructor = lexicalGlobalObject->m_typedArrayUint8.constructor(lexicalGlobalObject);
MarkedArgumentBuffer argsBuffer;
argsBuffer.append(arrayValue);
JSValue target = globalObject->JSBufferConstructor();
// TODO: I wish we could avoid this - it adds ~30ns of overhead just using JSC::construct.
auto* object = JSC::construct(lexicalGlobalObject, constructor, target, argsBuffer, "Buffer failed to construct"_s);
RETURN_IF_EXCEPTION(throwScope, {});
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(object));
}
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_ArrayBuffer, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto argsCount = callFrame->argumentCount();
auto arrayBufferValue = callFrame->argument(0);
auto offsetValue = callFrame->argument(1);
auto lengthValue = callFrame->argument(2);
return constructBufferFromArrayBuffer(throwScope, lexicalGlobalObject, argsCount, arrayBufferValue, offsetValue, lengthValue);
}
EncodedJSValue constructBufferFromArrayBuffer(JSC::ThrowScope& throwScope, JSGlobalObject* lexicalGlobalObject, size_t argsCount, JSValue arrayBufferValue, JSValue offsetValue, JSValue lengthValue)
{
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
auto* jsBuffer = jsCast<JSC::JSArrayBuffer*>(arrayBufferValue.asCell());
RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
if (buffer->isDetached()) {
// TOOD: return Node.js error
throwTypeError(globalObject, throwScope, "Buffer is detached"_s);
return {};
}
size_t byteLength = buffer->byteLength();
double byteLengthD = byteLength;
// This closely matches `new Uint8Array(buffer, byteOffset, length)` in JavaScriptCore's implementation.
// See Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h
size_t offset = 0;
std::optional<size_t> length;
size_t maxLength = byteLength;
if (argsCount > 1) {
if (!offsetValue.isUndefined()) {
offsetValue = jsDoubleNumber(offsetValue.toNumber(lexicalGlobalObject));
RETURN_IF_EXCEPTION(throwScope, {});
if (std::isnan(offsetValue.asNumber())) offsetValue = jsNumber(0);
if ((byteLengthD - offsetValue.asNumber()) < 0) return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, lexicalGlobalObject, "offset"_s);
maxLength = byteLengthD - offsetValue.asNumber();
offset = offsetValue.asNumber();
}
if (!lengthValue.isUndefined()) {
lengthValue = jsDoubleNumber(lengthValue.toNumber(lexicalGlobalObject));
RETURN_IF_EXCEPTION(throwScope, {});
if (std::isnan(lengthValue.asNumber())) lengthValue = jsNumber(0);
length = lengthValue.asNumber();
if (length > 0) {
if (length > maxLength) {
return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, lexicalGlobalObject, "length"_s);
}
} else {
length = 0;
}
}
}
if (offset > byteLength) {
return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, globalObject, "offset"_s);
}
if (!length) {
if (buffer->isResizableOrGrowableShared()) {
if (UNLIKELY(offset > byteLength)) {
return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, globalObject, "byteOffset"_s);
}
} else {
length = (byteLength - offset);
}
}
if (offset + length.value() > byteLength) {
return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, globalObject, "length"_s);
}
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTFMove(buffer), offset, length);
if (UNLIKELY(!uint8Array)) {
throwOutOfMemoryError(globalObject, throwScope);
return JSC::JSValue::encode({});
}
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
}
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_Size, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto lengthValue = callFrame->argument(0);
Bun::V::validateNumber(throwScope, lexicalGlobalObject, lengthValue, "size"_s, jsNumber(0), jsNumber(Bun::Buffer::kMaxLength));
RETURN_IF_EXCEPTION(throwScope, {});
size_t length = lengthValue.toLength(lexicalGlobalObject);
return JSBuffer__bufferFromLength(lexicalGlobalObject, length);
}
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_String, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto stringValue = callFrame->argument(0);
auto encodingValue = callFrame->argument(1);
return constructBufferFromStringAndEncoding(lexicalGlobalObject, stringValue, encodingValue);
}
static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexicalGlobalObject, JSValue newTarget, ArgList args)
{
VM& vm = lexicalGlobalObject->vm();
@@ -2474,56 +2633,7 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
case ArrayBufferType: {
// This closely matches `new Uint8Array(buffer, byteOffset, length)` in JavaScriptCore's implementation.
// See Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h
size_t offset = 0;
std::optional<size_t> length;
if (argsCount > 1) {
offset = args.at(1).toTypedArrayIndex(globalObject, "byteOffset"_s);
// TOOD: return Node.js error
RETURN_IF_EXCEPTION(throwScope, {});
if (argsCount > 2) {
// If the length value is present but undefined, treat it as missing.
JSValue lengthValue = args.at(2);
if (!lengthValue.isUndefined()) {
length = lengthValue.toTypedArrayIndex(globalObject, "length"_s);
// TOOD: return Node.js error
RETURN_IF_EXCEPTION(throwScope, {});
}
}
}
auto* jsBuffer = jsCast<JSC::JSArrayBuffer*>(distinguishingArg.asCell());
RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
if (buffer->isDetached()) {
// TOOD: return Node.js error
throwTypeError(globalObject, throwScope, "Buffer is detached"_s);
return {};
}
if (!length) {
size_t byteLength = buffer->byteLength();
if (buffer->isResizableOrGrowableShared()) {
if (UNLIKELY(offset > byteLength)) {
// TOOD: return Node.js error
throwNodeRangeError(globalObject, throwScope, "byteOffset exceeds source ArrayBuffer byteLength"_s);
return {};
}
} else {
length = (byteLength - offset);
}
}
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTFMove(buffer), offset, length);
if (UNLIKELY(!uint8Array)) {
throwOutOfMemoryError(globalObject, throwScope);
return JSC::JSValue::encode({});
}
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
return constructBufferFromArrayBuffer(throwScope, lexicalGlobalObject, args.size(), distinguishingArg, args.at(1), args.at(2));
}
default: {
break;
@@ -2531,24 +2641,7 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
}
}
JSC::JSObject* constructor = lexicalGlobalObject->m_typedArrayUint8.constructor(lexicalGlobalObject);
MarkedArgumentBuffer argsBuffer;
argsBuffer.append(distinguishingArg);
for (size_t i = 1; i < argsCount; ++i)
argsBuffer.append(args.at(i));
JSValue target = newTarget;
if (!target || !target.isCell()) {
target = globalObject->JSBufferConstructor();
}
JSC::JSObject* object = JSC::construct(lexicalGlobalObject, constructor, target, args, "Buffer failed to construct"_s);
if (!object) {
return JSC::JSValue::encode({});
}
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(object));
return constructBufferFromArray(throwScope, lexicalGlobalObject, distinguishingArg);
}
JSC_DEFINE_HOST_FUNCTION(callJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))

View File

@@ -67,3 +67,11 @@ JSC::Structure* createBufferStructure(JSC::VM&, JSC::JSGlobalObject*, JSC::JSVal
JSC::JSObject* createBufferConstructor(JSC::VM&, JSC::JSGlobalObject*, JSC::JSObject* bufferPrototype);
}
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_Array, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame));
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_ArrayBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame));
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_Size, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame));
JSC_DEFINE_HOST_FUNCTION(jsFunction_BufferFrom_String, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame));
JSC::EncodedJSValue constructBufferFromArrayBuffer(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* lexicalGlobalObject, size_t argsCount, JSC::JSValue arrayBufferValue, JSC::JSValue offsetValue, JSC::JSValue lengthValue);
JSC::EncodedJSValue constructBufferFromArray(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue arrayValue);

View File

@@ -100,7 +100,27 @@ JSC::EncodedJSValue V::validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObjec
if (max_isnonnull) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, max_num, Bun::UPPER, value);
return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, ""_s, value);
}
return JSValue::encode(jsUndefined());
}
JSC::EncodedJSValue V::validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, const WTF::String& name, JSValue min, JSValue max)
{
if (!value.isNumber()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "number"_s, value);
auto value_num = value.asNumber();
auto min_num = min.toNumber(globalObject);
RETURN_IF_EXCEPTION(scope, {});
auto max_num = max.toNumber(globalObject);
RETURN_IF_EXCEPTION(scope, {});
auto min_isnonnull = !min.isUndefinedOrNull();
auto max_isnonnull = !max.isUndefinedOrNull();
if ((min_isnonnull && value_num < min_num) || (max_isnonnull && value_num > max_num) || ((min_isnonnull || max_isnonnull) && std::isnan(value_num))) {
if (min_isnonnull && max_isnonnull) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, min_num, max_num, value);
if (min_isnonnull) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, min_num, Bun::LOWER, value);
if (max_isnonnull) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, max_num, Bun::UPPER, value);
return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, ""_s, value);
}
return JSValue::encode(jsUndefined());
}
JSC::EncodedJSValue V::validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, ASCIILiteral name, JSValue min, JSValue max)
@@ -533,6 +553,12 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBuffer, (JSC::JSGlobalObject * globa
auto buffer = callFrame->argument(0);
auto name = callFrame->argument(1);
return V::validateBuffer(scope, globalObject, buffer, name);
}
JSC::EncodedJSValue V::validateBuffer(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue buffer, JSValue nameValue)
{
auto name = nameValue.isUndefined() ? "buffer"_s : nameValue.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
if (!buffer.isCell()) return JSValue::encode(jsUndefined());
auto ty = buffer.asCell()->type();

View File

@@ -26,6 +26,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBuffer, (JSC::JSGlobalObject * globa
namespace V {
JSC::EncodedJSValue validateInteger(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, const WTF::String& name, JSC::JSValue min, JSC::JSValue max);
JSC::EncodedJSValue validateInteger(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, JSC::JSValue name, JSC::JSValue min, JSC::JSValue max);
JSC::EncodedJSValue validateInteger(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, ASCIILiteral name, JSC::JSValue min, JSC::JSValue max);
JSC::EncodedJSValue validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, JSC::JSValue name, JSC::JSValue min, JSC::JSValue max);
@@ -34,6 +35,7 @@ JSC::EncodedJSValue validateFiniteNumber(JSC::ThrowScope& scope, JSC::JSGlobalOb
JSC::EncodedJSValue validateString(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name);
JSC::EncodedJSValue validateString(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, ASCIILiteral name);
JSC::EncodedJSValue validateArray(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name, JSValue minLength);
JSC::EncodedJSValue validateBuffer(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue buffer, JSValue name);
JSC::EncodedJSValue validateArray(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, ASCIILiteral name, JSValue minLength);
JSC::EncodedJSValue validateUint32(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name, JSValue positive);
JSC::EncodedJSValue validateUint32(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, ASCIILiteral name, JSValue positive);

View File

@@ -687,7 +687,7 @@ pub const ZigString = extern struct {
return (@intFromPtr(this._unsafe_ptr_do_not_use) & (1 << 63)) != 0;
}
pub inline fn utf16Slice(this: *const ZigString) []align(1) const u16 {
pub fn utf16Slice(this: *const ZigString) []align(1) const u16 {
if (comptime bun.Environment.allow_assert) {
if (this.len > 0 and !this.is16Bit()) {
@panic("ZigString.utf16Slice() called on a latin1 string.\nPlease use .toSlice() instead or carefully check that .is16Bit() is false first.");
@@ -697,7 +697,7 @@ pub const ZigString = extern struct {
return @as([*]align(1) const u16, @ptrCast(untagged(this._unsafe_ptr_do_not_use)))[0..this.len];
}
pub inline fn utf16SliceAligned(this: *const ZigString) []const u16 {
pub fn utf16SliceAligned(this: *const ZigString) []const u16 {
if (comptime bun.Environment.allow_assert) {
if (this.len > 0 and !this.is16Bit()) {
@panic("ZigString.utf16SliceAligned() called on a latin1 string.\nPlease use .toSlice() instead or carefully check that .is16Bit() is false first.");

View File

@@ -49,6 +49,8 @@ pub const BufferVectorized = struct {
Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .hex),
} catch return false;
if (written == 0 and str.length() > 0) return false;
switch (written) {
0 => return true,
1 => {

View File

@@ -1248,7 +1248,7 @@ pub const Encoder = struct {
},
.hex => {
return strings.decodeHexToBytes(to_ptr[0..to_len], u8, input[0..len]);
return strings.decodeHexToBytesTruncate(to_ptr[0..to_len], u8, input[0..len]);
},
.base64, .base64url => {
@@ -1327,7 +1327,7 @@ pub const Encoder = struct {
},
.hex => {
return strings.decodeHexToBytes(to[0..to_len], u16, input[0..len]);
return strings.decodeHexToBytesTruncate(to[0..to_len], u16, input[0..len]);
},
.base64, .base64url => {

View File

@@ -47,6 +47,7 @@ pub var start_time: i128 = undefined;
const Bunfig = @import("./bunfig.zig").Bunfig;
const OOM = bun.OOM;
export var Bun__Node__ZeroFillBuffers = false;
export var Bun__Node__ProcessNoDeprecation = false;
export var Bun__Node__ProcessThrowDeprecation = false;
@@ -236,6 +237,8 @@ pub const Arguments = struct {
clap.parseParam("--conditions <STR>... Pass custom conditions to resolve") catch unreachable,
clap.parseParam("--fetch-preconnect <STR>... Preconnect to a URL while code is loading") catch unreachable,
clap.parseParam("--max-http-header-size <INT> Set the maximum size of HTTP headers in bytes. Default is 16KiB") catch unreachable,
clap.parseParam("--expose-internals Expose internals used for testing Bun itself. Usage of these APIs are completely unsupported.") catch unreachable,
clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable,
clap.parseParam("--dns-result-order <STR> Set the default order of DNS lookup results. Valid orders: verbatim (default), ipv4first, ipv6first") catch unreachable,
clap.parseParam("--expose-gc Expose gc() on the global object. Has no effect on Bun.gc().") catch unreachable,
clap.parseParam("--no-deprecation Suppress all reporting of the custom deprecation.") catch unreachable,
@@ -813,6 +816,9 @@ pub const Arguments = struct {
if (args.option("--title")) |title| {
Bun__Node__ProcessTitle = title;
}
if (args.flag("--zero-fill-buffers")) {
Bun__Node__ZeroFillBuffers = true;
}
}
if (opts.port != null and opts.origin == null) {

View File

@@ -582,7 +582,11 @@ interface String {
}
declare var $Buffer: {
new (a: any, b?: any, c?: any): Buffer;
new (array: Array): Buffer;
new (arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;
new (buffer: Buffer): Buffer;
new (size: number): Buffer;
new (string: string, encoding?: BufferEncoding): Buffer;
};
declare interface Error {

View File

@@ -1,78 +1,37 @@
// This is marked as a constructor because Node.js allows `new Buffer.from`,
// Some legacy dependencies depend on this, see #3638
$constructor;
export function from(items) {
if ($isUndefinedOrNull(items)) {
throw new TypeError(
"The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object.",
);
}
export function from(value, encodingOrOffset, length) {
const { fromString, fromArrayBuffer, fromObject } = require("internal/buffer");
const { isAnyArrayBuffer } = require("node:util/types");
// TODO: figure out why private symbol not found
if (
typeof items === "string" ||
(typeof items === "object" &&
($isTypedArrayView(items) ||
items instanceof ArrayBuffer ||
items instanceof SharedArrayBuffer ||
items instanceof String))
) {
switch ($argumentCount()) {
case 1: {
return new $Buffer(items);
}
case 2: {
return new $Buffer(items, $argument(1));
}
default: {
return new $Buffer(items, $argument(1), $argument(2));
}
if (typeof value === "string") return fromString(value, encodingOrOffset);
if (typeof value === "object" && value !== null) {
if (isAnyArrayBuffer(value)) return fromArrayBuffer(value, encodingOrOffset, length);
const valueOf = value.valueOf && value.valueOf();
if (valueOf != null && valueOf !== value && (typeof valueOf === "string" || typeof valueOf === "object")) {
return Buffer.from(valueOf, encodingOrOffset, length);
}
}
if (typeof items === "object") {
const data = items.data;
if (items.type === "Buffer" && Array.isArray(data)) {
return new $Buffer(data);
}
}
var arrayLike = $toObject(
items,
"The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.",
) as ArrayLike<any>;
if (!$isJSArray(arrayLike)) {
const toPrimitive = $tryGetByIdWithWellKnownSymbol(items, "toPrimitive");
if (toPrimitive) {
const primitive = toPrimitive.$call(items, "string");
const b = fromObject(value);
if (b) return b;
const toPrimitive = $tryGetByIdWithWellKnownSymbol(value, "toPrimitive");
if (typeof toPrimitive === "function") {
const primitive = toPrimitive.$call(value, "string");
if (typeof primitive === "string") {
switch ($argumentCount()) {
case 1: {
return new $Buffer(primitive);
}
case 2: {
return new $Buffer(primitive, $argument(1));
}
default: {
return new $Buffer(primitive, $argument(1), $argument(2));
}
}
return fromString(primitive, encodingOrOffset);
}
}
if (!("length" in arrayLike) || $isCallable(arrayLike)) {
throw new TypeError(
"The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.",
);
}
}
// Don't pass the second argument because Node's Buffer.from doesn't accept
// a function and Uint8Array.from requires it if it exists
// That means we cannot use $tailCallFowrardArguments here, sadly
return new $Buffer(Uint8Array.from(arrayLike).buffer);
throw $ERR_INVALID_ARG_TYPE(
"first argument",
["string", "Buffer", "ArrayBuffer", "Array", "Array-like Object"],
value,
);
}
export function isBuffer(bufferlike) {

View File

@@ -8,46 +8,93 @@ interface BufferExt extends Buffer {
toString(offset: number, length: number, encoding?: BufferEncoding): string;
}
export function setBigUint64(this: BufferExt, offset, value, le) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigUint64(
offset,
value,
le,
);
}
export function readInt8(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt8(offset);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 1);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getInt8(offset);
}
export function readUInt8(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint8(offset);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 1);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getUint8(offset);
}
export function readInt16LE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt16(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 2);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getInt16(offset, true);
}
export function readInt16BE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt16(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 2);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getInt16(offset, false);
}
export function readUInt16LE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint16(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 2);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getUint16(offset, true);
}
export function readUInt16BE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint16(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 2);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getUint16(offset, false);
}
export function readInt32LE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt32(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 4);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getInt32(offset, true);
}
export function readInt32BE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getInt32(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 4);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getInt32(offset, false);
}
export function readUInt32LE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint32(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 4);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getUint32(offset, true);
}
export function readUInt32BE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint32(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 4);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getUint32(offset, false);
}
export function readIntLE(this: BufferExt, offset, byteLength) {
const { ERR_INVALID_ARG_TYPE, validateInteger, boundsError } = require("internal/buffer");
if (offset === undefined) throw ERR_INVALID_ARG_TYPE("offset", "number", offset);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
switch (byteLength) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
validateInteger(offset, "offset");
if (!(offset >= 0 && offset <= this.length - byteLength)) boundsError(offset, this.length - byteLength);
break;
}
switch (byteLength) {
case 1: {
return view.getInt8(offset);
@@ -71,10 +118,26 @@ export function readIntLE(this: BufferExt, offset, byteLength) {
return (last | ((last & (2 ** 15)) * 0x1fffe)) * 2 ** 32 + view.getUint32(offset, true);
}
}
throw new RangeError("byteLength must be >= 1 and <= 6");
boundsError(byteLength, 6, "byteLength");
}
export function readIntBE(this: BufferExt, offset, byteLength) {
const { ERR_INVALID_ARG_TYPE, validateInteger, boundsError } = require("internal/buffer");
if (offset === undefined) throw ERR_INVALID_ARG_TYPE("offset", "number", offset);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
switch (byteLength) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
validateInteger(offset, "offset");
if (!(offset >= 0 && offset <= this.length - byteLength)) boundsError(offset, this.length - byteLength);
break;
}
switch (byteLength) {
case 1: {
return view.getInt8(offset);
@@ -98,10 +161,26 @@ export function readIntBE(this: BufferExt, offset, byteLength) {
return (last | ((last & (2 ** 15)) * 0x1fffe)) * 2 ** 32 + view.getUint32(offset + 2, false);
}
}
throw new RangeError("byteLength must be >= 1 and <= 6");
boundsError(byteLength, 6, "byteLength");
}
export function readUIntLE(this: BufferExt, offset, byteLength) {
const { ERR_INVALID_ARG_TYPE, validateInteger, boundsError } = require("internal/buffer");
if (offset === undefined) throw ERR_INVALID_ARG_TYPE("offset", "number", offset);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
switch (byteLength) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
validateInteger(offset, "offset");
if (!(offset >= 0 && offset <= this.length - byteLength)) boundsError(offset, this.length - byteLength);
break;
}
switch (byteLength) {
case 1: {
return view.getUint8(offset);
@@ -122,10 +201,26 @@ export function readUIntLE(this: BufferExt, offset, byteLength) {
return view.getUint16(offset + 4, true) * 2 ** 32 + view.getUint32(offset, true);
}
}
throw new RangeError("byteLength must be >= 1 and <= 6");
boundsError(byteLength, 6, "byteLength");
}
export function readUIntBE(this: BufferExt, offset, byteLength) {
const { ERR_INVALID_ARG_TYPE, validateInteger, boundsError } = require("internal/buffer");
if (offset === undefined) throw ERR_INVALID_ARG_TYPE("offset", "number", offset);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
switch (byteLength) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
validateInteger(offset, "offset");
if (!(offset >= 0 && offset <= this.length - byteLength)) boundsError(offset, this.length - byteLength);
break;
}
switch (byteLength) {
case 1: {
return view.getUint8(offset);
@@ -146,331 +241,404 @@ export function readUIntBE(this: BufferExt, offset, byteLength) {
return view.getUint16(offset, false) * 2 ** 32 + view.getUint32(offset + 2, false);
}
}
throw new RangeError("byteLength must be >= 1 and <= 6");
boundsError(byteLength, 6, "byteLength");
}
export function readFloatLE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat32(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 4);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getFloat32(offset, true);
}
export function readFloatBE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat32(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 4);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getFloat32(offset, false);
}
export function readDoubleLE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat64(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 8);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getFloat64(offset, true);
}
export function readDoubleBE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat64(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 8);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getFloat64(offset, false);
}
export function readBigInt64LE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigInt64(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 8);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getBigInt64(offset, true);
}
export function readBigInt64BE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigInt64(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 8);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getBigInt64(offset, false);
}
export function readBigUInt64LE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigUint64(offset, true);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 8);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getBigUint64(offset, true);
}
export function readBigUInt64BE(this: BufferExt, offset) {
return (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigUint64(offset, false);
if (offset === undefined) offset = 0;
require("internal/buffer").check_read(this, offset, 8);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
return view.getBigUint64(offset, false);
}
export function writeInt8(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt8(
offset === undefined ? (offset = 0) : offset,
value,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int8(this, value, offset, -0x80, 0x7f);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setInt8(offset, value);
return offset + 1;
}
export function writeUInt8(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint8(
offset === undefined ? (offset = 0) : offset,
value,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int8(this, value, offset, 0, 0xff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setUint8(offset, value);
return offset + 1;
}
export function writeInt16LE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt16(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int16(this, value, offset, -0x8000, 0x7fff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setInt16(offset, value, true);
return offset + 2;
}
export function writeInt16BE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt16(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int16(this, value, offset, -0x8000, 0x7fff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setInt16(offset, value, false);
return offset + 2;
}
export function writeUInt16LE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint16(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int16(this, value, offset, 0, 0xffff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setUint16(offset, value, true);
return offset + 2;
}
export function writeUInt16BE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint16(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int16(this, value, offset, 0, 0xffff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setUint16(offset, value, false);
return offset + 2;
}
export function writeInt32LE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt32(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int32(this, value, offset, -0x80000000, 0x7fffffff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setInt32(offset, value, true);
return offset + 4;
}
export function writeInt32BE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt32(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int32(this, value, offset, -0x80000000, 0x7fffffff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setInt32(offset, value, false);
return offset + 4;
}
export function writeUInt32LE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint32(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int32(this, value, offset, 0, 0xffffffff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setUint32(offset, value, true);
return offset + 4;
}
export function writeUInt32BE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setUint32(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").check_int32(this, value, offset, 0, 0xffffffff);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setUint32(offset, value, false);
return offset + 4;
}
export function writeIntLE(this: BufferExt, value, offset, byteLength) {
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
value = +value;
switch (byteLength) {
case 1: {
require("internal/buffer").check_int8(this, value, offset, -0x80, 0x7f);
view.setInt8(offset, value);
break;
}
case 2: {
require("internal/buffer").check_int16(this, value, offset, -0x8000, 0x7fff);
view.setInt16(offset, value, true);
break;
}
case 3: {
require("internal/buffer").check_int24(this, value, offset, -0x800000, 0x7fffff);
view.setUint16(offset, value & 0xffff, true);
view.setInt8(offset + 2, Math.floor(value * 2 ** -16));
break;
}
case 4: {
require("internal/buffer").check_int32(this, value, offset, -0x80000000, 0x7fffffff);
view.setInt32(offset, value, true);
break;
}
case 5: {
require("internal/buffer").check_int40(this, value, offset, -0x8000000000, 0x7fffffffff);
view.setUint32(offset, value | 0, true);
view.setInt8(offset + 4, Math.floor(value * 2 ** -32));
break;
}
case 6: {
require("internal/buffer").check_int48(this, value, offset, -0x800000000000, 0x7fffffffffff);
view.setUint32(offset, value | 0, true);
view.setInt16(offset + 4, Math.floor(value * 2 ** -32), true);
break;
}
default: {
throw new RangeError("byteLength must be >= 1 and <= 6");
require("internal/buffer").boundsError(byteLength, 6, "byteLength");
}
}
return offset + byteLength;
}
export function writeIntBE(this: BufferExt, value, offset, byteLength) {
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
value = +value;
switch (byteLength) {
case 1: {
require("internal/buffer").check_int8(this, value, offset, -0x80, 0x7f);
view.setInt8(offset, value);
break;
}
case 2: {
require("internal/buffer").check_int16(this, value, offset, -0x8000, 0x7fff);
view.setInt16(offset, value, false);
break;
}
case 3: {
require("internal/buffer").check_int24(this, value, offset, -0x800000, 0x7fffff);
view.setUint16(offset + 1, value & 0xffff, false);
view.setInt8(offset, Math.floor(value * 2 ** -16));
break;
}
case 4: {
require("internal/buffer").check_int32(this, value, offset, -0x80000000, 0x7fffffff);
view.setInt32(offset, value, false);
break;
}
case 5: {
require("internal/buffer").check_int40(this, value, offset, -0x8000000000, 0x7fffffffff);
view.setUint32(offset + 1, value | 0, false);
view.setInt8(offset, Math.floor(value * 2 ** -32));
break;
}
case 6: {
require("internal/buffer").check_int48(this, value, offset, -0x800000000000, 0x7fffffffffff);
view.setUint32(offset + 2, value | 0, false);
view.setInt16(offset, Math.floor(value * 2 ** -32), false);
break;
}
default: {
throw new RangeError("byteLength must be >= 1 and <= 6");
require("internal/buffer").boundsError(byteLength, 6, "byteLength");
}
}
return offset + byteLength;
}
export function writeUIntLE(this: BufferExt, value, offset, byteLength) {
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
value = +value;
switch (byteLength) {
case 1: {
require("internal/buffer").check_int8(this, value, offset, 0, 0xff);
view.setUint8(offset, value);
break;
}
case 2: {
require("internal/buffer").check_int16(this, value, offset, 0, 0xffff);
view.setUint16(offset, value, true);
break;
}
case 3: {
require("internal/buffer").check_int24(this, value, offset, 0, 0xffffff);
view.setUint16(offset, value & 0xffff, true);
view.setUint8(offset + 2, Math.floor(value * 2 ** -16));
break;
}
case 4: {
require("internal/buffer").check_int32(this, value, offset, 0, 0xffffffff);
view.setUint32(offset, value, true);
break;
}
case 5: {
require("internal/buffer").check_int40(this, value, offset, 0, 0xffffffffff);
view.setUint32(offset, value | 0, true);
view.setUint8(offset + 4, Math.floor(value * 2 ** -32));
break;
}
case 6: {
require("internal/buffer").check_int48(this, value, offset, 0, 0xffffffffffff);
view.setUint32(offset, value | 0, true);
view.setUint16(offset + 4, Math.floor(value * 2 ** -32), true);
break;
}
default: {
throw new RangeError("byteLength must be >= 1 and <= 6");
require("internal/buffer").boundsError(byteLength, 6, "byteLength");
}
}
return offset + byteLength;
}
export function writeUIntBE(this: BufferExt, value, offset, byteLength) {
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
value = +value;
switch (byteLength) {
case 1: {
require("internal/buffer").check_int8(this, value, offset, 0, 0xff);
view.setUint8(offset, value);
break;
}
case 2: {
require("internal/buffer").check_int16(this, value, offset, 0, 0xffff);
view.setUint16(offset, value, false);
break;
}
case 3: {
require("internal/buffer").check_int24(this, value, offset, 0, 0xffffff);
view.setUint16(offset + 1, value & 0xffff, false);
view.setUint8(offset, Math.floor(value * 2 ** -16));
break;
}
case 4: {
require("internal/buffer").check_int32(this, value, offset, 0, 0xffffffff);
view.setUint32(offset, value, false);
break;
}
case 5: {
require("internal/buffer").check_int40(this, value, offset, 0, 0xffffffffff);
view.setUint32(offset + 1, value | 0, false);
view.setUint8(offset, Math.floor(value * 2 ** -32));
break;
}
case 6: {
require("internal/buffer").check_int48(this, value, offset, 0, 0xffffffffffff);
view.setUint32(offset + 2, value | 0, false);
view.setUint16(offset, Math.floor(value * 2 ** -32), false);
break;
}
default: {
throw new RangeError("byteLength must be >= 1 and <= 6");
require("internal/buffer").boundsError(byteLength, 6, "byteLength");
}
}
return offset + byteLength;
}
export function writeFloatLE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat32(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").checkBounds(this, offset, 3);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setFloat32(offset, value, true);
return offset + 4;
}
export function writeFloatBE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat32(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").checkBounds(this, offset, 3);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setFloat32(offset, value, false);
return offset + 4;
}
export function writeDoubleLE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat64(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").checkBounds(this, offset, 7);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setFloat64(offset, value, true);
return offset + 8;
}
export function writeDoubleBE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat64(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
value = +value;
require("internal/buffer").checkBounds(this, offset, 7);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setFloat64(offset, value, false);
return offset + 8;
}
export function writeBigInt64LE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigInt64(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
require("internal/buffer").check_bigint64(this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
if (typeof value !== "bigint") throw require("internal/buffer").ERR_INVALID_ARG_TYPE("value", "bigint", value);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setBigInt64(offset, value, true);
return offset + 8;
}
export function writeBigInt64BE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigInt64(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
require("internal/buffer").check_bigint64(this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn);
if (typeof value !== "bigint") throw require("internal/buffer").ERR_INVALID_ARG_TYPE("value", "bigint", value);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setBigInt64(offset, value, false);
return offset + 8;
}
export function writeBigUInt64LE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigUint64(
offset === undefined ? (offset = 0) : offset,
value,
true,
);
if (offset === undefined) offset = 0;
require("internal/buffer").check_bigint64(this, value, offset, 0n, 0xffffffffffffffffn);
if (typeof value !== "bigint") throw require("internal/buffer").ERR_INVALID_ARG_TYPE("value", "bigint", value);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setBigUint64(offset, value, true);
return offset + 8;
}
export function writeBigUInt64BE(this: BufferExt, value, offset) {
(this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setBigUint64(
offset === undefined ? (offset = 0) : offset,
value,
false,
);
if (offset === undefined) offset = 0;
require("internal/buffer").check_bigint64(this, value, offset, 0n, 0xffffffffffffffffn);
if (typeof value !== "bigint") throw require("internal/buffer").ERR_INVALID_ARG_TYPE("value", "bigint", value);
const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength));
view.setBigUint64(offset, value, false);
return offset + 8;
}
@@ -487,7 +655,7 @@ export function slice(this: BufferExt, start, end) {
// Use Math.trunc() to convert offset to an integer value that can be larger
// than an Int32. Hence, don't use offset | 0 or similar techniques.
offset = Math.trunc(offset);
if (offset === 0 || offset !== offset) {
if (offset === undefined || offset !== offset) {
return 0;
} else if (offset < 0) {
offset += length;

134
src/js/internal/buffer.ts Normal file
View File

@@ -0,0 +1,134 @@
// XXX: should more of this file be c++ imports?
const { validateNumber, validateInteger } = require("internal/validators");
const { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, ERR_BUFFER_OUT_OF_BOUNDS } = require("internal/errors");
const { isAnyArrayBuffer } = require("node:util/types");
const BufferFrom1 = $newCppFunction("JSBuffer.cpp", "jsFunction_BufferFrom_Array", 0);
const BufferFrom2 = $newCppFunction("JSBuffer.cpp", "jsFunction_BufferFrom_ArrayBuffer", 0);
const BufferFrom4 = $newCppFunction("JSBuffer.cpp", "jsFunction_BufferFrom_Size", 0);
const BufferFrom5 = $newCppFunction("JSBuffer.cpp", "jsFunction_BufferFrom_String", 0);
const ArrayIsArray = Array.isArray;
function boundsError(value, length, type?) {
if (Math.floor(value) !== value) {
validateNumber(value, type);
throw ERR_OUT_OF_RANGE(type || "offset", "an integer", value);
}
if (length < 0) throw ERR_BUFFER_OUT_OF_BOUNDS();
throw ERR_OUT_OF_RANGE(type || "offset", `>= ${type ? 1 : 0} and <= ${length}`, value);
}
function checkBounds(buf, offset, byteLength) {
validateNumber(offset, "offset");
if (buf[offset] === undefined || buf[offset + byteLength] === undefined)
boundsError(offset, buf.length - (byteLength + 1));
}
function checkInt(value, min, max, buf, offset, byteLength) {
if (value > max || value < min) {
const n = typeof min === "bigint" ? "n" : "";
let range;
if (byteLength > 3) {
if (min === 0 || min === 0n) {
range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`;
} else {
range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and ` + `< 2${n} ** ${(byteLength + 1) * 8 - 1}${n}`;
}
} else {
range = `>= ${min}${n} and <= ${max}${n}`;
}
throw ERR_OUT_OF_RANGE("value", range, value);
}
checkBounds(buf, offset, byteLength);
}
function check_read(buf, offset, byteLength) {
validateInteger(offset, "offset");
const type = buf.length - byteLength;
if (!(offset >= 0 && offset <= type)) boundsError(offset, type);
}
function check_int8(buf, value, offset, min, max) {
validateNumber(offset, "offset");
if (value > max || value < min) throw ERR_OUT_OF_RANGE("value", `>= ${min} and <= ${max}`, value);
if (buf[offset] === undefined) boundsError(offset, buf.length - 1);
}
function check_int16(buf, value, offset, min, max) {
checkInt(value, min, max, buf, offset, 1);
}
function check_int24(buf, value, offset, min, max) {
checkInt(value, min, max, buf, offset, 2);
}
function check_int32(buf, value, offset, min, max) {
checkInt(value, min, max, buf, offset, 3);
}
function check_int40(buf, value, offset, min, max) {
checkInt(value, min, max, buf, offset, 4);
}
function check_int48(buf, value, offset, min, max) {
checkInt(value, min, max, buf, offset, 5);
}
function check_bigint64(buf, value, offset, min, max) {
checkInt(value, min, max, buf, offset, 7);
}
function fromString(string, encoding) {
return BufferFrom5(string, encoding);
}
function fromArrayBuffer(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number) {
return BufferFrom2(arrayBuffer, byteOffset, length);
}
function fromObject(obj) {
if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) {
if (typeof obj.length !== "number") {
return BufferFrom4(0);
}
return fromArrayLike(obj);
}
if (obj.type === "Buffer" && ArrayIsArray(obj.data)) {
return fromArrayLike(obj.data);
}
}
function fromArrayLike(obj) {
if (obj.length <= 0) return BufferFrom4(0);
if (obj.length < Buffer.poolSize >>> 1) {
// if (obj.length > poolSize - poolOffset) createPool();
// const b = new FastBuffer(allocPool, poolOffset, obj.length);
// TypedArrayPrototypeSet(b, obj, 0);
// poolOffset += obj.length;
// alignPool();
// return b;
}
return BufferFrom1(obj);
}
export default {
validateNumber,
validateInteger,
ERR_INVALID_ARG_TYPE,
ERR_OUT_OF_RANGE,
boundsError,
checkBounds,
checkInt,
check_read,
check_int8,
check_int16,
check_int24,
check_int32,
check_int40,
check_int48,
check_bigint64,
fromString,
fromObject,
fromArrayBuffer,
};

View File

@@ -1,6 +1,7 @@
import { Buffer, SlowBuffer, isAscii, isUtf8, kMaxLength } from "buffer";
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { gc } from "harness";
import vm from "node:vm";
const BufferModule = await import("buffer");
@@ -21,7 +22,7 @@ afterEach(() => gc());
const NumberIsInteger = Number.isInteger;
class ERR_INVALID_ARG_TYPE extends TypeError {
constructor() {
super("Invalid arg type" + Array.prototype.join.call(arguments, " "));
super(`The "${arguments[0]}" argument must be of type ${arguments[1]}. Received (${Bun.inspect(arguments[2])})`);
this.code = "ERR_INVALID_ARG_TYPE";
}
}
@@ -269,7 +270,7 @@ for (let withOverridenBufferWrite of [false, true]) {
// Invalid encoding for Buffer.write
expect(() => b.write("test string", 0, 5, "invalid")).toThrow(/encoding/);
// Unsupported arguments for Buffer.write
expect(() => b.write("test", "utf8", 0)).toThrow(/invalid/i);
expect(() => b.write("test", "utf8", 0)).toThrow(/The "offset" argument must be of type number. Received /);
});
it("create 0-length buffers", () => {
@@ -1205,7 +1206,7 @@ for (let withOverridenBufferWrite of [false, true]) {
it("toLocaleString()", () => {
const buf = Buffer.from("test");
expect(buf.toLocaleString()).toBe(buf.toString());
// expect(Buffer.prototype.toLocaleString).toBe(Buffer.prototype.toString);
expect(Buffer.prototype.toLocaleString).toBe(Buffer.prototype.toString);
});
it("alloc() should throw on invalid data", () => {
@@ -2116,7 +2117,7 @@ for (let withOverridenBufferWrite of [false, true]) {
const buf = Buffer.from(ab);
expect(buf instanceof Buffer).toBe(true);
// expect(buf.parent, buf.buffer);
expect(buf.parent, buf.buffer);
expect(buf.buffer).toBe(ab);
expect(buf.length).toBe(ab.byteLength);
@@ -2136,12 +2137,12 @@ for (let withOverridenBufferWrite of [false, true]) {
// Now test protecting users from doing stupid things
// expect(function () {
// function AB() {}
// Object.setPrototypeOf(AB, ArrayBuffer);
// Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
// // Buffer.from(new AB());
// }).toThrow();
expect(function () {
function AB() {}
Object.setPrototypeOf(AB, ArrayBuffer);
Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
Buffer.from(new AB());
}).toThrow();
// console.log(origAB !== ab);
// Test the byteOffset and length arguments
@@ -2670,8 +2671,8 @@ for (let withOverridenBufferWrite of [false, true]) {
});
// Test that ArrayBuffer from a different context is detected correctly
// const arrayBuf = vm.runInNewContext("new ArrayBuffer()");
// expect(Buffer.byteLength(arrayBuf)).toBe(0);
const arrayBuf = vm.runInNewContext("new ArrayBuffer()");
expect(Buffer.byteLength(arrayBuf)).toBe(0);
// Verify that invalid encodings are treated as utf8
for (let i = 1; i < 10; i++) {

View File

@@ -657,7 +657,7 @@ describe("fork", () => {
code: "ERR_INVALID_ARG_TYPE",
name: "TypeError",
message: expect.stringContaining(
`The "modulePath" argument must be of type string, Buffer or URL. Received `,
`The "modulePath" argument must be of type string, Buffer, or URL. Received `,
),
}),
);

View File

@@ -30,6 +30,7 @@ const net = require('net');
// Do not require 'os' until needed so that test-os-checked-function can
// monkey patch it. If 'os' is required here, that test will fail.
const path = require('path');
const { inspect } = require('util');
const { inspect, getCallSites } = require('util');
const { isMainThread } = require('worker_threads');
const { isModuleNamespaceObject } = require('util/types');
@@ -902,13 +903,14 @@ function invalidArgTypeHelper(input) {
return ` Received ${input}`;
}
if (typeof input === 'function') {
return ` Received function ${input.name}`;
if (input.name) return ` Received function ${input.name}`;
return ` Received function`;
}
if (typeof input === 'object') {
if (input.constructor?.name) {
return ` Received an instance of ${input.constructor.name}`;
}
return ` Received ${inspect(input, { depth: -1 })}`;
return ` Received ${inspect(input, { depth: 0 })}`;
}
let inspected = inspect(input, { colors: false });

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
'use strict';
require('../common');
const assert = require('assert');
const LENGTH = 16;
const ab = new ArrayBuffer(LENGTH);
const dv = new DataView(ab);
const ui = new Uint8Array(ab);
const buf = Buffer.from(ab);
assert.ok(buf instanceof Buffer);
assert.strictEqual(buf.parent, buf.buffer);
assert.strictEqual(buf.buffer, ab);
assert.strictEqual(buf.length, ab.byteLength);
buf.fill(0xC);
for (let i = 0; i < LENGTH; i++) {
assert.strictEqual(ui[i], 0xC);
ui[i] = 0xF;
assert.strictEqual(buf[i], 0xF);
}
buf.writeUInt32LE(0xF00, 0);
buf.writeUInt32BE(0xB47, 4);
buf.writeDoubleLE(3.1415, 8);
assert.strictEqual(dv.getUint32(0, true), 0xF00);
assert.strictEqual(dv.getUint32(4), 0xB47);
assert.strictEqual(dv.getFloat64(8, true), 3.1415);
// Now test protecting users from doing stupid things
assert.throws(function() {
function AB() { }
Object.setPrototypeOf(AB, ArrayBuffer);
Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
Buffer.from(new AB());
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The first argument must be of type string, ' +
'Buffer, ArrayBuffer, Array, or Array-like Object. Received ' +
'an instance of AB'
});
// Test the byteOffset and length arguments
{
const ab = new Uint8Array(5);
ab[0] = 1;
ab[1] = 2;
ab[2] = 3;
ab[3] = 4;
ab[4] = 5;
const buf = Buffer.from(ab.buffer, 1, 3);
assert.strictEqual(buf.length, 3);
assert.strictEqual(buf[0], 2);
assert.strictEqual(buf[1], 3);
assert.strictEqual(buf[2], 4);
buf[0] = 9;
assert.strictEqual(ab[1], 9);
assert.throws(() => Buffer.from(ab.buffer, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"offset" is outside of buffer bounds'
});
assert.throws(() => Buffer.from(ab.buffer, 3, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"length" is outside of buffer bounds'
});
}
// Test the deprecated Buffer() version also
{
const ab = new Uint8Array(5);
ab[0] = 1;
ab[1] = 2;
ab[2] = 3;
ab[3] = 4;
ab[4] = 5;
const buf = Buffer(ab.buffer, 1, 3);
assert.strictEqual(buf.length, 3);
assert.strictEqual(buf[0], 2);
assert.strictEqual(buf[1], 3);
assert.strictEqual(buf[2], 4);
buf[0] = 9;
assert.strictEqual(ab[1], 9);
assert.throws(() => Buffer(ab.buffer, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"offset" is outside of buffer bounds'
});
assert.throws(() => Buffer(ab.buffer, 3, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"length" is outside of buffer bounds'
});
}
{
// If byteOffset is not numeric, it defaults to 0.
const ab = new ArrayBuffer(10);
const expected = Buffer.from(ab, 0);
assert.deepStrictEqual(Buffer.from(ab, 'fhqwhgads'), expected);
assert.deepStrictEqual(Buffer.from(ab, NaN), expected);
assert.deepStrictEqual(Buffer.from(ab, {}), expected);
assert.deepStrictEqual(Buffer.from(ab, []), expected);
// If byteOffset can be converted to a number, it will be.
assert.deepStrictEqual(Buffer.from(ab, [1]), Buffer.from(ab, 1));
// If byteOffset is Infinity, throw.
assert.throws(() => {
Buffer.from(ab, Infinity);
}, {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"offset" is outside of buffer bounds'
});
}
{
// If length is not numeric, it defaults to 0.
const ab = new ArrayBuffer(10);
const expected = Buffer.from(ab, 0, 0);
assert.deepStrictEqual(Buffer.from(ab, 0, 'fhqwhgads'), expected);
assert.deepStrictEqual(Buffer.from(ab, 0, NaN), expected);
assert.deepStrictEqual(Buffer.from(ab, 0, {}), expected);
assert.deepStrictEqual(Buffer.from(ab, 0, []), expected);
// If length can be converted to a number, it will be.
assert.deepStrictEqual(Buffer.from(ab, 0, [1]), Buffer.from(ab, 0, 1));
// If length is Infinity, throw.
assert.throws(() => {
Buffer.from(ab, 0, Infinity);
}, {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"length" is outside of buffer bounds'
});
}
// Test an array like entry with the length set to NaN.
assert.deepStrictEqual(Buffer.from({ length: NaN }), Buffer.alloc(0));

View File

@@ -0,0 +1,38 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
if (true) return; // TODO: BUN
const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const { arrayBufferViewHasBuffer } = internalBinding('util');
const tests = [
{ length: 0, expectOnHeap: true },
{ length: 48, expectOnHeap: true },
{ length: 96, expectOnHeap: false },
{ length: 1024, expectOnHeap: false },
];
for (const { length, expectOnHeap } of tests) {
const arrays = [
new Uint8Array(length),
new Uint16Array(length / 2),
new Uint32Array(length / 4),
new Float32Array(length / 4),
new Float64Array(length / 8),
Buffer.alloc(length),
Buffer.allocUnsafeSlow(length),
// Buffer.allocUnsafe() is missing because it may use pooled allocations.
];
for (const array of arrays) {
const isOnHeap = !arrayBufferViewHasBuffer(array);
assert.strictEqual(isOnHeap, expectOnHeap,
`mismatch: ${isOnHeap} vs ${expectOnHeap} ` +
`for ${array.constructor.name}, length = ${length}`);
// Consistency check: Accessing .buffer should create it.
array.buffer; // eslint-disable-line no-unused-expressions
assert(arrayBufferViewHasBuffer(array));
}
}

View File

@@ -0,0 +1,48 @@
'use strict';
require('../common');
const assert = require('assert');
// Test hex strings and bad hex strings
{
const buf = Buffer.alloc(4);
assert.strictEqual(buf.length, 4);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('abcdxx', 0, 'hex'), 2);
assert.deepStrictEqual(buf, Buffer.from([0xab, 0xcd, 0x00, 0x00]));
assert.strictEqual(buf.toString('hex'), 'abcd0000');
assert.strictEqual(buf.write('abcdef01', 0, 'hex'), 4);
assert.deepStrictEqual(buf, Buffer.from([0xab, 0xcd, 0xef, 0x01]));
assert.strictEqual(buf.toString('hex'), 'abcdef01');
const copy = Buffer.from(buf.toString('hex'), 'hex');
assert.strictEqual(buf.toString('hex'), copy.toString('hex'));
}
{
const buf = Buffer.alloc(5);
assert.strictEqual(buf.write('abcdxx', 1, 'hex'), 2);
assert.strictEqual(buf.toString('hex'), '00abcd0000');
}
{
const buf = Buffer.alloc(4);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('xxabcd', 0, 'hex'), 0);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('xxab', 1, 'hex'), 0);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('cdxxab', 0, 'hex'), 1);
assert.deepStrictEqual(buf, Buffer.from([0xcd, 0, 0, 0]));
}
{
const buf = Buffer.alloc(256);
for (let i = 0; i < 256; i++)
buf[i] = i;
const hex = buf.toString('hex');
assert.deepStrictEqual(Buffer.from(hex, 'hex'), buf);
const badHex = `${hex.slice(0, 256)}xx${hex.slice(256, 510)}`;
assert.deepStrictEqual(Buffer.from(badHex, 'hex'), buf.slice(0, 128));
}

View File

@@ -0,0 +1,55 @@
'use strict';
require('../common');
const assert = require('assert');
const buf = Buffer.allocUnsafe(8);
['LE', 'BE'].forEach(function(endianness) {
// Should allow simple BigInts to be written and read
let val = 123456789n;
buf[`writeBigInt64${endianness}`](val, 0);
let rtn = buf[`readBigInt64${endianness}`](0);
assert.strictEqual(val, rtn);
// Should allow INT64_MAX to be written and read
val = 0x7fffffffffffffffn;
buf[`writeBigInt64${endianness}`](val, 0);
rtn = buf[`readBigInt64${endianness}`](0);
assert.strictEqual(val, rtn);
// Should read and write a negative signed 64-bit integer
val = -123456789n;
buf[`writeBigInt64${endianness}`](val, 0);
assert.strictEqual(val, buf[`readBigInt64${endianness}`](0));
// Should read and write an unsigned 64-bit integer
val = 123456789n;
buf[`writeBigUInt64${endianness}`](val, 0);
assert.strictEqual(val, buf[`readBigUInt64${endianness}`](0));
// Should throw a RangeError upon INT64_MAX+1 being written
assert.throws(function() {
const val = 0x8000000000000000n;
buf[`writeBigInt64${endianness}`](val, 0);
}, RangeError);
// Should throw a RangeError upon UINT64_MAX+1 being written
assert.throws(function() {
const val = 0x10000000000000000n;
buf[`writeBigUInt64${endianness}`](val, 0);
}, {
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "value" is out of range. It must be ' +
'>= 0n and < 2n ** 64n. Received 18446744073709551616n'
});
// Should throw a TypeError upon invalid input
assert.throws(function() {
buf[`writeBigInt64${endianness}`]('bad', 0);
}, TypeError);
// Should throw a TypeError upon invalid input
assert.throws(function() {
buf[`writeBigUInt64${endianness}`]('bad', 0);
}, TypeError);
});

View File

@@ -0,0 +1,132 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const SlowBuffer = require('buffer').SlowBuffer;
const vm = require('vm');
[
[32, 'latin1'],
[NaN, 'utf8'],
[{}, 'latin1'],
[],
].forEach((args) => {
assert.throws(
() => Buffer.byteLength(...args),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "string" argument must be of type string or an instance ' +
'of Buffer or ArrayBuffer.' +
common.invalidArgTypeHelper(args[0])
}
);
});
assert(ArrayBuffer.isView(new Buffer(10)));
assert(ArrayBuffer.isView(new SlowBuffer(10)));
assert(ArrayBuffer.isView(Buffer.alloc(10)));
assert(ArrayBuffer.isView(Buffer.allocUnsafe(10)));
assert(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10)));
assert(ArrayBuffer.isView(Buffer.from('')));
// buffer
const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]);
assert.strictEqual(Buffer.byteLength(incomplete), 5);
const ascii = Buffer.from('abc');
assert.strictEqual(Buffer.byteLength(ascii), 3);
// ArrayBuffer
const buffer = new ArrayBuffer(8);
assert.strictEqual(Buffer.byteLength(buffer), 8);
// TypedArray
const int8 = new Int8Array(8);
assert.strictEqual(Buffer.byteLength(int8), 8);
const uint8 = new Uint8Array(8);
assert.strictEqual(Buffer.byteLength(uint8), 8);
const uintc8 = new Uint8ClampedArray(2);
assert.strictEqual(Buffer.byteLength(uintc8), 2);
const int16 = new Int16Array(8);
assert.strictEqual(Buffer.byteLength(int16), 16);
const uint16 = new Uint16Array(8);
assert.strictEqual(Buffer.byteLength(uint16), 16);
const int32 = new Int32Array(8);
assert.strictEqual(Buffer.byteLength(int32), 32);
const uint32 = new Uint32Array(8);
assert.strictEqual(Buffer.byteLength(uint32), 32);
const float32 = new Float32Array(8);
assert.strictEqual(Buffer.byteLength(float32), 32);
const float64 = new Float64Array(8);
assert.strictEqual(Buffer.byteLength(float64), 64);
// DataView
const dv = new DataView(new ArrayBuffer(2));
assert.strictEqual(Buffer.byteLength(dv), 2);
// Special case: zero length string
assert.strictEqual(Buffer.byteLength('', 'ascii'), 0);
assert.strictEqual(Buffer.byteLength('', 'HeX'), 0);
// utf8
assert.strictEqual(Buffer.byteLength('∑éllö wørl∂!', 'utf-8'), 19);
assert.strictEqual(Buffer.byteLength('κλμνξο', 'utf8'), 12);
assert.strictEqual(Buffer.byteLength('挵挶挷挸挹', 'utf-8'), 15);
assert.strictEqual(Buffer.byteLength('𠝹𠱓𠱸', 'UTF8'), 12);
// Without an encoding, utf8 should be assumed
assert.strictEqual(Buffer.byteLength('hey there'), 9);
assert.strictEqual(Buffer.byteLength('𠱸挶νξ#xx :)'), 17);
assert.strictEqual(Buffer.byteLength('hello world', ''), 11);
// It should also be assumed with unrecognized encoding
assert.strictEqual(Buffer.byteLength('hello world', 'abc'), 11);
assert.strictEqual(Buffer.byteLength('ßœ∑≈', 'unkn0wn enc0ding'), 10);
// base64
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'base64'), 11);
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'BASE64'), 11);
assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE=', 'base64'), 14);
assert.strictEqual(Buffer.byteLength('aGkk', 'base64'), 3);
assert.strictEqual(
Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==', 'base64'), 25
);
// base64url
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'base64url'), 11);
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'BASE64URL'), 11);
assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE', 'base64url'), 14);
assert.strictEqual(Buffer.byteLength('aGkk', 'base64url'), 3);
assert.strictEqual(
Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw', 'base64url'), 25
);
// special padding
assert.strictEqual(Buffer.byteLength('aaa=', 'base64'), 2);
assert.strictEqual(Buffer.byteLength('aaaa==', 'base64'), 3);
assert.strictEqual(Buffer.byteLength('aaa=', 'base64url'), 2);
assert.strictEqual(Buffer.byteLength('aaaa==', 'base64url'), 3);
assert.strictEqual(Buffer.byteLength('Il était tué'), 14);
assert.strictEqual(Buffer.byteLength('Il était tué', 'utf8'), 14);
['ascii', 'latin1', 'binary']
.reduce((es, e) => es.concat(e, e.toUpperCase()), [])
.forEach((encoding) => {
assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 12);
});
['ucs2', 'ucs-2', 'utf16le', 'utf-16le']
.reduce((es, e) => es.concat(e, e.toUpperCase()), [])
.forEach((encoding) => {
assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 24);
});
// Test that ArrayBuffer from a different context is detected correctly
const arrayBuf = vm.runInNewContext('new ArrayBuffer()');
assert.strictEqual(Buffer.byteLength(arrayBuf), 0);
// Verify that invalid encodings are treated as utf8
for (let i = 1; i < 10; i++) {
const encoding = String(i).repeat(i);
assert.ok(!Buffer.isEncoding(encoding));
assert.strictEqual(Buffer.byteLength('foo', encoding),
Buffer.byteLength('foo', 'utf8'));
}

View File

@@ -0,0 +1,47 @@
'use strict';
require('../common');
const assert = require('assert');
const b = Buffer.alloc(1, 'a');
const c = Buffer.alloc(1, 'c');
const d = Buffer.alloc(2, 'aa');
const e = new Uint8Array([ 0x61, 0x61 ]); // ASCII 'aa', same as d
assert.strictEqual(b.compare(c), -1);
assert.strictEqual(c.compare(d), 1);
assert.strictEqual(d.compare(b), 1);
assert.strictEqual(d.compare(e), 0);
assert.strictEqual(b.compare(d), -1);
assert.strictEqual(b.compare(b), 0);
assert.strictEqual(Buffer.compare(b, c), -1);
assert.strictEqual(Buffer.compare(c, d), 1);
assert.strictEqual(Buffer.compare(d, b), 1);
assert.strictEqual(Buffer.compare(b, d), -1);
assert.strictEqual(Buffer.compare(c, c), 0);
assert.strictEqual(Buffer.compare(e, e), 0);
assert.strictEqual(Buffer.compare(d, e), 0);
assert.strictEqual(Buffer.compare(d, b), 1);
assert.strictEqual(Buffer.compare(Buffer.alloc(0), Buffer.alloc(0)), 0);
assert.strictEqual(Buffer.compare(Buffer.alloc(0), Buffer.alloc(1)), -1);
assert.strictEqual(Buffer.compare(Buffer.alloc(1), Buffer.alloc(0)), 1);
assert.throws(() => Buffer.compare(Buffer.alloc(1), 'abc'), {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "buf2" argument must be an instance of Buffer or Uint8Array. ' +
'Received type string ("abc")'
});
assert.throws(() => Buffer.compare('abc', Buffer.alloc(1)), {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "buf1" argument must be an instance of Buffer or Uint8Array. ' +
'Received type string ("abc")'
});
assert.throws(() => Buffer.alloc(1).compare('abc'), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "target" argument must be an instance of ' +
'Buffer or Uint8Array. Received type string ("abc")'
});

View File

@@ -0,0 +1,100 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
const assert = require('assert');
const zero = [];
const one = [ Buffer.from('asdf') ];
const long = [];
for (let i = 0; i < 10; i++) long.push(Buffer.from('asdf'));
const flatZero = Buffer.concat(zero);
const flatOne = Buffer.concat(one);
const flatLong = Buffer.concat(long);
const flatLongLen = Buffer.concat(long, 40);
assert.strictEqual(flatZero.length, 0);
assert.strictEqual(flatOne.toString(), 'asdf');
const check = 'asdf'.repeat(10);
// A special case where concat used to return the first item,
// if the length is one. This check is to make sure that we don't do that.
assert.notStrictEqual(flatOne, one[0]);
assert.strictEqual(flatLong.toString(), check);
assert.strictEqual(flatLongLen.toString(), check);
[undefined, null, Buffer.from('hello')].forEach((value) => {
assert.throws(() => {
Buffer.concat(value);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "list" argument must be an instance of Array.' +
`${common.invalidArgTypeHelper(value)}`
});
});
[[42], ['hello', Buffer.from('world')]].forEach((value) => {
assert.throws(() => {
Buffer.concat(value);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "list[0]" argument must be an instance of Buffer ' +
`or Uint8Array.${common.invalidArgTypeHelper(value[0])}`
});
});
assert.throws(() => {
Buffer.concat([Buffer.from('hello'), 3]);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "list[1]" argument must be an instance of Buffer ' +
'or Uint8Array. Received type number (3)'
});
// eslint-disable-next-line node-core/crypto-check
const random10 = common.hasCrypto ?
require('crypto').randomBytes(10) :
Buffer.alloc(10, 1);
const empty = Buffer.alloc(0);
assert.notDeepStrictEqual(random10, empty);
assert.notDeepStrictEqual(random10, Buffer.alloc(10));
assert.deepStrictEqual(Buffer.concat([], 100), empty);
assert.deepStrictEqual(Buffer.concat([random10], 0), empty);
assert.deepStrictEqual(Buffer.concat([random10], 10), random10);
assert.deepStrictEqual(Buffer.concat([random10, random10], 10), random10);
assert.deepStrictEqual(Buffer.concat([empty, random10]), random10);
assert.deepStrictEqual(Buffer.concat([random10, empty, empty]), random10);
// The tail should be zero-filled
assert.deepStrictEqual(Buffer.concat([empty], 100), Buffer.alloc(100));
assert.deepStrictEqual(Buffer.concat([empty], 4096), Buffer.alloc(4096));
assert.deepStrictEqual(
Buffer.concat([random10], 40),
Buffer.concat([random10, Buffer.alloc(30)]));
assert.deepStrictEqual(Buffer.concat([new Uint8Array([0x41, 0x42]),
new Uint8Array([0x43, 0x44])]),
Buffer.from('ABCD'));

View File

@@ -0,0 +1,17 @@
'use strict';
require('../common');
const assert = require('assert');
const { kMaxLength, kStringMaxLength } = require('buffer');
const { MAX_LENGTH, MAX_STRING_LENGTH } = require('buffer').constants;
assert.strictEqual(typeof MAX_LENGTH, 'number');
assert.strictEqual(typeof MAX_STRING_LENGTH, 'number');
assert(MAX_STRING_LENGTH <= MAX_LENGTH);
assert.throws(() => ' '.repeat(MAX_STRING_LENGTH + 1), /^RangeError: Out of memory$/);
' '.repeat(MAX_STRING_LENGTH); // Should not throw.
// Legacy values match:
assert.strictEqual(kMaxLength, MAX_LENGTH);
assert.strictEqual(kStringMaxLength, MAX_STRING_LENGTH);

View File

@@ -0,0 +1,237 @@
'use strict';
require('../common');
const assert = require('assert');
const b = Buffer.allocUnsafe(1024);
const c = Buffer.allocUnsafe(512);
let cntr = 0;
{
// copy 512 bytes, from 0 to 512.
b.fill(++cntr);
c.fill(++cntr);
const copied = b.copy(c, 0, 0, 512);
assert.strictEqual(copied, 512);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], b[i]);
}
}
{
// Current behavior is to coerce values to integers.
b.fill(++cntr);
c.fill(++cntr);
const copied = b.copy(c, '0', '0', '512');
assert.strictEqual(copied, 512);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], b[i]);
}
}
{
// Floats will be converted to integers via `Math.floor`
b.fill(++cntr);
c.fill(++cntr);
const copied = b.copy(c, 0, 0, 512.5);
assert.strictEqual(copied, 512);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], b[i]);
}
}
{
// Copy c into b, without specifying sourceEnd
b.fill(++cntr);
c.fill(++cntr);
const copied = c.copy(b, 0, 0);
assert.strictEqual(copied, c.length);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(b[i], c[i]);
}
}
{
// Copy c into b, without specifying sourceStart
b.fill(++cntr);
c.fill(++cntr);
const copied = c.copy(b, 0);
assert.strictEqual(copied, c.length);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(b[i], c[i]);
}
}
{
// Copied source range greater than source length
b.fill(++cntr);
c.fill(++cntr);
const copied = c.copy(b, 0, 0, c.length + 1);
assert.strictEqual(copied, c.length);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(b[i], c[i]);
}
}
{
// Copy longer buffer b to shorter c without targetStart
b.fill(++cntr);
c.fill(++cntr);
const copied = b.copy(c);
assert.strictEqual(copied, c.length);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], b[i]);
}
}
{
// Copy starting near end of b to c
b.fill(++cntr);
c.fill(++cntr);
const copied = b.copy(c, 0, b.length - Math.floor(c.length / 2));
assert.strictEqual(copied, Math.floor(c.length / 2));
for (let i = 0; i < Math.floor(c.length / 2); i++) {
assert.strictEqual(c[i], b[b.length - Math.floor(c.length / 2) + i]);
}
for (let i = Math.floor(c.length / 2) + 1; i < c.length; i++) {
assert.strictEqual(c[c.length - 1], c[i]);
}
}
{
// Try to copy 513 bytes, and check we don't overrun c
b.fill(++cntr);
c.fill(++cntr);
const copied = b.copy(c, 0, 0, 513);
assert.strictEqual(copied, c.length);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], b[i]);
}
}
{
// copy 768 bytes from b into b
b.fill(++cntr);
b.fill(++cntr, 256);
const copied = b.copy(b, 0, 256, 1024);
assert.strictEqual(copied, 768);
for (let i = 0; i < b.length; i++) {
assert.strictEqual(b[i], cntr);
}
}
// Copy string longer than buffer length (failure will segfault)
const bb = Buffer.allocUnsafe(10);
bb.fill('hello crazy world');
// Try to copy from before the beginning of b. Should not throw.
b.copy(c, 0, 100, 10);
// Throw with invalid source type
assert.throws(
() => Buffer.prototype.copy.call(0),
{
// code: 'ERR_INVALID_ARG_TYPE',
code: 'ERR_INVALID_THIS',
name: 'TypeError',
}
);
// Copy throws at negative targetStart
assert.throws(
() => Buffer.allocUnsafe(5).copy(Buffer.allocUnsafe(5), -1, 0),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "targetStart" is out of range. ' +
'It must be >= 0 and <= 5. Received -1'
}
);
// Copy throws at negative sourceStart
assert.throws(
() => Buffer.allocUnsafe(5).copy(Buffer.allocUnsafe(5), 0, -1),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
}
);
// Copy throws if sourceStart is greater than length of source
assert.throws(
() => Buffer.allocUnsafe(5).copy(Buffer.allocUnsafe(5), 0, 100),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
}
);
{
// Check sourceEnd resets to targetEnd if former is greater than the latter
b.fill(++cntr);
c.fill(++cntr);
b.copy(c, 0, 0, 1025);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], b[i]);
}
}
// Throw with negative sourceEnd
assert.throws(
() => b.copy(c, 0, 0, -1),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "sourceEnd" is out of range. ' +
'It must be >= 0 and <= 1024. Received -1'
}
);
// When sourceStart is greater than sourceEnd, zero copied
assert.strictEqual(b.copy(c, 0, 100, 10), 0);
// When targetStart > targetLength, zero copied
assert.strictEqual(b.copy(c, 512, 0, 10), 0);
// Test that the `target` can be a Uint8Array.
{
const d = new Uint8Array(c);
// copy 512 bytes, from 0 to 512.
b.fill(++cntr);
d.fill(++cntr);
const copied = b.copy(d, 0, 0, 512);
assert.strictEqual(copied, 512);
for (let i = 0; i < d.length; i++) {
assert.strictEqual(d[i], b[i]);
}
}
// Test that the source can be a Uint8Array, too.
{
const e = new Uint8Array(b);
// copy 512 bytes, from 0 to 512.
e.fill(++cntr);
c.fill(++cntr);
const copied = Buffer.prototype.copy.call(e, c, 0, 0, 512);
assert.strictEqual(copied, 512);
for (let i = 0; i < c.length; i++) {
assert.strictEqual(c[i], e[i]);
}
}
// https://github.com/nodejs/node/issues/23668: Do not crash for invalid input.
c.fill('c');
b.copy(c, 'not a valid offset');
// Make sure this acted like a regular copy with `0` offset.
assert.deepStrictEqual(c, b.slice(0, c.length));
{
c.fill('C');
assert.throws(() => {
b.copy(c, { [Symbol.toPrimitive]() { throw new Error('foo'); } });
}, /foo/);
// No copying took place:
assert.deepStrictEqual(c.toString(), 'C'.repeat(c.length));
}

View File

@@ -0,0 +1,25 @@
'use strict';
require('../common');
const assert = require('assert');
const b = Buffer.from('abcdf');
const c = Buffer.from('abcdf');
const d = Buffer.from('abcde');
const e = Buffer.from('abcdef');
assert.ok(b.equals(c));
assert.ok(!c.equals(d));
assert.ok(!d.equals(e));
assert.ok(d.equals(d));
assert.ok(d.equals(new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65])));
assert.throws(
() => Buffer.alloc(1).equals('abc'),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "otherBuffer" argument must be an instance of ' +
'Buffer or Uint8Array. Received type string ("abc")'
}
);

View File

@@ -0,0 +1,429 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const SIZE = 28;
const buf1 = Buffer.allocUnsafe(SIZE);
const buf2 = Buffer.allocUnsafe(SIZE);
// Default encoding
testBufs('abc');
testBufs('\u0222aa');
testBufs('a\u0234b\u0235c\u0236');
testBufs('abc', 4);
testBufs('abc', 5);
testBufs('abc', SIZE);
testBufs('\u0222aa', 2);
testBufs('\u0222aa', 8);
testBufs('a\u0234b\u0235c\u0236', 4);
testBufs('a\u0234b\u0235c\u0236', 12);
testBufs('abc', 4, 1);
testBufs('abc', 5, 1);
testBufs('\u0222aa', 8, 1);
testBufs('a\u0234b\u0235c\u0236', 4, 1);
testBufs('a\u0234b\u0235c\u0236', 12, 1);
// UTF8
testBufs('abc', 'utf8');
testBufs('\u0222aa', 'utf8');
testBufs('a\u0234b\u0235c\u0236', 'utf8');
testBufs('abc', 4, 'utf8');
testBufs('abc', 5, 'utf8');
testBufs('abc', SIZE, 'utf8');
testBufs('\u0222aa', 2, 'utf8');
testBufs('\u0222aa', 8, 'utf8');
testBufs('a\u0234b\u0235c\u0236', 4, 'utf8');
testBufs('a\u0234b\u0235c\u0236', 12, 'utf8');
testBufs('abc', 4, 1, 'utf8');
testBufs('abc', 5, 1, 'utf8');
testBufs('\u0222aa', 8, 1, 'utf8');
testBufs('a\u0234b\u0235c\u0236', 4, 1, 'utf8');
testBufs('a\u0234b\u0235c\u0236', 12, 1, 'utf8');
assert.strictEqual(Buffer.allocUnsafe(1).fill(0).fill('\u0222')[0], 0xc8);
// BINARY
testBufs('abc', 'binary');
testBufs('\u0222aa', 'binary');
testBufs('a\u0234b\u0235c\u0236', 'binary');
testBufs('abc', 4, 'binary');
testBufs('abc', 5, 'binary');
testBufs('abc', SIZE, 'binary');
testBufs('\u0222aa', 2, 'binary');
testBufs('\u0222aa', 8, 'binary');
testBufs('a\u0234b\u0235c\u0236', 4, 'binary');
testBufs('a\u0234b\u0235c\u0236', 12, 'binary');
testBufs('abc', 4, 1, 'binary');
testBufs('abc', 5, 1, 'binary');
testBufs('\u0222aa', 8, 1, 'binary');
testBufs('a\u0234b\u0235c\u0236', 4, 1, 'binary');
testBufs('a\u0234b\u0235c\u0236', 12, 1, 'binary');
// LATIN1
testBufs('abc', 'latin1');
testBufs('\u0222aa', 'latin1');
testBufs('a\u0234b\u0235c\u0236', 'latin1');
testBufs('abc', 4, 'latin1');
testBufs('abc', 5, 'latin1');
testBufs('abc', SIZE, 'latin1');
testBufs('\u0222aa', 2, 'latin1');
testBufs('\u0222aa', 8, 'latin1');
testBufs('a\u0234b\u0235c\u0236', 4, 'latin1');
testBufs('a\u0234b\u0235c\u0236', 12, 'latin1');
testBufs('abc', 4, 1, 'latin1');
testBufs('abc', 5, 1, 'latin1');
testBufs('\u0222aa', 8, 1, 'latin1');
testBufs('a\u0234b\u0235c\u0236', 4, 1, 'latin1');
testBufs('a\u0234b\u0235c\u0236', 12, 1, 'latin1');
// UCS2
testBufs('abc', 'ucs2');
testBufs('\u0222aa', 'ucs2');
testBufs('a\u0234b\u0235c\u0236', 'ucs2');
testBufs('abc', 4, 'ucs2');
testBufs('abc', SIZE, 'ucs2');
testBufs('\u0222aa', 2, 'ucs2');
testBufs('\u0222aa', 8, 'ucs2');
testBufs('a\u0234b\u0235c\u0236', 4, 'ucs2');
testBufs('a\u0234b\u0235c\u0236', 12, 'ucs2');
testBufs('abc', 4, 1, 'ucs2');
testBufs('abc', 5, 1, 'ucs2');
testBufs('\u0222aa', 8, 1, 'ucs2');
testBufs('a\u0234b\u0235c\u0236', 4, 1, 'ucs2');
testBufs('a\u0234b\u0235c\u0236', 12, 1, 'ucs2');
assert.strictEqual(Buffer.allocUnsafe(1).fill('\u0222', 'ucs2')[0], 0x22);
// HEX
testBufs('616263', 'hex');
testBufs('c8a26161', 'hex');
testBufs('61c8b462c8b563c8b6', 'hex');
testBufs('616263', 4, 'hex');
testBufs('616263', 5, 'hex');
testBufs('616263', SIZE, 'hex');
testBufs('c8a26161', 2, 'hex');
testBufs('c8a26161', 8, 'hex');
testBufs('61c8b462c8b563c8b6', 4, 'hex');
testBufs('61c8b462c8b563c8b6', 12, 'hex');
testBufs('616263', 4, 1, 'hex');
testBufs('616263', 5, 1, 'hex');
testBufs('c8a26161', 8, 1, 'hex');
testBufs('61c8b462c8b563c8b6', 4, 1, 'hex');
testBufs('61c8b462c8b563c8b6', 12, 1, 'hex');
assert.throws(() => {
const buf = Buffer.allocUnsafe(SIZE);
buf.fill('yKJh', 'hex');
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError'
});
assert.throws(() => {
const buf = Buffer.allocUnsafe(SIZE);
buf.fill('\u0222', 'hex');
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError'
});
// BASE64
testBufs('YWJj', 'base64');
testBufs('yKJhYQ==', 'base64');
testBufs('Yci0Ysi1Y8i2', 'base64');
testBufs('YWJj', 4, 'base64');
testBufs('YWJj', SIZE, 'base64');
testBufs('yKJhYQ==', 2, 'base64');
testBufs('yKJhYQ==', 8, 'base64');
testBufs('Yci0Ysi1Y8i2', 4, 'base64');
testBufs('Yci0Ysi1Y8i2', 12, 'base64');
testBufs('YWJj', 4, 1, 'base64');
testBufs('YWJj', 5, 1, 'base64');
testBufs('yKJhYQ==', 8, 1, 'base64');
testBufs('Yci0Ysi1Y8i2', 4, 1, 'base64');
testBufs('Yci0Ysi1Y8i2', 12, 1, 'base64');
// BASE64URL
testBufs('YWJj', 'base64url');
testBufs('yKJhYQ', 'base64url');
testBufs('Yci0Ysi1Y8i2', 'base64url');
testBufs('YWJj', 4, 'base64url');
testBufs('YWJj', SIZE, 'base64url');
testBufs('yKJhYQ', 2, 'base64url');
testBufs('yKJhYQ', 8, 'base64url');
testBufs('Yci0Ysi1Y8i2', 4, 'base64url');
testBufs('Yci0Ysi1Y8i2', 12, 'base64url');
testBufs('YWJj', 4, 1, 'base64url');
testBufs('YWJj', 5, 1, 'base64url');
testBufs('yKJhYQ', 8, 1, 'base64url');
testBufs('Yci0Ysi1Y8i2', 4, 1, 'base64url');
testBufs('Yci0Ysi1Y8i2', 12, 1, 'base64url');
// Buffer
function deepStrictEqualValues(buf, arr) {
for (const [index, value] of buf.entries()) {
assert.deepStrictEqual(value, arr[index]);
}
}
const buf2Fill = Buffer.allocUnsafe(1).fill(2);
deepStrictEqualValues(genBuffer(4, [buf2Fill]), [2, 2, 2, 2]);
deepStrictEqualValues(genBuffer(4, [buf2Fill, 1]), [0, 2, 2, 2]);
deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 3]), [0, 2, 2, 0]);
deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 1]), [0, 0, 0, 0]);
const hexBufFill = Buffer.allocUnsafe(2).fill(0).fill('0102', 'hex');
deepStrictEqualValues(genBuffer(4, [hexBufFill]), [1, 2, 1, 2]);
deepStrictEqualValues(genBuffer(4, [hexBufFill, 1]), [0, 1, 2, 1]);
deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 3]), [0, 1, 2, 0]);
deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 1]), [0, 0, 0, 0]);
// Check exceptions
[
[0, -1],
[0, 0, buf1.length + 1],
['', -1],
['', 0, buf1.length + 1],
['', 1, -1],
].forEach((args) => {
assert.throws(
() => buf1.fill(...args),
{ code: 'ERR_OUT_OF_RANGE' }
);
});
assert.throws(
() => buf1.fill('a', 0, buf1.length, 'node rocks!'),
{
code: 'ERR_UNKNOWN_ENCODING',
name: 'TypeError',
message: 'Unknown encoding: node rocks!'
}
);
[
['a', 0, 0, NaN],
['a', 0, 0, false],
].forEach((args) => {
assert.throws(
() => buf1.fill(...args),
{
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "encoding" argument must be of type ' +
`string.${common.invalidArgTypeHelper(args[3])}`
}
);
});
assert.throws(
() => buf1.fill('a', 0, 0, 'foo'),
{
code: 'ERR_UNKNOWN_ENCODING',
name: 'TypeError',
message: 'Unknown encoding: foo'
}
);
function genBuffer(size, args) {
const b = Buffer.allocUnsafe(size);
return b.fill(0).fill.apply(b, args);
}
function bufReset() {
buf1.fill(0);
buf2.fill(0);
}
// This is mostly accurate. Except write() won't write partial bytes to the
// string while fill() blindly copies bytes into memory. To account for that an
// error will be thrown if not all the data can be written, and the SIZE has
// been massaged to work with the input characters.
function writeToFill(string, offset, end, encoding) {
if (typeof offset === 'string') {
encoding = offset;
offset = 0;
end = buf2.length;
} else if (typeof end === 'string') {
encoding = end;
end = buf2.length;
} else if (end === undefined) {
end = buf2.length;
}
// Should never be reached.
if (offset < 0 || end > buf2.length)
throw new ERR_OUT_OF_RANGE();
if (end <= offset)
return buf2;
offset >>>= 0;
end >>>= 0;
assert(offset <= buf2.length);
// Convert "end" to "length" (which write understands).
const length = end - offset < 0 ? 0 : end - offset;
let wasZero = false;
do {
const written = buf2.write(string, offset, length, encoding);
offset += written;
// Safety check in case write falls into infinite loop.
if (written === 0) {
if (wasZero)
throw new Error('Could not write all data to Buffer');
else
wasZero = true;
}
} while (offset < buf2.length);
return buf2;
}
function testBufs(string, offset, length, encoding) {
bufReset();
buf1.fill.apply(buf1, arguments);
// Swap bytes on BE archs for ucs2 encoding.
assert.deepStrictEqual(buf1.fill.apply(buf1, arguments),
writeToFill.apply(null, arguments));
}
// Make sure these throw.
assert.throws(
() => Buffer.allocUnsafe(8).fill('a', -1),
{ code: 'ERR_OUT_OF_RANGE' });
assert.throws(
() => Buffer.allocUnsafe(8).fill('a', 0, 9),
{ code: 'ERR_OUT_OF_RANGE' });
// Make sure this doesn't hang indefinitely.
Buffer.allocUnsafe(8).fill('');
Buffer.alloc(8, '');
{
const buf = Buffer.alloc(64, 10);
for (let i = 0; i < buf.length; i++)
assert.strictEqual(buf[i], 10);
buf.fill(11, 0, buf.length >> 1);
for (let i = 0; i < buf.length >> 1; i++)
assert.strictEqual(buf[i], 11);
for (let i = (buf.length >> 1) + 1; i < buf.length; i++)
assert.strictEqual(buf[i], 10);
buf.fill('h');
for (let i = 0; i < buf.length; i++)
assert.strictEqual(buf[i], 'h'.charCodeAt(0));
buf.fill(0);
for (let i = 0; i < buf.length; i++)
assert.strictEqual(buf[i], 0);
buf.fill(null);
for (let i = 0; i < buf.length; i++)
assert.strictEqual(buf[i], 0);
buf.fill(1, 16, 32);
for (let i = 0; i < 16; i++)
assert.strictEqual(buf[i], 0);
for (let i = 16; i < 32; i++)
assert.strictEqual(buf[i], 1);
for (let i = 32; i < buf.length; i++)
assert.strictEqual(buf[i], 0);
}
{
const buf = Buffer.alloc(10, 'abc');
assert.strictEqual(buf.toString(), 'abcabcabca');
buf.fill('է');
assert.strictEqual(buf.toString(), 'էէէէէ');
}
// Make sure "end" is properly checked, even if it's magically mangled using
// Symbol.toPrimitive.
{
assert.throws(() => {
const end = {
[Symbol.toPrimitive]() {
return 1;
}
};
Buffer.alloc(1).fill(Buffer.alloc(1), 0, end);
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "end" argument must be of type number. Received an ' +
'instance of Object'
});
}
// Test that bypassing 'length' won't cause an abort.
// assert.throws(() => {
// const buf = Buffer.from('w00t');
// Object.defineProperty(buf, 'length', {
// value: 1337,
// enumerable: true
// });
// buf.fill('');
// }, {
// code: 'ERR_BUFFER_OUT_OF_BOUNDS',
// name: 'RangeError',
// message: 'Attempt to access memory outside buffer bounds'
// });
assert.deepStrictEqual(
Buffer.allocUnsafeSlow(16).fill('ab', 'utf16le'),
Buffer.from('61006200610062006100620061006200', 'hex'));
assert.deepStrictEqual(
Buffer.allocUnsafeSlow(15).fill('ab', 'utf16le'),
Buffer.from('610062006100620061006200610062', 'hex'));
assert.deepStrictEqual(
Buffer.allocUnsafeSlow(16).fill('ab', 'utf16le'),
Buffer.from('61006200610062006100620061006200', 'hex'));
assert.deepStrictEqual(
Buffer.allocUnsafeSlow(16).fill('a', 'utf16le'),
Buffer.from('61006100610061006100610061006100', 'hex'));
assert.strictEqual(
Buffer.allocUnsafeSlow(16).fill('a', 'utf16le').toString('utf16le'),
'a'.repeat(8));
assert.strictEqual(
Buffer.allocUnsafeSlow(16).fill('a', 'latin1').toString('latin1'),
'a'.repeat(16));
assert.strictEqual(
Buffer.allocUnsafeSlow(16).fill('a', 'utf8').toString('utf8'),
'a'.repeat(16));
assert.strictEqual(
Buffer.allocUnsafeSlow(16).fill('Љ', 'utf16le').toString('utf16le'),
'Љ'.repeat(8));
assert.strictEqual(
Buffer.allocUnsafeSlow(16).fill('Љ', 'latin1').toString('latin1'),
'\t'.repeat(16));
assert.strictEqual(
Buffer.allocUnsafeSlow(16).fill('Љ', 'utf8').toString('utf8'),
'Љ'.repeat(8));
assert.throws(() => {
const buf = Buffer.from('a'.repeat(1000));
buf.fill('This is not correctly encoded', 'hex');
}, {
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError'
});
{
const bufEmptyString = Buffer.alloc(5, '');
assert.strictEqual(bufEmptyString.toString(), '\x00\x00\x00\x00\x00');
const bufEmptyArray = Buffer.alloc(5, []);
assert.strictEqual(bufEmptyArray.toString(), '\x00\x00\x00\x00\x00');
const bufEmptyBuffer = Buffer.alloc(5, Buffer.alloc(5));
assert.strictEqual(bufEmptyBuffer.toString(), '\x00\x00\x00\x00\x00');
const bufZero = Buffer.alloc(5, 0);
assert.strictEqual(bufZero.toString(), '\x00\x00\x00\x00\x00');
}

View File

@@ -0,0 +1,141 @@
'use strict';
const common = require('../common');
const { deepStrictEqual, strictEqual, throws } = require('assert');
const { runInNewContext } = require('vm');
const checkString = 'test';
const check = Buffer.from(checkString);
class MyString extends String {
constructor() {
super(checkString);
}
}
class MyPrimitive {
[Symbol.toPrimitive]() {
return checkString;
}
}
class MyBadPrimitive {
[Symbol.toPrimitive]() {
return 1;
}
}
deepStrictEqual(Buffer.from(new String(checkString)), check);
deepStrictEqual(Buffer.from(new MyString()), check);
deepStrictEqual(Buffer.from(new MyPrimitive()), check);
deepStrictEqual(
Buffer.from(runInNewContext('new String(checkString)', { checkString })),
check
);
[
{},
new Boolean(true),
{ valueOf() { return null; } },
{ valueOf() { return undefined; } },
{ valueOf: null },
{ __proto__: null },
new Number(true),
new MyBadPrimitive(),
Symbol(),
5n,
(one, two, three) => {},
undefined,
null,
].forEach((input) => {
const errObj = {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The first argument must be of type string, ' +
'Buffer, ArrayBuffer, Array, or Array-like Object.' +
common.invalidArgTypeHelper(input)
};
throws(() => Buffer.from(input), errObj);
throws(() => Buffer.from(input, 'hex'), errObj);
});
Buffer.allocUnsafe(10); // Should not throw.
Buffer.from('deadbeaf', 'hex'); // Should not throw.
{
const u16 = new Uint16Array([0xffff]);
const b16 = Buffer.copyBytesFrom(u16);
u16[0] = 0;
strictEqual(b16.length, 2);
strictEqual(b16[0], 255);
strictEqual(b16[1], 255);
}
{
const u16 = new Uint16Array([0, 0xffff]);
const b16 = Buffer.copyBytesFrom(u16, 1, 5);
u16[0] = 0xffff;
u16[1] = 0;
strictEqual(b16.length, 2);
strictEqual(b16[0], 255);
strictEqual(b16[1], 255);
}
{
const u32 = new Uint32Array([0xffffffff]);
const b32 = Buffer.copyBytesFrom(u32);
u32[0] = 0;
strictEqual(b32.length, 4);
strictEqual(b32[0], 255);
strictEqual(b32[1], 255);
strictEqual(b32[2], 255);
strictEqual(b32[3], 255);
}
throws(() => {
Buffer.copyBytesFrom();
}, {
code: 'ERR_INVALID_ARG_TYPE',
});
['', Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined].forEach(
(notTypedArray) => throws(() => {
Buffer.copyBytesFrom('nope');
}, {
code: 'ERR_INVALID_ARG_TYPE',
})
);
['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), notANumber);
}, {
code: 'ERR_INVALID_ARG_TYPE',
})
);
[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), outOfRange);
}, {
code: 'ERR_OUT_OF_RANGE',
})
);
['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber);
}, {
code: 'ERR_INVALID_ARG_TYPE',
})
);
[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
throws(() => {
Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange);
}, {
code: 'ERR_OUT_OF_RANGE',
})
);

View File

@@ -0,0 +1,304 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const b = Buffer.from('abcdef');
const buf_a = Buffer.from('a');
const buf_bc = Buffer.from('bc');
const buf_f = Buffer.from('f');
const buf_z = Buffer.from('z');
const buf_empty = Buffer.from('');
assert(b.includes('a'));
assert(!b.includes('a', 1));
assert(!b.includes('a', -1));
assert(!b.includes('a', -4));
assert(b.includes('a', -b.length));
assert(b.includes('a', NaN));
assert(b.includes('a', -Infinity));
assert(!b.includes('a', Infinity));
assert(b.includes('bc'));
assert(!b.includes('bc', 2));
assert(!b.includes('bc', -1));
assert(!b.includes('bc', -3));
assert(b.includes('bc', -5));
assert(b.includes('bc', NaN));
assert(b.includes('bc', -Infinity));
assert(!b.includes('bc', Infinity));
assert(b.includes('f'), b.length - 1);
assert(!b.includes('z'));
assert(b.includes(''));
assert(b.includes('', 1));
assert(b.includes('', b.length + 1));
assert(b.includes('', Infinity));
assert(b.includes(buf_a));
assert(!b.includes(buf_a, 1));
assert(!b.includes(buf_a, -1));
assert(!b.includes(buf_a, -4));
assert(b.includes(buf_a, -b.length));
assert(b.includes(buf_a, NaN));
assert(b.includes(buf_a, -Infinity));
assert(!b.includes(buf_a, Infinity));
assert(b.includes(buf_bc));
assert(!b.includes(buf_bc, 2));
assert(!b.includes(buf_bc, -1));
assert(!b.includes(buf_bc, -3));
assert(b.includes(buf_bc, -5));
assert(b.includes(buf_bc, NaN));
assert(b.includes(buf_bc, -Infinity));
assert(!b.includes(buf_bc, Infinity));
assert(b.includes(buf_f), b.length - 1);
assert(!b.includes(buf_z));
assert(b.includes(buf_empty));
assert(b.includes(buf_empty, 1));
assert(b.includes(buf_empty, b.length + 1));
assert(b.includes(buf_empty, Infinity));
assert(b.includes(0x61));
assert(!b.includes(0x61, 1));
assert(!b.includes(0x61, -1));
assert(!b.includes(0x61, -4));
assert(b.includes(0x61, -b.length));
assert(b.includes(0x61, NaN));
assert(b.includes(0x61, -Infinity));
assert(!b.includes(0x61, Infinity));
assert(!b.includes(0x0));
// test offsets
assert(b.includes('d', 2));
assert(b.includes('f', 5));
assert(b.includes('f', -1));
assert(!b.includes('f', 6));
assert(b.includes(Buffer.from('d'), 2));
assert(b.includes(Buffer.from('f'), 5));
assert(b.includes(Buffer.from('f'), -1));
assert(!b.includes(Buffer.from('f'), 6));
assert(!Buffer.from('ff').includes(Buffer.from('f'), 1, 'ucs2'));
// test hex encoding
assert.strictEqual(
Buffer.from(b.toString('hex'), 'hex')
.includes('64', 0, 'hex'),
true
);
assert.strictEqual(
Buffer.from(b.toString('hex'), 'hex')
.includes(Buffer.from('64', 'hex'), 0, 'hex'),
true
);
// Test base64 encoding
assert.strictEqual(
Buffer.from(b.toString('base64'), 'base64')
.includes('ZA==', 0, 'base64'),
true
);
assert.strictEqual(
Buffer.from(b.toString('base64'), 'base64')
.includes(Buffer.from('ZA==', 'base64'), 0, 'base64'),
true
);
// test ascii encoding
assert.strictEqual(
Buffer.from(b.toString('ascii'), 'ascii')
.includes('d', 0, 'ascii'),
true
);
assert.strictEqual(
Buffer.from(b.toString('ascii'), 'ascii')
.includes(Buffer.from('d', 'ascii'), 0, 'ascii'),
true
);
// Test latin1 encoding
assert.strictEqual(
Buffer.from(b.toString('latin1'), 'latin1')
.includes('d', 0, 'latin1'),
true
);
assert.strictEqual(
Buffer.from(b.toString('latin1'), 'latin1')
.includes(Buffer.from('d', 'latin1'), 0, 'latin1'),
true
);
// Test binary encoding
assert.strictEqual(
Buffer.from(b.toString('binary'), 'binary')
.includes('d', 0, 'binary'),
true
);
assert.strictEqual(
Buffer.from(b.toString('binary'), 'binary')
.includes(Buffer.from('d', 'binary'), 0, 'binary'),
true
);
// test ucs2 encoding
let twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2');
assert(twoByteString.includes('\u0395', 4, 'ucs2'));
assert(twoByteString.includes('\u03a3', -4, 'ucs2'));
assert(twoByteString.includes('\u03a3', -6, 'ucs2'));
assert(twoByteString.includes(
Buffer.from('\u03a3', 'ucs2'), -6, 'ucs2'));
assert(!twoByteString.includes('\u03a3', -2, 'ucs2'));
const mixedByteStringUcs2 =
Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2');
assert(mixedByteStringUcs2.includes('bc', 0, 'ucs2'));
assert(mixedByteStringUcs2.includes('\u03a3', 0, 'ucs2'));
assert(!mixedByteStringUcs2.includes('\u0396', 0, 'ucs2'));
assert.ok(
mixedByteStringUcs2.includes(Buffer.from('bc', 'ucs2'), 0, 'ucs2'));
assert.ok(
mixedByteStringUcs2.includes(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2'));
assert.ok(
!mixedByteStringUcs2.includes(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2'));
twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2');
// Test single char pattern
assert(twoByteString.includes('\u039a', 0, 'ucs2'));
assert(twoByteString.includes('\u0391', 0, 'ucs2'), 'Alpha');
assert(twoByteString.includes('\u03a3', 0, 'ucs2'), 'First Sigma');
assert(twoByteString.includes('\u03a3', 6, 'ucs2'), 'Second Sigma');
assert(twoByteString.includes('\u0395', 0, 'ucs2'), 'Epsilon');
assert(!twoByteString.includes('\u0392', 0, 'ucs2'), 'Not beta');
// Test multi-char pattern
assert(twoByteString.includes('\u039a\u0391', 0, 'ucs2'), 'Lambda Alpha');
assert(twoByteString.includes('\u0391\u03a3', 0, 'ucs2'), 'Alpha Sigma');
assert(twoByteString.includes('\u03a3\u03a3', 0, 'ucs2'), 'Sigma Sigma');
assert(twoByteString.includes('\u03a3\u0395', 0, 'ucs2'), 'Sigma Epsilon');
const mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395');
assert(mixedByteStringUtf8.includes('bc'));
assert(mixedByteStringUtf8.includes('bc', 5));
assert(mixedByteStringUtf8.includes('bc', -8));
assert(mixedByteStringUtf8.includes('\u03a3'));
assert(!mixedByteStringUtf8.includes('\u0396'));
// Test complex string includes algorithms. Only trigger for long strings.
// Long string that isn't a simple repeat of a shorter string.
let longString = 'A';
for (let i = 66; i < 76; i++) { // from 'B' to 'K'
longString = longString + String.fromCharCode(i) + longString;
}
const longBufferString = Buffer.from(longString);
// Pattern of 15 chars, repeated every 16 chars in long
let pattern = 'ABACABADABACABA';
for (let i = 0; i < longBufferString.length - pattern.length; i += 7) {
const includes = longBufferString.includes(pattern, i);
assert(includes, `Long ABACABA...-string at index ${i}`);
}
assert(longBufferString.includes('AJABACA'), 'Long AJABACA, First J');
assert(longBufferString.includes('AJABACA', 511), 'Long AJABACA, Second J');
pattern = 'JABACABADABACABA';
assert(longBufferString.includes(pattern), 'Long JABACABA..., First J');
assert(longBufferString.includes(pattern, 512), 'Long JABACABA..., Second J');
// Search for a non-ASCII string in a pure ASCII string.
const asciiString = Buffer.from(
'arglebargleglopglyfarglebargleglopglyfarglebargleglopglyf');
assert(!asciiString.includes('\x2061'));
assert(asciiString.includes('leb', 0));
// Search in string containing many non-ASCII chars.
const allCodePoints = [];
for (let i = 0; i < 65534; i++) allCodePoints[i] = i;
const allCharsString = String.fromCharCode.apply(String, allCodePoints) +
String.fromCharCode(65534, 65535);
const allCharsBufferUtf8 = Buffer.from(allCharsString);
const allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2');
// Search for string long enough to trigger complex search with ASCII pattern
// and UC16 subject.
assert(!allCharsBufferUtf8.includes('notfound'));
assert(!allCharsBufferUcs2.includes('notfound'));
// Find substrings in Utf8.
let lengths = [1, 3, 15]; // Single char, simple and complex.
let indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b];
for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
for (let i = 0; i < indices.length; i++) {
const index = indices[i];
let length = lengths[lengthIndex];
if (index + length > 0x7F) {
length = 2 * length;
}
if (index + length > 0x7FF) {
length = 3 * length;
}
if (index + length > 0xFFFF) {
length = 4 * length;
}
const patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length);
assert(index, allCharsBufferUtf8.includes(patternBufferUtf8));
const patternStringUtf8 = patternBufferUtf8.toString();
assert(index, allCharsBufferUtf8.includes(patternStringUtf8));
}
}
// Find substrings in Usc2.
lengths = [2, 4, 16]; // Single char, simple and complex.
indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0];
for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
for (let i = 0; i < indices.length; i++) {
const index = indices[i] * 2;
const length = lengths[lengthIndex];
const patternBufferUcs2 =
allCharsBufferUcs2.slice(index, index + length);
assert.ok(
allCharsBufferUcs2.includes(patternBufferUcs2, 0, 'ucs2'));
const patternStringUcs2 = patternBufferUcs2.toString('ucs2');
assert.ok(
allCharsBufferUcs2.includes(patternStringUcs2, 0, 'ucs2'));
}
}
[
() => { },
{},
[],
].forEach((val) => {
assert.throws(
() => b.includes(val),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "value" argument must be of type number, string, Buffer, or Uint8Array.' + common.invalidArgTypeHelper(val)
}
);
});
// Test truncation of Number arguments to uint8
{
const buf = Buffer.from('this is a test');
assert.ok(buf.includes(0x6973));
assert.ok(buf.includes(0x697320));
assert.ok(buf.includes(0x69732069));
assert.ok(buf.includes(0x697374657374));
assert.ok(buf.includes(0x69737374));
assert.ok(buf.includes(0x69737465));
assert.ok(buf.includes(0x69737465));
assert.ok(buf.includes(-140));
assert.ok(buf.includes(-152));
assert.ok(!buf.includes(0xff));
assert.ok(!buf.includes(0xffff));
}

View File

@@ -0,0 +1,629 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const b = Buffer.from('abcdef');
const buf_a = Buffer.from('a');
const buf_bc = Buffer.from('bc');
const buf_f = Buffer.from('f');
const buf_z = Buffer.from('z');
const buf_empty = Buffer.from('');
const s = 'abcdef';
assert.strictEqual(b.indexOf('a'), 0);
assert.strictEqual(b.indexOf('a', 1), -1);
assert.strictEqual(b.indexOf('a', -1), -1);
assert.strictEqual(b.indexOf('a', -4), -1);
assert.strictEqual(b.indexOf('a', -b.length), 0);
assert.strictEqual(b.indexOf('a', NaN), 0);
assert.strictEqual(b.indexOf('a', -Infinity), 0);
assert.strictEqual(b.indexOf('a', Infinity), -1);
assert.strictEqual(b.indexOf('bc'), 1);
assert.strictEqual(b.indexOf('bc', 2), -1);
assert.strictEqual(b.indexOf('bc', -1), -1);
assert.strictEqual(b.indexOf('bc', -3), -1);
assert.strictEqual(b.indexOf('bc', -5), 1);
assert.strictEqual(b.indexOf('bc', NaN), 1);
assert.strictEqual(b.indexOf('bc', -Infinity), 1);
assert.strictEqual(b.indexOf('bc', Infinity), -1);
assert.strictEqual(b.indexOf('f'), b.length - 1);
assert.strictEqual(b.indexOf('z'), -1);
assert.strictEqual(b.indexOf(''), 0);
assert.strictEqual(b.indexOf('', 1), 1);
assert.strictEqual(b.indexOf('', b.length + 1), b.length);
assert.strictEqual(b.indexOf('', Infinity), b.length);
assert.strictEqual(b.indexOf(buf_a), 0);
assert.strictEqual(b.indexOf(buf_a, 1), -1);
assert.strictEqual(b.indexOf(buf_a, -1), -1);
assert.strictEqual(b.indexOf(buf_a, -4), -1);
assert.strictEqual(b.indexOf(buf_a, -b.length), 0);
assert.strictEqual(b.indexOf(buf_a, NaN), 0);
assert.strictEqual(b.indexOf(buf_a, -Infinity), 0);
assert.strictEqual(b.indexOf(buf_a, Infinity), -1);
assert.strictEqual(b.indexOf(buf_bc), 1);
assert.strictEqual(b.indexOf(buf_bc, 2), -1);
assert.strictEqual(b.indexOf(buf_bc, -1), -1);
assert.strictEqual(b.indexOf(buf_bc, -3), -1);
assert.strictEqual(b.indexOf(buf_bc, -5), 1);
assert.strictEqual(b.indexOf(buf_bc, NaN), 1);
assert.strictEqual(b.indexOf(buf_bc, -Infinity), 1);
assert.strictEqual(b.indexOf(buf_bc, Infinity), -1);
assert.strictEqual(b.indexOf(buf_f), b.length - 1);
assert.strictEqual(b.indexOf(buf_z), -1);
assert.strictEqual(b.indexOf(buf_empty), 0);
assert.strictEqual(b.indexOf(buf_empty, 1), 1);
assert.strictEqual(b.indexOf(buf_empty, b.length + 1), b.length);
assert.strictEqual(b.indexOf(buf_empty, Infinity), b.length);
assert.strictEqual(b.indexOf(0x61), 0);
assert.strictEqual(b.indexOf(0x61, 1), -1);
assert.strictEqual(b.indexOf(0x61, -1), -1);
assert.strictEqual(b.indexOf(0x61, -4), -1);
assert.strictEqual(b.indexOf(0x61, -b.length), 0);
assert.strictEqual(b.indexOf(0x61, NaN), 0);
assert.strictEqual(b.indexOf(0x61, -Infinity), 0);
assert.strictEqual(b.indexOf(0x61, Infinity), -1);
assert.strictEqual(b.indexOf(0x0), -1);
// test offsets
assert.strictEqual(b.indexOf('d', 2), 3);
assert.strictEqual(b.indexOf('f', 5), 5);
assert.strictEqual(b.indexOf('f', -1), 5);
assert.strictEqual(b.indexOf('f', 6), -1);
assert.strictEqual(b.indexOf(Buffer.from('d'), 2), 3);
assert.strictEqual(b.indexOf(Buffer.from('f'), 5), 5);
assert.strictEqual(b.indexOf(Buffer.from('f'), -1), 5);
assert.strictEqual(b.indexOf(Buffer.from('f'), 6), -1);
assert.strictEqual(Buffer.from('ff').indexOf(Buffer.from('f'), 1, 'ucs2'), -1);
// Test invalid and uppercase encoding
assert.strictEqual(b.indexOf('b', 'utf8'), 1);
assert.strictEqual(b.indexOf('b', 'UTF8'), 1);
assert.strictEqual(b.indexOf('62', 'HEX'), 1);
assert.throws(() => b.indexOf('bad', 'enc'), /Unknown encoding: enc/);
// test hex encoding
assert.strictEqual(
Buffer.from(b.toString('hex'), 'hex')
.indexOf('64', 0, 'hex'),
3
);
assert.strictEqual(
Buffer.from(b.toString('hex'), 'hex')
.indexOf(Buffer.from('64', 'hex'), 0, 'hex'),
3
);
// Test base64 encoding
assert.strictEqual(
Buffer.from(b.toString('base64'), 'base64')
.indexOf('ZA==', 0, 'base64'),
3
);
assert.strictEqual(
Buffer.from(b.toString('base64'), 'base64')
.indexOf(Buffer.from('ZA==', 'base64'), 0, 'base64'),
3
);
// Test base64url encoding
assert.strictEqual(
Buffer.from(b.toString('base64url'), 'base64url')
.indexOf('ZA==', 0, 'base64url'),
3
);
// test ascii encoding
assert.strictEqual(
Buffer.from(b.toString('ascii'), 'ascii')
.indexOf('d', 0, 'ascii'),
3
);
assert.strictEqual(
Buffer.from(b.toString('ascii'), 'ascii')
.indexOf(Buffer.from('d', 'ascii'), 0, 'ascii'),
3
);
// Test latin1 encoding
assert.strictEqual(
Buffer.from(b.toString('latin1'), 'latin1')
.indexOf('d', 0, 'latin1'),
3
);
assert.strictEqual(
Buffer.from(b.toString('latin1'), 'latin1')
.indexOf(Buffer.from('d', 'latin1'), 0, 'latin1'),
3
);
assert.strictEqual(
Buffer.from('aa\u00e8aa', 'latin1')
.indexOf('\u00e8', 'latin1'),
2
);
assert.strictEqual(
Buffer.from('\u00e8', 'latin1')
.indexOf('\u00e8', 'latin1'),
0
);
assert.strictEqual(
Buffer.from('\u00e8', 'latin1')
.indexOf(Buffer.from('\u00e8', 'latin1'), 'latin1'),
0
);
// Test binary encoding
assert.strictEqual(
Buffer.from(b.toString('binary'), 'binary')
.indexOf('d', 0, 'binary'),
3
);
assert.strictEqual(
Buffer.from(b.toString('binary'), 'binary')
.indexOf(Buffer.from('d', 'binary'), 0, 'binary'),
3
);
assert.strictEqual(
Buffer.from('aa\u00e8aa', 'binary')
.indexOf('\u00e8', 'binary'),
2
);
assert.strictEqual(
Buffer.from('\u00e8', 'binary')
.indexOf('\u00e8', 'binary'),
0
);
assert.strictEqual(
Buffer.from('\u00e8', 'binary')
.indexOf(Buffer.from('\u00e8', 'binary'), 'binary'),
0
);
// Test optional offset with passed encoding
assert.strictEqual(Buffer.from('aaaa0').indexOf('30', 'hex'), 4);
assert.strictEqual(Buffer.from('aaaa00a').indexOf('3030', 'hex'), 4);
{
// Test usc2 and utf16le encoding
['ucs2', 'utf16le'].forEach((encoding) => {
const twoByteString = Buffer.from(
'\u039a\u0391\u03a3\u03a3\u0395', encoding);
assert.strictEqual(twoByteString.indexOf('\u0395', 4, encoding), 8);
assert.strictEqual(twoByteString.indexOf('\u03a3', -4, encoding), 6);
assert.strictEqual(twoByteString.indexOf('\u03a3', -6, encoding), 4);
assert.strictEqual(twoByteString.indexOf(
Buffer.from('\u03a3', encoding), -6, encoding), 4);
assert.strictEqual(-1, twoByteString.indexOf('\u03a3', -2, encoding));
});
}
const mixedByteStringUcs2 =
Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2');
assert.strictEqual(mixedByteStringUcs2.indexOf('bc', 0, 'ucs2'), 6);
assert.strictEqual(mixedByteStringUcs2.indexOf('\u03a3', 0, 'ucs2'), 10);
assert.strictEqual(-1, mixedByteStringUcs2.indexOf('\u0396', 0, 'ucs2'));
assert.strictEqual(
mixedByteStringUcs2.indexOf(Buffer.from('bc', 'ucs2'), 0, 'ucs2'), 6);
assert.strictEqual(
mixedByteStringUcs2.indexOf(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2'), 10);
assert.strictEqual(
-1, mixedByteStringUcs2.indexOf(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2'));
{
const twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2');
// Test single char pattern
assert.strictEqual(twoByteString.indexOf('\u039a', 0, 'ucs2'), 0);
let index = twoByteString.indexOf('\u0391', 0, 'ucs2');
assert.strictEqual(index, 2, `Alpha - at index ${index}`);
index = twoByteString.indexOf('\u03a3', 0, 'ucs2');
assert.strictEqual(index, 4, `First Sigma - at index ${index}`);
index = twoByteString.indexOf('\u03a3', 6, 'ucs2');
assert.strictEqual(index, 6, `Second Sigma - at index ${index}`);
index = twoByteString.indexOf('\u0395', 0, 'ucs2');
assert.strictEqual(index, 8, `Epsilon - at index ${index}`);
index = twoByteString.indexOf('\u0392', 0, 'ucs2');
assert.strictEqual(-1, index, `Not beta - at index ${index}`);
// Test multi-char pattern
index = twoByteString.indexOf('\u039a\u0391', 0, 'ucs2');
assert.strictEqual(index, 0, `Lambda Alpha - at index ${index}`);
index = twoByteString.indexOf('\u0391\u03a3', 0, 'ucs2');
assert.strictEqual(index, 2, `Alpha Sigma - at index ${index}`);
index = twoByteString.indexOf('\u03a3\u03a3', 0, 'ucs2');
assert.strictEqual(index, 4, `Sigma Sigma - at index ${index}`);
index = twoByteString.indexOf('\u03a3\u0395', 0, 'ucs2');
assert.strictEqual(index, 6, `Sigma Epsilon - at index ${index}`);
}
const mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395');
assert.strictEqual(mixedByteStringUtf8.indexOf('bc'), 5);
assert.strictEqual(mixedByteStringUtf8.indexOf('bc', 5), 5);
assert.strictEqual(mixedByteStringUtf8.indexOf('bc', -8), 5);
assert.strictEqual(mixedByteStringUtf8.indexOf('\u03a3'), 7);
assert.strictEqual(mixedByteStringUtf8.indexOf('\u0396'), -1);
// Test complex string indexOf algorithms. Only trigger for long strings.
// Long string that isn't a simple repeat of a shorter string.
let longString = 'A';
for (let i = 66; i < 76; i++) { // from 'B' to 'K'
longString = longString + String.fromCharCode(i) + longString;
}
const longBufferString = Buffer.from(longString);
// Pattern of 15 chars, repeated every 16 chars in long
let pattern = 'ABACABADABACABA';
for (let i = 0; i < longBufferString.length - pattern.length; i += 7) {
const index = longBufferString.indexOf(pattern, i);
assert.strictEqual((i + 15) & ~0xf, index,
`Long ABACABA...-string at index ${i}`);
}
let index = longBufferString.indexOf('AJABACA');
assert.strictEqual(index, 510, `Long AJABACA, First J - at index ${index}`);
index = longBufferString.indexOf('AJABACA', 511);
assert.strictEqual(index, 1534, `Long AJABACA, Second J - at index ${index}`);
pattern = 'JABACABADABACABA';
index = longBufferString.indexOf(pattern);
assert.strictEqual(index, 511, `Long JABACABA..., First J - at index ${index}`);
index = longBufferString.indexOf(pattern, 512);
assert.strictEqual(
index, 1535, `Long JABACABA..., Second J - at index ${index}`);
// Search for a non-ASCII string in a pure ASCII string.
const asciiString = Buffer.from(
'arglebargleglopglyfarglebargleglopglyfarglebargleglopglyf');
assert.strictEqual(-1, asciiString.indexOf('\x2061'));
assert.strictEqual(asciiString.indexOf('leb', 0), 3);
// Search in string containing many non-ASCII chars.
const allCodePoints = [];
for (let i = 0; i < 65534; i++) allCodePoints[i] = i;
const allCharsString = String.fromCharCode.apply(String, allCodePoints) + String.fromCharCode(65534, 65535);
const allCharsBufferUtf8 = Buffer.from(allCharsString);
const allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2');
// Search for string long enough to trigger complex search with ASCII pattern
// and UC16 subject.
assert.strictEqual(-1, allCharsBufferUtf8.indexOf('notfound'));
assert.strictEqual(-1, allCharsBufferUcs2.indexOf('notfound'));
// Needle is longer than haystack, but only because it's encoded as UTF-16
assert.strictEqual(Buffer.from('aaaa').indexOf('a'.repeat(4), 'ucs2'), -1);
assert.strictEqual(Buffer.from('aaaa').indexOf('a'.repeat(4), 'utf8'), 0);
assert.strictEqual(Buffer.from('aaaa').indexOf('你好', 'ucs2'), -1);
// Haystack has odd length, but the needle is UCS2.
assert.strictEqual(Buffer.from('aaaaa').indexOf('b', 'ucs2'), -1);
{
// Find substrings in Utf8.
const lengths = [1, 3, 15]; // Single char, simple and complex.
const indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b];
for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
for (let i = 0; i < indices.length; i++) {
const index = indices[i];
let length = lengths[lengthIndex];
if (index + length > 0x7F) {
length = 2 * length;
}
if (index + length > 0x7FF) {
length = 3 * length;
}
if (index + length > 0xFFFF) {
length = 4 * length;
}
const patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length);
assert.strictEqual(index, allCharsBufferUtf8.indexOf(patternBufferUtf8));
const patternStringUtf8 = patternBufferUtf8.toString();
assert.strictEqual(index, allCharsBufferUtf8.indexOf(patternStringUtf8));
}
}
}
{
// Find substrings in Usc2.
const lengths = [2, 4, 16]; // Single char, simple and complex.
const indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0];
for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
for (let i = 0; i < indices.length; i++) {
const index = indices[i] * 2;
const length = lengths[lengthIndex];
const patternBufferUcs2 = allCharsBufferUcs2.slice(index, index + length);
const actualB = allCharsBufferUcs2.indexOf(patternBufferUcs2, 0, 'ucs2');
assert.strictEqual(actualB, index);
const patternStringUcs2 = patternBufferUcs2.toString('ucs2');
const actualS = allCharsBufferUcs2.indexOf(patternStringUcs2, 0, 'ucs2');
assert.strictEqual(actualS, index);
}
}
}
[
() => {},
{},
[],
].forEach((val) => {
assert.throws(
() => b.indexOf(val),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "value" argument must be of type number, string, Buffer, or Uint8Array.' + common.invalidArgTypeHelper(val)
}
);
});
// Test weird offset arguments.
// The following offsets coerce to NaN or 0, searching the whole Buffer
assert.strictEqual(b.indexOf('b', undefined), 1);
assert.strictEqual(b.indexOf('b', {}), 1);
assert.strictEqual(b.indexOf('b', 0), 1);
assert.strictEqual(b.indexOf('b', null), 1);
assert.strictEqual(b.indexOf('b', []), 1);
// The following offset coerces to 2, in other words +[2] === 2
assert.strictEqual(b.indexOf('b', [2]), -1);
// Behavior should match String.indexOf()
assert.strictEqual(
b.indexOf('b', undefined),
s.indexOf('b', undefined));
assert.strictEqual(
b.indexOf('b', {}),
s.indexOf('b', {}));
assert.strictEqual(
b.indexOf('b', 0),
s.indexOf('b', 0));
assert.strictEqual(
b.indexOf('b', null),
s.indexOf('b', null));
assert.strictEqual(
b.indexOf('b', []),
s.indexOf('b', []));
assert.strictEqual(
b.indexOf('b', [2]),
s.indexOf('b', [2]));
// All code for handling encodings is shared between Buffer.indexOf and
// Buffer.lastIndexOf, so only testing the separate lastIndexOf semantics.
// Test lastIndexOf basic functionality; Buffer b contains 'abcdef'.
// lastIndexOf string:
assert.strictEqual(b.lastIndexOf('a'), 0);
assert.strictEqual(b.lastIndexOf('a', 1), 0);
assert.strictEqual(b.lastIndexOf('b', 1), 1);
assert.strictEqual(b.lastIndexOf('c', 1), -1);
assert.strictEqual(b.lastIndexOf('a', -1), 0);
assert.strictEqual(b.lastIndexOf('a', -4), 0);
assert.strictEqual(b.lastIndexOf('a', -b.length), 0);
assert.strictEqual(b.lastIndexOf('a', -b.length - 1), -1);
assert.strictEqual(b.lastIndexOf('a', NaN), 0);
assert.strictEqual(b.lastIndexOf('a', -Infinity), -1);
assert.strictEqual(b.lastIndexOf('a', Infinity), 0);
// lastIndexOf Buffer:
assert.strictEqual(b.lastIndexOf(buf_a), 0);
assert.strictEqual(b.lastIndexOf(buf_a, 1), 0);
assert.strictEqual(b.lastIndexOf(buf_a, -1), 0);
assert.strictEqual(b.lastIndexOf(buf_a, -4), 0);
assert.strictEqual(b.lastIndexOf(buf_a, -b.length), 0);
assert.strictEqual(b.lastIndexOf(buf_a, -b.length - 1), -1);
assert.strictEqual(b.lastIndexOf(buf_a, NaN), 0);
assert.strictEqual(b.lastIndexOf(buf_a, -Infinity), -1);
assert.strictEqual(b.lastIndexOf(buf_a, Infinity), 0);
assert.strictEqual(b.lastIndexOf(buf_bc), 1);
assert.strictEqual(b.lastIndexOf(buf_bc, 2), 1);
assert.strictEqual(b.lastIndexOf(buf_bc, -1), 1);
assert.strictEqual(b.lastIndexOf(buf_bc, -3), 1);
assert.strictEqual(b.lastIndexOf(buf_bc, -5), 1);
assert.strictEqual(b.lastIndexOf(buf_bc, -6), -1);
assert.strictEqual(b.lastIndexOf(buf_bc, NaN), 1);
assert.strictEqual(b.lastIndexOf(buf_bc, -Infinity), -1);
assert.strictEqual(b.lastIndexOf(buf_bc, Infinity), 1);
assert.strictEqual(b.lastIndexOf(buf_f), b.length - 1);
assert.strictEqual(b.lastIndexOf(buf_z), -1);
assert.strictEqual(b.lastIndexOf(buf_empty), b.length);
assert.strictEqual(b.lastIndexOf(buf_empty, 1), 1);
assert.strictEqual(b.lastIndexOf(buf_empty, b.length + 1), b.length);
assert.strictEqual(b.lastIndexOf(buf_empty, Infinity), b.length);
// lastIndexOf number:
assert.strictEqual(b.lastIndexOf(0x61), 0);
assert.strictEqual(b.lastIndexOf(0x61, 1), 0);
assert.strictEqual(b.lastIndexOf(0x61, -1), 0);
assert.strictEqual(b.lastIndexOf(0x61, -4), 0);
assert.strictEqual(b.lastIndexOf(0x61, -b.length), 0);
assert.strictEqual(b.lastIndexOf(0x61, -b.length - 1), -1);
assert.strictEqual(b.lastIndexOf(0x61, NaN), 0);
assert.strictEqual(b.lastIndexOf(0x61, -Infinity), -1);
assert.strictEqual(b.lastIndexOf(0x61, Infinity), 0);
assert.strictEqual(b.lastIndexOf(0x0), -1);
// Test weird offset arguments.
// The following offsets coerce to NaN, searching the whole Buffer
assert.strictEqual(b.lastIndexOf('b', undefined), 1);
assert.strictEqual(b.lastIndexOf('b', {}), 1);
// The following offsets coerce to 0
assert.strictEqual(b.lastIndexOf('b', 0), -1);
assert.strictEqual(b.lastIndexOf('b', null), -1);
assert.strictEqual(b.lastIndexOf('b', []), -1);
// The following offset coerces to 2, in other words +[2] === 2
assert.strictEqual(b.lastIndexOf('b', [2]), 1);
// Behavior should match String.lastIndexOf()
assert.strictEqual(
b.lastIndexOf('b', undefined),
s.lastIndexOf('b', undefined));
assert.strictEqual(
b.lastIndexOf('b', {}),
s.lastIndexOf('b', {}));
assert.strictEqual(
b.lastIndexOf('b', 0),
s.lastIndexOf('b', 0));
assert.strictEqual(
b.lastIndexOf('b', null),
s.lastIndexOf('b', null));
assert.strictEqual(
b.lastIndexOf('b', []),
s.lastIndexOf('b', []));
assert.strictEqual(
b.lastIndexOf('b', [2]),
s.lastIndexOf('b', [2]));
// Test needles longer than the haystack.
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'ucs2'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'utf8'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'latin1'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 'binary'), -1);
assert.strictEqual(b.lastIndexOf(Buffer.from('aaaaaaaaaaaaaaa')), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 2, 'ucs2'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 3, 'utf8'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 5, 'latin1'), -1);
assert.strictEqual(b.lastIndexOf('aaaaaaaaaaaaaaa', 5, 'binary'), -1);
assert.strictEqual(b.lastIndexOf(Buffer.from('aaaaaaaaaaaaaaa'), 7), -1);
// 你好 expands to a total of 6 bytes using UTF-8 and 4 bytes using UTF-16
assert.strictEqual(buf_bc.lastIndexOf('你好', 'ucs2'), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 'utf8'), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 'latin1'), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 'binary'), -1);
assert.strictEqual(buf_bc.lastIndexOf(Buffer.from('你好')), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 2, 'ucs2'), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 3, 'utf8'), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 5, 'latin1'), -1);
assert.strictEqual(buf_bc.lastIndexOf('你好', 5, 'binary'), -1);
assert.strictEqual(buf_bc.lastIndexOf(Buffer.from('你好'), 7), -1);
// Test lastIndexOf on a longer buffer:
const bufferString = Buffer.from('a man a plan a canal panama');
assert.strictEqual(bufferString.lastIndexOf('canal'), 15);
assert.strictEqual(bufferString.lastIndexOf('panama'), 21);
assert.strictEqual(bufferString.lastIndexOf('a man a plan a canal panama'), 0);
assert.strictEqual(-1, bufferString.lastIndexOf('a man a plan a canal mexico'));
assert.strictEqual(-1, bufferString
.lastIndexOf('a man a plan a canal mexico city'));
assert.strictEqual(-1, bufferString.lastIndexOf(Buffer.from('a'.repeat(1000))));
assert.strictEqual(bufferString.lastIndexOf('a man a plan', 4), 0);
assert.strictEqual(bufferString.lastIndexOf('a '), 13);
assert.strictEqual(bufferString.lastIndexOf('a ', 13), 13);
assert.strictEqual(bufferString.lastIndexOf('a ', 12), 6);
assert.strictEqual(bufferString.lastIndexOf('a ', 5), 0);
assert.strictEqual(bufferString.lastIndexOf('a ', -1), 13);
assert.strictEqual(bufferString.lastIndexOf('a ', -27), 0);
assert.strictEqual(-1, bufferString.lastIndexOf('a ', -28));
// Test lastIndexOf for the case that the first character can be found,
// but in a part of the buffer that does not make search to search
// due do length constraints.
const abInUCS2 = Buffer.from('ab', 'ucs2');
assert.strictEqual(-1, Buffer.from('µaaaa¶bbbb', 'latin1').lastIndexOf('µ'));
assert.strictEqual(-1, Buffer.from('µaaaa¶bbbb', 'binary').lastIndexOf('µ'));
assert.strictEqual(-1, Buffer.from('bc').lastIndexOf('ab'));
assert.strictEqual(-1, Buffer.from('abc').lastIndexOf('qa'));
assert.strictEqual(-1, Buffer.from('abcdef').lastIndexOf('qabc'));
assert.strictEqual(-1, Buffer.from('bc').lastIndexOf(Buffer.from('ab')));
assert.strictEqual(-1, Buffer.from('bc', 'ucs2').lastIndexOf('ab', 'ucs2'));
assert.strictEqual(-1, Buffer.from('bc', 'ucs2').lastIndexOf(abInUCS2));
assert.strictEqual(Buffer.from('abc').lastIndexOf('ab'), 0);
assert.strictEqual(Buffer.from('abc').lastIndexOf('ab', 1), 0);
assert.strictEqual(Buffer.from('abc').lastIndexOf('ab', 2), 0);
assert.strictEqual(Buffer.from('abc').lastIndexOf('ab', 3), 0);
// The above tests test the LINEAR and SINGLE-CHAR strategies.
// Now, we test the BOYER-MOORE-HORSPOOL strategy.
// Test lastIndexOf on a long buffer w multiple matches:
pattern = 'JABACABADABACABA';
assert.strictEqual(longBufferString.lastIndexOf(pattern), 1535);
assert.strictEqual(longBufferString.lastIndexOf(pattern, 1535), 1535);
assert.strictEqual(longBufferString.lastIndexOf(pattern, 1534), 511);
// Finally, give it a really long input to trigger fallback from BMH to
// regular BOYER-MOORE (which has better worst-case complexity).
// Generate a really long Thue-Morse sequence of 'yolo' and 'swag',
// "yolo swag swag yolo swag yolo yolo swag" ..., goes on for about 5MB.
// This is hard to search because it all looks similar, but never repeats.
// countBits returns the number of bits in the binary representation of n.
function countBits(n) {
let count;
for (count = 0; n > 0; count++) {
n = n & (n - 1); // remove top bit
}
return count;
}
const parts = [];
for (let i = 0; i < 1000000; i++) {
parts.push((countBits(i) % 2 === 0) ? 'yolo' : 'swag');
}
const reallyLong = Buffer.from(parts.join(' '));
assert.strictEqual(reallyLong.slice(0, 19).toString(), 'yolo swag swag yolo');
// Expensive reverse searches. Stress test lastIndexOf:
pattern = reallyLong.slice(0, 100000); // First 1/50th of the pattern.
assert.strictEqual(reallyLong.lastIndexOf(pattern), 4751360);
assert.strictEqual(reallyLong.lastIndexOf(pattern, 4000000), 3932160);
assert.strictEqual(reallyLong.lastIndexOf(pattern, 3000000), 2949120);
pattern = reallyLong.slice(100000, 200000); // Second 1/50th.
assert.strictEqual(reallyLong.lastIndexOf(pattern), 4728480);
pattern = reallyLong.slice(0, 1000000); // First 1/5th.
assert.strictEqual(reallyLong.lastIndexOf(pattern), 3932160);
pattern = reallyLong.slice(0, 2000000); // first 2/5ths.
assert.strictEqual(reallyLong.lastIndexOf(pattern), 0);
// Test truncation of Number arguments to uint8
{
const buf = Buffer.from('this is a test');
assert.strictEqual(buf.indexOf(0x6973), 3);
assert.strictEqual(buf.indexOf(0x697320), 4);
assert.strictEqual(buf.indexOf(0x69732069), 2);
assert.strictEqual(buf.indexOf(0x697374657374), 0);
assert.strictEqual(buf.indexOf(0x69737374), 0);
assert.strictEqual(buf.indexOf(0x69737465), 11);
assert.strictEqual(buf.indexOf(0x69737465), 11);
assert.strictEqual(buf.indexOf(-140), 0);
assert.strictEqual(buf.indexOf(-152), 1);
assert.strictEqual(buf.indexOf(0xff), -1);
assert.strictEqual(buf.indexOf(0xffff), -1);
}
// Test that Uint8Array arguments are okay.
{
const needle = new Uint8Array([ 0x66, 0x6f, 0x6f ]);
const haystack = Buffer.from('a foo b foo');
assert.strictEqual(haystack.indexOf(needle), 2);
assert.strictEqual(haystack.lastIndexOf(needle), haystack.length - 3);
}
// Avoid abort because of invalid usage
// see https://github.com/nodejs/node/issues/32753
// {
// assert.throws(() => {
// const buffer = require('buffer');
// new buffer.Buffer.prototype.lastIndexOf(1, 'str');
// }, {
// code: 'ERR_INVALID_ARG_TYPE',
// name: 'TypeError',
// message: 'The "buffer" argument must be an instance of Buffer, ' +
// 'TypedArray, or DataView. ' +
// 'Received an instance of lastIndexOf'
// });
// }

View File

@@ -0,0 +1,10 @@
'use strict';
require('../common');
const assert = require('assert');
assert.throws(() => new Buffer(42, 'utf8'), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "string" argument must be of type string. Received type number (42)'
});

View File

@@ -0,0 +1,106 @@
'use strict';
require('../common');
const assert = require('assert');
// Testing basic buffer read functions
const buf = Buffer.from([0xa4, 0xfd, 0x48, 0xea, 0xcf, 0xff, 0xd9, 0x01, 0xde]);
function read(buff, funx, args, expected) {
assert.strictEqual(buff[funx](...args), expected);
assert.throws(
() => buff[funx](-1, args[1]),
{ name: 'RangeError', code: 'ERR_OUT_OF_RANGE' }
);
}
// Testing basic functionality of readDoubleBE() and readDoubleLE()
read(buf, 'readDoubleBE', [1], -3.1827727774563287e+295);
read(buf, 'readDoubleLE', [1], -6.966010051009108e+144);
// Testing basic functionality of readFloatBE() and readFloatLE()
read(buf, 'readFloatBE', [1], -1.6691549692541768e+37);
read(buf, 'readFloatLE', [1], -7861303808);
// Testing basic functionality of readInt8()
read(buf, 'readInt8', [1], -3);
// Testing basic functionality of readInt16BE() and readInt16LE()
read(buf, 'readInt16BE', [1], -696);
read(buf, 'readInt16LE', [1], 0x48fd);
// Testing basic functionality of readInt32BE() and readInt32LE()
read(buf, 'readInt32BE', [1], -45552945);
read(buf, 'readInt32LE', [1], -806729475);
// Testing basic functionality of readIntBE() and readIntLE()
read(buf, 'readIntBE', [1, 1], -3);
read(buf, 'readIntLE', [2, 1], 0x48);
// Testing basic functionality of readUInt8()
read(buf, 'readUInt8', [1], 0xfd);
// Testing basic functionality of readUInt16BE() and readUInt16LE()
read(buf, 'readUInt16BE', [2], 0x48ea);
read(buf, 'readUInt16LE', [2], 0xea48);
// Testing basic functionality of readUInt32BE() and readUInt32LE()
read(buf, 'readUInt32BE', [1], 0xfd48eacf);
read(buf, 'readUInt32LE', [1], 0xcfea48fd);
// Testing basic functionality of readUIntBE() and readUIntLE()
read(buf, 'readUIntBE', [2, 2], 0x48ea);
read(buf, 'readUIntLE', [2, 2], 0xea48);
// Error name and message
const OOR_ERROR =
{
name: 'RangeError'
};
const OOB_ERROR =
{
name: 'RangeError',
message: 'Attempt to access memory outside buffer bounds'
};
// Attempt to overflow buffers, similar to previous bug in array buffers
assert.throws(
() => Buffer.allocUnsafe(8).readFloatBE(0xffffffff), OOR_ERROR);
assert.throws(
() => Buffer.allocUnsafe(8).readFloatLE(0xffffffff), OOR_ERROR);
// Ensure negative values can't get past offset
assert.throws(
() => Buffer.allocUnsafe(8).readFloatBE(-1), OOR_ERROR);
assert.throws(
() => Buffer.allocUnsafe(8).readFloatLE(-1), OOR_ERROR);
// Offset checks
{
const buf = Buffer.allocUnsafe(0);
assert.throws(
() => buf.readUInt8(0), OOB_ERROR);
assert.throws(
() => buf.readInt8(0), OOB_ERROR);
}
[16, 32].forEach((bit) => {
const buf = Buffer.allocUnsafe(bit / 8 - 1);
[`Int${bit}B`, `Int${bit}L`, `UInt${bit}B`, `UInt${bit}L`].forEach((fn) => {
assert.throws(
() => buf[`read${fn}E`](0), OOB_ERROR);
});
});
[16, 32].forEach((bits) => {
const buf = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]);
['LE', 'BE'].forEach((endian) => {
assert.strictEqual(buf[`readUInt${bits}${endian}`](0),
(0xFFFFFFFF >>> (32 - bits)));
assert.strictEqual(buf[`readInt${bits}${endian}`](0),
(0xFFFFFFFF >> (32 - bits)));
});
});

View File

@@ -0,0 +1,148 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Test (64 bit) double
const buffer = Buffer.allocUnsafe(8);
buffer[0] = 0x55;
buffer[1] = 0x55;
buffer[2] = 0x55;
buffer[3] = 0x55;
buffer[4] = 0x55;
buffer[5] = 0x55;
buffer[6] = 0xd5;
buffer[7] = 0x3f;
assert.strictEqual(buffer.readDoubleBE(0), 1.1945305291680097e+103);
assert.strictEqual(buffer.readDoubleLE(0), 0.3333333333333333);
buffer[0] = 1;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
buffer[4] = 0;
buffer[5] = 0;
buffer[6] = 0xf0;
buffer[7] = 0x3f;
assert.strictEqual(buffer.readDoubleBE(0), 7.291122019655968e-304);
assert.strictEqual(buffer.readDoubleLE(0), 1.0000000000000002);
buffer[0] = 2;
assert.strictEqual(buffer.readDoubleBE(0), 4.778309726801735e-299);
assert.strictEqual(buffer.readDoubleLE(0), 1.0000000000000004);
buffer[0] = 1;
buffer[6] = 0;
buffer[7] = 0;
// eslint-disable-next-line no-loss-of-precision
assert.strictEqual(buffer.readDoubleBE(0), 7.291122019556398e-304);
assert.strictEqual(buffer.readDoubleLE(0), 5e-324);
buffer[0] = 0xff;
buffer[1] = 0xff;
buffer[2] = 0xff;
buffer[3] = 0xff;
buffer[4] = 0xff;
buffer[5] = 0xff;
buffer[6] = 0x0f;
buffer[7] = 0x00;
assert.ok(Number.isNaN(buffer.readDoubleBE(0)));
assert.strictEqual(buffer.readDoubleLE(0), 2.225073858507201e-308);
buffer[6] = 0xef;
buffer[7] = 0x7f;
assert.ok(Number.isNaN(buffer.readDoubleBE(0)));
assert.strictEqual(buffer.readDoubleLE(0), 1.7976931348623157e+308);
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
buffer[4] = 0;
buffer[5] = 0;
buffer[6] = 0xf0;
buffer[7] = 0x3f;
assert.strictEqual(buffer.readDoubleBE(0), 3.03865e-319);
assert.strictEqual(buffer.readDoubleLE(0), 1);
buffer[6] = 0;
buffer[7] = 0x40;
assert.strictEqual(buffer.readDoubleBE(0), 3.16e-322);
assert.strictEqual(buffer.readDoubleLE(0), 2);
buffer[7] = 0xc0;
assert.strictEqual(buffer.readDoubleBE(0), 9.5e-322);
assert.strictEqual(buffer.readDoubleLE(0), -2);
buffer[6] = 0x10;
buffer[7] = 0;
assert.strictEqual(buffer.readDoubleBE(0), 2.0237e-320);
assert.strictEqual(buffer.readDoubleLE(0), 2.2250738585072014e-308);
buffer[6] = 0;
assert.strictEqual(buffer.readDoubleBE(0), 0);
assert.strictEqual(buffer.readDoubleLE(0), 0);
assert.ok(1 / buffer.readDoubleLE(0) >= 0);
buffer[7] = 0x80;
assert.strictEqual(buffer.readDoubleBE(0), 6.3e-322);
assert.strictEqual(buffer.readDoubleLE(0), -0);
assert.ok(1 / buffer.readDoubleLE(0) < 0);
buffer[6] = 0xf0;
buffer[7] = 0x7f;
assert.strictEqual(buffer.readDoubleBE(0), 3.0418e-319);
assert.strictEqual(buffer.readDoubleLE(0), Infinity);
buffer[7] = 0xff;
assert.strictEqual(buffer.readDoubleBE(0), 3.04814e-319);
assert.strictEqual(buffer.readDoubleLE(0), -Infinity);
['readDoubleLE', 'readDoubleBE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[fn](undefined);
buffer[fn]();
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => buffer[fn](off),
{
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE',
message: `The "offset" argument must be of type number.${common.invalidArgTypeHelper(off)}`,
}
);
});
[-1, 1].forEach((offset) => {
assert.throws(
() => buffer[fn](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= 0. Received ${offset}`
});
});
assert.throws(
() => Buffer.alloc(1)[fn](1),
{
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: 'Attempt to access memory outside buffer bounds'
});
[Infinity, NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[fn](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});

View File

@@ -0,0 +1,110 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Test 32 bit float
const buffer = Buffer.alloc(4);
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0x80;
buffer[3] = 0x3f;
assert.strictEqual(buffer.readFloatBE(0), 4.600602988224807e-41);
assert.strictEqual(buffer.readFloatLE(0), 1);
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0xc0;
assert.strictEqual(buffer.readFloatBE(0), 2.6904930515036488e-43);
assert.strictEqual(buffer.readFloatLE(0), -2);
buffer[0] = 0xff;
buffer[1] = 0xff;
buffer[2] = 0x7f;
buffer[3] = 0x7f;
assert.ok(Number.isNaN(buffer.readFloatBE(0)));
assert.strictEqual(buffer.readFloatLE(0), 3.4028234663852886e+38);
buffer[0] = 0xab;
buffer[1] = 0xaa;
buffer[2] = 0xaa;
buffer[3] = 0x3e;
assert.strictEqual(buffer.readFloatBE(0), -1.2126478207002966e-12);
assert.strictEqual(buffer.readFloatLE(0), 0.3333333432674408);
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
assert.strictEqual(buffer.readFloatBE(0), 0);
assert.strictEqual(buffer.readFloatLE(0), 0);
assert.ok(1 / buffer.readFloatLE(0) >= 0);
buffer[3] = 0x80;
assert.strictEqual(buffer.readFloatBE(0), 1.793662034335766e-43);
assert.strictEqual(buffer.readFloatLE(0), -0);
assert.ok(1 / buffer.readFloatLE(0) < 0);
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0x80;
buffer[3] = 0x7f;
assert.strictEqual(buffer.readFloatBE(0), 4.609571298396486e-41);
assert.strictEqual(buffer.readFloatLE(0), Infinity);
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0x80;
buffer[3] = 0xff;
assert.strictEqual(buffer.readFloatBE(0), 4.627507918739843e-41);
assert.strictEqual(buffer.readFloatLE(0), -Infinity);
['readFloatLE', 'readFloatBE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[fn](undefined);
buffer[fn]();
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => buffer[fn](off),
{
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE',
message: `The "offset" argument must be of type number.${common.invalidArgTypeHelper(off)}`,
}
);
});
[-1, 1].forEach((offset) => {
assert.throws(
() => buffer[fn](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= 0. Received ${offset}`
});
});
assert.throws(
() => Buffer.alloc(1)[fn](1),
{
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: 'Attempt to access memory outside buffer bounds'
});
[Infinity, NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[fn](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});

View File

@@ -0,0 +1,198 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Test OOB
{
const buffer = Buffer.alloc(4);
['Int8', 'Int16BE', 'Int16LE', 'Int32BE', 'Int32LE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[`read${fn}`](undefined);
buffer[`read${fn}`]();
['', '0', null, {}, [], () => {}, true, false].forEach((o) => {
assert.throws(
() => buffer[`read${fn}`](o),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: `The "offset" argument must be of type number.${common.invalidArgTypeHelper(o)}`,
});
});
[Infinity, -1, -4294967295].forEach((offset) => {
assert.throws(
() => buffer[`read${fn}`](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError'
});
});
[NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[`read${fn}`](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}
// Test 8 bit signed integers
{
const data = Buffer.from([0x23, 0xab, 0x7c, 0xef]);
assert.strictEqual(data.readInt8(0), 0x23);
data[0] = 0xff;
assert.strictEqual(data.readInt8(0), -1);
data[0] = 0x87;
assert.strictEqual(data.readInt8(0), -121);
assert.strictEqual(data.readInt8(1), -85);
assert.strictEqual(data.readInt8(2), 124);
assert.strictEqual(data.readInt8(3), -17);
}
// Test 16 bit integers
{
const buffer = Buffer.from([0x16, 0x79, 0x65, 0x6e, 0x69, 0x78]);
assert.strictEqual(buffer.readInt16BE(0), 0x1679);
assert.strictEqual(buffer.readInt16LE(0), 0x7916);
buffer[0] = 0xff;
buffer[1] = 0x80;
assert.strictEqual(buffer.readInt16BE(0), -128);
assert.strictEqual(buffer.readInt16LE(0), -32513);
buffer[0] = 0x77;
buffer[1] = 0x65;
assert.strictEqual(buffer.readInt16BE(0), 0x7765);
assert.strictEqual(buffer.readInt16BE(1), 0x6565);
assert.strictEqual(buffer.readInt16BE(2), 0x656e);
assert.strictEqual(buffer.readInt16BE(3), 0x6e69);
assert.strictEqual(buffer.readInt16BE(4), 0x6978);
assert.strictEqual(buffer.readInt16LE(0), 0x6577);
assert.strictEqual(buffer.readInt16LE(1), 0x6565);
assert.strictEqual(buffer.readInt16LE(2), 0x6e65);
assert.strictEqual(buffer.readInt16LE(3), 0x696e);
assert.strictEqual(buffer.readInt16LE(4), 0x7869);
}
// Test 32 bit integers
{
const buffer = Buffer.from([0x43, 0x53, 0x16, 0x79, 0x36, 0x17]);
assert.strictEqual(buffer.readInt32BE(0), 0x43531679);
assert.strictEqual(buffer.readInt32LE(0), 0x79165343);
buffer[0] = 0xff;
buffer[1] = 0xfe;
buffer[2] = 0xef;
buffer[3] = 0xfa;
assert.strictEqual(buffer.readInt32BE(0), -69638);
assert.strictEqual(buffer.readInt32LE(0), -84934913);
buffer[0] = 0x42;
buffer[1] = 0xc3;
buffer[2] = 0x95;
buffer[3] = 0xa9;
assert.strictEqual(buffer.readInt32BE(0), 0x42c395a9);
assert.strictEqual(buffer.readInt32BE(1), -1013601994);
assert.strictEqual(buffer.readInt32BE(2), -1784072681);
assert.strictEqual(buffer.readInt32LE(0), -1449802942);
assert.strictEqual(buffer.readInt32LE(1), 917083587);
assert.strictEqual(buffer.readInt32LE(2), 389458325);
}
// Test Int
{
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
assert.strictEqual(buffer.readIntLE(0, 1), 0x01);
assert.strictEqual(buffer.readIntBE(0, 1), 0x01);
assert.strictEqual(buffer.readIntLE(0, 3), 0x030201);
assert.strictEqual(buffer.readIntBE(0, 3), 0x010203);
assert.strictEqual(buffer.readIntLE(0, 5), 0x0504030201);
assert.strictEqual(buffer.readIntBE(0, 5), 0x0102030405);
assert.strictEqual(buffer.readIntLE(0, 6), 0x060504030201);
assert.strictEqual(buffer.readIntBE(0, 6), 0x010203040506);
assert.strictEqual(buffer.readIntLE(1, 6), 0x070605040302);
assert.strictEqual(buffer.readIntBE(1, 6), 0x020304050607);
assert.strictEqual(buffer.readIntLE(2, 6), 0x080706050403);
assert.strictEqual(buffer.readIntBE(2, 6), 0x030405060708);
// Check byteLength.
['readIntBE', 'readIntLE'].forEach((fn) => {
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((len) => {
assert.throws(
() => buffer[fn](0, len),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[Infinity, -1].forEach((byteLength) => {
assert.throws(
() => buffer[fn](0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "byteLength" is out of range. ' +
`It must be >= 1 and <= 6. Received ${byteLength}`
});
});
[NaN, 1.01].forEach((byteLength) => {
assert.throws(
() => buffer[fn](0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "byteLength" is out of range. ' +
`It must be an integer. Received ${byteLength}`
});
});
});
// Test 1 to 6 bytes.
for (let i = 1; i <= 6; i++) {
['readIntBE', 'readIntLE'].forEach((fn) => {
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => {
assert.throws(
() => buffer[fn](o, i),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});
[-1, -4294967295].forEach((offset) => {
assert.throws(
() => buffer[fn](offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= ${8 - i}. Received ${offset}`
});
});
[Infinity, NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[fn](offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}
}

View File

@@ -0,0 +1,168 @@
'use strict';
require('../common');
const assert = require('assert');
const common = require('../common');
// Test OOB
{
const buffer = Buffer.alloc(4);
['UInt8', 'UInt16BE', 'UInt16LE', 'UInt32BE', 'UInt32LE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[`read${fn}`](undefined);
buffer[`read${fn}`]();
['', '0', null, {}, [], () => {}, true, false].forEach((o) => {
assert.throws(
() => buffer[`read${fn}`](o),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: `The "offset" argument must be of type number.${common.invalidArgTypeHelper(o)}`,
});
});
[Infinity, -1, -4294967295].forEach((offset) => {
assert.throws(
() => buffer[`read${fn}`](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
});
});
[NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[`read${fn}`](offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}
// Test 8 bit unsigned integers
{
const data = Buffer.from([0xff, 0x2a, 0x2a, 0x2a]);
assert.strictEqual(data.readUInt8(0), 255);
assert.strictEqual(data.readUInt8(1), 42);
assert.strictEqual(data.readUInt8(2), 42);
assert.strictEqual(data.readUInt8(3), 42);
}
// Test 16 bit unsigned integers
{
const data = Buffer.from([0x00, 0x2a, 0x42, 0x3f]);
assert.strictEqual(data.readUInt16BE(0), 0x2a);
assert.strictEqual(data.readUInt16BE(1), 0x2a42);
assert.strictEqual(data.readUInt16BE(2), 0x423f);
assert.strictEqual(data.readUInt16LE(0), 0x2a00);
assert.strictEqual(data.readUInt16LE(1), 0x422a);
assert.strictEqual(data.readUInt16LE(2), 0x3f42);
data[0] = 0xfe;
data[1] = 0xfe;
assert.strictEqual(data.readUInt16BE(0), 0xfefe);
assert.strictEqual(data.readUInt16LE(0), 0xfefe);
}
// Test 32 bit unsigned integers
{
const data = Buffer.from([0x32, 0x65, 0x42, 0x56, 0x23, 0xff]);
assert.strictEqual(data.readUInt32BE(0), 0x32654256);
assert.strictEqual(data.readUInt32BE(1), 0x65425623);
assert.strictEqual(data.readUInt32BE(2), 0x425623ff);
assert.strictEqual(data.readUInt32LE(0), 0x56426532);
assert.strictEqual(data.readUInt32LE(1), 0x23564265);
assert.strictEqual(data.readUInt32LE(2), 0xff235642);
}
// Test UInt
{
const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
assert.strictEqual(buffer.readUIntLE(0, 1), 0x01);
assert.strictEqual(buffer.readUIntBE(0, 1), 0x01);
assert.strictEqual(buffer.readUIntLE(0, 3), 0x030201);
assert.strictEqual(buffer.readUIntBE(0, 3), 0x010203);
assert.strictEqual(buffer.readUIntLE(0, 5), 0x0504030201);
assert.strictEqual(buffer.readUIntBE(0, 5), 0x0102030405);
assert.strictEqual(buffer.readUIntLE(0, 6), 0x060504030201);
assert.strictEqual(buffer.readUIntBE(0, 6), 0x010203040506);
assert.strictEqual(buffer.readUIntLE(1, 6), 0x070605040302);
assert.strictEqual(buffer.readUIntBE(1, 6), 0x020304050607);
assert.strictEqual(buffer.readUIntLE(2, 6), 0x080706050403);
assert.strictEqual(buffer.readUIntBE(2, 6), 0x030405060708);
// Check byteLength.
['readUIntBE', 'readUIntLE'].forEach((fn) => {
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((len) => {
assert.throws(
() => buffer[fn](0, len),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[Infinity, -1].forEach((byteLength) => {
assert.throws(
() => buffer[fn](0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "byteLength" is out of range. ' +
`It must be >= 1 and <= 6. Received ${byteLength}`
});
});
[NaN, 1.01].forEach((byteLength) => {
assert.throws(
() => buffer[fn](0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "byteLength" is out of range. ' +
`It must be an integer. Received ${byteLength}`
});
});
});
// Test 1 to 6 bytes.
for (let i = 1; i <= 6; i++) {
['readUIntBE', 'readUIntLE'].forEach((fn) => {
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => {
assert.throws(
() => buffer[fn](o, i),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: `The "offset" argument must be of type number.${common.invalidArgTypeHelper(o)}`,
});
});
[-1, -4294967295].forEach((offset) => {
assert.throws(
() => buffer[fn](offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= ${8 - i}. Received ${offset}`
});
});
[Infinity, NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[fn](offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}
}

View File

@@ -0,0 +1,27 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const sab = new SharedArrayBuffer(24);
const arr1 = new Uint16Array(sab);
const arr2 = new Uint16Array(12);
arr2[0] = 5000;
arr1[0] = 5000;
arr1[1] = 4000;
arr2[1] = 4000;
const arr_buf = Buffer.from(arr1.buffer);
const ar_buf = Buffer.from(arr2.buffer);
assert.deepStrictEqual(arr_buf, ar_buf);
arr1[1] = 6000;
arr2[1] = 6000;
assert.deepStrictEqual(arr_buf, ar_buf);
// Checks for calling Buffer.byteLength on a SharedArrayBuffer.
assert.strictEqual(Buffer.byteLength(sab), sab.byteLength);
Buffer.from({ buffer: sab }); // Should not throw.

View File

@@ -0,0 +1,108 @@
'use strict';
const common = require('../common');
const assert = require('assert');
[-1, 10].forEach((offset) => {
assert.throws(
() => Buffer.alloc(9).write('foo', offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= 9. Received ${offset}`
}
);
});
const resultMap = new Map([
['utf8', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
['ucs2', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])],
['ascii', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
['latin1', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
['binary', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
['utf16le', Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])],
['base64', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
['base64url', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
['hex', Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])],
]);
// utf8, ucs2, ascii, latin1, utf16le
const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1',
'binary', 'utf16le', 'utf-16le'];
encodings
.reduce((es, e) => es.concat(e, e.toUpperCase()), [])
.forEach((encoding) => {
const buf = Buffer.alloc(9);
const len = Buffer.byteLength('foo', encoding);
assert.strictEqual(buf.write('foo', 0, len, encoding), len);
if (encoding.includes('-'))
encoding = encoding.replace('-', '');
assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase()));
});
// base64
['base64', 'BASE64', 'base64url', 'BASE64URL'].forEach((encoding) => {
const buf = Buffer.alloc(9);
const len = Buffer.byteLength('Zm9v', encoding);
assert.strictEqual(buf.write('Zm9v', 0, len, encoding), len);
assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase()));
});
// hex
['hex', 'HEX'].forEach((encoding) => {
const buf = Buffer.alloc(9);
const len = Buffer.byteLength('666f6f', encoding);
assert.strictEqual(buf.write('666f6f', 0, len, encoding), len);
assert.deepStrictEqual(buf, resultMap.get(encoding.toLowerCase()));
});
// Invalid encodings
for (let i = 1; i < 10; i++) {
const encoding = String(i).repeat(i);
const error = common.expectsError({
code: 'ERR_UNKNOWN_ENCODING',
name: 'TypeError',
message: `Unknown encoding: ${encoding}`
});
assert.ok(!Buffer.isEncoding(encoding));
assert.throws(() => Buffer.alloc(9).write('foo', encoding), error);
}
// UCS-2 overflow CVE-2018-12115
for (let i = 1; i < 4; i++) {
// Allocate two Buffers sequentially off the pool. Run more than once in case
// we hit the end of the pool and don't get sequential allocations
const x = Buffer.allocUnsafe(4).fill(0);
const y = Buffer.allocUnsafe(4).fill(1);
// Should not write anything, pos 3 doesn't have enough room for a 16-bit char
assert.strictEqual(x.write('ыыыыыы', 3, 'ucs2'), 0);
// CVE-2018-12115 experienced via buffer overrun to next block in the pool
assert.strictEqual(Buffer.compare(y, Buffer.alloc(4, 1)), 0);
}
// Should not write any data when there is no space for 16-bit chars
const z = Buffer.alloc(4, 0);
assert.strictEqual(z.write('\u0001', 3, 'ucs2'), 0);
assert.strictEqual(Buffer.compare(z, Buffer.alloc(4, 0)), 0);
// Make sure longer strings are written up to the buffer end.
assert.strictEqual(z.write('abcd', 2), 2);
assert.deepStrictEqual([...z], [0, 0, 0x61, 0x62]);
// Large overrun could corrupt the process
assert.strictEqual(Buffer.alloc(4)
.write('ыыыыыы'.repeat(100), 3, 'utf16le'), 0);
{
// .write() does not affect the byte after the written-to slice of the Buffer.
// Refs: https://github.com/nodejs/node/issues/26422
const buf = Buffer.alloc(8);
assert.strictEqual(buf.write('ыы', 1, 'utf16le'), 4);
assert.deepStrictEqual([...buf], [0, 0x4b, 0x04, 0x4b, 0x04, 0, 0, 0]);
}

View File

@@ -0,0 +1,133 @@
'use strict';
// Tests to verify doubles are correctly written
require('../common');
const assert = require('assert');
const buffer = Buffer.allocUnsafe(16);
buffer.writeDoubleBE(2.225073858507201e-308, 0);
buffer.writeDoubleLE(2.225073858507201e-308, 8);
assert.ok(buffer.equals(new Uint8Array([
0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00,
])));
buffer.writeDoubleBE(1.0000000000000004, 0);
buffer.writeDoubleLE(1.0000000000000004, 8);
assert.ok(buffer.equals(new Uint8Array([
0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f,
])));
buffer.writeDoubleBE(-2, 0);
buffer.writeDoubleLE(-2, 8);
assert.ok(buffer.equals(new Uint8Array([
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
])));
buffer.writeDoubleBE(1.7976931348623157e+308, 0);
buffer.writeDoubleLE(1.7976931348623157e+308, 8);
assert.ok(buffer.equals(new Uint8Array([
0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f,
])));
buffer.writeDoubleBE(0 * -1, 0);
buffer.writeDoubleLE(0 * -1, 8);
assert.ok(buffer.equals(new Uint8Array([
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
])));
buffer.writeDoubleBE(Infinity, 0);
buffer.writeDoubleLE(Infinity, 8);
assert.ok(buffer.equals(new Uint8Array([
0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F,
])));
assert.strictEqual(buffer.readDoubleBE(0), Infinity);
assert.strictEqual(buffer.readDoubleLE(8), Infinity);
buffer.writeDoubleBE(-Infinity, 0);
buffer.writeDoubleLE(-Infinity, 8);
assert.ok(buffer.equals(new Uint8Array([
0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF,
])));
assert.strictEqual(buffer.readDoubleBE(0), -Infinity);
assert.strictEqual(buffer.readDoubleLE(8), -Infinity);
buffer.writeDoubleBE(NaN, 0);
buffer.writeDoubleLE(NaN, 8);
// JS only knows a single NaN but there exist two platform specific
// implementations. Therefore, allow both quiet and signalling NaNs.
if (buffer[1] === 0xF7) {
assert.ok(buffer.equals(new Uint8Array([
0x7F, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7F,
])));
} else {
assert.ok(buffer.equals(new Uint8Array([
0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F,
])));
}
assert.ok(Number.isNaN(buffer.readDoubleBE(0)));
assert.ok(Number.isNaN(buffer.readDoubleLE(8)));
// OOB in writeDouble{LE,BE} should throw.
{
const small = Buffer.allocUnsafe(1);
['writeDoubleLE', 'writeDoubleBE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[fn](23, undefined);
buffer[fn](23);
assert.throws(
() => small[fn](11.11, 0),
{
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: 'Attempt to access memory outside buffer bounds'
});
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => small[fn](23, off),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[Infinity, -1, 9].forEach((offset) => {
assert.throws(
() => buffer[fn](23, offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= 8. Received ${offset}`
});
});
[NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[fn](42, offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}

View File

@@ -0,0 +1,117 @@
'use strict';
// Tests to verify floats are correctly written
require('../common');
const assert = require('assert');
const buffer = Buffer.allocUnsafe(8);
buffer.writeFloatBE(1, 0);
buffer.writeFloatLE(1, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f ])));
buffer.writeFloatBE(1 / 3, 0);
buffer.writeFloatLE(1 / 3, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0x3e, 0xaa, 0xaa, 0xab, 0xab, 0xaa, 0xaa, 0x3e ])));
buffer.writeFloatBE(3.4028234663852886e+38, 0);
buffer.writeFloatLE(3.4028234663852886e+38, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f ])));
buffer.writeFloatLE(1.1754943508222875e-38, 0);
buffer.writeFloatBE(1.1754943508222875e-38, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00 ])));
buffer.writeFloatBE(0 * -1, 0);
buffer.writeFloatLE(0 * -1, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 ])));
buffer.writeFloatBE(Infinity, 0);
buffer.writeFloatLE(Infinity, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F ])));
assert.strictEqual(buffer.readFloatBE(0), Infinity);
assert.strictEqual(buffer.readFloatLE(4), Infinity);
buffer.writeFloatBE(-Infinity, 0);
buffer.writeFloatLE(-Infinity, 4);
assert.ok(buffer.equals(
new Uint8Array([ 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF ])));
assert.strictEqual(buffer.readFloatBE(0), -Infinity);
assert.strictEqual(buffer.readFloatLE(4), -Infinity);
buffer.writeFloatBE(NaN, 0);
buffer.writeFloatLE(NaN, 4);
// JS only knows a single NaN but there exist two platform specific
// implementations. Therefore, allow both quiet and signalling NaNs.
if (buffer[1] === 0xBF) {
assert.ok(
buffer.equals(new Uint8Array(
[ 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x7F ])));
} else {
assert.ok(
buffer.equals(new Uint8Array(
[ 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x7F ])));
}
assert.ok(Number.isNaN(buffer.readFloatBE(0)));
assert.ok(Number.isNaN(buffer.readFloatLE(4)));
// OOB in writeFloat{LE,BE} should throw.
{
const small = Buffer.allocUnsafe(1);
['writeFloatLE', 'writeFloatBE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[fn](23, undefined);
buffer[fn](23);
assert.throws(
() => small[fn](11.11, 0),
{
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: 'Attempt to access memory outside buffer bounds'
});
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => small[fn](23, off),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
});
[Infinity, -1, 5].forEach((offset) => {
assert.throws(
() => buffer[fn](23, offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= 4. Received ${offset}`
}
);
});
[NaN, 1.01].forEach((offset) => {
assert.throws(
() => buffer[fn](42, offset),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}

View File

@@ -0,0 +1,268 @@
'use strict';
// Tests to verify signed integers are correctly written
require('../common');
const assert = require('assert');
const errorOutOfBounds = {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: new RegExp('^The value of "value" is out of range\\. ' +
'It must be >= -\\d+ and <= \\d+\\. Received .+$')
};
// Test 8 bit
{
const buffer = Buffer.alloc(2);
buffer.writeInt8(0x23, 0);
buffer.writeInt8(-5, 1);
assert.ok(buffer.equals(new Uint8Array([ 0x23, 0xfb ])));
/* Make sure we handle min/max correctly */
buffer.writeInt8(0x7f, 0);
buffer.writeInt8(-0x80, 1);
assert.ok(buffer.equals(new Uint8Array([ 0x7f, 0x80 ])));
assert.throws(() => {
buffer.writeInt8(0x7f + 1, 0);
}, errorOutOfBounds);
assert.throws(() => {
buffer.writeInt8(-0x80 - 1, 0);
}, errorOutOfBounds);
// Verify that default offset works fine.
buffer.writeInt8(23, undefined);
buffer.writeInt8(23);
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => buffer.writeInt8(23, off),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[NaN, Infinity, -1, 1.01].forEach((off) => {
assert.throws(
() => buffer.writeInt8(23, off),
{ code: 'ERR_OUT_OF_RANGE' });
});
}
// Test 16 bit
{
const buffer = Buffer.alloc(4);
buffer.writeInt16BE(0x0023, 0);
buffer.writeInt16LE(0x0023, 2);
assert.ok(buffer.equals(new Uint8Array([ 0x00, 0x23, 0x23, 0x00 ])));
buffer.writeInt16BE(-5, 0);
buffer.writeInt16LE(-5, 2);
assert.ok(buffer.equals(new Uint8Array([ 0xff, 0xfb, 0xfb, 0xff ])));
buffer.writeInt16BE(-1679, 0);
buffer.writeInt16LE(-1679, 2);
assert.ok(buffer.equals(new Uint8Array([ 0xf9, 0x71, 0x71, 0xf9 ])));
/* Make sure we handle min/max correctly */
buffer.writeInt16BE(0x7fff, 0);
buffer.writeInt16BE(-0x8000, 2);
assert.ok(buffer.equals(new Uint8Array([ 0x7f, 0xff, 0x80, 0x00 ])));
buffer.writeInt16LE(0x7fff, 0);
buffer.writeInt16LE(-0x8000, 2);
assert.ok(buffer.equals(new Uint8Array([ 0xff, 0x7f, 0x00, 0x80 ])));
['writeInt16BE', 'writeInt16LE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[fn](23, undefined);
buffer[fn](23);
assert.throws(() => {
buffer[fn](0x7fff + 1, 0);
}, errorOutOfBounds);
assert.throws(() => {
buffer[fn](-0x8000 - 1, 0);
}, errorOutOfBounds);
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => buffer[fn](23, off),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[NaN, Infinity, -1, 1.01].forEach((off) => {
assert.throws(
() => buffer[fn](23, off),
{ code: 'ERR_OUT_OF_RANGE' });
});
});
}
// Test 32 bit
{
const buffer = Buffer.alloc(8);
buffer.writeInt32BE(0x23, 0);
buffer.writeInt32LE(0x23, 4);
assert.ok(buffer.equals(new Uint8Array([
0x00, 0x00, 0x00, 0x23, 0x23, 0x00, 0x00, 0x00,
])));
buffer.writeInt32BE(-5, 0);
buffer.writeInt32LE(-5, 4);
assert.ok(buffer.equals(new Uint8Array([
0xff, 0xff, 0xff, 0xfb, 0xfb, 0xff, 0xff, 0xff,
])));
buffer.writeInt32BE(-805306713, 0);
buffer.writeInt32LE(-805306713, 4);
assert.ok(buffer.equals(new Uint8Array([
0xcf, 0xff, 0xfe, 0xa7, 0xa7, 0xfe, 0xff, 0xcf,
])));
/* Make sure we handle min/max correctly */
buffer.writeInt32BE(0x7fffffff, 0);
buffer.writeInt32BE(-0x80000000, 4);
assert.ok(buffer.equals(new Uint8Array([
0x7f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
])));
buffer.writeInt32LE(0x7fffffff, 0);
buffer.writeInt32LE(-0x80000000, 4);
assert.ok(buffer.equals(new Uint8Array([
0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x80,
])));
['writeInt32BE', 'writeInt32LE'].forEach((fn) => {
// Verify that default offset works fine.
buffer[fn](23, undefined);
buffer[fn](23);
assert.throws(() => {
buffer[fn](0x7fffffff + 1, 0);
}, errorOutOfBounds);
assert.throws(() => {
buffer[fn](-0x80000000 - 1, 0);
}, errorOutOfBounds);
['', '0', null, {}, [], () => {}, true, false].forEach((off) => {
assert.throws(
() => buffer[fn](23, off),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[NaN, Infinity, -1, 1.01].forEach((off) => {
assert.throws(
() => buffer[fn](23, off),
{ code: 'ERR_OUT_OF_RANGE' });
});
});
}
// Test 48 bit
{
const value = 0x1234567890ab;
const buffer = Buffer.allocUnsafe(6);
buffer.writeIntBE(value, 0, 6);
assert.ok(buffer.equals(new Uint8Array([
0x12, 0x34, 0x56, 0x78, 0x90, 0xab,
])));
buffer.writeIntLE(value, 0, 6);
assert.ok(buffer.equals(new Uint8Array([
0xab, 0x90, 0x78, 0x56, 0x34, 0x12,
])));
}
// Test Int
{
const data = Buffer.alloc(8);
// Check byteLength.
['writeIntBE', 'writeIntLE'].forEach((fn) => {
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((bl) => {
assert.throws(
() => data[fn](23, 0, bl),
{ code: 'ERR_INVALID_ARG_TYPE' });
});
[Infinity, -1].forEach((byteLength) => {
assert.throws(
() => data[fn](23, 0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "byteLength" is out of range. ' +
`It must be >= 1 and <= 6. Received ${byteLength}`
}
);
});
[NaN, 1.01].forEach((byteLength) => {
assert.throws(
() => data[fn](42, 0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "byteLength" is out of range. ' +
`It must be an integer. Received ${byteLength}`
});
});
});
// Test 1 to 6 bytes.
for (let i = 1; i <= 6; i++) {
['writeIntBE', 'writeIntLE'].forEach((fn) => {
const min = -(2 ** (i * 8 - 1));
const max = 2 ** (i * 8 - 1) - 1;
let range = `>= ${min} and <= ${max}`;
if (i > 4) {
range = `>= -(2 ** ${i * 8 - 1}) and < 2 ** ${i * 8 - 1}`;
}
[min - 1, max + 1].forEach((val) => {
const received = String(val);
assert.throws(() => {
data[fn](val, 0, i);
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "value" is out of range. ' +
`It must be ${range}. Received ${received}`
});
});
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => {
assert.throws(
() => data[fn](min, o, i),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});
[Infinity, -1, -4294967295].forEach((offset) => {
assert.throws(
() => data[fn](min, offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= ${8 - i}. Received ${offset}`
});
});
[NaN, 1.01].forEach((offset) => {
assert.throws(
() => data[fn](max, offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
}
}

View File

@@ -0,0 +1,234 @@
'use strict';
require('../common');
const assert = require('assert');
const common = require('../common');
// We need to check the following things:
// - We are correctly resolving big endian (doesn't mean anything for 8 bit)
// - Correctly resolving little endian (doesn't mean anything for 8 bit)
// - Correctly using the offsets
// - Correctly interpreting values that are beyond the signed range as unsigned
{ // OOB
const data = Buffer.alloc(8);
['UInt8', 'UInt16BE', 'UInt16LE', 'UInt32BE', 'UInt32LE'].forEach((fn) => {
// Verify that default offset works fine.
data[`write${fn}`](23, undefined);
data[`write${fn}`](23);
['', '0', null, {}, [], () => {}, true, false].forEach((o) => {
assert.throws(
() => data[`write${fn}`](23, o),
{
code: 'ERR_INVALID_ARG_TYPE',
message: `The "offset" argument must be of type number.${common.invalidArgTypeHelper(o)}`,
});
});
[NaN, Infinity, -1, 1.01].forEach((o) => {
assert.throws(
() => data[`write${fn}`](23, o),
{ code: 'ERR_OUT_OF_RANGE' });
});
});
}
{ // Test 8 bit
const data = Buffer.alloc(4);
data.writeUInt8(23, 0);
data.writeUInt8(23, 1);
data.writeUInt8(23, 2);
data.writeUInt8(23, 3);
assert.ok(data.equals(new Uint8Array([23, 23, 23, 23])));
data.writeUInt8(23, 0);
data.writeUInt8(23, 1);
data.writeUInt8(23, 2);
data.writeUInt8(23, 3);
assert.ok(data.equals(new Uint8Array([23, 23, 23, 23])));
data.writeUInt8(255, 0);
assert.strictEqual(data[0], 255);
data.writeUInt8(255, 0);
assert.strictEqual(data[0], 255);
}
// Test 16 bit
{
let value = 0x2343;
const data = Buffer.alloc(4);
data.writeUInt16BE(value, 0);
assert.ok(data.equals(new Uint8Array([0x23, 0x43, 0, 0])));
data.writeUInt16BE(value, 1);
assert.ok(data.equals(new Uint8Array([0x23, 0x23, 0x43, 0])));
data.writeUInt16BE(value, 2);
assert.ok(data.equals(new Uint8Array([0x23, 0x23, 0x23, 0x43])));
data.writeUInt16LE(value, 0);
assert.ok(data.equals(new Uint8Array([0x43, 0x23, 0x23, 0x43])));
data.writeUInt16LE(value, 1);
assert.ok(data.equals(new Uint8Array([0x43, 0x43, 0x23, 0x43])));
data.writeUInt16LE(value, 2);
assert.ok(data.equals(new Uint8Array([0x43, 0x43, 0x43, 0x23])));
value = 0xff80;
data.writeUInt16LE(value, 0);
assert.ok(data.equals(new Uint8Array([0x80, 0xff, 0x43, 0x23])));
data.writeUInt16BE(value, 0);
assert.ok(data.equals(new Uint8Array([0xff, 0x80, 0x43, 0x23])));
value = 0xfffff;
['writeUInt16BE', 'writeUInt16LE'].forEach((fn) => {
assert.throws(
() => data[fn](value, 0),
{
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "value" is out of range. ' +
`It must be >= 0 and <= 65535. Received ${value}`
}
);
});
}
// Test 32 bit
{
const data = Buffer.alloc(6);
const value = 0xe7f90a6d;
data.writeUInt32BE(value, 0);
assert.ok(data.equals(new Uint8Array([0xe7, 0xf9, 0x0a, 0x6d, 0, 0])));
data.writeUInt32BE(value, 1);
assert.ok(data.equals(new Uint8Array([0xe7, 0xe7, 0xf9, 0x0a, 0x6d, 0])));
data.writeUInt32BE(value, 2);
assert.ok(data.equals(new Uint8Array([0xe7, 0xe7, 0xe7, 0xf9, 0x0a, 0x6d])));
data.writeUInt32LE(value, 0);
assert.ok(data.equals(new Uint8Array([0x6d, 0x0a, 0xf9, 0xe7, 0x0a, 0x6d])));
data.writeUInt32LE(value, 1);
assert.ok(data.equals(new Uint8Array([0x6d, 0x6d, 0x0a, 0xf9, 0xe7, 0x6d])));
data.writeUInt32LE(value, 2);
assert.ok(data.equals(new Uint8Array([0x6d, 0x6d, 0x6d, 0x0a, 0xf9, 0xe7])));
}
// Test 48 bit
{
const value = 0x1234567890ab;
const data = Buffer.allocUnsafe(6);
data.writeUIntBE(value, 0, 6);
assert.ok(data.equals(new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90, 0xab])));
data.writeUIntLE(value, 0, 6);
assert.ok(data.equals(new Uint8Array([0xab, 0x90, 0x78, 0x56, 0x34, 0x12])));
}
// Test UInt
{
const data = Buffer.alloc(8);
let val = 0x100;
// Check byteLength.
['writeUIntBE', 'writeUIntLE'].forEach((fn) => {
['', '0', null, {}, [], () => {}, true, false, undefined].forEach((bl) => {
assert.throws(
() => data[fn](23, 0, bl),
{
code: 'ERR_INVALID_ARG_TYPE',
});
});
[Infinity, -1].forEach((byteLength) => {
assert.throws(
() => data[fn](23, 0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "byteLength" is out of range. ' +
`It must be >= 1 and <= 6. Received ${byteLength}`
}
);
});
[NaN, 1.01].forEach((byteLength) => {
assert.throws(
() => data[fn](42, 0, byteLength),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "byteLength" is out of range. ' +
`It must be an integer. Received ${byteLength}`
});
});
});
// Test 1 to 6 bytes.
for (let i = 1; i <= 6; i++) {
const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`;
const received = String(val);
['writeUIntBE', 'writeUIntLE'].forEach((fn) => {
assert.throws(() => {
data[fn](val, 0, i);
}, {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "value" is out of range. ' +
`It must be >= 0 and <${range}. Received ${received}`
});
['', '0', null, {}, [], () => {}, true, false].forEach((o) => {
assert.throws(
() => data[fn](23, o, i),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
});
[Infinity, -1, -4294967295].forEach((offset) => {
assert.throws(
() => data[fn](val - 1, offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be >= 0 and <= ${8 - i}. Received ${offset}`
});
});
[NaN, 1.01].forEach((offset) => {
assert.throws(
() => data[fn](val - 1, offset, i),
{
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: 'The value of "offset" is out of range. ' +
`It must be an integer. Received ${offset}`
});
});
});
val *= 0x100;
}
}
for (const fn of [
'UInt8', 'UInt16LE', 'UInt16BE', 'UInt32LE', 'UInt32BE', 'UIntLE', 'UIntBE',
'BigUInt64LE', 'BigUInt64BE',
]) {
const p = Buffer.prototype;
const lowerFn = fn.replace(/UInt/, 'Uint');
assert.strictEqual(p[`write${fn}`], p[`write${lowerFn}`]);
assert.strictEqual(p[`read${fn}`], p[`read${lowerFn}`]);
}

View File

@@ -0,0 +1,32 @@
'use strict';
// Flags: --zero-fill-buffers
// when using --zero-fill-buffers, every Buffer and SlowBuffer
// instance must be zero filled upon creation
require('../common');
const SlowBuffer = require('buffer').SlowBuffer;
const assert = require('assert');
function isZeroFilled(buf) {
for (const n of buf)
if (n > 0) return false;
return true;
}
// This can be somewhat unreliable because the
// allocated memory might just already happen to
// contain all zeroes. The test is run multiple
// times to improve the reliability.
for (let i = 0; i < 50; i++) {
const bufs = [
Buffer.alloc(20),
Buffer(20),
Buffer.allocUnsafe(20),
SlowBuffer(20),
new SlowBuffer(20),
];
for (const buf of bufs) {
assert(isZeroFilled(buf));
}
}

View File

@@ -22,7 +22,7 @@ const createValidate = (line, args = []) => common.mustCall((e) => {
const rest = msg.substring(firstColon + 1);
assert.strictEqual(path.basename(fileName), 'test-common-must-not-call.js');
const argsInfo = args.length > 0 ?
`\ncalled with arguments: ${args.map(util.inspect).join(', ')}` : '';
`\ncalled with arguments: ${args.map(v => Bun.inspect(v)).join(', ')}` : '';
assert.strictEqual(rest, line + argsInfo);
});