mirror of
https://github.com/oven-sh/bun
synced 2026-02-03 07:28:53 +00:00
Compare commits
7 Commits
claude/esm
...
claude/imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
549bcbf1ca | ||
|
|
bd9fbc0cd3 | ||
|
|
d43b3e3119 | ||
|
|
6d592d633b | ||
|
|
1b76feba07 | ||
|
|
d16bbd80d3 | ||
|
|
cd786fd0ba |
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "JavaScriptCore/ArgList.h"
|
||||
#include "JavaScriptCore/ExceptionScope.h"
|
||||
#include "wtf/SIMDUTF.h"
|
||||
|
||||
#include "ActiveDOMObject.h"
|
||||
#include "ExtendedDOMClientIsoSubspaces.h"
|
||||
@@ -2110,6 +2111,199 @@ static JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlobalObje
|
||||
RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
|
||||
}
|
||||
|
||||
// Helper function for transcoding between encodings
|
||||
static JSC::JSUint8Array* transcodeBuffer(
|
||||
JSC::JSGlobalObject* lexicalGlobalObject,
|
||||
const uint8_t* source,
|
||||
size_t sourceLength,
|
||||
BufferEncodingType fromEncoding,
|
||||
BufferEncodingType toEncoding)
|
||||
{
|
||||
auto& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Handle empty source
|
||||
if (sourceLength == 0) {
|
||||
auto* emptyResult = createUninitializedBuffer(lexicalGlobalObject, 0);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
return emptyResult;
|
||||
}
|
||||
|
||||
JSC::JSUint8Array* result = nullptr;
|
||||
|
||||
// Handle same encoding
|
||||
if (fromEncoding == toEncoding) {
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, sourceLength);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (sourceLength > 0) {
|
||||
memcpy(result->typedVector(), source, sourceLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Transcoding logic based on encoding pairs
|
||||
switch (fromEncoding) {
|
||||
case BufferEncodingType::utf8: {
|
||||
switch (toEncoding) {
|
||||
case BufferEncodingType::utf16le: {
|
||||
// UTF-8 to UTF-16LE (ucs2 normalized to utf16le earlier)
|
||||
size_t expectedLength = simdutf::utf16_length_from_utf8(reinterpret_cast<const char*>(source), sourceLength);
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, expectedLength * 2);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
size_t actualLength = simdutf::convert_utf8_to_utf16le(
|
||||
reinterpret_cast<const char*>(source),
|
||||
sourceLength,
|
||||
reinterpret_cast<char16_t*>(result->typedVector()));
|
||||
|
||||
if (actualLength == 0) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "Invalid UTF-8 sequence"_s);
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case BufferEncodingType::ascii:
|
||||
case BufferEncodingType::latin1: {
|
||||
// UTF-8 to ASCII/Latin1: convert UTF-8 to UTF-16 first, then to Latin1
|
||||
// Invalid characters will be replaced with '?'
|
||||
size_t expectedUtf16Length = simdutf::utf16_length_from_utf8(reinterpret_cast<const char*>(source), sourceLength);
|
||||
std::vector<char16_t> utf16Buffer(expectedUtf16Length);
|
||||
|
||||
size_t actualLength = simdutf::convert_utf8_to_utf16le(
|
||||
reinterpret_cast<const char*>(source),
|
||||
sourceLength,
|
||||
utf16Buffer.data());
|
||||
|
||||
if (actualLength == 0) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "Invalid UTF-8 sequence"_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, actualLength);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
// Convert UTF-16 to ASCII/Latin1, replacing invalid chars with '?'
|
||||
// ASCII is 7-bit (0x00-0x7F), Latin1 is 8-bit (0x00-0xFF)
|
||||
const auto maxCodePoint = (toEncoding == BufferEncodingType::ascii) ? 0x7F : 0xFF;
|
||||
uint8_t* dest = result->typedVector();
|
||||
for (size_t i = 0; i < actualLength; i++) {
|
||||
char16_t c = utf16Buffer[i];
|
||||
dest[i] = (c <= maxCodePoint) ? static_cast<uint8_t>(c) : '?';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BufferEncodingType::utf16le: {
|
||||
// Source is UTF-16LE (ucs2 normalized to utf16le earlier)
|
||||
size_t utf16Length = sourceLength / 2;
|
||||
const char16_t* utf16Source = reinterpret_cast<const char16_t*>(source);
|
||||
|
||||
switch (toEncoding) {
|
||||
case BufferEncodingType::utf8: {
|
||||
// UTF-16LE to UTF-8
|
||||
size_t expectedLength = simdutf::utf8_length_from_utf16le(utf16Source, utf16Length);
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, expectedLength);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
size_t actualLength = simdutf::convert_utf16le_to_utf8(
|
||||
utf16Source,
|
||||
utf16Length,
|
||||
reinterpret_cast<char*>(result->typedVector()));
|
||||
|
||||
if (actualLength == 0) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "Invalid UTF-16 sequence"_s);
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case BufferEncodingType::ascii:
|
||||
case BufferEncodingType::latin1: {
|
||||
// UTF-16LE to ASCII/Latin1
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, utf16Length);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
// ASCII is 7-bit (0x00-0x7F), Latin1 is 8-bit (0x00-0xFF)
|
||||
const auto maxCodePoint = (toEncoding == BufferEncodingType::ascii) ? 0x7F : 0xFF;
|
||||
uint8_t* dest = result->typedVector();
|
||||
for (size_t i = 0; i < utf16Length; i++) {
|
||||
char16_t c = utf16Source[i];
|
||||
dest[i] = (c <= maxCodePoint) ? static_cast<uint8_t>(c) : '?';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BufferEncodingType::ascii:
|
||||
case BufferEncodingType::latin1: {
|
||||
// Source is ASCII/Latin1
|
||||
switch (toEncoding) {
|
||||
case BufferEncodingType::latin1: {
|
||||
// ASCII/Latin1 to Latin1 - if source is ASCII, all bytes are valid Latin1
|
||||
// if source is Latin1, just copy
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, sourceLength);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
if (sourceLength > 0) {
|
||||
memcpy(result->typedVector(), source, sourceLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case BufferEncodingType::ascii: {
|
||||
// ASCII/Latin1 to ASCII - clamp high bytes to '?'
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, sourceLength);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
uint8_t* dest = result->typedVector();
|
||||
for (size_t i = 0; i < sourceLength; i++) {
|
||||
uint8_t byte = source[i];
|
||||
dest[i] = (byte <= 0x7F) ? byte : '?';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case BufferEncodingType::utf8: {
|
||||
// Latin1 to UTF-8
|
||||
size_t expectedLength = simdutf::utf8_length_from_latin1(reinterpret_cast<const char*>(source), sourceLength);
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, expectedLength);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
[[maybe_unused]] size_t written = simdutf::convert_latin1_to_utf8(
|
||||
reinterpret_cast<const char*>(source),
|
||||
sourceLength,
|
||||
reinterpret_cast<char*>(result->typedVector()));
|
||||
|
||||
return result;
|
||||
}
|
||||
case BufferEncodingType::utf16le: {
|
||||
// Latin1 to UTF-16LE (ucs2 normalized to utf16le earlier)
|
||||
result = createUninitializedBuffer(lexicalGlobalObject, sourceLength * 2);
|
||||
RETURN_IF_EXCEPTION(scope, nullptr);
|
||||
|
||||
[[maybe_unused]] size_t written = simdutf::convert_latin1_to_utf16le(
|
||||
reinterpret_cast<const char*>(source),
|
||||
sourceLength,
|
||||
reinterpret_cast<char16_t*>(result->typedVector()));
|
||||
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If we get here, the encoding combination is not supported
|
||||
throwTypeError(lexicalGlobalObject, scope, "Unsupported encoding combination"_s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" JSC::EncodedJSValue JSBuffer__fromMmap(Zig::GlobalObject* globalObject, void* ptr, size_t length)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
@@ -2800,6 +2994,75 @@ JSC::JSObject* createBufferConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalO
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
// Transcode function with C linkage for NodeBufferModule
|
||||
extern "C" JSC::EncodedJSValue jsBufferFunction_transcode(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
|
||||
{
|
||||
using namespace WebCore;
|
||||
using namespace JSC;
|
||||
|
||||
auto& vm = lexicalGlobalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
// Validate arguments
|
||||
if (callFrame->argumentCount() < 3) {
|
||||
throwTypeError(lexicalGlobalObject, scope, "transcode requires 3 arguments"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
JSValue sourceValue = callFrame->argument(0);
|
||||
JSValue fromEncodingValue = callFrame->argument(1);
|
||||
JSValue toEncodingValue = callFrame->argument(2);
|
||||
|
||||
// Validate source is a Uint8Array
|
||||
auto* sourceView = JSC::jsDynamicCast<JSC::JSUint8Array*>(sourceValue);
|
||||
if (!sourceView) {
|
||||
return Bun::ERR::INVALID_ARG_TYPE(scope, lexicalGlobalObject, "source"_s, "Buffer or Uint8Array"_s, sourceValue);
|
||||
}
|
||||
|
||||
if (sourceView->isDetached()) {
|
||||
throwVMTypeError(lexicalGlobalObject, scope, "Cannot transcode a detached buffer"_s);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Parse encodings
|
||||
auto fromEncoding = parseEncoding(scope, lexicalGlobalObject, fromEncodingValue, false);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
auto toEncoding = parseEncoding(scope, lexicalGlobalObject, toEncodingValue, false);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
// Normalize encoding aliases: ucs2 is an alias for utf16le
|
||||
if (fromEncoding == BufferEncodingType::ucs2) {
|
||||
fromEncoding = BufferEncodingType::utf16le;
|
||||
}
|
||||
if (toEncoding == BufferEncodingType::ucs2) {
|
||||
toEncoding = BufferEncodingType::utf16le;
|
||||
}
|
||||
|
||||
// Check for supported encodings (ucs2 already normalized to utf16le)
|
||||
bool fromSupported = (fromEncoding == BufferEncodingType::utf8 || fromEncoding == BufferEncodingType::utf16le || fromEncoding == BufferEncodingType::latin1 || fromEncoding == BufferEncodingType::ascii);
|
||||
|
||||
bool toSupported = (toEncoding == BufferEncodingType::utf8 || toEncoding == BufferEncodingType::utf16le || toEncoding == BufferEncodingType::latin1 || toEncoding == BufferEncodingType::ascii);
|
||||
|
||||
if (!fromSupported || !toSupported) {
|
||||
auto* error = Bun::createError(lexicalGlobalObject, Bun::ErrorCode::ERR_UNKNOWN_ENCODING, "Unknown encoding"_s);
|
||||
scope.throwException(lexicalGlobalObject, error);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Perform transcoding
|
||||
auto* result = transcodeBuffer(
|
||||
lexicalGlobalObject,
|
||||
sourceView->typedVector(),
|
||||
sourceView->byteLength(),
|
||||
fromEncoding,
|
||||
toEncoding);
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(result));
|
||||
}
|
||||
|
||||
EncodedJSValue constructBufferFromArray(JSC::ThrowScope& throwScope, JSGlobalObject* lexicalGlobalObject, JSValue arrayValue)
|
||||
{
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
|
||||
@@ -124,6 +124,7 @@ JSC_DEFINE_HOST_FUNCTION(jsBufferConstructorFunction_isAscii,
|
||||
}
|
||||
|
||||
BUN_DECLARE_HOST_FUNCTION(jsFunctionResolveObjectURL);
|
||||
BUN_DECLARE_HOST_FUNCTION(jsBufferFunction_transcode);
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunctionNotImplemented,
|
||||
(JSGlobalObject * globalObject,
|
||||
@@ -203,7 +204,7 @@ DEFINE_NATIVE_MODULE(NodeBuffer)
|
||||
put(atobI, atobV);
|
||||
put(btoaI, btoaV);
|
||||
|
||||
auto* transcode = InternalFunction::createFunctionThatMasqueradesAsUndefined(vm, globalObject, 1, "transcode"_s, jsFunctionNotImplemented);
|
||||
auto* transcode = JSC::JSFunction::create(vm, globalObject, 3, "transcode"_s, jsBufferFunction_transcode, ImplementationVisibility::Public, NoIntrinsic, jsBufferFunction_transcode);
|
||||
|
||||
put(JSC::Identifier::fromString(vm, "transcode"_s), transcode);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ const { isModuleNamespaceObject } = require('util/types');
|
||||
const tmpdir = require('./tmpdir');
|
||||
const bits = ['arm64', 'loong64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64']
|
||||
.includes(process.arch) ? 64 : 32;
|
||||
const hasIntl = !!process.config.variables.v8_enable_i18n_support;
|
||||
const hasIntl = true; // Bun always has Intl support
|
||||
|
||||
const {
|
||||
atob,
|
||||
|
||||
@@ -46,19 +46,21 @@ assert.throws(
|
||||
{
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: 'The "source" argument must be an instance of Buffer ' +
|
||||
'or Uint8Array. Received null'
|
||||
// Bun uses "must be of type" while Node uses "must be an instance of"
|
||||
message: /The "source" argument must be (?:an instance of|of type) Buffer (?:or|and) Uint8Array\. Received null/
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => buffer.transcode(Buffer.from('a'), 'b', 'utf8'),
|
||||
/^Error: Unable to transcode Buffer \[U_ILLEGAL_ARGUMENT_ERROR\]/
|
||||
// Node.js uses ICU error, Bun uses ERR_UNKNOWN_ENCODING
|
||||
/Unable to transcode Buffer|Unknown encoding/
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => buffer.transcode(Buffer.from('a'), 'uf8', 'b'),
|
||||
/^Error: Unable to transcode Buffer \[U_ILLEGAL_ARGUMENT_ERROR\]$/
|
||||
// Node.js uses ICU error, Bun uses ERR_UNKNOWN_ENCODING
|
||||
/Unable to transcode Buffer|Unknown encoding/
|
||||
);
|
||||
|
||||
assert.deepStrictEqual(
|
||||
|
||||
142
test/regression/issue/24235.test.ts
Normal file
142
test/regression/issue/24235.test.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { Buffer, transcode } from "node:buffer";
|
||||
|
||||
describe("transcode", () => {
|
||||
test("should transcode UTF-8 to ASCII with replacement char", () => {
|
||||
const euroBuffer = Buffer.from("€", "utf8");
|
||||
const result = transcode(euroBuffer, "utf8", "ascii");
|
||||
expect(result.toString("ascii")).toBe("?");
|
||||
});
|
||||
|
||||
test("should transcode UTF-8 to Latin1 with replacement char", () => {
|
||||
const euroBuffer = Buffer.from("€", "utf8");
|
||||
const result = transcode(euroBuffer, "utf8", "latin1");
|
||||
expect(result.toString("latin1")).toBe("?");
|
||||
});
|
||||
|
||||
test("should transcode ASCII to UTF-8", () => {
|
||||
const asciiBuffer = Buffer.from("hello", "ascii");
|
||||
const result = transcode(asciiBuffer, "ascii", "utf8");
|
||||
expect(result.toString("utf8")).toBe("hello");
|
||||
});
|
||||
|
||||
test("should transcode Latin1 to UTF-8", () => {
|
||||
const latin1Buffer = Buffer.from([0xc0, 0xe9]); // À é
|
||||
const result = transcode(latin1Buffer, "latin1", "utf8");
|
||||
expect(result.toString("utf8")).toBe("Àé");
|
||||
});
|
||||
|
||||
test("should transcode UTF-8 to UTF-16LE", () => {
|
||||
const utf8Buffer = Buffer.from("hello", "utf8");
|
||||
const result = transcode(utf8Buffer, "utf8", "utf16le");
|
||||
expect(result.toString("utf16le")).toBe("hello");
|
||||
});
|
||||
|
||||
test("should transcode UTF-16LE to UTF-8", () => {
|
||||
const utf16Buffer = Buffer.from("hello", "utf16le");
|
||||
const result = transcode(utf16Buffer, "utf16le", "utf8");
|
||||
expect(result.toString("utf8")).toBe("hello");
|
||||
});
|
||||
|
||||
test("should transcode UCS2 to UTF-8", () => {
|
||||
const ucs2Buffer = Buffer.from("test", "ucs2");
|
||||
const result = transcode(ucs2Buffer, "ucs2", "utf8");
|
||||
expect(result.toString("utf8")).toBe("test");
|
||||
});
|
||||
|
||||
test("should handle empty buffer", () => {
|
||||
const emptyBuffer = Buffer.from("", "utf8");
|
||||
const result = transcode(emptyBuffer, "utf8", "ascii");
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
|
||||
test("should handle same encoding", () => {
|
||||
const buffer = Buffer.from("hello", "utf8");
|
||||
const result = transcode(buffer, "utf8", "utf8");
|
||||
expect(result.toString("utf8")).toBe("hello");
|
||||
});
|
||||
|
||||
test("should throw on invalid source type", () => {
|
||||
expect(() => {
|
||||
// @ts-expect-error - testing invalid input
|
||||
transcode("not a buffer", "utf8", "ascii");
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test("should throw on unsupported encoding", () => {
|
||||
const buffer = Buffer.from("test", "utf8");
|
||||
expect(() => {
|
||||
// @ts-expect-error - testing invalid encoding
|
||||
transcode(buffer, "utf8", "unsupported");
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test("should transcode UTF-16LE to ASCII with replacement", () => {
|
||||
const utf16Buffer = Buffer.from("hello€", "utf16le");
|
||||
const result = transcode(utf16Buffer, "utf16le", "ascii");
|
||||
expect(result.toString("ascii")).toBe("hello?");
|
||||
});
|
||||
|
||||
test("should transcode Latin1 to UTF-16LE", () => {
|
||||
const latin1Buffer = Buffer.from([0xc0, 0xe9]); // À é
|
||||
const result = transcode(latin1Buffer, "latin1", "utf16le");
|
||||
expect(result.toString("utf16le")).toBe("Àé");
|
||||
});
|
||||
|
||||
test("should handle multi-byte UTF-8 characters", () => {
|
||||
const utf8Buffer = Buffer.from("你好", "utf8");
|
||||
const result = transcode(utf8Buffer, "utf8", "utf16le");
|
||||
expect(result.toString("utf16le")).toBe("你好");
|
||||
});
|
||||
|
||||
test("should transcode UTF-16LE multi-byte to UTF-8", () => {
|
||||
const utf16Buffer = Buffer.from("你好", "utf16le");
|
||||
const result = transcode(utf16Buffer, "utf16le", "utf8");
|
||||
expect(result.toString("utf8")).toBe("你好");
|
||||
});
|
||||
|
||||
test("should transcode ASCII to Latin1", () => {
|
||||
const asciiBuffer = Buffer.from("hello", "ascii");
|
||||
const result = transcode(asciiBuffer, "ascii", "latin1");
|
||||
expect(result.toString("latin1")).toBe("hello");
|
||||
});
|
||||
|
||||
test("should transcode Latin1 to ASCII with high byte replacement", () => {
|
||||
// 0xC0 is 'À' which is > 0x7F, should become '?'
|
||||
const latin1Buffer = Buffer.from([0x68, 0x69, 0xc0], "latin1"); // "hi" + À
|
||||
const result = transcode(latin1Buffer, "latin1", "ascii");
|
||||
expect(result).toEqual(Buffer.from([0x68, 0x69, 0x3f])); // "hi?"
|
||||
});
|
||||
|
||||
test("should enforce 7-bit ASCII limit from UTF-8", () => {
|
||||
// © (U+00A9 = 0xA9 in Latin1) should become '?' in ASCII
|
||||
const utf8Buffer = Buffer.from("©", "utf8");
|
||||
const result = transcode(utf8Buffer, "utf8", "ascii");
|
||||
expect(result.toString("ascii")).toBe("?");
|
||||
});
|
||||
|
||||
test("should preserve Latin1 characters when transcoding to Latin1", () => {
|
||||
// À (0xC0) is valid in Latin1
|
||||
const latin1Buffer = Buffer.from([0xc0], "latin1");
|
||||
const result = transcode(latin1Buffer, "latin1", "latin1");
|
||||
expect(result).toEqual(Buffer.from([0xc0]));
|
||||
});
|
||||
|
||||
test("should treat ucs2 and utf16le as aliases", () => {
|
||||
const utf16leBuffer = Buffer.from("hi", "utf16le");
|
||||
const result = transcode(utf16leBuffer, "utf16le", "ucs2");
|
||||
expect(result.toString("ucs2")).toBe("hi");
|
||||
});
|
||||
|
||||
test("should transcode from ucs2 to utf16le", () => {
|
||||
const ucs2Buffer = Buffer.from("hello", "ucs2");
|
||||
const result = transcode(ucs2Buffer, "ucs2", "utf16le");
|
||||
expect(result.toString("utf16le")).toBe("hello");
|
||||
});
|
||||
|
||||
test("should transcode from ucs2 to utf8", () => {
|
||||
const ucs2Buffer = Buffer.from("test", "ucs2");
|
||||
const result = transcode(ucs2Buffer, "ucs2", "utf8");
|
||||
expect(result.toString("utf8")).toBe("test");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user