Mirror of :pserver:cvs@cvs.fefe.de:/cvs libowfat https://www.fefe.de/libowfat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

85 lines
2.7 KiB

  1. #include "scan.h"
  2. #include "haveuint128.h"
  3. size_t scan_ulongn(const char* src,size_t n,unsigned long int* dest) {
  4. register const char *tmp=src;
  5. register unsigned long int l=0;
  6. register unsigned char c;
  7. /* Since the conditions can be computed at compile time, the compiler
  8. * should only emit code for one of the implementations, depending on
  9. * which architecture the code is compiled for. */
  10. #if defined(__GNUC__) && (__GNUC__ >= 5)
  11. /* implementation for recent gcc or clang */
  12. int ok=0;
  13. for (; n-->0 && (c=(unsigned char)(*tmp-'0'))<10; ++tmp) {
  14. unsigned long v;
  15. if (__builtin_mul_overflow(l,10,&v) || __builtin_add_overflow(v,c,&v))
  16. break;
  17. l=v;
  18. ok=1;
  19. }
  20. if (!ok) return 0;
  21. *dest=l;
  22. return (size_t)(tmp-src);
  23. #else
  24. #ifdef HAVE_UINT128
  25. if (sizeof(unsigned long)==sizeof(unsigned long long) && sizeof(unsigned long)<sizeof(__uint128_t)) {
  26. /* implementation for 64-bit platforms with gcc */
  27. for (; n-->0 && (c=(unsigned char)(*tmp-'0'))<10; ++tmp) {
  28. __uint128_t L=(__uint128_t)l*10+c;
  29. if ((L >> ((sizeof(L)-sizeof(l))*8))) break;
  30. l=(unsigned long)L;
  31. }
  32. *dest=l;
  33. return (size_t)(tmp-src);
  34. } else
  35. #endif
  36. if (sizeof(unsigned long)<sizeof(unsigned long long)) {
  37. /* implementation for 32-bit platforms */
  38. for (; n-->0 && (c=(unsigned char)(*tmp-'0'))<10; ++tmp) {
  39. unsigned long long L=(unsigned long long)l*10+c;
  40. if ((unsigned long)L != L) break;
  41. l=(unsigned long)L;
  42. }
  43. *dest=l;
  44. return (size_t)(tmp-src);
  45. } else {
  46. /* implementation for 64-bit platforms without gcc */
  47. while (n-->0 && (c=(unsigned char)(*tmp-'0'))<10) {
  48. unsigned long int n;
  49. /* we want to do: l=l*10+c
  50. * but we need to check for integer overflow.
  51. * to check whether l*10 overflows, we could do
  52. * if ((l*10)/10 != l)
  53. * however, multiplication and division are expensive.
  54. * so instead of *10 we do (l<<3) (i.e. *8) + (l<<1) (i.e. *2)
  55. * and check for overflow on all the intermediate steps */
  56. n=l<<3; if ((n>>3)!=l) break;
  57. if (n+(l<<1)+c < n) break;
  58. l=n+(l<<1)+c;
  59. ++tmp;
  60. }
  61. if (tmp-src) *dest=l;
  62. return (size_t)(tmp-src);
  63. }
  64. #endif
  65. }
  66. #ifdef UNITTEST
  67. #include <assert.h>
  68. int main() {
  69. unsigned long l;
  70. assert(scan_ulongn("4294967295",10,&l) == 10 && l==4294967295ul);
  71. if (sizeof(unsigned long)==4) {
  72. assert(scan_ulongn("4294967296",10,&l) == 9 && l==429496729);
  73. } else {
  74. assert(scan_ulongn("18446744073709551615",20,&l) == 20 && l==18446744073709551615ull);
  75. assert(scan_ulongn("18446744073709551616",20,&l) == 19 && l==1844674407370955161ull);
  76. }
  77. assert(scan_ulongn("1234",3,&l)==3 && l==123);
  78. return 0;
  79. }
  80. #endif