From 65b4c793f011ab67ad51773273ebc4333c12bff5 Mon Sep 17 00:00:00 2001 From: Lucas Perlind Date: Sun, 27 Apr 2025 22:47:03 +1000 Subject: [PATCH 01/49] 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 02/49] 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 019084a17fb179a823a213591b016a43620e47ce Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Sat, 5 Jul 2025 13:55:14 -0700 Subject: [PATCH 03/49] 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 04/49] 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 05/49] 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 090cac62f9cc30f759cba086298b4bdb8c7c62b3 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Jul 2025 20:47:49 +0200 Subject: [PATCH 06/49] lb_const_string -> lb_const_value --- src/llvm_backend_proc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 9f6a1d653..844154064 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1962,7 +1962,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu LLVMValueRef values[2] = {}; values[0] = lb_const_string(m, file_name).value; - values[1] = lb_const_string(m, file->data).value; + values[1] = lb_const_value(m, t_u8_slice, exact_value_string(file->data)).value; LLVMValueRef element = llvm_const_named_struct(m, t_load_directory_file, values, gb_count_of(values)); elements[i] = element; } From 78126c56847b05d5640666540d02c39050e1524f Mon Sep 17 00:00:00 2001 From: Daniel Goldenberg Date: Wed, 9 Jul 2025 14:30:22 -0400 Subject: [PATCH 07/49] Add linux build script for kb_text_shape --- vendor/kb_text_shape/kb_text_shape_procs.odin | 2 +- vendor/kb_text_shape/src/build_unix.sh | 7 +++++++ vendor/kb_text_shape/src/kb_text_shape.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100755 vendor/kb_text_shape/src/build_unix.sh diff --git a/vendor/kb_text_shape/kb_text_shape_procs.odin b/vendor/kb_text_shape/kb_text_shape_procs.odin index b45a40eaa..047e16852 100644 --- a/vendor/kb_text_shape/kb_text_shape_procs.odin +++ b/vendor/kb_text_shape/kb_text_shape_procs.odin @@ -6,7 +6,7 @@ when ODIN_OS == .Windows { } } else { foreign import lib { - "kb_text_shape.a", + "lib/kb_text_shape.a", } } diff --git a/vendor/kb_text_shape/src/build_unix.sh b/vendor/kb_text_shape/src/build_unix.sh new file mode 100755 index 000000000..159aecb52 --- /dev/null +++ b/vendor/kb_text_shape/src/build_unix.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +mkdir -p "../lib" +cc -O2 -c kb_text_shape.c +ar -rcs ../lib/kb_text_shape.a kb_text_shape.o +rm *.o diff --git a/vendor/kb_text_shape/src/kb_text_shape.c b/vendor/kb_text_shape/src/kb_text_shape.c index 1d5ad0255..613669eca 100644 --- a/vendor/kb_text_shape/src/kb_text_shape.c +++ b/vendor/kb_text_shape/src/kb_text_shape.c @@ -1,4 +1,7 @@ #include +#ifndef _MSC_VER + #include +#endif #define KB_TEXT_SHAPE_NO_CRT #define KB_TEXT_SHAPE_IMPLEMENTATION From e735e036b4df84db84aa7d66c1118b3e906f0022 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 9 Jul 2025 23:55:57 +0200 Subject: [PATCH 08/49] Fix #5447 Thanks to @FourteenBrush --- src/check_stmt.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 07801b477..7187d95b3 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2778,6 +2778,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) Ast *stmt = ds->stmt; Ast *original_stmt = stmt; + if (stmt->kind == Ast_BlockStmt && stmt->BlockStmt.stmts.count == 0) { + break; // empty defer statement + } + bool is_singular = true; while (is_singular && stmt->kind == Ast_BlockStmt) { Ast *inner_stmt = nullptr; From e89abd006f943acdc4f20e3cedc86e5d56c98061 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 11 Jul 2025 15:29:27 +0200 Subject: [PATCH 09/49] Fix #5452 --- core/sys/windows/util.odin | 37 ++++++++++++++++++++++++++++---- tests/core/sys/windows/util.odin | 33 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 tests/core/sys/windows/util.odin diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index 30eecf8a1..995e8e0e5 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -170,15 +170,15 @@ wstring_to_utf8_alloc :: proc(s: wstring, N: int, allocator := context.temp_allo return string(text[:n]), nil } -wstring_to_utf8_buf :: proc(buf: []u8, s: wstring) -> (res: string) { - n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, -1, nil, 0, nil, nil) +wstring_to_utf8_buf :: proc(buf: []u8, s: wstring, N := -1) -> (res: string) { + n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil) if n == 0 { return } else if int(n) > len(buf) { return } - n2 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, -1, raw_data(buf), n, nil, nil) + n2 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(buf), n, nil, nil) if n2 == 0 { return } else if int(n2) > len(buf) { @@ -196,6 +196,21 @@ wstring_to_utf8_buf :: proc(buf: []u8, s: wstring) -> (res: string) { wstring_to_utf8 :: proc{wstring_to_utf8_alloc, wstring_to_utf8_buf} +/* +Converts a UTF-16 string into a regular UTF-8 `string` and allocates the result. +If the input is null-terminated, only the part of the input string leading up +to it will be converted. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be converted +- allocator: (default: context.allocator) + +Returns: +- res: A cloned and converted string +- err: An optional allocator error if one occured, `nil` otherwise +*/ utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) { if len(s) == 0 { return "", nil @@ -203,11 +218,25 @@ utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (r return wstring_to_utf8(raw_data(s), len(s), allocator) } +/* +Converts a UTF-16 string into a regular UTF-8 `string`, using `buf` as its backing. +If the input is null-terminated, only the part of the input string leading up +to it will be converted. + +*Uses `buf` for backing* + +Inputs: +- s: The string to be converted +- buf: Backing buffer for result string + +Returns: +- res: A converted string, backed byu `buf` +*/ utf16_to_utf8_buf :: proc(buf: []u8, s: []u16) -> (res: string) { if len(s) == 0 { return } - return wstring_to_utf8(buf, raw_data(s)) + return wstring_to_utf8(buf, raw_data(s), len(s)) } utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf} diff --git a/tests/core/sys/windows/util.odin b/tests/core/sys/windows/util.odin new file mode 100644 index 000000000..0201395f6 --- /dev/null +++ b/tests/core/sys/windows/util.odin @@ -0,0 +1,33 @@ +#+build windows +package test_core_sys_windows + +import "base:intrinsics" +import "core:testing" +import win32 "core:sys/windows" + +UTF16_Vector :: struct { + wstr: win32.wstring, + ustr: string, +} + +utf16_vectors := []UTF16_Vector{ + { + intrinsics.constant_utf16_cstring("Hellope, World!"), + "Hellope, World!", + }, + { + intrinsics.constant_utf16_cstring("Hellope\x00, World!"), + "Hellope", + }, +} + +@(test) +utf16_to_utf8_buf_test :: proc(t: ^testing.T) { + for test in utf16_vectors { + buf := make([]u8, len(test.ustr)) + defer delete(buf) + + res := win32.utf16_to_utf8_buf(buf[:], test.wstr[:len(test.ustr)]) + testing.expect_value(t, res, test.ustr) + } +} \ No newline at end of file From e647f560db721e733a4f331e63d23de46193aef6 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 11 Jul 2025 15:47:55 +0200 Subject: [PATCH 10/49] Try fix NetBSD CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4ab34fb5..7066dec08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: name: NetBSD Build, Check, and Test runs-on: ubuntu-latest env: - PKGSRC_BRANCH: 2024Q3 + PKGSRC_BRANCH: 2025Q2 steps: - uses: actions/checkout@v4 - name: Build, Check, and Test From 5ce8d1ede834134ec7fd71c62738791c2d36e171 Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 16:58:47 +0200 Subject: [PATCH 11/49] Add clangd stuff to gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index c85ccb4ad..be3e78954 100644 --- a/.gitignore +++ b/.gitignore @@ -297,3 +297,8 @@ build.sh *.rdi tests/issues/build/* misc/featuregen/featuregen + +# Clangd stuff +.cache/ +.clangd +compile_commands.json From 8cbf75c928e91a93fbd0ab48877c5f32afa3a9f4 Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:03:49 +0200 Subject: [PATCH 12/49] Add type_enum_is_contiguous intrinsic --- base/intrinsics/intrinsics.odin | 3 +++ src/check_builtin.cpp | 48 +++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 +++ 3 files changed, 55 insertions(+) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index c1d16c5e4..20a7f4a84 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -213,6 +213,9 @@ type_is_subtype_of :: proc($T, $U: typeid) -> bool --- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- +// Contiguous as in having a set of constants, when sorted, the difference between consecutive values is only 0 or 1 +type_enum_is_contiguous :: proc($T: typeid) -> bool where type_is_enum(T) --- + type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) --- type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 9f9787b61..70848cec6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1,5 +1,11 @@ typedef bool (BuiltinTypeIsProc)(Type *t); +gb_internal int enum_constant_entity_cmp(void const* a, void const* b) { + BigInt bi = (*cast(Entity const **)a)->Constant.value.value_integer; + BigInt bj = (*cast(Entity const **)b)->Constant.value.value_integer; + return big_int_cmp(&bi, &bj); +} + gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = { nullptr, // BuiltinProc__type_simple_boolean_begin @@ -6919,6 +6925,48 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_type_enum_is_contiguous: + { + Operand op = {}; + Type *bt = check_type(c, ce->args[0]); + Type *type = base_type(bt); + if (type == nullptr || type == t_invalid) { + error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name)); + return false; + } + if (!is_type_enum(type)) { + gbString t = type_to_string(type); + error(ce->args[0], "Expected an enum type for '%.*s', got %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + // sort enum fields in place in ascending order + Array enum_constants = type->Enum.fields; + array_sort(enum_constants, enum_constant_entity_cmp); + + BigInt minus_one = big_int_make_i64(-1); + BigInt diff = {}; + + bool contiguous = true; + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + + for (isize i = 0; i < enum_constants.count - 1; i++) { + BigInt curr = enum_constants[i]->Constant.value.value_integer; + BigInt next = enum_constants[i + 1]->Constant.value.value_integer; + big_int_sub(&diff, &curr, &next); + + if (!big_int_is_zero(&diff) && big_int_cmp(&diff, &minus_one) != 0) { + contiguous = false; + break; + } + } + + operand->value = exact_value_bool(contiguous); + break; + } + case BuiltinProc_type_equal_proc: { Operand op = {}; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 90652cb0b..38d49e330 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -325,6 +325,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_bit_set_backing_type, + BuiltinProc_type_enum_is_contiguous, + BuiltinProc_type_equal_proc, BuiltinProc_type_hasher_proc, BuiltinProc_type_map_info, @@ -678,6 +680,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_bit_set_backing_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_enum_is_contiguous"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics }, + {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, From 56c221e211a7336326d04ba6a1240f5f47f6f723 Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:06:08 +0200 Subject: [PATCH 13/49] Rephrasing for constants --- base/intrinsics/intrinsics.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 20a7f4a84..dc1690ba8 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -213,7 +213,7 @@ type_is_subtype_of :: proc($T, $U: typeid) -> bool --- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- -// Contiguous as in having a set of constants, when sorted, the difference between consecutive values is only 0 or 1 +// Contiguous as in having a set of constants, when sorted, the difference between consecutive constants is only 0 or 1 type_enum_is_contiguous :: proc($T: typeid) -> bool where type_is_enum(T) --- type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) --- From cb05271157a944c934dd8bf015570f25ebd2eccc Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 17:17:30 +0200 Subject: [PATCH 14/49] Add note on sparse vs contiguous --- base/intrinsics/intrinsics.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index dc1690ba8..c0685f4db 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -213,7 +213,8 @@ type_is_subtype_of :: proc($T, $U: typeid) -> bool --- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- -// Contiguous as in having a set of constants, when sorted, the difference between consecutive constants is only 0 or 1 +// "Contiguous" means that the set of enum constants, when sorted, have a difference of either 0 or 1 between consecutive values. +// This is the exact opposite of "sparse". type_enum_is_contiguous :: proc($T: typeid) -> bool where type_is_enum(T) --- type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) --- From 64bb0d1c7d03d0ce3a1643401009c528ec9ac296 Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 19:38:01 +0200 Subject: [PATCH 15/49] Allocate temp array instead of sorting in place --- src/check_builtin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 70848cec6..041e89afd 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -6941,8 +6941,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - // sort enum fields in place in ascending order - Array enum_constants = type->Enum.fields; + auto enum_constants = array_make(temporary_allocator(), type->Enum.fields.count); + array_copy(&enum_constants, type->Enum.fields, 0); array_sort(enum_constants, enum_constant_entity_cmp); BigInt minus_one = big_int_make_i64(-1); From 38faec757d4e4648a86fb17a1fda0e2399a3ea19 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 11 Jul 2025 18:41:40 +0100 Subject: [PATCH 16/49] Correct consume comment groups in both parsers --- core/odin/parser/parser.odin | 43 +++++++++++++++++++----------------- src/parser.cpp | 43 +++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 27b7edbf6..18dd9aa88 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -348,27 +348,30 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro } consume_comment_groups :: proc(p: ^Parser, prev: tokenizer.Token) { - if p.curr_tok.kind == .Comment { - comment: ^ast.Comment_Group - end_line := 0 - - if p.curr_tok.pos.line == prev.pos.line { - comment, end_line = consume_comment_group(p, 0) - if p.curr_tok.pos.line != end_line || p.curr_tok.kind == .EOF { - p.line_comment = comment - } - } - - end_line = -1 - for p.curr_tok.kind == .Comment { - comment, end_line = consume_comment_group(p, 1) - } - if end_line+1 >= p.curr_tok.pos.line || end_line < 0 { - p.lead_comment = comment - } - - assert(p.curr_tok.kind != .Comment) + if p.curr_tok.kind != .Comment { + return } + comment: ^ast.Comment_Group + end_line := 0 + + if p.curr_tok.pos.line == prev.pos.line { + comment, end_line = consume_comment_group(p, 0) + if p.curr_tok.pos.line != end_line || + p.curr_tok.pos.line == prev.pos.line+1 || + p.curr_tok.kind == .EOF { + p.line_comment = comment + } + } + + end_line = -1 + for p.curr_tok.kind == .Comment { + comment, end_line = consume_comment_group(p, 1) + } + if end_line+1 >= p.curr_tok.pos.line || end_line < 0 { + p.lead_comment = comment + } + + assert(p.curr_tok.kind != .Comment) } advance_token :: proc(p: ^Parser) -> tokenizer.Token { diff --git a/src/parser.cpp b/src/parser.cpp index 942e83f29..f6871c978 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1436,27 +1436,30 @@ gb_internal CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_ } gb_internal void consume_comment_groups(AstFile *f, Token prev) { - if (f->curr_token.kind == Token_Comment) { - CommentGroup *comment = nullptr; - isize end_line = 0; - - if (f->curr_token.pos.line == prev.pos.line) { - comment = consume_comment_group(f, 0, &end_line); - if (f->curr_token.pos.line != end_line || f->curr_token.kind == Token_EOF) { - f->line_comment = comment; - } - } - - end_line = -1; - while (f->curr_token.kind == Token_Comment) { - comment = consume_comment_group(f, 1, &end_line); - } - if (end_line+1 == f->curr_token.pos.line || end_line < 0) { - f->lead_comment = comment; - } - - GB_ASSERT(f->curr_token.kind != Token_Comment); + if (f->curr_token.kind != Token_Comment) { + return; } + CommentGroup *comment = nullptr; + isize end_line = 0; + + if (f->curr_token.pos.line == prev.pos.line) { + comment = consume_comment_group(f, 0, &end_line); + if (f->curr_token.pos.line != end_line || + f->curr_token.pos.line == prev.pos.line+1 || + f->curr_token.kind == Token_EOF) { + f->line_comment = comment; + } + } + + end_line = -1; + while (f->curr_token.kind == Token_Comment) { + comment = consume_comment_group(f, 1, &end_line); + } + if (end_line+1 == f->curr_token.pos.line || end_line < 0) { + f->lead_comment = comment; + } + + GB_ASSERT(f->curr_token.kind != Token_Comment); } gb_internal gb_inline bool ignore_newlines(AstFile *f) { From 2dd0b7528926f9ea275bdbd9b6654a3e1600ac54 Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 21:07:28 +0200 Subject: [PATCH 17/49] Fix BigInt leaking --- src/check_builtin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 041e89afd..4223419a5 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -6946,6 +6946,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As array_sort(enum_constants, enum_constant_entity_cmp); BigInt minus_one = big_int_make_i64(-1); + defer (big_int_dealloc(&minus_one)); BigInt diff = {}; bool contiguous = true; @@ -6956,6 +6957,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As BigInt curr = enum_constants[i]->Constant.value.value_integer; BigInt next = enum_constants[i + 1]->Constant.value.value_integer; big_int_sub(&diff, &curr, &next); + defer (big_int_dealloc(&diff)); if (!big_int_is_zero(&diff) && big_int_cmp(&diff, &minus_one) != 0) { contiguous = false; From d08ebf56e76e078e70ae02ac047d9eee9c68834c Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 21:10:42 +0200 Subject: [PATCH 18/49] Add intrinsics.type_enum_is_contiguous test --- .../test_intrinsics_enum_contiguous.odin | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/internal/test_intrinsics_enum_contiguous.odin diff --git a/tests/internal/test_intrinsics_enum_contiguous.odin b/tests/internal/test_intrinsics_enum_contiguous.odin new file mode 100644 index 000000000..7a6901b82 --- /dev/null +++ b/tests/internal/test_intrinsics_enum_contiguous.odin @@ -0,0 +1,24 @@ +package test_internal + +import "base:intrinsics" +import "core:testing" + +@(test) +test_intrinsics_enum_is_contiguous :: proc(t: ^testing.T) { + contiguous :: intrinsics.type_enum_is_contiguous + testing.expect(t, contiguous(enum { A=0, B=0, C=0 })) + testing.expect(t, contiguous(enum { A=0, B=1, C=2 })) + testing.expect(t, contiguous(enum { A=1, B=2, C=2 })) + testing.expect(t, contiguous(enum { A=-2, B=-1, C=0 })) + testing.expect(t, contiguous(enum { A=-8, B=-6, C=-7, D=-8 })) + testing.expect(t, contiguous(enum { C=4, A=3 })) + testing.expect(t, contiguous(enum { })) + testing.expect(t, contiguous(enum { A })) + testing.expect(t, contiguous(enum { Delta=-4 })) + testing.expect(t, contiguous(enum { X = 2 * len([?]u8{ 0 }) })) + testing.expect(t, contiguous(enum { Alpha=-2, Beta=-1, Gamma=0, Delta=-3 })) + + testing.expect(t, !contiguous(enum { A=1, B=3 })) + testing.expect(t, !contiguous(enum { B=-5, Beta=-3 })) + testing.expect(t, !contiguous(enum { A=0, B=-2 })) +} From 980370a24bb4eb5bddd88097ef6afbda452bffa0 Mon Sep 17 00:00:00 2001 From: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> Date: Fri, 11 Jul 2025 21:23:21 +0200 Subject: [PATCH 19/49] Add union kind assertion to enum_constant_entity_cmp --- src/check_builtin.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4223419a5..b833c7014 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1,9 +1,12 @@ typedef bool (BuiltinTypeIsProc)(Type *t); gb_internal int enum_constant_entity_cmp(void const* a, void const* b) { - BigInt bi = (*cast(Entity const **)a)->Constant.value.value_integer; - BigInt bj = (*cast(Entity const **)b)->Constant.value.value_integer; - return big_int_cmp(&bi, &bj); + Entity const *ea = *(cast(Entity const **)a); + Entity const *eb = *(cast(Entity const **)b); + GB_ASSERT(ea->kind == Entity_Constant && eb->kind == Entity_Constant); + GB_ASSERT(ea->Constant.value.kind == ExactValue_Integer && eb->Constant.value.kind == ExactValue_Integer); + + return big_int_cmp(&ea->Constant.value.value_integer, &eb->Constant.value.value_integer); } gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end - BuiltinProc__type_simple_boolean_begin] = { From b811414ed1f014aef464d4fa5195e77cbde7ba73 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 13 Jul 2025 15:24:53 -0400 Subject: [PATCH 20/49] Use fi.is_dir instead of path_is_directory() when checking if an import is a directory with a .odin extension. --- src/parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index f6871c978..5d0a75f8a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5797,7 +5797,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const for (FileInfo fi : list) { String name = fi.name; String ext = path_extension(name); - if (ext == FILE_EXT && !path_is_directory(name)) { + if (ext == FILE_EXT && !fi.is_dir) { files_with_ext += 1; } if (ext == FILE_EXT && !is_excluded_target_filename(name)) { @@ -5822,7 +5822,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const for (FileInfo fi : list) { String name = fi.name; String ext = path_extension(name); - if (ext == FILE_EXT && !path_is_directory(name)) { + if (ext == FILE_EXT && !fi.is_dir) { if (is_excluded_target_filename(name)) { continue; } From 9a87611609495f95429a8f4630243e62320e7f81 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 13 Jul 2025 15:39:41 -0400 Subject: [PATCH 21/49] Fix leftover poor indentation for objc_ivars MPSCQueue --- src/llvm_backend.hpp | 2 +- src/llvm_backend_general.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index fd6f50dcd..fef6e754d 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -242,7 +242,7 @@ struct lbGenerator : LinkerData { MPSCQueue entities_to_correct_linkage; MPSCQueue objc_selectors; MPSCQueue objc_classes; - MPSCQueue objc_ivars; + MPSCQueue objc_ivars; MPSCQueue raddebug_section_strings; }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index aaa9ffd4d..64ea58578 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -174,9 +174,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) { mpsc_init(&gen->entities_to_correct_linkage, heap_allocator()); mpsc_init(&gen->objc_selectors, heap_allocator()); mpsc_init(&gen->objc_classes, heap_allocator()); - mpsc_init(&gen->objc_ivars, heap_allocator()); + mpsc_init(&gen->objc_ivars, heap_allocator()); mpsc_init(&gen->raddebug_section_strings, heap_allocator()); - + return true; } From 219b0fe5352af2b9265b0389965aa411ad9af4b5 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 13 Jul 2025 15:45:21 -0400 Subject: [PATCH 22/49] Replace system:System.framework imports with system:System This makes the linker work for both macOS and iOS targets --- core/c/libc/complex.odin | 2 +- core/c/libc/ctype.odin | 2 +- core/c/libc/errno.odin | 2 +- core/c/libc/locale.odin | 2 +- core/c/libc/math.odin | 2 +- core/c/libc/setjmp.odin | 2 +- core/c/libc/signal.odin | 2 +- core/c/libc/stdio.odin | 2 +- core/c/libc/stdlib.odin | 2 +- core/c/libc/string.odin | 2 +- core/c/libc/time.odin | 2 +- core/c/libc/uchar.odin | 2 +- core/c/libc/wchar.odin | 2 +- core/c/libc/wctype.odin | 2 +- core/net/interface_darwin.odin | 2 +- core/os/os2/process_posix_darwin.odin | 2 +- core/os/os_darwin.odin | 4 ++-- core/sync/futex_darwin.odin | 2 +- core/sync/primitives_darwin.odin | 2 +- core/sys/darwin/Foundation/NSBlock.odin | 2 +- core/sys/darwin/darwin.odin | 2 +- core/sys/darwin/mach_darwin.odin | 2 +- core/sys/darwin/proc.odin | 2 +- core/sys/kqueue/kqueue.odin | 2 +- core/sys/posix/arpa_inet.odin | 2 +- core/sys/posix/dirent.odin | 2 +- core/sys/posix/dlfcn.odin | 2 +- core/sys/posix/fcntl.odin | 2 +- core/sys/posix/fnmatch.odin | 2 +- core/sys/posix/glob.odin | 2 +- core/sys/posix/grp.odin | 2 +- core/sys/posix/langinfo.odin | 2 +- core/sys/posix/libgen.odin | 2 +- core/sys/posix/monetary.odin | 2 +- core/sys/posix/net_if.odin | 2 +- core/sys/posix/netdb.odin | 2 +- core/sys/posix/netinet_in.odin | 2 +- core/sys/posix/poll.odin | 2 +- core/sys/posix/pthread.odin | 2 +- core/sys/posix/pwd.odin | 2 +- core/sys/posix/sched.odin | 2 +- core/sys/posix/setjmp.odin | 2 +- core/sys/posix/signal.odin | 2 +- core/sys/posix/signal_libc.odin | 2 +- core/sys/posix/stdio.odin | 2 +- core/sys/posix/stdio_libc.odin | 2 +- core/sys/posix/stdlib.odin | 2 +- core/sys/posix/stdlib_libc.odin | 2 +- core/sys/posix/string.odin | 2 +- core/sys/posix/string_libc.odin | 2 +- core/sys/posix/sys_ipc.odin | 2 +- core/sys/posix/sys_mman.odin | 2 +- core/sys/posix/sys_msg.odin | 2 +- core/sys/posix/sys_resource.odin | 2 +- core/sys/posix/sys_select.odin | 2 +- core/sys/posix/sys_sem.odin | 2 +- core/sys/posix/sys_shm.odin | 2 +- core/sys/posix/sys_socket.odin | 2 +- core/sys/posix/sys_stat.odin | 2 +- core/sys/posix/sys_statvfs.odin | 2 +- core/sys/posix/sys_time.odin | 2 +- core/sys/posix/sys_times.odin | 2 +- core/sys/posix/sys_uio.odin | 2 +- core/sys/posix/sys_utsname.odin | 2 +- core/sys/posix/sys_wait.odin | 2 +- core/sys/posix/termios.odin | 2 +- core/sys/posix/time.odin | 2 +- core/sys/posix/ulimit.odin | 2 +- core/sys/posix/unistd.odin | 2 +- core/sys/posix/unistd_libc.odin | 2 +- core/sys/posix/utime.odin | 2 +- core/sys/posix/wordexp.odin | 2 +- 72 files changed, 73 insertions(+), 73 deletions(-) diff --git a/core/c/libc/complex.odin b/core/c/libc/complex.odin index 98fd7b1bb..7b6ac6417 100644 --- a/core/c/libc/complex.odin +++ b/core/c/libc/complex.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/ctype.odin b/core/c/libc/ctype.odin index 185385a5e..d156f1ed7 100644 --- a/core/c/libc/ctype.odin +++ b/core/c/libc/ctype.odin @@ -3,7 +3,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin index 5d1ca8248..138d70a80 100644 --- a/core/c/libc/errno.odin +++ b/core/c/libc/errno.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin index d95f5c164..27317526c 100644 --- a/core/c/libc/locale.odin +++ b/core/c/libc/locale.odin @@ -5,7 +5,7 @@ import "core:c" when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/math.odin b/core/c/libc/math.odin index 81d51728d..d0a015c70 100644 --- a/core/c/libc/math.odin +++ b/core/c/libc/math.odin @@ -7,7 +7,7 @@ import "base:intrinsics" when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/setjmp.odin b/core/c/libc/setjmp.odin index 101b614b3..4f5319316 100644 --- a/core/c/libc/setjmp.odin +++ b/core/c/libc/setjmp.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/signal.odin b/core/c/libc/signal.odin index c447e3cc3..cddf06916 100644 --- a/core/c/libc/signal.odin +++ b/core/c/libc/signal.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index 56e4e8f66..854a98637 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -9,7 +9,7 @@ when ODIN_OS == .Windows { "system:legacy_stdio_definitions.lib", } } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin index c0e273872..ca906a5f0 100644 --- a/core/c/libc/stdlib.odin +++ b/core/c/libc/stdlib.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/string.odin b/core/c/libc/string.odin index 4ec4f3a7a..2ea54579c 100644 --- a/core/c/libc/string.odin +++ b/core/c/libc/string.odin @@ -7,7 +7,7 @@ import "base:runtime" when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin index 33f8dc3af..6106923f5 100644 --- a/core/c/libc/time.odin +++ b/core/c/libc/time.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/uchar.odin b/core/c/libc/uchar.odin index a10969ceb..997b99b46 100644 --- a/core/c/libc/uchar.odin +++ b/core/c/libc/uchar.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/wchar.odin b/core/c/libc/wchar.odin index f0dae720e..248611409 100644 --- a/core/c/libc/wchar.odin +++ b/core/c/libc/wchar.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin index b96410b4c..6526a14e2 100644 --- a/core/c/libc/wctype.odin +++ b/core/c/libc/wctype.odin @@ -5,7 +5,7 @@ package libc when ODIN_OS == .Windows { foreign import libc "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/net/interface_darwin.odin b/core/net/interface_darwin.odin index f189e5844..9883c10af 100644 --- a/core/net/interface_darwin.odin +++ b/core/net/interface_darwin.odin @@ -23,7 +23,7 @@ package net import "core:strings" import "core:sys/posix" -foreign import lib "system:System.framework" +foreign import lib "system:System" @(private) _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) { diff --git a/core/os/os2/process_posix_darwin.odin b/core/os/os2/process_posix_darwin.odin index ac2d4b78c..7625e513a 100644 --- a/core/os/os2/process_posix_darwin.odin +++ b/core/os/os2/process_posix_darwin.odin @@ -10,7 +10,7 @@ import "core:sys/posix" import "core:sys/unix" import "core:time" -foreign import lib "system:System.framework" +foreign import lib "system:System" foreign lib { sysctl :: proc( diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 18f80623d..1010d27a8 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -1,8 +1,8 @@ package os foreign import dl "system:dl" -foreign import libc "system:System.framework" -foreign import pthread "system:System.framework" +foreign import libc "system:System" +foreign import pthread "system:System" import "base:runtime" import "core:strings" diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index 10ff7bfbb..2b104fed6 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -6,7 +6,7 @@ import "core:c" import "core:sys/darwin" import "core:time" -foreign import System "system:System.framework" +foreign import System "system:System" foreign System { // __ulock_wait is not available on 10.15 diff --git a/core/sync/primitives_darwin.odin b/core/sync/primitives_darwin.odin index 141cea744..4ae4b7789 100644 --- a/core/sync/primitives_darwin.odin +++ b/core/sync/primitives_darwin.odin @@ -5,7 +5,7 @@ package sync import "core:c" import "base:intrinsics" -foreign import pthread "system:System.framework" +foreign import pthread "system:System" _current_thread_id :: proc "contextless" () -> int { tid: u64 diff --git a/core/sys/darwin/Foundation/NSBlock.odin b/core/sys/darwin/Foundation/NSBlock.odin index 1ef5e8a9b..8e65391f4 100644 --- a/core/sys/darwin/Foundation/NSBlock.odin +++ b/core/sys/darwin/Foundation/NSBlock.odin @@ -62,7 +62,7 @@ global_block_descriptor := Block_Descriptor{ size = size_of(Internal_Block_Literal), } -foreign import libSystem "system:System.framework" +foreign import libSystem "system:System" foreign libSystem { _NSConcreteGlobalBlock: intrinsics.objc_class _NSConcreteStackBlock: intrinsics.objc_class diff --git a/core/sys/darwin/darwin.odin b/core/sys/darwin/darwin.odin index 96cfc7be6..52297505a 100644 --- a/core/sys/darwin/darwin.odin +++ b/core/sys/darwin/darwin.odin @@ -4,7 +4,7 @@ package darwin import "core:c" @(export) -foreign import system "system:System.framework" +foreign import system "system:System" Bool :: b8 diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 2cc823e69..4e4ed6796 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -1,6 +1,6 @@ package darwin -foreign import mach "system:System.framework" +foreign import mach "system:System" import "core:c" import "base:intrinsics" diff --git a/core/sys/darwin/proc.odin b/core/sys/darwin/proc.odin index fa5391f6f..ccd05f478 100644 --- a/core/sys/darwin/proc.odin +++ b/core/sys/darwin/proc.odin @@ -4,7 +4,7 @@ import "base:intrinsics" import "core:sys/posix" -foreign import lib "system:System.framework" +foreign import lib "system:System" // Incomplete bindings to the proc API on MacOS, add to when needed. diff --git a/core/sys/kqueue/kqueue.odin b/core/sys/kqueue/kqueue.odin index 56be1cf7a..25ee9bdce 100644 --- a/core/sys/kqueue/kqueue.odin +++ b/core/sys/kqueue/kqueue.odin @@ -2,7 +2,7 @@ package kqueue when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/arpa_inet.odin b/core/sys/posix/arpa_inet.odin index ac850ed49..6edb9e535 100644 --- a/core/sys/posix/arpa_inet.odin +++ b/core/sys/posix/arpa_inet.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else when ODIN_OS == .Haiku { foreign import lib "system:network" } else { diff --git a/core/sys/posix/dirent.odin b/core/sys/posix/dirent.odin index 1394f6b9e..cf15dada4 100644 --- a/core/sys/posix/dirent.odin +++ b/core/sys/posix/dirent.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/dlfcn.odin b/core/sys/posix/dlfcn.odin index 378c95c32..6c96b0079 100644 --- a/core/sys/posix/dlfcn.odin +++ b/core/sys/posix/dlfcn.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { foreign import lib "system:dl" } else { diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin index bc0b5b5ba..db095c418 100644 --- a/core/sys/posix/fcntl.odin +++ b/core/sys/posix/fcntl.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin index 04c3d2888..efe179324 100644 --- a/core/sys/posix/fnmatch.odin +++ b/core/sys/posix/fnmatch.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin index fb90b7546..530481587 100644 --- a/core/sys/posix/glob.odin +++ b/core/sys/posix/glob.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin index 3694308a0..8e8e69fc2 100644 --- a/core/sys/posix/grp.odin +++ b/core/sys/posix/grp.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/langinfo.odin b/core/sys/posix/langinfo.odin index 1fddfe280..195de650d 100644 --- a/core/sys/posix/langinfo.odin +++ b/core/sys/posix/langinfo.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/libgen.odin b/core/sys/posix/libgen.odin index 2354bf70d..aa2effd72 100644 --- a/core/sys/posix/libgen.odin +++ b/core/sys/posix/libgen.odin @@ -2,7 +2,7 @@ package posix when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/monetary.odin b/core/sys/posix/monetary.odin index a444bff09..2e4105881 100644 --- a/core/sys/posix/monetary.odin +++ b/core/sys/posix/monetary.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/net_if.odin b/core/sys/posix/net_if.odin index 774d11b72..182a049d1 100644 --- a/core/sys/posix/net_if.odin +++ b/core/sys/posix/net_if.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/netdb.odin b/core/sys/posix/netdb.odin index ff1cb9d4c..f2f83875f 100644 --- a/core/sys/posix/netdb.odin +++ b/core/sys/posix/netdb.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/netinet_in.odin b/core/sys/posix/netinet_in.odin index ec05915de..4b74b87f0 100644 --- a/core/sys/posix/netinet_in.odin +++ b/core/sys/posix/netinet_in.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin index 44ec767a6..a9e582b51 100644 --- a/core/sys/posix/poll.odin +++ b/core/sys/posix/poll.odin @@ -6,7 +6,7 @@ import "base:intrinsics" import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/pthread.odin b/core/sys/posix/pthread.odin index 36a3cd7b3..c7255baa3 100644 --- a/core/sys/posix/pthread.odin +++ b/core/sys/posix/pthread.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Linux { foreign import lib "system:pthread" } else { diff --git a/core/sys/posix/pwd.odin b/core/sys/posix/pwd.odin index 75d15c899..c3ee3c0f6 100644 --- a/core/sys/posix/pwd.odin +++ b/core/sys/posix/pwd.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sched.odin b/core/sys/posix/sched.odin index 82b335653..cc509ba8e 100644 --- a/core/sys/posix/sched.odin +++ b/core/sys/posix/sched.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/setjmp.odin b/core/sys/posix/setjmp.odin index 926dbd3ad..a26bdb93e 100644 --- a/core/sys/posix/setjmp.odin +++ b/core/sys/posix/setjmp.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/signal.odin b/core/sys/posix/signal.odin index 882cd9fa8..69b405c5d 100644 --- a/core/sys/posix/signal.odin +++ b/core/sys/posix/signal.odin @@ -6,7 +6,7 @@ import "base:intrinsics" import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/signal_libc.odin b/core/sys/posix/signal_libc.odin index 7a054ddd7..ba0fbf084 100644 --- a/core/sys/posix/signal_libc.odin +++ b/core/sys/posix/signal_libc.odin @@ -9,7 +9,7 @@ import "core:c/libc" when ODIN_OS == .Windows { foreign import lib "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/stdio.odin b/core/sys/posix/stdio.odin index 24464dfd8..69c8ad3cb 100644 --- a/core/sys/posix/stdio.odin +++ b/core/sys/posix/stdio.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/stdio_libc.odin b/core/sys/posix/stdio_libc.odin index 12706970d..8ccdcc37a 100644 --- a/core/sys/posix/stdio_libc.odin +++ b/core/sys/posix/stdio_libc.odin @@ -10,7 +10,7 @@ when ODIN_OS == .Windows { "system:legacy_stdio_definitions.lib", } } else when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/stdlib.odin b/core/sys/posix/stdlib.odin index 5f1ae1908..0a6e5403c 100644 --- a/core/sys/posix/stdlib.odin +++ b/core/sys/posix/stdlib.odin @@ -6,7 +6,7 @@ import "base:intrinsics" import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/stdlib_libc.odin b/core/sys/posix/stdlib_libc.odin index 6574026f4..e31c51704 100644 --- a/core/sys/posix/stdlib_libc.odin +++ b/core/sys/posix/stdlib_libc.odin @@ -9,7 +9,7 @@ import "core:c/libc" when ODIN_OS == .Windows { foreign import lib "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/string.odin b/core/sys/posix/string.odin index 3f9dbb43e..3d0c5b7a2 100644 --- a/core/sys/posix/string.odin +++ b/core/sys/posix/string.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/string_libc.odin b/core/sys/posix/string_libc.odin index 72164cc4c..d689847ee 100644 --- a/core/sys/posix/string_libc.odin +++ b/core/sys/posix/string_libc.odin @@ -4,7 +4,7 @@ package posix when ODIN_OS == .Windows { foreign import lib "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_ipc.odin b/core/sys/posix/sys_ipc.odin index bf5938ce1..5814c7211 100644 --- a/core/sys/posix/sys_ipc.odin +++ b/core/sys/posix/sys_ipc.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_mman.odin b/core/sys/posix/sys_mman.odin index 0594672ae..2d51083dc 100644 --- a/core/sys/posix/sys_mman.odin +++ b/core/sys/posix/sys_mman.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_msg.odin b/core/sys/posix/sys_msg.odin index c578b1fc6..87d5089ea 100644 --- a/core/sys/posix/sys_msg.odin +++ b/core/sys/posix/sys_msg.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_resource.odin b/core/sys/posix/sys_resource.odin index ae478382a..a748c2bba 100644 --- a/core/sys/posix/sys_resource.odin +++ b/core/sys/posix/sys_resource.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin index a75e58de6..117dee625 100644 --- a/core/sys/posix/sys_select.odin +++ b/core/sys/posix/sys_select.odin @@ -6,7 +6,7 @@ import "base:intrinsics" import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_sem.odin b/core/sys/posix/sys_sem.odin index 069315f87..e876cf74a 100644 --- a/core/sys/posix/sys_sem.odin +++ b/core/sys/posix/sys_sem.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_shm.odin b/core/sys/posix/sys_shm.odin index 8f3c56b9c..8ee16e5a3 100644 --- a/core/sys/posix/sys_shm.odin +++ b/core/sys/posix/sys_shm.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_socket.odin b/core/sys/posix/sys_socket.odin index 0645893d0..812451219 100644 --- a/core/sys/posix/sys_socket.odin +++ b/core/sys/posix/sys_socket.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/sys/posix/sys_stat.odin b/core/sys/posix/sys_stat.odin index 265356e54..df0bf2b49 100644 --- a/core/sys/posix/sys_stat.odin +++ b/core/sys/posix/sys_stat.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_statvfs.odin b/core/sys/posix/sys_statvfs.odin index 47c810135..1a332c5ce 100644 --- a/core/sys/posix/sys_statvfs.odin +++ b/core/sys/posix/sys_statvfs.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_time.odin b/core/sys/posix/sys_time.odin index 94eafec85..058166759 100644 --- a/core/sys/posix/sys_time.odin +++ b/core/sys/posix/sys_time.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_times.odin b/core/sys/posix/sys_times.odin index 73db489a7..636d3e153 100644 --- a/core/sys/posix/sys_times.odin +++ b/core/sys/posix/sys_times.odin @@ -2,7 +2,7 @@ package posix when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_uio.odin b/core/sys/posix/sys_uio.odin index 5770f8058..b4411851b 100644 --- a/core/sys/posix/sys_uio.odin +++ b/core/sys/posix/sys_uio.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin index 5ea8807a7..61f88b584 100644 --- a/core/sys/posix/sys_utsname.odin +++ b/core/sys/posix/sys_utsname.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin index d3bcdfddd..e12fcd212 100644 --- a/core/sys/posix/sys_wait.odin +++ b/core/sys/posix/sys_wait.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/termios.odin b/core/sys/posix/termios.odin index 4ca884e87..b385b7097 100644 --- a/core/sys/posix/termios.odin +++ b/core/sys/posix/termios.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin index 88f0153f4..7d55cf15b 100644 --- a/core/sys/posix/time.odin +++ b/core/sys/posix/time.odin @@ -5,7 +5,7 @@ import "core:c" import "core:c/libc" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/ulimit.odin b/core/sys/posix/ulimit.odin index 0f87641fa..892f6dee4 100644 --- a/core/sys/posix/ulimit.odin +++ b/core/sys/posix/ulimit.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin index b8020317c..b05f1e4fa 100644 --- a/core/sys/posix/unistd.odin +++ b/core/sys/posix/unistd.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/unistd_libc.odin b/core/sys/posix/unistd_libc.odin index 74edb6862..85d019f21 100644 --- a/core/sys/posix/unistd_libc.odin +++ b/core/sys/posix/unistd_libc.odin @@ -6,7 +6,7 @@ import "core:c" when ODIN_OS == .Windows { foreign import lib "system:libucrt.lib" } else when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/utime.odin b/core/sys/posix/utime.odin index 98c8166d6..fca0dee59 100644 --- a/core/sys/posix/utime.odin +++ b/core/sys/posix/utime.odin @@ -2,7 +2,7 @@ package posix when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } diff --git a/core/sys/posix/wordexp.odin b/core/sys/posix/wordexp.odin index a9e6f39a7..92d6aba40 100644 --- a/core/sys/posix/wordexp.odin +++ b/core/sys/posix/wordexp.odin @@ -4,7 +4,7 @@ package posix import "core:c" when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" + foreign import lib "system:System" } else { foreign import lib "system:c" } From 0e245fb40f12f0dd4ac53cc9c535e354a0540ea6 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 13 Jul 2025 20:17:30 -0400 Subject: [PATCH 23/49] Updated iOS/iPhoneSimulator build support --- base/builtin/builtin.odin | 2 +- base/runtime/core.odin | 1 + base/runtime/heap_allocator_unix.odin | 2 +- src/build_settings.cpp | 35 ++++++++++++----- src/checker.cpp | 7 ++-- src/linker.cpp | 54 ++++++++++++++++++++++++--- 6 files changed, 81 insertions(+), 20 deletions(-) diff --git a/base/builtin/builtin.odin b/base/builtin/builtin.odin index 0196e2030..af102ee0b 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`, and `.Android`. + Possible values are: `.Default` `.iOS`, .iPhoneSimulator, and `.Android`. */ ODIN_PLATFORM_SUBTARGET :: ODIN_PLATFORM_SUBTARGET diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 1dadbbf6f..6c43e6c16 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -558,6 +558,7 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{ Odin_Platform_Subtarget_Type :: enum int { Default, iOS, + iPhoneSimulator Android, } */ diff --git a/base/runtime/heap_allocator_unix.odin b/base/runtime/heap_allocator_unix.odin index d4590d2dd..f6e7ce39e 100644 --- a/base/runtime/heap_allocator_unix.odin +++ b/base/runtime/heap_allocator_unix.odin @@ -3,7 +3,7 @@ package runtime when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" + foreign import libc "system:System" } else { foreign import libc "system:c" } diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ebe57bf1e..312512568 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -172,6 +172,7 @@ struct TargetMetrics { enum Subtarget : u32 { Subtarget_Default, Subtarget_iOS, + Subtarget_iPhoneSimulator, Subtarget_Android, Subtarget_COUNT, @@ -180,6 +181,7 @@ enum Subtarget : u32 { gb_global String subtarget_strings[Subtarget_COUNT] = { str_lit(""), str_lit("ios"), + str_lit("iphonesimulator"), str_lit("android"), }; @@ -1824,16 +1826,29 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta } } - if (metrics->os == TargetOs_darwin && subtarget == Subtarget_iOS) { - switch (metrics->arch) { - case TargetArch_arm64: - bc->metrics.target_triplet = str_lit("arm64-apple-ios"); - break; - case TargetArch_amd64: - bc->metrics.target_triplet = str_lit("x86_64-apple-ios"); - break; - default: - GB_PANIC("Unknown architecture for darwin"); + if (metrics->os == TargetOs_darwin) { + switch (subtarget) { + case Subtarget_iOS: + switch (metrics->arch) { + case TargetArch_arm64: + bc->metrics.target_triplet = str_lit("arm64-apple-ios"); + break; + default: + GB_PANIC("Unknown architecture for -subtarget:ios"); + } + break; + case Subtarget_iPhoneSimulator: + switch (metrics->arch) { + case TargetArch_arm64: + bc->metrics.target_triplet = str_lit("arm64-apple-ios-simulator"); + break; + case TargetArch_amd64: + bc->metrics.target_triplet = str_lit("x86_64-apple-ios-simulator"); + break; + default: + GB_PANIC("Unknown architecture for -subtarget:iphonesimulator"); + } + break; } } else if (metrics->os == TargetOs_linux && subtarget == Subtarget_Android) { switch (metrics->arch) { diff --git a/src/checker.cpp b/src/checker.cpp index 4ebabe004..625536caa 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1171,9 +1171,10 @@ gb_internal void init_universal(void) { { GlobalEnumValue values[Subtarget_COUNT] = { - {"Default", Subtarget_Default}, - {"iOS", Subtarget_iOS}, - {"Android", Subtarget_Android}, + {"Default", Subtarget_Default}, + {"iOS", Subtarget_iOS}, + {"iPhoneSimulator", Subtarget_iPhoneSimulator}, + {"Android", Subtarget_Android}, }; auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values)); diff --git a/src/linker.cpp b/src/linker.cpp index bf2ba6fe0..01fa7065a 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -591,7 +591,7 @@ try_cross_linking:; // Do not add libc again, this is added later already, and omitted with // the `-no-crt` flag, not skipping here would cause duplicate library // warnings when linking on darwin and might link libc silently even with `-no-crt`. - if (lib == str_lit("System.framework") || lib == str_lit("c")) { + if (lib == str_lit("System.framework") || lib == str_lit("System") || lib == str_lit("c")) { continue; } @@ -772,10 +772,54 @@ try_cross_linking:; gbString platform_lib_str = gb_string_make(heap_allocator(), ""); defer (gb_string_free(platform_lib_str)); if (build_context.metrics.os == TargetOs_darwin) { - // Get the MacOSX SDK path. + // Get the SDK path. gbString darwin_sdk_path = gb_string_make(temporary_allocator(), ""); - if (!system_exec_command_line_app_output("xcrun --sdk macosx --show-sdk-path", &darwin_sdk_path)) { - darwin_sdk_path = gb_string_set(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"); + + char const* darwin_platform_name = "MacOSX"; + char const* darwin_xcrun_sdk_name = "macosx"; + char const* darwin_min_version_id = "macosx"; + + // NOTE(harold): We set the clang_path to run through xcrun because otherwise it complaints about the the sysroot + // 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: + darwin_platform_name = "iPhoneOS"; + darwin_xcrun_sdk_name = "iphoneos"; + darwin_min_version_id = "ios"; + clang_path = "xcrun --sdk iphoneos clang"; + break; + case Subtarget_iPhoneSimulator: + darwin_platform_name = "iPhoneSimulator"; + darwin_xcrun_sdk_name = "iphonesimulator"; + darwin_min_version_id = "ios-simulator"; + clang_path = "xcrun --sdk iphonesimulator clang"; + break; + } + + const char* original_clang_path = original_clang_path; + + gbString darwin_find_sdk_cmd = gb_string_make(temporary_allocator(), ""); + darwin_find_sdk_cmd = gb_string_append_fmt(darwin_find_sdk_cmd, "xcrun --sdk %s --show-sdk-path", darwin_xcrun_sdk_name); + + if (!system_exec_command_line_app_output(darwin_find_sdk_cmd, &darwin_sdk_path)) { + + // Fallback to default clang, since `xcrun --sdk` did not work. + clang_path = original_clang_path; + + // Best-effort fallback to known locations + gbString darwin_sdk_path = gb_string_make(temporary_allocator(), ""); + darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/%s.sdk", darwin_platform_name); + + if (!path_is_directory(make_string_c(darwin_sdk_path))) { + gb_string_clear(darwin_sdk_path); + darwin_sdk_path = gb_string_append_fmt(darwin_sdk_path, "/Applications/Xcode.app/Contents/Developer/Platforms/%s.platform/Developer/SDKs/%s.sdk", darwin_platform_name); + + if (!path_is_directory(make_string_c(darwin_sdk_path))) { + gb_printf_err("Failed to find %s SDK\n", darwin_platform_name); + return -1; + } + } } else { // Trim the trailing newline. darwin_sdk_path = gb_string_trim_space(darwin_sdk_path); @@ -797,7 +841,7 @@ try_cross_linking:; // Only specify this flag if the user has given a minimum version to target. // This will cause warnings to show up for mismatched libraries. if (build_context.minimum_os_version_string_given) { - link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string)); + link_settings = gb_string_append_fmt(link_settings, "-m%s-version-min=%.*s ", darwin_min_version_id, LIT(build_context.minimum_os_version_string)); } if (build_context.build_mode != BuildMode_DynamicLibrary) { From 4d2d890b4c87f3376626b88782e64f97e749eaa8 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Sun, 13 Jul 2025 20:29:54 -0400 Subject: [PATCH 24/49] Fix accidental incorrect assignment --- src/linker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linker.cpp b/src/linker.cpp index 01fa7065a..bf7705282 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -797,7 +797,7 @@ try_cross_linking:; break; } - const char* original_clang_path = original_clang_path; + const char* original_clang_path = clang_path; gbString darwin_find_sdk_cmd = gb_string_make(temporary_allocator(), ""); darwin_find_sdk_cmd = gb_string_append_fmt(darwin_find_sdk_cmd, "xcrun --sdk %s --show-sdk-path", darwin_xcrun_sdk_name); From 070943aa9865bda8c163dc76fde70a2602d447c4 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 14 Jul 2025 12:53:01 -0400 Subject: [PATCH 25/49] Provide default minimum version for iOS and apply its target triplet. - Fix incorrect clang_path override for iOS during link stage. --- src/build_settings.cpp | 12 ++++++++---- src/linker.cpp | 14 ++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 312512568..094cf07f0 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1907,12 +1907,16 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta // does not annoy the user with version warnings. if (metrics->os == TargetOs_darwin) { if (!bc->minimum_os_version_string_given) { - bc->minimum_os_version_string = str_lit("11.0.0"); + if (subtarget == Subtarget_Default) { + bc->minimum_os_version_string = str_lit("11.0.0"); + } else if (subtarget == Subtarget_iOS || 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"); + } } - if (subtarget == Subtarget_Default) { - bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string); - } + bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string); } else if (selected_subtarget == Subtarget_Android) { init_android_values(bc->build_mode == BuildMode_Executable && (bc->command_kind & Command__does_build) != 0); } diff --git a/src/linker.cpp b/src/linker.cpp index bf7705282..413995e0d 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -431,8 +431,10 @@ try_cross_linking:; // Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable. const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator()); + bool has_odin_clang_path_env = true; if (clang_path == NULL) { clang_path = "clang"; + has_odin_clang_path_env = false; } // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library @@ -779,6 +781,8 @@ try_cross_linking:; char const* darwin_xcrun_sdk_name = "macosx"; char const* darwin_min_version_id = "macosx"; + const char* original_clang_path = clang_path; + // NOTE(harold): We set the clang_path to run through xcrun because otherwise it complaints about the the sysroot // 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. @@ -787,18 +791,20 @@ try_cross_linking:; darwin_platform_name = "iPhoneOS"; darwin_xcrun_sdk_name = "iphoneos"; darwin_min_version_id = "ios"; - clang_path = "xcrun --sdk iphoneos clang"; + if (!has_odin_clang_path_env) { + clang_path = "xcrun --sdk iphoneos clang"; + } break; case Subtarget_iPhoneSimulator: darwin_platform_name = "iPhoneSimulator"; darwin_xcrun_sdk_name = "iphonesimulator"; darwin_min_version_id = "ios-simulator"; - clang_path = "xcrun --sdk iphonesimulator clang"; + if (!has_odin_clang_path_env) { + clang_path = "xcrun --sdk iphonesimulator clang"; + } break; } - const char* original_clang_path = clang_path; - gbString darwin_find_sdk_cmd = gb_string_make(temporary_allocator(), ""); darwin_find_sdk_cmd = gb_string_append_fmt(darwin_find_sdk_cmd, "xcrun --sdk %s --show-sdk-path", darwin_xcrun_sdk_name); From 77e5c7141448219a6a0fc40f281951c4e8a5c0eb Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 14 Jul 2025 14:24:15 -0400 Subject: [PATCH 26/49] Fix correct versioned target triplet for iphonesimulator subtarget - Always set the `-m*-version-min` linker flag for non-macOS Darwin subtargets --- src/build_settings.cpp | 11 ++++++++++- src/linker.cpp | 4 +++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 094cf07f0..d98340844 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1916,7 +1916,16 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta } } - bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string); + if (subtarget == Subtarget_iPhoneSimulator) { + // For the iOS simulator subtarget, the version must be between 'ios' and '-simulator'. + String suffix = str_lit("-simulator"); + GB_ASSERT(string_ends_with(bc->metrics.target_triplet, suffix)); + + String prefix = substring(bc->metrics.target_triplet, 0, bc->metrics.target_triplet.len - suffix.len); + bc->metrics.target_triplet = concatenate3_strings(permanent_allocator(), prefix, bc->minimum_os_version_string, suffix); + } else { + bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string); + } } else if (selected_subtarget == Subtarget_Android) { init_android_values(bc->build_mode == BuildMode_Executable && (bc->command_kind & Command__does_build) != 0); } diff --git a/src/linker.cpp b/src/linker.cpp index 413995e0d..7647ed872 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -846,7 +846,9 @@ try_cross_linking:; // Only specify this flag if the user has given a minimum version to target. // This will cause warnings to show up for mismatched libraries. - if (build_context.minimum_os_version_string_given) { + // NOTE(harold): For device subtargets we have to explicitly set the default version to + // avoid the same warning since we configure our own minimum version when compiling for devices. + if (build_context.minimum_os_version_string_given || selected_subtarget != Subtarget_Default) { link_settings = gb_string_append_fmt(link_settings, "-m%s-version-min=%.*s ", darwin_min_version_id, LIT(build_context.minimum_os_version_string)); } From bab4ce11fc1d52e2a63ed950fcf3cb0510cbe642 Mon Sep 17 00:00:00 2001 From: Harold Brenes Date: Mon, 14 Jul 2025 21:55:28 -0400 Subject: [PATCH 27/49] 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 28/49] 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 29/49] 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 30/49] 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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 QyN`s>I;HLZ6e8vS1L~-|-v9sr diff --git a/vendor/kb_text_shape/src/kb_text_shape.h b/vendor/kb_text_shape/src/kb_text_shape.h index 43949fecf..57d3ca7a0 100644 --- a/vendor/kb_text_shape/src/kb_text_shape.h +++ b/vendor/kb_text_shape/src/kb_text_shape.h @@ -1,4 +1,4 @@ -/* kb_text_shape - v1.0 - text segmentation and shaping +/* kb_text_shape - v1.03 - text segmentation and shaping by Jimmy Lefevre SECURITY @@ -35,16 +35,52 @@ kbts_FreeShapeState() kbts_FontFromFile() kbts_FreeFont() + Additionally, we call KBTS_MEMSET(), which defaults to memset(), including outside of NO_CRT code. + If you want to redirect it to your own function, you can do this: + #define KBTS_MEMSET my_awesome_memset API Segmentation kbts_BeginBreak() - kbts_BreakAddCodepoint() -- Feed a codepoint to the breaker, call kbts_Break() after this + kbts_BreakAddCodepoint() -- Feed a codepoint to the breaker. + You need to call Break() repeatedly after every call to BreakAddCodepoint(). + Something like: + kbts_BreakAddCodepoint(&BreakState, ...); + kbts_break Break; + while(kbts_Break(&BreakState, &Break)) {...} + + When you call Break(), We guarantee that breaks are returned in-order. On our side, this means + that they are buffered and reordered. On your side, it means that there is a delay of a few + characters between your current position and the Break.Position that you will see. + + In some cases, our buffering might break. When that happens, we set + BREAK_STATE_FLAG_RAN_OUT_OF_REORDER_BUFFER_SPACE, and kbts_BreakStateIsValid() will return false. + This is a sticky error, so you can check it whenever you like. + To clear the error flag and start segmenting again, you will need to call BeginBreak(&BreakState), + which resets the entire state. + + Note that the input configurations for which our buffering breaks should be, for all intents and + purposes, nonsensical. If you find legitimate text that we cannot segment without running out of + buffer space, then that is a bug. + + The default buffer size is determined by the BREAK_REORDER_BUFFER_FLUSH_THRESHOLD. If you really + need a bigger buffer, then you might want to consider modifying this constant and recompiling + the library, although this should be viewed as an emergency solution and not a routine + configuration option. kbts_BreakFlush() kbts_Break() -- Call repeatedly to get breaks kbts_BreakStateIsValid() Easy font loading - kbts_FontFromFile() -- Open a font, byteswap it in place, and allocate auxiliary structures + kbts_FontFromFile() -- Open a font, byteswap it in place, and allocate auxiliary structures. + When you read a font with kb_text_shape, the library will byteswap its data in-place and perform + a bunch of other pre-computation passes to figure out memory limits and other useful information. + This means you cannot trivially pass our pointer to the font data to any other TTF library, since + they will expect the data to be in big endian format, which it won't be after we are done with it. + + You can expect font reading to be pretty slow. + On the other hand, you can expect shaping to be pretty fast. + + To open a font with your own IO and memory allocation, see "Manual Memory Management" below. kbts_FreeFont() kbts_FontIsValid() Shaping @@ -52,23 +88,76 @@ kbts_FreeShapeState() kbts_ShapeConfig() -- Bake a font/script-specific shaping configuration kbts_CodepointToGlyph() - kbts_InferScript() - kbts_Shape() -- Returns 1 if more memory is needed, you should probably call this in a while() + kbts_InferScript() -- Hacky script recognition for when no segmentation data is available + kbts_Shape() -- Returns 1 if more memory is needed, you should probably call this in a while(). + This is how you might call this in practice: + while(kbts_Shape(State, &Config, Direction, Direction, Glyphs, &GlyphCount, GlyphCapacity)) + { + Glyphs = realloc(Glyphs, sizeof(kbts_glyph) * State->RequiredGlyphCapacity); + 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 31/49] 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 32/49] 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 33/49] 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 34/49] 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 35/49] 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 36/49] 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 37/49] 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 38/49] 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 39/49] 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 40/49] 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 41/49] 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 42/49] 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 43/49] 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 44/49] 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 45/49] 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 46/49] 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 47/49] 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 48/49] 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 93a39affd1470d47f0991ec23b7bf1ae2be15818 Mon Sep 17 00:00:00 2001 From: Paul-Andre Henegar Date: Mon, 28 Jul 2025 01:42:19 -0400 Subject: [PATCH 49/49] 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;