]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * libString, Copyright (C) 1999 Patrick Alken | |
3 | * This library comes with absolutely NO WARRANTY | |
4 | * | |
5 | * Should you choose to use and/or modify this source code, please | |
6 | * do so under the terms of the GNU General Public License under which | |
7 | * this library is distributed. | |
8 | */ | |
9 | #include "stdinc.h" | |
10 | #include "rsdb.h" | |
11 | ||
12 | /* | |
13 | * This table is arranged in chronological order from 0-999, | |
14 | * however the numbers are written backwards, so the number 100 | |
15 | * is expressed in this table as "001". | |
16 | * It's purpose is to ensure fast conversions from integers to | |
17 | * ASCII strings. When an integer variable is encountered, a | |
18 | * simple hash algorithm is used to determine where to look | |
19 | * in this array for the corresponding string. | |
20 | * This outperforms continually dividing by 10 and using the | |
21 | * digit obtained as a character, because we can now divide by | |
22 | * 1000 and use the remainder directly, thus cutting down on | |
23 | * the number of costly divisions needed. For an integer's worst | |
24 | * case, 2 divisions are needed because it can only go up to | |
25 | * 32767, so after 2 divisions by 1000, and some algebra, we will | |
26 | * be left with 327 which we can get from this table. This is much | |
27 | * better than the 5 divisions by 10 that we would need if we did | |
28 | * it the conventional way. Of course, if we made this table go | |
29 | * from 0-9999, only 1 division would be needed. | |
30 | * Longs and unsigned ints of course, are another matter :-). | |
31 | * | |
32 | * Patrick Alken <wnder@underworld.net> | |
33 | */ | |
34 | ||
35 | /* | |
36 | * Set this to the number of indices (numbers) in our table | |
37 | */ | |
38 | #define TABLE_MAX 1000 | |
39 | ||
40 | static const char *IntTable[] = { | |
41 | "000", "100", "200", "300", "400", | |
42 | "500", "600", "700", "800", "900", | |
43 | "010", "110", "210", "310", "410", | |
44 | "510", "610", "710", "810", "910", | |
45 | "020", "120", "220", "320", "420", | |
46 | "520", "620", "720", "820", "920", | |
47 | "030", "130", "230", "330", "430", | |
48 | "530", "630", "730", "830", "930", | |
49 | "040", "140", "240", "340", "440", | |
50 | "540", "640", "740", "840", "940", | |
51 | "050", "150", "250", "350", "450", | |
52 | "550", "650", "750", "850", "950", | |
53 | "060", "160", "260", "360", "460", | |
54 | "560", "660", "760", "860", "960", | |
55 | "070", "170", "270", "370", "470", | |
56 | "570", "670", "770", "870", "970", | |
57 | "080", "180", "280", "380", "480", | |
58 | "580", "680", "780", "880", "980", | |
59 | "090", "190", "290", "390", "490", | |
60 | "590", "690", "790", "890", "990", | |
61 | "001", "101", "201", "301", "401", | |
62 | "501", "601", "701", "801", "901", | |
63 | "011", "111", "211", "311", "411", | |
64 | "511", "611", "711", "811", "911", | |
65 | "021", "121", "221", "321", "421", | |
66 | "521", "621", "721", "821", "921", | |
67 | "031", "131", "231", "331", "431", | |
68 | "531", "631", "731", "831", "931", | |
69 | "041", "141", "241", "341", "441", | |
70 | "541", "641", "741", "841", "941", | |
71 | "051", "151", "251", "351", "451", | |
72 | "551", "651", "751", "851", "951", | |
73 | "061", "161", "261", "361", "461", | |
74 | "561", "661", "761", "861", "961", | |
75 | "071", "171", "271", "371", "471", | |
76 | "571", "671", "771", "871", "971", | |
77 | "081", "181", "281", "381", "481", | |
78 | "581", "681", "781", "881", "981", | |
79 | "091", "191", "291", "391", "491", | |
80 | "591", "691", "791", "891", "991", | |
81 | "002", "102", "202", "302", "402", | |
82 | "502", "602", "702", "802", "902", | |
83 | "012", "112", "212", "312", "412", | |
84 | "512", "612", "712", "812", "912", | |
85 | "022", "122", "222", "322", "422", | |
86 | "522", "622", "722", "822", "922", | |
87 | "032", "132", "232", "332", "432", | |
88 | "532", "632", "732", "832", "932", | |
89 | "042", "142", "242", "342", "442", | |
90 | "542", "642", "742", "842", "942", | |
91 | "052", "152", "252", "352", "452", | |
92 | "552", "652", "752", "852", "952", | |
93 | "062", "162", "262", "362", "462", | |
94 | "562", "662", "762", "862", "962", | |
95 | "072", "172", "272", "372", "472", | |
96 | "572", "672", "772", "872", "972", | |
97 | "082", "182", "282", "382", "482", | |
98 | "582", "682", "782", "882", "982", | |
99 | "092", "192", "292", "392", "492", | |
100 | "592", "692", "792", "892", "992", | |
101 | "003", "103", "203", "303", "403", | |
102 | "503", "603", "703", "803", "903", | |
103 | "013", "113", "213", "313", "413", | |
104 | "513", "613", "713", "813", "913", | |
105 | "023", "123", "223", "323", "423", | |
106 | "523", "623", "723", "823", "923", | |
107 | "033", "133", "233", "333", "433", | |
108 | "533", "633", "733", "833", "933", | |
109 | "043", "143", "243", "343", "443", | |
110 | "543", "643", "743", "843", "943", | |
111 | "053", "153", "253", "353", "453", | |
112 | "553", "653", "753", "853", "953", | |
113 | "063", "163", "263", "363", "463", | |
114 | "563", "663", "763", "863", "963", | |
115 | "073", "173", "273", "373", "473", | |
116 | "573", "673", "773", "873", "973", | |
117 | "083", "183", "283", "383", "483", | |
118 | "583", "683", "783", "883", "983", | |
119 | "093", "193", "293", "393", "493", | |
120 | "593", "693", "793", "893", "993", | |
121 | "004", "104", "204", "304", "404", | |
122 | "504", "604", "704", "804", "904", | |
123 | "014", "114", "214", "314", "414", | |
124 | "514", "614", "714", "814", "914", | |
125 | "024", "124", "224", "324", "424", | |
126 | "524", "624", "724", "824", "924", | |
127 | "034", "134", "234", "334", "434", | |
128 | "534", "634", "734", "834", "934", | |
129 | "044", "144", "244", "344", "444", | |
130 | "544", "644", "744", "844", "944", | |
131 | "054", "154", "254", "354", "454", | |
132 | "554", "654", "754", "854", "954", | |
133 | "064", "164", "264", "364", "464", | |
134 | "564", "664", "764", "864", "964", | |
135 | "074", "174", "274", "374", "474", | |
136 | "574", "674", "774", "874", "974", | |
137 | "084", "184", "284", "384", "484", | |
138 | "584", "684", "784", "884", "984", | |
139 | "094", "194", "294", "394", "494", | |
140 | "594", "694", "794", "894", "994", | |
141 | "005", "105", "205", "305", "405", | |
142 | "505", "605", "705", "805", "905", | |
143 | "015", "115", "215", "315", "415", | |
144 | "515", "615", "715", "815", "915", | |
145 | "025", "125", "225", "325", "425", | |
146 | "525", "625", "725", "825", "925", | |
147 | "035", "135", "235", "335", "435", | |
148 | "535", "635", "735", "835", "935", | |
149 | "045", "145", "245", "345", "445", | |
150 | "545", "645", "745", "845", "945", | |
151 | "055", "155", "255", "355", "455", | |
152 | "555", "655", "755", "855", "955", | |
153 | "065", "165", "265", "365", "465", | |
154 | "565", "665", "765", "865", "965", | |
155 | "075", "175", "275", "375", "475", | |
156 | "575", "675", "775", "875", "975", | |
157 | "085", "185", "285", "385", "485", | |
158 | "585", "685", "785", "885", "985", | |
159 | "095", "195", "295", "395", "495", | |
160 | "595", "695", "795", "895", "995", | |
161 | "006", "106", "206", "306", "406", | |
162 | "506", "606", "706", "806", "906", | |
163 | "016", "116", "216", "316", "416", | |
164 | "516", "616", "716", "816", "916", | |
165 | "026", "126", "226", "326", "426", | |
166 | "526", "626", "726", "826", "926", | |
167 | "036", "136", "236", "336", "436", | |
168 | "536", "636", "736", "836", "936", | |
169 | "046", "146", "246", "346", "446", | |
170 | "546", "646", "746", "846", "946", | |
171 | "056", "156", "256", "356", "456", | |
172 | "556", "656", "756", "856", "956", | |
173 | "066", "166", "266", "366", "466", | |
174 | "566", "666", "766", "866", "966", | |
175 | "076", "176", "276", "376", "476", | |
176 | "576", "676", "776", "876", "976", | |
177 | "086", "186", "286", "386", "486", | |
178 | "586", "686", "786", "886", "986", | |
179 | "096", "196", "296", "396", "496", | |
180 | "596", "696", "796", "896", "996", | |
181 | "007", "107", "207", "307", "407", | |
182 | "507", "607", "707", "807", "907", | |
183 | "017", "117", "217", "317", "417", | |
184 | "517", "617", "717", "817", "917", | |
185 | "027", "127", "227", "327", "427", | |
186 | "527", "627", "727", "827", "927", | |
187 | "037", "137", "237", "337", "437", | |
188 | "537", "637", "737", "837", "937", | |
189 | "047", "147", "247", "347", "447", | |
190 | "547", "647", "747", "847", "947", | |
191 | "057", "157", "257", "357", "457", | |
192 | "557", "657", "757", "857", "957", | |
193 | "067", "167", "267", "367", "467", | |
194 | "567", "667", "767", "867", "967", | |
195 | "077", "177", "277", "377", "477", | |
196 | "577", "677", "777", "877", "977", | |
197 | "087", "187", "287", "387", "487", | |
198 | "587", "687", "787", "887", "987", | |
199 | "097", "197", "297", "397", "497", | |
200 | "597", "697", "797", "897", "997", | |
201 | "008", "108", "208", "308", "408", | |
202 | "508", "608", "708", "808", "908", | |
203 | "018", "118", "218", "318", "418", | |
204 | "518", "618", "718", "818", "918", | |
205 | "028", "128", "228", "328", "428", | |
206 | "528", "628", "728", "828", "928", | |
207 | "038", "138", "238", "338", "438", | |
208 | "538", "638", "738", "838", "938", | |
209 | "048", "148", "248", "348", "448", | |
210 | "548", "648", "748", "848", "948", | |
211 | "058", "158", "258", "358", "458", | |
212 | "558", "658", "758", "858", "958", | |
213 | "068", "168", "268", "368", "468", | |
214 | "568", "668", "768", "868", "968", | |
215 | "078", "178", "278", "378", "478", | |
216 | "578", "678", "778", "878", "978", | |
217 | "088", "188", "288", "388", "488", | |
218 | "588", "688", "788", "888", "988", | |
219 | "098", "198", "298", "398", "498", | |
220 | "598", "698", "798", "898", "998", | |
221 | "009", "109", "209", "309", "409", | |
222 | "509", "609", "709", "809", "909", | |
223 | "019", "119", "219", "319", "419", | |
224 | "519", "619", "719", "819", "919", | |
225 | "029", "129", "229", "329", "429", | |
226 | "529", "629", "729", "829", "929", | |
227 | "039", "139", "239", "339", "439", | |
228 | "539", "639", "739", "839", "939", | |
229 | "049", "149", "249", "349", "449", | |
230 | "549", "649", "749", "849", "949", | |
231 | "059", "159", "259", "359", "459", | |
232 | "559", "659", "759", "859", "959", | |
233 | "069", "169", "269", "369", "469", | |
234 | "569", "669", "769", "869", "969", | |
235 | "079", "179", "279", "379", "479", | |
236 | "579", "679", "779", "879", "979", | |
237 | "089", "189", "289", "389", "489", | |
238 | "589", "689", "789", "889", "989", | |
239 | "099", "199", "299", "399", "499", | |
240 | "599", "699", "799", "899", "999" | |
241 | }; | |
242 | ||
243 | /* | |
244 | * Since we calculate the right-most digits for %d %u etc first, | |
245 | * we need a temporary buffer to store them in until we get | |
246 | * to the left-most digits | |
247 | */ | |
248 | ||
249 | #define TEMPBUF_MAX 20 | |
250 | ||
251 | static char TempBuffer[TEMPBUF_MAX]; | |
252 | ||
253 | /* | |
254 | vSnprintf() | |
255 | Backend to Snprintf() - performs the construction of 'dest' | |
256 | using the string 'format' and the given arguments. Also makes sure | |
257 | not more than 'bytes' characters are copied to 'dest' | |
258 | ||
259 | We always allow room for a terminating \0 character, so at most, | |
260 | bytes - 1 characters will be written to dest. | |
261 | ||
262 | Return: Number of characters written, NOT including the terminating | |
263 | \0 character which is *always* placed at the end of the string | |
264 | ||
265 | NOTE: This function handles the following flags only: | |
266 | %s %d %c %u %ld %lu | |
267 | In addition, this function performs *NO* precision, padding, | |
268 | or width formatting. If it receives an unknown % character, | |
269 | it will call vsprintf() to complete the remainder of the | |
270 | string. | |
271 | */ | |
272 | ||
273 | int | |
274 | rs_vsnprintf(char *dest, const size_t bytes, const char *format, va_list args) | |
275 | { | |
276 | char ch; | |
277 | int written = 0; /* bytes written so far */ | |
278 | int maxbytes = bytes - 1; | |
279 | ||
280 | while((ch = *format++) && (written < maxbytes)) | |
281 | { | |
282 | if(ch == '%') | |
283 | { | |
284 | /* | |
285 | * Advance past the % | |
286 | */ | |
287 | ch = *format++; | |
288 | ||
289 | /* | |
290 | * Put the most common cases first - %s %d etc | |
291 | */ | |
292 | ||
293 | if(ch == 's') | |
294 | { | |
295 | const char *str = va_arg(args, const char *); | |
296 | ||
297 | while((*dest = *str)) | |
298 | { | |
299 | ++dest; | |
300 | ++str; | |
301 | ||
302 | if(++written >= maxbytes) | |
303 | break; | |
304 | } | |
305 | ||
306 | continue; | |
307 | } | |
308 | ||
309 | if(ch == 'd') | |
310 | { | |
311 | int num = va_arg(args, int); | |
312 | int quotient; | |
313 | const char *str; | |
314 | char *digitptr = TempBuffer; | |
315 | ||
316 | /* | |
317 | * We have to special-case "0" unfortunately | |
318 | */ | |
319 | if(num == 0) | |
320 | { | |
321 | *dest++ = '0'; | |
322 | ++written; | |
323 | continue; | |
324 | } | |
325 | ||
326 | if(num < 0) | |
327 | { | |
328 | *dest++ = '-'; | |
329 | if(++written >= maxbytes) | |
330 | continue; | |
331 | ||
332 | num = -num; | |
333 | } | |
334 | ||
335 | do | |
336 | { | |
337 | quotient = num / TABLE_MAX; | |
338 | ||
339 | /* | |
340 | * We'll start with the right-most digits of 'num'. | |
341 | * Dividing by TABLE_MAX cuts off all but the X | |
342 | * right-most digits, where X is such that: | |
343 | * | |
344 | * 10^X = TABLE_MAX | |
345 | * | |
346 | * For example, if num = 1200, and TABLE_MAX = 1000, | |
347 | * quotient will be 1. Multiplying this by 1000 and | |
348 | * subtracting from 1200 gives: 1200 - (1 * 1000) = 200. | |
349 | * We then go right to slot 200 in our array and behold! | |
350 | * The string "002" (200 backwards) is conveniently | |
351 | * waiting for us. Then repeat the process with the | |
352 | * digits left. | |
353 | * | |
354 | * The reason we need to have the integers written | |
355 | * backwards, is because we don't know how many digits | |
356 | * there are. If we want to express the number 12130 | |
357 | * for example, our first pass would leave us with 130, | |
358 | * whose slot in the array yields "031", which we | |
359 | * plug into our TempBuffer[]. The next pass gives us | |
360 | * 12, whose slot yields "21" which we append to | |
361 | * TempBuffer[], leaving us with "03121". This is the | |
362 | * exact number we want, only backwards, so it is | |
363 | * a simple matter to reverse the string. If we used | |
364 | * straightfoward numbers, we would have a TempBuffer | |
365 | * looking like this: "13012" which would be a nightmare | |
366 | * to deal with. | |
367 | */ | |
368 | ||
369 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
370 | ||
371 | while((*digitptr = *str)) | |
372 | { | |
373 | ++digitptr; | |
374 | ++str; | |
375 | } | |
376 | } | |
377 | while((num = quotient) != 0); | |
378 | ||
379 | /* | |
380 | * If the last quotient was a 1 or 2 digit number, there | |
381 | * will be one or more leading zeroes in TempBuffer[] - | |
382 | * get rid of them. | |
383 | */ | |
384 | while(*(digitptr - 1) == '0') | |
385 | --digitptr; | |
386 | ||
387 | while(digitptr != TempBuffer) | |
388 | { | |
389 | *dest++ = *--digitptr; | |
390 | if(++written >= maxbytes) | |
391 | break; | |
392 | } | |
393 | ||
394 | continue; | |
395 | } /* if (ch == 'd') */ | |
396 | ||
397 | if(ch == 'c') | |
398 | { | |
399 | *dest++ = va_arg(args, int); | |
400 | ||
401 | ++written; | |
402 | ||
403 | continue; | |
404 | } /* if (ch == 'c') */ | |
405 | ||
406 | if(ch == 'u') | |
407 | { | |
408 | unsigned int num = va_arg(args, unsigned int); | |
409 | unsigned int quotient; | |
410 | const char *str; | |
411 | char *digitptr = TempBuffer; | |
412 | ||
413 | if(num == 0) | |
414 | { | |
415 | *dest++ = '0'; | |
416 | ++written; | |
417 | continue; | |
418 | } | |
419 | ||
420 | do | |
421 | { | |
422 | quotient = num / TABLE_MAX; | |
423 | ||
424 | /* | |
425 | * Very similar to case 'd' | |
426 | */ | |
427 | ||
428 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
429 | ||
430 | while((*digitptr = *str)) | |
431 | { | |
432 | ++digitptr; | |
433 | ++str; | |
434 | } | |
435 | } | |
436 | while((num = quotient) != 0); | |
437 | ||
438 | while(*(digitptr - 1) == '0') | |
439 | --digitptr; | |
440 | ||
441 | while(digitptr != TempBuffer) | |
442 | { | |
443 | *dest++ = *--digitptr; | |
444 | if(++written >= maxbytes) | |
445 | break; | |
446 | } | |
447 | ||
448 | continue; | |
449 | } /* if (ch == 'u') */ | |
450 | ||
451 | if(ch == 'Q') | |
452 | { | |
453 | const char *arg = va_arg(args, const char *); | |
454 | ||
455 | if(arg == NULL) | |
456 | continue; | |
457 | ||
458 | const char *str = rsdb_quote(arg); | |
459 | ||
460 | while((*dest = *str)) | |
461 | { | |
462 | ++dest; | |
463 | ++str; | |
464 | ||
465 | if(++written >= maxbytes) | |
466 | break; | |
467 | } | |
468 | ||
469 | continue; | |
470 | } | |
471 | ||
472 | if(ch == 'l') | |
473 | { | |
474 | if(*format == 'u') | |
475 | { | |
476 | unsigned long num = va_arg(args, unsigned long); | |
477 | unsigned long quotient; | |
478 | const char *str; | |
479 | char *digitptr = TempBuffer; | |
480 | ||
481 | ++format; | |
482 | ||
483 | if(num == 0) | |
484 | { | |
485 | *dest++ = '0'; | |
486 | ++written; | |
487 | continue; | |
488 | } | |
489 | ||
490 | do | |
491 | { | |
492 | quotient = num / TABLE_MAX; | |
493 | ||
494 | /* | |
495 | * Very similar to case 'u' | |
496 | */ | |
497 | ||
498 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
499 | ||
500 | while((*digitptr = *str)) | |
501 | { | |
502 | ++digitptr; | |
503 | ++str; | |
504 | } | |
505 | } | |
506 | while((num = quotient) != 0); | |
507 | ||
508 | while(*(digitptr - 1) == '0') | |
509 | --digitptr; | |
510 | ||
511 | while(digitptr != TempBuffer) | |
512 | { | |
513 | *dest++ = *--digitptr; | |
514 | if(++written >= maxbytes) | |
515 | break; | |
516 | } | |
517 | ||
518 | continue; | |
519 | } | |
520 | else | |
521 | /* if (*format == 'u') */ if(*format == 'd') | |
522 | { | |
523 | long num = va_arg(args, long); | |
524 | long quotient; | |
525 | const char *str; | |
526 | char *digitptr = TempBuffer; | |
527 | ||
528 | ++format; | |
529 | ||
530 | if(num == 0) | |
531 | { | |
532 | *dest++ = '0'; | |
533 | ++written; | |
534 | continue; | |
535 | } | |
536 | ||
537 | if(num < 0) | |
538 | { | |
539 | *dest++ = '-'; | |
540 | if(++written >= maxbytes) | |
541 | continue; | |
542 | ||
543 | num = -num; | |
544 | } | |
545 | ||
546 | do | |
547 | { | |
548 | quotient = num / TABLE_MAX; | |
549 | ||
550 | str = IntTable[num - (quotient * TABLE_MAX)]; | |
551 | ||
552 | while((*digitptr = *str)) | |
553 | { | |
554 | ++digitptr; | |
555 | ++str; | |
556 | } | |
557 | } | |
558 | while((num = quotient) != 0); | |
559 | ||
560 | while(*(digitptr - 1) == '0') | |
561 | --digitptr; | |
562 | ||
563 | while(digitptr != TempBuffer) | |
564 | { | |
565 | *dest++ = *--digitptr; | |
566 | if(++written >= maxbytes) | |
567 | break; | |
568 | } | |
569 | ||
570 | continue; | |
571 | } | |
572 | else /* if (*format == 'd') */ | |
573 | { | |
574 | /* XXX error */ | |
575 | exit(1); | |
576 | } | |
577 | ||
578 | ||
579 | } /* if (ch == 'l') */ | |
580 | ||
581 | if(ch != '%') | |
582 | { | |
583 | /* XXX error */ | |
584 | exit(1); | |
585 | } /* if (ch != '%') */ | |
586 | } /* if (ch == '%') */ | |
587 | ||
588 | *dest++ = ch; | |
589 | ++written; | |
590 | } /* while ((ch = *format++) && (written < maxbytes)) */ | |
591 | ||
592 | /* | |
593 | * Terminate the destination buffer with a \0 | |
594 | */ | |
595 | *dest = '\0'; | |
596 | ||
597 | return (written); | |
598 | } /* vSnprintf() */ | |
599 | ||
600 | /* | |
601 | rs_snprintf() | |
602 | Optimized version of snprintf(). | |
603 | ||
604 | Inputs: dest - destination string | |
605 | bytes - number of bytes to copy | |
606 | format - formatted string | |
607 | args - args to 'format' | |
608 | ||
609 | Return: number of characters copied, NOT including the terminating | |
610 | NULL which is always placed at the end of the string | |
611 | */ | |
612 | ||
613 | int | |
614 | rs_snprintf(char *dest, const size_t bytes, const char *format, ...) | |
615 | { | |
616 | va_list args; | |
617 | int count; | |
618 | ||
619 | va_start(args, format); | |
620 | ||
621 | count = rs_vsnprintf(dest, bytes, format, args); | |
622 | ||
623 | va_end(args); | |
624 | ||
625 | return (count); | |
626 | } /* Snprintf() */ |