stou.ha (5642B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use ascii; 5 use encoding::utf8; 6 use strings; 7 use types; 8 9 fn rune_to_integer(r: rune) (u64 | void) = { 10 if (ascii::isdigit(r)) 11 return (r: u32 - '0'): u64 12 else if (ascii::isalpha(r) && ascii::islower(r)) 13 return (r: u32 - 'a'): u64 + 10 14 else if (ascii::isalpha(r) && ascii::isupper(r)) 15 return (r: u32 - 'A'): u64 + 10; 16 }; 17 18 fn parseint(s: str, base: base) ((bool, u64) | invalid | overflow) = { 19 if (base == base::DEFAULT) { 20 base = base::DEC; 21 }; 22 assert(base == 2 || base == 8 || base == 10 || base == 16); 23 24 if (len(s) == 0) { 25 return 0: invalid; 26 }; 27 28 let buf = strings::toutf8(s); 29 let i = 0z; 30 31 let sign = buf[i] == '-'; 32 if (sign || buf[i] == '+') { 33 i += 1; 34 }; 35 36 // Require at least one digit. 37 if (i == len(buf)) { 38 return i: invalid; 39 }; 40 41 let n = 0u64; 42 for (i < len(buf); i += 1) { 43 const digit = match (rune_to_integer(buf[i]: rune)) { 44 case void => 45 return i: invalid; 46 case let d: u64 => 47 yield d; 48 }; 49 50 if (digit >= base) { 51 return i: invalid; 52 }; 53 54 const old = n; 55 56 n *= base; 57 n += digit; 58 59 if (n < old) { 60 return overflow; 61 }; 62 }; 63 return (sign, n); 64 }; 65 66 // Converts a string to a u64 in the given base. Returns [[invalid]] if the 67 // string is empty or contains invalid characters. Returns [[overflow]] if the 68 // number is too large to be represented by a u64. 69 export fn stou64b(s: str, base: base) (u64 | invalid | overflow) = { 70 let (sign, u) = parseint(s, base)?; 71 if (sign) { 72 return overflow; 73 }; 74 return u; 75 }; 76 77 fn stoumax(s: str, base: base, max: u64) (u64 | invalid | overflow) = { 78 const n = stou64b(s, base)?; 79 if (n > max) { 80 return overflow; 81 }; 82 return n; 83 }; 84 85 // Converts a string to a u32 in the given base. Returns [[invalid]] if the 86 // string is empty or contains invalid characters. Returns [[overflow]] if the 87 // number is too large to be represented by a u32. 88 export fn stou32b(s: str, base: base) (u32 | invalid | overflow) = 89 stoumax(s, base, types::U32_MAX)?: u32; 90 91 // Converts a string to a u16 in the given base. Returns [[invalid]] if the 92 // string is empty or contains invalid characters. Returns [[overflow]] if the 93 // number is too large to be represented by a u16. 94 export fn stou16b(s: str, base: base) (u16 | invalid | overflow) = 95 stoumax(s, base, types::U16_MAX)?: u16; 96 97 // Converts a string to a u8 in the given base. Returns [[invalid]] if the 98 // string is empty or contains invalid characters. Returns [[overflow]] if the 99 // number is too large to be represented by a u8. 100 export fn stou8b(s: str, base: base) (u8 | invalid | overflow) = 101 stoumax(s, base, types::U8_MAX)?: u8; 102 103 // Converts a string to a uint in the given base. Returns [[invalid]] if the 104 // string is empty or contains invalid characters. Returns [[overflow]] if the 105 // number is too large to be represented by a uint. 106 export fn stoub(s: str, base: base) (uint | invalid | overflow) = 107 stoumax(s, base, types::UINT_MAX)?: uint; 108 109 // Converts a string to a size in the given base. Returns [[invalid]] if the 110 // string is empty or contains invalid characters. Returns [[overflow]] if the 111 // number is too large to be represented by a size. 112 export fn stozb(s: str, base: base) (size | invalid | overflow) = 113 stoumax(s, base, types::SIZE_MAX)?: size; 114 115 // Converts a string to a u64 in base 10. Returns [[invalid]] if the string is 116 // empty or contains invalid characters. Returns [[overflow]] if the number is 117 // too large to be represented by a u64. 118 export fn stou64(s: str) (u64 | invalid | overflow) = stou64b(s, base::DEC); 119 120 // Converts a string to a u32 in base 10. Returns [[invalid]] if the string is 121 // empty or contains invalid characters. Returns [[overflow]] if the number is 122 // too large to be represented by a u32. 123 export fn stou32(s: str) (u32 | invalid | overflow) = stou32b(s, base::DEC); 124 125 // Converts a string to a u16 in base 10. Returns [[invalid]] if the string is 126 // empty or contains invalid characters. Returns [[overflow]] if the number is 127 // too large to be represented by a u16. 128 export fn stou16(s: str) (u16 | invalid | overflow) = stou16b(s, base::DEC); 129 130 // Converts a string to a u8 in base 10. Returns [[invalid]] if the string is 131 // empty or contains invalid characters. Returns [[overflow]] if the number is 132 // too large to be represented by a u8. 133 export fn stou8(s: str) (u8 | invalid | overflow) = stou8b(s, base::DEC); 134 135 // Converts a string to a uint in base 10. Returns [[invalid]] if the string is 136 // empty or contains invalid characters. Returns [[overflow]] if the number is 137 // too large to be represented by a uint. 138 export fn stou(s: str) (uint | invalid | overflow) = stoub(s, base::DEC); 139 140 // Converts a string to a size in base 10. Returns [[invalid]] if the string is 141 // empty or contains invalid characters. Returns [[overflow]] if the number is 142 // too large to be represented by a size. 143 export fn stoz(s: str) (size | invalid | overflow) = stozb(s, base::DEC); 144 145 @test fn stou() void = { 146 assert(stou64("") as invalid == 0); 147 assert(stou64("+") as invalid == 1); 148 assert(stou64("+a") as invalid == 1); 149 assert(stou64("abc") as invalid == 0); 150 assert(stou64("1a") as invalid == 1); 151 152 assert(stou64("18446744073709551616") is overflow); 153 assert(stou64("184467440737095516150") is overflow); 154 assert(stou64("-1") is overflow); 155 156 assert(stou64("0") as u64 == 0); 157 assert(stou64("1") as u64 == 1); 158 assert(stou64("18446744073709551615") as u64 == 18446744073709551615); 159 }; 160 161 @test fn stoub() void = { 162 assert(stou64b("7f", 16) as u64 == 0x7f); 163 assert(stou64b("7F", 16) as u64 == 0x7f); 164 assert(stou64b("37", 8) as u64 == 0o37); 165 assert(stou64b("110101", 2) as u64 == 0b110101); 166 };