1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 textbuf.cpp
6
7 Debugger text buffering engine.
8
9 ***************************************************************************/
10
11 #include "textbuf.h"
12
13 #include <new>
14
15
16
17 /***************************************************************************
18 CONSTANTS
19 ***************************************************************************/
20
21 #define MAX_LINE_LENGTH 250
22
23
24
25 /***************************************************************************
26 TYPE DEFINITIONS
27 ***************************************************************************/
28
29 struct text_buffer
30 {
text_buffertext_buffer31 text_buffer(u32 bytes, u32 lines) noexcept
32 : buffer(new (std::nothrow) char [bytes])
33 , lineoffs(new (std::nothrow) s32 [lines])
34 , bufsize(buffer ? bytes : 0)
35 , linesize(lineoffs ? lines : 0)
36 {
37 }
38
39 std::unique_ptr<char []> const buffer;
40 std::unique_ptr<s32 []> const lineoffs;
41 s32 const bufsize;
42 s32 bufstart = 0;
43 s32 bufend = 0;
44 s32 const linesize;
45 s32 linestart = 0;
46 s32 lineend = 0;
47 u32 linestartseq = 0;
48 s32 maxwidth = 0;
49
50 /*-------------------------------------------------
51 buffer_used - return the number of bytes
52 currently held in the buffer
53 -------------------------------------------------*/
54
buffer_usedtext_buffer55 s32 buffer_used() const noexcept
56 {
57 s32 const used(bufend - bufstart);
58 return (used < 0) ? (used + bufsize) : used;
59 }
60
61 /*-------------------------------------------------
62 buffer_space - return the number of bytes
63 available in the buffer
64 -------------------------------------------------*/
65
buffer_spacetext_buffer66 s32 buffer_space() const noexcept
67 {
68 return bufsize - buffer_used();
69 }
70 };
71
72
73
74 /***************************************************************************
75
76 Buffer object management
77
78 ***************************************************************************/
79
80 /*-------------------------------------------------
81 text_buffer_alloc - allocate a new text buffer
82 -------------------------------------------------*/
83
text_buffer_alloc(u32 bytes,u32 lines)84 text_buffer_ptr text_buffer_alloc(u32 bytes, u32 lines)
85 {
86 // allocate memory for the text buffer object
87 text_buffer_ptr text(new (std::nothrow) text_buffer(bytes, lines));
88
89 if (!text)
90 return nullptr;
91
92 if (!text->buffer || !text->lineoffs)
93 return nullptr;
94
95 // initialize the buffer description
96 text_buffer_clear(*text);
97
98 return text;
99 }
100
101
102 /*-------------------------------------------------
103 text_buffer_free - free a previously allocated
104 text buffer
105 -------------------------------------------------*/
106
operator ()(text_buffer * text) const107 void text_buffer_deleter::operator()(text_buffer *text) const
108 {
109 delete text;
110 }
111
112
113 /*-------------------------------------------------
114 text_buffer_clear - clear a text buffer
115 -------------------------------------------------*/
116
text_buffer_clear(text_buffer & text)117 void text_buffer_clear(text_buffer &text)
118 {
119 // reset all the buffer pointers and other bits
120 text.bufstart = 0;
121 text.bufend = 0;
122
123 text.linestart = 0;
124 text.lineend = 0;
125 text.linestartseq = 0;
126
127 text.maxwidth = 0;
128
129 // create the initial line
130 text.lineoffs[0] = 0;
131 text.buffer[text.lineoffs[0]] = 0;
132 }
133
134
135
136 /***************************************************************************
137
138 Adding data to the buffer
139
140 ***************************************************************************/
141
142 /*-------------------------------------------------
143 text_buffer_print - print data to the text
144 buffer
145 -------------------------------------------------*/
146
text_buffer_print(text_buffer & text,const char * data)147 void text_buffer_print(text_buffer &text, const char *data)
148 {
149 text_buffer_print_wrap(text, data, MAX_LINE_LENGTH);
150 }
151
152
153 /*-------------------------------------------------
154 text_buffer_print_wrap - print data to the
155 text buffer with word wrapping to a given
156 column
157 -------------------------------------------------*/
158
text_buffer_print_wrap(text_buffer & text,const char * data,int wrapcol)159 void text_buffer_print_wrap(text_buffer &text, const char *data, int wrapcol)
160 {
161 s32 const stopcol = (wrapcol < MAX_LINE_LENGTH) ? wrapcol : MAX_LINE_LENGTH;
162 s32 needed_space;
163
164 /* we need to ensure there is enough space for this string plus enough for the max line length */
165 needed_space = s32(strlen(data)) + MAX_LINE_LENGTH;
166
167 /* make space in the buffer if we need to */
168 while (text.buffer_space() < needed_space && text.linestart != text.lineend)
169 {
170 text.linestartseq++;
171 if (++text.linestart >= text.linesize)
172 text.linestart = 0;
173 text.bufstart = text.lineoffs[text.linestart];
174 }
175
176 /* now add the data */
177 for ( ; *data; data++)
178 {
179 int ch = *data;
180 int linelen;
181
182 /* a CR resets our position */
183 if (ch == '\r')
184 text.bufend = text.lineoffs[text.lineend];
185
186 /* non-CR data is just characters */
187 else if (ch != '\n')
188 text.buffer[text.bufend++] = ch;
189
190 /* an explicit newline or line-too-long condition inserts a newline */
191 linelen = text.bufend - text.lineoffs[text.lineend];
192 if (ch == '\n' || linelen >= stopcol)
193 {
194 int overflow = 0;
195
196 /* if we're wrapping, back off until we hit a space */
197 if (linelen >= wrapcol)
198 {
199 /* scan backwards, removing characters along the way */
200 overflow = 1;
201 while (overflow < linelen && text.buffer[text.bufend - overflow] != ' ')
202 overflow++;
203
204 /* if we found a space, take it; otherwise, reset and pretend we didn't try */
205 if (overflow < linelen)
206 linelen -= overflow;
207 else
208 overflow = 0;
209 }
210
211 /* did we beat the max width */
212 if (linelen > text.maxwidth)
213 text.maxwidth = linelen;
214
215 /* append a terminator */
216 if (overflow == 0)
217 text.buffer[text.bufend++] = 0;
218 else
219 text.buffer[text.bufend - overflow] = 0;
220
221 /* determine what the next line will be */
222 if (++text.lineend >= text.linesize)
223 text.lineend = 0;
224
225 /* if we're out of lines, consume the next one */
226 if (text.lineend == text.linestart)
227 {
228 text.linestartseq++;
229 if (++text.linestart >= text.linesize)
230 text.linestart = 0;
231 text.bufstart = text.lineoffs[text.linestart];
232 }
233
234 /* if we don't have enough room in the buffer for a max line, wrap to the start */
235 if (text.bufend + MAX_LINE_LENGTH + 1 >= text.bufsize)
236 text.bufend = 0;
237
238 /* create a new empty line */
239 text.lineoffs[text.lineend] = text.bufend - (overflow ? (overflow - 1) : 0);
240 }
241 }
242
243 /* nullptr terminate what we have on this line */
244 text.buffer[text.bufend] = 0;
245 }
246
247
248
249 /***************************************************************************
250
251 Reading data from the buffer
252
253 ***************************************************************************/
254
255 /*-------------------------------------------------
256 text_buffer_max_width - return the maximum
257 width of all lines seen so far
258 -------------------------------------------------*/
259
text_buffer_max_width(text_buffer const & text)260 u32 text_buffer_max_width(text_buffer const &text)
261 {
262 return text.maxwidth;
263 }
264
265
266 /*-------------------------------------------------
267 text_buffer_num_lines - return the number of
268 lines in the text buffer
269 -------------------------------------------------*/
270
text_buffer_num_lines(text_buffer const & text)271 u32 text_buffer_num_lines(text_buffer const &text)
272 {
273 s32 const lines = text.lineend + 1 - text.linestart;
274 return (lines <= 0) ? (lines + text.linesize) : lines;
275 }
276
277
278 /*-------------------------------------------------
279 text_buffer_line_index_to_seqnum - convert a
280 line index into a sequence number
281 -------------------------------------------------*/
282
text_buffer_line_index_to_seqnum(text_buffer const & text,u32 index)283 u32 text_buffer_line_index_to_seqnum(text_buffer const &text, u32 index)
284 {
285 return text.linestartseq + index;
286 }
287
288
289 /*-------------------------------------------------
290 text_buffer_get_seqnum_line - get a pointer to
291 an indexed line in the buffer
292 -------------------------------------------------*/
293
text_buffer_get_seqnum_line(text_buffer const & text,u32 seqnum)294 const char *text_buffer_get_seqnum_line(text_buffer const &text, u32 seqnum)
295 {
296 u32 const numlines = text_buffer_num_lines(text);
297 u32 const index = seqnum - text.linestartseq;
298 if (index >= numlines)
299 return nullptr;
300 return &text.buffer[text.lineoffs[(text.linestart + index) % text.linesize]];
301 }
302
303 /*---------------------------------------------------------------------
304 text_buffer_lines::text_buffer_line_iterator::operator*
305 Gets the line that the iterator currently points to.
306 -----------------------------------------------------------------------*/
307
operator *() const308 text_buffer_line text_buffer_lines::text_buffer_line_iterator::operator*() const
309 {
310 char const *const line = &m_buffer.buffer[m_buffer.lineoffs[m_lineptr]];
311
312 auto next_lineptr = m_lineptr + 1;
313 if (next_lineptr == m_buffer.linesize)
314 next_lineptr = 0;
315
316 char const *const nextline = &m_buffer.buffer[m_buffer.lineoffs[next_lineptr]];
317
318 // -1 for the '\0' at the end of line
319 ptrdiff_t difference = (nextline - line) - 1;
320
321 if (difference < 0)
322 difference += m_buffer.bufsize;
323
324 return text_buffer_line{ line, size_t(difference) };
325 }
326
327 /*---------------------------------------------------------------------
328 text_buffer_lines::text_buffer_line_iterator::operator++
329 Moves to the next line.
330 -----------------------------------------------------------------------*/
331
operator ++()332 text_buffer_lines::text_buffer_line_iterator &text_buffer_lines::text_buffer_line_iterator::operator++()
333 {
334 if (++m_lineptr == m_buffer.linesize)
335 m_lineptr = 0;
336
337 return *this;
338 }
339
340 /*------------------------------------------------------
341 text_buffer_lines::begin()
342 Returns an iterator that points to the first line.
343 --------------------------------------------------------*/
344
begin() const345 text_buffer_lines::iterator text_buffer_lines::begin() const
346 {
347 return text_buffer_line_iterator(m_buffer, m_buffer.linestart);
348 }
349
350 /*-----------------------------------------------------------
351 text_buffer_lines::begin()
352 Returns an iterator that points just past the last line.
353 -------------------------------------------------------------*/
354
end() const355 text_buffer_lines::iterator text_buffer_lines::end() const
356 {
357 return text_buffer_line_iterator(m_buffer, m_buffer.lineend);
358 }
359