diff --git a/.gitignore b/.gitignore index 52a4782b2..750145c58 100644 --- a/.gitignore +++ b/.gitignore @@ -303,7 +303,7 @@ bin/ # - Linux/MacOS odin !odin/ -odin.dSYM +**/*.dSYM *.bin demo.bin libLLVM*.so* diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 4f6fa2713..635c85e6f 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -73,6 +73,8 @@ expect :: proc(val, expected_val: T) -> T --- // Linux and Darwin Only syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr --- +// FreeBSD, NetBSD, et cetera +syscall_bsd :: proc(id: uintptr, args: ..uintptr) -> (uintptr, bool) --- // Atomics diff --git a/base/runtime/default_allocators_arena.odin b/base/runtime/default_temp_allocator_arena.odin similarity index 98% rename from base/runtime/default_allocators_arena.odin rename to base/runtime/default_temp_allocator_arena.odin index 571590f93..ab9f7df4a 100644 --- a/base/runtime/default_allocators_arena.odin +++ b/base/runtime/default_temp_allocator_arena.odin @@ -12,7 +12,8 @@ Memory_Block :: struct { capacity: uint, } -// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary +// NOTE: This is a growing arena that is only used for the default temp allocator. +// For your own growing arena needs, prefer `Arena` from `core:mem/virtual`. Arena :: struct { backing_allocator: Allocator, curr_block: ^Memory_Block, diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 205d7eb7e..0a9cab860 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -73,7 +73,12 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, _ = read_u64(r) } - r := &global_rand_seed + r: ^Default_Random_State = --- + if data == nil { + r = &global_rand_seed + } else { + r = cast(^Default_Random_State)data + } switch mode { case .Read: diff --git a/core/container/avl/avl.odin b/core/container/avl/avl.odin index 582cd87fd..8a9d1f3d9 100644 --- a/core/container/avl/avl.odin +++ b/core/container/avl/avl.odin @@ -87,7 +87,7 @@ init_cmp :: proc( init_ordered :: proc( t: ^$T/Tree($Value), node_allocator := context.allocator, -) where intrinsics.type_is_ordered_numeric(Value) { +) where intrinsics.type_is_ordered(Value) { init_cmp(t, slice.cmp_proc(Value), node_allocator) } diff --git a/core/container/rbtree/rbtree.odin b/core/container/rbtree/rbtree.odin index 8ab131b3b..57a68a948 100644 --- a/core/container/rbtree/rbtree.odin +++ b/core/container/rbtree/rbtree.odin @@ -79,7 +79,7 @@ init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering, // init_ordered initializes a tree containing ordered keys, with // a comparison function that results in an ascending order sort. -init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered_numeric(Key) { +init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered(Key) { init_cmp(t, slice.cmp_proc(Key), node_allocator) } diff --git a/core/flags/LICENSE b/core/flags/LICENSE new file mode 100644 index 000000000..e4e21e62d --- /dev/null +++ b/core/flags/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, Feoramund + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/core/flags/constants.odin b/core/flags/constants.odin new file mode 100644 index 000000000..ab3dc9a0a --- /dev/null +++ b/core/flags/constants.odin @@ -0,0 +1,38 @@ +package flags + +import "core:time" + +// Set to true to compile with support for core named types disabled, as a +// fallback in the event your platform does not support one of the types, or +// you have no need for them and want a smaller binary. +NO_CORE_NAMED_TYPES :: #config(ODIN_CORE_FLAGS_NO_CORE_NAMED_TYPES, false) + +// Override support for parsing `time` types. +IMPORTING_TIME :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED) + +// Override support for parsing `net` types. +// TODO: Update this when the BSDs are supported. +IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin) + +TAG_ARGS :: "args" +SUBTAG_NAME :: "name" +SUBTAG_POS :: "pos" +SUBTAG_REQUIRED :: "required" +SUBTAG_HIDDEN :: "hidden" +SUBTAG_VARIADIC :: "variadic" +SUBTAG_FILE :: "file" +SUBTAG_PERMS :: "perms" +SUBTAG_INDISTINCT :: "indistinct" + +TAG_USAGE :: "usage" + +UNDOCUMENTED_FLAG :: "" + +INTERNAL_VARIADIC_FLAG :: "varg" + +RESERVED_HELP_FLAG :: "help" +RESERVED_HELP_FLAG_SHORT :: "h" + +// If there are more than this number of flags in total, only the required and +// positional flags will be shown in the one-line usage summary. +ONE_LINE_FLAG_CUTOFF_COUNT :: 16 diff --git a/core/flags/doc.odin b/core/flags/doc.odin new file mode 100644 index 000000000..c3663c419 --- /dev/null +++ b/core/flags/doc.odin @@ -0,0 +1,181 @@ +/* +package flags implements a command-line argument parser. + +It works by using Odin's run-time type information to determine where and how +to store data on a struct provided by the program. Type conversion is handled +automatically and errors are reported with useful messages. + + +Command-Line Syntax: + +Arguments are treated differently depending on how they're formatted. +The format is similar to the Odin binary's way of handling compiler flags. + +``` +type handling +------------ ------------------------ + depends on struct layout +- set a bool true +- set flag to option +- set flag to option, alternative syntax +-:= set map[key] to value +``` + + +Struct Tags: + +Users of the `core:encoding/json` package may be familiar with using tags to +annotate struct metadata. The same technique is used here to annotate where +arguments should go and which are required. + +Under the `args` tag, there are the following subtags: + +- `name=S`: set `S` as the flag's name. +- `pos=N`: place positional argument `N` into this flag. +- `hidden`: hide this flag from the usage documentation. +- `required`: cause verification to fail if this argument is not set. +- `variadic`: take all remaining arguments when set, UNIX-style only. +- `file`: for `os.Handle` types, file open mode. +- `perms`: for `os.Handle` types, file open permissions. +- `indistinct`: allow the setting of distinct types by their base type. + +`required` may be given a range specifier in the following formats: +``` +min + ( + error: string, + handled: bool, + alloc_error: runtime.Allocator_Error, +) { + if data_type == Fixed_Point1_1 { + handled = true + ptr := cast(^Fixed_Point1_1)data + + // precision := flags.get_subtag(args_tag, "precision") + + if len(unparsed_value) == 3 { + ptr.integer = unparsed_value[0] - '0' + ptr.fractional = unparsed_value[2] - '0' + } else { + error = "Incorrect format. Must be in the form of `i.f`." + } + + // Perform sanity checking here in the type parsing phase. + // + // The validation phase is flag-specific. + if !(0 <= ptr.integer && ptr.integer < 10) || !(0 <= ptr.fractional && ptr.fractional < 10) { + error = "Incorrect format. Must be between `0.0` and `9.9`." + } + } + + return +} + +my_custom_flag_checker :: proc( + model: rawptr, + name: string, + value: any, + args_tag: string, +) -> (error: string) { + if name == "iterations" { + v := value.(int) + if !(1 <= v && v < 5) { + error = "Iterations only supports 1 ..< 5." + } + } + + return +} + +Distinct_Int :: distinct int + +main :: proc() { + Options :: struct { + + file: os.Handle `args:"pos=0,required,file=r" usage:"Input file."`, + output: os.Handle `args:"pos=1,file=cw" usage:"Output file."`, + + hub: net.Host_Or_Endpoint `usage:"Internet address to contact for updates."`, + schedule: datetime.DateTime `usage:"Launch tasks at this time."`, + + opt: Optimization_Level `usage:"Optimization level."`, + todo: [dynamic]string `usage:"Todo items."`, + + accuracy: Fixed_Point1_1 `args:"required" usage:"Lenience in FLOP calculations."`, + iterations: int `usage:"Run this many times."`, + + // Note how the parser will transform this flag's name into `special-int`. + special_int: Distinct_Int `args:"indistinct" usage:"Able to set distinct types."`, + + quat: quaternion256, + + bits: bit_set[0..<8], + + // Many different requirement styles: + + // gadgets: [dynamic]string `args:"required=1" usage:"gadgets"`, + // widgets: [dynamic]string `args:"required=<3" usage:"widgets"`, + // foos: [dynamic]string `args:"required=2<4"`, + // bars: [dynamic]string `args:"required=3<4"`, + // bots: [dynamic]string `args:"required"`, + + // (Maps) Only available in Odin style: + + // assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`, + + // (Variadic) Only available in UNIX style: + + // bots: [dynamic]string `args:"variadic=2,required"`, + + verbose: bool `usage:"Show verbose output."`, + debug: bool `args:"hidden" usage:"print debug info"`, + + varg: [dynamic]string `usage:"Any extra arguments go here."`, + } + + opt: Options + style : flags.Parsing_Style = .Odin + + flags.register_type_setter(my_custom_type_setter) + flags.register_flag_checker(my_custom_flag_checker) + flags.parse_or_exit(&opt, os.args, style) + + fmt.printfln("%#v", opt) + + if opt.output != 0 { + os.write_string(opt.output, "Hellope!\n") + } +} diff --git a/core/flags/internal_assignment.odin b/core/flags/internal_assignment.odin new file mode 100644 index 000000000..4c4ee58fe --- /dev/null +++ b/core/flags/internal_assignment.odin @@ -0,0 +1,262 @@ +//+private +package flags + +import "base:intrinsics" +@require import "base:runtime" +import "core:container/bit_array" +@require import "core:fmt" +@require import "core:mem" +import "core:reflect" +@require import "core:strconv" +@require import "core:strings" + +// Push a positional argument onto a data struct, checking for specified +// positionals first before adding it to a fallback field. +@(optimization_mode="size") +push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) { + if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) { + // The max index is set, which means we're out of space. + // Add one free bit by setting the index above to false. + bit_array.set(&parser.filled_pos, 1 + parser.filled_pos.max_index, false) + } + + pos: int = --- + { + iter := bit_array.make_iterator(&parser.filled_pos) + ok: bool + pos, ok = bit_array.iterate_by_unset(&iter) + + // This may be an allocator error. + assert(ok, "Unable to find a free spot in the positional bit_array.") + } + + field, index, has_pos_assigned := get_field_by_pos(model, pos) + + if !has_pos_assigned { + when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) { + // Add it to the fallback array. + field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG) + } else { + return Parse_Error { + .Extra_Positional, + fmt.tprintf("Got extra positional argument `%s` with nowhere to store it.", arg), + } + } + } + + ptr := cast(rawptr)(cast(uintptr)model + field.offset) + args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS) + field_name := get_field_name(field) + error = parse_and_set_pointer_by_type(ptr, arg, field.type, args_tag) + #partial switch &specific_error in error { + case Parse_Error: + specific_error.message = fmt.tprintf("Unable to set positional #%i (%s) of type %v to `%s`.%s%s", + pos, + field_name, + field.type, + arg, + " " if len(specific_error.message) > 0 else "", + specific_error.message) + case nil: + bit_array.set(&parser.filled_pos, pos) + bit_array.set(&parser.fields_set, index) + } + + return +} + +register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int) { + if pos, ok := get_field_pos(field); ok { + bit_array.set(&parser.filled_pos, pos) + } + + bit_array.set(&parser.fields_set, index) +} + +// Set a `-flag` argument, Odin-style. +@(optimization_mode="size") +set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) { + // We make a special case for help requests. + switch name { + case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT: + return Help_Request{} + } + + field, index := get_field_by_name(model, name) or_return + + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Boolean: + ptr := cast(^bool)(cast(uintptr)model + field.offset) + ptr^ = true + case: + return Parse_Error { + .Bad_Value, + fmt.tprintf("Unable to set `%s` of type %v to true.", name, field.type), + } + } + + register_field(parser, field, index) + return +} + +// Set a `-flag` argument, UNIX-style. +@(optimization_mode="size") +set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) { + // We make a special case for help requests. + switch name { + case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT: + return 0, Help_Request{} + } + + field, index := get_field_by_name(model, name) or_return + + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Boolean: + ptr := cast(^bool)(cast(uintptr)model + field.offset) + ptr^ = true + case runtime.Type_Info_Dynamic_Array: + future_args = 1 + if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok { + if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic { + // Variadic arrays may specify how many arguments they consume at once. + // Otherwise, they take everything that's left. + if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok { + future_args = cast(int)value + } else { + future_args = max(int) + } + } + } + case: + // `--flag`, waiting on its value. + future_args = 1 + } + + register_field(parser, field, index) + return +} + +// Set a `-flag:option` argument. +@(optimization_mode="size") +set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) { + field, index := get_field_by_name(model, name) or_return + + if len(option) == 0 { + return Parse_Error { + .No_Value, + fmt.tprintf("Setting `%s` to an empty value is meaningless.", name), + } + } + + // Guard against incorrect syntax. + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + return Parse_Error { + .No_Value, + fmt.tprintf("Unable to set `%s` of type %v to `%s`. Are you missing an `=`? The correct format is `map:key=value`.", name, field.type, option), + } + } + + ptr := cast(rawptr)(cast(uintptr)model + field.offset) + args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS) + error = parse_and_set_pointer_by_type(ptr, option, field.type, args_tag) + #partial switch &specific_error in error { + case Parse_Error: + specific_error.message = fmt.tprintf("Unable to set `%s` of type %v to `%s`.%s%s", + name, + field.type, + option, + " " if len(specific_error.message) > 0 else "", + specific_error.message) + case nil: + register_field(parser, field, index) + } + + return +} + +// Set a `-map:key=value` argument. +@(optimization_mode="size") +set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) { + field, index := get_field_by_name(model, name) or_return + + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + key := key + key_ptr := cast(rawptr)&key + key_cstr: cstring + if reflect.is_cstring(specific_type_info.key) { + // We clone the key here, because it's liable to be a slice of an + // Odin string, and we need to put a NUL terminator in it. + key_cstr = strings.clone_to_cstring(key) + key_ptr = &key_cstr + } + defer if key_cstr != nil { + delete(key_cstr) + } + + raw_map := (^runtime.Raw_Map)(cast(uintptr)model + field.offset) + + hash := specific_type_info.map_info.key_hasher(key_ptr, runtime.map_seed(raw_map^)) + + backing_alloc := false + elem_backing: []byte + value_ptr: rawptr + + if raw_map.allocator.procedure == nil { + raw_map.allocator = context.allocator + } else { + value_ptr = runtime.__dynamic_map_get(raw_map, + specific_type_info.map_info, + hash, + key_ptr, + ) + } + + if value_ptr == nil { + alloc_error: runtime.Allocator_Error = --- + elem_backing, alloc_error = mem.alloc_bytes(specific_type_info.value.size, specific_type_info.value.align) + if elem_backing == nil { + return Parse_Error { + alloc_error, + "Failed to allocate element backing for map value.", + } + } + + backing_alloc = true + value_ptr = raw_data(elem_backing) + } + + args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS) + error = parse_and_set_pointer_by_type(value_ptr, value, specific_type_info.value, args_tag) + #partial switch &specific_error in error { + case Parse_Error: + specific_error.message = fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.%s%s", + name, + field.type, + key, + value, + " " if len(specific_error.message) > 0 else "", + specific_error.message) + } + + if backing_alloc { + runtime.__dynamic_map_set(raw_map, + specific_type_info.map_info, + hash, + key_ptr, + value_ptr, + ) + + delete(elem_backing) + } + + register_field(parser, field, index) + return + } + + return Parse_Error { + .Bad_Value, + fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.", name, field.type, key, value), + } +} diff --git a/core/flags/internal_parsing.odin b/core/flags/internal_parsing.odin new file mode 100644 index 000000000..7a769b17c --- /dev/null +++ b/core/flags/internal_parsing.odin @@ -0,0 +1,170 @@ +//+private +package flags + +import "core:container/bit_array" +import "core:strconv" +import "core:strings" + +// Used to group state together. +Parser :: struct { + // `fields_set` tracks which arguments have been set. + // It uses their struct field index. + fields_set: bit_array.Bit_Array, + + // `filled_pos` tracks which arguments have been filled into positional + // spots, much like how `fmt` treats them. + filled_pos: bit_array.Bit_Array, +} + +parse_one_odin_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (error: Error) { + arg := arg + + if strings.has_prefix(arg, "-") { + arg = arg[1:] + + flag: string + assignment_rune: rune + find_assignment: for r, i in arg { + switch r { + case ':', '=': + assignment_rune = r + flag = arg[:i] + arg = arg[1 + i:] + break find_assignment + case: + continue find_assignment + } + } + + if assignment_rune == 0 { + if len(arg) == 0 { + return Parse_Error { + .No_Flag, + "No flag was given.", + } + } + + // -flag + set_odin_flag(model, parser, arg) or_return + + } else if assignment_rune == ':' { + // -flag:option -map:key=value + error = set_option(model, parser, flag, arg) + + if error != nil { + // -flag:option did not work, so this may be a -map:key=value set. + find_equals: for r, i in arg { + if r == '=' { + key := arg[:i] + arg = arg[1 + i:] + error = set_key_value(model, parser, flag, key, arg) + break find_equals + } + } + } + + } else { + // -flag=option, alternative syntax + set_option(model, parser, flag, arg) or_return + } + + } else { + // positional + error = push_positional(model, parser, arg) + } + + return +} + +parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> ( + future_args: int, + current_flag: string, + error: Error, +) { + arg := arg + + if strings.has_prefix(arg, "-") { + // -flag + arg = arg[1:] + + if strings.has_prefix(arg, "-") { + // Allow `--` to function as `-`. + arg = arg[1:] + + if len(arg) == 0 { + // `--`, and only `--`. + // Everything from now on will be treated as an argument. + future_args = max(int) + current_flag = INTERNAL_VARIADIC_FLAG + return + } + } + + flag: string + find_assignment: for r, i in arg { + if r == '=' { + // --flag=option + flag = arg[:i] + arg = arg[1 + i:] + error = set_option(model, parser, flag, arg) + return + } + } + + // --flag option, potentially + future_args = set_unix_flag(model, parser, arg) or_return + current_flag = arg + + } else { + // positional + error = push_positional(model, parser, arg) + } + + return +} + +// Parse a number of requirements specifier. +// +// Examples: +// +// `min` +// ` (minimum, maximum: int, ok: bool) { + if len(str) == 0 { + return 1, max(int), true + } + + if less_than := strings.index_byte(str, '<'); less_than != -1 { + if len(str) == 1 { + return 0, 0, false + } + + #no_bounds_check left := str[:less_than] + #no_bounds_check right := str[1 + less_than:] + + if left_value, parse_ok := strconv.parse_u64_of_base(left, 10); parse_ok { + minimum = cast(int)left_value + } else if len(left) > 0 { + return 0, 0, false + } + + if right_value, parse_ok := strconv.parse_u64_of_base(right, 10); parse_ok { + maximum = cast(int)right_value + } else if len(right) > 0 { + return 0, 0, false + } else { + maximum = max(int) + } + } else { + if value, parse_ok := strconv.parse_u64_of_base(str, 10); parse_ok { + minimum = cast(int)value + maximum = max(int) + } else { + return 0, 0, false + } + } + + ok = true + return +} diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin new file mode 100644 index 000000000..c9b1f59fd --- /dev/null +++ b/core/flags/internal_rtti.odin @@ -0,0 +1,536 @@ +//+private +package flags + +import "base:intrinsics" +import "base:runtime" +import "core:fmt" +import "core:mem" +import "core:os" +import "core:reflect" +import "core:strconv" +import "core:strings" +@require import "core:time" +@require import "core:time/datetime" +import "core:unicode/utf8" + +@(optimization_mode="size") +parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool { + bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) { + return value, min <= value && value <= max + } + + bounded_uint :: proc(value, max: u128) -> (result: u128, ok: bool) { + return value, value <= max + } + + // NOTE(Feoramund): This procedure has been written with the goal in mind + // of generating the least amount of assembly, given that this library is + // likely to be called once and forgotten. + // + // I've rewritten the switch tables below in 3 different ways, and the + // current one generates the least amount of code for me on Linux AMD64. + // + // The other two ways were: + // + // - the original implementation: use of parametric polymorphism which led + // to dozens of functions generated, one for each type. + // + // - a `value, ok` assignment statement with the `or_return` done at the + // end of the switch, instead of inline. + // + // This seems to be the smallest way for now. + + #partial switch specific_type_info in type_info.variant { + case runtime.Type_Info_Integer: + if specific_type_info.signed { + value := strconv.parse_i128(str) or_return + switch type_info.id { + case i8: (cast(^i8) ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return + case i16: (cast(^i16) ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return + case i32: (cast(^i32) ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return + case i64: (cast(^i64) ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return + case i128: (cast(^i128) ptr)^ = value + + case int: (cast(^int) ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return + + case i16le: (cast(^i16le) ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return + case i32le: (cast(^i32le) ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return + case i64le: (cast(^i64le) ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return + case i128le: (cast(^i128le)ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return + + case i16be: (cast(^i16be) ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return + case i32be: (cast(^i32be) ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return + case i64be: (cast(^i64be) ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return + case i128be: (cast(^i128be)ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return + } + } else { + value := strconv.parse_u128(str) or_return + switch type_info.id { + case u8: (cast(^u8) ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return + case u16: (cast(^u16) ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return + case u32: (cast(^u32) ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return + case u64: (cast(^u64) ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return + case u128: (cast(^u128) ptr)^ = value + + case uint: (cast(^uint) ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return + case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return + + case u16le: (cast(^u16le) ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return + case u32le: (cast(^u32le) ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return + case u64le: (cast(^u64le) ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return + case u128le: (cast(^u128le) ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return + + case u16be: (cast(^u16be) ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return + case u32be: (cast(^u32be) ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return + case u64be: (cast(^u64be) ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return + case u128be: (cast(^u128be) ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return + } + } + + case runtime.Type_Info_Rune: + if utf8.rune_count_in_string(str) != 1 { + return false + } + + (cast(^rune)ptr)^ = utf8.rune_at_pos(str, 0) + + case runtime.Type_Info_Float: + value := strconv.parse_f64(str) or_return + switch type_info.id { + case f16: (cast(^f16) ptr)^ = cast(f16) value + case f32: (cast(^f32) ptr)^ = cast(f32) value + case f64: (cast(^f64) ptr)^ = value + + case f16le: (cast(^f16le)ptr)^ = cast(f16le) value + case f32le: (cast(^f32le)ptr)^ = cast(f32le) value + case f64le: (cast(^f64le)ptr)^ = cast(f64le) value + + case f16be: (cast(^f16be)ptr)^ = cast(f16be) value + case f32be: (cast(^f32be)ptr)^ = cast(f32be) value + case f64be: (cast(^f64be)ptr)^ = cast(f64be) value + } + + case runtime.Type_Info_Complex: + value := strconv.parse_complex128(str) or_return + switch type_info.id { + case complex128: (cast(^complex128)ptr)^ = value + case complex64: (cast(^complex64) ptr)^ = cast(complex64)value + case complex32: (cast(^complex32) ptr)^ = cast(complex32)value + } + + case runtime.Type_Info_Quaternion: + value := strconv.parse_quaternion256(str) or_return + switch type_info.id { + case quaternion256: (cast(^quaternion256)ptr)^ = value + case quaternion128: (cast(^quaternion128)ptr)^ = cast(quaternion128)value + case quaternion64: (cast(^quaternion64) ptr)^ = cast(quaternion64)value + } + + case runtime.Type_Info_String: + if specific_type_info.is_cstring { + cstr_ptr := cast(^cstring)ptr + if cstr_ptr != nil { + // Prevent memory leaks from us setting this value multiple times. + delete(cstr_ptr^) + } + cstr_ptr^ = strings.clone_to_cstring(str) + } else { + (cast(^string)ptr)^ = str + } + + case runtime.Type_Info_Boolean: + value := strconv.parse_bool(str) or_return + switch type_info.id { + case bool: (cast(^bool) ptr)^ = value + case b8: (cast(^b8) ptr)^ = cast(b8) value + case b16: (cast(^b16) ptr)^ = cast(b16) value + case b32: (cast(^b32) ptr)^ = cast(b32) value + case b64: (cast(^b64) ptr)^ = cast(b64) value + } + + case runtime.Type_Info_Bit_Set: + // Parse a string of 1's and 0's, from left to right, + // least significant bit to most significant bit. + value: u128 + + // NOTE: `upper` is inclusive, i.e: `0..=31` + max_bit_index := cast(u128)(1 + specific_type_info.upper - specific_type_info.lower) + bit_index : u128 = 0 + #no_bounds_check for string_index : uint = 0; string_index < len(str); string_index += 1 { + if bit_index == max_bit_index { + // The string's too long for this bit_set. + return false + } + + switch str[string_index] { + case '1': + value |= 1 << bit_index + bit_index += 1 + case '0': + bit_index += 1 + continue + case '_': + continue + case: + return false + } + } + + if specific_type_info.underlying != nil { + set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id) + } else { + switch 8*type_info.size { + case 8: (cast(^u8) ptr)^ = cast(u8) value + case 16: (cast(^u16) ptr)^ = cast(u16) value + case 32: (cast(^u32) ptr)^ = cast(u32) value + case 64: (cast(^u64) ptr)^ = cast(u64) value + case 128: (cast(^u128) ptr)^ = cast(u128) value + } + } + + case: + fmt.panicf("Unsupported base data type: %v", specific_type_info) + } + + return true +} + +// This proc exists to make error handling easier, since everything in the base +// type one above works on booleans. It's a simple parsing error if it's false. +// +// However, here we have to be more careful about how we handle errors, +// especially with files. +// +// We want to provide as informative as an error as we can. +@(optimization_mode="size", disabled=NO_CORE_NAMED_TYPES) +parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) { + // Core types currently supported: + // + // - os.Handle + // - time.Time + // - datetime.DateTime + // - net.Host_Or_Endpoint + + GENERIC_RFC_3339_ERROR :: "Invalid RFC 3339 string. Try this format: `yyyy-mm-ddThh:mm:ssZ`, for example `2024-02-29T16:30:00Z`." + + out_error^ = nil + + if data_type == os.Handle { + // NOTE: `os` is hopefully available everywhere, even if it might panic on some calls. + wants_read := false + wants_write := false + mode: int + + if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok { + for i := 0; i < len(file); i += 1 { + #no_bounds_check switch file[i] { + case 'r': wants_read = true + case 'w': wants_write = true + case 'c': mode |= os.O_CREATE + case 'a': mode |= os.O_APPEND + case 't': mode |= os.O_TRUNC + } + } + } + + // Sane default. + // owner/group/other: r--r--r-- + perms: int = 0o444 + + if wants_read && wants_write { + mode |= os.O_RDWR + perms |= 0o200 + } else if wants_write { + mode |= os.O_WRONLY + perms |= 0o200 + } else { + mode |= os.O_RDONLY + } + + if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok { + if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok { + perms = cast(int)value + } + } + + handle, errno := os.open(str, mode, perms) + if errno != 0 { + // NOTE(Feoramund): os.Errno is system-dependent, and there's + // currently no good way to translate them all into strings. + // + // The upcoming `os2` package will hopefully solve this. + // + // We can at least provide the number for now, so the user can look + // it up. + out_error^ = Open_File_Error { + str, + errno, + mode, + perms, + } + return + } + + (cast(^os.Handle)ptr)^ = handle + return + } + + when IMPORTING_TIME { + if data_type == time.Time { + // NOTE: The leap second data is discarded. + res, consumed := time.rfc3339_to_time_utc(str) + if consumed == 0 { + // The RFC 3339 parsing facilities provide no indication as to what + // went wrong, so just treat it as a regular parsing error. + out_error^ = Parse_Error { + .Bad_Value, + GENERIC_RFC_3339_ERROR, + } + return + } + + (cast(^time.Time)ptr)^ = res + return + } else if data_type == datetime.DateTime { + // NOTE: The UTC offset and leap second data are discarded. + res, _, _, consumed := time.rfc3339_to_components(str) + if consumed == 0 { + out_error^ = Parse_Error { + .Bad_Value, + GENERIC_RFC_3339_ERROR, + } + return + } + + (cast(^datetime.DateTime)ptr)^ = res + return + } + } + + when IMPORTING_NET { + if try_net_parse_workaround(data_type, str, ptr, out_error) { + return + } + } + + out_error ^= Parse_Error { + // The caller will add more details. + .Unsupported_Type, + "", + } +} + +@(optimization_mode="size") +set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) { + switch data_type { + case i8: (cast(^i8) ptr)^ = cast(i8) value + case i16: (cast(^i16) ptr)^ = cast(i16) value + case i32: (cast(^i32) ptr)^ = cast(i32) value + case i64: (cast(^i64) ptr)^ = cast(i64) value + case i128: (cast(^i128) ptr)^ = cast(i128) value + + case int: (cast(^int) ptr)^ = cast(int) value + + case i16le: (cast(^i16le) ptr)^ = cast(i16le) value + case i32le: (cast(^i32le) ptr)^ = cast(i32le) value + case i64le: (cast(^i64le) ptr)^ = cast(i64le) value + case i128le: (cast(^i128le) ptr)^ = cast(i128le) value + + case i16be: (cast(^i16be) ptr)^ = cast(i16be) value + case i32be: (cast(^i32be) ptr)^ = cast(i32be) value + case i64be: (cast(^i64be) ptr)^ = cast(i64be) value + case i128be: (cast(^i128be) ptr)^ = cast(i128be) value + + case u8: (cast(^u8) ptr)^ = cast(u8) value + case u16: (cast(^u16) ptr)^ = cast(u16) value + case u32: (cast(^u32) ptr)^ = cast(u32) value + case u64: (cast(^u64) ptr)^ = cast(u64) value + case u128: (cast(^u128) ptr)^ = cast(u128) value + + case uint: (cast(^uint) ptr)^ = cast(uint) value + case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) value + + case u16le: (cast(^u16le) ptr)^ = cast(u16le) value + case u32le: (cast(^u32le) ptr)^ = cast(u32le) value + case u64le: (cast(^u64le) ptr)^ = cast(u64le) value + case u128le: (cast(^u128le) ptr)^ = cast(u128le) value + + case u16be: (cast(^u16be) ptr)^ = cast(u16be) value + case u32be: (cast(^u32be) ptr)^ = cast(u32be) value + case u64be: (cast(^u64be) ptr)^ = cast(u64be) value + case u128be: (cast(^u128be) ptr)^ = cast(u128be) value + + case rune: (cast(^rune) ptr)^ = cast(rune) value + + case: + fmt.panicf("Unsupported integer backing type: %v", data_type) + } +} + +@(optimization_mode="size") +parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) { + #partial switch specific_type_info in type_info.variant { + case runtime.Type_Info_Named: + if global_custom_type_setter != nil { + // The program gets to go first. + error_message, handled, alloc_error := global_custom_type_setter(ptr, type_info.id, str, arg_tag) + + if alloc_error != nil { + // There was an allocation error. Bail out. + return Parse_Error { + alloc_error, + "Custom type setter encountered allocation error.", + } + } + + if handled { + // The program handled the type. + + if len(error_message) != 0 { + // However, there was an error. Pass it along. + error = Parse_Error { + .Bad_Value, + error_message, + } + } + + return + } + } + + // Might be a named enum. Need to check here first, since we handle all enums. + if enum_type_info, is_enum := specific_type_info.base.variant.(runtime.Type_Info_Enum); is_enum { + if value, ok := reflect.enum_from_name_any(type_info.id, str); ok { + set_unbounded_integer_by_type(ptr, value, enum_type_info.base.id) + } else { + return Parse_Error { + .Bad_Value, + fmt.tprintf("Invalid value name. Valid names are: %s", enum_type_info.names), + } + } + } else { + parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error) + + if error != nil { + // So far, it's none of the types that we recognize. + // Check to see if we can set it by base type, if allowed. + if _, is_indistinct := get_struct_subtag(arg_tag, SUBTAG_INDISTINCT); is_indistinct { + return parse_and_set_pointer_by_type(ptr, str, specific_type_info.base, arg_tag) + } + } + } + + case runtime.Type_Info_Dynamic_Array: + ptr := cast(^runtime.Raw_Dynamic_Array)ptr + + // Try to convert the value first. + elem_backing, alloc_error := mem.alloc_bytes(specific_type_info.elem.size, specific_type_info.elem.align) + if alloc_error != nil { + return Parse_Error { + alloc_error, + "Failed to allocate element backing for dynamic array.", + } + } + defer delete(elem_backing) + parse_and_set_pointer_by_type(raw_data(elem_backing), str, specific_type_info.elem, arg_tag) or_return + + if !runtime.__dynamic_array_resize(ptr, specific_type_info.elem.size, specific_type_info.elem.align, ptr.len + 1) { + // NOTE: This is purely an assumption that it's OOM. + // Regardless, the resize failed. + return Parse_Error { + runtime.Allocator_Error.Out_Of_Memory, + "Failed to resize dynamic array.", + } + } + + subptr := cast(rawptr)( + cast(uintptr)ptr.data + + cast(uintptr)((ptr.len - 1) * specific_type_info.elem.size)) + mem.copy(subptr, raw_data(elem_backing), len(elem_backing)) + + case runtime.Type_Info_Enum: + // This is a nameless enum. + // The code here is virtually the same as above for named enums. + if value, ok := reflect.enum_from_name_any(type_info.id, str); ok { + set_unbounded_integer_by_type(ptr, value, specific_type_info.base.id) + } else { + return Parse_Error { + .Bad_Value, + fmt.tprintf("Invalid value name. Valid names are: %s", specific_type_info.names), + } + } + + case: + if !parse_and_set_pointer_by_base_type(ptr, str, type_info) { + return Parse_Error { + // The caller will add more details. + .Bad_Value, + "", + } + } + } + + return +} + +get_struct_subtag :: get_subtag + +get_field_name :: proc(field: reflect.Struct_Field) -> string { + if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok { + if name_subtag, name_ok := get_struct_subtag(args_tag, SUBTAG_NAME); name_ok { + return name_subtag + } + } + + name, _ := strings.replace_all(field.name, "_", "-", context.temp_allocator) + return name +} + +get_field_pos :: proc(field: reflect.Struct_Field) -> (int, bool) { + if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok { + if pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS); pos_ok { + if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok { + return cast(int)value, true + } + } + } + + return 0, false +} + +// Get a struct field by its field name or `name` subtag. +get_field_by_name :: proc(model: ^$T, name: string) -> (result: reflect.Struct_Field, index: int, error: Error) { + for field, i in reflect.struct_fields_zipped(T) { + if get_field_name(field) == name { + return field, i, nil + } + } + + error = Parse_Error { + .Missing_Flag, + fmt.tprintf("Unable to find any flag named `%s`.", name), + } + return +} + +// Get a struct field by its `pos` subtag. +get_field_by_pos :: proc(model: ^$T, pos: int) -> (result: reflect.Struct_Field, index: int, ok: bool) { + for field, i in reflect.struct_fields_zipped(T) { + args_tag, tag_ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS) + if !tag_ok { + continue + } + + pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS) + if !pos_ok { + continue + } + + value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10) + if parse_ok && cast(int)value == pos { + return field, i, true + } + } + + return +} diff --git a/core/flags/internal_rtti_nonbsd.odin b/core/flags/internal_rtti_nonbsd.odin new file mode 100644 index 000000000..196c27ab8 --- /dev/null +++ b/core/flags/internal_rtti_nonbsd.odin @@ -0,0 +1,31 @@ +//+private +//+build !freebsd !netbsd !openbsd +package flags + +import "core:net" + +// This proc exists purely as a workaround for import restrictions. +// Returns true if caller should return early. +try_net_parse_workaround :: #force_inline proc ( + data_type: typeid, + str: string, + ptr: rawptr, + out_error: ^Error, +) -> bool { + if data_type == net.Host_Or_Endpoint { + addr, net_error := net.parse_hostname_or_endpoint(str) + if net_error != nil { + // We pass along `net.Error` here. + out_error^ = Parse_Error { + net_error, + "Invalid Host/Endpoint.", + } + return true + } + + (cast(^net.Host_Or_Endpoint)ptr)^ = addr + return true + } + + return false +} diff --git a/core/flags/internal_validation.odin b/core/flags/internal_validation.odin new file mode 100644 index 000000000..fd4bafeef --- /dev/null +++ b/core/flags/internal_validation.odin @@ -0,0 +1,244 @@ +//+private +package flags + +@require import "base:runtime" +@require import "core:container/bit_array" +@require import "core:fmt" +@require import "core:mem" +@require import "core:os" +@require import "core:reflect" +@require import "core:strconv" +@require import "core:strings" + +// This proc is used to assert that `T` meets the expectations of the library. +@(optimization_mode="size", disabled=ODIN_DISABLE_ASSERT) +validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) { + positionals_assigned_so_far: bit_array.Bit_Array + defer bit_array.destroy(&positionals_assigned_so_far) + + check_fields: for field in reflect.struct_fields_zipped(T) { + if style == .Unix { + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + fmt.panicf("%T.%s is a map type, and these are not supported in UNIX-style parsing mode.", + model_type, field.name, loc = loc) + } + } + + name_is_safe := true + defer { + fmt.assertf(name_is_safe, "%T.%s is using a reserved name.", + model_type, field.name, loc = loc) + } + + switch field.name { + case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT: + name_is_safe = false + } + + args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS) + if !ok { + // If it has no args tag, then we've checked all we need to. + // Most of this proc is validating that the subtags are sane. + continue + } + + if name, has_name := get_struct_subtag(args_tag, SUBTAG_NAME); has_name { + fmt.assertf(len(name) > 0, "%T.%s has a zero-length `%s`.", + model_type, field.name, SUBTAG_NAME, loc = loc) + + fmt.assertf(strings.index(name, " ") == -1, "%T.%s has a `%s` with spaces in it.", + model_type, field.name, SUBTAG_NAME, loc = loc) + + switch name { + case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT: + name_is_safe = false + continue check_fields + case: + name_is_safe = true + } + } + + if pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS); has_pos { + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.", + model_type, field.name, SUBTAG_POS, loc = loc) + } + + pos_value, pos_ok := strconv.parse_u64_of_base(pos_str, 10) + fmt.assertf(pos_ok, "%T.%s has `%s` defined as %q but cannot be parsed a base-10 integer >= 0.", + model_type, field.name, SUBTAG_POS, pos_str, loc = loc) + fmt.assertf(!bit_array.get(&positionals_assigned_so_far, pos_value), "%T.%s has `%s` set to #%i, but that position has already been assigned to another flag.", + model_type, field.name, SUBTAG_POS, pos_value, loc = loc) + bit_array.set(&positionals_assigned_so_far, pos_value) + } + + required_min, required_max: int + if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required { + fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.", + model_type, field.name, loc = loc) + + fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.", + model_type, field.name, loc = loc) + + if len(requirement) > 0 { + if required_min, required_max, ok = parse_requirements(requirement); ok { + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Dynamic_Array: + fmt.assertf(required_min != required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are the same. Increase the maximum by 1 for an exact number of arguments: (%i<%i)", + model_type, + field.name, + SUBTAG_REQUIRED, + requirement, + required_min, + 1 + required_max, + loc = loc) + + fmt.assertf(required_min < required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are swapped.", + model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc) + + case: + fmt.panicf("%T.%s has `%s` defined as %q, but ranges are only supported on dynamic arrays.", + model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc) + } + } else { + fmt.panicf("%T.%s has `%s` defined as %q, but it cannot be parsed as a valid range.", + model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc) + } + } + } + + if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic { + if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok { + fmt.assertf(value > 0, + "%T.%s has `%s` set to %i. It must be greater than zero.", + model_type, field.name, value, SUBTAG_VARIADIC, loc = loc) + fmt.assertf(value != 1, + "%T.%s has `%s` set to 1. This has no effect.", + model_type, field.name, SUBTAG_VARIADIC, loc = loc) + } + + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Dynamic_Array: + fmt.assertf(style != .Odin, + "%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.", + model_type, field.name, SUBTAG_VARIADIC, loc = loc) + case: + fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.", + model_type, field.name, SUBTAG_VARIADIC, loc = loc) + } + } + + allowed_to_define_file_perms: bool = --- + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + allowed_to_define_file_perms = specific_type_info.value.id == os.Handle + case runtime.Type_Info_Dynamic_Array: + allowed_to_define_file_perms = specific_type_info.elem.id == os.Handle + case: + allowed_to_define_file_perms = field.type.id == os.Handle + } + + if _, has_file := get_struct_subtag(args_tag, SUBTAG_FILE); has_file { + fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.", + model_type, field.name, SUBTAG_FILE, loc = loc) + } + + if _, has_perms := get_struct_subtag(args_tag, SUBTAG_PERMS); has_perms { + fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.", + model_type, field.name, SUBTAG_PERMS, loc = loc) + } + + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + fmt.assertf(reflect.is_string(specific_type_info.key), "%T.%s is defined as a map[%T]. Only string types are currently supported as map keys.", + model_type, + field.name, + specific_type_info.key) + } + } +} + +// Validate that all the required arguments are set and that the set arguments +// are up to the program's expectations. +@(optimization_mode="size") +validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error { + check_fields: for field, index in reflect.struct_fields_zipped(T) { + was_set := bit_array.get(&parser.fields_set, index) + + field_name := get_field_name(field) + args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS) + requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED) + + required_min, required_max: int + has_requirements: bool + if is_required { + required_min, required_max, has_requirements = parse_requirements(requirement) + } + + if has_requirements && required_min == 0 { + // Allow `0 ptr.len || ptr.len >= required_max { + if required_max == max(int) { + return Validation_Error { + fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i.", + field_name, + ptr.len, + "" if ptr.len == 1 else "s", + required_min), + } + } else { + return Validation_Error { + fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i and at most %i.", + field_name, + ptr.len, + "" if ptr.len == 1 else "s", + required_min, + required_max - 1), + } + } + } + } else if !was_set { + if is_required { + return Validation_Error { + fmt.tprintf("The required flag `%s` was not set.", field_name), + } + } + + // Not set, not required; moving on. + continue + } + + // All default checks have passed. The program gets a look at it now. + + if global_custom_flag_checker != nil { + ptr := cast(rawptr)(cast(uintptr)model + field.offset) + error := global_custom_flag_checker(model, + field.name, + mem.make_any(ptr, field.type.id), + args_tag) + + if len(error) > 0 { + // The program reported an error message. + return Validation_Error { error } + } + } + } + + return nil +} diff --git a/core/flags/parsing.odin b/core/flags/parsing.odin new file mode 100644 index 000000000..b6b63fdb6 --- /dev/null +++ b/core/flags/parsing.odin @@ -0,0 +1,101 @@ +package flags + +@require import "core:container/bit_array" +@require import "core:fmt" + +Parsing_Style :: enum { + // Odin-style: `-flag`, `-flag:option`, `-map:key=value` + Odin, + // UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument` + Unix, +} + +/* +Parse a slice of command-line arguments into an annotated struct. + +*Allocates Using Provided Allocator* + +By default, this proc will only allocate memory outside of its lifetime if it +has to append to a dynamic array, set a map value, or set a cstring. + +The program is expected to free any allocations on `model` as a result of parsing. + +Inputs: +- model: A pointer to an annotated struct with flag definitions. +- args: A slice of strings, usually `os.args[1:]`. +- style: The argument parsing style. +- validate_args: If `true`, will ensure that all required arguments are set if no errors occurred. +- strict: If `true`, will return on first error. Otherwise, parsing continues. +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +- error: A union of errors; parsing, file open, a help request, or validation. +*/ +@(optimization_mode="size") +parse :: proc( + model: ^$T, + args: []string, + style: Parsing_Style = .Odin, + validate_args: bool = true, + strict: bool = true, + allocator := context.allocator, + loc := #caller_location, +) -> (error: Error) { + context.allocator = allocator + validate_structure(model^, style, loc) + + parser: Parser + defer { + bit_array.destroy(&parser.filled_pos) + bit_array.destroy(&parser.fields_set) + } + + switch style { + case .Odin: + for arg in args { + error = parse_one_odin_arg(model, &parser, arg) + if strict && error != nil { + return + } + } + + case .Unix: + // Support for `-flag argument (repeating-argument ...)` + future_args: int + current_flag: string + + for i := 0; i < len(args); i += 1 { + #no_bounds_check arg := args[i] + future_args, current_flag, error = parse_one_unix_arg(model, &parser, arg) + if strict && error != nil { + return + } + + for starting_future_args := future_args; future_args > 0; future_args -= 1 { + i += 1 + if i == len(args) { + if future_args == starting_future_args { + return Parse_Error { + .No_Value, + fmt.tprintf("Expected a value for `%s` but none was given.", current_flag), + } + } + break + } + #no_bounds_check arg = args[i] + + error = set_option(model, &parser, current_flag, arg) + if strict && error != nil { + return + } + } + } + } + + if error == nil && validate_args { + return validate_arguments(model, &parser) + } + + return +} diff --git a/core/flags/rtti.odin b/core/flags/rtti.odin new file mode 100644 index 000000000..ce7a23773 --- /dev/null +++ b/core/flags/rtti.odin @@ -0,0 +1,43 @@ +package flags + +import "base:runtime" + +/* +Handle setting custom data types. + +Inputs: +- data: A raw pointer to the field where the data will go. +- data_type: Type information on the underlying field. +- unparsed_value: The unparsed string that the flag is being set to. +- args_tag: The `args` tag from the struct's field. + +Returns: +- error: An error message, or an empty string if no error occurred. +- handled: A boolean indicating if the setter handles this type. +- alloc_error: If an allocation error occurred, return it here. +*/ +Custom_Type_Setter :: #type proc( + data: rawptr, + data_type: typeid, + unparsed_value: string, + args_tag: string, +) -> ( + error: string, + handled: bool, + alloc_error: runtime.Allocator_Error, +) + +@(private) +global_custom_type_setter: Custom_Type_Setter + +/* +Set the global custom type setter. + +Note that only one can be active at a time. + +Inputs: +- setter: The type setter. Pass `nil` to disable any previously set setter. +*/ +register_type_setter :: proc(setter: Custom_Type_Setter) { + global_custom_type_setter = setter +} diff --git a/core/flags/usage.odin b/core/flags/usage.odin new file mode 100644 index 000000000..48137b6cd --- /dev/null +++ b/core/flags/usage.odin @@ -0,0 +1,293 @@ +package flags + +import "base:runtime" +import "core:fmt" +import "core:io" +import "core:reflect" +import "core:slice" +import "core:strconv" +import "core:strings" + +/* +Write out the documentation for the command-line arguments to a stream. + +Inputs: +- out: The stream to write to. +- data_type: The typeid of the data structure to describe. +- program: The name of the program, usually the first argument to `os.args`. +- style: The argument parsing style, required to show flags in the proper style. +*/ +@(optimization_mode="size") +write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) { + // All flags get their tags parsed so they can be reasoned about later. + Flag :: struct { + name: string, + usage: string, + type_description: string, + full_length: int, + pos: int, + required_min, required_max: int, + is_positional: bool, + is_required: bool, + is_boolean: bool, + is_variadic: bool, + variadic_length: int, + } + + // + // POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ... + // + sort_flags :: proc(i, j: Flag) -> slice.Ordering { + // `varg` goes to the end. + if i.name == INTERNAL_VARIADIC_FLAG { + return .Greater + } else if j.name == INTERNAL_VARIADIC_FLAG { + return .Less + } + + // Handle positionals. + if i.is_positional { + if j.is_positional { + return slice.cmp(i.pos, j.pos) + } else { + return .Less + } + } else { + if j.is_positional { + return .Greater + } + } + + // Then required flags. + if i.is_required { + if !j.is_required { + return .Less + } + } else if j.is_required { + return .Greater + } + + // Finally, sort by name. + return slice.cmp(i.name, j.name) + } + + describe_array_requirements :: proc(flag: Flag) -> (spec: string) { + if flag.is_required { + if flag.required_min == flag.required_max - 1 { + spec = fmt.tprintf(", exactly %i", flag.required_min) + } else if flag.required_min > 0 && flag.required_max == max(int) { + spec = fmt.tprintf(", at least %i", flag.required_min) + } else if flag.required_min == 0 && flag.required_max > 1 { + spec = fmt.tprintf(", at most %i", flag.required_max - 1) + } else if flag.required_min > 0 && flag.required_max > 1 { + spec = fmt.tprintf(", between %i and %i", flag.required_min, flag.required_max - 1) + } else { + spec = ", required" + } + } + return + } + + builder := strings.builder_make() + defer strings.builder_destroy(&builder) + + flag_prefix, flag_assignment: string = ---, --- + switch style { + case .Odin: flag_prefix = "-"; flag_assignment = ":" + case .Unix: flag_prefix = "--"; flag_assignment = " " + } + + visible_flags: [dynamic]Flag + defer delete(visible_flags) + + longest_flag_length: int + + for field in reflect.struct_fields_zipped(data_type) { + flag: Flag + + if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok { + if _, is_hidden := get_struct_subtag(args_tag, SUBTAG_HIDDEN); is_hidden { + // Hidden flags stay hidden. + continue + } + if pos_str, is_pos := get_struct_subtag(args_tag, SUBTAG_POS); is_pos { + flag.is_positional = true + if pos, parse_ok := strconv.parse_u64_of_base(pos_str, 10); parse_ok { + flag.pos = cast(int)pos + } + } + if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required { + flag.is_required = true + flag.required_min, flag.required_max, _ = parse_requirements(requirement) + } + if length_str, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic { + flag.is_variadic = true + if length, parse_ok := strconv.parse_u64_of_base(length_str, 10); parse_ok { + flag.variadic_length = cast(int)length + } + } + } + + flag.name = get_field_name(field) + flag.is_boolean = reflect.is_boolean(field.type) + + if usage, ok := reflect.struct_tag_lookup(field.tag, TAG_USAGE); ok { + flag.usage = usage + } else { + flag.usage = UNDOCUMENTED_FLAG + } + + #partial switch specific_type_info in field.type.variant { + case runtime.Type_Info_Map: + flag.type_description = fmt.tprintf("<%v>=<%v>%s", + specific_type_info.key.id, + specific_type_info.value.id, + ", required" if flag.is_required else "") + + case runtime.Type_Info_Dynamic_Array: + requirement_spec := describe_array_requirements(flag) + + if flag.is_variadic || flag.name == INTERNAL_VARIADIC_FLAG { + if flag.variadic_length == 0 { + flag.type_description = fmt.tprintf("<%v, ...>%s", + specific_type_info.elem.id, + requirement_spec) + } else { + flag.type_description = fmt.tprintf("<%v, %i at once>%s", + specific_type_info.elem.id, + flag.variadic_length, + requirement_spec) + } + } else { + flag.type_description = fmt.tprintf("<%v>%s", specific_type_info.elem.id, + requirement_spec if len(requirement_spec) > 0 else ", multiple") + } + + case: + if flag.is_boolean { + /* + if flag.is_required { + flag.type_description = ", required" + } + */ + } else { + flag.type_description = fmt.tprintf("<%v>%s", + field.type.id, + ", required" if flag.is_required else "") + } + } + + if flag.name == INTERNAL_VARIADIC_FLAG { + flag.full_length = len(flag.type_description) + } else if flag.is_boolean { + flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description) + } else { + flag.full_length = len(flag_prefix) + len(flag.name) + len(flag_assignment) + len(flag.type_description) + } + + longest_flag_length = max(longest_flag_length, flag.full_length) + + append(&visible_flags, flag) + } + + slice.sort_by_cmp(visible_flags[:], sort_flags) + + // All the flags have been figured out now. + + if len(program) > 0 { + keep_it_short := len(visible_flags) >= ONE_LINE_FLAG_CUTOFF_COUNT + + strings.write_string(&builder, "Usage:\n\t") + strings.write_string(&builder, program) + + for flag in visible_flags { + if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_VARIADIC_FLAG) { + continue + } + + strings.write_byte(&builder, ' ') + + if flag.name == INTERNAL_VARIADIC_FLAG { + strings.write_string(&builder, "...") + continue + } + + if !flag.is_required { strings.write_byte(&builder, '[') } + if !flag.is_positional { strings.write_string(&builder, flag_prefix) } + strings.write_string(&builder, flag.name) + if !flag.is_required { strings.write_byte(&builder, ']') } + } + + strings.write_byte(&builder, '\n') + } + + if len(visible_flags) == 0 { + // No visible flags. An unusual situation, but prevent any extra work. + fmt.wprint(out, strings.to_string(builder)) + return + } + + strings.write_string(&builder, "Flags:\n") + + // Divide the positional/required arguments and the non-required arguments. + divider_index := -1 + for flag, i in visible_flags { + if !flag.is_positional && !flag.is_required { + divider_index = i + break + } + } + if divider_index == 0 { + divider_index = -1 + } + + for flag, i in visible_flags { + if i == divider_index { + SPACING :: 2 // Number of spaces before the '|' from below. + strings.write_byte(&builder, '\t') + spacing := strings.repeat(" ", SPACING + longest_flag_length, context.temp_allocator) + strings.write_string(&builder, spacing) + strings.write_string(&builder, "|\n") + } + + strings.write_byte(&builder, '\t') + + if flag.name == INTERNAL_VARIADIC_FLAG { + strings.write_string(&builder, flag.type_description) + } else { + strings.write_string(&builder, flag_prefix) + strings.write_string(&builder, flag.name) + if !flag.is_boolean { + strings.write_string(&builder, flag_assignment) + } + strings.write_string(&builder, flag.type_description) + } + + if strings.contains_rune(flag.usage, '\n') { + // Multi-line usage documentation. Let's make it look nice. + usage_builder := strings.builder_make(context.temp_allocator) + + strings.write_byte(&usage_builder, '\n') + iter := strings.trim_space(flag.usage) + for line in strings.split_lines_iterator(&iter) { + strings.write_string(&usage_builder, "\t\t") + strings.write_string(&usage_builder, strings.trim_left_space(line)) + strings.write_byte(&usage_builder, '\n') + } + + strings.write_string(&builder, strings.to_string(usage_builder)) + } else { + // Single-line usage documentation. + spacing := strings.repeat(" ", + (longest_flag_length) - flag.full_length, + context.temp_allocator) + + strings.write_string(&builder, spacing) + strings.write_string(&builder, " | ") + strings.write_string(&builder, flag.usage) + strings.write_byte(&builder, '\n') + } + } + + fmt.wprint(out, strings.to_string(builder)) +} diff --git a/core/flags/util.odin b/core/flags/util.odin new file mode 100644 index 000000000..e4f32eea1 --- /dev/null +++ b/core/flags/util.odin @@ -0,0 +1,130 @@ +package flags + +import "core:fmt" +@require import "core:os" +@require import "core:path/filepath" +import "core:strings" + +/* +Parse any arguments into an annotated struct or exit if there was an error. + +*Allocates Using Provided Allocator* + +This is a convenience wrapper over `parse` and `print_errors`. + +Inputs: +- model: A pointer to an annotated struct. +- program_args: A slice of strings, usually `os.args`. +- style: The argument parsing style. +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) +*/ +@(optimization_mode="size") +parse_or_exit :: proc( + model: ^$T, + program_args: []string, + style: Parsing_Style = .Odin, + allocator := context.allocator, + loc := #caller_location, +) { + assert(len(program_args) > 0, "Program arguments slice is empty.", loc) + + program := filepath.base(program_args[0]) + args: []string + + if len(program_args) > 1 { + args = program_args[1:] + } + + error := parse(model, args, style) + if error != nil { + stderr := os.stream_from_handle(os.stderr) + + if len(args) == 0 { + // No arguments entered, and there was an error; show the usage, + // specifically on STDERR. + write_usage(stderr, T, program, style) + fmt.wprintln(stderr) + } + + print_errors(T, error, program, style) + + _, was_help_request := error.(Help_Request) + os.exit(0 if was_help_request else 1) + } +} +/* +Print out any errors that may have resulted from parsing. + +All error messages print to STDERR, while usage goes to STDOUT, if requested. + +Inputs: +- data_type: The typeid of the data structure to describe, if usage is requested. +- error: The error returned from `parse`. +- style: The argument parsing style, required to show flags in the proper style, when usage is shown. +*/ +@(optimization_mode="size") +print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) { + stderr := os.stream_from_handle(os.stderr) + stdout := os.stream_from_handle(os.stdout) + + switch specific_error in error { + case Parse_Error: + fmt.wprintfln(stderr, "[%T.%v] %s", specific_error, specific_error.reason, specific_error.message) + case Open_File_Error: + fmt.wprintfln(stderr, "[%T#%i] Unable to open file with perms 0o%o in mode 0x%x: %s", + specific_error, + specific_error.errno, + specific_error.perms, + specific_error.mode, + specific_error.filename) + case Validation_Error: + fmt.wprintfln(stderr, "[%T] %s", specific_error, specific_error.message) + case Help_Request: + write_usage(stdout, data_type, program, style) + } +} +/* +Get the value for a subtag. + +This is useful if you need to parse through the `args` tag for a struct field +on a custom type setter or custom flag checker. + +Example: + + import "core:flags" + import "core:fmt" + + subtag_example :: proc() { + args_tag := "precision=3,signed" + + precision, has_precision := flags.get_subtag(args_tag, "precision") + signed, is_signed := flags.get_subtag(args_tag, "signed") + + fmt.printfln("precision = %q, %t", precision, has_precision) + fmt.printfln("signed = %q, %t", signed, is_signed) + } + +Output: + + precision = "3", true + signed = "", true + +*/ +get_subtag :: proc(tag, id: string) -> (value: string, ok: bool) { + // This proc was initially private in `internal_rtti.odin`, but given how + // useful it would be to custom type setters and flag checkers, it lives + // here now. + + tag := tag + + for subtag in strings.split_iterator(&tag, ",") { + if equals := strings.index_byte(subtag, '='); equals != -1 && id == subtag[:equals] { + return subtag[1 + equals:], true + } else if id == subtag { + return "", true + } + } + + return +} diff --git a/core/flags/validation.odin b/core/flags/validation.odin new file mode 100644 index 000000000..e370cff48 --- /dev/null +++ b/core/flags/validation.odin @@ -0,0 +1,37 @@ +package flags + +/* +Check a flag after parsing, during the validation stage. + +Inputs: +- model: A raw pointer to the data structure provided to `parse`. +- name: The name of the flag being checked. +- value: An `any` type that contains the value to be checked. +- args_tag: The `args` tag from within the struct. + +Returns: +- error: An error message, or an empty string if no error occurred. +*/ +Custom_Flag_Checker :: #type proc( + model: rawptr, + name: string, + value: any, + args_tag: string, +) -> ( + error: string, +) + +@(private) +global_custom_flag_checker: Custom_Flag_Checker + +/* +Set the global custom flag checker. + +Note that only one can be active at a time. + +Inputs: +- checker: The flag checker. Pass `nil` to disable any previously set checker. +*/ +register_flag_checker :: proc(checker: Custom_Flag_Checker) { + global_custom_flag_checker = checker +} diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 4c65dd01f..b1c95866f 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1973,11 +1973,13 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St // fi.hash = false; fi.indent += 1 - if !is_soa && hash { + is_empty := len(info.names) == 0 + + if !is_soa && hash && !is_empty { io.write_byte(fi.writer, '\n', &fi.n) } defer { - if hash { + if !is_soa && hash && !is_empty { for _ in 0.. 0 { + for _ in 0.. S where intrinsics.type_is_comparable(T) #no_bound } i := 1 for j in 1.. bool) -> S #no_bounds_check { } i := 1 for j in 1.. (res: string) { return string(b.buf[:]) } /* +Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring + +Inputs: +- b: A pointer to builder + +Returns: +- res: A cstring of the Builder's buffer +*/ +to_cstring :: proc(b: ^Builder) -> (res: cstring) { + append(&b.buf, 0) + pop(&b.buf) + return cstring(raw_data(b.buf)) +} +/* Returns the length of the Builder's buffer, in bytes Inputs: diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin index 08f7cd6c9..d12409f32 100644 --- a/core/sync/futex_netbsd.odin +++ b/core/sync/futex_netbsd.odin @@ -30,8 +30,8 @@ get_last_error :: proc "contextless" () -> int { } _futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool { - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0, 0, 0) == -1 { - switch get_last_error() { + if error, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), 0, 0, 0); !ok { + switch error { case EINTR, EAGAIN: return true case: @@ -45,11 +45,11 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du if duration <= 0 { return false } - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{ + if error, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAIT_PRIVATE, uintptr(expected), cast(uintptr) &Time_Spec{ time_sec = cast(uint)(duration / 1e9), time_nsec = cast(uint)(duration % 1e9), - }, 0, 0) == -1 { - switch get_last_error() { + }, 0, 0); !ok { + switch error { case EINTR, EAGAIN: return true case ETIMEDOUT: @@ -62,13 +62,13 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du } _futex_signal :: proc "contextless" (futex: ^Futex) { - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0) == -1 { + if _, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0); !ok { _panic("futex_wake_single failure") } } _futex_broadcast :: proc "contextless" (futex: ^Futex) { - if cast(int) intrinsics.syscall(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0) == -1 { + if _, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0); !ok { _panic("_futex_wake_all failure") } } diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin index 7159a7c3a..e6103a58a 100644 --- a/core/sys/darwin/Foundation/NSWindow.odin +++ b/core/sys/darwin/Foundation/NSWindow.odin @@ -712,3 +712,7 @@ Window_setDelegate :: proc "c" (self: ^Window, delegate: ^WindowDelegate) { Window_backingScaleFactor :: proc "c" (self: ^Window) -> Float { return msgSend(Float, self, "backingScaleFactor") } +@(objc_type=Window, objc_name="setWantsLayer") +Window_setWantsLayer :: proc "c" (self: ^Window, ok: BOOL) { + msgSend(nil, self, "setWantsLayer:", ok) +} diff --git a/core/sys/info/platform_freebsd.odin b/core/sys/info/platform_freebsd.odin index c1429c4b2..b26fb7875 100644 --- a/core/sys/info/platform_freebsd.odin +++ b/core/sys/info/platform_freebsd.odin @@ -12,7 +12,7 @@ version_string_buf: [1024]u8 init_os_version :: proc () { os_version.platform = .FreeBSD - kernel_version_buf: [129]u8 + kernel_version_buf: [1024]u8 b := strings.builder_from_bytes(version_string_buf[:]) // Retrieve kernel info using `sysctl`, e.g. FreeBSD 13.1-RELEASE-p2 GENERIC diff --git a/core/sys/unix/sysctl_freebsd.odin b/core/sys/unix/sysctl_freebsd.odin index d1acbc2a1..35c5db02c 100644 --- a/core/sys/unix/sysctl_freebsd.odin +++ b/core/sys/unix/sysctl_freebsd.odin @@ -5,14 +5,15 @@ import "base:intrinsics" sysctl :: proc(mib: []i32, val: ^$T) -> (ok: bool) { mib := mib - result_size := i64(size_of(T)) + result_size := u64(size_of(T)) - res := intrinsics.syscall(SYS_sysctl, + res: uintptr + res, ok = intrinsics.syscall_bsd(SYS_sysctl, uintptr(raw_data(mib)), uintptr(len(mib)), uintptr(val), uintptr(&result_size), uintptr(0), uintptr(0), ) - return res == 0 + return } // See /usr/include/sys/sysctl.h for details diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 0d9ad826a..5e45c6076 100755 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -38,16 +38,30 @@ foreign kernel32 { lpNumberOfCharsWritten: LPDWORD, lpReserved: LPVOID) -> BOOL --- + PeekConsoleInputW :: proc(hConsoleInput: HANDLE, + lpBuffer: ^INPUT_RECORD, + nLength: DWORD, + lpNumberOfEventsRead: LPDWORD) -> BOOL --- + + ReadConsoleInputW :: proc(hConsoleInput: HANDLE, + lpBuffer: ^INPUT_RECORD, + nLength: DWORD, + lpNumberOfEventsRead: LPDWORD) -> BOOL --- + GetConsoleMode :: proc(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL --- SetConsoleMode :: proc(hConsoleHandle: HANDLE, dwMode: DWORD) -> BOOL --- SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE, - dwCursorPosition: COORD) -> BOOL --- + dwCursorPosition: COORD) -> BOOL --- SetConsoleTextAttribute :: proc(hConsoleOutput: HANDLE, - wAttributes: WORD) -> BOOL --- + wAttributes: WORD) -> BOOL --- + GetConsoleCP :: proc() -> UINT --- + SetConsoleCP :: proc(wCodePageID: UINT) -> BOOL --- + GetConsoleOutputCP :: proc() -> UINT --- SetConsoleOutputCP :: proc(wCodePageID: UINT) -> BOOL --- - + FlushConsoleInputBuffer :: proc(hConsoleInput: HANDLE) -> BOOL --- + GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL --- SetHandleInformation :: proc(hObject: HANDLE, dwMask: DWORD, @@ -85,6 +99,12 @@ foreign kernel32 { RemoveDirectoryW :: proc(lpPathName: LPCWSTR) -> BOOL --- SetFileAttributesW :: proc(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL --- SetLastError :: proc(dwErrCode: DWORD) --- + GetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL --- + SetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL --- + ClearCommError :: proc(hFile: HANDLE, lpErrors: ^Com_Error, lpStat: ^COMSTAT) -> BOOL --- + GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- + SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- + GetCommPorts :: proc(lpPortNumbers: PULONG, uPortNumbersCount: ULONG, puPortNumbersFound: PULONG) -> ULONG --- GetCommandLineW :: proc() -> LPCWSTR --- GetTempPathW :: proc(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD --- GetCurrentProcess :: proc() -> HANDLE --- @@ -413,6 +433,7 @@ foreign kernel32 { LoadLibraryW :: proc(c_str: LPCWSTR) -> HMODULE --- FreeLibrary :: proc(h: HMODULE) -> BOOL --- GetProcAddress :: proc(h: HMODULE, c_str: LPCSTR) -> rawptr --- + LoadLibraryExW :: proc(c_str: LPCWSTR, file: HANDLE, flags: LoadLibraryEx_Flags) -> HMODULE --- GetFullPathNameW :: proc(filename: LPCWSTR, buffer_length: DWORD, buffer: LPCWSTR, file_part: ^LPCWSTR) -> DWORD --- @@ -1015,82 +1036,6 @@ PHANDLER_ROUTINE :: HandlerRoutine // NOTE(Jeroen, 2024-06-13): As Odin now supports bit_fields, we no longer need // a helper procedure. `init_dcb_with_config` and `get_dcb_config` have been removed. -DTR_Control :: enum byte { - Disable = 0, - Enable = 1, - Handshake = 2, -} -RTS_Control :: enum byte { - Disable = 0, - Enable = 1, - Handshake = 2, - Toggle = 3, -} -Parity :: enum byte { - None = 0, - Odd = 1, - Even = 2, - Mark = 3, - Space = 4, -} -Stop_Bits :: enum byte { - One = 0, - One_And_A_Half = 1, - Two = 2, -} - -DCB :: struct { - DCBlength: DWORD, - BaudRate: DWORD, - using _: bit_field DWORD { - fBinary: bool | 1, - fParity: bool | 1, - fOutxCtsFlow: bool | 1, - fOutxDsrFlow: bool | 1, - fDtrControl: DTR_Control | 2, - fDsrSensitivity: bool | 1, - fTXContinueOnXoff: bool | 1, - fOutX: bool | 1, - fInX: bool | 1, - fErrorChar: bool | 1, - fNull: bool | 1, - fRtsControl: RTS_Control | 2, - fAbortOnError: bool | 1, - }, - wReserved: WORD, - XOnLim: WORD, - XOffLim: WORD, - ByteSize: BYTE, - Parity: Parity, - StopBits: Stop_Bits, - XonChar: byte, - XoffChar: byte, - ErrorChar: byte, - EofChar: byte, - EvtChar: byte, - wReserved1: WORD, -} - -@(default_calling_convention="system") -foreign kernel32 { - GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- - SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- -} - -COMMTIMEOUTS :: struct { - ReadIntervalTimeout: DWORD, - ReadTotalTimeoutMultiplier: DWORD, - ReadTotalTimeoutConstant: DWORD, - WriteTotalTimeoutMultiplier: DWORD, - WriteTotalTimeoutConstant: DWORD, -} - -@(default_calling_convention="system") -foreign kernel32 { - GetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL --- - SetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL --- -} - LPFIBER_START_ROUTINE :: #type proc "system" (lpFiberParameter: LPVOID) @(default_calling_convention = "system") @@ -1167,9 +1112,9 @@ Battery_Flag :: enum BYTE { Low = 1, Critical = 2, Charging = 3, - No_Battery = 7, + No_Battery = 7, } -Battery_Flags :: bit_set[Battery_Flag; BYTE] +Battery_Flags :: bit_set[Battery_Flag; BYTE] /* Global Memory Flags */ GMEM_FIXED :: 0x0000 diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 7ed322169..e568a7bc7 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -2674,6 +2674,22 @@ OSVERSIONINFOEXW :: struct { wReserved: UCHAR, } +LoadLibraryEx_Flag :: enum DWORD { + LOAD_LIBRARY_AS_DATAFILE = 1, // 1 << 1: 0x0002, + LOAD_WITH_ALTERED_SEARCH_PATH = 3, // 1 << 3: 0x0008, + LOAD_IGNORE_CODE_AUTHZ_LEVEL = 4, // 1 << 4: 0x0010, + LOAD_LIBRARY_AS_IMAGE_RESOURCE = 5, // 1 << 5: 0x0020, + LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 6, // 1 << 6: 0x0040, + LOAD_LIBRARY_REQUIRE_SIGNED_TARGET = 7, // 1 << 7: 0x0080, + LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 8, // 1 << 8: 0x0100, + LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 9, // 1 << 9: 0x0200, + LOAD_LIBRARY_SEARCH_USER_DIRS = 10, // 1 << 10: 0x0400, + LOAD_LIBRARY_SEARCH_SYSTEM32 = 11, // 1 << 11: 0x0800, + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 12, // 1 << 12: 0x1000, + LOAD_LIBRARY_SAFE_CURRENT_DIRS = 13, // 1 << 13: 0x2000, +} +LoadLibraryEx_Flags :: distinct bit_set[LoadLibraryEx_Flag] + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits // Used in LogonUserExW PQUOTA_LIMITS :: struct { @@ -3983,6 +3999,70 @@ CONSOLE_CURSOR_INFO :: struct { PCONSOLE_SCREEN_BUFFER_INFO :: ^CONSOLE_SCREEN_BUFFER_INFO PCONSOLE_CURSOR_INFO :: ^CONSOLE_CURSOR_INFO +Event_Type :: enum WORD { + KEY_EVENT = 0x0001, + MOUSE_EVENT = 0x0002, + WINDOW_BUFFER_SIZE_EVENT = 0x0004, + MENU_EVENT = 0x0008, + FOCUS_EVENT = 0x0010, +} + +INPUT_RECORD :: struct { + EventType: Event_Type, + Event: struct #raw_union { + KeyEvent: KEY_EVENT_RECORD, + MouseEvent: MOUSE_EVENT_RECORD, + WindowBufferSizeEvent: WINDOW_BUFFER_SIZE_RECORD, + MenuEvent: MENU_EVENT_RECORD, + FocusEvent: FOCUS_EVENT_RECORD, + }, +} + +Control_Key_State_Bits :: enum { + RIGHT_ALT_PRESSED, + LEFT_ALT_PRESSED, + RIGHT_CTRL_PRESSED, + LEFT_CTRL_PRESSED, + SHIFT_PRESSED, + NUMLOCK_ON, + SCROLLLOCK_ON, + CAPSLOCK_ON, + ENHANCED_KEY, +} +Control_Key_State :: bit_set[Control_Key_State_Bits; DWORD] + +KEY_EVENT_RECORD :: struct { + bKeyDown: BOOL, + wRepeatCount: WORD, + wVirtualKeyCode: WORD, + wVirtualScanCode: WORD, + uChar: struct #raw_union { + UnicodeChar: WCHAR, + AsciiChar: CHAR, + }, + dwControlKeyState: Control_Key_State, +} + +MOUSE_EVENT_RECORD :: struct { + dwMousePosition: COORD, + dwButtonState: DWORD, + dwControlKeyState: DWORD, + dwEventFlags: DWORD, +} + +WINDOW_BUFFER_SIZE_RECORD :: struct { + dwSize: COORD, +} + +MENU_EVENT_RECORD :: struct { + dwCommandId: UINT, +} + +FOCUS_EVENT_RECORD :: struct { + bSetFocus: BOOL, +} + + // // Networking // @@ -4217,3 +4297,92 @@ SOCKADDR :: struct { sa_family: ADDRESS_FAMILY, sa_data: [14]CHAR, } + +DTR_Control :: enum byte { + Disable = 0, + Enable = 1, + Handshake = 2, +} +RTS_Control :: enum byte { + Disable = 0, + Enable = 1, + Handshake = 2, + Toggle = 3, +} +Parity :: enum byte { + None = 0, + Odd = 1, + Even = 2, + Mark = 3, + Space = 4, +} +Stop_Bits :: enum byte { + One = 0, + One_And_A_Half = 1, + Two = 2, +} + +DCB :: struct { + DCBlength: DWORD, + BaudRate: DWORD, + using _: bit_field DWORD { + fBinary: bool | 1, + fParity: bool | 1, + fOutxCtsFlow: bool | 1, + fOutxDsrFlow: bool | 1, + fDtrControl: DTR_Control | 2, + fDsrSensitivity: bool | 1, + fTXContinueOnXoff: bool | 1, + fOutX: bool | 1, + fInX: bool | 1, + fErrorChar: bool | 1, + fNull: bool | 1, + fRtsControl: RTS_Control | 2, + fAbortOnError: bool | 1, + }, + wReserved: WORD, + XOnLim: WORD, + XOffLim: WORD, + ByteSize: BYTE, + Parity: Parity, + StopBits: Stop_Bits, + XonChar: byte, + XoffChar: byte, + ErrorChar: byte, + EofChar: byte, + EvtChar: byte, + wReserved1: WORD, +} + +COMMTIMEOUTS :: struct { + ReadIntervalTimeout: DWORD, + ReadTotalTimeoutMultiplier: DWORD, + ReadTotalTimeoutConstant: DWORD, + WriteTotalTimeoutMultiplier: DWORD, + WriteTotalTimeoutConstant: DWORD, +} + +Com_Stat_Bits :: enum { + fCtsHold, + fDsrHold, + fRlsdHold, + fXoffHold, + fXoffSent, + fEof, + fTxim, +} +COMSTAT :: struct { + bits: bit_set[Com_Stat_Bits; DWORD], + cbInQue: DWORD, + cbOutQue: DWORD, +} + +Com_Error_Bits :: enum { + RXOVER, + OVERRUN, + RXPARITY, + FRAME, + BREAK, +} +Com_Error :: bit_set[Com_Error_Bits; DWORD] + diff --git a/core/testing/runner.odin b/core/testing/runner.odin index a0f9eee31..3510856c7 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -9,6 +9,7 @@ import "core:encoding/ansi" import "core:fmt" import "core:io" @require import pkg_log "core:log" +import "core:math/rand" import "core:mem" import "core:os" import "core:slice" @@ -106,6 +107,13 @@ run_test_task :: proc(task: thread.Task) { options = Default_Test_Logger_Opts, } + random_generator_state: runtime.Default_Random_State + context.random_generator = { + procedure = runtime.default_random_generator_proc, + data = &random_generator_state, + } + rand.reset(data.t.seed) + free_all(context.temp_allocator) data.it.p(&data.t) diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin index 54bf8b80f..d590fd123 100644 --- a/core/text/i18n/doc.odin +++ b/core/text/i18n/doc.odin @@ -1,31 +1,44 @@ /* -The `i18n` package is flexible and easy to use. +The `i18n` package is a flexible and easy to use way to localise applications. -It has one call to get a translation: `get`, which the user can alias into something like `T`. +It has two calls to get a translation: `get()` and `get_n()`, which the user can alias into something like `T` and `Tn` +with statements like: + T :: i18n.get + Tn :: i18n.get_n. -`get`, referred to as `T` here, has a few different signatures. -All of them will return the key if the entry can't be found in the active translation catalog. +`get()` is used for retrieving the translation of sentences which **never** change in form, +like for instance "Connection established" or "All temporary files have been deleted". +Note that the number (singular, dual, plural, whatever else) is not relevant: the sentence is fixed and it will have only one possible translation in any other language. -- `T(key)` returns the translation of `key`. -- `T(key, n)` returns a pluralized translation of `key` according to value `n`. +`get_n()` is used for retrieving the translations of sentences which change according to the number of items referenced. +The various signatures of `get_n()` have one more parameter, `n`, which will receive that number and be used +to select the correct form according to the pluralizer attached to the message catalogue when initially loaded; +for instance, to summarise a rather complex matter, some languages use the singular form when referring to 0 items and some use the (only in their case) plural forms; +also, languages may have more or less quantifier forms than a single singular form and a universal plural form: +for instance, Chinese has just one form for any quantity, while Welsh may have up to 6 different forms for specific different quantities. -- `T(section, key)` returns the translation of `key` in `section`. -- `T(section, key, n)` returns a pluralized translation of `key` in `section` according to value `n`. +Both `get()` and `get_n()`, referred to as `T` and `Tn` here, have several different signatures. +All of them will return the key if the entry can't be found in the active translation catalogue. +By default lookup take place in the global `i18n.ACTIVE` catalogue for ease of use, unless a specific catalogue is supplied. -By default lookup take place in the global `i18n.ACTIVE` catalog for ease of use. -If you want to override which translation to use, for example in a language preview dialog, you can use the following: +- `T(key)` returns the translation of `key`. +- `T(key, catalog)` returns the translation of `key` from explictly supplied catalogue. +- `T(section, key)` returns the translation of `key` in `section`. +- `T(section, key, catalog)` returns the translation of `key` in `section` from explictly supplied catalogue. -- `T(key, n, catalog)` returns the pluralized version of `key` from explictly supplied catalog. -- `T(section, key, n, catalog)` returns the pluralized version of `key` in `section` from explictly supplied catalog. +- `Tn(key, n)` returns the translation of `key` according to number of items `n`. +- `Tn(key, n, catalog)` returns the translation of `key` from explictly supplied catalogue. +- `Tn(section, key, n)` returns the translation of `key` in `section` according to number of items `n`. +- `Tn(section, key, n, catalog)` returns the translation of `key` in `section` according to number of items `n` from explictly supplied catalogue. If a catalog has translation contexts or sections, then omitting it in the above calls looks up in section "". -The default pluralization rule is n != 1, which is to say that passing n == 1 (or not passing n) returns the singular form. -Passing n != 1 returns plural form 1. +The default pluralization rule is `n != 1`, which is to say that passing `n == 1` returns the singular form (in slot 0). +Passing `n != 1` returns the plural form in slot 1 (if any). Should a language not conform to this rule, you can pass a pluralizer procedure to the catalog parser. -This is a procedure that maps an integer to an integer, taking a value and returning which plural slot should be used. +This is a procedure that maps an integer to an integer, taking a quantity and returning which plural slot should be used. You can also assign it to a loaded catalog after parsing, of course. @@ -34,24 +47,21 @@ Example: import "core:fmt" import "core:text/i18n" - T :: i18n.get + T :: i18n.get + Tn :: i18n.get_n mo :: proc() { using fmt err: i18n.Error - /* - Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter. - */ + // Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter. i18n.ACTIVE, err = i18n.parse_mo(#load("translations/nl_NL.mo")) defer i18n.destroy() if err != .None { return } - /* - These are in the .MO catalog. - */ + // These are in the .MO catalog. println("-----") println(T("")) println("-----") @@ -60,13 +70,11 @@ Example: println(T("Hellope, World!")) println("-----") // We pass 1 into `T` to get the singular format string, then 1 again into printf. - printf(T("There is %d leaf.\n", 1), 1) + printf(Tn("There is %d leaf.\n", 1), 1) // We pass 42 into `T` to get the plural format string, then 42 again into printf. - printf(T("There is %d leaf.\n", 42), 42) + printf(Tn("There is %d leaf.\n", 42), 42) - /* - This isn't in the translation catalog, so the key is passed back untranslated. - */ + // This isn't in the translation catalog, so the key is passed back untranslated. println("-----") println(T("Come visit us on Discord!")) } @@ -76,19 +84,13 @@ Example: err: i18n.Error - /* - Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter. - */ + // Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter. i18n.ACTIVE, err = i18n.parse_qt(#load("translations/nl_NL-qt-ts.ts")) defer i18n.destroy() - if err != .None { - return - } + if err != .None { return } - /* - These are in the .TS catalog. As you can see they have sections. - */ + // These are in the .TS catalog. As you can see they have sections. println("--- Page section ---") println("Page:Text for translation =", T("Page", "Text for translation")) println("-----") @@ -99,8 +101,8 @@ Example: println("-----") println("--- apple_count section ---") println("apple_count:%d apple(s) =") - println("\t 1 =", T("apple_count", "%d apple(s)", 1)) - println("\t 42 =", T("apple_count", "%d apple(s)", 42)) + println("\t 1 =", Tn("apple_count", "%d apple(s)", 1)) + println("\t 42 =", Tn("apple_count", "%d apple(s)", 42)) } */ package i18n diff --git a/core/text/i18n/i18n.odin b/core/text/i18n/i18n.odin index 64593c4e8..0190ef0f7 100644 --- a/core/text/i18n/i18n.odin +++ b/core/text/i18n/i18n.odin @@ -10,23 +10,13 @@ package i18n */ import "core:strings" -/* - TODO: - - Support for more translation catalog file formats. -*/ - -/* - Currently active catalog. -*/ +// Currently active catalog. ACTIVE: ^Translation // Allow between 1 and 255 plural forms. Default: 10. MAX_PLURALS :: min(max(#config(ODIN_i18N_MAX_PLURAL_FORMS, 10), 1), 255) -/* - The main data structure. This can be generated from various different file formats, as long as we have a parser for them. -*/ - +// The main data structure. This can be generated from various different file formats, as long as we have a parser for them. Section :: map[string][]string Translation :: struct { @@ -37,34 +27,24 @@ Translation :: struct { } Error :: enum { - /* - General return values. - */ + // General return values. None = 0, Empty_Translation_Catalog, Duplicate_Key, - /* - Couldn't find, open or read file. - */ + // Couldn't find, open or read file. File_Error, - /* - File too short. - */ + // File too short. Premature_EOF, - /* - GNU Gettext *.MO file errors. - */ + // GNU Gettext *.MO file errors. MO_File_Invalid_Signature, MO_File_Unsupported_Version, MO_File_Invalid, MO_File_Incorrect_Plural_Count, - /* - Qt Linguist *.TS file errors. - */ + // Qt Linguist *.TS file errors. TS_File_Parse_Error, TS_File_Expected_Context, TS_File_Expected_Context_Name, @@ -85,73 +65,142 @@ DEFAULT_PARSE_OPTIONS :: Parse_Options{ } /* - Several ways to use: - - get(key), which defaults to the singular form and i18n.ACTIVE catalog, or - - get(key, number), which returns the appropriate plural from the active catalog, or - - get(key, number, catalog) to grab text from a specific one. -*/ -get_single_section :: proc(key: string, number := 1, catalog: ^Translation = ACTIVE) -> (value: string) { - /* - A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule. - */ - plural := 1 if number != 1 else 0 + Returns the first translation string for the passed `key`. + It is also aliased with `get()`. - if catalog.pluralize != nil { - plural = catalog.pluralize(number) - } - return get_by_slot(key, plural, catalog) + Two ways to use it: + - get(key), which defaults to the `i18n.ACTIVE` catalogue, or + - get(key, catalog) to grab text from a specific loaded catalogue + + Inputs: + - key: the string to translate + - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE) + + Returns: the translated string, or the original `key` if no translation was found. +*/ +get_single_section :: proc(key: string, catalog: ^Translation = ACTIVE) -> (value: string) { + return get_by_slot(key, 0, catalog) } /* - Several ways to use: - - get(section, key), which defaults to the singular form and i18n.ACTIVE catalog, or - - get(section, key, number), which returns the appropriate plural from the active catalog, or - - get(section, key, number, catalog) to grab text from a specific one. -*/ -get_by_section :: proc(section, key: string, number := 1, catalog: ^Translation = ACTIVE) -> (value: string) { - /* - A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule. - */ - plural := 1 if number != 1 else 0 + Returns the first translation string for the passed `key` in a specific section or context. + It is also aliases with `get()`. - if catalog.pluralize != nil { - plural = catalog.pluralize(number) - } - return get_by_slot(section, key, plural, catalog) + Two ways to use it: + - get(section, key), which defaults to the `i18n.ACTIVE` catalogue, or + - get(section, key, catalog) to grab text from a specific loaded catalogue + + Inputs: + - section: the catalogue section (sometimes also called 'context') in which to look up the translation + - key: the string to translate + - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE) + + Returns: the translated string, or the original `key` if no translation was found. +*/ +get_by_section :: proc(section, key: string, catalog: ^Translation = ACTIVE) -> (value: string) { + return get_by_slot(section, key, 0, catalog) } + get :: proc{get_single_section, get_by_section} /* - Several ways to use: - - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or - - get_by_slot(key, slot), which returns the requested plural from the active catalog, or - - get_by_slot(key, slot, catalog) to grab text from a specific one. + Returns the translation string for the passed `key` in a specific plural form (if present in the catalogue). + It is also aliased with `get_n()`. + + Two ways to use it: + - get_n(key, quantity), which returns the appropriate plural from the active catalogue, or + - get_n(key, quantity, catalog) to grab text from a specific loaded catalogue + + Inputs: + - key: the string to translate + - quantity: the quantity of item to be used to select the correct plural form + - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE) + + Returns: the translated string, or the original `key` if no translation was found. +*/ +get_single_section_with_quantity :: proc(key: string, quantity: int, catalog: ^Translation = ACTIVE) -> (value: string) { + /* + A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule. + */ + slot := 1 if quantity != 1 else 0 + + if catalog.pluralize != nil { + slot = catalog.pluralize(quantity) + } + return get_by_slot(key, slot, catalog) +} + +/* + Returns the translation string for the passed `key` in a specific plural form (if present in the catalogue) + in a specific section or context. + It is also aliases with `get_n()`. + + Two ways to use it: + - get(section, key, quantity), which returns the appropriate plural from the active catalogue, or + - get(section, key, quantity, catalog) to grab text from a specific loaded catalogue + + Inputs: + - section: the catalogue section (sometime also called 'context') from which to lookup the translation + - key: the string to translate + - qantity: the quantity of item to be used to select the correct plural form + - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE) + + Returns: the translated string, or the original `key` if no translation was found +*/ +get_by_section_with_quantity :: proc(section, key: string, quantity: int, catalog: ^Translation = ACTIVE) -> (value: string) { + /* + A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule. + */ + slot := 1 if quantity != 1 else 0 + + if catalog.pluralize != nil { + slot = catalog.pluralize(quantity) + } + return get_by_slot(section, key, slot, catalog) +} +get_n :: proc{get_single_section_with_quantity, get_by_section_with_quantity} + +/* + Two ways to use: + - get_by_slot(key, slot), which returns the requested plural from the active catalogue, or + - get_by_slot(key, slot, catalog) to grab text from a specific loaded catalogue. If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string. + - section: the catalogue section (sometime also called 'context') from which to lookup the translation + + Inputs: + - key: the string to translate. + - slot: the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue). + - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE) + + Returns: the translated string, or the original `key` if no translation was found. */ -get_by_slot_single_section :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) { +get_by_slot_single_section :: proc(key: string, slot: int, catalog: ^Translation = ACTIVE) -> (value: string) { return get_by_slot_by_section("", key, slot, catalog) } /* - Several ways to use: - - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or + Two ways to use: - get_by_slot(key, slot), which returns the requested plural from the active catalog, or - get_by_slot(key, slot, catalog) to grab text from a specific one. If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string. + + Inputs: + - section: the catalogue section (sometime also called 'context') from which to lookup the translation + - key: the string to translate. + - slot: the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue). + - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE) + + Returns: the translated string or the original `key` if no translation was found. */ -get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) { +get_by_slot_by_section :: proc(section, key: string, slot: int, catalog: ^Translation = ACTIVE) -> (value: string) { if catalog == nil || section not_in catalog.k_v { - /* - Return the key if the catalog catalog hasn't been initialized yet, or the section is not present. - */ + // Return the key if the catalog catalog hasn't been initialized yet, or the section is not present. return key } - /* - Return the translation from the requested slot if this key is known, else return the key. - */ + // Return the translation from the requested slot if this key is known, else return the key. if translations, ok := catalog.k_v[section][key]; ok { plural := min(max(0, slot), len(catalog.k_v[section][key]) - 1) return translations[plural] @@ -161,7 +210,6 @@ get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Transl get_by_slot :: proc{get_by_slot_single_section, get_by_slot_by_section} /* - Same for destroy: - destroy(), to clean up the currently active catalog catalog i18n.ACTIVE - destroy(catalog), to clean up a specific catalog. */ diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index 891c90bf3..af345f733 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -5,6 +5,12 @@ REPLACEMENT_CHAR :: '\ufffd' // Represented an invalid code point MAX_ASCII :: '\u007f' // Maximum ASCII value MAX_LATIN1 :: '\u00ff' // Maximum Latin-1 value +ZERO_WIDTH_SPACE :: '\u200B' +ZERO_WIDTH_NON_JOINER :: '\u200C' +ZERO_WIDTH_JOINER :: '\u200D' +WORD_JOINER :: '\u2060' + +@(require_results) binary_search :: proc(c: i32, table: []i32, length, stride: int) -> int { n := length t := 0 @@ -24,6 +30,7 @@ binary_search :: proc(c: i32, table: []i32, length, stride: int) -> int { return -1 } +@(require_results) to_lower :: proc(r: rune) -> rune { c := i32(r) p := binary_search(c, to_lower_ranges[:], len(to_lower_ranges)/3, 3) @@ -36,6 +43,7 @@ to_lower :: proc(r: rune) -> rune { } return rune(c) } +@(require_results) to_upper :: proc(r: rune) -> rune { c := i32(r) p := binary_search(c, to_upper_ranges[:], len(to_upper_ranges)/3, 3) @@ -48,6 +56,7 @@ to_upper :: proc(r: rune) -> rune { } return rune(c) } +@(require_results) to_title :: proc(r: rune) -> rune { c := i32(r) p := binary_search(c, to_upper_singlets[:], len(to_title_singlets)/2, 2) @@ -58,6 +67,7 @@ to_title :: proc(r: rune) -> rune { } +@(require_results) is_lower :: proc(r: rune) -> bool { if r <= MAX_ASCII { return u32(r)-'a' < 26 @@ -74,6 +84,7 @@ is_lower :: proc(r: rune) -> bool { return false } +@(require_results) is_upper :: proc(r: rune) -> bool { if r <= MAX_ASCII { return u32(r)-'A' < 26 @@ -91,6 +102,7 @@ is_upper :: proc(r: rune) -> bool { } is_alpha :: is_letter +@(require_results) is_letter :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pLmask != 0 @@ -111,10 +123,12 @@ is_letter :: proc(r: rune) -> bool { return false } +@(require_results) is_title :: proc(r: rune) -> bool { return is_upper(r) && is_lower(r) } +@(require_results) is_digit :: proc(r: rune) -> bool { if r <= MAX_LATIN1 { return '0' <= r && r <= '9' @@ -124,6 +138,7 @@ is_digit :: proc(r: rune) -> bool { is_white_space :: is_space +@(require_results) is_space :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { switch r { @@ -140,18 +155,20 @@ is_space :: proc(r: rune) -> bool { return false } +@(require_results) is_combining :: proc(r: rune) -> bool { c := i32(r) return c >= 0x0300 && (c <= 0x036f || - (c >= 0x1ab0 && c <= 0x1aff) || - (c >= 0x1dc0 && c <= 0x1dff) || - (c >= 0x20d0 && c <= 0x20ff) || - (c >= 0xfe20 && c <= 0xfe2f)) + (c >= 0x1ab0 && c <= 0x1aff) || + (c >= 0x1dc0 && c <= 0x1dff) || + (c >= 0x20d0 && c <= 0x20ff) || + (c >= 0xfe20 && c <= 0xfe2f)) } +@(require_results) is_graphic :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pg != 0 @@ -159,6 +176,7 @@ is_graphic :: proc(r: rune) -> bool { return false } +@(require_results) is_print :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pp != 0 @@ -166,6 +184,7 @@ is_print :: proc(r: rune) -> bool { return false } +@(require_results) is_control :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pC != 0 @@ -173,6 +192,7 @@ is_control :: proc(r: rune) -> bool { return false } +@(require_results) is_number :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pN != 0 @@ -180,6 +200,7 @@ is_number :: proc(r: rune) -> bool { return false } +@(require_results) is_punct :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pP != 0 @@ -187,9 +208,285 @@ is_punct :: proc(r: rune) -> bool { return false } +@(require_results) is_symbol :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pS != 0 } return false } + +// +// The procedures below are accurate as of Unicode 15.1.0. +// + +// Emoji_Modifier +@(require_results) +is_emoji_modifier :: proc(r: rune) -> bool { + return 0x1F3FB <= r && r <= 0x1F3FF +} + +// Regional_Indicator +@(require_results) +is_regional_indicator :: proc(r: rune) -> bool { + return 0x1F1E6 <= r && r <= 0x1F1FF +} + +// General_Category=Enclosing_Mark +@(require_results) +is_enclosing_mark :: proc(r: rune) -> bool { + switch r { + case 0x0488, + 0x0489, + 0x1ABE, + 0x20DD ..= 0x20E0, + 0x20E2 ..= 0x20E4, + 0xA670 ..= 0xA672: + return true + } + + return false +} + +// Prepended_Concatenation_Mark +@(require_results) +is_prepended_concatenation_mark :: proc(r: rune) -> bool { + switch r { + case 0x00600 ..= 0x00605, + 0x006DD, + 0x0070F, + 0x00890 ..= 0x00891, + 0x008E2, + 0x110BD, + 0x110CD: + return true + case: + return false + } +} + +// General_Category=Spacing_Mark +@(require_results) +is_spacing_mark :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, spacing_mark_ranges[:], len(spacing_mark_ranges)/2, 2) + if p >= 0 && spacing_mark_ranges[p] <= c && c <= spacing_mark_ranges[p+1] { + return true + } + return false +} + +// General_Category=Nonspacing_Mark +@(require_results) +is_nonspacing_mark :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, nonspacing_mark_ranges[:], len(nonspacing_mark_ranges)/2, 2) + if p >= 0 && nonspacing_mark_ranges[p] <= c && c <= nonspacing_mark_ranges[p+1] { + return true + } + return false +} + +// Extended_Pictographic +@(require_results) +is_emoji_extended_pictographic :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, emoji_extended_pictographic_ranges[:], len(emoji_extended_pictographic_ranges)/2, 2) + if p >= 0 && emoji_extended_pictographic_ranges[p] <= c && c <= emoji_extended_pictographic_ranges[p+1] { + return true + } + return false +} + +// Grapheme_Extend +@(require_results) +is_grapheme_extend :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, grapheme_extend_ranges[:], len(grapheme_extend_ranges)/2, 2) + if p >= 0 && grapheme_extend_ranges[p] <= c && c <= grapheme_extend_ranges[p+1] { + return true + } + return false +} + + +// Hangul_Syllable_Type=Leading_Jamo +@(require_results) +is_hangul_syllable_leading :: proc(r: rune) -> bool { + return 0x1100 <= r && r <= 0x115F || 0xA960 <= r && r <= 0xA97C +} + +// Hangul_Syllable_Type=Vowel_Jamo +@(require_results) +is_hangul_syllable_vowel :: proc(r: rune) -> bool { + return 0x1160 <= r && r <= 0x11A7 || 0xD7B0 <= r && r <= 0xD7C6 +} + +// Hangul_Syllable_Type=Trailing_Jamo +@(require_results) +is_hangul_syllable_trailing :: proc(r: rune) -> bool { + return 0x11A8 <= r && r <= 0x11FF || 0xD7CB <= r && r <= 0xD7FB +} + +// Hangul_Syllable_Type=LV_Syllable +@(require_results) +is_hangul_syllable_lv :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, hangul_syllable_lv_singlets[:], len(hangul_syllable_lv_singlets), 1) + if p >= 0 && c == hangul_syllable_lv_singlets[p] { + return true + } + return false +} + +// Hangul_Syllable_Type=LVT_Syllable +@(require_results) +is_hangul_syllable_lvt :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, hangul_syllable_lvt_ranges[:], len(hangul_syllable_lvt_ranges)/2, 2) + if p >= 0 && hangul_syllable_lvt_ranges[p] <= c && c <= hangul_syllable_lvt_ranges[p+1] { + return true + } + return false +} + + +// Indic_Syllabic_Category=Consonant_Preceding_Repha +@(require_results) +is_indic_consonant_preceding_repha :: proc(r: rune) -> bool { + switch r { + case 0x00D4E, + 0x11941, + 0x11D46, + 0x11F02: + return true + case: + return false + } +} + +// Indic_Syllabic_Category=Consonant_Prefixed +@(require_results) +is_indic_consonant_prefixed :: proc(r: rune) -> bool { + switch r { + case 0x111C2 ..= 0x111C3, + 0x1193F, + 0x11A3A, + 0x11A84 ..= 0x11A89: + return true + case: + return false + } +} + +// Indic_Conjunct_Break=Linker +@(require_results) +is_indic_conjunct_break_linker :: proc(r: rune) -> bool { + switch r { + case 0x094D, + 0x09CD, + 0x0ACD, + 0x0B4D, + 0x0C4D, + 0x0D4D: + return true + case: + return false + } +} + +// Indic_Conjunct_Break=Consonant +@(require_results) +is_indic_conjunct_break_consonant :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, indic_conjunct_break_consonant_ranges[:], len(indic_conjunct_break_consonant_ranges)/2, 2) + if p >= 0 && indic_conjunct_break_consonant_ranges[p] <= c && c <= indic_conjunct_break_consonant_ranges[p+1] { + return true + } + return false +} + +// Indic_Conjunct_Break=Extend +@(require_results) +is_indic_conjunct_break_extend :: proc(r: rune) -> bool { + c := i32(r) + p := binary_search(c, indic_conjunct_break_extend_ranges[:], len(indic_conjunct_break_extend_ranges)/2, 2) + if p >= 0 && indic_conjunct_break_extend_ranges[p] <= c && c <= indic_conjunct_break_extend_ranges[p+1] { + return true + } + return false +} + + +/* +For grapheme text segmentation, from Unicode TR 29 Rev 43: + +``` +Indic_Syllabic_Category = Consonant_Preceding_Repha, or +Indic_Syllabic_Category = Consonant_Prefixed, or +Prepended_Concatenation_Mark = Yes +``` +*/ +@(require_results) +is_gcb_prepend_class :: proc(r: rune) -> bool { + return is_indic_consonant_preceding_repha(r) || is_indic_consonant_prefixed(r) || is_prepended_concatenation_mark(r) +} + +/* +For grapheme text segmentation, from Unicode TR 29 Rev 43: + +``` +Grapheme_Extend = Yes, or +Emoji_Modifier = Yes + +This includes: +General_Category = Nonspacing_Mark +General_Category = Enclosing_Mark +U+200C ZERO WIDTH NON-JOINER + +plus a few General_Category = Spacing_Mark needed for canonical equivalence. +``` +*/ +@(require_results) +is_gcb_extend_class :: proc(r: rune) -> bool { + return is_grapheme_extend(r) || is_emoji_modifier(r) +} + +// Return values: +// +// - 2 if East_Asian_Width=F or W, or +// - 0 if non-printable / zero-width, or +// - 1 in all other cases. +// +@(require_results) +normalized_east_asian_width :: proc(r: rune) -> int { + // This is a different interpretation of the BOM which occurs in the middle of text. + ZERO_WIDTH_NO_BREAK_SPACE :: '\uFEFF' + + if is_control(r) { + return 0 + } else if r <= 0x10FF { + // Easy early out for low runes. + return 1 + } + + switch r { + case ZERO_WIDTH_NO_BREAK_SPACE, + ZERO_WIDTH_SPACE, + ZERO_WIDTH_NON_JOINER, + ZERO_WIDTH_JOINER, + WORD_JOINER: + return 0 + } + + c := i32(r) + p := binary_search(c, normalized_east_asian_width_ranges[:], len(normalized_east_asian_width_ranges)/3, 3) + if p >= 0 && normalized_east_asian_width_ranges[p] <= c && c <= normalized_east_asian_width_ranges[p+1] { + return cast(int)normalized_east_asian_width_ranges[p+2] + } + return 1 +} + +// +// End of Unicode 15.1.0 block. +// diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin index dfa5caaa2..c0b3fe434 100644 --- a/core/unicode/tables.odin +++ b/core/unicode/tables.odin @@ -1270,3 +1270,2623 @@ to_title_singlets := [?]i32{ 0x01f1, 501, 0x01f3, 499, } + +// +// The tables below are accurate as of Unicode 15.1.0. +// + +@(rodata) +spacing_mark_ranges := [?]i32 { + 0x0903, 0x0903, + 0x093B, 0x093B, + 0x093E, 0x0940, + 0x0949, 0x094C, + 0x094E, 0x094F, + 0x0982, 0x0983, + 0x09BE, 0x09C0, + 0x09C7, 0x09C8, + 0x09CB, 0x09CC, + 0x09D7, 0x09D7, + 0x0A03, 0x0A03, + 0x0A3E, 0x0A40, + 0x0A83, 0x0A83, + 0x0ABE, 0x0AC0, + 0x0AC9, 0x0AC9, + 0x0ACB, 0x0ACC, + 0x0B02, 0x0B03, + 0x0B3E, 0x0B3E, + 0x0B40, 0x0B40, + 0x0B47, 0x0B48, + 0x0B4B, 0x0B4C, + 0x0B57, 0x0B57, + 0x0BBE, 0x0BBF, + 0x0BC1, 0x0BC2, + 0x0BC6, 0x0BC8, + 0x0BCA, 0x0BCC, + 0x0BD7, 0x0BD7, + 0x0C01, 0x0C03, + 0x0C41, 0x0C44, + 0x0C82, 0x0C83, + 0x0CBE, 0x0CBE, + 0x0CC0, 0x0CC4, + 0x0CC7, 0x0CC8, + 0x0CCA, 0x0CCB, + 0x0CD5, 0x0CD6, + 0x0CF3, 0x0CF3, + 0x0D02, 0x0D03, + 0x0D3E, 0x0D40, + 0x0D46, 0x0D48, + 0x0D4A, 0x0D4C, + 0x0D57, 0x0D57, + 0x0D82, 0x0D83, + 0x0DCF, 0x0DD1, + 0x0DD8, 0x0DDF, + 0x0DF2, 0x0DF3, + 0x0F3E, 0x0F3F, + 0x0F7F, 0x0F7F, + 0x102B, 0x102C, + 0x1031, 0x1031, + 0x1038, 0x1038, + 0x103B, 0x103C, + 0x1056, 0x1057, + 0x1062, 0x1064, + 0x1067, 0x106D, + 0x1083, 0x1084, + 0x1087, 0x108C, + 0x108F, 0x108F, + 0x109A, 0x109C, + 0x1715, 0x1715, + 0x1734, 0x1734, + 0x17B6, 0x17B6, + 0x17BE, 0x17C5, + 0x17C7, 0x17C8, + 0x1923, 0x1926, + 0x1929, 0x192B, + 0x1930, 0x1931, + 0x1933, 0x1938, + 0x1A19, 0x1A1A, + 0x1A55, 0x1A55, + 0x1A57, 0x1A57, + 0x1A61, 0x1A61, + 0x1A63, 0x1A64, + 0x1A6D, 0x1A72, + 0x1B04, 0x1B04, + 0x1B35, 0x1B35, + 0x1B3B, 0x1B3B, + 0x1B3D, 0x1B41, + 0x1B43, 0x1B44, + 0x1B82, 0x1B82, + 0x1BA1, 0x1BA1, + 0x1BA6, 0x1BA7, + 0x1BAA, 0x1BAA, + 0x1BE7, 0x1BE7, + 0x1BEA, 0x1BEC, + 0x1BEE, 0x1BEE, + 0x1BF2, 0x1BF3, + 0x1C24, 0x1C2B, + 0x1C34, 0x1C35, + 0x1CE1, 0x1CE1, + 0x1CF7, 0x1CF7, + 0x302E, 0x302F, + 0xA823, 0xA824, + 0xA827, 0xA827, + 0xA880, 0xA881, + 0xA8B4, 0xA8C3, + 0xA952, 0xA953, + 0xA983, 0xA983, + 0xA9B4, 0xA9B5, + 0xA9BA, 0xA9BB, + 0xA9BE, 0xA9C0, + 0xAA2F, 0xAA30, + 0xAA33, 0xAA34, + 0xAA4D, 0xAA4D, + 0xAA7B, 0xAA7B, + 0xAA7D, 0xAA7D, + 0xAAEB, 0xAAEB, + 0xAAEE, 0xAAEF, + 0xAAF5, 0xAAF5, + 0xABE3, 0xABE4, + 0xABE6, 0xABE7, + 0xABE9, 0xABEA, + 0xABEC, 0xABEC, + 0x11000, 0x11000, + 0x11002, 0x11002, + 0x11082, 0x11082, + 0x110B0, 0x110B2, + 0x110B7, 0x110B8, + 0x1112C, 0x1112C, + 0x11145, 0x11146, + 0x11182, 0x11182, + 0x111B3, 0x111B5, + 0x111BF, 0x111C0, + 0x111CE, 0x111CE, + 0x1122C, 0x1122E, + 0x11232, 0x11233, + 0x11235, 0x11235, + 0x112E0, 0x112E2, + 0x11302, 0x11303, + 0x1133E, 0x1133F, + 0x11341, 0x11344, + 0x11347, 0x11348, + 0x1134B, 0x1134D, + 0x11357, 0x11357, + 0x11362, 0x11363, + 0x11435, 0x11437, + 0x11440, 0x11441, + 0x11445, 0x11445, + 0x114B0, 0x114B2, + 0x114B9, 0x114B9, + 0x114BB, 0x114BE, + 0x114C1, 0x114C1, + 0x115AF, 0x115B1, + 0x115B8, 0x115BB, + 0x115BE, 0x115BE, + 0x11630, 0x11632, + 0x1163B, 0x1163C, + 0x1163E, 0x1163E, + 0x116AC, 0x116AC, + 0x116AE, 0x116AF, + 0x116B6, 0x116B6, + 0x11720, 0x11721, + 0x11726, 0x11726, + 0x1182C, 0x1182E, + 0x11838, 0x11838, + 0x11930, 0x11935, + 0x11937, 0x11938, + 0x1193D, 0x1193D, + 0x11940, 0x11940, + 0x11942, 0x11942, + 0x119D1, 0x119D3, + 0x119DC, 0x119DF, + 0x119E4, 0x119E4, + 0x11A39, 0x11A39, + 0x11A57, 0x11A58, + 0x11A97, 0x11A97, + 0x11C2F, 0x11C2F, + 0x11C3E, 0x11C3E, + 0x11CA9, 0x11CA9, + 0x11CB1, 0x11CB1, + 0x11CB4, 0x11CB4, + 0x11D8A, 0x11D8E, + 0x11D93, 0x11D94, + 0x11D96, 0x11D96, + 0x11EF5, 0x11EF6, + 0x11F03, 0x11F03, + 0x11F34, 0x11F35, + 0x11F3E, 0x11F3F, + 0x11F41, 0x11F41, + 0x16F51, 0x16F87, + 0x16FF0, 0x16FF1, + 0x1D165, 0x1D166, + 0x1D16D, 0x1D172, +} + +@(rodata) +nonspacing_mark_ranges := [?]i32 { + 0x0300, 0x036F, + 0x0483, 0x0487, + 0x0591, 0x05BD, + 0x05BF, 0x05BF, + 0x05C1, 0x05C2, + 0x05C4, 0x05C5, + 0x05C7, 0x05C7, + 0x0610, 0x061A, + 0x064B, 0x065F, + 0x0670, 0x0670, + 0x06D6, 0x06DC, + 0x06DF, 0x06E4, + 0x06E7, 0x06E8, + 0x06EA, 0x06ED, + 0x0711, 0x0711, + 0x0730, 0x074A, + 0x07A6, 0x07B0, + 0x07EB, 0x07F3, + 0x07FD, 0x07FD, + 0x0816, 0x0819, + 0x081B, 0x0823, + 0x0825, 0x0827, + 0x0829, 0x082D, + 0x0859, 0x085B, + 0x0898, 0x089F, + 0x08CA, 0x08E1, + 0x08E3, 0x0902, + 0x093A, 0x093A, + 0x093C, 0x093C, + 0x0941, 0x0948, + 0x094D, 0x094D, + 0x0951, 0x0957, + 0x0962, 0x0963, + 0x0981, 0x0981, + 0x09BC, 0x09BC, + 0x09C1, 0x09C4, + 0x09CD, 0x09CD, + 0x09E2, 0x09E3, + 0x09FE, 0x09FE, + 0x0A01, 0x0A02, + 0x0A3C, 0x0A3C, + 0x0A41, 0x0A42, + 0x0A47, 0x0A48, + 0x0A4B, 0x0A4D, + 0x0A51, 0x0A51, + 0x0A70, 0x0A71, + 0x0A75, 0x0A75, + 0x0A81, 0x0A82, + 0x0ABC, 0x0ABC, + 0x0AC1, 0x0AC5, + 0x0AC7, 0x0AC8, + 0x0ACD, 0x0ACD, + 0x0AE2, 0x0AE3, + 0x0AFA, 0x0AFF, + 0x0B01, 0x0B01, + 0x0B3C, 0x0B3C, + 0x0B3F, 0x0B3F, + 0x0B41, 0x0B44, + 0x0B4D, 0x0B4D, + 0x0B55, 0x0B56, + 0x0B62, 0x0B63, + 0x0B82, 0x0B82, + 0x0BC0, 0x0BC0, + 0x0BCD, 0x0BCD, + 0x0C00, 0x0C00, + 0x0C04, 0x0C04, + 0x0C3C, 0x0C3C, + 0x0C3E, 0x0C40, + 0x0C46, 0x0C48, + 0x0C4A, 0x0C4D, + 0x0C55, 0x0C56, + 0x0C62, 0x0C63, + 0x0C81, 0x0C81, + 0x0CBC, 0x0CBC, + 0x0CBF, 0x0CBF, + 0x0CC6, 0x0CC6, + 0x0CCC, 0x0CCD, + 0x0CE2, 0x0CE3, + 0x0D00, 0x0D01, + 0x0D3B, 0x0D3C, + 0x0D41, 0x0D44, + 0x0D4D, 0x0D4D, + 0x0D62, 0x0D63, + 0x0D81, 0x0D81, + 0x0DCA, 0x0DCA, + 0x0DD2, 0x0DD4, + 0x0DD6, 0x0DD6, + 0x0E31, 0x0E31, + 0x0E34, 0x0E3A, + 0x0E47, 0x0E4E, + 0x0EB1, 0x0EB1, + 0x0EB4, 0x0EBC, + 0x0EC8, 0x0ECE, + 0x0F18, 0x0F19, + 0x0F35, 0x0F35, + 0x0F37, 0x0F37, + 0x0F39, 0x0F39, + 0x0F71, 0x0F7E, + 0x0F80, 0x0F84, + 0x0F86, 0x0F87, + 0x0F8D, 0x0F97, + 0x0F99, 0x0FBC, + 0x0FC6, 0x0FC6, + 0x102D, 0x1030, + 0x1032, 0x1037, + 0x1039, 0x103A, + 0x103D, 0x103E, + 0x1058, 0x1059, + 0x105E, 0x1060, + 0x1071, 0x1074, + 0x1082, 0x1082, + 0x1085, 0x1086, + 0x108D, 0x108D, + 0x109D, 0x109D, + 0x135D, 0x135F, + 0x1712, 0x1714, + 0x1732, 0x1733, + 0x1752, 0x1753, + 0x1772, 0x1773, + 0x17B4, 0x17B5, + 0x17B7, 0x17BD, + 0x17C6, 0x17C6, + 0x17C9, 0x17D3, + 0x17DD, 0x17DD, + 0x180B, 0x180D, + 0x180F, 0x180F, + 0x1885, 0x1886, + 0x18A9, 0x18A9, + 0x1920, 0x1922, + 0x1927, 0x1928, + 0x1932, 0x1932, + 0x1939, 0x193B, + 0x1A17, 0x1A18, + 0x1A1B, 0x1A1B, + 0x1A56, 0x1A56, + 0x1A58, 0x1A5E, + 0x1A60, 0x1A60, + 0x1A62, 0x1A62, + 0x1A65, 0x1A6C, + 0x1A73, 0x1A7C, + 0x1A7F, 0x1A7F, + 0x1AB0, 0x1ABD, + 0x1ABF, 0x1ACE, + 0x1B00, 0x1B03, + 0x1B34, 0x1B34, + 0x1B36, 0x1B3A, + 0x1B3C, 0x1B3C, + 0x1B42, 0x1B42, + 0x1B6B, 0x1B73, + 0x1B80, 0x1B81, + 0x1BA2, 0x1BA5, + 0x1BA8, 0x1BA9, + 0x1BAB, 0x1BAD, + 0x1BE6, 0x1BE6, + 0x1BE8, 0x1BE9, + 0x1BED, 0x1BED, + 0x1BEF, 0x1BF1, + 0x1C2C, 0x1C33, + 0x1C36, 0x1C37, + 0x1CD0, 0x1CD2, + 0x1CD4, 0x1CE0, + 0x1CE2, 0x1CE8, + 0x1CED, 0x1CED, + 0x1CF4, 0x1CF4, + 0x1CF8, 0x1CF9, + 0x1DC0, 0x1DFF, + 0x20D0, 0x20DC, + 0x20E1, 0x20E1, + 0x20E5, 0x20F0, + 0x2CEF, 0x2CF1, + 0x2D7F, 0x2D7F, + 0x2DE0, 0x2DFF, + 0x302A, 0x302D, + 0x3099, 0x309A, + 0xA66F, 0xA66F, + 0xA674, 0xA67D, + 0xA69E, 0xA69F, + 0xA6F0, 0xA6F1, + 0xA802, 0xA802, + 0xA806, 0xA806, + 0xA80B, 0xA80B, + 0xA825, 0xA826, + 0xA82C, 0xA82C, + 0xA8C4, 0xA8C5, + 0xA8E0, 0xA8F1, + 0xA8FF, 0xA8FF, + 0xA926, 0xA92D, + 0xA947, 0xA951, + 0xA980, 0xA982, + 0xA9B3, 0xA9B3, + 0xA9B6, 0xA9B9, + 0xA9BC, 0xA9BD, + 0xA9E5, 0xA9E5, + 0xAA29, 0xAA2E, + 0xAA31, 0xAA32, + 0xAA35, 0xAA36, + 0xAA43, 0xAA43, + 0xAA4C, 0xAA4C, + 0xAA7C, 0xAA7C, + 0xAAB0, 0xAAB0, + 0xAAB2, 0xAAB4, + 0xAAB7, 0xAAB8, + 0xAABE, 0xAABF, + 0xAAC1, 0xAAC1, + 0xAAEC, 0xAAED, + 0xAAF6, 0xAAF6, + 0xABE5, 0xABE5, + 0xABE8, 0xABE8, + 0xABED, 0xABED, + 0xFB1E, 0xFB1E, + 0xFE00, 0xFE0F, + 0xFE20, 0xFE2F, + 0x101FD, 0x101FD, + 0x102E0, 0x102E0, + 0x10376, 0x1037A, + 0x10A01, 0x10A03, + 0x10A05, 0x10A06, + 0x10A0C, 0x10A0F, + 0x10A38, 0x10A3A, + 0x10A3F, 0x10A3F, + 0x10AE5, 0x10AE6, + 0x10D24, 0x10D27, + 0x10EAB, 0x10EAC, + 0x10EFD, 0x10EFF, + 0x10F46, 0x10F50, + 0x10F82, 0x10F85, + 0x11001, 0x11001, + 0x11038, 0x11046, + 0x11070, 0x11070, + 0x11073, 0x11074, + 0x1107F, 0x11081, + 0x110B3, 0x110B6, + 0x110B9, 0x110BA, + 0x110C2, 0x110C2, + 0x11100, 0x11102, + 0x11127, 0x1112B, + 0x1112D, 0x11134, + 0x11173, 0x11173, + 0x11180, 0x11181, + 0x111B6, 0x111BE, + 0x111C9, 0x111CC, + 0x111CF, 0x111CF, + 0x1122F, 0x11231, + 0x11234, 0x11234, + 0x11236, 0x11237, + 0x1123E, 0x1123E, + 0x11241, 0x11241, + 0x112DF, 0x112DF, + 0x112E3, 0x112EA, + 0x11300, 0x11301, + 0x1133B, 0x1133C, + 0x11340, 0x11340, + 0x11366, 0x1136C, + 0x11370, 0x11374, + 0x11438, 0x1143F, + 0x11442, 0x11444, + 0x11446, 0x11446, + 0x1145E, 0x1145E, + 0x114B3, 0x114B8, + 0x114BA, 0x114BA, + 0x114BF, 0x114C0, + 0x114C2, 0x114C3, + 0x115B2, 0x115B5, + 0x115BC, 0x115BD, + 0x115BF, 0x115C0, + 0x115DC, 0x115DD, + 0x11633, 0x1163A, + 0x1163D, 0x1163D, + 0x1163F, 0x11640, + 0x116AB, 0x116AB, + 0x116AD, 0x116AD, + 0x116B0, 0x116B5, + 0x116B7, 0x116B7, + 0x1171D, 0x1171F, + 0x11722, 0x11725, + 0x11727, 0x1172B, + 0x1182F, 0x11837, + 0x11839, 0x1183A, + 0x1193B, 0x1193C, + 0x1193E, 0x1193E, + 0x11943, 0x11943, + 0x119D4, 0x119D7, + 0x119DA, 0x119DB, + 0x119E0, 0x119E0, + 0x11A01, 0x11A0A, + 0x11A33, 0x11A38, + 0x11A3B, 0x11A3E, + 0x11A47, 0x11A47, + 0x11A51, 0x11A56, + 0x11A59, 0x11A5B, + 0x11A8A, 0x11A96, + 0x11A98, 0x11A99, + 0x11C30, 0x11C36, + 0x11C38, 0x11C3D, + 0x11C3F, 0x11C3F, + 0x11C92, 0x11CA7, + 0x11CAA, 0x11CB0, + 0x11CB2, 0x11CB3, + 0x11CB5, 0x11CB6, + 0x11D31, 0x11D36, + 0x11D3A, 0x11D3A, + 0x11D3C, 0x11D3D, + 0x11D3F, 0x11D45, + 0x11D47, 0x11D47, + 0x11D90, 0x11D91, + 0x11D95, 0x11D95, + 0x11D97, 0x11D97, + 0x11EF3, 0x11EF4, + 0x11F00, 0x11F01, + 0x11F36, 0x11F3A, + 0x11F40, 0x11F40, + 0x11F42, 0x11F42, + 0x13440, 0x13440, + 0x13447, 0x13455, + 0x16AF0, 0x16AF4, + 0x16B30, 0x16B36, + 0x16F4F, 0x16F4F, + 0x16F8F, 0x16F92, + 0x16FE4, 0x16FE4, + 0x1BC9D, 0x1BC9E, + 0x1CF00, 0x1CF2D, + 0x1CF30, 0x1CF46, + 0x1D167, 0x1D169, + 0x1D17B, 0x1D182, + 0x1D185, 0x1D18B, + 0x1D1AA, 0x1D1AD, + 0x1D242, 0x1D244, + 0x1DA00, 0x1DA36, + 0x1DA3B, 0x1DA6C, + 0x1DA75, 0x1DA75, + 0x1DA84, 0x1DA84, + 0x1DA9B, 0x1DA9F, + 0x1DAA1, 0x1DAAF, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E08F, 0x1E08F, + 0x1E130, 0x1E136, + 0x1E2AE, 0x1E2AE, + 0x1E2EC, 0x1E2EF, + 0x1E4EC, 0x1E4EF, + 0x1E8D0, 0x1E8D6, + 0x1E944, 0x1E94A, + 0xE0100, 0xE01EF, +} + +@(rodata) +emoji_extended_pictographic_ranges := [?]i32 { + 0x00A9, 0x00A9, + 0x00AE, 0x00AE, + 0x203C, 0x203C, + 0x2049, 0x2049, + 0x2122, 0x2122, + 0x2139, 0x2139, + 0x2194, 0x2199, + 0x21A9, 0x21AA, + 0x231A, 0x231B, + 0x2328, 0x2328, + 0x2388, 0x2388, + 0x23CF, 0x23CF, + 0x23E9, 0x23EC, + 0x23ED, 0x23EE, + 0x23EF, 0x23EF, + 0x23F0, 0x23F0, + 0x23F1, 0x23F2, + 0x23F3, 0x23F3, + 0x23F8, 0x23FA, + 0x24C2, 0x24C2, + 0x25AA, 0x25AB, + 0x25B6, 0x25B6, + 0x25C0, 0x25C0, + 0x25FB, 0x25FE, + 0x2600, 0x2601, + 0x2602, 0x2603, + 0x2604, 0x2604, + 0x2605, 0x2605, + 0x2607, 0x260D, + 0x260E, 0x260E, + 0x260F, 0x2610, + 0x2611, 0x2611, + 0x2612, 0x2612, + 0x2614, 0x2615, + 0x2616, 0x2617, + 0x2618, 0x2618, + 0x2619, 0x261C, + 0x261D, 0x261D, + 0x261E, 0x261F, + 0x2620, 0x2620, + 0x2621, 0x2621, + 0x2622, 0x2623, + 0x2624, 0x2625, + 0x2626, 0x2626, + 0x2627, 0x2629, + 0x262A, 0x262A, + 0x262B, 0x262D, + 0x262E, 0x262E, + 0x262F, 0x262F, + 0x2630, 0x2637, + 0x2638, 0x2639, + 0x263A, 0x263A, + 0x263B, 0x263F, + 0x2640, 0x2640, + 0x2641, 0x2641, + 0x2642, 0x2642, + 0x2643, 0x2647, + 0x2648, 0x2653, + 0x2654, 0x265E, + 0x265F, 0x265F, + 0x2660, 0x2660, + 0x2661, 0x2662, + 0x2663, 0x2663, + 0x2664, 0x2664, + 0x2665, 0x2666, + 0x2667, 0x2667, + 0x2668, 0x2668, + 0x2669, 0x267A, + 0x267B, 0x267B, + 0x267C, 0x267D, + 0x267E, 0x267E, + 0x267F, 0x267F, + 0x2680, 0x2685, + 0x2690, 0x2691, + 0x2692, 0x2692, + 0x2693, 0x2693, + 0x2694, 0x2694, + 0x2695, 0x2695, + 0x2696, 0x2697, + 0x2698, 0x2698, + 0x2699, 0x2699, + 0x269A, 0x269A, + 0x269B, 0x269C, + 0x269D, 0x269F, + 0x26A0, 0x26A1, + 0x26A2, 0x26A6, + 0x26A7, 0x26A7, + 0x26A8, 0x26A9, + 0x26AA, 0x26AB, + 0x26AC, 0x26AF, + 0x26B0, 0x26B1, + 0x26B2, 0x26BC, + 0x26BD, 0x26BE, + 0x26BF, 0x26C3, + 0x26C4, 0x26C5, + 0x26C6, 0x26C7, + 0x26C8, 0x26C8, + 0x26C9, 0x26CD, + 0x26CE, 0x26CE, + 0x26CF, 0x26CF, + 0x26D0, 0x26D0, + 0x26D1, 0x26D1, + 0x26D2, 0x26D2, + 0x26D3, 0x26D3, + 0x26D4, 0x26D4, + 0x26D5, 0x26E8, + 0x26E9, 0x26E9, + 0x26EA, 0x26EA, + 0x26EB, 0x26EF, + 0x26F0, 0x26F1, + 0x26F2, 0x26F3, + 0x26F4, 0x26F4, + 0x26F5, 0x26F5, + 0x26F6, 0x26F6, + 0x26F7, 0x26F9, + 0x26FA, 0x26FA, + 0x26FB, 0x26FC, + 0x26FD, 0x26FD, + 0x26FE, 0x2701, + 0x2702, 0x2702, + 0x2703, 0x2704, + 0x2705, 0x2705, + 0x2708, 0x270C, + 0x270D, 0x270D, + 0x270E, 0x270E, + 0x270F, 0x270F, + 0x2710, 0x2711, + 0x2712, 0x2712, + 0x2714, 0x2714, + 0x2716, 0x2716, + 0x271D, 0x271D, + 0x2721, 0x2721, + 0x2728, 0x2728, + 0x2733, 0x2734, + 0x2744, 0x2744, + 0x2747, 0x2747, + 0x274C, 0x274C, + 0x274E, 0x274E, + 0x2753, 0x2755, + 0x2757, 0x2757, + 0x2763, 0x2763, + 0x2764, 0x2764, + 0x2765, 0x2767, + 0x2795, 0x2797, + 0x27A1, 0x27A1, + 0x27B0, 0x27B0, + 0x27BF, 0x27BF, + 0x2934, 0x2935, + 0x2B05, 0x2B07, + 0x2B1B, 0x2B1C, + 0x2B50, 0x2B50, + 0x2B55, 0x2B55, + 0x3030, 0x3030, + 0x303D, 0x303D, + 0x3297, 0x3297, + 0x3299, 0x3299, + 0x1F000, 0x1F003, + 0x1F004, 0x1F004, + 0x1F005, 0x1F0CE, + 0x1F0CF, 0x1F0CF, + 0x1F0D0, 0x1F0FF, + 0x1F10D, 0x1F10F, + 0x1F12F, 0x1F12F, + 0x1F16C, 0x1F16F, + 0x1F170, 0x1F171, + 0x1F17E, 0x1F17F, + 0x1F18E, 0x1F18E, + 0x1F191, 0x1F19A, + 0x1F1AD, 0x1F1E5, + 0x1F201, 0x1F202, + 0x1F203, 0x1F20F, + 0x1F21A, 0x1F21A, + 0x1F22F, 0x1F22F, + 0x1F232, 0x1F23A, + 0x1F23C, 0x1F23F, + 0x1F249, 0x1F24F, + 0x1F250, 0x1F251, + 0x1F252, 0x1F2FF, + 0x1F300, 0x1F30C, + 0x1F30D, 0x1F30E, + 0x1F30F, 0x1F30F, + 0x1F310, 0x1F310, + 0x1F311, 0x1F311, + 0x1F312, 0x1F312, + 0x1F313, 0x1F315, + 0x1F316, 0x1F318, + 0x1F319, 0x1F319, + 0x1F31A, 0x1F31A, + 0x1F31B, 0x1F31B, + 0x1F31C, 0x1F31C, + 0x1F31D, 0x1F31E, + 0x1F31F, 0x1F320, + 0x1F321, 0x1F321, + 0x1F322, 0x1F323, + 0x1F324, 0x1F32C, + 0x1F32D, 0x1F32F, + 0x1F330, 0x1F331, + 0x1F332, 0x1F333, + 0x1F334, 0x1F335, + 0x1F336, 0x1F336, + 0x1F337, 0x1F34A, + 0x1F34B, 0x1F34B, + 0x1F34C, 0x1F34F, + 0x1F350, 0x1F350, + 0x1F351, 0x1F37B, + 0x1F37C, 0x1F37C, + 0x1F37D, 0x1F37D, + 0x1F37E, 0x1F37F, + 0x1F380, 0x1F393, + 0x1F394, 0x1F395, + 0x1F396, 0x1F397, + 0x1F398, 0x1F398, + 0x1F399, 0x1F39B, + 0x1F39C, 0x1F39D, + 0x1F39E, 0x1F39F, + 0x1F3A0, 0x1F3C4, + 0x1F3C5, 0x1F3C5, + 0x1F3C6, 0x1F3C6, + 0x1F3C7, 0x1F3C7, + 0x1F3C8, 0x1F3C8, + 0x1F3C9, 0x1F3C9, + 0x1F3CA, 0x1F3CA, + 0x1F3CB, 0x1F3CE, + 0x1F3CF, 0x1F3D3, + 0x1F3D4, 0x1F3DF, + 0x1F3E0, 0x1F3E3, + 0x1F3E4, 0x1F3E4, + 0x1F3E5, 0x1F3F0, + 0x1F3F1, 0x1F3F2, + 0x1F3F3, 0x1F3F3, + 0x1F3F4, 0x1F3F4, + 0x1F3F5, 0x1F3F5, + 0x1F3F6, 0x1F3F6, + 0x1F3F7, 0x1F3F7, + 0x1F3F8, 0x1F3FA, + 0x1F400, 0x1F407, + 0x1F408, 0x1F408, + 0x1F409, 0x1F40B, + 0x1F40C, 0x1F40E, + 0x1F40F, 0x1F410, + 0x1F411, 0x1F412, + 0x1F413, 0x1F413, + 0x1F414, 0x1F414, + 0x1F415, 0x1F415, + 0x1F416, 0x1F416, + 0x1F417, 0x1F429, + 0x1F42A, 0x1F42A, + 0x1F42B, 0x1F43E, + 0x1F43F, 0x1F43F, + 0x1F440, 0x1F440, + 0x1F441, 0x1F441, + 0x1F442, 0x1F464, + 0x1F465, 0x1F465, + 0x1F466, 0x1F46B, + 0x1F46C, 0x1F46D, + 0x1F46E, 0x1F4AC, + 0x1F4AD, 0x1F4AD, + 0x1F4AE, 0x1F4B5, + 0x1F4B6, 0x1F4B7, + 0x1F4B8, 0x1F4EB, + 0x1F4EC, 0x1F4ED, + 0x1F4EE, 0x1F4EE, + 0x1F4EF, 0x1F4EF, + 0x1F4F0, 0x1F4F4, + 0x1F4F5, 0x1F4F5, + 0x1F4F6, 0x1F4F7, + 0x1F4F8, 0x1F4F8, + 0x1F4F9, 0x1F4FC, + 0x1F4FD, 0x1F4FD, + 0x1F4FE, 0x1F4FE, + 0x1F4FF, 0x1F502, + 0x1F503, 0x1F503, + 0x1F504, 0x1F507, + 0x1F508, 0x1F508, + 0x1F509, 0x1F509, + 0x1F50A, 0x1F514, + 0x1F515, 0x1F515, + 0x1F516, 0x1F52B, + 0x1F52C, 0x1F52D, + 0x1F52E, 0x1F53D, + 0x1F546, 0x1F548, + 0x1F549, 0x1F54A, + 0x1F54B, 0x1F54E, + 0x1F54F, 0x1F54F, + 0x1F550, 0x1F55B, + 0x1F55C, 0x1F567, + 0x1F568, 0x1F56E, + 0x1F56F, 0x1F570, + 0x1F571, 0x1F572, + 0x1F573, 0x1F579, + 0x1F57A, 0x1F57A, + 0x1F57B, 0x1F586, + 0x1F587, 0x1F587, + 0x1F588, 0x1F589, + 0x1F58A, 0x1F58D, + 0x1F58E, 0x1F58F, + 0x1F590, 0x1F590, + 0x1F591, 0x1F594, + 0x1F595, 0x1F596, + 0x1F597, 0x1F5A3, + 0x1F5A4, 0x1F5A4, + 0x1F5A5, 0x1F5A5, + 0x1F5A6, 0x1F5A7, + 0x1F5A8, 0x1F5A8, + 0x1F5A9, 0x1F5B0, + 0x1F5B1, 0x1F5B2, + 0x1F5B3, 0x1F5BB, + 0x1F5BC, 0x1F5BC, + 0x1F5BD, 0x1F5C1, + 0x1F5C2, 0x1F5C4, + 0x1F5C5, 0x1F5D0, + 0x1F5D1, 0x1F5D3, + 0x1F5D4, 0x1F5DB, + 0x1F5DC, 0x1F5DE, + 0x1F5DF, 0x1F5E0, + 0x1F5E1, 0x1F5E1, + 0x1F5E2, 0x1F5E2, + 0x1F5E3, 0x1F5E3, + 0x1F5E4, 0x1F5E7, + 0x1F5E8, 0x1F5E8, + 0x1F5E9, 0x1F5EE, + 0x1F5EF, 0x1F5EF, + 0x1F5F0, 0x1F5F2, + 0x1F5F3, 0x1F5F3, + 0x1F5F4, 0x1F5F9, + 0x1F5FA, 0x1F5FA, + 0x1F5FB, 0x1F5FF, + 0x1F600, 0x1F600, + 0x1F601, 0x1F606, + 0x1F607, 0x1F608, + 0x1F609, 0x1F60D, + 0x1F60E, 0x1F60E, + 0x1F60F, 0x1F60F, + 0x1F610, 0x1F610, + 0x1F611, 0x1F611, + 0x1F612, 0x1F614, + 0x1F615, 0x1F615, + 0x1F616, 0x1F616, + 0x1F617, 0x1F617, + 0x1F618, 0x1F618, + 0x1F619, 0x1F619, + 0x1F61A, 0x1F61A, + 0x1F61B, 0x1F61B, + 0x1F61C, 0x1F61E, + 0x1F61F, 0x1F61F, + 0x1F620, 0x1F625, + 0x1F626, 0x1F627, + 0x1F628, 0x1F62B, + 0x1F62C, 0x1F62C, + 0x1F62D, 0x1F62D, + 0x1F62E, 0x1F62F, + 0x1F630, 0x1F633, + 0x1F634, 0x1F634, + 0x1F635, 0x1F635, + 0x1F636, 0x1F636, + 0x1F637, 0x1F640, + 0x1F641, 0x1F644, + 0x1F645, 0x1F64F, + 0x1F680, 0x1F680, + 0x1F681, 0x1F682, + 0x1F683, 0x1F685, + 0x1F686, 0x1F686, + 0x1F687, 0x1F687, + 0x1F688, 0x1F688, + 0x1F689, 0x1F689, + 0x1F68A, 0x1F68B, + 0x1F68C, 0x1F68C, + 0x1F68D, 0x1F68D, + 0x1F68E, 0x1F68E, + 0x1F68F, 0x1F68F, + 0x1F690, 0x1F690, + 0x1F691, 0x1F693, + 0x1F694, 0x1F694, + 0x1F695, 0x1F695, + 0x1F696, 0x1F696, + 0x1F697, 0x1F697, + 0x1F698, 0x1F698, + 0x1F699, 0x1F69A, + 0x1F69B, 0x1F6A1, + 0x1F6A2, 0x1F6A2, + 0x1F6A3, 0x1F6A3, + 0x1F6A4, 0x1F6A5, + 0x1F6A6, 0x1F6A6, + 0x1F6A7, 0x1F6AD, + 0x1F6AE, 0x1F6B1, + 0x1F6B2, 0x1F6B2, + 0x1F6B3, 0x1F6B5, + 0x1F6B6, 0x1F6B6, + 0x1F6B7, 0x1F6B8, + 0x1F6B9, 0x1F6BE, + 0x1F6BF, 0x1F6BF, + 0x1F6C0, 0x1F6C0, + 0x1F6C1, 0x1F6C5, + 0x1F6C6, 0x1F6CA, + 0x1F6CB, 0x1F6CB, + 0x1F6CC, 0x1F6CC, + 0x1F6CD, 0x1F6CF, + 0x1F6D0, 0x1F6D0, + 0x1F6D1, 0x1F6D2, + 0x1F6D3, 0x1F6D4, + 0x1F6D5, 0x1F6D5, + 0x1F6D6, 0x1F6D7, + 0x1F6D8, 0x1F6DB, + 0x1F6DC, 0x1F6DC, + 0x1F6DD, 0x1F6DF, + 0x1F6E0, 0x1F6E5, + 0x1F6E6, 0x1F6E8, + 0x1F6E9, 0x1F6E9, + 0x1F6EA, 0x1F6EA, + 0x1F6EB, 0x1F6EC, + 0x1F6ED, 0x1F6EF, + 0x1F6F0, 0x1F6F0, + 0x1F6F1, 0x1F6F2, + 0x1F6F3, 0x1F6F3, + 0x1F6F4, 0x1F6F6, + 0x1F6F7, 0x1F6F8, + 0x1F6F9, 0x1F6F9, + 0x1F6FA, 0x1F6FA, + 0x1F6FB, 0x1F6FC, + 0x1F6FD, 0x1F6FF, + 0x1F774, 0x1F77F, + 0x1F7D5, 0x1F7DF, + 0x1F7E0, 0x1F7EB, + 0x1F7EC, 0x1F7EF, + 0x1F7F0, 0x1F7F0, + 0x1F7F1, 0x1F7FF, + 0x1F80C, 0x1F80F, + 0x1F848, 0x1F84F, + 0x1F85A, 0x1F85F, + 0x1F888, 0x1F88F, + 0x1F8AE, 0x1F8FF, + 0x1F90C, 0x1F90C, + 0x1F90D, 0x1F90F, + 0x1F910, 0x1F918, + 0x1F919, 0x1F91E, + 0x1F91F, 0x1F91F, + 0x1F920, 0x1F927, + 0x1F928, 0x1F92F, + 0x1F930, 0x1F930, + 0x1F931, 0x1F932, + 0x1F933, 0x1F93A, + 0x1F93C, 0x1F93E, + 0x1F93F, 0x1F93F, + 0x1F940, 0x1F945, + 0x1F947, 0x1F94B, + 0x1F94C, 0x1F94C, + 0x1F94D, 0x1F94F, + 0x1F950, 0x1F95E, + 0x1F95F, 0x1F96B, + 0x1F96C, 0x1F970, + 0x1F971, 0x1F971, + 0x1F972, 0x1F972, + 0x1F973, 0x1F976, + 0x1F977, 0x1F978, + 0x1F979, 0x1F979, + 0x1F97A, 0x1F97A, + 0x1F97B, 0x1F97B, + 0x1F97C, 0x1F97F, + 0x1F980, 0x1F984, + 0x1F985, 0x1F991, + 0x1F992, 0x1F997, + 0x1F998, 0x1F9A2, + 0x1F9A3, 0x1F9A4, + 0x1F9A5, 0x1F9AA, + 0x1F9AB, 0x1F9AD, + 0x1F9AE, 0x1F9AF, + 0x1F9B0, 0x1F9B9, + 0x1F9BA, 0x1F9BF, + 0x1F9C0, 0x1F9C0, + 0x1F9C1, 0x1F9C2, + 0x1F9C3, 0x1F9CA, + 0x1F9CB, 0x1F9CB, + 0x1F9CC, 0x1F9CC, + 0x1F9CD, 0x1F9CF, + 0x1F9D0, 0x1F9E6, + 0x1F9E7, 0x1F9FF, + 0x1FA00, 0x1FA6F, + 0x1FA70, 0x1FA73, + 0x1FA74, 0x1FA74, + 0x1FA75, 0x1FA77, + 0x1FA78, 0x1FA7A, + 0x1FA7B, 0x1FA7C, + 0x1FA7D, 0x1FA7F, + 0x1FA80, 0x1FA82, + 0x1FA83, 0x1FA86, + 0x1FA87, 0x1FA88, + 0x1FA89, 0x1FA8F, + 0x1FA90, 0x1FA95, + 0x1FA96, 0x1FAA8, + 0x1FAA9, 0x1FAAC, + 0x1FAAD, 0x1FAAF, + 0x1FAB0, 0x1FAB6, + 0x1FAB7, 0x1FABA, + 0x1FABB, 0x1FABD, + 0x1FABE, 0x1FABE, + 0x1FABF, 0x1FABF, + 0x1FAC0, 0x1FAC2, + 0x1FAC3, 0x1FAC5, + 0x1FAC6, 0x1FACD, + 0x1FACE, 0x1FACF, + 0x1FAD0, 0x1FAD6, + 0x1FAD7, 0x1FAD9, + 0x1FADA, 0x1FADB, + 0x1FADC, 0x1FADF, + 0x1FAE0, 0x1FAE7, + 0x1FAE8, 0x1FAE8, + 0x1FAE9, 0x1FAEF, + 0x1FAF0, 0x1FAF6, + 0x1FAF7, 0x1FAF8, + 0x1FAF9, 0x1FAFF, + 0x1FC00, 0x1FFFD, +} + +@(rodata) +grapheme_extend_ranges := [?]i32 { + 0x0300, 0x036F, + 0x0483, 0x0487, + 0x0488, 0x0489, + 0x0591, 0x05BD, + 0x05BF, 0x05BF, + 0x05C1, 0x05C2, + 0x05C4, 0x05C5, + 0x05C7, 0x05C7, + 0x0610, 0x061A, + 0x064B, 0x065F, + 0x0670, 0x0670, + 0x06D6, 0x06DC, + 0x06DF, 0x06E4, + 0x06E7, 0x06E8, + 0x06EA, 0x06ED, + 0x0711, 0x0711, + 0x0730, 0x074A, + 0x07A6, 0x07B0, + 0x07EB, 0x07F3, + 0x07FD, 0x07FD, + 0x0816, 0x0819, + 0x081B, 0x0823, + 0x0825, 0x0827, + 0x0829, 0x082D, + 0x0859, 0x085B, + 0x0898, 0x089F, + 0x08CA, 0x08E1, + 0x08E3, 0x0902, + 0x093A, 0x093A, + 0x093C, 0x093C, + 0x0941, 0x0948, + 0x094D, 0x094D, + 0x0951, 0x0957, + 0x0962, 0x0963, + 0x0981, 0x0981, + 0x09BC, 0x09BC, + 0x09BE, 0x09BE, + 0x09C1, 0x09C4, + 0x09CD, 0x09CD, + 0x09D7, 0x09D7, + 0x09E2, 0x09E3, + 0x09FE, 0x09FE, + 0x0A01, 0x0A02, + 0x0A3C, 0x0A3C, + 0x0A41, 0x0A42, + 0x0A47, 0x0A48, + 0x0A4B, 0x0A4D, + 0x0A51, 0x0A51, + 0x0A70, 0x0A71, + 0x0A75, 0x0A75, + 0x0A81, 0x0A82, + 0x0ABC, 0x0ABC, + 0x0AC1, 0x0AC5, + 0x0AC7, 0x0AC8, + 0x0ACD, 0x0ACD, + 0x0AE2, 0x0AE3, + 0x0AFA, 0x0AFF, + 0x0B01, 0x0B01, + 0x0B3C, 0x0B3C, + 0x0B3E, 0x0B3E, + 0x0B3F, 0x0B3F, + 0x0B41, 0x0B44, + 0x0B4D, 0x0B4D, + 0x0B55, 0x0B56, + 0x0B57, 0x0B57, + 0x0B62, 0x0B63, + 0x0B82, 0x0B82, + 0x0BBE, 0x0BBE, + 0x0BC0, 0x0BC0, + 0x0BCD, 0x0BCD, + 0x0BD7, 0x0BD7, + 0x0C00, 0x0C00, + 0x0C04, 0x0C04, + 0x0C3C, 0x0C3C, + 0x0C3E, 0x0C40, + 0x0C46, 0x0C48, + 0x0C4A, 0x0C4D, + 0x0C55, 0x0C56, + 0x0C62, 0x0C63, + 0x0C81, 0x0C81, + 0x0CBC, 0x0CBC, + 0x0CBF, 0x0CBF, + 0x0CC2, 0x0CC2, + 0x0CC6, 0x0CC6, + 0x0CCC, 0x0CCD, + 0x0CD5, 0x0CD6, + 0x0CE2, 0x0CE3, + 0x0D00, 0x0D01, + 0x0D3B, 0x0D3C, + 0x0D3E, 0x0D3E, + 0x0D41, 0x0D44, + 0x0D4D, 0x0D4D, + 0x0D57, 0x0D57, + 0x0D62, 0x0D63, + 0x0D81, 0x0D81, + 0x0DCA, 0x0DCA, + 0x0DCF, 0x0DCF, + 0x0DD2, 0x0DD4, + 0x0DD6, 0x0DD6, + 0x0DDF, 0x0DDF, + 0x0E31, 0x0E31, + 0x0E34, 0x0E3A, + 0x0E47, 0x0E4E, + 0x0EB1, 0x0EB1, + 0x0EB4, 0x0EBC, + 0x0EC8, 0x0ECE, + 0x0F18, 0x0F19, + 0x0F35, 0x0F35, + 0x0F37, 0x0F37, + 0x0F39, 0x0F39, + 0x0F71, 0x0F7E, + 0x0F80, 0x0F84, + 0x0F86, 0x0F87, + 0x0F8D, 0x0F97, + 0x0F99, 0x0FBC, + 0x0FC6, 0x0FC6, + 0x102D, 0x1030, + 0x1032, 0x1037, + 0x1039, 0x103A, + 0x103D, 0x103E, + 0x1058, 0x1059, + 0x105E, 0x1060, + 0x1071, 0x1074, + 0x1082, 0x1082, + 0x1085, 0x1086, + 0x108D, 0x108D, + 0x109D, 0x109D, + 0x135D, 0x135F, + 0x1712, 0x1714, + 0x1732, 0x1733, + 0x1752, 0x1753, + 0x1772, 0x1773, + 0x17B4, 0x17B5, + 0x17B7, 0x17BD, + 0x17C6, 0x17C6, + 0x17C9, 0x17D3, + 0x17DD, 0x17DD, + 0x180B, 0x180D, + 0x180F, 0x180F, + 0x1885, 0x1886, + 0x18A9, 0x18A9, + 0x1920, 0x1922, + 0x1927, 0x1928, + 0x1932, 0x1932, + 0x1939, 0x193B, + 0x1A17, 0x1A18, + 0x1A1B, 0x1A1B, + 0x1A56, 0x1A56, + 0x1A58, 0x1A5E, + 0x1A60, 0x1A60, + 0x1A62, 0x1A62, + 0x1A65, 0x1A6C, + 0x1A73, 0x1A7C, + 0x1A7F, 0x1A7F, + 0x1AB0, 0x1ABD, + 0x1ABE, 0x1ABE, + 0x1ABF, 0x1ACE, + 0x1B00, 0x1B03, + 0x1B34, 0x1B34, + 0x1B35, 0x1B35, + 0x1B36, 0x1B3A, + 0x1B3C, 0x1B3C, + 0x1B42, 0x1B42, + 0x1B6B, 0x1B73, + 0x1B80, 0x1B81, + 0x1BA2, 0x1BA5, + 0x1BA8, 0x1BA9, + 0x1BAB, 0x1BAD, + 0x1BE6, 0x1BE6, + 0x1BE8, 0x1BE9, + 0x1BED, 0x1BED, + 0x1BEF, 0x1BF1, + 0x1C2C, 0x1C33, + 0x1C36, 0x1C37, + 0x1CD0, 0x1CD2, + 0x1CD4, 0x1CE0, + 0x1CE2, 0x1CE8, + 0x1CED, 0x1CED, + 0x1CF4, 0x1CF4, + 0x1CF8, 0x1CF9, + 0x1DC0, 0x1DFF, + 0x200C, 0x200C, + 0x20D0, 0x20DC, + 0x20DD, 0x20E0, + 0x20E1, 0x20E1, + 0x20E2, 0x20E4, + 0x20E5, 0x20F0, + 0x2CEF, 0x2CF1, + 0x2D7F, 0x2D7F, + 0x2DE0, 0x2DFF, + 0x302A, 0x302D, + 0x302E, 0x302F, + 0x3099, 0x309A, + 0xA66F, 0xA66F, + 0xA670, 0xA672, + 0xA674, 0xA67D, + 0xA69E, 0xA69F, + 0xA6F0, 0xA6F1, + 0xA802, 0xA802, + 0xA806, 0xA806, + 0xA80B, 0xA80B, + 0xA825, 0xA826, + 0xA82C, 0xA82C, + 0xA8C4, 0xA8C5, + 0xA8E0, 0xA8F1, + 0xA8FF, 0xA8FF, + 0xA926, 0xA92D, + 0xA947, 0xA951, + 0xA980, 0xA982, + 0xA9B3, 0xA9B3, + 0xA9B6, 0xA9B9, + 0xA9BC, 0xA9BD, + 0xA9E5, 0xA9E5, + 0xAA29, 0xAA2E, + 0xAA31, 0xAA32, + 0xAA35, 0xAA36, + 0xAA43, 0xAA43, + 0xAA4C, 0xAA4C, + 0xAA7C, 0xAA7C, + 0xAAB0, 0xAAB0, + 0xAAB2, 0xAAB4, + 0xAAB7, 0xAAB8, + 0xAABE, 0xAABF, + 0xAAC1, 0xAAC1, + 0xAAEC, 0xAAED, + 0xAAF6, 0xAAF6, + 0xABE5, 0xABE5, + 0xABE8, 0xABE8, + 0xABED, 0xABED, + 0xFB1E, 0xFB1E, + 0xFE00, 0xFE0F, + 0xFE20, 0xFE2F, + 0xFF9E, 0xFF9F, + 0x101FD, 0x101FD, + 0x102E0, 0x102E0, + 0x10376, 0x1037A, + 0x10A01, 0x10A03, + 0x10A05, 0x10A06, + 0x10A0C, 0x10A0F, + 0x10A38, 0x10A3A, + 0x10A3F, 0x10A3F, + 0x10AE5, 0x10AE6, + 0x10D24, 0x10D27, + 0x10EAB, 0x10EAC, + 0x10EFD, 0x10EFF, + 0x10F46, 0x10F50, + 0x10F82, 0x10F85, + 0x11001, 0x11001, + 0x11038, 0x11046, + 0x11070, 0x11070, + 0x11073, 0x11074, + 0x1107F, 0x11081, + 0x110B3, 0x110B6, + 0x110B9, 0x110BA, + 0x110C2, 0x110C2, + 0x11100, 0x11102, + 0x11127, 0x1112B, + 0x1112D, 0x11134, + 0x11173, 0x11173, + 0x11180, 0x11181, + 0x111B6, 0x111BE, + 0x111C9, 0x111CC, + 0x111CF, 0x111CF, + 0x1122F, 0x11231, + 0x11234, 0x11234, + 0x11236, 0x11237, + 0x1123E, 0x1123E, + 0x11241, 0x11241, + 0x112DF, 0x112DF, + 0x112E3, 0x112EA, + 0x11300, 0x11301, + 0x1133B, 0x1133C, + 0x1133E, 0x1133E, + 0x11340, 0x11340, + 0x11357, 0x11357, + 0x11366, 0x1136C, + 0x11370, 0x11374, + 0x11438, 0x1143F, + 0x11442, 0x11444, + 0x11446, 0x11446, + 0x1145E, 0x1145E, + 0x114B0, 0x114B0, + 0x114B3, 0x114B8, + 0x114BA, 0x114BA, + 0x114BD, 0x114BD, + 0x114BF, 0x114C0, + 0x114C2, 0x114C3, + 0x115AF, 0x115AF, + 0x115B2, 0x115B5, + 0x115BC, 0x115BD, + 0x115BF, 0x115C0, + 0x115DC, 0x115DD, + 0x11633, 0x1163A, + 0x1163D, 0x1163D, + 0x1163F, 0x11640, + 0x116AB, 0x116AB, + 0x116AD, 0x116AD, + 0x116B0, 0x116B5, + 0x116B7, 0x116B7, + 0x1171D, 0x1171F, + 0x11722, 0x11725, + 0x11727, 0x1172B, + 0x1182F, 0x11837, + 0x11839, 0x1183A, + 0x11930, 0x11930, + 0x1193B, 0x1193C, + 0x1193E, 0x1193E, + 0x11943, 0x11943, + 0x119D4, 0x119D7, + 0x119DA, 0x119DB, + 0x119E0, 0x119E0, + 0x11A01, 0x11A0A, + 0x11A33, 0x11A38, + 0x11A3B, 0x11A3E, + 0x11A47, 0x11A47, + 0x11A51, 0x11A56, + 0x11A59, 0x11A5B, + 0x11A8A, 0x11A96, + 0x11A98, 0x11A99, + 0x11C30, 0x11C36, + 0x11C38, 0x11C3D, + 0x11C3F, 0x11C3F, + 0x11C92, 0x11CA7, + 0x11CAA, 0x11CB0, + 0x11CB2, 0x11CB3, + 0x11CB5, 0x11CB6, + 0x11D31, 0x11D36, + 0x11D3A, 0x11D3A, + 0x11D3C, 0x11D3D, + 0x11D3F, 0x11D45, + 0x11D47, 0x11D47, + 0x11D90, 0x11D91, + 0x11D95, 0x11D95, + 0x11D97, 0x11D97, + 0x11EF3, 0x11EF4, + 0x11F00, 0x11F01, + 0x11F36, 0x11F3A, + 0x11F40, 0x11F40, + 0x11F42, 0x11F42, + 0x13440, 0x13440, + 0x13447, 0x13455, + 0x16AF0, 0x16AF4, + 0x16B30, 0x16B36, + 0x16F4F, 0x16F4F, + 0x16F8F, 0x16F92, + 0x16FE4, 0x16FE4, + 0x1BC9D, 0x1BC9E, + 0x1CF00, 0x1CF2D, + 0x1CF30, 0x1CF46, + 0x1D165, 0x1D165, + 0x1D167, 0x1D169, + 0x1D16E, 0x1D172, + 0x1D17B, 0x1D182, + 0x1D185, 0x1D18B, + 0x1D1AA, 0x1D1AD, + 0x1D242, 0x1D244, + 0x1DA00, 0x1DA36, + 0x1DA3B, 0x1DA6C, + 0x1DA75, 0x1DA75, + 0x1DA84, 0x1DA84, + 0x1DA9B, 0x1DA9F, + 0x1DAA1, 0x1DAAF, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E08F, 0x1E08F, + 0x1E130, 0x1E136, + 0x1E2AE, 0x1E2AE, + 0x1E2EC, 0x1E2EF, + 0x1E4EC, 0x1E4EF, + 0x1E8D0, 0x1E8D6, + 0x1E944, 0x1E94A, + 0xE0020, 0xE007F, + 0xE0100, 0xE01EF, +} + +@(rodata) +hangul_syllable_lv_singlets := [?]i32 { + 0xAC00, + 0xAC1C, + 0xAC38, + 0xAC54, + 0xAC70, + 0xAC8C, + 0xACA8, + 0xACC4, + 0xACE0, + 0xACFC, + 0xAD18, + 0xAD34, + 0xAD50, + 0xAD6C, + 0xAD88, + 0xADA4, + 0xADC0, + 0xADDC, + 0xADF8, + 0xAE14, + 0xAE30, + 0xAE4C, + 0xAE68, + 0xAE84, + 0xAEA0, + 0xAEBC, + 0xAED8, + 0xAEF4, + 0xAF10, + 0xAF2C, + 0xAF48, + 0xAF64, + 0xAF80, + 0xAF9C, + 0xAFB8, + 0xAFD4, + 0xAFF0, + 0xB00C, + 0xB028, + 0xB044, + 0xB060, + 0xB07C, + 0xB098, + 0xB0B4, + 0xB0D0, + 0xB0EC, + 0xB108, + 0xB124, + 0xB140, + 0xB15C, + 0xB178, + 0xB194, + 0xB1B0, + 0xB1CC, + 0xB1E8, + 0xB204, + 0xB220, + 0xB23C, + 0xB258, + 0xB274, + 0xB290, + 0xB2AC, + 0xB2C8, + 0xB2E4, + 0xB300, + 0xB31C, + 0xB338, + 0xB354, + 0xB370, + 0xB38C, + 0xB3A8, + 0xB3C4, + 0xB3E0, + 0xB3FC, + 0xB418, + 0xB434, + 0xB450, + 0xB46C, + 0xB488, + 0xB4A4, + 0xB4C0, + 0xB4DC, + 0xB4F8, + 0xB514, + 0xB530, + 0xB54C, + 0xB568, + 0xB584, + 0xB5A0, + 0xB5BC, + 0xB5D8, + 0xB5F4, + 0xB610, + 0xB62C, + 0xB648, + 0xB664, + 0xB680, + 0xB69C, + 0xB6B8, + 0xB6D4, + 0xB6F0, + 0xB70C, + 0xB728, + 0xB744, + 0xB760, + 0xB77C, + 0xB798, + 0xB7B4, + 0xB7D0, + 0xB7EC, + 0xB808, + 0xB824, + 0xB840, + 0xB85C, + 0xB878, + 0xB894, + 0xB8B0, + 0xB8CC, + 0xB8E8, + 0xB904, + 0xB920, + 0xB93C, + 0xB958, + 0xB974, + 0xB990, + 0xB9AC, + 0xB9C8, + 0xB9E4, + 0xBA00, + 0xBA1C, + 0xBA38, + 0xBA54, + 0xBA70, + 0xBA8C, + 0xBAA8, + 0xBAC4, + 0xBAE0, + 0xBAFC, + 0xBB18, + 0xBB34, + 0xBB50, + 0xBB6C, + 0xBB88, + 0xBBA4, + 0xBBC0, + 0xBBDC, + 0xBBF8, + 0xBC14, + 0xBC30, + 0xBC4C, + 0xBC68, + 0xBC84, + 0xBCA0, + 0xBCBC, + 0xBCD8, + 0xBCF4, + 0xBD10, + 0xBD2C, + 0xBD48, + 0xBD64, + 0xBD80, + 0xBD9C, + 0xBDB8, + 0xBDD4, + 0xBDF0, + 0xBE0C, + 0xBE28, + 0xBE44, + 0xBE60, + 0xBE7C, + 0xBE98, + 0xBEB4, + 0xBED0, + 0xBEEC, + 0xBF08, + 0xBF24, + 0xBF40, + 0xBF5C, + 0xBF78, + 0xBF94, + 0xBFB0, + 0xBFCC, + 0xBFE8, + 0xC004, + 0xC020, + 0xC03C, + 0xC058, + 0xC074, + 0xC090, + 0xC0AC, + 0xC0C8, + 0xC0E4, + 0xC100, + 0xC11C, + 0xC138, + 0xC154, + 0xC170, + 0xC18C, + 0xC1A8, + 0xC1C4, + 0xC1E0, + 0xC1FC, + 0xC218, + 0xC234, + 0xC250, + 0xC26C, + 0xC288, + 0xC2A4, + 0xC2C0, + 0xC2DC, + 0xC2F8, + 0xC314, + 0xC330, + 0xC34C, + 0xC368, + 0xC384, + 0xC3A0, + 0xC3BC, + 0xC3D8, + 0xC3F4, + 0xC410, + 0xC42C, + 0xC448, + 0xC464, + 0xC480, + 0xC49C, + 0xC4B8, + 0xC4D4, + 0xC4F0, + 0xC50C, + 0xC528, + 0xC544, + 0xC560, + 0xC57C, + 0xC598, + 0xC5B4, + 0xC5D0, + 0xC5EC, + 0xC608, + 0xC624, + 0xC640, + 0xC65C, + 0xC678, + 0xC694, + 0xC6B0, + 0xC6CC, + 0xC6E8, + 0xC704, + 0xC720, + 0xC73C, + 0xC758, + 0xC774, + 0xC790, + 0xC7AC, + 0xC7C8, + 0xC7E4, + 0xC800, + 0xC81C, + 0xC838, + 0xC854, + 0xC870, + 0xC88C, + 0xC8A8, + 0xC8C4, + 0xC8E0, + 0xC8FC, + 0xC918, + 0xC934, + 0xC950, + 0xC96C, + 0xC988, + 0xC9A4, + 0xC9C0, + 0xC9DC, + 0xC9F8, + 0xCA14, + 0xCA30, + 0xCA4C, + 0xCA68, + 0xCA84, + 0xCAA0, + 0xCABC, + 0xCAD8, + 0xCAF4, + 0xCB10, + 0xCB2C, + 0xCB48, + 0xCB64, + 0xCB80, + 0xCB9C, + 0xCBB8, + 0xCBD4, + 0xCBF0, + 0xCC0C, + 0xCC28, + 0xCC44, + 0xCC60, + 0xCC7C, + 0xCC98, + 0xCCB4, + 0xCCD0, + 0xCCEC, + 0xCD08, + 0xCD24, + 0xCD40, + 0xCD5C, + 0xCD78, + 0xCD94, + 0xCDB0, + 0xCDCC, + 0xCDE8, + 0xCE04, + 0xCE20, + 0xCE3C, + 0xCE58, + 0xCE74, + 0xCE90, + 0xCEAC, + 0xCEC8, + 0xCEE4, + 0xCF00, + 0xCF1C, + 0xCF38, + 0xCF54, + 0xCF70, + 0xCF8C, + 0xCFA8, + 0xCFC4, + 0xCFE0, + 0xCFFC, + 0xD018, + 0xD034, + 0xD050, + 0xD06C, + 0xD088, + 0xD0A4, + 0xD0C0, + 0xD0DC, + 0xD0F8, + 0xD114, + 0xD130, + 0xD14C, + 0xD168, + 0xD184, + 0xD1A0, + 0xD1BC, + 0xD1D8, + 0xD1F4, + 0xD210, + 0xD22C, + 0xD248, + 0xD264, + 0xD280, + 0xD29C, + 0xD2B8, + 0xD2D4, + 0xD2F0, + 0xD30C, + 0xD328, + 0xD344, + 0xD360, + 0xD37C, + 0xD398, + 0xD3B4, + 0xD3D0, + 0xD3EC, + 0xD408, + 0xD424, + 0xD440, + 0xD45C, + 0xD478, + 0xD494, + 0xD4B0, + 0xD4CC, + 0xD4E8, + 0xD504, + 0xD520, + 0xD53C, + 0xD558, + 0xD574, + 0xD590, + 0xD5AC, + 0xD5C8, + 0xD5E4, + 0xD600, + 0xD61C, + 0xD638, + 0xD654, + 0xD670, + 0xD68C, + 0xD6A8, + 0xD6C4, + 0xD6E0, + 0xD6FC, + 0xD718, + 0xD734, + 0xD750, + 0xD76C, + 0xD788, +} + +@(rodata) +hangul_syllable_lvt_ranges := [?]i32 { + 0xAC01, 0xAC1B, + 0xAC1D, 0xAC37, + 0xAC39, 0xAC53, + 0xAC55, 0xAC6F, + 0xAC71, 0xAC8B, + 0xAC8D, 0xACA7, + 0xACA9, 0xACC3, + 0xACC5, 0xACDF, + 0xACE1, 0xACFB, + 0xACFD, 0xAD17, + 0xAD19, 0xAD33, + 0xAD35, 0xAD4F, + 0xAD51, 0xAD6B, + 0xAD6D, 0xAD87, + 0xAD89, 0xADA3, + 0xADA5, 0xADBF, + 0xADC1, 0xADDB, + 0xADDD, 0xADF7, + 0xADF9, 0xAE13, + 0xAE15, 0xAE2F, + 0xAE31, 0xAE4B, + 0xAE4D, 0xAE67, + 0xAE69, 0xAE83, + 0xAE85, 0xAE9F, + 0xAEA1, 0xAEBB, + 0xAEBD, 0xAED7, + 0xAED9, 0xAEF3, + 0xAEF5, 0xAF0F, + 0xAF11, 0xAF2B, + 0xAF2D, 0xAF47, + 0xAF49, 0xAF63, + 0xAF65, 0xAF7F, + 0xAF81, 0xAF9B, + 0xAF9D, 0xAFB7, + 0xAFB9, 0xAFD3, + 0xAFD5, 0xAFEF, + 0xAFF1, 0xB00B, + 0xB00D, 0xB027, + 0xB029, 0xB043, + 0xB045, 0xB05F, + 0xB061, 0xB07B, + 0xB07D, 0xB097, + 0xB099, 0xB0B3, + 0xB0B5, 0xB0CF, + 0xB0D1, 0xB0EB, + 0xB0ED, 0xB107, + 0xB109, 0xB123, + 0xB125, 0xB13F, + 0xB141, 0xB15B, + 0xB15D, 0xB177, + 0xB179, 0xB193, + 0xB195, 0xB1AF, + 0xB1B1, 0xB1CB, + 0xB1CD, 0xB1E7, + 0xB1E9, 0xB203, + 0xB205, 0xB21F, + 0xB221, 0xB23B, + 0xB23D, 0xB257, + 0xB259, 0xB273, + 0xB275, 0xB28F, + 0xB291, 0xB2AB, + 0xB2AD, 0xB2C7, + 0xB2C9, 0xB2E3, + 0xB2E5, 0xB2FF, + 0xB301, 0xB31B, + 0xB31D, 0xB337, + 0xB339, 0xB353, + 0xB355, 0xB36F, + 0xB371, 0xB38B, + 0xB38D, 0xB3A7, + 0xB3A9, 0xB3C3, + 0xB3C5, 0xB3DF, + 0xB3E1, 0xB3FB, + 0xB3FD, 0xB417, + 0xB419, 0xB433, + 0xB435, 0xB44F, + 0xB451, 0xB46B, + 0xB46D, 0xB487, + 0xB489, 0xB4A3, + 0xB4A5, 0xB4BF, + 0xB4C1, 0xB4DB, + 0xB4DD, 0xB4F7, + 0xB4F9, 0xB513, + 0xB515, 0xB52F, + 0xB531, 0xB54B, + 0xB54D, 0xB567, + 0xB569, 0xB583, + 0xB585, 0xB59F, + 0xB5A1, 0xB5BB, + 0xB5BD, 0xB5D7, + 0xB5D9, 0xB5F3, + 0xB5F5, 0xB60F, + 0xB611, 0xB62B, + 0xB62D, 0xB647, + 0xB649, 0xB663, + 0xB665, 0xB67F, + 0xB681, 0xB69B, + 0xB69D, 0xB6B7, + 0xB6B9, 0xB6D3, + 0xB6D5, 0xB6EF, + 0xB6F1, 0xB70B, + 0xB70D, 0xB727, + 0xB729, 0xB743, + 0xB745, 0xB75F, + 0xB761, 0xB77B, + 0xB77D, 0xB797, + 0xB799, 0xB7B3, + 0xB7B5, 0xB7CF, + 0xB7D1, 0xB7EB, + 0xB7ED, 0xB807, + 0xB809, 0xB823, + 0xB825, 0xB83F, + 0xB841, 0xB85B, + 0xB85D, 0xB877, + 0xB879, 0xB893, + 0xB895, 0xB8AF, + 0xB8B1, 0xB8CB, + 0xB8CD, 0xB8E7, + 0xB8E9, 0xB903, + 0xB905, 0xB91F, + 0xB921, 0xB93B, + 0xB93D, 0xB957, + 0xB959, 0xB973, + 0xB975, 0xB98F, + 0xB991, 0xB9AB, + 0xB9AD, 0xB9C7, + 0xB9C9, 0xB9E3, + 0xB9E5, 0xB9FF, + 0xBA01, 0xBA1B, + 0xBA1D, 0xBA37, + 0xBA39, 0xBA53, + 0xBA55, 0xBA6F, + 0xBA71, 0xBA8B, + 0xBA8D, 0xBAA7, + 0xBAA9, 0xBAC3, + 0xBAC5, 0xBADF, + 0xBAE1, 0xBAFB, + 0xBAFD, 0xBB17, + 0xBB19, 0xBB33, + 0xBB35, 0xBB4F, + 0xBB51, 0xBB6B, + 0xBB6D, 0xBB87, + 0xBB89, 0xBBA3, + 0xBBA5, 0xBBBF, + 0xBBC1, 0xBBDB, + 0xBBDD, 0xBBF7, + 0xBBF9, 0xBC13, + 0xBC15, 0xBC2F, + 0xBC31, 0xBC4B, + 0xBC4D, 0xBC67, + 0xBC69, 0xBC83, + 0xBC85, 0xBC9F, + 0xBCA1, 0xBCBB, + 0xBCBD, 0xBCD7, + 0xBCD9, 0xBCF3, + 0xBCF5, 0xBD0F, + 0xBD11, 0xBD2B, + 0xBD2D, 0xBD47, + 0xBD49, 0xBD63, + 0xBD65, 0xBD7F, + 0xBD81, 0xBD9B, + 0xBD9D, 0xBDB7, + 0xBDB9, 0xBDD3, + 0xBDD5, 0xBDEF, + 0xBDF1, 0xBE0B, + 0xBE0D, 0xBE27, + 0xBE29, 0xBE43, + 0xBE45, 0xBE5F, + 0xBE61, 0xBE7B, + 0xBE7D, 0xBE97, + 0xBE99, 0xBEB3, + 0xBEB5, 0xBECF, + 0xBED1, 0xBEEB, + 0xBEED, 0xBF07, + 0xBF09, 0xBF23, + 0xBF25, 0xBF3F, + 0xBF41, 0xBF5B, + 0xBF5D, 0xBF77, + 0xBF79, 0xBF93, + 0xBF95, 0xBFAF, + 0xBFB1, 0xBFCB, + 0xBFCD, 0xBFE7, + 0xBFE9, 0xC003, + 0xC005, 0xC01F, + 0xC021, 0xC03B, + 0xC03D, 0xC057, + 0xC059, 0xC073, + 0xC075, 0xC08F, + 0xC091, 0xC0AB, + 0xC0AD, 0xC0C7, + 0xC0C9, 0xC0E3, + 0xC0E5, 0xC0FF, + 0xC101, 0xC11B, + 0xC11D, 0xC137, + 0xC139, 0xC153, + 0xC155, 0xC16F, + 0xC171, 0xC18B, + 0xC18D, 0xC1A7, + 0xC1A9, 0xC1C3, + 0xC1C5, 0xC1DF, + 0xC1E1, 0xC1FB, + 0xC1FD, 0xC217, + 0xC219, 0xC233, + 0xC235, 0xC24F, + 0xC251, 0xC26B, + 0xC26D, 0xC287, + 0xC289, 0xC2A3, + 0xC2A5, 0xC2BF, + 0xC2C1, 0xC2DB, + 0xC2DD, 0xC2F7, + 0xC2F9, 0xC313, + 0xC315, 0xC32F, + 0xC331, 0xC34B, + 0xC34D, 0xC367, + 0xC369, 0xC383, + 0xC385, 0xC39F, + 0xC3A1, 0xC3BB, + 0xC3BD, 0xC3D7, + 0xC3D9, 0xC3F3, + 0xC3F5, 0xC40F, + 0xC411, 0xC42B, + 0xC42D, 0xC447, + 0xC449, 0xC463, + 0xC465, 0xC47F, + 0xC481, 0xC49B, + 0xC49D, 0xC4B7, + 0xC4B9, 0xC4D3, + 0xC4D5, 0xC4EF, + 0xC4F1, 0xC50B, + 0xC50D, 0xC527, + 0xC529, 0xC543, + 0xC545, 0xC55F, + 0xC561, 0xC57B, + 0xC57D, 0xC597, + 0xC599, 0xC5B3, + 0xC5B5, 0xC5CF, + 0xC5D1, 0xC5EB, + 0xC5ED, 0xC607, + 0xC609, 0xC623, + 0xC625, 0xC63F, + 0xC641, 0xC65B, + 0xC65D, 0xC677, + 0xC679, 0xC693, + 0xC695, 0xC6AF, + 0xC6B1, 0xC6CB, + 0xC6CD, 0xC6E7, + 0xC6E9, 0xC703, + 0xC705, 0xC71F, + 0xC721, 0xC73B, + 0xC73D, 0xC757, + 0xC759, 0xC773, + 0xC775, 0xC78F, + 0xC791, 0xC7AB, + 0xC7AD, 0xC7C7, + 0xC7C9, 0xC7E3, + 0xC7E5, 0xC7FF, + 0xC801, 0xC81B, + 0xC81D, 0xC837, + 0xC839, 0xC853, + 0xC855, 0xC86F, + 0xC871, 0xC88B, + 0xC88D, 0xC8A7, + 0xC8A9, 0xC8C3, + 0xC8C5, 0xC8DF, + 0xC8E1, 0xC8FB, + 0xC8FD, 0xC917, + 0xC919, 0xC933, + 0xC935, 0xC94F, + 0xC951, 0xC96B, + 0xC96D, 0xC987, + 0xC989, 0xC9A3, + 0xC9A5, 0xC9BF, + 0xC9C1, 0xC9DB, + 0xC9DD, 0xC9F7, + 0xC9F9, 0xCA13, + 0xCA15, 0xCA2F, + 0xCA31, 0xCA4B, + 0xCA4D, 0xCA67, + 0xCA69, 0xCA83, + 0xCA85, 0xCA9F, + 0xCAA1, 0xCABB, + 0xCABD, 0xCAD7, + 0xCAD9, 0xCAF3, + 0xCAF5, 0xCB0F, + 0xCB11, 0xCB2B, + 0xCB2D, 0xCB47, + 0xCB49, 0xCB63, + 0xCB65, 0xCB7F, + 0xCB81, 0xCB9B, + 0xCB9D, 0xCBB7, + 0xCBB9, 0xCBD3, + 0xCBD5, 0xCBEF, + 0xCBF1, 0xCC0B, + 0xCC0D, 0xCC27, + 0xCC29, 0xCC43, + 0xCC45, 0xCC5F, + 0xCC61, 0xCC7B, + 0xCC7D, 0xCC97, + 0xCC99, 0xCCB3, + 0xCCB5, 0xCCCF, + 0xCCD1, 0xCCEB, + 0xCCED, 0xCD07, + 0xCD09, 0xCD23, + 0xCD25, 0xCD3F, + 0xCD41, 0xCD5B, + 0xCD5D, 0xCD77, + 0xCD79, 0xCD93, + 0xCD95, 0xCDAF, + 0xCDB1, 0xCDCB, + 0xCDCD, 0xCDE7, + 0xCDE9, 0xCE03, + 0xCE05, 0xCE1F, + 0xCE21, 0xCE3B, + 0xCE3D, 0xCE57, + 0xCE59, 0xCE73, + 0xCE75, 0xCE8F, + 0xCE91, 0xCEAB, + 0xCEAD, 0xCEC7, + 0xCEC9, 0xCEE3, + 0xCEE5, 0xCEFF, + 0xCF01, 0xCF1B, + 0xCF1D, 0xCF37, + 0xCF39, 0xCF53, + 0xCF55, 0xCF6F, + 0xCF71, 0xCF8B, + 0xCF8D, 0xCFA7, + 0xCFA9, 0xCFC3, + 0xCFC5, 0xCFDF, + 0xCFE1, 0xCFFB, + 0xCFFD, 0xD017, + 0xD019, 0xD033, + 0xD035, 0xD04F, + 0xD051, 0xD06B, + 0xD06D, 0xD087, + 0xD089, 0xD0A3, + 0xD0A5, 0xD0BF, + 0xD0C1, 0xD0DB, + 0xD0DD, 0xD0F7, + 0xD0F9, 0xD113, + 0xD115, 0xD12F, + 0xD131, 0xD14B, + 0xD14D, 0xD167, + 0xD169, 0xD183, + 0xD185, 0xD19F, + 0xD1A1, 0xD1BB, + 0xD1BD, 0xD1D7, + 0xD1D9, 0xD1F3, + 0xD1F5, 0xD20F, + 0xD211, 0xD22B, + 0xD22D, 0xD247, + 0xD249, 0xD263, + 0xD265, 0xD27F, + 0xD281, 0xD29B, + 0xD29D, 0xD2B7, + 0xD2B9, 0xD2D3, + 0xD2D5, 0xD2EF, + 0xD2F1, 0xD30B, + 0xD30D, 0xD327, + 0xD329, 0xD343, + 0xD345, 0xD35F, + 0xD361, 0xD37B, + 0xD37D, 0xD397, + 0xD399, 0xD3B3, + 0xD3B5, 0xD3CF, + 0xD3D1, 0xD3EB, + 0xD3ED, 0xD407, + 0xD409, 0xD423, + 0xD425, 0xD43F, + 0xD441, 0xD45B, + 0xD45D, 0xD477, + 0xD479, 0xD493, + 0xD495, 0xD4AF, + 0xD4B1, 0xD4CB, + 0xD4CD, 0xD4E7, + 0xD4E9, 0xD503, + 0xD505, 0xD51F, + 0xD521, 0xD53B, + 0xD53D, 0xD557, + 0xD559, 0xD573, + 0xD575, 0xD58F, + 0xD591, 0xD5AB, + 0xD5AD, 0xD5C7, + 0xD5C9, 0xD5E3, + 0xD5E5, 0xD5FF, + 0xD601, 0xD61B, + 0xD61D, 0xD637, + 0xD639, 0xD653, + 0xD655, 0xD66F, + 0xD671, 0xD68B, + 0xD68D, 0xD6A7, + 0xD6A9, 0xD6C3, + 0xD6C5, 0xD6DF, + 0xD6E1, 0xD6FB, + 0xD6FD, 0xD717, + 0xD719, 0xD733, + 0xD735, 0xD74F, + 0xD751, 0xD76B, + 0xD76D, 0xD787, + 0xD789, 0xD7A3, +} + +@(rodata) +indic_conjunct_break_consonant_ranges := [?]i32 { + 0x0915, 0x0939, + 0x0958, 0x095F, + 0x0978, 0x097F, + 0x0995, 0x09A8, + 0x09AA, 0x09B0, + 0x09B2, 0x09B2, + 0x09B6, 0x09B9, + 0x09DC, 0x09DD, + 0x09DF, 0x09DF, + 0x09F0, 0x09F1, + 0x0A95, 0x0AA8, + 0x0AAA, 0x0AB0, + 0x0AB2, 0x0AB3, + 0x0AB5, 0x0AB9, + 0x0AF9, 0x0AF9, + 0x0B15, 0x0B28, + 0x0B2A, 0x0B30, + 0x0B32, 0x0B33, + 0x0B35, 0x0B39, + 0x0B5C, 0x0B5D, + 0x0B5F, 0x0B5F, + 0x0B71, 0x0B71, + 0x0C15, 0x0C28, + 0x0C2A, 0x0C39, + 0x0C58, 0x0C5A, + 0x0D15, 0x0D3A, +} + +@(rodata) +indic_conjunct_break_extend_ranges := [?]i32 { + 0x0300, 0x034E, + 0x0350, 0x036F, + 0x0483, 0x0487, + 0x0591, 0x05BD, + 0x05BF, 0x05BF, + 0x05C1, 0x05C2, + 0x05C4, 0x05C5, + 0x05C7, 0x05C7, + 0x0610, 0x061A, + 0x064B, 0x065F, + 0x0670, 0x0670, + 0x06D6, 0x06DC, + 0x06DF, 0x06E4, + 0x06E7, 0x06E8, + 0x06EA, 0x06ED, + 0x0711, 0x0711, + 0x0730, 0x074A, + 0x07EB, 0x07F3, + 0x07FD, 0x07FD, + 0x0816, 0x0819, + 0x081B, 0x0823, + 0x0825, 0x0827, + 0x0829, 0x082D, + 0x0859, 0x085B, + 0x0898, 0x089F, + 0x08CA, 0x08E1, + 0x08E3, 0x08FF, + 0x093C, 0x093C, + 0x0951, 0x0954, + 0x09BC, 0x09BC, + 0x09FE, 0x09FE, + 0x0A3C, 0x0A3C, + 0x0ABC, 0x0ABC, + 0x0B3C, 0x0B3C, + 0x0C3C, 0x0C3C, + 0x0C55, 0x0C56, + 0x0CBC, 0x0CBC, + 0x0D3B, 0x0D3C, + 0x0E38, 0x0E3A, + 0x0E48, 0x0E4B, + 0x0EB8, 0x0EBA, + 0x0EC8, 0x0ECB, + 0x0F18, 0x0F19, + 0x0F35, 0x0F35, + 0x0F37, 0x0F37, + 0x0F39, 0x0F39, + 0x0F71, 0x0F72, + 0x0F74, 0x0F74, + 0x0F7A, 0x0F7D, + 0x0F80, 0x0F80, + 0x0F82, 0x0F84, + 0x0F86, 0x0F87, + 0x0FC6, 0x0FC6, + 0x1037, 0x1037, + 0x1039, 0x103A, + 0x108D, 0x108D, + 0x135D, 0x135F, + 0x1714, 0x1714, + 0x17D2, 0x17D2, + 0x17DD, 0x17DD, + 0x18A9, 0x18A9, + 0x1939, 0x193B, + 0x1A17, 0x1A18, + 0x1A60, 0x1A60, + 0x1A75, 0x1A7C, + 0x1A7F, 0x1A7F, + 0x1AB0, 0x1ABD, + 0x1ABF, 0x1ACE, + 0x1B34, 0x1B34, + 0x1B6B, 0x1B73, + 0x1BAB, 0x1BAB, + 0x1BE6, 0x1BE6, + 0x1C37, 0x1C37, + 0x1CD0, 0x1CD2, + 0x1CD4, 0x1CE0, + 0x1CE2, 0x1CE8, + 0x1CED, 0x1CED, + 0x1CF4, 0x1CF4, + 0x1CF8, 0x1CF9, + 0x1DC0, 0x1DFF, + 0x200D, 0x200D, + 0x20D0, 0x20DC, + 0x20E1, 0x20E1, + 0x20E5, 0x20F0, + 0x2CEF, 0x2CF1, + 0x2D7F, 0x2D7F, + 0x2DE0, 0x2DFF, + 0x302A, 0x302D, + 0x302E, 0x302F, + 0x3099, 0x309A, + 0xA66F, 0xA66F, + 0xA674, 0xA67D, + 0xA69E, 0xA69F, + 0xA6F0, 0xA6F1, + 0xA82C, 0xA82C, + 0xA8E0, 0xA8F1, + 0xA92B, 0xA92D, + 0xA9B3, 0xA9B3, + 0xAAB0, 0xAAB0, + 0xAAB2, 0xAAB4, + 0xAAB7, 0xAAB8, + 0xAABE, 0xAABF, + 0xAAC1, 0xAAC1, + 0xAAF6, 0xAAF6, + 0xABED, 0xABED, + 0xFB1E, 0xFB1E, + 0xFE20, 0xFE2F, + 0x101FD, 0x101FD, + 0x102E0, 0x102E0, + 0x10376, 0x1037A, + 0x10A0D, 0x10A0D, + 0x10A0F, 0x10A0F, + 0x10A38, 0x10A3A, + 0x10A3F, 0x10A3F, + 0x10AE5, 0x10AE6, + 0x10D24, 0x10D27, + 0x10EAB, 0x10EAC, + 0x10EFD, 0x10EFF, + 0x10F46, 0x10F50, + 0x10F82, 0x10F85, + 0x11070, 0x11070, + 0x1107F, 0x1107F, + 0x110BA, 0x110BA, + 0x11100, 0x11102, + 0x11133, 0x11134, + 0x11173, 0x11173, + 0x111CA, 0x111CA, + 0x11236, 0x11236, + 0x112E9, 0x112EA, + 0x1133B, 0x1133C, + 0x11366, 0x1136C, + 0x11370, 0x11374, + 0x11446, 0x11446, + 0x1145E, 0x1145E, + 0x114C3, 0x114C3, + 0x115C0, 0x115C0, + 0x116B7, 0x116B7, + 0x1172B, 0x1172B, + 0x1183A, 0x1183A, + 0x1193E, 0x1193E, + 0x11943, 0x11943, + 0x11A34, 0x11A34, + 0x11A47, 0x11A47, + 0x11A99, 0x11A99, + 0x11D42, 0x11D42, + 0x11D44, 0x11D45, + 0x11D97, 0x11D97, + 0x11F42, 0x11F42, + 0x16AF0, 0x16AF4, + 0x16B30, 0x16B36, + 0x1BC9E, 0x1BC9E, + 0x1D165, 0x1D165, + 0x1D167, 0x1D169, + 0x1D16E, 0x1D172, + 0x1D17B, 0x1D182, + 0x1D185, 0x1D18B, + 0x1D1AA, 0x1D1AD, + 0x1D242, 0x1D244, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E08F, 0x1E08F, + 0x1E130, 0x1E136, + 0x1E2AE, 0x1E2AE, + 0x1E2EC, 0x1E2EF, + 0x1E4EC, 0x1E4EF, + 0x1E8D0, 0x1E8D6, + 0x1E944, 0x1E94A, +} + +// Fullwidth (F) and Wide (W) are counted as 2. +// Everything else is 1. +// +// Derived from: https://unicode.org/Public/15.1.0/ucd/EastAsianWidth.txt +@(rodata) +normalized_east_asian_width_ranges := [?]i32 { + 0x0000, 0x10FF, 1, + 0x1100, 0x115F, 2, + 0x1160, 0x2319, 1, + 0x231A, 0x231B, 2, + 0x231C, 0x2328, 1, + 0x2329, 0x232A, 2, + 0x232B, 0x23E8, 1, + 0x23E9, 0x23EC, 2, + 0x23ED, 0x23EF, 1, + 0x23F0, 0x23F0, 2, + 0x23F1, 0x23F2, 1, + 0x23F3, 0x23F3, 2, + 0x23F4, 0x25FC, 1, + 0x25FD, 0x25FE, 2, + 0x25FF, 0x2613, 1, + 0x2614, 0x2615, 2, + 0x2616, 0x2647, 1, + 0x2648, 0x2653, 2, + 0x2654, 0x267E, 1, + 0x267F, 0x267F, 2, + 0x2680, 0x2692, 1, + 0x2693, 0x2693, 2, + 0x2694, 0x26A0, 1, + 0x26A1, 0x26A1, 2, + 0x26A2, 0x26A9, 1, + 0x26AA, 0x26AB, 2, + 0x26AC, 0x26BC, 1, + 0x26BD, 0x26BE, 2, + 0x26BF, 0x26C3, 1, + 0x26C4, 0x26C5, 2, + 0x26C6, 0x26CD, 1, + 0x26CE, 0x26CE, 2, + 0x26CF, 0x26D3, 1, + 0x26D4, 0x26D4, 2, + 0x26D5, 0x26E9, 1, + 0x26EA, 0x26EA, 2, + 0x26EB, 0x26F1, 1, + 0x26F2, 0x26F3, 2, + 0x26F4, 0x26F4, 1, + 0x26F5, 0x26F5, 2, + 0x26F6, 0x26F9, 1, + 0x26FA, 0x26FA, 2, + 0x26FB, 0x26FC, 1, + 0x26FD, 0x26FD, 2, + 0x26FE, 0x2704, 1, + 0x2705, 0x2705, 2, + 0x2706, 0x2709, 1, + 0x270A, 0x270B, 2, + 0x270C, 0x2727, 1, + 0x2728, 0x2728, 2, + 0x2729, 0x274B, 1, + 0x274C, 0x274C, 2, + 0x274D, 0x274D, 1, + 0x274E, 0x274E, 2, + 0x274F, 0x2752, 1, + 0x2753, 0x2755, 2, + 0x2756, 0x2756, 1, + 0x2757, 0x2757, 2, + 0x2758, 0x2794, 1, + 0x2795, 0x2797, 2, + 0x2798, 0x27AF, 1, + 0x27B0, 0x27B0, 2, + 0x27B1, 0x27BE, 1, + 0x27BF, 0x27BF, 2, + 0x27C0, 0x2B1A, 1, + 0x2B1B, 0x2B1C, 2, + 0x2B1D, 0x2B4F, 1, + 0x2B50, 0x2B50, 2, + 0x2B51, 0x2B54, 1, + 0x2B55, 0x2B55, 2, + 0x2B56, 0x2E5D, 1, + 0x2E80, 0x303E, 2, + 0x303F, 0x303F, 1, + 0x3041, 0x3247, 2, + 0x3248, 0x324F, 1, + 0x3250, 0x4DBF, 2, + 0x4DC0, 0x4DFF, 1, + 0x4E00, 0xA4C6, 2, + 0xA4D0, 0xA95F, 1, + 0xA960, 0xA97C, 2, + 0xA980, 0xABF9, 1, + 0xAC00, 0xD7A3, 2, + 0xD7B0, 0xF8FF, 1, + 0xF900, 0xFAFF, 2, + 0xFB00, 0xFE0F, 1, + 0xFE10, 0xFE19, 2, + 0xFE20, 0xFE2F, 1, + 0xFE30, 0xFE6B, 2, + 0xFE70, 0xFEFF, 1, + 0xFF01, 0xFF60, 2, + 0xFF61, 0xFFDC, 1, + 0xFFE0, 0xFFE6, 2, + 0xFFE8, 0x16F9F, 1, + 0x16FE0, 0x1B2FB, 2, + 0x1BC00, 0x1F003, 1, + 0x1F004, 0x1F004, 2, + 0x1F005, 0x1F0CE, 1, + 0x1F0CF, 0x1F0CF, 2, + 0x1F0D1, 0x1F18D, 1, + 0x1F18E, 0x1F18E, 2, + 0x1F18F, 0x1F190, 1, + 0x1F191, 0x1F19A, 2, + 0x1F19B, 0x1F1FF, 1, + 0x1F200, 0x1F320, 2, + 0x1F321, 0x1F32C, 1, + 0x1F32D, 0x1F335, 2, + 0x1F336, 0x1F336, 1, + 0x1F337, 0x1F37C, 2, + 0x1F37D, 0x1F37D, 1, + 0x1F37E, 0x1F393, 2, + 0x1F394, 0x1F39F, 1, + 0x1F3A0, 0x1F3CA, 2, + 0x1F3CB, 0x1F3CE, 1, + 0x1F3CF, 0x1F3D3, 2, + 0x1F3D4, 0x1F3DF, 1, + 0x1F3E0, 0x1F3F0, 2, + 0x1F3F1, 0x1F3F3, 1, + 0x1F3F4, 0x1F3F4, 2, + 0x1F3F5, 0x1F3F7, 1, + 0x1F3F8, 0x1F43E, 2, + 0x1F43F, 0x1F43F, 1, + 0x1F440, 0x1F440, 2, + 0x1F441, 0x1F441, 1, + 0x1F442, 0x1F4FC, 2, + 0x1F4FD, 0x1F4FE, 1, + 0x1F4FF, 0x1F53D, 2, + 0x1F53E, 0x1F54A, 1, + 0x1F54B, 0x1F54E, 2, + 0x1F54F, 0x1F54F, 1, + 0x1F550, 0x1F567, 2, + 0x1F568, 0x1F579, 1, + 0x1F57A, 0x1F57A, 2, + 0x1F57B, 0x1F594, 1, + 0x1F595, 0x1F596, 2, + 0x1F597, 0x1F5A3, 1, + 0x1F5A4, 0x1F5A4, 2, + 0x1F5A5, 0x1F5FA, 1, + 0x1F5FB, 0x1F64F, 2, + 0x1F650, 0x1F67F, 1, + 0x1F680, 0x1F6C5, 2, + 0x1F6C6, 0x1F6CB, 1, + 0x1F6CC, 0x1F6CC, 2, + 0x1F6CD, 0x1F6CF, 1, + 0x1F6D0, 0x1F6D2, 2, + 0x1F6D3, 0x1F6D4, 1, + 0x1F6D5, 0x1F6DF, 2, + 0x1F6E0, 0x1F6EA, 1, + 0x1F6EB, 0x1F6EC, 2, + 0x1F6F0, 0x1F6F3, 1, + 0x1F6F4, 0x1F6FC, 2, + 0x1F700, 0x1F7D9, 1, + 0x1F7E0, 0x1F7F0, 2, + 0x1F800, 0x1F90B, 1, + 0x1F90C, 0x1F93A, 2, + 0x1F93B, 0x1F93B, 1, + 0x1F93C, 0x1F945, 2, + 0x1F946, 0x1F946, 1, + 0x1F947, 0x1F9FF, 2, + 0x1FA00, 0x1FA6D, 1, + 0x1FA70, 0x1FAF8, 2, + 0x1FB00, 0x1FBF9, 1, + 0x20000, 0x3FFFD, 2, + 0xE0001, 0x10FFFD, 1, +} + +// +// End of Unicode 15.1.0 block. +// diff --git a/core/unicode/utf8/grapheme.odin b/core/unicode/utf8/grapheme.odin new file mode 100644 index 000000000..911165af9 --- /dev/null +++ b/core/unicode/utf8/grapheme.odin @@ -0,0 +1,420 @@ +package utf8 + +import "core:unicode" + +ZERO_WIDTH_JOINER :: unicode.ZERO_WIDTH_JOINER +is_control :: unicode.is_control +is_hangul_syllable_leading :: unicode.is_hangul_syllable_leading +is_hangul_syllable_vowel :: unicode.is_hangul_syllable_vowel +is_hangul_syllable_trailing :: unicode.is_hangul_syllable_trailing +is_hangul_syllable_lv :: unicode.is_hangul_syllable_lv +is_hangul_syllable_lvt :: unicode.is_hangul_syllable_lvt +is_indic_conjunct_break_extend :: unicode.is_indic_conjunct_break_extend +is_indic_conjunct_break_linker :: unicode.is_indic_conjunct_break_linker +is_indic_conjunct_break_consonant :: unicode.is_indic_conjunct_break_consonant +is_gcb_extend_class :: unicode.is_gcb_extend_class +is_spacing_mark :: unicode.is_spacing_mark +is_gcb_prepend_class :: unicode.is_gcb_prepend_class +is_emoji_extended_pictographic :: unicode.is_emoji_extended_pictographic +is_regional_indicator :: unicode.is_regional_indicator +normalized_east_asian_width :: unicode.normalized_east_asian_width + + +Grapheme :: struct { + byte_index: int, + rune_index: int, + width: int, +} + +/* +Count the individual graphemes in a UTF-8 string. + +Inputs: +- str: The input string. + +Returns: +- graphemes: The number of graphemes in the string. +- runes: The number of runes in the string. +- width: The width of the string in number of monospace cells. +*/ +@(require_results) +grapheme_count :: proc(str: string) -> (graphemes, runes, width: int) { + _, graphemes, runes, width = decode_grapheme_clusters(str, false) + return +} + +/* +Decode the individual graphemes in a UTF-8 string. + +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string. +- track_graphemes: Whether or not to allocate and return `graphemes` with extra data about each grapheme. +- allocator: (default: context.allocator) + +Returns: +- graphemes: Extra data about each grapheme. +- grapheme_count: The number of graphemes in the string. +- rune_count: The number of runes in the string. +- width: The width of the string in number of monospace cells. +*/ +@(require_results) +decode_grapheme_clusters :: proc( + str: string, + track_graphemes := true, + allocator := context.allocator, +) -> ( + graphemes: [dynamic]Grapheme, + grapheme_count: int, + rune_count: int, + width: int, +) { + // The following procedure implements text segmentation by breaking on + // Grapheme Cluster Boundaries[1], using the values[2] and rules[3] from + // the Unicode® Standard Annex #29, entitled: + // + // UNICODE TEXT SEGMENTATION + // + // Version: Unicode 15.1.0 + // Date: 2023-08-16 + // Revision: 43 + // + // This procedure is conformant[4] to UAX29-C1-1, otherwise known as the + // extended, non-legacy ruleset. + // + // Please see the references below for more information. + // + // + // NOTE(Feoramund): This procedure has not been highly optimized. + // A couple opportunities were taken to bypass repeated checking when a + // rune is outside of certain codepoint ranges, but little else has been + // done. Standard switches, conditionals, and binary search are used to + // see if a rune fits into a certain category. + // + // I did find that only one prior rune of state was necessary to build an + // algorithm that successfully passes all 4,835 test cases provided with + // this implementation from the Unicode organization's website. + // + // My initial implementation tracked explicit breaks and counted them once + // the string iteration had terminated. I've found this current + // implementation to be far simpler and need no allocations (unless the + // caller wants position data). + // + // Most rules work backwards instead of forwards which has helped keep this + // simple, despite its length and verbosity. + // + // + // The implementation has been left verbose and in the order described by + // the specification, to enable better readability and future upkeep. + // + // Some possible optimizations might include: + // + // - saving the type of `last_rune` instead of the exact rune. + // - reordering rules. + // - combining tables. + // + // + // [1]: https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries + // [2]: https://www.unicode.org/reports/tr29/#Default_Grapheme_Cluster_Table + // [3]: https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules + // [4]: https://www.unicode.org/reports/tr29/#Conformance + + // Additionally, this procedure now takes into account Standard Annex #11, + // in order to estimate how visually wide the string will appear on a + // monospaced display. This can only ever be a rough guess, as this tends + // to be an implementation detail relating to which fonts are being used, + // how codepoints are interpreted and drawn, if codepoint sequences are + // interpreted correctly, and et cetera. + // + // For example, a program may not properly interpret an emoji modifier + // sequence and print the component glyphs instead of one whole glyph. + // + // See here for more information: https://www.unicode.org/reports/tr11/ + // + // NOTE: There is no explicit mention of what to do with zero-width spaces + // as far as grapheme cluster segmentation goes, therefore this + // implementation may count and return graphemes with a `width` of zero. + // + // Treat them as any other space. + + Grapheme_Cluster_Sequence :: enum { + None, + Indic, + Emoji, + Regional, + } + + context.allocator = allocator + + last_rune: rune + last_rune_breaks_forward: bool + + last_width: int + last_grapheme_count: int + + bypass_next_rune: bool + + regional_indicator_counter: int + + current_sequence: Grapheme_Cluster_Sequence + continue_sequence: bool + + for this_rune, byte_index in str { + defer { + // "Break at the start and end of text, unless the text is empty." + // + // GB1: sot ÷ Any + // GB2: Any ÷ eot + if rune_count == 0 && grapheme_count == 0 { + grapheme_count += 1 + } + + if grapheme_count > last_grapheme_count { + width += normalized_east_asian_width(this_rune) + if track_graphemes { + append(&graphemes, Grapheme{ + byte_index, + rune_count, + width - last_width, + }) + } + last_grapheme_count = grapheme_count + last_width = width + } + + last_rune = this_rune + rune_count += 1 + + if !continue_sequence { + current_sequence = .None + regional_indicator_counter = 0 + } + continue_sequence = false + } + + // "Do not break between a CR and LF. Otherwise, break before and after controls." + // + // GB3: CR × LF + // GB4: (Control | CR | LF) ÷ + // GB5: ÷ (Control | CR | LF) + if this_rune == '\n' && last_rune == '\r' { + last_rune_breaks_forward = false + bypass_next_rune = false + continue + } + + if is_control(this_rune) { + grapheme_count += 1 + last_rune_breaks_forward = true + bypass_next_rune = true + continue + } + + // (This check is for rules that work forwards, instead of backwards.) + if bypass_next_rune { + if last_rune_breaks_forward { + grapheme_count += 1 + last_rune_breaks_forward = false + } + + bypass_next_rune = false + continue + } + + // (Optimization 1: Prevent low runes from proceeding further.) + // + // * 0xA9 and 0xAE are in the Extended_Pictographic range, + // which is checked later in GB11. + if this_rune != 0xA9 && this_rune != 0xAE && this_rune <= 0x2FF { + grapheme_count += 1 + continue + } + + // (Optimization 2: Check if the rune is in the Hangul space before getting specific.) + if 0x1100 <= this_rune && this_rune <= 0xD7FB { + // "Do not break Hangul syllable sequences." + // + // GB6: L × (L | V | LV | LVT) + // GB7: (LV | V) × (V | T) + // GB8: (LVT | T) × T + if is_hangul_syllable_leading(this_rune) || + is_hangul_syllable_lv(this_rune) || + is_hangul_syllable_lvt(this_rune) + { + if !is_hangul_syllable_leading(last_rune) { + grapheme_count += 1 + } + continue + } + + if is_hangul_syllable_vowel(this_rune) { + if is_hangul_syllable_leading(last_rune) || + is_hangul_syllable_vowel(last_rune) || + is_hangul_syllable_lv(last_rune) + { + continue + } + grapheme_count += 1 + continue + } + + if is_hangul_syllable_trailing(this_rune) { + if is_hangul_syllable_trailing(last_rune) || + is_hangul_syllable_lvt(last_rune) || + is_hangul_syllable_lv(last_rune) || + is_hangul_syllable_vowel(last_rune) + { + continue + } + grapheme_count += 1 + continue + } + } + + // "Do not break before extending characters or ZWJ." + // + // GB9: × (Extend | ZWJ) + if this_rune == ZERO_WIDTH_JOINER { + continue_sequence = true + continue + } + + if is_gcb_extend_class(this_rune) { + // (Support for GB9c.) + if current_sequence == .Indic { + if is_indic_conjunct_break_extend(this_rune) && ( + is_indic_conjunct_break_linker(last_rune) || + is_indic_conjunct_break_consonant(last_rune) ) + { + continue_sequence = true + continue + } + + if is_indic_conjunct_break_linker(this_rune) && ( + is_indic_conjunct_break_linker(last_rune) || + is_indic_conjunct_break_extend(last_rune) || + is_indic_conjunct_break_consonant(last_rune) ) + { + continue_sequence = true + continue + } + + continue + } + + // (Support for GB11.) + if current_sequence == .Emoji && ( + is_gcb_extend_class(last_rune) || + is_emoji_extended_pictographic(last_rune) ) + { + continue_sequence = true + } + + continue + } + + // _The GB9a and GB9b rules only apply to extended grapheme clusters:_ + // "Do not break before SpacingMarks, or after Prepend characters." + // + // GB9a: × SpacingMark + // GB9b: Prepend × + if is_spacing_mark(this_rune) { + continue + } + + if is_gcb_prepend_class(this_rune) { + grapheme_count += 1 + bypass_next_rune = true + continue + } + + // _The GB9c rule only applies to extended grapheme clusters:_ + // "Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker." + // + // GB9c: \p{InCB=Consonant} [ \p{InCB=Extend} \p{InCB=Linker} ]* \p{InCB=Linker} [ \p{InCB=Extend} \p{InCB=Linker} ]* × \p{InCB=Consonant} + if is_indic_conjunct_break_consonant(this_rune) { + if current_sequence == .Indic { + if last_rune == ZERO_WIDTH_JOINER || + is_indic_conjunct_break_linker(last_rune) + { + continue_sequence = true + } else { + grapheme_count += 1 + } + } else { + grapheme_count += 1 + current_sequence = .Indic + continue_sequence = true + } + continue + } + + if is_indic_conjunct_break_extend(this_rune) { + if current_sequence == .Indic { + if is_indic_conjunct_break_consonant(last_rune) || + is_indic_conjunct_break_linker(last_rune) + { + continue_sequence = true + } else { + grapheme_count += 1 + } + } + continue + } + + if is_indic_conjunct_break_linker(this_rune) { + if current_sequence == .Indic { + if is_indic_conjunct_break_extend(last_rune) || + is_indic_conjunct_break_linker(last_rune) + { + continue_sequence = true + } else { + grapheme_count += 1 + } + } + continue + } + + // + // (Curiously, there is no GB10.) + // + + // "Do not break within emoji modifier sequences or emoji zwj sequences." + // + // GB11: \p{Extended_Pictographic} Extend* ZWJ × \p{Extended_Pictographic} + if is_emoji_extended_pictographic(this_rune) { + if current_sequence != .Emoji || last_rune != ZERO_WIDTH_JOINER { + grapheme_count += 1 + } + current_sequence = .Emoji + continue_sequence = true + continue + } + + // "Do not break within emoji flag sequences. + // That is, do not break between regional indicator (RI) symbols + // if there is an odd number of RI characters before the break point." + // + // GB12: sot (RI RI)* RI × RI + // GB13: [^RI] (RI RI)* RI × RI + if is_regional_indicator(this_rune) { + if regional_indicator_counter & 1 == 0 { + grapheme_count += 1 + } + + current_sequence = .Regional + continue_sequence = true + regional_indicator_counter += 1 + + continue + } + + // "Otherwise, break everywhere." + // + // GB999: Any ÷ Any + grapheme_count += 1 + } + + return +} diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 6c3972987..5202d72be 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -121,6 +121,7 @@ import edit "core:text/edit" import thread "core:thread" import time "core:time" import datetime "core:time/datetime" +import flags "core:flags" import sysinfo "core:sys/info" @@ -233,6 +234,7 @@ _ :: edit _ :: thread _ :: time _ :: datetime +_ :: flags _ :: sysinfo _ :: unicode _ :: utf8 diff --git a/examples/all/all_vendor_windows.odin b/examples/all/all_vendor_windows.odin new file mode 100644 index 000000000..0c72c886c --- /dev/null +++ b/examples/all/all_vendor_windows.odin @@ -0,0 +1,4 @@ +package all + +import wgpu "vendor:wgpu" +_ :: wgpu \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index dc11a5fd2..0495cf3b5 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -844,6 +844,8 @@ struct BuildContext { bool show_unused; bool show_unused_with_location; bool show_more_timings; + bool show_defineables; + String export_defineables_file; bool show_system_calls; bool keep_temp_files; bool ignore_unknown_attributes; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e1506f64e..a8299d0f5 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1756,9 +1756,21 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->mode = Addressing_Constant; operand->value = exact_value_bool(is_defined); + // If the arg is a selector expression we don't add it, `-define` only allows identifiers. + if (arg->kind == Ast_Ident) { + Defineable defineable = {}; + defineable.docs = nullptr; + defineable.name = arg->Ident.token.string; + defineable.default_value = exact_value_bool(false); + defineable.pos = arg->Ident.token.pos; + + MUTEX_GUARD(&c->info->defineables_mutex); + array_add(&c->info->defineables, defineable); + } + } else if (name == "config") { if (ce->args.count != 2) { - error(call, "'#config' expects 2 argument, got %td", ce->args.count); + error(call, "'#config' expects 2 arguments, got %td", ce->args.count); return false; } Ast *arg = unparen_expr(ce->args[0]); @@ -1793,8 +1805,22 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->value = found->Constant.value; } } - } + + Defineable defineable = {}; + defineable.docs = nullptr; + defineable.name = name; + defineable.default_value = def.value; + defineable.pos = arg->Ident.token.pos; + if (c->decl) { + defineable.docs = c->decl->docs; + } + + MUTEX_GUARD(&c->info->defineables_mutex); + array_add(&c->info->defineables, defineable); + + } + // Bodging in #region & #endregion support else if (name == "region") { operand->type = t_untyped_bool; @@ -1806,7 +1832,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->mode = Addressing_Constant; operand->value = exact_value_bool(true); } - + else { error(call, "Unknown directive call: #%.*s", LIT(name)); } @@ -5103,15 +5129,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As isize max_arg_count = 32; switch (build_context.metrics.os) { - case TargetOs_windows: - case TargetOs_freestanding: - error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os])); - break; case TargetOs_darwin: case TargetOs_linux: case TargetOs_essence: - case TargetOs_freebsd: - case TargetOs_openbsd: case TargetOs_haiku: switch (build_context.metrics.arch) { case TargetArch_i386: @@ -5121,6 +5141,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } break; + default: + error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os])); + break; } if (ce->args.count > max_arg_count) { @@ -5134,6 +5157,55 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return true; } break; + case BuiltinProc_syscall_bsd: + { + convert_to_typed(c, operand, t_uintptr); + if (!is_type_uintptr(operand->type)) { + gbString t = type_to_string(operand->type); + error(operand->expr, "Argument 0 must be of type 'uintptr', got %s", t); + gb_string_free(t); + } + for (isize i = 1; i < ce->args.count; i++) { + Operand x = {}; + check_expr(c, &x, ce->args[i]); + if (x.mode != Addressing_Invalid) { + convert_to_typed(c, &x, t_uintptr); + } + convert_to_typed(c, &x, t_uintptr); + if (!is_type_uintptr(x.type)) { + gbString t = type_to_string(x.type); + error(x.expr, "Argument %td must be of type 'uintptr', got %s", i, t); + gb_string_free(t); + } + } + + isize max_arg_count = 32; + + switch (build_context.metrics.os) { + case TargetOs_freebsd: + case TargetOs_netbsd: + case TargetOs_openbsd: + switch (build_context.metrics.arch) { + case TargetArch_amd64: + case TargetArch_arm64: + max_arg_count = 7; + break; + } + break; + default: + error(call, "'%.*s' is not supported on this platform (%.*s)", LIT(builtin_name), LIT(target_os_names[build_context.metrics.os])); + break; + } + + if (ce->args.count > max_arg_count) { + error(ast_end_token(call), "'%.*s' has a maximum of %td arguments on this platform (%.*s), got %td", LIT(builtin_name), max_arg_count, LIT(target_os_names[build_context.metrics.os]), ce->args.count); + } + + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(t_uintptr); + return true; + } + break; case BuiltinProc_type_base_type: diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 02445cbc6..818556951 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1077,7 +1077,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } - if (e->pkg != nullptr && e->token.string == "main") { + if (e->pkg != nullptr && e->token.string == "main" && !build_context.no_entry_point) { if (e->pkg->kind != Package_Runtime) { if (pt->param_count != 0 || pt->result_count != 0) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 49144f4b9..5b5fc43cb 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2606,6 +2606,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * if (o->mode == Addressing_Constant) { Type *type = base_type(o->type); if (!is_type_constant_type(o->type)) { + if (is_type_array_like(o->type)) { + o->mode = Addressing_Value; + return; + } + gbString xt = type_to_string(o->type); gbString err_str = expr_to_string(node); error(op, "Invalid type, '%s', for constant unary expression '%s'", xt, err_str); @@ -8324,6 +8329,14 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A } o->type = t_untyped_string; o->value = exact_value_string(file); + } else if (name == "directory") { + String file = get_file_path_string(bd->token.pos.file_id); + String path = dir_from_path(file); + if (build_context.obfuscate_source_code_locations) { + path = obfuscate_string(path, "D"); + } + o->type = t_untyped_string; + o->value = exact_value_string(path); } else if (name == "line") { i32 line = bd->token.pos.line; if (build_context.obfuscate_source_code_locations) { @@ -9821,7 +9834,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * if (tav.mode != Addressing_Constant) { continue; } - GB_ASSERT(tav.value.kind == ExactValue_Integer); + if (tav.value.kind != ExactValue_Integer) { + continue; + } i64 v = big_int_to_i64(&tav.value.value_integer); i64 lower = bt->BitSet.lower; u64 index = cast(u64)(v-lower); diff --git a/src/checker.cpp b/src/checker.cpp index 852fb89bb..4945b3810 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1302,6 +1302,7 @@ gb_internal void init_checker_info(CheckerInfo *i) { array_init(&i->init_procedures, a, 0, 0); array_init(&i->fini_procedures, a, 0, 0); array_init(&i->required_foreign_imports_through_force, a, 0, 0); + array_init(&i->defineables, a); map_init(&i->objc_msgSend_types); string_map_init(&i->load_file_cache); @@ -1331,6 +1332,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { string_map_destroy(&i->packages); array_free(&i->variable_init_order); array_free(&i->required_foreign_imports_through_force); + array_free(&i->defineables); mpsc_destroy(&i->entity_queue); mpsc_destroy(&i->definition_queue); @@ -4110,6 +4112,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { bool is_test = false; bool is_init = false; bool is_fini = false; + bool is_priv = false; for_array(i, vd->attributes) { Ast *attr = vd->attributes[i]; @@ -4154,6 +4157,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } if (!success) { error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + } else { + is_priv = true; } @@ -4175,6 +4180,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } + if (is_priv && is_test) { + error(decl, "Attribute 'private' is not allowed on a test case"); + return; + } + if (entity_visibility_kind == EntityVisiblity_Public && (c->scope->flags&ScopeFlag_File) && c->scope->file) { diff --git a/src/checker.hpp b/src/checker.hpp index 492a64fb6..781737140 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -382,6 +382,17 @@ struct GenTypesData { RecursiveMutex mutex; }; +struct Defineable { + String name; + ExactValue default_value; + TokenPos pos; + CommentGroup *docs; + + // These strings are only computed from previous fields when defineables are being shown or exported. + String default_value_str; + String pos_str; +}; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Checker *checker; @@ -408,6 +419,9 @@ struct CheckerInfo { Array entities; Array required_foreign_imports_through_force; + BlockingMutex defineables_mutex; + Array defineables; + // Below are accessed within procedures RwMutex global_untyped_mutex; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 35acad42f..a2b8a5361 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -192,6 +192,7 @@ BuiltinProc__simd_end, // Platform specific intrinsics BuiltinProc_syscall, + BuiltinProc_syscall_bsd, BuiltinProc_x86_cpuid, BuiltinProc_x86_xgetbv, @@ -512,7 +513,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, + {STR_LIT("syscall_bsd"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/linker.cpp b/src/linker.cpp index 25c54a6ab..9eed14ea9 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -265,16 +265,20 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (!build_context.use_lld) { // msvc String res_path = {}; defer (gb_free(heap_allocator(), res_path.text)); + + // TODO(Jeroen): Add ability to reuse .res file instead of recompiling, if `-resource:file.res` is given. if (build_context.has_resource) { String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\"")); gb_free(heap_allocator(), temp_res_path.text); - String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); + String temp_rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); + String rc_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_rc_path, str_lit("\"")); + gb_free(heap_allocator(), temp_rc_path.text); defer (gb_free(heap_allocator(), rc_path.text)); result = system_exec_command_line_app("msvc-link", - "\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"", + "\"%.*src.exe\" /nologo /fo %.*s %.*s", LIT(windows_sdk_bin_path), LIT(res_path), LIT(rc_path) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 87f75fb1d..958b4fd38 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2747,26 +2747,10 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu { GB_ASSERT(arg_count <= 7); - // FreeBSD additionally clobbers r8, r9, r10, but they - // can also be used to pass in arguments, so this needs - // to be handled in two parts. - bool clobber_arg_regs[7] = { - false, false, false, false, false, false, false - }; - if (build_context.metrics.os == TargetOs_freebsd) { - clobber_arg_regs[4] = true; // r10 - clobber_arg_regs[5] = true; // r8 - clobber_arg_regs[6] = true; // r9 - } - char asm_string[] = "syscall"; gbString constraints = gb_string_make(heap_allocator(), "={rax}"); for (unsigned i = 0; i < arg_count; i++) { - if (!clobber_arg_regs[i]) { - constraints = gb_string_appendc(constraints, ",{"); - } else { - constraints = gb_string_appendc(constraints, ",+{"); - } + constraints = gb_string_appendc(constraints, ",{"); static char const *regs[] = { "rax", "rdi", @@ -2790,36 +2774,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu // Some but not all system calls will additionally // clobber memory. // - // As a fix for CVE-2019-5595, FreeBSD started - // clobbering R8, R9, and R10, instead of restoring - // them. Additionally unlike Linux, instead of - // returning negative errno, positive errno is - // returned and CF is set. - // // TODO: // * Figure out what Darwin does. - // * Add some extra handling to propagate CF back - // up to the caller on FreeBSD systems so that - // the caller knows that the return value is - // positive errno. constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}"); - if (build_context.metrics.os == TargetOs_freebsd) { - // Second half of dealing with FreeBSD's system - // call semantics. Explicitly clobber the registers - // that were not used to pass in arguments, and - // then clobber RFLAGS. - if (arg_count < 5) { - constraints = gb_string_appendc(constraints, ",~{r10}"); - } - if (arg_count < 6) { - constraints = gb_string_appendc(constraints, ",~{r8}"); - } - if (arg_count < 7) { - constraints = gb_string_appendc(constraints, ",~{r9}"); - } - constraints = gb_string_appendc(constraints, ",~{cc}"); - } - inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); } break; @@ -2927,6 +2884,139 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu res.type = t_uintptr; return res; } + case BuiltinProc_syscall_bsd: + { + // This is a BSD-style syscall where errors are indicated by a high + // Carry Flag and a positive return value, allowing the kernel to + // return any value that fits into a machine word. + // + // This is unlike Linux, where errors are indicated by a negative + // return value, limiting what can be expressed in one result. + unsigned arg_count = cast(unsigned)ce->args.count; + LLVMValueRef *args = gb_alloc_array(permanent_allocator(), LLVMValueRef, arg_count); + for_array(i, ce->args) { + lbValue arg = lb_build_expr(p, ce->args[i]); + arg = lb_emit_conv(p, arg, t_uintptr); + args[i] = arg.value; + } + + LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr); + LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count); + for (unsigned i = 0; i < arg_count; i++) { + llvm_arg_types[i] = llvm_uintptr; + } + + LLVMTypeRef *results = gb_alloc_array(permanent_allocator(), LLVMTypeRef, 2); + results[0] = lb_type(p->module, t_uintptr); + results[1] = lb_type(p->module, t_bool); + LLVMTypeRef llvm_results = LLVMStructTypeInContext(p->module->ctx, results, 2, false); + + LLVMTypeRef func_type = LLVMFunctionType(llvm_results, llvm_arg_types, arg_count, false); + + LLVMValueRef inline_asm = nullptr; + + switch (build_context.metrics.arch) { + case TargetArch_amd64: + { + GB_ASSERT(arg_count <= 7); + + char asm_string[] = "syscall; setnb %cl"; + + // Using CL as an output; RCX doesn't need to get clobbered later. + gbString constraints = gb_string_make(heap_allocator(), "={rax},={cl}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "rax", + "rdi", + "rsi", + "rdx", + "r10", + "r8", + "r9", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + // NOTE(Feoramund): If you're experiencing instability + // regarding syscalls during optimized builds, it is + // possible that the ABI has changed for your platform, + // or I've missed a register clobber. + // + // Documentation on this topic is sparse, but I was able to + // determine what registers were being clobbered by adding + // dummy values to them, setting a breakpoint after the + // syscall, and checking the state of the registers afterwards. + // + // Be advised that manually stepping through a debugger may + // cause the kernel to not return via sysret, which will + // preserve register state that normally would've been + // otherwise clobbered. + // + // It is also possible that some syscalls clobber different registers. + + if (build_context.metrics.os == TargetOs_freebsd) { + // As a fix for CVE-2019-5595, FreeBSD started + // clobbering R8, R9, and R10, instead of restoring + // them. + // + // More info here: + // + // https://www.freebsd.org/security/advisories/FreeBSD-SA-19:01.syscall.asc + // https://github.com/freebsd/freebsd-src/blob/098dbd7ff7f3da9dda03802cdb2d8755f816eada/sys/amd64/amd64/exception.S#L605 + // https://stackoverflow.com/q/66878250 + constraints = gb_string_appendc(constraints, ",~{r8},~{r9},~{r10}"); + } + + // Both FreeBSD and NetBSD might clobber RDX. + // + // For NetBSD, it was clobbered during a call to sysctl. + // + // For FreeBSD, it's listed as "return value 2" in their + // AMD64 assembly, so there's no guarantee that it will persist. + constraints = gb_string_appendc(constraints, ",~{rdx},~{r11},~{cc},~{memory}"); + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); + } + break; + case TargetArch_arm64: + { + GB_ASSERT(arg_count <= 7); + + char asm_string[] = "svc #0; cset x8, cc"; + gbString constraints = gb_string_make(heap_allocator(), "={x0},={x8}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "x8", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + // FreeBSD clobbered x1 on a call to sysctl. + constraints = gb_string_appendc(constraints, ",~{x1},~{cc},~{memory}"); + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); + } + break; + default: + GB_PANIC("Unsupported platform"); + } + + lbValue res = {}; + res.value = LLVMBuildCall2(p->builder, func_type, inline_asm, args, arg_count, ""); + res.type = make_optional_ok_type(t_uintptr, true); + + return res; + } case BuiltinProc_objc_send: return lb_handle_objc_send(p, expr); diff --git a/src/main.cpp b/src/main.cpp index f4cd40fe9..368b9aa6d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -288,6 +288,9 @@ enum BuildFlagKind { BuildFlag_NoThreadedChecker, BuildFlag_ShowDebugMessages, + BuildFlag_ShowDefineables, + BuildFlag_ExportDefineables, + BuildFlag_Vet, BuildFlag_VetShadowing, BuildFlag_VetUnused, @@ -483,6 +486,9 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_ShowDefineables, str_lit("show-defineables"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_ExportDefineables, str_lit("export-defineables"), BuildFlagParam_String, Command__does_check); + add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check); @@ -814,6 +820,24 @@ gb_internal bool parse_build_flags(Array args) { break; } + case BuildFlag_ShowDefineables: { + GB_ASSERT(value.kind == ExactValue_Invalid); + build_context.show_defineables = true; + break; + } + case BuildFlag_ExportDefineables: { + GB_ASSERT(value.kind == ExactValue_String); + + String export_path = string_trim_whitespace(value.value_string); + if (is_build_flag_path_valid(export_path)) { + build_context.export_defineables_file = path_to_full_path(heap_allocator(), export_path); + } else { + gb_printf_err("Invalid -export-defineables path, got %.*s\n", LIT(export_path)); + bad_flags = true; + } + + break; + } case BuildFlag_ShowSystemCalls: { GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_system_calls = true; @@ -1553,6 +1577,115 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin gb_printf("Done.\n"); } +gb_internal void check_defines(BuildContext *bc, Checker *c) { + for (auto const &entry : bc->defined_values) { + String name = make_string_c(entry.key); + ExactValue value = entry.value; + GB_ASSERT(value.kind != ExactValue_Invalid); + + bool found = false; + for_array(i, c->info.defineables) { + Defineable *def = &c->info.defineables[i]; + if (def->name == name) { + found = true; + break; + } + } + + if (!found) { + ERROR_BLOCK(); + warning(nullptr, "given -define:%.*s is unused in the project", LIT(name)); + error_line("\tSuggestion: use the -show-defineables flag for an overview of the possible defines\n"); + } + } +} + +gb_internal void temp_alloc_defineable_strings(Checker *c) { + for_array(i, c->info.defineables) { + Defineable *def = &c->info.defineables[i]; + def->default_value_str = make_string_c(write_exact_value_to_string(gb_string_make(temporary_allocator(), ""), def->default_value)); + def->pos_str = make_string_c(token_pos_to_string(def->pos)); + } +} + +gb_internal GB_COMPARE_PROC(defineables_cmp) { + Defineable *x = (Defineable *)a; + Defineable *y = (Defineable *)b; + + int cmp = 0; + + String x_file = get_file_path_string(x->pos.file_id); + String y_file = get_file_path_string(y->pos.file_id); + cmp = string_compare(x_file, y_file); + if (cmp) { + return cmp; + } + + return i32_cmp(x->pos.offset, y->pos.offset); +} + +gb_internal void sort_defineables(Checker *c) { + gb_sort_array(c->info.defineables.data, c->info.defineables.count, defineables_cmp); +} + +gb_internal void export_defineables(Checker *c, String path) { + gbFile f = {}; + gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, (char *)path.text); + if (err != gbFileError_None) { + gb_printf_err("Failed to export defineables to: %.*s\n", LIT(path)); + gb_exit(1); + return; + } else { + gb_printf("Exporting defineables to '%.*s'...\n", LIT(path)); + } + defer (gb_file_close(&f)); + + gbString docs = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(docs)); + + gb_fprintf(&f, "Defineable,Default Value,Docs,Location\n"); + for_array(i, c->info.defineables) { + Defineable *def = &c->info.defineables[i]; + + gb_string_clear(docs); + if (def->docs) { + docs = gb_string_appendc(docs, "\""); + for (Token const &token : def->docs->list) { + for (isize i = 0; i < token.string.len; i++) { + u8 c = token.string.text[i]; + if (c == '"') { + docs = gb_string_appendc(docs, "\"\""); + } else { + docs = gb_string_append_length(docs, &c, 1); + } + } + } + docs = gb_string_appendc(docs, "\""); + } + + gb_fprintf(&f,"%.*s,%.*s,%s,%.*s\n", LIT(def->name), LIT(def->default_value_str), docs, LIT(def->pos_str)); + } +} + +gb_internal void show_defineables(Checker *c) { + for_array(i, c->info.defineables) { + Defineable *def = &c->info.defineables[i]; + if (has_ansi_terminal_colours()) { + gb_printf("\x1b[0;90m"); + } + printf("%.*s\n", LIT(def->pos_str)); + if (def->docs) { + for (Token const &token : def->docs->list) { + gb_printf("%.*s\n", LIT(token.string)); + } + } + if (has_ansi_terminal_colours()) { + gb_printf("\x1b[0m"); + } + gb_printf("%.*s :: %.*s\n\n", LIT(def->name), LIT(def->default_value_str)); + } +} + gb_internal void show_timings(Checker *c, Timings *t) { Parser *p = c->parser; isize lines = p->total_line_count; @@ -1955,6 +2088,15 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Usage in code:"); print_usage_line(3, "#config(SPAM, default_value)"); print_usage_line(0, ""); + + print_usage_line(1, "-show-defineables"); + print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project."); + print_usage_line(0, ""); + + print_usage_line(1, "-export-defineables:"); + print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path."); + print_usage_line(2, "Example: -export-defineables:defineables.csv"); + print_usage_line(0, ""); } if (build) { @@ -2953,6 +3095,7 @@ int main(int arg_count, char const **arg_ptr) { defer (destroy_checker(checker)); check_parsed_files(checker); + check_defines(&build_context, checker); if (any_errors()) { print_all_errors(); return 1; @@ -2961,6 +3104,19 @@ int main(int arg_count, char const **arg_ptr) { print_all_errors(); } + if (build_context.show_defineables || build_context.export_defineables_file != "") { + TEMPORARY_ALLOCATOR_GUARD(); + temp_alloc_defineable_strings(checker); + sort_defineables(checker); + + if (build_context.show_defineables) { + show_defineables(checker); + } + + if (build_context.export_defineables_file != "") { + export_defineables(checker, build_context.export_defineables_file); + } + } if (build_context.command_kind == Command_strip_semicolon) { return strip_semicolons(parser); diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index 556f371af..0e2d0d94a 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -20,9 +20,6 @@ test_avl :: proc(t: ^testing.T) { iter := avl.iterator(&tree, avl.Direction.Forward) testing.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - // Test insertion. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. inserted_map := make(map[int]^avl.Node(int)) diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 425a9b440..b686ef6dd 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -14,9 +14,6 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { defer mem.tracking_allocator_destroy(&track) context.allocator = mem.tracking_allocator(&track) - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - log.infof("Testing Red-Black Tree($Key=%v,$Value=%v) using random seed %v.", type_info_of(Key), type_info_of(Value), t.seed) tree: rb.Tree(Key, Value) rb.init(&tree) diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin new file mode 100644 index 000000000..e32c6832c --- /dev/null +++ b/tests/core/flags/test_core_flags.odin @@ -0,0 +1,1393 @@ +package test_core_flags + +import "base:runtime" +import "core:bytes" +import "core:flags" +import "core:fmt" +@require import "core:log" +import "core:math" +@require import "core:net" +import "core:os" +import "core:strings" +import "core:testing" +import "core:time/datetime" + +@(test) +test_no_args :: proc(t: ^testing.T) { + S :: struct { + a: string, + } + s: S + args: []string + result := flags.parse(&s, args) + testing.expect_value(t, result, nil) +} + +@(test) +test_two_flags :: proc(t: ^testing.T) { + S :: struct { + i: string, + o: string, + } + s: S + args := [?]string { "-i:hellope", "-o:world" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.i, "hellope") + testing.expect_value(t, s.o, "world") +} + +@(test) +test_extra_arg :: proc(t: ^testing.T) { + S :: struct { + a: string, + } + s: S + args := [?]string { "-a:hellope", "world" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Extra_Positional) + } +} + +@(test) +test_assignment_oddities :: proc(t: ^testing.T) { + S :: struct { + s: string, + } + s: S + + { + args := [?]string { "-s:=" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.s, "=") + } + + { + args := [?]string { "-s=:" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.s, ":") + } + + { + args := [?]string { "-" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Flag) + } + } +} + +@(test) +test_string_into_int :: proc(t: ^testing.T) { + S :: struct { + n: int, + } + s: S + args := [?]string { "-n:hellope" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } +} + +@(test) +test_string_into_bool :: proc(t: ^testing.T) { + S :: struct { + b: bool, + } + s: S + args := [?]string { "-b:hellope" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } +} + +@(test) +test_all_bools :: proc(t: ^testing.T) { + S :: struct { + a: bool, + b: b8, + c: b16, + d: b32, + e: b64, + } + s: S + s.a = true + s.c = true + args := [?]string { "-a:false", "-b:true", "-c:0", "-d", "-e:1" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, false) + testing.expect_value(t, s.b, true) + testing.expect_value(t, s.c, false) + testing.expect_value(t, s.d, true) + testing.expect_value(t, s.e, true) +} + +@(test) +test_all_ints :: proc(t: ^testing.T) { + S :: struct { + a: u8, + b: i8, + c: u16, + d: i16, + e: u32, + f: i32, + g: u64, + i: i64, + j: u128, + k: i128, + } + + s: S + args := [?]string { + fmt.tprintf("-a:%i", max(u8)), + fmt.tprintf("-b:%i", min(i8)), + fmt.tprintf("-c:%i", max(u16)), + fmt.tprintf("-d:%i", min(i16)), + fmt.tprintf("-e:%i", max(u32)), + fmt.tprintf("-f:%i", min(i32)), + fmt.tprintf("-g:%i", max(u64)), + fmt.tprintf("-i:%i", min(i64)), + fmt.tprintf("-j:%i", max(u128)), + fmt.tprintf("-k:%i", min(i128)), + } + + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, max(u8)) + testing.expect_value(t, s.b, min(i8)) + testing.expect_value(t, s.c, max(u16)) + testing.expect_value(t, s.d, min(i16)) + testing.expect_value(t, s.e, max(u32)) + testing.expect_value(t, s.f, min(i32)) + testing.expect_value(t, s.g, max(u64)) + testing.expect_value(t, s.i, min(i64)) + testing.expect_value(t, s.j, max(u128)) + testing.expect_value(t, s.k, min(i128)) +} + +@(test) +test_all_floats :: proc(t: ^testing.T) { + S :: struct { + a: f16, + b: f32, + c: f64, + d: f64, + e: f64, + } + s: S + args := [?]string { "-a:100", "-b:3.14", "-c:-123.456", "-d:nan", "-e:inf" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 100) + testing.expect_value(t, s.b, 3.14) + testing.expect_value(t, s.c, -123.456) + testing.expectf(t, math.is_nan(s.d), "expected NaN, got %v", s.d) + testing.expectf(t, math.is_inf(s.e, +1), "expected +Inf, got %v", s.e) +} + +@(test) +test_all_enums :: proc(t: ^testing.T) { + E :: enum { A, B } + S :: struct { + nameless: enum { C, D }, + named: E, + } + s: S + args := [?]string { "-nameless:D", "-named:B" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, cast(int)s.nameless, 1) + testing.expect_value(t, s.named, E.B) +} + +@(test) +test_all_complex :: proc(t: ^testing.T) { + S :: struct { + a: complex32, + b: complex64, + c: complex128, + } + s: S + args := [?]string { "-a:1+0i", "-b:3+7i", "-c:NaNNaNi" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, real(s.a), 1) + testing.expect_value(t, imag(s.a), 0) + testing.expect_value(t, real(s.b), 3) + testing.expect_value(t, imag(s.b), 7) + testing.expectf(t, math.is_nan(real(s.c)), "expected NaN, got %v", real(s.c)) + testing.expectf(t, math.is_nan(imag(s.c)), "expected NaN, got %v", imag(s.c)) +} + +@(test) +test_all_quaternion :: proc(t: ^testing.T) { + S :: struct { + a: quaternion64, + b: quaternion128, + c: quaternion256, + } + s: S + args := [?]string { "-a:1+0i+1j+0k", "-b:3+7i+5j-3k", "-c:NaNNaNi+Infj-Infk" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + + raw_a := (cast(^runtime.Raw_Quaternion64)&s.a) + raw_b := (cast(^runtime.Raw_Quaternion128)&s.b) + raw_c := (cast(^runtime.Raw_Quaternion256)&s.c) + + testing.expect_value(t, raw_a.real, 1) + testing.expect_value(t, raw_a.imag, 0) + testing.expect_value(t, raw_a.jmag, 1) + testing.expect_value(t, raw_a.kmag, 0) + + testing.expect_value(t, raw_b.real, 3) + testing.expect_value(t, raw_b.imag, 7) + testing.expect_value(t, raw_b.jmag, 5) + testing.expect_value(t, raw_b.kmag, -3) + + testing.expectf(t, math.is_nan(raw_c.real), "expected NaN, got %v", raw_c.real) + testing.expectf(t, math.is_nan(raw_c.imag), "expected NaN, got %v", raw_c.imag) + testing.expectf(t, math.is_inf(raw_c.jmag, +1), "expected +Inf, got %v", raw_c.jmag) + testing.expectf(t, math.is_inf(raw_c.kmag, -1), "expected -Inf, got %v", raw_c.kmag) +} + +@(test) +test_all_bit_sets :: proc(t: ^testing.T) { + E :: enum { + Option_A, + Option_B, + } + S :: struct { + a: bit_set[0..<8], + b: bit_set[0..<16; u16], + c: bit_set[16..<18; rune], + d: bit_set[0..<1; i8], + e: bit_set[0..<128], + f: bit_set[-32..<32], + g: bit_set[E], + i: bit_set[E; u8], + } + s: S + { + args := [?]string { + "-a:10101", + "-b:0000_0000_0000_0001", + "-c:11", + "-d:___1", + "-e:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "-f:1", + "-g:01", + "-i:1", + } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, bit_set[0..<8]{0, 2, 4}) + testing.expect_value(t, s.b, bit_set[0..<16; u16]{15}) + testing.expect_value(t, s.c, bit_set[16..<18; rune]{16, 17}) + testing.expect_value(t, s.d, bit_set[0..<1; i8]{0}) + testing.expect_value(t, s.e, bit_set[0..<128]{127}) + testing.expect_value(t, s.f, bit_set[-32..<32]{-32}) + testing.expect_value(t, s.g, bit_set[E]{E.Option_B}) + testing.expect_value(t, s.i, bit_set[E; u8]{E.Option_A}) + } + { + args := [?]string { "-d:11" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } +} + +@(test) +test_all_strings :: proc(t: ^testing.T) { + S :: struct { + a, b, c: string, + d: cstring, + } + s: S + args := [?]string { "-a:hi", "-b:hellope", "-c:spaced out", "-d:cstr", "-d:cstr-overwrite" } + result := flags.parse(&s, args[:]) + defer delete(s.d) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, "hi") + testing.expect_value(t, s.b, "hellope") + testing.expect_value(t, s.c, "spaced out") + testing.expect_value(t, s.d, "cstr-overwrite") +} + +@(test) +test_runes :: proc(t: ^testing.T) { + S :: struct { + a, b, c: rune, + } + s: S + args := [?]string { "-a:a", "-b:ツ", "-c:\U0010FFFF" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 'a') + testing.expect_value(t, s.b, 'ツ') + testing.expect_value(t, s.c, '\U0010FFFF') +} + +@(test) +test_no_value :: proc(t: ^testing.T) { + S :: struct { + a: rune, + } + s: S + + { + args := [?]string { "-a:" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value) + } + } + + { + args := [?]string { "-a=" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value) + } + } +} + +@(test) +test_overflow :: proc(t: ^testing.T) { + S :: struct { + a: u8, + } + s: S + args := [?]string { "-a:256" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } +} + +@(test) +test_underflow :: proc(t: ^testing.T) { + S :: struct { + a: i8, + } + s: S + args := [?]string { "-a:-129" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } +} + +@(test) +test_arrays :: proc(t: ^testing.T) { + S :: struct { + a: [dynamic]string, + b: [dynamic]int, + } + s: S + args := [?]string { "-a:abc", "-b:1", "-a:foo", "-b:3" } + result := flags.parse(&s, args[:]) + defer { + delete(s.a) + delete(s.b) + } + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.a), 2) + testing.expect_value(t, len(s.b), 2) + + if len(s.a) < 2 || len(s.b) < 2 { + return + } + + testing.expect_value(t, s.a[0], "abc") + testing.expect_value(t, s.a[1], "foo") + testing.expect_value(t, s.b[0], 1) + testing.expect_value(t, s.b[1], 3) +} + +@(test) +test_varargs :: proc(t: ^testing.T) { + S :: struct { + varg: [dynamic]string, + } + s: S + args := [?]string { "abc", "foo", "bar" } + result := flags.parse(&s, args[:]) + defer delete(s.varg) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.varg), 3) + + if len(s.varg) < 3 { + return + } + + testing.expect_value(t, s.varg[0], "abc") + testing.expect_value(t, s.varg[1], "foo") + testing.expect_value(t, s.varg[2], "bar") +} + +@(test) +test_mixed_varargs :: proc(t: ^testing.T) { + S :: struct { + input: string `args:"pos=0"`, + varg: [dynamic]string, + } + s: S + args := [?]string { "abc", "foo", "bar" } + result := flags.parse(&s, args[:]) + defer delete(s.varg) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.varg), 2) + + if len(s.varg) < 2 { + return + } + + testing.expect_value(t, s.input, "abc") + testing.expect_value(t, s.varg[0], "foo") + testing.expect_value(t, s.varg[1], "bar") +} + +@(test) +test_maps :: proc(t: ^testing.T) { + S :: struct { + a: map[string]string, + b: map[string]int, + } + s: S + args := [?]string { "-a:abc=foo", "-b:bar=42" } + result := flags.parse(&s, args[:]) + defer { + delete(s.a) + delete(s.b) + } + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.a), 1) + testing.expect_value(t, len(s.b), 1) + + if len(s.a) < 1 || len(s.b) < 1 { + return + } + + abc, has_abc := s.a["abc"] + bar, has_bar := s.b["bar"] + + testing.expect(t, has_abc, "expected map to have `abc` key set") + testing.expect(t, has_bar, "expected map to have `bar` key set") + testing.expect_value(t, abc, "foo") + testing.expect_value(t, bar, 42) +} + +@(test) +test_invalid_map_syntax :: proc(t: ^testing.T) { + S :: struct { + a: map[string]string, + } + s: S + args := [?]string { "-a:foo:42" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value) + } +} + +@(test) +test_underline_name_to_dash :: proc(t: ^testing.T) { + S :: struct { + a_b: int, + } + s: S + args := [?]string { "-a-b:3" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a_b, 3) +} + +@(test) +test_tags_pos :: proc(t: ^testing.T) { + S :: struct { + b: int `args:"pos=1"`, + a: int `args:"pos=0"`, + } + s: S + args := [?]string { "42", "99" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 42) + testing.expect_value(t, s.b, 99) +} + +@(test) +test_tags_name :: proc(t: ^testing.T) { + S :: struct { + a: int `args:"name=alice"`, + b: int `args:"name=bill"`, + } + s: S + args := [?]string { "-alice:1", "-bill:2" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 1) + testing.expect_value(t, s.b, 2) +} + +@(test) +test_tags_required :: proc(t: ^testing.T) { + S :: struct { + a: int, + b: int `args:"required"`, + } + s: S + args := [?]string { "-a:1" } + result := flags.parse(&s, args[:]) + _, ok := result.(flags.Validation_Error) + testing.expectf(t, ok, "unexpected result: %v", result) +} + +@(test) +test_tags_required_pos :: proc(t: ^testing.T) { + S :: struct { + a: int `args:"pos=0,required"`, + b: int `args:"pos=1"`, + } + s: S + args := [?]string { "-b:5" } + result := flags.parse(&s, args[:]) + _, ok := result.(flags.Validation_Error) + testing.expectf(t, ok, "unexpected result: %v", result) +} + +@(test) +test_tags_required_limit_min :: proc(t: ^testing.T) { + S :: struct { + n: [dynamic]int `args:"required=3"`, + } + + { + s: S + args := [?]string { "-n:1" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + _, ok := result.(flags.Validation_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + } + + { + s: S + args := [?]string { "-n:3", "-n:5", "-n:7" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.n), 3) + + if len(s.n) == 3 { + testing.expect_value(t, s.n[0], 3) + testing.expect_value(t, s.n[1], 5) + testing.expect_value(t, s.n[2], 7) + } + } +} + +@(test) +test_tags_required_limit_min_max :: proc(t: ^testing.T) { + S :: struct { + n: [dynamic]int `args:"required=2<4"`, + } + + { + s: S + args := [?]string { "-n:1" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + _, ok := result.(flags.Validation_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + } + + { + s: S + args := [?]string { "-n:1", "-n:2", "-n:3", "-n:4" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + _, ok := result.(flags.Validation_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + } + + { + s: S + args := [?]string { "-n:3", "-n:5", "-n:7" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.n), 3) + + if len(s.n) == 3 { + testing.expect_value(t, s.n[0], 3) + testing.expect_value(t, s.n[1], 5) + testing.expect_value(t, s.n[2], 7) + } + } +} + +@(test) +test_tags_required_limit_max :: proc(t: ^testing.T) { + S :: struct { + n: [dynamic]int `args:"required=<4"`, + } + + { + s: S + args: []string + result := flags.parse(&s, args) + testing.expect_value(t, result, nil) + } + + { + s: S + args := [?]string { "-n:1", "-n:2", "-n:3", "-n:4" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + _, ok := result.(flags.Validation_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + } + + { + s: S + args := [?]string { "-n:3", "-n:5", "-n:7" } + result := flags.parse(&s, args[:]) + defer delete(s.n) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.n), 3) + + if len(s.n) == 3 { + testing.expect_value(t, s.n[0], 3) + testing.expect_value(t, s.n[1], 5) + testing.expect_value(t, s.n[2], 7) + } + } +} + +@(test) +test_tags_pos_out_of_order :: proc(t: ^testing.T) { + S :: struct { + a: int `args:"pos=2"`, + varg: [dynamic]int, + } + s: S + args := [?]string { "1", "2", "3", "4" } + result := flags.parse(&s, args[:]) + defer delete(s.varg) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.varg), 3) + + if len(s.varg) < 3 { + return + } + + testing.expect_value(t, s.a, 3) + testing.expect_value(t, s.varg[0], 1) + testing.expect_value(t, s.varg[1], 2) + testing.expect_value(t, s.varg[2], 4) +} + +@(test) +test_missing_flag :: proc(t: ^testing.T) { + S :: struct { + a: int, + } + s: S + args := [?]string { "-b" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag) + } +} + +@(test) +test_alt_syntax :: proc(t: ^testing.T) { + S :: struct { + a: int, + } + s: S + args := [?]string { "-a=3" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 3) +} + +@(test) +test_strict_returns_first_error :: proc(t: ^testing.T) { + S :: struct { + b: int, + c: int, + } + s: S + args := [?]string { "-a=3", "-b=3" } + result := flags.parse(&s, args[:], strict=true) + err, ok := result.(flags.Parse_Error) + testing.expect_value(t, s.b, 0) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag) + } +} + +@(test) +test_non_strict_returns_last_error :: proc(t: ^testing.T) { + S :: struct { + a: int, + b: int, + } + s: S + args := [?]string { "-a=foo", "-b=2", "-c=3" } + result := flags.parse(&s, args[:], strict=false) + err, ok := result.(flags.Parse_Error) + testing.expect_value(t, s.b, 2) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag) + } +} + +@(test) +test_map_overwrite :: proc(t: ^testing.T) { + S :: struct { + m: map[string]int, + } + s: S + args := [?]string { "-m:foo=3", "-m:foo=5" } + result := flags.parse(&s, args[:]) + defer delete(s.m) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.m), 1) + foo, has_foo := s.m["foo"] + testing.expect(t, has_foo, "expected map to have `foo` key set") + testing.expect_value(t, foo, 5) +} + +@(test) +test_maps_of_arrays :: proc(t: ^testing.T) { + // Why you would ever want to do this, I don't know, but it's possible! + S :: struct { + m: map[string][dynamic]int, + } + s: S + args := [?]string { "-m:foo=1", "-m:foo=2", "-m:bar=3" } + result := flags.parse(&s, args[:]) + defer { + for _, v in s.m { + delete(v) + } + delete(s.m) + } + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.m), 2) + + if len(s.m) != 2 { + return + } + + foo, has_foo := s.m["foo"] + bar, has_bar := s.m["bar"] + + testing.expect_value(t, has_foo, true) + testing.expect_value(t, has_bar, true) + + if has_foo { + testing.expect_value(t, len(foo), 2) + if len(foo) == 2 { + testing.expect_value(t, foo[0], 1) + testing.expect_value(t, foo[1], 2) + } + } + + if has_bar { + testing.expect_value(t, len(bar), 1) + if len(bar) == 1 { + testing.expect_value(t, bar[0], 3) + } + } +} + +@(test) +test_builtin_help_flag :: proc(t: ^testing.T) { + S :: struct {} + s: S + + args_short := [?]string { "-h" } + args_normal := [?]string { "-help" } + + result := flags.parse(&s, args_short[:]) + _, ok := result.(flags.Help_Request) + testing.expectf(t, ok, "unexpected result: %v", result) + + result = flags.parse(&s, args_normal[:]) + _, ok = result.(flags.Help_Request) + testing.expectf(t, ok, "unexpected result: %v", result) +} + +// This test makes sure that if a positional argument is specified, it won't be +// overwritten by an unspecified positional, which should follow the principle +// of least surprise for the user. +@(test) +test_pos_nonoverlap :: proc(t: ^testing.T) { + S :: struct { + a: int `args:"pos=0"`, + b: int `args:"pos=1"`, + } + s: S + + args := [?]string { "-a:3", "5" } + + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 3) + testing.expect_value(t, s.b, 5) +} + +// This test ensures the underlying `bit_array` container handles many +// arguments in a sane manner. +@(test) +test_pos_many_args :: proc(t: ^testing.T) { + S :: struct { + varg: [dynamic]int, + a: int `args:"pos=0,required"`, + b: int `args:"pos=64,required"`, + c: int `args:"pos=66,required"`, + d: int `args:"pos=129,required"`, + } + s: S + + args: [dynamic]string + defer delete(s.varg) + + for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) } + defer { + for a in args { + delete(a) + } + delete(args) + } + + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + + testing.expect_value(t, s.a, 1) + for i in 1 ..< 63 { testing.expect_value(t, s.varg[i], 2 + i) } + testing.expect_value(t, s.b, 65) + testing.expect_value(t, s.varg[63], 66) + testing.expect_value(t, s.c, 67) + testing.expect_value(t, s.varg[64], 68) + testing.expect_value(t, s.varg[65], 69) + testing.expect_value(t, s.varg[66], 70) + for i in 67 ..< 126 { testing.expect_value(t, s.varg[i], 4 + i) } + testing.expect_value(t, s.d, 130) +} + +@(test) +test_unix :: proc(t: ^testing.T) { + S :: struct { + a: string, + } + s: S + + { + args := [?]string { "--a", "hellope" } + + result := flags.parse(&s, args[:], .Unix) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, "hellope") + } + + { + args := [?]string { "-a", "hellope", "--a", "world" } + + result := flags.parse(&s, args[:], .Unix) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, "world") + } + + { + args := [?]string { "-a=hellope" } + + result := flags.parse(&s, args[:], .Unix) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, "hellope") + } +} + +@(test) +test_unix_variadic :: proc(t: ^testing.T) { + S :: struct { + a: [dynamic]int `args:"variadic"`, + } + s: S + + args := [?]string { "--a", "7", "32", "11" } + + result := flags.parse(&s, args[:], .Unix) + defer delete(s.a) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.a), 3) + + if len(s.a) < 3 { + return + } + + testing.expect_value(t, s.a[0], 7) + testing.expect_value(t, s.a[1], 32) + testing.expect_value(t, s.a[2], 11) +} + +@(test) +test_unix_variadic_limited :: proc(t: ^testing.T) { + S :: struct { + a: [dynamic]int `args:"variadic=2"`, + b: int, + } + s: S + + args := [?]string { "-a", "11", "101", "-b", "3" } + + result := flags.parse(&s, args[:], .Unix) + defer delete(s.a) + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.a), 2) + + if len(s.a) < 2 { + return + } + + testing.expect_value(t, s.a[0], 11) + testing.expect_value(t, s.a[1], 101) + testing.expect_value(t, s.b, 3) +} + +@(test) +test_unix_positional :: proc(t: ^testing.T) { + S :: struct { + a: int `args:"pos=1"`, + b: int `args:"pos=0"`, + } + s: S + + args := [?]string { "-b", "17", "11" } + + result := flags.parse(&s, args[:], .Unix) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a, 11) + testing.expect_value(t, s.b, 17) +} + +@(test) +test_unix_positional_with_variadic :: proc(t: ^testing.T) { + S :: struct { + varg: [dynamic]int, + v: [dynamic]int `args:"variadic"`, + } + s: S + + args := [?]string { "35", "-v", "17", "11" } + + result := flags.parse(&s, args[:], .Unix) + defer { + delete(s.varg) + delete(s.v) + } + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.varg), 1) + testing.expect_value(t, len(s.v), 2) +} + +@(test) +test_unix_double_dash_variadic :: proc(t: ^testing.T) { + S :: struct { + varg: [dynamic]string, + i: int, + } + s: S + + args := [?]string { "-i", "3", "--", "hellope", "-i", "5" } + + result := flags.parse(&s, args[:], .Unix) + defer { + delete(s.varg) + } + testing.expect_value(t, result, nil) + testing.expect_value(t, len(s.varg), 3) + testing.expect_value(t, s.i, 3) + + if len(s.varg) != 3 { + return + } + + testing.expect_value(t, s.varg[0], "hellope") + testing.expect_value(t, s.varg[1], "-i") + testing.expect_value(t, s.varg[2], "5") +} + +@(test) +test_unix_no_value :: proc(t: ^testing.T) { + S :: struct { + i: int, + } + s: S + + args := [?]string { "--i" } + + result := flags.parse(&s, args[:], .Unix) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value) + } +} + +// This test ensures there are no bad frees with cstrings. +@(test) +test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) { + S :: struct { + varg: [dynamic]cstring, + } + s: S + + args := [?]string { "Hellope", "world!" } + result := flags.parse(&s, args[:]) + defer { + for v in s.varg { + delete(v) + } + delete(s.varg) + } + testing.expect_value(t, result, nil) +} + +// This test ensures there are no double allocations with cstrings. +@(test) +test_if_map_cstrings_get_freed :: proc(t: ^testing.T) { + S :: struct { + m: map[cstring]cstring, + } + s: S + + args := [?]string { "-m:hellope=world", "-m:hellope=bar", "-m:hellope=foo" } + result := flags.parse(&s, args[:]) + defer { + for _, v in s.m { + delete(v) + } + delete(s.m) + } + testing.expect_value(t, result, nil) + testing.expect_value(t, s.m["hellope"], "foo") +} + +@(test) +test_os_handle :: proc(t: ^testing.T) { + defer if !testing.failed(t) { + // Delete the file now that we're done. + // + // This is not done all the time, just in case the file is useful to debugging. + testing.expect_value(t, os.remove(TEMPORARY_FILENAME), os.ERROR_NONE) + } + + TEMPORARY_FILENAME :: "test_core_flags_write_test_output_data" + + test_data := "Hellope!" + + W :: struct { + outf: os.Handle `args:"file=cw"`, + } + w: W + + args := [?]string { fmt.tprintf("-outf:%s", TEMPORARY_FILENAME) } + result := flags.parse(&w, args[:]) + testing.expect_value(t, result, nil) + if result != nil { + return + } + defer os.close(w.outf) + os.write_string(w.outf, test_data) + + R :: struct { + inf: os.Handle `args:"file=r"`, + } + r: R + + args = [?]string { fmt.tprintf("-inf:%s", TEMPORARY_FILENAME) } + result = flags.parse(&r, args[:]) + testing.expect_value(t, result, nil) + if result != nil { + return + } + defer os.close(r.inf) + data, read_ok := os.read_entire_file_from_handle(r.inf, context.temp_allocator) + testing.expect_value(t, read_ok, true) + file_contents_equal := 0 == bytes.compare(transmute([]u8)test_data, data) + testing.expectf(t, file_contents_equal, "expected file contents to be the same, got %v", data) +} + +@(test) +test_distinct_types :: proc(t: ^testing.T) { + I :: distinct int + S :: struct { + base_i: I `args:"indistinct"`, + unmodified_i: I, + } + s: S + + { + args := [?]string {"-base-i:1"} + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + } + + { + args := [?]string {"-unmodified-i:1"} + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Unsupported_Type) + } + } +} + +@(test) +test_datetime :: proc(t: ^testing.T) { + when flags.IMPORTING_TIME { + W :: struct { + t: datetime.DateTime, + } + w: W + + args := [?]string { "-t:2024-06-04T12:34:56Z" } + result := flags.parse(&w, args[:]) + testing.expect_value(t, result, nil) + if result != nil { + return + } + testing.expect_value(t, w.t.date.year, 2024) + testing.expect_value(t, w.t.date.month, 6) + testing.expect_value(t, w.t.date.day, 4) + } else { + log.info("Skipping test due to lack of platform support.") + } +} + +@(test) +test_net :: proc(t: ^testing.T) { + when flags.IMPORTING_NET { + W :: struct { + addr: net.Host_Or_Endpoint, + } + w: W + + args := [?]string { "-addr:odin-lang.org:80" } + result := flags.parse(&w, args[:]) + testing.expect_value(t, result, nil) + if result != nil { + return + } + host, is_host := w.addr.(net.Host) + testing.expectf(t, is_host, "expected type of `addr` to be `net.Host`, was %v", w.addr) + testing.expect_value(t, host.hostname, "odin-lang.org") + testing.expect_value(t, host.port, 80) + } else { + log.info("Skipping test due to lack of platform support.") + } +} + +@(test) +test_custom_type_setter :: proc(t: ^testing.T) { + Custom_Bool :: distinct bool + Custom_Data :: struct { + a: int, + } + + S :: struct { + a: Custom_Data, + b: Custom_Bool `args:"indistinct"`, + } + s: S + + // NOTE: Mind that this setter is global state, and the test runner is multi-threaded. + // It should be fine so long as all type setter tests are in this one test proc. + flags.register_type_setter(proc (data: rawptr, data_type: typeid, _, _: string) -> (string, bool, runtime.Allocator_Error) { + if data_type == Custom_Data { + (cast(^Custom_Data)data).a = 32 + return "", true, nil + } + return "", false, nil + }) + defer flags.register_type_setter(nil) + args := [?]string { "-a:hellope", "-b:true" } + result := flags.parse(&s, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, s.a.a, 32) + testing.expect_value(t, s.b, true) +} + +// This test is sensitive to many of the underlying mechanisms of the library, +// so if something isn't working, it'll probably show up here first, but it may +// not be immediately obvious as to what's wrong. +// +// It makes for a good early warning system. +@(test) +test_usage_write_odin :: proc(t: ^testing.T) { + Expected_Output :: `Usage: + varg required-number [number] [name] -bars -bots -foos -gadgets -widgets [-array] [-count] [-greek] [-map-type] [-verbose] ... +Flags: + -required-number:, required | some number + -number: | some other number + -name: + Multi-line documentation + gets formatted + very nicely. + -bars:, exactly 3 | + -bots:, at least 1 | + -foos:, between 2 and 3 | + -gadgets:, at least 1 | + -widgets:, at most 2 | + | + -array:, multiple | + -count: | + -greek: | + -map-type:= | + -verbose | + | +` + + Custom_Enum :: enum { + Alpha, + Omega, + } + + S :: struct { + required_number: int `args:"pos=0,required" usage:"some number"`, + number: int `args:"pos=1" usage:"some other number"`, + name: string `args:"pos=2" usage:" + Multi-line documentation + gets formatted +very nicely. + +"`, + + c: u8 `args:"name=count"`, + greek: Custom_Enum, + + array: [dynamic]rune, + map_type: map[cstring]byte, + + gadgets: [dynamic]string `args:"required=1"`, + widgets: [dynamic]string `args:"required=<3"`, + foos: [dynamic]string `args:"required=2<4"`, + bars: [dynamic]string `args:"required=3<4"`, + bots: [dynamic]string `args:"required"`, + + debug: bool `args:"hidden" usage:"print debug info"`, + verbose: bool, + + varg: [dynamic]string, + } + + builder := strings.builder_make() + defer strings.builder_destroy(&builder) + writer := strings.to_stream(&builder) + flags.write_usage(writer, S, "varg", .Odin) + testing.expect_value(t, strings.to_string(builder), Expected_Output) +} + +@(test) +test_usage_write_unix :: proc(t: ^testing.T) { + Expected_Output :: `Usage: + varg required-number [number] [name] --bars --bots --foos --gadgets --variadic-flag --widgets [--array] [--count] [--greek] [--verbose] ... +Flags: + --required-number , required | some number + --number | some other number + --name + Multi-line documentation + gets formatted + very nicely. + --bars , exactly 3 | + --bots , at least 1 | + --foos , between 2 and 3 | + --gadgets , at least 1 | + --variadic-flag , at least 2 | + --widgets , at most 2 | + | + --array , multiple | + --count | + --greek | + --verbose | + | +` + + Custom_Enum :: enum { + Alpha, + Omega, + } + + S :: struct { + required_number: int `args:"pos=0,required" usage:"some number"`, + number: int `args:"pos=1" usage:"some other number"`, + name: string `args:"pos=2" usage:" + Multi-line documentation + gets formatted +very nicely. + +"`, + + c: u8 `args:"name=count"`, + greek: Custom_Enum, + + array: [dynamic]rune, + variadic_flag: [dynamic]int `args:"variadic,required=2"`, + + gadgets: [dynamic]string `args:"required=1"`, + widgets: [dynamic]string `args:"required=<3"`, + foos: [dynamic]string `args:"required=2<4"`, + bars: [dynamic]string `args:"required=3<4"`, + bots: [dynamic]string `args:"required"`, + + debug: bool `args:"hidden" usage:"print debug info"`, + verbose: bool, + + varg: [dynamic]string, + } + + builder := strings.builder_make() + defer strings.builder_destroy(&builder) + writer := strings.to_stream(&builder) + flags.write_usage(writer, S, "varg", .Unix) + testing.expect_value(t, strings.to_string(builder), Expected_Output) +} diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index 0255717a2..c3f0bee91 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -53,8 +53,7 @@ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { testing.expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state") // XXH3_128_update - random_seed := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&random_seed) + rand.reset(t.seed) for len(b) > 0 { update_size := min(len(b), rand.int_max(8192)) if update_size > 4096 { diff --git a/tests/core/math/big/build.bat b/tests/core/math/big/build.bat index 54b715a4f..ad199d775 100644 --- a/tests/core/math/big/build.bat +++ b/tests/core/math/big/build.bat @@ -5,7 +5,7 @@ set TEST_ARGS=-fast-tests set TEST_ARGS=-no-random set TEST_ARGS= set OUT_NAME=math_big_test_library.dll -set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style -define:ODIN_TEST_FANCY=false +set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style echo --- echo Running core:math/big tests echo --- diff --git a/tests/core/math/rand/test_core_math_rand.odin b/tests/core/math/rand/test_core_math_rand.odin new file mode 100644 index 000000000..392d3d241 --- /dev/null +++ b/tests/core/math/rand/test_core_math_rand.odin @@ -0,0 +1,35 @@ +package test_core_math_rand + +import "core:math/rand" +import "core:testing" + +@test +test_default_rand_determinism :: proc(t: ^testing.T) { + rand.reset(13) + first_value := rand.int127() + rand.reset(13) + second_value := rand.int127() + + testing.expect(t, first_value == second_value, "Context default random number generator is non-deterministic.") +} + +@test +test_default_rand_determinism_user_set :: proc(t: ^testing.T) { + rng_state_1 := rand.create(13) + rng_state_2 := rand.create(13) + + rng_1 := rand.default_random_generator(&rng_state_1) + rng_2 := rand.default_random_generator(&rng_state_2) + + first_value, second_value: i128 + { + context.random_generator = rng_1 + first_value = rand.int127() + } + { + context.random_generator = rng_2 + second_value = rand.int127() + } + + testing.expect(t, first_value == second_value, "User-set default random number generator is non-deterministic.") +} diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 7620d7d6e..a84420cca 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -19,11 +19,13 @@ download_assets :: proc() { @(require) import "encoding/json" @(require) import "encoding/varint" @(require) import "encoding/xml" +@(require) import "flags" @(require) import "fmt" @(require) import "math" @(require) import "math/big" @(require) import "math/linalg/glsl" @(require) import "math/noise" +@(require) import "math/rand" @(require) import "mem" @(require) import "net" @(require) import "odin" @@ -37,3 +39,4 @@ download_assets :: proc() { @(require) import "text/match" @(require) import "thread" @(require) import "time" +@(require) import "unicode" diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 432636664..3e249055b 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -11,8 +11,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} for test_size in test_sizes { - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -63,8 +62,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} for test_size in test_sizes { - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -203,7 +201,7 @@ test_permutation_iterator :: proc(t: ^testing.T) { permutations_counted: int for slice.permute(&iter) { n := 0 - for item, index in s { + for item in s { n *= 10 n += item } @@ -218,3 +216,63 @@ test_permutation_iterator :: proc(t: ^testing.T) { testing.expect_value(t, len(seen), FAC_5) testing.expect_value(t, permutations_counted, FAC_5) } + +// Test inputs from #3276 and #3769 +UNIQUE_TEST_VECTORS :: [][2][]int{ + {{2,2,2}, {2}}, + {{1,1,1,2,2,3,3,3,3}, {1,2,3}}, + {{1,2,4,4,5}, {1,2,4,5}}, +} + +@test +test_unique :: proc(t: ^testing.T) { + for v in UNIQUE_TEST_VECTORS { + assorted := v[0] + expected := v[1] + + uniq := slice.unique(assorted) + testing.expectf(t, slice.equal(uniq, expected), "Expected slice.uniq(%v) == %v, got %v", v[0], v[1], uniq) + } + + for v in UNIQUE_TEST_VECTORS { + assorted := v[0] + expected := v[1] + + uniq := slice.unique_proc(assorted, proc(a, b: int) -> bool { + return a == b + }) + testing.expectf(t, slice.equal(uniq, expected), "Expected slice.unique_proc(%v, ...) == %v, got %v", v[0], v[1], uniq) + } + + r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) + + // 10_000 random tests + for _ in 0..<10_000 { + assorted: [dynamic]i64 + expected: [dynamic]i64 + + // Prime with 1 value + old := rand.int63() + append(&assorted, old) + append(&expected, old) + + // Add 99 additional random values + for _ in 1..<100 { + new := rand.int63() + append(&assorted, new) + if old != new { + append(&expected, new) + } + old = new + } + + original := slice.clone(assorted[:]) + uniq := slice.unique(assorted[:]) + testing.expectf(t, slice.equal(uniq, expected[:]), "Expected slice.uniq(%v) == %v, got %v", original, expected, uniq) + + delete(assorted) + delete(original) + delete(expected) + } +} \ No newline at end of file diff --git a/tests/core/text/i18n/test_core_text_i18n.odin b/tests/core/text/i18n/test_core_text_i18n.odin index f6cffc318..4c70bd8b9 100644 --- a/tests/core/text/i18n/test_core_text_i18n.odin +++ b/tests/core/text/i18n/test_core_text_i18n.odin @@ -5,6 +5,7 @@ import "core:testing" import "core:text/i18n" T :: i18n.get +Tn :: i18n.get_n Test :: struct { section: string, @@ -47,7 +48,8 @@ test_custom_pluralizer :: proc(t: ^testing.T) { {"", "Message1/plural", "This is message 1", 1}, {"", "Message1/plural", "This is message 1 - plural A", 1_000_000}, {"", "Message1/plural", "This is message 1 - plural B", 42}, - // This isn't in the catalog, so should ruturn the key. + + // This isn't in the catalog, so should return the key. {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, }, }) @@ -61,11 +63,11 @@ test_mixed_context :: proc(t: ^testing.T) { plural = nil, tests = { // These are in the catalog. - {"", "Message1", "This is message 1 without Context", 1}, - {"Context", "Message1", "This is message 1 with Context", 1}, + {"", "Message1", "This is message 1 without Context",-1}, + {"Context", "Message1", "This is message 1 with Context", -1}, // This isn't in the catalog, so should ruturn the key. - {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, + {"", "Come visit us on Discord!", "Come visit us on Discord!", -1}, }, }) } @@ -90,15 +92,15 @@ test_nl_mo :: proc(t: ^testing.T) { plural = nil, // Default pluralizer tests = { // These are in the catalog. - {"", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.", 1}, - {"", "Hellope, World!", "Hallo, Wereld!", 1}, + {"", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.", -1}, + {"", "Hellope, World!", "Hallo, Wereld!", -1}, {"", "There is %d leaf.\n", "Er is %d blad.\n", 1}, {"", "There are %d leaves.\n", "Er is %d blad.\n", 1}, {"", "There is %d leaf.\n", "Er zijn %d bladeren.\n", 42}, {"", "There are %d leaves.\n", "Er zijn %d bladeren.\n", 42}, // This isn't in the catalog, so should ruturn the key. - {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, + {"", "Come visit us on Discord!", "Come visit us on Discord!", -1}, }, }) } @@ -111,15 +113,15 @@ test_qt_linguist :: proc(t: ^testing.T) { plural = nil, // Default pluralizer tests = { // These are in the catalog. - {"Page", "Text for translation", "Tekst om te vertalen", 1}, - {"Page", "Also text to translate", "Ook tekst om te vertalen", 1}, - {"installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, + {"Page", "Text for translation", "Tekst om te vertalen", -1}, + {"Page", "Also text to translate", "Ook tekst om te vertalen", -1}, + {"installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur", -1}, {"apple_count", "%d apple(s)", "%d appel", 1}, {"apple_count", "%d apple(s)", "%d appels", 42}, // These aren't in the catalog, so should ruturn the key. - {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, - {"Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", 1}, + {"", "Come visit us on Discord!", "Come visit us on Discord!", -1}, + {"Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", -1}, }, }) } @@ -133,16 +135,16 @@ test_qt_linguist_merge_sections :: proc(t: ^testing.T) { options = {merge_sections = true}, tests = { // All of them are now in section "", lookup with original section should return the key. - {"", "Text for translation", "Tekst om te vertalen", 1}, - {"", "Also text to translate", "Ook tekst om te vertalen", 1}, - {"", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, + {"", "Text for translation", "Tekst om te vertalen", -1}, + {"", "Also text to translate", "Ook tekst om te vertalen", -1}, + {"", "99 bottles of beer on the wall", "99 flessen bier op de muur", -1}, {"", "%d apple(s)", "%d appel", 1}, {"", "%d apple(s)", "%d appels", 42}, // All of them are now in section "", lookup with original section should return the key. - {"Page", "Text for translation", "Text for translation", 1}, - {"Page", "Also text to translate", "Also text to translate", 1}, - {"installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall", 1}, + {"Page", "Text for translation", "Text for translation", -1}, + {"Page", "Also text to translate", "Also text to translate", -1}, + {"installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall", -1}, {"apple_count", "%d apple(s)", "%d apple(s)", 1}, {"apple_count", "%d apple(s)", "%d apple(s)", 42}, }, @@ -175,7 +177,7 @@ test :: proc(t: ^testing.T, suite: Test_Suite, loc := #caller_location) { if err == .None { for test in suite.tests { - val := T(test.section, test.key, test.n, cat) + val := test.n > -1 ? Tn(test.section, test.key, test.n, cat): T(test.section, test.key, cat) testing.expectf(t, val == test.val, "Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val, loc=loc) } } diff --git a/tests/core/unicode/test_core_unicode.odin b/tests/core/unicode/test_core_unicode.odin new file mode 100644 index 000000000..a1f6ac934 --- /dev/null +++ b/tests/core/unicode/test_core_unicode.odin @@ -0,0 +1,135 @@ +package test_core_unicode + +import "core:log" +import "core:testing" +import "core:unicode/utf8" + +Test_Case :: struct { + str: string, + expected_clusters: int, +} + +run_test_cases :: proc(t: ^testing.T, test_cases: []Test_Case, loc := #caller_location) { + failed := 0 + for c, i in test_cases { + log.debugf("(#% 4i) %q ...", i, c.str) + result, _, _ := utf8.grapheme_count(c.str) + if !testing.expectf(t, result == c.expected_clusters, + "(#% 4i) graphemes: %i != %i, %q %s", i, result, c.expected_clusters, c.str, c.str, + loc = loc) + { + failed += 1 + } + } + + log.logf(.Error if failed > 0 else .Info, "% 4i/% 4i test cases failed.", failed, len(test_cases), location = loc) +} + +@test +test_official_gcb_cases :: proc(t: ^testing.T) { + run_test_cases(t, official_grapheme_break_test_cases) +} + +@test +test_official_emoji_cases :: proc(t: ^testing.T) { + run_test_cases(t, official_emoji_test_cases) +} + +@test +test_grapheme_byte_index_segmentation :: proc(t: ^testing.T) { + SAMPLE_1 :: "\U0001F600" + SAMPLE_2 :: "\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F" + SAMPLE_3 :: "\U0001F468\U0001F3FB\u200D\U0001F9B0" + + str := SAMPLE_1 + SAMPLE_2 + SAMPLE_3 + SAMPLE_2 + SAMPLE_1 + + graphemes, _, _, _ := utf8.decode_grapheme_clusters(str) + defer delete(graphemes) + + defer if testing.failed(t) { + log.infof("%#v\n%q\n%v", graphemes, str, transmute([]u8)str) + } + if !testing.expect_value(t, len(graphemes), 5) { + return + } + + testing.expect_value(t, graphemes[0].rune_index, 0) + testing.expect_value(t, graphemes[1].rune_index, 1) + testing.expect_value(t, graphemes[2].rune_index, 8) + testing.expect_value(t, graphemes[3].rune_index, 12) + testing.expect_value(t, graphemes[4].rune_index, 19) + + grapheme_1 := str[graphemes[0].byte_index:graphemes[1].byte_index] + grapheme_2 := str[graphemes[1].byte_index:graphemes[2].byte_index] + grapheme_3 := str[graphemes[2].byte_index:graphemes[3].byte_index] + grapheme_4 := str[graphemes[3].byte_index:graphemes[4].byte_index] + grapheme_5 := str[graphemes[4].byte_index:] + + testing.expectf(t, grapheme_1 == SAMPLE_1, "expected %q, got %q", SAMPLE_1, grapheme_1) + testing.expectf(t, grapheme_2 == SAMPLE_2, "expected %q, got %q", SAMPLE_2, grapheme_2) + testing.expectf(t, grapheme_3 == SAMPLE_3, "expected %q, got %q", SAMPLE_3, grapheme_3) + testing.expectf(t, grapheme_4 == SAMPLE_2, "expected %q, got %q", SAMPLE_2, grapheme_2) + testing.expectf(t, grapheme_5 == SAMPLE_1, "expected %q, got %q", SAMPLE_1, grapheme_1) +} + +@test +test_width :: proc(t: ^testing.T) { + { + str := "He\u200dllo" + graphemes, _, width := utf8.grapheme_count(str) + testing.expect_value(t, graphemes, 5) + testing.expect_value(t, width, 5) + } + + { + // Note that a zero-width space is still considered a grapheme as far + // as the specification is concerned. + str := "He\u200bllo" + graphemes, _, width := utf8.grapheme_count(str) + testing.expect_value(t, graphemes, 6) + testing.expect_value(t, width, 5) + } + + { + str := "\U0001F926\U0001F3FC\u200D\u2642" + graphemes, _, width := utf8.grapheme_count(str) + testing.expect_value(t, graphemes, 1) + testing.expect_value(t, width, 2) + } + + { + str := "H̷e̶l̵l̸o̴p̵e̷ ̸w̶o̸r̵l̶d̵!̴" + graphemes, _, width := utf8.grapheme_count(str) + testing.expect_value(t, graphemes, 14) + testing.expect_value(t, width, 14) + } + + { + str := "aカ.ヒフ" + graphemes, grapheme_count, _, width := utf8.decode_grapheme_clusters(str) + defer delete(graphemes) + testing.expect_value(t, grapheme_count, 5) + testing.expect_value(t, width, 8) + if grapheme_count == 5 { + testing.expect_value(t, graphemes[0].width, 1) + testing.expect_value(t, graphemes[1].width, 2) + testing.expect_value(t, graphemes[2].width, 1) + testing.expect_value(t, graphemes[3].width, 2) + testing.expect_value(t, graphemes[4].width, 2) + } + } + + { + str := "いろはにほへ" + graphemes, _, width := utf8.grapheme_count(str) + testing.expect_value(t, graphemes, 6) + testing.expect_value(t, width, 12) + } + + { + str := "舍利弗,是諸法空相,不生不滅,不垢不淨,不增不減。" + graphemes, _, width := utf8.grapheme_count(str) + testing.expect_value(t, graphemes, 25) + testing.expect_value(t, width, 50) + } +} diff --git a/tests/core/unicode/test_core_unicode_data.odin b/tests/core/unicode/test_core_unicode_data.odin new file mode 100644 index 000000000..594af3c65 --- /dev/null +++ b/tests/core/unicode/test_core_unicode_data.odin @@ -0,0 +1,4912 @@ +package test_core_unicode + +// This file contains test data licensed under the Unicode Consortium that has +// been converted to a format that will work within Odin. +// +// The Unicode license to which said test data is under is included verbatim +// within this file, along with the names of the files from which the test data +// has been converted. + +/* +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. +*/ + + +// https://unicode.org/Public/15.1.0/ucd/auxiliary/GraphemeBreakTest.txt +// +// GraphemeBreakTest-15.1.0.txt +// Date: 2023-08-07, 15:52:55 GMT +// © 2023 Unicode®, Inc. +// Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. +// For terms of use, see https://www.unicode.org/terms_of_use.html +@(rodata) +official_grapheme_break_test_cases := []Test_Case { +{"\u0020\u0020", 2}, +{"\u0020\u0308\u0020", 2}, +{"\u0020\u000D", 2}, +{"\u0020\u0308\u000D", 2}, +{"\u0020\u000A", 2}, +{"\u0020\u0308\u000A", 2}, +{"\u0020\u0001", 2}, +{"\u0020\u0308\u0001", 2}, +{"\u0020\u034F", 1}, +{"\u0020\u0308\u034F", 1}, +{"\u0020\U0001F1E6", 2}, +{"\u0020\u0308\U0001F1E6", 2}, +{"\u0020\u0600", 2}, +{"\u0020\u0308\u0600", 2}, +{"\u0020\u0A03", 1}, +{"\u0020\u0308\u0A03", 1}, +{"\u0020\u1100", 2}, +{"\u0020\u0308\u1100", 2}, +{"\u0020\u1160", 2}, +{"\u0020\u0308\u1160", 2}, +{"\u0020\u11A8", 2}, +{"\u0020\u0308\u11A8", 2}, +{"\u0020\uAC00", 2}, +{"\u0020\u0308\uAC00", 2}, +{"\u0020\uAC01", 2}, +{"\u0020\u0308\uAC01", 2}, +{"\u0020\u0900", 1}, +{"\u0020\u0308\u0900", 1}, +{"\u0020\u0903", 1}, +{"\u0020\u0308\u0903", 1}, +{"\u0020\u0904", 2}, +{"\u0020\u0308\u0904", 2}, +{"\u0020\u0D4E", 2}, +{"\u0020\u0308\u0D4E", 2}, +{"\u0020\u0915", 2}, +{"\u0020\u0308\u0915", 2}, +{"\u0020\u231A", 2}, +{"\u0020\u0308\u231A", 2}, +{"\u0020\u0300", 1}, +{"\u0020\u0308\u0300", 1}, +{"\u0020\u093C", 1}, +{"\u0020\u0308\u093C", 1}, +{"\u0020\u094D", 1}, +{"\u0020\u0308\u094D", 1}, +{"\u0020\u200D", 1}, +{"\u0020\u0308\u200D", 1}, +{"\u0020\u0378", 2}, +{"\u0020\u0308\u0378", 2}, +{"\u000D\u0020", 2}, +{"\u000D\u0308\u0020", 3}, +{"\u000D\u000D", 2}, +{"\u000D\u0308\u000D", 3}, +{"\u000D\u000A", 1}, +{"\u000D\u0308\u000A", 3}, +{"\u000D\u0001", 2}, +{"\u000D\u0308\u0001", 3}, +{"\u000D\u034F", 2}, +{"\u000D\u0308\u034F", 2}, +{"\u000D\U0001F1E6", 2}, +{"\u000D\u0308\U0001F1E6", 3}, +{"\u000D\u0600", 2}, +{"\u000D\u0308\u0600", 3}, +{"\u000D\u0A03", 2}, +{"\u000D\u0308\u0A03", 2}, +{"\u000D\u1100", 2}, +{"\u000D\u0308\u1100", 3}, +{"\u000D\u1160", 2}, +{"\u000D\u0308\u1160", 3}, +{"\u000D\u11A8", 2}, +{"\u000D\u0308\u11A8", 3}, +{"\u000D\uAC00", 2}, +{"\u000D\u0308\uAC00", 3}, +{"\u000D\uAC01", 2}, +{"\u000D\u0308\uAC01", 3}, +{"\u000D\u0900", 2}, +{"\u000D\u0308\u0900", 2}, +{"\u000D\u0903", 2}, +{"\u000D\u0308\u0903", 2}, +{"\u000D\u0904", 2}, +{"\u000D\u0308\u0904", 3}, +{"\u000D\u0D4E", 2}, +{"\u000D\u0308\u0D4E", 3}, +{"\u000D\u0915", 2}, +{"\u000D\u0308\u0915", 3}, +{"\u000D\u231A", 2}, +{"\u000D\u0308\u231A", 3}, +{"\u000D\u0300", 2}, +{"\u000D\u0308\u0300", 2}, +{"\u000D\u093C", 2}, +{"\u000D\u0308\u093C", 2}, +{"\u000D\u094D", 2}, +{"\u000D\u0308\u094D", 2}, +{"\u000D\u200D", 2}, +{"\u000D\u0308\u200D", 2}, +{"\u000D\u0378", 2}, +{"\u000D\u0308\u0378", 3}, +{"\u000A\u0020", 2}, +{"\u000A\u0308\u0020", 3}, +{"\u000A\u000D", 2}, +{"\u000A\u0308\u000D", 3}, +{"\u000A\u000A", 2}, +{"\u000A\u0308\u000A", 3}, +{"\u000A\u0001", 2}, +{"\u000A\u0308\u0001", 3}, +{"\u000A\u034F", 2}, +{"\u000A\u0308\u034F", 2}, +{"\u000A\U0001F1E6", 2}, +{"\u000A\u0308\U0001F1E6", 3}, +{"\u000A\u0600", 2}, +{"\u000A\u0308\u0600", 3}, +{"\u000A\u0A03", 2}, +{"\u000A\u0308\u0A03", 2}, +{"\u000A\u1100", 2}, +{"\u000A\u0308\u1100", 3}, +{"\u000A\u1160", 2}, +{"\u000A\u0308\u1160", 3}, +{"\u000A\u11A8", 2}, +{"\u000A\u0308\u11A8", 3}, +{"\u000A\uAC00", 2}, +{"\u000A\u0308\uAC00", 3}, +{"\u000A\uAC01", 2}, +{"\u000A\u0308\uAC01", 3}, +{"\u000A\u0900", 2}, +{"\u000A\u0308\u0900", 2}, +{"\u000A\u0903", 2}, +{"\u000A\u0308\u0903", 2}, +{"\u000A\u0904", 2}, +{"\u000A\u0308\u0904", 3}, +{"\u000A\u0D4E", 2}, +{"\u000A\u0308\u0D4E", 3}, +{"\u000A\u0915", 2}, +{"\u000A\u0308\u0915", 3}, +{"\u000A\u231A", 2}, +{"\u000A\u0308\u231A", 3}, +{"\u000A\u0300", 2}, +{"\u000A\u0308\u0300", 2}, +{"\u000A\u093C", 2}, +{"\u000A\u0308\u093C", 2}, +{"\u000A\u094D", 2}, +{"\u000A\u0308\u094D", 2}, +{"\u000A\u200D", 2}, +{"\u000A\u0308\u200D", 2}, +{"\u000A\u0378", 2}, +{"\u000A\u0308\u0378", 3}, +{"\u0001\u0020", 2}, +{"\u0001\u0308\u0020", 3}, +{"\u0001\u000D", 2}, +{"\u0001\u0308\u000D", 3}, +{"\u0001\u000A", 2}, +{"\u0001\u0308\u000A", 3}, +{"\u0001\u0001", 2}, +{"\u0001\u0308\u0001", 3}, +{"\u0001\u034F", 2}, +{"\u0001\u0308\u034F", 2}, +{"\u0001\U0001F1E6", 2}, +{"\u0001\u0308\U0001F1E6", 3}, +{"\u0001\u0600", 2}, +{"\u0001\u0308\u0600", 3}, +{"\u0001\u0A03", 2}, +{"\u0001\u0308\u0A03", 2}, +{"\u0001\u1100", 2}, +{"\u0001\u0308\u1100", 3}, +{"\u0001\u1160", 2}, +{"\u0001\u0308\u1160", 3}, +{"\u0001\u11A8", 2}, +{"\u0001\u0308\u11A8", 3}, +{"\u0001\uAC00", 2}, +{"\u0001\u0308\uAC00", 3}, +{"\u0001\uAC01", 2}, +{"\u0001\u0308\uAC01", 3}, +{"\u0001\u0900", 2}, +{"\u0001\u0308\u0900", 2}, +{"\u0001\u0903", 2}, +{"\u0001\u0308\u0903", 2}, +{"\u0001\u0904", 2}, +{"\u0001\u0308\u0904", 3}, +{"\u0001\u0D4E", 2}, +{"\u0001\u0308\u0D4E", 3}, +{"\u0001\u0915", 2}, +{"\u0001\u0308\u0915", 3}, +{"\u0001\u231A", 2}, +{"\u0001\u0308\u231A", 3}, +{"\u0001\u0300", 2}, +{"\u0001\u0308\u0300", 2}, +{"\u0001\u093C", 2}, +{"\u0001\u0308\u093C", 2}, +{"\u0001\u094D", 2}, +{"\u0001\u0308\u094D", 2}, +{"\u0001\u200D", 2}, +{"\u0001\u0308\u200D", 2}, +{"\u0001\u0378", 2}, +{"\u0001\u0308\u0378", 3}, +{"\u034F\u0020", 2}, +{"\u034F\u0308\u0020", 2}, +{"\u034F\u000D", 2}, +{"\u034F\u0308\u000D", 2}, +{"\u034F\u000A", 2}, +{"\u034F\u0308\u000A", 2}, +{"\u034F\u0001", 2}, +{"\u034F\u0308\u0001", 2}, +{"\u034F\u034F", 1}, +{"\u034F\u0308\u034F", 1}, +{"\u034F\U0001F1E6", 2}, +{"\u034F\u0308\U0001F1E6", 2}, +{"\u034F\u0600", 2}, +{"\u034F\u0308\u0600", 2}, +{"\u034F\u0A03", 1}, +{"\u034F\u0308\u0A03", 1}, +{"\u034F\u1100", 2}, +{"\u034F\u0308\u1100", 2}, +{"\u034F\u1160", 2}, +{"\u034F\u0308\u1160", 2}, +{"\u034F\u11A8", 2}, +{"\u034F\u0308\u11A8", 2}, +{"\u034F\uAC00", 2}, +{"\u034F\u0308\uAC00", 2}, +{"\u034F\uAC01", 2}, +{"\u034F\u0308\uAC01", 2}, +{"\u034F\u0900", 1}, +{"\u034F\u0308\u0900", 1}, +{"\u034F\u0903", 1}, +{"\u034F\u0308\u0903", 1}, +{"\u034F\u0904", 2}, +{"\u034F\u0308\u0904", 2}, +{"\u034F\u0D4E", 2}, +{"\u034F\u0308\u0D4E", 2}, +{"\u034F\u0915", 2}, +{"\u034F\u0308\u0915", 2}, +{"\u034F\u231A", 2}, +{"\u034F\u0308\u231A", 2}, +{"\u034F\u0300", 1}, +{"\u034F\u0308\u0300", 1}, +{"\u034F\u093C", 1}, +{"\u034F\u0308\u093C", 1}, +{"\u034F\u094D", 1}, +{"\u034F\u0308\u094D", 1}, +{"\u034F\u200D", 1}, +{"\u034F\u0308\u200D", 1}, +{"\u034F\u0378", 2}, +{"\u034F\u0308\u0378", 2}, +{"\U0001F1E6\u0020", 2}, +{"\U0001F1E6\u0308\u0020", 2}, +{"\U0001F1E6\u000D", 2}, +{"\U0001F1E6\u0308\u000D", 2}, +{"\U0001F1E6\u000A", 2}, +{"\U0001F1E6\u0308\u000A", 2}, +{"\U0001F1E6\u0001", 2}, +{"\U0001F1E6\u0308\u0001", 2}, +{"\U0001F1E6\u034F", 1}, +{"\U0001F1E6\u0308\u034F", 1}, +{"\U0001F1E6\U0001F1E6", 1}, +{"\U0001F1E6\u0308\U0001F1E6", 2}, +{"\U0001F1E6\u0600", 2}, +{"\U0001F1E6\u0308\u0600", 2}, +{"\U0001F1E6\u0A03", 1}, +{"\U0001F1E6\u0308\u0A03", 1}, +{"\U0001F1E6\u1100", 2}, +{"\U0001F1E6\u0308\u1100", 2}, +{"\U0001F1E6\u1160", 2}, +{"\U0001F1E6\u0308\u1160", 2}, +{"\U0001F1E6\u11A8", 2}, +{"\U0001F1E6\u0308\u11A8", 2}, +{"\U0001F1E6\uAC00", 2}, +{"\U0001F1E6\u0308\uAC00", 2}, +{"\U0001F1E6\uAC01", 2}, +{"\U0001F1E6\u0308\uAC01", 2}, +{"\U0001F1E6\u0900", 1}, +{"\U0001F1E6\u0308\u0900", 1}, +{"\U0001F1E6\u0903", 1}, +{"\U0001F1E6\u0308\u0903", 1}, +{"\U0001F1E6\u0904", 2}, +{"\U0001F1E6\u0308\u0904", 2}, +{"\U0001F1E6\u0D4E", 2}, +{"\U0001F1E6\u0308\u0D4E", 2}, +{"\U0001F1E6\u0915", 2}, +{"\U0001F1E6\u0308\u0915", 2}, +{"\U0001F1E6\u231A", 2}, +{"\U0001F1E6\u0308\u231A", 2}, +{"\U0001F1E6\u0300", 1}, +{"\U0001F1E6\u0308\u0300", 1}, +{"\U0001F1E6\u093C", 1}, +{"\U0001F1E6\u0308\u093C", 1}, +{"\U0001F1E6\u094D", 1}, +{"\U0001F1E6\u0308\u094D", 1}, +{"\U0001F1E6\u200D", 1}, +{"\U0001F1E6\u0308\u200D", 1}, +{"\U0001F1E6\u0378", 2}, +{"\U0001F1E6\u0308\u0378", 2}, +{"\u0600\u0020", 1}, +{"\u0600\u0308\u0020", 2}, +{"\u0600\u000D", 2}, +{"\u0600\u0308\u000D", 2}, +{"\u0600\u000A", 2}, +{"\u0600\u0308\u000A", 2}, +{"\u0600\u0001", 2}, +{"\u0600\u0308\u0001", 2}, +{"\u0600\u034F", 1}, +{"\u0600\u0308\u034F", 1}, +{"\u0600\U0001F1E6", 1}, +{"\u0600\u0308\U0001F1E6", 2}, +{"\u0600\u0600", 1}, +{"\u0600\u0308\u0600", 2}, +{"\u0600\u0A03", 1}, +{"\u0600\u0308\u0A03", 1}, +{"\u0600\u1100", 1}, +{"\u0600\u0308\u1100", 2}, +{"\u0600\u1160", 1}, +{"\u0600\u0308\u1160", 2}, +{"\u0600\u11A8", 1}, +{"\u0600\u0308\u11A8", 2}, +{"\u0600\uAC00", 1}, +{"\u0600\u0308\uAC00", 2}, +{"\u0600\uAC01", 1}, +{"\u0600\u0308\uAC01", 2}, +{"\u0600\u0900", 1}, +{"\u0600\u0308\u0900", 1}, +{"\u0600\u0903", 1}, +{"\u0600\u0308\u0903", 1}, +{"\u0600\u0904", 1}, +{"\u0600\u0308\u0904", 2}, +{"\u0600\u0D4E", 1}, +{"\u0600\u0308\u0D4E", 2}, +{"\u0600\u0915", 1}, +{"\u0600\u0308\u0915", 2}, +{"\u0600\u231A", 1}, +{"\u0600\u0308\u231A", 2}, +{"\u0600\u0300", 1}, +{"\u0600\u0308\u0300", 1}, +{"\u0600\u093C", 1}, +{"\u0600\u0308\u093C", 1}, +{"\u0600\u094D", 1}, +{"\u0600\u0308\u094D", 1}, +{"\u0600\u200D", 1}, +{"\u0600\u0308\u200D", 1}, +{"\u0600\u0378", 1}, +{"\u0600\u0308\u0378", 2}, +{"\u0A03\u0020", 2}, +{"\u0A03\u0308\u0020", 2}, +{"\u0A03\u000D", 2}, +{"\u0A03\u0308\u000D", 2}, +{"\u0A03\u000A", 2}, +{"\u0A03\u0308\u000A", 2}, +{"\u0A03\u0001", 2}, +{"\u0A03\u0308\u0001", 2}, +{"\u0A03\u034F", 1}, +{"\u0A03\u0308\u034F", 1}, +{"\u0A03\U0001F1E6", 2}, +{"\u0A03\u0308\U0001F1E6", 2}, +{"\u0A03\u0600", 2}, +{"\u0A03\u0308\u0600", 2}, +{"\u0A03\u0A03", 1}, +{"\u0A03\u0308\u0A03", 1}, +{"\u0A03\u1100", 2}, +{"\u0A03\u0308\u1100", 2}, +{"\u0A03\u1160", 2}, +{"\u0A03\u0308\u1160", 2}, +{"\u0A03\u11A8", 2}, +{"\u0A03\u0308\u11A8", 2}, +{"\u0A03\uAC00", 2}, +{"\u0A03\u0308\uAC00", 2}, +{"\u0A03\uAC01", 2}, +{"\u0A03\u0308\uAC01", 2}, +{"\u0A03\u0900", 1}, +{"\u0A03\u0308\u0900", 1}, +{"\u0A03\u0903", 1}, +{"\u0A03\u0308\u0903", 1}, +{"\u0A03\u0904", 2}, +{"\u0A03\u0308\u0904", 2}, +{"\u0A03\u0D4E", 2}, +{"\u0A03\u0308\u0D4E", 2}, +{"\u0A03\u0915", 2}, +{"\u0A03\u0308\u0915", 2}, +{"\u0A03\u231A", 2}, +{"\u0A03\u0308\u231A", 2}, +{"\u0A03\u0300", 1}, +{"\u0A03\u0308\u0300", 1}, +{"\u0A03\u093C", 1}, +{"\u0A03\u0308\u093C", 1}, +{"\u0A03\u094D", 1}, +{"\u0A03\u0308\u094D", 1}, +{"\u0A03\u200D", 1}, +{"\u0A03\u0308\u200D", 1}, +{"\u0A03\u0378", 2}, +{"\u0A03\u0308\u0378", 2}, +{"\u1100\u0020", 2}, +{"\u1100\u0308\u0020", 2}, +{"\u1100\u000D", 2}, +{"\u1100\u0308\u000D", 2}, +{"\u1100\u000A", 2}, +{"\u1100\u0308\u000A", 2}, +{"\u1100\u0001", 2}, +{"\u1100\u0308\u0001", 2}, +{"\u1100\u034F", 1}, +{"\u1100\u0308\u034F", 1}, +{"\u1100\U0001F1E6", 2}, +{"\u1100\u0308\U0001F1E6", 2}, +{"\u1100\u0600", 2}, +{"\u1100\u0308\u0600", 2}, +{"\u1100\u0A03", 1}, +{"\u1100\u0308\u0A03", 1}, +{"\u1100\u1100", 1}, +{"\u1100\u0308\u1100", 2}, +{"\u1100\u1160", 1}, +{"\u1100\u0308\u1160", 2}, +{"\u1100\u11A8", 2}, +{"\u1100\u0308\u11A8", 2}, +{"\u1100\uAC00", 1}, +{"\u1100\u0308\uAC00", 2}, +{"\u1100\uAC01", 1}, +{"\u1100\u0308\uAC01", 2}, +{"\u1100\u0900", 1}, +{"\u1100\u0308\u0900", 1}, +{"\u1100\u0903", 1}, +{"\u1100\u0308\u0903", 1}, +{"\u1100\u0904", 2}, +{"\u1100\u0308\u0904", 2}, +{"\u1100\u0D4E", 2}, +{"\u1100\u0308\u0D4E", 2}, +{"\u1100\u0915", 2}, +{"\u1100\u0308\u0915", 2}, +{"\u1100\u231A", 2}, +{"\u1100\u0308\u231A", 2}, +{"\u1100\u0300", 1}, +{"\u1100\u0308\u0300", 1}, +{"\u1100\u093C", 1}, +{"\u1100\u0308\u093C", 1}, +{"\u1100\u094D", 1}, +{"\u1100\u0308\u094D", 1}, +{"\u1100\u200D", 1}, +{"\u1100\u0308\u200D", 1}, +{"\u1100\u0378", 2}, +{"\u1100\u0308\u0378", 2}, +{"\u1160\u0020", 2}, +{"\u1160\u0308\u0020", 2}, +{"\u1160\u000D", 2}, +{"\u1160\u0308\u000D", 2}, +{"\u1160\u000A", 2}, +{"\u1160\u0308\u000A", 2}, +{"\u1160\u0001", 2}, +{"\u1160\u0308\u0001", 2}, +{"\u1160\u034F", 1}, +{"\u1160\u0308\u034F", 1}, +{"\u1160\U0001F1E6", 2}, +{"\u1160\u0308\U0001F1E6", 2}, +{"\u1160\u0600", 2}, +{"\u1160\u0308\u0600", 2}, +{"\u1160\u0A03", 1}, +{"\u1160\u0308\u0A03", 1}, +{"\u1160\u1100", 2}, +{"\u1160\u0308\u1100", 2}, +{"\u1160\u1160", 1}, +{"\u1160\u0308\u1160", 2}, +{"\u1160\u11A8", 1}, +{"\u1160\u0308\u11A8", 2}, +{"\u1160\uAC00", 2}, +{"\u1160\u0308\uAC00", 2}, +{"\u1160\uAC01", 2}, +{"\u1160\u0308\uAC01", 2}, +{"\u1160\u0900", 1}, +{"\u1160\u0308\u0900", 1}, +{"\u1160\u0903", 1}, +{"\u1160\u0308\u0903", 1}, +{"\u1160\u0904", 2}, +{"\u1160\u0308\u0904", 2}, +{"\u1160\u0D4E", 2}, +{"\u1160\u0308\u0D4E", 2}, +{"\u1160\u0915", 2}, +{"\u1160\u0308\u0915", 2}, +{"\u1160\u231A", 2}, +{"\u1160\u0308\u231A", 2}, +{"\u1160\u0300", 1}, +{"\u1160\u0308\u0300", 1}, +{"\u1160\u093C", 1}, +{"\u1160\u0308\u093C", 1}, +{"\u1160\u094D", 1}, +{"\u1160\u0308\u094D", 1}, +{"\u1160\u200D", 1}, +{"\u1160\u0308\u200D", 1}, +{"\u1160\u0378", 2}, +{"\u1160\u0308\u0378", 2}, +{"\u11A8\u0020", 2}, +{"\u11A8\u0308\u0020", 2}, +{"\u11A8\u000D", 2}, +{"\u11A8\u0308\u000D", 2}, +{"\u11A8\u000A", 2}, +{"\u11A8\u0308\u000A", 2}, +{"\u11A8\u0001", 2}, +{"\u11A8\u0308\u0001", 2}, +{"\u11A8\u034F", 1}, +{"\u11A8\u0308\u034F", 1}, +{"\u11A8\U0001F1E6", 2}, +{"\u11A8\u0308\U0001F1E6", 2}, +{"\u11A8\u0600", 2}, +{"\u11A8\u0308\u0600", 2}, +{"\u11A8\u0A03", 1}, +{"\u11A8\u0308\u0A03", 1}, +{"\u11A8\u1100", 2}, +{"\u11A8\u0308\u1100", 2}, +{"\u11A8\u1160", 2}, +{"\u11A8\u0308\u1160", 2}, +{"\u11A8\u11A8", 1}, +{"\u11A8\u0308\u11A8", 2}, +{"\u11A8\uAC00", 2}, +{"\u11A8\u0308\uAC00", 2}, +{"\u11A8\uAC01", 2}, +{"\u11A8\u0308\uAC01", 2}, +{"\u11A8\u0900", 1}, +{"\u11A8\u0308\u0900", 1}, +{"\u11A8\u0903", 1}, +{"\u11A8\u0308\u0903", 1}, +{"\u11A8\u0904", 2}, +{"\u11A8\u0308\u0904", 2}, +{"\u11A8\u0D4E", 2}, +{"\u11A8\u0308\u0D4E", 2}, +{"\u11A8\u0915", 2}, +{"\u11A8\u0308\u0915", 2}, +{"\u11A8\u231A", 2}, +{"\u11A8\u0308\u231A", 2}, +{"\u11A8\u0300", 1}, +{"\u11A8\u0308\u0300", 1}, +{"\u11A8\u093C", 1}, +{"\u11A8\u0308\u093C", 1}, +{"\u11A8\u094D", 1}, +{"\u11A8\u0308\u094D", 1}, +{"\u11A8\u200D", 1}, +{"\u11A8\u0308\u200D", 1}, +{"\u11A8\u0378", 2}, +{"\u11A8\u0308\u0378", 2}, +{"\uAC00\u0020", 2}, +{"\uAC00\u0308\u0020", 2}, +{"\uAC00\u000D", 2}, +{"\uAC00\u0308\u000D", 2}, +{"\uAC00\u000A", 2}, +{"\uAC00\u0308\u000A", 2}, +{"\uAC00\u0001", 2}, +{"\uAC00\u0308\u0001", 2}, +{"\uAC00\u034F", 1}, +{"\uAC00\u0308\u034F", 1}, +{"\uAC00\U0001F1E6", 2}, +{"\uAC00\u0308\U0001F1E6", 2}, +{"\uAC00\u0600", 2}, +{"\uAC00\u0308\u0600", 2}, +{"\uAC00\u0A03", 1}, +{"\uAC00\u0308\u0A03", 1}, +{"\uAC00\u1100", 2}, +{"\uAC00\u0308\u1100", 2}, +{"\uAC00\u1160", 1}, +{"\uAC00\u0308\u1160", 2}, +{"\uAC00\u11A8", 1}, +{"\uAC00\u0308\u11A8", 2}, +{"\uAC00\uAC00", 2}, +{"\uAC00\u0308\uAC00", 2}, +{"\uAC00\uAC01", 2}, +{"\uAC00\u0308\uAC01", 2}, +{"\uAC00\u0900", 1}, +{"\uAC00\u0308\u0900", 1}, +{"\uAC00\u0903", 1}, +{"\uAC00\u0308\u0903", 1}, +{"\uAC00\u0904", 2}, +{"\uAC00\u0308\u0904", 2}, +{"\uAC00\u0D4E", 2}, +{"\uAC00\u0308\u0D4E", 2}, +{"\uAC00\u0915", 2}, +{"\uAC00\u0308\u0915", 2}, +{"\uAC00\u231A", 2}, +{"\uAC00\u0308\u231A", 2}, +{"\uAC00\u0300", 1}, +{"\uAC00\u0308\u0300", 1}, +{"\uAC00\u093C", 1}, +{"\uAC00\u0308\u093C", 1}, +{"\uAC00\u094D", 1}, +{"\uAC00\u0308\u094D", 1}, +{"\uAC00\u200D", 1}, +{"\uAC00\u0308\u200D", 1}, +{"\uAC00\u0378", 2}, +{"\uAC00\u0308\u0378", 2}, +{"\uAC01\u0020", 2}, +{"\uAC01\u0308\u0020", 2}, +{"\uAC01\u000D", 2}, +{"\uAC01\u0308\u000D", 2}, +{"\uAC01\u000A", 2}, +{"\uAC01\u0308\u000A", 2}, +{"\uAC01\u0001", 2}, +{"\uAC01\u0308\u0001", 2}, +{"\uAC01\u034F", 1}, +{"\uAC01\u0308\u034F", 1}, +{"\uAC01\U0001F1E6", 2}, +{"\uAC01\u0308\U0001F1E6", 2}, +{"\uAC01\u0600", 2}, +{"\uAC01\u0308\u0600", 2}, +{"\uAC01\u0A03", 1}, +{"\uAC01\u0308\u0A03", 1}, +{"\uAC01\u1100", 2}, +{"\uAC01\u0308\u1100", 2}, +{"\uAC01\u1160", 2}, +{"\uAC01\u0308\u1160", 2}, +{"\uAC01\u11A8", 1}, +{"\uAC01\u0308\u11A8", 2}, +{"\uAC01\uAC00", 2}, +{"\uAC01\u0308\uAC00", 2}, +{"\uAC01\uAC01", 2}, +{"\uAC01\u0308\uAC01", 2}, +{"\uAC01\u0900", 1}, +{"\uAC01\u0308\u0900", 1}, +{"\uAC01\u0903", 1}, +{"\uAC01\u0308\u0903", 1}, +{"\uAC01\u0904", 2}, +{"\uAC01\u0308\u0904", 2}, +{"\uAC01\u0D4E", 2}, +{"\uAC01\u0308\u0D4E", 2}, +{"\uAC01\u0915", 2}, +{"\uAC01\u0308\u0915", 2}, +{"\uAC01\u231A", 2}, +{"\uAC01\u0308\u231A", 2}, +{"\uAC01\u0300", 1}, +{"\uAC01\u0308\u0300", 1}, +{"\uAC01\u093C", 1}, +{"\uAC01\u0308\u093C", 1}, +{"\uAC01\u094D", 1}, +{"\uAC01\u0308\u094D", 1}, +{"\uAC01\u200D", 1}, +{"\uAC01\u0308\u200D", 1}, +{"\uAC01\u0378", 2}, +{"\uAC01\u0308\u0378", 2}, +{"\u0900\u0020", 2}, +{"\u0900\u0308\u0020", 2}, +{"\u0900\u000D", 2}, +{"\u0900\u0308\u000D", 2}, +{"\u0900\u000A", 2}, +{"\u0900\u0308\u000A", 2}, +{"\u0900\u0001", 2}, +{"\u0900\u0308\u0001", 2}, +{"\u0900\u034F", 1}, +{"\u0900\u0308\u034F", 1}, +{"\u0900\U0001F1E6", 2}, +{"\u0900\u0308\U0001F1E6", 2}, +{"\u0900\u0600", 2}, +{"\u0900\u0308\u0600", 2}, +{"\u0900\u0A03", 1}, +{"\u0900\u0308\u0A03", 1}, +{"\u0900\u1100", 2}, +{"\u0900\u0308\u1100", 2}, +{"\u0900\u1160", 2}, +{"\u0900\u0308\u1160", 2}, +{"\u0900\u11A8", 2}, +{"\u0900\u0308\u11A8", 2}, +{"\u0900\uAC00", 2}, +{"\u0900\u0308\uAC00", 2}, +{"\u0900\uAC01", 2}, +{"\u0900\u0308\uAC01", 2}, +{"\u0900\u0900", 1}, +{"\u0900\u0308\u0900", 1}, +{"\u0900\u0903", 1}, +{"\u0900\u0308\u0903", 1}, +{"\u0900\u0904", 2}, +{"\u0900\u0308\u0904", 2}, +{"\u0900\u0D4E", 2}, +{"\u0900\u0308\u0D4E", 2}, +{"\u0900\u0915", 2}, +{"\u0900\u0308\u0915", 2}, +{"\u0900\u231A", 2}, +{"\u0900\u0308\u231A", 2}, +{"\u0900\u0300", 1}, +{"\u0900\u0308\u0300", 1}, +{"\u0900\u093C", 1}, +{"\u0900\u0308\u093C", 1}, +{"\u0900\u094D", 1}, +{"\u0900\u0308\u094D", 1}, +{"\u0900\u200D", 1}, +{"\u0900\u0308\u200D", 1}, +{"\u0900\u0378", 2}, +{"\u0900\u0308\u0378", 2}, +{"\u0903\u0020", 2}, +{"\u0903\u0308\u0020", 2}, +{"\u0903\u000D", 2}, +{"\u0903\u0308\u000D", 2}, +{"\u0903\u000A", 2}, +{"\u0903\u0308\u000A", 2}, +{"\u0903\u0001", 2}, +{"\u0903\u0308\u0001", 2}, +{"\u0903\u034F", 1}, +{"\u0903\u0308\u034F", 1}, +{"\u0903\U0001F1E6", 2}, +{"\u0903\u0308\U0001F1E6", 2}, +{"\u0903\u0600", 2}, +{"\u0903\u0308\u0600", 2}, +{"\u0903\u0A03", 1}, +{"\u0903\u0308\u0A03", 1}, +{"\u0903\u1100", 2}, +{"\u0903\u0308\u1100", 2}, +{"\u0903\u1160", 2}, +{"\u0903\u0308\u1160", 2}, +{"\u0903\u11A8", 2}, +{"\u0903\u0308\u11A8", 2}, +{"\u0903\uAC00", 2}, +{"\u0903\u0308\uAC00", 2}, +{"\u0903\uAC01", 2}, +{"\u0903\u0308\uAC01", 2}, +{"\u0903\u0900", 1}, +{"\u0903\u0308\u0900", 1}, +{"\u0903\u0903", 1}, +{"\u0903\u0308\u0903", 1}, +{"\u0903\u0904", 2}, +{"\u0903\u0308\u0904", 2}, +{"\u0903\u0D4E", 2}, +{"\u0903\u0308\u0D4E", 2}, +{"\u0903\u0915", 2}, +{"\u0903\u0308\u0915", 2}, +{"\u0903\u231A", 2}, +{"\u0903\u0308\u231A", 2}, +{"\u0903\u0300", 1}, +{"\u0903\u0308\u0300", 1}, +{"\u0903\u093C", 1}, +{"\u0903\u0308\u093C", 1}, +{"\u0903\u094D", 1}, +{"\u0903\u0308\u094D", 1}, +{"\u0903\u200D", 1}, +{"\u0903\u0308\u200D", 1}, +{"\u0903\u0378", 2}, +{"\u0903\u0308\u0378", 2}, +{"\u0904\u0020", 2}, +{"\u0904\u0308\u0020", 2}, +{"\u0904\u000D", 2}, +{"\u0904\u0308\u000D", 2}, +{"\u0904\u000A", 2}, +{"\u0904\u0308\u000A", 2}, +{"\u0904\u0001", 2}, +{"\u0904\u0308\u0001", 2}, +{"\u0904\u034F", 1}, +{"\u0904\u0308\u034F", 1}, +{"\u0904\U0001F1E6", 2}, +{"\u0904\u0308\U0001F1E6", 2}, +{"\u0904\u0600", 2}, +{"\u0904\u0308\u0600", 2}, +{"\u0904\u0A03", 1}, +{"\u0904\u0308\u0A03", 1}, +{"\u0904\u1100", 2}, +{"\u0904\u0308\u1100", 2}, +{"\u0904\u1160", 2}, +{"\u0904\u0308\u1160", 2}, +{"\u0904\u11A8", 2}, +{"\u0904\u0308\u11A8", 2}, +{"\u0904\uAC00", 2}, +{"\u0904\u0308\uAC00", 2}, +{"\u0904\uAC01", 2}, +{"\u0904\u0308\uAC01", 2}, +{"\u0904\u0900", 1}, +{"\u0904\u0308\u0900", 1}, +{"\u0904\u0903", 1}, +{"\u0904\u0308\u0903", 1}, +{"\u0904\u0904", 2}, +{"\u0904\u0308\u0904", 2}, +{"\u0904\u0D4E", 2}, +{"\u0904\u0308\u0D4E", 2}, +{"\u0904\u0915", 2}, +{"\u0904\u0308\u0915", 2}, +{"\u0904\u231A", 2}, +{"\u0904\u0308\u231A", 2}, +{"\u0904\u0300", 1}, +{"\u0904\u0308\u0300", 1}, +{"\u0904\u093C", 1}, +{"\u0904\u0308\u093C", 1}, +{"\u0904\u094D", 1}, +{"\u0904\u0308\u094D", 1}, +{"\u0904\u200D", 1}, +{"\u0904\u0308\u200D", 1}, +{"\u0904\u0378", 2}, +{"\u0904\u0308\u0378", 2}, +{"\u0D4E\u0020", 1}, +{"\u0D4E\u0308\u0020", 2}, +{"\u0D4E\u000D", 2}, +{"\u0D4E\u0308\u000D", 2}, +{"\u0D4E\u000A", 2}, +{"\u0D4E\u0308\u000A", 2}, +{"\u0D4E\u0001", 2}, +{"\u0D4E\u0308\u0001", 2}, +{"\u0D4E\u034F", 1}, +{"\u0D4E\u0308\u034F", 1}, +{"\u0D4E\U0001F1E6", 1}, +{"\u0D4E\u0308\U0001F1E6", 2}, +{"\u0D4E\u0600", 1}, +{"\u0D4E\u0308\u0600", 2}, +{"\u0D4E\u0A03", 1}, +{"\u0D4E\u0308\u0A03", 1}, +{"\u0D4E\u1100", 1}, +{"\u0D4E\u0308\u1100", 2}, +{"\u0D4E\u1160", 1}, +{"\u0D4E\u0308\u1160", 2}, +{"\u0D4E\u11A8", 1}, +{"\u0D4E\u0308\u11A8", 2}, +{"\u0D4E\uAC00", 1}, +{"\u0D4E\u0308\uAC00", 2}, +{"\u0D4E\uAC01", 1}, +{"\u0D4E\u0308\uAC01", 2}, +{"\u0D4E\u0900", 1}, +{"\u0D4E\u0308\u0900", 1}, +{"\u0D4E\u0903", 1}, +{"\u0D4E\u0308\u0903", 1}, +{"\u0D4E\u0904", 1}, +{"\u0D4E\u0308\u0904", 2}, +{"\u0D4E\u0D4E", 1}, +{"\u0D4E\u0308\u0D4E", 2}, +{"\u0D4E\u0915", 1}, +{"\u0D4E\u0308\u0915", 2}, +{"\u0D4E\u231A", 1}, +{"\u0D4E\u0308\u231A", 2}, +{"\u0D4E\u0300", 1}, +{"\u0D4E\u0308\u0300", 1}, +{"\u0D4E\u093C", 1}, +{"\u0D4E\u0308\u093C", 1}, +{"\u0D4E\u094D", 1}, +{"\u0D4E\u0308\u094D", 1}, +{"\u0D4E\u200D", 1}, +{"\u0D4E\u0308\u200D", 1}, +{"\u0D4E\u0378", 1}, +{"\u0D4E\u0308\u0378", 2}, +{"\u0915\u0020", 2}, +{"\u0915\u0308\u0020", 2}, +{"\u0915\u000D", 2}, +{"\u0915\u0308\u000D", 2}, +{"\u0915\u000A", 2}, +{"\u0915\u0308\u000A", 2}, +{"\u0915\u0001", 2}, +{"\u0915\u0308\u0001", 2}, +{"\u0915\u034F", 1}, +{"\u0915\u0308\u034F", 1}, +{"\u0915\U0001F1E6", 2}, +{"\u0915\u0308\U0001F1E6", 2}, +{"\u0915\u0600", 2}, +{"\u0915\u0308\u0600", 2}, +{"\u0915\u0A03", 1}, +{"\u0915\u0308\u0A03", 1}, +{"\u0915\u1100", 2}, +{"\u0915\u0308\u1100", 2}, +{"\u0915\u1160", 2}, +{"\u0915\u0308\u1160", 2}, +{"\u0915\u11A8", 2}, +{"\u0915\u0308\u11A8", 2}, +{"\u0915\uAC00", 2}, +{"\u0915\u0308\uAC00", 2}, +{"\u0915\uAC01", 2}, +{"\u0915\u0308\uAC01", 2}, +{"\u0915\u0900", 1}, +{"\u0915\u0308\u0900", 1}, +{"\u0915\u0903", 1}, +{"\u0915\u0308\u0903", 1}, +{"\u0915\u0904", 2}, +{"\u0915\u0308\u0904", 2}, +{"\u0915\u0D4E", 2}, +{"\u0915\u0308\u0D4E", 2}, +{"\u0915\u0915", 2}, +{"\u0915\u0308\u0915", 2}, +{"\u0915\u231A", 2}, +{"\u0915\u0308\u231A", 2}, +{"\u0915\u0300", 1}, +{"\u0915\u0308\u0300", 1}, +{"\u0915\u093C", 1}, +{"\u0915\u0308\u093C", 1}, +{"\u0915\u094D", 1}, +{"\u0915\u0308\u094D", 1}, +{"\u0915\u200D", 1}, +{"\u0915\u0308\u200D", 1}, +{"\u0915\u0378", 2}, +{"\u0915\u0308\u0378", 2}, +{"\u231A\u0020", 2}, +{"\u231A\u0308\u0020", 2}, +{"\u231A\u000D", 2}, +{"\u231A\u0308\u000D", 2}, +{"\u231A\u000A", 2}, +{"\u231A\u0308\u000A", 2}, +{"\u231A\u0001", 2}, +{"\u231A\u0308\u0001", 2}, +{"\u231A\u034F", 1}, +{"\u231A\u0308\u034F", 1}, +{"\u231A\U0001F1E6", 2}, +{"\u231A\u0308\U0001F1E6", 2}, +{"\u231A\u0600", 2}, +{"\u231A\u0308\u0600", 2}, +{"\u231A\u0A03", 1}, +{"\u231A\u0308\u0A03", 1}, +{"\u231A\u1100", 2}, +{"\u231A\u0308\u1100", 2}, +{"\u231A\u1160", 2}, +{"\u231A\u0308\u1160", 2}, +{"\u231A\u11A8", 2}, +{"\u231A\u0308\u11A8", 2}, +{"\u231A\uAC00", 2}, +{"\u231A\u0308\uAC00", 2}, +{"\u231A\uAC01", 2}, +{"\u231A\u0308\uAC01", 2}, +{"\u231A\u0900", 1}, +{"\u231A\u0308\u0900", 1}, +{"\u231A\u0903", 1}, +{"\u231A\u0308\u0903", 1}, +{"\u231A\u0904", 2}, +{"\u231A\u0308\u0904", 2}, +{"\u231A\u0D4E", 2}, +{"\u231A\u0308\u0D4E", 2}, +{"\u231A\u0915", 2}, +{"\u231A\u0308\u0915", 2}, +{"\u231A\u231A", 2}, +{"\u231A\u0308\u231A", 2}, +{"\u231A\u0300", 1}, +{"\u231A\u0308\u0300", 1}, +{"\u231A\u093C", 1}, +{"\u231A\u0308\u093C", 1}, +{"\u231A\u094D", 1}, +{"\u231A\u0308\u094D", 1}, +{"\u231A\u200D", 1}, +{"\u231A\u0308\u200D", 1}, +{"\u231A\u0378", 2}, +{"\u231A\u0308\u0378", 2}, +{"\u0300\u0020", 2}, +{"\u0300\u0308\u0020", 2}, +{"\u0300\u000D", 2}, +{"\u0300\u0308\u000D", 2}, +{"\u0300\u000A", 2}, +{"\u0300\u0308\u000A", 2}, +{"\u0300\u0001", 2}, +{"\u0300\u0308\u0001", 2}, +{"\u0300\u034F", 1}, +{"\u0300\u0308\u034F", 1}, +{"\u0300\U0001F1E6", 2}, +{"\u0300\u0308\U0001F1E6", 2}, +{"\u0300\u0600", 2}, +{"\u0300\u0308\u0600", 2}, +{"\u0300\u0A03", 1}, +{"\u0300\u0308\u0A03", 1}, +{"\u0300\u1100", 2}, +{"\u0300\u0308\u1100", 2}, +{"\u0300\u1160", 2}, +{"\u0300\u0308\u1160", 2}, +{"\u0300\u11A8", 2}, +{"\u0300\u0308\u11A8", 2}, +{"\u0300\uAC00", 2}, +{"\u0300\u0308\uAC00", 2}, +{"\u0300\uAC01", 2}, +{"\u0300\u0308\uAC01", 2}, +{"\u0300\u0900", 1}, +{"\u0300\u0308\u0900", 1}, +{"\u0300\u0903", 1}, +{"\u0300\u0308\u0903", 1}, +{"\u0300\u0904", 2}, +{"\u0300\u0308\u0904", 2}, +{"\u0300\u0D4E", 2}, +{"\u0300\u0308\u0D4E", 2}, +{"\u0300\u0915", 2}, +{"\u0300\u0308\u0915", 2}, +{"\u0300\u231A", 2}, +{"\u0300\u0308\u231A", 2}, +{"\u0300\u0300", 1}, +{"\u0300\u0308\u0300", 1}, +{"\u0300\u093C", 1}, +{"\u0300\u0308\u093C", 1}, +{"\u0300\u094D", 1}, +{"\u0300\u0308\u094D", 1}, +{"\u0300\u200D", 1}, +{"\u0300\u0308\u200D", 1}, +{"\u0300\u0378", 2}, +{"\u0300\u0308\u0378", 2}, +{"\u093C\u0020", 2}, +{"\u093C\u0308\u0020", 2}, +{"\u093C\u000D", 2}, +{"\u093C\u0308\u000D", 2}, +{"\u093C\u000A", 2}, +{"\u093C\u0308\u000A", 2}, +{"\u093C\u0001", 2}, +{"\u093C\u0308\u0001", 2}, +{"\u093C\u034F", 1}, +{"\u093C\u0308\u034F", 1}, +{"\u093C\U0001F1E6", 2}, +{"\u093C\u0308\U0001F1E6", 2}, +{"\u093C\u0600", 2}, +{"\u093C\u0308\u0600", 2}, +{"\u093C\u0A03", 1}, +{"\u093C\u0308\u0A03", 1}, +{"\u093C\u1100", 2}, +{"\u093C\u0308\u1100", 2}, +{"\u093C\u1160", 2}, +{"\u093C\u0308\u1160", 2}, +{"\u093C\u11A8", 2}, +{"\u093C\u0308\u11A8", 2}, +{"\u093C\uAC00", 2}, +{"\u093C\u0308\uAC00", 2}, +{"\u093C\uAC01", 2}, +{"\u093C\u0308\uAC01", 2}, +{"\u093C\u0900", 1}, +{"\u093C\u0308\u0900", 1}, +{"\u093C\u0903", 1}, +{"\u093C\u0308\u0903", 1}, +{"\u093C\u0904", 2}, +{"\u093C\u0308\u0904", 2}, +{"\u093C\u0D4E", 2}, +{"\u093C\u0308\u0D4E", 2}, +{"\u093C\u0915", 2}, +{"\u093C\u0308\u0915", 2}, +{"\u093C\u231A", 2}, +{"\u093C\u0308\u231A", 2}, +{"\u093C\u0300", 1}, +{"\u093C\u0308\u0300", 1}, +{"\u093C\u093C", 1}, +{"\u093C\u0308\u093C", 1}, +{"\u093C\u094D", 1}, +{"\u093C\u0308\u094D", 1}, +{"\u093C\u200D", 1}, +{"\u093C\u0308\u200D", 1}, +{"\u093C\u0378", 2}, +{"\u093C\u0308\u0378", 2}, +{"\u094D\u0020", 2}, +{"\u094D\u0308\u0020", 2}, +{"\u094D\u000D", 2}, +{"\u094D\u0308\u000D", 2}, +{"\u094D\u000A", 2}, +{"\u094D\u0308\u000A", 2}, +{"\u094D\u0001", 2}, +{"\u094D\u0308\u0001", 2}, +{"\u094D\u034F", 1}, +{"\u094D\u0308\u034F", 1}, +{"\u094D\U0001F1E6", 2}, +{"\u094D\u0308\U0001F1E6", 2}, +{"\u094D\u0600", 2}, +{"\u094D\u0308\u0600", 2}, +{"\u094D\u0A03", 1}, +{"\u094D\u0308\u0A03", 1}, +{"\u094D\u1100", 2}, +{"\u094D\u0308\u1100", 2}, +{"\u094D\u1160", 2}, +{"\u094D\u0308\u1160", 2}, +{"\u094D\u11A8", 2}, +{"\u094D\u0308\u11A8", 2}, +{"\u094D\uAC00", 2}, +{"\u094D\u0308\uAC00", 2}, +{"\u094D\uAC01", 2}, +{"\u094D\u0308\uAC01", 2}, +{"\u094D\u0900", 1}, +{"\u094D\u0308\u0900", 1}, +{"\u094D\u0903", 1}, +{"\u094D\u0308\u0903", 1}, +{"\u094D\u0904", 2}, +{"\u094D\u0308\u0904", 2}, +{"\u094D\u0D4E", 2}, +{"\u094D\u0308\u0D4E", 2}, +{"\u094D\u0915", 2}, +{"\u094D\u0308\u0915", 2}, +{"\u094D\u231A", 2}, +{"\u094D\u0308\u231A", 2}, +{"\u094D\u0300", 1}, +{"\u094D\u0308\u0300", 1}, +{"\u094D\u093C", 1}, +{"\u094D\u0308\u093C", 1}, +{"\u094D\u094D", 1}, +{"\u094D\u0308\u094D", 1}, +{"\u094D\u200D", 1}, +{"\u094D\u0308\u200D", 1}, +{"\u094D\u0378", 2}, +{"\u094D\u0308\u0378", 2}, +{"\u200D\u0020", 2}, +{"\u200D\u0308\u0020", 2}, +{"\u200D\u000D", 2}, +{"\u200D\u0308\u000D", 2}, +{"\u200D\u000A", 2}, +{"\u200D\u0308\u000A", 2}, +{"\u200D\u0001", 2}, +{"\u200D\u0308\u0001", 2}, +{"\u200D\u034F", 1}, +{"\u200D\u0308\u034F", 1}, +{"\u200D\U0001F1E6", 2}, +{"\u200D\u0308\U0001F1E6", 2}, +{"\u200D\u0600", 2}, +{"\u200D\u0308\u0600", 2}, +{"\u200D\u0A03", 1}, +{"\u200D\u0308\u0A03", 1}, +{"\u200D\u1100", 2}, +{"\u200D\u0308\u1100", 2}, +{"\u200D\u1160", 2}, +{"\u200D\u0308\u1160", 2}, +{"\u200D\u11A8", 2}, +{"\u200D\u0308\u11A8", 2}, +{"\u200D\uAC00", 2}, +{"\u200D\u0308\uAC00", 2}, +{"\u200D\uAC01", 2}, +{"\u200D\u0308\uAC01", 2}, +{"\u200D\u0900", 1}, +{"\u200D\u0308\u0900", 1}, +{"\u200D\u0903", 1}, +{"\u200D\u0308\u0903", 1}, +{"\u200D\u0904", 2}, +{"\u200D\u0308\u0904", 2}, +{"\u200D\u0D4E", 2}, +{"\u200D\u0308\u0D4E", 2}, +{"\u200D\u0915", 2}, +{"\u200D\u0308\u0915", 2}, +{"\u200D\u231A", 2}, +{"\u200D\u0308\u231A", 2}, +{"\u200D\u0300", 1}, +{"\u200D\u0308\u0300", 1}, +{"\u200D\u093C", 1}, +{"\u200D\u0308\u093C", 1}, +{"\u200D\u094D", 1}, +{"\u200D\u0308\u094D", 1}, +{"\u200D\u200D", 1}, +{"\u200D\u0308\u200D", 1}, +{"\u200D\u0378", 2}, +{"\u200D\u0308\u0378", 2}, +{"\u0378\u0020", 2}, +{"\u0378\u0308\u0020", 2}, +{"\u0378\u000D", 2}, +{"\u0378\u0308\u000D", 2}, +{"\u0378\u000A", 2}, +{"\u0378\u0308\u000A", 2}, +{"\u0378\u0001", 2}, +{"\u0378\u0308\u0001", 2}, +{"\u0378\u034F", 1}, +{"\u0378\u0308\u034F", 1}, +{"\u0378\U0001F1E6", 2}, +{"\u0378\u0308\U0001F1E6", 2}, +{"\u0378\u0600", 2}, +{"\u0378\u0308\u0600", 2}, +{"\u0378\u0A03", 1}, +{"\u0378\u0308\u0A03", 1}, +{"\u0378\u1100", 2}, +{"\u0378\u0308\u1100", 2}, +{"\u0378\u1160", 2}, +{"\u0378\u0308\u1160", 2}, +{"\u0378\u11A8", 2}, +{"\u0378\u0308\u11A8", 2}, +{"\u0378\uAC00", 2}, +{"\u0378\u0308\uAC00", 2}, +{"\u0378\uAC01", 2}, +{"\u0378\u0308\uAC01", 2}, +{"\u0378\u0900", 1}, +{"\u0378\u0308\u0900", 1}, +{"\u0378\u0903", 1}, +{"\u0378\u0308\u0903", 1}, +{"\u0378\u0904", 2}, +{"\u0378\u0308\u0904", 2}, +{"\u0378\u0D4E", 2}, +{"\u0378\u0308\u0D4E", 2}, +{"\u0378\u0915", 2}, +{"\u0378\u0308\u0915", 2}, +{"\u0378\u231A", 2}, +{"\u0378\u0308\u231A", 2}, +{"\u0378\u0300", 1}, +{"\u0378\u0308\u0300", 1}, +{"\u0378\u093C", 1}, +{"\u0378\u0308\u093C", 1}, +{"\u0378\u094D", 1}, +{"\u0378\u0308\u094D", 1}, +{"\u0378\u200D", 1}, +{"\u0378\u0308\u200D", 1}, +{"\u0378\u0378", 2}, +{"\u0378\u0308\u0378", 2}, +{"\u000D\u000A\u0061\u000A\u0308", 4}, +{"\u0061\u0308", 1}, +{"\u0020\u200D\u0646", 2}, +{"\u0646\u200D\u0020", 2}, +{"\u1100\u1100", 1}, +{"\uAC00\u11A8\u1100", 2}, +{"\uAC01\u11A8\u1100", 2}, +{"\U0001F1E6\U0001F1E7\U0001F1E8\u0062", 3}, +{"\u0061\U0001F1E6\U0001F1E7\U0001F1E8\u0062", 4}, +{"\u0061\U0001F1E6\U0001F1E7\u200D\U0001F1E8\u0062", 4}, +{"\u0061\U0001F1E6\u200D\U0001F1E7\U0001F1E8\u0062", 4}, +{"\u0061\U0001F1E6\U0001F1E7\U0001F1E8\U0001F1E9\u0062", 4}, +{"\u0061\u200D", 1}, +{"\u0061\u0308\u0062", 2}, +{"\u0061\u0903\u0062", 2}, +{"\u0061\u0600\u0062", 2}, +{"\U0001F476\U0001F3FF\U0001F476", 2}, +{"\u0061\U0001F3FF\U0001F476", 2}, +{"\u0061\U0001F3FF\U0001F476\u200D\U0001F6D1", 2}, +{"\U0001F476\U0001F3FF\u0308\u200D\U0001F476\U0001F3FF", 1}, +{"\U0001F6D1\u200D\U0001F6D1", 1}, +{"\u0061\u200D\U0001F6D1", 2}, +{"\u2701\u200D\u2701", 1}, +{"\u0061\u200D\u2701", 2}, +{"\u0915\u0924", 2}, +{"\u0915\u094D\u0924", 1}, +{"\u0915\u094D\u094D\u0924", 1}, +{"\u0915\u094D\u200D\u0924", 1}, +{"\u0915\u093C\u200D\u094D\u0924", 1}, +{"\u0915\u093C\u094D\u200D\u0924", 1}, +{"\u0915\u094D\u0924\u094D\u092F", 1}, +{"\u0915\u094D\u0061", 2}, +{"\u0061\u094D\u0924", 2}, +{"\u003F\u094D\u0924", 2}, +{"\u0915\u094D\u094D\u0924", 1}, +} + +// (Note that the test cases below only include the multi-codepoint cases +// listed in the file, as these were the only relevant ones for counting +// grapheme cluster boundaries.) +// +// https://unicode.org/Public/emoji/15.1/emoji-test.txt +// +// emoji-test.txt +// Date: 2023-06-05, 21:39:54 GMT +// © 2023 Unicode®, Inc. +// Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. +// For terms of use, see https://www.unicode.org/terms_of_use.html +@(rodata) +official_emoji_test_cases := []Test_Case { +{"\u263A\uFE0F", 1}, +{"\U0001F636\u200D\U0001F32B\uFE0F", 1}, +{"\U0001F636\u200D\U0001F32B", 1}, +{"\U0001F62E\u200D\U0001F4A8", 1}, +{"\U0001F642\u200D\u2194\uFE0F", 1}, +{"\U0001F642\u200D\u2194", 1}, +{"\U0001F642\u200D\u2195\uFE0F", 1}, +{"\U0001F642\u200D\u2195", 1}, +{"\U0001F635\u200D\U0001F4AB", 1}, +{"\u2639\uFE0F", 1}, +{"\u2620\uFE0F", 1}, +{"\u2763\uFE0F", 1}, +{"\u2764\uFE0F\u200D\U0001F525", 1}, +{"\u2764\u200D\U0001F525", 1}, +{"\u2764\uFE0F\u200D\U0001FA79", 1}, +{"\u2764\u200D\U0001FA79", 1}, +{"\u2764\uFE0F", 1}, +{"\U0001F573\uFE0F", 1}, +{"\U0001F441\uFE0F\u200D\U0001F5E8\uFE0F", 1}, +{"\U0001F441\u200D\U0001F5E8\uFE0F", 1}, +{"\U0001F441\uFE0F\u200D\U0001F5E8", 1}, +{"\U0001F441\u200D\U0001F5E8", 1}, +{"\U0001F5E8\uFE0F", 1}, +{"\U0001F5EF\uFE0F", 1}, +{"\U0001F44B\U0001F3FB", 1}, +{"\U0001F44B\U0001F3FC", 1}, +{"\U0001F44B\U0001F3FD", 1}, +{"\U0001F44B\U0001F3FE", 1}, +{"\U0001F44B\U0001F3FF", 1}, +{"\U0001F91A\U0001F3FB", 1}, +{"\U0001F91A\U0001F3FC", 1}, +{"\U0001F91A\U0001F3FD", 1}, +{"\U0001F91A\U0001F3FE", 1}, +{"\U0001F91A\U0001F3FF", 1}, +{"\U0001F590\uFE0F", 1}, +{"\U0001F590\U0001F3FB", 1}, +{"\U0001F590\U0001F3FC", 1}, +{"\U0001F590\U0001F3FD", 1}, +{"\U0001F590\U0001F3FE", 1}, +{"\U0001F590\U0001F3FF", 1}, +{"\u270B\U0001F3FB", 1}, +{"\u270B\U0001F3FC", 1}, +{"\u270B\U0001F3FD", 1}, +{"\u270B\U0001F3FE", 1}, +{"\u270B\U0001F3FF", 1}, +{"\U0001F596\U0001F3FB", 1}, +{"\U0001F596\U0001F3FC", 1}, +{"\U0001F596\U0001F3FD", 1}, +{"\U0001F596\U0001F3FE", 1}, +{"\U0001F596\U0001F3FF", 1}, +{"\U0001FAF1\U0001F3FB", 1}, +{"\U0001FAF1\U0001F3FC", 1}, +{"\U0001FAF1\U0001F3FD", 1}, +{"\U0001FAF1\U0001F3FE", 1}, +{"\U0001FAF1\U0001F3FF", 1}, +{"\U0001FAF2\U0001F3FB", 1}, +{"\U0001FAF2\U0001F3FC", 1}, +{"\U0001FAF2\U0001F3FD", 1}, +{"\U0001FAF2\U0001F3FE", 1}, +{"\U0001FAF2\U0001F3FF", 1}, +{"\U0001FAF3\U0001F3FB", 1}, +{"\U0001FAF3\U0001F3FC", 1}, +{"\U0001FAF3\U0001F3FD", 1}, +{"\U0001FAF3\U0001F3FE", 1}, +{"\U0001FAF3\U0001F3FF", 1}, +{"\U0001FAF4\U0001F3FB", 1}, +{"\U0001FAF4\U0001F3FC", 1}, +{"\U0001FAF4\U0001F3FD", 1}, +{"\U0001FAF4\U0001F3FE", 1}, +{"\U0001FAF4\U0001F3FF", 1}, +{"\U0001FAF7\U0001F3FB", 1}, +{"\U0001FAF7\U0001F3FC", 1}, +{"\U0001FAF7\U0001F3FD", 1}, +{"\U0001FAF7\U0001F3FE", 1}, +{"\U0001FAF7\U0001F3FF", 1}, +{"\U0001FAF8\U0001F3FB", 1}, +{"\U0001FAF8\U0001F3FC", 1}, +{"\U0001FAF8\U0001F3FD", 1}, +{"\U0001FAF8\U0001F3FE", 1}, +{"\U0001FAF8\U0001F3FF", 1}, +{"\U0001F44C\U0001F3FB", 1}, +{"\U0001F44C\U0001F3FC", 1}, +{"\U0001F44C\U0001F3FD", 1}, +{"\U0001F44C\U0001F3FE", 1}, +{"\U0001F44C\U0001F3FF", 1}, +{"\U0001F90C\U0001F3FB", 1}, +{"\U0001F90C\U0001F3FC", 1}, +{"\U0001F90C\U0001F3FD", 1}, +{"\U0001F90C\U0001F3FE", 1}, +{"\U0001F90C\U0001F3FF", 1}, +{"\U0001F90F\U0001F3FB", 1}, +{"\U0001F90F\U0001F3FC", 1}, +{"\U0001F90F\U0001F3FD", 1}, +{"\U0001F90F\U0001F3FE", 1}, +{"\U0001F90F\U0001F3FF", 1}, +{"\u270C\uFE0F", 1}, +{"\u270C\U0001F3FB", 1}, +{"\u270C\U0001F3FC", 1}, +{"\u270C\U0001F3FD", 1}, +{"\u270C\U0001F3FE", 1}, +{"\u270C\U0001F3FF", 1}, +{"\U0001F91E\U0001F3FB", 1}, +{"\U0001F91E\U0001F3FC", 1}, +{"\U0001F91E\U0001F3FD", 1}, +{"\U0001F91E\U0001F3FE", 1}, +{"\U0001F91E\U0001F3FF", 1}, +{"\U0001FAF0\U0001F3FB", 1}, +{"\U0001FAF0\U0001F3FC", 1}, +{"\U0001FAF0\U0001F3FD", 1}, +{"\U0001FAF0\U0001F3FE", 1}, +{"\U0001FAF0\U0001F3FF", 1}, +{"\U0001F91F\U0001F3FB", 1}, +{"\U0001F91F\U0001F3FC", 1}, +{"\U0001F91F\U0001F3FD", 1}, +{"\U0001F91F\U0001F3FE", 1}, +{"\U0001F91F\U0001F3FF", 1}, +{"\U0001F918\U0001F3FB", 1}, +{"\U0001F918\U0001F3FC", 1}, +{"\U0001F918\U0001F3FD", 1}, +{"\U0001F918\U0001F3FE", 1}, +{"\U0001F918\U0001F3FF", 1}, +{"\U0001F919\U0001F3FB", 1}, +{"\U0001F919\U0001F3FC", 1}, +{"\U0001F919\U0001F3FD", 1}, +{"\U0001F919\U0001F3FE", 1}, +{"\U0001F919\U0001F3FF", 1}, +{"\U0001F448\U0001F3FB", 1}, +{"\U0001F448\U0001F3FC", 1}, +{"\U0001F448\U0001F3FD", 1}, +{"\U0001F448\U0001F3FE", 1}, +{"\U0001F448\U0001F3FF", 1}, +{"\U0001F449\U0001F3FB", 1}, +{"\U0001F449\U0001F3FC", 1}, +{"\U0001F449\U0001F3FD", 1}, +{"\U0001F449\U0001F3FE", 1}, +{"\U0001F449\U0001F3FF", 1}, +{"\U0001F446\U0001F3FB", 1}, +{"\U0001F446\U0001F3FC", 1}, +{"\U0001F446\U0001F3FD", 1}, +{"\U0001F446\U0001F3FE", 1}, +{"\U0001F446\U0001F3FF", 1}, +{"\U0001F595\U0001F3FB", 1}, +{"\U0001F595\U0001F3FC", 1}, +{"\U0001F595\U0001F3FD", 1}, +{"\U0001F595\U0001F3FE", 1}, +{"\U0001F595\U0001F3FF", 1}, +{"\U0001F447\U0001F3FB", 1}, +{"\U0001F447\U0001F3FC", 1}, +{"\U0001F447\U0001F3FD", 1}, +{"\U0001F447\U0001F3FE", 1}, +{"\U0001F447\U0001F3FF", 1}, +{"\u261D\uFE0F", 1}, +{"\u261D\U0001F3FB", 1}, +{"\u261D\U0001F3FC", 1}, +{"\u261D\U0001F3FD", 1}, +{"\u261D\U0001F3FE", 1}, +{"\u261D\U0001F3FF", 1}, +{"\U0001FAF5\U0001F3FB", 1}, +{"\U0001FAF5\U0001F3FC", 1}, +{"\U0001FAF5\U0001F3FD", 1}, +{"\U0001FAF5\U0001F3FE", 1}, +{"\U0001FAF5\U0001F3FF", 1}, +{"\U0001F44D\U0001F3FB", 1}, +{"\U0001F44D\U0001F3FC", 1}, +{"\U0001F44D\U0001F3FD", 1}, +{"\U0001F44D\U0001F3FE", 1}, +{"\U0001F44D\U0001F3FF", 1}, +{"\U0001F44E\U0001F3FB", 1}, +{"\U0001F44E\U0001F3FC", 1}, +{"\U0001F44E\U0001F3FD", 1}, +{"\U0001F44E\U0001F3FE", 1}, +{"\U0001F44E\U0001F3FF", 1}, +{"\u270A\U0001F3FB", 1}, +{"\u270A\U0001F3FC", 1}, +{"\u270A\U0001F3FD", 1}, +{"\u270A\U0001F3FE", 1}, +{"\u270A\U0001F3FF", 1}, +{"\U0001F44A\U0001F3FB", 1}, +{"\U0001F44A\U0001F3FC", 1}, +{"\U0001F44A\U0001F3FD", 1}, +{"\U0001F44A\U0001F3FE", 1}, +{"\U0001F44A\U0001F3FF", 1}, +{"\U0001F91B\U0001F3FB", 1}, +{"\U0001F91B\U0001F3FC", 1}, +{"\U0001F91B\U0001F3FD", 1}, +{"\U0001F91B\U0001F3FE", 1}, +{"\U0001F91B\U0001F3FF", 1}, +{"\U0001F91C\U0001F3FB", 1}, +{"\U0001F91C\U0001F3FC", 1}, +{"\U0001F91C\U0001F3FD", 1}, +{"\U0001F91C\U0001F3FE", 1}, +{"\U0001F91C\U0001F3FF", 1}, +{"\U0001F44F\U0001F3FB", 1}, +{"\U0001F44F\U0001F3FC", 1}, +{"\U0001F44F\U0001F3FD", 1}, +{"\U0001F44F\U0001F3FE", 1}, +{"\U0001F44F\U0001F3FF", 1}, +{"\U0001F64C\U0001F3FB", 1}, +{"\U0001F64C\U0001F3FC", 1}, +{"\U0001F64C\U0001F3FD", 1}, +{"\U0001F64C\U0001F3FE", 1}, +{"\U0001F64C\U0001F3FF", 1}, +{"\U0001FAF6\U0001F3FB", 1}, +{"\U0001FAF6\U0001F3FC", 1}, +{"\U0001FAF6\U0001F3FD", 1}, +{"\U0001FAF6\U0001F3FE", 1}, +{"\U0001FAF6\U0001F3FF", 1}, +{"\U0001F450\U0001F3FB", 1}, +{"\U0001F450\U0001F3FC", 1}, +{"\U0001F450\U0001F3FD", 1}, +{"\U0001F450\U0001F3FE", 1}, +{"\U0001F450\U0001F3FF", 1}, +{"\U0001F932\U0001F3FB", 1}, +{"\U0001F932\U0001F3FC", 1}, +{"\U0001F932\U0001F3FD", 1}, +{"\U0001F932\U0001F3FE", 1}, +{"\U0001F932\U0001F3FF", 1}, +{"\U0001F91D\U0001F3FB", 1}, +{"\U0001F91D\U0001F3FC", 1}, +{"\U0001F91D\U0001F3FD", 1}, +{"\U0001F91D\U0001F3FE", 1}, +{"\U0001F91D\U0001F3FF", 1}, +{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FC", 1}, +{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FD", 1}, +{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FE", 1}, +{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FF", 1}, +{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FB", 1}, +{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FD", 1}, +{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FE", 1}, +{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FF", 1}, +{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FB", 1}, +{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FC", 1}, +{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FE", 1}, +{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FF", 1}, +{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FB", 1}, +{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FC", 1}, +{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FD", 1}, +{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FF", 1}, +{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FB", 1}, +{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FC", 1}, +{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FD", 1}, +{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FE", 1}, +{"\U0001F64F\U0001F3FB", 1}, +{"\U0001F64F\U0001F3FC", 1}, +{"\U0001F64F\U0001F3FD", 1}, +{"\U0001F64F\U0001F3FE", 1}, +{"\U0001F64F\U0001F3FF", 1}, +{"\u270D\uFE0F", 1}, +{"\u270D\U0001F3FB", 1}, +{"\u270D\U0001F3FC", 1}, +{"\u270D\U0001F3FD", 1}, +{"\u270D\U0001F3FE", 1}, +{"\u270D\U0001F3FF", 1}, +{"\U0001F485\U0001F3FB", 1}, +{"\U0001F485\U0001F3FC", 1}, +{"\U0001F485\U0001F3FD", 1}, +{"\U0001F485\U0001F3FE", 1}, +{"\U0001F485\U0001F3FF", 1}, +{"\U0001F933\U0001F3FB", 1}, +{"\U0001F933\U0001F3FC", 1}, +{"\U0001F933\U0001F3FD", 1}, +{"\U0001F933\U0001F3FE", 1}, +{"\U0001F933\U0001F3FF", 1}, +{"\U0001F4AA\U0001F3FB", 1}, +{"\U0001F4AA\U0001F3FC", 1}, +{"\U0001F4AA\U0001F3FD", 1}, +{"\U0001F4AA\U0001F3FE", 1}, +{"\U0001F4AA\U0001F3FF", 1}, +{"\U0001F9B5\U0001F3FB", 1}, +{"\U0001F9B5\U0001F3FC", 1}, +{"\U0001F9B5\U0001F3FD", 1}, +{"\U0001F9B5\U0001F3FE", 1}, +{"\U0001F9B5\U0001F3FF", 1}, +{"\U0001F9B6\U0001F3FB", 1}, +{"\U0001F9B6\U0001F3FC", 1}, +{"\U0001F9B6\U0001F3FD", 1}, +{"\U0001F9B6\U0001F3FE", 1}, +{"\U0001F9B6\U0001F3FF", 1}, +{"\U0001F442\U0001F3FB", 1}, +{"\U0001F442\U0001F3FC", 1}, +{"\U0001F442\U0001F3FD", 1}, +{"\U0001F442\U0001F3FE", 1}, +{"\U0001F442\U0001F3FF", 1}, +{"\U0001F9BB\U0001F3FB", 1}, +{"\U0001F9BB\U0001F3FC", 1}, +{"\U0001F9BB\U0001F3FD", 1}, +{"\U0001F9BB\U0001F3FE", 1}, +{"\U0001F9BB\U0001F3FF", 1}, +{"\U0001F443\U0001F3FB", 1}, +{"\U0001F443\U0001F3FC", 1}, +{"\U0001F443\U0001F3FD", 1}, +{"\U0001F443\U0001F3FE", 1}, +{"\U0001F443\U0001F3FF", 1}, +{"\U0001F441\uFE0F", 1}, +{"\U0001F476\U0001F3FB", 1}, +{"\U0001F476\U0001F3FC", 1}, +{"\U0001F476\U0001F3FD", 1}, +{"\U0001F476\U0001F3FE", 1}, +{"\U0001F476\U0001F3FF", 1}, +{"\U0001F9D2\U0001F3FB", 1}, +{"\U0001F9D2\U0001F3FC", 1}, +{"\U0001F9D2\U0001F3FD", 1}, +{"\U0001F9D2\U0001F3FE", 1}, +{"\U0001F9D2\U0001F3FF", 1}, +{"\U0001F466\U0001F3FB", 1}, +{"\U0001F466\U0001F3FC", 1}, +{"\U0001F466\U0001F3FD", 1}, +{"\U0001F466\U0001F3FE", 1}, +{"\U0001F466\U0001F3FF", 1}, +{"\U0001F467\U0001F3FB", 1}, +{"\U0001F467\U0001F3FC", 1}, +{"\U0001F467\U0001F3FD", 1}, +{"\U0001F467\U0001F3FE", 1}, +{"\U0001F467\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FF", 1}, +{"\U0001F471\U0001F3FB", 1}, +{"\U0001F471\U0001F3FC", 1}, +{"\U0001F471\U0001F3FD", 1}, +{"\U0001F471\U0001F3FE", 1}, +{"\U0001F471\U0001F3FF", 1}, +{"\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FF", 1}, +{"\U0001F9D4\U0001F3FB", 1}, +{"\U0001F9D4\U0001F3FC", 1}, +{"\U0001F9D4\U0001F3FD", 1}, +{"\U0001F9D4\U0001F3FE", 1}, +{"\U0001F9D4\U0001F3FF", 1}, +{"\U0001F9D4\u200D\u2642\uFE0F", 1}, +{"\U0001F9D4\u200D\u2642", 1}, +{"\U0001F9D4\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9D4\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9D4\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9D4\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9D4\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9D4\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9D4\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9D4\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9D4\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9D4\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9D4\u200D\u2640\uFE0F", 1}, +{"\U0001F9D4\u200D\u2640", 1}, +{"\U0001F9D4\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9D4\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9D4\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9D4\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9D4\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9D4\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9D4\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9D4\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9D4\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9D4\U0001F3FF\u200D\u2640", 1}, +{"\U0001F468\u200D\U0001F9B0", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9B0", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9B0", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9B0", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9B0", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9B0", 1}, +{"\U0001F468\u200D\U0001F9B1", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9B1", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9B1", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9B1", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9B1", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9B1", 1}, +{"\U0001F468\u200D\U0001F9B3", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9B3", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9B3", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9B3", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9B3", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9B3", 1}, +{"\U0001F468\u200D\U0001F9B2", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9B2", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9B2", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9B2", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9B2", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9B2", 1}, +{"\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF", 1}, +{"\U0001F469\u200D\U0001F9B0", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9B0", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9B0", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9B0", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9B0", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9B0", 1}, +{"\U0001F9D1\u200D\U0001F9B0", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9B0", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9B0", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9B0", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9B0", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9B0", 1}, +{"\U0001F469\u200D\U0001F9B1", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9B1", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9B1", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9B1", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9B1", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9B1", 1}, +{"\U0001F9D1\u200D\U0001F9B1", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9B1", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9B1", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9B1", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9B1", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9B1", 1}, +{"\U0001F469\u200D\U0001F9B3", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9B3", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9B3", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9B3", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9B3", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9B3", 1}, +{"\U0001F9D1\u200D\U0001F9B3", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9B3", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9B3", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9B3", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9B3", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9B3", 1}, +{"\U0001F469\u200D\U0001F9B2", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9B2", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9B2", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9B2", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9B2", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9B2", 1}, +{"\U0001F9D1\u200D\U0001F9B2", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9B2", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9B2", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9B2", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9B2", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9B2", 1}, +{"\U0001F471\u200D\u2640\uFE0F", 1}, +{"\U0001F471\u200D\u2640", 1}, +{"\U0001F471\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F471\U0001F3FB\u200D\u2640", 1}, +{"\U0001F471\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F471\U0001F3FC\u200D\u2640", 1}, +{"\U0001F471\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F471\U0001F3FD\u200D\u2640", 1}, +{"\U0001F471\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F471\U0001F3FE\u200D\u2640", 1}, +{"\U0001F471\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F471\U0001F3FF\u200D\u2640", 1}, +{"\U0001F471\u200D\u2642\uFE0F", 1}, +{"\U0001F471\u200D\u2642", 1}, +{"\U0001F471\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F471\U0001F3FB\u200D\u2642", 1}, +{"\U0001F471\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F471\U0001F3FC\u200D\u2642", 1}, +{"\U0001F471\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F471\U0001F3FD\u200D\u2642", 1}, +{"\U0001F471\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F471\U0001F3FE\u200D\u2642", 1}, +{"\U0001F471\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F471\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9D3\U0001F3FB", 1}, +{"\U0001F9D3\U0001F3FC", 1}, +{"\U0001F9D3\U0001F3FD", 1}, +{"\U0001F9D3\U0001F3FE", 1}, +{"\U0001F9D3\U0001F3FF", 1}, +{"\U0001F474\U0001F3FB", 1}, +{"\U0001F474\U0001F3FC", 1}, +{"\U0001F474\U0001F3FD", 1}, +{"\U0001F474\U0001F3FE", 1}, +{"\U0001F474\U0001F3FF", 1}, +{"\U0001F475\U0001F3FB", 1}, +{"\U0001F475\U0001F3FC", 1}, +{"\U0001F475\U0001F3FD", 1}, +{"\U0001F475\U0001F3FE", 1}, +{"\U0001F475\U0001F3FF", 1}, +{"\U0001F64D\U0001F3FB", 1}, +{"\U0001F64D\U0001F3FC", 1}, +{"\U0001F64D\U0001F3FD", 1}, +{"\U0001F64D\U0001F3FE", 1}, +{"\U0001F64D\U0001F3FF", 1}, +{"\U0001F64D\u200D\u2642\uFE0F", 1}, +{"\U0001F64D\u200D\u2642", 1}, +{"\U0001F64D\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F64D\U0001F3FB\u200D\u2642", 1}, +{"\U0001F64D\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F64D\U0001F3FC\u200D\u2642", 1}, +{"\U0001F64D\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F64D\U0001F3FD\u200D\u2642", 1}, +{"\U0001F64D\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F64D\U0001F3FE\u200D\u2642", 1}, +{"\U0001F64D\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F64D\U0001F3FF\u200D\u2642", 1}, +{"\U0001F64D\u200D\u2640\uFE0F", 1}, +{"\U0001F64D\u200D\u2640", 1}, +{"\U0001F64D\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F64D\U0001F3FB\u200D\u2640", 1}, +{"\U0001F64D\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F64D\U0001F3FC\u200D\u2640", 1}, +{"\U0001F64D\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F64D\U0001F3FD\u200D\u2640", 1}, +{"\U0001F64D\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F64D\U0001F3FE\u200D\u2640", 1}, +{"\U0001F64D\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F64D\U0001F3FF\u200D\u2640", 1}, +{"\U0001F64E\U0001F3FB", 1}, +{"\U0001F64E\U0001F3FC", 1}, +{"\U0001F64E\U0001F3FD", 1}, +{"\U0001F64E\U0001F3FE", 1}, +{"\U0001F64E\U0001F3FF", 1}, +{"\U0001F64E\u200D\u2642\uFE0F", 1}, +{"\U0001F64E\u200D\u2642", 1}, +{"\U0001F64E\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F64E\U0001F3FB\u200D\u2642", 1}, +{"\U0001F64E\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F64E\U0001F3FC\u200D\u2642", 1}, +{"\U0001F64E\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F64E\U0001F3FD\u200D\u2642", 1}, +{"\U0001F64E\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F64E\U0001F3FE\u200D\u2642", 1}, +{"\U0001F64E\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F64E\U0001F3FF\u200D\u2642", 1}, +{"\U0001F64E\u200D\u2640\uFE0F", 1}, +{"\U0001F64E\u200D\u2640", 1}, +{"\U0001F64E\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F64E\U0001F3FB\u200D\u2640", 1}, +{"\U0001F64E\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F64E\U0001F3FC\u200D\u2640", 1}, +{"\U0001F64E\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F64E\U0001F3FD\u200D\u2640", 1}, +{"\U0001F64E\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F64E\U0001F3FE\u200D\u2640", 1}, +{"\U0001F64E\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F64E\U0001F3FF\u200D\u2640", 1}, +{"\U0001F645\U0001F3FB", 1}, +{"\U0001F645\U0001F3FC", 1}, +{"\U0001F645\U0001F3FD", 1}, +{"\U0001F645\U0001F3FE", 1}, +{"\U0001F645\U0001F3FF", 1}, +{"\U0001F645\u200D\u2642\uFE0F", 1}, +{"\U0001F645\u200D\u2642", 1}, +{"\U0001F645\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F645\U0001F3FB\u200D\u2642", 1}, +{"\U0001F645\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F645\U0001F3FC\u200D\u2642", 1}, +{"\U0001F645\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F645\U0001F3FD\u200D\u2642", 1}, +{"\U0001F645\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F645\U0001F3FE\u200D\u2642", 1}, +{"\U0001F645\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F645\U0001F3FF\u200D\u2642", 1}, +{"\U0001F645\u200D\u2640\uFE0F", 1}, +{"\U0001F645\u200D\u2640", 1}, +{"\U0001F645\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F645\U0001F3FB\u200D\u2640", 1}, +{"\U0001F645\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F645\U0001F3FC\u200D\u2640", 1}, +{"\U0001F645\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F645\U0001F3FD\u200D\u2640", 1}, +{"\U0001F645\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F645\U0001F3FE\u200D\u2640", 1}, +{"\U0001F645\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F645\U0001F3FF\u200D\u2640", 1}, +{"\U0001F646\U0001F3FB", 1}, +{"\U0001F646\U0001F3FC", 1}, +{"\U0001F646\U0001F3FD", 1}, +{"\U0001F646\U0001F3FE", 1}, +{"\U0001F646\U0001F3FF", 1}, +{"\U0001F646\u200D\u2642\uFE0F", 1}, +{"\U0001F646\u200D\u2642", 1}, +{"\U0001F646\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F646\U0001F3FB\u200D\u2642", 1}, +{"\U0001F646\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F646\U0001F3FC\u200D\u2642", 1}, +{"\U0001F646\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F646\U0001F3FD\u200D\u2642", 1}, +{"\U0001F646\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F646\U0001F3FE\u200D\u2642", 1}, +{"\U0001F646\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F646\U0001F3FF\u200D\u2642", 1}, +{"\U0001F646\u200D\u2640\uFE0F", 1}, +{"\U0001F646\u200D\u2640", 1}, +{"\U0001F646\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F646\U0001F3FB\u200D\u2640", 1}, +{"\U0001F646\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F646\U0001F3FC\u200D\u2640", 1}, +{"\U0001F646\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F646\U0001F3FD\u200D\u2640", 1}, +{"\U0001F646\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F646\U0001F3FE\u200D\u2640", 1}, +{"\U0001F646\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F646\U0001F3FF\u200D\u2640", 1}, +{"\U0001F481\U0001F3FB", 1}, +{"\U0001F481\U0001F3FC", 1}, +{"\U0001F481\U0001F3FD", 1}, +{"\U0001F481\U0001F3FE", 1}, +{"\U0001F481\U0001F3FF", 1}, +{"\U0001F481\u200D\u2642\uFE0F", 1}, +{"\U0001F481\u200D\u2642", 1}, +{"\U0001F481\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F481\U0001F3FB\u200D\u2642", 1}, +{"\U0001F481\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F481\U0001F3FC\u200D\u2642", 1}, +{"\U0001F481\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F481\U0001F3FD\u200D\u2642", 1}, +{"\U0001F481\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F481\U0001F3FE\u200D\u2642", 1}, +{"\U0001F481\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F481\U0001F3FF\u200D\u2642", 1}, +{"\U0001F481\u200D\u2640\uFE0F", 1}, +{"\U0001F481\u200D\u2640", 1}, +{"\U0001F481\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F481\U0001F3FB\u200D\u2640", 1}, +{"\U0001F481\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F481\U0001F3FC\u200D\u2640", 1}, +{"\U0001F481\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F481\U0001F3FD\u200D\u2640", 1}, +{"\U0001F481\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F481\U0001F3FE\u200D\u2640", 1}, +{"\U0001F481\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F481\U0001F3FF\u200D\u2640", 1}, +{"\U0001F64B\U0001F3FB", 1}, +{"\U0001F64B\U0001F3FC", 1}, +{"\U0001F64B\U0001F3FD", 1}, +{"\U0001F64B\U0001F3FE", 1}, +{"\U0001F64B\U0001F3FF", 1}, +{"\U0001F64B\u200D\u2642\uFE0F", 1}, +{"\U0001F64B\u200D\u2642", 1}, +{"\U0001F64B\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F64B\U0001F3FB\u200D\u2642", 1}, +{"\U0001F64B\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F64B\U0001F3FC\u200D\u2642", 1}, +{"\U0001F64B\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F64B\U0001F3FD\u200D\u2642", 1}, +{"\U0001F64B\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F64B\U0001F3FE\u200D\u2642", 1}, +{"\U0001F64B\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F64B\U0001F3FF\u200D\u2642", 1}, +{"\U0001F64B\u200D\u2640\uFE0F", 1}, +{"\U0001F64B\u200D\u2640", 1}, +{"\U0001F64B\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F64B\U0001F3FB\u200D\u2640", 1}, +{"\U0001F64B\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F64B\U0001F3FC\u200D\u2640", 1}, +{"\U0001F64B\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F64B\U0001F3FD\u200D\u2640", 1}, +{"\U0001F64B\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F64B\U0001F3FE\u200D\u2640", 1}, +{"\U0001F64B\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F64B\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9CF\U0001F3FB", 1}, +{"\U0001F9CF\U0001F3FC", 1}, +{"\U0001F9CF\U0001F3FD", 1}, +{"\U0001F9CF\U0001F3FE", 1}, +{"\U0001F9CF\U0001F3FF", 1}, +{"\U0001F9CF\u200D\u2642\uFE0F", 1}, +{"\U0001F9CF\u200D\u2642", 1}, +{"\U0001F9CF\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9CF\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9CF\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9CF\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9CF\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9CF\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9CF\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9CF\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9CF\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9CF\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9CF\u200D\u2640\uFE0F", 1}, +{"\U0001F9CF\u200D\u2640", 1}, +{"\U0001F9CF\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9CF\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9CF\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9CF\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9CF\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9CF\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9CF\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9CF\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9CF\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9CF\U0001F3FF\u200D\u2640", 1}, +{"\U0001F647\U0001F3FB", 1}, +{"\U0001F647\U0001F3FC", 1}, +{"\U0001F647\U0001F3FD", 1}, +{"\U0001F647\U0001F3FE", 1}, +{"\U0001F647\U0001F3FF", 1}, +{"\U0001F647\u200D\u2642\uFE0F", 1}, +{"\U0001F647\u200D\u2642", 1}, +{"\U0001F647\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F647\U0001F3FB\u200D\u2642", 1}, +{"\U0001F647\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F647\U0001F3FC\u200D\u2642", 1}, +{"\U0001F647\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F647\U0001F3FD\u200D\u2642", 1}, +{"\U0001F647\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F647\U0001F3FE\u200D\u2642", 1}, +{"\U0001F647\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F647\U0001F3FF\u200D\u2642", 1}, +{"\U0001F647\u200D\u2640\uFE0F", 1}, +{"\U0001F647\u200D\u2640", 1}, +{"\U0001F647\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F647\U0001F3FB\u200D\u2640", 1}, +{"\U0001F647\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F647\U0001F3FC\u200D\u2640", 1}, +{"\U0001F647\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F647\U0001F3FD\u200D\u2640", 1}, +{"\U0001F647\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F647\U0001F3FE\u200D\u2640", 1}, +{"\U0001F647\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F647\U0001F3FF\u200D\u2640", 1}, +{"\U0001F926\U0001F3FB", 1}, +{"\U0001F926\U0001F3FC", 1}, +{"\U0001F926\U0001F3FD", 1}, +{"\U0001F926\U0001F3FE", 1}, +{"\U0001F926\U0001F3FF", 1}, +{"\U0001F926\u200D\u2642\uFE0F", 1}, +{"\U0001F926\u200D\u2642", 1}, +{"\U0001F926\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F926\U0001F3FB\u200D\u2642", 1}, +{"\U0001F926\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F926\U0001F3FC\u200D\u2642", 1}, +{"\U0001F926\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F926\U0001F3FD\u200D\u2642", 1}, +{"\U0001F926\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F926\U0001F3FE\u200D\u2642", 1}, +{"\U0001F926\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F926\U0001F3FF\u200D\u2642", 1}, +{"\U0001F926\u200D\u2640\uFE0F", 1}, +{"\U0001F926\u200D\u2640", 1}, +{"\U0001F926\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F926\U0001F3FB\u200D\u2640", 1}, +{"\U0001F926\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F926\U0001F3FC\u200D\u2640", 1}, +{"\U0001F926\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F926\U0001F3FD\u200D\u2640", 1}, +{"\U0001F926\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F926\U0001F3FE\u200D\u2640", 1}, +{"\U0001F926\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F926\U0001F3FF\u200D\u2640", 1}, +{"\U0001F937\U0001F3FB", 1}, +{"\U0001F937\U0001F3FC", 1}, +{"\U0001F937\U0001F3FD", 1}, +{"\U0001F937\U0001F3FE", 1}, +{"\U0001F937\U0001F3FF", 1}, +{"\U0001F937\u200D\u2642\uFE0F", 1}, +{"\U0001F937\u200D\u2642", 1}, +{"\U0001F937\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F937\U0001F3FB\u200D\u2642", 1}, +{"\U0001F937\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F937\U0001F3FC\u200D\u2642", 1}, +{"\U0001F937\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F937\U0001F3FD\u200D\u2642", 1}, +{"\U0001F937\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F937\U0001F3FE\u200D\u2642", 1}, +{"\U0001F937\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F937\U0001F3FF\u200D\u2642", 1}, +{"\U0001F937\u200D\u2640\uFE0F", 1}, +{"\U0001F937\u200D\u2640", 1}, +{"\U0001F937\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F937\U0001F3FB\u200D\u2640", 1}, +{"\U0001F937\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F937\U0001F3FC\u200D\u2640", 1}, +{"\U0001F937\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F937\U0001F3FD\u200D\u2640", 1}, +{"\U0001F937\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F937\U0001F3FE\u200D\u2640", 1}, +{"\U0001F937\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F937\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9D1\u200D\u2695\uFE0F", 1}, +{"\U0001F9D1\u200D\u2695", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2695\uFE0F", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2695", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2695\uFE0F", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2695", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2695\uFE0F", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2695", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2695\uFE0F", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2695", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2695\uFE0F", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2695", 1}, +{"\U0001F468\u200D\u2695\uFE0F", 1}, +{"\U0001F468\u200D\u2695", 1}, +{"\U0001F468\U0001F3FB\u200D\u2695\uFE0F", 1}, +{"\U0001F468\U0001F3FB\u200D\u2695", 1}, +{"\U0001F468\U0001F3FC\u200D\u2695\uFE0F", 1}, +{"\U0001F468\U0001F3FC\u200D\u2695", 1}, +{"\U0001F468\U0001F3FD\u200D\u2695\uFE0F", 1}, +{"\U0001F468\U0001F3FD\u200D\u2695", 1}, +{"\U0001F468\U0001F3FE\u200D\u2695\uFE0F", 1}, +{"\U0001F468\U0001F3FE\u200D\u2695", 1}, +{"\U0001F468\U0001F3FF\u200D\u2695\uFE0F", 1}, +{"\U0001F468\U0001F3FF\u200D\u2695", 1}, +{"\U0001F469\u200D\u2695\uFE0F", 1}, +{"\U0001F469\u200D\u2695", 1}, +{"\U0001F469\U0001F3FB\u200D\u2695\uFE0F", 1}, +{"\U0001F469\U0001F3FB\u200D\u2695", 1}, +{"\U0001F469\U0001F3FC\u200D\u2695\uFE0F", 1}, +{"\U0001F469\U0001F3FC\u200D\u2695", 1}, +{"\U0001F469\U0001F3FD\u200D\u2695\uFE0F", 1}, +{"\U0001F469\U0001F3FD\u200D\u2695", 1}, +{"\U0001F469\U0001F3FE\u200D\u2695\uFE0F", 1}, +{"\U0001F469\U0001F3FE\u200D\u2695", 1}, +{"\U0001F469\U0001F3FF\u200D\u2695\uFE0F", 1}, +{"\U0001F469\U0001F3FF\u200D\u2695", 1}, +{"\U0001F9D1\u200D\U0001F393", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F393", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F393", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F393", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F393", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F393", 1}, +{"\U0001F468\u200D\U0001F393", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F393", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F393", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F393", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F393", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F393", 1}, +{"\U0001F469\u200D\U0001F393", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F393", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F393", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F393", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F393", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F393", 1}, +{"\U0001F9D1\u200D\U0001F3EB", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F3EB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F3EB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F3EB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F3EB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F3EB", 1}, +{"\U0001F468\u200D\U0001F3EB", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F3EB", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F3EB", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F3EB", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F3EB", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F3EB", 1}, +{"\U0001F469\u200D\U0001F3EB", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F3EB", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F3EB", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F3EB", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F3EB", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F3EB", 1}, +{"\U0001F9D1\u200D\u2696\uFE0F", 1}, +{"\U0001F9D1\u200D\u2696", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2696\uFE0F", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2696", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2696\uFE0F", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2696", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2696\uFE0F", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2696", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2696\uFE0F", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2696", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2696\uFE0F", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2696", 1}, +{"\U0001F468\u200D\u2696\uFE0F", 1}, +{"\U0001F468\u200D\u2696", 1}, +{"\U0001F468\U0001F3FB\u200D\u2696\uFE0F", 1}, +{"\U0001F468\U0001F3FB\u200D\u2696", 1}, +{"\U0001F468\U0001F3FC\u200D\u2696\uFE0F", 1}, +{"\U0001F468\U0001F3FC\u200D\u2696", 1}, +{"\U0001F468\U0001F3FD\u200D\u2696\uFE0F", 1}, +{"\U0001F468\U0001F3FD\u200D\u2696", 1}, +{"\U0001F468\U0001F3FE\u200D\u2696\uFE0F", 1}, +{"\U0001F468\U0001F3FE\u200D\u2696", 1}, +{"\U0001F468\U0001F3FF\u200D\u2696\uFE0F", 1}, +{"\U0001F468\U0001F3FF\u200D\u2696", 1}, +{"\U0001F469\u200D\u2696\uFE0F", 1}, +{"\U0001F469\u200D\u2696", 1}, +{"\U0001F469\U0001F3FB\u200D\u2696\uFE0F", 1}, +{"\U0001F469\U0001F3FB\u200D\u2696", 1}, +{"\U0001F469\U0001F3FC\u200D\u2696\uFE0F", 1}, +{"\U0001F469\U0001F3FC\u200D\u2696", 1}, +{"\U0001F469\U0001F3FD\u200D\u2696\uFE0F", 1}, +{"\U0001F469\U0001F3FD\u200D\u2696", 1}, +{"\U0001F469\U0001F3FE\u200D\u2696\uFE0F", 1}, +{"\U0001F469\U0001F3FE\u200D\u2696", 1}, +{"\U0001F469\U0001F3FF\u200D\u2696\uFE0F", 1}, +{"\U0001F469\U0001F3FF\u200D\u2696", 1}, +{"\U0001F9D1\u200D\U0001F33E", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F33E", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F33E", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F33E", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F33E", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F33E", 1}, +{"\U0001F468\u200D\U0001F33E", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F33E", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F33E", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F33E", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F33E", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F33E", 1}, +{"\U0001F469\u200D\U0001F33E", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F33E", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F33E", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F33E", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F33E", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F33E", 1}, +{"\U0001F9D1\u200D\U0001F373", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F373", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F373", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F373", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F373", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F373", 1}, +{"\U0001F468\u200D\U0001F373", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F373", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F373", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F373", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F373", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F373", 1}, +{"\U0001F469\u200D\U0001F373", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F373", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F373", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F373", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F373", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F373", 1}, +{"\U0001F9D1\u200D\U0001F527", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F527", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F527", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F527", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F527", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F527", 1}, +{"\U0001F468\u200D\U0001F527", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F527", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F527", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F527", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F527", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F527", 1}, +{"\U0001F469\u200D\U0001F527", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F527", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F527", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F527", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F527", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F527", 1}, +{"\U0001F9D1\u200D\U0001F3ED", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F3ED", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F3ED", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F3ED", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F3ED", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F3ED", 1}, +{"\U0001F468\u200D\U0001F3ED", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F3ED", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F3ED", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F3ED", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F3ED", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F3ED", 1}, +{"\U0001F469\u200D\U0001F3ED", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F3ED", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F3ED", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F3ED", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F3ED", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F3ED", 1}, +{"\U0001F9D1\u200D\U0001F4BC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F4BC", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F4BC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F4BC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F4BC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F4BC", 1}, +{"\U0001F468\u200D\U0001F4BC", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F4BC", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F4BC", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F4BC", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F4BC", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F4BC", 1}, +{"\U0001F469\u200D\U0001F4BC", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F4BC", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F4BC", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F4BC", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F4BC", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F4BC", 1}, +{"\U0001F9D1\u200D\U0001F52C", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F52C", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F52C", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F52C", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F52C", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F52C", 1}, +{"\U0001F468\u200D\U0001F52C", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F52C", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F52C", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F52C", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F52C", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F52C", 1}, +{"\U0001F469\u200D\U0001F52C", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F52C", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F52C", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F52C", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F52C", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F52C", 1}, +{"\U0001F9D1\u200D\U0001F4BB", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F4BB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F4BB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F4BB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F4BB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F4BB", 1}, +{"\U0001F468\u200D\U0001F4BB", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F4BB", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F4BB", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F4BB", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F4BB", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F4BB", 1}, +{"\U0001F469\u200D\U0001F4BB", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F4BB", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F4BB", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F4BB", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F4BB", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F4BB", 1}, +{"\U0001F9D1\u200D\U0001F3A4", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F3A4", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F3A4", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F3A4", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F3A4", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F3A4", 1}, +{"\U0001F468\u200D\U0001F3A4", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F3A4", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F3A4", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F3A4", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F3A4", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F3A4", 1}, +{"\U0001F469\u200D\U0001F3A4", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F3A4", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F3A4", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F3A4", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F3A4", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F3A4", 1}, +{"\U0001F9D1\u200D\U0001F3A8", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F3A8", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F3A8", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F3A8", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F3A8", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F3A8", 1}, +{"\U0001F468\u200D\U0001F3A8", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F3A8", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F3A8", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F3A8", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F3A8", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F3A8", 1}, +{"\U0001F469\u200D\U0001F3A8", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F3A8", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F3A8", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F3A8", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F3A8", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F3A8", 1}, +{"\U0001F9D1\u200D\u2708\uFE0F", 1}, +{"\U0001F9D1\u200D\u2708", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2708\uFE0F", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2708", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2708\uFE0F", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2708", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2708\uFE0F", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2708", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2708\uFE0F", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2708", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2708\uFE0F", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2708", 1}, +{"\U0001F468\u200D\u2708\uFE0F", 1}, +{"\U0001F468\u200D\u2708", 1}, +{"\U0001F468\U0001F3FB\u200D\u2708\uFE0F", 1}, +{"\U0001F468\U0001F3FB\u200D\u2708", 1}, +{"\U0001F468\U0001F3FC\u200D\u2708\uFE0F", 1}, +{"\U0001F468\U0001F3FC\u200D\u2708", 1}, +{"\U0001F468\U0001F3FD\u200D\u2708\uFE0F", 1}, +{"\U0001F468\U0001F3FD\u200D\u2708", 1}, +{"\U0001F468\U0001F3FE\u200D\u2708\uFE0F", 1}, +{"\U0001F468\U0001F3FE\u200D\u2708", 1}, +{"\U0001F468\U0001F3FF\u200D\u2708\uFE0F", 1}, +{"\U0001F468\U0001F3FF\u200D\u2708", 1}, +{"\U0001F469\u200D\u2708\uFE0F", 1}, +{"\U0001F469\u200D\u2708", 1}, +{"\U0001F469\U0001F3FB\u200D\u2708\uFE0F", 1}, +{"\U0001F469\U0001F3FB\u200D\u2708", 1}, +{"\U0001F469\U0001F3FC\u200D\u2708\uFE0F", 1}, +{"\U0001F469\U0001F3FC\u200D\u2708", 1}, +{"\U0001F469\U0001F3FD\u200D\u2708\uFE0F", 1}, +{"\U0001F469\U0001F3FD\u200D\u2708", 1}, +{"\U0001F469\U0001F3FE\u200D\u2708\uFE0F", 1}, +{"\U0001F469\U0001F3FE\u200D\u2708", 1}, +{"\U0001F469\U0001F3FF\u200D\u2708\uFE0F", 1}, +{"\U0001F469\U0001F3FF\u200D\u2708", 1}, +{"\U0001F9D1\u200D\U0001F680", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F680", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F680", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F680", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F680", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F680", 1}, +{"\U0001F468\u200D\U0001F680", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F680", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F680", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F680", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F680", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F680", 1}, +{"\U0001F469\u200D\U0001F680", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F680", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F680", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F680", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F680", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F680", 1}, +{"\U0001F9D1\u200D\U0001F692", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F692", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F692", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F692", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F692", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F692", 1}, +{"\U0001F468\u200D\U0001F692", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F692", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F692", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F692", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F692", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F692", 1}, +{"\U0001F469\u200D\U0001F692", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F692", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F692", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F692", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F692", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F692", 1}, +{"\U0001F46E\U0001F3FB", 1}, +{"\U0001F46E\U0001F3FC", 1}, +{"\U0001F46E\U0001F3FD", 1}, +{"\U0001F46E\U0001F3FE", 1}, +{"\U0001F46E\U0001F3FF", 1}, +{"\U0001F46E\u200D\u2642\uFE0F", 1}, +{"\U0001F46E\u200D\u2642", 1}, +{"\U0001F46E\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F46E\U0001F3FB\u200D\u2642", 1}, +{"\U0001F46E\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F46E\U0001F3FC\u200D\u2642", 1}, +{"\U0001F46E\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F46E\U0001F3FD\u200D\u2642", 1}, +{"\U0001F46E\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F46E\U0001F3FE\u200D\u2642", 1}, +{"\U0001F46E\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F46E\U0001F3FF\u200D\u2642", 1}, +{"\U0001F46E\u200D\u2640\uFE0F", 1}, +{"\U0001F46E\u200D\u2640", 1}, +{"\U0001F46E\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F46E\U0001F3FB\u200D\u2640", 1}, +{"\U0001F46E\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F46E\U0001F3FC\u200D\u2640", 1}, +{"\U0001F46E\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F46E\U0001F3FD\u200D\u2640", 1}, +{"\U0001F46E\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F46E\U0001F3FE\u200D\u2640", 1}, +{"\U0001F46E\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F46E\U0001F3FF\u200D\u2640", 1}, +{"\U0001F575\uFE0F", 1}, +{"\U0001F575\U0001F3FB", 1}, +{"\U0001F575\U0001F3FC", 1}, +{"\U0001F575\U0001F3FD", 1}, +{"\U0001F575\U0001F3FE", 1}, +{"\U0001F575\U0001F3FF", 1}, +{"\U0001F575\uFE0F\u200D\u2642\uFE0F", 1}, +{"\U0001F575\u200D\u2642\uFE0F", 1}, +{"\U0001F575\uFE0F\u200D\u2642", 1}, +{"\U0001F575\u200D\u2642", 1}, +{"\U0001F575\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F575\U0001F3FB\u200D\u2642", 1}, +{"\U0001F575\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F575\U0001F3FC\u200D\u2642", 1}, +{"\U0001F575\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F575\U0001F3FD\u200D\u2642", 1}, +{"\U0001F575\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F575\U0001F3FE\u200D\u2642", 1}, +{"\U0001F575\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F575\U0001F3FF\u200D\u2642", 1}, +{"\U0001F575\uFE0F\u200D\u2640\uFE0F", 1}, +{"\U0001F575\u200D\u2640\uFE0F", 1}, +{"\U0001F575\uFE0F\u200D\u2640", 1}, +{"\U0001F575\u200D\u2640", 1}, +{"\U0001F575\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F575\U0001F3FB\u200D\u2640", 1}, +{"\U0001F575\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F575\U0001F3FC\u200D\u2640", 1}, +{"\U0001F575\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F575\U0001F3FD\u200D\u2640", 1}, +{"\U0001F575\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F575\U0001F3FE\u200D\u2640", 1}, +{"\U0001F575\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F575\U0001F3FF\u200D\u2640", 1}, +{"\U0001F482\U0001F3FB", 1}, +{"\U0001F482\U0001F3FC", 1}, +{"\U0001F482\U0001F3FD", 1}, +{"\U0001F482\U0001F3FE", 1}, +{"\U0001F482\U0001F3FF", 1}, +{"\U0001F482\u200D\u2642\uFE0F", 1}, +{"\U0001F482\u200D\u2642", 1}, +{"\U0001F482\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F482\U0001F3FB\u200D\u2642", 1}, +{"\U0001F482\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F482\U0001F3FC\u200D\u2642", 1}, +{"\U0001F482\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F482\U0001F3FD\u200D\u2642", 1}, +{"\U0001F482\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F482\U0001F3FE\u200D\u2642", 1}, +{"\U0001F482\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F482\U0001F3FF\u200D\u2642", 1}, +{"\U0001F482\u200D\u2640\uFE0F", 1}, +{"\U0001F482\u200D\u2640", 1}, +{"\U0001F482\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F482\U0001F3FB\u200D\u2640", 1}, +{"\U0001F482\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F482\U0001F3FC\u200D\u2640", 1}, +{"\U0001F482\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F482\U0001F3FD\u200D\u2640", 1}, +{"\U0001F482\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F482\U0001F3FE\u200D\u2640", 1}, +{"\U0001F482\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F482\U0001F3FF\u200D\u2640", 1}, +{"\U0001F977\U0001F3FB", 1}, +{"\U0001F977\U0001F3FC", 1}, +{"\U0001F977\U0001F3FD", 1}, +{"\U0001F977\U0001F3FE", 1}, +{"\U0001F977\U0001F3FF", 1}, +{"\U0001F477\U0001F3FB", 1}, +{"\U0001F477\U0001F3FC", 1}, +{"\U0001F477\U0001F3FD", 1}, +{"\U0001F477\U0001F3FE", 1}, +{"\U0001F477\U0001F3FF", 1}, +{"\U0001F477\u200D\u2642\uFE0F", 1}, +{"\U0001F477\u200D\u2642", 1}, +{"\U0001F477\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F477\U0001F3FB\u200D\u2642", 1}, +{"\U0001F477\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F477\U0001F3FC\u200D\u2642", 1}, +{"\U0001F477\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F477\U0001F3FD\u200D\u2642", 1}, +{"\U0001F477\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F477\U0001F3FE\u200D\u2642", 1}, +{"\U0001F477\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F477\U0001F3FF\u200D\u2642", 1}, +{"\U0001F477\u200D\u2640\uFE0F", 1}, +{"\U0001F477\u200D\u2640", 1}, +{"\U0001F477\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F477\U0001F3FB\u200D\u2640", 1}, +{"\U0001F477\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F477\U0001F3FC\u200D\u2640", 1}, +{"\U0001F477\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F477\U0001F3FD\u200D\u2640", 1}, +{"\U0001F477\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F477\U0001F3FE\u200D\u2640", 1}, +{"\U0001F477\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F477\U0001F3FF\u200D\u2640", 1}, +{"\U0001FAC5\U0001F3FB", 1}, +{"\U0001FAC5\U0001F3FC", 1}, +{"\U0001FAC5\U0001F3FD", 1}, +{"\U0001FAC5\U0001F3FE", 1}, +{"\U0001FAC5\U0001F3FF", 1}, +{"\U0001F934\U0001F3FB", 1}, +{"\U0001F934\U0001F3FC", 1}, +{"\U0001F934\U0001F3FD", 1}, +{"\U0001F934\U0001F3FE", 1}, +{"\U0001F934\U0001F3FF", 1}, +{"\U0001F478\U0001F3FB", 1}, +{"\U0001F478\U0001F3FC", 1}, +{"\U0001F478\U0001F3FD", 1}, +{"\U0001F478\U0001F3FE", 1}, +{"\U0001F478\U0001F3FF", 1}, +{"\U0001F473\U0001F3FB", 1}, +{"\U0001F473\U0001F3FC", 1}, +{"\U0001F473\U0001F3FD", 1}, +{"\U0001F473\U0001F3FE", 1}, +{"\U0001F473\U0001F3FF", 1}, +{"\U0001F473\u200D\u2642\uFE0F", 1}, +{"\U0001F473\u200D\u2642", 1}, +{"\U0001F473\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F473\U0001F3FB\u200D\u2642", 1}, +{"\U0001F473\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F473\U0001F3FC\u200D\u2642", 1}, +{"\U0001F473\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F473\U0001F3FD\u200D\u2642", 1}, +{"\U0001F473\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F473\U0001F3FE\u200D\u2642", 1}, +{"\U0001F473\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F473\U0001F3FF\u200D\u2642", 1}, +{"\U0001F473\u200D\u2640\uFE0F", 1}, +{"\U0001F473\u200D\u2640", 1}, +{"\U0001F473\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F473\U0001F3FB\u200D\u2640", 1}, +{"\U0001F473\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F473\U0001F3FC\u200D\u2640", 1}, +{"\U0001F473\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F473\U0001F3FD\u200D\u2640", 1}, +{"\U0001F473\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F473\U0001F3FE\u200D\u2640", 1}, +{"\U0001F473\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F473\U0001F3FF\u200D\u2640", 1}, +{"\U0001F472\U0001F3FB", 1}, +{"\U0001F472\U0001F3FC", 1}, +{"\U0001F472\U0001F3FD", 1}, +{"\U0001F472\U0001F3FE", 1}, +{"\U0001F472\U0001F3FF", 1}, +{"\U0001F9D5\U0001F3FB", 1}, +{"\U0001F9D5\U0001F3FC", 1}, +{"\U0001F9D5\U0001F3FD", 1}, +{"\U0001F9D5\U0001F3FE", 1}, +{"\U0001F9D5\U0001F3FF", 1}, +{"\U0001F935\U0001F3FB", 1}, +{"\U0001F935\U0001F3FC", 1}, +{"\U0001F935\U0001F3FD", 1}, +{"\U0001F935\U0001F3FE", 1}, +{"\U0001F935\U0001F3FF", 1}, +{"\U0001F935\u200D\u2642\uFE0F", 1}, +{"\U0001F935\u200D\u2642", 1}, +{"\U0001F935\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F935\U0001F3FB\u200D\u2642", 1}, +{"\U0001F935\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F935\U0001F3FC\u200D\u2642", 1}, +{"\U0001F935\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F935\U0001F3FD\u200D\u2642", 1}, +{"\U0001F935\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F935\U0001F3FE\u200D\u2642", 1}, +{"\U0001F935\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F935\U0001F3FF\u200D\u2642", 1}, +{"\U0001F935\u200D\u2640\uFE0F", 1}, +{"\U0001F935\u200D\u2640", 1}, +{"\U0001F935\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F935\U0001F3FB\u200D\u2640", 1}, +{"\U0001F935\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F935\U0001F3FC\u200D\u2640", 1}, +{"\U0001F935\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F935\U0001F3FD\u200D\u2640", 1}, +{"\U0001F935\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F935\U0001F3FE\u200D\u2640", 1}, +{"\U0001F935\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F935\U0001F3FF\u200D\u2640", 1}, +{"\U0001F470\U0001F3FB", 1}, +{"\U0001F470\U0001F3FC", 1}, +{"\U0001F470\U0001F3FD", 1}, +{"\U0001F470\U0001F3FE", 1}, +{"\U0001F470\U0001F3FF", 1}, +{"\U0001F470\u200D\u2642\uFE0F", 1}, +{"\U0001F470\u200D\u2642", 1}, +{"\U0001F470\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F470\U0001F3FB\u200D\u2642", 1}, +{"\U0001F470\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F470\U0001F3FC\u200D\u2642", 1}, +{"\U0001F470\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F470\U0001F3FD\u200D\u2642", 1}, +{"\U0001F470\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F470\U0001F3FE\u200D\u2642", 1}, +{"\U0001F470\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F470\U0001F3FF\u200D\u2642", 1}, +{"\U0001F470\u200D\u2640\uFE0F", 1}, +{"\U0001F470\u200D\u2640", 1}, +{"\U0001F470\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F470\U0001F3FB\u200D\u2640", 1}, +{"\U0001F470\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F470\U0001F3FC\u200D\u2640", 1}, +{"\U0001F470\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F470\U0001F3FD\u200D\u2640", 1}, +{"\U0001F470\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F470\U0001F3FE\u200D\u2640", 1}, +{"\U0001F470\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F470\U0001F3FF\u200D\u2640", 1}, +{"\U0001F930\U0001F3FB", 1}, +{"\U0001F930\U0001F3FC", 1}, +{"\U0001F930\U0001F3FD", 1}, +{"\U0001F930\U0001F3FE", 1}, +{"\U0001F930\U0001F3FF", 1}, +{"\U0001FAC3\U0001F3FB", 1}, +{"\U0001FAC3\U0001F3FC", 1}, +{"\U0001FAC3\U0001F3FD", 1}, +{"\U0001FAC3\U0001F3FE", 1}, +{"\U0001FAC3\U0001F3FF", 1}, +{"\U0001FAC4\U0001F3FB", 1}, +{"\U0001FAC4\U0001F3FC", 1}, +{"\U0001FAC4\U0001F3FD", 1}, +{"\U0001FAC4\U0001F3FE", 1}, +{"\U0001FAC4\U0001F3FF", 1}, +{"\U0001F931\U0001F3FB", 1}, +{"\U0001F931\U0001F3FC", 1}, +{"\U0001F931\U0001F3FD", 1}, +{"\U0001F931\U0001F3FE", 1}, +{"\U0001F931\U0001F3FF", 1}, +{"\U0001F469\u200D\U0001F37C", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F37C", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F37C", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F37C", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F37C", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F37C", 1}, +{"\U0001F468\u200D\U0001F37C", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F37C", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F37C", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F37C", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F37C", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F37C", 1}, +{"\U0001F9D1\u200D\U0001F37C", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F37C", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F37C", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F37C", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F37C", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F37C", 1}, +{"\U0001F47C\U0001F3FB", 1}, +{"\U0001F47C\U0001F3FC", 1}, +{"\U0001F47C\U0001F3FD", 1}, +{"\U0001F47C\U0001F3FE", 1}, +{"\U0001F47C\U0001F3FF", 1}, +{"\U0001F385\U0001F3FB", 1}, +{"\U0001F385\U0001F3FC", 1}, +{"\U0001F385\U0001F3FD", 1}, +{"\U0001F385\U0001F3FE", 1}, +{"\U0001F385\U0001F3FF", 1}, +{"\U0001F936\U0001F3FB", 1}, +{"\U0001F936\U0001F3FC", 1}, +{"\U0001F936\U0001F3FD", 1}, +{"\U0001F936\U0001F3FE", 1}, +{"\U0001F936\U0001F3FF", 1}, +{"\U0001F9D1\u200D\U0001F384", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F384", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F384", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F384", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F384", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F384", 1}, +{"\U0001F9B8\U0001F3FB", 1}, +{"\U0001F9B8\U0001F3FC", 1}, +{"\U0001F9B8\U0001F3FD", 1}, +{"\U0001F9B8\U0001F3FE", 1}, +{"\U0001F9B8\U0001F3FF", 1}, +{"\U0001F9B8\u200D\u2642\uFE0F", 1}, +{"\U0001F9B8\u200D\u2642", 1}, +{"\U0001F9B8\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9B8\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9B8\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9B8\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9B8\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9B8\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9B8\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9B8\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9B8\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9B8\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9B8\u200D\u2640\uFE0F", 1}, +{"\U0001F9B8\u200D\u2640", 1}, +{"\U0001F9B8\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9B8\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9B8\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9B8\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9B8\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9B8\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9B8\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9B8\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9B8\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9B8\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9B9\U0001F3FB", 1}, +{"\U0001F9B9\U0001F3FC", 1}, +{"\U0001F9B9\U0001F3FD", 1}, +{"\U0001F9B9\U0001F3FE", 1}, +{"\U0001F9B9\U0001F3FF", 1}, +{"\U0001F9B9\u200D\u2642\uFE0F", 1}, +{"\U0001F9B9\u200D\u2642", 1}, +{"\U0001F9B9\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9B9\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9B9\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9B9\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9B9\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9B9\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9B9\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9B9\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9B9\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9B9\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9B9\u200D\u2640\uFE0F", 1}, +{"\U0001F9B9\u200D\u2640", 1}, +{"\U0001F9B9\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9B9\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9B9\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9B9\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9B9\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9B9\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9B9\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9B9\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9B9\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9B9\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9D9\U0001F3FB", 1}, +{"\U0001F9D9\U0001F3FC", 1}, +{"\U0001F9D9\U0001F3FD", 1}, +{"\U0001F9D9\U0001F3FE", 1}, +{"\U0001F9D9\U0001F3FF", 1}, +{"\U0001F9D9\u200D\u2642\uFE0F", 1}, +{"\U0001F9D9\u200D\u2642", 1}, +{"\U0001F9D9\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9D9\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9D9\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9D9\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9D9\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9D9\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9D9\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9D9\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9D9\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9D9\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9D9\u200D\u2640\uFE0F", 1}, +{"\U0001F9D9\u200D\u2640", 1}, +{"\U0001F9D9\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9D9\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9D9\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9D9\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9D9\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9D9\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9D9\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9D9\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9D9\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9D9\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9DA\U0001F3FB", 1}, +{"\U0001F9DA\U0001F3FC", 1}, +{"\U0001F9DA\U0001F3FD", 1}, +{"\U0001F9DA\U0001F3FE", 1}, +{"\U0001F9DA\U0001F3FF", 1}, +{"\U0001F9DA\u200D\u2642\uFE0F", 1}, +{"\U0001F9DA\u200D\u2642", 1}, +{"\U0001F9DA\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9DA\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9DA\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9DA\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9DA\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9DA\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9DA\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9DA\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9DA\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9DA\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9DA\u200D\u2640\uFE0F", 1}, +{"\U0001F9DA\u200D\u2640", 1}, +{"\U0001F9DA\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9DA\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9DA\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9DA\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9DA\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9DA\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9DA\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9DA\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9DA\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9DA\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9DB\U0001F3FB", 1}, +{"\U0001F9DB\U0001F3FC", 1}, +{"\U0001F9DB\U0001F3FD", 1}, +{"\U0001F9DB\U0001F3FE", 1}, +{"\U0001F9DB\U0001F3FF", 1}, +{"\U0001F9DB\u200D\u2642\uFE0F", 1}, +{"\U0001F9DB\u200D\u2642", 1}, +{"\U0001F9DB\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9DB\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9DB\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9DB\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9DB\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9DB\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9DB\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9DB\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9DB\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9DB\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9DB\u200D\u2640\uFE0F", 1}, +{"\U0001F9DB\u200D\u2640", 1}, +{"\U0001F9DB\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9DB\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9DB\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9DB\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9DB\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9DB\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9DB\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9DB\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9DB\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9DB\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9DC\U0001F3FB", 1}, +{"\U0001F9DC\U0001F3FC", 1}, +{"\U0001F9DC\U0001F3FD", 1}, +{"\U0001F9DC\U0001F3FE", 1}, +{"\U0001F9DC\U0001F3FF", 1}, +{"\U0001F9DC\u200D\u2642\uFE0F", 1}, +{"\U0001F9DC\u200D\u2642", 1}, +{"\U0001F9DC\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9DC\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9DC\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9DC\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9DC\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9DC\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9DC\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9DC\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9DC\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9DC\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9DC\u200D\u2640\uFE0F", 1}, +{"\U0001F9DC\u200D\u2640", 1}, +{"\U0001F9DC\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9DC\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9DC\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9DC\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9DC\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9DC\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9DC\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9DC\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9DC\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9DC\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9DD\U0001F3FB", 1}, +{"\U0001F9DD\U0001F3FC", 1}, +{"\U0001F9DD\U0001F3FD", 1}, +{"\U0001F9DD\U0001F3FE", 1}, +{"\U0001F9DD\U0001F3FF", 1}, +{"\U0001F9DD\u200D\u2642\uFE0F", 1}, +{"\U0001F9DD\u200D\u2642", 1}, +{"\U0001F9DD\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9DD\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9DD\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9DD\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9DD\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9DD\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9DD\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9DD\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9DD\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9DD\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9DD\u200D\u2640\uFE0F", 1}, +{"\U0001F9DD\u200D\u2640", 1}, +{"\U0001F9DD\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9DD\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9DD\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9DD\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9DD\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9DD\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9DD\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9DD\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9DD\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9DD\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9DE\u200D\u2642\uFE0F", 1}, +{"\U0001F9DE\u200D\u2642", 1}, +{"\U0001F9DE\u200D\u2640\uFE0F", 1}, +{"\U0001F9DE\u200D\u2640", 1}, +{"\U0001F9DF\u200D\u2642\uFE0F", 1}, +{"\U0001F9DF\u200D\u2642", 1}, +{"\U0001F9DF\u200D\u2640\uFE0F", 1}, +{"\U0001F9DF\u200D\u2640", 1}, +{"\U0001F486\U0001F3FB", 1}, +{"\U0001F486\U0001F3FC", 1}, +{"\U0001F486\U0001F3FD", 1}, +{"\U0001F486\U0001F3FE", 1}, +{"\U0001F486\U0001F3FF", 1}, +{"\U0001F486\u200D\u2642\uFE0F", 1}, +{"\U0001F486\u200D\u2642", 1}, +{"\U0001F486\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F486\U0001F3FB\u200D\u2642", 1}, +{"\U0001F486\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F486\U0001F3FC\u200D\u2642", 1}, +{"\U0001F486\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F486\U0001F3FD\u200D\u2642", 1}, +{"\U0001F486\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F486\U0001F3FE\u200D\u2642", 1}, +{"\U0001F486\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F486\U0001F3FF\u200D\u2642", 1}, +{"\U0001F486\u200D\u2640\uFE0F", 1}, +{"\U0001F486\u200D\u2640", 1}, +{"\U0001F486\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F486\U0001F3FB\u200D\u2640", 1}, +{"\U0001F486\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F486\U0001F3FC\u200D\u2640", 1}, +{"\U0001F486\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F486\U0001F3FD\u200D\u2640", 1}, +{"\U0001F486\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F486\U0001F3FE\u200D\u2640", 1}, +{"\U0001F486\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F486\U0001F3FF\u200D\u2640", 1}, +{"\U0001F487\U0001F3FB", 1}, +{"\U0001F487\U0001F3FC", 1}, +{"\U0001F487\U0001F3FD", 1}, +{"\U0001F487\U0001F3FE", 1}, +{"\U0001F487\U0001F3FF", 1}, +{"\U0001F487\u200D\u2642\uFE0F", 1}, +{"\U0001F487\u200D\u2642", 1}, +{"\U0001F487\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F487\U0001F3FB\u200D\u2642", 1}, +{"\U0001F487\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F487\U0001F3FC\u200D\u2642", 1}, +{"\U0001F487\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F487\U0001F3FD\u200D\u2642", 1}, +{"\U0001F487\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F487\U0001F3FE\u200D\u2642", 1}, +{"\U0001F487\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F487\U0001F3FF\u200D\u2642", 1}, +{"\U0001F487\u200D\u2640\uFE0F", 1}, +{"\U0001F487\u200D\u2640", 1}, +{"\U0001F487\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F487\U0001F3FB\u200D\u2640", 1}, +{"\U0001F487\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F487\U0001F3FC\u200D\u2640", 1}, +{"\U0001F487\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F487\U0001F3FD\u200D\u2640", 1}, +{"\U0001F487\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F487\U0001F3FE\u200D\u2640", 1}, +{"\U0001F487\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F487\U0001F3FF\u200D\u2640", 1}, +{"\U0001F6B6\U0001F3FB", 1}, +{"\U0001F6B6\U0001F3FC", 1}, +{"\U0001F6B6\U0001F3FD", 1}, +{"\U0001F6B6\U0001F3FE", 1}, +{"\U0001F6B6\U0001F3FF", 1}, +{"\U0001F6B6\u200D\u2642\uFE0F", 1}, +{"\U0001F6B6\u200D\u2642", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2642", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2642", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2642", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2642", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2642", 1}, +{"\U0001F6B6\u200D\u2640\uFE0F", 1}, +{"\U0001F6B6\u200D\u2640", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2640", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2640", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2640", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2640", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2640", 1}, +{"\U0001F6B6\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u27A1", 1}, +{"\U0001F6B6\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F6B6\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FB\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FC\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FD\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FE\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F6B6\U0001F3FF\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9CD\U0001F3FB", 1}, +{"\U0001F9CD\U0001F3FC", 1}, +{"\U0001F9CD\U0001F3FD", 1}, +{"\U0001F9CD\U0001F3FE", 1}, +{"\U0001F9CD\U0001F3FF", 1}, +{"\U0001F9CD\u200D\u2642\uFE0F", 1}, +{"\U0001F9CD\u200D\u2642", 1}, +{"\U0001F9CD\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9CD\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9CD\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9CD\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9CD\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9CD\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9CD\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9CD\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9CD\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9CD\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9CD\u200D\u2640\uFE0F", 1}, +{"\U0001F9CD\u200D\u2640", 1}, +{"\U0001F9CD\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9CD\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9CD\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9CD\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9CD\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9CD\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9CD\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9CD\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9CD\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9CD\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9CE\U0001F3FB", 1}, +{"\U0001F9CE\U0001F3FC", 1}, +{"\U0001F9CE\U0001F3FD", 1}, +{"\U0001F9CE\U0001F3FE", 1}, +{"\U0001F9CE\U0001F3FF", 1}, +{"\U0001F9CE\u200D\u2642\uFE0F", 1}, +{"\U0001F9CE\u200D\u2642", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9CE\u200D\u2640\uFE0F", 1}, +{"\U0001F9CE\u200D\u2640", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9CE\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u27A1", 1}, +{"\U0001F9CE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F9CE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FB\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FC\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FD\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FE\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F9CE\U0001F3FF\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F9D1\u200D\U0001F9AF", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9AF", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9AF", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9AF", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9AF", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9AF", 1}, +{"\U0001F9D1\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F468\u200D\U0001F9AF", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9AF", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9AF", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9AF", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9AF", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9AF", 1}, +{"\U0001F468\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F469\u200D\U0001F9AF", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9AF", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9AF", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9AF", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9AF", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9AF", 1}, +{"\U0001F469\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9AF\u200D\u27A1", 1}, +{"\U0001F9D1\u200D\U0001F9BC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9BC", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9BC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9BC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9BC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9BC", 1}, +{"\U0001F9D1\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F468\u200D\U0001F9BC", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9BC", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9BC", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9BC", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9BC", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9BC", 1}, +{"\U0001F468\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F469\u200D\U0001F9BC", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9BC", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9BC", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9BC", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9BC", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9BC", 1}, +{"\U0001F469\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9BC\u200D\u27A1", 1}, +{"\U0001F9D1\u200D\U0001F9BD", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9BD", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9BD", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9BD", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9BD", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9BD", 1}, +{"\U0001F9D1\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F468\u200D\U0001F9BD", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9BD", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9BD", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9BD", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9BD", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9BD", 1}, +{"\U0001F468\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F469\u200D\U0001F9BD", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9BD", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9BD", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9BD", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9BD", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9BD", 1}, +{"\U0001F469\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F9BD\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FB", 1}, +{"\U0001F3C3\U0001F3FC", 1}, +{"\U0001F3C3\U0001F3FD", 1}, +{"\U0001F3C3\U0001F3FE", 1}, +{"\U0001F3C3\U0001F3FF", 1}, +{"\U0001F3C3\u200D\u2642\uFE0F", 1}, +{"\U0001F3C3\u200D\u2642", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2642", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2642", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2642", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2642", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2642", 1}, +{"\U0001F3C3\u200D\u2640\uFE0F", 1}, +{"\U0001F3C3\u200D\u2640", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2640", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2640", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2640", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2640", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2640", 1}, +{"\U0001F3C3\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u27A1", 1}, +{"\U0001F3C3\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2640\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2640\u200D\u27A1", 1}, +{"\U0001F3C3\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FB\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FC\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FD\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FE\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2642\u200D\u27A1\uFE0F", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1", 1}, +{"\U0001F3C3\U0001F3FF\u200D\u2642\u200D\u27A1", 1}, +{"\U0001F483\U0001F3FB", 1}, +{"\U0001F483\U0001F3FC", 1}, +{"\U0001F483\U0001F3FD", 1}, +{"\U0001F483\U0001F3FE", 1}, +{"\U0001F483\U0001F3FF", 1}, +{"\U0001F57A\U0001F3FB", 1}, +{"\U0001F57A\U0001F3FC", 1}, +{"\U0001F57A\U0001F3FD", 1}, +{"\U0001F57A\U0001F3FE", 1}, +{"\U0001F57A\U0001F3FF", 1}, +{"\U0001F574\uFE0F", 1}, +{"\U0001F574\U0001F3FB", 1}, +{"\U0001F574\U0001F3FC", 1}, +{"\U0001F574\U0001F3FD", 1}, +{"\U0001F574\U0001F3FE", 1}, +{"\U0001F574\U0001F3FF", 1}, +{"\U0001F46F\u200D\u2642\uFE0F", 1}, +{"\U0001F46F\u200D\u2642", 1}, +{"\U0001F46F\u200D\u2640\uFE0F", 1}, +{"\U0001F46F\u200D\u2640", 1}, +{"\U0001F9D6\U0001F3FB", 1}, +{"\U0001F9D6\U0001F3FC", 1}, +{"\U0001F9D6\U0001F3FD", 1}, +{"\U0001F9D6\U0001F3FE", 1}, +{"\U0001F9D6\U0001F3FF", 1}, +{"\U0001F9D6\u200D\u2642\uFE0F", 1}, +{"\U0001F9D6\u200D\u2642", 1}, +{"\U0001F9D6\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9D6\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9D6\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9D6\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9D6\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9D6\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9D6\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9D6\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9D6\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9D6\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9D6\u200D\u2640\uFE0F", 1}, +{"\U0001F9D6\u200D\u2640", 1}, +{"\U0001F9D6\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9D6\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9D6\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9D6\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9D6\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9D6\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9D6\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9D6\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9D6\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9D6\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9D7\U0001F3FB", 1}, +{"\U0001F9D7\U0001F3FC", 1}, +{"\U0001F9D7\U0001F3FD", 1}, +{"\U0001F9D7\U0001F3FE", 1}, +{"\U0001F9D7\U0001F3FF", 1}, +{"\U0001F9D7\u200D\u2642\uFE0F", 1}, +{"\U0001F9D7\u200D\u2642", 1}, +{"\U0001F9D7\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9D7\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9D7\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9D7\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9D7\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9D7\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9D7\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9D7\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9D7\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9D7\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9D7\u200D\u2640\uFE0F", 1}, +{"\U0001F9D7\u200D\u2640", 1}, +{"\U0001F9D7\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9D7\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9D7\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9D7\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9D7\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9D7\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9D7\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9D7\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9D7\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9D7\U0001F3FF\u200D\u2640", 1}, +{"\U0001F3C7\U0001F3FB", 1}, +{"\U0001F3C7\U0001F3FC", 1}, +{"\U0001F3C7\U0001F3FD", 1}, +{"\U0001F3C7\U0001F3FE", 1}, +{"\U0001F3C7\U0001F3FF", 1}, +{"\u26F7\uFE0F", 1}, +{"\U0001F3C2\U0001F3FB", 1}, +{"\U0001F3C2\U0001F3FC", 1}, +{"\U0001F3C2\U0001F3FD", 1}, +{"\U0001F3C2\U0001F3FE", 1}, +{"\U0001F3C2\U0001F3FF", 1}, +{"\U0001F3CC\uFE0F", 1}, +{"\U0001F3CC\U0001F3FB", 1}, +{"\U0001F3CC\U0001F3FC", 1}, +{"\U0001F3CC\U0001F3FD", 1}, +{"\U0001F3CC\U0001F3FE", 1}, +{"\U0001F3CC\U0001F3FF", 1}, +{"\U0001F3CC\uFE0F\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\uFE0F\u200D\u2642", 1}, +{"\U0001F3CC\u200D\u2642", 1}, +{"\U0001F3CC\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\U0001F3FB\u200D\u2642", 1}, +{"\U0001F3CC\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\U0001F3FC\u200D\u2642", 1}, +{"\U0001F3CC\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\U0001F3FD\u200D\u2642", 1}, +{"\U0001F3CC\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\U0001F3FE\u200D\u2642", 1}, +{"\U0001F3CC\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F3CC\U0001F3FF\u200D\u2642", 1}, +{"\U0001F3CC\uFE0F\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\uFE0F\u200D\u2640", 1}, +{"\U0001F3CC\u200D\u2640", 1}, +{"\U0001F3CC\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\U0001F3FB\u200D\u2640", 1}, +{"\U0001F3CC\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\U0001F3FC\u200D\u2640", 1}, +{"\U0001F3CC\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\U0001F3FD\u200D\u2640", 1}, +{"\U0001F3CC\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\U0001F3FE\u200D\u2640", 1}, +{"\U0001F3CC\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F3CC\U0001F3FF\u200D\u2640", 1}, +{"\U0001F3C4\U0001F3FB", 1}, +{"\U0001F3C4\U0001F3FC", 1}, +{"\U0001F3C4\U0001F3FD", 1}, +{"\U0001F3C4\U0001F3FE", 1}, +{"\U0001F3C4\U0001F3FF", 1}, +{"\U0001F3C4\u200D\u2642\uFE0F", 1}, +{"\U0001F3C4\u200D\u2642", 1}, +{"\U0001F3C4\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F3C4\U0001F3FB\u200D\u2642", 1}, +{"\U0001F3C4\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F3C4\U0001F3FC\u200D\u2642", 1}, +{"\U0001F3C4\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F3C4\U0001F3FD\u200D\u2642", 1}, +{"\U0001F3C4\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F3C4\U0001F3FE\u200D\u2642", 1}, +{"\U0001F3C4\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F3C4\U0001F3FF\u200D\u2642", 1}, +{"\U0001F3C4\u200D\u2640\uFE0F", 1}, +{"\U0001F3C4\u200D\u2640", 1}, +{"\U0001F3C4\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F3C4\U0001F3FB\u200D\u2640", 1}, +{"\U0001F3C4\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F3C4\U0001F3FC\u200D\u2640", 1}, +{"\U0001F3C4\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F3C4\U0001F3FD\u200D\u2640", 1}, +{"\U0001F3C4\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F3C4\U0001F3FE\u200D\u2640", 1}, +{"\U0001F3C4\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F3C4\U0001F3FF\u200D\u2640", 1}, +{"\U0001F6A3\U0001F3FB", 1}, +{"\U0001F6A3\U0001F3FC", 1}, +{"\U0001F6A3\U0001F3FD", 1}, +{"\U0001F6A3\U0001F3FE", 1}, +{"\U0001F6A3\U0001F3FF", 1}, +{"\U0001F6A3\u200D\u2642\uFE0F", 1}, +{"\U0001F6A3\u200D\u2642", 1}, +{"\U0001F6A3\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F6A3\U0001F3FB\u200D\u2642", 1}, +{"\U0001F6A3\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F6A3\U0001F3FC\u200D\u2642", 1}, +{"\U0001F6A3\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F6A3\U0001F3FD\u200D\u2642", 1}, +{"\U0001F6A3\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F6A3\U0001F3FE\u200D\u2642", 1}, +{"\U0001F6A3\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F6A3\U0001F3FF\u200D\u2642", 1}, +{"\U0001F6A3\u200D\u2640\uFE0F", 1}, +{"\U0001F6A3\u200D\u2640", 1}, +{"\U0001F6A3\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F6A3\U0001F3FB\u200D\u2640", 1}, +{"\U0001F6A3\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F6A3\U0001F3FC\u200D\u2640", 1}, +{"\U0001F6A3\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F6A3\U0001F3FD\u200D\u2640", 1}, +{"\U0001F6A3\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F6A3\U0001F3FE\u200D\u2640", 1}, +{"\U0001F6A3\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F6A3\U0001F3FF\u200D\u2640", 1}, +{"\U0001F3CA\U0001F3FB", 1}, +{"\U0001F3CA\U0001F3FC", 1}, +{"\U0001F3CA\U0001F3FD", 1}, +{"\U0001F3CA\U0001F3FE", 1}, +{"\U0001F3CA\U0001F3FF", 1}, +{"\U0001F3CA\u200D\u2642\uFE0F", 1}, +{"\U0001F3CA\u200D\u2642", 1}, +{"\U0001F3CA\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F3CA\U0001F3FB\u200D\u2642", 1}, +{"\U0001F3CA\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F3CA\U0001F3FC\u200D\u2642", 1}, +{"\U0001F3CA\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F3CA\U0001F3FD\u200D\u2642", 1}, +{"\U0001F3CA\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F3CA\U0001F3FE\u200D\u2642", 1}, +{"\U0001F3CA\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F3CA\U0001F3FF\u200D\u2642", 1}, +{"\U0001F3CA\u200D\u2640\uFE0F", 1}, +{"\U0001F3CA\u200D\u2640", 1}, +{"\U0001F3CA\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F3CA\U0001F3FB\u200D\u2640", 1}, +{"\U0001F3CA\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F3CA\U0001F3FC\u200D\u2640", 1}, +{"\U0001F3CA\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F3CA\U0001F3FD\u200D\u2640", 1}, +{"\U0001F3CA\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F3CA\U0001F3FE\u200D\u2640", 1}, +{"\U0001F3CA\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F3CA\U0001F3FF\u200D\u2640", 1}, +{"\u26F9\uFE0F", 1}, +{"\u26F9\U0001F3FB", 1}, +{"\u26F9\U0001F3FC", 1}, +{"\u26F9\U0001F3FD", 1}, +{"\u26F9\U0001F3FE", 1}, +{"\u26F9\U0001F3FF", 1}, +{"\u26F9\uFE0F\u200D\u2642\uFE0F", 1}, +{"\u26F9\u200D\u2642\uFE0F", 1}, +{"\u26F9\uFE0F\u200D\u2642", 1}, +{"\u26F9\u200D\u2642", 1}, +{"\u26F9\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\u26F9\U0001F3FB\u200D\u2642", 1}, +{"\u26F9\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\u26F9\U0001F3FC\u200D\u2642", 1}, +{"\u26F9\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\u26F9\U0001F3FD\u200D\u2642", 1}, +{"\u26F9\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\u26F9\U0001F3FE\u200D\u2642", 1}, +{"\u26F9\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\u26F9\U0001F3FF\u200D\u2642", 1}, +{"\u26F9\uFE0F\u200D\u2640\uFE0F", 1}, +{"\u26F9\u200D\u2640\uFE0F", 1}, +{"\u26F9\uFE0F\u200D\u2640", 1}, +{"\u26F9\u200D\u2640", 1}, +{"\u26F9\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\u26F9\U0001F3FB\u200D\u2640", 1}, +{"\u26F9\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\u26F9\U0001F3FC\u200D\u2640", 1}, +{"\u26F9\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\u26F9\U0001F3FD\u200D\u2640", 1}, +{"\u26F9\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\u26F9\U0001F3FE\u200D\u2640", 1}, +{"\u26F9\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\u26F9\U0001F3FF\u200D\u2640", 1}, +{"\U0001F3CB\uFE0F", 1}, +{"\U0001F3CB\U0001F3FB", 1}, +{"\U0001F3CB\U0001F3FC", 1}, +{"\U0001F3CB\U0001F3FD", 1}, +{"\U0001F3CB\U0001F3FE", 1}, +{"\U0001F3CB\U0001F3FF", 1}, +{"\U0001F3CB\uFE0F\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\uFE0F\u200D\u2642", 1}, +{"\U0001F3CB\u200D\u2642", 1}, +{"\U0001F3CB\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\U0001F3FB\u200D\u2642", 1}, +{"\U0001F3CB\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\U0001F3FC\u200D\u2642", 1}, +{"\U0001F3CB\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\U0001F3FD\u200D\u2642", 1}, +{"\U0001F3CB\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\U0001F3FE\u200D\u2642", 1}, +{"\U0001F3CB\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F3CB\U0001F3FF\u200D\u2642", 1}, +{"\U0001F3CB\uFE0F\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\uFE0F\u200D\u2640", 1}, +{"\U0001F3CB\u200D\u2640", 1}, +{"\U0001F3CB\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\U0001F3FB\u200D\u2640", 1}, +{"\U0001F3CB\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\U0001F3FC\u200D\u2640", 1}, +{"\U0001F3CB\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\U0001F3FD\u200D\u2640", 1}, +{"\U0001F3CB\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\U0001F3FE\u200D\u2640", 1}, +{"\U0001F3CB\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F3CB\U0001F3FF\u200D\u2640", 1}, +{"\U0001F6B4\U0001F3FB", 1}, +{"\U0001F6B4\U0001F3FC", 1}, +{"\U0001F6B4\U0001F3FD", 1}, +{"\U0001F6B4\U0001F3FE", 1}, +{"\U0001F6B4\U0001F3FF", 1}, +{"\U0001F6B4\u200D\u2642\uFE0F", 1}, +{"\U0001F6B4\u200D\u2642", 1}, +{"\U0001F6B4\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F6B4\U0001F3FB\u200D\u2642", 1}, +{"\U0001F6B4\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F6B4\U0001F3FC\u200D\u2642", 1}, +{"\U0001F6B4\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F6B4\U0001F3FD\u200D\u2642", 1}, +{"\U0001F6B4\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F6B4\U0001F3FE\u200D\u2642", 1}, +{"\U0001F6B4\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F6B4\U0001F3FF\u200D\u2642", 1}, +{"\U0001F6B4\u200D\u2640\uFE0F", 1}, +{"\U0001F6B4\u200D\u2640", 1}, +{"\U0001F6B4\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F6B4\U0001F3FB\u200D\u2640", 1}, +{"\U0001F6B4\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F6B4\U0001F3FC\u200D\u2640", 1}, +{"\U0001F6B4\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F6B4\U0001F3FD\u200D\u2640", 1}, +{"\U0001F6B4\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F6B4\U0001F3FE\u200D\u2640", 1}, +{"\U0001F6B4\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F6B4\U0001F3FF\u200D\u2640", 1}, +{"\U0001F6B5\U0001F3FB", 1}, +{"\U0001F6B5\U0001F3FC", 1}, +{"\U0001F6B5\U0001F3FD", 1}, +{"\U0001F6B5\U0001F3FE", 1}, +{"\U0001F6B5\U0001F3FF", 1}, +{"\U0001F6B5\u200D\u2642\uFE0F", 1}, +{"\U0001F6B5\u200D\u2642", 1}, +{"\U0001F6B5\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F6B5\U0001F3FB\u200D\u2642", 1}, +{"\U0001F6B5\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F6B5\U0001F3FC\u200D\u2642", 1}, +{"\U0001F6B5\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F6B5\U0001F3FD\u200D\u2642", 1}, +{"\U0001F6B5\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F6B5\U0001F3FE\u200D\u2642", 1}, +{"\U0001F6B5\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F6B5\U0001F3FF\u200D\u2642", 1}, +{"\U0001F6B5\u200D\u2640\uFE0F", 1}, +{"\U0001F6B5\u200D\u2640", 1}, +{"\U0001F6B5\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F6B5\U0001F3FB\u200D\u2640", 1}, +{"\U0001F6B5\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F6B5\U0001F3FC\u200D\u2640", 1}, +{"\U0001F6B5\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F6B5\U0001F3FD\u200D\u2640", 1}, +{"\U0001F6B5\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F6B5\U0001F3FE\u200D\u2640", 1}, +{"\U0001F6B5\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F6B5\U0001F3FF\u200D\u2640", 1}, +{"\U0001F938\U0001F3FB", 1}, +{"\U0001F938\U0001F3FC", 1}, +{"\U0001F938\U0001F3FD", 1}, +{"\U0001F938\U0001F3FE", 1}, +{"\U0001F938\U0001F3FF", 1}, +{"\U0001F938\u200D\u2642\uFE0F", 1}, +{"\U0001F938\u200D\u2642", 1}, +{"\U0001F938\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F938\U0001F3FB\u200D\u2642", 1}, +{"\U0001F938\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F938\U0001F3FC\u200D\u2642", 1}, +{"\U0001F938\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F938\U0001F3FD\u200D\u2642", 1}, +{"\U0001F938\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F938\U0001F3FE\u200D\u2642", 1}, +{"\U0001F938\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F938\U0001F3FF\u200D\u2642", 1}, +{"\U0001F938\u200D\u2640\uFE0F", 1}, +{"\U0001F938\u200D\u2640", 1}, +{"\U0001F938\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F938\U0001F3FB\u200D\u2640", 1}, +{"\U0001F938\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F938\U0001F3FC\u200D\u2640", 1}, +{"\U0001F938\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F938\U0001F3FD\u200D\u2640", 1}, +{"\U0001F938\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F938\U0001F3FE\u200D\u2640", 1}, +{"\U0001F938\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F938\U0001F3FF\u200D\u2640", 1}, +{"\U0001F93C\u200D\u2642\uFE0F", 1}, +{"\U0001F93C\u200D\u2642", 1}, +{"\U0001F93C\u200D\u2640\uFE0F", 1}, +{"\U0001F93C\u200D\u2640", 1}, +{"\U0001F93D\U0001F3FB", 1}, +{"\U0001F93D\U0001F3FC", 1}, +{"\U0001F93D\U0001F3FD", 1}, +{"\U0001F93D\U0001F3FE", 1}, +{"\U0001F93D\U0001F3FF", 1}, +{"\U0001F93D\u200D\u2642\uFE0F", 1}, +{"\U0001F93D\u200D\u2642", 1}, +{"\U0001F93D\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F93D\U0001F3FB\u200D\u2642", 1}, +{"\U0001F93D\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F93D\U0001F3FC\u200D\u2642", 1}, +{"\U0001F93D\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F93D\U0001F3FD\u200D\u2642", 1}, +{"\U0001F93D\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F93D\U0001F3FE\u200D\u2642", 1}, +{"\U0001F93D\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F93D\U0001F3FF\u200D\u2642", 1}, +{"\U0001F93D\u200D\u2640\uFE0F", 1}, +{"\U0001F93D\u200D\u2640", 1}, +{"\U0001F93D\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F93D\U0001F3FB\u200D\u2640", 1}, +{"\U0001F93D\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F93D\U0001F3FC\u200D\u2640", 1}, +{"\U0001F93D\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F93D\U0001F3FD\u200D\u2640", 1}, +{"\U0001F93D\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F93D\U0001F3FE\u200D\u2640", 1}, +{"\U0001F93D\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F93D\U0001F3FF\u200D\u2640", 1}, +{"\U0001F93E\U0001F3FB", 1}, +{"\U0001F93E\U0001F3FC", 1}, +{"\U0001F93E\U0001F3FD", 1}, +{"\U0001F93E\U0001F3FE", 1}, +{"\U0001F93E\U0001F3FF", 1}, +{"\U0001F93E\u200D\u2642\uFE0F", 1}, +{"\U0001F93E\u200D\u2642", 1}, +{"\U0001F93E\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F93E\U0001F3FB\u200D\u2642", 1}, +{"\U0001F93E\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F93E\U0001F3FC\u200D\u2642", 1}, +{"\U0001F93E\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F93E\U0001F3FD\u200D\u2642", 1}, +{"\U0001F93E\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F93E\U0001F3FE\u200D\u2642", 1}, +{"\U0001F93E\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F93E\U0001F3FF\u200D\u2642", 1}, +{"\U0001F93E\u200D\u2640\uFE0F", 1}, +{"\U0001F93E\u200D\u2640", 1}, +{"\U0001F93E\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F93E\U0001F3FB\u200D\u2640", 1}, +{"\U0001F93E\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F93E\U0001F3FC\u200D\u2640", 1}, +{"\U0001F93E\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F93E\U0001F3FD\u200D\u2640", 1}, +{"\U0001F93E\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F93E\U0001F3FE\u200D\u2640", 1}, +{"\U0001F93E\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F93E\U0001F3FF\u200D\u2640", 1}, +{"\U0001F939\U0001F3FB", 1}, +{"\U0001F939\U0001F3FC", 1}, +{"\U0001F939\U0001F3FD", 1}, +{"\U0001F939\U0001F3FE", 1}, +{"\U0001F939\U0001F3FF", 1}, +{"\U0001F939\u200D\u2642\uFE0F", 1}, +{"\U0001F939\u200D\u2642", 1}, +{"\U0001F939\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F939\U0001F3FB\u200D\u2642", 1}, +{"\U0001F939\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F939\U0001F3FC\u200D\u2642", 1}, +{"\U0001F939\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F939\U0001F3FD\u200D\u2642", 1}, +{"\U0001F939\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F939\U0001F3FE\u200D\u2642", 1}, +{"\U0001F939\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F939\U0001F3FF\u200D\u2642", 1}, +{"\U0001F939\u200D\u2640\uFE0F", 1}, +{"\U0001F939\u200D\u2640", 1}, +{"\U0001F939\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F939\U0001F3FB\u200D\u2640", 1}, +{"\U0001F939\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F939\U0001F3FC\u200D\u2640", 1}, +{"\U0001F939\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F939\U0001F3FD\u200D\u2640", 1}, +{"\U0001F939\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F939\U0001F3FE\u200D\u2640", 1}, +{"\U0001F939\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F939\U0001F3FF\u200D\u2640", 1}, +{"\U0001F9D8\U0001F3FB", 1}, +{"\U0001F9D8\U0001F3FC", 1}, +{"\U0001F9D8\U0001F3FD", 1}, +{"\U0001F9D8\U0001F3FE", 1}, +{"\U0001F9D8\U0001F3FF", 1}, +{"\U0001F9D8\u200D\u2642\uFE0F", 1}, +{"\U0001F9D8\u200D\u2642", 1}, +{"\U0001F9D8\U0001F3FB\u200D\u2642\uFE0F", 1}, +{"\U0001F9D8\U0001F3FB\u200D\u2642", 1}, +{"\U0001F9D8\U0001F3FC\u200D\u2642\uFE0F", 1}, +{"\U0001F9D8\U0001F3FC\u200D\u2642", 1}, +{"\U0001F9D8\U0001F3FD\u200D\u2642\uFE0F", 1}, +{"\U0001F9D8\U0001F3FD\u200D\u2642", 1}, +{"\U0001F9D8\U0001F3FE\u200D\u2642\uFE0F", 1}, +{"\U0001F9D8\U0001F3FE\u200D\u2642", 1}, +{"\U0001F9D8\U0001F3FF\u200D\u2642\uFE0F", 1}, +{"\U0001F9D8\U0001F3FF\u200D\u2642", 1}, +{"\U0001F9D8\u200D\u2640\uFE0F", 1}, +{"\U0001F9D8\u200D\u2640", 1}, +{"\U0001F9D8\U0001F3FB\u200D\u2640\uFE0F", 1}, +{"\U0001F9D8\U0001F3FB\u200D\u2640", 1}, +{"\U0001F9D8\U0001F3FC\u200D\u2640\uFE0F", 1}, +{"\U0001F9D8\U0001F3FC\u200D\u2640", 1}, +{"\U0001F9D8\U0001F3FD\u200D\u2640\uFE0F", 1}, +{"\U0001F9D8\U0001F3FD\u200D\u2640", 1}, +{"\U0001F9D8\U0001F3FE\u200D\u2640\uFE0F", 1}, +{"\U0001F9D8\U0001F3FE\u200D\u2640", 1}, +{"\U0001F9D8\U0001F3FF\u200D\u2640\uFE0F", 1}, +{"\U0001F9D8\U0001F3FF\u200D\u2640", 1}, +{"\U0001F6C0\U0001F3FB", 1}, +{"\U0001F6C0\U0001F3FC", 1}, +{"\U0001F6C0\U0001F3FD", 1}, +{"\U0001F6C0\U0001F3FE", 1}, +{"\U0001F6C0\U0001F3FF", 1}, +{"\U0001F6CC\U0001F3FB", 1}, +{"\U0001F6CC\U0001F3FC", 1}, +{"\U0001F6CC\U0001F3FD", 1}, +{"\U0001F6CC\U0001F3FE", 1}, +{"\U0001F6CC\U0001F3FF", 1}, +{"\U0001F9D1\u200D\U0001F91D\u200D\U0001F9D1", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F46D\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F46D\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F46D\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F46D\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F46D\U0001F3FF", 1}, +{"\U0001F46B\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F46B\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F46B\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F46B\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F46B\U0001F3FF", 1}, +{"\U0001F46C\U0001F3FB", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F46C\U0001F3FC", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F46C\U0001F3FD", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F46C\U0001F3FE", 1}, +{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F46C\U0001F3FF", 1}, +{"\U0001F48F\U0001F3FB", 1}, +{"\U0001F48F\U0001F3FC", 1}, +{"\U0001F48F\U0001F3FD", 1}, +{"\U0001F48F\U0001F3FE", 1}, +{"\U0001F48F\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468", 1}, +{"\U0001F469\u200D\u2764\u200D\U0001F48B\u200D\U0001F468", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468", 1}, +{"\U0001F468\u200D\u2764\u200D\U0001F48B\u200D\U0001F468", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469", 1}, +{"\U0001F469\u200D\u2764\u200D\U0001F48B\u200D\U0001F469", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F491\U0001F3FB", 1}, +{"\U0001F491\U0001F3FC", 1}, +{"\U0001F491\U0001F3FD", 1}, +{"\U0001F491\U0001F3FE", 1}, +{"\U0001F491\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1}, +{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F468", 1}, +{"\U0001F469\u200D\u2764\u200D\U0001F468", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F468", 1}, +{"\U0001F468\u200D\u2764\u200D\U0001F468", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FB", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FC", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FD", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FE", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FF", 1}, +{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F469", 1}, +{"\U0001F469\u200D\u2764\u200D\U0001F469", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FB", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FC", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FD", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FE", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FF", 1}, +{"\U0001F468\u200D\U0001F469\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F469\u200D\U0001F467", 1}, +{"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F469\u200D\U0001F466\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F467", 1}, +{"\U0001F468\u200D\U0001F468\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F468\u200D\U0001F467", 1}, +{"\U0001F468\u200D\U0001F468\u200D\U0001F467\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F468\u200D\U0001F466\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F468\u200D\U0001F467\u200D\U0001F467", 1}, +{"\U0001F469\u200D\U0001F469\u200D\U0001F466", 1}, +{"\U0001F469\u200D\U0001F469\u200D\U0001F467", 1}, +{"\U0001F469\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 1}, +{"\U0001F469\u200D\U0001F469\u200D\U0001F466\u200D\U0001F466", 1}, +{"\U0001F469\u200D\U0001F469\u200D\U0001F467\u200D\U0001F467", 1}, +{"\U0001F468\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F466\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F467", 1}, +{"\U0001F468\u200D\U0001F467\u200D\U0001F466", 1}, +{"\U0001F468\u200D\U0001F467\u200D\U0001F467", 1}, +{"\U0001F469\u200D\U0001F466", 1}, +{"\U0001F469\u200D\U0001F466\u200D\U0001F466", 1}, +{"\U0001F469\u200D\U0001F467", 1}, +{"\U0001F469\u200D\U0001F467\u200D\U0001F466", 1}, +{"\U0001F469\u200D\U0001F467\u200D\U0001F467", 1}, +{"\U0001F5E3\uFE0F", 1}, +{"\U0001F9D1\u200D\U0001F9D1\u200D\U0001F9D2", 1}, +{"\U0001F9D1\u200D\U0001F9D1\u200D\U0001F9D2\u200D\U0001F9D2", 1}, +{"\U0001F9D1\u200D\U0001F9D2", 1}, +{"\U0001F9D1\u200D\U0001F9D2\u200D\U0001F9D2", 1}, +{"\U0001F415\u200D\U0001F9BA", 1}, +{"\U0001F408\u200D\u2B1B", 1}, +{"\U0001F43F\uFE0F", 1}, +{"\U0001F43B\u200D\u2744\uFE0F", 1}, +{"\U0001F43B\u200D\u2744", 1}, +{"\U0001F54A\uFE0F", 1}, +{"\U0001F426\u200D\u2B1B", 1}, +{"\U0001F426\u200D\U0001F525", 1}, +{"\U0001F577\uFE0F", 1}, +{"\U0001F578\uFE0F", 1}, +{"\U0001F3F5\uFE0F", 1}, +{"\u2618\uFE0F", 1}, +{"\U0001F34B\u200D\U0001F7E9", 1}, +{"\U0001F336\uFE0F", 1}, +{"\U0001F344\u200D\U0001F7EB", 1}, +{"\U0001F37D\uFE0F", 1}, +{"\U0001F5FA\uFE0F", 1}, +{"\U0001F3D4\uFE0F", 1}, +{"\u26F0\uFE0F", 1}, +{"\U0001F3D5\uFE0F", 1}, +{"\U0001F3D6\uFE0F", 1}, +{"\U0001F3DC\uFE0F", 1}, +{"\U0001F3DD\uFE0F", 1}, +{"\U0001F3DE\uFE0F", 1}, +{"\U0001F3DF\uFE0F", 1}, +{"\U0001F3DB\uFE0F", 1}, +{"\U0001F3D7\uFE0F", 1}, +{"\U0001F3D8\uFE0F", 1}, +{"\U0001F3DA\uFE0F", 1}, +{"\u26E9\uFE0F", 1}, +{"\U0001F3D9\uFE0F", 1}, +{"\u2668\uFE0F", 1}, +{"\U0001F3CE\uFE0F", 1}, +{"\U0001F3CD\uFE0F", 1}, +{"\U0001F6E3\uFE0F", 1}, +{"\U0001F6E4\uFE0F", 1}, +{"\U0001F6E2\uFE0F", 1}, +{"\U0001F6F3\uFE0F", 1}, +{"\u26F4\uFE0F", 1}, +{"\U0001F6E5\uFE0F", 1}, +{"\u2708\uFE0F", 1}, +{"\U0001F6E9\uFE0F", 1}, +{"\U0001F6F0\uFE0F", 1}, +{"\U0001F6CE\uFE0F", 1}, +{"\u23F1\uFE0F", 1}, +{"\u23F2\uFE0F", 1}, +{"\U0001F570\uFE0F", 1}, +{"\U0001F321\uFE0F", 1}, +{"\u2600\uFE0F", 1}, +{"\u2601\uFE0F", 1}, +{"\u26C8\uFE0F", 1}, +{"\U0001F324\uFE0F", 1}, +{"\U0001F325\uFE0F", 1}, +{"\U0001F326\uFE0F", 1}, +{"\U0001F327\uFE0F", 1}, +{"\U0001F328\uFE0F", 1}, +{"\U0001F329\uFE0F", 1}, +{"\U0001F32A\uFE0F", 1}, +{"\U0001F32B\uFE0F", 1}, +{"\U0001F32C\uFE0F", 1}, +{"\u2602\uFE0F", 1}, +{"\u26F1\uFE0F", 1}, +{"\u2744\uFE0F", 1}, +{"\u2603\uFE0F", 1}, +{"\u2604\uFE0F", 1}, +{"\U0001F397\uFE0F", 1}, +{"\U0001F39F\uFE0F", 1}, +{"\U0001F396\uFE0F", 1}, +{"\u26F8\uFE0F", 1}, +{"\U0001F579\uFE0F", 1}, +{"\u2660\uFE0F", 1}, +{"\u2665\uFE0F", 1}, +{"\u2666\uFE0F", 1}, +{"\u2663\uFE0F", 1}, +{"\u265F\uFE0F", 1}, +{"\U0001F5BC\uFE0F", 1}, +{"\U0001F576\uFE0F", 1}, +{"\U0001F6CD\uFE0F", 1}, +{"\u26D1\uFE0F", 1}, +{"\U0001F399\uFE0F", 1}, +{"\U0001F39A\uFE0F", 1}, +{"\U0001F39B\uFE0F", 1}, +{"\u260E\uFE0F", 1}, +{"\U0001F5A5\uFE0F", 1}, +{"\U0001F5A8\uFE0F", 1}, +{"\u2328\uFE0F", 1}, +{"\U0001F5B1\uFE0F", 1}, +{"\U0001F5B2\uFE0F", 1}, +{"\U0001F39E\uFE0F", 1}, +{"\U0001F4FD\uFE0F", 1}, +{"\U0001F56F\uFE0F", 1}, +{"\U0001F5DE\uFE0F", 1}, +{"\U0001F3F7\uFE0F", 1}, +{"\u2709\uFE0F", 1}, +{"\U0001F5F3\uFE0F", 1}, +{"\u270F\uFE0F", 1}, +{"\u2712\uFE0F", 1}, +{"\U0001F58B\uFE0F", 1}, +{"\U0001F58A\uFE0F", 1}, +{"\U0001F58C\uFE0F", 1}, +{"\U0001F58D\uFE0F", 1}, +{"\U0001F5C2\uFE0F", 1}, +{"\U0001F5D2\uFE0F", 1}, +{"\U0001F5D3\uFE0F", 1}, +{"\U0001F587\uFE0F", 1}, +{"\u2702\uFE0F", 1}, +{"\U0001F5C3\uFE0F", 1}, +{"\U0001F5C4\uFE0F", 1}, +{"\U0001F5D1\uFE0F", 1}, +{"\U0001F5DD\uFE0F", 1}, +{"\u26CF\uFE0F", 1}, +{"\u2692\uFE0F", 1}, +{"\U0001F6E0\uFE0F", 1}, +{"\U0001F5E1\uFE0F", 1}, +{"\u2694\uFE0F", 1}, +{"\U0001F6E1\uFE0F", 1}, +{"\u2699\uFE0F", 1}, +{"\U0001F5DC\uFE0F", 1}, +{"\u2696\uFE0F", 1}, +{"\u26D3\uFE0F\u200D\U0001F4A5", 1}, +{"\u26D3\u200D\U0001F4A5", 1}, +{"\u26D3\uFE0F", 1}, +{"\u2697\uFE0F", 1}, +{"\U0001F6CF\uFE0F", 1}, +{"\U0001F6CB\uFE0F", 1}, +{"\u26B0\uFE0F", 1}, +{"\u26B1\uFE0F", 1}, +{"\u26A0\uFE0F", 1}, +{"\u2622\uFE0F", 1}, +{"\u2623\uFE0F", 1}, +{"\u2B06\uFE0F", 1}, +{"\u2197\uFE0F", 1}, +{"\u27A1\uFE0F", 1}, +{"\u2198\uFE0F", 1}, +{"\u2B07\uFE0F", 1}, +{"\u2199\uFE0F", 1}, +{"\u2B05\uFE0F", 1}, +{"\u2196\uFE0F", 1}, +{"\u2195\uFE0F", 1}, +{"\u2194\uFE0F", 1}, +{"\u21A9\uFE0F", 1}, +{"\u21AA\uFE0F", 1}, +{"\u2934\uFE0F", 1}, +{"\u2935\uFE0F", 1}, +{"\u269B\uFE0F", 1}, +{"\U0001F549\uFE0F", 1}, +{"\u2721\uFE0F", 1}, +{"\u2638\uFE0F", 1}, +{"\u262F\uFE0F", 1}, +{"\u271D\uFE0F", 1}, +{"\u2626\uFE0F", 1}, +{"\u262A\uFE0F", 1}, +{"\u262E\uFE0F", 1}, +{"\u25B6\uFE0F", 1}, +{"\u23ED\uFE0F", 1}, +{"\u23EF\uFE0F", 1}, +{"\u25C0\uFE0F", 1}, +{"\u23EE\uFE0F", 1}, +{"\u23F8\uFE0F", 1}, +{"\u23F9\uFE0F", 1}, +{"\u23FA\uFE0F", 1}, +{"\u23CF\uFE0F", 1}, +{"\u2640\uFE0F", 1}, +{"\u2642\uFE0F", 1}, +{"\u26A7\uFE0F", 1}, +{"\u2716\uFE0F", 1}, +{"\u267E\uFE0F", 1}, +{"\u203C\uFE0F", 1}, +{"\u2049\uFE0F", 1}, +{"\u3030\uFE0F", 1}, +{"\u2695\uFE0F", 1}, +{"\u267B\uFE0F", 1}, +{"\u269C\uFE0F", 1}, +{"\u2611\uFE0F", 1}, +{"\u2714\uFE0F", 1}, +{"\u303D\uFE0F", 1}, +{"\u2733\uFE0F", 1}, +{"\u2734\uFE0F", 1}, +{"\u2747\uFE0F", 1}, +{"\u00A9\uFE0F", 1}, +{"\u00AE\uFE0F", 1}, +{"\u2122\uFE0F", 1}, +{"\u0023\uFE0F\u20E3", 1}, +{"\u0023\u20E3", 1}, +{"\u002A\uFE0F\u20E3", 1}, +{"\u002A\u20E3", 1}, +{"\u0030\uFE0F\u20E3", 1}, +{"\u0030\u20E3", 1}, +{"\u0031\uFE0F\u20E3", 1}, +{"\u0031\u20E3", 1}, +{"\u0032\uFE0F\u20E3", 1}, +{"\u0032\u20E3", 1}, +{"\u0033\uFE0F\u20E3", 1}, +{"\u0033\u20E3", 1}, +{"\u0034\uFE0F\u20E3", 1}, +{"\u0034\u20E3", 1}, +{"\u0035\uFE0F\u20E3", 1}, +{"\u0035\u20E3", 1}, +{"\u0036\uFE0F\u20E3", 1}, +{"\u0036\u20E3", 1}, +{"\u0037\uFE0F\u20E3", 1}, +{"\u0037\u20E3", 1}, +{"\u0038\uFE0F\u20E3", 1}, +{"\u0038\u20E3", 1}, +{"\u0039\uFE0F\u20E3", 1}, +{"\u0039\u20E3", 1}, +{"\U0001F170\uFE0F", 1}, +{"\U0001F171\uFE0F", 1}, +{"\u2139\uFE0F", 1}, +{"\u24C2\uFE0F", 1}, +{"\U0001F17E\uFE0F", 1}, +{"\U0001F17F\uFE0F", 1}, +{"\U0001F202\uFE0F", 1}, +{"\U0001F237\uFE0F", 1}, +{"\u3297\uFE0F", 1}, +{"\u3299\uFE0F", 1}, +{"\u25FC\uFE0F", 1}, +{"\u25FB\uFE0F", 1}, +{"\u25AA\uFE0F", 1}, +{"\u25AB\uFE0F", 1}, +{"\U0001F3F3\uFE0F", 1}, +{"\U0001F3F3\uFE0F\u200D\U0001F308", 1}, +{"\U0001F3F3\u200D\U0001F308", 1}, +{"\U0001F3F3\uFE0F\u200D\u26A7\uFE0F", 1}, +{"\U0001F3F3\u200D\u26A7\uFE0F", 1}, +{"\U0001F3F3\uFE0F\u200D\u26A7", 1}, +{"\U0001F3F3\u200D\u26A7", 1}, +{"\U0001F3F4\u200D\u2620\uFE0F", 1}, +{"\U0001F3F4\u200D\u2620", 1}, +{"\U0001F1E6\U0001F1E8", 1}, +{"\U0001F1E6\U0001F1E9", 1}, +{"\U0001F1E6\U0001F1EA", 1}, +{"\U0001F1E6\U0001F1EB", 1}, +{"\U0001F1E6\U0001F1EC", 1}, +{"\U0001F1E6\U0001F1EE", 1}, +{"\U0001F1E6\U0001F1F1", 1}, +{"\U0001F1E6\U0001F1F2", 1}, +{"\U0001F1E6\U0001F1F4", 1}, +{"\U0001F1E6\U0001F1F6", 1}, +{"\U0001F1E6\U0001F1F7", 1}, +{"\U0001F1E6\U0001F1F8", 1}, +{"\U0001F1E6\U0001F1F9", 1}, +{"\U0001F1E6\U0001F1FA", 1}, +{"\U0001F1E6\U0001F1FC", 1}, +{"\U0001F1E6\U0001F1FD", 1}, +{"\U0001F1E6\U0001F1FF", 1}, +{"\U0001F1E7\U0001F1E6", 1}, +{"\U0001F1E7\U0001F1E7", 1}, +{"\U0001F1E7\U0001F1E9", 1}, +{"\U0001F1E7\U0001F1EA", 1}, +{"\U0001F1E7\U0001F1EB", 1}, +{"\U0001F1E7\U0001F1EC", 1}, +{"\U0001F1E7\U0001F1ED", 1}, +{"\U0001F1E7\U0001F1EE", 1}, +{"\U0001F1E7\U0001F1EF", 1}, +{"\U0001F1E7\U0001F1F1", 1}, +{"\U0001F1E7\U0001F1F2", 1}, +{"\U0001F1E7\U0001F1F3", 1}, +{"\U0001F1E7\U0001F1F4", 1}, +{"\U0001F1E7\U0001F1F6", 1}, +{"\U0001F1E7\U0001F1F7", 1}, +{"\U0001F1E7\U0001F1F8", 1}, +{"\U0001F1E7\U0001F1F9", 1}, +{"\U0001F1E7\U0001F1FB", 1}, +{"\U0001F1E7\U0001F1FC", 1}, +{"\U0001F1E7\U0001F1FE", 1}, +{"\U0001F1E7\U0001F1FF", 1}, +{"\U0001F1E8\U0001F1E6", 1}, +{"\U0001F1E8\U0001F1E8", 1}, +{"\U0001F1E8\U0001F1E9", 1}, +{"\U0001F1E8\U0001F1EB", 1}, +{"\U0001F1E8\U0001F1EC", 1}, +{"\U0001F1E8\U0001F1ED", 1}, +{"\U0001F1E8\U0001F1EE", 1}, +{"\U0001F1E8\U0001F1F0", 1}, +{"\U0001F1E8\U0001F1F1", 1}, +{"\U0001F1E8\U0001F1F2", 1}, +{"\U0001F1E8\U0001F1F3", 1}, +{"\U0001F1E8\U0001F1F4", 1}, +{"\U0001F1E8\U0001F1F5", 1}, +{"\U0001F1E8\U0001F1F7", 1}, +{"\U0001F1E8\U0001F1FA", 1}, +{"\U0001F1E8\U0001F1FB", 1}, +{"\U0001F1E8\U0001F1FC", 1}, +{"\U0001F1E8\U0001F1FD", 1}, +{"\U0001F1E8\U0001F1FE", 1}, +{"\U0001F1E8\U0001F1FF", 1}, +{"\U0001F1E9\U0001F1EA", 1}, +{"\U0001F1E9\U0001F1EC", 1}, +{"\U0001F1E9\U0001F1EF", 1}, +{"\U0001F1E9\U0001F1F0", 1}, +{"\U0001F1E9\U0001F1F2", 1}, +{"\U0001F1E9\U0001F1F4", 1}, +{"\U0001F1E9\U0001F1FF", 1}, +{"\U0001F1EA\U0001F1E6", 1}, +{"\U0001F1EA\U0001F1E8", 1}, +{"\U0001F1EA\U0001F1EA", 1}, +{"\U0001F1EA\U0001F1EC", 1}, +{"\U0001F1EA\U0001F1ED", 1}, +{"\U0001F1EA\U0001F1F7", 1}, +{"\U0001F1EA\U0001F1F8", 1}, +{"\U0001F1EA\U0001F1F9", 1}, +{"\U0001F1EA\U0001F1FA", 1}, +{"\U0001F1EB\U0001F1EE", 1}, +{"\U0001F1EB\U0001F1EF", 1}, +{"\U0001F1EB\U0001F1F0", 1}, +{"\U0001F1EB\U0001F1F2", 1}, +{"\U0001F1EB\U0001F1F4", 1}, +{"\U0001F1EB\U0001F1F7", 1}, +{"\U0001F1EC\U0001F1E6", 1}, +{"\U0001F1EC\U0001F1E7", 1}, +{"\U0001F1EC\U0001F1E9", 1}, +{"\U0001F1EC\U0001F1EA", 1}, +{"\U0001F1EC\U0001F1EB", 1}, +{"\U0001F1EC\U0001F1EC", 1}, +{"\U0001F1EC\U0001F1ED", 1}, +{"\U0001F1EC\U0001F1EE", 1}, +{"\U0001F1EC\U0001F1F1", 1}, +{"\U0001F1EC\U0001F1F2", 1}, +{"\U0001F1EC\U0001F1F3", 1}, +{"\U0001F1EC\U0001F1F5", 1}, +{"\U0001F1EC\U0001F1F6", 1}, +{"\U0001F1EC\U0001F1F7", 1}, +{"\U0001F1EC\U0001F1F8", 1}, +{"\U0001F1EC\U0001F1F9", 1}, +{"\U0001F1EC\U0001F1FA", 1}, +{"\U0001F1EC\U0001F1FC", 1}, +{"\U0001F1EC\U0001F1FE", 1}, +{"\U0001F1ED\U0001F1F0", 1}, +{"\U0001F1ED\U0001F1F2", 1}, +{"\U0001F1ED\U0001F1F3", 1}, +{"\U0001F1ED\U0001F1F7", 1}, +{"\U0001F1ED\U0001F1F9", 1}, +{"\U0001F1ED\U0001F1FA", 1}, +{"\U0001F1EE\U0001F1E8", 1}, +{"\U0001F1EE\U0001F1E9", 1}, +{"\U0001F1EE\U0001F1EA", 1}, +{"\U0001F1EE\U0001F1F1", 1}, +{"\U0001F1EE\U0001F1F2", 1}, +{"\U0001F1EE\U0001F1F3", 1}, +{"\U0001F1EE\U0001F1F4", 1}, +{"\U0001F1EE\U0001F1F6", 1}, +{"\U0001F1EE\U0001F1F7", 1}, +{"\U0001F1EE\U0001F1F8", 1}, +{"\U0001F1EE\U0001F1F9", 1}, +{"\U0001F1EF\U0001F1EA", 1}, +{"\U0001F1EF\U0001F1F2", 1}, +{"\U0001F1EF\U0001F1F4", 1}, +{"\U0001F1EF\U0001F1F5", 1}, +{"\U0001F1F0\U0001F1EA", 1}, +{"\U0001F1F0\U0001F1EC", 1}, +{"\U0001F1F0\U0001F1ED", 1}, +{"\U0001F1F0\U0001F1EE", 1}, +{"\U0001F1F0\U0001F1F2", 1}, +{"\U0001F1F0\U0001F1F3", 1}, +{"\U0001F1F0\U0001F1F5", 1}, +{"\U0001F1F0\U0001F1F7", 1}, +{"\U0001F1F0\U0001F1FC", 1}, +{"\U0001F1F0\U0001F1FE", 1}, +{"\U0001F1F0\U0001F1FF", 1}, +{"\U0001F1F1\U0001F1E6", 1}, +{"\U0001F1F1\U0001F1E7", 1}, +{"\U0001F1F1\U0001F1E8", 1}, +{"\U0001F1F1\U0001F1EE", 1}, +{"\U0001F1F1\U0001F1F0", 1}, +{"\U0001F1F1\U0001F1F7", 1}, +{"\U0001F1F1\U0001F1F8", 1}, +{"\U0001F1F1\U0001F1F9", 1}, +{"\U0001F1F1\U0001F1FA", 1}, +{"\U0001F1F1\U0001F1FB", 1}, +{"\U0001F1F1\U0001F1FE", 1}, +{"\U0001F1F2\U0001F1E6", 1}, +{"\U0001F1F2\U0001F1E8", 1}, +{"\U0001F1F2\U0001F1E9", 1}, +{"\U0001F1F2\U0001F1EA", 1}, +{"\U0001F1F2\U0001F1EB", 1}, +{"\U0001F1F2\U0001F1EC", 1}, +{"\U0001F1F2\U0001F1ED", 1}, +{"\U0001F1F2\U0001F1F0", 1}, +{"\U0001F1F2\U0001F1F1", 1}, +{"\U0001F1F2\U0001F1F2", 1}, +{"\U0001F1F2\U0001F1F3", 1}, +{"\U0001F1F2\U0001F1F4", 1}, +{"\U0001F1F2\U0001F1F5", 1}, +{"\U0001F1F2\U0001F1F6", 1}, +{"\U0001F1F2\U0001F1F7", 1}, +{"\U0001F1F2\U0001F1F8", 1}, +{"\U0001F1F2\U0001F1F9", 1}, +{"\U0001F1F2\U0001F1FA", 1}, +{"\U0001F1F2\U0001F1FB", 1}, +{"\U0001F1F2\U0001F1FC", 1}, +{"\U0001F1F2\U0001F1FD", 1}, +{"\U0001F1F2\U0001F1FE", 1}, +{"\U0001F1F2\U0001F1FF", 1}, +{"\U0001F1F3\U0001F1E6", 1}, +{"\U0001F1F3\U0001F1E8", 1}, +{"\U0001F1F3\U0001F1EA", 1}, +{"\U0001F1F3\U0001F1EB", 1}, +{"\U0001F1F3\U0001F1EC", 1}, +{"\U0001F1F3\U0001F1EE", 1}, +{"\U0001F1F3\U0001F1F1", 1}, +{"\U0001F1F3\U0001F1F4", 1}, +{"\U0001F1F3\U0001F1F5", 1}, +{"\U0001F1F3\U0001F1F7", 1}, +{"\U0001F1F3\U0001F1FA", 1}, +{"\U0001F1F3\U0001F1FF", 1}, +{"\U0001F1F4\U0001F1F2", 1}, +{"\U0001F1F5\U0001F1E6", 1}, +{"\U0001F1F5\U0001F1EA", 1}, +{"\U0001F1F5\U0001F1EB", 1}, +{"\U0001F1F5\U0001F1EC", 1}, +{"\U0001F1F5\U0001F1ED", 1}, +{"\U0001F1F5\U0001F1F0", 1}, +{"\U0001F1F5\U0001F1F1", 1}, +{"\U0001F1F5\U0001F1F2", 1}, +{"\U0001F1F5\U0001F1F3", 1}, +{"\U0001F1F5\U0001F1F7", 1}, +{"\U0001F1F5\U0001F1F8", 1}, +{"\U0001F1F5\U0001F1F9", 1}, +{"\U0001F1F5\U0001F1FC", 1}, +{"\U0001F1F5\U0001F1FE", 1}, +{"\U0001F1F6\U0001F1E6", 1}, +{"\U0001F1F7\U0001F1EA", 1}, +{"\U0001F1F7\U0001F1F4", 1}, +{"\U0001F1F7\U0001F1F8", 1}, +{"\U0001F1F7\U0001F1FA", 1}, +{"\U0001F1F7\U0001F1FC", 1}, +{"\U0001F1F8\U0001F1E6", 1}, +{"\U0001F1F8\U0001F1E7", 1}, +{"\U0001F1F8\U0001F1E8", 1}, +{"\U0001F1F8\U0001F1E9", 1}, +{"\U0001F1F8\U0001F1EA", 1}, +{"\U0001F1F8\U0001F1EC", 1}, +{"\U0001F1F8\U0001F1ED", 1}, +{"\U0001F1F8\U0001F1EE", 1}, +{"\U0001F1F8\U0001F1EF", 1}, +{"\U0001F1F8\U0001F1F0", 1}, +{"\U0001F1F8\U0001F1F1", 1}, +{"\U0001F1F8\U0001F1F2", 1}, +{"\U0001F1F8\U0001F1F3", 1}, +{"\U0001F1F8\U0001F1F4", 1}, +{"\U0001F1F8\U0001F1F7", 1}, +{"\U0001F1F8\U0001F1F8", 1}, +{"\U0001F1F8\U0001F1F9", 1}, +{"\U0001F1F8\U0001F1FB", 1}, +{"\U0001F1F8\U0001F1FD", 1}, +{"\U0001F1F8\U0001F1FE", 1}, +{"\U0001F1F8\U0001F1FF", 1}, +{"\U0001F1F9\U0001F1E6", 1}, +{"\U0001F1F9\U0001F1E8", 1}, +{"\U0001F1F9\U0001F1E9", 1}, +{"\U0001F1F9\U0001F1EB", 1}, +{"\U0001F1F9\U0001F1EC", 1}, +{"\U0001F1F9\U0001F1ED", 1}, +{"\U0001F1F9\U0001F1EF", 1}, +{"\U0001F1F9\U0001F1F0", 1}, +{"\U0001F1F9\U0001F1F1", 1}, +{"\U0001F1F9\U0001F1F2", 1}, +{"\U0001F1F9\U0001F1F3", 1}, +{"\U0001F1F9\U0001F1F4", 1}, +{"\U0001F1F9\U0001F1F7", 1}, +{"\U0001F1F9\U0001F1F9", 1}, +{"\U0001F1F9\U0001F1FB", 1}, +{"\U0001F1F9\U0001F1FC", 1}, +{"\U0001F1F9\U0001F1FF", 1}, +{"\U0001F1FA\U0001F1E6", 1}, +{"\U0001F1FA\U0001F1EC", 1}, +{"\U0001F1FA\U0001F1F2", 1}, +{"\U0001F1FA\U0001F1F3", 1}, +{"\U0001F1FA\U0001F1F8", 1}, +{"\U0001F1FA\U0001F1FE", 1}, +{"\U0001F1FA\U0001F1FF", 1}, +{"\U0001F1FB\U0001F1E6", 1}, +{"\U0001F1FB\U0001F1E8", 1}, +{"\U0001F1FB\U0001F1EA", 1}, +{"\U0001F1FB\U0001F1EC", 1}, +{"\U0001F1FB\U0001F1EE", 1}, +{"\U0001F1FB\U0001F1F3", 1}, +{"\U0001F1FB\U0001F1FA", 1}, +{"\U0001F1FC\U0001F1EB", 1}, +{"\U0001F1FC\U0001F1F8", 1}, +{"\U0001F1FD\U0001F1F0", 1}, +{"\U0001F1FE\U0001F1EA", 1}, +{"\U0001F1FE\U0001F1F9", 1}, +{"\U0001F1FF\U0001F1E6", 1}, +{"\U0001F1FF\U0001F1F2", 1}, +{"\U0001F1FF\U0001F1FC", 1}, +{"\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F", 1}, +{"\U0001F3F4\U000E0067\U000E0062\U000E0073\U000E0063\U000E0074\U000E007F", 1}, +{"\U0001F3F4\U000E0067\U000E0062\U000E0077\U000E006C\U000E0073\U000E007F", 1}, +} diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index ab7e52f33..9bd5d34ea 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -16,8 +16,7 @@ map_insert_random_key_value :: proc(t: ^testing.T) { defer delete(m) unique_keys := 0 - r := rand.create(t.seed + seed_incr) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed + seed_incr) for _ in 0.. HRESULT, - AddMessage: proc "system" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY, Severity: MESSAGE_SEVERITY, ID: MESSAGE_ID, pDescription: cstring) -> HRESULT, - AddRetrievalFilterEntries: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, - AddStorageFilterEntries: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, - ClearRetrievalFilter: proc "system" (this: ^IInfoQueue), - ClearStorageFilter: proc "system" (this: ^IInfoQueue), + SetMessageCountLimit: proc "system" (this: ^IInfoQueue, MessageCountLimit: u64) -> HRESULT, ClearStoredMessages: proc "system" (this: ^IInfoQueue), - GetBreakOnCategory: proc "system" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY) -> BOOL, - GetBreakOnID: proc "system" (this: ^IInfoQueue, ID: MESSAGE_ID) -> BOOL, - GetBreakOnSeverity: proc "system" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY) -> BOOL, GetMessage: proc "system" (this: ^IInfoQueue, MessageIndex: u64, pMessage: ^MESSAGE, pMessageByteLength: ^SIZE_T) -> HRESULT, - GetMessageCountLimit: proc "system" (this: ^IInfoQueue) -> u64, - GetMuteDebugOutput: proc "system" (this: ^IInfoQueue) -> BOOL, GetNumMessagesAllowedByStorageFilter: proc "system" (this: ^IInfoQueue) -> u64, GetNumMessagesDeniedByStorageFilter: proc "system" (this: ^IInfoQueue) -> u64, - GetNumMessagesDiscardedByMessageCountLimit: proc "system" (this: ^IInfoQueue) -> u64, GetNumStoredMessages: proc "system" (this: ^IInfoQueue) -> u64, GetNumStoredMessagesAllowedByRetrievalFilter: proc "system" (this: ^IInfoQueue) -> u64, - GetRetrievalFilter: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER, pFilterByteLength: ^SIZE_T) -> HRESULT, - GetRetrievalFilterStackSize: proc "system" (this: ^IInfoQueue) -> u64, + GetNumMessagesDiscardedByMessageCountLimit: proc "system" (this: ^IInfoQueue) -> u64, + GetMessageCountLimit: proc "system" (this: ^IInfoQueue) -> u64, + AddStorageFilterEntries: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, GetStorageFilter: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER, pFilterByteLength: ^SIZE_T) -> HRESULT, - GetStorageFilterStackSize: proc "system" (this: ^IInfoQueue) -> u64, - PopRetrievalFilter: proc "system" (this: ^IInfoQueue), - PopStorageFilter: proc "system" (this: ^IInfoQueue), - PushCopyOfRetrievalFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, - PushCopyOfStorageFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, - PushEmptyRetrievalFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, + ClearStorageFilter: proc "system" (this: ^IInfoQueue), PushEmptyStorageFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, + PushCopyOfStorageFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, + PushStorageFilter: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + PopStorageFilter: proc "system" (this: ^IInfoQueue), + GetStorageFilterStackSize: proc "system" (this: ^IInfoQueue) -> u64, + AddRetrievalFilterEntries: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + GetRetrievalFilter: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER, pFilterByteLength: ^SIZE_T) -> HRESULT, + ClearRetrievalFilter: proc "system" (this: ^IInfoQueue), + PushEmptyRetrievalFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, + PushCopyOfRetrievalFilter: proc "system" (this: ^IInfoQueue) -> HRESULT, + PushRetrievalFilter: proc "system" (this: ^IInfoQueue, pFilter: ^INFO_QUEUE_FILTER) -> HRESULT, + PopRetrievalFilter: proc "system" (this: ^IInfoQueue), + GetRetrievalFilterStackSize: proc "system" (this: ^IInfoQueue) -> u64, + AddMessage: proc "system" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY, Severity: MESSAGE_SEVERITY, ID: MESSAGE_ID, pDescription: cstring) -> HRESULT, + AddApplicationMessage: proc "system" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY, pDescription: cstring) -> HRESULT, SetBreakOnCategory: proc "system" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY, bEnable: BOOL) -> HRESULT, - SetBreakOnID: proc "system" (this: ^IInfoQueue, ID: MESSAGE_ID, bEnable: BOOL) -> HRESULT, SetBreakOnSeverity: proc "system" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY, bEnable: BOOL) -> HRESULT, - SetMessageCountLimit: proc "system" (this: ^IInfoQueue, MessageCountLimit: u64) -> HRESULT, + SetBreakOnID: proc "system" (this: ^IInfoQueue, ID: MESSAGE_ID, bEnable: BOOL) -> HRESULT, + GetBreakOnCategory: proc "system" (this: ^IInfoQueue, Category: MESSAGE_CATEGORY) -> BOOL, + GetBreakOnSeverity: proc "system" (this: ^IInfoQueue, Severity: MESSAGE_SEVERITY) -> BOOL, + GetBreakOnID: proc "system" (this: ^IInfoQueue, ID: MESSAGE_ID) -> BOOL, SetMuteDebugOutput: proc "system" (this: ^IInfoQueue, bMute: BOOL), + GetMuteDebugOutput: proc "system" (this: ^IInfoQueue) -> BOOL, } MESSAGE_ID :: enum u32 { diff --git a/vendor/glfw/native_linux.odin b/vendor/glfw/native_linux.odin index 9b9e14790..6833d2893 100644 --- a/vendor/glfw/native_linux.odin +++ b/vendor/glfw/native_linux.odin @@ -2,14 +2,18 @@ package glfw -// TODO: Native Linux -// Display* glfwGetX11Display(void); -// RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); -// RROutput glfwGetX11Monitor(GLFWmonitor* monitor); -// Window glfwGetX11Window(GLFWwindow* window); -// void glfwSetX11SelectionString(const char* string); -// const char* glfwGetX11SelectionString(void); +import "vendor:x11/xlib" -// struct wl_display* glfwGetWaylandDisplay(void); -// struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); -// struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); +@(default_calling_convention="c", link_prefix="glfw") +foreign { + GetX11Display :: proc() -> ^xlib.Display --- + GetX11Window :: proc(window: WindowHandle) -> xlib.Window --- + GetX11Adapter :: proc(monitor: MonitorHandle) -> xlib.RRCrtc --- + GetX11Monitor :: proc(monitor: MonitorHandle) -> xlib.RROutput --- + SetX11SelectionString :: proc(string: cstring) --- + GetX11SelectionString :: proc() -> cstring --- + + GetWaylandDisplay :: proc() -> rawptr /* struct wl_display* */ --- + GetWaylandWindow :: proc(window: WindowHandle) -> rawptr /* struct wl_surface* */ --- + GetWaylandMonitor :: proc(monitor: MonitorHandle) -> rawptr /* struct wl_output* */ --- +} diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index 5c7f97fae..74ad7568e 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -96,6 +96,10 @@ class WasmMemoryInterface { }; loadPtr(addr) { return this.loadU32(addr); } + loadB32(addr) { + return this.loadU32(addr) != 0; + } + loadBytes(ptr, len) { return new Uint8Array(this.memory.buffer, ptr, Number(len)); } @@ -104,6 +108,16 @@ class WasmMemoryInterface { const bytes = this.loadBytes(ptr, Number(len)); return new TextDecoder().decode(bytes); } + + loadCstring(ptr) { + const start = this.loadPtr(ptr); + if (start == 0) { + return null; + } + let len = 0; + for (; this.mem.getUint8(start+len) != 0; len += 1) {} + return this.loadString(start, len); + } storeU8(addr, value) { this.mem.setUint8 (addr, value); } storeI8(addr, value) { this.mem.setInt8 (addr, value); } @@ -1245,7 +1259,7 @@ class WebGLInterface { }; -function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { +function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) { const MAX_INFO_CONSOLE_LINES = 512; let infoConsoleLines = new Array(); let currentLine = {}; @@ -1366,8 +1380,15 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { let event_temp_data = {}; let webglContext = new WebGLInterface(wasmMemoryInterface); + + const env = {}; + + if (memory) { + env.memory = memory; + } + return { - "env": {}, + env, "odin_env": { write: (fd, ptr, len) => { const str = wasmMemoryInterface.loadString(ptr, len); @@ -1720,13 +1741,16 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { * @param {string} wasmPath - Path to the WASM module to run * @param {?HTMLPreElement} consoleElement - Optional console/pre element to append output to, in addition to the console * @param {any} extraForeignImports - Imports, in addition to the default runtime to provide the module + * @param {?WasmMemoryInterface} wasmMemoryInterface - Optional memory to use instead of the defaults * @param {?int} intSize - Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32` */ -async function runWasm(wasmPath, consoleElement, extraForeignImports, intSize = 4) { - const wasmMemoryInterface = new WasmMemoryInterface(); +async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemoryInterface, intSize = 4) { + if (!wasmMemoryInterface) { + wasmMemoryInterface = new WasmMemoryInterface(); + } wasmMemoryInterface.setIntSize(intSize); - let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement); + let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory); let exports = {}; if (extraForeignImports !== undefined) { @@ -1741,11 +1765,17 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, intSize = const wasm = await WebAssembly.instantiate(file, imports); exports = wasm.instance.exports; wasmMemoryInterface.setExports(exports); - wasmMemoryInterface.setMemory(exports.memory); + + if (exports.memory) { + if (wasmMemoryInterface.memory) { + console.warn("WASM module exports memory, but `runWasm` was given an interface with existing memory too"); + } + wasmMemoryInterface.setMemory(exports.memory); + } exports._start(); - // Define a `@export step :: proc(dt: f32) -> (continue: bool) {` + // Define a `@export step :: proc(dt: f32) -> (keep_going: bool) {` // in your app and it will get called every frame. // return `false` to stop the execution of the module. if (exports.step) { diff --git a/vendor/wgpu/.gitignore b/vendor/wgpu/.gitignore new file mode 100644 index 000000000..330d70755 --- /dev/null +++ b/vendor/wgpu/.gitignore @@ -0,0 +1,9 @@ +lib/* +!lib/.gitkeep +example/web/triangle.wasm +example/web/wgpu.js +example/web/runtime.js +example/example +example/example.exe +example/triangle +example/triangle.exe diff --git a/vendor/wgpu/README.md b/vendor/wgpu/README.md new file mode 100644 index 000000000..3561642f4 --- /dev/null +++ b/vendor/wgpu/README.md @@ -0,0 +1,48 @@ +# WGPU + +A cross-platform (and WASM) GPU API. + +WASM support is achieved by providing wrappers around the browser native WebGPU API +that are called instead of the [wgpu-native](https://github.com/gfx-rs/wgpu-native) library, +the wgpu-native library provides support for all other targets. + +Have a look at the `example/` directory for the rendering of a basic triangle. + +## Getting the wgpu-native libraries + +For native support (not the browser), some libraries are required. Fortunately this is +extremely easy, just download them from the [releases on GitHub](https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1), +the bindings are for v0.19.4.1 at the moment. + +These are expected in the `lib` folder under the same name as they are released (just unzipped). +By default it will look for a static release version (`wgpu-OS-ARCH-release.a|lib`), +you can set `-define:WGPU_DEBUG=true` for it to look for a debug version, +and use `-define:WGPU_SHARED=true` to look for the shared libraries. + +## WASM + +For WASM, the module has to be built with a function table to enable callbacks. +You can do so using `-extra-linker-flags:"--export-table"`. + +Being able to allocate is also required (for some auxiliary APIs but also for mapping/unmapping buffers). + +You can set the context that is used for allocations by setting the global variable `wpgu.g_context`. +It will default to the `runtime.default_context`. + +Again, have a look at the `example/` and how it is set up, doing the `--import-memory` and the likes +is not strictly necessary but allows your app more memory than the minimal default. + +The bindings work on both `-target:js_wasm32` and `-target:js_wasm64p32`. + +## GLFW Glue + +There is an inner package `glfwglue` that can be used to glue together WGPU and GLFW. +It exports one procedure `GetSurface(wgpu.Instance, glfw.WindowHandle) -> glfw.Surface`. +The procedure will call the needed target specific procedures and return a surface configured +for the given window. + +To support Wayland on Linux, you need to have GLFW compiled to support it, and use +`-define:WGPU_GFLW_GLUE_SUPPORT_WAYLAND=true` to enable the package to check for Wayland. + +Do note that wgpu does not require GLFW, you can use native windows or another windowing library too. +For that you can take inspiration from `glfwglue` on glueing them together. diff --git a/vendor/wgpu/example/Makefile b/vendor/wgpu/example/Makefile new file mode 100644 index 000000000..f19997881 --- /dev/null +++ b/vendor/wgpu/example/Makefile @@ -0,0 +1,17 @@ +FILES := $(wildcard *) + +# NOTE: changing this requires changing the same values in the `web/index.html`. +INITIAL_MEMORY_PAGES := 2000 +MAX_MEMORY_PAGES := 65536 + +PAGE_SIZE := 65536 +INITIAL_MEMORY_BYTES := $(shell expr $(INITIAL_MEMORY_PAGES) \* $(PAGE_SIZE)) +MAX_MEMORY_BYTES := $(shell expr $(MAX_MEMORY_PAGES) \* $(PAGE_SIZE)) + +web/triangle.wasm: $(FILES) ../wgpu.js ../../wasm/js/runtime.js + odin build . \ + -target:js_wasm32 -out:web/triangle.wasm -o:size \ + -extra-linker-flags:"--export-table --import-memory --initial-memory=$(INITIAL_MEMORY_BYTES) --max-memory=$(MAX_MEMORY_BYTES)" + + cp ../wgpu.js web/wgpu.js + cp ../../wasm/js/runtime.js web/runtime.js diff --git a/vendor/wgpu/example/build.bat b/vendor/wgpu/example/build.bat new file mode 100644 index 000000000..cd3ca63ba --- /dev/null +++ b/vendor/wgpu/example/build.bat @@ -0,0 +1,12 @@ +REM NOTE: changing this requires changing the same values in the `web/index.html`. +set INITIAL_MEMORY_PAGES=2000 +set MAX_MEMORY_PAGES=65536 + +set PAGE_SIZE=65536 +set /a INITIAL_MEMORY_BYTES=%INITIAL_MEMORY_PAGES% * %PAGE_SIZE% +set /a MAX_MEMORY_BYTES=%MAX_MEMORY_PAGES% * %PAGE_SIZE% + +call odin.exe build . -target:js_wasm32 -out:web/triangle.wasm -o:size -extra-linker-flags:"--export-table --import-memory --initial-memory=%INITIAL_MEMORY_BYTES% --max-memory=%MAX_MEMORY_BYTES%" + +copy "..\wgpu.js" "web\wgpu.js" +copy "..\..\wasm\js\runtime.js" "web\runtime.js" \ No newline at end of file diff --git a/vendor/wgpu/example/main.odin b/vendor/wgpu/example/main.odin new file mode 100644 index 000000000..39161311c --- /dev/null +++ b/vendor/wgpu/example/main.odin @@ -0,0 +1,187 @@ +package vendor_wgpu_example_triangle + +import "base:runtime" + +import "core:fmt" + +import "vendor:wgpu" + +State :: struct { + ctx: runtime.Context, + os: OS, + + instance: wgpu.Instance, + surface: wgpu.Surface, + adapter: wgpu.Adapter, + device: wgpu.Device, + config: wgpu.SurfaceConfiguration, + queue: wgpu.Queue, + module: wgpu.ShaderModule, + pipeline_layout: wgpu.PipelineLayout, + pipeline: wgpu.RenderPipeline, +} + +@(private="file") +state: State + +main :: proc() { + state.ctx = context + + os_init(&state.os) + + state.instance = wgpu.CreateInstance(nil) + if state.instance == nil { + panic("WebGPU is not supported") + } + state.surface = os_get_surface(&state.os, state.instance) + + wgpu.InstanceRequestAdapter(state.instance, &{ compatibleSurface = state.surface }, on_adapter, nil) + + on_adapter :: proc "c" (status: wgpu.RequestAdapterStatus, adapter: wgpu.Adapter, message: cstring, userdata: rawptr) { + context = state.ctx + if status != .Success || adapter == nil { + fmt.panicf("request adapter failure: [%v] %s", status, message) + } + state.adapter = adapter + wgpu.AdapterRequestDevice(adapter, nil, on_device) + } + + on_device :: proc "c" (status: wgpu.RequestDeviceStatus, device: wgpu.Device, message: cstring, userdata: rawptr) { + context = state.ctx + if status != .Success || device == nil { + fmt.panicf("request device failure: [%v] %s", status, message) + } + state.device = device + + width, height := os_get_render_bounds(&state.os) + + state.config = wgpu.SurfaceConfiguration { + device = state.device, + usage = { .RenderAttachment }, + format = .BGRA8Unorm, + width = width, + height = height, + presentMode = .Fifo, + alphaMode = .Opaque, + } + wgpu.SurfaceConfigure(state.surface, &state.config) + + state.queue = wgpu.DeviceGetQueue(state.device) + + shader :: ` + @vertex + fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { + let x = f32(i32(in_vertex_index) - 1); + let y = f32(i32(in_vertex_index & 1u) * 2 - 1); + return vec4(x, y, 0.0, 1.0); + } + + @fragment + fn fs_main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); + }` + + state.module = wgpu.DeviceCreateShaderModule(state.device, &{ + nextInChain = &wgpu.ShaderModuleWGSLDescriptor{ + sType = .ShaderModuleWGSLDescriptor, + code = shader, + }, + }) + + state.pipeline_layout = wgpu.DeviceCreatePipelineLayout(state.device, &{}) + state.pipeline = wgpu.DeviceCreateRenderPipeline(state.device, &{ + layout = state.pipeline_layout, + vertex = { + module = state.module, + entryPoint = "vs_main", + }, + fragment = &{ + module = state.module, + entryPoint = "fs_main", + targetCount = 1, + targets = &wgpu.ColorTargetState{ + format = .BGRA8Unorm, + writeMask = wgpu.ColorWriteMaskFlags_All, + }, + }, + primitive = { + topology = .TriangleList, + + }, + multisample = { + count = 1, + mask = 0xFFFFFFFF, + }, + }) + + os_run(&state.os) + } +} + +resize :: proc "c" () { + context = state.ctx + + state.config.width, state.config.height = os_get_render_bounds(&state.os) + wgpu.SurfaceConfigure(state.surface, &state.config) +} + +frame :: proc "c" (dt: f32) { + context = state.ctx + + surface_texture := wgpu.SurfaceGetCurrentTexture(state.surface) + switch surface_texture.status { + case .Success: + // All good, could check for `surface_texture.suboptimal` here. + case .Timeout, .Outdated, .Lost: + // Skip this frame, and re-configure surface. + if surface_texture.texture != nil { + wgpu.TextureRelease(surface_texture.texture) + } + resize() + return + case .OutOfMemory, .DeviceLost: + // Fatal error + fmt.panicf("[triangle] get_current_texture status=%v", surface_texture.status) + } + defer wgpu.TextureRelease(surface_texture.texture) + + frame := wgpu.TextureCreateView(surface_texture.texture, nil) + defer wgpu.TextureViewRelease(frame) + + command_encoder := wgpu.DeviceCreateCommandEncoder(state.device, nil) + defer wgpu.CommandEncoderRelease(command_encoder) + + render_pass_encoder := wgpu.CommandEncoderBeginRenderPass( + command_encoder, &{ + colorAttachmentCount = 1, + colorAttachments = &wgpu.RenderPassColorAttachment{ + view = frame, + loadOp = .Clear, + storeOp = .Store, + clearValue = { r = 0, g = 1, b = 0, a = 1 }, + }, + }, + ) + defer wgpu.RenderPassEncoderRelease(render_pass_encoder) + + wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) + wgpu.RenderPassEncoderDraw(render_pass_encoder, vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) + wgpu.RenderPassEncoderEnd(render_pass_encoder) + + command_buffer := wgpu.CommandEncoderFinish(command_encoder, nil) + defer wgpu.CommandBufferRelease(command_buffer) + + wgpu.QueueSubmit(state.queue, { command_buffer }) + wgpu.SurfacePresent(state.surface) +} + +finish :: proc() { + wgpu.RenderPipelineRelease(state.pipeline) + wgpu.PipelineLayoutRelease(state.pipeline_layout) + wgpu.ShaderModuleRelease(state.module) + wgpu.QueueRelease(state.queue) + wgpu.DeviceRelease(state.device) + wgpu.AdapterRelease(state.adapter) + wgpu.SurfaceRelease(state.surface) + wgpu.InstanceRelease(state.instance) +} diff --git a/vendor/wgpu/example/os_glfw.odin b/vendor/wgpu/example/os_glfw.odin new file mode 100644 index 000000000..2b1817fa5 --- /dev/null +++ b/vendor/wgpu/example/os_glfw.odin @@ -0,0 +1,55 @@ +//+build !js +package vendor_wgpu_example_triangle + +import "core:time" + +import "vendor:glfw" +import "vendor:wgpu" +import "vendor:wgpu/glfwglue" + +OS :: struct { + window: glfw.WindowHandle, +} + +os_init :: proc(os: ^OS) { + if !glfw.Init() { + panic("[glfw] init failure") + } + + glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API) + os.window = glfw.CreateWindow(960, 540, "WGPU Native Triangle", nil, nil) + + glfw.SetFramebufferSizeCallback(os.window, size_callback) +} + +os_run :: proc(os: ^OS) { + dt: f32 + + for !glfw.WindowShouldClose(os.window) { + start := time.tick_now() + + glfw.PollEvents() + frame(dt) + + dt = f32(time.duration_seconds(time.tick_since(start))) + } + + finish() + + glfw.DestroyWindow(os.window) + glfw.Terminate() +} + +os_get_render_bounds :: proc(os: ^OS) -> (width, height: u32) { + iw, ih := glfw.GetWindowSize(os.window) + return u32(iw), u32(ih) +} + +os_get_surface :: proc(os: ^OS, instance: wgpu.Instance) -> wgpu.Surface { + return glfwglue.GetSurface(instance, os.window) +} + +@(private="file") +size_callback :: proc "c" (window: glfw.WindowHandle, width, height: i32) { + resize() +} diff --git a/vendor/wgpu/example/os_js.odin b/vendor/wgpu/example/os_js.odin new file mode 100644 index 000000000..9634f4afe --- /dev/null +++ b/vendor/wgpu/example/os_js.odin @@ -0,0 +1,60 @@ +package vendor_wgpu_example_triangle + +import "vendor:wgpu" +import "vendor:wasm/js" + +OS :: struct { + initialized: bool, +} + +@(private="file") +g_os: ^OS + +os_init :: proc(os: ^OS) { + g_os = os + assert(js.add_window_event_listener(.Resize, nil, size_callback)) +} + +// NOTE: frame loop is done by the runtime.js repeatedly calling `step`. +os_run :: proc(os: ^OS) { + os.initialized = true +} + +os_get_render_bounds :: proc(os: ^OS) -> (width, height: u32) { + rect := js.get_bounding_client_rect("body") + return u32(rect.width), u32(rect.height) +} + +os_get_surface :: proc(os: ^OS, instance: wgpu.Instance) -> wgpu.Surface { + return wgpu.InstanceCreateSurface( + instance, + &wgpu.SurfaceDescriptor{ + nextInChain = &wgpu.SurfaceDescriptorFromCanvasHTMLSelector{ + sType = .SurfaceDescriptorFromCanvasHTMLSelector, + selector = "#wgpu-canvas", + }, + }, + ) +} + +@(private="file", export) +step :: proc(dt: f32) -> bool { + if !g_os.initialized { + return true + } + + frame(dt) + return true +} + +@(private="file", fini) +os_fini :: proc() { + js.remove_window_event_listener(.Resize, nil, size_callback) + + finish() +} + +@(private="file") +size_callback :: proc(e: js.Event) { + resize() +} diff --git a/vendor/wgpu/example/web/index.html b/vendor/wgpu/example/web/index.html new file mode 100644 index 000000000..61872e35a --- /dev/null +++ b/vendor/wgpu/example/web/index.html @@ -0,0 +1,23 @@ + + + + + + WGPU WASM Triangle + + + + + + + + + diff --git a/vendor/wgpu/glfwglue/glue.odin b/vendor/wgpu/glfwglue/glue.odin new file mode 100644 index 000000000..83c497543 --- /dev/null +++ b/vendor/wgpu/glfwglue/glue.odin @@ -0,0 +1,6 @@ +//+build !linux +//+build !windows +//+build !darwin +package wgpu_glfw_glue + +#panic("package wgpu/glfwglue is not supported on the current target") diff --git a/vendor/wgpu/glfwglue/glue_darwin.odin b/vendor/wgpu/glfwglue/glue_darwin.odin new file mode 100644 index 000000000..c1477f4b0 --- /dev/null +++ b/vendor/wgpu/glfwglue/glue_darwin.odin @@ -0,0 +1,23 @@ +package wgpu_glfw_glue + +import "vendor:glfw" +import "vendor:wgpu" +import CA "vendor:darwin/QuartzCore" + +GetSurface :: proc(instance: wgpu.Instance, window: glfw.WindowHandle) -> wgpu.Surface { + ns_window := glfw.GetCocoaWindow(window) + ns_window->contentView()->setWantsLayer(true) + metal_layer := CA.MetalLayer_layer() + ns_window->contentView()->setLayer(metal_layer) + return wgpu.InstanceCreateSurface( + instance, + &wgpu.SurfaceDescriptor{ + nextInChain = &wgpu.SurfaceDescriptorFromMetalLayer{ + chain = wgpu.ChainedStruct{ + sType = .SurfaceDescriptorFromMetalLayer, + }, + layer = rawptr(metal_layer), + }, + }, + ) +} diff --git a/vendor/wgpu/glfwglue/glue_linux.odin b/vendor/wgpu/glfwglue/glue_linux.odin new file mode 100644 index 000000000..35c36a37d --- /dev/null +++ b/vendor/wgpu/glfwglue/glue_linux.odin @@ -0,0 +1,43 @@ +package wgpu_glfw_glue + +import "vendor:glfw" +import "vendor:wgpu" + +// GLFW needs to be compiled with wayland support for this to work. +SUPPORT_WAYLAND :: #config(WGPU_GFLW_GLUE_SUPPORT_WAYLAND, false) + +GetSurface :: proc(instance: wgpu.Instance, window: glfw.WindowHandle) -> wgpu.Surface { + when SUPPORT_WAYLAND { + if glfw.GetPlatform() == glfw.PLATFORM_WAYLAND { + display := glfw.GetWaylandDisplay() + surface := glfw.GetWaylandWindow(window) + return wgpu.InstanceCreateSurface( + instance, + &wgpu.SurfaceDescriptor{ + nextInChain = &wgpu.SurfaceDescriptorFromWaylandSurface{ + chain = { + sType = .SurfaceDescriptorFromWaylandSurface, + }, + display = display, + surface = surface, + }, + }, + ) + } + } + + display := glfw.GetX11Display() + window := glfw.GetX11Window(window) + return wgpu.InstanceCreateSurface( + instance, + &wgpu.SurfaceDescriptor{ + nextInChain = &wgpu.SurfaceDescriptorFromXlibWindow{ + chain = { + sType = .SurfaceDescriptorFromXlibWindow, + }, + display = display, + window = u64(window), + }, + }, + ) +} diff --git a/vendor/wgpu/glfwglue/glue_windows.odin b/vendor/wgpu/glfwglue/glue_windows.odin new file mode 100644 index 000000000..73a933f37 --- /dev/null +++ b/vendor/wgpu/glfwglue/glue_windows.odin @@ -0,0 +1,23 @@ +package wgpu_glfw_glue + +import win "core:sys/windows" + +import "vendor:glfw" +import "vendor:wgpu" + +GetSurface :: proc(instance: wgpu.Instance, window: glfw.WindowHandle) -> wgpu.Surface { + hwnd := glfw.GetWin32Window(window) + hinstance := win.GetModuleHandleW(nil) + return wgpu.InstanceCreateSurface( + instance, + &wgpu.SurfaceDescriptor{ + nextInChain = &wgpu.SurfaceDescriptorFromWindowsHWND{ + chain = wgpu.ChainedStruct{ + sType = .SurfaceDescriptorFromWindowsHWND, + }, + hinstance = rawptr(hinstance), + hwnd = rawptr(hwnd), + }, + }, + ) +} diff --git a/vendor/wgpu/lib/.gitkeep b/vendor/wgpu/lib/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll new file mode 100644 index 000000000..650260bfc Binary files /dev/null and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib new file mode 100644 index 000000000..b838c2695 Binary files /dev/null and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.dll.lib differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib new file mode 100644 index 000000000..ea4044d81 Binary files /dev/null and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.lib differ diff --git a/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb new file mode 100644 index 000000000..b30090276 Binary files /dev/null and b/vendor/wgpu/lib/wgpu-windows-x86_64-release/wgpu_native.pdb differ diff --git a/vendor/wgpu/wgpu.js b/vendor/wgpu/wgpu.js new file mode 100644 index 000000000..4fe78c992 --- /dev/null +++ b/vendor/wgpu/wgpu.js @@ -0,0 +1,2916 @@ +(function() { + +/** + * Assumptions: + * - Ability to allocate memory, set the context to allocate with using the global `wgpu.g_context` + * - Exports a function table (for callbacks), added with `-extra-linker-flags:"--export-table"` + */ +class WebGPUInterface { + + /** + * @param {WasmMemoryInterface} mem + */ + constructor(mem) { + this.mem = mem; + + this.enums = { + FeatureName: [undefined, "depth-clip-control", "depth32float-stencil8", "timestamp-query", "texture-compression-bc", "texture-compression-etc2", "texture-compression-astc", "indirect-first-instance", "shader-f16", "rg11b10ufloat-renderable", "bgra8unorm-storage", "float32-filterable", ], + StoreOp: [undefined, "store", "discard", ], + LoadOp: [undefined, "clear", "load", ], + BufferBindingType: [undefined, "uniform", "storage", "read-only-storage", ], + SamplerBindingType: [undefined, "filtering", "non-filtering", "comparison", ], + TextureSampleType: [undefined, "float", "unfilterable-float", "depth", "sint", "uint", ], + TextureViewDimension: [undefined, "1d", "2d", "2d-array", "cube", "cube-array", "3d", ], + StorageTextureAccess: [undefined, "write-only", "read-only", "read-write", ], + TextureFormat: [undefined, "r8unorm", "r8snorm", "r8uint", "r8sint", "r16uint", "r16sint", "r16float", "rg8unorm", "rg8snorm", "rg8uint", "rg8sint", "r32float", "r32uint", "r32sint", "rg16uint", "rg16sint", "rg16float", "rgba8unorm", "rgba8unorm-srgb", "rgba8snorm", "rgba8uint", "rgba8sint", "bgra8unorm", "bgra8unorm-srgb", "rgb10a2uint", "rgb10a2unorm", "rg11b10ufloat", "rgb9e5ufloat", "rg32float", "rg32uint", "rg32sint", "rgba16uint", "rgba16sint", "rgba16float", "rgba32float", "rgba32uint", "rgba32sint", "stencil8", "depth16unorm", "depth24plus", "depth24plus-stencil8", "depth32float", "depth32float-stencil8", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb", "bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm", "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb", "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm", "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb", ], + QueryType: ["occlusion", "timestamp", ], + VertexStepMode: ["vertex", "instance", "vertex-buffer-not-used", ], + VertexFormat: [undefined, "uint8x2", "uint8x4", "sint8x2", "sint8x4", "unorm8x2", "unorm8x4", "snorm8x2", "snorm8x4", "uint16x2", "uint16x4", "sint16x2", "sint16x4", "unorm16x2", "unorm16x4", "snorm16x2", "snorm16x4", "float16x2", "float16x4", "float32", "float32x2", "float32x3", "float32x4", "uint32", "uint32x2", "uint32x3", "uint32x4", "sint32", "sint32x2", "sint32x3", "sint32x4", ], + PrimitiveTopology: ["point-list", "line-list", "line-strip", "triangle-list", "triangle-strip", ], + IndexFormat: [undefined, "uint16", "uint32", ], + FrontFace: ["ccw", "cw", ], + CullMode: ["none", "front", "back", ], + AddressMode: ["repeat", "mirror-repeat", "clamp-to-edge", ], + FilterMode: ["nearest", "linear", ], + MipmapFilterMode: ["nearest", "linear", ], + CompareFunction: [undefined, "never", "less", "less-equal", "greater", "greater-equal", "equal", "not-equal", "always", ], + TextureDimension: ["1d", "2d", "3d", ], + ErrorType: ["no-error", "validation", "out-of-memory", "internal", "unknown", "device-lost", ], + WGSLFeatureName: [undefined, "readonly_and_readwrite_storage_textures", "packed_4x8_integer_dot_product", "unrestricted_pointer_parameters", "pointer_composite_access", ], + PowerPreference: [undefined, "low-power", "high-performance", ], + CompositeAlphaMode: ["auto", "opaque", "premultiplied", "unpremultiplied", "inherit", ], + StencilOperation: ["keep", "zero", "replace", "invert", "increment-clamp", "decrement-clamp", "increment-wrap", "decrement-wrap", ], + BlendOperation: ["add", "subtract", "reverse-subtract", "min", "max", ], + BlendFactor: ["zero", "one", "src", "one-minus-src", "src-alpha", "one-minus-src-alpha", "dst", "one-minus-dst", "dst-alpha", "one-minus-dst-alpha", "src-alpha-saturated", "constant", "one-minus-constant", ], + PresentMode: ["fifo", "fifo-relaxed", "immediate", "mailbox", ], + TextureAspect: ["all", "stencil-only", "depth-only"], + }; + + /** @type {WebGPUObjectManager<{}>} */ + this.instances = new WebGPUObjectManager("Instance", this.mem); + + /** @type {WebGPUObjectManager} */ + this.adapters = new WebGPUObjectManager("Adapter", this.mem); + + /** @type {WebGPUObjectManager} */ + this.bindGroups = new WebGPUObjectManager("BindGroup", this.mem); + + /** @type {WebGPUObjectManager} */ + this.bindGroupLayouts = new WebGPUObjectManager("BindGroupLayout", this.mem); + + /** @type {WebGPUObjectManager<{ buffer: GPUBuffer, mapping: ?{ range: ArrayBuffer, ptr: number, size: number } }>} */ + this.buffers = new WebGPUObjectManager("Buffer", this.mem); + + /** @type {WebGPUObjectManager} */ + this.devices = new WebGPUObjectManager("Device", this.mem); + + /** @type {WebGPUObjectManager} */ + this.commandBuffers = new WebGPUObjectManager("CommandBuffer", this.mem); + + /** @type {WebGPUObjectManager} */ + this.commandEncoders = new WebGPUObjectManager("CommandEncoder", this.mem); + + /** @type {WebGPUObjectManager} */ + this.computePassEncoders = new WebGPUObjectManager("ComputePassEncoder", this.mem); + + /** @type {WebGPUObjectManager} */ + this.renderPassEncoders = new WebGPUObjectManager("RenderPassEncoder", this.mem); + + /** @type {WebGPUObjectManager} */ + this.querySets = new WebGPUObjectManager("QuerySet", this.mem); + + /** @type {WebGPUObjectManager} */ + this.computePipelines = new WebGPUObjectManager("ComputePipeline", this.mem); + + /** @type {WebGPUObjectManager} */ + this.pipelineLayouts = new WebGPUObjectManager("PipelineLayout", this.mem); + + /** @type {WebGPUObjectManager} */ + this.queues = new WebGPUObjectManager("Queue", this.mem); + + /** @type {WebGPUObjectManager} */ + this.renderBundles = new WebGPUObjectManager("RenderBundle", this.mem); + + /** @type {WebGPUObjectManager} */ + this.renderBundleEncoders = new WebGPUObjectManager("RenderBundleEncoder", this.mem); + + /** @type {WebGPUObjectManager} */ + this.renderPipelines = new WebGPUObjectManager("RenderPipeline", this.mem); + + /** @type {WebGPUObjectManager} */ + this.samplers = new WebGPUObjectManager("Sampler", this.mem); + + /** @type {WebGPUObjectManager} */ + this.shaderModules = new WebGPUObjectManager("ShaderModule", this.mem); + + /** @type {WebGPUObjectManager} */ + this.surfaces = new WebGPUObjectManager("Surface", this.mem); + + /** @type {WebGPUObjectManager} */ + this.textures = new WebGPUObjectManager("Texture", this.mem); + + /** @type {WebGPUObjectManager} */ + this.textureViews = new WebGPUObjectManager("TextureView", this.mem); + } + + /** + * @param {number|BigInt} src + * @returns {number|BigInt} + */ + uint(src) { + if (this.mem.intSize == 8) { + return BigInt(src); + } else if (this.mem.intSize == 4) { + return src; + } else { + throw new Error("unreachable"); + } + } + + /** + * @param {number|BigInt} src + * @returns {number} + */ + unwrapBigInt(src) { + if (typeof src == "number") { + return src; + } + + const MAX_SAFE_INTEGER = 9007199254740991n; + if (typeof src != "bigint") { + throw new TypeError(`unwrapBigInt got invalid param of type ${typeof src}`); + } + + if (src > MAX_SAFE_INTEGER) { + throw new Error(`unwrapBigInt precision would be lost converting ${src}`); + } + + return Number(src); + } + + /** + * @param {boolean} condition + * @param {string} message + */ + assert(condition, message = "assertion failure") { + if (!condition) { + throw new Error(message); + } + } + + /** + * @template T + * + * @param {number} count + * @param {number} start + * @param {function(number): T} decoder + * @param {number} stride + * @returns {Array} + */ + array(count, start, decoder, stride) { + if (count == 0) { + return []; + } + this.assert(start != 0); + + const out = []; + for (let i = 0; i < count; i += 1) { + out.push(decoder.call(this, start)); + start += stride; + } + return out; + } + + /** + * @param {string} name + * @param {number} ptr + * @returns {`GPU${name}`} + */ + enumeration(name, ptr) { + const int = this.mem.loadI32(ptr); + this.assert(this.enums[name], `Unknown enumeration "${name}"`); + return this.enums[name][int]; + } + + /** + * @param {GPUSupportedFeatures} features + * @param {number} ptr + * @returns {BigInt|number} + */ + genericEnumerateFeatures(features, ptr) { + const availableFeatures = []; + this.enums.FeatureName.forEach((feature, value) => { + if (!feature) { + return; + } + + if (features.has(feature)) { + availableFeatures.push(value); + } + }); + + if (ptr != 0) { + for (let i = 0; i < availableFeatures.length; i += 1) { + this.mem.storeI32(ptr + (i * 4), availableFeatures[i]); + } + } + + return this.uint(availableFeatures.length); + } + + /** + * @param {GPUSupportedLimits} limits + * @param {number} ptr + */ + genericGetLimits(limits, supportedLimitsPtr) { + this.assert(supportedLimitsPtr != 0); + const limitsPtr = supportedLimitsPtr + 8; + + this.mem.storeU32(limitsPtr + 0, limits.maxTextureDimension1D); + this.mem.storeU32(limitsPtr + 4, limits.maxTextureDimension2D); + this.mem.storeU32(limitsPtr + 8, limits.maxTextureDimension3D); + this.mem.storeU32(limitsPtr + 12, limits.maxTextureArrayLayers); + this.mem.storeU32(limitsPtr + 16, limits.maxBindGroups); + this.mem.storeU32(limitsPtr + 20, limits.maxBindGroupsPlusVertexBuffers); + this.mem.storeU32(limitsPtr + 24, limits.maxBindingsPerBindGroup); + this.mem.storeU32(limitsPtr + 28, limits.maxDynamicUniformBuffersPerPipelineLayout); + this.mem.storeU32(limitsPtr + 32, limits.maxDynamicStorageBuffersPerPipelineLayout); + this.mem.storeU32(limitsPtr + 36, limits.maxSampledTexturesPerShaderStage); + this.mem.storeU32(limitsPtr + 40, limits.maxSamplersPerShaderStage); + this.mem.storeU32(limitsPtr + 44, limits.maxStorageBuffersPerShaderStage); + this.mem.storeU32(limitsPtr + 48, limits.maxStorageTexturesPerShaderStage); + this.mem.storeU32(limitsPtr + 52, limits.maxUniformBuffersPerShaderStage); + this.mem.storeU64(limitsPtr + 56, limits.maxUniformBufferBindingSize); + this.mem.storeU64(limitsPtr + 64, limits.maxStorageBufferBindingSize); + this.mem.storeU32(limitsPtr + 72, limits.minUniformBufferOffsetAlignment); + this.mem.storeU32(limitsPtr + 76, limits.minStorageBufferOffsetAlignment); + this.mem.storeU32(limitsPtr + 80, limits.maxVertexBuffers); + this.mem.storeU64(limitsPtr + 88, limits.maxBufferSize); + this.mem.storeU32(limitsPtr + 96, limits.maxVertexAttributes); + this.mem.storeU32(limitsPtr + 100, limits.maxVertexBufferArrayStride); + this.mem.storeU32(limitsPtr + 104, limits.maxInterStageShaderComponents); + this.mem.storeU32(limitsPtr + 108, limits.maxInterStageShaderVariables); + this.mem.storeU32(limitsPtr + 112, limits.maxColorAttachments); + this.mem.storeU32(limitsPtr + 116, limits.maxColorAttachmentBytesPerSample); + this.mem.storeU32(limitsPtr + 120, limits.maxComputeWorkgroupStorageSize); + this.mem.storeU32(limitsPtr + 124, limits.maxComputeInvocationsPerWorkgroup); + this.mem.storeU32(limitsPtr + 128, limits.maxComputeWorkgroupSizeX); + this.mem.storeU32(limitsPtr + 132, limits.maxComputeWorkgroupSizeY); + this.mem.storeU32(limitsPtr + 136, limits.maxComputeWorkgroupSizeZ); + this.mem.storeU32(limitsPtr + 140, limits.maxComputeWorkgroupsPerDimension); + + return true; + } + + /** + * @param {number} ptr + * @returns {GPUFeatureName} + */ + FeatureNamePtr(ptr) { + return this.FeatureName(this.mem.loadI32(ptr)); + } + + /** + * @param {number} featureInt + * @returns {GPUFeatureName} + */ + FeatureName(featureInt) { + return this.enums.FeatureName[featureInt]; + } + + /** + * @param {number} ptr + * @returns {GPUSupportedLimits} + */ + RequiredLimitsPtr(ptr) { + const start = this.mem.loadPtr(ptr); + if (start == 0) { + return undefined; + } + + return this.Limits(start + 8); + } + + /** + * @param {number} start + * @return {GPUSupportedLimits} + */ + Limits(start) { + const limitU32 = (ptr) => { + const value = this.mem.loadU32(ptr); + if (value == 0xFFFFFFFF) { // LIMIT_32_UNDEFINED. + return undefined; + } + return value; + }; + + const limitU64 = (ptr) => { + const part1 = this.mem.loadU32(ptr); + const part2 = this.mem.loadU32(ptr + 4); + if (part1 != 0xFFFFFFFF || part2 != 0xFFFFFFFF) { // LIMIT_64_UNDEFINED. + return this.mem.loadU64(ptr); + } + return undefined; + }; + + return { + maxTextureDimension1D: limitU32(start + 0), + maxTextureDimension2D: limitU32(start + 4), + maxTextureDimension3D: limitU32(start + 8), + maxTextureArrayLayers: limitU32(start + 12), + maxBindGroups: limitU32(start + 16), + maxBindGroupsPlusVertexBuffers: limitU32(start + 20), + maxBindingsPerBindGroup: limitU32(start + 24), + maxDynamicUniformBuffersPerPipelineLayout: limitU32(start + 28), + maxDynamicStorageBuffersPerPipelineLayout: limitU32(start + 32), + maxSampledTexturesPerShaderStage: limitU32(start + 36), + maxSamplersPerShaderStage: limitU32(start + 40), + maxStorageBuffersPerShaderStage: limitU32(start + 44), + maxStorageTexturesPerShaderStage: limitU32(start + 48), + maxUniformBuffersPerShaderStage: limitU32(start + 52), + maxUniformBufferBindingSize: limitU64(start + 56), + maxStorageBufferBindingSize: limitU64(start + 64), + minUniformBufferOffsetAlignment: limitU32(start + 72), + minStorageBufferOffsetAlignment: limitU32(start + 76), + maxVertexBuffers: limitU32(start + 80), + maxBufferSize: limitU64(start + 88), + maxVertexAttributes: limitU32(start + 96), + maxVertexBufferArrayStride: limitU32(start + 100), + maxInterStageShaderComponents: limitU32(start + 104), + maxInterStageShaderVariables: limitU32(start + 108), + maxColorAttachments: limitU32(start + 112), + maxColorAttachmentBytesPerSample: limitU32(start + 116), + maxComputeWorkgroupStorageSize: limitU32(start + 120), + maxComputeInvocationsPerWorkgroup: limitU32(start + 124), + maxComputeWorkgroupSizeX: limitU32(start + 128), + maxComputeWorkgroupSizeY: limitU32(start + 132), + maxComputeWorkgroupSizeZ: limitU32(start + 136), + maxComputeWorkgroupsPerDimension: limitU32(start + 140), + }; + } + + /** + * @param {number} start + * @returns {GPUQueueDescriptor} + */ + QueueDescriptor(start) { + return { + label: this.mem.loadCstring(start + 4), + }; + } + + /** + * @param {number} ptr + * @returns {GPUComputePassTimestampWrites} + */ + ComputePassTimestampWritesPtr(ptr) { + const start = this.mem.loadPtr(ptr); + if (start == 0) { + return undefined; + } + + return { + querySet: this.querySets.get(this.mem.loadPtr(start + 0)), + beginningOfPassWriteIndex: this.mem.loadU32(start + 4), + endOfPassWriteIndex: this.mem.loadU32(start + 8), + }; + } + + /** + * @param {number} start + * @returns {GPURenderPassColorAttachment} + */ + RenderPassColorAttachment(start) { + const viewIdx = this.mem.loadPtr(start + 4); + const resolveTargetIdx = this.mem.loadPtr(start + 8); + + return { + view: viewIdx > 0 ? this.textureViews.get(viewIdx) : undefined, + resolveTarget: resolveTargetIdx > 0 ? this.textureViews.get(resolveTargetIdx) : undefined, + loadOp: this.enumeration("LoadOp", start + 12), + storeOp: this.enumeration("StoreOp", start + 16), + clearValue: this.Color(start + 24), + }; + } + + /** + * @param {number} start + * @returns {GPUColor} + */ + Color(start) { + return { + r: this.mem.loadF64(start + 0), + g: this.mem.loadF64(start + 8), + b: this.mem.loadF64(start + 16), + a: this.mem.loadF64(start + 24), + }; + } + + /** + * @param {number} ptr + * @returns {GPURenderPassDepthStencilAttachment} + */ + RenderPassDepthStencilAttachmentPtr(ptr) { + const start = this.mem.loadPtr(ptr); + if (start == 0) { + return undefined; + } + + return { + view: this.textureViews.get(this.mem.loadPtr(start + 0)), + depthLoadOp: this.enumeration("LoadOp", start + 4), + depthStoreOp: this.enumeration("StoreOp", start + 8), + depthClearValue: this.mem.loadF32(start + 12), + depthReadOnly: this.mem.loadB32(start + 16), + stencilLoadOp: this.enumeration("LoadOp", start + 20), + stencilStoreOp: this.enumeration("StoreOp", start + 24), + stencilClearValue: this.mem.loadF32(start + 28), + stencilReadOnly: this.mem.loadB32(start + 32), + }; + } + + /** + * @param {number} ptr + * @returns {undefined|GPUQuerySet} + */ + QuerySet(ptr) { + ptr = this.mem.loadPtr(ptr); + if (ptr == 0) { + return undefined; + } + + return this.querySets.get(ptr); + } + + /** + * @param {number} ptr + * @returns {GPURenderPassTimestampWrites} + */ + RenderPassTimestampWritesPtr(ptr) { + return this.ComputePassTimestampWritesPtr(ptr); + } + + /** + * @param {number} start + * @returns {GPUImageDataLayout} + */ + TextureDataLayout(start) { + return { + offset: this.mem.loadU64(start + 8), + bytesPerRow: this.mem.loadU32(start + 16), + rowsPerImage: this.mem.loadU32(start + 20), + }; + } + + /** + * @param {number} start + * @returns {GPUImageCopyBuffer} + */ + ImageCopyBuffer(start) { + return { + ...this.TextureDataLayout(start + 8), + buffer: this.buffers.get(this.mem.loadPtr(start + 32)).buffer, + }; + } + + /** + * @param {number} start + * @returns {GPUImageCopyTexture} + */ + ImageCopyTexture(start) { + return { + texture: this.textures.get(this.mem.loadPtr(start + 4)), + mipLevel: this.mem.loadU32(start + 8), + origin: this.Origin3D(start + 12), + aspect: this.enumeration("TextureAspect", start + 24), + }; + } + + /** + * @param {number} start + * @returns {GPUOrigin3D} + */ + Origin3D(start) { + return { + x: this.mem.loadU32(start + 0), + y: this.mem.loadU32(start + 4), + z: this.mem.loadU32(start + 8), + }; + } + + /** + * @param {number} start + * @returns {GPUExtent3D} + */ + Extent3D(start) { + return { + width: this.mem.loadU32(start + 0), + height: this.mem.loadU32(start + 4), + depthOrArrayLayers: this.mem.loadU32(start + 8), + }; + } + + /** + * @param {number} start + * @returns {GPUBindGroupEntry} + */ + BindGroupEntry(start) { + const buffer = this.mem.loadPtr(start + 8); + const sampler = this.mem.loadPtr(start + 32); + const textureView = this.mem.loadPtr(start + 36); + + /** @type {GPUBindingResource} */ + let resource; + if (buffer > 0) { + resource = { + buffer: this.buffers.get(buffer).buffer, + offset: this.mem.loadU64(start + 16), + size: this.mem.loadU64(start + 24), + } + } else if (sampler > 0) { + resource = this.samplers.get(sampler); + } else if (textureView > 0) { + resource = this.textureViews.get(textureView); + } + + return { + binding: this.mem.loadU32(start + 4), + resource: resource, + }; + } + + /** + * @param {number} start + * @returns {GPUBindGroupLayoutEntry} + */ + BindGroupLayoutEntry(start) { + const entry = { + binding: this.mem.loadU32(start + 4), + visibility: this.mem.loadU32(start + 8), + buffer: this.BufferBindingLayout(start + 16), + sampler: this.SamplerBindingLayout(start + 40), + texture: this.TextureBindingLayout(start + 48), + storageTexture: this.StorageTextureBindingLayout(start + 64), + }; + if (!entry.buffer.type) { + entry.buffer = undefined; + } + if (!entry.sampler.type) { + entry.sampler = undefined; + } + if (!entry.texture.sampleType) { + entry.texture = undefined; + } + if (!entry.storageTexture.access) { + entry.storageTexture = undefined; + } + return entry; + } + + /** + * @param {number} start + * @returns {GPUBufferBindingLayout} + */ + BufferBindingLayout(start) { + return { + type: this.enumeration("BufferBindingType", start + 4), + hasDynamicOffset: this.mem.loadB32(start + 8), + minBindingSize: this.mem.loadU64(start + 16), + }; + } + + /** + * @param {number} start + * @returns {GPUSamplerBindingLayout} + */ + SamplerBindingLayout(start) { + return { + type: this.enumeration("SamplerBindingType", start + 4), + }; + } + + /** + * @param {number} start + * @returns {GPUTextureBindingLayout} + */ + TextureBindingLayout(start) { + return { + sampleType: this.enumeration("TextureSampleType", start + 4), + viewDimension: this.enumeration("TextureViewDimension", start + 8), + multisampled: this.mem.loadB32(start + 12), + }; + } + + /** + * @param {number} start + * @returns {GPUStorageTextureBindingLayout} + */ + StorageTextureBindingLayout(start) { + return { + access: this.enumeration("StorageTextureAccess", start + 4), + format: this.enumeration("TextureFormat", start + 8), + viewDimension: this.enumeration("TextureViewDimension", start + 12), + }; + } + + /** + * @param {number} start + * @returns {GPUProgrammableStage} + */ + ProgrammableStageDescriptor(start) { + const constantsArray = this.array( + this.mem.loadUint(start + 8 + this.mem.intSize), + this.mem.loadPtr(start + 8 + this.mem.intSize*2), + this.ConstantEntry, + 16, + ); + return { + module: this.shaderModules.get(this.mem.loadPtr(start + 4)), + entryPoint: this.mem.loadCstring(start + 8), + constants: constantsArray.reduce((prev, curr) => { + prev[curr.key] = curr.value; + return prev; + }, {}), + }; + } + + /** + * @param {number} start + * @returns {{ key: string, value: number }} + */ + ConstantEntry(start) { + return { + key: this.mem.loadCstring(start + 4), + value: this.mem.loadF64(start + 8), + }; + } + + /** + * @param {number} start + * @returns {GPUComputePipelineDescriptor} + */ + ComputePipelineDescriptor(start) { + const layoutIdx = this.mem.loadPtr(start + 8) + return { + label: this.mem.loadCstring(start + 4), + layout: layoutIdx > 0 ? this.pipelineLayouts.get(layoutIdx) : undefined, + compute: this.ProgrammableStageDescriptor(start + 8 + this.mem.intSize), + }; + } + + /** + * @param {number} start + * @returns {GPUVertexState} + */ + VertexState(start) { + let off = 8 + this.mem.intSize; + const constantsArray = this.array( + this.mem.loadUint(start + off), + this.mem.loadPtr(start + off + this.mem.intSize), + this.ConstantEntry, + 16, + ); + + off += this.mem.intSize * 2; + + return { + module: this.shaderModules.get(this.mem.loadPtr(start + 4)), + entryPoint: this.mem.loadCstring(start + 8), + constants: constantsArray.reduce((prev, curr) => { + prev[curr.key] = curr.value; + return prev; + }, {}), + buffers: this.array( + this.mem.loadUint(start + off), + this.mem.loadPtr(start + off + this.mem.intSize), + this.VertexBufferLayout, + this.mem.intSize == 8 ? 32 : 24, + ), + }; + } + + /** + * @param {number} start + * @returns {?GPUVertexBufferLayout} + */ + VertexBufferLayout(start) { + const stepMode = this.enumeration("VertexStepMode", start + 8); + if (stepMode == "vertex-buffer-not-used") { + return null; + } + return { + arrayStride: this.mem.loadU64(start + 0), + stepMode: stepMode, + attributes: this.array( + this.mem.loadUint(start + 8 + this.mem.intSize), + this.mem.loadPtr(start + 8 + this.mem.intSize*2), + this.VertexAttribute, + 24, + ), + }; + } + + /** + * @param {number} start + * @returns {GPUVertexAttribute} + */ + VertexAttribute(start) { + return { + format: this.enumeration("VertexFormat", start + 0), + offset: this.mem.loadU64(start + 8), + shaderLocation: this.mem.loadU32(start + 16), + }; + } + + /** + * @param {number} start + * @returns {GPUPrimitiveState} + */ + PrimitiveState(start) { + let unclippedDepth = undefined; + const nextInChain = this.mem.loadPtr(start); + if (nextInChain != 0) { + const nextInChainType = this.mem.loadI32(nextInChain + 4); + // PrimitiveDepthClipControl = 0x00000007, + if (nextInChainType == 7) { + unclippedDepth = this.mem.loadB32(nextInChain + 8); + } + } + + return { + topology: this.enumeration("PrimitiveTopology", start + 4), + stripIndexFormat: this.enumeration("IndexFormat", start + 8), + frontFace: this.enumeration("FrontFace", start + 12), + cullMode: this.enumeration("CullMode", start + 16), + unclippedDepth: unclippedDepth, + }; + } + + /** + * @param {number} start + * @returns {GPURenderPipelineDescriptor} + */ + RenderPipelineDescriptor(start) { + const layoutIdx = this.mem.loadPtr(start + 8); + const offs = this.mem.intSize == 8 ? [64, 84, 88, 104] : [40, 60, 64, 80]; + return { + label: this.mem.loadCstring(start + 4), + layout: layoutIdx > 0 ? this.pipelineLayouts.get(layoutIdx) : undefined, + vertex: this.VertexState(start + 8 + this.mem.intSize), + primitive: this.PrimitiveState(start + offs[0]), + depthStencil: this.DepthStencilStatePtr(start + offs[1]), + multisample: this.MultisampleState(start + offs[2]), + fragment: this.FragmentStatePtr(start + offs[3]), + }; + } + + /** + * @param {number} start + * @returns {GPUShaderModuleCompilationHint} + */ + ShaderModuleCompilationHint(start) { + return { + entryPoint: this.mem.loadCstring(start + 4), + layout: this.pipelineLayouts.get(this.mem.loadPtr(start + 8)), + }; + } + + /** + * @param {number} ptr + * @returns {?GPUDepthStencilState} + */ + DepthStencilStatePtr(ptr) { + const start = this.mem.loadPtr(ptr); + if (start == 0) { + return undefined; + } + + return { + format: this.enumeration("TextureFormat", start + 4), + depthWriteEnabled: this.mem.loadB32(start + 8), + depthCompare: this.enumeration("CompareFunction", start + 12), + stencilFront: this.StencilFaceState(start + 16), + stencilBack: this.StencilFaceState(start + 32), + stencilReadMask: this.mem.loadU32(start + 48), + stencilWriteMask: this.mem.loadU32(start + 52), + depthBias: this.mem.loadI32(start + 56), + depthBiasSlopeScale: this.mem.loadF32(start + 60), + depthBiasClamp: this.mem.loadF32(start + 64), + }; + } + + /** + * @param {number} start + * @returns {GPUStencilFaceState} + */ + StencilFaceState(start) { + return { + compare: this.enumeration("CompareFunction", start + 0), + failOp: this.enumeration("StencilOperation", start + 4), + depthFailOp: this.enumeration("StencilOperation", start + 8), + passOp: this.enumeration("StencilOperation", start + 12), + }; + } + + /** + * @param {number} start + * @returns {GPUMultisampleState} + */ + MultisampleState(start) { + return { + count: this.mem.loadU32(start + 4), + mask: this.mem.loadU32(start + 8), + alphaToCoverageEnabled: this.mem.loadB32(start + 12), + }; + } + + /** + * @param {number} ptr + * @returns {?GPUFragmentState} + */ + FragmentStatePtr(ptr) { + const start = this.mem.loadPtr(ptr); + if (start == 0) { + return undefined; + } + + let off = 8 + this.mem.intSize; + + const constantsArray = this.array( + this.mem.loadUint(start + off), + this.mem.loadPtr(start + off + this.mem.intSize), + this.ConstantEntry, + 16, + ); + + off += this.mem.intSize * 2; + + return { + module: this.shaderModules.get(this.mem.loadPtr(start + 4)), + entryPoint: this.mem.loadCstring(start + 8), + constants: constantsArray.reduce((prev, curr) => { + prev[curr.key] = curr.value; + return prev; + }, {}), + targets: this.array( + this.mem.loadUint(start + off), + this.mem.loadPtr(start + off + this.mem.intSize), + this.ColorTargetState, + 16, + ), + }; + } + + /** + * @param {number} start + * @returns {GPUColorTargetState} + */ + ColorTargetState(start) { + return { + format: this.enumeration("TextureFormat", start + 4), + blend: this.BlendStatePtr(start + 8), + writeMask: this.mem.loadU32(start + 12), + }; + } + + /** + * @param {number} ptr + * @returns {?GPUBlendState} + */ + BlendStatePtr(ptr) { + const start = this.mem.loadPtr(ptr); + if (start == 0) { + return undefined; + } + + return { + color: this.BlendComponent(start + 0), + alpha: this.BlendComponent(start + 12), + }; + } + + /** + * @param {number} start + * @returns {?GPUBlendComponent} + */ + BlendComponent(start) { + return { + operation: this.enumeration("BlendOperation", start + 0), + srcFactor: this.enumeration("BlendFactor", start + 4), + dstFactor: this.enumeration("BlendFactor", start + 8), + }; + } + + getInterface() { + return { + /** + * @param {0|number} descriptorPtr + * @returns {number} + */ + wgpuCreateInstance: (descriptorPtr) => { + if (!navigator.gpu) { + console.error("WebGPU is not supported by this browser"); + return 0; + } + + return this.instances.create({}); + }, + + /** + * @param {number} deviceIdx + * @param {number} procNamePtr + * @returns {number} + */ + wgpuGetProcAddress: (deviceIdx, procNamePtr) => { + console.error(`unimplemented: wgpuGetProcAddress`); + return 0; + }, + + /* ---------------------- Adapter ---------------------- */ + + /** + * @param {number} adapterIdx + * @param {number} featuresPtr + * @returns {number|BigInt} + */ + wgpuAdapterEnumerateFeatures: (adapterIdx, featuresPtr) => { + const adapter = this.adapters.get(adapterIdx); + return this.genericEnumerateFeatures(adapter.features, featuresPtr); + }, + + /** + * @param {number} adapterIdx + * @param {number} supportedLimitsPtr + * @returns {boolean} + */ + wgpuAdapterGetLimits: (adapterIdx, supportedLimitsPtr) => { + const adapter = this.adapters.get(adapterIdx); + return this.genericGetLimits(adapter.limits, supportedLimitsPtr); + }, + + /** + * @param {number} adapterIdx + * @param {number} propertiesPtr + */ + wgpuAdapterGetProperties: (adapterIdx, propertiesPtr) => { + this.assert(propertiesPtr != 0); + // Unknown adapter. + this.mem.storeI32(propertiesPtr + 28, 3); + // WebGPU backend. + this.mem.storeI32(propertiesPtr + 32, 2); + }, + + /** + * @param {number} adapterIdx + * @param {number} featureInt + * @returns {boolean} + */ + wgpuAdapterHasFeature: (adapterIdx, featureInt) => { + const adapter = this.adapters.get(adapterIdx); + return adapter.features.has(this.enums.FeatureName[featureInt]); + }, + + /** + * @param {number} adapterIdx + * @param {number} callbackPtr + * @param {0|number} userdata + */ + wgpuAdapterRequestAdapterInfo: async (adapterIdx, callbackPtr, userdata) => { + const adapter = this.adapters.get(adapterIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + + const info = await adapter.requestAdapterInfo(); + + const addr = this.mem.exports.wgpu_alloc(16); + + const vendorLength = new TextEncoder().encode(info.vendor).length; + const vendorAddr = this.mem.exports.wgpu_alloc(vendorLength); + this.mem.storeString(vendorAddr, info.vendor); + this.mem.storeI32(addr + 0, vendorAddr); + + const architectureLength = new TextEncoder().encode(info.architecture).length; + const architectureAddr = this.mem.exports.wgpu_alloc(architectureLength); + this.mem.storeString(architectureAddr, info.architecture); + this.mem.storeI32(addr + 4, architectureAddr); + + + const deviceLength = new TextEncoder().encode(info.device).length; + const deviceAddr = this.mem.exports.wgpu_alloc(deviceLength); + this.mem.storeString(deviceAddr, info.device); + this.mem.storeI32(addr + 8, deviceAddr); + + + const descriptionLength = new TextEncoder().encode(info.description).length; + const descriptionAddr = this.mem.exports.wgpu_alloc(descriptionLength); + this.mem.storeString(descriptionAddr, info.description); + this.mem.storeI32(addr + 12, descriptionAddr); + + callback(addr, userdata); + + this.mem.exports.wgpu_free(descriptionAddr); + this.mem.exports.wgpu_free(deviceAddr); + this.mem.exports.wgpu_free(architectureAddr); + this.mem.exports.wgpu_free(vendorAddr); + this.mem.exports.wgpu_free(addr); + }, + + /** + * @param {number} adapterIdx + * @param {0|number} descriptorPtr + * @param {number} callbackPtr + * @param {0|number} userdata + */ + wgpuAdapterRequestDevice: async (adapterIdx, descriptorPtr, callbackPtr, userdata) => { + const adapter = this.adapters.get(adapterIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + + /** @type {GPUDeviceDescriptor} */ + let descriptor; + if (descriptorPtr != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + requiredFeatures: this.array( + this.mem.loadUint(descriptorPtr + 8), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize), + this.FeatureNamePtr, + 4, + ), + requiredLimits: this.RequiredLimitsPtr(descriptorPtr + 8 + this.mem.intSize + 4), + defaultQueue: this.QueueDescriptor( descriptorPtr + 8 + this.mem.intSize + 4 + 4), + }; + } + + let deviceIdx; + try { + const device = await adapter.requestDevice(descriptor); + deviceIdx = this.devices.create(device); + // NOTE: don't callback here, any errors that happen later will then be caught by the catch here. + } catch (e) { + console.warn(e); + callback(1, null, null, userdata); + } + + callback(0, deviceIdx, null, userdata); + }, + + ...this.adapters.interface(), + + /* ---------------------- BindGroup ---------------------- */ + + ...this.bindGroups.interface(true), + + /* ---------------------- BindGroupLayout ---------------------- */ + + ...this.bindGroupLayouts.interface(true), + + /* ---------------------- Buffer ---------------------- */ + + /** @param {number} bufferIdx */ + wgpuBufferDestroy: (bufferIdx) => { + const buffer = this.buffers.get(bufferIdx); + buffer.buffer.destroy(); + }, + + /** + * @param {number} bufferIdx + * @param {number|BigInt} offset + * @param {number|BigInt} size + * @returns {number} + */ + wgpuBufferGetMappedRange: (bufferIdx, offset, size) => { + const buffer = this.buffers.get(bufferIdx); + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + + this.assert(!buffer.mapping, "buffer already mapped"); + + const range = buffer.buffer.getMappedRange(offset, size); + + const ptr = this.mem.exports.wgpu_alloc(range.byteLength); + + buffer.mapping = { range: range, ptr: ptr, size: range.byteLength }; + return ptr; + }, + + /** + * @param {number} bufferIdx + * @returns {BigInt} + */ + wgpuBufferGetSize: (bufferIdx) => { + const buffer = this.buffers.get(bufferIdx); + return BigInt(buffer.buffer.size); + }, + + /** + * @param {number} bufferIdx + * @returns {number} + */ + wgpuBufferGetUsage: (bufferIdx) => { + const buffer = this.buffers.get(bufferIdx); + return buffer.buffer.usage; + }, + + /** + * @param {number} bufferIdx + * @param {number} mode + * @param {number|BigInt} offset + * @param {number|BigInt} size + * @param {number} callbackPtr + * @param {0|number} userdata + */ + wgpuBufferMapAsync: async (bufferIdx, mode, offset, size, callbackPtr, userdata) => { + const buffer = this.buffers.get(bufferIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + + if (buffer.buffer.mapState == "pending") { + callback(this.enums.BufferMapAsyncStatus.MappingAlreadyPending, userdata); + } else { + let result; + try { + await buffer.buffer.mapAsync(mode, offset, size); + result = 0; // Success. + } catch(e) { + console.warn(e); + result = 2; // Unknown error. + + if (e instanceof DomException) { + if (e.name == "OperationError") { + result = 1; // Validation error. + } + } + } + + callback(result, userdata); + } + }, + + /** + * @param {number} bufferIdx + * @param {number} labelPtr + */ + wgpuBufferSetLabel: (bufferIdx, labelPtr) => { + const buffer = this.buffers.get(bufferIdx); + buffer.buffer.label = this.mem.loadCstring(labelPtr); + }, + + /** + * @param {number} bufferIdx + */ + wgpuBufferUnmap: (bufferIdx) => { + const buffer = this.buffers.get(bufferIdx); + this.assert(buffer.mapping, "buffer not mapped"); + + const mapping = new Uint8Array(this.mem.memory.buffer, buffer.mapping.ptr, buffer.mapping.size); + (new Uint8Array(buffer.mapping.range)).set(mapping); + + buffer.buffer.unmap(); + + this.mem.exports.wgpu_free(buffer.mapping.ptr); + buffer.mapping = null; + }, + + ...this.buffers.interface(), + + /* ---------------------- CommandBuffer ---------------------- */ + + ...this.commandBuffers.interface(true), + + /* ---------------------- CommandEncoder ---------------------- */ + + /** + * @param {number} commandEncoderIdx + * @param {0|number} descriptorPtr + * @return {number} The compute pass encoder + */ + wgpuCommandEncoderBeginComputePass: (commandEncoderIdx, descriptorPtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + + /** @type {?GPUComputePassDescriptor} */ + let descriptor; + if (descriptorPtr != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + timestampWrites: this.ComputePassTimestampWritesPtr(descriptorPtr + 8), + }; + } + + const computePassEncoder = commandEncoder.beginComputePass(descriptor); + return this.computePassEncoders.create(computePassEncoder); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} descriptorPtr + * @return {number} The render pass encoder + */ + wgpuCommandEncoderBeginRenderPass: (commandEncoderIdx, descriptorPtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + this.assert(descriptorPtr != 0); + + let maxDrawCount = undefined; + const nextInChain = this.mem.loadPtr(descriptorPtr); + if (nextInChain != 0) { + const nextInChainType = this.mem.loadI32(nextInChain + 4); + // RenderPassDescriptorMaxDrawCount = 0x0000000F, + if (nextInChainType == 0x0000000F) { + maxDrawCount = this.mem.loadU64(nextInChain + 8); + } + } + + /** @type {GPURenderPassDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + colorAttachments: this.array( + this.mem.loadUint(descriptorPtr + 8), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize), + this.RenderPassColorAttachment, + 56, + ), + depthStencilAttachment: this.RenderPassDepthStencilAttachmentPtr(descriptorPtr + 8 + this.mem.intSize + 4), + occlusionQuerySet: this.QuerySet(descriptorPtr + 8 + this.mem.intSize + 4 + 4), + timestampWrites: this.RenderPassTimestampWritesPtr(descriptorPtr + 8 + this.mem.intSize + 4 + 4), + maxDrawCount: maxDrawCount, + }; + + const renderPassEncoder = commandEncoder.beginRenderPass(descriptor); + return this.renderPassEncoders.create(renderPassEncoder); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} bufferIdx + * @param {BigInt} offset + * @param {BigInt} size + */ + wgpuCommandEncoderClearBuffer: (commandEncoderIdx, bufferIdx, offset, size) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + const buffer = this.buffers.get(bufferIdx); + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + commandEncoder.clearBuffer(buffer.buffer, offset, size); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} sourceIdx + * @param {BigInt} sourceOffset + * @param {number} destinationIdx + * @param {BigInt} destinationOffset + * @param {BigInt} size + */ + wgpuCommandEncoderCopyBufferToBuffer: (commandEncoderIdx, sourceIdx, sourceOffset, destinationIdx, destinationOffset, size) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + const source = this.buffers.get(sourceIdx); + const destination = this.buffers.get(destinationIdx); + sourceOffset = this.unwrapBigInt(sourceOffset); + destinationOffset = this.unwrapBigInt(destinationOffset); + size = this.unwrapBigInt(size); + commandEncoder.copyBufferToBuffer(source.buffer, sourceOffset, destination.buffer, destinationOffset, size); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} sourcePtr + * @param {number} destinationPtr + * @param {number} copySizePtr + */ + wgpuCommandEncoderCopyBufferToTexture: (commandEncoderIdx, sourcePtr, destinationPtr, copySizePtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + commandEncoder.copyBufferToTexture( + this.ImageCopyBuffer(sourcePtr), + this.ImageCopyTexture(destinationPtr), + this.Extent3D(copySizePtr), + ); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} sourcePtr + * @param {number} destinationPtr + * @param {number} copySizePtr + */ + wgpuCommandEncoderCopyTextureToBuffer: (commandEncoderIdx, sourcePtr, destinationPtr, copySizePtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + commandEncoder.copyTextureToBuffer( + this.ImageCopyTexture(sourcePtr), + this.ImageCopyBuffer(destinationPtr), + this.Extent3D(copySizePtr), + ); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} sourcePtr + * @param {number} destinationPtr + * @param {number} copySizePtr + */ + wgpuCommandEncoderCopyTextureToTexture: (commandEncoderIdx, sourcePtr, destinationPtr, copySizePtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + commandEncoder.copyTextureToTexture( + this.ImageCopyTexture(sourcePtr), + this.ImageCopyTexture(destinationPtr), + this.Extent3D(copySizePtr), + ); + }, + + /** + * @param {number} commandEncoderIdx + * @param {0|number} descriptorPtr + * @returns {number} The command buffer. + */ + wgpuCommandEncoderFinish: (commandEncoderIdx, descriptorPtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + + /** @type {undefined|GPUCommandBufferDescriptor} */ + let descriptor; + if (descriptorPtr != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + }; + } + + const commandBuffer = commandEncoder.finish(descriptor); + return this.commandBuffers.create(commandBuffer); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} markerLabelPtr + */ + wgpuCommandEncoderInsertDebugMarker: (commandEncoderIdx, markerLabelPtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + commandEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr)); + }, + + /** + * @param {number} commandEncoderIdx + */ + wgpuCommandEncoderPopDebugGroup: (commandEncoderIdx) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + commandEncoder.popDebugGroup(); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} markerLabelPtr + */ + wgpuCommandEncoderPushDebugGroup: (commandEncoderIdx, groupLabelPtr) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + commandEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr)); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} querySetIdx + * @param {number} firstQuery + * @param {number} queryCount + * @param {number} destinationIdx + * @param {BigInt} destinationOffset + */ + wgpuCommandEncoderResolveQuerySet: (commandEncoderIdx, querySetIdx, firstQuery, queryCount, destinationIdx, destinationOffset) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + const querySet = this.querySets.get(querySetIdx); + const destination = this.buffers.get(destinationIdx); + destinationOffset = this.unwrapBigInt(destinationOffset); + commandEncoder.resolveQuerySet(querySet, firstQuery, queryCount, destination.buffer, destinationOffset); + }, + + /** + * @param {number} commandEncoderIdx + * @param {number} querySetIdx + * @param {number} queryIndex + */ + wgpuCommandEncoderWriteTimestamp: (commandEncoderIdx, querySetIdx, queryIndex) => { + const commandEncoder = this.commandEncoders.get(commandEncoderIdx); + const querySet = this.querySets.get(querySetIdx); + commandEncoder.writeTimestamp(querySet, queryIndex); + }, + + ...this.commandEncoders.interface(true), + + /* ---------------------- ComputePassEncoder ---------------------- */ + + + /** + * @param {number} computePassEncoderIdx + * @param {number} workgroupCountX + * @param {number} workgroupCountY + * @param {number} workgroupCountZ + */ + wgpuComputePassEncoderDispachWorkgroups: (computePassEncoderIdx, workgroupCountX, workgroupCountY, workgroupCountZ) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + computePassEncoder.dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ); + }, + + /** + * @param {number} computePassEncoderIdx + * @param {number} indirectBufferIdx + * @param {BigInt} indirectOffset + */ + wgpuComputePassEncoderDispachWorkgroupsIndirect: (computePassEncoderIdx, indirectBufferIdx, indirectOffset) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + const indirectBuffer = this.buffers.get(indirectBufferIdx); + indirectOffset = this.unwrapBigInt(indirectOffset); + computePassEncoder.dispatchWorkgroupsIndirect(indirectBuffer.buffer, indirectOffset); + }, + + /** + * @param {number} computePassEncoderIdx + */ + wgpuComputePassEncoderEnd: (computePassEncoderIdx) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + computePassEncoder.end(); + }, + + /** + * @param {number} computePassEncoderIdx + * @param {number} markerLabelPtr + */ + wgpuComputePassEncoderInsertDebugMarker: (computePassEncoderIdx, markerLabelPtr) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + computePassEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr)); + }, + + /** + * @param {number} computePassEncoderIdx + */ + wgpuComputePassEncoderPopDebugGroup: (computePassEncoderIdx) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + computePassEncoder.popDebugGroup(); + }, + + /** + * @param {number} computePassEncoderIdx + * @param {number} markerLabelPtr + */ + wgpuComputePassEncoderPushDebugGroup: (computePassEncoderIdx, groupLabelPtr) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + computePassEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr)); + }, + + /** + * @param {number} computePassEncoderIdx + * @param {number} groupIndex + * @param {0|number} groupIdx + * @param {number|BigInt} dynamicOffsetCount + * @param {number} dynamicOffsetsPtr + */ + wgpuComputePassEncoderSetBindGroup: (computePassEncoderIdx, groupIndex, groupIdx, dynamicOffsetCount, dynamicOffsetsPtr) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + dynamicOffsetCount = this.unwrapBigInt(dynamicOffsetCount); + + let bindGroup; + if (groupIdx != 0) { + bindGroup = this.bindGroups.get(groupIdx); + } + + const dynamicOffsets = []; + for (let i = 0; i < dynamicOffsetCount; i += 1) { + dynamicOffsets.push(this.mem.loadU32(dynamicOffsetsPtr)); + dynamicOffsetsPtr += 4; + } + + computePassEncoder.setBindGroup(groupIndex, bindGroup, dynamicOffsets); + }, + + /** + * @param {number} computePassEncoderIdx + * @param {number} pipelineIdx + */ + wgpuComputePassEncoderSetPipeline: (computePassEncoderIdx, pipelineIdx) => { + const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx); + const pipeline = this.computePipelines.get(pipelineIdx); + computePassEncoder.setPipeline(pipeline); + }, + + ...this.computePassEncoders.interface(true), + + /* ---------------------- ComputePipeline ---------------------- */ + + /** + * @param {number} computePipelineIdx + * @param {number} groupIndex + * @returns {number} + */ + wgpuComputePipelineGetBindGroupLayout: (computePipelineIdx, groupIndex) => { + const computePipeline = this.computePipelines.get(computePipelineIdx); + const bindGroupLayout = computePipeline.getBindGroupLayout(groupIndex); + return this.bindGroupLayouts.create(bindGroupLayout); + }, + + ...this.computePipelines.interface(true), + + /* ---------------------- Device ---------------------- */ + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The bind group. + */ + wgpuDeviceCreateBindGroup: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPUBindGroupDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + layout: this.bindGroupLayouts.get(this.mem.loadPtr(descriptorPtr + 8)), + entries: this.array( + this.mem.loadUint(descriptorPtr + 8 + this.mem.intSize), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize * 2), + this.BindGroupEntry, + 40, + ), + }; + + const bindGroup = device.createBindGroup(descriptor); + return this.bindGroups.create(bindGroup); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The bind group layout. + */ + wgpuDeviceCreateBindGroupLayout: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPUBindGroupLayoutDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + entries: this.array( + this.mem.loadUint(descriptorPtr + 8), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize), + this.BindGroupLayoutEntry, + 80, + ), + }; + + const bindGroupLayout = device.createBindGroupLayout(descriptor); + return this.bindGroupLayouts.create(bindGroupLayout); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The buffer. + */ + wgpuDeviceCreateBuffer: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPUBufferDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + usage: this.mem.loadU32(descriptorPtr + 8), + size: this.mem.loadU64(descriptorPtr + 16), + mappedAtCreation: this.mem.loadB32(descriptorPtr + 24), + }; + + const buffer = device.createBuffer(descriptor); + return this.buffers.create({buffer: buffer, mapping: null}); + }, + + /** + * @param {number} deviceIdx + * @param {0|number} descriptorPtr + * @returns {number} The command encoder. + */ + wgpuDeviceCreateCommandEncoder: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + + /** @type {GPUCommandEncoderDescriptor} */ + let descriptor; + if (descriptor != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + }; + } + + const commandEncoder = device.createCommandEncoder(descriptor); + return this.commandEncoders.create(commandEncoder); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The compute pipeline. + */ + wgpuDeviceCreateComputePipeline: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + const computePipeline = device.createComputePipeline(this.ComputePipelineDescriptor(descriptorPtr)); + return this.computePipelines.create(computePipeline); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuDeviceCreateComputePipelineAsync: async (deviceIdx, descriptorPtr, callbackPtr, userdata) => { + const device = this.devices.get(deviceIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + this.assert(descriptorPtr != 0); + + let result; + let resultIdx; + try { + const computePipeline = await device.createComputePipelineAsync(this.ComputePipelineDescriptor(descriptorPtr)); + resultIdx = this.computePipelines.create(computePipeline); + result = 0; /* Success */ + // NOTE: don't callback here, any errors that happen later will then be caught by the catch here. + } catch (e) { + console.warn(e); + result = 5; /* Unknown error */ + } + + callback(result, resultIdx, null, userdata); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The pipeline layout. + */ + wgpuDeviceCreatePipelineLayout: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPUPipelineLayoutDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + bindGroupLayouts: this.array( + this.mem.loadUint(descriptorPtr + 8), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize), + (ptr) => this.bindGroupLayouts.get(this.mem.loadPtr(ptr)), + 4, + ), + }; + + const pipelineLayout = device.createPipelineLayout(descriptor); + return this.pipelineLayouts.create(pipelineLayout); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The query set. + */ + wgpuDeviceCreateQuerySet: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPUQuerySetDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + type: this.QueryType(descriptorPtr + 8), + count: this.mem.loadU32(descriptorPtr + 12), + }; + + const querySet = device.createQuerySet(descriptor); + return this.querySets.create(querySet); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The query set. + */ + wgpuDeviceCreateRenderBundleEncoder: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPURenderBundleEncoderDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + colorFormats: this.array( + this.mem.loadUint(descriptorPtr + 8), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize), + this.TextureFormat, + 4, + ), + depthStencilFormat: this.enumeration("TextureFormat", descriptorPtr + 8 + this.mem.intSize + 4), + sampleCount: this.mem.loadU32(descriptorPtr + 8 + this.mem.intSize + 8), + depthReadOnly: this.mem.loadB32(descriptorPtr + 8 + this.mem.intSize + 12), + stencilReadOnly: this.mem.loadB32(descriptorPtr + 8 + this.mem.intSize + 16), + }; + + const renderBundleEncoder = device.createRenderBundleEncoder(descriptor); + return this.renderBundleEncoders.create(renderBundleEncoder); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The render pipeline. + */ + wgpuDeviceCreateRenderPipeline: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + const descriptor = this.RenderPipelineDescriptor(descriptorPtr); + const renderPipeline = device.createRenderPipeline(descriptor); + return this.renderPipelines.create(renderPipeline); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuDeviceCreateRenderPipelineAsync: async (deviceIdx, descriptorPtr, callbackPtr, userdata) => { + const device = this.devices.get(deviceIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + this.assert(descriptorPtr != 0); + + let result; + let resultIdx; + try { + const renderPipeline = await device.createRenderPipelineAsync(this.RenderPipelineDescriptor(descriptorPtr)); + resultIdx = this.renderPipelines.create(renderPipeline); + result = 0; /* Success */ + // NOTE: don't callback here, any errors that happen later will then be caught by the catch here. + } catch (e) { + console.warn(e); + result = 5; /* Unknown error */ + } + + callback(result, resultIdx, null, userdata); + }, + + /** + * @param {number} deviceIdx + * @param {0|number} descriptorPtr + * @returns {number} The sampler. + */ + wgpuDeviceCreateSampler: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + + /** @type {?GPUSamplerDescriptor} */ + let descriptor; + if (descriptorPtr != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + addressModeU: this.enumeration("AddressMode", descriptorPtr + 8), + addressModeV: this.enumeration("AddressMode", descriptorPtr + 12), + addressModeW: this.enumeration("AddressMode", descriptorPtr + 16), + magFilter: this.enumeration("FilterMode", descriptorPtr + 20), + minFilter: this.enumeration("FilterMode", descriptorPtr + 24), + mipMapFilter: this.enumeration("MipmapFilterMode", descriptorPtr + 28), + lodMinClamp: this.mem.loadF32(descriptorPtr + 32), + lodMaxClamp: this.mem.loadF32(descriptorPtr + 36), + compare: this.enumeration("CompareFunction", descriptorPtr + 40), + maxAnisotropy: this.mem.loadU16(descriptorPtr + 44), + }; + } + + const sampler = device.createSampler(descriptor); + return this.samplers.create(sampler); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The shader module. + */ + wgpuDeviceCreateShaderModule: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + const nextInChain = this.mem.loadPtr(descriptorPtr); + const nextInChainType = this.mem.loadI32(nextInChain + 4); + + // ShaderModuleWGSLDescriptor = 0x00000006, + if (nextInChainType != 6) { + throw new TypeError(`Descriptor type should be 'ShaderModuleWGSLDescriptor', got ${nextInChainType}`); + } + + /** @type {GPUShaderModuleDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + code: this.mem.loadCstring(nextInChain + 8), + compilationHints: this.array( + this.mem.loadUint(descriptorPtr + 8), + this.mem.loadPtr(descriptorPtr + 8 + this.mem.intSize), + this.ShaderModuleCompilationHint, + 12, + ), + }; + + const shaderModule = device.createShaderModule(descriptor); + return this.shaderModules.create(shaderModule); + }, + + /** + * @param {number} deviceIdx + * @param {number} descriptorPtr + * @returns {number} The texture. + */ + wgpuDeviceCreateTexture: (deviceIdx, descriptorPtr) => { + const device = this.devices.get(deviceIdx); + this.assert(descriptorPtr != 0); + + /** @type {GPUTextureDescriptor} */ + const descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + usage: this.mem.loadU32(descriptorPtr + 8), + dimension: this.enumeration("TextureDimension", descriptorPtr + 12), + size: this.Extent3D(descriptorPtr + 16), + format: this.enumeration("TextureFormat", descriptorPtr + 28), + mipLevelCount: this.mem.loadU32(descriptorPtr + 32), + sampleCount: this.mem.loadU32(descriptorPtr + 36), + viewFormats: this.array( + this.mem.loadUint(descriptorPtr + 40), + this.mem.loadPtr(descriptorPtr + 40 + this.mem.intSize), + (ptr) => this.enumeration("TextureFormat", ptr), + 4, + ), + }; + + const texture = device.createTexture(descriptor); + return this.textures.create(texture); + }, + + /** + * @param {number} deviceIdx + */ + wgpuDeviceDestroy: (deviceIdx) => { + const device = this.devices.get(deviceIdx); + device.destroy(); + }, + + /** + * @param {number} deviceIdx + * @param {number} featuresPtr + * @returns {number|BigInt} + */ + wgpuDeviceEnumerateFeatures: (deviceIdx, featuresPtr) => { + const device = this.devices.get(deviceIdx); + return this.genericEnumerateFeatures(device.features, featuresPtr); + }, + + /** + * @param {number} deviceIdx + * @param {number} limitsPtr + * @returns {boolean} + */ + wgpuDeviceGetLimits: (deviceIdx, limitsPtr) => { + const device = this.devices.get(deviceIdx); + return this.genericGetLimits(device.limits, limitsPtr); + }, + + /** + * @param {number} deviceIdx + * @returns {number} + */ + wgpuDeviceGetQueue: (deviceIdx) => { + const device = this.devices.get(deviceIdx); + return this.queues.create(device.queue); + }, + + /** + * @param {number} deviceIdx + * @param {number} featureInt + * @returns {boolean} + */ + wgpuDeviceHasFeature: (deviceIdx, featureInt) => { + const device = this.devices.get(deviceIdx); + return device.features.has(this.enums.FeatureName[featureInt]); + }, + + /** + * @param {number} deviceIdx + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuDevicePopErrorScope: async (deviceIdx, callbackPtr, userdata) => { + const device = this.devices.get(deviceIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + const error = await device.popErrorScope(); + if (!error) { + callback(0, null, userdata); + return; + } + console.warn(error); + let status = 4; + if (error instanceof GPUValidationError) { + status = 1; + } else if (error instanceof GPUOutOfMemoryError) { + status = 2; + } else if (error instanceof GPUInternalError) { + status = 3; + } + callback(status, null, userdata); + }, + + /** + * @param {number} deviceIdx + * @param {number} filterInt + */ + wgpuDevicePushErrorScope: (deviceIdx, filterInt) => { + const device = this.devices.get(deviceIdx); + device.pushErrorScope(this.enums.ErrorFilter[filterInt]); + }, + + /** + * @param {number} deviceIdx + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuDeviceSetUncapturedErrorCallback: (deviceIdx, callbackPtr, userdata) => { + const device = this.devices.get(deviceIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + + device.onuncapturederror = (ev) => { + console.warn(ev.error); + let status = 4; + if (error instanceof GPUValidationError) { + status = 1; + } else if (error instanceof GPUOutOfMemoryError) { + status = 2; + } else if (error instanceof GPUInternalError) { + status = 3; + } + callback(status, null, userdata); + }; + }, + + ...this.devices.interface(true), + + /* ---------------------- Instance ---------------------- */ + + /** + * @param {number} instanceIdx + * @param {number} descriptorPtr + */ + wgpuInstanceCreateSurface: (instanceIdx, descriptorPtr) => { + this.assert(instanceIdx > 0); + this.assert(descriptorPtr != 0); + + const nextInChain = this.mem.loadPtr(descriptorPtr); + const nextInChainType = this.mem.loadI32(nextInChain + 4); + + // SurfaceDescriptorFromCanvasHTMLSelector = 0x00000004, + if (nextInChainType != 4) { + throw new TypeError(`Descriptor type should be 'SurfaceDescriptorFromCanvasHTMLSelector', got ${nextInChainType}`); + } + + const selector = this.mem.loadCstring(nextInChain + 8); + const surface = document.querySelector(selector); + if (!surface) { + throw new Error(`Selector '${selector}' did not match any element`); + } + if (!(surface instanceof HTMLCanvasElement)) { + throw new Error('Selector matches an element that is not a canvas'); + } + + return this.surfaces.create(surface); + }, + + /** + * @param {number} instanceIdx + * @param {number} featureInt + * @returns {boolean} + */ + wgpuInstanceHasWGSLLanguageFeature: (instanceIdx, featureInt) => { + return navigator.gpu.wgslLanguageFeatures.has(this.enums.WGSLFeatureName[featureInt]); + }, + + /** + * @param {number} instanceIdx + */ + wgpuInstanceProcessEvents: (instanceIdx) => { + console.warn("unimplemented: wgpuInstanceProcessEvents"); + }, + + /** + * @param {number} instanceIdx + * @param {0|number} optionsPtr + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuInstanceRequestAdapter: async (instanceIdx, optionsPtr, callbackPtr, userdata) => { + this.assert(instanceIdx > 0); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + + /** @type {GPURequestAdapterOptions} */ + let options; + if (optionsPtr != 0) { + options = { + powerPreference: this.enumeration("PowerPreference", optionsPtr + 8), + forceFallbackAdapter: this.mem.loadB32(optionsPtr + 16), + }; + } + + let adapterIdx; + try { + const adapter = await navigator.gpu.requestAdapter(options); + adapterIdx = this.adapters.create(adapter); + // NOTE: don't callback here, any errors that happen later will then be caught by the catch here. + } catch(e) { + console.warn(e); + callback(2, null, null, userdata); + } + + callback(0, adapterIdx, null, userdata); + }, + + ...this.instances.interface(false), + + /* ---------------------- PipelineLayout ---------------------- */ + + ...this.pipelineLayouts.interface(true), + + /* ---------------------- QuerySet ---------------------- */ + + /** + * @param {number} querySetIdx + */ + wgpuQuerySetDestroy: (querySetIdx) => { + const querySet = this.querySets.get(querySetIdx); + querySet.destroy(); + }, + + /** + * @param {number} querySetIdx + * @returns {number} + */ + wgpuQuerySetGetCount: (querySetIdx) => { + const querySet = this.querySets.get(querySetIdx); + return querySet.count; + }, + + /** + * @param {number} querySetIdx + * @returns {number} + */ + wgpuQuerySetGetType: (querySetIdx) => { + const querySet = this.querySets.get(querySetIdx); + return this.enums.QueryType.indexOf(querySet.type); + }, + + ...this.querySets.interface(true), + + /* ---------------------- Queue ---------------------- */ + + /** + * @param {number} queueIdx + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuQueueOnSubmittedWorkDone: async (queueIdx, callbackPtr, userdata) => { + const queue = this.queues.get(queueIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + let result; + try { + await queue.onSubmittedWorkDone(); + result = 0; + } catch(e) { + console.warn(e); + result = 1; + } + callback(result, userdata); + }, + + /** + * @param {number} queueIdx + * @param {BigInt|number} commandCount + * @param {number} commandsPtr + */ + wgpuQueueSubmit: (queueIdx, commandCount, commandsPtr) => { + const queue = this.queues.get(queueIdx); + const commands = this.array( + this.unwrapBigInt(commandCount), + commandsPtr, + (ptr) => this.commandBuffers.get(this.mem.loadPtr(ptr)), + 4, + ); + queue.submit(commands); + }, + + /** + * @param {number} queueIdx + * @param {number} bufferIdx + * @param {BigInt} bufferOffset + * @param {number} dataPtr + * @param {number|BigInt} size + */ + wgpuQueueWriteBuffer: (queueIdx, bufferIdx, bufferOffset, dataPtr, size) => { + const queue = this.queues.get(queueIdx); + const buffer = this.buffers.get(bufferIdx); + bufferOffset = this.unwrapBigInt(bufferOffset); + size = this.unwrapBigInt(size); + queue.writeBuffer(buffer.buffer, bufferOffset, this.mem.loadBytes(dataPtr, size), 0, size); + }, + + /** + * @param {number} queueIdx + * @param {number} destinationPtr + * @param {number} dataPtr + * @param {number|BigInt} dataSize + * @param {number} dataLayoutPtr + * @param {number} writeSizePtr + */ + wgpuQueueWriteTexture: (queueIdx, destinationPtr, dataPtr, dataSize, dataLayoutPtr, writeSizePtr) => { + const queue = this.queues.get(queueIdx); + const destination = this.ImageCopyTexture(destinationPtr); + dataSize = this.unwrapBigInt(dataSize); + const dataLayout = this.TextureDataLayout(dataLayoutPtr); + const writeSize = this.Extent3D(writeSizePtr); + queue.writeTexture(destination, this.mem.loadBytes(dataPtr, dataSize), dataLayout, writeSize); + }, + + ...this.queues.interface(true), + + /* ---------------------- RenderBundle ---------------------- */ + + ...this.renderBundles.interface(true), + + /* ---------------------- RenderBundleEncoder ---------------------- */ + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} vertexCount + * @param {number} instanceCount + * @param {number} firstVertex + * @param {number} firstInstance + */ + wgpuRenderBundleEncoderDraw: (renderBundleEncoderIdx, vertexCount, instanceCount, firstVertex, firstInstance) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + renderBundleEncoder.draw(vertexCount, instanceCount, firstVertex, firstInstance); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} indexCount + * @param {number} instanceCount + * @param {number} firstIndex + * @param {number} baseVertex + * @param {number} firstInstance + */ + wgpuRenderBundleEncoderDrawIndexed: (renderBundleEncoderIdx, indexCount, instanceCount, firstIndex, baseVertex, firstInstance) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + renderBundleEncoder.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} indirectBufferIdx + * @param {BigInt} indirectOffset + */ + wgpuRenderBundleEncoderDrawIndexedIndirect: (renderBundleEncoderIdx, indirectBufferIdx, indirectOffset) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + indirectOffset = this.unwrapBigInt(indirectOffset); + const buffer = this.buffers.get(indirectBufferIdx); + renderBundleEncoder.drawIndexedIndirect(buffer.buffer, indirectOffset); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} indirectBufferIdx + * @param {BigInt} indirectOffset + */ + wgpuRenderBundleEncoderDrawIndirect: (renderBundleEncoderIdx, indirectBufferIdx, indirectOffset) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + indirectOffset = this.unwrapBigInt(indirectOffset); + const buffer = this.buffers.get(indirectBufferIdx); + renderBundleEncoder.drawIndirect(buffer.buffer, indirectOffset); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {0|number} descriptorPtr + * @returns {number} + */ + wgpuRenderBundleEncoderFinish: (renderBundleEncoderIdx, descriptorPtr) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + + /** @type {?GPURenderBundleDescriptor} */ + let descriptor; + if (descriptorPtr != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + }; + } + + const renderBundle = renderBundleEncoder.finish(descriptor); + return this.renderBundles.create(renderBundle); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} markerLabelPtr + */ + wgpuRenderBundleEncoderInsertDebugMarker: (renderBundleEncoderIdx, markerLabelPtr) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + this.assert(markerLabelPtr != 0); + const markerLabel = this.mem.loadCstring(markerLabelPtr); + renderBundleEncoder.insertDebugMarker(markerLabel); + }, + + /** + * @param {number} renderBundleEncoderIdx + */ + wgpuRenderBundleEncoderPopDebugGroup: (renderBundleEncoderIdx) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + renderBundleEncoder.popDebugGroup(); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} groupLabelPtr + */ + wgpuRenderBundleEncoderPushDebugGroup: (renderBundleEncoderIdx, groupLabelPtr) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + this.assert(groupLabelPtr!= 0); + const groupLabel = this.mem.loadCstring(groupLabelPtr); + renderBundleEncoder.pushDebugGroup(groupLabel); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} groupIndex + * @param {0|number} groupIdx + * @param {number|BigInt} dynamicOffsetCount + * @param {number} dynamicOffsetsPtr + */ + wgpuRenderBundleEncoderSetBindGroup: (renderBundleEncoderIdx, groupIndex, groupIdx, dynamicOffsetCount, dynamicOffsetsPtr) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + + let group; + if (groupIdx > 0) { + group = this.bindGroups.get(groupIdx); + } + + dynamicOffsetCount = this.unwrapBigInt(dynamicOffsetCount); + const dynamicOffsets = this.array(dynamicOffsetCount, dynamicOffsetsPtr, this.mem.loadU32, 4); + + renderBundleEncoder.setBindGroup(groupIndex, group, dynamicOffsets); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} bufferIdx + * @param {number} formatInt + * @param {BigInt} offset + * @param {BigInt} size + */ + wgpuRenderBundleEncoderSetIndexBuffer: (renderBundleEncoderIdx, bufferIdx, formatInt, offset, size) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + const buffer = this.buffers.get(bufferIdx); + const format = this.enums.IndexFormat[formatInt]; + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + renderBundleEncoder.setIndexBuffer(buffer.buffer, format, offset, size); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} pipelineIdx + */ + wgpuRenderBundleEncoderSetPipeline: (renderBundleEncoderIdx, pipelineIdx) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + const pipeline = this.renderPipelines.get(pipelineIdx); + renderBundleEncoder.setPipeline(pipeline); + }, + + /** + * @param {number} renderBundleEncoderIdx + * @param {number} slot + * @param {0|number} bufferIdx + * @param {BigInt} offset + * @param {BigInt} size + */ + wgpuRenderBundleEncoderSetVertexBuffer: (renderBundleEncoderIdx, slot, bufferIdx, offset, size) => { + const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx); + + let buffer; + if (bufferIdx > 0) { + buffer = this.buffers.get(bufferIdx).buffer; + } + + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + renderBundleEncoder.setVertexBuffer(slot, buffer, offset, size); + }, + + ...this.renderBundleEncoders.interface(true), + + /* ---------------------- RenderPassEncoder ---------------------- */ + + /** + * @param {number} renderPassEncoderIdx + * @param {number} queryIndex + */ + wgpuRenderPassEncoderBeginOcclusionQuery: (renderPassEncoderIdx, queryIndex) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.beginOcclusionQuery(queryIndex); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} vertexCount + * @param {number} instanceCount + * @param {number} firstVertex + * @param {number} firstInstance + */ + wgpuRenderPassEncoderDraw: (renderPassEncoderIdx, vertexCount, instanceCount, firstVertex, firstInstance) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.draw(vertexCount, instanceCount, firstVertex, firstInstance); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} indexCount + * @param {number} instanceCount + * @param {number} firstIndex + * @param {number} baseVertex + * @param {number} firstInstance + */ + wgpuRenderPassEncoderDrawIndexed: (renderPassEncoderIdx, indexCount, instanceCount, firstIndex, baseVertex, firstInstance) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} indirectBufferIdx + * @param {BigInt} indirectOffset + */ + wgpuRenderPassEncoderDrawIndexedIndirect: (renderPassEncoderIdx, indirectBufferIdx, indirectOffset) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + const buffer = this.buffers.get(indirectBufferIdx); + indirectOffset = this.unwrapBigInt(indirectOffset); + renderPassEncoder.drawIndexedIndirect(buffer.buffer, indirectOffset); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} indirectBufferIdx + * @param {BigInt} indirectOffset + */ + wgpuRenderPassEncoderDrawIndirect: (renderPassEncoderIdx, indirectBufferIdx, indirectOffset) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + const buffer = this.buffers.get(indirectBufferIdx); + indirectOffset = this.unwrapBigInt(indirectOffset); + renderPassEncoder.drawIndirect(buffer.buffer, indirectOffset); + }, + + /** + * @param {number} renderPassEncoderIdx + */ + wgpuRenderPassEncoderEnd: (renderPassEncoderIdx) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.end(); + }, + + /** + * @param {number} renderPassEncoderIdx + */ + wgpuRenderPassEncoderEndOcclusionQuery: (renderPassEncoderIdx) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.endOcclusionQuery(); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number|BigInt} bundleCount + * @param {number} bundlesPtr + */ + wgpuRenderPassEncoderExecuteBundles: (renderPassEncoderIdx, bundleCount, bundlesPtr) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + bundleCount = this.unwrapBigInt(bundleCount); + const bundles = this.array( + bundleCount, + bundlesPtr, + (ptr) => this.renderBundles.get(this.mem.loadPtr(ptr)), + 4, + ); + renderPassEncoder.executeBundles(bundles); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} markerLabelPtr + */ + wgpuRenderPassEncoderInsertDebugMarker: (renderPassEncoderIdx, markerLabelPtr) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + const markerLabel = this.mem.loadCstring(markerLabelPtr); + renderPassEncoder.insertDebugMarker(markerLabel); + }, + + /** + * @param {number} renderPassEncoderIdx + */ + wgpuRenderPassEncoderPopDebugGroup: (renderPassEncoderIdx) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.popDebugGroup(); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} groupLabelPtr + */ + wgpuRenderPassEncoderPushDebugGroup: (renderPassEncoderIdx, groupLabelPtr) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + const groupLabel = this.mem.loadCstring(groupLabelPtr); + renderPassEncoder.pushDebugGroup(groupLabel); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} groupIndex + * @param {0|number} groupIdx + * @param {number|BigInt} dynamicOffsetCount + * @param {number} dynamicOffsetsPtr + */ + wgpuRenderPassEncoderSetBindGroup: (renderPassEncoderIdx, groupIndex, groupIdx, dynamicOffsetCount, dynamicOffsetsPtr) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + + let group; + if (groupIdx > 0) { + group = this.bindGroups.get(groupIdx); + } + + dynamicOffsetCount = this.unwrapBigInt(dynamicOffsetCount); + const dynamicOffsets = this.array(dynamicOffsetCount, dynamicOffsetsPtr, this.mem.loadU32, 4); + + renderPassEncoder.setBindGroup(groupIndex, group, dynamicOffsets); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} bufferIdx + * @param {number} formatInt + * @param {BigInt} offset + * @param {BigInt} size + */ + wgpuRenderPassEncoderSetIndexBuffer: (renderPassEncoderIdx, bufferIdx, formatInt, offset, size) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + const buffer = this.buffers.get(bufferIdx); + const format = this.enums.IndexFormat[formatInt]; + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + renderPassEncoder.setIndexBuffer(buffer.buffer, format, offset, size); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} pipelineIdx + */ + wgpuRenderPassEncoderSetPipeline: (renderPassEncoderIdx, pipelineIdx) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + const pipeline = this.renderPipelines.get(pipelineIdx); + renderPassEncoder.setPipeline(pipeline); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + wgpuRenderPassEncoderSetScissorRect: (renderPassEncoderIdx, x, y, width, height) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.setScissorRect(x, y, width, height); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} reference + */ + wgpuRenderPassEncoderSetStencilReference: (renderPassEncoderIdx, reference) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.setStencilReference(reference); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} slot + * @param {0|number} bufferIdx + * @param {BigInt} offset + * @param {BigInt} size + */ + wgpuRenderPassEncoderSetVertexBuffer: (renderPassEncoderIdx, slot, bufferIdx, offset, size) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + + let buffer; + if (bufferIdx > 0) { + buffer = this.buffers.get(bufferIdx).buffer; + } + + offset = this.unwrapBigInt(offset); + size = this.unwrapBigInt(size); + renderPassEncoder.setVertexBuffer(slot, buffer, offset, size); + }, + + /** + * @param {number} renderPassEncoderIdx + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + * @param {number} minDepth + * @param {number} maxDepth + */ + wgpuRenderPassEncoderSetViewport: (renderPassEncoderIdx, x, y, width, height, minDepth, maxDepth) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + renderPassEncoder.setViewport(x, y, width, height, minDepth, maxDepth); + }, + + ...this.renderPassEncoders.interface(true), + + /* ---------------------- RenderPipeline ---------------------- */ + + /** + * @param {number} renderPipelineIdx + * @param {number} groupIndex + * @returns {number} + */ + wgpuRenderPipelineGetBindGroupLayout: (renderPipelineIdx, groupIndex) => { + const renderPipeline = this.renderPipelines.get(renderPipelineIdx); + const bindGroupLayout = renderPipeline.getBindGroupLayout(groupIndex); + return this.bindGroupLayouts.create(bindGroupLayout); + }, + + ...this.renderPipelines.interface(true), + + /* ---------------------- Sampler ---------------------- */ + + ...this.samplers.interface(true), + + /* ---------------------- ShaderModule ---------------------- */ + + /** + * @param {number} shaderModuleIdx + * @param {number} callbackPtr + * @param {number} userdata + */ + wgpuShaderModuleGetCompilationInfo: async (shaderModuleIdx, callbackPtr, userdata) => { + const shaderModule = this.shaderModules.get(shaderModuleIdx); + const callback = this.mem.exports.__indirect_function_table.get(callbackPtr); + + let status = 0; + let retAddr = 0; + + const ptrsToFree = []; + + try { + const compilationInfo = await shaderModule.getCompilationInfo(); + + const size = compilationInfo.messages.length * 72; + const addr = this.mem.exports.wgpu_alloc(size); + ptrsToFree.push(addr); + compilationInfo.messages.forEach((message, i) => { + const messageLength = new TextEncoder().encode(message.message).length; + const messageAddr = this.mem.exports.wgpu_alloc(messageLength); + ptrsToFree.push(messageAddr); + this.mem.storeString(messageAddr, message.message); + this.mem.storeI32(addr + (i * size) + 4); + + this.mem.storeI32(addr + (i * size) + 8, this.enums.CompilationMessageType.indexOf(message.type)); + + this.mem.storeU64(addr + (i * size) + 16, message.lineNum); + this.mem.storeU64(addr + (i * size) + 24, message.linePos); + this.mem.storeU64(addr + (i * size) + 32, message.offset); + this.mem.storeU64(addr + (i * size) + 40, message.length); + + // TODO: UTF16 units. + this.mem.storeU64(addr + (i * size) + 48, message.linePos); + this.mem.storeU64(addr + (i * size) + 56, message.offset); + this.mem.storeU64(addr + (i * size) + 64, message.length); + }); + + retAddr = this.mem.exports.wgpu_alloc(3*this.mem.intSize); + ptrsToFree.push(retAddr); + this.mem.storeUint(retAddr + this.mem.intSize, compilationInfo.messages.length); + this.mem.storeI32(retAddr + this.mem.intSize*2, addr); + } catch (e) { + console.warn(e); + status = 1; + } + + callback(status, retAddr, userdata); + + ptrsToFree.forEach(ptr => this.mem.exports.wgpu_free(ptr)); + }, + + ...this.shaderModules.interface(true), + + /* ---------------------- Surface ---------------------- */ + + /** + * @param {number} surfaceIdx + * @param {number} configPtr + */ + wgpuSurfaceConfigure: (surfaceIdx, configPtr) => { + const surface = this.surfaces.get(surfaceIdx); + const context = surface.getContext('webgpu'); + + const widthOff = 16 + this.mem.intSize + 8; + surface.width = this.mem.loadU32(configPtr + widthOff); + surface.height = this.mem.loadU32(configPtr + widthOff + 4); + + /** @type {GPUCanvasConfiguration} */ + const config = { + device: this.devices.get(this.mem.loadPtr(configPtr + 4)), + format: this.enumeration("TextureFormat", configPtr + 8), + usage: this.mem.loadU32(configPtr + 12), + viewFormats: this.array( + this.mem.loadUint(configPtr + 16), + this.mem.loadPtr(configPtr + 16 + this.mem.intSize), + (ptr) => this.enumeration("TextureFormat", ptr), + 4, + ), + alphaMode: this.enumeration("CompositeAlphaMode", configPtr + widthOff - 4), + // // NOTE: present mode seems unused. + presentMode: this.enumeration("PresentMode", configPtr + widthOff + 4), + }; + + context.configure(config); + }, + + /** + * @param {number} surfaceIdx + * @param {number} adapterIdx + * @param {number} capabilitiesPtr + */ + wgpuSurfaceGetCapabilities: (surfaceIdx, adapterIdx, capabilitiesPtr) => { + const formatStr = navigator.gpu.getPreferredCanvasFormat(); + const format = this.enums.TextureFormat.indexOf(formatStr); + + this.mem.storeUint(capabilitiesPtr + this.mem.intSize, 1); + const formatAddr = this.mem.exports.wgpu_alloc(4); + this.mem.storeI32(formatAddr, format); + this.mem.storeI32(capabilitiesPtr + this.mem.intSize*2, formatAddr); + + // NOTE: present modes don't seem to actually do anything in JS, we can just give back a default FIFO though. + this.mem.storeUint(capabilitiesPtr + this.mem.intSize*3, 1); + const presentModesAddr = this.mem.exports.wgpu_alloc(4); + this.mem.storeI32(presentModesAddr, 0); + this.mem.storeI32(capabilitiesPtr + this.mem.intSize*4, presentModesAddr); + + // Browser seems to support opaque (1) and premultiplied (2). + this.mem.storeUint(capabilitiesPtr + this.mem.intSize*5, 2); + const alphaModesAddr = this.mem.exports.wgpu_alloc(8); + this.mem.storeI32(alphaModesAddr + 0, 1); // Opaque. + this.mem.storeI32(alphaModesAddr + 4, 2); // premultiplied. + this.mem.storeI32(capabilitiesPtr + this.mem.intSize*6, alphaModesAddr); + }, + + /** + * @param {number} surfaceIdx + * @param {number} texturePtr + */ + wgpuSurfaceGetCurrentTexture: (surfaceIdx, texturePtr) => { + const surface = this.surfaces.get(surfaceIdx); + const context = surface.getContext('webgpu'); + const texture = context.getCurrentTexture(); + + const textureIdx = this.textures.create(texture); + this.mem.storeI32(texturePtr, textureIdx); + + // TODO: determine suboptimal and/or status. + }, + + /** + * @param {number} surfaceIdx + * @param {number} texturePtr + * @returns {number} + */ + wgpuSurfaceGetPreferredFormat: (surfaceIdx, adapterIdx) => { + const formatStr = navigator.gpu.getPreferredCanvasFormat(); + const format = this.enums.TextureFormat.indexOf(formatStr); + return format; + }, + + /** + * @param {number} surfaceIdx + */ + wgpuSurfacePresent: (surfaceIdx) => { + // NOTE: Not really anything to do here. + }, + + /** + * @param {number} surfaceIdx + */ + wgpuSurfaceUnconfigure: (surfaceIdx) => { + const surface = this.surfaces.get(surfaceIdx); + surface.getContext('webgpu').unconfigure(); + }, + + ...this.surfaces.interface(true), + + /* ---------------------- SurfaceCapabilities ---------------------- */ + + /** + * @param {number} surfaceCapabilitiesPtr + */ + wgpuSurfaceCapabilitiesFreeMembers: (surfaceCapabilitiesPtr) => { + const formatsAddr = this.mem.loadI32(surfaceCapabilitiesPtr + this.mem.intSize*2); + this.mem.exports.wgpu_free(formatsAddr); + + const presentModesAddr = this.mem.loadI32(surfaceCapabilitiesPtr + this.mem.intSize*4); + this.mem.exports.wgpu_free(presentModesAddr); + + const alphaModesAddr = this.mem.loadI32(surfaceCapabilitiesPtr + this.mem.intSize*6); + this.mem.exports.wgpu_free(alphaModesAddr); + }, + + /* ---------------------- Texture ---------------------- */ + + /** + * @param {number} textureIdx + * @param {0|number} descriptorPtr + * @returns {number} + */ + wgpuTextureCreateView: (textureIdx, descriptorPtr) => { + const texture = this.textures.get(textureIdx); + + /** @type {?GPUTextureViewDescriptor} */ + let descriptor; + if (descriptorPtr != 0) { + descriptor = { + label: this.mem.loadCstring(descriptorPtr + 4), + format: this.enumeration("TextureFormat", descriptorPtr + 8), + dimension: this.enumeration("TextureViewDimension", descriptorPtr + 12), + baseMipLevel: this.mem.loadU32(descriptorPtr + 16), + mipLevelCount: this.mem.loadU32(descriptorPtr + 20), + baseArrayLayer: this.mem.loadU32(descriptorPtr + 24), + arrayLayerCount: this.mem.loadU32(descriptorPtr + 28), + aspect: this.enumeration("TextureAspect", descriptorPtr + 32), + }; + if (descriptor.arrayLayerCount == 0xFFFFFFFF) { + descriptor.arrayLayerCount = undefined; + } + if (descriptor.mipLevelCount == 0xFFFFFFFF) { + descriptor.mipLevelCount = undefined; + } + } + + const textureView = texture.createView(descriptor); + return this.textureViews.create(textureView); + }, + + /** + * @param {number} textureIdx + */ + wgpuTextureDestroy: (textureIdx) => { + const texture = this.textures.get(textureIdx); + texture.destroy(); + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureDepthOrArrayLayers: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return texture.depthOrArrayLayers; + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetDimension: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return this.enums.TextureDimension.indexOf(texture.dimension); + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetFormat: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return this.enums.TextureFormat.indexOf(texture.format); + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetHeight: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return texture.height; + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetMipLevelCount: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return texture.mipLevelCount; + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetSampleCount: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return texture.sampleCount; + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetUsage: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return texture.usage; + }, + + /** + * @param {number} textureIdx + * @returns {number} + */ + wgpuTextureGetWidth: (textureIdx) => { + const texture = this.textures.get(textureIdx); + return texture.width; + }, + + ...this.textures.interface(true), + + /* ---------------------- TextureView ---------------------- */ + + ...this.textureViews.interface(true), + }; + } +} + +/** @template T */ +class WebGPUObjectManager { + + /** + * @param {string} name + * @param {WasmMemoryInterface} mem + */ + constructor(name, mem) { + this.name = name; + this.mem = mem; + + this.idx = 0; + + /** @type {Record} */ + this.objects = {}; + } + + /** + * @param {T} object + * @returns {number} + */ + create(object) { + this.objects[this.idx] = { references: 1, object }; + this.idx += 1; + return this.idx; + } + + /** + * @param {number} idx + * @returns {T} + */ + get(idx) { + return this.objects[idx-1].object; + } + + /** @param {number} idx */ + release(idx) { + this.objects[idx-1].references -= 1; + if (this.objects[idx-1].references == 0) { + delete this.objects[idx-1]; + } + } + + /** @param {number} idx */ + reference(idx) { + this.objects[idx-1].references += 1; + } + + interface(withLabelSetter = false) { + const inter = {}; + inter[`wgpu${this.name}Reference`] = this.reference.bind(this); + inter[`wgpu${this.name}Release`] = this.release.bind(this); + if (withLabelSetter) { + inter[`wgpu${this.name}SetLabel`] = (idx, labelPtr) => { + const obj = this.get(idx); + obj.label = this.mem.loadCstring(labelPtr); + }; + } + return inter; + } +} + +window.odin = window.odin || {}; +window.odin.WebGPUInterface = WebGPUInterface; + +})(); diff --git a/vendor/wgpu/wgpu.odin b/vendor/wgpu/wgpu.odin new file mode 100644 index 000000000..1a89c9132 --- /dev/null +++ b/vendor/wgpu/wgpu.odin @@ -0,0 +1,1636 @@ +package wgpu + +import "base:intrinsics" + +WGPU_SHARED :: #config(WGPU_SHARED, false) +WGPU_DEBUG :: #config(WGPU_DEBUG, false) + +@(private) TYPE :: "debug" when WGPU_DEBUG else "release" + +when ODIN_OS == .Windows { + @(private) ARCH :: "x86_64" when ODIN_ARCH == .amd64 else "x86_64" when ODIN_ARCH == .i386 else #panic("unsupported WGPU Native architecture") + @(private) EXT :: ".dll.lib" when WGPU_SHARED else ".lib" + @(private) LIB :: "lib/wgpu-windows-" + ARCH + "-" + TYPE + "/wgpu_native" + EXT + + when !#exists(LIB) { + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") + } + + foreign import libwgpu { + LIB, + "system:d3dcompiler.lib", + "system:ws2_32.lib", + "system:userenv.lib", + "system:bcrypt.lib", + "system:ntdll.lib", + "system:opengl32.lib", + "system:advapi32.lib", + } +} else when ODIN_OS == .Darwin { + @(private) ARCH :: "x86_64" when ODIN_ARCH == .amd64 else "aarch64" when ODIN_ARCH == .arm64 else #panic("unsupported WGPU Native architecture") + @(private) EXT :: ".dylib" when WGPU_SHARED else ".a" + @(private) LIB :: "lib/wgpu-macos-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT + + when !#exists(LIB) { + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") + } + + foreign import libwgpu { + LIB, + "system:CoreFoundation.framework", + "system:QuartzCore.framework", + "system:Metal.framework", + } +} else when ODIN_OS == .Linux { + @(private) ARCH :: "x86_64" when ODIN_ARCH == .amd64 else "aarch64" when ODIN_ARCH == .arm64 else #panic("unsupported WGPU Native architecture") + @(private) EXT :: ".so" when WGPU_SHARED else ".a" + @(private) LIB :: "lib/wgpu-linux-" + ARCH + "-" + TYPE + "/libwgpu_native" + EXT + + when !#exists(LIB) { + #panic("Could not find the compiled WGPU Native library at '" + #directory + LIB + "', these can be downloaded from https://github.com/gfx-rs/wgpu-native/releases/tag/v0.19.4.1, make sure to read the README at '" + #directory + "vendor/wgpu/README.md'") + } + + foreign import libwgpu { + LIB, + "system:ld", + "system:m", + } +} else when ODIN_OS == .JS { + foreign import libwgpu "wgpu" +} + +ARRAY_LAYER_COUNT_UNDEFINED :: max(u32) +COPY_STRIDE_UNDEFINED :: max(u32) +DEPTH_SLICE_UNDEFINED :: max(u32) +LIMIT_U32_UNDEFINED :: max(u32) +LIMIT_U64_UNDEFINED :: max(u64) +MIP_LEVEL_COUNT_UNDEFINED :: max(u32) +QUERY_SET_INDEX_UNDEFINED :: max(u32) +WHOLE_MAP_SIZE :: max(uint) +WHOLE_SIZE :: max(u64) + +Flags :: u32 + +Adapter :: distinct rawptr +BindGroup :: distinct rawptr +BindGroupLayout :: distinct rawptr +Buffer :: distinct rawptr +CommandBuffer :: distinct rawptr +CommandEncoder :: distinct rawptr +ComputePassEncoder :: distinct rawptr +ComputePipeline :: distinct rawptr +Device :: distinct rawptr +Instance :: distinct rawptr +PipelineLayout :: distinct rawptr +QuerySet :: distinct rawptr +Queue :: distinct rawptr +RenderBundle :: distinct rawptr +RenderBundleEncoder :: distinct rawptr +RenderPassEncoder :: distinct rawptr +RenderPipeline :: distinct rawptr +Sampler :: distinct rawptr +ShaderModule :: distinct rawptr +Surface :: distinct rawptr +Texture :: distinct rawptr +TextureView :: distinct rawptr + +AdapterType :: enum i32 { + DiscreteGPU = 0x00000000, + IntegratedGPU = 0x00000001, + CPU = 0x00000002, + Unknown = 0x00000003, +} + +AddressMode :: enum i32 { + Repeat = 0x00000000, + MirrorRepeat = 0x00000001, + ClampToEdge = 0x00000002, +} + +BackendType :: enum i32 { + Undefined = 0x00000000, + Null = 0x00000001, + WebGPU = 0x00000002, + D3D11 = 0x00000003, + D3D12 = 0x00000004, + Metal = 0x00000005, + Vulkan = 0x00000006, + OpenGL = 0x00000007, + OpenGLES = 0x00000008, +} + +BlendFactor :: enum i32 { + Zero = 0x00000000, + One = 0x00000001, + Src = 0x00000002, + OneMinusSrc = 0x00000003, + SrcAlpha = 0x00000004, + OneMinusSrcAlpha = 0x00000005, + Dst = 0x00000006, + OneMinusDst = 0x00000007, + DstAlpha = 0x00000008, + OneMinusDstAlpha = 0x00000009, + SrcAlphaSaturated = 0x0000000A, + Constant = 0x0000000B, + OneMinusConstant = 0x0000000C, +} + +BlendOperation :: enum i32 { + Add = 0x00000000, + Subtract = 0x00000001, + ReverseSubtract = 0x00000002, + Min = 0x00000003, + Max = 0x00000004, +} + +BufferBindingType :: enum i32 { + Undefined = 0x00000000, + Uniform = 0x00000001, + Storage = 0x00000002, + ReadOnlyStorage = 0x00000003, +} + +BufferMapAsyncStatus :: enum i32 { + Success = 0x00000000, + ValidationError = 0x00000001, + Unknown = 0x00000002, + DeviceLost = 0x00000003, + DestroyedBeforeCallback = 0x00000004, + UnmappedBeforeCallback = 0x00000005, + MappingAlreadyPending = 0x00000006, + OffsetOutOfRange = 0x00000007, + SizeOutOfRange = 0x00000008, +} + +BufferMapState :: enum i32 { + Unmapped = 0x00000000, + Pending = 0x00000001, + Mapped = 0x00000002, +} + +CompareFunction :: enum i32 { + Undefined = 0x00000000, + Never = 0x00000001, + Less = 0x00000002, + LessEqual = 0x00000003, + Greater = 0x00000004, + GreaterEqual = 0x00000005, + Equal = 0x00000006, + NotEqual = 0x00000007, + Always = 0x00000008, +} + +CompilationInfoRequestStatus :: enum i32 { + Success = 0x00000000, + Error = 0x00000001, + DeviceLost = 0x00000002, + Unknown = 0x00000003, +} + +CompilationMessageType :: enum i32 { + Error = 0x00000000, + Warning = 0x00000001, + Info = 0x00000002, +} + +CompositeAlphaMode :: enum i32 { + Auto = 0x00000000, + Opaque = 0x00000001, + Premultiplied = 0x00000002, + Unpremultiplied = 0x00000003, + Inherit = 0x00000004, +} + +CreatePipelineAsyncStatus :: enum i32 { + Success = 0x00000000, + ValidationError = 0x00000001, + InternalError = 0x00000002, + DeviceLost = 0x00000003, + DeviceDestroyed = 0x00000004, + Unknown = 0x00000005, +} + +CullMode :: enum i32 { + None = 0x00000000, + Front = 0x00000001, + Back = 0x00000002, +} + +DeviceLostReason :: enum i32 { + Undefined = 0x00000000, + Destroyed = 0x00000001, +} + +ErrorFilter :: enum i32 { + Validation = 0x00000000, + OutOfMemory = 0x00000001, + Internal = 0x00000002, +} + +ErrorType :: enum i32 { + NoError = 0x00000000, + Validation = 0x00000001, + OutOfMemory = 0x00000002, + Internal = 0x00000003, + Unknown = 0x00000004, + DeviceLost = 0x00000005, +} + +FeatureName :: enum i32 { + // WebGPU. + Undefined = 0x00000000, + DepthClipControl = 0x00000001, + Depth32FloatStencil8 = 0x00000002, + TimestampQuery = 0x00000003, + TextureCompressionBC = 0x00000004, + TextureCompressionETC2 = 0x00000005, + TextureCompressionASTC = 0x00000006, + IndirectFirstInstance = 0x00000007, + ShaderF16 = 0x00000008, + RG11B10UfloatRenderable = 0x00000009, + BGRA8UnormStorage = 0x0000000A, + Float32Filterable = 0x0000000B, + + // Native. + PushConstants = 0x00030001, + TextureAdapterSpecificFormatFeatures, + MultiDrawIndirect, + MultiDrawIndirectCount, + VertexWritableStorage, + TextureBindingArray, + SampledTextureAndStorageBufferArrayNonUniformIndexing, + PipelineStatisticsQuery, + StorageResourceBindingArray, + PartiallyBoundBindingArray, +} + +FilterMode :: enum i32 { + Nearest = 0x00000000, + Linear = 0x00000001, +} + +FrontFace :: enum i32 { + CCW = 0x00000000, + CW = 0x00000001, +} + +IndexFormat :: enum i32 { + Undefined = 0x00000000, + Uint16 = 0x00000001, + Uint32 = 0x00000002, +} + +LoadOp :: enum i32 { + Undefined = 0x00000000, + Clear = 0x00000001, + Load = 0x00000002, +} + +MipmapFilterMode :: enum i32 { + Nearest = 0x00000000, + Linear = 0x00000001, +} + +PowerPreference :: enum i32 { + Undefined = 0x00000000, + LowPower = 0x00000001, + HighPerformance = 0x00000002, +} + +PresentMode :: enum i32 { + Fifo = 0x00000000, + FifoRelaxed = 0x00000001, + Immediate = 0x00000002, + Mailbox = 0x00000003, +} + +PrimitiveTopology :: enum i32 { + PointList = 0x00000000, + LineList = 0x00000001, + LineStrip = 0x00000002, + TriangleList = 0x00000003, + TriangleStrip = 0x00000004, +} + +QueryType :: enum i32 { + // WebGPU. + Occlusion = 0x00000000, + Timestamp = 0x00000001, + + // Native. + PipelineStatistics = 0x00030000, +} + +QueueWorkDoneStatus :: enum i32 { + Success = 0x00000000, + Error = 0x00000001, + Unknown = 0x00000002, + DeviceLost = 0x00000003, +} + +RequestAdapterStatus :: enum i32 { + Success = 0x00000000, + Unavailable = 0x00000001, + Error = 0x00000002, + Unknown = 0x00000003, +} + +RequestDeviceStatus :: enum i32 { + Success = 0x00000000, + Error = 0x00000001, + Unknown = 0x00000002, +} + +SType :: enum i32 { + // WebGPU. + Invalid = 0x00000000, + SurfaceDescriptorFromMetalLayer = 0x00000001, + SurfaceDescriptorFromWindowsHWND = 0x00000002, + SurfaceDescriptorFromXlibWindow = 0x00000003, + SurfaceDescriptorFromCanvasHTMLSelector = 0x00000004, + ShaderModuleSPIRVDescriptor = 0x00000005, + ShaderModuleWGSLDescriptor = 0x00000006, + PrimitiveDepthClipControl = 0x00000007, + SurfaceDescriptorFromWaylandSurface = 0x00000008, + SurfaceDescriptorFromAndroidNativeWindow = 0x00000009, + SurfaceDescriptorFromXcbWindow = 0x0000000A, + RenderPassDescriptorMaxDrawCount = 0x0000000F, + + // Native. + DeviceExtras = 0x00030001, + RequiredLimitsExtras, + PipelineLayoutExtras, + ShaderModuleGLSLDescriptor, + SupportedLimitsExtras, + InstanceExtras, + BindGroupEntryExtras, + BindGroupLayoutEntryExtras, + QuerySetDescriptorExtras, + SurfaceConfigurationExtras, +} + +SamplerBindingType :: enum i32 { + Undefined = 0x00000000, + Filtering = 0x00000001, + NonFiltering = 0x00000002, + Comparison = 0x00000003, +} + +StencilOperation :: enum i32 { + Keep = 0x00000000, + Zero = 0x00000001, + Replace = 0x00000002, + Invert = 0x00000003, + IncrementClamp = 0x00000004, + DecrementClamp = 0x00000005, + IncrementWrap = 0x00000006, + DecrementWrap = 0x00000007, +} + +StorageTextureAccess :: enum i32 { + Undefined = 0x00000000, + WriteOnly = 0x00000001, + ReadOnly = 0x00000002, + ReadWrite = 0x00000003, +} + +StoreOp :: enum i32 { + Undefined = 0x00000000, + Store = 0x00000001, + Discard = 0x00000002, +} + +SurfaceGetCurrentTextureStatus :: enum i32 { + Success = 0x00000000, + Timeout = 0x00000001, + Outdated = 0x00000002, + Lost = 0x00000003, + OutOfMemory = 0x00000004, + DeviceLost = 0x00000005, +} + +TextureAspect :: enum i32 { + All = 0x00000000, + StencilOnly = 0x00000001, + DepthOnly = 0x00000002, +} + +TextureDimension :: enum i32 { + _1D = 0x00000000, + _2D = 0x00000001, + _3D = 0x00000002, +} + +TextureFormat :: enum i32 { + Undefined = 0x00000000, + R8Unorm = 0x00000001, + R8Snorm = 0x00000002, + R8Uint = 0x00000003, + R8Sint = 0x00000004, + R16Uint = 0x00000005, + R16Sint = 0x00000006, + R16Float = 0x00000007, + RG8Unorm = 0x00000008, + RG8Snorm = 0x00000009, + RG8Uint = 0x0000000A, + RG8Sint = 0x0000000B, + R32Float = 0x0000000C, + R32Uint = 0x0000000D, + R32Sint = 0x0000000E, + RG16Uint = 0x0000000F, + RG16Sint = 0x00000010, + RG16Float = 0x00000011, + RGBA8Unorm = 0x00000012, + RGBA8UnormSrgb = 0x00000013, + RGBA8Snorm = 0x00000014, + RGBA8Uint = 0x00000015, + RGBA8Sint = 0x00000016, + BGRA8Unorm = 0x00000017, + BGRA8UnormSrgb = 0x00000018, + RGB10A2Uint = 0x00000019, + RGB10A2Unorm = 0x0000001A, + RG11B10Ufloat = 0x0000001B, + RGB9E5Ufloat = 0x0000001C, + RG32Float = 0x0000001D, + RG32Uint = 0x0000001E, + RG32Sint = 0x0000001F, + RGBA16Uint = 0x00000020, + RGBA16Sint = 0x00000021, + RGBA16Float = 0x00000022, + RGBA32Float = 0x00000023, + RGBA32Uint = 0x00000024, + RGBA32Sint = 0x00000025, + Stencil8 = 0x00000026, + Depth16Unorm = 0x00000027, + Depth24Plus = 0x00000028, + Depth24PlusStencil8 = 0x00000029, + Depth32Float = 0x0000002A, + Depth32FloatStencil8 = 0x0000002B, + BC1RGBAUnorm = 0x0000002C, + BC1RGBAUnormSrgb = 0x0000002D, + BC2RGBAUnorm = 0x0000002E, + BC2RGBAUnormSrgb = 0x0000002F, + BC3RGBAUnorm = 0x00000030, + BC3RGBAUnormSrgb = 0x00000031, + BC4RUnorm = 0x00000032, + BC4RSnorm = 0x00000033, + BC5RGUnorm = 0x00000034, + BC5RGSnorm = 0x00000035, + BC6HRGBUfloat = 0x00000036, + BC6HRGBFloat = 0x00000037, + BC7RGBAUnorm = 0x00000038, + BC7RGBAUnormSrgb = 0x00000039, + ETC2RGB8Unorm = 0x0000003A, + ETC2RGB8UnormSrgb = 0x0000003B, + ETC2RGB8A1Unorm = 0x0000003C, + ETC2RGB8A1UnormSrgb = 0x0000003D, + ETC2RGBA8Unorm = 0x0000003E, + ETC2RGBA8UnormSrgb = 0x0000003F, + EACR11Unorm = 0x00000040, + EACR11Snorm = 0x00000041, + EACRG11Unorm = 0x00000042, + EACRG11Snorm = 0x00000043, + ASTC4x4Unorm = 0x00000044, + ASTC4x4UnormSrgb = 0x00000045, + ASTC5x4Unorm = 0x00000046, + ASTC5x4UnormSrgb = 0x00000047, + ASTC5x5Unorm = 0x00000048, + ASTC5x5UnormSrgb = 0x00000049, + ASTC6x5Unorm = 0x0000004A, + ASTC6x5UnormSrgb = 0x0000004B, + ASTC6x6Unorm = 0x0000004C, + ASTC6x6UnormSrgb = 0x0000004D, + ASTC8x5Unorm = 0x0000004E, + ASTC8x5UnormSrgb = 0x0000004F, + ASTC8x6Unorm = 0x00000050, + ASTC8x6UnormSrgb = 0x00000051, + ASTC8x8Unorm = 0x00000052, + ASTC8x8UnormSrgb = 0x00000053, + ASTC10x5Unorm = 0x00000054, + ASTC10x5UnormSrgb = 0x00000055, + ASTC10x6Unorm = 0x00000056, + ASTC10x6UnormSrgb = 0x00000057, + ASTC10x8Unorm = 0x00000058, + ASTC10x8UnormSrgb = 0x00000059, + ASTC10x10Unorm = 0x0000005A, + ASTC10x10UnormSrgb = 0x0000005B, + ASTC12x10Unorm = 0x0000005C, + ASTC12x10UnormSrgb = 0x0000005D, + ASTC12x12Unorm = 0x0000005E, + ASTC12x12UnormSrgb = 0x0000005F, +} + +TextureSampleType :: enum i32 { + Undefined = 0x00000000, + Float = 0x00000001, + UnfilterableFloat = 0x00000002, + Depth = 0x00000003, + Sint = 0x00000004, + Uint = 0x00000005, +} + +TextureViewDimension :: enum i32 { + Undefined = 0x00000000, + _1D = 0x00000001, + _2D = 0x00000002, + _2DArray = 0x00000003, + Cube = 0x00000004, + CubeArray = 0x00000005, + _3D = 0x00000006, +} + +VertexFormat :: enum i32 { + Undefined = 0x00000000, + Uint8x2 = 0x00000001, + Uint8x4 = 0x00000002, + Sint8x2 = 0x00000003, + Sint8x4 = 0x00000004, + Unorm8x2 = 0x00000005, + Unorm8x4 = 0x00000006, + Snorm8x2 = 0x00000007, + Snorm8x4 = 0x00000008, + Uint16x2 = 0x00000009, + Uint16x4 = 0x0000000A, + Sint16x2 = 0x0000000B, + Sint16x4 = 0x0000000C, + Unorm16x2 = 0x0000000D, + Unorm16x4 = 0x0000000E, + Snorm16x2 = 0x0000000F, + Snorm16x4 = 0x00000010, + Float16x2 = 0x00000011, + Float16x4 = 0x00000012, + Float32 = 0x00000013, + Float32x2 = 0x00000014, + Float32x3 = 0x00000015, + Float32x4 = 0x00000016, + Uint32 = 0x00000017, + Uint32x2 = 0x00000018, + Uint32x3 = 0x00000019, + Uint32x4 = 0x0000001A, + Sint32 = 0x0000001B, + Sint32x2 = 0x0000001C, + Sint32x3 = 0x0000001D, + Sint32x4 = 0x0000001E, +} + +VertexStepMode :: enum i32 { + Vertex = 0x00000000, + Instance = 0x00000001, + VertexBufferNotUsed = 0x00000002, +} + +// WGSLFeatureName :: enum i32 { +// Undefined = 0x00000000, +// ReadonlyAndReadwriteStorageTextures = 0x00000001, +// Packed4x8IntegerDotProduct = 0x00000002, +// UnrestrictedPointerParameters = 0x00000003, +// PointerCompositeAccess = 0x00000004, +// } + +BufferUsage :: enum i32 { + MapRead = 0x00000000, + MapWrite = 0x00000001, + CopySrc = 0x00000002, + CopyDst = 0x00000003, + Index = 0x00000004, + Vertex = 0x00000005, + Uniform = 0x00000006, + Storage = 0x00000007, + Indirect = 0x00000008, + QueryResolve = 0x00000009, +} +BufferUsageFlags :: bit_set[BufferUsage; Flags] + +ColorWriteMask :: enum i32 { + Red = 0x00000000, + Green = 0x00000001, + Blue = 0x00000002, + Alpha = 0x00000003, +} +ColorWriteMaskFlags :: bit_set[ColorWriteMask; Flags] +ColorWriteMaskFlags_All :: ColorWriteMaskFlags{ .Red, .Green, .Blue, .Alpha } + +MapMode :: enum i32 { + Read = 0x00000000, + Write = 0x00000001, +} +MapModeFlags :: bit_set[MapMode; Flags] + +ShaderStage :: enum i32 { + Vertex = 0x00000000, + Fragment = 0x00000001, + Compute = 0x00000002, +} +ShaderStageFlags :: bit_set[ShaderStage; Flags] + +TextureUsage :: enum i32 { + CopySrc = 0x00000000, + CopyDst = 0x00000001, + TextureBinding = 0x00000002, + StorageBinding = 0x00000003, + RenderAttachment = 0x00000004, +} +TextureUsageFlags :: bit_set[TextureUsage; Flags] + + +BufferMapAsyncCallback :: #type proc "c" (status: BufferMapAsyncStatus, /* NULLABLE */ userdata: rawptr) +ShaderModuleGetCompilationInfoCallback :: #type proc "c" (status: CompilationInfoRequestStatus, compilationInfo: ^CompilationInfo, /* NULLABLE */ userdata: rawptr) +DeviceCreateComputePipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: ComputePipeline, message: cstring, /* NULLABLE */ userdata: rawptr) +DeviceCreateRenderPipelineAsyncCallback :: #type proc "c" (status: CreatePipelineAsyncStatus, pipeline: RenderPipeline, message: cstring, /* NULLABLE */ userdata: rawptr) + +DeviceLostCallback :: #type proc "c" (reason: DeviceLostReason, message: cstring, userdata: rawptr) +ErrorCallback :: #type proc "c" (type: ErrorType, message: cstring, userdata: rawptr) + +Proc :: distinct rawptr + +QueueOnSubmittedWorkDoneCallback :: #type proc "c" (status: QueueWorkDoneStatus, /* NULLABLE */ userdata: rawptr) +InstanceRequestAdapterCallback :: #type proc "c" (status: RequestAdapterStatus, adapter: Adapter, message: cstring, /* NULLABLE */ userdata: rawptr) +AdapterRequestDeviceCallback :: #type proc "c" (status: RequestDeviceStatus, device: Device, message: cstring, /* NULLABLE */ userdata: rawptr) + +// AdapterRequestAdapterInfoCallback :: #type proc "c" (adapterInfo: AdapterInfo, /* NULLABLE */ userdata: rawptr) + +ChainedStruct :: struct { + next: ^ChainedStruct, + sType: SType, +} + +ChainedStructOut :: struct { + next: ^ChainedStructOut, + sType: SType, +} + +// AdapterInfo :: struct { +// next: ^ChainedStructOut, +// vendor: cstring, +// architecture: cstring, +// device: cstring, +// description: cstring, +// backendType: BackendType, +// adapterType: AdapterType, +// vendorID: u32, +// deviceID: u32, +// } + +AdapterProperties :: struct { + nextInChain: ^ChainedStructOut, + vendorID: u32, + vendorName: cstring, + architecture: cstring, + deviceID: u32, + name: cstring, + driverDescription: cstring, + adapterType: AdapterType, + backendType: BackendType, +} + +BindGroupEntry :: struct { + nextInChain: ^ChainedStruct, + binding: u32, + /* NULLABLE */ buffer: Buffer, + offset: u64, + size: u64, + /* NULLABLE */ sampler: Sampler, + /* NULLABLE */ textureView: TextureView, +} + +BlendComponent :: struct { + operation: BlendOperation, + srcFactor: BlendFactor, + dstFactor: BlendFactor, +} + +BufferBindingLayout :: struct { + nextInChain: ^ChainedStruct, + type: BufferBindingType, + hasDynamicOffset: b32, + minBindingSize: u64, +} + +BufferDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + usage: BufferUsageFlags, + size: u64, + mappedAtCreation: b32, +} + +Color :: struct { + r: f64, + g: f64, + b: f64, + a: f64, +} + +CommandBufferDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, +} + +CommandEncoderDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, +} + +CompilationMessage :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ message: cstring, + type: CompilationMessageType, + lineNum: u64, + linePos: u64, + offset: u64, + length: u64, + utf16LinePos: u64, + utf16Offset: u64, + utf16Length: u64, +} + +ComputePassTimestampWrites :: struct { + querySet: QuerySet, + beginningOfPassWriteIndex: u32, + endOfPassWriteIndex: u32, +} + +ConstantEntry :: struct { + nextInChain: ^ChainedStruct, + key: cstring, + value: f64, +} + +Extent3D :: struct { + width: u32, + height: u32, + depthOrArrayLayers: u32, +} + +InstanceDescriptor :: struct { + nextInChain: ^ChainedStruct, +} + +Limits :: struct { + maxTextureDimension1D: u32, + maxTextureDimension2D: u32, + maxTextureDimension3D: u32, + maxTextureArrayLayers: u32, + maxBindGroups: u32, + maxBindGroupsPlusVertexBuffers: u32, + maxBindingsPerBindGroup: u32, + maxDynamicUniformBuffersPerPipelineLayout: u32, + maxDynamicStorageBuffersPerPipelineLayout: u32, + maxSampledTexturesPerShaderStage: u32, + maxSamplersPerShaderStage: u32, + maxStorageBuffersPerShaderStage: u32, + maxStorageTexturesPerShaderStage: u32, + maxUniformBuffersPerShaderStage: u32, + maxUniformBufferBindingSize: u64, + maxStorageBufferBindingSize: u64, + minUniformBufferOffsetAlignment: u32, + minStorageBufferOffsetAlignment: u32, + maxVertexBuffers: u32, + maxBufferSize: u64, + maxVertexAttributes: u32, + maxVertexBufferArrayStride: u32, + maxInterStageShaderComponents: u32, + maxInterStageShaderVariables: u32, + maxColorAttachments: u32, + maxColorAttachmentBytesPerSample: u32, + maxComputeWorkgroupStorageSize: u32, + maxComputeInvocationsPerWorkgroup: u32, + maxComputeWorkgroupSizeX: u32, + maxComputeWorkgroupSizeY: u32, + maxComputeWorkgroupSizeZ: u32, + maxComputeWorkgroupsPerDimension: u32, +} + +MultisampleState :: struct { + nextInChain: ^ChainedStruct, + count: u32, + mask: u32, + alphaToCoverageEnabled: b32, +} + +Origin3D :: struct { + x: u32, + y: u32, + z: u32, +} + +PipelineLayoutDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + bindGroupLayoutCount: uint, + bindGroupLayouts: [^]BindGroupLayout `fmt:"v,bindGroupLayoutCount"`, +} + +PrimitiveDepthClipControl :: struct { + using chain: ChainedStruct, + unclippedDepth: b32, +} + +PrimitiveState :: struct { + nextInChain: ^ChainedStruct, + topology: PrimitiveTopology, + stripIndexFormat: IndexFormat, + frontFace: FrontFace, + cullMode: CullMode, +} + +QuerySetDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + type: QueryType, + count: u32, +} + +QueueDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, +} + +RenderBundleDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, +} + +RenderBundleEncoderDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + colorFormatCount: uint, + colorFormats: [^]TextureFormat `fmt:"v,colorFormatCount"`, + depthStencilFormat: TextureFormat, + sampleCount: u32, + depthReadOnly: b32, + stencilReadOnly: b32, +} + +RenderPassDepthStencilAttachment :: struct { + view: TextureView, + depthLoadOp: LoadOp, + depthStoreOp: StoreOp, + depthClearValue: f32, + depthReadOnly: b32, + stencilLoadOp: LoadOp, + stencilStoreOp: StoreOp, + stencilClearValue: u32, + stencilReadOnly: b32, +} + +RenderPassDescriptorMaxDrawCount :: struct { + using chain: ChainedStruct, + maxDrawCount: u64, +} + +RenderPassTimestampWrites :: struct { + querySet: QuerySet, + beginningOfPassWriteIndex: u32, + endOfPassWriteIndex: u32, +} + +RequestAdapterOptions :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ compatibleSurface: Surface, + powerPreference: PowerPreference, + backendType: BackendType, + forceFallbackAdapter: b32, +} + +SamplerBindingLayout :: struct { + nextInChain: ^ChainedStruct, + type: SamplerBindingType, +} + +SamplerDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + addressModeU: AddressMode, + addressModeV: AddressMode, + addressModeW: AddressMode, + magFilter: FilterMode, + minFilter: FilterMode, + mipmapFilter: MipmapFilterMode, + lodMinClamp: f32, + lodMaxClamp: f32, + compare: CompareFunction, + maxAnisotropy: u16, +} + +ShaderModuleCompilationHint :: struct { + nextInChain: ^ChainedStruct, + entryPoint: cstring, + layout: PipelineLayout, +} + +ShaderModuleSPIRVDescriptor :: struct { + using chain: ChainedStruct, + codeSize: u32, + code: /* const */ [^]u32 `fmt:"v,codeSize"`, +} + +ShaderModuleWGSLDescriptor :: struct { + using chain: ChainedStruct, + code: cstring, +} + +StencilFaceState :: struct { + compare: CompareFunction, + failOp: StencilOperation, + depthFailOp: StencilOperation, + passOp: StencilOperation, +} + +StorageTextureBindingLayout :: struct { + nextInChain: ^ChainedStruct, + access: StorageTextureAccess, + format: TextureFormat, + viewDimension: TextureViewDimension, +} + +SurfaceCapabilities :: struct { + nextInChain: ^ChainedStructOut, + formatCount: uint, + formats: /* const */ [^]TextureFormat `fmt:"v,formatCount"`, + presentModeCount: uint, + presentModes: /* const */ [^]PresentMode `fmt:"v,presentModeCount"`, + alphaModeCount: uint, + alphaModes: /* const */ [^]CompositeAlphaMode `fmt:"v,alphaModeCount"`, +} + +SurfaceConfiguration :: struct { + nextInChain: ^ChainedStruct, + device: Device, + format: TextureFormat, + usage: TextureUsageFlags, + viewFormatCount: uint, + viewFormats: [^]TextureFormat `fmt:"v,viewFormatCount"`, + alphaMode: CompositeAlphaMode, + width: u32, + height: u32, + presentMode: PresentMode, +} + +SurfaceDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, +} + +SurfaceDescriptorFromAndroidNativeWindow :: struct { + using chain: ChainedStruct, + window: rawptr, +} + +SurfaceDescriptorFromCanvasHTMLSelector :: struct { + using chain: ChainedStruct, + selector: cstring, +} + +SurfaceDescriptorFromMetalLayer :: struct { + using chain: ChainedStruct, + layer: rawptr, +} + +SurfaceDescriptorFromWaylandSurface :: struct { + using chain: ChainedStruct, + display: rawptr, + surface: rawptr, +} + +SurfaceDescriptorFromWindowsHWND :: struct { + using chain: ChainedStruct, + hinstance: rawptr, + hwnd: rawptr, +} + +SurfaceDescriptorFromXcbWindow :: struct { + using chain: ChainedStruct, + connection: rawptr, + window: u32, +} + +SurfaceDescriptorFromXlibWindow :: struct { + using chain: ChainedStruct, + display: rawptr, + window: u64, +} + +SurfaceTexture :: struct { + texture: Texture, + suboptimal: b32, + status: SurfaceGetCurrentTextureStatus, +} + +TextureBindingLayout :: struct { + nextInChain: ^ChainedStruct, + sampleType: TextureSampleType, + viewDimension: TextureViewDimension, + multisampled: b32, +} + +TextureDataLayout :: struct { + nextInChain: ^ChainedStruct, + offset: u64, + bytesPerRow: u32, + rowsPerImage: u32, +} + +TextureViewDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + format: TextureFormat, + dimension: TextureViewDimension, + baseMipLevel: u32, + mipLevelCount: u32, + baseArrayLayer: u32, + arrayLayerCount: u32, + aspect: TextureAspect, +} + +VertexAttribute :: struct { + format: VertexFormat, + offset: u64, + shaderLocation: u32, +} + +BindGroupDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + layout: BindGroupLayout, + entryCount: uint, + entries: [^]BindGroupEntry `fmt:"v,entryCount"`, +} + +BindGroupLayoutEntry :: struct { + nextInChain: ^ChainedStruct, + binding: u32, + visibility: ShaderStageFlags, + buffer: BufferBindingLayout, + sampler: SamplerBindingLayout, + texture: TextureBindingLayout, + storageTexture: StorageTextureBindingLayout, +} + +BlendState :: struct { + color: BlendComponent, + alpha: BlendComponent, +} + +CompilationInfo :: struct { + nextInChain: ^ChainedStruct, + messageCount: uint, + messages: [^]CompilationMessage `fmt:"v,messageCount"`, +} + +ComputePassDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + /* NULLABLE */ timestampWrites: /* const */ ^ComputePassTimestampWrites, +} + +DepthStencilState :: struct { + nextInChain: ^ChainedStruct, + format: TextureFormat, + depthWriteEnabled: b32, + depthCompare: CompareFunction, + stencilFront: StencilFaceState, + stencilBack: StencilFaceState, + stencilReadMask: u32, + stencilWriteMask: u32, + depthBias: i32, + depthBiasSlopeScale: f32, + depthBiasClamp: f32, +} + +ImageCopyBuffer :: struct { + nextInChain: ^ChainedStruct, + layout: TextureDataLayout, + buffer: Buffer, +} + +ImageCopyTexture :: struct { + nextInChain: ^ChainedStruct, + texture: Texture, + mipLevel: u32, + origin: Origin3D, + aspect: TextureAspect, +} + +ProgrammableStageDescriptor :: struct { + nextInChain: ^ChainedStruct, + module: ShaderModule, + /* NULLABLE */ entryPoint: cstring, + constantCount: uint, + constants: [^]ConstantEntry `fmt:"v,constantCount"`, +} + +RenderPassColorAttachment :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ view: TextureView, + // depthSlice: u32, + /* NULLABLE */ resolveTarget: TextureView, + loadOp: LoadOp, + storeOp: StoreOp, + clearValue: Color, +} + +RequiredLimits :: struct { + nextInChain: ^ChainedStruct, + limits: Limits, +} + +ShaderModuleDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + hintCount: uint, + hints: [^]ShaderModuleCompilationHint `fmt:"v,hintCount"`, +} + +SupportedLimits :: struct { + nextInChain: ^ChainedStructOut, + limits: Limits, +} + +TextureDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + usage: TextureUsageFlags, + dimension: TextureDimension, + size: Extent3D, + format: TextureFormat, + mipLevelCount: u32, + sampleCount: u32, + viewFormatCount: uint, + viewFormats: [^]TextureFormat `fmt:"v,viewFormatCount"`, +} + +VertexBufferLayout :: struct { + arrayStride: u64, + stepMode: VertexStepMode, + attributeCount: uint, + attributes: [^]VertexAttribute `fmt:"v,attributeCount"`, +} + +BindGroupLayoutDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + entryCount: uint, + entries: [^]BindGroupLayoutEntry `fmt:"v,entryCount"`, +} + +ColorTargetState :: struct { + nextInChain: ^ChainedStruct, + format: TextureFormat, + /* NULLABLE */ blend: /* const */ ^BlendState, + writeMask: ColorWriteMaskFlags, +} + +ComputePipelineDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + /* NULLABLE */ layout: PipelineLayout, + compute: ProgrammableStageDescriptor, +} + +DeviceDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + requiredFeatureCount: uint, + requiredFeatures: [^]FeatureName `fmt:"v,requiredFeatureCount"`, + /* NULLABLE */ requiredLimits: /* const */ ^RequiredLimits, + defaultQueue: QueueDescriptor, + deviceLostCallback: DeviceLostCallback, + deviceLostUserdata: rawptr, +} + +RenderPassDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + colorAttachmentCount: uint, + colorAttachments: [^]RenderPassColorAttachment `fmt:"v,colorAttachmentCount"`, + /* NULLABLE */ depthStencilAttachment: /* const */ ^RenderPassDepthStencilAttachment, + /* NULLABLE */ occlusionQuerySet: QuerySet, + /* NULLABLE */ timestampWrites: /* const */ ^RenderPassTimestampWrites, +} + +VertexState :: struct { + nextInChain: ^ChainedStruct, + module: ShaderModule, + /* NULLABLE */ entryPoint: cstring, + constantCount: uint, + constants: [^]ConstantEntry `fmt:"v,constantCount"`, + bufferCount: uint, + buffers: [^]VertexBufferLayout `fmt:"v,bufferCount"`, +} + +FragmentState :: struct { + nextInChain: ^ChainedStruct, + module: ShaderModule, + /* NULLABLE */ entryPoint: cstring, + constantCount: uint, + constants: [^]ConstantEntry `fmt:"v,constantCount"`, + targetCount: uint, + targets: [^]ColorTargetState `fmt:"v,targetCount"`, +} + +RenderPipelineDescriptor :: struct { + nextInChain: ^ChainedStruct, + /* NULLABLE */ label: cstring, + /* NULLABLE */ layout: PipelineLayout, + vertex: VertexState, + primitive: PrimitiveState, + /* NULLABLE */ depthStencil: /* const */ ^DepthStencilState, + multisample: MultisampleState, + /* NULLABLE */ fragment: /* const */ ^FragmentState, +} + +@(link_prefix="wgpu", default_calling_convention="c") +foreign libwgpu { + CreateInstance :: proc(/* NULLABLE */ descriptor: /* const */ ^InstanceDescriptor = nil) -> Instance --- + GetProcAddress :: proc(device: Device, procName: cstring) -> Proc --- + + // Methods of Adapter + @(link_name="wgpuAdapterEnumerateFeatures") + RawAdapterEnumerateFeatures :: proc(adapter: Adapter, features: [^]FeatureName) -> uint --- + @(link_name="wgpuAdapterGetLimits") + RawAdapterGetLimits :: proc(adapter: Adapter, limits: ^SupportedLimits) -> b32 --- + @(link_name="wgpuAdapterGetProperties") + RawAdapterGetProperties :: proc(adapter: Adapter, properties: ^AdapterProperties) --- + AdapterHasFeature :: proc(adapter: Adapter, feature: FeatureName) -> b32 --- + // AdapterRequestAdapterInfo :: proc(adapter: Adapter, callback: AdapterRequestAdapterInfoCallback, /* NULLABLE */ userdata: rawptr) --- + AdapterRequestDevice :: proc(adapter: Adapter, /* NULLABLE */ descriptor: /* const */ ^DeviceDescriptor, callback: AdapterRequestDeviceCallback, /* NULLABLE */ userdata: rawptr = nil) --- + AdapterReference :: proc(adapter: Adapter) --- + AdapterRelease :: proc(adapter: Adapter) --- + + // Methods of BindGroup + BindGroupSetLabel :: proc(bindGroup: BindGroup, label: cstring) --- + BindGroupReference :: proc(bindGroup: BindGroup) --- + BindGroupRelease :: proc(bindGroup: BindGroup) --- + + // Methods of BindGroupLayout + BindGroupLayoutSetLabel :: proc(bindGroupLayout: BindGroupLayout, label: cstring) --- + BindGroupLayoutReference :: proc(bindGroupLayout: BindGroupLayout) --- + BindGroupLayoutRelease :: proc(bindGroupLayout: BindGroupLayout) --- + + // Methods of Buffer + BufferDestroy :: proc(buffer: Buffer) --- + @(link_name="wgpuBufferGetConstMappedRange") + RawBufferGetConstMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> /* const */ rawptr --- + BufferGetMapState :: proc(buffer: Buffer) -> BufferMapState --- + @(link_name="wgpuBufferGetMappedRange") + RawBufferGetMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> rawptr --- + BufferGetSize :: proc(buffer: Buffer) -> u64 --- + BufferGetUsage :: proc(buffer: Buffer) -> BufferUsageFlags --- + BufferMapAsync :: proc(buffer: Buffer, mode: MapModeFlags, offset: uint, size: uint, callback: BufferMapAsyncCallback, /* NULLABLE */ userdata: rawptr = nil) --- + BufferSetLabel :: proc(buffer: Buffer, label: cstring) --- + BufferUnmap :: proc(buffer: Buffer) --- + BufferReference :: proc(buffer: Buffer) --- + BufferRelease :: proc(buffer: Buffer) --- + + // Methods of CommandBuffer + CommandBufferSetLabel :: proc(commandBuffer: CommandBuffer, label: cstring) --- + CommandBufferReference :: proc(commandBuffer: CommandBuffer) --- + CommandBufferRelease :: proc(commandBuffer: CommandBuffer) --- + + // Methods of CommandEncoder + CommandEncoderBeginComputePass :: proc(commandEncoder: CommandEncoder, /* NULLABLE */ descriptor: /* const */ ^ComputePassDescriptor = nil) -> ComputePassEncoder --- + CommandEncoderBeginRenderPass :: proc(commandEncoder: CommandEncoder, descriptor: /* const */ ^RenderPassDescriptor) -> RenderPassEncoder --- + CommandEncoderClearBuffer :: proc(commandEncoder: CommandEncoder, buffer: Buffer, offset: u64, size: u64) --- + CommandEncoderCopyBufferToBuffer :: proc(commandEncoder: CommandEncoder, source: Buffer, sourceOffset: u64, destination: Buffer, destinationOffset: u64, size: u64) --- + CommandEncoderCopyBufferToTexture :: proc(commandEncoder: CommandEncoder, source: /* const */ ^ImageCopyBuffer, destination: /* const */ ^ImageCopyTexture, copySize: /* const */ ^Extent3D) --- + CommandEncoderCopyTextureToBuffer :: proc(commandEncoder: CommandEncoder, source: /* const */ ^ImageCopyTexture, destination: /* const */ ^ImageCopyBuffer, copySize: /* const */ ^Extent3D) --- + CommandEncoderCopyTextureToTexture :: proc(commandEncoder: CommandEncoder, source: /* const */ ^ImageCopyTexture, destination: /* const */ ^ImageCopyTexture, copySize: /* const */ ^Extent3D) --- + CommandEncoderFinish :: proc(commandEncoder: CommandEncoder, /* NULLABLE */ descriptor: /* const */ ^CommandBufferDescriptor = nil) -> CommandBuffer --- + CommandEncoderInsertDebugMarker :: proc(commandEncoder: CommandEncoder, markerLabel: cstring) --- + CommandEncoderPopDebugGroup :: proc(commandEncoder: CommandEncoder) --- + CommandEncoderPushDebugGroup :: proc(commandEncoder: CommandEncoder, groupLabel: cstring) --- + CommandEncoderResolveQuerySet :: proc(commandEncoder: CommandEncoder, querySet: QuerySet, firstQuery: u32, queryCount: u32, destination: Buffer, destinationOffset: u64) --- + CommandEncoderSetLabel :: proc(commandEncoder: CommandEncoder, label: cstring) --- + CommandEncoderWriteTimestamp :: proc(commandEncoder: CommandEncoder, querySet: QuerySet, queryIndex: u32) --- + CommandEncoderReference :: proc(commandEncoder: CommandEncoder) --- + CommandEncoderRelease :: proc(commandEncoder: CommandEncoder) --- + + // Methods of ComputePassEncoder + ComputePassEncoderDispatchWorkgroups :: proc(computePassEncoder: ComputePassEncoder, workgroupCountX: u32, workgroupCountY: u32, workgroupCountZ: u32) --- + ComputePassEncoderDispatchWorkgroupsIndirect :: proc(computePassEncoder: ComputePassEncoder, indirectBuffer: Buffer, indirectOffset: u64) --- + ComputePassEncoderEnd :: proc(computePassEncoder: ComputePassEncoder) --- + ComputePassEncoderInsertDebugMarker :: proc(computePassEncoder: ComputePassEncoder, markerLabel: cstring) --- + ComputePassEncoderPopDebugGroup :: proc(computePassEncoder: ComputePassEncoder) --- + ComputePassEncoderPushDebugGroup :: proc(computePassEncoder: ComputePassEncoder, groupLabel: cstring) --- + @(link_name="wgpuComputePassEncoderSetBindGroup") + RawComputePassEncoderSetBindGroup :: proc(computePassEncoder: ComputePassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsetCount: uint, dynamicOffsets: [^]u32) --- + ComputePassEncoderSetLabel :: proc(computePassEncoder: ComputePassEncoder, label: cstring) --- + ComputePassEncoderSetPipeline :: proc(computePassEncoder: ComputePassEncoder, pipeline: ComputePipeline) --- + ComputePassEncoderReference :: proc(computePassEncoder: ComputePassEncoder) --- + ComputePassEncoderRelease :: proc(computePassEncoder: ComputePassEncoder) --- + + // Methods of ComputePipeline + ComputePipelineGetBindGroupLayout :: proc(computePipeline: ComputePipeline, groupIndex: u32) -> BindGroupLayout --- + ComputePipelineSetLabel :: proc(computePipeline: ComputePipeline, label: cstring) --- + ComputePipelineReference :: proc(computePipeline: ComputePipeline) --- + ComputePipelineRelease :: proc(computePipeline: ComputePipeline) --- + + // Methods of Device + DeviceCreateBindGroup :: proc(device: Device, descriptor: /* const */ ^BindGroupDescriptor) -> BindGroup --- + DeviceCreateBindGroupLayout :: proc(device: Device, descriptor: /* const */ ^BindGroupLayoutDescriptor) -> BindGroupLayout --- + DeviceCreateBuffer :: proc(device: Device, descriptor: /* const */ ^BufferDescriptor) -> Buffer --- + DeviceCreateCommandEncoder :: proc(device: Device, /* NULLABLE */ descriptor: /* const */ ^CommandEncoderDescriptor = nil) -> CommandEncoder --- + DeviceCreateComputePipeline :: proc(device: Device, descriptor: /* const */ ^ComputePipelineDescriptor) -> ComputePipeline --- + DeviceCreateComputePipelineAsync :: proc(device: Device, descriptor: /* const */ ^ComputePipelineDescriptor, callback: DeviceCreateComputePipelineAsyncCallback, /* NULLABLE */ userdata: rawptr = nil) --- + DeviceCreatePipelineLayout :: proc(device: Device, descriptor: /* const */ ^PipelineLayoutDescriptor) -> PipelineLayout --- + DeviceCreateQuerySet :: proc(device: Device, descriptor: /* const */ ^QuerySetDescriptor) -> QuerySet --- + DeviceCreateRenderBundleEncoder :: proc(device: Device, descriptor: /* const */ ^RenderBundleEncoderDescriptor) -> RenderBundleEncoder --- + DeviceCreateRenderPipeline :: proc(device: Device, descriptor: /* const */ ^RenderPipelineDescriptor) -> RenderPipeline --- + DeviceCreateRenderPipelineAsync :: proc(device: Device, descriptor: /* const */ ^RenderPipelineDescriptor, callback: DeviceCreateRenderPipelineAsyncCallback, /* NULLABLE */ userdata: rawptr = nil) --- + DeviceCreateSampler :: proc(device: Device, /* NULLABLE */ descriptor: /* const */ ^SamplerDescriptor = nil) -> Sampler --- + DeviceCreateShaderModule :: proc(device: Device, descriptor: /* const */ ^ShaderModuleDescriptor) -> ShaderModule --- + DeviceCreateTexture :: proc(device: Device, descriptor: /* const */ ^TextureDescriptor) -> Texture --- + DeviceDestroy :: proc(device: Device) --- + @(link_name="wgpuDeviceEnumerateFeatures") + RawDeviceEnumerateFeatures :: proc(device: Device, features: ^FeatureName) -> uint --- + @(link_name="wgpuDeviceGetLimits") + RawDeviceGetLimits :: proc(device: Device, limits: ^SupportedLimits) -> b32 --- + DeviceGetQueue :: proc(device: Device) -> Queue --- + DeviceHasFeature :: proc(device: Device, feature: FeatureName) -> b32 --- + DevicePopErrorScope :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) --- + DevicePushErrorScope :: proc(device: Device, filter: ErrorFilter) --- + DeviceSetLabel :: proc(device: Device, label: cstring) --- + DeviceSetUncapturedErrorCallback :: proc(device: Device, callback: ErrorCallback, userdata: rawptr) --- + DeviceReference :: proc(device: Device) --- + DeviceRelease :: proc(device: Device) --- + + // Methods of Instance + InstanceCreateSurface :: proc(instance: Instance, descriptor: /* const */ ^SurfaceDescriptor) -> Surface --- + // InstanceHasWGSLLanguageFeature :: proc(instance: Instance, feature: WGSLFeatureName) -> b32 --- + InstanceProcessEvents :: proc(instance: Instance) --- + InstanceRequestAdapter :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^RequestAdapterOptions, callback: InstanceRequestAdapterCallback, /* NULLABLE */ userdata: rawptr = nil) --- + InstanceReference :: proc(instance: Instance) --- + InstanceRelease :: proc(instance: Instance) --- + + // Methods of PipelineLayout + PipelineLayoutSetLabel :: proc(pipelineLayout: PipelineLayout, label: cstring) --- + PipelineLayoutReference :: proc(pipelineLayout: PipelineLayout) --- + PipelineLayoutRelease :: proc(pipelineLayout: PipelineLayout) --- + + // Methods of QuerySet + QuerySetDestroy :: proc(querySet: QuerySet) --- + QuerySetGetCount :: proc(querySet: QuerySet) -> u32 --- + QuerySetGetType :: proc(querySet: QuerySet) -> QueryType --- + QuerySetSetLabel :: proc(querySet: QuerySet, label: cstring) --- + QuerySetReference :: proc(querySet: QuerySet) --- + QuerySetRelease :: proc(querySet: QuerySet) --- + + // Methods of Queue + QueueOnSubmittedWorkDone :: proc(queue: Queue, callback: QueueOnSubmittedWorkDoneCallback, /* NULLABLE */ userdata: rawptr = nil) --- + QueueSetLabel :: proc(queue: Queue, label: cstring) --- + @(link_name="wgpuQueueSubmit") + RawQueueSubmit :: proc(queue: Queue, commandCount: uint, commands: [^]CommandBuffer) --- + QueueWriteBuffer :: proc(queue: Queue, buffer: Buffer, bufferOffset: u64, data: /* const */ rawptr, size: uint) --- + QueueWriteTexture :: proc(queue: Queue, destination: /* const */ ^ImageCopyTexture, data: /* const */ rawptr, dataSize: uint, dataLayout: /* const */ ^TextureDataLayout, writeSize: /* const */ ^Extent3D) --- + QueueReference :: proc(queue: Queue) --- + QueueRelease :: proc(queue: Queue) --- + + // Methods of RenderBundle + RenderBundleSetLabel :: proc(renderBundle: RenderBundle, label: cstring) --- + RenderBundleReference :: proc(renderBundle: RenderBundle) --- + RenderBundleRelease :: proc(renderBundle: RenderBundle) --- + + // Methods of RenderBundleEncoder + RenderBundleEncoderDraw :: proc(renderBundleEncoder: RenderBundleEncoder, vertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32) --- + RenderBundleEncoderDrawIndexed :: proc(renderBundleEncoder: RenderBundleEncoder, indexCount: u32, instanceCount: u32, firstIndex: u32, baseVertex: i32, firstInstance: u32) --- + RenderBundleEncoderDrawIndexedIndirect :: proc(renderBundleEncoder: RenderBundleEncoder, indirectBuffer: Buffer, indirectOffset: u64) --- + RenderBundleEncoderDrawIndirect :: proc(renderBundleEncoder: RenderBundleEncoder, indirectBuffer: Buffer, indirectOffset: u64) --- + RenderBundleEncoderFinish :: proc(renderBundleEncoder: RenderBundleEncoder, /* NULLABLE */ descriptor: /* const */ ^RenderBundleDescriptor = nil) -> RenderBundle --- + RenderBundleEncoderInsertDebugMarker :: proc(renderBundleEncoder: RenderBundleEncoder, markerLabel: cstring) --- + RenderBundleEncoderPopDebugGroup :: proc(renderBundleEncoder: RenderBundleEncoder) --- + RenderBundleEncoderPushDebugGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupLabel: cstring) --- + @(link_name="wgpuRenderBundleEncoderSetBindGroup") + RawRenderBundleEncoderSetBindGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsetCount: uint, dynamicOffsets: [^]u32) --- + RenderBundleEncoderSetIndexBuffer :: proc(renderBundleEncoder: RenderBundleEncoder, buffer: Buffer, format: IndexFormat, offset: u64, size: u64) --- + RenderBundleEncoderSetLabel :: proc(renderBundleEncoder: RenderBundleEncoder, label: cstring) --- + RenderBundleEncoderSetPipeline :: proc(renderBundleEncoder: RenderBundleEncoder, pipeline: RenderPipeline) --- + RenderBundleEncoderSetVertexBuffer :: proc(renderBundleEncoder: RenderBundleEncoder, slot: u32, /* NULLABLE */ buffer: Buffer, offset: u64, size: u64) --- + RenderBundleEncoderReference :: proc(renderBundleEncoder: RenderBundleEncoder) --- + RenderBundleEncoderRelease :: proc(renderBundleEncoder: RenderBundleEncoder) --- + + // Methods of RenderPassEncoder + RenderPassEncoderBeginOcclusionQuery :: proc(renderPassEncoder: RenderPassEncoder, queryIndex: u32) --- + RenderPassEncoderDraw :: proc(renderPassEncoder: RenderPassEncoder, vertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32) --- + RenderPassEncoderDrawIndexed :: proc(renderPassEncoder: RenderPassEncoder, indexCount: u32, instanceCount: u32, firstIndex: u32, baseVertex: i32, firstInstance: u32) --- + RenderPassEncoderDrawIndexedIndirect :: proc(renderPassEncoder: RenderPassEncoder, indirectBuffer: Buffer, indirectOffset: u64) --- + RenderPassEncoderDrawIndirect :: proc(renderPassEncoder: RenderPassEncoder, indirectBuffer: Buffer, indirectOffset: u64) --- + RenderPassEncoderEnd :: proc(renderPassEncoder: RenderPassEncoder) --- + RenderPassEncoderEndOcclusionQuery :: proc(renderPassEncoder: RenderPassEncoder) --- + @(link_name="wgpuRenderPassEncoderExecuteBundles") + RawRenderPassEncoderExecuteBundles :: proc(renderPassEncoder: RenderPassEncoder, bundleCount: uint, bundles: [^]RenderBundle) --- + RenderPassEncoderInsertDebugMarker :: proc(renderPassEncoder: RenderPassEncoder, markerLabel: cstring) --- + RenderPassEncoderPopDebugGroup :: proc(renderPassEncoder: RenderPassEncoder) --- + RenderPassEncoderPushDebugGroup :: proc(renderPassEncoder: RenderPassEncoder, groupLabel: cstring) --- + @(link_name="wgpuRenderPassEncoderSetBindGroup") + RawRenderPassEncoderSetBindGroup :: proc(renderPassEncoder: RenderPassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsetCount: uint, dynamicOffsets: [^]u32) --- + RenderPassEncoderSetBlendConstant :: proc(renderPassEncoder: RenderPassEncoder, color: /* const */ ^Color) --- + RenderPassEncoderSetIndexBuffer :: proc(renderPassEncoder: RenderPassEncoder, buffer: Buffer, format: IndexFormat, offset: u64, size: u64) --- + RenderPassEncoderSetLabel :: proc(renderPassEncoder: RenderPassEncoder, label: cstring) --- + RenderPassEncoderSetPipeline :: proc(renderPassEncoder: RenderPassEncoder, pipeline: RenderPipeline) --- + RenderPassEncoderSetScissorRect :: proc(renderPassEncoder: RenderPassEncoder, x: u32, y: u32, width: u32, height: u32) --- + RenderPassEncoderSetStencilReference :: proc(renderPassEncoder: RenderPassEncoder, reference: u32) --- + RenderPassEncoderSetVertexBuffer :: proc(renderPassEncoder: RenderPassEncoder, slot: u32, /* NULLABLE */ buffer: Buffer, offset: u64, size: u64) --- + RenderPassEncoderSetViewport :: proc(renderPassEncoder: RenderPassEncoder, x: f32, y: f32, width: f32, height: f32, minDepth: f32, maxDepth: f32) --- + RenderPassEncoderReference :: proc(renderPassEncoder: RenderPassEncoder) --- + RenderPassEncoderRelease :: proc(renderPassEncoder: RenderPassEncoder) --- + + // Methods of RenderPipeline + RenderPipelineGetBindGroupLayout :: proc(renderPipeline: RenderPipeline, groupIndex: u32) -> BindGroupLayout --- + RenderPipelineSetLabel :: proc(renderPipeline: RenderPipeline, label: cstring) --- + RenderPipelineReference :: proc(renderPipeline: RenderPipeline) --- + RenderPipelineRelease :: proc(renderPipeline: RenderPipeline) --- + + // Methods of Sampler + SamplerSetLabel :: proc(sampler: Sampler, label: cstring) --- + SamplerReference :: proc(sampler: Sampler) --- + SamplerRelease :: proc(sampler: Sampler) --- + + // Methods of ShaderModule + ShaderModuleGetCompilationInfo :: proc(shaderModule: ShaderModule, callback: ShaderModuleGetCompilationInfoCallback, /* NULLABLE */ userdata: rawptr = nil) --- + ShaderModuleSetLabel :: proc(shaderModule: ShaderModule, label: cstring) --- + ShaderModuleReference :: proc(shaderModule: ShaderModule) --- + ShaderModuleRelease :: proc(shaderModule: ShaderModule) --- + + // Methods of Surface + SurfaceConfigure :: proc(surface: Surface, config: /* const */ ^SurfaceConfiguration) --- + @(link_name="wgpuSurfaceGetCapabilities") + RawSurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter, capabilities: ^SurfaceCapabilities) --- + @(link_name="wgpuSurfaceGetCurrentTexture") + RawSurfaceGetCurrentTexture :: proc(surface: Surface, surfaceTexture: ^SurfaceTexture) --- + SurfaceGetPreferredFormat :: proc(surface: Surface, adapter: Adapter) -> TextureFormat --- + SurfacePresent :: proc(surface: Surface) --- + // SurfaceSetLabel :: proc(surface: Surface, label: cstring) --- + SurfaceUnconfigure :: proc(surface: Surface) --- + SurfaceReference :: proc(surface: Surface) --- + SurfaceRelease :: proc(surface: Surface) --- + + // Methods of SurfaceCapabilities + SurfaceCapabilitiesFreeMembers :: proc(surfaceCapabilities: SurfaceCapabilities) --- + + // Methods of Texture + TextureCreateView :: proc(texture: Texture, /* NULLABLE */ descriptor: /* const */ ^TextureViewDescriptor = nil) -> TextureView --- + TextureDestroy :: proc(texture: Texture) --- + TextureGetDepthOrArrayLayers :: proc(texture: Texture) -> u32 --- + TextureGetDimension :: proc(texture: Texture) -> TextureDimension --- + TextureGetFormat :: proc(texture: Texture) -> TextureFormat --- + TextureGetHeight :: proc(texture: Texture) -> u32 --- + TextureGetMipLevelCount :: proc(texture: Texture) -> u32 --- + TextureGetSampleCount :: proc(texture: Texture) -> u32 --- + TextureGetUsage :: proc(texture: Texture) -> TextureUsageFlags --- + TextureGetWidth :: proc(texture: Texture) -> u32 --- + TextureSetLabel :: proc(texture: Texture, label: cstring) --- + TextureReference :: proc(texture: Texture) --- + TextureRelease :: proc(texture: Texture) --- + + // Methods of TextureView + TextureViewSetLabel :: proc(textureView: TextureView, label: cstring) --- + TextureViewReference :: proc(textureView: TextureView) --- + TextureViewRelease :: proc(textureView: TextureView) --- +} + +// Wrappers of Adapter + +AdapterEnumerateFeatures :: proc(adapter: Adapter, allocator := context.allocator) -> []FeatureName { + count := RawAdapterEnumerateFeatures(adapter, nil) + features := make([]FeatureName, count, allocator) + RawAdapterEnumerateFeatures(adapter, raw_data(features)) + return features +} + +AdapterGetLimits :: proc(adapter: Adapter) -> (limits: SupportedLimits, ok: bool) { + ok = bool(RawAdapterGetLimits(adapter, &limits)) + return +} + +AdapterGetProperties :: proc(adapter: Adapter) -> (properties: AdapterProperties) { + RawAdapterGetProperties(adapter, &properties) + return +} + +// Wrappers of Buffer + +BufferGetConstMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> []byte { + return ([^]byte)(RawBufferGetConstMappedRange(buffer, offset, size))[:size] +} + +BufferGetConstMappedRangeTyped :: proc(buffer: Buffer, offset: uint, $T: typeid) -> ^T + where !intrinsics.type_is_sliceable(T) { + + return (^T)(RawBufferGetConstMappedRange(buffer, 0, size_of(T))) +} + +BufferGetConstMappedRangeSlice :: proc(buffer: Buffer, offset: uint, length: uint, $T: typeid) -> []T { + return ([^]T)(RawBufferGetConstMappedRange(buffer, offset, size_of(T)*length))[:length] +} + +BufferGetMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> []byte { + return ([^]byte)(RawBufferGetMappedRange(buffer, offset, size))[:size] +} + +BufferGetMappedRangeTyped :: proc(buffer: Buffer, offset: uint, $T: typeid) -> ^T + where !intrinsics.type_is_sliceable(T) { + + return (^T)(RawBufferGetMappedRange(buffer, offset, size_of(T))) +} + +BufferGetMappedRangeSlice :: proc(buffer: Buffer, offset: uint, $T: typeid, length: uint) -> []T { + return ([^]T)(RawBufferGetMappedRange(buffer, offset, size_of(T)*length))[:length] +} + +// Wrappers of ComputePassEncoder + +ComputePassEncoderSetBindGroup :: proc(computePassEncoder: ComputePassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { + RawComputePassEncoderSetBindGroup(computePassEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets)) +} + +// Wrappers of Device + +DeviceEnumerateFeatures :: proc(device: Device, allocator := context.allocator) -> []FeatureName { + count := RawDeviceEnumerateFeatures(device, nil) + features := make([]FeatureName, count, allocator) + RawDeviceEnumerateFeatures(device, raw_data(features)) + return features +} + +DeviceGetLimits :: proc(device: Device) -> (limits: SupportedLimits, ok: bool) { + ok = bool(RawDeviceGetLimits(device, &limits)) + return +} + +BufferWithDataDescriptor :: struct { + /* NULLABLE */ label: cstring, + usage: BufferUsageFlags, +} + +DeviceCreateBufferWithDataSlice :: proc(device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: []$T) -> (buf: Buffer) { + size := u64(size_of(T) * len(data)) + buf = DeviceCreateBuffer(device, &{ + label = descriptor.label, + usage = descriptor.usage, + size = size, + mappedAtCreation = true, + }) + + mapping := BufferGetMappedRangeSlice(buf, 0, T, len(data)) + copy(mapping, data) + + BufferUnmap(buf) + return +} + +DeviceCreateBufferWithDataTyped :: proc(device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: $T) -> (buf: Buffer) + where !intrinsics.type_is_sliceable(T) { + + buf = DeviceCreateBuffer(device, &{ + label = descriptor.label, + usage = descriptor.usage, + size = size_of(T), + mappedAtCreation = true, + }) + + mapping := BufferGetMappedRangeTyped(buf, 0, T) + mapping^ = data + + BufferUnmap(buf) + return +} + +DeviceCreateBufferWithData :: proc { + DeviceCreateBufferWithDataSlice, + DeviceCreateBufferWithDataTyped, +} + +// Wrappers of Queue + +QueueSubmit :: proc(queue: Queue, commands: []CommandBuffer) { + RawQueueSubmit(queue, len(commands), raw_data(commands)) +} + +// Wrappers of RenderBundleEncoder + +RenderBundleEncoderSetBindGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { + RawRenderBundleEncoderSetBindGroup(renderBundleEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets)) +} + +// Wrappers of RenderPassEncoder + +RenderPassEncoderExecuteBundles :: proc(renderPassEncoder: RenderPassEncoder, bundles: []RenderBundle) { + RawRenderPassEncoderExecuteBundles(renderPassEncoder, len(bundles), raw_data(bundles)) +} + +RenderPassEncoderSetBindGroup :: proc(renderPassEncoder: RenderPassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { + RawRenderPassEncoderSetBindGroup(renderPassEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets)) +} + +// Wrappers of Surface + +SurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter) -> (capabilities: SurfaceCapabilities) { + RawSurfaceGetCapabilities(surface, adapter, &capabilities) + return +} + +SurfaceGetCurrentTexture :: proc(surface: Surface) -> (surface_texture: SurfaceTexture) { + RawSurfaceGetCurrentTexture(surface, &surface_texture) + return +} diff --git a/vendor/wgpu/wgpu_js.odin b/vendor/wgpu/wgpu_js.odin new file mode 100644 index 000000000..f375a0d69 --- /dev/null +++ b/vendor/wgpu/wgpu_js.odin @@ -0,0 +1,26 @@ +package wgpu + +import "base:runtime" + +g_context: runtime.Context + +@(private="file", init) +wgpu_init_allocator :: proc() { + if g_context.allocator.procedure == nil { + g_context = runtime.default_context() + } +} + +@(private="file", export) +wgpu_alloc :: proc "contextless" (size: i32) -> [^]byte { + context = g_context + bytes, err := runtime.mem_alloc(int(size), 16) + assert(err == nil, "wgpu_alloc failed") + return raw_data(bytes) +} + +@(private="file", export) +wgpu_free :: proc "contextless" (ptr: rawptr) { + context = g_context + assert(free(ptr) == nil, "wgpu_free failed") +} diff --git a/vendor/wgpu/wgpu_native.odin b/vendor/wgpu/wgpu_native.odin new file mode 100644 index 000000000..2b10e3c17 --- /dev/null +++ b/vendor/wgpu/wgpu_native.odin @@ -0,0 +1,75 @@ +//+build !js +package wgpu + +BINDINGS_VERSION :: [4]u8{0, 19, 4, 1} +BINDINGS_VERSION_STRING :: "0.19.4.1" + +@(private="file", init) +wgpu_native_version_check :: proc() { + v := (transmute([4]u8)GetVersion()).wzyx + + if v != BINDINGS_VERSION { + buf: [1024]byte + n := copy(buf[:], "wgpu-native version mismatch: ") + n += copy(buf[n:], "bindings are for version ") + n += copy(buf[n:], BINDINGS_VERSION_STRING) + n += copy(buf[n:], ", but a different version is linked") + panic(string(buf[:n])) + } +} + +@(link_prefix="wgpu") +foreign { + @(link_name="wgpuGenerateReport") + RawGenerateReport :: proc(instance: Instance, report: ^GlobalReport) --- + @(link_name="wgpuInstanceEnumerateAdapters") + RawInstanceEnumerateAdapters :: proc(instance: Instance, /* NULLABLE */ options: /* const */ ^InstanceEnumerateAdapterOptions, adapters: [^]Adapter) -> uint --- + + @(link_name="wgpuQueueSubmitForIndex") + RawQueueSubmitForIndex :: proc(queue: Queue, commandCount: uint, commands: [^]CommandBuffer) -> SubmissionIndex --- + + // Returns true if the queue is empty, or false if there are more queue submissions still in flight. + @(link_name="wgpuDevicePoll") + RawDevicePoll :: proc(device: Device, wait: b32, /* NULLABLE */ wrappedSubmissionIndex: /* const */ ^WrappedSubmissionIndex) -> b32 --- + + SetLogCallback :: proc "odin" (callback: LogCallback) --- + + SetLogLevel :: proc(level: LogLevel) --- + + GetVersion :: proc() -> u32 --- + + RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: cstring) --- + + RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) --- + RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) --- + + RenderPassEncoderMultiDrawIndirectCount :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count_buffer: Buffer, count_buffer_offset: u64, max_count: u32) --- + RenderPassEncoderMultiDrawIndexedIndirectCount :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count_buffer: Buffer, count_buffer_offset: u64, max_count: u32) --- + + ComputePassEncoderBeginPipelineStatisticsQuery :: proc(computePassEncoder: ComputePassEncoder, querySet: QuerySet, queryIndex: u32) --- + ComputePassEncoderEndPipelineStatisticsQuery :: proc(computePassEncoder: ComputePassEncoder) --- + RenderPassEncoderBeginPipelineStatisticsQuery :: proc(renderPassEncoder: RenderPassEncoder, querySet: QuerySet, queryIndex: u32) --- + RenderPassEncoderEndPipelineStatisticsQuery :: proc(renderPassEncoder: RenderPassEncoder) --- +} + +GenerateReport :: proc(instance: Instance) -> (report: GlobalReport) { + RawGenerateReport(instance, &report) + return +} + +InstanceEnumerateAdapters :: proc(instance: Instance, options: ^InstanceEnumerateAdapterOptions = nil, allocator := context.allocator) -> (adapters: []Adapter) { + count := RawInstanceEnumerateAdapters(instance, options, nil) + adapters = make([]Adapter, count, allocator) + RawInstanceEnumerateAdapters(instance, options, raw_data(adapters)) + return +} + +QueueSubmitForIndex :: proc(queue: Queue, commands: []CommandBuffer) -> SubmissionIndex { + return RawQueueSubmitForIndex(queue, len(commands), raw_data(commands)) +} + +DevicePoll :: proc(device: Device, wait: b32) -> (wrappedSubmissionIndex: WrappedSubmissionIndex, ok: bool) { + ok = bool(RawDevicePoll(device, wait, &wrappedSubmissionIndex)) + return +} + diff --git a/vendor/wgpu/wgpu_native_types.odin b/vendor/wgpu/wgpu_native_types.odin new file mode 100644 index 000000000..b0837198f --- /dev/null +++ b/vendor/wgpu/wgpu_native_types.odin @@ -0,0 +1,212 @@ +package wgpu + +import "base:runtime" + +LogLevel :: enum i32 { + Off, + Error, + Warn, + Info, + Debug, + Trace, +} + +InstanceBackend :: enum i32 { + Vulkan, + GL, + Metal, + DX12, + DX11, + BrowserWebGPU, +} +InstanceBackendFlags :: bit_set[InstanceBackend; Flags] +InstanceBackendFlags_All :: InstanceBackendFlags{} +InstanceBackendFlags_Primary :: InstanceBackendFlags{ .Vulkan, .Metal, .DX12, .BrowserWebGPU } +InstanceBackendFlags_Secondary :: InstanceBackendFlags{ .GL, .DX11 } + +InstanceFlag :: enum i32 { + Debug, + Validation, + DiscardHalLabels, +} +InstanceFlags :: bit_set[InstanceFlag; Flags] +InstanceFlags_Default :: InstanceFlags{} + +Dx12Compiler :: enum i32 { + Undefined, + Fxc, + Dxc, +} + +Gles3MinorVersion :: enum i32 { + Automatic, + Version0, + Version1, + Version2, +} + +PipelineStatisticName :: enum i32 { + VertexShaderInvocations, + ClipperInvocations, + ClipperPrimitivesOut, + FragmentShaderInvocations, + ComputeShaderInvocations, +} + +InstanceExtras :: struct { + using chain: ChainedStruct, + backends: InstanceBackendFlags, + flags: InstanceFlags, + dx12ShaderCompiler: Dx12Compiler, + gles3MinorVersion: Gles3MinorVersion, + dxilPath: cstring, + dxcPath: cstring, +} + +DeviceExtras :: struct { + using chain: ChainedStruct, + tracePath: cstring, +} + +NativeLimits :: struct { + maxPushConstantSize: u32, + maxNonSamplerBindings: u32, +} + +RequiredLimitsExtras :: struct { + using chain: ChainedStruct, + limits: NativeLimits, +} + +SupportedLimitsExtras :: struct { + using chain: ChainedStruct, + limits: NativeLimits, +} + +PushConstantRange :: struct { + stages: ShaderStageFlags, + start: u32, + end: u32, +} + +PipelineLayoutExtras :: struct { + using chain: ChainedStruct, + pushConstantRangeCount: uint, + pushConstantRanges: [^]PushConstantRange `fmt:"v,pushConstantRangeCount"`, +} + +SubmissionIndex :: distinct u64 + +WrappedSubmissionIndex :: struct { + queue: Queue, + submissionIndex: SubmissionIndex, +} + +ShaderDefine :: struct { + name: cstring, + value: cstring, +} + +ShaderModuleGLSLDescriptor :: struct { + using chain: ChainedStruct, + stage: ShaderStage, + code: cstring, + defineCount: uint, + defines: [^]ShaderDefine `fmt:"v,defineCount"`, +} + +RegistryReport :: struct { + numAllocated: uint, + numKeptFromUser: uint, + numReleasedFromUser: uint, + numErrors: uint, + elementSize: uint, +} + +HubReport :: struct { + adapters: RegistryReport, + devices: RegistryReport, + queues: RegistryReport, + pipelineLayouts: RegistryReport, + shaderModules: RegistryReport, + bindGroupLayouts: RegistryReport, + bindGroups: RegistryReport, + commandBuffers: RegistryReport, + renderBundles: RegistryReport, + renderPipelines: RegistryReport, + computePipelines: RegistryReport, + querySets: RegistryReport, + buffers: RegistryReport, + textures: RegistryReport, + textureViews: RegistryReport, + samplers: RegistryReport, +} + +GlobalReport :: struct { + surfaces: RegistryReport, + backendType: BackendType, + vulkan: HubReport, + metal: HubReport, + dx12: HubReport, + gl: HubReport, +} + +InstanceEnumerateAdapterOptions :: struct { + nextInChain: ^ChainedStruct, + backends: InstanceBackendFlags, +} + +BindGroupEntryExtras :: struct { + using chain: ChainedStruct, + buffers: [^]Buffer `fmt:"v,bufferCount"`, + bufferCount: uint, + samplers: [^]Sampler `fmt:"v,samplerCount"`, + samplerCount: uint, + textureViews: [^]TextureView `fmt:"v,textureViewCount"`, + textureViewCount: uint, +} + +BindGroupLayoutEntryExtras :: struct { + using chain: ChainedStruct, + count: u32, +} + +QuerySetDescriptorExtras :: struct { + using chain: ChainedStruct, + pipelineStatistics: [^]PipelineStatisticName `fmt:"v,pipelineStatisticCount"`, + pipelineStatisticCount: uint, +} + +SurfaceConfigurationExtras :: struct { + using chain: ChainedStruct, + desiredMaximumFrameLatency: i32, +} + +LogCallback :: #type proc "odin" (level: LogLevel, message: cstring) + +// Wrappers + +ConvertOdinToWGPULogLevel :: proc(level: runtime.Logger_Level) -> LogLevel { + switch { + case level < .Debug: return .Trace + case level < .Info: return .Debug + case level < .Warning: return .Info + case level < .Error: return .Warn + case: return .Error + } +} + +ConvertWGPUToOdinLogLevel :: proc(level: LogLevel) -> runtime.Logger_Level { + switch level { + case .Off, .Trace, .Debug: return .Debug + case .Info: return .Info + case .Warn: return .Warning + case .Error: return .Error + case: return .Error + } +} + +ConvertLogLevel :: proc { + ConvertOdinToWGPULogLevel, + ConvertWGPUToOdinLogLevel, +} diff --git a/vendor/x11/xlib/xlib_types.odin b/vendor/x11/xlib/xlib_types.odin index d333c3c79..8cd0131fe 100644 --- a/vendor/x11/xlib/xlib_types.odin +++ b/vendor/x11/xlib/xlib_types.odin @@ -24,6 +24,9 @@ Cursor :: XID Colormap :: XID GContext :: XID +RRCrtc :: XID +RROutput :: XID + KeyCode :: u8 /* ---- X11/Xlib.h ---------------------------------------------------------*/