mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 12:52:46 +00:00
Add a buffer-gap text buffer
This should be good for text editing and working with text files in general.
This commit is contained in:
parent
a30433fa9e
commit
f7493fe8fb
5 changed files with 517 additions and 2 deletions
98
include/QF/txtbuffer.h
Normal file
98
include/QF/txtbuffer.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
txtbuffer.h
|
||||
|
||||
Text buffer for edit or scrollback
|
||||
|
||||
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __QF_txtbuffer_h
|
||||
#define __QF_txtbuffer_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/** \defgroup txtbuffer Text buffers
|
||||
\ingroup utils
|
||||
*/
|
||||
///@{
|
||||
|
||||
/// must be a power of 2
|
||||
#define TXTBUFFER_GROW 0x4000
|
||||
|
||||
/** Text buffer implementing efficient editing.
|
||||
*/
|
||||
typedef struct txtbuffer_s {
|
||||
struct txtbuffer_s *next;
|
||||
char *text;
|
||||
size_t textSize; ///< amount of text in the buffer
|
||||
size_t bufferSize; ///< current capacity of the buffer
|
||||
size_t gapOffset; ///< beginning of the gap
|
||||
size_t gapSize; ///< size of gap. gapSize + textSize == bufferSize
|
||||
} txtbuffer_t;
|
||||
|
||||
/** Create a new, empty buffer.
|
||||
*/
|
||||
txtbuffer_t *TextBuffer_Create (void);
|
||||
|
||||
/** Destroy a buffer, freeing all resources connected to it.
|
||||
\param buffer The buffer to destroy.
|
||||
*/
|
||||
void TextBuffer_Destroy (txtbuffer_t *buffer);
|
||||
|
||||
/** Insert a block of text at the specified offset.
|
||||
|
||||
Text after the offset is moved to be after the inserted block of text.
|
||||
The buffer is resized as necessary.
|
||||
nul characters are not significant in that they do not mark the end of
|
||||
the text to be inserted.
|
||||
|
||||
\param buffer The buffer to be updated
|
||||
\param offset The offset in the buffer at which to insert the text block
|
||||
\param text The text block to be inserted. May contain nul ('\0')
|
||||
characters.
|
||||
\param text_len The number of characters to insert.
|
||||
\return 1 for success, 0 for failure (offset not valid or out
|
||||
of memory)
|
||||
*/
|
||||
int TextBuffer_InsertAt (txtbuffer_t *buffer, size_t offset,
|
||||
const char *text, size_t text_len);
|
||||
|
||||
/** Delete a block of text from the buffer.
|
||||
|
||||
The buffer is not resized. Rather, its capacity before resizing is require
|
||||
is increased.
|
||||
|
||||
\param buffer The buffer to be updated
|
||||
\param offset The offset of the beginning of the text block to be deleted
|
||||
\param len The amount of characters to be deleted. Values larger than
|
||||
the amount of text in the buffer beyond the beginning of
|
||||
block are truncated to the amount of remaining text. Thus
|
||||
using excessivly large values sets the offset to be the
|
||||
end of the buffer.
|
||||
\return 1 for success, 0 for failure (offset not valid)
|
||||
*/
|
||||
int TextBuffer_DeleteAt (txtbuffer_t *buffer, size_t offset, size_t len);
|
||||
|
||||
///@}
|
||||
|
||||
#endif//__QF_txtbuffer_h
|
|
@ -53,7 +53,8 @@ libQFutil_la_SOURCES= \
|
|||
fendian.c hash.c idparse.c info.c link.c llist.c \
|
||||
mathlib.c mdfour.c mersenne.c msg.c pakfile.c plugin.c qargs.c qendian.c \
|
||||
qfplist.c quakefs.c quakeio.c riff.c script.c segtext.c set.c sizebuf.c \
|
||||
string.c sys.c va.c ver_check.c vrect.c wad.c wadfile.c zone.c \
|
||||
string.c sys.c txtbuffer.c va.c ver_check.c vrect.c wad.c wadfile.c \
|
||||
zone.c \
|
||||
$(dirent) $(fnmatch) $(getopt)
|
||||
|
||||
EXTRA_DIST= $(fnmatch_src) $(getopt_src)
|
||||
|
|
|
@ -4,7 +4,7 @@ AM_CPPFLAGS= -I$(top_srcdir)/include
|
|||
|
||||
check_PROGRAMS= \
|
||||
test-bary test-cs test-dq test-half test-mat3 test-mat4 test-plist \
|
||||
test-qfs test-quat test-seb test-seg test-set test-vrect
|
||||
test-qfs test-quat test-seb test-seg test-set test-txtbuffer test-vrect
|
||||
|
||||
test_bary_SOURCES=test-bary.c
|
||||
test_bary_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
@ -54,6 +54,10 @@ test_set_SOURCES=test-set.c
|
|||
test_set_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
test_set_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
||||
test_txtbuffer_SOURCES=test-txtbuffer.c
|
||||
test_txtbuffer_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
test_txtbuffer_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
||||
test_vrect_SOURCES=test-vrect.c
|
||||
test_vrect_LDADD=$(top_builddir)/libs/util/libQFutil.la
|
||||
test_vrect_DEPENDENCIES=$(top_builddir)/libs/util/libQFutil.la
|
||||
|
|
240
libs/util/test/test-txtbuffer.c
Normal file
240
libs/util/test/test-txtbuffer.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "QF/txtbuffer.h"
|
||||
|
||||
static size_t
|
||||
check_text_ptr (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
return buffer->text != 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_textSize (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
return buffer->textSize;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_bufferSize (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
return buffer->bufferSize;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_gapOffset (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
return buffer->gapOffset;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_gapSize (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
return buffer->gapSize;
|
||||
}
|
||||
|
||||
static size_t
|
||||
insert_text (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
if (TextBuffer_InsertAt (buffer, offset, txt, length)) {
|
||||
memset (buffer->text + buffer->gapOffset, 0xff, buffer->gapSize);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
delete_text (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
if (TextBuffer_DeleteAt (buffer, offset, length)) {
|
||||
memset (buffer->text + buffer->gapOffset, 0xff, buffer->gapSize);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
destroy_buffer (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
TextBuffer_Destroy (buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef size_t (*test_func) (txtbuffer_t *buffer, size_t offset,
|
||||
const char *text, size_t length);
|
||||
|
||||
static const char test_text[] = {
|
||||
"Guarding the entrance to the Grendal\n"
|
||||
"Gorge is the Shadow Gate, a small keep\n"
|
||||
"and monastary which was once the home\n"
|
||||
"of the Shadow cult.\n\n"
|
||||
"For years the Shadow Gate existed in\n"
|
||||
"obscurity but after the cult discovered\n"
|
||||
"the \3023\354\341\343\353\240\307\341\364\345 in the caves below\n"
|
||||
"the empire took notice.\n"
|
||||
"A batallion of Imperial Knights were\n"
|
||||
"sent to the gate to destroy the cult\n"
|
||||
"and claim the artifact for the King.",
|
||||
};
|
||||
static const char empty[TXTBUFFER_GROW] = { };
|
||||
|
||||
static size_t
|
||||
compare_text (txtbuffer_t *buffer, size_t offset, const char *txt,
|
||||
size_t length)
|
||||
{
|
||||
//printf("%zd %ld %zd\n", offset, txt - test_text, length);
|
||||
size_t res = memcmp (buffer->text + offset, txt, length) == 0;
|
||||
if (!res) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
//printf ("%02x %02x\n", txt[i] & 0xff, buffer->text[offset + i] & 0xff);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define txtsize (sizeof (test_text) - 1)
|
||||
#define txtsize0 txtsize
|
||||
#define bufsize0 (TXTBUFFER_GROW)
|
||||
#define gapoffs0 txtsize0
|
||||
#define gapsize0 (bufsize0 - txtsize0)
|
||||
#define subtxtlen 200
|
||||
#define suboffs 200
|
||||
#define txtsize1 (txtsize0 + subtxtlen)
|
||||
#define bufsize1 (TXTBUFFER_GROW)
|
||||
#define gapoffs1 (suboffs + subtxtlen)
|
||||
#define gapsize1 (bufsize1 - txtsize1)
|
||||
#define deltxtlen 350
|
||||
#define deloffs 150
|
||||
#define txtsize2 (txtsize + subtxtlen - deltxtlen)
|
||||
#define bufsize2 (TXTBUFFER_GROW)
|
||||
#define gapoffs2 (deloffs)
|
||||
#define gapsize2 (bufsize2 - txtsize2)
|
||||
#define deltxtrem (txtsize2 - gapoffs2)
|
||||
#define emptyoffs 370
|
||||
#define txtsize3 (txtsize + sizeof (empty))
|
||||
#define bufsize3 (2 * TXTBUFFER_GROW)
|
||||
#define gapoffs3 (emptyoffs + sizeof (empty))
|
||||
#define gapsize3 (bufsize3 - txtsize3)
|
||||
#define txtsize4 (txtsize + 2 * sizeof (empty))
|
||||
#define bufsize4 (3 * TXTBUFFER_GROW)
|
||||
#define gapoffs4 (emptyoffs + sizeof (empty))
|
||||
#define gapsize4 (bufsize4 - txtsize4)
|
||||
|
||||
struct {
|
||||
test_func test;
|
||||
size_t offset_param;
|
||||
const char *text_param;
|
||||
size_t length_param;
|
||||
int test_expect;
|
||||
} tests[] = {
|
||||
{ check_text_ptr, 0, 0, 0, 0 },
|
||||
{ get_textSize, 0, 0, 0, 0 },
|
||||
{ get_bufferSize, 0, 0, 0, 0 },
|
||||
{ get_gapOffset, 0, 0, 0, 0 },
|
||||
{ get_gapSize, 0, 0, 0, 0 },
|
||||
{ insert_text, 0, test_text, txtsize, 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize0 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize0 },
|
||||
{ get_gapOffset, 0, 0, 0, gapoffs0 },
|
||||
{ get_gapSize, 0, 0, 0, gapsize0 },
|
||||
{ compare_text, 0, test_text, txtsize, 1 },
|
||||
{ insert_text, suboffs, test_text, subtxtlen, 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize1 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize1 },
|
||||
{ get_gapOffset, 0, 0, 0, gapoffs1 },
|
||||
{ get_gapSize, 0, 0, 0, gapsize1 },
|
||||
{ compare_text, 0, test_text, txtsize, 0 },
|
||||
{ compare_text, 0, test_text, suboffs, 1 },
|
||||
{ compare_text, suboffs, test_text, subtxtlen, 1 },
|
||||
{ compare_text, gapoffs1 + gapsize1, test_text + subtxtlen, txtsize - subtxtlen, 1 },
|
||||
{ delete_text, deloffs, 0, deltxtlen, 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize2 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize2 },
|
||||
{ get_gapOffset, 0, 0, 0, gapoffs2 },
|
||||
{ get_gapSize, 0, 0, 0, gapsize2 },
|
||||
{ compare_text, 0, test_text, suboffs, 0 },
|
||||
{ compare_text, suboffs, test_text, subtxtlen, 0 },
|
||||
{ compare_text, gapoffs1 + gapsize1, test_text + subtxtlen, txtsize - subtxtlen, 0 },
|
||||
{ compare_text, 0, test_text, deloffs, 1 },
|
||||
{ compare_text, gapoffs2 + gapsize2, test_text + txtsize - deltxtrem, deltxtrem, 1 },
|
||||
{ compare_text, gapoffs2 + gapsize2 - 1, test_text + txtsize - deltxtrem - 1, 1, 0 },
|
||||
{ delete_text, 0, 0, -1, 1 },
|
||||
{ check_text_ptr, 0, 0, 0, 1 },
|
||||
{ get_textSize, 0, 0, 0, 0 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize2 },
|
||||
{ get_gapOffset, 0, 0, 0, 0 },
|
||||
{ get_gapSize, 0, 0, 0, bufsize2 },
|
||||
{ insert_text, 1, test_text, txtsize, 0 },
|
||||
{ get_textSize, 0, 0, 0, 0 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize2 },
|
||||
{ get_gapOffset, 0, 0, 0, 0 },
|
||||
{ get_gapSize, 0, 0, 0, bufsize2 },
|
||||
{ insert_text, 0, test_text, txtsize, 1 },
|
||||
{ insert_text, 300, 0, 0, 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize0 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize2 },
|
||||
{ get_gapOffset, 0, 0, 0, 300 },
|
||||
{ get_gapSize, 0, 0, 0, gapsize0 },
|
||||
{ compare_text, 0, test_text, 300, 1 },
|
||||
{ compare_text, 300, test_text + 300, 1, 0 },
|
||||
{ compare_text, 300 + gapsize0, test_text + 300, txtsize - 300, 1 },
|
||||
{ compare_text, 300 + gapsize0 - 1, test_text + 300 - 1, 1, 0 },
|
||||
{ insert_text, 350, 0, 0, 1 },
|
||||
{ compare_text, (emptyoffs - 20) + gapsize0, test_text + (emptyoffs - 20), txtsize - (emptyoffs - 20), 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize0 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize2 },
|
||||
{ get_gapOffset, 0, 0, 0, (emptyoffs - 20) },
|
||||
{ get_gapSize, 0, 0, 0, gapsize0 },
|
||||
{ insert_text, emptyoffs, empty, sizeof (empty), 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize3 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize3 },
|
||||
{ get_gapOffset, 0, 0, 0, gapoffs3 },
|
||||
{ get_gapSize, 0, 0, 0, gapsize3 },
|
||||
{ insert_text, emptyoffs, empty, sizeof (empty), 1 },
|
||||
{ get_textSize, 0, 0, 0, txtsize4 },
|
||||
{ get_bufferSize, 0, 0, 0, bufsize4 },
|
||||
{ get_gapOffset, 0, 0, 0, gapoffs4 },
|
||||
{ get_gapSize, 0, 0, 0, gapsize4 },
|
||||
{ destroy_buffer, 0, 0, 0, 0 },
|
||||
};
|
||||
#define num_tests (sizeof (tests) / sizeof (tests[0]))
|
||||
int test_start_line = __LINE__ - num_tests - 2;
|
||||
|
||||
int
|
||||
main (int argc, const char **argv)
|
||||
{
|
||||
size_t i;
|
||||
int res = 0;
|
||||
|
||||
txtbuffer_t *buffer = TextBuffer_Create ();
|
||||
|
||||
for (i = 0; i < num_tests; i++) {
|
||||
if (tests[i].test) {
|
||||
int test_res;
|
||||
test_res = tests[i].test (buffer,
|
||||
tests[i].offset_param,
|
||||
tests[i].text_param,
|
||||
tests[i].length_param);
|
||||
if (test_res != tests[i].test_expect) {
|
||||
res |= 1;
|
||||
printf ("test %d (line %d) failed\n", (int) i,
|
||||
(int) i + test_start_line);
|
||||
printf ("expect: %d\n", tests[i].test_expect);
|
||||
printf ("got : %d\n", test_res);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
172
libs/util/txtbuffer.c
Normal file
172
libs/util/txtbuffer.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
txtbuffer.c
|
||||
|
||||
Text buffer
|
||||
|
||||
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "QF/alloc.h"
|
||||
#include "QF/qtypes.h"
|
||||
#include "QF/sys.h"
|
||||
#include "QF/txtbuffer.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
static txtbuffer_t *txtbuffers_freelist;
|
||||
|
||||
static char *
|
||||
txtbuffer_open_gap (txtbuffer_t *buffer, size_t offset, size_t length)
|
||||
{
|
||||
size_t len;
|
||||
char *dst;
|
||||
char *src;
|
||||
|
||||
if (offset == buffer->gapOffset && length <= buffer->gapSize) {
|
||||
return buffer->text + buffer->gapOffset;
|
||||
}
|
||||
if (length <= buffer->gapSize) {
|
||||
// no resize needed
|
||||
if (offset < buffer->gapOffset) {
|
||||
len = buffer->gapOffset - offset;
|
||||
dst = buffer->text + buffer->gapOffset + buffer->gapSize - len;
|
||||
src = buffer->text + offset;
|
||||
} else {
|
||||
len = offset - buffer->gapOffset;
|
||||
dst = buffer->text + buffer->gapOffset;
|
||||
src = buffer->text + buffer->gapOffset + buffer->gapSize;
|
||||
}
|
||||
memmove (dst, src, len);
|
||||
} else {
|
||||
// need to resize the buffer
|
||||
// grow the buffer by the difference in lengths rounded up to the
|
||||
// next multiple of TXTBUFFER_GROW
|
||||
size_t new_size = buffer->bufferSize + length - buffer->gapSize;
|
||||
new_size = (new_size + TXTBUFFER_GROW - 1) & ~(TXTBUFFER_GROW - 1);
|
||||
char *new_text = realloc (buffer->text, new_size);
|
||||
|
||||
if (!new_text) {
|
||||
return 0;
|
||||
}
|
||||
buffer->text = new_text;
|
||||
|
||||
if (offset < buffer->gapOffset) {
|
||||
// move the old post-gap to the end of the new buffer
|
||||
len = buffer->bufferSize - (buffer->gapOffset + buffer->gapSize);
|
||||
dst = buffer->text + new_size - len;
|
||||
src = buffer->text + buffer->gapOffset + buffer->gapSize;
|
||||
memmove (dst, src, len);
|
||||
// move the remaining chunk to after the end of the new gap or
|
||||
// just before the location of the previous move
|
||||
len = buffer->gapOffset - offset;
|
||||
dst -= len;
|
||||
src = buffer->text + offset;
|
||||
memmove (dst, src, len);
|
||||
} else if (offset > buffer->gapOffset) {
|
||||
// move the old post-offset to the end of the new buffer
|
||||
len = buffer->bufferSize - (offset + buffer->gapSize);
|
||||
dst = buffer->text + new_size - len;
|
||||
src = buffer->text + offset + buffer->gapSize;
|
||||
memmove (dst, src, len);
|
||||
// move the old post-gap to offset block to before the new gap
|
||||
len = offset - buffer->gapOffset;
|
||||
dst = buffer->text + buffer->gapOffset;
|
||||
src = buffer->text + buffer->gapOffset + buffer->gapSize;
|
||||
memmove (dst, src, len);
|
||||
} else {
|
||||
// the gap only grew, did not move
|
||||
len = buffer->bufferSize - (offset + buffer->gapSize);
|
||||
dst = buffer->text + new_size - len;
|
||||
src = buffer->text + buffer->gapOffset + buffer->gapSize;
|
||||
memmove (dst, src, len);
|
||||
}
|
||||
buffer->gapSize += new_size - buffer->bufferSize;
|
||||
buffer->bufferSize = new_size;
|
||||
}
|
||||
buffer->gapOffset = offset;
|
||||
return buffer->text + buffer->gapOffset;
|
||||
}
|
||||
|
||||
VISIBLE txtbuffer_t *
|
||||
TextBuffer_Create (void)
|
||||
{
|
||||
txtbuffer_t *buffer;
|
||||
ALLOC (16, txtbuffer_t, txtbuffers, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
VISIBLE void
|
||||
TextBuffer_Destroy (txtbuffer_t *buffer)
|
||||
{
|
||||
if (buffer->text) {
|
||||
free (buffer->text);
|
||||
}
|
||||
FREE (txtbuffers, buffer);
|
||||
}
|
||||
|
||||
VISIBLE int
|
||||
TextBuffer_InsertAt (txtbuffer_t *buffer, size_t offset,
|
||||
const char *text, size_t text_len)
|
||||
{
|
||||
char *dst;
|
||||
|
||||
if (offset > buffer->textSize) {
|
||||
return 0;
|
||||
}
|
||||
dst = txtbuffer_open_gap (buffer, offset, text_len);
|
||||
if (!dst) {
|
||||
return 0;
|
||||
}
|
||||
memcpy (dst, text, text_len);
|
||||
buffer->textSize += text_len;
|
||||
buffer->gapOffset += text_len;
|
||||
buffer->gapSize -= text_len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
VISIBLE int
|
||||
TextBuffer_DeleteAt (txtbuffer_t *buffer, size_t offset, size_t len)
|
||||
{
|
||||
if (offset > buffer->textSize) {
|
||||
return 0;
|
||||
}
|
||||
// only moves, does not resize, the gap if necessary
|
||||
txtbuffer_open_gap (buffer, offset, buffer->gapSize);
|
||||
// clamp len to the amount of text beyond offset
|
||||
if (len > buffer->textSize - offset) {
|
||||
len = buffer->textSize - offset;
|
||||
}
|
||||
// delete the text
|
||||
buffer->gapSize += len;
|
||||
buffer->textSize -= len;
|
||||
return 1;
|
||||
}
|
Loading…
Reference in a new issue