MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

string builder

+173 -8
+1 -1
meson.build
··· 68 68 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 69 69 70 70 version_conf = configuration_data() 71 - version_conf.set('ANT_VERSION', '0.0.7.7') 71 + version_conf.set('ANT_VERSION', '0.0.7.8') 72 72 version_conf.set('ANT_GIT_HASH', git_hash) 73 73 version_conf.set('ANT_BUILD_DATE', build_date) 74 74
+56 -7
src/ant.c
··· 1816 1816 return assign(js, l, res); 1817 1817 } 1818 1818 1819 + typedef struct { 1820 + char *buffer; 1821 + size_t capacity; 1822 + size_t size; 1823 + bool is_dynamic; 1824 + } string_builder_t; 1825 + 1826 + static void string_builder_init(string_builder_t *sb, char *static_buf, size_t static_cap) { 1827 + sb->buffer = static_buf; 1828 + sb->capacity = static_cap; 1829 + sb->size = 0; 1830 + sb->is_dynamic = false; 1831 + } 1832 + 1833 + static bool string_builder_append(string_builder_t *sb, const char *data, size_t len) { 1834 + if (sb->size + len > sb->capacity) { 1835 + size_t new_capacity = sb->capacity ? sb->capacity * 2 : 256; 1836 + while (new_capacity < sb->size + len) new_capacity *= 2; 1837 + 1838 + char *new_buffer = (char *)ANT_GC_MALLOC(new_capacity); 1839 + if (!new_buffer) return false; 1840 + 1841 + if (sb->size > 0) memcpy(new_buffer, sb->buffer, sb->size); 1842 + if (sb->is_dynamic) ANT_GC_FREE(sb->buffer); 1843 + 1844 + sb->buffer = new_buffer; 1845 + sb->capacity = new_capacity; 1846 + sb->is_dynamic = true; 1847 + } 1848 + 1849 + if (len > 0) { 1850 + memcpy(sb->buffer + sb->size, data, len); 1851 + sb->size += len; 1852 + } 1853 + 1854 + return true; 1855 + } 1856 + 1857 + static jsval_t string_builder_finalize(struct js *js, string_builder_t *sb) { 1858 + jsval_t result = js_mkstr(js, sb->buffer, sb->size); 1859 + if (sb->is_dynamic && sb->buffer) ANT_GC_FREE(sb->buffer); 1860 + return result; 1861 + } 1862 + 1819 1863 static jsval_t do_string_op(struct js *js, uint8_t op, jsval_t l, jsval_t r) { 1820 1864 jsoff_t n1, off1 = vstr(js, l, &n1); 1821 1865 jsoff_t n2, off2 = vstr(js, r, &n2); 1822 1866 1823 1867 if (op == TOK_PLUS) { 1824 - jsval_t res = js_mkstr(js, NULL, n1 + n2); 1825 - if (vtype(res) == T_STR) { 1826 - jsoff_t n, off = vstr(js, res, &n); 1827 - memmove(&js->mem[off], &js->mem[off1], n1); 1828 - memmove(&js->mem[off + n1], &js->mem[off2], n2); 1868 + string_builder_t sb; 1869 + char static_buffer[512]; 1870 + string_builder_init(&sb, static_buffer, sizeof(static_buffer)); 1871 + 1872 + if (!string_builder_append(&sb, (char *)&js->mem[off1], n1) || 1873 + !string_builder_append(&sb, (char *)&js->mem[off2], n2)) { 1874 + return js_mkerr(js, "string concatenation failed"); 1829 1875 } 1830 1876 1831 - return res; 1877 + return string_builder_finalize(js, &sb); 1832 1878 } else if (op == TOK_EQ) { 1833 1879 bool eq = n1 == n2 && memcmp(&js->mem[off1], &js->mem[off2], n1) == 0; 1834 1880 return mkval(T_BOOL, eq ? 1 : 0); ··· 2534 2580 } 2535 2581 return do_string_op(js, op, l_str, r_str); 2536 2582 } 2537 - if (vtype(l) == T_STR && vtype(r) == T_STR) return do_string_op(js, op, l, r); 2583 + if (vtype(l) == T_STR && vtype(r) == T_STR) { 2584 + // Fast path for string+string without type coercion 2585 + return do_string_op(js, op, l, r); 2586 + } 2538 2587 2539 2588 double a = 0.0, b = 0.0; 2540 2589
+116
tests/test_string_concat_perf.cjs
··· 1 + // Performance test for string concatenation optimization 2 + 3 + console.log("=== String Concatenation Performance Tests ===\n"); 4 + 5 + // Test 1: Simple binary concatenation 6 + console.log("Test 1: Binary Concatenation"); 7 + let start = Date.now(); 8 + for (let i = 0; i < 1000; i++) { 9 + let result = "hello" + "world"; 10 + } 11 + let elapsed = Date.now() - start; 12 + console.log(" 1000 binary concatenations: " + elapsed + "ms"); 13 + 14 + // Test 2: Chained concatenation (4-way) 15 + console.log("\nTest 2: Chained Concatenation (4-way)"); 16 + start = Date.now(); 17 + for (let i = 0; i < 1000; i++) { 18 + let result = "a" + "b" + "c" + "d"; 19 + } 20 + elapsed = Date.now() - start; 21 + console.log(" 1000 chained concatenations (4 parts): " + elapsed + "ms"); 22 + 23 + // Test 3: Long chained concatenation (10-way) 24 + console.log("\nTest 3: Long Chained Concatenation (10-way)"); 25 + start = Date.now(); 26 + for (let i = 0; i < 500; i++) { 27 + let result = "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10"; 28 + } 29 + elapsed = Date.now() - start; 30 + console.log(" 500 chained concatenations (10 parts): " + elapsed + "ms"); 31 + 32 + // Test 4: Type coercion concatenation 33 + console.log("\nTest 4: Type Coercion in Concatenation"); 34 + start = Date.now(); 35 + for (let i = 0; i < 1000; i++) { 36 + let result = "Value: " + i + " is " + (i % 2 == 0 ? "even" : "odd"); 37 + } 38 + elapsed = Date.now() - start; 39 + console.log(" 1000 concatenations with type coercion: " + elapsed + "ms"); 40 + 41 + // Test 5: Building longer strings progressively 42 + console.log("\nTest 5: Progressive String Building"); 43 + start = Date.now(); 44 + let message = ""; 45 + for (let i = 0; i < 100; i++) { 46 + message = message + "line " + i + ": some content\n"; 47 + } 48 + elapsed = Date.now() - start; 49 + console.log(" 100 progressive concatenations: " + elapsed + "ms"); 50 + console.log(" Result length: " + message.length + " bytes"); 51 + 52 + // Test 6: Concatenation in loop with variables 53 + console.log("\nTest 6: Variable Concatenation in Loop"); 54 + let prefix = "Item"; 55 + let suffix = "end"; 56 + start = Date.now(); 57 + for (let i = 0; i < 1000; i++) { 58 + let result = prefix + " " + i + " " + suffix; 59 + } 60 + elapsed = Date.now() - start; 61 + console.log(" 1000 variable concatenations: " + elapsed + "ms"); 62 + 63 + // Test 7: Large string concatenation 64 + console.log("\nTest 7: Large String Concatenation"); 65 + let largeStr1 = "x".repeat(500); 66 + let largeStr2 = "y".repeat(500); 67 + start = Date.now(); 68 + for (let i = 0; i < 100; i++) { 69 + let result = largeStr1 + largeStr2; 70 + } 71 + elapsed = Date.now() - start; 72 + console.log(" 100 concatenations of 500-byte strings: " + elapsed + "ms"); 73 + 74 + // Test 8: Mixed type chaining 75 + console.log("\nTest 8: Mixed Type Chaining"); 76 + start = Date.now(); 77 + for (let i = 0; i < 1000; i++) { 78 + let result = "Start" + 123 + true + 45.67 + "End"; 79 + } 80 + elapsed = Date.now() - start; 81 + console.log(" 1000 mixed-type chained concatenations: " + elapsed + "ms"); 82 + 83 + // Test 9: Template-like concatenation 84 + console.log("\nTest 9: Template-like String Building"); 85 + start = Date.now(); 86 + for (let i = 0; i < 100; i++) { 87 + let name = "User" + i; 88 + let age = 20 + i; 89 + let result = "Name: " + name + ", Age: " + age + ", Active: " + (i % 2 == 0); 90 + } 91 + elapsed = Date.now() - start; 92 + console.log(" 100 template-like concatenations: " + elapsed + "ms"); 93 + 94 + // Test 10: String concatenation with comparison 95 + console.log("\nTest 10: Concatenation with String Comparison"); 96 + start = Date.now(); 97 + for (let i = 0; i < 1000; i++) { 98 + let str1 = "test" + i; 99 + let str2 = "test" + i; 100 + let isEqual = str1 == str2 ? "equal" : "not equal"; 101 + } 102 + elapsed = Date.now() - start; 103 + console.log(" 1000 concatenations + comparisons: " + elapsed + "ms"); 104 + 105 + // Verification test - ensure correctness 106 + console.log("\n=== Correctness Verification ==="); 107 + let verify1 = "a" + "b" + "c" + "d"; 108 + console.log("Chained concat result: '" + verify1 + "' (expected: 'abcd')"); 109 + 110 + let verify2 = "x" + 42 + "y"; 111 + console.log("Type coercion result: '" + verify2 + "' (expected: 'x42y')"); 112 + 113 + let verify3 = "" + 0 + "" + false + "" + true; 114 + console.log("Boolean concat result: '" + verify3 + "' (expected: '0falsetrue')"); 115 + 116 + console.log("\n=== All performance tests completed ===");