Skip to content

Commit 0a8bf0b

Browse files
authored
Merge pull request #4403 from juj/textdecoder
textdecoder
2 parents 2363ce5 + 61bd42b commit 0a8bf0b

File tree

9 files changed

+416
-37
lines changed

9 files changed

+416
-37
lines changed

src/preamble.js

Lines changed: 80 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -515,41 +515,57 @@ function stringToAscii(str, outPtr) {
515515
// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns
516516
// a copy of that string as a Javascript String object.
517517

518+
#if TEXTDECODER
519+
var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;
520+
#endif
518521
function UTF8ArrayToString(u8Array, idx) {
519-
var u0, u1, u2, u3, u4, u5;
520-
521-
var str = '';
522-
while (1) {
523-
// For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629
524-
u0 = u8Array[idx++];
525-
if (!u0) return str;
526-
if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }
527-
u1 = u8Array[idx++] & 63;
528-
if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }
529-
u2 = u8Array[idx++] & 63;
530-
if ((u0 & 0xF0) == 0xE0) {
531-
u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
532-
} else {
533-
u3 = u8Array[idx++] & 63;
534-
if ((u0 & 0xF8) == 0xF0) {
535-
u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | u3;
522+
#if TEXTDECODER
523+
var endPtr = idx;
524+
// TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself.
525+
// Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage.
526+
while (u8Array[endPtr]) ++endPtr;
527+
528+
if (endPtr - idx > 16 && u8Array.subarray && UTF8Decoder) {
529+
return UTF8Decoder.decode(u8Array.subarray(idx, endPtr));
530+
} else {
531+
#endif
532+
var u0, u1, u2, u3, u4, u5;
533+
534+
var str = '';
535+
while (1) {
536+
// For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629
537+
u0 = u8Array[idx++];
538+
if (!u0) return str;
539+
if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }
540+
u1 = u8Array[idx++] & 63;
541+
if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }
542+
u2 = u8Array[idx++] & 63;
543+
if ((u0 & 0xF0) == 0xE0) {
544+
u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
536545
} else {
537-
u4 = u8Array[idx++] & 63;
538-
if ((u0 & 0xFC) == 0xF8) {
539-
u0 = ((u0 & 3) << 24) | (u1 << 18) | (u2 << 12) | (u3 << 6) | u4;
546+
u3 = u8Array[idx++] & 63;
547+
if ((u0 & 0xF8) == 0xF0) {
548+
u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | u3;
540549
} else {
541-
u5 = u8Array[idx++] & 63;
542-
u0 = ((u0 & 1) << 30) | (u1 << 24) | (u2 << 18) | (u3 << 12) | (u4 << 6) | u5;
550+
u4 = u8Array[idx++] & 63;
551+
if ((u0 & 0xFC) == 0xF8) {
552+
u0 = ((u0 & 3) << 24) | (u1 << 18) | (u2 << 12) | (u3 << 6) | u4;
553+
} else {
554+
u5 = u8Array[idx++] & 63;
555+
u0 = ((u0 & 1) << 30) | (u1 << 24) | (u2 << 18) | (u3 << 12) | (u4 << 6) | u5;
556+
}
543557
}
544558
}
559+
if (u0 < 0x10000) {
560+
str += String.fromCharCode(u0);
561+
} else {
562+
var ch = u0 - 0x10000;
563+
str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
564+
}
545565
}
546-
if (u0 < 0x10000) {
547-
str += String.fromCharCode(u0);
548-
} else {
549-
var ch = u0 - 0x10000;
550-
str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
551-
}
566+
#if TEXTDECODER
552567
}
568+
#endif
553569
}
554570
{{{ maybeExport('UTF8ArrayToString') }}}
555571

@@ -669,18 +685,36 @@ function lengthBytesUTF8(str) {
669685
// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns
670686
// a copy of that string as a Javascript String object.
671687

688+
var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined;
672689
function UTF16ToString(ptr) {
673-
var i = 0;
690+
#if ASSERTIONS
691+
assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!');
692+
#endif
693+
#if TEXTDECODER
694+
var endPtr = ptr;
695+
// TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself.
696+
// Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage.
697+
var idx = endPtr >> 1;
698+
while (HEAP16[idx]) ++idx;
699+
endPtr = idx << 1;
700+
701+
if (endPtr - ptr > 32 && UTF16Decoder) {
702+
return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr));
703+
} else {
704+
#endif
705+
var i = 0;
674706

675-
var str = '';
676-
while (1) {
677-
var codeUnit = {{{ makeGetValue('ptr', 'i*2', 'i16') }}};
678-
if (codeUnit == 0)
679-
return str;
680-
++i;
681-
// fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through.
682-
str += String.fromCharCode(codeUnit);
707+
var str = '';
708+
while (1) {
709+
var codeUnit = {{{ makeGetValue('ptr', 'i*2', 'i16') }}};
710+
if (codeUnit == 0) return str;
711+
++i;
712+
// fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through.
713+
str += String.fromCharCode(codeUnit);
714+
}
715+
#if TEXTDECODER
683716
}
717+
#endif
684718
}
685719
{{{ maybeExport('UTF16ToString') }}}
686720

@@ -696,6 +730,9 @@ function UTF16ToString(ptr) {
696730
// Returns the number of bytes written, EXCLUDING the null terminator.
697731

698732
function stringToUTF16(str, outPtr, maxBytesToWrite) {
733+
#if ASSERTIONS
734+
assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!');
735+
#endif
699736
#if ASSERTIONS
700737
assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
701738
#endif
@@ -727,6 +764,9 @@ function lengthBytesUTF16(str) {
727764
{{{ maybeExport('lengthBytesUTF16') }}}
728765

729766
function UTF32ToString(ptr) {
767+
#if ASSERTIONS
768+
assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!');
769+
#endif
730770
var i = 0;
731771

732772
var str = '';
@@ -759,6 +799,9 @@ function UTF32ToString(ptr) {
759799
// Returns the number of bytes written, EXCLUDING the null terminator.
760800

761801
function stringToUTF32(str, outPtr, maxBytesToWrite) {
802+
#if ASSERTIONS
803+
assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!');
804+
#endif
762805
#if ASSERTIONS
763806
assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');
764807
#endif

src/settings.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,4 +754,6 @@ var CYBERDWARF = 0; // see http://kripken.github.io/emscripten-site/docs/debuggi
754754

755755
var BUNDLED_CD_DEBUG_FILE = ""; // Path to the CyberDWARF debug file passed to the compiler
756756

757+
var TEXTDECODER = 1; // Is enabled, use the JavaScript TextDecoder API for string marshalling.
758+
// Enabled by default, set this to 0 to disable.
757759
// Reserved: variables containing POINTER_MASKING.

tests/ascii_corpus.txt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor.
2+
3+
Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula.
4+
5+
Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum.
6+
7+
Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue.
8+
9+
Cras lobortis sem ultrices leo. Donec magna fusce ac ante. Nullam est nisi blandit eget, suscipit vitae posuere quis ante. Quisque vitae tortor tellus feugiat adipiscing. Morbi ac elit et diam bibendum bibendum. Suspendisse id diam, donec adipiscing vulputate metus. Cras pellentesque vestibulum sem. Maecenas ut elit quis nisl vestibulum bibendum. Aenean eu erat quis turpis consequat vehicula. Morbi lacus velit, tristique ut iaculis volutpat in velit. Duis nec mauris et velit mollis aliquam, nullam posuere. Mauris at turpis sit amet dui imperdiet lobortis, proin eu felis.
10+
11+
Donec nec dui, in viverra tristique sapien. Suspendisse tincidunt consequat ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut lacinia luctus nunc. Etiam molestie hendrerit risus. Curabitur venenatis risus varius odio. Quisque elit ante, lacinia eget mollis sed, fermentum nec nisl. Nullam volutpat odio dolor tempor posuere. Suspendisse et elit vel sem interdum consequat. Aenean pulvinar nisl vel neque. Morbi mi ac neque ullamcorper dignissim. Nulla suscipit ipsum. Duis adipiscing turpis vitae turpis. In quis nisl ut tincidunt felis sit amet ipsum. Fusce facilisis nam tortor orci, facilisis sit amet accumsan vel, aliquam nec odio. Fusce accumsan libero et nisi. Lorem ipsum pede id faucibus aliquet, diam velit commodo elit, quis ultricies justo metus sit amet metus. Suspendisse interdum nulla sit amet enim. Etiam ultrices fusce nibh. Maecenas sed dolor vitae nisi volutpat commodo. Nulla interdum egestas lectus. Maecenas imperdiet arcu et orci.
12+
13+
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor.
14+
15+
Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula.
16+
17+
Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum.
18+
19+
Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue.
20+
21+
Cras lobortis sem ultrices leo. Donec magna fusce ac ante. Nullam est nisi blandit eget, suscipit vitae posuere quis ante. Quisque vitae tortor tellus feugiat adipiscing. Morbi ac elit et diam bibendum bibendum. Suspendisse id diam, donec adipiscing vulputate metus. Cras pellentesque vestibulum sem. Maecenas ut elit quis nisl vestibulum bibendum. Aenean eu erat quis turpis consequat vehicula. Morbi lacus velit, tristique ut iaculis volutpat in velit. Duis nec mauris et velit mollis aliquam, nullam posuere. Mauris at turpis sit amet dui imperdiet lobortis, proin eu felis.
22+
23+
Donec nec dui, in viverra tristique sapien. Suspendisse tincidunt consequat ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut lacinia luctus nunc. Etiam molestie hendrerit risus. Curabitur venenatis risus varius odio. Quisque elit ante, lacinia eget mollis sed, fermentum nec nisl. Nullam volutpat odio dolor tempor posuere. Suspendisse et elit vel sem interdum consequat. Aenean pulvinar nisl vel neque. Morbi mi ac neque ullamcorper dignissim. Nulla suscipit ipsum. Duis adipiscing turpis vitae turpis. In quis nisl ut tincidunt felis sit amet ipsum. Fusce facilisis nam tortor orci, facilisis sit amet accumsan vel, aliquam nec odio. Fusce accumsan libero et nisi. Lorem ipsum pede id faucibus aliquet, diam velit commodo elit, quis ultricies justo metus sit amet metus. Suspendisse interdum nulla sit amet enim. Etiam ultrices fusce nibh. Maecenas sed dolor vitae nisi volutpat commodo. Nulla interdum egestas lectus. Maecenas imperdiet arcu et orci.
24+
25+
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor.
26+
27+
Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula.
28+
29+
Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum.
30+
31+
Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue.
32+
33+
Cras lobortis sem ultrices leo. Donec magna fusce ac ante. Nullam est nisi blandit eget, suscipit vitae posuere quis ante. Quisque vitae tortor tellus feugiat adipiscing. Morbi ac elit et diam bibendum bibendum. Suspendisse id diam, donec adipiscing vulputate metus. Cras pellentesque vestibulum sem. Maecenas ut elit quis nisl vestibulum bibendum. Aenean eu erat quis turpis consequat vehicula. Morbi lacus velit, tristique ut iaculis volutpat in velit. Duis nec mauris et velit mollis aliquam, nullam posuere. Mauris at turpis sit amet dui imperdiet lobortis, proin eu felis.
34+
35+
Donec nec dui, in viverra tristique sapien. Suspendisse tincidunt consequat ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut lacinia luctus nunc. Etiam molestie hendrerit risus. Curabitur venenatis risus varius odio. Quisque elit ante, lacinia eget mollis sed, fermentum nec nisl. Nullam volutpat odio dolor tempor posuere. Suspendisse et elit vel sem interdum consequat. Aenean pulvinar nisl vel neque. Morbi mi ac neque ullamcorper dignissim. Nulla suscipit ipsum. Duis adipiscing turpis vitae turpis. In quis nisl ut tincidunt felis sit amet ipsum. Fusce facilisis nam tortor orci, facilisis sit amet accumsan vel, aliquam nec odio. Fusce accumsan libero et nisi. Lorem ipsum pede id faucibus aliquet, diam velit commodo elit, quis ultricies justo metus sit amet metus. Suspendisse interdum nulla sit amet enim. Etiam ultrices fusce nibh. Maecenas sed dolor vitae nisi volutpat commodo. Nulla interdum egestas lectus. Maecenas imperdiet arcu et orci.

tests/benchmark_utf16.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
#include <wchar.h>
4+
#include <iostream>
5+
#include <cassert>
6+
#include <emscripten.h>
7+
8+
double test(const unsigned short *str) {
9+
double res = EM_ASM_DOUBLE({
10+
var t0 = _emscripten_get_now();
11+
var str = Module.UTF16ToString($0);
12+
var t1 = _emscripten_get_now();
13+
Module.print('t: ' + (t1 - t0) + ', len(result): ' + str.length + ', result: ' + str.slice(0, 100));
14+
return (t1-t0);
15+
}, str);
16+
return res;
17+
}
18+
19+
unsigned short *utf16_corpus = 0;
20+
long utf16_corpus_length = 0;
21+
22+
unsigned short *randomString(int len) {
23+
if (!utf16_corpus) {
24+
// FILE *handle = fopen("ascii_corpus.txt", "rb");
25+
FILE *handle = fopen("utf16_corpus.txt", "rb");
26+
fseek(handle, 0, SEEK_END);
27+
utf16_corpus_length = ftell(handle)/2;
28+
assert(utf16_corpus_length > 0);
29+
utf16_corpus = new unsigned short[utf16_corpus_length+1];
30+
fseek(handle, 0, SEEK_SET);
31+
fread(utf16_corpus, 2, utf16_corpus_length, handle);
32+
fclose(handle);
33+
utf16_corpus[utf16_corpus_length] = 0;
34+
}
35+
int startIdx = rand() % (utf16_corpus_length - len);
36+
while((utf16_corpus[startIdx] & 0xFF00) == 0xDC00) {
37+
++startIdx;
38+
if (startIdx + len > utf16_corpus_length) len = utf16_corpus_length - startIdx;
39+
}
40+
assert(len > 0);
41+
unsigned short *s = new unsigned short[len+1];
42+
memcpy(s, utf16_corpus + startIdx, len*2);
43+
s[len] = 0;
44+
while(((unsigned short)s[len-1] & 0xFF00) == 0xD800) { s[--len] = 0; }
45+
assert(len >= 0);
46+
return s;
47+
}
48+
49+
int main() {
50+
srand(time(NULL));
51+
double t = 0;
52+
double t2 = emscripten_get_now();
53+
for(int i = 0; i < 10; ++i) {
54+
// FF Nightly: Already on small strings of 64 bytes in length, TextDecoder trumps in performance.
55+
unsigned short *str = randomString(100);
56+
t += test(str);
57+
free(str);
58+
}
59+
double t3 = emscripten_get_now();
60+
printf("OK. Time: %f (%f).\n", t, t3-t2);
61+
62+
#ifdef REPORT_RESULT
63+
int result = 0;
64+
REPORT_RESULT();
65+
#endif
66+
}

0 commit comments

Comments
 (0)