~sircmpwn/hare

ref: c66099085ccde84906ac368ee82214a7422bfd42 hare/strconv/stou.ha -rw-r--r-- 5.4 KiB
c6609908Eyal Sawady README.md: fix typo 1 year, 3 months ago
                                                                                
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use strings;
use types;
use ascii;
use encoding::utf8;

fn rune_to_integer(r: rune) (u64 | void) = {
	if (ascii::isdigit(r))
		return (r: u32 - '0': u32): u64
	else if (ascii::isalpha(r) && ascii::islower(r))
		return (r: u32 - 'a': u32): u64 + 10
	else if (ascii::isalpha(r) && ascii::isupper(r))
		return (r: u32 - 'A': u32): u64 + 10;
};

// Converts a string to a u64 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u64, [strconv::overflow] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou64b(s: str, base: uint) (u64 | invalid | overflow) = {
	assert(base == 2 || base == 8 || base == 10 || base == 16);

	if (len(s) == 0) {
		return invalid;
	};

	let n = 0z;
	let iter = strings::iter(s);
	for (true) {
		let r: rune = match (strings::next(&iter)) {
			void => break,
			r: rune => r,
		};

		let digit = match (rune_to_integer(r)) {
			void => return invalid,
			d: u64 => d,
		};

		if (digit >= base: u64) return invalid;

		let old = n;

		n *= base;
		n += digit;

		if (n < old) {
			return overflow;
		};
	};
	return n;
};

// Converts a string to a u32 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u32, [strconv::overflow] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou32b(s: str, base: uint) (u32 | invalid | overflow) = {
	let n = stou64b(s, base)?;
	if (n <= types::U32_MAX: u64) {
		return n: u32;
	};
	return overflow;
};

// Converts a string to a u16 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u16, [strconv::overflow] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou16b(s: str, base: uint) (u16 | invalid | overflow) = {
	let n = stou64b(s, base)?;
	if (n <= types::U16_MAX: u64) {
		return n: u16;
	};
	return overflow;
};

// Converts a string to a u8 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u8, [strconv::overflow] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou8b(s: str, base: uint) (u8 | invalid | overflow) = {
	let n = stou64b(s, base)?;
	if (n <= types::U8_MAX: u64) {
		return n: u8;
	};
	return overflow;
};

// Converts a string to a uint in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a uint, [strconv::overflow] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stoub(s: str, base: uint) (uint | invalid | overflow) = {
	static assert(size(uint) == size(u32) || size(uint) == size(u64));
	return
		if (size(uint) == size(u32)) stou32b(s, base)?: uint
		else stou64b(s, base)?: uint;
};

// Converts a string to a size in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a size, [strconv::overflow] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stozb(s: str, base: uint) (size | invalid | overflow) = {
	static assert(size(size) == size(u32) || size(size) == size(u64));
	return if (size(size) == size(u32)) match (stou32b(s, base)) {
		v: (invalid | overflow) => v,
		n: u32 => n: size,
	} else match (stou64b(s, base)) {
		v: (invalid | overflow) => v,
		n: u64 => n: size,
	};
};

// Converts a string to a u64 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u64, [strconv::overflow] is
// returned.
export fn stou64(s: str) (u64 | invalid | overflow) = stou64b(s, 10);

// Converts a string to a u32 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u32, [strconv::overflow] is
// returned.
export fn stou32(s: str) (u32 | invalid | overflow) = stou32b(s, 10);

// Converts a string to a u16 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u16, [strconv::overflow] is
// returned.
export fn stou16(s: str) (u16 | invalid | overflow) = stou16b(s, 10);

// Converts a string to a u8 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u8, [strconv::overflow] is
// returned.
export fn stou8(s: str) (u8 | invalid | overflow) = stou8b(s, 10);

// Converts a string to a uint in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a uint, [strconv::overflow] is
// returned.
export fn stou(s: str) (uint | invalid | overflow) = stoub(s, 10);

// Converts a string to a u64 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u64, [strconv::overflow] is
// returned.
export fn stoz(s: str) (size | invalid | overflow) = stozb(s, 10);