From b978959fae1c7449f24706fb3e33630b617a73f0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Mar 2019 20:58:01 +0000 Subject: [PATCH] Improve package strconv --- core/fmt/fmt.odin | 2 +- core/strconv/generic_float.odin | 361 +++++++++++++++++++++++++++ core/strconv/strconv.odin | 418 ++++++-------------------------- 3 files changed, 430 insertions(+), 351 deletions(-) create mode 100644 core/strconv/generic_float.odin diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 8657c6db7..c1d21c00d 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -3,7 +3,7 @@ package fmt import "core:runtime" import "core:os" import "core:mem" -import "core:bits" +import "core:math/bits" import "core:unicode/utf8" import "core:types" import "core:strconv" diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin new file mode 100644 index 000000000..807d7ec04 --- /dev/null +++ b/core/strconv/generic_float.odin @@ -0,0 +1,361 @@ +package strconv + +using import "core:decimal" + +Int_Flag :: enum { + Prefix, + Plus, + Space, +} +Int_Flags :: bit_set[Int_Flag]; + +Decimal_Slice :: struct { + digits: []byte, + count: int, + decimal_point: int, + neg: bool, +} + +Float_Info :: struct { + mantbits: uint, + expbits: uint, + bias: int, +} + + +_f16_info := Float_Info{10, 5, -15}; +_f32_info := Float_Info{23, 8, -127}; +_f64_info := Float_Info{52, 11, -1023}; + + +generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte { + bits: u64; + flt: ^Float_Info; + switch bit_size { + case 32: + bits = u64(transmute(u32)f32(val)); + flt = &_f32_info; + case 64: + bits = transmute(u64)val; + flt = &_f64_info; + case: + panic("strconv: invalid bit_size"); + } + + neg := bits>>(flt.expbits+flt.mantbits) != 0; + exp := int(bits>>flt.mantbits) & (1< []byte { + Buffer :: struct { + b: []byte, + n: int, + } + + to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n]; + add_bytes :: proc(buf: ^Buffer, bytes: ..byte) { + buf.n += copy(buf.b[buf.n:], bytes); + } + + b := Buffer{b = buf}; + + switch fmt { + case 'f', 'F': + add_bytes(&b, neg ? '-' : '+'); + + // integer, padded with zeros when needed + if digs.decimal_point > 0 { + m := min(digs.count, digs.decimal_point); + add_bytes(&b, ..digs.digits[0:m]); + for ; m < digs.decimal_point; m += 1 { + add_bytes(&b, '0'); + } + } else { + add_bytes(&b, '0'); + } + + + // fractional part + if prec > 0 { + add_bytes(&b, '.'); + for i in 0..prec-1 { + c: byte = '0'; + if j := digs.decimal_point + i; 0 <= j && j < digs.count { + c = digs.digits[j]; + } + add_bytes(&b, c); + } + } + return to_bytes(b); + + case 'e', 'E': + add_bytes(&b, neg ? '-' : '+'); + + ch := byte('0'); + if digs.count != 0 { + ch = digs.digits[0]; + } + add_bytes(&b, ch); + + if prec > 0 { + add_bytes(&b, '.'); + i := 1; + m := min(digs.count, prec+1); + if i < m { + add_bytes(&b, ..digs.digits[i:m]); + i = m; + } + for ; i <= prec; i += 1 { + add_bytes(&b, '0'); + } + } + + add_bytes(&b, fmt); + exp := digs.decimal_point-1; + if digs.count == 0 { + // Zero has exponent of 0 + exp = 0; + } + + ch = '+'; + if exp < 0 { + ch = '-'; + exp = -exp; + } + add_bytes(&b, ch); + + switch { + case exp < 10: add_bytes(&b, '0', byte(exp)+'0'); // add prefix 0 + case exp < 100: add_bytes(&b, byte(exp/10)+'0', byte(exp%10)+'0'); + case: add_bytes(&b, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0'); + } + + return to_bytes(b); + + case 'g', 'G': + eprec := prec; + if eprec > digs.count && digs.count >= digs.decimal_point { + eprec = digs.count; + } + + if shortest { + eprec = 6; + } + + exp := digs.decimal_point - 1; + if exp < -4 || exp >= eprec { + if prec > digs.count { + prec = digs.count; + } + return format_digits(buf, shortest, neg, digs, prec-1, fmt+'e'-'g'); // keep the same case + } + + if prec > digs.decimal_point { + prec = digs.count; + } + + return format_digits(buf, shortest, neg, digs, max(prec-digs.decimal_point, 0), 'f'); + + case: + add_bytes(&b, '%', fmt); + return to_bytes(b); + } + + +} + +round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) { + if mant == 0 { // If mantissa is zero, the number is zero + d.count = 0; + return; + } + + /* + 10^(dp-nd) > 2^(exp-mantbits) + log2(10) * (dp-nd) > exp-mantbits + log(2) >~ 0.332 + 332*(dp-nd) >= 100*(exp-mantbits) + */ + minexp := flt.bias+1; + if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) { + // Number is already its shortest + return; + } + + upper_: Decimal; upper := &upper_; + assign(upper, 2*mant - 1); + shift(upper, exp - int(flt.mantbits) - 1); + + mantlo: u64; + explo: int; + if mant > 1< (unsigned: u64, neg: bool) { + 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 u, neg; +} + +append_bits :: proc(buf: []byte, u: 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"); + } + + neg: bool; + a: [129]byte; + i := len(a); + u, neg = is_integer_negative(u, 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)]); +} diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 07f506b6d..0b1f10f6c 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1,14 +1,6 @@ package strconv -using import "core:decimal" - -Int_Flag :: enum { - Prefix, - Plus, - Space, -} -Int_Flags :: bit_set[Int_Flag]; - +import "core:unicode/utf8" parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) { switch s { @@ -204,363 +196,89 @@ append_uint :: proc(buf: []byte, u: u64, base: int) -> string { append_int :: proc(buf: []byte, i: i64, base: int) -> string { return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil); } -itoa :: proc(buf: []byte, i: int) -> string do return append_int(buf, i64(i), 10); + +itoa :: proc(buf: []byte, i: int) -> string { + return append_int(buf, i64(i), 10); +} +atoi :: proc(s: string) -> int { + return parse_int(s); +} append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { return string(generic_ftoa(buf, f, fmt, prec, bit_size)); } +quote :: proc(buf: []byte, s: string) -> string { + write_byte :: inline proc(buf: []byte, i: ^int, bytes: ..byte) { + n := copy(buf[i^:], bytes[:]); + i^ += n; + } + c :: '"'; + i := 0; -Decimal_Slice :: struct { - digits: []byte, - count: int, - decimal_point: int, - neg: bool, + write_byte(buf, &i, c); + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]); + width = 1; + if r >= utf8.RUNE_SELF { + r, width = utf8.decode_rune_in_string(s); + } + if width == 1 && r == utf8.RUNE_ERROR { + write_byte(buf, &i, '\\', 'x'); + write_byte(buf, &i, digits[s[0]>>4]); + write_byte(buf, &i, digits[s[0]&0xf]); + } + s := quote_rune(buf[i:], r); + i += len(s); + } + write_byte(buf, &i, c); + return string(buf[:i]); } -Float_Info :: struct { - mantbits: uint, - expbits: uint, - bias: int, -} +quote_rune :: proc(buf: []byte, r: rune) -> string { + write_byte :: inline proc(buf: []byte, i: ^int, bytes: ..byte) { + n := copy(buf[i^:], bytes[:]); + i^ += n; + } + write_string :: inline proc(buf: []byte, i: ^int, s: string) { + n := copy(buf[i^:], cast([]byte)s); + i^ += n; + } + write_rune :: inline proc(buf: []byte, i: ^int, r: rune) { + b, w := utf8.encode_rune(r); + n := copy(buf[i^:], b[:w]); + i^ += n; + } + i := 0; + write_byte(buf, &i, '\''); -_f16_info := Float_Info{10, 5, -15}; -_f32_info := Float_Info{23, 8, -127}; -_f64_info := Float_Info{52, 11, -1023}; - - -generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte { - bits: u64; - flt: ^Float_Info; - switch bit_size { - case 32: - bits = u64(transmute(u32)f32(val)); - flt = &_f32_info; - case 64: - bits = transmute(u64)val; - flt = &_f64_info; + switch r { + case '\a': write_string(buf, &i, "\\a"); + case '\b': write_string(buf, &i, "\\b"); + case '\e': write_string(buf, &i, "\\e"); + case '\f': write_string(buf, &i, "\\f"); + case '\n': write_string(buf, &i, "\\n"); + case '\r': write_string(buf, &i, "\\r"); + case '\t': write_string(buf, &i, "\\t"); + case '\v': write_string(buf, &i, "\\v"); case: - panic("strconv: invalid bit_size"); - } - - neg := bits>>(flt.expbits+flt.mantbits) != 0; - exp := int(bits>>flt.mantbits) & (1< []byte { - Buffer :: struct { - b: []byte, - n: int, - } - - to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n]; - add_bytes :: proc(buf: ^Buffer, bytes: ..byte) { - buf.n += copy(buf.b[buf.n:], bytes); - } - - b := Buffer{b = buf}; - - switch fmt { - case 'f', 'F': - add_bytes(&b, neg ? '-' : '+'); - - // integer, padded with zeros when needed - if digs.decimal_point > 0 { - m := min(digs.count, digs.decimal_point); - add_bytes(&b, ..digs.digits[0:m]); - for ; m < digs.decimal_point; m += 1 { - add_bytes(&b, '0'); + if r < 32 { + write_string(buf, &i, "\\x"); + b: [2]byte; + s := append_bits(b[:], u64(r), 16, true, 64, digits, nil); + switch len(s) { + case 0: write_string(buf, &i, "00"); + case 1: write_rune(buf, &i, '0'); + case 2: write_string(buf, &i, s); } } else { - add_bytes(&b, '0'); + write_rune(buf, &i, r); } - - - // fractional part - if prec > 0 { - add_bytes(&b, '.'); - for i in 0..prec-1 { - c: byte = '0'; - if j := digs.decimal_point + i; 0 <= j && j < digs.count { - c = digs.digits[j]; - } - add_bytes(&b, c); - } - } - return to_bytes(b); - - case 'e', 'E': - add_bytes(&b, neg ? '-' : '+'); - - ch := byte('0'); - if digs.count != 0 { - ch = digs.digits[0]; - } - add_bytes(&b, ch); - - if prec > 0 { - add_bytes(&b, '.'); - i := 1; - m := min(digs.count, prec+1); - if i < m { - add_bytes(&b, ..digs.digits[i:m]); - i = m; - } - for ; i <= prec; i += 1 { - add_bytes(&b, '0'); - } - } - - add_bytes(&b, fmt); - exp := digs.decimal_point-1; - if digs.count == 0 { - // Zero has exponent of 0 - exp = 0; - } - - ch = '+'; - if exp < 0 { - ch = '-'; - exp = -exp; - } - add_bytes(&b, ch); - - switch { - case exp < 10: add_bytes(&b, '0', byte(exp)+'0'); // add prefix 0 - case exp < 100: add_bytes(&b, byte(exp/10)+'0', byte(exp%10)+'0'); - case: add_bytes(&b, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0'); - } - - return to_bytes(b); - - case 'g', 'G': - eprec := prec; - if eprec > digs.count && digs.count >= digs.decimal_point { - eprec = digs.count; - } - - if shortest { - eprec = 6; - } - - exp := digs.decimal_point - 1; - if exp < -4 || exp >= eprec { - if prec > digs.count { - prec = digs.count; - } - return format_digits(buf, shortest, neg, digs, prec-1, fmt+'e'-'g'); // keep the same case - } - - if prec > digs.decimal_point { - prec = digs.count; - } - - return format_digits(buf, shortest, neg, digs, max(prec-digs.decimal_point, 0), 'f'); - - case: - add_bytes(&b, '%', fmt); - return to_bytes(b); } + write_byte(buf, &i, '\''); - + return string(buf[:i]); } - -round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) { - if mant == 0 { // If mantissa is zero, the number is zero - d.count = 0; - return; - } - - /* - 10^(dp-nd) > 2^(exp-mantbits) - log2(10) * (dp-nd) > exp-mantbits - log(2) >~ 0.332 - 332*(dp-nd) >= 100*(exp-mantbits) - */ - minexp := flt.bias+1; - if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) { - // Number is already its shortest - return; - } - - upper_: Decimal; upper := &upper_; - assign(upper, 2*mant - 1); - shift(upper, exp - int(flt.mantbits) - 1); - - mantlo: u64; - explo: int; - if mant > 1< (unsigned: u64, neg: bool) { - 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 u, neg; -} - -append_bits :: proc(buf: []byte, u: 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"); - } - - neg: bool; - a: [129]byte; - i := len(a); - u, neg = is_integer_negative(u, 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)]); -} -