From b396280df76b34bc2d741ded6a72e89d32c70565 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sun, 25 Aug 2024 03:57:59 -0700 Subject: [PATCH 001/160] add stuff needed for process control --- core/os/os_darwin.odin | 25 ++++++++++++ core/sys/darwin/mach_darwin.odin | 65 ++++++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 1253e9ae6..69411fdb7 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -9,6 +9,7 @@ import "core:strings" import "core:c" Handle :: distinct i32 +Pid :: distinct i32 File_Time :: distinct u64 INVALID_HANDLE :: ~Handle(0) @@ -584,6 +585,8 @@ F_GETPATH :: 50 // return the full path of the fd foreign libc { @(link_name="__error") __error :: proc() -> ^c.int --- + @(link_name="posix_spawn") _unix_posix_spawn :: proc(pid: ^Pid, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg mode: ..u16) -> Handle --- @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @@ -677,6 +680,28 @@ get_last_error_string :: proc() -> string { return string(_darwin_string_error(__error()^)) } +posix_spawn :: proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr) -> (Pid, Error) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + + args_cstrs := make([]cstring, len(args) + 2, context.temp_allocator) + args_cstrs[0] = strings.clone_to_cstring(path, context.temp_allocator) + for i := 0; i < len(args); i += 1 { + args_cstrs[i+1] = strings.clone_to_cstring(args[i], context.temp_allocator) + } + + envs_cstrs := make([]cstring, len(envs) + 1, context.temp_allocator) + for i := 0; i < len(envs); i += 1 { + envs_cstrs[i] = strings.clone_to_cstring(envs[i], context.temp_allocator) + } + + child_pid: Pid + status := _unix_posix_spawn(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) + if status != 0 { + return 0, get_last_error() + } + return child_pid, nil +} @(require_results) open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (handle: Handle, err: Error) { diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index ac33ebb62..33f37637d 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -1,6 +1,6 @@ package darwin -foreign import pthread "system:System.framework" +foreign import mach "system:System.framework" import "core:c" @@ -8,15 +8,74 @@ import "core:c" // However all other sync primitives are aligned for robustness. // I cannot currently align these though. // See core/sys/unix/pthread_linux.odin/pthread_t. -task_t :: distinct u64 +mach_port_t :: distinct i32 +task_t :: mach_port_t + semaphore_t :: distinct u64 kern_return_t :: distinct u64 thread_act_t :: distinct u64 +MACH_MSG_PORT_DESCRIPTOR :: 0 + +MACH_SEND_MSG :: 0x00000001 +MACH_RCV_MSG :: 0x00000002 +MACH_SEND_TIMEOUT :: 0x00000010 +MACH_RCV_TIMEOUT :: 0x00000100 + +MACH_MSG_TYPE_COPY_SEND :: 19 +MACH_MSG_TYPE_MAKE_SEND :: 20 +MACH_MSGH_BITS_COMPLEX :: 0x80000000 + +MACH_PORT_RIGHT_SEND :: 0 +MACH_PORT_RIGHT_RECEIVE :: 1 + +TASK_BOOTSTRAP_PORT :: 4 + +mach_msg_option_t :: distinct i32 +name_t :: distinct cstring + +mach_msg_port_descriptor_t :: struct { + name: mach_port_t, + _: u32, + extra: bit_field u32 { + _: u32 | 16, + disposition: u32 | 8, + type: u32 | 8, + }, +} + +mach_msg_header_t :: struct { + msgh_bits: u32, + msgh_size: u32, + msgh_remote_port: mach_port_t, + msgh_local_port: mach_port_t, + msgh_voucher_port: u32, + msgh_id: i32, +} + +mach_msg_body_t :: struct { + msgh_descriptor_count: u32, +} + +mach_msg_trailer_t :: struct { + msgh_trailer_type: u32, + msgh_trailer_size: u32, +} + @(default_calling_convention="c") -foreign pthread { +foreign mach { mach_task_self :: proc() -> task_t --- + mach_msg :: proc(header: rawptr, option: mach_msg_option_t, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> kern_return_t --- + mach_msg_send :: proc(header: rawptr) -> kern_return_t --- + + mach_port_allocate :: proc(task: task_t, right: u32, name: rawptr) -> kern_return_t --- + mach_port_deallocate :: proc(task: task_t, name: u32) -> kern_return_t --- + mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> kern_return_t --- + + task_get_special_port :: proc(task: task_t, port: i32, special_port: ^mach_port_t) -> kern_return_t --- + bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t --- + bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t --- semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t --- semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t --- From 3209b319e85fa943f904d3af8e37c14ba58b6080 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Wed, 28 Aug 2024 23:50:17 -0700 Subject: [PATCH 002/160] more useful pieces --- core/sys/darwin/mach_darwin.odin | 80 +++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 33f37637d..1482a93d0 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -13,8 +13,10 @@ task_t :: mach_port_t semaphore_t :: distinct u64 -kern_return_t :: distinct u64 -thread_act_t :: distinct u64 +kern_return_t :: distinct u64 +thread_act_t :: distinct u64 +thread_state_t :: distinct ^u32 +thread_list_t :: [^]thread_act_t MACH_MSG_PORT_DESCRIPTOR :: 0 @@ -30,15 +32,30 @@ MACH_MSGH_BITS_COMPLEX :: 0x80000000 MACH_PORT_RIGHT_SEND :: 0 MACH_PORT_RIGHT_RECEIVE :: 1 +VM_PROT_NONE :: 0 +VM_PROT_READ :: 1 +VM_PROT_WRITE :: 2 +VM_PROT_EXECUTE :: 4 + +VM_INHERIT_SHARE :: 0 +VM_INHERIT_COPY :: 1 +VM_INHERIT_NONE :: 2 +VM_INHERIT_DONATE_COPY :: 3 + TASK_BOOTSTRAP_PORT :: 4 +X86_THREAD_STATE32 :: 1 +X86_THREAD_STATE64 :: 4 +X86_THREAD_STATE32_COUNT :: size_of(x86_thread_state32_t) / size_of(u32) +X86_THREAD_STATE64_COUNT :: size_of(x86_thread_state64_t) / size_of(u32) + mach_msg_option_t :: distinct i32 name_t :: distinct cstring mach_msg_port_descriptor_t :: struct { name: mach_port_t, _: u32, - extra: bit_field u32 { + using _: bit_field u32 { _: u32 | 16, disposition: u32 | 8, type: u32 | 8, @@ -63,17 +80,68 @@ mach_msg_trailer_t :: struct { msgh_trailer_size: u32, } +x86_thread_state32_t :: struct { + eax: u32, + ebx: u32, + ecx: u32, + edx: u32, + edi: u32, + esi: u32, + ebp: u32, + esp: u32, + ss: u32, + eflags: u32, + eip: u32, + cs: u32, + ds: u32, + es: u32, + fs: u32, + gs: u32, +} + +x86_thread_state64_t :: struct { + rax: u64, + rbx: u64, + rcx: u64, + rdx: u64, + rdi: u64, + rsi: u64, + rbp: u64, + rsp: u64, + r8: u64, + r9: u64, + r10: u64, + r11: u64, + r12: u64, + r13: u64, + r14: u64, + r15: u64, + rip: u64, + rflags: u64, + cs: u64, + fs: u64, + gs: u64, +} + @(default_calling_convention="c") foreign mach { - mach_task_self :: proc() -> task_t --- - mach_msg :: proc(header: rawptr, option: mach_msg_option_t, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> kern_return_t --- - mach_msg_send :: proc(header: rawptr) -> kern_return_t --- + mach_task_self :: proc() -> task_t --- + mach_msg :: proc(header: rawptr, option: mach_msg_option_t, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> kern_return_t --- + mach_msg_send :: proc(header: rawptr) -> kern_return_t --- + mach_vm_allocate :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> kern_return_t --- + mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> kern_return_t --- + mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: i32) -> kern_return_t --- mach_port_allocate :: proc(task: task_t, right: u32, name: rawptr) -> kern_return_t --- mach_port_deallocate :: proc(task: task_t, name: u32) -> kern_return_t --- mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> kern_return_t --- task_get_special_port :: proc(task: task_t, port: i32, special_port: ^mach_port_t) -> kern_return_t --- + task_suspend :: proc(task: task_t) -> kern_return_t --- + task_resume :: proc(task: task_t) -> kern_return_t --- + task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> kern_return_t --- + thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t --- + bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t --- bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t --- From c520f029bb4702a3190c92945eef15dd4fca52ba Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 29 Aug 2024 00:54:45 -0700 Subject: [PATCH 003/160] initial posix/spawn --- core/sys/posix/spawn.odin | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 core/sys/posix/spawn.odin diff --git a/core/sys/posix/spawn.odin b/core/sys/posix/spawn.odin new file mode 100644 index 000000000..3ef738b52 --- /dev/null +++ b/core/sys/posix/spawn.odin @@ -0,0 +1,13 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +foreign lib { + posix_spawn :: proc(pid: ^pid_t, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- +} From 0e344ae2d6b5be224433e82cdb13515e87339097 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 29 Aug 2024 01:54:09 -0700 Subject: [PATCH 004/160] add ARM thread state --- core/sys/darwin/mach_darwin.odin | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 1482a93d0..31aff259c 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -18,6 +18,9 @@ thread_act_t :: distinct u64 thread_state_t :: distinct ^u32 thread_list_t :: [^]thread_act_t +MACH_PORT_NULL :: 0 +MACH_PORT_DEAD :: ~mach_port_t(0) + MACH_MSG_PORT_DESCRIPTOR :: 0 MACH_SEND_MSG :: 0x00000001 @@ -44,10 +47,12 @@ VM_INHERIT_DONATE_COPY :: 3 TASK_BOOTSTRAP_PORT :: 4 +BOOTSTRAP_NAME_IN_USE :: 1101 + X86_THREAD_STATE32 :: 1 X86_THREAD_STATE64 :: 4 -X86_THREAD_STATE32_COUNT :: size_of(x86_thread_state32_t) / size_of(u32) -X86_THREAD_STATE64_COUNT :: size_of(x86_thread_state64_t) / size_of(u32) +ARM_THREAD_STATE64 :: 6 + mach_msg_option_t :: distinct i32 name_t :: distinct cstring @@ -98,6 +103,7 @@ x86_thread_state32_t :: struct { fs: u32, gs: u32, } +X86_THREAD_STATE32_COUNT :: size_of(x86_thread_state32_t) / size_of(u32) x86_thread_state64_t :: struct { rax: u64, @@ -122,6 +128,18 @@ x86_thread_state64_t :: struct { fs: u64, gs: u64, } +X86_THREAD_STATE64_COUNT :: size_of(x86_thread_state64_t) / size_of(u32) + +arm_thread_state64_t :: struct { + x: [29]u64, + fp: u64, + lr: u64, + sp: u64, + pc: u64, + cpsr: u32, + pad: u32, +} +ARM_THREAD_STATE64_COUNT :: size_of(arm_thread_state64_t) / size_of(u32) @(default_calling_convention="c") foreign mach { From d026ac2df24bc8c4fe2c8e418d028b9ecebef845 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 29 Aug 2024 11:41:36 -0700 Subject: [PATCH 005/160] add thread-info and memmap-info --- core/sys/darwin/mach_darwin.odin | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 31aff259c..2bcb12cbb 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -17,6 +17,7 @@ kern_return_t :: distinct u64 thread_act_t :: distinct u64 thread_state_t :: distinct ^u32 thread_list_t :: [^]thread_act_t +vm_region_recurse_info_t :: distinct ^i32 MACH_PORT_NULL :: 0 MACH_PORT_DEAD :: ~mach_port_t(0) @@ -141,6 +142,36 @@ arm_thread_state64_t :: struct { } ARM_THREAD_STATE64_COUNT :: size_of(arm_thread_state64_t) / size_of(u32) +THREAD_IDENTIFIER_INFO :: 4 +thread_identifier_info :: struct { + thread_id: u64, + thread_handler: u64, + dispatch_qaddr: u64, +} +THREAD_IDENTIFIER_INFO_COUNT :: size_of(thread_identifier_info) / size_of(u32) + +vm_region_submap_info_64 :: struct { + protection: u32, + max_protection: u32, + inheritance: u32, + offset: u64, + user_tag: u32, + pages_residept: u32, + pages_shared_now_private: u32, + pages_swapped_out: u32, + pages_dirtied: u32, + ref_count: u32, + shadow_depth: u16, + external_pager: u8, + share_mode: u8, + is_submap: b32, + behavior: i32, + object_id: u32, + user_wired_count: u16, + pages_reusable: u32, +} +VM_REGION_SUBMAP_INFO_COUNT_64 :: size_of(vm_region_submap_info_64) / size_of(u32) + @(default_calling_convention="c") foreign mach { mach_task_self :: proc() -> task_t --- @@ -149,6 +180,7 @@ foreign mach { mach_vm_allocate :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> kern_return_t --- mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> kern_return_t --- mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: i32) -> kern_return_t --- + mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> kern_return_t --- mach_port_allocate :: proc(task: task_t, right: u32, name: rawptr) -> kern_return_t --- mach_port_deallocate :: proc(task: task_t, name: u32) -> kern_return_t --- @@ -158,7 +190,9 @@ foreign mach { task_suspend :: proc(task: task_t) -> kern_return_t --- task_resume :: proc(task: task_t) -> kern_return_t --- task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> kern_return_t --- + thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t --- + thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t --- bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t --- bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t --- From cc6e1bdcd6b638c0379079e293dc3381d772b666 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 29 Aug 2024 12:02:54 -0700 Subject: [PATCH 006/160] add more dyld info collect --- core/sys/darwin/mach_darwin.odin | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 2bcb12cbb..47bc97c9b 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -18,6 +18,7 @@ thread_act_t :: distinct u64 thread_state_t :: distinct ^u32 thread_list_t :: [^]thread_act_t vm_region_recurse_info_t :: distinct ^i32 +task_info_t :: distinct ^i32 MACH_PORT_NULL :: 0 MACH_PORT_DEAD :: ~mach_port_t(0) @@ -54,6 +55,7 @@ X86_THREAD_STATE32 :: 1 X86_THREAD_STATE64 :: 4 ARM_THREAD_STATE64 :: 6 +TASK_DYLD_INFO :: 17 mach_msg_option_t :: distinct i32 name_t :: distinct cstring @@ -172,6 +174,14 @@ vm_region_submap_info_64 :: struct { } VM_REGION_SUBMAP_INFO_COUNT_64 :: size_of(vm_region_submap_info_64) / size_of(u32) +TASK_DYLD_INFO :: 17 +task_dyld_info :: struct { + all_image_info_addr: u64, + all_image_info_size: u64, + all_image_info_format: i32, +} +TASK_DYLD_INFO_COUNT :: size_of(task_dyld_info) / size_of(u32) + @(default_calling_convention="c") foreign mach { mach_task_self :: proc() -> task_t --- @@ -190,6 +200,7 @@ foreign mach { task_suspend :: proc(task: task_t) -> kern_return_t --- task_resume :: proc(task: task_t) -> kern_return_t --- task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> kern_return_t --- + task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t --- thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t --- thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t --- From 06bc3b2a3667360faa79c2b1f9008cdddb73b23b Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 29 Aug 2024 13:41:53 -0700 Subject: [PATCH 007/160] oops, duplicate const --- core/sys/darwin/mach_darwin.odin | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 47bc97c9b..42c8c6bc1 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -55,8 +55,6 @@ X86_THREAD_STATE32 :: 1 X86_THREAD_STATE64 :: 4 ARM_THREAD_STATE64 :: 6 -TASK_DYLD_INFO :: 17 - mach_msg_option_t :: distinct i32 name_t :: distinct cstring From 2119062a5a8d35ac77dfc931a59e7254859681cb Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 29 Aug 2024 17:00:02 -0700 Subject: [PATCH 008/160] add more dyld support --- core/sys/darwin/mach_darwin.odin | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 42c8c6bc1..bbaa290e6 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -180,6 +180,59 @@ task_dyld_info :: struct { } TASK_DYLD_INFO_COUNT :: size_of(task_dyld_info) / size_of(u32) +dyld_image_info :: struct { + image_load_addr: u64, + image_file_path: cstring, + image_file_mod_date: u64, +} + +dyld_uuid_info :: struct { + image_load_addr: u64, + image_uuid: [16]u8, +} + +dyld_all_image_infos :: struct { + version: u32, + info_array_count: u32, + info_array: rawptr, + notification: rawptr, + process_detached_from_shared_region: b32, + libSystem_initialized: b32, + dyld_image_load_addr: u64, + jit_info: rawptr, + dyld_version: cstring, + error_message: cstring, + termination_flags: u64, + core_symbolication_shm_page: rawptr, + system_order_flag: u64, + uuid_array_count: u64, + uuid_array: rawptr, + dyld_all_image_infos_addr: u64, + initial_image_count: u64, + error_kind: u64, + error_client_of_dylib_path: cstring, + error_target_dylib_path: cstring, + error_symbol: cstring, + shared_cache_slide: u64, + shared_cache_uuid: [16]u8, + shared_cache_base_addr: u64, + info_array_change_timestamp: u64, + dyld_path: cstring, + notify_ports: [8]mach_port_t, + reserved: [7]u64, + shared_cache_fsid: u64, + shared_cache_fsobjid: u64, + compact_dyld_image_info_addr: u64, + compact_dyld_image_info_size: u64, + platform: u32, + aot_info_count: u32, + aot_info_array: rawptr, + aot_info_array_change_timestamp: u64, + aot_shared_cache_base_address: u64, + aot_shared_cache_uuid: [16]u8, +} + + @(default_calling_convention="c") foreign mach { mach_task_self :: proc() -> task_t --- @@ -189,6 +242,9 @@ foreign mach { mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> kern_return_t --- mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: i32) -> kern_return_t --- mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> kern_return_t --- + vm_page_size: u64 + vm_page_mask: u64 + vm_page_shift: i32 mach_port_allocate :: proc(task: task_t, right: u32, name: rawptr) -> kern_return_t --- mach_port_deallocate :: proc(task: task_t, name: u32) -> kern_return_t --- @@ -215,3 +271,7 @@ foreign mach { semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t --- } + +mach_vm_trunc_page :: proc(v: u64) -> u64 { + return v & ~vm_page_mask +} From 99938c7d4fb26d43a07dd4b8f4f00ab87e67e73f Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 24 Sep 2024 02:31:44 -0700 Subject: [PATCH 009/160] spawn tweaks --- core/os/os_darwin.odin | 22 ++++++++++++++++------ core/sys/darwin/mach_darwin.odin | 1 + core/sys/posix/spawn.odin | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 69411fdb7..f694dd588 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -7,6 +7,7 @@ foreign import pthread "system:System.framework" import "base:runtime" import "core:strings" import "core:c" +import "core:sys/posix" Handle :: distinct i32 Pid :: distinct i32 @@ -585,8 +586,6 @@ F_GETPATH :: 50 // return the full path of the fd foreign libc { @(link_name="__error") __error :: proc() -> ^c.int --- - @(link_name="posix_spawn") _unix_posix_spawn :: proc(pid: ^Pid, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg mode: ..u16) -> Handle --- @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @@ -680,7 +679,7 @@ get_last_error_string :: proc() -> string { return string(_darwin_string_error(__error()^)) } -posix_spawn :: proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr) -> (Pid, Error) { +_spawn :: #force_inline proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr, is_spawnp: bool) -> (posix.pid_t, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() path_cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -695,13 +694,24 @@ posix_spawn :: proc(path: string, args: []string, envs: []string, file_actions: envs_cstrs[i] = strings.clone_to_cstring(envs[i], context.temp_allocator) } - child_pid: Pid - status := _unix_posix_spawn(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) + child_pid: posix.pid_t + status: i32 + if is_spawnp { + status = posix.posix_spawnp(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) + } else { + status = posix.posix_spawn(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) + } if status != 0 { - return 0, get_last_error() + return 0, Platform_Error(status) } return child_pid, nil } +spawn :: proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr) -> (posix.pid_t, Error) { + return _spawn(path, args, envs, file_actions, attributes, false) +} +spawnp :: proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr) -> (posix.pid_t, Error) { + return _spawn(path, args, envs, file_actions, attributes, true) +} @(require_results) open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (handle: Handle, err: Error) { diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index bbaa290e6..1e728fe78 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -255,6 +255,7 @@ foreign mach { task_resume :: proc(task: task_t) -> kern_return_t --- task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> kern_return_t --- task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t --- + task_terminate :: proc(task: task_t) -> kern_return_t --- thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t --- thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t --- diff --git a/core/sys/posix/spawn.odin b/core/sys/posix/spawn.odin index 3ef738b52..154509160 100644 --- a/core/sys/posix/spawn.odin +++ b/core/sys/posix/spawn.odin @@ -10,4 +10,5 @@ when ODIN_OS == .Darwin { foreign lib { posix_spawn :: proc(pid: ^pid_t, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- + posix_spawnp :: proc(pid: ^pid_t, file: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- } From 65b4c793f011ab67ad51773273ebc4333c12bff5 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Sun, 27 Apr 2025 22:47:03 +1000 Subject: [PATCH 010/160] Add -vet-explicit-allocators This vet flag will make it so that allocators must be explicitly used in places where context.allocator and context.temp_allocator are a procedure parameter. The goal of this flag is to prevent using the context.allocator in cases where a different allocator was meant to be used. Some code bases default context.allocator to nil/panic allocator to catch this at runtime. This effectively makes it a compile time error instead. --- src/build_settings.cpp | 3 +++ src/check_expr.cpp | 37 ++++++++++++++++++++++++++++++------- src/main.cpp | 26 ++++++++++++++++---------- src/parser.cpp | 5 +++++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c941e0f68..c2d244440 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -305,6 +305,7 @@ enum VetFlags : u64 { VetFlag_Cast = 1u<<8, VetFlag_Tabs = 1u<<9, VetFlag_UnusedProcedures = 1u<<10, + VetFlag_ExplicitAllocators = 1u<<11, VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, @@ -338,6 +339,8 @@ u64 get_vet_flag_from_name(String const &name) { return VetFlag_Tabs; } else if (name == "unused-procedures") { return VetFlag_UnusedProcedures; + } else if (name == "explicit-allocators") { + return VetFlag_ExplicitAllocators; } return VetFlag_NONE; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 91d9e669f..6daa3edf8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6253,20 +6253,43 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A for (isize i = 0; i < pt->param_count; i++) { if (!visited[i]) { Entity *e = pt->params->Tuple.variables[i]; + bool context_allocator_error = false; if (e->kind == Entity_Variable) { if (e->Variable.param_value.kind != ParameterValue_Invalid) { - ordered_operands[i].mode = Addressing_Value; - ordered_operands[i].type = e->type; - ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + if (ast_file_vet_explicit_allocators(c->file)) { + // NOTE(lucas): check if we are trying to default to context.allocator or context.temp_allocator + if (e->Variable.param_value.original_ast_expr->kind == Ast_SelectorExpr) { + auto& expr = e->Variable.param_value.original_ast_expr->SelectorExpr.expr; + auto& selector = e->Variable.param_value.original_ast_expr->SelectorExpr.selector; + if (expr->kind == Ast_Implicit && + expr->Implicit.string == STR_LIT("context") && + selector->kind == Ast_Ident && + (selector->Ident.token.string == STR_LIT("allocator") || + selector->Ident.token.string == STR_LIT("temp_allocator"))) { + context_allocator_error = true; + } + } + } - dummy_argument_count += 1; - score += assign_score_function(1); - continue; + if (!context_allocator_error) { + ordered_operands[i].mode = Addressing_Value; + ordered_operands[i].type = e->type; + ordered_operands[i].expr = e->Variable.param_value.original_ast_expr; + + dummy_argument_count += 1; + score += assign_score_function(1); + continue; + } } } if (show_error) { - if (e->kind == Entity_TypeName) { + if (context_allocator_error) { + gbString str = type_to_string(e->type); + error(call, "Parameter '%.*s' of type '%s' must be explicitly provided in procedure call", + LIT(e->token.string), str); + gb_string_free(str); + } else if (e->kind == Entity_TypeName) { error(call, "Type parameter '%.*s' is missing in procedure call", LIT(e->token.string)); } else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) { diff --git a/src/main.cpp b/src/main.cpp index b2cfbe018..1b02d78f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -346,6 +346,7 @@ enum BuildFlagKind { BuildFlag_VetCast, BuildFlag_VetTabs, BuildFlag_VetPackages, + BuildFlag_VetExplicitAllocators, BuildFlag_CustomAttribute, BuildFlag_IgnoreUnknownAttributes, @@ -565,6 +566,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_VetExplicitAllocators, str_lit("vet-explicit-allocators"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); @@ -1276,16 +1278,17 @@ gb_internal bool parse_build_flags(Array args) { build_context.vet_flags |= VetFlag_All; break; - case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break; - case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break; - case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; - case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; - case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break; - case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break; - case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; - case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; - case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; - case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; + case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break; + case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break; + case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; + case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; + case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break; + case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break; + case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; + case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; + case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; + case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; + case BuildFlag_VetExplicitAllocators: build_context.vet_flags |= VetFlag_ExplicitAllocators; break; case BuildFlag_VetUnusedProcedures: build_context.vet_flags |= VetFlag_UnusedProcedures; if (!set_flags[BuildFlag_VetPackages]) { @@ -2841,6 +2844,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio print_usage_line(2, "Checks for the use of 'using' as a statement."); print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); } + if (print_flag("-vet-explicit-allocators")) { + print_usage_line(2, "Checks for the use of allocators being explicitely passed to a procedure."); + } } if (check) { diff --git a/src/parser.cpp b/src/parser.cpp index a397585e8..ea95dac31 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -33,6 +33,10 @@ gb_internal bool ast_file_vet_deprecated(AstFile *f) { return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0; } +gb_internal bool ast_file_vet_explicit_allocators(AstFile *f) { + return (ast_file_vet_flags(f) & VetFlag_ExplicitAllocators) != 0; +} + gb_internal bool file_allow_newline(AstFile *f) { bool is_strict = build_context.strict_style || ast_file_vet_style(f); return !is_strict; @@ -6325,6 +6329,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) { error_line("\textra\n"); error_line("\tcast\n"); error_line("\ttabs\n"); + error_line("\texplicit-allocators\n"); return build_context.vet_flags; } } From 51d427f424b7ffbf44bb9aa1e9086e2984b419f0 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Mon, 28 Apr 2025 19:30:35 +1000 Subject: [PATCH 011/160] Remove vet explicit allocators from cli --- src/main.cpp | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1b02d78f6..b2cfbe018 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -346,7 +346,6 @@ enum BuildFlagKind { BuildFlag_VetCast, BuildFlag_VetTabs, BuildFlag_VetPackages, - BuildFlag_VetExplicitAllocators, BuildFlag_CustomAttribute, BuildFlag_IgnoreUnknownAttributes, @@ -566,7 +565,6 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check); - add_flag(&build_flags, BuildFlag_VetExplicitAllocators, str_lit("vet-explicit-allocators"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); @@ -1278,17 +1276,16 @@ gb_internal bool parse_build_flags(Array args) { build_context.vet_flags |= VetFlag_All; break; - case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break; - case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break; - case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; - case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; - case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break; - case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break; - case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; - case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; - case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; - case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; - case BuildFlag_VetExplicitAllocators: build_context.vet_flags |= VetFlag_ExplicitAllocators; break; + case BuildFlag_VetUnusedVariables: build_context.vet_flags |= VetFlag_UnusedVariables; break; + case BuildFlag_VetUnusedImports: build_context.vet_flags |= VetFlag_UnusedImports; break; + case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break; + case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break; + case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break; + case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break; + case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; + case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; + case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; + case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; case BuildFlag_VetUnusedProcedures: build_context.vet_flags |= VetFlag_UnusedProcedures; if (!set_flags[BuildFlag_VetPackages]) { @@ -2844,9 +2841,6 @@ gb_internal void print_show_help(String const arg0, String command, String optio print_usage_line(2, "Checks for the use of 'using' as a statement."); print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring."); } - if (print_flag("-vet-explicit-allocators")) { - print_usage_line(2, "Checks for the use of allocators being explicitely passed to a procedure."); - } } if (check) { From b1ed22d84f2aae617c90c60701e9f25345cab980 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sun, 8 Jun 2025 16:16:34 -0700 Subject: [PATCH 012/160] fix frequency grab --- core/sys/darwin/mach_darwin.odin | 4 ++-- core/time/tsc_darwin.odin | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 01affa6e8..2c8f38002 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -123,7 +123,7 @@ x86_thread_state32_t :: struct { } X86_THREAD_STATE32_COUNT :: size_of(x86_thread_state32_t) / size_of(u32) -x86_thread_state64_t :: struct { +x86_thread_state64_t :: struct #packed { rax: u64, rbx: u64, rcx: u64, @@ -148,7 +148,7 @@ x86_thread_state64_t :: struct { } X86_THREAD_STATE64_COUNT :: size_of(x86_thread_state64_t) / size_of(u32) -arm_thread_state64_t :: struct { +arm_thread_state64_t :: struct #packed { x: [29]u64, fp: u64, lr: u64, diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin index 3726cff49..1210992a6 100644 --- a/core/time/tsc_darwin.odin +++ b/core/time/tsc_darwin.odin @@ -4,7 +4,13 @@ package time import "core:sys/unix" _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) { - unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return + if ODIN_ARCH == .amd64 { + unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return + } else if ODIN_ARCH == .arm64 { + unix.sysctlbyname("hw.tbfrequency", &freq) or_return + } else { + return + } ok = true return } From 019084a17fb179a823a213591b016a43620e47ce Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Sat, 5 Jul 2025 13:55:14 -0700 Subject: [PATCH 013/160] table lookup intrinsic --- base/intrinsics/intrinsics.odin | 1 + core/simd/simd.odin | 51 +++++++ src/check_builtin.cpp | 52 +++++++ src/checker_builtin_procs.hpp | 2 + src/llvm_backend_proc.cpp | 262 ++++++++++++++++++++++++++++++++ 5 files changed, 368 insertions(+) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index c1d16c5e4..9edf7bcd8 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -310,6 +310,7 @@ simd_indices :: proc($T: typeid/#simd[$N]$E) -> T where type_is_numeric(T) --- simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T --- simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- +simd_table_lookup :: proc(table: #simd[N]T, indices: #simd[N]T) -> #simd[N]T where type_is_integer(T) --- // Lane-wise operations simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index b4779b5ff..eb4912e58 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -2440,6 +2440,57 @@ Graphically, the operation looks as follows. The `t` and `f` represent the */ select :: intrinsics.simd_select +/* +Runtime Equivalent to Shuffle. + +Performs element-wise table lookups using runtime indices. +Each element in the indices vector selects an element from the table vector. +The indices are automatically masked to prevent out-of-bounds access. + +This operation is hardware-accelerated on most platforms when using 8-bit +integer vectors. For other element types or unsupported vector sizes, it +falls back to software emulation. + +Inputs: +- `table`: The lookup table vector (should be power-of-2 size for correct masking). +- `indices`: The indices vector (automatically masked to valid range). + +Returns: +- A vector where `result[i] = table[indices[i] & (table_size-1)]`. + +Operation: + + for i in 0 ..< len(indices) { + masked_index := indices[i] & (len(table) - 1) + result[i] = table[masked_index] + } + return result + +Implementation: + + | Platform | Lane Size | Implementation | + |-------------|-------------------------------------------|---------------------| + | x86-64 | pshufb (16B), vpshufb (32B), AVX512 (64B) | Single vector | + | ARM64 | tbl1 (16B), tbl2 (32B), tbl4 (64B) | Automatic splitting | + | ARM32 | vtbl1 (8B), vtbl2 (16B), vtbl4 (32B) | Automatic splitting | + | WebAssembly | i8x16.swizzle (16B), Emulation (>16B) | Mixed | + | Other | Emulation | Software | + +Example: + + import "core:simd" + import "core:fmt" + + table_lookup_example :: proc() { + table := simd.u8x16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + indices := simd.u8x16{15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} + result := simd.table_lookup(table, indices) + fmt.println(result) // Expected: {15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} + } + +*/ +table_lookup :: intrinsics.simd_table_lookup + /* Compute the square root of each lane in a SIMD vector. */ diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 9f9787b61..c7386a97d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1150,6 +1150,58 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return true; } + case BuiltinProc_simd_table_lookup: + { + if (ce->args.count != 2) { + error(call, "'%.*s' expected 2 arguments, got %td", LIT(builtin_name), ce->args.count); + return false; + } + + Operand table = {}; + Operand indices = {}; + check_expr(c, &table, ce->args[0]); if (table.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &indices, ce->args[1], table.type); if (indices.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(table.type)) { + error(table.expr, "'%.*s' expected a simd vector type for table", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(indices.type)) { + error(indices.expr, "'%.*s' expected a simd vector type for indices", LIT(builtin_name)); + return false; + } + + Type *table_elem = base_array_type(table.type); + Type *indices_elem = base_array_type(indices.type); + + if (!is_type_integer(table_elem)) { + gbString table_str = type_to_string(table.type); + error(table.expr, "'%.*s' expected table to be a simd vector of integers, got '%s'", LIT(builtin_name), table_str); + gb_string_free(table_str); + return false; + } + + if (!is_type_integer(indices_elem)) { + gbString indices_str = type_to_string(indices.type); + error(indices.expr, "'%.*s' expected indices to be a simd vector of integers, got '%s'", LIT(builtin_name), indices_str); + gb_string_free(indices_str); + return false; + } + + if (!are_types_identical(table.type, indices.type)) { + gbString table_str = type_to_string(table.type); + gbString indices_str = type_to_string(indices.type); + error(indices.expr, "'%.*s' expected table and indices to have the same type, got '%s' vs '%s'", LIT(builtin_name), table_str, indices_str); + gb_string_free(indices_str); + gb_string_free(table_str); + return false; + } + + operand->mode = Addressing_Value; + operand->type = table.type; + return true; + } + case BuiltinProc_simd_ceil: case BuiltinProc_simd_floor: case BuiltinProc_simd_trunc: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 90652cb0b..59fc84a4e 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -191,6 +191,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_shuffle, BuiltinProc_simd_select, + BuiltinProc_simd_table_lookup, BuiltinProc_simd_ceil, BuiltinProc_simd_floor, @@ -550,6 +551,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_table_lookup"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 9f6a1d653..da8e0f91c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1721,6 +1721,268 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn return res; } + case BuiltinProc_simd_table_lookup: + { + LLVMValueRef table = arg0.value; + LLVMValueRef indices = lb_build_expr(p, ce->args[1]).value; + + Type *vt = arg0.type; + GB_ASSERT(vt->kind == Type_SimdVector); + i64 count = vt->SimdVector.count; + Type *elem_type = vt->SimdVector.elem; + i64 elem_size = type_size_of(elem_type); + + // Determine strategy based on element size and target architecture + char const *intrinsic_name = nullptr; + bool use_hardware_table_lookup = false; + + // 8-bit elements: Use dedicated table lookup instructions + if (elem_size == 1) { + use_hardware_table_lookup = true; + + if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) { + // x86/x86-64: Use pshufb intrinsics + switch (count) { + case 16: + intrinsic_name = "llvm.x86.ssse3.pshuf.b.128"; + break; + case 32: + intrinsic_name = "llvm.x86.avx2.pshuf.b"; + break; + case 64: + intrinsic_name = "llvm.x86.avx512.pshuf.b.512"; + break; + default: + use_hardware_table_lookup = false; + break; + } + } else if (build_context.metrics.arch == TargetArch_arm64) { + // ARM64: Use NEON tbl intrinsics with automatic table splitting + switch (count) { + case 16: + intrinsic_name = "llvm.aarch64.neon.tbl1"; + break; + case 32: + intrinsic_name = "llvm.aarch64.neon.tbl2"; + break; + case 48: + intrinsic_name = "llvm.aarch64.neon.tbl3"; + break; + case 64: + intrinsic_name = "llvm.aarch64.neon.tbl4"; + break; + default: + use_hardware_table_lookup = false; + break; + } + } else if (build_context.metrics.arch == TargetArch_arm32) { + // ARM32: Use NEON vtbl intrinsics with automatic table splitting + switch (count) { + case 8: + intrinsic_name = "llvm.arm.neon.vtbl1"; + break; + case 16: + intrinsic_name = "llvm.arm.neon.vtbl2"; + break; + case 24: + intrinsic_name = "llvm.arm.neon.vtbl3"; + break; + case 32: + intrinsic_name = "llvm.arm.neon.vtbl4"; + break; + default: + use_hardware_table_lookup = false; + break; + } + } else if (build_context.metrics.arch == TargetArch_wasm32 || build_context.metrics.arch == TargetArch_wasm64p32) { + // WebAssembly: Use swizzle (only supports 16-byte vectors) + if (count == 16) { + intrinsic_name = "llvm.wasm.swizzle"; + } else { + use_hardware_table_lookup = false; + } + } else { + use_hardware_table_lookup = false; + } + } + + if (use_hardware_table_lookup && intrinsic_name != nullptr) { + // Use dedicated hardware table lookup instruction + + // Check if required target features are enabled + bool features_enabled = true; + if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) { + // x86/x86-64 feature checking + if (count == 32) { + // AVX2 requires ssse3 + avx2 features + if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) || + !check_target_feature_is_enabled(str_lit("avx2"), nullptr)) { + features_enabled = false; + } + } else if (count == 64) { + // AVX512 requires ssse3 + avx2 + avx512f + avx512bw features + if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) || + !check_target_feature_is_enabled(str_lit("avx2"), nullptr) || + !check_target_feature_is_enabled(str_lit("avx512f"), nullptr) || + !check_target_feature_is_enabled(str_lit("avx512bw"), nullptr)) { + features_enabled = false; + } + } + } else if (build_context.metrics.arch == TargetArch_arm64 || build_context.metrics.arch == TargetArch_arm32) { + // ARM/ARM64 feature checking - NEON is required for all table lookups + if (!check_target_feature_is_enabled(str_lit("neon"), nullptr)) { + features_enabled = false; + } + } + + if (features_enabled) { + // Add target features to function attributes for LLVM instruction selection + if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) { + // x86/x86-64 function attributes + if (count == 32) { + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+ssse3")); + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256")); + } else if (count == 64) { + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+avx512f,+avx512bw,+ssse3")); + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("512")); + } + } else if (build_context.metrics.arch == TargetArch_arm64) { + // ARM64 function attributes - enable NEON for table lookup instructions + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon")); + // Set appropriate vector width for multi-table operations + if (count >= 32) { + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256")); + } + } else if (build_context.metrics.arch == TargetArch_arm32) { + // ARM32 function attributes - enable NEON for table lookup instructions + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon")); + } + + // Handle ARM's multi-table intrinsics by splitting the table vector + if (build_context.metrics.arch == TargetArch_arm64 && count > 16) { + // ARM64 TBL2/TBL3/TBL4: Split table into multiple 16-byte vectors + LLVMTypeRef v16i8_type = LLVMVectorType(LLVMInt8TypeInContext(p->module->ctx), 16); + int num_tables = count / 16; + GB_ASSERT_MSG(count % 16 == 0, "ARM64 table size must be multiple of 16 bytes, got %lld bytes", count); + GB_ASSERT_MSG(num_tables <= 4, "ARM64 NEON supports maximum 4 tables (tbl4), got %d tables for %lld-byte vector", num_tables, count); + + LLVMValueRef table_parts[4]; // Max 4 tables for tbl4 + for (int i = 0; i < num_tables; i++) { + // Extract 16-byte slice from the larger table + LLVMValueRef indices_for_extract[16]; + for (int j = 0; j < 16; j++) { + indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 16 + j, false); + } + LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 16); + table_parts[i] = LLVMBuildShuffleVector(p->builder, table, LLVMGetUndef(LLVMTypeOf(table)), extract_mask, ""); + } + + // Call appropriate ARM64 tbl intrinsic + if (count == 32) { + LLVMValueRef args[3] = { table_parts[0], table_parts[1], indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0); + } else if (count == 48) { + LLVMValueRef args[4] = { table_parts[0], table_parts[1], table_parts[2], indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0); + } else if (count == 64) { + LLVMValueRef args[5] = { table_parts[0], table_parts[1], table_parts[2], table_parts[3], indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0); + } + } else if (build_context.metrics.arch == TargetArch_arm32 && count > 8) { + // ARM32 VTBL2/VTBL3/VTBL4: Split table into multiple 8-byte vectors + LLVMTypeRef v8i8_type = LLVMVectorType(LLVMInt8TypeInContext(p->module->ctx), 8); + int num_tables = count / 8; + GB_ASSERT_MSG(count % 8 == 0, "ARM32 table size must be multiple of 8 bytes, got %lld bytes", count); + GB_ASSERT_MSG(num_tables <= 4, "ARM32 NEON supports maximum 4 tables (vtbl4), got %d tables for %lld-byte vector", num_tables, count); + + LLVMValueRef table_parts[4]; // Max 4 tables for vtbl4 + for (int i = 0; i < num_tables; i++) { + // Extract 8-byte slice from the larger table + LLVMValueRef indices_for_extract[8]; + for (int j = 0; j < 8; j++) { + indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 8 + j, false); + } + LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 8); + table_parts[i] = LLVMBuildShuffleVector(p->builder, table, LLVMGetUndef(LLVMTypeOf(table)), extract_mask, ""); + } + + // Call appropriate ARM32 vtbl intrinsic + if (count == 16) { + LLVMValueRef args[3] = { table_parts[0], table_parts[1], indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0); + } else if (count == 24) { + LLVMValueRef args[4] = { table_parts[0], table_parts[1], table_parts[2], indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0); + } else if (count == 32) { + LLVMValueRef args[5] = { table_parts[0], table_parts[1], table_parts[2], table_parts[3], indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0); + } + } else { + // Single-table case (x86, WebAssembly, ARM single-table) + LLVMValueRef args[2] = { table, indices }; + res.value = lb_call_intrinsic(p, intrinsic_name, args, gb_count_of(args), nullptr, 0); + } + return res; + } else { + // Features not enabled, fall back to emulation + use_hardware_table_lookup = false; + } + } + + // Fallback: Emulate with extracts and inserts for all element sizes + GB_ASSERT(count > 0 && count <= 64); // Sanity check + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + LLVMTypeRef i32_type = LLVMInt32TypeInContext(p->module->ctx); + LLVMTypeRef elem_llvm_type = lb_type(p->module, elem_type); + + // Calculate mask based on element size and vector count + i64 max_index = count - 1; + LLVMValueRef index_mask; + + if (elem_size == 1) { + // 8-bit: mask to table size (like pshufb behavior) + index_mask = LLVMConstInt(elem_llvm_type, max_index, false); + } else if (elem_size == 2) { + // 16-bit: mask to table size + index_mask = LLVMConstInt(elem_llvm_type, max_index, false); + } else if (elem_size == 4) { + // 32-bit: mask to table size + index_mask = LLVMConstInt(elem_llvm_type, max_index, false); + } else { + // 64-bit: mask to table size + index_mask = LLVMConstInt(elem_llvm_type, max_index, false); + } + + for (i64 i = 0; i < count; i++) { + LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false); + LLVMValueRef index_elem = LLVMBuildExtractElement(p->builder, indices, idx_i, ""); + + // Mask index to valid range + LLVMValueRef masked_index = LLVMBuildAnd(p->builder, index_elem, index_mask, ""); + + // Convert to i32 for extractelement + LLVMValueRef index_i32; + if (LLVMGetIntTypeWidth(LLVMTypeOf(masked_index)) < 32) { + index_i32 = LLVMBuildZExt(p->builder, masked_index, i32_type, ""); + } else if (LLVMGetIntTypeWidth(LLVMTypeOf(masked_index)) > 32) { + index_i32 = LLVMBuildTrunc(p->builder, masked_index, i32_type, ""); + } else { + index_i32 = masked_index; + } + + values[i] = LLVMBuildExtractElement(p->builder, table, index_i32, ""); + } + + // Build result vector + res.value = LLVMGetUndef(LLVMTypeOf(table)); + for (i64 i = 0; i < count; i++) { + LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false); + res.value = LLVMBuildInsertElement(p->builder, res.value, values[i], idx_i, ""); + } + return res; + } + case BuiltinProc_simd_ceil: case BuiltinProc_simd_floor: case BuiltinProc_simd_trunc: From 33c6a979e93a4f4c203d596a6244c3fa3c53730f Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Sat, 5 Jul 2025 14:11:29 -0700 Subject: [PATCH 014/160] fix explicit cast --- src/llvm_backend_proc.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index da8e0f91c..a1c62d555 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1861,8 +1861,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn // Handle ARM's multi-table intrinsics by splitting the table vector if (build_context.metrics.arch == TargetArch_arm64 && count > 16) { // ARM64 TBL2/TBL3/TBL4: Split table into multiple 16-byte vectors - LLVMTypeRef v16i8_type = LLVMVectorType(LLVMInt8TypeInContext(p->module->ctx), 16); - int num_tables = count / 16; + int num_tables = cast(int)(count / 16); GB_ASSERT_MSG(count % 16 == 0, "ARM64 table size must be multiple of 16 bytes, got %lld bytes", count); GB_ASSERT_MSG(num_tables <= 4, "ARM64 NEON supports maximum 4 tables (tbl4), got %d tables for %lld-byte vector", num_tables, count); @@ -1890,8 +1889,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn } } else if (build_context.metrics.arch == TargetArch_arm32 && count > 8) { // ARM32 VTBL2/VTBL3/VTBL4: Split table into multiple 8-byte vectors - LLVMTypeRef v8i8_type = LLVMVectorType(LLVMInt8TypeInContext(p->module->ctx), 8); - int num_tables = count / 8; + int num_tables = cast(int)count / 8; GB_ASSERT_MSG(count % 8 == 0, "ARM32 table size must be multiple of 8 bytes, got %lld bytes", count); GB_ASSERT_MSG(num_tables <= 4, "ARM32 NEON supports maximum 4 tables (vtbl4), got %d tables for %lld-byte vector", num_tables, count); From fc78f6e83bf800c76cd8ac86281359c91261dd89 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Sat, 5 Jul 2025 16:26:07 -0700 Subject: [PATCH 015/160] x86 sse --- src/llvm_backend_proc.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a1c62d555..1fe8a15fe 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1813,7 +1813,12 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn bool features_enabled = true; if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) { // x86/x86-64 feature checking - if (count == 32) { + if (count == 16) { + // SSE/SSSE3 for 128-bit vectors + if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr)) { + features_enabled = false; + } + } else if (count == 32) { // AVX2 requires ssse3 + avx2 features if (!check_target_feature_is_enabled(str_lit("ssse3"), nullptr) || !check_target_feature_is_enabled(str_lit("avx2"), nullptr)) { @@ -1839,7 +1844,11 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn // Add target features to function attributes for LLVM instruction selection if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) { // x86/x86-64 function attributes - if (count == 32) { + if (count == 16) { + // SSE/SSSE3 for 128-bit vectors + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+ssse3")); + lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("128")); + } else if (count == 32) { lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+avx,+avx2,+ssse3")); lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256")); } else if (count == 64) { From 2dae1d8a4134226887a6e6a0aad0318e46e40cde Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 7 Jul 2025 14:36:31 -0700 Subject: [PATCH 016/160] fix tabbing --- core/sys/darwin/mach_darwin.odin | 4 ---- core/time/tsc_darwin.odin | 14 +++++++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 2c8f38002..b66b90325 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -5,10 +5,6 @@ foreign import mach "system:System.framework" import "core:c" import "base:intrinsics" -// NOTE(tetra): Unclear whether these should be aligned 16 or not. -// However all other sync primitives are aligned for robustness. -// I cannot currently align these though. -// See core/sys/unix/pthread_linux.odin/pthread_t. mach_port_t :: distinct c.uint task_t :: mach_port_t diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin index 1210992a6..fdda5db77 100644 --- a/core/time/tsc_darwin.odin +++ b/core/time/tsc_darwin.odin @@ -4,13 +4,13 @@ package time import "core:sys/unix" _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) { - if ODIN_ARCH == .amd64 { - unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return - } else if ODIN_ARCH == .arm64 { - unix.sysctlbyname("hw.tbfrequency", &freq) or_return - } else { - return - } + if ODIN_ARCH == .amd64 { + unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return + } else if ODIN_ARCH == .arm64 { + unix.sysctlbyname("hw.tbfrequency", &freq) or_return + } else { + return + } ok = true return } From bab4ce11fc1d52e2a63ed950fcf3cb0510cbe642 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 14 Jul 2025 21:55:28 -0400 Subject: [PATCH 017/160] Rename `iOS` subtarget to `iPhone` for consistency. Add `ODIN_PLATFORM_SUBTARGET_IOS` builtin constant which evaluated to `true` when the platform is `Darwin` and the subtarget it either `iPhone` or `iPhoneSimulator` --- base/builtin/builtin.odin | 2 +- base/runtime/core.odin | 4 +++- core/sys/darwin/sync.odin | 20 +++++--------------- core/sys/info/platform_darwin.odin | 2 +- src/build_settings.cpp | 10 +++++----- src/checker.cpp | 2 +- src/linker.cpp | 2 +- 7 files changed, 17 insertions(+), 25 deletions(-) diff --git a/base/builtin/builtin.odin b/base/builtin/builtin.odin index af102ee0b..2dd214321 100644 --- a/base/builtin/builtin.odin +++ b/base/builtin/builtin.odin @@ -145,7 +145,7 @@ ODIN_OS_STRING :: ODIN_OS_STRING /* An `enum` value indicating the platform subtarget, chosen using the `-subtarget` switch. - Possible values are: `.Default` `.iOS`, .iPhoneSimulator, and `.Android`. + Possible values are: `.Default` `.iPhone`, .iPhoneSimulator, and `.Android`. */ ODIN_PLATFORM_SUBTARGET :: ODIN_PLATFORM_SUBTARGET diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 6c43e6c16..090d1a65b 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -557,7 +557,7 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{ // Defined internally by the compiler Odin_Platform_Subtarget_Type :: enum int { Default, - iOS, + iPhone, iPhoneSimulator Android, } @@ -566,6 +566,8 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET) Odin_Platform_Subtarget_Types :: bit_set[Odin_Platform_Subtarget_Type] +@(builtin) +ODIN_PLATFORM_SUBTARGET_IOS :: ODIN_PLATFORM_SUBTARGET == .iPhone || ODIN_PLATFORM_SUBTARGET == .iPhoneSimulator /* // Defined internally by the compiler diff --git a/core/sys/darwin/sync.odin b/core/sys/darwin/sync.odin index 6d68dc8f8..5f4f16fc3 100644 --- a/core/sys/darwin/sync.odin +++ b/core/sys/darwin/sync.odin @@ -3,23 +3,13 @@ package darwin // #define OS_WAIT_ON_ADDR_AVAILABILITY \ // __API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4)) when ODIN_OS == .Darwin { - - when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 17_04_00 { - WAIT_ON_ADDRESS_AVAILABLE :: true - } else when ODIN_MINIMUM_OS_VERSION >= 14_04_00 { - WAIT_ON_ADDRESS_AVAILABLE :: true + when ODIN_PLATFORM_SUBTARGET_IOS { + WAIT_ON_ADDRESS_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 17_04_00 + ULOCK_WAIT_2_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 14_00_00 } else { - WAIT_ON_ADDRESS_AVAILABLE :: false + WAIT_ON_ADDRESS_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 14_04_00 + ULOCK_WAIT_2_AVAILABLE :: ODIN_MINIMUM_OS_VERSION >= 11_00_00 } - - when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 14_00_00 { - ULOCK_WAIT_2_AVAILABLE :: true - } else when ODIN_MINIMUM_OS_VERSION >= 11_00_00 { - ULOCK_WAIT_2_AVAILABLE :: true - } else { - ULOCK_WAIT_2_AVAILABLE :: false - } - } else { WAIT_ON_ADDRESS_AVAILABLE :: false ULOCK_WAIT_2_AVAILABLE :: false diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index dd7f0fa03..3fc8064ec 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -28,7 +28,7 @@ init_platform :: proc() { macos_version = {int(version.majorVersion), int(version.minorVersion), int(version.patchVersion)} - when ODIN_PLATFORM_SUBTARGET == .iOS { + when ODIN_PLATFORM_SUBTARGET_IOS { os_version.platform = .iOS ws(&b, "iOS") } else { diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d98340844..46e7ecb4e 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -171,7 +171,7 @@ struct TargetMetrics { enum Subtarget : u32 { Subtarget_Default, - Subtarget_iOS, + Subtarget_iPhone, Subtarget_iPhoneSimulator, Subtarget_Android, @@ -180,7 +180,7 @@ enum Subtarget : u32 { gb_global String subtarget_strings[Subtarget_COUNT] = { str_lit(""), - str_lit("ios"), + str_lit("iphone"), str_lit("iphonesimulator"), str_lit("android"), }; @@ -1828,7 +1828,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta if (metrics->os == TargetOs_darwin) { switch (subtarget) { - case Subtarget_iOS: + case Subtarget_iPhone: switch (metrics->arch) { case TargetArch_arm64: bc->metrics.target_triplet = str_lit("arm64-apple-ios"); @@ -1909,7 +1909,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta if (!bc->minimum_os_version_string_given) { if (subtarget == Subtarget_Default) { bc->minimum_os_version_string = str_lit("11.0.0"); - } else if (subtarget == Subtarget_iOS || subtarget == Subtarget_iPhoneSimulator) { + } else if (subtarget == Subtarget_iPhone || subtarget == Subtarget_iPhoneSimulator) { // NOTE(harold): We default to 17.4 on iOS because that's when os_sync_wait_on_address was added and // we'd like to avoid any potential App Store issues by using the private ulock_* there. bc->minimum_os_version_string = str_lit("17.4"); @@ -1917,7 +1917,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta } if (subtarget == Subtarget_iPhoneSimulator) { - // For the iOS simulator subtarget, the version must be between 'ios' and '-simulator'. + // For the iPhoneSimulator subtarget, the version must be between 'ios' and '-simulator'. String suffix = str_lit("-simulator"); GB_ASSERT(string_ends_with(bc->metrics.target_triplet, suffix)); diff --git a/src/checker.cpp b/src/checker.cpp index 625536caa..1821cbd4d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1172,7 +1172,7 @@ gb_internal void init_universal(void) { { GlobalEnumValue values[Subtarget_COUNT] = { {"Default", Subtarget_Default}, - {"iOS", Subtarget_iOS}, + {"iPhone", Subtarget_iPhone}, {"iPhoneSimulator", Subtarget_iPhoneSimulator}, {"Android", Subtarget_Android}, }; diff --git a/src/linker.cpp b/src/linker.cpp index 7647ed872..41333a3c9 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -787,7 +787,7 @@ try_cross_linking:; // being set to 'MacOSX' even though we've set the sysroot to the correct SDK (-Wincompatible-sysroot). // This is because it is likely not using the SDK's toolchain Apple Clang but another one installed in the system. switch (selected_subtarget) { - case Subtarget_iOS: + case Subtarget_iPhone: darwin_platform_name = "iPhoneOS"; darwin_xcrun_sdk_name = "iphoneos"; darwin_min_version_id = "ios"; From 63b9cb18ef9d64c1de6b5dbb018993968586dd59 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 14 Jul 2025 22:32:06 -0400 Subject: [PATCH 018/160] Missing rename in panic string --- src/build_settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 46e7ecb4e..fb88b588a 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1834,7 +1834,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->metrics.target_triplet = str_lit("arm64-apple-ios"); break; default: - GB_PANIC("Unknown architecture for -subtarget:ios"); + GB_PANIC("Unknown architecture for -subtarget:iphone"); } break; case Subtarget_iPhoneSimulator: From efe4f40974a760d2a950ea4ff2d1cd708865b426 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Tue, 15 Jul 2025 19:09:36 -0400 Subject: [PATCH 019/160] Fix @objc_implement methods not respecting @objc_is_class_method Fix incorrect type encoding for objc_class --- base/runtime/procs_darwin.odin | 1 + src/llvm_backend.cpp | 42 ++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/base/runtime/procs_darwin.odin b/base/runtime/procs_darwin.odin index 0aec57e80..20f09400d 100644 --- a/base/runtime/procs_darwin.odin +++ b/base/runtime/procs_darwin.odin @@ -31,5 +31,6 @@ foreign ObjC { class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar --- class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint --- ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr --- + object_getClass :: proc "c" (obj: objc_id) -> objc_Class --- } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 3cf77256b..13a1d8cf3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1300,18 +1300,8 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { const bool is_union = base->kind == Type_Union; if (!is_union) { - // Check for objc_SEL - if (internal_check_is_assignable_to(base, t_objc_SEL)) { - return str_lit(":"); - } - - // Check for objc_Class - if (internal_check_is_assignable_to(base, t_objc_SEL)) { - return str_lit("#"); - } - // Treat struct as an Objective-C Class? - if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) { + if (has_type_got_objc_class_attribute(t) && pointer_depth == 0) { return str_lit("#"); } } @@ -1354,6 +1344,17 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { return str_lit("?"); case Type_Pointer: { + // NOTE: These types are pointers, so we must check here for special cases + // Check for objc_SEL + if (internal_check_is_assignable_to(t, t_objc_SEL)) { + return str_lit(":"); + } + + // Check for objc_Class + if (internal_check_is_assignable_to(t, t_objc_Class)) { + return str_lit("#"); + } + String pointee = lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1); // Special case for Objective-C Objects if (pointer_depth == 0 && pointee == "@") { @@ -1645,6 +1646,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { continue; } + // Check if it has any class methods ahead of time so that we know to grab the meta_class + lbValue meta_class_value = {}; + for (const ObjcMethodData &md : *methods) { + if (!md.ac.objc_is_class_method) { + continue; + } + + // Get the meta_class + args.count = 1; + args[0] = class_value; + meta_class_value = lb_emit_runtime_call(p, "object_getClass", args); + + break; + } + for (const ObjcMethodData &md : *methods) { GB_ASSERT( md.proc_entity->kind == Entity_Procedure); Type *method_type = md.proc_entity->type; @@ -1770,8 +1786,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { GB_ASSERT(sel_address); lbValue selector_value = lb_addr_load(p, *sel_address); + lbValue target_class = !md.ac.objc_is_class_method ? class_value : meta_class_value; + args.count = 4; - args[0] = class_value; // Class + args[0] = target_class; // Class args[1] = selector_value; // SEL args[2] = lbValue { wrapper_proc->value, wrapper_proc->type }; args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding)); From e5baf483fd6c2788818f7c9611122872379f10ae Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Jul 2025 15:14:41 +0100 Subject: [PATCH 020/160] Update `vendor:kb_text_shape` to v1.03 --- vendor/kb_text_shape/kb_text_shape_procs.odin | 32 +- vendor/kb_text_shape/kb_text_shape_types.odin | 216 +- vendor/kb_text_shape/lib/kb_text_shape.lib | Bin 699064 -> 714902 bytes vendor/kb_text_shape/src/kb_text_shape.h | 4224 ++++++++++++----- 4 files changed, 3179 insertions(+), 1293 deletions(-) diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index 047e16852..efcdcc4ed 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -14,6 +14,12 @@ import "core:mem" @(default_calling_convention="c", link_prefix="kbts_", require_results) foreign lib { + FeatureTagToId :: proc(Tag: feature_tag) -> feature_id --- + FeatureOverride :: proc(Id: feature_id, Alternate: b32, Value: u32) -> feature_override --- + FeatureOverrideFromTag :: proc(Tag: feature_tag, Alternate: b32, Value: u32) -> feature_override --- + GlyphConfigOverrideFeature :: proc(Config: ^glyph_config, Id: feature_id, Alternate: b32, Value: u32) -> b32 --- + GlyphConfigOverrideFeatureFromTag :: proc(Config: ^glyph_config, Tag: feature_tag, Alternate: b32, Value: u32) -> b32 --- + FontIsValid :: proc(Font: ^font) -> b32 --- SizeOfShapeState :: proc(Font: ^font) -> un --- @@ -21,7 +27,7 @@ foreign lib { ShapeConfig :: proc(Font: ^font, Script: script, Language: language) -> shape_config --- ShaperIsComplex :: proc(Shaper: shaper) -> b32 --- - ScriptIsComplex :: proc(Script: script) -> b32 --- + ScriptTagToScript :: proc(Tag: script_tag) -> script --- Shape :: proc(State: ^shape_state, Config: ^shape_config, MainDirection, RunDirection: direction, @@ -37,6 +43,26 @@ foreign lib { InferScript :: proc(Direction: ^direction, Script: ^script, GlyphScript: script) --- } + +@(require_results) +GlyphConfig :: proc "c" (FeatureOverrides: []feature_override) -> glyph_config { + @(default_calling_convention="c", require_results) + foreign lib { + kbts_GlyphConfig :: proc(FeatureOverrides: [^]feature_override, FeatureOverrideCount: u32) -> glyph_config --- + } + return kbts_GlyphConfig(raw_data(FeatureOverrides), u32(len(FeatureOverrides))) + +} + +@(require_results) +EmptyGlyphConfig :: proc(FeatureOverrides: []feature_override) -> glyph_config { + @(default_calling_convention="c", require_results) + foreign lib { + kbts_EmptyGlyphConfig :: proc(FeatureOverrides: [^]feature_override, FeatureOverrideCapacity: u32) -> glyph_config --- + } + return kbts_EmptyGlyphConfig(raw_data(FeatureOverrides), u32(len(FeatureOverrides))) +} + @(require_results) PlaceShapeState :: proc "c" (Memory: []byte) -> ^shape_state { @(default_calling_convention="c", require_results) @@ -58,10 +84,10 @@ DecodeUtf8 :: proc "contextless" (String: string) -> (Codepoint: rune, SourceCha @(default_calling_convention="c", require_results) foreign lib { - kbts_DecodeUtf8 :: proc(Utf8: [^]byte, Length: uint) -> decode --- + kbts_DecodeUtf8 :: proc(Utf8: [^]byte, Length: un) -> decode --- } - Decode := kbts_DecodeUtf8(raw_data(String), len(String)) + Decode := kbts_DecodeUtf8(raw_data(String), un(len(String))) return Decode.Codepoint, Decode.SourceCharactersConsumed, bool(Decode.Valid) } diff --git a/vendor/kb_text_shape/kb_text_shape_types.odin b/vendor/kb_text_shape/kb_text_shape_types.odin index 0c94318eb..952393e57 100644 --- a/vendor/kb_text_shape/kb_text_shape_types.odin +++ b/vendor/kb_text_shape/kb_text_shape_types.odin @@ -801,6 +801,7 @@ op_kind :: enum u8 { NORMALIZE_HANGUL, FLAG_JOINING_LETTERS, GSUB_FEATURES, + GSUB_FEATURES_WITH_USER, // Positioning ops. GPOS_METRICS, @@ -1067,6 +1068,179 @@ shaper :: enum u32 { USE, } +script_tag :: enum u32 { + DONT_KNOW = (' ' | ' '<<8 | ' '<<16 | ' '<<24), + ADLAM = ('a' | 'd'<<8 | 'l'<<16 | 'm'<<24), + AHOM = ('a' | 'h'<<8 | 'o'<<16 | 'm'<<24), + ANATOLIAN_HIEROGLYPHS = ('h' | 'l'<<8 | 'u'<<16 | 'w'<<24), + ARABIC = ('a' | 'r'<<8 | 'a'<<16 | 'b'<<24), + ARMENIAN = ('a' | 'r'<<8 | 'm'<<16 | 'n'<<24), + AVESTAN = ('a' | 'v'<<8 | 's'<<16 | 't'<<24), + BALINESE = ('b' | 'a'<<8 | 'l'<<16 | 'i'<<24), + BAMUM = ('b' | 'a'<<8 | 'm'<<16 | 'u'<<24), + BASSA_VAH = ('b' | 'a'<<8 | 's'<<16 | 's'<<24), + BATAK = ('b' | 'a'<<8 | 't'<<16 | 'k'<<24), + BENGALI = ('b' | 'n'<<8 | 'g'<<16 | '2'<<24), + BHAIKSUKI = ('b' | 'h'<<8 | 'k'<<16 | 's'<<24), + BOPOMOFO = ('b' | 'o'<<8 | 'p'<<16 | 'o'<<24), + BRAHMI = ('b' | 'r'<<8 | 'a'<<16 | 'h'<<24), + BUGINESE = ('b' | 'u'<<8 | 'g'<<16 | 'i'<<24), + BUHID = ('b' | 'u'<<8 | 'h'<<16 | 'd'<<24), + CANADIAN_SYLLABICS = ('c' | 'a'<<8 | 'n'<<16 | 's'<<24), + CARIAN = ('c' | 'a'<<8 | 'r'<<16 | 'i'<<24), + CAUCASIAN_ALBANIAN = ('a' | 'g'<<8 | 'h'<<16 | 'b'<<24), + CHAKMA = ('c' | 'a'<<8 | 'k'<<16 | 'm'<<24), + CHAM = ('c' | 'h'<<8 | 'a'<<16 | 'm'<<24), + CHEROKEE = ('c' | 'h'<<8 | 'e'<<16 | 'r'<<24), + CHORASMIAN = ('c' | 'h'<<8 | 'r'<<16 | 's'<<24), + CJK_IDEOGRAPHIC = ('h' | 'a'<<8 | 'n'<<16 | 'i'<<24), + COPTIC = ('c' | 'o'<<8 | 'p'<<16 | 't'<<24), + CYPRIOT_SYLLABARY = ('c' | 'p'<<8 | 'r'<<16 | 't'<<24), + CYPRO_MINOAN = ('c' | 'p'<<8 | 'm'<<16 | 'n'<<24), + CYRILLIC = ('c' | 'y'<<8 | 'r'<<16 | 'l'<<24), + DEFAULT = ('D' | 'F'<<8 | 'L'<<16 | 'T'<<24), + DEFAULT2 = ('D' | 'F'<<8 | 'L'<<16 | 'T'<<24), + DESERET = ('d' | 's'<<8 | 'r'<<16 | 't'<<24), + DEVANAGARI = ('d' | 'e'<<8 | 'v'<<16 | '2'<<24), + DIVES_AKURU = ('d' | 'i'<<8 | 'a'<<16 | 'k'<<24), + DOGRA = ('d' | 'o'<<8 | 'g'<<16 | 'r'<<24), + DUPLOYAN = ('d' | 'u'<<8 | 'p'<<16 | 'l'<<24), + EGYPTIAN_HIEROGLYPHS = ('e' | 'g'<<8 | 'y'<<16 | 'p'<<24), + ELBASAN = ('e' | 'l'<<8 | 'b'<<16 | 'a'<<24), + ELYMAIC = ('e' | 'l'<<8 | 'y'<<16 | 'm'<<24), + ETHIOPIC = ('e' | 't'<<8 | 'h'<<16 | 'i'<<24), + GARAY = ('g' | 'a'<<8 | 'r'<<16 | 'a'<<24), + GEORGIAN = ('g' | 'e'<<8 | 'o'<<16 | 'r'<<24), + GLAGOLITIC = ('g' | 'l'<<8 | 'a'<<16 | 'g'<<24), + GOTHIC = ('g' | 'o'<<8 | 't'<<16 | 'h'<<24), + GRANTHA = ('g' | 'r'<<8 | 'a'<<16 | 'n'<<24), + GREEK = ('g' | 'r'<<8 | 'e'<<16 | 'k'<<24), + GUJARATI = ('g' | 'j'<<8 | 'r'<<16 | '2'<<24), + GUNJALA_GONDI = ('g' | 'o'<<8 | 'n'<<16 | 'g'<<24), + GURMUKHI = ('g' | 'u'<<8 | 'r'<<16 | '2'<<24), + GURUNG_KHEMA = ('g' | 'u'<<8 | 'k'<<16 | 'h'<<24), + HANGUL = ('h' | 'a'<<8 | 'n'<<16 | 'g'<<24), + HANIFI_ROHINGYA = ('r' | 'o'<<8 | 'h'<<16 | 'g'<<24), + HANUNOO = ('h' | 'a'<<8 | 'n'<<16 | 'o'<<24), + HATRAN = ('h' | 'a'<<8 | 't'<<16 | 'r'<<24), + HEBREW = ('h' | 'e'<<8 | 'b'<<16 | 'r'<<24), + HIRAGANA = ('k' | 'a'<<8 | 'n'<<16 | 'a'<<24), + IMPERIAL_ARAMAIC = ('a' | 'r'<<8 | 'm'<<16 | 'i'<<24), + INSCRIPTIONAL_PAHLAVI = ('p' | 'h'<<8 | 'l'<<16 | 'i'<<24), + INSCRIPTIONAL_PARTHIAN = ('p' | 'r'<<8 | 't'<<16 | 'i'<<24), + JAVANESE = ('j' | 'a'<<8 | 'v'<<16 | 'a'<<24), + KAITHI = ('k' | 't'<<8 | 'h'<<16 | 'i'<<24), + KANNADA = ('k' | 'n'<<8 | 'd'<<16 | '2'<<24), + KATAKANA = ('k' | 'a'<<8 | 'n'<<16 | 'a'<<24), + KAWI = ('k' | 'a'<<8 | 'w'<<16 | 'i'<<24), + KAYAH_LI = ('k' | 'a'<<8 | 'l'<<16 | 'i'<<24), + KHAROSHTHI = ('k' | 'h'<<8 | 'a'<<16 | 'r'<<24), + KHITAN_SMALL_SCRIPT = ('k' | 'i'<<8 | 't'<<16 | 's'<<24), + KHMER = ('k' | 'h'<<8 | 'm'<<16 | 'r'<<24), + KHOJKI = ('k' | 'h'<<8 | 'o'<<16 | 'j'<<24), + KHUDAWADI = ('s' | 'i'<<8 | 'n'<<16 | 'd'<<24), + KIRAT_RAI = ('k' | 'r'<<8 | 'a'<<16 | 'i'<<24), + LAO = ('l' | 'a'<<8 | 'o'<<16 | ' '<<24), + LATIN = ('l' | 'a'<<8 | 't'<<16 | 'n'<<24), + LEPCHA = ('l' | 'e'<<8 | 'p'<<16 | 'c'<<24), + LIMBU = ('l' | 'i'<<8 | 'm'<<16 | 'b'<<24), + LINEAR_A = ('l' | 'i'<<8 | 'n'<<16 | 'a'<<24), + LINEAR_B = ('l' | 'i'<<8 | 'n'<<16 | 'b'<<24), + LISU = ('l' | 'i'<<8 | 's'<<16 | 'u'<<24), + LYCIAN = ('l' | 'y'<<8 | 'c'<<16 | 'i'<<24), + LYDIAN = ('l' | 'y'<<8 | 'd'<<16 | 'i'<<24), + MAHAJANI = ('m' | 'a'<<8 | 'h'<<16 | 'j'<<24), + MAKASAR = ('m' | 'a'<<8 | 'k'<<16 | 'a'<<24), + MALAYALAM = ('m' | 'l'<<8 | 'm'<<16 | '2'<<24), + MANDAIC = ('m' | 'a'<<8 | 'n'<<16 | 'd'<<24), + MANICHAEAN = ('m' | 'a'<<8 | 'n'<<16 | 'i'<<24), + MARCHEN = ('m' | 'a'<<8 | 'r'<<16 | 'c'<<24), + MASARAM_GONDI = ('g' | 'o'<<8 | 'n'<<16 | 'm'<<24), + MEDEFAIDRIN = ('m' | 'e'<<8 | 'd'<<16 | 'f'<<24), + MEETEI_MAYEK = ('m' | 't'<<8 | 'e'<<16 | 'i'<<24), + MENDE_KIKAKUI = ('m' | 'e'<<8 | 'n'<<16 | 'd'<<24), + MEROITIC_CURSIVE = ('m' | 'e'<<8 | 'r'<<16 | 'c'<<24), + MEROITIC_HIEROGLYPHS = ('m' | 'e'<<8 | 'r'<<16 | 'o'<<24), + MIAO = ('p' | 'l'<<8 | 'r'<<16 | 'd'<<24), + MODI = ('m' | 'o'<<8 | 'd'<<16 | 'i'<<24), + MONGOLIAN = ('m' | 'o'<<8 | 'n'<<16 | 'g'<<24), + MRO = ('m' | 'r'<<8 | 'o'<<16 | 'o'<<24), + MULTANI = ('m' | 'u'<<8 | 'l'<<16 | 't'<<24), + MYANMAR = ('m' | 'y'<<8 | 'm'<<16 | '2'<<24), + NABATAEAN = ('n' | 'b'<<8 | 'a'<<16 | 't'<<24), + NAG_MUNDARI = ('n' | 'a'<<8 | 'g'<<16 | 'm'<<24), + NANDINAGARI = ('n' | 'a'<<8 | 'n'<<16 | 'd'<<24), + NEWA = ('n' | 'e'<<8 | 'w'<<16 | 'a'<<24), + NEW_TAI_LUE = ('t' | 'a'<<8 | 'l'<<16 | 'u'<<24), + NKO = ('n' | 'k'<<8 | 'o'<<16 | ' '<<24), + NUSHU = ('n' | 's'<<8 | 'h'<<16 | 'u'<<24), + NYIAKENG_PUACHUE_HMONG = ('h' | 'm'<<8 | 'n'<<16 | 'p'<<24), + OGHAM = ('o' | 'g'<<8 | 'a'<<16 | 'm'<<24), + OL_CHIKI = ('o' | 'l'<<8 | 'c'<<16 | 'k'<<24), + OL_ONAL = ('o' | 'n'<<8 | 'a'<<16 | 'o'<<24), + OLD_ITALIC = ('i' | 't'<<8 | 'a'<<16 | 'l'<<24), + OLD_HUNGARIAN = ('h' | 'u'<<8 | 'n'<<16 | 'g'<<24), + OLD_NORTH_ARABIAN = ('n' | 'a'<<8 | 'r'<<16 | 'b'<<24), + OLD_PERMIC = ('p' | 'e'<<8 | 'r'<<16 | 'm'<<24), + OLD_PERSIAN_CUNEIFORM = ('x' | 'p'<<8 | 'e'<<16 | 'o'<<24), + OLD_SOGDIAN = ('s' | 'o'<<8 | 'g'<<16 | 'o'<<24), + OLD_SOUTH_ARABIAN = ('s' | 'a'<<8 | 'r'<<16 | 'b'<<24), + OLD_TURKIC = ('o' | 'r'<<8 | 'k'<<16 | 'h'<<24), + OLD_UYGHUR = ('o' | 'u'<<8 | 'g'<<16 | 'r'<<24), + ODIA = ('o' | 'r'<<8 | 'y'<<16 | '2'<<24), + OSAGE = ('o' | 's'<<8 | 'g'<<16 | 'e'<<24), + OSMANYA = ('o' | 's'<<8 | 'm'<<16 | 'a'<<24), + PAHAWH_HMONG = ('h' | 'm'<<8 | 'n'<<16 | 'g'<<24), + PALMYRENE = ('p' | 'a'<<8 | 'l'<<16 | 'm'<<24), + PAU_CIN_HAU = ('p' | 'a'<<8 | 'u'<<16 | 'c'<<24), + PHAGS_PA = ('p' | 'h'<<8 | 'a'<<16 | 'g'<<24), + PHOENICIAN = ('p' | 'h'<<8 | 'n'<<16 | 'x'<<24), + PSALTER_PAHLAVI = ('p' | 'h'<<8 | 'l'<<16 | 'p'<<24), + REJANG = ('r' | 'j'<<8 | 'n'<<16 | 'g'<<24), + RUNIC = ('r' | 'u'<<8 | 'n'<<16 | 'r'<<24), + SAMARITAN = ('s' | 'a'<<8 | 'm'<<16 | 'r'<<24), + SAURASHTRA = ('s' | 'a'<<8 | 'u'<<16 | 'r'<<24), + SHARADA = ('s' | 'h'<<8 | 'r'<<16 | 'd'<<24), + SHAVIAN = ('s' | 'h'<<8 | 'a'<<16 | 'w'<<24), + SIDDHAM = ('s' | 'i'<<8 | 'd'<<16 | 'd'<<24), + SIGN_WRITING = ('s' | 'g'<<8 | 'n'<<16 | 'w'<<24), + SOGDIAN = ('s' | 'o'<<8 | 'g'<<16 | 'd'<<24), + SINHALA = ('s' | 'i'<<8 | 'n'<<16 | 'h'<<24), + SORA_SOMPENG = ('s' | 'o'<<8 | 'r'<<16 | 'a'<<24), + SOYOMBO = ('s' | 'o'<<8 | 'y'<<16 | 'o'<<24), + SUMERO_AKKADIAN_CUNEIFORM = ('x' | 's'<<8 | 'u'<<16 | 'x'<<24), + SUNDANESE = ('s' | 'u'<<8 | 'n'<<16 | 'd'<<24), + SUNUWAR = ('s' | 'u'<<8 | 'n'<<16 | 'u'<<24), + SYLOTI_NAGRI = ('s' | 'y'<<8 | 'l'<<16 | 'o'<<24), + SYRIAC = ('s' | 'y'<<8 | 'r'<<16 | 'c'<<24), + TAGALOG = ('t' | 'g'<<8 | 'l'<<16 | 'g'<<24), + TAGBANWA = ('t' | 'a'<<8 | 'g'<<16 | 'b'<<24), + TAI_LE = ('t' | 'a'<<8 | 'l'<<16 | 'e'<<24), + TAI_THAM = ('l' | 'a'<<8 | 'n'<<16 | 'a'<<24), + TAI_VIET = ('t' | 'a'<<8 | 'v'<<16 | 't'<<24), + TAKRI = ('t' | 'a'<<8 | 'k'<<16 | 'r'<<24), + TAMIL = ('t' | 'm'<<8 | 'l'<<16 | '2'<<24), + TANGSA = ('t' | 'n'<<8 | 's'<<16 | 'a'<<24), + TANGUT = ('t' | 'a'<<8 | 'n'<<16 | 'g'<<24), + TELUGU = ('t' | 'e'<<8 | 'l'<<16 | '2'<<24), + THAANA = ('t' | 'h'<<8 | 'a'<<16 | 'a'<<24), + THAI = ('t' | 'h'<<8 | 'a'<<16 | 'i'<<24), + TIBETAN = ('t' | 'i'<<8 | 'b'<<16 | 't'<<24), + TIFINAGH = ('t' | 'f'<<8 | 'n'<<16 | 'g'<<24), + TIRHUTA = ('t' | 'i'<<8 | 'r'<<16 | 'h'<<24), + TODHRI = ('t' | 'o'<<8 | 'd'<<16 | 'r'<<24), + TOTO = ('t' | 'o'<<8 | 't'<<16 | 'o'<<24), + TULU_TIGALARI = ('t' | 'u'<<8 | 't'<<16 | 'g'<<24), + UGARITIC_CUNEIFORM = ('u' | 'g'<<8 | 'a'<<16 | 'r'<<24), + VAI = ('v' | 'a'<<8 | 'i'<<16 | ' '<<24), + VITHKUQI = ('v' | 'i'<<8 | 't'<<16 | 'h'<<24), + WANCHO = ('w' | 'c'<<8 | 'h'<<16 | 'o'<<24), + WARANG_CITI = ('w' | 'a'<<8 | 'r'<<16 | 'a'<<24), + YEZIDI = ('y' | 'e'<<8 | 'z'<<16 | 'i'<<24), + YI = ('y' | 'i'<<8 | ' '<<16 | ' '<<24), + ZANABAZAR_SQUARE = ('z' | 'a'<<8 | 'n'<<16 | 'b'<<24), +} + script :: enum u32 { DONT_KNOW, ADLAM, @@ -1241,6 +1415,7 @@ script :: enum u32 { } feature_tag :: enum u32 { + UNREGISTERED = 0, isol = ('i' | 's'<<8 | 'o'<<16 | 'l'<<24), /* Isolated Forms */ fina = ('f' | 'i'<<8 | 'n'<<16 | 'a'<<24), /* Terminal Forms */ fin2 = ('f' | 'i'<<8 | 'n'<<16 | '2'<<24), /* Terminal Forms #2 */ @@ -1371,6 +1546,7 @@ feature_tag :: enum u32 { } feature_id :: enum u32 { + UNREGISTERED = 0, isol, /* Isolated Forms */ fina, /* Terminal Forms */ fin2, /* Terminal Forms #2 */ @@ -1531,6 +1707,7 @@ lookup_subtable_info :: struct { font :: struct { FileBase: [^]byte, + FileSize: un, Head: ^head, Cmap: ^u16, Gdef: ^gdef, @@ -1563,16 +1740,30 @@ glyph_classes :: struct { MarkAttachmentClass: u16, } +glyph_config :: struct { + EnabledFeatures: feature_set, + DisabledFeatures: feature_set, + FeatureOverrideCount: u32, + FeatureOverrideCapacity: u32, + RequiredFeatureOverrideCapacity: u32, + FeatureOverrides: [^]feature_override `fmt:"v,FeatureOverrideCount"`, +} + glyph :: struct { Codepoint: rune, - Id: u16, + Id: u16, // Glyph index. This is what you want to use to query outline data. Uid: u16, Classes: glyph_classes, Decomposition: u64, + Config: ^glyph_config, + Flags: glyph_flags, + // These fields are the glyph's final positioning data. + // For normal usage, you should not have to use these directly yourself. + // In case you are curious or have a specific need, see kbts_PositionGlyph() to see how these are used. OffsetX: i32, OffsetY: i32, AdvanceX: i32, @@ -1644,9 +1835,10 @@ skip_flag :: enum u32 { } op_state_gsub :: struct { - LookupIndex: un, - GlyphFilter: glyph_flags, - SkipFlags: skip_flags, + LookupFeatures: feature_set, + LookupIndex: un, + GlyphFilter: glyph_flags, + SkipFlags: skip_flags, } op_state_normalize_hangul :: struct { @@ -1661,6 +1853,7 @@ op_state_op_specific :: struct #raw_union { } lookup_indices :: struct { + FeatureTag: feature_tag, FeatureId: feature_id, SkipFlags: skip_flags, GlyphFilter: glyph_flags, @@ -1672,6 +1865,12 @@ feature_set :: struct { Flags: [(uint(len(feature_id)) + 63) / 64]u64, } +feature_override :: struct { + Id: feature_id, + Tag: feature_tag, + EnabledOrAlternatePlusOne: u32, +} + op :: struct { Kind: op_kind, Features: feature_set, @@ -1686,7 +1885,10 @@ op_state :: struct { ResumePoint: u32, FeatureCount: u32, - FeatureLookupIndices: [MAX_SIMULTANEOUS_FEATURES]lookup_indices, + FeatureLookupIndices: [MAX_SIMULTANEOUS_FEATURES]lookup_indices `fmt:"v,FeatureCount"`, + + UnregisteredFeatureCount: u32, + UnregisteredFeatureTags: [MAX_SIMULTANEOUS_FEATURES]feature_tag `fmt:"v,UnregisteredFeatureCount"`, OpSpecific: op_state_op_specific, @@ -1718,6 +1920,8 @@ shape_config :: struct { Langsys: [shaping_table]^langsys, OpLists: [4]op_list, + Features: ^feature_set, + Shaper: shaper, ShaperProperties: ^shaper_properties, @@ -1747,6 +1951,8 @@ shape_state :: struct { MainDirection: direction, RunDirection: direction, + UserFeatures: feature_set, + GlyphArray: glyph_array, ClusterGlyphArray: glyph_array, diff --git a/vendor/kb_text_shape/lib/kb_text_shape.lib b/vendor/kb_text_shape/lib/kb_text_shape.lib index eb30f917e5dbade034ecb8b0b874f2dd9da26a78..1c3b94779ba2baaa1216b84525fbdae75b28103c 100644 GIT binary patch delta 107984 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 delta 93698 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 + GlyphCapacity = State->RequiredGlyphCapacity; + } + Once Shape() returns 0, you are done shaping. Glyph indices are in the kbts_glyph.Id field. + Please note that, while the glyphs do also contain a Codepoint field, this field will mostly + be meaningless whenever complex shaping operations occur. This is because fonts exclusively + work on glyph indices, and a lot of ligatures are obviously a combination of several codepoints + and do not have a corresponding codepoint in the Unicode world. + The same is true when a single glyph is split into multiple glyphs. A font might decide to + decompose a letter-with-accent into a letter glyph + an accent glyph. In that case, we will + know what the accent glyph's index is, but we are not told what its codepoint is. + + There is currently no way to track where in the source text each glyph originates from. + One thing we might try is to have a "void *UserData" member on each glyph, and flow it through + the different substitutions, but I personally have not needed this yet and I do not have good + test cases for it. If you are interested, let me know! + + Final positions are in font units and can be extracted with Cursor() and PositionGlyph(). + To convert font units to fractional pixels in FreeType: + (FontX * FtSizeMetrics.x_scale) >> 16 + (FontY * FtSizeMetrics.y_scale) >> 16 + This will give you 26.6 fractional pixel units. + See https://freetype.org/freetype2/docs/reference/ft2-sizing_and_scaling.html for more info. kbts_ResetShapeState() + Shaping - feature control + kbts_FeatureOverride() -- Describe a manual override for a font feature + kbts_FeatureOverrideFromTag() -- This also works on features that do not have a kbts_feature_id, but they will be slower. + (Calling this with a feature that has a kbts_feature_id will not be slower.) + kbts_GlyphConfig() -- Bake per-glyph parameters (for now, only feature overrides) + Shaping - feature control - incremental API + With this API, you provide a buffer first, and gradually construct the glyph_config. + kbts_EmptyGlyphConfig() + kbts_GlyphConfigOverrideFeature() + kbts_GlyphConfigOverrideFeatureFromTag() Layout kbts_Cursor() kbts_PositionGlyph() Manual memory management kbts_SizeOfShapeState() kbts_PlaceShapeState() - kbts_ReadFontHeader() -- Read the top of the file and return how many bytes are needed to read the rest - kbts_ReadFontData() -- Read and byteswap the rest + kbts_ReadFontHeader() -- Read and byteswap the top of the file. + kbts_ReadFontData() -- Read and byteswap the rest. kbts_PostReadFontInitialize() -- Initialize auxiliary structures + Example code for reading a font file with this API looks like this: + size_t ScratchSize = kbts_ReadFontHeader(&Font, Data, Size); + size_t PermanentMemorySize = kbts_ReadFontData(&Font, malloc(ScratchSize), ScratchSize); + kbts_PostReadFontInitialize(&Font, malloc(PermanentMemorySize), PermanentMemorySize); + + Please note that, AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE. + AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE. + AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE. + AS SOON AS YOU CALL ReadFontHeader(), THE FONT DATA IS MODIFIED IN-PLACE! + If you need to open the same font with another library, you need to copy the data BEFORE + calling ReadFontHeader(). + + The buffer you pass to ReadFontData() is temporary and can be freed once the function returns. + The buffer you pass to PostReadFontInitialize() is persistent and can only be freed once you + are done with the font. Utility, etc. kbts_ShaperIsComplex() kbts_ScriptIsComplex() kbts_InferScript() -- Stupid script detection. Do not ship this! Use script breaks instead. kbts_DecodeUtf8() + kbts_ScriptTagToScript() + kbts_FeatureTagToId() EXAMPLE USAGE Complete example: @@ -83,7 +172,7 @@ kbts_direction Direction = KBTS_DIRECTION_NONE; for(size_t StringAt = 0; StringAt < Length;) { - kbts_decode Decode = kbts_DecodeUtf8(String, Length - StringAt); + kbts_decode Decode = kbts_DecodeUtf8(String + StringAt, Length - StringAt); StringAt += Decode.SourceCharactersConsumed; if(Decode.Valid) { @@ -142,8 +231,24 @@ } } + Control which font features apply to which glyphs: + kbts_feature_override Ss03FeatureOverrides[] = { + kbts_FeatureOverride(KBTS_FEATURE_ID_ccmp, 0, 0), // Disable ccmp + kbts_FeatureOverride(KBTS_FEATURE_ID_ss03, 0, 1), // Enable ss03 + }; + kbts_glyph_config Ss03Config = kbts_GlyphConfig(Ss03FeatureOverrides, 2); + kbts_feature_override SaltFeatureOverrides[] = { + kbts_FeatureOverride(KBTS_FEATURE_ID_salt, 1, 3), // Pick alternate glyph number 3 from feature 'salt' + }; + kbts_glyph_config SaltConfig = kbts_GlyphConfig(SaltFeatureOverrides, 1); + + // Then, do this before calling kbts_Shape(): + MyGlyphs[0].Config = &Ss03Config; + MyGlyphs[1].Config = &SaltConfig; + Open a font with your own memory: kbts_font Font; + // Be careful: ReadFontHeader() and ReadFontData() both byteswap font data in-place! size_t ScratchSize = kbts_ReadFontHeader(&Font, Data, Size); size_t PermanentMemorySize = kbts_ReadFontData(&Font, malloc(ScratchSize), ScratchSize); kbts_PostReadFontInitialize(&Font, malloc(PermanentMemorySize), PermanentMemorySize); @@ -181,6 +286,29 @@ // Once a shape_config has been created, it is assumed to be immutable and can be trivially shared // between runs/operations that have the same parameters. + PERFORMANCE + Just like most libraries that interact with font files, we use the file as an in-memory database. + There are a few issues with this approach: + - Font files can be arbitrarily complex, making it difficult to predict system behavior at runtime. + - Font files are encoded in big endian byte order, which is stupid and slow. + We compensate for this by pre-processing as much as we can when opening the file. Notably, we + byteswap everything we need in-place, we precompute some useful runtime memory bounds, and we + allocate a few auxiliary acceleration structures. + + Since we byteswap everything in-place, you cannot pass the same font data to kbts and to another + library, because the other library will expect everything to be big endian. + + As a result of this approach, opening fonts is slow and shaping is fast. This is very much intentional. + At the time of writing (2025-07-13), on Harfbuzz's test suite, we are, on average, 4.5x faster than + Harfbuzz on my laptop (Ryzen 9 5900HX). As fonts are complex, and Harfbuzz's test suite is quite varied, + the speedup numbers are rather spread out: + Best: 22x + 10th percentile: 6.5x + Median: 4.5x + 90th percentile: 2.5x + Worst: 1.05x + So, aside from a few extreme cases, you should expect a small integer speedup factor compared to Harfbuzz. + LANGUAGE SUPPORT Shaping is NOT supported for the following scripts: Zawgyi: some fonts exist, but no standardized OpenType feature set seems to exist as of writing. @@ -215,6 +343,19 @@ 0x2069 Pop directional isolate See https://unicode.org/reports/tr9 for more information. + VERSION HISTORY + 1.03 - New functions: kbts_FeatureTagToId(), kbts_FeatureOverrideFromTag(), kbts_EmptyGlyphConfig(), kbts_GlyphConfigOverrideFeature(), kbts_GlyphConfigOverrideFeatureFromTag(), kbts_ScriptTagToScript() + Unregistered features can now be overriden using their tags. + This is slower than overriding registered features, i.e. those that have a kbts_feature_id. + Compiler warning cleanup + 1.02b - Feature control for GPOS features + Bounds checking in ReadFontHeader + 1.02a - Positioning fix for format 2 GPOS pair adjustments + 1.02 - Added per-glyph manual feature control through kbts_FeatureOverride(), kbts_GlyphConfig() + Added enum definitions for features cv01-cv99 and ss01-ss20 + 1.01 - Header cleanup and glyph output documentation + 1.0 - Initial release + TODO Word dictionaries for word breaking: CJK, etc. 'stch' feature. @@ -245,7 +386,7 @@ #ifndef KB_TEXT_SHAPE_INCLUDED -# define KB_TEXT_SHAPE_INDLUDED +# define KB_TEXT_SHAPE_INCLUDED # ifndef kbts_s64 # if defined(_MSC_VER) || defined(__BORLANDC__) @@ -341,6 +482,8 @@ typedef kbts_u8 kbts_joining_feature; enum kbts_joining_feature_enum { KBTS_JOINING_FEATURE_NONE, + + // These must correspond with glyph_flags and FEATURE_IDs. KBTS_JOINING_FEATURE_ISOL, KBTS_JOINING_FEATURE_FINA, KBTS_JOINING_FEATURE_FIN2, @@ -1135,6 +1278,7 @@ enum kbts_op_kind_enum KBTS_OP_KIND_NORMALIZE_HANGUL, KBTS_OP_KIND_FLAG_JOINING_LETTERS, KBTS_OP_KIND_GSUB_FEATURES, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, // Positioning ops. KBTS_OP_KIND_GPOS_METRICS, @@ -1191,8 +1335,11 @@ enum kbts_glyph_flags_enum // In USE, glyphs are mostly not pre-flagged for feature application. // However, we do want to flag rphf/pref results for reordering, so we want to // keep all of the flags as usual, and only use these feature flags for filtering. -#define KBTS_USE_GLYPH_FEATURE_MASK (((KBTS_GLYPH_FLAG_INIT << 1) - 1) | KBTS_GLYPH_FLAG_NUMR | KBTS_GLYPH_FLAG_DNOM | KBTS_GLYPH_FLAG_FRAC) -#define KBTS_JOINING_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT) +#define KBTS_USE_GLYPH_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | \ + KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT | KBTS_GLYPH_FLAG_NUMR | \ + KBTS_GLYPH_FLAG_DNOM | KBTS_GLYPH_FLAG_FRAC) +#define KBTS_JOINING_FEATURE_MASK (KBTS_GLYPH_FLAG_ISOL | KBTS_GLYPH_FLAG_FINA | KBTS_GLYPH_FLAG_FIN2 | KBTS_GLYPH_FLAG_FIN3 | \ + KBTS_GLYPH_FLAG_MEDI | KBTS_GLYPH_FLAG_MED2 | KBTS_GLYPH_FLAG_INIT) #define KBTS_JOINING_FEATURE_TO_GLYPH_FLAG(Feature) (1 << ((Feature) - 1)) // Japanese text contains "kinsoku" characters, around which breaking a line is forbidden. @@ -1420,8 +1567,184 @@ enum kbts_shaper_enum KBTS_SHAPER_COUNT, }; +typedef kbts_u32 kbts_script_tag; +enum kbts_script_tag_enum +{ + KBTS_SCRIPT_TAG_DONT_KNOW = KBTS_FOURCC(' ', ' ', ' ', ' '), + KBTS_SCRIPT_TAG_ADLAM = KBTS_FOURCC('a', 'd', 'l', 'm'), + KBTS_SCRIPT_TAG_AHOM = KBTS_FOURCC('a', 'h', 'o', 'm'), + KBTS_SCRIPT_TAG_ANATOLIAN_HIEROGLYPHS = KBTS_FOURCC('h', 'l', 'u', 'w'), + KBTS_SCRIPT_TAG_ARABIC = KBTS_FOURCC('a', 'r', 'a', 'b'), + KBTS_SCRIPT_TAG_ARMENIAN = KBTS_FOURCC('a', 'r', 'm', 'n'), + KBTS_SCRIPT_TAG_AVESTAN = KBTS_FOURCC('a', 'v', 's', 't'), + KBTS_SCRIPT_TAG_BALINESE = KBTS_FOURCC('b', 'a', 'l', 'i'), + KBTS_SCRIPT_TAG_BAMUM = KBTS_FOURCC('b', 'a', 'm', 'u'), + KBTS_SCRIPT_TAG_BASSA_VAH = KBTS_FOURCC('b', 'a', 's', 's'), + KBTS_SCRIPT_TAG_BATAK = KBTS_FOURCC('b', 'a', 't', 'k'), + KBTS_SCRIPT_TAG_BENGALI = KBTS_FOURCC('b', 'n', 'g', '2'), + KBTS_SCRIPT_TAG_BHAIKSUKI = KBTS_FOURCC('b', 'h', 'k', 's'), + KBTS_SCRIPT_TAG_BOPOMOFO = KBTS_FOURCC('b', 'o', 'p', 'o'), + KBTS_SCRIPT_TAG_BRAHMI = KBTS_FOURCC('b', 'r', 'a', 'h'), + KBTS_SCRIPT_TAG_BUGINESE = KBTS_FOURCC('b', 'u', 'g', 'i'), + KBTS_SCRIPT_TAG_BUHID = KBTS_FOURCC('b', 'u', 'h', 'd'), + KBTS_SCRIPT_TAG_CANADIAN_SYLLABICS = KBTS_FOURCC('c', 'a', 'n', 's'), + KBTS_SCRIPT_TAG_CARIAN = KBTS_FOURCC('c', 'a', 'r', 'i'), + KBTS_SCRIPT_TAG_CAUCASIAN_ALBANIAN = KBTS_FOURCC('a', 'g', 'h', 'b'), + KBTS_SCRIPT_TAG_CHAKMA = KBTS_FOURCC('c', 'a', 'k', 'm'), + KBTS_SCRIPT_TAG_CHAM = KBTS_FOURCC('c', 'h', 'a', 'm'), + KBTS_SCRIPT_TAG_CHEROKEE = KBTS_FOURCC('c', 'h', 'e', 'r'), + KBTS_SCRIPT_TAG_CHORASMIAN = KBTS_FOURCC('c', 'h', 'r', 's'), + KBTS_SCRIPT_TAG_CJK_IDEOGRAPHIC = KBTS_FOURCC('h', 'a', 'n', 'i'), + KBTS_SCRIPT_TAG_COPTIC = KBTS_FOURCC('c', 'o', 'p', 't'), + KBTS_SCRIPT_TAG_CYPRIOT_SYLLABARY = KBTS_FOURCC('c', 'p', 'r', 't'), + KBTS_SCRIPT_TAG_CYPRO_MINOAN = KBTS_FOURCC('c', 'p', 'm', 'n'), + KBTS_SCRIPT_TAG_CYRILLIC = KBTS_FOURCC('c', 'y', 'r', 'l'), + KBTS_SCRIPT_TAG_DEFAULT = KBTS_FOURCC('D', 'F', 'L', 'T'), + KBTS_SCRIPT_TAG_DEFAULT2 = KBTS_FOURCC('D', 'F', 'L', 'T'), + KBTS_SCRIPT_TAG_DESERET = KBTS_FOURCC('d', 's', 'r', 't'), + KBTS_SCRIPT_TAG_DEVANAGARI = KBTS_FOURCC('d', 'e', 'v', '2'), + KBTS_SCRIPT_TAG_DIVES_AKURU = KBTS_FOURCC('d', 'i', 'a', 'k'), + KBTS_SCRIPT_TAG_DOGRA = KBTS_FOURCC('d', 'o', 'g', 'r'), + KBTS_SCRIPT_TAG_DUPLOYAN = KBTS_FOURCC('d', 'u', 'p', 'l'), + KBTS_SCRIPT_TAG_EGYPTIAN_HIEROGLYPHS = KBTS_FOURCC('e', 'g', 'y', 'p'), + KBTS_SCRIPT_TAG_ELBASAN = KBTS_FOURCC('e', 'l', 'b', 'a'), + KBTS_SCRIPT_TAG_ELYMAIC = KBTS_FOURCC('e', 'l', 'y', 'm'), + KBTS_SCRIPT_TAG_ETHIOPIC = KBTS_FOURCC('e', 't', 'h', 'i'), + KBTS_SCRIPT_TAG_GARAY = KBTS_FOURCC('g', 'a', 'r', 'a'), + KBTS_SCRIPT_TAG_GEORGIAN = KBTS_FOURCC('g', 'e', 'o', 'r'), + KBTS_SCRIPT_TAG_GLAGOLITIC = KBTS_FOURCC('g', 'l', 'a', 'g'), + KBTS_SCRIPT_TAG_GOTHIC = KBTS_FOURCC('g', 'o', 't', 'h'), + KBTS_SCRIPT_TAG_GRANTHA = KBTS_FOURCC('g', 'r', 'a', 'n'), + KBTS_SCRIPT_TAG_GREEK = KBTS_FOURCC('g', 'r', 'e', 'k'), + KBTS_SCRIPT_TAG_GUJARATI = KBTS_FOURCC('g', 'j', 'r', '2'), + KBTS_SCRIPT_TAG_GUNJALA_GONDI = KBTS_FOURCC('g', 'o', 'n', 'g'), + KBTS_SCRIPT_TAG_GURMUKHI = KBTS_FOURCC('g', 'u', 'r', '2'), + KBTS_SCRIPT_TAG_GURUNG_KHEMA = KBTS_FOURCC('g', 'u', 'k', 'h'), + KBTS_SCRIPT_TAG_HANGUL = KBTS_FOURCC('h', 'a', 'n', 'g'), + KBTS_SCRIPT_TAG_HANIFI_ROHINGYA = KBTS_FOURCC('r', 'o', 'h', 'g'), + KBTS_SCRIPT_TAG_HANUNOO = KBTS_FOURCC('h', 'a', 'n', 'o'), + KBTS_SCRIPT_TAG_HATRAN = KBTS_FOURCC('h', 'a', 't', 'r'), + KBTS_SCRIPT_TAG_HEBREW = KBTS_FOURCC('h', 'e', 'b', 'r'), + KBTS_SCRIPT_TAG_HIRAGANA = KBTS_FOURCC('k', 'a', 'n', 'a'), + KBTS_SCRIPT_TAG_IMPERIAL_ARAMAIC = KBTS_FOURCC('a', 'r', 'm', 'i'), + KBTS_SCRIPT_TAG_INSCRIPTIONAL_PAHLAVI = KBTS_FOURCC('p', 'h', 'l', 'i'), + KBTS_SCRIPT_TAG_INSCRIPTIONAL_PARTHIAN = KBTS_FOURCC('p', 'r', 't', 'i'), + KBTS_SCRIPT_TAG_JAVANESE = KBTS_FOURCC('j', 'a', 'v', 'a'), + KBTS_SCRIPT_TAG_KAITHI = KBTS_FOURCC('k', 't', 'h', 'i'), + KBTS_SCRIPT_TAG_KANNADA = KBTS_FOURCC('k', 'n', 'd', '2'), + KBTS_SCRIPT_TAG_KATAKANA = KBTS_FOURCC('k', 'a', 'n', 'a'), + KBTS_SCRIPT_TAG_KAWI = KBTS_FOURCC('k', 'a', 'w', 'i'), + KBTS_SCRIPT_TAG_KAYAH_LI = KBTS_FOURCC('k', 'a', 'l', 'i'), + KBTS_SCRIPT_TAG_KHAROSHTHI = KBTS_FOURCC('k', 'h', 'a', 'r'), + KBTS_SCRIPT_TAG_KHITAN_SMALL_SCRIPT = KBTS_FOURCC('k', 'i', 't', 's'), + KBTS_SCRIPT_TAG_KHMER = KBTS_FOURCC('k', 'h', 'm', 'r'), + KBTS_SCRIPT_TAG_KHOJKI = KBTS_FOURCC('k', 'h', 'o', 'j'), + KBTS_SCRIPT_TAG_KHUDAWADI = KBTS_FOURCC('s', 'i', 'n', 'd'), + KBTS_SCRIPT_TAG_KIRAT_RAI = KBTS_FOURCC('k', 'r', 'a', 'i'), + KBTS_SCRIPT_TAG_LAO = KBTS_FOURCC('l', 'a', 'o', ' '), + KBTS_SCRIPT_TAG_LATIN = KBTS_FOURCC('l', 'a', 't', 'n'), + KBTS_SCRIPT_TAG_LEPCHA = KBTS_FOURCC('l', 'e', 'p', 'c'), + KBTS_SCRIPT_TAG_LIMBU = KBTS_FOURCC('l', 'i', 'm', 'b'), + KBTS_SCRIPT_TAG_LINEAR_A = KBTS_FOURCC('l', 'i', 'n', 'a'), + KBTS_SCRIPT_TAG_LINEAR_B = KBTS_FOURCC('l', 'i', 'n', 'b'), + KBTS_SCRIPT_TAG_LISU = KBTS_FOURCC('l', 'i', 's', 'u'), + KBTS_SCRIPT_TAG_LYCIAN = KBTS_FOURCC('l', 'y', 'c', 'i'), + KBTS_SCRIPT_TAG_LYDIAN = KBTS_FOURCC('l', 'y', 'd', 'i'), + KBTS_SCRIPT_TAG_MAHAJANI = KBTS_FOURCC('m', 'a', 'h', 'j'), + KBTS_SCRIPT_TAG_MAKASAR = KBTS_FOURCC('m', 'a', 'k', 'a'), + KBTS_SCRIPT_TAG_MALAYALAM = KBTS_FOURCC('m', 'l', 'm', '2'), + KBTS_SCRIPT_TAG_MANDAIC = KBTS_FOURCC('m', 'a', 'n', 'd'), + KBTS_SCRIPT_TAG_MANICHAEAN = KBTS_FOURCC('m', 'a', 'n', 'i'), + KBTS_SCRIPT_TAG_MARCHEN = KBTS_FOURCC('m', 'a', 'r', 'c'), + KBTS_SCRIPT_TAG_MASARAM_GONDI = KBTS_FOURCC('g', 'o', 'n', 'm'), + KBTS_SCRIPT_TAG_MEDEFAIDRIN = KBTS_FOURCC('m', 'e', 'd', 'f'), + KBTS_SCRIPT_TAG_MEETEI_MAYEK = KBTS_FOURCC('m', 't', 'e', 'i'), + KBTS_SCRIPT_TAG_MENDE_KIKAKUI = KBTS_FOURCC('m', 'e', 'n', 'd'), + KBTS_SCRIPT_TAG_MEROITIC_CURSIVE = KBTS_FOURCC('m', 'e', 'r', 'c'), + KBTS_SCRIPT_TAG_MEROITIC_HIEROGLYPHS = KBTS_FOURCC('m', 'e', 'r', 'o'), + KBTS_SCRIPT_TAG_MIAO = KBTS_FOURCC('p', 'l', 'r', 'd'), + KBTS_SCRIPT_TAG_MODI = KBTS_FOURCC('m', 'o', 'd', 'i'), + KBTS_SCRIPT_TAG_MONGOLIAN = KBTS_FOURCC('m', 'o', 'n', 'g'), + KBTS_SCRIPT_TAG_MRO = KBTS_FOURCC('m', 'r', 'o', 'o'), + KBTS_SCRIPT_TAG_MULTANI = KBTS_FOURCC('m', 'u', 'l', 't'), + KBTS_SCRIPT_TAG_MYANMAR = KBTS_FOURCC('m', 'y', 'm', '2'), + KBTS_SCRIPT_TAG_NABATAEAN = KBTS_FOURCC('n', 'b', 'a', 't'), + KBTS_SCRIPT_TAG_NAG_MUNDARI = KBTS_FOURCC('n', 'a', 'g', 'm'), + KBTS_SCRIPT_TAG_NANDINAGARI = KBTS_FOURCC('n', 'a', 'n', 'd'), + KBTS_SCRIPT_TAG_NEWA = KBTS_FOURCC('n', 'e', 'w', 'a'), + KBTS_SCRIPT_TAG_NEW_TAI_LUE = KBTS_FOURCC('t', 'a', 'l', 'u'), + KBTS_SCRIPT_TAG_NKO = KBTS_FOURCC('n', 'k', 'o', ' '), + KBTS_SCRIPT_TAG_NUSHU = KBTS_FOURCC('n', 's', 'h', 'u'), + KBTS_SCRIPT_TAG_NYIAKENG_PUACHUE_HMONG = KBTS_FOURCC('h', 'm', 'n', 'p'), + KBTS_SCRIPT_TAG_OGHAM = KBTS_FOURCC('o', 'g', 'a', 'm'), + KBTS_SCRIPT_TAG_OL_CHIKI = KBTS_FOURCC('o', 'l', 'c', 'k'), + KBTS_SCRIPT_TAG_OL_ONAL = KBTS_FOURCC('o', 'n', 'a', 'o'), + KBTS_SCRIPT_TAG_OLD_ITALIC = KBTS_FOURCC('i', 't', 'a', 'l'), + KBTS_SCRIPT_TAG_OLD_HUNGARIAN = KBTS_FOURCC('h', 'u', 'n', 'g'), + KBTS_SCRIPT_TAG_OLD_NORTH_ARABIAN = KBTS_FOURCC('n', 'a', 'r', 'b'), + KBTS_SCRIPT_TAG_OLD_PERMIC = KBTS_FOURCC('p', 'e', 'r', 'm'), + KBTS_SCRIPT_TAG_OLD_PERSIAN_CUNEIFORM = KBTS_FOURCC('x', 'p', 'e', 'o'), + KBTS_SCRIPT_TAG_OLD_SOGDIAN = KBTS_FOURCC('s', 'o', 'g', 'o'), + KBTS_SCRIPT_TAG_OLD_SOUTH_ARABIAN = KBTS_FOURCC('s', 'a', 'r', 'b'), + KBTS_SCRIPT_TAG_OLD_TURKIC = KBTS_FOURCC('o', 'r', 'k', 'h'), + KBTS_SCRIPT_TAG_OLD_UYGHUR = KBTS_FOURCC('o', 'u', 'g', 'r'), + KBTS_SCRIPT_TAG_ODIA = KBTS_FOURCC('o', 'r', 'y', '2'), + KBTS_SCRIPT_TAG_OSAGE = KBTS_FOURCC('o', 's', 'g', 'e'), + KBTS_SCRIPT_TAG_OSMANYA = KBTS_FOURCC('o', 's', 'm', 'a'), + KBTS_SCRIPT_TAG_PAHAWH_HMONG = KBTS_FOURCC('h', 'm', 'n', 'g'), + KBTS_SCRIPT_TAG_PALMYRENE = KBTS_FOURCC('p', 'a', 'l', 'm'), + KBTS_SCRIPT_TAG_PAU_CIN_HAU = KBTS_FOURCC('p', 'a', 'u', 'c'), + KBTS_SCRIPT_TAG_PHAGS_PA = KBTS_FOURCC('p', 'h', 'a', 'g'), + KBTS_SCRIPT_TAG_PHOENICIAN = KBTS_FOURCC('p', 'h', 'n', 'x'), + KBTS_SCRIPT_TAG_PSALTER_PAHLAVI = KBTS_FOURCC('p', 'h', 'l', 'p'), + KBTS_SCRIPT_TAG_REJANG = KBTS_FOURCC('r', 'j', 'n', 'g'), + KBTS_SCRIPT_TAG_RUNIC = KBTS_FOURCC('r', 'u', 'n', 'r'), + KBTS_SCRIPT_TAG_SAMARITAN = KBTS_FOURCC('s', 'a', 'm', 'r'), + KBTS_SCRIPT_TAG_SAURASHTRA = KBTS_FOURCC('s', 'a', 'u', 'r'), + KBTS_SCRIPT_TAG_SHARADA = KBTS_FOURCC('s', 'h', 'r', 'd'), + KBTS_SCRIPT_TAG_SHAVIAN = KBTS_FOURCC('s', 'h', 'a', 'w'), + KBTS_SCRIPT_TAG_SIDDHAM = KBTS_FOURCC('s', 'i', 'd', 'd'), + KBTS_SCRIPT_TAG_SIGN_WRITING = KBTS_FOURCC('s', 'g', 'n', 'w'), + KBTS_SCRIPT_TAG_SOGDIAN = KBTS_FOURCC('s', 'o', 'g', 'd'), + KBTS_SCRIPT_TAG_SINHALA = KBTS_FOURCC('s', 'i', 'n', 'h'), + KBTS_SCRIPT_TAG_SORA_SOMPENG = KBTS_FOURCC('s', 'o', 'r', 'a'), + KBTS_SCRIPT_TAG_SOYOMBO = KBTS_FOURCC('s', 'o', 'y', 'o'), + KBTS_SCRIPT_TAG_SUMERO_AKKADIAN_CUNEIFORM = KBTS_FOURCC('x', 's', 'u', 'x'), + KBTS_SCRIPT_TAG_SUNDANESE = KBTS_FOURCC('s', 'u', 'n', 'd'), + KBTS_SCRIPT_TAG_SUNUWAR = KBTS_FOURCC('s', 'u', 'n', 'u'), + KBTS_SCRIPT_TAG_SYLOTI_NAGRI = KBTS_FOURCC('s', 'y', 'l', 'o'), + KBTS_SCRIPT_TAG_SYRIAC = KBTS_FOURCC('s', 'y', 'r', 'c'), + KBTS_SCRIPT_TAG_TAGALOG = KBTS_FOURCC('t', 'g', 'l', 'g'), + KBTS_SCRIPT_TAG_TAGBANWA = KBTS_FOURCC('t', 'a', 'g', 'b'), + KBTS_SCRIPT_TAG_TAI_LE = KBTS_FOURCC('t', 'a', 'l', 'e'), + KBTS_SCRIPT_TAG_TAI_THAM = KBTS_FOURCC('l', 'a', 'n', 'a'), + KBTS_SCRIPT_TAG_TAI_VIET = KBTS_FOURCC('t', 'a', 'v', 't'), + KBTS_SCRIPT_TAG_TAKRI = KBTS_FOURCC('t', 'a', 'k', 'r'), + KBTS_SCRIPT_TAG_TAMIL = KBTS_FOURCC('t', 'm', 'l', '2'), + KBTS_SCRIPT_TAG_TANGSA = KBTS_FOURCC('t', 'n', 's', 'a'), + KBTS_SCRIPT_TAG_TANGUT = KBTS_FOURCC('t', 'a', 'n', 'g'), + KBTS_SCRIPT_TAG_TELUGU = KBTS_FOURCC('t', 'e', 'l', '2'), + KBTS_SCRIPT_TAG_THAANA = KBTS_FOURCC('t', 'h', 'a', 'a'), + KBTS_SCRIPT_TAG_THAI = KBTS_FOURCC('t', 'h', 'a', 'i'), + KBTS_SCRIPT_TAG_TIBETAN = KBTS_FOURCC('t', 'i', 'b', 't'), + KBTS_SCRIPT_TAG_TIFINAGH = KBTS_FOURCC('t', 'f', 'n', 'g'), + KBTS_SCRIPT_TAG_TIRHUTA = KBTS_FOURCC('t', 'i', 'r', 'h'), + KBTS_SCRIPT_TAG_TODHRI = KBTS_FOURCC('t', 'o', 'd', 'r'), + KBTS_SCRIPT_TAG_TOTO = KBTS_FOURCC('t', 'o', 't', 'o'), + KBTS_SCRIPT_TAG_TULU_TIGALARI = KBTS_FOURCC('t', 'u', 't', 'g'), + KBTS_SCRIPT_TAG_UGARITIC_CUNEIFORM = KBTS_FOURCC('u', 'g', 'a', 'r'), + KBTS_SCRIPT_TAG_VAI = KBTS_FOURCC('v', 'a', 'i', ' '), + KBTS_SCRIPT_TAG_VITHKUQI = KBTS_FOURCC('v', 'i', 't', 'h'), + KBTS_SCRIPT_TAG_WANCHO = KBTS_FOURCC('w', 'c', 'h', 'o'), + KBTS_SCRIPT_TAG_WARANG_CITI = KBTS_FOURCC('w', 'a', 'r', 'a'), + KBTS_SCRIPT_TAG_YEZIDI = KBTS_FOURCC('y', 'e', 'z', 'i'), + KBTS_SCRIPT_TAG_YI = KBTS_FOURCC('y', 'i', ' ', ' '), + KBTS_SCRIPT_TAG_ZANABAZAR_SQUARE = KBTS_FOURCC('z', 'a', 'n', 'b'), +}; + typedef kbts_u32 kbts_script; -enum kbts_script_enum { +enum kbts_script_enum +{ KBTS_SCRIPT_DONT_KNOW, KBTS_SCRIPT_ADLAM, KBTS_SCRIPT_AHOM, @@ -1595,154 +1918,505 @@ enum kbts_script_enum { KBTS_SCRIPT_COUNT, }; - -// The order of the first few features here matters. -// They must map 1:1 with glyph flags that are part of GLYPH_FEATURE_MASK. -# define KBTS_X_FEATURES \ -KBTS_X(isol, 'i', 's', 'o', 'l') /* Isolated Forms */ \ -KBTS_X(fina, 'f', 'i', 'n', 'a') /* Terminal Forms */ \ -KBTS_X(fin2, 'f', 'i', 'n', '2') /* Terminal Forms #2 */ \ -KBTS_X(fin3, 'f', 'i', 'n', '3') /* Terminal Forms #3 */ \ -KBTS_X(medi, 'm', 'e', 'd', 'i') /* Medial Forms */ \ -KBTS_X(med2, 'm', 'e', 'd', '2') /* Medial Forms #2 */ \ -KBTS_X(init, 'i', 'n', 'i', 't') /* Initial Forms */ \ -KBTS_X(ljmo, 'l', 'j', 'm', 'o') /* Leading Jamo Forms */ \ -KBTS_X(vjmo, 'v', 'j', 'm', 'o') /* Vowel Jamo Forms */ \ -KBTS_X(tjmo, 't', 'j', 'm', 'o') /* Trailing Jamo Forms */ \ -KBTS_X(rphf, 'r', 'p', 'h', 'f') /* Reph Form */ \ -KBTS_X(blwf, 'b', 'l', 'w', 'f') /* Below-base Forms */ \ -KBTS_X(half, 'h', 'a', 'l', 'f') /* Half Forms */ \ -KBTS_X(pstf, 'p', 's', 't', 'f') /* Post-base Forms */ \ -KBTS_X(abvf, 'a', 'b', 'v', 'f') /* Above-base Forms */ \ -KBTS_X(pref, 'p', 'r', 'e', 'f') /* Pre-base Forms */ \ -KBTS_X(numr, 'n', 'u', 'm', 'r') /* Numerators */ \ -KBTS_X(frac, 'f', 'r', 'a', 'c') /* Fractions */ \ -KBTS_X(dnom, 'd', 'n', 'o', 'm') /* Denominators */ \ -KBTS_X(cfar, 'c', 'f', 'a', 'r') /* Conjunct Form After Ro */ \ -KBTS_X(aalt, 'a', 'a', 'l', 't') /* Access All Alternates */ \ -KBTS_X(abvm, 'a', 'b', 'v', 'm') /* Above-base Mark Positioning */ \ -KBTS_X(abvs, 'a', 'b', 'v', 's') /* Above-base Substitutions */ \ -KBTS_X(afrc, 'a', 'f', 'r', 'c') /* Alternative Fractions */ \ -KBTS_X(akhn, 'a', 'k', 'h', 'n') /* Akhand */ \ -KBTS_X(apkn, 'a', 'p', 'k', 'n') /* Kerning for Alternate Proportional Widths */ \ -KBTS_X(blwm, 'b', 'l', 'w', 'm') /* Below-base Mark Positioning */ \ -KBTS_X(blws, 'b', 'l', 'w', 's') /* Below-base Substitutions */ \ -KBTS_X(calt, 'c', 'a', 'l', 't') /* Contextual Alternates */ \ -KBTS_X(case, 'c', 'a', 's', 'e') /* Case-sensitive Forms */ \ -KBTS_X(ccmp, 'c', 'c', 'm', 'p') /* Glyph Composition / Decomposition */ \ -KBTS_X(chws, 'c', 'h', 'w', 's') /* Contextual Half-width Spacing */ \ -KBTS_X(cjct, 'c', 'j', 'c', 't') /* Conjunct Forms */ \ -KBTS_X(clig, 'c', 'l', 'i', 'g') /* Contextual Ligatures */ \ -KBTS_X(cpct, 'c', 'p', 'c', 't') /* Centered CJK Punctuation */ \ -KBTS_X(cpsp, 'c', 'p', 's', 'p') /* Capital Spacing */ \ -KBTS_X(cswh, 'c', 's', 'w', 'h') /* Contextual Swash */ \ -KBTS_X(curs, 'c', 'u', 'r', 's') /* Cursive Positioning */ \ -KBTS_X(cv01, 'c', 'v', '0', '1') /* 'cv99' Character Variant 1 – Character Variant 99 */ \ -KBTS_X(c2pc, 'c', '2', 'p', 'c') /* Petite Capitals From Capitals */ \ -KBTS_X(c2sc, 'c', '2', 's', 'c') /* Small Capitals From Capitals */ \ -KBTS_X(dist, 'd', 'i', 's', 't') /* Distances */ \ -KBTS_X(dlig, 'd', 'l', 'i', 'g') /* Discretionary Ligatures */ \ -KBTS_X(dtls, 'd', 't', 'l', 's') /* Dotless Forms */ \ -KBTS_X(expt, 'e', 'x', 'p', 't') /* Expert Forms */ \ -KBTS_X(falt, 'f', 'a', 'l', 't') /* Final Glyph on Line Alternates */ \ -KBTS_X(flac, 'f', 'l', 'a', 'c') /* Flattened Accent Forms */ \ -KBTS_X(fwid, 'f', 'w', 'i', 'd') /* Full Widths */ \ -KBTS_X(haln, 'h', 'a', 'l', 'n') /* Halant Forms */ \ -KBTS_X(halt, 'h', 'a', 'l', 't') /* Alternate Half Widths */ \ -KBTS_X(hist, 'h', 'i', 's', 't') /* Historical Forms */ \ -KBTS_X(hkna, 'h', 'k', 'n', 'a') /* Horizontal Kana Alternates */ \ -KBTS_X(hlig, 'h', 'l', 'i', 'g') /* Historical Ligatures */ \ -KBTS_X(hngl, 'h', 'n', 'g', 'l') /* Hangul */ \ -KBTS_X(hojo, 'h', 'o', 'j', 'o') /* Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) */ \ -KBTS_X(hwid, 'h', 'w', 'i', 'd') /* Half Widths */ \ -KBTS_X(ital, 'i', 't', 'a', 'l') /* Italics */ \ -KBTS_X(jalt, 'j', 'a', 'l', 't') /* Justification Alternates */ \ -KBTS_X(jp78, 'j', 'p', '7', '8') /* JIS78 Forms */ \ -KBTS_X(jp83, 'j', 'p', '8', '3') /* JIS83 Forms */ \ -KBTS_X(jp90, 'j', 'p', '9', '0') /* JIS90 Forms */ \ -KBTS_X(jp04, 'j', 'p', '0', '4') /* JIS2004 Forms */ \ -KBTS_X(kern, 'k', 'e', 'r', 'n') /* Kerning */ \ -KBTS_X(lfbd, 'l', 'f', 'b', 'd') /* Left Bounds */ \ -KBTS_X(liga, 'l', 'i', 'g', 'a') /* Standard Ligatures */ \ -KBTS_X(lnum, 'l', 'n', 'u', 'm') /* Lining Figures */ \ -KBTS_X(locl, 'l', 'o', 'c', 'l') /* Localized Forms */ \ -KBTS_X(ltra, 'l', 't', 'r', 'a') /* Left-to-right Alternates */ \ -KBTS_X(ltrm, 'l', 't', 'r', 'm') /* Left-to-right Mirrored Forms */ \ -KBTS_X(mark, 'm', 'a', 'r', 'k') /* Mark Positioning */ \ -KBTS_X(mgrk, 'm', 'g', 'r', 'k') /* Mathematical Greek */ \ -KBTS_X(mkmk, 'm', 'k', 'm', 'k') /* Mark to Mark Positioning */ \ -KBTS_X(mset, 'm', 's', 'e', 't') /* Mark Positioning via Substitution */ \ -KBTS_X(nalt, 'n', 'a', 'l', 't') /* Alternate Annotation Forms */ \ -KBTS_X(nlck, 'n', 'l', 'c', 'k') /* NLC Kanji Forms */ \ -KBTS_X(nukt, 'n', 'u', 'k', 't') /* Nukta Forms */ \ -KBTS_X(onum, 'o', 'n', 'u', 'm') /* Oldstyle Figures */ \ -KBTS_X(opbd, 'o', 'p', 'b', 'd') /* Optical Bounds */ \ -KBTS_X(ordn, 'o', 'r', 'd', 'n') /* Ordinals */ \ -KBTS_X(ornm, 'o', 'r', 'n', 'm') /* Ornaments */ \ -KBTS_X(palt, 'p', 'a', 'l', 't') /* Proportional Alternate Widths */ \ -KBTS_X(pcap, 'p', 'c', 'a', 'p') /* Petite Capitals */ \ -KBTS_X(pkna, 'p', 'k', 'n', 'a') /* Proportional Kana */ \ -KBTS_X(pnum, 'p', 'n', 'u', 'm') /* Proportional Figures */ \ -KBTS_X(pres, 'p', 'r', 'e', 's') /* Pre-base Substitutions */ \ -KBTS_X(psts, 'p', 's', 't', 's') /* Post-base Substitutions */ \ -KBTS_X(pwid, 'p', 'w', 'i', 'd') /* Proportional Widths */ \ -KBTS_X(qwid, 'q', 'w', 'i', 'd') /* Quarter Widths */ \ -KBTS_X(rand, 'r', 'a', 'n', 'd') /* Randomize */ \ -KBTS_X(rclt, 'r', 'c', 'l', 't') /* Required Contextual Alternates */ \ -KBTS_X(rkrf, 'r', 'k', 'r', 'f') /* Rakar Forms */ \ -KBTS_X(rlig, 'r', 'l', 'i', 'g') /* Required Ligatures */ \ -KBTS_X(rtbd, 'r', 't', 'b', 'd') /* Right Bounds */ \ -KBTS_X(rtla, 'r', 't', 'l', 'a') /* Right-to-left Alternates */ \ -KBTS_X(rtlm, 'r', 't', 'l', 'm') /* Right-to-left Mirrored Forms */ \ -KBTS_X(ruby, 'r', 'u', 'b', 'y') /* Ruby Notation Forms */ \ -KBTS_X(rvrn, 'r', 'v', 'r', 'n') /* Required Variation Alternates */ \ -KBTS_X(salt, 's', 'a', 'l', 't') /* Stylistic Alternates */ \ -KBTS_X(sinf, 's', 'i', 'n', 'f') /* Scientific Inferiors */ \ -KBTS_X(size, 's', 'i', 'z', 'e') /* Optical size */ \ -KBTS_X(smcp, 's', 'm', 'c', 'p') /* Small Capitals */ \ -KBTS_X(smpl, 's', 'm', 'p', 'l') /* Simplified Forms */ \ -KBTS_X(ss01, 's', 's', '0', '1') /* 'ss20' Stylistic Set 1 – Stylistic Set 20 */ \ -KBTS_X(ssty, 's', 's', 't', 'y') /* Math Script-style Alternates */ \ -KBTS_X(stch, 's', 't', 'c', 'h') /* Stretching Glyph Decomposition */ \ -KBTS_X(subs, 's', 'u', 'b', 's') /* Subscript */ \ -KBTS_X(sups, 's', 'u', 'p', 's') /* Superscript */ \ -KBTS_X(swsh, 's', 'w', 's', 'h') /* Swash */ \ -KBTS_X(test, 't', 'e', 's', 't') /* Test features, only for development */ \ -KBTS_X(titl, 't', 'i', 't', 'l') /* Titling */ \ -KBTS_X(tnam, 't', 'n', 'a', 'm') /* Traditional Name Forms */ \ -KBTS_X(tnum, 't', 'n', 'u', 'm') /* Tabular Figures */ \ -KBTS_X(trad, 't', 'r', 'a', 'd') /* Traditional Forms */ \ -KBTS_X(twid, 't', 'w', 'i', 'd') /* Third Widths */ \ -KBTS_X(unic, 'u', 'n', 'i', 'c') /* Unicase */ \ -KBTS_X(valt, 'v', 'a', 'l', 't') /* Alternate Vertical Metrics */ \ -KBTS_X(vapk, 'v', 'a', 'p', 'k') /* Kerning for Alternate Proportional Vertical Metrics */ \ -KBTS_X(vatu, 'v', 'a', 't', 'u') /* Vattu Variants */ \ -KBTS_X(vchw, 'v', 'c', 'h', 'w') /* Vertical Contextual Half-width Spacing */ \ -KBTS_X(vert, 'v', 'e', 'r', 't') /* Vertical Alternates */ \ -KBTS_X(vhal, 'v', 'h', 'a', 'l') /* Alternate Vertical Half Metrics */ \ -KBTS_X(vkna, 'v', 'k', 'n', 'a') /* Vertical Kana Alternates */ \ -KBTS_X(vkrn, 'v', 'k', 'r', 'n') /* Vertical Kerning */ \ -KBTS_X(vpal, 'v', 'p', 'a', 'l') /* Proportional Alternate Vertical Metrics */ \ -KBTS_X(vrt2, 'v', 'r', 't', '2') /* Vertical Alternates and Rotation */ \ -KBTS_X(vrtr, 'v', 'r', 't', 'r') /* Vertical Alternates for Rotation */ \ -KBTS_X(zero, 'z', 'e', 'r', 'o') /* Slashed Zero */ - typedef kbts_u32 kbts_feature_tag; enum kbts_feature_tag_enum { -# define KBTS_X(Name, C0, C1, C2, C3) KBTS_FEATURE_TAG_##Name = KBTS_FOURCC(C0, C1, C2, C3), - KBTS_X_FEATURES -# undef KBTS_X + KBTS_FEATURE_TAG_UNREGISTERED = KBTS_FOURCC(0, 0, 0, 0), // Features that aren't pre-defined in the OpenType spec + KBTS_FEATURE_TAG_isol = KBTS_FOURCC('i', 's', 'o', 'l'), // Isolated Forms + KBTS_FEATURE_TAG_fina = KBTS_FOURCC('f', 'i', 'n', 'a'), // Terminal Forms + KBTS_FEATURE_TAG_fin2 = KBTS_FOURCC('f', 'i', 'n', '2'), // Terminal Forms #2 + KBTS_FEATURE_TAG_fin3 = KBTS_FOURCC('f', 'i', 'n', '3'), // Terminal Forms #3 + KBTS_FEATURE_TAG_medi = KBTS_FOURCC('m', 'e', 'd', 'i'), // Medial Forms + KBTS_FEATURE_TAG_med2 = KBTS_FOURCC('m', 'e', 'd', '2'), // Medial Forms #2 + KBTS_FEATURE_TAG_init = KBTS_FOURCC('i', 'n', 'i', 't'), // Initial Forms + KBTS_FEATURE_TAG_ljmo = KBTS_FOURCC('l', 'j', 'm', 'o'), // Leading Jamo Forms + KBTS_FEATURE_TAG_vjmo = KBTS_FOURCC('v', 'j', 'm', 'o'), // Vowel Jamo Forms + KBTS_FEATURE_TAG_tjmo = KBTS_FOURCC('t', 'j', 'm', 'o'), // Trailing Jamo Forms + KBTS_FEATURE_TAG_rphf = KBTS_FOURCC('r', 'p', 'h', 'f'), // Reph Form + KBTS_FEATURE_TAG_blwf = KBTS_FOURCC('b', 'l', 'w', 'f'), // Below-base Forms + KBTS_FEATURE_TAG_half = KBTS_FOURCC('h', 'a', 'l', 'f'), // Half Forms + KBTS_FEATURE_TAG_pstf = KBTS_FOURCC('p', 's', 't', 'f'), // Post-base Forms + KBTS_FEATURE_TAG_abvf = KBTS_FOURCC('a', 'b', 'v', 'f'), // Above-base Forms + KBTS_FEATURE_TAG_pref = KBTS_FOURCC('p', 'r', 'e', 'f'), // Pre-base Forms + KBTS_FEATURE_TAG_numr = KBTS_FOURCC('n', 'u', 'm', 'r'), // Numerators + KBTS_FEATURE_TAG_frac = KBTS_FOURCC('f', 'r', 'a', 'c'), // Fractions + KBTS_FEATURE_TAG_dnom = KBTS_FOURCC('d', 'n', 'o', 'm'), // Denominators + KBTS_FEATURE_TAG_cfar = KBTS_FOURCC('c', 'f', 'a', 'r'), // Conjunct Form After Ro + KBTS_FEATURE_TAG_aalt = KBTS_FOURCC('a', 'a', 'l', 't'), // Access All Alternates + KBTS_FEATURE_TAG_abvm = KBTS_FOURCC('a', 'b', 'v', 'm'), // Above-base Mark Positioning + KBTS_FEATURE_TAG_abvs = KBTS_FOURCC('a', 'b', 'v', 's'), // Above-base Substitutions + KBTS_FEATURE_TAG_afrc = KBTS_FOURCC('a', 'f', 'r', 'c'), // Alternative Fractions + KBTS_FEATURE_TAG_akhn = KBTS_FOURCC('a', 'k', 'h', 'n'), // Akhand + KBTS_FEATURE_TAG_apkn = KBTS_FOURCC('a', 'p', 'k', 'n'), // Kerning for Alternate Proportional Widths + KBTS_FEATURE_TAG_blwm = KBTS_FOURCC('b', 'l', 'w', 'm'), // Below-base Mark Positioning + KBTS_FEATURE_TAG_blws = KBTS_FOURCC('b', 'l', 'w', 's'), // Below-base Substitutions + KBTS_FEATURE_TAG_calt = KBTS_FOURCC('c', 'a', 'l', 't'), // Contextual Alternates + KBTS_FEATURE_TAG_case = KBTS_FOURCC('c', 'a', 's', 'e'), // Case-sensitive Forms + KBTS_FEATURE_TAG_ccmp = KBTS_FOURCC('c', 'c', 'm', 'p'), // Glyph Composition / Decomposition + KBTS_FEATURE_TAG_chws = KBTS_FOURCC('c', 'h', 'w', 's'), // Contextual Half-width Spacing + KBTS_FEATURE_TAG_cjct = KBTS_FOURCC('c', 'j', 'c', 't'), // Conjunct Forms + KBTS_FEATURE_TAG_clig = KBTS_FOURCC('c', 'l', 'i', 'g'), // Contextual Ligatures + KBTS_FEATURE_TAG_cpct = KBTS_FOURCC('c', 'p', 'c', 't'), // Centered CJK Punctuation + KBTS_FEATURE_TAG_cpsp = KBTS_FOURCC('c', 'p', 's', 'p'), // Capital Spacing + KBTS_FEATURE_TAG_cswh = KBTS_FOURCC('c', 's', 'w', 'h'), // Contextual Swash + KBTS_FEATURE_TAG_curs = KBTS_FOURCC('c', 'u', 'r', 's'), // Cursive Positioning + KBTS_FEATURE_TAG_cv01 = KBTS_FOURCC('c', 'v', '0', '1'), // Character Variant 1 + KBTS_FEATURE_TAG_cv02 = KBTS_FOURCC('c', 'v', '0', '2'), // Character Variant 2 + KBTS_FEATURE_TAG_cv03 = KBTS_FOURCC('c', 'v', '0', '3'), // Character Variant 3 + KBTS_FEATURE_TAG_cv04 = KBTS_FOURCC('c', 'v', '0', '4'), // Character Variant 4 + KBTS_FEATURE_TAG_cv05 = KBTS_FOURCC('c', 'v', '0', '5'), // Character Variant 5 + KBTS_FEATURE_TAG_cv06 = KBTS_FOURCC('c', 'v', '0', '6'), // Character Variant 6 + KBTS_FEATURE_TAG_cv07 = KBTS_FOURCC('c', 'v', '0', '7'), // Character Variant 7 + KBTS_FEATURE_TAG_cv08 = KBTS_FOURCC('c', 'v', '0', '8'), // Character Variant 8 + KBTS_FEATURE_TAG_cv09 = KBTS_FOURCC('c', 'v', '0', '9'), // Character Variant 9 + KBTS_FEATURE_TAG_cv10 = KBTS_FOURCC('c', 'v', '1', '0'), // Character Variant 10 + KBTS_FEATURE_TAG_cv11 = KBTS_FOURCC('c', 'v', '1', '1'), // Character Variant 11 + KBTS_FEATURE_TAG_cv12 = KBTS_FOURCC('c', 'v', '1', '2'), // Character Variant 12 + KBTS_FEATURE_TAG_cv13 = KBTS_FOURCC('c', 'v', '1', '3'), // Character Variant 13 + KBTS_FEATURE_TAG_cv14 = KBTS_FOURCC('c', 'v', '1', '4'), // Character Variant 14 + KBTS_FEATURE_TAG_cv15 = KBTS_FOURCC('c', 'v', '1', '5'), // Character Variant 15 + KBTS_FEATURE_TAG_cv16 = KBTS_FOURCC('c', 'v', '1', '6'), // Character Variant 16 + KBTS_FEATURE_TAG_cv17 = KBTS_FOURCC('c', 'v', '1', '7'), // Character Variant 17 + KBTS_FEATURE_TAG_cv18 = KBTS_FOURCC('c', 'v', '1', '8'), // Character Variant 18 + KBTS_FEATURE_TAG_cv19 = KBTS_FOURCC('c', 'v', '1', '9'), // Character Variant 19 + KBTS_FEATURE_TAG_cv20 = KBTS_FOURCC('c', 'v', '2', '0'), // Character Variant 20 + KBTS_FEATURE_TAG_cv21 = KBTS_FOURCC('c', 'v', '2', '1'), // Character Variant 21 + KBTS_FEATURE_TAG_cv22 = KBTS_FOURCC('c', 'v', '2', '2'), // Character Variant 22 + KBTS_FEATURE_TAG_cv23 = KBTS_FOURCC('c', 'v', '2', '3'), // Character Variant 23 + KBTS_FEATURE_TAG_cv24 = KBTS_FOURCC('c', 'v', '2', '4'), // Character Variant 24 + KBTS_FEATURE_TAG_cv25 = KBTS_FOURCC('c', 'v', '2', '5'), // Character Variant 25 + KBTS_FEATURE_TAG_cv26 = KBTS_FOURCC('c', 'v', '2', '6'), // Character Variant 26 + KBTS_FEATURE_TAG_cv27 = KBTS_FOURCC('c', 'v', '2', '7'), // Character Variant 27 + KBTS_FEATURE_TAG_cv28 = KBTS_FOURCC('c', 'v', '2', '8'), // Character Variant 28 + KBTS_FEATURE_TAG_cv29 = KBTS_FOURCC('c', 'v', '2', '9'), // Character Variant 29 + KBTS_FEATURE_TAG_cv30 = KBTS_FOURCC('c', 'v', '3', '0'), // Character Variant 30 + KBTS_FEATURE_TAG_cv31 = KBTS_FOURCC('c', 'v', '3', '1'), // Character Variant 31 + KBTS_FEATURE_TAG_cv32 = KBTS_FOURCC('c', 'v', '3', '2'), // Character Variant 32 + KBTS_FEATURE_TAG_cv33 = KBTS_FOURCC('c', 'v', '3', '3'), // Character Variant 33 + KBTS_FEATURE_TAG_cv34 = KBTS_FOURCC('c', 'v', '3', '4'), // Character Variant 34 + KBTS_FEATURE_TAG_cv35 = KBTS_FOURCC('c', 'v', '3', '5'), // Character Variant 35 + KBTS_FEATURE_TAG_cv36 = KBTS_FOURCC('c', 'v', '3', '6'), // Character Variant 36 + KBTS_FEATURE_TAG_cv37 = KBTS_FOURCC('c', 'v', '3', '7'), // Character Variant 37 + KBTS_FEATURE_TAG_cv38 = KBTS_FOURCC('c', 'v', '3', '8'), // Character Variant 38 + KBTS_FEATURE_TAG_cv39 = KBTS_FOURCC('c', 'v', '3', '9'), // Character Variant 39 + KBTS_FEATURE_TAG_cv40 = KBTS_FOURCC('c', 'v', '4', '0'), // Character Variant 40 + KBTS_FEATURE_TAG_cv41 = KBTS_FOURCC('c', 'v', '4', '1'), // Character Variant 41 + KBTS_FEATURE_TAG_cv42 = KBTS_FOURCC('c', 'v', '4', '2'), // Character Variant 42 + KBTS_FEATURE_TAG_cv43 = KBTS_FOURCC('c', 'v', '4', '3'), // Character Variant 43 + KBTS_FEATURE_TAG_cv44 = KBTS_FOURCC('c', 'v', '4', '4'), // Character Variant 44 + KBTS_FEATURE_TAG_cv45 = KBTS_FOURCC('c', 'v', '4', '5'), // Character Variant 45 + KBTS_FEATURE_TAG_cv46 = KBTS_FOURCC('c', 'v', '4', '6'), // Character Variant 46 + KBTS_FEATURE_TAG_cv47 = KBTS_FOURCC('c', 'v', '4', '7'), // Character Variant 47 + KBTS_FEATURE_TAG_cv48 = KBTS_FOURCC('c', 'v', '4', '8'), // Character Variant 48 + KBTS_FEATURE_TAG_cv49 = KBTS_FOURCC('c', 'v', '4', '9'), // Character Variant 49 + KBTS_FEATURE_TAG_cv50 = KBTS_FOURCC('c', 'v', '5', '0'), // Character Variant 50 + KBTS_FEATURE_TAG_cv51 = KBTS_FOURCC('c', 'v', '5', '1'), // Character Variant 51 + KBTS_FEATURE_TAG_cv52 = KBTS_FOURCC('c', 'v', '5', '2'), // Character Variant 52 + KBTS_FEATURE_TAG_cv53 = KBTS_FOURCC('c', 'v', '5', '3'), // Character Variant 53 + KBTS_FEATURE_TAG_cv54 = KBTS_FOURCC('c', 'v', '5', '4'), // Character Variant 54 + KBTS_FEATURE_TAG_cv55 = KBTS_FOURCC('c', 'v', '5', '5'), // Character Variant 55 + KBTS_FEATURE_TAG_cv56 = KBTS_FOURCC('c', 'v', '5', '6'), // Character Variant 56 + KBTS_FEATURE_TAG_cv57 = KBTS_FOURCC('c', 'v', '5', '7'), // Character Variant 57 + KBTS_FEATURE_TAG_cv58 = KBTS_FOURCC('c', 'v', '5', '8'), // Character Variant 58 + KBTS_FEATURE_TAG_cv59 = KBTS_FOURCC('c', 'v', '5', '9'), // Character Variant 59 + KBTS_FEATURE_TAG_cv60 = KBTS_FOURCC('c', 'v', '6', '0'), // Character Variant 60 + KBTS_FEATURE_TAG_cv61 = KBTS_FOURCC('c', 'v', '6', '1'), // Character Variant 61 + KBTS_FEATURE_TAG_cv62 = KBTS_FOURCC('c', 'v', '6', '2'), // Character Variant 62 + KBTS_FEATURE_TAG_cv63 = KBTS_FOURCC('c', 'v', '6', '3'), // Character Variant 63 + KBTS_FEATURE_TAG_cv64 = KBTS_FOURCC('c', 'v', '6', '4'), // Character Variant 64 + KBTS_FEATURE_TAG_cv65 = KBTS_FOURCC('c', 'v', '6', '5'), // Character Variant 65 + KBTS_FEATURE_TAG_cv66 = KBTS_FOURCC('c', 'v', '6', '6'), // Character Variant 66 + KBTS_FEATURE_TAG_cv67 = KBTS_FOURCC('c', 'v', '6', '7'), // Character Variant 67 + KBTS_FEATURE_TAG_cv68 = KBTS_FOURCC('c', 'v', '6', '8'), // Character Variant 68 + KBTS_FEATURE_TAG_cv69 = KBTS_FOURCC('c', 'v', '6', '9'), // Character Variant 69 + KBTS_FEATURE_TAG_cv70 = KBTS_FOURCC('c', 'v', '7', '0'), // Character Variant 70 + KBTS_FEATURE_TAG_cv71 = KBTS_FOURCC('c', 'v', '7', '1'), // Character Variant 71 + KBTS_FEATURE_TAG_cv72 = KBTS_FOURCC('c', 'v', '7', '2'), // Character Variant 72 + KBTS_FEATURE_TAG_cv73 = KBTS_FOURCC('c', 'v', '7', '3'), // Character Variant 73 + KBTS_FEATURE_TAG_cv74 = KBTS_FOURCC('c', 'v', '7', '4'), // Character Variant 74 + KBTS_FEATURE_TAG_cv75 = KBTS_FOURCC('c', 'v', '7', '5'), // Character Variant 75 + KBTS_FEATURE_TAG_cv76 = KBTS_FOURCC('c', 'v', '7', '6'), // Character Variant 76 + KBTS_FEATURE_TAG_cv77 = KBTS_FOURCC('c', 'v', '7', '7'), // Character Variant 77 + KBTS_FEATURE_TAG_cv78 = KBTS_FOURCC('c', 'v', '7', '8'), // Character Variant 78 + KBTS_FEATURE_TAG_cv79 = KBTS_FOURCC('c', 'v', '7', '9'), // Character Variant 79 + KBTS_FEATURE_TAG_cv80 = KBTS_FOURCC('c', 'v', '8', '0'), // Character Variant 80 + KBTS_FEATURE_TAG_cv81 = KBTS_FOURCC('c', 'v', '8', '1'), // Character Variant 81 + KBTS_FEATURE_TAG_cv82 = KBTS_FOURCC('c', 'v', '8', '2'), // Character Variant 82 + KBTS_FEATURE_TAG_cv83 = KBTS_FOURCC('c', 'v', '8', '3'), // Character Variant 83 + KBTS_FEATURE_TAG_cv84 = KBTS_FOURCC('c', 'v', '8', '4'), // Character Variant 84 + KBTS_FEATURE_TAG_cv85 = KBTS_FOURCC('c', 'v', '8', '5'), // Character Variant 85 + KBTS_FEATURE_TAG_cv86 = KBTS_FOURCC('c', 'v', '8', '6'), // Character Variant 86 + KBTS_FEATURE_TAG_cv87 = KBTS_FOURCC('c', 'v', '8', '7'), // Character Variant 87 + KBTS_FEATURE_TAG_cv88 = KBTS_FOURCC('c', 'v', '8', '8'), // Character Variant 88 + KBTS_FEATURE_TAG_cv89 = KBTS_FOURCC('c', 'v', '8', '9'), // Character Variant 89 + KBTS_FEATURE_TAG_cv90 = KBTS_FOURCC('c', 'v', '9', '0'), // Character Variant 90 + KBTS_FEATURE_TAG_cv91 = KBTS_FOURCC('c', 'v', '9', '1'), // Character Variant 91 + KBTS_FEATURE_TAG_cv92 = KBTS_FOURCC('c', 'v', '9', '2'), // Character Variant 92 + KBTS_FEATURE_TAG_cv93 = KBTS_FOURCC('c', 'v', '9', '3'), // Character Variant 93 + KBTS_FEATURE_TAG_cv94 = KBTS_FOURCC('c', 'v', '9', '4'), // Character Variant 94 + KBTS_FEATURE_TAG_cv95 = KBTS_FOURCC('c', 'v', '9', '5'), // Character Variant 95 + KBTS_FEATURE_TAG_cv96 = KBTS_FOURCC('c', 'v', '9', '6'), // Character Variant 96 + KBTS_FEATURE_TAG_cv97 = KBTS_FOURCC('c', 'v', '9', '7'), // Character Variant 97 + KBTS_FEATURE_TAG_cv98 = KBTS_FOURCC('c', 'v', '9', '8'), // Character Variant 98 + KBTS_FEATURE_TAG_cv99 = KBTS_FOURCC('c', 'v', '9', '9'), // Character Variant 99 + KBTS_FEATURE_TAG_c2pc = KBTS_FOURCC('c', '2', 'p', 'c'), // Petite Capitals From Capitals + KBTS_FEATURE_TAG_c2sc = KBTS_FOURCC('c', '2', 's', 'c'), // Small Capitals From Capitals + KBTS_FEATURE_TAG_dist = KBTS_FOURCC('d', 'i', 's', 't'), // Distances + KBTS_FEATURE_TAG_dlig = KBTS_FOURCC('d', 'l', 'i', 'g'), // Discretionary Ligatures + KBTS_FEATURE_TAG_dtls = KBTS_FOURCC('d', 't', 'l', 's'), // Dotless Forms + KBTS_FEATURE_TAG_expt = KBTS_FOURCC('e', 'x', 'p', 't'), // Expert Forms + KBTS_FEATURE_TAG_falt = KBTS_FOURCC('f', 'a', 'l', 't'), // Final Glyph on Line Alternates + KBTS_FEATURE_TAG_flac = KBTS_FOURCC('f', 'l', 'a', 'c'), // Flattened Accent Forms + KBTS_FEATURE_TAG_fwid = KBTS_FOURCC('f', 'w', 'i', 'd'), // Full Widths + KBTS_FEATURE_TAG_haln = KBTS_FOURCC('h', 'a', 'l', 'n'), // Halant Forms + KBTS_FEATURE_TAG_halt = KBTS_FOURCC('h', 'a', 'l', 't'), // Alternate Half Widths + KBTS_FEATURE_TAG_hist = KBTS_FOURCC('h', 'i', 's', 't'), // Historical Forms + KBTS_FEATURE_TAG_hkna = KBTS_FOURCC('h', 'k', 'n', 'a'), // Horizontal Kana Alternates + KBTS_FEATURE_TAG_hlig = KBTS_FOURCC('h', 'l', 'i', 'g'), // Historical Ligatures + KBTS_FEATURE_TAG_hngl = KBTS_FOURCC('h', 'n', 'g', 'l'), // Hangul + KBTS_FEATURE_TAG_hojo = KBTS_FOURCC('h', 'o', 'j', 'o'), // Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) + KBTS_FEATURE_TAG_hwid = KBTS_FOURCC('h', 'w', 'i', 'd'), // Half Widths + KBTS_FEATURE_TAG_ital = KBTS_FOURCC('i', 't', 'a', 'l'), // Italics + KBTS_FEATURE_TAG_jalt = KBTS_FOURCC('j', 'a', 'l', 't'), // Justification Alternates + KBTS_FEATURE_TAG_jp78 = KBTS_FOURCC('j', 'p', '7', '8'), // JIS78 Forms + KBTS_FEATURE_TAG_jp83 = KBTS_FOURCC('j', 'p', '8', '3'), // JIS83 Forms + KBTS_FEATURE_TAG_jp90 = KBTS_FOURCC('j', 'p', '9', '0'), // JIS90 Forms + KBTS_FEATURE_TAG_jp04 = KBTS_FOURCC('j', 'p', '0', '4'), // JIS2004 Forms + KBTS_FEATURE_TAG_kern = KBTS_FOURCC('k', 'e', 'r', 'n'), // Kerning + KBTS_FEATURE_TAG_lfbd = KBTS_FOURCC('l', 'f', 'b', 'd'), // Left Bounds + KBTS_FEATURE_TAG_liga = KBTS_FOURCC('l', 'i', 'g', 'a'), // Standard Ligatures + KBTS_FEATURE_TAG_lnum = KBTS_FOURCC('l', 'n', 'u', 'm'), // Lining Figures + KBTS_FEATURE_TAG_locl = KBTS_FOURCC('l', 'o', 'c', 'l'), // Localized Forms + KBTS_FEATURE_TAG_ltra = KBTS_FOURCC('l', 't', 'r', 'a'), // Left-to-right Alternates + KBTS_FEATURE_TAG_ltrm = KBTS_FOURCC('l', 't', 'r', 'm'), // Left-to-right Mirrored Forms + KBTS_FEATURE_TAG_mark = KBTS_FOURCC('m', 'a', 'r', 'k'), // Mark Positioning + KBTS_FEATURE_TAG_mgrk = KBTS_FOURCC('m', 'g', 'r', 'k'), // Mathematical Greek + KBTS_FEATURE_TAG_mkmk = KBTS_FOURCC('m', 'k', 'm', 'k'), // Mark to Mark Positioning + KBTS_FEATURE_TAG_mset = KBTS_FOURCC('m', 's', 'e', 't'), // Mark Positioning via Substitution + KBTS_FEATURE_TAG_nalt = KBTS_FOURCC('n', 'a', 'l', 't'), // Alternate Annotation Forms + KBTS_FEATURE_TAG_nlck = KBTS_FOURCC('n', 'l', 'c', 'k'), // NLC Kanji Forms + KBTS_FEATURE_TAG_nukt = KBTS_FOURCC('n', 'u', 'k', 't'), // Nukta Forms + KBTS_FEATURE_TAG_onum = KBTS_FOURCC('o', 'n', 'u', 'm'), // Oldstyle Figures + KBTS_FEATURE_TAG_opbd = KBTS_FOURCC('o', 'p', 'b', 'd'), // Optical Bounds + KBTS_FEATURE_TAG_ordn = KBTS_FOURCC('o', 'r', 'd', 'n'), // Ordinals + KBTS_FEATURE_TAG_ornm = KBTS_FOURCC('o', 'r', 'n', 'm'), // Ornaments + KBTS_FEATURE_TAG_palt = KBTS_FOURCC('p', 'a', 'l', 't'), // Proportional Alternate Widths + KBTS_FEATURE_TAG_pcap = KBTS_FOURCC('p', 'c', 'a', 'p'), // Petite Capitals + KBTS_FEATURE_TAG_pkna = KBTS_FOURCC('p', 'k', 'n', 'a'), // Proportional Kana + KBTS_FEATURE_TAG_pnum = KBTS_FOURCC('p', 'n', 'u', 'm'), // Proportional Figures + KBTS_FEATURE_TAG_pres = KBTS_FOURCC('p', 'r', 'e', 's'), // Pre-base Substitutions + KBTS_FEATURE_TAG_psts = KBTS_FOURCC('p', 's', 't', 's'), // Post-base Substitutions + KBTS_FEATURE_TAG_pwid = KBTS_FOURCC('p', 'w', 'i', 'd'), // Proportional Widths + KBTS_FEATURE_TAG_qwid = KBTS_FOURCC('q', 'w', 'i', 'd'), // Quarter Widths + KBTS_FEATURE_TAG_rand = KBTS_FOURCC('r', 'a', 'n', 'd'), // Randomize + KBTS_FEATURE_TAG_rclt = KBTS_FOURCC('r', 'c', 'l', 't'), // Required Contextual Alternates + KBTS_FEATURE_TAG_rkrf = KBTS_FOURCC('r', 'k', 'r', 'f'), // Rakar Forms + KBTS_FEATURE_TAG_rlig = KBTS_FOURCC('r', 'l', 'i', 'g'), // Required Ligatures + KBTS_FEATURE_TAG_rtbd = KBTS_FOURCC('r', 't', 'b', 'd'), // Right Bounds + KBTS_FEATURE_TAG_rtla = KBTS_FOURCC('r', 't', 'l', 'a'), // Right-to-left Alternates + KBTS_FEATURE_TAG_rtlm = KBTS_FOURCC('r', 't', 'l', 'm'), // Right-to-left Mirrored Forms + KBTS_FEATURE_TAG_ruby = KBTS_FOURCC('r', 'u', 'b', 'y'), // Ruby Notation Forms + KBTS_FEATURE_TAG_rvrn = KBTS_FOURCC('r', 'v', 'r', 'n'), // Required Variation Alternates + KBTS_FEATURE_TAG_salt = KBTS_FOURCC('s', 'a', 'l', 't'), // Stylistic Alternates + KBTS_FEATURE_TAG_sinf = KBTS_FOURCC('s', 'i', 'n', 'f'), // Scientific Inferiors + KBTS_FEATURE_TAG_size = KBTS_FOURCC('s', 'i', 'z', 'e'), // Optical size + KBTS_FEATURE_TAG_smcp = KBTS_FOURCC('s', 'm', 'c', 'p'), // Small Capitals + KBTS_FEATURE_TAG_smpl = KBTS_FOURCC('s', 'm', 'p', 'l'), // Simplified Forms + KBTS_FEATURE_TAG_ss01 = KBTS_FOURCC('s', 's', '0', '1'), // Stylistic Set 1 + KBTS_FEATURE_TAG_ss02 = KBTS_FOURCC('s', 's', '0', '2'), // Stylistic Set 2 + KBTS_FEATURE_TAG_ss03 = KBTS_FOURCC('s', 's', '0', '3'), // Stylistic Set 3 + KBTS_FEATURE_TAG_ss04 = KBTS_FOURCC('s', 's', '0', '4'), // Stylistic Set 4 + KBTS_FEATURE_TAG_ss05 = KBTS_FOURCC('s', 's', '0', '5'), // Stylistic Set 5 + KBTS_FEATURE_TAG_ss06 = KBTS_FOURCC('s', 's', '0', '6'), // Stylistic Set 6 + KBTS_FEATURE_TAG_ss07 = KBTS_FOURCC('s', 's', '0', '7'), // Stylistic Set 7 + KBTS_FEATURE_TAG_ss08 = KBTS_FOURCC('s', 's', '0', '8'), // Stylistic Set 8 + KBTS_FEATURE_TAG_ss09 = KBTS_FOURCC('s', 's', '0', '9'), // Stylistic Set 9 + KBTS_FEATURE_TAG_ss10 = KBTS_FOURCC('s', 's', '1', '0'), // Stylistic Set 10 + KBTS_FEATURE_TAG_ss11 = KBTS_FOURCC('s', 's', '1', '1'), // Stylistic Set 11 + KBTS_FEATURE_TAG_ss12 = KBTS_FOURCC('s', 's', '1', '2'), // Stylistic Set 12 + KBTS_FEATURE_TAG_ss13 = KBTS_FOURCC('s', 's', '1', '3'), // Stylistic Set 13 + KBTS_FEATURE_TAG_ss14 = KBTS_FOURCC('s', 's', '1', '4'), // Stylistic Set 14 + KBTS_FEATURE_TAG_ss15 = KBTS_FOURCC('s', 's', '1', '5'), // Stylistic Set 15 + KBTS_FEATURE_TAG_ss16 = KBTS_FOURCC('s', 's', '1', '6'), // Stylistic Set 16 + KBTS_FEATURE_TAG_ss17 = KBTS_FOURCC('s', 's', '1', '7'), // Stylistic Set 17 + KBTS_FEATURE_TAG_ss18 = KBTS_FOURCC('s', 's', '1', '8'), // Stylistic Set 18 + KBTS_FEATURE_TAG_ss19 = KBTS_FOURCC('s', 's', '1', '9'), // Stylistic Set 19 + KBTS_FEATURE_TAG_ss20 = KBTS_FOURCC('s', 's', '2', '0'), // Stylistic Set 20 + KBTS_FEATURE_TAG_ssty = KBTS_FOURCC('s', 's', 't', 'y'), // Math Script-style Alternates + KBTS_FEATURE_TAG_stch = KBTS_FOURCC('s', 't', 'c', 'h'), // Stretching Glyph Decomposition + KBTS_FEATURE_TAG_subs = KBTS_FOURCC('s', 'u', 'b', 's'), // Subscript + KBTS_FEATURE_TAG_sups = KBTS_FOURCC('s', 'u', 'p', 's'), // Superscript + KBTS_FEATURE_TAG_swsh = KBTS_FOURCC('s', 'w', 's', 'h'), // Swash + KBTS_FEATURE_TAG_test = KBTS_FOURCC('t', 'e', 's', 't'), // Test features, only for development + KBTS_FEATURE_TAG_titl = KBTS_FOURCC('t', 'i', 't', 'l'), // Titling + KBTS_FEATURE_TAG_tnam = KBTS_FOURCC('t', 'n', 'a', 'm'), // Traditional Name Forms + KBTS_FEATURE_TAG_tnum = KBTS_FOURCC('t', 'n', 'u', 'm'), // Tabular Figures + KBTS_FEATURE_TAG_trad = KBTS_FOURCC('t', 'r', 'a', 'd'), // Traditional Forms + KBTS_FEATURE_TAG_twid = KBTS_FOURCC('t', 'w', 'i', 'd'), // Third Widths + KBTS_FEATURE_TAG_unic = KBTS_FOURCC('u', 'n', 'i', 'c'), // Unicase + KBTS_FEATURE_TAG_valt = KBTS_FOURCC('v', 'a', 'l', 't'), // Alternate Vertical Metrics + KBTS_FEATURE_TAG_vapk = KBTS_FOURCC('v', 'a', 'p', 'k'), // Kerning for Alternate Proportional Vertical Metrics + KBTS_FEATURE_TAG_vatu = KBTS_FOURCC('v', 'a', 't', 'u'), // Vattu Variants + KBTS_FEATURE_TAG_vchw = KBTS_FOURCC('v', 'c', 'h', 'w'), // Vertical Contextual Half-width Spacing + KBTS_FEATURE_TAG_vert = KBTS_FOURCC('v', 'e', 'r', 't'), // Vertical Alternates + KBTS_FEATURE_TAG_vhal = KBTS_FOURCC('v', 'h', 'a', 'l'), // Alternate Vertical Half Metrics + KBTS_FEATURE_TAG_vkna = KBTS_FOURCC('v', 'k', 'n', 'a'), // Vertical Kana Alternates + KBTS_FEATURE_TAG_vkrn = KBTS_FOURCC('v', 'k', 'r', 'n'), // Vertical Kerning + KBTS_FEATURE_TAG_vpal = KBTS_FOURCC('v', 'p', 'a', 'l'), // Proportional Alternate Vertical Metrics + KBTS_FEATURE_TAG_vrt2 = KBTS_FOURCC('v', 'r', 't', '2'), // Vertical Alternates and Rotation + KBTS_FEATURE_TAG_vrtr = KBTS_FOURCC('v', 'r', 't', 'r'), // Vertical Alternates for Rotation + KBTS_FEATURE_TAG_zero = KBTS_FOURCC('z', 'e', 'r', 'o'), // Slashed Zero }; typedef kbts_u32 kbts_feature_id; enum kbts_feature_id_enum { -# define KBTS_X(Name, C0, C1, C2, C3) KBTS_FEATURE_ID_##Name, - KBTS_X_FEATURES -# undef KBTS_X - - KBTS_FEATURE_ID_COUNT, + KBTS_FEATURE_ID_UNREGISTERED, // Features that aren't pre-defined in the OpenType spec + KBTS_FEATURE_ID_isol, // Isolated Forms + KBTS_FEATURE_ID_fina, // Terminal Forms + KBTS_FEATURE_ID_fin2, // Terminal Forms #2 + KBTS_FEATURE_ID_fin3, // Terminal Forms #3 + KBTS_FEATURE_ID_medi, // Medial Forms + KBTS_FEATURE_ID_med2, // Medial Forms #2 + KBTS_FEATURE_ID_init, // Initial Forms + KBTS_FEATURE_ID_ljmo, // Leading Jamo Forms + KBTS_FEATURE_ID_vjmo, // Vowel Jamo Forms + KBTS_FEATURE_ID_tjmo, // Trailing Jamo Forms + KBTS_FEATURE_ID_rphf, // Reph Form + KBTS_FEATURE_ID_blwf, // Below-base Forms + KBTS_FEATURE_ID_half, // Half Forms + KBTS_FEATURE_ID_pstf, // Post-base Forms + KBTS_FEATURE_ID_abvf, // Above-base Forms + KBTS_FEATURE_ID_pref, // Pre-base Forms + KBTS_FEATURE_ID_numr, // Numerators + KBTS_FEATURE_ID_frac, // Fractions + KBTS_FEATURE_ID_dnom, // Denominators + KBTS_FEATURE_ID_cfar, // Conjunct Form After Ro + KBTS_FEATURE_ID_aalt, // Access All Alternates + KBTS_FEATURE_ID_abvm, // Above-base Mark Positioning + KBTS_FEATURE_ID_abvs, // Above-base Substitutions + KBTS_FEATURE_ID_afrc, // Alternative Fractions + KBTS_FEATURE_ID_akhn, // Akhand + KBTS_FEATURE_ID_apkn, // Kerning for Alternate Proportional Widths + KBTS_FEATURE_ID_blwm, // Below-base Mark Positioning + KBTS_FEATURE_ID_blws, // Below-base Substitutions + KBTS_FEATURE_ID_calt, // Contextual Alternates + KBTS_FEATURE_ID_case, // Case-sensitive Forms + KBTS_FEATURE_ID_ccmp, // Glyph Composition / Decomposition + KBTS_FEATURE_ID_chws, // Contextual Half-width Spacing + KBTS_FEATURE_ID_cjct, // Conjunct Forms + KBTS_FEATURE_ID_clig, // Contextual Ligatures + KBTS_FEATURE_ID_cpct, // Centered CJK Punctuation + KBTS_FEATURE_ID_cpsp, // Capital Spacing + KBTS_FEATURE_ID_cswh, // Contextual Swash + KBTS_FEATURE_ID_curs, // Cursive Positioning + KBTS_FEATURE_ID_cv01, // Character Variant 1 + KBTS_FEATURE_ID_cv02, // Character Variant 2 + KBTS_FEATURE_ID_cv03, // Character Variant 3 + KBTS_FEATURE_ID_cv04, // Character Variant 4 + KBTS_FEATURE_ID_cv05, // Character Variant 5 + KBTS_FEATURE_ID_cv06, // Character Variant 6 + KBTS_FEATURE_ID_cv07, // Character Variant 7 + KBTS_FEATURE_ID_cv08, // Character Variant 8 + KBTS_FEATURE_ID_cv09, // Character Variant 9 + KBTS_FEATURE_ID_cv10, // Character Variant 10 + KBTS_FEATURE_ID_cv11, // Character Variant 11 + KBTS_FEATURE_ID_cv12, // Character Variant 12 + KBTS_FEATURE_ID_cv13, // Character Variant 13 + KBTS_FEATURE_ID_cv14, // Character Variant 14 + KBTS_FEATURE_ID_cv15, // Character Variant 15 + KBTS_FEATURE_ID_cv16, // Character Variant 16 + KBTS_FEATURE_ID_cv17, // Character Variant 17 + KBTS_FEATURE_ID_cv18, // Character Variant 18 + KBTS_FEATURE_ID_cv19, // Character Variant 19 + KBTS_FEATURE_ID_cv20, // Character Variant 20 + KBTS_FEATURE_ID_cv21, // Character Variant 21 + KBTS_FEATURE_ID_cv22, // Character Variant 22 + KBTS_FEATURE_ID_cv23, // Character Variant 23 + KBTS_FEATURE_ID_cv24, // Character Variant 24 + KBTS_FEATURE_ID_cv25, // Character Variant 25 + KBTS_FEATURE_ID_cv26, // Character Variant 26 + KBTS_FEATURE_ID_cv27, // Character Variant 27 + KBTS_FEATURE_ID_cv28, // Character Variant 28 + KBTS_FEATURE_ID_cv29, // Character Variant 29 + KBTS_FEATURE_ID_cv30, // Character Variant 30 + KBTS_FEATURE_ID_cv31, // Character Variant 31 + KBTS_FEATURE_ID_cv32, // Character Variant 32 + KBTS_FEATURE_ID_cv33, // Character Variant 33 + KBTS_FEATURE_ID_cv34, // Character Variant 34 + KBTS_FEATURE_ID_cv35, // Character Variant 35 + KBTS_FEATURE_ID_cv36, // Character Variant 36 + KBTS_FEATURE_ID_cv37, // Character Variant 37 + KBTS_FEATURE_ID_cv38, // Character Variant 38 + KBTS_FEATURE_ID_cv39, // Character Variant 39 + KBTS_FEATURE_ID_cv40, // Character Variant 40 + KBTS_FEATURE_ID_cv41, // Character Variant 41 + KBTS_FEATURE_ID_cv42, // Character Variant 42 + KBTS_FEATURE_ID_cv43, // Character Variant 43 + KBTS_FEATURE_ID_cv44, // Character Variant 44 + KBTS_FEATURE_ID_cv45, // Character Variant 45 + KBTS_FEATURE_ID_cv46, // Character Variant 46 + KBTS_FEATURE_ID_cv47, // Character Variant 47 + KBTS_FEATURE_ID_cv48, // Character Variant 48 + KBTS_FEATURE_ID_cv49, // Character Variant 49 + KBTS_FEATURE_ID_cv50, // Character Variant 50 + KBTS_FEATURE_ID_cv51, // Character Variant 51 + KBTS_FEATURE_ID_cv52, // Character Variant 52 + KBTS_FEATURE_ID_cv53, // Character Variant 53 + KBTS_FEATURE_ID_cv54, // Character Variant 54 + KBTS_FEATURE_ID_cv55, // Character Variant 55 + KBTS_FEATURE_ID_cv56, // Character Variant 56 + KBTS_FEATURE_ID_cv57, // Character Variant 57 + KBTS_FEATURE_ID_cv58, // Character Variant 58 + KBTS_FEATURE_ID_cv59, // Character Variant 59 + KBTS_FEATURE_ID_cv60, // Character Variant 60 + KBTS_FEATURE_ID_cv61, // Character Variant 61 + KBTS_FEATURE_ID_cv62, // Character Variant 62 + KBTS_FEATURE_ID_cv63, // Character Variant 63 + KBTS_FEATURE_ID_cv64, // Character Variant 64 + KBTS_FEATURE_ID_cv65, // Character Variant 65 + KBTS_FEATURE_ID_cv66, // Character Variant 66 + KBTS_FEATURE_ID_cv67, // Character Variant 67 + KBTS_FEATURE_ID_cv68, // Character Variant 68 + KBTS_FEATURE_ID_cv69, // Character Variant 69 + KBTS_FEATURE_ID_cv70, // Character Variant 70 + KBTS_FEATURE_ID_cv71, // Character Variant 71 + KBTS_FEATURE_ID_cv72, // Character Variant 72 + KBTS_FEATURE_ID_cv73, // Character Variant 73 + KBTS_FEATURE_ID_cv74, // Character Variant 74 + KBTS_FEATURE_ID_cv75, // Character Variant 75 + KBTS_FEATURE_ID_cv76, // Character Variant 76 + KBTS_FEATURE_ID_cv77, // Character Variant 77 + KBTS_FEATURE_ID_cv78, // Character Variant 78 + KBTS_FEATURE_ID_cv79, // Character Variant 79 + KBTS_FEATURE_ID_cv80, // Character Variant 80 + KBTS_FEATURE_ID_cv81, // Character Variant 81 + KBTS_FEATURE_ID_cv82, // Character Variant 82 + KBTS_FEATURE_ID_cv83, // Character Variant 83 + KBTS_FEATURE_ID_cv84, // Character Variant 84 + KBTS_FEATURE_ID_cv85, // Character Variant 85 + KBTS_FEATURE_ID_cv86, // Character Variant 86 + KBTS_FEATURE_ID_cv87, // Character Variant 87 + KBTS_FEATURE_ID_cv88, // Character Variant 88 + KBTS_FEATURE_ID_cv89, // Character Variant 89 + KBTS_FEATURE_ID_cv90, // Character Variant 90 + KBTS_FEATURE_ID_cv91, // Character Variant 91 + KBTS_FEATURE_ID_cv92, // Character Variant 92 + KBTS_FEATURE_ID_cv93, // Character Variant 93 + KBTS_FEATURE_ID_cv94, // Character Variant 94 + KBTS_FEATURE_ID_cv95, // Character Variant 95 + KBTS_FEATURE_ID_cv96, // Character Variant 96 + KBTS_FEATURE_ID_cv97, // Character Variant 97 + KBTS_FEATURE_ID_cv98, // Character Variant 98 + KBTS_FEATURE_ID_cv99, // Character Variant 99 + KBTS_FEATURE_ID_c2pc, // Petite Capitals From Capitals + KBTS_FEATURE_ID_c2sc, // Small Capitals From Capitals + KBTS_FEATURE_ID_dist, // Distances + KBTS_FEATURE_ID_dlig, // Discretionary Ligatures + KBTS_FEATURE_ID_dtls, // Dotless Forms + KBTS_FEATURE_ID_expt, // Expert Forms + KBTS_FEATURE_ID_falt, // Final Glyph on Line Alternates + KBTS_FEATURE_ID_flac, // Flattened Accent Forms + KBTS_FEATURE_ID_fwid, // Full Widths + KBTS_FEATURE_ID_haln, // Halant Forms + KBTS_FEATURE_ID_halt, // Alternate Half Widths + KBTS_FEATURE_ID_hist, // Historical Forms + KBTS_FEATURE_ID_hkna, // Horizontal Kana Alternates + KBTS_FEATURE_ID_hlig, // Historical Ligatures + KBTS_FEATURE_ID_hngl, // Hangul + KBTS_FEATURE_ID_hojo, // Hojo Kanji Forms (JIS X 0212-1990 Kanji Forms) + KBTS_FEATURE_ID_hwid, // Half Widths + KBTS_FEATURE_ID_ital, // Italics + KBTS_FEATURE_ID_jalt, // Justification Alternates + KBTS_FEATURE_ID_jp78, // JIS78 Forms + KBTS_FEATURE_ID_jp83, // JIS83 Forms + KBTS_FEATURE_ID_jp90, // JIS90 Forms + KBTS_FEATURE_ID_jp04, // JIS2004 Forms + KBTS_FEATURE_ID_kern, // Kerning + KBTS_FEATURE_ID_lfbd, // Left Bounds + KBTS_FEATURE_ID_liga, // Standard Ligatures + KBTS_FEATURE_ID_lnum, // Lining Figures + KBTS_FEATURE_ID_locl, // Localized Forms + KBTS_FEATURE_ID_ltra, // Left-to-right Alternates + KBTS_FEATURE_ID_ltrm, // Left-to-right Mirrored Forms + KBTS_FEATURE_ID_mark, // Mark Positioning + KBTS_FEATURE_ID_mgrk, // Mathematical Greek + KBTS_FEATURE_ID_mkmk, // Mark to Mark Positioning + KBTS_FEATURE_ID_mset, // Mark Positioning via Substitution + KBTS_FEATURE_ID_nalt, // Alternate Annotation Forms + KBTS_FEATURE_ID_nlck, // NLC Kanji Forms + KBTS_FEATURE_ID_nukt, // Nukta Forms + KBTS_FEATURE_ID_onum, // Oldstyle Figures + KBTS_FEATURE_ID_opbd, // Optical Bounds + KBTS_FEATURE_ID_ordn, // Ordinals + KBTS_FEATURE_ID_ornm, // Ornaments + KBTS_FEATURE_ID_palt, // Proportional Alternate Widths + KBTS_FEATURE_ID_pcap, // Petite Capitals + KBTS_FEATURE_ID_pkna, // Proportional Kana + KBTS_FEATURE_ID_pnum, // Proportional Figures + KBTS_FEATURE_ID_pres, // Pre-base Substitutions + KBTS_FEATURE_ID_psts, // Post-base Substitutions + KBTS_FEATURE_ID_pwid, // Proportional Widths + KBTS_FEATURE_ID_qwid, // Quarter Widths + KBTS_FEATURE_ID_rand, // Randomize + KBTS_FEATURE_ID_rclt, // Required Contextual Alternates + KBTS_FEATURE_ID_rkrf, // Rakar Forms + KBTS_FEATURE_ID_rlig, // Required Ligatures + KBTS_FEATURE_ID_rtbd, // Right Bounds + KBTS_FEATURE_ID_rtla, // Right-to-left Alternates + KBTS_FEATURE_ID_rtlm, // Right-to-left Mirrored Forms + KBTS_FEATURE_ID_ruby, // Ruby Notation Forms + KBTS_FEATURE_ID_rvrn, // Required Variation Alternates + KBTS_FEATURE_ID_salt, // Stylistic Alternates + KBTS_FEATURE_ID_sinf, // Scientific Inferiors + KBTS_FEATURE_ID_size, // Optical size + KBTS_FEATURE_ID_smcp, // Small Capitals + KBTS_FEATURE_ID_smpl, // Simplified Forms + KBTS_FEATURE_ID_ss01, // Stylistic Set 1 + KBTS_FEATURE_ID_ss02, // Stylistic Set 2 + KBTS_FEATURE_ID_ss03, // Stylistic Set 3 + KBTS_FEATURE_ID_ss04, // Stylistic Set 4 + KBTS_FEATURE_ID_ss05, // Stylistic Set 5 + KBTS_FEATURE_ID_ss06, // Stylistic Set 6 + KBTS_FEATURE_ID_ss07, // Stylistic Set 7 + KBTS_FEATURE_ID_ss08, // Stylistic Set 8 + KBTS_FEATURE_ID_ss09, // Stylistic Set 9 + KBTS_FEATURE_ID_ss10, // Stylistic Set 10 + KBTS_FEATURE_ID_ss11, // Stylistic Set 11 + KBTS_FEATURE_ID_ss12, // Stylistic Set 12 + KBTS_FEATURE_ID_ss13, // Stylistic Set 13 + KBTS_FEATURE_ID_ss14, // Stylistic Set 14 + KBTS_FEATURE_ID_ss15, // Stylistic Set 15 + KBTS_FEATURE_ID_ss16, // Stylistic Set 16 + KBTS_FEATURE_ID_ss17, // Stylistic Set 17 + KBTS_FEATURE_ID_ss18, // Stylistic Set 18 + KBTS_FEATURE_ID_ss19, // Stylistic Set 19 + KBTS_FEATURE_ID_ss20, // Stylistic Set 20 + KBTS_FEATURE_ID_ssty, // Math Script-style Alternates + KBTS_FEATURE_ID_stch, // Stretching Glyph Decomposition + KBTS_FEATURE_ID_subs, // Subscript + KBTS_FEATURE_ID_sups, // Superscript + KBTS_FEATURE_ID_swsh, // Swash + KBTS_FEATURE_ID_test, // Test features, only for development + KBTS_FEATURE_ID_titl, // Titling + KBTS_FEATURE_ID_tnam, // Traditional Name Forms + KBTS_FEATURE_ID_tnum, // Tabular Figures + KBTS_FEATURE_ID_trad, // Traditional Forms + KBTS_FEATURE_ID_twid, // Third Widths + KBTS_FEATURE_ID_unic, // Unicase + KBTS_FEATURE_ID_valt, // Alternate Vertical Metrics + KBTS_FEATURE_ID_vapk, // Kerning for Alternate Proportional Vertical Metrics + KBTS_FEATURE_ID_vatu, // Vattu Variants + KBTS_FEATURE_ID_vchw, // Vertical Contextual Half-width Spacing + KBTS_FEATURE_ID_vert, // Vertical Alternates + KBTS_FEATURE_ID_vhal, // Alternate Vertical Half Metrics + KBTS_FEATURE_ID_vkna, // Vertical Kana Alternates + KBTS_FEATURE_ID_vkrn, // Vertical Kerning + KBTS_FEATURE_ID_vpal, // Proportional Alternate Vertical Metrics + KBTS_FEATURE_ID_vrt2, // Vertical Alternates and Rotation + KBTS_FEATURE_ID_vrtr, // Vertical Alternates for Rotation + KBTS_FEATURE_ID_zero, // Slashed Zero + KBTS_FEATURE_ID_COUNT, }; typedef kbts_u8 kbts_shaping_table; @@ -1783,6 +2457,7 @@ typedef struct kbts_lookup_subtable_info typedef struct kbts_font { char *FileBase; + kbts_un FileSize; kbts_head *Head; kbts_u16 *Cmap; kbts_gdef *Gdef; @@ -1815,17 +2490,44 @@ typedef struct kbts_glyph_classes kbts_u16 MarkAttachmentClass; } kbts_glyph_classes; +typedef struct kbts_feature_set +{ + kbts_u64 Flags[(KBTS_FEATURE_ID_COUNT + 63) / 64]; +} kbts_feature_set; + +typedef struct kbts_feature_override +{ + kbts_feature_id Id; + kbts_feature_tag Tag; + kbts_u32 EnabledOrAlternatePlusOne; +} kbts_feature_override; + +typedef struct kbts_glyph_config +{ + kbts_feature_set EnabledFeatures; + kbts_feature_set DisabledFeatures; + kbts_u32 FeatureOverrideCount; + kbts_u32 FeatureOverrideCapacity; + kbts_u32 RequiredFeatureOverrideCapacity; + kbts_feature_override *FeatureOverrides; // [FeatureOverrideCount] +} kbts_glyph_config; + typedef struct kbts_glyph { kbts_u32 Codepoint; - kbts_u16 Id; + kbts_u16 Id; // Glyph index. This is what you want to use to query outline data. kbts_u16 Uid; kbts_glyph_classes Classes; kbts_u64 Decomposition; + kbts_glyph_config *Config; + kbts_glyph_flags Flags; + // These fields are the glyph's final positioning data. + // For normal usage, you should not have to use these directly yourself. + // In case you are curious or have a specific need, see kbts_PositionGlyph() to see how these are used. kbts_s32 OffsetX; kbts_s32 OffsetY; kbts_s32 AdvanceX; @@ -1891,6 +2593,7 @@ typedef struct kbts_op_state_normalize typedef struct kbts_op_state_gsub { + kbts_feature_set LookupFeatures; kbts_un LookupIndex; kbts_u32 GlyphFilter; kbts_u32 SkipFlags; @@ -1911,6 +2614,7 @@ typedef union kbts_op_state_op_specific typedef struct kbts_lookup_indices { + kbts_u32 FeatureTag; kbts_u32 FeatureId; kbts_u32 SkipFlags; kbts_u32 GlyphFilter; @@ -1918,11 +2622,6 @@ typedef struct kbts_lookup_indices kbts_u16 *Indices; } kbts_lookup_indices; -typedef struct kbts_feature_set -{ - kbts_u64 Flags[(KBTS_FEATURE_ID_COUNT + 63) / 64]; -} kbts_feature_set; - typedef struct kbts_op { kbts_op_kind Kind; @@ -1940,6 +2639,8 @@ typedef struct kbts_op_state kbts_u32 FeatureCount; kbts_lookup_indices FeatureLookupIndices[KBTS_MAX_SIMULTANEOUS_FEATURES]; + kbts_u32 UnregisteredFeatureCount; + kbts_feature_tag UnregisteredFeatureTags[KBTS_MAX_SIMULTANEOUS_FEATURES]; kbts_op_state_op_specific OpSpecific; @@ -1973,6 +2674,8 @@ typedef struct kbts_shape_config kbts_langsys *Langsys[KBTS_SHAPING_TABLE_COUNT]; kbts_op_list OpLists[4]; + kbts_feature_set *Features; + kbts_shaper Shaper; kbts_shaper_properties *ShaperProperties; @@ -2003,6 +2706,8 @@ typedef struct kbts_shape_state kbts_direction MainDirection; kbts_direction RunDirection; + kbts_feature_set UserFeatures; + kbts_glyph_array GlyphArray; kbts_glyph_array ClusterGlyphArray; @@ -2124,13 +2829,19 @@ typedef struct kbts_decode kbts_u32 Valid; } kbts_decode; -// Shaping #ifndef KB_TEXT_SHAPE_NO_CRT KBTS_EXPORT kbts_font kbts_FontFromFile(const char *FileName); KBTS_EXPORT void kbts_FreeFont(kbts_font *Font); KBTS_EXPORT kbts_shape_state *kbts_CreateShapeState(kbts_font *Font); KBTS_EXPORT void kbts_FreeShapeState(kbts_shape_state *State); #endif +KBTS_EXPORT kbts_feature_id kbts_FeatureTagToId(kbts_feature_tag Tag); +KBTS_EXPORT kbts_feature_override kbts_FeatureOverride(kbts_feature_id Id, int Alternate, kbts_u32 Value); +KBTS_EXPORT kbts_feature_override kbts_FeatureOverrideFromTag(kbts_feature_tag Tag, int Alternate, kbts_u32 Value); +KBTS_EXPORT kbts_glyph_config kbts_GlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCount); +KBTS_EXPORT kbts_glyph_config kbts_EmptyGlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCapacity); +KBTS_EXPORT int kbts_GlyphConfigOverrideFeature(kbts_glyph_config *Config, kbts_feature_id Id, int Alternate, kbts_u32 Value); +KBTS_EXPORT int kbts_GlyphConfigOverrideFeatureFromTag(kbts_glyph_config *Config, kbts_feature_tag Tag, int Alternate, kbts_u32 Value); KBTS_EXPORT int kbts_FontIsValid(kbts_font *Font); KBTS_EXPORT kbts_un kbts_ReadFontHeader(kbts_font *Font, void *Data, kbts_un Size); KBTS_EXPORT kbts_un kbts_ReadFontData(kbts_font *Font, void *Scratch, kbts_un ScratchSize); @@ -2138,8 +2849,8 @@ KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_ KBTS_EXPORT kbts_un kbts_SizeOfShapeState(kbts_font *Font); KBTS_EXPORT kbts_shape_state *kbts_PlaceShapeState(void *Address, kbts_un Size); KBTS_EXPORT void kbts_ResetShapeState(kbts_shape_state *State); -KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_u32 Script, kbts_u32 Language); -KBTS_EXPORT kbts_u32 kbts_ShaperIsComplex(kbts_shaper Shaper); +KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language); +KBTS_EXPORT int kbts_ShaperIsComplex(kbts_shaper Shaper); KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, kbts_direction MainDirection, kbts_direction RunDirection, kbts_glyph *Glyphs, kbts_u32 *GlyphCount, kbts_u32 GlyphCapacity); KBTS_EXPORT kbts_cursor kbts_Cursor(kbts_direction Direction); KBTS_EXPORT void kbts_PositionGlyph(kbts_cursor *Cursor, kbts_glyph *Glyph, kbts_s32 *X, kbts_s32 *Y); @@ -2148,11 +2859,11 @@ KBTS_EXPORT int kbts_BreakStateIsValid(kbts_break_state *State); KBTS_EXPORT void kbts_BreakAddCodepoint(kbts_break_state *State, kbts_u32 Codepoint, kbts_u32 PositionIncrement, int EndOfText); KBTS_EXPORT void kbts_BreakFlush(kbts_break_state *State); KBTS_EXPORT int kbts_Break(kbts_break_state *State, kbts_break *Break); -KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, size_t Length); +KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length); KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint); KBTS_EXPORT void kbts_InferScript(kbts_direction *Direction, kbts_script *Script, kbts_script GlyphScript); KBTS_EXPORT int kbts_ScriptIsComplex(kbts_script Script); -KBTS_EXPORT kbts_u32 kbts_ShaperIsComplex(kbts_shaper Shaper); +KBTS_EXPORT kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag); #endif #ifdef KB_TEXT_SHAPE_IMPLEMENTATION @@ -2204,6 +2915,11 @@ KBTS_EXPORT kbts_u32 kbts_ShaperIsComplex(kbts_shaper Shaper); #include #endif +#ifndef KBTS_MEMSET +#include +#define KBTS_MEMSET memset +#endif + #ifndef kbts_ByteSwap16 # if defined(_MSC_VER) && !defined(__clang__) # define kbts_ByteSwap16(X) _byteswap_ushort(X) @@ -2220,6 +2936,8 @@ KBTS_EXPORT kbts_u32 kbts_ShaperIsComplex(kbts_shaper Shaper); #define KBTS_FEATURE_FLAG0(Feature) (1ull << KBTS_FEATURE_ID_##Feature) #define KBTS_FEATURE_FLAG1(Feature) (1ull << (KBTS_FEATURE_ID_##Feature - 64)) +#define KBTS_FEATURE_FLAG2(Feature) (1ull << (KBTS_FEATURE_ID_##Feature - 128)) +#define KBTS_FEATURE_FLAG3(Feature) (1ull << (KBTS_FEATURE_ID_##Feature - 192)) // # define KBTS_DUMP # ifdef KBTS_DUMP @@ -2243,178 +2961,610 @@ typedef struct kbts_script_properties { } kbts_script_properties; static kbts_script_properties kbts_ScriptProperties[KBTS_SCRIPT_COUNT] = { - {KBTS_FOURCC(' ', ' ', ' ', ' '),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('a', 'd', 'l', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('a', 'h', 'o', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('h', 'l', 'u', 'w'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('a', 'r', 'a', 'b'),KBTS_SHAPER_ARABIC}, - {KBTS_FOURCC('a', 'r', 'm', 'n'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('a', 'v', 's', 't'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'a', 'l', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'a', 'm', 'u'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('b', 'a', 's', 's'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'a', 't', 'k'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'n', 'g', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('b', 'h', 'k', 's'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'o', 'p', 'o'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('b', 'r', 'a', 'h'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'u', 'g', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('b', 'u', 'h', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('c', 'a', 'n', 's'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('c', 'a', 'r', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('a', 'g', 'h', 'b'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('c', 'a', 'k', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('c', 'h', 'a', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('c', 'h', 'e', 'r'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('c', 'h', 'r', 's'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('h', 'a', 'n', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('c', 'o', 'p', 't'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('c', 'p', 'r', 't'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('c', 'p', 'm', 'n'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('c', 'y', 'r', 'l'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('D', 'F', 'L', 'T'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('D', 'F', 'L', 'T'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('d', 's', 'r', 't'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('d', 'e', 'v', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('d', 'i', 'a', 'k'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('d', 'o', 'g', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('d', 'u', 'p', 'l'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('e', 'g', 'y', 'p'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('e', 'l', 'b', 'a'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('e', 'l', 'y', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('e', 't', 'h', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('g', 'a', 'r', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('g', 'e', 'o', 'r'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('g', 'l', 'a', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('g', 'o', 't', 'h'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('g', 'r', 'a', 'n'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('g', 'r', 'e', 'k'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('g', 'j', 'r', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('g', 'o', 'n', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('g', 'u', 'r', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('g', 'u', 'k', 'h'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('h', 'a', 'n', 'g'),KBTS_SHAPER_HANGUL}, - {KBTS_FOURCC('r', 'o', 'h', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('h', 'a', 'n', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('h', 'a', 't', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('h', 'e', 'b', 'r'),KBTS_SHAPER_HEBREW}, - {KBTS_FOURCC('k', 'a', 'n', 'a'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('a', 'r', 'm', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('p', 'h', 'l', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('p', 'r', 't', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('j', 'a', 'v', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 't', 'h', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 'n', 'd', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('k', 'a', 'n', 'a'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('k', 'a', 'w', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 'a', 'l', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 'h', 'a', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 'i', 't', 's'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 'h', 'm', 'r'),KBTS_SHAPER_KHMER}, - {KBTS_FOURCC('k', 'h', 'o', 'j'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'i', 'n', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('k', 'r', 'a', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('l', 'a', 'o', ' '),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('l', 'a', 't', 'n'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('l', 'e', 'p', 'c'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('l', 'i', 'm', 'b'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('l', 'i', 'n', 'a'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('l', 'i', 'n', 'b'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('l', 'i', 's', 'u'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('l', 'y', 'c', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('l', 'y', 'd', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('m', 'a', 'h', 'j'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'a', 'k', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'l', 'm', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('m', 'a', 'n', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'a', 'n', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'a', 'r', 'c'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('g', 'o', 'n', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'e', 'd', 'f'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 't', 'e', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'e', 'n', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'e', 'r', 'c'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'e', 'r', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('p', 'l', 'r', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'o', 'd', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'o', 'n', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'r', 'o', 'o'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('m', 'u', 'l', 't'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('m', 'y', 'm', '2'),KBTS_SHAPER_MYANMAR}, - {KBTS_FOURCC('n', 'b', 'a', 't'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('n', 'a', 'g', 'm'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('n', 'a', 'n', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('n', 'e', 'w', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'a', 'l', 'u'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('n', 'k', 'o', ' '),KBTS_SHAPER_USE}, - {KBTS_FOURCC('n', 's', 'h', 'u'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('h', 'm', 'n', 'p'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('o', 'g', 'a', 'm'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('o', 'l', 'c', 'k'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('o', 'n', 'a', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('i', 't', 'a', 'l'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('h', 'u', 'n', 'g'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('n', 'a', 'r', 'b'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('p', 'e', 'r', 'm'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('x', 'p', 'e', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'o', 'g', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'a', 'r', 'b'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('o', 'r', 'k', 'h'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('o', 'u', 'g', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('o', 'r', 'y', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('o', 's', 'g', 'e'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('o', 's', 'm', 'a'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('h', 'm', 'n', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('p', 'a', 'l', 'm'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('p', 'a', 'u', 'c'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('p', 'h', 'a', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('p', 'h', 'n', 'x'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('p', 'h', 'l', 'p'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('r', 'j', 'n', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('r', 'u', 'n', 'r'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('s', 'a', 'm', 'r'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('s', 'a', 'u', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'h', 'r', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'h', 'a', 'w'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('s', 'i', 'd', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'g', 'n', 'w'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'o', 'g', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'i', 'n', 'h'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'o', 'r', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'o', 'y', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('x', 's', 'u', 'x'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'u', 'n', 'd'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'u', 'n', 'u'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'y', 'l', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('s', 'y', 'r', 'c'),KBTS_SHAPER_ARABIC}, - {KBTS_FOURCC('t', 'g', 'l', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'a', 'g', 'b'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'a', 'l', 'e'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('l', 'a', 'n', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'a', 'v', 't'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'a', 'k', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'm', 'l', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('t', 'n', 's', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'a', 'n', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'e', 'l', '2'),KBTS_SHAPER_INDIC}, - {KBTS_FOURCC('t', 'h', 'a', 'a'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('t', 'h', 'a', 'i'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('t', 'i', 'b', 't'),KBTS_SHAPER_TIBETAN}, - {KBTS_FOURCC('t', 'f', 'n', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'i', 'r', 'h'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'o', 'd', 'r'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'o', 't', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('t', 'u', 't', 'g'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('u', 'g', 'a', 'r'),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('v', 'a', 'i', ' '),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('v', 'i', 't', 'h'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('w', 'c', 'h', 'o'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('w', 'a', 'r', 'a'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('y', 'e', 'z', 'i'),KBTS_SHAPER_USE}, - {KBTS_FOURCC('y', 'i', ' ', ' '),KBTS_SHAPER_DEFAULT}, - {KBTS_FOURCC('z', 'a', 'n', 'b'),KBTS_SHAPER_USE}, + {KBTS_FOURCC(' ', ' ', ' ', ' '), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('a', 'd', 'l', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('a', 'h', 'o', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('h', 'l', 'u', 'w'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('a', 'r', 'a', 'b'), KBTS_SHAPER_ARABIC}, + {KBTS_FOURCC('a', 'r', 'm', 'n'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('a', 'v', 's', 't'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'a', 'l', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'a', 'm', 'u'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('b', 'a', 's', 's'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'a', 't', 'k'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'n', 'g', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('b', 'h', 'k', 's'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'o', 'p', 'o'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('b', 'r', 'a', 'h'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'u', 'g', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('b', 'u', 'h', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('c', 'a', 'n', 's'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('c', 'a', 'r', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('a', 'g', 'h', 'b'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('c', 'a', 'k', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('c', 'h', 'a', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('c', 'h', 'e', 'r'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('c', 'h', 'r', 's'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('h', 'a', 'n', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('c', 'o', 'p', 't'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('c', 'p', 'r', 't'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('c', 'p', 'm', 'n'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('c', 'y', 'r', 'l'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('D', 'F', 'L', 'T'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('D', 'F', 'L', 'T'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('d', 's', 'r', 't'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('d', 'e', 'v', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('d', 'i', 'a', 'k'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('d', 'o', 'g', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('d', 'u', 'p', 'l'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('e', 'g', 'y', 'p'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('e', 'l', 'b', 'a'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('e', 'l', 'y', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('e', 't', 'h', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('g', 'a', 'r', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('g', 'e', 'o', 'r'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('g', 'l', 'a', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('g', 'o', 't', 'h'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('g', 'r', 'a', 'n'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('g', 'r', 'e', 'k'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('g', 'j', 'r', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('g', 'o', 'n', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('g', 'u', 'r', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('g', 'u', 'k', 'h'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('h', 'a', 'n', 'g'), KBTS_SHAPER_HANGUL}, + {KBTS_FOURCC('r', 'o', 'h', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('h', 'a', 'n', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('h', 'a', 't', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('h', 'e', 'b', 'r'), KBTS_SHAPER_HEBREW}, + {KBTS_FOURCC('k', 'a', 'n', 'a'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('a', 'r', 'm', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('p', 'h', 'l', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('p', 'r', 't', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('j', 'a', 'v', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 't', 'h', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 'n', 'd', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('k', 'a', 'n', 'a'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('k', 'a', 'w', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 'a', 'l', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 'h', 'a', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 'i', 't', 's'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 'h', 'm', 'r'), KBTS_SHAPER_KHMER}, + {KBTS_FOURCC('k', 'h', 'o', 'j'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'i', 'n', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('k', 'r', 'a', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('l', 'a', 'o', ' '), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('l', 'a', 't', 'n'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('l', 'e', 'p', 'c'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('l', 'i', 'm', 'b'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('l', 'i', 'n', 'a'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('l', 'i', 'n', 'b'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('l', 'i', 's', 'u'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('l', 'y', 'c', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('l', 'y', 'd', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('m', 'a', 'h', 'j'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'a', 'k', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'l', 'm', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('m', 'a', 'n', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'a', 'n', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'a', 'r', 'c'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('g', 'o', 'n', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'e', 'd', 'f'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 't', 'e', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'e', 'n', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'e', 'r', 'c'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'e', 'r', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('p', 'l', 'r', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'o', 'd', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'o', 'n', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'r', 'o', 'o'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('m', 'u', 'l', 't'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('m', 'y', 'm', '2'), KBTS_SHAPER_MYANMAR}, + {KBTS_FOURCC('n', 'b', 'a', 't'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('n', 'a', 'g', 'm'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('n', 'a', 'n', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('n', 'e', 'w', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'a', 'l', 'u'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('n', 'k', 'o', ' '), KBTS_SHAPER_USE}, + {KBTS_FOURCC('n', 's', 'h', 'u'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('h', 'm', 'n', 'p'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('o', 'g', 'a', 'm'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('o', 'l', 'c', 'k'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('o', 'n', 'a', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('i', 't', 'a', 'l'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('h', 'u', 'n', 'g'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('n', 'a', 'r', 'b'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('p', 'e', 'r', 'm'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('x', 'p', 'e', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'o', 'g', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'a', 'r', 'b'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('o', 'r', 'k', 'h'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('o', 'u', 'g', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('o', 'r', 'y', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('o', 's', 'g', 'e'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('o', 's', 'm', 'a'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('h', 'm', 'n', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('p', 'a', 'l', 'm'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('p', 'a', 'u', 'c'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('p', 'h', 'a', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('p', 'h', 'n', 'x'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('p', 'h', 'l', 'p'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('r', 'j', 'n', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('r', 'u', 'n', 'r'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('s', 'a', 'm', 'r'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('s', 'a', 'u', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'h', 'r', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'h', 'a', 'w'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('s', 'i', 'd', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'g', 'n', 'w'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'o', 'g', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'i', 'n', 'h'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'o', 'r', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'o', 'y', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('x', 's', 'u', 'x'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'u', 'n', 'd'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'u', 'n', 'u'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'y', 'l', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('s', 'y', 'r', 'c'), KBTS_SHAPER_ARABIC}, + {KBTS_FOURCC('t', 'g', 'l', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'a', 'g', 'b'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'a', 'l', 'e'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('l', 'a', 'n', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'a', 'v', 't'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'a', 'k', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'm', 'l', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('t', 'n', 's', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'a', 'n', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'e', 'l', '2'), KBTS_SHAPER_INDIC}, + {KBTS_FOURCC('t', 'h', 'a', 'a'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('t', 'h', 'a', 'i'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('t', 'i', 'b', 't'), KBTS_SHAPER_TIBETAN}, + {KBTS_FOURCC('t', 'f', 'n', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'i', 'r', 'h'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'o', 'd', 'r'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'o', 't', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('t', 'u', 't', 'g'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('u', 'g', 'a', 'r'), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('v', 'a', 'i', ' '), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('v', 'i', 't', 'h'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('w', 'c', 'h', 'o'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('w', 'a', 'r', 'a'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('y', 'e', 'z', 'i'), KBTS_SHAPER_USE}, + {KBTS_FOURCC('y', 'i', ' ', ' '), KBTS_SHAPER_DEFAULT}, + {KBTS_FOURCC('z', 'a', 'n', 'b'), KBTS_SHAPER_USE}, }; +KBTS_EXPORT kbts_script kbts_ScriptTagToScript(kbts_script_tag Tag) +{ + kbts_script Result = 0; + switch(Tag) + { + case KBTS_SCRIPT_TAG_DONT_KNOW: Result = KBTS_SCRIPT_DONT_KNOW; break; + case KBTS_SCRIPT_TAG_ADLAM: Result = KBTS_SCRIPT_ADLAM; break; + case KBTS_SCRIPT_TAG_AHOM: Result = KBTS_SCRIPT_AHOM; break; + case KBTS_SCRIPT_TAG_ANATOLIAN_HIEROGLYPHS: Result = KBTS_SCRIPT_ANATOLIAN_HIEROGLYPHS; break; + case KBTS_SCRIPT_TAG_ARABIC: Result = KBTS_SCRIPT_ARABIC; break; + case KBTS_SCRIPT_TAG_ARMENIAN: Result = KBTS_SCRIPT_ARMENIAN; break; + case KBTS_SCRIPT_TAG_AVESTAN: Result = KBTS_SCRIPT_AVESTAN; break; + case KBTS_SCRIPT_TAG_BALINESE: Result = KBTS_SCRIPT_BALINESE; break; + case KBTS_SCRIPT_TAG_BAMUM: Result = KBTS_SCRIPT_BAMUM; break; + case KBTS_SCRIPT_TAG_BASSA_VAH: Result = KBTS_SCRIPT_BASSA_VAH; break; + case KBTS_SCRIPT_TAG_BATAK: Result = KBTS_SCRIPT_BATAK; break; + case KBTS_SCRIPT_TAG_BENGALI: Result = KBTS_SCRIPT_BENGALI; break; + case KBTS_SCRIPT_TAG_BHAIKSUKI: Result = KBTS_SCRIPT_BHAIKSUKI; break; + case KBTS_SCRIPT_TAG_BOPOMOFO: Result = KBTS_SCRIPT_BOPOMOFO; break; + case KBTS_SCRIPT_TAG_BRAHMI: Result = KBTS_SCRIPT_BRAHMI; break; + case KBTS_SCRIPT_TAG_BUGINESE: Result = KBTS_SCRIPT_BUGINESE; break; + case KBTS_SCRIPT_TAG_BUHID: Result = KBTS_SCRIPT_BUHID; break; + case KBTS_SCRIPT_TAG_CANADIAN_SYLLABICS: Result = KBTS_SCRIPT_CANADIAN_SYLLABICS; break; + case KBTS_SCRIPT_TAG_CARIAN: Result = KBTS_SCRIPT_CARIAN; break; + case KBTS_SCRIPT_TAG_CAUCASIAN_ALBANIAN: Result = KBTS_SCRIPT_CAUCASIAN_ALBANIAN; break; + case KBTS_SCRIPT_TAG_CHAKMA: Result = KBTS_SCRIPT_CHAKMA; break; + case KBTS_SCRIPT_TAG_CHAM: Result = KBTS_SCRIPT_CHAM; break; + case KBTS_SCRIPT_TAG_CHEROKEE: Result = KBTS_SCRIPT_CHEROKEE; break; + case KBTS_SCRIPT_TAG_CHORASMIAN: Result = KBTS_SCRIPT_CHORASMIAN; break; + case KBTS_SCRIPT_TAG_CJK_IDEOGRAPHIC: Result = KBTS_SCRIPT_CJK_IDEOGRAPHIC; break; + case KBTS_SCRIPT_TAG_COPTIC: Result = KBTS_SCRIPT_COPTIC; break; + case KBTS_SCRIPT_TAG_CYPRIOT_SYLLABARY: Result = KBTS_SCRIPT_CYPRIOT_SYLLABARY; break; + case KBTS_SCRIPT_TAG_CYPRO_MINOAN: Result = KBTS_SCRIPT_CYPRO_MINOAN; break; + case KBTS_SCRIPT_TAG_CYRILLIC: Result = KBTS_SCRIPT_CYRILLIC; break; + case KBTS_SCRIPT_TAG_DEFAULT: Result = KBTS_SCRIPT_DEFAULT; break; + case KBTS_SCRIPT_TAG_DESERET: Result = KBTS_SCRIPT_DESERET; break; + case KBTS_SCRIPT_TAG_DEVANAGARI: Result = KBTS_SCRIPT_DEVANAGARI; break; + case KBTS_SCRIPT_TAG_DIVES_AKURU: Result = KBTS_SCRIPT_DIVES_AKURU; break; + case KBTS_SCRIPT_TAG_DOGRA: Result = KBTS_SCRIPT_DOGRA; break; + case KBTS_SCRIPT_TAG_DUPLOYAN: Result = KBTS_SCRIPT_DUPLOYAN; break; + case KBTS_SCRIPT_TAG_EGYPTIAN_HIEROGLYPHS: Result = KBTS_SCRIPT_EGYPTIAN_HIEROGLYPHS; break; + case KBTS_SCRIPT_TAG_ELBASAN: Result = KBTS_SCRIPT_ELBASAN; break; + case KBTS_SCRIPT_TAG_ELYMAIC: Result = KBTS_SCRIPT_ELYMAIC; break; + case KBTS_SCRIPT_TAG_ETHIOPIC: Result = KBTS_SCRIPT_ETHIOPIC; break; + case KBTS_SCRIPT_TAG_GARAY: Result = KBTS_SCRIPT_GARAY; break; + case KBTS_SCRIPT_TAG_GEORGIAN: Result = KBTS_SCRIPT_GEORGIAN; break; + case KBTS_SCRIPT_TAG_GLAGOLITIC: Result = KBTS_SCRIPT_GLAGOLITIC; break; + case KBTS_SCRIPT_TAG_GOTHIC: Result = KBTS_SCRIPT_GOTHIC; break; + case KBTS_SCRIPT_TAG_GRANTHA: Result = KBTS_SCRIPT_GRANTHA; break; + case KBTS_SCRIPT_TAG_GREEK: Result = KBTS_SCRIPT_GREEK; break; + case KBTS_SCRIPT_TAG_GUJARATI: Result = KBTS_SCRIPT_GUJARATI; break; + case KBTS_SCRIPT_TAG_GUNJALA_GONDI: Result = KBTS_SCRIPT_GUNJALA_GONDI; break; + case KBTS_SCRIPT_TAG_GURMUKHI: Result = KBTS_SCRIPT_GURMUKHI; break; + case KBTS_SCRIPT_TAG_GURUNG_KHEMA: Result = KBTS_SCRIPT_GURUNG_KHEMA; break; + case KBTS_SCRIPT_TAG_HANGUL: Result = KBTS_SCRIPT_HANGUL; break; + case KBTS_SCRIPT_TAG_HANIFI_ROHINGYA: Result = KBTS_SCRIPT_HANIFI_ROHINGYA; break; + case KBTS_SCRIPT_TAG_HANUNOO: Result = KBTS_SCRIPT_HANUNOO; break; + case KBTS_SCRIPT_TAG_HATRAN: Result = KBTS_SCRIPT_HATRAN; break; + case KBTS_SCRIPT_TAG_HEBREW: Result = KBTS_SCRIPT_HEBREW; break; + case KBTS_SCRIPT_TAG_HIRAGANA: Result = KBTS_SCRIPT_HIRAGANA; break; + case KBTS_SCRIPT_TAG_IMPERIAL_ARAMAIC: Result = KBTS_SCRIPT_IMPERIAL_ARAMAIC; break; + case KBTS_SCRIPT_TAG_INSCRIPTIONAL_PAHLAVI: Result = KBTS_SCRIPT_INSCRIPTIONAL_PAHLAVI; break; + case KBTS_SCRIPT_TAG_INSCRIPTIONAL_PARTHIAN: Result = KBTS_SCRIPT_INSCRIPTIONAL_PARTHIAN; break; + case KBTS_SCRIPT_TAG_JAVANESE: Result = KBTS_SCRIPT_JAVANESE; break; + case KBTS_SCRIPT_TAG_KAITHI: Result = KBTS_SCRIPT_KAITHI; break; + case KBTS_SCRIPT_TAG_KANNADA: Result = KBTS_SCRIPT_KANNADA; break; + case KBTS_SCRIPT_TAG_KAWI: Result = KBTS_SCRIPT_KAWI; break; + case KBTS_SCRIPT_TAG_KAYAH_LI: Result = KBTS_SCRIPT_KAYAH_LI; break; + case KBTS_SCRIPT_TAG_KHAROSHTHI: Result = KBTS_SCRIPT_KHAROSHTHI; break; + case KBTS_SCRIPT_TAG_KHITAN_SMALL_SCRIPT: Result = KBTS_SCRIPT_KHITAN_SMALL_SCRIPT; break; + case KBTS_SCRIPT_TAG_KHMER: Result = KBTS_SCRIPT_KHMER; break; + case KBTS_SCRIPT_TAG_KHOJKI: Result = KBTS_SCRIPT_KHOJKI; break; + case KBTS_SCRIPT_TAG_KHUDAWADI: Result = KBTS_SCRIPT_KHUDAWADI; break; + case KBTS_SCRIPT_TAG_KIRAT_RAI: Result = KBTS_SCRIPT_KIRAT_RAI; break; + case KBTS_SCRIPT_TAG_LAO: Result = KBTS_SCRIPT_LAO; break; + case KBTS_SCRIPT_TAG_LATIN: Result = KBTS_SCRIPT_LATIN; break; + case KBTS_SCRIPT_TAG_LEPCHA: Result = KBTS_SCRIPT_LEPCHA; break; + case KBTS_SCRIPT_TAG_LIMBU: Result = KBTS_SCRIPT_LIMBU; break; + case KBTS_SCRIPT_TAG_LINEAR_A: Result = KBTS_SCRIPT_LINEAR_A; break; + case KBTS_SCRIPT_TAG_LINEAR_B: Result = KBTS_SCRIPT_LINEAR_B; break; + case KBTS_SCRIPT_TAG_LISU: Result = KBTS_SCRIPT_LISU; break; + case KBTS_SCRIPT_TAG_LYCIAN: Result = KBTS_SCRIPT_LYCIAN; break; + case KBTS_SCRIPT_TAG_LYDIAN: Result = KBTS_SCRIPT_LYDIAN; break; + case KBTS_SCRIPT_TAG_MAHAJANI: Result = KBTS_SCRIPT_MAHAJANI; break; + case KBTS_SCRIPT_TAG_MAKASAR: Result = KBTS_SCRIPT_MAKASAR; break; + case KBTS_SCRIPT_TAG_MALAYALAM: Result = KBTS_SCRIPT_MALAYALAM; break; + case KBTS_SCRIPT_TAG_MANDAIC: Result = KBTS_SCRIPT_MANDAIC; break; + case KBTS_SCRIPT_TAG_MANICHAEAN: Result = KBTS_SCRIPT_MANICHAEAN; break; + case KBTS_SCRIPT_TAG_MARCHEN: Result = KBTS_SCRIPT_MARCHEN; break; + case KBTS_SCRIPT_TAG_MASARAM_GONDI: Result = KBTS_SCRIPT_MASARAM_GONDI; break; + case KBTS_SCRIPT_TAG_MEDEFAIDRIN: Result = KBTS_SCRIPT_MEDEFAIDRIN; break; + case KBTS_SCRIPT_TAG_MEETEI_MAYEK: Result = KBTS_SCRIPT_MEETEI_MAYEK; break; + case KBTS_SCRIPT_TAG_MENDE_KIKAKUI: Result = KBTS_SCRIPT_MENDE_KIKAKUI; break; + case KBTS_SCRIPT_TAG_MEROITIC_CURSIVE: Result = KBTS_SCRIPT_MEROITIC_CURSIVE; break; + case KBTS_SCRIPT_TAG_MEROITIC_HIEROGLYPHS: Result = KBTS_SCRIPT_MEROITIC_HIEROGLYPHS; break; + case KBTS_SCRIPT_TAG_MIAO: Result = KBTS_SCRIPT_MIAO; break; + case KBTS_SCRIPT_TAG_MODI: Result = KBTS_SCRIPT_MODI; break; + case KBTS_SCRIPT_TAG_MONGOLIAN: Result = KBTS_SCRIPT_MONGOLIAN; break; + case KBTS_SCRIPT_TAG_MRO: Result = KBTS_SCRIPT_MRO; break; + case KBTS_SCRIPT_TAG_MULTANI: Result = KBTS_SCRIPT_MULTANI; break; + case KBTS_SCRIPT_TAG_MYANMAR: Result = KBTS_SCRIPT_MYANMAR; break; + case KBTS_SCRIPT_TAG_NABATAEAN: Result = KBTS_SCRIPT_NABATAEAN; break; + case KBTS_SCRIPT_TAG_NAG_MUNDARI: Result = KBTS_SCRIPT_NAG_MUNDARI; break; + case KBTS_SCRIPT_TAG_NANDINAGARI: Result = KBTS_SCRIPT_NANDINAGARI; break; + case KBTS_SCRIPT_TAG_NEWA: Result = KBTS_SCRIPT_NEWA; break; + case KBTS_SCRIPT_TAG_NEW_TAI_LUE: Result = KBTS_SCRIPT_NEW_TAI_LUE; break; + case KBTS_SCRIPT_TAG_NKO: Result = KBTS_SCRIPT_NKO; break; + case KBTS_SCRIPT_TAG_NUSHU: Result = KBTS_SCRIPT_NUSHU; break; + case KBTS_SCRIPT_TAG_NYIAKENG_PUACHUE_HMONG: Result = KBTS_SCRIPT_NYIAKENG_PUACHUE_HMONG; break; + case KBTS_SCRIPT_TAG_OGHAM: Result = KBTS_SCRIPT_OGHAM; break; + case KBTS_SCRIPT_TAG_OL_CHIKI: Result = KBTS_SCRIPT_OL_CHIKI; break; + case KBTS_SCRIPT_TAG_OL_ONAL: Result = KBTS_SCRIPT_OL_ONAL; break; + case KBTS_SCRIPT_TAG_OLD_ITALIC: Result = KBTS_SCRIPT_OLD_ITALIC; break; + case KBTS_SCRIPT_TAG_OLD_HUNGARIAN: Result = KBTS_SCRIPT_OLD_HUNGARIAN; break; + case KBTS_SCRIPT_TAG_OLD_NORTH_ARABIAN: Result = KBTS_SCRIPT_OLD_NORTH_ARABIAN; break; + case KBTS_SCRIPT_TAG_OLD_PERMIC: Result = KBTS_SCRIPT_OLD_PERMIC; break; + case KBTS_SCRIPT_TAG_OLD_PERSIAN_CUNEIFORM: Result = KBTS_SCRIPT_OLD_PERSIAN_CUNEIFORM; break; + case KBTS_SCRIPT_TAG_OLD_SOGDIAN: Result = KBTS_SCRIPT_OLD_SOGDIAN; break; + case KBTS_SCRIPT_TAG_OLD_SOUTH_ARABIAN: Result = KBTS_SCRIPT_OLD_SOUTH_ARABIAN; break; + case KBTS_SCRIPT_TAG_OLD_TURKIC: Result = KBTS_SCRIPT_OLD_TURKIC; break; + case KBTS_SCRIPT_TAG_OLD_UYGHUR: Result = KBTS_SCRIPT_OLD_UYGHUR; break; + case KBTS_SCRIPT_TAG_ODIA: Result = KBTS_SCRIPT_ODIA; break; + case KBTS_SCRIPT_TAG_OSAGE: Result = KBTS_SCRIPT_OSAGE; break; + case KBTS_SCRIPT_TAG_OSMANYA: Result = KBTS_SCRIPT_OSMANYA; break; + case KBTS_SCRIPT_TAG_PAHAWH_HMONG: Result = KBTS_SCRIPT_PAHAWH_HMONG; break; + case KBTS_SCRIPT_TAG_PALMYRENE: Result = KBTS_SCRIPT_PALMYRENE; break; + case KBTS_SCRIPT_TAG_PAU_CIN_HAU: Result = KBTS_SCRIPT_PAU_CIN_HAU; break; + case KBTS_SCRIPT_TAG_PHAGS_PA: Result = KBTS_SCRIPT_PHAGS_PA; break; + case KBTS_SCRIPT_TAG_PHOENICIAN: Result = KBTS_SCRIPT_PHOENICIAN; break; + case KBTS_SCRIPT_TAG_PSALTER_PAHLAVI: Result = KBTS_SCRIPT_PSALTER_PAHLAVI; break; + case KBTS_SCRIPT_TAG_REJANG: Result = KBTS_SCRIPT_REJANG; break; + case KBTS_SCRIPT_TAG_RUNIC: Result = KBTS_SCRIPT_RUNIC; break; + case KBTS_SCRIPT_TAG_SAMARITAN: Result = KBTS_SCRIPT_SAMARITAN; break; + case KBTS_SCRIPT_TAG_SAURASHTRA: Result = KBTS_SCRIPT_SAURASHTRA; break; + case KBTS_SCRIPT_TAG_SHARADA: Result = KBTS_SCRIPT_SHARADA; break; + case KBTS_SCRIPT_TAG_SHAVIAN: Result = KBTS_SCRIPT_SHAVIAN; break; + case KBTS_SCRIPT_TAG_SIDDHAM: Result = KBTS_SCRIPT_SIDDHAM; break; + case KBTS_SCRIPT_TAG_SIGN_WRITING: Result = KBTS_SCRIPT_SIGN_WRITING; break; + case KBTS_SCRIPT_TAG_SOGDIAN: Result = KBTS_SCRIPT_SOGDIAN; break; + case KBTS_SCRIPT_TAG_SINHALA: Result = KBTS_SCRIPT_SINHALA; break; + case KBTS_SCRIPT_TAG_SORA_SOMPENG: Result = KBTS_SCRIPT_SORA_SOMPENG; break; + case KBTS_SCRIPT_TAG_SOYOMBO: Result = KBTS_SCRIPT_SOYOMBO; break; + case KBTS_SCRIPT_TAG_SUMERO_AKKADIAN_CUNEIFORM: Result = KBTS_SCRIPT_SUMERO_AKKADIAN_CUNEIFORM; break; + case KBTS_SCRIPT_TAG_SUNDANESE: Result = KBTS_SCRIPT_SUNDANESE; break; + case KBTS_SCRIPT_TAG_SUNUWAR: Result = KBTS_SCRIPT_SUNUWAR; break; + case KBTS_SCRIPT_TAG_SYLOTI_NAGRI: Result = KBTS_SCRIPT_SYLOTI_NAGRI; break; + case KBTS_SCRIPT_TAG_SYRIAC: Result = KBTS_SCRIPT_SYRIAC; break; + case KBTS_SCRIPT_TAG_TAGALOG: Result = KBTS_SCRIPT_TAGALOG; break; + case KBTS_SCRIPT_TAG_TAGBANWA: Result = KBTS_SCRIPT_TAGBANWA; break; + case KBTS_SCRIPT_TAG_TAI_LE: Result = KBTS_SCRIPT_TAI_LE; break; + case KBTS_SCRIPT_TAG_TAI_THAM: Result = KBTS_SCRIPT_TAI_THAM; break; + case KBTS_SCRIPT_TAG_TAI_VIET: Result = KBTS_SCRIPT_TAI_VIET; break; + case KBTS_SCRIPT_TAG_TAKRI: Result = KBTS_SCRIPT_TAKRI; break; + case KBTS_SCRIPT_TAG_TAMIL: Result = KBTS_SCRIPT_TAMIL; break; + case KBTS_SCRIPT_TAG_TANGSA: Result = KBTS_SCRIPT_TANGSA; break; + case KBTS_SCRIPT_TAG_TANGUT: Result = KBTS_SCRIPT_TANGUT; break; + case KBTS_SCRIPT_TAG_TELUGU: Result = KBTS_SCRIPT_TELUGU; break; + case KBTS_SCRIPT_TAG_THAANA: Result = KBTS_SCRIPT_THAANA; break; + case KBTS_SCRIPT_TAG_THAI: Result = KBTS_SCRIPT_THAI; break; + case KBTS_SCRIPT_TAG_TIBETAN: Result = KBTS_SCRIPT_TIBETAN; break; + case KBTS_SCRIPT_TAG_TIFINAGH: Result = KBTS_SCRIPT_TIFINAGH; break; + case KBTS_SCRIPT_TAG_TIRHUTA: Result = KBTS_SCRIPT_TIRHUTA; break; + case KBTS_SCRIPT_TAG_TODHRI: Result = KBTS_SCRIPT_TODHRI; break; + case KBTS_SCRIPT_TAG_TOTO: Result = KBTS_SCRIPT_TOTO; break; + case KBTS_SCRIPT_TAG_TULU_TIGALARI: Result = KBTS_SCRIPT_TULU_TIGALARI; break; + case KBTS_SCRIPT_TAG_UGARITIC_CUNEIFORM: Result = KBTS_SCRIPT_UGARITIC_CUNEIFORM; break; + case KBTS_SCRIPT_TAG_VAI: Result = KBTS_SCRIPT_VAI; break; + case KBTS_SCRIPT_TAG_VITHKUQI: Result = KBTS_SCRIPT_VITHKUQI; break; + case KBTS_SCRIPT_TAG_WANCHO: Result = KBTS_SCRIPT_WANCHO; break; + case KBTS_SCRIPT_TAG_WARANG_CITI: Result = KBTS_SCRIPT_WARANG_CITI; break; + case KBTS_SCRIPT_TAG_YEZIDI: Result = KBTS_SCRIPT_YEZIDI; break; + case KBTS_SCRIPT_TAG_YI: Result = KBTS_SCRIPT_YI; break; + case KBTS_SCRIPT_TAG_ZANABAZAR_SQUARE: Result = KBTS_SCRIPT_ZANABAZAR_SQUARE; break; + default: break; + } + return Result; +} + +KBTS_EXPORT kbts_feature_id kbts_FeatureTagToId(kbts_feature_tag Tag) +{ + kbts_feature_id Result = 0; + switch(Tag) + { + case KBTS_FEATURE_TAG_isol: Result = KBTS_FEATURE_ID_isol; break; + case KBTS_FEATURE_TAG_fina: Result = KBTS_FEATURE_ID_fina; break; + case KBTS_FEATURE_TAG_fin2: Result = KBTS_FEATURE_ID_fin2; break; + case KBTS_FEATURE_TAG_fin3: Result = KBTS_FEATURE_ID_fin3; break; + case KBTS_FEATURE_TAG_medi: Result = KBTS_FEATURE_ID_medi; break; + case KBTS_FEATURE_TAG_med2: Result = KBTS_FEATURE_ID_med2; break; + case KBTS_FEATURE_TAG_init: Result = KBTS_FEATURE_ID_init; break; + case KBTS_FEATURE_TAG_ljmo: Result = KBTS_FEATURE_ID_ljmo; break; + case KBTS_FEATURE_TAG_vjmo: Result = KBTS_FEATURE_ID_vjmo; break; + case KBTS_FEATURE_TAG_tjmo: Result = KBTS_FEATURE_ID_tjmo; break; + case KBTS_FEATURE_TAG_rphf: Result = KBTS_FEATURE_ID_rphf; break; + case KBTS_FEATURE_TAG_blwf: Result = KBTS_FEATURE_ID_blwf; break; + case KBTS_FEATURE_TAG_half: Result = KBTS_FEATURE_ID_half; break; + case KBTS_FEATURE_TAG_pstf: Result = KBTS_FEATURE_ID_pstf; break; + case KBTS_FEATURE_TAG_abvf: Result = KBTS_FEATURE_ID_abvf; break; + case KBTS_FEATURE_TAG_pref: Result = KBTS_FEATURE_ID_pref; break; + case KBTS_FEATURE_TAG_numr: Result = KBTS_FEATURE_ID_numr; break; + case KBTS_FEATURE_TAG_frac: Result = KBTS_FEATURE_ID_frac; break; + case KBTS_FEATURE_TAG_dnom: Result = KBTS_FEATURE_ID_dnom; break; + case KBTS_FEATURE_TAG_cfar: Result = KBTS_FEATURE_ID_cfar; break; + case KBTS_FEATURE_TAG_aalt: Result = KBTS_FEATURE_ID_aalt; break; + case KBTS_FEATURE_TAG_abvm: Result = KBTS_FEATURE_ID_abvm; break; + case KBTS_FEATURE_TAG_abvs: Result = KBTS_FEATURE_ID_abvs; break; + case KBTS_FEATURE_TAG_afrc: Result = KBTS_FEATURE_ID_afrc; break; + case KBTS_FEATURE_TAG_akhn: Result = KBTS_FEATURE_ID_akhn; break; + case KBTS_FEATURE_TAG_apkn: Result = KBTS_FEATURE_ID_apkn; break; + case KBTS_FEATURE_TAG_blwm: Result = KBTS_FEATURE_ID_blwm; break; + case KBTS_FEATURE_TAG_blws: Result = KBTS_FEATURE_ID_blws; break; + case KBTS_FEATURE_TAG_calt: Result = KBTS_FEATURE_ID_calt; break; + case KBTS_FEATURE_TAG_case: Result = KBTS_FEATURE_ID_case; break; + case KBTS_FEATURE_TAG_ccmp: Result = KBTS_FEATURE_ID_ccmp; break; + case KBTS_FEATURE_TAG_chws: Result = KBTS_FEATURE_ID_chws; break; + case KBTS_FEATURE_TAG_cjct: Result = KBTS_FEATURE_ID_cjct; break; + case KBTS_FEATURE_TAG_clig: Result = KBTS_FEATURE_ID_clig; break; + case KBTS_FEATURE_TAG_cpct: Result = KBTS_FEATURE_ID_cpct; break; + case KBTS_FEATURE_TAG_cpsp: Result = KBTS_FEATURE_ID_cpsp; break; + case KBTS_FEATURE_TAG_cswh: Result = KBTS_FEATURE_ID_cswh; break; + case KBTS_FEATURE_TAG_curs: Result = KBTS_FEATURE_ID_curs; break; + case KBTS_FEATURE_TAG_cv01: Result = KBTS_FEATURE_ID_cv01; break; + case KBTS_FEATURE_TAG_cv02: Result = KBTS_FEATURE_ID_cv02; break; + case KBTS_FEATURE_TAG_cv03: Result = KBTS_FEATURE_ID_cv03; break; + case KBTS_FEATURE_TAG_cv04: Result = KBTS_FEATURE_ID_cv04; break; + case KBTS_FEATURE_TAG_cv05: Result = KBTS_FEATURE_ID_cv05; break; + case KBTS_FEATURE_TAG_cv06: Result = KBTS_FEATURE_ID_cv06; break; + case KBTS_FEATURE_TAG_cv07: Result = KBTS_FEATURE_ID_cv07; break; + case KBTS_FEATURE_TAG_cv08: Result = KBTS_FEATURE_ID_cv08; break; + case KBTS_FEATURE_TAG_cv09: Result = KBTS_FEATURE_ID_cv09; break; + case KBTS_FEATURE_TAG_cv10: Result = KBTS_FEATURE_ID_cv10; break; + case KBTS_FEATURE_TAG_cv11: Result = KBTS_FEATURE_ID_cv11; break; + case KBTS_FEATURE_TAG_cv12: Result = KBTS_FEATURE_ID_cv12; break; + case KBTS_FEATURE_TAG_cv13: Result = KBTS_FEATURE_ID_cv13; break; + case KBTS_FEATURE_TAG_cv14: Result = KBTS_FEATURE_ID_cv14; break; + case KBTS_FEATURE_TAG_cv15: Result = KBTS_FEATURE_ID_cv15; break; + case KBTS_FEATURE_TAG_cv16: Result = KBTS_FEATURE_ID_cv16; break; + case KBTS_FEATURE_TAG_cv17: Result = KBTS_FEATURE_ID_cv17; break; + case KBTS_FEATURE_TAG_cv18: Result = KBTS_FEATURE_ID_cv18; break; + case KBTS_FEATURE_TAG_cv19: Result = KBTS_FEATURE_ID_cv19; break; + case KBTS_FEATURE_TAG_cv20: Result = KBTS_FEATURE_ID_cv20; break; + case KBTS_FEATURE_TAG_cv21: Result = KBTS_FEATURE_ID_cv21; break; + case KBTS_FEATURE_TAG_cv22: Result = KBTS_FEATURE_ID_cv22; break; + case KBTS_FEATURE_TAG_cv23: Result = KBTS_FEATURE_ID_cv23; break; + case KBTS_FEATURE_TAG_cv24: Result = KBTS_FEATURE_ID_cv24; break; + case KBTS_FEATURE_TAG_cv25: Result = KBTS_FEATURE_ID_cv25; break; + case KBTS_FEATURE_TAG_cv26: Result = KBTS_FEATURE_ID_cv26; break; + case KBTS_FEATURE_TAG_cv27: Result = KBTS_FEATURE_ID_cv27; break; + case KBTS_FEATURE_TAG_cv28: Result = KBTS_FEATURE_ID_cv28; break; + case KBTS_FEATURE_TAG_cv29: Result = KBTS_FEATURE_ID_cv29; break; + case KBTS_FEATURE_TAG_cv30: Result = KBTS_FEATURE_ID_cv30; break; + case KBTS_FEATURE_TAG_cv31: Result = KBTS_FEATURE_ID_cv31; break; + case KBTS_FEATURE_TAG_cv32: Result = KBTS_FEATURE_ID_cv32; break; + case KBTS_FEATURE_TAG_cv33: Result = KBTS_FEATURE_ID_cv33; break; + case KBTS_FEATURE_TAG_cv34: Result = KBTS_FEATURE_ID_cv34; break; + case KBTS_FEATURE_TAG_cv35: Result = KBTS_FEATURE_ID_cv35; break; + case KBTS_FEATURE_TAG_cv36: Result = KBTS_FEATURE_ID_cv36; break; + case KBTS_FEATURE_TAG_cv37: Result = KBTS_FEATURE_ID_cv37; break; + case KBTS_FEATURE_TAG_cv38: Result = KBTS_FEATURE_ID_cv38; break; + case KBTS_FEATURE_TAG_cv39: Result = KBTS_FEATURE_ID_cv39; break; + case KBTS_FEATURE_TAG_cv40: Result = KBTS_FEATURE_ID_cv40; break; + case KBTS_FEATURE_TAG_cv41: Result = KBTS_FEATURE_ID_cv41; break; + case KBTS_FEATURE_TAG_cv42: Result = KBTS_FEATURE_ID_cv42; break; + case KBTS_FEATURE_TAG_cv43: Result = KBTS_FEATURE_ID_cv43; break; + case KBTS_FEATURE_TAG_cv44: Result = KBTS_FEATURE_ID_cv44; break; + case KBTS_FEATURE_TAG_cv45: Result = KBTS_FEATURE_ID_cv45; break; + case KBTS_FEATURE_TAG_cv46: Result = KBTS_FEATURE_ID_cv46; break; + case KBTS_FEATURE_TAG_cv47: Result = KBTS_FEATURE_ID_cv47; break; + case KBTS_FEATURE_TAG_cv48: Result = KBTS_FEATURE_ID_cv48; break; + case KBTS_FEATURE_TAG_cv49: Result = KBTS_FEATURE_ID_cv49; break; + case KBTS_FEATURE_TAG_cv50: Result = KBTS_FEATURE_ID_cv50; break; + case KBTS_FEATURE_TAG_cv51: Result = KBTS_FEATURE_ID_cv51; break; + case KBTS_FEATURE_TAG_cv52: Result = KBTS_FEATURE_ID_cv52; break; + case KBTS_FEATURE_TAG_cv53: Result = KBTS_FEATURE_ID_cv53; break; + case KBTS_FEATURE_TAG_cv54: Result = KBTS_FEATURE_ID_cv54; break; + case KBTS_FEATURE_TAG_cv55: Result = KBTS_FEATURE_ID_cv55; break; + case KBTS_FEATURE_TAG_cv56: Result = KBTS_FEATURE_ID_cv56; break; + case KBTS_FEATURE_TAG_cv57: Result = KBTS_FEATURE_ID_cv57; break; + case KBTS_FEATURE_TAG_cv58: Result = KBTS_FEATURE_ID_cv58; break; + case KBTS_FEATURE_TAG_cv59: Result = KBTS_FEATURE_ID_cv59; break; + case KBTS_FEATURE_TAG_cv60: Result = KBTS_FEATURE_ID_cv60; break; + case KBTS_FEATURE_TAG_cv61: Result = KBTS_FEATURE_ID_cv61; break; + case KBTS_FEATURE_TAG_cv62: Result = KBTS_FEATURE_ID_cv62; break; + case KBTS_FEATURE_TAG_cv63: Result = KBTS_FEATURE_ID_cv63; break; + case KBTS_FEATURE_TAG_cv64: Result = KBTS_FEATURE_ID_cv64; break; + case KBTS_FEATURE_TAG_cv65: Result = KBTS_FEATURE_ID_cv65; break; + case KBTS_FEATURE_TAG_cv66: Result = KBTS_FEATURE_ID_cv66; break; + case KBTS_FEATURE_TAG_cv67: Result = KBTS_FEATURE_ID_cv67; break; + case KBTS_FEATURE_TAG_cv68: Result = KBTS_FEATURE_ID_cv68; break; + case KBTS_FEATURE_TAG_cv69: Result = KBTS_FEATURE_ID_cv69; break; + case KBTS_FEATURE_TAG_cv70: Result = KBTS_FEATURE_ID_cv70; break; + case KBTS_FEATURE_TAG_cv71: Result = KBTS_FEATURE_ID_cv71; break; + case KBTS_FEATURE_TAG_cv72: Result = KBTS_FEATURE_ID_cv72; break; + case KBTS_FEATURE_TAG_cv73: Result = KBTS_FEATURE_ID_cv73; break; + case KBTS_FEATURE_TAG_cv74: Result = KBTS_FEATURE_ID_cv74; break; + case KBTS_FEATURE_TAG_cv75: Result = KBTS_FEATURE_ID_cv75; break; + case KBTS_FEATURE_TAG_cv76: Result = KBTS_FEATURE_ID_cv76; break; + case KBTS_FEATURE_TAG_cv77: Result = KBTS_FEATURE_ID_cv77; break; + case KBTS_FEATURE_TAG_cv78: Result = KBTS_FEATURE_ID_cv78; break; + case KBTS_FEATURE_TAG_cv79: Result = KBTS_FEATURE_ID_cv79; break; + case KBTS_FEATURE_TAG_cv80: Result = KBTS_FEATURE_ID_cv80; break; + case KBTS_FEATURE_TAG_cv81: Result = KBTS_FEATURE_ID_cv81; break; + case KBTS_FEATURE_TAG_cv82: Result = KBTS_FEATURE_ID_cv82; break; + case KBTS_FEATURE_TAG_cv83: Result = KBTS_FEATURE_ID_cv83; break; + case KBTS_FEATURE_TAG_cv84: Result = KBTS_FEATURE_ID_cv84; break; + case KBTS_FEATURE_TAG_cv85: Result = KBTS_FEATURE_ID_cv85; break; + case KBTS_FEATURE_TAG_cv86: Result = KBTS_FEATURE_ID_cv86; break; + case KBTS_FEATURE_TAG_cv87: Result = KBTS_FEATURE_ID_cv87; break; + case KBTS_FEATURE_TAG_cv88: Result = KBTS_FEATURE_ID_cv88; break; + case KBTS_FEATURE_TAG_cv89: Result = KBTS_FEATURE_ID_cv89; break; + case KBTS_FEATURE_TAG_cv90: Result = KBTS_FEATURE_ID_cv90; break; + case KBTS_FEATURE_TAG_cv91: Result = KBTS_FEATURE_ID_cv91; break; + case KBTS_FEATURE_TAG_cv92: Result = KBTS_FEATURE_ID_cv92; break; + case KBTS_FEATURE_TAG_cv93: Result = KBTS_FEATURE_ID_cv93; break; + case KBTS_FEATURE_TAG_cv94: Result = KBTS_FEATURE_ID_cv94; break; + case KBTS_FEATURE_TAG_cv95: Result = KBTS_FEATURE_ID_cv95; break; + case KBTS_FEATURE_TAG_cv96: Result = KBTS_FEATURE_ID_cv96; break; + case KBTS_FEATURE_TAG_cv97: Result = KBTS_FEATURE_ID_cv97; break; + case KBTS_FEATURE_TAG_cv98: Result = KBTS_FEATURE_ID_cv98; break; + case KBTS_FEATURE_TAG_cv99: Result = KBTS_FEATURE_ID_cv99; break; + case KBTS_FEATURE_TAG_c2pc: Result = KBTS_FEATURE_ID_c2pc; break; + case KBTS_FEATURE_TAG_c2sc: Result = KBTS_FEATURE_ID_c2sc; break; + case KBTS_FEATURE_TAG_dist: Result = KBTS_FEATURE_ID_dist; break; + case KBTS_FEATURE_TAG_dlig: Result = KBTS_FEATURE_ID_dlig; break; + case KBTS_FEATURE_TAG_dtls: Result = KBTS_FEATURE_ID_dtls; break; + case KBTS_FEATURE_TAG_expt: Result = KBTS_FEATURE_ID_expt; break; + case KBTS_FEATURE_TAG_falt: Result = KBTS_FEATURE_ID_falt; break; + case KBTS_FEATURE_TAG_flac: Result = KBTS_FEATURE_ID_flac; break; + case KBTS_FEATURE_TAG_fwid: Result = KBTS_FEATURE_ID_fwid; break; + case KBTS_FEATURE_TAG_haln: Result = KBTS_FEATURE_ID_haln; break; + case KBTS_FEATURE_TAG_halt: Result = KBTS_FEATURE_ID_halt; break; + case KBTS_FEATURE_TAG_hist: Result = KBTS_FEATURE_ID_hist; break; + case KBTS_FEATURE_TAG_hkna: Result = KBTS_FEATURE_ID_hkna; break; + case KBTS_FEATURE_TAG_hlig: Result = KBTS_FEATURE_ID_hlig; break; + case KBTS_FEATURE_TAG_hngl: Result = KBTS_FEATURE_ID_hngl; break; + case KBTS_FEATURE_TAG_hojo: Result = KBTS_FEATURE_ID_hojo; break; + case KBTS_FEATURE_TAG_hwid: Result = KBTS_FEATURE_ID_hwid; break; + case KBTS_FEATURE_TAG_ital: Result = KBTS_FEATURE_ID_ital; break; + case KBTS_FEATURE_TAG_jalt: Result = KBTS_FEATURE_ID_jalt; break; + case KBTS_FEATURE_TAG_jp78: Result = KBTS_FEATURE_ID_jp78; break; + case KBTS_FEATURE_TAG_jp83: Result = KBTS_FEATURE_ID_jp83; break; + case KBTS_FEATURE_TAG_jp90: Result = KBTS_FEATURE_ID_jp90; break; + case KBTS_FEATURE_TAG_jp04: Result = KBTS_FEATURE_ID_jp04; break; + case KBTS_FEATURE_TAG_kern: Result = KBTS_FEATURE_ID_kern; break; + case KBTS_FEATURE_TAG_lfbd: Result = KBTS_FEATURE_ID_lfbd; break; + case KBTS_FEATURE_TAG_liga: Result = KBTS_FEATURE_ID_liga; break; + case KBTS_FEATURE_TAG_lnum: Result = KBTS_FEATURE_ID_lnum; break; + case KBTS_FEATURE_TAG_locl: Result = KBTS_FEATURE_ID_locl; break; + case KBTS_FEATURE_TAG_ltra: Result = KBTS_FEATURE_ID_ltra; break; + case KBTS_FEATURE_TAG_ltrm: Result = KBTS_FEATURE_ID_ltrm; break; + case KBTS_FEATURE_TAG_mark: Result = KBTS_FEATURE_ID_mark; break; + case KBTS_FEATURE_TAG_mgrk: Result = KBTS_FEATURE_ID_mgrk; break; + case KBTS_FEATURE_TAG_mkmk: Result = KBTS_FEATURE_ID_mkmk; break; + case KBTS_FEATURE_TAG_mset: Result = KBTS_FEATURE_ID_mset; break; + case KBTS_FEATURE_TAG_nalt: Result = KBTS_FEATURE_ID_nalt; break; + case KBTS_FEATURE_TAG_nlck: Result = KBTS_FEATURE_ID_nlck; break; + case KBTS_FEATURE_TAG_nukt: Result = KBTS_FEATURE_ID_nukt; break; + case KBTS_FEATURE_TAG_onum: Result = KBTS_FEATURE_ID_onum; break; + case KBTS_FEATURE_TAG_opbd: Result = KBTS_FEATURE_ID_opbd; break; + case KBTS_FEATURE_TAG_ordn: Result = KBTS_FEATURE_ID_ordn; break; + case KBTS_FEATURE_TAG_ornm: Result = KBTS_FEATURE_ID_ornm; break; + case KBTS_FEATURE_TAG_palt: Result = KBTS_FEATURE_ID_palt; break; + case KBTS_FEATURE_TAG_pcap: Result = KBTS_FEATURE_ID_pcap; break; + case KBTS_FEATURE_TAG_pkna: Result = KBTS_FEATURE_ID_pkna; break; + case KBTS_FEATURE_TAG_pnum: Result = KBTS_FEATURE_ID_pnum; break; + case KBTS_FEATURE_TAG_pres: Result = KBTS_FEATURE_ID_pres; break; + case KBTS_FEATURE_TAG_psts: Result = KBTS_FEATURE_ID_psts; break; + case KBTS_FEATURE_TAG_pwid: Result = KBTS_FEATURE_ID_pwid; break; + case KBTS_FEATURE_TAG_qwid: Result = KBTS_FEATURE_ID_qwid; break; + case KBTS_FEATURE_TAG_rand: Result = KBTS_FEATURE_ID_rand; break; + case KBTS_FEATURE_TAG_rclt: Result = KBTS_FEATURE_ID_rclt; break; + case KBTS_FEATURE_TAG_rkrf: Result = KBTS_FEATURE_ID_rkrf; break; + case KBTS_FEATURE_TAG_rlig: Result = KBTS_FEATURE_ID_rlig; break; + case KBTS_FEATURE_TAG_rtbd: Result = KBTS_FEATURE_ID_rtbd; break; + case KBTS_FEATURE_TAG_rtla: Result = KBTS_FEATURE_ID_rtla; break; + case KBTS_FEATURE_TAG_rtlm: Result = KBTS_FEATURE_ID_rtlm; break; + case KBTS_FEATURE_TAG_ruby: Result = KBTS_FEATURE_ID_ruby; break; + case KBTS_FEATURE_TAG_rvrn: Result = KBTS_FEATURE_ID_rvrn; break; + case KBTS_FEATURE_TAG_salt: Result = KBTS_FEATURE_ID_salt; break; + case KBTS_FEATURE_TAG_sinf: Result = KBTS_FEATURE_ID_sinf; break; + case KBTS_FEATURE_TAG_size: Result = KBTS_FEATURE_ID_size; break; + case KBTS_FEATURE_TAG_smcp: Result = KBTS_FEATURE_ID_smcp; break; + case KBTS_FEATURE_TAG_smpl: Result = KBTS_FEATURE_ID_smpl; break; + case KBTS_FEATURE_TAG_ss01: Result = KBTS_FEATURE_ID_ss01; break; + case KBTS_FEATURE_TAG_ss02: Result = KBTS_FEATURE_ID_ss02; break; + case KBTS_FEATURE_TAG_ss03: Result = KBTS_FEATURE_ID_ss03; break; + case KBTS_FEATURE_TAG_ss04: Result = KBTS_FEATURE_ID_ss04; break; + case KBTS_FEATURE_TAG_ss05: Result = KBTS_FEATURE_ID_ss05; break; + case KBTS_FEATURE_TAG_ss06: Result = KBTS_FEATURE_ID_ss06; break; + case KBTS_FEATURE_TAG_ss07: Result = KBTS_FEATURE_ID_ss07; break; + case KBTS_FEATURE_TAG_ss08: Result = KBTS_FEATURE_ID_ss08; break; + case KBTS_FEATURE_TAG_ss09: Result = KBTS_FEATURE_ID_ss09; break; + case KBTS_FEATURE_TAG_ss10: Result = KBTS_FEATURE_ID_ss10; break; + case KBTS_FEATURE_TAG_ss11: Result = KBTS_FEATURE_ID_ss11; break; + case KBTS_FEATURE_TAG_ss12: Result = KBTS_FEATURE_ID_ss12; break; + case KBTS_FEATURE_TAG_ss13: Result = KBTS_FEATURE_ID_ss13; break; + case KBTS_FEATURE_TAG_ss14: Result = KBTS_FEATURE_ID_ss14; break; + case KBTS_FEATURE_TAG_ss15: Result = KBTS_FEATURE_ID_ss15; break; + case KBTS_FEATURE_TAG_ss16: Result = KBTS_FEATURE_ID_ss16; break; + case KBTS_FEATURE_TAG_ss17: Result = KBTS_FEATURE_ID_ss17; break; + case KBTS_FEATURE_TAG_ss18: Result = KBTS_FEATURE_ID_ss18; break; + case KBTS_FEATURE_TAG_ss19: Result = KBTS_FEATURE_ID_ss19; break; + case KBTS_FEATURE_TAG_ss20: Result = KBTS_FEATURE_ID_ss20; break; + case KBTS_FEATURE_TAG_ssty: Result = KBTS_FEATURE_ID_ssty; break; + case KBTS_FEATURE_TAG_stch: Result = KBTS_FEATURE_ID_stch; break; + case KBTS_FEATURE_TAG_subs: Result = KBTS_FEATURE_ID_subs; break; + case KBTS_FEATURE_TAG_sups: Result = KBTS_FEATURE_ID_sups; break; + case KBTS_FEATURE_TAG_swsh: Result = KBTS_FEATURE_ID_swsh; break; + case KBTS_FEATURE_TAG_test: Result = KBTS_FEATURE_ID_test; break; + case KBTS_FEATURE_TAG_titl: Result = KBTS_FEATURE_ID_titl; break; + case KBTS_FEATURE_TAG_tnam: Result = KBTS_FEATURE_ID_tnam; break; + case KBTS_FEATURE_TAG_tnum: Result = KBTS_FEATURE_ID_tnum; break; + case KBTS_FEATURE_TAG_trad: Result = KBTS_FEATURE_ID_trad; break; + case KBTS_FEATURE_TAG_twid: Result = KBTS_FEATURE_ID_twid; break; + case KBTS_FEATURE_TAG_unic: Result = KBTS_FEATURE_ID_unic; break; + case KBTS_FEATURE_TAG_valt: Result = KBTS_FEATURE_ID_valt; break; + case KBTS_FEATURE_TAG_vapk: Result = KBTS_FEATURE_ID_vapk; break; + case KBTS_FEATURE_TAG_vatu: Result = KBTS_FEATURE_ID_vatu; break; + case KBTS_FEATURE_TAG_vchw: Result = KBTS_FEATURE_ID_vchw; break; + case KBTS_FEATURE_TAG_vert: Result = KBTS_FEATURE_ID_vert; break; + case KBTS_FEATURE_TAG_vhal: Result = KBTS_FEATURE_ID_vhal; break; + case KBTS_FEATURE_TAG_vkna: Result = KBTS_FEATURE_ID_vkna; break; + case KBTS_FEATURE_TAG_vkrn: Result = KBTS_FEATURE_ID_vkrn; break; + case KBTS_FEATURE_TAG_vpal: Result = KBTS_FEATURE_ID_vpal; break; + case KBTS_FEATURE_TAG_vrt2: Result = KBTS_FEATURE_ID_vrt2; break; + case KBTS_FEATURE_TAG_vrtr: Result = KBTS_FEATURE_ID_vrtr; break; + case KBTS_FEATURE_TAG_zero: Result = KBTS_FEATURE_ID_zero; break; + default: break; + } + return Result; +} + static kbts_s32 kbts_UnicodeParentDeltas[1679] = { 132,133,134,135,244,246,248,250,252,254,315,351,416,418,7678,7680,7682,7792,7794,132,133,134,135,275,277,279,281,283,285,346,382,447, 449,7709,7711,7713,7823,7825,131,132,133,134,174,176,178,180,182,416,418,452,7604,7606,7764,7766,7768,131,132,133,134,205,207,209,211,213, @@ -11791,7 +12941,7 @@ static kbts_indic_script_properties kbts_IndicScriptProperties(kbts_u32 Script) return Result; } -static void kbts_ByteSwapArray16(kbts_u16 *Array, kbts_un Count) +static void kbts_ByteSwapArray16Unchecked(kbts_u16 *Array, kbts_un Count) { KBTS_FOR(It, 0, Count) { @@ -11799,7 +12949,18 @@ static void kbts_ByteSwapArray16(kbts_u16 *Array, kbts_un Count) } } -static void kbts_ByteSwapArray32(kbts_u32 *Array, kbts_un Count) +static int kbts_ByteSwapArray16(kbts_u16 *Array, kbts_un Count, char *End) +{ + int Result = 0; + if((char *)(Array + Count) <= End) + { + kbts_ByteSwapArray16Unchecked(Array, Count); + Result = 1; + } + return Result; +} + +static void kbts_ByteSwapArray32Unchecked(kbts_u32 *Array, kbts_un Count) { KBTS_FOR(It, 0, Count) { @@ -11807,6 +12968,17 @@ static void kbts_ByteSwapArray32(kbts_u32 *Array, kbts_un Count) } } +static int kbts_ByteSwapArray32(kbts_u32 *Array, kbts_un Count, char *End) +{ + int Result = 0; + if((char *)(Array + Count) <= End) + { + kbts_ByteSwapArray32Unchecked(Array, Count); + Result = 1; + } + return Result; +} + static kbts_u64 kbts_ContainsFeature(kbts_feature_set *Set, kbts_feature_id Id) { kbts_un WordIndex = Id / 64; @@ -11824,6 +12996,97 @@ static void kbts_AddFeature(kbts_feature_set *Set, kbts_feature_id Id) Set->Flags[WordIndex] |= 1ull << BitIndex; } +static kbts_feature_override kbts_FeatureOverrideBase(kbts_feature_id Id, kbts_feature_tag Tag, int Alternate, kbts_u32 Value) +{ + kbts_feature_override Result = KBTS_ZERO; + Result.Id = Id; + Result.Tag = Tag; + + if(Alternate) + { + Result.EnabledOrAlternatePlusOne = Value + 1; + } + else + { + Result.EnabledOrAlternatePlusOne = Value; + } + + return Result; +} +KBTS_EXPORT kbts_feature_override kbts_FeatureOverrideFromTag(kbts_feature_tag Tag, int Alternate, kbts_u32 Value) +{ + kbts_feature_override Result = kbts_FeatureOverrideBase(kbts_FeatureTagToId(Tag), Tag, Alternate, Value); + return Result; +} +KBTS_EXPORT kbts_feature_override kbts_FeatureOverride(kbts_feature_id Id, int Alternate, kbts_u32 Value) +{ + kbts_feature_override Result = kbts_FeatureOverrideBase(Id, 0, Alternate, Value); + return Result; +} + +KBTS_EXPORT kbts_glyph_config kbts_GlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCount) +{ + kbts_glyph_config Result = KBTS_ZERO; + Result.FeatureOverrides = FeatureOverrides; + Result.FeatureOverrideCount = FeatureOverrideCount; + + KBTS_FOR(FeatureOverrideIndex, 0, FeatureOverrideCount) + { + kbts_feature_override *Override = &FeatureOverrides[FeatureOverrideIndex]; + if(Override->EnabledOrAlternatePlusOne) + { + kbts_AddFeature(&Result.EnabledFeatures, FeatureOverrides[FeatureOverrideIndex].Id); + } + else + { + kbts_AddFeature(&Result.DisabledFeatures, FeatureOverrides[FeatureOverrideIndex].Id); + } + } + + return Result; +} + +KBTS_EXPORT kbts_glyph_config kbts_EmptyGlyphConfig(kbts_feature_override *FeatureOverrides, kbts_u32 FeatureOverrideCapacity) +{ + kbts_glyph_config Result = KBTS_ZERO; + Result.FeatureOverrides = FeatureOverrides; + Result.FeatureOverrideCapacity = FeatureOverrideCapacity; + return Result; +} + +static int kbts_GlyphConfigOverrideFeatureBase(kbts_glyph_config *Config, kbts_feature_id Id, kbts_feature_tag Tag, int Alternate, kbts_u32 Value) +{ + kbts_feature_set *Set = &Config->EnabledFeatures; + if(!Value) + { + Set = &Config->DisabledFeatures; + } + kbts_AddFeature(Set, Id); + + if(!Id || Alternate) + { + if(Config->FeatureOverrideCount < Config->FeatureOverrideCapacity) + { + kbts_feature_override Override = kbts_FeatureOverrideFromTag(Tag, Alternate, Value); + Config->FeatureOverrides[Config->FeatureOverrideCount++] = Override; + } + Config->RequiredFeatureOverrideCapacity += 1; + } + + int Result = Config->RequiredFeatureOverrideCapacity <= Config->FeatureOverrideCapacity; + return Result; +} +KBTS_EXPORT int kbts_GlyphConfigOverrideFeature(kbts_glyph_config *Config, kbts_feature_id Id, int Alternate, kbts_u32 Value) +{ + int Result = kbts_GlyphConfigOverrideFeatureBase(Config, Id, 0, Alternate, Value); + return Result; +} +KBTS_EXPORT int kbts_GlyphConfigOverrideFeatureFromTag(kbts_glyph_config *Config, kbts_feature_tag Tag, int Alternate, kbts_u32 Value) +{ + int Result = kbts_GlyphConfigOverrideFeatureBase(Config, kbts_FeatureTagToId(Tag), Tag, Alternate, Value); + return Result; +} + // // TTF struct definitions. // @@ -13015,13 +14278,6 @@ static kbts_class_sequence_rule *kbts_GetClassSequenceRule(kbts_class_sequence_r return Result; } -static kbts_coverage *kbts_GetCoverage(kbts_sequence_context_3 *Context, kbts_un Index) -{ - kbts_u16 *Offsets = (kbts_u16 *)(Context + 1); - kbts_coverage *Result = KBTS_POINTER_OFFSET(kbts_coverage, Context, Offsets[Index]); - return Result; -} - static kbts_chained_sequence_rule_set *kbts_GetChainedSequenceRuleSet(kbts_chained_sequence_context_1 *Context, kbts_un Index) { kbts_u16 *Offsets = (kbts_u16 *)(Context + 1); @@ -13051,6 +14307,7 @@ static kbts_chained_sequence_rule *kbts_GetChainedClassSequenceRule(kbts_chained return Result; } +#if 0 // @Incomplete static kbts_feature_variation_pointer kbts_GetFeatureVariation(kbts_feature_variations *Variations, kbts_un Index) { kbts_feature_variation_record *Records = (kbts_feature_variation_record *)(Variations + 1); @@ -13079,6 +14336,7 @@ static kbts_feature_substitution_pointer kbts_GetFeatureSubstitution(kbts_featur Result.AlternateFeature = KBTS_POINTER_OFFSET(kbts_feature, Table, Record->AlternateFeatureOffset); return Result; } +#endif static kbts_cmap_subtable_pointer kbts_GetCmapSubtable(kbts_cmap *Cmap, kbts_un Index) { @@ -13174,11 +14432,28 @@ static kbts_mark_info kbts_GetMarkInfo(void *Subtable, kbts_un SubtableOffsetToM typedef struct kbts_byteswap_context { char *FileBase; + char *FileEnd; kbts_u32 *Pointers; kbts_un PointerCapacity; kbts_un PointerCount; + + int Error; } kbts_byteswap_context; +static int kbts_ByteSwapArray16Context(kbts_u16 *Array, kbts_un Count, kbts_byteswap_context *Context) +{ + int Result = kbts_ByteSwapArray16(Array, Count, Context->FileEnd); + Context->Error |= !Result; + return Result; +} + +static int kbts_ByteSwapArray32Context(kbts_u32 *Array, kbts_un Count, kbts_byteswap_context *Context) +{ + int Result = kbts_ByteSwapArray32(Array, Count, Context->FileEnd); + Context->Error |= !Result; + return Result; +} + typedef struct kbts_cover_glyph_result { kbts_u32 Valid; @@ -13244,7 +14519,7 @@ static int kbts_PushLookup(kbts_gdef *Gdef, kbts_lookup_info_frame *Frames, kbts static int kbts_AlreadyVisited(kbts_byteswap_context *Context, void *Pointer) { - int Result = !Pointer; + int Result = !Pointer || Context->Error; if(!Result) { @@ -13255,13 +14530,16 @@ static int kbts_AlreadyVisited(kbts_byteswap_context *Context, void *Pointer) if(Context->PointerCount) { kbts_un PointerCount = Context->PointerCount; - while(PointerCount > 1) + if(PointerCount) { - kbts_un HalfCount = PointerCount / 2; - Index = (Pointers[Index + HalfCount - 1] < Pointer32) ? (Index + HalfCount) : Index; - PointerCount -= HalfCount; + while(PointerCount > 1) + { + kbts_un HalfCount = PointerCount / 2; + Index = (Pointers[Index + HalfCount - 1] < Pointer32) ? (Index + HalfCount) : Index; + PointerCount -= HalfCount; + } + Result = (Pointer32 == Pointers[Index]); } - Result = (Pointer32 == Pointers[Index]); } if(!Result && (Context->PointerCount < Context->PointerCapacity)) @@ -13285,8 +14563,8 @@ static void kbts_ByteSwapFeature(kbts_byteswap_context *Context, kbts_feature *F if(!kbts_AlreadyVisited(Context, Feature)) { kbts_u16 *LookupIndices = KBTS_POINTER_AFTER(kbts_u16, Feature); - kbts_ByteSwapArray16(&Feature->FeatureParamsOffset, 2); - kbts_ByteSwapArray16(LookupIndices, Feature->LookupIndexCount); + Context->Error |= !kbts_ByteSwapArray16(&Feature->FeatureParamsOffset, 2, Context->FileEnd); + Context->Error |= !kbts_ByteSwapArray16(LookupIndices, Feature->LookupIndexCount, Context->FileEnd); // We require lookup indices to be sorted per feature for the lookup application order to match Harfbuzz. // Lookup indices are _typically_ sorted per feature, but we can't assume it is always the case. @@ -13318,7 +14596,7 @@ static void kbts_ByteSwapFeature(kbts_byteswap_context *Context, kbts_feature *F static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsub_gpos *Header) { - kbts_ByteSwapArray16(&Header->Major, 5); + kbts_ByteSwapArray16Context(&Header->Major, 5, Context); if(Header->Minor == 1) { @@ -13349,8 +14627,8 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu if(DefaultLangsys && !kbts_AlreadyVisited(Context, DefaultLangsys)) { - kbts_ByteSwapArray16(&DefaultLangsys->LookupOrderOffset, 3); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, DefaultLangsys), DefaultLangsys->FeatureIndexCount); + kbts_ByteSwapArray16Context(&DefaultLangsys->LookupOrderOffset, 3, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, DefaultLangsys), DefaultLangsys->FeatureIndexCount, Context); } kbts_langsys_record *LangsysRecords = KBTS_POINTER_AFTER(kbts_langsys_record, Script); @@ -13363,8 +14641,8 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu kbts_langsys *Langsys = KBTS_POINTER_OFFSET(kbts_langsys, Script, LangsysRecord->Offset); if(!kbts_AlreadyVisited(Context, Langsys)) { - kbts_ByteSwapArray16(&Langsys->LookupOrderOffset, 3); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Langsys), Langsys->FeatureIndexCount); + kbts_ByteSwapArray16Context(&Langsys->LookupOrderOffset, 3, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Langsys), Langsys->FeatureIndexCount, Context); } } @@ -13405,7 +14683,7 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu if(!kbts_AlreadyVisited(Context, FeatureVariations)) { - kbts_ByteSwapArray16(&FeatureVariations->Major, 2); + kbts_ByteSwapArray16Context(&FeatureVariations->Major, 2, Context); FeatureVariations->RecordCount = kbts_ByteSwap32(FeatureVariations->RecordCount); kbts_feature_variation_record *Records = KBTS_POINTER_AFTER(kbts_feature_variation_record, FeatureVariations); @@ -13413,7 +14691,7 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu { kbts_feature_variation_record *Record = &Records[VariationIndex]; - kbts_ByteSwapArray32(&Record->ConditionSetOffset, 2); + kbts_ByteSwapArray32Context(&Record->ConditionSetOffset, 2, Context); kbts_condition_set *Set = KBTS_POINTER_OFFSET(kbts_condition_set, FeatureVariations, Record->ConditionSetOffset); @@ -13422,7 +14700,7 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu Set->Count = kbts_ByteSwap16(Set->Count); kbts_u32 *ConditionOffsets = KBTS_POINTER_AFTER(kbts_u32, Set); - kbts_ByteSwapArray32(ConditionOffsets, Set->Count); + kbts_ByteSwapArray32Context(ConditionOffsets, Set->Count, Context); KBTS_FOR(ConditionIndex, 0, Set->Count) { @@ -13430,7 +14708,7 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu if(!kbts_AlreadyVisited(Context, Condition)) { - kbts_ByteSwapArray16(&Condition->Format, 4); + kbts_ByteSwapArray16Context(&Condition->Format, 4, Context); } } @@ -13438,7 +14716,7 @@ static void kbts_ByteSwapGsubGposCommon(kbts_byteswap_context *Context, kbts_gsu if(!kbts_AlreadyVisited(Context, FeatureSubst)) { - kbts_ByteSwapArray16(&FeatureSubst->Major, 3); + kbts_ByteSwapArray16Context(&FeatureSubst->Major, 3, Context); kbts_feature_table_substitution_record *SubstRecords = KBTS_POINTER_AFTER(kbts_feature_table_substitution_record, FeatureSubst); KBTS_FOR(SubstRecordIndex, 0, FeatureSubst->Count) @@ -13486,7 +14764,7 @@ static int kbts_ByteSwapLookup(kbts_byteswap_context *Context, kbts_lookup *Look { Result = 1; - kbts_ByteSwapArray16(&Lookup->Type, 3); + kbts_ByteSwapArray16Context(&Lookup->Type, 3, Context); kbts_u16 *SubtableOffsets = KBTS_POINTER_AFTER(kbts_u16, Lookup); kbts_un U16Count = Lookup->SubtableCount; @@ -13494,7 +14772,7 @@ static int kbts_ByteSwapLookup(kbts_byteswap_context *Context, kbts_lookup *Look { U16Count += 1; } - kbts_ByteSwapArray16(SubtableOffsets, U16Count); + kbts_ByteSwapArray16Context(SubtableOffsets, U16Count, Context); } return Result; @@ -13504,7 +14782,7 @@ static void kbts_ByteSwapCoverage(kbts_byteswap_context *Context, kbts_coverage { if(!kbts_AlreadyVisited(Context, Coverage)) { - kbts_ByteSwapArray16(&Coverage->Format, 2); + kbts_ByteSwapArray16Context(&Coverage->Format, 2, Context); kbts_un U16Count = 0; if(Coverage->Format == 1) @@ -13516,7 +14794,7 @@ static void kbts_ByteSwapCoverage(kbts_byteswap_context *Context, kbts_coverage U16Count = Coverage->Count * 3; } - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Coverage), U16Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Coverage), U16Count, Context); } } @@ -13536,7 +14814,7 @@ static void kbts_ByteSwapAnchor(kbts_byteswap_context *Context, kbts_anchor *Anc U16Count = 4; } - kbts_ByteSwapArray16((kbts_u16 *)&Anchor->X, U16Count); + kbts_ByteSwapArray16Context((kbts_u16 *)&Anchor->X, U16Count, Context); } } @@ -13547,7 +14825,7 @@ static void kbts_ByteSwapBaseArray(kbts_byteswap_context *Context, kbts_u16 Mark Array->BaseCount = kbts_ByteSwap16(Array->BaseCount); kbts_u16 *BaseAnchorOffsets = KBTS_POINTER_AFTER(kbts_u16, Array); - kbts_ByteSwapArray16(BaseAnchorOffsets, Array->BaseCount * MarkClassCount); + kbts_ByteSwapArray16Context(BaseAnchorOffsets, Array->BaseCount * MarkClassCount, Context); KBTS_FOR(OffsetIndex, 0, (kbts_un)Array->BaseCount * MarkClassCount) { @@ -13565,7 +14843,7 @@ static void kbts_ByteSwapDevice(kbts_byteswap_context *Context, kbts_device *Dev { if(!kbts_AlreadyVisited(Context, Device)) { - kbts_ByteSwapArray16(&Device->U.Device.StartSize, 3); + kbts_ByteSwapArray16Context(&Device->U.Device.StartSize, 3, Context); if(Device->DeltaFormat <= 3) { @@ -13582,7 +14860,7 @@ static kbts_unpacked_value_record kbts_ByteSwapValueRecord(kbts_byteswap_context { kbts_un U16Count = kbts_PopCount32(ValueFormat); - kbts_ByteSwapArray16(Record, U16Count); + kbts_ByteSwapArray16Context(Record, U16Count, Context); Result = kbts_UnpackValueRecord(Parent, ValueFormat, Record); @@ -13600,7 +14878,7 @@ static void kbts_ByteSwapMarkArray(kbts_byteswap_context *Context, kbts_mark_arr if(!kbts_AlreadyVisited(Context, MarkArray)) { MarkArray->Count = kbts_ByteSwap16(MarkArray->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, MarkArray), MarkArray->Count * 2); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, MarkArray), MarkArray->Count * 2, Context); kbts_mark_record *MarkRecords = KBTS_POINTER_AFTER(kbts_mark_record, MarkArray); KBTS_FOR(MarkRecordIndex, 0, MarkArray->Count) @@ -13617,7 +14895,7 @@ static void kbts_ByteSwapChainedSequenceRuleSet(kbts_byteswap_context *Context, if(Set && !kbts_AlreadyVisited(Context, Set)) { Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); KBTS_FOR(RuleIndex, 0, Set->Count) { @@ -13628,7 +14906,7 @@ static void kbts_ByteSwapChainedSequenceRuleSet(kbts_byteswap_context *Context, kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 1); kbts_un U16Count = Unpacked.BacktrackCount + Unpacked.InputCount + Unpacked.LookaheadCount + Unpacked.RecordCount * 2 + 3; - kbts_ByteSwapArray16(&Rule->BacktrackGlyphCount, U16Count); + kbts_ByteSwapArray16Context(&Rule->BacktrackGlyphCount, U16Count, Context); } } } @@ -13669,22 +14947,28 @@ static void kbts_ByteSwapClassDefinition(kbts_byteswap_context *Context, kbts_u1 if(*Base == 1) { kbts_class_definition_1 *ClassDef = (kbts_class_definition_1 *)Base; - kbts_ByteSwapArray16(&ClassDef->StartGlyphId, 2); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, ClassDef), ClassDef->GlyphCount); + kbts_ByteSwapArray16Context(&ClassDef->StartGlyphId, 2, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, ClassDef), ClassDef->GlyphCount, Context); } else if(*Base == 2) { kbts_class_definition_2 *ClassDef = (kbts_class_definition_2 *)Base; ClassDef->Count = kbts_ByteSwap16(ClassDef->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, ClassDef), ClassDef->Count * 3); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, ClassDef), ClassDef->Count * 3, Context); } } } -static kbts_u16 kbts_GlyphClassFromTable(kbts_u16 *ClassDefinitionBase, kbts_un Id) +typedef struct kbts_glyph_class_from_table_result { - kbts_u16 Result = 0; + int Found; + kbts_u16 Class; +} kbts_glyph_class_from_table_result; + +static kbts_glyph_class_from_table_result kbts_GlyphClassFromTable(kbts_u16 *ClassDefinitionBase, kbts_un Id) +{ + kbts_glyph_class_from_table_result Result = KBTS_ZERO; // From the Microsoft docs: // There is one offset to a ChainedClassSequenceRuleSet subtable for each class defined in the input sequence @@ -13704,7 +14988,8 @@ static kbts_u16 kbts_GlyphClassFromTable(kbts_u16 *ClassDefinitionBase, kbts_un kbts_un Offset = Id - ClassDef->StartGlyphId; if(Offset < ClassDef->GlyphCount) { - Result = GlyphClasses[Offset]; + Result.Class = GlyphClasses[Offset]; + Result.Found = 1; } } else if(*ClassDefinitionBase == 2) @@ -13713,15 +14998,19 @@ static kbts_u16 kbts_GlyphClassFromTable(kbts_u16 *ClassDefinitionBase, kbts_un kbts_class_range_record *Ranges = KBTS_POINTER_AFTER(kbts_class_range_record, ClassDef); kbts_un RangeCount = ClassDef->Count; - while(RangeCount > 1) + if(RangeCount) { - kbts_un HalfCount = RangeCount / 2; - Ranges = (Ranges[HalfCount - 1].EndGlyphId < Id) ? (Ranges + HalfCount) : Ranges; - RangeCount -= HalfCount; - } - if((Id >= Ranges->StartGlyphId) && (Id <= Ranges->EndGlyphId)) - { - Result = Ranges->Class; + while(RangeCount > 1) + { + kbts_un HalfCount = RangeCount / 2; + Ranges = (Ranges[HalfCount - 1].EndGlyphId < Id) ? (Ranges + HalfCount) : Ranges; + RangeCount -= HalfCount; + } + if((Id >= Ranges->StartGlyphId) && (Id <= Ranges->EndGlyphId)) + { + Result.Class = Ranges->Class; + Result.Found = 1; + } } } @@ -13734,37 +15023,40 @@ static kbts_cover_glyph_result kbts_CoverGlyph(kbts_coverage *Coverage, kbts_u32 kbts_cover_glyph_result Result = KBTS_ZERO; kbts_un Count = Coverage->Count; - if(Coverage->Format == 1) + if(Count) { - kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Coverage); + if(Coverage->Format == 1) + { + kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Coverage); - while(Count > 1) - { - kbts_un HalfCount = Count / 2; - GlyphIds = (GlyphIds[HalfCount - 1] < GlyphId) ? (GlyphIds + HalfCount) : GlyphIds; - Count -= HalfCount; - } + while(Count > 1) + { + kbts_un HalfCount = Count / 2; + GlyphIds = (GlyphIds[HalfCount - 1] < GlyphId) ? (GlyphIds + HalfCount) : GlyphIds; + Count -= HalfCount; + } - if(GlyphId == *GlyphIds) - { - Result.Valid = 1; - Result.Index = (kbts_u32)(GlyphIds - KBTS_POINTER_AFTER(kbts_u16, Coverage)); + if(GlyphId == *GlyphIds) + { + Result.Valid = 1; + Result.Index = (kbts_u32)(GlyphIds - KBTS_POINTER_AFTER(kbts_u16, Coverage)); + } } - } - else if(Coverage->Format == 2) - { - kbts_range_record *Ranges = KBTS_POINTER_AFTER(kbts_range_record, Coverage); + else if(Coverage->Format == 2) + { + kbts_range_record *Ranges = KBTS_POINTER_AFTER(kbts_range_record, Coverage); - while(Count > 1) - { - kbts_un HalfCount = Count / 2; - Ranges = (Ranges[HalfCount - 1].EndGlyphId < GlyphId) ? (Ranges + HalfCount) : Ranges; - Count -= HalfCount; - } - if((GlyphId >= Ranges->StartGlyphId) && (GlyphId <= Ranges->EndGlyphId)) - { - Result.Valid = 1; - Result.Index = Ranges->StartCoverageIndex + GlyphId - Ranges->StartGlyphId; + while(Count > 1) + { + kbts_un HalfCount = Count / 2; + Ranges = (Ranges[HalfCount - 1].EndGlyphId < GlyphId) ? (Ranges + HalfCount) : Ranges; + Count -= HalfCount; + } + if((GlyphId >= Ranges->StartGlyphId) && (GlyphId <= Ranges->EndGlyphId)) + { + Result.Valid = 1; + Result.Index = Ranges->StartCoverageIndex + GlyphId - Ranges->StartGlyphId; + } } } @@ -13778,7 +15070,7 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, { kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; Subst->SeqRuleSetCount = kbts_ByteSwap16(Subst->SeqRuleSetCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->SeqRuleSetCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->SeqRuleSetCount, Context); KBTS_FOR(SetIndex, 0, Subst->SeqRuleSetCount) { @@ -13787,7 +15079,7 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, if(Set && !kbts_AlreadyVisited(Context, Set)) { Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); KBTS_FOR(RuleIndex, 0, Set->Count) { @@ -13795,8 +15087,8 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, if(!kbts_AlreadyVisited(Context, Rule)) { - kbts_ByteSwapArray16(&Rule->GlyphCount, 2); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Rule), (kbts_un)Rule->GlyphCount - 1 + (kbts_un)Rule->SequenceLookupCount * 2); + kbts_ByteSwapArray16Context(&Rule->GlyphCount, 2, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Rule), (kbts_un)Rule->GlyphCount - 1 + (kbts_un)Rule->SequenceLookupCount * 2, Context); } } } @@ -13805,8 +15097,8 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, else if(Base[0] == 2) { kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; - kbts_ByteSwapArray16(&Subst->ClassDefOffset, 2); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ClassSequenceRuleSetCount); + kbts_ByteSwapArray16Context(&Subst->ClassDefOffset, 2, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ClassSequenceRuleSetCount, Context); kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); kbts_ByteSwapClassDefinition(Context, ClassDefBase); @@ -13818,7 +15110,7 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, if(Set && !kbts_AlreadyVisited(Context, Set)) { Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); KBTS_FOR(RuleIndex, 0, Set->Count) { @@ -13826,8 +15118,8 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, if(!kbts_AlreadyVisited(Context, Rule)) { - kbts_ByteSwapArray16(&Rule->GlyphCount, 2); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Rule), Rule->GlyphCount - 1 + 2 * Rule->SequenceLookupCount); + kbts_ByteSwapArray16Context(&Rule->GlyphCount, 2, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Rule), Rule->GlyphCount - 1 + 2 * Rule->SequenceLookupCount, Context); } } } @@ -13867,8 +15159,8 @@ static void kbts_ByteSwapSequenceContextSubtable(kbts_byteswap_context *Context, else if(Base[0] == 3) { kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - kbts_ByteSwapArray16(&Subst->GlyphCount, 2); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->GlyphCount + 2 * Subst->SequenceLookupCount); + kbts_ByteSwapArray16Context(&Subst->GlyphCount, 2, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->GlyphCount + 2 * Subst->SequenceLookupCount, Context); kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); KBTS_FOR(CoverageIndex, 0, Subst->GlyphCount) @@ -14145,7 +15437,7 @@ static void kbts_ByteSwapChainedSequenceContextSubtable(kbts_byteswap_context *C { kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; Subst->ChainedSequenceRuleSetCount = kbts_ByteSwap16(Subst->ChainedSequenceRuleSetCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ChainedSequenceRuleSetCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ChainedSequenceRuleSetCount, Context); KBTS_FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) { @@ -14155,8 +15447,8 @@ static void kbts_ByteSwapChainedSequenceContextSubtable(kbts_byteswap_context *C else if(Base[0] == 2) { kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; - kbts_ByteSwapArray16(&Subst->BacktrackClassDefOffset, 4); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ChainedClassSequenceRuleSetCount); + kbts_ByteSwapArray16Context(&Subst->BacktrackClassDefOffset, 4, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->ChainedClassSequenceRuleSetCount, Context); kbts_u16 *BacktrackClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->BacktrackClassDefOffset); kbts_ByteSwapClassDefinition(Context, BacktrackClassDefinition); @@ -14229,7 +15521,7 @@ static void kbts_ByteSwapChainedSequenceContextSubtable(kbts_byteswap_context *C kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 1); kbts_un U16Count = Unpacked.BacktrackCount + Unpacked.InputCount + Unpacked.LookaheadCount + Unpacked.RecordCount * 2 + 4; - kbts_ByteSwapArray16(&Subst->BacktrackGlyphCount, U16Count); + kbts_ByteSwapArray16Context(&Subst->BacktrackGlyphCount, U16Count, Context); KBTS_FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) { @@ -14355,7 +15647,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts if(Subst->Format == 2) { kbts_u16 *GlyphIds = KBTS_POINTER_AFTER(kbts_u16, Subst); - kbts_ByteSwapArray16(GlyphIds, Subst->DeltaOrCount.GlyphCount); + kbts_ByteSwapArray16Context(GlyphIds, Subst->DeltaOrCount.GlyphCount, Context); #ifdef KBTS_DUMP KBTS_DUMPF(" ["); @@ -14374,7 +15666,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts { kbts_multiple_substitution *Subst = (kbts_multiple_substitution *)Base; Subst->SequenceCount = kbts_ByteSwap16(Subst->SequenceCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->SequenceCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->SequenceCount, Context); KBTS_FOR(SequenceIndex, 0, Subst->SequenceCount) { @@ -14384,7 +15676,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts { Sequence->GlyphCount = kbts_ByteSwap16(Sequence->GlyphCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Sequence), Sequence->GlyphCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Sequence), Sequence->GlyphCount, Context); } #ifdef KBTS_DUMP @@ -14405,7 +15697,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts { kbts_alternate_substitution *Subst = (kbts_alternate_substitution *)Base; Subst->AlternateSetCount = kbts_ByteSwap16(Subst->AlternateSetCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->AlternateSetCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->AlternateSetCount, Context); KBTS_FOR(SetIndex, 0, Subst->AlternateSetCount) { @@ -14415,7 +15707,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts { Set->GlyphCount = kbts_ByteSwap16(Set->GlyphCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Set), Set->GlyphCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->GlyphCount, Context); } #ifdef KBTS_DUMP @@ -14436,7 +15728,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts { kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Base; Subst->LigatureSetCount = kbts_ByteSwap16(Subst->LigatureSetCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->LigatureSetCount); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Subst), Subst->LigatureSetCount, Context); KBTS_FOR(SetIndex, 0, Subst->LigatureSetCount) { @@ -14445,7 +15737,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts if(!kbts_AlreadyVisited(Context, Set)) { Set->Count = kbts_ByteSwap16(Set->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Set), Set->Count, Context); KBTS_FOR(LigatureIndex, 0, Set->Count) { @@ -14453,8 +15745,8 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts if(!kbts_AlreadyVisited(Context, Ligature)) { - kbts_ByteSwapArray16(&Ligature->Glyph, 2); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Ligature), Ligature->ComponentCount - 1); + kbts_ByteSwapArray16Context(&Ligature->Glyph, 2, Context); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Ligature), Ligature->ComponentCount - 1, Context); # ifdef KBTS_DUMP KBTS_DUMPF("ligature: ["); @@ -14494,7 +15786,7 @@ static void kbts_ByteSwapGsubLookupSubtable(kbts_byteswap_context *Context, kbts kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 1); kbts_un U16Count = Unpacked.BacktrackCount + Unpacked.GlyphCount + Unpacked.LookaheadCount + 3; - kbts_ByteSwapArray16(&Subst->BacktrackGlyphCount, U16Count); + kbts_ByteSwapArray16Context(&Subst->BacktrackGlyphCount, U16Count, Context); KBTS_FOR(BacktrackCoverageIndex, 0, Unpacked.BacktrackCount) { @@ -14559,10 +15851,10 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts if(*Base == 1) { kbts_pair_adjustment_1 *Adjust = (kbts_pair_adjustment_1 *)Base; - kbts_ByteSwapArray16(&Adjust->ValueFormat1, 3); + kbts_ByteSwapArray16Context(&Adjust->ValueFormat1, 3, Context); kbts_u16 *SetOffsets = KBTS_POINTER_AFTER(kbts_u16, Adjust); - kbts_ByteSwapArray16(SetOffsets, Adjust->SetCount); + kbts_ByteSwapArray16Context(SetOffsets, Adjust->SetCount, Context); kbts_un Size1 = kbts_PopCount32(Adjust->ValueFormat1); kbts_un Size2 = kbts_PopCount32(Adjust->ValueFormat2); @@ -14595,7 +15887,7 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts else if(*Base == 2) { kbts_pair_adjustment_2 *Adjust = (kbts_pair_adjustment_2 *)Base; - kbts_ByteSwapArray16(&Adjust->ValueFormat1, 6); + kbts_ByteSwapArray16Context(&Adjust->ValueFormat1, 6, Context); kbts_ByteSwapClassDefinition(Context, KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition1Offset)); kbts_ByteSwapClassDefinition(Context, KBTS_POINTER_OFFSET(kbts_u16, Adjust, Adjust->ClassDefinition2Offset)); @@ -14622,7 +15914,7 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts { kbts_cursive_attachment *Adjust = (kbts_cursive_attachment *)Base; Adjust->EntryExitCount = kbts_ByteSwap16(Adjust->EntryExitCount); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Adjust), Adjust->EntryExitCount * 2); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, Adjust), Adjust->EntryExitCount * 2, Context); kbts_entry_exit *EntryExits = KBTS_POINTER_AFTER(kbts_entry_exit, Adjust); KBTS_FOR(EntryExitIndex, 0, Adjust->EntryExitCount) @@ -14646,7 +15938,7 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts case 6: { kbts_mark_to_base_attachment *Adjust = (kbts_mark_to_base_attachment *)Base; - kbts_ByteSwapArray16(&Adjust->BaseCoverageOffset, 4); + kbts_ByteSwapArray16Context(&Adjust->BaseCoverageOffset, 4, Context); kbts_ByteSwapCoverage(Context, KBTS_POINTER_OFFSET(kbts_coverage, Adjust, Adjust->BaseCoverageOffset)); kbts_ByteSwapMarkArray(Context, KBTS_POINTER_OFFSET(kbts_mark_array, Adjust, Adjust->MarkArrayOffset)); @@ -14657,7 +15949,7 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts case 5: { kbts_mark_to_ligature_attachment *Adjust = (kbts_mark_to_ligature_attachment *)Base; - kbts_ByteSwapArray16(&Adjust->LigatureCoverageOffset, 4); + kbts_ByteSwapArray16Context(&Adjust->LigatureCoverageOffset, 4, Context); kbts_ByteSwapCoverage(Context, KBTS_POINTER_OFFSET(kbts_coverage, Adjust, Adjust->LigatureCoverageOffset)); kbts_ByteSwapMarkArray(Context, KBTS_POINTER_OFFSET(kbts_mark_array, Adjust, Adjust->MarkArrayOffset)); @@ -14666,7 +15958,7 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts if(!kbts_AlreadyVisited(Context, LigatureArray)) { LigatureArray->Count = kbts_ByteSwap16(LigatureArray->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, LigatureArray), LigatureArray->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, LigatureArray), LigatureArray->Count, Context); KBTS_FOR(AttachIndex, 0, LigatureArray->Count) { @@ -14677,7 +15969,7 @@ static void kbts_ByteSwapGposLookupSubtable(kbts_byteswap_context *Context, kbts Attach->Count = kbts_ByteSwap16(Attach->Count); kbts_u16 *AttachAnchorOffsets = KBTS_POINTER_AFTER(kbts_u16, Attach); - kbts_ByteSwapArray16(AttachAnchorOffsets, Attach->Count * Adjust->MarkClassCount); + kbts_ByteSwapArray16Context(AttachAnchorOffsets, Attach->Count * Adjust->MarkClassCount, Context); KBTS_FOR(ComponentIndex, 0, Attach->Count) { @@ -14729,20 +16021,17 @@ static kbts_glyph_classes kbts_GlyphClasses(kbts_font *Font, kbts_u32 Id) kbts_gdef *Gdef = Font->Gdef; if(Gdef) { - kbts_u16 Class = 0; if(Gdef->ClassDefinitionOffset) { kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); - Class = kbts_GlyphClassFromTable(ClassDefBase, Id); + Result.Class = kbts_GlyphClassFromTable(ClassDefBase, Id).Class; } - if(Gdef->MarkAttachmentClassDefinitionOffset && (Class == KBTS_GLYPH_CLASS_MARK)) + if(Gdef->MarkAttachmentClassDefinitionOffset && (Result.Class == KBTS_GLYPH_CLASS_MARK)) { kbts_u16 *MarkAttachmentClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); - Result.MarkAttachmentClass = kbts_GlyphClassFromTable(MarkAttachmentClassDefBase, Id); + Result.MarkAttachmentClass = kbts_GlyphClassFromTable(MarkAttachmentClassDefBase, Id).Class; } - - Result.Class = Class; } return Result; @@ -14808,8 +16097,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint { Result.Id = Cmap0->GlyphIdArray[Codepoint]; } - } - break; + } break; case 2: { @@ -14850,8 +16138,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint Result.Id = GlyphId; } - } - break; + } break; case 4: { @@ -14861,31 +16148,36 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint kbts_u16 *StartCodes = EndCodes + SegmentCount + 1; kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount); kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount); + kbts_un SegmentIndexOffset = 0; - KBTS_FOR(SegmentIndex, 0, SegmentCount) + if(SegmentCount) { - kbts_u16 Start = StartCodes[SegmentIndex]; - if((Codepoint >= Start) && (Codepoint <= EndCodes[SegmentIndex])) + while(SegmentCount > 1) { - kbts_s16 Delta = IdDeltas[SegmentIndex]; - kbts_u16 RangeOffset = IdRangeOffsets[SegmentIndex]; - - kbts_u16 GlyphId = (kbts_u16)Delta; - if(RangeOffset) - { - GlyphId += *(&IdRangeOffsets[SegmentIndex] + (Codepoint - Start) + RangeOffset / 2); - } - else - { - GlyphId += (kbts_u16)(Codepoint); - } - Result.Id = GlyphId; - - break; + kbts_un HalfCount = SegmentCount / 2; + SegmentIndexOffset = (EndCodes[SegmentIndexOffset + HalfCount - 1] < Codepoint) ? (SegmentIndexOffset + HalfCount) : SegmentIndexOffset; + SegmentCount -= HalfCount; } } - } - break; + + kbts_u16 Start = StartCodes[SegmentIndexOffset]; + if((Codepoint >= Start) && (Codepoint <= EndCodes[SegmentIndexOffset])) + { + kbts_s16 Delta = IdDeltas[SegmentIndexOffset]; + kbts_u16 RangeOffset = IdRangeOffsets[SegmentIndexOffset]; + + kbts_u16 GlyphId = (kbts_u16)Delta; + if(RangeOffset) + { + GlyphId += *(&IdRangeOffsets[SegmentIndexOffset] + (Codepoint - Start) + RangeOffset / 2); + } + else + { + GlyphId += (kbts_u16)(Codepoint); + } + Result.Id = GlyphId; + } + } break; case 6: { @@ -14897,8 +16189,7 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint { Result.Id = GlyphIds[Offset]; } - } - break; + } break; case 12: { @@ -14906,22 +16197,25 @@ KBTS_EXPORT kbts_glyph kbts_CodepointToGlyph(kbts_font *Font, kbts_u32 Codepoint kbts_sequential_map_group *Groups = KBTS_POINTER_AFTER(kbts_sequential_map_group, Cmap12); kbts_un GlyphId = 0; - KBTS_FOR(GroupIndex, 0, Cmap12->GroupCount) + kbts_un GroupCount = Cmap12->GroupCount; + if(GroupCount) { - kbts_sequential_map_group *Group = &Groups[GroupIndex]; - - if((Codepoint >= Group->StartCharacterCode) && (Codepoint <= Group->EndCharacterCode)) + while(GroupCount > 1) { - kbts_un Offset = Codepoint - Group->StartCharacterCode; - GlyphId = Group->StartGlyphId + Offset; - - break; + kbts_un HalfCount = GroupCount / 2; + Groups = (Groups[HalfCount - 1].EndCharacterCode < Codepoint) ? (Groups + HalfCount) : Groups; + GroupCount -= HalfCount; } } + if((Codepoint >= Groups->StartCharacterCode) && (Codepoint <= Groups->EndCharacterCode)) + { + kbts_un Offset = Codepoint - Groups->StartCharacterCode; + GlyphId = Groups->StartGlyphId + Offset; + } + Result.Id = (kbts_u16)GlyphId; - } - break; + } break; } } @@ -14990,22 +16284,6 @@ static kbts_u32 kbts_IsValidFeatureIteration(kbts_iterate_features *It) return Result; } -static kbts_feature_id kbts_FeatureToId(kbts_feature_tag Feature) -{ - kbts_feature_id Result = 0; - - switch(Feature) - { -# define KBTS_X(Name, C0, C1, C2, C3) case KBTS_FEATURE_TAG_##Name: Result = KBTS_FEATURE_ID_##Name; break; - KBTS_X_FEATURES -# undef KBTS_X - - default: break; - } - - return Result; -} - static kbts_u32 kbts_NextFeature(kbts_iterate_features *It) { kbts_u32 Result = 0; @@ -15038,13 +16316,15 @@ static kbts_u32 kbts_NextFeature(kbts_iterate_features *It) It->FeatureIndex += 1; - kbts_u32 FeatureId = kbts_FeatureToId(Feature.Tag); - kbts_u64 FeatureFlag = (FeatureId < 32) ? (1ull << FeatureId) : 0; - if(kbts_ContainsFeature(&It->EnabledFeatures, kbts_FeatureToId(Feature.Tag))) + kbts_u32 FeatureId = kbts_FeatureTagToId(Feature.Tag); + if(kbts_ContainsFeature(&It->EnabledFeatures, FeatureId)) { It->Feature = Feature.Feature; It->CurrentFeatureTag = Feature.Tag; - It->CurrentFeatureFlag = FeatureFlag & KBTS_GLYPH_FEATURE_MASK; + if(FeatureId && (FeatureId <= 32)) + { + It->CurrentFeatureFlag = (1 << (FeatureId - 1)) & KBTS_GLYPH_FEATURE_MASK; + } Result = 1; break; @@ -15108,29 +16388,101 @@ typedef struct kbts_do_single_substitution_result kbts_u32 PerformedSubstitution; } kbts_do_single_substitution_result; +static kbts_feature_set kbts_ShaperFeatures[] = { + /* KBTS_SHAPER_DEFAULT */ {{KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs) | + KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | + KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(calt), + 0, + KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(liga) | + KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | + KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk), + KBTS_FEATURE_FLAG3(rvrn)}}, + /* KBTS_SHAPER_ARABIC */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | + KBTS_FEATURE_FLAG0(isol) | KBTS_FEATURE_FLAG0(fina) | KBTS_FEATURE_FLAG0(fin2) | KBTS_FEATURE_FLAG0(fin3) | + KBTS_FEATURE_FLAG0(medi) | KBTS_FEATURE_FLAG0(med2) | KBTS_FEATURE_FLAG0(init) | KBTS_FEATURE_FLAG0(calt) | + KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG2(mset) | KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | + KBTS_FEATURE_FLAG0(curs), + 0, + KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(liga) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(mark) | + KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | KBTS_FEATURE_FLAG2(locl), + KBTS_FEATURE_FLAG3(rvrn) | KBTS_FEATURE_FLAG3(rtlm) | KBTS_FEATURE_FLAG3(stch) | KBTS_FEATURE_FLAG3(rtla)}}, + /* KBTS_SHAPER_HANGUL */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ljmo) | + KBTS_FEATURE_FLAG0(vjmo) | KBTS_FEATURE_FLAG0(tjmo) | KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | + KBTS_FEATURE_FLAG0(ccmp) | KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(curs), + 0, + KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | + KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | + KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(liga), + KBTS_FEATURE_FLAG3(rvrn)}}, + /* KBTS_SHAPER_HEBREW */ {{KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs) | + KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | + KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(calt), + 0, + KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(liga) | + KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | + KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk), + KBTS_FEATURE_FLAG3(rvrn)}}, // (Same as DEFAULT) + /* KBTS_SHAPER_INDIC */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(akhn) | + KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(abvf) | + KBTS_FEATURE_FLAG0(half) | KBTS_FEATURE_FLAG0(pstf) | KBTS_FEATURE_FLAG0(cjct) | KBTS_FEATURE_FLAG0(abvs) | + KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(init) | KBTS_FEATURE_FLAG0(calt) | KBTS_FEATURE_FLAG0(clig) | + KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), + 0, + KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(nukt) | KBTS_FEATURE_FLAG2(rkrf) | + KBTS_FEATURE_FLAG2(pres) | KBTS_FEATURE_FLAG2(psts) | KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(rlig) | + KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(haln) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(mark) | + KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(kern), + KBTS_FEATURE_FLAG3(rvrn) | KBTS_FEATURE_FLAG3(vatu),}}, + /* KBTS_SHAPER_KHMER */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | + KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(abvf) | KBTS_FEATURE_FLAG0(pstf) | + KBTS_FEATURE_FLAG0(cfar) | KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | + KBTS_FEATURE_FLAG0(clig) | KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), + 0, + KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | + KBTS_FEATURE_FLAG2(pres) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(locl) | + KBTS_FEATURE_FLAG2(psts) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(rlig), + KBTS_FEATURE_FLAG3(rvrn)}}, + /* KBTS_SHAPER_MYANMAR */ {{KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(pstf) | + KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | + KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | KBTS_FEATURE_FLAG0(clig) | + KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), + 0, + KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | + KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(psts) | KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(pres) | + KBTS_FEATURE_FLAG2(liga) | KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern), + KBTS_FEATURE_FLAG3(rvrn)}}, + /* KBTS_SHAPER_TIBETAN */ {{KBTS_FEATURE_FLAG0(ccmp) | KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | + KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm), + 0, + KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(liga) | KBTS_FEATURE_FLAG2(kern) | KBTS_FEATURE_FLAG2(mkmk), + 0}}, + /* KBTS_SHAPER_USE */ {{KBTS_FEATURE_FLAG0(frac) | KBTS_FEATURE_FLAG0(numr) | KBTS_FEATURE_FLAG0(dnom) | KBTS_FEATURE_FLAG0(ccmp) | + KBTS_FEATURE_FLAG0(akhn) | KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(pref) | KBTS_FEATURE_FLAG0(abvf) | + KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(cjct) | KBTS_FEATURE_FLAG0(half) | KBTS_FEATURE_FLAG0(pstf) | + KBTS_FEATURE_FLAG0(fina) | KBTS_FEATURE_FLAG0(init) | KBTS_FEATURE_FLAG0(isol) | KBTS_FEATURE_FLAG0(medi) | + KBTS_FEATURE_FLAG0(abvs) | KBTS_FEATURE_FLAG0(blws) | KBTS_FEATURE_FLAG0(calt) | KBTS_FEATURE_FLAG0(clig) | + KBTS_FEATURE_FLAG0(abvm) | KBTS_FEATURE_FLAG0(blwm) | KBTS_FEATURE_FLAG0(curs), + 0, + KBTS_FEATURE_FLAG2(ltra) | KBTS_FEATURE_FLAG2(ltrm) | KBTS_FEATURE_FLAG2(locl) | KBTS_FEATURE_FLAG2(nukt) | + KBTS_FEATURE_FLAG2(rkrf) | KBTS_FEATURE_FLAG2(rlig) | KBTS_FEATURE_FLAG2(dist) | KBTS_FEATURE_FLAG2(kern) | + KBTS_FEATURE_FLAG2(mark) | KBTS_FEATURE_FLAG2(mkmk) | KBTS_FEATURE_FLAG2(haln) | KBTS_FEATURE_FLAG2(liga) | + KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(pres) | KBTS_FEATURE_FLAG2(psts), + KBTS_FEATURE_FLAG3(rvrn) | KBTS_FEATURE_FLAG3(vatu)}}, +}; + // Make sure that these fit KBTS_MAX_SIMULTANEOUS_FEATURES! static kbts_u8 kbts_Ops_Default[] = { KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 12, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, - KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_clig, - KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_rclt, - KBTS_OP_KIND_GPOS_METRICS, - KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_curs, - KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, - KBTS_OP_KIND_POST_GPOS_FIXUP, -}; -static kbts_u8 kbts_Ops_Hebrew[] = { - KBTS_OP_KIND_NORMALIZE, - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 12, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, - KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_clig, - KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_rclt, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 12, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, + KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_clig, + KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_rclt, KBTS_OP_KIND_GPOS_METRICS, KBTS_OP_KIND_GPOS_FEATURES, 7, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_curs, KBTS_FEATURE_ID_dist, KBTS_FEATURE_ID_kern, KBTS_OP_KIND_POST_GPOS_FIXUP, }; + /* @Incomplete: Vertical text. static kbts_u8 kbts_Ops_DefaultTtbBtt[] = { KBTS_OP_KIND_NORMALIZE, @@ -15145,8 +16497,8 @@ static kbts_u8 kbts_Ops_DefaultTtbBtt[] = { static kbts_u8 kbts_Ops_Hangul[] = { KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_NORMALIZE_HANGUL, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 8, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, - KBTS_FEATURE_ID_ljmo, KBTS_FEATURE_ID_vjmo, KBTS_FEATURE_ID_tjmo, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 8, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, + KBTS_FEATURE_ID_ljmo, KBTS_FEATURE_ID_vjmo, KBTS_FEATURE_ID_tjmo, KBTS_OP_KIND_GPOS_METRICS, KBTS_OP_KIND_GPOS_FEATURES, 13, KBTS_FEATURE_ID_abvm, KBTS_FEATURE_ID_blwm, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_mark, KBTS_FEATURE_ID_mkmk, KBTS_FEATURE_ID_rlig, KBTS_FEATURE_ID_liga, KBTS_FEATURE_ID_clig, KBTS_FEATURE_ID_curs, @@ -15156,7 +16508,7 @@ static kbts_u8 kbts_Ops_Hangul[] = { static kbts_u8 kbts_Ops_ArabicRclt[] = { KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_FLAG_JOINING_LETTERS, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_stch, KBTS_OP_KIND_GSUB_FEATURES, 2, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_isol, @@ -15179,7 +16531,7 @@ static kbts_u8 kbts_Ops_ArabicRclt[] = { static kbts_u8 kbts_Ops_ArabicNoRclt[] = { KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_FLAG_JOINING_LETTERS, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_rtla, KBTS_FEATURE_ID_rtlm, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_stch, KBTS_OP_KIND_GSUB_FEATURES, 2, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_locl, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_isol, @@ -15204,7 +16556,7 @@ static kbts_u8 kbts_Ops_ArabicNoRclt[] = { static kbts_u8 kbts_Ops_Indic0[] = { KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, }; // After BeginCluster. static kbts_u8 kbts_Ops_Indic1[] = { @@ -15236,7 +16588,7 @@ static kbts_u8 kbts_Ops_Indic3[] = { static kbts_u8 kbts_Ops_Use0[] = { KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_FLAG_JOINING_LETTERS, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, }; static kbts_u8 kbts_Ops_Use1[] = { KBTS_OP_KIND_GSUB_FEATURES, 4, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_nukt, KBTS_FEATURE_ID_akhn, @@ -15256,7 +16608,7 @@ static kbts_u8 kbts_Ops_Use3[] = { KBTS_OP_KIND_POST_GPOS_FIXUP, }; static kbts_u8 kbts_Ops_Tibetan[] = { - KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_locl, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 1, KBTS_FEATURE_ID_locl, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_ccmp, KBTS_OP_KIND_GSUB_FEATURES, 4, KBTS_FEATURE_ID_abvs, KBTS_FEATURE_ID_blws, KBTS_FEATURE_ID_calt, KBTS_FEATURE_ID_liga, KBTS_OP_KIND_GPOS_METRICS, @@ -15266,7 +16618,7 @@ static kbts_u8 kbts_Ops_Tibetan[] = { static kbts_u8 kbts_Ops_Khmer0[] = { KBTS_OP_KIND_PRE_NORMALIZE_DOTTED_CIRCLES, KBTS_OP_KIND_NORMALIZE, KBTS_OP_KIND_GSUB_FEATURES, 1, KBTS_FEATURE_ID_rvrn, - KBTS_OP_KIND_GSUB_FEATURES, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, + KBTS_OP_KIND_GSUB_FEATURES_WITH_USER, 5, KBTS_FEATURE_ID_frac, KBTS_FEATURE_ID_numr, KBTS_FEATURE_ID_dnom, KBTS_FEATURE_ID_ltra, KBTS_FEATURE_ID_ltrm, }; static kbts_u8 kbts_Ops_Khmer1[] = { KBTS_OP_KIND_GSUB_FEATURES, 7, KBTS_FEATURE_ID_locl, KBTS_FEATURE_ID_ccmp, KBTS_FEATURE_ID_pref, KBTS_FEATURE_ID_blwf, KBTS_FEATURE_ID_abvf, @@ -15303,7 +16655,7 @@ static kbts_op_list kbts_ShaperOpLists[KBTS_SHAPER_COUNT] = { /* DEFAULT, */ {kbts_Ops_Default, KBTS_ARRAY_LENGTH(kbts_Ops_Default)}, /* ARABIC, */ {kbts_Ops_ArabicRclt, KBTS_ARRAY_LENGTH(kbts_Ops_ArabicRclt)}, /* HANGUL, */ {kbts_Ops_Hangul, KBTS_ARRAY_LENGTH(kbts_Ops_Hangul)}, - /* HEBREW, */ {kbts_Ops_Hebrew, KBTS_ARRAY_LENGTH(kbts_Ops_Hebrew)}, + /* HEBREW, */ {kbts_Ops_Default, KBTS_ARRAY_LENGTH(kbts_Ops_Default)}, /* INDIC, */ {kbts_Ops_Indic0, KBTS_ARRAY_LENGTH(kbts_Ops_Indic0)}, /* KHMER, */ {kbts_Ops_Khmer0, KBTS_ARRAY_LENGTH(kbts_Ops_Khmer0)}, /* MYANMAR, */ {kbts_Ops_Myanmar0, KBTS_ARRAY_LENGTH(kbts_Ops_Myanmar0)}, @@ -15338,7 +16690,7 @@ KBTS_EXPORT kbts_un kbts_SizeOfShapeState(kbts_font *Font) KBTS_EXPORT kbts_shape_state *kbts_PlaceShapeState(void *Address, kbts_un Size) { kbts_shape_state *State = (kbts_shape_state *)Address; - memset(State, 0, Size); + KBTS_MEMSET(State, 0, Size); return State; } @@ -15413,7 +16765,6 @@ static int kbts_GrowGlyphArray(kbts_u32 *ResumePoint_, kbts_glyph_array *Array, if(NewTotalCount <= Array->Capacity) { - // @Cleanup: memmove if(NewTotalCount > TotalCount) { for(kbts_un ToIndex = NewTotalCount; ToIndex > InsertIndex + GrowCount; --ToIndex) @@ -15479,19 +16830,21 @@ static void kbts_BeginFeatures(kbts_op_state *State, kbts_shape_config *Config, // } //} - kbts_u32 FeatureId = kbts_FeatureToId(Feature.Tag); + kbts_u32 FeatureId = kbts_FeatureTagToId(Feature.Tag); if(Feature.Feature->LookupIndexCount && kbts_ContainsFeature(&EnabledFeatures, FeatureId)) { kbts_lookup_indices LookupIndices = KBTS_ZERO; + LookupIndices.FeatureTag = Feature.Tag; LookupIndices.FeatureId = FeatureId; LookupIndices.SkipFlags = kbts_SkipFlags(FeatureId, Config->Shaper); // For Myanmar, we could try and tag glyphs depending on their Indic properties in BeginCluster, just like we do for // Indic scripts. // However, Harfbuzz does _not_ do this, so it seems like a bunch of work that would, at best, make us diverge from // Harfbuzz more often. - if(Config->Shaper != KBTS_SHAPER_MYANMAR) + if((Config->Shaper != KBTS_SHAPER_MYANMAR) && (FeatureId >= 1) && (FeatureId <= 32)) { - LookupIndices.GlyphFilter = (FeatureId < 32) ? (1 << FeatureId) & KBTS_GLYPH_FEATURE_MASK : 0; + // These must properly map KBTS_FEATURE_ID to kbts_glyph_flags! + LookupIndices.GlyphFilter = (1 << (FeatureId - 1)) & KBTS_GLYPH_FEATURE_MASK; } LookupIndices.Count = Feature.Feature->LookupIndexCount; LookupIndices.Indices = KBTS_POINTER_AFTER(kbts_u16, Feature.Feature); @@ -15658,10 +17011,10 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L // Instead, we know which set to use based on the current glyph's class. // From the Microsoft docs: // The class value is used as the index into an array of offsets to ClassSequenceRuleSet tables. - kbts_u16 CurrentGlyphClass = kbts_GlyphClassFromTable(ClassDefinitionBase, CurrentGlyph->Id); - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, CurrentGlyphClass); + kbts_glyph_class_from_table_result CurrentGlyphClass = kbts_GlyphClassFromTable(ClassDefinitionBase, CurrentGlyph->Id); + kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, CurrentGlyphClass.Class); - if((CurrentGlyphClass < Subst->ClassSequenceRuleSetCount) && Set) + if((CurrentGlyphClass.Class < Subst->ClassSequenceRuleSetCount) && Set) { KBTS_FOR(RuleIndex, 0, Set->Count) { @@ -15673,7 +17026,7 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L if(!kbts_SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { InputOffsets[InputCount] = (kbts_u16)(InputGlyph - CurrentGlyph); - InputClasses[InputCount++] = kbts_GlyphClassFromTable(ClassDefinitionBase, InputGlyph->Id); + InputClasses[InputCount++] = kbts_GlyphClassFromTable(ClassDefinitionBase, InputGlyph->Id).Class; } InputGlyph += 1; @@ -15801,7 +17154,7 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L // current glyph. The class value is used as the index into an array of offsets to ChainedClassSequenceRuleSet // tables. // - kbts_u16 CurrentGlyphClass = kbts_GlyphClassFromTable(InputClassDefinition, CurrentGlyph->Id); + kbts_u16 CurrentGlyphClass = kbts_GlyphClassFromTable(InputClassDefinition, CurrentGlyph->Id).Class; kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, CurrentGlyphClass); // If the glyph was contained in the coverage table, then it should have a valid class. // Nevertheless, one Harfbuzz test font did not remove out-of-bounds glyph classes from the class definition @@ -15824,8 +17177,9 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L { if(!kbts_SkipGlyph(BacktrackGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - kbts_u16 Class = kbts_GlyphClassFromTable(BacktrackClassDefinition, BacktrackGlyph->Id); - BacktrackClasses[BacktrackClassCount++] = Class; + // @Robustness: Do we want to break if we don't find a class? + kbts_glyph_class_from_table_result Class = kbts_GlyphClassFromTable(BacktrackClassDefinition, BacktrackGlyph->Id); + BacktrackClasses[BacktrackClassCount++] = Class.Class; } BacktrackGlyph -= 1; @@ -15836,19 +17190,20 @@ static kbts_sequence_lookup_result kbts_DoSequenceLookup(kbts_unpacked_lookup *L { if(!kbts_SkipGlyph(InputGlyph, Lookup, SkipFlags, SkipUnicodeFlags)) { - kbts_u16 InputClass = kbts_GlyphClassFromTable(InputClassDefinition, InputGlyph->Id); + kbts_glyph_class_from_table_result InputClass = kbts_GlyphClassFromTable(InputClassDefinition, InputGlyph->Id); // In many cases, the font designer just wants to match "a set of glyphs" forward, // and it doesn't matter whether those glyphs are in the input sequence or part of the lookahead. // This happens often enough that we care to special-case it. - kbts_u16 LookaheadClass = InputClass; + kbts_glyph_class_from_table_result LookaheadClass = InputClass; if(LookaheadClassDefinition != InputClassDefinition) { LookaheadClass = kbts_GlyphClassFromTable(LookaheadClassDefinition, InputGlyph->Id); } + // @Robustness: Do we want to break if we don't find a class? InputClassOffsets[InputClassCount] = (kbts_u16)(InputGlyph - CurrentGlyph); - InputClasses[InputClassCount] = InputClass; - LookaheadClasses[InputClassCount] = LookaheadClass; + InputClasses[InputClassCount] = InputClass.Class; + LookaheadClasses[InputClassCount] = LookaheadClass.Class; InputClassCount += 1; } @@ -16083,6 +17438,7 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi kbts_unpacked_value_record Unpacked1 = KBTS_ZERO; kbts_unpacked_value_record Unpacked2 = KBTS_ZERO; + int Valid = 0; if(Base[0] == 1) { @@ -16107,6 +17463,7 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi Unpacked1 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat1, Records); Records += Unpacked1.Size; Unpacked2 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat2, Records); + Valid = 1; } } } @@ -16121,28 +17478,41 @@ static kbts_do_single_adjustment_result kbts_DoSingleAdjustment(kbts_shape_confi kbts_un PairRecordSize = Size1 + Size2; kbts_u16 *PairRecords = KBTS_POINTER_AFTER(kbts_u16, Adjust); - kbts_u32 Class1 = kbts_GlyphClassFromTable(ClassDef1, CurrentGlyph->Id); - kbts_u32 Class2 = kbts_GlyphClassFromTable(ClassDef2, NextGlyphId); + // From the Microsoft docs: + // PairPosFormat2 requires that each glyph in all pairs be assigned to a class, which is + // identified by an integer called a class value. + // This _seems_ like it would mean that, if either glyph is not specified in its respective + // class definition table, then we should skip the lookup. + // However, this seems wrong in practice. Undefined classes seem to just default to 0, and then + // the bounds check takes care of deciding whether the lookup is okay to apply or not. + kbts_glyph_class_from_table_result Class1 = kbts_GlyphClassFromTable(ClassDef1, CurrentGlyph->Id); + kbts_glyph_class_from_table_result Class2 = kbts_GlyphClassFromTable(ClassDef2, NextGlyphId); + if((Class1.Class < Adjust->Class1Count) && (Class2.Class < Adjust->Class2Count)) + { + kbts_u16 *PairRecord = PairRecords + Class1.Class * PairRecordSize * Adjust->Class2Count + Class2.Class * PairRecordSize; - kbts_u16 *PairRecord = PairRecords + Class1 * PairRecordSize * Adjust->Class2Count + Class2 * PairRecordSize; + Unpacked1 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat1, PairRecord); + PairRecord += Size1; - Unpacked1 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat1, PairRecord); - PairRecord += Size1; - - Unpacked2 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat2, PairRecord); - PairRecord += Size2; + Unpacked2 = kbts_UnpackValueRecord(Adjust, Adjust->ValueFormat2, PairRecord); + PairRecord += Size2; + Valid = 1; + } } - kbts_ApplyValueRecord(CurrentGlyph, &Unpacked1); - CurrentGlyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - - kbts_ApplyValueRecord(&InputGlyphs[NextGlyph.Index], &Unpacked2); - NextGlyph.Glyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; - - Result.PositionedGlyphCount = 2; - if(!Unpacked2.Size) + if(Valid) { - Result.PositionedGlyphCount = 1; + kbts_ApplyValueRecord(CurrentGlyph, &Unpacked1); + CurrentGlyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; + + kbts_ApplyValueRecord(&InputGlyphs[NextGlyph.Index], &Unpacked2); + NextGlyph.Glyph->Flags |= KBTS_GLYPH_FLAG_USED_IN_GPOS; + + Result.PositionedGlyphCount = 2; + if(!Unpacked2.Size) + { + Result.PositionedGlyphCount = 1; + } } } } @@ -16823,8 +18193,66 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap kbts_alternate_set *Set = kbts_GetAlternateSet(Subst, Cover.Index); kbts_u16 *AltGlyphIds = KBTS_POINTER_AFTER(kbts_u16, Set); - // @Incomplete: Have a way for the user to select which alternative to use. - kbts_u16 NewId = AltGlyphIds[0]; + kbts_un AlternateIndex = 0; + + { + kbts_feature_set *ShaperFeatures = ShapeState->Config->Features; + kbts_glyph_config *GlyphConfig = CurrentGlyph->Config; + if(GlyphConfig) + { + int HasOverride = 0; + KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(GlyphConfig->EnabledFeatures.Flags)) + { + kbts_u64 Flags = GlyphConfig->EnabledFeatures.Flags[WordIndex] & ShaperFeatures->Flags[WordIndex]; + if(Flags) + { + HasOverride = 1; + break; + } + } + + if(HasOverride) + { + kbts_op_state_gsub *Gsub = &ShapeState->OpState.OpSpecific.Gsub; + KBTS_FOR(OverrideIndex, 0, GlyphConfig->FeatureOverrideCount) + { + kbts_feature_override *Override = &GlyphConfig->FeatureOverrides[OverrideIndex]; + kbts_u32 EnabledOrAlternatePlusOne = Override->EnabledOrAlternatePlusOne; + + if(EnabledOrAlternatePlusOne && kbts_ContainsFeature(&Gsub->LookupFeatures, Override->Id)) + { + int Match = 1; + if(!Override->Id) + { + // Slow path for unregistered features. + Match = 0; + KBTS_FOR(UnregisteredFeatureIndex, 0, ShapeState->OpState.UnregisteredFeatureCount) + { + if(Override->Tag == ShapeState->OpState.UnregisteredFeatureTags[UnregisteredFeatureIndex]) + { + Match = 1; + break; + } + } + } + + if(Match) + { + AlternateIndex = EnabledOrAlternatePlusOne - 1; + break; + } + } + } + } + } + } + + if(AlternateIndex >= Set->GlyphCount) + { + AlternateIndex = 0; + } + + kbts_u16 NewId = AltGlyphIds[AlternateIndex]; kbts_GsubMutate(Font, CurrentGlyph, NewId, GeneratedGlyphFlags); } } break; @@ -16926,6 +18354,8 @@ static kbts_substitution_result_flags kbts_DoSubstitution(kbts_shape_state *Shap } } + // Currently, we only take the main glyph's config into account while making the ligature's config. + // Maybe we should merge all of the components' configs into one instead? kbts_GsubMutate(Font, CurrentGlyph, Ligature->Glyph, GeneratedGlyphFlags | KBTS_GLYPH_FLAG_LIGATURE); CurrentGlyph->Uid = (kbts_u16)LigatureUid; // Harfbuzz does this, because Uniscribe does this, and so we do the same. Sigh. @@ -17032,7 +18462,6 @@ Cleanup:; static kbts_u32 kbts_WouldSubstitute(kbts_shape_state *ShapeState, kbts_lookup_list *LookupList, kbts_gsub_frame *Frames, kbts_feature *Feature, kbts_skip_flags SkipFlags, kbts_glyph *Glyphs, kbts_un GlyphCount) { kbts_u32 Result = 0; - kbts_u32 DummyGlyphCount = (kbts_u32)GlyphCount; kbts_glyph_array GlyphArray = kbts_GlyphArray(Glyphs, GlyphCount, GlyphCount, GlyphCount); kbts_iterate_lookups IterateLookups = kbts_IterateLookups(LookupList, Feature); @@ -17068,9 +18497,7 @@ Done:; return Result; } -#include - -static int kbts_NextLookupIndex(kbts_op_state *S, kbts_un *LookupIndex, kbts_u32 *SkipFlags_, kbts_u32 *GlyphFilter_) +static int kbts_NextLookupIndex(kbts_op_state *S, kbts_un *LookupIndex, kbts_u32 *SkipFlags_, kbts_u32 *GlyphFilter_, kbts_feature_set *FeatureSet_) { kbts_un LowestIndex = 0xFFFFFFFF; KBTS_FOR(FeatureIndex, 0, S->FeatureCount) @@ -17083,8 +18510,10 @@ static int kbts_NextLookupIndex(kbts_op_state *S, kbts_un *LookupIndex, kbts_u32 } } + kbts_feature_set FeatureSet = KBTS_ZERO; kbts_skip_flags SkipFlags = 0; kbts_u32 GlyphFilter = 0; + kbts_un UnregisteredFeatureCount = 0; KBTS_FOR(FeatureIndex, 0, S->FeatureCount) { kbts_lookup_indices *Indices = &S->FeatureLookupIndices[FeatureIndex]; @@ -17093,6 +18522,13 @@ static int kbts_NextLookupIndex(kbts_op_state *S, kbts_un *LookupIndex, kbts_u32 { SkipFlags |= Indices->SkipFlags; GlyphFilter |= Indices->GlyphFilter; + kbts_AddFeature(&FeatureSet, Indices->FeatureId); + + if(!Indices->FeatureId && (UnregisteredFeatureCount < KBTS_MAX_SIMULTANEOUS_FEATURES)) + { + S->UnregisteredFeatureTags[UnregisteredFeatureCount++] = Indices->FeatureTag; + } + ++Indices->Indices; --Indices->Count; if(!Indices->Count) { @@ -17101,12 +18537,62 @@ static int kbts_NextLookupIndex(kbts_op_state *S, kbts_un *LookupIndex, kbts_u32 } } + S->UnregisteredFeatureCount = (kbts_u32)UnregisteredFeatureCount; *LookupIndex = LowestIndex; *SkipFlags_ = SkipFlags; *GlyphFilter_ = GlyphFilter; + *FeatureSet_ = FeatureSet; return LowestIndex != 0xFFFFFFFF; } +static int kbts_ConfigAllowsFeatures(kbts_op_state *S, kbts_shape_config *Config, kbts_glyph_config *GlyphConfig, kbts_feature_set *Features) +{ + kbts_glyph_config DummyGlyphConfig = KBTS_ZERO; + kbts_u64 UserEnabled = 0; // Whether the user enabled _any_ feature corresponding to this lookup. + kbts_u64 UserDisabled = 1; // Whether the user disabled _all_ features corresponding to this lookup. + kbts_u64 DefaultEnabled = 0; // Whether any feature is non-user. + + if(!GlyphConfig) + { + GlyphConfig = &DummyGlyphConfig; + } + + kbts_u64 Mask = 1; // Ignore unregistered features in the broad pass. + KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(GlyphConfig->EnabledFeatures.Flags)) + { + kbts_u64 LookupFeatureFlags = Features->Flags[WordIndex] & ~Mask; + Mask = 0; + + UserEnabled |= GlyphConfig->EnabledFeatures.Flags[WordIndex] & LookupFeatureFlags; + UserDisabled &= (GlyphConfig->DisabledFeatures.Flags[WordIndex] & LookupFeatureFlags) == LookupFeatureFlags; + DefaultEnabled |= Features->Flags[WordIndex] & Config->Features->Flags[WordIndex]; + } + + if(Features->Flags[0] & (GlyphConfig->EnabledFeatures.Flags[0] | GlyphConfig->DisabledFeatures.Flags[0]) & KBTS_FEATURE_FLAG0(UNREGISTERED)) + { + // Slow path for unregistered features. + KBTS_FOR(FeatureOverrideIndex, 0, GlyphConfig->FeatureOverrideCount) + { + kbts_feature_override *Override = &GlyphConfig->FeatureOverrides[FeatureOverrideIndex]; + if(Override->Id == KBTS_FEATURE_ID_UNREGISTERED) + { + kbts_feature_tag OverrideTag = Override->Tag; + KBTS_FOR(UnregisteredFeatureIndex, 0, S->UnregisteredFeatureCount) + { + if(OverrideTag == S->UnregisteredFeatureTags[UnregisteredFeatureIndex]) + { + UserEnabled |= Override->EnabledOrAlternatePlusOne; + UserDisabled &= !Override->EnabledOrAlternatePlusOne; + break; + } + } + } + } + } + int Result = (!UserDisabled && (DefaultEnabled || UserEnabled)); + return Result; +} + static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *GlyphArray) { KBTS_INSTRUMENT_FUNCTION_BEGIN @@ -17138,11 +18624,10 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G // The USE spec gives us a list of codepoint sequences which necessitate insertion of a dotted circle. // These sequences are from IndicShapingInvalidClusters.txt. S->GlyphIndex = 0; - - kbts_u64 Codepoints = 0; // 0xABBBBBCCCCCDDDDD + kbts_u64 Codepoints; Codepoints = 0; // 0xABBBBBCCCCCDDDDD for(; S->GlyphIndex < GlyphArray->Count; ++S->GlyphIndex) { - kbts_u32 NewCodepoint = Glyphs[S->GlyphIndex].Codepoint & 0x1FFFFF; + kbts_u32 NewCodepoint; NewCodepoint = Glyphs[S->GlyphIndex].Codepoint & 0x1FFFFF; Codepoints = (Codepoints << 21) | NewCodepoint; // This switch is faster than any table lookup I could come up with in my tests. @@ -17289,9 +18774,8 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G // myanmar: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, // use: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, - kbts_glyph *DecompositionGlyphs = KBTS_POINTER_AFTER(kbts_glyph, S); - { // Full NFD decomposition + kbts_glyph *DecompositionGlyphs; DecompositionGlyphs = KBTS_POINTER_AFTER(kbts_glyph, S); for(S->GlyphIndex = 0; S->GlyphIndex < GlyphArray->Count; ++S->GlyphIndex) { DecompositionGlyphs[0] = Glyphs[S->GlyphIndex]; @@ -17320,6 +18804,7 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G KBTS_FOR(DecompositionIndex, 0, DecompositionSize) { kbts_glyph DecompositionGlyph = kbts_CodepointToGlyph(Font, kbts_GetDecompositionCodepoint(Decomposition, DecompositionIndex)); + DecompositionGlyph.Config = GlyphToDecompose.Config; AnyUnsupported |= !DecompositionGlyph.Id; Decomposed[DecompositionIndex] = DecompositionGlyph; @@ -17386,11 +18871,24 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G AfterFractionSlashGlyphFlags = Swap; } + // We also collate user features here. + kbts_feature_set UserFeatures = KBTS_ZERO; + kbts_feature_set *DefaultFeatures = Config->Features; + KBTS_FOR(GlyphIndex, 0, S->WrittenCount) { kbts_glyph *Glyph = &Glyphs[GlyphIndex]; Glyph->Uid = (kbts_u16)++ShapeState->NextGlyphUid; + if(Glyph->Config) + { + kbts_glyph_config *GlyphConfig = Glyph->Config; + KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(UserFeatures.Flags)) + { + UserFeatures.Flags[WordIndex] |= GlyphConfig->EnabledFeatures.Flags[WordIndex] & ~DefaultFeatures->Flags[WordIndex]; + } + } + kbts_un AvailableGlyphCount = 0; if(!Glyph->CombiningClass) { @@ -17428,6 +18926,7 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G kbts_u32 DecompositionSize = kbts_GetDecompositionSize(ParentGlyph.Decomposition); ParentGlyph.Uid = LastBase->Uid; + ParentGlyph.Config = LastBase->Config; if((DecompositionSize == AvailableGlyphCount) && ParentGlyph.Id) { if(DecompositionSize == 2) @@ -17484,6 +18983,15 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G InFraction = 0; } } + + // Ignore added features that are already part of the shaper. + kbts_feature_set *ShaperFeatures = Config->Features; + KBTS_FOR(WordIndex, 0, KBTS_ARRAY_LENGTH(UserFeatures.Flags)) + { + UserFeatures.Flags[WordIndex] &= ~ShaperFeatures->Flags[WordIndex]; + } + + ShapeState->UserFeatures = UserFeatures; } { // Unicode mark reordering. @@ -17659,10 +19167,14 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G else if((Config->Script == KBTS_SCRIPT_THAI) || (Config->Script == KBTS_SCRIPT_LAO)) { // Decompose sara/sala ams. - kbts_un AboveBaseGlyphCount = 0; + kbts_un AboveBaseGlyphCount; AboveBaseGlyphCount = 0; for(S->GlyphIndex = 0; S->GlyphIndex < S->WrittenCount; ++S->GlyphIndex) { - ResumePoint5:; + if(0) + { + ResumePoint5:; + AboveBaseGlyphCount = S->OpSpecific.Normalize.AboveBaseGlyphCount; + } kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; kbts_u32 Codepoint = Glyph->Codepoint; @@ -17714,148 +19226,148 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G case KBTS_OP_KIND_NORMALIZE_HANGUL: { KBTS_INSTRUMENT_BLOCK_BEGIN("NORMALIZE_HANGUL") + kbts_op_state_normalize_hangul *NormalizeHangul; NormalizeHangul = &S->OpSpecific.NormalizeHangul; S->GlyphIndex = 0; while(S->GlyphIndex < GlyphArray->Count) { - kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; - - kbts_un L = 0; - kbts_un V = 0; - kbts_un T = 0; - - kbts_hangul_syllable_info LInfo = kbts_HangulSyllableInfo(Glyph->Codepoint); - if(LInfo.Type >= KBTS_HANGUL_SYLLABLE_TYPE_LV) { - kbts_un SIndex = (Glyph->Codepoint - 0xAC00); + kbts_glyph *Glyph = &Glyphs[S->GlyphIndex]; + kbts_un L = 0; + kbts_un V = 0; + kbts_un T = 0; - L = 0x1100 + SIndex / 588; - V = 0x1161 + (SIndex % 588) / 28; - - kbts_un TIndex = SIndex % 28; - if(TIndex) + kbts_hangul_syllable_info LInfo = kbts_HangulSyllableInfo(Glyph->Codepoint); + if(LInfo.Type >= KBTS_HANGUL_SYLLABLE_TYPE_LV) { - T = 0x11A7 + TIndex; - } - } - else if(LInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_L) - { - L = Glyph->Codepoint; - } + kbts_un SIndex = (Glyph->Codepoint - 0xAC00); - S->GlyphIndex += 1; + L = 0x1100 + SIndex / 588; + V = 0x1161 + (SIndex % 588) / 28; - kbts_op_state_normalize_hangul *NormalizeHangul = &S->OpSpecific.NormalizeHangul; - - if(L) - { - kbts_hangul_syllable_info VInfo = KBTS_ZERO; - - if(!V && (S->GlyphIndex < GlyphArray->Count)) - { - kbts_u32 VCodepoint = Glyphs[S->GlyphIndex].Codepoint; - - VInfo = kbts_HangulSyllableInfo(VCodepoint); - - if(VInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_V) + kbts_un TIndex = SIndex % 28; + if(TIndex) { - V = VCodepoint; - - S->GlyphIndex += 1; + T = 0x11A7 + TIndex; } } - - if(V) + else if(LInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_L) { - kbts_hangul_syllable_info TInfo = KBTS_ZERO; + L = Glyph->Codepoint; + } - if(!T && (S->GlyphIndex < GlyphArray->Count)) + S->GlyphIndex += 1; + + if(L) + { + kbts_hangul_syllable_info VInfo = KBTS_ZERO; + + if(!V && (S->GlyphIndex < GlyphArray->Count)) { - kbts_u32 TCodepoint = Glyphs[S->GlyphIndex].Codepoint; + kbts_u32 VCodepoint = Glyphs[S->GlyphIndex].Codepoint; - TInfo = kbts_HangulSyllableInfo(TCodepoint); + VInfo = kbts_HangulSyllableInfo(VCodepoint); - if(TInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_T) + if(VInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_V) { - T = TCodepoint; + V = VCodepoint; S->GlyphIndex += 1; } } - NormalizeHangul->LvtGlyphCount = 0; - - // Check for any tone marks that we need to swap to the front of the syllable. - // The OpenType shaping documents say that we need to do this after applying GSUB features, but - // harfbuzz does it before, so it's probably fine to do it here? - // It's also basically free to do here, which is nice. - if(S->GlyphIndex < GlyphArray->Count) + if(V) { - kbts_u32 ToneMarkCodepoint = Glyphs[S->GlyphIndex].Codepoint; + kbts_hangul_syllable_info TInfo = KBTS_ZERO; - if((ToneMarkCodepoint >= 0x302E) && (ToneMarkCodepoint <= 0x302F)) + if(!T && (S->GlyphIndex < GlyphArray->Count)) { - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = kbts_CodepointToGlyph(Font, ToneMarkCodepoint); + kbts_u32 TCodepoint = Glyphs[S->GlyphIndex].Codepoint; - S->GlyphIndex += 1; - } - } + TInfo = kbts_HangulSyllableInfo(TCodepoint); - if(LInfo.Composable & VInfo.Composable & TInfo.Composable) - { - // Try LVT. - kbts_un LvtCodepoint = 0xAC00 + (L - 0x1100) * 588 + (V - 0x1161) * 28 + (T - 0x11A7); + if(TInfo.Type == KBTS_HANGUL_SYLLABLE_TYPE_T) + { + T = TCodepoint; - kbts_glyph LvtGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)LvtCodepoint); - if(LvtGlyph.Id) - { - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LvtGlyph; - } - } - - if(!NormalizeHangul->LvtGlyphCount) - { - kbts_glyph LvGlyph = {0}; - if(LInfo.Composable & VInfo.Composable) - { - // Try LV. - kbts_un LvCodepoint = 0xAC00 + (L - 0x1100) * 588 + (V - 0x1161) * 28; - - LvGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)LvCodepoint); + S->GlyphIndex += 1; + } } - if(LvGlyph.Id) - { - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LvGlyph; - } - else - { - // Do L-V. - kbts_glyph LGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)L); - LGlyph.Flags |= KBTS_GLYPH_FLAG_LJMO; + NormalizeHangul->LvtGlyphCount = 0; - kbts_glyph VGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)V); - VGlyph.Flags |= KBTS_GLYPH_FLAG_VJMO; + // Check for any tone marks that we need to swap to the front of the syllable. + // The OpenType shaping documents say that we need to do this after applying GSUB features, but + // harfbuzz does it before, so it's probably fine to do it here? + // It's also basically free to do here, which is nice. + if(S->GlyphIndex < GlyphArray->Count) + { + kbts_u32 ToneMarkCodepoint = Glyphs[S->GlyphIndex].Codepoint; - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LGlyph; - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = VGlyph; + if((ToneMarkCodepoint >= 0x302E) && (ToneMarkCodepoint <= 0x302F)) + { + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = kbts_CodepointToGlyph(Font, ToneMarkCodepoint); + + S->GlyphIndex += 1; + } } - if(T) + if(LInfo.Composable & VInfo.Composable & TInfo.Composable) { - kbts_glyph TGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)T); - TGlyph.Flags |= KBTS_GLYPH_FLAG_TJMO; + // Try LVT. + kbts_un LvtCodepoint = 0xAC00 + (L - 0x1100) * 588 + (V - 0x1161) * 28 + (T - 0x11A7); - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = TGlyph; + kbts_glyph LvtGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)LvtCodepoint); + if(LvtGlyph.Id) + { + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LvtGlyph; + } + } + + if(!NormalizeHangul->LvtGlyphCount) + { + kbts_glyph LvGlyph = KBTS_ZERO; + if(LInfo.Composable & VInfo.Composable) + { + // Try LV. + kbts_un LvCodepoint = 0xAC00 + (L - 0x1100) * 588 + (V - 0x1161) * 28; + + LvGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)LvCodepoint); + } + + if(LvGlyph.Id) + { + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LvGlyph; + } + else + { + // Do L-V. + kbts_glyph LGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)L); + LGlyph.Flags |= KBTS_GLYPH_FLAG_LJMO; + + kbts_glyph VGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)V); + VGlyph.Flags |= KBTS_GLYPH_FLAG_VJMO; + + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = LGlyph; + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = VGlyph; + } + + if(T) + { + kbts_glyph TGlyph = kbts_CodepointToGlyph(Font, (kbts_u32)T); + TGlyph.Flags |= KBTS_GLYPH_FLAG_TJMO; + + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = TGlyph; + } } } } - } - if(!NormalizeHangul->LvtGlyphCount) - { - kbts_glyph NewGlyph = kbts_CodepointToGlyph(Font, Glyph->Codepoint); + if(!NormalizeHangul->LvtGlyphCount) + { + kbts_glyph NewGlyph = kbts_CodepointToGlyph(Font, Glyph->Codepoint); - NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = NewGlyph; + NormalizeHangul->LvtGlyphs[NormalizeHangul->LvtGlyphCount++] = NewGlyph; + } } { // Insert the LVT glyphs. @@ -17887,16 +19399,19 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G case KBTS_OP_KIND_GSUB_FEATURES: { KBTS_INSTRUMENT_BLOCK_BEGIN("GSUB_FEATURES") - kbts_gsub_gpos *FontGsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - kbts_lookup_list *LookupList = kbts_GetLookupList(FontGsub); - kbts_gsub_frame *Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, S); - kbts_op_state_gsub *Gsub = &S->OpSpecific.Gsub; + + kbts_gsub_gpos *FontGsub; FontGsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; + kbts_lookup_list *LookupList; LookupList = kbts_GetLookupList(FontGsub); + kbts_gsub_frame *Frames; Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, S); + kbts_op_state_gsub *Gsub; Gsub = &S->OpSpecific.Gsub; + kbts_u32 GlyphFilter; + kbts_skip_flags SkipFlags; + kbts_feature_set LookupFeatures; kbts_BeginFeatures(S, Config, KBTS_SHAPING_TABLE_GSUB, Op->Features); - kbts_u32 GlyphFilter; - kbts_skip_flags SkipFlags = 0; - while(kbts_NextLookupIndex(S, &Gsub->LookupIndex, &SkipFlags, &GlyphFilter)) + while(kbts_NextLookupIndex(S, &Gsub->LookupIndex, &SkipFlags, &GlyphFilter, &LookupFeatures)) { + Gsub->LookupFeatures = LookupFeatures; S->GlyphIndex = 0; // From the Microsoft docs: @@ -17908,52 +19423,56 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G while(S->GlyphIndex < GlyphArray->Count) { Frames[0].InputGlyphCount = 1; - kbts_u32 FilterMask = Config->Shaper == KBTS_SHAPER_USE ? KBTS_USE_GLYPH_FEATURE_MASK : KBTS_GLYPH_FEATURE_MASK; - kbts_u32 EffectiveGlyphFilter = GlyphFilter & FilterMask; - // Reverse chaining substitutions are tricky. - // See the comment at :ReverseChaining. - // @Duplication: We just copy the top-level mirroring logic from DoSubstitution here for now. - kbts_lookup *Lookup = kbts_GetLookup(LookupList, Gsub->LookupIndex); - kbts_un CurrentGlyphIndex = (Lookup->Type == 8) ? GlyphArray->Count - 1 - S->GlyphIndex : S->GlyphIndex; - - if(kbts_GlyphIncludedInLookup(Config->Font, 0, Gsub->LookupIndex, Glyphs[CurrentGlyphIndex].Id) && ((Glyphs[CurrentGlyphIndex].Flags & EffectiveGlyphFilter) == EffectiveGlyphFilter)) { - S->FrameCount = 0; + kbts_u32 FilterMask = Config->Shaper == KBTS_SHAPER_USE ? KBTS_USE_GLYPH_FEATURE_MASK : KBTS_GLYPH_FEATURE_MASK; + kbts_u32 EffectiveGlyphFilter = GlyphFilter & FilterMask; + // Reverse chaining substitutions are tricky. + // See the comment at :ReverseChaining. + // @Duplication: We just copy the top-level mirroring logic from DoSubstitution here for now. + kbts_lookup *Lookup = kbts_GetLookup(LookupList, Gsub->LookupIndex); + kbts_un CurrentGlyphIndex = (Lookup->Type == 8) ? GlyphArray->Count - 1 - S->GlyphIndex : S->GlyphIndex; + kbts_glyph *CurrentGlyph = &Glyphs[CurrentGlyphIndex]; + + if(kbts_GlyphIncludedInLookup(Config->Font, 0, Gsub->LookupIndex, CurrentGlyph->Id) && + ((CurrentGlyph->Flags & EffectiveGlyphFilter) == EffectiveGlyphFilter) && + kbts_ConfigAllowsFeatures(S, Config, CurrentGlyph->Config, &LookupFeatures)) { - kbts_gsub_frame *Frame = &Frames[S->FrameCount++]; + kbts_gsub_frame *Frame = &Frames[0]; Frame->LookupIndex = (kbts_u16)Gsub->LookupIndex; Frame->SubtableIndex = 0; Frame->InputGlyphIndex = (kbts_u16)S->GlyphIndex; + S->FrameCount = 1; + } + } + + while(S->FrameCount) + { + if(0) + { + ResumePoint2:; + FontGsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; + Gsub = &S->OpSpecific.Gsub; + LookupList = kbts_GetLookupList(FontGsub); + Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, S); + LookupFeatures = Gsub->LookupFeatures; + GlyphFilter = Gsub->GlyphFilter; + SkipFlags = Gsub->SkipFlags; } - while(S->FrameCount) + // These flags are used by USE. + kbts_u32 GeneratedGlyphFlags = GlyphFilter & (KBTS_GLYPH_FLAG_RPHF | KBTS_GLYPH_FLAG_PREF); + kbts_substitution_result_flags SubstitutionFlags = kbts_DoSubstitution(ShapeState, LookupList, Frames, &S->FrameCount, GlyphArray, 0, SkipFlags, GeneratedGlyphFlags); + if(SubstitutionFlags & KBTS_SUBSTITUTION_RESULT_FLAG_GROW_BUFFER) { - if(0) - { - ResumePoint2:; - FontGsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - Gsub = &S->OpSpecific.Gsub; - LookupList = kbts_GetLookupList(FontGsub); - Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, S); - GlyphFilter = Gsub->GlyphFilter; - SkipFlags = Gsub->SkipFlags; - } + Gsub->GlyphFilter = GlyphFilter; + Gsub->SkipFlags = SkipFlags; + S->ResumePoint = 2; - // These flags are used by USE. - kbts_u32 GeneratedGlyphFlags = GlyphFilter & (KBTS_GLYPH_FLAG_RPHF | KBTS_GLYPH_FLAG_PREF); - kbts_substitution_result_flags SubstitutionFlags = kbts_DoSubstitution(ShapeState, LookupList, Frames, &S->FrameCount, GlyphArray, 0, SkipFlags, GeneratedGlyphFlags); - if(SubstitutionFlags & KBTS_SUBSTITUTION_RESULT_FLAG_GROW_BUFFER) - { - Gsub->GlyphFilter = GlyphFilter; - Gsub->SkipFlags = SkipFlags; - S->ResumePoint = 2; - - KBTS_INSTRUMENT_END - return 1; - } + KBTS_INSTRUMENT_END + return 1; } } @@ -18107,7 +19626,8 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G kbts_un LookupIndex; kbts_skip_flags SkipFlags; kbts_u32 GlyphFilter; - while(kbts_NextLookupIndex(S, &LookupIndex, &SkipFlags, &GlyphFilter)) + kbts_feature_set LookupFeatures; + while(kbts_NextLookupIndex(S, &LookupIndex, &SkipFlags, &GlyphFilter, &LookupFeatures)) { kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); @@ -18117,7 +19637,8 @@ static kbts_u32 kbts_ExecuteOp(kbts_shape_state *ShapeState, kbts_glyph_array *G { kbts_un DeltaGlyphIndex = 1; - if(kbts_GlyphIncludedInLookup(Config->Font, 1, LookupIndex, GlyphArray->Glyphs[PositionedGlyphCount].Id)) + if(kbts_GlyphIncludedInLookup(Config->Font, 1, LookupIndex, GlyphArray->Glyphs[PositionedGlyphCount].Id) && + kbts_ConfigAllowsFeatures(S, Config, Glyphs[PositionedGlyphCount].Config, &LookupFeatures)) { KBTS_FOR(SubtableIndex, 0, Lookup.SubtableCount) { @@ -18355,8 +19876,6 @@ static kbts_glyph kbts_Substitute1(kbts_shape_state *ShapeState, kbts_lookup_lis Frames[0].InputGlyphCount = 1; kbts_u32 FrameCount = 1; - kbts_u32 GlyphCount = 1; - while(FrameCount) { kbts_substitution_result_flags SubstitutionResult = kbts_DoSubstitution(ShapeState, LookupList, Frames, &FrameCount, &GlyphArray, 0, SkipFlags, 0); @@ -18457,7 +19976,6 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, kbts_gsub_frame *Frames = KBTS_POINTER_AFTER(kbts_gsub_frame, OpState); kbts_glyph *OnePastLastSyllableGlyph = Glyphs + ScanGlyphIndex; - kbts_script Script = ShapeState->Config->Script; if((Config->Script == KBTS_SCRIPT_KANNADA) && (ScanGlyphIndex >= 3) && (Glyphs[0].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_RA) && (Glyphs[1].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_HALANT) && (Glyphs[2].SyllabicClass == KBTS_INDIC_SYLLABIC_CLASS_ZWJ)) @@ -18542,8 +20060,6 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, Scratch[0] = Config->Virama; Scratch[2] = Config->Virama; - kbts_feature *Pstf = Config->Pstf; - kbts_feature *Blwf = Config->Blwf; kbts_feature *Locl = Config->Locl; kbts_feature *Vatu = Config->Vatu; @@ -18859,8 +20375,8 @@ static kbts_begin_cluster_result kbts_BeginCluster(kbts_shape_state *ShapeState, } // All post-base glyphs get BLWF, ABVF, PSTF. Some get PREF. - kbts_glyph Scratch[2] = {0}; - kbts_glyph *LastGlyph = 0; + kbts_glyph Scratch[2]; Scratch[0] = Scratch[1] = KBTS_ZERO_TYPE(kbts_glyph); + kbts_glyph *LastGlyph; LastGlyph = 0; KBTS_FOR(GlyphIndex, BaseIndex + 1, ScanGlyphIndex) { kbts_glyph *Glyph = &Glyphs[GlyphIndex]; @@ -19730,7 +21246,7 @@ static kbts_end_cluster_result kbts_EndCluster(kbts_shape_state *ShapeState, kbt return Result; } -static kbts_op_list kbts_OpList(uint8_t *Ops, kbts_un Size) +static kbts_op_list kbts_OpList(kbts_u8 *Ops, kbts_un Size) { kbts_op_list Result = KBTS_ZERO; Result.Ops = Ops; @@ -19739,7 +21255,7 @@ static kbts_op_list kbts_OpList(uint8_t *Ops, kbts_un Size) } #define KBTS_OP_LIST(OpList) kbts_OpList((kbts_Ops_##OpList), KBTS_ARRAY_LENGTH(kbts_Ops_##OpList)) -KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_u32 Script, kbts_u32 Language) +KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_script Script, kbts_language Language) { kbts_shape_config Result = KBTS_ZERO; @@ -19836,9 +21352,11 @@ KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_u32 Script, break; } + Result.Features = &kbts_ShaperFeatures[Result.Shaper]; + kbts_feature *Rclt = 0; kbts_feature_set SyllableFeatureSet = {{KBTS_FEATURE_FLAG0(rphf) | KBTS_FEATURE_FLAG0(blwf) | KBTS_FEATURE_FLAG0(half) | KBTS_FEATURE_FLAG0(pstf) | KBTS_FEATURE_FLAG0(pref), - KBTS_FEATURE_FLAG1(rclt) | KBTS_FEATURE_FLAG1(locl) | KBTS_FEATURE_FLAG1(vatu)}}; + 0, KBTS_FEATURE_FLAG2(rclt) | KBTS_FEATURE_FLAG2(locl), KBTS_FEATURE_FLAG3(vatu)}}; kbts_iterate_features IterateFeatures = kbts_IterateFeatures(&Result, KBTS_SHAPING_TABLE_GSUB, SyllableFeatureSet); while(kbts_NextFeature(&IterateFeatures)) { @@ -19884,21 +21402,37 @@ KBTS_EXPORT kbts_shape_config kbts_ShapeConfig(kbts_font *Font, kbts_u32 Script, return Result; } -static kbts_op kbts_ReadOp(kbts_u8 *Ops, kbts_u32 *Ip_) +static kbts_op kbts_ReadOp(kbts_shape_state *State, kbts_u8 *Ops) { - kbts_u32 Ip = *Ip_; kbts_op Result = KBTS_ZERO; - Result.Kind = Ops[Ip++]; + Result.Kind = Ops[State->Ip++]; + + if(Result.Kind == KBTS_OP_KIND_GSUB_FEATURES_WITH_USER) + { + Result.Kind = KBTS_OP_KIND_GSUB_FEATURES; + Result.Features = State->UserFeatures; + } + if((Result.Kind == KBTS_OP_KIND_GSUB_FEATURES) || (Result.Kind == KBTS_OP_KIND_GPOS_FEATURES)) { - kbts_un FeatureCount = Ops[Ip++]; + kbts_un FeatureCount = Ops[State->Ip++]; + int Rtl = (State->RunDirection == KBTS_DIRECTION_RTL); KBTS_FOR(FeatureIndex, 0, FeatureCount) { - kbts_u32 FeatureId = Ops[Ip++]; + kbts_u32 FeatureId = Ops[State->Ip++]; + + if((FeatureId == KBTS_FEATURE_ID_ltra) && Rtl) + { + FeatureId = KBTS_FEATURE_ID_rtla; + } + else if((FeatureId == KBTS_FEATURE_ID_ltrm) && Rtl) + { + FeatureId = KBTS_FEATURE_ID_rtlm; + } + kbts_AddFeature(&Result.Features, FeatureId); } } - *Ip_ = Ip; return Result; } @@ -19908,11 +21442,15 @@ KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, k State->Config = Config; State->MainDirection = MainDirection; State->RunDirection = RunDirection; - State->GlyphArray = kbts_GlyphArray(Glyphs, *GlyphCount, *GlyphCount, GlyphCapacity); kbts_glyph_array *GlyphArray = &State->GlyphArray; + // The Glyphs array might move after a grow, so update the pointers here. + // We preserve Count information, though, because it makes it simpler not to touch anything + // when we are dealing with sub-arrays like Cluster. + GlyphArray->Glyphs = Glyphs; + GlyphArray->Capacity = GlyphCapacity; kbts_glyph_array *Cluster = &State->ClusterGlyphArray; - Cluster->Glyphs = Glyphs + State->At; // In case the Glyphs array moved after a grow. + Cluster->Glyphs = Glyphs + State->At; Cluster->Capacity = GlyphCapacity - State->At; kbts_u32 ResumePoint = State->ResumePoint; @@ -19927,11 +21465,13 @@ KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, k case 6: goto ResumePoint6; break; } + *GlyphArray = kbts_GlyphArray(Glyphs, *GlyphCount, *GlyphCount, GlyphCapacity); + // For simple shapers, all of the shaping happens in this single loop. // For complex shapers, this loop is preparing the text for clustering logic, which happens below. for(State->Ip = 0; State->Ip < Config->OpLists[0].Length;) { - State->Op = kbts_ReadOp(Config->OpLists[0].Ops, &State->Ip); + State->Op = kbts_ReadOp(State, Config->OpLists[0].Ops); ResumePoint1:; if(kbts_ExecuteOp(State, GlyphArray)) { @@ -19956,19 +21496,20 @@ KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, k } } - kbts_begin_cluster_result BeginClusterResult = kbts_BeginCluster(State, Glyphs + State->At, GlyphArray->Count - State->At); - GlyphArray->Count += BeginClusterResult.InsertedGlyphCount; - GlyphArray->TotalCount += BeginClusterResult.InsertedGlyphCount; - State->ClusterGlyphCount = (kbts_u32)BeginClusterResult.ClusterGlyphCount; + { + kbts_begin_cluster_result BeginClusterResult = kbts_BeginCluster(State, Glyphs + State->At, GlyphArray->Count - State->At); + GlyphArray->Count += BeginClusterResult.InsertedGlyphCount; + GlyphArray->TotalCount += BeginClusterResult.InsertedGlyphCount; + State->ClusterGlyphCount = (kbts_u32)BeginClusterResult.ClusterGlyphCount; + *Cluster = kbts_GlyphArray(Glyphs + State->At, BeginClusterResult.ClusterGlyphCount, GlyphArray->Count - State->At, GlyphCapacity - State->At); + } - *Cluster = kbts_GlyphArray(Glyphs + State->At, BeginClusterResult.ClusterGlyphCount, GlyphArray->Count - State->At, GlyphCapacity - State->At); - - kbts_glyph *LastGlyphInCluster = &Glyphs[State->At + Cluster->Count - 1]; + kbts_glyph *LastGlyphInCluster; LastGlyphInCluster = &Glyphs[State->At + Cluster->Count - 1]; State->WordBreak = !(LastGlyphInCluster->UnicodeFlags & KBTS_UNICODE_FLAG_PART_OF_WORD); for(State->Ip = 0; State->Ip < Config->OpLists[1].Length;) { - State->Op = kbts_ReadOp(Config->OpLists[1].Ops, &State->Ip); + State->Op = kbts_ReadOp(State, Config->OpLists[1].Ops); ResumePoint3:; if(kbts_ExecuteOp(State, Cluster)) { @@ -19977,7 +21518,7 @@ KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, k } } - kbts_end_cluster_result EndClusterResult = kbts_EndCluster(State, Cluster); + kbts_end_cluster_result EndClusterResult; EndClusterResult = kbts_EndCluster(State, Cluster); if(EndClusterResult.InsertDottedCircle) { State->DottedCircleInsertIndex = (kbts_u32)EndClusterResult.DottedCircleIndex; @@ -19992,7 +21533,7 @@ KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, k for(State->Ip = 0; State->Ip < Config->OpLists[2].Length;) { - State->Op = kbts_ReadOp(Config->OpLists[2].Ops, &State->Ip); + State->Op = kbts_ReadOp(State, Config->OpLists[2].Ops); ResumePoint4:; if(kbts_ExecuteOp(State, Cluster)) { @@ -20012,7 +21553,7 @@ KBTS_EXPORT int kbts_Shape(kbts_shape_state *State, kbts_shape_config *Config, k // This is where Indic GPOS + post-passes happen. for(State->Ip = 0; State->Ip < Config->OpLists[3].Length;) { - State->Op = kbts_ReadOp(Config->OpLists[3].Ops, &State->Ip); + State->Op = kbts_ReadOp(State, Config->OpLists[3].Ops); ResumePoint5:; if(kbts_ExecuteOp(State, GlyphArray)) { @@ -20087,516 +21628,592 @@ KBTS_EXPORT void kbts_PositionGlyph(kbts_cursor *Cursor, kbts_glyph *Glyph, kbts KBTS_EXPORT kbts_un kbts_ReadFontHeader(kbts_font *Font, void *Data, kbts_un Size) { - // @Incomplete: Add a bounds checking/validation pass. KBTS_UNUSED(Size); - Font->FileBase = (char *)Data; - kbts_table_directory *Directory = (kbts_table_directory *)Font->FileBase; - - Directory->TableCount = kbts_ByteSwap16(Directory->TableCount); - Directory->SearchRange = kbts_ByteSwap16(Directory->SearchRange); - Directory->EntrySelector = kbts_ByteSwap16(Directory->EntrySelector); - Directory->RangeShift = kbts_ByteSwap16(Directory->RangeShift); - - kbts_table_record *Tables = (kbts_table_record *)(Directory + 1); - - kbts_un ShapingTableSizes[KBTS_SHAPING_TABLE_COUNT] = {0}; - kbts_u32 GdefSize = 0; - - for(kbts_un TableIndex = 0; TableIndex < Directory->TableCount; ++TableIndex) + kbts_un Result = 0; + if(Size >= sizeof(kbts_table_directory)) { - kbts_table_record *Table = &Tables[TableIndex]; - Table->Checksum = kbts_ByteSwap32(Table->Checksum); - Table->Offset = kbts_ByteSwap32(Table->Offset); - Table->Length = kbts_ByteSwap32(Table->Length); - void *TableBase = KBTS_POINTER_OFFSET(void, Font->FileBase, Table->Offset); + char *FileEnd = (char *)Data + Size; + Font->FileBase = (char *)Data; + Font->FileSize = Size; + kbts_table_directory *Directory = (kbts_table_directory *)Font->FileBase; - switch(Table->Tag) + Directory->TableCount = kbts_ByteSwap16(Directory->TableCount); + Directory->SearchRange = kbts_ByteSwap16(Directory->SearchRange); + Directory->EntrySelector = kbts_ByteSwap16(Directory->EntrySelector); + Directory->RangeShift = kbts_ByteSwap16(Directory->RangeShift); + + kbts_table_record *Tables = KBTS_POINTER_AFTER(kbts_table_record, Directory); + + kbts_un ShapingTableSizes[KBTS_SHAPING_TABLE_COUNT] = {0}; + kbts_u32 GdefSize = 0; + kbts_un DirectoryTableCapacity = (FileEnd - (char *)Tables) / sizeof(kbts_table_record); + if(Directory->TableCount <= DirectoryTableCapacity) { - case KBTS_FOURCC('h', 'e', 'a', 'd'): - { - kbts_head *Head = (kbts_head *)TableBase; - kbts_ByteSwapArray16(&Head->Major, 2); - kbts_ByteSwapArray32(&Head->Revision, 2); - // We do not swap the magic number. - kbts_ByteSwapArray16(&Head->Flags, 2); - // We do not swap file times. - kbts_ByteSwapArray16((kbts_u16 *)&Head->XMin, 9); - - Font->Head = Head; - } break; - - case KBTS_FOURCC('c', 'm', 'a', 'p'): - { - kbts_cmap *Cmap = (kbts_cmap *)TableBase; - Cmap->Version = kbts_ByteSwap16(Cmap->Version); - Cmap->TableCount = kbts_ByteSwap16(Cmap->TableCount); - - kbts_encoding_record *Records = KBTS_POINTER_AFTER(kbts_encoding_record, Cmap); - - KBTS_FOR(It, 0, Cmap->TableCount) + for(kbts_un TableIndex = 0; TableIndex < Directory->TableCount; ++TableIndex) { - kbts_encoding_record *Record = &Records[It]; - Record->EncodingId = kbts_ByteSwap16(Record->EncodingId); - Record->PlatformId = kbts_ByteSwap16(Record->PlatformId); - Record->SubtableOffset = kbts_ByteSwap32(Record->SubtableOffset); - } + kbts_table_record *Table = &Tables[TableIndex]; + Table->Checksum = kbts_ByteSwap32(Table->Checksum); + Table->Offset = kbts_ByteSwap32(Table->Offset); + Table->Length = kbts_ByteSwap32(Table->Length); + int TableValid = 0; - kbts_cmap_subtable_pointer PreferredSubtable = KBTS_ZERO; - kbts_u16 PreferredFormat = 1; - KBTS_FOR(It, 0, Cmap->TableCount) - { - kbts_cmap_subtable_pointer Subtable = kbts_GetCmapSubtable(Cmap, It); - kbts_u16 Format = kbts_ByteSwap16(*Subtable.Subtable); - - if(Format == 14) + void *TableBase = KBTS_POINTER_OFFSET(void, Font->FileBase, Table->Offset); + char *TableEnd = (char *)TableBase + Table->Length; + if(((char *)TableBase >= (char *)(Tables + Directory->TableCount)) && (TableEnd <= FileEnd)) { - Font->Cmap14 = (kbts_cmap_14 *)Subtable.Subtable; - } - else if(!PreferredSubtable.Subtable) - { - PreferredSubtable = Subtable; - } - else - { - kbts_u16 Precedence = kbts_CmapFormatPrecedence[Format]; - kbts_u16 PreferredPrecedence = kbts_CmapFormatPrecedence[PreferredFormat]; - - if((Precedence > PreferredPrecedence) || ((Precedence == PreferredPrecedence) && (Subtable.PlatformId == 3))) + switch(Table->Tag) { - PreferredSubtable = Subtable; - PreferredFormat = Format; + case KBTS_FOURCC('h', 'e', 'a', 'd'): + { + if(Table->Length >= sizeof(kbts_head)) + { + kbts_head *Head = (kbts_head *)TableBase; + kbts_ByteSwapArray16Unchecked(&Head->Major, 2); + kbts_ByteSwapArray32Unchecked(&Head->Revision, 2); + // We do not swap the magic number. + kbts_ByteSwapArray16Unchecked(&Head->Flags, 2); + // We do not swap file times. + kbts_ByteSwapArray16Unchecked((kbts_u16 *)&Head->XMin, 9); + + Font->Head = Head; + TableValid = 1; + } + } break; + + case KBTS_FOURCC('c', 'm', 'a', 'p'): + { + if(Table->Length >= sizeof(kbts_cmap)) + { + kbts_cmap *Cmap = (kbts_cmap *)TableBase; + Cmap->Version = kbts_ByteSwap16(Cmap->Version); + Cmap->TableCount = kbts_ByteSwap16(Cmap->TableCount); + + kbts_encoding_record *Records = KBTS_POINTER_AFTER(kbts_encoding_record, Cmap); + + if((char *)(Records + Cmap->TableCount) <= TableEnd) + { + KBTS_FOR(It, 0, Cmap->TableCount) + { + kbts_encoding_record *Record = &Records[It]; + Record->EncodingId = kbts_ByteSwap16(Record->EncodingId); + Record->PlatformId = kbts_ByteSwap16(Record->PlatformId); + Record->SubtableOffset = kbts_ByteSwap32(Record->SubtableOffset); + } + + kbts_cmap_subtable_pointer PreferredSubtable = KBTS_ZERO; + kbts_u16 PreferredFormat = 1; + KBTS_FOR(It, 0, Cmap->TableCount) + { + kbts_cmap_subtable_pointer Subtable = kbts_GetCmapSubtable(Cmap, It); + if((char *)(Subtable.Subtable + 1) <= TableEnd) + { + kbts_u16 Format = kbts_ByteSwap16(*Subtable.Subtable); + + if(Format == 14) + { + if((char *)(Subtable.Subtable + sizeof(kbts_cmap_14)) <= TableEnd) + { + Font->Cmap14 = (kbts_cmap_14 *)Subtable.Subtable; + } + } + else if(!PreferredSubtable.Subtable) + { + PreferredSubtable = Subtable; + } + else if(Format < KBTS_ARRAY_LENGTH(kbts_CmapFormatPrecedence)) + { + kbts_u16 Precedence = kbts_CmapFormatPrecedence[Format]; + kbts_u16 PreferredPrecedence = kbts_CmapFormatPrecedence[PreferredFormat]; + + if((Precedence > PreferredPrecedence) || ((Precedence == PreferredPrecedence) && (Subtable.PlatformId == 3))) + { + PreferredSubtable = Subtable; + PreferredFormat = Format; + } + } + } + } + + if(PreferredSubtable.Subtable) + { + *PreferredSubtable.Subtable = kbts_ByteSwap16(*PreferredSubtable.Subtable); + switch(*PreferredSubtable.Subtable) + { + case 0: + { + kbts_cmap_0 *Cmap0 = (kbts_cmap_0 *)PreferredSubtable.Subtable; + if((char *)(Cmap0 + 1) <= TableEnd) + { + Cmap0->Length = kbts_ByteSwap16(Cmap0->Length); + Cmap0->Language = kbts_ByteSwap16(Cmap0->Language); + TableValid = 1; + } + } + break; + + case 2: + { + kbts_cmap_2 *Cmap2 = (kbts_cmap_2 *)PreferredSubtable.Subtable; + if(kbts_ByteSwapArray16(&Cmap2->Length, 258, TableEnd)) + { + kbts_un SubHeaderCount = 0; + KBTS_FOR(It, 0, 256) + { + kbts_un SubHeaderIndex = Cmap2->SubHeaderKeys[It]; + SubHeaderCount = KBTS_MAX(SubHeaderCount, SubHeaderIndex + 1); + } + + kbts_sub_header *SubHeaders = KBTS_POINTER_AFTER(kbts_sub_header, Cmap2); + if(kbts_ByteSwapArray16(&SubHeaders->FirstCode, 4 * SubHeaderCount, TableEnd)) + { + kbts_u16 *GlyphIds = (kbts_u16 *)(SubHeaders + SubHeaderCount); + + kbts_sn GlyphIdCount = 0; + KBTS_FOR(It, 0, SubHeaderCount) + { + kbts_sub_header *SubHeader = &SubHeaders[It]; + + kbts_u16 *OnePastLastGlyphId = &SubHeader->IdRangeOffset + SubHeader->IdRangeOffset / 2 + SubHeader->EntryCount; + GlyphIdCount = KBTS_MAX(GlyphIdCount, OnePastLastGlyphId - GlyphIds); + } + + if(kbts_ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount, TableEnd)) + { + TableValid = 1; + } + } + } + } + break; + + case 4: + { + kbts_cmap_4 *Cmap4 = (kbts_cmap_4 *)PreferredSubtable.Subtable; + if(kbts_ByteSwapArray16(&Cmap4->Length, 5, TableEnd) && + kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Cmap4), Cmap4->SegmentCountTimesTwo * 2 + 1, TableEnd)) + { + kbts_un SegmentCount = Cmap4->SegmentCountTimesTwo / 2; + kbts_u16 *EndCodes = KBTS_POINTER_AFTER(kbts_u16, Cmap4); + kbts_u16 *StartCodes = EndCodes + SegmentCount + 1; + kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount); + kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount); + kbts_u16 *GlyphIds = IdRangeOffsets + SegmentCount; + + kbts_sn GlyphIdCount = 0; + + KBTS_FOR(SegmentIndex, 0, SegmentCount) + { + kbts_u16 Offset = IdRangeOffsets[SegmentIndex]; + + if(Offset) + { + kbts_u16 *IdLookup = &IdRangeOffsets[SegmentIndex] + (EndCodes[SegmentIndex] - StartCodes[SegmentIndex] + 1) + Offset / 2; + + GlyphIdCount = KBTS_MAX(GlyphIdCount, (IdLookup - GlyphIds)); + } + } + + if(kbts_ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount, TableEnd)) + { + TableValid = 1; + } + } + } + break; + + case 6: + { + kbts_cmap_6 *Cmap6 = (kbts_cmap_6 *)PreferredSubtable.Subtable; + if(kbts_ByteSwapArray16(&Cmap6->Length, 4, TableEnd) && + kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Cmap6), Cmap6->EntryCount, TableEnd)) + { + TableValid = 1; + } + } + break; + + case 12: + { + kbts_cmap_12_13 *Cmap12 = (kbts_cmap_12_13 *)PreferredSubtable.Subtable; + if(kbts_ByteSwapArray32(&Cmap12->Length, 3, TableEnd) && + kbts_ByteSwapArray32(KBTS_POINTER_AFTER(kbts_u32, Cmap12), Cmap12->GroupCount * 3, TableEnd)) + { + TableValid = 1; + } + } + break; + } + + Font->Cmap = PreferredSubtable.Subtable; + } + } + } } - } - } + break; - *PreferredSubtable.Subtable = kbts_ByteSwap16(*PreferredSubtable.Subtable); - switch(*PreferredSubtable.Subtable) - { - case 0: - { - kbts_cmap_0 *Cmap0 = (kbts_cmap_0 *)PreferredSubtable.Subtable; - Cmap0->Length = kbts_ByteSwap16(Cmap0->Length); - Cmap0->Language = kbts_ByteSwap16(Cmap0->Language); - } - break; - - case 2: - { - kbts_cmap_2 *Cmap2 = (kbts_cmap_2 *)PreferredSubtable.Subtable; - kbts_ByteSwapArray16(&Cmap2->Length, 258); - - kbts_un SubHeaderCount = 0; - KBTS_FOR(It, 0, 256) - { - kbts_un SubHeaderIndex = Cmap2->SubHeaderKeys[It]; - SubHeaderCount = KBTS_MAX(SubHeaderCount, SubHeaderIndex + 1); - } - - kbts_sub_header *SubHeaders = KBTS_POINTER_AFTER(kbts_sub_header, Cmap2); - kbts_ByteSwapArray16(&SubHeaders->FirstCode, 4 * SubHeaderCount); - - kbts_u16 *GlyphIds = (kbts_u16 *)(SubHeaders + SubHeaderCount); - - kbts_sn GlyphIdCount = 0; - KBTS_FOR(It, 0, SubHeaderCount) - { - kbts_sub_header *SubHeader = &SubHeaders[It]; - - kbts_u16 *OnePastLastGlyphId = &SubHeader->IdRangeOffset + SubHeader->IdRangeOffset / 2 + SubHeader->EntryCount; - GlyphIdCount = KBTS_MAX(GlyphIdCount, OnePastLastGlyphId - GlyphIds); - } - - kbts_ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount); - } - break; - - case 4: - { - kbts_cmap_4 *Cmap4 = (kbts_cmap_4 *)PreferredSubtable.Subtable; - kbts_ByteSwapArray16(&Cmap4->Length, 5); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Cmap4), Cmap4->SegmentCountTimesTwo * 2 + 1); - - kbts_un SegmentCount = Cmap4->SegmentCountTimesTwo / 2; - kbts_u16 *EndCodes = KBTS_POINTER_AFTER(kbts_u16, Cmap4); - kbts_u16 *StartCodes = EndCodes + SegmentCount + 1; - kbts_s16 *IdDeltas = (kbts_s16 *)(StartCodes + SegmentCount); - kbts_u16 *IdRangeOffsets = (kbts_u16 *)(IdDeltas + SegmentCount); - kbts_u16 *GlyphIds = IdRangeOffsets + SegmentCount; - - kbts_sn GlyphIdCount = 0; - - KBTS_FOR(SegmentIndex, 0, SegmentCount) - { - kbts_u16 Offset = IdRangeOffsets[SegmentIndex]; - - if(Offset) + case KBTS_FOURCC('G', 'D', 'E', 'F'): { - kbts_u16 *IdLookup = &IdRangeOffsets[SegmentIndex] + (EndCodes[SegmentIndex] - StartCodes[SegmentIndex] + 1) + Offset / 2; + kbts_gdef *Gdef = (kbts_gdef *)TableBase; + GdefSize = Table->Length; - GlyphIdCount = KBTS_MAX(GlyphIdCount, (IdLookup - GlyphIds)); + if(kbts_ByteSwapArray16(&Gdef->Major, 6, TableEnd)) + { + if(Gdef->Minor >= 2) + { + if(Table->Length >= 14) + { + Gdef->MarkGlyphSetsDefinitionOffset = kbts_ByteSwap16(Gdef->MarkGlyphSetsDefinitionOffset); + + if(Gdef->Minor == 3) + { + if(Table->Length >= sizeof(kbts_gdef)) + { + // @Incomplete + Gdef->ItemVariationStoreOffset = kbts_ByteSwap32(Gdef->ItemVariationStoreOffset); + TableValid = 1; + } + } + else + { + TableValid = 1; + } + } + } + else + { + TableValid = 1; + } + } + + Font->Gdef = Gdef; + } + break; + + case KBTS_FOURCC('G', 'S', 'U', 'B'): + case KBTS_FOURCC('G', 'P', 'O', 'S'): + { + // We do not do any bounds checking here because Gsub/Gpos tables get byteswapped later on, + // in ByteSwapGsubGposCommon. + kbts_un Index = (Table->Tag == KBTS_FOURCC('G', 'S', 'U', 'B')) ? KBTS_SHAPING_TABLE_GSUB : KBTS_SHAPING_TABLE_GPOS; + Font->ShapingTables[Index] = (kbts_gsub_gpos *)TableBase; + ShapingTableSizes[Index] = Table->Length; + TableValid = 1; + } break; + + case KBTS_FOURCC('h', 'h', 'e', 'a'): + case KBTS_FOURCC('v', 'h', 'e', 'a'): + { + kbts_un Orientation = Table->Tag == KBTS_FOURCC('h', 'h', 'e', 'a') ? KBTS_ORIENTATION_HORIZONTAL : KBTS_ORIENTATION_VERTICAL; + kbts_hea *Hea = (kbts_hea *)TableBase; + if(kbts_ByteSwapArray16((kbts_u16 *)Hea, sizeof(kbts_hea) / sizeof(kbts_u16), TableEnd)) + { + Font->Hea[Orientation] = Hea; + TableValid = 1; + } + } break; + + case KBTS_FOURCC('h', 'm', 't', 'x'): + case KBTS_FOURCC('v', 'm', 't', 'x'): + { + kbts_un Orientation = Table->Tag == KBTS_FOURCC('h', 'm', 't', 'x') ? KBTS_ORIENTATION_HORIZONTAL : KBTS_ORIENTATION_VERTICAL; + kbts_u16 *Mtx = (kbts_u16 *)TableBase; + kbts_ByteSwapArray16Unchecked(Mtx, Table->Length / sizeof(kbts_u16)); + Font->Mtx[Orientation] = Mtx; + TableValid = 1; + } break; + + case KBTS_FOURCC('m', 'a', 'x', 'p'): + { + if(Table->Length >= 6) + { + Font->Maxp = (kbts_maxp *)TableBase; + Font->Maxp->Major = kbts_ByteSwap16(Font->Maxp->Major); + Font->Maxp->Minor = kbts_ByteSwap16(Font->Maxp->Minor); + + kbts_un U16Count = 0; + if(!Font->Maxp->Major && (Font->Maxp->Minor == 0x5000)) + { + U16Count = 1; + } + else if((Font->Maxp->Major == 1) && !Font->Maxp->Minor) + { + U16Count = 14; + } + + if(kbts_ByteSwapArray16(&Font->Maxp->GlyphCount, U16Count, TableEnd)) + { + Font->GlyphCount = Font->Maxp->GlyphCount; + TableValid = 1; + } + } + } break; + + default: + { + TableValid = 1; + } break; } } - kbts_ByteSwapArray16(GlyphIds, (kbts_un)GlyphIdCount); - } - break; - - case 6: - { - kbts_cmap_6 *Cmap6 = (kbts_cmap_6 *)PreferredSubtable.Subtable; - kbts_ByteSwapArray16(&Cmap6->Length, 4); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, Cmap6), Cmap6->EntryCount); - } - break; - - case 12: - { - kbts_cmap_12_13 *Cmap12 = (kbts_cmap_12_13 *)PreferredSubtable.Subtable; - kbts_ByteSwapArray32(&Cmap12->Length, 3); - kbts_ByteSwapArray32(KBTS_POINTER_AFTER(kbts_u32, Cmap12), Cmap12->GroupCount * 3); - } - break; - } - - Font->Cmap = PreferredSubtable.Subtable; - } - break; - - case KBTS_FOURCC('G', 'D', 'E', 'F'): - { - Font->Gdef = (kbts_gdef *)TableBase; - GdefSize = Table->Length; - - kbts_gdef *Gdef = Font->Gdef; - - kbts_ByteSwapArray16(&Gdef->Major, 6); - - if(Gdef->Minor >= 2) - { - Gdef->MarkGlyphSetsDefinitionOffset = kbts_ByteSwap16(Gdef->MarkGlyphSetsDefinitionOffset); - - if(Gdef->Minor == 3) + if(!TableValid) { - // @Incomplete - Gdef->ItemVariationStoreOffset = kbts_ByteSwap32(Gdef->ItemVariationStoreOffset); + goto Error; } } } - break; - - case KBTS_FOURCC('G', 'S', 'U', 'B'): - case KBTS_FOURCC('G', 'P', 'O', 'S'): + else { - kbts_un Index = (Table->Tag == KBTS_FOURCC('G', 'S', 'U', 'B')) ? KBTS_SHAPING_TABLE_GSUB : KBTS_SHAPING_TABLE_GPOS; - Font->ShapingTables[Index] = (kbts_gsub_gpos *)TableBase; - ShapingTableSizes[Index] = Table->Length; + goto Error; } - break; - case KBTS_FOURCC('h', 'h', 'e', 'a'): - case KBTS_FOURCC('v', 'h', 'e', 'a'): + if(kbts_FontIsValid(Font)) { - kbts_un Orientation = Table->Tag == KBTS_FOURCC('h', 'h', 'e', 'a') ? KBTS_ORIENTATION_HORIZONTAL : KBTS_ORIENTATION_VERTICAL; - Font->Hea[Orientation] = (kbts_hea *)TableBase; - kbts_ByteSwapArray16((kbts_u16 *)Font->Hea[Orientation], Table->Length / sizeof(kbts_u16)); - } - break; - - case KBTS_FOURCC('h', 'm', 't', 'x'): - case KBTS_FOURCC('v', 'm', 't', 'x'): - { - kbts_un Orientation = Table->Tag == KBTS_FOURCC('h', 'm', 't', 'x') ? KBTS_ORIENTATION_HORIZONTAL : KBTS_ORIENTATION_VERTICAL; - Font->Mtx[Orientation] = KBTS_POINTER_OFFSET(kbts_u16, Font->FileBase, Table->Offset); - kbts_ByteSwapArray16(Font->Mtx[Orientation], Table->Length / sizeof(kbts_u16)); - } - break; - - case KBTS_FOURCC('m', 'a', 'x', 'p'): - { - Font->Maxp = KBTS_POINTER_OFFSET(kbts_maxp, Font->FileBase, Table->Offset); - Font->Maxp->Major = kbts_ByteSwap16(Font->Maxp->Major); - Font->Maxp->Minor = kbts_ByteSwap16(Font->Maxp->Minor); - - kbts_un U16Count = 0; - if(!Font->Maxp->Major && (Font->Maxp->Minor == 0x5000)) - { - U16Count = 1; - } - else if((Font->Maxp->Major == 1) && !Font->Maxp->Minor) - { - U16Count = 14; - } - - kbts_ByteSwapArray16(&Font->Maxp->GlyphCount, U16Count); - Font->GlyphCount = Font->Maxp->GlyphCount; - } - break; + Result = sizeof(kbts_u32) * ((ShapingTableSizes[KBTS_SHAPING_TABLE_GSUB] + ShapingTableSizes[KBTS_SHAPING_TABLE_GPOS] + GdefSize) / 2); } } + else + { + goto Error; + } + + if(0) + { + Error:; + Font->Error = 1; + } - kbts_un Result = sizeof(kbts_u32) * ((ShapingTableSizes[KBTS_SHAPING_TABLE_GSUB] + ShapingTableSizes[KBTS_SHAPING_TABLE_GPOS] + GdefSize) / 2); return (kbts_u32)Result; } KBTS_EXPORT kbts_un kbts_ReadFontData(kbts_font *Font, void *Scratch, kbts_un ScratchSize) { - kbts_byteswap_context ByteSwapContext = KBTS_ZERO; - ByteSwapContext.FileBase = Font->FileBase; - ByteSwapContext.PointerCapacity = ScratchSize / sizeof(kbts_u32); - ByteSwapContext.Pointers = (kbts_u32 *)Scratch; - - kbts_un TotalLookupCount = 0; - kbts_un TotalSubtableCount = 0; - - kbts_gdef *Gdef = Font->Gdef; - if(Gdef) + kbts_un Result = 0; + if(kbts_FontIsValid(Font)) { - if(Gdef->ClassDefinitionOffset) - { - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); - kbts_ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); - } + kbts_byteswap_context ByteSwapContext = KBTS_ZERO; + ByteSwapContext.FileBase = Font->FileBase; + ByteSwapContext.FileEnd = Font->FileBase + Font->FileSize; + ByteSwapContext.PointerCapacity = ScratchSize / sizeof(kbts_u32); + ByteSwapContext.Pointers = (kbts_u32 *)Scratch; - if(Gdef->MarkAttachmentClassDefinitionOffset) - { - kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); - kbts_ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); - } + kbts_un TotalLookupCount = 0; + kbts_un TotalSubtableCount = 0; - if((Gdef->Minor >= 2) && Gdef->MarkGlyphSetsDefinitionOffset) + kbts_gdef *Gdef = Font->Gdef; + if(Gdef) { - kbts_mark_glyph_sets *MarkGlyphSets = KBTS_POINTER_OFFSET(kbts_mark_glyph_sets, Gdef, Gdef->MarkGlyphSetsDefinitionOffset); - kbts_ByteSwapArray16(&MarkGlyphSets->Format, 2); - if(MarkGlyphSets->Format == 1) + if(Gdef->ClassDefinitionOffset) { - kbts_u32 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u32, MarkGlyphSets); - kbts_ByteSwapArray32(CoverageOffsets, MarkGlyphSets->MarkGlyphSetCount); + kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->ClassDefinitionOffset); + kbts_ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); + } - KBTS_FOR(MarkGlyphSetIndex, 0, MarkGlyphSets->MarkGlyphSetCount) + if(Gdef->MarkAttachmentClassDefinitionOffset) + { + kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Gdef, Gdef->MarkAttachmentClassDefinitionOffset); + kbts_ByteSwapClassDefinition(&ByteSwapContext, ClassDefBase); + } + + if((Gdef->Minor >= 2) && Gdef->MarkGlyphSetsDefinitionOffset) + { + kbts_mark_glyph_sets *MarkGlyphSets = KBTS_POINTER_OFFSET(kbts_mark_glyph_sets, Gdef, Gdef->MarkGlyphSetsDefinitionOffset); + kbts_ByteSwapArray16Context(&MarkGlyphSets->Format, 2, &ByteSwapContext); + if(MarkGlyphSets->Format == 1) { - kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, MarkGlyphSets, CoverageOffsets[MarkGlyphSetIndex]); - kbts_ByteSwapCoverage(&ByteSwapContext, Coverage); + kbts_u32 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u32, MarkGlyphSets); + kbts_ByteSwapArray32Context(CoverageOffsets, MarkGlyphSets->MarkGlyphSetCount, &ByteSwapContext); + + KBTS_FOR(MarkGlyphSetIndex, 0, MarkGlyphSets->MarkGlyphSetCount) + { + kbts_coverage *Coverage = KBTS_POINTER_OFFSET(kbts_coverage, MarkGlyphSets, CoverageOffsets[MarkGlyphSetIndex]); + kbts_ByteSwapCoverage(&ByteSwapContext, Coverage); + } } } } - } - kbts_gsub_gpos *Gsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; - if(Gsub) - { - kbts_ByteSwapGsubGposCommon(&ByteSwapContext, Gsub); - - kbts_lookup_list *LookupList = kbts_GetLookupList(Gsub); - LookupList->Count = kbts_ByteSwap16(LookupList->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, LookupList), LookupList->Count); - - TotalLookupCount += LookupList->Count; - - KBTS_FOR(LookupIndex, 0, LookupList->Count) + kbts_gsub_gpos *Gsub = Font->ShapingTables[KBTS_SHAPING_TABLE_GSUB]; + if(Gsub) { - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); + kbts_ByteSwapGsubGposCommon(&ByteSwapContext, Gsub); - KBTS_DUMPF("GSUB Lookup %llu:\n", LookupIndex); + kbts_lookup_list *LookupList = kbts_GetLookupList(Gsub); + LookupList->Count = kbts_ByteSwap16(LookupList->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, LookupList), LookupList->Count, &ByteSwapContext); - if(kbts_ByteSwapLookup(&ByteSwapContext, PackedLookup)) + TotalLookupCount += LookupList->Count; + + KBTS_FOR(LookupIndex, 0, LookupList->Count) { - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); - KBTS_DUMPF(" Flags %u\n", Lookup.Flags); + kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); - KBTS_FOR(SubstitutionIndex, 0, Lookup.SubtableCount) + KBTS_DUMPF("GSUB Lookup %llu:\n", LookupIndex); + + if(kbts_ByteSwapLookup(&ByteSwapContext, PackedLookup)) { - kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); + kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); + KBTS_DUMPF(" Flags %u\n", Lookup.Flags); - KBTS_DUMPF(" Subtable %llu:\n", SubstitutionIndex); + KBTS_FOR(SubstitutionIndex, 0, Lookup.SubtableCount) + { + kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); - kbts_ByteSwapGsubLookupSubtable(&ByteSwapContext, Lookup.Type, Base); + KBTS_DUMPF(" Subtable %llu:\n", SubstitutionIndex); + + kbts_ByteSwapGsubLookupSubtable(&ByteSwapContext, Lookup.Type, Base); + } } + + TotalSubtableCount += PackedLookup->SubtableCount; } - - TotalSubtableCount += PackedLookup->SubtableCount; } - } - kbts_gsub_gpos *Gpos = Font->ShapingTables[KBTS_SHAPING_TABLE_GPOS]; - if(Gpos) - { - kbts_ByteSwapGsubGposCommon(&ByteSwapContext, Gpos); - - kbts_lookup_list *LookupList = kbts_GetLookupList(Gpos); - LookupList->Count = kbts_ByteSwap16(LookupList->Count); - kbts_ByteSwapArray16(KBTS_POINTER_AFTER(kbts_u16, LookupList), LookupList->Count); - - TotalLookupCount += LookupList->Count; - - KBTS_FOR(LookupIndex, 0, LookupList->Count) + kbts_gsub_gpos *Gpos = Font->ShapingTables[KBTS_SHAPING_TABLE_GPOS]; + if(Gpos) { - kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); + kbts_ByteSwapGsubGposCommon(&ByteSwapContext, Gpos); - KBTS_DUMPF("GPOS Lookup %llu:\n", LookupIndex); + kbts_lookup_list *LookupList = kbts_GetLookupList(Gpos); + LookupList->Count = kbts_ByteSwap16(LookupList->Count); + kbts_ByteSwapArray16Context(KBTS_POINTER_AFTER(kbts_u16, LookupList), LookupList->Count, &ByteSwapContext); - if(kbts_ByteSwapLookup(&ByteSwapContext, PackedLookup)) + TotalLookupCount += LookupList->Count; + + KBTS_FOR(LookupIndex, 0, LookupList->Count) { - kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); + kbts_lookup *PackedLookup = kbts_GetLookup(LookupList, LookupIndex); - KBTS_DUMPF(" Flags %x\n", Lookup.Flags); + KBTS_DUMPF("GPOS Lookup %llu:\n", LookupIndex); - KBTS_FOR(SubstitutionIndex, 0, Lookup.SubtableCount) + if(kbts_ByteSwapLookup(&ByteSwapContext, PackedLookup)) { - kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); + kbts_unpacked_lookup Lookup = kbts_UnpackLookup(Font->Gdef, PackedLookup); - KBTS_DUMPF(" Subtable %zu:\n", (size_t)SubstitutionIndex); + KBTS_DUMPF(" Flags %x\n", Lookup.Flags); - kbts_ByteSwapGposLookupSubtable(&ByteSwapContext, LookupList, Lookup.Type, Base); + KBTS_FOR(SubstitutionIndex, 0, Lookup.SubtableCount) + { + kbts_u16 *Base = KBTS_POINTER_OFFSET(kbts_u16, PackedLookup, Lookup.SubtableOffsets[SubstitutionIndex]); + + KBTS_DUMPF(" Subtable %llu:\n", (kbts_un)SubstitutionIndex); + + kbts_ByteSwapGposLookupSubtable(&ByteSwapContext, LookupList, Lookup.Type, Base); + } } + + TotalSubtableCount += PackedLookup->SubtableCount; } - - TotalSubtableCount += PackedLookup->SubtableCount; } - } - // At this point, we are done byteswapping the file, so we can start reusing the scratch memory. + // At this point, we are done byteswapping the file, so we can start reusing the scratch memory. - if(Gsub) - { - kbts_lookup_list *LookupList = kbts_GetLookupList(Gsub); - - // Figure out lookup recursion depth and other useful metrics. - // Most of these are not used yet, but would be useful for a streaming shaper and/or to inform GLYPH_BUFFER_GROW_MARGIN. - kbts_un MaximumBacktrackWithoutSkippingGlyphs = 0; - kbts_un MaximumLookaheadWithoutSkippingGlyphs = 0; - kbts_un MaximumLookupStackSize = 1; - kbts_un MaximumSubstitutionOutputSize = 1; - kbts_un MaximumInputSequenceLength = 1; - - // We are done byteswapping the file, so we can reclaim the scratch memory. - kbts_lookup_info_frame *Frames = (kbts_lookup_info_frame *)Scratch; - kbts_un FrameCapacity = ScratchSize / sizeof(kbts_lookup_info_frame); - - KBTS_FOR(RootLookupIndex, 0, LookupList->Count) + if(Gsub) { - kbts_lookup *PackedRootLookup = kbts_GetLookup(LookupList, RootLookupIndex); - kbts_unpacked_lookup RootLookup = kbts_UnpackLookup(Gdef, PackedRootLookup); + kbts_lookup_list *LookupList = kbts_GetLookupList(Gsub); - KBTS_FOR(RootSubtableIndex, 0, RootLookup.SubtableCount) + // Figure out lookup recursion depth and other useful metrics. + // Most of these are not used yet, but would be useful for a streaming shaper and/or to inform GLYPH_BUFFER_GROW_MARGIN. + kbts_un MaximumBacktrackWithoutSkippingGlyphs = 0; + kbts_un MaximumLookaheadWithoutSkippingGlyphs = 0; + kbts_un MaximumLookupStackSize = 1; + kbts_un MaximumSubstitutionOutputSize = 1; + kbts_un MaximumInputSequenceLength = 1; + + // We are done byteswapping the file, so we can reclaim the scratch memory. + kbts_lookup_info_frame *Frames = (kbts_lookup_info_frame *)Scratch; + kbts_un FrameCapacity = ScratchSize / sizeof(kbts_lookup_info_frame); + + KBTS_FOR(RootLookupIndex, 0, LookupList->Count) { - kbts_un FrameCount = 0; + kbts_lookup *PackedRootLookup = kbts_GetLookup(LookupList, RootLookupIndex); + kbts_unpacked_lookup RootLookup = kbts_UnpackLookup(Gdef, PackedRootLookup); + + KBTS_FOR(RootSubtableIndex, 0, RootLookup.SubtableCount) { - kbts_lookup_info_frame First = KBTS_ZERO; - First.LookupType = RootLookup.Type; - First.Base = KBTS_POINTER_OFFSET(kbts_u16, PackedRootLookup, RootLookup.SubtableOffsets[RootSubtableIndex]); - First.StackSize = 1; - if(FrameCount < FrameCapacity) + kbts_un FrameCount = 0; { - Frames[FrameCount++] = First; - } - else - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - - while(FrameCount) - { - kbts_lookup_info_frame Frame = Frames[--FrameCount]; - kbts_u16 LookupType = Frame.LookupType; - kbts_u16 *Base = Frame.Base; - kbts_u32 LookaheadOffset = Frame.LookaheadOffset; - kbts_u32 StackSize = Frame.StackSize; - - MaximumLookupStackSize = KBTS_MAX(MaximumLookupStackSize, StackSize); - - while(LookupType == 7) - { - kbts_extension *Subst = (kbts_extension *)Base; - - Base = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->Offset); - LookupType = Subst->LookupType; - } - - kbts_u32 Result = 1; - - switch(LookupType) - { - case 2: - { - kbts_multiple_substitution *Subst = (kbts_multiple_substitution *)Base; - KBTS_FOR(SequenceIndex, 0, Subst->SequenceCount) + kbts_lookup_info_frame First = KBTS_ZERO; + First.LookupType = RootLookup.Type; + First.Base = KBTS_POINTER_OFFSET(kbts_u16, PackedRootLookup, RootLookup.SubtableOffsets[RootSubtableIndex]); + First.StackSize = 1; + if(FrameCount < FrameCapacity) { - kbts_sequence *Sequence = kbts_GetSequence(Subst, SequenceIndex); - - MaximumSubstitutionOutputSize = KBTS_MAX(MaximumSubstitutionOutputSize, Sequence->GlyphCount); + Frames[FrameCount++] = First; } - } break; - - case 4: - { - kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Base; - - KBTS_FOR(SetIndex, 0, Subst->LigatureSetCount) + else { - kbts_ligature_set *Set = kbts_GetLigatureSet(Subst, SetIndex); + Font->Error = 1; + goto DoneGatheringLookupInfo; + } + } - KBTS_FOR(LigatureIndex, 0, Set->Count) + while(FrameCount) + { + kbts_lookup_info_frame Frame = Frames[--FrameCount]; + kbts_u16 LookupType = Frame.LookupType; + kbts_u16 *Base = Frame.Base; + kbts_u32 LookaheadOffset = Frame.LookaheadOffset; + kbts_u32 StackSize = Frame.StackSize; + + MaximumLookupStackSize = KBTS_MAX(MaximumLookupStackSize, StackSize); + + while(LookupType == 7) + { + kbts_extension *Subst = (kbts_extension *)Base; + + Base = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->Offset); + LookupType = Subst->LookupType; + } + + switch(LookupType) + { + case 2: + { + kbts_multiple_substitution *Subst = (kbts_multiple_substitution *)Base; + KBTS_FOR(SequenceIndex, 0, Subst->SequenceCount) { - kbts_ligature *Ligature = kbts_GetLigature(Set, LigatureIndex); + kbts_sequence *Sequence = kbts_GetSequence(Subst, SequenceIndex); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Ligature->ComponentCount - 1); + MaximumSubstitutionOutputSize = KBTS_MAX(MaximumSubstitutionOutputSize, Sequence->GlyphCount); } - } - } break; + } break; - case 5: - { - if(Base[0] == 1) + case 4: { - kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; + kbts_ligature_substitution *Subst = (kbts_ligature_substitution *)Base; - KBTS_FOR(SetIndex, 0, Subst->SeqRuleSetCount) + KBTS_FOR(SetIndex, 0, Subst->LigatureSetCount) { - kbts_sequence_rule_set *Set = kbts_GetSequenceRuleSet(Subst, SetIndex); + kbts_ligature_set *Set = kbts_GetLigatureSet(Subst, SetIndex); - KBTS_FOR(RuleIndex, 0, Set->Count) + KBTS_FOR(LigatureIndex, 0, Set->Count) { - kbts_sequence_rule *Rule = kbts_GetSequenceRule(Set, RuleIndex); + kbts_ligature *Ligature = kbts_GetLigature(Set, LigatureIndex); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Rule->GlyphCount - 1); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Rule->GlyphCount); - - kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); - kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); - KBTS_FOR(RecordIndex, 0, Rule->SequenceLookupCount) - { - kbts_sequence_lookup_record *Record = &Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } + MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Ligature->ComponentCount - 1); } } - } - else if(Base[0] == 2) + } break; + + case 5: { - kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) + if(Base[0] == 1) { - kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, SetIndex); + kbts_sequence_context_1 *Subst = (kbts_sequence_context_1 *)Base; - if(Set) + KBTS_FOR(SetIndex, 0, Subst->SeqRuleSetCount) { + kbts_sequence_rule_set *Set = kbts_GetSequenceRuleSet(Subst, SetIndex); + KBTS_FOR(RuleIndex, 0, Set->Count) { - kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); + kbts_sequence_rule *Rule = kbts_GetSequenceRule(Set, RuleIndex); MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Rule->GlyphCount - 1); MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Rule->GlyphCount); kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); - KBTS_FOR(RecordIndex, 0, Rule->SequenceLookupCount) { kbts_sequence_lookup_record *Record = &Records[RecordIndex]; @@ -20609,144 +22226,179 @@ KBTS_EXPORT kbts_un kbts_ReadFontData(kbts_font *Font, void *Scratch, kbts_un Sc } } } - } - else if(Base[0] == 3) - { - kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Subst->GlyphCount - 1); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Subst->GlyphCount); - - kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); - kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(CoverageOffsets + Subst->GlyphCount); - - KBTS_FOR(RecordIndex, 0, Subst->SequenceLookupCount) + else if(Base[0] == 2) { - kbts_sequence_lookup_record *Record = &Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; + + KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } - } - } break; + kbts_class_sequence_rule_set *Set = kbts_GetClassSequenceRuleSet(Subst, SetIndex); - case 6: - { - if(Base[0] == 1) - { - kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) - { - kbts_chained_sequence_rule_set *Set = kbts_GetChainedSequenceRuleSet(Subst, SetIndex); - - if(Set) - { - KBTS_FOR(RuleIndex, 0, Set->Count) + if(Set) { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); - - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); - - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) + KBTS_FOR(RuleIndex, 0, Set->Count) { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + kbts_class_sequence_rule *Rule = kbts_GetClassSequenceRule(Set, RuleIndex); + + MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Rule->GlyphCount - 1); + MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Rule->GlyphCount); + + kbts_u16 *InputSequence = KBTS_POINTER_AFTER(kbts_u16, Rule); + kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(InputSequence + Rule->GlyphCount - 1); + + KBTS_FOR(RecordIndex, 0, Rule->SequenceLookupCount) { - Font->Error = 1; - goto DoneGatheringLookupInfo; + kbts_sequence_lookup_record *Record = &Records[RecordIndex]; + if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + { + Font->Error = 1; + goto DoneGatheringLookupInfo; + } } } } } } - } - else if(Base[0] == 2) - { - kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; - - KBTS_FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) + else if(Base[0] == 3) { - // @Duplication with 6.1. - kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, SetIndex); + kbts_sequence_context_3 *Subst = (kbts_sequence_context_3 *)Base; - if(Set) + MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Subst->GlyphCount - 1); + MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Subst->GlyphCount); + + kbts_u16 *CoverageOffsets = KBTS_POINTER_AFTER(kbts_u16, Subst); + kbts_sequence_lookup_record *Records = (kbts_sequence_lookup_record *)(CoverageOffsets + Subst->GlyphCount); + + KBTS_FOR(RecordIndex, 0, Subst->SequenceLookupCount) { - KBTS_FOR(RuleIndex, 0, Set->Count) + kbts_sequence_lookup_record *Record = &Records[RecordIndex]; + if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) { - kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); - kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); + Font->Error = 1; + goto DoneGatheringLookupInfo; + } + } + } + } break; - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); + case 6: + { + if(Base[0] == 1) + { + kbts_chained_sequence_context_1 *Subst = (kbts_chained_sequence_context_1 *)Base; - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) + KBTS_FOR(SetIndex, 0, Subst->ChainedSequenceRuleSetCount) + { + kbts_chained_sequence_rule_set *Set = kbts_GetChainedSequenceRuleSet(Subst, SetIndex); + + if(Set) + { + KBTS_FOR(RuleIndex, 0, Set->Count) { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); + kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); + + MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); + MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); + MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); + + KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) { - Font->Error = 1; - goto DoneGatheringLookupInfo; + kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; + if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + { + Font->Error = 1; + goto DoneGatheringLookupInfo; + } } } } } } - } - else if(Base[0] == 3) + else if(Base[0] == 2) + { + kbts_chained_sequence_context_2 *Subst = (kbts_chained_sequence_context_2 *)Base; + + KBTS_FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) + { + // @Duplication with 6.1. + kbts_chained_sequence_rule_set *Set = kbts_GetChainedClassSequenceRuleSet(Subst, SetIndex); + + if(Set) + { + KBTS_FOR(RuleIndex, 0, Set->Count) + { + kbts_chained_sequence_rule *Rule = kbts_GetChainedSequenceRule(Set, RuleIndex); + kbts_unpacked_chained_sequence_rule Unpacked = kbts_UnpackChainedSequenceRule(Rule, 0); + + MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); + MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); + MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); + + KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) + { + kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; + if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + { + Font->Error = 1; + goto DoneGatheringLookupInfo; + } + } + } + } + } + } + else if(Base[0] == 3) + { + kbts_chained_sequence_context_3 *Subst = (kbts_chained_sequence_context_3 *)Base; + kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 0); + + MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); + MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); + MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); + + KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) + { + kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; + if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) + { + Font->Error = 1; + goto DoneGatheringLookupInfo; + } + } + } + } break; + + case 8: { - kbts_chained_sequence_context_3 *Subst = (kbts_chained_sequence_context_3 *)Base; - kbts_unpacked_chained_sequence_context_3 Unpacked = kbts_UnpackChainedSequenceContext3(Subst, 0); + kbts_reverse_chain_substitution *Subst = (kbts_reverse_chain_substitution *)Base; + kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 0); MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - MaximumInputSequenceLength = KBTS_MAX(MaximumInputSequenceLength, Unpacked.InputCount); - - KBTS_FOR(RecordIndex, 0, Unpacked.RecordCount) - { - kbts_sequence_lookup_record *Record = &Unpacked.Records[RecordIndex]; - if(!kbts_PushLookup(Gdef, Frames, &FrameCount, FrameCapacity, kbts_GetLookup(LookupList, Record->LookupListIndex), LookaheadOffset + Record->SequenceIndex, StackSize + RecordIndex + 1)) - { - Font->Error = 1; - goto DoneGatheringLookupInfo; - } - } + } break; } - } break; - - case 8: - { - kbts_reverse_chain_substitution *Subst = (kbts_reverse_chain_substitution *)Base; - kbts_unpacked_reverse_chain_substitution Unpacked = kbts_UnpackReverseChainSubstitution(Subst, 0); - - MaximumBacktrackWithoutSkippingGlyphs = KBTS_MAX(MaximumBacktrackWithoutSkippingGlyphs, Unpacked.BacktrackCount); - MaximumLookaheadWithoutSkippingGlyphs = KBTS_MAX(MaximumLookaheadWithoutSkippingGlyphs, LookaheadOffset + Unpacked.LookaheadCount); - } break; } } } + + DoneGatheringLookupInfo:; + Font->LookupInfo.MaximumBacktrackWithoutSkippingGlyphs = (kbts_u32)MaximumBacktrackWithoutSkippingGlyphs; + Font->LookupInfo.MaximumLookaheadWithoutSkippingGlyphs = (kbts_u32)MaximumLookaheadWithoutSkippingGlyphs; + Font->LookupInfo.MaximumSubstitutionOutputSize = (kbts_u32)MaximumSubstitutionOutputSize; + Font->LookupInfo.MaximumInputSequenceLength = (kbts_u32)MaximumInputSequenceLength; + Font->LookupInfo.MaximumLookupStackSize = (kbts_u32)MaximumLookupStackSize; } - DoneGatheringLookupInfo:; - Font->LookupInfo.MaximumBacktrackWithoutSkippingGlyphs = (kbts_u32)MaximumBacktrackWithoutSkippingGlyphs; - Font->LookupInfo.MaximumLookaheadWithoutSkippingGlyphs = (kbts_u32)MaximumLookaheadWithoutSkippingGlyphs; - Font->LookupInfo.MaximumSubstitutionOutputSize = (kbts_u32)MaximumSubstitutionOutputSize; - Font->LookupInfo.MaximumInputSequenceLength = (kbts_u32)MaximumInputSequenceLength; - Font->LookupInfo.MaximumLookupStackSize = (kbts_u32)MaximumLookupStackSize; + Font->LookupCount = (kbts_u32)TotalLookupCount; + Font->SubtableCount = (kbts_u32)TotalSubtableCount; + + kbts_un GlyphLookupMatrixSizeInBytes = ((((TotalLookupCount * Font->GlyphCount) + 7) / 8) + 3) & ~3; + kbts_un GlyphLookupSubtableMatrixSizeInBytes = ((((TotalSubtableCount * Font->GlyphCount) + 7) / 8) + 3) & ~3; + Result = GlyphLookupMatrixSizeInBytes + GlyphLookupSubtableMatrixSizeInBytes + sizeof(kbts_u32) * TotalLookupCount + sizeof(kbts_lookup_subtable_info) * TotalSubtableCount; + + Font->Error |= ByteSwapContext.Error; } - - Font->LookupCount = (kbts_u32)TotalLookupCount; - Font->SubtableCount = (kbts_u32)TotalSubtableCount; - - kbts_un GlyphLookupMatrixSizeInBytes = ((((TotalLookupCount * Font->GlyphCount) + 7) / 8) + 3) & ~3; - kbts_un GlyphLookupSubtableMatrixSizeInBytes = ((((TotalSubtableCount * Font->GlyphCount) + 7) / 8) + 3) & ~3; - kbts_un Result = GlyphLookupMatrixSizeInBytes + GlyphLookupSubtableMatrixSizeInBytes + sizeof(kbts_u32) * TotalLookupCount + sizeof(kbts_lookup_subtable_info) * TotalSubtableCount; return Result; } @@ -20765,7 +22417,7 @@ KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_ kbts_un GlyphLookupSubtableMatrixSizeInBytes = (GlyphLookupSubtableMatrixSizeInBits + 7) / 8; GlyphLookupSubtableMatrixSizeInBytes = (GlyphLookupSubtableMatrixSizeInBytes + 3) & ~3; // Align to u32 - memset(Memory, 0, MemorySize); + KBTS_MEMSET(Memory, 0, MemorySize); kbts_u32 *GlyphLookupMatrix = (kbts_u32 *)Memory; kbts_u32 *GlyphLookupSubtableMatrix = KBTS_POINTER_OFFSET(kbts_u32, GlyphLookupMatrix, GlyphLookupMatrixSizeInBytes); @@ -20893,7 +22545,7 @@ KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_ { kbts_sequence_context_2 *Subst = (kbts_sequence_context_2 *)Base; kbts_u16 *ClassDefBase = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->ClassDefOffset); - kbts_u32 Class = kbts_GlyphClassFromTable(ClassDefBase, Glyph.Id); + kbts_glyph_class_from_table_result Class = kbts_GlyphClassFromTable(ClassDefBase, Glyph.Id); KBTS_FOR(SetIndex, 0, Subst->ClassSequenceRuleSetCount) { @@ -20909,7 +22561,7 @@ KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_ KBTS_FOR(SequenceIndex, 1, Rule->GlyphCount) { - if(SequenceClasses[SequenceIndex - 1] == Class) + if(SequenceClasses[SequenceIndex - 1] == Class.Class) { InSecondary = 1; goto DoneCheckingForInclusion; @@ -20999,9 +22651,9 @@ KBTS_EXPORT int kbts_PostReadFontInitialize(kbts_font *Font, void *Memory, kbts_ kbts_u16 *InputClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->InputClassDefOffset); kbts_u16 *LookaheadClassDefinition = KBTS_POINTER_OFFSET(kbts_u16, Subst, Subst->LookaheadClassDefOffset); - kbts_u16 BacktrackClass = kbts_GlyphClassFromTable(BacktrackClassDefinition, Glyph.Id); - kbts_u16 InputClass = kbts_GlyphClassFromTable(InputClassDefinition, Glyph.Id); - kbts_u16 LookaheadClass = kbts_GlyphClassFromTable(LookaheadClassDefinition, Glyph.Id); + kbts_u16 BacktrackClass = kbts_GlyphClassFromTable(BacktrackClassDefinition, Glyph.Id).Class; + kbts_u16 InputClass = kbts_GlyphClassFromTable(InputClassDefinition, Glyph.Id).Class; + kbts_u16 LookaheadClass = kbts_GlyphClassFromTable(LookaheadClassDefinition, Glyph.Id).Class; KBTS_FOR(SetIndex, 0, Subst->ChainedClassSequenceRuleSetCount) { @@ -22127,20 +23779,22 @@ static void kbts_BreakAddCodepoint_(kbts_break_state *State, kbts_u32 Codepoint, #undef KBTS_LINE_BREAK #undef KBTS_LINE_UNBREAK - kbts_u64 EffectiveLineBreaks = LineBreaks & ~(LineUnbreaks | LineUnbreaksAsync); - - kbts_DoLineBreak(State, PositionOffset3 + LineBreak3PositionOffset, EffectiveLineBreaks >> 48, 0, 0); - if(EndOfText) { - kbts_DoLineBreak(State, PositionOffset2 + LineBreak2PositionOffset, EffectiveLineBreaks >> 32, 0, 0); - { // @Cleanup: This is the same flag code as DoLineBreak, but we want to use FlagState buffering for this. - // The only places where we want to manually call DoBreak is for asynchronous/buffered guys. - kbts_u8 FlushFlags = 0; - if((EffectiveLineBreaks >> 16) & KBTS_LINE_BREAK_ALLOWED_MASK) FlushFlags |= KBTS_BREAK_FLAG_LINE_SOFT; - if((EffectiveLineBreaks >> 16) & KBTS_LINE_BREAK_REQUIRED_MASK) FlushFlags |= KBTS_BREAK_FLAG_LINE_HARD; - KBTS_BREAK(FlushFlags, 1); + kbts_u64 EffectiveLineBreaks = LineBreaks & ~(LineUnbreaks | LineUnbreaksAsync); + + kbts_DoLineBreak(State, PositionOffset3 + LineBreak3PositionOffset, EffectiveLineBreaks >> 48, 0, 0); + if(EndOfText) + { + kbts_DoLineBreak(State, PositionOffset2 + LineBreak2PositionOffset, EffectiveLineBreaks >> 32, 0, 0); + { // @Cleanup: This is the same flag code as DoLineBreak, but we want to use FlagState buffering for this. + // The only places where we want to manually call DoBreak is for asynchronous/buffered guys. + kbts_u8 FlushFlags = 0; + if((EffectiveLineBreaks >> 16) & KBTS_LINE_BREAK_ALLOWED_MASK) FlushFlags |= KBTS_BREAK_FLAG_LINE_SOFT; + if((EffectiveLineBreaks >> 16) & KBTS_LINE_BREAK_REQUIRED_MASK) FlushFlags |= KBTS_BREAK_FLAG_LINE_HARD; + KBTS_BREAK(FlushFlags, 1); + } + // Lines are never broken after the end of text. } - // Lines are never broken after the end of text. } State->LineBreaks = LineBreaks; @@ -22225,13 +23879,13 @@ KBTS_EXPORT void kbts_BeginBreak(kbts_break_state *State, kbts_direction MainDir { if(State) { - memset(State, 0, sizeof(*State)); + KBTS_MEMSET(State, 0, sizeof(*State)); State->MainDirection = (kbts_u8)MainDirection; State->JapaneseLineBreakStyle = JapaneseLineBreakStyle; } } -KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, size_t Length) +KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, kbts_un Length) { kbts_decode Result = KBTS_ZERO; const char *Utf8Start = Utf8; @@ -22279,8 +23933,8 @@ KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, size_t Length) FollowupCharacterCount = 3; Result.Codepoint = First & 7; Result.Valid = 1; - -} break; + } + break; } if(Length > FollowupCharacterCount) @@ -22312,9 +23966,9 @@ KBTS_EXPORT kbts_decode kbts_DecodeUtf8(const char *Utf8, size_t Length) return Result; } -KBTS_EXPORT kbts_u32 kbts_ShaperIsComplex(kbts_shaper Shaper) +KBTS_EXPORT int kbts_ShaperIsComplex(kbts_shaper Shaper) { - kbts_u32 Result = Shaper != KBTS_SHAPER_DEFAULT; // @Incomplete? + int Result = Shaper != KBTS_SHAPER_DEFAULT; // @Incomplete? return Result; } From e8b2f1eeaac191fa4edc8c06fa6a4f232666be65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Jul 2025 15:25:05 +0100 Subject: [PATCH 021/160] Fix indentation --- vendor/kb_text_shape/kb_text_shape_types.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/kb_text_shape/kb_text_shape_types.odin b/vendor/kb_text_shape/kb_text_shape_types.odin index 952393e57..929af64d2 100644 --- a/vendor/kb_text_shape/kb_text_shape_types.odin +++ b/vendor/kb_text_shape/kb_text_shape_types.odin @@ -1853,7 +1853,7 @@ op_state_op_specific :: struct #raw_union { } lookup_indices :: struct { - FeatureTag: feature_tag, + FeatureTag: feature_tag, FeatureId: feature_id, SkipFlags: skip_flags, GlyphFilter: glyph_flags, From edc01293b245b00af53c069162cd1966adeb6d5c Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 16 Jul 2025 17:13:23 -0400 Subject: [PATCH 022/160] Allow objective-c procedures to have their `@objc_name` attribute inferred. The `@objc_name` is automatically inferred if it is not specified and the procedure name is prefixed with type name specified in `@objc_type`, followed by an `_`. What followed the `_` is interpreted as the `@objc_name`. --- src/check_decl.cpp | 206 +++++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 99 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3d0d95556..0bacf891b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1001,119 +1001,127 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext &ac) { - if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) { + if (!ac.objc_type) { return; } - if (ac.objc_name.len == 0 && ac.objc_is_class_method) { - error(e->token, "@(objc_name) is required with @(objc_is_class_method)"); - } else if (ac.objc_type == nullptr) { - error(e->token, "@(objc_name) requires that @(objc_type) to be set"); - } else if (ac.objc_name.len == 0 && ac.objc_type) { - error(e->token, "@(objc_name) is required with @(objc_type)"); - } else { - Type *t = ac.objc_type; - GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage. - Entity *tn = t->Named.type_name; + Type *t = ac.objc_type; + GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage. - GB_ASSERT(tn->kind == Entity_TypeName); + // Attempt to infer th objc_name automatically if the proc name contains + // the type name objc_type's name, followed by an underscore, as a prefix. + if (ac.objc_name.len == 0) { + String proc_name = e->token.string; + String type_name = t->Named.name; - if (tn->scope != e->scope) { - error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); + if (proc_name.len > type_name.len + 1 && + proc_name[type_name.len] == '_' && + str_eq(type_name, substring(proc_name, 0, type_name.len)) + ) { + ac.objc_name = substring(proc_name, type_name.len+1, proc_name.len); } else { + error(e->token, "@(objc_name) requires that @(objc_type) be set or inferred " + "by prefixing the proc name with the type and underscore: MyObjcType_myProcName :: proc()."); + } + } - // Enable implementation by default if the class is an implementer too and - // @objc_implement was not set to false explicitly in this proc. - bool implement = tn->TypeName.objc_is_implementation; - if (ac.objc_is_disabled_implement) { - implement = false; - } + Entity *tn = t->Named.type_name; + GB_ASSERT(tn->kind == Entity_TypeName); - if (implement) { - GB_ASSERT(e->kind == Entity_Procedure); + if (tn->scope != e->scope) { + error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope"); + } else { + // Enable implementation by default if the class is an implementer too and + // @objc_implement was not set to false explicitly in this proc. + bool implement = tn->TypeName.objc_is_implementation; + if (ac.objc_is_disabled_implement) { + implement = false; + } - auto &proc = e->type->Proc; - Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil; + if (implement) { + GB_ASSERT(e->kind == Entity_Procedure); - if (!tn->TypeName.objc_is_implementation) { - error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied"); - } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { - error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)"); - } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { - error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set"); - } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) { - error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention"); - } else if (proc.result_count > 1) { - error(e->token, "Objective-C method implementations may return at most 1 value"); - } else { - // Always export unconditionally - // NOTE(harold): This means check_objc_methods() MUST be called before - // e->Procedure.is_export is set in check_proc_decl()! - if (ac.is_export) { - error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly"); - } - if (ac.link_name != "") { - error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly"); - } + auto &proc = e->type->Proc; + Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil; - ac.is_export = true; - ac.linkage = STR_LIT("strong"); - - auto method = ObjcMethodData{ ac, e }; - method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; - - CheckerInfo *info = ctx->info; - mutex_lock(&info->objc_method_mutex); - defer (mutex_unlock(&info->objc_method_mutex)); - - Array* method_list = map_get(&info->objc_method_implementations, t); - if (method_list) { - array_add(method_list, method); - } else { - auto list = array_make(permanent_allocator(), 1, 8); - list[0] = method; - - map_set(&info->objc_method_implementations, t, list); - } - } - } else if (ac.objc_selector != "") { - error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations."); - } - - mutex_lock(&global_type_name_objc_metadata_mutex); - defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); - - if (!tn->TypeName.objc_metadata) { - tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); - } - auto *md = tn->TypeName.objc_metadata; - mutex_lock(md->mutex); - defer (mutex_unlock(md->mutex)); - - if (!ac.objc_is_class_method) { - bool ok = true; - for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { - if (entry.name == ac.objc_name) { - error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); - ok = false; - break; - } - } - if (ok) { - array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); - } + if (!tn->TypeName.objc_is_implementation) { + error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied"); + } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) { + error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)"); + } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { + error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set"); + } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) { + error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention"); + } else if (proc.result_count > 1) { + error(e->token, "Objective-C method implementations may return at most 1 value"); } else { - bool ok = true; - for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { - if (entry.name == ac.objc_name) { - error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); - ok = false; - break; - } + // Always export unconditionally + // NOTE(harold): This means check_objc_methods() MUST be called before + // e->Procedure.is_export is set in check_proc_decl()! + if (ac.is_export) { + error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly"); } - if (ok) { - array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + if (ac.link_name != "") { + error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly"); } + + ac.is_export = true; + ac.linkage = STR_LIT("strong"); + + auto method = ObjcMethodData{ ac, e }; + method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; + + CheckerInfo *info = ctx->info; + mutex_lock(&info->objc_method_mutex); + defer (mutex_unlock(&info->objc_method_mutex)); + + Array* method_list = map_get(&info->objc_method_implementations, t); + if (method_list) { + array_add(method_list, method); + } else { + auto list = array_make(permanent_allocator(), 1, 8); + list[0] = method; + + map_set(&info->objc_method_implementations, t, list); + } + } + } else if (ac.objc_selector != "") { + error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations."); + } + + mutex_lock(&global_type_name_objc_metadata_mutex); + defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); + + if (!tn->TypeName.objc_metadata) { + tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); + } + auto *md = tn->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + + if (!ac.objc_is_class_method) { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } else { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); } } } From f6993a8205fc7fbc3cdbb86d46086b5357394e07 Mon Sep 17 00:00:00 2001 From: connnnal <216976529+connnnal@users.noreply.github.com> Date: Thu, 17 Jul 2025 04:57:59 +0100 Subject: [PATCH 023/160] Add IUnknown UUID --- core/sys/windows/ole32.odin | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/sys/windows/ole32.odin b/core/sys/windows/ole32.odin index 7409d40dc..2e59949e3 100644 --- a/core/sys/windows/ole32.odin +++ b/core/sys/windows/ole32.odin @@ -25,11 +25,12 @@ COINIT :: enum DWORD { SPEED_OVER_MEMORY = 0x8, } +IUnknown_UUID_STRING :: "00000000-0000-0000-C000-000000000046" +IUnknown_UUID := &IID{0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} +IUnknownVtbl :: IUnknown_VTable IUnknown :: struct { using _iunknown_vtable: ^IUnknown_VTable, } - -IUnknownVtbl :: IUnknown_VTable IUnknown_VTable :: struct { QueryInterface: proc "system" (This: ^IUnknown, riid: REFIID, ppvObject: ^rawptr) -> HRESULT, AddRef: proc "system" (This: ^IUnknown) -> ULONG, From ecd41b155db7a1ed93923ddc296fab1036e14392 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Wed, 16 Jul 2025 21:54:24 -0700 Subject: [PATCH 024/160] rename table_lookup to runtime_swizzle --- base/intrinsics/intrinsics.odin | 2 +- core/simd/simd.odin | 6 +++--- src/check_builtin.cpp | 4 ++-- src/checker_builtin_procs.hpp | 4 ++-- src/llvm_backend_proc.cpp | 20 ++++++++++---------- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 9edf7bcd8..d2ed95ab3 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -310,7 +310,7 @@ simd_indices :: proc($T: typeid/#simd[$N]$E) -> T where type_is_numeric(T) --- simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T --- simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- -simd_table_lookup :: proc(table: #simd[N]T, indices: #simd[N]T) -> #simd[N]T where type_is_integer(T) --- +simd_runtime_swizzle :: proc(table: #simd[N]T, indices: #simd[N]T) -> #simd[N]T where type_is_integer(T) --- // Lane-wise operations simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index eb4912e58..303eceb97 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -2481,15 +2481,15 @@ Example: import "core:simd" import "core:fmt" - table_lookup_example :: proc() { + runtime_swizzle_example :: proc() { table := simd.u8x16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} indices := simd.u8x16{15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} - result := simd.table_lookup(table, indices) + result := simd.runtime_swizzle(table, indices) fmt.println(result) // Expected: {15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} } */ -table_lookup :: intrinsics.simd_table_lookup +runtime_swizzle :: intrinsics.simd_runtime_swizzle /* Compute the square root of each lane in a SIMD vector. diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c7386a97d..d786afb8e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1150,7 +1150,7 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return true; } - case BuiltinProc_simd_table_lookup: + case BuiltinProc_simd_runtime_swizzle: { if (ce->args.count != 2) { error(call, "'%.*s' expected 2 arguments, got %td", LIT(builtin_name), ce->args.count); @@ -1163,7 +1163,7 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan check_expr_with_type_hint(c, &indices, ce->args[1], table.type); if (indices.mode == Addressing_Invalid) return false; if (!is_type_simd_vector(table.type)) { - error(table.expr, "'%.*s' expected a simd vector type for table", LIT(builtin_name)); + error(table.expr, "'%.*s' expected a simd vector type for runtime swizzle", LIT(builtin_name)); return false; } if (!is_type_simd_vector(indices.type)) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 59fc84a4e..8898d4c11 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -191,7 +191,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_shuffle, BuiltinProc_simd_select, - BuiltinProc_simd_table_lookup, + BuiltinProc_simd_runtime_swizzle, BuiltinProc_simd_ceil, BuiltinProc_simd_floor, @@ -551,7 +551,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_table_lookup"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_runtime_swizzle"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1fe8a15fe..a7766cab2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1721,7 +1721,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn return res; } - case BuiltinProc_simd_table_lookup: + case BuiltinProc_simd_runtime_swizzle: { LLVMValueRef table = arg0.value; LLVMValueRef indices = lb_build_expr(p, ce->args[1]).value; @@ -1734,11 +1734,11 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn // Determine strategy based on element size and target architecture char const *intrinsic_name = nullptr; - bool use_hardware_table_lookup = false; + bool use_hardware_runtime_swizzle = false; // 8-bit elements: Use dedicated table lookup instructions if (elem_size == 1) { - use_hardware_table_lookup = true; + use_hardware_runtime_swizzle = true; if (build_context.metrics.arch == TargetArch_amd64 || build_context.metrics.arch == TargetArch_i386) { // x86/x86-64: Use pshufb intrinsics @@ -1753,7 +1753,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn intrinsic_name = "llvm.x86.avx512.pshuf.b.512"; break; default: - use_hardware_table_lookup = false; + use_hardware_runtime_swizzle = false; break; } } else if (build_context.metrics.arch == TargetArch_arm64) { @@ -1772,7 +1772,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn intrinsic_name = "llvm.aarch64.neon.tbl4"; break; default: - use_hardware_table_lookup = false; + use_hardware_runtime_swizzle = false; break; } } else if (build_context.metrics.arch == TargetArch_arm32) { @@ -1791,7 +1791,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn intrinsic_name = "llvm.arm.neon.vtbl4"; break; default: - use_hardware_table_lookup = false; + use_hardware_runtime_swizzle = false; break; } } else if (build_context.metrics.arch == TargetArch_wasm32 || build_context.metrics.arch == TargetArch_wasm64p32) { @@ -1799,14 +1799,14 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn if (count == 16) { intrinsic_name = "llvm.wasm.swizzle"; } else { - use_hardware_table_lookup = false; + use_hardware_runtime_swizzle = false; } } else { - use_hardware_table_lookup = false; + use_hardware_runtime_swizzle = false; } } - if (use_hardware_table_lookup && intrinsic_name != nullptr) { + if (use_hardware_runtime_swizzle && intrinsic_name != nullptr) { // Use dedicated hardware table lookup instruction // Check if required target features are enabled @@ -1932,7 +1932,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn return res; } else { // Features not enabled, fall back to emulation - use_hardware_table_lookup = false; + use_hardware_runtime_swizzle = false; } } From 3e5de5f705c91812f0cd49df813f75144be6598a Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Wed, 16 Jul 2025 23:23:11 -0700 Subject: [PATCH 025/160] add did you mean for card/len --- src/check_builtin.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index b833c7014..5baf67135 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2333,7 +2333,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (mode == Addressing_Invalid) { gbString t = type_to_string(operand->type); - error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t); + if (is_type_bit_set(op_type) && id == BuiltinProc_len) { + error(call, "'%.*s' is not supported for '%s', did you mean 'card'?", LIT(builtin_name), t); + } else { + error(call, "'%.*s' is not supported for '%s'", LIT(builtin_name), t); + } return false; } From 6c81df82a68a2e573ed119f6b6ebd4cd98463ae6 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Wed, 16 Jul 2025 23:43:41 -0700 Subject: [PATCH 026/160] cleanup langauge / errors about table vs swizzle --- src/check_builtin.cpp | 32 ++++++++++---------- src/llvm_backend_proc.cpp | 62 +++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d786afb8e..89dc8fb33 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1157,27 +1157,27 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return false; } - Operand table = {}; + Operand src = {}; Operand indices = {}; - check_expr(c, &table, ce->args[0]); if (table.mode == Addressing_Invalid) return false; - check_expr_with_type_hint(c, &indices, ce->args[1], table.type); if (indices.mode == Addressing_Invalid) return false; + check_expr(c, &src, ce->args[0]); if (src.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &indices, ce->args[1], src.type); if (indices.mode == Addressing_Invalid) return false; - if (!is_type_simd_vector(table.type)) { - error(table.expr, "'%.*s' expected a simd vector type for runtime swizzle", LIT(builtin_name)); + if (!is_type_simd_vector(src.type)) { + error(src.expr, "'%.*s' expected first argument to be a simd vector", LIT(builtin_name)); return false; } if (!is_type_simd_vector(indices.type)) { - error(indices.expr, "'%.*s' expected a simd vector type for indices", LIT(builtin_name)); + error(indices.expr, "'%.*s' expected second argument (indices) to be a simd vector", LIT(builtin_name)); return false; } - Type *table_elem = base_array_type(table.type); + Type *src_elem = base_array_type(src.type); Type *indices_elem = base_array_type(indices.type); - if (!is_type_integer(table_elem)) { - gbString table_str = type_to_string(table.type); - error(table.expr, "'%.*s' expected table to be a simd vector of integers, got '%s'", LIT(builtin_name), table_str); - gb_string_free(table_str); + if (!is_type_integer(src_elem)) { + gbString src_str = type_to_string(src.type); + error(src.expr, "'%.*s' expected first argument to be a simd vector of integers, got '%s'", LIT(builtin_name), src_str); + gb_string_free(src_str); return false; } @@ -1188,17 +1188,17 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return false; } - if (!are_types_identical(table.type, indices.type)) { - gbString table_str = type_to_string(table.type); + if (!are_types_identical(src.type, indices.type)) { + gbString src_str = type_to_string(src.type); gbString indices_str = type_to_string(indices.type); - error(indices.expr, "'%.*s' expected table and indices to have the same type, got '%s' vs '%s'", LIT(builtin_name), table_str, indices_str); + error(indices.expr, "'%.*s' expected both arguments to have the same type, got '%s' vs '%s'", LIT(builtin_name), src_str, indices_str); gb_string_free(indices_str); - gb_string_free(table_str); + gb_string_free(src_str); return false; } operand->mode = Addressing_Value; - operand->type = table.type; + operand->type = src.type; return true; } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a7766cab2..5894a1844 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1723,7 +1723,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn case BuiltinProc_simd_runtime_swizzle: { - LLVMValueRef table = arg0.value; + LLVMValueRef src = arg0.value; LLVMValueRef indices = lb_build_expr(p, ce->args[1]).value; Type *vt = arg0.type; @@ -1807,7 +1807,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn } if (use_hardware_runtime_swizzle && intrinsic_name != nullptr) { - // Use dedicated hardware table lookup instruction + // Use dedicated hardware swizzle instruction // Check if required target features are enabled bool features_enabled = true; @@ -1834,7 +1834,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn } } } else if (build_context.metrics.arch == TargetArch_arm64 || build_context.metrics.arch == TargetArch_arm32) { - // ARM/ARM64 feature checking - NEON is required for all table lookups + // ARM/ARM64 feature checking - NEON is required for all table/swizzle ops if (!check_target_feature_is_enabled(str_lit("neon"), nullptr)) { features_enabled = false; } @@ -1856,77 +1856,77 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("512")); } } else if (build_context.metrics.arch == TargetArch_arm64) { - // ARM64 function attributes - enable NEON for table lookup instructions + // ARM64 function attributes - enable NEON for swizzle instructions lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon")); - // Set appropriate vector width for multi-table operations + // Set appropriate vector width for multi-swizzle operations if (count >= 32) { lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("min-legal-vector-width"), str_lit("256")); } } else if (build_context.metrics.arch == TargetArch_arm32) { - // ARM32 function attributes - enable NEON for table lookup instructions + // ARM32 function attributes - enable NEON for swizzle instructions lb_add_attribute_to_proc_with_string(p->module, p->value, str_lit("target-features"), str_lit("+neon")); } - // Handle ARM's multi-table intrinsics by splitting the table vector + // Handle ARM's multi-swizzle intrinsics by splitting the src vector if (build_context.metrics.arch == TargetArch_arm64 && count > 16) { - // ARM64 TBL2/TBL3/TBL4: Split table into multiple 16-byte vectors + // ARM64 TBL2/TBL3/TBL4: Split src into multiple 16-byte vectors int num_tables = cast(int)(count / 16); - GB_ASSERT_MSG(count % 16 == 0, "ARM64 table size must be multiple of 16 bytes, got %lld bytes", count); + GB_ASSERT_MSG(count % 16 == 0, "ARM64 src size must be multiple of 16 bytes, got %lld bytes", count); GB_ASSERT_MSG(num_tables <= 4, "ARM64 NEON supports maximum 4 tables (tbl4), got %d tables for %lld-byte vector", num_tables, count); - LLVMValueRef table_parts[4]; // Max 4 tables for tbl4 + LLVMValueRef src_parts[4]; // Max 4 tables for tbl4 for (int i = 0; i < num_tables; i++) { - // Extract 16-byte slice from the larger table + // Extract 16-byte slice from the larger src LLVMValueRef indices_for_extract[16]; for (int j = 0; j < 16; j++) { indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 16 + j, false); } LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 16); - table_parts[i] = LLVMBuildShuffleVector(p->builder, table, LLVMGetUndef(LLVMTypeOf(table)), extract_mask, ""); + src_parts[i] = LLVMBuildShuffleVector(p->builder, src, LLVMGetUndef(LLVMTypeOf(src)), extract_mask, ""); } // Call appropriate ARM64 tbl intrinsic if (count == 32) { - LLVMValueRef args[3] = { table_parts[0], table_parts[1], indices }; + LLVMValueRef args[3] = { src_parts[0], src_parts[1], indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0); } else if (count == 48) { - LLVMValueRef args[4] = { table_parts[0], table_parts[1], table_parts[2], indices }; + LLVMValueRef args[4] = { src_parts[0], src_parts[1], src_parts[2], indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0); } else if (count == 64) { - LLVMValueRef args[5] = { table_parts[0], table_parts[1], table_parts[2], table_parts[3], indices }; + LLVMValueRef args[5] = { src_parts[0], src_parts[1], src_parts[2], src_parts[3], indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0); } } else if (build_context.metrics.arch == TargetArch_arm32 && count > 8) { - // ARM32 VTBL2/VTBL3/VTBL4: Split table into multiple 8-byte vectors + // ARM32 VTBL2/VTBL3/VTBL4: Split src into multiple 8-byte vectors int num_tables = cast(int)count / 8; - GB_ASSERT_MSG(count % 8 == 0, "ARM32 table size must be multiple of 8 bytes, got %lld bytes", count); + GB_ASSERT_MSG(count % 8 == 0, "ARM32 src size must be multiple of 8 bytes, got %lld bytes", count); GB_ASSERT_MSG(num_tables <= 4, "ARM32 NEON supports maximum 4 tables (vtbl4), got %d tables for %lld-byte vector", num_tables, count); - LLVMValueRef table_parts[4]; // Max 4 tables for vtbl4 + LLVMValueRef src_parts[4]; // Max 4 tables for vtbl4 for (int i = 0; i < num_tables; i++) { - // Extract 8-byte slice from the larger table + // Extract 8-byte slice from the larger src LLVMValueRef indices_for_extract[8]; for (int j = 0; j < 8; j++) { indices_for_extract[j] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), i * 8 + j, false); } LLVMValueRef extract_mask = LLVMConstVector(indices_for_extract, 8); - table_parts[i] = LLVMBuildShuffleVector(p->builder, table, LLVMGetUndef(LLVMTypeOf(table)), extract_mask, ""); + src_parts[i] = LLVMBuildShuffleVector(p->builder, src, LLVMGetUndef(LLVMTypeOf(src)), extract_mask, ""); } // Call appropriate ARM32 vtbl intrinsic if (count == 16) { - LLVMValueRef args[3] = { table_parts[0], table_parts[1], indices }; + LLVMValueRef args[3] = { src_parts[0], src_parts[1], indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, 3, nullptr, 0); } else if (count == 24) { - LLVMValueRef args[4] = { table_parts[0], table_parts[1], table_parts[2], indices }; + LLVMValueRef args[4] = { src_parts[0], src_parts[1], src_parts[2], indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, 4, nullptr, 0); } else if (count == 32) { - LLVMValueRef args[5] = { table_parts[0], table_parts[1], table_parts[2], table_parts[3], indices }; + LLVMValueRef args[5] = { src_parts[0], src_parts[1], src_parts[2], src_parts[3], indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, 5, nullptr, 0); } } else { - // Single-table case (x86, WebAssembly, ARM single-table) - LLVMValueRef args[2] = { table, indices }; + // Single runtime swizzle case (x86, WebAssembly, ARM single-table) + LLVMValueRef args[2] = { src, indices }; res.value = lb_call_intrinsic(p, intrinsic_name, args, gb_count_of(args), nullptr, 0); } return res; @@ -1948,16 +1948,16 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn LLVMValueRef index_mask; if (elem_size == 1) { - // 8-bit: mask to table size (like pshufb behavior) + // 8-bit: mask to src size (like pshufb behavior) index_mask = LLVMConstInt(elem_llvm_type, max_index, false); } else if (elem_size == 2) { - // 16-bit: mask to table size + // 16-bit: mask to src size index_mask = LLVMConstInt(elem_llvm_type, max_index, false); } else if (elem_size == 4) { - // 32-bit: mask to table size + // 32-bit: mask to src size index_mask = LLVMConstInt(elem_llvm_type, max_index, false); } else { - // 64-bit: mask to table size + // 64-bit: mask to src size index_mask = LLVMConstInt(elem_llvm_type, max_index, false); } @@ -1978,11 +1978,11 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn index_i32 = masked_index; } - values[i] = LLVMBuildExtractElement(p->builder, table, index_i32, ""); + values[i] = LLVMBuildExtractElement(p->builder, src, index_i32, ""); } // Build result vector - res.value = LLVMGetUndef(LLVMTypeOf(table)); + res.value = LLVMGetUndef(LLVMTypeOf(src)); for (i64 i = 0; i < count; i++) { LLVMValueRef idx_i = LLVMConstInt(i32_type, cast(unsigned)i, false); res.value = LLVMBuildInsertElement(p->builder, res.value, values[i], idx_i, ""); From 80cd080175071138fd5357b558445e56c40c292b Mon Sep 17 00:00:00 2001 From: WisonYe Date: Sat, 19 Jul 2025 11:54:17 +1200 Subject: [PATCH 027/160] Fixed 'Odin/core/os/os_freebsd.odin(971:7) Index 0 is out of range 0..<0' when using '-default-to-nil-allocator'. --- core/os/os_freebsd.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index f57c464ac..d12210ac9 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -967,8 +967,8 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in runtime.args__ { - res[i] = string(arg) + for arg, i in res { + res[i] = string(runtime.args__[i]) } return res } From 26e4104ac923f51ada45b7d827c41a0279153b80 Mon Sep 17 00:00:00 2001 From: WisonYe Date: Sat, 19 Jul 2025 11:59:18 +1200 Subject: [PATCH 028/160] Fixed 'Odin/core/os/os_linux.odin(1104:7) Index 0 is out of range 0..<0' when using '-default-to-nil-allocator'. --- core/os/os_linux.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 683c893f9..60421f9a5 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -1100,8 +1100,8 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in runtime.args__ { - res[i] = string(arg) + for arg, i in arg { + res[i] = string(runtime.args__[i]) } return res } From b9f08412aed5f71cef8f37f975980a167382cc4f Mon Sep 17 00:00:00 2001 From: WisonYe Date: Sat, 19 Jul 2025 12:03:22 +1200 Subject: [PATCH 029/160] Fixed Index 0 is out of range 0..<0' when using '-default-to-nil-allocator' for Linux/OpenBSD/NetBSD. --- core/os/os_linux.odin | 2 +- core/os/os_netbsd.odin | 4 ++-- core/os/os_openbsd.odin | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 60421f9a5..3fe194fcd 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -1100,7 +1100,7 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in arg { + for arg, i in res { res[i] = string(runtime.args__[i]) } return res diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index efdd852fe..90482a5c9 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -1017,8 +1017,8 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in runtime.args__ { - res[i] = string(arg) + for arg, i in res { + res[i] = string(runtime.args__[i]) } return res } diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index c68f7943c..f8e46741b 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -917,8 +917,8 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in runtime.args__ { - res[i] = string(arg) + for arg, i in res { + res[i] = string(runtime.args__[i]) } return res } From 4ccdb48044b6a5c5172d69eac61210329fff6b65 Mon Sep 17 00:00:00 2001 From: WisonYe Date: Sat, 19 Jul 2025 12:29:03 +1200 Subject: [PATCH 030/160] Fixed the build check: ('arg' declared but not used) against the '-default-to-nil-allocator' fix for FreeBSD/OpenBSD/NetBSD/Linux. --- core/os/os_freebsd.odin | 2 +- core/os/os_linux.odin | 2 +- core/os/os_netbsd.odin | 2 +- core/os/os_openbsd.odin | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index d12210ac9..aeffdcb87 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -967,7 +967,7 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in res { + for _, i in res { res[i] = string(runtime.args__[i]) } return res diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 3fe194fcd..66c30711d 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -1100,7 +1100,7 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in res { + for _, i in res { res[i] = string(runtime.args__[i]) } return res diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index 90482a5c9..accc5abcd 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -1017,7 +1017,7 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in res { + for _, i in res { res[i] = string(runtime.args__[i]) } return res diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index f8e46741b..ec9181ba6 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -917,7 +917,7 @@ _processor_core_count :: proc() -> int { @(private, require_results) _alloc_command_line_arguments :: proc() -> []string { res := make([]string, len(runtime.args__)) - for arg, i in res { + for _, i in res { res[i] = string(runtime.args__[i]) } return res From 2fc8ca6cf5c468f8a59e15a65c0446fd4ef8037c Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Sat, 19 Jul 2025 16:44:45 +0400 Subject: [PATCH 031/160] Disable filepath/match.odin and filepath/walk.odin compilation on js targets --- core/path/filepath/match.odin | 1 + core/path/filepath/walk.odin | 1 + 2 files changed, 2 insertions(+) diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index 1f0ac9287..3eaa7c6fe 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -1,4 +1,5 @@ #+build !wasi +#+build !js package filepath import "core:os" diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin index 53b10eed7..05d67daf0 100644 --- a/core/path/filepath/walk.odin +++ b/core/path/filepath/walk.odin @@ -1,4 +1,5 @@ #+build !wasi +#+build !js package filepath import "core:os" From 164bb52212c26ef82e8799475c3e69006dc10ce6 Mon Sep 17 00:00:00 2001 From: Laytan Date: Sat, 19 Jul 2025 19:50:32 +0200 Subject: [PATCH 032/160] crypto/hash: hash_bytes_to_buffer slice result to digest size --- core/crypto/hash/hash.odin | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/crypto/hash/hash.odin b/core/crypto/hash/hash.odin index d47f0ab46..66d9201cd 100644 --- a/core/crypto/hash/hash.odin +++ b/core/crypto/hash/hash.odin @@ -21,8 +21,7 @@ hash_string :: proc(algorithm: Algorithm, data: string, allocator := context.all // in a newly allocated slice. hash_bytes :: proc(algorithm: Algorithm, data: []byte, allocator := context.allocator) -> []byte { dst := make([]byte, DIGEST_SIZES[algorithm], allocator) - hash_bytes_to_buffer(algorithm, data, dst) - return dst + return hash_bytes_to_buffer(algorithm, data, dst) } // hash_string_to_buffer will hash the given input and assign the @@ -46,7 +45,7 @@ hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) -> []byte update(&ctx, data) final(&ctx, hash) - return hash + return hash[:DIGEST_SIZES[algorithm]] } // hash_stream will incrementally fully consume a stream, and return the From e7e16f063e0d3dd65c1b3ddcc1dd53f974c12c1e Mon Sep 17 00:00:00 2001 From: brandon Date: Sat, 19 Jul 2025 18:51:51 -0400 Subject: [PATCH 033/160] SDL_image save functions should return bool --- vendor/sdl3/image/sdl_image.odin | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vendor/sdl3/image/sdl_image.odin b/vendor/sdl3/image/sdl_image.odin index 96c3901c8..d2c628a86 100644 --- a/vendor/sdl3/image/sdl_image.odin +++ b/vendor/sdl3/image/sdl_image.odin @@ -89,12 +89,12 @@ foreign lib { ReadXPMFromArrayToRGB888 :: proc(xpm: [^]cstring) -> ^SDL.Surface --- /* Individual saving functions */ - SaveAVIF :: proc(surface: ^SDL.Surface, file: cstring, quality: c.int) -> c.int --- - SaveAVIF_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool, quality: c.int) -> c.int --- - SavePNG :: proc(surface: ^SDL.Surface, file: cstring) -> c.int --- - SavePNG_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool) -> c.int --- - SaveJPG :: proc(surface: ^SDL.Surface, file: cstring, quality: c.int) -> c.int --- - SaveJPG_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool, quality: c.int) -> c.int --- + SaveAVIF :: proc(surface: ^SDL.Surface, file: cstring, quality: c.int) -> c.bool --- + SaveAVIF_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool, quality: c.int) -> c.bool --- + SavePNG :: proc(surface: ^SDL.Surface, file: cstring) -> c.bool --- + SavePNG_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool) -> c.bool --- + SaveJPG :: proc(surface: ^SDL.Surface, file: cstring, quality: c.int) -> c.bool --- + SaveJPG_IO :: proc(surface: ^SDL.Surface, dst: ^SDL.IOStream, closeio: bool, quality: c.int) -> c.bool --- LoadAnimation :: proc(file: cstring) -> ^Animation --- LoadAnimation_IO :: proc(src: ^SDL.IOStream, closeio: bool) -> ^Animation --- From 0dd0a3813bcc3b8c238b5e3c7272439ed08455d0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 20 Jul 2025 15:32:22 +0100 Subject: [PATCH 034/160] Fix very subtle bug in `virtual.memory_block_alloc` --- core/mem/virtual/virtual.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index c4c3b1727..3027e5848 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -89,8 +89,8 @@ memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags reserved = align_formula(reserved, page_size) committed = clamp(committed, 0, reserved) - total_size := uint(reserved + max(alignment, size_of(Platform_Memory_Block))) - base_offset := uintptr(max(alignment, size_of(Platform_Memory_Block))) + total_size := reserved + alignment + size_of(Platform_Memory_Block) + base_offset := mem.align_forward_uintptr(size_of(Platform_Memory_Block), max(uintptr(alignment), align_of(Platform_Memory_Block))) protect_offset := uintptr(0) do_protection := false From 7c917d56e9d07a5c8940560d7f1d35c01bc5fe58 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 16 Jul 2025 16:05:58 -0400 Subject: [PATCH 035/160] Check for invalid subtargets. - Add 'ios' pseudo-subtarget which triggets with either iPhone or iPhoneSimulator subtargets. - Treat an explicit 'default' subtarget as exclusive only to the default subtarget, not an other platform-compatible subtargets. - 'generic' continues to resolve to true for any platform-compatible subtarget as it names appears to imply such behavior. --- src/build_settings.cpp | 31 ++++++++++++++++++++----------- src/parser.cpp | 26 +++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fb88b588a..ab501fe84 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -174,8 +174,9 @@ enum Subtarget : u32 { Subtarget_iPhone, Subtarget_iPhoneSimulator, Subtarget_Android, - + Subtarget_COUNT, + Subtarget_Invalid, // NOTE(harold): Must appear after _COUNT as this is not a real subtarget }; gb_global String subtarget_strings[Subtarget_COUNT] = { @@ -859,7 +860,7 @@ gb_global NamedTargetMetrics *selected_target_metrics; gb_global Subtarget selected_subtarget; -gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr) { +gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr, String *subtarget_str = nullptr) { String os_name = str; String subtarget = {}; auto part = string_partition(str, str_lit(":")); @@ -876,18 +877,26 @@ gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtar break; } } - if (subtarget_) *subtarget_ = Subtarget_Default; - if (subtarget.len != 0) { - if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) { - if (subtarget_) *subtarget_ = Subtarget_Default; - } else { - for (isize i = 1; i < Subtarget_COUNT; i++) { - if (str_eq_ignore_case(subtarget_strings[i], subtarget)) { - if (subtarget_) *subtarget_ = cast(Subtarget)i; - break; + if (subtarget_str) *subtarget_str = subtarget; + + if (subtarget_) { + if (subtarget.len != 0) { + *subtarget_ = Subtarget_Invalid; + + if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) { + *subtarget_ = Subtarget_Default; + + } else { + for (isize i = 1; i < Subtarget_COUNT; i++) { + if (str_eq_ignore_case(subtarget_strings[i], subtarget)) { + *subtarget_ = cast(Subtarget)i; + break; + } } } + } else { + *subtarget_ = Subtarget_Default; } } diff --git a/src/parser.cpp b/src/parser.cpp index 5d0a75f8a..7a2693e29 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6220,9 +6220,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { continue; } - Subtarget subtarget = Subtarget_Default; + Subtarget subtarget = Subtarget_Invalid; + String subtarget_str = {}; - TargetOsKind os = get_target_os_from_string(p, &subtarget); + TargetOsKind os = get_target_os_from_string(p, &subtarget, &subtarget_str); TargetArchKind arch = get_target_arch_from_string(p); num_tokens += 1; @@ -6233,10 +6234,29 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { break; } + bool is_ios_subtarget = false; + if (subtarget == Subtarget_Invalid) { + // Special case for pseudo subtarget + if (!str_eq_ignore_case(subtarget_str, "ios")) { + syntax_error(token_for_pos, "Invalid subtarget '%.*s'.", LIT(subtarget_str)); + break; + } + + is_ios_subtarget = true; + } + + if (os != TargetOs_Invalid) { this_kind_os_seen = true; - bool same_subtarget = (subtarget == Subtarget_Default) || (subtarget == selected_subtarget); + // NOTE: Only testing for 'default' and not 'generic' because the 'generic' nomenclature implies any subtarget. + bool is_explicit_default_subtarget = str_eq_ignore_case(subtarget_str, "default"); + bool same_subtarget = (subtarget == Subtarget_Default && !is_explicit_default_subtarget) || (subtarget == selected_subtarget); + + // Special case for iPhone or iPhoneSimulator + if (is_ios_subtarget && (selected_subtarget == Subtarget_iPhone || selected_subtarget == Subtarget_iPhoneSimulator)) { + same_subtarget = true; + } GB_ASSERT(arch == TargetArch_Invalid); if (is_notted) { From 58f32cd6909aa83c107175ecf1e03c1148b7bc26 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:40:16 -0400 Subject: [PATCH 036/160] Fix Linux-specific optimized test failure The stack was not aligned as expected for `buddy_allocator_init` when `-o:speed` was enabled, making this a test failure that only appeared with optimizations enabled. The data is now aligned specifically, as it should be. --- tests/core/mem/test_core_mem.odin | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/core/mem/test_core_mem.odin b/tests/core/mem/test_core_mem.odin index c1cb59c68..9d64e50a3 100644 --- a/tests/core/mem/test_core_mem.odin +++ b/tests/core/mem/test_core_mem.odin @@ -282,13 +282,18 @@ test_dynamic_arena :: proc(t: ^testing.T) { @test test_buddy :: proc(t: ^testing.T) { - N :: 4096 + N :: 8192 buf: [N]u8 + base := &buf[0] + address := mem.align_forward(base, size_of(mem.Buddy_Block)) + delta := uintptr(address) - uintptr(base) + ba: mem.Buddy_Allocator - mem.buddy_allocator_init(&ba, buf[:], align_of(u8)) - basic_sanity_test(t, mem.buddy_allocator(&ba), N / 8) - basic_sanity_test(t, mem.buddy_allocator(&ba), N / 8) + + mem.buddy_allocator_init(&ba, buf[delta:delta+N/2], size_of(mem.Buddy_Block)) + basic_sanity_test(t, mem.buddy_allocator(&ba), N / 16) + basic_sanity_test(t, mem.buddy_allocator(&ba), N / 16) } @test From a0efdf26a6fef9d2db8d7659708b8b9fb142af32 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:52:55 -0400 Subject: [PATCH 037/160] mem: Clarify `Buddy_Allocator` requirements --- core/mem/allocators.odin | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 0eacb1b65..cb9301f60 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -2223,6 +2223,9 @@ Initialize a buddy allocator. This procedure initializes the buddy allocator `b` with a backing buffer `data` and block alignment specified by `alignment`. + +`alignment` may be any power of two, but the backing buffer must be aligned to +at least `size_of(Buddy_Block)`. */ buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, loc := #caller_location) { assert(data != nil) @@ -2233,7 +2236,7 @@ buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, alignment = size_of(Buddy_Block) } ptr := raw_data(data) - assert(uintptr(ptr) % uintptr(alignment) == 0, "data is not aligned to minimum alignment", loc) + assert(uintptr(ptr) % uintptr(alignment) == 0, "The data is not aligned to the minimum alignment, which must be at least `size_of(Buddy_Block)`.", loc) b.head = (^Buddy_Block)(ptr) b.head.size = len(data) b.head.is_free = true From 3a7e4873cdd392a8aa035afaac31ef0987bfbb38 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 25 Jul 2025 12:00:24 +0200 Subject: [PATCH 038/160] Fix #5498 Also: - Expands `tests/core/hash` - Fixes bug found in `#hash(s, "murmur64")` --- core/hash/hash.odin | 2 +- src/gb/gb.h | 2 +- tests/core/hash/test_core_hash.odin | 328 +++++++++++------- ...tors_xxhash.odin => test_core_xxhash.odin} | 134 +++++++ 4 files changed, 348 insertions(+), 118 deletions(-) rename tests/core/hash/{test_vectors_xxhash.odin => test_core_xxhash.odin} (98%) diff --git a/core/hash/hash.odin b/core/hash/hash.odin index 45f524d8a..6c048c05b 100644 --- a/core/hash/hash.odin +++ b/core/hash/hash.odin @@ -127,7 +127,7 @@ jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 { } @(optimization_mode="favor_size") -murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 { +murmur32 :: proc "contextless" (data: []byte, seed := u32(0x9747b28c)) -> u32 { c1_32: u32 : 0xcc9e2d51 c2_32: u32 : 0x1b873593 diff --git a/src/gb/gb.h b/src/gb/gb.h index 6ce8626c0..ffc40b8ca 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -4869,8 +4869,8 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) { u64 h = seed ^ (len * m); u64 const *data = cast(u64 const *)data_; - u8 const *data2 = cast(u8 const *)data_; u64 const* end = data + (len / 8); + u8 const *data2 = cast(u8 const *)end; while (data != end) { u64 k = *data++; diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index adb55d2d8..8a951b186 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -1,140 +1,186 @@ #+feature dynamic-literals package test_core_hash -import "core:hash/xxhash" import "core:hash" import "core:testing" -import "core:math/rand" import "base:intrinsics" +/* + Built-in `#hash`es: + #hash("murmur32"), + #hash("murmur64"), + }; +*/ + +V32 :: struct{s: string, h: u32} +V64 :: struct{s: string, h: u64} + @test -test_xxhash_zero_fixed :: proc(t: ^testing.T) { - many_zeroes := make([]u8, 16 * 1024 * 1024) - defer delete(many_zeroes) - - // All at once. - for i, v in ZERO_VECTORS { - b := many_zeroes[:i] - - xxh32 := xxhash.XXH32(b) - xxh64 := xxhash.XXH64(b) - xxh3_64 := xxhash.XXH3_64(b) - xxh3_128 := xxhash.XXH3_128(b) - - testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) - testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64) - testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64) - testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) +test_adler32_vectors :: proc(t: ^testing.T) { + vectors :: []V32{ + {"" , 0x00000001}, + {"a" , 0x00620062}, + {"abc" , 0x024d0127}, + {"Hello" , 0x058c01f5}, + {"world" , 0x06a60229}, + {"Hello, world!", 0x205e048a}, } + + for vector in vectors { + b := transmute([]u8)vector.s + adler := hash.adler32(b) + testing.expectf(t, adler == vector.h, "\n\t[ADLER-32(%v)] Expected: 0x%08x, got: 0x%08x", vector.s, vector.h, adler) + } + + testing.expect_value(t, #hash(vectors[0].s, "adler32"), int(vectors[0].h)) + testing.expect_value(t, #hash(vectors[1].s, "adler32"), int(vectors[1].h)) + testing.expect_value(t, #hash(vectors[2].s, "adler32"), int(vectors[2].h)) + testing.expect_value(t, #hash(vectors[3].s, "adler32"), int(vectors[3].h)) + testing.expect_value(t, #hash(vectors[4].s, "adler32"), int(vectors[4].h)) + testing.expect_value(t, #hash(vectors[5].s, "adler32"), int(vectors[5].h)) } -@(test) -test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { - many_zeroes := make([]u8, 16 * 1024 * 1024) - defer delete(many_zeroes) +@test +test_djb2_vectors :: proc(t: ^testing.T) { + vectors :: []V32{ + {"" , 5381}, // Initial seed + {"a" , 0x0002b606}, + {"abc" , 0x0b885c8b}, + {"Hello" , 0x0d4f2079}, + {"world" , 0x10a7356d}, + {"Hello, world!", 0xe18796ae}, + } - // Streamed - for i, v in ZERO_VECTORS { - b := many_zeroes[:i] - - xxh_32_state, xxh_32_err := xxhash.XXH32_create_state() - defer xxhash.XXH32_destroy_state(xxh_32_state) - testing.expect(t, xxh_32_err == nil, "Problem initializing XXH_32 state") - - xxh_64_state, xxh_64_err := xxhash.XXH64_create_state() - defer xxhash.XXH64_destroy_state(xxh_64_state) - testing.expect(t, xxh_64_err == nil, "Problem initializing XXH_64 state") - - xxh3_64_state, xxh3_64_err := xxhash.XXH3_create_state() - defer xxhash.XXH3_destroy_state(xxh3_64_state) - testing.expect(t, xxh3_64_err == nil, "Problem initializing XXH3_64 state") - - xxh3_128_state, xxh3_128_err := xxhash.XXH3_create_state() - defer xxhash.XXH3_destroy_state(xxh3_128_state) - testing.expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state") - - // XXH3_128_update - rand.reset(t.seed) - for len(b) > 0 { - update_size := min(len(b), rand.int_max(8192)) - if update_size > 4096 { - update_size %= 73 - } - xxhash.XXH32_update (xxh_32_state, b[:update_size]) - xxhash.XXH64_update (xxh_64_state, b[:update_size]) - - xxhash.XXH3_64_update (xxh3_64_state, b[:update_size]) - xxhash.XXH3_128_update(xxh3_128_state, b[:update_size]) - - b = b[update_size:] - } - - // Now finalize - xxh32 := xxhash.XXH32_digest(xxh_32_state) - xxh64 := xxhash.XXH64_digest(xxh_64_state) - - xxh3_64 := xxhash.XXH3_64_digest(xxh3_64_state) - xxh3_128 := xxhash.XXH3_128_digest(xxh3_128_state) - - testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) - testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64) - testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64) - testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) + for vector in vectors { + b := transmute([]u8)vector.s + djb2 := hash.djb2(b) + testing.expectf(t, djb2 == vector.h, "\n\t[DJB-2(%v)] Expected: 0x%08x, got: 0x%08x", vector.s, vector.h, djb2) } } @test -test_xxhash_seeded :: proc(t: ^testing.T) { - buf := make([]u8, 256) - defer delete(buf) - - for seed, table in XXHASH_TEST_VECTOR_SEEDED { - for v, i in table { - b := buf[:i] - - xxh32 := xxhash.XXH32(b, u32(seed)) - xxh64 := xxhash.XXH64(b, seed) - xxh3_64 := xxhash.XXH3_64(b, seed) - xxh3_128 := xxhash.XXH3_128(b, seed) - - testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) - testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64) - testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64) - testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) - - if len(b) > xxhash.XXH3_MIDSIZE_MAX { - xxh3_state, _ := xxhash.XXH3_create_state() - xxhash.XXH3_64_reset_with_seed(xxh3_state, seed) - xxhash.XXH3_64_update(xxh3_state, b) - xxh3_64_streamed := xxhash.XXH3_64_digest(xxh3_state) - xxhash.XXH3_destroy_state(xxh3_state) - testing.expectf(t, xxh3_64_streamed == v.xxh3_64, "[XXH3_64s(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64_streamed) - - xxh3_state2, _ := xxhash.XXH3_create_state() - xxhash.XXH3_128_reset_with_seed(xxh3_state2, seed) - xxhash.XXH3_128_update(xxh3_state2, b) - xxh3_128_streamed := xxhash.XXH3_128_digest(xxh3_state2) - xxhash.XXH3_destroy_state(xxh3_state2) - testing.expectf(t, xxh3_128_streamed == v.xxh3_128, "[XXH3_128s(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128_streamed) - } - } +test_fnv32_vectors :: proc(t: ^testing.T) { + vectors :: []V32{ + {"" , 0x811c9dc5}, + {"a" , 0x050c5d7e}, + {"abc" , 0x439c2f4b}, + {"Hello" , 0x3726bd47}, + {"world" , 0x9b8e862f}, + {"Hello, world!", 0xe84ead66}, } + + for vector in vectors { + b := transmute([]u8)vector.s + fnv := hash.fnv32_no_a(b) + testing.expectf(t, fnv == vector.h, "\n\t[FNV-32(%v)] Expected: 0x%08x, got: 0x%08x", vector.s, vector.h, fnv) + } + + testing.expect_value(t, #hash(vectors[0].s, "fnv32"), int(vectors[0].h)) + testing.expect_value(t, #hash(vectors[1].s, "fnv32"), int(vectors[1].h)) + testing.expect_value(t, #hash(vectors[2].s, "fnv32"), int(vectors[2].h)) + testing.expect_value(t, #hash(vectors[3].s, "fnv32"), int(vectors[3].h)) + testing.expect_value(t, #hash(vectors[4].s, "fnv32"), int(vectors[4].h)) + testing.expect_value(t, #hash(vectors[5].s, "fnv32"), int(vectors[5].h)) } @test -test_xxhash_secret :: proc(t: ^testing.T) { - buf := make([]u8, 256) - defer delete(buf) - - for secret, table in XXHASH_TEST_VECTOR_SECRET { - secret_bytes := transmute([]u8)secret - for v, i in table { - b := buf[:i] - - xxh3_128 := xxhash.XXH3_128(b, secret_bytes) - testing.expectf(t, xxh3_128 == v.xxh3_128_secret, "[XXH3_128(%03d)] Expected: %32x, got: %32x", i, v.xxh3_128_secret, xxh3_128) - } +test_fnv64_vectors :: proc(t: ^testing.T) { + vectors :: []V64{ + {"" , 0xcbf29ce484222325}, + {"a" , 0xaf63bd4c8601b7be}, + {"abc" , 0xd8dcca186bafadcb}, + {"Hello" , 0xfa365282a44c0ba7}, + {"world" , 0x3ec0cf0cc4a6540f}, + {"Hello, world!", 0x6519bd6389aaa166}, } + + for vector in vectors { + b := transmute([]u8)vector.s + fnv := hash.fnv64_no_a(b) + testing.expectf(t, fnv == vector.h, "\n\t[FNV-64(%v)] Expected: 0x%16x, got: 0x%16x", vector.s, vector.h, fnv) + } + + testing.expect_value(t, i128(#hash(vectors[0].s, "fnv64")), i128(vectors[0].h)) + testing.expect_value(t, i128(#hash(vectors[1].s, "fnv64")), i128(vectors[1].h)) + testing.expect_value(t, i128(#hash(vectors[2].s, "fnv64")), i128(vectors[2].h)) + testing.expect_value(t, i128(#hash(vectors[3].s, "fnv64")), i128(vectors[3].h)) + testing.expect_value(t, i128(#hash(vectors[4].s, "fnv64")), i128(vectors[4].h)) + testing.expect_value(t, i128(#hash(vectors[5].s, "fnv64")), i128(vectors[5].h)) +} + +@test +test_fnv32a_vectors :: proc(t: ^testing.T) { + vectors :: []V32{ + {"" , 0x811c9dc5}, + {"a" , 0xe40c292c}, + {"abc" , 0x1a47e90b}, + {"Hello" , 0xf55c314b}, + {"world" , 0x37a3e893}, + {"Hello, world!", 0xed90f094}, + } + + for vector in vectors { + b := transmute([]u8)vector.s + fnv := hash.fnv32a(b) + testing.expectf(t, fnv == vector.h, "\n\t[FNV-32a(%v)] Expected: 0x%08x, got: 0x%08x", vector.s, vector.h, fnv) + } + + testing.expect_value(t, #hash(vectors[0].s, "fnv32a"), int(vectors[0].h)) + testing.expect_value(t, #hash(vectors[1].s, "fnv32a"), int(vectors[1].h)) + testing.expect_value(t, #hash(vectors[2].s, "fnv32a"), int(vectors[2].h)) + testing.expect_value(t, #hash(vectors[3].s, "fnv32a"), int(vectors[3].h)) + testing.expect_value(t, #hash(vectors[4].s, "fnv32a"), int(vectors[4].h)) + testing.expect_value(t, #hash(vectors[5].s, "fnv32a"), int(vectors[5].h)) +} + +@test +test_fnv64a_vectors :: proc(t: ^testing.T) { + vectors :: []V64{ + {"" , 0xcbf29ce484222325}, + {"a" , 0xaf63dc4c8601ec8c}, + {"abc" , 0xe71fa2190541574b}, + {"Hello" , 0x63f0bfacf2c00f6b}, + {"world" , 0x4f59ff5e730c8af3}, + {"Hello, world!", 0x38d1334144987bf4}, + } + + for vector in vectors { + b := transmute([]u8)vector.s + fnv := hash.fnv64a(b) + testing.expectf(t, fnv == vector.h, "\n\t[FNV-64a(%v)] Expected: 0x%16x, got: 0x%16x", vector.s, vector.h, fnv) + } + + testing.expect_value(t, i128(#hash(vectors[0].s, "fnv64a")), i128(vectors[0].h)) + testing.expect_value(t, i128(#hash(vectors[1].s, "fnv64a")), i128(vectors[1].h)) + testing.expect_value(t, i128(#hash(vectors[2].s, "fnv64a")), i128(vectors[2].h)) + testing.expect_value(t, i128(#hash(vectors[3].s, "fnv64a")), i128(vectors[3].h)) + testing.expect_value(t, i128(#hash(vectors[4].s, "fnv64a")), i128(vectors[4].h)) + testing.expect_value(t, i128(#hash(vectors[5].s, "fnv64a")), i128(vectors[5].h)) +} + +@test +test_crc32_vectors :: proc(t: ^testing.T) { + vectors :: []V32{ + {"" , 0x00000000}, + {"a" , 0xe8b7be43}, + {"abc" , 0x352441c2}, + {"Hello" , 0xf7d18982}, + {"world" , 0x3a771143}, + {"Hello, world!", 0xebe6c6e6}, + } + + for vector in vectors { + b := transmute([]u8)vector.s + crc := hash.crc32(b) + testing.expectf(t, crc == vector.h, "\n\t[CRC-32(%v)] Expected: 0x%08x, got: 0x%08x", vector.s, vector.h, crc) + } + + testing.expect_value(t, #hash(vectors[0].s, "crc32"), int(vectors[0].h)) + testing.expect_value(t, #hash(vectors[1].s, "crc32"), int(vectors[1].h)) + testing.expect_value(t, #hash(vectors[2].s, "crc32"), int(vectors[2].h)) + testing.expect_value(t, #hash(vectors[3].s, "crc32"), int(vectors[3].h)) + testing.expect_value(t, #hash(vectors[4].s, "crc32"), int(vectors[4].h)) + testing.expect_value(t, #hash(vectors[5].s, "crc32"), int(vectors[5].h)) } @test @@ -167,4 +213,54 @@ test_crc64_vectors :: proc(t: ^testing.T) { testing.expectf(t, iso == expected[2], "[ CRC-64 ISO 3306] Expected: %016x, got: %016x", expected[2], iso) testing.expectf(t, iso2 == expected[3], "[~CRC-64 ISO 3306] Expected: %016x, got: %016x", expected[3], iso2) } +} + +@test +test_murmur32_vectors :: proc(t: ^testing.T) { + vectors :: []V32{ + {"" , 0xebb6c228}, + {"a" , 0x7fa09ea6}, + {"abc" , 0xc84a62dd}, + {"Hello" , 0xec73fdbe}, + {"world" , 0xd7f8a5f2}, + {"Hello, world!", 0x24884cba}, + } + + for vector in vectors { + b := transmute([]u8)vector.s + murmur := hash.murmur32(b) + testing.expectf(t, murmur == vector.h, "\n\t[MURMUR-32(%v)] Expected: 0x%08x, got: 0x%08x", vector.s, vector.h, murmur) + } + + testing.expect_value(t, #hash(vectors[0].s, "murmur32"), int(vectors[0].h)) + testing.expect_value(t, #hash(vectors[1].s, "murmur32"), int(vectors[1].h)) + testing.expect_value(t, #hash(vectors[2].s, "murmur32"), int(vectors[2].h)) + testing.expect_value(t, #hash(vectors[3].s, "murmur32"), int(vectors[3].h)) + testing.expect_value(t, #hash(vectors[4].s, "murmur32"), int(vectors[4].h)) + testing.expect_value(t, #hash(vectors[5].s, "murmur32"), int(vectors[5].h)) +} + +@test +test_murmur64_vectors :: proc(t: ^testing.T) { + vectors :: []V64{ + {"" , 0x8397626cd6895052}, + {"a" , 0xe96b6245652273ae}, + {"abc" , 0xa9316c8740c81414}, + {"Hello" , 0x89cc3a85a7045a4f}, + {"world" , 0xf030e222b1f740f6}, + {"Hello, world!", 0x710583fa7f802a84}, + } + + for vector in vectors { + b := transmute([]u8)vector.s + murmur := hash.murmur64a(b) + testing.expectf(t, murmur == vector.h, "\n\t[MURMUR-64(%v)] Expected: 0x%16x, got: 0x%16x", vector.s, vector.h, murmur) + } + + testing.expect_value(t, i128(#hash(vectors[0].s, "murmur64")), i128(vectors[0].h)) + testing.expect_value(t, i128(#hash(vectors[1].s, "murmur64")), i128(vectors[1].h)) + testing.expect_value(t, i128(#hash(vectors[2].s, "murmur64")), i128(vectors[2].h)) + testing.expect_value(t, i128(#hash(vectors[3].s, "murmur64")), i128(vectors[3].h)) + testing.expect_value(t, i128(#hash(vectors[4].s, "murmur64")), i128(vectors[4].h)) + testing.expect_value(t, i128(#hash(vectors[5].s, "murmur64")), i128(vectors[5].h)) } \ No newline at end of file diff --git a/tests/core/hash/test_vectors_xxhash.odin b/tests/core/hash/test_core_xxhash.odin similarity index 98% rename from tests/core/hash/test_vectors_xxhash.odin rename to tests/core/hash/test_core_xxhash.odin index 04e2d4f1f..f544f6699 100644 --- a/tests/core/hash/test_vectors_xxhash.odin +++ b/tests/core/hash/test_core_xxhash.odin @@ -2,6 +2,140 @@ #+feature dynamic-literals package test_core_hash +import "core:hash/xxhash" +import "core:testing" +import "core:math/rand" + +@test +test_xxhash_zero_fixed :: proc(t: ^testing.T) { + many_zeroes := make([]u8, 16 * 1024 * 1024) + defer delete(many_zeroes) + + // All at once. + for i, v in ZERO_VECTORS { + b := many_zeroes[:i] + + xxh32 := xxhash.XXH32(b) + xxh64 := xxhash.XXH64(b) + xxh3_64 := xxhash.XXH3_64(b) + xxh3_128 := xxhash.XXH3_128(b) + + testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) + testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64) + testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64) + testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) + } +} + +@(test) +test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { + many_zeroes := make([]u8, 16 * 1024 * 1024) + defer delete(many_zeroes) + + // Streamed + for i, v in ZERO_VECTORS { + b := many_zeroes[:i] + + xxh_32_state, xxh_32_err := xxhash.XXH32_create_state() + defer xxhash.XXH32_destroy_state(xxh_32_state) + testing.expect(t, xxh_32_err == nil, "Problem initializing XXH_32 state") + + xxh_64_state, xxh_64_err := xxhash.XXH64_create_state() + defer xxhash.XXH64_destroy_state(xxh_64_state) + testing.expect(t, xxh_64_err == nil, "Problem initializing XXH_64 state") + + xxh3_64_state, xxh3_64_err := xxhash.XXH3_create_state() + defer xxhash.XXH3_destroy_state(xxh3_64_state) + testing.expect(t, xxh3_64_err == nil, "Problem initializing XXH3_64 state") + + xxh3_128_state, xxh3_128_err := xxhash.XXH3_create_state() + defer xxhash.XXH3_destroy_state(xxh3_128_state) + testing.expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state") + + // XXH3_128_update + rand.reset(t.seed) + for len(b) > 0 { + update_size := min(len(b), rand.int_max(8192)) + if update_size > 4096 { + update_size %= 73 + } + xxhash.XXH32_update (xxh_32_state, b[:update_size]) + xxhash.XXH64_update (xxh_64_state, b[:update_size]) + + xxhash.XXH3_64_update (xxh3_64_state, b[:update_size]) + xxhash.XXH3_128_update(xxh3_128_state, b[:update_size]) + + b = b[update_size:] + } + + // Now finalize + xxh32 := xxhash.XXH32_digest(xxh_32_state) + xxh64 := xxhash.XXH64_digest(xxh_64_state) + + xxh3_64 := xxhash.XXH3_64_digest(xxh3_64_state) + xxh3_128 := xxhash.XXH3_128_digest(xxh3_128_state) + + testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) + testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64) + testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64) + testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) + } +} + +@test +test_xxhash_seeded :: proc(t: ^testing.T) { + buf := make([]u8, 256) + defer delete(buf) + + for seed, table in XXHASH_TEST_VECTOR_SEEDED { + for v, i in table { + b := buf[:i] + + xxh32 := xxhash.XXH32(b, u32(seed)) + xxh64 := xxhash.XXH64(b, seed) + xxh3_64 := xxhash.XXH3_64(b, seed) + xxh3_128 := xxhash.XXH3_128(b, seed) + + testing.expectf(t, xxh32 == v.xxh_32, "[ XXH32(%03d) ] Expected: %08x, got: %08x", i, v.xxh_32, xxh32) + testing.expectf(t, xxh64 == v.xxh_64, "[ XXH64(%03d) ] Expected: %16x, got: %16x", i, v.xxh_64, xxh64) + testing.expectf(t, xxh3_64 == v.xxh3_64, "[XXH3_64(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64) + testing.expectf(t, xxh3_128 == v.xxh3_128, "[XXH3_128(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128) + + if len(b) > xxhash.XXH3_MIDSIZE_MAX { + xxh3_state, _ := xxhash.XXH3_create_state() + xxhash.XXH3_64_reset_with_seed(xxh3_state, seed) + xxhash.XXH3_64_update(xxh3_state, b) + xxh3_64_streamed := xxhash.XXH3_64_digest(xxh3_state) + xxhash.XXH3_destroy_state(xxh3_state) + testing.expectf(t, xxh3_64_streamed == v.xxh3_64, "[XXH3_64s(%03d) ] Expected: %16x, got: %16x", i, v.xxh3_64, xxh3_64_streamed) + + xxh3_state2, _ := xxhash.XXH3_create_state() + xxhash.XXH3_128_reset_with_seed(xxh3_state2, seed) + xxhash.XXH3_128_update(xxh3_state2, b) + xxh3_128_streamed := xxhash.XXH3_128_digest(xxh3_state2) + xxhash.XXH3_destroy_state(xxh3_state2) + testing.expectf(t, xxh3_128_streamed == v.xxh3_128, "[XXH3_128s(%03d) ] Expected: %32x, got: %32x", i, v.xxh3_128, xxh3_128_streamed) + } + } + } +} + +@test +test_xxhash_secret :: proc(t: ^testing.T) { + buf := make([]u8, 256) + defer delete(buf) + + for secret, table in XXHASH_TEST_VECTOR_SECRET { + secret_bytes := transmute([]u8)secret + for v, i in table { + b := buf[:i] + + xxh3_128 := xxhash.XXH3_128(b, secret_bytes) + testing.expectf(t, xxh3_128 == v.xxh3_128_secret, "[XXH3_128(%03d)] Expected: %32x, got: %32x", i, v.xxh3_128_secret, xxh3_128) + } + } +} + XXHASH_Test_Vectors :: struct #packed { /* Old hashes From 55de1dd92327bacc2ff6d4fcaa3aa4a87fd8f1a2 Mon Sep 17 00:00:00 2001 From: robomage Date: Sun, 27 Jul 2025 17:39:42 -0400 Subject: [PATCH 039/160] SDL2 - audio allow change bit_set sdl.OpenAudioDevice was incorrectly using a bool instead of a c.int for it's last parameter. To make the proc call more idiomatic and inline with other bindings a new bit_set was introduced to be used in place of the constants --- vendor/sdl2/sdl_audio.odin | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/vendor/sdl2/sdl_audio.odin b/vendor/sdl2/sdl_audio.odin index 6ff9e93f4..296a773c4 100644 --- a/vendor/sdl2/sdl_audio.odin +++ b/vendor/sdl2/sdl_audio.odin @@ -74,12 +74,14 @@ when ODIN_ENDIAN == .Little { AUDIO_F32SYS :: AUDIO_F32MSB } - -AUDIO_ALLOW_FREQUENCY_CHANGE :: 0x00000001 -AUDIO_ALLOW_FORMAT_CHANGE :: 0x00000002 -AUDIO_ALLOW_CHANNELS_CHANGE :: 0x00000004 -AUDIO_ALLOW_SAMPLES_CHANGE :: 0x00000008 -AUDIO_ALLOW_ANY_CHANGE :: AUDIO_ALLOW_FREQUENCY_CHANGE|AUDIO_ALLOW_FORMAT_CHANGE|AUDIO_ALLOW_CHANNELS_CHANGE|AUDIO_ALLOW_SAMPLES_CHANGE +AudioAllowChangeFlags :: distinct bit_set[AudioAllowChangeFlag; c.int] +AudioAllowChangeFlag :: enum c.int { + FREQUENCY = 0, + FORMAT = 1, + CHANNELS = 2, + SAMPLES = 3, +} +AUDIO_ALLOW_ANY_CHANGE :: AudioAllowChangeFlags{.FREQUENCY, .FORMAT, .CHANNELS, .SAMPLES} AudioCallback :: proc "c" (userdata: rawptr, stream: [^]u8, len: c.int) @@ -151,7 +153,7 @@ foreign lib { iscapture: bool, desired: ^AudioSpec, obtained: ^AudioSpec, - allowed_changes: bool) -> AudioDeviceID --- + allowed_changes: AudioAllowChangeFlags) -> AudioDeviceID --- } From 93a39affd1470d47f0991ec23b7bf1ae2be15818 Mon Sep 17 00:00:00 2001 From: Paul-Andre Henegar Date: Mon, 28 Jul 2025 01:42:19 -0400 Subject: [PATCH 040/160] Fix issue 5474 The fix was adding `is_constant = false;` I also removed the unnecessary check regarding the first element of the BitSet, since it's checked inside the loop, and also fixed a typo in the message. --- src/check_expr.cpp | 84 ++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b6fd85231..dd6a89e5b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10335,52 +10335,48 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * is_constant = false; } - if (cl->elems[0]->kind == Ast_FieldValue) { - error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); - is_constant = false; - } else { - for (Ast *elem : cl->elems) { - if (elem->kind == Ast_FieldValue) { - error(elem, "'field = value' in a bit_set a literal is not allowed"); + for (Ast *elem : cl->elems) { + if (elem->kind == Ast_FieldValue) { + error(elem, "'field = value' in a bit_set literal is not allowed"); + is_constant = false; + continue; + } + + check_expr_with_type_hint(c, o, elem, et); + + if (is_constant) { + is_constant = o->mode == Addressing_Constant; + } + + if (elem->kind == Ast_BinaryExpr) { + switch (elem->BinaryExpr.op.kind) { + case Token_Or: + { + gbString x = expr_to_string(elem->BinaryExpr.left); + gbString y = expr_to_string(elem->BinaryExpr.right); + gbString e = expr_to_string(elem); + error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e); + gb_string_free(e); + gb_string_free(y); + gb_string_free(x); + } + break; + } + } + + check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); + if (o->mode == Addressing_Constant) { + i64 lower = t->BitSet.lower; + i64 upper = t->BitSet.upper; + i64 v = exact_value_to_i64(o->value); + if (lower <= v && v <= upper) { + // okay + } else { + gbString s = expr_to_string(o->expr); + error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper); + gb_string_free(s); continue; } - - check_expr_with_type_hint(c, o, elem, et); - - if (is_constant) { - is_constant = o->mode == Addressing_Constant; - } - - if (elem->kind == Ast_BinaryExpr) { - switch (elem->BinaryExpr.op.kind) { - case Token_Or: - { - gbString x = expr_to_string(elem->BinaryExpr.left); - gbString y = expr_to_string(elem->BinaryExpr.right); - gbString e = expr_to_string(elem); - error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e); - gb_string_free(e); - gb_string_free(y); - gb_string_free(x); - } - break; - } - } - - check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal")); - if (o->mode == Addressing_Constant) { - i64 lower = t->BitSet.lower; - i64 upper = t->BitSet.upper; - i64 v = exact_value_to_i64(o->value); - if (lower <= v && v <= upper) { - // okay - } else { - gbString s = expr_to_string(o->expr); - error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper); - gb_string_free(s); - continue; - } - } } } break; From 1b15c9928b5960c515b68fea3eafe7580902642b Mon Sep 17 00:00:00 2001 From: Taylor <2374670+taylormck@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:28:19 +0900 Subject: [PATCH 041/160] add cgltf filter type and wrap mode enums --- vendor/cgltf/cgltf.odin | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/vendor/cgltf/cgltf.odin b/vendor/cgltf/cgltf.odin index e9dc7ef84..92b498d37 100644 --- a/vendor/cgltf/cgltf.odin +++ b/vendor/cgltf/cgltf.odin @@ -262,12 +262,28 @@ image :: struct { extensions: [^]extension `fmt:"v,extensions_count"`, } +filter_type :: enum c.int { + undefined = 0, + nearest = 9728, + linear = 9729, + nearest_mipmap_nearest = 9984, + linear_mipmap_nearest = 9985, + nearest_mipmap_linear = 9986, + linear_mipmap_linear = 9987, +} + +wrap_mode :: enum c.int { + clamp_to_edge = 33071, + mirrored_repeat = 33648, + repeat = 10497, +} + sampler :: struct { name: cstring, - mag_filter: c.int, - min_filter: c.int, - wrap_s: c.int, - wrap_t: c.int, + mag_filter: filter_type, + min_filter: filter_type, + wrap_s: wrap_mode, + wrap_t: wrap_mode, extras: extras_t, extensions_count: uint, extensions: [^]extension `fmt:"v,extensions_count"`, From 1848e0df05a14679d49e5b87988fa6c7b609739b Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 15:10:42 -0700 Subject: [PATCH 042/160] use the correct frequency for the arm tsc timer --- base/intrinsics/intrinsics.odin | 3 +++ core/time/tsc_darwin.odin | 3 ++- src/check_builtin.cpp | 1 + src/checker_builtin_procs.hpp | 3 +++ src/llvm_backend_proc.cpp | 13 +++++++++++++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 7e45abb8f..7057aba84 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -361,6 +361,9 @@ wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_u x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) --- x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- +// Arm64 targets +arm64_read_cycle_counter_frequency :: proc() -> i64 --- + // Darwin targets only objc_object :: struct{} diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin index fdda5db77..55abf5868 100644 --- a/core/time/tsc_darwin.odin +++ b/core/time/tsc_darwin.odin @@ -1,13 +1,14 @@ #+private package time +import "base:intrinsics" import "core:sys/unix" _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) { if ODIN_ARCH == .amd64 { unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return } else if ODIN_ARCH == .arm64 { - unix.sysctlbyname("hw.tbfrequency", &freq) or_return + freq = u64(intrinsics.arm64_read_cycle_counter_frequency()) } else { return } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 58fa44ec9..e9da79932 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4713,6 +4713,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } break; + case BuiltinProc_arm64_read_cycle_counter_frequency: case BuiltinProc_read_cycle_counter: operand->mode = Addressing_Value; operand->type = t_i64; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 91cef481e..474932970 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -225,6 +225,8 @@ BuiltinProc__simd_end, BuiltinProc_x86_cpuid, BuiltinProc_x86_xgetbv, + BuiltinProc_arm64_read_cycle_counter_frequency, + // Constant type tests BuiltinProc__type_begin, @@ -584,6 +586,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("syscall_bsd"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("arm64_read_cycle_counter_frequency"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ffa434278..3f5819b43 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2809,6 +2809,19 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } return res; } + case BuiltinProc_arm64_read_cycle_counter_frequency: + { + lbValue res = {}; + res.type = tv.type; + + LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false); + bool has_side_effects = false; + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects); + GB_ASSERT(the_asm != nullptr); + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + + return res; + } case BuiltinProc_count_trailing_zeros: return lb_emit_count_trailing_zeros(p, lb_build_expr(p, ce->args[0]), tv.type); From 76b00c046b3c58670c60badbf5f15a66cbe25f85 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 15:30:24 -0700 Subject: [PATCH 043/160] arm64 has an invariant tsc too --- core/time/perf.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/time/perf.odin b/core/time/perf.odin index 265a20edf..f4e1b4aa8 100644 --- a/core/time/perf.odin +++ b/core/time/perf.odin @@ -104,6 +104,8 @@ TSC at a fixed frequency, independent of ACPI state, and CPU frequency. has_invariant_tsc :: proc "contextless" () -> bool { when ODIN_ARCH == .amd64 { return x86_has_invariant_tsc() + } else when ODIN_ARCH == .arm64 { + return true } return false From 5b020591d8174e06d5d556ee0baccd1e35aaca49 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 16:19:51 -0700 Subject: [PATCH 044/160] update to spall format v3 --- core/prof/spall/spall.odin | 124 ++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 36 deletions(-) diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 12f082b2c..2c6a981ad 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -8,29 +8,36 @@ import "base:intrinsics" MANUAL_MAGIC :: u64le(0x0BADF00D) -Manual_Header :: struct #packed { +Manual_Stream_Header :: struct #packed { magic: u64le, version: u64le, timestamp_scale: f64le, reserved: u64le, } +Manual_Buffer_Header :: struct #packed { + size: u32le, + tid: u32le, + pid: u32le, + first_ts: u64le, +} + Manual_Event_Type :: enum u8 { - Invalid = 0, + Invalid = 0, - Begin = 3, - End = 4, - Instant = 5, + Begin = 3, + End = 4, + Instant = 5, - Pad_Skip = 7, + Pad_Skip = 7, + + Name_Process = 8, + Name_Thread = 9, } Begin_Event :: struct #packed { type: Manual_Event_Type, - category: u8, - pid: u32le, - tid: u32le, - ts: f64le, + ts: u64le, name_len: u8, args_len: u8, } @@ -38,9 +45,7 @@ BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255 End_Event :: struct #packed { type: Manual_Event_Type, - pid: u32le, - tid: u32le, - ts: f64le, + ts: u64le, } Pad_Skip :: struct #packed { @@ -48,6 +53,11 @@ Pad_Skip :: struct #packed { size: u32le, } +Name_Container_Event :: struct #packed { + type: Manual_Event_Type, + name_len: u8, +} + // User Interface Context :: struct { @@ -77,7 +87,7 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam ctx.timestamp_scale = timestamp_scale temp := [size_of(Manual_Header)]u8{} - _build_header(temp[:], ctx.timestamp_scale) + _build_stream_header(temp[:], ctx.timestamp_scale) os.write(ctx.fd, temp[:]) ok = true return @@ -102,23 +112,32 @@ context_destroy :: proc(ctx: ^Context) { buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok { assert(len(data) >= 1024) - buffer.data = data - buffer.tid = tid - buffer.pid = pid - buffer.head = 0 + buffer.data = data + buffer.tid = tid + buffer.pid = pid + buffer.first_ts = 0 + buffer.head = size_of(Manual_Buffer_Header) ok = true return } @(no_instrumentation) buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ { + buffer_size := buffer.head - size_of(Manual_Buffer_Header) + hdr := (^Manual_Buffer_Header)(raw_data(buffer.data)) + hdr.size = buffer_size + hdr.pid = buffer.pid + hdr.tid = buffer.tid + hdr.first_ts = buffer.first_ts + start := _trace_now(ctx) write(ctx.fd, buffer.data[:buffer.head]) - buffer.head = 0 + buffer.head = size_of(Manual_Buffer_Header) end := _trace_now(ctx) - buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start, buffer.tid, buffer.pid) - buffer.head += _build_end(buffer.data[buffer.head:], end, buffer.tid, buffer.pid) + buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start) + buffer.head += _build_end(buffer.data[buffer.head:], end) + buffer.first_ts = end } buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) { @@ -141,16 +160,16 @@ _scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #c } @(no_instrumentation) -_trace_now :: proc "contextless" (ctx: ^Context) -> f64 { +_trace_now :: proc "contextless" (ctx: ^Context) -> u64 { if !ctx.precise_time { - return f64(tick_now()) / 1_000 + return u64(tick_now()) } - return f64(intrinsics.read_cycle_counter()) + return u64(intrinsics.read_cycle_counter()) } @(no_instrumentation) -_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok { +_build_stream_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok { header_size = size_of(Manual_Header) if header_size > len(buffer) { return 0, false @@ -158,7 +177,7 @@ _build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (hea hdr := (^Manual_Header)(raw_data(buffer)) hdr.magic = MANUAL_MAGIC - hdr.version = 1 + hdr.version = 3 hdr.timestamp_scale = f64le(timestamp_scale) hdr.reserved = 0 ok = true @@ -166,7 +185,7 @@ _build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (hea } @(no_instrumentation) -_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { +_build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, args: string, ts: u64) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { ev := (^Begin_Event)(raw_data(buffer)) name_len := min(len(name), 255) args_len := min(len(args), 255) @@ -177,9 +196,7 @@ _build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, ar } ev.type = .Begin - ev.pid = u32le(pid) - ev.tid = u32le(tid) - ev.ts = f64le(ts) + ev.ts = u64le(ts) ev.name_len = u8(name_len) ev.args_len = u8(args_len) intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len) @@ -190,7 +207,7 @@ _build_begin :: #force_inline proc "contextless" (buffer: []u8, name: string, ar } @(no_instrumentation) -_build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok { +_build_end :: proc "contextless" (buffer: []u8, ts: u64) -> (event_size: int, ok: bool) #optional_ok { ev := (^End_Event)(raw_data(buffer)) event_size = size_of(End_Event) if event_size > len(buffer) { @@ -198,9 +215,28 @@ _build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> } ev.type = .End - ev.pid = u32le(pid) - ev.tid = u32le(tid) - ev.ts = f64le(ts) + ev.ts = u64le(ts) + ok = true + + return +} + +@(no_instrumentation) +_build_name_event :: #force_inline proc "contextless" (buffer: []u8, name: string, type: Manual_Event_Type) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { + ev := (^Name_Container_Event)(raw_data(buffer)) + name_len := min(len(name), 255) + + event_size = size_of(Name_Container_Event) + name_len + if event_size > len(buffer) { + return 0, false + } + if type != .Name_Process && type != .Name_Thread { + return 0, false + } + + ev.type = type + ev.name_len = u8(name_len) + intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Name_Container_Event):]), raw_data(name), name_len) ok = true return @@ -212,7 +248,7 @@ _buffer_begin :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: strin buffer_flush(ctx, buffer) } name := location.procedure if name == "" else name - buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid) + buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx)) } @(no_instrumentation) @@ -223,7 +259,23 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch buffer_flush(ctx, buffer) } - buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid) + buffer.head += _build_end(buffer.data[buffer.head:], ts) +} + +@(no_instrumentation) +_buffer_name_thread :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ { + if buffer.head + NAME_EVENT_MAX > len(buffer.data) { + buffer_flush(ctx, buffer) + } + buffer.head += _build_name_event(buffer.data[buffer.head:], name, .Name_Thread) +} + +@(no_instrumentation) +_buffer_name_process :: proc "contextless" (ctx: ^Context, buffer: ^Buffer, name: string, location := #caller_location) #no_bounds_check /* bounds check would segfault instrumentation */ { + if buffer.head + NAME_EVENT_MAX > len(buffer.data) { + buffer_flush(ctx, buffer) + } + buffer.head += _build_name_event(buffer.data[buffer.head:], name, .Name_Process) } @(no_instrumentation) From 2dd1e3c8e31f3a6c0d8af2e469d3d738ecfc2a9a Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 16:24:29 -0700 Subject: [PATCH 045/160] fix casts and consts --- core/prof/spall/spall.odin | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 2c6a981ad..5ad3bf5b8 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -57,6 +57,7 @@ Name_Container_Event :: struct #packed { type: Manual_Event_Type, name_len: u8, } +NAME_EVENT_MAX :: size_of(Name_Container_Event) + 255 // User Interface @@ -71,6 +72,7 @@ Buffer :: struct { head: int, tid: u32, pid: u32, + first_ts: u64, } BUFFER_DEFAULT_SIZE :: 0x10_0000 @@ -125,10 +127,10 @@ buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buff buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ { buffer_size := buffer.head - size_of(Manual_Buffer_Header) hdr := (^Manual_Buffer_Header)(raw_data(buffer.data)) - hdr.size = buffer_size - hdr.pid = buffer.pid - hdr.tid = buffer.tid - hdr.first_ts = buffer.first_ts + hdr.size = u32le(buffer_size) + hdr.pid = u32le(buffer.pid) + hdr.tid = u32le(buffer.tid) + hdr.first_ts = u64le(buffer.first_ts) start := _trace_now(ctx) write(ctx.fd, buffer.data[:buffer.head]) @@ -170,12 +172,12 @@ _trace_now :: proc "contextless" (ctx: ^Context) -> u64 { @(no_instrumentation) _build_stream_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok { - header_size = size_of(Manual_Header) + header_size = size_of(Manual_Stream_Header) if header_size > len(buffer) { return 0, false } - hdr := (^Manual_Header)(raw_data(buffer)) + hdr := (^Manual_Stream_Header)(raw_data(buffer)) hdr.magic = MANUAL_MAGIC hdr.version = 3 hdr.timestamp_scale = f64le(timestamp_scale) From 7986d859243e95a82c334bdbe6f2761a6a41209e Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 16:25:42 -0700 Subject: [PATCH 046/160] name_container_event -> name_event --- core/prof/spall/spall.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 5ad3bf5b8..e11404bcd 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -53,11 +53,11 @@ Pad_Skip :: struct #packed { size: u32le, } -Name_Container_Event :: struct #packed { +Name_Event :: struct #packed { type: Manual_Event_Type, name_len: u8, } -NAME_EVENT_MAX :: size_of(Name_Container_Event) + 255 +NAME_EVENT_MAX :: size_of(Name_Event) + 255 // User Interface @@ -225,10 +225,10 @@ _build_end :: proc "contextless" (buffer: []u8, ts: u64) -> (event_size: int, ok @(no_instrumentation) _build_name_event :: #force_inline proc "contextless" (buffer: []u8, name: string, type: Manual_Event_Type) -> (event_size: int, ok: bool) #optional_ok #no_bounds_check /* bounds check would segfault instrumentation */ { - ev := (^Name_Container_Event)(raw_data(buffer)) + ev := (^Name_Event)(raw_data(buffer)) name_len := min(len(name), 255) - event_size = size_of(Name_Container_Event) + name_len + event_size = size_of(Name_Event) + name_len if event_size > len(buffer) { return 0, false } @@ -238,7 +238,7 @@ _build_name_event :: #force_inline proc "contextless" (buffer: []u8, name: strin ev.type = type ev.name_len = u8(name_len) - intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Name_Container_Event):]), raw_data(name), name_len) + intrinsics.mem_copy_non_overlapping(raw_data(buffer[size_of(Name_Event):]), raw_data(name), name_len) ok = true return From e1fd69f573bcf8ba9c8cfc961a007ab23aa83135 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 16:27:54 -0700 Subject: [PATCH 047/160] oops, one more manual_header --- core/prof/spall/spall.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index e11404bcd..4d37913e4 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -88,7 +88,7 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam ctx.precise_time = precise_time ctx.timestamp_scale = timestamp_scale - temp := [size_of(Manual_Header)]u8{} + temp := [size_of(Manual_Stream_Header)]u8{} _build_stream_header(temp[:], ctx.timestamp_scale) os.write(ctx.fd, temp[:]) ok = true From 1a9b128bfc4edff7b2651d68d1d350ec39b7e3a5 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 16:32:10 -0700 Subject: [PATCH 048/160] update linux-arm to use the asm intrin for freq --- core/time/tsc_linux.odin | 53 ++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin index a83634414..92712f136 100644 --- a/core/time/tsc_linux.odin +++ b/core/time/tsc_linux.odin @@ -5,29 +5,34 @@ package time import linux "core:sys/linux" _get_tsc_frequency :: proc "contextless" () -> (u64, bool) { - // Get the file descriptor for the perf mapping - perf_attr := linux.Perf_Event_Attr{} - perf_attr.size = size_of(perf_attr) - perf_attr.type = .HARDWARE - perf_attr.config.hw = .INSTRUCTIONS - perf_attr.flags = {.Disabled, .Exclude_Kernel, .Exclude_HV} - fd, perf_errno := linux.perf_event_open(&perf_attr, linux.Pid(0), -1, linux.Fd(-1), {}) - if perf_errno != nil { - return 0, false + if ODIN_ARCH == .arm64 { + freq = u64(intrinsics.arm64_read_cycle_counter_frequency()) + return frequency, true + } else { + // Get the file descriptor for the perf mapping + perf_attr := linux.Perf_Event_Attr{} + perf_attr.size = size_of(perf_attr) + perf_attr.type = .HARDWARE + perf_attr.config.hw = .INSTRUCTIONS + perf_attr.flags = {.Disabled, .Exclude_Kernel, .Exclude_HV} + fd, perf_errno := linux.perf_event_open(&perf_attr, linux.Pid(0), -1, linux.Fd(-1), {}) + if perf_errno != nil { + return 0, false + } + defer linux.close(fd) + // Map it into the memory + page_size : uint = 4096 + addr, mmap_errno := linux.mmap(0, page_size, {.READ}, {.SHARED}, fd) + if mmap_errno != nil { + return 0, false + } + defer linux.munmap(addr, page_size) + // Get the frequency from the mapped page + event_page := cast(^linux.Perf_Event_Mmap_Page) addr + if .User_Time not_in event_page.cap.flags { + return 0, false + } + frequency := u64((u128(1_000_000_000) << u128(event_page.time_shift)) / u128(event_page.time_mult)) + return frequency, true } - defer linux.close(fd) - // Map it into the memory - page_size : uint = 4096 - addr, mmap_errno := linux.mmap(0, page_size, {.READ}, {.SHARED}, fd) - if mmap_errno != nil { - return 0, false - } - defer linux.munmap(addr, page_size) - // Get the frequency from the mapped page - event_page := cast(^linux.Perf_Event_Mmap_Page) addr - if .User_Time not_in event_page.cap.flags { - return 0, false - } - frequency := u64((u128(1_000_000_000) << u128(event_page.time_shift)) / u128(event_page.time_mult)) - return frequency, true } From b5629aeed9eea4daadacad8b174947a17a11670b Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Mon, 28 Jul 2025 16:34:53 -0700 Subject: [PATCH 049/160] copy-paste typo fix --- core/time/tsc_linux.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin index 92712f136..b9381dba2 100644 --- a/core/time/tsc_linux.odin +++ b/core/time/tsc_linux.odin @@ -6,7 +6,7 @@ import linux "core:sys/linux" _get_tsc_frequency :: proc "contextless" () -> (u64, bool) { if ODIN_ARCH == .arm64 { - freq = u64(intrinsics.arm64_read_cycle_counter_frequency()) + frequency := u64(intrinsics.arm64_read_cycle_counter_frequency()) return frequency, true } else { // Get the file descriptor for the perf mapping From 6c2b72f70128e63dea0b009f298e0b34bd9fd865 Mon Sep 17 00:00:00 2001 From: Taylor <2374670+taylormck@users.noreply.github.com> Date: Wed, 30 Jul 2025 00:17:40 +0900 Subject: [PATCH 050/160] fix: align the enum values --- vendor/cgltf/cgltf.odin | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vendor/cgltf/cgltf.odin b/vendor/cgltf/cgltf.odin index 92b498d37..bab58d851 100644 --- a/vendor/cgltf/cgltf.odin +++ b/vendor/cgltf/cgltf.odin @@ -263,19 +263,19 @@ image :: struct { } filter_type :: enum c.int { - undefined = 0, - nearest = 9728, - linear = 9729, + undefined = 0, + nearest = 9728, + linear = 9729, nearest_mipmap_nearest = 9984, - linear_mipmap_nearest = 9985, - nearest_mipmap_linear = 9986, - linear_mipmap_linear = 9987, + linear_mipmap_nearest = 9985, + nearest_mipmap_linear = 9986, + linear_mipmap_linear = 9987, } wrap_mode :: enum c.int { - clamp_to_edge = 33071, + clamp_to_edge = 33071, mirrored_repeat = 33648, - repeat = 10497, + repeat = 10497, } sampler :: struct { From e7670e58dd602803d8335b9996639003844bc4c3 Mon Sep 17 00:00:00 2001 From: Laytan Date: Tue, 29 Jul 2025 18:50:27 +0200 Subject: [PATCH 051/160] saner stack linker flags for WASM Supersedes #5425 --- src/build_settings.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e1b9c4156..5afc9c44f 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1887,6 +1887,13 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta str_lit("-target "), bc->metrics.target_triplet, str_lit(" ")); } else if (is_arch_wasm()) { gbString link_flags = gb_string_make(heap_allocator(), " "); + + // NOTE(laytan): Put the stack first in the memory, + // causing a stack overflow to error immediately instead of corrupting globals. + link_flags = gb_string_appendc(link_flags, "--stack-first "); + // NOTE(laytan): default stack size is 64KiB, up to a more reasonable 1MiB. + link_flags = gb_string_appendc(link_flags, "--stack-size=1048576 "); + // link_flags = gb_string_appendc(link_flags, "--export-all "); // link_flags = gb_string_appendc(link_flags, "--export-table "); // if (bc->metrics.arch == TargetArch_wasm64) { From c966e635204f3252c295976f7bf02c452d866b81 Mon Sep 17 00:00:00 2001 From: Laytan Date: Tue, 29 Jul 2025 19:04:38 +0200 Subject: [PATCH 052/160] fix ignored args in multi return handling Fixes #5483 --- src/llvm_backend_proc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ffa434278..5c38367f1 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1073,6 +1073,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c lbValue result = {}; + isize ignored_args = 0; auto processed_args = array_make(permanent_allocator(), 0, args.count); { @@ -1095,6 +1096,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c lbArgType *arg = &ft->args[param_index]; if (arg->kind == lbArg_Ignore) { param_index += 1; + ignored_args += 1; continue; } @@ -1203,7 +1205,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c auto tuple_fix_values = slice_make(permanent_allocator(), ret_count); auto tuple_geps = slice_make(permanent_allocator(), ret_count); - isize offset = ft->original_arg_count; + isize offset = ft->original_arg_count - ignored_args; for (isize j = 0; j < ret_count-1; j++) { lbValue ret_arg_ptr = processed_args[offset + j]; lbValue ret_arg = lb_emit_load(p, ret_arg_ptr); From af0b90bd3e0ef7d11eec9cc07aac3b6774c7c818 Mon Sep 17 00:00:00 2001 From: Laytan Date: Tue, 29 Jul 2025 19:07:28 +0200 Subject: [PATCH 053/160] fix flag --- src/build_settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 5afc9c44f..46a4f9ae5 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1892,7 +1892,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta // causing a stack overflow to error immediately instead of corrupting globals. link_flags = gb_string_appendc(link_flags, "--stack-first "); // NOTE(laytan): default stack size is 64KiB, up to a more reasonable 1MiB. - link_flags = gb_string_appendc(link_flags, "--stack-size=1048576 "); + link_flags = gb_string_appendc(link_flags, "-z stack-size=1048576 "); // link_flags = gb_string_appendc(link_flags, "--export-all "); // link_flags = gb_string_appendc(link_flags, "--export-table "); From fe2b48dec4ba99239a6f8fe9944b1545aac9a355 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 11:48:58 -0700 Subject: [PATCH 054/160] enum pass --- core/os/os_darwin.odin | 4 +- core/sys/darwin/mach_darwin.odin | 135 +++++++++++++++++++++++++------ core/sys/posix/spawn.odin | 6 +- core/time/tsc_linux.odin | 1 + 4 files changed, 115 insertions(+), 31 deletions(-) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 3c12e8c7f..a27add029 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -695,13 +695,13 @@ _spawn :: #force_inline proc(path: string, args: []string, envs: []string, file_ } child_pid: posix.pid_t - status: i32 + status: posix.Errno if is_spawnp { status = posix.posix_spawnp(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) } else { status = posix.posix_spawn(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) } - if status != 0 { + if status != .NONE { return 0, Platform_Error(status) } return child_pid, nil diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index ce8594a6c..2e57bc1c9 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -22,27 +22,6 @@ MACH_PORT_DEAD :: ~mach_port_t(0) MACH_MSG_PORT_DESCRIPTOR :: 0 -MACH_SEND_MSG :: 0x00000001 -MACH_RCV_MSG :: 0x00000002 -MACH_SEND_TIMEOUT :: 0x00000010 -MACH_RCV_TIMEOUT :: 0x00000100 - -MACH_MSG_TYPE_COPY_SEND :: 19 -MACH_MSG_TYPE_MAKE_SEND :: 20 -MACH_MSGH_BITS_COMPLEX :: 0x80000000 - -MACH_PORT_RIGHT_SEND :: 0 -MACH_PORT_RIGHT_RECEIVE :: 1 - -VM_INHERIT_SHARE :: 0 -VM_INHERIT_COPY :: 1 -VM_INHERIT_NONE :: 2 -VM_INHERIT_DONATE_COPY :: 3 - -TASK_BOOTSTRAP_PORT :: 4 - -BOOTSTRAP_NAME_IN_USE :: 1101 - X86_THREAD_STATE32 :: 1 X86_THREAD_STATE64 :: 4 ARM_THREAD_STATE64 :: 6 @@ -69,6 +48,8 @@ vm_inherit_t :: distinct c.uint mach_port_name_t :: distinct c.uint +mach_port_right_t :: distinct c.uint + sync_policy_t :: distinct c.int mach_msg_port_descriptor_t :: struct { @@ -81,6 +62,77 @@ mach_msg_port_descriptor_t :: struct { }, } +Task_Port_Type :: enum u32 { + Kernel = 1, + Host, + Name, + Bootstrap, + Seatbelt = 7, + Access = 9, +} + +Bootstrap_Error :: enum u32 { + Success, + Not_Privileged = 1100, + Name_In_Use = 1101, + Unknown_Service = 1102, + Service_Active = 1103, + Bad_Count = 1104, + No_Memory = 1105, + No_Children = 1106, +} + +Msg_Type :: enum u32 { + Unstructured = 0, + Bit = 0, + Boolean = 0, + Integer_16 = 1, + Integer_32 = 2, + Char = 8, + Byte = 9, + Integer_8 = 9, + Real = 10, + Integer_64 = 11, + String = 12, + String_C = 12, + + Port_Name = 15, + + Move_Receive = 16, + Port_Receive = 16, + Move_Send = 17, + Port_Send = 17, + Move_Send_Once = 18, + Port_Send_Once = 18, + Copy_Send = 19, + Make_Send = 20, + Make_Send_Once = 21, +} + +Msg_Header_Bits :: enum u32 { + Zero = 0, + Remote_Mask = 0xff, + Local_Mask = 0xff00, + Migrated = 0x08000000, + Unused = 0x07ff0000, + Complex_Data = 0x10000000, + Complex_Ports = 0x20000000, + Circular = 0x40000000, + Complex = 0x80000000, +} + +mach_msg_type_t :: struct { + using _: bit_field u32 { + name: u32 | 8, + size: u32 | 8, + number: u32 | 12, + inline: u32 | 1, + longform: u32 | 1, + deallocate: u32 | 1, + unused: u32 | 1, + }, +} + mach_msg_header_t :: struct { msgh_bits: u32, msgh_size: u32, @@ -248,18 +300,18 @@ dyld_all_image_infos :: struct { @(default_calling_convention="c") foreign mach { - mach_task_self :: proc() -> task_t --- - mach_msg :: proc(header: rawptr, option: mach_msg_option_t, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> kern_return_t --- + mach_task_self :: proc() -> mach_port_t --- + mach_msg :: proc(header: rawptr, option: Msg_Option_Flags, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> kern_return_t --- mach_msg_send :: proc(header: rawptr) -> kern_return_t --- mach_vm_allocate :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> kern_return_t --- mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> kern_return_t --- - mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: i32) -> kern_return_t --- + mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: VM_Inherit) -> kern_return_t --- mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> kern_return_t --- vm_page_size: u64 vm_page_mask: u64 vm_page_shift: i32 - mach_port_allocate :: proc(task: task_t, right: u32, name: rawptr) -> kern_return_t --- + mach_port_allocate :: proc(task: task_t, right: Port_Right, name: rawptr) -> kern_return_t --- mach_port_deallocate :: proc(task: task_t, name: u32) -> kern_return_t --- mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> kern_return_t --- @@ -732,6 +784,39 @@ VM_PROT_NONE :: VM_Prot_Flags{} VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write} VM_PROT_ALL :: VM_Prot_Flags{.Read, .Write, .Execute} +/* + * Mach msg options, defined as bits within the mach_msg_option_t type + */ + +Msg_Option :: enum mach_msg_option_t { + Send_Msg, + Receive_Msg, + + Send_Timeout = LOG2(0x10), + Send_Notify = LOG2(0x20), + Send_Interrupt = LOG2(0x40), + Send_Cancel = LOG2(0x80), + Receive_Timeout = LOG2(0x100), + Receive_Notify = LOG2(0x200), + Receive_Interrupt = LOG2(0x400), + Receive_Large = LOG2(0x800), + Send_Always = LOG2(0x10000), +} + +Msg_Option_Flags :: distinct bit_set[Msg_Option; mach_msg_option_t] + +/* + * Enumeration of valid values for mach_port_right_t + */ + +Port_Right :: enum mach_port_right_t { + Send, + Receive, + Send_Once, + Port_Set, + Dead_Name, +} + /* * Enumeration of valid values for vm_inherit_t. */ diff --git a/core/sys/posix/spawn.odin b/core/sys/posix/spawn.odin index 154509160..8933915be 100644 --- a/core/sys/posix/spawn.odin +++ b/core/sys/posix/spawn.odin @@ -1,7 +1,5 @@ package posix -import "core:c" - when ODIN_OS == .Darwin { foreign import lib "system:System.framework" } else { @@ -9,6 +7,6 @@ when ODIN_OS == .Darwin { } foreign lib { - posix_spawn :: proc(pid: ^pid_t, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- - posix_spawnp :: proc(pid: ^pid_t, file: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> c.int --- + posix_spawn :: proc(pid: ^pid_t, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> Errno --- + posix_spawnp :: proc(pid: ^pid_t, file: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> Errno --- } diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin index b9381dba2..bfae79fb4 100644 --- a/core/time/tsc_linux.odin +++ b/core/time/tsc_linux.odin @@ -2,6 +2,7 @@ #+build linux package time +import "base:intrinsics" import linux "core:sys/linux" _get_tsc_frequency :: proc "contextless" () -> (u64, bool) { From 0a102bd757fb0a5871a00a28db6d46cf025bb8d0 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 11:52:58 -0700 Subject: [PATCH 055/160] shuffle to Kern_Return --- core/sys/darwin/mach_darwin.odin | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 2e57bc1c9..19515fe87 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -301,26 +301,26 @@ dyld_all_image_infos :: struct { @(default_calling_convention="c") foreign mach { mach_task_self :: proc() -> mach_port_t --- - mach_msg :: proc(header: rawptr, option: Msg_Option_Flags, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> kern_return_t --- - mach_msg_send :: proc(header: rawptr) -> kern_return_t --- - mach_vm_allocate :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> kern_return_t --- - mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> kern_return_t --- - mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: VM_Inherit) -> kern_return_t --- - mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> kern_return_t --- + mach_msg :: proc(header: rawptr, option: Msg_Option_Flags, send_size: u32, receive_limit: u32, receive_name: mach_port_t, timeout: u32, notify: mach_port_t) -> Kern_Return --- + mach_msg_send :: proc(header: rawptr) -> Kern_Return --- + mach_vm_allocate :: proc(target_task: task_t, adddress: u64, size: u64, flags: i32) -> Kern_Return --- + mach_vm_deallocate :: proc(target_task: task_t, adddress: ^u64, size: u64) -> Kern_Return --- + mach_vm_remap :: proc(target_task: task_t, page: rawptr, size: u64, mask: u64, flags: i32, src_task: task_t, src_address: u64, copy: b32, cur_protection: ^i32, max_protection: ^i32, inheritance: VM_Inherit) -> Kern_Return --- + mach_vm_region_recurse :: proc(target_task: task_t, address: ^u64, size: ^u64, depth: ^u32, info: vm_region_recurse_info_t, count: ^u32) -> Kern_Return --- vm_page_size: u64 vm_page_mask: u64 vm_page_shift: i32 - mach_port_allocate :: proc(task: task_t, right: Port_Right, name: rawptr) -> kern_return_t --- - mach_port_deallocate :: proc(task: task_t, name: u32) -> kern_return_t --- - mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> kern_return_t --- + mach_port_allocate :: proc(task: task_t, right: Port_Right, name: rawptr) -> Kern_Return --- + mach_port_deallocate :: proc(task: task_t, name: u32) -> Kern_Return --- + mach_port_extract_right :: proc(task: task_t, name: u32, msgt_name: u32, poly: ^mach_port_t, poly_poly: ^mach_port_t) -> Kern_Return --- - task_get_special_port :: proc(task: task_t, port: i32, special_port: ^mach_port_t) -> kern_return_t --- - task_suspend :: proc(task: task_t) -> kern_return_t --- - task_resume :: proc(task: task_t) -> kern_return_t --- - task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> kern_return_t --- - task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t --- - task_terminate :: proc(task: task_t) -> kern_return_t --- + task_get_special_port :: proc(task: task_t, port: i32, special_port: ^mach_port_t) -> Kern_Return --- + task_suspend :: proc(task: task_t) -> Kern_Return --- + task_resume :: proc(task: task_t) -> Kern_Return --- + task_threads :: proc(task: task_t, thread_list: ^thread_list_t, list_count: ^u32) -> Kern_Return --- + task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> Kern_Return --- + task_terminate :: proc(task: task_t) -> Kern_Return --- semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return --- semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return --- @@ -331,11 +331,11 @@ foreign mach { semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return --- - thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t --- - thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t --- + thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> Kern_Return --- + thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> Kern_Return --- - bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t --- - bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t --- + bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> Kern_Return --- + bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> Kern_Return --- vm_map :: proc( target_task: vm_map_t, From c1b3d035e4be4bdbebb397035a95e6dbc60f1bd6 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 12:12:15 -0700 Subject: [PATCH 056/160] remove spawn from os, comment sys/posix/spawn --- core/os/os_darwin.odin | 35 ----------------------------------- core/sys/posix/spawn.odin | 8 ++++++++ 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index a27add029..1010d27a8 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -7,10 +7,8 @@ foreign import pthread "system:System" import "base:runtime" import "core:strings" import "core:c" -import "core:sys/posix" Handle :: distinct i32 -Pid :: distinct i32 File_Time :: distinct u64 INVALID_HANDLE :: ~Handle(0) @@ -679,39 +677,6 @@ get_last_error_string :: proc() -> string { return string(_darwin_string_error(__error()^)) } -_spawn :: #force_inline proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr, is_spawnp: bool) -> (posix.pid_t, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - args_cstrs := make([]cstring, len(args) + 2, context.temp_allocator) - args_cstrs[0] = strings.clone_to_cstring(path, context.temp_allocator) - for i := 0; i < len(args); i += 1 { - args_cstrs[i+1] = strings.clone_to_cstring(args[i], context.temp_allocator) - } - - envs_cstrs := make([]cstring, len(envs) + 1, context.temp_allocator) - for i := 0; i < len(envs); i += 1 { - envs_cstrs[i] = strings.clone_to_cstring(envs[i], context.temp_allocator) - } - - child_pid: posix.pid_t - status: posix.Errno - if is_spawnp { - status = posix.posix_spawnp(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) - } else { - status = posix.posix_spawn(&child_pid, path_cstr, file_actions, attributes, raw_data(args_cstrs), raw_data(envs_cstrs)) - } - if status != .NONE { - return 0, Platform_Error(status) - } - return child_pid, nil -} -spawn :: proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr) -> (posix.pid_t, Error) { - return _spawn(path, args, envs, file_actions, attributes, false) -} -spawnp :: proc(path: string, args: []string, envs: []string, file_actions: rawptr, attributes: rawptr) -> (posix.pid_t, Error) { - return _spawn(path, args, envs, file_actions, attributes, true) -} @(require_results) open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (handle: Handle, err: Error) { diff --git a/core/sys/posix/spawn.odin b/core/sys/posix/spawn.odin index 8933915be..584201bcf 100644 --- a/core/sys/posix/spawn.odin +++ b/core/sys/posix/spawn.odin @@ -7,6 +7,14 @@ when ODIN_OS == .Darwin { } foreign lib { + /* + Creates a child process from a provided filepath + spawnp searches directories on the path for the file + + Returns: 0 on success, with the child pid returned in the pid argument, or error values on failure. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html ]] + */ posix_spawn :: proc(pid: ^pid_t, path: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> Errno --- posix_spawnp :: proc(pid: ^pid_t, file: cstring, file_actions: rawptr, attrp: rawptr, argv: [^]cstring, envp: [^]cstring) -> Errno --- } From 9a2e6fff481968f201e6bf6a601ab7a4e3fbef45 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 12:21:46 -0700 Subject: [PATCH 057/160] switch to read_cycle_counter_frequency from arm64_read_cycle_counter_freq, trap on invalid arch --- base/intrinsics/intrinsics.odin | 3 +-- core/time/tsc_darwin.odin | 2 +- core/time/tsc_linux.odin | 2 +- src/check_builtin.cpp | 2 +- src/checker_builtin_procs.hpp | 4 ++-- src/llvm_backend_proc.cpp | 16 ++++++++++------ 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 7057aba84..be75739fe 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -32,6 +32,7 @@ trap :: proc() -> ! --- alloca :: proc(size, align: int) -> [^]u8 --- cpu_relax :: proc() --- read_cycle_counter :: proc() -> i64 --- +read_cycle_counter_frequency :: proc() -> i64 --- count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- @@ -361,8 +362,6 @@ wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_u x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) --- x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- -// Arm64 targets -arm64_read_cycle_counter_frequency :: proc() -> i64 --- // Darwin targets only diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin index 55abf5868..78d5b33f8 100644 --- a/core/time/tsc_darwin.odin +++ b/core/time/tsc_darwin.odin @@ -8,7 +8,7 @@ _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) { if ODIN_ARCH == .amd64 { unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return } else if ODIN_ARCH == .arm64 { - freq = u64(intrinsics.arm64_read_cycle_counter_frequency()) + freq = u64(intrinsics.read_cycle_counter_frequency()) } else { return } diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin index bfae79fb4..7232e0631 100644 --- a/core/time/tsc_linux.odin +++ b/core/time/tsc_linux.odin @@ -7,7 +7,7 @@ import linux "core:sys/linux" _get_tsc_frequency :: proc "contextless" () -> (u64, bool) { if ODIN_ARCH == .arm64 { - frequency := u64(intrinsics.arm64_read_cycle_counter_frequency()) + frequency := u64(intrinsics.read_cycle_counter_frequency()) return frequency, true } else { // Get the file descriptor for the perf mapping diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e9da79932..66be3c69e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4713,7 +4713,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As } break; - case BuiltinProc_arm64_read_cycle_counter_frequency: + case BuiltinProc_read_cycle_counter_frequency: case BuiltinProc_read_cycle_counter: operand->mode = Addressing_Value; operand->type = t_i64; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 474932970..8e135ab10 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -61,6 +61,7 @@ enum BuiltinProcId { BuiltinProc_trap, BuiltinProc_debug_trap, BuiltinProc_read_cycle_counter, + BuiltinProc_read_cycle_counter_frequency, BuiltinProc_count_ones, BuiltinProc_count_zeros, @@ -225,7 +226,6 @@ BuiltinProc__simd_end, BuiltinProc_x86_cpuid, BuiltinProc_x86_xgetbv, - BuiltinProc_arm64_read_cycle_counter_frequency, // Constant type tests @@ -423,6 +423,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("trap"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics, /*diverging*/true}, {STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false}, {STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("read_cycle_counter_frequency"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("count_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -586,7 +587,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("syscall_bsd"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("arm64_read_cycle_counter_frequency"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 3f5819b43..5e38e6bdc 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2809,16 +2809,20 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } return res; } - case BuiltinProc_arm64_read_cycle_counter_frequency: + case BuiltinProc_read_cycle_counter_frequency: { lbValue res = {}; res.type = tv.type; - LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false); - bool has_side_effects = false; - LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects); - GB_ASSERT(the_asm != nullptr); - res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + if (build_context.metrics.arch == TargetArch_arm64) { + LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false); + bool has_side_effects = false; + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects); + GB_ASSERT(the_asm != nullptr); + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + } else { + GB_PANIC("Unsupported architecture: %.*s", LIT(target_arch_names[build_context.metrics.arch])); + } return res; } From 9c5e577792eff2cd9c95b590526c17a4e8762d5a Mon Sep 17 00:00:00 2001 From: Steve Waddicor Date: Tue, 29 Jul 2025 21:01:22 +0100 Subject: [PATCH 058/160] Additional obj-c methods for darwin Foundation Added support for NSBitmapImageRep class. Added ability to set contents to a CALayer. I needed these to support a port of Handmade Hero, but they are of general use. --- .../darwin/Foundation/NSBitmapImageRep.odin | 50 +++++++++++++++++++ core/sys/darwin/Foundation/NSWindow.odin | 8 +++ 2 files changed, 58 insertions(+) create mode 100644 core/sys/darwin/Foundation/NSBitmapImageRep.odin diff --git a/core/sys/darwin/Foundation/NSBitmapImageRep.odin b/core/sys/darwin/Foundation/NSBitmapImageRep.odin new file mode 100644 index 000000000..2fcdf6530 --- /dev/null +++ b/core/sys/darwin/Foundation/NSBitmapImageRep.odin @@ -0,0 +1,50 @@ +package objc_Foundation + +import "base:intrinsics" + +@(objc_class="NSBitmapImageRep") +BitmapImageRep :: struct { using _: Object } + +@(objc_type=BitmapImageRep, objc_name="alloc", objc_is_class_method=true) +BitmapImageRep_alloc :: proc "c" () -> ^BitmapImageRep { + return msgSend(^BitmapImageRep, BitmapImageRep, "alloc") +} + +@(objc_type=BitmapImageRep, objc_name="initWithBitmapDataPlanes") +BitmapImageRep_initWithBitmapDataPlanes :: proc "c" ( + self: ^BitmapImageRep, + bitmapDataPlanes: ^^u8, + pixelsWide: Integer, + pixelsHigh: Integer, + bitsPerSample: Integer, + samplesPerPixel: Integer, + hasAlpha: bool, + isPlanar: bool, + colorSpaceName: ^String, + bytesPerRow: Integer, + bitsPerPixel: Integer) -> ^BitmapImageRep { + + return msgSend(^BitmapImageRep, + self, + "initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:", + bitmapDataPlanes, + pixelsWide, + pixelsHigh, + bitsPerSample, + samplesPerPixel, + hasAlpha, + isPlanar, + colorSpaceName, + bytesPerRow, + bitsPerPixel) +} + +@(objc_type=BitmapImageRep, objc_name="bitmapData") +BitmapImageRep_bitmapData :: proc "c" (self: ^BitmapImageRep) -> rawptr { + return msgSend(rawptr, self, "bitmapData") +} + +@(objc_type=BitmapImageRep, objc_name="CGImage") +BitmapImageRep_CGImage :: proc "c" (self: ^BitmapImageRep) -> rawptr { + return msgSend(rawptr, self, "CGImage") +} diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin index f113dd3df..fb280aa79 100644 --- a/core/sys/darwin/Foundation/NSWindow.odin +++ b/core/sys/darwin/Foundation/NSWindow.odin @@ -568,6 +568,14 @@ window_delegate_register_and_alloc :: proc(template: WindowDelegateTemplate, cla @(objc_class="CALayer") Layer :: struct { using _: Object } +@(objc_type=Layer, objc_name="contents") +Layer_contents :: proc "c" (self: ^Layer) -> rawptr { + return msgSend(rawptr, self, "contents") +} +@(objc_type=Layer, objc_name="setContents") +Layer_setContents :: proc "c" (self: ^Layer, contents: rawptr) { + msgSend(nil, self, "setContents:", contents) +} @(objc_type=Layer, objc_name="contentsScale") Layer_contentsScale :: proc "c" (self: ^Layer) -> Float { return msgSend(Float, self, "contentsScale") From 4d9fdf5bd381c21ae68e08e52a0d98729a390cfe Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 13:09:05 -0700 Subject: [PATCH 059/160] move to checker error, rather than panic --- src/check_builtin.cpp | 8 ++++++++ src/llvm_backend_proc.cpp | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 66be3c69e..974224ed2 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4714,6 +4714,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; case BuiltinProc_read_cycle_counter_frequency: + if (build_context.metrics.arch != TargetArch_arm64) { + error(call, "'%.*s' is only allowed on arm64 targets", LIT(builtin_name)); + return false; + } + operand->mode = Addressing_Value; + operand->type = t_i64; + break; + case BuiltinProc_read_cycle_counter: operand->mode = Addressing_Value; operand->type = t_i64; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 5e38e6bdc..d89720e0d 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2820,8 +2820,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs $0, cntfrq_el0"), str_lit("=r"), has_side_effects); GB_ASSERT(the_asm != nullptr); res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); - } else { - GB_PANIC("Unsupported architecture: %.*s", LIT(target_arch_names[build_context.metrics.arch])); } return res; From 517e392abedcdb3ac76fbbf737318b65e855e0c5 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 13:18:40 -0700 Subject: [PATCH 060/160] if -> when --- core/time/tsc_linux.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin index 7232e0631..5b4c1c2c2 100644 --- a/core/time/tsc_linux.odin +++ b/core/time/tsc_linux.odin @@ -6,7 +6,7 @@ import "base:intrinsics" import linux "core:sys/linux" _get_tsc_frequency :: proc "contextless" () -> (u64, bool) { - if ODIN_ARCH == .arm64 { + when ODIN_ARCH == .arm64 { frequency := u64(intrinsics.read_cycle_counter_frequency()) return frequency, true } else { From 35a95fc3e4c67dcf32462c112675699673c037f2 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 13:28:04 -0700 Subject: [PATCH 061/160] workaround to avoid excessive file-splitting --- core/time/tsc_linux.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin index 5b4c1c2c2..f59a0338b 100644 --- a/core/time/tsc_linux.odin +++ b/core/time/tsc_linux.odin @@ -3,7 +3,7 @@ package time import "base:intrinsics" -import linux "core:sys/linux" +@(require) import linux "core:sys/linux" _get_tsc_frequency :: proc "contextless" () -> (u64, bool) { when ODIN_ARCH == .arm64 { From 42f2891f35a6f3de733783e599bc6e7f547ac510 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 13:54:56 -0700 Subject: [PATCH 062/160] attempt to fix x86 osx build --- core/time/tsc_darwin.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin index 78d5b33f8..925bd55e0 100644 --- a/core/time/tsc_darwin.odin +++ b/core/time/tsc_darwin.odin @@ -5,9 +5,9 @@ import "base:intrinsics" import "core:sys/unix" _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) { - if ODIN_ARCH == .amd64 { + when ODIN_ARCH == .amd64 { unix.sysctlbyname("machdep.tsc.frequency", &freq) or_return - } else if ODIN_ARCH == .arm64 { + } else when ODIN_ARCH == .arm64 { freq = u64(intrinsics.read_cycle_counter_frequency()) } else { return From b5a8a3cf944b0861b8188b4ba59eb8f0da3523b6 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Tue, 29 Jul 2025 13:58:30 -0700 Subject: [PATCH 063/160] require to reduce files for a one line function --- core/time/tsc_darwin.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin index 925bd55e0..2efd35f20 100644 --- a/core/time/tsc_darwin.odin +++ b/core/time/tsc_darwin.odin @@ -2,7 +2,7 @@ package time import "base:intrinsics" -import "core:sys/unix" +@require import "core:sys/unix" _get_tsc_frequency :: proc "contextless" () -> (freq: u64, ok: bool) { when ODIN_ARCH == .amd64 { From e54bf247468bd9d316ffe059bd7b3f923363478e Mon Sep 17 00:00:00 2001 From: Mecso2 Date: Wed, 30 Jul 2025 12:44:53 +0200 Subject: [PATCH 064/160] fix amd64 no-crt entry assembly --- base/runtime/entry_unix_no_crt_amd64.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/runtime/entry_unix_no_crt_amd64.asm b/base/runtime/entry_unix_no_crt_amd64.asm index f0bdce8d7..1d2d3ccea 100644 --- a/base/runtime/entry_unix_no_crt_amd64.asm +++ b/base/runtime/entry_unix_no_crt_amd64.asm @@ -35,7 +35,7 @@ _start: xor rbp, rbp ;; Load argc into 1st param reg, argv into 2nd param reg pop rdi - mov rdx, rsi + mov rsi, rsp ;; Align stack pointer down to 16-bytes (sysv calling convention) and rsp, -16 ;; Call into odin entry point From 3e10684630bd6cd0602124cf3cb574345483eb93 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Wed, 30 Jul 2025 14:09:50 -0700 Subject: [PATCH 065/160] adjust scale with new format, fix segfault for auto-trace --- core/prof/spall/spall.odin | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 4d37913e4..16b809359 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -97,7 +97,7 @@ context_create_with_scale :: proc(filename: string, precise_time: bool, timestam context_create_with_sleep :: proc(filename: string, sleep := 2 * time.Second) -> (ctx: Context, ok: bool) #optional_ok { freq, freq_ok := time.tsc_frequency(sleep) - timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1 + timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000_000) if freq_ok else 1 return context_create_with_scale(filename, freq_ok, timestamp_scale) } @@ -125,6 +125,10 @@ buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buff @(no_instrumentation) buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_check /* bounds check would segfault instrumentation */ { + if len(buffer.data) == 0 { + return + } + buffer_size := buffer.head - size_of(Manual_Buffer_Header) hdr := (^Manual_Buffer_Header)(raw_data(buffer.data)) hdr.size = u32le(buffer_size) From aa6a7498045788b318436db22e44b763799a4c22 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Jul 2025 23:11:18 +0100 Subject: [PATCH 066/160] Improve atomic logic for `sync.Wait_Group` --- core/sync/extended.odin | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 0cea38d7f..199407428 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -47,12 +47,12 @@ wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) { guard(&wg.mutex) atomic_add(&wg.counter, delta) - if wg.counter < 0 { + switch counter := atomic_load(&wg.counter); counter { + case counter < 0: panic_contextless("sync.Wait_Group negative counter") - } - if wg.counter == 0 { + case if wg.counter == 0: cond_broadcast(&wg.cond) - if wg.counter != 0 { + if atomic_load(&wg.counter) != 0 { panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") } } @@ -78,11 +78,8 @@ wait group's internal counter reaches zero. wait_group_wait :: proc "contextless" (wg: ^Wait_Group) { guard(&wg.mutex) - if wg.counter != 0 { + for atomic_load(&wg.counter) != 0 { cond_wait(&wg.cond, &wg.mutex) - if wg.counter != 0 { - panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") - } } } @@ -100,13 +97,10 @@ wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: t } guard(&wg.mutex) - if wg.counter != 0 { + for atomic_load(&wg.counter) != 0 { if !cond_wait_with_timeout(&wg.cond, &wg.mutex, duration) { return false } - if wg.counter != 0 { - panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") - } } return true } From 7057fc8dfc960ad3e7ea4f76deeaafd0cdcf4cf6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Jul 2025 23:14:29 +0100 Subject: [PATCH 067/160] Remove the semantics of `#no_copy`, keep the grammar --- base/runtime/core.odin | 2 +- src/check_decl.cpp | 7 ------- src/check_expr.cpp | 23 ----------------------- src/check_stmt.cpp | 2 -- src/check_type.cpp | 1 - src/llvm_backend_type.cpp | 2 +- src/name_canonicalization.cpp | 1 - src/types.cpp | 7 ------- 8 files changed, 2 insertions(+), 43 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 090d1a65b..baecb4146 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -115,7 +115,7 @@ Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8] Type_Info_Struct_Flag :: enum u8 { packed = 0, raw_union = 1, - no_copy = 2, + _ = 2, align = 3, } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0bacf891b..dd4c09e85 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -145,13 +145,6 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l if (d != nullptr) { d->init_expr = o->expr; } - - if (o->type && is_type_no_copy(o->type)) { - ERROR_BLOCK(); - if (check_no_copy_assignment(*o, str_lit("initialization"))) { - error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression"); - } - } } if (rhs_count > 0 && lhs_count != rhs_count) { error(lhs[0]->token, "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index dd6a89e5b..6723a7580 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5763,22 +5763,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals return false; } -gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) { - if (o.type && is_type_no_copy(o.type)) { - Ast *expr = unparen_expr(o.expr); - if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) { - if (expr->kind == Ast_CallExpr) { - // Okay - } else { - error(o.expr, "Invalid use of #no_copy value in %.*s", LIT(context)); - return true; - } - } - } - return false; -} - - gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, Array *operands, Slice const &rhs) { bool optional_ok = false; isize tuple_index = 0; @@ -5849,7 +5833,6 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array for (Entity *e : tuple->variables) { o.type = e->type; array_add(operands, o); - check_no_copy_assignment(o, str_lit("assignment")); } tuple_index += tuple->variables.count; @@ -6236,12 +6219,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } - for (Operand const &o : ordered_operands) { - if (o.mode != Addressing_Invalid) { - check_no_copy_assignment(o, str_lit("procedure call expression")); - } - } - for (isize i = 0; i < pt->param_count; i++) { if (!visited[i]) { Entity *e = pt->params->Tuple.variables[i]; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 7187d95b3..bc9b6c5dd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -430,8 +430,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O Ast *node = unparen_expr(lhs->expr); - check_no_copy_assignment(*rhs, context_name); - // NOTE(bill): Ignore assignments to '_' if (is_blank_ident(node)) { check_assignment(ctx, rhs, nullptr, str_lit("assignment to '_' identifier")); diff --git a/src/check_type.cpp b/src/check_type.cpp index 5f9540ee0..79705b928 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -646,7 +646,6 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * struct_type->Struct.node = node; struct_type->Struct.scope = ctx->scope; struct_type->Struct.is_packed = st->is_packed; - struct_type->Struct.is_no_copy = st->is_no_copy; struct_type->Struct.polymorphic_params = check_record_polymorphic_params( ctx, st->polymorphic_params, &struct_type->Struct.is_polymorphic, diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 4e514c3d1..43c5f0b40 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -797,7 +797,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ u8 flags = 0; if (t->Struct.is_packed) flags |= 1<<0; if (t->Struct.is_raw_union) flags |= 1<<1; - if (t->Struct.is_no_copy) flags |= 1<<2; + // if (t->Struct.custom_align) flags |= 1<<3; vals[6] = lb_const_int(m, t_u8, flags).value; diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 0372f5039..e3090368a 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -687,7 +687,6 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { if (type->Struct.is_packed) type_writer_appendc(w, "#packed"); if (type->Struct.is_raw_union) type_writer_appendc(w, "#raw_union"); - if (type->Struct.is_no_copy) type_writer_appendc(w, "#no_copy"); if (type->Struct.custom_min_field_align != 0) type_writer_append_fmt(w, "#min_field_align(%lld)", cast(long long)type->Struct.custom_min_field_align); if (type->Struct.custom_max_field_align != 0) type_writer_append_fmt(w, "#max_field_align(%lld)", cast(long long)type->Struct.custom_max_field_align); if (type->Struct.custom_align != 0) type_writer_append_fmt(w, "#align(%lld)", cast(long long)type->Struct.custom_align); diff --git a/src/types.cpp b/src/types.cpp index 74da7f6aa..2e696810d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -155,7 +155,6 @@ struct TypeStruct { bool are_offsets_being_processed : 1; bool is_packed : 1; bool is_raw_union : 1; - bool is_no_copy : 1; bool is_poly_specialized : 1; }; @@ -1780,10 +1779,6 @@ gb_internal bool is_type_raw_union(Type *t) { t = base_type(t); return (t->kind == Type_Struct && t->Struct.is_raw_union); } -gb_internal bool is_type_no_copy(Type *t) { - t = base_type(t); - return (t->kind == Type_Struct && t->Struct.is_no_copy); -} gb_internal bool is_type_enum(Type *t) { t = base_type(t); return (t->kind == Type_Enum); @@ -2859,7 +2854,6 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple case Type_Struct: if (x->Struct.is_raw_union == y->Struct.is_raw_union && - x->Struct.is_no_copy == y->Struct.is_no_copy && x->Struct.fields.count == y->Struct.fields.count && x->Struct.is_packed == y->Struct.is_packed && x->Struct.soa_kind == y->Struct.soa_kind && @@ -4832,7 +4826,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed"); if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); - if (type->Struct.is_no_copy) str = gb_string_appendc(str, " #no_copy"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); str = gb_string_appendc(str, " {"); From a2e7a62a2633b253acb4de8249319cc1968f659f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Jul 2025 23:14:53 +0100 Subject: [PATCH 068/160] Add `virtual.new_clone` --- core/mem/virtual/arena_util.odin | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/mem/virtual/arena_util.odin b/core/mem/virtual/arena_util.odin index 4e28c17d6..fb778687b 100644 --- a/core/mem/virtual/arena_util.odin +++ b/core/mem/virtual/arena_util.odin @@ -20,6 +20,17 @@ new_aligned :: proc(arena: ^Arena, $T: typeid, alignment: uint, loc := #caller_l return } +// The `new_clone` procedure allocates memory for a type `T` from a `virtual.Arena`. The second argument is a value that +// is to be copied to the allocated data. The value returned is a pointer to a newly allocated value of that type using the specified allocator. +@(require_results) +new_clone :: proc(arena: ^Arena, data: $T, loc := #caller_location) -> (ptr: ^T, err: Allocator_Error) { + ptr, err = new_aligned(arena, T, align_of(T), loc) + if ptr != nil && err == nil { + ptr^ = data + } + return +} + // `make_slice` allocates and initializes a slice. Like `new`, the second argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // From 6a10cfdc304dee49549af638836d8a1b64326958 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Jul 2025 23:15:14 +0100 Subject: [PATCH 069/160] Fix typo --- core/sync/extended.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 199407428..f1d8348ee 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -50,7 +50,7 @@ wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) { switch counter := atomic_load(&wg.counter); counter { case counter < 0: panic_contextless("sync.Wait_Group negative counter") - case if wg.counter == 0: + case wg.counter == 0: cond_broadcast(&wg.cond) if atomic_load(&wg.counter) != 0 { panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") From 9b8223dd69d70c6f44fd9c65194f895cdcae4940 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Jul 2025 23:19:14 +0100 Subject: [PATCH 070/160] Remove use of `.no_copy` --- base/runtime/print.odin | 2 +- core/reflect/types.odin | 2 +- core/sync/extended.odin | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/runtime/print.odin b/base/runtime/print.odin index c28fd593d..145f002d1 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -403,7 +403,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_string("struct ") if .packed in info.flags { print_string("#packed ") } if .raw_union in info.flags { print_string("#raw_union ") } - if .no_copy in info.flags { print_string("#no_copy ") } + // if .no_copy in info.flags { print_string("#no_copy ") } if .align in info.flags { print_string("#align(") print_u64(u64(ti.align)) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index ba47fee4d..511c5c9bd 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -630,7 +630,7 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt io.write_string(w, "struct ", &n) or_return if .packed in info.flags { io.write_string(w, "#packed ", &n) or_return } if .raw_union in info.flags { io.write_string(w, "#raw_union ", &n) or_return } - if .no_copy in info.flags { io.write_string(w, "#no_copy ", &n) or_return } + // if .no_copy in info.flags { io.write_string(w, "#no_copy ", &n) or_return } if .align in info.flags { io.write_string(w, "#align(", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return diff --git a/core/sync/extended.odin b/core/sync/extended.odin index f1d8348ee..82fc3d751 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -47,7 +47,7 @@ wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) { guard(&wg.mutex) atomic_add(&wg.counter, delta) - switch counter := atomic_load(&wg.counter); counter { + switch counter := atomic_load(&wg.counter); { case counter < 0: panic_contextless("sync.Wait_Group negative counter") case wg.counter == 0: From 393e00bec3e855475659de0c6c38d3898a36cb36 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 31 Jul 2025 16:40:40 +0200 Subject: [PATCH 071/160] Placate linker --- base/runtime/entry_unix_no_crt_amd64.asm | 1 + 1 file changed, 1 insertion(+) diff --git a/base/runtime/entry_unix_no_crt_amd64.asm b/base/runtime/entry_unix_no_crt_amd64.asm index 1d2d3ccea..225356a2f 100644 --- a/base/runtime/entry_unix_no_crt_amd64.asm +++ b/base/runtime/entry_unix_no_crt_amd64.asm @@ -3,6 +3,7 @@ bits 64 extern _start_odin global _start +section .note.GNU-stack section .text ;; Entry point for programs that specify -no-crt option From 9d40f371bebaca6c74b615fa03dd5574eb51e327 Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Wed, 30 Jul 2025 12:37:12 -0400 Subject: [PATCH 072/160] Add static SIMD support to XXH3 in core:hash/xxhash. This uses compile-time features to decide how large of a SIMD vector to use. It currently has checks for amd64/i386 to size its vectors for SSE2/AVX2/AVX512 as necessary. The generalized SIMD functions could also be useful for multiversioning of the hash procs, to allow for run-time dispatch based on available CPU features. --- core/hash/xxhash/common.odin | 32 ++++++++++ core/hash/xxhash/xxhash_3.odin | 88 ++++++++++++++++++++++++++-- core/hash/xxhash/xxhash_3_intel.odin | 13 ++++ core/hash/xxhash/xxhash_3_other.odin | 8 +++ 4 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 core/hash/xxhash/xxhash_3_intel.odin create mode 100644 core/hash/xxhash/xxhash_3_other.odin diff --git a/core/hash/xxhash/common.odin b/core/hash/xxhash/common.odin index adfc1bac2..636393b52 100644 --- a/core/hash/xxhash/common.odin +++ b/core/hash/xxhash/common.odin @@ -101,3 +101,35 @@ XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) return u64(b) } } + +XXH64_read64_simd :: #force_inline proc(buf: []$E, $W: uint, alignment := Alignment.Unaligned) -> (res: #simd[W]u64) { + if alignment == .Aligned { + res = (^#simd[W]u64)(raw_data(buf))^ + } else { + res = intrinsics.unaligned_load((^#simd[W]u64)(raw_data(buf))) + } + + when ODIN_ENDIAN == .Big { + bytes := transmute(#simd[W*8]u8)res + bytes = intrinsics.simd_lanes_reverse(bytes) + res = transmute(#simd[W]u64)bytes + res = intrinsics.simd_lanes_reverse(res) + } + return +} + +XXH64_write64_simd :: #force_inline proc(buf: []$E, value: $V/#simd[$W]u64, alignment := Alignment.Unaligned) { + value := value + when ODIN_ENDIAN == .Big { + bytes := transmute(#simd[W*8]u8)value + bytes = intrinsics.simd_lanes_reverse(bytes) + value = transmute(#simd[W]u64)bytes + value = intrinsics.simd_lanes_reverse(value) + } + + if alignment == .Aligned { + (^V)(raw_data(buf))^ = value + } else { + intrinsics.unaligned_store((^V)(raw_data(buf)), value) + } +} diff --git a/core/hash/xxhash/xxhash_3.odin b/core/hash/xxhash/xxhash_3.odin index 293e98528..8e88d4a90 100644 --- a/core/hash/xxhash/xxhash_3.odin +++ b/core/hash/xxhash/xxhash_3.odin @@ -52,6 +52,7 @@ XXH3_SECRET_SIZE_MIN :: 136 #assert(len(XXH3_kSecret) == 192 && len(XXH3_kSecret) > XXH3_SECRET_SIZE_MIN) XXH_ACC_ALIGN :: 8 /* scalar */ +XXH_MAX_WIDTH :: #config(XXH_MAX_WIDTH, 512) / 64 /* This is the optimal update size for incremental hashing. @@ -733,10 +734,6 @@ XXH3_accumulate_512_f :: #type proc(acc: []xxh_u64, input: []u8, secret: XXH3_scramble_accumulator_f :: #type proc(acc: []xxh_u64, secret: []u8) XXH3_init_custom_secret_f :: #type proc(custom_secret: []u8, seed64: xxh_u64) -XXH3_accumulate_512 : XXH3_accumulate_512_f = XXH3_accumulate_512_scalar -XXH3_scramble_accumulator : XXH3_scramble_accumulator_f = XXH3_scramble_accumulator_scalar -XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar - /* scalar variants - universal */ @(optimization_mode="favor_size") XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) { @@ -785,6 +782,89 @@ XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64 } } +/* generalized SIMD variants */ +@(optimization_mode="favor_size") +XXH3_accumulate_512_simd_generic :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8, $W: uint) { + u32xW :: #simd[W]u32 + u64xW :: #simd[W]u64 + + #no_bounds_check for i in uint(0).. 1 { + XXH3_accumulate_512_simd_generic(acc, input, secret, XXH_NATIVE_WIDTH) + } else { + XXH3_accumulate_512_scalar(acc, input, secret) + } +} + +XXH3_scramble_accumulator :: #force_inline proc(acc: []xxh_u64, secret: []u8) { + when XXH_NATIVE_WIDTH > 1 { + XXH3_scramble_accumulator_simd_generic(acc, secret, XXH_NATIVE_WIDTH) + } else { + XXH3_scramble_accumulator_scalar(acc, secret) + } +} + +XXH3_init_custom_secret :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) { + when XXH_NATIVE_WIDTH > 1 { + XXH3_init_custom_secret_simd_generic(custom_secret, seed64, XXH_NATIVE_WIDTH) + } else { + XXH3_init_custom_secret_scalar(custom_secret, seed64) + } +} + XXH_PREFETCH_DIST :: 320 /* diff --git a/core/hash/xxhash/xxhash_3_intel.odin b/core/hash/xxhash/xxhash_3_intel.odin new file mode 100644 index 000000000..3397fcef5 --- /dev/null +++ b/core/hash/xxhash/xxhash_3_intel.odin @@ -0,0 +1,13 @@ +#+build amd64, i386 +package xxhash + +import "base:intrinsics" + +@(private="file") SSE2_FEATURES :: "sse2" +@(private="file") AVX2_FEATURES :: "avx2" +@(private="file") AVX512_FEATURES :: "avx512dq,evex512" + +XXH_NATIVE_WIDTH :: min(XXH_MAX_WIDTH, + 8 when intrinsics.has_target_feature(AVX512_FEATURES) else + 4 when intrinsics.has_target_feature(AVX2_FEATURES) else + 2 when intrinsics.has_target_feature(SSE2_FEATURES) else 1) diff --git a/core/hash/xxhash/xxhash_3_other.odin b/core/hash/xxhash/xxhash_3_other.odin new file mode 100644 index 000000000..e1a5d0474 --- /dev/null +++ b/core/hash/xxhash/xxhash_3_other.odin @@ -0,0 +1,8 @@ +#+build !amd64 +#+build !i386 +package xxhash + +import "base:runtime" + +XXH_NATIVE_WIDTH :: min(XXH_MAX_WIDTH, + 2 when runtime.HAS_HARDWARE_SIMD else 1) From 2f8b390c1922f096daf56c2400b212457fd37dca Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Wed, 30 Jul 2025 14:29:44 -0400 Subject: [PATCH 073/160] Various minor changes in XXH3. This includes various minor things that didn't seem right or could be improved, including: - XXH3_state is documented to have a strict alignment requirement of 64 bytes, and thus came with a disclaimer not to use `new` because it wouldn't be aligned correctly. It now has an `#align(64)` so that it will. - An _internal proc being marked #force_no_inline (every other one is #force_inline) - Unnecessarily casting the product of two u32s through u128 (and ultimately truncating to u64 anyway) --- core/hash/xxhash/xxhash_3.odin | 36 ++++++++++++++++------------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/core/hash/xxhash/xxhash_3.odin b/core/hash/xxhash/xxhash_3.odin index 8e88d4a90..555390bc5 100644 --- a/core/hash/xxhash/xxhash_3.odin +++ b/core/hash/xxhash/xxhash_3.odin @@ -63,10 +63,11 @@ XXH3_INTERNAL_BUFFER_SIZE :: 256 Streaming state. IMPORTANT: This structure has a strict alignment requirement of 64 bytes!! ** - Do not allocate this with `make()` or `new`, it will not be sufficiently aligned. - Use`XXH3_create_state` and `XXH3_destroy_state, or stack allocation. + Default allocators will align it correctly if created via `new`, as will + placing this struct on the cache, but if using a custom allocator make sure + that it handles the alignment correctly! */ -XXH3_state :: struct { +XXH3_state :: struct #align(64) { acc: [8]u64, custom_secret: [XXH_SECRET_DEFAULT_SIZE]u8, buffer: [XXH3_INTERNAL_BUFFER_SIZE]u8, @@ -478,7 +479,7 @@ XXH3_128bits_internal :: #force_inline proc( /* === Public XXH128 API === */ @(optimization_mode="favor_size") XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) { - return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed) + return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_default) } @(optimization_mode="favor_size") @@ -748,7 +749,7 @@ XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, se sec := XXH64_read64(xsecret[8 * i:]) data_key := data_val ~ sec xacc[i ~ 1] += data_val /* swap adjacent lanes */ - xacc[i ] += u64(u128(u32(data_key)) * u128(u64(data_key >> 32))) + xacc[i ] += u64(u32(data_key)) * u64(data_key >> 32) } } @@ -966,19 +967,8 @@ XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator) } -/* - XXH3_hashLong_64b_withSeed(): - Generate a custom key based on alteration of default XXH3_kSecret with the seed, - and then use this key for long mode hashing. - - This operation is decently fast but nonetheless costs a little bit of time. - Try to avoid it whenever possible (typically when seed==0). - - It's important for performance that XXH3_hashLong is not inlined. Not sure - why (uop cache maybe?), but the difference is large and easily measurable. -*/ @(optimization_mode="favor_size") -XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc( +XXH3_hashLong_64b_withSeed_internal :: #force_inline proc( input: []u8, seed: xxh_u64, f_acc512: XXH3_accumulate_512_f, @@ -995,7 +985,15 @@ XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc( } /* - It's important for performance that XXH3_hashLong is not inlined. + XXH3_hashLong_64b_withSeed(): + Generate a custom key based on alteration of default XXH3_kSecret with the seed, + and then use this key for long mode hashing. + + This operation is decently fast but nonetheless costs a little bit of time. + Try to avoid it whenever possible (typically when seed==0). + + It's important for performance that XXH3_hashLong is not inlined. Not sure + why (uop cache maybe?), but the difference is large and easily measurable. */ @(optimization_mode="favor_size") XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) { @@ -1006,7 +1004,7 @@ XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64) @(optimization_mode="favor_size") -XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) { +XXH3_64bits_internal :: #force_inline proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) { assert(len(secret) >= XXH3_SECRET_SIZE_MIN) /* If an action is to be taken if len(secret) condition is not respected, it should be done here. From f61dc7d071a95b1cdaaaed6f1451c7e1ddf384c5 Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Wed, 30 Jul 2025 16:47:06 -0400 Subject: [PATCH 074/160] Remove favor_size attributes inhibiting SIMD optimizations. This makes a tremendous (2x with SSE2, 3x with AVX2) difference on big datasets on my system, but this may be hardware-dependent (e.g. instruction cache sizes). Naturally, this also results in somewhat larger code for the large-data case (~75% larger). --- core/hash/xxhash/xxhash_3.odin | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core/hash/xxhash/xxhash_3.odin b/core/hash/xxhash/xxhash_3.odin index 555390bc5..bd5534f23 100644 --- a/core/hash/xxhash/xxhash_3.odin +++ b/core/hash/xxhash/xxhash_3.odin @@ -382,7 +382,6 @@ XXH3_INIT_ACC :: [XXH_ACC_NB]xxh_u64{ XXH_SECRET_MERGEACCS_START :: 11 -@(optimization_mode="favor_size") XXH3_hashLong_128b_internal :: #force_inline proc( input: []u8, secret: []u8, @@ -410,7 +409,6 @@ XXH3_hashLong_128b_internal :: #force_inline proc( /* * It's important for performance that XXH3_hashLong is not inlined. */ -@(optimization_mode="favor_size") XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) { return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator) } @@ -418,12 +416,10 @@ XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, /* * It's important for performance that XXH3_hashLong is not inlined. */ -@(optimization_mode="favor_size") XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) { return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator) } -@(optimization_mode="favor_size") XXH3_hashLong_128b_withSeed_internal :: #force_inline proc( input: []u8, seed: xxh_u64, secret: []u8, f_acc512: XXH3_accumulate_512_f, @@ -444,7 +440,6 @@ XXH3_hashLong_128b_withSeed_internal :: #force_inline proc( /* * It's important for performance that XXH3_hashLong is not inlined. */ - @(optimization_mode="favor_size") XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) { return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret) } @@ -784,7 +779,6 @@ XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64 } /* generalized SIMD variants */ -@(optimization_mode="favor_size") XXH3_accumulate_512_simd_generic :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8, $W: uint) { u32xW :: #simd[W]u32 u64xW :: #simd[W]u64 @@ -824,7 +818,6 @@ XXH3_scramble_accumulator_simd_generic :: #force_inline proc(acc: []xxh_u64, sec } } -@(optimization_mode="favor_size") XXH3_init_custom_secret_simd_generic :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64, $W: uint) { u64xW :: #simd[W]u64 @@ -950,7 +943,6 @@ XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8, /* It's important for performance that XXH3_hashLong is not inlined. */ -@(optimization_mode="favor_size") XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) { return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator) } @@ -962,12 +954,10 @@ XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u This variant enforces that the compiler can detect that, and uses this opportunity to streamline the generated code for better performance. */ -@(optimization_mode="favor_size") XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) { return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator) } -@(optimization_mode="favor_size") XXH3_hashLong_64b_withSeed_internal :: #force_inline proc( input: []u8, seed: xxh_u64, @@ -995,7 +985,6 @@ XXH3_hashLong_64b_withSeed_internal :: #force_inline proc( It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe?), but the difference is large and easily measurable. */ -@(optimization_mode="favor_size") XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) { return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret) } From c3bae964d0223121985a52d3c860f879f842f0d3 Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 31 Jul 2025 20:26:22 +0200 Subject: [PATCH 075/160] amd64 abi fixes regarding vectors - Fixes the code so SSEUp is grouped/skipped over properly (Fixes #5429) - Fixes f16 vectors using garbage widths, because it would call LLVMGetIntTypeWidth and an f16 is not an int so doesn't have that function --- src/llvm_abi.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index e1cbe7558..c4c5b5a63 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -850,14 +850,18 @@ namespace lbAbiAmd64SysV { } else if (oldv == RegClass_SSEUp) { oldv = RegClass_SSEDv; } else if (is_sse(oldv)) { - i++; - while (i != e && oldv == RegClass_SSEUp) { - i++; + for (i++; i < e; i++) { + RegClass v = (*cls)[cast(isize)i]; + if (v != RegClass_SSEUp) { + break; + } } } else if (oldv == RegClass_X87) { - i++; - while (i != e && oldv == RegClass_X87Up) { - i++; + for (i++; i < e; i++) { + RegClass v = (*cls)[cast(isize)i]; + if (v != RegClass_X87Up) { + break; + } } } else { i++; @@ -1046,10 +1050,9 @@ namespace lbAbiAmd64SysV { i64 elem_sz = lb_sizeof(elem); LLVMTypeKind elem_kind = LLVMGetTypeKind(elem); RegClass reg = RegClass_NoClass; - unsigned elem_width = LLVMGetIntTypeWidth(elem); switch (elem_kind) { - case LLVMIntegerTypeKind: - case LLVMHalfTypeKind: + case LLVMIntegerTypeKind: { + unsigned elem_width = LLVMGetIntTypeWidth(elem); switch (elem_width) { case 8: reg = RegClass_SSEInt8; break; case 16: reg = RegClass_SSEInt16; break; @@ -1065,6 +1068,10 @@ namespace lbAbiAmd64SysV { GB_PANIC("Unhandled integer width for vector type %u", elem_width); } break; + }; + case LLVMHalfTypeKind: + reg = RegClass_SSEInt16; + break; case LLVMFloatTypeKind: reg = RegClass_SSEFv; break; From f32ee28e6d90d15f5c5fb225c19b5292e76302ed Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 31 Jul 2025 20:50:46 +0200 Subject: [PATCH 076/160] amd64 support `half` in the abi too --- src/llvm_abi.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index c4c5b5a63..3666c74b5 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -527,6 +527,8 @@ namespace lbAbiAmd64SysV { enum RegClass { RegClass_NoClass, RegClass_Int, + RegClass_SSEHs, + RegClass_SSEHv, RegClass_SSEFs, RegClass_SSEFv, RegClass_SSEDs, @@ -545,6 +547,8 @@ namespace lbAbiAmd64SysV { gb_internal bool is_sse(RegClass reg_class) { switch (reg_class) { + case RegClass_SSEHs: + case RegClass_SSEHv: case RegClass_SSEFs: case RegClass_SSEFv: case RegClass_SSEDs: @@ -693,6 +697,8 @@ namespace lbAbiAmd64SysV { case RegClass_Int: needed_int += 1; break; + case RegClass_SSEHs: + case RegClass_SSEHv: case RegClass_SSEFs: case RegClass_SSEFv: case RegClass_SSEDs: @@ -804,6 +810,8 @@ namespace lbAbiAmd64SysV { to_write = RegClass_Memory; } else if (newv == RegClass_SSEUp) { switch (oldv) { + case RegClass_SSEHv: + case RegClass_SSEHs: case RegClass_SSEFv: case RegClass_SSEFs: case RegClass_SSEDv: @@ -918,6 +926,7 @@ namespace lbAbiAmd64SysV { sz -= rs; break; } + case RegClass_SSEHv: case RegClass_SSEFv: case RegClass_SSEDv: case RegClass_SSEInt8: @@ -928,6 +937,10 @@ namespace lbAbiAmd64SysV { unsigned elems_per_word = 0; LLVMTypeRef elem_type = nullptr; switch (reg_class) { + case RegClass_SSEHv: + elems_per_word = 4; + elem_type = LLVMHalfTypeInContext(c); + break; case RegClass_SSEFv: elems_per_word = 2; elem_type = LLVMFloatTypeInContext(c); @@ -962,6 +975,10 @@ namespace lbAbiAmd64SysV { continue; } break; + case RegClass_SSEHs: + array_add(&types, LLVMHalfTypeInContext(c)); + sz -= 2; + break; case RegClass_SSEFs: array_add(&types, LLVMFloatTypeInContext(c)); sz -= 4; @@ -1008,9 +1025,11 @@ namespace lbAbiAmd64SysV { break; } case LLVMPointerTypeKind: - case LLVMHalfTypeKind: unify(cls, ix + off/8, RegClass_Int); break; + case LLVMHalfTypeKind: + unify(cls, ix + off/8, (off%8 == 2) ? RegClass_SSEHv : RegClass_SSEHs); + break; case LLVMFloatTypeKind: unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); break; @@ -1070,7 +1089,7 @@ namespace lbAbiAmd64SysV { break; }; case LLVMHalfTypeKind: - reg = RegClass_SSEInt16; + reg = RegClass_SSEHv; break; case LLVMFloatTypeKind: reg = RegClass_SSEFv; From 5527982c20b043ebfc15273a1718343a7f5669b8 Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 31 Jul 2025 22:01:17 +0200 Subject: [PATCH 077/160] fix vec offset calculation --- src/llvm_abi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 3666c74b5..b6ed395d9 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1028,7 +1028,7 @@ namespace lbAbiAmd64SysV { unify(cls, ix + off/8, RegClass_Int); break; case LLVMHalfTypeKind: - unify(cls, ix + off/8, (off%8 == 2) ? RegClass_SSEHv : RegClass_SSEHs); + unify(cls, ix + off/8, (off%8 == 6) ? RegClass_SSEHv : RegClass_SSEHs); break; case LLVMFloatTypeKind: unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); From 4ef7ed1cbdf675ce62f7f305b6edb9fd76084c6c Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Thu, 31 Jul 2025 16:51:42 -0400 Subject: [PATCH 078/160] Skip bounds checking on the inner accumulate loop. This helps performance with SSE (somewhat) and AVX-512 (quite a bit), but not AVX2 for some reason. --- core/hash/xxhash/xxhash_3.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/hash/xxhash/xxhash_3.odin b/core/hash/xxhash/xxhash_3.odin index bd5534f23..fe92f16d9 100644 --- a/core/hash/xxhash/xxhash_3.odin +++ b/core/hash/xxhash/xxhash_3.odin @@ -64,7 +64,7 @@ XXH3_INTERNAL_BUFFER_SIZE :: 256 IMPORTANT: This structure has a strict alignment requirement of 64 bytes!! ** Default allocators will align it correctly if created via `new`, as will - placing this struct on the cache, but if using a custom allocator make sure + placing this struct on the stack, but if using a custom allocator make sure that it handles the alignment correctly! */ XXH3_state :: struct #align(64) { @@ -870,7 +870,7 @@ XXH_PREFETCH_DIST :: 320 XXH3_accumulate :: #force_inline proc( acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) { - for n := uint(0); n < nbStripes; n += 1 { + #no_bounds_check for n := uint(0); n < nbStripes; n += 1 { when !XXH_DISABLE_PREFETCH { in_ptr := &input[n * XXH_STRIPE_LEN] prefetch(in_ptr, XXH_PREFETCH_DIST) From d0890c2135f236395cf4a412c6e36f48db77cce9 Mon Sep 17 00:00:00 2001 From: Laytan Date: Fri, 1 Aug 2025 19:18:43 +0200 Subject: [PATCH 079/160] fix dropping part of particular vecs --- src/llvm_abi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index b6ed395d9..8dbb8fa76 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1028,7 +1028,7 @@ namespace lbAbiAmd64SysV { unify(cls, ix + off/8, RegClass_Int); break; case LLVMHalfTypeKind: - unify(cls, ix + off/8, (off%8 == 6) ? RegClass_SSEHv : RegClass_SSEHs); + unify(cls, ix + off/8, (off%8 != 0) ? RegClass_SSEHv : RegClass_SSEHs); break; case LLVMFloatTypeKind: unify(cls, ix + off/8, (off%8 == 4) ? RegClass_SSEFv : RegClass_SSEFs); From 20318e7a3b3ce4e836f70be8305048e0482ab94a Mon Sep 17 00:00:00 2001 From: Laytan Date: Fri, 1 Aug 2025 19:19:23 +0200 Subject: [PATCH 080/160] concrete types to make llvm 14 happy Fixes #5463 Fixes #5244 Fixes #5435 --- src/llvm_backend_general.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 64ea58578..3ce0c725f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2584,16 +2584,19 @@ general_end:; if (src_size > dst_size) { GB_ASSERT(p->decl_block != p->curr_block); // NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want. + + LLVMTypeRef llvm_src_type = LLVMPointerType(src_type, 0); + LLVMTypeRef llvm_dst_type = LLVMPointerType(dst_type, 0); LLVMValueRef val_ptr; if (LLVMIsALoadInst(val)) { val_ptr = LLVMGetOperand(val, 0); } else if (LLVMIsAAllocaInst(val)) { - val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), ""); + val_ptr = LLVMBuildPointerCast(p->builder, val, llvm_src_type, ""); } else { // NOTE(laytan): we need a pointer to memcpy from. LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align); - val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), ""); + val_ptr = LLVMBuildPointerCast(p->builder, val_copy, llvm_src_type, ""); LLVMBuildStore(p->builder, val, val_ptr); } @@ -2601,11 +2604,11 @@ general_end:; max_align = gb_max(max_align, 16); LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align); - LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), ""); + LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, llvm_dst_type, ""); LLVMTypeRef types[3] = { - lb_type(p->module, t_rawptr), - lb_type(p->module, t_rawptr), + llvm_dst_type, + llvm_src_type, lb_type(p->module, t_int) }; From d343b93ef2ea3bd5b874a88c72902afa0b3ee45f Mon Sep 17 00:00:00 2001 From: Laytan Date: Fri, 1 Aug 2025 20:31:53 +0200 Subject: [PATCH 081/160] fix check --- src/checker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index 1821cbd4d..a1d8f98d7 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6952,7 +6952,7 @@ gb_internal void check_parsed_files(Checker *c) { if (entry.key != tt.hash) { continue; } - auto const &other = type_info_types[entry.value]; + auto const &other = c->info.type_info_types_hash_map[entry.value]; if (are_types_identical_unique_tuples(tt.type, other.type)) { continue; } From 9dfce3da86419ae477a97113bea381c7f4be104d Mon Sep 17 00:00:00 2001 From: Steve Waddicor Date: Fri, 1 Aug 2025 23:06:04 +0100 Subject: [PATCH 082/160] Convert leading spaces to tabs --- .../darwin/Foundation/NSBitmapImageRep.odin | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/core/sys/darwin/Foundation/NSBitmapImageRep.odin b/core/sys/darwin/Foundation/NSBitmapImageRep.odin index 2fcdf6530..059a75e43 100644 --- a/core/sys/darwin/Foundation/NSBitmapImageRep.odin +++ b/core/sys/darwin/Foundation/NSBitmapImageRep.odin @@ -7,44 +7,44 @@ BitmapImageRep :: struct { using _: Object } @(objc_type=BitmapImageRep, objc_name="alloc", objc_is_class_method=true) BitmapImageRep_alloc :: proc "c" () -> ^BitmapImageRep { - return msgSend(^BitmapImageRep, BitmapImageRep, "alloc") + return msgSend(^BitmapImageRep, BitmapImageRep, "alloc") } @(objc_type=BitmapImageRep, objc_name="initWithBitmapDataPlanes") BitmapImageRep_initWithBitmapDataPlanes :: proc "c" ( - self: ^BitmapImageRep, - bitmapDataPlanes: ^^u8, - pixelsWide: Integer, - pixelsHigh: Integer, - bitsPerSample: Integer, - samplesPerPixel: Integer, - hasAlpha: bool, - isPlanar: bool, - colorSpaceName: ^String, - bytesPerRow: Integer, - bitsPerPixel: Integer) -> ^BitmapImageRep { + self: ^BitmapImageRep, + bitmapDataPlanes: ^^u8, + pixelsWide: Integer, + pixelsHigh: Integer, + bitsPerSample: Integer, + samplesPerPixel: Integer, + hasAlpha: bool, + isPlanar: bool, + colorSpaceName: ^String, + bytesPerRow: Integer, + bitsPerPixel: Integer) -> ^BitmapImageRep { return msgSend(^BitmapImageRep, - self, - "initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:", - bitmapDataPlanes, - pixelsWide, - pixelsHigh, - bitsPerSample, - samplesPerPixel, - hasAlpha, - isPlanar, - colorSpaceName, - bytesPerRow, - bitsPerPixel) + self, + "initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:", + bitmapDataPlanes, + pixelsWide, + pixelsHigh, + bitsPerSample, + samplesPerPixel, + hasAlpha, + isPlanar, + colorSpaceName, + bytesPerRow, + bitsPerPixel) } @(objc_type=BitmapImageRep, objc_name="bitmapData") BitmapImageRep_bitmapData :: proc "c" (self: ^BitmapImageRep) -> rawptr { - return msgSend(rawptr, self, "bitmapData") + return msgSend(rawptr, self, "bitmapData") } @(objc_type=BitmapImageRep, objc_name="CGImage") BitmapImageRep_CGImage :: proc "c" (self: ^BitmapImageRep) -> rawptr { - return msgSend(rawptr, self, "CGImage") + return msgSend(rawptr, self, "CGImage") } From 2561427dd396a69cd49eb02c0814c4e8e8b3a08f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 11:00:15 +0100 Subject: [PATCH 083/160] Add `string16` and `cstring16` (UTF-16 based strings) --- base/runtime/core.odin | 13 ++- base/runtime/core_builtin.odin | 28 +++++- base/runtime/internal.odin | 87 +++++++++++++++++ core/fmt/fmt.odin | 76 +++++++++++++++ core/io/io.odin | 24 +++++ core/io/util.odin | 27 ++++++ core/unicode/utf16/utf16.odin | 20 ++++ src/build_settings.cpp | 8 +- src/cached.cpp | 2 +- src/check_builtin.cpp | 9 +- src/check_decl.cpp | 6 ++ src/check_expr.cpp | 88 +++++++++++++++++ src/checker.cpp | 16 +-- src/common.cpp | 2 +- src/exact_value.cpp | 52 +++++++++- src/llvm_backend_expr.cpp | 72 ++++++++++++++ src/llvm_backend_general.cpp | 31 ++++++ src/llvm_backend_proc.cpp | 9 ++ src/llvm_backend_type.cpp | 28 +++++- src/llvm_backend_utility.cpp | 12 +++ src/main.cpp | 8 +- src/microsoft_craziness.h | 4 +- src/path.cpp | 8 +- src/string.cpp | 172 +++++++++++++++++++++++++++++---- src/types.cpp | 133 +++++++++++++++++++++---- 25 files changed, 873 insertions(+), 62 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index baecb4146..fe40427ff 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -73,7 +73,7 @@ Type_Info_Rune :: struct {} Type_Info_Float :: struct {endianness: Platform_Endianness} Type_Info_Complex :: struct {} Type_Info_Quaternion :: struct {} -Type_Info_String :: struct {is_cstring: bool} +Type_Info_String :: struct {is_cstring: bool, is_utf16: bool} Type_Info_Boolean :: struct {} Type_Info_Any :: struct {} Type_Info_Type_Id :: struct {} @@ -397,6 +397,11 @@ Raw_String :: struct { len: int, } +Raw_String16 :: struct { + data: [^]u16, + len: int, +} + Raw_Slice :: struct { data: rawptr, len: int, @@ -450,6 +455,12 @@ Raw_Cstring :: struct { } #assert(size_of(Raw_Cstring) == size_of(cstring)) +Raw_Cstring16 :: struct { + data: [^]u16, +} +#assert(size_of(Raw_Cstring16) == size_of(cstring16)) + + Raw_Soa_Pointer :: struct { data: rawptr, index: int, diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index e2ba14f3a..09118998c 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -86,11 +86,26 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int } return n } + +// `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`. +// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum +// of len(src) and len(dst). +// +// Prefer the procedure group `copy`. +@builtin +copy_from_string16 :: proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int { + n := min(len(dst), len(src)) + if n > 0 { + intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(u16)) + } + return n +} + // `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`. // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum // of len(src) and len(dst). @builtin -copy :: proc{copy_slice, copy_from_string} +copy :: proc{copy_slice, copy_from_string, copy_from_string16} @@ -285,6 +300,15 @@ delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error } +@builtin +delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + return mem_free_with_size(raw_data(str), len(str)*size_of(u16), allocator, loc) +} +@builtin +delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + return mem_free((^u16)(str), allocator, loc) +} + // `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation. // // Note: Prefer `delete` over the specific `delete_*` procedures where possible. @@ -297,6 +321,8 @@ delete :: proc{ delete_map, delete_soa_slice, delete_soa_dynamic_array, + delete_string16, + delete_cstring16, } diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 907b187f1..660af58ab 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -493,12 +493,40 @@ string_cmp :: proc "contextless" (a, b: string) -> int { return ret } + +string16_eq :: proc "contextless" (lhs, rhs: string16) -> bool { + x := transmute(Raw_String16)lhs + y := transmute(Raw_String16)rhs + if x.len != y.len { + return false + } + return #force_inline memory_equal(x.data, y.data, x.len*size_of(u16)) +} + +string16_cmp :: proc "contextless" (a, b: string16) -> int { + x := transmute(Raw_String16)a + y := transmute(Raw_String16)b + + ret := memory_compare(x.data, y.data, min(x.len, y.len)*size_of(u16)) + if ret == 0 && x.len != y.len { + return -1 if x.len < y.len else +1 + } + return ret +} + string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) } string_lt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0 } string_gt :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0 } string_le :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0 } string_ge :: #force_inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0 } +string16_ne :: #force_inline proc "contextless" (a, b: string16) -> bool { return !string16_eq(a, b) } +string16_lt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) < 0 } +string16_gt :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) > 0 } +string16_le :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) <= 0 } +string16_ge :: #force_inline proc "contextless" (a, b: string16) -> bool { return string16_cmp(a, b) >= 0 } + + cstring_len :: proc "contextless" (s: cstring) -> int { p0 := uintptr((^byte)(s)) p := p0 @@ -508,6 +536,16 @@ cstring_len :: proc "contextless" (s: cstring) -> int { return int(p - p0) } +cstring16_len :: proc "contextless" (s: cstring16) -> int { + p := ([^]u16)(s) + n := 0 + for p != nil && p[0] != 0 { + p = p[1:] + n += 1 + } + return n +} + cstring_to_string :: proc "contextless" (s: cstring) -> string { if s == nil { return "" @@ -517,6 +555,15 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string { return transmute(string)Raw_String{ptr, n} } +cstring16_to_string16 :: proc "contextless" (s: cstring16) -> string16 { + if s == nil { + return "" + } + ptr := (^u16)(s) + n := cstring16_len(s) + return transmute(string16)Raw_String16{ptr, n} +} + cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool { x := ([^]byte)(lhs) @@ -559,6 +606,46 @@ cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 } cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 } +cstring16_eq :: proc "contextless" (lhs, rhs: cstring16) -> bool { + x := ([^]u16)(lhs) + y := ([^]u16)(rhs) + if x == y { + return true + } + if (x == nil) ~ (y == nil) { + return false + } + xn := cstring16_len(lhs) + yn := cstring16_len(rhs) + if xn != yn { + return false + } + return #force_inline memory_equal(x, y, xn*size_of(u16)) +} + +cstring16_cmp :: proc "contextless" (lhs, rhs: cstring16) -> int { + x := ([^]u16)(lhs) + y := ([^]u16)(rhs) + if x == y { + return 0 + } + if (x == nil) ~ (y == nil) { + return -1 if x == nil else +1 + } + xn := cstring16_len(lhs) + yn := cstring16_len(rhs) + ret := memory_compare(x, y, min(xn, yn)*size_of(u16)) + if ret == 0 && xn != yn { + return -1 if xn < yn else +1 + } + return ret +} + +cstring16_ne :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return !cstring16_eq(a, b) } +cstring16_lt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) < 0 } +cstring16_gt :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) > 0 } +cstring16_le :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) <= 0 } +cstring16_ge :: #force_inline proc "contextless" (a, b: cstring16) -> bool { return cstring16_cmp(a, b) >= 0 } complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b) } complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b) } diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 0f6470cca..7fe6287d4 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1551,6 +1551,79 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) { fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) { fmt_string(fi, string(s), verb) } + +// Formats a string UTF-16 with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The string to format. +// - verb: The format specifier character (e.g. 's', 'v', 'q', 'x', 'X'). +// +fmt_string16 :: proc(fi: ^Info, s: string16, verb: rune) { + s, verb := s, verb + if ol, ok := fi.optional_len.?; ok { + s = s[:clamp(ol, 0, len(s))] + } + if !fi.in_bad && fi.record_level > 0 && verb == 'v' { + verb = 'q' + } + + switch verb { + case 's', 'v': + if fi.width_set { + if fi.width > len(s) { + if fi.minus { + io.write_string16(fi.writer, s, &fi.n) + } + + for _ in 0.. 0 && space { + io.write_byte(fi.writer, ' ', &fi.n) + } + char_set := __DIGITS_UPPER + if verb == 'x' { + char_set = __DIGITS_LOWER + } + _fmt_int(fi, u64(s[i]), 16, false, bit_size=16, digits=char_set) + } + + case: + fmt_bad_verb(fi, verb) + } +} +// Formats a C-style UTF-16 string with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The C-style string to format. +// - verb: The format specifier character (Ref fmt_string). +// +fmt_cstring16 :: proc(fi: ^Info, s: cstring16, verb: rune) { + fmt_string16(fi, string16(s), verb) +} + // Formats a raw pointer with a specific format. // // Inputs: @@ -3210,6 +3283,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb) case cstring: fmt_cstring(fi, a, verb) + case string16: fmt_string16(fi, a, verb) + case cstring16: fmt_cstring16(fi, a, verb) + case typeid: reflect.write_typeid(fi.writer, a, &fi.n) case i16le: fmt_int(fi, u64(a), true, 16, verb) diff --git a/core/io/io.odin b/core/io/io.odin index c2b44cbdb..5431519bf 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -5,6 +5,7 @@ package io import "base:intrinsics" import "core:unicode/utf8" +import "core:unicode/utf16" // Seek whence values Seek_From :: enum { @@ -314,6 +315,29 @@ write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, return write(s, transmute([]byte)str, n_written) } +// write_string16 writes the contents of the string16 s to w reencoded as utf-8 +write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: int, err: Error) { + for i := 0; i < len(str); i += 1 { + r := rune(utf16.REPLACEMENT_CHAR) + + switch c := str[i]; { + case c < utf16._surr1, utf16._surr3 <= c: + r = rune(c) + case utf16._surr1 <= c && c < utf16._surr2 && i+1 < len(str) && + utf16._surr2 <= str[i+1] && str[i+1] < utf16._surr3: + r = utf16.decode_surrogate_pair(rune(c), rune(str[i+1])) + i += 1 + } + + w, err := write_rune(s, r, n_written) + n += w + if err != nil { + return + } + } + return +} + // write_rune writes a UTF-8 encoded rune to w. write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) { defer if err == nil && n_written != nil { diff --git a/core/io/util.odin b/core/io/util.odin index fa98e007b..72983523a 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -264,6 +264,33 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written return } +write_quoted_string16 :: proc(w: Writer, str: string16, quote: byte = '"', n_written: ^int = nil, for_json := false) -> (n: int, err: Error) { + defer if n_written != nil { + n_written^ += n + } + write_byte(w, quote, &n) or_return + for width, s := 0, str; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RUNE_SELF { + r, width = utf16.decode_rune_in_string(s) + } + if width == 1 && r == utf8.RUNE_ERROR { + write_byte(w, '\\', &n) or_return + write_byte(w, 'x', &n) or_return + write_byte(w, DIGITS_LOWER[s[0]>>4], &n) or_return + write_byte(w, DIGITS_LOWER[s[0]&0xf], &n) or_return + continue + } + + n_wrapper(write_escaped_rune(w, r, quote, false, nil, for_json), &n) or_return + + } + write_byte(w, quote, &n) or_return + return +} + + // writer append a quoted rune into the byte buffer, return the written size write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) { _write_byte :: #force_inline proc(w: Writer, c: byte) -> int { diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index e2bcf7f68..9a8cfe438 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -106,6 +106,26 @@ decode :: proc(d: []rune, s: []u16) -> (n: int) { return } +decode_rune_in_string :: proc(s: string16) -> (r: rune, width: int) { + r = rune(REPLACEMENT_CHAR) + n := len(s) + if n < 1 { + return + } + width = 1 + + + switch c := s[0]; { + case c < _surr1, _surr3 <= c: + r = rune(c) + case _surr1 <= c && c < _surr2 && 1 < len(s) && + _surr2 <= s[1] && s[1] < _surr3: + r = decode_surrogate_pair(rune(c), rune(s[1])) + width += 1 + } + return +} + rune_count :: proc(s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { c := s[i] diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 46a4f9ae5..40bbe41e5 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1089,7 +1089,7 @@ gb_internal String internal_odin_root_dir(void) { text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); GetModuleFileNameW(nullptr, text, cast(int)len); - path = string16_to_string(heap_allocator(), make_string16(text, len)); + path = string16_to_string(heap_allocator(), make_string16(cast(u16 *)text, len)); for (i = path.len-1; i >= 0; i--) { u8 c = path[i]; @@ -1387,14 +1387,14 @@ gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_) { mutex_lock(&fullpath_mutex); - len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr); + len = GetFullPathNameW(cast(wchar_t *)&string16[0], 0, nullptr, nullptr); if (len != 0) { wchar_t *text = gb_alloc_array(permanent_allocator(), wchar_t, len+1); - GetFullPathNameW(&string16[0], len, text, nullptr); + GetFullPathNameW(cast(wchar_t *)&string16[0], len, text, nullptr); mutex_unlock(&fullpath_mutex); text[len] = 0; - result = string16_to_string(a, make_string16(text, len)); + result = string16_to_string(a, make_string16(cast(u16 *)text, len)); result = string_trim_whitespace(result); // Replace Windows style separators diff --git a/src/cached.cpp b/src/cached.cpp index efdadce7b..61b5d01b4 100644 --- a/src/cached.cpp +++ b/src/cached.cpp @@ -231,7 +231,7 @@ Array cache_gather_envs() { wchar_t *curr_string = strings; while (curr_string && *curr_string) { - String16 wstr = make_string16_c(curr_string); + String16 wstr = make_string16_c(cast(u16 *)curr_string); curr_string += wstr.len+1; String str = string16_to_string(temporary_allocator(), wstr); if (string_starts_with(str, str_lit("CURR_DATE_TIME="))) { diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 974224ed2..d36cf4520 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2327,6 +2327,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (is_type_string(op_type) && id == BuiltinProc_len) { if (operand->mode == Addressing_Constant) { mode = Addressing_Constant; + + GB_ASSERT_MSG(!is_type_string16(op_type), "TODO(bill): constant utf-16 string len"); + String str = operand->value.value_string; value = exact_value_i64(str.len); type = t_untyped_integer; @@ -2334,6 +2337,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As mode = Addressing_Value; if (is_type_cstring(op_type)) { add_package_dependency(c, "runtime", "cstring_len"); + } else if (is_type_cstring16(op_type)) { + add_package_dependency(c, "runtime", "cstring16_len"); } } } else if (is_type_array(op_type)) { @@ -4683,7 +4688,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; case Type_Basic: if (t->Basic.kind == Basic_string) { - operand->type = alloc_type_multi_pointer(t_u8); + operand->type = t_u8_multi_ptr; + } else if (t->Basic.kind == Basic_string16) { + operand->type = t_u16_multi_ptr; } break; case Type_Pointer: diff --git a/src/check_decl.cpp b/src/check_decl.cpp index dd4c09e85..af46ee40e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -815,6 +815,12 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) { if (sig_compare(is_type_cstring, is_type_u8_multi_ptr, x, y)) { return true; } + if (sig_compare(is_type_cstring16, is_type_u16_ptr, x, y)) { + return true; + } + if (sig_compare(is_type_cstring16, is_type_u16_multi_ptr, x, y)) { + return true; + } if (sig_compare(is_type_uintptr, is_type_rawptr, x, y)) { return true; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6723a7580..57073e22f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2862,6 +2862,14 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t add_package_dependency(c, "runtime", "string_eq"); add_package_dependency(c, "runtime", "string_ne"); break; + case Basic_cstring16: + add_package_dependency(c, "runtime", "cstring16_eq"); + add_package_dependency(c, "runtime", "cstring16_ne"); + break; + case Basic_string16: + add_package_dependency(c, "runtime", "string16_eq"); + add_package_dependency(c, "runtime", "string16_ne"); + break; } break; case Type_Struct: @@ -3035,6 +3043,24 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break; case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break; } + } else if (is_type_cstring16(x->type) && is_type_cstring16(y->type)) { + switch (op) { + case Token_CmpEq: add_package_dependency(c, "runtime", "cstring16_eq"); break; + case Token_NotEq: add_package_dependency(c, "runtime", "cstring16_ne"); break; + case Token_Lt: add_package_dependency(c, "runtime", "cstring16_lt"); break; + case Token_Gt: add_package_dependency(c, "runtime", "cstring16_gt"); break; + case Token_LtEq: add_package_dependency(c, "runtime", "cstring16_le"); break; + case Token_GtEq: add_package_dependency(c, "runtime", "cstring16_gt"); break; + } + } else if (is_type_string16(x->type) || is_type_string16(y->type)) { + switch (op) { + case Token_CmpEq: add_package_dependency(c, "runtime", "string16_eq"); break; + case Token_NotEq: add_package_dependency(c, "runtime", "string16_ne"); break; + case Token_Lt: add_package_dependency(c, "runtime", "string16_lt"); break; + case Token_Gt: add_package_dependency(c, "runtime", "string16_gt"); break; + case Token_LtEq: add_package_dependency(c, "runtime", "string16_le"); break; + case Token_GtEq: add_package_dependency(c, "runtime", "string16_gt"); break; + } } else if (is_type_string(x->type) || is_type_string(y->type)) { switch (op) { case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break; @@ -3340,6 +3366,11 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type return true; } + // []u16 <-> string16 (not cstring16) + if (is_type_u16_slice(src) && (is_type_string16(dst) && !is_type_cstring16(dst))) { + return true; + } + // cstring -> string if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) { if (operand->mode != Addressing_Constant) { @@ -3347,6 +3378,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } return true; } + // cstring16 -> string16 + if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) { + if (operand->mode != Addressing_Constant) { + add_package_dependency(c, "runtime", "cstring16_to_string16"); + } + return true; + } + // cstring -> ^u8 if (are_types_identical(src, t_cstring) && is_type_u8_ptr(dst)) { return !is_constant; @@ -3372,6 +3411,34 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type if (is_type_rawptr(src) && are_types_identical(dst, t_cstring)) { return !is_constant; } + + // cstring -> ^u16 + if (are_types_identical(src, t_cstring16) && is_type_u16_ptr(dst)) { + return !is_constant; + } + // cstring -> [^]u16 + if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) { + return !is_constant; + } + // cstring -> rawptr + if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) { + return !is_constant; + } + + + // ^u16 -> cstring16 + if (is_type_u16_ptr(src) && are_types_identical(dst, t_cstring16)) { + return !is_constant; + } + // [^]u16 -> cstring + if (is_type_u16_multi_ptr(src) && are_types_identical(dst, t_cstring16)) { + return !is_constant; + } + // rawptr -> cstring16 + if (is_type_rawptr(src) && are_types_identical(dst, t_cstring16)) { + return !is_constant; + } + // proc <-> proc if (is_type_proc(src) && is_type_proc(dst)) { if (is_type_polymorphic(dst)) { @@ -4558,6 +4625,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar // target_type = t_untyped_nil; } else if (is_type_cstring(target_type)) { // target_type = t_untyped_nil; + } else if (is_type_cstring16(target_type)) { + // target_type = t_untyped_nil; } else if (!type_has_nil(target_type)) { operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -8226,6 +8295,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 case Type_Basic: if (t->Basic.kind == Basic_string) { if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String); *max_count = o->value.value_string.len; } if (o->mode != Addressing_Constant) { @@ -8233,6 +8303,16 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 } o->type = t_u8; return true; + } else if (t->Basic.kind == Basic_string16) { + if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String16); + *max_count = o->value.value_string16.len; + } + if (o->mode != Addressing_Constant) { + o->mode = Addressing_Value; + } + o->type = t_u16; + return true; } else if (t->Basic.kind == Basic_UntypedString) { if (o->mode == Addressing_Constant) { *max_count = o->value.value_string.len; @@ -10879,9 +10959,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { valid = true; if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String); max_count = o->value.value_string.len; } o->type = type_deref(o->type); + } else if (t->Basic.kind == Basic_string16) { + valid = true; + if (o->mode == Addressing_Constant) { + GB_ASSERT(o->value.kind == ExactValue_String16); + max_count = o->value.value_string16.len; + } + o->type = type_deref(o->type); } break; diff --git a/src/checker.cpp b/src/checker.cpp index a1d8f98d7..20da5b19b 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1363,13 +1363,15 @@ gb_internal void init_universal(void) { } - t_u8_ptr = alloc_type_pointer(t_u8); - t_u8_multi_ptr = alloc_type_multi_pointer(t_u8); - t_int_ptr = alloc_type_pointer(t_int); - t_i64_ptr = alloc_type_pointer(t_i64); - t_f64_ptr = alloc_type_pointer(t_f64); - t_u8_slice = alloc_type_slice(t_u8); - t_string_slice = alloc_type_slice(t_string); + t_u8_ptr = alloc_type_pointer(t_u8); + t_u8_multi_ptr = alloc_type_multi_pointer(t_u8); + t_u16_ptr = alloc_type_pointer(t_u16); + t_u16_multi_ptr = alloc_type_multi_pointer(t_u16); + t_int_ptr = alloc_type_pointer(t_int); + t_i64_ptr = alloc_type_pointer(t_i64); + t_f64_ptr = alloc_type_pointer(t_f64); + t_u8_slice = alloc_type_slice(t_u8); + t_string_slice = alloc_type_slice(t_string); // intrinsics types for objective-c stuff { diff --git a/src/common.cpp b/src/common.cpp index ad1e5a851..b3761fc36 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -669,7 +669,7 @@ gb_internal gb_inline f64 gb_sqrt(f64 x) { gb_internal wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { u32 i, j; - u32 len = cast(u32)string16_len(cmd_line); + u32 len = cast(u32)string16_len(cast(u16 *)cmd_line); i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *); wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t)); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 37751c8f1..f2aed84c2 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -29,6 +29,7 @@ enum ExactValueKind { ExactValue_Compound = 8, ExactValue_Procedure = 9, ExactValue_Typeid = 10, + ExactValue_String16 = 11, ExactValue_Count, }; @@ -46,6 +47,7 @@ struct ExactValue { Ast * value_compound; Ast * value_procedure; Type * value_typeid; + String16 value_string16; }; }; @@ -66,6 +68,9 @@ gb_internal uintptr hash_exact_value(ExactValue v) { case ExactValue_String: res = gb_fnv32a(v.value_string.text, v.value_string.len); break; + case ExactValue_String16: + res = gb_fnv32a(v.value_string.text, v.value_string.len*gb_size_of(u16)); + break; case ExactValue_Integer: { u32 key = gb_fnv32a(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used); @@ -118,6 +123,11 @@ gb_internal ExactValue exact_value_string(String string) { result.value_string = string; return result; } +gb_internal ExactValue exact_value_string16(String16 string) { + ExactValue result = {ExactValue_String16}; + result.value_string16 = string; + return result; +} gb_internal ExactValue exact_value_i64(i64 i) { ExactValue result = {ExactValue_Integer}; @@ -656,6 +666,7 @@ gb_internal i32 exact_value_order(ExactValue const &v) { return 0; case ExactValue_Bool: case ExactValue_String: + case ExactValue_String16: return 1; case ExactValue_Integer: return 2; @@ -689,6 +700,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: + case ExactValue_String16: case ExactValue_Quaternion: case ExactValue_Pointer: case ExactValue_Compound: @@ -891,7 +903,18 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E gb_memmove(data, sx.text, sx.len); gb_memmove(data+sx.len, sy.text, sy.len); return exact_value_string(make_string(data, len)); - break; + } + case ExactValue_String16: { + if (op != Token_Add) goto error; + + // NOTE(bill): How do you minimize this over allocation? + String sx = x.value_string; + String sy = y.value_string; + isize len = sx.len+sy.len; + u16 *data = gb_alloc_array(permanent_allocator(), u16, len); + gb_memmove(data, sx.text, sx.len*gb_size_of(u16)); + gb_memmove(data+sx.len, sy.text, sy.len*gb_size_of(u16)); + return exact_value_string16(make_string16(data, len)); } } @@ -994,6 +1017,19 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) } break; } + case ExactValue_String16: { + String16 a = x.value_string16; + String16 b = y.value_string16; + switch (op) { + case Token_CmpEq: return a == b; + case Token_NotEq: return a != b; + case Token_Lt: return a < b; + case Token_LtEq: return a <= b; + case Token_Gt: return a > b; + case Token_GtEq: return a >= b; + } + break; + } case ExactValue_Pointer: { switch (op) { @@ -1050,6 +1086,20 @@ gb_internal gbString write_exact_value_to_string(gbString str, ExactValue const gb_free(heap_allocator(), s.text); return str; } + case ExactValue_String16: { + String s = quote_to_ascii(heap_allocator(), v.value_string16); + string_limit = gb_max(string_limit, 36); + if (s.len <= string_limit) { + str = gb_string_append_length(str, s.text, s.len); + } else { + isize n = string_limit/5; + str = gb_string_append_length(str, s.text, n); + str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n)); + str = gb_string_append_length(str, s.text+s.len-n, n); + } + gb_free(heap_allocator(), s.text); + return str; + } case ExactValue_Integer: { String s = big_int_to_string(heap_allocator(), &v.value_integer); str = gb_string_append_length(str, s.text, s.len); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 74aea82f1..fbf0dea11 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1656,6 +1656,8 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { res.type = t; res.value = llvm_cstring(m, str); return res; + } else if (src->kind == Type_Basic && src->Basic.kind == Basic_string16 && dst->Basic.kind == Basic_cstring16) { + GB_PANIC("TODO(bill): UTF-16 string"); } // if (is_type_float(dst)) { // return value; @@ -1795,6 +1797,38 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } + + if (is_type_cstring16(src) && is_type_u16_ptr(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_u16_ptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_u8_multi_ptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_cstring16(src) && is_type_rawptr(dst)) { + return lb_emit_transmute(p, value, dst); + } + if (is_type_rawptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, dst); + } + + if (are_types_identical(src, t_cstring16) && are_types_identical(dst, t_string16)) { + TEMPORARY_ALLOCATOR_GUARD(); + + lbValue c = lb_emit_conv(p, value, t_cstring16); + auto args = array_make(temporary_allocator(), 1); + args[0] = c; + lbValue s = lb_emit_runtime_call(p, "cstring16_to_string16", args); + return lb_emit_conv(p, s, dst); + } + + + // integer -> boolean if (is_type_integer(src) && is_type_boolean(dst)) { lbValue res = {}; @@ -2296,6 +2330,14 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } + // []u16 <-> string16 + if (is_type_u16_slice(src) && is_type_string16(dst)) { + return lb_emit_transmute(p, value, t); + } + if (is_type_string16(src) && is_type_u16_slice(dst)) { + return lb_emit_transmute(p, value, t); + } + // []byte/[]u8 <-> string if (is_type_u8_slice(src) && is_type_string(dst)) { return lb_emit_transmute(p, value, t); @@ -2304,6 +2346,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return lb_emit_transmute(p, value, t); } + if (is_type_array_like(dst)) { Type *elem = base_array_type(dst); isize index_count = cast(isize)get_array_type_count(dst); @@ -2483,6 +2526,12 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (is_type_untyped(src)) { + if (is_type_string(src) && is_type_string16(dst)) { + GB_PANIC("TODO(bill): UTF-16 string"); + lbAddr result = lb_add_local_generated(p, t, false); + lb_addr_store(p, result, value); + return lb_addr_load(p, result); + } if (is_type_string(src) && is_type_string(dst)) { lbAddr result = lb_add_local_generated(p, t, false); lb_addr_store(p, result, value); @@ -3056,6 +3105,13 @@ gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); } return res; + case Basic_cstring16: + if (op_kind == Token_CmpEq) { + res.value = LLVMBuildIsNull(p->builder, x.value, ""); + } else if (op_kind == Token_NotEq) { + res.value = LLVMBuildIsNotNull(p->builder, x.value, ""); + } + return res; case Basic_any: { // TODO(bill): is this correct behaviour for nil comparison for any? @@ -4432,6 +4488,22 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { } case Type_Basic: { + if (is_type_string16(type)) { + GB_ASSERT_MSG(are_types_identical(type, t_string16), "got %s", type_to_string(type)); + lbValue len = lb_string_len(p, base); + if (high.value == nullptr) high = len; + + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } + + lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + + lbAddr str = lb_add_local_generated(p, t_string16, false); + lb_fill_string(p, str, elem, new_len); + return str; + } GB_ASSERT_MSG(are_types_identical(type, t_string), "got %s", type_to_string(type)); lbValue len = lb_string_len(p, base); if (high.value == nullptr) high = len; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 3ce0c725f..d9771a75b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1812,6 +1812,37 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return type; } case Basic_cstring: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0); + + + case Basic_string16: + { + char const *name = "..string16"; + LLVMTypeRef type = LLVMGetTypeByName(m->mod, name); + if (type != nullptr) { + return type; + } + type = LLVMStructCreateNamed(ctx, name); + + if (build_context.metrics.ptr_size < build_context.metrics.int_size) { + GB_ASSERT(build_context.metrics.ptr_size == 4); + GB_ASSERT(build_context.metrics.int_size == 8); + LLVMTypeRef fields[3] = { + LLVMPointerType(lb_type(m, t_u16), 0), + lb_type(m, t_i32), + lb_type(m, t_int), + }; + LLVMStructSetBody(type, fields, 3, false); + } else { + LLVMTypeRef fields[2] = { + LLVMPointerType(lb_type(m, t_u16), 0), + lb_type(m, t_int), + }; + LLVMStructSetBody(type, fields, 2, false); + } + return type; + } + case Basic_cstring16: return LLVMPointerType(LLVMInt16TypeInContext(ctx), 0); + case Basic_any: { char const *name = "..any"; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index e63c92f6f..8f306b771 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2289,6 +2289,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } if (is_type_cstring(t)) { return lb_cstring_len(p, v); + } else if (is_type_cstring16(t)) { + return lb_cstring16_len(p, v); + } else if (is_type_string16(t)) { + return lb_string_len(p, v); } else if (is_type_string(t)) { return lb_string_len(p, v); } else if (is_type_array(t)) { @@ -2728,6 +2732,11 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu res = lb_emit_conv(p, res, tv.type); } else if (t->Basic.kind == Basic_cstring) { res = lb_emit_conv(p, x, tv.type); + } else if (t->Basic.kind == Basic_string16) { + res = lb_string_elem(p, x); + res = lb_emit_conv(p, res, tv.type); + } else if (t->Basic.kind == Basic_cstring16) { + res = lb_emit_conv(p, x, tv.type); } break; case Type_Pointer: diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 43c5f0b40..a91d77fe5 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -531,7 +531,33 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ case Basic_cstring: { tag_type = t_type_info_string; - LLVMValueRef vals[1] = { + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, true).value, + lb_const_bool(m, t_bool, false).value, + }; + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + } + break; + + case Basic_string16: + { + tag_type = t_type_info_string; + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, false).value, + lb_const_bool(m, t_bool, true).value, + }; + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + } + break; + + + case Basic_cstring16: + { + tag_type = t_type_info_string; + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, true).value, lb_const_bool(m, t_bool, true).value, }; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 521553147..d4117b7ff 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1626,11 +1626,17 @@ gb_internal void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue ba gb_internal lbValue lb_string_elem(lbProcedure *p, lbValue string) { Type *t = base_type(string.type); + if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) { + return lb_emit_struct_ev(p, string, 0); + } GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string); return lb_emit_struct_ev(p, string, 0); } gb_internal lbValue lb_string_len(lbProcedure *p, lbValue string) { Type *t = base_type(string.type); + if (t->kind == Type_Basic && t->Basic.kind == Basic_string16) { + return lb_emit_struct_ev(p, string, 1); + } GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t)); return lb_emit_struct_ev(p, string, 1); } @@ -1641,6 +1647,12 @@ gb_internal lbValue lb_cstring_len(lbProcedure *p, lbValue value) { args[0] = lb_emit_conv(p, value, t_cstring); return lb_emit_runtime_call(p, "cstring_len", args); } +gb_internal lbValue lb_cstring16_len(lbProcedure *p, lbValue value) { + GB_ASSERT(is_type_cstring16(value.type)); + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_emit_conv(p, value, t_cstring16); + return lb_emit_runtime_call(p, "cstring16_len", args); +} gb_internal lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) { diff --git a/src/main.cpp b/src/main.cpp index 112d1208a..5a43e3c02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -142,9 +142,9 @@ gb_internal i32 system_exec_command_line_app_internal(bool exit_on_err, char con } wcmd = string_to_string16(permanent_allocator(), make_string(cast(u8 *)cmd_line, cmd_len-1)); - if (CreateProcessW(nullptr, wcmd.text, - nullptr, nullptr, true, 0, nullptr, nullptr, - &start_info, &pi)) { + if (CreateProcessW(nullptr, cast(wchar_t *)wcmd.text, + nullptr, nullptr, true, 0, nullptr, nullptr, + &start_info, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code); @@ -232,7 +232,7 @@ gb_internal Array setup_args(int argc, char const **argv) { wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc); auto args = array_make(a, 0, wargc); for (isize i = 0; i < wargc; i++) { - wchar_t *warg = wargv[i]; + u16 *warg = cast(u16 *)wargv[i]; isize wlen = string16_len(warg); String16 wstr = make_string16(warg, wlen); String arg = string16_to_string(a, wstr); diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h index b0fd22a23..933607a2a 100644 --- a/src/microsoft_craziness.h +++ b/src/microsoft_craziness.h @@ -59,7 +59,7 @@ struct Find_Result { }; gb_internal String mc_wstring_to_string(wchar_t const *str) { - return string16_to_string(mc_allocator, make_string16_c(str)); + return string16_to_string(mc_allocator, make_string16_c(cast(u16 *)str)); } gb_internal String16 mc_string_to_wstring(String str) { @@ -103,7 +103,7 @@ gb_internal HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) { String16 wildcard_wide = mc_string_to_wstring(wildcard); defer (mc_free(wildcard_wide)); - HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data); + HANDLE handle = FindFirstFileW(cast(wchar_t *)wildcard_wide.text, &_find_data); if (handle == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; find_data->file_attributes = _find_data.dwFileAttributes; diff --git a/src/path.cpp b/src/path.cpp index d5e982088..2b97a04df 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -130,7 +130,7 @@ gb_internal String directory_from_path(String const &s) { String16 wstr = string_to_string16(a, path); defer (gb_free(a, wstr.text)); - i32 attribs = GetFileAttributesW(wstr.text); + i32 attribs = GetFileAttributesW(cast(wchar_t *)wstr.text); if (attribs < 0) return false; return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -360,7 +360,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) defer (gb_free(a, wstr.text)); WIN32_FIND_DATAW file_data = {}; - HANDLE find_file = FindFirstFileW(wstr.text, &file_data); + HANDLE find_file = FindFirstFileW(cast(wchar_t *)wstr.text, &file_data); if (find_file == INVALID_HANDLE_VALUE) { return ReadDirectory_Unknown; } @@ -372,7 +372,7 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) wchar_t *filename_w = file_data.cFileName; u64 size = cast(u64)file_data.nFileSizeLow; size |= (cast(u64)file_data.nFileSizeHigh) << 32; - String name = string16_to_string(a, make_string16_c(filename_w)); + String name = string16_to_string(a, make_string16_c(cast(u16 *)filename_w)); if (name == "." || name == "..") { gb_free(a, name.text); continue; @@ -494,7 +494,7 @@ gb_internal bool write_directory(String path) { #else gb_internal bool write_directory(String path) { String16 wstr = string_to_string16(heap_allocator(), path); - LPCWSTR wdirectory_name = wstr.text; + LPCWSTR wdirectory_name = cast(wchar_t *)wstr.text; HANDLE directory = CreateFileW(wdirectory_name, GENERIC_WRITE, diff --git a/src/string.cpp b/src/string.cpp index ae8d066b1..8405938f4 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -26,15 +26,14 @@ struct String_Iterator { // NOTE(bill): String16 is only used for Windows due to its file directories struct String16 { - wchar_t *text; - isize len; - wchar_t const &operator[](isize i) const { + u16 * text; + isize len; + u16 const &operator[](isize i) const { GB_ASSERT_MSG(0 <= i && i < len, "[%td]", i); return text[i]; } }; - gb_internal gb_inline String make_string(u8 const *text, isize len) { String s; s.text = cast(u8 *)text; @@ -45,19 +44,19 @@ gb_internal gb_inline String make_string(u8 const *text, isize len) { return s; } - -gb_internal gb_inline String16 make_string16(wchar_t const *text, isize len) { +gb_internal gb_inline String16 make_string16(u16 const *text, isize len) { String16 s; - s.text = cast(wchar_t *)text; + s.text = cast(u16 *)text; s.len = len; return s; } -gb_internal isize string16_len(wchar_t const *s) { + +gb_internal isize string16_len(u16 const *s) { if (s == nullptr) { return 0; } - wchar_t const *p = s; + u16 const *p = s; while (*p) { p++; } @@ -69,7 +68,7 @@ gb_internal gb_inline String make_string_c(char const *text) { return make_string(cast(u8 *)cast(void *)text, gb_strlen(text)); } -gb_internal gb_inline String16 make_string16_c(wchar_t const *text) { +gb_internal gb_inline String16 make_string16_c(u16 const *text) { return make_string16(text, string16_len(text)); } @@ -145,6 +144,27 @@ gb_internal int string_compare(String const &a, String const &b) { return res; } + +gb_internal int string16_compare(String16 const &a, String16 const &b) { + if (a.text == b.text) { + return cast(int)(a.len - b.len); + } + if (a.text == nullptr) { + return -1; + } + if (b.text == nullptr) { + return +1; + } + + uintptr n = gb_min(a.len, b.len); + int res = memcmp(a.text, b.text, n*gb_size_of(u16)); + if (res == 0) { + res = cast(int)(a.len - b.len); + } + return res; +} + + gb_internal isize string_index_byte(String const &s, u8 x) { for (isize i = 0; i < s.len; i++) { if (s.text[i] == x) { @@ -182,6 +202,26 @@ template gb_internal bool operator >= (String const &a, char const (&b template <> bool operator == (String const &a, char const (&b)[1]) { return a.len == 0; } template <> bool operator != (String const &a, char const (&b)[1]) { return a.len != 0; } + +gb_internal gb_inline bool str_eq(String16 const &a, String16 const &b) { + if (a.len != b.len) return false; + if (a.len == 0) return true; + return memcmp(a.text, b.text, a.len) == 0; +} +gb_internal gb_inline bool str_ne(String16 const &a, String16 const &b) { return !str_eq(a, b); } +gb_internal gb_inline bool str_lt(String16 const &a, String16 const &b) { return string16_compare(a, b) < 0; } +gb_internal gb_inline bool str_gt(String16 const &a, String16 const &b) { return string16_compare(a, b) > 0; } +gb_internal gb_inline bool str_le(String16 const &a, String16 const &b) { return string16_compare(a, b) <= 0; } +gb_internal gb_inline bool str_ge(String16 const &a, String16 const &b) { return string16_compare(a, b) >= 0; } + +gb_internal gb_inline bool operator == (String16 const &a, String16 const &b) { return str_eq(a, b); } +gb_internal gb_inline bool operator != (String16 const &a, String16 const &b) { return str_ne(a, b); } +gb_internal gb_inline bool operator < (String16 const &a, String16 const &b) { return str_lt(a, b); } +gb_internal gb_inline bool operator > (String16 const &a, String16 const &b) { return str_gt(a, b); } +gb_internal gb_inline bool operator <= (String16 const &a, String16 const &b) { return str_le(a, b); } +gb_internal gb_inline bool operator >= (String16 const &a, String16 const &b) { return str_ge(a, b); } + + gb_internal gb_inline bool string_starts_with(String const &s, String const &prefix) { if (prefix.len > s.len) { return false; @@ -614,7 +654,7 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons // TODO(bill): Make this non-windows specific gb_internal String16 string_to_string16(gbAllocator a, String s) { int len, len1; - wchar_t *text; + u16 *text; if (s.len < 1) { return make_string16(nullptr, 0); @@ -625,9 +665,9 @@ gb_internal String16 string_to_string16(gbAllocator a, String s) { return make_string16(nullptr, 0); } - text = gb_alloc_array(a, wchar_t, len+1); + text = gb_alloc_array(a, u16, len+1); - len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len); + len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, cast(wchar_t *)text, cast(int)len); if (len1 == 0) { gb_free(a, text); return make_string16(nullptr, 0); @@ -646,7 +686,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) { return make_string(nullptr, 0); } - len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0); + len = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, nullptr, 0); if (len == 0) { return make_string(nullptr, 0); } @@ -654,7 +694,7 @@ gb_internal String string16_to_string(gbAllocator a, String16 s) { text = gb_alloc_array(a, u8, len+1); - len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len); + len1 = convert_widechar_to_multibyte(cast(wchar_t *)s.text, cast(int)s.len, cast(char *)text, cast(int)len); if (len1 == 0) { gb_free(a, text); return make_string(nullptr, 0); @@ -674,9 +714,9 @@ gb_internal String temporary_directory(gbAllocator allocator) { return String{0}; } DWORD len = gb_max(MAX_PATH, n); - wchar_t *b = gb_alloc_array(heap_allocator(), wchar_t, len+1); + u16 *b = gb_alloc_array(heap_allocator(), u16, len+1); defer (gb_free(heap_allocator(), b)); - n = GetTempPathW(len, b); + n = GetTempPathW(len, cast(wchar_t *)b); if (n == 3 && b[1] == ':' && b[2] == '\\') { } else if (n > 0 && b[n-1] == '\\') { @@ -791,6 +831,104 @@ gb_internal String quote_to_ascii(gbAllocator a, String str, u8 quote='"') { return res; } +gb_internal Rune decode_surrogate_pair(u16 r1, u16 r2) { + static Rune const _surr1 = 0xd800; + static Rune const _surr2 = 0xdc00; + static Rune const _surr3 = 0xe000; + static Rune const _surr_self = 0x10000; + + if (_surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3) { + return (((r1-_surr1)<<10) | (r2 - _surr2)) + _surr_self; + } + return GB_RUNE_INVALID; +} + +gb_internal String quote_to_ascii(gbAllocator a, String16 str, u8 quote='"') { + static Rune const _surr1 = 0xd800; + static Rune const _surr2 = 0xdc00; + static Rune const _surr3 = 0xe000; + static Rune const _surr_self = 0x10000; + + u16 *s = cast(u16 *)str.text; + isize n = str.len; + auto buf = array_make(a, 0, n*2); + array_add(&buf, quote); + for (isize width = 0; n > 0; s += width, n -= width) { + Rune r = cast(Rune)s[0]; + width = 1; + if (r < _surr1 || _surr3 <= r) { + r = cast(Rune)r; + } else if (_surr1 <= r && r < _surr2) { + if (n>1) { + r = decode_surrogate_pair(s[0], s[1]); + if (r != GB_RUNE_INVALID) { + width = 2; + } + } else { + r = GB_RUNE_INVALID; + } + } + if (width == 1 && r == GB_RUNE_INVALID) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[s[0]>>4]); + array_add(&buf, cast(u8)lower_hex[s[0]&0xf]); + continue; + } + + if (r == quote || r == '\\') { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, u8(r)); + continue; + } + if (r < 0x80 && is_printable(r)) { + array_add(&buf, u8(r)); + continue; + } + switch (r) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + default: + if (r < ' ') { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[b>>4]); + array_add(&buf, cast(u8)lower_hex[b&0xf]); + } + if (r > GB_RUNE_MAX) { + r = 0XFFFD; + } + if (r < 0x10000) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'u'); + for (isize i = 12; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } else { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'U'); + for (isize i = 28; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } + } + } + + + + array_add(&buf, quote); + String res = {}; + res.text = buf.data; + res.len = buf.count; + return res; +} + diff --git a/src/types.cpp b/src/types.cpp index 2e696810d..ad576c8af 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -41,8 +41,13 @@ enum BasicKind { Basic_uint, Basic_uintptr, Basic_rawptr, - Basic_string, // ^u8 + int - Basic_cstring, // ^u8 + + Basic_string, // [^]u8 + int + Basic_cstring, // [^]u8 + + Basic_string16, // [^]u16 + int + Basic_cstring16, // [^]u16 + int + Basic_any, // rawptr + ^Type_Info Basic_typeid, @@ -500,8 +505,14 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}}, {Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}}, + {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, {Type_Basic, {Basic_cstring, BasicFlag_String, -1, STR_LIT("cstring")}}, + + {Type_Basic, {Basic_string16, BasicFlag_String, -1, STR_LIT("string16")}}, + {Type_Basic, {Basic_cstring16, BasicFlag_String, -1, STR_LIT("cstring16")}}, + + {Type_Basic, {Basic_any, 0, 16, STR_LIT("any")}}, {Type_Basic, {Basic_typeid, 0, 8, STR_LIT("typeid")}}, @@ -591,8 +602,12 @@ gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uintptr = &basic_types[Basic_uintptr]; gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; + gb_global Type *t_string = &basic_types[Basic_string]; gb_global Type *t_cstring = &basic_types[Basic_cstring]; +gb_global Type *t_string16 = &basic_types[Basic_string16]; +gb_global Type *t_cstring16 = &basic_types[Basic_cstring16]; + gb_global Type *t_any = &basic_types[Basic_any]; gb_global Type *t_typeid = &basic_types[Basic_typeid]; @@ -630,6 +645,8 @@ gb_global Type *t_untyped_uninit = &basic_types[Basic_UntypedUninit]; gb_global Type *t_u8_ptr = nullptr; gb_global Type *t_u8_multi_ptr = nullptr; +gb_global Type *t_u16_ptr = nullptr; +gb_global Type *t_u16_multi_ptr = nullptr; gb_global Type *t_int_ptr = nullptr; gb_global Type *t_i64_ptr = nullptr; gb_global Type *t_f64_ptr = nullptr; @@ -1292,6 +1309,14 @@ gb_internal bool is_type_string(Type *t) { } return false; } +gb_internal bool is_type_string16(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_string16; + } + return false; +} gb_internal bool is_type_cstring(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1300,6 +1325,14 @@ gb_internal bool is_type_cstring(Type *t) { } return false; } +gb_internal bool is_type_cstring16(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_cstring16; + } + return false; +} gb_internal bool is_type_typed(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1429,6 +1462,12 @@ gb_internal bool is_type_u8(Type *t) { } return false; } +gb_internal bool is_type_u16(Type *t) { + if (t->kind == Type_Basic) { + return t->Basic.kind == Basic_u16; + } + return false; +} gb_internal bool is_type_array(Type *t) { t = base_type(t); if (t == nullptr) { return false; } @@ -1690,6 +1729,39 @@ gb_internal bool is_type_rune_array(Type *t) { return false; } +gb_internal bool is_type_u16_slice(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Slice) { + return is_type_u16(t->Slice.elem); + } + return false; +} +gb_internal bool is_type_u16_array(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Array) { + return is_type_u16(t->Array.elem); + } + return false; +} +gb_internal bool is_type_u16_ptr(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Pointer) { + return is_type_u16(t->Slice.elem); + } + return false; +} +gb_internal bool is_type_u16_multi_ptr(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_MultiPointer) { + return is_type_u16(t->Slice.elem); + } + return false; +} + gb_internal bool is_type_array_like(Type *t) { return is_type_array(t) || is_type_enumerated_array(t); @@ -2109,7 +2181,7 @@ gb_internal bool is_type_indexable(Type *t) { Type *bt = base_type(t); switch (bt->kind) { case Type_Basic: - return bt->Basic.kind == Basic_string; + return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16; case Type_Array: case Type_Slice: case Type_DynamicArray: @@ -2129,7 +2201,7 @@ gb_internal bool is_type_sliceable(Type *t) { Type *bt = base_type(t); switch (bt->kind) { case Type_Basic: - return bt->Basic.kind == Basic_string; + return bt->Basic.kind == Basic_string || bt->Basic.kind == Basic_string16; case Type_Array: case Type_Slice: case Type_DynamicArray: @@ -2376,6 +2448,7 @@ gb_internal bool type_has_nil(Type *t) { case Basic_any: return true; case Basic_cstring: + case Basic_cstring16: return true; case Basic_typeid: return true; @@ -2443,8 +2516,9 @@ gb_internal bool is_type_comparable(Type *t) { case Basic_rune: return true; case Basic_string: - return true; case Basic_cstring: + case Basic_string16: + case Basic_cstring16: return true; case Basic_typeid: return true; @@ -3774,10 +3848,12 @@ gb_internal i64 type_size_of(Type *t) { if (t->kind == Type_Basic) { GB_ASSERT_MSG(is_type_typed(t), "%s", type_to_string(t)); switch (t->Basic.kind) { - case Basic_string: size = 2*build_context.int_size; break; - case Basic_cstring: size = build_context.ptr_size; break; - case Basic_any: size = 16; break; - case Basic_typeid: size = 8; break; + case Basic_string: size = 2*build_context.int_size; break; + case Basic_cstring: size = build_context.ptr_size; break; + case Basic_string16: size = 2*build_context.int_size; break; + case Basic_cstring16: size = build_context.ptr_size; break; + case Basic_any: size = 16; break; + case Basic_typeid: size = 8; break; case Basic_int: case Basic_uint: size = build_context.int_size; @@ -3837,10 +3913,12 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) { case Type_Basic: { GB_ASSERT(is_type_typed(t)); switch (t->Basic.kind) { - case Basic_string: return build_context.int_size; - case Basic_cstring: return build_context.ptr_size; - case Basic_any: return 8; - case Basic_typeid: return 8; + case Basic_string: return build_context.int_size; + case Basic_cstring: return build_context.ptr_size; + case Basic_string16: return build_context.int_size; + case Basic_cstring16: return build_context.ptr_size; + case Basic_any: return 8; + case Basic_typeid: return 8; case Basic_int: case Basic_uint: return build_context.int_size; @@ -4088,10 +4166,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { return size; } switch (kind) { - case Basic_string: return 2*build_context.int_size; - case Basic_cstring: return build_context.ptr_size; - case Basic_any: return 16; - case Basic_typeid: return 8; + case Basic_string: return 2*build_context.int_size; + case Basic_cstring: return build_context.ptr_size; + case Basic_string16: return 2*build_context.int_size; + case Basic_cstring16: return build_context.ptr_size; + case Basic_any: return 16; + case Basic_typeid: return 8; case Basic_int: case Basic_uint: return build_context.int_size; @@ -4320,6 +4400,15 @@ gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) { if (field_type_) *field_type_ = t_int; return build_context.int_size; // len } + } else if (t->Basic.kind == Basic_string16) { + switch (index) { + case 0: + if (field_type_) *field_type_ = t_u16_ptr; + return 0; // data + case 1: + if (field_type_) *field_type_ = t_int; + return build_context.int_size; // len + } } else if (t->Basic.kind == Basic_any) { switch (index) { case 0: @@ -4396,6 +4485,11 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) { case 0: t = t_rawptr; break; case 1: t = t_int; break; } + } else if (t->Basic.kind == Basic_string16) { + switch (index) { + case 0: t = t_rawptr; break; + case 1: t = t_int; break; + } } else if (t->Basic.kind == Basic_any) { switch (index) { case 0: t = t_rawptr; break; @@ -4637,6 +4731,11 @@ gb_internal Type *type_internal_index(Type *t, isize index) { GB_ASSERT(index == 0 || index == 1); return index == 0 ? t_u8_ptr : t_int; } + case Basic_string16: + { + GB_ASSERT(index == 0 || index == 1); + return index == 0 ? t_u16_ptr : t_int; + } case Basic_any: { GB_ASSERT(index == 0 || index == 1); From ae02d3d02d2eb5132fa7c6573ed7db20d7e18f3e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 11:55:16 +0100 Subject: [PATCH 084/160] Begin supporting `string16` across the core library --- base/intrinsics/intrinsics.odin | 1 + base/runtime/print.odin | 6 +++ core/encoding/cbor/tags.odin | 2 +- core/encoding/cbor/unmarshal.odin | 2 + core/encoding/json/marshal.odin | 8 ++-- core/encoding/json/unmarshal.odin | 4 +- core/flags/internal_rtti.odin | 2 + core/fmt/fmt.odin | 16 +++---- core/io/io.odin | 4 +- core/reflect/types.odin | 8 ++-- src/check_builtin.cpp | 2 + src/check_expr.cpp | 42 ++++++++++++++--- src/checker_builtin_procs.hpp | 2 + src/llvm_backend.cpp | 6 +++ src/llvm_backend_const.cpp | 77 +++++++++++++++++++++++++++++-- src/llvm_backend_debug.cpp | 14 ++++++ src/llvm_backend_expr.cpp | 3 +- src/llvm_backend_general.cpp | 37 +++++++++++++++ src/llvm_backend_utility.cpp | 19 +++++++- src/string.cpp | 7 +++ 20 files changed, 230 insertions(+), 32 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index be75739fe..d45d24f48 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -141,6 +141,7 @@ type_is_quaternion :: proc($T: typeid) -> bool --- type_is_string :: proc($T: typeid) -> bool --- type_is_typeid :: proc($T: typeid) -> bool --- type_is_any :: proc($T: typeid) -> bool --- +type_is_string16 :: proc($T: typeid) -> bool --- type_is_endian_platform :: proc($T: typeid) -> bool --- type_is_endian_little :: proc($T: typeid) -> bool --- diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 145f002d1..85ed49445 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -293,7 +293,13 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_string("quaternion") print_u64(u64(8*ti.size)) case Type_Info_String: + if info.is_cstring { + print_byte('c') + } print_string("string") + if info.is_utf16 { + print_string("16") + } case Type_Info_Boolean: switch ti.id { case bool: print_string("bool") diff --git a/core/encoding/cbor/tags.odin b/core/encoding/cbor/tags.odin index 17420af46..e0e69cbf5 100644 --- a/core/encoding/cbor/tags.odin +++ b/core/encoding/cbor/tags.odin @@ -298,7 +298,7 @@ tag_base64_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, #partial switch t in ti.variant { case reflect.Type_Info_String: - + assert(!t.is_utf16) if t.is_cstring { length := base64.decoded_len(bytes) builder := strings.builder_make(0, length+1) diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index 365ac5d6f..2840429f5 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -335,6 +335,8 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) { #partial switch t in ti.variant { case reflect.Type_Info_String: + assert(!t.is_utf16) + bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return if t.is_cstring { diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index ebb9a639c..cdb00a354 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -353,10 +353,10 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: #partial switch info in ti.variant { case runtime.Type_Info_String: switch x in v { - case string: - return x == "" - case cstring: - return x == nil || x == "" + case string: return x == "" + case cstring: return x == nil || x == "" + case string16: return x == "" + case cstring16: return x == nil || x == "" } case runtime.Type_Info_Any: return v.(any) == nil diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index b9ed1476f..51e7e3b81 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -570,7 +570,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm key_ptr: rawptr #partial switch tk in t.key.variant { - case runtime.Type_Info_String: + case runtime.Type_Info_String: + assert(!tk.is_utf16) + key_ptr = rawptr(&key) key_cstr: cstring if reflect.is_cstring(t.key) { diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 1c559ca55..58224cc87 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -127,6 +127,8 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: } case runtime.Type_Info_String: + assert(!specific_type_info.is_utf16) + if specific_type_info.is_cstring { cstr_ptr := (^cstring)(ptr) if cstr_ptr != nil { diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 7fe6287d4..9c245de94 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -2346,14 +2346,14 @@ fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflec } switch reflect.type_info_base(elem).id { - case byte: fmt_string(fi, string(([^]byte)(data)[:n]), verb); return - case u16: print_utf16(fi, ([^]u16)(data)[:n]); return - case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return - case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return - case u32: print_utf32(fi, ([^]u32)(data)[:n]); return - case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return - case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return - case rune: print_utf32(fi, ([^]rune)(data)[:n]); return + case byte: fmt_string(fi, string (([^]byte)(data)[:n]), verb); return + case u16: fmt_string16(fi, string16(([^]u16) (data)[:n]), verb); return + case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return + case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return + case u32: print_utf32(fi, ([^]u32)(data)[:n]); return + case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return + case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return + case rune: print_utf32(fi, ([^]rune)(data)[:n]); return } } if verb == 'p' { diff --git a/core/io/io.odin b/core/io/io.odin index 5431519bf..c4eb6a073 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -319,7 +319,6 @@ write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: int, err: Error) { for i := 0; i < len(str); i += 1 { r := rune(utf16.REPLACEMENT_CHAR) - switch c := str[i]; { case c < utf16._surr1, utf16._surr3 <= c: r = rune(c) @@ -329,7 +328,8 @@ write_string16 :: proc(s: Writer, str: string16, n_written: ^int = nil) -> (n: i i += 1 } - w, err := write_rune(s, r, n_written) + w: int + w, err = write_rune(s, r, n_written) n += w if err != nil { return diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 511c5c9bd..2351408cc 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -511,9 +511,11 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt io.write_i64(w, i64(8*ti.size), 10, &n) or_return case Type_Info_String: if info.is_cstring { - io.write_string(w, "cstring", &n) or_return - } else { - io.write_string(w, "string", &n) or_return + io.write_byte(w, 'c', &n) or_return + } + io.write_string(w, "string", &n) or_return + if info.is_utf16 { + io.write_string(w, "16", &n) or_return } case Type_Info_Boolean: switch ti.id { diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d36cf4520..4abace637 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -19,6 +19,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_complex, is_type_quaternion, is_type_string, + is_type_string16, is_type_typeid, is_type_any, is_type_endian_platform, @@ -6139,6 +6140,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_complex: case BuiltinProc_type_is_quaternion: case BuiltinProc_type_is_string: + case BuiltinProc_type_is_string16: case BuiltinProc_type_is_typeid: case BuiltinProc_type_is_any: case BuiltinProc_type_is_endian_platform: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 57073e22f..8d2e4d637 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2106,6 +2106,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i } else if (is_type_boolean(type)) { return in_value.kind == ExactValue_Bool; } else if (is_type_string(type)) { + if (in_value.kind == ExactValue_String16) { + return is_type_string16(type) || is_type_cstring16(type); + } return in_value.kind == ExactValue_String; } else if (is_type_integer(type) || is_type_rune(type)) { if (in_value.kind == ExactValue_Bool) { @@ -2320,6 +2323,9 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i if (in_value.kind == ExactValue_String) { return false; } + if (in_value.kind == ExactValue_String16) { + return false; + } if (out_value) *out_value = in_value; } else if (is_type_bit_set(type)) { if (in_value.kind == ExactValue_Integer) { @@ -4654,6 +4660,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar break; } } + } else if (operand->value.kind == ExactValue_String16) { + String16 s = operand->value.value_string16; + if (is_type_u16_array(t)) { + if (s.len == t->Array.count) { + break; + } + } } operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -4983,6 +4996,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v if (success_) *success_ = true; if (finish_) *finish_ = true; return exact_value_u64(val); + } else if (value.kind == ExactValue_String16) { + GB_ASSERT(0 <= index && index < value.value_string.len); + u16 val = value.value_string16[index]; + if (success_) *success_ = true; + if (finish_) *finish_ = true; + return exact_value_u64(val); } if (value.kind != ExactValue_Compound) { if (success_) *success_ = true; @@ -11124,15 +11143,21 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, o->expr = node; return kind; } - - String s = {}; - if (o->value.kind == ExactValue_String) { - s = o->value.value_string; - } - o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + + if (o->value.kind == ExactValue_String16) { + String16 s = o->value.value_string16; + + o->value = exact_value_string16(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + } else { + String s = {}; + if (o->value.kind == ExactValue_String) { + s = o->value.value_string; + } + + o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1])); + } } return kind; } @@ -11221,6 +11246,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast Type *t = t_invalid; switch (node->tav.value.kind) { case ExactValue_String: t = t_untyped_string; break; + case ExactValue_String16: t = t_string16; break; // TODO(bill): determine this correctly case ExactValue_Float: t = t_untyped_float; break; case ExactValue_Complex: t = t_untyped_complex; break; case ExactValue_Quaternion: t = t_untyped_quaternion; break; @@ -11657,6 +11683,8 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) { return !v.value_bool; case ExactValue_String: return v.value_string.len == 0; + case ExactValue_String16: + return v.value_string16.len == 0; case ExactValue_Integer: return big_int_is_zero(&v.value_integer); case ExactValue_Float: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 8e135ab10..bff887d9e 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -250,6 +250,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_complex, BuiltinProc_type_is_quaternion, BuiltinProc_type_is_string, + BuiltinProc_type_is_string16, BuiltinProc_type_is_typeid, BuiltinProc_type_is_any, @@ -607,6 +608,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_string16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 13a1d8cf3..f37415cc1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1264,7 +1264,13 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { case Basic_string: return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}"); + case Basic_string16: + return build_context.metrics.int_size == 4 ? str_lit("{string16=*i}") : str_lit("{string16=*q}"); + case Basic_cstring: return str_lit("*"); + case Basic_cstring16: return str_lit("*"); + + case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info case Basic_typeid: diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index c3112934e..8c05ed4a2 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -122,6 +122,25 @@ gb_internal lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) { gb_internal LLVMValueRef llvm_const_string_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) { + GB_ASSERT(!is_type_string16(t)); + if (build_context.metrics.ptr_size < build_context.metrics.int_size) { + LLVMValueRef values[3] = { + data, + LLVMConstNull(lb_type(m, t_i32)), + len, + }; + return llvm_const_named_struct_internal(lb_type(m, t), values, 3); + } else { + LLVMValueRef values[2] = { + data, + len, + }; + return llvm_const_named_struct_internal(lb_type(m, t), values, 2); + } +} + +gb_internal LLVMValueRef llvm_const_string16_internal(lbModule *m, Type *t, LLVMValueRef data, LLVMValueRef len) { + GB_ASSERT(is_type_string16(t)); if (build_context.metrics.ptr_size < build_context.metrics.int_size) { LLVMValueRef values[3] = { data, @@ -238,6 +257,10 @@ gb_internal lbValue lb_const_string(lbModule *m, String const &value) { return lb_const_value(m, t_string, exact_value_string(value)); } +gb_internal lbValue lb_const_string(lbModule *m, String16 const &value) { + return lb_const_value(m, t_string16, exact_value_string16(value)); +} + gb_internal lbValue lb_const_bool(lbModule *m, Type *type, bool value) { lbValue res = {}; @@ -569,7 +592,11 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb GB_ASSERT(is_type_slice(type)); res.value = lb_find_or_add_entity_string_byte_slice_with_type(m, value.value_string, original_type).value; return res; - } else { + } else if (value.kind == ExactValue_String16) { + GB_ASSERT(is_type_slice(type)); + GB_PANIC("TODO(bill): UTF-16 String"); + return res; + }else { ast_node(cl, CompoundLit, value.value_compound); isize count = cl->elems.count; @@ -751,15 +778,23 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb { bool custom_link_section = cc.link_section.len > 0; - LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); + LLVMValueRef ptr = nullptr; lbValue res = {}; res.type = default_type(original_type); + if (is_type_string16(res.type) || is_type_cstring16(res.type)) { + TEMPORARY_ALLOCATOR_GUARD(); + String16 s16 = string_to_string16(temporary_allocator(), value.value_string); + ptr = lb_find_or_add_entity_string16_ptr(m, s16, custom_link_section); + } else { + ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); + } + if (custom_link_section) { LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section)); } - if (is_type_cstring(res.type)) { + if (is_type_cstring(res.type) || is_type_cstring16(res.type)) { res.value = ptr; } else { if (value.value_string.len == 0) { @@ -768,12 +803,46 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true); GB_ASSERT(is_type_string(original_type)); - res.value = llvm_const_string_internal(m, original_type, ptr, str_len); + if (is_type_string16(res.type)) { + res.value = llvm_const_string16_internal(m, original_type, ptr, str_len); + } else { + res.value = llvm_const_string_internal(m, original_type, ptr, str_len); + } } return res; } + case ExactValue_String16: + { + GB_ASSERT(is_type_string16(res.type) || is_type_cstring16(res.type)); + + bool custom_link_section = cc.link_section.len > 0; + + LLVMValueRef ptr = lb_find_or_add_entity_string16_ptr(m, value.value_string16, custom_link_section); + lbValue res = {}; + res.type = default_type(original_type); + + if (custom_link_section) { + LLVMSetSection(ptr, alloc_cstring(permanent_allocator(), cc.link_section)); + } + + if (is_type_cstring16(res.type)) { + res.value = ptr; + } else { + if (value.value_string16.len == 0) { + ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + } + LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string16.len, true); + GB_ASSERT(is_type_string(original_type)); + + res.value = llvm_const_string16_internal(m, original_type, ptr, str_len); + } + + return res; + } + + case ExactValue_Integer: if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(type)) { LLVMTypeRef t = lb_type(m, original_type); diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 024c5564e..182920fc7 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -802,6 +802,20 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("char"), 8, LLVMDWARFTypeEncoding_Unsigned); return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring", 7); } + + case Basic_string16: + { + LLVMMetadataRef elements[2] = {}; + elements[0] = lb_debug_struct_field(m, str_lit("data"), t_u16_ptr, 0); + elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, int_bits); + return lb_debug_basic_struct(m, str_lit("string16"), 2*int_bits, int_bits, elements, gb_count_of(elements)); + } + case Basic_cstring16: + { + LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("wchar_t"), 16, LLVMDWARFTypeEncoding_Unsigned); + return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, ptr_bits, ptr_bits, 0, "cstring16", 7); + } + case Basic_any: { LLVMMetadataRef elements[2] = {}; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index fbf0dea11..3463b6083 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4354,12 +4354,13 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) { } - case Type_Basic: { // Basic_string + case Type_Basic: { // Basic_string/Basic_string16 lbValue str; lbValue elem; lbValue len; lbValue index; + str = lb_build_expr(p, ie->expr); if (deref) { str = lb_emit_load(p, str); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index d9771a75b..9ef1c23c0 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2715,6 +2715,43 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co } } +gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String16 const &str, bool custom_link_section) { + // TODO(bill): caching for UTF-16 strings + + LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; + + LLVMValueRef data = nullptr; + { + LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx); + + TEMPORARY_ALLOCATOR_GUARD(); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1); + + for (isize i = 0; i < str.len; i++) { + values[i] = LLVMConstInt(llvm_u16, str.text[i], false); + } + values[str.len] = LLVMConstInt(llvm_u16, 0, false); + + data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1)); + } + + + u32 id = m->global_array_index.fetch_add(1); + gbString name = gb_string_make(temporary_allocator(), "csbs$"); + name = gb_string_appendc(name, m->module_name); + name = gb_string_append_fmt(name, "$%x", id); + + LLVMTypeRef type = LLVMTypeOf(data); + LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); + LLVMSetInitializer(global_data, data); + lb_make_global_private_const(global_data); + LLVMSetAlignment(global_data, 1); + + LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); + return ptr; +} + gb_internal lbValue lb_find_or_add_entity_string(lbModule *m, String const &str, bool custom_link_section) { LLVMValueRef ptr = nullptr; if (str.len != 0) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index d4117b7ff..ea1bae4e9 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -6,6 +6,7 @@ gb_internal bool lb_is_type_aggregate(Type *t) { case Type_Basic: switch (t->Basic.kind) { case Basic_string: + case Basic_string16: case Basic_any: return true; @@ -981,7 +982,8 @@ gb_internal i32 lb_convert_struct_index(lbModule *m, Type *t, i32 index) { } else if (build_context.ptr_size != build_context.int_size) { switch (t->kind) { case Type_Basic: - if (t->Basic.kind != Basic_string) { + if (t->Basic.kind != Basic_string && + t->Basic.kind != Basic_string16) { break; } /*fallthrough*/ @@ -1160,6 +1162,11 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) { case 0: result_type = alloc_type_pointer(t->Slice.elem); break; case 1: result_type = t_int; break; } + } else if (is_type_string16(t)) { + switch (index) { + case 0: result_type = t_u16_ptr; break; + case 1: result_type = t_int; break; + } } else if (is_type_string(t)) { switch (index) { case 0: result_type = t_u8_ptr; break; @@ -1273,6 +1280,12 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) { switch (t->kind) { case Type_Basic: switch (t->Basic.kind) { + case Basic_string16: + switch (index) { + case 0: result_type = t_u16_ptr; break; + case 1: result_type = t_int; break; + } + break; case Basic_string: switch (index) { case 0: result_type = t_u8_ptr; break; @@ -1440,6 +1453,10 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection e = lb_emit_struct_ep(p, e, index); break; + case Basic_string16: + e = lb_emit_struct_ep(p, e, index); + break; + default: GB_PANIC("un-gep-able type %s", type_to_string(type)); break; diff --git a/src/string.cpp b/src/string.cpp index 8405938f4..8cc0e93f3 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -79,6 +79,13 @@ gb_internal String substring(String const &s, isize lo, isize hi) { return make_string(s.text+lo, hi-lo); } +gb_internal String16 substring(String16 const &s, isize lo, isize hi) { + isize max = s.len; + GB_ASSERT_MSG(lo <= hi && hi <= max, "%td..%td..%td", lo, hi, max); + + return make_string16(s.text+lo, hi-lo); +} + gb_internal char *alloc_cstring(gbAllocator a, String s) { char *c_str = gb_alloc_array(a, char, s.len+1); From bb4bc316a4bd86774953f1e8fcefffb5ed8bbf37 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:20:35 +0100 Subject: [PATCH 085/160] `for in string16`; Support `string16` across core --- base/runtime/internal.odin | 62 +++++++++++++ core/c/libc/locale.odin | 16 ++-- core/debug/trace/trace_windows.odin | 2 +- core/dynlib/lib_windows.odin | 2 +- core/mem/virtual/virtual_windows.odin | 2 +- core/os/dir_windows.odin | 2 +- core/os/os2/file_windows.odin | 2 +- core/os/stat_windows.odin | 4 +- core/path/filepath/path_windows.odin | 4 +- core/sys/info/platform_windows.odin | 12 +-- core/sys/windows/comctl32.odin | 10 +-- core/sys/windows/ip_helper.odin | 6 +- core/sys/windows/types.odin | 4 +- core/sys/windows/util.odin | 50 +++++------ core/time/timezone/tz_windows.odin | 8 +- core/unicode/utf16/utf16.odin | 32 ++++++- src/check_builtin.cpp | 6 +- src/check_expr.cpp | 2 +- src/check_stmt.cpp | 26 +++++- src/llvm_backend_stmt.cpp | 121 +++++++++++++++++++++++++- src/llvm_backend_utility.cpp | 17 ++++ 21 files changed, 321 insertions(+), 69 deletions(-) diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 660af58ab..4f9509b23 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -781,6 +781,68 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) { return r, size } + +string16_decode_rune :: #force_inline proc "contextless" (s: string16) -> (rune, int) { + REPLACEMENT_CHAR :: '\ufffd' + _surr1 :: 0xd800 + _surr2 :: 0xdc00 + _surr3 :: 0xe000 + _surr_self :: 0x10000 + + r := rune(REPLACEMENT_CHAR) + + if len(s) < 1 { + return r, 0 + } + + w := 1 + switch c := s[0]; { + case c < _surr1, _surr3 <= c: + r = rune(c) + case _surr1 <= c && c < _surr2 && 1 < len(s) && + _surr2 <= s[1] && s[1] < _surr3: + r1, r2 := rune(c), rune(s[1]) + if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 { + r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self + } + w += 1 + } + return r, w +} + +string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) { + REPLACEMENT_CHAR :: '\ufffd' + _surr1 :: 0xd800 + _surr2 :: 0xdc00 + _surr3 :: 0xe000 + _surr_self :: 0x10000 + + r := rune(REPLACEMENT_CHAR) + + if len(s) < 1 { + return r, 0 + } + + n := len(s)-1 + c := s[n] + w := 1 + if _surr2 <= c && c < _surr3 { + if n >= 1 { + r1 := rune(s[n-1]) + r2 := rune(c) + if _surr1 <= r1 && r1 < _surr2 { + r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self + } + w = 2 + } + } else if c < _surr1 || _surr3 <= c { + r = rune(c) + } + return r, w +} + + + abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 { p, q := abs(real(x)), abs(imag(x)) if p < q { diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin index 27317526c..3216e0f90 100644 --- a/core/c/libc/locale.odin +++ b/core/c/libc/locale.odin @@ -72,14 +72,14 @@ when ODIN_OS == .Windows { n_sep_by_space: c.char, p_sign_posn: c.char, n_sign_posn: c.char, - _W_decimal_point: [^]u16 `fmt:"s,0"`, - _W_thousands_sep: [^]u16 `fmt:"s,0"`, - _W_int_curr_symbol: [^]u16 `fmt:"s,0"`, - _W_currency_symbol: [^]u16 `fmt:"s,0"`, - _W_mon_decimal_point: [^]u16 `fmt:"s,0"`, - _W_mon_thousands_sep: [^]u16 `fmt:"s,0"`, - _W_positive_sign: [^]u16 `fmt:"s,0"`, - _W_negative_sign: [^]u16 `fmt:"s,0"`, + _W_decimal_point: cstring16, + _W_thousands_sep: cstring16, + _W_int_curr_symbol: cstring16, + _W_currency_symbol: cstring16, + _W_mon_decimal_point: cstring16, + _W_mon_thousands_sep: cstring16, + _W_positive_sign: cstring16, + _W_negative_sign: cstring16, } } else { lconv :: struct { diff --git a/core/debug/trace/trace_windows.odin b/core/debug/trace/trace_windows.odin index 96507714c..04e92f125 100644 --- a/core/debug/trace/trace_windows.odin +++ b/core/debug/trace/trace_windows.odin @@ -54,7 +54,7 @@ _resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> ( symbol.SizeOfStruct = size_of(symbol^) symbol.MaxNameLen = 255 if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) { - fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator) + fl.procedure, _ = win32.wstring_to_utf8(cstring16(&symbol.Name[0]), -1, allocator) } else { fl.procedure = fmt.aprintf("(procedure: 0x%x)", frame, allocator=allocator) } diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin index 05cd2cb3c..95372dac6 100644 --- a/core/dynlib/lib_windows.odin +++ b/core/dynlib/lib_windows.odin @@ -13,7 +13,7 @@ _LIBRARY_FILE_EXTENSION :: "dll" _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) { // NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL wide_path := win32.utf8_to_wstring(path, allocator) - defer free(wide_path, allocator) + defer free(rawptr(wide_path), allocator) handle := cast(Library)win32.LoadLibraryW(wide_path) return handle, handle != nil } diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 0da8498d5..3fd4eeb68 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -72,7 +72,7 @@ foreign Kernel32 { flProtect: u32, dwMaximumSizeHigh: u32, dwMaximumSizeLow: u32, - lpName: [^]u16, + lpName: cstring16, ) -> rawptr --- MapViewOfFile :: proc( diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin index ae3e6922c..40f4b9e9b 100644 --- a/core/os/dir_windows.odin +++ b/core/os/dir_windows.odin @@ -87,7 +87,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F defer delete(path) find_data := &win32.WIN32_FIND_DATAW{} - find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) + find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data) if find_handle == win32.INVALID_HANDLE_VALUE { err = get_last_error() return dfi[:], err diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 40d012183..1134e765c 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -619,7 +619,7 @@ _symlink :: proc(old_name, new_name: string) -> Error { return .Unsupported } -_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) { +_open_sym_link :: proc(p: cstring16) -> (handle: win32.HANDLE, err: Error) { attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS) attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil) diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin index ca4f87668..662c9f9e6 100644 --- a/core/os/stat_windows.odin +++ b/core/os/stat_windows.odin @@ -17,7 +17,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa buf := make([dynamic]u16, 100) defer delete(buf) for { - n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) if n == 0 { return "", get_last_error() } @@ -154,7 +154,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ( return nil, get_last_error() } buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) - buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0) + buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0) return buf[:buf_len], nil } @(private, require_results) diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 0dcb28cf8..24c6e00a5 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -61,13 +61,13 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Error) { } p := win32.utf8_to_utf16(name, ta) - n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) + n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil) if n == 0 { return "", os.get_last_error() } buf := make([]u16, n, ta) - n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) if n == 0 { delete(buf) return "", os.get_last_error() diff --git a/core/sys/info/platform_windows.odin b/core/sys/info/platform_windows.odin index 4c00ddadf..dd1441d30 100644 --- a/core/sys/info/platform_windows.odin +++ b/core/sys/info/platform_windows.odin @@ -324,8 +324,8 @@ read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok status := sys.RegGetValueW( hkey, - &key_name_wide[0], - &val_name_wide[0], + cstring16(&key_name_wide[0]), + cstring16(&val_name_wide[0]), sys.RRF_RT_REG_SZ, nil, raw_data(result_wide[:]), @@ -359,8 +359,8 @@ read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool result_size := sys.DWORD(size_of(i32)) status := sys.RegGetValueW( hkey, - &key_name_wide[0], - &val_name_wide[0], + cstring16(&key_name_wide[0]), + cstring16(&val_name_wide[0]), sys.RRF_RT_REG_DWORD, nil, &res, @@ -386,8 +386,8 @@ read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool result_size := sys.DWORD(size_of(i64)) status := sys.RegGetValueW( hkey, - &key_name_wide[0], - &val_name_wide[0], + cstring16(&key_name_wide[0]), + cstring16(&val_name_wide[0]), sys.RRF_RT_REG_QWORD, nil, &res, diff --git a/core/sys/windows/comctl32.odin b/core/sys/windows/comctl32.odin index d954f952c..c7a166634 100644 --- a/core/sys/windows/comctl32.odin +++ b/core/sys/windows/comctl32.odin @@ -573,10 +573,10 @@ Button_GetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT) return cast(BOOL)SendMessageW(hwnd, BCM_GETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin)) } Button_SetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(psz)) + return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(rawptr(psz))) } Button_GetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR, pcc: ^c_int) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(psz)) + return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(rawptr(psz))) } Button_GetNoteLength :: #force_inline proc "system" (hwnd: HWND) -> LRESULT { return SendMessageW(hwnd, BCM_GETNOTELENGTH, 0, 0) @@ -604,10 +604,10 @@ EDITBALLOONTIP :: struct { PEDITBALLOONTIP :: ^EDITBALLOONTIP Edit_SetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(lpcwText)) + return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(rawptr(lpcwText))) } Edit_SetCueBannerTextFocused :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR, fDrawFocused: BOOL) -> BOOL { - return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(lpcwText)) + return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(rawptr(lpcwText))) } Edit_GetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpwText: LPWSTR, cchText: LONG) -> BOOL { return cast(BOOL)SendMessageW(hwnd, EM_GETCUEBANNER, uintptr(lpwText), cast(LPARAM)cchText) @@ -1197,7 +1197,7 @@ ListView_GetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, p return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMPOSITION, cast(WPARAM)i, cast(LPARAM)uintptr(ppt)) } ListView_GetStringWidth :: #force_inline proc "system" (hwndLV: HWND, psz: LPCWSTR) -> c_int { - return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(psz)) + return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(rawptr(psz))) } ListView_HitTest :: #force_inline proc "system" (hwndLV: HWND, pinfo: ^LV_HITTESTINFO) -> c_int { return cast(c_int)SendMessageW(hwndLV, LVM_HITTEST, 0, cast(LPARAM)uintptr(pinfo)) diff --git a/core/sys/windows/ip_helper.odin b/core/sys/windows/ip_helper.odin index 7a6e545ac..d2e75d531 100644 --- a/core/sys/windows/ip_helper.odin +++ b/core/sys/windows/ip_helper.odin @@ -38,9 +38,9 @@ IP_Adapter_Addresses :: struct { FirstAnycastAddress: ^IP_ADAPTER_ANYCAST_ADDRESS_XP, FirstMulticastAddress: ^IP_ADAPTER_MULTICAST_ADDRESS_XP, FirstDnsServerAddress: ^IP_ADAPTER_DNS_SERVER_ADDRESS_XP, - DnsSuffix: ^u16, - Description: ^u16, - FriendlyName: ^u16, + DnsSuffix: cstring16, + Description: cstring16, + FriendlyName: cstring16, PhysicalAddress: [8]u8, PhysicalAddressLength: u32, Anonymous2: struct #raw_union { diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 92b1cb15c..be16d2fdd 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -107,8 +107,8 @@ PDWORD64 :: ^DWORD64 PDWORD_PTR :: ^DWORD_PTR ATOM :: distinct WORD -wstring :: [^]WCHAR -PWSTR :: [^]WCHAR +wstring :: cstring16 +PWSTR :: cstring16 PBYTE :: ^BYTE LPBYTE :: ^BYTE diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index 995e8e0e5..10dc907e7 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -122,14 +122,14 @@ utf8_to_utf16 :: proc{utf8_to_utf16_alloc, utf8_to_utf16_buf} utf8_to_wstring_alloc :: proc(s: string, allocator := context.temp_allocator) -> wstring { if res := utf8_to_utf16(s, allocator); len(res) > 0 { - return raw_data(res) + return wstring(raw_data(res)) } return nil } utf8_to_wstring_buf :: proc(buf: []u16, s: string) -> wstring { if res := utf8_to_utf16(buf, s); len(res) > 0 { - return raw_data(res) + return wstring(raw_data(res)) } return nil } @@ -215,7 +215,7 @@ utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (r if len(s) == 0 { return "", nil } - return wstring_to_utf8(raw_data(s), len(s), allocator) + return wstring_to_utf8(wstring(raw_data(s)), len(s), allocator) } /* @@ -236,7 +236,7 @@ utf16_to_utf8_buf :: proc(buf: []u8, s: []u16) -> (res: string) { if len(s) == 0 { return } - return wstring_to_utf8(buf, raw_data(s), len(s)) + return wstring_to_utf8(buf, wstring(raw_data(s)), len(s)) } utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf} @@ -298,7 +298,7 @@ _add_user :: proc(servername: string, username: string, password: string) -> (ok servername_w = nil } else { server := utf8_to_utf16(servername, context.temp_allocator) - servername_w = &server[0] + servername_w = wstring(&server[0]) } if len(username) == 0 || len(username) > LM20_UNLEN { @@ -348,7 +348,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s res := LookupAccountNameW( nil, // Look on this computer first - &username_w[0], + wstring(&username_w[0]), &sid, &cbsid, nil, @@ -364,10 +364,10 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s res = LookupAccountNameW( nil, - &username_w[0], + wstring(&username_w[0]), &sid, &cbsid, - &cname_w[0], + wstring(&cname_w[0]), &computer_name_size, &pe_use, ) @@ -390,7 +390,7 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) { res := LookupAccountNameW( nil, // Look on this computer first - &username_w[0], + wstring(&username_w[0]), sid, &cbsid, nil, @@ -406,10 +406,10 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) { res = LookupAccountNameW( nil, - &username_w[0], + wstring(&username_w[0]), sid, &cbsid, - &cname_w[0], + wstring(&cname_w[0]), &computer_name_size, &pe_use, ) @@ -428,7 +428,7 @@ add_user_to_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) { group_name := utf8_to_utf16(group, context.temp_allocator) ok = NetLocalGroupAddMembers( nil, - &group_name[0], + wstring(&group_name[0]), 0, &group_member, 1, @@ -443,7 +443,7 @@ add_del_from_group :: proc(sid: ^SID, group: string) -> (ok: NET_API_STATUS) { group_name := utf8_to_utf16(group, context.temp_allocator) ok = NetLocalGroupDelMembers( nil, - &group_name[0], + cstring16(&group_name[0]), 0, &group_member, 1, @@ -465,19 +465,19 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) { if res == false { return false, "" } - defer LocalFree(sb) + defer LocalFree(rawptr(sb)) pszProfilePath := make([]u16, 257, context.temp_allocator) res2 := CreateProfile( sb, - &username_w[0], - &pszProfilePath[0], + cstring16(&username_w[0]), + cstring16(&pszProfilePath[0]), 257, ) if res2 != 0 { return false, "" } - profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else "" + profile_path = wstring_to_utf8(wstring(&pszProfilePath[0]), 257) or_else "" return true, profile_path } @@ -495,7 +495,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) { if res == false { return false } - defer LocalFree(sb) + defer LocalFree(rawptr(sb)) res2 := DeleteProfileW( sb, @@ -548,13 +548,13 @@ delete_user :: proc(servername: string, username: string) -> (ok: bool) { servername_w = nil } else { server := utf8_to_utf16(servername, context.temp_allocator) - servername_w = &server[0] + servername_w = wstring(&server[0]) } username_w := utf8_to_utf16(username) res := NetUserDel( servername_w, - &username_w[0], + wstring(&username_w[0]), ) if res != .Success { return false @@ -586,9 +586,9 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P user_token: HANDLE ok = bool(LogonUserW( - lpszUsername = &username_w[0], - lpszDomain = &domain_w[0], - lpszPassword = &password_w[0], + lpszUsername = wstring(&username_w[0]), + lpszDomain = wstring(&domain_w[0]), + lpszPassword = wstring(&password_w[0]), dwLogonType = .NEW_CREDENTIALS, dwLogonProvider = .WINNT50, phToken = &user_token, @@ -605,8 +605,8 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P ok = bool(CreateProcessAsUserW( user_token, - &app_w[0], - &commandline_w[0], + wstring(&app_w[0]), + wstring(&commandline_w[0]), nil, // lpProcessAttributes, nil, // lpThreadAttributes, false, // bInheritHandles, diff --git a/core/time/timezone/tz_windows.odin b/core/time/timezone/tz_windows.odin index 8dc5f533c..fe00719a2 100644 --- a/core/time/timezone/tz_windows.odin +++ b/core/time/timezone/tz_windows.odin @@ -159,9 +159,9 @@ iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> status: windows.UError iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator) - defer free(iana_name_wstr, allocator) + defer free(rawptr(iana_name_wstr), allocator) - wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, raw_data(wintz_name_buffer[:]), len(wintz_name_buffer), &status) + wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, cstring16(raw_data(wintz_name_buffer[:])), len(wintz_name_buffer), &status) if status != .U_ZERO_ERROR { return } @@ -178,7 +178,7 @@ local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: iana_name_buffer: [128]u16 status: windows.UError - zone_str_len := windows.ucal_getDefaultTimeZone(raw_data(iana_name_buffer[:]), len(iana_name_buffer), &status) + zone_str_len := windows.ucal_getDefaultTimeZone(cstring16(raw_data(iana_name_buffer[:])), len(iana_name_buffer), &status) if status != .U_ZERO_ERROR { return } @@ -291,7 +291,7 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re defer delete(tz_key, allocator) tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator) - defer free(tz_key_wstr, allocator) + defer free(rawptr(tz_key_wstr), allocator) key: windows.HKEY res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key) diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index 9a8cfe438..d3f98584b 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -126,7 +126,37 @@ decode_rune_in_string :: proc(s: string16) -> (r: rune, width: int) { return } -rune_count :: proc(s: []u16) -> (n: int) { +string_to_runes :: proc "odin" (s: string16, allocator := context.allocator) -> (runes: []rune) { + n := rune_count(s) + + runes = make([]rune, n, allocator) + i := 0 + for r in s { + runes[i] = r + i += 1 + } + return +} + + +rune_count :: proc{ + rune_count_in_string, + rune_count_in_slice, +} +rune_count_in_string :: proc(s: string16) -> (n: int) { + for i := 0; i < len(s); i += 1 { + c := s[i] + if _surr1 <= c && c < _surr2 && i+1 < len(s) && + _surr2 <= s[i+1] && s[i+1] < _surr3 { + i += 1 + } + n += 1 + } + return +} + + +rune_count_in_slice :: proc(s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { c := s[i] if _surr1 <= c && c < _surr2 && i+1 < len(s) && diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4abace637..66ea0cfbd 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -7179,7 +7179,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } operand->mode = Addressing_Value; - operand->type = alloc_type_multi_pointer(t_u16); + if (type_hint != nullptr && is_type_cstring16(type_hint)) { + operand->type = type_hint; + } else { + operand->type = alloc_type_multi_pointer(t_u16); + } operand->value = {}; break; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8d2e4d637..34149f92b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3426,7 +3426,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type if (are_types_identical(src, t_cstring16) && is_type_u16_multi_ptr(dst)) { return !is_constant; } - // cstring -> rawptr + // cstring16 -> rawptr if (are_types_identical(src, t_cstring16) && is_type_rawptr(dst)) { return !is_constant; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index bc9b6c5dd..ae88ff333 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -974,7 +974,14 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod Type *t = base_type(operand.type); switch (t->kind) { case Type_Basic: - if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + if (is_type_string16(t) && t->Basic.kind != Basic_cstring) { + val0 = t_rune; + val1 = t_int; + inline_for_depth = exact_value_i64(operand.value.value_string.len); + if (unroll_count > 0) { + error(node, "#unroll(%lld) does not support strings", cast(long long)unroll_count); + } + } else if (is_type_string(t) && t->Basic.kind != Basic_cstring) { val0 = t_rune; val1 = t_int; inline_for_depth = exact_value_i64(operand.value.value_string.len); @@ -1236,7 +1243,11 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs); - if (is_type_string(x.type)) { + if (is_type_string16(x.type)) { + // NOTE(bill): Force dependency for strings here + add_package_dependency(ctx, "runtime", "string16_le"); + add_package_dependency(ctx, "runtime", "string16_lt"); + } else if (is_type_string(x.type)) { // NOTE(bill): Force dependency for strings here add_package_dependency(ctx, "runtime", "string_le"); add_package_dependency(ctx, "runtime", "string_lt"); @@ -1770,7 +1781,16 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) switch (t->kind) { case Type_Basic: - if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { + if (t->Basic.kind == Basic_string16) { + is_possibly_addressable = false; + array_add(&vals, t_rune); + array_add(&vals, t_int); + if (is_reverse) { + add_package_dependency(ctx, "runtime", "string16_decode_last_rune"); + } else { + add_package_dependency(ctx, "runtime", "string16_decode_rune"); + } + } else if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) { is_possibly_addressable = false; array_add(&vals, t_rune); array_add(&vals, t_int); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 027837f3f..5481ca447 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -622,6 +622,121 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t if (done_) *done_ = done; } +gb_internal void lb_build_range_string16(lbProcedure *p, lbValue expr, Type *val_type, + lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_, + bool is_reverse) { + + lbModule *m = p->module; + lbValue count = lb_const_int(m, t_int, 0); + Type *expr_type = base_type(expr.type); + switch (expr_type->kind) { + case Type_Basic: + count = lb_string_len(p, expr); + break; + default: + GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type)); + break; + } + + lbValue val = {}; + lbValue idx = {}; + lbBlock *loop = nullptr; + lbBlock *done = nullptr; + lbBlock *body = nullptr; + + loop = lb_create_block(p, "for.string16.loop"); + body = lb_create_block(p, "for.string16.body"); + done = lb_create_block(p, "for.string16.done"); + + lbAddr offset_ = lb_add_local_generated(p, t_int, false); + lbValue offset = {}; + lbValue cond = {}; + + if (!is_reverse) { + /* + for c, offset in str { + ... + } + + offset := 0 + for offset < len(str) { + c, _w := string16_decode_rune(str[offset:]) + ... + offset += _w + } + */ + lb_addr_store(p, offset_, lb_const_int(m, t_int, 0)); + + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + + offset = lb_addr_load(p, offset_); + cond = lb_emit_comp(p, Token_Lt, offset, count); + } else { + // NOTE(bill): REVERSED LOGIC + /* + #reverse for c, offset in str { + ... + } + + offset := len(str) + for offset > 0 { + c, _w := string16_decode_last_rune(str[:offset]) + offset -= _w + ... + } + */ + lb_addr_store(p, offset_, count); + + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + offset = lb_addr_load(p, offset_); + cond = lb_emit_comp(p, Token_Gt, offset, lb_const_int(m, t_int, 0)); + } + lb_emit_if(p, cond, body, done); + lb_start_block(p, body); + + + lbValue rune_and_len = {}; + if (!is_reverse) { + lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset); + lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int); + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_emit_string16(p, str_elem, str_len); + + rune_and_len = lb_emit_runtime_call(p, "string16_decode_rune", args); + lbValue len = lb_emit_struct_ev(p, rune_and_len, 1); + lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int)); + + idx = offset; + } else { + // NOTE(bill): REVERSED LOGIC + lbValue str_elem = lb_string_elem(p, expr); + lbValue str_len = offset; + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_emit_string16(p, str_elem, str_len); + + rune_and_len = lb_emit_runtime_call(p, "string16_decode_last_rune", args); + lbValue len = lb_emit_struct_ev(p, rune_and_len, 1); + lb_addr_store(p, offset_, lb_emit_arith(p, Token_Sub, offset, len, t_int)); + + idx = lb_addr_load(p, offset_); + } + + + if (val_type != nullptr) { + val = lb_emit_struct_ev(p, rune_and_len, 0); + } + + if (val_) *val_ = val; + if (idx_) *idx_ = idx; + if (loop_) *loop_ = loop; + if (done_) *done_ = done; +} + + gb_internal Ast *lb_strip_and_prefix(Ast *ident) { if (ident != nullptr) { @@ -1138,7 +1253,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc } Type *t = base_type(string.type); GB_ASSERT(!is_type_cstring(t)); - lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse); + if (is_type_string16(t)) { + lb_build_range_string16(p, string, val0_type, &val, &key, &loop, &done, rs->reverse); + } else { + lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse); + } break; } case Type_Tuple: diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index ea1bae4e9..dcb95a9a2 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -191,6 +191,23 @@ gb_internal lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue mi return z; } +gb_internal lbValue lb_emit_string16(lbProcedure *p, lbValue str_elem, lbValue str_len) { + if (false && lb_is_const(str_elem) && lb_is_const(str_len)) { + LLVMValueRef values[2] = { + str_elem.value, + str_len.value, + }; + lbValue res = {}; + res.type = t_string16; + res.value = llvm_const_named_struct(p->module, t_string16, values, gb_count_of(values)); + return res; + } else { + lbAddr res = lb_add_local_generated(p, t_string16, false); + lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), str_elem); + lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), str_len); + return lb_addr_load(p, res); + } +} gb_internal lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { From 620bf162a048fdf29fdfcedc12abae79cffeedf4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:32:18 +0100 Subject: [PATCH 086/160] Cache const `string16` in LLVM --- src/check_builtin.cpp | 13 +++++-- src/common.cpp | 1 + src/llvm_backend.hpp | 3 +- src/llvm_backend_const.cpp | 2 +- src/llvm_backend_expr.cpp | 6 --- src/llvm_backend_general.cpp | 71 +++++++++++++++++++++++++++++++++++- 6 files changed, 83 insertions(+), 13 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 66ea0cfbd..da5eb8977 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2329,10 +2329,15 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (operand->mode == Addressing_Constant) { mode = Addressing_Constant; - GB_ASSERT_MSG(!is_type_string16(op_type), "TODO(bill): constant utf-16 string len"); - - String str = operand->value.value_string; - value = exact_value_i64(str.len); + if (operand->value.kind == ExactValue_String) { + String str = operand->value.value_string; + value = exact_value_i64(str.len); + } else if (operand->value.kind == ExactValue_String16) { + String16 str = operand->value.value_string16; + value = exact_value_i64(str.len); + } else { + GB_PANIC("Unhandled value kind: %d", operand->value.kind); + } type = t_untyped_integer; } else { mode = Addressing_Value; diff --git a/src/common.cpp b/src/common.cpp index b3761fc36..53848cacf 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -350,6 +350,7 @@ gb_global bool global_module_path_set = false; #include "ptr_map.cpp" #include "ptr_set.cpp" #include "string_map.cpp" +#include "string16_map.cpp" #include "string_set.cpp" #include "priority_queue.cpp" #include "thread_pool.cpp" diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index fef6e754d..648e8a732 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -173,7 +173,8 @@ struct lbModule { PtrMap procedure_values; Array missing_procedures_to_check; - StringMap const_strings; + StringMap const_strings; + String16Map const_string16s; PtrMap function_type_map; diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 8c05ed4a2..cba0000cd 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -594,7 +594,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb return res; } else if (value.kind == ExactValue_String16) { GB_ASSERT(is_type_slice(type)); - GB_PANIC("TODO(bill): UTF-16 String"); + res.value = lb_find_or_add_entity_string16_slice_with_type(m, value.value_string16, original_type).value; return res; }else { ast_node(cl, CompoundLit, value.value_compound); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 3463b6083..8ad6a5a1c 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2526,12 +2526,6 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { if (is_type_untyped(src)) { - if (is_type_string(src) && is_type_string16(dst)) { - GB_PANIC("TODO(bill): UTF-16 string"); - lbAddr result = lb_add_local_generated(p, t, false); - lb_addr_store(p, result, value); - return lb_addr_load(p, result); - } if (is_type_string(src) && is_type_string(dst)) { lbAddr result = lb_add_local_generated(p, t, false); lb_addr_store(p, result, value); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 9ef1c23c0..064d0ef39 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -85,6 +85,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { string_map_init(&m->members); string_map_init(&m->procedures); string_map_init(&m->const_strings); + string16_map_init(&m->const_string16s); map_init(&m->function_type_map); string_map_init(&m->gen_procs); if (USE_SEPARATE_MODULES) { @@ -2716,7 +2717,18 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String co } gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String16 const &str, bool custom_link_section) { - // TODO(bill): caching for UTF-16 strings + String16HashKey key = {}; + LLVMValueRef *found = nullptr; + + if (!custom_link_section) { + key = string_hash_string(str); + found = string16_map_get(&m->const_string16s, key); + } + if (found != nullptr) { + return *found; + } + + LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; @@ -2749,6 +2761,9 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String1 LLVMSetAlignment(global_data, 1); LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); + if (!custom_link_section) { + string16_map_set(&m->const_string16s, key, ptr); + } return ptr; } @@ -2812,6 +2827,60 @@ gb_internal lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule * return res; } +gb_internal lbValue lb_find_or_add_entity_string16_slice_with_type(lbModule *m, String16 const &str, Type *slice_type) { + GB_ASSERT(is_type_slice(slice_type)); + LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; + LLVMValueRef data = nullptr; + { + LLVMTypeRef llvm_u16 = LLVMInt16TypeInContext(m->ctx); + + TEMPORARY_ALLOCATOR_GUARD(); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, str.len+1); + + for (isize i = 0; i < str.len; i++) { + values[i] = LLVMConstInt(llvm_u16, str.text[i], false); + } + values[str.len] = LLVMConstInt(llvm_u16, 0, false); + + data = LLVMConstArray(llvm_u16, values, cast(unsigned)(str.len+1)); + } + + u32 id = m->global_array_index.fetch_add(1); + gbString name = gb_string_make(temporary_allocator(), "csba$"); + name = gb_string_appendc(name, m->module_name); + name = gb_string_append_fmt(name, "$%x", id); + + LLVMTypeRef type = LLVMTypeOf(data); + LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); + LLVMSetInitializer(global_data, data); + lb_make_global_private_const(global_data); + LLVMSetAlignment(global_data, 1); + + i64 data_len = str.len; + LLVMValueRef ptr = nullptr; + if (data_len != 0) { + ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); + } else { + ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + } + if (!is_type_u16_slice(slice_type)) { + Type *bt = base_type(slice_type); + Type *elem = bt->Slice.elem; + i64 sz = type_size_of(elem); + GB_ASSERT(sz > 0); + ptr = LLVMConstPointerCast(ptr, lb_type(m, alloc_type_pointer(elem))); + data_len /= sz; + } + + LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), data_len, true); + LLVMValueRef values[2] = {ptr, len}; + + lbValue res = {}; + res.value = llvm_const_named_struct(m, slice_type, values, 2); + res.type = slice_type; + return res; +} gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { From 9b5700225d3d2e22003251ed8578fb2ccdee1ec2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:33:33 +0100 Subject: [PATCH 087/160] Add string16_map.cpp --- src/string16_map.cpp | 538 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 src/string16_map.cpp diff --git a/src/string16_map.cpp b/src/string16_map.cpp new file mode 100644 index 000000000..c9e2eb817 --- /dev/null +++ b/src/string16_map.cpp @@ -0,0 +1,538 @@ +GB_STATIC_ASSERT(sizeof(MapIndex) == sizeof(u32)); + + +struct String16HashKey { + String16 string; + u32 hash; + + operator String16() const noexcept { + return this->string; + } + operator String16 const &() const noexcept { + return this->string; + } +}; +gb_internal gb_inline u32 string_hash(String16 const &s) { + u32 res = fnv32a(s.text, s.len*gb_size_of(u16)) & 0x7fffffff; + return res | (res == 0); +} + +gb_internal gb_inline String16HashKey string_hash_string(String16 const &s) { + String16HashKey hash_key = {}; + hash_key.hash = string_hash(s); + hash_key.string = s; + return hash_key; +} + + +#if 1 /* old string map */ + +template +struct String16MapEntry { + String16 key; + u32 hash; + MapIndex next; + T value; +}; + +template +struct String16Map { + MapIndex * hashes; + usize hashes_count; + String16MapEntry *entries; + u32 count; + u32 entries_capacity; +}; + + +template gb_internal void string16_map_init (String16Map *h, usize capacity = 16); +template gb_internal void string16_map_destroy (String16Map *h); + +template gb_internal T * string16_map_get (String16Map *h, String16HashKey const &key); +template gb_internal T & string16_map_must_get(String16Map *h, String16HashKey const &key); +template gb_internal void string16_map_set (String16Map *h, String16HashKey const &key, T const &value); + +// template gb_internal void string16_map_remove (String16Map *h, String16HashKey const &key); +template gb_internal void string16_map_clear (String16Map *h); +template gb_internal void string16_map_grow (String16Map *h); +template gb_internal void string16_map_reserve (String16Map *h, usize new_count); + +gb_internal gbAllocator string16_map_allocator(void) { + return heap_allocator(); +} + +template +gb_internal gb_inline void string16_map_init(String16Map *h, usize capacity) { + capacity = next_pow2_isize(capacity); + string16_map_reserve(h, capacity); +} + +template +gb_internal gb_inline void string16_map_destroy(String16Map *h) { + gb_free(string16_map_allocator(), h->hashes); + gb_free(string16_map_allocator(), h->entries); +} + + +template +gb_internal void string16_map__resize_hashes(String16Map *h, usize count) { + h->hashes_count = cast(u32)resize_array_raw(&h->hashes, string16_map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE); +} + + +template +gb_internal void string16_map__reserve_entries(String16Map *h, usize capacity) { + h->entries_capacity = cast(u32)resize_array_raw(&h->entries, string16_map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE); +} + + +template +gb_internal MapIndex string16_map__add_entry(String16Map *h, u32 hash, String16 const &key) { + String16MapEntry e = {}; + e.key = key; + e.hash = hash; + e.next = MAP_SENTINEL; + if (h->count+1 >= h->entries_capacity) { + string16_map__reserve_entries(h, gb_max(h->entries_capacity*2, 4)); + } + h->entries[h->count++] = e; + return cast(MapIndex)(h->count-1); +} + +template +gb_internal MapFindResult string16_map__find(String16Map *h, u32 hash, String16 const &key) { + MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; + if (h->hashes_count != 0) { + fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1)); + fr.entry_index = h->hashes[fr.hash_index]; + while (fr.entry_index != MAP_SENTINEL) { + auto *entry = &h->entries[fr.entry_index]; + if (entry->hash == hash && entry->key == key) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = entry->next; + } + } + return fr; +} + +template +gb_internal MapFindResult string16_map__find_from_entry(String16Map *h, String16MapEntry *e) { + MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; + if (h->hashes_count != 0) { + fr.hash_index = cast(MapIndex)(e->hash & (h->hashes_count-1)); + fr.entry_index = h->hashes[fr.hash_index]; + while (fr.entry_index != MAP_SENTINEL) { + auto *entry = &h->entries[fr.entry_index]; + if (entry == e) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = entry->next; + } + } + return fr; +} + +template +gb_internal b32 string16_map__full(String16Map *h) { + return 0.75f * h->hashes_count <= h->count; +} + +template +gb_inline void string16_map_grow(String16Map *h) { + isize new_count = gb_max(h->hashes_count<<1, 16); + string16_map_reserve(h, new_count); +} + + +template +gb_internal void string16_map_reset_entries(String16Map *h) { + for (u32 i = 0; i < h->hashes_count; i++) { + h->hashes[i] = MAP_SENTINEL; + } + for (isize i = 0; i < h->count; i++) { + MapFindResult fr; + String16MapEntry *e = &h->entries[i]; + e->next = MAP_SENTINEL; + fr = string16_map__find_from_entry(h, e); + if (fr.entry_prev == MAP_SENTINEL) { + h->hashes[fr.hash_index] = cast(MapIndex)i; + } else { + h->entries[fr.entry_prev].next = cast(MapIndex)i; + } + } +} + +template +gb_internal void string16_map_reserve(String16Map *h, usize cap) { + if (h->count*2 < h->hashes_count) { + return; + } + string16_map__reserve_entries(h, cap); + string16_map__resize_hashes(h, cap*2); + string16_map_reset_entries(h); +} + +template +gb_internal T *string16_map_get(String16Map *h, u32 hash, String16 const &key) { + MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; + if (h->hashes_count != 0) { + fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1)); + fr.entry_index = h->hashes[fr.hash_index]; + while (fr.entry_index != MAP_SENTINEL) { + auto *entry = &h->entries[fr.entry_index]; + if (entry->hash == hash && entry->key == key) { + return &entry->value; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = entry->next; + } + } + return nullptr; +} + + +template +gb_internal gb_inline T *string16_map_get(String16Map *h, String16HashKey const &key) { + return string16_map_get(h, key.hash, key.string); +} +template +gb_internal T &string16_map_must_get(String16Map *h, u32 hash, String16 const &key) { + isize index = string16_map__find(h, hash, key).entry_index; + GB_ASSERT(index != MAP_SENTINEL); + return h->entries[index].value; +} + +template +gb_internal T &string16_map_must_get(String16Map *h, String16HashKey const &key) { + return string16_map_must_get(h, key.hash, key.string); +} + +template +gb_internal void string16_map_set(String16Map *h, u32 hash, String16 const &key, T const &value) { + MapIndex index; + MapFindResult fr; + if (h->hashes_count == 0) { + string16_map_grow(h); + } + fr = string16_map__find(h, hash, key); + if (fr.entry_index != MAP_SENTINEL) { + index = fr.entry_index; + } else { + index = string16_map__add_entry(h, hash, key); + if (fr.entry_prev != MAP_SENTINEL) { + h->entries[fr.entry_prev].next = index; + } else { + h->hashes[fr.hash_index] = index; + } + } + h->entries[index].value = value; + + if (string16_map__full(h)) { + string16_map_grow(h); + } +} + +template +gb_internal gb_inline void string16_map_set(String16Map *h, String16HashKey const &key, T const &value) { + string16_map_set(h, key.hash, key.string, value); +} + + +template +gb_internal gb_inline void string16_map_clear(String16Map *h) { + h->count = 0; + for (u32 i = 0; i < h->hashes_count; i++) { + h->hashes[i] = MAP_SENTINEL; + } +} + + + +template +gb_internal String16MapEntry *begin(String16Map &m) noexcept { + return m.entries; +} +template +gb_internal String16MapEntry const *begin(String16Map const &m) noexcept { + return m.entries; +} + + +template +gb_internal String16MapEntry *end(String16Map &m) noexcept { + return m.entries + m.count; +} + +template +gb_internal String16MapEntry const *end(String16Map const &m) noexcept { + return m.entries + m.count; +} + +#else /* new string map */ + +template +struct StringMapEntry { + String key; + u32 hash; + T value; +}; + +template +struct StringMap { + String16MapEntry *entries; + u32 count; + u32 capacity; +}; + + +template gb_internal void string16_map_init (String16Map *h, usize capacity = 16); +template gb_internal void string16_map_destroy (String16Map *h); + +template gb_internal T * string16_map_get (String16Map *h, String16 const &key); +template gb_internal T * string16_map_get (String16Map *h, String16HashKey const &key); + +template gb_internal T & string16_map_must_get(String16Map *h, String16 const &key); +template gb_internal T & string16_map_must_get(String16Map *h, String16HashKey const &key); + +template gb_internal void string16_map_set (String16Map *h, String16 const &key, T const &value); +template gb_internal void string16_map_set (String16Map *h, String16HashKey const &key, T const &value); + +// template gb_internal void string16_map_remove (String16Map *h, String16HashKey const &key); +template gb_internal void string16_map_clear (String16Map *h); +template gb_internal void string16_map_grow (String16Map *h); +template gb_internal void string16_map_reserve (String16Map *h, usize new_count); + +gb_internal gbAllocator string16_map_allocator(void) { + return heap_allocator(); +} + +template +gb_internal gb_inline void string16_map_init(String16Map *h, usize capacity) { + capacity = next_pow2_isize(capacity); + string16_map_reserve(h, capacity); +} + +template +gb_internal gb_inline void string16_map_destroy(String16Map *h) { + gb_free(string16_map_allocator(), h->entries); +} + + +template +gb_internal void string16_map__insert(String16Map *h, u32 hash, String16 const &key, T const &value) { + if (h->count+1 >= h->capacity) { + string16_map_grow(h); + } + GB_ASSERT(h->count+1 < h->capacity); + + u32 mask = h->capacity-1; + MapIndex index = hash & mask; + MapIndex original_index = index; + do { + auto *entry = h->entries+index; + if (entry->hash == 0) { + entry->key = key; + entry->hash = hash; + entry->value = value; + + h->count += 1; + return; + } + index = (index+1)&mask; + } while (index != original_index); + + GB_PANIC("Full map"); +} + +template +gb_internal b32 string16_map__full(String16Map *h) { + return 0.75f * h->count <= h->capacity; +} + +template +gb_inline void string16_map_grow(String16Map *h) { + isize new_capacity = gb_max(h->capacity<<1, 16); + string16_map_reserve(h, new_capacity); +} + + +template +gb_internal void string16_map_reserve(String16Map *h, usize cap) { + if (cap < h->capacity) { + return; + } + cap = next_pow2_isize(cap); + + String16Map new_h = {}; + new_h.count = 0; + new_h.capacity = cast(u32)cap; + new_h.entries = gb_alloc_array(string16_map_allocator(), String16MapEntry, new_h.capacity); + + if (h->count) { + for (u32 i = 0; i < h->capacity; i++) { + auto *entry = h->entries+i; + if (entry->hash) { + string16_map__insert(&new_h, entry->hash, entry->key, entry->value); + } + } + } + string16_map_destroy(h); + *h = new_h; +} + +template +gb_internal T *string16_map_get(String16Map *h, u32 hash, String16 const &key) { + if (h->count == 0) { + return nullptr; + } + u32 mask = (h->capacity-1); + u32 index = hash & mask; + u32 original_index = index; + do { + auto *entry = h->entries+index; + u32 curr_hash = entry->hash; + if (curr_hash == 0) { + // NOTE(bill): no found, but there isn't any key removal for this hash map + return nullptr; + } else if (curr_hash == hash && entry->key == key) { + return &entry->value; + } + index = (index+1) & mask; + } while (original_index != index); + return nullptr; +} + + +template +gb_internal gb_inline T *string16_map_get(String16Map *h, String16HashKey const &key) { + return string16_map_get(h, key.hash, key.string); +} + +template +gb_internal gb_inline T *string16_map_get(String16Map *h, String16 const &key) { + return string16_map_get(h, string_hash(key), key); +} + +template +gb_internal T &string16_map_must_get(String16Map *h, u32 hash, String16 const &key) { + T *found = string16_map_get(h, hash, key); + GB_ASSERT(found != nullptr); + return *found; +} + +template +gb_internal T &string16_map_must_get(String16Map *h, String16HashKey const &key) { + return string16_map_must_get(h, key.hash, key.string); +} + +template +gb_internal gb_inline T &string16_map_must_get(String16Map *h, String16 const &key) { + return string16_map_must_get(h, string_hash(key), key); +} + +template +gb_internal void string16_map_set(String16Map *h, u32 hash, String16 const &key, T const &value) { + if (h->count == 0) { + string16_map_grow(h); + } + auto *found = string16_map_get(h, hash, key); + if (found) { + *found = value; + return; + } + string16_map__insert(h, hash, key, value); +} + +template +gb_internal gb_inline void string16_map_set(String16Map *h, String16 const &key, T const &value) { + string16_map_set(h, string_hash_string(key), value); +} + +template +gb_internal gb_inline void string16_map_set(String16Map *h, String16HashKey const &key, T const &value) { + string16_map_set(h, key.hash, key.string, value); +} + + +template +gb_internal gb_inline void string16_map_clear(String16Map *h) { + h->count = 0; + gb_zero_array(h->entries, h->capacity); +} + + +template +struct StringMapIterator { + String16Map *map; + MapIndex index; + + StringMapIterator &operator++() noexcept { + for (;;) { + ++index; + if (map->capacity == index) { + return *this; + } + String16MapEntry *entry = map->entries+index; + if (entry->hash != 0) { + return *this; + } + } + } + + bool operator==(StringMapIterator const &other) const noexcept { + return this->map == other->map && this->index == other->index; + } + + operator String16MapEntry *() const { + return map->entries+index; + } +}; + + +template +gb_internal StringMapIterator end(String16Map &m) noexcept { + return StringMapIterator{&m, m.capacity}; +} + +template +gb_internal StringMapIterator const end(String16Map const &m) noexcept { + return StringMapIterator{&m, m.capacity}; +} + + + +template +gb_internal StringMapIterator begin(String16Map &m) noexcept { + if (m.count == 0) { + return end(m); + } + + MapIndex index = 0; + while (index < m.capacity) { + if (m.entries[index].hash) { + break; + } + index++; + } + return StringMapIterator{&m, index}; +} +template +gb_internal StringMapIterator const begin(String16Map const &m) noexcept { + if (m.count == 0) { + return end(m); + } + + MapIndex index = 0; + while (index < m.capacity) { + if (m.entries[index].hash) { + break; + } + index++; + } + return StringMapIterator{&m, index}; +} + +#endif \ No newline at end of file From c631a8eff59f8c4f76b1a39d1ca04b9fdf0cb85c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:47:03 +0100 Subject: [PATCH 088/160] os2 internals -> (c)string16 --- core/os/os2/dir_windows.odin | 14 ++---- core/os/os2/env_windows.odin | 2 +- core/os/os2/file_windows.odin | 70 ++++++++++++++++++++++-------- core/os/os2/path_windows.odin | 8 ++-- core/os/os2/process_windows.odin | 8 ++-- core/os/os2/stat_windows.odin | 14 +++--- core/os/os2/temp_file_windows.odin | 4 +- core/os/os2/user_windows.odin | 3 +- core/sys/windows/kernel32.odin | 2 +- core/sys/windows/types.odin | 2 +- 10 files changed, 77 insertions(+), 50 deletions(-) diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin index 4cf1f8396..6c754a677 100644 --- a/core/os/os2/dir_windows.odin +++ b/core/os/os2/dir_windows.odin @@ -16,7 +16,7 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al } temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - path := concatenate({base_path, `\`, win32_wstring_to_utf8(raw_data(d.cFileName[:]), temp_allocator) or_else ""}, allocator) or_return + path := concatenate({base_path, `\`, win32_wstring_to_utf8(cstring16(raw_data(d.cFileName[:])), temp_allocator) or_else ""}, allocator) or_return handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0) defer win32.CloseHandle(handle) @@ -107,15 +107,7 @@ _read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) { return } - wpath: []u16 - { - i := 0 - for impl.wname[i] != 0 { - i += 1 - } - wpath = impl.wname[:i] - } - + wpath := string16(impl.wname) temp_allocator := TEMP_ALLOCATOR_GUARD({}) wpath_search := make([]u16, len(wpath)+3, temp_allocator) @@ -124,7 +116,7 @@ _read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) { wpath_search[len(wpath)+1] = '*' wpath_search[len(wpath)+2] = 0 - it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data) + it.impl.find_handle = win32.FindFirstFileW(cstring16(raw_data(wpath_search)), &it.impl.find_data) if it.impl.find_handle == win32.INVALID_HANDLE_VALUE { read_directory_iterator_set_error(it, impl.name, _get_platform_error()) return diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index 55b2bb5ee..d389f8860 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -31,7 +31,7 @@ _lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: return "", false } - value = win32_utf16_to_utf8(b[:n], allocator) or_else "" + value = win32_utf16_to_utf8(string16(b[:n]), allocator) or_else "" found = true return } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 1134e765c..d63702709 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -247,7 +247,7 @@ _destroy :: proc(f: ^File_Impl) -> Error { } a := f.allocator - err0 := free(f.wname, a) + err0 := free(rawptr(f.wname), a) err1 := delete(f.name, a) err2 := free(f, a) err3 := delete(f.r_buf, a) @@ -661,7 +661,7 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st } - handle := _open_sym_link(raw_data(p)) or_return + handle := _open_sym_link(cstring16(raw_data(p))) or_return defer win32.CloseHandle(handle) n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS) @@ -672,7 +672,7 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) buf := make([]u16, n+1, temp_allocator) - n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS) + n = win32.GetFinalPathNameByHandleW(handle, cstring16(raw_data(buf)), u32(len(buf)), win32.VOLUME_NAME_DOS) if n == 0 { return "", _get_platform_error() } @@ -713,7 +713,7 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er switch rdb.ReparseTag { case win32.IO_REPARSE_TAG_SYMLINK: rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest) - pb := win32.wstring(&rb.PathBuffer) + pb := ([^]u16)(&rb.PathBuffer) pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0 p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength] if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 { @@ -723,7 +723,7 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er case win32.IO_REPARSE_TAG_MOUNT_POINT: rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest) - pb := win32.wstring(&rb.PathBuffer) + pb := ([^]u16)(&rb.PathBuffer) pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0 p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength] return _normalize_link_path(p, allocator) @@ -874,8 +874,8 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, @(private="package", require_results) -win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: [^]u16, err: runtime.Allocator_Error) { - ws = raw_data(win32_utf8_to_utf16(s, allocator) or_return) +win32_utf8_to_wstring :: proc(s: string, allocator: runtime.Allocator) -> (ws: cstring16, err: runtime.Allocator_Error) { + ws = cstring16(raw_data(win32_utf8_to_utf16(s, allocator) or_return)) return } @@ -909,24 +909,26 @@ win32_utf8_to_utf16 :: proc(s: string, allocator: runtime.Allocator) -> (ws: []u } @(private="package", require_results) -win32_wstring_to_utf8 :: proc(s: [^]u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { - if s == nil || s[0] == 0 { +win32_wstring_to_utf8 :: proc(s: cstring16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { + if s == nil || s == "" { return "", nil } - n := 0 - for s[n] != 0 { - n += 1 - } - return win32_utf16_to_utf8(s[:n], allocator) + return win32_utf16_to_utf8(string16(s), allocator) +} + +@(private="package") +win32_utf16_to_utf8 :: proc{ + win32_utf16_string16_to_utf8, + win32_utf16_u16_to_utf8 } @(private="package", require_results) -win32_utf16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { +win32_utf16_string16_to_utf8 :: proc(s: string16, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { if len(s) == 0 { return } - n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), nil, 0, nil, nil) + n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), nil, 0, nil, nil) if n == 0 { return } @@ -938,7 +940,41 @@ win32_utf16_to_utf8 :: proc(s: []u16, allocator: runtime.Allocator) -> (res: str // will not be null terminated. text := make([]byte, n, allocator) or_return - n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), raw_data(text), n, nil, nil) + n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), raw_data(text), n, nil, nil) + if n1 == 0 { + delete(text, allocator) + return + } + + for i in 0.. (res: string, err: runtime.Allocator_Error) { + if len(s) == 0 { + return + } + + n := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), nil, 0, nil, nil) + if n == 0 { + return + } + + // If N < 0 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 be null terminated. + // If N > 0 it assumes the wide string is not null terminated and the resulting string + // will not be null terminated. + text := make([]byte, n, allocator) or_return + + n1 := win32.WideCharToMultiByte(win32.CP_UTF8, win32.WC_ERR_INVALID_CHARS, cstring16(raw_data(s)), i32(len(s)), raw_data(text), n, nil, nil) if n1 == 0 { delete(text, allocator) return diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index c2e51040f..e0a00b07a 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -91,11 +91,11 @@ _remove_all :: proc(path: string) -> Error { nil, win32.FO_DELETE, dir, - &empty[0], + cstring16(&empty[0]), win32.FOF_NOCONFIRMATION | win32.FOF_NOERRORUI | win32.FOF_SILENT, false, nil, - &empty[0], + cstring16(&empty[0]), } res := win32.SHFileOperationW(&file_op) if res != 0 { @@ -303,13 +303,13 @@ _get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absol } temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) rel_utf16 := win32.utf8_to_utf16(rel, temp_allocator) - n := win32.GetFullPathNameW(raw_data(rel_utf16), 0, nil, nil) + n := win32.GetFullPathNameW(cstring16(raw_data(rel_utf16)), 0, nil, nil) if n == 0 { return "", Platform_Error(win32.GetLastError()) } buf := make([]u16, n, temp_allocator) or_return - n = win32.GetFullPathNameW(raw_data(rel_utf16), u32(n), raw_data(buf), nil) + n = win32.GetFullPathNameW(cstring16(raw_data(rel_utf16)), u32(n), cstring16(raw_data(buf)), nil) if n == 0 { return "", Platform_Error(win32.GetLastError()) } diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin index 199e5ad74..990da6616 100644 --- a/core/os/os2/process_windows.odin +++ b/core/os/os2/process_windows.odin @@ -175,7 +175,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator info.fields += {.Command_Line} } if .Command_Args in selection { - info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return + info.command_args = _parse_command_line(cstring16(raw_data(cmdline_w)), allocator) or_return info.fields += {.Command_Args} } } @@ -286,7 +286,7 @@ _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields info.fields += {.Command_Line} } if .Command_Args in selection { - info.command_args = _parse_command_line(raw_data(cmdline_w), allocator) or_return + info.command_args = _parse_command_line(cstring16(raw_data(cmdline_w)), allocator) or_return info.fields += {.Command_Args} } } @@ -610,7 +610,7 @@ _process_exe_by_pid :: proc(pid: int, allocator: runtime.Allocator) -> (exe_path err =_get_platform_error() return } - return win32_wstring_to_utf8(raw_data(entry.szExePath[:]), allocator) + return win32_wstring_to_utf8(cstring16(raw_data(entry.szExePath[:])), allocator) } _get_process_user :: proc(process_handle: win32.HANDLE, allocator: runtime.Allocator) -> (full_username: string, err: Error) { @@ -650,7 +650,7 @@ _get_process_user :: proc(process_handle: win32.HANDLE, allocator: runtime.Alloc return strings.concatenate({domain, "\\", username}, allocator) } -_parse_command_line :: proc(cmd_line_w: [^]u16, allocator: runtime.Allocator) -> (argv: []string, err: Error) { +_parse_command_line :: proc(cmd_line_w: cstring16, allocator: runtime.Allocator) -> (argv: []string, err: Error) { argc: i32 argv_w := win32.CommandLineToArgvW(cmd_line_w, &argc) if argv_w == nil { diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 3cdc80405..3dee42be6 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -49,12 +49,12 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path p := win32_utf8_to_utf16(name, temp_allocator) or_return - n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) + n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil) if n == 0 { return "", _get_platform_error() } buf := make([]u16, n+1, temp_allocator) - n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) if n == 0 { return "", _get_platform_error() } @@ -140,8 +140,8 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) buf := make([]u16, max(n, 260)+1, temp_allocator) - n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) - return _cleanpath_from_buf(buf[:n], allocator) + n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0) + return _cleanpath_from_buf(string16(buf[:n]), allocator) } _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { @@ -158,12 +158,12 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({}) buf := make([]u16, max(n, 260)+1, temp_allocator) - n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0) return _cleanpath_strip_prefix(buf[:n]), nil } -_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { - buf := buf +_cleanpath_from_buf :: proc(buf: string16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { + buf := transmute([]u16)buf buf = _cleanpath_strip_prefix(buf) return win32_utf16_to_utf8(buf, allocator) } diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 9d75ef99d..91ea284a1 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -12,12 +12,12 @@ _temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Er temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) b := make([]u16, max(win32.MAX_PATH, n), temp_allocator) - n = win32.GetTempPathW(u32(len(b)), raw_data(b)) + n = win32.GetTempPathW(u32(len(b)), cstring16(raw_data(b))) if n == 3 && b[1] == ':' && b[2] == '\\' { } else if n > 0 && b[n-1] == '\\' { n -= 1 } - return win32_utf16_to_utf8(b[:n], allocator) + return win32_utf16_to_utf8(string16(b[:n]), allocator) } diff --git a/core/os/os2/user_windows.odin b/core/os/os2/user_windows.odin index d68f933ce..75d0ba6ac 100644 --- a/core/os/os2/user_windows.odin +++ b/core/os/os2/user_windows.odin @@ -74,6 +74,5 @@ _get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime. return "", .Invalid_Path } - dir, _ = win32.wstring_to_utf8(path_w, -1, allocator) - return + return win32_wstring_to_utf8(cstring16(path_w), allocator) } \ No newline at end of file diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 76f2897ac..114e70b41 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -258,7 +258,7 @@ foreign kernel32 { ) -> BOOL --- CreateProcessW :: proc( lpApplicationName: LPCWSTR, - lpCommandLine: LPWSTR, + lpCommandLine: LPCWSTR, lpProcessAttributes: LPSECURITY_ATTRIBUTES, lpThreadAttributes: LPSECURITY_ATTRIBUTES, bInheritHandles: BOOL, diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index be16d2fdd..200b0d62b 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -1698,7 +1698,7 @@ NM_FONTCHANGED :: NM_OUTOFMEMORY-22 NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct NM_TVSTATEIMAGECHANGING :: NM_OUTOFMEMORY-23 // uses NMTVSTATEIMAGECHANGING struct, defined after HTREEITEM -PCZZWSTR :: ^WCHAR +PCZZWSTR :: cstring16 SHFILEOPSTRUCTW :: struct { hwnd: HWND, From 7c281a9614d815fda4c3ac5fd53ce940662c3264 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:51:08 +0100 Subject: [PATCH 089/160] Fix [^]u16 <-> cstring16 conversions --- src/llvm_backend_expr.cpp | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 8ad6a5a1c..f58e2d9ac 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2330,6 +2330,21 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } + // [^]u16 <-> cstring16 + if (is_type_u16_multi_ptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, t); + } + if (is_type_cstring16(src) && is_type_u16_multi_ptr(dst)) { + return lb_emit_transmute(p, value, t); + } + if (is_type_u16_ptr(src) && is_type_cstring16(dst)) { + return lb_emit_transmute(p, value, t); + } + if (is_type_cstring16(src) && is_type_u16_ptr(dst)) { + return lb_emit_transmute(p, value, t); + } + + // []u16 <-> string16 if (is_type_u16_slice(src) && is_type_string16(dst)) { return lb_emit_transmute(p, value, t); @@ -2753,7 +2768,53 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left return lb_compare_records(p, op_kind, left, right, b); } + + if (is_type_string16(a) || is_type_cstring16(a)) { + if (is_type_cstring16(a) && is_type_cstring16(b)) { + left = lb_emit_conv(p, left, t_cstring16); + right = lb_emit_conv(p, right, t_cstring16); + char const *runtime_procedure = nullptr; + switch (op_kind) { + case Token_CmpEq: runtime_procedure = "cstring16_eq"; break; + case Token_NotEq: runtime_procedure = "cstring16_ne"; break; + case Token_Lt: runtime_procedure = "cstring16_lt"; break; + case Token_Gt: runtime_procedure = "cstring16_gt"; break; + case Token_LtEq: runtime_procedure = "cstring16_le"; break; + case Token_GtEq: runtime_procedure = "cstring16_ge"; break; + } + GB_ASSERT(runtime_procedure != nullptr); + + auto args = array_make(permanent_allocator(), 2); + args[0] = left; + args[1] = right; + return lb_emit_runtime_call(p, runtime_procedure, args); + } + + + if (is_type_cstring16(a) ^ is_type_cstring16(b)) { + left = lb_emit_conv(p, left, t_string); + right = lb_emit_conv(p, right, t_string); + } + + char const *runtime_procedure = nullptr; + switch (op_kind) { + case Token_CmpEq: runtime_procedure = "string16_eq"; break; + case Token_NotEq: runtime_procedure = "string16_ne"; break; + case Token_Lt: runtime_procedure = "string16_lt"; break; + case Token_Gt: runtime_procedure = "string16_gt"; break; + case Token_LtEq: runtime_procedure = "string16_le"; break; + case Token_GtEq: runtime_procedure = "string16_ge"; break; + } + GB_ASSERT(runtime_procedure != nullptr); + + auto args = array_make(permanent_allocator(), 2); + args[0] = left; + args[1] = right; + return lb_emit_runtime_call(p, runtime_procedure, args); + } + if (is_type_string(a)) { + if (is_type_cstring(a) && is_type_cstring(b)) { left = lb_emit_conv(p, left, t_cstring); right = lb_emit_conv(p, right, t_cstring); From 5aec40e3e059e4ddbf8a004d61ba8d4883e6d281 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 12:55:05 +0100 Subject: [PATCH 090/160] Remove unneeded uses of `intrinsics.constant_utf16_cstring` --- core/os/os2/file_windows.odin | 2 +- tests/core/sys/windows/test_kernel32.odin | 2 +- tests/core/sys/windows/util.odin | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index d63702709..304cf99ac 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -919,7 +919,7 @@ win32_wstring_to_utf8 :: proc(s: cstring16, allocator: runtime.Allocator) -> (re @(private="package") win32_utf16_to_utf8 :: proc{ win32_utf16_string16_to_utf8, - win32_utf16_u16_to_utf8 + win32_utf16_u16_to_utf8, } @(private="package", require_results) diff --git a/tests/core/sys/windows/test_kernel32.odin b/tests/core/sys/windows/test_kernel32.odin index f6a88c769..7df16113a 100644 --- a/tests/core/sys/windows/test_kernel32.odin +++ b/tests/core/sys/windows/test_kernel32.odin @@ -17,7 +17,7 @@ lcid_to_local :: proc(t: ^testing.T) { exp :: "en-US" testing.expectf(t, str == exp, "%v (should be: %v)", str, exp) - cc2 := win32.LocaleNameToLCID(L(exp), 0) + cc2 := win32.LocaleNameToLCID(exp, 0) testing.expectf(t, cc2 == 0x0409, "%#x (should be: %#x)", u32(cc2), 0x0409) //fmt.printfln("%0X", lcid) diff --git a/tests/core/sys/windows/util.odin b/tests/core/sys/windows/util.odin index 0201395f6..6e4bef9b7 100644 --- a/tests/core/sys/windows/util.odin +++ b/tests/core/sys/windows/util.odin @@ -12,11 +12,11 @@ UTF16_Vector :: struct { utf16_vectors := []UTF16_Vector{ { - intrinsics.constant_utf16_cstring("Hellope, World!"), + "Hellope, World!", "Hellope, World!", }, { - intrinsics.constant_utf16_cstring("Hellope\x00, World!"), + "Hellope\x00, World!", "Hellope", }, } From e049dde582ac6525b9ce470a4af89075408c8de9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 13:01:58 +0100 Subject: [PATCH 091/160] Fix tests for UTF-16 strings --- core/sys/windows/types.odin | 2 +- tests/core/sys/windows/test_kernel32.odin | 2 +- tests/core/sys/windows/test_ole32.odin | 4 ++-- tests/core/sys/windows/util.odin | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 200b0d62b..d5c9b1d20 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -145,7 +145,7 @@ LPSTR :: ^CHAR LPWSTR :: ^WCHAR OLECHAR :: WCHAR BSTR :: ^OLECHAR -LPOLESTR :: ^OLECHAR +LPOLESTR :: cstring16 LPCOLESTR :: LPCSTR LPFILETIME :: ^FILETIME LPWSABUF :: ^WSABUF diff --git a/tests/core/sys/windows/test_kernel32.odin b/tests/core/sys/windows/test_kernel32.odin index 7df16113a..15f3b5173 100644 --- a/tests/core/sys/windows/test_kernel32.odin +++ b/tests/core/sys/windows/test_kernel32.odin @@ -12,7 +12,7 @@ lcid_to_local :: proc(t: ^testing.T) { cc := win32.LCIDToLocaleName(lcid, &wname[0], len(wname) - 1, 0) testing.expectf(t, cc == 6, "%#x (should be: %#x)", u32(cc), 6) if cc == 0 {return} - str, err := win32.wstring_to_utf8(win32.wstring(&wname), int(cc)) + str, err := win32.wstring_to_utf8(win32.wstring(&wname[0]), int(cc)) testing.expectf(t, err == .None, "%v (should be: %x)", err, 0) exp :: "en-US" testing.expectf(t, str == exp, "%v (should be: %v)", str, exp) diff --git a/tests/core/sys/windows/test_ole32.odin b/tests/core/sys/windows/test_ole32.odin index 8be231e1f..a0a2590b8 100644 --- a/tests/core/sys/windows/test_ole32.odin +++ b/tests/core/sys/windows/test_ole32.odin @@ -9,7 +9,7 @@ import "core:testing" string_from_clsid :: proc(t: ^testing.T) { p: win32.LPOLESTR hr := win32.StringFromCLSID(win32.CLSID_FileOpenDialog, &p) - defer if p != nil {win32.CoTaskMemFree(p)} + defer if p != nil {win32.CoTaskMemFree(rawptr(p))} testing.expectf(t, win32.SUCCEEDED(hr), "%x (should be: %x)", u32(hr), 0) testing.expectf(t, p != nil, "%v is nil", p) @@ -33,7 +33,7 @@ clsid_from_string :: proc(t: ^testing.T) { string_from_iid :: proc(t: ^testing.T) { p: win32.LPOLESTR hr := win32.StringFromIID(win32.IID_IFileDialog, &p) - defer if p != nil {win32.CoTaskMemFree(p)} + defer if p != nil {win32.CoTaskMemFree(rawptr(p))} testing.expectf(t, win32.SUCCEEDED(hr), "%x (should be: %x)", u32(hr), 0) testing.expectf(t, p != nil, "%v is nil", p) diff --git a/tests/core/sys/windows/util.odin b/tests/core/sys/windows/util.odin index 6e4bef9b7..e2ab9cde0 100644 --- a/tests/core/sys/windows/util.odin +++ b/tests/core/sys/windows/util.odin @@ -27,7 +27,8 @@ utf16_to_utf8_buf_test :: proc(t: ^testing.T) { buf := make([]u8, len(test.ustr)) defer delete(buf) - res := win32.utf16_to_utf8_buf(buf[:], test.wstr[:len(test.ustr)]) + wstr := string16(test.wstr) + res := win32.utf16_to_utf8_buf(buf[:], transmute([]u16)wstr) testing.expect_value(t, res, test.ustr) } } \ No newline at end of file From dca9bf0b0c8a3ad2ac6854c901d99868d3a296d5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 13:11:34 +0100 Subject: [PATCH 092/160] Fix string16 literal length set in LLVM --- src/llvm_backend_const.cpp | 13 ++++++++++--- src/llvm_backend_general.cpp | 4 ++-- src/string.cpp | 2 -- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index cba0000cd..e64be49f2 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -782,9 +782,12 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb lbValue res = {}; res.type = default_type(original_type); + isize len = value.value_string.len; + if (is_type_string16(res.type) || is_type_cstring16(res.type)) { TEMPORARY_ALLOCATOR_GUARD(); String16 s16 = string_to_string16(temporary_allocator(), value.value_string); + len = s16.len; ptr = lb_find_or_add_entity_string16_ptr(m, s16, custom_link_section); } else { ptr = lb_find_or_add_entity_string_ptr(m, value.value_string, custom_link_section); @@ -797,10 +800,14 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb if (is_type_cstring(res.type) || is_type_cstring16(res.type)) { res.value = ptr; } else { - if (value.value_string.len == 0) { - ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + if (len == 0) { + if (is_type_string16(res.type)) { + ptr = LLVMConstNull(lb_type(m, t_u16_ptr)); + } else { + ptr = LLVMConstNull(lb_type(m, t_u8_ptr)); + } } - LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true); + LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), len, true); GB_ASSERT(is_type_string(original_type)); if (is_type_string16(res.type)) { diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 064d0ef39..d84b8302b 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2758,7 +2758,7 @@ gb_internal LLVMValueRef lb_find_or_add_entity_string16_ptr(lbModule *m, String1 LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); lb_make_global_private_const(global_data); - LLVMSetAlignment(global_data, 1); + LLVMSetAlignment(global_data, 2); LLVMValueRef ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2); if (!custom_link_section) { @@ -2855,7 +2855,7 @@ gb_internal lbValue lb_find_or_add_entity_string16_slice_with_type(lbModule *m, LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name); LLVMSetInitializer(global_data, data); lb_make_global_private_const(global_data); - LLVMSetAlignment(global_data, 1); + LLVMSetAlignment(global_data, 2); i64 data_len = str.len; LLVMValueRef ptr = nullptr; diff --git a/src/string.cpp b/src/string.cpp index 8cc0e93f3..2087a5fee 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -658,7 +658,6 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons -// TODO(bill): Make this non-windows specific gb_internal String16 string_to_string16(gbAllocator a, String s) { int len, len1; u16 *text; @@ -680,7 +679,6 @@ gb_internal String16 string_to_string16(gbAllocator a, String s) { return make_string16(nullptr, 0); } text[len] = 0; - return make_string16(text, len); } From 86bd9186f9913f4b98f73d9f344d5277acf67fc0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 13:17:31 +0100 Subject: [PATCH 093/160] Fix `string16 != ""` comparison --- src/llvm_backend_expr.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index f58e2d9ac..f9007d960 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1559,16 +1559,24 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { return lb_emit_conv(p, cmp, type); } else if (lb_is_empty_string_constant(be->right) && !is_type_union(be->left->tav.type)) { // `x == ""` or `x != ""` + Type *str_type = t_string; + if (is_type_string16(be->left->tav.type)) { + str_type = t_string16; + } lbValue s = lb_build_expr(p, be->left); - s = lb_emit_conv(p, s, t_string); + s = lb_emit_conv(p, s, str_type); lbValue len = lb_string_len(p, s); lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0)); Type *type = default_type(tv.type); return lb_emit_conv(p, cmp, type); } else if (lb_is_empty_string_constant(be->left) && !is_type_union(be->right->tav.type)) { // `"" == x` or `"" != x` + Type *str_type = t_string; + if (is_type_string16(be->right->tav.type)) { + str_type = t_string16; + } lbValue s = lb_build_expr(p, be->right); - s = lb_emit_conv(p, s, t_string); + s = lb_emit_conv(p, s, str_type); lbValue len = lb_string_len(p, s); lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0)); Type *type = default_type(tv.type); @@ -2792,8 +2800,8 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left if (is_type_cstring16(a) ^ is_type_cstring16(b)) { - left = lb_emit_conv(p, left, t_string); - right = lb_emit_conv(p, right, t_string); + left = lb_emit_conv(p, left, t_string16); + right = lb_emit_conv(p, right, t_string16); } char const *runtime_procedure = nullptr; From f8d24923fdd701c0a08b8ffb7200cebdc9820b76 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Aug 2025 13:23:20 +0100 Subject: [PATCH 094/160] Fix `cstring != ""` --- src/llvm_backend_expr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index f9007d960..5425572c7 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1560,7 +1560,7 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { } else if (lb_is_empty_string_constant(be->right) && !is_type_union(be->left->tav.type)) { // `x == ""` or `x != ""` Type *str_type = t_string; - if (is_type_string16(be->left->tav.type)) { + if (is_type_string16(be->left->tav.type) || is_type_cstring16(be->left->tav.type)) { str_type = t_string16; } lbValue s = lb_build_expr(p, be->left); @@ -1572,7 +1572,7 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { } else if (lb_is_empty_string_constant(be->left) && !is_type_union(be->right->tav.type)) { // `"" == x` or `"" != x` Type *str_type = t_string; - if (is_type_string16(be->right->tav.type)) { + if (is_type_string16(be->right->tav.type) || is_type_cstring16(be->right->tav.type)) { str_type = t_string16; } lbValue s = lb_build_expr(p, be->right); From 25389ed086d8c12adf3fff9b5db952749c74a981 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 2 Aug 2025 15:22:23 +0200 Subject: [PATCH 095/160] thread: set stack size to rlimit --- core/thread/thread_unix.odin | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 1431442a9..1db32657e 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -81,8 +81,13 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { } defer posix.pthread_attr_destroy(&attrs) - // NOTE(tetra, 2019-11-01): These only fail if their argument is invalid. + stacksize: posix.rlimit + if res := posix.getrlimit(.STACK, &stacksize); res == .OK && stacksize.rlim_cur > 0 { + _ = posix.pthread_attr_setstacksize(&attrs, uint(stacksize.rlim_cur)) + } + res: posix.Errno + // NOTE(tetra, 2019-11-01): These only fail if their argument is invalid. res = posix.pthread_attr_setdetachstate(&attrs, .CREATE_JOINABLE) assert(res == nil) when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { From a9e9e88f0629afd7d34022c45197ac6941820f81 Mon Sep 17 00:00:00 2001 From: Wesley Kerfoot Date: Sun, 3 Aug 2025 11:30:09 -0400 Subject: [PATCH 096/160] add missing xlib functions for getting and setting text properties --- vendor/x11/xlib/xlib_procs.odin | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vendor/x11/xlib/xlib_procs.odin b/vendor/x11/xlib/xlib_procs.odin index 2cd4e0f83..f5ac373ae 100644 --- a/vendor/x11/xlib/xlib_procs.odin +++ b/vendor/x11/xlib/xlib_procs.odin @@ -389,6 +389,18 @@ foreign xlib { requestor: Window, time: Time, ) --- + GetTextProperty :: proc( + display: ^Display, + window: Window, + text_prop_return: ^XTextProperty, + property: Atom, + ) -> Status --- + SetTextProperty :: proc( + display: ^Display, + window: Window, + text_prop: ^XTextProperty, + property: Atom, + ) --- // Creating and freeing pixmaps CreatePixmap :: proc( display: ^Display, From 70818030f65be53b3b5bd966a088f726b2db9789 Mon Sep 17 00:00:00 2001 From: A1029384756 Date: Mon, 4 Aug 2025 00:27:03 -0400 Subject: [PATCH 097/160] [nightly] added linux arm build [nightly] docker container for linux arm [nightly] removed setup-alpine for arm [nightly] docker container for all linux + linux arm dep for upload [nightly] x86 remove arm reference [nightly] final fixes --- .github/workflows/nightly.yml | 69 ++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index eb67eb209..6826239e8 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -47,20 +47,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: jirutka/setup-alpine@v1 - with: - branch: edge - - name: (Linux) Download LLVM + - name: (Linux) Download LLVM and Build Odin run: | - apk add --no-cache \ - musl-dev llvm20-dev clang20 git mold lz4 \ - libxml2-static llvm20-static zlib-static zstd-static \ - make - shell: alpine.sh --root {0} - - name: build odin - # NOTE: this build does slow compile times because of musl - run: ci/build_linux_static.sh - shell: alpine.sh {0} + docker run --rm -v "$PWD:/src" -w /src alpine sh -c ' + apk add --no-cache \ + musl-dev llvm20-dev clang20 git mold lz4 \ + libxml2-static llvm20-static zlib-static zstd-static \ + make && + ./ci/build_linux_static.sh + ' - name: Odin run run: ./odin run examples/demo - name: Copy artifacts @@ -85,6 +80,45 @@ jobs: with: name: linux_artifacts path: dist.tar.gz + build_linux_arm: + name: Linux ARM Build + if: github.repository == 'odin-lang/Odin' + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + - name: (Linux ARM) Download LLVM and Build Odin + run: | + docker run --rm -v "$PWD:/src" -w /src arm64v8/alpine sh -c ' + apk add --no-cache \ + musl-dev llvm20-dev clang20 git mold lz4 \ + libxml2-static llvm20-static zlib-static zstd-static \ + make && + ./ci/build_linux_static.sh + ' + - name: Odin run + run: ./odin run examples/demo + - name: Copy artifacts + run: | + FILE="odin-linux-arm64-nightly+$(date -I)" + mkdir $FILE + cp odin $FILE + cp LICENSE $FILE + cp -r shared $FILE + cp -r base $FILE + cp -r core $FILE + cp -r vendor $FILE + cp -r examples $FILE + # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 + tar -czvf dist.tar.gz $FILE + - name: Odin run + run: | + FILE="odin-linux-arm64-nightly+$(date -I)" + $FILE/odin run examples/demo + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: linux_arm_artifacts + path: dist.tar.gz build_macos: name: MacOS Build if: github.repository == 'odin-lang/Odin' @@ -163,7 +197,7 @@ jobs: path: dist.tar.gz upload_b2: runs-on: [ubuntu-latest] - needs: [build_windows, build_macos, build_macos_arm, build_linux] + needs: [build_windows, build_macos, build_macos_arm, build_linux, build_linux_arm] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -192,6 +226,12 @@ jobs: name: linux_artifacts path: linux_artifacts + - name: Download Ubuntu ARM artifacts + uses: actions/download-artifact@v4.1.7 + with: + name: linux_arm_artifacts + path: linux_arm_artifacts + - name: Download macOS artifacts uses: actions/download-artifact@v4.1.7 with: @@ -219,6 +259,7 @@ jobs: file linux_artifacts/dist.tar.gz python3 ci/nightly.py artifact windows-amd64 windows_artifacts/ python3 ci/nightly.py artifact linux-amd64 linux_artifacts/dist.tar.gz + python3 ci/nightly.py artifact linux-arm64 linux_arm_artifacts/dist.tar.gz python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.tar.gz python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.tar.gz python3 ci/nightly.py prune From c910b5e583b8064f551179bfddc7c59ef5a32675 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 4 Aug 2025 12:13:30 +0100 Subject: [PATCH 098/160] Add `intrinsics.type_is_nearly_simple_compare` --- src/check_builtin.cpp | 2 ++ src/check_expr.cpp | 2 +- src/checker_builtin_procs.hpp | 2 ++ src/types.cpp | 56 +++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 974224ed2..880de73f5 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -32,6 +32,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_sliceable, is_type_comparable, is_type_simple_compare, + is_type_nearly_simple_compare, is_type_dereferenceable, is_type_valid_for_keys, is_type_valid_for_matrix_elems, @@ -6145,6 +6146,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_sliceable: case BuiltinProc_type_is_comparable: case BuiltinProc_type_is_simple_compare: + case BuiltinProc_type_is_nearly_simple_compare: case BuiltinProc_type_is_dereferenceable: case BuiltinProc_type_is_valid_map_key: case BuiltinProc_type_is_valid_matrix_elements: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6723a7580..51fb5511b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6533,7 +6533,7 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco Entity *e = entry.value; switch (e->kind) { case Entity_TypeName: { - if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + // if (print_count == 0) error_line("\n\tWith the following definitions:\n"); gbString str = type_to_string(e->type); error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 8e135ab10..e9655e88a 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -264,6 +264,7 @@ BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_sliceable, BuiltinProc_type_is_comparable, BuiltinProc_type_is_simple_compare, // easily compared using memcmp + BuiltinProc_type_is_nearly_simple_compare, // easily compared using memcmp (including floats) BuiltinProc_type_is_dereferenceable, BuiltinProc_type_is_valid_map_key, BuiltinProc_type_is_valid_matrix_elements, @@ -621,6 +622,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_comparable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_nearly_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_valid_map_key"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_valid_matrix_elements"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/types.cpp b/src/types.cpp index 2e696810d..9ffd10ca8 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2559,6 +2559,62 @@ gb_internal bool is_type_simple_compare(Type *t) { return false; } +// NOTE(bill): type can be easily compared using memcmp or contains a float +gb_internal bool is_type_nearly_simple_compare(Type *t) { + t = core_type(t); + switch (t->kind) { + case Type_Array: + return is_type_nearly_simple_compare(t->Array.elem); + + case Type_EnumeratedArray: + return is_type_nearly_simple_compare(t->EnumeratedArray.elem); + + case Type_Basic: + if (t->Basic.flags & (BasicFlag_SimpleCompare|BasicFlag_Numeric)) { + return true; + } + if (t->Basic.kind == Basic_typeid) { + return true; + } + return false; + + case Type_Pointer: + case Type_MultiPointer: + case Type_SoaPointer: + case Type_Proc: + case Type_BitSet: + return true; + + case Type_Matrix: + return is_type_nearly_simple_compare(t->Matrix.elem); + + case Type_Struct: + for_array(i, t->Struct.fields) { + Entity *f = t->Struct.fields[i]; + if (!is_type_nearly_simple_compare(f->type)) { + return false; + } + } + return true; + + case Type_Union: + for_array(i, t->Union.variants) { + Type *v = t->Union.variants[i]; + if (!is_type_nearly_simple_compare(v)) { + return false; + } + } + // make it dumb on purpose + return t->Union.variants.count == 1; + + case Type_SimdVector: + return is_type_nearly_simple_compare(t->SimdVector.elem); + + } + + return false; +} + gb_internal bool is_type_load_safe(Type *type) { GB_ASSERT(type != nullptr); type = core_type(core_array_type(type)); From 45e4ce5d449f0fad58c6f527c5b926bf2d58402f Mon Sep 17 00:00:00 2001 From: Jack Punter Date: Mon, 4 Aug 2025 23:15:16 +0100 Subject: [PATCH 099/160] Adds @(no_instrumentation) to spall buffer and SCOPED operations Currently without this scoped event names are not displaying correctly when auto-tracing is enabled. The buffer_destroy event, obviously, fails to be completed (as theres no buffer to write the end event to, and context_destroy should happen after all the buffers are destroyed so there's, again, no buffers to write to. --- core/prof/spall/spall.odin | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index 16b809359..dc53dc3dc 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -103,6 +103,7 @@ context_create_with_sleep :: proc(filename: string, sleep := 2 * time.Second) -> context_create :: proc{context_create_with_scale, context_create_with_sleep} +@(no_instrumentation) context_destroy :: proc(ctx: ^Context) { if ctx == nil { return @@ -146,6 +147,7 @@ buffer_flush :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_c buffer.first_ts = end } +@(no_instrumentation) buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) { buffer_flush(ctx, buffer) @@ -155,12 +157,14 @@ buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) { @(deferred_in=_scoped_buffer_end) +@(no_instrumentation) SCOPED_EVENT :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) -> bool { _buffer_begin(ctx, buffer, name, args, location) return true } @(private) +@(no_instrumentation) _scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #caller_location) { _buffer_end(ctx, buffer) } From b1cda52fd6a2ef7f12c18cc8e1f2befd8350a8ce Mon Sep 17 00:00:00 2001 From: Jared Cone Date: Mon, 4 Aug 2025 19:42:23 -0700 Subject: [PATCH 100/160] Fixed delete-after-free in file_windows.odin --- core/os/os2/file_windows.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 40d012183..dbac78d0e 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -249,9 +249,9 @@ _destroy :: proc(f: ^File_Impl) -> Error { a := f.allocator err0 := free(f.wname, a) err1 := delete(f.name, a) - err2 := free(f, a) - err3 := delete(f.r_buf, a) - err4 := delete(f.w_buf, a) + err2 := delete(f.r_buf, a) + err3 := delete(f.w_buf, a) + err4 := free(f, a) err0 or_return err1 or_return err2 or_return From 7f194080e6fca33a8341f2c86d52a85be056cf8f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 5 Aug 2025 12:27:28 +0100 Subject: [PATCH 101/160] Fix possible race condition with struct offsets --- src/checker.cpp | 6 +++++- src/types.cpp | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index a1d8f98d7..dbe2af866 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6802,7 +6802,11 @@ gb_internal void check_parsed_files(Checker *c) { for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { - (void)type_align_of(e->type); + if (e->TypeName.is_type_alias) { + // Ignore for the time being + } else { + (void)type_align_of(e->type); + } } else if (e->kind == Entity_Procedure) { DeclInfo *decl = e->decl_info; ast_node(pl, ProcLit, decl->proc_lit); diff --git a/src/types.cpp b/src/types.cpp index 9ffd10ca8..861841b59 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -152,10 +152,11 @@ struct TypeStruct { bool is_polymorphic; bool are_offsets_set : 1; - bool are_offsets_being_processed : 1; bool is_packed : 1; bool is_raw_union : 1; bool is_poly_specialized : 1; + + std::atomic are_offsets_being_processed; }; struct TypeUnion { @@ -4099,18 +4100,18 @@ gb_internal bool type_set_offsets(Type *t) { if (t->kind == Type_Struct) { MUTEX_GUARD(&t->Struct.offset_mutex); if (!t->Struct.are_offsets_set) { - t->Struct.are_offsets_being_processed = true; + t->Struct.are_offsets_being_processed.store(true); t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_min_field_align, t->Struct.custom_max_field_align); - t->Struct.are_offsets_being_processed = false; + t->Struct.are_offsets_being_processed.store(false); t->Struct.are_offsets_set = true; return true; } } else if (is_type_tuple(t)) { MUTEX_GUARD(&t->Tuple.mutex); if (!t->Tuple.are_offsets_set) { - t->Tuple.are_offsets_being_processed = true; + t->Tuple.are_offsets_being_processed.store(true); t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1, 0); - t->Tuple.are_offsets_being_processed = false; + t->Tuple.are_offsets_being_processed.store(false); t->Tuple.are_offsets_set = true; return true; } @@ -4293,9 +4294,12 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) { if (path->failure) { return FAILURE_SIZE; } - if (t->Struct.are_offsets_being_processed && t->Struct.offsets == nullptr) { - type_path_print_illegal_cycle(path, path->path.count-1); - return FAILURE_SIZE; + { + MUTEX_GUARD(&t->Struct.offset_mutex); + if (t->Struct.are_offsets_being_processed.load() && t->Struct.offsets == nullptr) { + type_path_print_illegal_cycle(path, path->path.count-1); + return FAILURE_SIZE; + } } type_set_offsets(t); GB_ASSERT(t->Struct.fields.count == 0 || t->Struct.offsets != nullptr); From accdd7c2af4c2b9f4a0b923a47df4c2eb6074b0a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 5 Aug 2025 12:31:57 +0100 Subject: [PATCH 102/160] Fix atomics for tuples --- src/types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.cpp b/src/types.cpp index 861841b59..29412fa25 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -255,7 +255,7 @@ struct TypeProc { Slice variables; /* Entity_Variable */ \ i64 * offsets; \ BlockingMutex mutex; /* for settings offsets */ \ - bool are_offsets_being_processed; \ + std::atomic are_offsets_being_processed; \ bool are_offsets_set; \ bool is_packed; \ }) \ From af3184adc96cef59fff986ea6400caa6dbdb56ae Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 5 Aug 2025 15:12:54 +0100 Subject: [PATCH 103/160] Change `is_utf16` field to `encoding` and use an enum --- base/runtime/core.odin | 7 ++++++- base/runtime/print.odin | 5 +++-- core/encoding/cbor/tags.odin | 2 +- core/encoding/cbor/unmarshal.odin | 2 +- core/encoding/json/unmarshal.odin | 2 +- core/flags/internal_rtti.odin | 2 +- core/reflect/types.odin | 5 +++-- src/checker.cpp | 3 +++ src/llvm_backend_type.cpp | 16 ++++++++++++---- src/types.cpp | 2 ++ 10 files changed, 33 insertions(+), 13 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index fe40427ff..478a3d307 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -61,6 +61,11 @@ Type_Info_Struct_Soa_Kind :: enum u8 { Dynamic = 3, } +Type_Info_String_Encoding_Kind :: enum u8 { + UTF_8 = 0, + UTF_16 = 1, +} + // Variant Types Type_Info_Named :: struct { name: string, @@ -73,7 +78,7 @@ Type_Info_Rune :: struct {} Type_Info_Float :: struct {endianness: Platform_Endianness} Type_Info_Complex :: struct {} Type_Info_Quaternion :: struct {} -Type_Info_String :: struct {is_cstring: bool, is_utf16: bool} +Type_Info_String :: struct {is_cstring: bool, encoding: Type_Info_String_Encoding_Kind} Type_Info_Boolean :: struct {} Type_Info_Any :: struct {} Type_Info_Type_Id :: struct {} diff --git a/base/runtime/print.odin b/base/runtime/print.odin index 85ed49445..2cfb6661b 100644 --- a/base/runtime/print.odin +++ b/base/runtime/print.odin @@ -297,8 +297,9 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_byte('c') } print_string("string") - if info.is_utf16 { - print_string("16") + switch info.encoding { + case .UTF_8: /**/ + case .UTF_16: print_string("16") } case Type_Info_Boolean: switch ti.id { diff --git a/core/encoding/cbor/tags.odin b/core/encoding/cbor/tags.odin index e0e69cbf5..ae1664dfc 100644 --- a/core/encoding/cbor/tags.odin +++ b/core/encoding/cbor/tags.odin @@ -298,7 +298,7 @@ tag_base64_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, #partial switch t in ti.variant { case reflect.Type_Info_String: - assert(!t.is_utf16) + assert(t.encoding == .UTF_8) if t.is_cstring { length := base64.decoded_len(bytes) builder := strings.builder_make(0, length+1) diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index 2840429f5..043b2ec60 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -335,7 +335,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) { #partial switch t in ti.variant { case reflect.Type_Info_String: - assert(!t.is_utf16) + assert(t.encoding == .UTF_8) bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 51e7e3b81..0b65adaac 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -571,7 +571,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm #partial switch tk in t.key.variant { case runtime.Type_Info_String: - assert(!tk.is_utf16) + assert(tk.encoding == .UTF_8) key_ptr = rawptr(&key) key_cstr: cstring diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 58224cc87..a1b050597 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -127,7 +127,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: } case runtime.Type_Info_String: - assert(!specific_type_info.is_utf16) + assert(specific_type_info.encoding == .UTF_8) if specific_type_info.is_cstring { cstr_ptr := (^cstring)(ptr) diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2351408cc..98b7b368f 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -514,8 +514,9 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt io.write_byte(w, 'c', &n) or_return } io.write_string(w, "string", &n) or_return - if info.is_utf16 { - io.write_string(w, "16", &n) or_return + switch info.encoding { + case .UTF_8: /**/ + case .UTF_16: io.write_string(w, "16", &n) or_return } case Type_Info_Boolean: switch ti.id { diff --git a/src/checker.cpp b/src/checker.cpp index e9fa792f3..e72061f56 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3101,6 +3101,9 @@ gb_internal void init_core_type_info(Checker *c) { GB_ASSERT(tis->fields.count == 5); + Entity *type_info_string_encoding_kind = find_core_entity(c, str_lit("Type_Info_String_Encoding_Kind")); + t_type_info_string_encoding_kind = type_info_string_encoding_kind->type; + Entity *type_info_variant = tis->fields[4]; Type *tiv_type = type_info_variant->type; GB_ASSERT(is_type_union(tiv_type)); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index a91d77fe5..d1e7c0559 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -525,7 +525,15 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ break; case Basic_string: - tag_type = t_type_info_string; + { + tag_type = t_type_info_string; + LLVMValueRef vals[2] = { + lb_const_bool(m, t_bool, false).value, + lb_const_int(m, t_type_info_string_encoding_kind, 0).value, + }; + + variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); + } break; case Basic_cstring: @@ -533,7 +541,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ tag_type = t_type_info_string; LLVMValueRef vals[2] = { lb_const_bool(m, t_bool, true).value, - lb_const_bool(m, t_bool, false).value, + lb_const_int(m, t_type_info_string_encoding_kind, 0).value, }; variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); @@ -545,7 +553,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ tag_type = t_type_info_string; LLVMValueRef vals[2] = { lb_const_bool(m, t_bool, false).value, - lb_const_bool(m, t_bool, true).value, + lb_const_int(m, t_type_info_string_encoding_kind, 1).value, }; variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); @@ -558,7 +566,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ tag_type = t_type_info_string; LLVMValueRef vals[2] = { lb_const_bool(m, t_bool, true).value, - lb_const_bool(m, t_bool, true).value, + lb_const_int(m, t_type_info_string_encoding_kind, 1).value, }; variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals)); diff --git a/src/types.cpp b/src/types.cpp index 51d170f2b..c465714db 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -661,6 +661,8 @@ gb_global Type *t_type_info_enum_value = nullptr; gb_global Type *t_type_info_ptr = nullptr; gb_global Type *t_type_info_enum_value_ptr = nullptr; +gb_global Type *t_type_info_string_encoding_kind = nullptr; + gb_global Type *t_type_info_named = nullptr; gb_global Type *t_type_info_integer = nullptr; gb_global Type *t_type_info_rune = nullptr; From ec7509430369eb5d57a081507792dc03b1c05bab Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 5 Aug 2025 21:31:06 +0200 Subject: [PATCH 104/160] remove windows binaries from non-windows releases --- .github/workflows/nightly.yml | 4 ++++ ci/remove_windows_binaries.sh | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100755 ci/remove_windows_binaries.sh diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 6826239e8..2c9dc30ae 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -69,6 +69,7 @@ jobs: cp -r core $FILE cp -r vendor $FILE cp -r examples $FILE + ./ci/remove_windows_binaries.sh $FILE # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 tar -czvf dist.tar.gz $FILE - name: Odin run @@ -108,6 +109,7 @@ jobs: cp -r core $FILE cp -r vendor $FILE cp -r examples $FILE + ./ci/remove_windows_binaries.sh $FILE # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 tar -czvf dist.tar.gz $FILE - name: Odin run @@ -145,6 +147,7 @@ jobs: cp -r core $FILE cp -r vendor $FILE cp -r examples $FILE + ./ci/remove_windows_binaries.sh $FILE dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 tar -czvf dist.tar.gz $FILE @@ -183,6 +186,7 @@ jobs: cp -r core $FILE cp -r vendor $FILE cp -r examples $FILE + ./ci/remove_windows_binaries.sh $FILE dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38 tar -czvf dist.tar.gz $FILE diff --git a/ci/remove_windows_binaries.sh b/ci/remove_windows_binaries.sh new file mode 100755 index 000000000..0722fc455 --- /dev/null +++ b/ci/remove_windows_binaries.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +find "$1" -type f \(\ + -iname "*.exe" \ + -o -iname "*.dll" \ + -o -iname "*.lib" \ + -o -iname "*.pdb" \ + \) -delete From 9d8e15b3af6e41b3b3d4359eb0d2324cfa34608f Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Wed, 6 Aug 2025 15:13:12 -0400 Subject: [PATCH 105/160] Implementes the block ABI.2010.3.16 natively via the `objc_block` intrinsic and the `Objc_Block` builtin type. See: https://clang.llvm.org/docs/Block-ABI-Apple.html --- base/intrinsics/intrinsics.odin | 1 + base/runtime/core_builtin.odin | 5 + base/runtime/procs_darwin.odin | 12 +- src/check_builtin.cpp | 224 ++++++++++++++++++ src/checker.cpp | 4 + src/checker_builtin_procs.hpp | 2 + src/llvm_backend.hpp | 6 +- src/llvm_backend_proc.cpp | 1 + src/llvm_backend_utility.cpp | 391 ++++++++++++++++++++++++++++++++ 9 files changed, 644 insertions(+), 2 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index d45d24f48..4f644728e 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -381,6 +381,7 @@ objc_register_selector :: proc($name: string) -> objc_SEL --- objc_find_class :: proc($name: string) -> objc_Class --- objc_register_class :: proc($name: string) -> objc_Class --- objc_ivar_get :: proc(self: ^$T) -> ^$U --- +objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) --- valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr --- diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 09118998c..3a51d71fb 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -5,6 +5,11 @@ import "base:intrinsics" @builtin Maybe :: union($T: typeid) {T} +/* +Represents an Objective-C block with a given procedure signature T +*/ +@builtin +Objc_Block :: struct($T: typeid) where intrinsics.type_is_proc(T) { using _: intrinsics.objc_object } /* Recovers the containing/parent struct from a pointer to one of its fields. diff --git a/base/runtime/procs_darwin.odin b/base/runtime/procs_darwin.odin index 20f09400d..d176f0f63 100644 --- a/base/runtime/procs_darwin.odin +++ b/base/runtime/procs_darwin.odin @@ -1,9 +1,12 @@ #+private package runtime -@(priority_index=-1e6) +@(priority_index=-1e5) foreign import ObjC "system:objc" +@(priority_index=-1e6) +foreign import libSystem "system:System" + import "base:intrinsics" objc_id :: ^intrinsics.objc_object @@ -34,3 +37,10 @@ foreign ObjC { object_getClass :: proc "c" (obj: objc_id) -> objc_Class --- } +foreign libSystem { + _NSConcreteGlobalBlock: intrinsics.objc_class + _NSConcreteStackBlock: intrinsics.objc_class + + _Block_object_assign :: proc "c" (rawptr, rawptr, i32) --- + _Block_object_dispose :: proc "c" (rawptr, i32) --- +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 57413f519..9e2ad86c0 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -457,6 +457,229 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan return true; } break; + + case BuiltinProc_objc_block: + { + // NOTE(harold): The last argument specified in the call is the handler proc, + // any other arguments before it are capture by-copy arguments. + auto param_operands = slice_make(permanent_allocator(), ce->args.count); + + isize capture_arg_count = ce->args.count - 1; + + // NOTE(harold): The first parameter is already checked at check_builtin_procedure(). + // Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc. + param_operands[0] = *operand; + + for (isize i = 0; i < ce->args.count-1; i++) { + Operand x = {}; + check_expr(c, &x, ce->args[i]); + + switch (x.mode) { + case Addressing_Value: + case Addressing_Context: + case Addressing_Variable: + case Addressing_Constant: + param_operands[i] = x; + break; + + default: + gbString e = expr_to_string(x.expr); + gbString t = type_to_string(x.type); + error(x.expr, "'%.*s' capture arguments must be values, but got %s of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + } + + // Validate handler proc + Operand handler = {}; + + if (capture_arg_count == 0) { + // It's already been checked and assigned + handler = param_operands[0]; + } else { + check_expr_or_type(c, &handler, ce->args[capture_arg_count]); + param_operands[capture_arg_count] = handler; + } + + if (!is_operand_value(handler) || handler.type->kind != Type_Proc) { + gbString e = expr_to_string(handler.expr); + gbString t = type_to_string(handler.type); + error(handler.expr, "'%.*s' expected a procedure, but got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + Ast *handler_node = unparen_expr(handler.expr); + + // Only direct reference to procs are allowed + switch (handler_node->kind) { + case Ast_ProcLit: break; // ok + case Ast_Ident: { + auto& ident = handler_node->Ident; + + if (ident.entity == nullptr) { + error(handler.expr, "'%.*s' failed to resolve entity from expression", LIT(builtin_name)); + return false; + } + + if (ident.entity->kind != Entity_Procedure) { + gbString e = expr_to_string(handler_node); + + ERROR_BLOCK(); + error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name)); + if(ident.entity->kind == Entity_Variable) { + error_line("\tSuggestion: Variables referencing a procedure are not allowed, they are not a direct procedure reference."); + } else { + error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e); // NOTE(harold): Is this case possible to hit? + } + error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name)); + + gb_string_free(e); + return false; + } + } break; + + default: { + gbString e = expr_to_string(handler_node); + ERROR_BLOCK(); + error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name)); + if( handler_node->kind == Ast_CallExpr) { + error_line("\tSuggestion: Do not use a procedure returned from another procedure."); + } else { + error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e); + } + error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name)); + + gb_string_free(e); + } return false; + } // End switch + + auto& handler_type_proc = handler.type->Proc; + + if (capture_arg_count > handler_type_proc.param_count) { + error(handler.expr, "'%.*s' captured arguments exceeded the handler's parameter count", LIT(builtin_name)); + return false; + } + + // If the handler proc is odin calling convention, but there must be a context defined in this scope. + if (handler_type_proc.calling_convention == ProcCC_Odin) { + if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { + ERROR_BLOCK(); + error(handler.expr, "The handler procedure for '%.*s' requires a context, but no context is defined in the current scope", LIT(builtin_name)); + error_line("\tSuggestion: 'context = runtime.default_context()', or use the \"c\" calling convention for the handler procedure"); + return false; + } + } + + // At most a single return value is supported + if (handler_type_proc.result_count > 1) { + error(handler_type_proc.node->ProcType.results, "Handler procedures for '%.*s' cannot have multiple return values", LIT(builtin_name)); + return false; + } + + // Ensure that captured args are assignable to the handler's corresponding capture params + if (handler_type_proc.param_count > 0) { + auto& handler_param_types = handler.type->Proc.params->Tuple.variables; + Slice handler_capture_param_types = slice(handler_param_types, handler_param_types.count - capture_arg_count, handler_param_types.count); + + for (isize i = 0; i < capture_arg_count; i++) { + Operand op = param_operands[i]; + if (!check_is_assignable_to(c, &op, handler_capture_param_types[i]->type)) { + gbString e = expr_to_string(op.expr); + gbString src = type_to_string(op.type); + gbString dst = type_to_string(handler_capture_param_types[i]->type); + error(op.expr, "'%.*s' captured value '%s' of type '%s' is not assignable to type '%s'", LIT(builtin_name), e, src, dst); + gb_string_free(e); + gb_string_free(src); + gb_string_free(dst); + return false; + } + } + } + + ProcCallingConvention cc = handler_type_proc.calling_convention; + switch (cc) { + case ProcCC_Odin: + case ProcCC_Contextless: + case ProcCC_CDecl: + break; // ok + default: + ERROR_BLOCK(); + + error(handler.expr, "'%.*s' Invalid calling convention for block procedure.", LIT(builtin_name)); + error_line("\tSuggestion: Do not specify a calling convention ot else use \"c\" or \"cotextless\""); + return false; + } + + if (handler_type_proc.is_polymorphic) { + error(handler.expr, "'%.*s' Unspecialized polymorphic procedures are not allowed.", LIT(builtin_name)); + return false; + } + + // Create the specialized Objc_Block type that this intrinsic will return + Token ident = {}; + ident.kind = Token_Ident; + ident.string = str_lit("Objc_Block"); + ident.pos = ast_token(call).pos; + + Token l_paren = {}; + l_paren.kind = Token_OpenParen; + l_paren.string = str_lit("("); + l_paren.pos = ident.pos; + + Token r_paren = {}; + r_paren.kind = Token_CloseParen; + l_paren.string = str_lit(")"); + r_paren.pos = ident.pos; + + // Remove the capture args from the resulting Objc_Block type signature + Ast* handler_proc_type_copy = clone_ast(handler_type_proc.node); + handler_proc_type_copy->ProcType.params->FieldList.list.count -= capture_arg_count; + + // Make sure the Objc_Block's specialized proc is always "c" calling conv, + // even if we have a context, as the invoker is always "c". + // This allows us to have compatibility with the target block types with either calling convention used. + handler_proc_type_copy->ProcType.calling_convention = ProcCC_CDecl; + + Array poly_args = {}; + array_init(&poly_args, permanent_allocator(), 1, 1); + poly_args[0] = handler_proc_type_copy; + + + Type *t_Objc_Block = find_core_type(c->checker, str_lit("Objc_Block")); + Operand poly_op = {}; + poly_op.type = t_Objc_Block; + poly_op.mode = Addressing_Type; + + Ast *poly_call = ast_call_expr(nullptr, ast_ident(nullptr, ident), poly_args, l_paren, r_paren, {}); + + auto err = check_polymorphic_record_type(c, &poly_op, poly_call); + + if (err != 0) { + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + error(handler.expr, "'%.*s' failed to determine resulting Objc_Block handler procedure", LIT(builtin_name)); + return false; + } + + GB_ASSERT(poly_op.type != t_Objc_Block); + GB_ASSERT(poly_op.mode == Addressing_Type); + + bool is_global_block = capture_arg_count == 0 && handler_type_proc.calling_convention != ProcCC_Odin; + if (is_global_block) { + try_to_add_package_dependency(c, "runtime", "_NSConcreteGlobalBlock"); + } else { + try_to_add_package_dependency(c, "runtime", "_NSConcreteStackBlock"); + } + + *operand = poly_op; + operand->type = alloc_type_pointer(operand->type); + operand->mode = Addressing_Value; + return true; + } break; } } @@ -2291,6 +2514,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_selector: case BuiltinProc_objc_register_class: case BuiltinProc_objc_ivar_get: + case BuiltinProc_objc_block: return check_builtin_objc_procedure(c, operand, call, id, type_hint); case BuiltinProc___entry_point: diff --git a/src/checker.cpp b/src/checker.cpp index e72061f56..a13290750 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1460,6 +1460,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { mpsc_destroy(&i->foreign_decls_to_check); map_destroy(&i->objc_msgSend_types); + string_set_destroy(&i->obcj_class_name_set); + mpsc_destroy(&i->objc_class_implementations); + map_destroy(&i->objc_method_implementations); + string_map_destroy(&i->load_file_cache); string_map_destroy(&i->load_directory_cache); map_destroy(&i->load_directory_map); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index b8b105fd2..da5418c1c 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -351,6 +351,7 @@ BuiltinProc__type_end, BuiltinProc_objc_register_selector, BuiltinProc_objc_register_class, BuiltinProc_objc_ivar_get, + BuiltinProc_objc_block, BuiltinProc_constant_utf16_cstring, @@ -711,6 +712,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 648e8a732..cc3dcaa4a 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -198,6 +198,7 @@ struct lbModule { StringMap objc_classes; StringMap objc_selectors; StringMap objc_ivars; + isize objc_next_block_id; // Used to name objective-c blocks, per module PtrMap map_cell_info_map; // address of runtime.Map_Info PtrMap map_info_map; // address of runtime.Map_Cell_Info @@ -483,7 +484,10 @@ gb_internal void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, l gb_internal void lb_start_block(lbProcedure *p, lbBlock *b); gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr); - +gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type); +gb_internal void lb_begin_procedure_body(lbProcedure *p); +gb_internal void lb_end_procedure_body(lbProcedure *p); +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining); gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p); gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 8f306b771..c95bb0c29 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3746,6 +3746,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr); case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr); case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr); + case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr); case BuiltinProc_constant_utf16_cstring: diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index dcb95a9a2..f7807364a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2263,6 +2263,397 @@ gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) { return lb_handle_objc_ivar_for_objc_object_pointer(p, self); } +gb_internal void lb_create_objc_block_helper_procs( + lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset, + Slice capture_values, Slice objc_object_indices, + lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper +) { + gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", m->objc_next_block_id); + gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_block_id); + + // copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos + // dispose: Block_Literal *src, i32 field_apropos + Type *types[3] = { t_rawptr, t_rawptr, t_i32 }; + + Type *copy_tuple = alloc_type_tuple_from_field_types(types, 3, false, true); + Type *dispose_tuple = alloc_type_tuple_from_field_types(&types[1], 2, false, true); + + Type *copy_proc_type = alloc_type_proc(nullptr, copy_tuple, 3, nullptr, 0, false, ProcCC_CDecl); + Type *dispose_proc_type = alloc_type_proc(nullptr, dispose_tuple, 2, nullptr, 0, false, ProcCC_CDecl); + + lbProcedure *copy_proc = lb_create_dummy_procedure(m, make_string((u8*)copy_helper_name, gb_string_length(copy_helper_name)), copy_proc_type); + lbProcedure *dispose_proc = lb_create_dummy_procedure(m, make_string((u8*)dispose_helper_name, gb_string_length(dispose_helper_name)), dispose_proc_type); + LLVMSetLinkage(copy_proc->value, LLVMPrivateLinkage); + LLVMSetLinkage(dispose_proc->value, LLVMPrivateLinkage); + + + const int BLOCK_FIELD_IS_OBJECT = 3; // id, NSObject, __attribute__((NSObject)), block, ... + const int BLOCK_FIELD_IS_BLOCK = 7; // a block variable + + Type *block_base_type = find_core_type(m->info->checker, str_lit("Objc_Block")); + + auto is_object_objc_block = [](Type *type, Type *block_base_type) -> bool { + + Type *base = base_type(type_deref(type)); + GB_ASSERT(base->kind == Type_Struct); + + while (is_type_polymorphic_record_specialized(base)) { + if (base->Struct.polymorphic_parent) { + base = base->Struct.polymorphic_parent; + + if (base == block_base_type) { + return true; + } + base = base_type(base); + GB_ASSERT(base->kind == Type_Struct); + } + } + + return false; + }; + + lb_begin_procedure_body(copy_proc); + lb_begin_procedure_body(dispose_proc); + { + for (isize object_index : objc_object_indices) { + const auto field_offset = unsigned(capture_field_offset+object_index); + + Type *field_type = capture_values[object_index].type; + LLVMTypeRef field_raw_type = lb_type(m, field_type); + + GB_ASSERT(is_type_objc_object(field_type)); + bool is_block_obj = is_object_objc_block(field_type, block_base_type); + + auto copy_args = array_make(temporary_allocator(), 3, 3); + auto dispose_args = array_make(temporary_allocator(), 2, 2); + + // Copy helper + { + LLVMValueRef dst_field = LLVMBuildStructGEP2(copy_proc->builder, block_lit_type, copy_proc->raw_input_parameters[0], field_offset, ""); + LLVMValueRef src_field = LLVMBuildStructGEP2(copy_proc->builder, block_lit_type, copy_proc->raw_input_parameters[1], field_offset, ""); + + lbValue dst_value = {}, src_value = {}; + dst_value.type = alloc_type_pointer(field_type); + dst_value.value = dst_field; + + src_value.type = field_type; + src_value.value = LLVMBuildLoad2(copy_proc->builder, field_raw_type, src_field, ""); + + copy_args[0] = dst_value; + copy_args[1] = src_value; + copy_args[2] = lb_const_int(m, t_i32, u64(is_block_obj ? BLOCK_FIELD_IS_BLOCK : BLOCK_FIELD_IS_OBJECT)); + + lb_emit_runtime_call(copy_proc, "_Block_object_assign", copy_args); + } + + // Dispose helper + { + LLVMValueRef src_field = LLVMBuildStructGEP2(dispose_proc->builder, block_lit_type, dispose_proc->raw_input_parameters[0], field_offset, ""); + lbValue src_value = {}; + src_value.type = field_type; + src_value.value = LLVMBuildLoad2(dispose_proc->builder, field_raw_type, src_field, ""); + + dispose_args[0] = src_value; + dispose_args[1] = lb_const_int(m, t_i32, u64(is_block_obj ? BLOCK_FIELD_IS_BLOCK : BLOCK_FIELD_IS_OBJECT)); + + lb_emit_runtime_call(dispose_proc, "_Block_object_dispose", dispose_args); + } + } + } + lb_end_procedure_body(copy_proc); + lb_end_procedure_body(dispose_proc); + + + out_copy_helper = copy_proc; + out_dispose_helper = dispose_proc; +} + +gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) { + /// #See: https://clang.llvm.org/docs/Block-ABI-Apple.html + /// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h + /// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h + /// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp + + ast_node(ce, CallExpr, expr); + GB_ASSERT(ce->args.count > 0); + + lbModule *m = p->module; + + m->objc_next_block_id += 1; + + const isize capture_arg_count = ce->args.count - 1; + + Type *block_result_type = type_of_expr(expr); + GB_ASSERT(block_result_type != nullptr && block_result_type->kind == Type_Pointer); + + LLVMTypeRef lb_type_rawptr = lb_type(m, t_rawptr); + LLVMTypeRef lb_type_i32 = lb_type(m, t_i32); + LLVMTypeRef lb_type_int = lb_type(m, t_int); + + // Build user proc + // Type * user_proc_type = type_of_expr(ce->args[capture_arg_count]); + lbValue user_proc_value = lb_build_expr(p, ce->args[capture_arg_count]); + auto& user_proc = user_proc_value.type->Proc; + GB_ASSERT(user_proc_value.type->kind == Type_Proc); + + const bool is_global = capture_arg_count == 0 && user_proc.calling_convention != ProcCC_Odin; + const isize block_forward_args = user_proc.param_count - capture_arg_count; + const isize capture_fields_offset = user_proc.calling_convention != ProcCC_Odin ? 5 : 6; + + Ast *proc_lit = unparen_expr(ce->args[capture_arg_count]); + if (proc_lit->kind == Ast_Ident) { + proc_lit = proc_lit->Ident.entity->decl_info->proc_lit; + } + GB_ASSERT(proc_lit->kind == Ast_ProcLit); + + lbProcedure *copy_helper = {}, *dispose_helper = {}; + + // Build captured arguments & collect the ones that are Objective-C objects + auto captured_values = array_make(temporary_allocator(), capture_arg_count, capture_arg_count); + auto objc_captures = array_make(temporary_allocator()); + + for (isize i = 0; i < capture_arg_count; i++) { + captured_values[i] = lb_build_expr(p, ce->args[i]); + + if (is_type_pointer(captured_values[i].type) && is_type_objc_object(captured_values[i].type)) { + array_add(&objc_captures, i); + } + } + + const bool has_objc_fields = objc_captures.count > 0; + + + // Create proc with the block signature + // (takes a block literal pointer as the first parameter, followed by any expected ones from the user's proc) + gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", m->objc_next_block_id); + + // Add + 1 because the first parameter received is the block literal pointer itself + auto invoker_args = array_make(temporary_allocator(), block_forward_args + 1, block_forward_args + 1); + invoker_args[0] = t_rawptr; + + GB_ASSERT(block_forward_args <= user_proc.param_count); + if (user_proc.param_count > 0) { + Slice user_proc_param_types = user_proc.params->Tuple.variables; + for (isize i = 0; i < block_forward_args; i++) { + invoker_args[i+1] = user_proc_param_types[i]->type; + } + } + + GB_ASSERT(user_proc.result_count <= 1); + + Type *invoker_args_tuple = alloc_type_tuple_from_field_types(invoker_args.data, invoker_args.count, false, true); + Type *invoker_results_tuple = nullptr; + if (user_proc.result_count > 0) { + invoker_results_tuple = alloc_type_tuple_from_field_types(&user_proc.results->Tuple.variables[0]->type, 1, false, true); + } + + Type *invoker_proc_type = alloc_type_proc(nullptr, invoker_args_tuple, invoker_args_tuple->Tuple.variables.count, + invoker_results_tuple, user_proc.result_count, false, ProcCC_CDecl); + + lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name, + gb_string_length(block_invoker_name)), invoker_proc_type); + LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage); + + // Create the block descriptor and block literal + gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_"); + block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id); + + gbString block_desc_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Descriptor_"); + block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id); + + LLVMTypeRef block_lit_type = {}; + LLVMTypeRef block_desc_type = {}; + LLVMValueRef block_desc_initializer = {}; + + { + block_desc_type = LLVMStructCreateNamed(m->ctx, block_desc_type_name); + + LLVMTypeRef fields_types[4] = { + lb_type_int, // Reserved + lb_type_int, // Block size + lb_type_rawptr, // Copy helper func pointer + lb_type_rawptr, // Dispose helper func pointer + }; + + LLVMStructSetBody(block_desc_type, fields_types, has_objc_fields ? 4 : 2, false); + } + + { + block_lit_type = LLVMStructCreateNamed(m->ctx, block_lit_type_name); + + auto fields = array_make(temporary_allocator()); + + array_add(&fields, lb_type_rawptr); // isa + array_add(&fields, lb_type_i32); // flags + array_add(&fields, lb_type_i32); // reserved + array_add(&fields, lb_type_rawptr); // invoke + array_add(&fields, block_desc_type); // descriptor + + if (user_proc.calling_convention == ProcCC_Odin) { + array_add(&fields, lb_type(m, t_context)); // context + } + + // From here on, fields for the captured vars are added + for (lbValue cap_arg : captured_values) { + array_add(&fields, lb_type(m, cap_arg.type)); + } + + LLVMStructSetBody(block_lit_type, fields.data, (unsigned)fields.count, false); + } + + // Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block) + if (has_objc_fields) { + lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset, + slice(captured_values, 0, captured_values.count), + slice(objc_captures, 0, objc_captures.count), + copy_helper, dispose_helper); + } + + { + LLVMValueRef fields_values[4] = { + lb_const_int(m, t_int, 0).value, // Reserved + lb_const_int(m, t_int, u64(lb_sizeof(block_lit_type))).value, // Block size + has_objc_fields ? copy_helper->value : nullptr, // Copy helper + has_objc_fields ? dispose_helper->value : nullptr, // Dispose helper + }; + + block_desc_initializer = LLVMConstNamedStruct(block_desc_type, fields_values, has_objc_fields ? 4 : 2); + } + + // Create global block descriptor + gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_"); + desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id); + + LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name); + LLVMSetInitializer(p_descriptor, block_desc_initializer); + + + /// Invoker body + lb_begin_procedure_body(invoker_proc); + { + auto call_args = array_make(temporary_allocator(), user_proc.param_count, user_proc.param_count); + + for (isize i = 1; i < invoker_proc->raw_input_parameters.count; i++) { + lbValue arg = {}; + arg.type = invoker_args[i]; + arg.value = invoker_proc->raw_input_parameters[i], + call_args[i-1] = arg; + } + + LLVMValueRef block_literal = invoker_proc->raw_input_parameters[0]; + + // Push context, if needed + if (user_proc.calling_convention == ProcCC_Odin) { + LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context"); + lbValue ctx_val = {}; + ctx_val.type = t_context_ptr; + ctx_val.value = p_context; + + lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val)); + } + + // Copy capture parameters from the block literal + for (isize i = 0; i < capture_arg_count; i++) { + LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), ""); + + lbValue cap_arg = {}; + cap_arg.value = cap_value; + cap_arg.type = alloc_type_pointer(captured_values[i].type); + + lbValue arg = lb_emit_load(invoker_proc, cap_arg); + call_args[block_forward_args+i] = arg; + } + + lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining); + + GB_ASSERT(user_proc.result_count <= 1); + if (user_proc.result_count > 0) { + GB_ASSERT(result.value != nullptr); + LLVMBuildRet(p->builder, result.value); + } + } + lb_end_procedure_body(invoker_proc); + + + /// Create local block literal + const int BLOCK_HAS_COPY_DISPOSE = (1 << 25); + const int BLOCK_IS_GLOBAL = (1 << 28); + + int raw_flags = is_global ? BLOCK_IS_GLOBAL : 0; + if (has_objc_fields) { + raw_flags |= BLOCK_HAS_COPY_DISPOSE; + } + + gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_"); + block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id); + + lbValue result = {}; + result.type = block_result_type; + + lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock")); + lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags); + lbValue reserved_val = lb_const_int(m, t_i32, 0); + + if (is_global) { + LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name); + result.value = p_block_lit; + + LLVMValueRef fields_values[5] = { + isa_val.value, // isa + flags_val.value, // flags + reserved_val.value, // reserved + invoker_proc->value, // invoke + p_descriptor // descriptor + }; + + LLVMValueRef g_block_lit_initializer = LLVMConstNamedStruct(block_lit_type, fields_values, gb_count_of(fields_values)); + LLVMSetInitializer(p_block_lit, g_block_lit_initializer); + + } else { + LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name); + result.value = p_block_lit; + + // Initialize it + LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa"); + LLVMValueRef f_flags = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 1, "flags"); + LLVMValueRef f_reserved = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 2, "reserved"); + LLVMValueRef f_invoke = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 3, "invoke"); + LLVMValueRef f_descriptor = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 4, "descriptor"); + + LLVMBuildStore(p->builder, isa_val.value, f_isa); + LLVMBuildStore(p->builder, flags_val.value, f_flags); + LLVMBuildStore(p->builder, reserved_val.value, f_reserved); + LLVMBuildStore(p->builder, invoker_proc->value, f_invoke); + LLVMBuildStore(p->builder, p_descriptor, f_descriptor); + + // Store current context, if there is one + if (user_proc.calling_convention == ProcCC_Odin) { + LLVMValueRef f_context = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 5, "context"); + lbAddr p_current_context = lb_find_or_generate_context_ptr(p); + + LLVMValueRef context_size = LLVMConstInt(LLVMInt64TypeInContext(m->ctx), (u64)lb_sizeof(lb_type(m, t_context)), false); + LLVMBuildMemCpy(p->builder, f_context, lb_try_get_alignment(f_context, 1), + p_current_context.addr.value, lb_try_get_alignment(p_current_context.addr.value, 1), context_size); + } + + // Store captured args into the block + for (isize i = 0; i < captured_values.count; i++) { + lbValue capture_arg = captured_values[i]; + + unsigned field_index = unsigned(capture_fields_offset + i); + LLVMValueRef f_capture = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, field_index, "capture_arg"); + + lbValue f_capture_val = {}; + f_capture_val.type = alloc_type_pointer(capture_arg.type); + f_capture_val.value = f_capture; + + lb_emit_store(p, f_capture_val, capture_arg); + } + } + + return result; +} + gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) { ast_node(ce, CallExpr, expr); From a492fa7bc2f83d08a1a639cd2b8087e4752a51a5 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Wed, 6 Aug 2025 23:56:36 -0400 Subject: [PATCH 106/160] Fix Metal mesh shading bindings --- vendor/darwin/Metal/MetalClasses.odin | 363 ++++++++++++++++++++------ vendor/darwin/Metal/MetalEnums.odin | 6 + 2 files changed, 284 insertions(+), 85 deletions(-) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index 2792cb119..ef57be0b4 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -2767,6 +2767,10 @@ RenderPipelineDescriptor_fragmentBuffers :: #force_inline proc "c" (self: ^Rende RenderPipelineDescriptor_fragmentFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^Function { return msgSend(^Function, self, "fragmentFunction") } +@(objc_type=RenderPipelineDescriptor, objc_name="fragmentLinkedFunctions") +RenderPipelineDescriptor_fragmentLinkedFunctions :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "fragmentLinkedFunctions") +} @(objc_type=RenderPipelineDescriptor, objc_name="inputPrimitiveTopology") RenderPipelineDescriptor_inputPrimitiveTopology :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> PrimitiveTopologyClass { return msgSend(PrimitiveTopologyClass, self, "inputPrimitiveTopology") @@ -2831,6 +2835,10 @@ RenderPipelineDescriptor_setDepthAttachmentPixelFormat :: #force_inline proc "c" RenderPipelineDescriptor_setFragmentFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, fragmentFunction: ^Function) { msgSend(nil, self, "setFragmentFunction:", fragmentFunction) } +@(objc_type=RenderPipelineDescriptor, objc_name="setFragmentLinkedFunctions") +RenderPipelineDescriptor_setFragmentLinkedFunctions :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, fragmentLinkedFunctions: ^LinkedFunctions) { + msgSend(nil, self, "setFragmentLinkedFunctions:", fragmentLinkedFunctions) +} @(objc_type=RenderPipelineDescriptor, objc_name="setInputPrimitiveTopology") RenderPipelineDescriptor_setInputPrimitiveTopology :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, inputPrimitiveTopology: PrimitiveTopologyClass) { msgSend(nil, self, "setInputPrimitiveTopology:", inputPrimitiveTopology) @@ -2940,86 +2948,6 @@ RenderPipelineDescriptor_vertexFunction :: #force_inline proc "c" (self: ^Render return msgSend(^Function, self, "vertexFunction") } -@(objc_type=RenderPipelineDescriptor, objc_name="objectFunction") -RenderPipelineDescriptor_objectFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^Function { - return msgSend(^Function, self, "objectFunction") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setObjectFunction") -RenderPipelineDescriptor_setObjectFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, objectFunction: ^Function) { - msgSend(nil, self, "setObjectFunction:", objectFunction) -} -@(objc_type=RenderPipelineDescriptor, objc_name="meshFunction") -RenderPipelineDescriptor_meshFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^Function { - return msgSend(^Function, self, "meshFunction") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setMeshFunction") -RenderPipelineDescriptor_setMeshFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, meshFunction: ^Function) { - msgSend(nil, self, "setMeshFunction:", meshFunction) -} - -@(objc_type=RenderPipelineDescriptor, objc_name="maxTotalThreadsPerObjectThreadgroup") -RenderPipelineDescriptor_maxTotalThreadsPerObjectThreadgroup :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> NS.UInteger { - return msgSend(NS.UInteger, self, "maxTotalThreadsPerObjectThreadgroup") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setMaxTotalThreadsPerObjectThreadgroup") -RenderPipelineDescriptor_setMaxTotalThreadsPerObjectThreadgroup :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, maxTotalThreadsPerObjectThreadgroup: NS.UInteger) { - msgSend(nil, self, "setMaxTotalThreadsPerObjectThreadgroup:", maxTotalThreadsPerObjectThreadgroup) -} -@(objc_type=RenderPipelineDescriptor, objc_name="maxTotalThreadsPerMeshThreadgroup") -RenderPipelineDescriptor_maxTotalThreadsPerMeshThreadgroup :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> NS.UInteger { - return msgSend(NS.UInteger, self, "maxTotalThreadsPerMeshThreadgroup") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setMaxTotalThreadsPerMeshThreadgroup") -RenderPipelineDescriptor_setMaxTotalThreadsPerMeshThreadgroup :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, maxTotalThreadsPerMeshThreadgroup: NS.UInteger) { - msgSend(nil, self, "setMaxTotalThreadsPerMeshThreadgroup:", maxTotalThreadsPerMeshThreadgroup) -} -@(objc_type=RenderPipelineDescriptor, objc_name="objectThreadgroupSizeIsMultipleOfThreadExecutionWidth") -RenderPipelineDescriptor_objectThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> NS.UInteger { - return msgSend(NS.UInteger, self, "objectThreadgroupSizeIsMultipleOfThreadExecutionWidth") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth") -RenderPipelineDescriptor_setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, objectThreadgroupSizeIsMultipleOfThreadExecutionWidth: NS.UInteger) { - msgSend(nil, self, "setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth:", objectThreadgroupSizeIsMultipleOfThreadExecutionWidth) -} - -@(objc_type=RenderPipelineDescriptor, objc_name="meshThreadgroupSizeIsMultipleOfThreadExecutionWidth") -RenderPipelineDescriptor_meshThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> BOOL { - return msgSend(BOOL, self, "meshThreadgroupSizeIsMultipleOfThreadExecutionWidth") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth") -RenderPipelineDescriptor_setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, meshThreadgroupSizeIsMultipleOfThreadExecutionWidth: BOOL) { - msgSend(nil, self, "setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth:", meshThreadgroupSizeIsMultipleOfThreadExecutionWidth) -} - - -@(objc_type=RenderPipelineDescriptor, objc_name="payloadMemoryLength") -RenderPipelineDescriptor_payloadMemoryLength :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> NS.UInteger { - return msgSend(NS.UInteger, self, "payloadMemoryLength") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setPayloadMemoryLength") -RenderPipelineDescriptor_setPayloadMemoryLength :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, payloadMemoryLength: NS.UInteger) { - msgSend(nil, self, "setPayloadMemoryLength:", payloadMemoryLength) -} -@(objc_type=RenderPipelineDescriptor, objc_name="maxTotalThreadgroupsPerMeshGrid") -RenderPipelineDescriptor_maxTotalThreadgroupsPerMeshGrid :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> NS.UInteger { - return msgSend(NS.UInteger, self, "maxTotalThreadgroupsPerMeshGrid") -} -@(objc_type=RenderPipelineDescriptor, objc_name="setMaxTotalThreadgroupsPerMeshGrid") -RenderPipelineDescriptor_setMaxTotalThreadgroupsPerMeshGrid :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, maxTotalThreadgroupsPerMeshGrid: NS.UInteger) { - msgSend(nil, self, "setMaxTotalThreadgroupsPerMeshGrid:", maxTotalThreadgroupsPerMeshGrid) -} - -@(objc_type=RenderPipelineDescriptor, objc_name="objectBuffers") -RenderPipelineDescriptor_objectBuffers :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { - return msgSend(^PipelineBufferDescriptorArray, self, "objectBuffers") -} -@(objc_type=RenderPipelineDescriptor, objc_name="meshBuffers") -RenderPipelineDescriptor_meshBuffers :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { - return msgSend(^PipelineBufferDescriptorArray, self, "meshBuffers") -} - - - @(objc_type=RenderPipelineDescriptor, objc_name="alphaToCoverageEnabled") RenderPipelineDescriptor_alphaToCoverageEnabled :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> BOOL { return msgSend(BOOL, self, "alphaToCoverageEnabled") @@ -3034,6 +2962,272 @@ RenderPipelineDescriptor_rasterizationEnabled :: #force_inline proc "c" (self: ^ return msgSend(BOOL, self, "rasterizationEnabled") } +@(objc_type=RenderPipelineDescriptor, objc_name="shaderValidation") +RenderPipelineDescriptor_shaderValidation :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ShaderValidation { + return msgSend(ShaderValidation, self, "shaderValidation") +} +@(objc_type=RenderPipelineDescriptor, objc_name="setShaderValidation") +RenderPipelineDescriptor_setShaderValidation :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, shaderValidation: ShaderValidation) { + msgSend(nil, self, "setShaderValidation:", shaderValidation) +} + + +//////////////////////////////////////////////////////////////////////////////// + + +@(objc_class="MTLMeshRenderPipelineDescriptor") +MeshRenderPipelineDescriptor :: struct{ using _: NS.Copying(MeshRenderPipelineDescriptor) } + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="alloc", objc_is_class_method=true) +MeshRenderPipelineDescriptor_alloc :: #force_inline proc "c" () -> ^MeshRenderPipelineDescriptor { + return msgSend(^MeshRenderPipelineDescriptor, MeshRenderPipelineDescriptor, "alloc") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="init") +MeshRenderPipelineDescriptor_init :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^MeshRenderPipelineDescriptor { + return msgSend(^MeshRenderPipelineDescriptor, self, "init") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="binaryArchives") +MeshRenderPipelineDescriptor_binaryArchives :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^NS.Array { + return msgSend(^NS.Array, self, "binaryArchives") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setBinaryArchives") +MeshRenderPipelineDescriptor_setBinaryArchives :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, binaryArchives: ^NS.Array) { + msgSend(nil, self, "setBinaryArchives:", binaryArchives) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="colorAttachments") +MeshRenderPipelineDescriptor_colorAttachments :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^RenderPipelineColorAttachmentDescriptorArray { + return msgSend(^RenderPipelineColorAttachmentDescriptorArray, self, "colorAttachments") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="depthAttachmentPixelFormat") +MeshRenderPipelineDescriptor_depthAttachmentPixelFormat :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "depthAttachmentPixelFormat") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setDepthAttachmentPixelFormat") +MeshRenderPipelineDescriptor_setDepthAttachmentPixelFormat :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, depthAttachmentPixelFormat: PixelFormat) { + msgSend(nil, self, "setDepthAttachmentPixelFormat:", depthAttachmentPixelFormat) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="fragmentBuffers") +MeshRenderPipelineDescriptor_fragmentBuffers :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "fragmentBuffers") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="fragmentFunction") +MeshRenderPipelineDescriptor_fragmentFunction :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "fragmentFunction") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setFragmentFunction") +MeshRenderPipelineDescriptor_setFragmentFunction :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, fragmentFunction: ^Function) { + msgSend(nil, self, "setFragmentFunction:", fragmentFunction) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="fragmentLinkedFunctions") +MeshRenderPipelineDescriptor_fragmentLinkedFunctions :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "fragmentLinkedFunctions") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setFragmentLinkedFunctions") +MeshRenderPipelineDescriptor_setFragmentLinkedFunctions :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, fragmentLinkedFunctions: ^LinkedFunctions) { + msgSend(nil, self, "setFragmentLinkedFunctions:", fragmentLinkedFunctions) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="alphaToCoverageEnabled") +MeshRenderPipelineDescriptor_alphaToCoverageEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "alphaToCoverageEnabled") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="isAlphaToCoverageEnabled") +MeshRenderPipelineDescriptor_isAlphaToCoverageEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isAlphaToCoverageEnabled") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setAlphaToCoverageEnabled") +MeshRenderPipelineDescriptor_setAlphaToCoverageEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, alphaToCoverageEnabled: BOOL) { + msgSend(nil, self, "setAlphaToCoverageEnabled:", alphaToCoverageEnabled) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="alphaToOneEnabled") +MeshRenderPipelineDescriptor_alphaToOneEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "alphaToOneEnabled") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="isAlphaToOneEnabled") +MeshRenderPipelineDescriptor_isAlphaToOneEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isAlphaToOneEnabled") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setAlphaToOneEnabled") +MeshRenderPipelineDescriptor_setAlphaToOneEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, alphaToOneEnabled: BOOL) { + msgSend(nil, self, "setAlphaToOneEnabled:", alphaToOneEnabled) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="rasterizationEnabled") +MeshRenderPipelineDescriptor_rasterizationEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "rasterizationEnabled") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="isRasterizationEnabled") +MeshRenderPipelineDescriptor_isRasterizationEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "isRasterizationEnabled") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setRasterizationEnabled") +MeshRenderPipelineDescriptor_setRasterizationEnabled :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, rasterizationEnabled: BOOL) { + msgSend(nil, self, "setRasterizationEnabled:", rasterizationEnabled) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="label") +MeshRenderPipelineDescriptor_label :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^NS.String { + return msgSend(^NS.String, self, "label") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="maxTotalThreadgroupsPerMeshGrid") +MeshRenderPipelineDescriptor_maxTotalThreadgroupsPerMeshGrid :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadgroupsPerMeshGrid") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMaxTotalThreadgroupsPerMeshGrid") +MeshRenderPipelineDescriptor_setMaxTotalThreadgroupsPerMeshGrid :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, maxTotalThreadgroupsPerMeshGrid: NS.UInteger) { + msgSend(nil, self, "setMaxTotalThreadgroupsPerMeshGrid:", maxTotalThreadgroupsPerMeshGrid) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="maxTotalThreadsPerMeshThreadgroup") +MeshRenderPipelineDescriptor_maxTotalThreadsPerMeshThreadgroup :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadsPerMeshThreadgroup") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMaxTotalThreadsPerMeshThreadgroup") +MeshRenderPipelineDescriptor_setMaxTotalThreadsPerMeshThreadgroup :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, maxTotalThreadsPerMeshThreadgroup: NS.UInteger) { + msgSend(nil, self, "setMaxTotalThreadsPerMeshThreadgroup:", maxTotalThreadsPerMeshThreadgroup) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="maxTotalThreadsPerObjectThreadgroup") +MeshRenderPipelineDescriptor_maxTotalThreadsPerObjectThreadgroup :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxTotalThreadsPerObjectThreadgroup") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMaxTotalThreadsPerObjectThreadgroup") +MeshRenderPipelineDescriptor_setMaxTotalThreadsPerObjectThreadgroup :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, maxTotalThreadsPerObjectThreadgroup: NS.UInteger) { + msgSend(nil, self, "setMaxTotalThreadsPerObjectThreadgroup:", maxTotalThreadsPerObjectThreadgroup) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="maxVertexAmplificationCount") +MeshRenderPipelineDescriptor_maxVertexAmplificationCount :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "maxVertexAmplificationCount") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMaxVertexAmplificationCount") +MeshRenderPipelineDescriptor_setMaxVertexAmplificationCount :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, maxVertexAmplificationCount: NS.UInteger) { + msgSend(nil, self, "setMaxVertexAmplificationCount:", maxVertexAmplificationCount) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="meshBuffers") +MeshRenderPipelineDescriptor_meshBuffers :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "meshBuffers") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="meshFunction") +MeshRenderPipelineDescriptor_meshFunction :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "meshFunction") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMeshFunction") +MeshRenderPipelineDescriptor_setMeshFunction :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, meshFunction: ^Function) { + msgSend(nil, self, "setMeshFunction:", meshFunction) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="meshLinkedFunctions") +MeshRenderPipelineDescriptor_meshLinkedFunctions :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "meshLinkedFunctions") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMeshLinkedFunctions") +MeshRenderPipelineDescriptor_setMeshLinkedFunctions :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, meshLinkedFunctions: ^LinkedFunctions) { + msgSend(nil, self, "setMeshLinkedFunctions:", meshLinkedFunctions) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="meshThreadgroupSizeIsMultipleOfThreadExecutionWidth") +MeshRenderPipelineDescriptor_meshThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "meshThreadgroupSizeIsMultipleOfThreadExecutionWidth") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth") +MeshRenderPipelineDescriptor_setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, meshThreadgroupSizeIsMultipleOfThreadExecutionWidth: BOOL) { + msgSend(nil, self, "setMeshThreadgroupSizeIsMultipleOfThreadExecutionWidth:", meshThreadgroupSizeIsMultipleOfThreadExecutionWidth) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="objectBuffers") +MeshRenderPipelineDescriptor_objectBuffers :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^PipelineBufferDescriptorArray { + return msgSend(^PipelineBufferDescriptorArray, self, "objectBuffers") +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="objectFunction") +MeshRenderPipelineDescriptor_objectFunction :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^Function { + return msgSend(^Function, self, "objectFunction") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setObjectFunction") +MeshRenderPipelineDescriptor_setObjectFunction :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, objectFunction: ^Function) { + msgSend(nil, self, "setObjectFunction:", objectFunction) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="objectLinkedFunctions") +MeshRenderPipelineDescriptor_objectLinkedFunctions :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "objectLinkedFunctions") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setObjectLinkedFunctions") +MeshRenderPipelineDescriptor_setObjectLinkedFunctions :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, objectLinkedFunctions: ^LinkedFunctions) { + msgSend(nil, self, "setObjectLinkedFunctions:", objectLinkedFunctions) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="objectThreadgroupSizeIsMultipleOfThreadExecutionWidth") +MeshRenderPipelineDescriptor_objectThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "objectThreadgroupSizeIsMultipleOfThreadExecutionWidth") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth") +MeshRenderPipelineDescriptor_setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, objectThreadgroupSizeIsMultipleOfThreadExecutionWidth: BOOL) { + msgSend(nil, self, "setObjectThreadgroupSizeIsMultipleOfThreadExecutionWidth:", objectThreadgroupSizeIsMultipleOfThreadExecutionWidth) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="payloadMemoryLength") +MeshRenderPipelineDescriptor_payloadMemoryLength :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "payloadMemoryLength") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setPayloadMemoryLength") +MeshRenderPipelineDescriptor_setPayloadMemoryLength :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, payloadMemoryLength: NS.UInteger) { + msgSend(nil, self, "setPayloadMemoryLength:", payloadMemoryLength) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="rasterSampleCount") +MeshRenderPipelineDescriptor_rasterSampleCount :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> NS.UInteger { + return msgSend(NS.UInteger, self, "rasterSampleCount") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setRasterSampleCount") +MeshRenderPipelineDescriptor_setRasterSampleCount :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, rasterSampleCount: NS.UInteger) { + msgSend(nil, self, "setRasterSampleCount:", rasterSampleCount) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="shaderValidation") +MeshRenderPipelineDescriptor_shaderValidation :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> ShaderValidation { + return msgSend(ShaderValidation, self, "shaderValidation") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setShaderValidation") +MeshRenderPipelineDescriptor_setShaderValidation :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, shaderValidation: ShaderValidation) { + msgSend(nil, self, "setShaderValidation:", shaderValidation) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="stencilAttachmentPixelFormat") +MeshRenderPipelineDescriptor_stencilAttachmentPixelFormat :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> PixelFormat { + return msgSend(PixelFormat, self, "stencilAttachmentPixelFormat") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setStencilAttachmentPixelFormat") +MeshRenderPipelineDescriptor_setStencilAttachmentPixelFormat :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, stencilAttachmentPixelFormat: PixelFormat) { + msgSend(nil, self, "setStencilAttachmentPixelFormat:", stencilAttachmentPixelFormat) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="supportIndirectCommandBuffers") +MeshRenderPipelineDescriptor_supportIndirectCommandBuffers :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) -> BOOL { + return msgSend(BOOL, self, "supportIndirectCommandBuffers") +} +@(objc_type=MeshRenderPipelineDescriptor, objc_name="setSupportIndirectCommandBuffers") +MeshRenderPipelineDescriptor_setSupportIndirectCommandBuffers :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor, supportIndirectCommandBuffers: BOOL) { + msgSend(nil, self, "setSupportIndirectCommandBuffers:", supportIndirectCommandBuffers) +} + +@(objc_type=MeshRenderPipelineDescriptor, objc_name="reset") +MeshRenderPipelineDescriptor_reset :: #force_inline proc "c" (self: ^MeshRenderPipelineDescriptor) { + msgSend(nil, self, "reset") +} + //////////////////////////////////////////////////////////////////////////////// @@ -5702,14 +5896,13 @@ Device_supportsVertexAmplificationCount :: #force_inline proc "c" (self: ^Device @(objc_type=Device, objc_name="newRenderPipelineStateWithMeshDescriptor") -Device_newRenderPipelineStateWithMeshDescriptor :: #force_inline proc "contextless" (self: ^Device, options: PipelineOption, reflection: ^AutoreleasedRenderPipelineReflection) -> (state: ^RenderPipelineState, error: ^NS.Error) { - state = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithMeshDescriptor:options:reflection:error:", options, reflection, &error) +Device_newRenderPipelineStateWithMeshDescriptor :: #force_inline proc "c" (self: ^Device, descriptor: ^MeshRenderPipelineDescriptor, options: PipelineOption, reflection: ^AutoreleasedRenderPipelineReflection) -> (state: ^RenderPipelineState, error: ^NS.Error) { + state = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithMeshDescriptor:options:reflection:error:", descriptor, options, reflection, &error) return } @(objc_type=Device, objc_name="newRenderPipelineStateWithMeshDescriptorAndCompletionHandler") -Device_newRenderPipelineStateWithMeshDescriptorAndCompletionHandler :: #force_inline proc "c" (self: ^Device, options: PipelineOption, completionHandler: ^NewRenderPipelineStateWithReflectionCompletionHandler) -> (state: ^RenderPipelineState) { - state = msgSend(^RenderPipelineState, self, "newRenderPipelineStateWithMeshDescriptor:options:completionHandler:", options, completionHandler) - return +Device_newRenderPipelineStateWithMeshDescriptorAndCompletionHandler :: #force_inline proc "c" (self: ^Device, descriptor: ^MeshRenderPipelineDescriptor, options: PipelineOption, completionHandler: NewRenderPipelineStateWithReflectionCompletionHandler) { + msgSend(nil, self, "newRenderPipelineStateWithMeshDescriptor:options:completionHandler:", descriptor, options, completionHandler) } @(objc_type=Device, objc_name="newIOHandle") diff --git a/vendor/darwin/Metal/MetalEnums.odin b/vendor/darwin/Metal/MetalEnums.odin index 5cef5f18d..7d4e86d65 100644 --- a/vendor/darwin/Metal/MetalEnums.odin +++ b/vendor/darwin/Metal/MetalEnums.odin @@ -1050,3 +1050,9 @@ VertexStepFunction :: enum NS.UInteger { PerPatch = 3, PerPatchControlPoint = 4, } + +ShaderValidation :: enum NS.UInteger { + Default = 0, + Enabled = 1, + Disabled = 2, +} From 5a998d44d4007e9edf43b8310794471b857d4e4a Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Thu, 7 Aug 2025 00:25:36 -0400 Subject: [PATCH 107/160] Add bindings for vertexLinkedFunctions --- vendor/darwin/Metal/MetalClasses.odin | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index ef57be0b4..a4c7b43f2 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -2767,6 +2767,10 @@ RenderPipelineDescriptor_fragmentBuffers :: #force_inline proc "c" (self: ^Rende RenderPipelineDescriptor_fragmentFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^Function { return msgSend(^Function, self, "fragmentFunction") } +@(objc_type=RenderPipelineDescriptor, objc_name="vertexLinkedFunctions") +RenderPipelineDescriptor_vertexLinkedFunctions :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^LinkedFunctions { + return msgSend(^LinkedFunctions, self, "vertexLinkedFunctions") +} @(objc_type=RenderPipelineDescriptor, objc_name="fragmentLinkedFunctions") RenderPipelineDescriptor_fragmentLinkedFunctions :: #force_inline proc "c" (self: ^RenderPipelineDescriptor) -> ^LinkedFunctions { return msgSend(^LinkedFunctions, self, "fragmentLinkedFunctions") @@ -2835,6 +2839,10 @@ RenderPipelineDescriptor_setDepthAttachmentPixelFormat :: #force_inline proc "c" RenderPipelineDescriptor_setFragmentFunction :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, fragmentFunction: ^Function) { msgSend(nil, self, "setFragmentFunction:", fragmentFunction) } +@(objc_type=RenderPipelineDescriptor, objc_name="setVertexLinkedFunctions") +RenderPipelineDescriptor_setVertexLinkedFunctions :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, vertexLinkedFunctions: ^LinkedFunctions) { + msgSend(nil, self, "setVertexLinkedFunctions:", vertexLinkedFunctions) +} @(objc_type=RenderPipelineDescriptor, objc_name="setFragmentLinkedFunctions") RenderPipelineDescriptor_setFragmentLinkedFunctions :: #force_inline proc "c" (self: ^RenderPipelineDescriptor, fragmentLinkedFunctions: ^LinkedFunctions) { msgSend(nil, self, "setFragmentLinkedFunctions:", fragmentLinkedFunctions) From 34b4b84b644c726869b863278f22f223d65e45af Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 7 Aug 2025 12:34:41 +0100 Subject: [PATCH 108/160] Add `intrinsics.type_canonical_name` --- base/intrinsics/intrinsics.odin | 3 +++ src/check_builtin.cpp | 16 ++++++++++++++++ src/checker_builtin_procs.hpp | 3 +++ 3 files changed, 22 insertions(+) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index d45d24f48..c008321f0 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -233,6 +233,9 @@ type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), t type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) --- +// Returns the canonicalized name of the type, of which is used to produce the pseudo-unique 'typeid' +type_canonical_name :: proc($T: typeid) -> string --- + constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- constant_log2 :: proc($v: $T) -> T where type_is_integer(T) --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 57413f519..18c75c455 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -7134,6 +7134,22 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_type_canonical_name: + { + Operand op = {}; + Type *type = check_type(c, ce->args[0]); + Type *bt = base_type(type); + if (bt == nullptr || bt == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + + operand->mode = Addressing_Constant; + operand->type = t_untyped_string; + operand->value = type_to_canonical_string(permanent_allocator(), type); + break; + } + case BuiltinProc_procedure_of: { Ast *call_expr = unparen_expr(ce->args[0]); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index b8b105fd2..29b12ba1c 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -339,6 +339,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_has_shared_fields, + BuiltinProc_type_canonical_name, + BuiltinProc__type_end, BuiltinProc_procedure_of, @@ -697,6 +699,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_has_shared_fields"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_canonical_name"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, From 3de300d2f936a3702b73dabce9e877756892646f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 7 Aug 2025 12:38:15 +0100 Subject: [PATCH 109/160] Fix typo --- src/check_builtin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 18c75c455..0daf101b7 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -7146,7 +7146,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As operand->mode = Addressing_Constant; operand->type = t_untyped_string; - operand->value = type_to_canonical_string(permanent_allocator(), type); + operand->value = exact_value_string(type_to_canonical_string(permanent_allocator(), type)); break; } From 1470c300c7b729ea58df5cd6ac2cfd2006389660 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 7 Aug 2025 12:45:59 +0100 Subject: [PATCH 110/160] Add support for `[dynamic]T` in `.raddbg` --- src/llvm_backend.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f37415cc1..fd3701108 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3374,8 +3374,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { LLVMModuleRef mod = m->mod; LLVMContextRef ctx = m->ctx; - lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); - lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"[]?\", expr: \"array(data, len)\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"string\", expr: \"array(data, len)\"}"); + lb_add_raddbg_string(m, "type_view: {type: \"[dynamic]?\", expr: \"rows($, array(data, len), len, cap, allocator)\"}"); // column major matrices lb_add_raddbg_string(m, "type_view: {type: \"matrix[1, ?]?\", expr: \"columns($.data, $[0])\"}"); From 3a1171e25f604e5978a3da303ae9caed44a44ac3 Mon Sep 17 00:00:00 2001 From: connnnal <216976529+connnnal@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:15:12 +0100 Subject: [PATCH 111/160] Alias [^]u16 to Windows LPCWSTR --- vendor/directx/d3d11/d3d11.odin | 2 +- vendor/directx/d3d12/d3d12.odin | 38 +++++++++++++++++---------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/vendor/directx/d3d11/d3d11.odin b/vendor/directx/d3d11/d3d11.odin index bb91e87ce..c15f19934 100644 --- a/vendor/directx/d3d11/d3d11.odin +++ b/vendor/directx/d3d11/d3d11.odin @@ -19,7 +19,7 @@ BOOL :: dxgi.BOOL UINT :: dxgi.UINT INT :: dxgi.INT -LPCWSTR :: [^]u16 +LPCWSTR :: windows.LPCWSTR RECT :: dxgi.RECT SIZE :: dxgi.SIZE diff --git a/vendor/directx/d3d12/d3d12.odin b/vendor/directx/d3d12/d3d12.odin index 9cb1eec48..2bfd4de4f 100644 --- a/vendor/directx/d3d12/d3d12.odin +++ b/vendor/directx/d3d12/d3d12.odin @@ -22,6 +22,8 @@ BOOL :: dxgi.BOOL RECT :: dxgi.RECT +LPCWSTR :: win32.LPCWSTR + IModuleInstance :: d3d_compiler.ID3D11ModuleInstance IBlob :: d3d_compiler.ID3DBlob IModule :: d3d_compiler.ID3D11Module @@ -680,7 +682,7 @@ IObject_VTable :: struct { GetPrivateData: proc "system" (this: ^IObject, guid: ^GUID, pDataSize: ^u32, pData: rawptr) -> HRESULT, SetPrivateData: proc "system" (this: ^IObject, guid: ^GUID, DataSize: u32, pData: rawptr) -> HRESULT, SetPrivateDataInterface: proc "system" (this: ^IObject, guid: ^GUID, pData: ^IUnknown) -> HRESULT, - SetName: proc "system" (this: ^IObject, Name: [^]u16) -> HRESULT, + SetName: proc "system" (this: ^IObject, Name: LPCWSTR) -> HRESULT, } @@ -2714,9 +2716,9 @@ IDevice_VTable :: struct { CreateHeap: proc "system" (this: ^IDevice, pDesc: ^HEAP_DESC, riid: ^IID, ppvHeap: ^rawptr) -> HRESULT, CreatePlacedResource: proc "system" (this: ^IDevice, pHeap: ^IHeap, HeapOffset: u64, pDesc: ^RESOURCE_DESC, InitialState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, riid: ^IID, ppvResource: ^rawptr) -> HRESULT, CreateReservedResource: proc "system" (this: ^IDevice, pDesc: ^RESOURCE_DESC, InitialState: RESOURCE_STATES, pOptimizedClearValue: ^CLEAR_VALUE, riid: ^IID, ppvResource: ^rawptr) -> HRESULT, - CreateSharedHandle: proc "system" (this: ^IDevice, pObject: ^IDeviceChild, pAttributes: ^win32.SECURITY_ATTRIBUTES, Access: u32, Name: [^]u16, pHandle: ^HANDLE) -> HRESULT, + CreateSharedHandle: proc "system" (this: ^IDevice, pObject: ^IDeviceChild, pAttributes: ^win32.SECURITY_ATTRIBUTES, Access: u32, Name: LPCWSTR, pHandle: ^HANDLE) -> HRESULT, OpenSharedHandle: proc "system" (this: ^IDevice, NTHandle: HANDLE, riid: ^IID, ppvObj: ^rawptr) -> HRESULT, - OpenSharedHandleByName: proc "system" (this: ^IDevice, Name: [^]u16, Access: u32, pNTHandle: ^HANDLE) -> HRESULT, + OpenSharedHandleByName: proc "system" (this: ^IDevice, Name: LPCWSTR, Access: u32, pNTHandle: ^HANDLE) -> HRESULT, MakeResident: proc "system" (this: ^IDevice, NumObjects: u32, ppObjects: [^]^IPageable) -> HRESULT, Evict: proc "system" (this: ^IDevice, NumObjects: u32, ppObjects: [^]^IPageable) -> HRESULT, CreateFence: proc "system" (this: ^IDevice, InitialValue: u64, Flags: FENCE_FLAGS, riid: ^IID, ppFence: ^rawptr) -> HRESULT, @@ -2738,9 +2740,9 @@ IPipelineLibrary :: struct #raw_union { } IPipelineLibrary_VTable :: struct { using id3d12devicechild_vtable: IDeviceChild_VTable, - StorePipeline: proc "system" (this: ^IPipelineLibrary, pName: [^]u16, pPipeline: ^IPipelineState) -> HRESULT, - LoadGraphicsPipeline: proc "system" (this: ^IPipelineLibrary, pName: [^]u16, pDesc: ^GRAPHICS_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, - LoadComputePipeline: proc "system" (this: ^IPipelineLibrary, pName: [^]u16, pDesc: ^COMPUTE_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + StorePipeline: proc "system" (this: ^IPipelineLibrary, pName: LPCWSTR, pPipeline: ^IPipelineState) -> HRESULT, + LoadGraphicsPipeline: proc "system" (this: ^IPipelineLibrary, pName: LPCWSTR, pDesc: ^GRAPHICS_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + LoadComputePipeline: proc "system" (this: ^IPipelineLibrary, pName: LPCWSTR, pDesc: ^COMPUTE_PIPELINE_STATE_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, GetSerializedSize: proc "system" (this: ^IPipelineLibrary) -> SIZE_T, Serialize: proc "system" (this: ^IPipelineLibrary, pData: rawptr, DataSizeInBytes: SIZE_T) -> HRESULT, } @@ -2754,7 +2756,7 @@ IPipelineLibrary1 :: struct #raw_union { } IPipelineLibrary1_VTable :: struct { using id3d12pipelinelibrary_vtable: IPipelineLibrary_VTable, - LoadPipeline: proc "system" (this: ^IPipelineLibrary1, pName: [^]u16, pDesc: ^PIPELINE_STATE_STREAM_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, + LoadPipeline: proc "system" (this: ^IPipelineLibrary1, pName: LPCWSTR, pDesc: ^PIPELINE_STATE_STREAM_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT, } MULTIPLE_FENCE_WAIT_FLAGS :: distinct bit_set[MULTIPLE_FENCE_WAIT_FLAG; u32] @@ -2961,7 +2963,7 @@ META_COMMAND_PARAMETER_STAGE :: enum i32 { } META_COMMAND_PARAMETER_DESC :: struct { - Name: [^]u16, + Name: LPCWSTR, Type: META_COMMAND_PARAMETER_TYPE, Flags: META_COMMAND_PARAMETER_FLAGS, RequiredResourceState: RESOURCE_STATES, @@ -2991,7 +2993,7 @@ GRAPHICS_STATES :: enum i32 { META_COMMAND_DESC :: struct { Id: GUID, - Name: [^]u16, + Name: LPCWSTR, InitializationDirtyState: GRAPHICS_STATES, ExecutionDirtyState: GRAPHICS_STATES, } @@ -3012,8 +3014,8 @@ IStateObjectProperties :: struct #raw_union { } IStateObjectProperties_VTable :: struct { using iunknown_vtable: IUnknown_VTable, - GetShaderIdentifier: proc "system" (this: ^IStateObjectProperties, pExportName: [^]u16) -> rawptr, - GetShaderStackSize: proc "system" (this: ^IStateObjectProperties, pExportName: [^]u16) -> u64, + GetShaderIdentifier: proc "system" (this: ^IStateObjectProperties, pExportName: LPCWSTR) -> rawptr, + GetShaderStackSize: proc "system" (this: ^IStateObjectProperties, pExportName: LPCWSTR) -> u64, GetPipelineStackSize: proc "system" (this: ^IStateObjectProperties) -> u64, SetPipelineStackSize: proc "system" (this: ^IStateObjectProperties, PipelineStackSizeInBytes: u64), } @@ -3067,8 +3069,8 @@ EXPORT_FLAG :: enum u32 { } EXPORT_DESC :: struct { - Name: [^]u16, - ExportToRename: [^]u16, + Name: LPCWSTR, + ExportToRename: LPCWSTR, Flags: EXPORT_FLAGS, } @@ -3414,9 +3416,9 @@ AUTO_BREADCRUMB_OP :: enum i32 { AUTO_BREADCRUMB_NODE :: struct { pCommandListDebugNameA: cstring, - pCommandListDebugNameW: [^]u16, + pCommandListDebugNameW: LPCWSTR, pCommandQueueDebugNameA: cstring, - pCommandQueueDebugNameW: [^]u16, + pCommandQueueDebugNameW: LPCWSTR, pCommandList: ^IGraphicsCommandList, pCommandQueue: ^ICommandQueue, BreadcrumbCount: u32, @@ -3427,14 +3429,14 @@ AUTO_BREADCRUMB_NODE :: struct { DRED_BREADCRUMB_CONTEXT :: struct { BreadcrumbIndex: u32, - pContextString: [^]u16, + pContextString: LPCWSTR, } AUTO_BREADCRUMB_NODE1 :: struct { pCommandListDebugNameA: cstring, - pCommandListDebugNameW: [^]u16, + pCommandListDebugNameW: LPCWSTR, pCommandQueueDebugNameA: cstring, - pCommandQueueDebugNameW: [^]u16, + pCommandQueueDebugNameW: LPCWSTR, pCommandList: ^IGraphicsCommandList, pCommandQueue: ^ICommandQueue, BreadcrumbCount: u32, From 46b7abee9fdd339bac31fe7bc79551f2b3881f46 Mon Sep 17 00:00:00 2001 From: Sunagatov Denis Date: Fri, 8 Aug 2025 03:18:57 +1100 Subject: [PATCH 112/160] Fix ICE on missing procedure in base:runtime When a required built-in procedure is missing from the base:runtime package, an assert should be triggered. However this does not happen and instead the compiler crashes silently. The cause is the null-dereference after scope_lookup_current returns nullptr. This adds an assertion that the runtime procedure is found, before proceeding to check it's type and performing further lookups. --- src/llvm_backend_general.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 5d6a55973..67e799918 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2792,6 +2792,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { lbGenerator *gen = m->gen; + GB_ASSERT(e != nullptr); GB_ASSERT(is_type_proc(e->type)); e = strip_entity_wrapping(e); GB_ASSERT(e != nullptr); From b6944b8acb55c090d68d77014017282f0f4df9fb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 7 Aug 2025 17:53:54 +0100 Subject: [PATCH 113/160] Fix instantiation of package for parapoly records --- src/check_type.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 79705b928..a427a1927 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -286,9 +286,20 @@ gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(Checker gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) { GB_ASSERT(is_type_named(named_type)); + GB_ASSERT(original_type->kind == Type_Named); gbAllocator a = heap_allocator(); Scope *s = ctx->scope->parent; + AstPackage *pkg = nullptr; + if (original_type->Named.type_name && original_type->Named.type_name->pkg) { + pkg = original_type->Named.type_name->pkg; + } + + if (pkg == nullptr) { + // NOTE(bill): if the `pkg` cannot be determined, default to the current context's pkg instead + pkg = ctx->pkg; + } + Entity *e = nullptr; { Token token = ast_token(node); @@ -300,12 +311,11 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T e = alloc_entity_type_name(s, token, named_type); e->state = EntityState_Resolved; e->file = ctx->file; - e->pkg = ctx->pkg; + e->pkg = pkg; add_entity_use(ctx, node, e); } named_type->Named.type_name = e; - GB_ASSERT(original_type->kind == Type_Named); e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name; // TODO(bill): Is this even correct? Or should the metadata be copied? e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata; From c22da490b9416b242235a1748d3aa2d4c9fde539 Mon Sep 17 00:00:00 2001 From: Sunagatov Denis Date: Fri, 8 Aug 2025 04:56:59 +1100 Subject: [PATCH 114/160] Print the name of missing runtime procedure This continues the fix made at 46b7abee9fdd339bac31fe7bc79551f2b3881f46, by asserting on missing entity early and printing it's name if its not found. --- src/llvm_backend_proc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index c95bb0c29..04dcc6662 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -985,6 +985,7 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue gb_internal lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) { AstPackage *pkg = m->info->runtime_package; Entity *e = scope_lookup_current(pkg->scope, name); + GB_ASSERT_MSG(e != nullptr, "Runtime procedure not found: %s", name); return lb_find_procedure_value_from_entity(m, e); } From e6635e2508339634921198d4adf9aa9dee418403 Mon Sep 17 00:00:00 2001 From: Sunagatov Denis Date: Fri, 8 Aug 2025 05:45:39 +1100 Subject: [PATCH 115/160] Update runtime doc file This updates and reformats the doc file for the runtime package. The list of things that need to be implemented for a compiler runtime to work has been updated in accordance with recent changes to runtime (notably `cstring16` and `string16` types and `#load_directory`). Some copy-paste errors around string-comparison procedures have been fixed. The file has been renamed to `doc.odin` as per convention, the comment has been moved to the top of the package declaration and the comment is now markdown-formatted. Also wrote down a little map to maybe slightly help with understanding navigation within package runtime's files and added a few more explanations as to why that list of declarations exists and how to use it. The list of declarations has been detailed to some degree, explaining under what conditions the declarations are required. --- base/runtime/doc.odin | 244 +++++++++++++++++++++++++++++++++++++++++ base/runtime/docs.odin | 180 ------------------------------ 2 files changed, 244 insertions(+), 180 deletions(-) create mode 100644 base/runtime/doc.odin delete mode 100644 base/runtime/docs.odin diff --git a/base/runtime/doc.odin b/base/runtime/doc.odin new file mode 100644 index 000000000..7369e36b5 --- /dev/null +++ b/base/runtime/doc.odin @@ -0,0 +1,244 @@ + +/* +Declarations which are required by the compiler + +## Descriptions of files + +There are a lot of files in this package and below is described roughly what +kind of functionality is placed in different files: + +| File pattern | Description +|----------------------|------------------------------------------------------| +| `core.odin` | Contains the declarations that compiler will require to be present. Contains context-related declarations, `Type_Info` declarations and some other types used to implement the runtime and other packages. | +| `core_builtin*.odin` | Contain `@(builtin)` declarations that can be used without importing the package. Most of them aren't required by the compiler | +| `default_*.odin` | Contain default implementations for context allocators | +| `entry_*.odin` | Contain OS-specific entry points | +| `os_specific_*.odin` | Contain OS-specific utility procedures | +| `*internal*.odin` | Contain implementations for internal procedures that can be called by the compiler | + +## Implementing custom runtime + +For embedded and kernel development it might be required to re-implement parts +of the `base:runtime` package. This can include changing the default printing +procedures that handle console output when the program panics, custom +entry-points, tailored for a specific platform or execution environment, or +simply switching up implementations of some procedures. + +In case this is required, the following is suggested: + +1. Define `$ODIN_ROOT` environment variable to point to a directory within your + project that contains the following directories: `base/`, `core/` and `vendor/`. +2. Inside the `$ODIN_ROOT/base` subdirectory, implement the *necessary + declarations*. + +What constitutes the necessary definitions is described below. + +### Context-related + +The compiler will require these declarations as they concern the `context` +variable. + +* `Maybe` +* `Source_Code_Location` +* `Context` +* `Allocator` +* `Random_Generator` +* `Logger` +* `__init_context` + +### Runtime initialization/cleanup + +These are not strictly required for compilation, but if global variables or +`@(init)`/`@(fini)` blocks are used, these procedures need to be called inside +the entry point. + +* `_startup_runtime` +* `_cleanup_runtime` + +### Type assertion check + +These procedures are called every time `.(Type)` expressions are used in order +to check the union tag or the underlying type of `any` before returning the +value of the underlying type. These are not required if `-no-type-assert` is +specified. + +* `type_assertion_check` +* `type_assertion_check2` (takes in typeid) + +### Bounds checking procedures + +These procedures are called every time index or slicing expression are used in +order to perform bounds-checking before the actual operation. These are not +required if the `-no-bounds-check` option is specified. + +* `bounds_check_error` +* `matrix_bounds_check_error` +* `slice_expr_error_hi` +* `slice_expr_error_lo_hi` +* `multi_pointer_slice_expr_error` + +### cstring calls + +If `cstring` or `cstring16` types are used, these procedures are required. + +* `cstring_to_string` +* `cstring_len` +* `cstring16_to_string16` +* `cstring16_len` + +### Comparison + +These procedures are required for comparison operators between strings and other +compound types to function properly. If strings, structs nor unions are compared, +only `string_eq` procedure is required. + +* `memory_equal` +* `memory_compare` +* `memory_compare_zero` +* `cstring_eq` +* `cstring16_eq` +* `cstring_ne` +* `cstring16_ne` +* `cstring_lt` +* `cstring16_lt` +* `cstring_gt` +* `cstring16_gt` +* `cstring_le` +* `cstring16_le` +* `cstring_ge` +* `cstring16_ge` +* `string_eq` +* `string16_eq` +* `string_ne` +* `string16_ne` +* `string_lt` +* `string16_lt` +* `string_gt` +* `string16_gt` +* `string_le` +* `string16_le` +* `string_ge` +* `string16_ge` +* `complex32_eq` +* `complex32_ne` +* `complex64_eq` +* `complex64_ne` +* `complex128_eq` +* `complex128_ne` +* `quaternion64_eq` +* `quaternion64_ne` +* `quaternion128_eq` +* `quaternion128_ne` +* `quaternion256_eq` +* `quaternion256_ne` + +### for-in `string` type + +These procedures are required to iterate strings using `for ... in` loop. If this +kind of loop isn't used, these procedures aren't required. + +* `string_decode_rune` +* `string_decode_last_rune` (for `#reverse for`) + +### Required when RTTI is enabled (the vast majority of targets) + +These declarations are required unless the `-no-rtti` compiler option is +specified. Note that in order to be useful, some other procedures need to be +implemented. Those procedures aren't mentioned here as the compiler won't +complain if they're missing. + +* `Type_Info` +* `type_table` +* `__type_info_of` + +### Hashing + +Required if maps are used + +* `default_hasher` +* `default_hasher_cstring` +* `default_hasher_string` + +### Pseudo-CRT required procedured due to LLVM but useful in general + +* `memset` +* `memcpy` +* `memove` + +### Procedures required by the LLVM backend if u128/i128 is used + +* `umodti3` +* `udivti3` +* `modti3` +* `divti3` +* `fixdfti` +* `fixunsdfti` +* `fixunsdfdi` +* `floattidf` +* `floattidf_unsigned` +* `truncsfhf2` +* `truncdfhf2` +* `gnu_h2f_ieee` +* `gnu_f2h_ieee` +* `extendhfsf2` + +### Procedures required by the LLVM backend if f16 is used (WASM only) + +* `__ashlti3` +* `__multi3` + +### When -no-crt is defined (windows only) + +* `_tls_index` +* `_fltused` + +### Arithmetic + +* `quo_complex32` +* `quo_complex64` +* `quo_complex128` + +* `mul_quaternion64` +* `mul_quaternion128` +* `mul_quaternion256` + +* `quo_quaternion64` +* `quo_quaternion128` +* `quo_quaternion256` + +* `abs_complex32` +* `abs_complex64` +* `abs_complex128` + +* `abs_quaternion64` +* `abs_quaternion128` +* `abs_quaternion256` + +## Map specific calls + +* `map_seed_from_map_data` +* `__dynamic_map_check_grow` (for static map calls) +* `map_insert_hash_dynamic` (for static map calls) +* `__dynamic_map_get` (for dynamic map calls) +* `__dynamic_map_set` (for dynamic map calls) + +## Dynamic literals (`[dynamic]T` and `map[K]V`) (can be disabled with `-no-dynamic-literals`) + +* `__dynamic_array_reserve` +* `__dynamic_array_append` +* `__dynamic_map_reserve` + +### Objective-C specific + +* `objc_lookUpClass` +* `sel_registerName` +* `objc_allocateClassPair` + +### Other required declarations + +This is required without conditions. + +* `Load_Directory_File` + +*/ +package runtime diff --git a/base/runtime/docs.odin b/base/runtime/docs.odin deleted file mode 100644 index f6b439aa0..000000000 --- a/base/runtime/docs.odin +++ /dev/null @@ -1,180 +0,0 @@ -package runtime - -/* - -package runtime has numerous entities (declarations) which are required by the compiler to function. - - -## Basic types and calls (and anything they rely on) - -Source_Code_Location -Context -Allocator -Logger - -__init_context -_cleanup_runtime - - -## cstring calls - -cstring_to_string -cstring_len - - - -## Required when RTTI is enabled (the vast majority of targets) - -Type_Info - -type_table -__type_info_of - - -## Hashing - -default_hasher -default_hasher_cstring -default_hasher_string - - -## Pseudo-CRT required procedured due to LLVM but useful in general -memset -memcpy -memove - - -## Procedures required by the LLVM backend if u128/i128 is used -umodti3 -udivti3 -modti3 -divti3 -fixdfti -fixunsdfti -fixunsdfdi -floattidf -floattidf_unsigned -truncsfhf2 -truncdfhf2 -gnu_h2f_ieee -gnu_f2h_ieee -extendhfsf2 - -## Procedures required by the LLVM backend if f16 is used -__ashlti3 // wasm specific -__multi3 // wasm specific - - -## Required an entry point is defined (i.e. 'main') - -args__ - - -## When -no-crt is defined (and not a wasm target) (mostly due to LLVM) -_tls_index -_fltused - - -## Bounds checking procedures (when not disabled with -no-bounds-check) - -bounds_check_error -matrix_bounds_check_error -slice_expr_error_hi -slice_expr_error_lo_hi -multi_pointer_slice_expr_error - - -## Type assertion check - -type_assertion_check -type_assertion_check2 // takes in typeid - - -## Arithmetic - -quo_complex32 -quo_complex64 -quo_complex128 - -mul_quaternion64 -mul_quaternion128 -mul_quaternion256 - -quo_quaternion64 -quo_quaternion128 -quo_quaternion256 - -abs_complex32 -abs_complex64 -abs_complex128 - -abs_quaternion64 -abs_quaternion128 -abs_quaternion256 - - -## Comparison - -memory_equal -memory_compare -memory_compare_zero - -cstring_eq -cstring_ne -cstring_lt -cstring_gt -cstring_le -cstring_gt - -string_eq -string_ne -string_lt -string_gt -string_le -string_gt - -complex32_eq -complex32_ne -complex64_eq -complex64_ne -complex128_eq -complex128_ne - -quaternion64_eq -quaternion64_ne -quaternion128_eq -quaternion128_ne -quaternion256_eq -quaternion256_ne - - -## Map specific calls - -map_seed_from_map_data -__dynamic_map_check_grow // static map calls -map_insert_hash_dynamic // static map calls -__dynamic_map_get // dynamic map calls -__dynamic_map_set // dynamic map calls - - -## Dynamic literals ([dynamic]T and map[K]V) (can be disabled with -no-dynamic-literals) - -__dynamic_array_reserve -__dynamic_array_append - -__dynamic_map_reserve - - -## Objective-C specific - -objc_lookUpClass -sel_registerName -objc_allocateClassPair - - -## for-in `string` type - -string_decode_rune -string_decode_last_rune // #reverse for - -*/ \ No newline at end of file From e4a0228a8030f1d8d8793464be9ea7f8ae889941 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 10:00:11 +0100 Subject: [PATCH 116/160] Define the behaviour of integer division by zero --- src/build_settings.cpp | 7 ++ src/check_expr.cpp | 52 ++++++++++- src/llvm_backend_expr.cpp | 188 ++++++++++++++++++++++++++++++++------ src/main.cpp | 30 +++++- 4 files changed, 247 insertions(+), 30 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 40bbe41e5..c2a56d1bb 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -404,6 +404,11 @@ String linker_choices[Linker_COUNT] = { str_lit("radlink"), }; +enum IntegerDivisionByZeroKind : u8 { + IntegerDivisionByZero_Trap, + IntegerDivisionByZero_Zero, +}; + // This stores the information for the specify architecture of this build struct BuildContext { // Constants @@ -485,6 +490,8 @@ struct BuildContext { bool keep_object_files; bool disallow_do; + IntegerDivisionByZeroKind integer_division_by_zero_behaviour; + LinkerChoice linker_choice; StringSet custom_attributes; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index faa338f36..1b62de410 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -129,6 +129,8 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type gb_internal bool is_exact_value_zero(ExactValue const &v); +gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(Ast *node); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -4308,7 +4310,25 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ } if (fail) { - error(y->expr, "Division by zero not allowed"); + if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) { + if (check_for_integer_division_by_zero(node) == IntegerDivisionByZero_Zero) { + // Okay + break; + } + } + + switch (op.kind) { + case Token_Mod: + case Token_ModMod: + case Token_ModEq: + case Token_ModModEq: + error(y->expr, "Division by zero through '%.*s' not allowed", LIT(token_strings[op.kind])); + break; + case Token_Quo: + case Token_QuoEq: + error(y->expr, "Division by zero not allowed"); + break; + } x->mode = Addressing_Invalid; return; } @@ -4348,7 +4368,30 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ } } - x->value = exact_binary_operator_value(op.kind, a, b); + match_exact_values(&a, &b); + + + if (check_for_integer_division_by_zero(node) == IntegerDivisionByZero_Zero && + b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) && + (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) { + if (op.kind == Token_QuoEq) { + // x/0 == 0 + x->value = b; + } else { + // x%0 == x + /* + NOTE(bill): @integer division by zero rules + + truncated: r = a - b*trunc(a/b) + floored: r = a - b*floor(a/b) + + IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) + */ + x->value = a; + } + } else { + x->value = exact_binary_operator_value(op.kind, a, b); + } if (is_type_typed(x->type)) { if (node != nullptr) { @@ -9595,6 +9638,11 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom return cl->elems.count > 0; } +gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(Ast *node) { + // TODO(bill): per file `#+feature` flags + return build_context.integer_division_by_zero_behaviour; +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 5425572c7..6652c51d3 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -283,6 +283,12 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, return res; } +gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero(lbProcedure *p) { + // TODO(bill): per file `#+feature` flags + return build_context.integer_division_by_zero_behaviour; +} + + gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, lbValue *res_) { GB_ASSERT(is_type_array_like(type)); Type *elem_type = base_array_type(type); @@ -354,7 +360,6 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu } } else { - switch (op) { case Token_Add: z = LLVMBuildAdd(p->builder, x, y, ""); @@ -366,17 +371,15 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu z = LLVMBuildMul(p->builder, x, y, ""); break; case Token_Quo: - if (is_type_unsigned(integral_type)) { - z = LLVMBuildUDiv(p->builder, x, y, ""); - } else { - z = LLVMBuildSDiv(p->builder, x, y, ""); + { + auto *call = is_type_unsigned(integral_type) ? LLVMBuildUDiv : LLVMBuildSDiv; + z = call(p->builder, x, y, ""); } break; case Token_Mod: - if (is_type_unsigned(integral_type)) { - z = LLVMBuildURem(p->builder, x, y, ""); - } else { - z = LLVMBuildSRem(p->builder, x, y, ""); + { + auto *call = is_type_unsigned(integral_type) ? LLVMBuildURem : LLVMBuildSRem; + z = call(p->builder, x, y, ""); } break; case Token_ModMod: @@ -1111,6 +1114,150 @@ gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue l return {}; } +gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_signed) { + LLVMTypeRef type = LLVMTypeOf(rhs); + GB_ASSERT(LLVMTypeOf(lhs) == type); + + auto *call = is_signed ? LLVMBuildSDiv : LLVMBuildUDiv; + + + LLVMValueRef incoming_values[2] = {}; + LLVMBasicBlockRef incoming_blocks[2] = {}; + + lbBlock *safe_block = lb_create_block(p, "div.safe"); + lbBlock *edge_case_block = lb_create_block(p, "div.edge"); + lbBlock *done_block = lb_create_block(p, "div.done"); + + LLVMValueRef zero = LLVMConstNull(type); + LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); + lbValue cond = {dem_check, t_untyped_bool}; + + lb_emit_if(p, cond, safe_block, edge_case_block); + + lb_start_block(p, safe_block); + incoming_values[0] = call(p->builder, lhs, rhs, ""); + + lb_emit_jump(p, done_block); + + lb_start_block(p, edge_case_block); + + incoming_values[1] = zero; + + switch (lb_check_for_integer_division_by_zero(p)) { + case IntegerDivisionByZero_Trap: + lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); + LLVMBuildUnreachable(p->builder); + break; + case IntegerDivisionByZero_Zero: + // Already fine + break; + } + + lb_emit_jump(p, done_block); + lb_start_block(p, done_block); + + LLVMValueRef res = incoming_values[0]; + + switch (lb_check_for_integer_division_by_zero(p)) { + case IntegerDivisionByZero_Trap: + res = incoming_values[0]; + break; + case IntegerDivisionByZero_Zero: + res = LLVMBuildPhi(p->builder, type, ""); + + GB_ASSERT(p->curr_block->preds.count >= 2); + incoming_blocks[0] = p->curr_block->preds[0]->block; + incoming_blocks[1] = p->curr_block->preds[1]->block; + + LLVMAddIncoming(res, incoming_values, incoming_blocks, 2); + break; + } + + return res; +} + +gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_unsigned, bool is_floored) { + LLVMTypeRef type = LLVMTypeOf(rhs); + GB_ASSERT(LLVMTypeOf(lhs) == type); + + LLVMValueRef incoming_values[2] = {}; + LLVMBasicBlockRef incoming_blocks[2] = {}; + + lbBlock *safe_block = lb_create_block(p, "div.safe"); + lbBlock *edge_case_block = lb_create_block(p, "div.edge"); + lbBlock *done_block = lb_create_block(p, "div.done"); + + LLVMValueRef zero = LLVMConstNull(type); + LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); + lbValue cond = {dem_check, t_untyped_bool}; + + lb_emit_if(p, cond, safe_block, edge_case_block); + + lb_start_block(p, safe_block); + + if (is_floored) { // %% + if (is_unsigned) { + incoming_values[0] = LLVMBuildURem(p->builder, lhs, rhs, ""); + } else { + LLVMValueRef a = LLVMBuildSRem(p->builder, lhs, rhs, ""); + LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs, ""); + LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs, ""); + incoming_values[0] = c; + } + } else { // % + if (is_unsigned) { + incoming_values[0] = LLVMBuildURem(p->builder, lhs, rhs, ""); + } else { + incoming_values[0] = LLVMBuildSRem(p->builder, lhs, rhs, ""); + } + } + + lb_emit_jump(p, done_block); + + lb_start_block(p, edge_case_block); + + /* + NOTE(bill): @integer division by zero rules + + truncated: r = a - b*trunc(a/b) + floored: r = a - b*floor(a/b) + + IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) + */ + incoming_values[1] = lhs; + + switch (lb_check_for_integer_division_by_zero(p)) { + case IntegerDivisionByZero_Trap: + lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); + LLVMBuildUnreachable(p->builder); + break; + case IntegerDivisionByZero_Zero: + // Already fine + break; + } + + lb_emit_jump(p, done_block); + lb_start_block(p, done_block); + + LLVMValueRef res = incoming_values[0]; + + switch (lb_check_for_integer_division_by_zero(p)) { + case IntegerDivisionByZero_Trap: + res = incoming_values[0]; + break; + case IntegerDivisionByZero_Zero: + res = LLVMBuildPhi(p->builder, type, ""); + + GB_ASSERT(p->curr_block->preds.count >= 2); + incoming_blocks[0] = p->curr_block->preds[0]->block; + incoming_blocks[1] = p->curr_block->preds[1]->block; + + LLVMAddIncoming(res, incoming_values, incoming_blocks, 2); + break; + } + + return res; +} gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) { @@ -1350,33 +1497,20 @@ handle_op:; if (is_type_float(integral_type)) { res.value = LLVMBuildFDiv(p->builder, lhs.value, rhs.value, ""); return res; - } else if (is_type_unsigned(integral_type)) { - res.value = LLVMBuildUDiv(p->builder, lhs.value, rhs.value, ""); + } else { + res.value = lb_integer_division(p, lhs.value, rhs.value, !is_type_unsigned(integral_type)); return res; } - res.value = LLVMBuildSDiv(p->builder, lhs.value, rhs.value, ""); - return res; case Token_Mod: if (is_type_float(integral_type)) { res.value = LLVMBuildFRem(p->builder, lhs.value, rhs.value, ""); return res; - } else if (is_type_unsigned(integral_type)) { - res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, ""); - return res; } - res.value = LLVMBuildSRem(p->builder, lhs.value, rhs.value, ""); + res.value = lb_integer_modulo(p, lhs.value, rhs.value, is_type_unsigned(integral_type), /*is_floored*/false); return res; case Token_ModMod: - if (is_type_unsigned(integral_type)) { - res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, ""); - return res; - } else { - LLVMValueRef a = LLVMBuildSRem(p->builder, lhs.value, rhs.value, ""); - LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs.value, ""); - LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs.value, ""); - res.value = c; - return res; - } + res.value = lb_integer_modulo(p, lhs.value, rhs.value, is_type_unsigned(integral_type), /*is_floored*/true); + return res; case Token_And: res.value = LLVMBuildAnd(p->builder, lhs.value, rhs.value, ""); diff --git a/src/main.cpp b/src/main.cpp index 5a43e3c02..0bfab0344 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -392,6 +392,8 @@ enum BuildFlagKind { BuildFlag_PrintLinkerFlags, + BuildFlag_IntegerDivisionByZero, + // internal use only BuildFlag_InternalFastISel, BuildFlag_InternalIgnoreLazy, @@ -613,6 +615,9 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build); + add_flag(&build_flags, BuildFlag_IntegerDivisionByZero, str_lit("integer-division-by-zero"), BuildFlagParam_String, Command__does_check); + + add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all); @@ -1515,7 +1520,7 @@ gb_internal bool parse_build_flags(Array args) { } else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) { build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix; } else { - gb_printf_err("-error-pos-style options are 'unix', 'odin' and 'default' (odin)\n"); + gb_printf_err("-error-pos-style options are 'unix', 'odin', and 'default' (odin)\n"); bad_flags = true; } break; @@ -1539,6 +1544,18 @@ gb_internal bool parse_build_flags(Array args) { build_context.print_linker_flags = true; break; + case BuildFlag_IntegerDivisionByZero: + GB_ASSERT(value.kind == ExactValue_String); + if (str_eq_ignore_case(value.value_string, "trap")) { + build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Trap; + } else if (str_eq_ignore_case(value.value_string, "zero")) { + build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Zero; + } else { + gb_printf_err("-integer-division-by-zero options are 'trap' and 'zero'.\n"); + bad_flags = true; + } + break; + case BuildFlag_InternalFastISel: build_context.fast_isel = true; break; @@ -2561,7 +2578,18 @@ gb_internal int print_show_help(String const arg0, String command, String option if (print_flag("-ignore-warnings")) { print_usage_line(2, "Ignores warning messages."); } + } + if (check) { + if (print_flag("-integer-division-by-zero:")) { + print_usage_line(2, "Specifies the default behaviour for integer division by zero."); + print_usage_line(2, "Available Options:"); + print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero"); + print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == 0"); + } + } + + if (check) { if (print_flag("-json-errors")) { print_usage_line(2, "Prints the error messages as json to stderr."); } From 991883d0e1376c1e5675e88c26fee89362adc7b1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 10:24:44 +0100 Subject: [PATCH 117/160] Add `#+feature integer-division-by-zero:` --- src/build_settings.cpp | 12 +++++++++++ src/check_expr.cpp | 15 ++++++++++---- src/llvm_backend_expr.cpp | 30 ++++++++++++++++++++++------ src/parser.cpp | 42 ++++++++++++++++++++++++++++----------- 4 files changed, 77 insertions(+), 22 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c2a56d1bb..e21e7da12 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -352,12 +352,24 @@ u64 get_vet_flag_from_name(String const &name) { enum OptInFeatureFlags : u64 { OptInFeatureFlag_NONE = 0, OptInFeatureFlag_DynamicLiterals = 1u<<0, + + OptInFeatureFlag_IntegerDivisionByZero_Trap = 1u<<1, + OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<2, + + OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|OptInFeatureFlag_IntegerDivisionByZero_Zero, + }; u64 get_feature_flag_from_name(String const &name) { if (name == "dynamic-literals") { return OptInFeatureFlag_DynamicLiterals; } + if (name == "integer-division-by-zero:trap") { + return OptInFeatureFlag_IntegerDivisionByZero_Trap; + } + if (name == "integer-division-by-zero:zero") { + return OptInFeatureFlag_IntegerDivisionByZero_Zero; + } return OptInFeatureFlag_NONE; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1b62de410..b68fe0ed0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -129,7 +129,7 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type gb_internal bool is_exact_value_zero(ExactValue const &v); -gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(Ast *node); +gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node); enum LoadDirectiveResult { LoadDirective_Success = 0, @@ -4311,7 +4311,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ if (fail) { if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) { - if (check_for_integer_division_by_zero(node) == IntegerDivisionByZero_Zero) { + if (check_for_integer_division_by_zero(c, node) == IntegerDivisionByZero_Zero) { // Okay break; } @@ -4371,7 +4371,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ match_exact_values(&a, &b); - if (check_for_integer_division_by_zero(node) == IntegerDivisionByZero_Zero && + if (check_for_integer_division_by_zero(c, node) == IntegerDivisionByZero_Zero && b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) && (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) { if (op.kind == Token_QuoEq) { @@ -9638,8 +9638,15 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom return cl->elems.count > 0; } -gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(Ast *node) { +gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) { // TODO(bill): per file `#+feature` flags + u64 flags = check_feature_flags(c, node); + if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) != 0) { + return IntegerDivisionByZero_Trap; + } + if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) != 0) { + return IntegerDivisionByZero_Zero; + } return build_context.integer_division_by_zero_behaviour; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 6652c51d3..b20aef742 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -283,8 +283,26 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, return res; } -gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero(lbProcedure *p) { - // TODO(bill): per file `#+feature` flags +gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) { + AstFile *file = nullptr; + + if (p->body && p->body->file()) { + file = p->body->file(); + } else if (p->type_expr && p->type_expr->file()) { + file = p->type_expr->file(); + } else if (p->entity && p->entity->file) { + file = p->entity->file; + } + + if (file != nullptr && file->feature_flags_set) { + u64 flags = file->feature_flags; + if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) { + return IntegerDivisionByZero_Trap; + } + if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) { + return IntegerDivisionByZero_Zero; + } + } return build_context.integer_division_by_zero_behaviour; } @@ -1143,7 +1161,7 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L incoming_values[1] = zero; - switch (lb_check_for_integer_division_by_zero(p)) { + switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); LLVMBuildUnreachable(p->builder); @@ -1158,7 +1176,7 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L LLVMValueRef res = incoming_values[0]; - switch (lb_check_for_integer_division_by_zero(p)) { + switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: res = incoming_values[0]; break; @@ -1226,7 +1244,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV */ incoming_values[1] = lhs; - switch (lb_check_for_integer_division_by_zero(p)) { + switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); LLVMBuildUnreachable(p->builder); @@ -1241,7 +1259,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV LLVMValueRef res = incoming_values[0]; - switch (lb_check_for_integer_division_by_zero(p)) { + switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: res = incoming_values[0]; break; diff --git a/src/parser.cpp b/src/parser.cpp index 1ccc3feaa..58d7acfa5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6289,7 +6289,7 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { return any_correct; } -gb_internal String vet_tag_get_token(String s, String *out) { +gb_internal String vet_tag_get_token(String s, String *out, bool allow_colon) { s = string_trim_whitespace(s); isize n = 0; while (n < s.len) { @@ -6297,7 +6297,7 @@ gb_internal String vet_tag_get_token(String s, String *out) { isize width = utf8_decode(&s[n], s.len-n, &rune); if (n == 0 && rune == '!') { - } else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-') { + } else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-' && !(allow_colon && rune == ':')) { isize k = gb_max(gb_max(n, width), 1); *out = substring(s, k, s.len); return substring(s, 0, k); @@ -6323,7 +6323,7 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) { u64 vet_not_flags = 0; while (s.len > 0) { - String p = string_trim_whitespace(vet_tag_get_token(s, &s)); + String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/false)); if (p.len == 0) { break; } @@ -6391,7 +6391,7 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { u64 feature_not_flags = 0; while (s.len > 0) { - String p = string_trim_whitespace(vet_tag_get_token(s, &s)); + String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/true)); if (p.len == 0) { break; } @@ -6413,26 +6413,44 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { } else { feature_flags |= flag; } + if (is_notted) { + switch (flag) { + case OptInFeatureFlag_IntegerDivisionByZero_Trap: + case OptInFeatureFlag_IntegerDivisionByZero_Zero: + syntax_error(token_for_pos, "Feature flag does not support notting with '!' - '%.*s'", LIT(p)); + break; + } + } } else { ERROR_BLOCK(); syntax_error(token_for_pos, "Invalid feature flag name: %.*s", LIT(p)); error_line("\tExpected one of the following\n"); error_line("\tdynamic-literals\n"); + error_line("\tinteger-division-by-zero:trap\n"); + error_line("\tinteger-division-by-zero:zero\n"); return OptInFeatureFlag_NONE; } } + u64 res = OptInFeatureFlag_NONE; + if (feature_flags == 0 && feature_not_flags == 0) { - return OptInFeatureFlag_NONE; + res = OptInFeatureFlag_NONE; + } else if (feature_flags == 0 && feature_not_flags != 0) { + res = OptInFeatureFlag_NONE &~ feature_not_flags; + } else if (feature_flags != 0 && feature_not_flags == 0) { + res = feature_flags; + } else { + GB_ASSERT(feature_flags != 0 && feature_not_flags != 0); + res = feature_flags &~ feature_not_flags; } - if (feature_flags == 0 && feature_not_flags != 0) { - return OptInFeatureFlag_NONE &~ feature_not_flags; + + if ((res & OptInFeatureFlag_IntegerDivisionByZero_ALL) == + OptInFeatureFlag_IntegerDivisionByZero_ALL) { + syntax_error(token_for_pos, "Only one integer-division-by-zero feature flag can be enabled"); } - if (feature_flags != 0 && feature_not_flags == 0) { - return feature_flags; - } - GB_ASSERT(feature_flags != 0 && feature_not_flags != 0); - return feature_flags &~ feature_not_flags; + + return res; } gb_internal String dir_from_path(String path) { From ee01643229cc015c35b3b1c237caa66a056bb9be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 10:41:05 +0100 Subject: [PATCH 118/160] Add `-integer-division-by-zero:self` --- src/build_settings.cpp | 7 ++++++- src/check_expr.cpp | 28 ++++++++++++++++++++++------ src/llvm_backend_expr.cpp | 17 +++++++++++++---- src/main.cpp | 9 ++++++--- src/parser.cpp | 1 + 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e21e7da12..a6dce5233 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -355,8 +355,9 @@ enum OptInFeatureFlags : u64 { OptInFeatureFlag_IntegerDivisionByZero_Trap = 1u<<1, OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<2, + OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<3, - OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|OptInFeatureFlag_IntegerDivisionByZero_Zero, + OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|OptInFeatureFlag_IntegerDivisionByZero_Zero|OptInFeatureFlag_IntegerDivisionByZero_Self, }; @@ -370,6 +371,9 @@ u64 get_feature_flag_from_name(String const &name) { if (name == "integer-division-by-zero:zero") { return OptInFeatureFlag_IntegerDivisionByZero_Zero; } + if (name == "integer-division-by-zero:self") { + return OptInFeatureFlag_IntegerDivisionByZero_Self; + } return OptInFeatureFlag_NONE; } @@ -419,6 +423,7 @@ String linker_choices[Linker_COUNT] = { enum IntegerDivisionByZeroKind : u8 { IntegerDivisionByZero_Trap, IntegerDivisionByZero_Zero, + IntegerDivisionByZero_Self, }; // This stores the information for the specify architecture of this build diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b68fe0ed0..782080c93 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4311,7 +4311,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ if (fail) { if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) { - if (check_for_integer_division_by_zero(c, node) == IntegerDivisionByZero_Zero) { + if (check_for_integer_division_by_zero(c, node) != IntegerDivisionByZero_Trap) { // Okay break; } @@ -4371,14 +4371,19 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ match_exact_values(&a, &b); - if (check_for_integer_division_by_zero(c, node) == IntegerDivisionByZero_Zero && + IntegerDivisionByZeroKind zero_behaviour = check_for_integer_division_by_zero(c, node); + if (zero_behaviour != IntegerDivisionByZero_Trap && b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) && (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) { if (op.kind == Token_QuoEq) { - // x/0 == 0 - x->value = b; + if (zero_behaviour == IntegerDivisionByZero_Zero) { + // x/0 == 0 + x->value = b; + } else { + // x/0 == x + x->value = a; + } } else { - // x%0 == x /* NOTE(bill): @integer division by zero rules @@ -4386,8 +4391,16 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ floored: r = a - b*floor(a/b) IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) + IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0) */ - x->value = a; + + if (zero_behaviour == IntegerDivisionByZero_Zero) { + // x%0 == x + x->value = a; + } else { + // x%0 == 0 + x->value = b; + } } } else { x->value = exact_binary_operator_value(op.kind, a, b); @@ -9647,6 +9660,9 @@ gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(Checker if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) != 0) { return IntegerDivisionByZero_Zero; } + if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Self) != 0) { + return IntegerDivisionByZero_Self; + } return build_context.integer_division_by_zero_behaviour; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index b20aef742..b44d2215e 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -302,6 +302,9 @@ gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_beha if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) { return IntegerDivisionByZero_Zero; } + if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) { + return IntegerDivisionByZero_Self; + } } return build_context.integer_division_by_zero_behaviour; } @@ -1159,7 +1162,6 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L lb_start_block(p, edge_case_block); - incoming_values[1] = zero; switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: @@ -1167,7 +1169,10 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L LLVMBuildUnreachable(p->builder); break; case IntegerDivisionByZero_Zero: - // Already fine + incoming_values[1] = zero; + break; + case IntegerDivisionByZero_Self: + incoming_values[1] = lhs; break; } @@ -1178,6 +1183,7 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: + case IntegerDivisionByZero_Self: res = incoming_values[0]; break; case IntegerDivisionByZero_Zero: @@ -1242,7 +1248,6 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) */ - incoming_values[1] = lhs; switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: @@ -1250,7 +1255,10 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV LLVMBuildUnreachable(p->builder); break; case IntegerDivisionByZero_Zero: - // Already fine + incoming_values[1] = lhs; + break; + case IntegerDivisionByZero_Self: + incoming_values[1] = zero; break; } @@ -1261,6 +1269,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV switch (lb_check_for_integer_division_by_zero_behaviour(p)) { case IntegerDivisionByZero_Trap: + case IntegerDivisionByZero_Self: res = incoming_values[0]; break; case IntegerDivisionByZero_Zero: diff --git a/src/main.cpp b/src/main.cpp index 0bfab0344..2f0b1c19b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1550,8 +1550,10 @@ gb_internal bool parse_build_flags(Array args) { build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Trap; } else if (str_eq_ignore_case(value.value_string, "zero")) { build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Zero; - } else { - gb_printf_err("-integer-division-by-zero options are 'trap' and 'zero'.\n"); + } else if (str_eq_ignore_case(value.value_string, "self")) { + build_context.integer_division_by_zero_behaviour = IntegerDivisionByZero_Self; + }else { + gb_printf_err("-integer-division-by-zero options are 'trap', 'zero', and 'self'.\n"); bad_flags = true; } break; @@ -2585,7 +2587,8 @@ gb_internal int print_show_help(String const arg0, String command, String option print_usage_line(2, "Specifies the default behaviour for integer division by zero."); print_usage_line(2, "Available Options:"); print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero"); - print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == 0"); + print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == x"); + print_usage_line(3, "-integer-division-by-zero:self x/0 == x and x%%0 == 0 and x%%%%0 == 0"); } } diff --git a/src/parser.cpp b/src/parser.cpp index 58d7acfa5..c63ffe747 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6428,6 +6428,7 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { error_line("\tdynamic-literals\n"); error_line("\tinteger-division-by-zero:trap\n"); error_line("\tinteger-division-by-zero:zero\n"); + error_line("\tinteger-division-by-zero:self\n"); return OptInFeatureFlag_NONE; } } From 7b5cee9f1e6f6048d8689dbdf959409bd4073b3b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 10:50:24 +0100 Subject: [PATCH 119/160] Correct #+feature check --- src/parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index c63ffe747..a05e183ce 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6446,8 +6446,8 @@ gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { res = feature_flags &~ feature_not_flags; } - if ((res & OptInFeatureFlag_IntegerDivisionByZero_ALL) == - OptInFeatureFlag_IntegerDivisionByZero_ALL) { + u64 idbz_count = gb_count_set_bits(res & OptInFeatureFlag_IntegerDivisionByZero_ALL); + if (idbz_count > 1) { syntax_error(token_for_pos, "Only one integer-division-by-zero feature flag can be enabled"); } From 8c1eda3e3a112ac1475674f306ac876cebdd762a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 11:10:01 +0100 Subject: [PATCH 120/160] Handle `fixed_point_div` and `fixed_point_div_sat` --- src/llvm_backend_expr.cpp | 73 +++++++++++++++++++++++++++++++++++++++ src/llvm_backend_proc.cpp | 14 +++++--- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index b44d2215e..a29682874 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1200,6 +1200,79 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L return res; } +gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, LLVMValueRef scale, Type *platform_type, char const *name) { + LLVMTypeRef type = LLVMTypeOf(rhs); + GB_ASSERT(LLVMTypeOf(lhs) == type); + + LLVMValueRef incoming_values[2] = {}; + LLVMBasicBlockRef incoming_blocks[2] = {}; + + lbBlock *safe_block = lb_create_block(p, "div.safe"); + lbBlock *edge_case_block = lb_create_block(p, "div.edge"); + lbBlock *done_block = lb_create_block(p, "div.done"); + + LLVMValueRef zero = LLVMConstNull(type); + LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); + lbValue cond = {dem_check, t_untyped_bool}; + + lb_emit_if(p, cond, safe_block, edge_case_block); + + lb_start_block(p, safe_block); + + { + LLVMTypeRef types[1] = {lb_type(p->module, platform_type)}; + + LLVMValueRef args[3] = { + lhs, + rhs, + scale }; + + incoming_values[0] = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + } + + lb_emit_jump(p, done_block); + + lb_start_block(p, edge_case_block); + + + switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + case IntegerDivisionByZero_Trap: + lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); + LLVMBuildUnreachable(p->builder); + break; + case IntegerDivisionByZero_Zero: + incoming_values[1] = zero; + break; + case IntegerDivisionByZero_Self: + incoming_values[1] = lhs; + break; + } + + lb_emit_jump(p, done_block); + lb_start_block(p, done_block); + + LLVMValueRef res = incoming_values[0]; + + switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + case IntegerDivisionByZero_Trap: + case IntegerDivisionByZero_Self: + res = incoming_values[0]; + break; + case IntegerDivisionByZero_Zero: + res = LLVMBuildPhi(p->builder, type, ""); + + GB_ASSERT(p->curr_block->preds.count >= 2); + incoming_blocks[0] = p->curr_block->preds[0]->block; + incoming_blocks[1] = p->curr_block->preds[1]->block; + + LLVMAddIncoming(res, incoming_values, incoming_blocks, 2); + break; + } + + return res; +} + + gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLVMValueRef rhs, bool is_unsigned, bool is_floored) { LLVMTypeRef type = LLVMTypeOf(rhs); GB_ASSERT(LLVMTypeOf(lhs) == type); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 04dcc6662..f2e6662c8 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3302,16 +3302,22 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } GB_ASSERT(name != nullptr); - LLVMTypeRef types[1] = {lb_type(p->module, platform_type)}; lbValue res = {}; + res.type = platform_type; - LLVMValueRef args[3] = { + if (id == BuiltinProc_fixed_point_div || + id == BuiltinProc_fixed_point_div_sat) { + res.value = lb_integer_division_intrinsics(p, x.value, y.value, scale.value, platform_type, name); + } else { + LLVMTypeRef types[1] = {lb_type(p->module, platform_type)}; + + LLVMValueRef args[3] = { x.value, y.value, scale.value }; - res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); - res.type = platform_type; + res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + } return lb_emit_conv(p, res, tv.type); } From 7642e0a0e0bb0ff79da6ac7a2ba3b787afa32b78 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:10:01 +0100 Subject: [PATCH 121/160] Require `@(init)` and `@(fini)` to be `proc "contextless" ()` --- base/runtime/default_allocators_nil.odin | 2 +- base/runtime/default_temp_allocator_arena.odin | 7 +++++-- base/runtime/default_temporary_allocator.odin | 6 +++--- base/runtime/thread_management.odin | 11 +++++++++-- core/encoding/cbor/tags.odin | 10 ++++++---- core/image/bmp/bmp.odin | 2 +- core/image/general.odin | 10 +++++----- core/image/netpbm/netpbm.odin | 2 +- core/image/png/png.odin | 2 +- core/image/qoi/qoi.odin | 2 +- core/image/tga/tga.odin | 2 +- core/log/file_console_logger.odin | 4 +++- core/math/big/helpers.odin | 18 ++++++++++-------- core/math/big/internal.odin | 11 ++++++----- core/mem/virtual/virtual.odin | 2 +- core/mem/virtual/virtual_linux.odin | 4 ++-- core/mem/virtual/virtual_other.odin | 2 +- core/mem/virtual/virtual_posix.odin | 4 ++-- core/mem/virtual/virtual_windows.odin | 4 ++-- core/net/socket_windows.odin | 2 +- core/os/os2/allocators.odin | 4 ++-- core/os/os2/file_posix.odin | 4 ++-- core/os/os2/file_wasi.odin | 4 ++-- core/os/os2/file_windows.odin | 8 ++++---- core/os/os2/path_windows.odin | 2 +- core/os/os_darwin.odin | 6 ++++-- core/os/os_freebsd.odin | 6 ++++-- core/os/os_haiku.odin | 6 ++++-- core/os/os_linux.odin | 6 ++++-- core/os/os_netbsd.odin | 6 ++++-- core/os/os_openbsd.odin | 6 ++++-- core/os/os_wasi.odin | 10 ++++++---- core/os/os_windows.odin | 6 ++++-- core/sys/info/cpu_intel.odin | 4 ++-- core/sys/info/cpu_linux_arm.odin | 4 +++- core/sys/info/cpu_linux_intel.odin | 5 ++++- core/sys/info/cpu_windows.odin | 5 ++++- core/sys/info/platform_darwin.odin | 5 ++++- core/sys/info/platform_linux.odin | 9 ++++++--- core/sys/info/platform_windows.odin | 12 ++++++++---- core/sys/windows/util.odin | 4 ++-- core/terminal/internal.odin | 7 +++++-- core/terminal/terminal_js.odin | 6 +++--- core/terminal/terminal_posix.odin | 8 +++++--- core/terminal/terminal_windows.odin | 9 ++++++--- src/checker.cpp | 9 +++++++++ vendor/miniaudio/common.odin | 4 ++-- 47 files changed, 170 insertions(+), 102 deletions(-) diff --git a/base/runtime/default_allocators_nil.odin b/base/runtime/default_allocators_nil.odin index e7a1b1a74..14edd11dd 100644 --- a/base/runtime/default_allocators_nil.odin +++ b/base/runtime/default_allocators_nil.odin @@ -23,7 +23,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, return nil, .None } -nil_allocator :: proc() -> Allocator { +nil_allocator :: proc "contextless" () -> Allocator { return Allocator{ procedure = nil_allocator_proc, data = nil, diff --git a/base/runtime/default_temp_allocator_arena.odin b/base/runtime/default_temp_allocator_arena.odin index ca144b66f..525f81825 100644 --- a/base/runtime/default_temp_allocator_arena.odin +++ b/base/runtime/default_temp_allocator_arena.odin @@ -52,10 +52,13 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint return } -memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) { +memory_block_dealloc :: proc "contextless" (block_to_free: ^Memory_Block, loc := #caller_location) { if block_to_free != nil { + allocator := block_to_free.allocator // sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity) + context = default_context() + context.allocator = allocator mem_free(block_to_free, allocator, loc) } } @@ -172,7 +175,7 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { arena.total_used = 0 } -arena_destroy :: proc(arena: ^Arena, loc := #caller_location) { +arena_destroy :: proc "contextless" (arena: ^Arena, loc := #caller_location) { for arena.curr_block != nil { free_block := arena.curr_block arena.curr_block = free_block.prev diff --git a/base/runtime/default_temporary_allocator.odin b/base/runtime/default_temporary_allocator.odin index b355ded70..671728be8 100644 --- a/base/runtime/default_temporary_allocator.odin +++ b/base/runtime/default_temporary_allocator.odin @@ -8,7 +8,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {} - default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {} + default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {} default_temp_allocator_proc :: nil_allocator_proc @@ -28,7 +28,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { _ = arena_init(&s.arena, uint(size), backing_allocator) } - default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) { + default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) { if s != nil { arena_destroy(&s.arena) s^ = {} @@ -56,7 +56,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { } @(fini, private) - _destroy_temp_allocator_fini :: proc() { + _destroy_temp_allocator_fini :: proc "contextless" () { default_temp_allocator_destroy(&global_default_temp_allocator_data) } } diff --git a/base/runtime/thread_management.odin b/base/runtime/thread_management.odin index cabd4691c..97dcbc8f5 100644 --- a/base/runtime/thread_management.odin +++ b/base/runtime/thread_management.odin @@ -1,10 +1,14 @@ package runtime -Thread_Local_Cleaner :: #type proc "odin" () +Thread_Local_Cleaner_Odin :: #type proc "odin" () +Thread_Local_Cleaner_Contextless :: #type proc "contextless" () + +Thread_Local_Cleaner :: union #shared_nil {Thread_Local_Cleaner_Odin, Thread_Local_Cleaner_Contextless} @(private="file") thread_local_cleaners: [8]Thread_Local_Cleaner + // Add a procedure that will be run at the end of a thread for the purpose of // deallocating state marked as `thread_local`. // @@ -29,6 +33,9 @@ run_thread_local_cleaners :: proc "odin" () { if p == nil { break } - p() + switch v in p { + case Thread_Local_Cleaner_Odin: v() + case Thread_Local_Cleaner_Contextless: v() + } } } diff --git a/core/encoding/cbor/tags.odin b/core/encoding/cbor/tags.odin index ae1664dfc..be07b926a 100644 --- a/core/encoding/cbor/tags.odin +++ b/core/encoding/cbor/tags.odin @@ -82,14 +82,16 @@ _tag_implementations_id: map[string]Tag_Implementation _tag_implementations_type: map[typeid]Tag_Implementation // Register a custom tag implementation to be used when marshalling that type and unmarshalling that tag number. -tag_register_type :: proc(impl: Tag_Implementation, nr: Tag_Number, type: typeid) { +tag_register_type :: proc "contextless" (impl: Tag_Implementation, nr: Tag_Number, type: typeid) { + context = runtime.default_context() _tag_implementations_nr[nr] = impl _tag_implementations_type[type] = impl } // Register a custom tag implementation to be used when marshalling that tag number or marshalling // a field with the struct tag `cbor_tag:"nr"`. -tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string) { +tag_register_number :: proc "contextless" (impl: Tag_Implementation, nr: Tag_Number, id: string) { + context = runtime.default_context() _tag_implementations_nr[nr] = impl _tag_implementations_id[id] = impl } @@ -98,13 +100,13 @@ tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR) @(private, init, disabled=!INITIALIZE_DEFAULT_TAGS) -tags_initialize_defaults :: proc() { +tags_initialize_defaults :: proc "contextless" () { tags_register_defaults() } // Registers tags that have implementations provided by this package. // This is done by default and can be controlled with the `CBOR_INITIALIZE_DEFAULT_TAGS` define. -tags_register_defaults :: proc() { +tags_register_defaults :: proc "contextless" () { tag_register_number({nil, tag_time_unmarshal, tag_time_marshal}, TAG_EPOCH_TIME_NR, TAG_EPOCH_TIME_ID) tag_register_number({nil, tag_base64_unmarshal, tag_base64_marshal}, TAG_BASE64_NR, TAG_BASE64_ID) tag_register_number({nil, tag_cbor_unmarshal, tag_cbor_marshal}, TAG_CBOR_NR, TAG_CBOR_ID) diff --git a/core/image/bmp/bmp.odin b/core/image/bmp/bmp.odin index 057c2ffa0..d5a094e83 100644 --- a/core/image/bmp/bmp.odin +++ b/core/image/bmp/bmp.odin @@ -741,6 +741,6 @@ destroy :: proc(img: ^Image) { } @(init, private) -_register :: proc() { +_register :: proc "contextless" () { image.register(.BMP, load_from_bytes, destroy) } diff --git a/core/image/general.odin b/core/image/general.odin index e92b54f18..336b41d25 100644 --- a/core/image/general.odin +++ b/core/image/general.odin @@ -10,13 +10,13 @@ Destroy_Proc :: #type proc(img: ^Image) _internal_loaders: [Which_File_Type]Loader_Proc _internal_destroyers: [Which_File_Type]Destroy_Proc -register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) { - assert(loader != nil) - assert(destroyer != nil) - assert(_internal_loaders[kind] == nil) +register :: proc "contextless" (kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) { + assert_contextless(loader != nil) + assert_contextless(destroyer != nil) + assert_contextless(_internal_loaders[kind] == nil) _internal_loaders[kind] = loader - assert(_internal_destroyers[kind] == nil) + assert_contextless(_internal_destroyers[kind] == nil) _internal_destroyers[kind] = destroyer } diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin index a9dc6599a..25e0228b5 100644 --- a/core/image/netpbm/netpbm.odin +++ b/core/image/netpbm/netpbm.odin @@ -720,7 +720,7 @@ autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, for } @(init, private) -_register :: proc() { +_register :: proc "contextless" () { loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) { return load_from_bytes(data, allocator) } diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 3eb56c245..87efcf9b5 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -1611,6 +1611,6 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH } @(init, private) -_register :: proc() { +_register :: proc "contextless" () { image.register(.PNG, load_from_bytes, destroy) } diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin index 6b6149e60..ded8d7971 100644 --- a/core/image/qoi/qoi.odin +++ b/core/image/qoi/qoi.odin @@ -371,6 +371,6 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) { } @(init, private) -_register :: proc() { +_register :: proc "contextless" () { image.register(.QOI, load_from_bytes, destroy) } diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index 46e37a0cf..5fda803c5 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -406,6 +406,6 @@ IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4 IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5 @(init, private) -_register :: proc() { +_register :: proc "contextless" () { image.register(.TGA, load_from_bytes, destroy) } \ No newline at end of file diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index 0fe5c3477..f0acc8a22 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -43,12 +43,14 @@ File_Console_Logger_Data :: struct { @(private) global_subtract_stderr_options: Options @(init, private) -init_standard_stream_status :: proc() { +init_standard_stream_status :: proc "contextless" () { // NOTE(Feoramund): While it is technically possible for these streams to // be redirected during the runtime of the program, the cost of checking on // every single log message is not worth it to support such an // uncommonly-used feature. if terminal.color_enabled { + context = runtime.default_context() + // This is done this way because it's possible that only one of these // streams could be redirected to a file. if !terminal.is_terminal(os.stdout) { diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index ee09bb2c7..569f0b810 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -7,6 +7,7 @@ package math_big import "base:intrinsics" +import "base:runtime" import rnd "core:math/rand" /* @@ -778,22 +779,23 @@ int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, alloca INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{} @(init, private) -_init_constants :: proc() { +_init_constants :: proc "contextless" () { initialize_constants() } -initialize_constants :: proc() -> (res: int) { - internal_set( INT_ZERO, 0); INT_ZERO.flags = {.Immutable} - internal_set( INT_ONE, 1); INT_ONE.flags = {.Immutable} - internal_set(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable} +initialize_constants :: proc "contextless" () -> (res: int) { + context = runtime.default_context() + internal_int_set_from_integer( INT_ZERO, 0); INT_ZERO.flags = {.Immutable} + internal_int_set_from_integer( INT_ONE, 1); INT_ONE.flags = {.Immutable} + internal_int_set_from_integer(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable} /* We set these special values to -1 or 1 so they don't get mistake for zero accidentally. This allows for shortcut tests of is_zero as .used == 0. */ - internal_set( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN} - internal_set( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf} - internal_set(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf} + internal_int_set_from_integer( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN} + internal_int_set_from_integer( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf} + internal_int_set_from_integer(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf} return _DEFAULT_MUL_KARATSUBA_CUTOFF } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 4707177c4..8b176e7c0 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -27,10 +27,11 @@ package math_big -import "core:mem" -import "base:intrinsics" -import rnd "core:math/rand" import "base:builtin" +import "base:intrinsics" +import "base:runtime" +import "core:mem" +import rnd "core:math/rand" /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -2885,12 +2886,12 @@ internal_clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context } internal_clear_if_uninitialized :: proc {internal_clear_if_uninitialized_single, internal_clear_if_uninitialized_multi, } -internal_error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) { +internal_error_if_immutable_single :: proc "contextless" (arg: ^Int) -> (err: Error) { if arg != nil && .Immutable in arg.flags { return .Assignment_To_Immutable } return nil } -internal_error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) { +internal_error_if_immutable_multi :: proc "contextless" (args: ..^Int) -> (err: Error) { for i in args { if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable } } diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index 3027e5848..3f388acf3 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -9,7 +9,7 @@ _ :: runtime DEFAULT_PAGE_SIZE := uint(4096) @(init, private) -platform_memory_init :: proc() { +platform_memory_init :: proc "contextless" () { _platform_memory_init() } diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index 3e0d7668b..f819fbf86 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -43,10 +43,10 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return errno == .NONE } -_platform_memory_init :: proc() { +_platform_memory_init :: proc "contextless" () { DEFAULT_PAGE_SIZE = 4096 // is power of two - assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) + assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } diff --git a/core/mem/virtual/virtual_other.odin b/core/mem/virtual/virtual_other.odin index a57856975..c6386e842 100644 --- a/core/mem/virtual/virtual_other.odin +++ b/core/mem/virtual/virtual_other.odin @@ -25,7 +25,7 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return false } -_platform_memory_init :: proc() { +_platform_memory_init :: proc "contextless" () { } _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin index 0b304a5e7..4bb161770 100644 --- a/core/mem/virtual/virtual_posix.odin +++ b/core/mem/virtual/virtual_posix.odin @@ -28,13 +28,13 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return posix.mprotect(data, size, transmute(posix.Prot_Flags)flags) == .OK } -_platform_memory_init :: proc() { +_platform_memory_init :: proc "contextless" () { // NOTE: `posix.PAGESIZE` due to legacy reasons could be wrong so we use `sysconf`. size := posix.sysconf(._PAGESIZE) DEFAULT_PAGE_SIZE = uint(max(size, posix.PAGESIZE)) // is power of two - assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) + assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 3fd4eeb68..1d777af17 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -146,13 +146,13 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) @(no_sanitize_address) -_platform_memory_init :: proc() { +_platform_memory_init :: proc "contextless" () { sys_info: SYSTEM_INFO GetSystemInfo(&sys_info) DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, uint(sys_info.dwPageSize)) // is power of two - assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) + assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) } diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin index cab820ed5..9127874de 100644 --- a/core/net/socket_windows.odin +++ b/core/net/socket_windows.odin @@ -79,7 +79,7 @@ Shutdown_Manner :: enum c.int { } @(init, private) -ensure_winsock_initialized :: proc() { +ensure_winsock_initialized :: proc "contextless" () { win.ensure_winsock_initialized() } diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin index cedfbdee1..36a7d72be 100644 --- a/core/os/os2/allocators.odin +++ b/core/os/os2/allocators.odin @@ -16,7 +16,7 @@ MAX_TEMP_ARENA_COLLISIONS :: MAX_TEMP_ARENA_COUNT - 1 global_default_temp_allocator_arenas: [MAX_TEMP_ARENA_COUNT]runtime.Arena @(fini, private) -temp_allocator_fini :: proc() { +temp_allocator_fini :: proc "contextless" () { for &arena in global_default_temp_allocator_arenas { runtime.arena_destroy(&arena) } @@ -69,6 +69,6 @@ _temp_allocator_end :: proc(tmp: runtime.Arena_Temp) { } @(init, private) -init_thread_local_cleaner :: proc() { +init_thread_local_cleaner :: proc "contextless" () { runtime.add_thread_local_cleaner(temp_allocator_fini) } diff --git a/core/os/os2/file_posix.odin b/core/os/os2/file_posix.odin index 2d74618ee..fed8d766c 100644 --- a/core/os/os2/file_posix.odin +++ b/core/os/os2/file_posix.odin @@ -25,8 +25,8 @@ File_Impl :: struct { } @(init) -init_std_files :: proc() { - new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File { +init_std_files :: proc "contextless" () { + new_std :: proc "contextless" (impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File { impl.file.impl = impl impl.fd = fd impl.allocator = runtime.nil_allocator() diff --git a/core/os/os2/file_wasi.odin b/core/os/os2/file_wasi.odin index 0245841e3..1d417ffb1 100644 --- a/core/os/os2/file_wasi.odin +++ b/core/os/os2/file_wasi.odin @@ -30,8 +30,8 @@ Preopen :: struct { preopens: []Preopen @(init) -init_std_files :: proc() { - new_std :: proc(impl: ^File_Impl, fd: wasi.fd_t, name: string) -> ^File { +init_std_files :: proc "contextless" () { + new_std :: proc "contextless" (impl: ^File_Impl, fd: wasi.fd_t, name: string) -> ^File { impl.file.impl = impl impl.allocator = runtime.nil_allocator() impl.fd = fd diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 25e9cb4b0..b39e65fe2 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -43,8 +43,8 @@ File_Impl :: struct { } @(init) -init_std_files :: proc() { - new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File { +init_std_files :: proc "contextless" () { + new_std :: proc "contextless" (impl: ^File_Impl, code: u32, name: string) -> ^File { impl.file.impl = impl impl.allocator = runtime.nil_allocator() @@ -77,7 +77,7 @@ init_std_files :: proc() { stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "") } -_handle :: proc(f: ^File) -> win32.HANDLE { +_handle :: proc "contextless" (f: ^File) -> win32.HANDLE { return win32.HANDLE(_fd(f)) } @@ -234,7 +234,7 @@ _clone :: proc(f: ^File) -> (clone: ^File, err: Error) { return _new_file(uintptr(clonefd), name(f), file_allocator()) } -_fd :: proc(f: ^File) -> uintptr { +_fd :: proc "contextless" (f: ^File) -> uintptr { if f == nil || f.impl == nil { return INVALID_HANDLE } diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index e0a00b07a..e5a1545ec 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -160,7 +160,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err can_use_long_paths: bool @(init) -init_long_path_support :: proc() { +init_long_path_support :: proc "contextless" () { can_use_long_paths = false key: win32.HKEY diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 1010d27a8..77b5825dd 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -1226,7 +1226,8 @@ _processor_core_count :: proc() -> int { } @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() res := make([]string, len(runtime.args__)) for _, i in res { res[i] = string(runtime.args__[i]) @@ -1235,7 +1236,8 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index aeffdcb87..0542e10dc 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -965,7 +965,8 @@ _processor_core_count :: proc() -> int { @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() res := make([]string, len(runtime.args__)) for _, i in res { res[i] = string(runtime.args__[i]) @@ -974,6 +975,7 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin index b56d516a4..e7c71338b 100644 --- a/core/os/os_haiku.odin +++ b/core/os/os_haiku.odin @@ -317,7 +317,8 @@ file_size :: proc(fd: Handle) -> (i64, Error) { args := _alloc_command_line_arguments() @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() res := make([]string, len(runtime.args__)) for arg, i in runtime.args__ { res[i] = string(arg) @@ -326,7 +327,8 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 66c30711d..15d230820 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -1098,7 +1098,8 @@ _processor_core_count :: proc() -> int { } @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() res := make([]string, len(runtime.args__)) for _, i in res { res[i] = string(runtime.args__[i]) @@ -1107,7 +1108,8 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index accc5abcd..30511012f 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -1015,7 +1015,8 @@ _processor_core_count :: proc() -> int { } @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() res := make([]string, len(runtime.args__)) for _, i in res { res[i] = string(runtime.args__[i]) @@ -1024,6 +1025,7 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index ec9181ba6..50ee37dff 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -915,7 +915,8 @@ _processor_core_count :: proc() -> int { } @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() res := make([]string, len(runtime.args__)) for _, i in res { res[i] = string(runtime.args__[i]) @@ -924,6 +925,7 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index f135e4d42..53c242a01 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -28,16 +28,18 @@ stderr: Handle = 2 args := _alloc_command_line_arguments() @(private, require_results) -_alloc_command_line_arguments :: proc() -> (args: []string) { - args = make([]string, len(runtime.args__)) +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() + args := make([]string, len(runtime.args__)) for &arg, i in args { arg = string(runtime.args__[i]) } - return + return args } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() delete(args) } diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index 3c1725cc5..03c194596 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -194,7 +194,8 @@ current_thread_id :: proc "contextless" () -> int { @(private, require_results) -_alloc_command_line_arguments :: proc() -> []string { +_alloc_command_line_arguments :: proc "contextless" () -> []string { + context = runtime.default_context() arg_count: i32 arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count) arg_list := make([]string, int(arg_count)) @@ -216,7 +217,8 @@ _alloc_command_line_arguments :: proc() -> []string { } @(private, fini) -_delete_command_line_arguments :: proc() { +_delete_command_line_arguments :: proc "contextless" () { + context = runtime.default_context() for s in args { delete(s) } diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index 7c5b38ca4..e8f07c732 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -52,7 +52,7 @@ CPU :: struct { cpu: CPU @(init, private) -init_cpu_features :: proc "c" () { +init_cpu_features :: proc "contextless" () { is_set :: #force_inline proc "c" (bit: u32, value: u32) -> bool { return (value>>bit) & 0x1 != 0 } @@ -156,7 +156,7 @@ init_cpu_features :: proc "c" () { _cpu_name_buf: [72]u8 @(init, private) -init_cpu_name :: proc "c" () { +init_cpu_name :: proc "contextless" () { number_of_extended_ids, _, _, _ := cpuid(0x8000_0000, 0) if number_of_extended_ids < 0x8000_0004 { return diff --git a/core/sys/info/cpu_linux_arm.odin b/core/sys/info/cpu_linux_arm.odin index cde76a83d..6e8b1a634 100644 --- a/core/sys/info/cpu_linux_arm.odin +++ b/core/sys/info/cpu_linux_arm.odin @@ -2,11 +2,13 @@ #+build linux package sysinfo +import "base:runtime" import "core:sys/linux" import "core:strings" @(init, private) -init_cpu_features :: proc() { +init_cpu_features :: proc "contextless" () { + context = runtime.default_context() fd, err := linux.open("/proc/cpuinfo", {}) if err != .NONE { return } defer linux.close(fd) diff --git a/core/sys/info/cpu_linux_intel.odin b/core/sys/info/cpu_linux_intel.odin index e43737475..af76a75e4 100644 --- a/core/sys/info/cpu_linux_intel.odin +++ b/core/sys/info/cpu_linux_intel.odin @@ -2,12 +2,15 @@ #+build linux package sysinfo +import "base:runtime" import "core:sys/linux" import "core:strings" import "core:strconv" @(init, private) -init_cpu_core_count :: proc() { +init_cpu_core_count :: proc "contextless" () { + context = runtime.default_context() + fd, err := linux.open("/proc/cpuinfo", {}) if err != .NONE { return } defer linux.close(fd) diff --git a/core/sys/info/cpu_windows.odin b/core/sys/info/cpu_windows.odin index 7dd2d2a8c..72d79f9a7 100644 --- a/core/sys/info/cpu_windows.odin +++ b/core/sys/info/cpu_windows.odin @@ -2,9 +2,12 @@ package sysinfo import sys "core:sys/windows" import "base:intrinsics" +import "base:runtime" @(init, private) -init_cpu_core_count :: proc() { +init_cpu_core_count :: proc "contextless" () { + context = runtime.default_context() + infos: []sys.SYSTEM_LOGICAL_PROCESSOR_INFORMATION defer delete(infos) diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index 3fc8064ec..07c26ec28 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -1,5 +1,7 @@ package sysinfo +import "base:runtime" + import "core:strconv" import "core:strings" import "core:sys/unix" @@ -9,7 +11,8 @@ import NS "core:sys/darwin/Foundation" version_string_buf: [1024]u8 @(init, private) -init_platform :: proc() { +init_platform :: proc "contextless" () { + context = runtime.default_context() ws :: strings.write_string wi :: strings.write_int diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 9c342e567..43cd580c1 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -1,6 +1,7 @@ package sysinfo import "base:intrinsics" +import "base:runtime" import "core:strconv" import "core:strings" @@ -10,7 +11,9 @@ import "core:sys/linux" version_string_buf: [1024]u8 @(init, private) -init_os_version :: proc () { +init_os_version :: proc "contextless" () { + context = runtime.default_context() + os_version.platform = .Linux b := strings.builder_from_bytes(version_string_buf[:]) @@ -91,11 +94,11 @@ init_os_version :: proc () { } @(init, private) -init_ram :: proc() { +init_ram :: proc "contextless" () { // Retrieve RAM info using `sysinfo` sys_info: linux.Sys_Info errno := linux.sysinfo(&sys_info) - assert(errno == .NONE, "Good luck to whoever's debugging this, something's seriously cucked up!") + assert_contextless(errno == .NONE, "Good luck to whoever's debugging this, something's seriously cucked up!") ram = RAM{ total_ram = int(sys_info.totalram) * int(sys_info.mem_unit), free_ram = int(sys_info.freeram) * int(sys_info.mem_unit), diff --git a/core/sys/info/platform_windows.odin b/core/sys/info/platform_windows.odin index dd1441d30..ff8ebe2ee 100644 --- a/core/sys/info/platform_windows.odin +++ b/core/sys/info/platform_windows.odin @@ -12,7 +12,9 @@ import "base:runtime" version_string_buf: [1024]u8 @(init, private) -init_os_version :: proc () { +init_os_version :: proc "contextless" () { + context = runtime.default_context() + /* NOTE(Jeroen): `GetVersionEx` will return 6.2 for Windows 10 unless the program is manifested for Windows 10. @@ -43,6 +45,7 @@ init_os_version :: proc () { 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 ") @@ -259,7 +262,7 @@ init_os_version :: proc () { } @(init, private) -init_ram :: proc() { +init_ram :: proc "contextless" () { state: sys.MEMORYSTATUSEX state.dwLength = size_of(state) @@ -276,10 +279,11 @@ init_ram :: proc() { } @(init, private) -init_gpu_info :: proc() { - +init_gpu_info :: proc "contextless" () { GPU_INFO_BASE :: "SYSTEM\\ControlSet001\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\" + context = runtime.default_context() + gpu_list: [dynamic]GPU gpu_index: int diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index 10dc907e7..125038ac4 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -628,7 +628,7 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P } } -ensure_winsock_initialized :: proc() { +ensure_winsock_initialized :: proc "contextless" () { @static gate := false @static initted := false @@ -644,7 +644,7 @@ ensure_winsock_initialized :: proc() { unused_info: WSADATA version_requested := WORD(2) << 8 | 2 res := WSAStartup(version_requested, &unused_info) - assert(res == 0, "unable to initialized Winsock2") + assert_contextless(res == 0, "unable to initialized Winsock2") initted = true } diff --git a/core/terminal/internal.odin b/core/terminal/internal.odin index 44007e14f..9404ff833 100644 --- a/core/terminal/internal.odin +++ b/core/terminal/internal.odin @@ -1,6 +1,7 @@ #+private package terminal +import "base:runtime" import "core:os" import "core:strings" @@ -68,9 +69,11 @@ get_environment_color :: proc() -> Color_Depth { } @(init) -init_terminal :: proc() { +init_terminal :: proc "contextless" () { _init_terminal() + context = runtime.default_context() + // We respect `NO_COLOR` specifically as a color-disabler but not as a // blanket ban on any terminal manipulation codes, hence why this comes // after `_init_terminal` which will allow Windows to enable Virtual @@ -81,6 +84,6 @@ init_terminal :: proc() { } @(fini) -fini_terminal :: proc() { +fini_terminal :: proc "contextless" () { _fini_terminal() } diff --git a/core/terminal/terminal_js.odin b/core/terminal/terminal_js.odin index 2d880420b..4dcd4465e 100644 --- a/core/terminal/terminal_js.odin +++ b/core/terminal/terminal_js.odin @@ -4,12 +4,12 @@ package terminal import "core:os" -_is_terminal :: proc(handle: os.Handle) -> bool { +_is_terminal :: proc "contextless" (handle: os.Handle) -> bool { return true } -_init_terminal :: proc() { +_init_terminal :: proc "contextless" () { color_depth = .None } -_fini_terminal :: proc() { } \ No newline at end of file +_fini_terminal :: proc "contextless" () { } \ No newline at end of file diff --git a/core/terminal/terminal_posix.odin b/core/terminal/terminal_posix.odin index f578e12c6..8d96dd256 100644 --- a/core/terminal/terminal_posix.odin +++ b/core/terminal/terminal_posix.odin @@ -2,15 +2,17 @@ #+build linux, darwin, netbsd, openbsd, freebsd, haiku package terminal +import "base:runtime" import "core:os" import "core:sys/posix" -_is_terminal :: proc(handle: os.Handle) -> bool { +_is_terminal :: proc "contextless" (handle: os.Handle) -> bool { return bool(posix.isatty(posix.FD(handle))) } -_init_terminal :: proc() { +_init_terminal :: proc "contextless" () { + context = runtime.default_context() color_depth = get_environment_color() } -_fini_terminal :: proc() { } +_fini_terminal :: proc "contextless" () { } diff --git a/core/terminal/terminal_windows.odin b/core/terminal/terminal_windows.odin index 18ec98332..6d5f98a1f 100644 --- a/core/terminal/terminal_windows.odin +++ b/core/terminal/terminal_windows.odin @@ -1,10 +1,11 @@ #+private package terminal +import "base:runtime" import "core:os" import "core:sys/windows" -_is_terminal :: proc(handle: os.Handle) -> bool { +_is_terminal :: proc "contextless" (handle: os.Handle) -> bool { is_tty := windows.GetFileType(windows.HANDLE(handle)) == windows.FILE_TYPE_CHAR return is_tty } @@ -18,7 +19,7 @@ old_modes: [2]struct{ } @(init) -_init_terminal :: proc() { +_init_terminal :: proc "contextless" () { vtp_enabled: bool for &v in old_modes { @@ -42,13 +43,15 @@ _init_terminal :: proc() { // This color depth is available on Windows 10 since build 10586. color_depth = .Four_Bit } else { + context = runtime.default_context() + // The user may be on a non-default terminal emulator. color_depth = get_environment_color() } } @(fini) -_fini_terminal :: proc() { +_fini_terminal :: proc "contextless" () { for v in old_modes { handle := windows.GetStdHandle(v.handle) if handle == windows.INVALID_HANDLE || handle == nil { diff --git a/src/checker.cpp b/src/checker.cpp index a13290750..f631a1412 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2675,6 +2675,10 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st is_init = false; } + if (t->Proc.calling_convention != ProcCC_Contextless) { + error(e->token, "@(init) procedures must be declared as \"contextless\""); + } + if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { error(e->token, "@(init) procedures must be declared at the file scope"); is_init = false; @@ -2689,6 +2693,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st error(e->token, "An @(init) procedure must not use a blank identifier as its name"); } + if (is_init) { add_dependency_to_set(c, e); array_add(&c->info.init_procedures, e); @@ -2706,6 +2711,10 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st is_fini = false; } + if (t->Proc.calling_convention != ProcCC_Contextless) { + error(e->token, "@(fini) procedures must be declared as \"contextless\""); + } + if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { error(e->token, "@(fini) procedures must be declared at the file scope"); is_fini = false; diff --git a/vendor/miniaudio/common.odin b/vendor/miniaudio/common.odin index 0263278bc..e675cb7f6 100644 --- a/vendor/miniaudio/common.odin +++ b/vendor/miniaudio/common.odin @@ -25,7 +25,7 @@ BINDINGS_VERSION :: [3]u32{BINDINGS_VERSION_MAJOR, BINDINGS_VERSION_MIN BINDINGS_VERSION_STRING :: "0.11.22" @(init) -version_check :: proc() { +version_check :: proc "contextless" () { v: [3]u32 version(&v.x, &v.y, &v.z) if v != BINDINGS_VERSION { @@ -43,7 +43,7 @@ version_check :: proc() { n += copy(buf[n:], "and executing `make`") } - panic(string(buf[:n])) + panic_contextless(string(buf[:n])) } } From 8b860da2eb5f65ac23702d7f139da46646ba2ec1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:12:08 +0100 Subject: [PATCH 122/160] Remove unused import --- core/math/big/internal.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 8b176e7c0..e0bc1ae06 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -29,7 +29,6 @@ package math_big import "base:builtin" import "base:intrinsics" -import "base:runtime" import "core:mem" import rnd "core:math/rand" From b86932c03c51efbb6c6146f8c91dd03bf82ab226 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:21:50 +0100 Subject: [PATCH 123/160] Disallow `proc "odin"` procedures to be called in the global scope in variable declarations --- src/check_decl.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index af46ee40e..ee7906e5e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1851,6 +1851,12 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, c.scope = d->scope; c.decl = d; c.type_level = 0; + c.curr_proc_calling_convention = ProcCC_Contextless; + + auto prev_flags = c.scope->flags; + defer (c.scope->flags = prev_flags); + c.scope->flags &= ~ScopeFlag_ContextDefined; + e->parent_proc_decl = c.curr_proc_decl; e->state = EntityState_InProgress; From 06e48099ca808dc9d5b3fbcaebebd4123f097a86 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:23:22 +0100 Subject: [PATCH 124/160] Improve error message --- src/check_expr.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index faa338f36..e3881986b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8166,8 +8166,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) { if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { ERROR_BLOCK(); - error(call, "'context' has not been defined within this scope, but is required for this procedure call"); - error_line("\tSuggestion: 'context = runtime.default_context()'"); + if (c->scope->flags & ScopeFlag_File) { + error(call, "Procedures requiring a 'context' cannot be called at the global scope"); + } else { + error(call, "'context' has not been defined within this scope, but is required for this procedure call"); + error_line("\tSuggestion: 'context = runtime.default_context()'"); + } } } From d3f649d244529d93b489140e5c7a1aaff97bedb2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:25:58 +0100 Subject: [PATCH 125/160] Make `get_args` contextless --- core/os/os2/process.odin | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 3c84f3539..635befc64 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -16,7 +16,8 @@ Arguments to the current process. args := get_args() @(private="file") -get_args :: proc() -> []string { +get_args :: proc "contextless" () -> []string { + context = runtime.default_context() result := make([]string, len(runtime.args__), heap_allocator()) for rt_arg, i in runtime.args__ { result[i] = string(rt_arg) @@ -24,6 +25,12 @@ get_args :: proc() -> []string { return result } +@(fini, private="file") +delete_args :: proc "contextless" () { + context = runtime.default_context() + delete(args, heap_allocator()) +} + /* Exit the current process. */ From d381d0ece4b9cd5fcf454fbd7ca79969d2b76cb8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:33:34 +0100 Subject: [PATCH 126/160] Fix more procedure to be `contextless` --- core/flags/rtti.odin | 2 +- core/sys/posix/stdlib_libc.odin | 2 +- tests/core/flags/test_core_flags.odin | 2 +- tests/core/normal.odin | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/flags/rtti.odin b/core/flags/rtti.odin index ce7a23773..058292698 100644 --- a/core/flags/rtti.odin +++ b/core/flags/rtti.odin @@ -38,6 +38,6 @@ Note that only one can be active at a time. Inputs: - setter: The type setter. Pass `nil` to disable any previously set setter. */ -register_type_setter :: proc(setter: Custom_Type_Setter) { +register_type_setter :: proc "contextless" (setter: Custom_Type_Setter) { global_custom_type_setter = setter } diff --git a/core/sys/posix/stdlib_libc.odin b/core/sys/posix/stdlib_libc.odin index e31c51704..966dc1d32 100644 --- a/core/sys/posix/stdlib_libc.odin +++ b/core/sys/posix/stdlib_libc.odin @@ -60,7 +60,7 @@ wctomb :: libc.wctomb mbstowcs :: libc.mbstowcs wcstombs :: libc.wcstombs -free :: #force_inline proc(ptr: $T) where intrinsics.type_is_pointer(T) || intrinsics.type_is_multi_pointer(T) || T == cstring { +free :: #force_inline proc "c" (ptr: $T) where intrinsics.type_is_pointer(T) || intrinsics.type_is_multi_pointer(T) || T == cstring { libc.free(rawptr(ptr)) } diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index 0527d85c5..0cfcf8e75 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -17,7 +17,7 @@ Custom_Data :: struct { } @(init) -init_custom_type_setter :: proc() { +init_custom_type_setter :: proc "contextless" () { // NOTE: This is done here so it can be out of the flow of the // multi-threaded test runner, to prevent any data races that could be // reported by using `-sanitize:thread`. diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 5bc73bd24..a64f38209 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -2,10 +2,10 @@ package tests_core import rlibc "core:c/libc" -@(init) +@(init="contextless") download_assets :: proc() { if rlibc.system("python3 " + ODIN_ROOT + "tests/core/download_assets.py " + ODIN_ROOT + "tests/core/assets") != 0 { - panic("downloading test assets failed!") + panic_contextless("downloading test assets failed!") } } From 414b1059a80b6fd96e839d761c71338a60b697ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:36:56 +0100 Subject: [PATCH 127/160] Fix typo --- tests/core/normal.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/normal.odin b/tests/core/normal.odin index a64f38209..fe69acf64 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -2,8 +2,8 @@ package tests_core import rlibc "core:c/libc" -@(init="contextless") -download_assets :: proc() { +@(init) +download_assets :: proc "contextless" () { if rlibc.system("python3 " + ODIN_ROOT + "tests/core/download_assets.py " + ODIN_ROOT + "tests/core/assets") != 0 { panic_contextless("downloading test assets failed!") } From 0c7260e7010461f1321c9c3f7d50f2f35f89abb1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:37:36 +0100 Subject: [PATCH 128/160] Update src/main.cpp Co-authored-by: Itzik Shneorson <35134559+itziksn@users.noreply.github.com> --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2f0b1c19b..06b9cab94 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2583,7 +2583,7 @@ gb_internal int print_show_help(String const arg0, String command, String option } if (check) { - if (print_flag("-integer-division-by-zero:")) { + if (print_flag("-integer-division-by-zero:")) { print_usage_line(2, "Specifies the default behaviour for integer division by zero."); print_usage_line(2, "Available Options:"); print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero"); From 78290ceef38e7fff9b605c168eed5f24d65895c2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:38:20 +0100 Subject: [PATCH 129/160] Add contextless to another proc --- core/os/os2/file_linux.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index b1d11b425..92f0c1541 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -45,8 +45,8 @@ _stderr := File{ } @init -_standard_stream_init :: proc() { - new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File { +_standard_stream_init :: proc "contextless" () { + new_std :: proc "contextless" (impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File { impl.file.impl = impl impl.fd = linux.Fd(fd) impl.allocator = runtime.nil_allocator() From a0462aa8d7bd622d70d8c0d4a25dddbe081dc86d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:49:24 +0100 Subject: [PATCH 130/160] Fix test for global procedure initialization --- tests/internal/test_global_any.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/internal/test_global_any.odin b/tests/internal/test_global_any.odin index 73b70e0a4..850884912 100644 --- a/tests/internal/test_global_any.odin +++ b/tests/internal/test_global_any.odin @@ -3,7 +3,7 @@ package test_internal @(private="file") global_any_from_proc: any = from_proc() -from_proc :: proc() -> f32 { +from_proc :: proc "contextless" () -> f32 { return 1.1 } From aa747a1c10c3b20c589e610d1f7dffe4ddaff4fa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 12:55:23 +0100 Subject: [PATCH 131/160] Add more "contextless" --- core/sys/info/platform_bsd.odin | 6 ++++-- core/sys/info/platform_freebsd.odin | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/sys/info/platform_bsd.odin b/core/sys/info/platform_bsd.odin index 6bb32cd3d..2f8d7f5bb 100644 --- a/core/sys/info/platform_bsd.odin +++ b/core/sys/info/platform_bsd.odin @@ -10,7 +10,9 @@ import "base:runtime" version_string_buf: [1024]u8 @(init, private) -init_os_version :: proc () { +init_os_version :: proc "contextless" () { + context = runtime.default_context() + when ODIN_OS == .NetBSD { os_version.platform = .NetBSD } else { @@ -66,7 +68,7 @@ init_os_version :: proc () { } @(init, private) -init_ram :: proc() { +init_ram :: proc "contextless" () { // Retrieve RAM info using `sysctl` mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM64} mem_size: u64 diff --git a/core/sys/info/platform_freebsd.odin b/core/sys/info/platform_freebsd.odin index b26fb7875..eb39769de 100644 --- a/core/sys/info/platform_freebsd.odin +++ b/core/sys/info/platform_freebsd.odin @@ -9,7 +9,9 @@ import "base:runtime" version_string_buf: [1024]u8 @(init, private) -init_os_version :: proc () { +init_os_version :: proc "contextless" () { + context = runtime.default_context() + os_version.platform = .FreeBSD kernel_version_buf: [1024]u8 @@ -68,7 +70,7 @@ init_os_version :: proc () { } @(init, private) -init_ram :: proc() { +init_ram :: proc "contextless" () { // Retrieve RAM info using `sysctl` mib := []i32{sys.CTL_HW, sys.HW_PHYSMEM} mem_size: u64 From 8ff15eaf6905eb167dc541cd2558599a0a334c28 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 13:02:37 +0100 Subject: [PATCH 132/160] More contextless fixes --- core/os/os2/process_posix_darwin.odin | 2 +- core/os/os_wasi.odin | 13 +++++++------ core/sys/info/cpu_linux_riscv64.odin | 8 ++++---- core/sys/unix/sysctl_freebsd.odin | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/os/os2/process_posix_darwin.odin b/core/os/os2/process_posix_darwin.odin index 7625e513a..f655d42a9 100644 --- a/core/os/os2/process_posix_darwin.odin +++ b/core/os/os2/process_posix_darwin.odin @@ -13,7 +13,7 @@ import "core:time" foreign import lib "system:System" foreign lib { - sysctl :: proc( + sysctl :: proc "c" ( name: [^]i32, namelen: u32, oldp: rawptr, oldlenp: ^uint, newp: rawptr, newlen: uint, diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index 53c242a01..fe0a1fb3e 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -30,11 +30,11 @@ args := _alloc_command_line_arguments() @(private, require_results) _alloc_command_line_arguments :: proc "contextless" () -> []string { context = runtime.default_context() - args := make([]string, len(runtime.args__)) - for &arg, i in args { + cmd_args := make([]string, len(runtime.args__)) + for &arg, i in cmd_args { arg = string(runtime.args__[i]) } - return args + return cmd_args } @(private, fini) @@ -59,9 +59,8 @@ Preopen :: struct { preopens: []Preopen @(init, private) -init_preopens :: proc() { - - strip_prefixes :: proc(path: string) -> string { +init_preopens :: proc "contextless" () { + strip_prefixes :: proc "contextless"(path: string) -> string { path := path loop: for len(path) > 0 { switch { @@ -78,6 +77,8 @@ init_preopens :: proc() { return path } + context = runtime.default_context() + dyn_preopens: [dynamic]Preopen loop: for fd := wasi.fd_t(3); ; fd += 1 { desc, err := wasi.fd_prestat_get(fd) diff --git a/core/sys/info/cpu_linux_riscv64.odin b/core/sys/info/cpu_linux_riscv64.odin index 3d36d126d..39ed69240 100644 --- a/core/sys/info/cpu_linux_riscv64.odin +++ b/core/sys/info/cpu_linux_riscv64.odin @@ -7,7 +7,7 @@ import "base:intrinsics" import "core:sys/linux" @(init, private) -init_cpu_features :: proc() { +init_cpu_features :: proc "contextless" () { _features: CPU_Features defer cpu.features = _features @@ -85,7 +85,7 @@ init_cpu_features :: proc() { return } - assert(pairs[0].key == .IMA_EXT_0) + assert_contextless(pairs[0].key == .IMA_EXT_0) exts := pairs[0].value.ima_ext_0 exts -= { .FD, .C, .V } _features += transmute(CPU_Features)exts @@ -97,7 +97,7 @@ init_cpu_features :: proc() { _features += { .Misaligned_Supported } } } else { - assert(pairs[1].key == .CPUPERF_0) + assert_contextless(pairs[1].key == .CPUPERF_0) if .FAST in pairs[1].value.cpu_perf_0 { _features += { .Misaligned_Supported, .Misaligned_Fast } } else if .UNSUPPORTED not_in pairs[1].value.cpu_perf_0 { @@ -108,6 +108,6 @@ init_cpu_features :: proc() { } @(init, private) -init_cpu_name :: proc() { +init_cpu_name :: proc "contextless" () { cpu.name = "RISCV64" } diff --git a/core/sys/unix/sysctl_freebsd.odin b/core/sys/unix/sysctl_freebsd.odin index f5fee6c6c..cdd591a5b 100644 --- a/core/sys/unix/sysctl_freebsd.odin +++ b/core/sys/unix/sysctl_freebsd.odin @@ -3,7 +3,7 @@ package unix import "base:intrinsics" -sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { +sysctl :: proc "contextless" (mib: []i32, val: ^$T) -> (ok: bool) { mib := mib result_size := u64(size_of(T)) From 7c93a5ae802b5a17aff6fea7368f9b27deed6517 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 13:16:20 +0100 Subject: [PATCH 133/160] Add missing `contextless` --- core/sys/info/cpu_linux_riscv64.odin | 2 +- core/sys/unix/sysctl_openbsd.odin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sys/info/cpu_linux_riscv64.odin b/core/sys/info/cpu_linux_riscv64.odin index 39ed69240..e65e8a3d2 100644 --- a/core/sys/info/cpu_linux_riscv64.odin +++ b/core/sys/info/cpu_linux_riscv64.odin @@ -81,7 +81,7 @@ init_cpu_features :: proc "contextless" () { } err := linux.riscv_hwprobe(raw_data(pairs), len(pairs), 0, nil, {}) if err != nil { - assert(err == .ENOSYS, "unexpected error from riscv_hwprobe()") + assert_contextless(err == .ENOSYS, "unexpected error from riscv_hwprobe()") return } diff --git a/core/sys/unix/sysctl_openbsd.odin b/core/sys/unix/sysctl_openbsd.odin index 49c9b6336..e71b743f8 100644 --- a/core/sys/unix/sysctl_openbsd.odin +++ b/core/sys/unix/sysctl_openbsd.odin @@ -9,7 +9,7 @@ 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) { +sysctl :: proc "contextless" (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) From 804984ed35faceee3e7629dff0c8d9df86db3d98 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 13:29:49 +0100 Subject: [PATCH 134/160] `contextless` fixes for `js` --- base/runtime/procs_js.odin | 4 ++-- vendor/libc/libc.odin | 4 ++-- vendor/libc/stdlib.odin | 2 +- vendor/wgpu/wgpu_js.odin | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/base/runtime/procs_js.odin b/base/runtime/procs_js.odin index 58bed808d..3690f9436 100644 --- a/base/runtime/procs_js.odin +++ b/base/runtime/procs_js.odin @@ -3,8 +3,8 @@ package runtime init_default_context_for_js: Context @(init, private="file") -init_default_context :: proc() { - init_default_context_for_js = context +init_default_context :: proc "contextless" () { + __init_context(&init_default_context_for_js) } @(export) diff --git a/vendor/libc/libc.odin b/vendor/libc/libc.odin index 00d687109..3ec25aacf 100644 --- a/vendor/libc/libc.odin +++ b/vendor/libc/libc.odin @@ -10,8 +10,8 @@ g_ctx: runtime.Context g_allocator: mem.Compat_Allocator @(init) -init_context :: proc() { - g_ctx = context +init_context :: proc "contextless" () { + g_ctx = runtime.default_context() // Wrapping the allocator with the mem.Compat_Allocator so we can // mimic the realloc semantics. diff --git a/vendor/libc/stdlib.odin b/vendor/libc/stdlib.odin index 9f578a436..bb9233a28 100644 --- a/vendor/libc/stdlib.odin +++ b/vendor/libc/stdlib.odin @@ -169,7 +169,7 @@ exit :: proc "c" (exit_code: c.int) -> ! { } @(private, fini) -finish_atexit :: proc "c" () { +finish_atexit :: proc "contextless" () { n := intrinsics.atomic_exchange(&atexit_functions_count, 0) for function in atexit_functions[:n] { function() diff --git a/vendor/wgpu/wgpu_js.odin b/vendor/wgpu/wgpu_js.odin index 3c8375adb..3217a97dc 100644 --- a/vendor/wgpu/wgpu_js.odin +++ b/vendor/wgpu/wgpu_js.odin @@ -5,7 +5,7 @@ import "base:runtime" g_context: runtime.Context @(private="file", init) -wgpu_init_allocator :: proc() { +wgpu_init_allocator :: proc "contextless" () { if g_context.allocator.procedure == nil { g_context = runtime.default_context() } From 0f5ced52a0f87187ac3099238c726d1230aaabbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 13:42:48 +0100 Subject: [PATCH 135/160] Assign `context` in scope --- vendor/libc/libc.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/vendor/libc/libc.odin b/vendor/libc/libc.odin index 3ec25aacf..a5508e14f 100644 --- a/vendor/libc/libc.odin +++ b/vendor/libc/libc.odin @@ -12,6 +12,7 @@ g_allocator: mem.Compat_Allocator @(init) init_context :: proc "contextless" () { g_ctx = runtime.default_context() + context = g_ctx // Wrapping the allocator with the mem.Compat_Allocator so we can // mimic the realloc semantics. From 443dd566e39721d3e5321212fcfc418d7a7d43fe Mon Sep 17 00:00:00 2001 From: xenobas Date: Fri, 8 Aug 2025 14:40:43 +0100 Subject: [PATCH 136/160] Add a better suggestion for cases that don't need ^ operator --- src/check_expr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index aa9c8837d..f314ce3cd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2455,7 +2455,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o } else if (is_type_pointer(o->type) && are_types_identical(type_deref(o->type), type)) { gbString s = expr_to_string(o->expr); - error_line("\tSuggestion: Did you mean `%s^`\n", s); + if (s[0] == '&') error_line("\tSuggestion: Did you mean `%s`\n", &s[1]); + else error_line("\tSuggestion: Did you mean `%s^`\n", s); gb_string_free(s); } } From 14ca1c8c89dd219dd52d061649d4db7778df2e64 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 8 Aug 2025 15:41:44 +0100 Subject: [PATCH 137/160] Add another `contextless` --- core/sys/unix/sysctl_netbsd.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sys/unix/sysctl_netbsd.odin b/core/sys/unix/sysctl_netbsd.odin index ad89b9ad4..b70740721 100644 --- a/core/sys/unix/sysctl_netbsd.odin +++ b/core/sys/unix/sysctl_netbsd.odin @@ -8,7 +8,7 @@ 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) { +sysctl :: proc "contextless" (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) From 5db6ea8354ec19b0800839d770f6216fde6027c5 Mon Sep 17 00:00:00 2001 From: janga-perlind Date: Sat, 9 Aug 2025 12:37:15 +1000 Subject: [PATCH 138/160] Do not check for explicit allocators when determining proc in proc group --- src/check_expr.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b4cfaaf00..6a50b553e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6147,7 +6147,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Entity *entity, Type *proc_type, Array positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, - CallArgumentData *data) { + CallArgumentData *data, + bool checking_proc_group) { TEMPORARY_ALLOCATOR_GUARD(); CallArgumentError err = CallArgumentError_None; @@ -6314,7 +6315,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A bool context_allocator_error = false; if (e->kind == Entity_Variable) { if (e->Variable.param_value.kind != ParameterValue_Invalid) { - if (ast_file_vet_explicit_allocators(c->file)) { + if (ast_file_vet_explicit_allocators(c->file) && !checking_proc_group) { // NOTE(lucas): check if we are trying to default to context.allocator or context.temp_allocator if (e->Variable.param_value.original_ast_expr->kind == Ast_SelectorExpr) { auto& expr = e->Variable.param_value.original_ast_expr->SelectorExpr.expr; @@ -6741,7 +6742,8 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera Entity *e, Type *proc_type, Array const &positional_operands, Array const &named_operands, CallArgumentErrorMode show_error_mode, - CallArgumentData *data) { + CallArgumentData *data, + bool checking_proc_group) { bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors; @@ -6765,7 +6767,7 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera } GB_ASSERT(proc_type->kind == Type_Proc); - CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data); + CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data, checking_proc_group); if (return_on_failure && err != CallArgumentError_None) { return false; } @@ -6919,7 +6921,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, e, e->type, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, - &data); + &data, false); } return data; } @@ -7063,7 +7065,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, p, pt, positional_operands, named_operands, CallArgumentErrorMode::NoErrors, - &data); + &data, true); if (!is_a_candidate) { continue; } @@ -7372,7 +7374,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, e, e->type, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, - &data); + &data, false); return data; } @@ -7485,7 +7487,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op nullptr, proc_type, positional_operands, named_operands, CallArgumentErrorMode::ShowErrors, - &data); + &data, false); } else if (pt) { data.result_type = pt->results; } From 277c6cac7175d78bd2e42db17e7ca7475ec6d574 Mon Sep 17 00:00:00 2001 From: Alex Riedl Date: Sat, 9 Aug 2025 10:47:07 -0500 Subject: [PATCH 139/160] Fix some webgl bindings --- core/sys/wasm/js/odin.js | 37 ++++++++++++++++++++ vendor/wasm/WebGL/webgl.odin | 66 ++++++++++++++++++++++++++++++----- vendor/wasm/WebGL/webgl2.odin | 2 +- 3 files changed, 96 insertions(+), 9 deletions(-) diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index 37a57a59d..1fbcc886e 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -392,6 +392,9 @@ class WebGLInterface { BindTexture: (target, texture) => { this.ctx.bindTexture(target, texture ? this.textures[texture] : null) }, + BindRenderbuffer: (target, renderbuffer) => { + this.ctx.bindRenderbuffer(target, renderbuffer ? this.renderbuffers[renderbuffer] : null) + }, BlendColor: (red, green, blue, alpha) => { this.ctx.blendColor(red, green, blue, alpha); }, @@ -809,6 +812,40 @@ class WebGLInterface { Uniform3i: (location, v0, v1, v2) => { this.ctx.uniform3i(this.uniforms[location], v0, v1, v2); }, Uniform4i: (location, v0, v1, v2, v3) => { this.ctx.uniform4i(this.uniforms[location], v0, v1, v2, v3); }, + Uniform1fv: (location, count, addr) => { + let array = this.mem.loadF32Array(addr, 1*count); + this.ctx.uniform1fv(this.uniforms[location], array); + }, + Uniform2fv: (location, count, addr) => { + let array = this.mem.loadF32Array(addr, 2*count); + this.ctx.uniform2fv(this.uniforms[location], array); + }, + Uniform3fv: (location, count, addr) => { + let array = this.mem.loadF32Array(addr, 3*count); + this.ctx.uniform3fv(this.uniforms[location], array); + }, + Uniform4fv: (location, count, addr) => { + let array = this.mem.loadF32Array(addr, 4*count); + this.ctx.uniform4fv(this.uniforms[location], array); + }, + + Uniform1iv: (location, count, addr) => { + let array = this.mem.loadI32Array(addr, 1*count); + this.ctx.uniform1iv(this.uniforms[location], array); + }, + Uniform2iv: (location, count, addr) => { + let array = this.mem.loadI32Array(addr, 2*count); + this.ctx.uniform2iv(this.uniforms[location], array); + }, + Uniform3iv: (location, count, addr) => { + let array = this.mem.loadI32Array(addr, 3*count); + this.ctx.uniform3iv(this.uniforms[location], array); + }, + Uniform4iv: (location, count, addr) => { + let array = this.mem.loadI32Array(addr, 4*count); + this.ctx.uniform4iv(this.uniforms[location], array); + }, + UniformMatrix2fv: (location, addr) => { let array = this.mem.loadF32Array(addr, 2*2); this.ctx.uniformMatrix2fv(this.uniforms[location], false, array); diff --git a/vendor/wasm/WebGL/webgl.odin b/vendor/wasm/WebGL/webgl.odin index 96d363ba2..5616f3660 100644 --- a/vendor/wasm/WebGL/webgl.odin +++ b/vendor/wasm/WebGL/webgl.odin @@ -52,6 +52,7 @@ foreign webgl { BindBuffer :: proc(target: Enum, buffer: Buffer) --- BindFramebuffer :: proc(target: Enum, framebuffer: Framebuffer) --- BindTexture :: proc(target: Enum, texture: Texture) --- + BindRenderbuffer :: proc(target: Enum, renderbuffer: Renderbuffer) --- BlendColor :: proc(red, green, blue, alpha: f32) --- BlendEquation :: proc(mode: Enum) --- BlendEquationSeparate :: proc(modeRGB: Enum, modeAlpha: Enum) --- @@ -172,14 +173,63 @@ foreign webgl { Viewport :: proc(x, y, w, h: i32) --- } -Uniform1fv :: proc "contextless" (location: i32, v: f32) { Uniform1f(location, v) } -Uniform2fv :: proc "contextless" (location: i32, v: glm.vec2) { Uniform2f(location, v.x, v.y) } -Uniform3fv :: proc "contextless" (location: i32, v: glm.vec3) { Uniform3f(location, v.x, v.y, v.z) } -Uniform4fv :: proc "contextless" (location: i32, v: glm.vec4) { Uniform4f(location, v.x, v.y, v.z, v.w) } -Uniform1iv :: proc "contextless" (location: i32, v: i32) { Uniform1i(location, v) } -Uniform2iv :: proc "contextless" (location: i32, v: glm.ivec2) { Uniform2i(location, v.x, v.y) } -Uniform3iv :: proc "contextless" (location: i32, v: glm.ivec3) { Uniform3i(location, v.x, v.y, v.z) } -Uniform4iv :: proc "contextless" (location: i32, v: glm.ivec4) { Uniform4i(location, v.x, v.y, v.z, v.w) } +Uniform1fv :: proc "contextless" (location: i32, v: []f32) { + foreign webgl { + @(link_name="Uniform1fv") + _Uniform1fv :: proc "contextless" (location: i32, count: int, value: [^]f32) --- + } + _Uniform1fv(location, len(v), &v[0]) +} +Uniform2fv :: proc "contextless" (location: i32, v: []glm.vec2) { + foreign webgl { + @(link_name="Uniform2fv") + _Uniform2fv :: proc "contextless" (location: i32, count: int, value: [^]f32) --- + } + _Uniform2fv(location, len(v), &v[0].x) +} +Uniform3fv :: proc "contextless" (location: i32, v: []glm.vec3) { + foreign webgl { + @(link_name="Uniform3fv") + _Uniform3fv :: proc "contextless" (location: i32, count: int, value: [^]f32) --- + } + _Uniform3fv(location, len(v), &v[0].x) +} +Uniform4fv :: proc "contextless" (location: i32, v: []glm.vec4) { + foreign webgl { + @(link_name="Uniform4fv") + _Uniform4fv :: proc "contextless" (location: i32, count: int, value: [^]f32) --- + } + _Uniform4fv(location, len(v), &v[0].x) +} + +Uniform1iv :: proc "contextless" (location: i32, v: []i32) { + foreign webgl { + @(link_name="Uniform1iv") + _Uniform1iv :: proc "contextless" (location: i32, count: int, value: [^]i32) --- + } + _Uniform1iv(location, len(v), &v[0]) +} +Uniform2iv :: proc "contextless" (location: i32, v: []glm.ivec2) { + foreign webgl { + @(link_name="Uniform2iv") + _Uniform2iv :: proc "contextless" (location: i32, count: int, value: [^]i32) --- + } + _Uniform2iv(location, len(v), &v[0].x) +} +Uniform3iv :: proc "contextless" (location: i32, v: []glm.ivec3) { + foreign webgl { + @(link_name="Uniform3iv") + _Uniform3iv :: proc "contextless" (location: i32, count: int, value: [^]i32) --- + } + _Uniform3iv(location, len(v), &v[0].x) +} +Uniform4iv :: proc "contextless" (location: i32, v: []glm.ivec4) { + foreign webgl { + @(link_name="Uniform4iv") + _Uniform4iv :: proc "contextless" (location: i32, count: int, value: [^]i32) --- + } + _Uniform4iv(location, len(v), &v[0].x) +} VertexAttrib1fv :: proc "contextless" (index: i32, v: f32) { VertexAttrib1f(index, v) } VertexAttrib2fv :: proc "contextless" (index: i32, v: glm.vec2){ VertexAttrib2f(index, v.x, v.y) } diff --git a/vendor/wasm/WebGL/webgl2.odin b/vendor/wasm/WebGL/webgl2.odin index 74f0534d7..66a739303 100644 --- a/vendor/wasm/WebGL/webgl2.odin +++ b/vendor/wasm/WebGL/webgl2.odin @@ -36,7 +36,7 @@ foreign webgl2 { /* Texture objects */ TexStorage3D :: proc(target: Enum, levels: i32, internalformat: Enum, width, height, depth: i32) --- TexImage3D :: proc(target: Enum, level: i32, internalformat: Enum, width, height, depth: i32, border: i32, format, type: Enum, size: int, data: rawptr) --- - TexSubImage3D :: proc(target: Enum, level: i32, xoffset, yoffset, width, height, depth: i32, format, type: Enum, size: int, data: rawptr) --- + TexSubImage3D :: proc(target: Enum, level: i32, xoffset, yoffset, zoffset, width, height, depth: i32, format, type: Enum, size: int, data: rawptr) --- CompressedTexImage3D :: proc(target: Enum, level: i32, internalformat: Enum, width, height, depth: i32, border: i32, imageSize: int, data: rawptr) --- CompressedTexSubImage3D :: proc(target: Enum, level: i32, xoffset, yoffset: i32, width, height, depth: i32, format: Enum, imageSize: int, data: rawptr) --- CopyTexSubImage3D :: proc(target: Enum, level: i32, xoffset, yoffset, zoffset: i32, x, y, width, height: i32) --- From 983f3ec423a6a424c0e64ed2a5de41edffc459ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 10 Aug 2025 15:03:30 +0100 Subject: [PATCH 140/160] Add `#+feature global-context` This allows to use of `context` in the global scope on a per file basis. --- src/build_settings.cpp | 7 +++++++ src/check_decl.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index a6dce5233..fad4bedaa 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -357,6 +357,9 @@ enum OptInFeatureFlags : u64 { OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<2, OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<3, + OptInFeatureFlag_GlobalContext = 1u<<4, + + OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|OptInFeatureFlag_IntegerDivisionByZero_Zero|OptInFeatureFlag_IntegerDivisionByZero_Self, }; @@ -374,6 +377,10 @@ u64 get_feature_flag_from_name(String const &name) { if (name == "integer-division-by-zero:self") { return OptInFeatureFlag_IntegerDivisionByZero_Self; } + + if (name == "global-context") { + return OptInFeatureFlag_GlobalContext; + } return OptInFeatureFlag_NONE; } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index ee7906e5e..b2522f24a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1855,7 +1855,12 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, auto prev_flags = c.scope->flags; defer (c.scope->flags = prev_flags); - c.scope->flags &= ~ScopeFlag_ContextDefined; + + if (check_feature_flags(ctx, d->decl_node) & OptInFeatureFlag_GlobalContext) { + c.scope->flags |= ScopeFlag_ContextDefined; + } else { + c.scope->flags &= ~ScopeFlag_ContextDefined; + } e->parent_proc_decl = c.curr_proc_decl; From ecb6b35da561bfdaf89c0d39865a93abc26c3ad0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 10 Aug 2025 15:29:53 +0100 Subject: [PATCH 141/160] Fix name canonicalization for named specialized forms --- src/name_canonicalization.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index e3090368a..6a4538e26 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -505,7 +505,13 @@ write_base_name: Type *params = nullptr; Entity *parent = type_get_polymorphic_parent(e->type, ¶ms); - if (parent && (parent->token.string == e->token.string)) { + if (parent && (e->token.string == parent->token.string)) { + // Check for `distinct` forms + type_writer_append(w, parent->token.string.text, parent->token.string.len); + write_canonical_params(w, params); + } else if (parent && string_starts_with(e->token.string, parent->token.string) && + string_contains_char(e->token.string, '(')) { + // Check for named specialization forms type_writer_append(w, parent->token.string.text, parent->token.string.len); write_canonical_params(w, params); } else { @@ -767,7 +773,6 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { case Type_Named: if (type->Named.type_name != nullptr) { write_canonical_entity_name(w, type->Named.type_name); - return; } else { type_writer_append(w, type->Named.name.text, type->Named.name.len); } From da76c743e9b7c88f2f15bbee1dba3b2ab6ebc0b0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 10 Aug 2025 17:42:49 +0100 Subject: [PATCH 142/160] Add shortcut for division by a constant --- src/llvm_backend_expr.cpp | 131 ++++++++++++++++++++++++++------------ 1 file changed, 89 insertions(+), 42 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index a29682874..13c6082fb 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1139,8 +1139,23 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L LLVMTypeRef type = LLVMTypeOf(rhs); GB_ASSERT(LLVMTypeOf(lhs) == type); + LLVMValueRef zero = LLVMConstNull(type); + auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p); + auto *call = is_signed ? LLVMBuildSDiv : LLVMBuildUDiv; + if (LLVMIsConstant(rhs)) { + if (LLVMIsNull(rhs)) { + switch (behaviour) { + case IntegerDivisionByZero_Self: + return lhs; + case IntegerDivisionByZero_Zero: + return zero; + } + } else { + return call(p->builder, lhs, rhs, ""); + } + } LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; @@ -1149,7 +1164,6 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L lbBlock *edge_case_block = lb_create_block(p, "div.edge"); lbBlock *done_block = lb_create_block(p, "div.done"); - LLVMValueRef zero = LLVMConstNull(type); LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); lbValue cond = {dem_check, t_untyped_bool}; @@ -1157,13 +1171,12 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L lb_start_block(p, safe_block); incoming_values[0] = call(p->builder, lhs, rhs, ""); - lb_emit_jump(p, done_block); lb_start_block(p, edge_case_block); - switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + switch (behaviour) { case IntegerDivisionByZero_Trap: lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); LLVMBuildUnreachable(p->builder); @@ -1181,7 +1194,7 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L LLVMValueRef res = incoming_values[0]; - switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + switch (behaviour) { case IntegerDivisionByZero_Trap: case IntegerDivisionByZero_Self: res = incoming_values[0]; @@ -1204,22 +1217,10 @@ gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValu LLVMTypeRef type = LLVMTypeOf(rhs); GB_ASSERT(LLVMTypeOf(lhs) == type); - LLVMValueRef incoming_values[2] = {}; - LLVMBasicBlockRef incoming_blocks[2] = {}; - - lbBlock *safe_block = lb_create_block(p, "div.safe"); - lbBlock *edge_case_block = lb_create_block(p, "div.edge"); - lbBlock *done_block = lb_create_block(p, "div.done"); - LLVMValueRef zero = LLVMConstNull(type); - LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); - lbValue cond = {dem_check, t_untyped_bool}; + auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p); - lb_emit_if(p, cond, safe_block, edge_case_block); - - lb_start_block(p, safe_block); - - { + auto const do_op = [&]() -> LLVMValueRef { LLVMTypeRef types[1] = {lb_type(p->module, platform_type)}; LLVMValueRef args[3] = { @@ -1227,15 +1228,42 @@ gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValu rhs, scale }; - incoming_values[0] = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + return lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types)); + }; + + if (LLVMIsConstant(rhs)) { + if (LLVMIsNull(rhs)) { + switch (behaviour) { + case IntegerDivisionByZero_Self: + return lhs; + case IntegerDivisionByZero_Zero: + return zero; + } + } else { + return do_op(); + } } + LLVMValueRef incoming_values[2] = {}; + LLVMBasicBlockRef incoming_blocks[2] = {}; + + lbBlock *safe_block = lb_create_block(p, "div.safe"); + lbBlock *edge_case_block = lb_create_block(p, "div.edge"); + lbBlock *done_block = lb_create_block(p, "div.done"); + + LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); + lbValue cond = {dem_check, t_untyped_bool}; + + lb_emit_if(p, cond, safe_block, edge_case_block); + + lb_start_block(p, safe_block); + incoming_values[0] = do_op(); lb_emit_jump(p, done_block); lb_start_block(p, edge_case_block); - switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + switch (behaviour) { case IntegerDivisionByZero_Trap: lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); LLVMBuildUnreachable(p->builder); @@ -1253,7 +1281,7 @@ gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValu LLVMValueRef res = incoming_values[0]; - switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + switch (behaviour) { case IntegerDivisionByZero_Trap: case IntegerDivisionByZero_Self: res = incoming_values[0]; @@ -1277,6 +1305,42 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV LLVMTypeRef type = LLVMTypeOf(rhs); GB_ASSERT(LLVMTypeOf(lhs) == type); + LLVMValueRef zero = LLVMConstNull(type); + auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p); + + auto const do_op = [&]() -> LLVMValueRef { + if (is_floored) { // %% + if (is_unsigned) { + return LLVMBuildURem(p->builder, lhs, rhs, ""); + } else { + LLVMValueRef a = LLVMBuildSRem(p->builder, lhs, rhs, ""); + LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs, ""); + LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs, ""); + return c; + } + } else { // % + if (is_unsigned) { + return LLVMBuildURem(p->builder, lhs, rhs, ""); + } else { + return LLVMBuildSRem(p->builder, lhs, rhs, ""); + } + } + }; + + if (LLVMIsConstant(rhs)) { + if (LLVMIsNull(rhs)) { + switch (behaviour) { + case IntegerDivisionByZero_Self: + return zero; + case IntegerDivisionByZero_Zero: + return lhs; + } + } else { + return do_op(); + } + } + + LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; @@ -1284,31 +1348,13 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV lbBlock *edge_case_block = lb_create_block(p, "div.edge"); lbBlock *done_block = lb_create_block(p, "div.done"); - LLVMValueRef zero = LLVMConstNull(type); LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); lbValue cond = {dem_check, t_untyped_bool}; lb_emit_if(p, cond, safe_block, edge_case_block); lb_start_block(p, safe_block); - - if (is_floored) { // %% - if (is_unsigned) { - incoming_values[0] = LLVMBuildURem(p->builder, lhs, rhs, ""); - } else { - LLVMValueRef a = LLVMBuildSRem(p->builder, lhs, rhs, ""); - LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs, ""); - LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs, ""); - incoming_values[0] = c; - } - } else { // % - if (is_unsigned) { - incoming_values[0] = LLVMBuildURem(p->builder, lhs, rhs, ""); - } else { - incoming_values[0] = LLVMBuildSRem(p->builder, lhs, rhs, ""); - } - } - + incoming_values[0] = do_op(); lb_emit_jump(p, done_block); lb_start_block(p, edge_case_block); @@ -1320,9 +1366,10 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV floored: r = a - b*floor(a/b) IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) + IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0) */ - switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + switch (behaviour) { case IntegerDivisionByZero_Trap: lb_call_intrinsic(p, "llvm.trap", nullptr, 0, nullptr, 0); LLVMBuildUnreachable(p->builder); @@ -1340,7 +1387,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV LLVMValueRef res = incoming_values[0]; - switch (lb_check_for_integer_division_by_zero_behaviour(p)) { + switch (behaviour) { case IntegerDivisionByZero_Trap: case IntegerDivisionByZero_Self: res = incoming_values[0]; From 4cbcb3ace7c0d066e8d5105e4b3582300f93d533 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 10 Aug 2025 17:53:07 +0100 Subject: [PATCH 143/160] Add shortcut for `unsigned_x/power_of_two` -> `unsigned_x >> log2(power_of_two)` --- src/common.cpp | 7 +++++++ src/llvm_backend_expr.cpp | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/common.cpp b/src/common.cpp index 53848cacf..5b007bf2c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -80,6 +80,13 @@ gb_internal gb_inline bool is_power_of_two(i64 x) { return !(x & (x-1)); } +gb_internal gb_inline bool is_power_of_two_u64(u64 x) { + if (x == 0) { + return false; + } + return !(x & (x-1)); +} + gb_internal int isize_cmp(isize x, isize y) { if (x < y) { return -1; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 13c6082fb..f922c6359 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1153,6 +1153,17 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L return zero; } } else { + if (!is_signed && lb_sizeof(type) <= 8) { + u64 v = cast(u64)LLVMConstIntGetZExtValue(rhs); + if (v == 1) { + return lhs; + } else if (is_power_of_two_u64(v)) { + u64 n = floor_log2(v); + LLVMValueRef bits = LLVMConstInt(type, n, false); + return LLVMBuildLShr(p->builder, lhs, bits, ""); + } + } + return call(p->builder, lhs, rhs, ""); } } From b6e0b4848116697fa86a46c0b06a5ceb3654d5c6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 10 Aug 2025 17:58:46 +0100 Subject: [PATCH 144/160] Rename block names from `div` to `mod`. --- src/llvm_backend_expr.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index f922c6359..dbbc6268a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1355,9 +1355,9 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; - lbBlock *safe_block = lb_create_block(p, "div.safe"); - lbBlock *edge_case_block = lb_create_block(p, "div.edge"); - lbBlock *done_block = lb_create_block(p, "div.done"); + lbBlock *safe_block = lb_create_block(p, "mod.safe"); + lbBlock *edge_case_block = lb_create_block(p, "mod.edge"); + lbBlock *done_block = lb_create_block(p, "mod.done"); LLVMValueRef dem_check = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, zero, ""); lbValue cond = {dem_check, t_untyped_bool}; From 8df69c95c3163562b6caf6c55651363c17c3f478 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 10 Aug 2025 18:29:08 +0100 Subject: [PATCH 145/160] Add `-integer-division-by-zero:all-bits` --- src/build_settings.cpp | 19 ++++++++++++++----- src/check_expr.cpp | 31 +++++++++++++++++++++++++------ src/llvm_backend_expr.cpp | 19 +++++++++++++++++++ src/main.cpp | 1 + 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fad4bedaa..4bee0ad4e 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -353,14 +353,18 @@ enum OptInFeatureFlags : u64 { OptInFeatureFlag_NONE = 0, OptInFeatureFlag_DynamicLiterals = 1u<<0, - OptInFeatureFlag_IntegerDivisionByZero_Trap = 1u<<1, - OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<2, - OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<3, + OptInFeatureFlag_GlobalContext = 1u<<1, - OptInFeatureFlag_GlobalContext = 1u<<4, + OptInFeatureFlag_IntegerDivisionByZero_Trap = 1u<<2, + OptInFeatureFlag_IntegerDivisionByZero_Zero = 1u<<3, + OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<4, + OptInFeatureFlag_IntegerDivisionByZero_AllBits = 1u<<5, - OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|OptInFeatureFlag_IntegerDivisionByZero_Zero|OptInFeatureFlag_IntegerDivisionByZero_Self, + OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap| + OptInFeatureFlag_IntegerDivisionByZero_Zero| + OptInFeatureFlag_IntegerDivisionByZero_Self| + OptInFeatureFlag_IntegerDivisionByZero_AllBits, }; @@ -377,6 +381,10 @@ u64 get_feature_flag_from_name(String const &name) { if (name == "integer-division-by-zero:self") { return OptInFeatureFlag_IntegerDivisionByZero_Self; } + if (name == "integer-division-by-zero:all-bits") { + return OptInFeatureFlag_IntegerDivisionByZero_AllBits; + } + if (name == "global-context") { return OptInFeatureFlag_GlobalContext; @@ -431,6 +439,7 @@ enum IntegerDivisionByZeroKind : u8 { IntegerDivisionByZero_Trap, IntegerDivisionByZero_Zero, IntegerDivisionByZero_Self, + IntegerDivisionByZero_AllBits, }; // This stores the information for the specify architecture of this build diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e0b6408e3..7020b4f4b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4377,12 +4377,23 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) && (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) { if (op.kind == Token_QuoEq) { - if (zero_behaviour == IntegerDivisionByZero_Zero) { + switch (zero_behaviour) { + case IntegerDivisionByZero_Zero: // x/0 == 0 x->value = b; - } else { + break; + case IntegerDivisionByZero_Self: // x/0 == x x->value = a; + break; + case IntegerDivisionByZero_AllBits: + // x/0 == 0b111...111 + if (is_type_untyped(x->type)) { + x->value = exact_value_i64(-1); + } else { + x->value = exact_unary_operator_value(Token_Xor, b, cast(i32)(8*type_size_of(x->type)), is_type_unsigned(x->type)); + } + break; } } else { /* @@ -4391,16 +4402,21 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ truncated: r = a - b*trunc(a/b) floored: r = a - b*floor(a/b) - IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) - IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0) + IFF a/0 == 0, then (a%0 == a) or (a%%0 == a) + IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0) + IFF a/0 == 0b111..., then (a%0 == a) or (a%%0 == a) */ - if (zero_behaviour == IntegerDivisionByZero_Zero) { + switch (zero_behaviour) { + case IntegerDivisionByZero_Zero: + case IntegerDivisionByZero_AllBits: // x%0 == x x->value = a; - } else { + break; + case IntegerDivisionByZero_Self: // x%0 == 0 x->value = b; + break; } } } else { @@ -9670,6 +9686,9 @@ gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(Checker if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Self) != 0) { return IntegerDivisionByZero_Self; } + if ((flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) != 0) { + return IntegerDivisionByZero_AllBits; + } return build_context.integer_division_by_zero_behaviour; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index dbbc6268a..ebc3ec158 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -305,6 +305,9 @@ gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_beha if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) { return IntegerDivisionByZero_Self; } + if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) { + return IntegerDivisionByZero_AllBits; + } } return build_context.integer_division_by_zero_behaviour; } @@ -1140,6 +1143,7 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L GB_ASSERT(LLVMTypeOf(lhs) == type); LLVMValueRef zero = LLVMConstNull(type); + LLVMValueRef all_bits = LLVMConstNot(zero); auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p); auto *call = is_signed ? LLVMBuildSDiv : LLVMBuildUDiv; @@ -1151,6 +1155,9 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L return lhs; case IntegerDivisionByZero_Zero: return zero; + case IntegerDivisionByZero_AllBits: + // return all_bits; + break; } } else { if (!is_signed && lb_sizeof(type) <= 8) { @@ -1198,6 +1205,9 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L case IntegerDivisionByZero_Self: incoming_values[1] = lhs; break; + case IntegerDivisionByZero_AllBits: + incoming_values[1] = all_bits; + break; } lb_emit_jump(p, done_block); @@ -1211,6 +1221,7 @@ gb_internal LLVMValueRef lb_integer_division(lbProcedure *p, LLVMValueRef lhs, L res = incoming_values[0]; break; case IntegerDivisionByZero_Zero: + case IntegerDivisionByZero_AllBits: res = LLVMBuildPhi(p->builder, type, ""); GB_ASSERT(p->curr_block->preds.count >= 2); @@ -1229,6 +1240,7 @@ gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValu GB_ASSERT(LLVMTypeOf(lhs) == type); LLVMValueRef zero = LLVMConstNull(type); + LLVMValueRef all_bits = LLVMConstNot(zero); auto behaviour = lb_check_for_integer_division_by_zero_behaviour(p); auto const do_op = [&]() -> LLVMValueRef { @@ -1285,6 +1297,9 @@ gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValu case IntegerDivisionByZero_Self: incoming_values[1] = lhs; break; + case IntegerDivisionByZero_AllBits: + incoming_values[1] = all_bits; + break; } lb_emit_jump(p, done_block); @@ -1298,6 +1313,7 @@ gb_internal LLVMValueRef lb_integer_division_intrinsics(lbProcedure *p, LLVMValu res = incoming_values[0]; break; case IntegerDivisionByZero_Zero: + case IntegerDivisionByZero_AllBits: res = LLVMBuildPhi(p->builder, type, ""); GB_ASSERT(p->curr_block->preds.count >= 2); @@ -1344,6 +1360,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV case IntegerDivisionByZero_Self: return zero; case IntegerDivisionByZero_Zero: + case IntegerDivisionByZero_AllBits: return lhs; } } else { @@ -1386,6 +1403,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV LLVMBuildUnreachable(p->builder); break; case IntegerDivisionByZero_Zero: + case IntegerDivisionByZero_AllBits: incoming_values[1] = lhs; break; case IntegerDivisionByZero_Self: @@ -1404,6 +1422,7 @@ gb_internal LLVMValueRef lb_integer_modulo(lbProcedure *p, LLVMValueRef lhs, LLV res = incoming_values[0]; break; case IntegerDivisionByZero_Zero: + case IntegerDivisionByZero_AllBits: res = LLVMBuildPhi(p->builder, type, ""); GB_ASSERT(p->curr_block->preds.count >= 2); diff --git a/src/main.cpp b/src/main.cpp index 06b9cab94..db4dee080 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2589,6 +2589,7 @@ gb_internal int print_show_help(String const arg0, String command, String option print_usage_line(3, "-integer-division-by-zero:trap Trap on division/modulo/remainder by zero"); print_usage_line(3, "-integer-division-by-zero:zero x/0 == 0 and x%%0 == x and x%%%%0 == x"); print_usage_line(3, "-integer-division-by-zero:self x/0 == x and x%%0 == 0 and x%%%%0 == 0"); + print_usage_line(3, "-integer-division-by-zero:all-bits x/0 == ~T(0) and x%%0 == x and x%%%%0 == x"); } } From 2648bd8fcdecc0a632dda59ed39e7f680f53c691 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 12 Aug 2025 09:51:16 +0100 Subject: [PATCH 146/160] Allow `#+feature global-context` for `@(init)` and `@(fini)` procedures as an interim measure --- src/checker.cpp | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index f631a1412..44e63b750 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -565,6 +565,26 @@ gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) { return 0; } +gb_internal u64 check_feature_flags(Entity *e) { + if (e == nullptr) { + return 0; + } + AstFile *file = nullptr; + if (e->file == nullptr) { + file = e->file; + } + if (file == nullptr) { + if (e->decl_info && e->decl_info->decl_node) { + file = e->decl_info->decl_node->file(); + } + } + if (file != nullptr && file->feature_flags_set) { + return file->feature_flags; + } + return 0; +} + + enum VettedEntityKind { VettedEntity_Invalid, @@ -2675,8 +2695,13 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st is_init = false; } - if (t->Proc.calling_convention != ProcCC_Contextless) { - error(e->token, "@(init) procedures must be declared as \"contextless\""); + u64 feature_flags = check_feature_flags(e); + if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) { + if (t->Proc.calling_convention != ProcCC_Contextless) { + ERROR_BLOCK(); + error(e->token, "@(init) procedures must be declared as \"contextless\""); + error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'"); + } } if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { @@ -2711,8 +2736,13 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st is_fini = false; } - if (t->Proc.calling_convention != ProcCC_Contextless) { - error(e->token, "@(fini) procedures must be declared as \"contextless\""); + u64 feature_flags = check_feature_flags(e); + if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) { + if (t->Proc.calling_convention != ProcCC_Contextless) { + ERROR_BLOCK(); + error(e->token, "@(fini) procedures must be declared as \"contextless\""); + error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'"); + } } if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { From b6a0606c98606b8963af94966e1c6d70da4cfcd4 Mon Sep 17 00:00:00 2001 From: Wrath Date: Tue, 12 Aug 2025 19:59:38 -0400 Subject: [PATCH 147/160] Added getaddrinfo flags --- core/sys/windows/types.odin | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index d5c9b1d20..904970589 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -3385,6 +3385,19 @@ FILE_ATTRIBUTE_TAG_INFO :: struct { ReparseTag: DWORD, } +// getaddrinfo flags https://learn.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-addrinfoa +AI_PASSIVE :: 0x01 +AI_CANONNAME :: 0x02 +AI_NUMERICHOST :: 0x04 +AI_ALL :: 0x0100 +AI_ADDRCONFIG :: 0x0400 +AI_V4MAPPED :: 0x0800 +AI_NON_AUTHORITATIVE :: 0x04000 +AI_SECURE :: 0x08000 +AI_RETURN_PREFERRED_NAMES :: 0x010000 +AI_FQDN :: 0x00020000 +AI_FILESERVER :: 0x00040000 + PADDRINFOEXW :: ^ADDRINFOEXW LPADDRINFOEXW :: ^ADDRINFOEXW ADDRINFOEXW :: struct { From df849891c1131fba12d170fb76eea626606c017b Mon Sep 17 00:00:00 2001 From: Xotchkass <78706300+Xotchkass@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:35:25 +0300 Subject: [PATCH 148/160] use runtime.random_generator_read_bytes in read procedure instead of filling it manually. --- core/math/rand/rand.odin | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index ece864f35..2fceee736 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -508,18 +508,8 @@ Possible Output: */ @(require_results) read :: proc(p: []byte, gen := context.random_generator) -> (n: int) { - pos := i8(0) - val := i64(0) - for n = 0; n < len(p); n += 1 { - if pos == 0 { - val = int63(gen) - pos = 7 - } - p[n] = byte(val) - val >>= 8 - pos -= 1 - } - return + if !runtime.random_generator_read_bytes(gen, p) {return 0} + return len(p) } /* From 2d5652dbd40e966d3373307e463f71b7dd1c37b0 Mon Sep 17 00:00:00 2001 From: Xotchkass <78706300+Xotchkass@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:39:18 +0300 Subject: [PATCH 149/160] remove private _random_u64 and move it's functionality into public uint64 --- core/math/rand/rand.odin | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 2fceee736..6b9a73395 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -56,13 +56,6 @@ query_info :: proc(gen := context.random_generator) -> Generator_Query_Info { } -@(private) -_random_u64 :: proc(gen := context.random_generator) -> (res: u64) { - ok := runtime.random_generator_read_ptr(gen, &res, size_of(res)) - assert(ok, "uninitialized gen/context.random_generator") - return -} - /* Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. @@ -84,7 +77,7 @@ Possible Output: */ @(require_results) -uint32 :: proc(gen := context.random_generator) -> (val: u32) { return u32(_random_u64(gen)) } +uint32 :: proc(gen := context.random_generator) -> (val: u32) {return u32(uint64(gen))} /* Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. @@ -107,7 +100,11 @@ Possible Output: */ @(require_results) -uint64 :: proc(gen := context.random_generator) -> (val: u64) { return _random_u64(gen) } +uint64 :: proc(gen := context.random_generator) -> (val: u64) { + ok := runtime.random_generator_read_ptr(gen, &val, size_of(val)) + assert(ok, "uninitialized gen/context.random_generator") + return +} /* Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. @@ -131,13 +128,13 @@ Possible Output: */ @(require_results) uint128 :: proc(gen := context.random_generator) -> (val: u128) { - a := u128(_random_u64(gen)) - b := u128(_random_u64(gen)) + a := u128(uint64(gen)) + b := u128(uint64(gen)) return (a<<64) | b } /* -Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. +Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. Returns: @@ -160,7 +157,7 @@ Possible Output: @(require_results) int31 :: proc(gen := context.random_generator) -> (val: i32) { return i32(uint32(gen) << 1 >> 1) } /* -Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. +Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. Returns: @@ -183,7 +180,7 @@ Possible Output: @(require_results) int63 :: proc(gen := context.random_generator) -> (val: i64) { return i64(uint64(gen) << 1 >> 1) } /* -Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. +Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. Returns: @@ -480,8 +477,8 @@ Possible Output: } /* -Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used. -Due to floating point precision there is no guarantee if the upper and lower bounds are inclusive/exclusive with the exact floating point value. +Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used. +Due to floating point precision there is no guarantee if the upper and lower bounds are inclusive/exclusive with the exact floating point value. Inputs: - p: The byte slice to fill @@ -513,7 +510,7 @@ read :: proc(p: []byte, gen := context.random_generator) -> (n: int) { } /* -Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used. +Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used. *Allocates Using Provided Allocator* @@ -556,7 +553,7 @@ perm :: proc(n: int, allocator := context.allocator, gen := context.random_gener } /* -Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used. +Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used. Inputs: - array: The slice to randomize @@ -597,7 +594,7 @@ shuffle :: proc(array: $T/[]$E, gen := context.random_generator) { } /* -Returns a random element from the provided slice. If no generator is provided the global random number generator will be used. +Returns a random element from the provided slice. If no generator is provided the global random number generator will be used. Inputs: - array: The slice to choose an element from From d60fba13df171fb0fb1e9cacf942ac83d1f56bd9 Mon Sep 17 00:00:00 2001 From: Xotchkass <78706300+Xotchkass@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:49:59 +0300 Subject: [PATCH 150/160] fix: throwing away the last generated byte in default_random_generator_proc --- base/runtime/random_generator.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 81432b330..ca5c008d0 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -97,7 +97,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, for &v in p { if pos == 0 { val = read_u64(r) - pos = 7 + pos = 8 } v = byte(val) val >>= 8 From 88dba1194ac9f2becffc5cae201de93d2fbc9405 Mon Sep 17 00:00:00 2001 From: diniamo Date: Thu, 14 Aug 2025 14:59:47 +0200 Subject: [PATCH 151/160] Fix ITimer_Flags_Bits enum value --- core/sys/linux/bits.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin index f9c4ec22e..64cdd2208 100644 --- a/core/sys/linux/bits.odin +++ b/core/sys/linux/bits.odin @@ -1838,7 +1838,7 @@ Clock_Id :: enum { Bits for POSIX interval timer flags. */ ITimer_Flags_Bits :: enum { - ABSTIME = 1, + ABSTIME = 0, } /* From bc78aad45fdde11b21b6c6c4c102ca65f78c20c4 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Thu, 14 Aug 2025 09:56:36 -0400 Subject: [PATCH 152/160] Fix typo in newBufferWithSliceNoCopy --- vendor/darwin/Metal/MetalClasses.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/darwin/Metal/MetalClasses.odin b/vendor/darwin/Metal/MetalClasses.odin index a4c7b43f2..67cf84f1e 100644 --- a/vendor/darwin/Metal/MetalClasses.odin +++ b/vendor/darwin/Metal/MetalClasses.odin @@ -5581,7 +5581,7 @@ Device_newBufferWithSlice :: #force_inline proc "c" (self: ^Device, slice: $S/[] } @(objc_type=Device, objc_name="newBufferWithSliceNoCopy") Device_newBufferWithSliceNoCopy :: #force_inline proc "c" (self: ^Device, slice: $S/[]$E, options: ResourceOptions, deallocator: rawptr) -> ^Buffer { - return Device_newBufferWithBytesNotCopy(self, mem.slice_to_bytes(slice), options, deallocator) + return Device_newBufferWithBytesNoCopy(self, mem.slice_to_bytes(slice), options, deallocator) } @(objc_type=Device, objc_name="newBufferWithLength") Device_newBufferWithLength :: #force_inline proc "c" (self: ^Device, length: NS.UInteger, options: ResourceOptions) -> ^Buffer { From c4102701fdcf42321a204e0b506706e70219a3b9 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Fri, 15 Aug 2025 11:04:00 -0500 Subject: [PATCH 153/160] misnamed parameter --- vendor/OpenGL/wrappers.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 1eb8fc72f..3f4593b45 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -880,7 +880,7 @@ when !GL_DEBUG { // VERSION_1_2 DrawRangeElements :: proc "c" (mode, start, end: u32, count: i32, type: u32, indices: rawptr, loc := #caller_location) { impl_DrawRangeElements(mode, start, end, count, type, indices); debug_helper(loc, 0, mode, start, end, count, type, indices) } - TexImage3D :: proc "c" (target: u32, level, internalformat, width, height, depth, border: i32, format, type: u32, pixels: rawptr, loc := #caller_location) { impl_TexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels); debug_helper(loc, 0, target, level, internalformat, width, height, depth, border, format, type, pixels) } + TexImage3D :: proc "c" (target: u32, level, internalformat, width, height, depth, border: i32, format, type: u32, data: rawptr, loc := #caller_location) { impl_TexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels); debug_helper(loc, 0, target, level, internalformat, width, height, depth, border, format, type, data) } TexSubImage3D :: proc "c" (target: u32, level, xoffset, yoffset, zoffset, width, height, depth: i32, format, type: u32, pixels: rawptr, loc := #caller_location) { impl_TexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); debug_helper(loc, 0, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) } CopyTexSubImage3D :: proc "c" (target: u32, level, xoffset, yoffset, zoffset, x, y, width, height: i32, loc := #caller_location) { impl_CopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); debug_helper(loc, 0, target, level, xoffset, yoffset, zoffset, x, y, width, height) } From fbac18ec008cc555262ff7380343cf229f2f5d34 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Fri, 15 Aug 2025 11:12:30 -0500 Subject: [PATCH 154/160] missed a spot --- vendor/OpenGL/wrappers.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/OpenGL/wrappers.odin b/vendor/OpenGL/wrappers.odin index 3f4593b45..d68efee6c 100644 --- a/vendor/OpenGL/wrappers.odin +++ b/vendor/OpenGL/wrappers.odin @@ -880,7 +880,7 @@ when !GL_DEBUG { // VERSION_1_2 DrawRangeElements :: proc "c" (mode, start, end: u32, count: i32, type: u32, indices: rawptr, loc := #caller_location) { impl_DrawRangeElements(mode, start, end, count, type, indices); debug_helper(loc, 0, mode, start, end, count, type, indices) } - TexImage3D :: proc "c" (target: u32, level, internalformat, width, height, depth, border: i32, format, type: u32, data: rawptr, loc := #caller_location) { impl_TexImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels); debug_helper(loc, 0, target, level, internalformat, width, height, depth, border, format, type, data) } + TexImage3D :: proc "c" (target: u32, level, internalformat, width, height, depth, border: i32, format, type: u32, data: rawptr, loc := #caller_location) { impl_TexImage3D(target, level, internalformat, width, height, depth, border, format, type, data); debug_helper(loc, 0, target, level, internalformat, width, height, depth, border, format, type, data) } TexSubImage3D :: proc "c" (target: u32, level, xoffset, yoffset, zoffset, width, height, depth: i32, format, type: u32, pixels: rawptr, loc := #caller_location) { impl_TexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); debug_helper(loc, 0, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels) } CopyTexSubImage3D :: proc "c" (target: u32, level, xoffset, yoffset, zoffset, x, y, width, height: i32, loc := #caller_location) { impl_CopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); debug_helper(loc, 0, target, level, xoffset, yoffset, zoffset, x, y, width, height) } From c6db3cc6709bf53d0bbfafab66769175e9a871ae Mon Sep 17 00:00:00 2001 From: alessio98888 <56458281+alessio98888@users.noreply.github.com> Date: Sat, 16 Aug 2025 19:50:06 +0200 Subject: [PATCH 155/160] Fix buddy allocator assert The last address of "data" is not "cast(uintptr)raw_data(data)+cast(uintptr)size" but "cast(uintptr)raw_data(data)+cast(uintptr)(size-1)". The original assert would fail when for example the allocation size requested and the buddy allocator allignment were both 64. --- core/mem/allocators.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index cb9301f60..2d7e7b3ea 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -2331,7 +2331,7 @@ buddy_allocator_alloc_bytes_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) } found.is_free = false data := ([^]byte)(found)[b.alignment:][:size] - assert(cast(uintptr)raw_data(data)+cast(uintptr)size < cast(uintptr)buddy_block_next(found), "Buddy_Allocator has made an allocation which overlaps a block header.") + assert(cast(uintptr)raw_data(data)+cast(uintptr)(size-1) < cast(uintptr)buddy_block_next(found), "Buddy_Allocator has made an allocation which overlaps a block header.") // ensure_poisoned(data) // sanitizer.address_unpoison(data) return data, nil From 17f9ad29afa8e40918c5859a2c7f9b6ef7a3740e Mon Sep 17 00:00:00 2001 From: connnnal <216976529+connnnal@users.noreply.github.com> Date: Sun, 17 Aug 2025 18:35:25 +0100 Subject: [PATCH 156/160] Make D3D12_FEATURE_DATA_D3D12_OPTIONS.MinPrecisionSupport a bitset https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_feature_data_d3d12_options --- vendor/directx/d3d12/d3d12.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/directx/d3d12/d3d12.odin b/vendor/directx/d3d12/d3d12.odin index 2bfd4de4f..0d4dbc4e0 100644 --- a/vendor/directx/d3d12/d3d12.odin +++ b/vendor/directx/d3d12/d3d12.odin @@ -851,10 +851,10 @@ FEATURE :: enum i32 { OPTIONS19 = 48, } -SHADER_MIN_PRECISION_SUPPORT :: enum i32 { - NONE = 0, - _10_BIT = 1, - _16_BIT = 2, +SHADER_MIN_PRECISION_SUPPORT :: distinct bit_set[SHADER_MIN_PRECISION_SUPPORT_FLAG; u32] +SHADER_MIN_PRECISION_SUPPORT_FLAG :: enum i32 { + _10_BIT, + _16_BIT, } TILED_RESOURCES_TIER :: enum i32 { From a0f6084b48e86295e3e6f4c444cbd936651fbdb0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Aug 2025 14:26:52 +0100 Subject: [PATCH 157/160] Rebuild stb libraries and compile `stb_truetype` using the `stb_rect_pack` rects --- vendor/stb/lib/stb_image.lib | Bin 274620 -> 288820 bytes vendor/stb/lib/stb_image_resize.lib | Bin 107674 -> 99126 bytes vendor/stb/lib/stb_image_write.lib | Bin 80908 -> 81326 bytes vendor/stb/lib/stb_rect_pack.lib | Bin 14996 -> 15174 bytes vendor/stb/lib/stb_sprintf.lib | Bin 25212 -> 24726 bytes vendor/stb/lib/stb_truetype.lib | Bin 170652 -> 168724 bytes vendor/stb/lib/stb_vorbis.lib | Bin 151738 -> 153860 bytes vendor/stb/src/stb_truetype.c | 2 ++ 8 files changed, 2 insertions(+) diff --git a/vendor/stb/lib/stb_image.lib b/vendor/stb/lib/stb_image.lib index f0cffb1fc67063e24f0ab4ac9bcf79a1ff77e4a6..bed913f617349e638a341ea8552f84b8d6b6d426 100644 GIT binary patch literal 288820 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 ?jH`5@@3g$~DmARZ%(_ur%WHDf9G7E%Ex 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 diff --git a/vendor/stb/lib/stb_image_write.lib b/vendor/stb/lib/stb_image_write.lib index 415a629966bf5ca42ee5f0275c1d7c05fe2b3a5b..8cd346f4f7b63e4e8c76f3b5c924e5c2299a2eb8 100644 GIT binary patch literal 81326 zcmY$iNi0gvu;bEKKm~^8rlzJw#-= zp01vb9sywS0=T%7uYCUbW9=`rAKHk1=#F(z)>;uw|WD?(l)~+Eadq(bcJcD?^>YH-mjY4<(}pd4 zj6n*(;bRVBnSlr}3#Q509VBG00;R!$3QzHdZq5!q&hCC*Zho#{`@+GB!G_v{P4x!} zfL)OZVu7O%B2xz9BjOaS2JEFGkc2%b4CDO^N;32Fii1n?ixSgQ9qbM5?c?Kf@{<#D z;)_dCGV|jLiZb&`(&FKq&_9V{8*<1>pB;}gpgGjkG?a#G_{QqvMkb4nQS>12qH zFG@|!aWF_UNlP*`OoQ=_QxeTAOjD8&{8USGixg8hKh*-9T@ZXrOG7gYa|A!p(lRj> zDUPg1H;a&n?kDnopHMQ*MGM6rPpl!li@X;8orUsjw4jq@B3 z2hJ{ruo#LQf>hnH@+l4KCLt_ z8Cf_jzbH91z926>J}on+B((@xE+YkzGLkrkJuC!%q&IiVyyF}^&rBqP2k2O3aSIhjfEFlR83 z+DE$6>VI9<8^rRhmPg-bSmq3HR?IwYGvn0vEI&m!1k~Kk4NsWijZ(1_GeUaG7Ul zU|?ourl4SMZfIx>R-3?;()OK!QEPFA;57yY17-#=&`T*wO)e=*1#=h}7}6LR7}lu4 zcu<;4fI%-MHK{aRB^WBgP{Y8$AfyiCL1+gC0Vlnb#F9h^7fk+PVPNRPP#17OuLxCP zrU(N=mkwA9oN!GWiT)>usSj@ z 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 VD{^xkAleO#pfo&}r$GS&7XS}UGCKeO literal 80908 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 diff --git a/vendor/stb/lib/stb_rect_pack.lib b/vendor/stb/lib/stb_rect_pack.lib index a0a139ace91d4afe44de5faeee62cd23c40be012..862fc292a4b43a328b906fd0dc86290914332cfd 100644 GIT binary patch delta 6140 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 WY;+wo(yq>t2_D@Bjhf3?RR92?qwzfe literal 14996 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 Qg~Mf+7TT&SI&FLc0CZ~2ng9R* diff --git a/vendor/stb/lib/stb_sprintf.lib b/vendor/stb/lib/stb_sprintf.lib index 35c2cecc9df45b36284b05d6441dc8a8d9850c1a..1d31c0de0148060c5b493c73b504b819a2308f27 100644 GIT binary patch delta 13615 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 O=Yw;8JaHE$Z~*|C8v618 diff --git a/vendor/stb/lib/stb_truetype.lib b/vendor/stb/lib/stb_truetype.lib index 16ecf944df9807f2d62cb212e611dd5df9270cd3..f1ec8e818a33bd0f8a5257f58a0c3da146f44e5b 100644 GIT binary patch delta 56265 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 KTXZoc3jhE?v!F2m delta 59092 zcmbQTjBCzTt_e~cMy3`P=4J+Fljj+VZM4YYpB~xCR5H0nATip**TKcxJ;2X5z~8}M z#nI6jMA#dHNh1&$3L=U@L@J0V0uf-YJy<#i#7_hfl^~)7M1Z;WMw_`bt2rk7OA4Y` zzqw8_hL2wM_i!NF?FQqRPJUx3A#QGAY_6c7ppd|o(iYED`sH5Qv?+`XrIV$Nq?z8t zO|~#nVw=Fgz#ttzIl)McX?6VM0y8Bx0R{$!GZ5xO1_p*ZU?vwU0|Ud}1O|pf36ph< z<+zTqFfg1+U|^V(I{A)~5}Q#P1A|{0SVo3{fgvJ|fuRJ%Rbs14V_=ZTnjB*)$JNTf zz+jfez_1&_ZDwF#xS7Sk5R(m-eaXPUP?gQVkeoC5jIk11c@6^uW5MJeb2+fpwgn6f zP6d-?OqAGs3K$py3&3`9FfcGg7cej+7fjAEQDVz2U|^6h0_$l2$rLd#+%B5D$3%(k zeGvmgLGk1>#&TS?3=9k{#S9DwizoA#DzRNHW?--=0n79=GBEg*FfiOM1v^NDk%8f7 zDFXvIkuk+ z3=9pkL2hOXV_;yYoHO}@g&N!WISdTpb0^DKDzPQbWng$Y7i1gPOhyKV&vO|Vdgf2g zu~cH)H=lulYauw$LCH*GAp?W%La@RGtPBk13mF)$FPr?tQi*N*3I>L_m6Lg_l-P<^ zGB7Z#0(polf`NfSWEBHL&Z@~VR!VFQs~8xbt)ARtBgYlUz`($_hJhh|&Ez#!N^F^H z7#JGXP4;n+;{wIzg>?)J;v2x$eqmr>Fx|kwpt%vOPl17fA#fuDL*d5BKGsTXts5B_ zE^P!mNS2v_;m$?|hM!x&q0G#}z+k$Sfx%)Y$W%5^e(Bf=3Ine1Yzz#UyBHV}4o()a zQDQrCkb%Mc&}17MC8pv-lM`&z*vbzvFti??++(A}wD$1i4K`{_mk)zM@O&bFPgW-DyhVAdc zQ3Hy@gYOv_wtfKHSI5A>aQOoRL*PfS>@-k;@R5Px<415%fl?3aCkBRfpTLUUKxN)1 z1_q(elXEQPxb`qIFt~hXU|{$Pc2Oz=1B2UF28PvN!FpTS85s6|WnggmJDJ5viRr~Z zkmI;NF)%Pp{LjG9%)mIg*G85*pOJxKH3K7q6cgj*S|>&JEG9;VXH1Nf&l<~eon~TS z_{GG?aFliO87Cz@Mm9!<1ssgvJSK!A|4U3`_MV`?xBxzt(4D_@NK>7uPx_1_mYrMurVWljpd~f$dvh%*fzu0+wb6 z+3#xtamyt}28ItNj0}g&z;aykLDiEPBSW_JWE(dn_M6s>3%?Fe=jyF3E}L%JhKN|6o3ZgZUEuEx9+No2R<-%ft- zuFl5m#K<7&H2I9X5|g>pofsMBI!)f=p~SQsB68S? zk>NZ<okU zlz9(2GcsI6k$dmV$nYB^$0X?jHc8Wkk-^ku@)}PireKIjwhJRexeM6rsVfLrxCbM{bri|39*hi( zo?suzdxFhU_he)+hKK}0L?S&I8B#sLG13eXneEBQu-p^u>!Y5G3^zTI?fdS@$iVCc zwolQEk-^XlS<>H&ks%r^$yDhDwy)ERkzuMA*!0a1k<(s`4A&uQzCuJ;y%`wchw&=mYkxz7Naj11=?B5xrgKYbY)SpC3il>NZE^!*qato^{|L_$Pz z{TLZ4!6LlV{TLZm`ypF<(vOkhsvp=*pZypanEjC@75y0*bp63*dHaJ+kN0O}$o2=@ z*9j4s>d(lq&>s{WO#2}s*Zmn89{YogWMT{e>kw|cqk)-awyn7$52Lwz))n#{7^=Q>QHdV%nSwFw>6ZJ;b17pH@x>l z85!8aAj%m*g)*C97$bvh7)Uv
T`ZivX@NJfVBkzm`;LPV}cGBP}bi2R3$utza6 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 diff --git a/vendor/stb/lib/stb_vorbis.lib b/vendor/stb/lib/stb_vorbis.lib index bf47eda261e2114f2de6289b4dbc39e577f9e0c1..e879131c5def05dd5561253aff558f1fb3e23524 100644 GIT binary patch literal 153860 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Ð`+EDW$iA8tYyqrx`O$TUOt{*>l; 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 j^5EkClOe*59n}9k{bBXml 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≻jfPm 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 U3=*U;mlznJ^U;*Sq|s>x00Wr;BLDyZ diff --git a/vendor/stb/src/stb_truetype.c b/vendor/stb/src/stb_truetype.c index 05c23f583..ed2415376 100644 --- a/vendor/stb/src/stb_truetype.c +++ b/vendor/stb/src/stb_truetype.c @@ -1,2 +1,4 @@ +#include "stb_rect_pack.h" + #define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" \ No newline at end of file From f8b56ccd88178a54cae8769d438fbb51b56c0469 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Aug 2025 15:51:11 +0100 Subject: [PATCH 158/160] Update stb_truetype.c again --- vendor/stb/lib/stb_image.lib | Bin 288820 -> 288820 bytes vendor/stb/lib/stb_image_resize.lib | Bin 99126 -> 99126 bytes vendor/stb/lib/stb_image_write.lib | Bin 81326 -> 81326 bytes vendor/stb/lib/stb_rect_pack.lib | Bin 15174 -> 15174 bytes vendor/stb/lib/stb_sprintf.lib | Bin 24726 -> 24726 bytes vendor/stb/lib/stb_truetype.lib | Bin 168724 -> 181362 bytes vendor/stb/lib/stb_vorbis.lib | Bin 153860 -> 153860 bytes vendor/stb/src/stb_truetype.c | 2 ++ 8 files changed, 2 insertions(+) diff --git a/vendor/stb/lib/stb_image.lib b/vendor/stb/lib/stb_image.lib index bed913f617349e638a341ea8552f84b8d6b6d426..b36fef6dc2421dd86c53f26486a51f0040d900d7 100644 GIT binary patch delta 60 zcmdmTL2%0j!3lCK7G|af8#_ JP-T>h0{~6D5vl+H diff --git a/vendor/stb/lib/stb_image_write.lib b/vendor/stb/lib/stb_image_write.lib index 8cd346f4f7b63e4e8c76f3b5c924e5c2299a2eb8..34e724fc818ec0a037ff36b1feb1a5964e019273 100644 GIT binary patch delta 57 zcmZ4Yn`PZ^mI-n!7G|af8Lja99!7ItjSCQJ`hHbfEv>&+vX~P?NtJdCb|Ie CFAwPe delta 57 zcmZ4Yn`PZ^mI-n!#-Lja99!7ItjSCQJ`hHbfEp8{R&$lW_9_8J6I}r4 Cjt><8 diff --git a/vendor/stb/lib/stb_rect_pack.lib b/vendor/stb/lib/stb_rect_pack.lib index 862fc292a4b43a328b906fd0dc86290914332cfd..47344b3662163b6a457664a1a0641cd75c3e7f62 100644 GIT binary patch delta 51 xcmX?BcC2iI9E*jSsli620v0f1^E8$S2y5~u4ll6KWCu<)rc<_?GdS-V0sx%94~PH& delta 51 xcmX?BcC2iI9E-83h3Q760v0f1^E8$S2y5~u4lj_9$z%skH6{kF%^95c3;~x~4zd6M diff --git a/vendor/stb/lib/stb_sprintf.lib b/vendor/stb/lib/stb_sprintf.lib index 1d31c0de0148060c5b493c73b504b819a2308f27..892e56c42196a3984b55144a8d97da68db9d0516 100644 GIT binary patch delta 44 vcmbPska5~U#tCvP7G|af8;l delta 44 ucmbPska5~U#tCvP#-UM)tDHxHrFvr!~y^@4-88H diff --git a/vendor/stb/lib/stb_truetype.lib b/vendor/stb/lib/stb_truetype.lib index f1ec8e818a33bd0f8a5257f58a0c3da146f44e5b..f86ddcc5e9fa110a5bbafc99924ab9c33d4f6f19 100644 GIT binary patch delta 50025 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 mN$duyYD^4T%}lcGOtOsInPi!mYygVB B4GRDO diff --git a/vendor/stb/src/stb_truetype.c b/vendor/stb/src/stb_truetype.c index ed2415376..974a0bdce 100644 --- a/vendor/stb/src/stb_truetype.c +++ b/vendor/stb/src/stb_truetype.c @@ -1,3 +1,5 @@ +#define STBRP_STATIC +#define STB_RECT_PACK_IMPLEMENTATION #include "stb_rect_pack.h" #define STB_TRUETYPE_IMPLEMENTATION From 409e3975669eb0d96655c84227d97512db80e72b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Aug 2025 21:47:09 +0100 Subject: [PATCH 159/160] Fix bug with `$fn: proc()` when passed with a global variable --- src/check_expr.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7020b4f4b..862dd9278 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6472,6 +6472,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A } } + if (e && e->kind == Entity_Constant && is_type_proc(e->type)) { + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant procedure value for the argument '%.*s'", LIT(e->token.string)); + } + err = CallArgumentError_NoneConstantParameter; + } + } if (!err && is_type_any(param_type)) { add_type_info_type(c, o->type); From 8bab146b2d9d151297391bd8d3a726e23eba1f1c Mon Sep 17 00:00:00 2001 From: Brad Lewis <22850972+BradLewis@users.noreply.github.com> Date: Tue, 19 Aug 2025 07:20:31 -0400 Subject: [PATCH 160/160] Check for EOF when scanning file tags --- core/odin/tokenizer/tokenizer.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index d4da82c56..a9d367a4d 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -209,14 +209,14 @@ scan_comment :: proc(t: ^Tokenizer) -> string { scan_file_tag :: proc(t: ^Tokenizer) -> string { offset := t.offset - 1 - for t.ch != '\n' { + for t.ch != '\n' && t.ch != utf8.RUNE_EOF { if t.ch == '/' { next := peek_byte(t, 0) if next == '/' || next == '*' { break } - } + } advance_rune(t) }