ucx/string.c

changeset 101
7b3a3130be44
parent 49
2f71f4ee247a
equal deleted inserted replaced
100:d2bd73d28ff1 101:7b3a3130be44
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28 #define CX_STR_IMPLEMENTATION
29 #include "cx/string.h" 29 #include "cx/string.h"
30 #include "cx/utils.h"
31 30
32 #include <string.h> 31 #include <string.h>
33 #include <stdarg.h> 32 #include <stdarg.h>
34 #include <ctype.h> 33 #include <ctype.h>
35 34 #include <assert.h>
36 #ifndef _WIN32 35 #include <errno.h>
37 36 #include <limits.h>
38 #include <strings.h> // for strncasecmp() 37 #include <float.h>
39 38
40 #endif // _WIN32 39 #ifdef _WIN32
40 #define cx_strcasecmp_impl _strnicmp
41 #else
42 #include <strings.h>
43 #define cx_strcasecmp_impl strncasecmp
44 #endif
41 45
42 cxmutstr cx_mutstr(char *cstring) { 46 cxmutstr cx_mutstr(char *cstring) {
43 return (cxmutstr) {cstring, strlen(cstring)}; 47 return (cxmutstr) {cstring, strlen(cstring)};
44 } 48 }
45 49
59 size_t length 63 size_t length
60 ) { 64 ) {
61 return (cxstring) {cstring, length}; 65 return (cxstring) {cstring, length};
62 } 66 }
63 67
64 cxstring cx_strcast(cxmutstr str) {
65 return (cxstring) {str.ptr, str.length};
66 }
67
68 void cx_strfree(cxmutstr *str) { 68 void cx_strfree(cxmutstr *str) {
69 if (str == NULL) return;
69 free(str->ptr); 70 free(str->ptr);
70 str->ptr = NULL; 71 str->ptr = NULL;
71 str->length = 0; 72 str->length = 0;
72 } 73 }
73 74
74 void cx_strfree_a( 75 void cx_strfree_a(
75 const CxAllocator *alloc, 76 const CxAllocator *alloc,
76 cxmutstr *str 77 cxmutstr *str
77 ) { 78 ) {
79 if (str == NULL) return;
78 cxFree(alloc, str->ptr); 80 cxFree(alloc, str->ptr);
79 str->ptr = NULL; 81 str->ptr = NULL;
80 str->length = 0; 82 str->length = 0;
81 } 83 }
82 84
87 if (count == 0) return 0; 89 if (count == 0) return 0;
88 90
89 va_list ap; 91 va_list ap;
90 va_start(ap, count); 92 va_start(ap, count);
91 size_t size = 0; 93 size_t size = 0;
92 cx_for_n(i, count) { 94 for (size_t i = 0; i < count; i++) {
93 cxstring str = va_arg(ap, cxstring); 95 cxstring str = va_arg(ap, cxstring);
96 if (size > SIZE_MAX - str.length) errno = EOVERFLOW;
94 size += str.length; 97 size += str.length;
95 } 98 }
96 va_end(ap); 99 va_end(ap);
97 100
98 return size; 101 return size;
104 size_t count, 107 size_t count,
105 ... 108 ...
106 ) { 109 ) {
107 if (count == 0) return str; 110 if (count == 0) return str;
108 111
109 cxstring *strings = calloc(count, sizeof(cxstring)); 112 cxstring strings_stack[8];
110 if (!strings) abort(); 113 cxstring *strings;
114 if (count > 8) {
115 strings = calloc(count, sizeof(cxstring));
116 if (strings == NULL) {
117 return (cxmutstr) {NULL, 0};
118 }
119 } else {
120 strings = strings_stack;
121 }
111 122
112 va_list ap; 123 va_list ap;
113 va_start(ap, count); 124 va_start(ap, count);
114 125
115 // get all args and overall length 126 // get all args and overall length
127 bool overflow = false;
116 size_t slen = str.length; 128 size_t slen = str.length;
117 cx_for_n(i, count) { 129 for (size_t i = 0; i < count; i++) {
118 cxstring s = va_arg (ap, cxstring); 130 cxstring s = va_arg (ap, cxstring);
119 strings[i] = s; 131 strings[i] = s;
132 if (slen > SIZE_MAX - str.length) overflow = true;
120 slen += s.length; 133 slen += s.length;
121 } 134 }
122 va_end(ap); 135 va_end(ap);
123 136
137 // abort in case of overflow
138 if (overflow) {
139 errno = EOVERFLOW;
140 if (strings != strings_stack) {
141 free(strings);
142 }
143 return (cxmutstr) { NULL, 0 };
144 }
145
124 // reallocate or create new string 146 // reallocate or create new string
147 char *newstr;
125 if (str.ptr == NULL) { 148 if (str.ptr == NULL) {
126 str.ptr = cxMalloc(alloc, slen + 1); 149 newstr = cxMalloc(alloc, slen + 1);
127 } else { 150 } else {
128 str.ptr = cxRealloc(alloc, str.ptr, slen + 1); 151 newstr = cxRealloc(alloc, str.ptr, slen + 1);
129 } 152 }
130 if (str.ptr == NULL) abort(); 153 if (newstr == NULL) {
154 if (strings != strings_stack) {
155 free(strings);
156 }
157 return (cxmutstr) {NULL, 0};
158 }
159 str.ptr = newstr;
131 160
132 // concatenate strings 161 // concatenate strings
133 size_t pos = str.length; 162 size_t pos = str.length;
134 str.length = slen; 163 str.length = slen;
135 cx_for_n(i, count) { 164 for (size_t i = 0; i < count; i++) {
136 cxstring s = strings[i]; 165 cxstring s = strings[i];
137 memcpy(str.ptr + pos, s.ptr, s.length); 166 memcpy(str.ptr + pos, s.ptr, s.length);
138 pos += s.length; 167 pos += s.length;
139 } 168 }
140 169
141 // terminate string 170 // terminate string
142 str.ptr[str.length] = '\0'; 171 str.ptr[str.length] = '\0';
143 172
144 // free temporary array 173 // free temporary array
145 free(strings); 174 if (strings != strings_stack) {
175 free(strings);
176 }
146 177
147 return str; 178 return str;
148 } 179 }
149 180
150 cxstring cx_strsubs( 181 cxstring cx_strsubs(
191 cxstring string, 222 cxstring string,
192 int chr 223 int chr
193 ) { 224 ) {
194 chr = 0xFF & chr; 225 chr = 0xFF & chr;
195 // TODO: improve by comparing multiple bytes at once 226 // TODO: improve by comparing multiple bytes at once
196 cx_for_n(i, string.length) { 227 for (size_t i = 0; i < string.length; i++) {
197 if (string.ptr[i] == chr) { 228 if (string.ptr[i] == chr) {
198 return cx_strsubs(string, i); 229 return cx_strsubs(string, i);
199 } 230 }
200 } 231 }
201 return (cxstring) {NULL, 0}; 232 return (cxstring) {NULL, 0};
234 } 265 }
235 266
236 #ifndef CX_STRSTR_SBO_SIZE 267 #ifndef CX_STRSTR_SBO_SIZE
237 #define CX_STRSTR_SBO_SIZE 512 268 #define CX_STRSTR_SBO_SIZE 512
238 #endif 269 #endif
239 unsigned const cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE; 270 const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
240 271
241 cxstring cx_strstr( 272 cxstring cx_strstr(
242 cxstring haystack, 273 cxstring haystack,
243 cxstring needle 274 cxstring needle
244 ) { 275 ) {
432 int cx_strcmp( 463 int cx_strcmp(
433 cxstring s1, 464 cxstring s1,
434 cxstring s2 465 cxstring s2
435 ) { 466 ) {
436 if (s1.length == s2.length) { 467 if (s1.length == s2.length) {
437 return memcmp(s1.ptr, s2.ptr, s1.length); 468 return strncmp(s1.ptr, s2.ptr, s1.length);
438 } else if (s1.length > s2.length) { 469 } else if (s1.length > s2.length) {
470 int r = strncmp(s1.ptr, s2.ptr, s2.length);
471 if (r != 0) return r;
439 return 1; 472 return 1;
440 } else { 473 } else {
474 int r = strncmp(s1.ptr, s2.ptr, s1.length);
475 if (r != 0) return r;
441 return -1; 476 return -1;
442 } 477 }
443 } 478 }
444 479
445 int cx_strcasecmp( 480 int cx_strcasecmp(
446 cxstring s1, 481 cxstring s1,
447 cxstring s2 482 cxstring s2
448 ) { 483 ) {
449 if (s1.length == s2.length) { 484 if (s1.length == s2.length) {
450 #ifdef _WIN32 485 return cx_strcasecmp_impl(s1.ptr, s2.ptr, s1.length);
451 return _strnicmp(s1.ptr, s2.ptr, s1.length);
452 #else
453 return strncasecmp(s1.ptr, s2.ptr, s1.length);
454 #endif
455 } else if (s1.length > s2.length) { 486 } else if (s1.length > s2.length) {
487 int r = cx_strcasecmp_impl(s1.ptr, s2.ptr, s2.length);
488 if (r != 0) return r;
456 return 1; 489 return 1;
457 } else { 490 } else {
491 int r = cx_strcasecmp_impl(s1.ptr, s2.ptr, s1.length);
492 if (r != 0) return r;
458 return -1; 493 return -1;
459 } 494 }
460 } 495 }
461 496
462 int cx_strcmp_p( 497 int cx_strcmp_p(
554 suffix.ptr, suffix.length) == 0; 589 suffix.ptr, suffix.length) == 0;
555 #endif 590 #endif
556 } 591 }
557 592
558 void cx_strlower(cxmutstr string) { 593 void cx_strlower(cxmutstr string) {
559 cx_for_n(i, string.length) { 594 for (size_t i = 0; i < string.length; i++) {
560 string.ptr[i] = (char) tolower(string.ptr[i]); 595 string.ptr[i] = (char) tolower(string.ptr[i]);
561 } 596 }
562 } 597 }
563 598
564 void cx_strupper(cxmutstr string) { 599 void cx_strupper(cxmutstr string) {
565 cx_for_n(i, string.length) { 600 for (size_t i = 0; i < string.length; i++) {
566 string.ptr[i] = (char) toupper(string.ptr[i]); 601 string.ptr[i] = (char) toupper(string.ptr[i]);
567 } 602 }
568 } 603 }
569 604
570 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE 605 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
746 delim.length = ctx->delim.length; 781 delim.length = ctx->delim.length;
747 } 782 }
748 783
749 // if more delimiters are specified, check them now 784 // if more delimiters are specified, check them now
750 if (ctx->delim_more_count > 0) { 785 if (ctx->delim_more_count > 0) {
751 cx_for_n(i, ctx->delim_more_count) { 786 for (size_t i = 0; i < ctx->delim_more_count; i++) {
752 cxstring d = cx_strstr(haystack, ctx->delim_more[i]); 787 cxstring d = cx_strstr(haystack, ctx->delim_more[i]);
753 if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) { 788 if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) {
754 delim.ptr = d.ptr; 789 delim.ptr = d.ptr;
755 delim.length = ctx->delim_more[i].length; 790 delim.length = ctx->delim_more[i].length;
756 } 791 }
782 size_t count 817 size_t count
783 ) { 818 ) {
784 ctx->delim_more = delim; 819 ctx->delim_more = delim;
785 ctx->delim_more_count = count; 820 ctx->delim_more_count = count;
786 } 821 }
822
823 #define cx_strtoX_signed_impl(rtype, rmin, rmax) \
824 long long result; \
825 if (cx_strtoll_lc(str, &result, base, groupsep)) { \
826 return -1; \
827 } \
828 if (result < rmin || result > rmax) { \
829 errno = ERANGE; \
830 return -1; \
831 } \
832 *output = (rtype) result; \
833 return 0
834
835 int cx_strtos_lc(cxstring str, short *output, int base, const char *groupsep) {
836 cx_strtoX_signed_impl(short, SHRT_MIN, SHRT_MAX);
837 }
838
839 int cx_strtoi_lc(cxstring str, int *output, int base, const char *groupsep) {
840 cx_strtoX_signed_impl(int, INT_MIN, INT_MAX);
841 }
842
843 int cx_strtol_lc(cxstring str, long *output, int base, const char *groupsep) {
844 cx_strtoX_signed_impl(long, LONG_MIN, LONG_MAX);
845 }
846
847 int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep) {
848 // strategy: parse as unsigned, check range, negate if required
849 bool neg = false;
850 size_t start_unsigned = 0;
851
852 // trim already, to search for a sign character
853 str = cx_strtrim(str);
854 if (str.length == 0) {
855 errno = EINVAL;
856 return -1;
857 }
858
859 // test if we have a negative sign character
860 if (str.ptr[start_unsigned] == '-') {
861 neg = true;
862 start_unsigned++;
863 // must not be followed by positive sign character
864 if (str.length == 1 || str.ptr[start_unsigned] == '+') {
865 errno = EINVAL;
866 return -1;
867 }
868 }
869
870 // now parse the number with strtoull
871 unsigned long long v;
872 cxstring ustr = start_unsigned == 0 ? str
873 : cx_strn(str.ptr + start_unsigned, str.length - start_unsigned);
874 int ret = cx_strtoull_lc(ustr, &v, base, groupsep);
875 if (ret != 0) return ret;
876 if (neg) {
877 if (v - 1 > LLONG_MAX) {
878 errno = ERANGE;
879 return -1;
880 }
881 *output = -(long long) v;
882 return 0;
883 } else {
884 if (v > LLONG_MAX) {
885 errno = ERANGE;
886 return -1;
887 }
888 *output = (long long) v;
889 return 0;
890 }
891 }
892
893 int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep) {
894 cx_strtoX_signed_impl(int8_t, INT8_MIN, INT8_MAX);
895 }
896
897 int cx_strtoi16_lc(cxstring str, int16_t *output, int base, const char *groupsep) {
898 cx_strtoX_signed_impl(int16_t, INT16_MIN, INT16_MAX);
899 }
900
901 int cx_strtoi32_lc(cxstring str, int32_t *output, int base, const char *groupsep) {
902 cx_strtoX_signed_impl(int32_t, INT32_MIN, INT32_MAX);
903 }
904
905 int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep) {
906 assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms
907 return cx_strtoll_lc(str, (long long*) output, base, groupsep);
908 }
909
910 int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep) {
911 #if SSIZE_MAX == INT32_MAX
912 return cx_strtoi32_lc(str, (int32_t*) output, base, groupsep);
913 #elif SSIZE_MAX == INT64_MAX
914 return cx_strtoll_lc(str, (long long*) output, base, groupsep);
915 #else
916 #error "unsupported ssize_t size"
917 #endif
918 }
919
920 #define cx_strtoX_unsigned_impl(rtype, rmax) \
921 uint64_t result; \
922 if (cx_strtou64_lc(str, &result, base, groupsep)) { \
923 return -1; \
924 } \
925 if (result > rmax) { \
926 errno = ERANGE; \
927 return -1; \
928 } \
929 *output = (rtype) result; \
930 return 0
931
932 int cx_strtous_lc(cxstring str, unsigned short *output, int base, const char *groupsep) {
933 cx_strtoX_unsigned_impl(unsigned short, USHRT_MAX);
934 }
935
936 int cx_strtou_lc(cxstring str, unsigned int *output, int base, const char *groupsep) {
937 cx_strtoX_unsigned_impl(unsigned int, UINT_MAX);
938 }
939
940 int cx_strtoul_lc(cxstring str, unsigned long *output, int base, const char *groupsep) {
941 cx_strtoX_unsigned_impl(unsigned long, ULONG_MAX);
942 }
943
944 int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep) {
945 // some sanity checks
946 str = cx_strtrim(str);
947 if (str.length == 0) {
948 errno = EINVAL;
949 return -1;
950 }
951 if (!(base == 2 || base == 8 || base == 10 || base == 16)) {
952 errno = EINVAL;
953 return -1;
954 }
955 if (groupsep == NULL) groupsep = "";
956
957 // find the actual start of the number
958 if (str.ptr[0] == '+') {
959 str.ptr++;
960 str.length--;
961 if (str.length == 0) {
962 errno = EINVAL;
963 return -1;
964 }
965 }
966 size_t start = 0;
967
968 // if base is 2 or 16, some leading stuff may appear
969 if (base == 2) {
970 if ((str.ptr[0] | 32) == 'b') {
971 start = 1;
972 } else if (str.ptr[0] == '0' && str.length > 1) {
973 if ((str.ptr[1] | 32) == 'b') {
974 start = 2;
975 }
976 }
977 } else if (base == 16) {
978 if ((str.ptr[0] | 32) == 'x' || str.ptr[0] == '#') {
979 start = 1;
980 } else if (str.ptr[0] == '0' && str.length > 1) {
981 if ((str.ptr[1] | 32) == 'x') {
982 start = 2;
983 }
984 }
985 }
986
987 // check if there are digits left
988 if (start >= str.length) {
989 errno = EINVAL;
990 return -1;
991 }
992
993 // now parse the number
994 unsigned long long result = 0;
995 for (size_t i = start; i < str.length; i++) {
996 // ignore group separators
997 if (strchr(groupsep, str.ptr[i])) continue;
998
999 // determine the digit value of the character
1000 unsigned char c = str.ptr[i];
1001 if (c >= 'a') c = 10 + (c - 'a');
1002 else if (c >= 'A') c = 10 + (c - 'A');
1003 else if (c >= '0') c = c - '0';
1004 else c = 255;
1005 if (c >= base) {
1006 errno = EINVAL;
1007 return -1;
1008 }
1009
1010 // now combine the digit with what we already have
1011 unsigned long right = (result & 0xff) * base + c;
1012 unsigned long long left = (result >> 8) * base + (right >> 8);
1013 if (left > (ULLONG_MAX >> 8)) {
1014 errno = ERANGE;
1015 return -1;
1016 }
1017 result = (left << 8) + (right & 0xff);
1018 }
1019
1020 *output = result;
1021 return 0;
1022 }
1023
1024 int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep) {
1025 cx_strtoX_unsigned_impl(uint8_t, UINT8_MAX);
1026 }
1027
1028 int cx_strtou16_lc(cxstring str, uint16_t *output, int base, const char *groupsep) {
1029 cx_strtoX_unsigned_impl(uint16_t, UINT16_MAX);
1030 }
1031
1032 int cx_strtou32_lc(cxstring str, uint32_t *output, int base, const char *groupsep) {
1033 cx_strtoX_unsigned_impl(uint32_t, UINT32_MAX);
1034 }
1035
1036 int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep) {
1037 assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms
1038 return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep);
1039 }
1040
1041 int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep) {
1042 #if SIZE_MAX == UINT32_MAX
1043 return cx_strtou32_lc(str, (uint32_t*) output, base, groupsep);
1044 #elif SIZE_MAX == UINT64_MAX
1045 return cx_strtoull_lc(str, (unsigned long long *) output, base, groupsep);
1046 #else
1047 #error "unsupported size_t size"
1048 #endif
1049 }
1050
1051 int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) {
1052 // use string to double and add a range check
1053 double d;
1054 int ret = cx_strtod_lc(str, &d, decsep, groupsep);
1055 if (ret != 0) return ret;
1056 // note: FLT_MIN is the smallest POSITIVE number that can be represented
1057 double test = d < 0 ? -d : d;
1058 if (test < FLT_MIN || test > FLT_MAX) {
1059 errno = ERANGE;
1060 return -1;
1061 }
1062 *output = (float) d;
1063 return 0;
1064 }
1065
1066 int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) {
1067 // TODO: overflow check
1068 // TODO: increase precision
1069
1070 // trim and check
1071 str = cx_strtrim(str);
1072 if (str.length == 0) {
1073 errno = EINVAL;
1074 return -1;
1075 }
1076
1077 double result = 0.;
1078 int sign = 1;
1079
1080 // check if there is a sign
1081 if (str.ptr[0] == '-') {
1082 sign = -1;
1083 str.ptr++;
1084 str.length--;
1085 } else if (str.ptr[0] == '+') {
1086 str.ptr++;
1087 str.length--;
1088 }
1089
1090 // there must be at least one char to parse
1091 if (str.length == 0) {
1092 errno = EINVAL;
1093 return -1;
1094 }
1095
1096 // parse all digits until we find the decsep
1097 size_t pos = 0;
1098 do {
1099 if (isdigit(str.ptr[pos])) {
1100 result = result * 10 + (str.ptr[pos] - '0');
1101 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1102 break;
1103 }
1104 } while (++pos < str.length);
1105
1106 // already done?
1107 if (pos == str.length) {
1108 *output = result * sign;
1109 return 0;
1110 }
1111
1112 // is the next char the decsep?
1113 if (str.ptr[pos] == decsep) {
1114 pos++;
1115 // it may end with the decsep, if it did not start with it
1116 if (pos == str.length) {
1117 if (str.length == 1) {
1118 errno = EINVAL;
1119 return -1;
1120 } else {
1121 *output = result * sign;
1122 return 0;
1123 }
1124 }
1125 // parse everything until exponent or end
1126 double factor = 1.;
1127 do {
1128 if (isdigit(str.ptr[pos])) {
1129 factor *= 0.1;
1130 result = result + factor * (str.ptr[pos] - '0');
1131 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1132 break;
1133 }
1134 } while (++pos < str.length);
1135 }
1136
1137 // no exponent?
1138 if (pos == str.length) {
1139 *output = result * sign;
1140 return 0;
1141 }
1142
1143 // now the next separator MUST be the exponent separator
1144 // and at least one char must follow
1145 if ((str.ptr[pos] | 32) != 'e' || str.length <= pos + 1) {
1146 errno = EINVAL;
1147 return -1;
1148 }
1149 pos++;
1150
1151 // check if we have a sign for the exponent
1152 double factor = 10.;
1153 if (str.ptr[pos] == '-') {
1154 factor = .1;
1155 pos++;
1156 } else if (str.ptr[pos] == '+') {
1157 pos++;
1158 }
1159
1160 // at least one digit must follow
1161 if (pos == str.length) {
1162 errno = EINVAL;
1163 return -1;
1164 }
1165
1166 // parse the exponent
1167 unsigned int exp = 0;
1168 do {
1169 if (isdigit(str.ptr[pos])) {
1170 exp = 10 * exp + (str.ptr[pos] - '0');
1171 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1172 errno = EINVAL;
1173 return -1;
1174 }
1175 } while (++pos < str.length);
1176
1177 // apply the exponent by fast exponentiation
1178 do {
1179 if (exp & 1) {
1180 result *= factor;
1181 }
1182 factor *= factor;
1183 } while ((exp >>= 1) > 0);
1184
1185 // store the result and exit
1186 *output = result * sign;
1187 return 0;
1188 }

mercurial