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