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