]> git.saurik.com Git - apple/libplatform.git/blame - src/simple/string_io.c
libplatform-254.40.4.tar.gz
[apple/libplatform.git] / src / simple / string_io.c
CommitLineData
ada7c492
A
1/*
2 * Copyright (c) 2005, 2006, 2009 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <stdlib.h>
25#include <unistd.h>
26#include <mach/mach_init.h>
27#include <mach/vm_map.h>
28#include <errno.h>
29
30#include "os/internal.h"
31#include "_simple.h"
32#include "platform/string.h"
33#include "platform/compat.h"
34
35#ifndef VM_PAGE_SIZE
36#define VM_PAGE_SIZE 4096
37#endif
38
39#define BUF_SIZE(s) (((BUF *)(s))->end - ((BUF *)(s))->buf + 1)
e45b4692
A
40#if DEBUG
41#define MYBUFSIZE 256
42#else
ada7c492
A
43/* we use a small buffer to minimize stack usage constraints */
44#define MYBUFSIZE 32
e45b4692 45#endif
ada7c492
A
46
47typedef struct _BUF {
48 char *buf;
49 char *ptr;
50 char *end;
51 int fd;
52 void (*full)(struct _BUF *);
53} BUF;
54
55/* flush the buffer */
56static void
57_flush(BUF *b)
58{
59 char *buf = b->buf;
60 ssize_t n = b->ptr - buf;
61 ssize_t w;
62
63 while (n > 0) {
64 w = write(b->fd, buf, n);
65 if (w < 0) {
66 if (errno == EINTR || errno == EAGAIN)
67 continue;
68 break;
69 }
70 n -= w;
71 buf += n;
72 }
73}
74
75/* flush the buffer and reset the pointer */
76static void
77_flush_reset(BUF *b)
78{
79 _flush(b);
80 b->ptr = b->buf;
81}
82
83/* enlarge the buffer */
84static void
85_enlarge(BUF *b)
86{
87 vm_address_t new;
88 vm_size_t sold, snew;
89 intptr_t diff;
90 kern_return_t kr;
91
92 new = (vm_address_t)(b->end + 1);
93 if(vm_allocate(mach_task_self(), &new, VM_PAGE_SIZE, 0) == 0) {
94 /* page is adjacent */
95 b->end += VM_PAGE_SIZE;
96 return;
97 }
98 sold = BUF_SIZE(b);
99 snew = (sold + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1);
100 if ((kr = vm_allocate(mach_task_self(), &new, snew, 1)) != 0) {
101 __LIBPLATFORM_CLIENT_CRASH__(kr, "Failed to allocate memory for buffer");
102 }
103 diff = new - (vm_address_t)b->buf;
104 memmove((void *)new, b->buf, sold);
105 if((intptr_t)(b->buf) & (VM_PAGE_SIZE - 1)) {
106 sold &= ~(VM_PAGE_SIZE - 1);
107 b->buf = (char *)((intptr_t)(b->buf + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1));
108 b->end = (char *)(new + snew - 1);
109 } else
110 b->end += diff + VM_PAGE_SIZE;
111 if(sold > 0) {
112 vm_deallocate(mach_task_self(), (vm_address_t)b->buf, sold);
113 }
114 b->buf = (char *)new;
115 b->ptr += diff;
116}
117
118static inline void put_s(BUF *, _esc_func, const char *);
119/* output a single character */
120static inline void
121put_c(BUF *b, _esc_func esc, unsigned char c)
122{
123 const char *cp;
124
125 if(esc && (cp = esc(c)) != NULL)
126 put_s(b, NULL, cp);
127 else {
128 if(b->ptr >= b->end)
129 b->full(b);
130 *b->ptr++ = c;
131 }
132}
133
134/* output a null-terminated string */
135static inline void
136put_s(BUF *b, _esc_func esc, const char *str)
137{
138 while(*str)
139 put_c(b, esc, *str++);
140}
141
142/* output a string of the specified size */
143static inline void
144put_n(BUF *b, _esc_func esc, const char *str, ssize_t n)
145{
146 while(n-- > 0)
147 put_c(b, esc, *str++);
148}
149
442fbc9d
A
150#if __LP64__ || defined(__arm64__)
151static unsigned long long
152udiv10(unsigned long long a, unsigned long long *rem)
153{
154 *rem = a % 10;
155 return a / 10;
156}
157#else
158unsigned long long
159udiv10(unsigned long long a, unsigned long long *rem_out)
160{
161 if (a <= UINT_MAX) {
162 *rem_out = (unsigned long long)((unsigned int)a % 10);
163 return (unsigned long long)((unsigned int)a / 10);
164 }
165
166 // The biggest multiple of 10 that dividend might contain
167 unsigned long long divisor = 0xa000000000000000;
168 unsigned long long dividend = a;
169 unsigned long long quotient = 0;
170
171 while (divisor >= 0xa) {
172 quotient = quotient << 1;
173 if (dividend >= divisor) {
174 dividend -= divisor;
175 quotient += 1;
176 }
177 divisor = divisor >> 1;
178 }
179
180 *rem_out = dividend;
181 return quotient;
182}
183#endif
184
ada7c492
A
185/*
186 * Output the signed decimal string representing the number in "in". "width" is
187 * the minimum field width, and "zero" is a boolean value, true for zero padding
188 * (otherwise blank padding).
189 */
190static void
191dec(BUF *b, _esc_func esc, long long in, int width, int zero)
192{
193 char buf[32];
194 char *cp = buf + sizeof(buf);
195 ssize_t pad;
196 int neg = 0;
197 unsigned long long n = (unsigned long long)in;
442fbc9d 198 unsigned long long rem;
ada7c492
A
199
200 if(in < 0) {
201 neg++;
202 width--;
203 n = ~n + 1;
204 }
205 *--cp = 0;
206 if(n) {
207 while(n) {
442fbc9d
A
208 n = udiv10(n, &rem);
209 *--cp = rem + '0';
ada7c492
A
210 }
211 } else
212 *--cp = '0';
213 if(neg && zero) {
214 put_c(b, esc, '-');
215 neg = 0;
216 }
217 pad = width - strlen(cp);
218 zero = zero ? '0' : ' ';
219 while(pad-- > 0)
220 put_c(b, esc, zero);
221 if(neg)
222 put_c(b, esc, '-');
223 put_s(b, esc, cp);
224}
225
89154bfb
A
226/*
227 * Output the octal string representing the number in "n". "width" is
228 * the minimum field width, and "zero" is a boolean value, true for zero padding
229 * (otherwise blank padding).
230 */
231static void
232oct(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
233{
234 char buf[32];
235 char *cp = buf + sizeof(buf);
236 ssize_t pad;
237
238 *--cp = 0;
239 if (n) {
240 while (n) {
241 *--cp = (n % 8) + '0';
242 n /= 8;
243 }
244 } else {
245 *--cp = '0';
246 }
247 pad = width - strlen(cp);
248 zero = zero ? '0' : ' ';
249 while (pad-- > 0) {
250 put_c(b, esc, zero);
251 }
252 put_s(b, esc, cp);
253}
254
ada7c492
A
255/*
256 * Output the hex string representing the number in "n". "width" is the
257 * minimum field width, and "zero" is a boolean value, true for zero padding
258 * (otherwise blank padding). "upper" is a boolean value, true for upper
259 * case hex characters, lower case otherwise. "p" is a boolean value, true
260 * if 0x should be prepended (for %p), otherwise nothing.
261 */
262static const char _h[] = "0123456789abcdef";
263static const char _H[] = "0123456789ABCDEF";
264static const char _0x[] = "0x";
265
266static void
267hex(BUF *b, _esc_func esc, unsigned long long n, int width, int zero, int upper, int p)
268{
269 char buf[32];
270 char *cp = buf + sizeof(buf);
271 const char *h = upper ? _H : _h;
272
273 *--cp = 0;
274 if(n) {
275 while(n) {
276 *--cp = h[n & 0xf];
277 n >>= 4;
278 }
279 } else
280 *--cp = '0';
281 if(p) {
282 width -= 2;
283 if(zero) {
284 put_s(b, esc, _0x);
285 p = 0;
286 }
287 }
288 width -= strlen(cp);
289 zero = zero ? '0' : ' ';
290 while(width-- > 0)
291 put_c(b, esc, zero);
292 if(p)
293 put_s(b, esc, _0x);
294 put_s(b, esc, cp);
295}
296
297/*
298 * Output the unsigned decimal string representing the number in "n". "width"
299 * is the minimum field width, and "zero" is a boolean value, true for zero
300 * padding (otherwise blank padding).
301 */
302static void
303udec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
304{
305 char buf[32];
306 char *cp = buf + sizeof(buf);
442fbc9d 307 unsigned long long rem;
ada7c492
A
308 ssize_t pad;
309
310 *--cp = 0;
311 if(n) {
312 while(n) {
442fbc9d
A
313 n = udiv10(n, &rem);
314 *--cp = rem + '0';
ada7c492
A
315 }
316 } else
317 *--cp = '0';
318 pad = width - strlen(cp);
319 zero = zero ? '0' : ' ';
320 while(pad-- > 0)
321 put_c(b, esc, zero);
322 put_s(b, esc, cp);
323}
324
325/*
326 * Output the unsigned decimal string representing the number in "n", rounded
327 * to the nearest MB, KB or b. "width" is the minimum field width, and "zero"
328 * is a boolean value, true for zero padding (otherwise blank padding).
329 */
330static void
331ydec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
332{
333 if(n >= 10 * (1 << 20)) {
334 n += (1 << 19);
335 udec(b, esc, n >> 20, width, zero);
336 put_s(b, esc, "MB");
337 } else if (n >= 10 * (1 << 10)) {
338 n += (1 << 9);
339 udec(b, esc, n >> 10, width, zero);
340 put_s(b, esc, "KB");
341 } else {
342 udec(b, esc, n, width, zero);
343 put_s(b, esc, "b");
344 }
345}
346
347/*
348 * The actual engine for all the _simple_*printf routines.
349 */
350static void
351__simple_bprintf(BUF *b, _esc_func esc, const char *fmt, va_list ap)
352{
353 while(*fmt) {
354 int lflag, zero, width;
355 char *cp;
356 if(!(cp = strchr(fmt, '%'))) {
357 put_s(b, esc, fmt);
358 break;
359 }
360 put_n(b, esc, fmt, cp - fmt);
361 fmt = cp + 1;
362 if(*fmt == '%') {
363 put_c(b, esc, '%');
364 fmt++;
365 continue;
366 }
367 lflag = zero = width = 0;
368 for(;;) {
369 switch(*fmt) {
370 case '0':
371 zero++;
372 fmt++;
373 /* drop through */
374 case '1': case '2': case '3': case '4': case '5':
375 case '6': case '7': case '8': case '9':
376 while(*fmt >= '0' && *fmt <= '9')
377 width = 10 * width + (*fmt++ - '0');
378 continue;
379 case 'c':
380 zero = zero ? '0' : ' ';
381 width--;
382 while(width-- > 0)
383 put_c(b, esc, zero);
384 put_c(b, esc, va_arg(ap, int));
385 break;
386 case 'd': case 'i':
387 switch(lflag) {
388 case 0:
389 dec(b, esc, va_arg(ap, int), width, zero);
390 break;
391 case 1:
392 dec(b, esc, va_arg(ap, long), width, zero);
393 break;
394 default:
395 dec(b, esc, va_arg(ap, long long), width, zero);
396 break;
397 }
398 break;
399 case 'l':
400 lflag++;
401 fmt++;
402 continue;
89154bfb
A
403 case 'o':
404 switch (lflag) {
405 case 0:
406 oct(b, esc, va_arg(ap, int), width, zero);
407 break;
408 case 1:
409 oct(b, esc, va_arg(ap, long), width, zero);
410 break;
411 default:
412 oct(b, esc, va_arg(ap, long long), width, zero);
413 break;
414 }
415 break;
ada7c492
A
416 case 'p':
417 hex(b, esc, (unsigned long)va_arg(ap, void *), width, zero, 0, 1);
418 break;
419 case 's':
420 cp = va_arg(ap, char *);
421 cp = cp ? cp : "(null)";
422 width -= strlen(cp);
423 zero = zero ? '0' : ' ';
424 while(width-- > 0)
425 put_c(b, esc, zero);
426 put_s(b, esc, cp);
427 break;
428 case 'u':
429 switch(lflag) {
430 case 0:
431 udec(b, esc, va_arg(ap, unsigned int), width, zero);
432 break;
433 case 1:
434 udec(b, esc, va_arg(ap, unsigned long), width, zero);
435 break;
436 default:
437 udec(b, esc, va_arg(ap, unsigned long long), width, zero);
438 break;
439 }
440 break;
441 case 'X': case 'x':
442 switch(lflag) {
443 case 0:
444 hex(b, esc, va_arg(ap, unsigned int), width, zero,
445 *fmt == 'X', 0);
446 break;
447 case 1:
448 hex(b, esc, va_arg(ap, unsigned long), width, zero,
449 *fmt == 'X', 0);
450 break;
451 default:
452 hex(b, esc, va_arg(ap, unsigned long long), width, zero,
453 *fmt == 'X', 0);
454 break;
455 }
456 break;
457 case 'y':
458 switch(lflag) {
459 case 0:
460 ydec(b, esc, va_arg(ap, unsigned int), width, zero);
461 break;
462 case 1:
463 ydec(b, esc, va_arg(ap, unsigned long), width, zero);
464 break;
465 default:
466 ydec(b, esc, va_arg(ap, unsigned long long), width, zero);
467 break;
468 }
469 break;
470 default:
471 put_c(b, esc, *fmt);
472 break;
473 }
474 break;
475 }
476 fmt++;
477 }
478}
479
480/*
481 * A simplified vfprintf variant. The format string is interpreted with
482 * arguments from the va_list, and the results are written to the given
483 * file descriptor.
484 */
485void
486_simple_vdprintf(int fd, const char *fmt, va_list ap)
487{
488 BUF b;
489 char buf[MYBUFSIZE];
490
491 b.buf = buf;
492 b.fd = fd;
493 b.ptr = b.buf;
494 b.end = b.buf + MYBUFSIZE;
495 b.full = _flush_reset;
496 __simple_bprintf(&b, NULL, fmt, ap);
497 _flush(&b);
498}
499
500/*
501 * A simplified fprintf variant. The format string is interpreted with
502 * arguments from the variable argument list, and the results are written
503 * to the given file descriptor.
504 */
505void
506_simple_dprintf(int fd, const char *fmt, ...)
507{
508 va_list ap;
509
510 va_start(ap, fmt);
511 _simple_vdprintf(fd, fmt, ap);
512 va_end(ap);
513}
514
515/*
516 * A simplified string allocate routine. Pass the opaque pointer to structure
517 * to _simple_*sprintf() routines. Use _simple_string() to retrieve the
518 * current string (the string is guaranteed to be null terminated only on
519 * the call to _simple_string()). Use _simple_sfree() to free the structure
520 * and string memory.
521 */
522_SIMPLE_STRING
523_simple_salloc(void)
524{
ada7c492
A
525 BUF *b;
526
e45b4692
A
527 if(vm_allocate(mach_task_self(), (vm_address_t *)&b, VM_PAGE_SIZE, 1))
528 return NULL;
ada7c492
A
529 b->ptr = b->buf = (char *)b + sizeof(BUF);
530 b->end = (char *)b + VM_PAGE_SIZE - 1;
531 b->full = _enlarge;
532 return (_SIMPLE_STRING)b;
533}
534
535/*
536 * The format string is interpreted with arguments from the va_list, and the
537 * results are appended to the string maintained by the opaque structure, as
e45b4692
A
538 * returned by a previous call to _simple_salloc(). Non-zero is returned on
539 * out-of-memory error.
ada7c492
A
540 */
541int
542_simple_vsprintf(_SIMPLE_STRING b, const char *fmt, va_list ap)
543{
544 return _simple_vesprintf(b, NULL, fmt, ap);
545}
546
547/*
548 * The format string is interpreted with arguments from the variable argument
549 * list, and the results are appended to the string maintained by the opaque
e45b4692
A
550 * structure, as returned by a previous call to _simple_salloc(). Non-zero is
551 * returned on out-of-memory error.
ada7c492
A
552 */
553int
554_simple_sprintf(_SIMPLE_STRING b, const char *fmt, ...)
555{
556 va_list ap;
557 int ret;
558
559 va_start(ap, fmt);
560 ret = _simple_vesprintf(b, NULL, fmt, ap);
561 va_end(ap);
562 return ret;
563}
564
565/*
566 * Like _simple_vsprintf(), except __esc is a function to call on each
567 * character; the function returns NULL if the character should be passed
568 * as is, otherwise, the returned character string is used instead.
569 */
570int
571_simple_vesprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, va_list ap)
572{
573 __simple_bprintf((BUF *)b, esc, fmt, ap);
574 return 0;
575}
576
577/*
578 * Like _simple_sprintf(), except __esc is a function to call on each
579 * character; the function returns NULL if the character should be passed
580 * as is, otherwise, the returned character string is used instead.
581 */
582int _simple_esprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, ...)
583{
584 va_list ap;
585 int ret;
586
587 va_start(ap, fmt);
588 ret = _simple_vesprintf(b, esc, fmt, ap);
589 va_end(ap);
590 return ret;
591}
592
593/*
594 * Return the null terminated string from the opaque structure, as returned
595 * by a previous call to _simple_salloc().
596 */
597char *
598_simple_string(_SIMPLE_STRING b)
599{
600 *((BUF *)b)->ptr = 0;
601 return ((BUF *)b)->buf;
602}
603
604/*
605 * Reposition the pointer to the first null in the buffer. After a call to
606 * _simple_string, the buffer can be modified, and shrunk.
607 */
608void
609_simple_sresize(_SIMPLE_STRING b)
610{
611 ((BUF *)b)->ptr = ((BUF *)b)->buf + strlen(((BUF *)b)->buf);
612}
613
614/*
615 * Append the null-terminated string to the string associated with the opaque
e45b4692 616 * structure. Non-zero is returned on out-of-memory error.
ada7c492
A
617 */
618int
619_simple_sappend(_SIMPLE_STRING b, const char *str)
620{
621 return _simple_esappend(b, NULL, str);
622}
623
624/*
625 * Like _simple_sappend(), except __esc is a function to call on each
626 * character; the function returns NULL if the character should be passed
627 * as is, otherwise, the returned character string is used instead.
628 */
629int _simple_esappend(_SIMPLE_STRING b, _esc_func esc, const char *str)
630{
631 put_s((BUF *)b, esc, str);
632 return 0;
633}
634
635/*
636 * Write the string associated with the opaque structure to the file descriptor.
637 */
638void
639_simple_put(_SIMPLE_STRING b, int fd)
640{
641 ((BUF *)b)->fd = fd;
642 _flush((BUF *)b);
643}
644
645/*
646 * Write the string associated with the opaque structure and a trailing newline,
647 * to the file descriptor.
648 */
649void
650_simple_putline(_SIMPLE_STRING b, int fd)
651{
652 ((BUF *)b)->fd = fd;
653 *((BUF *)b)->ptr++ = '\n';
654 _flush((BUF *)b);
655 ((BUF *)b)->ptr--;
656}
657
658/*
659 * Free the opaque structure, and the associated string.
660 */
661void
662_simple_sfree(_SIMPLE_STRING b)
663{
664 vm_size_t s;
665
666 if(b == NULL) return;
667 if(((intptr_t)(((BUF *)b)->buf) & (VM_PAGE_SIZE - 1)) == 0) {
668 vm_deallocate(mach_task_self(), (vm_address_t)((BUF *)b)->buf, BUF_SIZE(b));
669 s = VM_PAGE_SIZE;
670 } else {
671 s = ((BUF *)b)->end - (char *)b + 1;
672 }
673 vm_deallocate(mach_task_self(), (vm_address_t)b, s);
674}