documented some code

This commit is contained in:
Francesco Cozzuto
2023-03-31 17:54:24 +02:00
parent 34e01bb261
commit e29f789905
2 changed files with 101 additions and 8 deletions
+1 -3
View File
@@ -18,7 +18,7 @@ This repository is a self-contained gap buffer implementation in C which support
* [Testing](#testing) * [Testing](#testing)
## What is a gap buffer? ## 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". A gap buffer is a data structure that stores strings of text in a way that's optimized for operations 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 ### 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: 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:
@@ -34,8 +34,6 @@ Hello, |world!
``` ```
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. 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: 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:
``` ```
+100 -5
View File
@@ -20,14 +20,42 @@ struct GapBuffer {
uint8_t data[]; uint8_t data[];
}; };
/* Symbol: GapBuffer_getByteCount
** Return the length in bytes of the string stored
** in the buffer.
**
** Notes:
** - This information isn't very useful to the owner
** of the gap instance. It's only really used while
** testing. It may be good to make it private to the
** testing routine somehow.
*/
size_t GapBuffer_getByteCount(GapBuffer *buff) size_t GapBuffer_getByteCount(GapBuffer *buff)
{ {
return buff->total - buff->gap_length; return buff->total - buff->gap_length;
} }
/* Symbol: GapBuffer_createUsingMemory
** Instanciate a gap buffer by providing it the memory
** it should use. The gap buffer won't be able to relocate
** to a bigger memory region if needed.
**
** Arguments:
** - mem: Address of the memory region used by the buffer
** - len: Length in bytes of the region referred to by [mem]
**
** Returns:
** The pointer to a buffer instance or NULL if the
** provided memory wasn't enough.
**
** Notes:
** - If you only instanciate buffers this way, you basically
** can drop the dependency on the libc allocator and potentially
** libc entirely.
*/
GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len) GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len)
{ {
if (len < sizeof(GapBuffer)) if (mem == NULL || len < sizeof(GapBuffer))
return NULL; return NULL;
size_t capacity = len - sizeof(GapBuffer); size_t capacity = len - sizeof(GapBuffer);
@@ -41,6 +69,22 @@ GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len)
return buff; return buff;
} }
/* Symbol: GapBuffer_create
** Instanciate a gap buffer by allocating memory through
** the libc allocator.
**
** Gap buffers instanciated this way will allocate and
** relocate to bigger memory regions if necessary.
**
** Arguments:
** - capacity: The maximum string length (in bytes) that
** will be possible to store in the buffer
** without the need of allocating more memory.
**
** Returns:
** The pointer to a buffer instance or NULL if the
** dynamic memory allocation failed.
*/
GapBuffer *GapBuffer_create(size_t capacity) GapBuffer *GapBuffer_create(size_t capacity)
{ {
GapBuffer *buff = malloc(sizeof(GapBuffer) + capacity); GapBuffer *buff = malloc(sizeof(GapBuffer) + capacity);
@@ -54,30 +98,81 @@ GapBuffer *GapBuffer_create(size_t capacity)
return buff; return buff;
} }
/* Symbol: GapBuffer_destroy
** Delete an instanciated gap buffer.
*/
void GapBuffer_destroy(GapBuffer *buff) void GapBuffer_destroy(GapBuffer *buff)
{ {
if (!buff->is_static) if (!buff->is_static)
free(buff); free(buff);
} }
/* Symbol: getStringBeforeGap
** Returns a slice to the memory region before the gap
** in the form of a (pointer, length) pair.
*/
static String getStringBeforeGap(GapBuffer *buff) static String getStringBeforeGap(GapBuffer *buff)
{ {
return (String) { return (String) {
.data=buff->data,
.size=buff->gap_offset, .data=buff->data, // The start of the buffer is also the
// start of the region before the gap.
.size=buff->gap_offset, // The offset of the gap is the the length
// of the region that comes before it.
}; };
} }
/* Symbol: getStringAfterGap
** Returns a slice to the memory region after the gap
** in the form of a (pointer, length) pair.
*/
static String getStringAfterGap(GapBuffer *buff) static String getStringAfterGap(GapBuffer *buff)
{ {
// The first byte after the gap is the offset
// of the text that comes after the gap and
// the length of the region before the gap plus
// the length of the gap.
size_t first_byte_after_gap = buff->gap_offset + buff->gap_length; size_t first_byte_after_gap = buff->gap_offset + buff->gap_length;
return (String) { return (String) {
.data = buff->data + first_byte_after_gap, .data = buff->data + first_byte_after_gap,
.size = buff->total - first_byte_after_gap, .size = buff->total - first_byte_after_gap, // The length of the region following the
// gap is the total number of bytes minus
// the offset of the first byte after the gap.
}; };
} }
/* Forward declaration of [growGap] because [growGap]
** and [ensureSpace] refer to each other recursively,
** even though isn't not possible to have recursive
** calls during execution.
*/
static GapBuffer *growGap(GapBuffer *buff, size_t min); static GapBuffer *growGap(GapBuffer *buff, size_t min);
/* Symbol: ensureSpace
** Make sure that the buffer has at least [min]
** unused bytes by relocating the buffer to a
** bigger memory region if necessary.
**
** Arguments:
** - buff: Reference to the buffer's reference. If a relocation
** occurres, the caller's reference will be changed.
** I
** - min: Minimum number of free bytes the call must ensure.
** If the buffer has a gap smaller than [min], relocation
** will occur.
**
** Returns:
** True if the buffer has more than [min] free
** space or if relocation occurred succesfully.
** If relocation failed or the buffer isn't allowed
** to grow, false is returned.
**
** Notes:
** - Only gaps instanciated with [GapBuffer_create]
** are allowed to be relocated.
*/
static bool ensureSpace(GapBuffer **buff, size_t min) static bool ensureSpace(GapBuffer **buff, size_t min)
{ {
if ((*buff)->gap_length < min) { if ((*buff)->gap_length < min) {