]> Dogcows Code - chaz/yoink/blob - src/stlplus/strings/string_int.cpp
build system enhancements
[chaz/yoink] / src / stlplus / strings / string_int.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2
3 // Author: Andy Rushton
4 // Copyright: (c) Southampton University 1999-2004
5 // (c) Andy Rushton 2004-2009
6 // License: BSD License, see ../docs/license.html
7
8 ////////////////////////////////////////////////////////////////////////////////
9 #include "string_int.hpp"
10 #include <ctype.h>
11 #include <stdlib.h>
12
13 namespace stlplus
14 {
15
16 ////////////////////////////////////////////////////////////////////////////////
17 // character mappings
18
19 static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";
20 static int from_char [] =
21 {
22 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
23 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
24 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
25 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
26 -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
27 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
28 -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
29 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
30 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
38 };
39
40 ////////////////////////////////////////////////////////////////////////////////
41 // Conversions to string
42 // Local generic routines
43
44 // signed version of the generic image generation function for all integer types
45 template<typename T>
46 static std::string simage (T i, unsigned radix, radix_display_t display, unsigned width)
47 throw(std::invalid_argument)
48 {
49 if (radix < 2 || radix > 36)
50 throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));
51 // untangle all the options
52 bool hashed = false;
53 bool binary = false;
54 bool octal = false;
55 bool hex = false;
56 switch(display)
57 {
58 case radix_none:
59 break;
60 case radix_hash_style:
61 hashed = radix != 10;
62 break;
63 case radix_hash_style_all:
64 hashed = true;
65 break;
66 case radix_c_style:
67 if (radix == 16)
68 hex = true;
69 else if (radix == 8)
70 octal = true;
71 else if (radix == 2)
72 binary = true;
73 break;
74 case radix_c_style_or_hash:
75 if (radix == 16)
76 hex = true;
77 else if (radix == 8)
78 octal = true;
79 else if (radix == 2)
80 binary = true;
81 else if (radix != 10)
82 hashed = true;
83 break;
84 default:
85 throw std::invalid_argument("invalid radix display value");
86 }
87 // create constants of the same type as the template parameter to avoid type mismatches
88 const T t_zero(0);
89 const T t_radix(radix);
90 // the C representations for binary, octal and hex use 2's-complement representation
91 // all other represenations use sign-magnitude
92 std::string result;
93 if (hex || octal || binary)
94 {
95 // bit-pattern representation
96 // this is the binary representation optionally shown in octal or hex
97 // first generate the binary by masking the bits
98 // ensure that it has at least one bit!
99 for (T mask(1); ; mask <<= 1)
100 {
101 result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0');
102 if (mask == t_zero) break;
103 }
104 // the result is now the full width of the type - e.g. int will give a 32-bit result
105 // now interpret this as either binary, octal or hex and add the prefix
106 if (binary)
107 {
108 // the result is already binary - but the width may be wrong
109 // if this is still smaller than the width field, sign extend
110 // otherwise trim down to either the width or the smallest string that preserves the value
111 while (result.size() < width)
112 result.insert((std::string::size_type)0, 1, result[0]);
113 while (result.size() > width)
114 {
115 // do not trim to less than 2 bits (sign plus 1-bit magnitude)
116 if (result.size() <= 2) break;
117 // only trim if it doesn't change the sign and therefore the value
118 if (result[0] != result[1]) break;
119 result.erase(0,1);
120 }
121 // add the prefix
122 result.insert((std::string::size_type)0, "0b");
123 }
124 else if (octal)
125 {
126 // the result is currently binary - but before converting get the width right
127 // the width is expressed in octal digits so make the binary 3 times this
128 // if this is still smaller than the width field, sign extend
129 // otherwise trim down to either the width or the smallest string that preserves the value
130 // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier
131 while (result.size() < 3*width)
132 result.insert((std::string::size_type)0, 1, result[0]);
133 while (result.size() > 3*width)
134 {
135 // do not trim to less than 2 bits (sign plus 1-bit magnitude)
136 if (result.size() <= 2) break;
137 // only trim if it doesn't change the sign and therefore the value
138 if (result[0] != result[1]) break;
139 result.erase(0,1);
140 }
141 while (result.size() % 3 != 0)
142 result.insert((std::string::size_type)0, 1, result[0]);
143 // now convert to octal
144 std::string octal_result;
145 for (unsigned i = 0; i < result.size()/3; i++)
146 {
147 // yuck - ugly or what?
148 if (result[i*3] == '0')
149 {
150 if (result[i*3+1] == '0')
151 {
152 if (result[i*3+2] == '0')
153 octal_result += '0';
154 else
155 octal_result += '1';
156 }
157 else
158 {
159 if (result[i*3+2] == '0')
160 octal_result += '2';
161 else
162 octal_result += '3';
163 }
164 }
165 else
166 {
167 if (result[i*3+1] == '0')
168 {
169 if (result[i*3+2] == '0')
170 octal_result += '4';
171 else
172 octal_result += '5';
173 }
174 else
175 {
176 if (result[i*3+2] == '0')
177 octal_result += '6';
178 else
179 octal_result += '7';
180 }
181 }
182 }
183 result = octal_result;
184 // add the prefix
185 result.insert((std::string::size_type)0, "0");
186 }
187 else
188 {
189 // hex - similar to octal
190 while (result.size() < 4*width)
191 result.insert((std::string::size_type)0, 1, result[0]);
192 while (result.size() > 4*width)
193 {
194 // do not trim to less than 2 bits (sign plus 1-bit magnitude)
195 if (result.size() <= 2) break;
196 // only trim if it doesn't change the sign and therefore the value
197 if (result[0] != result[1]) break;
198 result.erase(0,1);
199 }
200 while (result.size() % 4 != 0)
201 result.insert((std::string::size_type)0, 1, result[0]);
202 // now convert to hex
203 std::string hex_result;
204 for (unsigned i = 0; i < result.size()/4; i++)
205 {
206 // yuck - ugly or what?
207 if (result[i*4] == '0')
208 {
209 if (result[i*4+1] == '0')
210 {
211 if (result[i*4+2] == '0')
212 {
213 if (result[i*4+3] == '0')
214 hex_result += '0';
215 else
216 hex_result += '1';
217 }
218 else
219 {
220 if (result[i*4+3] == '0')
221 hex_result += '2';
222 else
223 hex_result += '3';
224 }
225 }
226 else
227 {
228 if (result[i*4+2] == '0')
229 {
230 if (result[i*4+3] == '0')
231 hex_result += '4';
232 else
233 hex_result += '5';
234 }
235 else
236 {
237 if (result[i*4+3] == '0')
238 hex_result += '6';
239 else
240 hex_result += '7';
241 }
242 }
243 }
244 else
245 {
246 if (result[i*4+1] == '0')
247 {
248 if (result[i*4+2] == '0')
249 {
250 if (result[i*4+3] == '0')
251 hex_result += '8';
252 else
253 hex_result += '9';
254 }
255 else
256 {
257 if (result[i*4+3] == '0')
258 hex_result += 'a';
259 else
260 hex_result += 'b';
261 }
262 }
263 else
264 {
265 if (result[i*4+2] == '0')
266 {
267 if (result[i*4+3] == '0')
268 hex_result += 'c';
269 else
270 hex_result += 'd';
271 }
272 else
273 {
274 if (result[i*4+3] == '0')
275 hex_result += 'e';
276 else
277 hex_result += 'f';
278 }
279 }
280 }
281 }
282 result = hex_result;
283 // add the prefix
284 result.insert((std::string::size_type)0, "0x");
285 }
286 }
287 else
288 {
289 // convert to sign-magnitude
290 // the representation is:
291 // [radix#][sign]magnitude
292 bool negative = i < t_zero;
293 // create a representation of the magnitude by successive division
294 do
295 {
296 T ch = abs(i % t_radix);
297 i /= t_radix;
298 result.insert((std::string::size_type)0, 1, to_char[ch]);
299 }
300 while(i != t_zero || result.size() < width);
301 // add the prefixes
302 // add a sign only for negative values
303 if (negative)
304 result.insert((std::string::size_type)0, 1, '-');
305 // then prefix everything with the radix if the hashed representation was requested
306 if (hashed)
307 result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#");
308 }
309 return result;
310 }
311
312 // unsigned version
313 template<typename T>
314 static std::string uimage (T i, unsigned radix, radix_display_t display, unsigned width)
315 throw(std::invalid_argument)
316 {
317 if (radix < 2 || radix > 36)
318 throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));
319 // untangle all the options
320 bool hashed = false;
321 bool binary = false;
322 bool octal = false;
323 bool hex = false;
324 switch(display)
325 {
326 case radix_none:
327 break;
328 case radix_hash_style:
329 hashed = radix != 10;
330 break;
331 case radix_hash_style_all:
332 hashed = true;
333 break;
334 case radix_c_style:
335 if (radix == 16)
336 hex = true;
337 else if (radix == 8)
338 octal = true;
339 else if (radix == 2)
340 binary = true;
341 break;
342 case radix_c_style_or_hash:
343 if (radix == 16)
344 hex = true;
345 else if (radix == 8)
346 octal = true;
347 else if (radix == 2)
348 binary = true;
349 else if (radix != 10)
350 hashed = true;
351 break;
352 default:
353 throw std::invalid_argument("invalid radix display value");
354 }
355 // create constants of the same type as the template parameter to avoid type mismatches
356 const T t_zero(0);
357 const T t_radix(radix);
358 // the C representations for binary, octal and hex use 2's-complement representation
359 // all other represenations use sign-magnitude
360 std::string result;
361 if (hex || octal || binary)
362 {
363 // bit-pattern representation
364 // this is the binary representation optionally shown in octal or hex
365 // first generate the binary by masking the bits
366 // ensure at least one bit
367 for (T mask(1); ; mask <<= 1)
368 {
369 result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0');
370 if (mask == t_zero) break;
371 }
372 // the result is now the full width of the type - e.g. int will give a 32-bit result
373 // now interpret this as either binary, octal or hex and add the prefix
374 if (binary)
375 {
376 // the result is already binary - but the width may be wrong
377 // if this is still smaller than the width field, zero extend
378 // otherwise trim down to either the width or the smallest string that preserves the value
379 while (result.size() < width)
380 result.insert((std::string::size_type)0, 1, '0');
381 while (result.size() > width)
382 {
383 // do not trim to less than 1 bit (1-bit magnitude)
384 if (result.size() <= 1) break;
385 // only trim if it doesn't change the sign and therefore the value
386 if (result[0] != '0') break;
387 result.erase(0,1);
388 }
389 // add the prefix
390 result.insert((std::string::size_type)0, "0b");
391 }
392 else if (octal)
393 {
394 // the result is currently binary - but before converting get the width right
395 // the width is expressed in octal digits so make the binary 3 times this
396 // if this is still smaller than the width field, sign extend
397 // otherwise trim down to either the width or the smallest string that preserves the value
398 // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier
399 while (result.size() < 3*width)
400 result.insert((std::string::size_type)0, 1, '0');
401 while (result.size() > 3*width)
402 {
403 // do not trim to less than 1 bit (1-bit magnitude)
404 if (result.size() <= 1) break;
405 // only trim if it doesn't change the sign and therefore the value
406 if (result[0] != '0') break;
407 result.erase(0,1);
408 }
409 while (result.size() % 3 != 0)
410 result.insert((std::string::size_type)0, 1, '0');
411 // now convert to octal
412 std::string octal_result;
413 for (unsigned i = 0; i < result.size()/3; i++)
414 {
415 // yuck - ugly or what?
416 if (result[i*3] == '0')
417 {
418 if (result[i*3+1] == '0')
419 {
420 if (result[i*3+2] == '0')
421 octal_result += '0';
422 else
423 octal_result += '1';
424 }
425 else
426 {
427 if (result[i*3+2] == '0')
428 octal_result += '2';
429 else
430 octal_result += '3';
431 }
432 }
433 else
434 {
435 if (result[i*3+1] == '0')
436 {
437 if (result[i*3+2] == '0')
438 octal_result += '4';
439 else
440 octal_result += '5';
441 }
442 else
443 {
444 if (result[i*3+2] == '0')
445 octal_result += '6';
446 else
447 octal_result += '7';
448 }
449 }
450 }
451 result = octal_result;
452 // add the prefix if the leading digit is not already 0
453 if (result.empty() || result[0] != '0') result.insert((std::string::size_type)0, "0");
454 }
455 else
456 {
457 // similar to octal
458 while (result.size() < 4*width)
459 result.insert((std::string::size_type)0, 1, '0');
460 while (result.size() > 4*width)
461 {
462 // do not trim to less than 1 bit (1-bit magnitude)
463 if (result.size() <= 1) break;
464 // only trim if it doesn't change the sign and therefore the value
465 if (result[0] != '0') break;
466 result.erase(0,1);
467 }
468 while (result.size() % 4 != 0)
469 result.insert((std::string::size_type)0, 1, '0');
470 // now convert to hex
471 std::string hex_result;
472 for (unsigned i = 0; i < result.size()/4; i++)
473 {
474 // yuck - ugly or what?
475 if (result[i*4] == '0')
476 {
477 if (result[i*4+1] == '0')
478 {
479 if (result[i*4+2] == '0')
480 {
481 if (result[i*4+3] == '0')
482 hex_result += '0';
483 else
484 hex_result += '1';
485 }
486 else
487 {
488 if (result[i*4+3] == '0')
489 hex_result += '2';
490 else
491 hex_result += '3';
492 }
493 }
494 else
495 {
496 if (result[i*4+2] == '0')
497 {
498 if (result[i*4+3] == '0')
499 hex_result += '4';
500 else
501 hex_result += '5';
502 }
503 else
504 {
505 if (result[i*4+3] == '0')
506 hex_result += '6';
507 else
508 hex_result += '7';
509 }
510 }
511 }
512 else
513 {
514 if (result[i*4+1] == '0')
515 {
516 if (result[i*4+2] == '0')
517 {
518 if (result[i*4+3] == '0')
519 hex_result += '8';
520 else
521 hex_result += '9';
522 }
523 else
524 {
525 if (result[i*4+3] == '0')
526 hex_result += 'a';
527 else
528 hex_result += 'b';
529 }
530 }
531 else
532 {
533 if (result[i*4+2] == '0')
534 {
535 if (result[i*4+3] == '0')
536 hex_result += 'c';
537 else
538 hex_result += 'd';
539 }
540 else
541 {
542 if (result[i*4+3] == '0')
543 hex_result += 'e';
544 else
545 hex_result += 'f';
546 }
547 }
548 }
549 }
550 result = hex_result;
551 // add the prefix
552 result.insert((std::string::size_type)0, "0x");
553 }
554 }
555 else
556 {
557 // convert to sign-magnitude
558 // the representation is:
559 // [radix#]magnitude
560 // create a representation of the magnitude by successive division
561 do
562 {
563 T ch = i % t_radix;
564 i /= t_radix;
565 result.insert((std::string::size_type)0, 1, to_char[(int)ch]);
566 }
567 while(i != t_zero || result.size() < width);
568 // prefix everything with the radix if the hashed representation was requested
569 if (hashed)
570 result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#");
571 }
572 return result;
573 }
574
575 ////////////////////////////////////////////////////////////////////////////////
576 // exported conversions to string
577
578 std::string short_to_string(short i, unsigned radix, radix_display_t display, unsigned width)
579 throw(std::invalid_argument)
580 {
581 return simage(i, radix, display, width);
582 }
583
584 std::string unsigned_short_to_string(unsigned short i, unsigned radix, radix_display_t display, unsigned width)
585 throw(std::invalid_argument)
586 {
587 return uimage(i, radix, display, width);
588 }
589
590 std::string int_to_string(int i, unsigned radix, radix_display_t display, unsigned width)
591 throw(std::invalid_argument)
592 {
593 return simage(i, radix, display, width);
594 }
595
596 std::string unsigned_to_string(unsigned i, unsigned radix, radix_display_t display, unsigned width)
597 throw(std::invalid_argument)
598 {
599 return uimage(i, radix, display, width);
600 }
601
602 std::string long_to_string(long i, unsigned radix, radix_display_t display, unsigned width)
603 throw(std::invalid_argument)
604 {
605 return simage(i, radix, display, width);
606 }
607
608 std::string unsigned_long_to_string(unsigned long i, unsigned radix, radix_display_t display, unsigned width)
609 throw(std::invalid_argument)
610 {
611 return uimage(i, radix, display, width);
612 }
613
614 ////////////////////////////////////////////////////////////////////////////////
615 // Conversions FROM string
616 // local template function
617 // Note: this has been copied and modified for the inf class - so any changes here must be made there too
618
619 // signed version
620 template<typename T>
621 static T svalue(const std::string& str, unsigned radix)
622 throw(std::invalid_argument)
623 {
624 if (radix != 0 && (radix < 2 || radix > 36))
625 throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));
626 std::string::size_type i = 0;
627 // the radix passed as a parameter is just the default - it can be
628 // overridden by either the C prefix or the hash prefix. Note: a leading zero
629 // is the C-style prefix for octal - I only make this override the default
630 // when the default prefix is not specified
631 // First check for a C-style prefix
632 bool c_style = false;
633 if (i < str.size() && str[i] == '0')
634 {
635 // octal, binary or hex
636 if (i+1 < str.size() && tolower(str[i+1]) == 'x')
637 {
638 radix = 16;
639 i += 2;
640 c_style = true;
641 }
642 else if (i+1 < str.size() && tolower(str[i+1]) == 'b')
643 {
644 radix = 2;
645 i += 2;
646 c_style = true;
647 }
648 else if (radix == 0)
649 {
650 radix = 8;
651 i += 1;
652 c_style = true;
653 }
654 }
655 // now check for a hash-style prefix if a C-style prefix was not found
656 if (i == 0)
657 {
658 // scan for the sequence {digits}#
659 bool hash_found = false;
660 std::string::size_type j = i;
661 for (; j < str.size(); j++)
662 {
663 if (!isdigit(str[j]))
664 {
665 if (str[j] == '#')
666 hash_found = true;
667 break;
668 }
669 }
670 if (hash_found)
671 {
672 // use the hash prefix to define the radix
673 // i points to the start of the radix and j points to the # character
674 std::string slice = str.substr(i, j-i);
675 radix = string_to_unsigned(slice);
676 i = j+1;
677 }
678 }
679 if (radix == 0)
680 radix = 10;
681 if (radix < 2 || radix > 36)
682 throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));
683 T val(0);
684 if (c_style)
685 {
686 // the C style formats are bit patterns not integer values - these need
687 // to be sign-extended to get the right value
688 std::string binary;
689 if (radix == 2)
690 {
691 for (std::string::size_type j = i; j < str.size(); j++)
692 {
693 switch(str[j])
694 {
695 case '0':
696 binary += '0';
697 break;
698 case '1':
699 binary += '1';
700 break;
701 default:
702 throw std::invalid_argument("invalid binary character in string " + str);
703 break;
704 }
705 }
706 }
707 else if (radix == 8)
708 {
709 for (std::string::size_type j = i; j < str.size(); j++)
710 {
711 switch(str[j])
712 {
713 case '0':
714 binary += "000";
715 break;
716 case '1':
717 binary += "001";
718 break;
719 case '2':
720 binary += "010";
721 break;
722 case '3':
723 binary += "011";
724 break;
725 case '4':
726 binary += "100";
727 break;
728 case '5':
729 binary += "101";
730 break;
731 case '6':
732 binary += "110";
733 break;
734 case '7':
735 binary += "111";
736 break;
737 default:
738 throw std::invalid_argument("invalid octal character in string " + str);
739 break;
740 }
741 }
742 }
743 else
744 {
745 for (std::string::size_type j = i; j < str.size(); j++)
746 {
747 switch(tolower(str[j]))
748 {
749 case '0':
750 binary += "0000";
751 break;
752 case '1':
753 binary += "0001";
754 break;
755 case '2':
756 binary += "0010";
757 break;
758 case '3':
759 binary += "0011";
760 break;
761 case '4':
762 binary += "0100";
763 break;
764 case '5':
765 binary += "0101";
766 break;
767 case '6':
768 binary += "0110";
769 break;
770 case '7':
771 binary += "0111";
772 break;
773 case '8':
774 binary += "1000";
775 break;
776 case '9':
777 binary += "1001";
778 break;
779 case 'a':
780 binary += "1010";
781 break;
782 case 'b':
783 binary += "1011";
784 break;
785 case 'c':
786 binary += "1100";
787 break;
788 case 'd':
789 binary += "1101";
790 break;
791 case 'e':
792 binary += "1110";
793 break;
794 case 'f':
795 binary += "1111";
796 break;
797 default:
798 throw std::invalid_argument("invalid hex character in string " + str);
799 break;
800 }
801 }
802 }
803 // now sign-extend to the right number of bits for the type
804 while (binary.size() < sizeof(T)*8)
805 binary.insert((std::string::size_type)0, 1, binary.empty() ? '0' : binary[0]);
806 // now convert the value
807 for (std::string::size_type j = 0; j < binary.size(); j++)
808 {
809 val *= 2;
810 int ch = from_char[(unsigned char)binary[j]] ;
811 val += T(ch);
812 }
813 }
814 else
815 {
816 // now scan for a sign and find whether this is a negative number
817 bool negative = false;
818 if (i < str.size())
819 {
820 switch (str[i])
821 {
822 case '-':
823 negative = true;
824 i++;
825 break;
826 case '+':
827 i++;
828 break;
829 }
830 }
831 for (; i < str.size(); i++)
832 {
833 val *= T(radix);
834 int ch = from_char[(unsigned char)str[i]] ;
835 if (ch == -1 || (unsigned)ch >= radix)
836 throw std::invalid_argument("invalid character in string " + str);
837 val += T(ch);
838 }
839 if (negative)
840 val = -val;
841 }
842 return val;
843 }
844
845 // unsigned version
846 template<typename T>
847 static T uvalue(const std::string& str, unsigned radix)
848 throw(std::invalid_argument)
849 {
850 if (radix != 0 && (radix < 2 || radix > 36))
851 throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));
852 unsigned i = 0;
853 // the radix passed as a parameter is just the default - it can be
854 // overridden by either the C prefix or the hash prefix. Note: a leading
855 // zero is the C-style prefix for octal - I only make this override the
856 // default when the default prefix is not specified
857 // First check for a C-style prefix
858 bool c_style = false;
859 if (i < str.size() && str[i] == '0')
860 {
861 // binary or hex
862 if (i+1 < str.size() && tolower(str[i+1]) == 'x')
863 {
864 radix = 16;
865 i += 2;
866 c_style = true;
867 }
868 else if (i+1 < str.size() && tolower(str[i+1]) == 'b')
869 {
870 radix = 2;
871 i += 2;
872 c_style = true;
873 }
874 else if (radix == 0)
875 {
876 radix = 8;
877 i += 1;
878 c_style = true;
879 }
880 }
881 // now check for a hash-style prefix if a C-style prefix was not found
882 if (i == 0)
883 {
884 // scan for the sequence {digits}#
885 bool hash_found = false;
886 unsigned j = i;
887 for (; j < str.size(); j++)
888 {
889 if (!isdigit(str[j]))
890 {
891 if (str[j] == '#')
892 hash_found = true;
893 break;
894 }
895 }
896 if (hash_found)
897 {
898 // use the hash prefix to define the radix
899 // i points to the start of the radix and j points to the # character
900 std::string slice = str.substr(i, j-i);
901 radix = string_to_unsigned(slice);
902 i = j+1;
903 }
904 }
905 if (radix == 0)
906 radix = 10;
907 if (radix < 2 || radix > 36)
908 throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix));
909 T val(0);
910 if (c_style)
911 {
912 // the C style formats are bit patterns not integer values - these need
913 // to be sign-extended to get the right value
914 std::string binary;
915 if (radix == 2)
916 {
917 for (unsigned j = i; j < str.size(); j++)
918 {
919 switch(str[j])
920 {
921 case '0':
922 binary += '0';
923 break;
924 case '1':
925 binary += '1';
926 break;
927 default:
928 throw std::invalid_argument("invalid hex character in string " + str);
929 break;
930 }
931 }
932 }
933 else if (radix == 8)
934 {
935 for (unsigned j = i; j < str.size(); j++)
936 {
937 switch(str[j])
938 {
939 case '0':
940 binary += "000";
941 break;
942 case '1':
943 binary += "001";
944 break;
945 case '2':
946 binary += "010";
947 break;
948 case '3':
949 binary += "011";
950 break;
951 case '4':
952 binary += "100";
953 break;
954 case '5':
955 binary += "101";
956 break;
957 case '6':
958 binary += "110";
959 break;
960 case '7':
961 binary += "111";
962 break;
963 default:
964 throw std::invalid_argument("invalid octal character in string " + str);
965 break;
966 }
967 }
968 }
969 else
970 {
971 for (unsigned j = i; j < str.size(); j++)
972 {
973 switch(tolower(str[j]))
974 {
975 case '0':
976 binary += "0000";
977 break;
978 case '1':
979 binary += "0001";
980 break;
981 case '2':
982 binary += "0010";
983 break;
984 case '3':
985 binary += "0011";
986 break;
987 case '4':
988 binary += "0100";
989 break;
990 case '5':
991 binary += "0101";
992 break;
993 case '6':
994 binary += "0110";
995 break;
996 case '7':
997 binary += "0111";
998 break;
999 case '8':
1000 binary += "1000";
1001 break;
1002 case '9':
1003 binary += "1001";
1004 break;
1005 case 'a':
1006 binary += "1010";
1007 break;
1008 case 'b':
1009 binary += "1011";
1010 break;
1011 case 'c':
1012 binary += "1100";
1013 break;
1014 case 'd':
1015 binary += "1101";
1016 break;
1017 case 'e':
1018 binary += "1110";
1019 break;
1020 case 'f':
1021 binary += "1111";
1022 break;
1023 default:
1024 throw std::invalid_argument("invalid hex character in string " + str);
1025 break;
1026 }
1027 }
1028 }
1029 // now zero-extend to the right number of bits for the type
1030 while (binary.size() < sizeof(T)*8)
1031 binary.insert((std::string::size_type)0, 1, '0');
1032 // now convert the value
1033 for (unsigned j = 0; j < binary.size(); j++)
1034 {
1035 val *= 2;
1036 int ch = from_char[(unsigned char)binary[j]] ;
1037 val += T(ch);
1038 }
1039 }
1040 else
1041 {
1042 // now scan for a sign and find whether this is a negative number
1043 if (i < str.size())
1044 {
1045 switch (str[i])
1046 {
1047 case '-':
1048 throw std::invalid_argument("invalid sign character in string " + str + " for unsigned value");
1049 i++;
1050 break;
1051 case '+':
1052 i++;
1053 break;
1054 }
1055 }
1056 for (; i < str.size(); i++)
1057 {
1058 val *= T(radix);
1059 int ch = from_char[(unsigned char)str[i]] ;
1060 if (ch == -1 || (unsigned)ch >= radix)
1061 {
1062 throw std::invalid_argument("invalid character in string " + str);
1063 }
1064 val += T(ch);
1065 }
1066 }
1067 return val;
1068 }
1069
1070 ////////////////////////////////////////////////////////////////////////////////
1071 // exported functions
1072
1073 short string_to_short(const std::string& str, unsigned radix)
1074 throw(std::invalid_argument)
1075 {
1076 return svalue<short>(str, radix);
1077 }
1078
1079 unsigned short string_to_unsigned_short(const std::string& str, unsigned radix)
1080 throw(std::invalid_argument)
1081 {
1082 return uvalue<unsigned short>(str, radix);
1083 }
1084
1085 int string_to_int(const std::string& str, unsigned radix)
1086 throw(std::invalid_argument)
1087 {
1088 return svalue<int>(str, radix);
1089 }
1090
1091 unsigned string_to_unsigned(const std::string& str, unsigned radix)
1092 throw(std::invalid_argument)
1093 {
1094 return uvalue<unsigned>(str, radix);
1095 }
1096
1097 long string_to_long(const std::string& str, unsigned radix)
1098 throw(std::invalid_argument)
1099 {
1100 return svalue<long>(str, radix);
1101 }
1102
1103 unsigned long string_to_unsigned_long(const std::string& str, unsigned radix)
1104 throw(std::invalid_argument)
1105 {
1106 return uvalue<unsigned long>(str, radix);
1107 }
1108
1109 ////////////////////////////////////////////////////////////////////////////////
1110
1111 } // end namespace stlplus
This page took 0.088202 seconds and 4 git commands to generate.