diff options
Diffstat (limited to 'src/core/buffer.c')
| -rw-r--r-- | src/core/buffer.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/core/buffer.c b/src/core/buffer.c new file mode 100644 index 0000000..16a9dfe --- /dev/null +++ b/src/core/buffer.c @@ -0,0 +1,296 @@ +#include "core/buffer.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} |
