1 /** 2 * License: MIT 3 */ 4 module jansson_d.strconv; 5 6 7 package: 8 9 private static import core.stdc.errno; 10 private static import core.stdc.math; 11 private static import core.stdc.stdlib; 12 private static import core.stdc.string; 13 private static import jansson_d.jansson_config; 14 private static import jansson_d.jansson_private; 15 private static import jansson_d.strbuffer; 16 17 static if (jansson_d.jansson_config.JSON_HAVE_LOCALECONV) { 18 private static import core.stdc.locale; 19 20 /* 21 * - This code assumes that the decimal separator is exactly one 22 * character. 23 * 24 * - If setlocale() is called by another thread between the call to 25 * localeconv() and the call to sprintf() or strtod(), the result may 26 * be wrong. setlocale() is not thread-safe and should not be used 27 * this way. Multi-threaded programs should use uselocale() instead. 28 */ 29 30 nothrow @trusted @nogc 31 private void to_locale(scope jansson_d.strbuffer.strbuffer_t* strbuffer) 32 33 in 34 { 35 assert(strbuffer != null); 36 } 37 38 do 39 { 40 const char* point = core.stdc.locale.localeconv().decimal_point; 41 42 if (*point == '.') { 43 /* No conversion needed */ 44 return; 45 } 46 47 char* pos = core.stdc..string.strchr(strbuffer.value, '.'); 48 49 if (pos != null) { 50 *pos = *point; 51 } 52 } 53 54 nothrow @trusted @nogc 55 private void from_locale(scope char* buffer) 56 57 in 58 { 59 assert(buffer != null); 60 } 61 62 do 63 { 64 const char* point = core.stdc.locale.localeconv().decimal_point; 65 66 if (*point == '.') { 67 /* No conversion needed */ 68 return; 69 } 70 71 char* pos = core.stdc..string.strchr(buffer, *point); 72 73 if (pos != null) { 74 *pos = '.'; 75 } 76 } 77 } 78 79 nothrow @trusted @nogc 80 bool jsonp_strtod(scope jansson_d.strbuffer.strbuffer_t* strbuffer, scope double* out_) 81 82 in 83 { 84 assert(strbuffer != null); 85 assert(out_ != null); 86 } 87 88 do 89 { 90 static if (jansson_d.jansson_config.JSON_HAVE_LOCALECONV) { 91 .to_locale(strbuffer); 92 } 93 94 core.stdc.errno.errno = 0; 95 char* end = null; 96 double value = core.stdc.stdlib.strtod(strbuffer.value, &end); 97 assert(end == (strbuffer.value + strbuffer.length_)); 98 99 if (((value == core.stdc.math.HUGE_VAL) || (value == -core.stdc.math.HUGE_VAL)) && (core.stdc.errno.errno == core.stdc.errno.ERANGE)) { 100 /* Overflow */ 101 return false; 102 } 103 104 *out_ = value; 105 106 return true; 107 } 108 109 nothrow @trusted @nogc 110 int jsonp_dtostr(scope char* buffer, size_t size, double value, int precision) 111 112 in 113 { 114 assert(buffer != null); 115 } 116 117 do 118 { 119 if (precision == 0) { 120 precision = 17; 121 } 122 123 int ret = jansson_d.jansson_private.snprintf(buffer, size, "%.*g", precision, value); 124 125 if (ret < 0) { 126 return -1; 127 } 128 129 size_t length_ = cast(size_t)(ret); 130 131 if (length_ >= size) { 132 return -1; 133 } 134 135 static if (jansson_d.jansson_config.JSON_HAVE_LOCALECONV) { 136 .from_locale(buffer); 137 } 138 139 /* 140 * Make sure there's a dot or 'e' in the output. Otherwise 141 * a real is converted to an integer when decoding 142 */ 143 if ((core.stdc..string.strchr(buffer, '.') == null) && (core.stdc..string.strchr(buffer, 'e') == null)) { 144 if ((length_ + 3) >= size) { 145 /* No space to append ".0" */ 146 return -1; 147 } 148 149 buffer[length_] = '.'; 150 buffer[length_ + 1] = '0'; 151 buffer[length_ + 2] = '\0'; 152 length_ += 2; 153 } 154 155 /* 156 * Remove leading '+' from positive exponent. Also remove leading 157 * zeros from exponents (added by some printf() implementations) 158 */ 159 char* start = core.stdc..string.strchr(buffer, 'e'); 160 161 if (start != null) { 162 start++; 163 char* end = start + 1; 164 165 if (*start == '-') { 166 start++; 167 } 168 169 while (*end == '0') { 170 end++; 171 } 172 173 if (end != start) { 174 core.stdc..string.memmove(start, end, length_ - cast(size_t)(end - buffer)); 175 length_ -= cast(size_t)(end - start); 176 } 177 } 178 179 return cast(int)(length_); 180 }