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