1 /*************************************************************************************************
2 * The utility API of Tokyo Cabinet
3 * Copyright (C) 2006-2008 Mikio Hirabayashi
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 *************************************************************************************************/
21 /*************************************************************************************************
23 *************************************************************************************************/
26 /* String containing the version information. */
27 const char *tcversion = _TC_VERSION;
30 /* Call back function for handling a fatal error. */
31 void (*tcfatalfunc)(const char *message) = NULL;
34 /* Allocate a region on memory. */
35 void *tcmalloc(size_t size){
36 assert(size > 0 && size < INT_MAX);
37 char *p = malloc(size);
38 if(!p) tcmyfatal("out of memory");
43 /* Allocate a nullified region on memory. */
44 void *tccalloc(size_t nmemb, size_t size){
45 assert(nmemb > 0 && size < INT_MAX && size > 0 && size < INT_MAX);
46 char *p = calloc(nmemb, size);
47 if(!p) tcmyfatal("out of memory");
52 /* Re-allocate a region on memory. */
53 void *tcrealloc(void *ptr, size_t size){
54 assert(size >= 0 && size < INT_MAX);
55 char *p = realloc(ptr, size);
56 if(!p) tcmyfatal("out of memory");
61 /* Duplicate a region on memory. */
62 void *tcmemdup(const void *ptr, size_t size){
63 assert(ptr && size >= 0);
65 TCMALLOC(p, size + 1);
72 /* Duplicate a string on memory. */
73 char *tcstrdup(const void *str){
75 int size = strlen(str);
77 TCMALLOC(p, size + 1);
84 /* Free a region on memory. */
85 void tcfree(void *ptr){
91 /*************************************************************************************************
93 *************************************************************************************************/
96 #define TCXSTRUNIT 12 // allocation unit size of an extensible string
99 /* private function prototypes */
100 static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap);
103 /* Create an extensible string object. */
104 TCXSTR *tcxstrnew(void){
106 TCMALLOC(xstr, sizeof(*xstr));
107 TCMALLOC(xstr->ptr, TCXSTRUNIT);
109 xstr->asize = TCXSTRUNIT;
115 /* Create an extensible string object from a character string. */
116 TCXSTR *tcxstrnew2(const char *str){
119 TCMALLOC(xstr, sizeof(*xstr));
120 int size = strlen(str);
121 int asize = tclmax(size + 1, TCXSTRUNIT);
122 TCMALLOC(xstr->ptr, asize);
125 memcpy(xstr->ptr, str, size + 1);
130 /* Create an extensible string object with the initial allocation size. */
131 TCXSTR *tcxstrnew3(int asiz){
133 asiz = tclmax(asiz, TCXSTRUNIT);
135 TCMALLOC(xstr, sizeof(*xstr));
136 TCMALLOC(xstr->ptr, asiz);
144 /* Copy an extensible string object. */
145 TCXSTR *tcxstrdup(const TCXSTR *xstr){
148 TCMALLOC(nxstr, sizeof(*nxstr));
149 int asize = tclmax(xstr->size + 1, TCXSTRUNIT);
150 TCMALLOC(nxstr->ptr, asize);
151 nxstr->size = xstr->size;
152 nxstr->asize = asize;
153 memcpy(nxstr->ptr, xstr->ptr, xstr->size + 1);
158 /* Delete an extensible string object. */
159 void tcxstrdel(TCXSTR *xstr){
166 /* Concatenate a region to the end of an extensible string object. */
167 void tcxstrcat(TCXSTR *xstr, const void *ptr, int size){
168 assert(xstr && ptr && size >= 0);
169 int nsize = xstr->size + size + 1;
170 if(xstr->asize < nsize){
171 while(xstr->asize < nsize){
173 if(xstr->asize < nsize) xstr->asize = nsize;
175 TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
177 memcpy(xstr->ptr + xstr->size, ptr, size);
179 xstr->ptr[xstr->size] = '\0';
183 /* Concatenate a character string to the end of an extensible string object. */
184 void tcxstrcat2(TCXSTR *xstr, const char *str){
186 int size = strlen(str);
187 int nsize = xstr->size + size + 1;
188 if(xstr->asize < nsize){
189 while(xstr->asize < nsize){
191 if(xstr->asize < nsize) xstr->asize = nsize;
193 TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
195 memcpy(xstr->ptr + xstr->size, str, size + 1);
200 /* Get the pointer of the region of an extensible string object. */
201 const void *tcxstrptr(const TCXSTR *xstr){
207 /* Get the size of the region of an extensible string object. */
208 int tcxstrsize(const TCXSTR *xstr){
214 /* Clear an extensible string object. */
215 void tcxstrclear(TCXSTR *xstr){
222 /* Convert an extensible string object into a usual allocated region. */
223 void *tcxstrtomalloc(TCXSTR *xstr){
232 /* Create an extensible string object from an allocated region. */
233 TCXSTR *tcxstrfrommalloc(void *ptr, int size){
235 TCMALLOC(xstr, sizeof(*xstr));
236 TCREALLOC(xstr->ptr, ptr, size + 1);
237 xstr->ptr[size] = '\0';
244 /* Perform formatted output into an extensible string object. */
245 void tcxstrprintf(TCXSTR *xstr, const char *format, ...){
246 assert(xstr && format);
248 va_start(ap, format);
249 tcvxstrprintf(xstr, format, ap);
254 /* Allocate a formatted string on memory. */
255 char *tcsprintf(const char *format, ...){
257 TCXSTR *xstr = tcxstrnew();
259 va_start(ap, format);
260 tcvxstrprintf(xstr, format, ap);
262 return tcxstrtomalloc(xstr);
266 /* Perform formatted output into an extensible string object. */
267 static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap){
268 assert(xstr && format);
269 while(*format != '\0'){
271 char cbuf[TCNUMBUFSIZ];
276 while(strchr("0123456789 .+-hlLz", *format) && *format != '\0' &&
277 cblen < TCNUMBUFSIZ - 1){
278 if(*format == 'l' || *format == 'L') lnum++;
279 cbuf[cblen++] = *(format++);
281 cbuf[cblen++] = *format;
284 char *tmp, tbuf[TCNUMBUFSIZ*2];
287 tmp = va_arg(ap, char *);
288 if(!tmp) tmp = "(null)";
289 tcxstrcat2(xstr, tmp);
293 tlen = sprintf(tbuf, cbuf, va_arg(ap, long long));
294 } else if(lnum >= 1){
295 tlen = sprintf(tbuf, cbuf, va_arg(ap, long));
297 tlen = sprintf(tbuf, cbuf, va_arg(ap, int));
299 TCXSTRCAT(xstr, tbuf, tlen);
301 case 'o': case 'u': case 'x': case 'X': case 'c':
303 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long long));
304 } else if(lnum >= 1){
305 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long));
307 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int));
309 TCXSTRCAT(xstr, tbuf, tlen);
311 case 'e': case 'E': case 'f': case 'g': case 'G':
313 tlen = sprintf(tbuf, cbuf, va_arg(ap, long double));
315 tlen = sprintf(tbuf, cbuf, va_arg(ap, double));
317 TCXSTRCAT(xstr, tbuf, tlen);
320 tmp = va_arg(ap, char *);
321 if(!tmp) tmp = "(null)";
324 case '&': TCXSTRCAT(xstr, "&", 5); break;
325 case '<': TCXSTRCAT(xstr, "<", 4); break;
326 case '>': TCXSTRCAT(xstr, ">", 4); break;
327 case '"': TCXSTRCAT(xstr, """, 6); break;
329 if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f)))
330 TCXSTRCAT(xstr, tmp, 1);
337 tmp = va_arg(ap, char *);
338 if(!tmp) tmp = "(null)";
340 unsigned char c = *(unsigned char *)tmp;
341 if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
342 (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
343 TCXSTRCAT(xstr, tmp, 1);
345 tlen = sprintf(tbuf, "%%%02X", c);
346 TCXSTRCAT(xstr, tbuf, tlen);
352 TCXSTRCAT(xstr, "%", 1);
356 TCXSTRCAT(xstr, format, 1);
364 /*************************************************************************************************
366 *************************************************************************************************/
369 #define TCLISTUNIT 64 // allocation unit number of a list handle
372 /* private function prototypes */
373 static int tclistelemcmp(const void *a, const void *b);
374 static int tclistelemcmpci(const void *a, const void *b);
377 /* Create a list object. */
378 TCLIST *tclistnew(void){
380 TCMALLOC(list, sizeof(*list));
381 list->anum = TCLISTUNIT;
382 TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
389 /* Create a list object. */
390 TCLIST *tclistnew2(int anum){
392 TCMALLOC(list, sizeof(*list));
393 if(anum < 1) anum = 1;
395 TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
402 /* Copy a list object. */
403 TCLIST *tclistdup(const TCLIST *list){
406 if(num < 1) tclistnew();
407 const TCLISTDATUM *array = list->array + list->start;
409 TCMALLOC(nlist, sizeof(*nlist));
411 TCMALLOC(narray, sizeof(list->array[0]) * tclmax(num, 1));
412 for(int i = 0; i < num; i++){
413 int size = array[i].size;
414 TCMALLOC(narray[i].ptr, tclmax(size + 1, TCXSTRUNIT));
415 memcpy(narray[i].ptr, array[i].ptr, size + 1);
416 narray[i].size = array[i].size;
419 nlist->array = narray;
426 /* Delete a list object. */
427 void tclistdel(TCLIST *list){
429 TCLISTDATUM *array = list->array;
430 int end = list->start + list->num;
431 for(int i = list->start; i < end; i++){
439 /* Get the number of elements of a list object. */
440 int tclistnum(const TCLIST *list){
446 /* Get the pointer to the region of an element of a list object. */
447 const void *tclistval(const TCLIST *list, int index, int *sp){
448 assert(list && index >= 0 && sp);
449 if(index >= list->num) return NULL;
450 index += list->start;
451 *sp = list->array[index].size;
452 return list->array[index].ptr;
456 /* Get the string of an element of a list object. */
457 const char *tclistval2(const TCLIST *list, int index){
458 assert(list && index >= 0);
459 if(index >= list->num) return NULL;
460 index += list->start;
461 return list->array[index].ptr;
465 /* Add an element at the end of a list object. */
466 void tclistpush(TCLIST *list, const void *ptr, int size){
467 assert(list && ptr && size >= 0);
468 int index = list->start + list->num;
469 if(index >= list->anum){
470 list->anum += list->num + 1;
471 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
473 TCLISTDATUM *array = list->array;
474 TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
475 memcpy(array[index].ptr, ptr, size);
476 array[index].ptr[size] = '\0';
477 array[index].size = size;
482 /* Add a string element at the end of a list object. */
483 void tclistpush2(TCLIST *list, const char *str){
485 int index = list->start + list->num;
486 if(index >= list->anum){
487 list->anum += list->num + 1;
488 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
490 int size = strlen(str);
491 TCLISTDATUM *array = list->array;
492 TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
493 memcpy(array[index].ptr, str, size + 1);
494 array[index].size = size;
499 /* Add an allocated element at the end of a list object. */
500 void tclistpushmalloc(TCLIST *list, void *ptr, int size){
501 assert(list && ptr && size >= 0);
502 int index = list->start + list->num;
503 if(index >= list->anum){
504 list->anum += list->num + 1;
505 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
507 TCLISTDATUM *array = list->array;
508 TCREALLOC(array[index].ptr, ptr, size + 1);
509 array[index].ptr[size] = '\0';
510 array[index].size = size;
515 /* Remove an element of the end of a list object. */
516 void *tclistpop(TCLIST *list, int *sp){
518 if(list->num < 1) return NULL;
519 int index = list->start + list->num - 1;
521 *sp = list->array[index].size;
522 return list->array[index].ptr;
526 /* Remove a string element of the end of a list object. */
527 char *tclistpop2(TCLIST *list){
529 if(list->num < 1) return NULL;
530 int index = list->start + list->num - 1;
532 return list->array[index].ptr;
536 /* Add an element at the top of a list object. */
537 void tclistunshift(TCLIST *list, const void *ptr, int size){
538 assert(list && ptr && size >= 0);
540 if(list->start + list->num >= list->anum){
541 list->anum += list->num + 1;
542 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
544 list->start = list->anum - list->num;
545 memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
547 int index = list->start - 1;
548 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
549 memcpy(list->array[index].ptr, ptr, size);
550 list->array[index].ptr[size] = '\0';
551 list->array[index].size = size;
557 /* Add a string element at the top of a list object. */
558 void tclistunshift2(TCLIST *list, const char *str){
561 if(list->start + list->num >= list->anum){
562 list->anum += list->num + 1;
563 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
565 list->start = list->anum - list->num;
566 memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
568 int index = list->start - 1;
569 int size = strlen(str);
570 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
571 memcpy(list->array[index].ptr, str, size + 1);
572 list->array[index].size = size;
578 /* Remove an element of the top of a list object. */
579 void *tclistshift(TCLIST *list, int *sp){
581 if(list->num < 1) return NULL;
582 int index = list->start;
585 *sp = list->array[index].size;
586 void *rv = list->array[index].ptr;
587 if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
588 memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
595 /* Remove a string element of the top of a list object. */
596 char *tclistshift2(TCLIST *list){
598 if(list->num < 1) return NULL;
599 int index = list->start;
602 void *rv = list->array[index].ptr;
603 if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
604 memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
611 /* Add an element at the specified location of a list object. */
612 void tclistinsert(TCLIST *list, int index, const void *ptr, int size){
613 assert(list && index >= 0 && ptr && size >= 0);
614 if(index > list->num) return;
615 index += list->start;
616 if(list->start + list->num >= list->anum){
617 list->anum += list->num + 1;
618 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
620 memmove(list->array + index + 1, list->array + index,
621 sizeof(list->array[0]) * (list->start + list->num - index));
622 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
623 memcpy(list->array[index].ptr, ptr, size);
624 list->array[index].ptr[size] = '\0';
625 list->array[index].size = size;
630 /* Add a string element at the specified location of a list object. */
631 void tclistinsert2(TCLIST *list, int index, const char *str){
632 assert(list && index >= 0 && str);
633 if(index > list->num) return;
634 index += list->start;
635 if(list->start + list->num >= list->anum){
636 list->anum += list->num + 1;
637 TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
639 memmove(list->array + index + 1, list->array + index,
640 sizeof(list->array[0]) * (list->start + list->num - index));
641 int size = strlen(str);
642 TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
643 memcpy(list->array[index].ptr, str, size);
644 list->array[index].ptr[size] = '\0';
645 list->array[index].size = size;
650 /* Remove an element at the specified location of a list object. */
651 void *tclistremove(TCLIST *list, int index, int *sp){
652 assert(list && index >= 0 && sp);
653 if(index >= list->num) return NULL;
654 index += list->start;
655 char *ptr = list->array[index].ptr;
656 *sp = list->array[index].size;
658 memmove(list->array + index, list->array + index + 1,
659 sizeof(list->array[0]) * (list->start + list->num - index));
664 /* Remove a string element at the specified location of a list object. */
665 char *tclistremove2(TCLIST *list, int index){
666 assert(list && index >= 0);
667 if(index >= list->num) return NULL;
668 index += list->start;
669 char *ptr = list->array[index].ptr;
671 memmove(list->array + index, list->array + index + 1,
672 sizeof(list->array[0]) * (list->start + list->num - index));
677 /* Overwrite an element at the specified location of a list object. */
678 void tclistover(TCLIST *list, int index, const void *ptr, int size){
679 assert(list && index >= 0 && ptr && size >= 0);
680 if(index >= list->num) return;
681 index += list->start;
682 if(size > list->array[index].size)
683 TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
684 memcpy(list->array[index].ptr, ptr, size);
685 list->array[index].size = size;
686 list->array[index].ptr[size] = '\0';
690 /* Overwrite a string element at the specified location of a list object. */
691 void tclistover2(TCLIST *list, int index, const char *str){
692 assert(list && index >= 0 && str);
693 if(index >= list->num) return;
694 index += list->start;
695 int size = strlen(str);
696 if(size > list->array[index].size)
697 TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
698 memcpy(list->array[index].ptr, str, size + 1);
699 list->array[index].size = size;
703 /* Sort elements of a list object in lexical order. */
704 void tclistsort(TCLIST *list){
706 qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmp);
710 /* Sort elements of a list object in case-insensitive lexical order. */
711 void tclistsortci(TCLIST *list){
713 qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmpci);
717 /* Sort elements of a list object by an arbitrary comparison function. */
718 void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *)){
720 qsort(list->array + list->start, list->num, sizeof(list->array[0]),
721 (int (*)(const void *, const void *))cmp);
725 /* Search a list object for an element using liner search. */
726 int tclistlsearch(const TCLIST *list, const void *ptr, int size){
727 assert(list && ptr && size >= 0);
728 int end = list->start + list->num;
729 for(int i = list->start; i < end; i++){
730 if(list->array[i].size == size && !memcmp(list->array[i].ptr, ptr, size))
731 return i - list->start;
737 /* Search a list object for an element using binary search. */
738 int tclistbsearch(const TCLIST *list, const void *ptr, int size){
739 assert(list && ptr && size >= 0);
741 key.ptr = (char *)ptr;
743 TCLISTDATUM *res = bsearch(&key, list->array + list->start,
744 list->num, sizeof(list->array[0]), tclistelemcmp);
745 return res ? res - list->array - list->start : -1;
749 /* Clear a list object. */
750 void tclistclear(TCLIST *list){
752 TCLISTDATUM *array = list->array;
753 int end = list->start + list->num;
754 for(int i = list->start; i < end; i++){
762 /* Serialize a list object into a byte array. */
763 void *tclistdump(const TCLIST *list, int *sp){
765 const TCLISTDATUM *array = list->array;
766 int end = list->start + list->num;
768 for(int i = list->start; i < end; i++){
769 tsiz += array[i].size + sizeof(int);
772 TCMALLOC(buf, tsiz + 1);
774 for(int i = list->start; i < end; i++){
776 TCSETVNUMBUF(step, wp, array[i].size);
778 memcpy(wp, array[i].ptr, array[i].size);
786 /* Create a list object from a serialized byte array. */
787 TCLIST *tclistload(const void *ptr, int size){
788 assert(ptr && size >= 0);
790 TCMALLOC(list, sizeof(*list));
791 int anum = size / sizeof(int) + 1;
793 TCMALLOC(array, sizeof(array[0]) * anum);
795 const char *rp = ptr;
796 const char *ep = (char *)ptr + size;
799 TCREADVNUMBUF(rp, vsiz, step);
803 TCREALLOC(array, array, anum * sizeof(array[0]));
805 TCMALLOC(array[num].ptr, tclmax(vsiz + 1, TCXSTRUNIT));
806 memcpy(array[num].ptr, rp, vsiz);
807 array[num].ptr[vsiz] = '\0';
808 array[num].size = vsiz;
820 /* Compare two list elements in lexical order.
821 `a' specifies the pointer to one element.
822 `b' specifies the pointer to the other element.
823 The return value is positive if the former is big, negative if the latter is big, 0 if both
825 static int tclistelemcmp(const void *a, const void *b){
827 unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr;
828 unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr;
829 int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ?
830 ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size;
831 for(int i = 0; i < size; i++){
832 if(ao[i] > bo[i]) return 1;
833 if(ao[i] < bo[i]) return -1;
835 return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size;
839 /* Compare two list elements in case-insensitive lexical order..
840 `a' specifies the pointer to one element.
841 `b' specifies the pointer to the other element.
842 The return value is positive if the former is big, negative if the latter is big, 0 if both
844 static int tclistelemcmpci(const void *a, const void *b){
846 TCLISTDATUM *ap = (TCLISTDATUM *)a;
847 TCLISTDATUM *bp = (TCLISTDATUM *)b;
848 unsigned char *ao = (unsigned char *)ap->ptr;
849 unsigned char *bo = (unsigned char *)bp->ptr;
850 int size = (ap->size < bp->size) ? ap->size : bp->size;
851 for(int i = 0; i < size; i++){
854 if(ac >= 'A' && ac <= 'Z'){
860 if(bc >= 'A' && bc <= 'Z'){
864 if(ac > bc) return 1;
865 if(ac < bc) return -1;
866 if(!ab && bb) return 1;
867 if(ab && !bb) return -1;
869 return ap->size - bp->size;
874 /*************************************************************************************************
876 *************************************************************************************************/
879 #define TCMAPBNUM 4093 // allocation unit number of a map
880 #define TCMAPCSUNIT 52 // small allocation unit size of map concatenation
881 #define TCMAPCBUNIT 252 // big allocation unit size of map concatenation
883 /* get the first hash value */
884 #define TCMAPHASH1(TC_res, TC_kbuf, TC_ksiz) \
886 const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf); \
887 int _TC_ksiz = TC_ksiz; \
888 for((TC_res) = 19780211; _TC_ksiz--;){ \
889 (TC_res) = ((TC_res) << 5) + ((TC_res) << 2) + (TC_res) + *(_TC_p)++; \
893 /* get the second hash value */
894 #define TCMAPHASH2(TC_res, TC_kbuf, TC_ksiz) \
896 const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
897 int _TC_ksiz = TC_ksiz; \
898 for((TC_res) = 0x13579bdf; _TC_ksiz--;){ \
899 (TC_res) = ((TC_res) << 5) - (TC_res) + *(_TC_p)--; \
903 /* get the size of padding bytes for pointer alignment */
904 #define TCALIGNPAD(TC_hsiz) \
905 (((TC_hsiz | ~-(int)sizeof(void *)) + 1) - TC_hsiz)
907 /* compare two keys */
908 #define TCKEYCMP(TC_abuf, TC_asiz, TC_bbuf, TC_bsiz) \
909 ((TC_asiz > TC_bsiz) ? 1 : (TC_asiz < TC_bsiz) ? -1 : memcmp(TC_abuf, TC_bbuf, TC_asiz))
912 /* Create a map object. */
913 TCMAP *tcmapnew(void){
914 return tcmapnew2(TCMAPBNUM);
918 /* Create a map object with specifying the number of the buckets. */
919 TCMAP *tcmapnew2(uint32_t bnum){
920 if(bnum < 1) bnum = 1;
922 TCMALLOC(map, sizeof(*map));
924 TCCALLOC(buckets, bnum, sizeof(map->buckets[0]));
925 map->buckets = buckets;
936 /* Copy a map object. */
937 TCMAP *tcmapdup(const TCMAP *map){
939 TCMAP *nmap = tcmapnew2(tclmax(tclmax(map->bnum, map->rnum), TCMAPBNUM));
940 TCMAPREC *cur = map->cur;
943 ((TCMAP *)map)->cur = map->first;
944 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
946 const char *vbuf = tcmapiterval(kbuf, &vsiz);
947 tcmapputkeep(nmap, kbuf, ksiz, vbuf, vsiz);
949 ((TCMAP *)map)->cur = cur;
954 /* Close a map object. */
955 void tcmapdel(TCMAP *map){
957 TCMAPREC *rec = map->first;
959 TCMAPREC *next = rec->next;
968 /* Store a record into a map object. */
969 void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
970 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
972 TCMAPHASH1(hash, kbuf, ksiz);
973 int bidx = hash % map->bnum;
974 TCMAPREC *rec = map->buckets[bidx];
975 TCMAPREC **entp = map->buckets + bidx;
976 TCMAPHASH2(hash, kbuf, ksiz);
978 if(hash > rec->hash){
981 } else if(hash < rec->hash){
982 entp = &(rec->right);
985 char *dbuf = (char *)rec + sizeof(*rec);
986 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
991 entp = &(rec->right);
994 map->msiz += vsiz - rec->vsiz;
995 int psiz = TCALIGNPAD(ksiz);
996 if(vsiz > rec->vsiz){
998 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1000 if(map->first == old) map->first = rec;
1001 if(map->last == old) map->last = rec;
1002 if(map->cur == old) map->cur = rec;
1003 if(*entp == old) *entp = rec;
1004 if(rec->prev) rec->prev->next = rec;
1005 if(rec->next) rec->next->prev = rec;
1006 dbuf = (char *)rec + sizeof(*rec);
1009 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1010 dbuf[ksiz+psiz+vsiz] = '\0';
1016 int psiz = TCALIGNPAD(ksiz);
1017 map->msiz += ksiz + vsiz;
1018 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1019 char *dbuf = (char *)rec + sizeof(*rec);
1020 memcpy(dbuf, kbuf, ksiz);
1023 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1024 dbuf[ksiz+psiz+vsiz] = '\0';
1029 rec->prev = map->last;
1032 if(!map->first) map->first = rec;
1033 if(map->last) map->last->next = rec;
1039 /* Store a string record into a map object. */
1040 void tcmapput2(TCMAP *map, const char *kstr, const char *vstr){
1041 assert(map && kstr && vstr);
1042 tcmapput(map, kstr, strlen(kstr), vstr, strlen(vstr));
1046 /* Store a record of the value of two regions into a map object. */
1047 void tcmapput3(TCMAP *map, const char *kbuf, int ksiz,
1048 const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz){
1049 assert(map && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
1051 TCMAPHASH1(hash, kbuf, ksiz);
1052 int bidx = hash % map->bnum;
1053 TCMAPREC *rec = map->buckets[bidx];
1054 TCMAPREC **entp = map->buckets + bidx;
1055 TCMAPHASH2(hash, kbuf, ksiz);
1057 if(hash > rec->hash){
1058 entp = &(rec->left);
1060 } else if(hash < rec->hash){
1061 entp = &(rec->right);
1064 char *dbuf = (char *)rec + sizeof(*rec);
1065 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1067 entp = &(rec->left);
1069 } else if(kcmp > 0){
1070 entp = &(rec->right);
1073 int vsiz = fvsiz + lvsiz;
1074 map->msiz += vsiz - rec->vsiz;
1075 int psiz = TCALIGNPAD(ksiz);
1077 if(vsiz > rec->vsiz){
1078 TCMAPREC *old = rec;
1079 TCREALLOC(rec, rec, sizeof(*rec) + ksiz + vsiz + 1);
1081 if(map->first == old) map->first = rec;
1082 if(map->last == old) map->last = rec;
1083 if(map->cur == old) map->cur = rec;
1084 if(*entp == old) *entp = rec;
1085 if(rec->prev) rec->prev->next = rec;
1086 if(rec->next) rec->next->prev = rec;
1087 dbuf = (char *)rec + sizeof(*rec);
1090 memcpy(dbuf + ksiz, fvbuf, fvsiz);
1091 memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
1092 dbuf[ksiz+vsiz] = '\0';
1098 int vsiz = fvsiz + lvsiz;
1099 int psiz = TCALIGNPAD(ksiz);
1100 map->msiz += ksiz + vsiz;
1101 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1102 char *dbuf = (char *)rec + sizeof(*rec);
1103 memcpy(dbuf, kbuf, ksiz);
1107 memcpy(dbuf + ksiz, fvbuf, fvsiz);
1108 memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
1109 dbuf[ksiz+vsiz] = '\0';
1114 rec->prev = map->last;
1117 if(!map->first) map->first = rec;
1118 if(map->last) map->last->next = rec;
1124 /* Store a new record into a map object. */
1125 bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1126 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1128 TCMAPHASH1(hash, kbuf, ksiz);
1129 int bidx = hash % map->bnum;
1130 TCMAPREC *rec = map->buckets[bidx];
1131 TCMAPREC **entp = map->buckets + bidx;
1132 TCMAPHASH2(hash, kbuf, ksiz);
1134 if(hash > rec->hash){
1135 entp = &(rec->left);
1137 } else if(hash < rec->hash){
1138 entp = &(rec->right);
1141 char *dbuf = (char *)rec + sizeof(*rec);
1142 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1144 entp = &(rec->left);
1146 } else if(kcmp > 0){
1147 entp = &(rec->right);
1154 int psiz = TCALIGNPAD(ksiz);
1155 map->msiz += ksiz + vsiz;
1156 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
1157 char *dbuf = (char *)rec + sizeof(*rec);
1158 memcpy(dbuf, kbuf, ksiz);
1161 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1162 dbuf[ksiz+psiz+vsiz] = '\0';
1167 rec->prev = map->last;
1170 if(!map->first) map->first = rec;
1171 if(map->last) map->last->next = rec;
1178 /* Store a new string record into a map object.
1179 `map' specifies the map object.
1180 `kstr' specifies the string of the key.
1181 `vstr' specifies the string of the value.
1182 If successful, the return value is true, else, it is false.
1183 If a record with the same key exists in the database, this function has no effect. */
1184 bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr){
1185 assert(map && kstr && vstr);
1186 return tcmapputkeep(map, kstr, strlen(kstr), vstr, strlen(vstr));
1190 /* Concatenate a value at the end of the value of the existing record in a map object. */
1191 void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1192 assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1194 TCMAPHASH1(hash, kbuf, ksiz);
1195 int bidx = hash % map->bnum;
1196 TCMAPREC *rec = map->buckets[bidx];
1197 TCMAPREC **entp = map->buckets + bidx;
1198 TCMAPHASH2(hash, kbuf, ksiz);
1200 if(hash > rec->hash){
1201 entp = &(rec->left);
1203 } else if(hash < rec->hash){
1204 entp = &(rec->right);
1207 char *dbuf = (char *)rec + sizeof(*rec);
1208 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1210 entp = &(rec->left);
1212 } else if(kcmp > 0){
1213 entp = &(rec->right);
1217 int psiz = TCALIGNPAD(ksiz);
1218 int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1;
1219 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
1220 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
1221 TCMAPREC *old = rec;
1222 TCREALLOC(rec, rec, asiz);
1224 if(map->first == old) map->first = rec;
1225 if(map->last == old) map->last = rec;
1226 if(map->cur == old) map->cur = rec;
1227 if(*entp == old) *entp = rec;
1228 if(rec->prev) rec->prev->next = rec;
1229 if(rec->next) rec->next->prev = rec;
1230 dbuf = (char *)rec + sizeof(*rec);
1232 memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz);
1233 dbuf[ksiz+psiz+rec->vsiz+vsiz] = '\0';
1239 int psiz = TCALIGNPAD(ksiz);
1240 int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1;
1241 int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
1242 asiz = (asiz - 1) + unit - (asiz - 1) % unit;
1243 map->msiz += ksiz + vsiz;
1244 TCMALLOC(rec, asiz);
1245 char *dbuf = (char *)rec + sizeof(*rec);
1246 memcpy(dbuf, kbuf, ksiz);
1249 memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
1250 dbuf[ksiz+psiz+vsiz] = '\0';
1255 rec->prev = map->last;
1258 if(!map->first) map->first = rec;
1259 if(map->last) map->last->next = rec;
1265 /* Concatenate a string value at the end of the value of the existing record in a map object. */
1266 void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr){
1267 assert(map && kstr && vstr);
1268 tcmapputcat(map, kstr, strlen(kstr), vstr, strlen(vstr));
1272 /* Remove a record of a map object. */
1273 bool tcmapout(TCMAP *map, const void *kbuf, int ksiz){
1274 assert(map && kbuf && ksiz >= 0);
1276 TCMAPHASH1(hash, kbuf, ksiz);
1277 int bidx = hash % map->bnum;
1278 TCMAPREC *rec = map->buckets[bidx];
1279 TCMAPREC **entp = map->buckets + bidx;
1280 TCMAPHASH2(hash, kbuf, ksiz);
1282 if(hash > rec->hash){
1283 entp = &(rec->left);
1285 } else if(hash < rec->hash){
1286 entp = &(rec->right);
1289 char *dbuf = (char *)rec + sizeof(*rec);
1290 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1292 entp = &(rec->left);
1294 } else if(kcmp > 0){
1295 entp = &(rec->right);
1299 map->msiz -= rec->ksiz + rec->vsiz;
1300 if(rec->prev) rec->prev->next = rec->next;
1301 if(rec->next) rec->next->prev = rec->prev;
1302 if(rec == map->first) map->first = rec->next;
1303 if(rec == map->last) map->last = rec->prev;
1304 if(rec == map->cur) map->cur = rec->next;
1305 if(rec->left && !rec->right){
1307 } else if(!rec->left && rec->right){
1309 } else if(!rec->left && !rec->left){
1313 TCMAPREC *tmp = *entp;
1317 tmp->right = rec->right;
1328 /* Remove a string record of a map object. */
1329 bool tcmapout2(TCMAP *map, const char *kstr){
1330 assert(map && kstr);
1331 return tcmapout(map, kstr, strlen(kstr));
1335 /* Retrieve a record in a map object. */
1336 const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp){
1337 assert(map && kbuf && ksiz >= 0 && sp);
1339 TCMAPHASH1(hash, kbuf, ksiz);
1340 TCMAPREC *rec = map->buckets[hash%map->bnum];
1341 TCMAPHASH2(hash, kbuf, ksiz);
1343 if(hash > rec->hash){
1345 } else if(hash < rec->hash){
1348 char *dbuf = (char *)rec + sizeof(*rec);
1349 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1352 } else if(kcmp > 0){
1356 return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
1364 /* Retrieve a string record in a map object. */
1365 const char *tcmapget2(const TCMAP *map, const char *kstr){
1366 assert(map && kstr);
1367 int ksiz = strlen(kstr);
1369 TCMAPHASH1(hash, kstr, ksiz);
1370 TCMAPREC *rec = map->buckets[hash%map->bnum];
1371 TCMAPHASH2(hash, kstr, ksiz);
1373 if(hash > rec->hash){
1375 } else if(hash < rec->hash){
1378 char *dbuf = (char *)rec + sizeof(*rec);
1379 int kcmp = TCKEYCMP(kstr, ksiz, dbuf, rec->ksiz);
1382 } else if(kcmp > 0){
1385 return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
1393 /* Retrieve a semivolatile record in a map object. */
1394 const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp){
1395 assert(map && kbuf && ksiz >= 0 && sp);
1397 TCMAPHASH1(hash, kbuf, ksiz);
1398 TCMAPREC *rec = map->buckets[hash%map->bnum];
1399 TCMAPHASH2(hash, kbuf, ksiz);
1401 if(hash > rec->hash){
1403 } else if(hash < rec->hash){
1406 char *dbuf = (char *)rec + sizeof(*rec);
1407 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1410 } else if(kcmp > 0){
1413 if(map->last != rec){
1414 if(map->first == rec) map->first = rec->next;
1415 if(rec->prev) rec->prev->next = rec->next;
1416 if(rec->next) rec->next->prev = rec->prev;
1417 rec->prev = map->last;
1419 map->last->next = rec;
1423 return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
1431 /* Move a record to the edge of a map object. */
1432 bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head){
1433 assert(map && kbuf && ksiz >= 0);
1435 TCMAPHASH1(hash, kbuf, ksiz);
1436 TCMAPREC *rec = map->buckets[hash%map->bnum];
1437 TCMAPHASH2(hash, kbuf, ksiz);
1439 if(hash > rec->hash){
1441 } else if(hash < rec->hash){
1444 char *dbuf = (char *)rec + sizeof(*rec);
1445 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1448 } else if(kcmp > 0){
1452 if(map->first == rec) return true;
1453 if(map->last == rec) map->last = rec->prev;
1454 if(rec->prev) rec->prev->next = rec->next;
1455 if(rec->next) rec->next->prev = rec->prev;
1457 rec->next = map->first;
1458 map->first->prev = rec;
1461 if(map->last == rec) return true;
1462 if(map->first == rec) map->first = rec->next;
1463 if(rec->prev) rec->prev->next = rec->next;
1464 if(rec->next) rec->next->prev = rec->prev;
1465 rec->prev = map->last;
1467 map->last->next = rec;
1478 /* Move a string record to the edge of a map object. */
1479 bool tcmapmove2(TCMAP *map, const char *kstr, bool head){
1480 assert(map && kstr);
1481 return tcmapmove(map, kstr, strlen(kstr), head);
1485 /* Initialize the iterator of a map object. */
1486 void tcmapiterinit(TCMAP *map){
1488 map->cur = map->first;
1492 /* Get the next key of the iterator of a map object. */
1493 const void *tcmapiternext(TCMAP *map, int *sp){
1496 if(!map->cur) return NULL;
1498 map->cur = rec->next;
1500 return (char *)rec + sizeof(*rec);
1504 /* Get the next key string of the iterator of a map object. */
1505 const char *tcmapiternext2(TCMAP *map){
1508 if(!map->cur) return NULL;
1510 map->cur = rec->next;
1511 return (char *)rec + sizeof(*rec);
1515 /* Get the value bound to the key fetched from the iterator of a map object. */
1516 const void *tcmapiterval(const void *kbuf, int *sp){
1518 TCMAPREC *rec = (TCMAPREC *)((char *)kbuf - sizeof(*rec));
1520 return (char *)kbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
1524 /* Get the value string bound to the key fetched from the iterator of a map object. */
1525 const char *tcmapiterval2(const char *kstr){
1527 TCMAPREC *rec = (TCMAPREC *)(kstr - sizeof(*rec));
1528 return kstr + rec->ksiz + TCALIGNPAD(rec->ksiz);
1532 /* Get the number of records stored in a map object. */
1533 uint64_t tcmaprnum(const TCMAP *map){
1539 /* Get the total size of memory used in a map object. */
1540 uint64_t tcmapmsiz(const TCMAP *map){
1542 return map->msiz + map->rnum * (sizeof(*map->first) + sizeof(void *)) +
1543 map->bnum * sizeof(void *);
1547 /* Create a list object containing all keys in a map object. */
1548 TCLIST *tcmapkeys(const TCMAP *map){
1550 TCLIST *list = tclistnew2(map->rnum);
1551 TCMAPREC *cur = map->cur;
1554 ((TCMAP *)map)->cur = map->first;
1555 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
1556 TCLISTPUSH(list, kbuf, ksiz);
1558 ((TCMAP *)map)->cur = cur;
1563 /* Create a list object containing all values in a map object. */
1564 TCLIST *tcmapvals(const TCMAP *map){
1566 TCLIST *list = tclistnew2(map->rnum);
1567 TCMAPREC *cur = map->cur;
1570 ((TCMAP *)map)->cur = map->first;
1571 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
1573 const char *vbuf = tcmapiterval(kbuf, &vsiz);
1574 TCLISTPUSH(list, vbuf, vsiz);
1576 ((TCMAP *)map)->cur = cur;
1581 /* Add an integer to a record in a map object. */
1582 void tcmapaddint(TCMAP *map, const char *kbuf, int ksiz, int num){
1583 assert(map && kbuf && ksiz >= 0);
1585 TCMAPHASH1(hash, kbuf, ksiz);
1586 int bidx = hash % map->bnum;
1587 TCMAPREC *rec = map->buckets[bidx];
1588 TCMAPREC **entp = map->buckets + bidx;
1589 TCMAPHASH2(hash, kbuf, ksiz);
1591 if(hash > rec->hash){
1592 entp = &(rec->left);
1594 } else if(hash < rec->hash){
1595 entp = &(rec->right);
1598 char *dbuf = (char *)rec + sizeof(*rec);
1599 int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
1601 entp = &(rec->left);
1603 } else if(kcmp > 0){
1604 entp = &(rec->right);
1607 int psiz = TCALIGNPAD(ksiz);
1608 *(int *)(dbuf + ksiz + psiz) += num;
1613 int psiz = TCALIGNPAD(ksiz);
1614 TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
1615 char *dbuf = (char *)rec + sizeof(*rec);
1616 memcpy(dbuf, kbuf, ksiz);
1619 memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
1620 dbuf[ksiz+psiz+sizeof(num)] = '\0';
1621 rec->vsiz = sizeof(num);
1625 rec->prev = map->last;
1628 if(!map->first) map->first = rec;
1629 if(map->last) map->last->next = rec;
1635 /* Clear a map object. */
1636 void tcmapclear(TCMAP *map){
1638 TCMAPREC *rec = map->first;
1640 TCMAPREC *next = rec->next;
1644 TCMAPREC **buckets = map->buckets;
1645 int bnum = map->bnum;
1646 for(int i = 0; i < bnum; i++){
1657 /* Remove front records of a map object. */
1658 void tcmapcutfront(TCMAP *map, int num){
1659 assert(map && num >= 0);
1663 const char *kbuf = tcmapiternext(map, &ksiz);
1665 tcmapout(map, kbuf, ksiz);
1670 /* Serialize a map object into a byte array. */
1671 void *tcmapdump(const TCMAP *map, int *sp){
1673 TCMAPREC *cur = map->cur;
1675 const char *kbuf, *vbuf;
1677 ((TCMAP *)map)->cur = map->first;
1678 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
1679 vbuf = tcmapiterval(kbuf, &vsiz);
1680 tsiz += ksiz + vsiz + sizeof(int) * 2;
1683 TCMALLOC(buf, tsiz + 1);
1685 ((TCMAP *)map)->cur = map->first;
1686 while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
1687 vbuf = tcmapiterval(kbuf, &vsiz);
1689 TCSETVNUMBUF(step, wp, ksiz);
1691 memcpy(wp, kbuf, ksiz);
1693 TCSETVNUMBUF(step, wp, vsiz);
1695 memcpy(wp, vbuf, vsiz);
1698 ((TCMAP *)map)->cur = cur;
1704 /* Create a map object from a serialized byte array. */
1705 TCMAP *tcmapload(const void *ptr, int size){
1706 assert(ptr && size >= 0);
1707 TCMAP *map = tcmapnew();
1708 const char *rp = ptr;
1709 const char *ep = (char *)ptr + size;
1711 int step, ksiz, vsiz;
1712 TCREADVNUMBUF(rp, ksiz, step);
1714 const char *kbuf = rp;
1716 TCREADVNUMBUF(rp, vsiz, step);
1718 tcmapputkeep(map, kbuf, ksiz, rp, vsiz);
1725 /* Extract a map record from a serialized byte array. */
1726 void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){
1727 assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp);
1728 const char *rp = ptr;
1729 const char *ep = (char *)ptr + size;
1732 TCREADVNUMBUF(rp, rsiz, step);
1734 if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){
1736 TCREADVNUMBUF(rp, rsiz, step);
1740 TCMEMDUP(rv, rp, rsiz);
1744 TCREADVNUMBUF(rp, rsiz, step);
1753 /*************************************************************************************************
1754 * on-memory database
1755 *************************************************************************************************/
1758 #define TCMDBMNUM 8 // number of internal maps
1759 #define TCMDBBNUM 65536 // allocation unit number of a map
1761 /* get the first hash value */
1762 #define TCMDBHASH(TC_res, TC_kbuf, TC_ksiz) \
1764 const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
1765 int _TC_ksiz = TC_ksiz; \
1766 for((TC_res) = 0x20071123; _TC_ksiz--;){ \
1767 (TC_res) = ((TC_res) << 5) + (TC_res) + *(_TC_p)--; \
1769 (TC_res) &= TCMDBMNUM - 1; \
1773 /* Create an on-memory database object. */
1774 TCMDB *tcmdbnew(void){
1775 return tcmdbnew2(TCMDBBNUM);
1779 /* Create an on-memory database with specifying the number of the buckets. */
1780 TCMDB *tcmdbnew2(uint32_t bnum){
1782 if(bnum < 1) bnum = TCMDBBNUM;
1783 bnum = bnum / TCMDBMNUM + 17;
1784 TCMALLOC(mdb, sizeof(*mdb));
1785 TCMALLOC(mdb->mmtxs, sizeof(pthread_rwlock_t) * TCMDBMNUM);
1786 TCMALLOC(mdb->imtx, sizeof(pthread_mutex_t));
1787 TCMALLOC(mdb->maps, sizeof(TCMAP *) * TCMDBMNUM);
1788 if(pthread_mutex_init(mdb->imtx, NULL) != 0) tcmyfatal("mutex error");
1789 for(int i = 0; i < TCMDBMNUM; i++){
1790 if(pthread_rwlock_init((pthread_rwlock_t *)mdb->mmtxs + i, NULL) != 0)
1791 tcmyfatal("rwlock error");
1792 mdb->maps[i] = tcmapnew2(bnum);
1799 /* Delete an on-memory database object. */
1800 void tcmdbdel(TCMDB *mdb){
1802 for(int i = TCMDBMNUM - 1; i >= 0; i--){
1803 tcmapdel(mdb->maps[i]);
1804 pthread_rwlock_destroy((pthread_rwlock_t *)mdb->mmtxs + i);
1806 pthread_mutex_destroy(mdb->imtx);
1814 /* Store a record into an on-memory database. */
1815 void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1816 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1818 TCMDBHASH(mi, kbuf, ksiz);
1819 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
1820 tcmapput(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
1821 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1825 /* Store a string record into an on-memory database. */
1826 void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr){
1827 assert(mdb && kstr && vstr);
1828 tcmdbput(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
1832 /* Store a record of the value of two regions into an on-memory database object. */
1833 void tcmdbput3(TCMDB *mdb, const char *kbuf, int ksiz,
1834 const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz){
1835 assert(mdb && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
1837 TCMDBHASH(mi, kbuf, ksiz);
1838 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
1839 tcmapput3(mdb->maps[mi], kbuf, ksiz, fvbuf, fvsiz, lvbuf, lvsiz);
1840 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1844 /* Store a new record into an on-memory database. */
1845 bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1846 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1848 TCMDBHASH(mi, kbuf, ksiz);
1849 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
1850 bool rv = tcmapputkeep(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
1851 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1856 /* Store a new string record into an on-memory database. */
1857 bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr){
1858 assert(mdb && kstr && vstr);
1859 return tcmdbputkeep(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
1863 /* Concatenate a value at the end of the value of the existing record in an on-memory database. */
1864 void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
1865 assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
1867 TCMDBHASH(mi, kbuf, ksiz);
1868 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
1869 tcmapputcat(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
1870 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1874 /* Concatenate a string at the end of the value of the existing record in an on-memory database. */
1875 void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr){
1876 assert(mdb && kstr && vstr);
1877 tcmdbputcat(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
1881 /* Remove a record of an on-memory database. */
1882 bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz){
1883 assert(mdb && kbuf && ksiz >= 0);
1885 TCMDBHASH(mi, kbuf, ksiz);
1886 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
1887 bool rv = tcmapout(mdb->maps[mi], kbuf, ksiz);
1888 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1893 /* Remove a string record of an on-memory database. */
1894 bool tcmdbout2(TCMDB *mdb, const char *kstr){
1895 assert(mdb && kstr);
1896 return tcmdbout(mdb, kstr, strlen(kstr));
1900 /* Retrieve a record in an on-memory database. */
1901 void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
1902 assert(mdb && kbuf && ksiz >= 0 && sp);
1904 TCMDBHASH(mi, kbuf, ksiz);
1905 if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
1907 const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
1910 TCMEMDUP(rv, vbuf, vsiz);
1915 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1920 /* Retrieve a string record in an on-memory database. */
1921 char *tcmdbget2(TCMDB *mdb, const char *kstr){
1922 assert(mdb && kstr);
1924 return tcmdbget(mdb, kstr, strlen(kstr), &vsiz);
1928 /* Retrieve a record and move it astern in an on-memory database. */
1929 void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
1930 assert(mdb && kbuf && ksiz >= 0 && sp);
1932 TCMDBHASH(mi, kbuf, ksiz);
1933 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
1935 const char *vbuf = tcmapget3(mdb->maps[mi], kbuf, ksiz, &vsiz);
1938 TCMEMDUP(rv, vbuf, vsiz);
1943 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1948 /* Get the size of the value of a record in an on-memory database object. */
1949 int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz){
1950 assert(mdb && kbuf && ksiz >= 0);
1952 TCMDBHASH(mi, kbuf, ksiz);
1953 if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return -1;
1955 const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
1956 if(!vbuf) vsiz = -1;
1957 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1962 /* Get the size of the value of a string record in an on-memory database object. */
1963 int tcmdbvsiz2(TCMDB *mdb, const char *kstr){
1964 assert(mdb && kstr);
1965 return tcmdbvsiz(mdb, kstr, strlen(kstr));
1969 /* Initialize the iterator of an on-memory database. */
1970 void tcmdbiterinit(TCMDB *mdb){
1972 if(pthread_mutex_lock(mdb->imtx) != 0) return;
1973 for(int i = 0; i < TCMDBMNUM; i++){
1974 tcmapiterinit(mdb->maps[i]);
1977 pthread_mutex_unlock(mdb->imtx);
1981 /* Get the next key of the iterator of an on-memory database. */
1982 void *tcmdbiternext(TCMDB *mdb, int *sp){
1984 if(pthread_mutex_lock(mdb->imtx) != 0) return NULL;
1985 if(mdb->iter < 0 || mdb->iter >= TCMDBMNUM){
1986 pthread_mutex_unlock(mdb->imtx);
1990 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
1991 pthread_mutex_unlock(mdb->imtx);
1996 while(!(kbuf = tcmapiternext(mdb->maps[mi], &ksiz)) && mi < TCMDBMNUM - 1){
1997 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
1999 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
2000 pthread_mutex_unlock(mdb->imtx);
2006 TCMEMDUP(rv, kbuf, ksiz);
2011 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
2012 pthread_mutex_unlock(mdb->imtx);
2017 /* Get the next key string of the iterator of an on-memory database. */
2018 char *tcmdbiternext2(TCMDB *mdb){
2021 return tcmdbiternext(mdb, &ksiz);
2025 /* Get forward matching keys in an on-memory database object. */
2026 TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max){
2027 assert(mdb && pbuf && psiz >= 0);
2028 TCLIST* keys = tclistnew();
2029 if(pthread_mutex_lock(mdb->imtx) != 0) return keys;
2030 if(max < 0) max = INT_MAX;
2031 for(int i = 0; i < TCMDBMNUM && TCLISTNUM(keys) < max; i++){
2032 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
2033 TCMAP *map = mdb->maps[i];
2034 TCMAPREC *cur = map->cur;
2038 while(TCLISTNUM(keys) < max && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
2039 if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)) tclistpush(keys, kbuf, ksiz);
2042 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
2045 pthread_mutex_unlock(mdb->imtx);
2050 /* Get forward matching string keys in an on-memory database object. */
2051 TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max){
2052 assert(mdb && pstr);
2053 return tcmdbfwmkeys(mdb, pstr, strlen(pstr), max);
2057 /* Get the number of records stored in an on-memory database. */
2058 uint64_t tcmdbrnum(TCMDB *mdb){
2061 for(int i = 0; i < TCMDBMNUM; i++){
2062 rnum += tcmaprnum(mdb->maps[i]);
2068 /* Get the total size of memory used in an on-memory database object. */
2069 uint64_t tcmdbmsiz(TCMDB *mdb){
2072 for(int i = 0; i < TCMDBMNUM; i++){
2073 msiz += tcmapmsiz(mdb->maps[i]);
2079 /* Clear an on-memory database object. */
2080 void tcmdbvanish(TCMDB *mdb){
2082 for(int i = 0; i < TCMDBMNUM; i++){
2083 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
2084 tcmapclear(mdb->maps[i]);
2085 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
2091 /* Remove front records of a map object. */
2092 void tcmdbcutfront(TCMDB *mdb, int num){
2093 assert(mdb && num >= 0);
2094 num = num / TCMDBMNUM + 1;
2095 for(int i = 0; i < TCMDBMNUM; i++){
2096 if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
2097 tcmapcutfront(mdb->maps[i], num);
2098 pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
2105 /*************************************************************************************************
2107 *************************************************************************************************/
2110 #define TCMPOOLUNIT 128 // allocation unit size of memory pool elements
2113 /* Global memory pool object. */
2114 TCMPOOL *tcglobalmemorypool = NULL;
2117 /* private function prototypes */
2118 static void tcmpooldelglobal(void);
2121 /* Create a memory pool object. */
2122 TCMPOOL *tcmpoolnew(void){
2124 TCMALLOC(mpool, sizeof(*mpool));
2125 TCMALLOC(mpool->mutex, sizeof(pthread_mutex_t));
2126 if(pthread_mutex_init(mpool->mutex, NULL) != 0) tcmyfatal("locking failed");
2127 mpool->anum = TCMPOOLUNIT;
2128 TCMALLOC(mpool->elems, sizeof(mpool->elems[0]) * mpool->anum);
2134 /* Delete a memory pool object. */
2135 void tcmpooldel(TCMPOOL *mpool){
2137 TCMPELEM *elems = mpool->elems;
2138 for(int i = mpool->num - 1; i >= 0; i--){
2139 elems[i].del(elems[i].ptr);
2142 pthread_mutex_destroy(mpool->mutex);
2148 /* Relegate an arbitrary object to a memory pool object. */
2149 void tcmpoolput(TCMPOOL *mpool, void *ptr, void (*del)(void *)){
2150 assert(mpool && ptr && del);
2151 if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed");
2152 int num = mpool->num;
2153 if(num >= mpool->anum){
2155 TCREALLOC(mpool->elems, mpool->elems, mpool->anum * sizeof(mpool->elems[0]));
2157 mpool->elems[num].ptr = ptr;
2158 mpool->elems[num].del = del;
2160 pthread_mutex_unlock(mpool->mutex);
2164 /* Relegate an allocated region to a memory pool object. */
2165 void tcmpoolputptr(TCMPOOL *mpool, void *ptr){
2166 assert(mpool && ptr);
2167 tcmpoolput(mpool, ptr, (void (*)(void *))free);
2171 /* Relegate an extensible string object to a memory pool object. */
2172 void tcmpoolputxstr(TCMPOOL *mpool, TCXSTR *xstr){
2173 assert(mpool && xstr);
2174 tcmpoolput(mpool, xstr, (void (*)(void *))tcxstrdel);
2178 /* Relegate a list object to a memory pool object. */
2179 void tcmpoolputlist(TCMPOOL *mpool, TCLIST *list){
2180 assert(mpool && list);
2181 tcmpoolput(mpool, list, (void (*)(void *))tclistdel);
2185 /* Relegate a map object to a memory pool object. */
2186 void tcmpoolputmap(TCMPOOL *mpool, TCMAP *map){
2187 assert(mpool && map);
2188 tcmpoolput(mpool, map, (void (*)(void *))tcmapdel);
2192 /* Allocate a region relegated to a memory pool object. */
2193 void *tcmpoolmalloc(TCMPOOL *mpool, size_t size){
2194 assert(mpool && size > 0);
2196 TCMALLOC(ptr, size);
2197 tcmpoolput(mpool, ptr, (void (*)(void *))free);
2202 /* Create an extensible string object relegated to a memory pool object. */
2203 TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool){
2205 TCXSTR *xstr = tcxstrnew();
2206 tcmpoolput(mpool, xstr, (void (*)(void *))tcxstrdel);
2211 /* Create a list object relegated to a memory pool object. */
2212 TCLIST *tcmpoollistnew(TCMPOOL *mpool){
2214 TCLIST *list = tclistnew();
2215 tcmpoolput(mpool, list, (void (*)(void *))tclistdel);
2220 /* Create a map object relegated to a memory pool object. */
2221 TCMAP *tcmpoolmapnew(TCMPOOL *mpool){
2223 TCMAP *map = tcmapnew();
2224 tcmpoolput(mpool, map, (void (*)(void *))tcmapdel);
2229 /* Get the global memory pool object. */
2230 TCMPOOL *tcmpoolglobal(void){
2231 if(tcglobalmemorypool) return tcglobalmemorypool;
2232 tcglobalmemorypool = tcmpoolnew();
2233 atexit(tcmpooldelglobal);
2234 return tcglobalmemorypool;
2238 /* Detete global memory pool object. */
2239 static void tcmpooldelglobal(void){
2240 if(tcglobalmemorypool) tcmpooldel(tcglobalmemorypool);
2245 /*************************************************************************************************
2246 * miscellaneous utilities
2247 *************************************************************************************************/
2250 #define TCRANDDEV "/dev/urandom" // path of the random device file
2251 #define TCDISTBUFSIZ 16384 // size of an distance buffer
2254 /* File descriptor of random number generator. */
2255 int tcrandomdevfd = -1;
2258 /* private function prototypes */
2259 static void tcrandomfdclose(void);
2260 static time_t tcmkgmtime(struct tm *tm);
2263 /* Get the larger value of two integers. */
2264 long tclmax(long a, long b){
2265 return (a > b) ? a : b;
2269 /* Get the lesser value of two integers. */
2270 long tclmin(long a, long b){
2271 return (a < b) ? a : b;
2275 /* Get a random number as long integer based on uniform distribution. */
2276 unsigned long tclrand(void){
2277 static unsigned int cnt = 0;
2278 static unsigned int seed = 0;
2279 static unsigned long mask = 0;
2280 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2281 if((cnt & 0xff) == 0 && pthread_mutex_lock(&mutex) == 0){
2282 if(cnt++ == 0) seed = time(NULL);
2283 if(tcrandomdevfd == -1 && (tcrandomdevfd = open(TCRANDDEV, O_RDONLY, 00644)) != -1)
2284 atexit(tcrandomfdclose);
2285 if(tcrandomdevfd != -1) read(tcrandomdevfd, &mask, sizeof(mask));
2286 pthread_mutex_unlock(&mutex);
2288 return (mask ^ cnt++) ^ (unsigned long)rand_r(&seed);
2292 /* Get a random number as double decimal based on uniform distribution. */
2293 double tcdrand(void){
2294 return tclrand() / (ULONG_MAX + 0.01);
2298 /* Get a random number as double decimal based on normal distribution. */
2299 double tcdrandnd(double avg, double sd){
2301 return sqrt(-2.0 * log(tcdrand())) * cos(2 * 3.141592653589793 * tcdrand()) * sd + avg;
2305 /* Compare two strings with case insensitive evaluation. */
2306 int tcstricmp(const char *astr, const char *bstr){
2307 assert(astr && bstr);
2308 while(*astr != '\0'){
2309 if(*bstr == '\0') return 1;
2310 int ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr;
2311 int bc = (*bstr >= 'A' && *bstr <= 'Z') ? *bstr + ('a' - 'A') : *(unsigned char *)bstr;
2312 if(ac != bc) return ac - bc;
2316 return (*bstr == '\0') ? 0 : -1;
2320 /* Check whether a string begins with a key. */
2321 bool tcstrfwm(const char *str, const char *key){
2323 while(*key != '\0'){
2324 if(*str != *key || *str == '\0') return false;
2332 /* Check whether a string begins with a key with case insensitive evaluation. */
2333 bool tcstrifwm(const char *str, const char *key){
2335 while(*key != '\0'){
2336 if(*str == '\0') return false;
2338 if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
2340 if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
2341 if(sc != kc) return false;
2349 /* Check whether a string ends with a key. */
2350 bool tcstrbwm(const char *str, const char *key){
2352 int slen = strlen(str);
2353 int klen = strlen(key);
2354 for(int i = 1; i <= klen; i++){
2355 if(i > slen || str[slen-i] != key[klen-i]) return false;
2361 /* Check whether a string ends with a key with case insensitive evaluation. */
2362 bool tcstribwm(const char *str, const char *key){
2364 int slen = strlen(str);
2365 int klen = strlen(key);
2366 for(int i = 1; i <= klen; i++){
2367 if(i > slen) return false;
2368 int sc = str[slen-i];
2369 if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
2370 int kc = key[klen-i];
2371 if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
2372 if(sc != kc) return false;
2378 /* Calculate the edit distance of two strings. */
2379 int tcstrdist(const char *astr, const char *bstr){
2380 assert(astr && bstr);
2381 int alen = strlen(astr);
2382 int blen = strlen(bstr);
2383 int dsiz = blen + 1;
2384 int tbuf[TCDISTBUFSIZ];
2386 if((alen + 1) * dsiz < TCDISTBUFSIZ){
2389 TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
2391 for(int i = 0; i <= alen; i++){
2394 for(int i = 1; i <= blen; i++){
2399 for(int i = 1; i <= alen; i++){
2400 for(int j = 1; j <= blen; j++){
2401 int ac = tbl[(i-1)*dsiz+j] + 1;
2402 int bc = tbl[i*dsiz+j-1] + 1;
2403 int cc = tbl[(i-1)*dsiz+j-1] + (astr[i] != bstr[j]);
2404 ac = ac < bc ? ac : bc;
2405 tbl[i*dsiz+j] = ac < cc ? ac : cc;
2408 int rv = tbl[alen*dsiz+blen];
2409 if(tbl != tbuf) free(tbl);
2414 /* Calculate the edit distance of two UTF-8 strings. */
2415 int tcstrdistutf(const char *astr, const char *bstr){
2416 assert(astr && bstr);
2417 int alen = strlen(astr);
2418 uint16_t abuf[TCDISTBUFSIZ];
2420 if(alen < TCDISTBUFSIZ){
2423 TCMALLOC(aary, alen * sizeof(*aary));
2425 tcstrutftoucs(astr, aary, &alen);
2426 int blen = strlen(bstr);
2427 uint16_t bbuf[TCDISTBUFSIZ];
2429 if(blen < TCDISTBUFSIZ){
2432 TCMALLOC(bary, blen * sizeof(*bary));
2434 tcstrutftoucs(bstr, bary, &blen);
2435 int dsiz = blen + 1;
2436 int tbuf[TCDISTBUFSIZ];
2438 if((alen + 1) * dsiz < TCDISTBUFSIZ){
2441 TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
2443 for(int i = 0; i <= alen; i++){
2446 for(int i = 1; i <= blen; i++){
2451 for(int i = 1; i <= alen; i++){
2452 for(int j = 1; j <= blen; j++){
2453 int ac = tbl[(i-1)*dsiz+j] + 1;
2454 int bc = tbl[i*dsiz+j-1] + 1;
2455 int cc = tbl[(i-1)*dsiz+j-1] + (aary[i] != bary[j]);
2456 ac = ac < bc ? ac : bc;
2457 tbl[i*dsiz+j] = ac < cc ? ac : cc;
2462 int rv = tbl[alen*dsiz+blen];
2463 if(tbl != tbuf) free(tbl);
2464 if(bary != bbuf) free(bary);
2465 if(aary != abuf) free(aary);
2470 /* Convert the letters of a string into upper case. */
2471 char *tcstrtoupper(char *str){
2475 if(*wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A';
2482 /* Convert the letters of a string into lower case. */
2483 char *tcstrtolower(char *str){
2487 if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
2494 /* Cut space characters at head or tail of a string. */
2495 char *tcstrtrim(char *str){
2497 const char *rp = str;
2501 if(*rp > '\0' && *rp <= ' '){
2502 if(!head) *(wp++) = *rp;
2510 while(wp > str && wp[-1] > '\0' && wp[-1] <= ' '){
2517 /* Squeeze space characters in a string and trim it. */
2518 char *tcstrsqzspc(char *str){
2524 if(*rp > 0 && *rp <= ' '){
2525 if(!spc) *(wp++) = *rp;
2534 for(wp--; wp >= str; wp--){
2535 if(*wp > 0 && *wp <= ' '){
2545 /* Substitute characters in a string. */
2546 char *tcstrsubchr(char *str, const char *rstr, const char *sstr){
2547 assert(str && rstr && sstr);
2548 int slen = strlen(sstr);
2550 for(int i = 0; str[i] != '\0'; i++){
2551 const char *p = strchr(rstr, str[i]);
2554 if(idx < slen) *(wp++) = sstr[idx];
2564 /* Count the number of characters in a string of UTF-8. */
2565 int tcstrcntutf(const char *str){
2567 const unsigned char *rp = (unsigned char *)str;
2570 if((*rp & 0x80) == 0x00 || (*rp & 0xe0) == 0xc0 ||
2571 (*rp & 0xf0) == 0xe0 || (*rp & 0xf8) == 0xf0) cnt++;
2578 /* Cut a string of UTF-8 at the specified number of characters. */
2579 char *tcstrcututf(char *str, int num){
2580 assert(str && num >= 0);
2581 unsigned char *wp = (unsigned char *)str;
2584 if((*wp & 0x80) == 0x00 || (*wp & 0xe0) == 0xc0 ||
2585 (*wp & 0xf0) == 0xe0 || (*wp & 0xf8) == 0xf0){
2598 /* Convert a UTF-8 string into a UCS-2 array. */
2599 void tcstrutftoucs(const char *str, uint16_t *ary, int *np){
2600 assert(str && ary && np);
2601 const unsigned char *rp = (unsigned char *)str;
2602 unsigned int wi = 0;
2604 int c = *(unsigned char *)rp;
2607 } else if(c < 0xe0){
2609 ary[wi++] = ((rp[0] & 0x1f) << 6) | (rp[1] & 0x3f);
2612 } else if(c < 0xf0){
2613 if(rp[1] >= 0x80 && rp[2] >= 0x80){
2614 ary[wi++] = ((rp[0] & 0xf) << 12) | ((rp[1] & 0x3f) << 6) | (rp[2] & 0x3f);
2624 /* Convert a UCS-2 array into a UTF-8 string. */
2625 void tcstrucstoutf(const uint16_t *ary, int num, char *str){
2626 assert(ary && num >= 0 && str);
2627 unsigned char *wp = (unsigned char *)str;
2628 for(int i = 0; i < num; i++){
2629 unsigned int c = ary[i];
2632 } else if(c < 0x800){
2633 *(wp++) = 0xc0 | (c >> 6);
2634 *(wp++) = 0x80 | (c & 0x3f);
2636 *(wp++) = 0xe0 | (c >> 12);
2637 *(wp++) = 0x80 | ((c & 0xfff) >> 6);
2638 *(wp++) = 0x80 | (c & 0x3f);
2645 /* Create a list object by splitting a string. */
2646 TCLIST *tcstrsplit(const char *str, const char *delims){
2647 assert(str && delims);
2648 TCLIST *list = tclistnew();
2650 const char *sp = str;
2651 while(*str != '\0' && !strchr(delims, *str)){
2654 TCLISTPUSH(list, sp, str - sp);
2655 if(*str == '\0') break;
2662 /* Create a string by joining all elements of a list object. */
2663 char *tcstrjoin(TCLIST *list, char delim){
2665 int num = TCLISTNUM(list);
2667 for(int i = 0; i < num; i++){
2668 size += TCLISTVALNUM(list, i);
2671 TCMALLOC(buf, size);
2673 for(int i = 0; i < num; i++){
2674 if(i > 0) *(wp++) = delim;
2676 const char *vbuf = tclistval(list, i, &vsiz);
2677 memcpy(wp, vbuf, vsiz);
2685 /* Check whether a string matches a regular expression. */
2686 bool tcregexmatch(const char *str, const char *regex){
2687 assert(str && regex);
2688 int options = REG_EXTENDED | REG_NOSUB;
2690 options |= REG_ICASE;
2694 if(regcomp(&rbuf, regex, options) != 0) return false;
2695 bool rv = regexec(&rbuf, str, 0, NULL, 0) == 0;
2701 /* Replace each substring matching a regular expression string. */
2702 char *tcregexreplace(const char *str, const char *regex, const char *alt){
2703 assert(str && regex && alt);
2704 int options = REG_EXTENDED;
2706 options |= REG_ICASE;
2710 if(regex[0] == '\0' || regcomp(&rbuf, regex, options) != 0) return tcstrdup(str);
2711 regmatch_t subs[256];
2712 if(regexec(&rbuf, str, 32, subs, 0) != 0){
2714 return tcstrdup(str);
2716 const char *sp = str;
2717 TCXSTR *xstr = tcxstrnew();
2719 while(sp[0] != '\0' && regexec(&rbuf, sp, 10, subs, first ? 0 : REG_NOTBOL) == 0){
2721 if(subs[0].rm_so == -1) break;
2722 tcxstrcat(xstr, sp, subs[0].rm_so);
2723 for(const char *rp = alt; *rp != '\0'; rp++){
2725 if(rp[1] >= '0' && rp[1] <= '9'){
2726 int num = rp[1] - '0';
2727 if(subs[num].rm_so != -1 && subs[num].rm_eo != -1)
2728 tcxstrcat(xstr, sp + subs[num].rm_so, subs[num].rm_eo - subs[num].rm_so);
2730 } else if(rp[1] != '\0'){
2731 tcxstrcat(xstr, ++rp, 1);
2733 } else if(*rp == '&'){
2734 tcxstrcat(xstr, sp + subs[0].rm_so, subs[0].rm_eo - subs[0].rm_so);
2736 tcxstrcat(xstr, rp, 1);
2739 sp += subs[0].rm_eo;
2740 if(subs[0].rm_eo < 1) break;
2742 tcxstrcat2(xstr, sp);
2744 return tcxstrtomalloc(xstr);
2748 /* Get the time of day in seconds. */
2749 double tctime(void){
2751 if(gettimeofday(&tv, NULL) == -1) return 0.0;
2752 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
2756 /* Get the Gregorian calendar of a time. */
2757 void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp,
2758 int *hourp, int *minp, int *secp){
2759 if(t == INT64_MAX || jl == INT_MAX){
2762 if(gettimeofday(&tv, &tz) == 0){
2763 if(t == INT64_MAX) t = tv.tv_sec;
2764 if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
2766 if(yearp) *yearp = 0;
2769 if(hourp) *hourp = 0;
2774 time_t tt = (time_t)t + jl;
2776 if(!gmtime_r(&tt, &ts)){
2777 if(yearp) *yearp = 0;
2780 if(hourp) *hourp = 0;
2784 if(yearp) *yearp = ts.tm_year + 1900;
2785 if(monp) *monp = ts.tm_mon + 1;
2786 if(dayp) *dayp = ts.tm_mday;
2787 if(hourp) *hourp = ts.tm_hour;
2788 if(minp) *minp = ts.tm_min;
2789 if(secp) *secp = ts.tm_sec;
2793 /* Format a date as a string in W3CDTF. */
2794 void tcdatestrwww(int64_t t, int jl, char *buf){
2796 if(t == INT64_MAX || jl == INT_MAX){
2799 if(gettimeofday(&tv, &tz) == 0){
2800 if(t == INT64_MAX) t = tv.tv_sec;
2801 if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
2803 if(t == INT64_MAX) t = time(NULL);
2804 if(jl == INT_MAX) jl = 0;
2807 time_t tt = (time_t)t + jl;
2809 if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
2815 sprintf(tzone, "Z");
2818 sprintf(tzone, "-%02d:%02d", jl / 60, jl % 60);
2820 sprintf(tzone, "+%02d:%02d", jl / 60, jl % 60);
2822 sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%s",
2823 ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzone);
2827 /* Format a date as a string in RFC 1123 format. */
2828 void tcdatestrhttp(int64_t t, int jl, char *buf){
2830 if(t == INT64_MAX || jl == INT_MAX){
2833 if(gettimeofday(&tv, &tz) == 0){
2834 if(t == INT64_MAX) t = tv.tv_sec;
2835 if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
2837 if(t == INT64_MAX) t = time(NULL);
2838 if(jl == INT_MAX) jl = 0;
2841 time_t tt = (time_t)t + jl;
2843 if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
2848 switch(tcdayofweek(ts.tm_year, ts.tm_mon, ts.tm_mday)){
2849 case 0: wp += sprintf(wp, "Sun, "); break;
2850 case 1: wp += sprintf(wp, "Mon, "); break;
2851 case 2: wp += sprintf(wp, "Tue, "); break;
2852 case 3: wp += sprintf(wp, "Wed, "); break;
2853 case 4: wp += sprintf(wp, "Thu, "); break;
2854 case 5: wp += sprintf(wp, "Fri, "); break;
2855 case 6: wp += sprintf(wp, "Sat, "); break;
2857 wp += sprintf(wp, "%02d ", ts.tm_mday);
2859 case 1: wp += sprintf(wp, "Jan "); break;
2860 case 2: wp += sprintf(wp, "Feb "); break;
2861 case 3: wp += sprintf(wp, "Mar "); break;
2862 case 4: wp += sprintf(wp, "Apr "); break;
2863 case 5: wp += sprintf(wp, "May "); break;
2864 case 6: wp += sprintf(wp, "Jun "); break;
2865 case 7: wp += sprintf(wp, "Jul "); break;
2866 case 8: wp += sprintf(wp, "Aug "); break;
2867 case 9: wp += sprintf(wp, "Sep "); break;
2868 case 10: wp += sprintf(wp, "Oct "); break;
2869 case 11: wp += sprintf(wp, "Nov "); break;
2870 case 12: wp += sprintf(wp, "Dec "); break;
2872 wp += sprintf(wp, "%04d %02d:%02d:%02d ", ts.tm_year, ts.tm_hour, ts.tm_min, ts.tm_sec);
2877 sprintf(wp, "-%02d%02d", jl / 60, jl % 60);
2879 sprintf(wp, "+%02d%02d", jl / 60, jl % 60);
2884 /* Get the time value of a date string. */
2885 int64_t tcstrmktime(const char *str){
2887 while(*str > '\0' && *str <= ' '){
2890 if(*str == '\0') return 0;
2891 if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
2892 return (int64_t)strtoll(str + 2, NULL, 16);
2894 memset(&ts, 0, sizeof(ts));
2902 int len = strlen(str);
2904 time_t t = (time_t)strtoll(str, &pv, 10);
2905 if(*(signed char *)pv >= '\0' && *pv <= ' '){
2906 while(*pv > '\0' && *pv <= ' '){
2909 if(*pv == '\0') return (int64_t)t;
2911 if((pv[0] == 's' || pv[0] == 'S') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
2913 if((pv[0] == 'm' || pv[0] == 'M') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
2914 return (int64_t)t * 60;
2915 if((pv[0] == 'h' || pv[0] == 'H') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
2916 return (int64_t)t * 60 * 60;
2917 if((pv[0] == 'd' || pv[0] == 'D') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
2918 return (int64_t)t * 60 * 60 * 24;
2919 if(len > 4 && str[4] == '-'){
2920 ts.tm_year = atoi(str) - 1900;
2921 if((pv = strchr(str, '-')) != NULL && pv - str == 4){
2923 ts.tm_mon = atoi(rp) - 1;
2924 if((pv = strchr(rp, '-')) != NULL && pv - str == 7){
2926 ts.tm_mday = atoi(rp);
2927 if((pv = strchr(rp, 'T')) != NULL && pv - str == 10){
2929 ts.tm_hour = atoi(rp);
2930 if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
2932 ts.tm_min = atoi(rp);
2934 if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
2936 ts.tm_sec = atoi(rp);
2938 if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
2939 strtol(rp, &pv, 10);
2940 if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
2941 ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
2945 return (int64_t)tcmkgmtime(&ts);
2947 if(len > 4 && str[4] == '/'){
2948 ts.tm_year = atoi(str) - 1900;
2949 if((pv = strchr(str, '/')) != NULL && pv - str == 4){
2951 ts.tm_mon = atoi(rp) - 1;
2952 if((pv = strchr(rp, '/')) != NULL && pv - str == 7){
2954 ts.tm_mday = atoi(rp);
2955 if((pv = strchr(rp, ' ')) != NULL && pv - str == 10){
2957 ts.tm_hour = atoi(rp);
2958 if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
2960 ts.tm_min = atoi(rp);
2962 if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
2964 ts.tm_sec = atoi(rp);
2966 if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
2967 strtol(rp, &pv, 10);
2968 if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
2969 ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
2973 return (int64_t)tcmkgmtime(&ts);
2975 const char *crp = str;
2976 if(len >= 4 && str[3] == ',') crp = str + 4;
2980 ts.tm_mday = atoi(crp);
2981 while((*crp >= '0' && *crp <= '9') || *crp == ' '){
2984 if(tcstrifwm(crp, "Jan")){
2986 } else if(tcstrifwm(crp, "Feb")){
2988 } else if(tcstrifwm(crp, "Mar")){
2990 } else if(tcstrifwm(crp, "Apr")){
2992 } else if(tcstrifwm(crp, "May")){
2994 } else if(tcstrifwm(crp, "Jun")){
2996 } else if(tcstrifwm(crp, "Jul")){
2998 } else if(tcstrifwm(crp, "Aug")){
3000 } else if(tcstrifwm(crp, "Sep")){
3002 } else if(tcstrifwm(crp, "Oct")){
3004 } else if(tcstrifwm(crp, "Nov")){
3006 } else if(tcstrifwm(crp, "Dec")){
3011 if(ts.tm_mon >= 0) crp += 3;
3015 ts.tm_year = atoi(crp);
3016 if(ts.tm_year >= 1969) ts.tm_year -= 1900;
3017 while(*crp >= '0' && *crp <= '9'){
3023 if(ts.tm_mday > 0 && ts.tm_mon >= 0 && ts.tm_year >= 0){
3024 int clen = strlen(crp);
3025 if(clen >= 8 && crp[2] == ':' && crp[5] == ':'){
3026 ts.tm_hour = atoi(crp + 0);
3027 ts.tm_min = atoi(crp + 3);
3028 ts.tm_sec = atoi(crp + 6);
3029 if(clen >= 14 && crp[8] == ' ' && (crp[9] == '+' || crp[9] == '-')){
3030 ts.tm_sec -= ((crp[10] - '0') * 36000 + (crp[11] - '0') * 3600 +
3031 (crp[12] - '0') * 600 + (crp[13] - '0') * 60) * (crp[9] == '+' ? 1 : -1);
3032 } else if(clen > 9){
3033 if(!strcmp(crp + 9, "JST")){
3034 ts.tm_sec -= 9 * 3600;
3035 } else if(!strcmp(crp + 9, "CCT")){
3036 ts.tm_sec -= 8 * 3600;
3037 } else if(!strcmp(crp + 9, "KST")){
3038 ts.tm_sec -= 9 * 3600;
3039 } else if(!strcmp(crp + 9, "EDT")){
3040 ts.tm_sec -= -4 * 3600;
3041 } else if(!strcmp(crp + 9, "EST")){
3042 ts.tm_sec -= -5 * 3600;
3043 } else if(!strcmp(crp + 9, "CDT")){
3044 ts.tm_sec -= -5 * 3600;
3045 } else if(!strcmp(crp + 9, "CST")){
3046 ts.tm_sec -= -6 * 3600;
3047 } else if(!strcmp(crp + 9, "MDT")){
3048 ts.tm_sec -= -6 * 3600;
3049 } else if(!strcmp(crp + 9, "MST")){
3050 ts.tm_sec -= -7 * 3600;
3051 } else if(!strcmp(crp + 9, "PDT")){
3052 ts.tm_sec -= -7 * 3600;
3053 } else if(!strcmp(crp + 9, "PST")){
3054 ts.tm_sec -= -8 * 3600;
3055 } else if(!strcmp(crp + 9, "HDT")){
3056 ts.tm_sec -= -9 * 3600;
3057 } else if(!strcmp(crp + 9, "HST")){
3058 ts.tm_sec -= -10 * 3600;
3062 return (int64_t)tcmkgmtime(&ts);
3068 /* Get the day of week of a date. */
3069 int tcdayofweek(int year, int mon, int day){
3074 return (day + ((8 + (13 * mon)) / 5) + (year + (year / 4) - (year / 100) + (year / 400))) % 7;
3078 /* Close the random number generator. */
3079 static void tcrandomfdclose(void){
3080 close(tcrandomdevfd);
3084 /* Make the GMT from a time structure.
3085 `tm' specifies the pointer to the time structure.
3086 The return value is the GMT. */
3087 static time_t tcmkgmtime(struct tm *tm){
3088 #if defined(_SYS_LINUX_)
3093 static int jl = INT_MAX;
3097 if(gettimeofday(&tv, &tz) == 0){
3098 jl = tz.tz_minuteswest * -60;
3110 /*************************************************************************************************
3111 * filesystem utilities
3112 *************************************************************************************************/
3115 #define TCFILEMODE 00644 // permission of a creating file
3116 #define TCIOBUFSIZ 16384 // size of an I/O buffer
3119 /* Get the canonicalized absolute path of a file. */
3120 char *tcrealpath(const char *path){
3123 if(!realpath(path, buf)) return NULL;
3124 return tcstrdup(buf);
3128 /* Read whole data of a file. */
3129 void *tcreadfile(const char *path, int limit, int *sp){
3130 int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
3131 if(fd == -1) return NULL;
3133 TCXSTR *xstr = tcxstrnew();
3134 char buf[TCIOBUFSIZ];
3135 limit = limit > 0 ? limit : INT_MAX;
3137 while((rsiz = read(fd, buf, tclmin(TCIOBUFSIZ, limit))) > 0){
3138 TCXSTRCAT(xstr, buf, rsiz);
3141 if(sp) *sp = TCXSTRSIZE(xstr);
3142 return tcxstrtomalloc(xstr);
3145 if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
3149 limit = limit > 0 ? tclmin((int)sbuf.st_size, limit) : sbuf.st_size;
3151 TCMALLOC(buf, sbuf.st_size + 1);
3154 while((rsiz = read(fd, wp, limit - (wp - buf))) > 0){
3159 if(sp) *sp = wp - buf;
3164 /* Read every line of a file. */
3165 TCLIST *tcreadfilelines(const char *path){
3166 int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
3167 if(fd == -1) return NULL;
3168 TCLIST *list = tclistnew();
3169 TCXSTR *xstr = tcxstrnew();
3170 char buf[TCIOBUFSIZ];
3172 while((rsiz = read(fd, buf, TCIOBUFSIZ)) > 0){
3173 for(int i = 0; i < rsiz; i++){
3178 TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
3182 TCXSTRCAT(xstr, buf + i, 1);
3187 TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
3194 /* Write data into a file. */
3195 bool tcwritefile(const char *path, const void *ptr, int size){
3196 assert(ptr && size >= 0);
3198 if(path && (fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE)) == -1) return false;
3200 if(!tcwrite(fd, ptr, size)) err = true;
3201 if(close(fd) == -1) err = true;
3207 bool tccopyfile(const char *src, const char *dest){
3208 int ifd = open(src, O_RDONLY, TCFILEMODE);
3209 if(ifd == -1) return false;
3210 int ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE);
3217 char buf[TCIOBUFSIZ];
3218 int size = read(ifd, buf, TCIOBUFSIZ);
3220 if(!tcwrite(ofd, buf, size)){
3224 } else if(size == -1){
3233 if(close(ofd) == -1) err = true;
3234 if(close(ifd) == -1) err = true;
3239 /* Read names of files in a directory. */
3240 TCLIST *tcreaddir(const char *path){
3244 if(!(DD = opendir(path))) return NULL;
3245 TCLIST *list = tclistnew();
3246 while((dp = readdir(DD)) != NULL){
3247 if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
3248 TCLISTPUSH(list, dp->d_name, strlen(dp->d_name));
3255 /* Expand a pattern into a list of matched paths. */
3256 TCLIST *tcglobpat(const char *pattern){
3258 TCLIST *list = tclistnew();
3260 memset(&gbuf, 0, sizeof(gbuf));
3261 if(glob(pattern, GLOB_ERR | GLOB_NOSORT, NULL, &gbuf) == 0){
3262 for(int i = 0; i < gbuf.gl_pathc; i++){
3263 tclistpush2(list, gbuf.gl_pathv[i]);
3271 /* Remove a file or a directory and its sub ones recursively. */
3272 bool tcremovelink(const char *path){
3275 if(lstat(path, &sbuf) == -1) return false;
3276 if(unlink(path) == 0) return true;
3278 if(!S_ISDIR(sbuf.st_mode) || !(list = tcreaddir(path))) return false;
3279 bool tail = path[0] != '\0' && path[strlen(path)-1] == MYPATHCHR;
3280 for(int i = 0; i < TCLISTNUM(list); i++){
3281 const char *elem = TCLISTVALPTR(list, i);
3282 if(!strcmp(MYCDIRSTR, elem) || !strcmp(MYPDIRSTR, elem)) continue;
3285 cpath = tcsprintf("%s%s", path, elem);
3287 cpath = tcsprintf("%s%c%s", path, MYPATHCHR, elem);
3289 tcremovelink(cpath);
3293 return rmdir(path) == 0 ? true : false;
3297 /* Write data into a file. */
3298 bool tcwrite(int fd, const void *buf, size_t size){
3299 assert(fd >= 0 && buf && size >= 0);
3300 const char *rp = buf;
3302 int wb = write(fd, rp, size);
3304 case -1: if(errno != EINTR) return false;
3316 /* Read data from a file. */
3317 bool tcread(int fd, void *buf, size_t size){
3318 assert(fd >= 0 && buf && size >= 0);
3321 int rb = read(fd, wp, size);
3323 case -1: if(errno != EINTR) return false;
3324 case 0: return size < 1;
3335 bool tclock(int fd, bool ex, bool nb){
3338 memset(&lock, 0, sizeof(struct flock));
3339 lock.l_type = ex ? F_WRLCK : F_RDLCK;
3340 lock.l_whence = SEEK_SET;
3344 while(fcntl(fd, nb ? F_SETLK : F_SETLKW, &lock) == -1){
3345 if(errno != EINTR) return false;
3352 /*************************************************************************************************
3353 * encoding utilities
3354 *************************************************************************************************/
3357 #define TCURLELBNUM 31 // bucket number of URL elements
3358 #define TCENCBUFSIZ 32 // size of a buffer for encoding name
3359 #define TCXMLATBNUM 31 // bucket number of XML attributes
3362 /* Encode a serial object with URL encoding. */
3363 char *tcurlencode(const char *ptr, int size){
3364 assert(ptr && size >= 0);
3366 TCMALLOC(buf, size * 3 + 1);
3368 for(int i = 0; i < size; i++){
3369 int c = ((unsigned char *)ptr)[i];
3370 if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
3371 (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.!~*'()", c))){
3374 wp += sprintf(wp, "%%%02X", c);
3382 /* Decode a string encoded with URL encoding. */
3383 char *tcurldecode(const char *str, int *sp){
3385 char *buf = tcstrdup(str);
3387 while(*str != '\0'){
3390 if(((str[0] >= '0' && str[0] <= '9') || (str[0] >= 'A' && str[0] <= 'F') ||
3391 (str[0] >= 'a' && str[0] <= 'f')) &&
3392 ((str[1] >= '0' && str[1] <= '9') || (str[1] >= 'A' && str[1] <= 'F') ||
3393 (str[1] >= 'a' && str[1] <= 'f'))){
3394 unsigned char c = *str;
3395 if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
3396 if(c >= 'a' && c <= 'z'){
3404 if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
3405 if(c >= 'a' && c <= 'z'){
3406 *wp += c - 'a' + 10;
3415 } else if(*str == '+'){
3431 /* Break up a URL into elements. */
3432 TCMAP *tcurlbreak(const char *str){
3434 TCMAP *map = tcmapnew2(TCURLELBNUM);
3435 char *tmp = tcstrdup(str);
3436 const char *rp = tcstrtrim(tmp);
3437 tcmapput2(map, "self", rp);
3439 if(tcstrifwm(rp, "http://")){
3440 tcmapput2(map, "scheme", "http");
3443 } else if(tcstrifwm(rp, "https://")){
3444 tcmapput2(map, "scheme", "https");
3447 } else if(tcstrifwm(rp, "ftp://")){
3448 tcmapput2(map, "scheme", "ftp");
3451 } else if(tcstrifwm(rp, "sftp://")){
3452 tcmapput2(map, "scheme", "sftp");
3455 } else if(tcstrifwm(rp, "ftps://")){
3456 tcmapput2(map, "scheme", "ftps");
3459 } else if(tcstrifwm(rp, "tftp://")){
3460 tcmapput2(map, "scheme", "tftp");
3463 } else if(tcstrifwm(rp, "ldap://")){
3464 tcmapput2(map, "scheme", "ldap");
3467 } else if(tcstrifwm(rp, "ldaps://")){
3468 tcmapput2(map, "scheme", "ldaps");
3471 } else if(tcstrifwm(rp, "file://")){
3472 tcmapput2(map, "scheme", "file");
3477 if((ep = strchr(rp, '#')) != NULL){
3478 tcmapput2(map, "fragment", ep + 1);
3481 if((ep = strchr(rp, '?')) != NULL){
3482 tcmapput2(map, "query", ep + 1);
3486 if((ep = strchr(rp, '/')) != NULL){
3487 tcmapput2(map, "path", ep);
3490 tcmapput2(map, "path", "/");
3492 if((ep = strchr(rp, '@')) != NULL){
3494 if(rp[0] != '\0') tcmapput2(map, "authority", rp);
3497 if((ep = strchr(rp, ':')) != NULL){
3498 if(ep[1] != '\0') tcmapput2(map, "port", ep + 1);
3501 if(rp[0] != '\0') tcmapput2(map, "host", rp);
3503 tcmapput2(map, "path", rp);
3506 if((rp = tcmapget2(map, "path")) != NULL){
3507 if((ep = strrchr(rp, '/')) != NULL){
3508 if(ep[1] != '\0') tcmapput2(map, "file", ep + 1);
3510 tcmapput2(map, "file", rp);
3513 if((rp = tcmapget2(map, "file")) != NULL && (!strcmp(rp, ".") || !strcmp(rp, "..")))
3514 tcmapout2(map, "file");
3519 /* Resolve a relative URL with an absolute URL. */
3520 char *tcurlresolve(const char *base, const char *target){
3521 assert(base && target);
3522 const char *vbuf, *path;
3523 char *tmp, *wp, *enc;
3524 while(*base > '\0' && *base <= ' '){
3527 while(*target > '\0' && *target <= ' '){
3530 if(*target == '\0') target = base;
3531 TCXSTR *rbuf = tcxstrnew();
3532 TCMAP *telems = tcurlbreak(target);
3534 TCMAP *belems = tcurlbreak(tcmapget2(telems, "scheme") ? target : base);
3535 if((vbuf = tcmapget2(belems, "scheme")) != NULL){
3536 tcxstrcat2(rbuf, vbuf);
3537 TCXSTRCAT(rbuf, "://", 3);
3538 if(!tcstricmp(vbuf, "https")){
3540 } else if(!tcstricmp(vbuf, "ftp")){
3542 } else if(!tcstricmp(vbuf, "sftp")){
3544 } else if(!tcstricmp(vbuf, "ftps")){
3546 } else if(!tcstricmp(vbuf, "tftp")){
3548 } else if(!tcstricmp(vbuf, "ldap")){
3550 } else if(!tcstricmp(vbuf, "ldaps")){
3554 tcxstrcat2(rbuf, "http://");
3557 if((vbuf = tcmapget2(belems, "authority")) != NULL){
3558 if((wp = strchr(vbuf, ':')) != NULL){
3560 tmp = tcurldecode(vbuf, &vsiz);
3561 enc = tcurlencode(tmp, vsiz);
3562 tcxstrcat2(rbuf, enc);
3565 TCXSTRCAT(rbuf, ":", 1);
3567 tmp = tcurldecode(wp, &vsiz);
3568 enc = tcurlencode(tmp, vsiz);
3569 tcxstrcat2(rbuf, enc);
3573 tmp = tcurldecode(vbuf, &vsiz);
3574 enc = tcurlencode(tmp, vsiz);
3575 tcxstrcat2(rbuf, enc);
3579 TCXSTRCAT(rbuf, "@", 1);
3581 if((vbuf = tcmapget2(belems, "host")) != NULL){
3582 tmp = tcurldecode(vbuf, &vsiz);
3584 enc = tcurlencode(tmp, vsiz);
3585 tcxstrcat2(rbuf, enc);
3589 TCXSTRCAT(rbuf, "localhost", 9);
3592 char numbuf[TCNUMBUFSIZ];
3593 if((vbuf = tcmapget2(belems, "port")) != NULL && (num = atoi(vbuf)) != port && num > 0){
3594 sprintf(numbuf, ":%d", num);
3595 tcxstrcat2(rbuf, numbuf);
3597 if(!(path = tcmapget2(telems, "path"))) path = "/";
3598 if(path[0] == '\0' && (vbuf = tcmapget2(belems, "path")) != NULL) path = vbuf;
3599 if(path[0] == '\0') path = "/";
3600 TCLIST *bpaths = tclistnew();
3602 if(path[0] != '/' && (vbuf = tcmapget2(belems, "path")) != NULL){
3603 opaths = tcstrsplit(vbuf, "/");
3605 opaths = tcstrsplit("/", "/");
3607 free(tclistpop2(opaths));
3608 for(int i = 0; i < TCLISTNUM(opaths); i++){
3609 vbuf = tclistval(opaths, i, &vsiz);
3610 if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
3611 if(!strcmp(vbuf, "..")){
3612 free(tclistpop2(bpaths));
3614 TCLISTPUSH(bpaths, vbuf, vsiz);
3618 opaths = tcstrsplit(path, "/");
3619 for(int i = 0; i < TCLISTNUM(opaths); i++){
3620 vbuf = tclistval(opaths, i, &vsiz);
3621 if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
3622 if(!strcmp(vbuf, "..")){
3623 free(tclistpop2(bpaths));
3625 TCLISTPUSH(bpaths, vbuf, vsiz);
3629 for(int i = 0; i < TCLISTNUM(bpaths); i++){
3630 vbuf = TCLISTVALPTR(bpaths, i);
3631 if(strchr(vbuf, '%')){
3632 tmp = tcurldecode(vbuf, &vsiz);
3634 tmp = tcstrdup(vbuf);
3636 enc = tcurlencode(tmp, strlen(tmp));
3637 TCXSTRCAT(rbuf, "/", 1);
3638 tcxstrcat2(rbuf, enc);
3642 if(tcstrbwm(path, "/")) TCXSTRCAT(rbuf, "/", 1);
3644 if((vbuf = tcmapget2(telems, "query")) != NULL ||
3645 (*target == '#' && (vbuf = tcmapget2(belems, "query")) != NULL)){
3646 TCXSTRCAT(rbuf, "?", 1);
3647 TCLIST *qelems = tcstrsplit(vbuf, "&;");
3648 for(int i = 0; i < TCLISTNUM(qelems); i++){
3649 vbuf = TCLISTVALPTR(qelems, i);
3650 if(i > 0) TCXSTRCAT(rbuf, "&", 1);
3651 if((wp = strchr(vbuf, '=')) != NULL){
3653 tmp = tcurldecode(vbuf, &vsiz);
3654 enc = tcurlencode(tmp, vsiz);
3655 tcxstrcat2(rbuf, enc);
3658 TCXSTRCAT(rbuf, "=", 1);
3660 tmp = tcurldecode(wp, &vsiz);
3661 enc = tcurlencode(tmp, strlen(tmp));
3662 tcxstrcat2(rbuf, enc);
3666 tmp = tcurldecode(vbuf, &vsiz);
3667 enc = tcurlencode(tmp, vsiz);
3668 tcxstrcat2(rbuf, enc);
3675 if((vbuf = tcmapget2(telems, "fragment")) != NULL){
3676 tmp = tcurldecode(vbuf, &vsiz);
3677 enc = tcurlencode(tmp, vsiz);
3678 TCXSTRCAT(rbuf, "#", 1);
3679 tcxstrcat2(rbuf, enc);
3685 return tcxstrtomalloc(rbuf);
3689 /* Encode a serial object with Base64 encoding. */
3690 char *tcbaseencode(const char *ptr, int size){
3691 assert(ptr && size >= 0);
3692 char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3693 const unsigned char *obj = (const unsigned char *)ptr;
3695 TCMALLOC(buf, 4 * (size + 2) / 3 + 1);
3697 for(int i = 0; i < size; i += 3){
3700 *wp++ = tbl[obj[0] >> 2];
3701 *wp++ = tbl[(obj[0] & 3) << 4];
3706 *wp++ = tbl[obj[0] >> 2];
3707 *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
3708 *wp++ = tbl[(obj[1] & 0xf) << 2];
3712 *wp++ = tbl[obj[0] >> 2];
3713 *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
3714 *wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)];
3715 *wp++ = tbl[obj[2] & 0x3f];
3725 /* Decode a string encoded with Base64 encoding. */
3726 char *tcbasedecode(const char *str, int *sp){
3731 int len = strlen(str);
3733 TCMALLOC(obj, len + 4);
3734 unsigned char *wp = obj;
3735 while(bpos < len && eqcnt == 0){
3738 for(i = 0; bpos < len && i < 4; bpos++){
3739 if(str[bpos] >= 'A' && str[bpos] <= 'Z'){
3740 bits = (bits << 6) | (str[bpos] - 'A');
3742 } else if(str[bpos] >= 'a' && str[bpos] <= 'z'){
3743 bits = (bits << 6) | (str[bpos] - 'a' + 26);
3745 } else if(str[bpos] >= '0' && str[bpos] <= '9'){
3746 bits = (bits << 6) | (str[bpos] - '0' + 52);
3748 } else if(str[bpos] == '+'){
3749 bits = (bits << 6) | 62;
3751 } else if(str[bpos] == '/'){
3752 bits = (bits << 6) | 63;
3754 } else if(str[bpos] == '='){
3760 if(i == 0 && bpos >= len) continue;
3763 *wp++ = (bits >> 16) & 0xff;
3764 *wp++ = (bits >> 8) & 0xff;
3765 *wp++ = bits & 0xff;
3769 *wp++ = (bits >> 16) & 0xff;
3770 *wp++ = (bits >> 8) & 0xff;
3774 *wp++ = (bits >> 16) & 0xff;
3785 /* Encode a serial object with Quoted-printable encoding. */
3786 char *tcquoteencode(const char *ptr, int size){
3787 assert(ptr && size >= 0);
3788 const unsigned char *rp = (const unsigned char *)ptr;
3790 TCMALLOC(buf, size * 3 + 1);
3793 for(int i = 0; i < size; i++){
3794 if(rp[i] == '=' || (rp[i] < 0x20 && rp[i] != '\r' && rp[i] != '\n' && rp[i] != '\t') ||
3796 wp += sprintf(wp, "=%02X", rp[i]);
3808 /* Decode a string encoded with Quoted-printable encoding. */
3809 char *tcquotedecode(const char *str, int *sp){
3812 TCMALLOC(buf, strlen(str) + 1);
3814 for(; *str != '\0'; str++){
3819 } else if(str[0] == '\r' && str[1] == '\n'){
3821 } else if(str[0] != '\n' && str[0] != '\r'){
3822 if(*str >= 'A' && *str <= 'Z'){
3823 *wp = (*str - 'A' + 10) * 16;
3824 } else if(*str >= 'a' && *str <= 'z'){
3825 *wp = (*str - 'a' + 10) * 16;
3827 *wp = (*str - '0') * 16;
3830 if(*str == '\0') break;
3831 if(*str >= 'A' && *str <= 'Z'){
3832 *wp += *str - 'A' + 10;
3833 } else if(*str >= 'a' && *str <= 'z'){
3834 *wp += *str - 'a' + 10;
3851 /* Encode a string with MIME encoding. */
3852 char *tcmimeencode(const char *str, const char *encname, bool base){
3853 assert(str && encname);
3854 int len = strlen(str);
3856 TCMALLOC(buf, len * 3 + strlen(encname) + 16);
3858 wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q');
3859 char *enc = base ? tcbaseencode(str, len) : tcquoteencode(str, len);
3860 wp += sprintf(wp, "%s?=", enc);
3866 /* Decode a string encoded with MIME encoding. */
3867 char *tcmimedecode(const char *str, char *enp){
3869 if(enp) sprintf(enp, "US-ASCII");
3871 TCMALLOC(buf, strlen(str) + 1);
3873 while(*str != '\0'){
3874 if(tcstrfwm(str, "=?")){
3876 const char *pv = str;
3877 const char *ep = strchr(str, '?');
3879 if(enp && ep - pv < TCENCBUFSIZ){
3880 memcpy(enp, pv, ep - pv);
3884 bool quoted = (*pv == 'Q' || *pv == 'q');
3885 if(*pv != '\0') pv++;
3886 if(*pv != '\0') pv++;
3887 if(!(ep = strchr(pv, '?'))) continue;
3889 TCMEMDUP(tmp, pv, ep - pv);
3891 char *dec = quoted ? tcquotedecode(tmp, &len) : tcbasedecode(tmp, &len);
3892 wp += sprintf(wp, "%s", dec);
3896 if(*str != '\0') str++;
3907 /* Compress a serial object with Packbits encoding. */
3908 char *tcpackencode(const char *ptr, int size, int *sp){
3909 assert(ptr && size >= 0 && sp);
3911 TCMALLOC(buf, size * 2 + 1);
3913 const char *end = ptr + size;
3916 const char *rp = ptr + 1;
3918 while(rp < end && step < 0x7f && *rp == *ptr){
3922 if(step <= 1 && rp < end){
3925 while(rp < end && step < 0x7f && *rp != *(rp - 1)){
3930 if(rp < end && *(rp - 1) == *rp){
3935 *sp = step == 1 ? 1 : -step;
3947 /* Decompress a serial object compressed with Packbits encoding. */
3948 char *tcpackdecode(const char *ptr, int size, int *sp){
3949 assert(ptr && size >= 0 && sp);
3950 int asiz = size * 3;
3952 TCMALLOC(buf, asiz + 1);
3954 const char *end = ptr + size;
3956 int step = abs(*ptr);
3957 if(wi + step >= asiz){
3958 asiz = asiz * 2 + step;
3959 TCREALLOC(buf, buf, asiz + 1);
3962 memset(buf + wi, *ptr, step);
3965 step = tclmin(step, end - ptr);
3966 memcpy(buf + wi, ptr, step);
3977 /* Compress a serial object with Deflate encoding. */
3978 char *tcdeflate(const char *ptr, int size, int *sp){
3980 if(!_tc_deflate) return NULL;
3981 return _tc_deflate(ptr, size, sp, _TCZMZLIB);
3985 /* Decompress a serial object compressed with Deflate encoding. */
3986 char *tcinflate(const char *ptr, int size, int *sp){
3987 assert(ptr && size >= 0);
3988 if(!_tc_inflate) return NULL;
3989 return _tc_inflate(ptr, size, sp, _TCZMZLIB);
3993 /* Compress a serial object with GZIP encoding. */
3994 char *tcgzipencode(const char *ptr, int size, int *sp){
3996 if(!_tc_deflate) return NULL;
3997 return _tc_deflate(ptr, size, sp, _TCZMGZIP);
4001 /* Decompress a serial object compressed with GZIP encoding. */
4002 char *tcgzipdecode(const char *ptr, int size, int *sp){
4003 assert(ptr && size >= 0);
4004 if(!_tc_inflate) return NULL;
4005 return _tc_inflate(ptr, size, sp, _TCZMGZIP);
4009 /* Get the CRC32 checksum of a serial object. */
4010 unsigned int tcgetcrc(const char *ptr, int size){
4011 assert(ptr && size >= 0);
4012 if(!_tc_getcrc) return 0;
4013 return _tc_getcrc(ptr, size);
4017 /* Encode an array of nonnegative integers with BER encoding. */
4018 char *tcberencode(const unsigned int *ary, int anum, int *sp){
4019 assert(ary && anum >= 0 && sp);
4021 TCMALLOC(buf, anum * (sizeof(int) + 1) + 1);
4023 for(int i = 0; i < anum; i++){
4024 unsigned int num = ary[i];
4027 } else if(num < (1 << 14)){
4028 *(wp++) = (num >> 7) | 0x80;
4029 *(wp++) = num & 0x7f;
4030 } else if(num < (1 << 21)){
4031 *(wp++) = (num >> 14) | 0x80;
4032 *(wp++) = ((num >> 7) & 0x7f) | 0x80;
4033 *(wp++) = num & 0x7f;
4034 } else if(num < (1 << 28)){
4035 *(wp++) = (num >> 21) | 0x80;
4036 *(wp++) = ((num >> 14) & 0x7f) | 0x80;
4037 *(wp++) = ((num >> 7) & 0x7f) | 0x80;
4038 *(wp++) = num & 0x7f;
4040 *(wp++) = (num >> 28) | 0x80;
4041 *(wp++) = ((num >> 21) & 0x7f) | 0x80;
4042 *(wp++) = ((num >> 14) & 0x7f) | 0x80;
4043 *(wp++) = ((num >> 7) & 0x7f) | 0x80;
4044 *(wp++) = num & 0x7f;
4052 /* Decode a serial object encoded with BER encoding. */
4053 unsigned int *tcberdecode(const char *ptr, int size, int *np){
4054 assert(ptr && size >= 0 && np);
4056 TCMALLOC(buf, size * sizeof(*buf) + 1);
4057 unsigned int *wp = buf;
4059 unsigned int num = 0;
4062 c = *(unsigned char *)ptr;
4063 num = num * 0x80 + (c & 0x7f);
4066 } while(c >= 0x80 && size > 0);
4074 /* Escape meta characters in a string with the entity references of XML. */
4075 char *tcxmlescape(const char *str){
4077 const char *rp = str;
4100 TCMALLOC(buf, bsiz + 1);
4102 while(*str != '\0'){
4105 memcpy(wp, "&", 5);
4109 memcpy(wp, "<", 4);
4113 memcpy(wp, ">", 4);
4117 memcpy(wp, """, 6);
4131 /* Unescape entity references in a string of XML. */
4132 char *tcxmlunescape(const char *str){
4135 TCMALLOC(buf, strlen(str) + 1);
4137 while(*str != '\0'){
4139 if(tcstrfwm(str, "&")){
4142 } else if(tcstrfwm(str, "<")){
4145 } else if(tcstrfwm(str, ">")){
4148 } else if(tcstrfwm(str, """)){
4163 /* Split an XML string into tags and text sections. */
4164 TCLIST *tcxmlbreak(const char *str){
4166 TCLIST *list = tclistnew();
4173 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
4175 } else if(!tag && str[i] == '<'){
4176 if(str[i+1] == '!' && str[i+2] == '-' && str[i+3] == '-'){
4177 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
4178 if((ep = strstr(str + i, "-->")) != NULL){
4179 TCLISTPUSH(list, str + i, ep - str - i + 3);
4183 } else if(str[i+1] == '!' && str[i+2] == '[' && tcstrifwm(str + i, "<![CDATA[")){
4184 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
4185 if((ep = strstr(str + i, "]]>")) != NULL){
4187 TCXSTR *xstr = tcxstrnew();
4188 while(str + i < ep){
4190 TCXSTRCAT(xstr, "&", 5);
4191 } else if(str[i] == '<'){
4192 TCXSTRCAT(xstr, "<", 4);
4193 } else if(str[i] == '>'){
4194 TCXSTRCAT(xstr, ">", 4);
4196 TCXSTRCAT(xstr, str + i, 1);
4200 if(TCXSTRSIZE(xstr) > 0) TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
4206 if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
4210 } else if(tag && str[i] == '>'){
4211 if(i > pv) TCLISTPUSH(list, str + pv, i - pv + 1);
4221 /* Get the map of attributes of an XML tag. */
4222 TCMAP *tcxmlattrs(const char *str){
4224 TCMAP *map = tcmapnew2(TCXMLATBNUM);
4225 const unsigned char *rp = (unsigned char *)str;
4226 while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){
4229 const unsigned char *key = rp;
4230 while(*rp > 0x20 && *rp != '/' && *rp != '>'){
4233 tcmapputkeep(map, "", 0, (char *)key, rp - key);
4235 while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){
4239 while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){
4242 int ksiz = rp - key;
4243 while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){
4246 const unsigned char *val;
4251 while(*rp != '\0' && *rp != '"'){
4255 } else if(*rp == '\''){
4258 while(*rp != '\0' && *rp != '\''){
4264 while(*rp > 0x20 && *rp != '"' && *rp != '\'' && *rp != '>'){
4269 if(*rp != '\0') rp++;
4272 TCMEMDUP(copy, val, vsiz);
4273 char *raw = tcxmlunescape(copy);
4274 tcmapputkeep(map, (char *)key, ksiz, raw, strlen(raw));
4284 /*************************************************************************************************
4285 * features for experts
4286 *************************************************************************************************/
4289 #define TCBSENCUNIT 8192 // unit size of TCBS encoding
4290 #define TCBWTCNTMIN 64 // minimum element number of counting sort
4291 #define TCBWTCNTLV 4 // maximum recursion level of counting sort
4292 #define TCBWTBUFNUM 16384 // number of elements of BWT buffer
4294 typedef struct { // type of structure for a BWT character
4295 int fchr; // character code of the first character
4296 int tchr; // character code of the last character
4300 /* private function prototypes */
4301 static void tcglobalmutexinit(void);
4302 static void tcglobalmutexdestroy(void);
4303 static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level);
4304 static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip);
4305 static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip);
4306 static void tcbwtsortchrcount(unsigned char *str, int len);
4307 static void tcbwtsortchrinsert(unsigned char *str, int len);
4308 static void tcbwtsortreccount(TCBWTREC *arrays, int anum);
4309 static void tcbwtsortrecinsert(TCBWTREC *array, int anum);
4310 static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr);
4311 static void tcmtfencode(char *ptr, int size);
4312 static void tcmtfdecode(char *ptr, int size);
4313 static int tcgammaencode(const char *ptr, int size, char *obuf);
4314 static int tcgammadecode(const char *ptr, int size, char *obuf);
4317 /* Show error message on the standard error output and exit. */
4318 void *tcmyfatal(const char *message){
4321 tcfatalfunc(message);
4323 fprintf(stderr, "fatal error: %s\n", message);
4330 /* Global mutex object. */
4331 static pthread_rwlock_t tcglobalmutex;
4332 static pthread_once_t tcglobalmutexonce = PTHREAD_ONCE_INIT;
4335 /* Lock the global mutex object. */
4336 bool tcglobalmutexlock(void){
4337 pthread_once(&tcglobalmutexonce, tcglobalmutexinit);
4338 return pthread_rwlock_wrlock(&tcglobalmutex) == 0;
4342 /* Lock the global mutex object by shared locking. */
4343 bool tcglobalmutexlockshared(void){
4344 pthread_once(&tcglobalmutexonce, tcglobalmutexinit);
4345 return pthread_rwlock_rdlock(&tcglobalmutex) == 0;
4349 /* Unlock the global mutex object. */
4350 bool tcglobalmutexunlock(void){
4351 return pthread_rwlock_unlock(&tcglobalmutex) == 0;
4355 /* Compress a serial object with TCBS encoding. */
4356 char *tcbsencode(const char *ptr, int size, int *sp){
4357 assert(ptr && size >= 0 && sp);
4359 TCMALLOC(result, (size * 7) / 3 + (size / TCBSENCUNIT + 1) * sizeof(uint16_t) +
4360 TCBSENCUNIT * 2 + 0x200);
4361 char *pv = result + size + 0x100;
4363 char *tp = pv + size + 0x100;
4364 const char *end = ptr + size;
4366 int usiz = tclmin(TCBSENCUNIT, end - ptr);
4367 memcpy(tp, ptr, usiz);
4368 memcpy(tp + usiz, ptr, usiz);
4372 const char *arrays[usiz+1];
4373 for(int i = 0; i < usiz; i++){
4376 const char *fp = arrays[0];
4377 if(usiz >= TCBWTCNTMIN){
4378 tcbwtsortstrcount(arrays, usiz, usiz, 0);
4379 } else if(usiz > 1){
4380 tcbwtsortstrinsert(arrays, usiz, usiz, 0);
4382 for(int i = 0; i < usiz; i++){
4383 int tidx = arrays[i] - fp;
4386 *(wp++) = ptr[usiz-1];
4388 *(wp++) = ptr[tidx-1];
4392 memcpy(sp, &idx, sizeof(idx));
4396 tcmtfencode(pv, size);
4397 int nsiz = tcgammaencode(pv, size, result);
4403 /* Decompress a serial object compressed with TCBS encoding. */
4404 char *tcbsdecode(const char *ptr, int size, int *sp){
4406 TCMALLOC(result, size * 9 + 0x200);
4407 char *wp = result + size + 0x100;
4408 int nsiz = tcgammadecode(ptr, size, wp);
4409 tcmtfdecode(wp, nsiz);
4412 const char *end = ptr + nsiz;
4415 memcpy(&idx, ptr, sizeof(idx));
4418 int usiz = tclmin(TCBSENCUNIT, end - ptr);
4419 if(idx >= usiz) idx = 0;
4421 memcpy(rbuf, ptr, usiz);
4422 if(usiz >= TCBWTCNTMIN){
4423 tcbwtsortchrcount((unsigned char *)rbuf, usiz);
4424 } else if(usiz > 0){
4425 tcbwtsortchrinsert((unsigned char *)rbuf, usiz);
4427 int fnums[0x100], tnums[0x100];
4428 memset(fnums, 0, sizeof(fnums));
4429 memset(tnums, 0, sizeof(tnums));
4430 TCBWTREC array[usiz+1];
4431 TCBWTREC *rp = array;
4432 for(int i = 0; i < usiz; i++){
4433 int fc = *(unsigned char *)(rbuf + i);
4434 rp->fchr = (fc << 23) + fnums[fc]++;
4435 int tc = *(unsigned char *)(ptr + i);
4436 rp->tchr = (tc << 23) + tnums[tc]++;
4439 unsigned int fchr = array[idx].fchr;
4440 if(usiz >= TCBWTCNTMIN){
4441 tcbwtsortreccount(array, usiz);
4442 } else if(usiz > 1){
4443 tcbwtsortrecinsert(array, usiz);
4445 for(int i = 0; i < usiz; i++){
4446 if(array[i].fchr == fchr){
4451 for(int i = 0; i < usiz; i++){
4452 *(wp++) = array[idx].fchr >> 23;
4453 idx = tcbwtsearchrec(array, usiz, array[idx].fchr);
4463 /* Encode a serial object with BWT encoding. */
4464 char *tcbwtencode(const char *ptr, int size, int *idxp){
4465 assert(ptr && size >= 0 && idxp);
4469 TCMEMDUP(rv, "", 0);
4473 TCMALLOC(result, size * 3 + 1);
4474 char *tp = result + size + 1;
4475 memcpy(tp, ptr, size);
4476 memcpy(tp + size, ptr, size);
4477 const char *abuf[TCBWTBUFNUM];
4478 const char **arrays = abuf;
4479 if(size > TCBWTBUFNUM) TCMALLOC(arrays, sizeof(*arrays) * size);
4480 for(int i = 0; i < size; i++){
4483 const char *fp = arrays[0];
4484 if(size >= TCBWTCNTMIN){
4485 tcbwtsortstrcount(arrays, size, size, -1);
4486 } else if(size > 1){
4487 tcbwtsortstrinsert(arrays, size, size, 0);
4489 for(int i = 0; i < size; i++){
4490 int idx = arrays[i] - fp;
4493 result[i] = ptr[size-1];
4495 result[i] = ptr[idx-1];
4498 if(arrays != abuf) free(arrays);
4499 result[size] = '\0';
4504 /* Decode a serial object encoded with BWT encoding. */
4505 char *tcbwtdecode(const char *ptr, int size, int idx){
4506 assert(ptr && size >= 0);
4507 if(size < 1 || idx < 0){
4509 TCMEMDUP(rv, "", 0);
4512 if(idx >= size) idx = 0;
4514 TCMALLOC(result, size + 1);
4515 memcpy(result, ptr, size);
4516 if(size >= TCBWTCNTMIN){
4517 tcbwtsortchrcount((unsigned char *)result, size);
4519 tcbwtsortchrinsert((unsigned char *)result, size);
4521 int fnums[0x100], tnums[0x100];
4522 memset(fnums, 0, sizeof(fnums));
4523 memset(tnums, 0, sizeof(tnums));
4524 TCBWTREC abuf[TCBWTBUFNUM];
4525 TCBWTREC *array = abuf;
4526 if(size > TCBWTBUFNUM) TCMALLOC(array, sizeof(*array) * size);
4527 TCBWTREC *rp = array;
4528 for(int i = 0; i < size; i++){
4529 int fc = *(unsigned char *)(result + i);
4530 rp->fchr = (fc << 23) + fnums[fc]++;
4531 int tc = *(unsigned char *)(ptr + i);
4532 rp->tchr = (tc << 23) + tnums[tc]++;
4535 unsigned int fchr = array[idx].fchr;
4536 if(size >= TCBWTCNTMIN){
4537 tcbwtsortreccount(array, size);
4538 } else if(size > 1){
4539 tcbwtsortrecinsert(array, size);
4541 for(int i = 0; i < size; i++){
4542 if(array[i].fchr == fchr){
4548 for(int i = 0; i < size; i++){
4549 *(wp++) = array[idx].fchr >> 23;
4550 idx = tcbwtsearchrec(array, size, array[idx].fchr);
4553 if(array != abuf) free(array);
4558 /* Initialize the global mutex object */
4559 static void tcglobalmutexinit(void){
4560 if(!TCUSEPTHREAD) memset(&tcglobalmutex, 0, sizeof(tcglobalmutex));
4561 if(pthread_rwlock_init(&tcglobalmutex, NULL) != 0) tcmyfatal("rwlock error");
4562 atexit(tcglobalmutexdestroy);
4566 /* Destroy the global mutex object */
4567 static void tcglobalmutexdestroy(void){
4568 pthread_rwlock_destroy(&tcglobalmutex);
4572 /* Sort BWT string arrays by dicrionary order by counting sort.
4573 `array' specifies an array of string arrays.
4574 `anum' specifies the number of the array.
4575 `len' specifies the size of each string.
4576 `level' specifies the level of recursion. */
4577 static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level){
4578 assert(arrays && anum >= 0 && len >= 0);
4579 const char *nbuf[TCBWTBUFNUM];
4580 const char **narrays = nbuf;
4581 if(anum > TCBWTBUFNUM) TCMALLOC(narrays, sizeof(*narrays) * anum);
4582 int count[0x100], accum[0x100];
4583 memset(count, 0, sizeof(count));
4584 int skip = level < 0 ? 0 : level;
4585 for(int i = 0; i < anum; i++){
4586 count[((unsigned char *)arrays[i])[skip]]++;
4588 memcpy(accum, count, sizeof(count));
4589 for(int i = 1; i < 0x100; i++){
4590 accum[i] = accum[i-1] + accum[i];
4592 for(int i = 0; i < anum; i++){
4593 narrays[--accum[((unsigned char *)arrays[i])[skip]]] = arrays[i];
4596 if(level >= 0 && level < TCBWTCNTLV){
4597 for(int i = 0; i < 0x100; i++){
4600 if(c >= TCBWTCNTMIN){
4601 tcbwtsortstrcount(narrays + off, c, len, level + 1);
4603 tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
4609 for(int i = 0; i < 0x100; i++){
4612 if(c >= TCBWTCNTMIN){
4613 tcbwtsortstrheap(narrays + off, c, len, skip + 1);
4615 tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
4621 memcpy(arrays, narrays, anum * sizeof(*narrays));
4622 if(narrays != nbuf) free(narrays);
4626 /* Sort BWT string arrays by dicrionary order by insertion sort.
4627 `array' specifies an array of string arrays.
4628 `anum' specifies the number of the array.
4629 `len' specifies the size of each string.
4630 `skip' specifies the number of skipped bytes. */
4631 static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip){
4632 assert(arrays && anum >= 0 && len >= 0);
4633 for(int i = 1; i < anum; i++){
4635 const unsigned char *ap = (unsigned char *)arrays[i-1];
4636 const unsigned char *bp = (unsigned char *)arrays[i];
4637 for(int j = skip; j < len; j++){
4639 cmp = ap[j] - bp[j];
4644 const char *swap = arrays[i];
4646 for(j = i; j > 0; j--){
4648 const unsigned char *ap = (unsigned char *)arrays[j-1];
4649 const unsigned char *bp = (unsigned char *)swap;
4650 for(int k = skip; k < len; k++){
4652 cmp = ap[k] - bp[k];
4657 arrays[j] = arrays[j-1];
4665 /* Sort BWT string arrays by dicrionary order by heap sort.
4666 `array' specifies an array of string arrays.
4667 `anum' specifies the number of the array.
4668 `len' specifies the size of each string.
4669 `skip' specifies the number of skipped bytes. */
4670 static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip){
4671 assert(arrays && anum >= 0 && len >= 0);
4673 int bottom = (anum >> 1) + 1;
4682 const unsigned char *ap = (unsigned char *)arrays[i+1];
4683 const unsigned char *bp = (unsigned char *)arrays[i];
4684 for(int j = skip; j < len; j++){
4686 cmp = ap[j] - bp[j];
4693 const unsigned char *ap = (unsigned char *)arrays[mybot];
4694 const unsigned char *bp = (unsigned char *)arrays[i];
4695 for(int j = skip; j < len; j++){
4697 cmp = ap[j] - bp[j];
4702 const char *swap = arrays[mybot];
4703 arrays[mybot] = arrays[i];
4710 const char *swap = arrays[0];
4711 arrays[0] = arrays[top];
4719 const unsigned char *ap = (unsigned char *)arrays[i+1];
4720 const unsigned char *bp = (unsigned char *)arrays[i];
4721 for(int j = 0; j < len; j++){
4723 cmp = ap[j] - bp[j];
4730 const unsigned char *ap = (unsigned char *)arrays[mybot];
4731 const unsigned char *bp = (unsigned char *)arrays[i];
4732 for(int j = 0; j < len; j++){
4734 cmp = ap[j] - bp[j];
4739 swap = arrays[mybot];
4740 arrays[mybot] = arrays[i];
4749 /* Sort BWT characters by code number by counting sort.
4750 `str' specifies a string.
4751 `len' specifies the length of the string. */
4752 static void tcbwtsortchrcount(unsigned char *str, int len){
4753 assert(str && len >= 0);
4755 memset(cnt, 0, sizeof(cnt));
4756 for(int i = 0; i < len; i++){
4760 for(int i = 0; i < 0x100; i++){
4761 memset(str + pos, i, cnt[i]);
4767 /* Sort BWT characters by code number by insertion sort.
4768 `str' specifies a string.
4769 `len' specifies the length of the string. */
4770 static void tcbwtsortchrinsert(unsigned char *str, int len){
4771 assert(str && len >= 0);
4772 for(int i = 1; i < len; i++){
4773 if(str[i-1] - str[i] > 0){
4774 unsigned char swap = str[i];
4776 for(j = i; j > 0; j--){
4777 if(str[j-1] - swap < 0) break;
4786 /* Sort BWT records by code number by counting sort.
4787 `array' specifies an array of records.
4788 `anum' specifies the number of the array. */
4789 static void tcbwtsortreccount(TCBWTREC *array, int anum){
4790 assert(array && anum >= 0);
4791 TCBWTREC nbuf[TCBWTBUFNUM];
4792 TCBWTREC *narray = nbuf;
4793 if(anum > TCBWTBUFNUM) TCMALLOC(narray, sizeof(*narray) * anum);
4794 int count[0x100], accum[0x100];
4795 memset(count, 0, sizeof(count));
4796 for(int i = 0; i < anum; i++){
4797 count[array[i].tchr>>23]++;
4799 memcpy(accum, count, sizeof(count));
4800 for(int i = 1; i < 0x100; i++){
4801 accum[i] = accum[i-1] + accum[i];
4803 for(int i = 0; i < 0x100; i++){
4804 accum[i] -= count[i];
4806 for(int i = 0; i < anum; i++){
4807 narray[accum[array[i].tchr>>23]++] = array[i];
4809 memcpy(array, narray, anum * sizeof(*narray));
4810 if(narray != nbuf) free(narray);
4814 /* Sort BWT records by code number by insertion sort.
4815 `array' specifies an array of records..
4816 `anum' specifies the number of the array. */
4817 static void tcbwtsortrecinsert(TCBWTREC *array, int anum){
4818 assert(array && anum >= 0);
4819 for(int i = 1; i < anum; i++){
4820 if(array[i-1].tchr - array[i].tchr > 0){
4821 TCBWTREC swap = array[i];
4823 for(j = i; j > 0; j--){
4824 if(array[j-1].tchr - swap.tchr < 0) break;
4825 array[j] = array[j-1];
4833 /* Search the element of BWT records.
4834 `array' specifies an array of records.
4835 `anum' specifies the number of the array.
4836 `tchr' specifies the last code number. */
4837 static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr){
4838 assert(array && anum >= 0);
4843 mid = (bottom + top) >> 1;
4844 if(array[mid].tchr == tchr){
4846 } else if(array[mid].tchr < tchr){
4848 if(bottom >= anum) break;
4852 } while(bottom <= top);
4857 /* Initialization table for MTF encoder. */
4858 const unsigned char tcmtftable[] = {
4859 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
4860 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
4861 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
4862 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
4863 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
4864 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
4865 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
4866 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
4867 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
4868 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
4869 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
4870 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
4871 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
4872 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
4873 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
4874 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
4878 /* Encode a region with MTF encoding.
4879 `ptr' specifies the pointer to the region.
4880 `size' specifies the size of the region. */
4881 static void tcmtfencode(char *ptr, int size){
4882 unsigned char table1[0x100], table2[0x100], *table, *another;
4883 assert(ptr && size >= 0);
4884 memcpy(table1, tcmtftable, sizeof(tcmtftable));
4887 const char *end = ptr + size;
4890 unsigned char c = *ptr;
4891 unsigned char *tp = table;
4892 unsigned char *tend = table + 0x100;
4893 while(tp < tend && *tp != c){
4896 int idx = tp - table;
4899 memcpy(another, &c, 1);
4900 memcpy(another + 1, table, idx);
4901 memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
4902 unsigned char *swap = table;
4911 /* Decode a region compressed with MTF encoding.
4912 `ptr' specifies the pointer to the region.
4913 `size' specifies the size of the region. */
4914 static void tcmtfdecode(char *ptr, int size){
4915 assert(ptr && size >= 0);
4916 unsigned char table1[0x100], table2[0x100], *table, *another;
4917 assert(ptr && size >= 0);
4918 memcpy(table1, tcmtftable, sizeof(tcmtftable));
4921 const char *end = ptr + size;
4924 int idx = *(unsigned char *)ptr;
4925 unsigned char c = table[idx];
4928 memcpy(another, &c, 1);
4929 memcpy(another + 1, table, idx);
4930 memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
4931 unsigned char *swap = table;
4940 /* Encode a region with Elias gamma encoding.
4941 `ptr' specifies the pointer to the region.
4942 `size' specifies the size of the region.
4943 `ptr' specifies the pointer to the output buffer. */
4944 static int tcgammaencode(const char *ptr, int size, char *obuf){
4945 assert(ptr && size >= 0 && obuf);
4947 TCBITSTRMINITW(strm, obuf);
4948 const char *end = ptr + size;
4950 unsigned int c = *(unsigned char *)ptr;
4952 TCBITSTRMCAT(strm, 1);
4956 while(plen > 0 && !(c & (1 << plen))){
4961 TCBITSTRMCAT(strm, 0);
4964 int sign = (c & (1 << plen)) > 0;
4965 TCBITSTRMCAT(strm, sign);
4971 TCBITSTRMSETEND(strm);
4972 return TCBITSTRMSIZE(strm);
4976 /* Decode a region compressed with Elias gamma encoding.
4977 `ptr' specifies the pointer to the region.
4978 `size' specifies the size of the region.
4979 `ptr' specifies the pointer to the output buffer. */
4980 static int tcgammadecode(const char *ptr, int size, char *obuf){
4981 assert(ptr && size >= 0 && obuf);
4984 TCBITSTRMINITR(strm, ptr, size);
4985 int bnum = TCBITSTRMNUM(strm);
4988 TCBITSTRMREAD(strm, sign);
4995 TCBITSTRMREAD(strm, sign);
5001 while(bnum > 0 && plen-- > 0){
5002 TCBITSTRMREAD(strm, sign);
5004 c = (c << 1) + (sign > 0);