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, "&amp;", 5); break;
309               case '<': TCXSTRCAT(xstr, "&lt;", 4); break;
310               case '>': TCXSTRCAT(xstr, "&gt;", 4); break;
311               case '"': TCXSTRCAT(xstr, "&quot;", 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, &gts)) return 0;
5670   struct tm lts;
5671   t = 86400;
5672   if(!localtime_r(&t, &lts)) return 0;
5673   return mktime(&lts) - mktime(&gts);
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, "&amp;", 5);
8248         wp += 5;
8249         break;
8250       case '<':
8251         memcpy(wp, "&lt;", 4);
8252         wp += 4;
8253         break;
8254       case '>':
8255         memcpy(wp, "&gt;", 4);
8256         wp += 4;
8257         break;
8258       case '"':
8259         memcpy(wp, "&quot;", 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, "&amp;")){
8282         *(wp++) = '&';
8283         str += 5;
8284       } else if(tcstrfwm(str, "&lt;")){
8285         *(wp++) = '<';
8286         str += 4;
8287       } else if(tcstrfwm(str, "&gt;")){
8288         *(wp++) = '>';
8289         str += 4;
8290       } else if(tcstrfwm(str, "&quot;")){
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, "&amp;", 5);
8509             } else if(str[i] == '<'){
8510               TCXSTRCAT(xstr, "&lt;", 4);
8511             } else if(str[i] == '>'){
8512               TCXSTRCAT(xstr, "&gt;", 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