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