]> git.ipfire.org Git - thirdparty/squid.git/blob - lib/util.c
Merged from trunk.
[thirdparty/squid.git] / lib / util.c
1
2 /*
3 * $Id: util.c,v 1.98 2007/12/06 02:37:15 amosjeffries Exp $
4 *
5 * DEBUG:
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #define _etext etext
37
38 #include "config.h"
39 #include "profiling.h"
40
41 #if HAVE_STDIO_H
42 #include <stdio.h>
43 #endif
44 #if HAVE_STDLIB_H
45 #include <stdlib.h>
46 #endif
47 #if HAVE_STRING_H
48 #include <string.h>
49 #endif
50 #if HAVE_CTYPE_H
51 #include <ctype.h>
52 #endif
53 #if HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56 #if HAVE_GNUMALLLOC_H
57 #include <gnumalloc.h>
58 #elif HAVE_MALLOC_H
59 #include <malloc.h>
60 #endif
61 #if HAVE_ERRNO_H
62 #include <errno.h>
63 #endif
64 #if HAVE_MATH_H
65 #include <math.h>
66 #endif
67 #if HAVE_ASSERT_H
68 #include <assert.h>
69 #endif
70
71 #include "util.h"
72
73 static void default_failure_notify(const char *);
74
75 void (*failure_notify) (const char *) = default_failure_notify;
76 static char msg[128];
77
78 #if MEM_GEN_TRACE
79
80 static FILE *tracefp = NULL;
81
82 void
83 log_trace_init(char *fn)
84 {
85 tracefp = fopen(fn, "a+");
86
87 if (!tracefp) {
88 perror("log_trace_init");
89 exit(1);
90 }
91 }
92
93 void
94 log_trace_done()
95 {
96 fclose(tracefp);
97 tracefp = NULL;
98 }
99
100 #endif
101
102 #if XMALLOC_STATISTICS
103 #define DBG_MAXSIZE (1024*1024)
104 #define DBG_SPLIT (256) /* mallocs below this value are tracked with DBG_GRAIN_SM precision instead of DBG_GRAIN */
105 #define DBG_GRAIN (16)
106 #define DBG_GRAIN_SM (4)
107 #define DBG_OFFSET (DBG_SPLIT/DBG_GRAIN_SM - DBG_SPLIT/DBG_GRAIN )
108 #define DBG_MAXINDEX (DBG_MAXSIZE/DBG_GRAIN + DBG_OFFSET)
109 // #define DBG_INDEX(sz) (sz<DBG_MAXSIZE?(sz+DBG_GRAIN-1)/DBG_GRAIN:DBG_MAXINDEX)
110 static int malloc_sizes[DBG_MAXINDEX + 1];
111 static int malloc_histo[DBG_MAXINDEX + 1];
112 static int dbg_stat_init = 0;
113
114 static int
115 DBG_INDEX(int sz)
116 {
117 if (sz >= DBG_MAXSIZE)
118 return DBG_MAXINDEX;
119
120 if (sz <= DBG_SPLIT)
121 return (sz + DBG_GRAIN_SM - 1) / DBG_GRAIN_SM;
122
123 return (sz + DBG_GRAIN - 1) / DBG_GRAIN + DBG_OFFSET;
124 }
125
126 static void
127 stat_init(void)
128 {
129 int i;
130
131 for (i = 0; i <= DBG_MAXINDEX; i++)
132 malloc_sizes[i] = malloc_histo[i] = 0;
133
134 dbg_stat_init = 1;
135 }
136
137 static int
138 malloc_stat(int sz)
139 {
140 if (!dbg_stat_init)
141 stat_init();
142
143 return malloc_sizes[DBG_INDEX(sz)] += 1;
144 }
145
146 void
147 malloc_statistics(void (*func) (int, int, int, void *), void *data)
148 {
149 int i;
150
151 for (i = 0; i <= DBG_SPLIT; i += DBG_GRAIN_SM)
152 func(i, malloc_sizes[DBG_INDEX(i)], malloc_histo[DBG_INDEX(i)], data);
153
154 i -= DBG_GRAIN_SM;
155
156 for (i = i; i <= DBG_MAXSIZE; i += DBG_GRAIN)
157 func(i, malloc_sizes[DBG_INDEX(i)], malloc_histo[DBG_INDEX(i)], data);
158
159 xmemcpy(&malloc_histo, &malloc_sizes, sizeof(malloc_sizes));
160 }
161
162 #endif /* XMALLOC_STATISTICS */
163
164
165
166 #if XMALLOC_TRACE
167 char *xmalloc_file = "";
168 int xmalloc_line = 0;
169 char *xmalloc_func = "";
170 static int xmalloc_count = 0;
171 int xmalloc_trace = 0; /* Enable with -m option */
172 size_t xmalloc_total = 0;
173 #undef xmalloc
174 #undef xfree
175 #undef xxfree
176 #undef xrealloc
177 #undef xcalloc
178 #undef xstrdup
179 #endif
180
181 #if XMALLOC_DEBUG
182 #define DBG_ARRY_SZ (1<<11)
183 #define DBG_ARRY_BKTS (1<<8)
184 static void *(*malloc_ptrs)[DBG_ARRY_SZ];
185 static int malloc_size[DBG_ARRY_BKTS][DBG_ARRY_SZ];
186 #if XMALLOC_TRACE
187 static char *malloc_file[DBG_ARRY_BKTS][DBG_ARRY_SZ];
188 static short malloc_line[DBG_ARRY_BKTS][DBG_ARRY_SZ];
189 static int malloc_count[DBG_ARRY_BKTS][DBG_ARRY_SZ];
190 #endif
191 static int dbg_initd = 0;
192
193 #define DBG_HASH_BUCKET(ptr) (((((int)ptr)>>4)+(((int)ptr)>>12)+(((int)ptr)>>20))&0xFF)
194
195 static void
196 check_init(void)
197 {
198 int B = 0, I = 0;
199 /* calloc the ptrs so that we don't see them when hunting lost memory */
200 malloc_ptrs = calloc(DBG_ARRY_BKTS, sizeof(*malloc_ptrs));
201
202 for (B = 0; B < DBG_ARRY_BKTS; B++) {
203 for (I = 0; I < DBG_ARRY_SZ; I++) {
204 malloc_ptrs[B][I] = NULL;
205 malloc_size[B][I] = 0;
206 #if XMALLOC_TRACE
207
208 malloc_file[B][I] = NULL;
209 malloc_line[B][I] = 0;
210 malloc_count[B][I] = 0;
211 #endif
212
213 }
214 }
215
216 dbg_initd = 1;
217 }
218
219 static void
220 check_free(void *s)
221 {
222 int B, I;
223 B = DBG_HASH_BUCKET(s);
224
225 for (I = 0; I < DBG_ARRY_SZ; I++) {
226 if (malloc_ptrs[B][I] != s)
227 continue;
228
229 malloc_ptrs[B][I] = NULL;
230
231 malloc_size[B][I] = 0;
232
233 #if XMALLOC_TRACE
234
235 malloc_file[B][I] = NULL;
236
237 malloc_line[B][I] = 0;
238
239 malloc_count[B][I] = 0;
240
241 #endif
242
243 break;
244 }
245
246 if (I == DBG_ARRY_SZ) {
247 snprintf(msg, 128, "xfree: ERROR: s=%p not found!", s);
248 (*failure_notify) (msg);
249 }
250 }
251
252 static void
253 check_malloc(void *p, size_t sz)
254 {
255 void *P, *Q;
256 int B, I;
257
258 if (!dbg_initd)
259 check_init();
260
261 B = DBG_HASH_BUCKET(p);
262
263 for (I = 0; I < DBG_ARRY_SZ; I++) {
264 if (!(P = malloc_ptrs[B][I]))
265 continue;
266
267 Q = P + malloc_size[B][I];
268
269 if (P <= p && p < Q) {
270 snprintf(msg, 128, "xmalloc: ERROR: p=%p falls in P=%p+%d",
271 p, P, malloc_size[B][I]);
272 (*failure_notify) (msg);
273 }
274 }
275
276 for (I = 0; I < DBG_ARRY_SZ; I++) {
277 if (malloc_ptrs[B][I])
278 continue;
279
280 malloc_ptrs[B][I] = p;
281
282 malloc_size[B][I] = (int) sz;
283
284 #if XMALLOC_TRACE
285
286 malloc_file[B][I] = xmalloc_file;
287
288 malloc_line[B][I] = xmalloc_line;
289
290 malloc_count[B][I] = xmalloc_count;
291
292 #endif
293
294 break;
295 }
296
297 if (I == DBG_ARRY_SZ)
298 (*failure_notify) ("xmalloc: debug out of array space!");
299 }
300
301 #endif
302
303 #if XMALLOC_TRACE && !HAVE_MALLOCBLKSIZE
304 size_t
305 xmallocblksize(void *p)
306 {
307 int B, I;
308 B = DBG_HASH_BUCKET(p);
309
310 for (I = 0; I < DBG_ARRY_SZ; I++) {
311 if (malloc_ptrs[B][I] == p)
312 return malloc_size[B][I];
313 }
314
315 return 0;
316 }
317
318 #endif
319
320 #ifdef XMALLOC_TRACE
321 static char *
322 malloc_file_name(void *p)
323 {
324 int B, I;
325 B = DBG_HASH_BUCKET(p);
326
327 for (I = 0; I < DBG_ARRY_SZ; I++) {
328 if (malloc_ptrs[B][I] == p)
329 return malloc_file[B][I];
330 }
331
332 return 0;
333 }
334
335 int
336 malloc_line_number(void *p)
337 {
338 int B, I;
339 B = DBG_HASH_BUCKET(p);
340
341 for (I = 0; I < DBG_ARRY_SZ; I++) {
342 if (malloc_ptrs[B][I] == p)
343 return malloc_line[B][I];
344 }
345
346 return 0;
347 }
348
349 int
350 malloc_number(void *p)
351 {
352 int B, I;
353 B = DBG_HASH_BUCKET(p);
354
355 for (I = 0; I < DBG_ARRY_SZ; I++) {
356 if (malloc_ptrs[B][I] == p)
357 return malloc_count[B][I];
358 }
359
360 return 0;
361 }
362
363 static void
364 xmalloc_show_trace(void *p, int sign)
365 {
366 int statMemoryAccounted();
367 static size_t last_total = 0, last_accounted = 0, last_mallinfo = 0;
368 size_t accounted = statMemoryAccounted();
369 size_t mi = 0;
370 size_t sz;
371 #if HAVE_MALLINFO
372
373 struct mallinfo mp = mallinfo();
374 mi = mp.uordblks + mp.usmblks + mp.hblkhd;
375 #endif
376
377 sz = xmallocblksize(p) * sign;
378 xmalloc_total += sz;
379 xmalloc_count += sign > 0;
380
381 if (xmalloc_trace) {
382 fprintf(stderr, "%c%8p size=%5d/%d acc=%5d/%d mallinfo=%5d/%d %s:%d %s",
383 sign > 0 ? '+' : '-', p,
384 (int) xmalloc_total - last_total, (int) xmalloc_total,
385 (int) accounted - last_accounted, (int) accounted,
386 (int) mi - last_mallinfo, (int) mi,
387 xmalloc_file, xmalloc_line, xmalloc_func);
388
389 if (sign < 0)
390 fprintf(stderr, " (%d %s:%d)\n", malloc_number(p), malloc_file_name(p), malloc_line_number(p));
391 else
392 fprintf(stderr, " %d\n", xmalloc_count);
393 }
394
395 last_total = xmalloc_total;
396 last_accounted = accounted;
397 last_mallinfo = mi;
398 }
399
400 short malloc_refs[DBG_ARRY_BKTS][DBG_ARRY_SZ];
401 #define XMALLOC_LEAK_ALIGN (4)
402 static void
403 xmalloc_scan_region(void *start, int size, int depth)
404 {
405 int B, I;
406 char *ptr = start;
407 char *end = ptr + size - XMALLOC_LEAK_ALIGN;
408 static int sum = 0;
409
410 while (ptr <= end) {
411 void *p = *(void **) ptr;
412
413 if (p && p != start) {
414 B = DBG_HASH_BUCKET(p);
415
416 for (I = 0; I < DBG_ARRY_SZ; I++) {
417 if (malloc_ptrs[B][I] == p) {
418 if (!malloc_refs[B][I]++) {
419 /* A new reference */
420 fprintf(stderr, "%*s%p %s:%d size %d allocation %d\n",
421 depth, "",
422 malloc_ptrs[B][I], malloc_file[B][I],
423 malloc_line[B][I], malloc_size[B][I],
424 malloc_count[B][I]);
425 sum += malloc_size[B][I];
426 xmalloc_scan_region(malloc_ptrs[B][I], malloc_size[B][I], depth + 1);
427
428 if (depth == 0) {
429 if (sum != malloc_size[B][I])
430 fprintf(stderr, "=== %d bytes\n", sum);
431
432 sum = 0;
433 }
434
435 #if XMALLOC_SHOW_ALL_REFERENCES
436
437 } else {
438 /* We have already scanned this pointer... */
439 fprintf(stderr, "%*s%p %s:%d size %d allocation %d ... (%d)\n",
440 depth * 2, "",
441 malloc_ptrs[B][I], malloc_file[B][I],
442 malloc_line[B][I], malloc_size[B][I],
443 malloc_count[B][I], malloc_refs[B][I]);
444 #endif
445
446 }
447 }
448 }
449 }
450
451 ptr += XMALLOC_LEAK_ALIGN;
452 }
453 }
454
455 void
456 xmalloc_find_leaks(void)
457 {
458 int B, I;
459 int leak_sum = 0;
460
461 extern void _etext;
462 fprintf(stderr, "----- Memory map ----\n");
463 xmalloc_scan_region(&_etext, (void *) sbrk(0) - (void *) &_etext, 0);
464
465 for (B = 0; B < DBG_ARRY_BKTS; B++) {
466 for (I = 0; I < DBG_ARRY_SZ; I++) {
467 if (malloc_ptrs[B][I] && malloc_refs[B][I] == 0) {
468 /* Found a leak... */
469 fprintf(stderr, "Leak found: %p", malloc_ptrs[B][I]);
470 fprintf(stderr, " %s", malloc_file[B][I]);
471 fprintf(stderr, ":%d", malloc_line[B][I]);
472 fprintf(stderr, " size %d", malloc_size[B][I]);
473 fprintf(stderr, " allocation %d\n", malloc_count[B][I]);
474 leak_sum += malloc_size[B][I];
475 }
476 }
477 }
478
479 if (leak_sum) {
480 fprintf(stderr, "Total leaked memory: %d\n", leak_sum);
481 } else {
482 fprintf(stderr, "No memory leaks detected\n");
483 }
484
485 fprintf(stderr, "----------------------\n");
486 }
487
488 #endif /* XMALLOC_TRACE */
489
490 /*
491 * xmalloc() - same as malloc(3). Used for portability.
492 * Never returns NULL; fatal on error.
493 */
494 void *
495 xmalloc(size_t sz)
496 {
497 void *p;
498
499 PROF_start(xmalloc);
500
501 if (sz < 1)
502 sz = 1;
503
504 PROF_start(malloc);
505
506 p = malloc(sz);
507
508 PROF_stop(malloc);
509
510 if (p == NULL) {
511 if (failure_notify) {
512 snprintf(msg, 128, "xmalloc: Unable to allocate %d bytes!\n",
513 (int) sz);
514 (*failure_notify) (msg);
515 } else {
516 perror("malloc");
517 }
518
519 exit(1);
520 }
521
522 #if XMALLOC_DEBUG
523 check_malloc(p, sz);
524
525 #endif
526 #if XMALLOC_STATISTICS
527
528 malloc_stat(sz);
529
530 #endif
531 #if XMALLOC_TRACE
532
533 xmalloc_show_trace(p, 1);
534
535 #endif
536 #if MEM_GEN_TRACE
537
538 if (tracefp)
539 fprintf(tracefp, "m:%d:%p\n", sz, p);
540
541 #endif
542
543 PROF_stop(xmalloc);
544
545 return (p);
546 }
547
548 /*
549 * xfree() - same as free(3). Will not call free(3) if s == NULL.
550 */
551 void
552 xfree(void *s)
553 {
554 PROF_start(xfree);
555 #if XMALLOC_TRACE
556
557 xmalloc_show_trace(s, -1);
558 #endif
559
560 #if XMALLOC_DEBUG
561
562 if (s != NULL)
563 check_free(s);
564
565 #endif
566
567 if (s != NULL)
568 free(s);
569
570 #if MEM_GEN_TRACE
571
572 if (tracefp && s)
573 fprintf(tracefp, "f:%p\n", s);
574
575 #endif
576
577 PROF_stop(xfree);
578 }
579
580 /* xxfree() - like xfree(), but we already know s != NULL */
581 void
582 xxfree(const void *s_const)
583 {
584 void *s = (void *) s_const;
585 PROF_start(xxfree);
586 #if XMALLOC_TRACE
587
588 xmalloc_show_trace(s, -1);
589 #endif
590 #if XMALLOC_DEBUG
591
592 check_free(s);
593 #endif
594
595 free(s);
596 #if MEM_GEN_TRACE
597
598 if (tracefp && s)
599 fprintf(tracefp, "f:%p\n", s);
600
601 #endif
602
603 PROF_stop(xxfree);
604 }
605
606 /*
607 * xrealloc() - same as realloc(3). Used for portability.
608 * Never returns NULL; fatal on error.
609 */
610 void *
611 xrealloc(void *s, size_t sz)
612 {
613 void *p;
614
615 PROF_start(xrealloc);
616 #if XMALLOC_TRACE
617
618 xmalloc_show_trace(s, -1);
619 #endif
620
621 if (sz < 1)
622 sz = 1;
623
624 #if XMALLOC_DEBUG
625
626 if (s != NULL)
627 check_free(s);
628
629 #endif
630
631 if ((p = realloc(s, sz)) == NULL) {
632 if (failure_notify) {
633 snprintf(msg, 128, "xrealloc: Unable to reallocate %d bytes!\n",
634 (int) sz);
635 (*failure_notify) (msg);
636 } else {
637 perror("realloc");
638 }
639
640 exit(1);
641 }
642
643 #if XMALLOC_DEBUG
644 check_malloc(p, sz);
645
646 #endif
647 #if XMALLOC_STATISTICS
648
649 malloc_stat(sz);
650
651 #endif
652 #if XMALLOC_TRACE
653
654 xmalloc_show_trace(p, 1);
655
656 #endif
657 #if MEM_GEN_TRACE
658
659 if (tracefp) /* new ptr, old ptr, new size */
660 fprintf(tracefp, "r:%p:%p:%d\n", p, s, sz);
661
662 #endif
663
664 PROF_stop(xrealloc);
665
666 return (p);
667 }
668
669 /*
670 * xcalloc() - same as calloc(3). Used for portability.
671 * Never returns NULL; fatal on error.
672 */
673 void *
674 xcalloc(size_t n, size_t sz)
675 {
676 void *p;
677
678 PROF_start(xcalloc);
679
680 if (n < 1)
681 n = 1;
682
683 if (sz < 1)
684 sz = 1;
685
686 PROF_start(calloc);
687
688 p = calloc(n, sz);
689
690 PROF_stop(calloc);
691
692 if (p == NULL) {
693 if (failure_notify) {
694 snprintf(msg, 128, "xcalloc: Unable to allocate %u blocks of %u bytes!\n",
695 (unsigned int) n, (unsigned int) sz);
696 (*failure_notify) (msg);
697 } else {
698 perror("xcalloc");
699 }
700
701 exit(1);
702 }
703
704 #if XMALLOC_DEBUG
705 check_malloc(p, sz * n);
706
707 #endif
708 #if XMALLOC_STATISTICS
709
710 malloc_stat(sz * n);
711
712 #endif
713 #if XMALLOC_TRACE
714
715 xmalloc_show_trace(p, 1);
716
717 #endif
718 #if MEM_GEN_TRACE
719
720 if (tracefp)
721 fprintf(tracefp, "c:%u:%u:%p\n", (unsigned int) n, (unsigned int) sz, p);
722
723 #endif
724
725 PROF_stop(xcalloc);
726
727 return (p);
728 }
729
730 /*
731 * xstrdup() - same as strdup(3). Used for portability.
732 * Never returns NULL; fatal on error.
733 */
734 char *
735 xstrdup(const char *s)
736 {
737 size_t sz;
738 void *p;
739 PROF_start(xstrdup);
740
741 if (s == NULL) {
742 if (failure_notify) {
743 (*failure_notify) ("xstrdup: tried to dup a NULL pointer!\n");
744 } else {
745 fprintf(stderr, "xstrdup: tried to dup a NULL pointer!\n");
746 }
747
748 exit(1);
749 }
750
751 /* copy string, including terminating character */
752 sz = strlen(s) + 1;
753
754 p = memcpy(xmalloc(sz), s, sz);
755
756 PROF_stop(xstrdup);
757
758 return p;
759 }
760
761 /*
762 * xstrndup() - string dup with length limit.
763 */
764 char *
765 xstrndup(const char *s, size_t n)
766 {
767 size_t sz;
768 void *p;
769 PROF_start(xstrndup);
770 assert(s != NULL);
771 assert(n);
772 sz = strlen(s) + 1;
773
774 if (sz > n)
775 sz = n;
776
777 p = xstrncpy(xmalloc(sz), s, sz);
778
779 PROF_stop(xstrndup);
780
781 return p;
782 }
783
784 /*
785 * xstrerror() - strerror() wrapper
786 */
787 const char *
788 xstrerr(int error)
789 {
790 static char xstrerror_buf[BUFSIZ];
791 const char *errmsg;
792
793 errmsg = strerror(error);
794
795 if (!errmsg || !*errmsg)
796 errmsg = "Unknown error";
797
798 snprintf(xstrerror_buf, BUFSIZ, "(%d) %s", error, errmsg);
799
800 return xstrerror_buf;
801 }
802
803 const char *
804 xstrerror(void)
805 {
806 return xstrerr(errno);
807 }
808
809 void
810 Tolower(char *q)
811 {
812 char *s = q;
813
814 while (*s) {
815 *s = xtolower(*s);
816 s++;
817 }
818 }
819
820 int
821
822 tvSubMsec(struct timeval t1, struct timeval t2)
823 {
824 return (t2.tv_sec - t1.tv_sec) * 1000 +
825 (t2.tv_usec - t1.tv_usec) / 1000;
826 }
827
828 int
829
830 tvSubUsec(struct timeval t1, struct timeval t2)
831 {
832 return (t2.tv_sec - t1.tv_sec) * 1000000 +
833 (t2.tv_usec - t1.tv_usec);
834 }
835
836 double
837
838 tvSubDsec(struct timeval t1, struct timeval t2)
839 {
840 return (double) (t2.tv_sec - t1.tv_sec) +
841 (double) (t2.tv_usec - t1.tv_usec) / 1000000.0;
842 }
843
844 /*
845 * xstrncpy() - similar to strncpy(3) but terminates string
846 * always with '\0' if (n != 0 and dst != NULL),
847 * and doesn't do padding
848 */
849 char *
850 xstrncpy(char *dst, const char *src, size_t n)
851 {
852 char *r = dst;
853 PROF_start(xstrncpy);
854
855 if (!n || !dst)
856 return dst;
857
858 if (src)
859 while (--n != 0 && *src != '\0')
860 *dst++ = *src++;
861
862 *dst = '\0';
863
864 PROF_stop(xstrncpy);
865
866 return r;
867 }
868
869 /* returns the number of leading white spaces in str; handy in skipping ws */
870 size_t
871 xcountws(const char *str)
872 {
873 size_t count = 0;
874 PROF_start(xcountws);
875
876 if (str) {
877 while (xisspace(*str)) {
878 str++;
879 count++;
880 }
881 }
882
883 PROF_stop(xcountws);
884 return count;
885 }
886
887 /* somewhat safer calculation of %s */
888 double
889 xpercent(double part, double whole)
890 {
891 return xdiv(100 * part, whole);
892 }
893
894 int
895 xpercentInt(double part, double whole)
896 {
897 #if HAVE_RINT
898 return (int) rint(xpercent(part, whole));
899 #else
900 /* SCO 3.2v4.2 doesn't have rint() -- mauri@mbp.ee */
901 return (int) floor(xpercent(part, whole) + 0.5);
902 #endif
903 }
904
905 /* somewhat safer division */
906 double
907 xdiv(double nom, double denom)
908 {
909 return (denom != 0.0) ? nom / denom : -1.0;
910 }
911
912 /* integer to string */
913 const char *
914 xitoa(int num)
915 {
916 static char buf[24]; /* 2^64 = 18446744073709551616 */
917 snprintf(buf, sizeof(buf), "%d", num);
918 return buf;
919 }
920
921 /* int64_t to string */
922 const char *
923 xint64toa(int64_t num)
924 {
925 static char buf[24]; /* 2^64 = 18446744073709551616 */
926 snprintf(buf, sizeof(buf), "%" PRId64, num);
927 return buf;
928 }
929
930 /* A default failure notifier when the main program hasn't installed any */
931 void
932 default_failure_notify(const char *message)
933 {
934 if(write(2, message, strlen(message))) {}
935 if(write(2, "\n", 1)) {}
936 abort();
937 }
938
939 void
940 gb_flush(gb_t * g)
941 {
942 g->gb += (g->bytes >> 30);
943 g->bytes &= (1 << 30) - 1;
944 }
945
946 double
947 gb_to_double(const gb_t * g)
948 {
949 return ((double) g->gb) * ((double) (1 << 30)) + ((double) g->bytes);
950 }
951
952 const char *
953 double_to_str(char *buf, int buf_size, double value)
954 {
955 /* select format */
956
957 if (value < 1e9)
958 snprintf(buf, buf_size, "%.2f MB", value / 1e6);
959 else if (value < 1e12)
960 snprintf(buf, buf_size, "%.3f GB", value / 1e9);
961 else
962 snprintf(buf, buf_size, "%.4f TB", value / 1e12);
963
964 return buf;
965 }
966
967 const char *
968 gb_to_str(const gb_t * g)
969 {
970 /*
971 * it is often convenient to call gb_to_str several times for _one_ printf
972 */
973 #define max_cc_calls 5
974 typedef char GbBuf[32];
975 static GbBuf bufs[max_cc_calls];
976 static int call_id = 0;
977 double value = gb_to_double(g);
978 char *buf = bufs[call_id++];
979
980 if (call_id >= max_cc_calls)
981 call_id = 0;
982
983 /* select format */
984 if (value < 1e9)
985 snprintf(buf, sizeof(GbBuf), "%.2f MB", value / 1e6);
986 else if (value < 1e12)
987 snprintf(buf, sizeof(GbBuf), "%.2f GB", value / 1e9);
988 else
989 snprintf(buf, sizeof(GbBuf), "%.2f TB", value / 1e12);
990
991 return buf;
992 }