1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "lib/mlr_globals.h"
6 #include "lib/mlrutil.h"
7 #include "lib/string_builder.h"
8 #include "containers/hss.h"
9 #include "containers/slls.h"
10 #include "containers/lrec.h"
11
12 #define SB_ALLOC_LENGTH 256
13
14 static lrece_t* lrec_find_entry(lrec_t* prec, char* key);
15 static void lrec_link_at_head(lrec_t* prec, lrece_t* pe);
16 static void lrec_link_at_tail(lrec_t* prec, lrece_t* pe);
17
18 static void lrec_unbacked_free(lrec_t* prec);
19 static void lrec_free_single_line_backing(lrec_t* prec);
20 static void lrec_free_csv_backing(lrec_t* prec);
21 static void lrec_free_multiline_backing(lrec_t* prec);
22
23 // ----------------------------------------------------------------
lrec_unbacked_alloc()24 lrec_t* lrec_unbacked_alloc() {
25 lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
26 memset(prec, 0, sizeof(lrec_t));
27 prec->pfree_backing_func = lrec_unbacked_free;
28 return prec;
29 }
30
lrec_dkvp_alloc(char * line)31 lrec_t* lrec_dkvp_alloc(char* line) {
32 lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
33 memset(prec, 0, sizeof(lrec_t));
34 prec->psingle_line = line;
35 prec->pfree_backing_func = lrec_free_single_line_backing;
36 return prec;
37 }
38
lrec_nidx_alloc(char * line)39 lrec_t* lrec_nidx_alloc(char* line) {
40 lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
41 memset(prec, 0, sizeof(lrec_t));
42 prec->psingle_line = line;
43 prec->pfree_backing_func = lrec_free_single_line_backing;
44 return prec;
45 }
46
lrec_csvlite_alloc(char * data_line)47 lrec_t* lrec_csvlite_alloc(char* data_line) {
48 lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
49 memset(prec, 0, sizeof(lrec_t));
50 prec->psingle_line = data_line;
51 prec->pfree_backing_func = lrec_free_csv_backing;
52 return prec;
53 }
54
lrec_csv_alloc(char * data_line)55 lrec_t* lrec_csv_alloc(char* data_line) {
56 lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
57 memset(prec, 0, sizeof(lrec_t));
58 prec->psingle_line = data_line;
59 prec->pfree_backing_func = lrec_free_csv_backing;
60 return prec;
61 }
62
lrec_xtab_alloc(slls_t * pxtab_lines)63 lrec_t* lrec_xtab_alloc(slls_t* pxtab_lines) {
64 lrec_t* prec = mlr_malloc_or_die(sizeof(lrec_t));
65 memset(prec, 0, sizeof(lrec_t));
66 prec->pxtab_lines = pxtab_lines;
67 prec->pfree_backing_func = lrec_free_multiline_backing;
68 return prec;
69 }
70
71 // ----------------------------------------------------------------
lrec_free_contents(lrec_t * prec)72 static void lrec_free_contents(lrec_t* prec) {
73 for (lrece_t* pe = prec->phead; pe != NULL; /*pe = pe->pnext*/) {
74 if (pe->free_flags & FREE_ENTRY_KEY)
75 free(pe->key);
76 if (pe->free_flags & FREE_ENTRY_VALUE)
77 free(pe->value);
78 lrece_t* ope = pe;
79 pe = pe->pnext;
80 free(ope);
81 }
82 prec->pfree_backing_func(prec);
83 }
84
85 // ----------------------------------------------------------------
lrec_clear(lrec_t * prec)86 void lrec_clear(lrec_t* prec) {
87 if (prec == NULL)
88 return;
89 lrec_free_contents(prec);
90 memset(prec, 0, sizeof(lrec_t));
91 prec->pfree_backing_func = lrec_unbacked_free;
92 }
93
94 // ----------------------------------------------------------------
lrec_free(lrec_t * prec)95 void lrec_free(lrec_t* prec) {
96 if (prec == NULL)
97 return;
98 lrec_free_contents(prec);
99 free(prec);
100 }
101
102 // ----------------------------------------------------------------
lrec_copy(lrec_t * pinrec)103 lrec_t* lrec_copy(lrec_t* pinrec) {
104 lrec_t* poutrec = lrec_unbacked_alloc();
105 for (lrece_t* pe = pinrec->phead; pe != NULL; pe = pe->pnext) {
106 lrec_put(poutrec, mlr_strdup_or_die(pe->key), mlr_strdup_or_die(pe->value),
107 FREE_ENTRY_KEY|FREE_ENTRY_VALUE);
108 }
109 return poutrec;
110 }
111
112 // ----------------------------------------------------------------
lrec_put(lrec_t * prec,char * key,char * value,char free_flags)113 void lrec_put(lrec_t* prec, char* key, char* value, char free_flags) {
114 lrece_t* pe = lrec_find_entry(prec, key);
115
116 if (pe != NULL) {
117 if (pe->free_flags & FREE_ENTRY_VALUE) {
118 free(pe->value);
119 }
120 if (free_flags & FREE_ENTRY_KEY)
121 free(key);
122 pe->value = value;
123 if (free_flags & FREE_ENTRY_VALUE)
124 pe->free_flags |= FREE_ENTRY_VALUE;
125 else
126 pe->free_flags &= ~FREE_ENTRY_VALUE;
127 } else {
128 pe = mlr_malloc_or_die(sizeof(lrece_t));
129 pe->key = key;
130 pe->value = value;
131 pe->free_flags = free_flags;
132 pe->quote_flags = 0;
133
134 if (prec->phead == NULL) {
135 pe->pprev = NULL;
136 pe->pnext = NULL;
137 prec->phead = pe;
138 prec->ptail = pe;
139 } else {
140 pe->pprev = prec->ptail;
141 pe->pnext = NULL;
142 prec->ptail->pnext = pe;
143 prec->ptail = pe;
144 }
145 prec->field_count++;
146 }
147 }
148
lrec_put_ext(lrec_t * prec,char * key,char * value,char free_flags,char quote_flags)149 void lrec_put_ext(lrec_t* prec, char* key, char* value, char free_flags, char quote_flags) {
150 lrece_t* pe = lrec_find_entry(prec, key);
151
152 if (pe != NULL) {
153 if (pe->free_flags & FREE_ENTRY_VALUE) {
154 free(pe->value);
155 }
156 if (free_flags & FREE_ENTRY_KEY)
157 free(key);
158 pe->value = value;
159 if (free_flags & FREE_ENTRY_VALUE)
160 pe->free_flags |= FREE_ENTRY_VALUE;
161 else
162 pe->free_flags &= ~FREE_ENTRY_VALUE;
163 } else {
164 pe = mlr_malloc_or_die(sizeof(lrece_t));
165 pe->key = key;
166 pe->value = value;
167 pe->free_flags = free_flags;
168 pe->quote_flags = quote_flags;
169
170 if (prec->phead == NULL) {
171 pe->pprev = NULL;
172 pe->pnext = NULL;
173 prec->phead = pe;
174 prec->ptail = pe;
175 } else {
176 pe->pprev = prec->ptail;
177 pe->pnext = NULL;
178 prec->ptail->pnext = pe;
179 prec->ptail = pe;
180 }
181 prec->field_count++;
182 }
183 }
184
lrec_prepend(lrec_t * prec,char * key,char * value,char free_flags)185 void lrec_prepend(lrec_t* prec, char* key, char* value, char free_flags) {
186 lrece_t* pe = lrec_find_entry(prec, key);
187
188 if (pe != NULL) {
189 if (pe->free_flags & FREE_ENTRY_VALUE) {
190 free(pe->value);
191 }
192 pe->value = value;
193 pe->free_flags &= ~FREE_ENTRY_VALUE;
194 if (free_flags & FREE_ENTRY_VALUE)
195 pe->free_flags |= FREE_ENTRY_VALUE;
196 } else {
197 pe = mlr_malloc_or_die(sizeof(lrece_t));
198 pe->key = key;
199 pe->value = value;
200 pe->free_flags = free_flags;
201 pe->quote_flags = 0;
202
203 if (prec->phead == NULL) {
204 pe->pprev = NULL;
205 pe->pnext = NULL;
206 prec->phead = pe;
207 prec->ptail = pe;
208 } else {
209 pe->pnext = prec->phead;
210 pe->pprev = NULL;
211 prec->phead->pprev = pe;
212 prec->phead = pe;
213 }
214 prec->field_count++;
215 }
216 }
217
lrec_put_after(lrec_t * prec,lrece_t * pd,char * key,char * value,char free_flags)218 lrece_t* lrec_put_after(lrec_t* prec, lrece_t* pd, char* key, char* value, char free_flags) {
219 lrece_t* pe = lrec_find_entry(prec, key);
220
221 if (pe != NULL) { // Overwrite
222 if (pe->free_flags & FREE_ENTRY_VALUE) {
223 free(pe->value);
224 }
225 pe->value = value;
226 pe->free_flags &= ~FREE_ENTRY_VALUE;
227 if (free_flags & FREE_ENTRY_VALUE)
228 pe->free_flags |= FREE_ENTRY_VALUE;
229 } else { // Insert after specified entry
230 pe = mlr_malloc_or_die(sizeof(lrece_t));
231 pe->key = key;
232 pe->value = value;
233 pe->free_flags = free_flags;
234 pe->quote_flags = 0;
235
236 if (pd->pnext == NULL) { // Append at end of list
237 pd->pnext = pe;
238 pe->pprev = pd;
239 pe->pnext = NULL;
240 prec->ptail = pe;
241
242 } else {
243 lrece_t* pf = pd->pnext;
244 pd->pnext = pe;
245 pf->pprev = pe;
246 pe->pprev = pd;
247 pe->pnext = pf;
248 }
249
250 prec->field_count++;
251 }
252 return pe;
253 }
254
255 // ----------------------------------------------------------------
lrec_get(lrec_t * prec,char * key)256 char* lrec_get(lrec_t* prec, char* key) {
257 lrece_t* pe = lrec_find_entry(prec, key);
258 if (pe != NULL) {
259 return pe->value;
260 } else {
261 return NULL;
262 }
263 }
264
lrec_get_pff(lrec_t * prec,char * key,char ** ppfree_flags)265 char* lrec_get_pff(lrec_t* prec, char* key, char** ppfree_flags) {
266 lrece_t* pe = lrec_find_entry(prec, key);
267 if (pe != NULL) {
268 *ppfree_flags = &pe->free_flags;
269 return pe->value;
270 } else {
271 *ppfree_flags = NULL;
272 return NULL;
273 }
274 }
275
lrec_get_ext(lrec_t * prec,char * key,lrece_t ** ppentry)276 char* lrec_get_ext(lrec_t* prec, char* key, lrece_t** ppentry) {
277 lrece_t* pe = lrec_find_entry(prec, key);
278 if (pe != NULL) {
279 *ppentry = pe;
280 return pe->value;
281 } else {
282 *ppentry = NULL;;
283 return NULL;
284 }
285 }
286
287 // ----------------------------------------------------------------
lrec_get_pair_by_position(lrec_t * prec,int position)288 lrece_t* lrec_get_pair_by_position(lrec_t* prec, int position) { // 1-up not 0-up
289 if (position <= 0 || position > prec->field_count) {
290 return NULL;
291 }
292 int sought_index = position - 1;
293 int found_index = 0;
294 lrece_t* pe = NULL;
295 for (
296 found_index = 0, pe = prec->phead;
297 pe != NULL;
298 found_index++, pe = pe->pnext
299 ) {
300 if (found_index == sought_index) {
301 return pe;
302 }
303 }
304 fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n",
305 MLR_GLOBALS.bargv0, __FILE__, __LINE__);
306 exit(1);
307 }
308
lrec_get_key_by_position(lrec_t * prec,int position)309 char* lrec_get_key_by_position(lrec_t* prec, int position) { // 1-up not 0-up
310 lrece_t* pe = lrec_get_pair_by_position(prec, position);
311 if (pe == NULL) {
312 return NULL;
313 } else {
314 return pe->key;
315 }
316 }
317
lrec_get_value_by_position(lrec_t * prec,int position)318 char* lrec_get_value_by_position(lrec_t* prec, int position) { // 1-up not 0-up
319 lrece_t* pe = lrec_get_pair_by_position(prec, position);
320 if (pe == NULL) {
321 return NULL;
322 } else {
323 return pe->value;
324 }
325 }
326
327 // ----------------------------------------------------------------
lrec_remove(lrec_t * prec,char * key)328 void lrec_remove(lrec_t* prec, char* key) {
329 lrece_t* pe = lrec_find_entry(prec, key);
330 if (pe == NULL)
331 return;
332
333 lrec_unlink(prec, pe);
334
335 if (pe->free_flags & FREE_ENTRY_KEY) {
336 free(pe->key);
337 }
338 if (pe->free_flags & FREE_ENTRY_VALUE) {
339 free(pe->value);
340 }
341
342 free(pe);
343 }
344
345 // ----------------------------------------------------------------
lrec_remove_by_position(lrec_t * prec,int position)346 void lrec_remove_by_position(lrec_t* prec, int position) { // 1-up not 0-up
347 lrece_t* pe = lrec_get_pair_by_position(prec, position);
348 if (pe == NULL)
349 return;
350
351 lrec_unlink(prec, pe);
352
353 if (pe->free_flags & FREE_ENTRY_KEY) {
354 free(pe->key);
355 }
356 if (pe->free_flags & FREE_ENTRY_VALUE) {
357 free(pe->value);
358 }
359
360 free(pe);
361 }
362
363 // Before:
364 // "x" => "3"
365 // "y" => "4" <-- pold
366 // "z" => "5" <-- pnew
367 //
368 // Rename y to z
369 //
370 // After:
371 // "x" => "3"
372 // "z" => "4"
373 //
lrec_rename(lrec_t * prec,char * old_key,char * new_key,int new_needs_freeing)374 void lrec_rename(lrec_t* prec, char* old_key, char* new_key, int new_needs_freeing) {
375
376 lrece_t* pold = lrec_find_entry(prec, old_key);
377 if (pold != NULL) {
378 lrece_t* pnew = lrec_find_entry(prec, new_key);
379
380 if (pnew == NULL) { // E.g. rename "x" to "y" when "y" is not present
381 if (pold->free_flags & FREE_ENTRY_KEY) {
382 free(pold->key);
383 pold->key = new_key;
384 if (!new_needs_freeing)
385 pold->free_flags &= ~FREE_ENTRY_KEY;
386 } else {
387 pold->key = new_key;
388 if (new_needs_freeing)
389 pold->free_flags |= FREE_ENTRY_KEY;
390 }
391
392 } else { // E.g. rename "x" to "y" when "y" is already present
393 if (pnew->free_flags & FREE_ENTRY_VALUE) {
394 free(pnew->value);
395 }
396 if (pold->free_flags & FREE_ENTRY_KEY) {
397 free(pold->key);
398 pold->free_flags &= ~FREE_ENTRY_KEY;
399 }
400 pold->key = new_key;
401 if (new_needs_freeing)
402 pold->free_flags |= FREE_ENTRY_KEY;
403 else
404 pold->free_flags &= ~FREE_ENTRY_KEY;
405 lrec_unlink(prec, pnew);
406 free(pnew);
407 }
408 }
409 }
410
411 // Cases:
412 // 1. Rename field at position 3 from "x" to "y when "y" does not exist elsewhere in the srec
413 // 2. Rename field at position 3 from "x" to "y when "y" does exist elsewhere in the srec
414 // Note: position is 1-up not 0-up
lrec_rename_at_position(lrec_t * prec,int position,char * new_key,int new_needs_freeing)415 void lrec_rename_at_position(lrec_t* prec, int position, char* new_key, int new_needs_freeing){
416 lrece_t* pe = lrec_get_pair_by_position(prec, position);
417 if (pe == NULL) {
418 if (new_needs_freeing) {
419 free(new_key);
420 }
421 return;
422 }
423
424 lrece_t* pother = lrec_find_entry(prec, new_key);
425
426 if (pe->free_flags & FREE_ENTRY_KEY) {
427 free(pe->key);
428 }
429 pe->key = new_key;
430 if (new_needs_freeing) {
431 pe->free_flags |= FREE_ENTRY_KEY;
432 } else {
433 pe->free_flags &= ~FREE_ENTRY_KEY;
434 }
435 if (pother != NULL) {
436 lrec_unlink(prec, pother);
437 free(pother);
438 }
439 }
440
441 // ----------------------------------------------------------------
lrec_move_to_head(lrec_t * prec,char * key)442 void lrec_move_to_head(lrec_t* prec, char* key) {
443 lrece_t* pe = lrec_find_entry(prec, key);
444 if (pe == NULL)
445 return;
446
447 lrec_unlink(prec, pe);
448 lrec_link_at_head(prec, pe);
449 }
450
lrec_move_to_tail(lrec_t * prec,char * key)451 void lrec_move_to_tail(lrec_t* prec, char* key) {
452 lrece_t* pe = lrec_find_entry(prec, key);
453 if (pe == NULL)
454 return;
455
456 lrec_unlink(prec, pe);
457 lrec_link_at_tail(prec, pe);
458 }
459
460 // ----------------------------------------------------------------
461 // Simply rename the first (at most) n positions where n is the length of pnames.
462 //
463 // Possible complications:
464 //
465 // * pnames itself contains duplicates -- we require this as invariant-check from the caller since (for performance)
466 // we don't want to check this on every record processed.
467 //
468 // * pnames has length less than the current record and one of the new names becomes a clash with an existing name.
469 // Example:
470 // - Input record has names "a,b,c,d,e".
471 // - pnames is "d,x,f"
472 // - We then construct the invalid "d,x,f,d,e" -- we need to detect and unset the second 'd' field.
473
lrec_label(lrec_t * prec,slls_t * pnames_as_list,hss_t * pnames_as_set)474 void lrec_label(lrec_t* prec, slls_t* pnames_as_list, hss_t* pnames_as_set) {
475 lrece_t* pe = prec->phead;
476 sllse_t* pn = pnames_as_list->phead;
477
478 // Process the labels list
479 for ( ; pe != NULL && pn != NULL; pe = pe->pnext, pn = pn->pnext) {
480 char* new_name = pn->value;
481
482 if (pe->free_flags & FREE_ENTRY_KEY) {
483 free(pe->key);
484 }
485 pe->key = mlr_strdup_or_die(new_name);;
486 pe->free_flags |= FREE_ENTRY_KEY;
487 }
488
489 // Process the remaining fields in the record beyond those affected by the new-labels list
490 for ( ; pe != NULL; ) {
491 char* name = pe->key;
492 if (hss_has(pnames_as_set, name)) {
493 lrece_t* pnext = pe->pnext;
494 if (pe->free_flags & FREE_ENTRY_KEY) {
495 free(pe->key);
496 }
497 if (pe->free_flags & FREE_ENTRY_VALUE) {
498 free(pe->value);
499 }
500 lrec_unlink(prec, pe);
501 free(pe);
502 pe = pnext;
503 } else {
504 pe = pe->pnext;
505 }
506 }
507 }
508
509 // ----------------------------------------------------------------
lrece_update_value(lrece_t * pe,char * new_value,int new_needs_freeing)510 void lrece_update_value(lrece_t* pe, char* new_value, int new_needs_freeing) {
511 if (pe == NULL) {
512 return;
513 }
514 if (pe->free_flags & FREE_ENTRY_VALUE) {
515 free(pe->value);
516 }
517 pe->value = new_value;
518 if (new_needs_freeing)
519 pe->free_flags |= FREE_ENTRY_VALUE;
520 else
521 pe->free_flags &= ~FREE_ENTRY_VALUE;
522 }
523
524 // ----------------------------------------------------------------
lrec_unlink(lrec_t * prec,lrece_t * pe)525 void lrec_unlink(lrec_t* prec, lrece_t* pe) {
526 if (pe == prec->phead) {
527 if (pe == prec->ptail) {
528 prec->phead = NULL;
529 prec->ptail = NULL;
530 } else {
531 prec->phead = pe->pnext;
532 pe->pnext->pprev = NULL;
533 }
534 } else {
535 pe->pprev->pnext = pe->pnext;
536 if (pe == prec->ptail) {
537 prec->ptail = pe->pprev;
538 } else {
539 pe->pnext->pprev = pe->pprev;
540 }
541 }
542 prec->field_count--;
543 }
544
lrec_unlink_and_free(lrec_t * prec,lrece_t * pe)545 void lrec_unlink_and_free(lrec_t* prec, lrece_t* pe) {
546 if (pe->free_flags & FREE_ENTRY_KEY)
547 free(pe->key);
548 if (pe->free_flags & FREE_ENTRY_VALUE)
549 free(pe->value);
550 lrec_unlink(prec, pe);
551 free(pe);
552 }
553
554 // ----------------------------------------------------------------
lrec_link_at_head(lrec_t * prec,lrece_t * pe)555 static void lrec_link_at_head(lrec_t* prec, lrece_t* pe) {
556
557 if (prec->phead == NULL) {
558 pe->pprev = NULL;
559 pe->pnext = NULL;
560 prec->phead = pe;
561 prec->ptail = pe;
562 } else {
563 // [b,c,d] + a
564 pe->pprev = NULL;
565 pe->pnext = prec->phead;
566 prec->phead->pprev = pe;
567 prec->phead = pe;
568 }
569 prec->field_count++;
570 }
571
lrec_link_at_tail(lrec_t * prec,lrece_t * pe)572 static void lrec_link_at_tail(lrec_t* prec, lrece_t* pe) {
573
574 if (prec->phead == NULL) {
575 pe->pprev = NULL;
576 pe->pnext = NULL;
577 prec->phead = pe;
578 prec->ptail = pe;
579 } else {
580 pe->pprev = prec->ptail;
581 pe->pnext = NULL;
582 prec->ptail->pnext = pe;
583 prec->ptail = pe;
584 }
585 prec->field_count++;
586 }
587
588 // ----------------------------------------------------------------
lrec_dump(lrec_t * prec)589 void lrec_dump(lrec_t* prec) {
590 lrec_dump_fp(prec, stdout);
591 }
lrec_dump_fp(lrec_t * prec,FILE * fp)592 void lrec_dump_fp(lrec_t* prec, FILE* fp) {
593 if (prec == NULL) {
594 fprintf(fp, "NULL\n");
595 return;
596 }
597 fprintf(fp, "field_count = %d\n", prec->field_count);
598 fprintf(fp, "| phead: %16p | ptail %16p\n", prec->phead, prec->ptail);
599 for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
600 const char* key_string = (pe == NULL) ? "none" :
601 pe->key == NULL ? "null" :
602 pe->key;
603 const char* value_string = (pe == NULL) ? "none" :
604 pe->value == NULL ? "null" :
605 pe->value;
606 fprintf(fp,
607 "| prev: %16p curr: %16p next: %16p | key: %12s | value: %12s |\n",
608 pe->pprev, pe, pe->pnext,
609 key_string, value_string);
610 }
611 }
612
lrec_dump_titled(char * msg,lrec_t * prec)613 void lrec_dump_titled(char* msg, lrec_t* prec) {
614 printf("%s:\n", msg);
615 lrec_dump(prec);
616 printf("\n");
617 }
618
lrec_pointer_dump(lrec_t * prec)619 void lrec_pointer_dump(lrec_t* prec) {
620 printf("prec %p\n", prec);
621 for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
622 printf(" pe %p k %p v %p\n", pe, pe->key, pe->value);
623 }
624 }
625
626 // ----------------------------------------------------------------
lrec_unbacked_free(lrec_t * prec)627 static void lrec_unbacked_free(lrec_t* prec) {
628 }
629
lrec_free_single_line_backing(lrec_t * prec)630 static void lrec_free_single_line_backing(lrec_t* prec) {
631 free(prec->psingle_line);
632 }
633
lrec_free_csv_backing(lrec_t * prec)634 static void lrec_free_csv_backing(lrec_t* prec) {
635 free(prec->psingle_line);
636 }
637
lrec_free_multiline_backing(lrec_t * prec)638 static void lrec_free_multiline_backing(lrec_t* prec) {
639 slls_free(prec->pxtab_lines);
640 }
641
642 // ================================================================
643
644 // ----------------------------------------------------------------
645 // Note on efficiency:
646 //
647 // I was imagining/hoping that strcmp has additional optimizations (e.g.
648 // hand-coded in assembly), so I don't *want* to re-implement it (i.e. I
649 // probably can't outperform it).
650 //
651 // But actual experiments show I get about a 1-2% performance gain doing it
652 // myself (on my particular system).
653
lrec_find_entry(lrec_t * prec,char * key)654 static lrece_t* lrec_find_entry(lrec_t* prec, char* key) {
655 #if 1
656 for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
657 char* pa = pe->key;
658 char* pb = key;
659 while (*pa && *pb && (*pa == *pb)) {
660 pa++;
661 pb++;
662 }
663 if (*pa == 0 && *pb == 0)
664 return pe;
665 }
666 return NULL;
667 #else
668 for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext)
669 if (streq(pe->key, key))
670 return pe;
671 return NULL;
672 #endif
673 }
674
675 // ----------------------------------------------------------------
lrec_literal_1(char * k1,char * v1)676 lrec_t* lrec_literal_1(char* k1, char* v1) {
677 lrec_t* prec = lrec_unbacked_alloc();
678 lrec_put(prec, k1, v1, NO_FREE);
679 return prec;
680 }
681
lrec_literal_2(char * k1,char * v1,char * k2,char * v2)682 lrec_t* lrec_literal_2(char* k1, char* v1, char* k2, char* v2) {
683 lrec_t* prec = lrec_unbacked_alloc();
684 lrec_put(prec, k1, v1, NO_FREE);
685 lrec_put(prec, k2, v2, NO_FREE);
686 return prec;
687 }
688
lrec_literal_3(char * k1,char * v1,char * k2,char * v2,char * k3,char * v3)689 lrec_t* lrec_literal_3(char* k1, char* v1, char* k2, char* v2, char* k3, char* v3) {
690 lrec_t* prec = lrec_unbacked_alloc();
691 lrec_put(prec, k1, v1, NO_FREE);
692 lrec_put(prec, k2, v2, NO_FREE);
693 lrec_put(prec, k3, v3, NO_FREE);
694 return prec;
695 }
696
lrec_literal_4(char * k1,char * v1,char * k2,char * v2,char * k3,char * v3,char * k4,char * v4)697 lrec_t* lrec_literal_4(char* k1, char* v1, char* k2, char* v2, char* k3, char* v3, char* k4, char* v4) {
698 lrec_t* prec = lrec_unbacked_alloc();
699 lrec_put(prec, k1, v1, NO_FREE);
700 lrec_put(prec, k2, v2, NO_FREE);
701 lrec_put(prec, k3, v3, NO_FREE);
702 lrec_put(prec, k4, v4, NO_FREE);
703 return prec;
704 }
705
lrec_print(lrec_t * prec)706 void lrec_print(lrec_t* prec) {
707 FILE* output_stream = stdout;
708 char ors = '\n';
709 char ofs = ',';
710 char ops = '=';
711 if (prec == NULL) {
712 fputs("NULL", output_stream);
713 fputc(ors, output_stream);
714 return;
715 }
716 int nf = 0;
717 for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
718 if (nf > 0)
719 fputc(ofs, output_stream);
720 fputs(pe->key, output_stream);
721 fputc(ops, output_stream);
722 fputs(pe->value, output_stream);
723 nf++;
724 }
725 fputc(ors, output_stream);
726 }
727
lrec_sprint(lrec_t * prec,char * ors,char * ofs,char * ops)728 char* lrec_sprint(lrec_t* prec, char* ors, char* ofs, char* ops) {
729 string_builder_t* psb = sb_alloc(SB_ALLOC_LENGTH);
730 if (prec == NULL) {
731 sb_append_string(psb, "NULL");
732 } else {
733 int nf = 0;
734 for (lrece_t* pe = prec->phead; pe != NULL; pe = pe->pnext) {
735 if (nf > 0)
736 sb_append_string(psb, ofs);
737 sb_append_string(psb, pe->key);
738 sb_append_string(psb, ops);
739 sb_append_string(psb, pe->value);
740 nf++;
741 }
742 sb_append_string(psb, ors);
743 }
744 char* rv = sb_finish(psb);
745 sb_free(psb);
746 return rv;
747 }
748