]> git.ipfire.org Git - thirdparty/squid.git/blob - lib/util.c
Andres Kroonmaa's hi-res cpu profiling patch
[thirdparty/squid.git] / lib / util.c
1
2 /*
3 * $Id: util.c,v 1.87 2002/10/02 11:06:30 robertc 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 && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
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 #include "snprintf.h"
73
74 static void default_failure_notify(const char *);
75
76 void (*failure_notify) (const char *) = default_failure_notify;
77 static char msg[128];
78
79 #if MEM_GEN_TRACE
80
81 static FILE *tracefp = NULL;
82
83 void
84 log_trace_init(char *fn)
85 {
86 tracefp = fopen(fn, "a+");
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 for (i = 0; i <= DBG_MAXINDEX; i++)
131 malloc_sizes[i] = malloc_histo[i] = 0;
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 return malloc_sizes[DBG_INDEX(sz)] += 1;
141 }
142
143 void
144 malloc_statistics(void (*func) (int, int, int, void *), void *data)
145 {
146 int i;
147 for (i = 0; i <= DBG_SPLIT; i += DBG_GRAIN_SM)
148 func(i, malloc_sizes[DBG_INDEX(i)], malloc_histo[DBG_INDEX(i)], data);
149 i -= DBG_GRAIN_SM;
150 for (i = i; i <= DBG_MAXSIZE; i += DBG_GRAIN)
151 func(i, malloc_sizes[DBG_INDEX(i)], malloc_histo[DBG_INDEX(i)], data);
152 xmemcpy(&malloc_histo, &malloc_sizes, sizeof(malloc_sizes));
153 }
154 #endif /* XMALLOC_STATISTICS */
155
156
157
158 #if XMALLOC_TRACE
159 char *xmalloc_file = "";
160 int xmalloc_line = 0;
161 char *xmalloc_func = "";
162 static int xmalloc_count = 0;
163 int xmalloc_trace = 0; /* Enable with -m option */
164 size_t xmalloc_total = 0;
165 #undef xmalloc
166 #undef xfree
167 #undef xxfree
168 #undef xrealloc
169 #undef xcalloc
170 #undef xstrdup
171 #endif
172
173 #if XMALLOC_DEBUG
174 #define DBG_ARRY_SZ (1<<11)
175 #define DBG_ARRY_BKTS (1<<8)
176 static void *(*malloc_ptrs)[DBG_ARRY_SZ];
177 static int malloc_size[DBG_ARRY_BKTS][DBG_ARRY_SZ];
178 #if XMALLOC_TRACE
179 static char *malloc_file[DBG_ARRY_BKTS][DBG_ARRY_SZ];
180 static short malloc_line[DBG_ARRY_BKTS][DBG_ARRY_SZ];
181 static int malloc_count[DBG_ARRY_BKTS][DBG_ARRY_SZ];
182 #endif
183 static int dbg_initd = 0;
184
185 #define DBG_HASH_BUCKET(ptr) (((((int)ptr)>>4)+(((int)ptr)>>12)+(((int)ptr)>>20))&0xFF)
186
187 static void
188 check_init(void)
189 {
190 int B = 0, I = 0;
191 /* calloc the ptrs so that we don't see them when hunting lost memory */
192 malloc_ptrs = calloc(DBG_ARRY_BKTS, sizeof(*malloc_ptrs));
193 for (B = 0; B < DBG_ARRY_BKTS; B++) {
194 for (I = 0; I < DBG_ARRY_SZ; I++) {
195 malloc_ptrs[B][I] = NULL;
196 malloc_size[B][I] = 0;
197 #if XMALLOC_TRACE
198 malloc_file[B][I] = NULL;
199 malloc_line[B][I] = 0;
200 malloc_count[B][I] = 0;
201 #endif
202 }
203 }
204 dbg_initd = 1;
205 }
206
207 static void
208 check_free(void *s)
209 {
210 int B, I;
211 B = DBG_HASH_BUCKET(s);
212 for (I = 0; I < DBG_ARRY_SZ; I++) {
213 if (malloc_ptrs[B][I] != s)
214 continue;
215 malloc_ptrs[B][I] = NULL;
216 malloc_size[B][I] = 0;
217 #if XMALLOC_TRACE
218 malloc_file[B][I] = NULL;
219 malloc_line[B][I] = 0;
220 malloc_count[B][I] = 0;
221 #endif
222 break;
223 }
224 if (I == DBG_ARRY_SZ) {
225 snprintf(msg, 128, "xfree: ERROR: s=%p not found!", s);
226 (*failure_notify) (msg);
227 }
228 }
229
230 static void
231 check_malloc(void *p, size_t sz)
232 {
233 void *P, *Q;
234 int B, I;
235 if (!dbg_initd)
236 check_init();
237 B = DBG_HASH_BUCKET(p);
238 for (I = 0; I < DBG_ARRY_SZ; I++) {
239 if (!(P = malloc_ptrs[B][I]))
240 continue;
241 Q = P + malloc_size[B][I];
242 if (P <= p && p < Q) {
243 snprintf(msg, 128, "xmalloc: ERROR: p=%p falls in P=%p+%d",
244 p, P, malloc_size[B][I]);
245 (*failure_notify) (msg);
246 }
247 }
248 for (I = 0; I < DBG_ARRY_SZ; I++) {
249 if (malloc_ptrs[B][I])
250 continue;
251 malloc_ptrs[B][I] = p;
252 malloc_size[B][I] = (int) sz;
253 #if XMALLOC_TRACE
254 malloc_file[B][I] = xmalloc_file;
255 malloc_line[B][I] = xmalloc_line;
256 malloc_count[B][I] = xmalloc_count;
257 #endif
258 break;
259 }
260 if (I == DBG_ARRY_SZ)
261 (*failure_notify) ("xmalloc: debug out of array space!");
262 }
263 #endif
264
265 #if XMALLOC_TRACE && !HAVE_MALLOCBLKSIZE
266 size_t
267 xmallocblksize(void *p)
268 {
269 int B, I;
270 B = DBG_HASH_BUCKET(p);
271 for (I = 0; I < DBG_ARRY_SZ; I++) {
272 if (malloc_ptrs[B][I] == p)
273 return malloc_size[B][I];
274 }
275 return 0;
276 }
277 #endif
278
279 #ifdef XMALLOC_TRACE
280 static char *
281 malloc_file_name(void *p)
282 {
283 int B, I;
284 B = DBG_HASH_BUCKET(p);
285 for (I = 0; I < DBG_ARRY_SZ; I++) {
286 if (malloc_ptrs[B][I] == p)
287 return malloc_file[B][I];
288 }
289 return 0;
290 }
291 int
292 malloc_line_number(void *p)
293 {
294 int B, I;
295 B = DBG_HASH_BUCKET(p);
296 for (I = 0; I < DBG_ARRY_SZ; I++) {
297 if (malloc_ptrs[B][I] == p)
298 return malloc_line[B][I];
299 }
300 return 0;
301 }
302 int
303 malloc_number(void *p)
304 {
305 int B, I;
306 B = DBG_HASH_BUCKET(p);
307 for (I = 0; I < DBG_ARRY_SZ; I++) {
308 if (malloc_ptrs[B][I] == p)
309 return malloc_count[B][I];
310 }
311 return 0;
312 }
313 static void
314 xmalloc_show_trace(void *p, int sign)
315 {
316 int statMemoryAccounted();
317 static size_t last_total = 0, last_accounted = 0, last_mallinfo = 0;
318 size_t accounted = statMemoryAccounted();
319 size_t mi = 0;
320 size_t sz;
321 #if HAVE_MALLINFO
322 struct mallinfo mp = mallinfo();
323 mi = mp.uordblks + mp.usmblks + mp.hblkhd;
324 #endif
325 sz = xmallocblksize(p) * sign;
326 xmalloc_total += sz;
327 xmalloc_count += sign > 0;
328 if (xmalloc_trace) {
329 fprintf(stderr, "%c%8p size=%5d/%d acc=%5d/%d mallinfo=%5d/%d %s:%d %s",
330 sign > 0 ? '+' : '-', p,
331 (int) xmalloc_total - last_total, (int) xmalloc_total,
332 (int) accounted - last_accounted, (int) accounted,
333 (int) mi - last_mallinfo, (int) mi,
334 xmalloc_file, xmalloc_line, xmalloc_func);
335 if (sign < 0)
336 fprintf(stderr, " (%d %s:%d)\n", malloc_number(p), malloc_file_name(p), malloc_line_number(p));
337 else
338 fprintf(stderr, " %d\n", xmalloc_count);
339 }
340 last_total = xmalloc_total;
341 last_accounted = accounted;
342 last_mallinfo = mi;
343 }
344
345 short malloc_refs[DBG_ARRY_BKTS][DBG_ARRY_SZ];
346 #define XMALLOC_LEAK_ALIGN (4)
347 static void
348 xmalloc_scan_region(void *start, int size, int depth)
349 {
350 int B, I;
351 char *ptr = start;
352 char *end = ptr + size - XMALLOC_LEAK_ALIGN;
353 static int sum = 0;
354 while (ptr <= end) {
355 void *p = *(void **) ptr;
356 if (p && p != start) {
357 B = DBG_HASH_BUCKET(p);
358 for (I = 0; I < DBG_ARRY_SZ; I++) {
359 if (malloc_ptrs[B][I] == p) {
360 if (!malloc_refs[B][I]++) {
361 /* A new reference */
362 fprintf(stderr, "%*s%p %s:%d size %d allocation %d\n",
363 depth, "",
364 malloc_ptrs[B][I], malloc_file[B][I],
365 malloc_line[B][I], malloc_size[B][I],
366 malloc_count[B][I]);
367 sum += malloc_size[B][I];
368 xmalloc_scan_region(malloc_ptrs[B][I], malloc_size[B][I], depth + 1);
369 if (depth == 0) {
370 if (sum != malloc_size[B][I])
371 fprintf(stderr, "=== %d bytes\n", sum);
372 sum = 0;
373 }
374 #if XMALLOC_SHOW_ALL_REFERENCES
375 } else {
376 /* We have already scanned this pointer... */
377 fprintf(stderr, "%*s%p %s:%d size %d allocation %d ... (%d)\n",
378 depth * 2, "",
379 malloc_ptrs[B][I], malloc_file[B][I],
380 malloc_line[B][I], malloc_size[B][I],
381 malloc_count[B][I], malloc_refs[B][I]);
382 #endif
383 }
384 }
385 }
386 }
387 ptr += XMALLOC_LEAK_ALIGN;
388 }
389 }
390
391 void
392 xmalloc_find_leaks(void)
393 {
394 int B, I;
395 int leak_sum = 0;
396
397 extern void _etext;
398 fprintf(stderr, "----- Memory map ----\n");
399 xmalloc_scan_region(&_etext, (void *) sbrk(0) - (void *) &_etext, 0);
400 for (B = 0; B < DBG_ARRY_BKTS; B++) {
401 for (I = 0; I < DBG_ARRY_SZ; I++) {
402 if (malloc_ptrs[B][I] && malloc_refs[B][I] == 0) {
403 /* Found a leak... */
404 fprintf(stderr, "Leak found: %p", malloc_ptrs[B][I]);
405 fprintf(stderr, " %s", malloc_file[B][I]);
406 fprintf(stderr, ":%d", malloc_line[B][I]);
407 fprintf(stderr, " size %d", malloc_size[B][I]);
408 fprintf(stderr, " allocation %d\n", malloc_count[B][I]);
409 leak_sum += malloc_size[B][I];
410 }
411 }
412 }
413 if (leak_sum) {
414 fprintf(stderr, "Total leaked memory: %d\n", leak_sum);
415 } else {
416 fprintf(stderr, "No memory leaks detected\n");
417 }
418 fprintf(stderr, "----------------------\n");
419 }
420 #endif /* XMALLOC_TRACE */
421
422 /*
423 * xmalloc() - same as malloc(3). Used for portability.
424 * Never returns NULL; fatal on error.
425 */
426 void *
427 xmalloc(size_t sz)
428 {
429 void *p;
430
431 PROF_start(xmalloc);
432 if (sz < 1)
433 sz = 1;
434
435 PROF_start(malloc);
436 p = malloc(sz);
437 PROF_stop(malloc);
438 if (p == NULL) {
439 if (failure_notify) {
440 snprintf(msg, 128, "xmalloc: Unable to allocate %d bytes!\n",
441 (int) sz);
442 (*failure_notify) (msg);
443 } else {
444 perror("malloc");
445 }
446 exit(1);
447 }
448 #if XMALLOC_DEBUG
449 check_malloc(p, sz);
450 #endif
451 #if XMALLOC_STATISTICS
452 malloc_stat(sz);
453 #endif
454 #if XMALLOC_TRACE
455 xmalloc_show_trace(p, 1);
456 #endif
457 #if MEM_GEN_TRACE
458 if (tracefp)
459 fprintf(tracefp, "m:%d:%p\n", sz, p);
460 #endif
461 PROF_stop(xmalloc);
462 return (p);
463 }
464
465 /*
466 * xfree() - same as free(3). Will not call free(3) if s == NULL.
467 */
468 void
469 xfree(void *s)
470 {
471 PROF_start(xfree);
472 #if XMALLOC_TRACE
473 xmalloc_show_trace(s, -1);
474 #endif
475
476 #if XMALLOC_DEBUG
477 if (s != NULL)
478 check_free(s);
479 #endif
480 if (s != NULL)
481 free(s);
482 #if MEM_GEN_TRACE
483 if (tracefp && s)
484 fprintf(tracefp, "f:%p\n", s);
485 #endif
486 PROF_stop(xfree);
487 }
488
489 /* xxfree() - like xfree(), but we already know s != NULL */
490 void
491 xxfree(const void *s_const)
492 {
493 void *s = (void *)s_const;
494 PROF_start(xxfree);
495 #if XMALLOC_TRACE
496 xmalloc_show_trace(s, -1);
497 #endif
498 #if XMALLOC_DEBUG
499 check_free(s);
500 #endif
501 free(s);
502 #if MEM_GEN_TRACE
503 if (tracefp && s)
504 fprintf(tracefp, "f:%p\n", s);
505 #endif
506 PROF_stop(xxfree);
507 }
508
509 /*
510 * xrealloc() - same as realloc(3). Used for portability.
511 * Never returns NULL; fatal on error.
512 */
513 void *
514 xrealloc(void *s, size_t sz)
515 {
516 void *p;
517
518 PROF_start(xrealloc);
519 #if XMALLOC_TRACE
520 xmalloc_show_trace(s, -1);
521 #endif
522
523 if (sz < 1)
524 sz = 1;
525 #if XMALLOC_DEBUG
526 if (s != NULL)
527 check_free(s);
528 #endif
529 if ((p = realloc(s, sz)) == NULL) {
530 if (failure_notify) {
531 snprintf(msg, 128, "xrealloc: Unable to reallocate %d bytes!\n",
532 (int) sz);
533 (*failure_notify) (msg);
534 } else {
535 perror("realloc");
536 }
537 exit(1);
538 }
539 #if XMALLOC_DEBUG
540 check_malloc(p, sz);
541 #endif
542 #if XMALLOC_STATISTICS
543 malloc_stat(sz);
544 #endif
545 #if XMALLOC_TRACE
546 xmalloc_show_trace(p, 1);
547 #endif
548 #if MEM_GEN_TRACE
549 if (tracefp) /* new ptr, old ptr, new size */
550 fprintf(tracefp, "r:%p:%p:%d\n", p, s, sz);
551 #endif
552 PROF_stop(xrealloc);
553 return (p);
554 }
555
556 /*
557 * xcalloc() - same as calloc(3). Used for portability.
558 * Never returns NULL; fatal on error.
559 */
560 void *
561 xcalloc(size_t n, size_t sz)
562 {
563 void *p;
564
565 PROF_start(xcalloc);
566 if (n < 1)
567 n = 1;
568 if (sz < 1)
569 sz = 1;
570 PROF_start(calloc);
571 p = calloc(n, sz);
572 PROF_stop(calloc);
573 if (p == NULL) {
574 if (failure_notify) {
575 snprintf(msg, 128, "xcalloc: Unable to allocate %u blocks of %u bytes!\n",
576 (unsigned int) n, (unsigned int) sz);
577 (*failure_notify) (msg);
578 } else {
579 perror("xcalloc");
580 }
581 exit(1);
582 }
583 #if XMALLOC_DEBUG
584 check_malloc(p, sz * n);
585 #endif
586 #if XMALLOC_STATISTICS
587 malloc_stat(sz * n);
588 #endif
589 #if XMALLOC_TRACE
590 xmalloc_show_trace(p, 1);
591 #endif
592 #if MEM_GEN_TRACE
593 if (tracefp)
594 fprintf(tracefp, "c:%u:%u:%p\n", (unsigned int) n, (unsigned int) sz, p);
595 #endif
596 PROF_stop(xcalloc);
597 return (p);
598 }
599
600 /*
601 * xstrdup() - same as strdup(3). Used for portability.
602 * Never returns NULL; fatal on error.
603 */
604 char *
605 xstrdup(const char *s)
606 {
607 size_t sz;
608 void *p;
609 PROF_start(xstrdup);
610 if (s == NULL) {
611 if (failure_notify) {
612 (*failure_notify) ("xstrdup: tried to dup a NULL pointer!\n");
613 } else {
614 fprintf(stderr, "xstrdup: tried to dup a NULL pointer!\n");
615 }
616 exit(1);
617 }
618 /* copy string, including terminating character */
619 sz = strlen(s) + 1;
620 p = memcpy(xmalloc(sz), s, sz);
621 PROF_stop(xstrdup);
622 return p;
623 }
624
625 /*
626 * xstrndup() - string dup with length limit.
627 */
628 char *
629 xstrndup(const char *s, size_t n)
630 {
631 size_t sz;
632 void *p;
633 PROF_start(xstrndup);
634 assert(s);
635 assert(n);
636 sz = strlen(s) + 1;
637 if (sz > n)
638 sz = n;
639 p = xstrncpy(xmalloc(sz), s, sz);
640 PROF_stop(xstrndup);
641 return p;
642 }
643
644 /*
645 * xstrerror() - strerror() wrapper
646 */
647 const char *
648 xstrerror(void)
649 {
650 static char xstrerror_buf[BUFSIZ];
651 static char strerror_buf[BUFSIZ];
652
653 snprintf(strerror_buf, BUFSIZ, "%s", strerror(errno));
654
655 if (strerror_buf)
656 snprintf(xstrerror_buf, BUFSIZ, "(%d) %s", errno, strerror_buf);
657 else
658 snprintf(xstrerror_buf, BUFSIZ, "(%d) Unknown", errno);
659 return xstrerror_buf;
660 }
661
662 void
663 Tolower(char *q)
664 {
665 char *s = q;
666 while (*s) {
667 *s = tolower((unsigned char) *s);
668 s++;
669 }
670 }
671
672 int
673 tvSubMsec(struct timeval t1, struct timeval t2)
674 {
675 return (t2.tv_sec - t1.tv_sec) * 1000 +
676 (t2.tv_usec - t1.tv_usec) / 1000;
677 }
678
679 int
680 tvSubUsec(struct timeval t1, struct timeval t2)
681 {
682 return (t2.tv_sec - t1.tv_sec) * 1000000 +
683 (t2.tv_usec - t1.tv_usec);
684 }
685
686 double
687 tvSubDsec(struct timeval t1, struct timeval t2)
688 {
689 return (double) (t2.tv_sec - t1.tv_sec) +
690 (double) (t2.tv_usec - t1.tv_usec) / 1000000.0;
691 }
692
693 /*
694 * xstrncpy() - similar to strncpy(3) but terminates string
695 * always with '\0' if (n != 0 and dst != NULL),
696 * and doesn't do padding
697 */
698 char *
699 xstrncpy(char *dst, const char *src, size_t n)
700 {
701 char *r = dst;
702 PROF_start(xstrncpy);
703 if (!n || !dst)
704 return dst;
705 if (src)
706 while (--n != 0 && *src != '\0')
707 *dst++ = *src++;
708 *dst = '\0';
709 PROF_stop(xstrncpy);
710 return r;
711 }
712
713 /* returns the number of leading white spaces in str; handy in skipping ws */
714 size_t
715 xcountws(const char *str)
716 {
717 size_t count = 0;
718 PROF_start(xcountws);
719 if (str) {
720 while (xisspace(*str)) {
721 str++;
722 count++;
723 }
724 }
725 PROF_stop(xcountws);
726 return count;
727 }
728
729 /* somewhat safer calculation of %s */
730 double
731 xpercent(double part, double whole)
732 {
733 return xdiv(100 * part, whole);
734 }
735
736 int
737 xpercentInt(double part, double whole)
738 {
739 #if HAVE_RINT
740 return (int) rint(xpercent(part, whole));
741 #else
742 /* SCO 3.2v4.2 doesn't have rint() -- mauri@mbp.ee */
743 return (int) floor(xpercent(part, whole) + 0.5);
744 #endif
745 }
746
747 /* somewhat safer division */
748 double
749 xdiv(double nom, double denom)
750 {
751 return (denom != 0.0) ? nom / denom : -1.0;
752 }
753
754 /* integer to string */
755 const char *
756 xitoa(int num)
757 {
758 static char buf[24]; /* 2^64 = 18446744073709551616 */
759 snprintf(buf, sizeof(buf), "%d", num);
760 return buf;
761 }
762
763 /* A default failure notifier when the main program hasn't installed any */
764 void
765 default_failure_notify(const char *message)
766 {
767 write(2, message, strlen(message));
768 write(2, "\n", 1);
769 abort();
770 }
771
772 void
773 gb_flush(gb_t * g)
774 {
775 g->gb += (g->bytes >> 30);
776 g->bytes &= (1 << 30) - 1;
777 }
778
779 double
780 gb_to_double(const gb_t * g)
781 {
782 return ((double) g->gb) * ((double) (1 << 30)) + ((double) g->bytes);
783 }
784
785 const char *
786 double_to_str(char *buf, int buf_size, double value)
787 {
788 /* select format */
789 if (value < 1e9)
790 snprintf(buf, buf_size, "%.2f MB", value / 1e6);
791 else if (value < 1e12)
792 snprintf(buf, buf_size, "%.3f GB", value / 1e9);
793 else
794 snprintf(buf, buf_size, "%.4f TB", value / 1e12);
795 return buf;
796 }
797
798 const char *
799 gb_to_str(const gb_t * g)
800 {
801 /*
802 * it is often convenient to call gb_to_str several times for _one_ printf
803 */
804 #define max_cc_calls 5
805 typedef char GbBuf[32];
806 static GbBuf bufs[max_cc_calls];
807 static int call_id = 0;
808 double value = gb_to_double(g);
809 char *buf = bufs[call_id++];
810 if (call_id >= max_cc_calls)
811 call_id = 0;
812 /* select format */
813 if (value < 1e9)
814 snprintf(buf, sizeof(GbBuf), "%.2f MB", value / 1e6);
815 else if (value < 1e12)
816 snprintf(buf, sizeof(GbBuf), "%.2f GB", value / 1e9);
817 else
818 snprintf(buf, sizeof(GbBuf), "%.2f TB", value / 1e12);
819 return buf;
820 }