]> git.mxchange.org Git - simgear.git/blob - simgear/nasal/string.c
a94468c67d57321b8bb5cba6a6e44d8224ea987e
[simgear.git] / simgear / nasal / string.c
1 #include <math.h>
2 #include <string.h>
3
4 #include "nasal.h"
5 #include "data.h"
6
7 // The maximum number of significant (decimal!) figures in an IEEE
8 // double.
9 #define DIGITS 16
10
11 static int tonum(unsigned char* s, int len, double* result);
12 static int fromnum(double val, unsigned char* s);
13
14 int naStr_len(naRef s)
15 {
16     if(!IS_STR(s)) return 0;
17     return s.ref.ptr.str->len;
18 }
19
20 char* naStr_data(naRef s)
21 {
22     if(!IS_STR(s)) return 0;
23     return s.ref.ptr.str->data;
24 }
25
26 static void setlen(struct naStr* s, int sz)
27 {
28     if(s->data) naFree(s->data);
29     s->len = sz;
30     s->data = naAlloc(sz+1);
31     s->data[sz] = 0; // nul terminate
32 }
33
34 naRef naStr_fromdata(naRef dst, char* data, int len)
35 {
36     if(!IS_STR(dst)) return naNil();
37     setlen(dst.ref.ptr.str, len);
38     memcpy(dst.ref.ptr.str->data, data, len);
39     return dst;
40 }
41
42 naRef naStr_concat(naRef dest, naRef s1, naRef s2)
43 {
44     struct naStr* dst = dest.ref.ptr.str;
45     struct naStr* a = s1.ref.ptr.str;
46     struct naStr* b = s2.ref.ptr.str;
47     if(!(IS_STR(s1)&&IS_STR(s2)&&IS_STR(dest))) return naNil();
48     setlen(dst, a->len + b->len);
49     memcpy(dst->data, a->data, a->len);
50     memcpy(dst->data + a->len, b->data, b->len);
51     return dest;
52 }
53
54 naRef naStr_substr(naRef dest, naRef str, int start, int len)
55 {
56     struct naStr* dst = dest.ref.ptr.str;
57     struct naStr* s = str.ref.ptr.str;
58     if(!(IS_STR(dest)&&IS_STR(str))) return naNil();
59     if(start + len > s->len) { dst->len = 0; dst->data = 0; return naNil(); }
60     setlen(dst, len);
61     memcpy(dst->data, s->data + start, len);
62     return dest;
63 }
64
65 int naStr_equal(naRef s1, naRef s2)
66 {
67     struct naStr* a = s1.ref.ptr.str;
68     struct naStr* b = s2.ref.ptr.str;
69     if(a->data == b->data) return 1;
70     if(a->len != b->len) return 0;
71     if(memcmp(a->data, b->data, a->len) == 0) return 1;
72     return 0;
73 }
74
75 naRef naStr_fromnum(naRef dest, double num)
76 {
77     struct naStr* dst = dest.ref.ptr.str;
78     unsigned char buf[DIGITS+8];
79     setlen(dst, fromnum(num, buf));
80     memcpy(dst->data, buf, dst->len);
81     return dest;
82 }
83
84 int naStr_parsenum(char* str, int len, double* result)
85 {
86     return tonum(str, len, result);
87 }
88
89 int naStr_tonum(naRef str, double* out)
90 {
91     return tonum(str.ref.ptr.str->data, str.ref.ptr.str->len, out);
92 }
93
94 int naStr_numeric(naRef str)
95 {
96     double dummy;
97     return tonum(str.ref.ptr.str->data, str.ref.ptr.str->len, &dummy);
98 }
99
100 void naStr_gcclean(struct naStr* str)
101 {
102     naFree(str->data);
103     str->data = 0;
104     str->len = 0;
105 }
106
107 ////////////////////////////////////////////////////////////////////////
108 // Below is a custom double<->string conversion library.  Why not
109 // simply use sprintf and atof?  Because they aren't acceptably
110 // platform independant, sadly.  I've seen some very strange results.
111 // This works the same way everywhere, although it is tied to an
112 // assumption of standard IEEE 64 bit floating point doubles.
113 //
114 // In practice, these routines work quite well.  Testing conversions
115 // of random doubles to strings and back, this routine is beaten by
116 // glibc on roundoff error 23% of the time, beats glibc in 10% of
117 // cases, and ties (usually with an error of exactly zero) the
118 // remaining 67%.
119 ////////////////////////////////////////////////////////////////////////
120
121 // Reads an unsigned decimal out of the scalar starting at i, stores
122 // it in v, and returns the next index to start at.  Zero-length
123 // decimal numbers are allowed, and are returned as zero.
124 static int readdec(unsigned char* s, int len, int i, double* v)
125 {
126     *v = 0;
127     if(i >= len) return len;
128     while(i < len && s[i] >= '0' && s[i] <= '9') {
129         *v= (*v) * 10 + (s[i] - '0');
130         i++;
131     }
132     return i;
133 }
134
135 // Reads a signed integer out of the string starting at i, stores it
136 // in v, and returns the next index to start at.  Zero-length
137 // decimal numbers are allowed, and are returned as zero.
138 static int readsigned(unsigned char* s, int len, int i, double* v)
139 {
140     int i0 = i, i2;
141     double sgn=1, val;
142     if(i >= len) { *v = 0; return len; }
143     if(s[i] == '+')      { i++; }
144     else if(s[i] == '-') { i++; sgn = -1; }
145     i2 = readdec(s, len, i, &val);
146     if(i0 == i && i2 == i) {
147         *v = 0;
148         return i0; // don't successfully parse bare "+" or "-"
149     }
150     *v = sgn*val;
151     return i2;
152 }
153
154
155 // Integer decimal power utility, with a tweak that enforces
156 // integer-exactness for arguments where that is possible.
157 static double decpow(int exp)
158 {
159     double v = 1;
160     int absexp;
161     if(exp < 0 || exp >= DIGITS)
162         return pow(10, exp);
163     else
164         absexp = exp < 0 ? -exp : exp;
165     while(absexp--) v *= 10.0;
166     return v;
167 }
168
169 static int tonum(unsigned char* s, int len, double* result)
170 {
171     int i=0, fraclen=0;
172     double sgn=1, val, frac=0, exp=0;
173
174     // Special case, "." is not a number, even though "1." and ".0" are.
175     if(len == 1 && s[0] == '.')
176         return 0;
177
178     // Read the integer part
179     i = readsigned(s, len, i, &val);
180     if(val < 0) { sgn = -1; val = -val; }
181
182     // Read the fractional part, if any
183     if(i < len && s[i] == '.') {
184         i++;
185         fraclen = readdec(s, len, i, &frac) - i;
186         i += fraclen;
187     }
188
189     // Nothing so far?  Then the parse failed.
190     if(i == 0) return 0;
191
192     // Read the exponent, if any
193     if(i < len && (s[i] == 'e' || s[i] == 'E')) {
194         int i0 = i+1;
195         i = readsigned(s, len, i+1, &exp);
196         if(i == i0) return 0; // Must have a number after the "e"
197     }
198     
199     // compute the result
200     *result = sgn * (val + frac * decpow(-fraclen)) * decpow(exp);
201
202     // if we didn't use the whole string, return failure
203     if(i < len) return 0;
204     return 1;
205 }
206
207 // Very simple positive (!) integer print routine.  Puts the result in
208 // s and returns the number of characters written.  Does not null
209 // terminate the result.  Presumes at least a 32 bit integer, and
210 // cannot print integers larger than 9999999999.
211 static int decprint(int val, unsigned char* s)
212 {
213     int p=1, i=0;
214     if(val == 0) { *s = '0'; return 1; }
215     while(p <= 999999999 && p*10 <= val) p *= 10;
216     while(p > 0) {
217         int count = 0;
218         while(val >= p) { val -= p; count++; }
219         s[i++] = '0' + count;
220         p /= 10;
221     }
222     return i;
223 }
224
225 // Takes a positive (!) floating point numbers, and returns exactly
226 // DIGITS decimal numbers in the buffer pointed to by s, and an
227 // integer exponent as the return value.  For example, printing 1.0
228 // will result in "1000000000000000" in the buffer and -15 as the
229 // exponent.  The caller can then place the floating point as needed.
230 static int rawprint(double val, unsigned char* s)
231 {
232     int exponent = (int)floor(log10(val));
233     double mantissa = val / pow(10, exponent);
234     int i, c;
235     for(i=0; i<DIGITS-1; i++) {
236         int digit = (int)floor(mantissa);
237         s[i] = '0' + digit;
238         mantissa -= digit;
239         mantissa *= 10.0;
240     }
241     // Round (i.e. don't floor) the last digit
242     c = (int)floor(mantissa);
243     if(mantissa - c >= 0.5) c++;
244     if(c < 0) c = 0;
245     if(c > 9) c = 9;
246     s[i] = '0' + c;
247     return exponent - DIGITS + 1;
248 }
249
250 static int fromnum(double val, unsigned char* s)
251 {
252     unsigned char raw[DIGITS];
253     unsigned char* ptr = s;
254     int exp, digs, i=0;
255
256     // Handle negatives
257     if(val < 0) { *ptr++ = '-'; val = -val; }
258
259     // Exactly an integer is a special case
260     if(val == (int)val) {
261         ptr += decprint(val, ptr);
262         *ptr = 0;
263         return ptr - s;
264     }
265
266     // Get the raw digits
267     exp = rawprint(val, raw);
268
269     // Examine trailing zeros to get a significant digit count
270     for(i=DIGITS-1; i>0; i--)
271         if(raw[i] != '0') break;
272     digs = i+1;
273
274     if(exp > 0 || exp < -(DIGITS+3)) {
275         // Standard scientific notation
276         exp += DIGITS-1;
277         *ptr++ = raw[0];
278         if(digs > 1) {
279             *ptr++ = '.';
280             for(i=1; i<digs; i++) *ptr++ = raw[i];
281         }
282         *ptr++ = 'e';
283         if(exp < 0) { exp = -exp; *ptr++ = '-'; }
284         else { *ptr++ = '+'; }
285         if(exp < 10) *ptr++ = '0';
286         ptr += decprint(exp, ptr);
287     } else if(exp < 1-DIGITS) {
288         // Fraction with insignificant leading zeros
289         *ptr++ = '0'; *ptr++ = '.';
290         for(i=0; i<-(exp+DIGITS); i++) *ptr++ = '0';
291         for(i=0; i<digs; i++) *ptr++ = raw[i];
292     } else {
293         // Integer part
294         for(i=0; i<DIGITS+exp; i++) *ptr++ = raw[i];
295         if(i < digs) {
296             // Fraction, if any
297             *ptr++ = '.';
298             while(i<digs) *ptr++ = raw[i++];
299         }
300     }
301     *ptr = 0;
302     return ptr - s;
303 }