From b3d797598e1411524171e0f0cd7e672cc1c85ef6 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sat, 19 Feb 2022 15:48:32 -0800 Subject: [PATCH 01/16] fprintf tweaked to avoid calling file_size --- core/fmt/fmt.odin | 66 +++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 360a00b32..d800fd149 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -236,10 +236,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info arg_index: int = 0 + ret: int = 0 end := len(fmt) was_prev_index := false - size0 := io.size(auto_cast w) + total_size := 0 loop: for i := 0; i < end; /**/ { fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered} @@ -249,7 +250,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 } if i > prev_i { - io.write_string(fi.writer, fmt[prev_i:i]) + ret, _ = io.write_string(fi.writer, fmt[prev_i:i]) + total_size += ret } if i >= end { break loop @@ -265,12 +267,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 } io.write_byte(fi.writer, char) + total_size += 1 continue loop } else if char == '{' { if i < end && fmt[i] == char { // Skip extra one i += 1 io.write_byte(fi.writer, char) + total_size += 1 continue loop } } @@ -301,7 +305,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - io.write_string(w, "%!(BAD WIDTH)") + ret, _ = io.write_string(w, "%!(BAD WIDTH)") + total_size += ret } if fi.width < 0 { @@ -332,7 +337,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - io.write_string(fi.writer, "%!(BAD PRECISION)") + ret, _ = io.write_string(fi.writer, "%!(BAD PRECISION)") + total_size += ret } was_prev_index = false } else { @@ -345,7 +351,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - io.write_string(fi.writer, "%!(NO VERB)") + ret, _ = io.write_string(fi.writer, "%!(NO VERB)") + total_size += ret break loop } @@ -355,10 +362,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case verb == '%': io.write_byte(fi.writer, '%') + total_size += 1 case !fi.good_arg_index: - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") + ret, _ = io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") + total_size += ret case arg_index >= len(args): - io.write_string(fi.writer, "%!(MISSING ARGUMENT)") + ret, _ = io.write_string(fi.writer, "%!(MISSING ARGUMENT)") + total_size += ret case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -374,14 +384,16 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { arg_index = new_arg_index i = new_i } else { - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ") + ret, _ = io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ") + total_size += ret // Skip over the bad argument start_index := i for i < end && fmt[i] != '}' && fmt[i] != ':' { i += 1 } fmt_arg(&fi, fmt[start_index:i], 'v') - io.write_string(fi.writer, ")") + ret, _ = io.write_string(fi.writer, ")") + total_size += ret } } @@ -414,7 +426,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - io.write_string(fi.writer, "%!(BAD WIDTH)") + ret, _ = io.write_string(fi.writer, "%!(BAD WIDTH)") + total_size += ret } if fi.width < 0 { @@ -445,7 +458,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - io.write_string(fi.writer, "%!(BAD PRECISION)") + ret, _ = io.write_string(fi.writer, "%!(BAD PRECISION)") + total_size += ret } was_prev_index = false } else { @@ -459,7 +473,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { if i >= end { - io.write_string(fi.writer, "%!(NO VERB)") + ret, _ = io.write_string(fi.writer, "%!(NO VERB)") + total_size += ret break loop } @@ -469,7 +484,8 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") + ret, _ = io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") + total_size += ret break loop } @@ -478,11 +494,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case brace != '}': - io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") + ret, _ = io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") + total_size += ret case !fi.good_arg_index: - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") + ret, _ = io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") + total_size += ret case arg_index >= len(args): - io.write_string(fi.writer, "%!(MISSING ARGUMENT)") + ret, _ = io.write_string(fi.writer, "%!(MISSING ARGUMENT)") + total_size += ret case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -491,25 +510,28 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if !fi.reordered && arg_index < len(args) { - io.write_string(fi.writer, "%!(EXTRA ") + ret, _ = io.write_string(fi.writer, "%!(EXTRA ") + total_size += ret for arg, index in args[arg_index:] { if index > 0 { - io.write_string(fi.writer, ", ") + ret, _ = io.write_string(fi.writer, ", ") + total_size += ret } if arg == nil { - io.write_string(fi.writer, "") + ret, _ = io.write_string(fi.writer, "") + total_size += ret } else { fmt_arg(&fi, args[index], 'v') } } - io.write_string(fi.writer, ")") + ret, _ = io.write_string(fi.writer, ")") + total_size += ret } io.flush(auto_cast w) - size1 := io.size(auto_cast w) - return int(size1 - size0) + return int(total_size) } // wprint_type is a utility procedure to write a ^runtime.Type_Info value to w From ddf9c4a65b1cddc44665274a311be2449d48977c Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sat, 19 Feb 2022 16:38:33 -0800 Subject: [PATCH 02/16] switch to passing total_size to the io.writers --- core/fmt/fmt.odin | 67 ++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 45 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index d800fd149..2dc7534f9 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -236,7 +236,6 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info arg_index: int = 0 - ret: int = 0 end := len(fmt) was_prev_index := false @@ -250,8 +249,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 } if i > prev_i { - ret, _ = io.write_string(fi.writer, fmt[prev_i:i]) - total_size += ret + io.write_string(fi.writer, fmt[prev_i:i], &total_size) } if i >= end { break loop @@ -266,15 +264,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { // Skip extra one i += 1 } - io.write_byte(fi.writer, char) - total_size += 1 + io.write_byte(fi.writer, char, &total_size) continue loop } else if char == '{' { if i < end && fmt[i] == char { // Skip extra one i += 1 - io.write_byte(fi.writer, char) - total_size += 1 + io.write_byte(fi.writer, char, &total_size) continue loop } } @@ -305,8 +301,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - ret, _ = io.write_string(w, "%!(BAD WIDTH)") - total_size += ret + io.write_string(w, "%!(BAD WIDTH)", &total_size) } if fi.width < 0 { @@ -337,8 +332,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - ret, _ = io.write_string(fi.writer, "%!(BAD PRECISION)") - total_size += ret + io.write_string(fi.writer, "%!(BAD PRECISION)", &total_size) } was_prev_index = false } else { @@ -351,8 +345,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - ret, _ = io.write_string(fi.writer, "%!(NO VERB)") - total_size += ret + io.write_string(fi.writer, "%!(NO VERB)", &total_size) break loop } @@ -361,14 +354,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case verb == '%': - io.write_byte(fi.writer, '%') - total_size += 1 + io.write_byte(fi.writer, '%', &total_size) case !fi.good_arg_index: - ret, _ = io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") - total_size += ret + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &total_size) case arg_index >= len(args): - ret, _ = io.write_string(fi.writer, "%!(MISSING ARGUMENT)") - total_size += ret + io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &total_size) case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -384,16 +374,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { arg_index = new_arg_index i = new_i } else { - ret, _ = io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ") - total_size += ret + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &total_size) // Skip over the bad argument start_index := i for i < end && fmt[i] != '}' && fmt[i] != ':' { i += 1 } fmt_arg(&fi, fmt[start_index:i], 'v') - ret, _ = io.write_string(fi.writer, ")") - total_size += ret + io.write_string(fi.writer, ")", &total_size) } } @@ -426,8 +414,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - ret, _ = io.write_string(fi.writer, "%!(BAD WIDTH)") - total_size += ret + io.write_string(fi.writer, "%!(BAD WIDTH)", &total_size) } if fi.width < 0 { @@ -458,8 +445,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - ret, _ = io.write_string(fi.writer, "%!(BAD PRECISION)") - total_size += ret + io.write_string(fi.writer, "%!(BAD PRECISION)", &total_size) } was_prev_index = false } else { @@ -473,8 +459,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { if i >= end { - ret, _ = io.write_string(fi.writer, "%!(NO VERB)") - total_size += ret + io.write_string(fi.writer, "%!(NO VERB)", &total_size) break loop } @@ -484,8 +469,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - ret, _ = io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") - total_size += ret + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &total_size) break loop } @@ -494,14 +478,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case brace != '}': - ret, _ = io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)") - total_size += ret + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &total_size) case !fi.good_arg_index: - ret, _ = io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)") - total_size += ret + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &total_size) case arg_index >= len(args): - ret, _ = io.write_string(fi.writer, "%!(MISSING ARGUMENT)") - total_size += ret + io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &total_size) case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -510,23 +491,19 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if !fi.reordered && arg_index < len(args) { - ret, _ = io.write_string(fi.writer, "%!(EXTRA ") - total_size += ret + io.write_string(fi.writer, "%!(EXTRA ", &total_size) for arg, index in args[arg_index:] { if index > 0 { - ret, _ = io.write_string(fi.writer, ", ") - total_size += ret + io.write_string(fi.writer, ", ", &total_size) } if arg == nil { - ret, _ = io.write_string(fi.writer, "") - total_size += ret + io.write_string(fi.writer, "", &total_size) } else { fmt_arg(&fi, args[index], 'v') } } - ret, _ = io.write_string(fi.writer, ")") - total_size += ret + io.write_string(fi.writer, ")", &total_size) } io.flush(auto_cast w) From 14cb19c2df6c5204ddd691a62b80de0bb0d573cb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 20 Feb 2022 22:31:13 +0000 Subject: [PATCH 03/16] Update fmt to record the bytes written in the fmt.Info --- core/fmt/fmt.odin | 413 ++++++++++++++++++++-------------------- core/reflect/types.odin | 8 +- 2 files changed, 207 insertions(+), 214 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 2dc7534f9..d6c1894b7 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -32,6 +32,8 @@ Info :: struct { writer: io.Writer, arg: any, // Temporary record_level: int, + + n: int, // bytes written } // Custom formatter signature. It returns true if the formatting was successful and false when it could not be done @@ -196,19 +198,16 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { // so I am going to keep the same behaviour as `*println` for `*print` - size0 := io.size(auto_cast w) - for _, i in args { if i > 0 { - io.write_string(fi.writer, sep) + io.write_string(fi.writer, sep, &fi.n) } fmt_value(&fi, args[i], 'v') } io.flush(auto_cast w) - size1 := io.size(auto_cast w) - return int(size1 - size0) + return fi.n } // wprintln formats using the default print settings and writes to w @@ -216,20 +215,16 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w - size0 := io.size(auto_cast w) - for _, i in args { if i > 0 { - io.write_string(fi.writer, sep) + io.write_string(fi.writer, sep, &fi.n) } fmt_value(&fi, args[i], 'v') } - io.write_byte(fi.writer, '\n') + io.write_byte(fi.writer, '\n', &fi.n) io.flush(auto_cast w) - - size1 := io.size(auto_cast w) - return int(size1 - size0) + return fi.n } // wprintf formats according to the specififed format string and writes to w @@ -239,8 +234,6 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { end := len(fmt) was_prev_index := false - total_size := 0 - loop: for i := 0; i < end; /**/ { fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered} @@ -249,7 +242,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 } if i > prev_i { - io.write_string(fi.writer, fmt[prev_i:i], &total_size) + io.write_string(fi.writer, fmt[prev_i:i], &fi.n) } if i >= end { break loop @@ -264,13 +257,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { // Skip extra one i += 1 } - io.write_byte(fi.writer, char, &total_size) + io.write_byte(fi.writer, char, &fi.n) continue loop } else if char == '{' { if i < end && fmt[i] == char { // Skip extra one i += 1 - io.write_byte(fi.writer, char, &total_size) + io.write_byte(fi.writer, char, &fi.n) continue loop } } @@ -301,7 +294,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - io.write_string(w, "%!(BAD WIDTH)", &total_size) + io.write_string(w, "%!(BAD WIDTH)", &fi.n) } if fi.width < 0 { @@ -332,7 +325,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - io.write_string(fi.writer, "%!(BAD PRECISION)", &total_size) + io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n) } was_prev_index = false } else { @@ -345,7 +338,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - io.write_string(fi.writer, "%!(NO VERB)", &total_size) + io.write_string(fi.writer, "%!(NO VERB)", &fi.n) break loop } @@ -354,11 +347,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case verb == '%': - io.write_byte(fi.writer, '%', &total_size) + io.write_byte(fi.writer, '%', &fi.n) case !fi.good_arg_index: - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &total_size) + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n) case arg_index >= len(args): - io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &total_size) + io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n) case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -374,14 +367,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { arg_index = new_arg_index i = new_i } else { - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &total_size) + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &fi.n) // Skip over the bad argument start_index := i for i < end && fmt[i] != '}' && fmt[i] != ':' { i += 1 } fmt_arg(&fi, fmt[start_index:i], 'v') - io.write_string(fi.writer, ")", &total_size) + io.write_string(fi.writer, ")", &fi.n) } } @@ -414,7 +407,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { i += 1 fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index) if !fi.width_set { - io.write_string(fi.writer, "%!(BAD WIDTH)", &total_size) + io.write_string(fi.writer, "%!(BAD WIDTH)", &fi.n) } if fi.width < 0 { @@ -445,7 +438,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi.prec_set = false } if !fi.prec_set { - io.write_string(fi.writer, "%!(BAD PRECISION)", &total_size) + io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n) } was_prev_index = false } else { @@ -459,7 +452,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { if i >= end { - io.write_string(fi.writer, "%!(NO VERB)", &total_size) + io.write_string(fi.writer, "%!(NO VERB)", &fi.n) break loop } @@ -469,7 +462,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if i >= end { - io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &total_size) + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n) break loop } @@ -478,11 +471,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { switch { case brace != '}': - io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &total_size) + io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n) case !fi.good_arg_index: - io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &total_size) + io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n) case arg_index >= len(args): - io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &total_size) + io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n) case: fmt_arg(&fi, args[arg_index], verb) arg_index += 1 @@ -491,24 +484,24 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { } if !fi.reordered && arg_index < len(args) { - io.write_string(fi.writer, "%!(EXTRA ", &total_size) + io.write_string(fi.writer, "%!(EXTRA ", &fi.n) for arg, index in args[arg_index:] { if index > 0 { - io.write_string(fi.writer, ", ", &total_size) + io.write_string(fi.writer, ", ", &fi.n) } if arg == nil { - io.write_string(fi.writer, "", &total_size) + io.write_string(fi.writer, "", &fi.n) } else { fmt_arg(&fi, args[index], 'v') } } - io.write_string(fi.writer, ")", &total_size) + io.write_string(fi.writer, ")", &fi.n) } io.flush(auto_cast w) - return int(total_size) + return fi.n } // wprint_type is a utility procedure to write a ^runtime.Type_Info value to w @@ -596,23 +589,23 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { - io.write_string(writer, "%!") - io.write_rune(writer, verb) - io.write_byte(writer, '(') + io.write_string(writer, "%!", &fi.n) + io.write_rune(writer, verb, &fi.n) + io.write_byte(writer, '(', &fi.n) if arg.id != nil { - reflect.write_typeid(writer, arg.id) - io.write_byte(writer, '=') + reflect.write_typeid(writer, arg.id, &fi.n) + io.write_byte(writer, '=', &fi.n) fmt_value(fi, arg, 'v') } else { - io.write_string(writer, "") + io.write_string(writer, "", &fi.n) } - io.write_byte(writer, ')') + io.write_byte(writer, ')', &fi.n) } fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': - io.write_string(writer, b ? "true" : "false") + io.write_string(writer, b ? "true" : "false", &fi.n) case: fmt_bad_verb(fi, verb) } @@ -630,7 +623,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { } for i := 0; i < width; i += 1 { - io.write_byte(fi.writer, pad_byte) + io.write_byte(fi.writer, pad_byte, &fi.n) } } @@ -689,8 +682,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d case 16: c = 'x' } if c != 0 { - io.write_byte(fi.writer, '0') - io.write_byte(fi.writer, c) + io.write_byte(fi.writer, '0', &fi.n) + io.write_byte(fi.writer, c, &fi.n) } } @@ -755,8 +748,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i case 16: c = 'x' } if c != 0 { - io.write_byte(fi.writer, '0') - io.write_byte(fi.writer, c) + io.write_byte(fi.writer, '0', &fi.n) + io.write_byte(fi.writer, c, &fi.n) } } @@ -772,9 +765,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX" fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': - io.write_rune(fi.writer, r) + io.write_rune(fi.writer, r, &fi.n) case 'q': - strings.write_quoted_rune(fi.writer, r) + fi.n += strings.write_quoted_rune(fi.writer, r) case: fmt_int(fi, u64(r), false, 32, verb) } @@ -796,7 +789,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb) } else { - io.write_string(fi.writer, "U+") + io.write_string(fi.writer, "U+", &fi.n) _fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER) } @@ -821,7 +814,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru if r < 0 || r > utf8.MAX_RUNE { fmt_bad_verb(fi, verb) } else { - io.write_string(fi.writer, "U+") + io.write_string(fi.writer, "U+", &fi.n) _fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER) } @@ -832,18 +825,18 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru _pad :: proc(fi: ^Info, s: string) { if !fi.width_set { - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) return } width := fi.width - utf8.rune_count_in_string(s) if fi.minus { // right pad - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) fmt_write_padding(fi, width) } else { // left pad fmt_write_padding(fi, width) - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) } } @@ -869,15 +862,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - io.write_string(fi.writer, string(b)) + io.write_string(fi.writer, string(b), &fi.n) return } if fi.plus || b[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - io.write_byte(fi.writer, b[0]) + io.write_byte(fi.writer, b[0], &fi.n) fmt_write_padding(fi, fi.width - len(b)) - io.write_string(fi.writer, string(b[1:])) + io.write_string(fi.writer, string(b[1:]), &fi.n) } else { _pad(fi, string(b)) } @@ -905,15 +898,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { } if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') { - io.write_string(fi.writer, string(b)) + io.write_string(fi.writer, string(b), &fi.n) return } if fi.plus || str[0] != '+' { if fi.zero && fi.width_set && fi.width > len(b) { - io.write_byte(fi.writer, b[0]) + io.write_byte(fi.writer, b[0], &fi.n) fmt_write_padding(fi, fi.width - len(b)) - io.write_string(fi.writer, string(b[1:])) + io.write_string(fi.writer, string(b[1:]), &fi.n) } else { _pad(fi, string(b)) } @@ -937,7 +930,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { case: panic("Unhandled float size") } - io.write_string(fi.writer, "0h") + io.write_string(fi.writer, "0h", &fi.n) _fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER) @@ -950,15 +943,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_string :: proc(fi: ^Info, s: string, verb: rune) { switch verb { case 's', 'v': - io.write_string(fi.writer, s) + io.write_string(fi.writer, s, &fi.n) if fi.width_set && len(s) < fi.width { for _ in 0.. 0 && space { - io.write_byte(fi.writer, ' ') + io.write_byte(fi.writer, ' ', &fi.n) } char_set := __DIGITS_UPPER if verb == 'x' { @@ -989,7 +982,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { switch verb { case 'p', 'v': if !fi.hash || verb == 'v' { - io.write_string(fi.writer, "0x") + io.write_string(fi.writer, "0x", &fi.n) } _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER) @@ -1051,7 +1044,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } @@ -1065,11 +1058,11 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb) case 's', 'v': if str, ok := enum_value_to_string(v); ok { - io.write_string(fi.writer, str) + io.write_string(fi.writer, str, &fi.n) } else { - io.write_string(fi.writer, "%!(BAD ENUM VALUE=") + io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n) fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i') - io.write_string(fi.writer, ")") + io.write_string(fi.writer, ")", &fi.n) } } } @@ -1163,12 +1156,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { et := runtime.type_info_base(info.elem) if name != "" { - io.write_string(fi.writer, name) + io.write_string(fi.writer, name, &fi.n) } else { - reflect.write_type(fi.writer, type_info) + reflect.write_type(fi.writer, type_info, &fi.n) } - io.write_byte(fi.writer, '{') - defer io.write_byte(fi.writer, '}') + io.write_byte(fi.writer, '{', &fi.n) + defer io.write_byte(fi.writer, '}', &fi.n) e, is_enum := et.variant.(runtime.Type_Info_Enum) commas := 0 @@ -1178,21 +1171,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } if commas > 0 { - io.write_string(fi.writer, ", ") + io.write_string(fi.writer, ", ", &fi.n) } if is_enum { for ev, evi in e.values { v := u64(ev) if v == u64(i) { - io.write_string(fi.writer, e.names[evi]) + io.write_string(fi.writer, e.names[evi], &fi.n) commas += 1 continue loop } } } v := i64(i) + info.lower - io.write_i64(fi.writer, v, 10) + io.write_i64(fi.writer, v, 10, &fi.n) commas += 1 } } @@ -1200,20 +1193,20 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { fmt_write_indent :: proc(fi: ^Info) { for in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(array_data) + uintptr(i*elem_size) fmt_arg(fi, any{rawptr(data), elem_id}, verb) @@ -1245,14 +1238,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { n -= 1 } for in 0..") + io.write_string(fi.writer, "", &fi.n) return } @@ -1278,12 +1271,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { // Built-in Custom Formatters for core library types switch a in v { case runtime.Source_Code_Location: - io.write_string(fi.writer, a.file_path) - io.write_byte(fi.writer, '(') - io.write_int(fi.writer, int(a.line)) - io.write_byte(fi.writer, ':') - io.write_int(fi.writer, int(a.column)) - io.write_byte(fi.writer, ')') + io.write_string(fi.writer, a.file_path, &fi.n) + io.write_byte(fi.writer, '(', &fi.n) + io.write_int(fi.writer, int(a.line), 10, &fi.n) + io.write_byte(fi.writer, ':', &fi.n) + io.write_int(fi.writer, int(a.column), 10, &fi.n) + io.write_byte(fi.writer, ')', &fi.n) return case time.Duration: @@ -1337,7 +1330,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { w -= 1 switch { case u == 0: - io.write_string(fi.writer, "0s") + io.write_string(fi.writer, "0s", &fi.n) return case u < u64(time.Microsecond): prec = 0 @@ -1376,7 +1369,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { w -= 1 buf[w] = '-' } - io.write_string(fi.writer, string(buf[w:])) + io.write_string(fi.writer, string(buf[w:]), &fi.n) return case time.Time: @@ -1385,20 +1378,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { h, min, s := time.clock(t) ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9 write_padded_number(fi, i64(y), 4) - io.write_byte(fi.writer, '-') + io.write_byte(fi.writer, '-', &fi.n) write_padded_number(fi, i64(mon), 2) - io.write_byte(fi.writer, '-') + io.write_byte(fi.writer, '-', &fi.n) write_padded_number(fi, i64(d), 2) - io.write_byte(fi.writer, ' ') + io.write_byte(fi.writer, ' ', &fi.n) write_padded_number(fi, i64(h), 2) - io.write_byte(fi.writer, ':') + io.write_byte(fi.writer, ':', &fi.n) write_padded_number(fi, i64(min), 2) - io.write_byte(fi.writer, ':') + io.write_byte(fi.writer, ':', &fi.n) write_padded_number(fi, i64(s), 2) - io.write_byte(fi.writer, '.') + io.write_byte(fi.writer, '.', &fi.n) write_padded_number(fi, (ns), 9) - io.write_string(fi.writer, " +0000 UTC") + io.write_string(fi.writer, " +0000 UTC", &fi.n) return } @@ -1409,15 +1402,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { return } if b.is_raw_union { - io.write_string(fi.writer, info.name) - io.write_string(fi.writer, "{}") + io.write_string(fi.writer, info.name, &fi.n) + io.write_string(fi.writer, "{}", &fi.n) return } is_soa := b.soa_kind != .None - io.write_string(fi.writer, info.name) - io.write_byte(fi.writer, '[' if is_soa else '{') + io.write_string(fi.writer, info.name, &fi.n) + io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n) hash := fi.hash; defer fi.hash = hash indent := fi.indent; defer fi.indent -= 1 @@ -1426,13 +1419,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fi.indent += 1 if hash { - io.write_byte(fi.writer, '\n') + io.write_byte(fi.writer, '\n', &fi.n) } defer { if hash { - for in 0.. 0 { io.write_string(fi.writer, ", ") } + if !hash && index > 0 { io.write_string(fi.writer, ", ", &fi.n) } field_count := -1 - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } + if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) } - io.write_string(fi.writer, base_type_name) - io.write_byte(fi.writer, '{') - defer io.write_byte(fi.writer, '}') + io.write_string(fi.writer, base_type_name, &fi.n) + io.write_byte(fi.writer, '{', &fi.n) + defer io.write_byte(fi.writer, '}', &fi.n) for name, i in b.names { field_count += 1 - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } + if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) } if hash { fmt_write_indent(fi) } - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) t := b.types[i].variant.(runtime.Type_Info_Array).elem t_size := uintptr(t.size) if reflect.is_any(t) { - io.write_string(fi.writer, "any{}") + io.write_string(fi.writer, "any{}", &fi.n) } else { data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size) fmt_arg(fi, any{data, t.id}, 'v') } - if hash { io.write_string(fi.writer, ",\n") } + if hash { io.write_string(fi.writer, ",\n", &fi.n) } } } } else { @@ -1488,17 +1481,17 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fmt_write_indent(fi) } - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) if t := b.types[i]; reflect.is_any(t) { - io.write_string(fi.writer, "any{}") + io.write_string(fi.writer, "any{}", &fi.n) } else { data := rawptr(uintptr(v.data) + b.offsets[i]) fmt_arg(fi, any{data, t.id}, 'v') } - if hash { io.write_string(fi.writer, ",\n") } + if hash { io.write_string(fi.writer, ",\n", &fi.n) } } } @@ -1518,7 +1511,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Pointer: if v.id == typeid_of(^runtime.Type_Info) { - reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^) + reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^, &fi.n) } else { ptr := (^rawptr)(v.data)^ if verb != 'p' && info.elem != nil { @@ -1532,7 +1525,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { runtime.Type_Info_Dynamic_Array, runtime.Type_Info_Map: if ptr == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } if fi.record_level < 1 { @@ -1546,13 +1539,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct, runtime.Type_Info_Union: if ptr == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } if fi.record_level < 1 { fi.record_level += 1 defer fi.record_level -= 1 - io.write_byte(fi.writer, '&') + io.write_byte(fi.writer, '&', &fi.n) fmt_value(fi, a, verb) return } @@ -1575,13 +1568,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { runtime.Type_Info_Dynamic_Array, runtime.Type_Info_Map: if ptr == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } if fi.record_level < 1 { fi.record_level += 1 defer fi.record_level -= 1 - io.write_byte(fi.writer, '&') + io.write_byte(fi.writer, '&', &fi.n) fmt_value(fi, a, verb) return } @@ -1589,13 +1582,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct, runtime.Type_Info_Union: if ptr == nil { - io.write_string(fi.writer, "") + io.write_string(fi.writer, "", &fi.n) return } if fi.record_level < 1 { fi.record_level += 1 defer fi.record_level -= 1 - io.write_byte(fi.writer, '&') + io.write_byte(fi.writer, '&', &fi.n) fmt_value(fi, a, verb) return } @@ -1614,11 +1607,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Enumerated_Array: if fi.hash { - io.write_string(fi.writer, "[\n") + io.write_string(fi.writer, "[\n", &fi.n) defer { - io.write_byte(fi.writer, '\n') + io.write_byte(fi.writer, '\n', &fi.n) fmt_write_indent(fi) - io.write_byte(fi.writer, ']') + io.write_byte(fi.writer, ']', &fi.n) } indent := fi.indent fi.indent += 1 @@ -1629,32 +1622,32 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { idx, ok := stored_enum_value_to_string(info.index, info.min_value, i) if ok { - io.write_byte(fi.writer, '.') - io.write_string(fi.writer, idx) + io.write_byte(fi.writer, '.', &fi.n) + io.write_string(fi.writer, idx, &fi.n) } else { - io.write_i64(fi.writer, i64(info.min_value)+i64(i)) + io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n) } - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, " = ", &fi.n) data := uintptr(v.data) + uintptr(i*info.elem_size) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) - io.write_string(fi.writer, ",\n") + io.write_string(fi.writer, ",\n", &fi.n) } } else { - io.write_byte(fi.writer, '[') - defer io.write_byte(fi.writer, ']') + io.write_byte(fi.writer, '[', &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } idx, ok := stored_enum_value_to_string(info.index, info.min_value, i) if ok { - io.write_byte(fi.writer, '.') - io.write_string(fi.writer, idx) + io.write_byte(fi.writer, '.', &fi.n) + io.write_string(fi.writer, idx, &fi.n) } else { - io.write_i64(fi.writer, i64(info.min_value)+i64(i)) + io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n) } - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, " = ", &fi.n) data := uintptr(v.data) + uintptr(i*info.elem_size) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) @@ -1673,10 +1666,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Simd_Vector: - io.write_byte(fi.writer, '<') - defer io.write_byte(fi.writer, '>') + io.write_byte(fi.writer, '<', &fi.n) + defer io.write_byte(fi.writer, '>', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(v.data) + uintptr(i*info.elem_size) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) @@ -1699,8 +1692,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { return } - io.write_string(fi.writer, "map[") - defer io.write_byte(fi.writer, ']') + io.write_string(fi.writer, "map[", &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) m := (^mem.Raw_Map)(v.data) if m != nil { @@ -1714,14 +1707,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { entry_size := ed.elem_size for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(entries.data) + uintptr(i*entry_size) key := data + entry_type.offsets[2] fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v') - io.write_string(fi.writer, "=") + io.write_string(fi.writer, "=", &fi.n) value := data + entry_type.offsets[3] fmt_arg(fi, any{rawptr(value), info.value.id}, 'v') @@ -1730,21 +1723,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Struct: if info.is_raw_union { - io.write_string(fi.writer, "(raw_union)") + io.write_string(fi.writer, "(raw_union)", &fi.n) return } is_soa := info.soa_kind != .None - io.write_byte(fi.writer, '[' if is_soa else '{') - defer io.write_byte(fi.writer, ']' if is_soa else '}') + io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n) + defer io.write_byte(fi.writer, ']' if is_soa else '}', &fi.n) fi.indent += 1; defer fi.indent -= 1 hash := fi.hash; defer fi.hash = hash // fi.hash = false; - if hash { io.write_byte(fi.writer, '\n') } + if hash { io.write_byte(fi.writer, '\n', &fi.n) } if is_soa { fi.indent += 1 @@ -1773,33 +1766,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { for index in 0.. 0 { io.write_string(fi.writer, ", ") } + if !hash && index > 0 { io.write_string(fi.writer, ", ", &fi.n) } field_count := -1 - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } + if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) } - io.write_string(fi.writer, base_type_name) - io.write_byte(fi.writer, '{') - defer io.write_byte(fi.writer, '}') + io.write_string(fi.writer, base_type_name, &fi.n) + io.write_byte(fi.writer, '{', &fi.n) + defer io.write_byte(fi.writer, '}', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) } if hash { fmt_write_indent(fi) } - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) if info.soa_kind == .Fixed { t := info.types[i].variant.(runtime.Type_Info_Array).elem t_size := uintptr(t.size) if reflect.is_any(t) { - io.write_string(fi.writer, "any{}") + io.write_string(fi.writer, "any{}", &fi.n) } else { data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size) fmt_arg(fi, any{data, t.id}, 'v') @@ -1808,7 +1801,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { t := info.types[i].variant.(runtime.Type_Info_Pointer).elem t_size := uintptr(t.size) if reflect.is_any(t) { - io.write_string(fi.writer, "any{}") + io.write_string(fi.writer, "any{}", &fi.n) } else { field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^ data := rawptr(uintptr(field_ptr) + index*t_size) @@ -1816,7 +1809,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } } - if hash { io.write_string(fi.writer, ",\n") } + if hash { io.write_string(fi.writer, ",\n", &fi.n) } } } } else { @@ -1824,23 +1817,23 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { for name, i in info.names { field_count += 1 - if !hash && field_count > 0 { io.write_string(fi.writer, ", ") } + if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) } if hash { fmt_write_indent(fi) } - io.write_string(fi.writer, name) - io.write_string(fi.writer, " = ") + io.write_string(fi.writer, name, &fi.n) + io.write_string(fi.writer, " = ", &fi.n) if t := info.types[i]; reflect.is_any(t) { - io.write_string(fi.writer, "any{}") + io.write_string(fi.writer, "any{}", &fi.n) } else { data := rawptr(uintptr(v.data) + info.offsets[i]) fmt_arg(fi, any{data, t.id}, 'v') } if hash { - io.write_string(fi.writer, ",\n") + io.write_string(fi.writer, ",\n", &fi.n) } } } @@ -1848,14 +1841,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Union: if type_info.size == 0 { - io.write_string(fi.writer, "nil") + io.write_string(fi.writer, "nil", &fi.n) return } if reflect.type_info_union_is_pure_maybe(info) { if v.data == nil { - io.write_string(fi.writer, "nil") + io.write_string(fi.writer, "nil", &fi.n) } else { id := info.variants[0].id fmt_arg(fi, any{v.data, id}, verb) @@ -1881,12 +1874,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { assert(tag >= 0) if v.data == nil { - io.write_string(fi.writer, "nil") + io.write_string(fi.writer, "nil", &fi.n) } else if info.no_nil { id := info.variants[tag].id fmt_arg(fi, any{v.data, id}, verb) } else if tag == 0 { - io.write_string(fi.writer, "nil") + io.write_string(fi.writer, "nil", &fi.n) } else { id := info.variants[tag-1].id fmt_arg(fi, any{v.data, id}, verb) @@ -1898,16 +1891,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Procedure: ptr := (^rawptr)(v.data)^ if ptr == nil { - io.write_string(fi.writer, "nil") + io.write_string(fi.writer, "nil", &fi.n) } else { - reflect.write_typeid(fi.writer, v.id) - io.write_string(fi.writer, " @ ") + reflect.write_typeid(fi.writer, v.id, &fi.n) + io.write_string(fi.writer, " @ ", &fi.n) fmt_pointer(fi, ptr, 'p') } case runtime.Type_Info_Type_Id: id := (^typeid)(v.data)^ - reflect.write_typeid(fi.writer, id) + reflect.write_typeid(fi.writer, id, &fi.n) case runtime.Type_Info_Bit_Set: fmt_bit_set(fi, v) @@ -1924,18 +1917,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if verb == 'p' { fmt_pointer(fi, ptr, 'p') } else if ptr == nil { - io.write_string(fi.writer, "[]") + io.write_string(fi.writer, "[]", &fi.n) } else { len_ptr := uintptr(v.data) + uintptr(info.base_integer.size) len_any := any{rawptr(len_ptr), info.base_integer.id} len, _ := reflect.as_int(len_any) slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice) - io.write_byte(fi.writer, '[') - defer io.write_byte(fi.writer, ']') + io.write_byte(fi.writer, '[', &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) for i in 0.. 0 { io.write_string(fi.writer, ", ") } + if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(ptr) + uintptr(i*slice_type.elem_size) fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb) @@ -1943,32 +1936,32 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Matrix: - io.write_string(fi.writer, "matrix[") - defer io.write_byte(fi.writer, ']') + io.write_string(fi.writer, "matrix[", &fi.n) + defer io.write_byte(fi.writer, ']', &fi.n) fi.indent += 1 if fi.hash { // Printed as it is written - io.write_byte(fi.writer, '\n') + io.write_byte(fi.writer, '\n', &fi.n) for row in 0.. 0 { io.write_string(fi.writer, ", ") } + if col > 0 { io.write_string(fi.writer, ", ", &fi.n) } offset := (row + col*info.elem_stride)*info.elem_size data := uintptr(v.data) + uintptr(offset) fmt_arg(fi, any{rawptr(data), info.elem.id}, verb) } - io.write_string(fi.writer, ",\n") + io.write_string(fi.writer, ",\n", &fi.n) } } else { // Printed in Row-Major layout to match text layout for row in 0.. 0 { io.write_string(fi.writer, "; ") } + if row > 0 { io.write_string(fi.writer, "; ", &fi.n) } for col in 0.. 0 { io.write_string(fi.writer, ", ") } + if col > 0 { io.write_string(fi.writer, ", ", &fi.n) } offset := (row + col*info.elem_stride)*info.elem_size @@ -1992,10 +1985,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { r, i := real(c), imag(c) fmt_float(fi, r, bits/2, verb) if !fi.plus && i >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, i, bits/2, verb) - io.write_rune(fi.writer, 'i') + io.write_rune(fi.writer, 'i', &fi.n) case: fmt_bad_verb(fi, verb) @@ -2011,22 +2004,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { fmt_float(fi, r, bits/4, verb) if !fi.plus && i >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, i, bits/4, verb) - io.write_rune(fi.writer, 'i') + io.write_rune(fi.writer, 'i', &fi.n) if !fi.plus && j >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, j, bits/4, verb) - io.write_rune(fi.writer, 'j') + io.write_rune(fi.writer, 'j', &fi.n) if !fi.plus && k >= 0 { - io.write_rune(fi.writer, '+') + io.write_rune(fi.writer, '+', &fi.n) } fmt_float(fi, k, bits/4, verb) - io.write_rune(fi.writer, 'k') + io.write_rune(fi.writer, 'k', &fi.n) case: fmt_bad_verb(fi, verb) @@ -2046,7 +2039,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { switch a in arg { case ^runtime.Type_Info: ti = a } - reflect.write_type(fi.writer, ti) + reflect.write_type(fi.writer, ti, &fi.n) return } @@ -2064,12 +2057,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { custom_types: switch a in arg { case runtime.Source_Code_Location: if fi.hash && verb == 'v' { - io.write_string(fi.writer, a.file_path) - io.write_byte(fi.writer, '(') - io.write_i64(fi.writer, i64(a.line), 10) - io.write_byte(fi.writer, ':') - io.write_i64(fi.writer, i64(a.column), 10) - io.write_byte(fi.writer, ')') + io.write_string(fi.writer, a.file_path, &fi.n) + io.write_byte(fi.writer, '(', &fi.n) + io.write_i64(fi.writer, i64(a.line), 10, &fi.n) + io.write_byte(fi.writer, ':', &fi.n) + io.write_i64(fi.writer, i64(a.column), 10, &fi.n) + io.write_byte(fi.writer, ')', &fi.n) return } } @@ -2121,7 +2114,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb) case cstring: fmt_cstring(fi, a, verb) - case typeid: reflect.write_typeid(fi.writer, a) + case typeid: reflect.write_typeid(fi.writer, a, &fi.n) case i16le: fmt_int(fi, u64(a), true, 16, verb) case u16le: fmt_int(fi, u64(a), false, 16, verb) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index a9a4a8d48..2e2149820 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -334,11 +334,11 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool { -write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) { - write_type(buf, type_info_of(id)) +write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) { + return write_type_writer(strings.to_writer(buf), type_info_of(id)) } -write_typeid_writer :: proc(writer: io.Writer, id: typeid) { - write_type(writer, type_info_of(id)) +write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) { + return write_type_writer(writer, type_info_of(id), n_written) } write_typeid :: proc{ From 6145185478fdea85e0a2b9867e1a65a23b38dd7c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 21 Feb 2022 16:04:56 +0000 Subject: [PATCH 04/16] Add missing return types --- vendor/darwin/Metal/MetalClasses.odin | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index 9713bba02..8e86c60e2 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -6589,8 +6589,8 @@ Device_newRasterizationRateMap :: #force_inline proc(self: ^Device, descriptor: return msgSend(^RasterizationRateMap, self, "newRasterizationRateMapWithDescriptor:", descriptor) } @(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptorWithCompletionHandler") -Device_newRenderPipelineStateWithDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, completionHandler: NewRenderPipelineStateCompletionHandler) { - msgSend(nil, self, "newRenderPipelineStateWithDescriptor:completionHandler:", descriptor, completionHandler) +Device_newRenderPipelineStateWithDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, completionHandler: NewRenderPipelineStateCompletionHandler) -> ^RenderPipelineState { + return msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithDescriptor:completionHandler:", descriptor, completionHandler) } @(objc_type=Device, objc_name="newRenderPipelineState") Device_newRenderPipelineState :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor) -> (pipeline: ^RenderPipelineState, error: ^NS.Error) { @@ -6598,8 +6598,8 @@ Device_newRenderPipelineState :: #force_inline proc(self: ^Device, descriptor: ^ return } @(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptorWithOptionsAndCompletionHandler") -Device_newRenderPipelineStateWithDescriptorWithOptionsAndCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) { - msgSend(nil, self, "newRenderPipelineStateWithDescriptor:options:completionHandler:", descriptor, options, completionHandler) +Device_newRenderPipelineStateWithDescriptorWithOptionsAndCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) -> ^RenderPipelineState { + return msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithDescriptor:options:completionHandler:", descriptor, options, completionHandler) } @(objc_type=Device, objc_name="newRenderPipelineStateWithDescriptorWithReflection") Device_newRenderPipelineStateWithDescriptorWithReflection :: #force_inline proc(self: ^Device, descriptor: ^RenderPipelineDescriptor, options: PipelineOption, reflection: ^AutoreleasedRenderPipelineReflection) -> (pipeline: ^RenderPipelineState, error: ^NS.Error) { @@ -6607,8 +6607,8 @@ Device_newRenderPipelineStateWithDescriptorWithReflection :: #force_inline proc( return } @(objc_type=Device, objc_name="newRenderPipelineStateWithTileDescriptorWithCompletionHandler") -Device_newRenderPipelineStateWithTileDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^TileRenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) { - msgSend(nil, self, "newRenderPipelineStateWithTileDescriptor:options:completionHandler:", descriptor, options, completionHandler) +Device_newRenderPipelineStateWithTileDescriptorWithCompletionHandler :: #force_inline proc(self: ^Device, descriptor: ^TileRenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) -> ^RenderPipelineState { + return msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithTileDescriptor:options:completionHandler:", descriptor, options, completionHandler) } @(objc_type=Device, objc_name="newRenderPipelineStateWithTileDescriptorWithReflection") Device_newRenderPipelineStateWithTileDescriptorWithReflection :: #force_inline proc(self: ^Device, descriptor: ^TileRenderPipelineDescriptor, options: PipelineOption, reflection: ^AutoreleasedRenderPipelineReflection) -> (pipeline: ^RenderPipelineState, error: ^NS.Error) { From e6a7b85da44e141f170a528321ce02452731a3d4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 21 Feb 2022 20:49:59 +0000 Subject: [PATCH 05/16] Correct types --- vendor/darwin/Metal/MetalClasses.odin | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index 8e86c60e2..56d40f5f2 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -5508,8 +5508,8 @@ Buffer_newRemoteBufferViewForDevice :: #force_inline proc(self: ^Buffer, device: return msgSend(^Buffer, self, "newRemoteBufferViewForDevice:", device) } @(objc_type=Buffer, objc_name="newTexture") -Buffer_newTexture :: #force_inline proc(self: ^Buffer, descriptor: ^TextureDescriptor, offset: NS.UInteger, bytesPerRow: NS.UInteger) -> ^Buffer { - return msgSend(^Buffer, self, "newTextureWithDescriptor:offset:bytesPerRow:", descriptor, offset, bytesPerRow) +Buffer_newTexture :: #force_inline proc(self: ^Buffer, descriptor: ^TextureDescriptor, offset: NS.UInteger, bytesPerRow: NS.UInteger) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:offset:bytesPerRow:", descriptor, offset, bytesPerRow) } @(objc_type=Buffer, objc_name="remoteStorageBuffer") Buffer_remoteStorageBuffer :: #force_inline proc(self: ^Buffer) -> ^Buffer { @@ -6636,8 +6636,8 @@ Device_newSharedTextureWithHandle :: #force_inline proc(self: ^Device, sharedHan return msgSend(^SharedEvent, self, "newSharedTextureWithHandle:", sharedHandle) } @(objc_type=Device, objc_name="newTexture") -Device_newTexture :: #force_inline proc(self: ^Device, desc: ^TextureDescriptor) -> ^SharedEvent { - return msgSend(^SharedEvent, self, "newTextureWithDescriptor:", desc) +Device_newTexture :: #force_inline proc(self: ^Device, desc: ^TextureDescriptor) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:", desc) } @(objc_type=Device, objc_name="newTextureWithIOSurface") Device_newTextureWithIOSurface :: #force_inline proc(self: ^Device, descriptor: ^TextureDescriptor, iosurface: IOSurfaceRef, plane: NS.UInteger) -> ^Texture { @@ -7101,24 +7101,24 @@ Heap_label :: #force_inline proc(self: ^Heap) -> ^NS.String { return msgSend(^NS.String, self, "label") } @(objc_type=Heap, objc_name="maxAvailableSizeWithAlignment") -Heap_maxAvailableSizeWithAlignment :: #force_inline proc(self: ^Heap, alignment: NS.UInteger) -> ^Heap { - return msgSend(^Heap, self, "maxAvailableSizeWithAlignment:", alignment) +Heap_maxAvailableSizeWithAlignment :: #force_inline proc(self: ^Heap, alignment: NS.UInteger) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxAvailableSizeWithAlignment:", alignment) } @(objc_type=Heap, objc_name="newBuffer") -Heap_newBuffer :: #force_inline proc(self: ^Heap, length: NS.UInteger, options: ResourceOptions) -> ^Heap { - return msgSend(^Heap, self, "newBufferWithLength:options:", length, options) +Heap_newBuffer :: #force_inline proc(self: ^Heap, length: NS.UInteger, options: ResourceOptions) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithLength:options:", length, options) } @(objc_type=Heap, objc_name="newBufferWithOptions") -Heap_newBufferWithOptions :: #force_inline proc(self: ^Heap, length: NS.UInteger, options: ResourceOptions, offset: NS.UInteger) -> ^Heap { - return msgSend(^Heap, self, "newBufferWithLength:options:offset:", length, options, offset) +Heap_newBufferWithOptions :: #force_inline proc(self: ^Heap, length: NS.UInteger, options: ResourceOptions, offset: NS.UInteger) -> ^Buffer { + return msgSend(^Buffer, self, "newBufferWithLength:options:offset:", length, options, offset) } @(objc_type=Heap, objc_name="newTexture") -Heap_newTexture :: #force_inline proc(self: ^Heap, desc: ^TextureDescriptor) -> ^Heap { - return msgSend(^Heap, self, "newTextureWithDescriptor:", desc) +Heap_newTexture :: #force_inline proc(self: ^Heap, desc: ^TextureDescriptor) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:", desc) } @(objc_type=Heap, objc_name="newTextureWithOffset") -Heap_newTextureWithOffset :: #force_inline proc(self: ^Heap, descriptor: ^TextureDescriptor, offset: NS.UInteger) -> ^Heap { - return msgSend(^Heap, self, "newTextureWithDescriptor:offset:", descriptor, offset) +Heap_newTextureWithOffset :: #force_inline proc(self: ^Heap, descriptor: ^TextureDescriptor, offset: NS.UInteger) -> ^Texture { + return msgSend(^Texture, self, "newTextureWithDescriptor:offset:", descriptor, offset) } @(objc_type=Heap, objc_name="resourceOptions") Heap_resourceOptions :: #force_inline proc(self: ^Heap) -> ResourceOptions { From 2b5bc1d5589c5b0acba56ab5081f19145287d775 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 22 Feb 2022 17:08:57 +0100 Subject: [PATCH 06/16] [report] Fix `odin report` crash if `/usr/lib/os-release` doesn't exist. --- src/bug_report.cpp | 4 ++-- src/gb/gb.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 9a1cb2254..4dd251f24 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -473,11 +473,11 @@ void print_bug_report_help() { #elif defined(GB_SYSTEM_LINUX) /* - Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` + Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` */ gbAllocator a = heap_allocator(); - gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release"); + gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release"); defer (gb_file_free_contents(&release)); b32 found = 0; diff --git a/src/gb/gb.h b/src/gb/gb.h index d9bf09436..a9d6378c9 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -6093,6 +6093,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con } void gb_file_free_contents(gbFileContents *fc) { + if (fc == NULL || fc->size == 0) return; GB_ASSERT_NOT_NULL(fc->data); gb_free(fc->allocator, fc->data); fc->data = NULL; From e7be9493ba3042569fa6c8958941b3a813bed5e3 Mon Sep 17 00:00:00 2001 From: zhibog Date: Tue, 22 Feb 2022 19:56:07 +0100 Subject: [PATCH 07/16] Added SipHash + tests and fixed remaining semicolons in vendor/botan --- core/crypto/siphash/siphash.odin | 316 ++++++++++++++++++++++ tests/core/crypto/test_core_crypto.odin | 43 +++ tests/vendor/botan/test_vendor_botan.odin | 43 +++ vendor/botan/blake2b/blake2b.odin | 2 +- vendor/botan/gost/gost.odin | 2 +- vendor/botan/keccak/keccak.odin | 2 +- vendor/botan/md4/md4.odin | 2 +- vendor/botan/md5/md5.odin | 2 +- vendor/botan/ripemd/ripemd.odin | 2 +- vendor/botan/sha1/sha1.odin | 2 +- vendor/botan/sha2/sha2.odin | 8 +- vendor/botan/sha3/sha3.odin | 8 +- vendor/botan/shake/shake.odin | 4 +- vendor/botan/siphash/siphash.odin | 253 +++++++++++++++++ vendor/botan/skein512/skein512.odin | 6 +- vendor/botan/sm3/sm3.odin | 2 +- vendor/botan/streebog/streebog.odin | 4 +- vendor/botan/tiger/tiger.odin | 6 +- vendor/botan/whirlpool/whirlpool.odin | 2 +- 19 files changed, 682 insertions(+), 27 deletions(-) create mode 100644 core/crypto/siphash/siphash.odin create mode 100644 vendor/botan/siphash/siphash.odin diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin new file mode 100644 index 000000000..14cd53a4c --- /dev/null +++ b/core/crypto/siphash/siphash.odin @@ -0,0 +1,316 @@ +package siphash + +/* + Copyright 2022 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial implementation. + + Implementation of the SipHash hashing algorithm, as defined at and +*/ + +import "core:crypto" +import "core:crypto/util" +import "core:mem" + +/* + High level API +*/ + +KEY_SIZE :: 16 +DIGEST_SIZE :: 8 + +// sum_string_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_1_3 :: proc(msg, key: string) -> u64 { + return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 { + ctx: Context + hash: u64 + init(&ctx, key, 1, 3) + update(&ctx, msg) + final(&ctx, &hash) + return hash +} + +// sum_string_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8") + hash := sum_bytes_1_3(msg, key) + _collect_output(dst[:], hash) +} + +sum_1_3 :: proc { + sum_string_1_3, + sum_bytes_1_3, + sum_string_to_buffer_1_3, + sum_bytes_to_buffer_1_3, +} + +// verify_u64_1_3 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_1_3(msg, key) == tag +} + +// verify_bytes will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_1_3(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_1_3 :: proc { + verify_bytes_1_3, + verify_u64_1_3, +} + +// sum_string_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_2_4 :: proc(msg, key: string) -> u64 { + return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 { + ctx: Context + hash: u64 + init(&ctx, key, 2, 4) + update(&ctx, msg) + final(&ctx, &hash) + return hash +} + +// sum_string_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8") + hash := sum_bytes_2_4(msg, key) + _collect_output(dst[:], hash) +} + +sum_2_4 :: proc { + sum_string_2_4, + sum_bytes_2_4, + sum_string_to_buffer_2_4, + sum_bytes_to_buffer_2_4, +} + +// verify_u64_2_4 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_2_4(msg, key) == tag +} + +// verify_bytes will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_2_4(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_2_4 :: proc { + verify_bytes_2_4, + verify_u64_2_4, +} + +// sum_string_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_4_8 :: proc(msg, key: string) -> u64 { + return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 { + ctx: Context + hash: u64 + init(&ctx, key, 4, 8) + update(&ctx, msg) + final(&ctx, &hash) + return hash +} + +// sum_string_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8") + hash := sum_bytes_4_8(msg, key) + _collect_output(dst[:], hash) +} + +sum_4_8 :: proc { + sum_string_4_8, + sum_bytes_4_8, + sum_string_to_buffer_4_8, + sum_bytes_to_buffer_4_8, +} + +// verify_u64_4_8 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_4_8(msg, key) == tag +} + +// verify_bytes will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_4_8(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_4_8 :: proc { + verify_bytes_4_8, + verify_u64_4_8, +} + +/* + Low level API +*/ + +init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) { + assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16") + ctx.c_rounds = c_rounds + ctx.d_rounds = d_rounds + is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) || + (ctx.c_rounds == 2 && ctx.d_rounds == 4) || + (ctx.c_rounds == 4 && ctx.d_rounds == 8) + assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)") + ctx.k0 = util.U64_LE(key[:8]) + ctx.k1 = util.U64_LE(key[8:]) + ctx.v0 = 0x736f6d6570736575 ~ ctx.k0 + ctx.v1 = 0x646f72616e646f6d ~ ctx.k1 + ctx.v2 = 0x6c7967656e657261 ~ ctx.k0 + ctx.v3 = 0x7465646279746573 ~ ctx.k1 + ctx.is_initialized = true +} + +update :: proc(ctx: ^Context, data: []byte) { + assert(ctx.is_initialized, "crypto/siphash: Context is not initalized") + ctx.last_block = len(data) / 8 * 8 + ctx.buf = data + i := 0 + m: u64 + for i < ctx.last_block { + m = u64(ctx.buf[i] & 0xff) + i += 1 + + for r in u64(1)..<8 { + m |= u64(ctx.buf[i] & 0xff) << (r * 8) + i += 1 + } + + ctx.v3 ~= m + for _ in 0..= ctx.last_block; i -= 1 { + m <<= 8 + m |= u64(ctx.buf[i] & 0xff) + } + m |= u64(len(ctx.buf) << 56) + + ctx.v3 ~= m + + for _ in 0.. byte { + return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3)) +} + +_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) { + dst[0] = _get_byte(7, hash) + dst[1] = _get_byte(6, hash) + dst[2] = _get_byte(5, hash) + dst[3] = _get_byte(4, hash) + dst[4] = _get_byte(3, hash) + dst[5] = _get_byte(2, hash) + dst[6] = _get_byte(1, hash) + dst[7] = _get_byte(0, hash) +} + +_compress :: #force_inline proc "contextless" (ctx: ^Context) { + ctx.v0 += ctx.v1 + ctx.v1 = util.ROTL64(ctx.v1, 13) + ctx.v1 ~= ctx.v0 + ctx.v0 = util.ROTL64(ctx.v0, 32) + ctx.v2 += ctx.v3 + ctx.v3 = util.ROTL64(ctx.v3, 16) + ctx.v3 ~= ctx.v2 + ctx.v0 += ctx.v3 + ctx.v3 = util.ROTL64(ctx.v3, 21) + ctx.v3 ~= ctx.v0 + ctx.v2 += ctx.v1 + ctx.v1 = util.ROTL64(ctx.v1, 17) + ctx.v1 ~= ctx.v2 + ctx.v2 = util.ROTL64(ctx.v2, 32) +} diff --git a/tests/core/crypto/test_core_crypto.odin b/tests/core/crypto/test_core_crypto.odin index 2ad00be66..5682a6167 100644 --- a/tests/core/crypto/test_core_crypto.odin +++ b/tests/core/crypto/test_core_crypto.odin @@ -36,6 +36,7 @@ import "core:crypto/sm3" import "core:crypto/jh" import "core:crypto/groestl" import "core:crypto/haval" +import "core:crypto/siphash" TEST_count := 0 TEST_fail := 0 @@ -114,6 +115,7 @@ main :: proc() { test_haval_192(&t) test_haval_224(&t) test_haval_256(&t) + test_siphash_2_4(&t) // "modern" crypto tests test_chacha20(&t) @@ -1103,3 +1105,44 @@ test_haval_256 :: proc(t: ^testing.T) { expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str)) } } + +@(test) +test_siphash_2_4 :: proc(t: ^testing.T) { + // Test vectors from + // https://github.com/veorq/SipHash/blob/master/vectors.h + test_vectors := [?]u64 { + 0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d, + 0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137, + 0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7, + 0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5, + 0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd, + 0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8, + 0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad, + 0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342, + 0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae, + 0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c, + 0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95, + 0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb, + 0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a, + 0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499, + 0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93, + 0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572, + } + + key: [16]byte + for i in 0..<16 { + key[i] = byte(i) + } + + for i in 0.. [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/gost/gost.odin b/vendor/botan/gost/gost.odin index 9f081f9cb..6bf1c5b97 100644 --- a/vendor/botan/gost/gost.odin +++ b/vendor/botan/gost/gost.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/keccak/keccak.odin b/vendor/botan/keccak/keccak.odin index 3316de017..28e7374ba 100644 --- a/vendor/botan/keccak/keccak.odin +++ b/vendor/botan/keccak/keccak.odin @@ -44,7 +44,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/md4/md4.odin b/vendor/botan/md4/md4.odin index c8a1ad903..174676a82 100644 --- a/vendor/botan/md4/md4.odin +++ b/vendor/botan/md4/md4.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/md5/md5.odin b/vendor/botan/md5/md5.odin index 203f2d092..01e099062 100644 --- a/vendor/botan/md5/md5.odin +++ b/vendor/botan/md5/md5.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/ripemd/ripemd.odin b/vendor/botan/ripemd/ripemd.odin index 0a8195a96..230e4c0d9 100644 --- a/vendor/botan/ripemd/ripemd.odin +++ b/vendor/botan/ripemd/ripemd.odin @@ -44,7 +44,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the diff --git a/vendor/botan/sha1/sha1.odin b/vendor/botan/sha1/sha1.odin index 005b01821..1f74c8fc2 100644 --- a/vendor/botan/sha1/sha1.odin +++ b/vendor/botan/sha1/sha1.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/sha2/sha2.odin b/vendor/botan/sha2/sha2.odin index f5d6921a8..4c201cc26 100644 --- a/vendor/botan/sha2/sha2.odin +++ b/vendor/botan/sha2/sha2.odin @@ -47,7 +47,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -284,7 +284,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/sha3/sha3.odin b/vendor/botan/sha3/sha3.odin index cf9fa5b2b..4c1b87dda 100644 --- a/vendor/botan/sha3/sha3.odin +++ b/vendor/botan/sha3/sha3.odin @@ -47,7 +47,7 @@ hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_224 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_224(transmute([]byte)(data), hash); + hash_bytes_to_buffer_224(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_224 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_384 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_384(transmute([]byte)(data), hash); + hash_bytes_to_buffer_384(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_384 will hash the given input and write the @@ -284,7 +284,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/shake/shake.odin b/vendor/botan/shake/shake.odin index ac8432f64..f1023b90e 100644 --- a/vendor/botan/shake/shake.odin +++ b/vendor/botan/shake/shake.odin @@ -45,7 +45,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -124,7 +124,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the diff --git a/vendor/botan/siphash/siphash.odin b/vendor/botan/siphash/siphash.odin new file mode 100644 index 000000000..ae8e19917 --- /dev/null +++ b/vendor/botan/siphash/siphash.odin @@ -0,0 +1,253 @@ +package siphash + +/* + Copyright 2022 zhibog + Made available under the BSD-3 license. + + List of contributors: + zhibog: Initial implementation. + + Interface for the SipHash hashing algorithm. + The hash will be computed via bindings to the Botan crypto library + + Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4 +*/ + +import "core:crypto" +import "core:crypto/util" + +import botan "../bindings" + +KEY_SIZE :: 16 +DIGEST_SIZE :: 8 + +// sum_string_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_1_3 :: proc(msg, key: string) -> u64 { + return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_1_3 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 { + dst: [8]byte + ctx: botan.mac_t + init(&ctx, key[:], 1, 3) + update(&ctx, msg[:]) + final(&ctx, dst[:]) + return util.U64_LE(dst[:]) +} + +// sum_string_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_1_3 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "vendor/botan: Destination buffer needs to be at least of size 8") + ctx: botan.mac_t + init(&ctx, key[:], 1, 3) + update(&ctx, msg[:]) + final(&ctx, dst[:]) +} + +sum_1_3 :: proc { + sum_string_1_3, + sum_bytes_1_3, + sum_string_to_buffer_1_3, + sum_bytes_to_buffer_1_3, +} + +// verify_u64_1_3 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_1_3(msg, key) == tag +} + +// verify_bytes_1_3 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_1_3(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_1_3 :: proc { + verify_bytes_1_3, + verify_u64_1_3, +} + +// sum_string_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_2_4 :: proc(msg, key: string) -> u64 { + return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_2_4 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 { + dst: [8]byte + ctx: botan.mac_t + init(&ctx, key[:]) + update(&ctx, msg[:]) + final(&ctx, dst[:]) + return util.U64_LE(dst[:]) +} + +// sum_string_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_2_4 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "vendor/botan: Destination buffer needs to be at least of size 8") + ctx: botan.mac_t + init(&ctx, key[:]) + update(&ctx, msg[:]) + final(&ctx, dst[:]) +} + +sum_2_4 :: proc { + sum_string_2_4, + sum_bytes_2_4, + sum_string_to_buffer_2_4, + sum_bytes_to_buffer_2_4, +} + +sum_string :: sum_string_2_4 +sum_bytes :: sum_bytes_2_4 +sum_string_to_buffer :: sum_string_to_buffer_2_4 +sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4 +sum :: proc { + sum_string, + sum_bytes, + sum_string_to_buffer, + sum_bytes_to_buffer, +} + + +// verify_u64_2_4 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_2_4(msg, key) == tag +} + +// verify_bytes_2_4 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_2_4(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_2_4 :: proc { + verify_bytes_2_4, + verify_u64_2_4, +} + +verify_bytes :: verify_bytes_2_4 +verify_u64 :: verify_u64_2_4 +verify :: proc { + verify_bytes, + verify_u64, +} + +// sum_string_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_string_4_8 :: proc(msg, key: string) -> u64 { + return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key)) +} + +// sum_bytes_4_8 will hash the given message with the key and return +// the computed hash as a u64 +sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 { + dst: [8]byte + ctx: botan.mac_t + init(&ctx, key[:], 4, 8) + update(&ctx, msg[:]) + final(&ctx, dst[:]) + return util.U64_LE(dst[:]) +} + +// sum_string_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) { + sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst) +} + +// sum_bytes_to_buffer_4_8 will hash the given message with the key and write +// the computed hash into the provided destination buffer +sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) { + assert(len(dst) >= DIGEST_SIZE, "vendor/botan: Destination buffer needs to be at least of size 8") + ctx: botan.mac_t + init(&ctx, key[:], 4, 8) + update(&ctx, msg[:]) + final(&ctx, dst[:]) +} + +sum_4_8 :: proc { + sum_string_4_8, + sum_bytes_4_8, + sum_string_to_buffer_4_8, + sum_bytes_to_buffer_4_8, +} + +// verify_u64_4_8 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool { + return sum_bytes_4_8(msg, key) == tag +} + +// verify_bytes_4_8 will check if the supplied tag matches with the output you +// will get from the provided message and key +verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool { + derived_tag: [8]byte + sum_bytes_to_buffer_4_8(msg, key, derived_tag[:]) + return crypto.compare_constant_time(derived_tag[:], tag) == 1 +} + +verify_4_8 :: proc { + verify_bytes_4_8, + verify_u64_4_8, +} + +/* + Low level API +*/ + +Context :: botan.mac_t + +init :: proc(ctx: ^botan.mac_t, key: []byte, c_rounds := 2, d_rounds := 4) { + assert(len(key) == KEY_SIZE, "vendor/botan: Invalid key size, want 16") + is_valid_setting := (c_rounds == 1 && d_rounds == 3) || + (c_rounds == 2 && d_rounds == 4) || + (c_rounds == 4 && d_rounds == 8) + assert(is_valid_setting, "vendor/botan: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)") + if c_rounds == 1 && d_rounds == 3 { + botan.mac_init(ctx, botan.MAC_SIPHASH_1_3, 0) + } else if c_rounds == 2 && d_rounds == 4 { + botan.mac_init(ctx, botan.MAC_SIPHASH_2_4, 0) + } else if c_rounds == 4 && d_rounds == 8 { + botan.mac_init(ctx, botan.MAC_SIPHASH_4_8, 0) + } + botan.mac_set_key(ctx^, len(key) == 0 ? nil : &key[0], uint(len(key))) +} + +update :: proc "contextless" (ctx: ^botan.mac_t, data: []byte) { + botan.mac_update(ctx^, len(data) == 0 ? nil : &data[0], uint(len(data))) +} + +final :: proc "contextless" (ctx: ^botan.mac_t, dst: []byte) { + botan.mac_final(ctx^, &dst[0]) + reset(ctx) +} + +reset :: proc(ctx: ^botan.mac_t) { + botan.mac_destroy(ctx^) +} \ No newline at end of file diff --git a/vendor/botan/skein512/skein512.odin b/vendor/botan/skein512/skein512.odin index 490eeba03..4fed07853 100644 --- a/vendor/botan/skein512/skein512.odin +++ b/vendor/botan/skein512/skein512.odin @@ -47,7 +47,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -126,7 +126,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the @@ -205,7 +205,7 @@ hash_bytes_slice :: proc(data: []byte, bit_size: int, allocator := context.alloc // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_slice :: proc(data: string, hash: []byte, bit_size: int, allocator := context.allocator) { - hash_bytes_to_buffer_slice(transmute([]byte)(data), hash, bit_size, allocator); + hash_bytes_to_buffer_slice(transmute([]byte)(data), hash, bit_size, allocator) } // hash_bytes_to_buffer_slice will hash the given input and write the diff --git a/vendor/botan/sm3/sm3.odin b/vendor/botan/sm3/sm3.odin index 7eb3f1f8d..75cf40679 100644 --- a/vendor/botan/sm3/sm3.odin +++ b/vendor/botan/sm3/sm3.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the diff --git a/vendor/botan/streebog/streebog.odin b/vendor/botan/streebog/streebog.odin index cbf2047ed..20b4e6adb 100644 --- a/vendor/botan/streebog/streebog.odin +++ b/vendor/botan/streebog/streebog.odin @@ -45,7 +45,7 @@ hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_256 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_256(transmute([]byte)(data), hash); + hash_bytes_to_buffer_256(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_256 will hash the given input and write the @@ -124,7 +124,7 @@ hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_512 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_512(transmute([]byte)(data), hash); + hash_bytes_to_buffer_512(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_512 will hash the given input and write the diff --git a/vendor/botan/tiger/tiger.odin b/vendor/botan/tiger/tiger.odin index b29602b26..f24dc7019 100644 --- a/vendor/botan/tiger/tiger.odin +++ b/vendor/botan/tiger/tiger.odin @@ -46,7 +46,7 @@ hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_128 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_128(transmute([]byte)(data), hash); + hash_bytes_to_buffer_128(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_128 will hash the given input and write the @@ -125,7 +125,7 @@ hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_160 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_160(transmute([]byte)(data), hash); + hash_bytes_to_buffer_160(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_160 will hash the given input and write the @@ -204,7 +204,7 @@ hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer_192 :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer_192(transmute([]byte)(data), hash); + hash_bytes_to_buffer_192(transmute([]byte)(data), hash) } // hash_bytes_to_buffer_192 will hash the given input and write the diff --git a/vendor/botan/whirlpool/whirlpool.odin b/vendor/botan/whirlpool/whirlpool.odin index 2aff3c8ed..a7c1abbe8 100644 --- a/vendor/botan/whirlpool/whirlpool.odin +++ b/vendor/botan/whirlpool/whirlpool.odin @@ -44,7 +44,7 @@ hash_bytes :: proc "contextless" (data: []byte) -> [DIGEST_SIZE]byte { // computed hash to the second parameter. // It requires that the destination buffer is at least as big as the digest size hash_string_to_buffer :: proc(data: string, hash: []byte) { - hash_bytes_to_buffer(transmute([]byte)(data), hash); + hash_bytes_to_buffer(transmute([]byte)(data), hash) } // hash_bytes_to_buffer will hash the given input and write the From b6dc253d8b1252d8d6554a8f5fb6804170123a57 Mon Sep 17 00:00:00 2001 From: zhibog Date: Tue, 22 Feb 2022 20:02:34 +0100 Subject: [PATCH 08/16] Add generic procedure for default SipHash 2-4 --- core/crypto/siphash/siphash.odin | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin index 14cd53a4c..63576551c 100644 --- a/core/crypto/siphash/siphash.odin +++ b/core/crypto/siphash/siphash.odin @@ -8,6 +8,8 @@ package siphash zhibog: Initial implementation. Implementation of the SipHash hashing algorithm, as defined at and + + Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4 */ import "core:crypto" @@ -116,6 +118,17 @@ sum_2_4 :: proc { sum_bytes_to_buffer_2_4, } +sum_string :: sum_string_2_4 +sum_bytes :: sum_bytes_2_4 +sum_string_to_buffer :: sum_string_to_buffer_2_4 +sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4 +sum :: proc { + sum_string, + sum_bytes, + sum_string_to_buffer, + sum_bytes_to_buffer, +} + // verify_u64_2_4 will check if the supplied tag matches with the output you // will get from the provided message and key verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool { @@ -135,6 +148,13 @@ verify_2_4 :: proc { verify_u64_2_4, } +verify_bytes :: verify_bytes_2_4 +verify_u64 :: verify_u64_2_4 +verify :: proc { + verify_bytes, + verify_u64, +} + // sum_string_4_8 will hash the given message with the key and return // the computed hash as a u64 sum_string_4_8 :: proc(msg, key: string) -> u64 { From 460b5149af14bd700b188d3b398786b3081f2159 Mon Sep 17 00:00:00 2001 From: zhibog Date: Tue, 22 Feb 2022 20:06:52 +0100 Subject: [PATCH 09/16] Added missing constants --- vendor/botan/bindings/botan.odin | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/vendor/botan/bindings/botan.odin b/vendor/botan/bindings/botan.odin index fdddc99a9..74461871e 100644 --- a/vendor/botan/bindings/botan.odin +++ b/vendor/botan/bindings/botan.odin @@ -99,6 +99,10 @@ MAC_HMAC_SHA_384 :: "HMAC(SHA-384)" MAC_HMAC_SHA_512 :: "HMAC(SHA-512)" MAC_HMAC_MD5 :: "HMAC(MD5)" +MAC_SIPHASH_1_3 :: "SipHash(1,3)" +MAC_SIPHASH_2_4 :: "SipHash(2,4)" +MAC_SIPHASH_4_8 :: "SipHash(4,8)" + hash_struct :: struct{} hash_t :: ^hash_struct rng_struct :: struct{} @@ -136,11 +140,11 @@ totp_t :: ^totp_struct fpe_struct :: struct{} fpe_t :: ^fpe_struct -when ODIN_OS == .Windows { +when ODIN_OS == "windows" { foreign import botan_lib "botan.lib" -} else when ODIN_OS == .Linux { +} else when ODIN_OS == "linux" { foreign import botan_lib "system:botan-2" -} else when ODIN_OS == .Darwin { +} else when ODIN_OS == "darwin" { foreign import botan_lib "system:botan-2" } @@ -467,4 +471,4 @@ foreign botan_lib { fpe_destroy :: proc(fpe: fpe_t) -> c.int --- fpe_encrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- fpe_decrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- -} \ No newline at end of file +} From cdecb0ccc3bfc260bd34dce5f76c6238decaa0ef Mon Sep 17 00:00:00 2001 From: zhibog Date: Tue, 22 Feb 2022 20:10:35 +0100 Subject: [PATCH 10/16] Fix Odin_OS_Type --- vendor/botan/bindings/botan.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/botan/bindings/botan.odin b/vendor/botan/bindings/botan.odin index 74461871e..70311523d 100644 --- a/vendor/botan/bindings/botan.odin +++ b/vendor/botan/bindings/botan.odin @@ -140,11 +140,11 @@ totp_t :: ^totp_struct fpe_struct :: struct{} fpe_t :: ^fpe_struct -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import botan_lib "botan.lib" -} else when ODIN_OS == "linux" { +} else when ODIN_OS == .Linux { foreign import botan_lib "system:botan-2" -} else when ODIN_OS == "darwin" { +} else when ODIN_OS == .Darwin { foreign import botan_lib "system:botan-2" } @@ -471,4 +471,4 @@ foreign botan_lib { fpe_destroy :: proc(fpe: fpe_t) -> c.int --- fpe_encrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- fpe_decrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int --- -} +} \ No newline at end of file From 70e8d97ee1028aa491286195dfa75f7fc49d4133 Mon Sep 17 00:00:00 2001 From: zhibog Date: Tue, 22 Feb 2022 20:13:49 +0100 Subject: [PATCH 11/16] Fix procedure --- vendor/botan/siphash/siphash.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/botan/siphash/siphash.odin b/vendor/botan/siphash/siphash.odin index ae8e19917..c2b7b64f4 100644 --- a/vendor/botan/siphash/siphash.odin +++ b/vendor/botan/siphash/siphash.odin @@ -243,7 +243,7 @@ update :: proc "contextless" (ctx: ^botan.mac_t, data: []byte) { botan.mac_update(ctx^, len(data) == 0 ? nil : &data[0], uint(len(data))) } -final :: proc "contextless" (ctx: ^botan.mac_t, dst: []byte) { +final :: proc(ctx: ^botan.mac_t, dst: []byte) { botan.mac_final(ctx^, &dst[0]) reset(ctx) } From cad753e3986183fa180abae480a85432b4b36af1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 22:53:13 +0000 Subject: [PATCH 12/16] Simplify `parse_binary_expr` --- src/parser.cpp | 159 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 49 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 8a7ab2d20..c214df782 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3013,66 +3013,127 @@ i32 token_precedence(AstFile *f, TokenKind t) { return 0; } +// Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { +// Ast *expr = parse_unary_expr(f, lhs); +// for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { +// for (;;) { +// Token op = f->curr_token; +// i32 op_prec = token_precedence(f, op.kind); +// if (op_prec != prec) { +// // NOTE(bill): This will also catch operators that are not valid "binary" operators +// break; +// } +// Token prev = f->prev_token; +// switch (op.kind) { +// case Token_if: +// case Token_when: +// if (prev.pos.line < op.pos.line) { +// // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition +// goto loop_end; +// } +// break; +// } +// expect_operator(f); // NOTE(bill): error checks too + +// if (op.kind == Token_Question) { +// Ast *cond = expr; +// // Token_Question +// Ast *x = parse_expr(f, lhs); +// Token token_c = expect_token(f, Token_Colon); +// Ast *y = parse_expr(f, lhs); +// expr = ast_ternary_if_expr(f, x, cond, y); +// } else if (op.kind == Token_if || op.kind == Token_when) { +// Ast *x = expr; +// Ast *cond = parse_expr(f, lhs); +// Token tok_else = expect_token(f, Token_else); +// Ast *y = parse_expr(f, lhs); + +// switch (op.kind) { +// case Token_if: +// expr = ast_ternary_if_expr(f, x, cond, y); +// break; +// case Token_when: +// expr = ast_ternary_when_expr(f, x, cond, y); +// break; +// } +// } else { +// Ast *right = parse_binary_expr(f, false, prec+1); +// if (right == nullptr) { +// syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); +// } +// if (op.kind == Token_or_else) { +// // NOTE(bill): easier to handle its logic different with its own AST kind +// expr = ast_or_else_expr(f, expr, op, right); +// } else { +// expr = ast_binary_expr(f, op, expr, right); +// } +// } + +// lhs = false; +// } +// loop_end:; +// } +// return expr; +// } + Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Ast *expr = parse_unary_expr(f, lhs); - for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { - for (;;) { - Token op = f->curr_token; - i32 op_prec = token_precedence(f, op.kind); - if (op_prec != prec) { - // NOTE(bill): This will also catch operators that are not valid "binary" operators - break; + for (;;) { + Token op = f->curr_token; + i32 op_prec = token_precedence(f, op.kind); + if (op_prec < prec_in) { + // NOTE(bill): This will also catch operators that are not valid "binary" operators + break; + } + Token prev = f->prev_token; + switch (op.kind) { + case Token_if: + case Token_when: + if (prev.pos.line < op.pos.line) { + // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition + goto loop_end; } - Token prev = f->prev_token; + break; + } + expect_operator(f); // NOTE(bill): error checks too + + if (op.kind == Token_Question) { + Ast *cond = expr; + // Token_Question + Ast *x = parse_expr(f, lhs); + Token token_c = expect_token(f, Token_Colon); + Ast *y = parse_expr(f, lhs); + expr = ast_ternary_if_expr(f, x, cond, y); + } else if (op.kind == Token_if || op.kind == Token_when) { + Ast *x = expr; + Ast *cond = parse_expr(f, lhs); + Token tok_else = expect_token(f, Token_else); + Ast *y = parse_expr(f, lhs); + switch (op.kind) { case Token_if: + expr = ast_ternary_if_expr(f, x, cond, y); + break; case Token_when: - if (prev.pos.line < op.pos.line) { - // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition - goto loop_end; - } + expr = ast_ternary_when_expr(f, x, cond, y); break; } - expect_operator(f); // NOTE(bill): error checks too - - if (op.kind == Token_Question) { - Ast *cond = expr; - // Token_Question - Ast *x = parse_expr(f, lhs); - Token token_c = expect_token(f, Token_Colon); - Ast *y = parse_expr(f, lhs); - expr = ast_ternary_if_expr(f, x, cond, y); - } else if (op.kind == Token_if || op.kind == Token_when) { - Ast *x = expr; - Ast *cond = parse_expr(f, lhs); - Token tok_else = expect_token(f, Token_else); - Ast *y = parse_expr(f, lhs); - - switch (op.kind) { - case Token_if: - expr = ast_ternary_if_expr(f, x, cond, y); - break; - case Token_when: - expr = ast_ternary_when_expr(f, x, cond, y); - break; - } - } else { - Ast *right = parse_binary_expr(f, false, prec+1); - if (right == nullptr) { - syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); - } - if (op.kind == Token_or_else) { - // NOTE(bill): easier to handle its logic different with its own AST kind - expr = ast_or_else_expr(f, expr, op, right); - } else { - expr = ast_binary_expr(f, op, expr, right); - } + } else { + Ast *right = parse_binary_expr(f, false, op_prec+1); + if (right == nullptr) { + syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); + } + if (op.kind == Token_or_else) { + // NOTE(bill): easier to handle its logic different with its own AST kind + expr = ast_or_else_expr(f, expr, op, right); + } else { + expr = ast_binary_expr(f, op, expr, right); } - - lhs = false; } - loop_end:; + + lhs = false; } + loop_end:; return expr; } From 62d232d798fff25597b6e3591c6a178c32fa440e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 22:59:00 +0000 Subject: [PATCH 13/16] Correct `ExactValue_Pointer` --- src/exact_value.cpp | 17 +++++++++--- src/parser.cpp | 63 --------------------------------------------- 2 files changed, 14 insertions(+), 66 deletions(-) diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fd90278e5..3dae96853 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -630,6 +630,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: case ExactValue_Quaternion: + case ExactValue_Pointer: + case ExactValue_Procedure: + case ExactValue_Typeid: return; case ExactValue_Integer: @@ -671,9 +674,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) { return; } break; - - case ExactValue_Procedure: - return; } compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind); @@ -932,6 +932,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { break; } + case ExactValue_Pointer: { + switch (op) { + case Token_CmpEq: return x.value_pointer == y.value_pointer; + case Token_NotEq: return x.value_pointer != y.value_pointer; + case Token_Lt: return x.value_pointer < y.value_pointer; + case Token_LtEq: return x.value_pointer <= y.value_pointer; + case Token_Gt: return x.value_pointer > y.value_pointer; + case Token_GtEq: return x.value_pointer >= y.value_pointer; + } + } + case ExactValue_Typeid: switch (op) { case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); diff --git a/src/parser.cpp b/src/parser.cpp index c214df782..9659e8c18 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3013,69 +3013,6 @@ i32 token_precedence(AstFile *f, TokenKind t) { return 0; } -// Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { -// Ast *expr = parse_unary_expr(f, lhs); -// for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { -// for (;;) { -// Token op = f->curr_token; -// i32 op_prec = token_precedence(f, op.kind); -// if (op_prec != prec) { -// // NOTE(bill): This will also catch operators that are not valid "binary" operators -// break; -// } -// Token prev = f->prev_token; -// switch (op.kind) { -// case Token_if: -// case Token_when: -// if (prev.pos.line < op.pos.line) { -// // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition -// goto loop_end; -// } -// break; -// } -// expect_operator(f); // NOTE(bill): error checks too - -// if (op.kind == Token_Question) { -// Ast *cond = expr; -// // Token_Question -// Ast *x = parse_expr(f, lhs); -// Token token_c = expect_token(f, Token_Colon); -// Ast *y = parse_expr(f, lhs); -// expr = ast_ternary_if_expr(f, x, cond, y); -// } else if (op.kind == Token_if || op.kind == Token_when) { -// Ast *x = expr; -// Ast *cond = parse_expr(f, lhs); -// Token tok_else = expect_token(f, Token_else); -// Ast *y = parse_expr(f, lhs); - -// switch (op.kind) { -// case Token_if: -// expr = ast_ternary_if_expr(f, x, cond, y); -// break; -// case Token_when: -// expr = ast_ternary_when_expr(f, x, cond, y); -// break; -// } -// } else { -// Ast *right = parse_binary_expr(f, false, prec+1); -// if (right == nullptr) { -// syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); -// } -// if (op.kind == Token_or_else) { -// // NOTE(bill): easier to handle its logic different with its own AST kind -// expr = ast_or_else_expr(f, expr, op, right); -// } else { -// expr = ast_binary_expr(f, op, expr, right); -// } -// } - -// lhs = false; -// } -// loop_end:; -// } -// return expr; -// } - Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Ast *expr = parse_unary_expr(f, lhs); for (;;) { From ad2f1ac24ebdb4937c5a469c3d3f48b8f53f6218 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 23:01:28 +0000 Subject: [PATCH 14/16] Improve `union_tag_size` --- src/types.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/types.cpp b/src/types.cpp index 2c1e6162f..74080334a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2623,6 +2623,17 @@ i64 union_tag_size(Type *u) { // TODO(bill): Is this an okay approach? i64 max_align = 1; + + if (u->Union.variants.count < 1ull<<8) { + max_align = 1; + } else if (u->Union.variants.count < 1ull<<16) { + max_align = 2; + } else if (u->Union.variants.count < 1ull<<32) { + max_align = 4; + } else { + GB_PANIC("how many variants do you have?!"); + } + for_array(i, u->Union.variants) { Type *variant_type = u->Union.variants[i]; i64 align = type_align_of(variant_type); From 83f7a887b7ab9313e520211521297a9352c408b9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 23:03:04 +0000 Subject: [PATCH 15/16] Move comment --- src/exact_value.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 3dae96853..f6df48951 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -50,9 +50,9 @@ struct ExactValue { union { bool value_bool; String value_string; - BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer + BigInt value_integer; f64 value_float; - i64 value_pointer; + i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer Complex128 *value_complex; Quaternion256 *value_quaternion; Ast * value_compound; From e81ed9a9601f6a7761dec5b0f96cc8680a16f166 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 23:19:49 +0000 Subject: [PATCH 16/16] Add "Did you mean" to Objective-C fields --- src/check_expr.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 884f1bb9f..e07dc3d60 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") { } } +void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) { + if (e->kind != Entity_TypeName) { + return; + } + if (e->TypeName.objc_metadata == nullptr) { + return; + } + TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata; + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Struct); + + if (is_type) { + for_array(i, objc_metadata->type_entries) { + String name = objc_metadata->type_entries[i].name; + string_set_add(set, name); + } + } else { + for_array(i, objc_metadata->value_entries) { + String name = objc_metadata->value_entries[i].name; + string_set_add(set, name); + } + } + + for_array(i, t->Struct.fields) { + Entity *f = t->Struct.fields[i]; + if (f->flags & EntityFlag_Using && f->type != nullptr) { + if (f->type->kind == Type_Named && f->type->Named.type_name) { + populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type); + } + } + } +} + + +void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") { + ERROR_BLOCK(); + GB_ASSERT(e->kind == Entity_TypeName); + GB_ASSERT(e->TypeName.objc_metadata != nullptr); + auto *objc_metadata = e->TypeName.objc_metadata; + mutex_lock(objc_metadata->mutex); + defer (mutex_unlock(objc_metadata->mutex)); + + StringSet set = {}; + string_set_init(&set, heap_allocator()); + defer (string_set_destroy(&set)); + populate_check_did_you_mean_objc_entity(&set, e, is_type); + + + DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name); + defer (did_you_mean_destroy(&d)); + for_array(i, set.entries) { + did_you_mean_append(&d, set.entries[i].value); + } + check_did_you_mean_print(&d, prefix); +} + void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") { ERROR_BLOCK(); @@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array const &fields, check_did_you_mean_print(&d, prefix); } + void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") { ERROR_BLOCK(); @@ -4420,7 +4477,12 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (operand->type != nullptr && selector->kind == Ast_Ident) { String const &name = selector->Ident.token.string; Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { + if (operand->type->kind == Type_Named && + operand->type->Named.type_name && + operand->type->Named.type_name->kind == Entity_TypeName && + operand->type->Named.type_name->TypeName.objc_metadata) { + check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type); + } else if (bt->kind == Type_Struct) { check_did_you_mean_type(name, bt->Struct.fields); } else if (bt->kind == Type_Enum) { check_did_you_mean_type(name, bt->Enum.fields);