1 /**
2     nulib conversion functions.
3 
4     Copyright:
5         Copyright © 2023-2025, Kitsunebi Games
6         Copyright © 2023-2025, Inochi2D Project
7     
8     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9     Authors:   Luna Nielsen
10 */
11 module nulib.conv;
12 import nulib.c.stdlib;
13 import nulib.c.stdio;
14 import nulib.string;
15 import nulib.math;
16 import nulib.text.ascii;
17 import numem.core.traits;
18 import numem.core.hooks;
19 import numem.core.exception;
20 
21 // TODO:    This entire module should be rewritten into pure D.
22 //          Relying on the C standard library here is probably
23 //          not the best idea for portability.
24 
25 /**
26     Converts the given string slice to an integral value.
27 
28     Params:
29         str = The string to convert.
30         base = The base to read the string as.
31     
32     Returns:
33         The integral value on success, or $(D 0) on failure.
34 */
35 T to_integral(T)(inout(char)[] str, int base = 10) @nogc nothrow if (__traits(isIntegral, T)) {
36     if (str.length == 0)
37         return T.init;
38     
39     auto tmp = _tmpbuffer!8(str);
40     ulong out_;
41     switch(base) {
42         case 8:
43             cast(void)sscanf(tmp.ptr, "%llo", &out_);
44             return cast(T)out_;
45 
46         case 10:
47             static if (T.sizeof <= 4)
48                 return cast(T)atoi(tmp.ptr);
49             else
50                 return cast(T)atoll(tmp.ptr);
51 
52         case 16:
53             cast(void)sscanf(tmp.ptr, "%8llx", &out_);
54             return cast(T)out_;
55 
56         default:
57             return T.init;
58     }
59 }
60 
61 /// ditto
62 alias toInt = to_integral;
63 
64 /**
65     Converts the given string slice to an floating point value.
66 
67     Params:
68         str = The string to convert.
69     
70     Returns:
71         The floating point value on success, or $(D 0.0) on failure.
72 */
73 T to_floating(T)(inout(char)[] str) @nogc nothrow if (__traits(isFloating, T)) {
74     auto tmp = _tmpbuffer!32(str);
75     return cast(T)atof(tmp.ptr);
76 }
77 
78 /// ditto
79 alias toFloat = to_floating;
80 
81 /**
82     Converts the given value to a string.
83 
84     Params:
85         value = The input value.
86 
87     Returns:
88         A string parsed from $(D input).
89 */
90 nstring to_string(T)(T value) @nogc {
91     static if (isStringable!T) {
92         
93         return nstring(assumeNoThrowNoGC((T value) { return input.toString(); }, value));
94     } static if (isPointer!T) {
95         
96         return to_string_impl("%p", value);
97     } else static if (__traits(isIntegral, T)) {
98         enum ifmtstr = __traits(isUnsigned, T) ? "%.*u" : "%.*i";
99 
100         return to_string_impl(ifmtstr, T.sizeof, value);
101     } else static if (__traits(isFloating, T)) {
102 
103         return to_string_impl("%f", value);
104     } else {
105         
106         return nstring(T.stringof);
107     }
108 }
109 
110 /// ditto
111 alias text = to_string;
112 
113 /**
114     Converts the given value to a hexidecimal string.
115 
116     Params:
117         value = The input value.
118 
119     Returns:
120         A string parsed from $(D input).
121 */
122 nstring to_hex_string(T)(T value, bool lowercase = true) if (__traits(isIntegral, T)) {
123     nstring tmp = to_string_impl("%.*x", T.sizeof*2, value);
124 
125     if (lowercase) {
126         foreach(ref char c; cast(char[])tmp[]) {
127             c = toLower(c);
128         }
129     }
130     return tmp;
131 }
132 
133 /// ditto
134 alias toHexString = to_hex_string;
135 
136 /**
137     Converts the given value to a octal string.
138 
139     Params:
140         value = The input value.
141 
142     Returns:
143         A string parsed from $(D input).
144 */
145 nstring to_oct_string(T)(T value) if (__traits(isIntegral, T)) {
146     return to_string_impl("%.*o", T.sizeof*2, value); 
147 }
148 
149 /// ditto
150 alias toOctalString = to_oct_string;
151 
152 
153 
154 /**
155     Parses a hexidecimal number from the source.
156 
157     Params:
158         source = The source string
159     
160     Returns:
161         The number parsed from the source string.
162 */
163 T parseHex(T, S)(auto ref S source) @nogc nothrow
164 if (__traits(isIntegral, T)) {
165     enum maxRead = T.sizeof*2;
166     ulong result;
167     ubyte b;
168 
169     foreach(i; 0..min(maxRead, source.length)) {
170         b = (source[i] & 0xF) + (source[i] >> 6) | ((source[i] >> 3) & 0x8);
171         result = (result << 4) | b;
172     }
173     return cast(T)result;
174 }
175 
176 
177 //
178 //          IMPLEMENTATION DETAILS
179 //
180 
181 private:
182 
183 // Tables that contain the various printf modifiers
184 // used in string conversion.
185 __gshared const(char)*[4][2] _NU_IFMT_TABLE = [
186     ["%hhu", "%hu", "%u", "%llu"], 
187     ["%hhi", "%hi", "%i", "%lli"]
188 ];
189 
190 __gshared const(char)*[4][2] _NU_IFMT_TABLE_HEX = [
191     ["%hhx", "%hx", "%x", "%llx"], 
192     ["%hhX", "%hX", "%X", "%llX"]
193 ];
194 
195 __gshared const(char)*[4] _NU_IFMT_TABLE_OCT = 
196     ["%hho", "%ho", "%o", "%llo"];
197 
198 nstring to_string_impl(T...)(const(char)* fmtstring, T input) @nogc {
199     int reqlen = snprintf(null, 0, fmtstring, input);
200 
201     if (reqlen) {
202         size_t rlen = reqlen+1;
203 
204         char* tmp = cast(char*)nu_malloc(rlen);
205         cast(void)snprintf(tmp, rlen, fmtstring, input);
206         
207         nstring out_ = nstring(tmp[0..rlen]);
208         nu_free(cast(void*)tmp);
209         return out_;
210     }
211 
212     return nstring.init;
213 }
214 
215 /**
216     Creates a temporary buffer of the specified length.
217 */
218 char[len] _tmpbuffer(uint len)(inout(char)[] str) return {
219     size_t tbOffset = min(8, str.length);
220     char[len] tmp = 0;
221     tmp[0..tbOffset] = str[0..tbOffset];
222     return tmp;
223 }