summaryrefslogtreecommitdiff
path: root/src/core/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/buffer.c')
-rw-r--r--src/core/buffer.c296
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;
+}