1 /*-------------------------------------------------------------------------
2  *
3  * linebuffer.c
4  *	  a routines for iteration over stored lines
5  *
6  * Portions Copyright (c) 2017-2021 Pavel Stehule
7  *
8  * IDENTIFICATION
9  *	  src/linebuffer.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 
14 #include "pspg.h"
15 
16 #include <stdlib.h>
17 
18 /*
19  * Initialize line buffer iterator
20  */
21 inline void
init_lbi(LineBufferIter * lbi,LineBuffer * lb,MappedLine * order_map,int order_map_items,int init_pos)22 init_lbi(LineBufferIter *lbi,
23 		 LineBuffer *lb,
24 		 MappedLine *order_map,
25 		 int order_map_items,
26 		 int init_pos)
27 {
28 	lbi->start_lb = lb;
29 
30 	lbi->order_map = order_map;
31 	lbi->order_map_items = order_map_items;
32 
33 	lbi_set_lineno(lbi, init_pos);
34 }
35 
36 /*
37  * Common case - initialize line buffer iterator
38  * for stored data.
39  */
40 inline void
init_lbi_ddesc(LineBufferIter * lbi,DataDesc * desc,int init_pos)41 init_lbi_ddesc(LineBufferIter *lbi,
42 				  DataDesc *desc,
43 				  int init_pos)
44 {
45 	init_lbi(lbi,
46 			 &desc->rows,
47 			 desc->order_map,
48 			 desc->order_map_items,
49 			 init_pos);
50 }
51 
52 /*
53  * Set iterator to absolute position in line buffer
54  */
55 bool
lbi_set_lineno(LineBufferIter * lbi,int pos)56 lbi_set_lineno(LineBufferIter *lbi, int pos)
57 {
58 	lbi->lineno = pos;
59 
60 	if (lbi->order_map)
61 	{
62 		if (lbi->order_map_items > pos)
63 		{
64 			MappedLine *mpl = &lbi->order_map[pos];
65 
66 			lbi->current_lb = mpl->lnb;
67 			lbi->current_lb_rowno = mpl->lnb_row;
68 
69 			return true;
70 		}
71 
72 		/* set max lineno */
73 		lbi->lineno = lbi->order_map_items;
74 	}
75 	else
76 	{
77 		int		lineno_offset = 0;
78 
79 		lbi->current_lb = lbi->start_lb;
80 
81 		while (lbi->current_lb && pos >= LINEBUFFER_LINES)
82 		{
83 			pos -= LINEBUFFER_LINES;
84 			lineno_offset += lbi->current_lb->nrows;
85 
86 			lbi->current_lb = lbi->current_lb->next;
87 		}
88 
89 		if (lbi->current_lb)
90 		{
91 			if (pos < lbi->current_lb->nrows)
92 			{
93 				lbi->current_lb_rowno = pos;
94 
95 				return true;
96 			}
97 			else
98 				lbi->lineno = lineno_offset + lbi->current_lb->nrows;
99 		}
100 		else
101 			lbi->lineno = lineno_offset;
102 	}
103 
104 	lbi->current_lb = NULL;
105 	lbi->current_lb_rowno = 0;
106 
107 	return false;
108 }
109 
110 /*
111  * Initialize line buffer mark to current position in
112  * line buffer.
113  */
114 inline void
lbi_set_mark(LineBufferIter * lbi,LineBufferMark * lbm)115 lbi_set_mark(LineBufferIter *lbi, LineBufferMark *lbm)
116 {
117 	lbm->lb = lbi->current_lb;
118 	lbm->lb_rowno = lbi->current_lb_rowno;
119 	lbm->lineno = lbi->lineno;
120 }
121 
122 /*
123  * Initialize line buffer mark to current position in line
124  * buffer. Increase current position in line buffer. Returns
125  * true if line buffer mark is valid.
126  */
127 bool
lbi_set_mark_next(LineBufferIter * lbi,LineBufferMark * lbm)128 lbi_set_mark_next(LineBufferIter *lbi, LineBufferMark *lbm)
129 {
130 	lbi_set_mark(lbi, lbm);
131 	(void) lbi_next(lbi);
132 
133 	return lbm->lb && lbm->lb_rowno < lbm->lb->nrows;
134 }
135 
136 /*
137  * Sets mark to line buffer specified by position. When false,
138  * when position is not valid.
139  */
140 bool
ddesc_set_mark(LineBufferMark * lbm,DataDesc * desc,int pos)141 ddesc_set_mark(LineBufferMark *lbm, DataDesc *desc, int pos)
142 {
143 	lbm->lb = NULL;
144 	lbm->lineno = pos;
145 
146 	if (desc->order_map)
147 	{
148 		if (pos >= 0 && pos < desc->order_map_items)
149 		{
150 			lbm->lb = desc->order_map[pos].lnb;
151 			lbm->lb_rowno = desc->order_map[pos].lnb_row;
152 			lbm->lineno = pos;
153 
154 			return true;
155 		}
156 	}
157 	else
158 	{
159 		LineBuffer *lb = &desc->rows;
160 
161 		while (lb && pos >= LINEBUFFER_LINES)
162 		{
163 			lb = lb->next;
164 			pos -= LINEBUFFER_LINES;
165 		}
166 
167 		if (lb && pos < lb->nrows)
168 		{
169 			lbm->lb = lb;
170 			lbm->lb_rowno = pos;
171 
172 			return true;
173 		}
174 	}
175 
176 	return false;
177 }
178 
179 void
lbm_xor_mask(LineBufferMark * lbm,char mask)180 lbm_xor_mask(LineBufferMark *lbm, char mask)
181 {
182 	if (!lbm->lb->lineinfo)
183 	{
184 		/* smalloc returns zero fill memory already */
185 		lbm->lb->lineinfo = smalloc(LINEBUFFER_LINES * sizeof(LineInfo));
186 	}
187 
188 	lbm->lb->lineinfo[lbm->lb_rowno].mask ^= mask;
189 }
190 
191 /*
192  * Working horse of lbm_get_line and lbi_get_line routines
193  */
194 static bool
lb_get_line(LineBuffer * lb,int rowno,int lineno,char ** line,LineInfo ** linfo,int * linenoptr)195 lb_get_line(LineBuffer *lb,
196 			int rowno,
197 			int lineno,
198 			char **line,
199 			LineInfo **linfo,
200 			int	*linenoptr)
201 {
202 	if (linenoptr)
203 		*linenoptr = lineno;
204 
205 	if (lb && rowno >= 0 && rowno < lb->nrows)
206 	{
207 		if (line)
208 			*line = lb->rows[rowno];
209 
210 		if (linfo)
211 			*linfo = lb->lineinfo ? &lb->lineinfo[rowno] : NULL;
212 
213 		return true;
214 	}
215 
216 	if (line)
217 		*line = NULL;
218 
219 	if (linfo)
220 		*linfo = NULL;
221 
222 	return false;
223 }
224 
225 /*
226  * Returns line related to line buffer mark
227  */
228 bool
lbm_get_line(LineBufferMark * lbm,char ** line,LineInfo ** linfo,int * lineno)229 lbm_get_line(LineBufferMark *lbm,
230 			 char **line,
231 			 LineInfo **linfo,
232 			 int *lineno)
233 {
234 	return lb_get_line(lbm->lb,
235 					   lbm->lb_rowno,
236 					   lbm->lineno,
237 					   line,
238 					   linfo,
239 					   lineno);
240 }
241 
242 /*
243  * Returns true, when returns valid line from line buffer.
244  */
245 inline bool
lbi_get_line(LineBufferIter * lbi,char ** line,LineInfo ** linfo,int * lineno)246 lbi_get_line(LineBufferIter *lbi,
247 			  char **line,
248 			  LineInfo **linfo,
249 			  int *lineno)
250 {
251 	return lb_get_line(lbi->current_lb,
252 					   lbi->current_lb_rowno,
253 					   lbi->lineno,
254 					   line,
255 					   linfo,
256 					   lineno);
257 }
258 
259 /*
260  * Returns true, when returns valid line from line buffer.
261  * Increments position in linebuffer.
262  */
263 inline bool
lbi_get_line_next(LineBufferIter * lbi,char ** line,LineInfo ** linfo,int * lineno)264 lbi_get_line_next(LineBufferIter *lbi,
265 				  char **line,
266 				  LineInfo **linfo,
267 				  int *lineno)
268 {
269 	bool result;
270 
271 	result = lbi_get_line(lbi, line, linfo, lineno);
272 
273 	(void) lbi_next(lbi);
274 
275 	return result;
276 }
277 
278 
279 /*
280  * Returns true, when returns valid line from line buffer.
281  * Decreases position in linebuffer.
282  */
283 inline bool
lbi_get_line_prev(LineBufferIter * lbi,char ** line,LineInfo ** linfo,int * lineno)284 lbi_get_line_prev(LineBufferIter *lbi,
285 				  char **line,
286 				  LineInfo **linfo,
287 				  int *lineno)
288 {
289 	bool result;
290 
291 	result = lbi_get_line(lbi, line, linfo, lineno);
292 
293 	(void) lbi_prev(lbi);
294 
295 	return result;
296 }
297 
298 /*
299  * Move to prev line in line buffer. Returns false, when there
300  * is not valid line in buffer.
301  */
302 bool
lbi_prev(LineBufferIter * lbi)303 lbi_prev(LineBufferIter *lbi)
304 {
305 	if (lbi->order_map)
306 	{
307 		if (lbi->lineno > 0)
308 		{
309 			MappedLine *mpl;
310 
311 			lbi->lineno -= 1;
312 
313 			mpl = &lbi->order_map[lbi->lineno];
314 
315 			lbi->current_lb = mpl->lnb;
316 			lbi->current_lb_rowno = mpl->lnb_row;
317 
318 			return true;
319 		}
320 		else
321 			lbi->lineno = -1;
322 	}
323 	else
324 	{
325 		if (lbi->current_lb)
326 		{
327 			lbi->lineno -= 1;
328 
329 			lbi->current_lb_rowno -= 1;
330 			if (lbi->current_lb_rowno >= 0)
331 				return true;
332 
333 			if (lbi->current_lb->prev)
334 			{
335 				lbi->current_lb = lbi->current_lb->prev;
336 				lbi->current_lb_rowno = LINEBUFFER_LINES - 1;
337 
338 				return true;
339 			}
340 		}
341 	}
342 
343 	lbi->current_lb = NULL;
344 	lbi->current_lb_rowno = 0;
345 
346 	return false;
347 }
348 
349 /*
350  * Move on next line in line buffer. Returns false, when there
351  * is not valid line in buffer.
352  */
353 bool
lbi_next(LineBufferIter * lbi)354 lbi_next(LineBufferIter *lbi)
355 {
356 	if (lbi->order_map)
357 	{
358 		if (lbi->lineno + 1 < lbi->order_map_items)
359 		{
360 			MappedLine *mpl;
361 
362 			lbi->lineno += 1;
363 
364 			mpl = &lbi->order_map[lbi->lineno];
365 
366 			lbi->current_lb = mpl->lnb;
367 			lbi->current_lb_rowno = mpl->lnb_row;
368 
369 			return true;
370 		}
371 		else
372 			lbi->lineno = lbi->order_map_items;
373 	}
374 	else
375 	{
376 		if (lbi->current_lb)
377 		{
378 			/*
379 			 * Previous row must be valid, so we can increase
380 			 * lineno without creating gap after last line lineno.
381 			 */
382 			lbi->lineno += 1;
383 
384 			lbi->current_lb_rowno += 1;
385 			if (lbi->current_lb_rowno < lbi->current_lb->nrows)
386 				return true;
387 
388 			if (lbi->current_lb->next)
389 			{
390 				lbi->current_lb = lbi->current_lb->next;
391 				lbi->current_lb_rowno = 0;
392 
393 				return true;
394 			}
395 		}
396 	}
397 
398 	lbi->current_lb = NULL;
399 	lbi->current_lb_rowno = 0;
400 
401 	return false;
402 }
403 
404 /*
405  * Simple line buffer iterator allows just only forward
406  * scan.
407  */
408 SimpleLineBufferIter *
init_slbi_ddesc(SimpleLineBufferIter * slbi,DataDesc * desc)409 init_slbi_ddesc(SimpleLineBufferIter *slbi, DataDesc *desc)
410 {
411 	slbi->lb = &desc->rows;
412 	slbi->lb_rowno = 0;
413 
414 	if (slbi->lb->nrows > 0)
415 		return slbi;
416 	else
417 		return NULL;
418 
419 }
420 
421 SimpleLineBufferIter *
slbi_get_line_next(SimpleLineBufferIter * slbi,char ** line,LineInfo ** linfo)422 slbi_get_line_next(SimpleLineBufferIter *slbi,
423 				   char **line,
424 				   LineInfo **linfo)
425 {
426 	if (slbi)
427 	{
428 		LineBuffer *lb = slbi->lb;
429 
430 		/*
431 		 * one line should be available every time. The possibility
432 		 * is checked before
433 		 */
434 		if (linfo)
435 			*linfo = lb->lineinfo ? &lb->lineinfo[slbi->lb_rowno] : NULL;
436 
437 		if (line)
438 			*line = lb->rows[slbi->lb_rowno];
439 
440 		slbi->lb_rowno += 1;
441 
442 		/* check an possibility of next read */
443 		if (slbi->lb_rowno < lb->nrows)
444 			return slbi;
445 
446 		if (lb->next)
447 		{
448 			slbi->lb = lb->next;
449 			slbi->lb_rowno = 0;
450 
451 			/* should not be possible */
452 			if (slbi->lb->nrows == 0)
453 				return NULL;
454 		}
455 		else
456 			return NULL;
457 	}
458 	else
459 		*line = NULL;
460 
461 	return slbi;
462 }
463 
464 /*
465  * Free all lines stored in line buffer. An argument is data desc,
466  * because first chunk of line buffer is owned by data desc.
467  */
468 void
lb_free(DataDesc * desc)469 lb_free(DataDesc *desc)
470 {
471 	LineBuffer   *lb = &desc->rows;
472 	LineBuffer   *next;
473 	int		i;
474 
475 	while (lb)
476 	{
477 		for (i = 0; i < lb->nrows; i++)
478 			free(lb->rows[i]);
479 
480 		free(lb->lineinfo);
481 		next = lb->next;
482 
483 		if (lb != &desc->rows)
484 			free(lb);
485 
486 		lb = next;
487 	}
488 }
489 
490 /*
491  * Print all lines to stream
492  */
493 void
lb_print_all_ddesc(DataDesc * desc,FILE * f)494 lb_print_all_ddesc(DataDesc *desc, FILE *f)
495 {
496 	SimpleLineBufferIter slbi, *_slbi;
497 	int		res;
498 
499 	_slbi = init_slbi_ddesc(&slbi, desc);
500 
501 	while (_slbi)
502 	{
503 		char	   *line;
504 
505 		_slbi = slbi_get_line_next(_slbi, &line, NULL);
506 
507 		res = fprintf(f, "%s\n", line);
508 		if (res < 0)
509 			break;
510 	}
511 }
512