diff --git a/include/picrin.h b/include/picrin.h index eb42720c..e125260f 100644 --- a/include/picrin.h +++ b/include/picrin.h @@ -36,6 +36,7 @@ extern "C" { #include "xhash/xhash.h" #include "xfile/xfile.h" +#include "xrope/xrope.h" #if __STDC_VERSION__ >= 201112L # define NORETURN _Noreturn diff --git a/include/picrin/string.h b/include/picrin/string.h index da342bd5..c2564ffe 100644 --- a/include/picrin/string.h +++ b/include/picrin/string.h @@ -11,8 +11,7 @@ extern "C" { struct pic_string { PIC_OBJECT_HEADER - char *str; - size_t len; + xrope *rope; }; #define pic_str_p(v) (pic_type(v) == PIC_TT_STRING) @@ -30,6 +29,8 @@ pic_str *pic_strcat(pic_state *, pic_str *, pic_str *); pic_str *pic_substr(pic_state *, pic_str *, size_t, size_t); int pic_strcmp(pic_str *, pic_str *); +const char *pic_str_cstr(pic_str *); + pic_value pic_format(pic_state *, const char *, ...); pic_value pic_vformat(pic_state *, const char *, va_list); pic_value pic_vfformat(pic_state *, xFILE *, const char *, va_list); diff --git a/src/error.c b/src/error.c index 81cc3403..0522bc1b 100644 --- a/src/error.c +++ b/src/error.c @@ -16,7 +16,7 @@ pic_errmsg(pic_state *pic) { assert(pic->err != NULL); - return pic->err->msg->str; + return pic_str_cstr(pic->err->msg); } NORETURN static void diff --git a/src/gc.c b/src/gc.c index 26d81341..016b448e 100644 --- a/src/gc.c +++ b/src/gc.c @@ -595,7 +595,7 @@ gc_finalize_object(pic_state *pic, struct pic_object *obj) break; } case PIC_TT_STRING: { - pic_free(pic, (void*)((struct pic_string *)obj)->str); + XROPE_DECREF(((struct pic_string *)obj)->rope); break; } case PIC_TT_PORT: { diff --git a/src/macro.c b/src/macro.c index 3b0642ea..13a760e8 100644 --- a/src/macro.c +++ b/src/macro.c @@ -619,11 +619,11 @@ pic_macro_include(pic_state *pic) body = pic_list(pic, 1, pic_symbol_value(pic->sBEGIN)); for (i = 0; i < argc; ++i) { - char *filename; + const char *filename; if (! pic_str_p(argv[i])) { pic_error(pic, "expected string"); } - filename = pic_str_ptr(argv[i])->str; + filename = pic_str_cstr(pic_str_ptr(argv[i])); file = fopen(filename, "r"); if (file == NULL) { pic_error(pic, "could not open file"); diff --git a/src/parse.y b/src/parse.y index 7374b51b..3f615887 100644 --- a/src/parse.y +++ b/src/parse.y @@ -94,8 +94,7 @@ yy_str_new_cstr(struct parser_control *p, const char *cstr) struct pic_string *str; str = (struct pic_string *)yy_obj_alloc(p, sizeof(struct pic_string), PIC_TT_STRING); - str->len = strlen(cstr); - str->str = pic_strdup(p->pic, cstr); + str->rope = xr_new_volatile(cstr, strlen(cstr)); return pic_obj_value(str); } diff --git a/src/port.c b/src/port.c index ae40285a..84f60297 100644 --- a/src/port.c +++ b/src/port.c @@ -414,7 +414,7 @@ pic_port_read_line(pic_state *pic) } str = pic_get_output_string(pic, buf); - if (str->len == 0 && c == EOF) { + if (pic_strlen(str) == 0 && c == EOF) { return pic_eof_object(); } else { diff --git a/src/string.c b/src/string.c index 2bcdce92..9dc31975 100644 --- a/src/string.c +++ b/src/string.c @@ -9,24 +9,25 @@ #include "picrin/pair.h" #include "picrin/port.h" -pic_str * -pic_str_new(pic_state *pic, const char *cstr, size_t len) +static pic_str * +str_new_rope(pic_state *pic, xrope *rope) { pic_str *str; - char *copy; - - if (cstr) { - copy = pic_strdup(pic, cstr); - } else { - copy = (char *)pic_alloc(pic, len); - } str = (pic_str *)pic_obj_alloc(pic, sizeof(pic_str), PIC_TT_STRING); - str->len = len; - str->str = copy; + str->rope = rope; /* delegate ownership */ return str; } +pic_str * +pic_str_new(pic_state *pic, const char *imbed, size_t len) +{ + if (imbed == NULL && len > 0) { + pic_errorf(pic, "zero length specified against NULL ptr"); + } + return str_new_rope(pic, xr_new_volatile(imbed, len)); +} + pic_str * pic_str_new_cstr(pic_state *pic, const char *cstr) { @@ -38,73 +39,100 @@ pic_str_new_fill(pic_state *pic, size_t len, char fill) { size_t i; char *cstr; + pic_str *str; cstr = (char *)pic_alloc(pic, len + 1); + cstr[len] = '\0'; for (i = 0; i < len; ++i) { cstr[i] = fill; } - cstr[len] = '\0'; - return pic_str_new(pic, cstr, len); + str = pic_str_new(pic, cstr, len); + + pic_free(pic, cstr); + return str; } size_t pic_strlen(pic_str *str) { - return str->len; + return xr_len(str->rope); } char -pic_str_ref(pic_state *pic, pic_str *str, size_t n) +pic_str_ref(pic_state *pic, pic_str *str, size_t i) { - UNUSED(pic); + char c; - return str->str[n]; + c = xr_at(str->rope, i); + if (c == -1) { + pic_errorf(pic, "index out of range %d", i); + } + return c; +} + +static xrope * +xr_put(xrope *rope, size_t i, char c) +{ + xrope *x, *y; + char buf[1]; + + if (xr_len(rope) <= i) { + return NULL; + } + + buf[0] = c; + + x = xr_sub(rope, 0, i); + y = xr_new_volatile(buf, 1); + rope = xr_cat(x, y); + XROPE_DECREF(x); + XROPE_DECREF(y); + + x = rope; + y = xr_sub(rope, i + 1, xr_len(rope)); + rope = xr_cat(x, y); + XROPE_DECREF(x); + XROPE_DECREF(y); + + return rope; } void pic_str_set(pic_state *pic, pic_str *str, size_t i, char c) { - UNUSED(pic); + xrope *x; - str->str[i] = c; + x = xr_put(str->rope, i, c); + if (x == NULL) { + pic_errorf(pic, "index out of range %d", i); + } + XROPE_DECREF(str->rope); + str->rope = x; } pic_str * pic_strcat(pic_state *pic, pic_str *a, pic_str *b) { - size_t len; - char *buf; - - len = a->len + b->len; - buf = pic_alloc(pic, len + 1); - - memcpy(buf, a->str, a->len); - memcpy(buf + a->len, b->str, b->len); - buf[len] = '\0'; - - return pic_str_new(pic, buf, len); + return str_new_rope(pic, xr_cat(a->rope, b->rope)); } pic_str * pic_substr(pic_state *pic, pic_str *str, size_t s, size_t e) { - size_t len; - char *buf; - - len = e - s; - buf = pic_alloc(pic, len + 1); - - memcpy(buf, str->str + s, len); - buf[len] = '\0'; - - return pic_str_new(pic, buf, len); + return str_new_rope(pic, xr_sub(str->rope, s, e)); } int pic_strcmp(pic_str *str1, pic_str *str2) { - return strcmp(str1->str, str2->str); + return strcmp(xr_cstr(str1->rope), xr_cstr(str2->rope)); +} + +const char * +pic_str_cstr(pic_str *str) +{ + return xr_cstr(str->rope); } pic_value diff --git a/src/symbol.c b/src/symbol.c index a4159b32..7076f3ec 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -93,7 +93,7 @@ pic_symbol_string_to_symbol(pic_state *pic) pic_error(pic, "string->symbol: expected string"); } - return pic_symbol_value(pic_intern_cstr(pic, pic_str_ptr(v)->str)); + return pic_symbol_value(pic_intern_cstr(pic, pic_str_cstr(pic_str_ptr(v)))); } void diff --git a/src/vm.c b/src/vm.c index 970f48a6..b483841e 100644 --- a/src/vm.c +++ b/src/vm.c @@ -188,15 +188,15 @@ pic_get_args(pic_state *pic, const char *format, ...) } case 'z': { pic_value str; - char **cstr; + const char **cstr; - cstr = va_arg(ap, char **); + cstr = va_arg(ap, const char **); if (i < argc) { str = GET_OPERAND(pic,i); if (! pic_str_p(str)) { pic_error(pic, "pic_get_args: expected string"); } - *cstr = pic_str_ptr(str)->str; + *cstr = pic_str_cstr(pic_str_ptr(str)); i++; } break; diff --git a/src/write.c b/src/write.c index 3adcda38..0a7bcafe 100644 --- a/src/write.c +++ b/src/write.c @@ -155,11 +155,11 @@ static void write_str(pic_state *pic, struct pic_string *str, xFILE *file) { size_t i; - const char *cstr = str->str; + const char *cstr = pic_str_cstr(str); UNUSED(pic); - for (i = 0; i < str->len; ++i) { + for (i = 0; i < pic_strlen(str); ++i) { if (cstr[i] == '"' || cstr[i] == '\\') { xfputc('\\', file); }