aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Zilin Zhu <zhuzilinallen@gmail.com> 2022-09-02 03:56:41 +0800
committerGravatar GitHub <noreply@github.com> 2022-09-01 12:56:41 -0700
commit700c31dd131bd839c2cf28d6b34915fa111cead4 (patch)
tree9e2efce1a4efeefe62abbf7514e8d125a60ef797 /src
parentf023b89b732db0aff24445acbbe39c366d13118d (diff)
downloadbun-700c31dd131bd839c2cf28d6b34915fa111cead4.tar.gz
bun-700c31dd131bd839c2cf28d6b34915fa111cead4.tar.zst
bun-700c31dd131bd839c2cf28d6b34915fa111cead4.zip
Add native StringDecoder (#1188)
* Add native StringDecoder * fix upon reviews * add Constructor and use LazyClassStructure
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/JSBufferList.cpp2
-rw-r--r--src/bun.js/bindings/JSStringDecoder.cpp426
-rw-r--r--src/bun.js/bindings/JSStringDecoder.h129
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp36
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h5
-rw-r--r--src/bun.js/bindings/exports.zig1
-rw-r--r--src/bun.js/bindings/headers-handwritten.h1
-rw-r--r--src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h2
-rw-r--r--src/bun.js/bindings/webcore/DOMIsoSubspaces.h2
-rw-r--r--src/bun.js/javascript.zig15
-rw-r--r--src/bun.js/modules/StringDecoderModule.h15
-rw-r--r--src/bun.js/webcore/encoding.zig15
12 files changed, 639 insertions, 10 deletions
diff --git a/src/bun.js/bindings/JSBufferList.cpp b/src/bun.js/bindings/JSBufferList.cpp
index cccb827a9..50e0defe0 100644
--- a/src/bun.js/bindings/JSBufferList.cpp
+++ b/src/bun.js/bindings/JSBufferList.cpp
@@ -169,7 +169,7 @@ JSC::JSValue JSBufferList::_getBuffer(JSC::VM& vm, JSC::JSGlobalObject* lexicalG
RELEASE_AND_RETURN(throwScope, uint8Array);
}
-const JSC::ClassInfo JSBufferList::s_info = { "JSBufferList"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBufferList) };
+const JSC::ClassInfo JSBufferList::s_info = { "BufferList"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBufferList) };
JSC::GCClient::IsoSubspace* JSBufferList::subspaceForImpl(JSC::VM& vm)
{
diff --git a/src/bun.js/bindings/JSStringDecoder.cpp b/src/bun.js/bindings/JSStringDecoder.cpp
new file mode 100644
index 000000000..d9af6e49e
--- /dev/null
+++ b/src/bun.js/bindings/JSStringDecoder.cpp
@@ -0,0 +1,426 @@
+#include "JSStringDecoder.h"
+#include "JSBuffer.h"
+#include "JavaScriptCore/Lookup.h"
+#include "JavaScriptCore/ObjectConstructor.h"
+#include "ZigGlobalObject.h"
+#include "JSDOMOperation.h"
+#include "JSDOMAttribute.h"
+#include "headers.h"
+#include "JSDOMConvertEnumeration.h"
+
+namespace WebCore {
+
+using namespace JSC;
+
+static JSC_DECLARE_HOST_FUNCTION(jsStringDecoderPrototypeFunction_write);
+static JSC_DECLARE_HOST_FUNCTION(jsStringDecoderPrototypeFunction_end);
+static JSC_DECLARE_HOST_FUNCTION(jsStringDecoderPrototypeFunction_text);
+
+static JSC_DECLARE_CUSTOM_GETTER(jsStringDecoder_lastChar);
+static JSC_DECLARE_CUSTOM_GETTER(jsStringDecoder_lastNeed);
+static JSC_DECLARE_CUSTOM_GETTER(jsStringDecoder_lastTotal);
+
+void JSStringDecoder::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+}
+
+JSC::JSValue JSStringDecoder::fillLast(JSC::VM& vm, JSC::JSGlobalObject* globalObject, uint8_t* bufPtr, uint32_t length)
+{
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+ if (m_encoding == BufferEncodingType::utf8) {
+ // utf8CheckExtraBytes
+ if ((bufPtr[0] & 0xC0) != 0x80) {
+ m_lastNeed = 0;
+ RELEASE_AND_RETURN(throwScope, JSC::jsString(vm, WTF::String(u"\uFFFD", 1)));
+ }
+ if (m_lastNeed > 1 && length > 1) {
+ if ((bufPtr[1] & 0xC0) != 0x80) {
+ m_lastNeed = 1;
+ RELEASE_AND_RETURN(throwScope, JSC::jsString(vm, WTF::String(u"\uFFFD", 1)));
+ }
+ if (m_lastNeed > 2 && length > 2) {
+ if ((bufPtr[2] & 0xC0) != 0x80) {
+ m_lastNeed = 2;
+ RELEASE_AND_RETURN(throwScope, JSC::jsString(vm, WTF::String(u"\uFFFD", 1)));
+ }
+ }
+ }
+ }
+
+ if (m_lastNeed <= length) {
+ memmove(m_lastChar + m_lastTotal - m_lastNeed, bufPtr, m_lastNeed);
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(m_lastChar, m_lastTotal, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ memmove(m_lastChar + m_lastTotal - m_lastNeed, bufPtr, length);
+ m_lastNeed -= length;
+ RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm));
+}
+
+// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a
+// continuation byte. If an invalid byte is detected, -2 is returned.
+int8_t utf8CheckByte(uint8_t byte) {
+ if (byte <= 0x7F) return 0;
+ else if ((byte >> 5) == 0x06) return 2;
+ else if ((byte >> 4) == 0x0E) return 3;
+ else if ((byte >> 3) == 0x1E) return 4;
+ return (byte >> 6) == 0x02 ? -1 : -2;
+}
+
+// Checks at most 3 bytes at the end of a Buffer in order to detect an
+// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4)
+// needed to complete the UTF-8 character (if applicable) are returned.
+uint8_t JSStringDecoder::utf8CheckIncomplete(uint8_t* bufPtr, uint32_t length, uint32_t i)
+{
+ uint32_t j = length - 1;
+ if (j < i) return 0;
+ int8_t nb = utf8CheckByte(bufPtr[j]);
+ if (nb >= 0) {
+ if (nb > 0) m_lastNeed = nb - 1;
+ return nb;
+ }
+ if (--j < i || nb == -2) return 0;
+ nb = utf8CheckByte(bufPtr[j]);
+ if (nb >= 0) {
+ if (nb > 0) m_lastNeed = nb - 2;
+ return nb;
+ }
+ if (--j < i || nb == -2) return 0;
+ nb = utf8CheckByte(bufPtr[j]);
+ if (nb >= 0) {
+ if (nb > 0) {
+ if (nb == 2) nb = 0;else m_lastNeed = nb - 3;
+ }
+ return nb;
+ }
+ return 0;
+}
+
+// This is not the exposed text
+JSC::JSValue JSStringDecoder::text(JSC::VM& vm, JSC::JSGlobalObject* globalObject, uint8_t* bufPtr, uint32_t length, uint32_t offset)
+{
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+ switch (m_encoding) {
+ case BufferEncodingType::ucs2:
+ case BufferEncodingType::utf16le: {
+ if (length == offset)
+ RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm));
+ if ((length - offset) % 2 == 0) {
+ UChar c = (static_cast<uint16_t>(bufPtr[length - 1]) << 8) + static_cast<uint16_t>(bufPtr[length - 2]);
+ if (c >= 0xD800 && c <= 0xDBFF) {
+ m_lastNeed = 2;
+ m_lastTotal = 4;
+ m_lastChar[0] = bufPtr[length - 2];
+ m_lastChar[1] = bufPtr[length - 1];
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, length - offset - 2, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, length - offset, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ m_lastNeed = 1;
+ m_lastTotal = 2;
+ m_lastChar[0] = bufPtr[length - 1];
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, length - offset - 1, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ case BufferEncodingType::utf8: {
+ uint32_t total = utf8CheckIncomplete(bufPtr, length, offset);
+ if (!m_lastNeed)
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, length - offset, globalObject, static_cast<uint8_t>(m_encoding))));
+ m_lastTotal = total;
+ uint32_t end = length - (total - m_lastNeed);
+ if (end < length)
+ memmove(m_lastChar, bufPtr + end, std::min(4U, length - end));
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, end - offset, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ case BufferEncodingType::base64: {
+ uint32_t n = (length - offset) % 3;
+ if (n == 0)
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, length - offset, globalObject, static_cast<uint8_t>(m_encoding))));
+ m_lastNeed = 3 - n;
+ m_lastTotal = 3;
+ if (n == 1) {
+ m_lastChar[0] = bufPtr[length - 1];
+ } else {
+ m_lastChar[0] = bufPtr[length - 2];
+ m_lastChar[1] = bufPtr[length - 1];
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr + offset, length - offset - n, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ default: {
+ // should never reach here.
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ }
+ }
+
+}
+
+JSC::JSValue JSStringDecoder::write(JSC::VM& vm, JSC::JSGlobalObject* globalObject, uint8_t* bufPtr, uint32_t length)
+{
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ if (length == 0)
+ RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm));
+
+ switch (m_encoding) {
+ case BufferEncodingType::ucs2:
+ case BufferEncodingType::utf16le:
+ case BufferEncodingType::utf8:
+ case BufferEncodingType::base64: {
+ uint32_t offset = 0;
+ if (m_lastNeed) {
+ JSString* firstHalf = fillLast(vm, globalObject, bufPtr, length).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ if (firstHalf->length() == 0)
+ RELEASE_AND_RETURN(throwScope, firstHalf);
+ offset = m_lastNeed;
+ m_lastNeed = 0;
+
+ JSString* secondHalf = text(vm, globalObject, bufPtr, length, offset).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ if (secondHalf->length() == 0)
+ RELEASE_AND_RETURN(throwScope, firstHalf);
+ RELEASE_AND_RETURN(throwScope, JSC::jsString(globalObject, firstHalf, secondHalf));
+ }
+ JSString* str = text(vm, globalObject, bufPtr, length, offset).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ RELEASE_AND_RETURN(throwScope, str);
+ }
+ default: {
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(bufPtr, length, globalObject, static_cast<uint8_t>(m_encoding))));
+ }
+ }
+}
+
+JSC::JSValue JSStringDecoder::end(JSC::VM& vm, JSC::JSGlobalObject* globalObject, uint8_t* bufPtr, uint32_t length)
+{
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ switch (m_encoding) {
+ case BufferEncodingType::ucs2:
+ case BufferEncodingType::utf16le: {
+ if (length == 0) {
+ if (m_lastNeed) {
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(m_lastChar, m_lastTotal - m_lastNeed, globalObject, static_cast<uint8_t>(m_encoding))));
+ } else {
+ RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm));
+ }
+ }
+ JSString* firstHalf = write(vm, globalObject, bufPtr, length).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ if (m_lastNeed) {
+ JSString* secondHalf = JSC::JSValue::decode(Bun__encoding__toString(m_lastChar, m_lastTotal - m_lastNeed, globalObject, static_cast<uint8_t>(m_encoding))).toString(globalObject);
+ RELEASE_AND_RETURN(throwScope, JSC::jsString(globalObject, firstHalf, secondHalf));
+ } else {
+ RELEASE_AND_RETURN(throwScope, firstHalf);
+ }
+ }
+ case BufferEncodingType::utf8: {
+ if (length == 0) {
+ RELEASE_AND_RETURN(throwScope, m_lastNeed ? JSC::jsString(vm, WTF::String(u"\uFFFD", 1)) : JSC::jsEmptyString(vm));
+ }
+ JSString* firstHalf = write(vm, globalObject, bufPtr, length).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ RELEASE_AND_RETURN(throwScope, m_lastNeed ? JSC::jsString(globalObject, firstHalf, WTF::String(u"\uFFFD", 1)) : firstHalf);
+ }
+ case BufferEncodingType::base64: {
+ if (length == 0) {
+ if (m_lastNeed) {
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::decode(Bun__encoding__toString(m_lastChar, 3 - m_lastNeed, globalObject, static_cast<uint8_t>(m_encoding))));
+ } else {
+ RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm));
+ }
+ }
+ JSString* firstHalf = write(vm, globalObject, bufPtr, length).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ if (m_lastNeed) {
+ JSString* secondHalf = JSC::JSValue::decode(Bun__encoding__toString(m_lastChar, 3 - m_lastNeed, globalObject, static_cast<uint8_t>(m_encoding))).toString(globalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined());
+ RELEASE_AND_RETURN(throwScope, JSC::jsString(globalObject, firstHalf, secondHalf));
+ } else {
+ RELEASE_AND_RETURN(throwScope, firstHalf);
+ }
+ }
+ default: {
+ if (length == 0) {
+ RELEASE_AND_RETURN(throwScope, JSC::jsEmptyString(vm));
+ }
+ RELEASE_AND_RETURN(throwScope, write(vm, globalObject, bufPtr, length));
+ }
+ }
+}
+
+const JSC::ClassInfo JSStringDecoder::s_info = { "StringDecoder"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStringDecoder) };
+
+JSC::GCClient::IsoSubspace* JSStringDecoder::subspaceForImpl(JSC::VM& vm)
+{
+ return WebCore::subspaceForImpl<JSStringDecoder, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForStringDecoder.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForStringDecoder = WTFMove(space); },
+ [](auto& spaces) { return spaces.m_subspaceForStringDecoder.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForStringDecoder = WTFMove(space); });
+}
+
+STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSStringDecoderPrototype, JSStringDecoderPrototype::Base);
+
+static inline JSC::EncodedJSValue jsStringDecoderPrototypeFunction_writeBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSStringDecoder>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ if (callFrame->argumentCount() < 1) {
+ throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ return JSValue::encode(jsUndefined());
+ }
+
+ auto buffer = callFrame->uncheckedArgument(0);
+ JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ if (!view) {
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Uint8Array"_s);
+ return JSValue::encode(jsUndefined());
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(castedThis->write(vm, lexicalGlobalObject, view->typedVector(), view->length())));
+}
+static inline JSC::EncodedJSValue jsStringDecoderPrototypeFunction_endBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSStringDecoder>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ if (callFrame->argumentCount() < 1) {
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(castedThis->end(vm, lexicalGlobalObject, nullptr, 0)));
+ }
+
+ auto buffer = callFrame->uncheckedArgument(0);
+ JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ if (!view) {
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Uint8Array"_s);
+ return JSValue::encode(jsUndefined());
+ }
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(castedThis->end(vm, lexicalGlobalObject, view->typedVector(), view->length())));
+}
+static inline JSC::EncodedJSValue jsStringDecoderPrototypeFunction_textBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSStringDecoder>::ClassParameter castedThis)
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ if (callFrame->argumentCount() < 2) {
+ throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
+ return JSValue::encode(jsUndefined());
+ }
+
+ auto buffer = callFrame->uncheckedArgument(0);
+ JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ if (!view) {
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Uint8Array"_s);
+ return JSValue::encode(jsUndefined());
+ }
+ int32_t offset = callFrame->uncheckedArgument(1).toInt32(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined()));
+ if (offset > view->length())
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsEmptyString(vm)));
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(castedThis->write(vm, lexicalGlobalObject, view->typedVector() + offset, view->length() - offset)));
+}
+
+static JSC_DEFINE_HOST_FUNCTION(jsStringDecoderPrototypeFunction_write,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSStringDecoder>::call<jsStringDecoderPrototypeFunction_writeBody>(*globalObject, *callFrame, "write");
+}
+static JSC_DEFINE_HOST_FUNCTION(jsStringDecoderPrototypeFunction_end,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSStringDecoder>::call<jsStringDecoderPrototypeFunction_endBody>(*globalObject, *callFrame, "end");
+}
+static JSC_DEFINE_HOST_FUNCTION(jsStringDecoderPrototypeFunction_text,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ return IDLOperation<JSStringDecoder>::call<jsStringDecoderPrototypeFunction_textBody>(*globalObject, *callFrame, "text");
+}
+
+static JSC_DEFINE_CUSTOM_GETTER(jsStringDecoder_lastChar, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ JSStringDecoder* thisObject = jsCast<JSStringDecoder*>(JSValue::decode(thisValue));
+ auto buffer = ArrayBuffer::createFromBytes(thisObject->m_lastChar, 4, nullptr);
+ JSC::JSUint8Array* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, lexicalGlobalObject->typedArrayStructure(JSC::TypeUint8), WTFMove(buffer), 0, 4);
+ toBuffer(lexicalGlobalObject, uint8Array);
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
+}
+static JSC_DEFINE_CUSTOM_GETTER(jsStringDecoder_lastNeed, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ JSStringDecoder* thisObject = jsCast<JSStringDecoder*>(JSValue::decode(thisValue));
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(thisObject->m_lastNeed)));
+}
+static JSC_DEFINE_CUSTOM_GETTER(jsStringDecoder_lastTotal, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ auto& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ JSStringDecoder* thisObject = jsCast<JSStringDecoder*>(JSValue::decode(thisValue));
+ RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(thisObject->m_lastTotal)));
+}
+
+/* Hash table for prototype */
+static const HashTableValue JSStringDecoderPrototypeTableValues[]
+ = {
+ { "lastChar"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsStringDecoder_lastChar, 0 } },
+ { "lastNeed"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsStringDecoder_lastNeed, 0 } },
+ { "lastTotal"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsStringDecoder_lastTotal, 0 } },
+ { "write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsStringDecoderPrototypeFunction_write, 1 } },
+ { "end"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsStringDecoderPrototypeFunction_end, 1 } },
+ { "text"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsStringDecoderPrototypeFunction_text, 2 } },
+ };
+
+void JSStringDecoderPrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalThis)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, JSStringDecoder::info(), JSStringDecoderPrototypeTableValues, *this);
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+const ClassInfo JSStringDecoderPrototype::s_info = { "StringDecoder"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStringDecoderPrototype) };
+
+void JSStringDecoderConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype)
+{
+ Base::finishCreation(vm, 0, "StringDecoder"_s, PropertyAdditionMode::WithoutStructureTransition);
+ putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ ASSERT(inherits(info()));
+}
+
+JSStringDecoderConstructor* JSStringDecoderConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSStringDecoderPrototype* prototype) {
+ JSStringDecoderConstructor* ptr = new (NotNull, JSC::allocateCell<JSStringDecoderConstructor>(vm)) JSStringDecoderConstructor(vm, structure, construct);
+ ptr->finishCreation(vm, globalObject, prototype);
+ return ptr;
+}
+
+JSC::EncodedJSValue JSStringDecoderConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
+{
+ JSC::VM& vm = lexicalGlobalObject->vm();
+ auto encoding = BufferEncodingType::utf8;
+ if (callFrame->argumentCount() > 0) {
+ auto encoding_ = callFrame->argument(0).toString(lexicalGlobalObject);
+ std::optional<BufferEncodingType> opt = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, encoding_);
+ if (opt.has_value()) {
+ encoding = opt.value();
+ }
+ }
+ JSStringDecoder* stringDecoder = JSStringDecoder::create(
+ vm, lexicalGlobalObject, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSStringDecoderStructure(), encoding);
+ return JSC::JSValue::encode(stringDecoder);
+}
+
+void JSStringDecoderConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype)
+{
+}
+
+JSC::GCClient::IsoSubspace* JSStringDecoderConstructor::subspaceForImpl(JSC::VM& vm)
+{
+ return WebCore::subspaceForImpl<JSStringDecoderConstructor, UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForStringDecoderConstructor.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForStringDecoderConstructor = WTFMove(space); },
+ [](auto& spaces) { return spaces.m_subspaceForStringDecoderConstructor.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForStringDecoderConstructor = WTFMove(space); });
+}
+
+const ClassInfo JSStringDecoderConstructor::s_info = { "StringDecoder"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStringDecoderConstructor) };
+
+} // namespace Zig
diff --git a/src/bun.js/bindings/JSStringDecoder.h b/src/bun.js/bindings/JSStringDecoder.h
new file mode 100644
index 000000000..de2c54209
--- /dev/null
+++ b/src/bun.js/bindings/JSStringDecoder.h
@@ -0,0 +1,129 @@
+#pragma once
+
+#include "root.h"
+#include "BufferEncodingType.h"
+
+namespace WebCore {
+using namespace JSC;
+
+class JSStringDecoder : public JSC::JSDestructibleObject {
+ using Base = JSC::JSDestructibleObject;
+
+public:
+ JSStringDecoder(JSC::VM& vm, JSC::Structure* structure, BufferEncodingType encoding)
+ : Base(vm, structure), m_lastNeed(0), m_lastTotal(0), m_encoding(encoding)
+ {
+ }
+
+ DECLARE_INFO;
+
+ static constexpr unsigned StructureFlags = Base::StructureFlags;
+
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return subspaceForImpl(vm);
+ }
+
+ static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
+
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject,
+ JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype,
+ JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+ static JSStringDecoder* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, BufferEncodingType encoding)
+ {
+ JSStringDecoder* accessor = new (NotNull, JSC::allocateCell<JSStringDecoder>(vm)) JSStringDecoder(vm, structure, encoding);
+ accessor->finishCreation(vm, globalObject);
+ return accessor;
+ }
+
+ void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
+ static void destroy(JSCell*) {}
+
+ JSC::JSValue write(JSC::VM&, JSC::JSGlobalObject*, uint8_t*, uint32_t);
+ JSC::JSValue end(JSC::VM&, JSC::JSGlobalObject*, uint8_t*, uint32_t);
+
+ uint8_t m_lastNeed;
+ uint8_t m_lastTotal;
+ uint8_t m_lastChar[4];
+
+private:
+ JSC::JSValue fillLast(JSC::VM&, JSC::JSGlobalObject*, uint8_t*, uint32_t);
+ JSC::JSValue text(JSC::VM&, JSC::JSGlobalObject*, uint8_t*, uint32_t, uint32_t);
+ uint8_t utf8CheckIncomplete(uint8_t*, uint32_t, uint32_t);
+
+ BufferEncodingType m_encoding;
+};
+
+class JSStringDecoderPrototype : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+ static JSStringDecoderPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSStringDecoderPrototype* ptr = new (NotNull, JSC::allocateCell<JSStringDecoderPrototype>(vm)) JSStringDecoderPrototype(vm, structure);
+ ptr->finishCreation(vm, globalObject);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSStringDecoderPrototype(JSC::VM& vm, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+class JSStringDecoderConstructor final : public JSC::InternalFunction {
+public:
+ using Base = JSC::InternalFunction;
+ static JSStringDecoderConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSStringDecoderPrototype* prototype);
+
+ static constexpr unsigned StructureFlags = Base::StructureFlags;
+ static constexpr bool needsDestruction = false;
+
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
+ }
+
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return subspaceForImpl(vm);
+ }
+
+ static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
+
+ void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype);
+
+ // Must be defined for each specialization class.
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
+ DECLARE_EXPORT_INFO;
+private:
+ JSStringDecoderConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction)
+ : Base(vm, structure, nativeFunction, nativeFunction)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype);
+};
+
+}
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 05b5a3e29..855e212f9 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -87,6 +87,7 @@
#include "JSErrorEvent.h"
#include "JSCloseEvent.h"
#include "JSFetchHeaders.h"
+#include "JSStringDecoder.h"
#include "Process.h"
@@ -160,6 +161,7 @@ using JSBuffer = WebCore::JSBuffer;
#include "../modules/BufferModule.h"
#include "../modules/EventsModule.h"
#include "../modules/ProcessModule.h"
+#include "../modules/StringDecoderModule.h"
// #include <iostream>
static bool has_loaded_jsc = false;
@@ -1986,6 +1988,18 @@ void GlobalObject::finishCreation(VM& vm)
init.setConstructor(constructor);
});
+ m_JSStringDecoderClassStructure.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ auto* prototype = JSStringDecoderPrototype::create(
+ init.vm, init.global, JSStringDecoderPrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
+ auto* structure = JSStringDecoder::createStructure(init.vm, init.global, prototype);
+ auto* constructor = JSStringDecoderConstructor::create(
+ init.vm, init.global, JSStringDecoderConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
+ init.setPrototype(prototype);
+ init.setStructure(structure);
+ init.setConstructor(constructor);
+ });
+
m_JSFFIFunctionStructure.initLater(
[](LazyClassStructure::Initializer& init) {
init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->functionPrototype()));
@@ -2661,6 +2675,16 @@ static JSC_DEFINE_HOST_FUNCTION(functionFulfillModuleSync,
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
}
+ case SyntheticModuleType::StringDecoder: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(
+ generateStringDecoderSourceCode,
+ JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath("node:string_decoder"_s)), WTFMove(moduleKey)));
+
+ globalObject->moduleLoader()->provideFetch(globalObject, key, WTFMove(source));
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined()));
+ RELEASE_AND_RETURN(scope, JSValue::encode(JSC::jsUndefined()));
+ }
default: {
auto provider = Zig::SourceProvider::create(res.result.value);
globalObject->moduleLoader()->provideFetch(globalObject, key, JSC::SourceCode(provider));
@@ -2758,6 +2782,18 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb
scope.release();
return promise;
}
+ case SyntheticModuleType::StringDecoder: {
+ auto source = JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(generateStringDecoderSourceCode,
+ JSC::SourceOrigin(), WTFMove(moduleKey)));
+
+ auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
+ RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
+
+ promise->resolve(globalObject, sourceCode);
+ scope.release();
+ return promise;
+ }
default: {
auto provider = Zig::SourceProvider::create(res.result.value);
auto jsSourceCode = JSC::JSSourceCode::create(vm, JSC::SourceCode(provider));
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 53935a0ae..88149434c 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -186,6 +186,10 @@ public:
JSC::JSValue HTTPSResponseSinkPrototype() { return m_JSHTTPSResponseSinkClassStructure.prototypeInitializedOnMainThread(this); }
JSC::JSValue JSReadableHTTPSResponseSinkControllerPrototype() { return m_JSHTTPSResponseControllerPrototype.getInitializedOnMainThread(this); }
+ JSC::Structure* JSStringDecoderStructure() { return m_JSStringDecoderClassStructure.getInitializedOnMainThread(this); }
+ JSC::JSObject* JSStringDecoder() { return m_JSStringDecoderClassStructure.constructorInitializedOnMainThread(this); }
+ JSC::JSValue JSStringDecoderPrototype() { return m_JSStringDecoderClassStructure.prototypeInitializedOnMainThread(this); }
+
JSC::JSMap* readableStreamNativeMap() { return m_lazyReadableStreamPrototypeMap.getInitializedOnMainThread(this); }
JSC::JSMap* requireMap() { return m_requireMap.getInitializedOnMainThread(this); }
JSC::JSObject* encodeIntoObjectPrototype() { return m_encodeIntoObjectPrototype.getInitializedOnMainThread(this); }
@@ -318,6 +322,7 @@ private:
LazyClassStructure m_JSArrayBufferSinkClassStructure;
LazyClassStructure m_JSHTTPResponseSinkClassStructure;
LazyClassStructure m_JSHTTPSResponseSinkClassStructure;
+ LazyClassStructure m_JSStringDecoderClassStructure;
LazyProperty<JSGlobalObject, JSObject> m_JSArrayBufferControllerPrototype;
LazyProperty<JSGlobalObject, JSObject> m_JSHTTPSResponseControllerPrototype;
diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig
index 199259a69..2f10e75a4 100644
--- a/src/bun.js/bindings/exports.zig
+++ b/src/bun.js/bindings/exports.zig
@@ -247,6 +247,7 @@ pub const ResolvedSource = extern struct {
@"node:buffer" = 1024,
@"node:process" = 1025,
@"node:events" = 1026,
+ @"node:string_decoder" = 1027,
};
};
diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h
index 490242bd4..c9ac33e21 100644
--- a/src/bun.js/bindings/headers-handwritten.h
+++ b/src/bun.js/bindings/headers-handwritten.h
@@ -181,6 +181,7 @@ enum SyntheticModuleType : uint64_t {
Buffer = 1024,
Process = 1025,
Events = 1026,
+ StringDecoder = 1027,
};
extern "C" ZigErrorCode Zig_ErrorCodeParserError;
diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
index 79be4bf8c..75b7995f0 100644
--- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
@@ -26,6 +26,8 @@ public:
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSinkConstructor;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSinkController;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSSink;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStringDecoder;
+ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForStringDecoderConstructor;
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
/* --- bun --- */
diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
index 88298f9b5..42fb1d88b 100644
--- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h
@@ -26,6 +26,8 @@ public:
std::unique_ptr<IsoSubspace> m_subspaceForJSSinkConstructor;
std::unique_ptr<IsoSubspace> m_subspaceForJSSinkController;
std::unique_ptr<IsoSubspace> m_subspaceForJSSink;
+ std::unique_ptr<IsoSubspace> m_subspaceForStringDecoder;
+ std::unique_ptr<IsoSubspace> m_subspaceForStringDecoderConstructor;
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
/*-- BUN --*/
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index 9b7446d9c..c8a336c5e 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -868,6 +868,16 @@ pub const VirtualMachine = struct {
.hash = 0,
};
},
+ .@"node:string_decoder" => {
+ return ResolvedSource{
+ .allocator = null,
+ .source_code = ZigString.init(""),
+ .specifier = ZigString.init("node:string_decoder"),
+ .source_url = ZigString.init("node:string_decoder"),
+ .hash = 0,
+ .tag = ResolvedSource.Tag.@"node:string_decoder",
+ };
+ },
.@"bun:ffi" => {
return ResolvedSource{
.allocator = null,
@@ -2802,6 +2812,7 @@ pub const HardcodedModule = enum {
@"node:module",
@"node:os",
@"node:stream",
+ @"node:string_decoder",
@"node:path",
@"node:perf_hooks",
@"node:process",
@@ -2843,6 +2854,7 @@ pub const HardcodedModule = enum {
.{ "node:stream", HardcodedModule.@"node:stream" },
.{ "node:stream/consumer", HardcodedModule.@"node:stream/consumer" },
.{ "node:stream/web", HardcodedModule.@"node:stream/web" },
+ .{ "node:string_decoder", HardcodedModule.@"node:string_decoder" },
.{ "node:timers", HardcodedModule.@"node:timers" },
.{ "node:timers/promises", HardcodedModule.@"node:timers/promises" },
.{ "node:url", HardcodedModule.@"node:url" },
@@ -2850,6 +2862,7 @@ pub const HardcodedModule = enum {
.{ "path", HardcodedModule.@"node:path" },
.{ "process", HardcodedModule.@"node:process" },
.{ "streams", HardcodedModule.@"node:stream" },
+ .{ "string_decoder", HardcodedModule.@"node:string_decoder" },
.{ "undici", HardcodedModule.@"undici" },
.{ "ws", HardcodedModule.@"ws" },
},
@@ -2887,6 +2900,7 @@ pub const HardcodedModule = enum {
.{ "node:stream/consumer", "node:stream/consumer" },
.{ "node:stream/web", "node:stream/web" },
.{ "node:stream", "node:stream" },
+ .{ "node:string_decoder", "node:string_decoder" },
.{ "node:timers", "node:timers" },
.{ "node:timers/promises", "node:timers/promises" },
.{ "node:url", "node:url" },
@@ -2899,6 +2913,7 @@ pub const HardcodedModule = enum {
.{ "stream/consumer", "node:stream/consumer" },
.{ "stream/web", "node:stream/web" },
.{ "stream", "node:stream" },
+ .{ "string_decoder", "node:string_decoder" },
.{ "timers", "node:timers" },
.{ "timers/promises", "node:timers/promises" },
.{ "undici", "undici" },
diff --git a/src/bun.js/modules/StringDecoderModule.h b/src/bun.js/modules/StringDecoderModule.h
new file mode 100644
index 000000000..9beff4f3b
--- /dev/null
+++ b/src/bun.js/modules/StringDecoderModule.h
@@ -0,0 +1,15 @@
+#include "../bindings/ZigGlobalObject.h"
+#include "../bindings/JSStringDecoder.h"
+#include "JavaScriptCore/JSGlobalObject.h"
+
+namespace Zig {
+
+inline void generateStringDecoderSourceCode(JSC::JSGlobalObject* lexicalGlobalObject, JSC::Identifier moduleKey, Vector<JSC::Identifier, 4>& exportNames, JSC::MarkedArgumentBuffer& exportValues) {
+ JSC::VM& vm = lexicalGlobalObject->vm();
+ GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject);
+
+ exportNames.append(JSC::Identifier::fromString(vm, "StringDecoder"_s));
+ exportValues.append(globalObject->JSStringDecoder());
+}
+
+}
diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig
index de09e4be9..00b89d576 100644
--- a/src/bun.js/webcore/encoding.zig
+++ b/src/bun.js/webcore/encoding.zig
@@ -819,16 +819,13 @@ pub const Encoder = struct {
// For this, we rely on the GC to manage the memory to minimize potential for memory leaks
return ZigString.init(input).toValueGC(global);
},
- // potentially convert UTF-16 to UTF-8
- JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => {
- const converted = strings.toUTF16Alloc(allocator, input, false) catch return ZigString.init("Out of memory").toErrorInstance(global);
- if (converted) |utf16| {
- return ZigString.toExternalU16(utf16.ptr, utf16.len, global);
+ .ucs2, .utf16le => {
+ var output = allocator.alloc(u16, len / 2) catch return ZigString.init("Out of memory").toErrorInstance(global);
+ var i : usize = 0;
+ while (i < len / 2) : (i += 1) {
+ output[i] = (@intCast(u16, input[2 * i + 1]) << 8) + @intCast(u16, input[2 * i]);
}
-
- var output = allocator.alloc(u8, input.len) catch return ZigString.init("Out of memory").toErrorInstance(global);
- JSC.WTF.copyLCharsFromUCharSource(output.ptr, []align(1) const u16, @ptrCast([*]align(1) const u16, input.ptr)[0 .. input.len / 2]);
- return ZigString.init(output).toExternalValue(global);
+ return ZigString.toExternalU16(output.ptr, output.len, global);
},
JSC.Node.Encoding.hex => {