summaryrefslogtreecommitdiffhomepage
path: root/vm.c
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-01-03 17:33:50 +0100
committerJo-Philipp Wich <jo@mein.io>2022-01-04 16:20:23 +0100
commit2fd7ab5f73048f3363c05a614dc3362a816af237 (patch)
tree85c2e4b329f327c1058efa1612c30f4726816292 /vm.c
parent5bb9ab77945ec429a4bdf9b5d317237d7619f8cc (diff)
vm: optimize string concatenation
When concatenating strings, avoid allocating three times the required memory in the worst case. Instead of first allocating the string representations of the operands followed by the memory for the final string, allocate a string buffer and print the operands into it. This will grow the target memory as needed and avoid redundant internal copies of the involved strings. Also handle the special where the final string fits into a tagged pointer and deal with it accordingly. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'vm.c')
-rw-r--r--vm.c53
1 files changed, 33 insertions, 20 deletions
diff --git a/vm.c b/vm.c
index abdb9b9..e7757cd 100644
--- a/vm.c
+++ b/vm.c
@@ -1346,11 +1346,40 @@ uc_vm_value_bitop(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
}
static uc_value_t *
+uc_vm_string_concat(uc_vm_t *vm, uc_value_t *v1, uc_value_t *v2)
+{
+ char buf[sizeof(void *)], *s1, *s2;
+ uc_stringbuf_t *sbuf;
+ size_t l1, l2;
+
+ /* optimize cases for string+string concat... */
+ if (ucv_type(v1) == UC_STRING && ucv_type(v2) == UC_STRING) {
+ s1 = ucv_string_get(v1);
+ s2 = ucv_string_get(v2);
+ l1 = ucv_string_length(v1);
+ l2 = ucv_string_length(v2);
+
+ /* ... result fits into a tagged pointer */
+ if (l1 + l2 + 1 < sizeof(buf)) {
+ memcpy(&buf[0], s1, l1);
+ memcpy(&buf[l1], s2, l2);
+
+ return ucv_string_new_length(buf, l1 + l2);
+ }
+ }
+
+ sbuf = ucv_stringbuf_new();
+
+ ucv_to_stringbuf(vm, sbuf, v1, false);
+ ucv_to_stringbuf(vm, sbuf, v2, false);
+
+ return ucv_stringbuf_finish(sbuf);
+}
+
+static uc_value_t *
uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_value_t *operand)
{
uc_value_t *nv1, *nv2, *rv = NULL;
- char *s, *s1, *s2;
- size_t len1, len2;
uint64_t u1, u2;
int64_t n1, n2;
double d1, d2;
@@ -1359,24 +1388,8 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
operation == I_BAND || operation == I_BXOR || operation == I_BOR)
return uc_vm_value_bitop(vm, operation, value, operand);
- if (operation == I_ADD && (ucv_type(value) == UC_STRING || ucv_type(operand) == UC_STRING)) {
- s1 = (ucv_type(value) != UC_STRING) ? ucv_to_string(vm, value) : NULL;
- s2 = (ucv_type(operand) != UC_STRING) ? ucv_to_string(vm, operand) : NULL;
- len1 = s1 ? strlen(s1) : ucv_string_length(value);
- len2 = s2 ? strlen(s2) : ucv_string_length(operand);
- s = xalloc(len1 + len2 + 1);
-
- memcpy(s, s1 ? s1 : ucv_string_get(value), len1);
- memcpy(s + len1, s2 ? s2 : ucv_string_get(operand), len2);
- free(s1);
- free(s2);
-
- rv = ucv_string_new_length(s, len1 + len2);
-
- free(s);
-
- return rv;
- }
+ if (operation == I_ADD && (ucv_type(value) == UC_STRING || ucv_type(operand) == UC_STRING))
+ return uc_vm_string_concat(vm, value, operand);
nv1 = ucv_to_number(value);
nv2 = ucv_to_number(operand);