#include #include #include #include #include #include #include "wrmr.h" #include "dynarr.h" #include "txt.h" #include "utf8.h" void txt_replace_piece(Txt *b, u32 pi, TxtBufIdx buf, u32 ofs, u32 n) { b->ptbl.v[pi] = (TxtPiece) { buf, ofs, n }; } void txt_insert_piece(Txt *b, u32 pi, TxtBufIdx buf, u32 ofs, u32 n) { DA_FIT(&b->ptbl, b->ptbl.n + 1); if (pi < b->ptbl.n) { MOVE(&b->ptbl.v[pi+1], &b->ptbl.v[pi], b->ptbl.n - pi); } txt_replace_piece(b, pi, 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; } static void txt_buf_fit(Txt *b, TxtBufIdx bi, u32 sz) { TxtBuf *buf = &b->buf[bi]; if (sz > buf->c) { buf->c = stdc_bit_ceil(sz); buf->s = realloc(buf->s, buf->c); if (!buf->s) FAIL_WITH_MSG("realloc failure"); } } static void txt_buf_append(Txt *b, TxtBufIdx bi, const char *s, u32 n) { TxtBuf *buf = &b->buf[bi]; txt_buf_fit(b, bi, buf->n + n); memcpy(&buf->s[buf->n], s, n); buf->n += n; } u32 txt_add_piece(Txt *b, TxtLoc l) { if (l.p > 0 && !l.i) { l.p--; l.i = b->ptbl.v[l.p].n; } 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); goto insert; } } else { insert: if (l.p < b->ptbl.n && b->ptbl.v[l.p].n == 0) { txt_replace_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); } } return l.p; } u32 txt_insert(Txt *b, u32 cur, const char *s, u32 n) { TxtLoc l = txt_at(b, cur); l.p = txt_add_piece(b, l); 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, u32 ch) { char buf[6]; u32 n = utf8_encode_len(&ch, 1); utf8_encode(buf, &ch, 1); return txt_insert(b, cur, buf, n); } static int txt_are_pieces_adjacent(Txt *t, u32 a, u32 b) { TxtPiece *pa = &t->ptbl.v[a]; TxtPiece *pb = &t->ptbl.v[b]; return pa->buf == pb->buf && pa->ofs + pa->n == pb->ofs; } static void txt_join_or_kill(Txt *b, u32 pi) { if (b->ptbl.v[pi].n == 0 && !(pi == 0 && b->ptbl.n == 1)) { txt_remove_piece(b, pi); } if (pi + 1 < b->ptbl.n && txt_are_pieces_adjacent(b, pi, pi + 1)) { b->ptbl.v[pi].n += b->ptbl.v[pi+1].n; txt_remove_piece(b, pi + 1); } } u32 txt_delete(Txt *b, u32 cur, u32 n) { TxtLoc l = txt_at(b, cur); u32 pi = txt_split_piece(b, l.p, l.i); if (pi > 0) pi--; if (n > cur) n = cur; cur -= n; b->len -= n; for (;;) { TxtPiece *p = &b->ptbl.v[pi]; if (n >= p->n) { n -= p->n; p->n = 0; if (pi > 0 || b->ptbl.n > 1) txt_remove_piece(b, pi); } else { p->n -= n; n = 0; break; } if (pi == 0) break; pi--; } txt_join_or_kill(b, pi); return cur; } u32 txt_delete_c(Txt *b, u32 cur) { while (cur > 0 && (txt_byte(b, txt_at(b, cur-1)) & 0xc0) == 0x80) cur = txt_delete(b, cur, 1); cur = txt_delete(b, cur, 1); return cur; } int txt_load(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; } if (!sb.st_size) { txt_buf_fit(b, TXT_SRC, 8192); goto done; } void *m = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (m == MAP_FAILED) { close(fd); return -1; } txt_buf_fit(b, TXT_SRC, sb.st_size); memcpy(b->buf[TXT_SRC].s, m, sb.st_size); munmap(m, sb.st_size); b->buf[TXT_SRC].n = sb.st_size; close(fd); done: b->ptbl.n = 0; b->buf[TXT_ADD].n = 0; b->len = b->buf[TXT_SRC].n; txt_insert_piece(b, 0, TXT_SRC, 0, b->len); return 0; } int txt_save(Txt *t, const char *path) { FILE *f = fopen(path, "w"); if (!f) return -1; for (u32 i = 0; i < t->ptbl.n; i++) { TxtPiece *p = &t->ptbl.v[i]; TxtBuf *b = &t->buf[p->buf]; fwrite(b->s + p->ofs, 1, p->n, f); } fclose(f); return ferror(f) ? -1 : 0; } void txt_free(Txt *t) { for (u32 i = 0; i < COUNTOF(t->buf); i++) { free(t->buf[i].s); } free(t->ptbl.v); } /* reading individual chars */ u8 txt_byte(Txt *t, TxtLoc l) { TxtPiece *p = &t->ptbl.v[l.p]; TxtBuf *b = &t->buf[p->buf]; //if (l.p < t->ptbl.n && l.i == p->n) p++, l.i = 0; if (p->ofs + l.i >= b->n) return 0; return b->s[p->ofs + l.i]; } u32 txt_chr(Txt *t, TxtLoc l) { TxtPiece *p = &t->ptbl.v[l.p]; TxtBuf *b = &t->buf[p->buf]; /* * is this ever executed? * if (l.p < t->ptbl.n && l.i == p->n) p++, l.i = 0; */ return utf8_decode_at(b->s, p->ofs + l.i, b->n); } /* cursor manipulation */ 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) { b->ptbl.n - 1, b->ptbl.v[b->ptbl.n - 1].n }; } u32 txt_ofs(Txt *b, TxtLoc l) { u32 r = 0; for (u32 i = 0; i < l.p; i++) { r += b->ptbl.v[i].n; } return r + l.i; } TxtLoc txt_next(Txt *b, TxtLoc l) { TxtPiece *p = &b->ptbl.v[l.p]; if (l.p + 1 < b->ptbl.n) { if (l.i + 1 < p->n) l.i++; else l.p++, l.i = 0; } else { l.i++; if (l.i > p->n) l.i = p->n; } return l; } TxtLoc txt_prev(Txt *b, TxtLoc l) { if (l.i > 0) { return (TxtLoc) { l.p, l.i - 1 }; } else if (l.p > 0) { return (TxtLoc) { l.p - 1, b->ptbl.v[l.p - 1].n - 1 }; } else { return (TxtLoc) { 0, 0 }; } } int txt_at_start(Txt *b, TxtLoc l) { (void)b; return l.p == 0 && l.i == 0; } int txt_at_end(Txt *b, TxtLoc l) { return l.p + 1 == b->ptbl.n && l.i == b->ptbl.v[l.p].n; } int txt_before(TxtLoc a, TxtLoc b) { return a.p < b.p || (a.p == b.p && a.i < b.i); } int txt_after(TxtLoc a, TxtLoc b) { return a.p > b.p || (a.p == b.p && a.i > b.i); } u32 txt_chr_next(Txt *t, TxtLoc *l) { if (txt_at_end(t, *l)) return 0; u32 c = txt_chr(t, *l); u32 n = UTF8_CP_LEN(c); for (u32 i = 0; i < n; i++) *l = txt_next(t, *l); return c; }