diff --git a/build.bat b/build.bat index 06bc6c823..e08bfc04b 100644 --- a/build.bat +++ b/build.bat @@ -72,10 +72,12 @@ del *.ilk > NUL 2> NUL cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name% if %errorlevel% neq 0 goto end_of_build -call build_vendor.bat -if %errorlevel% neq 0 goto end_of_build +rem call build_vendor.bat +rem if %errorlevel% neq 0 goto end_of_build -if %release_mode% EQU 0 odin run examples/demo +odin run examples/demo -vet -strict-style +rem odin check examples/all +rem odin run examples/bug del *.obj > NUL 2> NUL diff --git a/build_odin.sh b/build_odin.sh index b8cd09c54..62d8a0f59 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -50,7 +50,19 @@ config_darwin() { } config_freebsd() { - : ${LLVM_CONFIG=/usr/local/bin/llvm-config11} + : ${LLVM_CONFIG=} + + if [ ! "$LLVM_CONFIG" ]; then + if which llvm-config11 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config11 + elif which llvm-config12 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config12 + elif which llvm-config13 > /dev/null 2>&1; then + LLVM_CONFIG=llvm-config13 + else + panic "Unable to find LLVM-config" + fi + fi CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)" diff --git a/core/compress/common.odin b/core/compress/common.odin index f4e378269..b42cbefff 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -294,6 +294,24 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid } } +@(optimization_mode="speed") +peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) { + size :: size_of(T) + + #no_bounds_check { + if len(z.input_data) >= size + offset { + buf := z.input_data[offset:][:size] + return (^T)(&buf[0])^, .None + } + } + + if len(z.input_data) == 0 { + return T{}, .EOF + } else { + return T{}, .Short_Buffer + } +} + @(optimization_mode="speed") peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) { size :: size_of(T) @@ -321,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid return res, .None } -peek_data :: proc{peek_data_from_memory, peek_data_from_stream} +@(optimization_mode="speed") +peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) { + size :: size_of(T) + + // Get current position to return to. + cur_pos, e1 := z.input->impl_seek(0, .Current) + if e1 != .None { + return T{}, e1 + } + + // Seek to offset. + pos, e2 := z.input->impl_seek(offset, .Start) + if e2 != .None { + return T{}, e2 + } + + r, e3 := io.to_reader_at(z.input) + if !e3 { + return T{}, .Empty + } + when size <= 128 { + b: [size]u8 + } else { + b := make([]u8, size, context.temp_allocator) + } + _, e4 := io.read_at(r, b[:], pos) + if e4 != .None { + return T{}, .Empty + } + + // Return read head to original position. + z.input->impl_seek(cur_pos, .Start) + + res = (^T)(&b[0])^ + return res, .None +} + +peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream} diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin index ae1ca9f62..b3a8ad43f 100644 --- a/core/container/queue/queue.odin +++ b/core/container/queue/queue.odin @@ -73,11 +73,18 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T { front :: proc(q: ^$Q/Queue($T)) -> T { return q.data[q.offset] } +front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { + return &q.data[q.offset] +} back :: proc(q: ^$Q/Queue($T)) -> T { idx := (q.offset+uint(q.len))%builtin.len(q.data) return q.data[idx] } +back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { + idx := (q.offset+uint(q.len))%builtin.len(q.data) + return &q.data[idx] +} set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) { runtime.bounds_check_error_loc(loc, i, builtin.len(q.data)) @@ -92,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^ return &q.data[idx] } +peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T { + runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data)) + idx := q.offset%builtin.len(q.data) + return &q.data[idx] +} + +peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T { + runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data)) + idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data) + return &q.data[idx] +} + // Push an element to the back of the queue push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool { if space(q^) == 0 { diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 8cc814fcf..8f7749aba 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -85,6 +85,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: 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) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index e68280bfe..7429a093d 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2059,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.. 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 31f622d2b..58ff83dd1 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -46,7 +46,7 @@ Image :: struct { height: int, channels: int, depth: int, // Channel depth in bits, typically 8 or 16 - pixels: bytes.Buffer, + pixels: bytes.Buffer `fmt:"-"`, /* Some image loaders/writers can return/take an optional background color. For convenience, we return them as u16 so we don't need to switch on the type @@ -61,6 +61,7 @@ Image_Metadata :: union #shared_nil { ^Netpbm_Info, ^PNG_Info, ^QOI_Info, + ^TGA_Info, } @@ -168,6 +169,7 @@ Error :: union #shared_nil { General_Image_Error :: enum { None = 0, + Unsupported_Option, // File I/O Unable_To_Read_File, Unable_To_Write_File, @@ -376,10 +378,20 @@ QOI_Info :: struct { header: QOI_Header, } +TGA_Data_Type :: enum u8 { + No_Image_Data = 0, + Uncompressed_Color_Mapped = 1, + Uncompressed_RGB = 2, + Uncompressed_Black_White = 3, + Compressed_Color_Mapped = 9, + Compressed_RGB = 10, + Compressed_Black_White = 11, +} + TGA_Header :: struct #packed { id_length: u8, color_map_type: u8, - data_type_code: u8, + data_type_code: TGA_Data_Type, color_map_origin: u16le, color_map_length: u16le, color_map_depth: u8, @@ -390,6 +402,52 @@ TGA_Header :: struct #packed { } #assert(size_of(TGA_Header) == 18) +New_TGA_Signature :: "TRUEVISION-XFILE.\x00" + +TGA_Footer :: struct #packed { + extension_area_offset: u32le, + developer_directory_offset: u32le, + signature: [18]u8 `fmt:"s,0"`, // Should match signature if New TGA. +} +#assert(size_of(TGA_Footer) == 26) + +TGA_Extension :: struct #packed { + extension_size: u16le, // Size of this struct. If not 495 bytes it means it's an unsupported version. + author_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated + author_comments: [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated. + datetime: struct {month, day, year, hour, minute, second: u16le}, + job_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated + job_time: struct {hour, minute, second: u16le}, + software_id: [41]u8 `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated + software_version: struct #packed { + number: u16le, // Version number * 100 + letter: u8 `fmt:"r"`, // " " if not used + }, + key_color: [4]u8, // ARGB key color used at time of production + aspect_ratio: [2]u16le, // Numerator / Denominator + gamma: [2]u16le, // Numerator / Denominator, range should be 0.0..10.0 + color_correction_offset: u32le, // 0 if no color correction information + postage_stamp_offset: u32le, // 0 if no thumbnail + scanline_offset: u32le, // 0 if no scanline table + attributes: TGA_Alpha_Kind, +} +#assert(size_of(TGA_Extension) == 495) + +TGA_Alpha_Kind :: enum u8 { + None, + Undefined_Ignore, + Undefined_Retain, + Useful, + Premultiplied, +} + +TGA_Info :: struct { + header: TGA_Header, + image_id: string, + footer: Maybe(TGA_Footer), + extension: Maybe(TGA_Extension), +} + // Function to help with image buffer calculations compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) { size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index 67a088eb5..39c46c7c7 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -4,6 +4,7 @@ List of contributors: Jeroen van Rijn: Initial implementation. + Benoit Jacquier: tga loader */ @@ -14,11 +15,16 @@ import "core:mem" import "core:image" import "core:bytes" import "core:os" +import "core:compress" +import "core:strings" + +// TODO: alpha_premultiply support Error :: image.Error Image :: image.Image Options :: image.Options +GA_Pixel :: image.GA_Pixel RGB_Pixel :: image.RGB_Pixel RGBA_Pixel :: image.RGBA_Pixel @@ -57,7 +63,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{} } header := image.TGA_Header{ - data_type_code = 0x02, // Color, uncompressed. + data_type_code = .Uncompressed_RGB, dimensions = {u16le(img.width), u16le(img.height)}, bits_per_pixel = u8(img.depth * img.channels), image_descriptor = 1 << 5, // Origin is top left. @@ -98,4 +104,337 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato return nil if write_ok else .Unable_To_Write_File } -save :: proc{save_to_memory, save_to_file} \ No newline at end of file +save :: proc{save_to_memory, save_to_file} + +load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + options := options + + if .alpha_premultiply in options { + return nil, .Unsupported_Option + } + + if .info in options { + options |= {.return_metadata, .do_not_decompress_image} + options -= {.info} + } + + if .return_header in options && .return_metadata in options { + options -= {.return_header} + } + + // First check for a footer. + filesize := compress.input_size(ctx) or_return + + footer: image.TGA_Footer + have_valid_footer := false + + extension: image.TGA_Extension + have_valid_extension := false + + if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) { + if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None { + if string(f.signature[:]) == image.New_TGA_Signature { + have_valid_footer = true + footer = f + + if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize { + if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None { + if e.extension_size == size_of(image.TGA_Extension) { + have_valid_extension = true + extension = e + } + } + } + } + } + } + + header := image.read_data(ctx, image.TGA_Header) or_return + + // Header checks + rle_encoding := false + color_mapped := false + black_white := false + src_channels := 0 + dest_depth := header.bits_per_pixel + dest_channels := 0 + + #partial switch header.data_type_code { + // Supported formats: RGB(A), RGB(A) RLE + case .Compressed_RGB: + rle_encoding = true + case .Uncompressed_RGB: + // Intentionally blank + case .Uncompressed_Black_White: + black_white = true + dest_depth = 24 + case .Uncompressed_Color_Mapped: + color_mapped = true + case .Compressed_Color_Mapped: + color_mapped = true + rle_encoding = true + case .Compressed_Black_White: + black_white = true + rle_encoding = true + dest_depth = 24 + + case: + return nil, .Unsupported_Format + } + + if color_mapped { + if header.color_map_type != 1 { + return nil, .Unsupported_Format + } + dest_depth = header.color_map_depth + + // Expect LUT entry index to be 8 bits + if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 { + return nil, .Unsupported_Format + } + } + + switch dest_depth { + case 15: // B5G5R5 + src_channels = 2 + dest_channels = 3 + if color_mapped { + src_channels = 1 + } + case 16: // B5G5R5A1 + src_channels = 2 + dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it. + if color_mapped { + src_channels = 1 + } + case 24: // RGB8 + src_channels = 1 if (color_mapped || black_white) else 3 + dest_channels = 3 + case 32: // RGBA8 + src_channels = 4 if !color_mapped else 1 + dest_channels = 4 + + case: + return nil, .Unsupported_Format + } + + if header.image_descriptor & IMAGE_DESCRIPTOR_INTERLEAVING_MASK != 0 { + return nil, .Unsupported_Format + } + + if int(header.dimensions[0]) * int(header.dimensions[1]) > image.MAX_DIMENSIONS { + return nil, .Image_Dimensions_Too_Large + } + + if img == nil { + img = new(Image) + } + + defer if err != nil { + destroy(img) + } + + img.which = .TGA + img.channels = 4 if .alpha_add_if_missing in options else dest_channels + img.channels = 3 if .alpha_drop_if_present in options else img.channels + + img.depth = 8 + img.width = int(header.dimensions[0]) + img.height = int(header.dimensions[1]) + + // Read Image ID if present + image_id := "" + if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None { + return img, .Corrupt + } else { + if .return_metadata in options { + id := strings.trim_right_null(string(_id)) + image_id = strings.clone(id) + } + } + + color_map := make([]RGBA_Pixel, header.color_map_length) + defer delete(color_map) + + if color_mapped { + switch header.color_map_depth { + case 16: + for i in 0..> 7) != 0 + rle_repetition_count = 1 + int(rle_cmd & 0x7F) + read_pixel = true + } else if !is_packet_rle { + read_pixel = rle_repetition_count > 0 + } else { + read_pixel = false + } + } + // Read pixel + if read_pixel { + src, src_err := compress.read_slice(ctx, src_channels) + if src_err != .None { + return img, .Corrupt + } + switch src_channels { + case 1: + // Color-mapped or Black & White + if black_white { + pixel = {src[0], src[0], src[0], 255} + } else if header.color_map_depth == 24 { + pixel = color_map[src[0]].bgra + } else if header.color_map_depth == 16 { + lut := color_map[src[0]] + v := u16(lut.r) | u16(lut.g) << 8 + b := u8( v & 31) << 3 + g := u8((v >> 5) & 31) << 3 + r := u8((v >> 10) & 31) << 3 + pixel = {r, g, b, 255} + } + + case 2: + v := u16(src[0]) | u16(src[1]) << 8 + b := u8( v & 31) << 3 + g := u8((v >> 5) & 31) << 3 + r := u8((v >> 10) & 31) << 3 + pixel = {r, g, b, 255} + + case 3: + pixel = {src[2], src[1], src[0], 255} + case 4: + pixel = {src[2], src[1], src[0], src[3]} + case: + return img, .Corrupt + } + } + + // Write pixel + copy(img.pixels.buf[offset:], pixel[:dest_channels]) + offset += dest_channels if origin_is_left else -dest_channels + rle_repetition_count -= 1 + } + line += 1 if origin_is_top else -1 + } + return img, nil +} + +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + ctx := &compress.Context_Memory_Input{ + input_data = data, + } + + img, err = load_from_context(ctx, options, allocator) + return img, err +} + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename) + defer delete(data) + + if ok { + return load_from_bytes(data, options) + } else { + return nil, .Unable_To_Read_File + } +} + +load :: proc{load_from_file, load_from_bytes, load_from_context} + +destroy :: proc(img: ^Image) { + if img == nil || img.width == 0 || img.height == 0 { + return + } + + bytes.buffer_destroy(&img.pixels) + if v, ok := img.metadata.(^image.TGA_Info); ok { + delete(v.image_id) + free(v) + } + + // Make destroy idempotent + img.width = 0 + img.height = 0 + free(img) +} + +IMAGE_DESCRIPTOR_INTERLEAVING_MASK :: (1<<6) | (1<<7) +IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4 +IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5 + +@(init, private) +_register :: proc() { + image.register(.TGA, load_from_bytes, destroy) +} \ No newline at end of file diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 0b682fbc3..0146746e7 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -188,6 +188,8 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) --- type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) --- +type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) --- + constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- // SIMD related diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin index 0bd7c3641..c3917707f 100644 --- a/core/math/ease/ease.odin +++ b/core/math/ease/ease.odin @@ -356,7 +356,7 @@ Flux_Tween :: struct($T: typeid) { flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) { return { values = make(map[^T]Flux_Tween(T), value_capacity), - keys_to_be_deleted = make([dynamic]^T, 0, value_capacity) + keys_to_be_deleted = make([dynamic]^T, 0, value_capacity), } } diff --git a/core/math/math.odin b/core/math/math.odin index 5f9c737ab..7687d65ef 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -1,6 +1,7 @@ package math import "core:intrinsics" +import "core:builtin" _ :: intrinsics Float_Class :: enum { @@ -36,6 +37,11 @@ MAX_F16_PRECISION :: 4 // Maximum number of meaningful digits after the decimal RAD_PER_DEG :: TAU/360.0 DEG_PER_RAD :: 360.0/TAU +abs :: builtin.abs +min :: builtin.min +max :: builtin.max +clamp :: builtin.clamp + sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) } sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) } sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) } 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/virtual/growing_arena.odin b/core/mem/virtual/growing_arena.odin index f1067071b..0fb826c07 100644 --- a/core/mem/virtual/growing_arena.odin +++ b/core/mem/virtual/growing_arena.odin @@ -13,10 +13,10 @@ 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 +growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) { + arena.curr_block = memory_block_alloc(0, reserved, {}) or_return arena.total_used = 0 - arena.total_reserved = arena.block.reserved + arena.total_reserved = arena.curr_block.reserved return } 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/os/os_darwin.odin b/core/os/os_darwin.odin index b5e67558c..5c2cf8ec2 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -596,7 +596,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { return "", Errno(get_last_error()) } - path := strings.clone_from_cstring(cstring(&buf[0]), context.temp_allocator) + path := strings.clone_from_cstring(cstring(&buf[0])) return path, ERROR_NONE } 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/os/os_openbsd.odin b/core/os/os_openbsd.odin index 9a3dbd874..fba079f15 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -705,4 +705,4 @@ _alloc_command_line_arguments :: proc() -> []string { res[i] = string(arg) } return res -} +} \ No newline at end of file diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 794f9668a..896fc4473 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -1,8 +1,9 @@ package reflect import "core:runtime" -import "core:mem" import "core:intrinsics" +import "core:mem" +_ :: mem _ :: intrinsics Type_Info :: runtime.Type_Info @@ -224,7 +225,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 } @@ -266,19 +267,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 @@ -301,10 +302,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 } @@ -344,14 +345,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) @@ -360,7 +361,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) @@ -729,6 +730,26 @@ 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} +} + +get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) { + ptr := rawptr(val) + tag := get_union_variant_raw_tag(val^) + mem.copy(&res, &ptr, size_of(ptr)) + set_union_variant_raw_tag(res, tag) + return +} + + set_union_variant_raw_tag :: proc(a: any, tag: i64) { if a == nil { return } @@ -826,17 +847,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 @@ -848,7 +869,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 } @@ -1341,11 +1362,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 @@ -1387,7 +1408,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) @@ -1425,7 +1446,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ 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 { @@ -1479,8 +1500,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 } @@ -1499,8 +1520,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 } @@ -1508,7 +1529,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.. (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 + return 0 } when size_of(E) == 0 { - a.len += 1 + array.len += 1 + return 1 } else { if cap(array) < len(array)+1 { cap := 2 * cap(array) + max(8, 1) @@ -361,23 +362,26 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { 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 } when size_of(E) == 0 { - a.len += arg_len + array.len += arg_len + return arg_len } else { if cap(array) < len(array)+arg_len { cap := 2 * cap(array) + max(8, arg_len) @@ -393,23 +397,25 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) } 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 @@ -417,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 } 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/slice/sort.odin b/core/slice/sort.odin index b6e455056..8177205ed 100644 --- a/core/slice/sort.odin +++ b/core/slice/sort.odin @@ -38,6 +38,52 @@ sort :: proc(data: $T/[]$E) where ORD(E) { } } + +sort_by_indices :: proc{ sort_by_indices_allocate, _sort_by_indices} + +sort_by_indices_allocate :: proc(data: $T/[]$E, indices: []int, allocator := context.allocator) -> (sorted: T) { + assert(len(data) == len(indices)) + sorted = make(T, len(data), allocator) + for v, i in indices { + sorted[i] = data[v] + } + return +} + +_sort_by_indices :: proc(data, sorted: $T/[]$E, indices: []int) { + assert(len(data) == len(indices)) + assert(len(data) == len(sorted)) + for v, i in indices { + sorted[i] = data[v] + } +} + +sort_by_indices_overwrite :: proc(data: $T/[]$E, indices: []int) { + assert(len(data) == len(indices)) + temp := make([]int, len(data), context.allocator) + defer delete(temp) + for v, i in indices { + temp[i] = data[v] + } + swap_with_slice(data, temp) +} + +// sort sorts a slice and returns a slice of the original indices +// This sort is not guaranteed to be stable +sort_with_indices :: proc(data: $T/[]$E, allocator := context.allocator) -> (indices: []int) where ORD(E) { + indices = make([]int, len(data), allocator) + when size_of(E) != 0 { + if n := len(data); n > 1 { + for _, idx in indices { + indices[idx] = idx + } + _quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), struct{}{}, .Ordered) + } + return indices + } + return indices +} + // sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j" // This sort is not guaranteed to be stable sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) { @@ -48,6 +94,22 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) { } } +// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j" +// This sort is not guaranteed to be stable +sort_by_with_indices :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool, allocator := context.allocator) -> (indices : []int) { + indices = make([]int, len(data), allocator) + when size_of(E) != 0 { + if n := len(data); n > 1 { + for _, idx in indices { + indices[idx] = idx + } + _quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), less, .Less) + return indices + } + } + return indices +} + sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) { when size_of(E) != 0 { if n := len(data); n > 1 { diff --git a/core/slice/sort_private.odin b/core/slice/sort_private.odin index d93d74bf9..86a6f5928 100644 --- a/core/slice/sort_private.odin +++ b/core/slice/sort_private.odin @@ -198,3 +198,180 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O } } } + +_quick_sort_general_with_indices :: proc(data: $T/[]$E, indices: []int, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check { + less :: #force_inline proc(a, b: E, call: P) -> bool { + when KIND == .Ordered { + return a < b + } else when KIND == .Less { + return call(a, b) + } else when KIND == .Cmp { + return call(a, b) == .Less + } else { + #panic("unhandled Sort_Kind") + } + } + + insertion_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check { + for i in a+1.. a && less(data[j], data[j-1], call); j -= 1 { + swap(data, j, j-1) + swap(indices, j, j-1) + } + } + } + + heap_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check { + sift_down :: proc(data: T, indices: []int, lo, hi, first: int, call: P) #no_bounds_check { + root := lo + for { + child := 2*root + 1 + if child >= hi { + break + } + if child+1 < hi && less(data[first+child], data[first+child+1], call) { + child += 1 + } + if !less(data[first+root], data[first+child], call) { + return + } + swap(data, first+root, first+child) + swap(indices, first+root, first+child) + root = child + } + } + + + first, lo, hi := a, 0, b-a + + for i := (hi-1)/2; i >= 0; i -= 1 { + sift_down(data, indices, i, hi, first, call) + } + + for i := hi-1; i >= 0; i -= 1 { + swap(data, first, first+i) + swap(indices, first, first+i) + sift_down(data, indices, lo, i, first, call) + } + } + + median3 :: proc(data: T, indices: []int, m1, m0, m2: int, call: P) #no_bounds_check { + if less(data[m1], data[m0], call) { + swap(data, m1, m0) + swap(indices, m1, m0) + } + if less(data[m2], data[m1], call) { + swap(data, m2, m1) + swap(indices, m2, m1) + if less(data[m1], data[m0], call) { + swap(data, m1, m0) + swap(indices, m1, m0) + } + } + } + + do_pivot :: proc(data: T, indices: []int, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check { + m := int(uint(lo+hi)>>1) + if hi-lo > 40 { + s := (hi-lo)/8 + median3(data, indices, lo, lo+s, lo+s*2, call) + median3(data, indices, m, m-s, m+s, call) + median3(data, indices, hi-1, hi-1-s, hi-1-s*2, call) + } + median3(data, indices, lo, m, hi-1, call) + + pivot := lo + a, c := lo+1, hi-1 + + + for ; a < c && less(data[a], data[pivot], call); a += 1 { + } + b := a + + for { + for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot + } + for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot + } + if b >= c { + break + } + + swap(data, b, c-1) + swap(indices, b, c-1) + b += 1 + c -= 1 + } + + protect := hi-c < 5 + if !protect && hi-c < (hi-lo)/4 { + dups := 0 + if !less(data[pivot], data[hi-1], call) { + swap(data, c, hi-1) + swap(indices, c, hi-1) + c += 1 + dups += 1 + } + if !less(data[b-1], data[pivot], call) { + b -= 1 + dups += 1 + } + + if !less(data[m], data[pivot], call) { + swap(data, m, b-1) + swap(indices, m, b-1) + b -= 1 + dups += 1 + } + protect = dups > 1 + } + if protect { + for { + for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 { + } + for ; a < b && less(data[a], data[pivot], call); a += 1 { + } + if a >= b { + break + } + swap(data, a, b-1) + swap(indices, a, b-1) + a += 1 + b -= 1 + } + } + swap(data, pivot, b-1) + swap(indices, pivot, b-1) + return b-1, c + } + + assert(len(data) == len(indices)) + + a, b, max_depth := a, b, max_depth + + for b-a > 12 { // only use shell sort for lengths <= 12 + if max_depth == 0 { + heap_sort(data, indices, a, b, call) + return + } + max_depth -= 1 + mlo, mhi := do_pivot(data, indices, a, b, call) + if mlo-a < b-mhi { + _quick_sort_general_with_indices(data, indices, a, mlo, max_depth, call, KIND) + a = mhi + } else { + _quick_sort_general_with_indices(data, indices, mhi, b, max_depth, call, KIND) + b = mlo + } + } + if b-a > 1 { + // Shell short with gap 6 + for i in a+6.. (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/strings.odin b/core/strings/strings.odin index 603917698..fcfc133f3 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1686,7 +1686,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte return clone(str, allocator) } - remains := length-1 + remains := length-n pad_len := rune_count(pad) b: Builder @@ -1702,14 +1702,14 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte return to_string(b) } -// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length +// left_justify returns a string with a pad string at right side if the str's rune length is smaller than length left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { return clone(str, allocator) } - remains := length-1 + remains := length-n pad_len := rune_count(pad) b: Builder @@ -1724,14 +1724,14 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context return to_string(b) } -// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length +// right_justify returns a string with a pad string at left side if the str's rune length is smaller than length right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { return clone(str, allocator) } - remains := length-1 + remains := length-n pad_len := rune_count(pad) b: Builder diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index a106faa9c..1c118e75d 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -8,8 +8,9 @@ import "core:time" foreign import System "System.framework" foreign System { + // __ulock_wait is not available on 10.15 + // See https://github.com/odin-lang/Odin/issues/1959 __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int --- - __ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int --- __ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int --- } @@ -28,9 +29,9 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool { } _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { - timeout_ns := u64(duration) + timeout_ns := u32(duration) * 1000 - s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0) + s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns) if s >= 0 { return true } diff --git a/core/sys/info/cpu_arm.odin b/core/sys/info/cpu_arm.odin new file mode 100644 index 000000000..f66f0e780 --- /dev/null +++ b/core/sys/info/cpu_arm.odin @@ -0,0 +1,26 @@ +//+build arm32, arm64 +package sysinfo + +// TODO: Set up an enum with the ARM equivalent of the above. +CPU_Feature :: enum u64 {} + +cpu_features: Maybe(CPU_Feature) +cpu_name: Maybe(string) + +@(init, private) +init_cpu_features :: proc "c" () { +} + +@(private) +_cpu_name_buf: [72]u8 + +@(init, private) +init_cpu_name :: proc "c" () { + when ODIN_ARCH == .arm32 { + copy(_cpu_name_buf[:], "ARM") + cpu_name = string(_cpu_name_buf[:3]) + } else { + copy(_cpu_name_buf[:], "ARM64") + cpu_name = string(_cpu_name_buf[:5]) + } +} \ No newline at end of file diff --git a/core/simd/x86/cpu.odin b/core/sys/info/cpu_intel.odin similarity index 71% rename from core/simd/x86/cpu.odin rename to core/sys/info/cpu_intel.odin index 14e90c0f0..8cd723203 100644 --- a/core/simd/x86/cpu.odin +++ b/core/sys/info/cpu_intel.odin @@ -1,5 +1,5 @@ //+build i386, amd64 -package simd_x86 +package sysinfo import "core:intrinsics" @@ -9,7 +9,6 @@ cpuid :: intrinsics.x86_cpuid // xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- xgetbv :: intrinsics.x86_xgetbv - CPU_Feature :: enum u64 { aes, // AES hardware implementation (AES NI) adx, // Multi-precision add-carry instruction extensions @@ -34,6 +33,7 @@ CPU_Feature :: enum u64 { CPU_Features :: distinct bit_set[CPU_Feature; u64] cpu_features: Maybe(CPU_Features) +cpu_name: Maybe(string) @(init, private) init_cpu_features :: proc "c" () { @@ -67,6 +67,13 @@ init_cpu_features :: proc "c" () { try_set(&set, .os_xsave, 27, ecx1) try_set(&set, .rdrand, 30, ecx1) + when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { + // xgetbv is an illegal instruction under FreeBSD 13 & OpenBSD 7.1 + // return before probing further + cpu_features = set + return + } + os_supports_avx := false if .os_xsave in set { eax, _ := xgetbv(0) @@ -92,3 +99,31 @@ init_cpu_features :: proc "c" () { cpu_features = set } + +@(private) +_cpu_name_buf: [72]u8 + +@(init, private) +init_cpu_name :: proc "c" () { + number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0) + if number_of_extended_ids < 0x8000_0004 { + return + } + + _buf := transmute(^[0x12]u32)&_cpu_name_buf + _buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0) + _buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0) + _buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0) + + // Some CPUs like may include leading or trailing spaces. Trim them. + // e.g. ` Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz` + + brand := string(_cpu_name_buf[:]) + for len(brand) > 0 && brand[0] == 0 || brand[0] == ' ' { + brand = brand[1:] + } + for len(brand) > 0 && brand[len(brand) - 1] == 0 || brand[len(brand) - 1] == ' ' { + brand = brand[:len(brand) - 1] + } + cpu_name = brand +} \ No newline at end of file diff --git a/core/sys/info/doc.odin b/core/sys/info/doc.odin new file mode 100644 index 000000000..81e437cf8 --- /dev/null +++ b/core/sys/info/doc.odin @@ -0,0 +1,78 @@ +/* + Copyright 2022 Jeroen van Rijn . + Made available under Odin's BSD-3 license. + + Package `core:sys/info` gathers system information on: + Windows, Linux, macOS, FreeBSD & OpenBSD. + + Simply import the package and you'll have access to the OS version, RAM amount + and CPU information. + + On Windows, GPUs will also be enumerated using the registry. + + CPU feature flags can be tested against `cpu_features`, where applicable, e.g. + `if .aes in si.aes { ... }` +*/ +// +ignore +package sysinfo + +import "core:fmt" +import si "core:sys/info" + +main :: proc() { + fmt.printf("Odin: %v\n", ODIN_VERSION) + fmt.printf("OS: %v\n", si.os_version.as_string) + fmt.printf("OS: %#v\n", si.os_version) + fmt.printf("CPU: %v\n", si.cpu_name) + fmt.printf("RAM: %v MiB\n", si.ram.total_ram / 1024 / 1024) + + fmt.println() + for gpu, i in si.gpus { + fmt.printf("GPU #%v:\n", i) + fmt.printf("\tVendor: %v\n", gpu.vendor_name) + fmt.printf("\tModel: %v\n", gpu.model_name) + fmt.printf("\tVRAM: %v MiB\n", gpu.total_ram / 1024 / 1024) + } +} + +/* + Example Windows output: + Odin: dev-2022-09 + OS: Windows 10 Professional (version: 20H2), build: 19042.1466 + OS: OS_Version{ + platform = "Windows", + major = 10, + minor = 0, + patch = 0, + build = [ + 19042, + 1466, + ], + version = "20H2", + as_string = "Windows 10 Professional (version: 20H2), build: 19042.1466", + } + CPU: AMD Ryzen 7 1800X Eight-Core Processor + RAM: 65469 MiB + + GPU #0: + Vendor: Advanced Micro Devices, Inc. + Model: Radeon RX Vega + VRAM: 8176 MiB + + Example macOS output: + ODIN: dev-2022-09 + OS: OS_Version{ + platform = "MacOS", + major = 21, + minor = 5, + patch = 0, + build = [ + 0, + 0, + ], + version = "21F79", + as_string = "macOS Monterey 12.4 (build 21F79, kernel 21.5.0)", + } + CPU: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz + RAM: 8192 MiB +*/ diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin new file mode 100644 index 000000000..fe58d5b06 --- /dev/null +++ b/core/sys/info/platform_darwin.odin @@ -0,0 +1,510 @@ +// +build darwin +package sysinfo + +import sys "core:sys/unix" +import "core:strconv" +import "core:strings" + +@(private) +version_string_buf: [1024]u8 + +@(init, private) +init_os_version :: proc () { + os_version.platform = .MacOS + + // Start building display version + b := strings.builder_from_bytes(version_string_buf[:]) + + mib := []i32{sys.CTL_KERN, sys.KERN_OSVERSION} + build_buf: [12]u8 + + ok := sys.sysctl(mib, &build_buf) + if !ok { + strings.write_string(&b, "macOS Unknown") + os_version.as_string = strings.to_string(b) + return + } + + build := string(cstring(&build_buf[0])) + + // Do we have an exact match? + match: Darwin_Match + rel, exact := macos_release_map[build] + + if exact { + match = .Exact + } else { + // Match on XNU kernel version + mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE} + version_bits: [12]u8 // enough for 999.999.999\x00 + have_kernel_version := sys.sysctl(mib, &version_bits) + + major_ok, minor_ok, patch_ok: bool + + triplet := strings.split(string(cstring(&version_bits[0])), ".", context.temp_allocator) + if len(triplet) != 3 { + have_kernel_version = false + } else { + rel.darwin.x, major_ok = strconv.parse_int(triplet[0]) + rel.darwin.y, minor_ok = strconv.parse_int(triplet[1]) + rel.darwin.z, patch_ok = strconv.parse_int(triplet[2]) + + if !(major_ok && minor_ok && patch_ok) { + have_kernel_version = false + } + } + + if !have_kernel_version { + // We don't know the kernel version, but we do know the build + strings.write_string(&b, "macOS Unknown (build ") + l := strings.builder_len(b) + strings.write_string(&b, build) + os_version.version = strings.to_string(b)[l:] + strings.write_rune(&b, ')') + os_version.as_string = strings.to_string(b) + return + } + rel, match = map_darwin_kernel_version_to_macos_release(build, rel.darwin) + } + + os_version.major = rel.darwin.x + os_version.minor = rel.darwin.y + os_version.patch = rel.darwin.z + + strings.write_string(&b, rel.os_name) + if match == .Exact || match == .Nearest { + strings.write_rune(&b, ' ') + strings.write_string(&b, rel.release.name) + strings.write_rune(&b, ' ') + strings.write_int(&b, rel.release.version.x) + if rel.release.version.y > 0 || rel.release.version.z > 0 { + strings.write_rune(&b, '.') + strings.write_int(&b, rel.release.version.y) + } + if rel.release.version.z > 0 { + strings.write_rune(&b, '.') + strings.write_int(&b, rel.release.version.z) + } + if match == .Nearest { + strings.write_rune(&b, '?') + } + } else { + strings.write_string(&b, " Unknown") + } + + strings.write_string(&b, " (build ") + l := strings.builder_len(b) + strings.write_string(&b, build) + os_version.version = strings.to_string(b)[l:] + + strings.write_string(&b, ", kernel ") + strings.write_int(&b, rel.darwin.x) + strings.write_rune(&b, '.') + strings.write_int(&b, rel.darwin.y) + strings.write_rune(&b, '.') + strings.write_int(&b, rel.darwin.z) + strings.write_rune(&b, ')') + + os_version.as_string = strings.to_string(b) +} + +@(init) +init_ram :: proc() { + // Retrieve RAM info using `sysctl` + + mib := []i32{sys.CTL_HW, sys.HW_MEMSIZE} + mem_size: u64 + if sys.sysctl(mib, &mem_size) { + ram.total_ram = int(mem_size) + } +} + +@(private) +Darwin_To_Release :: struct { + darwin: [3]int, // Darwin kernel triplet + os_name: string, // OS X, MacOS + release: struct { + name: string, // Monterey, Mojave, etc. + version: [3]int, // 12.4, etc. + }, +} + +// Important: Order from lowest to highest kernel version +@(private) +macos_release_map: map[string]Darwin_To_Release = { + // MacOS Tiger + "8A428" = {{8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, + "8A432" = {{8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, + "8B15" = {{8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, + "8B17" = {{8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, + "8C46" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + "8C47" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + "8E102" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + "8E45" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + "8E90" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + "8F46" = {{8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}}, + "8G32" = {{8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, + "8G1165" = {{8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, + "8H14" = {{8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, + "8G1454" = {{8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, + "8I127" = {{8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, + "8I1119" = {{8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, + "8J135" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + "8J2135a" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + "8K1079" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + "8N5107" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + "8L127" = {{8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, + "8L2127" = {{8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, + "8P135" = {{8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, + "8P2137" = {{8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, + "8R218" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + "8R2218" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + "8R2232" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + "8S165" = {{8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, + "8S2167" = {{8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, + + // MacOS Leopard + "9A581" = {{9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}}, + "9B18" = {{9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}}, + "9B2117" = {{9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}}, + "9C31" = {{9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, + "9C7010" = {{9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, + "9D34" = {{9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}}, + "9E17" = {{9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}}, + "9F33" = {{9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}}, + "9G55" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + "9G66" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + "9G71" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + "9J61" = {{9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}}, + "9L30" = {{9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, + "9L34" = {{9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, + + // MacOS Snow Leopard + "10A432" = {{10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, + "10A433" = {{10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, + "10B504" = {{10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}}, + "10C540" = {{10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}}, + "10D573" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + "10D575" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + "10D578" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + "10F569" = {{10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}}, + "10H574" = {{10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}}, + "10J567" = {{10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}}, + "10J869" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + "10J3250" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + "10J4138" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + "10K540" = {{10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, + "10K549" = {{10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, + + // MacOS Lion + "11A511" = {{11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, + "11A511s" = {{11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, + "11A2061" = {{11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, + "11A2063" = {{11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, + "11B26" = {{11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, + "11B2118" = {{11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, + "11C74" = {{11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}}, + "11D50" = {{11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}}, + "11E53" = {{11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}}, + "11G56" = {{11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, + "11G63" = {{11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, + + // MacOS Mountain Lion + "12A269" = {{12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}}, + "12B19" = {{12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}}, + "12C54" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + "12C60" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + "12C2034" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + "12C3104" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + "12D78" = {{12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}}, + "12E55" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + "12E3067" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + "12E4022" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + "12F37" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + "12F45" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + "12F2501" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + "12F2518" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + "12F2542" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + "12F2560" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + + // MacOS Mavericks + "13A603" = {{13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}}, + "13B42" = {{13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}}, + "13C64" = {{13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, + "13C1021" = {{13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, + "13D65" = {{13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}}, + "13E28" = {{13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}}, + "13F34" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1066" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1077" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1096" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1112" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1134" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1507" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1603" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1712" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1808" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + "13F1911" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + + // MacOS Yosemite + "14A389" = {{14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}}, + "14B25" = {{14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}}, + "14C109" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + "14C1510" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + "14C2043" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + "14C1514" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + "14C2513" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + "14D131" = {{14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, + "14D136" = {{14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, + "14E46" = {{14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}}, + "14F27" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1021" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1505" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1509" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1605" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1713" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1808" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1909" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F1912" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F2009" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F2109" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F2315" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F2411" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + "14F2511" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + + // MacOS El Capitan + "15A284" = {{15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}}, + "15B42" = {{15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}}, + "15C50" = {{15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}}, + "15D21" = {{15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}}, + "15E65" = {{15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}}, + "15F34" = {{15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}}, + "15G31" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1004" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1011" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1108" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1212" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1217" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1421" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1510" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G1611" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G17023" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G18013" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G19009" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G20015" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G21013" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + "15G22010" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + + // MacOS Sierra + "16A323" = {{16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}}, + "16B2555" = {{16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, + "16B2657" = {{16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, + "16C67" = {{16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, + "16C68" = {{16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, + "16D32" = {{16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}}, + "16E195" = {{16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}}, + "16F73" = {{16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, + "16F2073" = {{16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, + "16G29" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1036" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1114" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1212" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1314" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1408" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1510" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1618" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1710" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1815" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1917" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G1918" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G2016" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G2127" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G2128" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + "16G2136" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + + // MacOS High Sierra + "17A365" = {{17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, + "17A405" = {{17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, + "17B48" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + "17B1002" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + "17B1003" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + "17C88" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + "17C89" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + "17C205" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + "17C2205" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + "17D47" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + "17D2047" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + "17D102" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + "17D2102" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + "17E199" = {{17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, + "17E202" = {{17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, + "17F77" = {{17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}}, + "17G65" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G2208" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G2307" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G3025" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G4015" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G5019" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G6029" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G6030" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G7024" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G8029" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G8030" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G8037" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G9016" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G10021" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G11023" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G12034" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G13033" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G13035" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G14019" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G14033" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + "17G14042" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + + // MacOS Mojave + "18A391" = {{18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}}, + "18B75" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + "18B2107" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + "18B3094" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + "18C54" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}}, + "18D42" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + "18D43" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + "18D109" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + "18E226" = {{18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, + "18E227" = {{18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, + "18F132" = {{18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}}, + "18G84" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G87" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G95" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G103" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G1012" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G2022" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G3020" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G4032" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G5033" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G6020" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G6032" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G6042" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G7016" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G8012" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G8022" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G9028" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G9216" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + "18G9323" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + + // MacOS Catalina + "19A583" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + "19A602" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + "19A603" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + "19B88" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}}, + "19C57" = {{19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, + "19C58" = {{19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, + "19D76" = {{19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}}, + "19E266" = {{19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, + "19E287" = {{19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, + "19F96" = {{19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, + "19F101" = {{19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, + "19G73" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, + "19G2021" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, + "19H2" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H4" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H15" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H114" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H512" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H524" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1030" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1217" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1323" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1417" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1419" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1519" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1615" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1713" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1715" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1824" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H1922" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + "19H2026" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + + // MacOS Big Sur + "20A2411" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}}, + "20B29" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, + "20B50" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, + "20C69" = {{20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}}, + "20D64" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}}, + "20D74" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, + "20D75" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, + "20D80" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}}, + "20D91" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}}, + "20E232" = {{20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}}, + "20E241" = {{20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}}, + "20F71" = {{20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}}, + "20G71" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}}, + "20G80" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}}, + "20G95" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}}, + "20G165" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}}, + "20G224" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}}, + "20G314" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}}, + "20G415" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}}, + "20G417" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}}, + "20G527" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}}, + "20G624" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}}, + "20G630" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}}, + "20G730" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}}, + + // MacOS Monterey + "21A344" = {{21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}}, + "21A559" = {{21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}}, + "21C52" = {{21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}}, + "21D49" = {{21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}}, + "21D62" = {{21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}}, + "21E230" = {{21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}}, + "21E258" = {{21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}}, + "21F79" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + "21F2081" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + "21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + "21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}}, + "21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}}, +} + +@(private) +Darwin_Match :: enum { + Unknown, + Exact, + Nearest, +} + +@(private) +map_darwin_kernel_version_to_macos_release :: proc(build: string, darwin: [3]int) -> (res: Darwin_To_Release, match: Darwin_Match) { + // Find exact release match if possible. + if v, v_ok := macos_release_map[build]; v_ok { + return v, .Exact + } + + nearest: Darwin_To_Release + for _, v in macos_release_map { + // Try an exact match on XNU version first. + if darwin == v.darwin { + return v, .Exact + } + + // Major kernel version needs to match exactly, + // otherwise the release is considered .Unknown + if darwin.x == v.darwin.x { + if nearest == {} { + nearest = v + } + if darwin.y >= v.darwin.y && v.darwin != nearest.darwin { + nearest = v + if darwin.z >= v.darwin.z && v.darwin != nearest.darwin { + nearest = v + } + } + } + } + + if nearest == {} { + return {darwin, "macOS", {"Unknown", {}}}, .Unknown + } else { + return nearest, .Nearest + } +} \ No newline at end of file diff --git a/core/sys/info/platform_freebsd.odin b/core/sys/info/platform_freebsd.odin new file mode 100644 index 000000000..0ca2841be --- /dev/null +++ b/core/sys/info/platform_freebsd.odin @@ -0,0 +1,76 @@ +// +build freebsd +package sysinfo + +import sys "core:sys/unix" +import "core:strings" +import "core:strconv" + +@(private) +version_string_buf: [1024]u8 + +@(init, private) +init_os_version :: proc () { + os_version.platform = .FreeBSD + + kernel_version_buf: [129]u8 + + b := strings.builder_from_bytes(version_string_buf[:]) + // Retrieve kernel info using `sysctl`, e.g. FreeBSD 13.1-RELEASE-p2 GENERIC + mib := []i32{sys.CTL_KERN, sys.KERN_VERSION} + if !sys.sysctl(mib, &kernel_version_buf) { + return + } + + pretty_name := string(cstring(raw_data(kernel_version_buf[:]))) + pretty_name = strings.trim(pretty_name, "\n") + strings.write_string(&b, pretty_name) + + // l := strings.builder_len(b) + + // Retrieve kernel revision using `sysctl`, e.g. 199506 + mib = []i32{sys.CTL_KERN, sys.KERN_OSREV} + revision: int + if !sys.sysctl(mib, &revision) { + return + } + os_version.patch = revision + + strings.write_string(&b, ", revision ") + strings.write_int(&b, revision) + + // Finalize pretty name. + os_version.as_string = strings.to_string(b) + + // Retrieve kernel release using `sysctl`, e.g. 13.1-RELEASE-p2 + mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE} + if !sys.sysctl(mib, &kernel_version_buf) { + return + } + + // Parse kernel version + release := string(cstring(raw_data(kernel_version_buf[:]))) + version_bits := strings.split_n(release, "-", 2, context.temp_allocator) + if len(version_bits) > 1 { + // Parse major, minor from KERN_OSRELEASE + triplet := strings.split(version_bits[0], ".", context.temp_allocator) + if len(triplet) == 2 { + major, major_ok := strconv.parse_int(triplet[0]) + minor, minor_ok := strconv.parse_int(triplet[1]) + + if major_ok && minor_ok { + os_version.major = major + os_version.minor = minor + } + } + } +} + +@(init) +init_ram :: proc() { + // Retrieve RAM info using `sysctl` + mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM} + mem_size: u64 + if sys.sysctl(mib, &mem_size) { + ram.total_ram = int(mem_size) + } +} \ No newline at end of file diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin new file mode 100644 index 000000000..d1f9ca599 --- /dev/null +++ b/core/sys/info/platform_linux.odin @@ -0,0 +1,136 @@ +// +build linux +package sysinfo + +import "core:c" +import sys "core:sys/unix" +import "core:intrinsics" +import "core:os" +import "core:strings" +import "core:strconv" + +@(private) +version_string_buf: [1024]u8 + +@(init, private) +init_os_version :: proc () { + os_version.platform = .Linux + + // Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` + fd, err := os.open("/etc/os-release", os.O_RDONLY, 0) + if err != 0 { + return + } + defer os.close(fd) + + os_release_buf: [2048]u8 + n, read_err := os.read(fd, os_release_buf[:]) + if read_err != 0 { + return + } + release := string(os_release_buf[:n]) + + NEEDLE :: "PRETTY_NAME=\"" + pretty_start := strings.index(release, NEEDLE) + + b := strings.builder_from_bytes(version_string_buf[:]) + + if pretty_start > 0 { + for r, i in release[pretty_start + len(NEEDLE):] { + if r == '"' { + strings.write_string(&b, release[pretty_start + len(NEEDLE):][:i]) + break + } else if r == '\r' || r == '\n' { + strings.write_string(&b, "Unknown Linux Distro") + break + } + } + } + + NEW_UTS_LEN :: 64 + UTS_Name :: struct { + sys_name: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`, + node_name: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`, + release: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`, + version: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`, + machine: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`, + domain_name: [NEW_UTS_LEN + 1]u8 `fmt:"s,0"`, + } + uts: UTS_Name + + // Grab kernel info using `uname()` syscall, https://linux.die.net/man/2/uname + if intrinsics.syscall(sys.SYS_uname, uintptr(&uts)) != 0 { + return + } + + strings.write_string(&b, ", ") + strings.write_string(&b, string(cstring(&uts.sys_name[0]))) + strings.write_rune(&b, ' ') + + l := strings.builder_len(b) + strings.write_string(&b, string(cstring(&uts.release[0]))) + + // Parse kernel version, as substrings of the version info in `version_string_buf` + version_bits := strings.split_n(strings.to_string(b)[l:], "-", 2, context.temp_allocator) + if len(version_bits) > 1 { + os_version.version = version_bits[1] + } + + // Parse major, minor, patch from release info + triplet := strings.split(version_bits[0], ".", context.temp_allocator) + if len(triplet) == 3 { + major, major_ok := strconv.parse_int(triplet[0]) + minor, minor_ok := strconv.parse_int(triplet[1]) + patch, patch_ok := strconv.parse_int(triplet[2]) + + if major_ok && minor_ok && patch_ok { + os_version.major = major + os_version.minor = minor + os_version.patch = patch + } + } + + // Finish the string + os_version.as_string = strings.to_string(b) +} + +Sys_Info :: struct { + uptime: c.long, // Seconds since boot + loads: [3]c.long, // 1, 5, 15 minute load averages + totalram: c.ulong, // Total usable main memory size + freeram: c.ulong, // Available memory size + sharedram: c.ulong, // Amount of shared memory + bufferram: c.ulong, // Memory used by buffers + totalswap: c.ulong, // Total swap space size + freeswap: c.ulong, // Swap space still available + procs: c.ushort, // Number of current processes + totalhigh: c.ulong, // Total high memory size + freehigh: c.ulong, // Available high memory size + mem_unit: c.int, // Memory unit size in bytes + _padding: [20 - (2 * size_of(c.long)) - size_of(c.int)]u8, +} + +get_sysinfo :: proc "c" () -> (res: Sys_Info, ok: bool) { + si: Sys_Info + err := intrinsics.syscall(sys.SYS_sysinfo, uintptr(rawptr(&si))) + if err != 0 { + // Unable to retrieve sysinfo + return {}, false + } + return si, true +} + +@(init) +init_ram :: proc() { + // Retrieve RAM info using `sysinfo` + si, ok := get_sysinfo() + if !ok { + return + } + + ram = RAM{ + total_ram = int(si.totalram) * int(si.mem_unit), + free_ram = int(si.freeram) * int(si.mem_unit), + total_swap = int(si.totalswap) * int(si.mem_unit), + free_swap = int(si.freeswap) * int(si.mem_unit), + } +} \ No newline at end of file diff --git a/core/sys/info/platform_openbsd.odin b/core/sys/info/platform_openbsd.odin new file mode 100644 index 000000000..080fad9b7 --- /dev/null +++ b/core/sys/info/platform_openbsd.odin @@ -0,0 +1,69 @@ +// +build openbsd +package sysinfo + +import sys "core:sys/unix" +import "core:strings" +import "core:strconv" + +@(private) +version_string_buf: [1024]u8 + +@(init, private) +init_os_version :: proc () { + os_version.platform = .OpenBSD + + kernel_version_buf: [1024]u8 + + b := strings.builder_from_bytes(version_string_buf[:]) + // Retrieve kernel info using `sysctl`, e.g. OpenBSD + mib := []i32{sys.CTL_KERN, sys.KERN_OSTYPE} + if !sys.sysctl(mib, &kernel_version_buf) { + return + } + os_type := string(cstring(raw_data(kernel_version_buf[:]))) + strings.write_string(&b, os_type) + + mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE} + if !sys.sysctl(mib, &kernel_version_buf) { + return + } + + strings.write_rune(&b, ' ') + version := string(cstring(raw_data(kernel_version_buf[:]))) + strings.write_string(&b, version) + + // // Parse kernel version + triplet := strings.split(version, ".", context.temp_allocator) + if len(triplet) == 2 { + major, major_ok := strconv.parse_int(triplet[0]) + minor, minor_ok := strconv.parse_int(triplet[1]) + + if major_ok && minor_ok { + os_version.major = major + os_version.minor = minor + } + } + + // Retrieve kernel revision using `sysctl`, e.g. 199506 + mib = []i32{sys.CTL_KERN, sys.KERN_OSREV} + revision: int + if !sys.sysctl(mib, &revision) { + return + } + os_version.patch = revision + strings.write_string(&b, ", build ") + strings.write_int(&b, revision) + + // Finalize pretty name. + os_version.as_string = strings.to_string(b) +} + +@(init) +init_ram :: proc() { + // Retrieve RAM info using `sysctl` + mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM64} + mem_size: u64 + if sys.sysctl(mib, &mem_size) { + ram.total_ram = int(mem_size) + } +} \ No newline at end of file diff --git a/core/sys/info/platform_windows.odin b/core/sys/info/platform_windows.odin new file mode 100644 index 000000000..f7f689346 --- /dev/null +++ b/core/sys/info/platform_windows.odin @@ -0,0 +1,375 @@ +// +build windows +package sysinfo + +import sys "core:sys/windows" +import "core:intrinsics" +import "core:strings" +import "core:unicode/utf16" + +import "core:fmt" + +@(private) +version_string_buf: [1024]u8 + +@(init, private) +init_os_version :: proc () { + /* + NOTE(Jeroen): + `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. + `RtlGetVersion` will return the true version. + + Rather than include the WinDDK, we ask the kernel directly. + `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release) + + */ + os_version.platform = .Windows + + osvi: sys.OSVERSIONINFOEXW + osvi.dwOSVersionInfoSize = size_of(osvi) + status := sys.RtlGetVersion(&osvi) + + if status != 0 { + return + } + + product_type: sys.Windows_Product_Type + sys.GetProductInfo( + osvi.dwMajorVersion, osvi.dwMinorVersion, + u32(osvi.wServicePackMajor), u32(osvi.wServicePackMinor), + &product_type, + ) + + os_version.major = int(osvi.dwMajorVersion) + os_version.minor = int(osvi.dwMinorVersion) + os_version.build[0] = int(osvi.dwBuildNumber) + + b := strings.builder_from_bytes(version_string_buf[:]) + strings.write_string(&b, "Windows ") + + switch osvi.dwMajorVersion { + case 10: + switch osvi.wProductType { + case 1: // VER_NT_WORKSTATION: + if osvi.dwBuildNumber < 22000 { + strings.write_string(&b, "10 ") + } else { + strings.write_string(&b, "11 ") + } + format_windows_product_type(&b, product_type) + + case: // Server or Domain Controller + switch osvi.dwBuildNumber { + case 14393: + strings.write_string(&b, "2016 Server") + case 17763: + strings.write_string(&b, "2019 Server") + case 20348: + strings.write_string(&b, "2022 Server") + case: + strings.write_string(&b, "Unknown Server") + } + } + + case 6: + switch osvi.dwMinorVersion { + case 0: + switch osvi.wProductType { + case 1: // VER_NT_WORKSTATION + strings.write_string(&b, "Windows Vista ") + format_windows_product_type(&b, product_type) + case 3: + strings.write_string(&b, "Windows Server 2008") + } + + case 1: + switch osvi.wProductType { + case 1: // VER_NT_WORKSTATION: + strings.write_string(&b, "Windows 7 ") + format_windows_product_type(&b, product_type) + case 3: + strings.write_string(&b, "Windows Server 2008 R2") + } + + case 2: + switch osvi.wProductType { + case 1: // VER_NT_WORKSTATION: + strings.write_string(&b, "Windows 8 ") + format_windows_product_type(&b, product_type) + case 3: + strings.write_string(&b, "Windows Server 2012") + } + + case 3: + switch osvi.wProductType { + case 1: // VER_NT_WORKSTATION: + strings.write_string(&b, "Windows 8.1 ") + format_windows_product_type(&b, product_type) + case 3: + strings.write_string(&b, "Windows Server 2012 R2") + } + } + + case 5: + switch osvi.dwMinorVersion { + case 0: + strings.write_string(&b, "Windows 2000") + case 1: + strings.write_string(&b, "Windows XP") + case 2: + strings.write_string(&b, "Windows Server 2003") + } + } + + // Grab DisplayVersion + os_version.version = format_display_version(&b) + + // Grab build number and UBR + os_version.build[1] = format_build_number(&b, int(osvi.dwBuildNumber)) + + // Finish the string + os_version.as_string = strings.to_string(b) + + format_windows_product_type :: proc (b: ^strings.Builder, prod_type: sys.Windows_Product_Type) { + #partial switch prod_type { + case .ULTIMATE: + strings.write_string(b, "Ultimate") + + case .HOME_BASIC: + strings.write_string(b, "Home Basic") + + case .HOME_PREMIUM: + strings.write_string(b, "Home Premium") + + case .ENTERPRISE: + strings.write_string(b, "Enterprise") + + case .CORE: + strings.write_string(b, "Home Basic") + + case .HOME_BASIC_N: + strings.write_string(b, "Home Basic N") + + case .EDUCATION: + strings.write_string(b, "Education") + + case .EDUCATION_N: + strings.write_string(b, "Education N") + + case .BUSINESS: + strings.write_string(b, "Business") + + case .STANDARD_SERVER: + strings.write_string(b, "Standard Server") + + case .DATACENTER_SERVER: + strings.write_string(b, "Datacenter") + + case .SMALLBUSINESS_SERVER: + strings.write_string(b, "Windows Small Business Server") + + case .ENTERPRISE_SERVER: + strings.write_string(b, "Enterprise Server") + + case .STARTER: + strings.write_string(b, "Starter") + + case .DATACENTER_SERVER_CORE: + strings.write_string(b, "Datacenter Server Core") + + case .STANDARD_SERVER_CORE: + strings.write_string(b, "Server Standard Core") + + case .ENTERPRISE_SERVER_CORE: + strings.write_string(b, "Enterprise Server Core") + + case .BUSINESS_N: + strings.write_string(b, "Business N") + + case .HOME_SERVER: + strings.write_string(b, "Home Server") + + case .SERVER_FOR_SMALLBUSINESS: + strings.write_string(b, "Windows Server 2008 for Windows Essential Server Solutions") + + case .SMALLBUSINESS_SERVER_PREMIUM: + strings.write_string(b, "Small Business Server Premium") + + case .HOME_PREMIUM_N: + strings.write_string(b, "Home Premium N") + + case .ENTERPRISE_N: + strings.write_string(b, "Enterprise N") + + case .ULTIMATE_N: + strings.write_string(b, "Ultimate N") + + case .HYPERV: + strings.write_string(b, "HyperV") + + case .STARTER_N: + strings.write_string(b, "Starter N") + + case .PROFESSIONAL: + strings.write_string(b, "Professional") + + case .PROFESSIONAL_N: + strings.write_string(b, "Professional N") + + case: + strings.write_string(b, "Unknown Edition") + } + } + + // Grab Windows DisplayVersion (like 20H02) + format_display_version :: proc (b: ^strings.Builder) -> (version: string) { + dv, ok := read_reg( + sys.HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + "DisplayVersion", + string, + ) + defer delete(dv) // It'll be interned into `version_string_buf` + + if ok { + strings.write_string(b, " (version: ") + l := strings.builder_len(b^) + strings.write_string(b, dv) + version = strings.to_string(b^)[l:][:len(dv)] + strings.write_rune(b, ')') + } + return + } + + // Grab build number and UBR + format_build_number :: proc (b: ^strings.Builder, major_build: int) -> (ubr: int) { + res, ok := read_reg( + sys.HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + "UBR", + i32, + ) + + if ok { + ubr = int(res) + strings.write_string(b, ", build: ") + strings.write_int(b, major_build) + strings.write_rune(b, '.') + strings.write_int(b, ubr) + } + return + } +} + +@(init) +init_ram :: proc() { + state: sys.MEMORYSTATUSEX + + state.dwLength = size_of(state) + ok := sys.GlobalMemoryStatusEx(&state) + if !ok { + return + } + ram = RAM{ + total_ram = int(state.ullTotalPhys), + free_ram = int(state.ullAvailPhys), + total_swap = int(state.ullTotalPageFil), + free_swap = int(state.ullAvailPageFil), + } +} + +@(init, private) +init_gpu_info :: proc() { + + GPU_INFO_BASE :: "SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\" + + gpu_list: [dynamic]GPU + gpu_index: int + + for { + key := fmt.tprintf("%v\\%04d", GPU_INFO_BASE, gpu_index) + + if vendor, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "ProviderName", string); ok { + append(&gpu_list, GPU{vendor_name = vendor}) + } else { + break + } + + if desc, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc", string); ok { + gpu_list[gpu_index].model_name = desc + } + + if vram, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize", i64); ok { + gpu_list[gpu_index].total_ram = int(vram) + } + gpu_index += 1 + } + gpus = gpu_list[:] +} + +@(private) +read_reg :: proc(hkey: sys.HKEY, subkey, val: string, $T: typeid) -> (res: T, ok: bool) { + BUF_SIZE :: 1024 + + if len(subkey) == 0 || len(val) == 0 { + return {}, false + } + + key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) + val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) + + utf16.encode_string(key_name_wide, subkey) + utf16.encode_string(val_name_wide, val) + + when T == string { + result_wide := make([]u16, BUF_SIZE, context.temp_allocator) + result_size := sys.DWORD(BUF_SIZE * size_of(u16)) + + status := sys.RegGetValueW( + hkey, + &key_name_wide[0], + &val_name_wide[0], + sys.RRF_RT_REG_SZ, + nil, + raw_data(result_wide[:]), + &result_size, + ) + if status != 0 { + // Couldn't retrieve string + return + } + + // Result string will be allocated for the caller. + result_utf8 := make([]u8, BUF_SIZE * 4, context.temp_allocator) + utf16.decode_to_utf8(result_utf8, result_wide[:result_size]) + return strings.clone_from_cstring(cstring(raw_data(result_utf8))), true + + } else when T == i32 { + result_size := sys.DWORD(size_of(i32)) + status := sys.RegGetValueW( + hkey, + &key_name_wide[0], + &val_name_wide[0], + sys.RRF_RT_REG_DWORD, + nil, + &res, + &result_size, + ) + return res, status == 0 + + } else when T == i64 { + result_size := sys.DWORD(size_of(i64)) + status := sys.RegGetValueW( + hkey, + &key_name_wide[0], + &val_name_wide[0], + sys.RRF_RT_REG_QWORD, + nil, + &res, + &result_size, + ) + return res, status == 0 + } else { + #assert(false, "Unhandled type for read_reg") + } + return +} \ No newline at end of file diff --git a/core/sys/info/sysinfo.odin b/core/sys/info/sysinfo.odin new file mode 100644 index 000000000..69f9f1584 --- /dev/null +++ b/core/sys/info/sysinfo.odin @@ -0,0 +1,45 @@ +package sysinfo + +when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64) { + #assert(false, "This package is unsupported on this architecture.") +} + +os_version: OS_Version +ram: RAM +gpus: []GPU + +OS_Version_Platform :: enum { + Unknown, + Windows, + Linux, + MacOS, + iOS, + FreeBSD, + OpenBSD, + NetBSD, +} + +OS_Version :: struct { + platform: OS_Version_Platform, + + major: int, + minor: int, + patch: int, + build: [2]int, + version: string, + + as_string: string, +} + +RAM :: struct { + total_ram: int, + free_ram: int, + total_swap: int, + free_swap: int, +} + +GPU :: struct { + vendor_name: string, + model_name: string, + total_ram: int, +} \ No newline at end of file diff --git a/core/sys/unix/syscalls_freebsd.odin b/core/sys/unix/syscalls_freebsd.odin new file mode 100644 index 000000000..3441035cd --- /dev/null +++ b/core/sys/unix/syscalls_freebsd.odin @@ -0,0 +1,7 @@ +package unix + +// FreeBSD 13 syscall numbers +// See: https://alfonsosiciliano.gitlab.io/posts/2021-01-02-freebsd-system-calls-table.html + +SYS_uname : uintptr : 164 +SYS_sysctl : uintptr : 202 \ No newline at end of file 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/unix/syscalls_openbsd.odin b/core/sys/unix/syscalls_openbsd.odin new file mode 100644 index 000000000..703bc4d4e --- /dev/null +++ b/core/sys/unix/syscalls_openbsd.odin @@ -0,0 +1,6 @@ +package unix + +// OpenBSD 7.1 syscall numbers +// See: /usr/include/sys/syscall.h + +SYS_sysctl : uintptr : 202 \ No newline at end of file diff --git a/core/sys/unix/sysctl_darwin.odin b/core/sys/unix/sysctl_darwin.odin new file mode 100644 index 000000000..f9530b86f --- /dev/null +++ b/core/sys/unix/sysctl_darwin.odin @@ -0,0 +1,45 @@ +//+build darwin +package unix + +import "core:sys/darwin" +import "core:intrinsics" + +_ :: darwin + +sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { + mib := mib + result_size := i64(size_of(T)) + + res := intrinsics.syscall( + darwin.unix_offset_syscall(.sysctl), + uintptr(raw_data(mib)), uintptr(len(mib)), + uintptr(val), uintptr(&result_size), + uintptr(0), uintptr(0), + ) + return res == 0 +} + +// See sysctl.h for darwin for details +CTL_KERN :: 1 + KERN_OSTYPE :: 1 // Darwin + KERN_OSRELEASE :: 2 // 21.5.0 for 12.4 Monterey + KERN_OSREV :: 3 // i32: system revision + KERN_VERSION :: 4 // Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:darwin-8020.121.3~4/RELEASE_X86_64 + KERN_OSRELDATE :: 26 // i32: OS release date + KERN_OSVERSION :: 65 // Build number, e.g. 21F79 +CTL_VM :: 2 +CTL_VFS :: 3 +CTL_NET :: 4 +CTL_DEBUG :: 5 +CTL_HW :: 6 + HW_MACHINE :: 1 // x86_64 + HW_MODEL :: 2 // MacbookPro14,1 + HW_NCPU :: 3 /* int: number of cpus */ + HW_BYTEORDER :: 4 /* int: machine byte order */ + HW_MACHINE_ARCH :: 12 /* string: machine architecture */ + HW_VECTORUNIT :: 13 /* int: has HW vector unit? */ + HW_MEMSIZE :: 24 // u64 + HW_AVAILCPU :: 25 /* int: number of available CPUs */ + +CTL_MACHDEP :: 7 +CTL_USER :: 8 \ No newline at end of file diff --git a/core/sys/unix/sysctl_freebsd.odin b/core/sys/unix/sysctl_freebsd.odin new file mode 100644 index 000000000..5b0bcb88d --- /dev/null +++ b/core/sys/unix/sysctl_freebsd.odin @@ -0,0 +1,44 @@ +//+build freebsd +package unix + +import "core:intrinsics" + +sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { + mib := mib + result_size := i64(size_of(T)) + + res := intrinsics.syscall(SYS_sysctl, + uintptr(raw_data(mib)), uintptr(len(mib)), + uintptr(val), uintptr(&result_size), + uintptr(0), uintptr(0), + ) + return res == 0 +} + +// See /usr/include/sys/sysctl.h for details +CTL_SYSCTL :: 0 +CTL_KERN :: 1 + KERN_OSTYPE :: 1 + KERN_OSRELEASE :: 2 + KERN_OSREV :: 3 + KERN_VERSION :: 4 +CTL_VM :: 2 +CTL_VFS :: 3 +CTL_NET :: 4 +CTL_DEBUG :: 5 +CTL_HW :: 6 + HW_MACHINE :: 1 + HW_MODEL :: 2 + HW_NCPU :: 3 + HW_BYTEORDER :: 4 + HW_PHYSMEM :: 5 + HW_USERMEM :: 6 + HW_PAGESIZE :: 7 + HW_DISKNAMES :: 8 + HW_DISKSTATS :: 9 + HW_FLOATINGPT :: 10 + HW_MACHINE_ARCH :: 11 + HW_REALMEM :: 12 +CTL_MACHDEP :: 7 +CTL_USER :: 8 +CTL_P1003_1B :: 9 diff --git a/core/sys/unix/sysctl_openbsd.odin b/core/sys/unix/sysctl_openbsd.odin new file mode 100644 index 000000000..b93e8f9bd --- /dev/null +++ b/core/sys/unix/sysctl_openbsd.odin @@ -0,0 +1,49 @@ +//+build openbsd +package unix + +import "core:c" +foreign import libc "system:c" + +@(default_calling_convention="c") +foreign libc { + @(link_name="sysctl") _unix_sysctl :: proc(name: [^]i32, namelen: u32, oldp: rawptr, oldlenp: ^c.size_t, newp: rawptr, newlen: c.size_t) -> i32 --- +} + +sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { + mib := mib + result_size := c.size_t(size_of(T)) + res := _unix_sysctl(raw_data(mib), u32(len(mib)), val, &result_size, nil, 0) + return res == 0 +} + +// See /usr/include/sys/sysctl.h for details +CTL_SYSCTL :: 0 +CTL_KERN :: 1 + KERN_OSTYPE :: 1 + KERN_OSRELEASE :: 2 + KERN_OSREV :: 3 + KERN_VERSION :: 4 +CTL_VM :: 2 +CTL_FS :: 3 +CTL_NET :: 4 +CTL_DEBUG :: 5 +CTL_HW :: 6 + HW_MACHINE :: 1 + HW_MODEL :: 2 + HW_NCPU :: 3 + HW_BYTEORDER :: 4 + HW_PHYSMEM :: 5 + HW_USERMEM :: 6 + HW_PAGESIZE :: 7 + HW_DISKNAMES :: 8 + HW_DISKSTATS :: 9 + HW_DISKCOUNT :: 10 + HW_SENSORS :: 11 + HW_CPUSPEED :: 12 + HW_SETPERF :: 13 + HW_VENDOR :: 14 + HW_PRODUCT :: 15 + HW_VERSION :: 16 + HW_SERIALNO :: 17 + HW_UUID :: 18 + HW_PHYSMEM64 :: 19 \ No newline at end of file diff --git a/core/sys/valgrind/callgrind.odin b/core/sys/valgrind/callgrind.odin index 1396f82ad..cf377e353 100644 --- a/core/sys/valgrind/callgrind.odin +++ b/core/sys/valgrind/callgrind.odin @@ -13,10 +13,10 @@ Callgrind_Client_Request :: enum uintptr { } @(require_results) -callgrind_client_request_expr :: proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { +callgrind_client_request_expr :: #force_inline 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) { +callgrind_client_request_stmt :: #force_inline proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) { _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4) } diff --git a/core/sys/valgrind/helgrind.odin b/core/sys/valgrind/helgrind.odin new file mode 100644 index 000000000..41c047d05 --- /dev/null +++ b/core/sys/valgrind/helgrind.odin @@ -0,0 +1,178 @@ +//+build amd64 +package sys_valgrind + +import "core:intrinsics" + +Helgrind_Client_Request :: enum uintptr { + Clean_Memory = 'H'<<24 | 'G'<<16, + Set_My_pthread_t = ('H'<<25 | 'G'<<16)+256, + Pthread_Join_Post, + Pthread_Mutex_Init_Post, + Pthread_Mutex_Destroy_Pre, + Pthread_Mutex_Unlock_Pre, + Pthread_Mutex_Unlock_Post, + Pthread_Mutex_Lock_Pre, + Pthread_Mutex_Lock_Post, + Pthread_Cond_Signal_Pre, + Pthread_Cond_Broadcast_Pre, + Pthread_Cond_Wait_Pre, + Pthread_Cond_Wait_Post, + Pthread_Cond_Destroy_Pre, + Pthread_Rwlock_Init_Post, + Pthread_Rwlock_Destroy_Pre, + Pthread_Rwlock_Lock_Pre, + Pthread_Rwlock_Lock_Post, + Pthread_Rwlock_Unlock_Pre, + Pthread_Rwlock_Unlock_Post, + Posix_Sem_Init_Post, + Posix_Sem_Destroy_Pre, + Posix_Sem_Post_Pre, + Posix_Sem_Wait_Post, + Pthread_Barrier_Init_Pre, + Pthread_Barrier_Wait_Pre, + Pthread_Barrier_Destroy_Pre, + Pthread_Spin_Init_Or_Unlock_Pre, + Pthread_Spin_Init_Or_Unlock_Post, + Pthread_Spin_Lock_Pre, + Pthread_Spin_Lock_Post, + Pthread_Spin_Destroy_Pre, + Clientreq_Unimp, + Userso_Send_Pre, + Userso_Recv_Post, + Userso_Forget_All, + Reserved2, + Reserved3, + Reserved4, + Arange_Make_Untracked, + Arange_Make_Tracked, + Pthread_Barrier_Resize_Pre, + Clean_Memory_Heapblock, + Pthread_Cond_Init_Post, +} + +@(require_results) +helgrind_client_request_expr :: #force_inline proc "c" (default: uintptr, request: Helgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { + return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4) +} +helgrind_client_request_stmt :: #force_inline proc "c" (request: Helgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) { + _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4) +} + +helgrind_mutex_init_post :: proc "c" (mutex: rawptr, mb_rec: uint) { + helgrind_client_request_stmt(.Pthread_Mutex_Init_Post, uintptr(mutex), uintptr(mb_rec), 0, 0, 0) +} +helgrind_mutex_destroy_pre :: proc "c" (mutex: rawptr) { + helgrind_client_request_stmt(.Pthread_Mutex_Destroy_Pre, uintptr(mutex), 0, 0, 0, 0) +} +helgrind_mutex_lock_pre :: proc "c" (mutex: rawptr, is_try_lock: bool) { + helgrind_client_request_stmt(.Pthread_Mutex_Lock_Pre, uintptr(mutex), uintptr(is_try_lock), 0, 0, 0) +} +helgrind_mutex_lock_post :: proc "c" (mutex: rawptr) { + helgrind_client_request_stmt(.Pthread_Mutex_Lock_Post, uintptr(mutex), 0, 0, 0, 0) +} +helgrind_mutex_unlock_pre :: proc "c" (mutex: rawptr) { + helgrind_client_request_stmt(.Pthread_Mutex_Unlock_Pre, uintptr(mutex), 0, 0, 0, 0) +} +helgrind_mutex_unlock_post :: proc "c" (mutex: rawptr) { + helgrind_client_request_stmt(.Pthread_Mutex_Unlock_Post, uintptr(mutex), 0, 0, 0, 0) +} + +helgrind_rwlock_init_post :: proc "c" (lock: rawptr) { + helgrind_client_request_stmt(.Pthread_Rwlock_Init_Post, uintptr(lock), 0, 0, 0, 0) +} +helgrind_rwlock_destroy_pre :: proc "c" (lock: rawptr) { + helgrind_client_request_stmt(.Pthread_Rwlock_Destroy_Pre, uintptr(lock), 0, 0, 0, 0) +} +helgrind_rwlock_lock_pre :: proc "c" (lock: rawptr, is_w: bool) { + helgrind_client_request_stmt(.Pthread_Rwlock_Lock_Pre, uintptr(lock), uintptr(is_w), 0, 0, 0) +} +helgrind_rwlock_unlock_post :: proc "c" (lock: rawptr, is_w: bool) { + helgrind_client_request_stmt(.Pthread_Rwlock_Unlock_Pre, uintptr(lock), uintptr(is_w), 0, 0, 0) +} + + +helgrind_sem_init_post :: proc "c" (sem: rawptr, value: uint) { + helgrind_client_request_stmt(.Posix_Sem_Init_Post, uintptr(sem), uintptr(value), 0, 0, 0) +} +helgrind_sem_wait_post :: proc "c" (sem: rawptr) { + helgrind_client_request_stmt(.Posix_Sem_Wait_Post, uintptr(sem), 0, 0, 0, 0) +} +helgrind_sem_post_pre :: proc "c" (sem: rawptr) { + helgrind_client_request_stmt(.Posix_Sem_Post_Pre, uintptr(sem), 0, 0, 0, 0) +} +helgrind_sem_destroy_pre :: proc "c" (sem: rawptr) { + helgrind_client_request_stmt(.Posix_Sem_Destroy_Pre, uintptr(sem), 0, 0, 0, 0) +} + + +helgrind_barrier_init_pre :: proc "c" (bar: rawptr, count: uint, resizable: bool) { + helgrind_client_request_stmt(.Pthread_Barrier_Init_Pre, uintptr(bar), uintptr(count), uintptr(resizable), 0, 0) +} +helgrind_barrier_wait_pre :: proc "c" (bar: rawptr) { + helgrind_client_request_stmt(.Pthread_Barrier_Wait_Pre, uintptr(bar), 0, 0, 0, 0) +} +helgrind_barrier_resize_pre :: proc "c" (bar: rawptr, new_count: uint) { + helgrind_client_request_stmt(.Pthread_Barrier_Resize_Pre, uintptr(bar), uintptr(new_count), 0, 0, 0) +} +helgrind_barrier_destroy_pre :: proc "c" (bar: rawptr) { + helgrind_client_request_stmt(.Pthread_Barrier_Destroy_Pre, uintptr(bar), 0, 0, 0, 0) +} + + + +helgrind_clean_memory :: proc "c" (qzz_start: rawptr, qzz_len: uint) { + helgrind_client_request_stmt(.Clean_Memory, uintptr(qzz_start), uintptr(qzz_len), 0, 0, 0) +} +helgrind_clean_memory_slice :: proc "c" (qzz: []byte) { + helgrind_client_request_stmt(.Clean_Memory, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0) +} +helgrind_clean_memory_heap_block :: proc "c" (qzz_blockstart: rawptr) -> int { + return int(helgrind_client_request_expr(~uintptr(1), .Clean_Memory_Heapblock, uintptr(qzz_blockstart), 0, 0, 0, 0)) +} + + +helgrind_disable_checking :: proc "c" (qzz_start: rawptr, qzz_len: uint) { + helgrind_client_request_stmt(.Arange_Make_Untracked, uintptr(qzz_start), uintptr(qzz_len), 0, 0, 0) +} +helgrind_enable_checking :: proc "c" (qzz_start: rawptr, qzz_len: uint) { + helgrind_client_request_stmt(.Arange_Make_Tracked, uintptr(qzz_start), uintptr(qzz_len), 0, 0, 0) +} + + +helgrind_cond_init_post :: proc "c" (cond: rawptr) { + helgrind_client_request_stmt(.Pthread_Cond_Init_Post, uintptr(cond), 0, 0, 0, 0) +} +helgrind_cond_destroy_pre :: proc "c" (cond: rawptr) { + helgrind_client_request_stmt(.Pthread_Cond_Destroy_Pre, uintptr(cond), 0, 0, 0, 0) +} +helgrind_cond_signal_pre :: proc "c" (cond: rawptr) { + helgrind_client_request_stmt(.Pthread_Cond_Signal_Pre, uintptr(cond), 0, 0, 0, 0) +} +helgrind_cond_broadcast_pre :: proc "c" (cond: rawptr) { + helgrind_client_request_stmt(.Pthread_Cond_Broadcast_Pre, uintptr(cond), 0, 0, 0, 0) +} +helgrind_cond_wait_pre :: proc "c" (cond: rawptr, lock: rawptr) -> bool { + return 0 != helgrind_client_request_expr(0, .Pthread_Cond_Wait_Pre, uintptr(cond), uintptr(lock), 0, 0, 0) +} +helgrind_cond_wait_post :: proc "c" (cond: rawptr, lock: rawptr) -> bool { + return 0 != helgrind_client_request_expr(0, .Pthread_Cond_Wait_Post, uintptr(cond), uintptr(lock), 0, 0, 0) +} + + +helgrind_client_request_unimp :: #force_inline proc "c" (msg: cstring) { + helgrind_client_request_stmt(.Clientreq_Unimp, uintptr(rawptr(msg)), 0, 0, 0, 0) +} + + +helgrind_annotate_condvar_lock_wait :: #force_inline proc "c" (cv: rawptr, lock: rawptr) { + helgrind_client_request_unimp("ANNOTATE_CONDVAR_LOCK_WAIT") +} +helgrind_annotate_condvar_wait :: proc "c" (cv: rawptr) { + helgrind_client_request_unimp("ANNOTATE_CONDVAR_WAIT") +} +helgrind_annotate_condvar_signal :: proc "c" (cv: rawptr) { + helgrind_client_request_unimp("ANNOTATE_CONDVAR_SIGNAL") +} +helgrind_annotate_condvar_signal_all :: proc "c" (cv: rawptr) { + helgrind_client_request_unimp("ANNOTATE_CONDVAR_SIGNAL_ALL") +} \ No newline at end of file diff --git a/core/sys/valgrind/memcheck.odin b/core/sys/valgrind/memcheck.odin index 99c65272a..6a9fab854 100644 --- a/core/sys/valgrind/memcheck.odin +++ b/core/sys/valgrind/memcheck.odin @@ -22,10 +22,10 @@ Mem_Check_Client_Request :: enum uintptr { } @(require_results) -mem_check_client_request_expr :: proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { +mem_check_client_request_expr :: #force_inline 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) { +mem_check_client_request_stmt :: #force_inline 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) } diff --git a/core/sys/valgrind/valgrind.odin b/core/sys/valgrind/valgrind.odin index 2fb95588f..b587ea3bf 100644 --- a/core/sys/valgrind/valgrind.odin +++ b/core/sys/valgrind/valgrind.odin @@ -38,10 +38,10 @@ Client_Request :: enum uintptr { } @(require_results) -client_request_expr :: proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr { +client_request_expr :: #force_inline 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) { +client_request_stmt :: #force_inline proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) { _ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4) } @@ -49,8 +49,8 @@ client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: ui // 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) +running_on_valgrind :: proc "c" () -> uint { + return uint(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, diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index f055b6908..15ffa2dd3 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -24,6 +24,8 @@ foreign kernel32 { lpMode: LPDWORD) -> BOOL --- SetConsoleMode :: proc(hConsoleHandle: HANDLE, dwMode: DWORD) -> BOOL --- + SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE, + dwCursorPosition: COORD) -> BOOL --- GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL --- SetHandleInformation :: proc(hObject: HANDLE, @@ -94,6 +96,15 @@ foreign kernel32 { dwCreationFlags: DWORD, lpThreadId: LPDWORD, ) -> HANDLE --- + CreateRemoteThread :: proc( + hProcess: HANDLE, + lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: proc "stdcall" (rawptr) -> DWORD, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD, + ) -> HANDLE --- SwitchToThread :: proc() -> BOOL --- ResumeThread :: proc(thread: HANDLE) -> DWORD --- GetThreadPriority :: proc(thread: HANDLE) -> c_int --- @@ -326,6 +337,15 @@ foreign kernel32 { SetEndOfFile :: proc(hFile: HANDLE) -> BOOL --- CreatePipe :: proc(hReadPipe, hWritePipe: ^HANDLE, lpPipeAttributes: LPSECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL --- + + ConnectNamedPipe :: proc(hNamedPipe: HANDLE, lpOverlapped: LPOVERLAPPED,) -> BOOL --- + DisconnectNamedPipe :: proc(hNamedPipe: HANDLE,) -> BOOL --- + WaitNamedPipeW :: proc(lpNamedPipeName: LPCWSTR, nTimeOut: DWORD,) -> BOOL --- + + SetConsoleCtrlHandler :: proc(HandlerRoutine: PHANDLER_ROUTINE, Add: BOOL) -> BOOL --- + GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL --- + FreeConsole :: proc() -> BOOL --- + GetConsoleWindow :: proc() -> HWND --- } @@ -637,6 +657,13 @@ foreign kernel32 { ) -> BOOL --- } +@(default_calling_convention="stdcall") +foreign kernel32 { + GlobalMemoryStatusEx :: proc( + lpBuffer: ^MEMORYSTATUSEX, + ) -> BOOL --- +} + PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" () @(default_calling_convention="stdcall") @@ -782,15 +809,14 @@ foreign kernel32 { @(default_calling_convention="stdcall") foreign kernel32 { - @(link_name="SetConsoleCtrlHandler") set_console_ctrl_handler :: proc(handler: Handler_Routine, add: BOOL) -> BOOL --- + GetProductInfo :: proc( + OSMajorVersion: DWORD, + OSMinorVersion: DWORD, + SpMajorVersion: DWORD, + SpMinorVersion: DWORD, + product_type: ^Windows_Product_Type, + ) -> BOOL --- } -Handler_Routine :: proc(dwCtrlType: Control_Event) -> BOOL - -Control_Event :: enum DWORD { - control_c = 0, - _break = 1, - close = 2, - logoff = 5, - shutdown = 6, -} +HandlerRoutine :: proc "stdcall" (dwCtrlType: DWORD) -> BOOL +PHANDLER_ROUTINE :: HandlerRoutine diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index edf6e593e..95e53dd46 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -17,6 +17,7 @@ size_t :: c.size_t wchar_t :: c.wchar_t DWORD :: c_ulong +DWORDLONG :: c.ulonglong QWORD :: c.ulonglong HANDLE :: distinct LPVOID HINSTANCE :: HANDLE @@ -978,6 +979,35 @@ WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKF WS_VISIBLE : UINT : 0x1000_0000 WS_VSCROLL : UINT : 0x0020_0000 +WS_EX_ACCEPTFILES : UINT : 0x0000_0010 +WS_EX_APPWINDOW : UINT : 0x0004_0000 +WS_EX_CLIENTEDGE : UINT : 0x0000_0200 +WS_EX_COMPOSITED : UINT : 0x0200_0000 +WS_EX_CONTEXTHELP : UINT : 0x0000_0400 +WS_EX_CONTROLPARENT : UINT : 0x0001_0000 +WS_EX_DLGMODALFRAME : UINT : 0x0000_0001 +WS_EX_DRAGDETECT : UINT : 0x0000_0002 // undocumented +WS_EX_LAYERED : UINT : 0x0008_0000 +WS_EX_LAYOUTRTL : UINT : 0x0040_0000 +WS_EX_LEFT : UINT : 0x0000_0000 +WS_EX_LEFTSCROLLBAR : UINT : 0x0000_4000 +WS_EX_LTRREADING : UINT : 0x0000_0000 +WS_EX_MDICHILD : UINT : 0x0000_0040 +WS_EX_NOACTIVATE : UINT : 0x0800_0000 +WS_EX_NOINHERITLAYOUT : UINT : 0x0010_0000 +WS_EX_NOPARENTNOTIFY : UINT : 0x0000_0004 +WS_EX_NOREDIRECTIONBITMAP : UINT : 0x0020_0000 +WS_EX_OVERLAPPEDWINDOW : UINT : WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE +WS_EX_PALETTEWINDOW : UINT : WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST +WS_EX_RIGHT : UINT : 0x0000_1000 +WS_EX_RIGHTSCROLLBAR : UINT : 0x0000_0000 +WS_EX_RTLREADING : UINT : 0x0000_2000 +WS_EX_STATICEDGE : UINT : 0x0002_0000 +WS_EX_TOOLWINDOW : UINT : 0x0000_0080 +WS_EX_TOPMOST : UINT : 0x0000_0008 +WS_EX_TRANSPARENT : UINT : 0x0000_0020 +WS_EX_WINDOWEDGE : UINT : 0x0000_0100 + PBS_SMOOTH :: 0x01 PBS_VERTICAL :: 0x04 @@ -1628,6 +1658,8 @@ CONDITION_VARIABLE_INIT :: CONDITION_VARIABLE{} SRWLOCK_INIT :: SRWLOCK{} DETACHED_PROCESS: DWORD : 0x00000008 +CREATE_NEW_CONSOLE: DWORD : 0x00000010 +CREATE_NO_WINDOW: DWORD : 0x08000000 CREATE_NEW_PROCESS_GROUP: DWORD : 0x00000200 CREATE_UNICODE_ENVIRONMENT: DWORD : 0x00000400 STARTF_USESTDHANDLES: DWORD : 0x00000100 @@ -1689,6 +1721,7 @@ PIPE_WAIT: DWORD : 0x00000000 PIPE_TYPE_BYTE: DWORD : 0x00000000 PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008 PIPE_READMODE_BYTE: DWORD : 0x00000000 +PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000 FD_SETSIZE :: 64 @@ -3265,3 +3298,141 @@ IFileSaveDialogVtbl :: struct { GetProperties: proc "stdcall" (this: ^IFileSaveDialog, ppStore: ^^IPropertyStore) -> HRESULT, ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT, } + +MEMORYSTATUSEX :: struct { + dwLength: DWORD, + dwMemoryLoad: DWORD, + ullTotalPhys: DWORDLONG, + ullAvailPhys: DWORDLONG, + ullTotalPageFil: DWORDLONG, + ullAvailPageFil: DWORDLONG, + ullTotalVirtual: DWORDLONG, + ullAvailVirtual: DWORDLONG, + ullAvailExtendedVirtual: DWORDLONG, +} + +Windows_Product_Type :: enum DWORD { + BUSINESS = 0x00000006, // Business + BUSINESS_N = 0x00000010, // Business N + CLUSTER_SERVER = 0x00000012, // HPC Edition + CLUSTER_SERVER_V = 0x00000040, // Server Hyper Core V + CORE = 0x00000065, // Windows 10 Home + CORE_COUNTRYSPECIFIC = 0x00000063, // Windows 10 Home China + CORE_N = 0x00000062, // Windows 10 Home N + CORE_SINGLELANGUAGE = 0x00000064, // Windows 10 Home Single Language + DATACENTER_EVALUATION_SERVER = 0x00000050, // Server Datacenter (evaluation installation) + DATACENTER_A_SERVER_CORE = 0x00000091, // Server Datacenter, Semi-Annual Channel (core installation) + STANDARD_A_SERVER_CORE = 0x00000092, // Server Standard, Semi-Annual Channel (core installation) + DATACENTER_SERVER = 0x00000008, // Server Datacenter (full installation. For Server Core installations of Windows Server 2012 and later, use the method, Determining whether Server Core is running.) + DATACENTER_SERVER_CORE = 0x0000000C, // Server Datacenter (core installation, Windows Server 2008 R2 and earlier) + DATACENTER_SERVER_CORE_V = 0x00000027, // Server Datacenter without Hyper-V (core installation) + DATACENTER_SERVER_V = 0x00000025, // Server Datacenter without Hyper-V (full installation) + EDUCATION = 0x00000079, // Windows 10 Education + EDUCATION_N = 0x0000007A, // Windows 10 Education N + ENTERPRISE = 0x00000004, // Windows 10 Enterprise + ENTERPRISE_E = 0x00000046, // Windows 10 Enterprise E + ENTERPRISE_EVALUATION = 0x00000048, // Windows 10 Enterprise Evaluation + ENTERPRISE_N = 0x0000001B, // Windows 10 Enterprise N + ENTERPRISE_N_EVALUATION = 0x00000054, // Windows 10 Enterprise N Evaluation + ENTERPRISE_S = 0x0000007D, // Windows 10 Enterprise 2015 LTSB + ENTERPRISE_S_EVALUATION = 0x00000081, // Windows 10 Enterprise 2015 LTSB Evaluation + ENTERPRISE_S_N = 0x0000007E, // Windows 10 Enterprise 2015 LTSB N + ENTERPRISE_S_N_EVALUATION = 0x00000082, // Windows 10 Enterprise 2015 LTSB N Evaluation + ENTERPRISE_SERVER = 0x0000000A, // Server Enterprise (full installation) + ENTERPRISE_SERVER_CORE = 0x0000000E, // Server Enterprise (core installation) + ENTERPRISE_SERVER_CORE_V = 0x00000029, // Server Enterprise without Hyper-V (core installation) + ENTERPRISE_SERVER_IA64 = 0x0000000F, // Server Enterprise for Itanium-based Systems + ENTERPRISE_SERVER_V = 0x00000026, // Server Enterprise without Hyper-V (full installation) + ESSENTIALBUSINESS_SERVER_ADDL = 0x0000003C, // Windows Essential Server Solution Additional + ESSENTIALBUSINESS_SERVER_ADDLSVC = 0x0000003E, // Windows Essential Server Solution Additional SVC + ESSENTIALBUSINESS_SERVER_MGMT = 0x0000003B, // Windows Essential Server Solution Management + ESSENTIALBUSINESS_SERVER_MGMTSVC = 0x0000003D, // Windows Essential Server Solution Management SVC + HOME_BASIC = 0x00000002, // Home Basic + HOME_BASIC_E = 0x00000043, // Not supported + HOME_BASIC_N = 0x00000005, // Home Basic N + HOME_PREMIUM = 0x00000003, // Home Premium + HOME_PREMIUM_E = 0x00000044, // Not supported + HOME_PREMIUM_N = 0x0000001A, // Home Premium N + HOME_PREMIUM_SERVER = 0x00000022, // Windows Home Server 2011 + HOME_SERVER = 0x00000013, // Windows Storage Server 2008 R2 Essentials + HYPERV = 0x0000002A, // Microsoft Hyper-V Server + IOTENTERPRISE = 0x000000BC, // Windows IoT Enterprise + IOTENTERPRISE_S = 0x000000BF, // Windows IoT Enterprise LTSC + IOTUAP = 0x0000007B, // Windows 10 IoT Core + IOTUAPCOMMERCIAL = 0x00000083, // Windows 10 IoT Core Commercial + MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E, // Windows Essential Business Server Management Server + MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020, // Windows Essential Business Server Messaging Server + MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F, // Windows Essential Business Server Security Server + MOBILE_CORE = 0x00000068, // Windows 10 Mobile + MOBILE_ENTERPRISE = 0x00000085, // Windows 10 Mobile Enterprise + MULTIPOINT_PREMIUM_SERVER = 0x0000004D, // Windows MultiPoint Server Premium (full installation) + MULTIPOINT_STANDARD_SERVER = 0x0000004C, // Windows MultiPoint Server Standard (full installation) + PRO_WORKSTATION = 0x000000A1, // Windows 10 Pro for Workstations + PRO_WORKSTATION_N = 0x000000A2, // Windows 10 Pro for Workstations N + PROFESSIONAL = 0x00000030, // Windows 10 Pro + PROFESSIONAL_E = 0x00000045, // Not supported + PROFESSIONAL_N = 0x00000031, // Windows 10 Pro N + PROFESSIONAL_WMC = 0x00000067, // Professional with Media Center + SB_SOLUTION_SERVER = 0x00000032, // Windows Small Business Server 2011 Essentials + SB_SOLUTION_SERVER_EM = 0x00000036, // Server For SB Solutions EM + SERVER_FOR_SB_SOLUTIONS = 0x00000033, // Server For SB Solutions + SERVER_FOR_SB_SOLUTIONS_EM = 0x00000037, // Server For SB Solutions EM + SERVER_FOR_SMALLBUSINESS = 0x00000018, // Windows Server 2008 for Windows Essential Server Solutions + SERVER_FOR_SMALLBUSINESS_V = 0x00000023, // Windows Server 2008 without Hyper-V for Windows Essential Server Solutions + SERVER_FOUNDATION = 0x00000021, // Server Foundation + SMALLBUSINESS_SERVER = 0x00000009, // Windows Small Business Server + SMALLBUSINESS_SERVER_PREMIUM = 0x00000019, // Small Business Server Premium + SMALLBUSINESS_SERVER_PREMIUM_CORE = 0x0000003F, // Small Business Server Premium (core installation) + SOLUTION_EMBEDDEDSERVER = 0x00000038, // Windows MultiPoint Server + STANDARD_EVALUATION_SERVER = 0x0000004F, // Server Standard (evaluation installation) + STANDARD_SERVER = 0x00000007, // Server Standard (full installation. For Server Core installations of Windows Server 2012 and later, use the method, Determining whether Server Core is running.) + STANDARD_SERVER_CORE = 0x0000000D, // Server Standard (core installation, Windows Server 2008 R2 and earlier) + STANDARD_SERVER_CORE_V = 0x00000028, // Server Standard without Hyper-V (core installation) + STANDARD_SERVER_V = 0x00000024, // Server Standard without Hyper-V + STANDARD_SERVER_SOLUTIONS = 0x00000034, // Server Solutions Premium + STANDARD_SERVER_SOLUTIONS_CORE = 0x00000035, // Server Solutions Premium (core installation) + STARTER = 0x0000000B, // Starter + STARTER_E = 0x00000042, // Not supported + STARTER_N = 0x0000002F, // Starter N + STORAGE_ENTERPRISE_SERVER = 0x00000017, // Storage Server Enterprise + STORAGE_ENTERPRISE_SERVER_CORE = 0x0000002E, // Storage Server Enterprise (core installation) + STORAGE_EXPRESS_SERVER = 0x00000014, // Storage Server Express + STORAGE_EXPRESS_SERVER_CORE = 0x0000002B, // Storage Server Express (core installation) + STORAGE_STANDARD_EVALUATION_SERVER = 0x00000060, // Storage Server Standard (evaluation installation) + STORAGE_STANDARD_SERVER = 0x00000015, // Storage Server Standard + STORAGE_STANDARD_SERVER_CORE = 0x0000002C, // Storage Server Standard (core installation) + STORAGE_WORKGROUP_EVALUATION_SERVER = 0x0000005F, // Storage Server Workgroup (evaluation installation) + STORAGE_WORKGROUP_SERVER = 0x00000016, // Storage Server Workgroup + STORAGE_WORKGROUP_SERVER_CORE = 0x0000002D, // Storage Server Workgroup (core installation) + ULTIMATE = 0x00000001, // Ultimate + ULTIMATE_E = 0x00000047, // Not supported + ULTIMATE_N = 0x0000001C, // Ultimate N + UNDEFINED = 0x00000000, // An unknown product + WEB_SERVER = 0x00000011, // Web Server (full installation) + WEB_SERVER_CORE = 0x0000001D, // Web Server (core installation) +} + +ENABLE_ECHO_INPUT : DWORD : 0x0004 +ENABLE_INSERT_MODE : DWORD : 0x0020 +ENABLE_LINE_INPUT : DWORD : 0x0002 +ENABLE_MOUSE_INPUT : DWORD : 0x0010 +ENABLE_PROCESSED_INPUT : DWORD : 0x0001 +ENABLE_QUICK_EDIT_MODE : DWORD : 0x0040 +ENABLE_WINDOW_INPUT : DWORD : 0x0008 +ENABLE_VIRTUAL_TERMINAL_INPUT : DWORD : 0x0200 +ENABLE_PROCESSED_OUTPUT : DWORD : 0x0001 +ENABLE_WRAP_AT_EOL_OUTPUT : DWORD : 0x0002 +ENABLE_VIRTUAL_TERMINAL_PROCESSING : DWORD : 0x0004 +DISABLE_NEWLINE_AUTO_RETURN : DWORD : 0x0008 +ENABLE_LVB_GRID_WORLDWIDE : DWORD : 0x0010 + +CTRL_C_EVENT : DWORD : 0 +CTRL_BREAK_EVENT : DWORD : 1 +CTRL_CLOSE_EVENT : DWORD : 2 +CTRL_LOGOFF_EVENT : DWORD : 5 +CTRL_SHUTDOWN_EVENT : DWORD : 6 + +COORD :: struct { + X: SHORT, + Y: SHORT, +} diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 4b3d3e68e..d5ad9c378 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -190,6 +190,14 @@ foreign user32 { SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL --- CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT --- EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL --- + + DefRawInputProc :: proc(paRawInput: ^PRAWINPUT, nInput: INT, cbSizeHeader: UINT) -> LRESULT --- + GetRawInputBuffer :: proc(pRawInput: PRAWINPUT, pcbSize: PUINT, cbSizeHeader: UINT) -> UINT --- + GetRawInputData :: proc(hRawInput: HRAWINPUT, uiCommand: UINT, pData: LPVOID, pcbSize: PUINT, cbSizeHeader: UINT) -> UINT --- + GetRawInputDeviceInfoW :: proc(hDevice: HANDLE, uiCommand: UINT, pData: LPVOID, pcbSize: PUINT) -> UINT --- + GetRawInputDeviceList :: proc(pRawInputDeviceList: PRAWINPUTDEVICELIST, puiNumDevices: PUINT, cbSize: UINT) -> UINT --- + GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT --- + RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL --- } CreateWindowW :: #force_inline proc "stdcall" ( @@ -277,3 +285,146 @@ DPI_AWARENESS_CONTEXT_SYSTEM_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(1)) 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 + +RAWINPUTHEADER :: struct { + dwType: DWORD, + dwSize: DWORD, + hDevice: HANDLE, + wParam: WPARAM, +} + +RAWHID :: struct { + dwSizeHid: DWORD, + dwCount: DWORD, + bRawData: [1]BYTE, +} + +RAWMOUSE :: struct { + usFlags: USHORT, + DUMMYUNIONNAME: struct #raw_union { + ulButtons: ULONG, + DUMMYSTRUCTNAME: struct { + usButtonFlags: USHORT, + usButtonData: USHORT, + }, + }, + ulRawButtons: ULONG, + lLastX: LONG, + lLastY: LONG, + ulExtraInformation: ULONG, +} + +RAWKEYBOARD :: struct { + MakeCode: USHORT, + Flags: USHORT, + Rserved: USHORT, + VKey: USHORT, + Message: UINT, + ExtraInformation: ULONG, +} + +RAWINPUT :: struct { + header: RAWINPUTHEADER, + data: struct #raw_union { + mouse: RAWMOUSE, + keyboard: RAWKEYBOARD, + hid: RAWHID, + }, +} + +PRAWINPUT :: ^RAWINPUT +HRAWINPUT :: distinct LPARAM + +RAWINPUTDEVICE :: struct { + usUsagePage: USHORT, + usUsage: USHORT, + dwFlags: DWORD, + hwndTarget: HWND, +} + +PRAWINPUTDEVICE :: ^RAWINPUTDEVICE +PCRAWINPUTDEVICE :: PRAWINPUTDEVICE + +RAWINPUTDEVICELIST :: struct { + hDevice: HANDLE, + dwType: DWORD, +} + +PRAWINPUTDEVICELIST :: ^RAWINPUTDEVICELIST + +RID_DEVICE_INFO_HID :: struct { + dwVendorId: DWORD, + dwProductId: DWORD, + dwVersionNumber: DWORD, + usUsagePage: USHORT, + usUsage: USHORT, +} + +RID_DEVICE_INFO_KEYBOARD :: struct { + dwType: DWORD, + dwSubType: DWORD, + dwKeyboardMode: DWORD, + dwNumberOfFunctionKeys: DWORD, + dwNumberOfIndicators: DWORD, + dwNumberOfKeysTotal: DWORD, +} + +RID_DEVICE_INFO_MOUSE :: struct { + dwId: DWORD, + dwNumberOfButtons: DWORD, + dwSampleRate: DWORD, + fHasHorizontalWheel: BOOL, +} + +RID_DEVICE_INFO :: struct { + cbSize: DWORD, + dwType: DWORD, + DUMMYUNIONNAME: struct #raw_union { + mouse: RID_DEVICE_INFO_MOUSE, + keyboard: RID_DEVICE_INFO_KEYBOARD, + hid: RID_DEVICE_INFO_HID, + }, +} + +RIDEV_REMOVE :: 0x00000001 +RIDEV_EXCLUDE :: 0x00000010 +RIDEV_PAGEONLY :: 0x00000020 +RIDEV_NOLEGACY :: 0x00000030 +RIDEV_INPUTSINK :: 0x00000100 +RIDEV_CAPTUREMOUSE :: 0x00000200 +RIDEV_NOHOTKEYS :: 0x00000200 +RIDEV_APPKEYS :: 0x00000400 +RIDEV_EXINPUTSINK :: 0x00001000 +RIDEV_DEVNOTIFY :: 0x00002000 + +RID_HEADER :: 0x10000005 +RID_INPUT :: 0x10000003 + +RIM_TYPEMOUSE :: 0 +RIM_TYPEKEYBOARD :: 1 +RIM_TYPEHID :: 2 + +MOUSE_MOVE_RELATIVE :: 0x00 +MOUSE_MOVE_ABSOLUTE :: 0x01 +MOUSE_VIRTUAL_DESKTOP :: 0x02 +MOUSE_ATTRIUBTTES_CHANGED :: 0x04 +MOUSE_MOVE_NOCOALESCE :: 0x08 + +RI_MOUSE_BUTTON_1_DOWN :: 0x0001 +RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN +RI_MOUSE_BUTTON_1_UP :: 0x0002 +RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP +RI_MOUSE_BUTTON_2_DOWN :: 0x0004 +RI_MOUSE_RIGHT_BUTTON_DOWN :: RI_MOUSE_BUTTON_2_DOWN +RI_MOUSE_BUTTON_2_UP :: 0x0008 +RI_MOUSE_RIGHT_BUTTON_UP :: RI_MOUSE_BUTTON_2_UP +RI_MOUSE_BUTTON_3_DOWN :: 0x0010 +RI_MOUSE_MIDDLE_BUTTON_DOWN :: RI_MOUSE_BUTTON_3_DOWN +RI_MOUSE_BUTTON_3_UP :: 0x0020 +RI_MOUSE_MIDDLE_BUTTON_UP :: RI_MOUSE_BUTTON_3_UP +RI_MOUSE_BUTTON_4_DOWN :: 0x0040 +RI_MOUSE_BUTTON_4_UP :: 0x0080 +RI_MOUSE_BUTTON_5_DOWN :: 0x0100 +RI_MOUSE_BUTTON_5_UP :: 0x0200 +RI_MOUSE_WHEEL :: 0x0400 +RI_MOUSE_HWHEEL :: 0x0800 diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index 2bdd72ed2..36383a2c7 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -73,10 +73,10 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) // If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated // and will scan it to find the first null terminated character. The resulting string will - // also null terminated. + // also be null terminated. // If N != -1 it assumes the wide string is not null terminated and the resulting string - // will not be null terminated, we therefore have to force it to be null terminated manually. - text := make([]byte, n+1 if N != -1 else n) or_return + // will not be null terminated. + text := make([]byte, n) or_return n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil) if n1 == 0 { diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin index 7bd0bfe9f..f552b9a53 100644 --- a/core/sys/windows/winerror.odin +++ b/core/sys/windows/winerror.odin @@ -42,6 +42,8 @@ ERROR_TIMEOUT : DWORD : 1460 ERROR_DATATYPE_MISMATCH : DWORD : 1629 ERROR_UNSUPPORTED_TYPE : DWORD : 1630 ERROR_NOT_SAME_OBJECT : DWORD : 1656 +ERROR_PIPE_CONNECTED : DWORD : 0x80070217 +ERROR_PIPE_BUSY : DWORD : 231 E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001 diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 9f347fad3..1f56431c1 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -109,6 +109,8 @@ import i18n "core:text/i18n" import thread "core:thread" import time "core:time" +import sysinfo "core:sys/info" + import unicode "core:unicode" import utf8 "core:unicode/utf8" import utf8string "core:unicode/utf8/utf8string" @@ -206,6 +208,7 @@ _ :: scanner _ :: i18n _ :: thread _ :: time +_ :: sysinfo _ :: unicode _ :: utf8 _ :: utf8string diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 02a2b1ba2..f4f1b51be 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -22,6 +22,10 @@ #include #endif +#if defined(GB_SYSTEM_FREEBSD) + #include +#endif + /* NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`. */ @@ -217,7 +221,6 @@ void report_cpu_info() { Report the amount of installed RAM. */ void report_ram_info() { - gb_printf("\tRAM: "); #if defined(GB_SYSTEM_WINDOWS) @@ -243,247 +246,225 @@ void report_ram_info() { uint64_t ram_amount; size_t val_size = sizeof(ram_amount); - int sysctls[] = { CTL_HW, HW_MEMSIZE }; - if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) { + int mibs[] = { CTL_HW, HW_MEMSIZE }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } #elif defined(GB_SYSTEM_OPENBSD) uint64_t ram_amount; size_t val_size = sizeof(ram_amount); - int sysctls[] = { CTL_HW, HW_PHYSMEM64 }; - if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) { + int mibs[] = { CTL_HW, HW_PHYSMEM64 }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1)); } + #elif defined(GB_SYSTEM_FREEBSD) + uint64_t ram_amount; + size_t val_size = sizeof(ram_amount); + + int mibs[] = { CTL_HW, HW_PHYSMEM }; + if (sysctl(mibs, 2, &ram_amount, &val_size, NULL, 0) != -1) { + gb_printf("%lu MiB\n", ram_amount / gb_megabytes(1)); + } #else gb_printf("Unknown.\n"); #endif } -/* - NOTE(Jeroen): `odin report` prints some system information for easier bug reporting. -*/ -void print_bug_report_help() { - gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n"); - gb_printf("\tWebsite: https://odin-lang.org\n"); - gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n"); - /* - Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site. - gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n"); - */ - gb_printf("\n\n"); - - gb_printf("Useful information to add to a bug report:\n\n"); - - gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION)); - - #ifdef NIGHTLY - gb_printf("-nightly"); - #endif - - #ifdef GIT_SHA - gb_printf(":%s", GIT_SHA); - #endif - - gb_printf("\n"); - - /* - Print OS information. - */ +void report_os_info() { gb_printf("\tOS: "); #if defined(GB_SYSTEM_WINDOWS) - /* - NOTE(Jeroen): - `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. - `RtlGetVersion` will return the true version. + /* + NOTE(Jeroen): + `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. + `RtlGetVersion` will return the true version. - Rather than include the WinDDK, we ask the kernel directly. + Rather than include the WinDDK, we ask the kernel directly. - `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release) + `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion` is for the minor build version (Update Build Release) - */ - OSVERSIONINFOEXW osvi; - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + */ + OSVERSIONINFOEXW osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEXW)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); - typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*); - typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType); + typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(OSVERSIONINFOW*); + typedef BOOL (WINAPI* GetProductInfoPtr)(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion, PDWORD pdwReturnedProductType); - // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion - RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion"); - // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo - GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); + // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion + RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion"); + // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo + GetProductInfoPtr GetProductInfo = (GetProductInfoPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); - NTSTATUS status = {}; - DWORD ProductType = {}; - if (RtlGetVersion != nullptr) { - status = RtlGetVersion((OSVERSIONINFOW*)&osvi); + NTSTATUS status = {}; + DWORD ProductType = {}; + if (RtlGetVersion != nullptr) { + status = RtlGetVersion((OSVERSIONINFOW*)&osvi); + } + + if (RtlGetVersion == nullptr || status != 0x0) { + gb_printf("Windows (Unknown Version)"); + } else { + if (GetProductInfo != nullptr) { + GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType); } - if (RtlGetVersion == nullptr || status != 0x0) { - gb_printf("Windows (Unknown Version)"); - } else { - if (GetProductInfo != nullptr) { - GetProductInfo(osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.wServicePackMajor, osvi.wServicePackMinor, &ProductType); - } + if (false) { + gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion); + gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion); + gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber); + gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId); + gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor); + gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor); + gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask); + gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType); + } - if (false) { - gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion); - gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion); - gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber); - gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId); - gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor); - gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor); - gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask); - gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType); - } + gb_printf("Windows "); - gb_printf("Windows "); - - switch (osvi.dwMajorVersion) { - case 10: - /* - Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server - */ - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: // Workstation - if (osvi.dwBuildNumber < 22000) { - gb_printf("10 "); - } else { - gb_printf("11 "); - } - - report_windows_product_type(ProductType); + switch (osvi.dwMajorVersion) { + case 10: + /* + Windows 10 (Pro), Windows 2016 Server, Windows 2019 Server, Windows 2022 Server + */ + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: // Workstation + if (osvi.dwBuildNumber < 22000) { + gb_printf("10 "); + } else { + gb_printf("11 "); + } + + report_windows_product_type(ProductType); + break; + default: // Server or Domain Controller + switch(osvi.dwBuildNumber) { + case 14393: + gb_printf("2016 Server"); break; - default: // Server or Domain Controller - switch(osvi.dwBuildNumber) { - case 14393: - gb_printf("2016 Server"); - break; - case 17763: - gb_printf("2019 Server"); - break; - case 20348: - gb_printf("2022 Server"); - break; - default: - gb_printf("Unknown Server"); - break; + case 17763: + gb_printf("2019 Server"); + break; + case 20348: + gb_printf("2022 Server"); + break; + default: + gb_printf("Unknown Server"); + break; + } + } + break; + case 6: + switch (osvi.dwMinorVersion) { + case 0: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows Vista "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2008"); + break; } - } - break; - case 6: - switch (osvi.dwMinorVersion) { - case 0: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows Vista "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2008"); - break; - } - break; + break; - case 1: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows 7 "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2008 R2"); - break; - } - break; - case 2: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows 8 "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2012"); - break; - } - break; - case 3: - switch (osvi.wProductType) { - case VER_NT_WORKSTATION: - gb_printf("Windows 8.1 "); - report_windows_product_type(ProductType); - break; - case 3: - gb_printf("Windows Server 2012 R2"); - break; - } - break; - } - break; - case 5: - switch (osvi.dwMinorVersion) { - case 0: - gb_printf("Windows 2000"); - break; - case 1: - gb_printf("Windows XP"); - break; - case 2: - gb_printf("Windows Server 2003"); - break; - } - break; - default: - break; + case 1: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 7 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2008 R2"); + break; + } + break; + case 2: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 8 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2012"); + break; + } + break; + case 3: + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + gb_printf("Windows 8.1 "); + report_windows_product_type(ProductType); + break; + case 3: + gb_printf("Windows Server 2012 R2"); + break; + } + break; } - - /* - Grab Windows DisplayVersion (like 20H02) - */ - LPDWORD ValueType = {}; - DWORD UBR; - char DisplayVersion[256]; - DWORD ValueSize = 256; - - status = RegGetValue( - HKEY_LOCAL_MACHINE, - TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), - TEXT("DisplayVersion"), - RRF_RT_REG_SZ, - ValueType, - DisplayVersion, - &ValueSize - ); - - if (status == 0x0) { - gb_printf(" (version: %s)", DisplayVersion); + break; + case 5: + switch (osvi.dwMinorVersion) { + case 0: + gb_printf("Windows 2000"); + break; + case 1: + gb_printf("Windows XP"); + break; + case 2: + gb_printf("Windows Server 2003"); + break; } - - /* - Now print build number. - */ - gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber); - - ValueSize = sizeof(UBR); - status = RegGetValue( - HKEY_LOCAL_MACHINE, - TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), - TEXT("UBR"), - RRF_RT_REG_DWORD, - ValueType, - &UBR, - &ValueSize - ); - - if (status == 0x0) { - gb_printf(".%u", cast(unsigned)UBR); - } - gb_printf("\n"); + break; + default: + break; } + /* + Grab Windows DisplayVersion (like 20H02) + */ + LPDWORD ValueType = {}; + DWORD UBR; + char DisplayVersion[256]; + DWORD ValueSize = 256; + + status = RegGetValue( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + TEXT("DisplayVersion"), + RRF_RT_REG_SZ, + ValueType, + DisplayVersion, + &ValueSize + ); + + if (status == 0x0) { + gb_printf(" (version: %s)", DisplayVersion); + } + + /* + Now print build number. + */ + gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber); + + ValueSize = sizeof(UBR); + status = RegGetValue( + HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + TEXT("UBR"), + RRF_RT_REG_DWORD, + ValueType, + &UBR, + &ValueSize + ); + + if (status == 0x0) { + gb_printf(".%u", cast(unsigned)UBR); + } + gb_printf("\n"); + } #elif defined(GB_SYSTEM_LINUX) /* Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS` @@ -539,123 +520,411 @@ void print_bug_report_help() { gb_printf(", %s %s\n", info->sysname, info->release); #elif defined(GB_SYSTEM_OSX) - b32 found = 1; - uint32_t major, minor; + struct Darwin_To_Release { + const char* build; // 21G83 + int darwin[3]; // Darwin kernel triplet + const char* os_name; // OS X, MacOS + struct { + const char* name; // Monterey, Mojave, etc. + int version[3]; // 12.4, etc. + } release; + }; - #define osx_version_buffer 100 - char buffer[osx_version_buffer]; - size_t buffer_size = osx_version_buffer - 1; - #undef osx_version_buffer + Darwin_To_Release macos_release_map[] = { + {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, + {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}}, + {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, + {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}}, + {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}}, + {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}}, + {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, + {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}}, + {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, + {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}}, + {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, + {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}}, + {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}}, + {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, + {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}}, + {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, + {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}}, + {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}}, + {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, + {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}}, + {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}}, + {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}}, + {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}}, + {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, + {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}}, + {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}}, + {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}}, + {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}}, + {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}}, + {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}}, + {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, + {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}}, + {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, + {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}}, + {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}}, + {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}}, + {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}}, + {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}}, + {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}}, + {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}}, + {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}}, + {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, + {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}}, + {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, + {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}}, + {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, + {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}}, + {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, + {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}}, + {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}}, + {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}}, + {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}}, + {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, + {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}}, + {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}}, + {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}}, + {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}}, + {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}}, + {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}}, + {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}}, + {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}}, + {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}}, + {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, + {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}}, + {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}}, + {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}}, + {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}}, + {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}}, + {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}}, + {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}}, + {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, + {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}}, + {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}}, + {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}}, + {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}}, + {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}}, + {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}}, + {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}}, + {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}}, + {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}}, + {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}}, + {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}}, + {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, + {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}}, + {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, + {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}}, + {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}}, + {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}}, + {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, + {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}}, + {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}}, + {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, + {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}}, + {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}}, + {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}}, + {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}}, + {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, + {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}}, + {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}}, + {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}}, + {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}}, + {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}}, + {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}}, + {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}}, + {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, + {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}}, + {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}}, + {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}}, + {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}}, + {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}}, + {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, + {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}}, + {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}}, + {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, + {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}}, + {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, + {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}}, + {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, + {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}}, + {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}}, + {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}}, + {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, + {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}}, + {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}}, + {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}}, + {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, + {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}}, + {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}}, + {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}}, + {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}}, + {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}}, + {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}}, + {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}}, + {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}}, + {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}}, + {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}}, + {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}}, + {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}}, + {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}}, + {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}}, + {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}}, + {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}}, + {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}}, + {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}}, + {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}}, + {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}}, + {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}}, + {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}}, + {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}}, + {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}}, + {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}}, + {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}}, + {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}}, + {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}}, + }; - int sysctls[] = { CTL_KERN, KERN_OSRELEASE }; - if (sysctl(sysctls, 2, buffer, &buffer_size, NULL, 0) == -1) { - found = 0; + + b32 build_found = 1; + b32 darwin_found = 1; + uint32_t major, minor, patch; + + #define MACOS_VERSION_BUFFER_SIZE 100 + char build_buffer[MACOS_VERSION_BUFFER_SIZE]; + char darwin_buffer[MACOS_VERSION_BUFFER_SIZE]; + size_t build_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1; + size_t darwin_buffer_size = MACOS_VERSION_BUFFER_SIZE - 1; + #undef MACOS_VERSION_BUFFER_SIZE + + int build_mibs[] = { CTL_KERN, KERN_OSVERSION }; + if (sysctl(build_mibs, 2, build_buffer, &build_buffer_size, NULL, 0) == -1) { + build_found = 0; + } + + int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE }; + if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) { + gb_printf("macOS Unknown\n"); + return; } else { - if (sscanf(buffer, "%u.%u", &major, &minor) != 2) { - found = 0; + if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) { + darwin_found = 0; } } - if (found) { - switch (major) { - case 21: - /* - macOS Big Sur and newer - */ - major -= 9; + // Scan table for match on BUILD + int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]); + Darwin_To_Release match = {}; - gb_printf("macOS 12.0.%d Monterey\n", minor); + for (int build = 0; build < macos_release_count; build++) { + Darwin_To_Release rel = macos_release_map[build]; - break; - case 20: - /* - macOS Big Sur and newer - */ - major -= 9; - - gb_printf("macOS 11.%d Big Sur\n", minor); - - break; - case 14: - /* - macOS 10.1.1 and newer - */ - major -= 4; - - switch (minor) { - case 1: - gb_printf("macOS Puma 10.1\n"); - break; - - case 2: - gb_printf("macOS Jaguar 10.2\n"); - break; - - case 3: - gb_printf("macOS Panther 10.3\n"); - break; - - case 4: - gb_printf("macOS Tiger 10.4\n"); - break; - - case 5: - gb_printf("macOS Leopard 10.5\n"); - break; - - case 6: - gb_printf("macOS Snow Leopard 10.6\n"); - break; - - case 7: - gb_printf("macOS Lion 10.7\n"); - break; - - case 8: - gb_printf("macOS Mountain Lion 10.8\n"); - break; - - case 9: - gb_printf("macOS Mavericks 10.9\n"); - break; - - case 10: - gb_printf("macOS Yosemite 10.10\n"); - break; - - case 11: - gb_printf("macOS El Capitan 10.11\n"); - break; - - case 12: - gb_printf("macOS Sierra 10.12\n"); - break; - - case 13: - gb_printf("macOS High Sierra 10.13\n"); - break; - - case 14: - gb_printf("macOS Mojave 10.14\n"); - break; - - case 15: - gb_printf("macOS Catalina 10.15\n"); - break; - - default: - gb_printf("macOS %d.%d\n", major, minor); - break; - } - - break; - default: - gb_printf("macOS Unknown (kernel version %d.%d)\n", major, minor); + // Do we have an exact match on the BUILD? + if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) { + match = rel; break; } - } else { - gb_printf("macOS: Unknown\n"); - } + + // Do we have an exact Darwin match? + if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) { + match = rel; + break; + } + + // Major kernel version needs to match exactly, + if (rel.darwin[0] == major) { + // No major version match yet. + if (!match.os_name) { + match = rel; + } + if (minor >= rel.darwin[1]) { + match = rel; + if (patch >= rel.darwin[2]) { + match = rel; + } + } + } + } + + if (match.os_name) { + gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]); + if (match.release.version[1] > 0 || match.release.version[2] > 0) { + gb_printf(".%d", match.release.version[1]); + } + if (match.release.version[2] > 0) { + gb_printf(".%d", match.release.version[2]); + } + if (build_found) { + gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]); + } else { + gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]); + } + return; + } + + if (build_found && darwin_found) { + gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch); + return; + } else if (build_found) { + gb_printf("macOS Unknown (build: %s)\n", build_buffer); + return; + } else if (darwin_found) { + gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch); + return; + } #elif defined(GB_SYSTEM_OPENBSD) struct utsname un; @@ -664,10 +933,67 @@ void print_bug_report_help() { } else { gb_printf("OpenBSD: Unknown\n"); } - #else - gb_printf("Unknown\n"); + #elif defined(GB_SYSTEM_FREEBSD) + #define freebsd_version_buffer 129 + char buffer[freebsd_version_buffer]; + size_t buffer_size = freebsd_version_buffer - 1; + #undef freebsd_version_buffer + int mibs[] = { CTL_KERN, KERN_VERSION }; + if (sysctl(mibs, 2, buffer, &buffer_size, NULL, 0) == -1) { + gb_printf("FreeBSD: Unknown\n"); + } else { + // KERN_VERSION can end in a \n, replace it with a \0 + for (int i = 0; i < buffer_size; i += 1) { + if (buffer[i] == '\n') buffer[i] = 0; + } + gb_printf("%s", &buffer[0]); + + // Retrieve kernel revision using `sysctl`, e.g. 199506 + mibs[1] = KERN_OSREV; + uint64_t revision; + size_t revision_size = sizeof(revision); + + if (sysctl(mibs, 2, &revision, &revision_size, NULL, 0) == -1) { + gb_printf("\n"); + } else { + gb_printf(", revision %ld\n", revision); + } + } + #else + gb_printf("Unknown"); #endif +} + +// NOTE(Jeroen): `odin report` prints some system information for easier bug reporting. +void print_bug_report_help() { + gb_printf("Where to find more information and get into contact when you encounter a bug:\n\n"); + gb_printf("\tWebsite: https://odin-lang.org\n"); + gb_printf("\tGitHub: https://github.com/odin-lang/Odin/issues\n"); + /* + Uncomment and update URL once we have a Discord vanity URL. For now people can get here from the site. + gb_printf("\tDiscord: https://discord.com/invite/sVBPHEv\n"); + */ + gb_printf("\n\n"); + + gb_printf("Useful information to add to a bug report:\n\n"); + + gb_printf("\tOdin: %.*s", LIT(ODIN_VERSION)); + + #ifdef NIGHTLY + gb_printf("-nightly"); + #endif + + #ifdef GIT_SHA + gb_printf(":%s", GIT_SHA); + #endif + + gb_printf("\n"); + + /* + Print OS information. + */ + report_os_info(); /* Now print CPU info. @@ -678,4 +1004,4 @@ void print_bug_report_help() { And RAM info. */ report_ram_info(); -} +} \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 9d5c3e556..b877b8cb7 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -116,6 +116,7 @@ struct TargetMetrics { TargetArchKind arch; isize word_size; isize max_align; + isize max_simd_align; String target_triplet; String target_data_layout; TargetABIKind abi; @@ -224,6 +225,7 @@ struct BuildContext { String ODIN_VENDOR; // compiler vendor String ODIN_VERSION; // compiler version String ODIN_ROOT; // Odin ROOT + String ODIN_BUILD_PROJECT_NAME; // Odin main/initial package's directory name bool ODIN_DEBUG; // Odin in debug mode bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing) @@ -235,8 +237,9 @@ struct BuildContext { TargetEndianKind endian_kind; // In bytes - i64 word_size; // Size of a pointer, must be >= 4 - i64 max_align; // max alignment, must be >= 1 (and typically >= word_size) + i64 word_size; // Size of a pointer, must be >= 4 + i64 max_align; // max alignment, must be >= 1 (and typically >= word_size) + i64 max_simd_align; // max alignment, must be >= 1 (and typically >= word_size) CommandKind command_kind; String command; @@ -339,15 +342,13 @@ bool global_ignore_warnings(void) { gb_global TargetMetrics target_windows_i386 = { TargetOs_windows, TargetArch_i386, - 4, - 8, + 4, 4, 8, str_lit("i386-pc-windows-msvc"), }; gb_global TargetMetrics target_windows_amd64 = { TargetOs_windows, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-pc-windows-msvc"), str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), }; @@ -355,24 +356,21 @@ gb_global TargetMetrics target_windows_amd64 = { gb_global TargetMetrics target_linux_i386 = { TargetOs_linux, TargetArch_i386, - 4, - 8, + 4, 4, 8, str_lit("i386-pc-linux-gnu"), }; gb_global TargetMetrics target_linux_amd64 = { TargetOs_linux, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-pc-linux-gnu"), str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), }; gb_global TargetMetrics target_linux_arm64 = { TargetOs_linux, TargetArch_arm64, - 8, - 16, + 8, 8, 16, str_lit("aarch64-linux-elf"), str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), }; @@ -380,8 +378,7 @@ gb_global TargetMetrics target_linux_arm64 = { gb_global TargetMetrics target_linux_arm32 = { TargetOs_linux, TargetArch_arm32, - 4, - 8, + 4, 4, 8, str_lit("arm-linux-gnu"), str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"), }; @@ -389,8 +386,7 @@ gb_global TargetMetrics target_linux_arm32 = { gb_global TargetMetrics target_darwin_amd64 = { TargetOs_darwin, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-apple-darwin"), str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"), }; @@ -398,8 +394,7 @@ gb_global TargetMetrics target_darwin_amd64 = { gb_global TargetMetrics target_darwin_arm64 = { TargetOs_darwin, TargetArch_arm64, - 8, - 16, + 8, 8, 16, str_lit("arm64-apple-macosx11.0.0"), str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct? }; @@ -407,16 +402,14 @@ gb_global TargetMetrics target_darwin_arm64 = { gb_global TargetMetrics target_freebsd_i386 = { TargetOs_freebsd, TargetArch_i386, - 4, - 8, + 4, 4, 8, str_lit("i386-unknown-freebsd-elf"), }; gb_global TargetMetrics target_freebsd_amd64 = { TargetOs_freebsd, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-unknown-freebsd-elf"), str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), }; @@ -424,8 +417,7 @@ gb_global TargetMetrics target_freebsd_amd64 = { gb_global TargetMetrics target_openbsd_amd64 = { TargetOs_openbsd, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-unknown-openbsd-elf"), str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"), }; @@ -433,62 +425,48 @@ gb_global TargetMetrics target_openbsd_amd64 = { gb_global TargetMetrics target_essence_amd64 = { TargetOs_essence, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-pc-none-elf"), }; + gb_global TargetMetrics target_freestanding_wasm32 = { TargetOs_freestanding, TargetArch_wasm32, - 4, - 8, + 4, 8, 16, str_lit("wasm32-freestanding-js"), - str_lit(""), + str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"), }; gb_global TargetMetrics target_js_wasm32 = { TargetOs_js, TargetArch_wasm32, - 4, - 8, + 4, 8, 16, str_lit("wasm32-js-js"), - str_lit(""), -}; - -gb_global TargetMetrics target_js_wasm64 = { - TargetOs_js, - TargetArch_wasm64, - 8, - 16, - str_lit("wasm64-js-js"), - str_lit(""), + str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"), }; gb_global TargetMetrics target_wasi_wasm32 = { TargetOs_wasi, TargetArch_wasm32, - 4, - 8, + 4, 8, 16, str_lit("wasm32-wasi-js"), - str_lit(""), + str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"), }; -// gb_global TargetMetrics target_freestanding_wasm64 = { -// TargetOs_freestanding, -// TargetArch_wasm64, -// 8, -// 16, -// str_lit("wasm64-freestanding-js"), -// str_lit(""), -// }; +gb_global TargetMetrics target_js_wasm64 = { + TargetOs_js, + TargetArch_wasm64, + 8, 8, 16, + str_lit("wasm64-js-js"), + str_lit(""), +}; gb_global TargetMetrics target_freestanding_amd64_sysv = { TargetOs_freestanding, TargetArch_amd64, - 8, - 16, + 8, 8, 16, str_lit("x86_64-pc-none-gnu"), str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"), TargetABI_SysV, @@ -517,7 +495,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, - { str_lit("js_wasm64"), &target_js_wasm64 }, + // { str_lit("js_wasm64"), &target_js_wasm64 }, { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, }; @@ -853,10 +831,73 @@ String internal_odin_root_dir(void) { gb_mfree(argv); return make_string(nullptr, 0); } - // copy argv[0] to path_buf + + // NOTE(Jeroen): + // On OpenBSD, if `odin` is on the path, `argv[0]` will contain just `odin`, + // even though that isn't then the relative path. + // When run from Odin's directory, it returns `./odin`. + // Check argv[0] for lack of / to see if it's a reference to PATH. + // If so, walk PATH to find the executable. + len = gb_strlen(argv[0]); - if(len < path_buf.count) { - gb_memmove(&path_buf[0], argv[0], len); + + bool slash_found = false; + bool odin_found = false; + + for (int i = 0; i < len; i += 1) { + if (argv[0][i] == '/') { + slash_found = true; + break; + } + } + + if (slash_found) { + // copy argv[0] to path_buf + if(len < path_buf.count) { + gb_memmove(&path_buf[0], argv[0], len); + odin_found = true; + } + } else { + gbAllocator a = heap_allocator(); + char const *path_env = gb_get_env("PATH", a); + defer (gb_free(a, cast(void *)path_env)); + + if (path_env) { + int path_len = gb_strlen(path_env); + int path_start = 0; + int path_end = 0; + + for (; path_start < path_len; ) { + for (; path_end <= path_len; path_end++) { + if (path_env[path_end] == ':' || path_end == path_len) { + break; + } + } + String path_needle = (const String)make_string((const u8 *)(path_env + path_start), path_end - path_start); + String argv0 = (const String)make_string((const u8 *)argv[0], len); + String odin_candidate = concatenate3_strings(a, path_needle, STR_LIT("/"), argv0); + defer (gb_free(a, odin_candidate.text)); + + if (gb_file_exists((const char *)odin_candidate.text)) { + len = odin_candidate.len; + if(len < path_buf.count) { + gb_memmove(&path_buf[0], odin_candidate.text, len); + } + odin_found = true; + break; + } + + path_start = path_end + 1; + path_end = path_start; + if (path_start > path_len) { + break; + } + } + } + + if (!odin_found) { + gb_printf_err("Odin could not locate itself in PATH, and ODIN_ROOT wasn't set either.\n"); + } } gb_mfree(argv); #endif @@ -1084,14 +1125,16 @@ void init_build_context(TargetMetrics *cross_target) { GB_ASSERT(metrics->arch != TargetArch_Invalid); GB_ASSERT(metrics->word_size > 1); GB_ASSERT(metrics->max_align > 1); + GB_ASSERT(metrics->max_simd_align > 1); bc->metrics = *metrics; - bc->ODIN_OS = target_os_names[metrics->os]; - bc->ODIN_ARCH = target_arch_names[metrics->arch]; - bc->endian_kind = target_endians[metrics->arch]; - bc->word_size = metrics->word_size; - bc->max_align = metrics->max_align; + bc->ODIN_OS = target_os_names[metrics->os]; + bc->ODIN_ARCH = target_arch_names[metrics->arch]; + bc->endian_kind = target_endians[metrics->arch]; + bc->word_size = metrics->word_size; + bc->max_align = metrics->max_align; + bc->max_simd_align = metrics->max_simd_align; bc->link_flags = str_lit(" "); #if defined(DEFAULT_TO_THREADED_CHECKER) @@ -1495,6 +1538,12 @@ bool init_build_paths(String init_filename) { enable_target_feature({}, bc->target_features_string); } + { + String build_project_name = last_path_element(bc->build_paths[BuildPath_Main_Package].basename); + GB_ASSERT(build_project_name.len > 0); + bc->ODIN_BUILD_PROJECT_NAME = build_project_name; + } + return true; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 83136d576..f0db6afc4 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -341,6 +341,13 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call for (isize i = 2+arg_offset; i < ce->args.count; i++) { Operand x = {}; check_expr(c, &x, ce->args[i]); + if (is_type_untyped(x.type)) { + gbString e = expr_to_string(x.expr); + gbString t = type_to_string(x.type); + error(x.expr, "'%.*s' expects typed parameters, got %s of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + } param_types[i-arg_offset] = x.type; } @@ -4523,7 +4530,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x.mode != Addressing_Invalid) { convert_to_typed(c, &x, t_uintptr); } - if (!is_type_uintptr(operand->type)) { + convert_to_typed(c, &x, t_uintptr); + if (!is_type_uintptr(x.type)) { gbString t = type_to_string(x.type); error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t); gb_string_free(t); @@ -4605,6 +4613,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Type; break; + case BuiltinProc_type_convert_variants_to_pointers: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + if (is_type_polymorphic(bt)) { + // IGNORE polymorphic types + return true; + } else if (bt->kind != Type_Union) { + gbString t = type_to_string(operand->type); + error(operand->expr, "Expected a union type for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } else if (bt->Union.is_polymorphic) { + gbString t = type_to_string(operand->type); + error(operand->expr, "Expected a non-polymorphic union type for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *new_type = alloc_type_union(); + auto variants = slice_make(permanent_allocator(), bt->Union.variants.count); + for_array(i, bt->Union.variants) { + variants[i] = alloc_type_pointer(bt->Union.variants[i]); + } + new_type->Union.variants = variants; + + // NOTE(bill): Is this even correct? + new_type->Union.scope = bt->Union.scope; + + operand->type = new_type; + } + operand->mode = Addressing_Type; + break; case BuiltinProc_type_is_boolean: case BuiltinProc_type_is_integer: @@ -5390,10 +5438,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_valgrind_client_request: { - if (!is_arch_x86()) { - error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name)); - return false; - } + // NOTE(bill): Check it but make it a no-op for non x86 (i386, amd64) targets enum {ARG_COUNT = 7}; GB_ASSERT(builtin_procs[BuiltinProc_valgrind_client_request].arg_count == ARG_COUNT); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9c2d20781..38d17c131 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3039,8 +3039,8 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand x->type = xt; goto matrix_success; } else { - GB_ASSERT(is_type_matrix(yt)); GB_ASSERT(!is_type_matrix(xt)); + GB_ASSERT(is_type_matrix(yt)); if (op.kind == Token_Mul) { // NOTE(bill): no need to handle the matrix case here since it should be handled above @@ -3061,6 +3061,9 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count); } goto matrix_success; + } else if (are_types_identical(yt->Matrix.elem, xt)) { + x->type = check_matrix_type_hint(y->type, type_hint); + return; } } if (!are_types_identical(xt, yt)) { @@ -7359,6 +7362,14 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t return kind; } + if (x.mode == Addressing_Type || y.mode == Addressing_Type) { + Ast *type_expr = (x.mode == Addressing_Type) ? x.expr : y.expr; + gbString type_string = expr_to_string(type_expr); + error(node, "Type %s is invalid operand for ternary if expression", type_string); + gb_string_free(type_string); + return kind; + } + if (x.type == nullptr || x.type == t_invalid || y.type == nullptr || y.type == t_invalid) { return kind; @@ -7395,6 +7406,7 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t check_cast_internal(c, &y, type_hint)) { convert_to_typed(c, o, type_hint); update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint)); + o->type = type_hint; } } return kind; @@ -8769,7 +8781,10 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type Ast *first_arg = x.expr->SelectorExpr.expr; GB_ASSERT(first_arg != nullptr); - first_arg->state_flags |= StateFlag_SelectorCallExpr; + Entity *e = entity_of_node(se->expr); + if (!(e != nullptr && (e->kind == Entity_Procedure || e->kind == Entity_ProcGroup))) { + first_arg->state_flags |= StateFlag_SelectorCallExpr; + } Type *pt = base_type(x.type); GB_ASSERT(pt->kind == Type_Proc); @@ -9571,6 +9586,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Ast_MapType: case Ast_BitSetType: case Ast_MatrixType: + case Ast_RelativeType: o->mode = Addressing_Type; o->type = check_type(c, node); break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 451325324..9e4504dc1 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1396,6 +1396,23 @@ bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) { return id != BuiltinProc_Invalid; } +bool check_expr_is_stack_variable(Ast *expr) { + expr = unparen_expr(expr); + Entity *e = entity_of_node(expr); + if (e && e->kind == Entity_Variable) { + if (e->flags & (EntityFlag_Static|EntityFlag_Using|EntityFlag_ImplicitReference|EntityFlag_ForValue)) { + // okay + } else if (e->Variable.thread_local_model.len != 0) { + // okay + } else if (e->scope) { + if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) { + return true; + } + } + } + return false; +} + void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { u32 mod_flags = flags & (~Stmt_FallthroughAllowed); switch (node->kind) { @@ -1447,6 +1464,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { AstSelectorCallExpr *se = &expr->SelectorCallExpr; ast_node(ce, CallExpr, se->call); Type *t = base_type(type_of_expr(ce->proc)); + if (t == nullptr) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' is not a value field nor procedure", expr_str); + gb_string_free(expr_str); + return; + } if (t->kind == Type_Proc) { do_require = t->Proc.require_results; } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) { @@ -1678,6 +1701,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (is_type_untyped(o->type)) { update_untyped_expr_type(ctx, o->expr, e->type, true); } + + + // NOTE(bill): This is very basic escape analysis + // This needs to be improved tremendously, and a lot of it done during the + // middle-end (or LLVM side) to improve checks and error messages + Ast *expr = unparen_expr(o->expr); + if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) { + Ast *x = unparen_expr(expr->UnaryExpr.expr); + if (x->kind == Ast_CompoundLit) { + error(expr, "Cannot return the address to a stack value from a procedure"); + } else if (x->kind == Ast_IndexExpr) { + Ast *array = x->IndexExpr.expr; + if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) { + gbString t = type_to_string(type_of_expr(array)); + error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t); + gb_string_free(t); + } + } else { + if (check_expr_is_stack_variable(x)) { + error(expr, "Cannot return the address to a stack variable from a procedure"); + } + } + } } } case_end; @@ -1895,13 +1941,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } if (found == nullptr) { entity = alloc_entity_variable(ctx->scope, token, type, EntityState_Resolved); + entity->flags |= EntityFlag_ForValue; entity->flags |= EntityFlag_Value; - if (i == addressable_index) { - if (use_by_reference_for_value) { - entity->flags &= ~EntityFlag_Value; - } else { - entity->flags |= EntityFlag_ForValue; - } + if (i == addressable_index && use_by_reference_for_value) { + entity->flags &= ~EntityFlag_Value; } if (is_soa) { if (i == 0) { diff --git a/src/check_type.cpp b/src/check_type.cpp index da0a9706b..66417f97e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1643,8 +1643,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is bool valid = false; if (is_type_proc(op.type)) { Entity *proc_entity = entity_from_expr(op.expr); - valid = proc_entity != nullptr; - poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr); + valid = (proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure); + if (valid) { + poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr); + } } if (!valid) { if (op.mode == Addressing_Constant) { diff --git a/src/checker.cpp b/src/checker.cpp index a7470a4c9..16e480786 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -938,6 +938,7 @@ void init_universal(void) { add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR); add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION); add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); + add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME); { GlobalEnumValue values[TargetOs_COUNT] = { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 717422df1..b8cd84cc6 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -200,6 +200,8 @@ BuiltinProc__type_begin, BuiltinProc_type_core_type, BuiltinProc_type_elem_type, + BuiltinProc_type_convert_variants_to_pointers, + BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_boolean, BuiltinProc_type_is_integer, @@ -492,6 +494,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/common.cpp b/src/common.cpp index 77caddfe8..bc421b5fd 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -38,6 +38,10 @@ i64 next_pow2(i64 n); isize next_pow2_isize(isize n); void debugf(char const *fmt, ...); +#if defined(GB_SYSTEM_WINDOWS) && defined(GB_ARCH_32_BIT) +#error Odin on Windows requires a 64-bit build-system. The 'Developer Command Prompt' for VS still defaults to 32-bit shell. The 64-bit shell can be found under the name 'x64 Native Tools Command Prompt' for VS. For more information, please see https://odin-lang.org/docs/install/#for-windows +#endif + #include "threading.cpp" #include "unicode.cpp" #include "array.cpp" diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 2ee8dc673..4bdc31077 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -294,7 +294,7 @@ i64 lb_alignof(LLVMTypeRef type) { i64 elem_size = lb_sizeof(elem); i64 count = LLVMGetVectorSize(type); i64 size = count * elem_size; - return gb_clamp(next_pow2(size), 1, build_context.max_align); + return gb_clamp(next_pow2(size), 1, build_context.max_simd_align); } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6ee1541d6..4eb343fa7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1420,7 +1420,7 @@ void lb_generate_code(lbGenerator *gen) { } } - LLVMBool split_debug_inlining = false; + LLVMBool split_debug_inlining = build_context.build_mode == BuildMode_Assembly; LLVMBool debug_info_for_profiling = false; m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99, @@ -1615,6 +1615,7 @@ void lb_generate_code(lbGenerator *gen) { } if (is_foreign) { LLVMSetLinkage(g.value, LLVMExternalLinkage); + LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass); LLVMSetExternallyInitialized(g.value, true); lb_add_foreign_library_path(m, e->Variable.foreign_library); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 2d14070e2..5826dbaec 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -297,12 +297,16 @@ LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_ } if (!is_const) { + LLVMTypeRef llvm_elem_type = lb_type(m, elem_type); lbProcedure *p = m->curr_procedure; GB_ASSERT(p != nullptr); lbAddr v = lb_add_local_generated(p, type, false); lbValue ptr = lb_addr_get_ptr(p, v); for (isize i = 0; i < count; i++) { lbValue elem = lb_emit_array_epi(p, ptr, i); + if (is_type_proc(elem_type)) { + values[i] = LLVMConstPointerCast(values[i], llvm_elem_type); + } LLVMBuildStore(p->builder, values[i], elem.value); } return lb_addr_load(p, v).value; diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 29e074473..ea7322297 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -1042,7 +1042,8 @@ void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an // instruction "before" it. - LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block); + LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block); + // LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block); } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 3b2e6c29f..7c92c517c 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1,4 +1,4 @@ -lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false); +lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise); lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) { lbModule *m = p->module; @@ -137,7 +137,7 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) lbAddr res_addr = lb_add_local(p, type, nullptr, false, 0, true); lbValue res = lb_addr_get_ptr(p, res_addr); - bool inline_array_arith = type_size_of(type) <= build_context.max_align; + bool inline_array_arith = lb_can_try_to_inline_array_arith(type); i32 count = cast(i32)get_array_type_count(tl); @@ -436,7 +436,7 @@ lbValue lb_emit_arith_array(lbProcedure *p, TokenKind op, lbValue lhs, lbValue r return direct_vector_res; } - bool inline_array_arith = type_size_of(type) <= build_context.max_align; + bool inline_array_arith = lb_can_try_to_inline_array_arith(type); if (inline_array_arith) { auto dst_ptrs = slice_make(temporary_allocator(), n); @@ -987,7 +987,6 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) { GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type)); - if (op == Token_Mul && !component_wise) { Type *xt = base_type(lhs.type); Type *yt = base_type(rhs.type); @@ -1001,8 +1000,22 @@ lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue } else if (is_type_array_like(xt)) { GB_ASSERT(yt->kind == Type_Matrix); return lb_emit_vector_mul_matrix(p, lhs, rhs, type); - } + } else { + GB_ASSERT(xt->kind == Type_Basic); + GB_ASSERT(yt->kind == Type_Matrix); + GB_ASSERT(is_type_matrix(type)); + Type *array_type = alloc_type_array(yt->Matrix.elem, matrix_type_total_internal_elems(yt)); + GB_ASSERT(type_size_of(array_type) == type_size_of(yt)); + + lbValue array_lhs = lb_emit_conv(p, lhs, array_type); + lbValue array_rhs = rhs; + array_rhs.type = array_type; + + lbValue array = lb_emit_arith(p, op, array_lhs, array_rhs, array_type); + array.type = type; + return array; + } } else { if (is_type_matrix(lhs.type)) { rhs = lb_emit_conv(p, rhs, lhs.type); @@ -1047,7 +1060,7 @@ lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Ty if (is_type_array_like(lhs.type) || is_type_array_like(rhs.type)) { return lb_emit_arith_array(p, op, lhs, rhs, type); } else if (is_type_matrix(lhs.type) || is_type_matrix(rhs.type)) { - return lb_emit_arith_matrix(p, op, lhs, rhs, type); + return lb_emit_arith_matrix(p, op, lhs, rhs, type, false); } else if (is_type_complex(type)) { lhs = lb_emit_conv(p, lhs, type); rhs = lb_emit_conv(p, rhs, type); @@ -1320,7 +1333,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { if (is_type_matrix(be->left->tav.type) || is_type_matrix(be->right->tav.type)) { lbValue left = lb_build_expr(p, be->left); lbValue right = lb_build_expr(p, be->right); - return lb_emit_arith_matrix(p, be->op.kind, left, right, default_type(tv.type)); + return lb_emit_arith_matrix(p, be->op.kind, left, right, default_type(tv.type), false); } @@ -1777,9 +1790,6 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lbValue res = {}; res = lb_emit_conv(p, value, platform_src_type); res = lb_emit_conv(p, res, platform_dst_type); - if (is_type_different_to_arch_endianness(dst)) { - res = lb_emit_byte_swap(p, res, platform_dst_type); - } return lb_emit_conv(p, res, t); } @@ -1927,7 +1937,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } if (dst->Union.variants.count == 1) { Type *vt = dst->Union.variants[0]; - if (internal_check_is_assignable_to(src, vt)) { + if (internal_check_is_assignable_to(src_type, vt)) { value = lb_emit_conv(p, value, vt); lbAddr parent = lb_add_local_generated(p, t, true); lb_emit_store_union_variant(p, parent.addr, value, vt); @@ -2306,7 +2316,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri cmp_op = Token_And; } - bool inline_array_arith = type_size_of(tl) <= build_context.max_align; + bool inline_array_arith = lb_can_try_to_inline_array_arith(tl); i32 count = 0; switch (tl->kind) { case Type_Array: count = cast(i32)tl->Array.count; break; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 071986458..1f8fccdcb 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -591,6 +591,9 @@ bool lb_try_update_alignment(lbValue ptr, unsigned alignment) { return lb_try_update_alignment(ptr.value, alignment); } +bool lb_can_try_to_inline_array_arith(Type *t) { + return type_size_of(t) <= build_context.max_simd_align; +} bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) { Type *array_type = base_type(type_deref(ptr.type)); @@ -599,7 +602,7 @@ bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vector_type_) { Type *elem_type = base_array_type(array_type); // TODO(bill): Determine what is the correct limit for doing vector arithmetic - if (type_size_of(array_type) <= build_context.max_align && + if (lb_can_try_to_inline_array_arith(array_type) && is_type_valid_vector_elem(elem_type)) { // Try to treat it like a vector if possible bool possible = false; @@ -874,18 +877,6 @@ bool lb_is_type_proc_recursive(Type *t) { case Type_Pointer: t = t->Pointer.elem; break; - case Type_Array: - t = t->Array.elem; - break; - case Type_EnumeratedArray: - t = t->EnumeratedArray.elem; - break; - case Type_Slice: - t = t->Slice.elem; - break; - case Type_DynamicArray: - t = t->DynamicArray.elem; - break; case Type_Proc: return true; default: @@ -1887,16 +1878,16 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return LLVMPointerType(lb_type(m, type->Pointer.elem), 0); case Type_Array: { - m->internal_type_level -= 1; - LLVMTypeRef t = LLVMArrayType(lb_type(m, type->Array.elem), cast(unsigned)type->Array.count); m->internal_type_level += 1; + LLVMTypeRef t = LLVMArrayType(lb_type(m, type->Array.elem), cast(unsigned)type->Array.count); + m->internal_type_level -= 1; return t; } case Type_EnumeratedArray: { - m->internal_type_level -= 1; - LLVMTypeRef t = LLVMArrayType(lb_type(m, type->EnumeratedArray.elem), cast(unsigned)type->EnumeratedArray.count); m->internal_type_level += 1; + LLVMTypeRef t = LLVMArrayType(lb_type(m, type->EnumeratedArray.elem), cast(unsigned)type->EnumeratedArray.count); + m->internal_type_level -= 1; return t; } @@ -2098,14 +2089,11 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } case Type_Proc: - // if (m->internal_type_level > 256) { // TODO HACK(bill): is this really enough? - if (m->internal_type_level > 1) { // TODO HACK(bill): is this really enough? - return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); - } else { + { LLVMTypeRef proc_raw_type = lb_type_internal_for_procedures_raw(m, type); - return LLVMPointerType(proc_raw_type, 0); + gb_unused(proc_raw_type); + return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0); } - break; case Type_BitSet: { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f85d8397c..530c08944 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -736,6 +736,9 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, } for_array(i, processed_args) { lbValue arg = processed_args[i]; + if (is_type_proc(arg.type)) { + arg.value = LLVMBuildPointerCast(p->builder, arg.value, lb_type(p->module, arg.type), ""); + } args[arg_index++] = arg.value; } if (context_ptr.addr.value != nullptr) { @@ -2867,9 +2870,9 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { return y; } - Ast *pexpr = unparen_expr(ce->proc); + Ast *proc_expr = unparen_expr(ce->proc); if (proc_mode == Addressing_Builtin) { - Entity *e = entity_of_node(pexpr); + Entity *e = entity_of_node(proc_expr); BuiltinProcId id = BuiltinProc_Invalid; if (e != nullptr) { id = cast(BuiltinProcId)e->Builtin.id; @@ -2881,7 +2884,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { // NOTE(bill): Regular call lbValue value = {}; - Ast *proc_expr = unparen_expr(ce->proc); Entity *proc_entity = entity_of_node(proc_expr); if (proc_entity != nullptr) { diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index e55fae3a7..a080fc09d 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -646,7 +646,7 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco lbAddr array = lb_build_addr(p, expr); - if (is_type_pointer(type_deref(lb_addr_type(array)))) { + if (is_type_pointer(lb_addr_type(array))) { array = lb_addr(lb_addr_load(p, array)); } lbValue count = lb_soa_struct_len(p, lb_addr_load(p, array)); @@ -1796,7 +1796,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue rhs = lb_emit_conv(p, value, lhs_type); - bool inline_array_arith = type_size_of(array_type) <= build_context.max_align; + bool inline_array_arith = lb_can_try_to_inline_array_arith(array_type); if (lhs.kind == lbAddr_Swizzle) { @@ -1959,8 +1959,18 @@ void lb_build_assign_stmt(lbProcedure *p, AstAssignStmt *as) { } else { lbAddr lhs = lb_build_addr(p, as->lhs[0]); lbValue value = lb_build_expr(p, as->rhs[0]); - Type *lhs_type = lb_addr_type(lhs); + + // NOTE(bill): Allow for the weird edge case of: + // array *= matrix + if (op == Token_Mul && is_type_matrix(value.type) && is_type_array(lhs_type)) { + lbValue old_value = lb_addr_load(p, lhs); + Type *type = old_value.type; + lbValue new_value = lb_emit_vector_mul_matrix(p, old_value, value, type); + lb_addr_store(p, lhs, new_value); + return; + } + if (is_type_array(lhs_type)) { lb_build_assign_stmt_array(p, op, lhs, value); return; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 7163f1d9e..14592f29a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -225,6 +225,20 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) { if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), ""); return res; + } else if (is_type_array_like(src) && is_type_simd_vector(dst)) { + unsigned align = cast(unsigned)gb_max(type_align_of(src), type_align_of(dst)); + lbValue ptr = lb_address_from_load_or_generate_local(p, value); + if (lb_try_update_alignment(ptr, align)) { + LLVMTypeRef result_type = lb_type(p->module, t); + res.value = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(result_type, 0), ""); + res.value = LLVMBuildLoad2(p->builder, result_type, res.value, ""); + return res; + } + lbAddr addr = lb_add_local_generated(p, t, false); + lbValue ap = lb_addr_get_ptr(p, addr); + ap = lb_emit_conv(p, ap, alloc_type_pointer(value.type)); + lb_emit_store(p, ap, value); + return lb_addr_load(p, addr); } if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) { diff --git a/src/main.cpp b/src/main.cpp index ef1b8dda1..a1daa51d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -288,11 +288,15 @@ i32 linker_stage(lbGenerator *gen) { char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; if (!build_context.use_lld) { // msvc + String res_path = {}; + defer (gb_free(heap_allocator(), res_path.text)); if (build_context.has_resource) { + String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); + res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\"")); + gb_free(heap_allocator(), temp_res_path.text); + String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); - String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); defer (gb_free(heap_allocator(), rc_path.text)); - defer (gb_free(heap_allocator(), res_path.text)); result = system_exec_command_line_app("msvc-link", "\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"", @@ -304,42 +308,25 @@ i32 linker_stage(lbGenerator *gen) { if (result) { return result; } - - result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " - " %.*s " - " %.*s " - " %s " - "", - LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename), - link_settings, - subsystem_str, - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - lib_str - ); - } else { - result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s -OUT:\"%.*s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " - " %.*s " - " %.*s " - " %s " - "", - LIT(vs_exe_path), object_files, LIT(output_filename), - link_settings, - subsystem_str, - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - lib_str - ); } + result = system_exec_command_line_app("msvc-link", + "\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s " + "/nologo /incremental:no /opt:ref /subsystem:%s " + " %.*s " + " %.*s " + " %s " + "", + LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename), + link_settings, + subsystem_str, + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + lib_str + ); if (result) { return result; } - } else { // lld result = system_exec_command_line_app("msvc-lld-link", "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s " @@ -590,8 +577,8 @@ void usage(String argv0) { print_usage_line(1, "version print version"); print_usage_line(1, "report print information useful to reporting a bug"); print_usage_line(0, ""); - print_usage_line(0, "For further details on a command, use -help after the command name"); - print_usage_line(1, "e.g. odin build -help"); + print_usage_line(0, "For further details on a command, invoke command help:"); + print_usage_line(1, "e.g. `odin build -help` or `odin help build`"); } enum BuildFlagKind { @@ -1963,13 +1950,13 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project"); } - bool doc = command == "doc"; - bool build = command == "build"; - bool run_or_build = command == "run" || command == "build" || command == "test"; - bool test_only = command == "test"; + bool doc = command == "doc"; + bool build = command == "build"; + bool run_or_build = command == "run" || command == "build" || command == "test"; + bool test_only = command == "test"; bool strip_semicolon = command == "strip-semicolon"; - bool check_only = command == "check" || strip_semicolon; - bool check = run_or_build || check_only; + bool check_only = command == "check" || strip_semicolon; + bool check = run_or_build || check_only; print_usage_line(0, ""); print_usage_line(1, "Flags"); @@ -2085,7 +2072,7 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(3, "-build-mode:shared Build as a dynamically linked library"); print_usage_line(3, "-build-mode:obj Build as an object file"); print_usage_line(3, "-build-mode:object Build as an object file"); - print_usage_line(3, "-build-mode:assembly Build as an object file"); + print_usage_line(3, "-build-mode:assembly Build as an assembly file"); print_usage_line(3, "-build-mode:assembler Build as an assembly file"); print_usage_line(3, "-build-mode:asm Build as an assembly file"); print_usage_line(3, "-build-mode:llvm-ir Build as an LLVM IR file"); @@ -2701,6 +2688,14 @@ int main(int arg_count, char const **arg_ptr) { build_context.command_kind = Command_bug_report; print_bug_report_help(); return 0; + } else if (command == "help") { + if (args.count <= 2) { + usage(args[0]); + return 1; + } else { + print_show_help(args[0], args[2]); + return 0; + } } else { usage(args[0]); return 1; @@ -2727,7 +2722,12 @@ int main(int arg_count, char const **arg_ptr) { if (!single_file_package) { gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command)); - gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename)); + if (init_filename == "-file") { + gb_printf_err("Did you mean `%.*s %.*s -file`?\n", LIT(args[0]), LIT(command)); + } else { + gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename)); + } + gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n"); return 1; } else { diff --git a/src/parser.cpp b/src/parser.cpp index 9a5531289..70a87d2a5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5506,6 +5506,51 @@ isize calc_decl_count(Ast *decl) { return count; } +bool parse_build_project_directory_tag(Token token_for_pos, String s) { + String const prefix = str_lit("+build-project-name"); + GB_ASSERT(string_starts_with(s, prefix)); + s = string_trim_whitespace(substring(s, prefix.len, s.len)); + if (s.len == 0) { + return true; + } + + bool any_correct = false; + + while (s.len > 0) { + bool this_kind_correct = true; + + do { + String p = string_trim_whitespace(build_tag_get_token(s, &s)); + if (p.len == 0) break; + if (p == ",") break; + + bool is_notted = false; + if (p[0] == '!') { + is_notted = true; + p = substring(p, 1, p.len); + if (p.len == 0) { + syntax_error(token_for_pos, "Expected a build-project-name after '!'"); + break; + } + } + + if (p.len == 0) { + continue; + } + + if (is_notted) { + this_kind_correct = this_kind_correct && (p != build_context.ODIN_BUILD_PROJECT_NAME); + } else { + this_kind_correct = this_kind_correct && (p == build_context.ODIN_BUILD_PROJECT_NAME); + } + } while (s.len > 0); + + any_correct = any_correct || this_kind_correct; + } + + return any_correct; +} + bool parse_file(Parser *p, AstFile *f) { if (f->tokens.count == 0) { return true; @@ -5561,7 +5606,11 @@ bool parse_file(Parser *p, AstFile *f) { if (string_starts_with(str, str_lit("//"))) { String lc = string_trim_whitespace(substring(str, 2, str.len)); if (lc.len > 0 && lc[0] == '+') { - if (string_starts_with(lc, str_lit("+build"))) { + if (string_starts_with(lc, str_lit("+build-project-name"))) { + if (!parse_build_project_directory_tag(tok, lc)) { + return false; + } + } else if (string_starts_with(lc, str_lit("+build"))) { if (!parse_build_tag(tok, lc)) { return false; } diff --git a/src/types.cpp b/src/types.cpp index 0ca239bde..b7cb4dd2c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1428,7 +1428,7 @@ i64 matrix_align_of(Type *t, struct TypePath *tp) { } GB_ASSERT(min_alignment >= elem_align); - i64 align = gb_min(min_alignment, build_context.max_align); + i64 align = gb_min(min_alignment, build_context.max_simd_align); return align; } @@ -3556,7 +3556,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Type_SimdVector: { // IMPORTANT TODO(bill): Figure out the alignment of vector types - return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2); + return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_simd_align*2); } case Type_Matrix: @@ -3571,10 +3571,9 @@ i64 type_align_of_internal(Type *t, TypePath *path) { return build_context.word_size; } - // return gb_clamp(next_pow2(type_size_of(t)), 1, build_context.max_align); // NOTE(bill): Things that are bigger than build_context.word_size, are actually comprised of smaller types // TODO(bill): Is this correct for 128-bit types (integers)? - return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.word_size); + return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); } i64 *type_set_offsets_of(Slice const &fields, bool is_packed, bool is_raw_union) { diff --git a/tests/core/build.bat b/tests/core/build.bat index 77ff38038..94f8e0fd8 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -69,4 +69,9 @@ echo --- echo --- echo Running core:text/i18n tests echo --- -%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe \ No newline at end of file +%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe + +echo --- +echo Running core:slice tests +echo --- +%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe \ No newline at end of file diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin new file mode 100644 index 000000000..05dfb58ff --- /dev/null +++ b/tests/core/slice/test_core_slice.odin @@ -0,0 +1,182 @@ +package test_core_slice + +import "core:slice" +import "core:testing" +import "core:fmt" +import "core:os" +import "core:math/rand" + +TEST_count := 0 +TEST_fail := 0 + +when ODIN_TEST { + expect :: testing.expect + log :: testing.log +} else { + expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) { + TEST_count += 1 + if !condition { + TEST_fail += 1 + fmt.printf("[%v] %v\n", loc, message) + return + } + } + log :: proc(t: ^testing.T, v: any, loc := #caller_location) { + fmt.printf("[%v] ", loc) + fmt.printf("log: %v\n", v) + } +} + +main :: proc() { + t := testing.T{} + test_sort_with_indices(&t) + + fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) + if TEST_fail > 0 { + os.exit(1) + } +} + +@test +test_sort_with_indices :: proc(t: ^testing.T) { + seed := rand.uint64() + fmt.printf("Random seed: %v\n", seed) + + // Test sizes are all prime. + test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} + + for test_size in test_sizes { + fmt.printf("Sorting %v random u64 values along with index.\n", test_size) + + r := rand.create(seed) + + vals := make([]u64, test_size) + r_idx := make([]int, test_size) // Reverse index + defer { + delete(vals) + delete(r_idx) + } + + // Set up test values + for _, i in vals { + vals[i] = rand.uint64(&r) + } + + // Sort + f_idx := slice.sort_with_indices(vals) + defer delete(f_idx) + + // Verify sorted test values + rand.init(&r, seed) + + for v, i in f_idx { + r_idx[v] = i + } + + last: u64 + for v, i in vals { + if i > 0 { + val_pass := v >= last + expect(t, val_pass, "Expected values to have been sorted.") + if !val_pass { + break + } + } + + idx_pass := vals[r_idx[i]] == rand.uint64(&r) + expect(t, idx_pass, "Expected index to have been sorted.") + if !idx_pass { + break + } + last = v + } + } +} + +@test +test_sort_by_indices :: proc(t: ^testing.T) { + seed := rand.uint64() + fmt.printf("Random seed: %v\n", seed) + + // Test sizes are all prime. + test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} + + for test_size in test_sizes { + fmt.printf("Sorting %v random u64 values along with index.\n", test_size) + + r := rand.create(seed) + + vals := make([]u64, test_size) + r_idx := make([]int, test_size) // Reverse index + defer { + delete(vals) + delete(r_idx) + } + + // Set up test values + for _, i in vals { + vals[i] = rand.uint64(&r) + } + + // Sort + f_idx := slice.sort_with_indices(vals) + defer delete(f_idx) + + // Verify sorted test values + rand.init(&r, seed) + + { + indices := make([]int, test_size) + defer delete(indices) + for _, i in indices { + indices[i] = i + } + + sorted_indices := slice.sort_by_indices(indices, f_idx) + defer delete(sorted_indices) + for v, i in sorted_indices { + idx_pass := v == f_idx[i] + expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices") + if !idx_pass { + break + } + } + } + { + indices := make([]int, test_size) + defer delete(indices) + for _, i in indices { + indices[i] = i + } + + slice.sort_by_indices_overwrite(indices, f_idx) + for v, i in indices { + idx_pass := v == f_idx[i] + expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices") + if !idx_pass { + break + } + } + } + { + indices := make([]int, test_size) + swap := make([]int, test_size) + defer { + delete(indices) + delete(swap) + } + for _, i in indices { + indices[i] = i + } + + slice.sort_by_indices(indices, swap, f_idx) + for v, i in swap { + idx_pass := v == f_idx[i] + expect(t, idx_pass, "Expected the sorted index to be the same as the result from sort_with_indices") + if !idx_pass { + break + } + } + } + } +} diff --git a/tools/odinfmt/flag/flag.odin b/tools/odinfmt/flag/flag.odin index 33c0bfe12..6b2525717 100644 --- a/tools/odinfmt/flag/flag.odin +++ b/tools/odinfmt/flag/flag.odin @@ -146,7 +146,7 @@ reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error { type := types[i]; if named_type, ok := type.variant.(Type_Info_Named); ok { - if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && union_type.maybe && len(union_type.variants) == 1 { + if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && len(union_type.variants) == 1 { flag.optional = true; flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i])); type = union_type.variants[0]; diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index cebb20888..48e3b7212 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -70,7 +70,7 @@ walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip } main :: proc() { - init_global_temporary_allocator(mem.megabytes(100)); + init_global_temporary_allocator(mem.Megabyte * 100) args: Args; diff --git a/vendor/OpenGL/constants.odin b/vendor/OpenGL/constants.odin index 28c923903..c7d8b9542 100644 --- a/vendor/OpenGL/constants.odin +++ b/vendor/OpenGL/constants.odin @@ -1,5 +1,7 @@ package odin_gl +GL_DEBUG :: #config(GL_DEBUG, ODIN_DEBUG) + FALSE :: false TRUE :: true diff --git a/vendor/OpenGL/helpers.odin b/vendor/OpenGL/helpers.odin index 927129130..61c68ace5 100644 --- a/vendor/OpenGL/helpers.odin +++ b/vendor/OpenGL/helpers.odin @@ -46,7 +46,7 @@ get_last_error_message :: proc() -> (compile_message: string, compile_type: Shad // Shader checking and linking checking are identical // except for calling differently named GL functions // it's a bit ugly looking, but meh -when ODIN_DEBUG { +when GL_DEBUG { import "core:runtime" @private diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index b62ed216b..ba6ff369c 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -2,7 +2,7 @@ package odin_gl #assert(size_of(bool) == size_of(u8)) -when !ODIN_DEBUG { +when !GL_DEBUG { // VERSION_1_0 CullFace :: proc "c" (mode: u32) { impl_CullFace(mode) } FrontFace :: proc "c" (mode: u32) { impl_FrontFace(mode) } diff --git a/vendor/README.md b/vendor/README.md index 628ea2727..1e24b4d0a 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -134,4 +134,11 @@ See also LICENSE in the `GGPO` directory itself. `botan.lib` is available under Botan's [BSD](https://botan.randombit.net/license.txt) license. See also LICENSE in the `botan` directory itself. -Includes full bindings as well as wrappers to match the `core:crypto` API. \ No newline at end of file +Includes full bindings as well as wrappers to match the `core:crypto` API. + +## CommonMark + +[CMark](https://github.com/commonmark/cmark) CommonMark parsing library. + +See also LICENSE in the `commonmark` directory itself. +Includes full bindings and Windows `.lib` and `.dll`. \ No newline at end of file diff --git a/vendor/commonmark/LICENSE b/vendor/commonmark/LICENSE new file mode 100644 index 000000000..c87c7a615 --- /dev/null +++ b/vendor/commonmark/LICENSE @@ -0,0 +1,170 @@ +Copyright (c) 2014, John MacFarlane + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----- + +houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c + +derive from https://github.com/vmg/houdini (with some modifications) + +Copyright (C) 2012 Vicent Martí + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + +buffer.h, buffer.c, chunk.h + +are derived from code (C) 2012 Github, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + +utf8.c and utf8.c + +are derived from utf8proc +(), +(C) 2009 Public Software Group e. V., Berlin, Germany. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +----- + +The normalization code in normalize.py was derived from the +markdowntest project, Copyright 2013 Karl Dubost: + +The MIT License (MIT) + +Copyright (c) 2013 Karl Dubost + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----- + +The CommonMark spec (test/spec.txt) is + +Copyright (C) 2014-15 John MacFarlane + +Released under the Creative Commons CC-BY-SA 4.0 license: +. + +----- + +The test software in test/ is + +Copyright (c) 2014, John MacFarlane + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/commonmark/build.bat b/vendor/commonmark/build.bat new file mode 100644 index 000000000..1783e4651 --- /dev/null +++ b/vendor/commonmark/build.bat @@ -0,0 +1,4 @@ +@echo off +pushd W:\Odin-other\Odin-test +call build.bat +popd \ No newline at end of file diff --git a/vendor/commonmark/cmark.odin b/vendor/commonmark/cmark.odin new file mode 100644 index 000000000..313435870 --- /dev/null +++ b/vendor/commonmark/cmark.odin @@ -0,0 +1,526 @@ +/* + Bindings against CMark (https://github.com/commonmark/cmark) + + Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer. + See LICENSE for license details. +*/ +package commonmark + +import "core:c" +import "core:c/libc" +import "core:runtime" + +BINDING_VERSION :: Version_Info{major = 0, minor = 30, patch = 2} + +when ODIN_OS == .Windows { + foreign import lib { + "cmark_static.lib", + } +} else when ODIN_OS == .Linux { + foreign import lib { + "libcmark.a", + } +} + +Option :: enum c.int { + Source_Position = 1, // Include a `data-sourcepos` attribute on all block elements. + Hard_Breaks = 2, // Render `softbreak` as hard line breaks. + Safe = 3, // Defined for API compatibility, now enabled by default. + Unsafe = 17, // Render raw HTML and unsafe links (`javascript:`, `vbscript:`, + // `file:`, and `data:`, except for `image/png`, `image/gif`, + // `image/jpeg`, or `image/webp` mime types). By default, + // raw HTML is replaced by a placeholder HTML comment. Unsafe + // links are replaced by empty strings. + No_Breaks = 4, // Render `softbreak` elements as spaces. + Normalize = 8, // Legacy option, no effect. + Validate_UTF8 = 9, // Validate UTF-8 input before parsing, replacing illegal + // sequences with the replacement character U+FFFD. + Smart = 10, // Convert straight quotes to curly, --- to em dashes, -- to en dashes. +} +Options :: bit_set[Option; c.int] + +DEFAULT_OPTIONS :: Options{} + +Node_Type :: enum u16 { + // Error status + None = 0, + + /* Block */ + Document, + Block_Quote, + List, + Item, + Code_Block, + HTML_Block, + Custom_Block, + Paragraph, + Heading, + Thematic_Break, + + /* Inline */ + Text, + Soft_Break, + Line_Break, + Code, + HTML_Inline, + Custom_Inline, + Emph, + Strong, + Link, + Image, + + First_Block = Document, + Last_Block = Thematic_Break, + + First_Inline = Text, + Last_Inline = Image, +} + +List_Type :: enum c.int { + None, + Bullet, + Ordered, +} + +Delim_Type :: enum c.int { + None, + Period, + Paren, +} + +// Version information +Version_Info :: struct { + patch: u8, + minor: u8, + major: u8, + _: u8, +} + +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + version :: proc() -> (res: Version_Info) --- + version_string :: proc() -> (res: cstring) --- +} + +// Simple API +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Convert 'text' (assumed to be a UTF-8 encoded string with length `len`) from CommonMark Markdown to HTML + // returning a null-terminated, UTF-8-encoded string. It is the caller's responsibility + // to free the returned buffer. + markdown_to_html :: proc(text: cstring, length: c.size_t, options: Options) -> (html: cstring) --- +} + +markdown_to_html_from_string :: proc(text: string, options: Options) -> (html: string) { + return string(markdown_to_html(cstring(raw_data(text)), len(text), options)) +} + +// Custom allocator - Defines the memory allocation functions to be used by CMark +// when parsing and allocating a document tree +Allocator :: struct { + calloc: proc "c" (num: c.size_t, size: c.size_t) -> rawptr, + realloc: proc "c" (ptr: rawptr, new_size: c.size_t) -> rawptr, + free: proc "c" (ptr: rawptr), +} + +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Returns a pointer to the default memory allocator. + get_default_mem_allocator :: proc() -> (mem: ^Allocator) --- +} + +bufsize_t :: distinct i32 + +// Node creation, destruction, and tree traversal +Node :: struct { + mem: ^Allocator, + + next: ^Node, + prev: ^Node, + parent: ^Node, + first_child: ^Node, + last_child: ^Node, + + user_data: rawptr, + data: [^]u8, + len: bufsize_t, + + start_line: c.int, + start_column: c.int, + end_line: c.int, + end_column: c.int, + + type: Node_Type, + flags: Node_Flags, + + as: struct #raw_union { + list: List, + code: Code, + heading: Heading, + link: Link, + custom: Custom, + html_block_type: c.int, + }, +} + +Node_Flag :: enum u16 { + Open = 0, + Last_Line_Blank = 1, + Last_Line_Checked = 2, +} +Node_Flags :: bit_set[Node_Flag; u16] + +List :: struct { + marker_offset: c.int, + padding: c.int, + start: c.int, + list_type: u8, + delimiter: u8, + bullet_char: u8, + tight: c.bool, +} + +Code :: struct { + info: cstring, + fence_length: u8, + fence_offset: u8, + fence_char: u8, + fenced: b8, +} + +Heading :: struct { + internal_offset: c.int, + level: i8, + setext: c.bool, +} + + +Link :: struct { + url: cstring, + title: cstring, +} + +Custom :: struct { + on_enter: cstring, + on_exit: cstring, +} + +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Creates a new node of type 'type'. + // Note that the node may have other required properties, which it is the caller's responsibility + // to assign. + node_new :: proc(type: Node_Type) -> (node: ^Node) --- + + // Same as `node_new`, but explicitly listing the memory allocator used to allocate the node. + // Note: be sure to use the same allocator for every node in a tree, or bad things can happen. + node_new_with_mem :: proc(type: Node_Type, mem: ^Allocator) -> (node: ^Node) --- + + // Frees the memory allocated for a node and any children. + node_free :: proc(node: ^Node) --- + + /* + Tree Traversal + */ + // Returns the next node in the sequence after `node`, or nil if there is none. + node_next :: proc(node: ^Node) -> (next: ^Node) --- + + // Returns the previous node in the sequence after `node`, or nil if there is none. + node_previous :: proc(node: ^Node) -> (prev: ^Node) --- + + // Returns the parent of `node`, or nil if there is none. + node_parent :: proc(node: ^Node) -> (parent: ^Node) --- + + // Returns the first child of `node`, or nil if `node` has no children. + node_first_child :: proc(node: ^Node) -> (child: ^Node) --- + + // Returns the last child of `node`, or nil if `node` has no children. + node_last_child :: proc(node: ^Node) -> (child: ^Node) --- + +} + +Iter :: distinct rawptr + +Event_Type :: enum c.int { + None, + Done, + Enter, + Exit, +} + +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Creates a new iterator starting at 'root'. The current node and event + // type are undefined until `iter_next` is called for the first time. + // The memory allocated for the iterator should be released using + // 'iter_free' when it is no longer needed. + iter_new :: proc(root: ^Node) -> (iter: ^Iter) --- + + // Frees the memory allocated for an iterator. + iter_free :: proc(iter: ^Iter) --- + + // Advances to the next node and returns the event type (`.Enter`, `.Exit`, `.Done`) + iter_next :: proc(iter: ^Iter) -> (event_type: Event_Type) --- + + // Returns the current node. + iter_get_node :: proc(iter: ^Iter) -> (node: ^Node) --- + + // Returns the current event type. + iter_get_event_type :: proc(iter: ^Iter) -> (event_type: Event_Type) --- + + // Returns the root node. + iter_get_root :: proc(iter: ^Iter) -> (root: ^Node) --- + + // Resets the iterator so that the current node is `current` and + // the event type is `event_type`. The new current node must be a + // descendant of the root node or the root node itself. + iter_reset :: proc(iter: ^Iter, current: ^Node, event_type: Event_Type) --- +} + +// Accessors +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Returns the user data of `node`. + node_get_user_data :: proc(node: ^Node) -> (user_data: rawptr) --- + + // Sets arbitrary user data for `node`. Returns `true` on success, `false` on failure. + node_set_user_data :: proc(node: ^Node, user_data: rawptr) -> (success: b32) --- + + // Returns the type of `node`, or `.None` on error. + node_get_type :: proc(node: ^Node) -> (node_type: Node_Type) --- + + // Like `node_get_type`, but returns a string representation of the type, or "". + node_get_type_string :: proc(node: ^Node) -> (node_type: cstring) --- + + // Returns the string contents of `node`, or an empty string if none is set. + // Returns `nil` if called on a node that does not have string content. + node_get_literal :: proc(node: ^Node) -> (content: cstring) --- + + // Sets the string contents of `node`. Returns `true` on success, `false` on failure. + node_set_literal :: proc(node: ^Node, content: cstring) -> (success: b32) --- + + // Returns the heading level of `node`, or 0 if `node` is not a heading. + node_get_heading_level :: proc(node: ^Node) -> (level: c.int) --- + + // Sets the heading level of `node`. Returns `true` on success, `false` on failure. + node_set_heading_level :: proc(node: ^Node, level: c.int) -> (success: b32) --- + + // Returns the list type of `node`, or `.No_List` if not a list. + node_get_list_type :: proc(node: ^Node) -> (list_type: List_Type) --- + + // Sets the list type of `node`. Returns `true` on success, `false` on failure. + node_set_list_type :: proc(node: ^Node, list_type: List_Type) -> (success: b32) --- + + // Returns the list delimiter type of `node`, or `.No_Delim` if not a list. + node_get_list_delim :: proc(node: ^Node) -> (delim_type: Delim_Type) --- + + // Sets the delimiter type of `node`. Returns `true` on success, `false` on failure. + node_set_list_delim :: proc(node: ^Node, delim_type: Delim_Type) -> (success: b32) --- + + // Returns starting number of `node`, if it is an ordered list, otherwise 0. + node_get_list_start :: proc(node: ^Node) -> (start: c.int) --- + + // Sets starting number of `node`, if it is an ordered list. + // Returns `true` on success, `false` on failure. + node_set_list_start :: proc(node: ^Node, start: c.int) -> (success: b32) --- + + // Returns `true` if `node` is a tight list, `false` otherwise. + node_get_list_tight :: proc(node: ^Node) -> (tight: b32) --- + + // Sets the "tightness" of a list. Returns `true` on success, `false` on failure. + node_set_list_tight :: proc(node: ^Node, tight: b32) -> (success: b32) --- + + // Returns the info string from a fenced code block. + get_fence_info :: proc(node: ^Node) -> (fence_info: cstring) --- + + // Sets the info string in a fenced code block, returning `true` on success and `false` on failure. + node_set_fence_info :: proc(node: ^Node, fence_info: cstring) -> (success: b32) --- + + // Returns the URL of a link or image `node`, or an empty string if no URL is set. + // Returns nil if called on a node that is not a link or image. + node_get_url :: proc(node: ^Node) -> (url: cstring) --- + + // Sets the URL of a link or image `node`. Returns `true` on success, `false` on failure. + node_set_url :: proc(node: ^Node, url: cstring) -> (success: b32) --- + + // Returns the title of a link or image `node`, or an empty string if no title is set. + // Returns nil if called on a node that is not a link or image. + node_get_title :: proc(node: ^Node) -> (title: cstring) --- + + // Sets the title of a link or image `node`. Returns `true` on success, `false` on failure. + node_set_title :: proc(node: ^Node, title: cstring) -> (success: b32) --- + + // Returns the literal "on enter" text for a custom `node`, or an empty string if no on_enter is set. + // Returns nil if called on a non-custom node. + node_get_on_enter :: proc(node: ^Node) -> (on_enter: cstring) --- + + // Sets the literal text to render "on enter" for a custom `node`. + // Any children of the node will be rendered after this text. + // Returns `true` on success, `false`on failure. + node_set_on_enter :: proc(node: ^Node, on_enter: cstring) -> (success: b32) --- + + // Returns the line on which `node` begins. + node_get_start_line :: proc(node: ^Node) -> (line: c.int) --- + + // Returns the column at which `node` begins. + node_get_start_column :: proc(node: ^Node) -> (column: c.int) --- + + // Returns the line on which `node` ends. + node_get_end_line :: proc(node: ^Node) -> (line: c.int) --- + + // Returns the column at which `node` ends. + node_get_end_column :: proc(node: ^Node) -> (column: c.int) --- +} + +// Tree Manipulation +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Unlinks a `node`, removing it from the tree, but not freeing its memory. + // (Use `node_free` for that.) + node_unlink :: proc(node: ^Node) --- + + // Inserts 'sibling' before `node`. Returns `true` on success, `false` on failure. + node_insert_before :: proc(node: ^Node, sibling: ^Node) -> (success: b32) --- + + // Inserts 'sibling' after `node`. Returns `true` on success, `false` on failure. + node_insert_after :: proc(node: ^Node, sibling: ^Node) -> (success: b32) --- + + // Replaces 'oldnode' with 'newnode' and unlinks 'oldnode' + // (but does not free its memory). + // Returns `true` on success, `false` on failure. + node_replace :: proc(old_node: ^Node, new_node: ^Node) -> (success: b32) --- + + // Adds 'child' to the beginning of the children of `node`. + // Returns `true` on success, `false` on failure. + node_prepend_child :: proc(node: ^Node, child: ^Node) -> (success: b32) --- + + // Adds 'child' to the end of the children of `node`. + // Returns `true` on success, `false` on failure. + node_append_child :: proc(node: ^Node, child: ^Node) -> (success: b32) --- + + // Consolidates adjacent text nodes. + consolidate_text_nodes :: proc(root: ^Node) --- +} + +Parser :: distinct rawptr + +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Creates a new parser object. + parser_new :: proc(options: Options) -> (parser: ^Parser) --- + + // Creates a new parser object with the given memory allocator. + parser_new_with_mem :: proc(options: Options, mem: ^Allocator) -> (parser: ^Parser) --- + + // Frees memory allocated for a parser object. + parser_free :: proc(parser: ^Parser) --- + + // Feeds a string of length 'len' to 'parser'. + parser_feed :: proc(parser: ^Parser, buffer: [^]byte, len: c.size_t) --- + + // Finish parsing and return a pointer to a tree of nodes. + parser_finish :: proc(parser: ^Parser) -> (root: ^Node) --- + + // Parse a CommonMark document in 'buffer' of length 'len'. + // Returns a pointer to a tree of nodes. The memory allocated for + // the node tree should be released using 'node_free' when it is no longer needed. + parse_document :: proc(buffer: [^]byte, len: c.size_t, options: Options) -> (root: ^Node) --- + + // Parse a CommonMark document in file 'f', returning a pointer to a tree of nodes. + // The memory allocated for the node tree should be released using 'node_free' + // when it is no longer needed. + // + // Called `parse_from_libc_file` so as not to confuse with Odin's file handling. + + @(link_name = "parse_from_file") + parse_from_libc_file :: proc(file: ^libc.FILE, options: Options) -> (root: ^Node) --- +} + +parser_feed_from_string :: proc "c" (parser: ^Parser, s: string) { + parser_feed(parser, raw_data(s), len(s)) +} +parse_document_from_string :: proc "c" (s: string, options: Options) -> (root: ^Node) { + return parse_document(raw_data(s), len(s), options) +} + +// Rendering +@(default_calling_convention="c", link_prefix="cmark_") +foreign lib { + // Render a `node` tree as XML. + // It is the caller's responsibilityto free the returned buffer. + render_xml :: proc(root: ^Node, options: Options) -> (xml: cstring) --- + + // Render a `node` tree as an HTML fragment. + // It is up to the user to add an appropriate header and footer. + // It is the caller's responsibility to free the returned buffer. + render_html :: proc(root: ^Node, options: Options) -> (html: cstring) --- + + // Render a `node` tree as a groff man page, without the header. + // It is the caller's responsibility to free the returned buffer. + render_man :: proc(root: ^Node, options: Options, width: c.int) -> (groff: cstring) --- + + // Render a `node` tree as a commonmark document. + // It is the caller's responsibility to free the returned buffer. + render_commonmark :: proc(root: ^Node, options: Options, width: c.int) -> (commonmark: cstring) --- + + // Render a `node` tree as a LaTeX document. + // It is the caller's responsibility to free the returned buffer. + render_latex :: proc(root: ^Node, options: Options, width: c.int) -> (latex: cstring) --- +} + +// Helpers to free results from `render_*`. +free_rawptr :: proc "c" (ptr: rawptr) { + cmm := get_default_mem_allocator() + cmm.free(ptr) +} +free_cstring :: proc "c" (str: cstring) { + free_rawptr(rawptr(str)) +} +free_string :: proc "c" (s: string) { + free_rawptr(raw_data(s)) +} +free :: proc{free_rawptr, free_cstring} + +// Wrap CMark allocator as Odin allocator +@(private) +cmark_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, loc := #caller_location) -> (res: []byte, err: runtime.Allocator_Error) { + + cmark_alloc := cast(^Allocator)allocator_data + switch mode { + case .Alloc: + ptr := cmark_alloc.calloc(1, c.size_t(size)) + res = transmute([]byte)runtime.Raw_Slice{ptr, size} + return res, nil + + case .Free: + cmark_alloc.free(old_memory) + return nil, nil + + case .Free_All: + return nil, .Mode_Not_Implemented + + case .Resize: + new_ptr := cmark_alloc.realloc(old_memory, c.size_t(size)) + res = transmute([]byte)runtime.Raw_Slice{new_ptr, size} + if size > old_size { + runtime.mem_zero(raw_data(res[old_size:]), size - old_size) + } + return res, nil + + case .Query_Features: + return nil, nil + + case .Query_Info: + return nil, .Mode_Not_Implemented + } + return nil, nil +} + +get_default_mem_allocator_as_odin :: proc() -> runtime.Allocator { + return runtime.Allocator{ + procedure = cmark_allocator_proc, + data = rawptr(get_default_mem_allocator()), + } +} \ No newline at end of file diff --git a/vendor/commonmark/cmark_static.lib b/vendor/commonmark/cmark_static.lib new file mode 100644 index 000000000..ee733d87c Binary files /dev/null and b/vendor/commonmark/cmark_static.lib differ diff --git a/vendor/commonmark/doc.odin b/vendor/commonmark/doc.odin new file mode 100644 index 000000000..24f6780f3 --- /dev/null +++ b/vendor/commonmark/doc.odin @@ -0,0 +1,117 @@ +// +ignore +/* + Bindings against CMark (https://github.com/commonmark/cmark) + + Original authors: John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer. + See LICENSE for license details. +*/ +package commonmark + +/* + Parsing - Simple interface: + + ```odin + import cm "vendor:commonmark" + + hellope_world :: proc() { + fmt.printf("CMark version: %v\n", cm.version_string()) + + str := "Hellope *world*!" + root := cm.parse_document(raw_data(str), len(str), cm.DEFAULT_OPTIONS) + defer cm.node_free(root) + + html := cm.render_html(root, cm.DEFAULT_OPTIONS) + defer cm.free(html) + + fmt.println(html) + } + ``` + + Parsing - Streaming interface: + + ```odin + import cm "vendor:commonmark" + + streaming :: proc() { + using cm + + STR :: "Hellope *world*!\n\n" + N :: 50 + STREAM_SIZE :: 42 + + str_buf: [len(STR) * N]u8 + for i in 0.. STREAM_SIZE { + parser_feed(parser, raw_data(buf), STREAM_SIZE) + buf = buf[STREAM_SIZE:] + } + + if len(buf) > 0 { + parser_feed(parser, raw_data(buf), len(buf)) + buf = buf[len(buf):] + } + + root := parser_finish(parser) + defer cm.node_free(root) + + html := cm.render_html(root, cm.DEFAULT_OPTIONS) + defer cm.free(html) + + fmt.println(html) + } + + ``` + + An iterator will walk through a tree of nodes, starting from a root + node, returning one node at a time, together with information about + whether the node is being entered or exited. + + The iterator will first descend to a child node, if there is one. + When there is no child, the iterator will go to the next sibling. + When there is no next sibling, the iterator will return to the parent + (but with an `Event_Type.Exit`). + + The iterator will return `.Done` when it reaches the root node again. + + One natural application is an HTML renderer, where an `.Enter` event + outputs an open tag and an `.Exit` event outputs a close tag. + + An iterator might also be used to transform an AST in some systematic + way, for example, turning all level-3 headings into regular paragraphs. + + ```odin + usage_example(root: ^Node) { + ev_type: Event_Type + iter := iter_new(root) + defer iter_free(iter) + for { + ev_type = iter_next(iter) + if ev_type == .Done do break + cur := iter_get_node(iter) + // Do something with `cur` and `ev_type` + } + } + ``` + + Iterators will never return `.Exit` events for leaf nodes, + which are nodes of type: + + * HTML_Block + * Thematic_Break + * Code_Block + * Text + * Soft_Break + * Line_Break + * Code + * HTML_Inline + + Nodes must only be modified after an `.Exit` event, or an `.Enter` event for + leaf nodes. +*/ \ No newline at end of file diff --git a/vendor/darwin/Foundation/NSWindow.odin b/vendor/darwin/Foundation/NSWindow.odin index 330af6012..2efbe8ba3 100644 --- a/vendor/darwin/Foundation/NSWindow.odin +++ b/vendor/darwin/Foundation/NSWindow.odin @@ -103,18 +103,7 @@ Window_alloc :: proc() -> ^Window { @(objc_type=Window, objc_name="initWithContentRect") Window_initWithContentRect :: proc (self: ^Window, contentRect: Rect, styleMask: WindowStyleMask, backing: BackingStoreType, doDefer: bool) -> ^Window { - self := self - // HACK: due to a compiler bug, the generated calling code does not - // currently work for this message. Has to do with passing a struct along - // with other parameters, so we don't send the rect here. - // Omiting the rect argument here actually works, because of how the C - // calling conventions are defined. - self = msgSend(^Window, self, "initWithContentRect:styleMask:backing:defer:", styleMask, backing, doDefer) - - // apply the contentRect now, since we did not pass it to the init call - msgSend(nil, self, "setContentSize:", contentRect.size) - msgSend(nil, self, "setFrameOrigin:", contentRect.origin) - return self + return msgSend(^Window, self, "initWithContentRect:styleMask:backing:defer:", contentRect, styleMask, backing, doDefer) } @(objc_type=Window, objc_name="contentView") Window_contentView :: proc(self: ^Window) -> ^View { diff --git a/vendor/sdl2/sdl2.odin b/vendor/sdl2/sdl2.odin index 24ecc38e6..a89394d4b 100644 --- a/vendor/sdl2/sdl2.odin +++ b/vendor/sdl2/sdl2.odin @@ -200,7 +200,7 @@ Locale :: struct { @(default_calling_convention="c", link_prefix="SDL_") foreign lib { - GetPreferredLocales :: proc() -> ^Locale --- + GetPreferredLocales :: proc() -> [^]Locale --- } // misc diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index 60d114506..7f0ede146 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -1340,7 +1340,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { sqrt: (x) => Math.sqrt(x), sin: (x) => Math.sin(x), cos: (x) => Math.cos(x), - pow: (x) => Math.pow(x), + pow: (x, power) => Math.pow(x, power), fmuladd: (x, y, z) => x*y + z, ln: (x) => Math.log(x), exp: (x) => Math.exp(x),