README update

This commit is contained in:
Francesco Cozzuto
2023-03-31 17:21:34 +02:00
parent 1c9efb68e8
commit 34e01bb261
+91 -6
View File
@@ -2,8 +2,12 @@
This repository is a self-contained gap buffer implementation in C which supports unicode (utf-8). The aim of the implementation is to be simple, robust and easy to use. This repository is a self-contained gap buffer implementation in C which supports unicode (utf-8). The aim of the implementation is to be simple, robust and easy to use.
# Table of contents # Table of contents
* [License](#license)
* [What is a gap buffer?](#what-is-a-gap-buffer) * [What is a gap buffer?](#what-is-a-gap-buffer)
* [Insertion](#insertion)
* [Motion](#motion)
* [Deletion](#deletion)
* [Considerations](#considerations)
* [License](#license)
* [Usage](#usage) * [Usage](#usage)
* [Install](#install) * [Install](#install)
* [Instanciate](#instanciate) * [Instanciate](#instanciate)
@@ -13,6 +17,90 @@ This repository is a self-contained gap buffer implementation in C which support
* [Querying](#querying) * [Querying](#querying)
* [Testing](#testing) * [Testing](#testing)
## What is a gap buffer?
A gap buffer is a data structure that stores strings of text in a way that's optimized for operation based on a cursor, which makes it useful for text editors. The way it works is by dividing the memory is three regions: the text preceding the cursor, the unused memory and then the text following the cursor. The unused memory region is often referred to as "gap", hence the name "gap buffer".
### Insertion
Imagine storing the string "Hello, world!" in a buffer with a capacity of 20 bytes and placing the cursor just before the "w" of "world". What the gap buffer would look like is this:
```
Hello, |world!
+---+---+---+---+---+---+---|---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | | | | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---|---+---+---+---+---+---+---+---+---+---+---+---+---+
^ cursor
(the "_" represents a space)
```
In the diagram the cursor position was emphasized, but it's just a way to refer to the first unused byte, so it's actually redundant to specify it.
The region in the middle is usually referred as "gap" (hence the gap buffer).
When text is inserted in the buffer, it's placed before the cursor and the free space is decreased. Imagine inserting the string "my " in the previous example:
```
Hello, |world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | | | | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Hello, my |world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | m | y | _ | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
```
this operation is very fast because it does not depend on how much text is stored in the buffer. If we were to store the text in a basic string, we would have needed to move all of the text after the cursor before being able to insert the new text.
### Motion
To insert text in positions different than the cursor, you can change the cursor. This is done by moving text from the tail of the first region to the head of the second one, or viceversa. Let's move the cursor at the start of the string and then just before the "r" of "world"
```
Hello, my |world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | m | y | _ | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|Hello, my world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | H | e | l | l | o | , | _ | m | y | _ | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Hello, my wo|rld!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | m | y | _ | w | o | | | | | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
(now we put it back)
Hello, my |world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | m | y | _ | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
```
### Deletion
To delete text, you need to make sure that the text comes just after the cursor, then increase the gap size. Lets say we want to delete the " my " text in the previous example:
```
Hello, my |world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | _ | m | y | _ | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Hello,| my world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | | | | | _ | m | y | _ | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Hello,|world!
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | e | l | l | o | , | | | | | | | | | w | o | r | l | d | ! |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
```
### Considerations
Gap buffers are great because of their simplicity and speed for basic operations, but don't scale very well and more sophisticated operations are relatively slow (like moving the cursor to a line given its number). Alternative solutions are piece tables and ropes.
## License ## License
This code is MIT licensed This code is MIT licensed
@@ -24,15 +112,12 @@ This code is MIT licensed
> >
> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## What is a gap buffer?
A gap buffer is a data structure that stores strings of text in a way that's optimized for operations you do in a text editor.
## Usage ## Usage
An overview of how to use this code follows. You can find more information in the documentation that comes with the code. An overview of how to use this code follows. You can find more information in the documentation that comes with the code.
### Install ### Install
To use it, first you need to drop `gap_buffer.c` and `gap_buffer.h` in your project directory and link them during compilation To use it, first you need to drop `gap_buffer.c` and `gap_buffer.h` in your project directory and link them during compilation
like they were your files. like they were your own files.
### Instanciate ### Instanciate
You can instanciate a gap buffer in one of two ways: You can instanciate a gap buffer in one of two ways:
@@ -42,7 +127,7 @@ GapBuffer *GapBuffer_create(size_t capacity);
GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len); GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len);
``` ```
The basic option is using `GapBuffer *GapBuffer_create(size_t capacity)`, which instanciates a buffer with a given initial capacity allocating space through the libc allocator. If you fill the buffer up and insert more text, the buffer will grow by moving to a bigger memory region (also allocated through `malloc`). Conversely, `GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len)` doesn't use dynamic memory and does its job using only memory provided by the caller. Either way, if it wasn't possible to instanciate the gap buffer (either because the dynamic allocation failed or the provided memory isn't big enough), NULL is returned. The basic option is using `GapBuffer_create`, which instanciates a buffer with a given initial capacity allocating space through the libc allocator. If you fill the buffer up and insert more text, the buffer will grow by moving to a bigger memory region (also allocated through `malloc`). Conversely, `GapBuffer_createUsingMemory` doesn't use dynamic memory and does its job using only memory provided by the caller. Either way, if it wasn't possible to instanciate the gap buffer (either because the dynamic allocation failed or the provided memory isn't big enough), NULL is returned.
Once you're done with the buffer, you'll need to deallocate it using Once you're done with the buffer, you'll need to deallocate it using
```c ```c