forked from regehr/str2long_contest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ryanf_2.c
59 lines (49 loc) · 1.38 KB
/
ryanf_2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "str2long.h"
#define CHECK_ERROR(cond) if(cond) { error = 1; return 0; }
static size_t _strlen(const char* s) {
if(s == NULL) {
return 0;
}
size_t i;
for(i = 0; s[i] != 0; ++i);
return i;
}
static size_t leading_zeros(const char* s) {
if(s == NULL) {
return 0;
}
size_t i;
for(i = 0; s[i] == '0'; ++i);
return i;
}
static unsigned long ilog10_plus1(unsigned long val) {
unsigned long base = 1;
unsigned long i = 0;
while(val >= base) {
++i;
base *= 10;
}
return i;
}
long str2long_ryanf_2 (const char* s) {
CHECK_ERROR(s == NULL);
unsigned int negative = s[0] == '-';
size_t max_len = ilog10_plus1(LONG_MAX) + negative;
size_t len = _strlen(s);
size_t zeros = leading_zeros(&s[negative]);
CHECK_ERROR(len <= negative);
CHECK_ERROR(len - zeros > max_len);
long base = (negative ? -1 : 1);
long value = 0;
size_t i;
for(i = 0; i < len - negative - zeros; ++i) {
base *= (i > 0 ? 10 : 1); // base might roll over when multiplying at the end, so do it at the beginning
unsigned char c = s[len - i - 1] ^ '0';
long inc = c * base;
CHECK_ERROR(c > 9);
CHECK_ERROR(value > 0 && (LONG_MAX - value) < inc);
CHECK_ERROR(value < 0 && (LONG_MIN - value) > inc);
value += inc;
}
return value;
}