1 /*
2  * history.c
3  * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>
4  *
5  * Distributed under terms of the GPL3 license.
6  */
7 
8 #include "wcswidth.h"
9 #include "lineops.h"
10 #include "charsets.h"
11 #include <structmember.h>
12 #include "ringbuf.h"
13 
14 extern PyTypeObject Line_Type;
15 #define SEGMENT_SIZE 2048
16 
17 static void
add_segment(HistoryBuf * self)18 add_segment(HistoryBuf *self) {
19     self->num_segments += 1;
20     self->segments = realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments);
21     if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment");
22     HistoryBufSegment *s = self->segments + self->num_segments - 1;
23     const size_t cpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(CPUCell);
24     const size_t gpu_cells_size = self->xnum * SEGMENT_SIZE * sizeof(GPUCell);
25     s->cpu_cells = calloc(1, cpu_cells_size + gpu_cells_size + SEGMENT_SIZE * sizeof(line_attrs_type));
26     if (!s->cpu_cells) fatal("Out of memory allocating new history buffer segment");
27     s->gpu_cells = (GPUCell*)(((uint8_t*)s->cpu_cells) + cpu_cells_size);
28     s->line_attrs = (line_attrs_type*)(((uint8_t*)s->gpu_cells) + gpu_cells_size);
29 }
30 
31 static index_type
segment_for(HistoryBuf * self,index_type y)32 segment_for(HistoryBuf *self, index_type y) {
33     index_type seg_num = y / SEGMENT_SIZE;
34     while (UNLIKELY(seg_num >= self->num_segments && SEGMENT_SIZE * self->num_segments < self->ynum)) add_segment(self);
35     if (UNLIKELY(seg_num >= self->num_segments)) fatal("Out of bounds access to history buffer line number: %u", y);
36     return seg_num;
37 }
38 
39 #define seg_ptr(which, stride) { \
40     index_type seg_num = segment_for(self, y); \
41     y -= seg_num * SEGMENT_SIZE; \
42     return self->segments[seg_num].which + y * stride; \
43 }
44 
45 static CPUCell*
cpu_lineptr(HistoryBuf * self,index_type y)46 cpu_lineptr(HistoryBuf *self, index_type y) {
47     seg_ptr(cpu_cells, self->xnum);
48 }
49 
50 static GPUCell*
gpu_lineptr(HistoryBuf * self,index_type y)51 gpu_lineptr(HistoryBuf *self, index_type y) {
52     seg_ptr(gpu_cells, self->xnum);
53 }
54 
55 
56 static line_attrs_type*
attrptr(HistoryBuf * self,index_type y)57 attrptr(HistoryBuf *self, index_type y) {
58     seg_ptr(line_attrs, 1);
59 }
60 
61 static PagerHistoryBuf*
alloc_pagerhist(size_t pagerhist_sz)62 alloc_pagerhist(size_t pagerhist_sz) {
63     PagerHistoryBuf *ph;
64     if (!pagerhist_sz) return NULL;
65     ph = calloc(1, sizeof(PagerHistoryBuf));
66     if (!ph) return NULL;
67     size_t sz = MIN(1024u * 1024u, pagerhist_sz);
68     ph->ringbuf = ringbuf_new(sz);
69     if (!ph->ringbuf) { free(ph); return NULL; }
70     ph->maximum_size = pagerhist_sz;
71     return ph;
72 }
73 
74 static void
free_pagerhist(HistoryBuf * self)75 free_pagerhist(HistoryBuf *self) {
76     if (self->pagerhist && self->pagerhist->ringbuf) ringbuf_free((ringbuf_t*)&self->pagerhist->ringbuf);
77     free(self->pagerhist);
78     self->pagerhist = NULL;
79 }
80 
81 static bool
pagerhist_extend(PagerHistoryBuf * ph,size_t minsz)82 pagerhist_extend(PagerHistoryBuf *ph, size_t minsz) {
83     size_t buffer_size = ringbuf_capacity(ph->ringbuf);
84     if (buffer_size >= ph->maximum_size) return false;
85     size_t newsz = MIN(ph->maximum_size, buffer_size + MAX(1024u * 1024u, minsz));
86     ringbuf_t newbuf = ringbuf_new(newsz);
87     if (!newbuf) return false;
88     size_t count = ringbuf_bytes_used(ph->ringbuf);
89     if (count) ringbuf_copy(newbuf, ph->ringbuf, count);
90     ringbuf_free((ringbuf_t*)&ph->ringbuf);
91     ph->ringbuf = newbuf;
92     return true;
93 }
94 
95 static void
pagerhist_clear(HistoryBuf * self)96 pagerhist_clear(HistoryBuf *self) {
97     if (self->pagerhist && self->pagerhist->ringbuf) ringbuf_reset(self->pagerhist->ringbuf);
98 }
99 
100 static HistoryBuf*
create_historybuf(PyTypeObject * type,unsigned int xnum,unsigned int ynum,unsigned int pagerhist_sz)101 create_historybuf(PyTypeObject *type, unsigned int xnum, unsigned int ynum, unsigned int pagerhist_sz) {
102     if (xnum == 0 || ynum == 0) {
103         PyErr_SetString(PyExc_ValueError, "Cannot create an empty history buffer");
104         return NULL;
105     }
106     HistoryBuf *self = (HistoryBuf *)type->tp_alloc(type, 0);
107     if (self != NULL) {
108         self->xnum = xnum;
109         self->ynum = ynum;
110         self->num_segments = 0;
111         add_segment(self);
112         self->line = alloc_line();
113         self->line->xnum = xnum;
114         self->pagerhist = alloc_pagerhist(pagerhist_sz);
115     }
116     return self;
117 }
118 
119 static PyObject *
new(PyTypeObject * type,PyObject * args,PyObject UNUSED * kwds)120 new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
121     unsigned int xnum = 1, ynum = 1, pagerhist_sz = 0;
122     if (!PyArg_ParseTuple(args, "II|I", &ynum, &xnum, &pagerhist_sz)) return NULL;
123     HistoryBuf *ans = create_historybuf(type, xnum, ynum, pagerhist_sz);
124     return (PyObject*)ans;
125 }
126 
127 static void
dealloc(HistoryBuf * self)128 dealloc(HistoryBuf* self) {
129     Py_CLEAR(self->line);
130     for (size_t i = 0; i < self->num_segments; i++) free(self->segments[i].cpu_cells);
131     free(self->segments);
132     free_pagerhist(self);
133     Py_TYPE(self)->tp_free((PyObject*)self);
134 }
135 
136 static index_type
index_of(HistoryBuf * self,index_type lnum)137 index_of(HistoryBuf *self, index_type lnum) {
138     // The index (buffer position) of the line with line number lnum
139     // This is reverse indexing, i.e. lnum = 0 corresponds to the *last* line in the buffer.
140     if (self->count == 0) return 0;
141     index_type idx = self->count - 1 - MIN(self->count - 1, lnum);
142     return (self->start_of_data + idx) % self->ynum;
143 }
144 
145 static void
init_line(HistoryBuf * self,index_type num,Line * l)146 init_line(HistoryBuf *self, index_type num, Line *l) {
147     // Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num
148     l->cpu_cells = cpu_lineptr(self, num);
149     l->gpu_cells = gpu_lineptr(self, num);
150     l->continued = *attrptr(self, num) & CONTINUED_MASK;
151     l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false;
152 }
153 
154 void
historybuf_init_line(HistoryBuf * self,index_type lnum,Line * l)155 historybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) {
156     init_line(self, index_of(self, lnum), l);
157 }
158 
159 CPUCell*
historybuf_cpu_cells(HistoryBuf * self,index_type lnum)160 historybuf_cpu_cells(HistoryBuf *self, index_type lnum) {
161     return cpu_lineptr(self, index_of(self, lnum));
162 }
163 
164 void
historybuf_mark_line_clean(HistoryBuf * self,index_type y)165 historybuf_mark_line_clean(HistoryBuf *self, index_type y) {
166     line_attrs_type *p = attrptr(self, index_of(self, y));
167     *p &= ~TEXT_DIRTY_MASK;
168 }
169 
170 void
historybuf_mark_line_dirty(HistoryBuf * self,index_type y)171 historybuf_mark_line_dirty(HistoryBuf *self, index_type y) {
172     line_attrs_type *p = attrptr(self, index_of(self, y));
173     *p |= TEXT_DIRTY_MASK;
174 }
175 
176 void
historybuf_clear(HistoryBuf * self)177 historybuf_clear(HistoryBuf *self) {
178     pagerhist_clear(self);
179     self->count = 0;
180     self->start_of_data = 0;
181 }
182 
183 static bool
pagerhist_write_bytes(PagerHistoryBuf * ph,const uint8_t * buf,size_t sz)184 pagerhist_write_bytes(PagerHistoryBuf *ph, const uint8_t *buf, size_t sz) {
185     if (sz > ph->maximum_size) return false;
186     if (!sz) return true;
187     size_t space_in_ringbuf = ringbuf_bytes_free(ph->ringbuf);
188     if (sz > space_in_ringbuf) pagerhist_extend(ph, sz);
189     ringbuf_memcpy_into(ph->ringbuf, buf, sz);
190     return true;
191 }
192 
193 static bool
pagerhist_ensure_start_is_valid_utf8(PagerHistoryBuf * ph)194 pagerhist_ensure_start_is_valid_utf8(PagerHistoryBuf *ph) {
195     uint8_t scratch[8];
196     size_t num = ringbuf_memcpy_from(scratch, ph->ringbuf, arraysz(scratch));
197     uint32_t codep;
198     UTF8State state = UTF8_ACCEPT;
199     size_t count = 0;
200     size_t last_reject_at = 0;
201     while (count < num) {
202         decode_utf8(&state, &codep, scratch[count++]);
203         if (state == UTF8_ACCEPT) break;
204         if (state == UTF8_REJECT) { state = UTF8_ACCEPT; last_reject_at = count; }
205     }
206     if (last_reject_at) {
207         ringbuf_memmove_from(scratch, ph->ringbuf, last_reject_at);
208         return true;
209     }
210     return false;
211 }
212 
213 static bool
pagerhist_write_ucs4(PagerHistoryBuf * ph,const Py_UCS4 * buf,size_t sz)214 pagerhist_write_ucs4(PagerHistoryBuf *ph, const Py_UCS4 *buf, size_t sz) {
215     uint8_t scratch[4];
216     for (size_t i = 0; i < sz; i++) {
217         unsigned int num = encode_utf8(buf[i], (char*)scratch);
218         if (!pagerhist_write_bytes(ph, scratch, num)) return false;
219     }
220     return true;
221 }
222 
223 static void
pagerhist_push(HistoryBuf * self,ANSIBuf * as_ansi_buf)224 pagerhist_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) {
225     PagerHistoryBuf *ph = self->pagerhist;
226     if (!ph) return;
227     const GPUCell *prev_cell = NULL;
228     Line l = {.xnum=self->xnum};
229     init_line(self, self->start_of_data, &l);
230     line_as_ansi(&l, as_ansi_buf, &prev_cell);
231     if (ringbuf_bytes_used(ph->ringbuf) && !l.continued) pagerhist_write_bytes(ph, (const uint8_t*)"\n", 1);
232     pagerhist_write_bytes(ph, (const uint8_t*)"\x1b[m", 3);
233     if (pagerhist_write_ucs4(ph, as_ansi_buf->buf, as_ansi_buf->len)) pagerhist_write_bytes(ph, (const uint8_t*)"\r", 1);
234 }
235 
236 static index_type
historybuf_push(HistoryBuf * self,ANSIBuf * as_ansi_buf)237 historybuf_push(HistoryBuf *self, ANSIBuf *as_ansi_buf) {
238     index_type idx = (self->start_of_data + self->count) % self->ynum;
239     init_line(self, idx, self->line);
240     if (self->count == self->ynum) {
241         pagerhist_push(self, as_ansi_buf);
242         self->start_of_data = (self->start_of_data + 1) % self->ynum;
243     } else self->count++;
244     return idx;
245 }
246 
247 void
historybuf_add_line(HistoryBuf * self,const Line * line,ANSIBuf * as_ansi_buf)248 historybuf_add_line(HistoryBuf *self, const Line *line, ANSIBuf *as_ansi_buf) {
249     index_type idx = historybuf_push(self, as_ansi_buf);
250     copy_line(line, self->line);
251     *attrptr(self, idx) = (line->continued & CONTINUED_MASK) | (line->has_dirty_text ? TEXT_DIRTY_MASK : 0);
252 }
253 
254 bool
historybuf_pop_line(HistoryBuf * self,Line * line)255 historybuf_pop_line(HistoryBuf *self, Line *line) {
256     if (self->count <= 0) return false;
257     index_type idx = (self->start_of_data + self->count - 1) % self->ynum;
258     init_line(self, idx, line);
259     self->count--;
260     return true;
261 }
262 
263 static PyObject*
line(HistoryBuf * self,PyObject * val)264 line(HistoryBuf *self, PyObject *val) {
265 #define line_doc "Return the line with line number val. This buffer grows upwards, i.e. 0 is the most recently added line"
266     if (self->count == 0) { PyErr_SetString(PyExc_IndexError, "This buffer is empty"); return NULL; }
267     index_type lnum = PyLong_AsUnsignedLong(val);
268     if (lnum >= self->count) { PyErr_SetString(PyExc_IndexError, "Out of bounds"); return NULL; }
269     init_line(self, index_of(self, lnum), self->line);
270     Py_INCREF(self->line);
271     return (PyObject*)self->line;
272 }
273 
274 static PyObject*
__str__(HistoryBuf * self)275 __str__(HistoryBuf *self) {
276     PyObject *lines = PyTuple_New(self->count);
277     if (lines == NULL) return PyErr_NoMemory();
278     for (index_type i = 0; i < self->count; i++) {
279         init_line(self, index_of(self, i), self->line);
280         PyObject *t = line_as_unicode(self->line, false);
281         if (t == NULL) { Py_CLEAR(lines); return NULL; }
282         PyTuple_SET_ITEM(lines, i, t);
283     }
284     PyObject *sep = PyUnicode_FromString("\n");
285     PyObject *ans = PyUnicode_Join(sep, lines);
286     Py_CLEAR(lines); Py_CLEAR(sep);
287     return ans;
288 }
289 
290 static PyObject*
push(HistoryBuf * self,PyObject * args)291 push(HistoryBuf *self, PyObject *args) {
292 #define push_doc "Push a line into this buffer, removing the oldest line, if necessary"
293     Line *line;
294     if (!PyArg_ParseTuple(args, "O!", &Line_Type, &line)) return NULL;
295     ANSIBuf as_ansi_buf = {0};
296     historybuf_add_line(self, line, &as_ansi_buf);
297     free(as_ansi_buf.buf);
298     Py_RETURN_NONE;
299 }
300 
301 static PyObject*
as_ansi(HistoryBuf * self,PyObject * callback)302 as_ansi(HistoryBuf *self, PyObject *callback) {
303 #define as_ansi_doc "as_ansi(callback) -> The contents of this buffer as ANSI escaped text. callback is called with each successive line."
304     Line l = {.xnum=self->xnum};
305     const GPUCell *prev_cell = NULL;
306     ANSIBuf output = {0};
307     for(unsigned int i = 0; i < self->count; i++) {
308         init_line(self, i, &l);
309         if (i < self->count - 1) {
310             l.continued = *attrptr(self, index_of(self, i + 1)) & CONTINUED_MASK;
311         } else l.continued = false;
312         line_as_ansi(&l, &output, &prev_cell);
313         if (!l.continued) {
314             ensure_space_for(&output, buf, Py_UCS4, output.len + 1, capacity, 2048, false);
315             output.buf[output.len++] = 10; // 10 = \n
316         }
317         PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, output.buf, output.len);
318         if (ans == NULL) { PyErr_NoMemory(); goto end; }
319         PyObject *ret = PyObject_CallFunctionObjArgs(callback, ans, NULL);
320         Py_CLEAR(ans);
321         if (ret == NULL) goto end;
322         Py_CLEAR(ret);
323     }
324 end:
325     free(output.buf);
326     if (PyErr_Occurred()) return NULL;
327     Py_RETURN_NONE;
328 }
329 
330 static Line*
get_line(HistoryBuf * self,index_type y,Line * l)331 get_line(HistoryBuf *self, index_type y, Line *l) { init_line(self, index_of(self, self->count - y - 1), l); return l; }
332 
333 static char_type
pagerhist_remove_char(PagerHistoryBuf * ph,unsigned * count,uint8_t record[8])334 pagerhist_remove_char(PagerHistoryBuf *ph, unsigned *count, uint8_t record[8]) {
335     uint32_t codep; UTF8State state = UTF8_ACCEPT;
336     *count = 0;
337     size_t num = ringbuf_bytes_used(ph->ringbuf);
338     while (num--) {
339         record[*count] = ringbuf_move_char(ph->ringbuf);
340         decode_utf8(&state, &codep, record[*count]);
341         *count += 1;
342         if (state == UTF8_REJECT) { codep = 0; break; }
343         if (state == UTF8_ACCEPT) break;
344     }
345     return codep;
346 }
347 
348 static void
pagerhist_rewrap_to(HistoryBuf * self,index_type cells_in_line)349 pagerhist_rewrap_to(HistoryBuf *self, index_type cells_in_line) {
350     PagerHistoryBuf *ph = self->pagerhist;
351     if (!ph->ringbuf || !ringbuf_bytes_used(ph->ringbuf)) return;
352     PagerHistoryBuf *nph = calloc(sizeof(PagerHistoryBuf), 1);
353     if (!nph) return;
354     nph->maximum_size = ph->maximum_size;
355     nph->ringbuf = ringbuf_new(MIN(ph->maximum_size, ringbuf_capacity(ph->ringbuf) + 4096));
356     if (!nph->ringbuf) { free(nph); return ; }
357     ssize_t ch_width = 0;
358     unsigned count;
359     uint8_t record[8];
360     index_type num_in_current_line = 0;
361     char_type ch;
362     WCSState wcs_state;
363     initialize_wcs_state(&wcs_state);
364 
365 #define WRITE_CHAR() { \
366     if (num_in_current_line + ch_width > cells_in_line) { \
367         pagerhist_write_bytes(nph, (const uint8_t*)"\r", 1); \
368         num_in_current_line = 0; \
369     }\
370     if (ch_width >= 0 || (int)num_in_current_line >= -ch_width) num_in_current_line += ch_width; \
371     pagerhist_write_bytes(nph, record, count); \
372 }
373 
374     while (ringbuf_bytes_used(ph->ringbuf)) {
375         ch = pagerhist_remove_char(ph, &count, record);
376         if (ch == '\n') {
377             initialize_wcs_state(&wcs_state);
378             ch_width = 1;
379             WRITE_CHAR();
380             num_in_current_line = 0;
381         } else if (ch != '\r') {
382             ch_width = wcswidth_step(&wcs_state, ch);
383             WRITE_CHAR();
384         }
385     }
386     free_pagerhist(self);
387     self->pagerhist = nph;
388 #undef WRITE_CHAR
389 }
390 
391 static PyObject*
pagerhist_write(HistoryBuf * self,PyObject * what)392 pagerhist_write(HistoryBuf *self, PyObject *what) {
393     if (self->pagerhist && self->pagerhist->maximum_size) {
394         if (PyBytes_Check(what)) pagerhist_write_bytes(self->pagerhist, (const uint8_t*)PyBytes_AS_STRING(what), PyBytes_GET_SIZE(what));
395         else if (PyUnicode_Check(what) && PyUnicode_READY(what) == 0) {
396             Py_UCS4 *buf = PyUnicode_AsUCS4Copy(what);
397             if (buf) {
398                 pagerhist_write_ucs4(self->pagerhist, buf, PyUnicode_GET_LENGTH(what));
399                 PyMem_Free(buf);
400             }
401         }
402     }
403     Py_RETURN_NONE;
404 }
405 
406 static PyObject*
pagerhist_as_bytes(HistoryBuf * self,PyObject * args UNUSED)407 pagerhist_as_bytes(HistoryBuf *self, PyObject *args UNUSED) {
408 #define ph self->pagerhist
409     if (!ph || !ringbuf_bytes_used(ph->ringbuf)) return PyBytes_FromStringAndSize("", 0);
410     pagerhist_ensure_start_is_valid_utf8(ph);
411     if (ph->rewrap_needed) pagerhist_rewrap_to(self, self->xnum);
412 
413     Line l = {.xnum=self->xnum}; get_line(self, 0, &l);
414     size_t sz = ringbuf_bytes_used(ph->ringbuf);
415     if (!l.continued) sz += 1;
416     PyObject *ans = PyBytes_FromStringAndSize(NULL, sz);
417     if (!ans) return NULL;
418     uint8_t *buf = (uint8_t*)PyBytes_AS_STRING(ans);
419     ringbuf_memcpy_from(buf, ph->ringbuf, sz);
420     if (!l.continued) buf[sz-1] = '\n';
421     return ans;
422 #undef ph
423 }
424 
425 static PyObject *
pagerhist_as_text(HistoryBuf * self,PyObject * args)426 pagerhist_as_text(HistoryBuf *self, PyObject *args) {
427     PyObject *ans = NULL;
428     PyObject *bytes = pagerhist_as_bytes(self, args);
429     if (bytes) {
430         ans = PyUnicode_DecodeUTF8(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes), "ignore");
431         Py_DECREF(bytes);
432     }
433     return ans;
434 }
435 
436 typedef struct {
437     Line line;
438     HistoryBuf *self;
439 } GetLineWrapper;
440 
441 static Line*
get_line_wrapper(void * x,int y)442 get_line_wrapper(void *x, int y) {
443     GetLineWrapper *glw = x;
444     get_line(glw->self, y, &glw->line);
445     return &glw->line;
446 }
447 
448 static PyObject*
as_text(HistoryBuf * self,PyObject * args)449 as_text(HistoryBuf *self, PyObject *args) {
450     GetLineWrapper glw = {.self=self};
451     glw.line.xnum = self->xnum;
452     ANSIBuf output = {0};
453     PyObject *ans = as_text_generic(args, &glw, get_line_wrapper, self->count, &output);
454     free(output.buf);
455     return ans;
456 }
457 
458 
459 static PyObject*
dirty_lines(HistoryBuf * self,PyObject * a UNUSED)460 dirty_lines(HistoryBuf *self, PyObject *a UNUSED) {
461 #define dirty_lines_doc "dirty_lines() -> Line numbers of all lines that have dirty text."
462     PyObject *ans = PyList_New(0);
463     for (index_type i = 0; i < self->count; i++) {
464         if (*attrptr(self, i) & TEXT_DIRTY_MASK) {
465             PyList_Append(ans, PyLong_FromUnsignedLong(i));
466         }
467     }
468     return ans;
469 }
470 
471 static PyObject*
pagerhist_rewrap(HistoryBuf * self,PyObject * xnum)472 pagerhist_rewrap(HistoryBuf *self, PyObject *xnum) {
473     if (self->pagerhist) {
474         pagerhist_rewrap_to(self, PyLong_AsUnsignedLong(xnum));
475     }
476     Py_RETURN_NONE;
477 }
478 
479 
480 // Boilerplate {{{
481 static PyObject* rewrap(HistoryBuf *self, PyObject *args);
482 #define rewrap_doc ""
483 
484 static PyMethodDef methods[] = {
485     METHOD(line, METH_O)
486     METHOD(as_ansi, METH_O)
487     METHODB(pagerhist_write, METH_O),
488     METHODB(pagerhist_rewrap, METH_O),
489     METHODB(pagerhist_as_text, METH_NOARGS),
490     METHODB(pagerhist_as_bytes, METH_NOARGS),
491     METHODB(as_text, METH_VARARGS),
492     METHOD(dirty_lines, METH_NOARGS)
493     METHOD(push, METH_VARARGS)
494     METHOD(rewrap, METH_VARARGS)
495     {NULL, NULL, 0, NULL}  /* Sentinel */
496 };
497 
498 static PyMemberDef members[] = {
499     {"xnum", T_UINT, offsetof(HistoryBuf, xnum), READONLY, "xnum"},
500     {"ynum", T_UINT, offsetof(HistoryBuf, ynum), READONLY, "ynum"},
501     {"count", T_UINT, offsetof(HistoryBuf, count), READONLY, "count"},
502     {NULL}  /* Sentinel */
503 };
504 
505 PyTypeObject HistoryBuf_Type = {
506     PyVarObject_HEAD_INIT(NULL, 0)
507     .tp_name = "fast_data_types.HistoryBuf",
508     .tp_basicsize = sizeof(HistoryBuf),
509     .tp_dealloc = (destructor)dealloc,
510     .tp_flags = Py_TPFLAGS_DEFAULT,
511     .tp_doc = "History buffers",
512     .tp_methods = methods,
513     .tp_members = members,
514     .tp_str = (reprfunc)__str__,
515     .tp_new = new
516 };
517 
INIT_TYPE(HistoryBuf)518 INIT_TYPE(HistoryBuf)
519 
520 HistoryBuf *alloc_historybuf(unsigned int lines, unsigned int columns, unsigned int pagerhist_sz) {
521     return create_historybuf(&HistoryBuf_Type, columns, lines, pagerhist_sz);
522 }
523 // }}}
524 
525 #define BufType HistoryBuf
526 
527 #define map_src_index(y) ((src->start_of_data + y) % src->ynum)
528 
529 #define init_src_line(src_y) init_line(src, map_src_index(src_y), src->line);
530 
531 #define is_src_line_continued(src_y) (map_src_index(src_y) < src->ynum - 1 ? (*attrptr(src, map_src_index(src_y + 1)) & CONTINUED_MASK) : false)
532 
533 #define next_dest_line(cont) *attrptr(dest, historybuf_push(dest, as_ansi_buf)) = cont & CONTINUED_MASK; dest->line->continued = cont;
534 
535 #define first_dest_line next_dest_line(false);
536 
537 #include "rewrap.h"
538 
539 void
historybuf_rewrap(HistoryBuf * self,HistoryBuf * other,ANSIBuf * as_ansi_buf)540 historybuf_rewrap(HistoryBuf *self, HistoryBuf *other, ANSIBuf *as_ansi_buf) {
541     while(other->num_segments < self->num_segments) add_segment(other);
542     if (other->xnum == self->xnum && other->ynum == self->ynum) {
543         // Fast path
544         for (index_type i = 0; i < self->num_segments; i++) {
545             memcpy(other->segments[i].cpu_cells, self->segments[i].cpu_cells, SEGMENT_SIZE * self->xnum * sizeof(CPUCell));
546             memcpy(other->segments[i].gpu_cells, self->segments[i].gpu_cells, SEGMENT_SIZE * self->xnum * sizeof(GPUCell));
547             memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(line_attrs_type));
548         }
549         other->count = self->count; other->start_of_data = self->start_of_data;
550         return;
551     }
552     if (other->pagerhist && other->xnum != self->xnum && ringbuf_bytes_used(other->pagerhist->ringbuf))
553         other->pagerhist->rewrap_needed = true;
554     other->count = 0; other->start_of_data = 0;
555     if (self->count > 0) {
556         rewrap_inner(self, other, self->count, NULL, NULL, as_ansi_buf);
557         for (index_type i = 0; i < other->count; i++) *attrptr(other, (other->start_of_data + i) % other->ynum) |= TEXT_DIRTY_MASK;
558     }
559 }
560 
561 static PyObject*
rewrap(HistoryBuf * self,PyObject * args)562 rewrap(HistoryBuf *self, PyObject *args) {
563     HistoryBuf *other;
564     if (!PyArg_ParseTuple(args, "O!", &HistoryBuf_Type, &other)) return NULL;
565     ANSIBuf as_ansi_buf = {0};
566     historybuf_rewrap(self, other, &as_ansi_buf);
567     free(as_ansi_buf.buf);
568     Py_RETURN_NONE;
569 }
570