piece table 的C语言简单实现
piece table 的C語言簡單實現
piece table的介紹
piece table是文本編輯器領域的一個很重要的數據結構
能實現文本編輯器的數據結構有很多,例如vi編輯器遠古版本使用的一整塊數組、塊狀鏈表、行鏈表、數組的改進GAP BUFFER等,還有本文要介紹的piece table。
關于文本編輯器的各種實現方式,這篇論文給出了很詳細的介紹
https://pan.baidu.com/s/1tNuJ6trAStnr52z1QZDR4A
關于piece table,這篇文章給出了很詳細的說明
https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation
這里簡要說明什么是piece table
主要參考了這篇文章:
https://zhuanlan.zhihu.com/p/259387658
piece table 由三部分構成:
源文本、輸入緩存、pieces
源文本記錄源文件的內容,它是一個只讀字符串
輸入緩存記錄每一次輸入的內容。它是一個只增字符串(append only)
pieces是piece table的精華
它的作用是指示哪些是應當出現在渲染界面的內容
舉個例子,源文件內容為:
hello 01234最開始的時候pieces記錄的內容如下:
type |start |len SOURCE |0 |5此時渲染的內容為hello
我們往輸入緩存中添加, world
要使渲染層輸出內容為hello, world,需要將pieces記錄的內容改為:
type |start |len SOURCE |0 |5 ADDBUF |0 |7此時要刪除llo,,需要將pieces記錄的內容更改為
type |start |len SOURCE |0 |2 ADDBUF |1 |7此時的輸出為he world
pieces就像是手電筒一樣,照亮我們需要的字符,如要刪除某些字符,只要簡單地將它的范圍縮小即可。
piece table比起其他數據結構,優越的地方在于它可以迅速實現撤回和重做功能,無需復制粘貼大量數據
使用一個棧來存儲每次的操作,只需要對table鏈表執行對應的操作即可迅速實現撤回與重做功能。
另外,可以使用另一個結構來存儲每一行開始的位置等等。不在本文的考慮范圍內。
C語言的簡單實現
本次主要實現的功能為:在piecetable數據結構中添加一段字符、刪除一段字符
涉及到的數據結構操作有:
設計如下數據結構:
typedef struct buf_t {char *data;size_t curSize;size_t maxSize; }buf_t; // 變長緩存 typedef enum {SOURCE = 0,ADDBUF,UNKNOW }buf_e; typedef struct table_t {buf_e type;size_t start;size_t len;struct table_t *next; }table_t; // 單鏈表結構 typedef struct piecetable_t {buf_t addbuf; // append onlychar *source; // read onlytable_t *table; // linked list }piecetable_t;將輸入緩存設置為可擴容類型的
注意其中的table_t類型,我將其設置為無表頭的單向鏈表
實際上有表頭的鏈表更好寫一點
在實際使用中,可以使用紅黑數來重寫table_t類型的所有操作,將時間復雜度從O(n)提升到O(ln n)
創建與銷毀
設計如下函數
// 創建新的piecetable結構 piecetable_t *piecetable_new(char *srcTxt); // 銷毀該piecetable bool piecetable_free(piecetable_t *pt);/*** 創建新的piecetable類型* @param srcTxt 源文件指針*/ piecetable_t *piecetable_new(char *srcTxt) {piecetable_t *pt = (piecetable_t *)malloc(sizeof(piecetable_t));if (pt == NULL)return NULL;pt->source = srcTxt; // 只讀pt->table = table_init(srcTxt); // 無表頭的單鏈表結構buf_init(&(pt->addbuf)); // 可擴容的緩存數組return pt; }/*** 銷毀piecetable* @param pt 要銷毀的對象* @return 是否銷毀成功*/ bool piecetable_free(piecetable_t *pt) {free(pt->source);buf_free(&(pt->addbuf));table_free(pt->table);free(pt);return true; }具體函數實現細節請看等一下貼出來的源碼
添加一段字符串
設置如下函數來添加字符串
// 往pt的pos處后面插入大小為size的字符串 bool piecetable_ins(piecetable_t *pt, size_t pos, char *s, size_t size); /*** 往pt的pos處后面插入大小為size的字符串* @param pt* @param pos 插入位置* @param s 要插入的字符串* @param size 插入的字符串的長度* @return 是否插入成功*/ bool piecetable_ins(piecetable_t *pt, size_t pos, char *s, size_t size) {if (!pt || !s || size == 0)return false;if (!buf_ins(&(pt->addbuf), s, size))return false;table_t *tmp = table_ins(pt->table, pos, ADDBUF,pt->addbuf.curSize - size, size);if (!tmp)return false;pt->table = table_merge(tmp);return true; }往piece table中添加一串字符,比較簡單的實現方式是直接添加到addbuf中,然后再將新的位置添加到table結構的記錄中
但是,這么做在進行大量編輯的時候很容易把addbuf撐爆
比較理想的改進方式是,首先在addbuf中尋找子串,直接把子串的位置傳給table結構,找不到了再添加進去。
使用table_ins來將新的位置添加到table結構中。若添加失敗返回NULL。添加成功則返回table結構的頭指針。
下面table_merge的作用是,因為在table結構里面進行插入刪除等操作,難免會出現一些極端情況,例如
type | start | len XXX | 0 | 0 XXX | 0 | 0 XXX | 0 | 0 XXX | 0 | 0 XXX | 0 | 0 ...或者
type | start | len XXX | 0 | 1 XXX | 1 | 1 XXX | 2 | 1 XXX | 3 | 1 XXX | 4 | 1 ...使用table_merge函數將這些部分整合起來
下面給出table_ins的具體實現
/*** 往table里面插入記錄* @param table* @param pos 插入的位置* @param type 新記錄的類型* @param start 開始的位置* @param len 插入的長度* @return 插入是否成功*/ static table_t *table_ins(table_t *table, size_t pos,size_t type, size_t start, size_t len) {size_t curLen = 0;table_t *prev = NULL;table_t *cur = table_findPos(table, pos, &curLen, &prev);if (cur == NULL)return NULL;// curLen should be bigger than pospos -= curLen - cur->len;if (prev == NULL && cur->type == UNKNOW) // first chain{cur->type = type;cur->start = start;cur->len = len;return cur;}table_t *left, *right;if (pos == 0){if (prev == NULL)return table_new(type, start, len, cur);left = prev;right = cur;}else{left = cur;right = table_new(left->type, left->start + pos,left->len - pos, left->next);if (right == NULL)return NULL;left->len = pos;}table_t *tmp = table_new(type, start, len, right);if (tmp == NULL)return NULL;left->next = tmp;return table; }里面使用到了一個table_findPos函數
/*** 尋找處于位置pos處的table記錄* @param table* @param pos 要尋找的位置* @return 要尋找的table節點*/ static table_t *table_findPos(table_t *table, size_t pos, size_t *curLen, table_t **prev) {if (prev != NULL)*prev = NULL;if (table == NULL){if (pos > 0)return NULL;return table_new(UNKNOW, 0, 0, NULL);}*curLen = table->len;while (*curLen <= pos){if (prev != NULL)*prev = table;table = table->next;if (table == NULL){if (*curLen == pos)return table_new(UNKNOW, 0, 0, NULL);elsereturn NULL;}*curLen += table->len;}return table; }刪除一段字符串
刪除一段字符串的操作是添加一段字符串的反操作
/*** 從table鏈表結構映射的pos處刪除長度為len的數據* @param table [description]* @param pos [description]* @param len [description]* @return [description]*/ static table_t *table_del(table_t *table, size_t pos,size_t len) {size_t curLen;table_t *start = table_findPos(table, pos, &curLen, NULL);table_t *end = start;size_t deleLen, tmpPos;if (start == NULL)return NULL;pos -= curLen - start->len;tmpPos = pos;while (len && end){size_t surpLen = end->len - pos;deleLen = (surpLen >= len) ? len : surpLen;len -= deleLen;pos = 0;end = end->next;}if (end == NULL && len > 0) // 沒有找到結束節點,說明刪除的長度過長return NULL;if (start == end) // 在同一個節點上執行刪除操作{// 需要拆分節點start->next = table_new(start->type, start->start + tmpPos + deleLen,start->len - tmpPos - deleLen, start->next);start->len = tmpPos;}else{table_t *tmp = start->next;start->next = end;start->len = tmpPos;if (end != NULL){end->start += deleLen;end->len -= deleLen;}while (tmp != end) // tmp必定不為空{table_t *next = tmp->next;free(tmp);tmp = next;}}return table; }實驗
使用如下main函數設計一個簡單的行文本編輯器
顧名思義,它只能編輯一行的內容,超出一行的內容就會有奇怪的表現
這個程序只考慮了功能性,沒有考慮最佳實現方案
編譯時使用的makefile
all:make pieceTablepieceTable: pieceTable.ogcc -Wall -g -o pieceTable pieceTable.opieceTable.o: pieceTable.c pieceTable.hgcc -Wall -g -D PIECETABLE_TEST -c pieceTable.cclean:rm pieceTable.oremake:make cleanmake all源碼
源碼已經上傳CSDN,關注我之后就能看到
https://download.csdn.net/download/weixin_45206746/14623055
總結
以上是生活随笔為你收集整理的piece table 的C语言简单实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网站盈利有哪些模式?
- 下一篇: 软体机器人空间感知技术综述