diff --git a/core/strconv/integers.odin b/core/strconv/integers.odin new file mode 100644 index 000000000..12fd0029c --- /dev/null +++ b/core/strconv/integers.odin @@ -0,0 +1,167 @@ +package strconv + +Int_Flag :: enum { + Prefix, + Plus, + Space, +} +Int_Flags :: bit_set[Int_Flag]; + +MAX_BASE :: 32; +digits := "0123456789abcdefghijklmnopqrstuvwxyz"; + + +is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) { + u = x; + if is_signed { + switch bit_size { + case 8: + i := i8(u); + neg = i < 0; + u = u64(abs(i64(i))); + case 16: + i := i16(u); + neg = i < 0; + u = u64(abs(i64(i))); + case 32: + i := i32(u); + neg = i < 0; + u = u64(abs(i64(i))); + case 64: + i := i64(u); + neg = i < 0; + u = u64(abs(i64(i))); + case: + panic("is_integer_negative: Unknown integer size"); + } + } + return; +} + +append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { + if base < 2 || base > MAX_BASE { + panic("strconv: illegal base passed to append_bits"); + } + + a: [129]byte; + i := len(a); + u, neg := is_integer_negative(x, is_signed, bit_size); + b := u64(base); + for u >= b { + i-=1; a[i] = digits[u % b]; + u /= b; + } + i-=1; a[i] = digits[u % b]; + + if .Prefix in flags { + ok := true; + switch base { + case 2: i-=1; a[i] = 'b'; + case 8: i-=1; a[i] = 'o'; + case 10: i-=1; a[i] = 'd'; + case 12: i-=1; a[i] = 'z'; + case 16: i-=1; a[i] = 'x'; + case: ok = false; + } + if ok { + i-=1; a[i] = '0'; + } + } + + switch { + case neg: + i-=1; a[i] = '-'; + case .Plus in flags: + i-=1; a[i] = '+'; + case .Space in flags: + i-=1; a[i] = ' '; + } + + out := a[i:]; + copy(buf, out); + return string(buf[0:len(out)]); +} + +is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) { + u = x; + if is_signed { + switch bit_size { + case 8: + i := i8(u); + neg = i < 0; + u = u128(abs(i128(i))); + case 16: + i := i16(u); + neg = i < 0; + u = u128(abs(i128(i))); + case 32: + i := i32(u); + neg = i < 0; + u = u128(abs(i128(i))); + case 64: + i := i64(u); + neg = i < 0; + u = u128(abs(i128(i))); + case 128: + i := i128(u); + neg = i < 0; + u = u128(abs(i128(i))); + case: + panic("is_integer_negative: Unknown integer size"); + } + } + return; +} + +import "core:runtime" + +append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { + if base < 2 || base > MAX_BASE { + panic("strconv: illegal base passed to append_bits"); + } + + a: [140]byte; + i := len(a); + u, neg := is_integer_negative_128(x, is_signed, bit_size); + b := u128(base); + for u >= b && i >= 0 { + i-=1; + // rem: u128; + // u = runtime.udivmod128(u, b, &rem); + // u /= b; + rem := u % b; + u /= b; + + idx := u32(rem); + a[i] = digits[idx]; + } + i-=1; a[i] = digits[u64(u % b)]; + + if .Prefix in flags { + ok := true; + switch base { + case 2: i-=1; a[i] = 'b'; + case 8: i-=1; a[i] = 'o'; + case 10: i-=1; a[i] = 'd'; + case 12: i-=1; a[i] = 'z'; + case 16: i-=1; a[i] = 'x'; + case: ok = false; + } + if ok { + i-=1; a[i] = '0'; + } + } + + switch { + case neg: + i-=1; a[i] = '-'; + case .Plus in flags: + i-=1; a[i] = '+'; + case .Space in flags: + i-=1; a[i] = ' '; + } + + out := a[i:]; + copy(buf, out); + return string(buf[0:len(out)]); +}