New API for relocating the buffer
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
test
|
||||||
+143
-246
@@ -1,65 +1,40 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "gap_buffer.h"
|
#include "gap_buffer.h"
|
||||||
|
|
||||||
|
#ifdef GAPBUFFER_DEBUG
|
||||||
|
#define PRIVATE
|
||||||
|
#else
|
||||||
|
#define PRIVATE static
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const uint8_t *data;
|
const char *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
} String;
|
} String;
|
||||||
|
|
||||||
struct GapBuffer {
|
struct GapBuffer {
|
||||||
bool is_static;
|
void (*free)(void*);
|
||||||
bool no_resize;
|
|
||||||
size_t gap_offset;
|
size_t gap_offset;
|
||||||
size_t gap_length;
|
size_t gap_length;
|
||||||
size_t total;
|
size_t total;
|
||||||
uint8_t data[];
|
char data[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Symbol: GapBuffer_getByteCount
|
PRIVATE size_t getByteCount(GapBuffer *buff)
|
||||||
** 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.
|
|
||||||
**
|
|
||||||
** - This isn't declared in the header file, so
|
|
||||||
** if a program wants to use this function it need
|
|
||||||
** to specify its prototype explicitly.
|
|
||||||
*/
|
|
||||||
size_t GapBuffer_getByteCount(GapBuffer *buff)
|
|
||||||
{
|
{
|
||||||
return buff->total - buff->gap_length;
|
return buff->total - buff->gap_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Symbol: GapBuffer_createUsingMemory
|
GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len, void (*free)(void*))
|
||||||
** 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)
|
|
||||||
{
|
{
|
||||||
if (mem == NULL || len < sizeof(GapBuffer))
|
if (mem == NULL || len < sizeof(GapBuffer)) {
|
||||||
|
if (free) free(mem);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
size_t capacity = len - sizeof(GapBuffer);
|
size_t capacity = len - sizeof(GapBuffer);
|
||||||
|
|
||||||
@@ -67,37 +42,7 @@ GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len)
|
|||||||
buff->gap_offset = 0;
|
buff->gap_offset = 0;
|
||||||
buff->gap_length = capacity;
|
buff->gap_length = capacity;
|
||||||
buff->total = capacity;
|
buff->total = capacity;
|
||||||
buff->no_resize = true;
|
buff->free = free;
|
||||||
buff->is_static = true;
|
|
||||||
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 *buff = malloc(sizeof(GapBuffer) + capacity);
|
|
||||||
if (buff == NULL)
|
|
||||||
return NULL;
|
|
||||||
buff->gap_offset = 0;
|
|
||||||
buff->gap_length = capacity;
|
|
||||||
buff->total = capacity;
|
|
||||||
buff->no_resize = false;
|
|
||||||
buff->is_static = false;
|
|
||||||
return buff;
|
return buff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,15 +51,15 @@ GapBuffer *GapBuffer_create(size_t capacity)
|
|||||||
*/
|
*/
|
||||||
void GapBuffer_destroy(GapBuffer *buff)
|
void GapBuffer_destroy(GapBuffer *buff)
|
||||||
{
|
{
|
||||||
if (!buff->is_static)
|
if (buff->free)
|
||||||
free(buff);
|
buff->free(buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Symbol: getStringBeforeGap
|
/* Symbol: getStringBeforeGap
|
||||||
** Returns a slice to the memory region before the gap
|
** Returns a slice to the memory region before the gap
|
||||||
** in the form of a (pointer, length) pair.
|
** in the form of a (pointer, length) pair.
|
||||||
*/
|
*/
|
||||||
static String getStringBeforeGap(GapBuffer *buff)
|
PRIVATE String getStringBeforeGap(const GapBuffer *buff)
|
||||||
{
|
{
|
||||||
return (String) {
|
return (String) {
|
||||||
|
|
||||||
@@ -130,7 +75,7 @@ static String getStringBeforeGap(GapBuffer *buff)
|
|||||||
** Returns a slice to the memory region after the gap
|
** Returns a slice to the memory region after the gap
|
||||||
** in the form of a (pointer, length) pair.
|
** in the form of a (pointer, length) pair.
|
||||||
*/
|
*/
|
||||||
static String getStringAfterGap(GapBuffer *buff)
|
PRIVATE String getStringAfterGap(const GapBuffer *buff)
|
||||||
{
|
{
|
||||||
// The first byte after the gap is the offset
|
// The first byte after the gap is the offset
|
||||||
// of the text that comes after the gap and
|
// of the text that comes after the gap and
|
||||||
@@ -146,111 +91,53 @@ static String getStringAfterGap(GapBuffer *buff)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forward declaration of [growGap] because [growGap]
|
PRIVATE bool insertBytesBeforeCursor(GapBuffer *buff, String str)
|
||||||
** 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);
|
|
||||||
|
|
||||||
/* 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)
|
|
||||||
{
|
{
|
||||||
if ((*buff)->gap_length < min) {
|
if (buff->gap_length < str.size)
|
||||||
|
|
||||||
if ((*buff)->no_resize)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
GapBuffer *new_buff = growGap(*buff, min);
|
|
||||||
if (new_buff == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Free up the old buffer and swap the old
|
|
||||||
// parent's reference with the new one.
|
|
||||||
GapBuffer_destroy(*buff);
|
|
||||||
*buff = new_buff;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool insertBytesBeforeCursor(GapBuffer **buff, String str)
|
|
||||||
{
|
|
||||||
if (!ensureSpace(buff, str.size))
|
|
||||||
return false;
|
return false;
|
||||||
memcpy((*buff)->data + (*buff)->gap_offset, str.data, str.size);
|
|
||||||
(*buff)->gap_offset += str.size;
|
memcpy(buff->data + buff->gap_offset, str.data, str.size);
|
||||||
(*buff)->gap_length -= str.size;
|
buff->gap_offset += str.size;
|
||||||
|
buff->gap_length -= str.size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool insertBytesAfterCursor(GapBuffer **buff, String str)
|
PRIVATE bool insertBytesAfterCursor(GapBuffer *buff, String str)
|
||||||
{
|
{
|
||||||
if (!ensureSpace(buff, str.size))
|
if (buff->gap_length < str.size)
|
||||||
return false;
|
return false;
|
||||||
memcpy((*buff)->data + (*buff)->gap_offset + (*buff)->gap_length - str.size, str.data, str.size);
|
|
||||||
(*buff)->gap_length -= str.size;
|
memcpy(buff->data + buff->gap_offset + buff->gap_length - str.size, str.data, str.size);
|
||||||
|
buff->gap_length -= str.size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GapBuffer *growGap(GapBuffer *buff, size_t min)
|
GapBuffer *GapBuffer_cloneUsingMemory(void *mem, size_t len,
|
||||||
|
void (*free)(void*),
|
||||||
|
const GapBuffer *src)
|
||||||
{
|
{
|
||||||
size_t capacity = MAX(2 * buff->total, buff->total + min);
|
GapBuffer *clone = GapBuffer_createUsingMemory(mem, len, free);
|
||||||
GapBuffer *new_buff = GapBuffer_create(capacity);
|
if (!clone)
|
||||||
if (new_buff == NULL)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if(!insertBytesBeforeCursor(&new_buff, getStringBeforeGap(buff)) ||
|
String before = getStringBeforeGap(src);
|
||||||
!insertBytesAfterCursor(&new_buff, getStringAfterGap(buff))) {
|
if (!insertBytesBeforeCursor(clone, before))
|
||||||
GapBuffer_destroy(new_buff);
|
goto oopsie;
|
||||||
return NULL;
|
|
||||||
}
|
String after = getStringAfterGap(src);
|
||||||
return new_buff;
|
if (!insertBytesAfterCursor(clone, after))
|
||||||
|
goto oopsie;
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
|
||||||
|
oopsie:
|
||||||
|
GapBuffer_destroy(clone);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if and only if the [byte] is in the form 10xxxxxx
|
// Returns true if and only if the [byte] is in the form 10xxxxxx
|
||||||
static bool isSymbolAuxiliaryByte(uint8_t byte)
|
PRIVATE bool isSymbolAuxiliaryByte(uint8_t byte)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
0000 0
|
|
||||||
0001 1
|
|
||||||
0010 2
|
|
||||||
0011 3
|
|
||||||
0100 4
|
|
||||||
0101 5
|
|
||||||
0110 6
|
|
||||||
0111 7
|
|
||||||
1000 8
|
|
||||||
1001 9
|
|
||||||
1010 A 10
|
|
||||||
1011 B 11
|
|
||||||
1100 C 12
|
|
||||||
1101 D 13
|
|
||||||
1110 E 14
|
|
||||||
1111 F 15
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Hex Binary
|
// Hex Binary
|
||||||
// +-----+----------+
|
// +-----+----------+
|
||||||
// | C0 | 11000000 |
|
// | C0 | 11000000 |
|
||||||
@@ -261,30 +148,7 @@ static bool isSymbolAuxiliaryByte(uint8_t byte)
|
|||||||
return (byte & 0xC0) == 0x80;
|
return (byte & 0xC0) == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Symbol: getSymbolRune
|
PRIVATE int getSymbolRune(const char *sym, size_t symlen, uint32_t *rune)
|
||||||
** Converts a UTF-8 symbol to UTF-32.
|
|
||||||
**
|
|
||||||
** Arguments:
|
|
||||||
** - sym: string of bytes containing the UTF-8 symbol.
|
|
||||||
**
|
|
||||||
** - symlen: Length (in bytes) of the string [sym].
|
|
||||||
** I
|
|
||||||
** - rune: The location where the UTF-32 result will
|
|
||||||
** be stored. This argument isn't optional
|
|
||||||
** (you can pass NULL).
|
|
||||||
**
|
|
||||||
** Returns:
|
|
||||||
** The number of bytes of [sym] considered to
|
|
||||||
** decode the unicode symbol, 0 if the source
|
|
||||||
** string was empty or -1 if the source contained
|
|
||||||
** invalid UTF-8.
|
|
||||||
**
|
|
||||||
** Notes:
|
|
||||||
** - The (sym, symlen) pair can refer to a string of
|
|
||||||
** more than one unicode symbol.
|
|
||||||
*/
|
|
||||||
#warning "temporarily not static"
|
|
||||||
int getSymbolRune(const char *sym, size_t symlen, uint32_t *rune)
|
|
||||||
{
|
{
|
||||||
if(symlen == 0)
|
if(symlen == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -375,34 +239,7 @@ int getSymbolRune(const char *sym, size_t symlen, uint32_t *rune)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Symbol: GapBuffer_insertString
|
PRIVATE bool isValidUTF8(const char *str, size_t len)
|
||||||
** Insert a UTF-8 encoded string in the gap buffer,
|
|
||||||
** right before the cursor. If there's not enough
|
|
||||||
** space to store the string and the gap buffer
|
|
||||||
** was't instanciated with [GapBuffer_createUsingMemory],
|
|
||||||
** relocation to a bigger memory region occurres.
|
|
||||||
**
|
|
||||||
** Arguments:
|
|
||||||
** - buff: Reference to the caller's reference to the
|
|
||||||
** buffer. The caller's handle is changed if
|
|
||||||
** relocation occurres.
|
|
||||||
**
|
|
||||||
** - str: Pointer to the UTF-8 string to be inserted.
|
|
||||||
** I
|
|
||||||
** - len: Length (in bytes) of the input string.
|
|
||||||
**
|
|
||||||
** Returns:
|
|
||||||
** True if the text was inserted and false if either:
|
|
||||||
**
|
|
||||||
** - The buffer couldn't be relocated because
|
|
||||||
** instanciated with a fixed memory pool.
|
|
||||||
**
|
|
||||||
** - The buffer couldn't be relocated because
|
|
||||||
** a new memory region couldn't be allocated.
|
|
||||||
**
|
|
||||||
** - The input string wasn't valid UTF-8.
|
|
||||||
*/
|
|
||||||
bool GapBuffer_insertString(GapBuffer **buff, const char *str, size_t len)
|
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
while (i < len) {
|
while (i < len) {
|
||||||
@@ -412,10 +249,35 @@ bool GapBuffer_insertString(GapBuffer **buff, const char *str, size_t len)
|
|||||||
return false;
|
return false;
|
||||||
i += n;
|
i += n;
|
||||||
}
|
}
|
||||||
return insertBytesBeforeCursor(buff, (String) {.data=(const uint8_t*) str, .size=len});
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t getPrecedingSymbol(GapBuffer *buff, size_t num)
|
bool GapBuffer_insertString(GapBuffer *buff, const char *str, size_t len)
|
||||||
|
{
|
||||||
|
if (!isValidUTF8(str, len))
|
||||||
|
return false;
|
||||||
|
return insertBytesBeforeCursor(buff, (String) {.data=str, .size=len});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Symbol: getPrecedingSymbol
|
||||||
|
**
|
||||||
|
** Calculate the absolute byte offset of the
|
||||||
|
** [num]-th unicode symbol preceding the cursor.
|
||||||
|
**
|
||||||
|
** If less than [num] symbols precede the
|
||||||
|
** cursor, 0 is returned.
|
||||||
|
**
|
||||||
|
** Arguments:
|
||||||
|
** - buff: Reference to the gap buffer
|
||||||
|
**
|
||||||
|
** - num: Position of the unicode symbol preceding
|
||||||
|
** the cursor of which the offset should be
|
||||||
|
** returned, relative to the cursor.
|
||||||
|
**
|
||||||
|
** Notes:
|
||||||
|
** - It's analogous to getFollowingSymbol.
|
||||||
|
*/
|
||||||
|
PRIVATE size_t getPrecedingSymbol(GapBuffer *buff, size_t num)
|
||||||
{
|
{
|
||||||
size_t i = buff->gap_offset;
|
size_t i = buff->gap_offset;
|
||||||
|
|
||||||
@@ -441,7 +303,7 @@ static size_t getPrecedingSymbol(GapBuffer *buff, size_t num)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t getSymbolLengthFromFirstByte(uint8_t first)
|
PRIVATE size_t getSymbolLengthFromFirstByte(uint8_t first)
|
||||||
{
|
{
|
||||||
// NOTE: It's assumed a valid first byte
|
// NOTE: It's assumed a valid first byte
|
||||||
if (first >= 0xf0)
|
if (first >= 0xf0)
|
||||||
@@ -453,7 +315,25 @@ static size_t getSymbolLengthFromFirstByte(uint8_t first)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t getFollowingSymbol(GapBuffer *buff, size_t num)
|
/* Symbol: getFollowingSymbol
|
||||||
|
**
|
||||||
|
** Calculate the absolute byte offset of the
|
||||||
|
** [num]-th unicode symbol following the cursor.
|
||||||
|
**
|
||||||
|
** If less than [num] symbols follow the cursor,
|
||||||
|
** 0 is returned.
|
||||||
|
**
|
||||||
|
** Arguments:
|
||||||
|
** - buff: Reference to the gap buffer
|
||||||
|
**
|
||||||
|
** - num: Position of the unicode symbol following
|
||||||
|
** the cursor of which the offset should be
|
||||||
|
** returned, relative to the cursor.
|
||||||
|
**
|
||||||
|
** Notes:
|
||||||
|
** - It's analogous to getPrecedingSymbol.
|
||||||
|
*/
|
||||||
|
PRIVATE size_t getFollowingSymbol(GapBuffer *buff, size_t num)
|
||||||
{
|
{
|
||||||
size_t i = buff->gap_offset + buff->gap_length;
|
size_t i = buff->gap_offset + buff->gap_length;
|
||||||
while (num > 0 && i < buff->total) {
|
while (num > 0 && i < buff->total) {
|
||||||
@@ -476,7 +356,7 @@ void GapBuffer_removeBackwards(GapBuffer *buff, size_t num)
|
|||||||
buff->gap_offset = i;
|
buff->gap_offset = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moveBytesAfterGap(GapBuffer *buff, size_t num)
|
PRIVATE void moveBytesAfterGap(GapBuffer *buff, size_t num)
|
||||||
{
|
{
|
||||||
assert(buff->gap_offset >= num);
|
assert(buff->gap_offset >= num);
|
||||||
|
|
||||||
@@ -490,7 +370,7 @@ static void moveBytesAfterGap(GapBuffer *buff, size_t num)
|
|||||||
buff->gap_offset -= num;
|
buff->gap_offset -= num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moveBytesBeforeGap(GapBuffer *buff, size_t num)
|
PRIVATE void moveBytesBeforeGap(GapBuffer *buff, size_t num)
|
||||||
{
|
{
|
||||||
assert(buff->total - buff->gap_offset - buff->gap_length >= num); // FIXME: This triggers sometimes
|
assert(buff->total - buff->gap_offset - buff->gap_length >= num); // FIXME: This triggers sometimes
|
||||||
|
|
||||||
@@ -550,18 +430,12 @@ void GapBufferIter_init(GapBufferIter *iter, GapBuffer *buff)
|
|||||||
|
|
||||||
void GapBufferIter_free(GapBufferIter *iter)
|
void GapBufferIter_free(GapBufferIter *iter)
|
||||||
{
|
{
|
||||||
if (iter->mem != NULL) {
|
iter->mem = NULL;
|
||||||
free(iter->mem);
|
|
||||||
iter->mem = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GapBufferIter_next(GapBufferIter *iter, GapBufferLine *line)
|
bool GapBufferIter_next(GapBufferIter *iter, GapBufferLine *line)
|
||||||
{
|
{
|
||||||
if (iter->mem != NULL) {
|
iter->mem = NULL;
|
||||||
free(iter->mem);
|
|
||||||
iter->mem = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t i = iter->cur;
|
size_t i = iter->cur;
|
||||||
size_t total = iter->buff->total;
|
size_t total = iter->buff->total;
|
||||||
@@ -611,24 +485,15 @@ bool GapBufferIter_next(GapBufferIter *iter, GapBufferLine *line)
|
|||||||
iter->crossed_gap = true;
|
iter->crossed_gap = true;
|
||||||
|
|
||||||
if (line_length + line_length_2 > sizeof(iter->maybe)) {
|
if (line_length + line_length_2 > sizeof(iter->maybe)) {
|
||||||
char *str = malloc(line_length + line_length_2);
|
// Line will be truncated
|
||||||
if (str == NULL) {
|
if (line_length > sizeof(iter->maybe))
|
||||||
// Line will be truncated
|
memcpy(iter->maybe, data + line_offset, sizeof(iter->maybe));
|
||||||
if (line_length > sizeof(iter->maybe))
|
else {
|
||||||
memcpy(iter->maybe, data + line_offset, sizeof(iter->maybe));
|
memcpy(iter->maybe, data + line_offset, line_length);
|
||||||
else {
|
memcpy(iter->maybe + line_offset, data + line_offset_2, sizeof(iter->maybe) - line_length);
|
||||||
memcpy(iter->maybe, data + line_offset, line_length);
|
|
||||||
memcpy(iter->maybe + line_offset, data + line_offset_2, sizeof(iter->maybe) - line_length);
|
|
||||||
}
|
|
||||||
line->str = iter->maybe;
|
|
||||||
line->len = line_length + line_length_2;
|
|
||||||
} else {
|
|
||||||
iter->mem = str;
|
|
||||||
memcpy(str, data + line_offset, line_length);
|
|
||||||
memcpy(str + line_length, data + line_offset_2, line_length_2);
|
|
||||||
line->str = str;
|
|
||||||
line->len = line_length + line_length_2;
|
|
||||||
}
|
}
|
||||||
|
line->str = iter->maybe;
|
||||||
|
line->len = line_length + line_length_2;
|
||||||
} else {
|
} else {
|
||||||
memcpy(iter->maybe, data + line_offset, line_length);
|
memcpy(iter->maybe, data + line_offset, line_length);
|
||||||
memcpy(iter->maybe + line_length, data + line_offset_2, line_length_2);
|
memcpy(iter->maybe + line_length, data + line_offset_2, line_length_2);
|
||||||
@@ -646,3 +511,35 @@ bool GapBufferIter_next(GapBufferIter *iter, GapBufferLine *line)
|
|||||||
iter->cur = i;
|
iter->cur = i;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef GAPBUFFER_NOMALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
GapBuffer *GapBuffer_create(size_t capacity)
|
||||||
|
{
|
||||||
|
size_t len = sizeof(GapBuffer) + capacity;
|
||||||
|
void *mem = malloc(len);
|
||||||
|
return GapBuffer_createUsingMemory(mem, len, free);
|
||||||
|
}
|
||||||
|
bool GapBuffer_insertStringMaybeRelocate(GapBuffer **buff, const char *str, size_t len)
|
||||||
|
{
|
||||||
|
if (!GapBuffer_insertString(*buff, str, len)) {
|
||||||
|
// Need to relocate
|
||||||
|
GapBuffer *buff2 = GapBuffer_create(getByteCount(*buff) + len);
|
||||||
|
if (buff2 == NULL)
|
||||||
|
return false; // Failed to create new location
|
||||||
|
|
||||||
|
if (!GapBuffer_insertString(buff2, str, len)) {
|
||||||
|
// Insertion failed unexpectedly. The gap was created
|
||||||
|
// with enough free memory to hold the new text..
|
||||||
|
GapBuffer_destroy(buff2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap the parent buffer with the new one
|
||||||
|
GapBuffer_destroy(*buff);
|
||||||
|
*buff = buff2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+8
-9
@@ -1,6 +1,3 @@
|
|||||||
#warning "temp"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
@@ -11,7 +8,7 @@ typedef struct {
|
|||||||
bool crossed_gap;
|
bool crossed_gap;
|
||||||
size_t cur;
|
size_t cur;
|
||||||
void *mem;
|
void *mem;
|
||||||
char maybe[256];
|
char maybe[512];
|
||||||
} GapBufferIter;
|
} GapBufferIter;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -19,10 +16,10 @@ typedef struct {
|
|||||||
size_t len;
|
size_t len;
|
||||||
} GapBufferLine;
|
} GapBufferLine;
|
||||||
|
|
||||||
GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len);
|
GapBuffer *GapBuffer_createUsingMemory(void *mem, size_t len, void (*free)(void*));
|
||||||
GapBuffer *GapBuffer_create(size_t capacity);
|
GapBuffer *GapBuffer_cloneUsingMemory(void *mem, size_t len, void (*free)(void*), const GapBuffer *src);
|
||||||
void GapBuffer_destroy(GapBuffer *buff);
|
void GapBuffer_destroy(GapBuffer *buff);
|
||||||
bool GapBuffer_insertString(GapBuffer **buff, const char *str, size_t len);
|
bool GapBuffer_insertString(GapBuffer *buff, const char *str, size_t len);
|
||||||
void GapBuffer_moveRelative(GapBuffer *buff, int off);
|
void GapBuffer_moveRelative(GapBuffer *buff, int off);
|
||||||
void GapBuffer_moveAbsolute(GapBuffer *buff, size_t num);
|
void GapBuffer_moveAbsolute(GapBuffer *buff, size_t num);
|
||||||
void GapBuffer_removeForwards(GapBuffer *buff, size_t num);
|
void GapBuffer_removeForwards(GapBuffer *buff, size_t num);
|
||||||
@@ -31,5 +28,7 @@ void GapBufferIter_init(GapBufferIter *iter, GapBuffer *buff);
|
|||||||
void GapBufferIter_free(GapBufferIter *iter);
|
void GapBufferIter_free(GapBufferIter *iter);
|
||||||
bool GapBufferIter_next(GapBufferIter *iter, GapBufferLine *line);
|
bool GapBufferIter_next(GapBufferIter *iter, GapBufferLine *line);
|
||||||
|
|
||||||
#warning "temporarily not static"
|
#ifndef GAPBUFFER_NOMALLOC
|
||||||
int getSymbolRune(const char *sym, size_t symlen, uint32_t *rune);
|
GapBuffer *GapBuffer_create(size_t capacity);
|
||||||
|
bool GapBuffer_insertStringMaybeRelocate(GapBuffer **buff, const char *str, size_t len);
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
all: test
|
||||||
|
|
||||||
|
test: test.c gap_buffer.c
|
||||||
|
gcc $^ -o $@ -Wall -Wextra -DGAPBUFFER_DEBUG
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm test
|
||||||
@@ -6,6 +6,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "gap_buffer.h"
|
#include "gap_buffer.h"
|
||||||
|
|
||||||
|
size_t getByteCount(GapBuffer *buff);
|
||||||
|
int getSymbolRune(const char *sym, size_t symlen, uint32_t *rune);
|
||||||
|
|
||||||
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
|
||||||
static size_t generateUnsignedIntegerBetween(size_t min, size_t max)
|
static size_t generateUnsignedIntegerBetween(size_t min, size_t max)
|
||||||
@@ -63,7 +66,7 @@ static size_t generateUTF8String(char *dst, size_t max)
|
|||||||
uint32_t rune2;
|
uint32_t rune2;
|
||||||
int k = getSymbolRune((char*) temp, num, &rune2);
|
int k = getSymbolRune((char*) temp, num, &rune2);
|
||||||
assert(k >= 0);
|
assert(k >= 0);
|
||||||
assert(k == num);
|
assert((size_t) k == num);
|
||||||
assert(rune == rune2);
|
assert(rune == rune2);
|
||||||
|
|
||||||
memcpy(dst + len, temp, num);
|
memcpy(dst + len, temp, num);
|
||||||
@@ -72,7 +75,8 @@ static size_t generateUTF8String(char *dst, size_t max)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printStringAsHex(char *str, size_t len, FILE *stream)
|
/*
|
||||||
|
static void printStringAsHex(char *str, size_t len, FILE *stream)
|
||||||
{
|
{
|
||||||
fprintf(stream, "[ ");
|
fprintf(stream, "[ ");
|
||||||
for (size_t i = 0; i < len; i++) {
|
for (size_t i = 0; i < len; i++) {
|
||||||
@@ -81,6 +85,7 @@ void printStringAsHex(char *str, size_t len, FILE *stream)
|
|||||||
}
|
}
|
||||||
fprintf(stream, "]");
|
fprintf(stream, "]");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
@@ -94,7 +99,7 @@ int main(void)
|
|||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
size_t len = generateString(buffer, sizeof(buffer));
|
size_t len = generateString(buffer, sizeof(buffer));
|
||||||
bool done = GapBuffer_insertString(&gap_buffer, buffer, len);
|
bool done = GapBuffer_insertStringMaybeRelocate(&gap_buffer, buffer, len);
|
||||||
fprintf(stderr, "INSERT %ld \"%.*s\" .. %s\n", len, (int) len, buffer, done ? "DONE" : "NOT DONE");
|
fprintf(stderr, "INSERT %ld \"%.*s\" .. %s\n", len, (int) len, buffer, done ? "DONE" : "NOT DONE");
|
||||||
//printStringAsHex(buffer, len, stderr);
|
//printStringAsHex(buffer, len, stderr);
|
||||||
//fprintf(stderr, "\n");
|
//fprintf(stderr, "\n");
|
||||||
@@ -104,7 +109,7 @@ int main(void)
|
|||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
size_t len = generateUTF8String(buffer, sizeof(buffer));
|
size_t len = generateUTF8String(buffer, sizeof(buffer));
|
||||||
bool done = GapBuffer_insertString(&gap_buffer, buffer, len);
|
bool done = GapBuffer_insertStringMaybeRelocate(&gap_buffer, buffer, len);
|
||||||
fprintf(stderr, "INSERT %ld \"%.*s\" .. %s\n", len, (int) len, buffer, done ? "DONE" : "NOT DONE");
|
fprintf(stderr, "INSERT %ld \"%.*s\" .. %s\n", len, (int) len, buffer, done ? "DONE" : "NOT DONE");
|
||||||
//printStringAsHex(buffer, len, stderr);
|
//printStringAsHex(buffer, len, stderr);
|
||||||
//fprintf(stderr, "\n");
|
//fprintf(stderr, "\n");
|
||||||
@@ -113,7 +118,7 @@ int main(void)
|
|||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
size_t limit = 1.5 * GapBuffer_getSize(gap_buffer);
|
size_t limit = 1.5 * getByteCount(gap_buffer);
|
||||||
size_t index = generateUnsignedIntegerBetween(0, limit);
|
size_t index = generateUnsignedIntegerBetween(0, limit);
|
||||||
fprintf(stderr, "MOVE_ABSOLUTE %ld\n", index);
|
fprintf(stderr, "MOVE_ABSOLUTE %ld\n", index);
|
||||||
GapBuffer_moveAbsolute(gap_buffer, index);
|
GapBuffer_moveAbsolute(gap_buffer, index);
|
||||||
@@ -122,7 +127,7 @@ int main(void)
|
|||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
size_t limit = 1.5 * GapBuffer_getSize(gap_buffer);
|
size_t limit = 1.5 * getByteCount(gap_buffer);
|
||||||
size_t index = generateUnsignedIntegerBetween(0, limit);
|
size_t index = generateUnsignedIntegerBetween(0, limit);
|
||||||
fprintf(stderr, "MOVE_RELATIVE %ld\n", index);
|
fprintf(stderr, "MOVE_RELATIVE %ld\n", index);
|
||||||
GapBuffer_moveRelative(gap_buffer, index);
|
GapBuffer_moveRelative(gap_buffer, index);
|
||||||
@@ -132,7 +137,7 @@ int main(void)
|
|||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
size_t limit = 1.5 * GapBuffer_getSize(gap_buffer);
|
size_t limit = 1.5 * getByteCount(gap_buffer);
|
||||||
size_t length = generateUnsignedIntegerBetween(0, limit);
|
size_t length = generateUnsignedIntegerBetween(0, limit);
|
||||||
fprintf(stderr, "REMOVE_FORWARDS %ld\n", length);
|
fprintf(stderr, "REMOVE_FORWARDS %ld\n", length);
|
||||||
GapBuffer_removeForwards(gap_buffer, length);
|
GapBuffer_removeForwards(gap_buffer, length);
|
||||||
@@ -141,7 +146,7 @@ int main(void)
|
|||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
{
|
{
|
||||||
size_t limit = 1.5 * GapBuffer_getSize(gap_buffer);
|
size_t limit = 1.5 * getByteCount(gap_buffer);
|
||||||
size_t length = generateUnsignedIntegerBetween(0, limit);
|
size_t length = generateUnsignedIntegerBetween(0, limit);
|
||||||
fprintf(stderr, "REMOVE_BACKWARDS %ld\n", length);
|
fprintf(stderr, "REMOVE_BACKWARDS %ld\n", length);
|
||||||
GapBuffer_removeBackwards(gap_buffer, length);
|
GapBuffer_removeBackwards(gap_buffer, length);
|
||||||
|
|||||||
Reference in New Issue
Block a user