source: mainline/common/str.c@ 28c39f3

Last change on this file since 28c39f3 was 28c39f3, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Improve performance of some str functions

Some string functions are unnecessarily expensive.
Attempt to make them better by using bytewise operations instead
of transcoding to/from char32_t when possible.

  • Property mode set to 100644
File size: 39.7 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2005 Martin Decky
4 * Copyright (c) 2008 Jiri Svoboda
5 * Copyright (c) 2011 Martin Sucha
6 * Copyright (c) 2011 Oleg Romanenko
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * - The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/** @addtogroup libc
34 * @{
35 */
36
37/**
38 * @file
39 * @brief String functions.
40 *
41 * Strings and characters use the Universal Character Set (UCS). The standard
42 * strings, called just strings are encoded in UTF-8. Wide strings (encoded
43 * in UTF-32) are supported to a limited degree. A single character is
44 * represented as char32_t.@n
45 *
46 * Overview of the terminology:@n
47 *
48 * Term Meaning
49 * -------------------- ----------------------------------------------------
50 * byte 8 bits stored in uint8_t (unsigned 8 bit integer)
51 *
52 * character UTF-32 encoded Unicode character, stored in char32_t
53 * (unsigned 32 bit integer), code points 0 .. 1114111
54 * are valid
55 *
56 * Note that Unicode characters do not match
57 * one-to-one with displayed characters or glyphs on
58 * screen. For that level of precision, look up
59 * Grapheme Clusters.
60 *
61 * ASCII character 7 bit encoded ASCII character, stored in char
62 * (usually signed 8 bit integer), code points 0 .. 127
63 * are valid
64 *
65 * string UTF-8 encoded NULL-terminated Unicode string, char *
66 *
67 * wide string UTF-32 encoded NULL-terminated Unicode string,
68 * char32_t *
69 *
70 * [wide] string size number of BYTES in a [wide] string (excluding
71 * the NULL-terminator), size_t
72 *
73 * [wide] string length number of CHARACTERS in a [wide] string (excluding
74 * the NULL-terminator), size_t
75 *
76 * [wide] string width number of display cells on a monospace display taken
77 * by a [wide] string, size_t
78 *
79 * This is virtually impossible to determine exactly for
80 * all strings without knowing specifics of the display
81 * device, due to various factors affecting text output.
82 * If you have the option to query the terminal for
83 * position change caused by outputting the string,
84 * it is preferrable to determine width that way.
85 *
86 *
87 * Overview of string metrics:@n
88 *
89 * Metric Abbrev. Type Meaning
90 * ------ ------ ------ -------------------------------------------------
91 * size n size_t number of BYTES in a string (excluding the
92 * NULL-terminator)
93 *
94 * length l size_t number of CHARACTERS in a string (excluding the
95 * null terminator)
96 *
97 * width w size_t number of display cells on a monospace display
98 * taken by a string
99 *
100 *
101 * Function naming prefixes:@n
102 *
103 * chr_ operate on characters
104 * ascii_ operate on ASCII characters
105 * str_ operate on strings
106 * wstr_ operate on wide strings
107 *
108 * [w]str_[n|l|w] operate on a prefix limited by size, length
109 * or width
110 *
111 *
112 * A specific character inside a [wide] string can be referred to by:@n
113 *
114 * pointer (char *, char32_t *)
115 * byte offset (size_t)
116 * character index (size_t)
117 *
118 */
119
120#include <str.h>
121
122#include <align.h>
123#include <assert.h>
124#include <ctype.h>
125#include <errno.h>
126#include <macros.h>
127#include <mem.h>
128#include <stdbool.h>
129#include <stddef.h>
130#include <stdint.h>
131#include <stdlib.h>
132#include <uchar.h>
133
134/** Byte mask consisting of lowest @n bits (out of 8) */
135#define LO_MASK_8(n) ((uint8_t) ((1 << (n)) - 1))
136
137/** Byte mask consisting of lowest @n bits (out of 32) */
138#define LO_MASK_32(n) ((uint32_t) ((1 << (n)) - 1))
139
140/** Byte mask consisting of highest @n bits (out of 8) */
141#define HI_MASK_8(n) (~LO_MASK_8(8 - (n)))
142
143/** Number of data bits in a UTF-8 continuation byte */
144#define CONT_BITS 6
145
146static inline bool _is_ascii(uint8_t b)
147{
148 return b < 0x80;
149}
150
151static inline bool _is_continuation_byte(uint8_t b)
152{
153 return (b & 0xc0) == 0x80;
154}
155
156static inline int _char_continuation_bytes(char32_t c)
157{
158 if ((c & ~LO_MASK_32(11)) == 0)
159 return 1;
160
161 if ((c & ~LO_MASK_32(16)) == 0)
162 return 2;
163
164 if ((c & ~LO_MASK_32(21)) == 0)
165 return 3;
166
167 /* Codes longer than 21 bits are not supported */
168 return -1;
169}
170
171static inline int _continuation_bytes(uint8_t b)
172{
173 /* 0xxxxxxx */
174 if (_is_ascii(b))
175 return 0;
176
177 /* 110xxxxx 10xxxxxx */
178 if ((b & 0xe0) == 0xc0)
179 return 1;
180
181 /* 1110xxxx 10xxxxxx 10xxxxxx */
182 if ((b & 0xf0) == 0xe0)
183 return 2;
184
185 /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
186 if ((b & 0xf8) == 0xf0)
187 return 3;
188
189 return -1;
190}
191
192/** Decode a single character from a string.
193 *
194 * Decode a single character from a string of size @a size. Decoding starts
195 * at @a offset and this offset is moved to the beginning of the next
196 * character. In case of decoding error, offset generally advances at least
197 * by one. However, offset is never moved beyond size.
198 *
199 * @param str String (not necessarily NULL-terminated).
200 * @param offset Byte offset in string where to start decoding.
201 * @param size Size of the string (in bytes).
202 *
203 * @return Value of decoded character, U_SPECIAL on decoding error or
204 * NULL if attempt to decode beyond @a size.
205 *
206 */
207char32_t str_decode(const char *str, size_t *offset, size_t size)
208{
209 if (*offset + 1 > size)
210 return 0;
211
212 /* First byte read from string */
213 uint8_t b0 = (uint8_t) str[(*offset)++];
214
215 /* Fast exit for the most common case. */
216 if (_is_ascii(b0))
217 return b0;
218
219 /* 10xxxxxx -- unexpected continuation byte */
220 if (_is_continuation_byte(b0))
221 return U_SPECIAL;
222
223 /* Determine code length */
224
225 unsigned int cbytes = _continuation_bytes(b0);
226 unsigned int b0_bits = 6 - cbytes; /* Data bits in first byte */
227
228 if (*offset + cbytes > size)
229 return U_SPECIAL;
230
231 char32_t ch = b0 & LO_MASK_8(b0_bits);
232
233 /* Decode continuation bytes */
234 while (cbytes > 0) {
235 uint8_t b = (uint8_t) str[(*offset)++];
236
237 if (!_is_continuation_byte(b))
238 return U_SPECIAL;
239
240 /* Shift data bits to ch */
241 ch = (ch << CONT_BITS) | (char32_t) (b & LO_MASK_8(CONT_BITS));
242 cbytes--;
243 }
244
245 return ch;
246}
247
248/** Decode a single character from a string to the left.
249 *
250 * Decode a single character from a string of size @a size. Decoding starts
251 * at @a offset and this offset is moved to the beginning of the previous
252 * character. In case of decoding error, offset generally decreases at least
253 * by one. However, offset is never moved before 0.
254 *
255 * @param str String (not necessarily NULL-terminated).
256 * @param offset Byte offset in string where to start decoding.
257 * @param size Size of the string (in bytes).
258 *
259 * @return Value of decoded character, U_SPECIAL on decoding error or
260 * NULL if attempt to decode beyond @a start of str.
261 *
262 */
263char32_t str_decode_reverse(const char *str, size_t *offset, size_t size)
264{
265 if (*offset == 0)
266 return 0;
267
268 int cbytes = 0;
269 /* Continue while continuation bytes found */
270 while (*offset > 0 && cbytes < 4) {
271 uint8_t b = (uint8_t) str[--(*offset)];
272
273 if (_is_continuation_byte(b)) {
274 cbytes++;
275 continue;
276 }
277
278 /* Invalid byte. */
279 if (cbytes != _continuation_bytes(b))
280 return U_SPECIAL;
281
282 /* Start byte */
283 size_t start_offset = *offset;
284 return str_decode(str, &start_offset, size);
285 }
286
287 /* Too many continuation bytes */
288 return U_SPECIAL;
289}
290
291/** Encode a single character to string representation.
292 *
293 * Encode a single character to string representation (i.e. UTF-8) and store
294 * it into a buffer at @a offset. Encoding starts at @a offset and this offset
295 * is moved to the position where the next character can be written to.
296 *
297 * @param ch Input character.
298 * @param str Output buffer.
299 * @param offset Byte offset where to start writing.
300 * @param size Size of the output buffer (in bytes).
301 *
302 * @return EOK if the character was encoded successfully, EOVERFLOW if there
303 * was not enough space in the output buffer or EINVAL if the character
304 * code was invalid.
305 */
306errno_t chr_encode(char32_t ch, char *str, size_t *offset, size_t size)
307{
308 if (*offset >= size)
309 return EOVERFLOW;
310
311 /* Fast exit for the most common case. */
312 if (ch < 0x80) {
313 str[(*offset)++] = (char) ch;
314 return EOK;
315 }
316
317 /* Codes longer than 21 bits are not supported */
318 if (!chr_check(ch))
319 return EINVAL;
320
321 /* Determine how many continuation bytes are needed */
322
323 unsigned int cbytes = _char_continuation_bytes(ch);
324 unsigned int b0_bits = 6 - cbytes; /* Data bits in first byte */
325
326 /* Check for available space in buffer */
327 if (*offset + cbytes >= size)
328 return EOVERFLOW;
329
330 /* Encode continuation bytes */
331 unsigned int i;
332 for (i = cbytes; i > 0; i--) {
333 str[*offset + i] = 0x80 | (ch & LO_MASK_32(CONT_BITS));
334 ch >>= CONT_BITS;
335 }
336
337 /* Encode first byte */
338 str[*offset] = (ch & LO_MASK_32(b0_bits)) | HI_MASK_8(8 - b0_bits - 1);
339
340 /* Advance offset */
341 *offset += cbytes + 1;
342
343 return EOK;
344}
345
346/* Convert in place any bytes that don't form a valid character into U_SPECIAL. */
347static void _repair_string(char *str, size_t n)
348{
349 for (; *str && n > 0; str++, n--) {
350 int cont = _continuation_bytes(*str);
351 if (cont == 0)
352 continue;
353
354 if (cont < 0 || n <= (size_t) cont) {
355 *str = U_SPECIAL;
356 continue;
357 }
358
359 for (int i = 1; i <= cont; i++) {
360 if (!_is_continuation_byte(str[i])) {
361 *str = U_SPECIAL;
362 continue;
363 }
364 }
365 }
366}
367
368static size_t _str_size(const char *str)
369{
370 size_t size = 0;
371
372 while (*str++ != 0)
373 size++;
374
375 return size;
376}
377
378/** Get size of string.
379 *
380 * Get the number of bytes which are used by the string @a str (excluding the
381 * NULL-terminator).
382 *
383 * @param str String to consider.
384 *
385 * @return Number of bytes used by the string
386 *
387 */
388size_t str_size(const char *str)
389{
390 return _str_size(str);
391}
392
393/** Get size of wide string.
394 *
395 * Get the number of bytes which are used by the wide string @a str (excluding the
396 * NULL-terminator).
397 *
398 * @param str Wide string to consider.
399 *
400 * @return Number of bytes used by the wide string
401 *
402 */
403size_t wstr_size(const char32_t *str)
404{
405 return (wstr_length(str) * sizeof(char32_t));
406}
407
408/** Get size of string with length limit.
409 *
410 * Get the number of bytes which are used by up to @a max_len first
411 * characters in the string @a str. If @a max_len is greater than
412 * the length of @a str, the entire string is measured (excluding the
413 * NULL-terminator).
414 *
415 * @param str String to consider.
416 * @param max_len Maximum number of characters to measure.
417 *
418 * @return Number of bytes used by the characters.
419 *
420 */
421size_t str_lsize(const char *str, size_t max_len)
422{
423 size_t len = 0;
424 size_t offset = 0;
425
426 while (len < max_len) {
427 if (str_decode(str, &offset, STR_NO_LIMIT) == 0)
428 break;
429
430 len++;
431 }
432
433 return offset;
434}
435
436static size_t _str_nsize(const char *str, size_t max_size)
437{
438 size_t size = 0;
439
440 while ((*str++ != 0) && (size < max_size))
441 size++;
442
443 return size;
444}
445
446/** Get size of string with size limit.
447 *
448 * Get the number of bytes which are used by the string @a str
449 * (excluding the NULL-terminator), but no more than @max_size bytes.
450 *
451 * @param str String to consider.
452 * @param max_size Maximum number of bytes to measure.
453 *
454 * @return Number of bytes used by the string
455 *
456 */
457size_t str_nsize(const char *str, size_t max_size)
458{
459 return _str_nsize(str, max_size);
460}
461
462/** Get size of wide string with size limit.
463 *
464 * Get the number of bytes which are used by the wide string @a str
465 * (excluding the NULL-terminator), but no more than @max_size bytes.
466 *
467 * @param str Wide string to consider.
468 * @param max_size Maximum number of bytes to measure.
469 *
470 * @return Number of bytes used by the wide string
471 *
472 */
473size_t wstr_nsize(const char32_t *str, size_t max_size)
474{
475 return (wstr_nlength(str, max_size) * sizeof(char32_t));
476}
477
478/** Get size of wide string with length limit.
479 *
480 * Get the number of bytes which are used by up to @a max_len first
481 * wide characters in the wide string @a str. If @a max_len is greater than
482 * the length of @a str, the entire wide string is measured (excluding the
483 * NULL-terminator).
484 *
485 * @param str Wide string to consider.
486 * @param max_len Maximum number of wide characters to measure.
487 *
488 * @return Number of bytes used by the wide characters.
489 *
490 */
491size_t wstr_lsize(const char32_t *str, size_t max_len)
492{
493 return (wstr_nlength(str, max_len * sizeof(char32_t)) * sizeof(char32_t));
494}
495
496/** Get number of characters in a string.
497 *
498 * @param str NULL-terminated string.
499 *
500 * @return Number of characters in string.
501 *
502 */
503size_t str_length(const char *str)
504{
505 size_t len = 0;
506 size_t offset = 0;
507
508 while (str_decode(str, &offset, STR_NO_LIMIT) != 0)
509 len++;
510
511 return len;
512}
513
514/** Get number of characters in a wide string.
515 *
516 * @param str NULL-terminated wide string.
517 *
518 * @return Number of characters in @a str.
519 *
520 */
521size_t wstr_length(const char32_t *wstr)
522{
523 size_t len = 0;
524
525 while (*wstr++ != 0)
526 len++;
527
528 return len;
529}
530
531/** Get number of characters in a string with size limit.
532 *
533 * @param str NULL-terminated string.
534 * @param size Maximum number of bytes to consider.
535 *
536 * @return Number of characters in string.
537 *
538 */
539size_t str_nlength(const char *str, size_t size)
540{
541 size_t len = 0;
542 size_t offset = 0;
543
544 while (str_decode(str, &offset, size) != 0)
545 len++;
546
547 return len;
548}
549
550/** Get number of characters in a string with size limit.
551 *
552 * @param str NULL-terminated string.
553 * @param size Maximum number of bytes to consider.
554 *
555 * @return Number of characters in string.
556 *
557 */
558size_t wstr_nlength(const char32_t *str, size_t size)
559{
560 size_t len = 0;
561 size_t limit = ALIGN_DOWN(size, sizeof(char32_t));
562 size_t offset = 0;
563
564 while ((offset < limit) && (*str++ != 0)) {
565 len++;
566 offset += sizeof(char32_t);
567 }
568
569 return len;
570}
571
572/** Get character display width on a character cell display.
573 *
574 * @param ch Character
575 * @return Width of character in cells.
576 */
577size_t chr_width(char32_t ch)
578{
579 return 1;
580}
581
582/** Get string display width on a character cell display.
583 *
584 * @param str String
585 * @return Width of string in cells.
586 */
587size_t str_width(const char *str)
588{
589 size_t width = 0;
590 size_t offset = 0;
591 char32_t ch;
592
593 while ((ch = str_decode(str, &offset, STR_NO_LIMIT)) != 0)
594 width += chr_width(ch);
595
596 return width;
597}
598
599/** Check whether character is plain ASCII.
600 *
601 * @return True if character is plain ASCII.
602 *
603 */
604bool ascii_check(char32_t ch)
605{
606 if (ch <= 127)
607 return true;
608
609 return false;
610}
611
612/** Check whether character is valid
613 *
614 * @return True if character is a valid Unicode code point.
615 *
616 */
617bool chr_check(char32_t ch)
618{
619 if (ch <= 1114111)
620 return true;
621
622 return false;
623}
624
625/** Compare two NULL terminated strings.
626 *
627 * Do a char-by-char comparison of two NULL-terminated strings.
628 * The strings are considered equal iff their length is equal
629 * and both strings consist of the same sequence of characters.
630 *
631 * A string S1 is less than another string S2 if it has a character with
632 * lower value at the first character position where the strings differ.
633 * If the strings differ in length, the shorter one is treated as if
634 * padded by characters with a value of zero.
635 *
636 * @param s1 First string to compare.
637 * @param s2 Second string to compare.
638 *
639 * @return 0 if the strings are equal, -1 if the first is less than the second,
640 * 1 if the second is less than the first.
641 *
642 */
643int str_cmp(const char *s1, const char *s2)
644{
645 /*
646 * UTF-8 has the nice property that lexicographic ordering on bytes is
647 * the same as the lexicographic ordering of the character sequences.
648 */
649 while (*s1 == *s2 && *s1 != 0) {
650 s1++;
651 s2++;
652 }
653
654 if (*s1 == *s2)
655 return 0;
656
657 return (*s1 < *s2) ? -1 : 1;
658}
659
660/** Compare two NULL terminated strings with length limit.
661 *
662 * Do a char-by-char comparison of two NULL-terminated strings.
663 * The strings are considered equal iff
664 * min(str_length(s1), max_len) == min(str_length(s2), max_len)
665 * and both strings consist of the same sequence of characters,
666 * up to max_len characters.
667 *
668 * A string S1 is less than another string S2 if it has a character with
669 * lower value at the first character position where the strings differ.
670 * If the strings differ in length, the shorter one is treated as if
671 * padded by characters with a value of zero. Only the first max_len
672 * characters are considered.
673 *
674 * @param s1 First string to compare.
675 * @param s2 Second string to compare.
676 * @param max_len Maximum number of characters to consider.
677 *
678 * @return 0 if the strings are equal, -1 if the first is less than the second,
679 * 1 if the second is less than the first.
680 *
681 */
682int str_lcmp(const char *s1, const char *s2, size_t max_len)
683{
684 char32_t c1 = 0;
685 char32_t c2 = 0;
686
687 size_t off1 = 0;
688 size_t off2 = 0;
689
690 size_t len = 0;
691
692 while (true) {
693 if (len >= max_len)
694 break;
695
696 c1 = str_decode(s1, &off1, STR_NO_LIMIT);
697 c2 = str_decode(s2, &off2, STR_NO_LIMIT);
698
699 if (c1 < c2)
700 return -1;
701
702 if (c1 > c2)
703 return 1;
704
705 if (c1 == 0 || c2 == 0)
706 break;
707
708 ++len;
709 }
710
711 return 0;
712
713}
714
715/** Compare two NULL terminated strings in case-insensitive manner.
716 *
717 * Do a char-by-char comparison of two NULL-terminated strings.
718 * The strings are considered equal iff their length is equal
719 * and both strings consist of the same sequence of characters
720 * when converted to lower case.
721 *
722 * A string S1 is less than another string S2 if it has a character with
723 * lower value at the first character position where the strings differ.
724 * If the strings differ in length, the shorter one is treated as if
725 * padded by characters with a value of zero.
726 *
727 * @param s1 First string to compare.
728 * @param s2 Second string to compare.
729 *
730 * @return 0 if the strings are equal, -1 if the first is less than the second,
731 * 1 if the second is less than the first.
732 *
733 */
734int str_casecmp(const char *s1, const char *s2)
735{
736 // FIXME: doesn't work for non-ASCII caseful characters
737
738 char32_t c1 = 0;
739 char32_t c2 = 0;
740
741 size_t off1 = 0;
742 size_t off2 = 0;
743
744 while (true) {
745 c1 = tolower(str_decode(s1, &off1, STR_NO_LIMIT));
746 c2 = tolower(str_decode(s2, &off2, STR_NO_LIMIT));
747
748 if (c1 < c2)
749 return -1;
750
751 if (c1 > c2)
752 return 1;
753
754 if (c1 == 0 || c2 == 0)
755 break;
756 }
757
758 return 0;
759}
760
761/** Compare two NULL terminated strings with length limit in case-insensitive
762 * manner.
763 *
764 * Do a char-by-char comparison of two NULL-terminated strings.
765 * The strings are considered equal iff
766 * min(str_length(s1), max_len) == min(str_length(s2), max_len)
767 * and both strings consist of the same sequence of characters,
768 * up to max_len characters.
769 *
770 * A string S1 is less than another string S2 if it has a character with
771 * lower value at the first character position where the strings differ.
772 * If the strings differ in length, the shorter one is treated as if
773 * padded by characters with a value of zero. Only the first max_len
774 * characters are considered.
775 *
776 * @param s1 First string to compare.
777 * @param s2 Second string to compare.
778 * @param max_len Maximum number of characters to consider.
779 *
780 * @return 0 if the strings are equal, -1 if the first is less than the second,
781 * 1 if the second is less than the first.
782 *
783 */
784int str_lcasecmp(const char *s1, const char *s2, size_t max_len)
785{
786 // FIXME: doesn't work for non-ASCII caseful characters
787
788 char32_t c1 = 0;
789 char32_t c2 = 0;
790
791 size_t off1 = 0;
792 size_t off2 = 0;
793
794 size_t len = 0;
795
796 while (true) {
797 if (len >= max_len)
798 break;
799
800 c1 = tolower(str_decode(s1, &off1, STR_NO_LIMIT));
801 c2 = tolower(str_decode(s2, &off2, STR_NO_LIMIT));
802
803 if (c1 < c2)
804 return -1;
805
806 if (c1 > c2)
807 return 1;
808
809 if (c1 == 0 || c2 == 0)
810 break;
811
812 ++len;
813 }
814
815 return 0;
816
817}
818
819static bool _test_prefix(const char *s, const char *p)
820{
821 while (*s == *p && *s != 0) {
822 s++;
823 p++;
824 }
825
826 return *p == 0;
827}
828
829/** Test whether p is a prefix of s.
830 *
831 * Do a char-by-char comparison of two NULL-terminated strings
832 * and determine if p is a prefix of s.
833 *
834 * @param s The string in which to look
835 * @param p The string to check if it is a prefix of s
836 *
837 * @return true iff p is prefix of s else false
838 *
839 */
840bool str_test_prefix(const char *s, const char *p)
841{
842 return _test_prefix(s, p);
843}
844
845/** Get a string suffix.
846 *
847 * Return a string suffix defined by the prefix length.
848 *
849 * @param s The string to get the suffix from.
850 * @param prefix_length Number of prefix characters to ignore.
851 *
852 * @return String suffix.
853 *
854 */
855const char *str_suffix(const char *s, size_t prefix_length)
856{
857 size_t off = 0;
858 size_t i = 0;
859
860 while (true) {
861 str_decode(s, &off, STR_NO_LIMIT);
862 i++;
863
864 if (i >= prefix_length)
865 break;
866 }
867
868 return s + off;
869}
870
871/** Copy string as a sequence of bytes. */
872static void _str_cpy(char *dest, const char *src)
873{
874 while (*src)
875 *(dest++) = *(src++);
876
877 *dest = 0;
878}
879
880/** Copy string as a sequence of bytes. */
881static void _str_cpyn(char *dest, size_t size, const char *src)
882{
883 char *dest_top = dest + size - 1;
884
885 while (*src && dest < dest_top)
886 *(dest++) = *(src++);
887
888 *dest = 0;
889}
890
891/** Copy string.
892 *
893 * Copy source string @a src to destination buffer @a dest.
894 * No more than @a size bytes are written. If the size of the output buffer
895 * is at least one byte, the output string will always be well-formed, i.e.
896 * null-terminated and containing only complete characters.
897 *
898 * @param dest Destination buffer.
899 * @param count Size of the destination buffer (must be > 0).
900 * @param src Source string.
901 *
902 */
903void str_cpy(char *dest, size_t size, const char *src)
904{
905 /* There must be space for a null terminator in the buffer. */
906 assert(size > 0);
907 assert(src != NULL);
908 assert(dest != NULL);
909
910 /* Copy data. */
911 _str_cpyn(dest, size, src);
912
913 /* In-place translate invalid bytes to U_SPECIAL. */
914 _repair_string(dest, size);
915}
916
917/** Copy size-limited substring.
918 *
919 * Copy prefix of string @a src of max. size @a size to destination buffer
920 * @a dest. No more than @a size bytes are written. The output string will
921 * always be well-formed, i.e. null-terminated and containing only complete
922 * characters.
923 *
924 * No more than @a n bytes are read from the input string, so it does not
925 * have to be null-terminated.
926 *
927 * @param dest Destination buffer.
928 * @param count Size of the destination buffer (must be > 0).
929 * @param src Source string.
930 * @param n Maximum number of bytes to read from @a src.
931 *
932 */
933void str_ncpy(char *dest, size_t size, const char *src, size_t n)
934{
935 /* There must be space for a null terminator in the buffer. */
936 assert(size > 0);
937 assert(src != NULL);
938
939 /* Copy data. */
940 _str_cpyn(dest, min(size, n + 1), src);
941
942 /* In-place translate invalid bytes to U_SPECIAL. */
943 _repair_string(dest, size);
944}
945
946/** Append one string to another.
947 *
948 * Append source string @a src to string in destination buffer @a dest.
949 * Size of the destination buffer is @a dest. If the size of the output buffer
950 * is at least one byte, the output string will always be well-formed, i.e.
951 * null-terminated and containing only complete characters.
952 *
953 * @param dest Destination buffer.
954 * @param count Size of the destination buffer.
955 * @param src Source string.
956 */
957void str_append(char *dest, size_t size, const char *src)
958{
959 assert(src != NULL);
960 assert(dest != NULL);
961 assert(size > 0);
962
963 size_t dstr_size = _str_nsize(dest, size);
964 _str_cpyn(dest + dstr_size, size - dstr_size, src);
965 _repair_string(dest + dstr_size, size - dstr_size);
966}
967
968/** Convert space-padded ASCII to string.
969 *
970 * Common legacy text encoding in hardware is 7-bit ASCII fitted into
971 * a fixed-width byte buffer (bit 7 always zero), right-padded with spaces
972 * (ASCII 0x20). Convert space-padded ascii to string representation.
973 *
974 * If the text does not fit into the destination buffer, the function converts
975 * as many characters as possible and returns EOVERFLOW.
976 *
977 * If the text contains non-ASCII bytes (with bit 7 set), the whole string is
978 * converted anyway and invalid characters are replaced with question marks
979 * (U_SPECIAL) and the function returns EIO.
980 *
981 * Regardless of return value upon return @a dest will always be well-formed.
982 *
983 * @param dest Destination buffer
984 * @param size Size of destination buffer
985 * @param src Space-padded ASCII.
986 * @param n Size of the source buffer in bytes.
987 *
988 * @return EOK on success, EOVERFLOW if the text does not fit
989 * destination buffer, EIO if the text contains
990 * non-ASCII bytes.
991 */
992errno_t spascii_to_str(char *dest, size_t size, const uint8_t *src, size_t n)
993{
994 size_t len = 0;
995
996 /* Determine the length of the source string. */
997 for (size_t i = 0; i < n; i++) {
998 if (src[i] == 0)
999 break;
1000
1001 if (src[i] != ' ')
1002 len = i + 1;
1003 }
1004
1005 errno_t result = EOK;
1006 size_t out_len = min(len, size - 1);
1007
1008 /* Copy characters */
1009 for (size_t i = 0; i < out_len; i++) {
1010 dest[i] = src[i];
1011
1012 if (dest[i] < 0) {
1013 dest[i] = U_SPECIAL;
1014 result = EIO;
1015 }
1016 }
1017
1018 dest[out_len] = 0;
1019
1020 if (out_len < len)
1021 return EOVERFLOW;
1022
1023 return result;
1024}
1025
1026/** Convert wide string to string.
1027 *
1028 * Convert wide string @a src to string. The output is written to the buffer
1029 * specified by @a dest and @a size. @a size must be non-zero and the string
1030 * written will always be well-formed.
1031 *
1032 * @param dest Destination buffer.
1033 * @param size Size of the destination buffer.
1034 * @param src Source wide string.
1035 */
1036void wstr_to_str(char *dest, size_t size, const char32_t *src)
1037{
1038 char32_t ch;
1039 size_t src_idx;
1040 size_t dest_off;
1041
1042 /* There must be space for a null terminator in the buffer. */
1043 assert(size > 0);
1044
1045 src_idx = 0;
1046 dest_off = 0;
1047
1048 while ((ch = src[src_idx++]) != 0) {
1049 if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
1050 break;
1051 }
1052
1053 dest[dest_off] = '\0';
1054}
1055
1056/** Convert UTF16 string to string.
1057 *
1058 * Convert utf16 string @a src to string. The output is written to the buffer
1059 * specified by @a dest and @a size. @a size must be non-zero and the string
1060 * written will always be well-formed. Surrogate pairs also supported.
1061 *
1062 * @param dest Destination buffer.
1063 * @param size Size of the destination buffer.
1064 * @param src Source utf16 string.
1065 *
1066 * @return EOK, if success, an error code otherwise.
1067 */
1068errno_t utf16_to_str(char *dest, size_t size, const uint16_t *src)
1069{
1070 size_t idx = 0, dest_off = 0;
1071 char32_t ch;
1072 errno_t rc = EOK;
1073
1074 /* There must be space for a null terminator in the buffer. */
1075 assert(size > 0);
1076
1077 while (src[idx]) {
1078 if ((src[idx] & 0xfc00) == 0xd800) {
1079 if (src[idx + 1] && (src[idx + 1] & 0xfc00) == 0xdc00) {
1080 ch = 0x10000;
1081 ch += (src[idx] & 0x03FF) << 10;
1082 ch += (src[idx + 1] & 0x03FF);
1083 idx += 2;
1084 } else
1085 break;
1086 } else {
1087 ch = src[idx];
1088 idx++;
1089 }
1090 rc = chr_encode(ch, dest, &dest_off, size - 1);
1091 if (rc != EOK)
1092 break;
1093 }
1094 dest[dest_off] = '\0';
1095 return rc;
1096}
1097
1098/** Convert string to UTF16 string.
1099 *
1100 * Convert string @a src to utf16 string. The output is written to the buffer
1101 * specified by @a dest and @a dlen. @a dlen must be non-zero and the string
1102 * written will always be well-formed. Surrogate pairs also supported.
1103 *
1104 * @param dest Destination buffer.
1105 * @param dlen Number of utf16 characters that fit in the destination buffer.
1106 * @param src Source string.
1107 *
1108 * @return EOK, if success, an error code otherwise.
1109 */
1110errno_t str_to_utf16(uint16_t *dest, size_t dlen, const char *src)
1111{
1112 errno_t rc = EOK;
1113 size_t offset = 0;
1114 size_t idx = 0;
1115 char32_t c;
1116
1117 assert(dlen > 0);
1118
1119 while ((c = str_decode(src, &offset, STR_NO_LIMIT)) != 0) {
1120 if (c > 0x10000) {
1121 if (idx + 2 >= dlen - 1) {
1122 rc = EOVERFLOW;
1123 break;
1124 }
1125 c = (c - 0x10000);
1126 dest[idx] = 0xD800 | (c >> 10);
1127 dest[idx + 1] = 0xDC00 | (c & 0x3FF);
1128 idx++;
1129 } else {
1130 dest[idx] = c;
1131 }
1132
1133 idx++;
1134 if (idx >= dlen - 1) {
1135 rc = EOVERFLOW;
1136 break;
1137 }
1138 }
1139
1140 dest[idx] = '\0';
1141 return rc;
1142}
1143
1144/** Get size of UTF-16 string.
1145 *
1146 * Get the number of words which are used by the UTF-16 string @a ustr
1147 * (excluding the NULL-terminator).
1148 *
1149 * @param ustr UTF-16 string to consider.
1150 *
1151 * @return Number of words used by the UTF-16 string
1152 *
1153 */
1154size_t utf16_wsize(const uint16_t *ustr)
1155{
1156 size_t wsize = 0;
1157
1158 while (*ustr++ != 0)
1159 wsize++;
1160
1161 return wsize;
1162}
1163
1164/** Convert wide string to new string.
1165 *
1166 * Convert wide string @a src to string. Space for the new string is allocated
1167 * on the heap.
1168 *
1169 * @param src Source wide string.
1170 * @return New string.
1171 */
1172char *wstr_to_astr(const char32_t *src)
1173{
1174 char dbuf[STR_BOUNDS(1)];
1175 char *str;
1176 char32_t ch;
1177
1178 size_t src_idx;
1179 size_t dest_off;
1180 size_t dest_size;
1181
1182 /* Compute size of encoded string. */
1183
1184 src_idx = 0;
1185 dest_size = 0;
1186
1187 while ((ch = src[src_idx++]) != 0) {
1188 dest_off = 0;
1189 if (chr_encode(ch, dbuf, &dest_off, STR_BOUNDS(1)) != EOK)
1190 break;
1191 dest_size += dest_off;
1192 }
1193
1194 str = malloc(dest_size + 1);
1195 if (str == NULL)
1196 return NULL;
1197
1198 /* Encode string. */
1199
1200 src_idx = 0;
1201 dest_off = 0;
1202
1203 while ((ch = src[src_idx++]) != 0) {
1204 if (chr_encode(ch, str, &dest_off, dest_size) != EOK)
1205 break;
1206 }
1207
1208 str[dest_size] = '\0';
1209 return str;
1210}
1211
1212/** Convert string to wide string.
1213 *
1214 * Convert string @a src to wide string. The output is written to the
1215 * buffer specified by @a dest and @a dlen. @a dlen must be non-zero
1216 * and the wide string written will always be null-terminated.
1217 *
1218 * @param dest Destination buffer.
1219 * @param dlen Length of destination buffer (number of wchars).
1220 * @param src Source string.
1221 */
1222void str_to_wstr(char32_t *dest, size_t dlen, const char *src)
1223{
1224 size_t offset;
1225 size_t di;
1226 char32_t c;
1227
1228 assert(dlen > 0);
1229
1230 offset = 0;
1231 di = 0;
1232
1233 do {
1234 if (di >= dlen - 1)
1235 break;
1236
1237 c = str_decode(src, &offset, STR_NO_LIMIT);
1238 dest[di++] = c;
1239 } while (c != '\0');
1240
1241 dest[dlen - 1] = '\0';
1242}
1243
1244/** Convert string to wide string.
1245 *
1246 * Convert string @a src to wide string. A new wide NULL-terminated
1247 * string will be allocated on the heap.
1248 *
1249 * @param src Source string.
1250 */
1251char32_t *str_to_awstr(const char *str)
1252{
1253 size_t len = str_length(str);
1254
1255 char32_t *wstr = calloc(len + 1, sizeof(char32_t));
1256 if (wstr == NULL)
1257 return NULL;
1258
1259 str_to_wstr(wstr, len + 1, str);
1260 return wstr;
1261}
1262
1263static char *_strchr(const char *str, char c)
1264{
1265 while (*str != 0 && *str != c)
1266 str++;
1267
1268 return (*str == c) ? (char *) str : NULL;
1269}
1270
1271/** Find first occurence of character in string.
1272 *
1273 * @param str String to search.
1274 * @param ch Character to look for.
1275 *
1276 * @return Pointer to character in @a str or NULL if not found.
1277 */
1278char *str_chr(const char *str, char32_t ch)
1279{
1280 /* Fast path for an ASCII character. */
1281 if (ascii_check(ch))
1282 return _strchr(str, ch);
1283
1284 /* Convert character to UTF-8. */
1285 char utf8[STR_BOUNDS(1) + 1];
1286 size_t offset = 0;
1287
1288 if (chr_encode(ch, utf8, &offset, sizeof(utf8)) != EOK || offset == 0)
1289 return NULL;
1290
1291 utf8[offset] = '\0';
1292
1293 /* Find the first byte, then check if all of them are correct. */
1294 while (*str != 0) {
1295 str = _strchr(str, utf8[0]);
1296 if (!str)
1297 return NULL;
1298
1299 if (_test_prefix(str, utf8))
1300 return (char *) str;
1301
1302 str++;
1303 }
1304
1305 return NULL;
1306}
1307
1308/** Find first occurence of substring in string.
1309 *
1310 * @param hs Haystack (string)
1311 * @param n Needle (substring to look for)
1312 *
1313 * @return Pointer to character in @a hs or @c NULL if not found.
1314 */
1315char *str_str(const char *hs, const char *n)
1316{
1317 size_t hsize = _str_size(hs);
1318 size_t nsize = _str_size(n);
1319
1320 while (hsize >= nsize) {
1321 if (_test_prefix(hs, n))
1322 return (char *) hs;
1323
1324 hs++;
1325 hsize--;
1326 }
1327
1328 return NULL;
1329}
1330
1331static void _str_rtrim(char *str, char c)
1332{
1333 char *last = str;
1334
1335 while (*str) {
1336 if (*str != c)
1337 last = str;
1338
1339 str++;
1340 }
1341
1342 /* Truncate string. */
1343 last[1] = 0;
1344}
1345
1346/** Removes specified trailing characters from a string.
1347 *
1348 * @param str String to remove from.
1349 * @param ch Character to remove.
1350 */
1351void str_rtrim(char *str, char32_t ch)
1352{
1353 /* Fast path for the ASCII case. */
1354 if (ascii_check(ch)) {
1355 _str_rtrim(str, ch);
1356 return;
1357 }
1358
1359 size_t off = 0;
1360 size_t pos = 0;
1361 char32_t c;
1362 bool update_last_chunk = true;
1363 char *last_chunk = NULL;
1364
1365 while ((c = str_decode(str, &off, STR_NO_LIMIT))) {
1366 if (c != ch) {
1367 update_last_chunk = true;
1368 last_chunk = NULL;
1369 } else if (update_last_chunk) {
1370 update_last_chunk = false;
1371 last_chunk = (str + pos);
1372 }
1373 pos = off;
1374 }
1375
1376 if (last_chunk)
1377 *last_chunk = '\0';
1378}
1379
1380static void _str_ltrim(char *str, char c)
1381{
1382 char *p = str;
1383
1384 while (*p == c)
1385 p++;
1386
1387 if (str != p)
1388 _str_cpy(str, p);
1389}
1390
1391/** Removes specified leading characters from a string.
1392 *
1393 * @param str String to remove from.
1394 * @param ch Character to remove.
1395 */
1396void str_ltrim(char *str, char32_t ch)
1397{
1398 /* Fast path for the ASCII case. */
1399 if (ascii_check(ch)) {
1400 _str_ltrim(str, ch);
1401 return;
1402 }
1403
1404 char32_t acc;
1405 size_t off = 0;
1406 size_t pos = 0;
1407 size_t str_sz = str_size(str);
1408
1409 while ((acc = str_decode(str, &off, STR_NO_LIMIT)) != 0) {
1410 if (acc != ch)
1411 break;
1412 else
1413 pos = off;
1414 }
1415
1416 if (pos > 0) {
1417 memmove(str, &str[pos], str_sz - pos);
1418 pos = str_sz - pos;
1419 str[pos] = '\0';
1420 }
1421}
1422
1423static char *_str_rchr(const char *str, char c)
1424{
1425 const char *last = NULL;
1426
1427 while (*str) {
1428 if (*str == c)
1429 last = str;
1430
1431 str++;
1432 }
1433
1434 return (char *) last;
1435}
1436
1437/** Find last occurence of character in string.
1438 *
1439 * @param str String to search.
1440 * @param ch Character to look for.
1441 *
1442 * @return Pointer to character in @a str or NULL if not found.
1443 */
1444char *str_rchr(const char *str, char32_t ch)
1445{
1446 if (ascii_check(ch))
1447 return _str_rchr(str, ch);
1448
1449 char32_t acc;
1450 size_t off = 0;
1451 size_t last = 0;
1452 const char *res = NULL;
1453
1454 while ((acc = str_decode(str, &off, STR_NO_LIMIT)) != 0) {
1455 if (acc == ch)
1456 res = (str + last);
1457 last = off;
1458 }
1459
1460 return (char *) res;
1461}
1462
1463/** Insert a wide character into a wide string.
1464 *
1465 * Insert a wide character into a wide string at position
1466 * @a pos. The characters after the position are shifted.
1467 *
1468 * @param str String to insert to.
1469 * @param ch Character to insert to.
1470 * @param pos Character index where to insert.
1471 * @param max_pos Characters in the buffer.
1472 *
1473 * @return True if the insertion was sucessful, false if the position
1474 * is out of bounds.
1475 *
1476 */
1477bool wstr_linsert(char32_t *str, char32_t ch, size_t pos, size_t max_pos)
1478{
1479 size_t len = wstr_length(str);
1480
1481 if ((pos > len) || (pos + 1 > max_pos))
1482 return false;
1483
1484 size_t i;
1485 for (i = len; i + 1 > pos; i--)
1486 str[i + 1] = str[i];
1487
1488 str[pos] = ch;
1489
1490 return true;
1491}
1492
1493/** Remove a wide character from a wide string.
1494 *
1495 * Remove a wide character from a wide string at position
1496 * @a pos. The characters after the position are shifted.
1497 *
1498 * @param str String to remove from.
1499 * @param pos Character index to remove.
1500 *
1501 * @return True if the removal was sucessful, false if the position
1502 * is out of bounds.
1503 *
1504 */
1505bool wstr_remove(char32_t *str, size_t pos)
1506{
1507 size_t len = wstr_length(str);
1508
1509 if (pos >= len)
1510 return false;
1511
1512 size_t i;
1513 for (i = pos + 1; i <= len; i++)
1514 str[i - 1] = str[i];
1515
1516 return true;
1517}
1518
1519/** Duplicate string.
1520 *
1521 * Allocate a new string and copy characters from the source
1522 * string into it. The duplicate string is allocated via sleeping
1523 * malloc(), thus this function can sleep in no memory conditions.
1524 *
1525 * The allocation cannot fail and the return value is always
1526 * a valid pointer. The duplicate string is always a well-formed
1527 * null-terminated UTF-8 string, but it can differ from the source
1528 * string on the byte level.
1529 *
1530 * @param src Source string.
1531 *
1532 * @return Duplicate string.
1533 *
1534 */
1535char *str_dup(const char *src)
1536{
1537 size_t size = _str_size(src) + 1;
1538 char *dest = malloc(size);
1539 if (!dest)
1540 return NULL;
1541
1542 _str_cpy(dest, src);
1543 _repair_string(dest, size);
1544 return dest;
1545}
1546
1547/** Duplicate string with size limit.
1548 *
1549 * Allocate a new string and copy up to @max_size bytes from the source
1550 * string into it. The duplicate string is allocated via sleeping
1551 * malloc(), thus this function can sleep in no memory conditions.
1552 * No more than @max_size + 1 bytes is allocated, but if the size
1553 * occupied by the source string is smaller than @max_size + 1,
1554 * less is allocated.
1555 *
1556 * The allocation cannot fail and the return value is always
1557 * a valid pointer. The duplicate string is always a well-formed
1558 * null-terminated UTF-8 string, but it can differ from the source
1559 * string on the byte level.
1560 *
1561 * @param src Source string.
1562 * @param n Maximum number of bytes to duplicate.
1563 *
1564 * @return Duplicate string.
1565 *
1566 */
1567char *str_ndup(const char *src, size_t n)
1568{
1569 size_t size = _str_nsize(src, n) + 1;
1570
1571 char *dest = malloc(size);
1572 if (!dest)
1573 return NULL;
1574
1575 _str_cpyn(dest, size, src);
1576 _repair_string(dest, size);
1577 return dest;
1578}
1579
1580/** Split string by delimiters.
1581 *
1582 * @param s String to be tokenized. May not be NULL.
1583 * @param delim String with the delimiters.
1584 * @param next Variable which will receive the pointer to the
1585 * continuation of the string following the first
1586 * occurrence of any of the delimiter characters.
1587 * May be NULL.
1588 * @return Pointer to the prefix of @a s before the first
1589 * delimiter character. NULL if no such prefix
1590 * exists.
1591 */
1592char *str_tok(char *s, const char *delim, char **next)
1593{
1594 char *start, *end;
1595
1596 if (!s)
1597 return NULL;
1598
1599 size_t len = str_size(s);
1600 size_t cur;
1601 size_t tmp;
1602 char32_t ch;
1603
1604 /* Skip over leading delimiters. */
1605 tmp = 0;
1606 cur = 0;
1607 while ((ch = str_decode(s, &tmp, len)) && str_chr(delim, ch))
1608 cur = tmp;
1609 start = &s[cur];
1610
1611 /* Skip over token characters. */
1612 tmp = cur;
1613 while ((ch = str_decode(s, &tmp, len)) && !str_chr(delim, ch))
1614 cur = tmp;
1615 end = &s[cur];
1616 if (next)
1617 *next = (ch ? &s[tmp] : &s[cur]);
1618
1619 if (start == end)
1620 return NULL; /* No more tokens. */
1621
1622 /* Overwrite delimiter with NULL terminator. */
1623 *end = '\0';
1624 return start;
1625}
1626
1627void order_suffix(const uint64_t val, uint64_t *rv, char *suffix)
1628{
1629 if (val > UINT64_C(10000000000000000000)) {
1630 *rv = val / UINT64_C(1000000000000000000);
1631 *suffix = 'Z';
1632 } else if (val > UINT64_C(1000000000000000000)) {
1633 *rv = val / UINT64_C(1000000000000000);
1634 *suffix = 'E';
1635 } else if (val > UINT64_C(1000000000000000)) {
1636 *rv = val / UINT64_C(1000000000000);
1637 *suffix = 'T';
1638 } else if (val > UINT64_C(1000000000000)) {
1639 *rv = val / UINT64_C(1000000000);
1640 *suffix = 'G';
1641 } else if (val > UINT64_C(1000000000)) {
1642 *rv = val / UINT64_C(1000000);
1643 *suffix = 'M';
1644 } else if (val > UINT64_C(1000000)) {
1645 *rv = val / UINT64_C(1000);
1646 *suffix = 'k';
1647 } else {
1648 *rv = val;
1649 *suffix = ' ';
1650 }
1651}
1652
1653void bin_order_suffix(const uint64_t val, uint64_t *rv, const char **suffix,
1654 bool fixed)
1655{
1656 if (val > UINT64_C(1152921504606846976)) {
1657 *rv = val / UINT64_C(1125899906842624);
1658 *suffix = "EiB";
1659 } else if (val > UINT64_C(1125899906842624)) {
1660 *rv = val / UINT64_C(1099511627776);
1661 *suffix = "TiB";
1662 } else if (val > UINT64_C(1099511627776)) {
1663 *rv = val / UINT64_C(1073741824);
1664 *suffix = "GiB";
1665 } else if (val > UINT64_C(1073741824)) {
1666 *rv = val / UINT64_C(1048576);
1667 *suffix = "MiB";
1668 } else if (val > UINT64_C(1048576)) {
1669 *rv = val / UINT64_C(1024);
1670 *suffix = "KiB";
1671 } else {
1672 *rv = val;
1673 if (fixed)
1674 *suffix = "B ";
1675 else
1676 *suffix = "B";
1677 }
1678}
1679
1680/** @}
1681 */
Note: See TracBrowser for help on using the repository browser.