diff options
| author | Benoit Jacquier <benoit.jacquier@gmail.com> | 2022-08-27 16:22:37 +0200 |
|---|---|---|
| committer | Benoit Jacquier <benoit.jacquier@gmail.com> | 2022-08-27 16:22:37 +0200 |
| commit | 4e5337412a4e46fb26250f8adf1d019ddd8366c7 (patch) | |
| tree | a49c22bd4c894a26ddf8da92c10894fb8e03383f /core | |
| parent | 00f2e911a73e99b1283306272ff433984d90486c (diff) | |
| parent | c82d7d3d87c2dc77ce942b1cc450734baca3da14 (diff) | |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'core')
61 files changed, 1835 insertions, 753 deletions
diff --git a/core/bufio/scanner.odin b/core/bufio/scanner.odin index 86e6d8eb0..b9e620250 100644 --- a/core/bufio/scanner.odin +++ b/core/bufio/scanner.odin @@ -66,7 +66,7 @@ scanner_destroy :: proc(s: ^Scanner) { } -// Returns the first non-EOF error that was encounted by the scanner +// Returns the first non-EOF error that was encountered by the scanner scanner_error :: proc(s: ^Scanner) -> Scanner_Error { switch s._err { case .EOF, nil: diff --git a/core/c/libc/math.odin b/core/c/libc/math.odin index 6a7b81850..0a6ecc0c3 100644 --- a/core/c/libc/math.odin +++ b/core/c/libc/math.odin @@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf} fma :: proc{libc_fma, libc_fmaf} // But retain the 'f' suffix-variant functions as well so they can be used, -// a trick is used here where we use explicit procedrual overloading of one +// a trick is used here where we use explicit procedural overloading of one // procedure. This is done because the foreign block is marked @(private) and // aliasing functions does not remove privateness from the entity. acosf :: proc{libc_acosf} diff --git a/core/container/priority_queue/priority_queue.odin b/core/container/priority_queue/priority_queue.odin index e324287f3..0c5c4931d 100644 --- a/core/container/priority_queue/priority_queue.odin +++ b/core/container/priority_queue/priority_queue.odin @@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool { _shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) { j := j queue := pq.queue[:] - n := builtin.len(queue) for 0 <= j { i := (j-1)/2 if i == j || !pq.less(queue[j], queue[i]) { diff --git a/core/crypto/README.md b/core/crypto/README.md index ddcb12d81..dd594d5c2 100644 --- a/core/crypto/README.md +++ b/core/crypto/README.md @@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu ### Disclaimer The algorithms were ported out of curiosity and due to interest in the field. We have not had any of the code verified by a third party or tested/fuzzed by any automatic means. -Whereever we were able to find official test vectors, those were used to verify the implementation. +Wherever we were able to find official test vectors, those were used to verify the implementation. We do not recommend using them in a production environment, without any additional testing and/or verification. ### ToDo diff --git a/core/crypto/_fiat/README.md b/core/crypto/_fiat/README.md index cd510d442..4011a06c0 100644 --- a/core/crypto/_fiat/README.md +++ b/core/crypto/_fiat/README.md @@ -30,6 +30,6 @@ equivalence. For the most part, alterations to the base fiat-crypto generated code was kept to a minimum, to aid auditability. This results in a somewhat -ideosyncratic style, and in some cases minor performance penalties. +idiosyncratic style, and in some cases minor performance penalties. [1]: https://github.com/mit-plv/fiat-crypto diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin index a6c75f315..2d03829c2 100644 --- a/core/crypto/siphash/siphash.odin +++ b/core/crypto/siphash/siphash.odin @@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) { } update :: proc(ctx: ^Context, data: []byte) { - assert(ctx.is_initialized, "crypto/siphash: Context is not initalized") + assert(ctx.is_initialized, "crypto/siphash: Context is not initialized") ctx.last_block = len(data) / 8 * 8 ctx.buf = data i := 0 diff --git a/core/encoding/hxa/hxa.odin b/core/encoding/hxa/hxa.odin index f47661bad..9b24ede9c 100644 --- a/core/encoding/hxa/hxa.odin +++ b/core/encoding/hxa/hxa.odin @@ -107,7 +107,7 @@ Node :: struct { /* Conventions */ /* ------------ Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood. -A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data. +A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data. If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it! */ diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 54fab44c6..8f7749aba 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -17,25 +17,54 @@ Marshal_Error :: union #shared_nil { io.Error, } -marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) { +// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results +Marshal_Options :: struct { + // output based on spec + spec: Specification, + + // use line breaks & tab|spaces + pretty: bool, + + // spacing + use_spaces: bool, + spaces: int, + + // state + indentation: int, + + // option to output uint in JSON5 & MJSON + write_uint_as_hex: bool, + + // mjson output options + mjson_keys_use_quotes: bool, + mjson_keys_use_equal_sign: bool, + + // mjson state + mjson_skipped_first_braces_start: bool, + mjson_skipped_first_braces_end: bool, +} + +marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) { b := strings.builder_make(allocator) defer if err != nil { strings.builder_destroy(&b) } - marshal_to_builder(&b, v) or_return + opt := opt + marshal_to_builder(&b, v, &opt) or_return if len(b.buf) != 0 { data = b.buf[:] } + return data, nil } -marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { - return marshal_to_writer(strings.to_writer(b), v) +marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error { + return marshal_to_writer(strings.to_writer(b), v, opt) } -marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { +marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) { if v == nil { io.write_string(w, "null") or_return return @@ -56,6 +85,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { case i16: u = u128(i) case i32: u = u128(i) case i64: u = u128(i) + case i128: u = u128(i) case int: u = u128(i) case u8: u = u128(i) case u16: u = u128(i) @@ -82,7 +112,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { case u128be: u = u128(i) } - s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + s: string + + // allow uints to be printed as hex + if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) { + switch i in a { + case u8, u16, u32, u64, u128: + s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix }) + + case: + s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + } + } else { + s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + } + io.write_string(w, s) or_return @@ -147,6 +191,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { case runtime.Type_Info_Multi_Pointer: return .Unsupported_Type + case runtime.Type_Info_Soa_Pointer: + return .Unsupported_Type + case runtime.Type_Info_Procedure: return .Unsupported_Type @@ -166,52 +213,48 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { return .Unsupported_Type case runtime.Type_Info_Array: - io.write_byte(w, '[') or_return + opt_write_start(w, opt, '[') or_return for i in 0..<info.count { - if i > 0 { io.write_string(w, ", ") or_return } - + opt_write_iteration(w, opt, i) or_return data := uintptr(v.data) + uintptr(i*info.elem_size) - marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return + marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return } - io.write_byte(w, ']') or_return + opt_write_end(w, opt, ']') or_return case runtime.Type_Info_Enumerated_Array: index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum) - io.write_byte(w, '[') or_return + opt_write_start(w, opt, '[') or_return for i in 0..<info.count { - if i > 0 { io.write_string(w, ", ") or_return } - + opt_write_iteration(w, opt, i) or_return data := uintptr(v.data) + uintptr(i*info.elem_size) - marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return + marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return } - io.write_byte(w, ']') or_return + opt_write_end(w, opt, ']') or_return case runtime.Type_Info_Dynamic_Array: - io.write_byte(w, '[') or_return + opt_write_start(w, opt, '[') or_return array := cast(^mem.Raw_Dynamic_Array)v.data for i in 0..<array.len { - if i > 0 { io.write_string(w, ", ") or_return } - + opt_write_iteration(w, opt, i) or_return data := uintptr(array.data) + uintptr(i*info.elem_size) - marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return + marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return } - io.write_byte(w, ']') or_return + opt_write_end(w, opt, ']') or_return case runtime.Type_Info_Slice: - io.write_byte(w, '[') or_return + opt_write_start(w, opt, '[') or_return slice := cast(^mem.Raw_Slice)v.data for i in 0..<slice.len { - if i > 0 { io.write_string(w, ", ") or_return } - + opt_write_iteration(w, opt, i) or_return data := uintptr(slice.data) + uintptr(i*info.elem_size) - marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return + marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return } - io.write_byte(w, ']') or_return + opt_write_end(w, opt, ']') or_return case runtime.Type_Info_Map: m := (^mem.Raw_Map)(v.data) + opt_write_start(w, opt, '{') or_return - io.write_byte(w, '{') or_return if m != nil { if info.generated_struct == nil { return .Unsupported_Type @@ -223,31 +266,50 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { entry_size := ed.elem_size for i in 0..<entries.len { - if i > 0 { io.write_string(w, ", ") or_return } + opt_write_iteration(w, opt, i) or_return data := uintptr(entries.data) + uintptr(i*entry_size) key := rawptr(data + entry_type.offsets[2]) value := rawptr(data + entry_type.offsets[3]) - marshal_to_writer(w, any{key, info.key.id}) or_return - io.write_string(w, ": ") or_return - marshal_to_writer(w, any{value, info.value.id}) or_return + // check for string type + { + v := any{key, info.key.id} + ti := runtime.type_info_base(type_info_of(v.id)) + a := any{v.data, ti.id} + name: string + + #partial switch info in ti.variant { + case runtime.Type_Info_String: + switch s in a { + case string: name = s + case cstring: name = string(s) + } + opt_write_key(w, opt, name) or_return + + case: return .Unsupported_Type + } + } + + marshal_to_writer(w, any{value, info.value.id}, opt) or_return } } - io.write_byte(w, '}') or_return + + opt_write_end(w, opt, '}') or_return case runtime.Type_Info_Struct: - io.write_byte(w, '{') or_return + opt_write_start(w, opt, '{') or_return + for name, i in info.names { - if i > 0 { io.write_string(w, ", ") or_return } - io.write_quoted_string(w, name) or_return - io.write_string(w, ": ") or_return + opt_write_iteration(w, opt, i) or_return + opt_write_key(w, opt, name) or_return id := info.types[i].id data := rawptr(uintptr(v.data) + info.offsets[i]) - marshal_to_writer(w, any{data, id}) or_return + marshal_to_writer(w, any{data, id}, opt) or_return } - io.write_byte(w, '}') or_return + + opt_write_end(w, opt, '}') or_return case runtime.Type_Info_Union: tag_ptr := uintptr(v.data) + info.tag_offset @@ -270,11 +332,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { io.write_string(w, "null") or_return } else { id := info.variants[tag-1].id - return marshal_to_writer(w, any{v.data, id}) + return marshal_to_writer(w, any{v.data, id}, opt) } case runtime.Type_Info_Enum: - return marshal_to_writer(w, any{v.data, info.base.id}) + return marshal_to_writer(w, any{v.data, info.base.id}, opt) case runtime.Type_Info_Bit_Set: is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { @@ -330,3 +392,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) { return } + +// write key as quoted string or with optional quotes in mjson +opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) { + switch opt.spec { + case .JSON, .JSON5: + io.write_quoted_string(w, name) or_return + io.write_string(w, ": ") or_return + + case .MJSON: + if opt.mjson_keys_use_quotes { + io.write_quoted_string(w, name) or_return + } else { + io.write_string(w, name) or_return + } + + if opt.mjson_keys_use_equal_sign { + io.write_string(w, " = ") or_return + } else { + io.write_string(w, ": ") or_return + } + } + + return +} + +// insert start byte and increase indentation on pretty +opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) { + // skip mjson starting braces + if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start { + opt.mjson_skipped_first_braces_start = true + return + } + + io.write_byte(w, c) or_return + opt.indentation += 1 + + if opt.pretty { + io.write_byte(w, '\n') or_return + } + + return +} + +// insert comma seperation and write indentations +opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) { + switch opt.spec { + case .JSON, .JSON5: + if iteration > 0 { + io.write_string(w, ", ") or_return + + if opt.pretty { + io.write_byte(w, '\n') or_return + } + } + + opt_write_indentation(w, opt) or_return + + case .MJSON: + if iteration > 0 { + // on pretty no commas necessary + if opt.pretty { + io.write_byte(w, '\n') or_return + } else { + // comma seperation necessary for non pretty output! + io.write_string(w, ", ") or_return + } + } + + opt_write_indentation(w, opt) or_return + } + + return +} + +// decrease indent, write spacing and insert end byte +opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) { + if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end { + if opt.indentation == 0 { + opt.mjson_skipped_first_braces_end = true + return + } + } + + opt.indentation -= 1 + + if opt.pretty { + io.write_byte(w, '\n') or_return + opt_write_indentation(w, opt) or_return + } + + io.write_byte(w, c) or_return + return +} + +// writes current indentation level based on options +opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) { + if !opt.pretty { + return + } + + if opt.use_spaces { + spaces := opt.spaces == 0 ? 4 : opt.spaces + for _ in 0..<opt.indentation * spaces { + io.write_byte(w, ' ') or_return + } + } else { + for _ in 0..<opt.indentation { + io.write_byte(w, '\t') or_return + } + } + + return +} diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 2ff268a21..97d2421d4 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token} if end_token == .Close_Brace { - assert(expect_token(p, .Open_Brace) == nil) + unmarshal_expect_token(p, .Open_Brace) } v := v @@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm } if end_token == .Close_Brace { - assert(expect_token(p, .Close_Brace) == nil) + unmarshal_expect_token(p, .Close_Brace) } return } diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 4db140afa..7429a093d 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1031,6 +1031,15 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { } } +fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) { + io.write_string(fi.writer, "#soa{data=0x", &fi.n) + _fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER) + io.write_string(fi.writer, ", index=", &fi.n) + _fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER) + io.write_string(fi.writer, "}", &fi.n) +} + + enum_value_to_string :: proc(val: any) -> (string, bool) { v := val v.id = runtime.typeid_base(v.id) @@ -1867,6 +1876,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fmt_pointer(fi, ptr, verb) } + case runtime.Type_Info_Soa_Pointer: + ptr := (^runtime.Raw_Soa_Pointer)(v.data)^ + fmt_soa_pointer(fi, ptr, verb) + case runtime.Type_Info_Multi_Pointer: ptr := (^rawptr)(v.data)^ if ptr == nil { @@ -2046,18 +2059,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array) entry_type := ed.elem.variant.(runtime.Type_Info_Struct) entry_size := ed.elem_size - + /* + NOTE: The layout of a `map` is as follows: + + map[Key]Value + + ## Internal Layout + struct { + hashes: []int, + entries: [dynamic]struct{ + hash: uintptr, + next: int, + key: Key, + value: Value, + }, + } + */ for i in 0..<entries.len { if i > 0 { io.write_string(fi.writer, ", ", &fi.n) } data := uintptr(entries.data) + uintptr(i*entry_size) - key := data + entry_type.offsets[2] + key := data + entry_type.offsets[2] // key: Key fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v') io.write_string(fi.writer, "=", &fi.n) - value := data + entry_type.offsets[3] + value := data + entry_type.offsets[3] // value: Value fmt_arg(fi, any{rawptr(value), info.value.id}, 'v') } } diff --git a/core/image/common.odin b/core/image/common.odin index beb3f93ee..a627fa68b 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -475,7 +475,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok } // Does the image have 1 or 2 channels, a valid bit depth (8 or 16), -// Is the pointer valid, are the dimenions valid? +// Is the pointer valid, are the dimensions valid? is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) { // Were we actually given a valid image? if img == nil { @@ -495,7 +495,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) { // This returns 0 if any of the inputs is zero. bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth) - // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail. + // If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail. if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { return false } @@ -504,7 +504,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) { } // Does the image have 3 or 4 channels, a valid bit depth (8 or 16), -// Is the pointer valid, are the dimenions valid? +// Is the pointer valid, are the dimensions valid? is_valid_color_image :: proc(img: ^Image) -> (ok: bool) { // Were we actually given a valid image? if img == nil { @@ -524,7 +524,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) { // This returns 0 if any of the inputs is zero. bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth) - // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail. + // If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail. if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS { return false } @@ -533,7 +533,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) { } // Does the image have 1..4 channels, a valid bit depth (8 or 16), -// Is the pointer valid, are the dimenions valid? +// Is the pointer valid, are the dimensions valid? is_valid_image :: proc(img: ^Image) -> (ok: bool) { // Were we actually given a valid image? if img == nil { diff --git a/core/image/png/example.odin b/core/image/png/example.odin index 17436c260..c1e2f0c73 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b defer close(fd) write_string(fd, - fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)), + fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)), ) if channels == 3 { diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 35fdb58d8..3faa39c83 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -1002,7 +1002,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a o16 = o16[out_image_channels:] } case: - unreachable("We should never seen # channels other than 1-4 inclusive.") + panic("We should never seen # channels other than 1-4 inclusive.") } img.pixels = t @@ -1195,7 +1195,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a o = o[out_image_channels:] } case: - unreachable("We should never seen # channels other than 1-4 inclusive.") + panic("We should never seen # channels other than 1-4 inclusive.") } img.pixels = t @@ -1206,7 +1206,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw returns will likely bypass this processing pipeline. */ - unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here.") + panic("We should never see bit depths other than 8, 16 and 'Paletted' here.") } return img, nil diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 22b5d953d..0b682fbc3 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -295,6 +295,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL --- objc_find_class :: proc($name: string) -> objc_Class --- objc_register_class :: proc($name: string) -> objc_Class --- + +valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr --- + // Internal compiler use only __entry_point :: proc() ---
\ No newline at end of file diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index 7f0d3b07a..bf537a161 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg return Logger{file_console_logger_proc, data, lowest, opt} } -destroy_console_logger :: proc(log: ^Logger) { +destroy_console_logger :: proc(log: Logger) { free(log.data) } @@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string fmt.sbprintf(&buf, "[%s] ", data.ident) } //TODO(Hoej): When we have better atomics and such, make this thread-safe - fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text) + fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text) } do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) { diff --git a/core/log/log.odin b/core/log/log.odin index d34a135cc..a699247b8 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -6,7 +6,6 @@ import "core:fmt" // NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle. -Level :: runtime.Logger_Level /* Logger_Level :: enum { Debug = 0, @@ -16,8 +15,8 @@ Logger_Level :: enum { Fatal = 40, } */ +Level :: runtime.Logger_Level -Option :: runtime.Logger_Option /* Option :: enum { Level, @@ -30,11 +29,12 @@ Option :: enum { Terminal_Color } */ +Option :: runtime.Logger_Option -Options :: runtime.Logger_Options /* Options :: bit_set[Option]; */ +Options :: runtime.Logger_Options Full_Timestamp_Opts :: Options{ .Date, @@ -52,12 +52,11 @@ Location_File_Opts :: Options{ } -Logger_Proc :: runtime.Logger_Proc /* Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); */ +Logger_Proc :: runtime.Logger_Proc -Logger :: runtime.Logger /* Logger :: struct { procedure: Logger_Proc, @@ -66,6 +65,7 @@ Logger :: struct { options: Logger_Options, } */ +Logger :: runtime.Logger nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { // Do nothing @@ -75,7 +75,6 @@ nil_logger :: proc() -> Logger { return Logger{nil_logger_proc, nil, Level.Debug, nil} } -// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`? debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(level=.Debug, fmt_str=fmt_str, args=args, location=location) } diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin new file mode 100644 index 000000000..997bd1a68 --- /dev/null +++ b/core/log/log_allocator.odin @@ -0,0 +1,107 @@ +package log + +import "core:runtime" + +Log_Allocator :: struct { + allocator: runtime.Allocator, + level: Level, + prefix: string, + locked: bool, +} + +log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") { + la.allocator = allocator + la.level = level + la.prefix = prefix + la.locked = false +} + + +log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator { + return runtime.Allocator{ + procedure = log_allocator_proc, + data = la, + } +} + +log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) { + la := (^Log_Allocator)(allocator_data) + + padding := " " if la.prefix != "" else "" + + if !la.locked { + la.locked = true + defer la.locked = false + + switch mode { + case .Alloc: + logf( + level=la.level, + fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)", + args = {la.prefix, padding, size, alignment}, + location = location, + ) + case .Free: + if old_size != 0 { + logf( + level=la.level, + fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)", + args = {la.prefix, padding, old_memory, old_size}, + location = location, + ) + } else { + logf( + level=la.level, + fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)", + args = {la.prefix, padding, old_memory}, + location = location, + ) + } + case .Free_All: + logf( + level=la.level, + fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)", + args = {la.prefix, padding}, + location = location, + ) + case .Resize: + logf( + level=la.level, + fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)", + args = {la.prefix, padding, old_memory, old_size, size, alignment}, + location = location, + ) + case .Query_Features: + logf( + level=la.level, + fmt_str = "%s%ALLOCATOR(mode=.Query_Features)", + args = {la.prefix, padding}, + location = location, + ) + case .Query_Info: + logf( + level=la.level, + fmt_str = "%s%ALLOCATOR(mode=.Query_Info)", + args = {la.prefix, padding}, + location = location, + ) + } + } + + data, err := la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location) + if !la.locked { + la.locked = true + defer la.locked = false + if err != nil { + logf( + level=la.level, + fmt_str = "%s%ALLOCATOR ERROR=%v", + args = {la.prefix, padding, error}, + location = location, + ) + } + } + return data, err +}
\ No newline at end of file diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 3cce69675..6f972937a 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -449,7 +449,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra in the loop is non-zero, although very low. -- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas. - If the BPSW test and/or the addtional Frobenious test have been + If the BPSW test and/or the additional Frobenious test have been performed instead of just the Miller-Rabin test with the bases 2 and 3, a single extra test should suffice, so such a very unlikely event will not do much harm. diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin index 9f22fa45e..c11151b25 100644 --- a/core/math/linalg/general.odin +++ b/core/math/linalg/general.odin @@ -94,7 +94,7 @@ quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) { vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3} cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross} -vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) { +vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) { return v / length(v) } quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) { @@ -102,7 +102,7 @@ quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) { } normalize :: proc{vector_normalize, quaternion_normalize} -vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) { +vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) { m := length(v) return 0 if m == 0 else v/m } @@ -113,7 +113,7 @@ quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) { normalize0 :: proc{vector_normalize0, quaternion_normalize0} -vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) { +vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) { return math.sqrt(dot(v, v)) } diff --git a/core/math/math.odin b/core/math/math.odin index 88cba965a..5f9c737ab 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -185,16 +185,23 @@ log :: proc{ log_f64, log_f64le, log_f64be, } -log2_f16 :: logb_f16 -log2_f16le :: logb_f16le -log2_f16be :: logb_f16be -log2_f32 :: logb_f32 -log2_f32le :: logb_f32le -log2_f32be :: logb_f32be -log2_f64 :: logb_f64 -log2_f64le :: logb_f64le -log2_f64be :: logb_f64be -log2 :: logb +log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) } +log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) } +log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) } + +log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) } +log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) } +log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) } + +log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) } +log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) } +log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) } + +log2 :: proc{ + log2_f16, log2_f16le, log2_f16be, + log2_f32, log2_f32le, log2_f32be, + log2_f64, log2_f64le, log2_f64be, +} log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 } log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) } @@ -607,6 +614,25 @@ floor_mod :: proc "contextless" (x, y: $T) -> T return r } +divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T) + where intrinsics.type_is_integer(T) { + div = x / y + mod = x % y + return +} + +floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T) + where intrinsics.type_is_integer(T) { + div = x / y + mod = x % y + if (div > 0 && y < 0) || (mod < 0 && y > 0) { + div -= 1 + mod += y + } + return +} + + modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) { shift :: F16_SHIFT mask :: F16_MASK diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 7416ebdb7..54004d333 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -61,114 +61,38 @@ DEFAULT_PAGE_SIZE :: 4 * 1024 alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr { - if size == 0 { - return nil - } - if allocator.procedure == nil { - return nil - } - data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc) - _ = err + data, _ := runtime.mem_alloc(size, alignment, allocator, loc) return raw_data(data) } alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { - if size == 0 { - return nil, nil - } - if allocator.procedure == nil { - return nil, nil - } - return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc) + return runtime.mem_alloc(size, alignment, allocator, loc) } free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { - if ptr == nil { - return nil - } - if allocator.procedure == nil { - return nil - } - _, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc) - return err + return runtime.mem_free(ptr, allocator, loc) } free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { - if bytes == nil { - return nil - } - if allocator.procedure == nil { - return nil - } - _, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc) - return err + return runtime.mem_free_bytes(bytes, allocator, loc) } free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error { - if allocator.procedure != nil { - _, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc) - return err - } - return nil + return runtime.mem_free_all(allocator, loc) } resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr { - if allocator.procedure == nil { - return nil - } - if new_size == 0 { - if ptr != nil { - allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc) - } - return nil - } else if ptr == nil { - _, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc) - _ = err - return nil - } - data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc) - if err == .Mode_Not_Implemented { - data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc) - if err != nil { - return nil - } - runtime.copy(data, byte_slice(ptr, old_size)) - _, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc) - return raw_data(data) - } + data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc) return raw_data(data) } resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { - if allocator.procedure == nil { - return nil, nil - } - ptr := raw_data(old_data) - old_size := len(old_data) - if new_size == 0 { - if ptr != nil { - _, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc) - return nil, err - } - return nil, nil - } else if ptr == nil { - return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc) - } - data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc) - if err == .Mode_Not_Implemented { - data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc) - if err != nil { - return data, err - } - runtime.copy(data, old_data) - _, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc) - } - return data, err + return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc) } query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) { if allocator.procedure != nil { - allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc) + allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc) return set } return nil @@ -177,7 +101,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) { props.pointer = pointer if allocator.procedure != nil { - allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc) + allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc) } return } diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index e2a0cc0fc..0d52d2599 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -31,6 +31,14 @@ Arena_Temp_Memory :: struct { } +arena_init :: proc(a: ^Arena, data: []byte) { + a.data = data + a.offset = 0 + a.peak_used = 0 + a.temp_count = 0 +} + +@(deprecated="prefer 'mem.arena_init'") init_arena :: proc(a: ^Arena, data: []byte) { a.data = data a.offset = 0 @@ -293,6 +301,14 @@ Stack :: struct { peak_used: int, } +stack_init :: proc(s: ^Stack, data: []byte) { + s.data = data + s.prev_offset = 0 + s.curr_offset = 0 + s.peak_used = 0 +} + +@(deprecated="prefer 'mem.stack_init'") init_stack :: proc(s: ^Stack, data: []byte) { s.data = data s.prev_offset = 0 @@ -445,27 +461,34 @@ Small_Stack_Allocation_Header :: struct { // Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order Small_Stack :: struct { - data: []byte, - offset: int, + data: []byte, + offset: int, peak_used: int, } +small_stack_init :: proc(s: ^Small_Stack, data: []byte) { + s.data = data + s.offset = 0 + s.peak_used = 0 +} + +@(deprecated="prefer 'small_stack_init'") init_small_stack :: proc(s: ^Small_Stack, data: []byte) { - s.data = data - s.offset = 0 + s.data = data + s.offset = 0 s.peak_used = 0 } small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { return Allocator{ procedure = small_stack_allocator_proc, - data = stack, + data = stack, } } small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, - old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) { + old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) { s := cast(^Small_Stack)allocator_data if s.data == nil { diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 7295a2afa..f7be69adc 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -54,48 +54,7 @@ compare :: proc "contextless" (a, b: []byte) -> int { } compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check { - switch { - case a == b: - return 0 - case a == nil: - return -1 - case b == nil: - return -1 - case n == 0: - return 0 - } - - x := slice_ptr(a, n) - y := slice_ptr(b, n) - - SU :: size_of(uintptr) - fast := n/SU + 1 - offset := (fast-1)*SU - curr_block := 0 - if n < SU { - fast = 0 - } - - la := slice_ptr((^uintptr)(a), fast) - lb := slice_ptr((^uintptr)(b), fast) - - for /**/; curr_block < fast; curr_block += 1 { - if la[curr_block] ~ lb[curr_block] != 0 { - for pos := curr_block*SU; pos < n; pos += 1 { - if x[pos] ~ y[pos] != 0 { - return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1 - } - } - } - } - - for /**/; offset < n; offset += 1 { - if x[offset] ~ y[offset] != 0 { - return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1 - } - } - - return 0 + return runtime.memory_compare(a, b, n) } check_zero :: proc(data: []byte) -> bool { diff --git a/core/mem/raw.odin b/core/mem/raw.odin index 2bce2d7aa..8322ace30 100644 --- a/core/mem/raw.odin +++ b/core/mem/raw.odin @@ -8,6 +8,7 @@ Raw_Cstring :: runtime.Raw_Cstring Raw_Slice :: runtime.Raw_Slice Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array Raw_Map :: runtime.Raw_Map +Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer Raw_Complex64 :: struct {real, imag: f32} Raw_Complex128 :: struct {real, imag: f64} diff --git a/core/mem/virtual/arena_util.odin b/core/mem/virtual/arena_util.odin index 408566299..0e152db7a 100644 --- a/core/mem/virtual/arena_util.odin +++ b/core/mem/virtual/arena_util.odin @@ -1,5 +1,10 @@ package mem_virtual +arena_init :: proc{ + static_arena_init, + growing_arena_init, +} + arena_temp_begin :: proc{ static_arena_temp_begin, growing_arena_temp_begin, diff --git a/core/mem/virtual/growing_arena.odin b/core/mem/virtual/growing_arena.odin index 9a4272a1e..f1067071b 100644 --- a/core/mem/virtual/growing_arena.odin +++ b/core/mem/virtual/growing_arena.odin @@ -13,6 +13,13 @@ Growing_Arena :: struct { DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough +growing_arena_init :: proc(arena: ^Static_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) { + arena.block = memory_block_alloc(0, reserved, {}) or_return + arena.total_used = 0 + arena.total_reserved = arena.block.reserved + return +} + growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) { align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check { alignment_offset := uint(0) @@ -37,7 +44,7 @@ growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int block_size := max(size, arena.minimum_block_size) - new_block := memory_block_alloc(block_size, block_size, {}) or_return + new_block := memory_block_alloc(size, block_size, {}) or_return new_block.prev = arena.curr_block arena.curr_block = new_block arena.total_reserved += new_block.reserved @@ -95,9 +102,9 @@ growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator { } growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, - location := #caller_location) -> (data: []byte, err: Allocator_Error) { + size, alignment: int, + old_memory: rawptr, old_size: int, + location := #caller_location) -> (data: []byte, err: Allocator_Error) { arena := (^Growing_Arena)(allocator_data) switch mode { diff --git a/core/mem/virtual/virtual_darwin.odin b/core/mem/virtual/virtual_darwin.odin new file mode 100644 index 000000000..5505149b0 --- /dev/null +++ b/core/mem/virtual/virtual_darwin.odin @@ -0,0 +1,148 @@ +//+build darwin +//+private +package mem_virtual + +foreign import libc "System.framework" +import "core:c" + +PROT_NONE :: 0x0 /* [MC2] no permissions */ +PROT_READ :: 0x1 /* [MC2] pages can be read */ +PROT_WRITE :: 0x2 /* [MC2] pages can be written */ +PROT_EXEC :: 0x4 /* [MC2] pages can be executed */ + +// Sharing options +MAP_SHARED :: 0x1 /* [MF|SHM] share changes */ +MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */ + +// Other flags +MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */ +MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */ +MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */ +MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */ +MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */ +MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */ +MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */ +MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */ + +// Mapping type +MAP_FILE :: 0x0000 /* map from file (default) */ +MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */ + + +/* + * The MAP_RESILIENT_* flags can be used when the caller wants to map some + * possibly unreliable memory and be able to access it safely, possibly + * getting the wrong contents rather than raising any exception. + * For safety reasons, such mappings have to be read-only (PROT_READ access + * only). + * + * MAP_RESILIENT_CODESIGN: + * accessing this mapping will not generate code-signing violations, + * even if the contents are tainted. + * MAP_RESILIENT_MEDIA: + * accessing this mapping will not generate an exception if the contents + * are not available (unreachable removable or remote media, access beyond + * end-of-file, ...). Missing contents will be replaced with zeroes. + */ +MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */ +MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */ + +MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */ + +// Flags used to support translated processes. +MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */ +MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */ + +// Process memory locking +MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */ +MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */ + +MADV_NORMAL :: 0 /* [MC1] no further special treatment */ +MADV_RANDOM :: 1 /* [MC1] expect random page refs */ +MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */ +MADV_WILLNEED :: 3 /* [MC1] will need these pages */ +MADV_DONTNEED :: 4 /* [MC1] dont need these pages */ +MADV_FREE :: 5 /* pages unneeded, discard contents */ +MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */ +MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */ +MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */ +MADV_CAN_REUSE :: 9 +MADV_PAGEOUT :: 10 /* page out now (internal only) */ + +// msync() flags +MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */ +MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */ +MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */ +MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */ +MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */ + +// Return bits from mincore +MINCORE_INCORE :: 0x1 /* Page is incore */ +MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */ +MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */ +MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */ +MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */ +MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */ +MINCORE_COPIED :: 0x40 /* Page has been copied */ +MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */ + +// Allocation failure result +MAP_FAILED : rawptr = rawptr(~uintptr(0)) + +foreign libc { + @(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int --- + @(link_name="munlockall") _munlockall :: proc() -> c.int --- + @(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int --- + @(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr --- + @(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int --- + @(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int --- + @(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int --- + @(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int --- + @(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int --- + @(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int --- + @(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int --- + @(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int --- + @(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int --- + @(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int --- +} + + +_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { + result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) + if result == MAP_FAILED { + return nil, .Out_Of_Memory + } + return ([^]byte)(uintptr(result))[:size], nil +} + +_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { + result := _mprotect(data, size, PROT_READ|PROT_WRITE) + if result != 0 { + return .Out_Of_Memory + } + return nil +} +_decommit :: proc "contextless" (data: rawptr, size: uint) { + _mprotect(data, size, PROT_NONE) + _madvise(data, size, MADV_FREE) +} +_release :: proc "contextless" (data: rawptr, size: uint) { + _munmap(data, size) +} +_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { + pflags: c.int + pflags = PROT_NONE + if .Read in flags { pflags |= PROT_READ } + if .Write in flags { pflags |= PROT_WRITE } + if .Execute in flags { pflags |= PROT_EXEC } + err := _mprotect(data, size, pflags) + return err != 0 +} + + +_platform_memory_init :: proc() { + DEFAULT_PAGE_SIZE = 4096 + + // is power of two + assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) +} diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index 2f6fbdd01..0f376312f 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int { } mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int { - res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot)) + res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot)) return c.int(res) } diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index f4aa67446..c8f73a31b 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -552,13 +552,20 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) { return } +Field_Flags :: distinct bit_set[Field_Flag] + Field_Flag :: enum { + Invalid, + Unknown, + Ellipsis, Using, No_Alias, C_Vararg, Auto_Cast, Any_Int, + Subtype, + By_Ptr, Results, Tags, @@ -566,11 +573,38 @@ Field_Flag :: enum { Typeid_Token, } -Field_Flags :: distinct bit_set[Field_Flag] +field_flag_strings := [Field_Flag]string{ + .Invalid = "", + .Unknown = "", + + .Ellipsis = "..", + .Using = "using", + .No_Alias = "#no_alias", + .C_Vararg = "#c_vararg", + .Auto_Cast = "auto_cast", + .Any_Int = "#any_int", + .Subtype = "#subtype", + .By_Ptr = "#by_ptr", + + .Results = "results", + .Tags = "field tag", + .Default_Parameters = "default parameters", + .Typeid_Token = "typeid", +} + +field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{ + {"no_alias", .No_Alias}, + {"c_vararg", .C_Vararg}, + {"any_int", .Any_Int}, + {"subtype", .Subtype}, + {"by_ptr", .By_Ptr}, +} + Field_Flags_Struct :: Field_Flags{ .Using, .Tags, + .Subtype, } Field_Flags_Record_Poly_Params :: Field_Flags{ .Typeid_Token, @@ -583,6 +617,7 @@ Field_Flags_Signature :: Field_Flags{ .C_Vararg, .Auto_Cast, .Any_Int, + .By_Ptr, .Default_Parameters, } @@ -665,6 +700,7 @@ Proc_Type :: struct { Pointer_Type :: struct { using node: Expr, + tag: ^Expr, pointer: tokenizer.Pos, elem: ^Expr, } diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index 400c064f5..5ec6bc335 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node { r.results = auto_cast clone(r.results) case ^Pointer_Type: r.elem = clone(r.elem) + r.tag = clone(r.tag) case ^Multi_Pointer_Type: r.elem = clone(r.elem) case ^Array_Type: diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 62682004d..895fcf70d 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -186,6 +186,7 @@ Type_Kind :: enum u32le { Relative_Slice = 21, Multi_Pointer = 22, Matrix = 23, + Soa_Pointer = 24, } Type_Elems_Cap :: 4 @@ -245,6 +246,7 @@ Type :: struct { // .Relative_Slice - 2 types: 0=slice type, 1=base integer // .Multi_Pointer - 1 type: 0=element // .Matrix - 1 type: 0=element + // .Soa_Pointer - 1 type: 0=element types: Array(Type_Index), // Used by: diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 52ecb4781..bd1422da3 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -245,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) { return } skip_possible_newline :: proc(p: ^Parser) -> bool { - if .Optional_Semicolons not_in p.flags { - return false - } - - prev := p.curr_tok - if tokenizer.is_newline(prev) { + if tokenizer.is_newline(p.curr_tok) { advance_token(p) return true } @@ -1611,20 +1606,6 @@ new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.E return field } - -Field_Prefix :: enum { - Invalid, - Unknown, - - Using, - No_Alias, - C_Vararg, - Auto_Cast, - Any_Int, -} - -Field_Prefixes :: distinct bit_set[Field_Prefix] - Expr_And_Flags :: struct { expr: ^ast.Expr, flags: ast.Field_Flags, @@ -1666,7 +1647,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags, return idents[:] } -is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix { +is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag { #partial switch p.curr_tok.kind { case .EOF: return .Invalid @@ -1677,17 +1658,15 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix { advance_token(p) return .Auto_Cast case .Hash: + tok: tokenizer.Token advance_token(p) - defer advance_token(p) - #partial switch p.curr_tok.kind { - case .Ident: - switch p.curr_tok.text { - case "no_alias": - return .No_Alias - case "c_vararg": - return .C_Vararg - case "any_int": - return .Any_Int + tok = p.curr_tok + advance_token(p) + if tok.kind == .Ident { + for kf in ast.field_hash_flag_strings { + if kf.key == tok.text { + return kf.flag + } } } return .Unknown @@ -1695,8 +1674,8 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix { return .Invalid } -parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags { - counts: [len(Field_Prefix)]int +parse_field_prefixes :: proc(p: ^Parser) -> (flags: ast.Field_Flags) { + counts: [len(ast.Field_Flag)]int for { kind := is_token_field_prefix(p) @@ -1712,31 +1691,17 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags { counts[kind] += 1 } - flags: ast.Field_Flags - - for kind in Field_Prefix { + for kind in ast.Field_Flag { count := counts[kind] - switch kind { - case .Invalid, .Unknown: // Ignore - case .Using: - if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list") } - if count > 0 { flags += {.Using} } - case .No_Alias: - if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list") } - if count > 0 { flags += {.No_Alias} } - case .C_Vararg: - if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list") } - if count > 0 { flags += {.C_Vararg} } - case .Auto_Cast: - if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list") } - if count > 0 { flags += {.Auto_Cast} } - case .Any_Int: - if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list") } - if count > 0 { flags += {.Any_Int} } + if kind == .Invalid || kind == .Unknown { + // Ignore + } else { + if count > 1 { error(p, p.curr_tok.pos, "multiple '%s' in this field list", ast.field_flag_strings[kind]) } + if count > 0 { flags += {kind} } } } - return flags + return } check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) { @@ -1748,19 +1713,13 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se for flag in ast.Field_Flag { if flag not_in allowed_flags && flag in flags { - switch flag { - case .Using: - error(p, p.curr_tok.pos, "'using' is not allowed within this field list") - case .No_Alias: - error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list") - case .C_Vararg: - error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list") - case .Auto_Cast: - error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list") - case .Any_Int: - error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list") + #partial switch flag { + case .Unknown, .Invalid: + // ignore case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token: panic("Impossible prefixes") + case: + error(p, p.curr_tok.pos, "'%s' is not allowed within this field list", ast.field_flag_strings[flag]) } flags -= {flag} } @@ -2271,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { return parse_call_expr(p, bd) - case "soa", "simd": + case "soa": bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) bd.tok = tok bd.name = name.text @@ -2280,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { #partial switch t in type.derived_expr { case ^ast.Array_Type: t.tag = bd case ^ast.Dynamic_Array_Type: t.tag = bd + case ^ast.Pointer_Type: t.tag = bd + case: + error(p, original_type.pos, "expected an array or pointer type after #%s", name.text) + } + return original_type + + case "simd": + bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) + bd.tok = tok + bd.name = name.text + original_type := parse_type(p) + type := ast.unparen_expr(original_type) + #partial switch t in type.derived_expr { + case ^ast.Array_Type: t.tag = bd case: error(p, original_type.pos, "expected an array type after #%s", name.text) } @@ -2631,7 +2604,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { tok := expect_token(p, .Union) poly_params: ^ast.Field_List align: ^ast.Expr - is_maybe: bool is_no_nil: bool is_shared_nil: bool @@ -2656,10 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } align = parse_expr(p, true) case "maybe": - if is_maybe { - error(p, tag.pos, "duplicate union tag '#%s'", tag.text) - } - is_maybe = true + error(p, tag.pos, "#%s functionality has now been merged with standard 'union' functionality", tag.text) case "no_nil": if is_no_nil { error(p, tag.pos, "duplicate union tag '#%s'", tag.text) @@ -2676,19 +2645,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } p.expr_level = prev_level - if is_no_nil && is_maybe { - error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together") - } if is_no_nil && is_shared_nil { error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together") } - if is_shared_nil && is_maybe { - error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together") - } union_kind := ast.Union_Type_Kind.Normal switch { - case is_maybe: union_kind = .maybe case is_no_nil: union_kind = .no_nil case is_shared_nil: union_kind = .shared_nil } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 1fb653b85..00cccd2a7 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -6,19 +6,19 @@ import "core:runtime" user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: - dir = get_env("LocalAppData") + dir = get_env("LocalAppData", allocator) if dir != "" { dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: - dir = get_env("HOME") + dir = get_env("HOME", allocator) if dir != "" { dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems - dir = get_env("XDG_CACHE_HOME") + dir = get_env("XDG_CACHE_HOME", allocator) if dir == "" { - dir = get_env("HOME") + dir = get_env("HOME", allocator) if dir == "" { return } @@ -34,19 +34,19 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: - dir = get_env("AppData") + dir = get_env("AppData", allocator) if dir != "" { dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: - dir = get_env("HOME") + dir = get_env("HOME", allocator) if dir != "" { dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems - dir = get_env("XDG_CACHE_HOME") + dir = get_env("XDG_CACHE_HOME", allocator) if dir == "" { - dir = get_env("HOME") + dir = get_env("HOME", allocator) if dir == "" { return } @@ -59,13 +59,13 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro return } -user_home_dir :: proc() -> (dir: string, err: Error) { +user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { env := "HOME" #partial switch ODIN_OS { case .Windows: env = "USERPROFILE" } - if v := get_env(env); v != "" { + if v := get_env(env, allocator); v != "" { return v, nil } return "", .Invalid_Path diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index de3a22187..1064f4aa8 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -311,7 +311,7 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 { low := uintptr(offset & 0xFFFFFFFF) high := uintptr(offset >> 32) result: i64 - res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence))) + res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence))) return -1 if res < 0 else result } } diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index 00a9c9fb0..c932f202a 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont } - d, derr := os.open(dir) + d, derr := os.open(dir, os.O_RDONLY) if derr != 0 { return } diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 27a83e680..07ab94183 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -1,7 +1,6 @@ package reflect import "core:runtime" -import "core:mem" import "core:intrinsics" _ :: intrinsics @@ -34,6 +33,7 @@ Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice Type_Info_Matrix :: runtime.Type_Info_Matrix +Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value @@ -68,6 +68,7 @@ Type_Kind :: enum { Relative_Pointer, Relative_Slice, Matrix, + Soa_Pointer, } @@ -102,6 +103,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case Type_Info_Relative_Pointer: return .Relative_Pointer case Type_Info_Relative_Slice: return .Relative_Slice case Type_Info_Matrix: return .Matrix + case Type_Info_Soa_Pointer: return .Soa_Pointer } } @@ -194,6 +196,7 @@ typeid_elem :: proc(id: typeid) -> typeid { } case Type_Info_Pointer: return v.elem.id case Type_Info_Multi_Pointer: return v.elem.id + case Type_Info_Soa_Pointer: return v.elem.id case Type_Info_Array: return v.elem.id case Type_Info_Enumerated_Array: return v.elem.id case Type_Info_Slice: return v.elem.id @@ -220,7 +223,7 @@ align_of_typeid :: proc(T: typeid) -> int { as_bytes :: proc(v: any) -> []byte { if v != nil { sz := size_of_typeid(v.id) - return mem.slice_ptr((^byte)(v.data), sz) + return ([^]byte)(v.data)[:sz] } return nil } @@ -262,19 +265,19 @@ length :: proc(val: any) -> int { return a.count case Type_Info_Slice: - return (^mem.Raw_Slice)(val.data).len + return (^runtime.Raw_Slice)(val.data).len case Type_Info_Dynamic_Array: - return (^mem.Raw_Dynamic_Array)(val.data).len + return (^runtime.Raw_Dynamic_Array)(val.data).len case Type_Info_Map: - return (^mem.Raw_Map)(val.data).entries.len + return (^runtime.Raw_Map)(val.data).entries.len case Type_Info_String: if a.is_cstring { return len((^cstring)(val.data)^) } else { - return (^mem.Raw_String)(val.data).len + return (^runtime.Raw_String)(val.data).len } } return 0 @@ -297,10 +300,10 @@ capacity :: proc(val: any) -> int { return a.count case Type_Info_Dynamic_Array: - return (^mem.Raw_Dynamic_Array)(val.data).cap + return (^runtime.Raw_Dynamic_Array)(val.data).cap case Type_Info_Map: - return (^mem.Raw_Map)(val.data).entries.cap + return (^runtime.Raw_Map)(val.data).entries.cap } return 0 } @@ -340,14 +343,14 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any { return any{data, a.elem.id} case Type_Info_Slice: - raw := (^mem.Raw_Slice)(val.data) + raw := (^runtime.Raw_Slice)(val.data) runtime.bounds_check_error_loc(loc, i, raw.len) offset := uintptr(a.elem.size * i) data := rawptr(uintptr(raw.data) + offset) return any{data, a.elem.id} case Type_Info_Dynamic_Array: - raw := (^mem.Raw_Dynamic_Array)(val.data) + raw := (^runtime.Raw_Dynamic_Array)(val.data) runtime.bounds_check_error_loc(loc, i, raw.len) offset := uintptr(a.elem.size * i) data := rawptr(uintptr(raw.data) + offset) @@ -356,7 +359,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any { case Type_Info_String: if a.is_cstring { return nil } - raw := (^mem.Raw_String)(val.data) + raw := (^runtime.Raw_String)(val.data) runtime.bounds_check_error_loc(loc, i, raw.len) offset := uintptr(size_of(u8) * i) data := rawptr(uintptr(raw.data) + offset) @@ -725,6 +728,17 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 { panic("expected a union to reflect.get_union_variant_raw_tag") } +get_union_variant :: proc(a: any) -> any { + if a == nil { + return nil + } + id := union_variant_typeid(a) + if id == nil { + return nil + } + return any{a.data, id} +} + set_union_variant_raw_tag :: proc(a: any, tag: i64) { if a == nil { return } @@ -822,17 +836,17 @@ set_union_value :: proc(dst: any, value: any) -> bool { ti := runtime.type_info_base(type_info_of(dst.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if value.id == nil { - mem.zero(dst.data, ti.size) + intrinsics.mem_zero(dst.data, ti.size) return true } if ti.id == runtime.typeid_base(value.id) { - mem.copy(dst.data, value.data, ti.size) + intrinsics.mem_copy(dst.data, value.data, ti.size) return true } if type_info_union_is_pure_maybe(info) { if variant := info.variants[0]; variant.id == value.id { - mem.copy(dst.data, value.data, variant.size) + intrinsics.mem_copy(dst.data, value.data, variant.size) return true } return false @@ -844,7 +858,7 @@ set_union_value :: proc(dst: any, value: any) -> bool { if !info.no_nil { tag += 1 } - mem.copy(dst.data, value.data, variant.size) + intrinsics.mem_copy(dst.data, value.data, variant.size) set_union_variant_raw_tag(dst, tag) return true } @@ -1337,11 +1351,11 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) { case Type_Info_Slice: valid = true - value = (^mem.Raw_Slice)(a.data).data + value = (^runtime.Raw_Slice)(a.data).data case Type_Info_Dynamic_Array: valid = true - value = (^mem.Raw_Dynamic_Array)(a.data).data + value = (^runtime.Raw_Dynamic_Array)(a.data).data } return @@ -1383,7 +1397,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ } if .Simple_Compare in t.flags { - return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0 + return runtime.memory_compare(a.data, b.data, t.size) == 0 } t = runtime.type_info_core(t) @@ -1419,8 +1433,9 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ Type_Info_Enum, Type_Info_Simd_Vector, Type_Info_Relative_Pointer, + Type_Info_Soa_Pointer, Type_Info_Matrix: - return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0 + return runtime.memory_compare(a.data, b.data, t.size) == 0 case Type_Info_String: if v.is_cstring { @@ -1474,8 +1489,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ if !including_indirect_array_recursion { return false } - array_a := (^mem.Raw_Slice)(a.data) - array_b := (^mem.Raw_Slice)(b.data) + array_a := (^runtime.Raw_Slice)(a.data) + array_b := (^runtime.Raw_Slice)(b.data) if array_a.len != array_b.len { return false } @@ -1494,8 +1509,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ if !including_indirect_array_recursion { return false } - array_a := (^mem.Raw_Dynamic_Array)(a.data) - array_b := (^mem.Raw_Dynamic_Array)(b.data) + array_a := (^runtime.Raw_Dynamic_Array)(a.data) + array_b := (^runtime.Raw_Dynamic_Array)(b.data) if array_a.len != array_b.len { return false } @@ -1503,7 +1518,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ return true } if .Simple_Compare in v.elem.flags { - return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0 + return runtime.memory_compare((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0 } for i in 0..<array_a.len { diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 7be7ff812..f53b18e0d 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -68,6 +68,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { y := b.variant.(Type_Info_Multi_Pointer) or_return return are_types_identical(x.elem, y.elem) + case Type_Info_Soa_Pointer: + y := b.variant.(Type_Info_Soa_Pointer) or_return + return are_types_identical(x.elem, y.elem) + + case Type_Info_Procedure: y := b.variant.(Type_Info_Procedure) or_return switch { @@ -256,6 +261,11 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer) return ok } +is_soa_pointer :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + _, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer) + return ok +} is_pointer_internally :: proc(info: ^Type_Info) -> bool { if info == nil { return false } #partial switch v in info.variant { @@ -292,6 +302,11 @@ is_dynamic_map :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Map) return ok } +is_bit_set :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + _, ok := type_info_base(info).variant.(Type_Info_Bit_Set) + return ok +} is_slice :: proc(info: ^Type_Info) -> bool { if info == nil { return false } _, ok := type_info_base(info).variant.(Type_Info_Slice) @@ -437,6 +452,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - case Type_Info_Multi_Pointer: io.write_string(w, "[^]", &n) or_return write_type(w, info.elem, &n) or_return + case Type_Info_Soa_Pointer: + io.write_string(w, "#soa ^", &n) or_return + write_type(w, info.elem, &n) or_return case Type_Info_Procedure: io.write_string(w, "proc", &n) or_return if info.params == nil { diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 8fb3d7210..0310aff6d 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -176,6 +176,9 @@ Type_Info_Matrix :: struct { column_count: int, // Total element count = column_count * elem_stride } +Type_Info_Soa_Pointer :: struct { + elem: ^Type_Info, +} Type_Info_Flag :: enum u8 { Comparable = 0, @@ -217,6 +220,7 @@ Type_Info :: struct { Type_Info_Relative_Pointer, Type_Info_Relative_Slice, Type_Info_Matrix, + Type_Info_Soa_Pointer, }, } @@ -403,6 +407,12 @@ Raw_Cstring :: struct { data: [^]byte, } +Raw_Soa_Pointer :: struct { + data: rawptr, + index: int, +} + + /* // Defined internally by the compiler diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index 4f698a270..e3960088d 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -143,7 +143,7 @@ free_all :: proc{mem_free_all} @builtin delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { - return mem_free(raw_data(str), allocator, loc) + return mem_free_with_size(raw_data(str), len(str), allocator, loc) } @builtin delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { @@ -151,17 +151,24 @@ delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #cal } @builtin delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error { - return mem_free(raw_data(array), array.allocator, loc) + return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc) } @builtin delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { - return mem_free(raw_data(array), allocator, loc) + return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc) } @builtin delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { + Entry :: struct { + hash: uintptr, + next: int, + key: K, + value: V, + } + raw := transmute(Raw_Map)m err := delete_slice(raw.hashes, raw.entries.allocator, loc) - err1 := mem_free(raw.entries.data, raw.entries.allocator, loc) + err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc) if err == nil { err = err1 } @@ -335,68 +342,80 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: @builtin -append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { +append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int { if array == nil { - return - } - - if cap(array) < len(array)+1 { - cap := 2 * cap(array) + max(8, 1) - _ = reserve(array, cap, loc) + return 0 } - if cap(array)-len(array) > 0 { - a := (^Raw_Dynamic_Array)(array) - when size_of(E) != 0 { - data := ([^]E)(a.data) - assert(condition=data != nil, loc=loc) - data[a.len] = arg + when size_of(E) == 0 { + array.len += 1 + return 1 + } else { + if cap(array) < len(array)+1 { + cap := 2 * cap(array) + max(8, 1) + _ = reserve(array, cap, loc) } - a.len += 1 + if cap(array)-len(array) > 0 { + a := (^Raw_Dynamic_Array)(array) + when size_of(E) != 0 { + data := ([^]E)(a.data) + assert(condition=data != nil, loc=loc) + data[a.len] = arg + } + a.len += 1 + return 1 + } + return 0 } } @builtin -append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) { +append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int { if array == nil { - return + return 0 } arg_len := len(args) if arg_len <= 0 { - return + return 0 } - - if cap(array) < len(array)+arg_len { - cap := 2 * cap(array) + max(8, arg_len) - _ = reserve(array, cap, loc) - } - arg_len = min(cap(array)-len(array), arg_len) - if arg_len > 0 { - a := (^Raw_Dynamic_Array)(array) - when size_of(E) != 0 { - data := ([^]E)(a.data) - assert(condition=data != nil, loc=loc) - intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len) + when size_of(E) == 0 { + array.len += arg_len + return arg_len + } else { + if cap(array) < len(array)+arg_len { + cap := 2 * cap(array) + max(8, arg_len) + _ = reserve(array, cap, loc) } - a.len += arg_len + arg_len = min(cap(array)-len(array), arg_len) + if arg_len > 0 { + a := (^Raw_Dynamic_Array)(array) + when size_of(E) != 0 { + data := ([^]E)(a.data) + assert(condition=data != nil, loc=loc) + intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len) + } + a.len += arg_len + } + return arg_len } } // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type @builtin -append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) { +append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int { args := transmute([]E)arg - append_elems(array=array, args=args, loc=loc) + return append_elems(array=array, args=args, loc=loc) } // The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type @builtin -append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) { +append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) { for arg in args { - append(array = array, args = transmute([]E)(arg), loc = loc) + n += append(array = array, args = transmute([]E)(arg), loc = loc) } + return } // The append built-in procedure appends elements to the end of a dynamic array @@ -404,11 +423,13 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_ @builtin -append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) { +append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int { if array == nil { - return + return 0 } + prev_len := len(array) resize(array, len(array)+1) + return len(array)-prev_len } @@ -779,17 +800,3 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! { } p("not yet implemented", message, loc) } - -@builtin -@(disabled=ODIN_DISABLE_ASSERT) -unreachable :: proc(message := "", loc := #caller_location) -> ! { - p := context.assertion_failure_proc - if p == nil { - p = default_assertion_failure_proc - } - if message != "" { - p("internal error", message, loc) - } else { - p("internal error", "entered unreachable code", loc) - } -} diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin index d39c2dd0b..b6a685fcf 100644 --- a/core/runtime/dynamic_array_internal.odin +++ b/core/runtime/dynamic_array_internal.odin @@ -29,11 +29,15 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: new_size := cap * elem_size allocator := array.allocator - new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc) + new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc) if err != nil { return false } - if new_data != nil || elem_size == 0 { + if elem_size == 0 { + array.data = raw_data(new_data) + array.cap = cap + return true + } else if new_data != nil { array.data = raw_data(new_data) array.cap = min(cap, len(new_data)/elem_size) return true @@ -59,7 +63,7 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c new_size := new_cap * elem_size allocator := array.allocator - new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc) + new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc) if err != nil { return } diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin index fee0f570f..35b42d488 100644 --- a/core/runtime/dynamic_map_internal.odin +++ b/core/runtime/dynamic_map_internal.odin @@ -194,12 +194,15 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l new_size := new_count*size_of(T) new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc) - if new_data == nil || err != nil { + if err != nil { return false } - array.data = new_data - array.len = new_count - return true + if new_data != nil || size_of(E) == 0 { + array.data = raw_data(new_data) + array.len = new_count + return true + } + return false } __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) { @@ -207,7 +210,7 @@ __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_loc m.hashes[i] = -1 } - for i in 0 ..< m.entries.len { + for i in 0..<m.entries.len { entry_header := __dynamic_map_get_entry(header, i) entry_hash := __get_map_hash_from_entry(header, entry_header) entry_header.next = -1 diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 30798f623..048bff7ca 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -103,7 +103,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr { if data == nil { return nil } - if len < 0 { + if len <= 0 { return data } intrinsics.mem_zero(data, len) @@ -111,22 +111,18 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr { } mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { - if src == nil { - return dst + if src != nil && dst != src && len > 0 { + // NOTE(bill): This _must_ be implemented like C's memmove + intrinsics.mem_copy(dst, src, len) } - - // NOTE(bill): This _must_ be implemented like C's memmove - intrinsics.mem_copy(dst, src, len) return dst } mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { - if src == nil { - return dst + if src != nil && dst != src && len > 0 { + // NOTE(bill): This _must_ be implemented like C's memcpy + intrinsics.mem_copy_non_overlapping(dst, src, len) } - - // NOTE(bill): This _must_ be implemented like C's memcpy - intrinsics.mem_copy_non_overlapping(dst, src, len) return dst } @@ -142,28 +138,38 @@ mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNM return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) } -mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) { - if size == 0 { +mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { + if size == 0 || allocator.procedure == nil { return nil, nil } - if allocator.procedure == nil { - return nil, nil - } - data, err := allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) - return raw_data(data), err + return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) } mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { - if ptr == nil { - return .None - } - if allocator.procedure == nil { - return .None + if ptr == nil || allocator.procedure == nil { + return nil } _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc) return err } +mem_free_with_size :: #force_inline proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + if ptr == nil || allocator.procedure == nil { + return nil + } + _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc) + return err +} + +mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + if bytes == nil || allocator.procedure == nil { + return nil + } + _, err := allocator.procedure(allocator.data, .Free, 0, 0, raw_data(bytes), len(bytes), loc) + return err +} + + mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) { if allocator.procedure != nil { _, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc) @@ -171,21 +177,34 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle return } -mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (new_ptr: rawptr, err: Allocator_Error) { - new_data: []byte - switch { - case allocator.procedure == nil: - return - case new_size == 0: - new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc) - case ptr == nil: - new_data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc) - case: - new_data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc) - } - new_ptr = raw_data(new_data) - return +mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { + if allocator.procedure == nil { + return nil, nil + } + if new_size == 0 { + if ptr != nil { + _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc) + return nil, err + } + return nil, nil + } else if ptr == nil { + return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc) + } else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 { + return ([^]byte)(ptr)[:old_size], nil + } + + data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc) + if err == .Mode_Not_Implemented { + data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc) + if err != nil { + return data, err + } + copy(data, ([^]byte)(ptr)[:old_size]) + _, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc) + } + return data, err } + memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool { switch { case n == 0: return true @@ -341,7 +360,12 @@ string_eq :: proc "contextless" (lhs, rhs: string) -> bool { string_cmp :: proc "contextless" (a, b: string) -> int { x := transmute(Raw_String)a y := transmute(Raw_String)b - return memory_compare(x.data, y.data, min(x.len, y.len)) + + ret := memory_compare(x.data, y.data, min(x.len, y.len)) + if ret == 0 && x.len != y.len { + return -1 if x.len < y.len else +1 + } + return ret } string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) } diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 89c196fc2..959dad3a9 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -228,6 +228,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) { case Type_Info_Multi_Pointer: print_string("[^]") print_type(info.elem) + case Type_Info_Soa_Pointer: + print_string("#soa ^") + print_type(info.elem) case Type_Info_Procedure: print_string("proc") if info.params == nil { diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin index 782efa773..510abcbb9 100644 --- a/core/runtime/procs.odin +++ b/core/runtime/procs.odin @@ -6,7 +6,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows { @(private="file") @(default_calling_convention="stdcall") foreign lib { - RtlMoveMemory :: proc(dst, src: rawptr, length: int) --- + RtlMoveMemory :: proc(dst, s: rawptr, length: int) --- RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) --- } @@ -40,24 +40,34 @@ when ODIN_NO_CRT && ODIN_OS == .Windows { @(link_name="memmove", linkage="strong", require) memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr { - if dst != src { - d, s := ([^]byte)(dst), ([^]byte)(src) + d, s := ([^]byte)(dst), ([^]byte)(src) + if d == s || len == 0 { + return dst + } + if d > s && uintptr(d)-uintptr(s) < uintptr(len) { for i := len-1; i >= 0; i -= 1 { d[i] = s[i] } + return dst } - return dst - + + if s > d && uintptr(s)-uintptr(d) < uintptr(len) { + for i := 0; i < len; i += 1 { + d[i] = s[i] + } + return dst + } + return memcpy(dst, src, len) } @(link_name="memcpy", linkage="strong", require) memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr { - if dst != src { - d, s := ([^]byte)(dst), ([^]byte)(src) - for i := len-1; i >= 0; i -= 1 { + d, s := ([^]byte)(dst), ([^]byte)(src) + if d != s { + for i := 0; i < len; i += 1 { d[i] = s[i] } } - return dst + return d } } else { diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 440cf643f..e76a5599d 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -14,14 +14,14 @@ _ :: mem Turn a pointer and a length into a slice. */ from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T { - return ([^]T)(ptr)[:count] + return ([^]T)(ptr)[:count] } /* Turn a pointer and a length into a byte slice. */ bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte { - return ([^]byte)(ptr)[:byte_count] + return ([^]byte)(ptr)[:byte_count] } /* diff --git a/core/sort/sort.odin b/core/sort/sort.odin index 2ce74294d..a2b4d7ea0 100644 --- a/core/sort/sort.odin +++ b/core/sort/sort.odin @@ -684,5 +684,10 @@ compare_f64s :: proc(a, b: f64) -> int { compare_strings :: proc(a, b: string) -> int { x := transmute(mem.Raw_String)a y := transmute(mem.Raw_String)b - return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len)) + + ret := mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len)) + if ret == 0 && x.len != y.len { + return -1 if x.len < y.len else +1 + } + return ret } diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 65161a820..c2d48be7a 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -2,11 +2,13 @@ package strconv import "core:unicode/utf8" -parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) { +parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) { switch s { case "1", "t", "T", "true", "TRUE", "True": + if n != nil { n^ = len(s) } return true, true case "0", "f", "F", "false", "FALSE", "False": + if n != nil { n^ = len(s) } return false, true } return @@ -32,10 +34,13 @@ _digit_value :: proc(r: rune) -> int { // n, ok := strconv.parse_i64_of_base("-1234eeee", 10); // assert(n == -1234 && ok); // ``` -parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) { +parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) { assert(base <= 16, "base must be 1-16") s := str + + defer if n != nil { n^ = len(str)-len(s) } + if s == "" { return } @@ -87,8 +92,9 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) { // n, ok = strconv.parse_i64_maybe_prefixed("0xeeee"); // assert(n == 0xeeee && ok); // ``` -parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) { +parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) { s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -155,9 +161,10 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base} // n, ok = strconv.parse_u64_of_base("5678eeee", 16); // assert(n == 0x5678eeee && ok); // ``` -parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) { +parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) { assert(base <= 16, "base must be 1-16") s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -198,8 +205,9 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) { // n, ok = strconv.parse_u64_maybe_prefixed("0xeeee"); // assert(n == 0xeeee && ok); // ``` -parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) { +parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) { s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -259,11 +267,11 @@ parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base} // n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base // assert(n == 0xffff && ok); // ``` -parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) { +parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) { v: i64 = --- switch base { - case 0: v, ok = parse_i64_maybe_prefixed(s) - case: v, ok = parse_i64_of_base(s, base) + case 0: v, ok = parse_i64_maybe_prefixed(s, n) + case: v, ok = parse_i64_of_base(s, base, n) } value = int(v) return @@ -289,11 +297,11 @@ parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) { // n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base // assert(n == 0xffff && ok); // ``` -parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) { +parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) { v: u64 = --- switch base { - case 0: v, ok = parse_u64_maybe_prefixed(s) - case: v, ok = parse_u64_of_base(s, base) + case 0: v, ok = parse_u64_maybe_prefixed(s, n) + case: v, ok = parse_u64_of_base(s, base, n) } value = uint(v) return @@ -309,10 +317,11 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) { // n, ok := strconv.parse_i128_of_base("-1234eeee", 10); // assert(n == -1234 && ok); // ``` -parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) { +parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) { assert(base <= 16, "base must be 1-16") s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -364,8 +373,9 @@ parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) { // n, ok = strconv.parse_i128_maybe_prefixed("0xeeee"); // assert(n == 0xeeee && ok); // ``` -parse_i128_maybe_prefixed :: proc(str: string) -> (value: i128, ok: bool) { +parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) { s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -432,9 +442,10 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base} // n, ok = strconv.parse_u128_of_base("5678eeee", 16); // assert(n == 0x5678eeee && ok); // ``` -parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) { +parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) { assert(base <= 16, "base must be 1-16") s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -475,8 +486,9 @@ parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) { // n, ok = strconv.parse_u128_maybe_prefixed("0xeeee"); // assert(n == 0xeeee && ok); // ``` -parse_u128_maybe_prefixed :: proc(str: string) -> (value: u128, ok: bool) { +parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) { s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } @@ -535,9 +547,9 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base} // n, ok = strconv.parse_f32("12.34"); // assert(n == 12.34 && ok); // ``` -parse_f32 :: proc(s: string) -> (value: f32, ok: bool) { +parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) { v: f64 = --- - v, ok = parse_f64(s) + v, ok = parse_f64(s, n) return f32(v), ok } @@ -553,8 +565,9 @@ parse_f32 :: proc(s: string) -> (value: f32, ok: bool) { // n, ok = strconv.parse_f32("12.34"); // assert(n == 12.34 && ok); // ``` -parse_f64 :: proc(str: string) -> (value: f64, ok: bool) { +parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) { s := str + defer if n != nil { n^ = len(str)-len(s) } if s == "" { return } diff --git a/core/strings/intern.odin b/core/strings/intern.odin index 1e9577e61..5e9193a0d 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -31,31 +31,31 @@ intern_destroy :: proc(m: ^Intern) { // returns the `text` string from the intern map - gets set if it didnt exist yet // the returned string lives as long as the map entry lives -intern_get :: proc(m: ^Intern, text: string) -> string { - entry := _intern_get_entry(m, text) - #no_bounds_check return string(entry.str[:entry.len]) +intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) { + entry := _intern_get_entry(m, text) or_return + #no_bounds_check return string(entry.str[:entry.len]), nil } // returns the `text` cstring from the intern map - gets set if it didnt exist yet // the returned cstring lives as long as the map entry lives -intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring { - entry := _intern_get_entry(m, text) - return cstring(&entry.str[0]) +intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) { + entry := _intern_get_entry(m, text) or_return + return cstring(&entry.str[0]), nil } // looks up wether the `text` string exists in the map, returns the entry // sets & allocates the entry if it wasnt set yet -_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check { +_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check { if prev, ok := m.entries[text]; ok { - return prev + return prev, nil } if m.allocator.procedure == nil { m.allocator = context.allocator } entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1 - ptr, _ := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) - new_entry := (^Intern_Entry)(ptr) + bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return + new_entry = (^Intern_Entry)(raw_data(bytes)) new_entry.len = len(text) copy(new_entry.str[:new_entry.len], text) @@ -63,5 +63,5 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_ key := string(new_entry.str[:new_entry.len]) m.entries[key] = new_entry - return new_entry + return new_entry, nil } diff --git a/core/sync/futex_windows.odin b/core/sync/futex_windows.odin index ce662ba9e..ba6292742 100644 --- a/core/sync/futex_windows.odin +++ b/core/sync/futex_windows.odin @@ -15,18 +15,40 @@ foreign import Ntdll "system:Ntdll.lib" @(default_calling_convention="stdcall") foreign Ntdll { RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 --- + RtlNtStatusToDosError :: proc(status: i32) -> u32 --- + SetLastError :: proc(err: u32) --- } + +/* + NOTE(bill, 2022-08-17) + WaitOnAddress is implemented on top of RtlWaitOnAddress + BUT requires taking the return value of it and if it is non-zero + converting that status to a DOS error and then SetLastError + If this is not done, then things don't work as expected when + and error occurs + + GODDAMN MICROSOFT! +*/ +CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> bool { + status := RtlWaitOnAddress(Address, CompareAddress, AddressSize, Timeout) + if status != 0 { + SetLastError(RtlNtStatusToDosError(status)) + } + return status == 0 +} + + _futex_wait :: proc(f: ^Futex, expect: u32) -> bool { expect := expect - return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), nil) + return CustomWaitOnAddress(f, &expect, size_of(expect), nil) } _futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool { expect := expect // NOTE(bill): for some bizarre reason, this has be a negative number timeout := -i64(duration / 100) - return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), &timeout) + return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout) } _futex_signal :: proc(f: ^Futex) { diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin index d611e33f0..8ce3ca3cb 100644 --- a/core/sys/unix/syscalls_linux.odin +++ b/core/sys/unix/syscalls_linux.odin @@ -1568,7 +1568,7 @@ sys_gettid :: proc "contextless" () -> int { } sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int { - return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags)) + return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags)) } sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int { @@ -1622,7 +1622,7 @@ sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 { low := uintptr(offset & 0xFFFFFFFF) high := uintptr(offset >> 32) result: i64 - res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence))) + res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence))) return res if res < 0 else result } } @@ -1748,7 +1748,7 @@ sys_unlink :: proc "contextless" (path: cstring) -> int { } sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int { - return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag)) + return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flag))) } sys_rmdir :: proc "contextless" (path: cstring) -> int { diff --git a/core/sys/valgrind/callgrind.odin b/core/sys/valgrind/callgrind.odin new file mode 100644 index 000000000..1396f82ad --- /dev/null +++ b/core/sys/valgrind/callgrind.odin @@ -0,0 +1,63 @@ +//+build amd64 +package sys_valgrind + +import "core:intrinsics" + +Callgrind_Client_Request :: enum uintptr { + Dump_Stats = 'C'<<24 | 'T'<<16, + Zero_Stats, + Toggle_Collect, + Dump_Stats_At, + Start_Instrumentation, + Stop_Instrumentation, +} + +@(require_results) +callgrind_client_request_expr :: proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { + return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4) +} +callgrind_client_request_stmt :: proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) { + _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4) +} + +// Dump current state of cost centres, and zero them afterwards. +dump_stats :: proc "c" () { + callgrind_client_request_stmt(.Dump_Stats, 0, 0, 0, 0, 0) +} + +// Zero cost centres +zero_stats :: proc "c" () { + callgrind_client_request_stmt(.Zero_Stats, 0, 0, 0, 0, 0) +} + +// Toggles collection state. +// The collection state specifies whether the happening of events should be noted or +// if they are to be ignored. Events are noted by increment of counters in a cost centre. +toggle_collect :: proc "c" () { + callgrind_client_request_stmt(.Toggle_Collect, 0, 0, 0, 0, 0) +} + +// Dump current state of cost centres, and zero them afterwards. +// The argument is appended to a string stating the reason which triggered +// the dump. This string is written as a description field into the +// profile data dump. +dump_stats_at :: proc "c" (pos_str: rawptr) { + callgrind_client_request_stmt(.Dump_Stats_At, uintptr(pos_str), 0, 0, 0, 0) +} + +// Start full callgrind instrumentation if not already switched on. +// When cache simulation is done, it will flush the simulated cache; +// this will lead to an artificial cache warmup phase afterwards with +// cache misses which would not have happened in reality. +start_instrumentation :: proc "c" () { + callgrind_client_request_stmt(.Start_Instrumentation, 0, 0, 0, 0, 0) +} + +// Stop full callgrind instrumentation if not already switched off. +// This flushes Valgrinds translation cache, and does no additional instrumentation +// afterwards, which effectivly will run at the same speed as the "none" tool (ie. at minimal slowdown). +// Use this to bypass Callgrind aggregation for uninteresting code parts. +// To start Callgrind in this mode to ignore the setup phase, use the option "--instr-atstart=no". +stop_instrumentation :: proc "c" () { + callgrind_client_request_stmt(.Stop_Instrumentation, 0, 0, 0, 0, 0) +}
\ No newline at end of file diff --git a/core/sys/valgrind/memcheck.odin b/core/sys/valgrind/memcheck.odin new file mode 100644 index 000000000..99c65272a --- /dev/null +++ b/core/sys/valgrind/memcheck.odin @@ -0,0 +1,169 @@ +//+build amd64 +package sys_valgrind + +import "core:intrinsics" + +Mem_Check_Client_Request :: enum uintptr { + Make_Mem_No_Access = 'M'<<24 | 'C'<<16, + Make_Mem_Undefined, + Make_Mem_Defined, + Discard, + Check_Mem_Is_Addressable, + Check_Mem_Is_Defined, + Do_Leak_Check, + Count_Leaks, + Get_Vbits, + Set_Vbits, + Create_Block, + Make_Mem_Defined_If_Addressable, + Count_Leak_Blocks, + Enable_Addr_Error_Reporting_In_Range, + Disable_Addr_Error_Reporting_In_Range, +} + +@(require_results) +mem_check_client_request_expr :: proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { + return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4) +} +mem_check_client_request_stmt :: proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) { + _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4) +} + +// Mark memory at `raw_data(qzz)` as unaddressable for `len(qzz)` bytes. +// Returns true when run on Valgrind and false otherwise. +make_mem_no_access :: proc "c" (qzz: []byte) -> bool { + return 0 != mem_check_client_request_expr(0, .Make_Mem_No_Access, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} +// Mark memory at `raw_data(qzz)` as addressable but undefined for `len(qzz)` bytes. +// Returns true when run on Valgrind and false otherwise. +make_mem_undefined :: proc "c" (qzz: []byte) -> bool { + return 0 != mem_check_client_request_expr(0, .Make_Mem_Undefined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} +// Mark memory at `raw_data(qzz)` as addressable for `len(qzz)` bytes. +// Returns true when run on Valgrind and false otherwise. +make_mem_defined :: proc "c" (qzz: []byte) -> bool { + return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} + +// Check that memory at `raw_data(qzz)` is addressable for `len(qzz)` bytes. +// If suitable addressibility is not established, Valgrind prints an error +// message and returns the address of the first offending byte. +// Otherwise it returns zero. +check_mem_is_addressable :: proc "c" (qzz: []byte) -> uintptr { + return mem_check_client_request_expr(0, .Check_Mem_Is_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} +// Check that memory at `raw_data(qzz)` is addressable and defined for `len(qzz)` bytes. +// If suitable addressibility and definedness are not established, +// Valgrind prints an error message and returns the address of the first +// offending byte. Otherwise it returns zero. +check_mem_is_defined :: proc "c" (qzz: []byte) -> uintptr { + return mem_check_client_request_expr(0, .Check_Mem_Is_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} + +// Similar to `make_mem_defined(qzz)` except that addressability is not altered: +// bytes which are addressable are marked as defined, but those which +// are not addressable are left unchanged. +// Returns true when run on Valgrind and false otherwise. +make_mem_defined_if_addressable :: proc "c" (qzz: []byte) -> bool { + return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined_If_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} + +// Create a block-description handle. +// The description is an ascii string which is included in any messages +// pertaining to addresses within the specified memory range. +// Has no other effect on the properties of the memory range. +create_block :: proc "c" (qzz: []u8, desc: cstring) -> bool { + return 0 != mem_check_client_request_expr(0, .Create_Block, uintptr(raw_data(qzz)), uintptr(len(qzz)), uintptr(rawptr(desc)), 0, 0) +} +// Discard a block-description-handle. Returns true for an invalid handle, false for a valid handle. +discard :: proc "c" (blk_index: uintptr) -> bool { + return 0 != mem_check_client_request_expr(0, .Discard, 0, blk_index, 0, 0, 0) +} + + +// Do a full memory leak check (like `--leak-check=full`) mid-execution. +leak_check :: proc "c" () { + mem_check_client_request_stmt(.Do_Leak_Check, 0, 0, 0, 0, 0) +} +// Same as `leak_check()` but only showing the entries for which there was an increase +// in leaked bytes or leaked nr of blocks since the previous leak search. +added_leak_check :: proc "c" () { + mem_check_client_request_stmt(.Do_Leak_Check, 0, 1, 0, 0, 0) +} +// Same as `added_leak_check()` but showing entries with increased or decreased +// leaked bytes/blocks since previous leak search. +changed_leak_check :: proc "c" () { + mem_check_client_request_stmt(.Do_Leak_Check, 0, 2, 0, 0, 0) +} +// Do a summary memory leak check (like `--leak-check=summary`) mid-execution. +quick_leak_check :: proc "c" () { + mem_check_client_request_stmt(.Do_Leak_Check, 1, 0, 0, 0, 0) +} + +Count_Result :: struct { + leaked: uint, + dubious: uint, + reachable: uint, + suppressed: uint, +} + +count_leaks :: proc "c" () -> (res: Count_Result) { + mem_check_client_request_stmt( + .Count_Leaks, + uintptr(&res.leaked), + uintptr(&res.dubious), + uintptr(&res.reachable), + uintptr(&res.suppressed), + 0, + ) + return +} + +count_leak_blocks :: proc "c" () -> (res: Count_Result) { + mem_check_client_request_stmt( + .Count_Leak_Blocks, + uintptr(&res.leaked), + uintptr(&res.dubious), + uintptr(&res.reachable), + uintptr(&res.suppressed), + 0, + ) + return +} + +// Get the validity data for addresses zza and copy it +// into the provided zzvbits array. Return values: +// 0 - if not running on valgrind +// 1 - success +// 2 - [previously indicated unaligned arrays; these are now allowed] +// 3 - if any parts of zzsrc/zzvbits are not addressable. +// The metadata is not copied in cases 0, 2 or 3 so it should be +// impossible to segfault your system by using this call. +get_vbits :: proc(zza, zzvbits: []byte) -> u8 { + // assert requires a `context` thus these procedures cannot `proc "c"` + assert(len(zzvbits) >= len(zza)/8) + return u8(mem_check_client_request_expr(0, .Get_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0)) +} + +// Set the validity data for addresses zza, copying it +// from the provided zzvbits array. Return values: +// 0 - if not running on valgrind +// 1 - success +// 2 - [previously indicated unaligned arrays; these are now allowed] +// 3 - if any parts of zza/zzvbits are not addressable. +// The metadata is not copied in cases 0, 2 or 3 so it should be +// impossible to segfault your system by using this call. +set_vbits :: proc(zzvbits, zza: []byte) -> u8 { + // assert requires a `context` thus these procedures cannot `proc "c"` + assert(len(zzvbits) >= len(zza)/8) + return u8(mem_check_client_request_expr(0, .Set_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0)) +} + +// (Re-)enable reporting of addressing errors in the specified address range. +enable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr { + return mem_check_client_request_expr(0, .Enable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} +// Disable reporting of addressing errors in the specified address range. +disable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr { + return mem_check_client_request_expr(0, .Disable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +}
\ No newline at end of file diff --git a/core/sys/valgrind/valgrind.odin b/core/sys/valgrind/valgrind.odin new file mode 100644 index 000000000..2fb95588f --- /dev/null +++ b/core/sys/valgrind/valgrind.odin @@ -0,0 +1,182 @@ +//+build amd64 +package sys_valgrind + +import "core:intrinsics" + +Client_Request :: enum uintptr { + Running_On_Valgrind = 4097, + Discard_Translations = 4098, + Client_Call0 = 4353, + Client_Call1 = 4354, + Client_Call2 = 4355, + Client_Call3 = 4356, + Count_Errors = 4609, + Gdb_Monitor_Command = 4610, + Malloc_Like_Block = 4865, + Resize_Inplace_Block = 4875, + Free_Like_Block = 4866, + Create_Mem_Pool = 4867, + Destroy_Mem_Pool = 4868, + Mem_Pool_Alloc = 4869, + Mem_Pool_Free = 4870, + Mem_Pool_Trim = 4871, + Move_Mem_Pool = 4872, + Mem_Pool_Change = 4873, + Mem_Pool_Exists = 4874, + Printf = 5121, + Printf_Backtrace = 5122, + Printf_Valist_By_Ref = 5123, + Printf_Backtrace_Valist_By_Ref = 5124, + Stack_Register = 5377, + Stack_Deregister = 5378, + Stack_Change = 5379, + Load_Pdb_Debug_Info = 5633, + Map_Ip_To_Src_Loc = 5889, + Change_Err_Disablement = 6145, + Vex_Init_For_Iri = 6401, + Inner_Threads = 6402, +} + +@(require_results) +client_request_expr :: proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { + return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4) +} +client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) { + _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4) +} + +// Returns the number of Valgrinds this code is running under +// 0 - running natively +// 1 - running under Valgrind +// 2 - running under Valgrind which is running under another Valgrind +running_on_valgrind :: proc "c" () -> uintptr { + return client_request_expr(0, .Running_On_Valgrind, 0, 0, 0, 0, 0) +} + +// Discard translation of code in the slice qzz. Useful if you are debugging a JIT-er or some such, +// since it provides a way to make sure valgrind will retranslate the invalidated area. +discard_translations :: proc "c" (qzz: []byte) { + client_request_stmt(.Discard_Translations, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} + +non_simd_call0 :: proc "c" (p: proc "c" (uintptr) -> uintptr) -> uintptr { + return client_request_expr(0, .Client_Call0, uintptr(rawptr(p)), 0, 0, 0, 0) +} +non_simd_call1 :: proc "c" (p: proc "c" (uintptr, uintptr) -> uintptr, a0: uintptr) -> uintptr { + return client_request_expr(0, .Client_Call1, uintptr(rawptr(p)), a0, 0, 0, 0) +} +non_simd_call2 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr) -> uintptr, a0, a1: uintptr) -> uintptr { + return client_request_expr(0, .Client_Call2, uintptr(rawptr(p)), a0, a1, 0, 0) +} +non_simd_call3 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr, uintptr) -> uintptr, a0, a1, a2: uintptr) -> uintptr { + return client_request_expr(0, .Client_Call3, uintptr(rawptr(p)), a0, a1, a2, 0) +} + +// Counts the number of errors that have been recorded by a tool. +count_errrors :: proc "c" () -> uint { + return uint(client_request_expr(0, .Count_Errors, 0, 0, 0, 0, 0)) +} + +monitor_command :: proc "c" (command: cstring) -> bool { + return 0 != client_request_expr(0, .Gdb_Monitor_Command, uintptr(rawptr(command)), 0, 0, 0, 0) +} + + +malloc_like_block :: proc "c" (mem: []byte, rz_b: uintptr, is_zeroed: bool) { + client_request_stmt(.Malloc_Like_Block, uintptr(raw_data(mem)), uintptr(len(mem)), rz_b, uintptr(is_zeroed), 0) +} +resize_inplace_block :: proc "c" (old_mem: []byte, new_size: uint, rz_b: uintptr) { + client_request_stmt(.Resize_Inplace_Block, uintptr(raw_data(old_mem)), uintptr(len(old_mem)), uintptr(new_size), rz_b, 0) +} +free_like_block :: proc "c" (addr: rawptr, rz_b: uintptr) { + client_request_stmt(.Free_Like_Block, uintptr(addr), rz_b, 0, 0, 0) +} + +Mem_Pool_Flags :: distinct bit_set[Mem_Pool_Flag; uintptr] +Mem_Pool_Flag :: enum uintptr { + Auto_Free = 0, + Meta_Pool = 1, +} + +// Create a memory pool. +create_mem_pool :: proc "c" (pool: rawptr, rz_b: uintptr, is_zeroed: bool, flags: Mem_Pool_Flags) { + client_request_stmt(.Create_Mem_Pool, uintptr(pool), rz_b, uintptr(is_zeroed), transmute(uintptr)flags, 0) +} +// Destroy a memory pool. +destroy_mem_pool :: proc "c" (pool: rawptr) { + client_request_stmt(.Destroy_Mem_Pool, uintptr(pool), 0, 0, 0, 0) +} +// Associate a section of memory with a memory pool. +mem_pool_alloc :: proc "c" (pool: rawptr, mem: []byte) { + client_request_stmt(.Mem_Pool_Alloc, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0) +} +// Disassociate a section of memory from a memory pool. +mem_pool_free :: proc "c" (pool: rawptr, addr: rawptr) { + client_request_stmt(.Mem_Pool_Free, uintptr(pool), uintptr(addr), 0, 0, 0) +} +// Disassociate parts of a section of memory outside a particular range. +mem_pool_trim :: proc "c" (pool: rawptr, mem: []byte) { + client_request_stmt(.Mem_Pool_Trim, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0) +} +// Resize and/or move a section of memory associated with a memory pool. +move_mem_pool :: proc "c" (pool_a, pool_b: rawptr) { + client_request_stmt(.Move_Mem_Pool, uintptr(pool_a), uintptr(pool_b), 0, 0, 0) +} +// Resize and/or move a section of memory associated with a memory pool. +mem_pool_change :: proc "c" (pool: rawptr, addr_a: rawptr, mem: []byte) { + client_request_stmt(.Mem_Pool_Change, uintptr(pool), uintptr(addr_a), uintptr(raw_data(mem)), uintptr(len(mem)), 0) +} +// Return true if a memory pool exists +mem_pool_exists :: proc "c" (pool: rawptr) -> bool { + return 0 != client_request_expr(0, .Mem_Pool_Exists, uintptr(pool), 0, 0, 0, 0) +} + + +// Mark a section of memory as being a stack. Returns a stack id. +stack_register :: proc "c" (stack: []byte) -> (stack_id: uintptr) { + ptr := uintptr(raw_data(stack)) + return client_request_expr(0, .Stack_Register, ptr, ptr+uintptr(len(stack)), 0, 0, 0) +} + +// Unmark a section of memory associated with a stack id as being a stack. +stack_deregister :: proc "c" (id: uintptr) { + client_request_stmt(.Stack_Deregister, id, 0, 0, 0, 0) +} + +// Change the start and end address of the stack id with the `new_stack` slice. +stack_change :: proc "c" (id: uint, new_stack: []byte) { + ptr := uintptr(raw_data(new_stack)) + client_request_stmt(.Stack_Change, uintptr(id), ptr, ptr + uintptr(len(new_stack)), 0, 0) +} + + +// Disable error reporting for the current thread/ +// It behaves in a stack-like way, meaning you can safely call this multiple times +// given that `enable_error_reporting()` is called the same number of times to +// re-enable the error reporting. +// The first call of this macro disables reporting. +// Subsequent calls have no effect except to increase the number of `enable_error_reporting()` +// calls needed to re-enable reporting. +// Child threads do not inherit this setting from their parents; +// they are always created with reporting enabled. +disable_error_reporting :: proc "c" () { + client_request_stmt(.Change_Err_Disablement, 1, 0, 0, 0, 0) +} +// Re-enable error reporting +enable_error_reporting :: proc "c" () { + client_request_stmt(.Change_Err_Disablement, ~uintptr(0), 0, 0, 0, 0) +} + + +inner_threads :: proc "c" (qzz: rawptr) { + client_request_stmt(.Inner_Threads, uintptr(qzz), 0, 0, 0, 0) +} + + +// Map a code address to a source file name and line number. +// `buf64` must point to a 64-byte buffer in the caller's address space. +// The result will be dumped in there and is guaranteed to be zero terminated. +// If no info is found, the first byte is set to zero. +map_ip_to_src_loc :: proc "c" (addr: rawptr, buf64: ^[64]byte) -> uintptr { + return client_request_expr(0, .Map_Ip_To_Src_Loc, uintptr(addr), uintptr(buf64), 0, 0, 0) +}
\ No newline at end of file diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index 2e2a99617..d4f0d19cf 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1148,6 +1148,156 @@ foreign wasi { */ how: sdflags_t, ) -> errno_t --- + + + /** + * Return a description of the given preopened file descriptor. + */ + fd_prestat_dir_name :: proc( + fd: fd_t, + /** + * A buffer into which to write the preopened directory name. + */ + path: string, + ) -> errno_t --- + /** + * Create a directory. + * Note: This is similar to `mkdirat` in POSIX. + */ + path_create_directory :: proc( + fd: fd_t, + /** + * The path at which to create the directory. + */ + path: string, + ) -> errno_t --- + /** + * Adjust the timestamps of a file or directory. + * Note: This is similar to `utimensat` in POSIX. + */ + path_filestat_set_times :: proc( + fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + flags: lookupflags_t, + /** + * The path of the file or directory to operate on. + */ + path: string, + /** + * The desired values of the data access timestamp. + */ + atim: timestamp_t, + /** + * The desired values of the data modification timestamp. + */ + mtim: timestamp_t, + /** + * A bitmask indicating which timestamps to adjust. + */ + fst_flags: fstflags_t, + ) -> errno_t --- + /** + * Remove a directory. + * Return `errno::notempty` if the directory is not empty. + * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + */ + path_remove_directory :: proc( + fd: fd_t, + /** + * The path to a directory to remove. + */ + path: string, + ) -> errno_t --- + /** + * Create a hard link. + * Note: This is similar to `linkat` in POSIX. + */ + path_link :: proc( + old_fd: fd_t, + /** + * Flags determining the method of how the path is resolved. + */ + old_flags: lookupflags_t, + /** + * The source path from which to link. + */ + old_path: string, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path at which to create the hard link. + */ + new_path: string, + ) -> errno_t --- + + /** + * Rename a file or directory. + * Note: This is similar to `renameat` in POSIX. + */ + path_rename :: proc( + fd: fd_t, + /** + * The source path of the file or directory to rename. + */ + old_path: string, + /** + * The working directory at which the resolution of the new path starts. + */ + new_fd: fd_t, + /** + * The destination path to which to rename the file or directory. + */ + new_path: string, + ) -> errno_t --- + + /** + * Create a symbolic link. + * Note: This is similar to `symlinkat` in POSIX. + */ + path_symlink :: proc( + /** + * The contents of the symbolic link. + */ + old_path: string, + fd: fd_t, + /** + * The destination path at which to create the symbolic link. + */ + new_path: string, + ) -> errno_t --- + + /** + * Unlink a file. + * Return `errno::isdir` if the path refers to a directory. + * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + */ + path_unlink_file :: proc( + fd: fd_t, + /** + * The path to a file to unlink. + */ + path: string, + ) -> errno_t --- + + /** + * Write high-quality random data into a buffer. + * This function blocks when the implementation is unable to immediately + * provide sufficient high-quality random data. + * This function may execute slowly, so when large mounts of random data are + * required, it's advisable to use this function to seed a pseudo-random + * number generator, rather than to provide the random data directly. + */ + random_get :: proc( + /** + * The buffer to fill with random data. + */ + buf: []u8, + ) -> errno_t --- + } /** @@ -1250,7 +1400,7 @@ fd_pread :: proc "c" ( */ offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n) + err = wasi_fd_pread(fd, iovs, offset, &n) return } /** @@ -1281,7 +1431,7 @@ fd_pwrite :: proc "c" ( */ offset: filesize_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n) + err = wasi_fd_pwrite(fd, iovs, offset, &n) return } /** @@ -1297,7 +1447,7 @@ fd_read :: proc "c" ( */ iovs: []iovec_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n) + err = wasi_fd_read(fd, iovs, &n) return } /** @@ -1324,7 +1474,7 @@ fd_readdir :: proc "c" ( */ cookie: dircookie_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n) + err = wasi_fd_readdir(fd, buf, cookie, &n) return } /** @@ -1370,7 +1520,7 @@ fd_write :: proc "c" ( */ iovs: []ciovec_t, ) -> (n: size_t, err: errno_t) { - err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n) + err = wasi_fd_write(fd, iovs, &n) return } /** @@ -1390,7 +1540,7 @@ path_filestat_get :: proc "c" ( */ path: string, ) -> (offset: filestat_t, err: errno_t) { - err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset) + err = wasi_path_filestat_get(fd, flags, path, &offset) return } /** @@ -1432,7 +1582,7 @@ path_open :: proc "c" ( fs_rights_inheriting: rights_t, fdflags: fdflags_t, ) -> (file: fd_t, err: errno_t) { - err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) + err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file) return } /** @@ -1452,7 +1602,7 @@ path_readlink :: proc "c" ( */ buf: []u8, ) -> (n: size_t, err: errno_t) { - err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n) + err = wasi_path_readlink(fd, path, buf, &n) return } /** @@ -1495,7 +1645,7 @@ sock_recv :: proc "c" ( */ ri_flags: riflags_t, ) -> (n: size_t, flags: roflags_t, err: errno_t) { - err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags) + err = wasi_sock_recv(fd, ri_data, ri_flags, &n, &flags) return } /** @@ -1516,172 +1666,11 @@ sock_send :: proc "c" ( */ si_flags: siflags_t, ) -> (n: size_t, err: errno_t) { - err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n) + err = wasi_sock_send(fd, si_data, si_flags, &n) return } -/** - * Return a description of the given preopened file descriptor. - */ -fd_prestat_dir_name :: proc( - fd: fd_t, - /** - * A buffer into which to write the preopened directory name. - */ - path: string, -) -> errno_t { - return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path)) -} -/** - * Create a directory. - * Note: This is similar to `mkdirat` in POSIX. - */ -path_create_directory :: proc( - fd: fd_t, - /** - * The path at which to create the directory. - */ - path: string, -) -> errno_t { - return wasm_path_create_directory(fd, raw_data(path), len(path)) -} -/** - * Adjust the timestamps of a file or directory. - * Note: This is similar to `utimensat` in POSIX. - */ -path_filestat_set_times :: proc( - fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - flags: lookupflags_t, - /** - * The path of the file or directory to operate on. - */ - path: string, - /** - * The desired values of the data access timestamp. - */ - atim: timestamp_t, - /** - * The desired values of the data modification timestamp. - */ - mtim: timestamp_t, - /** - * A bitmask indicating which timestamps to adjust. - */ - fst_flags: fstflags_t, -) -> errno_t { - return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags) -} -/** - * Remove a directory. - * Return `errno::notempty` if the directory is not empty. - * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - */ -path_remove_directory :: proc( - fd: fd_t, - /** - * The path to a directory to remove. - */ - path: string, -) -> errno_t { - return wasm_path_remove_directory(fd, raw_data(path), len(path)) -} -/** - * Create a hard link. - * Note: This is similar to `linkat` in POSIX. - */ -path_link :: proc( - old_fd: fd_t, - /** - * Flags determining the method of how the path is resolved. - */ - old_flags: lookupflags_t, - /** - * The source path from which to link. - */ - old_path: string, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path at which to create the hard link. - */ - new_path: string, -) -> errno_t { - return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path)) -} -/** - * Rename a file or directory. - * Note: This is similar to `renameat` in POSIX. - */ -path_rename :: proc( - fd: fd_t, - /** - * The source path of the file or directory to rename. - */ - old_path: string, - /** - * The working directory at which the resolution of the new path starts. - */ - new_fd: fd_t, - /** - * The destination path to which to rename the file or directory. - */ - new_path: string, -) -> errno_t { - return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path)) -} -/** - * Create a symbolic link. - * Note: This is similar to `symlinkat` in POSIX. - */ -path_symlink :: proc( - /** - * The contents of the symbolic link. - */ - old_path: string, - fd: fd_t, - /** - * The destination path at which to create the symbolic link. - */ - new_path: string, -) -> errno_t { - return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path)) -} -/** - * Unlink a file. - * Return `errno::isdir` if the path refers to a directory. - * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - */ -path_unlink_file :: proc( - fd: fd_t, - /** - * The path to a file to unlink. - */ - path: string, -) -> errno_t { - return wasm_path_unlink_file(fd, raw_data(path), len(path)) -} -/** - * Write high-quality random data into a buffer. - * This function blocks when the implementation is unable to immediately - * provide sufficient high-quality random data. - * This function may execute slowly, so when large mounts of random data are - * required, it's advisable to use this function to seed a pseudo-random - * number generator, rather than to provide the random data directly. - */ -random_get :: proc( - /** - * The buffer to fill with random data. - */ - buf: []u8, -) -> errno_t { - return wasm_random_get(raw_data(buf), len(buf)) -} @@ -1722,8 +1711,7 @@ foreign wasi { @(link_name="fd_pread") wasi_fd_pread :: proc( fd: fd_t, - iovs: [^]iovec_t, - iovs_len: size_t, + iovs: []iovec_t, offset: filesize_t, retptr0: ^size_t, ) -> errno_t --- @@ -1735,23 +1723,20 @@ foreign wasi { @(link_name="fd_pwrite") wasi_fd_pwrite :: proc( fd: fd_t, - iovs: [^]ciovec_t, - iovs_len: size_t, + iovs: []ciovec_t, offset: filesize_t, retptr0: ^size_t, ) -> errno_t --- @(link_name="fd_read") wasi_fd_read :: proc( fd: fd_t, - iovs: [^]iovec_t, - iovs_len: size_t, + iovs: []iovec_t, retptr0: ^size_t, ) -> errno_t --- @(link_name="fd_readdir") wasi_fd_readdir :: proc( fd: fd_t, - buf: [^]u8, - buf_len: size_t, + buf: []u8, cookie: dircookie_t, retptr0: ^size_t, ) -> errno_t --- @@ -1770,8 +1755,7 @@ foreign wasi { @(link_name="fd_write") wasi_fd_write :: proc( fd: fd_t, - iovs: [^]ciovec_t, - iovs_len: size_t, + iovs: []ciovec_t, retptr0: ^size_t, ) -> errno_t --- @(link_name="path_filestat_get") @@ -1781,16 +1765,14 @@ foreign wasi { /** * The path of the file or directory to inspect. */ - path: [^]u8, - path_len: size_t, + path: string, retptr0: ^filestat_t, ) -> errno_t --- @(link_name="path_open") wasi_path_open :: proc( fd: fd_t, dirflags: lookupflags_t, - path: [^]u8, - path_len: size_t, + path: string, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, @@ -1800,10 +1782,8 @@ foreign wasi { @(link_name="path_readlink") wasi_path_readlink :: proc( fd: fd_t, - path: [^]u8, - path_len: size_t, - buf: [^]u8, - buf_len: size_t, + path: string, + buf: []u8, retptr0: ^size_t, ) -> errno_t --- @(link_name="poll_oneoff") @@ -1816,8 +1796,7 @@ foreign wasi { @(link_name="sock_recv") wasi_sock_recv :: proc( fd: fd_t, - ri_data: [^]iovec_t, - ri_data_len: size_t, + ri_data: []iovec_t, ri_flags: riflags_t, retptr0: ^size_t, retptr1: ^roflags_t, @@ -1825,75 +1804,8 @@ foreign wasi { @(link_name="sock_send") wasi_sock_send :: proc( fd: fd_t, - si_data: [^]ciovec_t, - si_data_len: size_t, + si_data: []ciovec_t, si_flags: siflags_t, retptr0: ^size_t, ) -> errno_t --- - @(link_name="fd_prestat_dir_name") - wasm_fd_prestat_dir_name :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="path_create_directory") - wasm_path_create_directory :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="path_filestat_set_times") - wasm_path_filestat_set_times :: proc( - fd: fd_t, - flags: lookupflags_t, - path: [^]u8, - path_len: size_t, - atim: timestamp_t, - mtim: timestamp_t, - fst_flags: fstflags_t, - ) -> errno_t --- - @(link_name="path_remove_directory") - wasm_path_remove_directory :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="path_link") - wasm_path_link :: proc( - old_fd: fd_t, - old_flags: lookupflags_t, - old_path: [^]u8, - old_path_len: size_t, - new_fd: fd_t, - new_path: [^]u8, - new_path_len: size_t, - ) -> errno_t --- - @(link_name="path_rename") - wasm_path_rename :: proc( - fd: fd_t, - old_path: [^]u8, - old_path_len: size_t, - new_fd: fd_t, - new_path: [^]u8, - new_path_len: size_t, - ) -> errno_t --- - @(link_name="path_symlink") - wasm_path_symlink :: proc( - old_path: [^]u8, - old_path_len: size_t, - fd: fd_t, - new_path: [^]u8, - new_path_len: size_t, - ) -> errno_t --- - @(link_name="path_unlink_file") - wasm_path_unlink_file :: proc( - fd: fd_t, - path: [^]u8, - path_len: size_t, - ) -> errno_t --- - @(link_name="random_get") - wasm_random_get :: proc( - buf: [^]u8, - buf_len: size_t, - ) -> errno_t --- } diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 284936852..f055b6908 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -290,10 +290,10 @@ foreign kernel32 { InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) --- AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) --- - TryAcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) -> BOOL --- + TryAcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) -> BOOLEAN --- ReleaseSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) --- AcquireSRWLockShared :: proc(SRWLock: ^SRWLOCK) --- - TryAcquireSRWLockShared :: proc(SRWLock: ^SRWLOCK) -> BOOL --- + TryAcquireSRWLockShared :: proc(SRWLock: ^SRWLOCK) -> BOOLEAN --- ReleaseSRWLockShared :: proc(SRWLock: ^SRWLOCK) --- InitializeConditionVariable :: proc(ConditionVariable: ^CONDITION_VARIABLE) --- diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 47de354b6..4b3d3e68e 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -18,6 +18,7 @@ foreign user32 { RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM --- RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM --- + UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL --- CreateWindowExW :: proc( dwExStyle: DWORD, @@ -41,6 +42,7 @@ foreign user32 { GetTopWindow :: proc(hWnd: HWND) -> HWND --- SetForegroundWindow :: proc(hWnd: HWND) -> BOOL --- GetForegroundWindow :: proc() -> HWND --- + UpdateWindow :: proc(hWnd: HWND) -> BOOL --- SetActiveWindow :: proc(hWnd: HWND) -> HWND --- GetActiveWindow :: proc() -> HWND --- @@ -94,6 +96,7 @@ foreign user32 { GetSystemMetrics :: proc(nIndex: c_int) -> c_int --- AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL --- AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL --- + AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL --- SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL --- @@ -136,7 +139,19 @@ foreign user32 { SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR --- EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL --- - + + MonitorFromPoint :: proc(pt: POINT, dwFlags: Monitor_From_Flags) -> HMONITOR --- + MonitorFromRect :: proc(lprc: LPRECT, dwFlags: Monitor_From_Flags) -> HMONITOR --- + MonitorFromWindow :: proc(hwnd: HWND, dwFlags: Monitor_From_Flags) -> HMONITOR --- + EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL --- + + SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT --- + GetThreadDpiAwarenessContext :: proc() -> DPI_AWARENESS_CONTEXT --- + GetWindowDpiAwarenessContext :: proc(hwnd: HWND) -> DPI_AWARENESS_CONTEXT --- + GetDpiFromDpiAwarenessContext :: proc(value: DPI_AWARENESS_CONTEXT) -> UINT --- + GetDpiForWindow :: proc(hwnd: HWND) -> UINT --- + SetProcessDpiAwarenessContext :: proc(value: DPI_AWARENESS_CONTEXT) -> BOOL --- + BroadcastSystemMessageW :: proc( flags: DWORD, lpInfo: LPDWORD, @@ -220,7 +235,7 @@ when ODIN_ARCH == .amd64 { SetClassLongPtrW :: SetClassLongW GetWindowLongPtrW :: GetWindowLongW - SetWindowLongPtrW :: GetWindowLongW + SetWindowLongPtrW :: SetWindowLongW } GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int { @@ -246,3 +261,19 @@ GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR { return cast(LPWSTR)uintptr(WORD(i)) } + +Monitor_From_Flags :: enum DWORD { + MONITOR_DEFAULTTONULL = 0x00000000, // Returns NULL + MONITOR_DEFAULTTOPRIMARY = 0x00000001, // Returns a handle to the primary display monitor + MONITOR_DEFAULTTONEAREST = 0x00000002, // Returns a handle to the display monitor that is nearest to the window +} + +Monitor_Enum_Proc :: #type proc "stdcall" (HMONITOR, HDC, LPRECT, LPARAM) -> BOOL + +USER_DEFAULT_SCREEN_DPI :: 96 +DPI_AWARENESS_CONTEXT :: distinct HANDLE +DPI_AWARENESS_CONTEXT_UNAWARE :: DPI_AWARENESS_CONTEXT(~uintptr(0)) // -1 +DPI_AWARENESS_CONTEXT_SYSTEM_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(1)) // -2 +DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(2)) // -3 +DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 :: DPI_AWARENESS_CONTEXT(~uintptr(3)) // -4 +DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED :: DPI_AWARENESS_CONTEXT(~uintptr(4)) // -5 diff --git a/core/sys/windows/wgl.odin b/core/sys/windows/wgl.odin index 689a41dea..150358d01 100644 --- a/core/sys/windows/wgl.odin +++ b/core/sys/windows/wgl.odin @@ -62,7 +62,7 @@ GetExtensionsStringARBType :: #type proc "c" (HDC) -> cstring // Procedures wglCreateContextAttribsARB: CreateContextAttribsARBType wglChoosePixelFormatARB: ChoosePixelFormatARBType - wglSwapIntervalExt: SwapIntervalEXTType + wglSwapIntervalEXT: SwapIntervalEXTType wglGetExtensionsStringARB: GetExtensionsStringARBType diff --git a/core/sys/windows/wglext.odin b/core/sys/windows/wglext.odin new file mode 100644 index 000000000..0c4b51d65 --- /dev/null +++ b/core/sys/windows/wglext.odin @@ -0,0 +1,147 @@ +// +build windows
+package sys_windows
+
+// WGL_ARB_buffer_region
+WGL_FRONT_COLOR_BUFFER_BIT_ARB :: 0x00000001
+WGL_BACK_COLOR_BUFFER_BIT_ARB :: 0x00000002
+WGL_DEPTH_BUFFER_BIT_ARB :: 0x00000004
+WGL_STENCIL_BUFFER_BIT_ARB :: 0x00000008
+
+wglCreateBufferRegionARBType :: #type proc "c" (hDC: HDC, iLayerPlane: c_int, uType: UINT) -> HANDLE
+wglDeleteBufferRegionARBType :: #type proc "c" (hRegion: HANDLE)
+wglSaveBufferRegionARBType :: #type proc "c" (hRegion: HANDLE, x: c_int, y: c_int, width: c_int, height: c_int) -> BOOL
+wglRestoreBufferRegionARBType :: #type proc "c" (hRegion: HANDLE, x: c_int, y: c_int, width: c_int, height: c_int, xSrc: c_int, ySrc: c_int) -> BOOL
+
+// wglCreateBufferRegionARB: wglCreateBufferRegionARBType
+// wglDeleteBufferRegionARB: wglDeleteBufferRegionARBType
+// wglSaveBufferRegionARB: wglSaveBufferRegionARBType
+// wglRestoreBufferRegionARB: wglRestoreBufferRegionARBType
+
+// WGL_ARB_context_flush_control
+WGL_CONTEXT_RELEASE_BEHAVIOR_ARB :: 0x2097
+WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB :: 0
+WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB :: 0x2098
+
+// WGL_ARB_create_context
+WGL_CONTEXT_DEBUG_BIT_ARB :: 0x0001
+WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002
+WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091
+WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092
+WGL_CONTEXT_LAYER_PLANE_ARB :: 0x2093
+WGL_CONTEXT_FLAGS_ARB :: 0x2094
+ERROR_INVALID_VERSION_ARB :: 0x2095
+
+// WGL_ARB_create_context_no_error
+WGL_CONTEXT_OPENGL_NO_ERROR_ARB :: 0x31B3
+
+// WGL_ARB_create_context_profile
+WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126
+WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001
+WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002
+ERROR_INVALID_PROFILE_ARB :: 0x2096
+
+// WGL_ARB_create_context_robustness
+WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB :: 0x00000004
+WGL_LOSE_CONTEXT_ON_RESET_ARB :: 0x8252
+WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB :: 0x8256
+WGL_NO_RESET_NOTIFICATION_ARB :: 0x8261
+
+// WGL_ARB_framebuffer_sRGB
+WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB :: 0x20A9
+
+// WGL_ARB_make_current_read
+ERROR_INVALID_PIXEL_TYPE_ARB :: 0x2043
+ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB :: 0x2054
+
+wglMakeContextCurrentARBType :: #type proc "c" (hDrawDC: HDC, hReadDC:HDC, hglrc: HGLRC) -> BOOL
+wglGetCurrentReadDCARBType :: #type proc "c" () -> HDC
+
+// wglMakeContextCurrentARB: wglMakeContextCurrentARBType
+// wglGetCurrentReadDCARB: wglGetCurrentReadDCARBType
+
+// WGL_ARB_multisample
+WGL_SAMPLE_BUFFERS_ARB :: 0x2041
+WGL_SAMPLES_ARB :: 0x2042
+
+// WGL_ARB_pbuffer
+HPBUFFERARB :: distinct rawptr
+WGL_DRAW_TO_PBUFFER_ARB :: 0x202D
+WGL_MAX_PBUFFER_PIXELS_ARB :: 0x202E
+WGL_MAX_PBUFFER_WIDTH_ARB :: 0x202F
+WGL_MAX_PBUFFER_HEIGHT_ARB :: 0x2030
+WGL_PBUFFER_LARGEST_ARB :: 0x2033
+WGL_PBUFFER_WIDTH_ARB :: 0x2034
+WGL_PBUFFER_HEIGHT_ARB :: 0x2035
+WGL_PBUFFER_LOST_ARB :: 0x2036
+
+wglCreatePbufferARBType :: #type proc "c" (hDC: HDC, iPixelFormat, iWidth, iHeight: c_int, piAttribList: [^]c_int) -> HPBUFFERARB
+wglGetPbufferDCARBType :: #type proc "c" (hPbuffer: HPBUFFERARB) -> HDC
+wglReleasePbufferDCARBType :: #type proc "c" (hPbuffer: HPBUFFERARB, hDC: HDC) -> c_int
+wglDestroyPbufferARBType :: #type proc "c" (hPbuffer: HPBUFFERARB) -> BOOL
+wglQueryPbufferARBType :: #type proc "c" (hPbuffer: HPBUFFERARB, iAttribute: c_int, piValue: ^c_int) -> BOOL
+
+// wglCreatePbufferARB: wglCreatePbufferARBType
+// wglGetPbufferDCARB: wglGetPbufferDCARBType
+// wglReleasePbufferDCARB: wglReleasePbufferDCARBType
+// wglDestroyPbufferARB: wglDestroyPbufferARBType
+// wglQueryPbufferARB: wglQueryPbufferARBType
+
+// WGL_ARB_pixel_format
+WGL_NUMBER_PIXEL_FORMATS_ARB :: 0x2000
+WGL_DRAW_TO_WINDOW_ARB :: 0x2001
+WGL_DRAW_TO_BITMAP_ARB :: 0x2002
+WGL_ACCELERATION_ARB :: 0x2003
+WGL_NEED_PALETTE_ARB :: 0x2004
+WGL_NEED_SYSTEM_PALETTE_ARB :: 0x2005
+WGL_SWAP_LAYER_BUFFERS_ARB :: 0x2006
+WGL_SWAP_METHOD_ARB :: 0x2007
+WGL_NUMBER_OVERLAYS_ARB :: 0x2008
+WGL_NUMBER_UNDERLAYS_ARB :: 0x2009
+WGL_TRANSPARENT_ARB :: 0x200A
+WGL_TRANSPARENT_RED_VALUE_ARB :: 0x2037
+WGL_TRANSPARENT_GREEN_VALUE_ARB :: 0x2038
+WGL_TRANSPARENT_BLUE_VALUE_ARB :: 0x2039
+WGL_TRANSPARENT_ALPHA_VALUE_ARB :: 0x203A
+WGL_TRANSPARENT_INDEX_VALUE_ARB :: 0x203B
+WGL_SHARE_DEPTH_ARB :: 0x200C
+WGL_SHARE_STENCIL_ARB :: 0x200D
+WGL_SHARE_ACCUM_ARB :: 0x200E
+WGL_SUPPORT_GDI_ARB :: 0x200F
+WGL_SUPPORT_OPENGL_ARB :: 0x2010
+WGL_DOUBLE_BUFFER_ARB :: 0x2011
+WGL_STEREO_ARB :: 0x2012
+WGL_PIXEL_TYPE_ARB :: 0x2013
+WGL_COLOR_BITS_ARB :: 0x2014
+WGL_RED_BITS_ARB :: 0x2015
+WGL_RED_SHIFT_ARB :: 0x2016
+WGL_GREEN_BITS_ARB :: 0x2017
+WGL_GREEN_SHIFT_ARB :: 0x2018
+WGL_BLUE_BITS_ARB :: 0x2019
+WGL_BLUE_SHIFT_ARB :: 0x201A
+WGL_ALPHA_BITS_ARB :: 0x201B
+WGL_ALPHA_SHIFT_ARB :: 0x201C
+WGL_ACCUM_BITS_ARB :: 0x201D
+WGL_ACCUM_RED_BITS_ARB :: 0x201E
+WGL_ACCUM_GREEN_BITS_ARB :: 0x201F
+WGL_ACCUM_BLUE_BITS_ARB :: 0x2020
+WGL_ACCUM_ALPHA_BITS_ARB :: 0x2021
+WGL_DEPTH_BITS_ARB :: 0x2022
+WGL_STENCIL_BITS_ARB :: 0x2023
+WGL_AUX_BUFFERS_ARB :: 0x2024
+WGL_NO_ACCELERATION_ARB :: 0x2025
+WGL_GENERIC_ACCELERATION_ARB :: 0x2026
+WGL_FULL_ACCELERATION_ARB :: 0x2027
+WGL_SWAP_EXCHANGE_ARB :: 0x2028
+WGL_SWAP_COPY_ARB :: 0x2029
+WGL_SWAP_UNDEFINED_ARB :: 0x202A
+WGL_TYPE_RGBA_ARB :: 0x202B
+WGL_TYPE_COLORINDEX_ARB :: 0x202C
+
+wglGetPixelFormatAttribivARBType :: #type proc "c" (hdc: HDC, iPixelFormat, iLayerPlane: c_int, nAttributes: UINT, piAttributes: [^]c_int, piValues: [^]c_int) -> BOOL
+wglGetPixelFormatAttribfvARBType :: #type proc "c" (hdc: HDC, iPixelFormat, iLayerPlane: c_int, nAttributes: UINT, piAttributes: [^]c_int, pfValues: [^]f32) -> BOOL
+
+// wglGetPixelFormatAttribivARB: wglGetPixelFormatAttribivARBType
+// wglGetPixelFormatAttribfvARB: wglGetPixelFormatAttribfvARBType
+
+// WGL_ARB_pixel_format_float
+WGL_TYPE_RGBA_FLOAT_ARB :: 0x21A0
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin index d6b883916..7ce9e545c 100644 --- a/core/sys/windows/window_messages.odin +++ b/core/sys/windows/window_messages.odin @@ -153,6 +153,7 @@ BM_CLICK :: 0x00f5 BM_GETIMAGE :: 0x00f6 BM_SETIMAGE :: 0x00f7 BM_SETDONTCLICK :: 0x00f8 +WM_INPUT_DEVICE_CHANGE :: 0x00fe WM_INPUT :: 0x00ff WM_KEYDOWN :: 0x0100 WM_KEYFIRST :: 0x0100 @@ -165,6 +166,7 @@ WM_SYSCHAR :: 0x0106 WM_SYSDEADCHAR :: 0x0107 WM_UNICHAR :: 0x0109 WM_KEYLAST :: 0x0109 +UNICODE_NOCHAR :: 0xFFFF WM_WNT_CONVERTREQUESTEX :: 0x0109 WM_CONVERTREQUEST :: 0x010a WM_CONVERTRESULT :: 0x010b @@ -279,6 +281,27 @@ WM_ENTERSIZEMOVE :: 0x0231 WM_EXITSIZEMOVE :: 0x0232 WM_DROPFILES :: 0x0233 WM_MDIREFRESHMENU :: 0x0234 +WM_POINTERDEVICECHANGE :: 0x0238 +WM_POINTERDEVICEINRANGE :: 0x0239 +WM_POINTERDEVICEOUTOFRANGE :: 0x023a +WM_TOUCH :: 0x0240 +WM_NCPOINTERUPDATE :: 0x0241 +WM_NCPOINTERDOWN :: 0x0242 +WM_NCPOINTERUP :: 0x0243 +WM_POINTERUPDATE :: 0x0245 +WM_POINTERDOWN :: 0x0246 +WM_POINTERUP :: 0x0247 +WM_POINTERENTER :: 0x0249 +WM_POINTERLEAVE :: 0x024a +WM_POINTERACTIVATE :: 0x024b +WM_POINTERCAPTURECHANGED :: 0x024c +WM_TOUCHHITTESTING :: 0x024d +WM_POINTERWHEEL :: 0x024e +WM_POINTERHWHEEL :: 0x024f +DM_POINTERHITTEST :: 0x0250 +WM_POINTERROUTEDTO :: 0x0251 +WM_POINTERROUTEDAWAY :: 0x0252 +WM_POINTERROUTEDRELEASED :: 0x0253 WM_IME_REPORT :: 0x0280 WM_IME_SETCONTEXT :: 0x0281 WM_IME_NOTIFY :: 0x0282 @@ -295,6 +318,13 @@ WM_NCMOUSEHOVER :: 0x02a0 WM_MOUSEHOVER :: 0x02a1 WM_NCMOUSELEAVE :: 0x02a2 WM_MOUSELEAVE :: 0x02a3 +WM_WTSSESSION_CHANGE :: 0x02b1 +WM_TABLET_FIRST :: 0x02c0 +WM_TABLET_LAST :: 0x02df +WM_DPICHANGED :: 0x02e0 +WM_DPICHANGED_BEFOREPARENT :: 0x02e2 +WM_DPICHANGED_AFTERPARENT :: 0x02e3 +WM_GETDPISCALEDSIZE :: 0x02e4 WM_CUT :: 0x0300 WM_COPY :: 0x0301 WM_PASTE :: 0x0302 @@ -317,6 +347,15 @@ WM_HOTKEY :: 0x0312 WM_PRINT :: 0x0317 WM_PRINTCLIENT :: 0x0318 WM_APPCOMMAND :: 0x0319 +WM_THEMECHANGED :: 0x031A +WM_CLIPBOARDUPDATE :: 0x031D +WM_DWMCOMPOSITIONCHANGED :: 0x031E +WM_DWMNCRENDERINGCHANGED :: 0x031F +WM_DWMCOLORIZATIONCOLORCHANGED:: 0x0320 +WM_DWMWINDOWMAXIMIZEDCHANGE :: 0x0321 +WM_DWMSENDICONICTHUMBNAIL :: 0x0323 +WM_DWMSENDICONICLIVEPREVIEWBITMAP :: 0x0326 +WM_GETTITLEBARINFOEX :: 0x033F WM_HANDHELDFIRST :: 0x0358 WM_HANDHELDLAST :: 0x035f WM_AFXFIRST :: 0x0360 diff --git a/core/text/i18n/gettext.odin b/core/text/i18n/gettext.odin index d99ec1c9b..d5537a19c 100644 --- a/core/text/i18n/gettext.odin +++ b/core/text/i18n/gettext.odin @@ -99,14 +99,14 @@ parse_mo_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, plur } for k in keys { - interned_key := strings.intern_get(&translation.intern, string(k)) + interned_key, _ := strings.intern_get(&translation.intern, string(k)) interned_vals := make([]string, len(keys)) last_val: string i := 0 for v in vals { - interned_vals[i] = strings.intern_get(&translation.intern, string(v)) + interned_vals[i], _ = strings.intern_get(&translation.intern, string(v)) last_val = interned_vals[i] i += 1 } diff --git a/core/text/i18n/qt_linguist.odin b/core/text/i18n/qt_linguist.odin index 036a89eeb..e7c1f9974 100644 --- a/core/text/i18n/qt_linguist.odin +++ b/core/text/i18n/qt_linguist.odin @@ -59,9 +59,9 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI return translation, .TS_File_Expected_Context_Name, } - section_name := strings.intern_get(&translation.intern, "") + section_name, _ := strings.intern_get(&translation.intern, "") if !options.merge_sections { - section_name = strings.intern_get(&translation.intern, ts.elements[section_name_id].value) + section_name, _ = strings.intern_get(&translation.intern, ts.elements[section_name_id].value) } if section_name not_in translation.k_v { @@ -92,8 +92,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI return translation, .TS_File_Expected_Translation } - source := strings.intern_get(&translation.intern, ts.elements[source_id].value) - xlat := strings.intern_get(&translation.intern, ts.elements[translation_id].value) + source, _ := strings.intern_get(&translation.intern, ts.elements[source_id].value) + xlat, _ := strings.intern_get(&translation.intern, ts.elements[translation_id].value) if source in section { return translation, .Duplicate_Key @@ -124,7 +124,7 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI if !numerus_found { break } - numerus := strings.intern_get(&translation.intern, ts.elements[numerus_id].value) + numerus, _ := strings.intern_get(&translation.intern, ts.elements[numerus_id].value) section[source][num_plurals] = numerus num_plurals += 1 |