#include "core/buffer.h" #include #include #include #define INITIAL_CAPACITY 1024 static size_t gap_size(const Buffer *buffer) { return buffer->gap_end - buffer->gap_start; } static size_t physical_index(const Buffer *buffer, size_t logical) { if (logical < buffer->gap_start) { return logical; } return logical + gap_size(buffer); } static bool ensure_gap(Buffer *buffer, size_t needed) { if (gap_size(buffer) >= needed) { return true; } size_t old_gap = gap_size(buffer); size_t old_capacity = buffer->capacity; size_t new_capacity = old_capacity; while ((new_capacity - buffer->gap_start - (old_capacity - buffer->gap_end)) < needed) { new_capacity *= 2; } char *new_data = (char *)malloc(new_capacity); if (!new_data) { return false; } memcpy(new_data, buffer->data, buffer->gap_start); size_t tail_size = old_capacity - buffer->gap_end; size_t new_gap_end = new_capacity - tail_size; memcpy(new_data + new_gap_end, buffer->data + buffer->gap_end, tail_size); free(buffer->data); buffer->data = new_data; buffer->capacity = new_capacity; buffer->gap_end = new_gap_end; (void)old_gap; return true; } static void move_gap(Buffer *buffer, size_t target) { if (target == buffer->gap_start) { return; } if (target < buffer->gap_start) { size_t move = buffer->gap_start - target; memmove(buffer->data + buffer->gap_end - move, buffer->data + target, move); buffer->gap_start = target; buffer->gap_end -= move; return; } size_t move = target - buffer->gap_start; memmove(buffer->data + buffer->gap_start, buffer->data + buffer->gap_end, move); buffer->gap_start += move; buffer->gap_end += move; } bool buffer_init(Buffer *buffer) { buffer->data = (char *)malloc(INITIAL_CAPACITY); if (!buffer->data) { return false; } buffer->capacity = INITIAL_CAPACITY; buffer->gap_start = 0; buffer->gap_end = INITIAL_CAPACITY; buffer->path[0] = '\0'; buffer->modified = false; return true; } void buffer_free(Buffer *buffer) { free(buffer->data); buffer->data = NULL; buffer->capacity = 0; buffer->gap_start = 0; buffer->gap_end = 0; } size_t buffer_length(const Buffer *buffer) { return buffer->capacity - gap_size(buffer); } size_t buffer_point(const Buffer *buffer) { return buffer->gap_start; } void buffer_set_point(Buffer *buffer, size_t point) { size_t len = buffer_length(buffer); if (point > len) { point = len; } move_gap(buffer, point); } bool buffer_insert_char(Buffer *buffer, char c) { if (!ensure_gap(buffer, 1)) { return false; } buffer->data[buffer->gap_start++] = c; buffer->modified = true; return true; } bool buffer_insert_cstr(Buffer *buffer, const char *text) { size_t len = strlen(text); if (!ensure_gap(buffer, len)) { return false; } memcpy(buffer->data + buffer->gap_start, text, len); buffer->gap_start += len; if (len > 0) { buffer->modified = true; } return true; } bool buffer_backspace(Buffer *buffer) { if (buffer->gap_start == 0) { return false; } buffer->gap_start--; buffer->modified = true; return true; } bool buffer_delete_forward(Buffer *buffer) { if (buffer->gap_end >= buffer->capacity) { return false; } buffer->gap_end++; buffer->modified = true; return true; } char buffer_char_at(const Buffer *buffer, size_t index) { size_t len = buffer_length(buffer); if (index >= len) { return '\0'; } return buffer->data[physical_index(buffer, index)]; } bool buffer_load_file(Buffer *buffer, const char *path) { FILE *fp = fopen(path, "rb"); if (!fp) { return false; } if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); return false; } long file_size = ftell(fp); if (file_size < 0) { fclose(fp); return false; } rewind(fp); buffer->gap_start = 0; buffer->gap_end = buffer->capacity; if (!ensure_gap(buffer, (size_t)file_size + 1)) { fclose(fp); return false; } size_t read = fread(buffer->data, 1, (size_t)file_size, fp); fclose(fp); if (read != (size_t)file_size) { return false; } buffer->gap_start = (size_t)file_size; buffer->gap_end = buffer->capacity; strncpy(buffer->path, path, sizeof(buffer->path) - 1); buffer->path[sizeof(buffer->path) - 1] = '\0'; buffer->modified = false; return true; } bool buffer_save_file(Buffer *buffer, const char *path) { const char *out_path = path; if (!out_path || out_path[0] == '\0') { out_path = buffer->path; } if (!out_path || out_path[0] == '\0') { return false; } FILE *fp = fopen(out_path, "wb"); if (!fp) { return false; } size_t before = buffer->gap_start; size_t after = buffer->capacity - buffer->gap_end; if (before > 0 && fwrite(buffer->data, 1, before, fp) != before) { fclose(fp); return false; } if (after > 0 && fwrite(buffer->data + buffer->gap_end, 1, after, fp) != after) { fclose(fp); return false; } fclose(fp); if (path && path[0] != '\0') { strncpy(buffer->path, path, sizeof(buffer->path) - 1); buffer->path[sizeof(buffer->path) - 1] = '\0'; } buffer->modified = false; return true; } const char *buffer_path(const Buffer *buffer) { return buffer->path; } bool buffer_modified(const Buffer *buffer) { return buffer->modified; } void buffer_index_to_line_col(const Buffer *buffer, size_t index, int *line, int *col) { size_t len = buffer_length(buffer); if (index > len) { index = len; } int l = 0; int c = 0; for (size_t i = 0; i < index; ++i) { char ch = buffer_char_at(buffer, i); if (ch == '\n') { l++; c = 0; } else { c++; } } *line = l; *col = c; } size_t buffer_line_col_to_index(const Buffer *buffer, int target_line, int target_col) { if (target_line < 0) { target_line = 0; } if (target_col < 0) { target_col = 0; } size_t len = buffer_length(buffer); int line = 0; int col = 0; for (size_t i = 0; i < len; ++i) { if (line == target_line && col == target_col) { return i; } char ch = buffer_char_at(buffer, i); if (ch == '\n') { if (line == target_line) { return i; } line++; col = 0; } else { col++; } } return len; }