diff options
author | 2022-08-20 15:17:17 +0800 | |
---|---|---|
committer | 2022-08-20 00:17:17 -0700 | |
commit | 2641884342f4757867565c30c224c9a5a6b9e2d3 (patch) | |
tree | 63b78e7fcd5e80dded052c87b39b561e554f4aa1 /src | |
parent | b1bc549cf71f58d480aeca6804cadcbc1b67d662 (diff) | |
download | bun-2641884342f4757867565c30c224c9a5a6b9e2d3.tar.gz bun-2641884342f4757867565c30c224c9a5a6b9e2d3.tar.zst bun-2641884342f4757867565c30c224c9a5a6b9e2d3.zip |
Add buffer.indexOf, includes and lastIndexOf (#1112)
* Add buffer.indexOf, includes and lastIndexOf
* use memmem
* use int64_t
* fix upon reviews
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/bindings/JSBuffer.cpp | 191 |
1 files changed, 159 insertions, 32 deletions
diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index a6f321b21..87ce722f2 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -231,33 +231,11 @@ static inline EncodedJSValue constructBufferFromLength(JSGlobalObject* lexicalGl return jsBufferConstructorFunction_allocUnsafeBody(lexicalGlobalObject, callFrame); } -static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) +static EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalObject, JSString* str, WebCore::BufferEncodingType encoding) { auto& vm = JSC::getVM(lexicalGlobalObject); - uint32_t offset = 0; - uint32_t length = castedThis->length(); - WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; - auto scope = DECLARE_THROW_SCOPE(vm); - EnsureStillAliveScope arg0 = callFrame->argument(0); - auto* str = arg0.value().toString(lexicalGlobalObject); - - EnsureStillAliveScope arg1 = callFrame->argument(1); - - if (str->length() == 0) - return constructBufferEmpty(lexicalGlobalObject, callFrame); - - if (callFrame->argumentCount() > 1) { - std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, callFrame->argument(1)); - if (!encoded) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return JSC::JSValue::encode(jsUndefined()); - } - - encoding = encoded.value(); - } - auto view = str->tryGetValue(lexicalGlobalObject); JSC::EncodedJSValue result; @@ -339,6 +317,37 @@ static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGl scope.throwException(lexicalGlobalObject, decoded); return JSC::JSValue::encode(jsUndefined()); } + return result; +} + +static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + uint32_t offset = 0; + uint32_t length = castedThis->length(); + WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; + + auto scope = DECLARE_THROW_SCOPE(vm); + + EnsureStillAliveScope arg0 = callFrame->argument(0); + auto* str = arg0.value().toString(lexicalGlobalObject); + + EnsureStillAliveScope arg1 = callFrame->argument(1); + + if (str->length() == 0) + return constructBufferEmpty(lexicalGlobalObject, callFrame); + + if (callFrame->argumentCount() > 1) { + std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, callFrame->argument(1)); + if (!encoded) { + throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + encoding = encoded.value(); + } + + JSC::EncodedJSValue result = constructFromEncoding(lexicalGlobalObject, str, encoding); RELEASE_AND_RETURN(scope, result); } @@ -982,20 +991,138 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob } } -static inline JSC::EncodedJSValue jsBufferPrototypeFunction_includesBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) +static int64_t indexOf(const uint8_t* thisPtr, int64_t thisLength, const uint8_t* valuePtr, int64_t valueLength, int64_t byteOffset) +{ + if (thisLength < valueLength + byteOffset) + return -1; + auto start = thisPtr + byteOffset; + auto it = static_cast<uint8_t*>(memmem(start, static_cast<size_t>(thisLength - byteOffset), valuePtr, static_cast<size_t>(valueLength))); + if (it != NULL) { + return it - thisPtr; + } + return -1; +} + +static int64_t lastIndexOf(const uint8_t* thisPtr, int64_t thisLength, const uint8_t* valuePtr, int64_t valueLength, int64_t byteOffset) +{ + auto start = thisPtr; + auto end = thisPtr + std::min(thisLength, byteOffset + valueLength); + auto it = std::find_end(start, end, valuePtr, valuePtr + valueLength); + if (it != end) { + return it - thisPtr; + } + return -1; +} + +static int64_t indexOf(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis, bool last) { auto& vm = JSC::getVM(lexicalGlobalObject); - return JSC::JSValue::encode(jsUndefined()); + auto scope = DECLARE_THROW_SCOPE(vm); + if (callFrame->argumentCount() < 1) { + throwVMError(lexicalGlobalObject, scope, createNotEnoughArgumentsError(lexicalGlobalObject)); + return JSValue::encode(jsUndefined()); + } + + 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) { + auto byteOffset_ = callFrame->uncheckedArgument(1).toNumber(lexicalGlobalObject); + 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) { + std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, callFrame->uncheckedArgument(2)); + if (!encoded) { + throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + encoding = encoded.value(); + } + } + + if (value.isString()) { + auto* str = value.toString(lexicalGlobalObject); + JSC::EncodedJSValue encodedBuffer = constructFromEncoding(lexicalGlobalObject, str, encoding); + 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); + } else { + return indexOf(typedVector, length, typedVectorValue, lengthValue, byteOffset); + } + } else if (value.isNumber()) { + uint8_t byteValue = static_cast<uint8_t>(value.toNumber(lexicalGlobalObject)); + if (last) { + for (int64_t i = byteOffset; i >= 0; --i) { + if (byteValue == typedVector[i]) { + return i; + } + } + } else { + for (int64_t i = byteOffset; i < length; ++i) { + if (byteValue == typedVector[i]) { + return i; + } + } + } + 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; + } + + return -1; +} + +static inline JSC::EncodedJSValue jsBufferPrototypeFunction_includesBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) +{ + 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<JSBuffer>::ClassParameter castedThis) { - auto& vm = JSC::getVM(lexicalGlobalObject); - return JSC::JSValue::encode(jsUndefined()); + 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<JSBuffer>::ClassParameter castedThis) { - auto& vm = JSC::getVM(lexicalGlobalObject); - return JSC::JSValue::encode(jsUndefined()); + 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<JSBuffer>::ClassParameter castedThis) { @@ -1434,9 +1561,9 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "fill"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_fill), (intptr_t)(4) } }, { "hexSlice"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Builtin), NoIntrinsic, { (intptr_t) static_cast<BuiltinGenerator>(jsBufferPrototypeHexSliceCodeGenerator), (intptr_t)(2) } }, { "hexWrite"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Builtin), NoIntrinsic, { (intptr_t) static_cast<BuiltinGenerator>(jsBufferPrototypeHexWriteCodeGenerator), (intptr_t)(1) } }, - // { "includes"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_includes), (intptr_t)(3) } }, - // { "indexOf"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_indexOf), (intptr_t)(3) } }, - // { "lastIndexOf"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_lastIndexOf), (intptr_t)(3) } }, + { "includes"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_includes), (intptr_t)(3) } }, + { "indexOf"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_indexOf), (intptr_t)(3) } }, + { "lastIndexOf"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { (intptr_t) static_cast<RawNativeFunction>(jsBufferPrototypeFunction_lastIndexOf), (intptr_t)(3) } }, { "latin1Slice"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Builtin), NoIntrinsic, { (intptr_t) static_cast<BuiltinGenerator>(jsBufferPrototypeLatin1SliceCodeGenerator), (intptr_t)(2) } }, { "latin1Write"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Builtin), NoIntrinsic, { (intptr_t) static_cast<BuiltinGenerator>(jsBufferPrototypeLatin1WriteCodeGenerator), (intptr_t)(1) } }, { "readBigInt64"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Builtin), NoIntrinsic, { (intptr_t) static_cast<BuiltinGenerator>(jsBufferPrototypeReadBigInt64LECodeGenerator), (intptr_t)(1) } }, |