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