From: Mike Lowis Date: Fri, 3 Oct 2025 20:13:07 +0000 (-0400) Subject: optimize regex searching to search lin bye line X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=refs%2Fheads%2Fgtk-bins;p=proto%2Faos.git optimize regex searching to search lin bye line --- diff --git a/bin/editor.c b/bin/editor.c index 7fd649b..ed1ec06 100644 --- a/bin/editor.c +++ b/bin/editor.c @@ -30,6 +30,8 @@ typedef struct EditItem *redo; bool edit_in_progress; GtkTextTag *highlight_tag; + char *line_buffer; // Reusable buffer for line text + size_t buffer_capacity; // Current capacity of line_buffer } WindowContext; #ifdef __APPLE__ @@ -46,6 +48,66 @@ static void CreateWindow(GtkApplication *app, char *path); // Edit Helper Routines //--------------------------------------------------------- +static void ensure_line_buffer_capacity(WindowContext *ctx, size_t needed_size) +{ + if (ctx->buffer_capacity < needed_size) + { + // Grow buffer to at least the needed size, with some extra space + ctx->buffer_capacity = needed_size + 1024; // Add 1KB extra + ctx->line_buffer = g_realloc(ctx->line_buffer, ctx->buffer_capacity); + } +} + +static void free_line_buffer(WindowContext *ctx) +{ + if (ctx->line_buffer) + { + g_free(ctx->line_buffer); + ctx->line_buffer = NULL; + ctx->buffer_capacity = 0; + } +} + +static size_t copy_text_iter_range_to_buffer(WindowContext *ctx, + const GtkTextIter *start, + const GtkTextIter *end) +{ + // Calculate the length needed (in bytes) + gint start_offset = gtk_text_iter_get_offset(start); + gint end_offset = gtk_text_iter_get_offset(end); + size_t char_count = end_offset - start_offset; + + if (char_count == 0) + { + if (ctx->line_buffer && ctx->buffer_capacity > 0) + { + ctx->line_buffer[0] = '\0'; + } + return 0; + } + + // Estimate byte count (UTF-8 can be 1-4 bytes per character) + size_t estimated_bytes = char_count * 4 + 1; // +1 for null terminator + ensure_line_buffer_capacity(ctx, estimated_bytes); + + // Copy text character by character to avoid allocation + GtkTextIter iter = *start; + size_t pos = 0; + + while (!gtk_text_iter_equal(&iter, end) && pos < ctx->buffer_capacity - 1) + { + gunichar ch = gtk_text_iter_get_char(&iter); + gint bytes = g_unichar_to_utf8(ch, &ctx->line_buffer[pos]); + pos += bytes; + + if (!gtk_text_iter_forward_char(&iter)) + break; + } + + ctx->line_buffer[pos] = '\0'; + return pos; +} + static void UpdateUndoRedoIcons(WindowContext *ctx) { gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), TRUE); @@ -232,13 +294,6 @@ static void OnCmdChanged(GtkApplication *app, gpointer user_data) return; } - // Get the entire text buffer content - char *buffer_text = gtk_text_buffer_get_text(ctx->buffer, &start, &end, FALSE); - if (!buffer_text) - { - return; - } - // Compile the regex pattern GError *error = NULL; GRegex *regex = g_regex_new(pattern, 0, 0, &error); @@ -266,54 +321,78 @@ static void OnCmdChanged(GtkApplication *app, gpointer user_data) search_start = match_end; } - g_free(buffer_text); printf("cmd (literal): '%s'\n", pattern); return; } - // Find all regex matches - GMatchInfo *match_info = NULL; + // Process buffer line by line using reusable buffer + gint line_count = gtk_text_buffer_get_line_count(ctx->buffer); gboolean first_match = TRUE; + gint current_char_offset = 0; // Track absolute character position for proper highlighting - if (g_regex_match(regex, buffer_text, 0, &match_info)) + for (gint line = 0; line < line_count; line++) { - do - { - gint start_pos, end_pos; - if (g_match_info_fetch_pos(match_info, 0, &start_pos, &end_pos)) - { - // Convert byte positions to text iterators - GtkTextIter match_start, match_end; - gtk_text_buffer_get_iter_at_offset(ctx->buffer, &match_start, start_pos); - gtk_text_buffer_get_iter_at_offset(ctx->buffer, &match_end, end_pos); + GtkTextIter line_start, line_end; + gtk_text_buffer_get_iter_at_line(ctx->buffer, &line_start, line); + line_end = line_start; + gtk_text_iter_forward_to_line_end(&line_end); - // Apply highlight tag to this match - gtk_text_buffer_apply_tag(ctx->buffer, ctx->highlight_tag, &match_start, &match_end); + // Copy line text to reusable buffer without allocation + size_t line_length = copy_text_iter_range_to_buffer(ctx, &line_start, &line_end); - // If this is the first match, scroll to center it in the view - if (first_match) + if (line_length == 0) + { + current_char_offset += gtk_text_iter_get_chars_in_line(&line_start); + continue; + } + + // Find matches in this line + GMatchInfo *match_info = NULL; + if (g_regex_match(regex, ctx->line_buffer, 0, &match_info)) + { + do + { + gint start_pos, end_pos; + if (g_match_info_fetch_pos(match_info, 0, &start_pos, &end_pos)) { - gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ctx->text_view), &match_start, 0.0, TRUE, 0.5, 0.5); - first_match = FALSE; + // Convert line-relative positions to buffer positions + GtkTextIter match_start, match_end; + match_start = line_start; + match_end = line_start; + + // Move to the match positions (handling UTF-8) + gtk_text_iter_forward_chars(&match_start, g_utf8_pointer_to_offset(ctx->line_buffer, ctx->line_buffer + start_pos)); + gtk_text_iter_forward_chars(&match_end, g_utf8_pointer_to_offset(ctx->line_buffer, ctx->line_buffer + end_pos)); + + // Apply highlight tag + gtk_text_buffer_apply_tag(ctx->buffer, ctx->highlight_tag, &match_start, &match_end); + + // Scroll to first match + if (first_match) + { + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ctx->text_view), &match_start, 0.0, TRUE, 0.5, 0.5); + first_match = FALSE; + } } - } - } while (g_match_info_next(match_info, NULL)); - } + } while (g_match_info_next(match_info, NULL)); + } - // Clean up - if (match_info) - { - g_match_info_free(match_info); + if (match_info) + { + g_match_info_free(match_info); + } } + + // Clean up regex g_regex_unref(regex); - g_free(buffer_text); - printf("cmd (regex): '%s'\n", pattern); + printf("cmd (regex optimized): '%s'\n", pattern); } static void OnQuit(GtkApplication *app, gpointer user_data) { WindowContext *ctx = (WindowContext *)user_data; + free_line_buffer(ctx); // Clean up the reusable line buffer gtk_window_close(GTK_WINDOW(ctx->window)); g_free(ctx); }