Use a circular buffer for the savepoints

Avoids mallocs during normal operation.
This commit is contained in:
Kovid Goyal 2016-11-19 11:03:21 +05:30
parent 6293b37ead
commit e3b9bfd4bb
4 changed files with 48 additions and 66 deletions

View File

@ -55,6 +55,12 @@ void cursor_reset(Cursor *self) {
self->color = 0; self->hidden = false;
}
void cursor_copy_to(Cursor *src, Cursor *dest) {
#define CCY(x) dest->x = src->x;
CCY(x); CCY(y); CCY(shape); CCY(blink); CCY(color); CCY(hidden);
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
}
static PyObject*
copy(Cursor *self);
#define copy_doc "Create a clone of this cursor"
@ -127,12 +133,10 @@ RICHCMP(Cursor)
Cursor*
cursor_copy(Cursor *self) {
#define CCY(x) ans->x = self->x;
Cursor* ans;
ans = alloc_cursor();
if (ans == NULL) { PyErr_NoMemory(); return NULL; }
CCY(x); CCY(y); CCY(shape); CCY(blink); CCY(color); CCY(hidden);
CCY(bold); CCY(italic); CCY(strikethrough); CCY(reverse); CCY(decoration); CCY(fg); CCY(bg); CCY(decoration_fg);
cursor_copy_to(self, ans);
return ans;
}

View File

@ -213,17 +213,22 @@ typedef struct {
PyTypeObject ScreenModes_Type;
typedef struct {
PyObject_HEAD
unsigned int current_charset;
uint16_t *g0_charset, *g1_charset;
uint32_t utf8_state;
Cursor *cursor;
Cursor cursor;
bool mDECOM;
bool mDECAWM;
} Savepoint;
PyTypeObject Savepoint_Type;
#define SAVEPOINTS_SZ 256
typedef struct {
Savepoint buf[SAVEPOINTS_SZ];
Savepoint *start_of_data;
Savepoint *end_of_data;
} SavepointBuffer;
#define PARSER_BUF_SZ 8192
@ -235,7 +240,8 @@ typedef struct {
uint32_t utf8_state;
uint16_t *g0_charset, *g1_charset;
Cursor *cursor;
PyObject *savepoints, *main_savepoints, *alt_savepoints, *callbacks;
SavepointBuffer main_savepoints, alt_savepoints;
PyObject *callbacks;
LineBuf *linebuf, *main_linebuf, *alt_linebuf;
bool *tabstops;
ChangeTracker *change_tracker;
@ -273,8 +279,11 @@ PyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *);
PyObject* parse_bytes(PyObject UNUSED *, PyObject *);
uint16_t* translation_table(char);
uint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte);
Savepoint* savepoints_pop(SavepointBuffer *pts);
Savepoint* savepoints_push(SavepointBuffer *pts);
void cursor_reset(Cursor*);
Cursor* cursor_copy(Cursor*);
void cursor_copy_to(Cursor *src, Cursor *dest);
void cursor_reset_display_attrs(Cursor*);
bool update_cell_range_data(SpriteMap *, Line *, unsigned int, unsigned int, ColorProfile *, const uint32_t, const uint32_t, unsigned int *);
uint32_t to_color(ColorProfile *, uint32_t, uint32_t);

View File

@ -7,41 +7,20 @@
#include "data-types.h"
static PyObject *
new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
Savepoint *self;
self = (Savepoint *)type->tp_alloc(type, 0);
return (PyObject*) self;
#define ADVANCE(x) \
self->x = (self->x >= self->buf + SAVEPOINTS_SZ - 1) ? self->buf : self->x + 1;
#define RETREAT(x) \
self->x = self->x == self->buf ? self->buf + SAVEPOINTS_SZ - 1 : self->x - 1;
Savepoint* savepoints_push(SavepointBuffer *self) {
ADVANCE(end_of_data);
if (self->end_of_data == self->start_of_data) ADVANCE(start_of_data);
return self->end_of_data;
}
static void
dealloc(Savepoint* self) {
Py_TYPE(self)->tp_free((PyObject*)self);
Savepoint* savepoints_pop(SavepointBuffer *self) {
if (self->start_of_data == self->end_of_data) return NULL;
RETREAT(end_of_data);
return self->end_of_data;
}
// Boilerplate {{{
static PyMethodDef methods[] = {
{NULL} /* Sentinel */
};
PyTypeObject Savepoint_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.Savepoint",
.tp_basicsize = sizeof(Savepoint),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "Savepoint",
.tp_methods = methods,
.tp_new = new,
};
INIT_TYPE(Savepoint)
Savepoint *alloc_savepoint() {
return (Savepoint*)new(&Savepoint_Type, NULL, NULL);
}
// }}}

View File

@ -34,12 +34,9 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self->cursor = alloc_cursor();
self->main_linebuf = alloc_linebuf(lines, columns); self->alt_linebuf = alloc_linebuf(lines, columns);
self->linebuf = self->main_linebuf;
// TODO: Change the savepoints objects to use a circular buffer, so there are no mallocs during normal operation
self->main_savepoints = PyList_New(0); self->alt_savepoints = PyList_New(0);
self->savepoints = self->main_savepoints;
self->change_tracker = alloc_change_tracker(lines, columns);
self->tabstops = PyMem_Calloc(self->columns, sizeof(bool));
if (self->cursor == NULL || self->main_linebuf == NULL || self->alt_linebuf == NULL || self->main_savepoints == NULL || self->alt_savepoints == NULL || self->change_tracker == NULL || self->tabstops == NULL) {
if (self->cursor == NULL || self->main_linebuf == NULL || self->alt_linebuf == NULL || self->change_tracker == NULL || self->tabstops == NULL) {
Py_CLEAR(self); return NULL;
}
}
@ -114,7 +111,7 @@ dealloc(Screen* self) {
Py_CLEAR(self->cursor);
Py_CLEAR(self->main_linebuf);
Py_CLEAR(self->alt_linebuf);
Py_CLEAR(self->main_savepoints); Py_CLEAR(self->alt_savepoints); Py_CLEAR(self->change_tracker);
Py_CLEAR(self->change_tracker);
PyMem_Free(self->tabstops);
Py_TYPE(self)->tp_free((PyObject*)self);
} // }}}
@ -341,10 +338,8 @@ void screen_toggle_screen_buffer(Screen *self) {
screen_save_cursor(self);
if (self->linebuf == self->main_linebuf) {
self->linebuf = self->alt_linebuf;
self->savepoints = self->alt_savepoints;
} else {
self->linebuf = self->main_linebuf;
self->savepoints = self->main_savepoints;
}
screen_restore_cursor(self);
tracker_update_screen(self->change_tracker);
@ -573,38 +568,33 @@ void screen_linefeed(Screen *self, uint8_t UNUSED ch) {
}
void screen_save_cursor(Screen *self) {
// We fail silently on out of memory errors
Savepoint *sp = alloc_savepoint();
if (sp == NULL) { PyErr_Clear(); return; }
sp->cursor = cursor_copy(self->cursor);
if (sp->cursor == NULL) { Py_CLEAR(sp); PyErr_Clear(); return; }
SavepointBuffer *pts = self->linebuf == self->main_linebuf ? &self->main_savepoints : &self->alt_savepoints;
Savepoint *sp = savepoints_push(pts);
cursor_copy_to(self->cursor, &(sp->cursor));
sp->g0_charset = self->g0_charset;
sp->g1_charset = self->g1_charset;
sp->current_charset = self->current_charset;
sp->mDECOM = self->modes.mDECOM;
sp->mDECAWM = self->modes.mDECAWM;
sp->utf8_state = self->utf8_state;
PyList_Append(self->savepoints, (PyObject*)sp); // We ignore failures
Py_CLEAR(sp);
}
void screen_restore_cursor(Screen *self) {
Py_ssize_t sz = PyList_GET_SIZE(self->savepoints);
if (sz > 0) {
Savepoint *sp = (Savepoint*)PyList_GET_ITEM(self->savepoints, sz - 1);
SavepointBuffer *pts = self->linebuf == self->main_linebuf ? &self->main_savepoints : &self->alt_savepoints;
Savepoint *sp = savepoints_pop(pts);
if (sp == NULL) {
screen_cursor_position(self, 1, 1);
tracker_cursor_changed(self->change_tracker);
screen_reset_mode(self, DECOM);
} else {
self->g0_charset = sp->g0_charset;
self->g1_charset = sp->g1_charset;
self->current_charset = sp->current_charset;
self->utf8_state = sp->utf8_state;
if (sp->mDECOM) screen_set_mode(self, DECOM);
if (sp->mDECAWM) screen_set_mode(self, DECAWM);
self->cursor = cursor_copy(sp->cursor);
cursor_copy_to(&(sp->cursor), self->cursor);
screen_ensure_bounds(self, false);
PyList_SetSlice(self->savepoints, sz-1, sz, NULL);
} else {
screen_cursor_position(self, 1, 1);
tracker_cursor_changed(self->change_tracker);
screen_reset_mode(self, DECOM);
}
}