From e3b9bfd4bba09507f5a917418372c815ca8575ab Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 19 Nov 2016 11:03:21 +0530 Subject: [PATCH] Use a circular buffer for the savepoints Avoids mallocs during normal operation. --- kitty/cursor.c | 10 +++++++--- kitty/data-types.h | 19 +++++++++++++----- kitty/savepoints.c | 49 +++++++++++++--------------------------------- kitty/screen.c | 36 ++++++++++++---------------------- 4 files changed, 48 insertions(+), 66 deletions(-) diff --git a/kitty/cursor.c b/kitty/cursor.c index 2e9a0bb10..1164d341b 100644 --- a/kitty/cursor.c +++ b/kitty/cursor.c @@ -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; } diff --git a/kitty/data-types.h b/kitty/data-types.h index 7393f5753..bc9f23750 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -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); diff --git a/kitty/savepoints.c b/kitty/savepoints.c index e5182e578..53cdac746 100644 --- a/kitty/savepoints.c +++ b/kitty/savepoints.c @@ -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); -} - -// }}} diff --git a/kitty/screen.c b/kitty/screen.c index c7c9c5809..9bc5d7bfa 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -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); } }