blob: 0763d6af4fb7da496adb8e6f3deef1e773a353e1 [file] [log] [blame]
/**
* @license
* Copyright 2020 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
#include "arrayUtils.js"
mergeInto(LibraryManager.library, {
$intArrayFromString__docs: '/** @type {function(string, boolean=, number=)} */',
$intArrayFromString: intArrayFromString,
$intArrayToString: intArrayToString,
// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the
// emscripten HEAP, returns a copy of that string as a Javascript String
// object.
$AsciiToString: function(ptr) {
#if CAN_ADDRESS_2GB
ptr >>>= 0;
#endif
var str = '';
while (1) {
var ch = {{{ makeGetValue('ptr++', 0, 'u8') }}};
if (!ch) return str;
str += String.fromCharCode(ch);
}
},
// Copies the given Javascript String object 'str' to the emscripten HEAP at
// address 'outPtr', null-terminated and encoded in ASCII form. The copy will
// require at most str.length+1 bytes of space in the HEAP.
$stringToAscii__deps: ['$writeAsciiToMemory'],
$stringToAscii: function(str, outPtr) {
return writeAsciiToMemory(str, outPtr, false);
},
#if TEXTDECODER == 2
$UTF16Decoder: "new TextDecoder('utf-16le');",
#elif TEXTDECODER == 1
$UTF16Decoder: "typeof TextDecoder != 'undefined' ? new TextDecoder('utf-16le') : undefined;",
#endif
// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the
// emscripten HEAP, returns a copy of that string as a Javascript String
// object.
#if TEXTDECODER
$UTF16ToString__deps: ['$UTF16Decoder'],
#endif
$UTF16ToString: function(ptr, maxBytesToRead) {
#if ASSERTIONS
assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!');
#endif
#if TEXTDECODER
var endPtr = ptr;
// TextDecoder needs to know the byte length in advance, it doesn't stop on
// null terminator by itself.
// Also, use the length info to avoid running tiny strings through
// TextDecoder, since .subarray() allocates garbage.
var idx = endPtr >> 1;
var maxIdx = idx + maxBytesToRead / 2;
// If maxBytesToRead is not passed explicitly, it will be undefined, and this
// will always evaluate to true. This saves on code size.
while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx;
endPtr = idx << 1;
#if TEXTDECODER != 2
if (endPtr - ptr > 32 && UTF16Decoder)
#endif // TEXTDECODER != 2
return UTF16Decoder.decode({{{ getUnsharedTextDecoderView('HEAPU8', 'ptr', 'endPtr') }}});
#endif // TEXTDECODER
#if TEXTDECODER != 2
// Fallback: decode without UTF16Decoder
var str = '';
// If maxBytesToRead is not passed explicitly, it will be undefined, and the
// for-loop's condition will always evaluate to true. The loop is then
// terminated on the first null char.
for (var i = 0; !(i >= maxBytesToRead / 2); ++i) {
var codeUnit = {{{ makeGetValue('ptr', 'i*2', 'i16') }}};
if (codeUnit == 0) break;
// fromCharCode constructs a character from a UTF-16 code unit, so we can
// pass the UTF16 string right through.
str += String.fromCharCode(codeUnit);
}
return str;
#endif // TEXTDECODER != 2
},
// Copies the given Javascript String object 'str' to the emscripten HEAP at
// address 'outPtr', null-terminated and encoded in UTF16 form. The copy will
// require at most str.length*4+2 bytes of space in the HEAP. Use the
// function lengthBytesUTF16() to compute the exact number of bytes (excluding
// null terminator) that this function will write.
// Parameters:
// str: the Javascript string to copy.
// outPtr: Byte address in Emscripten HEAP where to write the string to.
// maxBytesToWrite: The maximum number of bytes this function can write to
// the array. This count should include the null
// terminator, i.e. if maxBytesToWrite=2, only the null
// terminator will be written and nothing else.
// maxBytesToWrite<2 does not write any bytes to the
// output, not even the null terminator.
// Returns the number of bytes written, EXCLUDING the null terminator.
$stringToUTF16: function(str, outPtr, maxBytesToWrite) {
#if ASSERTIONS
assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!');
#endif
#if ASSERTIONS
assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
#endif
// Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed.
if (maxBytesToWrite === undefined) {
maxBytesToWrite = 0x7FFFFFFF;
}
if (maxBytesToWrite < 2) return 0;
maxBytesToWrite -= 2; // Null terminator.
var startPtr = outPtr;
var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length;
for (var i = 0; i < numCharsToWrite; ++i) {
// charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP.
var codeUnit = str.charCodeAt(i); // possibly a lead surrogate
{{{ makeSetValue('outPtr', 0, 'codeUnit', 'i16') }}};
outPtr += 2;
}
// Null-terminate the pointer to the HEAP.
{{{ makeSetValue('outPtr', 0, 0, 'i16') }}};
return outPtr - startPtr;
},
// Returns the number of bytes the given Javascript string takes if encoded as
// a UTF16 byte array, EXCLUDING the null terminator byte.
$lengthBytesUTF16: function(str) {
return str.length*2;
},
$UTF32ToString: function(ptr, maxBytesToRead) {
#if ASSERTIONS
assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!');
#endif
var i = 0;
var str = '';
// If maxBytesToRead is not passed explicitly, it will be undefined, and this
// will always evaluate to true. This saves on code size.
while (!(i >= maxBytesToRead / 4)) {
var utf32 = {{{ makeGetValue('ptr', 'i*4', 'i32') }}};
if (utf32 == 0) break;
++i;
// Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing.
// See http://unicode.org/faq/utf_bom.html#utf16-3
if (utf32 >= 0x10000) {
var ch = utf32 - 0x10000;
str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
} else {
str += String.fromCharCode(utf32);
}
}
return str;
},
// Copies the given Javascript String object 'str' to the emscripten HEAP at
// address 'outPtr', null-terminated and encoded in UTF32 form. The copy will
// require at most str.length*4+4 bytes of space in the HEAP.
// Use the function lengthBytesUTF32() to compute the exact number of bytes
// (excluding null terminator) that this function will write.
// Parameters:
// str: the Javascript string to copy.
// outPtr: Byte address in Emscripten HEAP where to write the string to.
// maxBytesToWrite: The maximum number of bytes this function can write to
// the array. This count should include the null
// terminator, i.e. if maxBytesToWrite=4, only the null
// terminator will be written and nothing else.
// maxBytesToWrite<4 does not write any bytes to the
// output, not even the null terminator.
// Returns the number of bytes written, EXCLUDING the null terminator.
$stringToUTF32: function(str, outPtr, maxBytesToWrite) {
#if CAN_ADDRESS_2GB
outPtr >>>= 0;
#endif
#if ASSERTIONS
assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!');
#endif
#if ASSERTIONS
assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
#endif
// Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed.
if (maxBytesToWrite === undefined) {
maxBytesToWrite = 0x7FFFFFFF;
}
if (maxBytesToWrite < 4) return 0;
var startPtr = outPtr;
var endPtr = startPtr + maxBytesToWrite - 4;
for (var i = 0; i < str.length; ++i) {
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.
// See http://unicode.org/faq/utf_bom.html#utf16-3
var codeUnit = str.charCodeAt(i); // possibly a lead surrogate
if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) {
var trailSurrogate = str.charCodeAt(++i);
codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF);
}
{{{ makeSetValue('outPtr', 0, 'codeUnit', 'i32') }}};
outPtr += 4;
if (outPtr + 4 > endPtr) break;
}
// Null-terminate the pointer to the HEAP.
{{{ makeSetValue('outPtr', 0, 0, 'i32') }}};
return outPtr - startPtr;
},
// Returns the number of bytes the given Javascript string takes if encoded as
// a UTF16 byte array, EXCLUDING the null terminator byte.
$lengthBytesUTF32: function(str) {
var len = 0;
for (var i = 0; i < str.length; ++i) {
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.
// See http://unicode.org/faq/utf_bom.html#utf16-3
var codeUnit = str.charCodeAt(i);
if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate.
len += 4;
}
return len;
},
// Allocate heap space for a JS string, and write it there.
// It is the responsibility of the caller to free() that memory.
$allocateUTF8: function(str) {
var size = lengthBytesUTF8(str) + 1;
var ret = {{{ makeMalloc('allocateUTF8', 'size') }}};
if (ret) stringToUTF8Array(str, HEAP8, ret, size);
return ret;
},
// Allocate stack space for a JS string, and write it there.
$allocateUTF8OnStack: function(str) {
var size = lengthBytesUTF8(str) + 1;
var ret = stackAlloc(size);
stringToUTF8Array(str, HEAP8, ret, size);
return ret;
},
// Deprecated: This function should not be called because it is unsafe and
// does not provide a maximum length limit of how many bytes it is allowed to
// write. Prefer calling the function stringToUTF8Array() instead, which takes
// in a maximum length that can be used to be secure from out of bounds
// writes.
$writeStringToMemory__docs: '/** @deprecated @param {boolean=} dontAddNull */',
$writeStringToMemory: function(string, buffer, dontAddNull) {
warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!');
var /** @type {number} */ lastChar, /** @type {number} */ end;
if (dontAddNull) {
// stringToUTF8Array always appends null. If we don't want to do that, remember the
// character that existed at the location where the null will be placed, and restore
// that after the write (below).
end = buffer + lengthBytesUTF8(string);
lastChar = HEAP8[end];
}
stringToUTF8(string, buffer, Infinity);
if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character.
},
$writeArrayToMemory: function(array, buffer) {
#if ASSERTIONS
assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)')
#endif
HEAP8.set(array, buffer);
},
$writeAsciiToMemory__docs: '/** @param {boolean=} dontAddNull */',
$writeAsciiToMemory: function(str, buffer, dontAddNull) {
for (var i = 0; i < str.length; ++i) {
#if ASSERTIONS
assert(str.charCodeAt(i) === (str.charCodeAt(i) & 0xff));
#endif
{{{ makeSetValue('buffer++', 0, 'str.charCodeAt(i)', 'i8') }}};
}
// Null-terminate the pointer to the HEAP.
if (!dontAddNull) {{{ makeSetValue('buffer', 0, 0, 'i8') }}};
},
});