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