From a14d3a62c47ad8db1de323f8eaf697c9eb586938 Mon Sep 17 00:00:00 2001 From: Yuichi Nishiwaki Date: Sat, 27 Feb 2016 00:44:32 +0900 Subject: [PATCH 1/4] don't use size_t for index --- extlib/benz/string.c | 75 +++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 46 deletions(-) diff --git a/extlib/benz/string.c b/extlib/benz/string.c index 0250e24b..d69d9ca8 100644 --- a/extlib/benz/string.c +++ b/extlib/benz/string.c @@ -9,15 +9,15 @@ struct chunk { char *str; int refcnt; - size_t len; + int len; char buf[1]; }; struct rope { int refcnt; - size_t weight; + int weight; struct chunk *chunk; - size_t offset; + int offset; struct rope *left, *right; }; @@ -26,9 +26,8 @@ struct rope { } while (0) #define CHUNK_DECREF(c) do { \ - struct chunk *c_ = (c); \ - if (! --c_->refcnt) { \ - pic_free(pic, c_); \ + if (! --(c)->refcnt) { \ + pic_free(pic, (c)); \ } \ } while (0) @@ -52,7 +51,7 @@ pic_rope_decref(pic_state *pic, struct rope *x) { } static struct chunk * -pic_make_chunk(pic_state *pic, const char *str, size_t len) +make_chunk(pic_state *pic, const char *str, int len) { struct chunk *c; @@ -67,7 +66,7 @@ pic_make_chunk(pic_state *pic, const char *str, size_t len) } static struct chunk * -pic_make_chunk_lit(pic_state *pic, const char *str, size_t len) +make_chunk_lit(pic_state *pic, const char *str, int len) { struct chunk *c; @@ -80,7 +79,7 @@ pic_make_chunk_lit(pic_state *pic, const char *str, size_t len) } static struct rope * -pic_make_rope(pic_state *pic, struct chunk *c) +make_rope(pic_state *pic, struct chunk *c) { struct rope *x; @@ -96,7 +95,7 @@ pic_make_rope(pic_state *pic, struct chunk *c) } static pic_value -pic_make_str(pic_state *pic, struct rope *rope) +make_str(pic_state *pic, struct rope *rope) { struct string *str; @@ -106,29 +105,6 @@ pic_make_str(pic_state *pic, struct rope *rope) return pic_obj_value(str); } -static size_t -rope_len(struct rope *x) -{ - return x->weight; -} - -static char -rope_at(struct rope *x, size_t i) -{ - while (i < x->weight) { - if (x->chunk) { - return x->chunk->str[x->offset + i]; - } - if (i < x->left->weight) { - x = x->left; - } else { - i -= x->left->weight; - x = x->right; - } - } - return -1; -} - static struct rope * rope_cat(pic_state *pic, struct rope *x, struct rope *y) { @@ -149,7 +125,7 @@ rope_cat(pic_state *pic, struct rope *x, struct rope *y) } static struct rope * -rope_sub(pic_state *pic, struct rope *x, size_t i, size_t j) +rope_sub(pic_state *pic, struct rope *x, int i, int j) { assert(i <= j); assert(j <= x->weight); @@ -196,7 +172,7 @@ rope_sub(pic_state *pic, struct rope *x, size_t i, size_t j) } static void -flatten(pic_state *pic, struct rope *x, struct chunk *c, size_t offset) +flatten(pic_state *pic, struct rope *x, struct chunk *c, int offset) { if (x->chunk) { memcpy(c->str + offset, x->chunk->str + x->offset, x->weight); @@ -253,44 +229,51 @@ pic_str_value(pic_state *pic, const char *str, int len) struct chunk *c; if (len > 0) { - c = pic_make_chunk(pic, str, len); + c = make_chunk(pic, str, len); } else { if (len == 0) { str = ""; } - c = pic_make_chunk_lit(pic, str, -len); + c = make_chunk_lit(pic, str, -len); } - return pic_make_str(pic, pic_make_rope(pic, c)); + return make_str(pic, make_rope(pic, c)); } int pic_str_len(pic_state *PIC_UNUSED(pic), pic_value str) { - return rope_len(pic_str_ptr(pic, str)->rope); + return pic_str_ptr(pic, str)->rope->weight; } char pic_str_ref(pic_state *pic, pic_value str, int i) { - int c; + struct rope *rope = pic_str_ptr(pic, str)->rope; - c = rope_at(pic_str_ptr(pic, str)->rope, i); - if (c == -1) { - pic_error(pic, "index out of range", 1, pic_int_value(pic, i)); + while (i < rope->weight) { + if (rope->chunk) { + return rope->chunk->str[rope->offset + i]; + } + if (i < rope->left->weight) { + rope = rope->left; + } else { + i -= rope->left->weight; + rope = rope->right; + } } - return (char)c; + PIC_UNREACHABLE(); } pic_value pic_str_cat(pic_state *pic, pic_value a, pic_value b) { - return pic_make_str(pic, rope_cat(pic, pic_str_ptr(pic, a)->rope, pic_str_ptr(pic, b)->rope)); + return make_str(pic, rope_cat(pic, pic_str_ptr(pic, a)->rope, pic_str_ptr(pic, b)->rope)); } pic_value pic_str_sub(pic_state *pic, pic_value str, int s, int e) { - return pic_make_str(pic, rope_sub(pic, pic_str_ptr(pic, str)->rope, s, e)); + return make_str(pic, rope_sub(pic, pic_str_ptr(pic, str)->rope, s, e)); } int From 8810d9425987f63b2c15dc7df4f857d7cf8757de Mon Sep 17 00:00:00 2001 From: Yuichi Nishiwaki Date: Sat, 27 Feb 2016 01:38:07 +0900 Subject: [PATCH 2/4] decrease sizeof struct rope --- extlib/benz/include/picrin/private/object.h | 2 +- extlib/benz/string.c | 272 ++++++++++---------- 2 files changed, 138 insertions(+), 136 deletions(-) diff --git a/extlib/benz/include/picrin/private/object.h b/extlib/benz/include/picrin/private/object.h index 71eda5f4..381ae8b0 100644 --- a/extlib/benz/include/picrin/private/object.h +++ b/extlib/benz/include/picrin/private/object.h @@ -198,7 +198,7 @@ void pic_put_identifier(pic_state *, pic_value id, pic_value uid, pic_value env) pic_value pic_find_identifier(pic_state *, pic_value id, pic_value env); pic_value pic_id_name(pic_state *, pic_value id); -void pic_rope_incref(pic_state *, struct rope *); +struct rope *pic_rope_incref(struct rope *); void pic_rope_decref(pic_state *, struct rope *); #define pic_func_p(pic, proc) (pic_type(pic, proc) == PIC_TYPE_FUNC) diff --git a/extlib/benz/string.c b/extlib/benz/string.c index d69d9ca8..839f8e70 100644 --- a/extlib/benz/string.c +++ b/extlib/benz/string.c @@ -7,8 +7,8 @@ #include "picrin/private/object.h" struct chunk { - char *str; int refcnt; + const char *str; int len; char buf[1]; }; @@ -16,9 +16,16 @@ struct chunk { struct rope { int refcnt; int weight; - struct chunk *chunk; - int offset; - struct rope *left, *right; + bool isleaf; + union { + struct { + struct chunk *chunk; + int offset; + } leaf; + struct { + struct rope *left, *right; + } node; + } u; }; #define CHUNK_INCREF(c) do { \ @@ -31,22 +38,22 @@ struct rope { } \ } while (0) -void -pic_rope_incref(pic_state *PIC_UNUSED(pic), struct rope *x) { - x->refcnt++; +struct rope * +pic_rope_incref(struct rope *rope) { + rope->refcnt++; + return rope; } void -pic_rope_decref(pic_state *pic, struct rope *x) { - if (! --x->refcnt) { - if (x->chunk) { - CHUNK_DECREF(x->chunk); - pic_free(pic, x); - return; +pic_rope_decref(pic_state *pic, struct rope *rope) { + if (! --rope->refcnt) { + if (rope->isleaf) { + CHUNK_DECREF(rope->u.leaf.chunk); + } else { + pic_rope_decref(pic, rope->u.node.left); + pic_rope_decref(pic, rope->u.node.right); } - pic_rope_decref(pic, x->left); - pic_rope_decref(pic, x->right); - pic_free(pic, x); + pic_free(pic, rope); } } @@ -60,8 +67,9 @@ make_chunk(pic_state *pic, const char *str, int len) c->str = c->buf; c->len = len; c->buf[len] = 0; - memcpy(c->buf, str, len); - + if (str) { + memcpy(c->buf, str, len); + } return c; } @@ -70,7 +78,7 @@ make_chunk_lit(pic_state *pic, const char *str, int len) { struct chunk *c; - c = pic_malloc(pic, sizeof(struct chunk)); + c = pic_malloc(pic, offsetof(struct chunk, buf)); c->refcnt = 1; c->str = (char *)str; c->len = len; @@ -79,19 +87,38 @@ make_chunk_lit(pic_state *pic, const char *str, int len) } static struct rope * -make_rope(pic_state *pic, struct chunk *c) +make_rope_leaf(pic_state *pic, struct chunk *c) { - struct rope *x; + struct rope *rope; - x = pic_malloc(pic, sizeof(struct rope)); - x->refcnt = 1; - x->left = NULL; - x->right = NULL; - x->weight = c->len; - x->offset = 0; - x->chunk = c; /* delegate ownership */ + rope = pic_malloc(pic, sizeof(struct rope)); + rope->refcnt = 1; + rope->weight = c->len; + rope->isleaf = true; + rope->u.leaf.offset = 0; + rope->u.leaf.chunk = c; /* delegate ownership */ - return x; + return rope; +} + +static struct rope * +make_rope_node(pic_state *pic, struct rope *left, struct rope *right) +{ + struct rope *rope; + + if (left == 0) + return pic_rope_incref(right); + if (right == 0) + return pic_rope_incref(left); + + rope = pic_malloc(pic, sizeof(struct rope)); + rope->refcnt = 1; + rope->weight = left->weight + right->weight; + rope->isleaf = false; + rope->u.node.left = pic_rope_incref(left); + rope->u.node.right = pic_rope_incref(right); + + return rope; } static pic_value @@ -106,110 +133,85 @@ make_str(pic_state *pic, struct rope *rope) } static struct rope * -rope_cat(pic_state *pic, struct rope *x, struct rope *y) +merge(pic_state *pic, struct rope *left, struct rope *right) { - struct rope *z; - - z = pic_malloc(pic, sizeof(struct rope)); - z->refcnt = 1; - z->left = x; - z->right = y; - z->weight = x->weight + y->weight; - z->offset = 0; - z->chunk = NULL; - - pic_rope_incref(pic, x); - pic_rope_incref(pic, y); - - return z; + return make_rope_node(pic, left, right); } static struct rope * -rope_sub(pic_state *pic, struct rope *x, int i, int j) +slice(pic_state *pic, struct rope *rope, int i, int j) { assert(i <= j); - assert(j <= x->weight); + assert(j <= rope->weight); - if (i == 0 && x->weight == j) { - pic_rope_incref(pic, x); - return x; + if (i == 0 && rope->weight == j) { + return pic_rope_incref(rope); } - if (x->chunk) { + if (rope->isleaf) { struct rope *y; - y = pic_malloc(pic, sizeof(struct rope)); - y->refcnt = 1; - y->left = NULL; - y->right = NULL; + y = make_rope_leaf(pic, rope->u.leaf.chunk); y->weight = j - i; - y->offset = x->offset + i; - y->chunk = x->chunk; + y->u.leaf.offset = rope->u.leaf.offset + i; - CHUNK_INCREF(x->chunk); + CHUNK_INCREF(rope->u.leaf.chunk); return y; } - if (j <= x->left->weight) { - return rope_sub(pic, x->left, i, j); - } - else if (x->left->weight <= i) { - return rope_sub(pic, x->right, i - x->left->weight, j - x->left->weight); - } - else { + if (j <= rope->u.node.left->weight) { + return slice(pic, rope->u.node.left, i, j); + } else if (rope->u.node.left->weight <= i) { + return slice(pic, rope->u.node.right, i - rope->u.node.left->weight, j - rope->u.node.left->weight); + } else { struct rope *r, *l; - l = rope_sub(pic, x->left, i, x->left->weight); - r = rope_sub(pic, x->right, 0, j - x->left->weight); - x = rope_cat(pic, l, r); + l = slice(pic, rope->u.node.left, i, rope->u.node.left->weight); + r = slice(pic, rope->u.node.right, 0, j - rope->u.node.left->weight); + rope = merge(pic, l, r); pic_rope_decref(pic, l); pic_rope_decref(pic, r); - return x; + return rope; } } static void -flatten(pic_state *pic, struct rope *x, struct chunk *c, int offset) +flatten(pic_state *pic, struct rope *rope, struct chunk *c, int offset) { - if (x->chunk) { - memcpy(c->str + offset, x->chunk->str + x->offset, x->weight); - CHUNK_DECREF(x->chunk); - - x->chunk = c; - x->offset = offset; + if (rope->isleaf) { + memcpy(c->buf + offset, rope->u.leaf.chunk->str + rope->u.leaf.offset, rope->weight); + CHUNK_DECREF(rope->u.leaf.chunk); + rope->u.leaf.chunk = c; + rope->u.leaf.offset = offset; CHUNK_INCREF(c); - return; - } - flatten(pic, x->left, c, offset); - flatten(pic, x->right, c, offset + x->left->weight); + } else { + flatten(pic, rope->u.node.left, c, offset); + flatten(pic, rope->u.node.right, c, offset + rope->u.node.left->weight); - pic_rope_decref(pic, x->left); - pic_rope_decref(pic, x->right); - x->left = x->right = NULL; - x->chunk = c; - x->offset = offset; - CHUNK_INCREF(c); + pic_rope_decref(pic, rope->u.node.left); + pic_rope_decref(pic, rope->u.node.right); + rope->isleaf = true; + rope->u.leaf.chunk = c; + rope->u.leaf.offset = offset; + CHUNK_INCREF(c); + } } static const char * -rope_cstr(pic_state *pic, struct rope *x) +rope_cstr(pic_state *pic, struct rope *rope) { struct chunk *c; - if (x->chunk && x->offset == 0 && x->weight == x->chunk->len) { - return x->chunk->str; /* reuse cached chunk */ + if (rope->isleaf && rope->u.leaf.offset == 0 && rope->weight == rope->u.leaf.chunk->len) { + return rope->u.leaf.chunk->str; /* reuse cached chunk */ } - c = pic_malloc(pic, offsetof(struct chunk, buf) + x->weight + 1); - c->refcnt = 1; - c->len = x->weight; - c->str = c->buf; - c->str[c->len] = '\0'; + c = make_chunk(pic, 0, rope->weight); - flatten(pic, x, c, 0); + flatten(pic, rope, c, 0); CHUNK_DECREF(c); return c->str; @@ -218,7 +220,7 @@ rope_cstr(pic_state *pic, struct rope *x) static void str_update(pic_state *pic, pic_value dst, pic_value src) { - pic_rope_incref(pic, pic_str_ptr(pic, src)->rope); + pic_rope_incref(pic_str_ptr(pic, src)->rope); pic_rope_decref(pic, pic_str_ptr(pic, dst)->rope); pic_str_ptr(pic, dst)->rope = pic_str_ptr(pic, src)->rope; } @@ -236,7 +238,37 @@ pic_str_value(pic_state *pic, const char *str, int len) } c = make_chunk_lit(pic, str, -len); } - return make_str(pic, make_rope(pic, c)); + return make_str(pic, make_rope_leaf(pic, c)); +} + +pic_value +pic_strf_value(pic_state *pic, const char *fmt, ...) +{ + va_list ap; + pic_value str; + + va_start(ap, fmt); + str = pic_vstrf_value(pic, fmt, ap); + va_end(ap); + + return str; +} + +pic_value +pic_vstrf_value(pic_state *pic, const char *fmt, va_list ap) +{ + pic_value str; + xFILE *file; + const char *buf; + int len; + + file = xfopen_buf(pic, NULL, 0, "w"); + + xvfprintf(pic, file, fmt, ap); + xfget_buf(pic, file, &buf, &len); + str = pic_str_value(pic, buf, len); + xfclose(pic, file); + return str; } int @@ -246,19 +278,19 @@ pic_str_len(pic_state *PIC_UNUSED(pic), pic_value str) } char -pic_str_ref(pic_state *pic, pic_value str, int i) +pic_str_ref(pic_state *PIC_UNUSED(pic), pic_value str, int i) { struct rope *rope = pic_str_ptr(pic, str)->rope; while (i < rope->weight) { - if (rope->chunk) { - return rope->chunk->str[rope->offset + i]; + if (rope->isleaf) { + return rope->u.leaf.chunk->str[rope->u.leaf.offset + i]; } - if (i < rope->left->weight) { - rope = rope->left; + if (i < rope->u.node.left->weight) { + rope = rope->u.node.left; } else { - i -= rope->left->weight; - rope = rope->right; + i -= rope->u.node.left->weight; + rope = rope->u.node.right; } } PIC_UNREACHABLE(); @@ -267,13 +299,13 @@ pic_str_ref(pic_state *pic, pic_value str, int i) pic_value pic_str_cat(pic_state *pic, pic_value a, pic_value b) { - return make_str(pic, rope_cat(pic, pic_str_ptr(pic, a)->rope, pic_str_ptr(pic, b)->rope)); + return make_str(pic, merge(pic, pic_str_ptr(pic, a)->rope, pic_str_ptr(pic, b)->rope)); } pic_value pic_str_sub(pic_state *pic, pic_value str, int s, int e) { - return make_str(pic, rope_sub(pic, pic_str_ptr(pic, str)->rope, s, e)); + return make_str(pic, slice(pic, pic_str_ptr(pic, str)->rope, s, e)); } int @@ -301,36 +333,6 @@ pic_str(pic_state *pic, pic_value str) return rope_cstr(pic, pic_str_ptr(pic, str)->rope); } -pic_value -pic_vstrf_value(pic_state *pic, const char *fmt, va_list ap) -{ - pic_value str; - xFILE *file; - const char *buf; - int len; - - file = xfopen_buf(pic, NULL, 0, "w"); - - xvfprintf(pic, file, fmt, ap); - xfget_buf(pic, file, &buf, &len); - str = pic_str_value(pic, buf, len); - xfclose(pic, file); - return str; -} - -pic_value -pic_strf_value(pic_state *pic, const char *fmt, ...) -{ - va_list ap; - pic_value str; - - va_start(ap, fmt); - str = pic_vstrf_value(pic, fmt, ap); - va_end(ap); - - return str; -} - static pic_value pic_str_string_p(pic_state *pic) { From d2167a87f960e775cc51904caf2c81e2e6b11b50 Mon Sep 17 00:00:00 2001 From: Yuichi Nishiwaki Date: Sat, 27 Feb 2016 01:55:49 +0900 Subject: [PATCH 3/4] optimize rope --- extlib/benz/string.c | 84 ++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/extlib/benz/string.c b/extlib/benz/string.c index 839f8e70..76f50550 100644 --- a/extlib/benz/string.c +++ b/extlib/benz/string.c @@ -20,7 +20,7 @@ struct rope { union { struct { struct chunk *chunk; - int offset; + const char *str; } leaf; struct { struct rope *left, *right; @@ -80,7 +80,7 @@ make_chunk_lit(pic_state *pic, const char *str, int len) c = pic_malloc(pic, offsetof(struct chunk, buf)); c->refcnt = 1; - c->str = (char *)str; + c->str = str; c->len = len; return c; @@ -95,8 +95,8 @@ make_rope_leaf(pic_state *pic, struct chunk *c) rope->refcnt = 1; rope->weight = c->len; rope->isleaf = true; - rope->u.leaf.offset = 0; - rope->u.leaf.chunk = c; /* delegate ownership */ + rope->u.leaf.chunk = c; /* delegate ownership */ + rope->u.leaf.str = c->str; return rope; } @@ -106,11 +106,6 @@ make_rope_node(pic_state *pic, struct rope *left, struct rope *right) { struct rope *rope; - if (left == 0) - return pic_rope_incref(right); - if (right == 0) - return pic_rope_incref(left); - rope = pic_malloc(pic, sizeof(struct rope)); rope->refcnt = 1; rope->weight = left->weight + right->weight; @@ -135,29 +130,31 @@ make_str(pic_state *pic, struct rope *rope) static struct rope * merge(pic_state *pic, struct rope *left, struct rope *right) { + if (left == 0) + return pic_rope_incref(right); + if (right == 0) + return pic_rope_incref(left); + return make_rope_node(pic, left, right); } static struct rope * slice(pic_state *pic, struct rope *rope, int i, int j) { - assert(i <= j); - assert(j <= rope->weight); - if (i == 0 && rope->weight == j) { return pic_rope_incref(rope); } if (rope->isleaf) { - struct rope *y; + struct rope *r; - y = make_rope_leaf(pic, rope->u.leaf.chunk); - y->weight = j - i; - y->u.leaf.offset = rope->u.leaf.offset + i; + r = make_rope_leaf(pic, rope->u.leaf.chunk); + r->weight = j - i; + r->u.leaf.str += i; CHUNK_INCREF(rope->u.leaf.chunk); - return y; + return r; } if (j <= rope->u.node.left->weight) { @@ -179,42 +176,27 @@ slice(pic_state *pic, struct rope *rope, int i, int j) } static void -flatten(pic_state *pic, struct rope *rope, struct chunk *c, int offset) +flatten(pic_state *pic, struct rope *rope, struct chunk *c, char *buf) { if (rope->isleaf) { - memcpy(c->buf + offset, rope->u.leaf.chunk->str + rope->u.leaf.offset, rope->weight); + memcpy(buf, rope->u.leaf.str, rope->weight); + } else { + flatten(pic, rope->u.node.left, c, buf); + flatten(pic, rope->u.node.right, c, buf + rope->u.node.left->weight); + } + + if (rope->isleaf) { CHUNK_DECREF(rope->u.leaf.chunk); rope->u.leaf.chunk = c; - rope->u.leaf.offset = offset; - CHUNK_INCREF(c); + rope->u.leaf.str = buf; } else { - flatten(pic, rope->u.node.left, c, offset); - flatten(pic, rope->u.node.right, c, offset + rope->u.node.left->weight); - pic_rope_decref(pic, rope->u.node.left); pic_rope_decref(pic, rope->u.node.right); rope->isleaf = true; rope->u.leaf.chunk = c; - rope->u.leaf.offset = offset; - CHUNK_INCREF(c); + rope->u.leaf.str = buf; } -} - -static const char * -rope_cstr(pic_state *pic, struct rope *rope) -{ - struct chunk *c; - - if (rope->isleaf && rope->u.leaf.offset == 0 && rope->weight == rope->u.leaf.chunk->len) { - return rope->u.leaf.chunk->str; /* reuse cached chunk */ - } - - c = make_chunk(pic, 0, rope->weight); - - flatten(pic, rope, c, 0); - - CHUNK_DECREF(c); - return c->str; + CHUNK_INCREF(c); } static void @@ -284,7 +266,7 @@ pic_str_ref(pic_state *PIC_UNUSED(pic), pic_value str, int i) while (i < rope->weight) { if (rope->isleaf) { - return rope->u.leaf.chunk->str[rope->u.leaf.offset + i]; + return rope->u.leaf.str[i]; } if (i < rope->u.node.left->weight) { rope = rope->u.node.left; @@ -330,7 +312,19 @@ pic_str_hash(pic_state *pic, pic_value str) const char * pic_str(pic_state *pic, pic_value str) { - return rope_cstr(pic, pic_str_ptr(pic, str)->rope); + struct rope *rope = pic_str_ptr(pic, str)->rope; + struct chunk *c; + + if (rope->isleaf && rope->u.leaf.str + rope->weight == rope->u.leaf.chunk->str + rope->u.leaf.chunk->len) { + return rope->u.leaf.str; /* reuse cached chunk */ + } + + c = make_chunk(pic, 0, rope->weight); + + flatten(pic, rope, c, c->buf); + + CHUNK_DECREF(c); + return c->str; } static pic_value From ae88bed481896291682d75ea12fc77a31b014a0a Mon Sep 17 00:00:00 2001 From: Yuichi Nishiwaki Date: Mon, 29 Feb 2016 16:48:38 +0900 Subject: [PATCH 4/4] remove struct chunk --- extlib/benz/string.c | 171 ++++++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 90 deletions(-) diff --git a/extlib/benz/string.c b/extlib/benz/string.c index 76f50550..939bcbaa 100644 --- a/extlib/benz/string.c +++ b/extlib/benz/string.c @@ -6,38 +6,22 @@ #include "picrin/extra.h" #include "picrin/private/object.h" -struct chunk { - int refcnt; - const char *str; - int len; - char buf[1]; -}; - struct rope { int refcnt; int weight; bool isleaf; union { struct { - struct chunk *chunk; - const char *str; + struct rope *owner; + const char *str; /* always points to zero-term'd buf */ } leaf; struct { struct rope *left, *right; } node; } u; + char buf[1]; }; -#define CHUNK_INCREF(c) do { \ - (c)->refcnt++; \ - } while (0) - -#define CHUNK_DECREF(c) do { \ - if (! --(c)->refcnt) { \ - pic_free(pic, (c)); \ - } \ - } while (0) - struct rope * pic_rope_incref(struct rope *rope) { rope->refcnt++; @@ -48,7 +32,9 @@ void pic_rope_decref(pic_state *pic, struct rope *rope) { if (! --rope->refcnt) { if (rope->isleaf) { - CHUNK_DECREF(rope->u.leaf.chunk); + if (rope->u.leaf.owner) { + pic_rope_decref(pic, rope->u.leaf.owner); + } } else { pic_rope_decref(pic, rope->u.node.left); pic_rope_decref(pic, rope->u.node.right); @@ -57,46 +43,59 @@ pic_rope_decref(pic_state *pic, struct rope *rope) { } } -static struct chunk * -make_chunk(pic_state *pic, const char *str, int len) -{ - struct chunk *c; - - c = pic_malloc(pic, offsetof(struct chunk, buf) + len + 1); - c->refcnt = 1; - c->str = c->buf; - c->len = len; - c->buf[len] = 0; - if (str) { - memcpy(c->buf, str, len); - } - return c; -} - -static struct chunk * -make_chunk_lit(pic_state *pic, const char *str, int len) -{ - struct chunk *c; - - c = pic_malloc(pic, offsetof(struct chunk, buf)); - c->refcnt = 1; - c->str = str; - c->len = len; - - return c; -} - static struct rope * -make_rope_leaf(pic_state *pic, struct chunk *c) +make_rope_leaf(pic_state *pic, const char *str, int len) { struct rope *rope; - rope = pic_malloc(pic, sizeof(struct rope)); + rope = pic_malloc(pic, offsetof(struct rope, buf) + len + 1); rope->refcnt = 1; - rope->weight = c->len; + rope->weight = len; rope->isleaf = true; - rope->u.leaf.chunk = c; /* delegate ownership */ - rope->u.leaf.str = c->str; + rope->u.leaf.owner = NULL; + rope->u.leaf.str = rope->buf; + rope->buf[len] = 0; + if (str) { + memcpy(rope->buf, str, len); + } + + return rope; +} + +static struct rope * +make_rope_lit(pic_state *pic, const char *str, int len) +{ + struct rope *rope; + + rope = pic_malloc(pic, offsetof(struct rope, buf)); + rope->refcnt = 1; + rope->weight = len; + rope->isleaf = true; + rope->u.leaf.owner = NULL; + rope->u.leaf.str = str; + + return rope; +} + +static struct rope * +make_rope_slice(pic_state *pic, struct rope *owner, int i, int j) +{ + struct rope *rope; + + assert(owner->isleaf); + + if (owner->u.leaf.owner != NULL) { + owner = owner->u.leaf.owner; + } + + rope = pic_malloc(pic, offsetof(struct rope, buf)); + rope->refcnt = 1; + rope->weight = j - i; + rope->isleaf = true; + rope->u.leaf.owner = owner; + rope->u.leaf.str = owner->u.leaf.str + i; + + pic_rope_incref(owner); return rope; } @@ -141,31 +140,27 @@ merge(pic_state *pic, struct rope *left, struct rope *right) static struct rope * slice(pic_state *pic, struct rope *rope, int i, int j) { + int lweight; + if (i == 0 && rope->weight == j) { return pic_rope_incref(rope); } if (rope->isleaf) { - struct rope *r; - - r = make_rope_leaf(pic, rope->u.leaf.chunk); - r->weight = j - i; - r->u.leaf.str += i; - - CHUNK_INCREF(rope->u.leaf.chunk); - - return r; + return make_rope_slice(pic, rope, i, j); } - if (j <= rope->u.node.left->weight) { + lweight = rope->u.node.left->weight; + + if (j <= lweight) { return slice(pic, rope->u.node.left, i, j); - } else if (rope->u.node.left->weight <= i) { - return slice(pic, rope->u.node.right, i - rope->u.node.left->weight, j - rope->u.node.left->weight); + } else if (lweight <= i) { + return slice(pic, rope->u.node.right, i - lweight, j - lweight); } else { struct rope *r, *l; - l = slice(pic, rope->u.node.left, i, rope->u.node.left->weight); - r = slice(pic, rope->u.node.right, 0, j - rope->u.node.left->weight); + l = slice(pic, rope->u.node.left, i, lweight); + r = slice(pic, rope->u.node.right, 0, j - lweight); rope = merge(pic, l, r); pic_rope_decref(pic, l); @@ -176,27 +171,25 @@ slice(pic_state *pic, struct rope *rope, int i, int j) } static void -flatten(pic_state *pic, struct rope *rope, struct chunk *c, char *buf) +flatten(pic_state *pic, struct rope *rope, struct rope *owner, char *buf) { if (rope->isleaf) { memcpy(buf, rope->u.leaf.str, rope->weight); } else { - flatten(pic, rope->u.node.left, c, buf); - flatten(pic, rope->u.node.right, c, buf + rope->u.node.left->weight); + flatten(pic, rope->u.node.left, owner, buf); + flatten(pic, rope->u.node.right, owner, buf + rope->u.node.left->weight); } - if (rope->isleaf) { - CHUNK_DECREF(rope->u.leaf.chunk); - rope->u.leaf.chunk = c; - rope->u.leaf.str = buf; - } else { + /* path compression */ + + if (! rope->isleaf) { + pic_rope_incref(owner); pic_rope_decref(pic, rope->u.node.left); pic_rope_decref(pic, rope->u.node.right); rope->isleaf = true; - rope->u.leaf.chunk = c; + rope->u.leaf.owner = owner; rope->u.leaf.str = buf; } - CHUNK_INCREF(c); } static void @@ -210,17 +203,17 @@ str_update(pic_state *pic, pic_value dst, pic_value src) pic_value pic_str_value(pic_state *pic, const char *str, int len) { - struct chunk *c; + struct rope *r; if (len > 0) { - c = make_chunk(pic, str, len); + r = make_rope_leaf(pic, str, len); } else { if (len == 0) { str = ""; } - c = make_chunk_lit(pic, str, -len); + r = make_rope_lit(pic, str, -len); } - return make_str(pic, make_rope_leaf(pic, c)); + return make_str(pic, r); } pic_value @@ -312,19 +305,17 @@ pic_str_hash(pic_state *pic, pic_value str) const char * pic_str(pic_state *pic, pic_value str) { - struct rope *rope = pic_str_ptr(pic, str)->rope; - struct chunk *c; + struct rope *rope = pic_str_ptr(pic, str)->rope, *r; - if (rope->isleaf && rope->u.leaf.str + rope->weight == rope->u.leaf.chunk->str + rope->u.leaf.chunk->len) { - return rope->u.leaf.str; /* reuse cached chunk */ + if (rope->isleaf && rope->u.leaf.str[rope->weight] == '\0') { + return rope->u.leaf.str; } - c = make_chunk(pic, 0, rope->weight); + r = make_rope_leaf(pic, 0, rope->weight); - flatten(pic, rope, c, c->buf); + flatten(pic, rope, r, r->buf); - CHUNK_DECREF(c); - return c->str; + return r->u.leaf.str; } static pic_value