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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 + 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;