1 /*************************************************************************************************
2 * The utility API of Tokyo Cabinet
3 * Copyright (C) 2006-2012 FAL Labs
4 * This file is part of Tokyo Cabinet.
5 * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
6 * the GNU Lesser General Public License as published by the Free Software Foundation; either
7 * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope
8 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10 * License for more details.
11 * You should have received a copy of the GNU Lesser General Public License along with Tokyo
12 * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13 * Boston, MA 02111-1307 USA.
14 *************************************************************************************************/
15
16
17 #include "tcutil.h"
18 #include "myconf.h"
19 #include "md5.h"
20
21
22
23 /*************************************************************************************************
24 * basic utilities
25 *************************************************************************************************/
26
27
28 /* String containing the version information. */
29 const char *tcversion = _TC_VERSION;
30
31
32 /* Call back function for handling a fatal error. */
33 void (*tcfatalfunc)(const char *message) = NULL;
34
35
36 /* Allocate a region on memory. */
tcmalloc(size_t size)37 void *tcmalloc(size_t size){
38 assert(size > 0 && size < INT_MAX);
39 char *p;
40 TCMALLOC(p, size);
41 return p;
42 }
43
44
45 /* Allocate a nullified region on memory. */
tccalloc(size_t nmemb,size_t size)46 void *tccalloc(size_t nmemb, size_t size){
47 assert(nmemb > 0 && nmemb < INT_MAX && size > 0 && size < INT_MAX);
48 char *p;
49 TCCALLOC(p, nmemb, size);
50 return p;
51 }
52
53
54 /* Re-allocate a region on memory. */
tcrealloc(void * ptr,size_t size)55 void *tcrealloc(void *ptr, size_t size){
56 assert(size >= 0 && size < INT_MAX);
57 char *p;
58 TCREALLOC(p, ptr, size);
59 return p;
60 }
61
62
63 /* Duplicate a region on memory. */
tcmemdup(const void * ptr,size_t size)64 void *tcmemdup(const void *ptr, size_t size){
65 assert(ptr && size >= 0);
66 char *p;
67 TCMALLOC(p, size + 1);
68 memcpy(p, ptr, size);
69 p[size] = '\0';
70 return p;
71 }
72
73
74 /* Duplicate a string on memory. */
tcstrdup(const void * str)75 char *tcstrdup(const void *str){
76 assert(str);
77 int size = strlen(str);
78 char *p;
79 TCMALLOC(p, size + 1);
80 memcpy(p, str, size);
81 p[size] = '\0';
82 return p;
83 }
84
85
86 /* Free a region on memory. */
tcfree(void * ptr)87 void tcfree(void *ptr){
88 TCFREE(ptr);
89 }
90
91
92
93 /*************************************************************************************************
94 * extensible string
95 *************************************************************************************************/
96
97
98 #define TCXSTRUNIT 12 // allocation unit size of an extensible string
99
100
101 /* private function prototypes */
102 static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap);
103
104
105 /* Create an extensible string object. */
tcxstrnew(void)106 TCXSTR *tcxstrnew(void){
107 TCXSTR *xstr;
108 TCMALLOC(xstr, sizeof(*xstr));
109 TCMALLOC(xstr->ptr, TCXSTRUNIT);
110 xstr->size = 0;
111 xstr->asize = TCXSTRUNIT;
112 xstr->ptr[0] = '\0';
113 return xstr;
114 }
115
116
117 /* Create an extensible string object from a character string. */
tcxstrnew2(const char * str)118 TCXSTR *tcxstrnew2(const char *str){
119 assert(str);
120 TCXSTR *xstr;
121 TCMALLOC(xstr, sizeof(*xstr));
122 int size = strlen(str);
123 int asize = tclmax(size + 1, TCXSTRUNIT);
124 TCMALLOC(xstr->ptr, asize);
125 xstr->size = size;
126 xstr->asize = asize;
127 memcpy(xstr->ptr, str, size + 1);
128 return xstr;
129 }
130
131
132 /* Create an extensible string object with the initial allocation size. */
tcxstrnew3(int asiz)133 TCXSTR *tcxstrnew3(int asiz){
134 assert(asiz >= 0);
135 asiz = tclmax(asiz, TCXSTRUNIT);
136 TCXSTR *xstr;
137 TCMALLOC(xstr, sizeof(*xstr));
138 TCMALLOC(xstr->ptr, asiz);
139 xstr->size = 0;
140 xstr->asize = asiz;
141 xstr->ptr[0] = '\0';
142 return xstr;
143 }
144
145
146 /* Copy an extensible string object. */
tcxstrdup(const TCXSTR * xstr)147 TCXSTR *tcxstrdup(const TCXSTR *xstr){
148 assert(xstr);
149 TCXSTR *nxstr;
150 TCMALLOC(nxstr, sizeof(*nxstr));
151 int asize = tclmax(xstr->size + 1, TCXSTRUNIT);
152 TCMALLOC(nxstr->ptr, asize);
153 nxstr->size = xstr->size;
154 nxstr->asize = asize;
155 memcpy(nxstr->ptr, xstr->ptr, xstr->size + 1);
156 return nxstr;
157 }
158
159
160 /* Delete an extensible string object. */
tcxstrdel(TCXSTR * xstr)161 void tcxstrdel(TCXSTR *xstr){
162 assert(xstr);
163 TCFREE(xstr->ptr);
164 TCFREE(xstr);
165 }
166
167
168 /* Concatenate a region to the end of an extensible string object. */
tcxstrcat(TCXSTR * xstr,const void * ptr,int size)169 void tcxstrcat(TCXSTR *xstr, const void *ptr, int size){
170 assert(xstr && ptr && size >= 0);
171 int nsize = xstr->size + size + 1;
172 if(xstr->asize < nsize){
173 while(xstr->asize < nsize){
174 xstr->asize *= 2;
175 if(xstr->asize < nsize) xstr->asize = nsize;
176 }
177 TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
178 }
179 memcpy(xstr->ptr + xstr->size, ptr, size);
180 xstr->size += size;
181 xstr->ptr[xstr->size] = '\0';
182 }
183
184
185 /* Concatenate a character string to the end of an extensible string object. */
tcxstrcat2(TCXSTR * xstr,const char * str)186 void tcxstrcat2(TCXSTR *xstr, const char *str){
187 assert(xstr && str);
188 int size = strlen(str);
189 int nsize = xstr->size + size + 1;
190 if(xstr->asize < nsize){
191 while(xstr->asize < nsize){
192 xstr->asize *= 2;
193 if(xstr->asize < nsize) xstr->asize = nsize;
194 }
195 TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
196 }
197 memcpy(xstr->ptr + xstr->size, str, size + 1);
198 xstr->size += size;
199 }
200
201
202 /* Get the pointer of the region of an extensible string object. */
tcxstrptr(const TCXSTR * xstr)203 const void *tcxstrptr(const TCXSTR *xstr){
204 assert(xstr);
205 return xstr->ptr;
206 }
207
208
209 /* Get the size of the region of an extensible string object. */
tcxstrsize(const TCXSTR * xstr)210 int tcxstrsize(const TCXSTR *xstr){
211 assert(xstr);
212 return xstr->size;
213 }
214
215
216 /* Clear an extensible string object. */
tcxstrclear(TCXSTR * xstr)217 void tcxstrclear(TCXSTR *xstr){
218 assert(xstr);
219 xstr->size = 0;
220 xstr->ptr[0] = '\0';
221 }
222
223
224 /* Perform formatted output into an extensible string object. */
tcxstrprintf(TCXSTR * xstr,const char * format,...)225 void tcxstrprintf(TCXSTR *xstr, const char *format, ...){
226 assert(xstr && format);
227 va_list ap;
228 va_start(ap, format);
229 tcvxstrprintf(xstr, format, ap);
230 va_end(ap);
231 }
232
233
234 /* Allocate a formatted string on memory. */
tcsprintf(const char * format,...)235 char *tcsprintf(const char *format, ...){
236 assert(format);
237 TCXSTR *xstr = tcxstrnew();
238 va_list ap;
239 va_start(ap, format);
240 tcvxstrprintf(xstr, format, ap);
241 va_end(ap);
242 return tcxstrtomalloc(xstr);
243 }
244
245
246 /* Perform formatted output into an extensible string object. */
tcvxstrprintf(TCXSTR * xstr,const char * format,va_list ap)247 static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap){
248 assert(xstr && format);
249 while(*format != '\0'){
250 if(*format == '%'){
251 char cbuf[TCNUMBUFSIZ];
252 cbuf[0] = '%';
253 int cblen = 1;
254 int lnum = 0;
255 format++;
256 while(strchr("0123456789 .+-hlLz", *format) && *format != '\0' &&
257 cblen < TCNUMBUFSIZ - 1){
258 if(*format == 'l' || *format == 'L') lnum++;
259 cbuf[cblen++] = *(format++);
260 }
261 cbuf[cblen++] = *format;
262 cbuf[cblen] = '\0';
263 int tlen;
264 char *tmp, tbuf[TCNUMBUFSIZ*4];
265 switch(*format){
266 case 's':
267 tmp = va_arg(ap, char *);
268 if(!tmp) tmp = "(null)";
269 tcxstrcat2(xstr, tmp);
270 break;
271 case 'd':
272 if(lnum >= 2){
273 tlen = sprintf(tbuf, cbuf, va_arg(ap, long long));
274 } else if(lnum >= 1){
275 tlen = sprintf(tbuf, cbuf, va_arg(ap, long));
276 } else {
277 tlen = sprintf(tbuf, cbuf, va_arg(ap, int));
278 }
279 TCXSTRCAT(xstr, tbuf, tlen);
280 break;
281 case 'o': case 'u': case 'x': case 'X': case 'c':
282 if(lnum >= 2){
283 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long long));
284 } else if(lnum >= 1){
285 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long));
286 } else {
287 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int));
288 }
289 TCXSTRCAT(xstr, tbuf, tlen);
290 break;
291 case 'e': case 'E': case 'f': case 'g': case 'G':
292 if(lnum >= 1){
293 tlen = snprintf(tbuf, sizeof(tbuf), cbuf, va_arg(ap, long double));
294 } else {
295 tlen = snprintf(tbuf, sizeof(tbuf), cbuf, va_arg(ap, double));
296 }
297 if(tlen < 0 || tlen > sizeof(tbuf)){
298 tbuf[sizeof(tbuf)-1] = '*';
299 tlen = sizeof(tbuf);
300 }
301 TCXSTRCAT(xstr, tbuf, tlen);
302 break;
303 case '@':
304 tmp = va_arg(ap, char *);
305 if(!tmp) tmp = "(null)";
306 while(*tmp){
307 switch(*tmp){
308 case '&': TCXSTRCAT(xstr, "&", 5); break;
309 case '<': TCXSTRCAT(xstr, "<", 4); break;
310 case '>': TCXSTRCAT(xstr, ">", 4); break;
311 case '"': TCXSTRCAT(xstr, """, 6); break;
312 default:
313 if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f)))
314 TCXSTRCAT(xstr, tmp, 1);
315 break;
316 }
317 tmp++;
318 }
319 break;
320 case '?':
321 tmp = va_arg(ap, char *);
322 if(!tmp) tmp = "(null)";
323 while(*tmp){
324 unsigned char c = *(unsigned char *)tmp;
325 if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
326 (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
327 TCXSTRCAT(xstr, tmp, 1);
328 } else {
329 tlen = sprintf(tbuf, "%%%02X", c);
330 TCXSTRCAT(xstr, tbuf, tlen);
331 }
332 tmp++;
333 }
334 break;
335 case 'b':
336 if(lnum >= 2){
337 tlen = tcnumtostrbin(va_arg(ap, unsigned long long), tbuf,
338 tcatoi(cbuf + 1), (cbuf[1] == '0') ? '0' : ' ');
339 } else if(lnum >= 1){
340 tlen = tcnumtostrbin(va_arg(ap, unsigned long), tbuf,
341 tcatoi(cbuf + 1), (cbuf[1] == '0') ? '0' : ' ');
342 } else {
343 tlen = tcnumtostrbin(va_arg(ap, unsigned int), tbuf,
344 tcatoi(cbuf + 1), (cbuf[1] == '0') ? '0' : ' ');
345 }
346 TCXSTRCAT(xstr, tbuf, tlen);
347 break;
348 case '%':
349 TCXSTRCAT(xstr, "%", 1);
350 break;
351 }
352 } else {
353 TCXSTRCAT(xstr, format, 1);
354 }
355 format++;
356 }
357 }
358
359
360
361 /*************************************************************************************************
362 * extensible string (for experts)
363 *************************************************************************************************/
364
365
366 /* Convert an extensible string object into a usual allocated region. */
tcxstrtomalloc(TCXSTR * xstr)367 void *tcxstrtomalloc(TCXSTR *xstr){
368 assert(xstr);
369 char *ptr;
370 ptr = xstr->ptr;
371 TCFREE(xstr);
372 return ptr;
373 }
374
375
376 /* Create an extensible string object from an allocated region. */
tcxstrfrommalloc(void * ptr,int size)377 TCXSTR *tcxstrfrommalloc(void *ptr, int size){
378 TCXSTR *xstr;
379 TCMALLOC(xstr, sizeof(*xstr));
380 TCREALLOC(xstr->ptr, ptr, size + 1);
381 xstr->ptr[size] = '\0';
382 xstr->size = size;
383 xstr->asize = size;
384 return xstr;
385 }
386
387
388
389 /*************************************************************************************************
390 * array list
391 *************************************************************************************************/
392
393
394 #define TCLISTUNIT 64 // allocation unit number of a list handle
395
396
397 /* private function prototypes */
398 static int tclistelemcmp(const void *a, const void *b);
399 static int tclistelemcmpci(const void *a, const void *b);
400
401
402 /* Create a list object. */
tclistnew(void)403 TCLIST *tclistnew(void){
404 TCLIST *list;
405 TCMALLOC(list, sizeof(*list));
406 list->anum = TCLISTUNIT;
407 TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
408 list->start = 0;
409 list->num = 0;
410 return list;
411 }
412
413
414 /* Create a list object. */
tclistnew2(int anum)415 TCLIST *tclistnew2(int anum){
416 TCLIST *list;
417 TCMALLOC(list, sizeof(*list));
418 if(anum < 1) anum = 1;
419 list->anum = anum;
420 TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
421 list->start = 0;
422 list->num = 0;
423 return list;
424 }
425
426
427 /* Create a list object with initial string elements. */
tclistnew3(const char * str,...)428 TCLIST *tclistnew3(const char *str, ...){
429 TCLIST *list = tclistnew();
430 if(str){
431 tclistpush2(list, str);
432 va_list ap;
433 va_start(ap, str);
434 const char *elem;
435 while((elem = va_arg(ap, char *)) != NULL){
436 tclistpush2(list, elem);
437 }
438 va_end(ap);
439 }
440 return list;
441 }
442
443
444 /* Copy a list object. */
tclistdup(const TCLIST * list)445 TCLIST *tclistdup(const TCLIST *list){
446 assert(list);
447 int num = list->num;
448 if(num < 1) return tclistnew();
449 const TCLISTDATUM *array = list->array + list->start;
450 TCLIST *nlist;
451 TCMALLOC(nlist, sizeof(*nlist));
452 TCLISTDATUM *narray;
453 TCMALLOC(narray, sizeof(list->array[0]) * num);
454 for(int i = 0; i < num; i++){
455 int size = array[i].size;
456 TCMALLOC(narray[i].ptr, tclmax(size + 1, TCXSTRUNIT));
457 memcpy(narray[i].ptr, array[i].ptr, size + 1);
458 narray[i].size = array[i].size;
459 }
460 nlist->anum = num;
461 nlist->array = narray;
462 nlist->start = 0;
463 nlist->num = num;
464 return nlist;
465 }
466
467
468 /* Delete a list object. */
tclistdel(TCLIST * list)469 void tclistdel(TCLIST *list){
470 assert(list);
471 TCLISTDATUM *array = list->array;
472 int end = list->start + list->num;
473 for(int i = list->start; i < end; i++){
474 TCFREE(array[i].ptr);
475 }
476 TCFREE(list->array);
477 TCFREE(list);
478 }
479
480
481 /* Get the number of elements of a list object. */
tclistnum(const TCLIST * list)482 int tclistnum(const TCLIST *list){
483 assert(list);
484 return list->num;
485 }
486
487
488 /* Get the pointer to the region of an element of a list object. */
tclistval(const TCLIST * list,int index,int * sp)489 const void *tclistval(const TCLIST *list, int index, int *sp){
490 assert(list && index >= 0 && sp);
491 if(index >= list->num) return NULL;
492 index += list->start;
493 *sp = list->array[index].size;
494 return list->array[index].ptr;
495 }
496
497
498 /* Get the string of an element of a list object. */
tclistval2(const TCLIST * list,int index)499 const char *tclistval2(const TCLIST *list, int index){
500 assert(list && index >= 0);
501 if(index >= list->num) return NULL;
502 index += list->start;
503 return list->array[index].ptr;
504 }
505
506
507 /* Add an element at the end of a list object. */
tclistpush(TCLIST * list,const void * ptr,int size)508 void tclistpush(TCLIST *list, const void *ptr, int size){
509 assert(list && ptr && size >= 0);
510 int index = list->start + list->num;
511 if(index >= list->anum){
512 list->anum += list->num + 1;
513 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
514 }
515 TCLISTDATUM *array = list->array;
516 TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
517 memcpy(array[index].ptr, ptr, size);
518 array[index].ptr[size] = '\0';
519 array[index].size = size;
520 list->num++;
521 }
522
523
524 /* Add a string element at the end of a list object. */
tclistpush2(TCLIST * list,const char * str)525 void tclistpush2(TCLIST *list, const char *str){
526 assert(list && str);
527 int index = list->start + list->num;
528 if(index >= list->anum){
529 list->anum += list->num + 1;
530 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
531 }
532 int size = strlen(str);
533 TCLISTDATUM *array = list->array;
534 TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
535 memcpy(array[index].ptr, str, size + 1);
536 array[index].size = size;
537 list->num++;
538 }
539
540
541 /* Remove an element of the end of a list object. */
tclistpop(TCLIST * list,int * sp)542 void *tclistpop(TCLIST *list, int *sp){
543 assert(list && sp);
544 if(list->num < 1) return NULL;
545 int index = list->start + list->num - 1;
546 list->num--;
547 *sp = list->array[index].size;
548 return list->array[index].ptr;
549 }
550
551
552 /* Remove a string element of the end of a list object. */
tclistpop2(TCLIST * list)553 char *tclistpop2(TCLIST *list){
554 assert(list);
555 if(list->num < 1) return NULL;
556 int index = list->start + list->num - 1;
557 list->num--;
558 return list->array[index].ptr;
559 }
560
561
562 /* Add an element at the top of a list object. */
tclistunshift(TCLIST * list,const void * ptr,int size)563 void tclistunshift(TCLIST *list, const void *ptr, int size){
564 assert(list && ptr && size >= 0);
565 if(list->start < 1){
566 if(list->start + list->num >= list->anum){
567 list->anum += list->num + 1;
568 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
569 }
570 list->start = list->anum - list->num;
571 memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
572 }
573 int index = list->start - 1;
574 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
575 memcpy(list->array[index].ptr, ptr, size);
576 list->array[index].ptr[size] = '\0';
577 list->array[index].size = size;
578 list->start--;
579 list->num++;
580 }
581
582
583 /* Add a string element at the top of a list object. */
tclistunshift2(TCLIST * list,const char * str)584 void tclistunshift2(TCLIST *list, const char *str){
585 assert(list && str);
586 if(list->start < 1){
587 if(list->start + list->num >= list->anum){
588 list->anum += list->num + 1;
589 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
590 }
591 list->start = list->anum - list->num;
592 memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
593 }
594 int index = list->start - 1;
595 int size = strlen(str);
596 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
597 memcpy(list->array[index].ptr, str, size + 1);
598 list->array[index].size = size;
599 list->start--;
600 list->num++;
601 }
602
603
604 /* Remove an element of the top of a list object. */
tclistshift(TCLIST * list,int * sp)605 void *tclistshift(TCLIST *list, int *sp){
606 assert(list && sp);
607 if(list->num < 1) return NULL;
608 int index = list->start;
609 list->start++;
610 list->num--;
611 *sp = list->array[index].size;
612 void *rv = list->array[index].ptr;
613 if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
614 memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
615 list->start = 0;
616 }
617 return rv;
618 }
619
620
621 /* Remove a string element of the top of a list object. */
tclistshift2(TCLIST * list)622 char *tclistshift2(TCLIST *list){
623 assert(list);
624 if(list->num < 1) return NULL;
625 int index = list->start;
626 list->start++;
627 list->num--;
628 void *rv = list->array[index].ptr;
629 if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
630 memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
631 list->start = 0;
632 }
633 return rv;
634 }
635
636
637 /* Add an element at the specified location of a list object. */
tclistinsert(TCLIST * list,int index,const void * ptr,int size)638 void tclistinsert(TCLIST *list, int index, const void *ptr, int size){
639 assert(list && index >= 0 && ptr && size >= 0);
640 if(index > list->num) return;
641 index += list->start;
642 if(list->start + list->num >= list->anum){
643 list->anum += list->num + 1;
644 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
645 }
646 memmove(list->array + index + 1, list->array + index,
647 sizeof(list->array[0]) * (list->start + list->num - index));
648 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
649 memcpy(list->array[index].ptr, ptr, size);
650 list->array[index].ptr[size] = '\0';
651 list->array[index].size = size;
652 list->num++;
653 }
654
655
656 /* Add a string element at the specified location of a list object. */
tclistinsert2(TCLIST * list,int index,const char * str)657 void tclistinsert2(TCLIST *list, int index, const char *str){
658 assert(list && index >= 0 && str);
659 if(index > list->num) return;
660 index += list->start;
661 if(list->start + list->num >= list->anum){
662 list->anum += list->num + 1;
663 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
664 }
665 memmove(list->array + index + 1, list->array + index,
666 sizeof(list->array[0]) * (list->start + list->num - index));
667 int size = strlen(str);
668 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
669 memcpy(list->array[index].ptr, str, size);
670 list->array[index].ptr[size] = '\0';
671 list->array[index].size = size;
672 list->num++;
673 }
674
675
676 /* Remove an element at the specified location of a list object. */
tclistremove(TCLIST * list,int index,int * sp)677 void *tclistremove(TCLIST *list, int index, int *sp){
678 assert(list && index >= 0 && sp);
679 if(index >= list->num) return NULL;
680 index += list->start;
681 void *rv = list->array[index].ptr;
682 *sp = list->array[index].size;
683 list->num--;
684 memmove(list->array + index, list->array + index + 1,
685 sizeof(list->array[0]) * (list->start + list->num - index));
686 return rv;
687 }
688
689
690 /* Remove a string element at the specified location of a list object. */
tclistremove2(TCLIST * list,int index)691 char *tclistremove2(TCLIST *list, int index){
692 assert(list && index >= 0);
693 if(index >= list->num) return NULL;
694 index += list->start;
695 void *rv = list->array[index].ptr;
696 list->num--;
697 memmove(list->array + index, list->array + index + 1,
698 sizeof(list->array[0]) * (list->start + list->num - index));
699 return rv;
700 }
701
702
703 /* Overwrite an element at the specified location of a list object. */
tclistover(TCLIST * list,int index,const void * ptr,int size)704 void tclistover(TCLIST *list, int index, const void *ptr, int size){
705 assert(list && index >= 0 && ptr && size >= 0);
706 if(index >= list->num) return;
707 index += list->start;
708 if(size > list->array[index].size)
709 TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
710 memcpy(list->array[index].ptr, ptr, size);
711 list->array[index].size = size;
712 list->array[index].ptr[size] = '\0';
713 }
714
715
716 /* Overwrite a string element at the specified location of a list object. */
tclistover2(TCLIST * list,int index,const char * str)717 void tclistover2(TCLIST *list, int index, const char *str){
718 assert(list && index >= 0 && str);
719 if(index >= list->num) return;
720 index += list->start;
721 int size = strlen(str);
722 if(size > list->array[index].size)
723 TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
724 memcpy(list->array[index].ptr, str, size + 1);
725 list->array[index].size = size;
726 }
727
728
729 /* Sort elements of a list object in lexical order. */
tclistsort(TCLIST * list)730 void tclistsort(TCLIST *list){
731 assert(list);
732 qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmp);
733 }
734
735
736 /* Search a list object for an element using liner search. */
tclistlsearch(const TCLIST * list,const void * ptr,int size)737 int tclistlsearch(const TCLIST *list, const void *ptr, int size){
738 assert(list && ptr && size >= 0);
739 int end = list->start + list->num;
740 for(int i = list->start; i < end; i++){
741 if(list->array[i].size == size && !memcmp(list->array[i].ptr, ptr, size))
742 return i - list->start;
743 }
744 return -1;
745 }
746
747
748 /* Search a list object for an element using binary search. */
tclistbsearch(const TCLIST * list,const void * ptr,int size)749 int tclistbsearch(const TCLIST *list, const void *ptr, int size){
750 assert(list && ptr && size >= 0);
751 TCLISTDATUM key;
752 key.ptr = (char *)ptr;
753 key.size = size;
754 TCLISTDATUM *res = bsearch(&key, list->array + list->start,
755 list->num, sizeof(list->array[0]), tclistelemcmp);
756 return res ? res - list->array - list->start : -1;
757 }
758
759
760 /* Clear a list object. */
tclistclear(TCLIST * list)761 void tclistclear(TCLIST *list){
762 assert(list);
763 TCLISTDATUM *array = list->array;
764 int end = list->start + list->num;
765 for(int i = list->start; i < end; i++){
766 TCFREE(array[i].ptr);
767 }
768 list->start = 0;
769 list->num = 0;
770 }
771
772
773 /* Serialize a list object into a byte array. */
tclistdump(const TCLIST * list,int * sp)774 void *tclistdump(const TCLIST *list, int *sp){
775 assert(list && sp);
776 const TCLISTDATUM *array = list->array;
777 int end = list->start + list->num;
778 int tsiz = 0;
779 for(int i = list->start; i < end; i++){
780 tsiz += array[i].size + sizeof(int);
781 }
782 char *buf;
783 TCMALLOC(buf, tsiz + 1);
784 char *wp = buf;
785 for(int i = list->start; i < end; i++){
786 int step;
787 TCSETVNUMBUF(step, wp, array[i].size);
788 wp += step;
789 memcpy(wp, array[i].ptr, array[i].size);
790 wp += array[i].size;
791 }
792 *sp = wp - buf;
793 return buf;
794 }
795
796
797 /* Create a list object from a serialized byte array. */
tclistload(const void * ptr,int size)798 TCLIST *tclistload(const void *ptr, int size){
799 assert(ptr && size >= 0);
800 TCLIST *list;
801 TCMALLOC(list, sizeof(*list));
802 int anum = size / sizeof(int) + 1;
803 TCLISTDATUM *array;
804 TCMALLOC(array, sizeof(array[0]) * anum);
805 int num = 0;
806 const char *rp = ptr;
807 const char *ep = (char *)ptr + size;
808 while(rp < ep){
809 int step, vsiz;
810 TCREADVNUMBUF(rp, vsiz, step);
811 rp += step;
812 if(num >= anum){
813 anum *= 2;
814 TCREALLOC(array, array, anum * sizeof(array[0]));
815 }
816 TCMALLOC(array[num].ptr, tclmax(vsiz + 1, TCXSTRUNIT));
817 memcpy(array[num].ptr, rp, vsiz);
818 array[num].ptr[vsiz] = '\0';
819 array[num].size = vsiz;
820 num++;
821 rp += vsiz;
822 }
823 list->anum = anum;
824 list->array = array;
825 list->start = 0;
826 list->num = num;
827 return list;
828 }
829
830
831 /* Compare two list elements in lexical order.
832 `a' specifies the pointer to one element.
833 `b' specifies the pointer to the other element.
834 The return value is positive if the former is big, negative if the latter is big, 0 if both
835 are equivalent. */
tclistelemcmp(const void * a,const void * b)836 static int tclistelemcmp(const void *a, const void *b){
837 assert(a && b);
838 unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr;
839 unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr;
840 int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ?
841 ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size;
842 for(int i = 0; i < size; i++){
843 if(ao[i] > bo[i]) return 1;
844 if(ao[i] < bo[i]) return -1;
845 }
846 return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size;
847 }
848
849
850 /* Compare two list elements in case-insensitive lexical order..
851 `a' specifies the pointer to one element.
852 `b' specifies the pointer to the other element.
853 The return value is positive if the former is big, negative if the latter is big, 0 if both
854 are equivalent. */
tclistelemcmpci(const void * a,const void * b)855 static int tclistelemcmpci(const void *a, const void *b){
856 assert(a && b);
857 TCLISTDATUM *ap = (TCLISTDATUM *)a;
858 TCLISTDATUM *bp = (TCLISTDATUM *)b;
859 unsigned char *ao = (unsigned char *)ap->ptr;
860 unsigned char *bo = (unsigned char *)bp->ptr;
861 int size = (ap->size < bp->size) ? ap->size : bp->size;
862 for(int i = 0; i < size; i++){
863 int ac = ao[i];
864 bool ab = false;
865 if(ac >= 'A' && ac <= 'Z'){
866 ac += 'a' - 'A';
867 ab = true;
868 }
869 int bc = bo[i];
870 bool bb = false;
871 if(bc >= 'A' && bc <= 'Z'){
872 bc += 'a' - 'A';
873 bb = true;
874 }
875 if(ac > bc) return 1;
876 if(ac < bc) return -1;
877 if(!ab && bb) return 1;
878 if(ab && !bb) return -1;
879 }
880 return ap->size - bp->size;
881 }
882
883
884
885 /*************************************************************************************************
886 * array list (for experts)
887 *************************************************************************************************/
888
889
890 /* Add an allocated element at the end of a list object. */
tclistpushmalloc(TCLIST * list,void * ptr,int size)891 void tclistpushmalloc(TCLIST *list, void *ptr, int size){
892 assert(list && ptr && size >= 0);
893 int index = list->start + list->num;
894 if(index >= list->anum){
895 list->anum += list->num + 1;
896 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
897 }
898 TCLISTDATUM *array = list->array;
899 TCREALLOC(array[index].ptr, ptr, size + 1);
900 array[index].ptr[size] = '\0';
901 array[index].size = size;
902 list->num++;
903 }
904
905
906 /* Sort elements of a list object in case-insensitive lexical order. */
tclistsortci(TCLIST * list)907 void tclistsortci(TCLIST *list){
908 assert(list);
909 qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmpci);
910 }
911
912
913 /* Sort elements of a list object by an arbitrary comparison function. */
tclistsortex(TCLIST * list,int (* cmp)(const TCLISTDATUM *,const TCLISTDATUM *))914 void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *)){
915 assert(list && cmp);
916 qsort(list->array + list->start, list->num, sizeof(list->array[0]),
917 (int (*)(const void *, const void *))cmp);
918 }
919
920
921 /* Invert elements of a list object. */
tclistinvert(TCLIST * list)922 void tclistinvert(TCLIST *list){
923 assert(list);
924 TCLISTDATUM *top = list->array + list->start;
925 TCLISTDATUM *bot = top + list->num - 1;
926 while(top < bot){
927 TCLISTDATUM swap = *top;
928 *top = *bot;
929 *bot = swap;
930 top++;
931 bot--;
932 }
933 }
934
935
936 /* Perform formatted output into a list object. */
tclistprintf(TCLIST * list,const char * format,...)937 void tclistprintf(TCLIST *list, const char *format, ...){
938 assert(list && format);
939 TCXSTR *xstr = tcxstrnew();
940 va_list ap;
941 va_start(ap, format);
942 tcvxstrprintf(xstr, format, ap);
943 va_end(ap);
944 int size = TCXSTRSIZE(xstr);
945 char *ptr = tcxstrtomalloc(xstr);
946 tclistpushmalloc(list, ptr, size);
947 }
948
949
950
951 /*************************************************************************************************
952 * hash map
953 *************************************************************************************************/
954
955
956 #define TCMAPKMAXSIZ 0xfffff // maximum size of each key
957 #define TCMAPDEFBNUM 4093 // default bucket number
958 #define TCMAPZMMINSIZ 131072 // minimum memory size to use nullified region
959 #define TCMAPCSUNIT 52 // small allocation unit size of map concatenation
960 #define TCMAPCBUNIT 252 // big allocation unit size of map concatenation
961 #define TCMAPTINYBNUM 31 // bucket number of a tiny map
962
963 /* get the first hash value */
964 #define TCMAPHASH1(TC_res, TC_kbuf, TC_ksiz) \
965 do { \
966 const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf); \
967 int _TC_ksiz = TC_ksiz; \
968 for((TC_res) = 19780211; _TC_ksiz--;){ \
969 (TC_res) = (TC_res) * 37 + *(_TC_p)++; \
970 } \
971 } while(false)
972
973 /* get the second hash value */
974 #define TCMAPHASH2(TC_res, TC_kbuf, TC_ksiz) \
975 do { \
976 const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
977 int _TC_ksiz = TC_ksiz; \
978 for((TC_res) = 0x13579bdf; _TC_ksiz--;){ \
979 (TC_res) = (TC_res) * 31 + *(_TC_p)--; \
980 } \
981 } while(false)
982
983 /* compare two keys */
984 #define TCKEYCMP(TC_abuf, TC_asiz, TC_bbuf, TC_bsiz) \
985 ((TC_asiz > TC_bsiz) ? 1 : (TC_asiz < TC_bsiz) ? -1 : memcmp(TC_abuf, TC_bbuf, TC_asiz))
986
987
988 /* Create a map object. */
tcmapnew(void)989 TCMAP *tcmapnew(void){
990 return tcmapnew2(TCMAPDEFBNUM);
991 }
992
993
994 /* Create a map object with specifying the number of the buckets. */
tcmapnew2(uint32_t bnum)995 TCMAP *tcmapnew2(uint32_t bnum){
996 if(bnum < 1) bnum = 1;
997 TCMAP *map;
998 TCMALLOC(map, sizeof(*map));
999 TCMAPREC **buckets;
1000 if(bnum >= TCMAPZMMINSIZ / sizeof(*buckets)){
1001 buckets = tczeromap(bnum * sizeof(*buckets));
1002 } else {
1003 TCCALLOC(buckets, bnum, sizeof(*buckets));
1004 }
1005 map->buckets = buckets;
1006 map->first = NULL;
1007 map->last = NULL;
1008 map->cur = NULL;
1009 map->bnum = bnum;
1010 map->rnum = 0;
1011 map->msiz = 0;
1012 return map;
1013 }
1014
1015
1016 /* Create a map object with initial string elements. */
tcmapnew3(const char * str,...)1017 TCMAP *tcmapnew3(const char *str, ...){
1018 TCMAP *map = tcmapnew2(TCMAPTINYBNUM);
1019 if(str){
1020 va_list ap;
1021 va_start(ap, str);
1022 const char *key = str;
1023 const char *elem;
1024 while((elem = va_arg(ap, char *)) != NULL){
1025 if(key){
1026 tcmapput2(map, key, elem);
1027 key = NULL;
1028 } else {
1029 key = elem;
1030 }
1031 }
1032 va_end(ap);
1033 }
1034 return map;
1035 }
1036
1037
1038 /* Copy a map object. */
tcmapdup(const TCMAP * map)1039 TCMAP *tcmapdup(const TCMAP *map){
1040 assert(map);
1041 TCMAP *nmap = tcmapnew2(tclmax(tclmax(map->bnum, map->rnum), TCMAPDEFBNUM));
1042 TCMAPREC *rec = map->first;
1043 while(rec){
1044 char *dbuf = (char *)rec + sizeof(*rec);
1045 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1046 tcmapput(nmap, dbuf, rksiz, dbuf + rksiz + TCALIGNPAD(rksiz), rec->vsiz);
1047 rec = rec->next;
1048 }
1049 return nmap;
1050 }
1051
1052
1053 /* Close a map object. */
tcmapdel(TCMAP * map)1054 void tcmapdel(TCMAP *map){
1055 assert(map);
1056 TCMAPREC *rec = map->first;
1057 while(rec){
1058 TCMAPREC *next = rec->next;
1059 TCFREE(rec);
1060 rec = next;
1061 }
1062 if(map->bnum >= TCMAPZMMINSIZ / sizeof(map->buckets[0])){
1063 tczerounmap(map->buckets);
1064 } else {
1065 TCFREE(map->buckets);
1066 }
1067 TCFREE(map);
1068 }
1069
1070
1071 /* Store a record into a map object. */
tcmapput(TCMAP * map,const void * kbuf,int ksiz,const void * vbuf,int vsiz)1072 void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1073 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1074 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1075 uint32_t hash;
1076 TCMAPHASH1(hash, kbuf, ksiz);
1077 int bidx = hash % map->bnum;
1078 TCMAPREC *rec = map->buckets[bidx];
1079 TCMAPREC **entp = map->buckets + bidx;
1080 TCMAPHASH2(hash, kbuf, ksiz);
1081 hash &= ~TCMAPKMAXSIZ;
1082 while(rec){
1083 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1084 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1085 if(hash > rhash){
1086 entp = &(rec->left);
1087 rec = rec->left;
1088 } else if(hash < rhash){
1089 entp = &(rec->right);
1090 rec = rec->right;
1091 } else {
1092 char *dbuf = (char *)rec + sizeof(*rec);
1093 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1094 if(kcmp < 0){
1095 entp = &(rec->left);
1096 rec = rec->left;
1097 } else if(kcmp > 0){
1098 entp = &(rec->right);
1099 rec = rec->right;
1100 } else {
1101 map->msiz += vsiz - rec->vsiz;
1102 int psiz = TCALIGNPAD(ksiz);
1103 if(vsiz > rec->vsiz){
1104 TCMAPREC *old = rec;
1105 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1106 if(rec != old){
1107 if(map->first == old) map->first = rec;
1108 if(map->last == old) map->last = rec;
1109 if(map->cur == old) map->cur = rec;
1110 *entp = rec;
1111 if(rec->prev) rec->prev->next = rec;
1112 if(rec->next) rec->next->prev = rec;
1113 dbuf = (char *)rec + sizeof(*rec);
1114 }
1115 }
1116 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1117 dbuf[ksiz+psiz+vsiz] = '\0';
1118 rec->vsiz = vsiz;
1119 return;
1120 }
1121 }
1122 }
1123 int psiz = TCALIGNPAD(ksiz);
1124 map->msiz += ksiz + vsiz;
1125 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1126 char *dbuf = (char *)rec + sizeof(*rec);
1127 memcpy(dbuf, kbuf, ksiz);
1128 dbuf[ksiz] = '\0';
1129 rec->ksiz = ksiz | hash;
1130 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1131 dbuf[ksiz+psiz+vsiz] = '\0';
1132 rec->vsiz = vsiz;
1133 rec->left = NULL;
1134 rec->right = NULL;
1135 rec->prev = map->last;
1136 rec->next = NULL;
1137 *entp = rec;
1138 if(!map->first) map->first = rec;
1139 if(map->last) map->last->next = rec;
1140 map->last = rec;
1141 map->rnum++;
1142 }
1143
1144
1145 /* Store a string record into a map object. */
tcmapput2(TCMAP * map,const char * kstr,const char * vstr)1146 void tcmapput2(TCMAP *map, const char *kstr, const char *vstr){
1147 assert(map && kstr && vstr);
1148 tcmapput(map, kstr, strlen(kstr), vstr, strlen(vstr));
1149 }
1150
1151
1152 /* Store a new record into a map object. */
tcmapputkeep(TCMAP * map,const void * kbuf,int ksiz,const void * vbuf,int vsiz)1153 bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1154 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1155 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1156 uint32_t hash;
1157 TCMAPHASH1(hash, kbuf, ksiz);
1158 int bidx = hash % map->bnum;
1159 TCMAPREC *rec = map->buckets[bidx];
1160 TCMAPREC **entp = map->buckets + bidx;
1161 TCMAPHASH2(hash, kbuf, ksiz);
1162 hash &= ~TCMAPKMAXSIZ;
1163 while(rec){
1164 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1165 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1166 if(hash > rhash){
1167 entp = &(rec->left);
1168 rec = rec->left;
1169 } else if(hash < rhash){
1170 entp = &(rec->right);
1171 rec = rec->right;
1172 } else {
1173 char *dbuf = (char *)rec + sizeof(*rec);
1174 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1175 if(kcmp < 0){
1176 entp = &(rec->left);
1177 rec = rec->left;
1178 } else if(kcmp > 0){
1179 entp = &(rec->right);
1180 rec = rec->right;
1181 } else {
1182 return false;
1183 }
1184 }
1185 }
1186 int psiz = TCALIGNPAD(ksiz);
1187 map->msiz += ksiz + vsiz;
1188 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1189 char *dbuf = (char *)rec + sizeof(*rec);
1190 memcpy(dbuf, kbuf, ksiz);
1191 dbuf[ksiz] = '\0';
1192 rec->ksiz = ksiz | hash;
1193 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1194 dbuf[ksiz+psiz+vsiz] = '\0';
1195 rec->vsiz = vsiz;
1196 rec->left = NULL;
1197 rec->right = NULL;
1198 rec->prev = map->last;
1199 rec->next = NULL;
1200 *entp = rec;
1201 if(!map->first) map->first = rec;
1202 if(map->last) map->last->next = rec;
1203 map->last = rec;
1204 map->rnum++;
1205 return true;
1206 }
1207
1208
1209 /* Store a new string record into a map object. */
tcmapputkeep2(TCMAP * map,const char * kstr,const char * vstr)1210 bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr){
1211 assert(map && kstr && vstr);
1212 return tcmapputkeep(map, kstr, strlen(kstr), vstr, strlen(vstr));
1213 }
1214
1215
1216 /* Concatenate a value at the end of the value of the existing record in a map object. */
tcmapputcat(TCMAP * map,const void * kbuf,int ksiz,const void * vbuf,int vsiz)1217 void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1218 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1219 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1220 uint32_t hash;
1221 TCMAPHASH1(hash, kbuf, ksiz);
1222 int bidx = hash % map->bnum;
1223 TCMAPREC *rec = map->buckets[bidx];
1224 TCMAPREC **entp = map->buckets + bidx;
1225 TCMAPHASH2(hash, kbuf, ksiz);
1226 hash &= ~TCMAPKMAXSIZ;
1227 while(rec){
1228 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1229 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1230 if(hash > rhash){
1231 entp = &(rec->left);
1232 rec = rec->left;
1233 } else if(hash < rhash){
1234 entp = &(rec->right);
1235 rec = rec->right;
1236 } else {
1237 char *dbuf = (char *)rec + sizeof(*rec);
1238 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1239 if(kcmp < 0){
1240 entp = &(rec->left);
1241 rec = rec->left;
1242 } else if(kcmp > 0){
1243 entp = &(rec->right);
1244 rec = rec->right;
1245 } else {
1246 map->msiz += vsiz;
1247 int psiz = TCALIGNPAD(ksiz);
1248 int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1;
1249 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
1250 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
1251 TCMAPREC *old = rec;
1252 TCREALLOC(rec, rec, asiz);
1253 if(rec != old){
1254 if(map->first == old) map->first = rec;
1255 if(map->last == old) map->last = rec;
1256 if(map->cur == old) map->cur = rec;
1257 *entp = rec;
1258 if(rec->prev) rec->prev->next = rec;
1259 if(rec->next) rec->next->prev = rec;
1260 dbuf = (char *)rec + sizeof(*rec);
1261 }
1262 memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz);
1263 rec->vsiz += vsiz;
1264 dbuf[ksiz+psiz+rec->vsiz] = '\0';
1265 return;
1266 }
1267 }
1268 }
1269 int psiz = TCALIGNPAD(ksiz);
1270 int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1;
1271 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
1272 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
1273 map->msiz += ksiz + vsiz;
1274 TCMALLOC(rec, asiz);
1275 char *dbuf = (char *)rec + sizeof(*rec);
1276 memcpy(dbuf, kbuf, ksiz);
1277 dbuf[ksiz] = '\0';
1278 rec->ksiz = ksiz | hash;
1279 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1280 dbuf[ksiz+psiz+vsiz] = '\0';
1281 rec->vsiz = vsiz;
1282 rec->left = NULL;
1283 rec->right = NULL;
1284 rec->prev = map->last;
1285 rec->next = NULL;
1286 *entp = rec;
1287 if(!map->first) map->first = rec;
1288 if(map->last) map->last->next = rec;
1289 map->last = rec;
1290 map->rnum++;
1291 }
1292
1293
1294 /* Concatenate a string value at the end of the value of the existing record in a map object. */
tcmapputcat2(TCMAP * map,const char * kstr,const char * vstr)1295 void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr){
1296 assert(map && kstr && vstr);
1297 tcmapputcat(map, kstr, strlen(kstr), vstr, strlen(vstr));
1298 }
1299
1300
1301 /* Remove a record of a map object. */
tcmapout(TCMAP * map,const void * kbuf,int ksiz)1302 bool tcmapout(TCMAP *map, const void *kbuf, int ksiz){
1303 assert(map && kbuf && ksiz >= 0);
1304 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1305 uint32_t hash;
1306 TCMAPHASH1(hash, kbuf, ksiz);
1307 int bidx = hash % map->bnum;
1308 TCMAPREC *rec = map->buckets[bidx];
1309 TCMAPREC **entp = map->buckets + bidx;
1310 TCMAPHASH2(hash, kbuf, ksiz);
1311 hash &= ~TCMAPKMAXSIZ;
1312 while(rec){
1313 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1314 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1315 if(hash > rhash){
1316 entp = &(rec->left);
1317 rec = rec->left;
1318 } else if(hash < rhash){
1319 entp = &(rec->right);
1320 rec = rec->right;
1321 } else {
1322 char *dbuf = (char *)rec + sizeof(*rec);
1323 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1324 if(kcmp < 0){
1325 entp = &(rec->left);
1326 rec = rec->left;
1327 } else if(kcmp > 0){
1328 entp = &(rec->right);
1329 rec = rec->right;
1330 } else {
1331 map->rnum--;
1332 map->msiz -= rksiz + rec->vsiz;
1333 if(rec->prev) rec->prev->next = rec->next;
1334 if(rec->next) rec->next->prev = rec->prev;
1335 if(rec == map->first) map->first = rec->next;
1336 if(rec == map->last) map->last = rec->prev;
1337 if(rec == map->cur) map->cur = rec->next;
1338 if(rec->left && !rec->right){
1339 *entp = rec->left;
1340 } else if(!rec->left && rec->right){
1341 *entp = rec->right;
1342 } else if(!rec->left){
1343 *entp = NULL;
1344 } else {
1345 *entp = rec->left;
1346 TCMAPREC *tmp = *entp;
1347 while(tmp->right){
1348 tmp = tmp->right;
1349 }
1350 tmp->right = rec->right;
1351 }
1352 TCFREE(rec);
1353 return true;
1354 }
1355 }
1356 }
1357 return false;
1358 }
1359
1360
1361 /* Remove a string record of a map object. */
tcmapout2(TCMAP * map,const char * kstr)1362 bool tcmapout2(TCMAP *map, const char *kstr){
1363 assert(map && kstr);
1364 return tcmapout(map, kstr, strlen(kstr));
1365 }
1366
1367
1368 /* Retrieve a record in a map object. */
tcmapget(const TCMAP * map,const void * kbuf,int ksiz,int * sp)1369 const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp){
1370 assert(map && kbuf && ksiz >= 0 && sp);
1371 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1372 uint32_t hash;
1373 TCMAPHASH1(hash, kbuf, ksiz);
1374 TCMAPREC *rec = map->buckets[hash%map->bnum];
1375 TCMAPHASH2(hash, kbuf, ksiz);
1376 hash &= ~TCMAPKMAXSIZ;
1377 while(rec){
1378 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1379 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1380 if(hash > rhash){
1381 rec = rec->left;
1382 } else if(hash < rhash){
1383 rec = rec->right;
1384 } else {
1385 char *dbuf = (char *)rec + sizeof(*rec);
1386 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1387 if(kcmp < 0){
1388 rec = rec->left;
1389 } else if(kcmp > 0){
1390 rec = rec->right;
1391 } else {
1392 *sp = rec->vsiz;
1393 return dbuf + rksiz + TCALIGNPAD(rksiz);
1394 }
1395 }
1396 }
1397 return NULL;
1398 }
1399
1400
1401 /* Retrieve a string record in a map object. */
tcmapget2(const TCMAP * map,const char * kstr)1402 const char *tcmapget2(const TCMAP *map, const char *kstr){
1403 assert(map && kstr);
1404 int ksiz = strlen(kstr);
1405 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1406 uint32_t hash;
1407 TCMAPHASH1(hash, kstr, ksiz);
1408 TCMAPREC *rec = map->buckets[hash%map->bnum];
1409 TCMAPHASH2(hash, kstr, ksiz);
1410 hash &= ~TCMAPKMAXSIZ;
1411 while(rec){
1412 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1413 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1414 if(hash > rhash){
1415 rec = rec->left;
1416 } else if(hash < rhash){
1417 rec = rec->right;
1418 } else {
1419 char *dbuf = (char *)rec + sizeof(*rec);
1420 int kcmp = TCKEYCMP(kstr, ksiz, dbuf, rksiz);
1421 if(kcmp < 0){
1422 rec = rec->left;
1423 } else if(kcmp > 0){
1424 rec = rec->right;
1425 } else {
1426 return dbuf + rksiz + TCALIGNPAD(rksiz);
1427 }
1428 }
1429 }
1430 return NULL;
1431 }
1432
1433
1434 /* Move a record to the edge of a map object. */
tcmapmove(TCMAP * map,const void * kbuf,int ksiz,bool head)1435 bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head){
1436 assert(map && kbuf && ksiz >= 0);
1437 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1438 uint32_t hash;
1439 TCMAPHASH1(hash, kbuf, ksiz);
1440 TCMAPREC *rec = map->buckets[hash%map->bnum];
1441 TCMAPHASH2(hash, kbuf, ksiz);
1442 hash &= ~TCMAPKMAXSIZ;
1443 while(rec){
1444 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1445 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1446 if(hash > rhash){
1447 rec = rec->left;
1448 } else if(hash < rhash){
1449 rec = rec->right;
1450 } else {
1451 char *dbuf = (char *)rec + sizeof(*rec);
1452 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1453 if(kcmp < 0){
1454 rec = rec->left;
1455 } else if(kcmp > 0){
1456 rec = rec->right;
1457 } else {
1458 if(head){
1459 if(map->first == rec) return true;
1460 if(map->last == rec) map->last = rec->prev;
1461 if(rec->prev) rec->prev->next = rec->next;
1462 if(rec->next) rec->next->prev = rec->prev;
1463 rec->prev = NULL;
1464 rec->next = map->first;
1465 map->first->prev = rec;
1466 map->first = rec;
1467 } else {
1468 if(map->last == rec) return true;
1469 if(map->first == rec) map->first = rec->next;
1470 if(rec->prev) rec->prev->next = rec->next;
1471 if(rec->next) rec->next->prev = rec->prev;
1472 rec->prev = map->last;
1473 rec->next = NULL;
1474 map->last->next = rec;
1475 map->last = rec;
1476 }
1477 return true;
1478 }
1479 }
1480 }
1481 return false;
1482 }
1483
1484
1485 /* Move a string record to the edge of a map object. */
tcmapmove2(TCMAP * map,const char * kstr,bool head)1486 bool tcmapmove2(TCMAP *map, const char *kstr, bool head){
1487 assert(map && kstr);
1488 return tcmapmove(map, kstr, strlen(kstr), head);
1489 }
1490
1491
1492 /* Initialize the iterator of a map object. */
tcmapiterinit(TCMAP * map)1493 void tcmapiterinit(TCMAP *map){
1494 assert(map);
1495 map->cur = map->first;
1496 }
1497
1498
1499 /* Get the next key of the iterator of a map object. */
tcmapiternext(TCMAP * map,int * sp)1500 const void *tcmapiternext(TCMAP *map, int *sp){
1501 assert(map && sp);
1502 TCMAPREC *rec;
1503 if(!map->cur) return NULL;
1504 rec = map->cur;
1505 map->cur = rec->next;
1506 *sp = rec->ksiz & TCMAPKMAXSIZ;
1507 return (char *)rec + sizeof(*rec);
1508 }
1509
1510
1511 /* Get the next key string of the iterator of a map object. */
tcmapiternext2(TCMAP * map)1512 const char *tcmapiternext2(TCMAP *map){
1513 assert(map);
1514 TCMAPREC *rec;
1515 if(!map->cur) return NULL;
1516 rec = map->cur;
1517 map->cur = rec->next;
1518 return (char *)rec + sizeof(*rec);
1519 }
1520
1521
1522 /* Get the number of records stored in a map object. */
tcmaprnum(const TCMAP * map)1523 uint64_t tcmaprnum(const TCMAP *map){
1524 assert(map);
1525 return map->rnum;
1526 }
1527
1528
1529 /* Get the total size of memory used in a map object. */
tcmapmsiz(const TCMAP * map)1530 uint64_t tcmapmsiz(const TCMAP *map){
1531 assert(map);
1532 return map->msiz + map->rnum * (sizeof(*map->first) + sizeof(tcgeneric_t)) +
1533 map->bnum * sizeof(void *);
1534 }
1535
1536
1537 /* Create a list object containing all keys in a map object. */
tcmapkeys(const TCMAP * map)1538 TCLIST *tcmapkeys(const TCMAP *map){
1539 assert(map);
1540 TCLIST *list = tclistnew2(map->rnum);
1541 TCMAPREC *rec = map->first;
1542 while(rec){
1543 char *dbuf = (char *)rec + sizeof(*rec);
1544 TCLISTPUSH(list, dbuf, rec->ksiz & TCMAPKMAXSIZ);
1545 rec = rec->next;
1546 }
1547 return list;
1548 }
1549
1550
1551 /* Create a list object containing all values in a map object. */
tcmapvals(const TCMAP * map)1552 TCLIST *tcmapvals(const TCMAP *map){
1553 assert(map);
1554 TCLIST *list = tclistnew2(map->rnum);
1555 TCMAPREC *rec = map->first;
1556 while(rec){
1557 char *dbuf = (char *)rec + sizeof(*rec);
1558 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1559 TCLISTPUSH(list, dbuf + rksiz + TCALIGNPAD(rksiz), rec->vsiz);
1560 rec = rec->next;
1561 }
1562 return list;
1563 }
1564
1565
1566 /* Add an integer to a record in a map object. */
tcmapaddint(TCMAP * map,const void * kbuf,int ksiz,int num)1567 int tcmapaddint(TCMAP *map, const void *kbuf, int ksiz, int num){
1568 assert(map && kbuf && ksiz >= 0);
1569 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1570 uint32_t hash;
1571 TCMAPHASH1(hash, kbuf, ksiz);
1572 int bidx = hash % map->bnum;
1573 TCMAPREC *rec = map->buckets[bidx];
1574 TCMAPREC **entp = map->buckets + bidx;
1575 TCMAPHASH2(hash, kbuf, ksiz);
1576 hash &= ~TCMAPKMAXSIZ;
1577 while(rec){
1578 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1579 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1580 if(hash > rhash){
1581 entp = &(rec->left);
1582 rec = rec->left;
1583 } else if(hash < rhash){
1584 entp = &(rec->right);
1585 rec = rec->right;
1586 } else {
1587 char *dbuf = (char *)rec + sizeof(*rec);
1588 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1589 if(kcmp < 0){
1590 entp = &(rec->left);
1591 rec = rec->left;
1592 } else if(kcmp > 0){
1593 entp = &(rec->right);
1594 rec = rec->right;
1595 } else {
1596 if(rec->vsiz != sizeof(num)) return INT_MIN;
1597 int *resp = (int *)(dbuf + ksiz + TCALIGNPAD(ksiz));
1598 return *resp += num;
1599 }
1600 }
1601 }
1602 int psiz = TCALIGNPAD(ksiz);
1603 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
1604 char *dbuf = (char *)rec + sizeof(*rec);
1605 memcpy(dbuf, kbuf, ksiz);
1606 dbuf[ksiz] = '\0';
1607 rec->ksiz = ksiz | hash;
1608 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
1609 dbuf[ksiz+psiz+sizeof(num)] = '\0';
1610 rec->vsiz = sizeof(num);
1611 rec->left = NULL;
1612 rec->right = NULL;
1613 rec->prev = map->last;
1614 rec->next = NULL;
1615 *entp = rec;
1616 if(!map->first) map->first = rec;
1617 if(map->last) map->last->next = rec;
1618 map->last = rec;
1619 map->rnum++;
1620 return num;
1621 }
1622
1623
1624 /* Add a real number to a record in a map object. */
tcmapadddouble(TCMAP * map,const void * kbuf,int ksiz,double num)1625 double tcmapadddouble(TCMAP *map, const void *kbuf, int ksiz, double num){
1626 assert(map && kbuf && ksiz >= 0);
1627 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1628 uint32_t hash;
1629 TCMAPHASH1(hash, kbuf, ksiz);
1630 int bidx = hash % map->bnum;
1631 TCMAPREC *rec = map->buckets[bidx];
1632 TCMAPREC **entp = map->buckets + bidx;
1633 TCMAPHASH2(hash, kbuf, ksiz);
1634 hash &= ~TCMAPKMAXSIZ;
1635 while(rec){
1636 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1637 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1638 if(hash > rhash){
1639 entp = &(rec->left);
1640 rec = rec->left;
1641 } else if(hash < rhash){
1642 entp = &(rec->right);
1643 rec = rec->right;
1644 } else {
1645 char *dbuf = (char *)rec + sizeof(*rec);
1646 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1647 if(kcmp < 0){
1648 entp = &(rec->left);
1649 rec = rec->left;
1650 } else if(kcmp > 0){
1651 entp = &(rec->right);
1652 rec = rec->right;
1653 } else {
1654 if(rec->vsiz != sizeof(num)) return nan("");
1655 double *resp = (double *)(dbuf + ksiz + TCALIGNPAD(ksiz));
1656 return *resp += num;
1657 }
1658 }
1659 }
1660 int psiz = TCALIGNPAD(ksiz);
1661 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
1662 char *dbuf = (char *)rec + sizeof(*rec);
1663 memcpy(dbuf, kbuf, ksiz);
1664 dbuf[ksiz] = '\0';
1665 rec->ksiz = ksiz | hash;
1666 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
1667 dbuf[ksiz+psiz+sizeof(num)] = '\0';
1668 rec->vsiz = sizeof(num);
1669 rec->left = NULL;
1670 rec->right = NULL;
1671 rec->prev = map->last;
1672 rec->next = NULL;
1673 *entp = rec;
1674 if(!map->first) map->first = rec;
1675 if(map->last) map->last->next = rec;
1676 map->last = rec;
1677 map->rnum++;
1678 return num;
1679 }
1680
1681
1682 /* Clear a map object. */
tcmapclear(TCMAP * map)1683 void tcmapclear(TCMAP *map){
1684 assert(map);
1685 TCMAPREC *rec = map->first;
1686 while(rec){
1687 TCMAPREC *next = rec->next;
1688 TCFREE(rec);
1689 rec = next;
1690 }
1691 TCMAPREC **buckets = map->buckets;
1692 int bnum = map->bnum;
1693 for(int i = 0; i < bnum; i++){
1694 buckets[i] = NULL;
1695 }
1696 map->first = NULL;
1697 map->last = NULL;
1698 map->cur = NULL;
1699 map->rnum = 0;
1700 map->msiz = 0;
1701 }
1702
1703
1704 /* Remove front records of a map object. */
tcmapcutfront(TCMAP * map,int num)1705 void tcmapcutfront(TCMAP *map, int num){
1706 assert(map && num >= 0);
1707 tcmapiterinit(map);
1708 while(num-- > 0){
1709 int ksiz;
1710 const char *kbuf = tcmapiternext(map, &ksiz);
1711 if(!kbuf) break;
1712 tcmapout(map, kbuf, ksiz);
1713 }
1714 }
1715
1716
1717 /* Serialize a map object into a byte array. */
tcmapdump(const TCMAP * map,int * sp)1718 void *tcmapdump(const TCMAP *map, int *sp){
1719 assert(map && sp);
1720 int tsiz = 0;
1721 TCMAPREC *rec = map->first;
1722 while(rec){
1723 tsiz += (rec->ksiz & TCMAPKMAXSIZ) + rec->vsiz + sizeof(int) * 2;
1724 rec = rec->next;
1725 }
1726 char *buf;
1727 TCMALLOC(buf, tsiz + 1);
1728 char *wp = buf;
1729 rec = map->first;
1730 while(rec){
1731 const char *kbuf = (char *)rec + sizeof(*rec);
1732 int ksiz = rec->ksiz & TCMAPKMAXSIZ;
1733 const char *vbuf = kbuf + ksiz + TCALIGNPAD(ksiz);
1734 int vsiz = rec->vsiz;
1735 int step;
1736 TCSETVNUMBUF(step, wp, ksiz);
1737 wp += step;
1738 memcpy(wp, kbuf, ksiz);
1739 wp += ksiz;
1740 TCSETVNUMBUF(step, wp, vsiz);
1741 wp += step;
1742 memcpy(wp, vbuf, vsiz);
1743 wp += vsiz;
1744 rec = rec->next;
1745 }
1746 *sp = wp - buf;
1747 return buf;
1748 }
1749
1750
1751 /* Create a map object from a serialized byte array. */
tcmapload(const void * ptr,int size)1752 TCMAP *tcmapload(const void *ptr, int size){
1753 assert(ptr && size >= 0);
1754 TCMAP *map = tcmapnew2(tclmin(size / 6 + 1, TCMAPDEFBNUM));
1755 const char *rp = ptr;
1756 const char *ep = (char *)ptr + size;
1757 while(rp < ep){
1758 int step, ksiz, vsiz;
1759 TCREADVNUMBUF(rp, ksiz, step);
1760 rp += step;
1761 const char *kbuf = rp;
1762 rp += ksiz;
1763 TCREADVNUMBUF(rp, vsiz, step);
1764 rp += step;
1765 tcmapputkeep(map, kbuf, ksiz, rp, vsiz);
1766 rp += vsiz;
1767 }
1768 return map;
1769 }
1770
1771
1772
1773 /*************************************************************************************************
1774 * hash map (for experts)
1775 *************************************************************************************************/
1776
1777
1778 /* Store a record and make it semivolatile in a map object. */
tcmapput3(TCMAP * map,const void * kbuf,int ksiz,const char * vbuf,int vsiz)1779 void tcmapput3(TCMAP *map, const void *kbuf, int ksiz, const char *vbuf, int vsiz){
1780 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1781 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1782 uint32_t hash;
1783 TCMAPHASH1(hash, kbuf, ksiz);
1784 int bidx = hash % map->bnum;
1785 TCMAPREC *rec = map->buckets[bidx];
1786 TCMAPREC **entp = map->buckets + bidx;
1787 TCMAPHASH2(hash, kbuf, ksiz);
1788 hash &= ~TCMAPKMAXSIZ;
1789 while(rec){
1790 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1791 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1792 if(hash > rhash){
1793 entp = &(rec->left);
1794 rec = rec->left;
1795 } else if(hash < rhash){
1796 entp = &(rec->right);
1797 rec = rec->right;
1798 } else {
1799 char *dbuf = (char *)rec + sizeof(*rec);
1800 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1801 if(kcmp < 0){
1802 entp = &(rec->left);
1803 rec = rec->left;
1804 } else if(kcmp > 0){
1805 entp = &(rec->right);
1806 rec = rec->right;
1807 } else {
1808 map->msiz += vsiz - rec->vsiz;
1809 int psiz = TCALIGNPAD(ksiz);
1810 if(vsiz > rec->vsiz){
1811 TCMAPREC *old = rec;
1812 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1813 if(rec != old){
1814 if(map->first == old) map->first = rec;
1815 if(map->last == old) map->last = rec;
1816 if(map->cur == old) map->cur = rec;
1817 *entp = rec;
1818 if(rec->prev) rec->prev->next = rec;
1819 if(rec->next) rec->next->prev = rec;
1820 dbuf = (char *)rec + sizeof(*rec);
1821 }
1822 }
1823 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1824 dbuf[ksiz+psiz+vsiz] = '\0';
1825 rec->vsiz = vsiz;
1826 if(map->last != rec){
1827 if(map->first == rec) map->first = rec->next;
1828 if(rec->prev) rec->prev->next = rec->next;
1829 if(rec->next) rec->next->prev = rec->prev;
1830 rec->prev = map->last;
1831 rec->next = NULL;
1832 map->last->next = rec;
1833 map->last = rec;
1834 }
1835 return;
1836 }
1837 }
1838 }
1839 int psiz = TCALIGNPAD(ksiz);
1840 map->msiz += ksiz + vsiz;
1841 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1842 char *dbuf = (char *)rec + sizeof(*rec);
1843 memcpy(dbuf, kbuf, ksiz);
1844 dbuf[ksiz] = '\0';
1845 rec->ksiz = ksiz | hash;
1846 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1847 dbuf[ksiz+psiz+vsiz] = '\0';
1848 rec->vsiz = vsiz;
1849 rec->left = NULL;
1850 rec->right = NULL;
1851 rec->prev = map->last;
1852 rec->next = NULL;
1853 *entp = rec;
1854 if(!map->first) map->first = rec;
1855 if(map->last) map->last->next = rec;
1856 map->last = rec;
1857 map->rnum++;
1858 }
1859
1860
1861 /* Store a record of the value of two regions into a map object. */
tcmapput4(TCMAP * map,const void * kbuf,int ksiz,const void * fvbuf,int fvsiz,const void * lvbuf,int lvsiz)1862 void tcmapput4(TCMAP *map, const void *kbuf, int ksiz,
1863 const void *fvbuf, int fvsiz, const void *lvbuf, int lvsiz){
1864 assert(map && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
1865 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1866 uint32_t hash;
1867 TCMAPHASH1(hash, kbuf, ksiz);
1868 int bidx = hash % map->bnum;
1869 TCMAPREC *rec = map->buckets[bidx];
1870 TCMAPREC **entp = map->buckets + bidx;
1871 TCMAPHASH2(hash, kbuf, ksiz);
1872 hash &= ~TCMAPKMAXSIZ;
1873 while(rec){
1874 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1875 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1876 if(hash > rhash){
1877 entp = &(rec->left);
1878 rec = rec->left;
1879 } else if(hash < rhash){
1880 entp = &(rec->right);
1881 rec = rec->right;
1882 } else {
1883 char *dbuf = (char *)rec + sizeof(*rec);
1884 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1885 if(kcmp < 0){
1886 entp = &(rec->left);
1887 rec = rec->left;
1888 } else if(kcmp > 0){
1889 entp = &(rec->right);
1890 rec = rec->right;
1891 } else {
1892 int vsiz = fvsiz + lvsiz;
1893 map->msiz += vsiz - rec->vsiz;
1894 int psiz = TCALIGNPAD(ksiz);
1895 ksiz += psiz;
1896 if(vsiz > rec->vsiz){
1897 TCMAPREC *old = rec;
1898 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + vsiz + 1);
1899 if(rec != old){
1900 if(map->first == old) map->first = rec;
1901 if(map->last == old) map->last = rec;
1902 if(map->cur == old) map->cur = rec;
1903 *entp = rec;
1904 if(rec->prev) rec->prev->next = rec;
1905 if(rec->next) rec->next->prev = rec;
1906 dbuf = (char *)rec + sizeof(*rec);
1907 }
1908 }
1909 memcpy(dbuf + ksiz, fvbuf, fvsiz);
1910 memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
1911 dbuf[ksiz+vsiz] = '\0';
1912 rec->vsiz = vsiz;
1913 return;
1914 }
1915 }
1916 }
1917 int vsiz = fvsiz + lvsiz;
1918 int psiz = TCALIGNPAD(ksiz);
1919 map->msiz += ksiz + vsiz;
1920 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1921 char *dbuf = (char *)rec + sizeof(*rec);
1922 memcpy(dbuf, kbuf, ksiz);
1923 dbuf[ksiz] = '\0';
1924 rec->ksiz = ksiz | hash;
1925 ksiz += psiz;
1926 memcpy(dbuf + ksiz, fvbuf, fvsiz);
1927 memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
1928 dbuf[ksiz+vsiz] = '\0';
1929 rec->vsiz = vsiz;
1930 rec->left = NULL;
1931 rec->right = NULL;
1932 rec->prev = map->last;
1933 rec->next = NULL;
1934 *entp = rec;
1935 if(!map->first) map->first = rec;
1936 if(map->last) map->last->next = rec;
1937 map->last = rec;
1938 map->rnum++;
1939 }
1940
1941
1942 /* Concatenate a value at the existing record and make it semivolatile in a map object. */
tcmapputcat3(TCMAP * map,const void * kbuf,int ksiz,const void * vbuf,int vsiz)1943 void tcmapputcat3(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1944 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1945 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
1946 uint32_t hash;
1947 TCMAPHASH1(hash, kbuf, ksiz);
1948 int bidx = hash % map->bnum;
1949 TCMAPREC *rec = map->buckets[bidx];
1950 TCMAPREC **entp = map->buckets + bidx;
1951 TCMAPHASH2(hash, kbuf, ksiz);
1952 hash &= ~TCMAPKMAXSIZ;
1953 while(rec){
1954 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
1955 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
1956 if(hash > rhash){
1957 entp = &(rec->left);
1958 rec = rec->left;
1959 } else if(hash < rhash){
1960 entp = &(rec->right);
1961 rec = rec->right;
1962 } else {
1963 char *dbuf = (char *)rec + sizeof(*rec);
1964 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
1965 if(kcmp < 0){
1966 entp = &(rec->left);
1967 rec = rec->left;
1968 } else if(kcmp > 0){
1969 entp = &(rec->right);
1970 rec = rec->right;
1971 } else {
1972 map->msiz += vsiz;
1973 int psiz = TCALIGNPAD(ksiz);
1974 int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1;
1975 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
1976 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
1977 TCMAPREC *old = rec;
1978 TCREALLOC(rec, rec, asiz);
1979 if(rec != old){
1980 if(map->first == old) map->first = rec;
1981 if(map->last == old) map->last = rec;
1982 if(map->cur == old) map->cur = rec;
1983 *entp = rec;
1984 if(rec->prev) rec->prev->next = rec;
1985 if(rec->next) rec->next->prev = rec;
1986 dbuf = (char *)rec + sizeof(*rec);
1987 }
1988 memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz);
1989 rec->vsiz += vsiz;
1990 dbuf[ksiz+psiz+rec->vsiz] = '\0';
1991 if(map->last != rec){
1992 if(map->first == rec) map->first = rec->next;
1993 if(rec->prev) rec->prev->next = rec->next;
1994 if(rec->next) rec->next->prev = rec->prev;
1995 rec->prev = map->last;
1996 rec->next = NULL;
1997 map->last->next = rec;
1998 map->last = rec;
1999 }
2000 return;
2001 }
2002 }
2003 }
2004 int psiz = TCALIGNPAD(ksiz);
2005 int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1;
2006 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
2007 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
2008 map->msiz += ksiz + vsiz;
2009 TCMALLOC(rec, asiz);
2010 char *dbuf = (char *)rec + sizeof(*rec);
2011 memcpy(dbuf, kbuf, ksiz);
2012 dbuf[ksiz] = '\0';
2013 rec->ksiz = ksiz | hash;
2014 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2015 dbuf[ksiz+psiz+vsiz] = '\0';
2016 rec->vsiz = vsiz;
2017 rec->left = NULL;
2018 rec->right = NULL;
2019 rec->prev = map->last;
2020 rec->next = NULL;
2021 *entp = rec;
2022 if(!map->first) map->first = rec;
2023 if(map->last) map->last->next = rec;
2024 map->last = rec;
2025 map->rnum++;
2026 }
2027
2028
2029 /* Store a record into a map object with a duplication handler. */
tcmapputproc(TCMAP * map,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)2030 bool tcmapputproc(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
2031 TCPDPROC proc, void *op){
2032 assert(map && kbuf && ksiz >= 0 && proc);
2033 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
2034 uint32_t hash;
2035 TCMAPHASH1(hash, kbuf, ksiz);
2036 int bidx = hash % map->bnum;
2037 TCMAPREC *rec = map->buckets[bidx];
2038 TCMAPREC **entp = map->buckets + bidx;
2039 TCMAPHASH2(hash, kbuf, ksiz);
2040 hash &= ~TCMAPKMAXSIZ;
2041 while(rec){
2042 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
2043 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
2044 if(hash > rhash){
2045 entp = &(rec->left);
2046 rec = rec->left;
2047 } else if(hash < rhash){
2048 entp = &(rec->right);
2049 rec = rec->right;
2050 } else {
2051 char *dbuf = (char *)rec + sizeof(*rec);
2052 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
2053 if(kcmp < 0){
2054 entp = &(rec->left);
2055 rec = rec->left;
2056 } else if(kcmp > 0){
2057 entp = &(rec->right);
2058 rec = rec->right;
2059 } else {
2060 int psiz = TCALIGNPAD(ksiz);
2061 int nvsiz;
2062 char *nvbuf = proc(dbuf + ksiz + psiz, rec->vsiz, &nvsiz, op);
2063 if(nvbuf == (void *)-1){
2064 map->rnum--;
2065 map->msiz -= rksiz + rec->vsiz;
2066 if(rec->prev) rec->prev->next = rec->next;
2067 if(rec->next) rec->next->prev = rec->prev;
2068 if(rec == map->first) map->first = rec->next;
2069 if(rec == map->last) map->last = rec->prev;
2070 if(rec == map->cur) map->cur = rec->next;
2071 if(rec->left && !rec->right){
2072 *entp = rec->left;
2073 } else if(!rec->left && rec->right){
2074 *entp = rec->right;
2075 } else if(!rec->left && !rec->left){
2076 *entp = NULL;
2077 } else {
2078 *entp = rec->left;
2079 TCMAPREC *tmp = *entp;
2080 while(tmp->right){
2081 tmp = tmp->right;
2082 }
2083 tmp->right = rec->right;
2084 }
2085 TCFREE(rec);
2086 return true;
2087 }
2088 if(!nvbuf) return false;
2089 map->msiz += nvsiz - rec->vsiz;
2090 if(nvsiz > rec->vsiz){
2091 TCMAPREC *old = rec;
2092 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + nvsiz + 1);
2093 if(rec != old){
2094 if(map->first == old) map->first = rec;
2095 if(map->last == old) map->last = rec;
2096 if(map->cur == old) map->cur = rec;
2097 *entp = rec;
2098 if(rec->prev) rec->prev->next = rec;
2099 if(rec->next) rec->next->prev = rec;
2100 dbuf = (char *)rec + sizeof(*rec);
2101 }
2102 }
2103 memcpy(dbuf + ksiz + psiz, nvbuf, nvsiz);
2104 dbuf[ksiz+psiz+nvsiz] = '\0';
2105 rec->vsiz = nvsiz;
2106 TCFREE(nvbuf);
2107 return true;
2108 }
2109 }
2110 }
2111 if(!vbuf) return false;
2112 int psiz = TCALIGNPAD(ksiz);
2113 int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1;
2114 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
2115 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
2116 map->msiz += ksiz + vsiz;
2117 TCMALLOC(rec, asiz);
2118 char *dbuf = (char *)rec + sizeof(*rec);
2119 memcpy(dbuf, kbuf, ksiz);
2120 dbuf[ksiz] = '\0';
2121 rec->ksiz = ksiz | hash;
2122 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2123 dbuf[ksiz+psiz+vsiz] = '\0';
2124 rec->vsiz = vsiz;
2125 rec->left = NULL;
2126 rec->right = NULL;
2127 rec->prev = map->last;
2128 rec->next = NULL;
2129 *entp = rec;
2130 if(!map->first) map->first = rec;
2131 if(map->last) map->last->next = rec;
2132 map->last = rec;
2133 map->rnum++;
2134 return true;
2135 }
2136
2137
2138 /* Retrieve a semivolatile record in a map object. */
tcmapget3(TCMAP * map,const void * kbuf,int ksiz,int * sp)2139 const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp){
2140 assert(map && kbuf && ksiz >= 0 && sp);
2141 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
2142 uint32_t hash;
2143 TCMAPHASH1(hash, kbuf, ksiz);
2144 TCMAPREC *rec = map->buckets[hash%map->bnum];
2145 TCMAPHASH2(hash, kbuf, ksiz);
2146 hash &= ~TCMAPKMAXSIZ;
2147 while(rec){
2148 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
2149 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
2150 if(hash > rhash){
2151 rec = rec->left;
2152 } else if(hash < rhash){
2153 rec = rec->right;
2154 } else {
2155 char *dbuf = (char *)rec + sizeof(*rec);
2156 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
2157 if(kcmp < 0){
2158 rec = rec->left;
2159 } else if(kcmp > 0){
2160 rec = rec->right;
2161 } else {
2162 if(map->last != rec){
2163 if(map->first == rec) map->first = rec->next;
2164 if(rec->prev) rec->prev->next = rec->next;
2165 if(rec->next) rec->next->prev = rec->prev;
2166 rec->prev = map->last;
2167 rec->next = NULL;
2168 map->last->next = rec;
2169 map->last = rec;
2170 }
2171 *sp = rec->vsiz;
2172 return dbuf + rksiz + TCALIGNPAD(rksiz);
2173 }
2174 }
2175 }
2176 return NULL;
2177 }
2178
2179
2180 /* Retrieve a string record in a map object with specifying the default value string. */
tcmapget4(TCMAP * map,const char * kstr,const char * dstr)2181 const char *tcmapget4(TCMAP *map, const char *kstr, const char *dstr){
2182 assert(map && kstr && dstr);
2183 int vsiz;
2184 const char *vbuf = tcmapget(map, kstr, strlen(kstr), &vsiz);
2185 return vbuf ? vbuf : dstr;
2186 }
2187
2188
2189 /* Initialize the iterator of a map object at the record corresponding a key. */
tcmapiterinit2(TCMAP * map,const void * kbuf,int ksiz)2190 void tcmapiterinit2(TCMAP *map, const void *kbuf, int ksiz){
2191 assert(map && kbuf && ksiz >= 0);
2192 if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ;
2193 uint32_t hash;
2194 TCMAPHASH1(hash, kbuf, ksiz);
2195 TCMAPREC *rec = map->buckets[hash%map->bnum];
2196 TCMAPHASH2(hash, kbuf, ksiz);
2197 hash &= ~TCMAPKMAXSIZ;
2198 while(rec){
2199 uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ;
2200 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
2201 if(hash > rhash){
2202 rec = rec->left;
2203 } else if(hash < rhash){
2204 rec = rec->right;
2205 } else {
2206 char *dbuf = (char *)rec + sizeof(*rec);
2207 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz);
2208 if(kcmp < 0){
2209 rec = rec->left;
2210 } else if(kcmp > 0){
2211 rec = rec->right;
2212 } else {
2213 map->cur = rec;
2214 return;
2215 }
2216 }
2217 }
2218 }
2219
2220
2221 /* Initialize the iterator of a map object at the record corresponding a key string. */
tcmapiterinit3(TCMAP * map,const char * kstr)2222 void tcmapiterinit3(TCMAP *map, const char *kstr){
2223 assert(map && kstr);
2224 tcmapiterinit2(map, kstr, strlen(kstr));
2225 }
2226
2227
2228 /* Get the value bound to the key fetched from the iterator of a map object. */
tcmapiterval(const void * kbuf,int * sp)2229 const void *tcmapiterval(const void *kbuf, int *sp){
2230 assert(kbuf && sp);
2231 TCMAPREC *rec = (TCMAPREC *)((char *)kbuf - sizeof(*rec));
2232 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
2233 *sp = rec->vsiz;
2234 return (char *)kbuf + rksiz + TCALIGNPAD(rksiz);
2235 }
2236
2237
2238 /* Get the value string bound to the key fetched from the iterator of a map object. */
tcmapiterval2(const char * kstr)2239 const char *tcmapiterval2(const char *kstr){
2240 assert(kstr);
2241 TCMAPREC *rec = (TCMAPREC *)(kstr - sizeof(*rec));
2242 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
2243 return kstr + rksiz + TCALIGNPAD(rksiz);
2244 }
2245
2246
2247 /* Create an array of strings of all keys in a map object. */
tcmapkeys2(const TCMAP * map,int * np)2248 const char **tcmapkeys2(const TCMAP *map, int *np){
2249 assert(map && np);
2250 const char **ary;
2251 TCMALLOC(ary, sizeof(*ary) * map->rnum + 1);
2252 int anum = 0;
2253 TCMAPREC *rec = map->first;
2254 while(rec){
2255 ary[(anum++)] = (char *)rec + sizeof(*rec);
2256 rec = rec->next;
2257 }
2258 *np = anum;
2259 return ary;
2260 }
2261
2262
2263 /* Create an array of strings of all values in a map object. */
tcmapvals2(const TCMAP * map,int * np)2264 const char **tcmapvals2(const TCMAP *map, int *np){
2265 assert(map && np);
2266 const char **ary;
2267 TCMALLOC(ary, sizeof(*ary) * map->rnum + 1);
2268 int anum = 0;
2269 TCMAPREC *rec = map->first;
2270 while(rec){
2271 uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ;
2272 ary[(anum++)] = (char *)rec + sizeof(*rec) + rksiz + TCALIGNPAD(rksiz);
2273 rec = rec->next;
2274 }
2275 *np = anum;
2276 return ary;
2277 }
2278
2279
2280 /* Extract a map record from a serialized byte array. */
tcmaploadone(const void * ptr,int size,const void * kbuf,int ksiz,int * sp)2281 void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){
2282 assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp);
2283 const char *rp = ptr;
2284 const char *ep = (char *)ptr + size;
2285 while(rp < ep){
2286 int step, rsiz;
2287 TCREADVNUMBUF(rp, rsiz, step);
2288 rp += step;
2289 if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){
2290 rp += rsiz;
2291 TCREADVNUMBUF(rp, rsiz, step);
2292 rp += step;
2293 *sp = rsiz;
2294 char *rv;
2295 TCMEMDUP(rv, rp, rsiz);
2296 return rv;
2297 }
2298 rp += rsiz;
2299 TCREADVNUMBUF(rp, rsiz, step);
2300 rp += step;
2301 rp += rsiz;
2302 }
2303 return NULL;
2304 }
2305
2306
2307 /* Perform formatted output into a map object. */
tcmapprintf(TCMAP * map,const char * kstr,const char * format,...)2308 void tcmapprintf(TCMAP *map, const char *kstr, const char *format, ...){
2309 assert(map && kstr && format);
2310 TCXSTR *xstr = tcxstrnew();
2311 va_list ap;
2312 va_start(ap, format);
2313 tcvxstrprintf(xstr, format, ap);
2314 va_end(ap);
2315 tcmapput(map, kstr, strlen(kstr), TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
2316 tcxstrdel(xstr);
2317 }
2318
2319
2320
2321 /*************************************************************************************************
2322 * ordered tree
2323 *************************************************************************************************/
2324
2325
2326 #define TREESTACKNUM 2048 // capacity of the stack of ordered tree
2327 #define TCTREECSUNIT 52 // small allocation unit size of map concatenation
2328 #define TCTREECBUNIT 252 // big allocation unit size of map concatenation
2329
2330
2331 /* private function prototypes */
2332 static TCTREEREC* tctreesplay(TCTREE *tree, const void *kbuf, int ksiz);
2333
2334
2335 /* Create a tree object. */
tctreenew(void)2336 TCTREE *tctreenew(void){
2337 return tctreenew2(tccmplexical, NULL);
2338 }
2339
2340
2341 /* Create a tree object with specifying the custom comparison function. */
tctreenew2(TCCMP cmp,void * cmpop)2342 TCTREE *tctreenew2(TCCMP cmp, void *cmpop){
2343 assert(cmp);
2344 TCTREE *tree;
2345 TCMALLOC(tree, sizeof(*tree));
2346 tree->root = NULL;
2347 tree->cur = NULL;
2348 tree->rnum = 0;
2349 tree->msiz = 0;
2350 tree->cmp = cmp;
2351 tree->cmpop = cmpop;
2352 return tree;
2353 }
2354
2355
2356 /* Copy a tree object. */
tctreedup(const TCTREE * tree)2357 TCTREE *tctreedup(const TCTREE *tree){
2358 assert(tree);
2359 TCTREE *ntree = tctreenew2(tree->cmp, tree->cmpop);
2360 if(tree->root){
2361 TCTREEREC *histbuf[TREESTACKNUM];
2362 TCTREEREC **history = histbuf;
2363 int hnum = 0;
2364 history[hnum++] = tree->root;
2365 while(hnum > 0){
2366 TCTREEREC *rec = history[--hnum];
2367 if(hnum >= TREESTACKNUM - 2 && history == histbuf){
2368 TCMALLOC(history, sizeof(*history) * tree->rnum);
2369 memcpy(history, histbuf, sizeof(*history) * hnum);
2370 }
2371 if(rec->left) history[hnum++] = rec->left;
2372 if(rec->right) history[hnum++] = rec->right;
2373 char *dbuf = (char *)rec + sizeof(*rec);
2374 tctreeput(ntree, dbuf, rec->ksiz, dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz);
2375 }
2376 if(history != histbuf) TCFREE(history);
2377 }
2378 return ntree;
2379 }
2380
2381
2382 /* Delete a tree object. */
tctreedel(TCTREE * tree)2383 void tctreedel(TCTREE *tree){
2384 assert(tree);
2385 if(tree->root){
2386 TCTREEREC *histbuf[TREESTACKNUM];
2387 TCTREEREC **history = histbuf;
2388 int hnum = 0;
2389 history[hnum++] = tree->root;
2390 while(hnum > 0){
2391 TCTREEREC *rec = history[--hnum];
2392 if(hnum >= TREESTACKNUM - 2 && history == histbuf){
2393 TCMALLOC(history, sizeof(*history) * tree->rnum);
2394 memcpy(history, histbuf, sizeof(*history) * hnum);
2395 }
2396 if(rec->left) history[hnum++] = rec->left;
2397 if(rec->right) history[hnum++] = rec->right;
2398 TCFREE(rec);
2399 }
2400 if(history != histbuf) TCFREE(history);
2401 }
2402 TCFREE(tree);
2403 }
2404
2405
2406 /* Store a record into a tree object. */
tctreeput(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz)2407 void tctreeput(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
2408 assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
2409 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2410 if(!top){
2411 int psiz = TCALIGNPAD(ksiz);
2412 TCTREEREC *rec;
2413 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2414 char *dbuf = (char *)rec + sizeof(*rec);
2415 memcpy(dbuf, kbuf, ksiz);
2416 dbuf[ksiz] = '\0';
2417 rec->ksiz = ksiz;
2418 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2419 dbuf[ksiz+psiz+vsiz] = '\0';
2420 rec->vsiz = vsiz;
2421 rec->left = NULL;
2422 rec->right = NULL;
2423 tree->root = rec;
2424 tree->rnum = 1;
2425 tree->msiz = ksiz + vsiz;
2426 return;
2427 }
2428 char *dbuf = (char *)top + sizeof(*top);
2429 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2430 if(cv < 0){
2431 int psiz = TCALIGNPAD(ksiz);
2432 TCTREEREC *rec;
2433 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2434 dbuf = (char *)rec + sizeof(*rec);
2435 memcpy(dbuf, kbuf, ksiz);
2436 dbuf[ksiz] = '\0';
2437 rec->ksiz = ksiz;
2438 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2439 dbuf[ksiz+psiz+vsiz] = '\0';
2440 rec->vsiz = vsiz;
2441 rec->left = top->left;
2442 rec->right = top;
2443 top->left = NULL;
2444 tree->rnum++;
2445 tree->msiz += ksiz + vsiz;
2446 tree->root = rec;
2447 } else if(cv > 0){
2448 int psiz = TCALIGNPAD(ksiz);
2449 TCTREEREC *rec;
2450 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2451 dbuf = (char *)rec + sizeof(*rec);
2452 memcpy(dbuf, kbuf, ksiz);
2453 dbuf[ksiz] = '\0';
2454 rec->ksiz = ksiz;
2455 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2456 dbuf[ksiz+psiz+vsiz] = '\0';
2457 rec->vsiz = vsiz;
2458 rec->left = top;
2459 rec->right = top->right;
2460 top->right = NULL;
2461 tree->rnum++;
2462 tree->msiz += ksiz + vsiz;
2463 tree->root = rec;
2464 } else {
2465 tree->msiz += vsiz - top->vsiz;
2466 int psiz = TCALIGNPAD(ksiz);
2467 if(vsiz > top->vsiz){
2468 TCTREEREC *old = top;
2469 TCREALLOC(top, top, sizeof(*top) + ksiz + psiz + vsiz + 1);
2470 if(top != old){
2471 if(tree->cur == old) tree->cur = top;
2472 dbuf = (char *)top + sizeof(*top);
2473 }
2474 }
2475 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2476 dbuf[ksiz+psiz+vsiz] = '\0';
2477 top->vsiz = vsiz;
2478 tree->root = top;
2479 }
2480 }
2481
2482
2483 /* Store a string record into a tree object. */
tctreeput2(TCTREE * tree,const char * kstr,const char * vstr)2484 void tctreeput2(TCTREE *tree, const char *kstr, const char *vstr){
2485 assert(tree && kstr && vstr);
2486 tctreeput(tree, kstr, strlen(kstr), vstr, strlen(vstr));
2487 }
2488
2489
2490 /* Store a new record into a tree object. */
tctreeputkeep(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz)2491 bool tctreeputkeep(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
2492 assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
2493 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2494 if(!top){
2495 int psiz = TCALIGNPAD(ksiz);
2496 TCTREEREC *rec;
2497 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2498 char *dbuf = (char *)rec + sizeof(*rec);
2499 memcpy(dbuf, kbuf, ksiz);
2500 dbuf[ksiz] = '\0';
2501 rec->ksiz = ksiz;
2502 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2503 dbuf[ksiz+psiz+vsiz] = '\0';
2504 rec->vsiz = vsiz;
2505 rec->left = NULL;
2506 rec->right = NULL;
2507 tree->root = rec;
2508 tree->rnum = 1;
2509 tree->msiz = ksiz + vsiz;
2510 return true;
2511 }
2512 char *dbuf = (char *)top + sizeof(*top);
2513 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2514 if(cv < 0){
2515 int psiz = TCALIGNPAD(ksiz);
2516 TCTREEREC *rec;
2517 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2518 dbuf = (char *)rec + sizeof(*rec);
2519 memcpy(dbuf, kbuf, ksiz);
2520 dbuf[ksiz] = '\0';
2521 rec->ksiz = ksiz;
2522 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2523 dbuf[ksiz+psiz+vsiz] = '\0';
2524 rec->vsiz = vsiz;
2525 rec->left = top->left;
2526 rec->right = top;
2527 top->left = NULL;
2528 tree->rnum++;
2529 tree->msiz += ksiz + vsiz;
2530 tree->root = rec;
2531 } else if(cv > 0){
2532 int psiz = TCALIGNPAD(ksiz);
2533 TCTREEREC *rec;
2534 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2535 dbuf = (char *)rec + sizeof(*rec);
2536 memcpy(dbuf, kbuf, ksiz);
2537 dbuf[ksiz] = '\0';
2538 rec->ksiz = ksiz;
2539 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2540 dbuf[ksiz+psiz+vsiz] = '\0';
2541 rec->vsiz = vsiz;
2542 rec->left = top;
2543 rec->right = top->right;
2544 top->right = NULL;
2545 tree->rnum++;
2546 tree->msiz += ksiz + vsiz;
2547 tree->root = rec;
2548 } else {
2549 tree->root = top;
2550 return false;
2551 }
2552 return true;
2553 }
2554
2555
2556 /* Store a new string record into a tree object. */
tctreeputkeep2(TCTREE * tree,const char * kstr,const char * vstr)2557 bool tctreeputkeep2(TCTREE *tree, const char *kstr, const char *vstr){
2558 assert(tree && kstr && vstr);
2559 return tctreeputkeep(tree, kstr, strlen(kstr), vstr, strlen(vstr));
2560 }
2561
2562
2563 /* Concatenate a value at the end of the value of the existing record in a tree object. */
tctreeputcat(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz)2564 void tctreeputcat(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
2565 assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
2566 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2567 if(!top){
2568 int psiz = TCALIGNPAD(ksiz);
2569 TCTREEREC *rec;
2570 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2571 char *dbuf = (char *)rec + sizeof(*rec);
2572 memcpy(dbuf, kbuf, ksiz);
2573 dbuf[ksiz] = '\0';
2574 rec->ksiz = ksiz;
2575 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2576 dbuf[ksiz+psiz+vsiz] = '\0';
2577 rec->vsiz = vsiz;
2578 rec->left = NULL;
2579 rec->right = NULL;
2580 tree->root = rec;
2581 tree->rnum = 1;
2582 tree->msiz = ksiz + vsiz;
2583 return;
2584 }
2585 char *dbuf = (char *)top + sizeof(*top);
2586 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2587 if(cv < 0){
2588 int psiz = TCALIGNPAD(ksiz);
2589 TCTREEREC *rec;
2590 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2591 dbuf = (char *)rec + sizeof(*rec);
2592 memcpy(dbuf, kbuf, ksiz);
2593 dbuf[ksiz] = '\0';
2594 rec->ksiz = ksiz;
2595 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2596 dbuf[ksiz+psiz+vsiz] = '\0';
2597 rec->vsiz = vsiz;
2598 rec->left = top->left;
2599 rec->right = top;
2600 top->left = NULL;
2601 tree->rnum++;
2602 tree->msiz += ksiz + vsiz;
2603 tree->root = rec;
2604 } else if(cv > 0){
2605 int psiz = TCALIGNPAD(ksiz);
2606 TCTREEREC *rec;
2607 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2608 dbuf = (char *)rec + sizeof(*rec);
2609 memcpy(dbuf, kbuf, ksiz);
2610 dbuf[ksiz] = '\0';
2611 rec->ksiz = ksiz;
2612 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2613 dbuf[ksiz+psiz+vsiz] = '\0';
2614 rec->vsiz = vsiz;
2615 rec->left = top;
2616 rec->right = top->right;
2617 top->right = NULL;
2618 tree->rnum++;
2619 tree->msiz += ksiz + vsiz;
2620 tree->root = rec;
2621 } else {
2622 tree->msiz += vsiz;
2623 int psiz = TCALIGNPAD(ksiz);
2624 int asiz = sizeof(*top) + ksiz + psiz + top->vsiz + vsiz + 1;
2625 int unit = (asiz <= TCTREECSUNIT) ? TCTREECSUNIT : TCTREECBUNIT;
2626 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
2627 TCTREEREC *old = top;
2628 TCREALLOC(top, top, asiz);
2629 if(top != old){
2630 if(tree->cur == old) tree->cur = top;
2631 dbuf = (char *)top + sizeof(*top);
2632 }
2633 memcpy(dbuf + ksiz + psiz + top->vsiz, vbuf, vsiz);
2634 top->vsiz += vsiz;
2635 dbuf[ksiz+psiz+top->vsiz] = '\0';
2636 tree->root = top;
2637 }
2638 }
2639
2640
2641 /* Concatenate a string value at the end of the value of the existing record in a tree object. */
tctreeputcat2(TCTREE * tree,const char * kstr,const char * vstr)2642 void tctreeputcat2(TCTREE *tree, const char *kstr, const char *vstr){
2643 assert(tree && kstr && vstr);
2644 tctreeputcat(tree, kstr, strlen(kstr), vstr, strlen(vstr));
2645 }
2646
2647
2648 /* Store a record into a tree object with a duplication handler. */
tctreeputproc(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)2649 bool tctreeputproc(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
2650 TCPDPROC proc, void *op){
2651 assert(tree && kbuf && ksiz >= 0 && proc);
2652 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2653 if(!top){
2654 if(!vbuf) return false;
2655 int psiz = TCALIGNPAD(ksiz);
2656 TCTREEREC *rec;
2657 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2658 char *dbuf = (char *)rec + sizeof(*rec);
2659 memcpy(dbuf, kbuf, ksiz);
2660 dbuf[ksiz] = '\0';
2661 rec->ksiz = ksiz;
2662 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2663 dbuf[ksiz+psiz+vsiz] = '\0';
2664 rec->vsiz = vsiz;
2665 rec->left = NULL;
2666 rec->right = NULL;
2667 tree->root = rec;
2668 tree->rnum = 1;
2669 tree->msiz = ksiz + vsiz;
2670 return true;
2671 }
2672 char *dbuf = (char *)top + sizeof(*top);
2673 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2674 if(cv < 0){
2675 if(!vbuf){
2676 tree->root = top;
2677 return false;
2678 }
2679 int psiz = TCALIGNPAD(ksiz);
2680 TCTREEREC *rec;
2681 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2682 dbuf = (char *)rec + sizeof(*rec);
2683 memcpy(dbuf, kbuf, ksiz);
2684 dbuf[ksiz] = '\0';
2685 rec->ksiz = ksiz;
2686 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2687 dbuf[ksiz+psiz+vsiz] = '\0';
2688 rec->vsiz = vsiz;
2689 rec->left = top->left;
2690 rec->right = top;
2691 top->left = NULL;
2692 tree->rnum++;
2693 tree->msiz += ksiz + vsiz;
2694 tree->root = rec;
2695 } else if(cv > 0){
2696 if(!vbuf){
2697 tree->root = top;
2698 return false;
2699 }
2700 int psiz = TCALIGNPAD(ksiz);
2701 TCTREEREC *rec;
2702 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
2703 dbuf = (char *)rec + sizeof(*rec);
2704 memcpy(dbuf, kbuf, ksiz);
2705 dbuf[ksiz] = '\0';
2706 rec->ksiz = ksiz;
2707 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
2708 dbuf[ksiz+psiz+vsiz] = '\0';
2709 rec->vsiz = vsiz;
2710 rec->left = top;
2711 rec->right = top->right;
2712 top->right = NULL;
2713 tree->rnum++;
2714 tree->msiz += ksiz + vsiz;
2715 tree->root = rec;
2716 } else {
2717 int psiz = TCALIGNPAD(ksiz);
2718 int nvsiz;
2719 char *nvbuf = proc(dbuf + ksiz + psiz, top->vsiz, &nvsiz, op);
2720 if(nvbuf == (void *)-1){
2721 tree->rnum--;
2722 tree->msiz -= top->ksiz + top->vsiz;
2723 if(tree->cur == top){
2724 TCTREEREC *rec = top->right;
2725 if(rec){
2726 while(rec->left){
2727 rec = rec->left;
2728 }
2729 }
2730 tree->cur = rec;
2731 }
2732 if(!top->left){
2733 tree->root = top->right;
2734 } else if(!top->right){
2735 tree->root = top->left;
2736 } else {
2737 tree->root = top->left;
2738 TCTREEREC *rec = tctreesplay(tree, kbuf, ksiz);
2739 rec->right = top->right;
2740 tree->root = rec;
2741 }
2742 TCFREE(top);
2743 return true;
2744 }
2745 if(!nvbuf){
2746 tree->root = top;
2747 return false;
2748 }
2749 tree->msiz += nvsiz - top->vsiz;
2750 if(nvsiz > top->vsiz){
2751 TCTREEREC *old = top;
2752 TCREALLOC(top, top, sizeof(*top) + ksiz + psiz + nvsiz + 1);
2753 if(top != old){
2754 if(tree->cur == old) tree->cur = top;
2755 dbuf = (char *)top + sizeof(*top);
2756 }
2757 }
2758 memcpy(dbuf + ksiz + psiz, nvbuf, nvsiz);
2759 dbuf[ksiz+psiz+nvsiz] = '\0';
2760 top->vsiz = nvsiz;
2761 TCFREE(nvbuf);
2762 tree->root = top;
2763 }
2764 return true;
2765 }
2766
2767
2768 /* Remove a record of a tree object. */
tctreeout(TCTREE * tree,const void * kbuf,int ksiz)2769 bool tctreeout(TCTREE *tree, const void *kbuf, int ksiz){
2770 assert(tree && kbuf && ksiz >= 0);
2771 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2772 if(!top) return false;
2773 char *dbuf = (char *)top + sizeof(*top);
2774 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2775 if(cv != 0){
2776 tree->root = top;
2777 return false;
2778 }
2779 tree->rnum--;
2780 tree->msiz -= top->ksiz + top->vsiz;
2781 if(tree->cur == top){
2782 TCTREEREC *rec = top->right;
2783 if(rec){
2784 while(rec->left){
2785 rec = rec->left;
2786 }
2787 }
2788 tree->cur = rec;
2789 }
2790 if(!top->left){
2791 tree->root = top->right;
2792 } else if(!top->right){
2793 tree->root = top->left;
2794 } else {
2795 tree->root = top->left;
2796 TCTREEREC *rec = tctreesplay(tree, kbuf, ksiz);
2797 rec->right = top->right;
2798 tree->root = rec;
2799 }
2800 TCFREE(top);
2801 return true;
2802 }
2803
2804
2805 /* Remove a string record of a tree object. */
tctreeout2(TCTREE * tree,const char * kstr)2806 bool tctreeout2(TCTREE *tree, const char *kstr){
2807 assert(tree && kstr);
2808 return tctreeout(tree, kstr, strlen(kstr));
2809 }
2810
2811
2812 /* Retrieve a record in a tree object. */
tctreeget(TCTREE * tree,const void * kbuf,int ksiz,int * sp)2813 const void *tctreeget(TCTREE *tree, const void *kbuf, int ksiz, int *sp){
2814 assert(tree && kbuf && ksiz >= 0 && sp);
2815 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2816 if(!top) return NULL;
2817 char *dbuf = (char *)top + sizeof(*top);
2818 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2819 if(cv != 0){
2820 tree->root = top;
2821 return NULL;
2822 }
2823 tree->root = top;
2824 *sp = top->vsiz;
2825 return dbuf + top->ksiz + TCALIGNPAD(top->ksiz);
2826 }
2827
2828
2829 /* Retrieve a string record in a tree object. */
tctreeget2(TCTREE * tree,const char * kstr)2830 const char *tctreeget2(TCTREE *tree, const char *kstr){
2831 assert(tree && kstr);
2832 int vsiz;
2833 return tctreeget(tree, kstr, strlen(kstr), &vsiz);
2834 }
2835
2836
2837 /* Initialize the iterator of a tree object. */
tctreeiterinit(TCTREE * tree)2838 void tctreeiterinit(TCTREE *tree){
2839 assert(tree);
2840 TCTREEREC *rec = tree->root;
2841 if(!rec) return;
2842 while(rec->left){
2843 rec = rec->left;
2844 }
2845 tree->cur = rec;
2846 }
2847
2848
2849 /* Get the next key of the iterator of a tree object. */
tctreeiternext(TCTREE * tree,int * sp)2850 const void *tctreeiternext(TCTREE *tree, int *sp){
2851 assert(tree && sp);
2852 if(!tree->cur) return NULL;
2853 TCTREEREC *rec = tree->cur;
2854 const char *kbuf = (char *)rec + sizeof(*rec);
2855 int ksiz = rec->ksiz;
2856 rec = tctreesplay(tree, kbuf, ksiz);
2857 if(!rec) return NULL;
2858 tree->root = rec;
2859 rec = rec->right;
2860 if(rec){
2861 while(rec->left){
2862 rec = rec->left;
2863 }
2864 }
2865 tree->cur = rec;
2866 *sp = ksiz;
2867 return kbuf;
2868 }
2869
2870
2871 /* Get the next key string of the iterator of a tree object. */
tctreeiternext2(TCTREE * tree)2872 const char *tctreeiternext2(TCTREE *tree){
2873 assert(tree);
2874 int ksiz;
2875 return tctreeiternext(tree, &ksiz);
2876 }
2877
2878
2879 /* Get the number of records stored in a tree object. */
tctreernum(const TCTREE * tree)2880 uint64_t tctreernum(const TCTREE *tree){
2881 assert(tree);
2882 return tree->rnum;
2883 }
2884
2885
2886 /* Get the total size of memory used in a tree object. */
tctreemsiz(const TCTREE * tree)2887 uint64_t tctreemsiz(const TCTREE *tree){
2888 assert(tree);
2889 return tree->msiz + tree->rnum * (sizeof(*tree->root) + sizeof(tcgeneric_t));
2890 }
2891
2892
2893 /* Create a list object containing all keys in a tree object. */
tctreekeys(const TCTREE * tree)2894 TCLIST *tctreekeys(const TCTREE *tree){
2895 assert(tree);
2896 TCLIST *list = tclistnew2(tree->rnum);
2897 if(tree->root){
2898 TCTREEREC **history;
2899 TCMALLOC(history, sizeof(*history) * tree->rnum);
2900 TCTREEREC **result;
2901 TCMALLOC(result, sizeof(*history) * tree->rnum);
2902 int hnum = 0;
2903 history[hnum++] = tree->root;
2904 while(hnum > 0){
2905 TCTREEREC *rec = history[--hnum];
2906 if(!rec){
2907 rec = result[hnum];
2908 char *dbuf = (char *)rec + sizeof(*rec);
2909 TCLISTPUSH(list, dbuf, rec->ksiz);
2910 continue;
2911 }
2912 if(rec->right) history[hnum++] = rec->right;
2913 history[hnum] = NULL;
2914 result[hnum] = rec;
2915 hnum++;
2916 if(rec->left) history[hnum++] = rec->left;
2917 }
2918 TCFREE(result);
2919 TCFREE(history);
2920 }
2921 return list;
2922 }
2923
2924
2925 /* Create a list object containing all values in a tree object. */
tctreevals(const TCTREE * tree)2926 TCLIST *tctreevals(const TCTREE *tree){
2927 assert(tree);
2928 TCLIST *list = tclistnew2(tree->rnum);
2929 if(tree->root){
2930 TCTREEREC **history;
2931 TCMALLOC(history, sizeof(*history) * tree->rnum);
2932 TCTREEREC **result;
2933 TCMALLOC(result, sizeof(*history) * tree->rnum);
2934 int hnum = 0;
2935 history[hnum++] = tree->root;
2936 while(hnum > 0){
2937 TCTREEREC *rec = history[--hnum];
2938 if(!rec){
2939 rec = result[hnum];
2940 char *dbuf = (char *)rec + sizeof(*rec);
2941 TCLISTPUSH(list, dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz);
2942 continue;
2943 }
2944 if(rec->right) history[hnum++] = rec->right;
2945 history[hnum] = NULL;
2946 result[hnum] = rec;
2947 hnum++;
2948 if(rec->left) history[hnum++] = rec->left;
2949 }
2950 TCFREE(result);
2951 TCFREE(history);
2952 }
2953 return list;
2954 }
2955
2956
2957 /* Add an integer to a record in a tree object. */
tctreeaddint(TCTREE * tree,const void * kbuf,int ksiz,int num)2958 int tctreeaddint(TCTREE *tree, const void *kbuf, int ksiz, int num){
2959 assert(tree && kbuf && ksiz >= 0);
2960 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
2961 if(!top){
2962 int psiz = TCALIGNPAD(ksiz);
2963 TCTREEREC *rec;
2964 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
2965 char *dbuf = (char *)rec + sizeof(*rec);
2966 memcpy(dbuf, kbuf, ksiz);
2967 dbuf[ksiz] = '\0';
2968 rec->ksiz = ksiz;
2969 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
2970 dbuf[ksiz+psiz+sizeof(num)] = '\0';
2971 rec->vsiz = sizeof(num);
2972 rec->left = NULL;
2973 rec->right = NULL;
2974 tree->root = rec;
2975 tree->rnum = 1;
2976 tree->msiz = ksiz + sizeof(num);
2977 return num;
2978 }
2979 char *dbuf = (char *)top + sizeof(*top);
2980 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
2981 if(cv < 0){
2982 int psiz = TCALIGNPAD(ksiz);
2983 TCTREEREC *rec;
2984 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
2985 dbuf = (char *)rec + sizeof(*rec);
2986 memcpy(dbuf, kbuf, ksiz);
2987 dbuf[ksiz] = '\0';
2988 rec->ksiz = ksiz;
2989 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
2990 dbuf[ksiz+psiz+sizeof(num)] = '\0';
2991 rec->vsiz = sizeof(num);
2992 rec->left = top->left;
2993 rec->right = top;
2994 top->left = NULL;
2995 tree->rnum++;
2996 tree->msiz += ksiz + sizeof(num);
2997 tree->root = rec;
2998 } else if(cv > 0){
2999 int psiz = TCALIGNPAD(ksiz);
3000 TCTREEREC *rec;
3001 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
3002 dbuf = (char *)rec + sizeof(*rec);
3003 memcpy(dbuf, kbuf, ksiz);
3004 dbuf[ksiz] = '\0';
3005 rec->ksiz = ksiz;
3006 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
3007 dbuf[ksiz+psiz+sizeof(num)] = '\0';
3008 rec->vsiz = sizeof(num);
3009 rec->left = top;
3010 rec->right = top->right;
3011 top->right = NULL;
3012 tree->rnum++;
3013 tree->msiz += ksiz + sizeof(num);
3014 tree->root = rec;
3015 } else {
3016 tree->root = top;
3017 if(top->vsiz != sizeof(num)) return INT_MIN;
3018 int *resp = (int *)(dbuf + ksiz + TCALIGNPAD(ksiz));
3019 return *resp += num;
3020 }
3021 return num;
3022 }
3023
3024
3025 /* Add a real number to a record in a tree object. */
tctreeadddouble(TCTREE * tree,const void * kbuf,int ksiz,double num)3026 double tctreeadddouble(TCTREE *tree, const void *kbuf, int ksiz, double num){
3027 assert(tree && kbuf && ksiz >= 0);
3028 TCTREEREC *top = tctreesplay(tree, kbuf, ksiz);
3029 if(!top){
3030 int psiz = TCALIGNPAD(ksiz);
3031 TCTREEREC *rec;
3032 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
3033 char *dbuf = (char *)rec + sizeof(*rec);
3034 memcpy(dbuf, kbuf, ksiz);
3035 dbuf[ksiz] = '\0';
3036 rec->ksiz = ksiz;
3037 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
3038 dbuf[ksiz+psiz+sizeof(num)] = '\0';
3039 rec->vsiz = sizeof(num);
3040 rec->left = NULL;
3041 rec->right = NULL;
3042 tree->root = rec;
3043 tree->rnum = 1;
3044 tree->msiz = ksiz + sizeof(num);
3045 return num;
3046 }
3047 char *dbuf = (char *)top + sizeof(*top);
3048 int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop);
3049 if(cv < 0){
3050 int psiz = TCALIGNPAD(ksiz);
3051 TCTREEREC *rec;
3052 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
3053 dbuf = (char *)rec + sizeof(*rec);
3054 memcpy(dbuf, kbuf, ksiz);
3055 dbuf[ksiz] = '\0';
3056 rec->ksiz = ksiz;
3057 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
3058 dbuf[ksiz+psiz+sizeof(num)] = '\0';
3059 rec->vsiz = sizeof(num);
3060 rec->left = top->left;
3061 rec->right = top;
3062 top->left = NULL;
3063 tree->rnum++;
3064 tree->msiz += ksiz + sizeof(num);
3065 tree->root = rec;
3066 } else if(cv > 0){
3067 int psiz = TCALIGNPAD(ksiz);
3068 TCTREEREC *rec;
3069 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
3070 dbuf = (char *)rec + sizeof(*rec);
3071 memcpy(dbuf, kbuf, ksiz);
3072 dbuf[ksiz] = '\0';
3073 rec->ksiz = ksiz;
3074 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
3075 dbuf[ksiz+psiz+sizeof(num)] = '\0';
3076 rec->vsiz = sizeof(num);
3077 rec->left = top;
3078 rec->right = top->right;
3079 top->right = NULL;
3080 tree->rnum++;
3081 tree->msiz += ksiz + sizeof(num);
3082 tree->root = rec;
3083 } else {
3084 tree->root = top;
3085 if(top->vsiz != sizeof(num)) return nan("");
3086 double *resp = (double *)(dbuf + ksiz + TCALIGNPAD(ksiz));
3087 return *resp += num;
3088 }
3089 return num;
3090 }
3091
3092
3093 /* Clear a tree object. */
tctreeclear(TCTREE * tree)3094 void tctreeclear(TCTREE *tree){
3095 assert(tree);
3096 if(tree->root){
3097 TCTREEREC *histbuf[TREESTACKNUM];
3098 TCTREEREC **history = histbuf;
3099 int hnum = 0;
3100 history[hnum++] = tree->root;
3101 while(hnum > 0){
3102 TCTREEREC *rec = history[--hnum];
3103 if(hnum >= TREESTACKNUM - 2 && history == histbuf){
3104 TCMALLOC(history, sizeof(*history) * tree->rnum);
3105 memcpy(history, histbuf, sizeof(*history) * hnum);
3106 }
3107 if(rec->left) history[hnum++] = rec->left;
3108 if(rec->right) history[hnum++] = rec->right;
3109 TCFREE(rec);
3110 }
3111 if(history != histbuf) TCFREE(history);
3112 }
3113 tree->root = NULL;
3114 tree->cur = NULL;
3115 tree->rnum = 0;
3116 tree->msiz = 0;
3117 }
3118
3119
3120 /* Remove fringe records of a tree object. */
tctreecutfringe(TCTREE * tree,int num)3121 void tctreecutfringe(TCTREE *tree, int num){
3122 assert(tree && num >= 0);
3123 if(!tree->root || num < 1) return;
3124 TCTREEREC **history;
3125 TCMALLOC(history, sizeof(*history) * tree->rnum);
3126 int hnum = 0;
3127 history[hnum++] = tree->root;
3128 for(int i = 0; i < hnum; i++){
3129 TCTREEREC *rec = history[i];
3130 if(rec->left) history[hnum++] = rec->left;
3131 if(rec->right) history[hnum++] = rec->right;
3132 }
3133 TCTREEREC *cur = NULL;
3134 for(int i = hnum - 1; i >= 0; i--){
3135 TCTREEREC *rec = history[i];
3136 if(rec->left){
3137 TCTREEREC *child = rec->left;
3138 tree->rnum--;
3139 tree->msiz -= child->ksiz + child->vsiz;
3140 rec->left = NULL;
3141 if(tree->cur == child){
3142 tree->cur = NULL;
3143 cur = child;
3144 } else {
3145 TCFREE(child);
3146 }
3147 if(--num < 1) break;
3148 }
3149 if(rec->right){
3150 TCTREEREC *child = rec->right;
3151 tree->rnum--;
3152 tree->msiz -= child->ksiz + child->vsiz;
3153 rec->right = NULL;
3154 if(tree->cur == child){
3155 tree->cur = NULL;
3156 cur = child;
3157 } else {
3158 TCFREE(child);
3159 }
3160 if(--num < 1) break;
3161 }
3162 }
3163 if(num > 0){
3164 TCFREE(tree->root);
3165 tree->root = NULL;
3166 tree->cur = NULL;
3167 tree->rnum = 0;
3168 tree->msiz = 0;
3169 }
3170 if(cur){
3171 char *dbuf = (char *)cur + sizeof(*cur);
3172 tctreeiterinit2(tree, dbuf, cur->ksiz);
3173 TCFREE(cur);
3174 }
3175 TCFREE(history);
3176 }
3177
3178
3179 /* Serialize a tree object into a byte array. */
tctreedump(const TCTREE * tree,int * sp)3180 void *tctreedump(const TCTREE *tree, int *sp){
3181 assert(tree && sp);
3182 int tsiz = 0;
3183 if(tree->root){
3184 TCTREEREC *histbuf[TREESTACKNUM];
3185 TCTREEREC **history = histbuf;
3186 int hnum = 0;
3187 history[hnum++] = tree->root;
3188 while(hnum > 0){
3189 TCTREEREC *rec = history[--hnum];
3190 if(hnum >= TREESTACKNUM - 2 && history == histbuf){
3191 TCMALLOC(history, sizeof(*history) * tree->rnum);
3192 memcpy(history, histbuf, sizeof(*history) * hnum);
3193 }
3194 if(rec->left) history[hnum++] = rec->left;
3195 if(rec->right) history[hnum++] = rec->right;
3196 tsiz += rec->ksiz + rec->vsiz + sizeof(int) * 2;
3197 }
3198 if(history != histbuf) TCFREE(history);
3199 }
3200 char *buf;
3201 TCMALLOC(buf, tsiz + 1);
3202 char *wp = buf;
3203 if(tree->root){
3204 TCTREEREC *histbuf[TREESTACKNUM];
3205 TCTREEREC **history = histbuf;
3206 int hnum = 0;
3207 history[hnum++] = tree->root;
3208 while(hnum > 0){
3209 TCTREEREC *rec = history[--hnum];
3210 if(hnum >= TREESTACKNUM - 2 && history == histbuf){
3211 TCMALLOC(history, sizeof(*history) * tree->rnum);
3212 memcpy(history, histbuf, sizeof(*history) * hnum);
3213 }
3214 if(rec->left) history[hnum++] = rec->left;
3215 if(rec->right) history[hnum++] = rec->right;
3216 const char *kbuf = (char *)rec + sizeof(*rec);
3217 int ksiz = rec->ksiz;
3218 const char *vbuf = kbuf + ksiz + TCALIGNPAD(ksiz);
3219 int vsiz = rec->vsiz;
3220 int step;
3221 TCSETVNUMBUF(step, wp, ksiz);
3222 wp += step;
3223 memcpy(wp, kbuf, ksiz);
3224 wp += ksiz;
3225 TCSETVNUMBUF(step, wp, vsiz);
3226 wp += step;
3227 memcpy(wp, vbuf, vsiz);
3228 wp += vsiz;
3229 }
3230 if(history != histbuf) TCFREE(history);
3231 }
3232 *sp = wp - buf;
3233 return buf;
3234 }
3235
3236
3237 /* Create a tree object from a serialized byte array. */
tctreeload(const void * ptr,int size,TCCMP cmp,void * cmpop)3238 TCTREE *tctreeload(const void *ptr, int size, TCCMP cmp, void *cmpop){
3239 assert(ptr && size >= 0 && cmp);
3240 TCTREE *tree = tctreenew2(cmp, cmpop);
3241 const char *rp = ptr;
3242 const char *ep = (char *)ptr + size;
3243 while(rp < ep){
3244 int step, ksiz, vsiz;
3245 TCREADVNUMBUF(rp, ksiz, step);
3246 rp += step;
3247 const char *kbuf = rp;
3248 rp += ksiz;
3249 TCREADVNUMBUF(rp, vsiz, step);
3250 rp += step;
3251 tctreeputkeep(tree, kbuf, ksiz, rp, vsiz);
3252 rp += vsiz;
3253 }
3254 return tree;
3255 }
3256
3257
3258 /* Perform the splay operation of a tree object.
3259 `tree' specifies the tree object.
3260 `kbuf' specifies the pointer to the region of the key.
3261 `ksiz' specifies the size of the region of the key.
3262 The return value is the pointer to the record corresponding the key. */
tctreesplay(TCTREE * tree,const void * kbuf,int ksiz)3263 static TCTREEREC* tctreesplay(TCTREE *tree, const void *kbuf, int ksiz){
3264 assert(tree && kbuf && ksiz >= 0);
3265 TCTREEREC *top = tree->root;
3266 if(!top) return NULL;
3267 TCCMP cmp = tree->cmp;
3268 void *cmpop = tree->cmpop;
3269 TCTREEREC ent;
3270 ent.left = NULL;
3271 ent.right = NULL;
3272 TCTREEREC *lrec = &ent;
3273 TCTREEREC *rrec = &ent;
3274 while(true){
3275 char *dbuf = (char *)top + sizeof(*top);
3276 int cv = cmp(kbuf, ksiz, dbuf, top->ksiz, cmpop);
3277 if(cv < 0){
3278 if(!top->left) break;
3279 dbuf = (char *)top->left + sizeof(*top);
3280 cv = cmp(kbuf, ksiz, dbuf, top->left->ksiz, cmpop);
3281 if(cv < 0){
3282 TCTREEREC *swap = top->left;
3283 top->left = swap->right;
3284 swap->right = top;
3285 top = swap;
3286 if(!top->left) break;
3287 }
3288 rrec->left = top;
3289 rrec = top;
3290 top = top->left;
3291 } else if(cv > 0){
3292 if(!top->right) break;
3293 dbuf = (char *)top->right + sizeof(*top);
3294 cv = cmp(kbuf, ksiz, dbuf, top->right->ksiz, cmpop);
3295 if(cv > 0){
3296 TCTREEREC *swap = top->right;
3297 top->right = swap->left;
3298 swap->left = top;
3299 top = swap;
3300 if(!top->right) break;
3301 }
3302 lrec->right = top;
3303 lrec = top;
3304 top = top->right;
3305 } else {
3306 break;
3307 }
3308 }
3309 lrec->right = top->left;
3310 rrec->left = top->right;
3311 top->left = ent.right;
3312 top->right = ent.left;
3313 return top;
3314 }
3315
3316
3317
3318 /*************************************************************************************************
3319 * ordered tree (for experts)
3320 *************************************************************************************************/
3321
3322
3323 /* Store a record into a tree object without balancing nodes. */
tctreeput3(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3324 void tctreeput3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3325 assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3326 TCTREEREC *rec = tree->root;
3327 TCTREEREC **entp = NULL;
3328 while(rec){
3329 char *dbuf = (char *)rec + sizeof(*rec);
3330 int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop);
3331 if(cv < 0){
3332 entp = &(rec->left);
3333 rec = rec->left;
3334 } else if(cv > 0){
3335 entp = &(rec->right);
3336 rec = rec->right;
3337 } else {
3338 tree->msiz += vsiz - rec->vsiz;
3339 int psiz = TCALIGNPAD(ksiz);
3340 if(vsiz > rec->vsiz){
3341 TCTREEREC *old = rec;
3342 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
3343 if(rec != old){
3344 if(tree->root == old) tree->root = rec;
3345 if(tree->cur == old) tree->cur = rec;
3346 if(entp) *entp = rec;
3347 dbuf = (char *)rec + sizeof(*rec);
3348 }
3349 }
3350 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
3351 dbuf[ksiz+psiz+vsiz] = '\0';
3352 rec->vsiz = vsiz;
3353 return;
3354 }
3355 }
3356 int psiz = TCALIGNPAD(ksiz);
3357 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
3358 char *dbuf = (char *)rec + sizeof(*rec);
3359 memcpy(dbuf, kbuf, ksiz);
3360 dbuf[ksiz] = '\0';
3361 rec->ksiz = ksiz;
3362 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
3363 dbuf[ksiz+psiz+vsiz] = '\0';
3364 rec->vsiz = vsiz;
3365 rec->left = NULL;
3366 rec->right = NULL;
3367 if(entp){
3368 *entp = rec;
3369 } else {
3370 tree->root = rec;
3371 }
3372 tree->rnum++;
3373 tree->msiz += ksiz + vsiz;
3374 }
3375
3376
3377 /* Store a new record into a map object without balancing nodes. */
tctreeputkeep3(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3378 bool tctreeputkeep3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3379 assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3380 TCTREEREC *rec = tree->root;
3381 TCTREEREC **entp = NULL;
3382 while(rec){
3383 char *dbuf = (char *)rec + sizeof(*rec);
3384 int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop);
3385 if(cv < 0){
3386 entp = &(rec->left);
3387 rec = rec->left;
3388 } else if(cv > 0){
3389 entp = &(rec->right);
3390 rec = rec->right;
3391 } else {
3392 return false;
3393 }
3394 }
3395 int psiz = TCALIGNPAD(ksiz);
3396 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
3397 char *dbuf = (char *)rec + sizeof(*rec);
3398 memcpy(dbuf, kbuf, ksiz);
3399 dbuf[ksiz] = '\0';
3400 rec->ksiz = ksiz;
3401 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
3402 dbuf[ksiz+psiz+vsiz] = '\0';
3403 rec->vsiz = vsiz;
3404 rec->left = NULL;
3405 rec->right = NULL;
3406 if(entp){
3407 *entp = rec;
3408 } else {
3409 tree->root = rec;
3410 }
3411 tree->rnum++;
3412 tree->msiz += ksiz + vsiz;
3413 return true;
3414 }
3415
3416
3417 /* Concatenate a value at the existing record in a tree object without balancing nodes. */
tctreeputcat3(TCTREE * tree,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3418 void tctreeputcat3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3419 assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3420 TCTREEREC *rec = tree->root;
3421 TCTREEREC **entp = NULL;
3422 while(rec){
3423 char *dbuf = (char *)rec + sizeof(*rec);
3424 int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop);
3425 if(cv < 0){
3426 entp = &(rec->left);
3427 rec = rec->left;
3428 } else if(cv > 0){
3429 entp = &(rec->right);
3430 rec = rec->right;
3431 } else {
3432 tree->msiz += vsiz;
3433 int psiz = TCALIGNPAD(ksiz);
3434 int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1;
3435 int unit = (asiz <= TCTREECSUNIT) ? TCTREECSUNIT : TCTREECBUNIT;
3436 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
3437 TCTREEREC *old = rec;
3438 TCREALLOC(rec, rec, asiz);
3439 if(rec != old){
3440 if(tree->root == old) tree->root = rec;
3441 if(tree->cur == old) tree->cur = rec;
3442 if(entp) *entp = rec;
3443 dbuf = (char *)rec + sizeof(*rec);
3444 }
3445 memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz);
3446 rec->vsiz += vsiz;
3447 dbuf[ksiz+psiz+rec->vsiz] = '\0';
3448 return;
3449 }
3450 }
3451 int psiz = TCALIGNPAD(ksiz);
3452 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
3453 char *dbuf = (char *)rec + sizeof(*rec);
3454 memcpy(dbuf, kbuf, ksiz);
3455 dbuf[ksiz] = '\0';
3456 rec->ksiz = ksiz;
3457 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
3458 dbuf[ksiz+psiz+vsiz] = '\0';
3459 rec->vsiz = vsiz;
3460 rec->left = NULL;
3461 rec->right = NULL;
3462 if(entp){
3463 *entp = rec;
3464 } else {
3465 tree->root = rec;
3466 }
3467 tree->rnum++;
3468 tree->msiz += ksiz + vsiz;
3469 }
3470
3471
3472 /* Retrieve a record in a tree object without balancing nodes. */
tctreeget3(const TCTREE * tree,const void * kbuf,int ksiz,int * sp)3473 const void *tctreeget3(const TCTREE *tree, const void *kbuf, int ksiz, int *sp){
3474 assert(tree && kbuf && ksiz >= 0 && sp);
3475 TCTREEREC *rec = tree->root;
3476 while(rec){
3477 char *dbuf = (char *)rec + sizeof(*rec);
3478 int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop);
3479 if(cv < 0){
3480 rec = rec->left;
3481 } else if(cv > 0){
3482 rec = rec->right;
3483 } else {
3484 *sp = rec->vsiz;
3485 return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
3486 }
3487 }
3488 return NULL;
3489 }
3490
3491
3492 /* Retrieve a string record in a tree object with specifying the default value string. */
tctreeget4(TCTREE * tree,const char * kstr,const char * dstr)3493 const char *tctreeget4(TCTREE *tree, const char *kstr, const char *dstr){
3494 assert(tree && kstr && dstr);
3495 int vsiz;
3496 const char *vbuf = tctreeget(tree, kstr, strlen(kstr), &vsiz);
3497 return vbuf ? vbuf : dstr;
3498 }
3499
3500
3501 /* Initialize the iterator of a tree object in front of records corresponding a key. */
tctreeiterinit2(TCTREE * tree,const void * kbuf,int ksiz)3502 void tctreeiterinit2(TCTREE *tree, const void *kbuf, int ksiz){
3503 assert(tree && kbuf && ksiz >= 0);
3504 TCTREEREC *rec = tree->root;
3505 while(rec){
3506 char *dbuf = (char *)rec + sizeof(*rec);
3507 int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop);
3508 if(cv < 0){
3509 tree->cur = rec;
3510 rec = rec->left;
3511 } else if(cv > 0){
3512 rec = rec->right;
3513 } else {
3514 tree->cur = rec;
3515 return;
3516 }
3517 }
3518 }
3519
3520
3521 /* Initialize the iterator of a tree object in front of records corresponding a key string. */
tctreeiterinit3(TCTREE * tree,const char * kstr)3522 void tctreeiterinit3(TCTREE *tree, const char *kstr){
3523 assert(tree);
3524 tctreeiterinit2(tree, kstr, strlen(kstr));
3525 }
3526
3527
3528 /* Get the value bound to the key fetched from the iterator of a tree object. */
tctreeiterval(const void * kbuf,int * sp)3529 const void *tctreeiterval(const void *kbuf, int *sp){
3530 assert(kbuf && sp);
3531 TCTREEREC *rec = (TCTREEREC *)((char *)kbuf - sizeof(*rec));
3532 *sp = rec->vsiz;
3533 return (char *)kbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
3534 }
3535
3536
3537 /* Get the value string bound to the key fetched from the iterator of a tree object. */
tctreeiterval2(const char * kstr)3538 const char *tctreeiterval2(const char *kstr){
3539 assert(kstr);
3540 TCTREEREC *rec = (TCTREEREC *)(kstr - sizeof(*rec));
3541 return kstr + rec->ksiz + TCALIGNPAD(rec->ksiz);
3542 }
3543
3544
3545 /* Create an array of strings of all keys in a tree object. */
tctreekeys2(const TCTREE * tree,int * np)3546 const char **tctreekeys2(const TCTREE *tree, int *np){
3547 assert(tree && np);
3548 const char **ary;
3549 TCMALLOC(ary, sizeof(*ary) * tree->rnum + 1);
3550 int anum = 0;
3551 if(tree->root){
3552 TCTREEREC **history;
3553 TCMALLOC(history, sizeof(*history) * tree->rnum);
3554 TCTREEREC **result;
3555 TCMALLOC(result, sizeof(*history) * tree->rnum);
3556 int hnum = 0;
3557 history[hnum++] = tree->root;
3558 while(hnum > 0){
3559 TCTREEREC *rec = history[--hnum];
3560 if(!rec){
3561 rec = result[hnum];
3562 ary[(anum++)] = (char *)rec + sizeof(*rec);
3563 continue;
3564 }
3565 if(rec->right) history[hnum++] = rec->right;
3566 history[hnum] = NULL;
3567 result[hnum] = rec;
3568 hnum++;
3569 if(rec->left) history[hnum++] = rec->left;
3570 }
3571 TCFREE(result);
3572 TCFREE(history);
3573 }
3574 *np = anum;
3575 return ary;
3576 }
3577
3578
3579 /* Create an array of strings of all values in a tree object. */
tctreevals2(const TCTREE * tree,int * np)3580 const char **tctreevals2(const TCTREE *tree, int *np){
3581 assert(tree && np);
3582 const char **ary;
3583 TCMALLOC(ary, sizeof(*ary) * tree->rnum + 1);
3584 int anum = 0;
3585 if(tree->root){
3586 TCTREEREC **history;
3587 TCMALLOC(history, sizeof(*history) * tree->rnum);
3588 TCTREEREC **result;
3589 TCMALLOC(result, sizeof(*history) * tree->rnum);
3590 int hnum = 0;
3591 history[hnum++] = tree->root;
3592 while(hnum > 0){
3593 TCTREEREC *rec = history[--hnum];
3594 if(!rec){
3595 rec = result[hnum];
3596 ary[(anum++)] = (char *)rec + sizeof(*rec);
3597 continue;
3598 }
3599 if(rec->right) history[hnum++] = rec->right;
3600 history[hnum] = NULL;
3601 result[hnum] = rec;
3602 hnum++;
3603 if(rec->left) history[hnum++] = rec->left;
3604 }
3605 TCFREE(result);
3606 TCFREE(history);
3607 }
3608 *np = anum;
3609 return ary;
3610 }
3611
3612
3613 /* Extract a tree record from a serialized byte array. */
tctreeloadone(const void * ptr,int size,const void * kbuf,int ksiz,int * sp)3614 void *tctreeloadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){
3615 assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp);
3616 const char *rp = ptr;
3617 const char *ep = (char *)ptr + size;
3618 while(rp < ep){
3619 int step, rsiz;
3620 TCREADVNUMBUF(rp, rsiz, step);
3621 rp += step;
3622 if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){
3623 rp += rsiz;
3624 TCREADVNUMBUF(rp, rsiz, step);
3625 rp += step;
3626 *sp = rsiz;
3627 char *rv;
3628 TCMEMDUP(rv, rp, rsiz);
3629 return rv;
3630 }
3631 rp += rsiz;
3632 TCREADVNUMBUF(rp, rsiz, step);
3633 rp += step;
3634 rp += rsiz;
3635 }
3636 return NULL;
3637 }
3638
3639
3640 /* Perform formatted output into a tree object. */
tctreeprintf(TCTREE * tree,const char * kstr,const char * format,...)3641 void tctreeprintf(TCTREE *tree, const char *kstr, const char *format, ...){
3642 assert(tree && kstr && format);
3643 TCXSTR *xstr = tcxstrnew();
3644 va_list ap;
3645 va_start(ap, format);
3646 tcvxstrprintf(xstr, format, ap);
3647 va_end(ap);
3648 tctreeput(tree, kstr, strlen(kstr), TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
3649 tcxstrdel(xstr);
3650 }
3651
3652
3653
3654 /*************************************************************************************************
3655 * on-memory hash database
3656 *************************************************************************************************/
3657
3658
3659 #define TCMDBMNUM 8 // number of internal maps
3660 #define TCMDBDEFBNUM 65536 // default bucket number
3661
3662 /* get the first hash value */
3663 #define TCMDBHASH(TC_res, TC_kbuf, TC_ksiz) \
3664 do { \
3665 const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
3666 int _TC_ksiz = TC_ksiz; \
3667 for((TC_res) = 0x20071123; _TC_ksiz--;){ \
3668 (TC_res) = (TC_res) * 33 + *(_TC_p)--; \
3669 } \
3670 (TC_res) &= TCMDBMNUM - 1; \
3671 } while(false)
3672
3673
3674 /* Create an on-memory hash database object. */
tcmdbnew(void)3675 TCMDB *tcmdbnew(void){
3676 return tcmdbnew2(TCMDBDEFBNUM);
3677 }
3678
3679
3680 /* Create an on-memory hash database with specifying the number of the buckets. */
tcmdbnew2(uint32_t bnum)3681 TCMDB *tcmdbnew2(uint32_t bnum){
3682 TCMDB *mdb;
3683 if(bnum < 1) bnum = TCMDBDEFBNUM;
3684 bnum = bnum / TCMDBMNUM + 17;
3685 TCMALLOC(mdb, sizeof(*mdb));
3686 TCMALLOC(mdb->mmtxs, sizeof(pthread_rwlock_t) * TCMDBMNUM);
3687 TCMALLOC(mdb->imtx, sizeof(pthread_mutex_t));
3688 TCMALLOC(mdb->maps, sizeof(TCMAP *) * TCMDBMNUM);
3689 if(pthread_mutex_init(mdb->imtx, NULL) != 0) tcmyfatal("mutex error");
3690 for(int i = 0; i < TCMDBMNUM; i++){
3691 if(pthread_rwlock_init((pthread_rwlock_t *)mdb->mmtxs + i, NULL) != 0)
3692 tcmyfatal("rwlock error");
3693 mdb->maps[i] = tcmapnew2(bnum);
3694 }
3695 mdb->iter = -1;
3696 return mdb;
3697 }
3698
3699
3700 /* Delete an on-memory hash database object. */
tcmdbdel(TCMDB * mdb)3701 void tcmdbdel(TCMDB *mdb){
3702 assert(mdb);
3703 for(int i = TCMDBMNUM - 1; i >= 0; i--){
3704 tcmapdel(mdb->maps[i]);
3705 pthread_rwlock_destroy((pthread_rwlock_t *)mdb->mmtxs + i);
3706 }
3707 pthread_mutex_destroy(mdb->imtx);
3708 TCFREE(mdb->maps);
3709 TCFREE(mdb->imtx);
3710 TCFREE(mdb->mmtxs);
3711 TCFREE(mdb);
3712 }
3713
3714
3715 /* Store a record into an on-memory hash database. */
tcmdbput(TCMDB * mdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3716 void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3717 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3718 unsigned int mi;
3719 TCMDBHASH(mi, kbuf, ksiz);
3720 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
3721 tcmapput(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
3722 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3723 }
3724
3725
3726 /* Store a string record into an on-memory hash database. */
tcmdbput2(TCMDB * mdb,const char * kstr,const char * vstr)3727 void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr){
3728 assert(mdb && kstr && vstr);
3729 tcmdbput(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
3730 }
3731
3732
3733 /* Store a new record into an on-memory hash database. */
tcmdbputkeep(TCMDB * mdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3734 bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3735 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3736 unsigned int mi;
3737 TCMDBHASH(mi, kbuf, ksiz);
3738 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
3739 bool rv = tcmapputkeep(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
3740 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3741 return rv;
3742 }
3743
3744
3745 /* Store a new string record into an on-memory hash database. */
tcmdbputkeep2(TCMDB * mdb,const char * kstr,const char * vstr)3746 bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr){
3747 assert(mdb && kstr && vstr);
3748 return tcmdbputkeep(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
3749 }
3750
3751
3752 /* Concatenate a value at the end of the existing record in an on-memory hash database. */
tcmdbputcat(TCMDB * mdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)3753 void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
3754 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
3755 unsigned int mi;
3756 TCMDBHASH(mi, kbuf, ksiz);
3757 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
3758 tcmapputcat(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
3759 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3760 }
3761
3762
3763 /* Concatenate a string at the end of the existing record in an on-memory hash database. */
tcmdbputcat2(TCMDB * mdb,const char * kstr,const char * vstr)3764 void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr){
3765 assert(mdb && kstr && vstr);
3766 tcmdbputcat(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
3767 }
3768
3769
3770 /* Remove a record of an on-memory hash database. */
tcmdbout(TCMDB * mdb,const void * kbuf,int ksiz)3771 bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz){
3772 assert(mdb && kbuf && ksiz >= 0);
3773 unsigned int mi;
3774 TCMDBHASH(mi, kbuf, ksiz);
3775 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
3776 bool rv = tcmapout(mdb->maps[mi], kbuf, ksiz);
3777 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3778 return rv;
3779 }
3780
3781
3782 /* Remove a string record of an on-memory hash database. */
tcmdbout2(TCMDB * mdb,const char * kstr)3783 bool tcmdbout2(TCMDB *mdb, const char *kstr){
3784 assert(mdb && kstr);
3785 return tcmdbout(mdb, kstr, strlen(kstr));
3786 }
3787
3788
3789 /* Retrieve a record in an on-memory hash database. */
tcmdbget(TCMDB * mdb,const void * kbuf,int ksiz,int * sp)3790 void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
3791 assert(mdb && kbuf && ksiz >= 0 && sp);
3792 unsigned int mi;
3793 TCMDBHASH(mi, kbuf, ksiz);
3794 if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
3795 int vsiz;
3796 const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
3797 char *rv;
3798 if(vbuf){
3799 TCMEMDUP(rv, vbuf, vsiz);
3800 *sp = vsiz;
3801 } else {
3802 rv = NULL;
3803 }
3804 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3805 return rv;
3806 }
3807
3808
3809 /* Retrieve a string record in an on-memory hash database. */
tcmdbget2(TCMDB * mdb,const char * kstr)3810 char *tcmdbget2(TCMDB *mdb, const char *kstr){
3811 assert(mdb && kstr);
3812 int vsiz;
3813 return tcmdbget(mdb, kstr, strlen(kstr), &vsiz);
3814 }
3815
3816
3817 /* Get the size of the value of a record in an on-memory hash database object. */
tcmdbvsiz(TCMDB * mdb,const void * kbuf,int ksiz)3818 int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz){
3819 assert(mdb && kbuf && ksiz >= 0);
3820 unsigned int mi;
3821 TCMDBHASH(mi, kbuf, ksiz);
3822 if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return -1;
3823 int vsiz;
3824 const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
3825 if(!vbuf) vsiz = -1;
3826 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3827 return vsiz;
3828 }
3829
3830
3831 /* Get the size of the value of a string record in an on-memory hash database object. */
tcmdbvsiz2(TCMDB * mdb,const char * kstr)3832 int tcmdbvsiz2(TCMDB *mdb, const char *kstr){
3833 assert(mdb && kstr);
3834 return tcmdbvsiz(mdb, kstr, strlen(kstr));
3835 }
3836
3837
3838 /* Initialize the iterator of an on-memory hash database. */
tcmdbiterinit(TCMDB * mdb)3839 void tcmdbiterinit(TCMDB *mdb){
3840 assert(mdb);
3841 if(pthread_mutex_lock(mdb->imtx) != 0) return;
3842 for(int i = 0; i < TCMDBMNUM; i++){
3843 tcmapiterinit(mdb->maps[i]);
3844 }
3845 mdb->iter = 0;
3846 pthread_mutex_unlock(mdb->imtx);
3847 }
3848
3849
3850 /* Get the next key of the iterator of an on-memory hash database. */
tcmdbiternext(TCMDB * mdb,int * sp)3851 void *tcmdbiternext(TCMDB *mdb, int *sp){
3852 assert(mdb && sp);
3853 if(pthread_mutex_lock(mdb->imtx) != 0) return NULL;
3854 if(mdb->iter < 0 || mdb->iter >= TCMDBMNUM){
3855 pthread_mutex_unlock(mdb->imtx);
3856 return NULL;
3857 }
3858 int mi = mdb->iter;
3859 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
3860 pthread_mutex_unlock(mdb->imtx);
3861 return NULL;
3862 }
3863 int ksiz;
3864 const char *kbuf;
3865 while(!(kbuf = tcmapiternext(mdb->maps[mi], &ksiz)) && mi < TCMDBMNUM - 1){
3866 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3867 mi = ++mdb->iter;
3868 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
3869 pthread_mutex_unlock(mdb->imtx);
3870 return NULL;
3871 }
3872 }
3873 char *rv;
3874 if(kbuf){
3875 TCMEMDUP(rv, kbuf, ksiz);
3876 *sp = ksiz;
3877 } else {
3878 rv = NULL;
3879 }
3880 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3881 pthread_mutex_unlock(mdb->imtx);
3882 return rv;
3883 }
3884
3885
3886 /* Get the next key string of the iterator of an on-memory hash database. */
tcmdbiternext2(TCMDB * mdb)3887 char *tcmdbiternext2(TCMDB *mdb){
3888 assert(mdb);
3889 int ksiz;
3890 return tcmdbiternext(mdb, &ksiz);
3891 }
3892
3893
3894 /* Get forward matching keys in an on-memory hash database object. */
tcmdbfwmkeys(TCMDB * mdb,const void * pbuf,int psiz,int max)3895 TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max){
3896 assert(mdb && pbuf && psiz >= 0);
3897 TCLIST* keys = tclistnew();
3898 if(pthread_mutex_lock(mdb->imtx) != 0) return keys;
3899 if(max < 0) max = INT_MAX;
3900 for(int i = 0; i < TCMDBMNUM && TCLISTNUM(keys) < max; i++){
3901 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
3902 TCMAP *map = mdb->maps[i];
3903 TCMAPREC *cur = map->cur;
3904 tcmapiterinit(map);
3905 const char *kbuf;
3906 int ksiz;
3907 while(TCLISTNUM(keys) < max && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
3908 if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)) TCLISTPUSH(keys, kbuf, ksiz);
3909 }
3910 map->cur = cur;
3911 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
3912 }
3913 }
3914 pthread_mutex_unlock(mdb->imtx);
3915 return keys;
3916 }
3917
3918
3919 /* Get forward matching string keys in an on-memory hash database object. */
tcmdbfwmkeys2(TCMDB * mdb,const char * pstr,int max)3920 TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max){
3921 assert(mdb && pstr);
3922 return tcmdbfwmkeys(mdb, pstr, strlen(pstr), max);
3923 }
3924
3925
3926 /* Get the number of records stored in an on-memory hash database. */
tcmdbrnum(TCMDB * mdb)3927 uint64_t tcmdbrnum(TCMDB *mdb){
3928 assert(mdb);
3929 uint64_t rnum = 0;
3930 for(int i = 0; i < TCMDBMNUM; i++){
3931 rnum += tcmaprnum(mdb->maps[i]);
3932 }
3933 return rnum;
3934 }
3935
3936
3937 /* Get the total size of memory used in an on-memory hash database object. */
tcmdbmsiz(TCMDB * mdb)3938 uint64_t tcmdbmsiz(TCMDB *mdb){
3939 assert(mdb);
3940 uint64_t msiz = 0;
3941 for(int i = 0; i < TCMDBMNUM; i++){
3942 msiz += tcmapmsiz(mdb->maps[i]);
3943 }
3944 return msiz;
3945 }
3946
3947
3948 /* Add an integer to a record in an on-memory hash database object. */
tcmdbaddint(TCMDB * mdb,const void * kbuf,int ksiz,int num)3949 int tcmdbaddint(TCMDB *mdb, const void *kbuf, int ksiz, int num){
3950 assert(mdb && kbuf && ksiz >= 0);
3951 unsigned int mi;
3952 TCMDBHASH(mi, kbuf, ksiz);
3953 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return INT_MIN;
3954 int rv = tcmapaddint(mdb->maps[mi], kbuf, ksiz, num);
3955 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3956 return rv;
3957 }
3958
3959
3960 /* Add a real number to a record in an on-memory hash database object. */
tcmdbadddouble(TCMDB * mdb,const void * kbuf,int ksiz,double num)3961 double tcmdbadddouble(TCMDB *mdb, const void *kbuf, int ksiz, double num){
3962 assert(mdb && kbuf && ksiz >= 0);
3963 unsigned int mi;
3964 TCMDBHASH(mi, kbuf, ksiz);
3965 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return nan("");
3966 double rv = tcmapadddouble(mdb->maps[mi], kbuf, ksiz, num);
3967 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
3968 return rv;
3969 }
3970
3971
3972 /* Clear an on-memory hash database object. */
tcmdbvanish(TCMDB * mdb)3973 void tcmdbvanish(TCMDB *mdb){
3974 assert(mdb);
3975 for(int i = 0; i < TCMDBMNUM; i++){
3976 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
3977 tcmapclear(mdb->maps[i]);
3978 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
3979 }
3980 }
3981 }
3982
3983
3984 /* Remove front records of a map object. */
tcmdbcutfront(TCMDB * mdb,int num)3985 void tcmdbcutfront(TCMDB *mdb, int num){
3986 assert(mdb && num >= 0);
3987 num = num / TCMDBMNUM + 1;
3988 for(int i = 0; i < TCMDBMNUM; i++){
3989 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
3990 tcmapcutfront(mdb->maps[i], num);
3991 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
3992 }
3993 }
3994 }
3995
3996
3997
3998 /*************************************************************************************************
3999 * on-memory hash database (for experts)
4000 *************************************************************************************************/
4001
4002
4003 /* Store a record and make it semivolatile in an on-memory hash database object. */
tcmdbput3(TCMDB * mdb,const void * kbuf,int ksiz,const char * vbuf,int vsiz)4004 void tcmdbput3(TCMDB *mdb, const void *kbuf, int ksiz, const char *vbuf, int vsiz){
4005 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4006 unsigned int mi;
4007 TCMDBHASH(mi, kbuf, ksiz);
4008 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
4009 tcmapput3(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
4010 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
4011 }
4012
4013
4014 /* Store a record of the value of two regions into an on-memory hash database object. */
tcmdbput4(TCMDB * mdb,const void * kbuf,int ksiz,const void * fvbuf,int fvsiz,const void * lvbuf,int lvsiz)4015 void tcmdbput4(TCMDB *mdb, const void *kbuf, int ksiz,
4016 const void *fvbuf, int fvsiz, const void *lvbuf, int lvsiz){
4017 assert(mdb && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
4018 unsigned int mi;
4019 TCMDBHASH(mi, kbuf, ksiz);
4020 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
4021 tcmapput4(mdb->maps[mi], kbuf, ksiz, fvbuf, fvsiz, lvbuf, lvsiz);
4022 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
4023 }
4024
4025
4026 /* Concatenate a value and make it semivolatile in on-memory hash database object. */
tcmdbputcat3(TCMDB * mdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4027 void tcmdbputcat3(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4028 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4029 unsigned int mi;
4030 TCMDBHASH(mi, kbuf, ksiz);
4031 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
4032 tcmapputcat3(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
4033 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
4034 }
4035
4036
4037 /* Store a record into a on-memory hash database object with a duplication handler. */
tcmdbputproc(TCMDB * mdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)4038 bool tcmdbputproc(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
4039 TCPDPROC proc, void *op){
4040 assert(mdb && kbuf && ksiz >= 0 && proc);
4041 unsigned int mi;
4042 TCMDBHASH(mi, kbuf, ksiz);
4043 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
4044 bool rv = tcmapputproc(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz, proc, op);
4045 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
4046 return rv;
4047 }
4048
4049
4050 /* Retrieve a record and move it astern in an on-memory hash database. */
tcmdbget3(TCMDB * mdb,const void * kbuf,int ksiz,int * sp)4051 void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
4052 assert(mdb && kbuf && ksiz >= 0 && sp);
4053 unsigned int mi;
4054 TCMDBHASH(mi, kbuf, ksiz);
4055 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
4056 int vsiz;
4057 const char *vbuf = tcmapget3(mdb->maps[mi], kbuf, ksiz, &vsiz);
4058 char *rv;
4059 if(vbuf){
4060 TCMEMDUP(rv, vbuf, vsiz);
4061 *sp = vsiz;
4062 } else {
4063 rv = NULL;
4064 }
4065 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
4066 return rv;
4067 }
4068
4069
4070 /* Initialize the iterator of an on-memory map database object in front of a key. */
tcmdbiterinit2(TCMDB * mdb,const void * kbuf,int ksiz)4071 void tcmdbiterinit2(TCMDB *mdb, const void *kbuf, int ksiz){
4072 if(pthread_mutex_lock(mdb->imtx) != 0) return;
4073 unsigned int mi;
4074 TCMDBHASH(mi, kbuf, ksiz);
4075 if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
4076 pthread_mutex_unlock(mdb->imtx);
4077 return;
4078 }
4079 int vsiz;
4080 if(tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz)){
4081 for(int i = 0; i < TCMDBMNUM; i++){
4082 tcmapiterinit(mdb->maps[i]);
4083 }
4084 tcmapiterinit2(mdb->maps[mi], kbuf, ksiz);
4085 mdb->iter = mi;
4086 }
4087 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
4088 pthread_mutex_unlock(mdb->imtx);
4089 }
4090
4091
4092 /* Initialize the iterator of an on-memory map database object in front of a key string. */
tcmdbiterinit3(TCMDB * mdb,const char * kstr)4093 void tcmdbiterinit3(TCMDB *mdb, const char *kstr){
4094 assert(mdb && kstr);
4095 tcmdbiterinit2(mdb, kstr, strlen(kstr));
4096 }
4097
4098
4099 /* Process each record atomically of an on-memory hash database object. */
tcmdbforeach(TCMDB * mdb,TCITER iter,void * op)4100 void tcmdbforeach(TCMDB *mdb, TCITER iter, void *op){
4101 assert(mdb && iter);
4102 for(int i = 0; i < TCMDBMNUM; i++){
4103 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) != 0){
4104 while(i >= 0){
4105 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
4106 i--;
4107 }
4108 return;
4109 }
4110 }
4111 bool cont = true;
4112 for(int i = 0; cont && i < TCMDBMNUM; i++){
4113 TCMAP *map = mdb->maps[i];
4114 TCMAPREC *cur = map->cur;
4115 tcmapiterinit(map);
4116 int ksiz;
4117 const char *kbuf;
4118 while(cont && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
4119 int vsiz;
4120 const char *vbuf = tcmapiterval(kbuf, &vsiz);
4121 if(!iter(kbuf, ksiz, vbuf, vsiz, op)) cont = false;
4122 }
4123 map->cur = cur;
4124 }
4125 for(int i = TCMDBMNUM - 1; i >= 0; i--){
4126 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
4127 }
4128 }
4129
4130
4131
4132 /*************************************************************************************************
4133 * on-memory tree database
4134 *************************************************************************************************/
4135
4136
4137 /* Create an on-memory tree database object. */
tcndbnew(void)4138 TCNDB *tcndbnew(void){
4139 return tcndbnew2(tccmplexical, NULL);
4140 }
4141
4142
4143 /* Create an on-memory tree database object with specifying the custom comparison function. */
tcndbnew2(TCCMP cmp,void * cmpop)4144 TCNDB *tcndbnew2(TCCMP cmp, void *cmpop){
4145 assert(cmp);
4146 TCNDB *ndb;
4147 TCMALLOC(ndb, sizeof(*ndb));
4148 TCMALLOC(ndb->mmtx, sizeof(pthread_mutex_t));
4149 if(pthread_mutex_init(ndb->mmtx, NULL) != 0) tcmyfatal("mutex error");
4150 ndb->tree = tctreenew2(cmp, cmpop);
4151 return ndb;
4152 }
4153
4154
4155 /* Delete an on-memory tree database object. */
tcndbdel(TCNDB * ndb)4156 void tcndbdel(TCNDB *ndb){
4157 assert(ndb);
4158 tctreedel(ndb->tree);
4159 pthread_mutex_destroy(ndb->mmtx);
4160 TCFREE(ndb->mmtx);
4161 TCFREE(ndb);
4162 }
4163
4164
4165 /* Store a record into an on-memory tree database object. */
tcndbput(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4166 void tcndbput(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4167 assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4168 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4169 tctreeput(ndb->tree, kbuf, ksiz, vbuf, vsiz);
4170 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4171 }
4172
4173
4174 /* Store a string record into an on-memory tree database object. */
tcndbput2(TCNDB * ndb,const char * kstr,const char * vstr)4175 void tcndbput2(TCNDB *ndb, const char *kstr, const char *vstr){
4176 assert(ndb && kstr && vstr);
4177 tcndbput(ndb, kstr, strlen(kstr), vstr, strlen(vstr));
4178 }
4179
4180
4181 /* Store a new record into an on-memory tree database object. */
tcndbputkeep(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4182 bool tcndbputkeep(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4183 assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4184 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false;
4185 bool rv = tctreeputkeep(ndb->tree, kbuf, ksiz, vbuf, vsiz);
4186 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4187 return rv;
4188 }
4189
4190
4191 /* Store a new string record into an on-memory tree database object. */
tcndbputkeep2(TCNDB * ndb,const char * kstr,const char * vstr)4192 bool tcndbputkeep2(TCNDB *ndb, const char *kstr, const char *vstr){
4193 assert(ndb && kstr && vstr);
4194 return tcndbputkeep(ndb, kstr, strlen(kstr), vstr, strlen(vstr));
4195 }
4196
4197
4198 /* Concatenate a value at the end of the existing record in an on-memory tree database. */
tcndbputcat(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4199 void tcndbputcat(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4200 assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4201 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4202 tctreeputcat(ndb->tree, kbuf, ksiz, vbuf, vsiz);
4203 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4204 }
4205
4206
4207 /* Concatenate a string at the end of the existing record in an on-memory tree database. */
tcndbputcat2(TCNDB * ndb,const char * kstr,const char * vstr)4208 void tcndbputcat2(TCNDB *ndb, const char *kstr, const char *vstr){
4209 assert(ndb && kstr && vstr);
4210 tcndbputcat(ndb, kstr, strlen(kstr), vstr, strlen(vstr));
4211 }
4212
4213
4214 /* Remove a record of an on-memory tree database object. */
tcndbout(TCNDB * ndb,const void * kbuf,int ksiz)4215 bool tcndbout(TCNDB *ndb, const void *kbuf, int ksiz){
4216 assert(ndb && kbuf && ksiz >= 0);
4217 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false;
4218 bool rv = tctreeout(ndb->tree, kbuf, ksiz);
4219 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4220 return rv;
4221 }
4222
4223
4224 /* Remove a string record of an on-memory tree database object. */
tcndbout2(TCNDB * ndb,const char * kstr)4225 bool tcndbout2(TCNDB *ndb, const char *kstr){
4226 assert(ndb && kstr);
4227 return tcndbout(ndb, kstr, strlen(kstr));
4228 }
4229
4230
4231 /* Retrieve a record in an on-memory tree database object. */
tcndbget(TCNDB * ndb,const void * kbuf,int ksiz,int * sp)4232 void *tcndbget(TCNDB *ndb, const void *kbuf, int ksiz, int *sp){
4233 assert(ndb && kbuf && ksiz >= 0 && sp);
4234 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return NULL;
4235 int vsiz;
4236 const char *vbuf = tctreeget(ndb->tree, kbuf, ksiz, &vsiz);
4237 char *rv;
4238 if(vbuf){
4239 TCMEMDUP(rv, vbuf, vsiz);
4240 *sp = vsiz;
4241 } else {
4242 rv = NULL;
4243 }
4244 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4245 return rv;
4246 }
4247
4248
4249 /* Retrieve a string record in an on-memory tree database object. */
tcndbget2(TCNDB * ndb,const char * kstr)4250 char *tcndbget2(TCNDB *ndb, const char *kstr){
4251 assert(ndb && kstr);
4252 int vsiz;
4253 return tcndbget(ndb, kstr, strlen(kstr), &vsiz);
4254 }
4255
4256
4257 /* Get the size of the value of a record in an on-memory tree database object. */
tcndbvsiz(TCNDB * ndb,const void * kbuf,int ksiz)4258 int tcndbvsiz(TCNDB *ndb, const void *kbuf, int ksiz){
4259 assert(ndb && kbuf && ksiz >= 0);
4260 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return -1;
4261 int vsiz;
4262 const char *vbuf = tctreeget(ndb->tree, kbuf, ksiz, &vsiz);
4263 if(!vbuf) vsiz = -1;
4264 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4265 return vsiz;
4266 }
4267
4268
4269 /* Get the size of the value of a string record in an on-memory tree database object. */
tcndbvsiz2(TCNDB * ndb,const char * kstr)4270 int tcndbvsiz2(TCNDB *ndb, const char *kstr){
4271 assert(ndb && kstr);
4272 return tcndbvsiz(ndb, kstr, strlen(kstr));
4273 }
4274
4275
4276 /* Initialize the iterator of an on-memory tree database object. */
tcndbiterinit(TCNDB * ndb)4277 void tcndbiterinit(TCNDB *ndb){
4278 assert(ndb);
4279 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4280 tctreeiterinit(ndb->tree);
4281 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4282 }
4283
4284
4285 /* Get the next key of the iterator of an on-memory tree database object. */
tcndbiternext(TCNDB * ndb,int * sp)4286 void *tcndbiternext(TCNDB *ndb, int *sp){
4287 assert(ndb && sp);
4288 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return NULL;
4289 int ksiz;
4290 const char *kbuf = tctreeiternext(ndb->tree, &ksiz);
4291 char *rv;
4292 if(kbuf){
4293 TCMEMDUP(rv, kbuf, ksiz);
4294 *sp = ksiz;
4295 } else {
4296 rv = NULL;
4297 }
4298 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4299 return rv;
4300 }
4301
4302
4303 /* Get the next key string of the iterator of an on-memory tree database object. */
tcndbiternext2(TCNDB * ndb)4304 char *tcndbiternext2(TCNDB *ndb){
4305 assert(ndb);
4306 int ksiz;
4307 return tcndbiternext(ndb, &ksiz);
4308 }
4309
4310
4311 /* Get forward matching keys in an on-memory tree database object. */
tcndbfwmkeys(TCNDB * ndb,const void * pbuf,int psiz,int max)4312 TCLIST *tcndbfwmkeys(TCNDB *ndb, const void *pbuf, int psiz, int max){
4313 assert(ndb && pbuf && psiz >= 0);
4314 TCLIST* keys = tclistnew();
4315 if(pthread_mutex_lock(ndb->mmtx) != 0) return keys;
4316 if(max < 0) max = INT_MAX;
4317 TCTREE *tree = ndb->tree;
4318 TCTREEREC *cur = tree->cur;
4319 tctreeiterinit2(tree, pbuf, psiz);
4320 const char *lbuf = NULL;
4321 int lsiz = 0;
4322 const char *kbuf;
4323 int ksiz;
4324 while(TCLISTNUM(keys) < max && (kbuf = tctreeiternext(tree, &ksiz)) != NULL){
4325 if(ksiz < psiz || memcmp(kbuf, pbuf, psiz)) break;
4326 if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){
4327 TCLISTPUSH(keys, kbuf, ksiz);
4328 if(TCLISTNUM(keys) >= max) break;
4329 lbuf = kbuf;
4330 lsiz = ksiz;
4331 }
4332 }
4333 tree->cur = cur;
4334 pthread_mutex_unlock(ndb->mmtx);
4335 return keys;
4336 }
4337
4338
4339 /* Get forward matching string keys in an on-memory tree database object. */
tcndbfwmkeys2(TCNDB * ndb,const char * pstr,int max)4340 TCLIST *tcndbfwmkeys2(TCNDB *ndb, const char *pstr, int max){
4341 assert(ndb && pstr);
4342 return tcndbfwmkeys(ndb, pstr, strlen(pstr), max);
4343 }
4344
4345
4346 /* Get the number of records stored in an on-memory tree database object. */
tcndbrnum(TCNDB * ndb)4347 uint64_t tcndbrnum(TCNDB *ndb){
4348 assert(ndb);
4349 return tctreernum(ndb->tree);
4350 }
4351
4352
4353 /* Get the total size of memory used in an on-memory tree database object. */
tcndbmsiz(TCNDB * ndb)4354 uint64_t tcndbmsiz(TCNDB *ndb){
4355 assert(ndb);
4356 return tctreemsiz(ndb->tree);
4357 }
4358
4359
4360 /* Add an integer to a record in an on-memory tree database object. */
tcndbaddint(TCNDB * ndb,const void * kbuf,int ksiz,int num)4361 int tcndbaddint(TCNDB *ndb, const void *kbuf, int ksiz, int num){
4362 assert(ndb && kbuf && ksiz >= 0);
4363 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return INT_MIN;
4364 int rv = tctreeaddint(ndb->tree, kbuf, ksiz, num);
4365 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4366 return rv;
4367 }
4368
4369
4370 /* Add a real number to a record in an on-memory tree database object. */
tcndbadddouble(TCNDB * ndb,const void * kbuf,int ksiz,double num)4371 double tcndbadddouble(TCNDB *ndb, const void *kbuf, int ksiz, double num){
4372 assert(ndb && kbuf && ksiz >= 0);
4373 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return nan("");
4374 double rv = tctreeadddouble(ndb->tree, kbuf, ksiz, num);
4375 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4376 return rv;
4377 }
4378
4379
4380 /* Clear an on-memory tree database object. */
tcndbvanish(TCNDB * ndb)4381 void tcndbvanish(TCNDB *ndb){
4382 assert(ndb);
4383 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0);
4384 tctreeclear(ndb->tree);
4385 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4386 }
4387
4388
4389 /* Remove fringe records of an on-memory tree database object. */
tcndbcutfringe(TCNDB * ndb,int num)4390 void tcndbcutfringe(TCNDB *ndb, int num){
4391 assert(ndb && num >= 0);
4392 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0);
4393 tctreecutfringe(ndb->tree, num);
4394 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4395 }
4396
4397
4398
4399 /*************************************************************************************************
4400 * ordered tree (for experts)
4401 *************************************************************************************************/
4402
4403
4404 /* Store a record into a on-memory tree database without balancing nodes. */
tcndbput3(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4405 void tcndbput3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4406 assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4407 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4408 tctreeput3(ndb->tree, kbuf, ksiz, vbuf, vsiz);
4409 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4410 }
4411
4412
4413 /* Store a new record into a on-memory tree database object without balancing nodes. */
tcndbputkeep3(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4414 bool tcndbputkeep3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4415 assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4416 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false;
4417 bool rv = tctreeputkeep3(ndb->tree, kbuf, ksiz, vbuf, vsiz);
4418 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4419 return rv;
4420 }
4421
4422
4423 /* Concatenate a value in a on-memory tree database without balancing nodes. */
tcndbputcat3(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)4424 void tcndbputcat3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
4425 assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
4426 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4427 tctreeputcat3(ndb->tree, kbuf, ksiz, vbuf, vsiz);
4428 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4429 }
4430
4431
4432 /* Store a record into a on-memory tree database object with a duplication handler. */
tcndbputproc(TCNDB * ndb,const void * kbuf,int ksiz,const void * vbuf,int vsiz,TCPDPROC proc,void * op)4433 bool tcndbputproc(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
4434 TCPDPROC proc, void *op){
4435 assert(ndb && kbuf && ksiz >= 0 && proc);
4436 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false;
4437 bool rv = tctreeputproc(ndb->tree, kbuf, ksiz, vbuf, vsiz, proc, op);
4438 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4439 return rv;
4440 }
4441
4442
4443 /* Retrieve a record in an on-memory tree database object without balancing nodes. */
tcndbget3(TCNDB * ndb,const void * kbuf,int ksiz,int * sp)4444 void *tcndbget3(TCNDB *ndb, const void *kbuf, int ksiz, int *sp){
4445 assert(ndb && kbuf && ksiz >= 0 && sp);
4446 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return NULL;
4447 int vsiz;
4448 const char *vbuf = tctreeget3(ndb->tree, kbuf, ksiz, &vsiz);
4449 char *rv;
4450 if(vbuf){
4451 TCMEMDUP(rv, vbuf, vsiz);
4452 *sp = vsiz;
4453 } else {
4454 rv = NULL;
4455 }
4456 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4457 return rv;
4458 }
4459
4460
4461 /* Initialize the iterator of an on-memory tree database object in front of a key. */
tcndbiterinit2(TCNDB * ndb,const void * kbuf,int ksiz)4462 void tcndbiterinit2(TCNDB *ndb, const void *kbuf, int ksiz){
4463 assert(ndb && kbuf && ksiz >= 0);
4464 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4465 tctreeiterinit2(ndb->tree, kbuf, ksiz);
4466 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4467 }
4468
4469
4470 /* Initialize the iterator of an on-memory tree database object in front of a key string. */
tcndbiterinit3(TCNDB * ndb,const char * kstr)4471 void tcndbiterinit3(TCNDB *ndb, const char *kstr){
4472 assert(ndb && kstr);
4473 tcndbiterinit2(ndb, kstr, strlen(kstr));
4474 }
4475
4476
4477 /* Process each record atomically of an on-memory tree database object. */
tcndbforeach(TCNDB * ndb,TCITER iter,void * op)4478 void tcndbforeach(TCNDB *ndb, TCITER iter, void *op){
4479 assert(ndb && iter);
4480 if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return;
4481 TCTREE *tree = ndb->tree;
4482 TCTREEREC *cur = tree->cur;
4483 tctreeiterinit(tree);
4484 int ksiz;
4485 const char *kbuf;
4486 while((kbuf = tctreeiternext(tree, &ksiz)) != NULL){
4487 int vsiz;
4488 const char *vbuf = tctreeiterval(kbuf, &vsiz);
4489 if(!iter(kbuf, ksiz, vbuf, vsiz, op)) break;
4490 }
4491 tree->cur = cur;
4492 pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx);
4493 }
4494
4495
4496
4497 /*************************************************************************************************
4498 * memory pool
4499 *************************************************************************************************/
4500
4501
4502 #define TCMPOOLUNIT 128 // allocation unit size of memory pool elements
4503
4504
4505 /* Global memory pool object. */
4506 TCMPOOL *tcglobalmemorypool = NULL;
4507
4508
4509 /* private function prototypes */
4510 static void tcmpooldelglobal(void);
4511
4512
4513 /* Create a memory pool object. */
tcmpoolnew(void)4514 TCMPOOL *tcmpoolnew(void){
4515 TCMPOOL *mpool;
4516 TCMALLOC(mpool, sizeof(*mpool));
4517 TCMALLOC(mpool->mutex, sizeof(pthread_mutex_t));
4518 if(pthread_mutex_init(mpool->mutex, NULL) != 0) tcmyfatal("locking failed");
4519 mpool->anum = TCMPOOLUNIT;
4520 TCMALLOC(mpool->elems, sizeof(mpool->elems[0]) * mpool->anum);
4521 mpool->num = 0;
4522 return mpool;
4523 }
4524
4525
4526 /* Delete a memory pool object. */
tcmpooldel(TCMPOOL * mpool)4527 void tcmpooldel(TCMPOOL *mpool){
4528 assert(mpool);
4529 TCMPELEM *elems = mpool->elems;
4530 for(int i = mpool->num - 1; i >= 0; i--){
4531 elems[i].del(elems[i].ptr);
4532 }
4533 TCFREE(elems);
4534 pthread_mutex_destroy(mpool->mutex);
4535 TCFREE(mpool->mutex);
4536 TCFREE(mpool);
4537 }
4538
4539
4540 /* Relegate an arbitrary object to a memory pool object. */
tcmpoolpush(TCMPOOL * mpool,void * ptr,void (* del)(void *))4541 void *tcmpoolpush(TCMPOOL *mpool, void *ptr, void (*del)(void *)){
4542 assert(mpool && del);
4543 if(!ptr) return NULL;
4544 if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed");
4545 int num = mpool->num;
4546 if(num >= mpool->anum){
4547 mpool->anum *= 2;
4548 TCREALLOC(mpool->elems, mpool->elems, mpool->anum * sizeof(mpool->elems[0]));
4549 }
4550 mpool->elems[num].ptr = ptr;
4551 mpool->elems[num].del = del;
4552 mpool->num++;
4553 pthread_mutex_unlock(mpool->mutex);
4554 return ptr;
4555 }
4556
4557
4558 /* Relegate an allocated region to a memory pool object. */
tcmpoolpushptr(TCMPOOL * mpool,void * ptr)4559 void *tcmpoolpushptr(TCMPOOL *mpool, void *ptr){
4560 assert(mpool);
4561 return tcmpoolpush(mpool, ptr, (void (*)(void *))free);
4562 }
4563
4564
4565 /* Relegate an extensible string object to a memory pool object. */
tcmpoolpushxstr(TCMPOOL * mpool,TCXSTR * xstr)4566 TCXSTR *tcmpoolpushxstr(TCMPOOL *mpool, TCXSTR *xstr){
4567 assert(mpool);
4568 return tcmpoolpush(mpool, xstr, (void (*)(void *))tcxstrdel);
4569 }
4570
4571
4572 /* Relegate a list object to a memory pool object. */
tcmpoolpushlist(TCMPOOL * mpool,TCLIST * list)4573 TCLIST *tcmpoolpushlist(TCMPOOL *mpool, TCLIST *list){
4574 assert(mpool);
4575 return tcmpoolpush(mpool, list, (void (*)(void *))tclistdel);
4576 }
4577
4578
4579 /* Relegate a map object to a memory pool object. */
tcmpoolpushmap(TCMPOOL * mpool,TCMAP * map)4580 TCMAP *tcmpoolpushmap(TCMPOOL *mpool, TCMAP *map){
4581 assert(mpool);
4582 return tcmpoolpush(mpool, map, (void (*)(void *))tcmapdel);
4583 }
4584
4585
4586 /* Relegate a tree object to a memory pool object. */
tcmpoolpushtree(TCMPOOL * mpool,TCTREE * tree)4587 TCTREE *tcmpoolpushtree(TCMPOOL *mpool, TCTREE *tree){
4588 assert(mpool);
4589 return tcmpoolpush(mpool, tree, (void (*)(void *))tctreedel);
4590 }
4591
4592
4593 /* Allocate a region relegated to a memory pool object. */
tcmpoolmalloc(TCMPOOL * mpool,size_t size)4594 void *tcmpoolmalloc(TCMPOOL *mpool, size_t size){
4595 assert(mpool && size > 0);
4596 void *ptr;
4597 TCMALLOC(ptr, size);
4598 tcmpoolpush(mpool, ptr, (void (*)(void *))free);
4599 return ptr;
4600 }
4601
4602
4603 /* Create an extensible string object relegated to a memory pool object. */
tcmpoolxstrnew(TCMPOOL * mpool)4604 TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool){
4605 assert(mpool);
4606 TCXSTR *xstr = tcxstrnew();
4607 tcmpoolpush(mpool, xstr, (void (*)(void *))tcxstrdel);
4608 return xstr;
4609 }
4610
4611
4612 /* Create a list object relegated to a memory pool object. */
tcmpoollistnew(TCMPOOL * mpool)4613 TCLIST *tcmpoollistnew(TCMPOOL *mpool){
4614 assert(mpool);
4615 TCLIST *list = tclistnew();
4616 tcmpoolpush(mpool, list, (void (*)(void *))tclistdel);
4617 return list;
4618 }
4619
4620
4621 /* Create a map object relegated to a memory pool object. */
tcmpoolmapnew(TCMPOOL * mpool)4622 TCMAP *tcmpoolmapnew(TCMPOOL *mpool){
4623 assert(mpool);
4624 TCMAP *map = tcmapnew();
4625 tcmpoolpush(mpool, map, (void (*)(void *))tcmapdel);
4626 return map;
4627 }
4628
4629
4630 /* Create a tree object relegated to a memory pool object. */
tcmpooltreenew(TCMPOOL * mpool)4631 TCTREE *tcmpooltreenew(TCMPOOL *mpool){
4632 assert(mpool);
4633 TCTREE *tree = tctreenew();
4634 tcmpoolpush(mpool, tree, (void (*)(void *))tctreedel);
4635 return tree;
4636 }
4637
4638
4639 /* Remove the most recently installed cleanup handler of a memory pool object. */
tcmpoolpop(TCMPOOL * mpool,bool exe)4640 void tcmpoolpop(TCMPOOL *mpool, bool exe){
4641 assert(mpool);
4642 if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed");
4643 if(mpool->num > 0){
4644 mpool->num--;
4645 if(exe) mpool->elems[mpool->num].del(mpool->elems[mpool->num].ptr);
4646 }
4647 pthread_mutex_unlock(mpool->mutex);
4648 }
4649
4650
4651 /* Remove all cleanup handler of a memory pool object.
4652 `mpool' specifies the memory pool object. */
tcmpoolclear(TCMPOOL * mpool,bool exe)4653 void tcmpoolclear(TCMPOOL *mpool, bool exe){
4654 assert(mpool);
4655 if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed");
4656 if(exe){
4657 for(int i = mpool->num - 1; i >= 0; i--){
4658 mpool->elems[i].del(mpool->elems[i].ptr);
4659 }
4660 }
4661 mpool->num = 0;
4662 pthread_mutex_unlock(mpool->mutex);
4663 }
4664
4665
4666 /* Get the global memory pool object. */
tcmpoolglobal(void)4667 TCMPOOL *tcmpoolglobal(void){
4668 if(tcglobalmemorypool) return tcglobalmemorypool;
4669 tcglobalmemorypool = tcmpoolnew();
4670 atexit(tcmpooldelglobal);
4671 return tcglobalmemorypool;
4672 }
4673
4674
4675 /* Detete the global memory pool object. */
tcmpooldelglobal(void)4676 static void tcmpooldelglobal(void){
4677 if(tcglobalmemorypool) tcmpooldel(tcglobalmemorypool);
4678 }
4679
4680
4681
4682 /*************************************************************************************************
4683 * miscellaneous utilities
4684 *************************************************************************************************/
4685
4686
4687 #define TCRANDDEV "/dev/urandom" // path of the random device file
4688 #define TCDISTMAXLEN 4096 // maximum size of a string for distance checking
4689 #define TCDISTBUFSIZ 16384 // size of a distance buffer
4690 #define TCLDBLCOLMAX 16 // maximum number of columns of the long double
4691
4692
4693 /* File descriptor of random number generator. */
4694 int tcrandomdevfd = -1;
4695
4696
4697 /* private function prototypes */
4698 static void tcrandomfdclose(void);
4699 static time_t tcmkgmtime(struct tm *tm);
4700
4701
4702 /* Get the larger value of two integers. */
tclmax(long a,long b)4703 long tclmax(long a, long b){
4704 return (a > b) ? a : b;
4705 }
4706
4707
4708 /* Get the lesser value of two integers. */
tclmin(long a,long b)4709 long tclmin(long a, long b){
4710 return (a < b) ? a : b;
4711 }
4712
4713
4714 /* Get a random number as long integer based on uniform distribution. */
tclrand(void)4715 unsigned long tclrand(void){
4716 static uint32_t cnt = 0;
4717 static uint64_t seed = 0;
4718 static uint64_t mask = 0;
4719 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
4720 if((cnt & 0xff) == 0 && pthread_mutex_lock(&mutex) == 0){
4721 if(cnt == 0) seed += time(NULL);
4722 if(tcrandomdevfd == -1 && (tcrandomdevfd = open(TCRANDDEV, O_RDONLY, 00644)) != -1)
4723 atexit(tcrandomfdclose);
4724 if(tcrandomdevfd == -1 || read(tcrandomdevfd, &mask, sizeof(mask)) != sizeof(mask)){
4725 double t = tctime();
4726 uint64_t tmask;
4727 memcpy(&tmask, &t, tclmin(sizeof(t), sizeof(tmask)));
4728 mask = (mask << 8) ^ tmask;
4729 }
4730 pthread_mutex_unlock(&mutex);
4731 }
4732 seed = seed * 123456789012301LL + 211;
4733 uint64_t num = (mask ^ cnt++) ^ seed;
4734 return TCSWAB64(num);
4735 }
4736
4737
4738 /* Get a random number as double decimal based on uniform distribution. */
tcdrand(void)4739 double tcdrand(void){
4740 double val = tclrand() / (double)ULONG_MAX;
4741 return val < 1.0 ? val : 0.0;
4742 }
4743
4744
4745 /* Get a random number as double decimal based on normal distribution. */
tcdrandnd(double avg,double sd)4746 double tcdrandnd(double avg, double sd){
4747 assert(sd >= 0.0);
4748 return sqrt(-2.0 * log(tcdrand())) * cos(2 * 3.141592653589793 * tcdrand()) * sd + avg;
4749 }
4750
4751
4752 /* Compare two strings with case insensitive evaluation. */
tcstricmp(const char * astr,const char * bstr)4753 int tcstricmp(const char *astr, const char *bstr){
4754 assert(astr && bstr);
4755 while(*astr != '\0'){
4756 if(*bstr == '\0') return 1;
4757 int ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr;
4758 int bc = (*bstr >= 'A' && *bstr <= 'Z') ? *bstr + ('a' - 'A') : *(unsigned char *)bstr;
4759 if(ac != bc) return ac - bc;
4760 astr++;
4761 bstr++;
4762 }
4763 return (*bstr == '\0') ? 0 : -1;
4764 }
4765
4766
4767 /* Check whether a string begins with a key. */
tcstrfwm(const char * str,const char * key)4768 bool tcstrfwm(const char *str, const char *key){
4769 assert(str && key);
4770 while(*key != '\0'){
4771 if(*str != *key || *str == '\0') return false;
4772 key++;
4773 str++;
4774 }
4775 return true;
4776 }
4777
4778
4779 /* Check whether a string begins with a key with case insensitive evaluation. */
tcstrifwm(const char * str,const char * key)4780 bool tcstrifwm(const char *str, const char *key){
4781 assert(str && key);
4782 while(*key != '\0'){
4783 if(*str == '\0') return false;
4784 int sc = *str;
4785 if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
4786 int kc = *key;
4787 if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
4788 if(sc != kc) return false;
4789 key++;
4790 str++;
4791 }
4792 return true;
4793 }
4794
4795
4796 /* Check whether a string ends with a key. */
tcstrbwm(const char * str,const char * key)4797 bool tcstrbwm(const char *str, const char *key){
4798 assert(str && key);
4799 int slen = strlen(str);
4800 int klen = strlen(key);
4801 for(int i = 1; i <= klen; i++){
4802 if(i > slen || str[slen-i] != key[klen-i]) return false;
4803 }
4804 return true;
4805 }
4806
4807
4808 /* Check whether a string ends with a key with case insensitive evaluation. */
tcstribwm(const char * str,const char * key)4809 bool tcstribwm(const char *str, const char *key){
4810 assert(str && key);
4811 int slen = strlen(str);
4812 int klen = strlen(key);
4813 for(int i = 1; i <= klen; i++){
4814 if(i > slen) return false;
4815 int sc = str[slen-i];
4816 if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
4817 int kc = key[klen-i];
4818 if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
4819 if(sc != kc) return false;
4820 }
4821 return true;
4822 }
4823
4824
4825 /* Calculate the edit distance of two strings. */
tcstrdist(const char * astr,const char * bstr)4826 int tcstrdist(const char *astr, const char *bstr){
4827 assert(astr && bstr);
4828 int alen = tclmin(strlen(astr), TCDISTMAXLEN);
4829 int blen = tclmin(strlen(bstr), TCDISTMAXLEN);
4830 int dsiz = blen + 1;
4831 int tbuf[TCDISTBUFSIZ];
4832 int *tbl;
4833 if((alen + 1) * dsiz < TCDISTBUFSIZ){
4834 tbl = tbuf;
4835 } else {
4836 TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
4837 }
4838 for(int i = 0; i <= alen; i++){
4839 tbl[i*dsiz] = i;
4840 }
4841 for(int i = 1; i <= blen; i++){
4842 tbl[i] = i;
4843 }
4844 astr--;
4845 bstr--;
4846 for(int i = 1; i <= alen; i++){
4847 for(int j = 1; j <= blen; j++){
4848 int ac = tbl[(i-1)*dsiz+j] + 1;
4849 int bc = tbl[i*dsiz+j-1] + 1;
4850 int cc = tbl[(i-1)*dsiz+j-1] + (astr[i] != bstr[j]);
4851 ac = ac < bc ? ac : bc;
4852 tbl[i*dsiz+j] = ac < cc ? ac : cc;
4853 }
4854 }
4855 int rv = tbl[alen*dsiz+blen];
4856 if(tbl != tbuf) TCFREE(tbl);
4857 return rv;
4858 }
4859
4860
4861 /* Calculate the edit distance of two UTF-8 strings. */
tcstrdistutf(const char * astr,const char * bstr)4862 int tcstrdistutf(const char *astr, const char *bstr){
4863 assert(astr && bstr);
4864 int alen = strlen(astr);
4865 uint16_t abuf[TCDISTBUFSIZ];
4866 uint16_t *aary;
4867 if(alen < TCDISTBUFSIZ){
4868 aary = abuf;
4869 } else {
4870 TCMALLOC(aary, alen * sizeof(*aary));
4871 }
4872 tcstrutftoucs(astr, aary, &alen);
4873 int blen = strlen(bstr);
4874 uint16_t bbuf[TCDISTBUFSIZ];
4875 uint16_t *bary;
4876 if(blen < TCDISTBUFSIZ){
4877 bary = bbuf;
4878 } else {
4879 TCMALLOC(bary, blen * sizeof(*bary));
4880 }
4881 tcstrutftoucs(bstr, bary, &blen);
4882 if(alen > TCDISTMAXLEN) alen = TCDISTMAXLEN;
4883 if(blen > TCDISTMAXLEN) blen = TCDISTMAXLEN;
4884 int dsiz = blen + 1;
4885 int tbuf[TCDISTBUFSIZ];
4886 int *tbl;
4887 if((alen + 1) * dsiz < TCDISTBUFSIZ){
4888 tbl = tbuf;
4889 } else {
4890 TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
4891 }
4892 for(int i = 0; i <= alen; i++){
4893 tbl[i*dsiz] = i;
4894 }
4895 for(int i = 1; i <= blen; i++){
4896 tbl[i] = i;
4897 }
4898 aary--;
4899 bary--;
4900 for(int i = 1; i <= alen; i++){
4901 for(int j = 1; j <= blen; j++){
4902 int ac = tbl[(i-1)*dsiz+j] + 1;
4903 int bc = tbl[i*dsiz+j-1] + 1;
4904 int cc = tbl[(i-1)*dsiz+j-1] + (aary[i] != bary[j]);
4905 ac = ac < bc ? ac : bc;
4906 tbl[i*dsiz+j] = ac < cc ? ac : cc;
4907 }
4908 }
4909 aary++;
4910 bary++;
4911 int rv = tbl[alen*dsiz+blen];
4912 if(tbl != tbuf) TCFREE(tbl);
4913 if(bary != bbuf) TCFREE(bary);
4914 if(aary != abuf) TCFREE(aary);
4915 return rv;
4916 }
4917
4918
4919 /* Convert the letters of a string into upper case. */
tcstrtoupper(char * str)4920 char *tcstrtoupper(char *str){
4921 assert(str);
4922 char *wp = str;
4923 while(*wp != '\0'){
4924 if(*wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A';
4925 wp++;
4926 }
4927 return str;
4928 }
4929
4930
4931 /* Convert the letters of a string into lower case. */
tcstrtolower(char * str)4932 char *tcstrtolower(char *str){
4933 assert(str);
4934 char *wp = str;
4935 while(*wp != '\0'){
4936 if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
4937 wp++;
4938 }
4939 return str;
4940 }
4941
4942
4943 /* Cut space characters at head or tail of a string. */
tcstrtrim(char * str)4944 char *tcstrtrim(char *str){
4945 assert(str);
4946 const char *rp = str;
4947 char *wp = str;
4948 bool head = true;
4949 while(*rp != '\0'){
4950 if(*rp > '\0' && *rp <= ' '){
4951 if(!head) *(wp++) = *rp;
4952 } else {
4953 *(wp++) = *rp;
4954 head = false;
4955 }
4956 rp++;
4957 }
4958 *wp = '\0';
4959 while(wp > str && wp[-1] > '\0' && wp[-1] <= ' '){
4960 *(--wp) = '\0';
4961 }
4962 return str;
4963 }
4964
4965
4966 /* Squeeze space characters in a string and trim it. */
tcstrsqzspc(char * str)4967 char *tcstrsqzspc(char *str){
4968 assert(str);
4969 char *rp = str;
4970 char *wp = str;
4971 bool spc = true;
4972 while(*rp != '\0'){
4973 if(*rp > 0 && *rp <= ' '){
4974 if(!spc) *(wp++) = *rp;
4975 spc = true;
4976 } else {
4977 *(wp++) = *rp;
4978 spc = false;
4979 }
4980 rp++;
4981 }
4982 *wp = '\0';
4983 for(wp--; wp >= str; wp--){
4984 if(*wp > 0 && *wp <= ' '){
4985 *wp = '\0';
4986 } else {
4987 break;
4988 }
4989 }
4990 return str;
4991 }
4992
4993
4994 /* Substitute characters in a string. */
tcstrsubchr(char * str,const char * rstr,const char * sstr)4995 char *tcstrsubchr(char *str, const char *rstr, const char *sstr){
4996 assert(str && rstr && sstr);
4997 int slen = strlen(sstr);
4998 char *wp = str;
4999 for(int i = 0; str[i] != '\0'; i++){
5000 const char *p = strchr(rstr, str[i]);
5001 if(p){
5002 int idx = p - rstr;
5003 if(idx < slen) *(wp++) = sstr[idx];
5004 } else {
5005 *(wp++) = str[i];
5006 }
5007 }
5008 *wp = '\0';
5009 return str;
5010 }
5011
5012
5013 /* Count the number of characters in a string of UTF-8. */
tcstrcntutf(const char * str)5014 int tcstrcntutf(const char *str){
5015 assert(str);
5016 const unsigned char *rp = (unsigned char *)str;
5017 int cnt = 0;
5018 while(*rp != '\0'){
5019 if((*rp & 0x80) == 0x00 || (*rp & 0xe0) == 0xc0 ||
5020 (*rp & 0xf0) == 0xe0 || (*rp & 0xf8) == 0xf0) cnt++;
5021 rp++;
5022 }
5023 return cnt;
5024 }
5025
5026
5027 /* Cut a string of UTF-8 at the specified number of characters. */
tcstrcututf(char * str,int num)5028 char *tcstrcututf(char *str, int num){
5029 assert(str && num >= 0);
5030 unsigned char *wp = (unsigned char *)str;
5031 int cnt = 0;
5032 while(*wp != '\0'){
5033 if((*wp & 0x80) == 0x00 || (*wp & 0xe0) == 0xc0 ||
5034 (*wp & 0xf0) == 0xe0 || (*wp & 0xf8) == 0xf0){
5035 cnt++;
5036 if(cnt > num){
5037 *wp = '\0';
5038 break;
5039 }
5040 }
5041 wp++;
5042 }
5043 return str;
5044 }
5045
5046
5047 /* Convert a UTF-8 string into a UCS-2 array. */
tcstrutftoucs(const char * str,uint16_t * ary,int * np)5048 void tcstrutftoucs(const char *str, uint16_t *ary, int *np){
5049 assert(str && ary && np);
5050 const unsigned char *rp = (unsigned char *)str;
5051 unsigned int wi = 0;
5052 while(*rp != '\0'){
5053 int c = *(unsigned char *)rp;
5054 if(c < 0x80){
5055 ary[wi++] = c;
5056 } else if(c < 0xe0){
5057 if(rp[1] >= 0x80){
5058 ary[wi++] = ((rp[0] & 0x1f) << 6) | (rp[1] & 0x3f);
5059 rp++;
5060 }
5061 } else if(c < 0xf0){
5062 if(rp[1] >= 0x80 && rp[2] >= 0x80){
5063 ary[wi++] = ((rp[0] & 0xf) << 12) | ((rp[1] & 0x3f) << 6) | (rp[2] & 0x3f);
5064 rp += 2;
5065 }
5066 }
5067 rp++;
5068 }
5069 *np = wi;
5070 }
5071
5072
5073 /* Convert a UCS-2 array into a UTF-8 string. */
tcstrucstoutf(const uint16_t * ary,int num,char * str)5074 int tcstrucstoutf(const uint16_t *ary, int num, char *str){
5075 assert(ary && num >= 0 && str);
5076 unsigned char *wp = (unsigned char *)str;
5077 for(int i = 0; i < num; i++){
5078 unsigned int c = ary[i];
5079 if(c < 0x80){
5080 *(wp++) = c;
5081 } else if(c < 0x800){
5082 *(wp++) = 0xc0 | (c >> 6);
5083 *(wp++) = 0x80 | (c & 0x3f);
5084 } else {
5085 *(wp++) = 0xe0 | (c >> 12);
5086 *(wp++) = 0x80 | ((c & 0xfff) >> 6);
5087 *(wp++) = 0x80 | (c & 0x3f);
5088 }
5089 }
5090 *wp = '\0';
5091 return (char *)wp - str;
5092 }
5093
5094
5095 /* Create a list object by splitting a string. */
tcstrsplit(const char * str,const char * delims)5096 TCLIST *tcstrsplit(const char *str, const char *delims){
5097 assert(str && delims);
5098 TCLIST *list = tclistnew();
5099 while(true){
5100 const char *sp = str;
5101 while(*str != '\0' && !strchr(delims, *str)){
5102 str++;
5103 }
5104 TCLISTPUSH(list, sp, str - sp);
5105 if(*str == '\0') break;
5106 str++;
5107 }
5108 return list;
5109 }
5110
5111
5112 /* Create a string by joining all elements of a list object. */
tcstrjoin(const TCLIST * list,char delim)5113 char *tcstrjoin(const TCLIST *list, char delim){
5114 assert(list);
5115 int num = TCLISTNUM(list);
5116 int size = num + 1;
5117 for(int i = 0; i < num; i++){
5118 size += TCLISTVALSIZ(list, i);
5119 }
5120 char *buf;
5121 TCMALLOC(buf, size);
5122 char *wp = buf;
5123 for(int i = 0; i < num; i++){
5124 if(i > 0) *(wp++) = delim;
5125 int vsiz;
5126 const char *vbuf = tclistval(list, i, &vsiz);
5127 memcpy(wp, vbuf, vsiz);
5128 wp += vsiz;
5129 }
5130 *wp = '\0';
5131 return buf;
5132 }
5133
5134
5135 /* Convert a string to an integer. */
tcatoi(const char * str)5136 int64_t tcatoi(const char *str){
5137 assert(str);
5138 while(*str > '\0' && *str <= ' '){
5139 str++;
5140 }
5141 int sign = 1;
5142 int64_t num = 0;
5143 if(*str == '-'){
5144 str++;
5145 sign = -1;
5146 } else if(*str == '+'){
5147 str++;
5148 }
5149 while(*str != '\0'){
5150 if(*str < '0' || *str > '9') break;
5151 num = num * 10 + *str - '0';
5152 str++;
5153 }
5154 return num * sign;
5155 }
5156
5157
5158 /* Convert a string with a metric prefix to an integer. */
tcatoix(const char * str)5159 int64_t tcatoix(const char *str){
5160 assert(str);
5161 while(*str > '\0' && *str <= ' '){
5162 str++;
5163 }
5164 int sign = 1;
5165 if(*str == '-'){
5166 str++;
5167 sign = -1;
5168 } else if(*str == '+'){
5169 str++;
5170 }
5171 long double num = 0;
5172 while(*str != '\0'){
5173 if(*str < '0' || *str > '9') break;
5174 num = num * 10 + *str - '0';
5175 str++;
5176 }
5177 if(*str == '.'){
5178 str++;
5179 long double base = 10;
5180 while(*str != '\0'){
5181 if(*str < '0' || *str > '9') break;
5182 num += (*str - '0') / base;
5183 str++;
5184 base *= 10;
5185 }
5186 }
5187 num *= sign;
5188 while(*str > '\0' && *str <= ' '){
5189 str++;
5190 }
5191 if(*str == 'k' || *str == 'K'){
5192 num *= 1LL << 10;
5193 } else if(*str == 'm' || *str == 'M'){
5194 num *= 1LL << 20;
5195 } else if(*str == 'g' || *str == 'G'){
5196 num *= 1LL << 30;
5197 } else if(*str == 't' || *str == 'T'){
5198 num *= 1LL << 40;
5199 } else if(*str == 'p' || *str == 'P'){
5200 num *= 1LL << 50;
5201 } else if(*str == 'e' || *str == 'E'){
5202 num *= 1LL << 60;
5203 }
5204 if(num > INT64_MAX) return INT64_MAX;
5205 if(num < INT64_MIN) return INT64_MIN;
5206 return num;
5207 }
5208
5209
5210 /* Convert a string to a real number. */
tcatof(const char * str)5211 double tcatof(const char *str){
5212 assert(str);
5213 while(*str > '\0' && *str <= ' '){
5214 str++;
5215 }
5216 int sign = 1;
5217 if(*str == '-'){
5218 str++;
5219 sign = -1;
5220 } else if(*str == '+'){
5221 str++;
5222 }
5223 if(tcstrifwm(str, "inf")) return HUGE_VAL * sign;
5224 if(tcstrifwm(str, "nan")) return nan("");
5225 long double num = 0;
5226 int col = 0;
5227 while(*str != '\0'){
5228 if(*str < '0' || *str > '9') break;
5229 num = num * 10 + *str - '0';
5230 str++;
5231 if(num > 0) col++;
5232 }
5233 if(*str == '.'){
5234 str++;
5235 long double fract = 0.0;
5236 long double base = 10;
5237 while(col < TCLDBLCOLMAX && *str != '\0'){
5238 if(*str < '0' || *str > '9') break;
5239 fract += (*str - '0') / base;
5240 str++;
5241 col++;
5242 base *= 10;
5243 }
5244 num += fract;
5245 }
5246 if(*str == 'e' || *str == 'E'){
5247 str++;
5248 num *= pow(10, tcatoi(str));
5249 }
5250 return num * sign;
5251 }
5252
5253
5254 /* Check whether a string matches a regular expression. */
tcregexmatch(const char * str,const char * regex)5255 bool tcregexmatch(const char *str, const char *regex){
5256 assert(str && regex);
5257 int options = REG_EXTENDED | REG_NOSUB;
5258 if(*regex == '*'){
5259 options |= REG_ICASE;
5260 regex++;
5261 }
5262 regex_t rbuf;
5263 if(regcomp(&rbuf, regex, options) != 0) return false;
5264 bool rv = regexec(&rbuf, str, 0, NULL, 0) == 0;
5265 regfree(&rbuf);
5266 return rv;
5267 }
5268
5269
5270 /* Replace each substring matching a regular expression string. */
tcregexreplace(const char * str,const char * regex,const char * alt)5271 char *tcregexreplace(const char *str, const char *regex, const char *alt){
5272 assert(str && regex && alt);
5273 int options = REG_EXTENDED;
5274 if(*regex == '*'){
5275 options |= REG_ICASE;
5276 regex++;
5277 }
5278 regex_t rbuf;
5279 if(regex[0] == '\0' || regcomp(&rbuf, regex, options) != 0) return tcstrdup(str);
5280 regmatch_t subs[256];
5281 if(regexec(&rbuf, str, 32, subs, 0) != 0){
5282 regfree(&rbuf);
5283 return tcstrdup(str);
5284 }
5285 const char *sp = str;
5286 TCXSTR *xstr = tcxstrnew();
5287 bool first = true;
5288 while(sp[0] != '\0' && regexec(&rbuf, sp, 10, subs, first ? 0 : REG_NOTBOL) == 0){
5289 first = false;
5290 if(subs[0].rm_so == -1) break;
5291 tcxstrcat(xstr, sp, subs[0].rm_so);
5292 for(const char *rp = alt; *rp != '\0'; rp++){
5293 if(*rp == '\\'){
5294 if(rp[1] >= '0' && rp[1] <= '9'){
5295 int num = rp[1] - '0';
5296 if(subs[num].rm_so != -1 && subs[num].rm_eo != -1)
5297 tcxstrcat(xstr, sp + subs[num].rm_so, subs[num].rm_eo - subs[num].rm_so);
5298 ++rp;
5299 } else if(rp[1] != '\0'){
5300 tcxstrcat(xstr, ++rp, 1);
5301 }
5302 } else if(*rp == '&'){
5303 tcxstrcat(xstr, sp + subs[0].rm_so, subs[0].rm_eo - subs[0].rm_so);
5304 } else {
5305 tcxstrcat(xstr, rp, 1);
5306 }
5307 }
5308 sp += subs[0].rm_eo;
5309 if(subs[0].rm_eo < 1) break;
5310 }
5311 tcxstrcat2(xstr, sp);
5312 regfree(&rbuf);
5313 return tcxstrtomalloc(xstr);
5314 }
5315
5316
5317 /* Get the MD5 hash value of a serial object. */
tcmd5hash(const void * ptr,int size,char * buf)5318 void tcmd5hash(const void *ptr, int size, char *buf){
5319 assert(ptr && size >= 0 && buf);
5320 int i;
5321 md5_state_t ms;
5322 md5_init(&ms);
5323 md5_append(&ms, (md5_byte_t *)ptr, size);
5324 unsigned char digest[16];
5325 md5_finish(&ms, (md5_byte_t *)digest);
5326 char *wp = buf;
5327 for(i = 0; i < 16; i++){
5328 wp += sprintf(wp, "%02x", digest[i]);
5329 }
5330 *wp = '\0';
5331 }
5332
5333
5334 /* Cipher or decipher a serial object with the Arcfour stream cipher. */
tcarccipher(const void * ptr,int size,const void * kbuf,int ksiz,void * obuf)5335 void tcarccipher(const void *ptr, int size, const void *kbuf, int ksiz, void *obuf){
5336 assert(ptr && size >= 0 && kbuf && ksiz >= 0 && obuf);
5337 if(ksiz < 1){
5338 kbuf = "";
5339 ksiz = 1;
5340 }
5341 uint32_t sbox[0x100], kbox[0x100];
5342 for(int i = 0; i < 0x100; i++){
5343 sbox[i] = i;
5344 kbox[i] = ((uint8_t *)kbuf)[i%ksiz];
5345 }
5346 int sidx = 0;
5347 for(int i = 0; i < 0x100; i++){
5348 sidx = (sidx + sbox[i] + kbox[i]) & 0xff;
5349 uint32_t swap = sbox[i];
5350 sbox[i] = sbox[sidx];
5351 sbox[sidx] = swap;
5352 }
5353 int x = 0;
5354 int y = 0;
5355 for(int i = 0; i < size; i++){
5356 x = (x + 1) & 0xff;
5357 y = (y + sbox[x]) & 0xff;
5358 int32_t swap = sbox[x];
5359 sbox[x] = sbox[y];
5360 sbox[y] = swap;
5361 ((uint8_t *)obuf)[i] = ((uint8_t *)ptr)[i] ^ sbox[(sbox[x]+sbox[y])&0xff];
5362 }
5363 }
5364
5365
5366 /* Get the time of day in seconds. */
tctime(void)5367 double tctime(void){
5368 struct timeval tv;
5369 if(gettimeofday(&tv, NULL) == -1) return 0.0;
5370 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
5371 }
5372
5373
5374 /* Get the Gregorian calendar of a time. */
tccalendar(int64_t t,int jl,int * yearp,int * monp,int * dayp,int * hourp,int * minp,int * secp)5375 void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp,
5376 int *hourp, int *minp, int *secp){
5377 if(t == INT64_MAX) t = time(NULL);
5378 if(jl == INT_MAX) jl = tcjetlag();
5379 time_t tt = (time_t)t + jl;
5380 struct tm ts;
5381 if(!gmtime_r(&tt, &ts)){
5382 if(yearp) *yearp = 0;
5383 if(monp) *monp = 0;
5384 if(dayp) *dayp = 0;
5385 if(hourp) *hourp = 0;
5386 if(minp) *minp = 0;
5387 if(secp) *secp = 0;
5388 }
5389 if(yearp) *yearp = ts.tm_year + 1900;
5390 if(monp) *monp = ts.tm_mon + 1;
5391 if(dayp) *dayp = ts.tm_mday;
5392 if(hourp) *hourp = ts.tm_hour;
5393 if(minp) *minp = ts.tm_min;
5394 if(secp) *secp = ts.tm_sec;
5395 }
5396
5397
5398 /* Format a date as a string in W3CDTF. */
tcdatestrwww(int64_t t,int jl,char * buf)5399 void tcdatestrwww(int64_t t, int jl, char *buf){
5400 assert(buf);
5401 if(t == INT64_MAX) t = time(NULL);
5402 if(jl == INT_MAX) jl = tcjetlag();
5403 time_t tt = (time_t)t + jl;
5404 struct tm ts;
5405 if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
5406 ts.tm_year += 1900;
5407 ts.tm_mon += 1;
5408 jl /= 60;
5409 char tzone[16];
5410 if(jl == 0){
5411 sprintf(tzone, "Z");
5412 } else if(jl < 0){
5413 jl *= -1;
5414 sprintf(tzone, "-%02d:%02d", jl / 60, jl % 60);
5415 } else {
5416 sprintf(tzone, "+%02d:%02d", jl / 60, jl % 60);
5417 }
5418 sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%s",
5419 ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzone);
5420 }
5421
5422
5423 /* Format a date as a string in RFC 1123 format. */
tcdatestrhttp(int64_t t,int jl,char * buf)5424 void tcdatestrhttp(int64_t t, int jl, char *buf){
5425 assert(buf);
5426 if(t == INT64_MAX) t = time(NULL);
5427 if(jl == INT_MAX) jl = tcjetlag();
5428 time_t tt = (time_t)t + jl;
5429 struct tm ts;
5430 if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
5431 ts.tm_year += 1900;
5432 ts.tm_mon += 1;
5433 jl /= 60;
5434 char *wp = buf;
5435 switch(tcdayofweek(ts.tm_year, ts.tm_mon, ts.tm_mday)){
5436 case 0: wp += sprintf(wp, "Sun, "); break;
5437 case 1: wp += sprintf(wp, "Mon, "); break;
5438 case 2: wp += sprintf(wp, "Tue, "); break;
5439 case 3: wp += sprintf(wp, "Wed, "); break;
5440 case 4: wp += sprintf(wp, "Thu, "); break;
5441 case 5: wp += sprintf(wp, "Fri, "); break;
5442 case 6: wp += sprintf(wp, "Sat, "); break;
5443 }
5444 wp += sprintf(wp, "%02d ", ts.tm_mday);
5445 switch(ts.tm_mon){
5446 case 1: wp += sprintf(wp, "Jan "); break;
5447 case 2: wp += sprintf(wp, "Feb "); break;
5448 case 3: wp += sprintf(wp, "Mar "); break;
5449 case 4: wp += sprintf(wp, "Apr "); break;
5450 case 5: wp += sprintf(wp, "May "); break;
5451 case 6: wp += sprintf(wp, "Jun "); break;
5452 case 7: wp += sprintf(wp, "Jul "); break;
5453 case 8: wp += sprintf(wp, "Aug "); break;
5454 case 9: wp += sprintf(wp, "Sep "); break;
5455 case 10: wp += sprintf(wp, "Oct "); break;
5456 case 11: wp += sprintf(wp, "Nov "); break;
5457 case 12: wp += sprintf(wp, "Dec "); break;
5458 }
5459 wp += sprintf(wp, "%04d %02d:%02d:%02d ", ts.tm_year, ts.tm_hour, ts.tm_min, ts.tm_sec);
5460 if(jl == 0){
5461 sprintf(wp, "GMT");
5462 } else if(jl < 0){
5463 jl *= -1;
5464 sprintf(wp, "-%02d%02d", jl / 60, jl % 60);
5465 } else {
5466 sprintf(wp, "+%02d%02d", jl / 60, jl % 60);
5467 }
5468 }
5469
5470
5471 /* Get the time value of a date string. */
tcstrmktime(const char * str)5472 int64_t tcstrmktime(const char *str){
5473 assert(str);
5474 while(*str > '\0' && *str <= ' '){
5475 str++;
5476 }
5477 if(*str == '\0') return INT64_MIN;
5478 if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) return tcatoih(str + 2);
5479 struct tm ts;
5480 memset(&ts, 0, sizeof(ts));
5481 ts.tm_year = 70;
5482 ts.tm_mon = 0;
5483 ts.tm_mday = 1;
5484 ts.tm_hour = 0;
5485 ts.tm_min = 0;
5486 ts.tm_sec = 0;
5487 ts.tm_isdst = 0;
5488 int len = strlen(str);
5489 time_t t = (time_t)tcatoi(str);
5490 const char *pv = str;
5491 while(*pv >= '0' && *pv <= '9'){
5492 pv++;
5493 }
5494 while(*pv > '\0' && *pv <= ' '){
5495 pv++;
5496 }
5497 if(*pv == '\0') return (int64_t)t;
5498 if((pv[0] == 's' || pv[0] == 'S') && pv[1] >= '\0' && pv[1] <= ' ')
5499 return (int64_t)t;
5500 if((pv[0] == 'm' || pv[0] == 'M') && pv[1] >= '\0' && pv[1] <= ' ')
5501 return (int64_t)t * 60;
5502 if((pv[0] == 'h' || pv[0] == 'H') && pv[1] >= '\0' && pv[1] <= ' ')
5503 return (int64_t)t * 60 * 60;
5504 if((pv[0] == 'd' || pv[0] == 'D') && pv[1] >= '\0' && pv[1] <= ' ')
5505 return (int64_t)t * 60 * 60 * 24;
5506 if(len > 4 && str[4] == '-'){
5507 ts.tm_year = tcatoi(str) - 1900;
5508 if((pv = strchr(str, '-')) != NULL && pv - str == 4){
5509 const char *rp = pv + 1;
5510 ts.tm_mon = tcatoi(rp) - 1;
5511 if((pv = strchr(rp, '-')) != NULL && pv - str == 7){
5512 rp = pv + 1;
5513 ts.tm_mday = tcatoi(rp);
5514 if((pv = strchr(rp, 'T')) != NULL && pv - str == 10){
5515 rp = pv + 1;
5516 ts.tm_hour = tcatoi(rp);
5517 if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
5518 rp = pv + 1;
5519 ts.tm_min = tcatoi(rp);
5520 }
5521 if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
5522 rp = pv + 1;
5523 ts.tm_sec = tcatoi(rp);
5524 }
5525 if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
5526 pv = rp;
5527 while(*pv >= '0' && *pv <= '9'){
5528 pv++;
5529 }
5530 if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
5531 ts.tm_sec -= (tcatoi(pv + 1) * 3600 + tcatoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
5532 }
5533 }
5534 }
5535 return (int64_t)tcmkgmtime(&ts);
5536 }
5537 if(len > 4 && str[4] == '/'){
5538 ts.tm_year = tcatoi(str) - 1900;
5539 if((pv = strchr(str, '/')) != NULL && pv - str == 4){
5540 const char *rp = pv + 1;
5541 ts.tm_mon = tcatoi(rp) - 1;
5542 if((pv = strchr(rp, '/')) != NULL && pv - str == 7){
5543 rp = pv + 1;
5544 ts.tm_mday = tcatoi(rp);
5545 if((pv = strchr(rp, ' ')) != NULL && pv - str == 10){
5546 rp = pv + 1;
5547 ts.tm_hour = tcatoi(rp);
5548 if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
5549 rp = pv + 1;
5550 ts.tm_min = tcatoi(rp);
5551 }
5552 if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
5553 rp = pv + 1;
5554 ts.tm_sec = tcatoi(rp);
5555 }
5556 if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
5557 pv = rp;
5558 while(*pv >= '0' && *pv <= '9'){
5559 pv++;
5560 }
5561 if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
5562 ts.tm_sec -= (tcatoi(pv + 1) * 3600 + tcatoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
5563 }
5564 }
5565 }
5566 return (int64_t)tcmkgmtime(&ts);
5567 }
5568 const char *crp = str;
5569 if(len >= 4 && str[3] == ',') crp = str + 4;
5570 while(*crp == ' '){
5571 crp++;
5572 }
5573 ts.tm_mday = tcatoi(crp);
5574 while((*crp >= '0' && *crp <= '9') || *crp == ' '){
5575 crp++;
5576 }
5577 if(tcstrifwm(crp, "Jan")){
5578 ts.tm_mon = 0;
5579 } else if(tcstrifwm(crp, "Feb")){
5580 ts.tm_mon = 1;
5581 } else if(tcstrifwm(crp, "Mar")){
5582 ts.tm_mon = 2;
5583 } else if(tcstrifwm(crp, "Apr")){
5584 ts.tm_mon = 3;
5585 } else if(tcstrifwm(crp, "May")){
5586 ts.tm_mon = 4;
5587 } else if(tcstrifwm(crp, "Jun")){
5588 ts.tm_mon = 5;
5589 } else if(tcstrifwm(crp, "Jul")){
5590 ts.tm_mon = 6;
5591 } else if(tcstrifwm(crp, "Aug")){
5592 ts.tm_mon = 7;
5593 } else if(tcstrifwm(crp, "Sep")){
5594 ts.tm_mon = 8;
5595 } else if(tcstrifwm(crp, "Oct")){
5596 ts.tm_mon = 9;
5597 } else if(tcstrifwm(crp, "Nov")){
5598 ts.tm_mon = 10;
5599 } else if(tcstrifwm(crp, "Dec")){
5600 ts.tm_mon = 11;
5601 } else {
5602 ts.tm_mon = -1;
5603 }
5604 if(ts.tm_mon >= 0) crp += 3;
5605 while(*crp == ' '){
5606 crp++;
5607 }
5608 ts.tm_year = tcatoi(crp);
5609 if(ts.tm_year >= 1969) ts.tm_year -= 1900;
5610 while(*crp >= '0' && *crp <= '9'){
5611 crp++;
5612 }
5613 while(*crp == ' '){
5614 crp++;
5615 }
5616 if(ts.tm_mday > 0 && ts.tm_mon >= 0 && ts.tm_year >= 0){
5617 int clen = strlen(crp);
5618 if(clen >= 8 && crp[2] == ':' && crp[5] == ':'){
5619 ts.tm_hour = tcatoi(crp + 0);
5620 ts.tm_min = tcatoi(crp + 3);
5621 ts.tm_sec = tcatoi(crp + 6);
5622 if(clen >= 14 && crp[8] == ' ' && (crp[9] == '+' || crp[9] == '-')){
5623 ts.tm_sec -= ((crp[10] - '0') * 36000 + (crp[11] - '0') * 3600 +
5624 (crp[12] - '0') * 600 + (crp[13] - '0') * 60) * (crp[9] == '+' ? 1 : -1);
5625 } else if(clen > 9){
5626 if(!strcmp(crp + 9, "JST")){
5627 ts.tm_sec -= 9 * 3600;
5628 } else if(!strcmp(crp + 9, "CCT")){
5629 ts.tm_sec -= 8 * 3600;
5630 } else if(!strcmp(crp + 9, "KST")){
5631 ts.tm_sec -= 9 * 3600;
5632 } else if(!strcmp(crp + 9, "EDT")){
5633 ts.tm_sec -= -4 * 3600;
5634 } else if(!strcmp(crp + 9, "EST")){
5635 ts.tm_sec -= -5 * 3600;
5636 } else if(!strcmp(crp + 9, "CDT")){
5637 ts.tm_sec -= -5 * 3600;
5638 } else if(!strcmp(crp + 9, "CST")){
5639 ts.tm_sec -= -6 * 3600;
5640 } else if(!strcmp(crp + 9, "MDT")){
5641 ts.tm_sec -= -6 * 3600;
5642 } else if(!strcmp(crp + 9, "MST")){
5643 ts.tm_sec -= -7 * 3600;
5644 } else if(!strcmp(crp + 9, "PDT")){
5645 ts.tm_sec -= -7 * 3600;
5646 } else if(!strcmp(crp + 9, "PST")){
5647 ts.tm_sec -= -8 * 3600;
5648 } else if(!strcmp(crp + 9, "HDT")){
5649 ts.tm_sec -= -9 * 3600;
5650 } else if(!strcmp(crp + 9, "HST")){
5651 ts.tm_sec -= -10 * 3600;
5652 }
5653 }
5654 }
5655 return (int64_t)tcmkgmtime(&ts);
5656 }
5657 return INT64_MIN;
5658 }
5659
5660
5661 /* Get the jet lag of the local time. */
tcjetlag(void)5662 int tcjetlag(void){
5663 #if defined(_SYS_LINUX_)
5664 tzset();
5665 return -timezone;
5666 #else
5667 time_t t = 86400;
5668 struct tm gts;
5669 if(!gmtime_r(&t, >s)) return 0;
5670 struct tm lts;
5671 t = 86400;
5672 if(!localtime_r(&t, <s)) return 0;
5673 return mktime(<s) - mktime(>s);
5674 #endif
5675 }
5676
5677
5678 /* Get the day of week of a date. */
tcdayofweek(int year,int mon,int day)5679 int tcdayofweek(int year, int mon, int day){
5680 if(mon < 3){
5681 year--;
5682 mon += 12;
5683 }
5684 return (day + ((8 + (13 * mon)) / 5) + (year + (year / 4) - (year / 100) + (year / 400))) % 7;
5685 }
5686
5687
5688 /* Close the random number generator. */
tcrandomfdclose(void)5689 static void tcrandomfdclose(void){
5690 close(tcrandomdevfd);
5691 }
5692
5693
5694 /* Make the GMT from a time structure.
5695 `tm' specifies the pointer to the time structure.
5696 The return value is the GMT. */
tcmkgmtime(struct tm * tm)5697 static time_t tcmkgmtime(struct tm *tm){
5698 #if defined(_SYS_LINUX_)
5699 assert(tm);
5700 return timegm(tm);
5701 #else
5702 assert(tm);
5703 return mktime(tm) + tcjetlag();
5704 #endif
5705 }
5706
5707
5708
5709 /*************************************************************************************************
5710 * miscellaneous utilities (for experts)
5711 *************************************************************************************************/
5712
5713
5714 #define TCCHIDXVNNUM 128 // number of virtual node of consistent hashing
5715
5716
5717 /* private function prototypes */
5718 static int tcstrutfkwicputtext(const uint16_t *oary, const uint16_t *nary, int si, int ti,
5719 int end, char *buf, const TCLIST *uwords, int opts);
5720 static int tcchidxcmp(const void *a, const void *b);
5721
5722
5723 /* Check whether a string is numeric completely or not. */
tcstrisnum(const char * str)5724 bool tcstrisnum(const char *str){
5725 assert(str);
5726 bool isnum = false;
5727 while(*str > '\0' && *str <= ' '){
5728 str++;
5729 }
5730 if(*str == '-') str++;
5731 while(*str >= '0' && *str <= '9'){
5732 isnum = true;
5733 str++;
5734 }
5735 if(*str == '.') str++;
5736 while(*str >= '0' && *str <= '9'){
5737 isnum = true;
5738 str++;
5739 }
5740 while(*str > '\0' && *str <= ' '){
5741 str++;
5742 }
5743 return isnum && *str == '\0';
5744 }
5745
5746
5747 /* Convert a hexadecimal string to an integer. */
tcatoih(const char * str)5748 int64_t tcatoih(const char *str){
5749 assert(str);
5750 while(*str > '\0' && *str <= ' '){
5751 str++;
5752 }
5753 if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')){
5754 str += 2;
5755 }
5756 int64_t num = 0;
5757 while(true){
5758 if(*str >= '0' && *str <= '9'){
5759 num = num * 0x10 + *str - '0';
5760 } else if(*str >= 'a' && *str <= 'f'){
5761 num = num * 0x10 + *str - 'a' + 10;
5762 } else if(*str >= 'A' && *str <= 'F'){
5763 num = num * 0x10 + *str - 'A' + 10;
5764 } else {
5765 break;
5766 }
5767 str++;
5768 }
5769 return num;
5770 }
5771
5772
5773 /* Skip space characters at head of a string. */
tcstrskipspc(const char * str)5774 const char *tcstrskipspc(const char *str){
5775 assert(str);
5776 while(*str > '\0' && *str <= ' '){
5777 str++;
5778 }
5779 return str;
5780 }
5781
5782
5783 /* Normalize a UTF-8 string. */
tcstrutfnorm(char * str,int opts)5784 char *tcstrutfnorm(char *str, int opts){
5785 assert(str);
5786 uint16_t buf[TCDISTBUFSIZ];
5787 uint16_t *ary;
5788 int len = strlen(str);
5789 if(len < TCDISTBUFSIZ){
5790 ary = buf;
5791 } else {
5792 TCMALLOC(ary, len * sizeof(*ary));
5793 }
5794 int num;
5795 tcstrutftoucs(str, ary, &num);
5796 num = tcstrucsnorm(ary, num, opts);
5797 tcstrucstoutf(ary, num, str);
5798 if(ary != buf) TCFREE(ary);
5799 return str;
5800 }
5801
5802
5803 /* Normalize a UCS-2 array. */
tcstrucsnorm(uint16_t * ary,int num,int opts)5804 int tcstrucsnorm(uint16_t *ary, int num, int opts){
5805 assert(ary && num >= 0);
5806 bool spcmode = opts & TCUNSPACE;
5807 bool lowmode = opts & TCUNLOWER;
5808 bool nacmode = opts & TCUNNOACC;
5809 bool widmode = opts & TCUNWIDTH;
5810 int wi = 0;
5811 for(int i = 0; i < num; i++){
5812 int c = ary[i];
5813 int high = c >> 8;
5814 if(high == 0x00){
5815 if(c <= 0x0020 || c == 0x007f){
5816 // control characters
5817 if(spcmode){
5818 ary[wi++] = 0x0020;
5819 if(wi < 2 || ary[wi-2] == 0x0020) wi--;
5820 } else if(c == 0x0009 || c == 0x000a || c == 0x000d){
5821 ary[wi++] = c;
5822 } else {
5823 ary[wi++] = 0x0020;
5824 }
5825 } else if(c == 0x00a0){
5826 // no-break space
5827 if(spcmode){
5828 ary[wi++] = 0x0020;
5829 if(wi < 2 || ary[wi-2] == 0x0020) wi--;
5830 } else {
5831 ary[wi++] = c;
5832 }
5833 } else {
5834 // otherwise
5835 if(lowmode){
5836 if(c < 0x007f){
5837 if(c >= 0x0041 && c <= 0x005a) c += 0x20;
5838 } else if(c >= 0x00c0 && c <= 0x00de && c != 0x00d7){
5839 c += 0x20;
5840 }
5841 }
5842 if(nacmode){
5843 if(c >= 0x00c0 && c <= 0x00c5){
5844 c = 'A';
5845 } else if(c == 0x00c7){
5846 c = 'C';
5847 } if(c >= 0x00c7 && c <= 0x00cb){
5848 c = 'E';
5849 } if(c >= 0x00cc && c <= 0x00cf){
5850 c = 'I';
5851 } else if(c == 0x00d0){
5852 c = 'D';
5853 } else if(c == 0x00d1){
5854 c = 'N';
5855 } if((c >= 0x00d2 && c <= 0x00d6) || c == 0x00d8){
5856 c = 'O';
5857 } if(c >= 0x00d9 && c <= 0x00dc){
5858 c = 'U';
5859 } if(c == 0x00dd || c == 0x00de){
5860 c = 'Y';
5861 } else if(c == 0x00df){
5862 c = 's';
5863 } else if(c >= 0x00e0 && c <= 0x00e5){
5864 c = 'a';
5865 } else if(c == 0x00e7){
5866 c = 'c';
5867 } if(c >= 0x00e7 && c <= 0x00eb){
5868 c = 'e';
5869 } if(c >= 0x00ec && c <= 0x00ef){
5870 c = 'i';
5871 } else if(c == 0x00f0){
5872 c = 'd';
5873 } else if(c == 0x00f1){
5874 c = 'n';
5875 } if((c >= 0x00f2 && c <= 0x00f6) || c == 0x00f8){
5876 c = 'o';
5877 } if(c >= 0x00f9 && c <= 0x00fc){
5878 c = 'u';
5879 } if(c >= 0x00fd && c <= 0x00ff){
5880 c = 'y';
5881 }
5882 }
5883 ary[wi++] = c;
5884 }
5885 } else if(high == 0x01){
5886 // latin-1 extended
5887 if(lowmode){
5888 if(c <= 0x0137){
5889 if((c & 1) == 0) c++;
5890 } else if(c == 0x0138){
5891 c += 0;
5892 } else if(c <= 0x0148){
5893 if((c & 1) == 1) c++;
5894 } else if(c == 0x0149){
5895 c += 0;
5896 } else if(c <= 0x0177){
5897 if((c & 1) == 0) c++;
5898 } else if(c == 0x0178){
5899 c = 0x00ff;
5900 } else if(c <= 0x017e){
5901 if((c & 1) == 1) c++;
5902 } else if(c == 0x017f){
5903 c += 0;
5904 }
5905 }
5906 if(nacmode){
5907 if(c == 0x00ff){
5908 c = 'y';
5909 } else if(c <= 0x0105){
5910 c = ((c & 1) == 0) ? 'A' : 'a';
5911 } else if(c <= 0x010d){
5912 c = ((c & 1) == 0) ? 'C' : 'c';
5913 } else if(c <= 0x0111){
5914 c = ((c & 1) == 0) ? 'D' : 'd';
5915 } else if(c <= 0x011b){
5916 c = ((c & 1) == 0) ? 'E' : 'e';
5917 } else if(c <= 0x0123){
5918 c = ((c & 1) == 0) ? 'G' : 'g';
5919 } else if(c <= 0x0127){
5920 c = ((c & 1) == 0) ? 'H' : 'h';
5921 } else if(c <= 0x0131){
5922 c = ((c & 1) == 0) ? 'I' : 'i';
5923 } else if(c == 0x0134){
5924 c = 'J';
5925 } else if(c == 0x0135){
5926 c = 'j';
5927 } else if(c == 0x0136){
5928 c = 'K';
5929 } else if(c == 0x0137){
5930 c = 'k';
5931 } else if(c == 0x0138){
5932 c = 'k';
5933 } else if(c >= 0x0139 && c <= 0x0142){
5934 c = ((c & 1) == 1) ? 'L' : 'l';
5935 } else if(c >= 0x0143 && c <= 0x0148){
5936 c = ((c & 1) == 1) ? 'N' : 'n';
5937 } else if(c >= 0x0149 && c <= 0x014b){
5938 c = ((c & 1) == 0) ? 'N' : 'n';
5939 } else if(c >= 0x014c && c <= 0x0151){
5940 c = ((c & 1) == 0) ? 'O' : 'o';
5941 } else if(c >= 0x0154 && c <= 0x0159){
5942 c = ((c & 1) == 0) ? 'R' : 'r';
5943 } else if(c >= 0x015a && c <= 0x0161){
5944 c = ((c & 1) == 0) ? 'S' : 's';
5945 } else if(c >= 0x0162 && c <= 0x0167){
5946 c = ((c & 1) == 0) ? 'T' : 't';
5947 } else if(c >= 0x0168 && c <= 0x0173){
5948 c = ((c & 1) == 0) ? 'U' : 'u';
5949 } else if(c == 0x0174){
5950 c = 'W';
5951 } else if(c == 0x0175){
5952 c = 'w';
5953 } else if(c == 0x0176){
5954 c = 'Y';
5955 } else if(c == 0x0177){
5956 c = 'y';
5957 } else if(c == 0x0178){
5958 c = 'Y';
5959 } else if(c >= 0x0179 && c <= 0x017e){
5960 c = ((c & 1) == 1) ? 'Z' : 'z';
5961 } else if(c == 0x017f){
5962 c = 's';
5963 }
5964 }
5965 ary[wi++] = c;
5966 } else if(high == 0x03){
5967 // greek
5968 if(lowmode){
5969 if(c >= 0x0391 && c <= 0x03a9){
5970 c += 0x20;
5971 } else if(c >= 0x03d8 && c <= 0x03ef){
5972 if((c & 1) == 0) c++;
5973 } else if(c == 0x0374 || c == 0x03f7 || c == 0x03fa){
5974 c++;
5975 }
5976 }
5977 ary[wi++] = c;
5978 } else if(high == 0x04){
5979 // cyrillic
5980 if(lowmode){
5981 if(c <= 0x040f){
5982 c += 0x50;
5983 } else if(c <= 0x042f){
5984 c += 0x20;
5985 } else if(c >= 0x0460 && c <= 0x0481){
5986 if((c & 1) == 0) c++;
5987 } else if(c >= 0x048a && c <= 0x04bf){
5988 if((c & 1) == 0) c++;
5989 } else if(c == 0x04c0){
5990 c = 0x04cf;
5991 } else if(c >= 0x04c1 && c <= 0x04ce){
5992 if((c & 1) == 1) c++;
5993 } else if(c >= 0x04d0){
5994 if((c & 1) == 0) c++;
5995 }
5996 }
5997 ary[wi++] = c;
5998 } else if(high == 0x20){
5999 if(c == 0x2002 || c == 0x2003 || c == 0x2009){
6000 // en space, em space, thin space
6001 if(spcmode){
6002 ary[wi++] = 0x0020;
6003 if(wi < 2 || ary[wi-2] == 0x0020) wi--;
6004 } else {
6005 ary[wi++] = c;
6006 }
6007 } else if(c == 0x2010){
6008 // hyphen
6009 ary[wi++] = widmode ? 0x002d : c;
6010 } else if(c == 0x2015){
6011 // fullwidth horizontal line
6012 ary[wi++] = widmode ? 0x002d : c;
6013 } else if(c == 0x2019){
6014 // apostrophe
6015 ary[wi++] = widmode ? 0x0027 : c;
6016 } else if(c == 0x2033){
6017 // double quotes
6018 ary[wi++] = widmode ? 0x0022 : c;
6019 } else {
6020 // (otherwise)
6021 ary[wi++] = c;
6022 }
6023 } else if(high == 0x22){
6024 if(c == 0x2212){
6025 // minus sign
6026 ary[wi++] = widmode ? 0x002d : c;
6027 } else {
6028 // (otherwise)
6029 ary[wi++] = c;
6030 }
6031 } else if(high == 0x30){
6032 if(c == 0x3000){
6033 // fullwidth space
6034 if(spcmode){
6035 ary[wi++] = 0x0020;
6036 if(wi < 2 || ary[wi-2] == 0x0020) wi--;
6037 } else if(widmode){
6038 ary[wi++] = 0x0020;
6039 } else {
6040 ary[wi++] = c;
6041 }
6042 } else {
6043 // (otherwise)
6044 ary[wi++] = c;
6045 }
6046 } else if(high == 0xff){
6047 if(c == 0xff01){
6048 // fullwidth exclamation
6049 ary[wi++] = widmode ? 0x0021 : c;
6050 } else if(c == 0xff03){
6051 // fullwidth igeta
6052 ary[wi++] = widmode ? 0x0023 : c;
6053 } else if(c == 0xff04){
6054 // fullwidth dollar
6055 ary[wi++] = widmode ? 0x0024 : c;
6056 } else if(c == 0xff05){
6057 // fullwidth parcent
6058 ary[wi++] = widmode ? 0x0025 : c;
6059 } else if(c == 0xff06){
6060 // fullwidth ampersand
6061 ary[wi++] = widmode ? 0x0026 : c;
6062 } else if(c == 0xff0a){
6063 // fullwidth asterisk
6064 ary[wi++] = widmode ? 0x002a : c;
6065 } else if(c == 0xff0b){
6066 // fullwidth plus
6067 ary[wi++] = widmode ? 0x002b : c;
6068 } else if(c == 0xff0c){
6069 // fullwidth comma
6070 ary[wi++] = widmode ? 0x002c : c;
6071 } else if(c == 0xff0e){
6072 // fullwidth period
6073 ary[wi++] = widmode ? 0x002e : c;
6074 } else if(c == 0xff0f){
6075 // fullwidth slash
6076 ary[wi++] = widmode ? 0x002f : c;
6077 } else if(c == 0xff1a){
6078 // fullwidth colon
6079 ary[wi++] = widmode ? 0x003a : c;
6080 } else if(c == 0xff1b){
6081 // fullwidth semicolon
6082 ary[wi++] = widmode ? 0x003b : c;
6083 } else if(c == 0xff1d){
6084 // fullwidth equal
6085 ary[wi++] = widmode ? 0x003d : c;
6086 } else if(c == 0xff1f){
6087 // fullwidth question
6088 ary[wi++] = widmode ? 0x003f : c;
6089 } else if(c == 0xff20){
6090 // fullwidth atmark
6091 ary[wi++] = widmode ? 0x0040 : c;
6092 } else if(c == 0xff3c){
6093 // fullwidth backslash
6094 ary[wi++] = widmode ? 0x005c : c;
6095 } else if(c == 0xff3e){
6096 // fullwidth circumflex
6097 ary[wi++] = widmode ? 0x005e : c;
6098 } else if(c == 0xff3f){
6099 // fullwidth underscore
6100 ary[wi++] = widmode ? 0x005f : c;
6101 } else if(c == 0xff5c){
6102 // fullwidth vertical line
6103 ary[wi++] = widmode ? 0x007c : c;
6104 } else if(c >= 0xff21 && c <= 0xff3a){
6105 // fullwidth alphabets
6106 if(widmode){
6107 if(lowmode){
6108 ary[wi++] = c - 0xfee0 + 0x20;
6109 } else {
6110 ary[wi++] = c - 0xfee0;
6111 }
6112 } else if(lowmode){
6113 ary[wi++] = c + 0x20;
6114 } else {
6115 ary[wi++] = c;
6116 }
6117 } else if(c >= 0xff41 && c <= 0xff5a){
6118 // fullwidth small alphabets
6119 ary[wi++] = widmode ? c - 0xfee0 : c;
6120 } else if(c >= 0xff10 && c <= 0xff19){
6121 // fullwidth numbers
6122 ary[wi++] = widmode ? c - 0xfee0 : c;
6123 } else if(c == 0xff61){
6124 // halfwidth full stop
6125 ary[wi++] = widmode ? 0x3002 : c;
6126 } else if(c == 0xff62){
6127 // halfwidth left corner
6128 ary[wi++] = widmode ? 0x300c : c;
6129 } else if(c == 0xff63){
6130 // halfwidth right corner
6131 ary[wi++] = widmode ? 0x300d : c;
6132 } else if(c == 0xff64){
6133 // halfwidth comma
6134 ary[wi++] = widmode ? 0x3001 : c;
6135 } else if(c == 0xff65){
6136 // halfwidth middle dot
6137 ary[wi++] = widmode ? 0x30fb : c;
6138 } else if(c == 0xff66){
6139 // halfwidth wo
6140 ary[wi++] = widmode ? 0x30f2 : c;
6141 } else if(c >= 0xff67 && c <= 0xff6b){
6142 // halfwidth small a-o
6143 ary[wi++] = widmode ? (c - 0xff67) * 2 + 0x30a1 : c;
6144 } else if(c >= 0xff6c && c <= 0xff6e){
6145 // halfwidth small ya-yo
6146 ary[wi++] = widmode ? (c - 0xff6c) * 2 + 0x30e3 : c;
6147 } else if(c == 0xff6f){
6148 // halfwidth small tu
6149 ary[wi++] = widmode ? 0x30c3 : c;
6150 } else if(c == 0xff70){
6151 // halfwidth prolonged mark
6152 ary[wi++] = widmode ? 0x30fc : c;
6153 } else if(c >= 0xff71 && c <= 0xff75){
6154 // halfwidth a-o
6155 if(widmode){
6156 ary[wi] = (c - 0xff71) * 2 + 0x30a2;
6157 if(c == 0xff73 && i < num - 1 && ary[i+1] == 0xff9e){
6158 ary[wi] = 0x30f4;
6159 i++;
6160 }
6161 wi++;
6162 } else {
6163 ary[wi++] = c;
6164 }
6165 } else if(c >= 0xff76 && c <= 0xff7a){
6166 // halfwidth ka-ko
6167 if(widmode){
6168 ary[wi] = (c - 0xff76) * 2 + 0x30ab;
6169 if(i < num - 1 && ary[i+1] == 0xff9e){
6170 ary[wi]++;
6171 i++;
6172 }
6173 wi++;
6174 } else {
6175 ary[wi++] = c;
6176 }
6177 } else if(c >= 0xff7b && c <= 0xff7f){
6178 // halfwidth sa-so
6179 if(widmode){
6180 ary[wi] = (c - 0xff7b) * 2 + 0x30b5;
6181 if(i < num - 1 && ary[i+1] == 0xff9e){
6182 ary[wi]++;
6183 i++;
6184 }
6185 wi++;
6186 } else {
6187 ary[wi++] = c;
6188 }
6189 } else if(c >= 0xff80 && c <= 0xff84){
6190 // halfwidth ta-to
6191 if(widmode){
6192 ary[wi] = (c - 0xff80) * 2 + 0x30bf + (c >= 0xff82 ? 1 : 0);
6193 if(i < num - 1 && ary[i+1] == 0xff9e){
6194 ary[wi]++;
6195 i++;
6196 }
6197 wi++;
6198 } else {
6199 ary[wi++] = c;
6200 }
6201 } else if(c >= 0xff85 && c <= 0xff89){
6202 // halfwidth na-no
6203 ary[wi++] = widmode ? c - 0xcebb : c;
6204 } else if(c >= 0xff8a && c <= 0xff8e){
6205 // halfwidth ha-ho
6206 if(widmode){
6207 ary[wi] = (c - 0xff8a) * 3 + 0x30cf;
6208 if(i < num - 1 && ary[i+1] == 0xff9e){
6209 ary[wi]++;
6210 i++;
6211 } else if(i < num - 1 && ary[i+1] == 0xff9f){
6212 ary[wi] += 2;
6213 i++;
6214 }
6215 wi++;
6216 } else {
6217 ary[wi++] = c;
6218 }
6219 } else if(c >= 0xff8f && c <= 0xff93){
6220 // halfwidth ma-mo
6221 ary[wi++] = widmode ? c - 0xceb1 : c;
6222 } else if(c >= 0xff94 && c <= 0xff96){
6223 // halfwidth ya-yo
6224 ary[wi++] = widmode ? (c - 0xff94) * 2 + 0x30e4 : c;
6225 } else if(c >= 0xff97 && c <= 0xff9b){
6226 // halfwidth ra-ro
6227 ary[wi++] = widmode ? c - 0xceae : c;
6228 } else if(c == 0xff9c){
6229 // halfwidth wa
6230 ary[wi++] = widmode ? 0x30ef : c;
6231 } else if(c == 0xff9d){
6232 // halfwidth nn
6233 ary[wi++] = widmode ? 0x30f3 : c;
6234 } else {
6235 // otherwise
6236 ary[wi++] = c;
6237 }
6238 } else {
6239 // otherwise
6240 ary[wi++] = c;
6241 }
6242 }
6243 if(spcmode){
6244 while(wi > 0 && ary[wi-1] == 0x0020){
6245 wi--;
6246 }
6247 }
6248 return wi;
6249 }
6250
6251
6252 /* Generate a keyword-in-context string from a text and keywords. */
tcstrkwic(const char * str,const TCLIST * words,int width,int opts)6253 TCLIST *tcstrkwic(const char *str, const TCLIST *words, int width, int opts){
6254 assert(str && words && width >= 0);
6255 TCLIST *texts = tclistnew();
6256 int len = strlen(str);
6257 uint16_t *oary, *nary;
6258 TCMALLOC(oary, sizeof(*oary) * len + 1);
6259 TCMALLOC(nary, sizeof(*nary) * len + 1);
6260 int oanum, nanum;
6261 tcstrutftoucs(str, oary, &oanum);
6262 tcstrutftoucs(str, nary, &nanum);
6263 nanum = tcstrucsnorm(nary, nanum, TCUNLOWER | TCUNNOACC | TCUNWIDTH);
6264 if(nanum != oanum){
6265 memcpy(nary, oary, sizeof(*oary) * oanum);
6266 for(int i = 0; i < oanum; i++){
6267 if(nary[i] >= 'A' && nary[i] <= 'Z') nary[i] += 'a' - 'A';
6268 }
6269 nanum = oanum;
6270 }
6271 int wnum = TCLISTNUM(words);
6272 TCLIST *uwords = tclistnew2(wnum);
6273 for(int i = 0; i < wnum; i++){
6274 const char *word;
6275 int wsiz;
6276 TCLISTVAL(word, words, i, wsiz);
6277 uint16_t *uwary;
6278 TCMALLOC(uwary, sizeof(*uwary) * wsiz + 1);
6279 int uwnum;
6280 tcstrutftoucs(word, uwary, &uwnum);
6281 uwnum = tcstrucsnorm(uwary, uwnum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH);
6282 if(uwnum > 0){
6283 tclistpushmalloc(uwords, uwary, sizeof(*uwary) * uwnum);
6284 } else {
6285 TCFREE(uwary);
6286 }
6287 }
6288 wnum = TCLISTNUM(uwords);
6289 int ri = 0;
6290 int pi = 0;
6291 while(ri < nanum){
6292 int step = 0;
6293 for(int i = 0; i < wnum; i++){
6294 const char *val;
6295 int uwnum;
6296 TCLISTVAL(val, uwords, i, uwnum);
6297 uint16_t *uwary = (uint16_t *)val;
6298 uwnum /= sizeof(*uwary);
6299 if(ri + uwnum <= nanum){
6300 int ci = 0;
6301 while(ci < uwnum && nary[ri+ci] == uwary[ci]){
6302 ci++;
6303 }
6304 if(ci == uwnum){
6305 int si = tclmax(ri - width, 0);
6306 if(opts & TCKWNOOVER) si = tclmax(si, pi);
6307 int ti = tclmin(ri + uwnum + width, nanum);
6308 char *tbuf;
6309 TCMALLOC(tbuf, (ti - si) * 5 + 1);
6310 int wi = 0;
6311 if(ri > si) wi += tcstrutfkwicputtext(oary, nary, si, ri, ri,
6312 tbuf + wi, uwords, opts);
6313 if(opts & TCKWMUTAB){
6314 tbuf[wi++] = '\t';
6315 } else if(opts & TCKWMUCTRL){
6316 tbuf[wi++] = 0x02;
6317 } else if(opts & TCKWMUBRCT){
6318 tbuf[wi++] = '[';
6319 }
6320 wi += tcstrucstoutf(oary + ri, ci, tbuf + wi);
6321 if(opts & TCKWMUTAB){
6322 tbuf[wi++] = '\t';
6323 } else if(opts & TCKWMUCTRL){
6324 tbuf[wi++] = 0x03;
6325 } else if(opts & TCKWMUBRCT){
6326 tbuf[wi++] = ']';
6327 }
6328 if(ti > ri + ci) wi += tcstrutfkwicputtext(oary, nary, ri + ci, ti,
6329 nanum, tbuf + wi, uwords, opts);
6330 if(wi > 0){
6331 tclistpushmalloc(texts, tbuf, wi);
6332 } else {
6333 TCFREE(tbuf);
6334 }
6335 if(ti > step) step = ti;
6336 if(step > pi) pi = step;
6337 if(opts & TCKWNOOVER) break;
6338 }
6339 }
6340 }
6341 if(ri == 0 && step < 1 && (opts & TCKWPULEAD)){
6342 int ti = tclmin(ri + width * 2, nanum);
6343 if(ti > 0){
6344 char *tbuf;
6345 TCMALLOC(tbuf, ti * 5 + 1);
6346 int wi = 0;
6347 wi += tcstrutfkwicputtext(oary, nary, 0, ti, nanum, tbuf + wi, uwords, opts);
6348 if(!(opts & TCKWNOOVER) && opts & TCKWMUTAB){
6349 tbuf[wi++] = '\t';
6350 tbuf[wi++] = '\t';
6351 }
6352 tclistpushmalloc(texts, tbuf, wi);
6353 }
6354 step = ti;
6355 }
6356 if(opts & TCKWNOOVER){
6357 ri = (step > 0) ? step : ri + 1;
6358 } else {
6359 ri++;
6360 }
6361 }
6362 tclistdel(uwords);
6363 TCFREE(nary);
6364 TCFREE(oary);
6365 return texts;
6366 }
6367
6368
6369 /* Tokenize a text separating by white space characters. */
tcstrtokenize(const char * str)6370 TCLIST *tcstrtokenize(const char *str){
6371 assert(str);
6372 TCLIST *tokens = tclistnew();
6373 const unsigned char *rp = (unsigned char *)str;
6374 while(*rp != '\0'){
6375 while(*rp <= ' '){
6376 rp++;
6377 }
6378 if(*rp == '"'){
6379 rp++;
6380 TCXSTR *buf = tcxstrnew();
6381 while(*rp != '\0'){
6382 if(*rp == '\\'){
6383 rp++;
6384 if(*rp != '\0') TCXSTRCAT(buf, rp, 1);
6385 rp++;
6386 } else if(*rp == '"'){
6387 rp++;
6388 break;
6389 } else {
6390 TCXSTRCAT(buf, rp, 1);
6391 rp++;
6392 }
6393 }
6394 int size = TCXSTRSIZE(buf);
6395 tclistpushmalloc(tokens, tcxstrtomalloc(buf), size);
6396 } else {
6397 const unsigned char *ep = rp;
6398 while(*ep > ' '){
6399 ep++;
6400 }
6401 if(ep > rp) TCLISTPUSH(tokens, rp, ep - rp);
6402 if(*ep != '\0'){
6403 rp = ep + 1;
6404 } else {
6405 break;
6406 }
6407 }
6408 }
6409 return tokens;
6410 }
6411
6412
6413 /* Create a list object by splitting a region by zero code. */
tcstrsplit2(const void * ptr,int size)6414 TCLIST *tcstrsplit2(const void *ptr, int size){
6415 assert(ptr && size >= 0);
6416 TCLIST *list = tclistnew();
6417 while(size >= 0){
6418 const char *rp = ptr;
6419 const char *ep = (const char *)ptr + size;
6420 while(rp < ep){
6421 if(*rp == '\0') break;
6422 rp++;
6423 }
6424 TCLISTPUSH(list, ptr, rp - (const char *)ptr);
6425 rp++;
6426 size -= rp - (const char *)ptr;
6427 ptr = rp;
6428 }
6429 return list;
6430 }
6431
6432
6433 /* Create a map object by splitting a string. */
tcstrsplit3(const char * str,const char * delims)6434 TCMAP *tcstrsplit3(const char *str, const char *delims){
6435 assert(str && delims);
6436 TCMAP *map = tcmapnew2(TCMAPTINYBNUM);
6437 const char *kbuf = NULL;
6438 int ksiz = 0;
6439 while(true){
6440 const char *sp = str;
6441 while(*str != '\0' && !strchr(delims, *str)){
6442 str++;
6443 }
6444 if(kbuf){
6445 tcmapput(map, kbuf, ksiz, sp, str - sp);
6446 kbuf = NULL;
6447 } else {
6448 kbuf = sp;
6449 ksiz = str - sp;
6450 }
6451 if(*str == '\0') break;
6452 str++;
6453 }
6454 return map;
6455 }
6456
6457
6458 /* Create a map object by splitting a region by zero code. */
tcstrsplit4(const void * ptr,int size)6459 TCMAP *tcstrsplit4(const void *ptr, int size){
6460 assert(ptr && size >= 0);
6461 TCMAP *map = tcmapnew2(tclmin(size / 6 + 1, TCMAPDEFBNUM));
6462 const char *kbuf = NULL;
6463 int ksiz = 0;
6464 while(size >= 0){
6465 const char *rp = ptr;
6466 const char *ep = (const char *)ptr + size;
6467 while(rp < ep){
6468 if(*rp == '\0') break;
6469 rp++;
6470 }
6471 if(kbuf){
6472 tcmapput(map, kbuf, ksiz, ptr, rp - (const char *)ptr);
6473 kbuf = NULL;
6474 } else {
6475 kbuf = ptr;
6476 ksiz = rp - (const char *)ptr;
6477 }
6478 rp++;
6479 size -= rp - (const char *)ptr;
6480 ptr = rp;
6481 }
6482 return map;
6483 }
6484
6485
6486 /* Create a region separated by zero code by joining all elements of a list object. */
tcstrjoin2(const TCLIST * list,int * sp)6487 void *tcstrjoin2(const TCLIST *list, int *sp){
6488 assert(list && sp);
6489 int num = TCLISTNUM(list);
6490 int size = num + 1;
6491 for(int i = 0; i < num; i++){
6492 size += TCLISTVALSIZ(list, i);
6493 }
6494 char *buf;
6495 TCMALLOC(buf, size);
6496 char *wp = buf;
6497 for(int i = 0; i < num; i++){
6498 if(i > 0) *(wp++) = '\0';
6499 int vsiz;
6500 const char *vbuf = tclistval(list, i, &vsiz);
6501 memcpy(wp, vbuf, vsiz);
6502 wp += vsiz;
6503 }
6504 *wp = '\0';
6505 *sp = wp - buf;
6506 return buf;
6507 }
6508
6509
6510 /* Create a string by joining all records of a map object. */
tcstrjoin3(const TCMAP * map,char delim)6511 char *tcstrjoin3(const TCMAP *map, char delim){
6512 assert(map);
6513 int num = (int)TCMAPRNUM(map);
6514 int size = num * 2 + 1;
6515 TCMAPREC *cur = map->cur;
6516 tcmapiterinit((TCMAP *)map);
6517 const char *kbuf;
6518 int ksiz;
6519 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
6520 int vsiz;
6521 tcmapiterval(kbuf, &vsiz);
6522 size += ksiz + vsiz;
6523 }
6524 char *buf;
6525 TCMALLOC(buf, size);
6526 char *wp = buf;
6527 tcmapiterinit((TCMAP *)map);
6528 bool first = true;
6529 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
6530 if(first){
6531 first = false;
6532 } else {
6533 *(wp++) = delim;
6534 }
6535 memcpy(wp, kbuf, ksiz);
6536 wp += ksiz;
6537 int vsiz;
6538 const char *vbuf = tcmapiterval(kbuf, &vsiz);
6539 *(wp++) = delim;
6540 memcpy(wp, vbuf, vsiz);
6541 wp += vsiz;
6542 }
6543 *wp = '\0';
6544 ((TCMAP *)map)->cur = cur;
6545 return buf;
6546 }
6547
6548
6549 /* Create a region separated by zero code by joining all records of a map object. */
tcstrjoin4(const TCMAP * map,int * sp)6550 void *tcstrjoin4(const TCMAP *map, int *sp){
6551 assert(map && sp);
6552 int num = (int)TCMAPRNUM(map);
6553 int size = num * 2 + 1;
6554 TCMAPREC *cur = map->cur;
6555 tcmapiterinit((TCMAP *)map);
6556 const char *kbuf;
6557 int ksiz;
6558 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
6559 int vsiz;
6560 tcmapiterval(kbuf, &vsiz);
6561 size += ksiz + vsiz;
6562 }
6563 char *buf;
6564 TCMALLOC(buf, size);
6565 char *wp = buf;
6566 tcmapiterinit((TCMAP *)map);
6567 bool first = true;
6568 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
6569 if(first){
6570 first = false;
6571 } else {
6572 *(wp++) = '\0';
6573 }
6574 memcpy(wp, kbuf, ksiz);
6575 wp += ksiz;
6576 int vsiz;
6577 const char *vbuf = tcmapiterval(kbuf, &vsiz);
6578 *(wp++) = '\0';
6579 memcpy(wp, vbuf, vsiz);
6580 wp += vsiz;
6581 }
6582 *wp = '\0';
6583 *sp = wp - buf;
6584 ((TCMAP *)map)->cur = cur;
6585 return buf;
6586 }
6587
6588
6589 /* Sort top records of an array. */
tctopsort(void * base,size_t nmemb,size_t size,size_t top,int (* compar)(const void *,const void *))6590 void tctopsort(void *base, size_t nmemb, size_t size, size_t top,
6591 int(*compar)(const void *, const void *)){
6592 assert(base && size > 0 && compar);
6593 if(nmemb < 1) return;
6594 if(top > nmemb) top = nmemb;
6595 char *bp = base;
6596 char *ep = bp + nmemb * size;
6597 char *rp = bp + size;
6598 int num = 1;
6599 char swap[size];
6600 while(rp < ep){
6601 if(num < top){
6602 int cidx = num;
6603 while(cidx > 0){
6604 int pidx = (cidx - 1) / 2;
6605 if(compar(bp + cidx * size, bp + pidx * size) <= 0) break;
6606 memcpy(swap, bp + cidx * size, size);
6607 memcpy(bp + cidx * size, bp + pidx * size, size);
6608 memcpy(bp + pidx * size, swap, size);
6609 cidx = pidx;
6610 }
6611 num++;
6612 } else if(compar(rp, bp) < 0){
6613 memcpy(swap, bp, size);
6614 memcpy(bp, rp, size);
6615 memcpy(rp, swap, size);
6616 int pidx = 0;
6617 int bot = num / 2;
6618 while(pidx < bot){
6619 int cidx = pidx * 2 + 1;
6620 if(cidx < num - 1 && compar(bp + cidx * size, bp + (cidx + 1) * size) < 0) cidx++;
6621 if(compar(bp + pidx * size, bp + cidx * size) > 0) break;
6622 memcpy(swap, bp + pidx * size, size);
6623 memcpy(bp + pidx * size, bp + cidx * size, size);
6624 memcpy(bp + cidx * size, swap, size);
6625 pidx = cidx;
6626 }
6627 }
6628 rp += size;
6629 }
6630 num = top - 1;
6631 while(num > 0){
6632 memcpy(swap, bp, size);
6633 memcpy(bp, bp + num * size, size);
6634 memcpy(bp + num * size, swap, size);
6635 int pidx = 0;
6636 int bot = num / 2;
6637 while(pidx < bot){
6638 int cidx = pidx * 2 + 1;
6639 if(cidx < num - 1 && compar(bp + cidx * size, bp + (cidx + 1) * size) < 0) cidx++;
6640 if(compar(bp + pidx * size, bp + cidx * size) > 0) break;
6641 memcpy(swap, bp + pidx * size, size);
6642 memcpy(bp + pidx * size, bp + cidx * size, size);
6643 memcpy(bp + cidx * size, swap, size);
6644 pidx = cidx;
6645 }
6646 num--;
6647 }
6648 }
6649
6650
6651 /* Suspend execution of the current thread. */
tcsleep(double sec)6652 bool tcsleep(double sec){
6653 if(!isnormal(sec) || sec <= 0.0) return false;
6654 if(sec <= 1.0 / sysconf(_SC_CLK_TCK)) return sched_yield() == 0;
6655 double integ, fract;
6656 fract = modf(sec, &integ);
6657 struct timespec req, rem;
6658 req.tv_sec = integ;
6659 req.tv_nsec = tclmin(fract * 1000.0 * 1000.0 * 1000.0, 999999999);
6660 while(nanosleep(&req, &rem) != 0){
6661 if(errno != EINTR) return false;
6662 req = rem;
6663 }
6664 return true;
6665 }
6666
6667
6668 /* Get the current system information. */
tcsysinfo(void)6669 TCMAP *tcsysinfo(void){
6670 #if defined(_SYS_LINUX_)
6671 TCMAP *info = tcmapnew2(TCMAPTINYBNUM);
6672 struct rusage rbuf;
6673 memset(&rbuf, 0, sizeof(rbuf));
6674 if(getrusage(RUSAGE_SELF, &rbuf) == 0){
6675 tcmapprintf(info, "utime", "%0.6f",
6676 rbuf.ru_utime.tv_sec + rbuf.ru_utime.tv_usec / 1000000.0);
6677 tcmapprintf(info, "stime", "%0.6f",
6678 rbuf.ru_stime.tv_sec + rbuf.ru_stime.tv_usec / 1000000.0);
6679 }
6680 TCLIST *lines = tcreadfilelines("/proc/self/status");
6681 if(lines){
6682 int ln = tclistnum(lines);
6683 for(int i = 0; i < ln; i++){
6684 const char *line = TCLISTVALPTR(lines, i);
6685 const char *rp = strchr(line, ':');
6686 if(!rp) continue;
6687 rp++;
6688 while(*rp > '\0' && *rp <= ' '){
6689 rp++;
6690 }
6691 if(tcstrifwm(line, "VmSize:")){
6692 int64_t size = tcatoix(rp);
6693 if(size > 0) tcmapprintf(info, "size", "%lld", (long long)size);
6694 } else if(tcstrifwm(line, "VmRSS:")){
6695 int64_t size = tcatoix(rp);
6696 if(size > 0) tcmapprintf(info, "rss", "%lld", (long long)size);
6697 }
6698 }
6699 tclistdel(lines);
6700 }
6701 lines = tcreadfilelines("/proc/meminfo");
6702 if(lines){
6703 int ln = tclistnum(lines);
6704 for(int i = 0; i < ln; i++){
6705 const char *line = TCLISTVALPTR(lines, i);
6706 const char *rp = strchr(line, ':');
6707 if(!rp) continue;
6708 rp++;
6709 while(*rp > '\0' && *rp <= ' '){
6710 rp++;
6711 }
6712 if(tcstrifwm(line, "MemTotal:")){
6713 int64_t size = tcatoix(rp);
6714 if(size > 0) tcmapprintf(info, "total", "%lld", (long long)size);
6715 } else if(tcstrifwm(line, "MemFree:")){
6716 int64_t size = tcatoix(rp);
6717 if(size > 0) tcmapprintf(info, "free", "%lld", (long long)size);
6718 } else if(tcstrifwm(line, "Cached:")){
6719 int64_t size = tcatoix(rp);
6720 if(size > 0) tcmapprintf(info, "cached", "%lld", (long long)size);
6721 }
6722 }
6723 tclistdel(lines);
6724 }
6725 lines = tcreadfilelines("/proc/cpuinfo");
6726 if(lines){
6727 int cnum = 0;
6728 int ln = tclistnum(lines);
6729 for(int i = 0; i < ln; i++){
6730 const char *line = TCLISTVALPTR(lines, i);
6731 if(tcstrifwm(line, "processor")) cnum++;
6732 }
6733 if(cnum > 0) tcmapprintf(info, "corenum", "%lld", (long long)cnum);
6734 tclistdel(lines);
6735 }
6736 return info;
6737 #elif defined(_SYS_FREEBSD_) || defined(_SYS_MACOSX_)
6738 TCMAP *info = tcmapnew2(TCMAPTINYBNUM);
6739 struct rusage rbuf;
6740 memset(&rbuf, 0, sizeof(rbuf));
6741 if(getrusage(RUSAGE_SELF, &rbuf) == 0){
6742 tcmapprintf(info, "utime", "%0.6f",
6743 rbuf.ru_utime.tv_sec + rbuf.ru_utime.tv_usec / 1000000.0);
6744 tcmapprintf(info, "stime", "%0.6f",
6745 rbuf.ru_stime.tv_sec + rbuf.ru_stime.tv_usec / 1000000.0);
6746 long tck = sysconf(_SC_CLK_TCK);
6747 int64_t size = (((double)rbuf.ru_ixrss + rbuf.ru_idrss + rbuf.ru_isrss) / tck) * 1024.0;
6748 if(size > 0) tcmapprintf(info, "rss", "%lld", (long long)size);
6749 }
6750 return info;
6751 #else
6752 TCMAP *info = tcmapnew2(TCMAPTINYBNUM);
6753 struct rusage rbuf;
6754 memset(&rbuf, 0, sizeof(rbuf));
6755 if(getrusage(RUSAGE_SELF, &rbuf) == 0){
6756 tcmapprintf(info, "utime", "%0.6f",
6757 rbuf.ru_utime.tv_sec + rbuf.ru_utime.tv_usec / 1000000.0);
6758 tcmapprintf(info, "stime", "%0.6f",
6759 rbuf.ru_stime.tv_sec + rbuf.ru_stime.tv_usec / 1000000.0);
6760 }
6761 return info;
6762 #endif
6763 }
6764
6765
6766 /* Create a consistent hashing object. */
tcchidxnew(int range)6767 TCCHIDX *tcchidxnew(int range){
6768 assert(range > 0);
6769 TCCHIDX *chidx;
6770 TCMALLOC(chidx, sizeof(*chidx));
6771 int nnum = range * TCCHIDXVNNUM;
6772 TCCHIDXNODE *nodes;
6773 TCMALLOC(nodes, nnum * sizeof(*nodes));
6774 unsigned int seed = 725;
6775 for(int i = 0; i < range; i++){
6776 int end = (i + 1) * TCCHIDXVNNUM;
6777 for(int j = i * TCCHIDXVNNUM; j < end; j++){
6778 nodes[j].seq = i;
6779 nodes[j].hash = (seed = seed * 123456761 + 211);
6780 }
6781 }
6782 qsort(nodes, nnum, sizeof(*nodes), tcchidxcmp);
6783 chidx->nodes = nodes;
6784 chidx->nnum = nnum;
6785 return chidx;
6786 }
6787
6788
6789 /* Delete a consistent hashing object. */
tcchidxdel(TCCHIDX * chidx)6790 void tcchidxdel(TCCHIDX *chidx){
6791 assert(chidx);
6792 TCFREE(chidx->nodes);
6793 TCFREE(chidx);
6794 }
6795
6796
6797 /* Get the consistent hashing value of a record. */
tcchidxhash(TCCHIDX * chidx,const void * ptr,int size)6798 int tcchidxhash(TCCHIDX *chidx, const void *ptr, int size){
6799 assert(chidx && ptr && size >= 0);
6800 uint32_t hash = 19771007;
6801 const char *rp = (char *)ptr + size;
6802 while(size--){
6803 hash = (hash * 31) ^ *(uint8_t *)--rp;
6804 hash ^= hash << 7;
6805 }
6806 TCCHIDXNODE *nodes = chidx->nodes;
6807 int low = 0;
6808 int high = chidx->nnum;
6809 while(low < high){
6810 int mid = (low + high) >> 1;
6811 uint32_t nhash = nodes[mid].hash;
6812 if(hash < nhash){
6813 high = mid;
6814 } else if(hash > nhash){
6815 low = mid + 1;
6816 } else {
6817 low = mid;
6818 break;
6819 }
6820 }
6821 if(low >= chidx->nnum) low = 0;
6822 return nodes[low].seq & INT_MAX;
6823 }
6824
6825
6826 /* Put a text into a KWIC buffer.
6827 `oary' specifies the original code array.
6828 `nary' specifies the normalized code array.
6829 `si' specifies the start index of the text.
6830 `ti' specifies the terminal index of the text.
6831 `end' specifies the end index of the code array.
6832 `buf' specifies the pointer to the output buffer.
6833 `uwords' specifies the list object of the words to be marked up.
6834 `opts' specifies the options.
6835 The return value is the length of the output. */
tcstrutfkwicputtext(const uint16_t * oary,const uint16_t * nary,int si,int ti,int end,char * buf,const TCLIST * uwords,int opts)6836 static int tcstrutfkwicputtext(const uint16_t *oary, const uint16_t *nary, int si, int ti,
6837 int end, char *buf, const TCLIST *uwords, int opts){
6838 assert(oary && nary && si >= 0 && ti >= 0 && end >= 0 && buf && uwords);
6839 if(!(opts & TCKWNOOVER)) return tcstrucstoutf(oary + si, ti - si, buf);
6840 if(!(opts & TCKWMUTAB) && !(opts & TCKWMUCTRL) && !(opts & TCKWMUBRCT))
6841 return tcstrucstoutf(oary + si, ti - si, buf);
6842 int wnum = TCLISTNUM(uwords);
6843 int ri = si;
6844 int wi = 0;
6845 while(ri < ti){
6846 int step = 0;
6847 for(int i = 0; i < wnum; i++){
6848 const char *val;
6849 int uwnum;
6850 TCLISTVAL(val, uwords, i, uwnum);
6851 uint16_t *uwary = (uint16_t *)val;
6852 uwnum /= sizeof(*uwary);
6853 if(ri + uwnum <= end){
6854 int ci = 0;
6855 while(ci < uwnum && nary[ri+ci] == uwary[ci]){
6856 ci++;
6857 }
6858 if(ci == uwnum){
6859 if(opts & TCKWMUTAB){
6860 buf[wi++] = '\t';
6861 } else if(opts & TCKWMUCTRL){
6862 buf[wi++] = 0x02;
6863 } else if(opts & TCKWMUBRCT){
6864 buf[wi++] = '[';
6865 }
6866 wi += tcstrucstoutf(oary + ri, ci, buf + wi);
6867 if(opts & TCKWMUTAB){
6868 buf[wi++] = '\t';
6869 } else if(opts & TCKWMUCTRL){
6870 buf[wi++] = 0x03;
6871 } else if(opts & TCKWMUBRCT){
6872 buf[wi++] = ']';
6873 }
6874 step = ri + ci;
6875 break;
6876 }
6877 }
6878 }
6879 if(step > 0){
6880 ri = step;
6881 } else {
6882 wi += tcstrucstoutf(oary + ri, 1, buf + wi);
6883 ri++;
6884 }
6885 }
6886 return wi;
6887 }
6888
6889
6890 /* Compare two consistent hashing nodes.
6891 `a' specifies the pointer to one node object.
6892 `b' specifies the pointer to the other node object.
6893 The return value is positive if the former is big, negative if the latter is big, 0 if both
6894 are equivalent. */
tcchidxcmp(const void * a,const void * b)6895 static int tcchidxcmp(const void *a, const void *b){
6896 if(((TCCHIDXNODE *)a)->hash == ((TCCHIDXNODE *)b)->hash) return 0;
6897 return ((TCCHIDXNODE *)a)->hash > ((TCCHIDXNODE *)b)->hash;
6898 }
6899
6900
6901
6902 /*************************************************************************************************
6903 * filesystem utilities
6904 *************************************************************************************************/
6905
6906
6907 #define TCFILEMODE 00644 // permission of a creating file
6908 #define TCIOBUFSIZ 16384 // size of an I/O buffer
6909
6910
6911 /* Get the canonicalized absolute path of a file. */
tcrealpath(const char * path)6912 char *tcrealpath(const char *path){
6913 assert(path);
6914 char buf[PATH_MAX+1];
6915 if(realpath(path, buf)) return tcstrdup(buf);
6916 if(errno == ENOENT){
6917 const char *pv = strrchr(path, MYPATHCHR);
6918 if(pv){
6919 if(pv == path) return tcstrdup(path);
6920 char *prefix = tcmemdup(path, pv - path);
6921 if(!realpath(prefix, buf)){
6922 TCFREE(prefix);
6923 return NULL;
6924 }
6925 TCFREE(prefix);
6926 pv++;
6927 } else {
6928 if(!realpath(MYCDIRSTR, buf)) return NULL;
6929 pv = path;
6930 }
6931 if(buf[0] == MYPATHCHR && buf[1] == '\0') buf[0] = '\0';
6932 char *str;
6933 TCMALLOC(str, strlen(buf) + strlen(pv) + 2);
6934 sprintf(str, "%s%c%s", buf, MYPATHCHR, pv);
6935 return str;
6936 }
6937 return NULL;
6938 }
6939
6940
6941 /* Get the status information of a file. */
tcstatfile(const char * path,bool * isdirp,int64_t * sizep,int64_t * mtimep)6942 bool tcstatfile(const char *path, bool *isdirp, int64_t *sizep, int64_t *mtimep){
6943 assert(path);
6944 struct stat sbuf;
6945 if(stat(path, &sbuf) != 0) return false;
6946 if(isdirp) *isdirp = S_ISDIR(sbuf.st_mode);
6947 if(sizep) *sizep = sbuf.st_size;
6948 if(mtimep) *mtimep = sbuf.st_mtime;
6949 return true;
6950 }
6951
6952
6953 /* Read whole data of a file. */
tcreadfile(const char * path,int limit,int * sp)6954 void *tcreadfile(const char *path, int limit, int *sp){
6955 int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
6956 if(fd == -1) return NULL;
6957 if(fd == 0){
6958 TCXSTR *xstr = tcxstrnew();
6959 char buf[TCIOBUFSIZ];
6960 limit = limit > 0 ? limit : INT_MAX;
6961 int rsiz;
6962 while((rsiz = read(fd, buf, tclmin(TCIOBUFSIZ, limit))) > 0){
6963 TCXSTRCAT(xstr, buf, rsiz);
6964 limit -= rsiz;
6965 }
6966 if(sp) *sp = TCXSTRSIZE(xstr);
6967 return tcxstrtomalloc(xstr);
6968 }
6969 struct stat sbuf;
6970 if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
6971 close(fd);
6972 return NULL;
6973 }
6974 limit = limit > 0 ? tclmin((int)sbuf.st_size, limit) : sbuf.st_size;
6975 char *buf;
6976 TCMALLOC(buf, sbuf.st_size + 1);
6977 char *wp = buf;
6978 int rsiz;
6979 while((rsiz = read(fd, wp, limit - (wp - buf))) > 0){
6980 wp += rsiz;
6981 }
6982 *wp = '\0';
6983 close(fd);
6984 if(sp) *sp = wp - buf;
6985 return buf;
6986 }
6987
6988
6989 /* Read every line of a file. */
tcreadfilelines(const char * path)6990 TCLIST *tcreadfilelines(const char *path){
6991 int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
6992 if(fd == -1) return NULL;
6993 TCLIST *list = tclistnew();
6994 TCXSTR *xstr = tcxstrnew();
6995 char buf[TCIOBUFSIZ];
6996 int rsiz;
6997 while((rsiz = read(fd, buf, TCIOBUFSIZ)) > 0){
6998 for(int i = 0; i < rsiz; i++){
6999 switch(buf[i]){
7000 case '\r':
7001 break;
7002 case '\n':
7003 TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
7004 tcxstrclear(xstr);
7005 break;
7006 default:
7007 TCXSTRCAT(xstr, buf + i, 1);
7008 break;
7009 }
7010 }
7011 }
7012 TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
7013 tcxstrdel(xstr);
7014 if(path) close(fd);
7015 return list;
7016 }
7017
7018
7019 /* Write data into a file. */
tcwritefile(const char * path,const void * ptr,int size)7020 bool tcwritefile(const char *path, const void *ptr, int size){
7021 assert(ptr && size >= 0);
7022 int fd = 1;
7023 if(path && (fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE)) == -1) return false;
7024 bool err = false;
7025 if(!tcwrite(fd, ptr, size)) err = true;
7026 if(close(fd) == -1) err = true;
7027 return !err;
7028 }
7029
7030
7031 /* Copy a file. */
tccopyfile(const char * src,const char * dest)7032 bool tccopyfile(const char *src, const char *dest){
7033 int ifd = open(src, O_RDONLY, TCFILEMODE);
7034 if(ifd == -1) return false;
7035 int ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE);
7036 if(ofd == -1){
7037 close(ifd);
7038 return false;
7039 }
7040 bool err = false;
7041 while(true){
7042 char buf[TCIOBUFSIZ];
7043 int size = read(ifd, buf, TCIOBUFSIZ);
7044 if(size > 0){
7045 if(!tcwrite(ofd, buf, size)){
7046 err = true;
7047 break;
7048 }
7049 } else if(size == -1){
7050 if(errno != EINTR){
7051 err = true;
7052 break;
7053 }
7054 } else {
7055 break;
7056 }
7057 }
7058 if(close(ofd) == -1) err = true;
7059 if(close(ifd) == -1) err = true;
7060 return !err;
7061 }
7062
7063
7064 /* Read names of files in a directory. */
tcreaddir(const char * path)7065 TCLIST *tcreaddir(const char *path){
7066 assert(path);
7067 DIR *DD;
7068 struct dirent *dp;
7069 if(!(DD = opendir(path))) return NULL;
7070 TCLIST *list = tclistnew();
7071 while((dp = readdir(DD)) != NULL){
7072 if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
7073 TCLISTPUSH(list, dp->d_name, strlen(dp->d_name));
7074 }
7075 closedir(DD);
7076 return list;
7077 }
7078
7079
7080 /* Expand a pattern into a list of matched paths. */
tcglobpat(const char * pattern)7081 TCLIST *tcglobpat(const char *pattern){
7082 assert(pattern);
7083 TCLIST *list = tclistnew();
7084 glob_t gbuf;
7085 memset(&gbuf, 0, sizeof(gbuf));
7086 if(glob(pattern, GLOB_ERR | GLOB_NOSORT, NULL, &gbuf) == 0){
7087 for(int i = 0; i < gbuf.gl_pathc; i++){
7088 tclistpush2(list, gbuf.gl_pathv[i]);
7089 }
7090 globfree(&gbuf);
7091 }
7092 return list;
7093 }
7094
7095
7096 /* Remove a file or a directory and its sub ones recursively. */
tcremovelink(const char * path)7097 bool tcremovelink(const char *path){
7098 assert(path);
7099 struct stat sbuf;
7100 if(lstat(path, &sbuf) == -1) return false;
7101 if(unlink(path) == 0) return true;
7102 TCLIST *list;
7103 if(!S_ISDIR(sbuf.st_mode) || !(list = tcreaddir(path))) return false;
7104 bool tail = path[0] != '\0' && path[strlen(path)-1] == MYPATHCHR;
7105 for(int i = 0; i < TCLISTNUM(list); i++){
7106 const char *elem = TCLISTVALPTR(list, i);
7107 if(!strcmp(MYCDIRSTR, elem) || !strcmp(MYPDIRSTR, elem)) continue;
7108 char *cpath;
7109 if(tail){
7110 cpath = tcsprintf("%s%s", path, elem);
7111 } else {
7112 cpath = tcsprintf("%s%c%s", path, MYPATHCHR, elem);
7113 }
7114 tcremovelink(cpath);
7115 TCFREE(cpath);
7116 }
7117 tclistdel(list);
7118 return rmdir(path) == 0 ? true : false;
7119 }
7120
7121
7122 /* Write data into a file. */
tcwrite(int fd,const void * buf,size_t size)7123 bool tcwrite(int fd, const void *buf, size_t size){
7124 assert(fd >= 0 && buf && size >= 0);
7125 const char *rp = buf;
7126 do {
7127 int wb = write(fd, rp, size);
7128 switch(wb){
7129 case -1: if(errno != EINTR) return false;
7130 case 0: break;
7131 default:
7132 rp += wb;
7133 size -= wb;
7134 break;
7135 }
7136 } while(size > 0);
7137 return true;
7138 }
7139
7140
7141 /* Read data from a file. */
tcread(int fd,void * buf,size_t size)7142 bool tcread(int fd, void *buf, size_t size){
7143 assert(fd >= 0 && buf && size >= 0);
7144 char *wp = buf;
7145 do {
7146 int rb = read(fd, wp, size);
7147 switch(rb){
7148 case -1: if(errno != EINTR) return false;
7149 case 0: return size < 1;
7150 default:
7151 wp += rb;
7152 size -= rb;
7153 }
7154 } while(size > 0);
7155 return true;
7156 }
7157
7158
7159 /* Lock a file. */
tclock(int fd,bool ex,bool nb)7160 bool tclock(int fd, bool ex, bool nb){
7161 assert(fd >= 0);
7162 struct flock lock;
7163 memset(&lock, 0, sizeof(struct flock));
7164 lock.l_type = ex ? F_WRLCK : F_RDLCK;
7165 lock.l_whence = SEEK_SET;
7166 lock.l_start = 0;
7167 lock.l_len = 0;
7168 lock.l_pid = 0;
7169 while(fcntl(fd, nb ? F_SETLK : F_SETLKW, &lock) == -1){
7170 if(errno != EINTR) return false;
7171 }
7172 return true;
7173 }
7174
7175
7176 /* Unlock a file. */
tcunlock(int fd)7177 bool tcunlock(int fd){
7178 assert(fd >= 0);
7179 struct flock lock;
7180 memset(&lock, 0, sizeof(struct flock));
7181 lock.l_type = F_UNLCK;
7182 lock.l_whence = SEEK_SET;
7183 lock.l_start = 0;
7184 lock.l_len = 0;
7185 lock.l_pid = 0;
7186 while(fcntl(fd, F_SETLKW, &lock) == -1){
7187 if(errno != EINTR) return false;
7188 }
7189 return true;
7190 }
7191
7192
7193 /* Execute a shell command. */
tcsystem(const char ** args,int anum)7194 int tcsystem(const char **args, int anum){
7195 assert(args && anum >= 0);
7196 if(anum < 1) return -1;
7197 TCXSTR *phrase = tcxstrnew3(anum * TCNUMBUFSIZ + 1);
7198 for(int i = 0; i < anum; i++){
7199 const char *rp = args[i];
7200 int len = strlen(rp);
7201 char *token;
7202 TCMALLOC(token, len * 2 + 1);
7203 char *wp = token;
7204 while(*rp != '\0'){
7205 switch(*rp){
7206 case '"': case '\\': case '$': case '`':
7207 *(wp++) = '\\';
7208 *(wp++) = *rp;
7209 break;
7210 default:
7211 *(wp++) = *rp;
7212 break;
7213 }
7214 rp++;
7215 }
7216 *wp = '\0';
7217 if(tcxstrsize(phrase)) tcxstrcat(phrase, " ", 1);
7218 tcxstrprintf(phrase, "\"%s\"", token);
7219 TCFREE(token);
7220 }
7221 int rv = system(tcxstrptr(phrase));
7222 if(WIFEXITED(rv)){
7223 rv = WEXITSTATUS(rv);
7224 } else {
7225 rv = INT_MAX;
7226 }
7227 tcxstrdel(phrase);
7228 return rv;
7229 }
7230
7231
7232
7233 /*************************************************************************************************
7234 * encoding utilities
7235 *************************************************************************************************/
7236
7237
7238 #define TCENCBUFSIZ 32 // size of a buffer for encoding name
7239 #define TCXMLATBNUM 31 // bucket number of XML attributes
7240
7241
7242 /* Encode a serial object with URL encoding. */
tcurlencode(const char * ptr,int size)7243 char *tcurlencode(const char *ptr, int size){
7244 assert(ptr && size >= 0);
7245 char *buf;
7246 TCMALLOC(buf, size * 3 + 1);
7247 char *wp = buf;
7248 for(int i = 0; i < size; i++){
7249 int c = ((unsigned char *)ptr)[i];
7250 if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
7251 (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.!~*'()", c))){
7252 *(wp++) = c;
7253 } else {
7254 wp += sprintf(wp, "%%%02X", c);
7255 }
7256 }
7257 *wp = '\0';
7258 return buf;
7259 }
7260
7261
7262 /* Decode a string encoded with URL encoding. */
tcurldecode(const char * str,int * sp)7263 char *tcurldecode(const char *str, int *sp){
7264 assert(str && sp);
7265 char *buf = tcstrdup(str);
7266 char *wp = buf;
7267 while(*str != '\0'){
7268 if(*str == '%'){
7269 str++;
7270 if(((str[0] >= '0' && str[0] <= '9') || (str[0] >= 'A' && str[0] <= 'F') ||
7271 (str[0] >= 'a' && str[0] <= 'f')) &&
7272 ((str[1] >= '0' && str[1] <= '9') || (str[1] >= 'A' && str[1] <= 'F') ||
7273 (str[1] >= 'a' && str[1] <= 'f'))){
7274 unsigned char c = *str;
7275 if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
7276 if(c >= 'a' && c <= 'z'){
7277 *wp = c - 'a' + 10;
7278 } else {
7279 *wp = c - '0';
7280 }
7281 *wp *= 0x10;
7282 str++;
7283 c = *str;
7284 if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
7285 if(c >= 'a' && c <= 'z'){
7286 *wp += c - 'a' + 10;
7287 } else {
7288 *wp += c - '0';
7289 }
7290 str++;
7291 wp++;
7292 } else {
7293 break;
7294 }
7295 } else if(*str == '+'){
7296 *wp = ' ';
7297 str++;
7298 wp++;
7299 } else {
7300 *wp = *str;
7301 str++;
7302 wp++;
7303 }
7304 }
7305 *wp = '\0';
7306 *sp = wp - buf;
7307 return buf;
7308 }
7309
7310
7311 /* Break up a URL into elements. */
tcurlbreak(const char * str)7312 TCMAP *tcurlbreak(const char *str){
7313 assert(str);
7314 TCMAP *map = tcmapnew2(TCMAPTINYBNUM);
7315 char *trim = tcstrdup(str);
7316 tcstrtrim(trim);
7317 const char *rp = trim;
7318 char *norm;
7319 TCMALLOC(norm, strlen(trim) * 3 + 1);
7320 char *wp = norm;
7321 while(*rp != '\0'){
7322 if(*rp > 0x20 && *rp < 0x7f){
7323 *(wp++) = *rp;
7324 } else {
7325 wp += sprintf(wp, "%%%02X", *(unsigned char *)rp);
7326 }
7327 rp++;
7328 }
7329 *wp = '\0';
7330 rp = norm;
7331 tcmapput2(map, "self", rp);
7332 bool serv = false;
7333 if(tcstrifwm(rp, "http://")){
7334 tcmapput2(map, "scheme", "http");
7335 rp += 7;
7336 serv = true;
7337 } else if(tcstrifwm(rp, "https://")){
7338 tcmapput2(map, "scheme", "https");
7339 rp += 8;
7340 serv = true;
7341 } else if(tcstrifwm(rp, "ftp://")){
7342 tcmapput2(map, "scheme", "ftp");
7343 rp += 6;
7344 serv = true;
7345 } else if(tcstrifwm(rp, "sftp://")){
7346 tcmapput2(map, "scheme", "sftp");
7347 rp += 7;
7348 serv = true;
7349 } else if(tcstrifwm(rp, "ftps://")){
7350 tcmapput2(map, "scheme", "ftps");
7351 rp += 7;
7352 serv = true;
7353 } else if(tcstrifwm(rp, "tftp://")){
7354 tcmapput2(map, "scheme", "tftp");
7355 rp += 7;
7356 serv = true;
7357 } else if(tcstrifwm(rp, "ldap://")){
7358 tcmapput2(map, "scheme", "ldap");
7359 rp += 7;
7360 serv = true;
7361 } else if(tcstrifwm(rp, "ldaps://")){
7362 tcmapput2(map, "scheme", "ldaps");
7363 rp += 8;
7364 serv = true;
7365 } else if(tcstrifwm(rp, "file://")){
7366 tcmapput2(map, "scheme", "file");
7367 rp += 7;
7368 serv = true;
7369 }
7370 char *ep;
7371 if((ep = strchr(rp, '#')) != NULL){
7372 tcmapput2(map, "fragment", ep + 1);
7373 *ep = '\0';
7374 }
7375 if((ep = strchr(rp, '?')) != NULL){
7376 tcmapput2(map, "query", ep + 1);
7377 *ep = '\0';
7378 }
7379 if(serv){
7380 if((ep = strchr(rp, '/')) != NULL){
7381 tcmapput2(map, "path", ep);
7382 *ep = '\0';
7383 } else {
7384 tcmapput2(map, "path", "/");
7385 }
7386 if((ep = strchr(rp, '@')) != NULL){
7387 *ep = '\0';
7388 if(rp[0] != '\0') tcmapput2(map, "authority", rp);
7389 rp = ep + 1;
7390 }
7391 if((ep = strchr(rp, ':')) != NULL){
7392 if(ep[1] != '\0') tcmapput2(map, "port", ep + 1);
7393 *ep = '\0';
7394 }
7395 if(rp[0] != '\0') tcmapput2(map, "host", rp);
7396 } else {
7397 tcmapput2(map, "path", rp);
7398 }
7399 TCFREE(norm);
7400 TCFREE(trim);
7401 if((rp = tcmapget2(map, "path")) != NULL){
7402 if((ep = strrchr(rp, '/')) != NULL){
7403 if(ep[1] != '\0') tcmapput2(map, "file", ep + 1);
7404 } else {
7405 tcmapput2(map, "file", rp);
7406 }
7407 }
7408 if((rp = tcmapget2(map, "file")) != NULL && (!strcmp(rp, ".") || !strcmp(rp, "..")))
7409 tcmapout2(map, "file");
7410 return map;
7411 }
7412
7413
7414 /* Resolve a relative URL with an absolute URL. */
tcurlresolve(const char * base,const char * target)7415 char *tcurlresolve(const char *base, const char *target){
7416 assert(base && target);
7417 const char *vbuf, *path;
7418 char *tmp, *wp, *enc;
7419 while(*base > '\0' && *base <= ' '){
7420 base++;
7421 }
7422 while(*target > '\0' && *target <= ' '){
7423 target++;
7424 }
7425 if(*target == '\0') target = base;
7426 TCXSTR *rbuf = tcxstrnew();
7427 TCMAP *telems = tcurlbreak(target);
7428 int port = 80;
7429 TCMAP *belems = tcurlbreak(tcmapget2(telems, "scheme") ? target : base);
7430 if((vbuf = tcmapget2(belems, "scheme")) != NULL){
7431 tcxstrcat2(rbuf, vbuf);
7432 TCXSTRCAT(rbuf, "://", 3);
7433 if(!tcstricmp(vbuf, "https")){
7434 port = 443;
7435 } else if(!tcstricmp(vbuf, "ftp")){
7436 port = 21;
7437 } else if(!tcstricmp(vbuf, "sftp")){
7438 port = 115;
7439 } else if(!tcstricmp(vbuf, "ftps")){
7440 port = 22;
7441 } else if(!tcstricmp(vbuf, "tftp")){
7442 port = 69;
7443 } else if(!tcstricmp(vbuf, "ldap")){
7444 port = 389;
7445 } else if(!tcstricmp(vbuf, "ldaps")){
7446 port = 636;
7447 }
7448 } else {
7449 tcxstrcat2(rbuf, "http://");
7450 }
7451 int vsiz;
7452 if((vbuf = tcmapget2(belems, "authority")) != NULL){
7453 if((wp = strchr(vbuf, ':')) != NULL){
7454 *wp = '\0';
7455 tmp = tcurldecode(vbuf, &vsiz);
7456 enc = tcurlencode(tmp, vsiz);
7457 tcxstrcat2(rbuf, enc);
7458 TCFREE(enc);
7459 TCFREE(tmp);
7460 TCXSTRCAT(rbuf, ":", 1);
7461 wp++;
7462 tmp = tcurldecode(wp, &vsiz);
7463 enc = tcurlencode(tmp, vsiz);
7464 tcxstrcat2(rbuf, enc);
7465 TCFREE(enc);
7466 TCFREE(tmp);
7467 } else {
7468 tmp = tcurldecode(vbuf, &vsiz);
7469 enc = tcurlencode(tmp, vsiz);
7470 tcxstrcat2(rbuf, enc);
7471 TCFREE(enc);
7472 TCFREE(tmp);
7473 }
7474 TCXSTRCAT(rbuf, "@", 1);
7475 }
7476 if((vbuf = tcmapget2(belems, "host")) != NULL){
7477 tmp = tcurldecode(vbuf, &vsiz);
7478 tcstrtolower(tmp);
7479 enc = tcurlencode(tmp, vsiz);
7480 tcxstrcat2(rbuf, enc);
7481 TCFREE(enc);
7482 TCFREE(tmp);
7483 } else {
7484 TCXSTRCAT(rbuf, "localhost", 9);
7485 }
7486 int num;
7487 char numbuf[TCNUMBUFSIZ];
7488 if((vbuf = tcmapget2(belems, "port")) != NULL && (num = tcatoi(vbuf)) != port && num > 0){
7489 sprintf(numbuf, ":%d", num);
7490 tcxstrcat2(rbuf, numbuf);
7491 }
7492 if(!(path = tcmapget2(telems, "path"))) path = "/";
7493 if(path[0] == '\0' && (vbuf = tcmapget2(belems, "path")) != NULL) path = vbuf;
7494 if(path[0] == '\0') path = "/";
7495 TCLIST *bpaths = tclistnew();
7496 TCLIST *opaths;
7497 if(path[0] != '/' && (vbuf = tcmapget2(belems, "path")) != NULL){
7498 opaths = tcstrsplit(vbuf, "/");
7499 } else {
7500 opaths = tcstrsplit("/", "/");
7501 }
7502 TCFREE(tclistpop2(opaths));
7503 for(int i = 0; i < TCLISTNUM(opaths); i++){
7504 vbuf = tclistval(opaths, i, &vsiz);
7505 if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
7506 if(!strcmp(vbuf, "..")){
7507 TCFREE(tclistpop2(bpaths));
7508 } else {
7509 TCLISTPUSH(bpaths, vbuf, vsiz);
7510 }
7511 }
7512 tclistdel(opaths);
7513 opaths = tcstrsplit(path, "/");
7514 for(int i = 0; i < TCLISTNUM(opaths); i++){
7515 vbuf = tclistval(opaths, i, &vsiz);
7516 if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
7517 if(!strcmp(vbuf, "..")){
7518 TCFREE(tclistpop2(bpaths));
7519 } else {
7520 TCLISTPUSH(bpaths, vbuf, vsiz);
7521 }
7522 }
7523 tclistdel(opaths);
7524 for(int i = 0; i < TCLISTNUM(bpaths); i++){
7525 vbuf = TCLISTVALPTR(bpaths, i);
7526 if(strchr(vbuf, '%')){
7527 tmp = tcurldecode(vbuf, &vsiz);
7528 } else {
7529 tmp = tcstrdup(vbuf);
7530 }
7531 enc = tcurlencode(tmp, strlen(tmp));
7532 TCXSTRCAT(rbuf, "/", 1);
7533 tcxstrcat2(rbuf, enc);
7534 TCFREE(enc);
7535 TCFREE(tmp);
7536 }
7537 if(tcstrbwm(path, "/")) TCXSTRCAT(rbuf, "/", 1);
7538 tclistdel(bpaths);
7539 if((vbuf = tcmapget2(telems, "query")) != NULL ||
7540 (*target == '#' && (vbuf = tcmapget2(belems, "query")) != NULL)){
7541 TCXSTRCAT(rbuf, "?", 1);
7542 TCLIST *qelems = tcstrsplit(vbuf, "&;");
7543 for(int i = 0; i < TCLISTNUM(qelems); i++){
7544 vbuf = TCLISTVALPTR(qelems, i);
7545 if(i > 0) TCXSTRCAT(rbuf, "&", 1);
7546 if((wp = strchr(vbuf, '=')) != NULL){
7547 *wp = '\0';
7548 tmp = tcurldecode(vbuf, &vsiz);
7549 enc = tcurlencode(tmp, vsiz);
7550 tcxstrcat2(rbuf, enc);
7551 TCFREE(enc);
7552 TCFREE(tmp);
7553 TCXSTRCAT(rbuf, "=", 1);
7554 wp++;
7555 tmp = tcurldecode(wp, &vsiz);
7556 enc = tcurlencode(tmp, strlen(tmp));
7557 tcxstrcat2(rbuf, enc);
7558 TCFREE(enc);
7559 TCFREE(tmp);
7560 } else {
7561 tmp = tcurldecode(vbuf, &vsiz);
7562 enc = tcurlencode(tmp, vsiz);
7563 tcxstrcat2(rbuf, enc);
7564 TCFREE(enc);
7565 TCFREE(tmp);
7566 }
7567 }
7568 tclistdel(qelems);
7569 }
7570 if((vbuf = tcmapget2(telems, "fragment")) != NULL){
7571 tmp = tcurldecode(vbuf, &vsiz);
7572 enc = tcurlencode(tmp, vsiz);
7573 TCXSTRCAT(rbuf, "#", 1);
7574 tcxstrcat2(rbuf, enc);
7575 TCFREE(enc);
7576 TCFREE(tmp);
7577 }
7578 tcmapdel(belems);
7579 tcmapdel(telems);
7580 return tcxstrtomalloc(rbuf);
7581 }
7582
7583
7584 /* Encode a serial object with Base64 encoding. */
tcbaseencode(const char * ptr,int size)7585 char *tcbaseencode(const char *ptr, int size){
7586 assert(ptr && size >= 0);
7587 char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7588 const unsigned char *obj = (const unsigned char *)ptr;
7589 char *buf;
7590 TCMALLOC(buf, 4 * (size + 2) / 3 + 1);
7591 char *wp = buf;
7592 for(int i = 0; i < size; i += 3){
7593 switch(size - i){
7594 case 1:
7595 *wp++ = tbl[obj[0] >> 2];
7596 *wp++ = tbl[(obj[0] & 3) << 4];
7597 *wp++ = '=';
7598 *wp++ = '=';
7599 break;
7600 case 2:
7601 *wp++ = tbl[obj[0] >> 2];
7602 *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
7603 *wp++ = tbl[(obj[1] & 0xf) << 2];
7604 *wp++ = '=';
7605 break;
7606 default:
7607 *wp++ = tbl[obj[0] >> 2];
7608 *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
7609 *wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)];
7610 *wp++ = tbl[obj[2] & 0x3f];
7611 break;
7612 }
7613 obj += 3;
7614 }
7615 *wp = '\0';
7616 return buf;
7617 }
7618
7619
7620 /* Decode a string encoded with Base64 encoding. */
tcbasedecode(const char * str,int * sp)7621 char *tcbasedecode(const char *str, int *sp){
7622 assert(str && sp);
7623 int cnt = 0;
7624 int bpos = 0;
7625 int eqcnt = 0;
7626 int len = strlen(str);
7627 unsigned char *obj;
7628 TCMALLOC(obj, len + 4);
7629 unsigned char *wp = obj;
7630 while(bpos < len && eqcnt == 0){
7631 int bits = 0;
7632 int i;
7633 for(i = 0; bpos < len && i < 4; bpos++){
7634 if(str[bpos] >= 'A' && str[bpos] <= 'Z'){
7635 bits = (bits << 6) | (str[bpos] - 'A');
7636 i++;
7637 } else if(str[bpos] >= 'a' && str[bpos] <= 'z'){
7638 bits = (bits << 6) | (str[bpos] - 'a' + 26);
7639 i++;
7640 } else if(str[bpos] >= '0' && str[bpos] <= '9'){
7641 bits = (bits << 6) | (str[bpos] - '0' + 52);
7642 i++;
7643 } else if(str[bpos] == '+'){
7644 bits = (bits << 6) | 62;
7645 i++;
7646 } else if(str[bpos] == '/'){
7647 bits = (bits << 6) | 63;
7648 i++;
7649 } else if(str[bpos] == '='){
7650 bits <<= 6;
7651 i++;
7652 eqcnt++;
7653 }
7654 }
7655 if(i == 0 && bpos >= len) continue;
7656 switch(eqcnt){
7657 case 0:
7658 *wp++ = (bits >> 16) & 0xff;
7659 *wp++ = (bits >> 8) & 0xff;
7660 *wp++ = bits & 0xff;
7661 cnt += 3;
7662 break;
7663 case 1:
7664 *wp++ = (bits >> 16) & 0xff;
7665 *wp++ = (bits >> 8) & 0xff;
7666 cnt += 2;
7667 break;
7668 case 2:
7669 *wp++ = (bits >> 16) & 0xff;
7670 cnt += 1;
7671 break;
7672 }
7673 }
7674 obj[cnt] = '\0';
7675 *sp = cnt;
7676 return (char *)obj;
7677 }
7678
7679
7680 /* Encode a serial object with Quoted-printable encoding. */
tcquoteencode(const char * ptr,int size)7681 char *tcquoteencode(const char *ptr, int size){
7682 assert(ptr && size >= 0);
7683 const unsigned char *rp = (const unsigned char *)ptr;
7684 char *buf;
7685 TCMALLOC(buf, size * 3 + 1);
7686 char *wp = buf;
7687 int cols = 0;
7688 for(int i = 0; i < size; i++){
7689 if(rp[i] == '=' || (rp[i] < 0x20 && rp[i] != '\r' && rp[i] != '\n' && rp[i] != '\t') ||
7690 rp[i] > 0x7e){
7691 wp += sprintf(wp, "=%02X", rp[i]);
7692 cols += 3;
7693 } else {
7694 *(wp++) = rp[i];
7695 cols++;
7696 }
7697 }
7698 *wp = '\0';
7699 return buf;
7700 }
7701
7702
7703 /* Decode a string encoded with Quoted-printable encoding. */
tcquotedecode(const char * str,int * sp)7704 char *tcquotedecode(const char *str, int *sp){
7705 assert(str && sp);
7706 char *buf;
7707 TCMALLOC(buf, strlen(str) + 1);
7708 char *wp = buf;
7709 for(; *str != '\0'; str++){
7710 if(*str == '='){
7711 str++;
7712 if(*str == '\0'){
7713 break;
7714 } else if(str[0] == '\r' && str[1] == '\n'){
7715 str++;
7716 } else if(str[0] != '\n' && str[0] != '\r'){
7717 if(*str >= 'A' && *str <= 'Z'){
7718 *wp = (*str - 'A' + 10) * 16;
7719 } else if(*str >= 'a' && *str <= 'z'){
7720 *wp = (*str - 'a' + 10) * 16;
7721 } else {
7722 *wp = (*str - '0') * 16;
7723 }
7724 str++;
7725 if(*str == '\0') break;
7726 if(*str >= 'A' && *str <= 'Z'){
7727 *wp += *str - 'A' + 10;
7728 } else if(*str >= 'a' && *str <= 'z'){
7729 *wp += *str - 'a' + 10;
7730 } else {
7731 *wp += *str - '0';
7732 }
7733 wp++;
7734 }
7735 } else {
7736 *wp = *str;
7737 wp++;
7738 }
7739 }
7740 *wp = '\0';
7741 *sp = wp - buf;
7742 return buf;
7743 }
7744
7745
7746 /* Encode a string with MIME encoding. */
tcmimeencode(const char * str,const char * encname,bool base)7747 char *tcmimeencode(const char *str, const char *encname, bool base){
7748 assert(str && encname);
7749 int len = strlen(str);
7750 char *buf;
7751 TCMALLOC(buf, len * 3 + strlen(encname) + 16);
7752 char *wp = buf;
7753 wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q');
7754 char *enc = base ? tcbaseencode(str, len) : tcquoteencode(str, len);
7755 wp += sprintf(wp, "%s?=", enc);
7756 TCFREE(enc);
7757 return buf;
7758 }
7759
7760
7761 /* Decode a string encoded with MIME encoding. */
tcmimedecode(const char * str,char * enp)7762 char *tcmimedecode(const char *str, char *enp){
7763 assert(str);
7764 if(enp) sprintf(enp, "US-ASCII");
7765 char *buf;
7766 TCMALLOC(buf, strlen(str) + 1);
7767 char *wp = buf;
7768 while(*str != '\0'){
7769 if(tcstrfwm(str, "=?")){
7770 str += 2;
7771 const char *pv = str;
7772 const char *ep = strchr(str, '?');
7773 if(!ep) continue;
7774 if(enp && ep - pv < TCENCBUFSIZ){
7775 memcpy(enp, pv, ep - pv);
7776 enp[ep-pv] = '\0';
7777 }
7778 pv = ep + 1;
7779 bool quoted = (*pv == 'Q' || *pv == 'q');
7780 if(*pv != '\0') pv++;
7781 if(*pv != '\0') pv++;
7782 if(!(ep = strchr(pv, '?'))) continue;
7783 char *tmp;
7784 TCMEMDUP(tmp, pv, ep - pv);
7785 int len;
7786 char *dec = quoted ? tcquotedecode(tmp, &len) : tcbasedecode(tmp, &len);
7787 wp += sprintf(wp, "%s", dec);
7788 TCFREE(dec);
7789 TCFREE(tmp);
7790 str = ep + 1;
7791 if(*str != '\0') str++;
7792 } else {
7793 *(wp++) = *str;
7794 str++;
7795 }
7796 }
7797 *wp = '\0';
7798 return buf;
7799 }
7800
7801
7802 /* Split a string of MIME into headers and the body. */
tcmimebreak(const char * ptr,int size,TCMAP * headers,int * sp)7803 char *tcmimebreak(const char *ptr, int size, TCMAP *headers, int *sp){
7804 assert(ptr && size >= 0 && sp);
7805 const char *head = NULL;
7806 int hlen = 0;
7807 for(int i = 0; i < size; i++){
7808 if(i < size - 4 && ptr[i] == '\r' && ptr[i+1] == '\n' &&
7809 ptr[i+2] == '\r' && ptr[i+3] == '\n'){
7810 head = ptr;
7811 hlen = i;
7812 ptr += i + 4;
7813 size -= i + 4;
7814 break;
7815 } else if(i < size - 2 && ptr[i] == '\n' && ptr[i+1] == '\n'){
7816 head = ptr;
7817 hlen = i;
7818 ptr += i + 2;
7819 size -= i + 2;
7820 break;
7821 }
7822 }
7823 if(head && headers){
7824 char *hbuf;
7825 TCMALLOC(hbuf, hlen + 1);
7826 int wi = 0;
7827 for(int i = 0; i < hlen; i++){
7828 if(head[i] == '\r') continue;
7829 if(i < hlen - 1 && head[i] == '\n' && (head[i+1] == ' ' || head[i+1] == '\t')){
7830 hbuf[wi++] = ' ';
7831 i++;
7832 } else {
7833 hbuf[wi++] = head[i];
7834 }
7835 }
7836 hbuf[wi] = '\0';
7837 TCLIST *list = tcstrsplit(hbuf, "\n");
7838 int ln = TCLISTNUM(list);
7839 for(int i = 0; i < ln; i++){
7840 const char *line = TCLISTVALPTR(list, i);
7841 const char *pv = strchr(line, ':');
7842 if(pv){
7843 char *name;
7844 TCMEMDUP(name, line, pv - line);
7845 for(int j = 0; name[j] != '\0'; j++){
7846 if(name[j] >= 'A' && name[j] <= 'Z') name[j] -= 'A' - 'a';
7847 }
7848 pv++;
7849 while(*pv == ' ' || *pv == '\t'){
7850 pv++;
7851 }
7852 tcmapput2(headers, name, pv);
7853 TCFREE(name);
7854 }
7855 }
7856 tclistdel(list);
7857 TCFREE(hbuf);
7858 const char *pv = tcmapget2(headers, "content-type");
7859 if(pv){
7860 const char *ep = strchr(pv, ';');
7861 if(ep){
7862 tcmapput(headers, "TYPE", 4, pv, ep - pv);
7863 do {
7864 ep++;
7865 while(ep[0] == ' '){
7866 ep++;
7867 }
7868 if(tcstrifwm(ep, "charset=")){
7869 ep += 8;
7870 while(*ep > '\0' && *ep <= ' '){
7871 ep++;
7872 }
7873 if(ep[0] == '"') ep++;
7874 pv = ep;
7875 while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){
7876 ep++;
7877 }
7878 tcmapput(headers, "CHARSET", 7, pv, ep - pv);
7879 } else if(tcstrifwm(ep, "boundary=")){
7880 ep += 9;
7881 while(*ep > '\0' && *ep <= ' '){
7882 ep++;
7883 }
7884 if(ep[0] == '"'){
7885 ep++;
7886 pv = ep;
7887 while(ep[0] != '\0' && ep[0] != '"'){
7888 ep++;
7889 }
7890 } else {
7891 pv = ep;
7892 while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){
7893 ep++;
7894 }
7895 }
7896 tcmapput(headers, "BOUNDARY", 8, pv, ep - pv);
7897 }
7898 } while((ep = strchr(ep, ';')) != NULL);
7899 } else {
7900 tcmapput(headers, "TYPE", 4, pv, strlen(pv));
7901 }
7902 }
7903 if((pv = tcmapget2(headers, "content-disposition")) != NULL){
7904 char *ep = strchr(pv, ';');
7905 if(ep){
7906 tcmapput(headers, "DISPOSITION", 11, pv, ep - pv);
7907 do {
7908 ep++;
7909 while(ep[0] == ' '){
7910 ep++;
7911 }
7912 if(tcstrifwm(ep, "filename=")){
7913 ep += 9;
7914 if(ep[0] == '"') ep++;
7915 pv = ep;
7916 while(ep[0] != '\0' && ep[0] != '"'){
7917 ep++;
7918 }
7919 tcmapput(headers, "FILENAME", 8, pv, ep - pv);
7920 } else if(tcstrifwm(ep, "name=")){
7921 ep += 5;
7922 if(ep[0] == '"') ep++;
7923 pv = ep;
7924 while(ep[0] != '\0' && ep[0] != '"'){
7925 ep++;
7926 }
7927 tcmapput(headers, "NAME", 4, pv, ep - pv);
7928 }
7929 } while((ep = strchr(ep, ';')) != NULL);
7930 } else {
7931 tcmapput(headers, "DISPOSITION", 11, pv, strlen(pv));
7932 }
7933 }
7934 }
7935 *sp = size;
7936 char *rv;
7937 TCMEMDUP(rv, ptr, size);
7938 return rv;
7939 }
7940
7941
7942 /* Split multipart data of MIME into its parts. */
tcmimeparts(const char * ptr,int size,const char * boundary)7943 TCLIST *tcmimeparts(const char *ptr, int size, const char *boundary){
7944 assert(ptr && size >= 0 && boundary);
7945 TCLIST *list = tclistnew();
7946 int blen = strlen(boundary);
7947 if(blen < 1) return list;
7948 const char *pv = NULL;
7949 for(int i = 0; i < size; i++){
7950 if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size &&
7951 tcstrfwm(ptr + i + 2, boundary) && strchr("\t\n\v\f\r ", ptr[i+2+blen])){
7952 pv = ptr + i + 2 + blen;
7953 if(*pv == '\r') pv++;
7954 if(*pv == '\n') pv++;
7955 size -= pv - ptr;
7956 ptr = pv;
7957 break;
7958 }
7959 }
7960 if(!pv) return list;
7961 for(int i = 0; i < size; i++){
7962 if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size &&
7963 tcstrfwm(ptr + i + 2, boundary) && strchr("\t\n\v\f\r -", ptr[i+2+blen])){
7964 const char *ep = ptr + i;
7965 if(ep > ptr && ep[-1] == '\n') ep--;
7966 if(ep > ptr && ep[-1] == '\r') ep--;
7967 if(ep > pv) TCLISTPUSH(list, pv, ep - pv);
7968 pv = ptr + i + 2 + blen;
7969 if(*pv == '\r') pv++;
7970 if(*pv == '\n') pv++;
7971 }
7972 }
7973 return list;
7974 }
7975
7976
7977 /* Encode a serial object with hexadecimal encoding. */
tchexencode(const char * ptr,int size)7978 char *tchexencode(const char *ptr, int size){
7979 assert(ptr && size >= 0);
7980 const unsigned char *rp = (const unsigned char *)ptr;
7981 char *buf;
7982 TCMALLOC(buf, size * 2 + 1);
7983 char *wp = buf;
7984 for(int i = 0; i < size; i++){
7985 wp += sprintf(wp, "%02x", rp[i]);
7986 }
7987 *wp = '\0';
7988 return buf;
7989 }
7990
7991
7992 /* Decode a string encoded with hexadecimal encoding. */
tchexdecode(const char * str,int * sp)7993 char *tchexdecode(const char *str, int *sp){
7994 assert(str && sp);
7995 int len = strlen(str);
7996 char *buf;
7997 TCMALLOC(buf, len + 1);
7998 char *wp = buf;
7999 for(int i = 0; i < len; i += 2){
8000 while(str[i] >= '\0' && str[i] <= ' '){
8001 i++;
8002 }
8003 int num = 0;
8004 int c = str[i];
8005 if(c == '\0') break;
8006 if(c >= '0' && c <= '9'){
8007 num = c - '0';
8008 } else if(c >= 'a' && c <= 'f'){
8009 num = c - 'a' + 10;
8010 } else if(c >= 'A' && c <= 'F'){
8011 num = c - 'A' + 10;
8012 } else if(c == '\0'){
8013 break;
8014 }
8015 c = str[i+1];
8016 if(c >= '0' && c <= '9'){
8017 num = num * 0x10 + c - '0';
8018 } else if(c >= 'a' && c <= 'f'){
8019 num = num * 0x10 + c - 'a' + 10;
8020 } else if(c >= 'A' && c <= 'F'){
8021 num = num * 0x10 + c - 'A' + 10;
8022 } else if(c == '\0'){
8023 break;
8024 }
8025 *(wp++) = num;
8026 }
8027 *wp = '\0';
8028 *sp = wp - buf;
8029 return buf;
8030 }
8031
8032
8033 /* Compress a serial object with Packbits encoding. */
tcpackencode(const char * ptr,int size,int * sp)8034 char *tcpackencode(const char *ptr, int size, int *sp){
8035 assert(ptr && size >= 0 && sp);
8036 char *buf;
8037 TCMALLOC(buf, size * 2 + 1);
8038 char *wp = buf;
8039 const char *end = ptr + size;
8040 while(ptr < end){
8041 char *sp = wp;
8042 const char *rp = ptr + 1;
8043 int step = 1;
8044 while(rp < end && step < 0x7f && *rp == *ptr){
8045 step++;
8046 rp++;
8047 }
8048 if(step <= 1 && rp < end){
8049 wp = sp + 1;
8050 *(wp++) = *ptr;
8051 while(rp < end && step < 0x7f && *rp != *(rp - 1)){
8052 *(wp++) = *rp;
8053 step++;
8054 rp++;
8055 }
8056 if(rp < end && *(rp - 1) == *rp){
8057 wp--;
8058 rp--;
8059 step--;
8060 }
8061 *sp = step == 1 ? 1 : -step;
8062 } else {
8063 *(wp++) = step;
8064 *(wp++) = *ptr;
8065 }
8066 ptr += step;
8067 }
8068 *sp = wp - buf;
8069 return buf;
8070 }
8071
8072
8073 /* Decompress a serial object compressed with Packbits encoding. */
tcpackdecode(const char * ptr,int size,int * sp)8074 char *tcpackdecode(const char *ptr, int size, int *sp){
8075 assert(ptr && size >= 0 && sp);
8076 int asiz = size * 3;
8077 char *buf;
8078 TCMALLOC(buf, asiz + 1);
8079 int wi = 0;
8080 const char *end = ptr + size;
8081 while(ptr < end){
8082 int step = abs(*ptr);
8083 if(wi + step >= asiz){
8084 asiz = asiz * 2 + step;
8085 TCREALLOC(buf, buf, asiz + 1);
8086 }
8087 if(*(ptr++) >= 0){
8088 memset(buf + wi, *ptr, step);
8089 ptr++;
8090 } else {
8091 step = tclmin(step, end - ptr);
8092 memcpy(buf + wi, ptr, step);
8093 ptr += step;
8094 }
8095 wi += step;
8096 }
8097 buf[wi] = '\0';
8098 *sp = wi;
8099 return buf;
8100 }
8101
8102
8103 /* Compress a serial object with Deflate encoding. */
tcdeflate(const char * ptr,int size,int * sp)8104 char *tcdeflate(const char *ptr, int size, int *sp){
8105 assert(ptr && size >= 0 && sp);
8106 if(!_tc_deflate) return NULL;
8107 return _tc_deflate(ptr, size, sp, _TCZMZLIB);
8108 }
8109
8110
8111 /* Decompress a serial object compressed with Deflate encoding. */
tcinflate(const char * ptr,int size,int * sp)8112 char *tcinflate(const char *ptr, int size, int *sp){
8113 assert(ptr && size >= 0 && sp);
8114 if(!_tc_inflate) return NULL;
8115 return _tc_inflate(ptr, size, sp, _TCZMZLIB);
8116 }
8117
8118
8119 /* Compress a serial object with GZIP encoding. */
tcgzipencode(const char * ptr,int size,int * sp)8120 char *tcgzipencode(const char *ptr, int size, int *sp){
8121 assert(ptr && size >= 0 && sp);
8122 if(!_tc_deflate) return NULL;
8123 return _tc_deflate(ptr, size, sp, _TCZMGZIP);
8124 }
8125
8126
8127 /* Decompress a serial object compressed with GZIP encoding. */
tcgzipdecode(const char * ptr,int size,int * sp)8128 char *tcgzipdecode(const char *ptr, int size, int *sp){
8129 assert(ptr && size >= 0 && sp);
8130 if(!_tc_inflate) return NULL;
8131 return _tc_inflate(ptr, size, sp, _TCZMGZIP);
8132 }
8133
8134
8135 /* Get the CRC32 checksum of a serial object. */
tcgetcrc(const char * ptr,int size)8136 unsigned int tcgetcrc(const char *ptr, int size){
8137 assert(ptr && size >= 0);
8138 if(!_tc_getcrc) return 0;
8139 return _tc_getcrc(ptr, size);
8140 }
8141
8142
8143 /* Compress a serial object with BZIP2 encoding. */
tcbzipencode(const char * ptr,int size,int * sp)8144 char *tcbzipencode(const char *ptr, int size, int *sp){
8145 assert(ptr && size >= 0 && sp);
8146 if(!_tc_bzcompress) return NULL;
8147 return _tc_bzcompress(ptr, size, sp);
8148 }
8149
8150
8151 /* Decompress a serial object compressed with BZIP2 encoding. */
tcbzipdecode(const char * ptr,int size,int * sp)8152 char *tcbzipdecode(const char *ptr, int size, int *sp){
8153 assert(ptr && size >= 0 && sp);
8154 if(!_tc_bzdecompress) return NULL;
8155 return _tc_bzdecompress(ptr, size, sp);
8156 }
8157
8158
8159 /* Encode an array of nonnegative integers with BER encoding. */
tcberencode(const unsigned int * ary,int anum,int * sp)8160 char *tcberencode(const unsigned int *ary, int anum, int *sp){
8161 assert(ary && anum >= 0 && sp);
8162 char *buf;
8163 TCMALLOC(buf, anum * (sizeof(int) + 1) + 1);
8164 char *wp = buf;
8165 for(int i = 0; i < anum; i++){
8166 unsigned int num = ary[i];
8167 if(num < (1 << 7)){
8168 *(wp++) = num;
8169 } else if(num < (1 << 14)){
8170 *(wp++) = (num >> 7) | 0x80;
8171 *(wp++) = num & 0x7f;
8172 } else if(num < (1 << 21)){
8173 *(wp++) = (num >> 14) | 0x80;
8174 *(wp++) = ((num >> 7) & 0x7f) | 0x80;
8175 *(wp++) = num & 0x7f;
8176 } else if(num < (1 << 28)){
8177 *(wp++) = (num >> 21) | 0x80;
8178 *(wp++) = ((num >> 14) & 0x7f) | 0x80;
8179 *(wp++) = ((num >> 7) & 0x7f) | 0x80;
8180 *(wp++) = num & 0x7f;
8181 } else {
8182 *(wp++) = (num >> 28) | 0x80;
8183 *(wp++) = ((num >> 21) & 0x7f) | 0x80;
8184 *(wp++) = ((num >> 14) & 0x7f) | 0x80;
8185 *(wp++) = ((num >> 7) & 0x7f) | 0x80;
8186 *(wp++) = num & 0x7f;
8187 }
8188 }
8189 *sp = wp - buf;
8190 return buf;
8191 }
8192
8193
8194 /* Decode a serial object encoded with BER encoding. */
tcberdecode(const char * ptr,int size,int * np)8195 unsigned int *tcberdecode(const char *ptr, int size, int *np){
8196 assert(ptr && size >= 0 && np);
8197 unsigned int *buf;
8198 TCMALLOC(buf, size * sizeof(*buf) + 1);
8199 unsigned int *wp = buf;
8200 while(size > 0){
8201 unsigned int num = 0;
8202 int c;
8203 do {
8204 c = *(unsigned char *)ptr;
8205 num = num * 0x80 + (c & 0x7f);
8206 ptr++;
8207 size--;
8208 } while(c >= 0x80 && size > 0);
8209 *(wp++) = num;
8210 }
8211 *np = wp - buf;
8212 return buf;
8213 }
8214
8215
8216 /* Escape meta characters in a string with the entity references of XML. */
tcxmlescape(const char * str)8217 char *tcxmlescape(const char *str){
8218 assert(str);
8219 const char *rp = str;
8220 int bsiz = 0;
8221 while(*rp != '\0'){
8222 switch(*rp){
8223 case '&':
8224 bsiz += 5;
8225 break;
8226 case '<':
8227 bsiz += 4;
8228 break;
8229 case '>':
8230 bsiz += 4;
8231 break;
8232 case '"':
8233 bsiz += 6;
8234 break;
8235 default:
8236 bsiz++;
8237 break;
8238 }
8239 rp++;
8240 }
8241 char *buf;
8242 TCMALLOC(buf, bsiz + 1);
8243 char *wp = buf;
8244 while(*str != '\0'){
8245 switch(*str){
8246 case '&':
8247 memcpy(wp, "&", 5);
8248 wp += 5;
8249 break;
8250 case '<':
8251 memcpy(wp, "<", 4);
8252 wp += 4;
8253 break;
8254 case '>':
8255 memcpy(wp, ">", 4);
8256 wp += 4;
8257 break;
8258 case '"':
8259 memcpy(wp, """, 6);
8260 wp += 6;
8261 break;
8262 default:
8263 *(wp++) = *str;
8264 break;
8265 }
8266 str++;
8267 }
8268 *wp = '\0';
8269 return buf;
8270 }
8271
8272
8273 /* Unescape entity references in a string of XML. */
tcxmlunescape(const char * str)8274 char *tcxmlunescape(const char *str){
8275 assert(str);
8276 char *buf;
8277 TCMALLOC(buf, strlen(str) + 1);
8278 char *wp = buf;
8279 while(*str != '\0'){
8280 if(*str == '&'){
8281 if(tcstrfwm(str, "&")){
8282 *(wp++) = '&';
8283 str += 5;
8284 } else if(tcstrfwm(str, "<")){
8285 *(wp++) = '<';
8286 str += 4;
8287 } else if(tcstrfwm(str, ">")){
8288 *(wp++) = '>';
8289 str += 4;
8290 } else if(tcstrfwm(str, """)){
8291 *(wp++) = '"';
8292 str += 6;
8293 } else {
8294 *(wp++) = *(str++);
8295 }
8296 } else {
8297 *(wp++) = *(str++);
8298 }
8299 }
8300 *wp = '\0';
8301 return buf;
8302 }
8303
8304
8305
8306 /*************************************************************************************************
8307 * encoding utilities (for experts)
8308 *************************************************************************************************/
8309
8310
8311 /* Encode a map object into a string in the x-www-form-urlencoded format. */
tcwwwformencode(const TCMAP * params)8312 char *tcwwwformencode(const TCMAP *params){
8313 assert(params);
8314 TCXSTR *xstr = tcxstrnew3(tcmaprnum(params) * TCXSTRUNIT * 3 + 1);
8315 TCMAPREC *cur = params->cur;
8316 tcmapiterinit((TCMAP *)params);
8317 const char *kbuf;
8318 int ksiz;
8319 while((kbuf = tcmapiternext((TCMAP *)params, &ksiz)) != NULL){
8320 int vsiz;
8321 const char *vbuf = tcmapiterval(kbuf, &vsiz);
8322 char *kenc = tcurlencode(kbuf, ksiz);
8323 char *venc = tcurlencode(vbuf, vsiz);
8324 if(TCXSTRSIZE(xstr) > 0) TCXSTRCAT(xstr, "&", 1);
8325 tcxstrcat2(xstr, kenc);
8326 TCXSTRCAT(xstr, "=", 1);
8327 tcxstrcat2(xstr, venc);
8328 TCFREE(venc);
8329 TCFREE(kenc);
8330 }
8331 ((TCMAP *)params)->cur = cur;
8332 return tcxstrtomalloc(xstr);
8333 }
8334
8335
8336 /* Decode a query string in the x-www-form-urlencoded format. */
tcwwwformdecode(const char * str,TCMAP * params)8337 void tcwwwformdecode(const char *str, TCMAP *params){
8338 assert(str && params);
8339 tcwwwformdecode2(str, strlen(str), NULL, params);
8340 }
8341
8342
8343 /* Decode a data region in the x-www-form-urlencoded or multipart-form-data format. */
tcwwwformdecode2(const void * ptr,int size,const char * type,TCMAP * params)8344 void tcwwwformdecode2(const void *ptr, int size, const char *type, TCMAP *params){
8345 assert(ptr && size >= 0 && params);
8346 if(type && tcstrfwm(tcstrskipspc(type), "multipart/")){
8347 const char *brd = strstr(type, "boundary=");
8348 if(brd){
8349 brd += 9;
8350 if(*brd == '"') brd++;
8351 char *bstr = tcstrdup(brd);
8352 char *wp = strchr(bstr, ';');
8353 if(wp) *wp = '\0';
8354 wp = strchr(bstr, '"');
8355 if(wp) *wp = '\0';
8356 TCLIST *parts = tcmimeparts(ptr, size, bstr);
8357 int pnum = tclistnum(parts);
8358 for(int i = 0; i < pnum; i++){
8359 int psiz;
8360 const char *part = tclistval(parts, i, &psiz);
8361 TCMAP *hmap = tcmapnew2(TCMAPTINYBNUM);
8362 int bsiz;
8363 char *body = tcmimebreak(part, psiz, hmap, &bsiz);
8364 int nsiz;
8365 const char *name = tcmapget(hmap, "NAME", 4, &nsiz);
8366 char numbuf[TCNUMBUFSIZ];
8367 if(!name){
8368 nsiz = sprintf(numbuf, "part:%d", i + 1);
8369 name = numbuf;
8370 }
8371 const char *tenc = tcmapget2(hmap, "content-transfer-encoding");
8372 if(tenc){
8373 if(tcstrifwm(tenc, "base64")){
8374 char *ebuf = tcbasedecode(body, &bsiz);
8375 TCFREE(body);
8376 body = ebuf;
8377 } else if(tcstrifwm(tenc, "quoted-printable")){
8378 char *ebuf = tcquotedecode(body, &bsiz);
8379 TCFREE(body);
8380 body = ebuf;
8381 }
8382 }
8383 tcmapputkeep(params, name, nsiz, body, bsiz);
8384 const char *fname = tcmapget2(hmap, "FILENAME");
8385 if(fname){
8386 if(*fname == '/'){
8387 fname = strrchr(fname, '/') + 1;
8388 } else if(((*fname >= 'a' && *fname <= 'z') || (*fname >= 'A' && *fname <= 'Z')) &&
8389 fname[1] == ':' && fname[2] == '\\'){
8390 fname = strrchr(fname, '\\') + 1;
8391 }
8392 if(*fname != '\0'){
8393 char key[nsiz+10];
8394 sprintf(key, "%s_filename", name);
8395 tcmapput2(params, key, fname);
8396 }
8397 }
8398 tcfree(body);
8399 tcmapdel(hmap);
8400 }
8401 tclistdel(parts);
8402 tcfree(bstr);
8403 }
8404 } else {
8405 const char *rp = ptr;
8406 const char *pv = rp;
8407 const char *ep = rp + size;
8408 char stack[TCIOBUFSIZ];
8409 while(rp < ep){
8410 if(*rp == '&' || *rp == ';'){
8411 while(pv < rp && *pv > '\0' && *pv <= ' '){
8412 pv++;
8413 }
8414 if(rp > pv){
8415 int len = rp - pv;
8416 char *rbuf;
8417 if(len < sizeof(stack)){
8418 rbuf = stack;
8419 } else {
8420 TCMALLOC(rbuf, len + 1);
8421 }
8422 memcpy(rbuf, pv, len);
8423 rbuf[len] = '\0';
8424 char *sep = strchr(rbuf, '=');
8425 if(sep){
8426 *(sep++) = '\0';
8427 } else {
8428 sep = "";
8429 }
8430 int ksiz;
8431 char *kbuf = tcurldecode(rbuf, &ksiz);
8432 int vsiz;
8433 char *vbuf = tcurldecode(sep, &vsiz);
8434 if(!tcmapputkeep(params, kbuf, ksiz, vbuf, vsiz)){
8435 tcmapputcat(params, kbuf, ksiz, "", 1);
8436 tcmapputcat(params, kbuf, ksiz, vbuf, vsiz);
8437 }
8438 TCFREE(vbuf);
8439 TCFREE(kbuf);
8440 if(rbuf != stack) TCFREE(rbuf);
8441 }
8442 pv = rp + 1;
8443 }
8444 rp++;
8445 }
8446 while(pv < rp && *pv > '\0' && *pv <= ' '){
8447 pv++;
8448 }
8449 if(rp > pv){
8450 int len = rp - pv;
8451 char *rbuf;
8452 if(len < sizeof(stack)){
8453 rbuf = stack;
8454 } else {
8455 TCMALLOC(rbuf, len + 1);
8456 }
8457 memcpy(rbuf, pv, len);
8458 rbuf[len] = '\0';
8459 char *sep = strchr(rbuf, '=');
8460 if(sep){
8461 *(sep++) = '\0';
8462 } else {
8463 sep = "";
8464 }
8465 int ksiz;
8466 char *kbuf = tcurldecode(rbuf, &ksiz);
8467 int vsiz;
8468 char *vbuf = tcurldecode(sep, &vsiz);
8469 if(!tcmapputkeep(params, kbuf, ksiz, vbuf, vsiz)){
8470 tcmapputcat(params, kbuf, ksiz, "", 1);
8471 tcmapputcat(params, kbuf, ksiz, vbuf, vsiz);
8472 }
8473 TCFREE(vbuf);
8474 TCFREE(kbuf);
8475 if(rbuf != stack) TCFREE(rbuf);
8476 }
8477 }
8478 }
8479
8480
8481 /* Split an XML string into tags and text sections. */
tcxmlbreak(const char * str)8482 TCLIST *tcxmlbreak(const char *str){
8483 assert(str);
8484 TCLIST *list = tclistnew();
8485 int i = 0;
8486 int pv = 0;
8487 bool tag = false;
8488 char *ep;
8489 while(true){
8490 if(str[i] == '\0'){
8491 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
8492 break;
8493 } else if(!tag && str[i] == '<'){
8494 if(str[i+1] == '!' && str[i+2] == '-' && str[i+3] == '-'){
8495 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
8496 if((ep = strstr(str + i, "-->")) != NULL){
8497 TCLISTPUSH(list, str + i, ep - str - i + 3);
8498 i = ep - str + 2;
8499 pv = i + 1;
8500 }
8501 } else if(str[i+1] == '!' && str[i+2] == '[' && tcstrifwm(str + i, "<![CDATA[")){
8502 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
8503 if((ep = strstr(str + i, "]]>")) != NULL){
8504 i += 9;
8505 TCXSTR *xstr = tcxstrnew();
8506 while(str + i < ep){
8507 if(str[i] == '&'){
8508 TCXSTRCAT(xstr, "&", 5);
8509 } else if(str[i] == '<'){
8510 TCXSTRCAT(xstr, "<", 4);
8511 } else if(str[i] == '>'){
8512 TCXSTRCAT(xstr, ">", 4);
8513 } else {
8514 TCXSTRCAT(xstr, str + i, 1);
8515 }
8516 i++;
8517 }
8518 if(TCXSTRSIZE(xstr) > 0) TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
8519 tcxstrdel(xstr);
8520 i = ep - str + 2;
8521 pv = i + 1;
8522 }
8523 } else {
8524 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
8525 tag = true;
8526 pv = i;
8527 }
8528 } else if(tag && str[i] == '>'){
8529 if(i > pv) TCLISTPUSH(list, str + pv, i - pv + 1);
8530 tag = false;
8531 pv = i + 1;
8532 }
8533 i++;
8534 }
8535 return list;
8536 }
8537
8538
8539 /* Get the map of attributes of an XML tag. */
tcxmlattrs(const char * str)8540 TCMAP *tcxmlattrs(const char *str){
8541 assert(str);
8542 TCMAP *map = tcmapnew2(TCXMLATBNUM);
8543 const unsigned char *rp = (unsigned char *)str;
8544 while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){
8545 rp++;
8546 }
8547 const unsigned char *key = rp;
8548 while(*rp > 0x20 && *rp != '/' && *rp != '>'){
8549 rp++;
8550 }
8551 tcmapputkeep(map, "", 0, (char *)key, rp - key);
8552 while(*rp != '\0'){
8553 while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){
8554 rp++;
8555 }
8556 key = rp;
8557 while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){
8558 rp++;
8559 }
8560 int ksiz = rp - key;
8561 while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){
8562 rp++;
8563 }
8564 const unsigned char *val;
8565 int vsiz;
8566 if(*rp == '"'){
8567 rp++;
8568 val = rp;
8569 while(*rp != '\0' && *rp != '"'){
8570 rp++;
8571 }
8572 vsiz = rp - val;
8573 } else if(*rp == '\''){
8574 rp++;
8575 val = rp;
8576 while(*rp != '\0' && *rp != '\''){
8577 rp++;
8578 }
8579 vsiz = rp - val;
8580 } else {
8581 val = rp;
8582 while(*rp > 0x20 && *rp != '"' && *rp != '\'' && *rp != '>'){
8583 rp++;
8584 }
8585 vsiz = rp - val;
8586 }
8587 if(*rp != '\0') rp++;
8588 if(ksiz > 0){
8589 char *copy;
8590 TCMEMDUP(copy, val, vsiz);
8591 char *raw = tcxmlunescape(copy);
8592 tcmapputkeep(map, (char *)key, ksiz, raw, strlen(raw));
8593 TCFREE(raw);
8594 TCFREE(copy);
8595 }
8596 }
8597 return map;
8598 }
8599
8600
8601 /* Escape meta characters in a string with backslash escaping of the C language. */
tccstrescape(const char * str)8602 char *tccstrescape(const char *str){
8603 assert(str);
8604 int asiz = TCXSTRUNIT * 2;
8605 char *buf;
8606 TCMALLOC(buf, asiz + 4);
8607 int wi = 0;
8608 bool hex = false;
8609 int c;
8610 while((c = *(unsigned char *)str) != '\0'){
8611 if(wi >= asiz){
8612 asiz *= 2;
8613 TCREALLOC(buf, buf, asiz + 4);
8614 }
8615 if(c < ' ' || c == 0x7f || c == '"' || c == '\'' || c == '\\'){
8616 switch(c){
8617 case '\t': wi += sprintf(buf + wi, "\\t"); break;
8618 case '\n': wi += sprintf(buf + wi, "\\n"); break;
8619 case '\r': wi += sprintf(buf + wi, "\\r"); break;
8620 case '\\': wi += sprintf(buf + wi, "\\\\"); break;
8621 default:
8622 wi += sprintf(buf + wi, "\\x%02X", c);
8623 hex = true;
8624 break;
8625 }
8626 } else {
8627 if(hex && ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))){
8628 wi += sprintf(buf + wi, "\\x%02X", c);
8629 hex = true;
8630 } else {
8631 buf[wi++] = c;
8632 hex = false;
8633 }
8634 }
8635 str++;
8636 }
8637 buf[wi] = '\0';
8638 return buf;
8639 }
8640
8641
8642 /* Unescape a string escaped by backslash escaping of the C language. */
tccstrunescape(const char * str)8643 char *tccstrunescape(const char *str){
8644 assert(str);
8645 int asiz = TCXSTRUNIT * 2;
8646 char *buf;
8647 TCMALLOC(buf, asiz + 4);
8648 int wi = 0;
8649 int c;
8650 while((c = *(unsigned char *)str) != '\0'){
8651 if(wi >= asiz){
8652 asiz *= 2;
8653 TCREALLOC(buf, buf, asiz + 4);
8654 }
8655 if(c == '\\' && str[1] != '\0'){
8656 str++;
8657 int si = wi;
8658 switch(*str){
8659 case 'a': buf[wi++] = '\a'; break;
8660 case 'b': buf[wi++] = '\b'; break;
8661 case 't': buf[wi++] = '\t'; break;
8662 case 'n': buf[wi++] = '\n'; break;
8663 case 'v': buf[wi++] = '\v'; break;
8664 case 'f': buf[wi++] = '\f'; break;
8665 case 'r': buf[wi++] = '\r'; break;
8666 }
8667 if(si == wi){
8668 c = *str;
8669 if(c == 'x'){
8670 str++;
8671 int code = 0;
8672 for(int i = 0; i < 2; i++){
8673 c = *str;
8674 if(c >= '0' && c <= '9'){
8675 code = code * 0x10 + c - '0';
8676 } else if(c >= 'A' && c <= 'F'){
8677 code = code * 0x10 + c - 'A' + 10;
8678 } else if(c >= 'a' && c <= 'f'){
8679 code = code * 0x10 + c - 'a' + 10;
8680 } else {
8681 break;
8682 }
8683 str++;
8684 }
8685 buf[wi++] = code;
8686 } else if(c == 'u' || c == 'U'){
8687 int len = (c == 'U') ? 8 : 4;
8688 str++;
8689 int code = 0;
8690 for(int i = 0; i < len; i++){
8691 c = *str;
8692 if(c >= '0' && c <= '9'){
8693 code = code * 0x10 + c - '0';
8694 } else if(c >= 'A' && c <= 'F'){
8695 code = code * 0x10 + c - 'A' + 10;
8696 } else if(c >= 'a' && c <= 'f'){
8697 code = code * 0x10 + c - 'a' + 10;
8698 } else {
8699 break;
8700 }
8701 str++;
8702 }
8703 uint16_t ary[1];
8704 ary[0] = code;
8705 wi += tcstrucstoutf(ary, 1, buf + wi);
8706 } else if(c >= '0' && c <= '8'){
8707 int code = 0;
8708 for(int i = 0; i < 3; i++){
8709 c = *str;
8710 if(c >= '0' && c <= '7'){
8711 code = code * 8 + c - '0';
8712 } else {
8713 break;
8714 }
8715 str++;
8716 }
8717 buf[wi++] = code;
8718 } else if(c != '\0'){
8719 buf[wi++] = c;
8720 str++;
8721 }
8722 } else {
8723 str++;
8724 }
8725 } else {
8726 buf[wi++] = c;
8727 str++;
8728 }
8729 }
8730 buf[wi] = '\0';
8731 return buf;
8732 }
8733
8734
8735 /* Escape meta characters in a string with backslash escaping of JSON. */
tcjsonescape(const char * str)8736 char *tcjsonescape(const char *str){
8737 assert(str);
8738 int asiz = TCXSTRUNIT * 2;
8739 char *buf;
8740 TCMALLOC(buf, asiz + 6);
8741 int wi = 0;
8742 int c;
8743 while((c = *(unsigned char *)str) != '\0'){
8744 if(wi >= asiz){
8745 asiz *= 2;
8746 TCREALLOC(buf, buf, asiz + 6);
8747 }
8748 if(c < ' ' || c == 0x7f || c == '"' || c == '\'' || c == '\\'){
8749 switch(c){
8750 case '\t': wi += sprintf(buf + wi, "\\t"); break;
8751 case '\n': wi += sprintf(buf + wi, "\\n"); break;
8752 case '\r': wi += sprintf(buf + wi, "\\r"); break;
8753 case '\\': wi += sprintf(buf + wi, "\\\\"); break;
8754 default: wi += sprintf(buf + wi, "\\u%04X", c); break;
8755 }
8756 } else {
8757 buf[wi++] = c;
8758 }
8759 str++;
8760 }
8761 buf[wi] = '\0';
8762 return buf;
8763 }
8764
8765
8766 /* Unescape a string escaped by backslash escaping of JSON. */
tcjsonunescape(const char * str)8767 char *tcjsonunescape(const char *str){
8768 assert(str);
8769 return tccstrunescape(str);
8770 }
8771
8772
8773
8774 /*************************************************************************************************
8775 * template serializer
8776 *************************************************************************************************/
8777
8778
8779 #define TCTMPLUNIT 65536 // allocation unit size of a template string
8780 #define TCTMPLMAXDEP 256 // maximum depth of template blocks
8781 #define TCTMPLBEGSEP "[%" // default beginning separator
8782 #define TCTMPLENDSEP "%]" // default beginning separator
8783 #define TCTYPRFXLIST "[list]\0:" // type prefix for a list object
8784 #define TCTYPRFXMAP "[map]\0:" // type prefix for a list object
8785
8786
8787 /* private function prototypes */
8788 static TCLIST *tctmpltokenize(const char *expr);
8789 static int tctmpldumpeval(TCXSTR *xstr, const char *expr, const TCLIST *elems, int cur, int num,
8790 const TCMAP **stack, int depth);
8791 static const char *tctmpldumpevalvar(const TCMAP **stack, int depth, const char *name,
8792 int *sp, int *np);
8793
8794
8795 /* Create a template object. */
tctmplnew(void)8796 TCTMPL *tctmplnew(void){
8797 TCTMPL *tmpl;
8798 TCMALLOC(tmpl, sizeof(*tmpl));
8799 tmpl->elems = NULL;
8800 tmpl->begsep = NULL;
8801 tmpl->endsep = NULL;
8802 tmpl->conf = tcmapnew2(TCMAPTINYBNUM);
8803 return tmpl;
8804 }
8805
8806
8807 /* Delete a template object. */
tctmpldel(TCTMPL * tmpl)8808 void tctmpldel(TCTMPL *tmpl){
8809 assert(tmpl);
8810 tcmapdel(tmpl->conf);
8811 if(tmpl->endsep) TCFREE(tmpl->endsep);
8812 if(tmpl->begsep) TCFREE(tmpl->begsep);
8813 if(tmpl->elems) tclistdel(tmpl->elems);
8814 TCFREE(tmpl);
8815 }
8816
8817
8818 /* Set the separator strings of a template object. */
tctmplsetsep(TCTMPL * tmpl,const char * begsep,const char * endsep)8819 void tctmplsetsep(TCTMPL *tmpl, const char *begsep, const char *endsep){
8820 assert(tmpl && begsep && endsep);
8821 if(tmpl->endsep) TCFREE(tmpl->endsep);
8822 if(tmpl->begsep) TCFREE(tmpl->begsep);
8823 tmpl->begsep = tcstrdup(begsep);
8824 tmpl->endsep = tcstrdup(endsep);
8825 }
8826
8827
8828 /* Load a template string into a template object. */
tctmplload(TCTMPL * tmpl,const char * str)8829 void tctmplload(TCTMPL *tmpl, const char *str){
8830 assert(tmpl && str);
8831 const char *begsep = tmpl->begsep;
8832 if(!begsep) begsep = TCTMPLBEGSEP;
8833 const char *endsep = tmpl->endsep;
8834 if(!endsep) endsep = TCTMPLENDSEP;
8835 int beglen = strlen(begsep);
8836 int endlen = strlen(endsep);
8837 if(beglen < 1 || endlen < 1) return;
8838 int begchr = *begsep;
8839 int endchr = *endsep;
8840 if(tmpl->elems) tclistdel(tmpl->elems);
8841 tcmapclear(tmpl->conf);
8842 TCLIST *elems = tclistnew();
8843 const char *rp = str;
8844 const char *pv = rp;
8845 while(*rp != '\0'){
8846 if(*rp == begchr && tcstrfwm(rp, begsep)){
8847 if(rp > pv) TCLISTPUSH(elems, pv, rp - pv);
8848 rp += beglen;
8849 pv = rp;
8850 while(*rp != '\0'){
8851 if(*rp == endchr && tcstrfwm(rp, endsep)){
8852 bool chop = false;
8853 while(pv < rp && *pv > '\0' && *pv <= ' '){
8854 pv++;
8855 }
8856 if(*pv == '"'){
8857 pv++;
8858 const char *sp = pv;
8859 while(pv < rp && *pv != '"'){
8860 pv++;
8861 }
8862 if(pv > sp) TCLISTPUSH(elems, sp, pv - sp);
8863 } else if(*pv == '\''){
8864 pv++;
8865 const char *sp = pv;
8866 while(pv < rp && *pv != '\''){
8867 pv++;
8868 }
8869 if(pv > sp) TCLISTPUSH(elems, sp, pv - sp);
8870 } else {
8871 const char *ep = rp;
8872 if(ep > pv && ep[-1] == '\\'){
8873 ep--;
8874 chop = true;
8875 }
8876 while(ep > pv && ((unsigned char *)ep)[-1] <= ' '){
8877 ep--;
8878 }
8879 int len = ep - pv;
8880 char *buf;
8881 TCMALLOC(buf, len + 1);
8882 *buf = '\0';
8883 memcpy(buf + 1, pv, len);
8884 tclistpushmalloc(elems, buf, len + 1);
8885 if(tcstrfwm(pv, "CONF")){
8886 const char *expr = (char *)TCLISTVALPTR(elems, TCLISTNUM(elems) - 1) + 1;
8887 TCLIST *tokens = tctmpltokenize(expr);
8888 int tnum = TCLISTNUM(tokens);
8889 if(tnum > 1 && !strcmp(TCLISTVALPTR(tokens, 0), "CONF")){
8890 const char *name = TCLISTVALPTR(tokens, 1);
8891 const char *value = (tnum > 2) ? TCLISTVALPTR(tokens, 2) : "";
8892 tcmapput2(tmpl->conf, name, value);
8893 }
8894 tclistdel(tokens);
8895 }
8896 }
8897 rp += endlen;
8898 if(chop){
8899 if(*rp == '\r') rp++;
8900 if(*rp == '\n') rp++;
8901 }
8902 break;
8903 }
8904 rp++;
8905 }
8906 pv = rp;
8907 } else {
8908 rp++;
8909 }
8910 }
8911 if(rp > pv) TCLISTPUSH(elems, pv, rp - pv);
8912 tmpl->elems = elems;
8913 }
8914
8915
8916 /* Load a template string from a file into a template object. */
tctmplload2(TCTMPL * tmpl,const char * path)8917 bool tctmplload2(TCTMPL *tmpl, const char *path){
8918 assert(tmpl && path);
8919 char *str = tcreadfile(path, -1, NULL);
8920 if(!str) return false;
8921 tctmplload(tmpl, str);
8922 TCFREE(str);
8923 return true;
8924 }
8925
8926
8927 /* Serialize the template string of a template object. */
tctmpldump(TCTMPL * tmpl,const TCMAP * vars)8928 char *tctmpldump(TCTMPL *tmpl, const TCMAP *vars){
8929 assert(tmpl && vars);
8930 TCXSTR *xstr = tcxstrnew3(TCTMPLUNIT);
8931 TCLIST *elems = tmpl->elems;
8932 if(elems){
8933 TCMAP *svars = tcmapnew2(TCMAPTINYBNUM);
8934 int cur = 0;
8935 int num = TCLISTNUM(elems);
8936 const TCMAP *stack[TCTMPLMAXDEP];
8937 int depth = 0;
8938 stack[depth++] = tmpl->conf;
8939 stack[depth++] = svars;
8940 stack[depth++] = vars;
8941 while(cur < num){
8942 const char *elem;
8943 int esiz;
8944 TCLISTVAL(elem, elems, cur, esiz);
8945 if(*elem == '\0' && esiz > 0){
8946 cur = tctmpldumpeval(xstr, elem + 1, elems, cur, num, stack, depth);
8947 } else {
8948 TCXSTRCAT(xstr, elem, esiz);
8949 cur++;
8950 }
8951 }
8952 tcmapdel(svars);
8953 }
8954 return tcxstrtomalloc(xstr);
8955 }
8956
8957
8958 /* Get the value of a configuration variable of a template object. */
tctmplconf(TCTMPL * tmpl,const char * name)8959 const char *tctmplconf(TCTMPL *tmpl, const char *name){
8960 assert(tmpl && name);
8961 return tcmapget2(tmpl->conf, name);
8962 }
8963
8964
8965 /* Store a list object into a list object with the type information. */
tclistpushlist(TCLIST * list,const TCLIST * obj)8966 void tclistpushlist(TCLIST *list, const TCLIST *obj){
8967 assert(list && obj);
8968 char vbuf[sizeof(TCTYPRFXLIST) - 1 + sizeof(obj)];
8969 memcpy(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1);
8970 memcpy(vbuf + sizeof(TCTYPRFXLIST) - 1, &obj, sizeof(obj));
8971 tclistpush(list, vbuf, sizeof(vbuf));
8972 }
8973
8974
8975 /* Store a map object into a list object with the type information. */
tclistpushmap(TCLIST * list,const TCMAP * obj)8976 void tclistpushmap(TCLIST *list, const TCMAP *obj){
8977 assert(list && obj);
8978 char vbuf[sizeof(TCTYPRFXMAP) - 1 + sizeof(obj)];
8979 memcpy(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1);
8980 memcpy(vbuf + sizeof(TCTYPRFXMAP) - 1, &obj, sizeof(obj));
8981 tclistpush(list, vbuf, sizeof(vbuf));
8982 }
8983
8984
8985 /* Store a list object into a map object with the type information. */
tcmapputlist(TCMAP * map,const char * kstr,const TCLIST * obj)8986 void tcmapputlist(TCMAP *map, const char *kstr, const TCLIST *obj){
8987 assert(map && kstr && obj);
8988 char vbuf[sizeof(TCTYPRFXLIST) - 1 + sizeof(obj)];
8989 memcpy(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1);
8990 memcpy(vbuf + sizeof(TCTYPRFXLIST) - 1, &obj, sizeof(obj));
8991 tcmapput(map, kstr, strlen(kstr), vbuf, sizeof(vbuf));
8992 }
8993
8994
8995 /* Store a map object into a map object with the type information. */
tcmapputmap(TCMAP * map,const char * kstr,const TCMAP * obj)8996 void tcmapputmap(TCMAP *map, const char *kstr, const TCMAP *obj){
8997 assert(map && kstr && obj);
8998 char vbuf[sizeof(TCTYPRFXMAP) - 1 + sizeof(obj)];
8999 memcpy(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1);
9000 memcpy(vbuf + sizeof(TCTYPRFXMAP) - 1, &obj, sizeof(obj));
9001 tcmapput(map, kstr, strlen(kstr), vbuf, sizeof(vbuf));
9002 }
9003
9004
9005 /* Tokenize an template expression.
9006 `expr' specifies the expression.
9007 The return value is a list object of tokens. */
tctmpltokenize(const char * expr)9008 static TCLIST *tctmpltokenize(const char *expr){
9009 TCLIST *tokens = tclistnew();
9010 const unsigned char *rp = (unsigned char *)expr;
9011 while(*rp != '\0'){
9012 while(*rp > '\0' && *rp <= ' '){
9013 rp++;
9014 }
9015 const unsigned char *pv = rp;
9016 if(*rp == '"'){
9017 pv++;
9018 rp++;
9019 while(*rp != '\0' && *rp != '"'){
9020 rp++;
9021 }
9022 TCLISTPUSH(tokens, pv, rp - pv);
9023 if(*rp == '"') rp++;
9024 } else if(*rp == '\''){
9025 pv++;
9026 rp++;
9027 while(*rp != '\0' && *rp != '\''){
9028 rp++;
9029 }
9030 TCLISTPUSH(tokens, pv, rp - pv);
9031 if(*rp == '\'') rp++;
9032 } else {
9033 while(*rp > ' '){
9034 rp++;
9035 }
9036 if(rp > pv) TCLISTPUSH(tokens, pv, rp - pv);
9037 }
9038 }
9039 return tokens;
9040 }
9041
9042
9043 /* Evaluate an template expression.
9044 `xstr' specifies the output buffer.
9045 `expr' specifies the expression.
9046 `elems' specifies the list of elements.
9047 `cur' specifies the current offset of the elements.
9048 `num' specifies the number of the elements.
9049 `stack' specifies the variable scope stack.
9050 `depth' specifies the current depth of the stack.
9051 The return value is the next offset of the elements. */
tctmpldumpeval(TCXSTR * xstr,const char * expr,const TCLIST * elems,int cur,int num,const TCMAP ** stack,int depth)9052 static int tctmpldumpeval(TCXSTR *xstr, const char *expr, const TCLIST *elems, int cur, int num,
9053 const TCMAP **stack, int depth){
9054 assert(expr && elems && cur >= 0 && num >= 0 && stack && depth >= 0);
9055 cur++;
9056 TCLIST *tokens = tctmpltokenize(expr);
9057 int tnum = TCLISTNUM(tokens);
9058 if(tnum > 0){
9059 const char *cmd = TCLISTVALPTR(tokens, 0);
9060 if(!strcmp(cmd, "IF")){
9061 bool sign = true;
9062 const char *eq = NULL;
9063 const char *inc = NULL;
9064 bool prt = false;
9065 const char *rx = NULL;
9066 for(int i = 1; i < tnum; i++){
9067 const char *token = TCLISTVALPTR(tokens, i);
9068 if(!strcmp(token, "NOT")){
9069 sign = !sign;
9070 } else if(!strcmp(token, "EQ")){
9071 if(++i < tnum) eq = TCLISTVALPTR(tokens, i);
9072 } else if(!strcmp(token, "INC")){
9073 if(++i < tnum) inc = TCLISTVALPTR(tokens, i);
9074 } else if(!strcmp(token, "PRT")){
9075 prt = true;
9076 } else if(!strcmp(token, "RX")){
9077 if(++i < tnum) rx = TCLISTVALPTR(tokens, i);
9078 }
9079 }
9080 TCXSTR *altxstr = NULL;
9081 if(xstr){
9082 const char *name = (tnum > 1) ? TCLISTVALPTR(tokens, 1) : "__";
9083 int vsiz, vnum;
9084 const char *vbuf = tctmpldumpevalvar(stack, depth, name, &vsiz, &vnum);
9085 char numbuf[TCNUMBUFSIZ];
9086 if(vbuf && vnum >= 0){
9087 vsiz = sprintf(numbuf, "%d", vnum);
9088 vbuf = numbuf;
9089 }
9090 bool bval = false;
9091 if(vbuf){
9092 if(eq){
9093 if(!strcmp(vbuf, eq)) bval = true;
9094 } else if(inc){
9095 if(strstr(vbuf, inc)) bval = true;
9096 } else if(prt){
9097 if(*tcstrskipspc(vbuf) != '\0') bval = true;
9098 } else if(rx){
9099 if(tcregexmatch(vbuf, rx)) bval = true;
9100 } else {
9101 bval = true;
9102 }
9103 }
9104 if(bval != sign){
9105 altxstr = xstr;
9106 xstr = NULL;
9107 }
9108 }
9109 while(cur < num){
9110 const char *elem;
9111 int esiz;
9112 TCLISTVAL(elem, elems, cur, esiz);
9113 if(*elem == '\0' && esiz > 0){
9114 cur = tctmpldumpeval(xstr, elem + 1, elems, cur, num, stack, depth);
9115 if(!strcmp(elem + 1, "ELSE")){
9116 xstr = altxstr;
9117 } else if(!strcmp(elem + 1, "END")){
9118 break;
9119 }
9120 } else {
9121 if(xstr) TCXSTRCAT(xstr, elem, esiz);
9122 cur++;
9123 }
9124 }
9125 } else if(!strcmp(cmd, "FOREACH")){
9126 const TCLIST *list = NULL;
9127 if(xstr){
9128 const char *name = (tnum > 1) ? TCLISTVALPTR(tokens, 1) : "";
9129 int vsiz, vnum;
9130 const char *vbuf = tctmpldumpevalvar(stack, depth, name, &vsiz, &vnum);
9131 if(vbuf && vsiz == sizeof(TCTYPRFXLIST) - 1 + sizeof(list) &&
9132 !memcmp(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1)){
9133 memcpy(&list, vbuf + sizeof(TCTYPRFXLIST) - 1, sizeof(list));
9134 }
9135 }
9136 if(list && TCLISTNUM(list) > 0){
9137 const char *name = (tnum > 2) ? TCLISTVALPTR(tokens, 2) : "";
9138 TCMAP *vars = tcmapnew2(1);
9139 if(depth < TCTMPLMAXDEP){
9140 stack[depth] = vars;
9141 depth++;
9142 }
9143 int lnum = TCLISTNUM(list);
9144 int beg = cur;
9145 for(int i = 0; i < lnum; i++){
9146 const char *vbuf;
9147 int vsiz;
9148 TCLISTVAL(vbuf, list, i, vsiz);
9149 if(vsiz == sizeof(TCTYPRFXLIST) - 1 + sizeof(TCLIST *) &&
9150 !memcmp(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1)){
9151 TCLIST *obj;
9152 memcpy(&obj, vbuf + sizeof(TCTYPRFXLIST) - 1, sizeof(obj));
9153 tcmapputlist(vars, name, obj);
9154 } else if(vsiz == sizeof(TCTYPRFXMAP) - 1 + sizeof(TCMAP *) &&
9155 !memcmp(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1)){
9156 TCMAP *obj;
9157 memcpy(&obj, vbuf + sizeof(TCTYPRFXMAP) - 1, sizeof(obj));
9158 tcmapputmap(vars, name, obj);
9159 } else {
9160 tcmapput2(vars, name, vbuf);
9161 }
9162 cur = beg;
9163 while(cur < num){
9164 const char *elem;
9165 int esiz;
9166 TCLISTVAL(elem, elems, cur, esiz);
9167 if(*elem == '\0' && esiz > 0){
9168 cur = tctmpldumpeval(xstr, elem + 1, elems, cur, num, stack, depth);
9169 if(!strcmp(elem + 1, "END")) break;
9170 } else {
9171 if(xstr) TCXSTRCAT(xstr, elem, esiz);
9172 cur++;
9173 }
9174 }
9175 }
9176 tcmapdel(vars);
9177 } else {
9178 while(cur < num){
9179 const char *elem;
9180 int esiz;
9181 TCLISTVAL(elem, elems, cur, esiz);
9182 if(*elem == '\0' && esiz > 0){
9183 cur = tctmpldumpeval(NULL, elem + 1, elems, cur, num, stack, depth);
9184 if(!strcmp(elem + 1, "END")) break;
9185 } else {
9186 cur++;
9187 }
9188 }
9189 }
9190 } else if(!strcmp(cmd, "SET")){
9191 if(xstr){
9192 const char *name = (tnum > 1) ? TCLISTVALPTR(tokens, 1) : "";
9193 const char *value = (tnum > 2) ? TCLISTVALPTR(tokens, 2) : "";
9194 tcmapput2((TCMAP *)stack[1], name, value);
9195 }
9196 } else if(xstr){
9197 int vsiz, vnum;
9198 const char *vbuf = tctmpldumpevalvar(stack, depth, cmd, &vsiz, &vnum);
9199 char numbuf[TCNUMBUFSIZ];
9200 if(vbuf && vnum >= 0){
9201 vsiz = sprintf(numbuf, "%d", vnum);
9202 vbuf = numbuf;
9203 }
9204 const char *enc = "";
9205 const char *def = NULL;
9206 for(int i = 1; i < tnum; i++){
9207 const char *token = TCLISTVALPTR(tokens, i);
9208 if(!strcmp(token, "ENC")){
9209 if(++i < tnum) enc = TCLISTVALPTR(tokens, i);
9210 } else if(!strcmp(token, "DEF")){
9211 if(++i < tnum) def = TCLISTVALPTR(tokens, i);
9212 }
9213 }
9214 if(!vbuf && def){
9215 vbuf = def;
9216 vsiz = strlen(def);
9217 }
9218 if(vbuf){
9219 if(!strcmp(enc, "URL")){
9220 char *ebuf = tcurlencode(vbuf, vsiz);
9221 tcxstrcat2(xstr, ebuf);
9222 TCFREE(ebuf);
9223 } else if(!strcmp(enc, "BASE")){
9224 char *ebuf = tcbaseencode(vbuf, vsiz);
9225 tcxstrcat2(xstr, ebuf);
9226 TCFREE(ebuf);
9227 } else if(!strcmp(enc, "QUOTE")){
9228 char *ebuf = tcquoteencode(vbuf, vsiz);
9229 tcxstrcat2(xstr, ebuf);
9230 TCFREE(ebuf);
9231 } else if(!strcmp(enc, "HEX")){
9232 char *ebuf = tchexencode(vbuf, vsiz);
9233 tcxstrcat2(xstr, ebuf);
9234 TCFREE(ebuf);
9235 } else if(!strcmp(enc, "XML")){
9236 char *ebuf = tcxmlescape(vbuf);
9237 tcxstrcat2(xstr, ebuf);
9238 TCFREE(ebuf);
9239 } else if(!strcmp(enc, "CSTR")){
9240 char *ebuf = tccstrescape(vbuf);
9241 tcxstrcat2(xstr, ebuf);
9242 TCFREE(ebuf);
9243 } else if(!strcmp(enc, "JSON")){
9244 char *ebuf = tcjsonescape(vbuf);
9245 tcxstrcat2(xstr, ebuf);
9246 TCFREE(ebuf);
9247 } else if(!strcmp(enc, "MD5")){
9248 char ebuf[48];
9249 tcmd5hash(vbuf, vsiz, ebuf);
9250 tcxstrcat2(xstr, ebuf);
9251 } else {
9252 tcxstrcat2(xstr, vbuf);
9253 }
9254 }
9255 }
9256 }
9257 tclistdel(tokens);
9258 return cur;
9259 }
9260
9261
9262 /* Evaluate a variable of a template expression.
9263 `stack' specifies the variable scope stack.
9264 `depth' specifies the current depth of the stack.
9265 `name' specifies the variable name.
9266 `sp' specifies the length of the region of the return value.
9267 `np' specifies the number information of the return value.
9268 The return value is the pointer to the region of the evaluated value. */
tctmpldumpevalvar(const TCMAP ** stack,int depth,const char * name,int * sp,int * np)9269 static const char *tctmpldumpevalvar(const TCMAP **stack, int depth, const char *name,
9270 int *sp, int *np){
9271 assert(stack && depth >= 0 && name && sp && np);
9272 const char *result = NULL;
9273 TCLIST *tokens = tcstrsplit(name, ".");
9274 int tnum = TCLISTNUM(tokens);
9275 if(tnum > 0){
9276 const char *token;
9277 int tsiz;
9278 TCLISTVAL(token, tokens, 0, tsiz);
9279 for(int i = depth - 1; i >= 0; i--){
9280 const TCMAP *vars = stack[i];
9281 int vsiz;
9282 const char *vbuf = tcmapget(vars, token, tsiz, &vsiz);
9283 int tidx = 1;
9284 if(vbuf){
9285 while(vbuf){
9286 if(vsiz == sizeof(TCTYPRFXLIST) - 1 + sizeof(TCLIST *) &&
9287 !memcmp(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1)){
9288 result = vbuf;
9289 *sp = vsiz;
9290 TCLIST *list;
9291 memcpy(&list, vbuf + sizeof(TCTYPRFXLIST) - 1, sizeof(list));
9292 *np = tclistnum(list);
9293 break;
9294 } else if(vsiz == sizeof(TCTYPRFXMAP) - 1 + sizeof(TCMAP *) &&
9295 !memcmp(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1)){
9296 if(tidx < tnum){
9297 memcpy(&vars, vbuf + sizeof(TCTYPRFXMAP) - 1, sizeof(TCMAP *));
9298 TCLISTVAL(token, tokens, tidx, tsiz);
9299 vbuf = tcmapget(vars, token, tsiz, &vsiz);
9300 tidx++;
9301 } else {
9302 result = vbuf;
9303 *sp = vsiz;
9304 TCMAP *map;
9305 memcpy(&map, vbuf + sizeof(TCTYPRFXMAP) - 1, sizeof(map));
9306 *np = tcmaprnum(map);
9307 break;
9308 }
9309 } else {
9310 result = vbuf;
9311 *sp = vsiz;
9312 *np = -1;
9313 break;
9314 }
9315 }
9316 break;
9317 }
9318 }
9319 }
9320 tclistdel(tokens);
9321 return result;
9322 }
9323
9324
9325
9326 /*************************************************************************************************
9327 * pointer list
9328 *************************************************************************************************/
9329
9330
9331 /* Create a pointer list object. */
tcptrlistnew(void)9332 TCPTRLIST *tcptrlistnew(void){
9333 TCPTRLIST *ptrlist;
9334 TCMALLOC(ptrlist, sizeof(*ptrlist));
9335 ptrlist->anum = TCLISTUNIT;
9336 TCMALLOC(ptrlist->array, sizeof(ptrlist->array[0]) * ptrlist->anum);
9337 ptrlist->start = 0;
9338 ptrlist->num = 0;
9339 return ptrlist;
9340 }
9341
9342
9343 /* Create a pointer list object with expecting the number of elements. */
tcptrlistnew2(int anum)9344 TCPTRLIST *tcptrlistnew2(int anum){
9345 TCPTRLIST *ptrlist;
9346 TCMALLOC(ptrlist, sizeof(*ptrlist));
9347 if(anum < 1) anum = 1;
9348 ptrlist->anum = anum;
9349 TCMALLOC(ptrlist->array, sizeof(ptrlist->array[0]) * ptrlist->anum);
9350 ptrlist->start = 0;
9351 ptrlist->num = 0;
9352 return ptrlist;
9353 }
9354
9355
9356 /* Copy a pointer list object. */
tcptrlistdup(const TCPTRLIST * ptrlist)9357 TCPTRLIST *tcptrlistdup(const TCPTRLIST *ptrlist){
9358 assert(ptrlist);
9359 int num = ptrlist->num;
9360 if(num < 1) return tcptrlistnew();
9361 void **array = ptrlist->array + ptrlist->start;
9362 TCPTRLIST *nptrlist;
9363 TCMALLOC(nptrlist, sizeof(*nptrlist));
9364 void **narray;
9365 TCMALLOC(narray, sizeof(*narray) * num);
9366 memcpy(narray, array, sizeof(*narray) * num);
9367 nptrlist->anum = num;
9368 nptrlist->array = narray;
9369 nptrlist->start = 0;
9370 nptrlist->num = num;
9371 return nptrlist;
9372 }
9373
9374
9375 /* Delete a pointer list object. */
tcptrlistdel(TCPTRLIST * ptrlist)9376 void tcptrlistdel(TCPTRLIST *ptrlist){
9377 assert(ptrlist);
9378 TCFREE(ptrlist->array);
9379 TCFREE(ptrlist);
9380 }
9381
9382
9383 /* Get the number of elements of a pointer list object. */
tcptrlistnum(const TCPTRLIST * ptrlist)9384 int tcptrlistnum(const TCPTRLIST *ptrlist){
9385 assert(ptrlist);
9386 return ptrlist->num;
9387 }
9388
9389
9390 /* Get the pointer to the region of an element of a pointer list object. */
tcptrlistval(const TCPTRLIST * ptrlist,int index)9391 void *tcptrlistval(const TCPTRLIST *ptrlist, int index){
9392 assert(ptrlist && index >= 0);
9393 if(index >= ptrlist->num) return NULL;
9394 return ptrlist->array[ptrlist->start+index];
9395 }
9396
9397
9398 /* Add an element at the end of a pointer list object. */
tcptrlistpush(TCPTRLIST * ptrlist,void * ptr)9399 void tcptrlistpush(TCPTRLIST *ptrlist, void *ptr){
9400 assert(ptrlist && ptr);
9401 int index = ptrlist->start + ptrlist->num;
9402 if(index >= ptrlist->anum){
9403 ptrlist->anum += ptrlist->num + 1;
9404 TCREALLOC(ptrlist->array, ptrlist->array, ptrlist->anum * sizeof(ptrlist->array[0]));
9405 }
9406 ptrlist->array[index] = ptr;
9407 ptrlist->num++;
9408 }
9409
9410
9411 /* Remove an element of the end of a pointer list object. */
tcptrlistpop(TCPTRLIST * ptrlist)9412 void *tcptrlistpop(TCPTRLIST *ptrlist){
9413 assert(ptrlist);
9414 if(ptrlist->num < 1) return NULL;
9415 int index = ptrlist->start + ptrlist->num - 1;
9416 ptrlist->num--;
9417 return ptrlist->array[index];
9418 }
9419
9420
9421 /* Add an element at the top of a pointer list object. */
tcptrlistunshift(TCPTRLIST * ptrlist,void * ptr)9422 void tcptrlistunshift(TCPTRLIST *ptrlist, void *ptr){
9423 assert(ptrlist && ptr);
9424 if(ptrlist->start < 1){
9425 if(ptrlist->start + ptrlist->num >= ptrlist->anum){
9426 ptrlist->anum += ptrlist->num + 1;
9427 TCREALLOC(ptrlist->array, ptrlist->array, ptrlist->anum * sizeof(ptrlist->array[0]));
9428 }
9429 ptrlist->start = ptrlist->anum - ptrlist->num;
9430 memmove(ptrlist->array + ptrlist->start, ptrlist->array,
9431 ptrlist->num * sizeof(ptrlist->array[0]));
9432 }
9433 ptrlist->start--;
9434 ptrlist->array[ptrlist->start] = ptr;
9435 ptrlist->num++;
9436 }
9437
9438
9439 /* Remove an element of the top of a pointer list object. */
tcptrlistshift(TCPTRLIST * ptrlist)9440 void *tcptrlistshift(TCPTRLIST *ptrlist){
9441 assert(ptrlist);
9442 if(ptrlist->num < 1) return NULL;
9443 int index = ptrlist->start;
9444 ptrlist->start++;
9445 ptrlist->num--;
9446 void *rv = ptrlist->array[index];
9447 if((ptrlist->start & 0xff) == 0 && ptrlist->start > (ptrlist->num >> 1)){
9448 memmove(ptrlist->array, ptrlist->array + ptrlist->start,
9449 ptrlist->num * sizeof(ptrlist->array[0]));
9450 ptrlist->start = 0;
9451 }
9452 return rv;
9453 }
9454
9455
9456 /* Add an element at the specified location of a pointer list object. */
tcptrlistinsert(TCPTRLIST * ptrlist,int index,void * ptr)9457 void tcptrlistinsert(TCPTRLIST *ptrlist, int index, void *ptr){
9458 assert(ptrlist && index >= 0 && ptr);
9459 if(index > ptrlist->num) return;
9460 index += ptrlist->start;
9461 if(ptrlist->start + ptrlist->num >= ptrlist->anum){
9462 ptrlist->anum += ptrlist->num + 1;
9463 TCREALLOC(ptrlist->array, ptrlist->array, ptrlist->anum * sizeof(ptrlist->array[0]));
9464 }
9465 memmove(ptrlist->array + index + 1, ptrlist->array + index,
9466 sizeof(ptrlist->array[0]) * (ptrlist->start + ptrlist->num - index));
9467 ptrlist->array[index] = ptr;
9468 ptrlist->num++;
9469 }
9470
9471
9472 /* Remove an element at the specified location of a pointer list object. */
tcptrlistremove(TCPTRLIST * ptrlist,int index)9473 void *tcptrlistremove(TCPTRLIST *ptrlist, int index){
9474 assert(ptrlist && index >= 0);
9475 if(index >= ptrlist->num) return NULL;
9476 index += ptrlist->start;
9477 void *rv = ptrlist->array[index];
9478 ptrlist->num--;
9479 memmove(ptrlist->array + index, ptrlist->array + index + 1,
9480 sizeof(ptrlist->array[0]) * (ptrlist->start + ptrlist->num - index));
9481 return rv;
9482 }
9483
9484
9485 /* Overwrite an element at the specified location of a pointer list object. */
tcptrlistover(TCPTRLIST * ptrlist,int index,void * ptr)9486 void tcptrlistover(TCPTRLIST *ptrlist, int index, void *ptr){
9487 assert(ptrlist && index >= 0 && ptr);
9488 if(index >= ptrlist->num) return;
9489 index += ptrlist->start;
9490 ptrlist->array[index] = ptr;
9491 }
9492
9493
9494 /* Clear a pointer list object. */
tcptrlistclear(TCPTRLIST * ptrlist)9495 void tcptrlistclear(TCPTRLIST *ptrlist){
9496 assert(ptrlist);
9497 ptrlist->start = 0;
9498 ptrlist->num = 0;
9499 }
9500
9501
9502
9503 /*************************************************************************************************
9504 * features for experts
9505 *************************************************************************************************/
9506
9507
9508 #define TCBSENCUNIT 8192 // unit size of TCBS encoding
9509 #define TCBWTCNTMIN 64 // minimum element number of counting sort
9510 #define TCBWTCNTLV 4 // maximum recursion level of counting sort
9511 #define TCBWTBUFNUM 16384 // number of elements of BWT buffer
9512
9513 typedef struct { // type of structure for a BWT character
9514 int fchr; // character code of the first character
9515 int tchr; // character code of the last character
9516 } TCBWTREC;
9517
9518
9519 /* private function prototypes */
9520 static void tcglobalinit(void);
9521 static void tcglobaldestroy(void);
9522 static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level);
9523 static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip);
9524 static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip);
9525 static void tcbwtsortchrcount(unsigned char *str, int len);
9526 static void tcbwtsortchrinsert(unsigned char *str, int len);
9527 static void tcbwtsortreccount(TCBWTREC *arrays, int anum);
9528 static void tcbwtsortrecinsert(TCBWTREC *array, int anum);
9529 static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr);
9530 static void tcmtfencode(char *ptr, int size);
9531 static void tcmtfdecode(char *ptr, int size);
9532 static int tcgammaencode(const char *ptr, int size, char *obuf);
9533 static int tcgammadecode(const char *ptr, int size, char *obuf);
9534
9535
9536 /* Get the message string corresponding to an error code. */
tcerrmsg(int ecode)9537 const char *tcerrmsg(int ecode){
9538 switch(ecode){
9539 case TCESUCCESS: return "success";
9540 case TCETHREAD: return "threading error";
9541 case TCEINVALID: return "invalid operation";
9542 case TCENOFILE: return "file not found";
9543 case TCENOPERM: return "no permission";
9544 case TCEMETA: return "invalid meta data";
9545 case TCERHEAD: return "invalid record header";
9546 case TCEOPEN: return "open error";
9547 case TCECLOSE: return "close error";
9548 case TCETRUNC: return "trunc error";
9549 case TCESYNC: return "sync error";
9550 case TCESTAT: return "stat error";
9551 case TCESEEK: return "seek error";
9552 case TCEREAD: return "read error";
9553 case TCEWRITE: return "write error";
9554 case TCEMMAP: return "mmap error";
9555 case TCELOCK: return "lock error";
9556 case TCEUNLINK: return "unlink error";
9557 case TCERENAME: return "rename error";
9558 case TCEMKDIR: return "mkdir error";
9559 case TCERMDIR: return "rmdir error";
9560 case TCEKEEP: return "existing record";
9561 case TCENOREC: return "no record found";
9562 case TCEMISC: return "miscellaneous error";
9563 }
9564 return "unknown error";
9565 }
9566
9567
9568 /* Show error message on the standard error output and exit. */
tcmyfatal(const char * message)9569 void *tcmyfatal(const char *message){
9570 assert(message);
9571 if(tcfatalfunc){
9572 tcfatalfunc(message);
9573 } else {
9574 fprintf(stderr, "fatal error: %s\n", message);
9575 }
9576 exit(1);
9577 return NULL;
9578 }
9579
9580
9581 /* Allocate a large nullified region. */
tczeromap(uint64_t size)9582 void *tczeromap(uint64_t size){
9583 #if defined(_SYS_LINUX_)
9584 assert(size > 0);
9585 void *ptr = mmap(0, sizeof(size) + size,
9586 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
9587 if(ptr == MAP_FAILED) tcmyfatal("out of memory");
9588 *(uint64_t *)ptr = size;
9589 return (char *)ptr + sizeof(size);
9590 #else
9591 assert(size > 0);
9592 void *ptr;
9593 TCCALLOC(ptr, 1, size);
9594 return ptr;
9595 #endif
9596 }
9597
9598
9599 /* Free a large nullfied region. */
tczerounmap(void * ptr)9600 void tczerounmap(void *ptr){
9601 #if defined(_SYS_LINUX_)
9602 assert(ptr);
9603 uint64_t size = *((uint64_t *)ptr - 1);
9604 munmap((char *)ptr - sizeof(size), sizeof(size) + size);
9605 #else
9606 assert(ptr);
9607 TCFREE(ptr);
9608 #endif
9609 }
9610
9611
9612 /* Global mutex object. */
9613 static pthread_once_t tcglobalonce = PTHREAD_ONCE_INIT;
9614 static pthread_rwlock_t tcglobalmutex;
9615 static pthread_mutex_t tcpathmutex;
9616 static TCMAP *tcpathmap;
9617
9618
9619 /* Lock the global mutex object. */
tcglobalmutexlock(void)9620 bool tcglobalmutexlock(void){
9621 pthread_once(&tcglobalonce, tcglobalinit);
9622 return pthread_rwlock_wrlock(&tcglobalmutex) == 0;
9623 }
9624
9625
9626 /* Lock the global mutex object by shared locking. */
tcglobalmutexlockshared(void)9627 bool tcglobalmutexlockshared(void){
9628 pthread_once(&tcglobalonce, tcglobalinit);
9629 return pthread_rwlock_rdlock(&tcglobalmutex) == 0;
9630 }
9631
9632
9633 /* Unlock the global mutex object. */
tcglobalmutexunlock(void)9634 bool tcglobalmutexunlock(void){
9635 return pthread_rwlock_unlock(&tcglobalmutex) == 0;
9636 }
9637
9638
9639 /* Lock the absolute path of a file. */
tcpathlock(const char * path)9640 bool tcpathlock(const char *path){
9641 assert(path);
9642 pthread_once(&tcglobalonce, tcglobalinit);
9643 if(pthread_mutex_lock(&tcpathmutex) != 0) return false;
9644 bool err = false;
9645 if(tcpathmap && !tcmapputkeep2(tcpathmap, path, "")) err = true;
9646 if(pthread_mutex_unlock(&tcpathmutex) != 0) err = true;
9647 return !err;
9648 }
9649
9650
9651 /* Unock the absolute path of a file. */
tcpathunlock(const char * path)9652 bool tcpathunlock(const char *path){
9653 assert(path);
9654 pthread_once(&tcglobalonce, tcglobalinit);
9655 if(pthread_mutex_lock(&tcpathmutex) != 0) return false;
9656 bool err = false;
9657 if(tcpathmap && !tcmapout2(tcpathmap, path)) err = true;
9658 if(pthread_mutex_unlock(&tcpathmutex) != 0) err = true;
9659 return !err;
9660 }
9661
9662
9663 /* Convert an integer to the string as binary numbers. */
tcnumtostrbin(uint64_t num,char * buf,int col,int fc)9664 int tcnumtostrbin(uint64_t num, char *buf, int col, int fc){
9665 assert(buf);
9666 char *wp = buf;
9667 int len = sizeof(num) * 8;
9668 bool zero = true;
9669 while(len-- > 0){
9670 if(num & (1ULL << 63)){
9671 *(wp++) = '1';
9672 zero = false;
9673 } else if(!zero){
9674 *(wp++) = '0';
9675 }
9676 num <<= 1;
9677 }
9678 if(col > 0){
9679 if(col > sizeof(num) * 8) col = sizeof(num) * 8;
9680 len = col - (wp - buf);
9681 if(len > 0){
9682 memmove(buf + len, buf, wp - buf);
9683 for(int i = 0; i < len; i++){
9684 buf[i] = fc;
9685 }
9686 wp += len;
9687 }
9688 } else if(zero){
9689 *(wp++) = '0';
9690 }
9691 *wp = '\0';
9692 return wp - buf;
9693 }
9694
9695
9696 /* Compare keys of two records by lexical order. */
tccmplexical(const char * aptr,int asiz,const char * bptr,int bsiz,void * op)9697 int tccmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
9698 assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
9699 int rv;
9700 TCCMPLEXICAL(rv, aptr, asiz, bptr, bsiz);
9701 return rv;
9702 }
9703
9704
9705 /* Compare two keys as decimal strings of real numbers. */
tccmpdecimal(const char * aptr,int asiz,const char * bptr,int bsiz,void * op)9706 int tccmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
9707 assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
9708 const unsigned char *arp = (unsigned char *)aptr;
9709 int alen = asiz;
9710 while(alen > 0 && (*arp <= ' ' || *arp == 0x7f)){
9711 arp++;
9712 alen--;
9713 }
9714 int64_t anum = 0;
9715 int asign = 1;
9716 if(alen > 0 && *arp == '-'){
9717 arp++;
9718 alen--;
9719 asign = -1;
9720 }
9721 while(alen > 0){
9722 int c = *arp;
9723 if(c < '0' || c > '9') break;
9724 anum = anum * 10 + c - '0';
9725 arp++;
9726 alen--;
9727 }
9728 anum *= asign;
9729 const unsigned char *brp = (unsigned char *)bptr;
9730 int blen = bsiz;
9731 while(blen > 0 && (*brp <= ' ' || *brp == 0x7f)){
9732 brp++;
9733 blen--;
9734 }
9735 int64_t bnum = 0;
9736 int bsign = 1;
9737 if(blen > 0 && *brp == '-'){
9738 brp++;
9739 blen--;
9740 bsign = -1;
9741 }
9742 while(blen > 0){
9743 int c = *brp;
9744 if(c < '0' || c > '9') break;
9745 bnum = bnum * 10 + c - '0';
9746 brp++;
9747 blen--;
9748 }
9749 bnum *= bsign;
9750 if(anum < bnum) return -1;
9751 if(anum > bnum) return 1;
9752 if((alen > 1 && *arp == '.') || (blen > 1 && *brp == '.')){
9753 long double aflt = 0;
9754 if(alen > 1 && *arp == '.'){
9755 arp++;
9756 alen--;
9757 if(alen > TCLDBLCOLMAX) alen = TCLDBLCOLMAX;
9758 long double base = 10;
9759 while(alen > 0){
9760 if(*arp < '0' || *arp > '9') break;
9761 aflt += (*arp - '0') / base;
9762 arp++;
9763 alen--;
9764 base *= 10;
9765 }
9766 aflt *= asign;
9767 }
9768 long double bflt = 0;
9769 if(blen > 1 && *brp == '.'){
9770 brp++;
9771 blen--;
9772 if(blen > TCLDBLCOLMAX) blen = TCLDBLCOLMAX;
9773 long double base = 10;
9774 while(blen > 0){
9775 if(*brp < '0' || *brp > '9') break;
9776 bflt += (*brp - '0') / base;
9777 brp++;
9778 blen--;
9779 base *= 10;
9780 }
9781 bflt *= bsign;
9782 }
9783 if(aflt < bflt) return -1;
9784 if(aflt > bflt) return 1;
9785 }
9786 int rv;
9787 TCCMPLEXICAL(rv, aptr, asiz, bptr, bsiz);
9788 return rv;
9789 }
9790
9791
9792 /* Compare two keys as 32-bit integers in the native byte order. */
tccmpint32(const char * aptr,int asiz,const char * bptr,int bsiz,void * op)9793 int tccmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
9794 assert(aptr && bptr);
9795 int32_t anum, bnum;
9796 if(asiz == sizeof(int32_t)){
9797 memcpy(&anum, aptr, sizeof(int32_t));
9798 } else if(asiz < sizeof(int32_t)){
9799 memset(&anum, 0, sizeof(int32_t));
9800 memcpy(&anum, aptr, asiz);
9801 } else {
9802 memcpy(&anum, aptr, sizeof(int32_t));
9803 }
9804 if(bsiz == sizeof(int32_t)){
9805 memcpy(&bnum, bptr, sizeof(int32_t));
9806 } else if(bsiz < sizeof(int32_t)){
9807 memset(&bnum, 0, sizeof(int32_t));
9808 memcpy(&bnum, bptr, bsiz);
9809 } else {
9810 memcpy(&bnum, bptr, sizeof(int32_t));
9811 }
9812 return (anum < bnum) ? -1 : anum > bnum;
9813 }
9814
9815
9816 /* Compare two keys as 64-bit integers in the native byte order. */
tccmpint64(const char * aptr,int asiz,const char * bptr,int bsiz,void * op)9817 int tccmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
9818 assert(aptr && bptr);
9819 int64_t anum, bnum;
9820 if(asiz == sizeof(int64_t)){
9821 memcpy(&anum, aptr, sizeof(int64_t));
9822 } else if(asiz < sizeof(int64_t)){
9823 memset(&anum, 0, sizeof(int64_t));
9824 memcpy(&anum, aptr, asiz);
9825 } else {
9826 memcpy(&anum, aptr, sizeof(int64_t));
9827 }
9828 if(bsiz == sizeof(int64_t)){
9829 memcpy(&bnum, bptr, sizeof(int64_t));
9830 } else if(bsiz < sizeof(int64_t)){
9831 memset(&bnum, 0, sizeof(int64_t));
9832 memcpy(&bnum, bptr, bsiz);
9833 } else {
9834 memcpy(&bnum, bptr, sizeof(int64_t));
9835 }
9836 return (anum < bnum) ? -1 : anum > bnum;
9837 }
9838
9839
9840 /* Compress a serial object with TCBS encoding. */
tcbsencode(const char * ptr,int size,int * sp)9841 char *tcbsencode(const char *ptr, int size, int *sp){
9842 assert(ptr && size >= 0 && sp);
9843 char *result;
9844 TCMALLOC(result, (size * 7) / 3 + (size / TCBSENCUNIT + 1) * sizeof(uint16_t) +
9845 TCBSENCUNIT * 2 + 0x200);
9846 char *pv = result + size + 0x100;
9847 char *wp = pv;
9848 char *tp = pv + size + 0x100;
9849 const char *end = ptr + size;
9850 while(ptr < end){
9851 int usiz = tclmin(TCBSENCUNIT, end - ptr);
9852 memcpy(tp, ptr, usiz);
9853 memcpy(tp + usiz, ptr, usiz);
9854 char *sp = wp;
9855 uint16_t idx = 0;
9856 wp += sizeof(idx);
9857 const char *arrays[usiz+1];
9858 for(int i = 0; i < usiz; i++){
9859 arrays[i] = tp + i;
9860 }
9861 const char *fp = arrays[0];
9862 if(usiz >= TCBWTCNTMIN){
9863 tcbwtsortstrcount(arrays, usiz, usiz, 0);
9864 } else if(usiz > 1){
9865 tcbwtsortstrinsert(arrays, usiz, usiz, 0);
9866 }
9867 for(int i = 0; i < usiz; i++){
9868 int tidx = arrays[i] - fp;
9869 if(tidx == 0){
9870 idx = i;
9871 *(wp++) = ptr[usiz-1];
9872 } else {
9873 *(wp++) = ptr[tidx-1];
9874 }
9875 }
9876 idx = TCHTOIS(idx);
9877 memcpy(sp, &idx, sizeof(idx));
9878 ptr += TCBSENCUNIT;
9879 }
9880 size = wp - pv;
9881 tcmtfencode(pv, size);
9882 int nsiz = tcgammaencode(pv, size, result);
9883 *sp = nsiz;
9884 return result;
9885 }
9886
9887
9888 /* Decompress a serial object compressed with TCBS encoding. */
tcbsdecode(const char * ptr,int size,int * sp)9889 char *tcbsdecode(const char *ptr, int size, int *sp){
9890 char *result;
9891 TCMALLOC(result, size * 9 + 0x200);
9892 char *wp = result + size + 0x100;
9893 int nsiz = tcgammadecode(ptr, size, wp);
9894 tcmtfdecode(wp, nsiz);
9895 ptr = wp;
9896 wp = result;
9897 const char *end = ptr + nsiz;
9898 while(ptr < end){
9899 uint16_t idx;
9900 memcpy(&idx, ptr, sizeof(idx));
9901 idx = TCITOHS(idx);
9902 ptr += sizeof(idx);
9903 int usiz = tclmin(TCBSENCUNIT, end - ptr);
9904 if(idx >= usiz) idx = 0;
9905 char rbuf[usiz+1];
9906 memcpy(rbuf, ptr, usiz);
9907 if(usiz >= TCBWTCNTMIN){
9908 tcbwtsortchrcount((unsigned char *)rbuf, usiz);
9909 } else if(usiz > 0){
9910 tcbwtsortchrinsert((unsigned char *)rbuf, usiz);
9911 }
9912 int fnums[0x100], tnums[0x100];
9913 memset(fnums, 0, sizeof(fnums));
9914 memset(tnums, 0, sizeof(tnums));
9915 TCBWTREC array[usiz+1];
9916 TCBWTREC *rp = array;
9917 for(int i = 0; i < usiz; i++){
9918 int fc = *(unsigned char *)(rbuf + i);
9919 rp->fchr = (fc << 23) + fnums[fc]++;
9920 int tc = *(unsigned char *)(ptr + i);
9921 rp->tchr = (tc << 23) + tnums[tc]++;
9922 rp++;
9923 }
9924 unsigned int fchr = array[idx].fchr;
9925 if(usiz >= TCBWTCNTMIN){
9926 tcbwtsortreccount(array, usiz);
9927 } else if(usiz > 1){
9928 tcbwtsortrecinsert(array, usiz);
9929 }
9930 for(int i = 0; i < usiz; i++){
9931 if(array[i].fchr == fchr){
9932 idx = i;
9933 break;
9934 }
9935 }
9936 for(int i = 0; i < usiz; i++){
9937 *(wp++) = array[idx].fchr >> 23;
9938 idx = tcbwtsearchrec(array, usiz, array[idx].fchr);
9939 }
9940 ptr += usiz;
9941 }
9942 *wp = '\0';
9943 *sp = wp - result;
9944 return result;
9945 }
9946
9947
9948 /* Encode a serial object with BWT encoding. */
tcbwtencode(const char * ptr,int size,int * idxp)9949 char *tcbwtencode(const char *ptr, int size, int *idxp){
9950 assert(ptr && size >= 0 && idxp);
9951 if(size < 1){
9952 *idxp = 0;
9953 char *rv;
9954 TCMEMDUP(rv, "", 0);
9955 return rv;
9956 }
9957 char *result;
9958 TCMALLOC(result, size * 3 + 1);
9959 char *tp = result + size + 1;
9960 memcpy(tp, ptr, size);
9961 memcpy(tp + size, ptr, size);
9962 const char *abuf[TCBWTBUFNUM];
9963 const char **arrays = abuf;
9964 if(size > TCBWTBUFNUM) TCMALLOC(arrays, sizeof(*arrays) * size);
9965 for(int i = 0; i < size; i++){
9966 arrays[i] = tp + i;
9967 }
9968 const char *fp = arrays[0];
9969 if(size >= TCBWTCNTMIN){
9970 tcbwtsortstrcount(arrays, size, size, -1);
9971 } else if(size > 1){
9972 tcbwtsortstrinsert(arrays, size, size, 0);
9973 }
9974 for(int i = 0; i < size; i++){
9975 int idx = arrays[i] - fp;
9976 if(idx == 0){
9977 *idxp = i;
9978 result[i] = ptr[size-1];
9979 } else {
9980 result[i] = ptr[idx-1];
9981 }
9982 }
9983 if(arrays != abuf) TCFREE(arrays);
9984 result[size] = '\0';
9985 return result;
9986 }
9987
9988
9989 /* Decode a serial object encoded with BWT encoding. */
tcbwtdecode(const char * ptr,int size,int idx)9990 char *tcbwtdecode(const char *ptr, int size, int idx){
9991 assert(ptr && size >= 0);
9992 if(size < 1 || idx < 0){
9993 char *rv;
9994 TCMEMDUP(rv, "", 0);
9995 return rv;
9996 }
9997 if(idx >= size) idx = 0;
9998 char *result;
9999 TCMALLOC(result, size + 1);
10000 memcpy(result, ptr, size);
10001 if(size >= TCBWTCNTMIN){
10002 tcbwtsortchrcount((unsigned char *)result, size);
10003 } else {
10004 tcbwtsortchrinsert((unsigned char *)result, size);
10005 }
10006 int fnums[0x100], tnums[0x100];
10007 memset(fnums, 0, sizeof(fnums));
10008 memset(tnums, 0, sizeof(tnums));
10009 TCBWTREC abuf[TCBWTBUFNUM];
10010 TCBWTREC *array = abuf;
10011 if(size > TCBWTBUFNUM) TCMALLOC(array, sizeof(*array) * size);
10012 TCBWTREC *rp = array;
10013 for(int i = 0; i < size; i++){
10014 int fc = *(unsigned char *)(result + i);
10015 rp->fchr = (fc << 23) + fnums[fc]++;
10016 int tc = *(unsigned char *)(ptr + i);
10017 rp->tchr = (tc << 23) + tnums[tc]++;
10018 rp++;
10019 }
10020 unsigned int fchr = array[idx].fchr;
10021 if(size >= TCBWTCNTMIN){
10022 tcbwtsortreccount(array, size);
10023 } else if(size > 1){
10024 tcbwtsortrecinsert(array, size);
10025 }
10026 for(int i = 0; i < size; i++){
10027 if(array[i].fchr == fchr){
10028 idx = i;
10029 break;
10030 }
10031 }
10032 char *wp = result;
10033 for(int i = 0; i < size; i++){
10034 *(wp++) = array[idx].fchr >> 23;
10035 idx = tcbwtsearchrec(array, size, array[idx].fchr);
10036 }
10037 *wp = '\0';
10038 if(array != abuf) TCFREE(array);
10039 return result;
10040 }
10041
10042
10043 /* Get the binary logarithm of an integer. */
tclog2l(long num)10044 long tclog2l(long num){
10045 if(num <= 1) return 0;
10046 num >>= 1;
10047 long rv = 0;
10048 while(num > 0){
10049 rv++;
10050 num >>= 1;
10051 }
10052 return rv;
10053 }
10054
10055
10056 /* Get the binary logarithm of a real number. */
tclog2d(double num)10057 double tclog2d(double num){
10058 return log(num) / log(2);
10059 }
10060
10061
10062 /* Get the aligned offset of a file offset. */
tcpagealign(uint64_t off)10063 uint64_t tcpagealign(uint64_t off){
10064 int ps = sysconf(_SC_PAGESIZE);
10065 int diff = off & (ps - 1);
10066 return (diff > 0) ? off + ps - diff : off;
10067 }
10068
10069
10070 /* Initialize the global mutex object */
tcglobalinit(void)10071 static void tcglobalinit(void){
10072 if(!TCUSEPTHREAD){
10073 memset(&tcglobalmutex, 0, sizeof(tcglobalmutex));
10074 memset(&tcpathmutex, 0, sizeof(tcpathmutex));
10075 }
10076 if(pthread_rwlock_init(&tcglobalmutex, NULL) != 0) tcmyfatal("rwlock error");
10077 if(pthread_mutex_init(&tcpathmutex, NULL) != 0) tcmyfatal("mutex error");
10078 tcpathmap = tcmapnew2(TCMAPTINYBNUM);
10079 atexit(tcglobaldestroy);
10080 }
10081
10082
10083 /* Destroy the global mutex object */
tcglobaldestroy(void)10084 static void tcglobaldestroy(void){
10085 tcmapdel(tcpathmap);
10086 pthread_mutex_destroy(&tcpathmutex);
10087 pthread_rwlock_destroy(&tcglobalmutex);
10088 }
10089
10090
10091 /* Sort BWT string arrays by dicrionary order by counting sort.
10092 `array' specifies an array of string arrays.
10093 `anum' specifies the number of the array.
10094 `len' specifies the size of each string.
10095 `level' specifies the level of recursion. */
tcbwtsortstrcount(const char ** arrays,int anum,int len,int level)10096 static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level){
10097 assert(arrays && anum >= 0 && len >= 0);
10098 const char *nbuf[TCBWTBUFNUM];
10099 const char **narrays = nbuf;
10100 if(anum > TCBWTBUFNUM) TCMALLOC(narrays, sizeof(*narrays) * anum);
10101 int count[0x100], accum[0x100];
10102 memset(count, 0, sizeof(count));
10103 int skip = level < 0 ? 0 : level;
10104 for(int i = 0; i < anum; i++){
10105 count[((unsigned char *)arrays[i])[skip]]++;
10106 }
10107 memcpy(accum, count, sizeof(count));
10108 for(int i = 1; i < 0x100; i++){
10109 accum[i] = accum[i-1] + accum[i];
10110 }
10111 for(int i = 0; i < anum; i++){
10112 narrays[--accum[((unsigned char *)arrays[i])[skip]]] = arrays[i];
10113 }
10114 int off = 0;
10115 if(level >= 0 && level < TCBWTCNTLV){
10116 for(int i = 0; i < 0x100; i++){
10117 int c = count[i];
10118 if(c > 1){
10119 if(c >= TCBWTCNTMIN){
10120 tcbwtsortstrcount(narrays + off, c, len, level + 1);
10121 } else {
10122 tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
10123 }
10124 }
10125 off += c;
10126 }
10127 } else {
10128 for(int i = 0; i < 0x100; i++){
10129 int c = count[i];
10130 if(c > 1){
10131 if(c >= TCBWTCNTMIN){
10132 tcbwtsortstrheap(narrays + off, c, len, skip + 1);
10133 } else {
10134 tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
10135 }
10136 }
10137 off += c;
10138 }
10139 }
10140 memcpy(arrays, narrays, anum * sizeof(*narrays));
10141 if(narrays != nbuf) TCFREE(narrays);
10142 }
10143
10144
10145 /* Sort BWT string arrays by dicrionary order by insertion sort.
10146 `array' specifies an array of string arrays.
10147 `anum' specifies the number of the array.
10148 `len' specifies the size of each string.
10149 `skip' specifies the number of skipped bytes. */
tcbwtsortstrinsert(const char ** arrays,int anum,int len,int skip)10150 static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip){
10151 assert(arrays && anum >= 0 && len >= 0);
10152 for(int i = 1; i < anum; i++){
10153 int cmp = 0;
10154 const unsigned char *ap = (unsigned char *)arrays[i-1];
10155 const unsigned char *bp = (unsigned char *)arrays[i];
10156 for(int j = skip; j < len; j++){
10157 if(ap[j] != bp[j]){
10158 cmp = ap[j] - bp[j];
10159 break;
10160 }
10161 }
10162 if(cmp > 0){
10163 const char *swap = arrays[i];
10164 int j;
10165 for(j = i; j > 0; j--){
10166 int cmp = 0;
10167 const unsigned char *ap = (unsigned char *)arrays[j-1];
10168 const unsigned char *bp = (unsigned char *)swap;
10169 for(int k = skip; k < len; k++){
10170 if(ap[k] != bp[k]){
10171 cmp = ap[k] - bp[k];
10172 break;
10173 }
10174 }
10175 if(cmp < 0) break;
10176 arrays[j] = arrays[j-1];
10177 }
10178 arrays[j] = swap;
10179 }
10180 }
10181 }
10182
10183
10184 /* Sort BWT string arrays by dicrionary order by heap sort.
10185 `array' specifies an array of string arrays.
10186 `anum' specifies the number of the array.
10187 `len' specifies the size of each string.
10188 `skip' specifies the number of skipped bytes. */
tcbwtsortstrheap(const char ** arrays,int anum,int len,int skip)10189 static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip){
10190 assert(arrays && anum >= 0 && len >= 0);
10191 anum--;
10192 int bottom = (anum >> 1) + 1;
10193 int top = anum;
10194 while(bottom > 0){
10195 bottom--;
10196 int mybot = bottom;
10197 int i = mybot * 2;
10198 while(i <= top){
10199 if(i < top){
10200 int cmp = 0;
10201 const unsigned char *ap = (unsigned char *)arrays[i+1];
10202 const unsigned char *bp = (unsigned char *)arrays[i];
10203 for(int j = skip; j < len; j++){
10204 if(ap[j] != bp[j]){
10205 cmp = ap[j] - bp[j];
10206 break;
10207 }
10208 }
10209 if(cmp > 0) i++;
10210 }
10211 int cmp = 0;
10212 const unsigned char *ap = (unsigned char *)arrays[mybot];
10213 const unsigned char *bp = (unsigned char *)arrays[i];
10214 for(int j = skip; j < len; j++){
10215 if(ap[j] != bp[j]){
10216 cmp = ap[j] - bp[j];
10217 break;
10218 }
10219 }
10220 if(cmp >= 0) break;
10221 const char *swap = arrays[mybot];
10222 arrays[mybot] = arrays[i];
10223 arrays[i] = swap;
10224 mybot = i;
10225 i = mybot * 2;
10226 }
10227 }
10228 while(top > 0){
10229 const char *swap = arrays[0];
10230 arrays[0] = arrays[top];
10231 arrays[top] = swap;
10232 top--;
10233 int mybot = bottom;
10234 int i = mybot * 2;
10235 while(i <= top){
10236 if(i < top){
10237 int cmp = 0;
10238 const unsigned char *ap = (unsigned char *)arrays[i+1];
10239 const unsigned char *bp = (unsigned char *)arrays[i];
10240 for(int j = 0; j < len; j++){
10241 if(ap[j] != bp[j]){
10242 cmp = ap[j] - bp[j];
10243 break;
10244 }
10245 }
10246 if(cmp > 0) i++;
10247 }
10248 int cmp = 0;
10249 const unsigned char *ap = (unsigned char *)arrays[mybot];
10250 const unsigned char *bp = (unsigned char *)arrays[i];
10251 for(int j = 0; j < len; j++){
10252 if(ap[j] != bp[j]){
10253 cmp = ap[j] - bp[j];
10254 break;
10255 }
10256 }
10257 if(cmp >= 0) break;
10258 swap = arrays[mybot];
10259 arrays[mybot] = arrays[i];
10260 arrays[i] = swap;
10261 mybot = i;
10262 i = mybot * 2;
10263 }
10264 }
10265 }
10266
10267
10268 /* Sort BWT characters by code number by counting sort.
10269 `str' specifies a string.
10270 `len' specifies the length of the string. */
tcbwtsortchrcount(unsigned char * str,int len)10271 static void tcbwtsortchrcount(unsigned char *str, int len){
10272 assert(str && len >= 0);
10273 int cnt[0x100];
10274 memset(cnt, 0, sizeof(cnt));
10275 for(int i = 0; i < len; i++){
10276 cnt[str[i]]++;
10277 }
10278 int pos = 0;
10279 for(int i = 0; i < 0x100; i++){
10280 memset(str + pos, i, cnt[i]);
10281 pos += cnt[i];
10282 }
10283 }
10284
10285
10286 /* Sort BWT characters by code number by insertion sort.
10287 `str' specifies a string.
10288 `len' specifies the length of the string. */
tcbwtsortchrinsert(unsigned char * str,int len)10289 static void tcbwtsortchrinsert(unsigned char *str, int len){
10290 assert(str && len >= 0);
10291 for(int i = 1; i < len; i++){
10292 if(str[i-1] - str[i] > 0){
10293 unsigned char swap = str[i];
10294 int j;
10295 for(j = i; j > 0; j--){
10296 if(str[j-1] - swap < 0) break;
10297 str[j] = str[j-1];
10298 }
10299 str[j] = swap;
10300 }
10301 }
10302 }
10303
10304
10305 /* Sort BWT records by code number by counting sort.
10306 `array' specifies an array of records.
10307 `anum' specifies the number of the array. */
tcbwtsortreccount(TCBWTREC * array,int anum)10308 static void tcbwtsortreccount(TCBWTREC *array, int anum){
10309 assert(array && anum >= 0);
10310 TCBWTREC nbuf[TCBWTBUFNUM];
10311 TCBWTREC *narray = nbuf;
10312 if(anum > TCBWTBUFNUM) TCMALLOC(narray, sizeof(*narray) * anum);
10313 int count[0x100], accum[0x100];
10314 memset(count, 0, sizeof(count));
10315 for(int i = 0; i < anum; i++){
10316 count[array[i].tchr>>23]++;
10317 }
10318 memcpy(accum, count, sizeof(count));
10319 for(int i = 1; i < 0x100; i++){
10320 accum[i] = accum[i-1] + accum[i];
10321 }
10322 for(int i = 0; i < 0x100; i++){
10323 accum[i] -= count[i];
10324 }
10325 for(int i = 0; i < anum; i++){
10326 narray[accum[array[i].tchr>>23]++] = array[i];
10327 }
10328 memcpy(array, narray, anum * sizeof(*narray));
10329 if(narray != nbuf) TCFREE(narray);
10330 }
10331
10332
10333 /* Sort BWT records by code number by insertion sort.
10334 `array' specifies an array of records..
10335 `anum' specifies the number of the array. */
tcbwtsortrecinsert(TCBWTREC * array,int anum)10336 static void tcbwtsortrecinsert(TCBWTREC *array, int anum){
10337 assert(array && anum >= 0);
10338 for(int i = 1; i < anum; i++){
10339 if(array[i-1].tchr - array[i].tchr > 0){
10340 TCBWTREC swap = array[i];
10341 int j;
10342 for(j = i; j > 0; j--){
10343 if(array[j-1].tchr - swap.tchr < 0) break;
10344 array[j] = array[j-1];
10345 }
10346 array[j] = swap;
10347 }
10348 }
10349 }
10350
10351
10352 /* Search the element of BWT records.
10353 `array' specifies an array of records.
10354 `anum' specifies the number of the array.
10355 `tchr' specifies the last code number. */
tcbwtsearchrec(TCBWTREC * array,int anum,int tchr)10356 static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr){
10357 assert(array && anum >= 0);
10358 int bottom = 0;
10359 int top = anum;
10360 int mid;
10361 do {
10362 mid = (bottom + top) >> 1;
10363 if(array[mid].tchr == tchr){
10364 return mid;
10365 } else if(array[mid].tchr < tchr){
10366 bottom = mid + 1;
10367 if(bottom >= anum) break;
10368 } else {
10369 top = mid - 1;
10370 }
10371 } while(bottom <= top);
10372 return -1;
10373 }
10374
10375
10376 /* Initialization table for MTF encoder. */
10377 const unsigned char tcmtftable[] = {
10378 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
10379 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
10380 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
10381 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
10382 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
10383 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
10384 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
10385 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
10386 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
10387 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
10388 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
10389 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
10390 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
10391 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
10392 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
10393 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
10394 };
10395
10396
10397 /* Encode a region with MTF encoding.
10398 `ptr' specifies the pointer to the region.
10399 `size' specifies the size of the region. */
tcmtfencode(char * ptr,int size)10400 static void tcmtfencode(char *ptr, int size){
10401 unsigned char table1[0x100], table2[0x100], *table, *another;
10402 assert(ptr && size >= 0);
10403 memcpy(table1, tcmtftable, sizeof(tcmtftable));
10404 table = table1;
10405 another = table2;
10406 const char *end = ptr + size;
10407 char *wp = ptr;
10408 while(ptr < end){
10409 unsigned char c = *ptr;
10410 unsigned char *tp = table;
10411 unsigned char *tend = table + 0x100;
10412 while(tp < tend && *tp != c){
10413 tp++;
10414 }
10415 int idx = tp - table;
10416 *(wp++) = idx;
10417 if(idx > 0){
10418 memcpy(another, &c, 1);
10419 memcpy(another + 1, table, idx);
10420 memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
10421 unsigned char *swap = table;
10422 table = another;
10423 another = swap;
10424 }
10425 ptr++;
10426 }
10427 }
10428
10429
10430 /* Decode a region compressed with MTF encoding.
10431 `ptr' specifies the pointer to the region.
10432 `size' specifies the size of the region. */
tcmtfdecode(char * ptr,int size)10433 static void tcmtfdecode(char *ptr, int size){
10434 assert(ptr && size >= 0);
10435 unsigned char table1[0x100], table2[0x100], *table, *another;
10436 assert(ptr && size >= 0);
10437 memcpy(table1, tcmtftable, sizeof(tcmtftable));
10438 table = table1;
10439 another = table2;
10440 const char *end = ptr + size;
10441 char *wp = ptr;
10442 while(ptr < end){
10443 int idx = *(unsigned char *)ptr;
10444 unsigned char c = table[idx];
10445 *(wp++) = c;
10446 if(idx > 0){
10447 memcpy(another, &c, 1);
10448 memcpy(another + 1, table, idx);
10449 memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
10450 unsigned char *swap = table;
10451 table = another;
10452 another = swap;
10453 }
10454 ptr++;
10455 }
10456 }
10457
10458
10459 /* Encode a region with Elias gamma encoding.
10460 `ptr' specifies the pointer to the region.
10461 `size' specifies the size of the region.
10462 `obuf' specifies the pointer to the output buffer.
10463 The return value is the size of the output buffer. */
tcgammaencode(const char * ptr,int size,char * obuf)10464 static int tcgammaencode(const char *ptr, int size, char *obuf){
10465 assert(ptr && size >= 0 && obuf);
10466 TCBITSTRM strm;
10467 TCBITSTRMINITW(strm, obuf);
10468 const char *end = ptr + size;
10469 while(ptr < end){
10470 unsigned int c = *(unsigned char *)ptr;
10471 if(!c){
10472 TCBITSTRMCAT(strm, 1);
10473 } else {
10474 c++;
10475 int plen = 8;
10476 while(plen > 0 && !(c & (1 << plen))){
10477 plen--;
10478 }
10479 int jlen = plen;
10480 while(jlen-- > 0){
10481 TCBITSTRMCAT(strm, 0);
10482 }
10483 while(plen >= 0){
10484 int sign = (c & (1 << plen)) > 0;
10485 TCBITSTRMCAT(strm, sign);
10486 plen--;
10487 }
10488 }
10489 ptr++;
10490 }
10491 TCBITSTRMSETEND(strm);
10492 return TCBITSTRMSIZE(strm);
10493 }
10494
10495
10496 /* Decode a region compressed with Elias gamma encoding.
10497 `ptr' specifies the pointer to the region.
10498 `size' specifies the size of the region.
10499 `obuf' specifies the pointer to the output buffer.
10500 The return value is the size of the output buffer. */
tcgammadecode(const char * ptr,int size,char * obuf)10501 static int tcgammadecode(const char *ptr, int size, char *obuf){
10502 assert(ptr && size >= 0 && obuf);
10503 char *wp = obuf;
10504 TCBITSTRM strm;
10505 TCBITSTRMINITR(strm, ptr, size);
10506 int bnum = TCBITSTRMNUM(strm);
10507 while(bnum > 0){
10508 int sign;
10509 TCBITSTRMREAD(strm, sign);
10510 bnum--;
10511 if(sign){
10512 *(wp++) = 0;
10513 } else {
10514 int plen = 1;
10515 while(bnum > 0){
10516 TCBITSTRMREAD(strm, sign);
10517 bnum--;
10518 if(sign) break;
10519 plen++;
10520 }
10521 unsigned int c = 1;
10522 while(bnum > 0 && plen-- > 0){
10523 TCBITSTRMREAD(strm, sign);
10524 bnum--;
10525 c = (c << 1) + (sign > 0);
10526 }
10527 *(wp++) = c - 1;
10528 }
10529 }
10530 return wp - obuf;
10531 }
10532
10533
10534
10535 // END OF FILE
10536