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