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 	}