strings: copy-on-write

Only copy strings when they are mutated. This avoids copying strings in
cases where neither the copy nor the original would be modified during
the lifetime of the copy, e.g. when passing strings by-value.

This is purely an optimization.
This commit is contained in:
Nunuhara Cabbage
2019-10-20 21:26:51 -07:00
parent e5ef9f4d2d
commit b3a660f8ec
5 changed files with 203 additions and 147 deletions
+134 -103
View File
@@ -22,127 +22,52 @@
#include "vm_string.h"
#include "utfsjis.h"
int nr_strings = 0;
static struct string *alloc_string(int size)
{
nr_strings++;
return xmalloc(sizeof(struct string) + size + 1);
}
void free_string(struct string *str)
{
if (!str->ref)
ERROR("Double free of string object");
if (!--str->ref) {
free(str);
nr_strings--;
}
}
struct string *make_string(const char *str, unsigned int len)
{
struct string *s = xmalloc(sizeof(struct string) + len + 1);
struct string *s = alloc_string(len);
s->size = len;
s->literal = false;
s->ref = 1;
s->cow = 0;
memcpy(s->text, str, len);
s->text[len] = '\0';
return s;
}
void free_string(struct string *str)
struct string *string_ref(struct string *s)
{
if (!str->literal)
free(str);
s->cow = 1;
s->ref++;
return s;
}
struct string *string_dup(const struct string *in)
{
struct string *out = xmalloc(sizeof(struct string) + in->size + 1);
struct string *out = alloc_string(in->size);
out->size = in->size;
out->literal = false;
out->ref = 1;
out->cow = 0;
memcpy(out->text, in->text, in->size + 1);
return out;
}
void string_append(struct string **_a, const struct string *b)
{
struct string *a = *_a;
size_t a_size = a->size;
if (a->literal) {
a = xmalloc(sizeof(struct string) + a->size + b->size + 1);
a->literal = false;
memcpy(a->text, (*_a)->text, a_size+1);
} else {
a = xrealloc(a, sizeof(struct string) + a->size + b->size + 1);
}
a->size = a_size + b->size;
memcpy(a->text + a_size, b->text, b->size + 1);
*_a = a;
}
struct string *string_concatenate(const struct string *a, const struct string *b)
{
struct string *s = xmalloc(sizeof(struct string) + a->size + b->size + 1);
s->size = a->size + b->size;
s->literal = false;
memcpy(s->text, a->text, a->size);
memcpy(s->text + a->size, b->text, b->size + 1);
return s;
}
struct string *string_copy(const struct string *s, int index, int len)
{
if (index < 0)
index = 0;
if (len <= 0)
return make_string("", 0);
if ((index = sjis_index(s->text, index)) < 0)
return make_string("", 0);
if ((len = sjis_index(s->text + index, len)) < 0)
len = s->size - index;
return make_string(s->text + index, len);
}
int string_find(const struct string *haystack, const struct string *needle)
{
int c = 0;
for (int i = 0; i < haystack->size; i++, c++) {
if (!strncmp(haystack->text+i, needle->text, needle->size))
return c;
if (SJIS_2BYTE(haystack->text[i])) {
i++;
}
}
return -1;
}
void string_push_back(struct string **s, int c)
{
int bytes = SJIS_2BYTE(c) ? 2 : 1;
*s = xrealloc(*s, sizeof(struct string) + (*s)->size + bytes + 1);
(*s)->text[(*s)->size++] = c & 0xFF;
if (bytes == 2) {
(*s)->text[(*s)->size++] = c >> 8;
}
(*s)->text[(*s)->size] = '\0';
}
void string_pop_back(struct string *s)
{
// get index of last character
int c = 0;
for (int i = 0; i < s->size; i++) {
c = i;
if (SJIS_2BYTE(s->text[i])) {
i++;
}
}
s->text[c] = '\0';
s->size = c;
}
void string_erase(struct string *s, int index)
{
int bytes;
if (index < 0)
index = 0;
if (index >= s->size)
return;
if ((index = sjis_index(s->text, index)) < 0)
return;
bytes = SJIS_2BYTE(s->text[index]) ? 2 : 1;
memcpy(s->text + index, s->text + index + bytes, s->size - index - bytes);
s->size -= bytes;
s->text[s->size] = '\0';
}
struct string *integer_to_string(int n)
{
char buf[512];
@@ -163,3 +88,109 @@ struct string *float_to_string(float f, int precision)
len = snprintf(buf, 512, "%.*f", precision, f);
return make_string(buf, len);
}
struct string *string_concatenate(const struct string *a, const struct string *b)
{
struct string *s = alloc_string(a->size + b->size);
s->size = a->size + b->size;
s->ref = 1;
s->cow = 0;
memcpy(s->text, a->text, a->size);
memcpy(s->text + a->size, b->text, b->size + 1);
return s;
}
struct string *string_copy(const struct string *s, int index, int len)
{
if (index < 0)
index = 0;
if (len <= 0)
return make_string("", 0);
if ((index = sjis_index(s->text, index)) < 0)
return make_string("", 0);
if ((len = sjis_index(s->text + index, len)) < 0)
len = s->size - index;
return make_string(s->text + index, len);
}
static struct string *cow_check(struct string *s)
{
if (s->cow && s->ref > 1) {
struct string *out = string_dup(s);
free_string(s);
return out;
}
if (s->cow)
s->cow = 0;
return s;
}
void string_append(struct string **_a, const struct string *b)
{
struct string *a = *_a = cow_check(*_a);
size_t a_size = a->size;
a = xrealloc(a, sizeof(struct string) + a->size + b->size + 1);
a->size = a_size + b->size;
memcpy(a->text + a_size, b->text, b->size + 1);
*_a = a;
}
void string_push_back(struct string **s, int c)
{
int bytes = SJIS_2BYTE(c) ? 2 : 1;
*s = cow_check(*s);
*s = xrealloc(*s, sizeof(struct string) + (*s)->size + bytes + 1);
(*s)->text[(*s)->size++] = c & 0xFF;
if (bytes == 2) {
(*s)->text[(*s)->size++] = c >> 8;
}
(*s)->text[(*s)->size] = '\0';
}
void string_pop_back(struct string **s)
{
*s = cow_check(*s);
// get index of last character
int c = 0;
for (int i = 0; i < (*s)->size; i++) {
c = i;
if (SJIS_2BYTE((*s)->text[i])) {
i++;
}
}
(*s)->text[c] = '\0';
(*s)->size = c;
}
void string_erase(struct string **s, int index)
{
int bytes;
if (index < 0)
index = 0;
if (index >= (*s)->size)
return;
if ((index = sjis_index((*s)->text, index)) < 0)
return;
bytes = SJIS_2BYTE((*s)->text[index]) ? 2 : 1;
*s = cow_check(*s);
memcpy((*s)->text + index, (*s)->text + index + bytes, (*s)->size - index - bytes);
(*s)->size -= bytes;
(*s)->text[(*s)->size] = '\0';
}
int string_find(const struct string *haystack, const struct string *needle)
{
int c = 0;
for (int i = 0; i < haystack->size; i++, c++) {
if (!strncmp(haystack->text+i, needle->text, needle->size))
return c;
if (SJIS_2BYTE(haystack->text[i])) {
i++;
}
}
return -1;
}