summaryrefslogtreecommitdiff
path: root/txt.c
blob: b71bba35d9987413f3b31999142f3ba87a7a47f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <stdint.h>
#include <stddef.h>

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "wrmr.h"
#include "dynarr.h"
#include "txt.h"

void txt_insert_piece(Txt *b, u32 pi, TxtBufIdx buf, u32 ofs, u32 n) { 
	DA_FIT(&b->ptbl, b->ptbl.n + 1);
	MOVE(&b->ptbl.v[pi+1], &b->ptbl.v[pi], b->ptbl.n - pi);
	b->ptbl.v[pi] = (TxtPiece) { buf, ofs, n };
	b->ptbl.n++;
}

void txt_remove_piece(Txt *b, u32 pi) {
	if (pi + 1 < b->ptbl.n) {
		MOVE(&b->ptbl.v[pi], &b->ptbl.v[pi+1], b->ptbl.n - (pi + 1));
	}
	b->ptbl.n--;
}

u32 txt_split_piece(Txt *b, u32 pi, u32 i) {
	TxtPiece *p = &b->ptbl.v[pi];
	if (i == 0) return pi;
	if (i == p->n) return pi + 1;
	txt_insert_piece(b, pi + 1, p->buf, p->ofs + i, p->n - i);
	b->ptbl.v[pi].n = i;
	return pi + 1;
}

TxtLoc txt_at(Txt *b, u32 cur) {
	for (u32 i = 0; i < b->ptbl.n; i++) {
		if (cur <= b->ptbl.v[i].n) {
			return (TxtLoc) { i, cur };
		}
		cur -= b->ptbl.v[i].n;
	}
	return (TxtLoc) { 0, 0 };
}

void txt_buf_append(Txt *b, TxtBufIdx bi, const char *s, u32 n) {
	TxtBuf *buf = &b->buf[bi];
	if (buf->n + n > buf->c) {
		buf->c = stdc_bit_ceil(buf->n + n);
		buf->s = realloc(buf->s, buf->c);
		if (!buf->s) FAIL_WITH_MSG("realloc failure");
	}
	memcpy(&buf->s[buf->n], s, n);
	buf->n += n;
}

u32 txt_insert(Txt *b, u32 cur, const char *s, u32 n) {
	TxtLoc l = txt_at(b, cur);
	if (l.p < b->ptbl.n) {
		TxtPiece *p = &b->ptbl.v[l.p];
		int mid = p->ofs + l.i < b->buf[p->buf].n;
		if (p->buf == TXT_SRC || mid) {
			l.p = txt_split_piece(b, l.p, l.i);
			txt_insert_piece(b, l.p, TXT_ADD, b->buf[TXT_ADD].n, 0);
		}
	} else {
		txt_insert_piece(b, l.p, TXT_ADD, b->buf[TXT_ADD].n, 0);
	}
	TxtPiece *p = &b->ptbl.v[l.p];
	txt_buf_append(b, p->buf, s, n);
	p->n += n;
	b->len += n;
	return cur + n;
}

u32 txt_insert_c(Txt *b, u32 cur, char ch) {
	/* TODO: utf-8 char */
	return txt_insert(b, cur, &ch, 1);
}

u32 txt_delete(Txt *b, u32 cur, u32 n) {
	TxtLoc l = txt_at(b, cur);
	txt_split_piece(b, l.p, l.i);
	TxtPiece *p = &b->ptbl.v[l.p];
	if (n > cur) n = cur;
	cur -= n;
	b->len -= n;
	while (n > p->n) {
		n -= p->n;
		if (l.p + 1 < b->ptbl.n) txt_remove_piece(b, l.p);
		b->ptbl.n--;
		if (!l.p) return cur;
		l.p--;
		p = &b->ptbl.v[l.p];
	}
	p->n -= n;
	if (p->n == 0) txt_remove_piece(b, l.p);
	return cur;
}

int txt_open(Txt *b, const char *path) {
	struct stat sb;
	int fd = open(path, O_RDONLY);
	if (fd == -1) return -1;
	if (fstat(fd, &sb)) {
		close(fd);
		return -1;
	}
	void *m = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (m == MAP_FAILED) {
		close(fd);
		return -1;
	}
	memset(b, 0, sizeof(TxtBuf));
	b->buf[TXT_SRC].s = m;
	b->buf[TXT_SRC].n = sb.st_size;
	b->buf[TXT_SRC].c = sb.st_size;
	b->len = sb.st_size;
	close(fd);
	txt_insert_piece(b, 0, TXT_SRC, 0, b->len);
	return 0;
}