| /** |
| * @license |
| * Copyright 2020 The Emscripten Authors |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| // runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime. |
| |
| // 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. |
| |
| function AsciiToString(ptr) { |
| #if CAN_ADDRESS_2GB |
| ptr >>>= 0; |
| #endif |
| var str = ''; |
| while (1) { |
| var ch = {{{ makeGetValue('ptr++', 0, 'i8', null, true) }}}; |
| 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. |
| |
| function stringToAscii(str, outPtr) { |
| return writeAsciiToMemory(str, outPtr, false); |
| } |
| |
| // 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 == 2 |
| var UTF16Decoder = new TextDecoder{{{ USE_PTHREADS ? 'Wrapper' : ''}}}('utf-16le'); |
| #else // TEXTDECODER == 2 |
| #if TEXTDECODER |
| var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder{{{ USE_PTHREADS ? 'Wrapper' : ''}}}('utf-16le') : undefined; |
| #endif // TEXTDECODER |
| #endif // TEXTDECODER == 2 |
| |
| function UTF16ToString(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(HEAPU8.subarray(ptr, endPtr)); |
| #if TEXTDECODER != 2 |
| } else { |
| #endif // TEXTDECODER != 2 |
| #endif // TEXTDECODER |
| 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; |
| #if TEXTDECODER && TEXTDECODER != 2 |
| } |
| #endif // TEXTDECODER |
| } |
| |
| // 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. |
| |
| function stringToUTF16(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. |
| |
| function lengthBytesUTF16(str) { |
| return str.length*2; |
| } |
| |
| function UTF32ToString(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. |
| |
| function stringToUTF32(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. |
| |
| function lengthBytesUTF32(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. |
| function allocateUTF8(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. |
| function allocateUTF8OnStack(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. |
| /** @deprecated |
| @param {boolean=} dontAddNull */ |
| function writeStringToMemory(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. |
| } |
| |
| function writeArrayToMemory(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); |
| } |
| |
| /** @param {boolean=} dontAddNull */ |
| function writeAsciiToMemory(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') }}}; |
| } |