From a01d4de1e2feda34a20bb8dd65ec76fef3c20d6b Mon Sep 17 00:00:00 2001 From: pommicket Date: Sun, 15 Oct 2023 15:23:58 -0400 Subject: use reference-counted strings for string settings this lets us remove the length limitations which were previously imposed --- buffer.c | 6 +++--- build.c | 4 ++-- config.c | 62 ++++++++++++++++++++++++++++++++++------------------------ node.c | 2 +- ted-internal.h | 18 ++++++++--------- ted.c | 12 ++++++------ util.c | 38 +++++++++++++++++++++++++++++++++++ util.h | 19 ++++++++++++++++++ 8 files changed, 114 insertions(+), 47 deletions(-) diff --git a/buffer.c b/buffer.c index 35330ec..56f0447 100644 --- a/buffer.c +++ b/buffer.c @@ -3834,7 +3834,7 @@ void buffer_dedent_cursor_line(TextBuffer *buffer) { void buffer_comment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) { const Settings *settings = buffer_settings(buffer); - const char *start = settings->comment_start, *end = settings->comment_end; + const char *start = rc_str(settings->comment_start, ""), *end = rc_str(settings->comment_end, ""); if (!start[0] && !end[0]) return; String32 start32 = str32_from_utf8(start), end32 = str32_from_utf8(end); @@ -3889,7 +3889,7 @@ static bool buffer_line_ends_with_ascii(TextBuffer *buffer, u32 line_idx, const void buffer_uncomment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) { const Settings *settings = buffer_settings(buffer); - const char *start = settings->comment_start, *end = settings->comment_end; + const char *start = rc_str(settings->comment_start, ""), *end = rc_str(settings->comment_end, ""); if (!start[0] && !end[0]) return; u32 start_len = (u32)strlen(start), end_len = (u32)strlen(end); @@ -3913,7 +3913,7 @@ void buffer_uncomment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) { void buffer_toggle_comment_lines(TextBuffer *buffer, u32 first_line, u32 last_line) { const Settings *settings = buffer_settings(buffer); - const char *start = settings->comment_start, *end = settings->comment_end; + const char *start = rc_str(settings->comment_start, ""), *end = rc_str(settings->comment_end, ""); if (!start[0] && !end[0]) return; // if first line is a comment, uncomment lines, otherwise, comment lines diff --git a/build.c b/build.c index df9a140..5df00fc 100644 --- a/build.c +++ b/build.c @@ -104,7 +104,7 @@ void build_start_with_command(Ted *ted, const char *command) { void build_start(Ted *ted) { const Settings *settings = ted_active_settings(ted); - const char *command = settings->build_command; + const char *command = rc_str(settings->build_command, ""); { char *root = ted_get_root_dir(ted); @@ -113,7 +113,7 @@ void build_start(Ted *ted) { } if (*command == 0) { - command = settings->build_default_command; + command = rc_str(settings->build_default_command, ""); typedef struct { const char *filename; const char *command; diff --git a/config.c b/config.c index 3e9aa2a..6a12b89 100644 --- a/config.c +++ b/config.c @@ -61,8 +61,7 @@ typedef struct { } SettingU32; typedef struct { const char *name; - const char *control; - size_t buf_size; + RcStr *const *control; bool per_language; } SettingString; typedef struct { @@ -153,15 +152,15 @@ static const SettingFloat settings_float[] = { {"lsp-delay", &settings_zero.lsp_delay, 0, 100, true}, }; static const SettingString settings_string[] = { - {"build-default-command", settings_zero.build_default_command, sizeof settings_zero.build_default_command, true}, - {"build-command", settings_zero.build_command, sizeof settings_zero.build_command, true}, - {"root-identifiers", settings_zero.root_identifiers, sizeof settings_zero.root_identifiers, true}, - {"lsp", settings_zero.lsp, sizeof settings_zero.lsp, true}, - {"lsp-configuration", settings_zero.lsp_configuration, sizeof settings_zero.lsp_configuration, true}, - {"comment-start", settings_zero.comment_start, sizeof settings_zero.comment_start, true}, - {"comment-end", settings_zero.comment_end, sizeof settings_zero.comment_end, true}, - {"font", settings_zero.font, sizeof settings_zero.font, false}, - {"font-bold", settings_zero.font_bold, sizeof settings_zero.font_bold, false}, + {"build-default-command", &settings_zero.build_default_command, true}, + {"build-command", &settings_zero.build_command, true}, + {"root-identifiers", &settings_zero.root_identifiers, true}, + {"lsp", &settings_zero.lsp, true}, + {"lsp-configuration", &settings_zero.lsp_configuration, true}, + {"comment-start", &settings_zero.comment_start, true}, + {"comment-end", &settings_zero.comment_end, true}, + {"font", &settings_zero.font, false}, + {"font-bold", &settings_zero.font_bold, false}, }; static const SettingKeyCombo settings_key_combo[] = { {"hover-key", &settings_zero.hover_key, true}, @@ -189,8 +188,9 @@ static void setting_float_set(Settings *settings, const SettingFloat *set, float *(float *)((char *)settings + ((char*)set->control - (char*)&settings_zero)) = value; } static void setting_string_set(Settings *settings, const SettingString *set, const char *value) { - char *control = (char *)settings + (set->control - (char*)&settings_zero); - str_cpy(control, set->buf_size, value); + RcStr **control = (RcStr **)((char *)settings + ((char *)set->control - (char*)&settings_zero)); + if (*control) rc_str_decref(control); + *control = rc_str_new(value, -1); } static void setting_key_combo_set(Settings *settings, const SettingKeyCombo *set, KeyCombo value) { KeyCombo *control = (KeyCombo *)((char *)settings + ((char*)set->control - (char*)&settings_zero)); @@ -272,7 +272,11 @@ static void settings_copy(Settings *dest, const Settings *src) { gl_rc_sab_incref(dest->bg_shader); gl_rc_texture_incref(dest->bg_texture); - + for (size_t i = 0; i < arr_count(settings_string); i++) { + const SettingString *s = &settings_string[i]; + RcStr *rc = *(RcStr **)((char *)dest + ((char *)s->control - (char *)&settings_zero)); + rc_str_incref(rc); + } context_copy(&dest->context, &src->context); dest->language_extensions = arr_copy(src->language_extensions); dest->key_actions = arr_copy(src->key_actions); @@ -1061,11 +1065,7 @@ static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings, } break; case SETTING_STRING: { const SettingString *setting = &setting_any->u._string; - if (strlen(value) >= setting->buf_size) { - config_err(cfg, "%s is too long (length: %zu, maximum length: %zu).", key, strlen(value), setting->buf_size - 1); - } else { - setting_string_set(settings, setting, value); - } + setting_string_set(settings, setting, value); } break; case SETTING_KEY_COMBO: { const SettingKeyCombo *setting = &setting_any->u._key; @@ -1217,13 +1217,22 @@ static void gluint_eliminate_duplicates(GLuint **arr) { arr_set_len(*arr, count); } +static void settings_free(Settings *settings) { + context_free(&settings->context); + arr_free(settings->language_extensions); + gl_rc_sab_decref(&settings->bg_shader); + gl_rc_texture_decref(&settings->bg_texture); + arr_free(settings->key_actions); + for (size_t i = 0; i < arr_count(settings_string); i++) { + const SettingString *s = &settings_string[i]; + RcStr **rc = (RcStr **)((char *)settings + ((char *)s->control - (char *)&settings_zero)); + rc_str_decref(rc); + } +} + void config_free(Ted *ted) { arr_foreach_ptr(ted->all_settings, Settings, settings) { - context_free(&settings->context); - arr_free(settings->language_extensions); - gl_rc_sab_decref(&settings->bg_shader); - gl_rc_texture_decref(&settings->bg_texture); - arr_free(settings->key_actions); + settings_free(settings); } @@ -1257,13 +1266,14 @@ char *settings_get_root_dir(const Settings *settings, const char *path) { if (entries) { // note: this may actually be NULL on the first iteration if `path` is a file for (int e = 0; entries[e]; ++e) { const char *entry_name = entries[e]->name; - const char *ident_name = settings->root_identifiers; + const char *root_identifiers = rc_str(settings->root_identifiers, ""); + const char *ident_name = root_identifiers; while (*ident_name) { const char *separators = ", \t\n\r\v"; size_t ident_len = strcspn(ident_name, separators); if (strlen(entry_name) == ident_len && strncmp(entry_name, ident_name, ident_len) == 0) { // we found an identifier! - u32 score = U32_MAX - (u32)(ident_name - settings->root_identifiers); + u32 score = U32_MAX - (u32)(ident_name - root_identifiers); if (score > best_path_score) { best_path_score = score; strbuf_cpy(best_path, pathbuf); diff --git a/node.c b/node.c index d7b021c..f4fe8fc 100644 --- a/node.c +++ b/node.c @@ -420,7 +420,7 @@ void node_frame(Ted *ted, Node *node, Rect r) { // set window title to active tab's title strbuf_printf(ted->window_title, "ted %s | %s", tab_title, settings->indent_with_spaces ? "spaces" : "tabs"); - if (*settings->lsp) { + if (*rc_str(settings->lsp, "")) { LSP *lsp = buffer_lsp(buffer); strbuf_catf(ted->window_title, " | LSP %s", lsp && lsp_is_initialized(lsp) && !lsp_has_exited(lsp) diff --git a/ted-internal.h b/ted-internal.h index 5594adc..fe377a4 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -151,23 +151,23 @@ struct Settings { GlRcSAB *bg_shader; GlRcTexture *bg_texture; /// string used to start comments - char comment_start[16]; + RcStr *comment_start; /// string used to end comments - char comment_end[16]; + RcStr *comment_end; /// Comma-separated list of file names which identify the project root - char root_identifiers[4096]; + RcStr *root_identifiers; /// LSP server command - char lsp[512]; + RcStr *lsp; /// LSP "configuration" JSON - char lsp_configuration[4096]; + RcStr *lsp_configuration; /// Build command. If non-empty, this overrides running `cargo build` if `Cargo.toml` exists, etc. - char build_command[1024]; + RcStr *build_command; /// Default build command for if `Cargo.toml`, `Makefile`, etc. do not exist. - char build_default_command[1024]; + RcStr *build_default_command; /// Comma separated list of paths to font files. - char font[4096]; + RcStr *font; /// Comma separated list of paths to bold font files. - char font_bold[4096]; + RcStr *font_bold; LanguageExtension *language_extensions; /// dynamic array, sorted by KEY_COMBO(modifier, key) KeyAction *key_actions; diff --git a/ted.c b/ted.c index d1db1fb..ee1c636 100644 --- a/ted.c +++ b/ted.c @@ -264,7 +264,7 @@ LSP *ted_get_lsp(Ted *ted, const char *path, Language language) { if (!lsp) break; const char *const lsp_command = lsp_get_command(lsp); const u16 lsp_port = lsp_get_port(lsp); - if (lsp_command && !streq(lsp_command, settings->lsp)) continue; + if (lsp_command && !streq(lsp_command, rc_str(settings->lsp, ""))) continue; if (lsp_port != settings->lsp_port) continue; if (!lsp_is_initialized(lsp)) { @@ -286,15 +286,15 @@ LSP *ted_get_lsp(Ted *ted, const char *path, Language language) { } if (i == TED_LSP_MAX) return NULL; // why are there so many LSP open??? - if (*settings->lsp || settings->lsp_port) { + if (*rc_str(settings->lsp, "") || settings->lsp_port) { // start up this LSP FILE *log = settings->lsp_log ? ted->log : NULL; char *root_dir = settings_get_root_dir(settings, path); LSPSetup setup = { .root_dir = root_dir, - .command = *settings->lsp ? settings->lsp : NULL, + .command = rc_str(settings->lsp, NULL), .port = settings->lsp_port, - .configuration = settings->lsp_configuration, + .configuration = rc_str(settings->lsp_configuration, NULL), .log = log, .send_delay = settings->lsp_delay, }; @@ -434,13 +434,13 @@ void ted_load_fonts(Ted *ted) { ted_free_fonts(ted); const Settings *settings = ted_active_settings(ted); - ted->font = ted_load_multifont(ted, settings->font); + ted->font = ted_load_multifont(ted, rc_str(settings->font, "")); if (!ted->font) { ted->font = ted_load_multifont(ted, "assets/font.ttf"); if (!ted->font) die("Couldn't load default font: %s.", ted->message); } - ted->font_bold = ted_load_multifont(ted, settings->font_bold); + ted->font_bold = ted_load_multifont(ted, rc_str(settings->font_bold, "")); if (!ted->font_bold) { ted->font_bold = ted->font; } diff --git a/util.c b/util.c index 89b3dc2..1649f91 100644 --- a/util.c +++ b/util.c @@ -17,6 +17,42 @@ // on 16-bit systems, this is 16383. on 32/64-bit systems, this is 1073741823 // it is unusual to have a string that long. #define STRLEN_SAFE_MAX (UINT_MAX >> 2) +struct RcStr { + u32 ref_count; + char str[]; +}; + +RcStr *rc_str_new(const char *s, i64 len) { + if (len < 0) { + len = (i64)strlen(s); + } + RcStr *rc = calloc(1, sizeof(RcStr) + (size_t)len + 1); + assert(rc); + memcpy(rc->str, s, (size_t)len); + rc->ref_count = 1; + return rc; +} + +void rc_str_incref(RcStr *str) { + if (str) + str->ref_count += 1; +} + +void rc_str_decref(RcStr **pstr) { + RcStr *const str = *pstr; + if (!str) return; + str->ref_count -= 1; + if (str->ref_count == 0) { + *pstr = NULL; + free(str); + } +} + +const char *rc_str(RcStr *str, const char *default_value) { + if (!str) return default_value; + assert(str->ref_count > 0); + return str->str; +} bool is32_word(char32_t c) { return c > WCHAR_MAX || c == '_' || iswalnum((wint_t)c); @@ -151,6 +187,8 @@ bool str_has_path_prefix(const char *path, const char *prefix) { } bool streq(const char *a, const char *b) { + assert(a); + assert(b); return strcmp(a, b) == 0; } diff --git a/util.h b/util.h index edbcbe3..1d60cf9 100644 --- a/util.h +++ b/util.h @@ -41,7 +41,26 @@ typedef struct { size_t len; } String32; +/// reference-counted string +typedef struct RcStr RcStr; +/// allocate new ref-counted string +/// +/// if `len == -1`, `s` is assumed to be null-terminated. otherwise, +/// at most `len` bytes are copied from `s`. +RcStr *rc_str_new(const char *s, i64 len); +/// increases the reference count of `str`. +/// +/// does nothing if `str` is `NULL`. +void rc_str_incref(RcStr *str); +/// decrease reference count of `*str` and set `*str` to `NULL`. +/// +/// this frees `*str` if the reference count hits 0. +/// +/// does nothing if `*str` is NULL. +void rc_str_decref(RcStr **str); +/// get rc string with default value if `s == NULL` +const char *rc_str(RcStr *s, const char *value_if_null); /// `isword` for 32-bit chars. bool is32_word(char32_t c); /// `isspace` for 32-bit chars. -- cgit v1.2.3