package reflect import "core:runtime" import "core:mem" import "core:intrinsics" _ :: intrinsics Type_Info :: runtime.Type_Info Type_Info_Named :: runtime.Type_Info_Named Type_Info_Integer :: runtime.Type_Info_Integer Type_Info_Rune :: runtime.Type_Info_Rune Type_Info_Float :: runtime.Type_Info_Float Type_Info_Complex :: runtime.Type_Info_Complex Type_Info_Quaternion :: runtime.Type_Info_Quaternion Type_Info_String :: runtime.Type_Info_String Type_Info_Boolean :: runtime.Type_Info_Boolean Type_Info_Any :: runtime.Type_Info_Any Type_Info_Type_Id :: runtime.Type_Info_Type_Id Type_Info_Pointer :: runtime.Type_Info_Pointer Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer Type_Info_Procedure :: runtime.Type_Info_Procedure Type_Info_Array :: runtime.Type_Info_Array Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array Type_Info_Slice :: runtime.Type_Info_Slice Type_Info_Tuple :: runtime.Type_Info_Tuple Type_Info_Struct :: runtime.Type_Info_Struct Type_Info_Union :: runtime.Type_Info_Union Type_Info_Enum :: runtime.Type_Info_Enum Type_Info_Map :: runtime.Type_Info_Map Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value Type_Kind :: enum { Invalid, Named, Integer, Rune, Float, Complex, Quaternion, String, Boolean, Any, Type_Id, Pointer, Multi_Pointer, Procedure, Array, Enumerated_Array, Dynamic_Array, Slice, Tuple, Struct, Union, Enum, Map, Bit_Set, Simd_Vector, Relative_Pointer, Relative_Slice, Matrix, } type_kind :: proc(T: typeid) -> Type_Kind { ti := type_info_of(T) if ti != nil { switch _ in ti.variant { case Type_Info_Named: return .Named case Type_Info_Integer: return .Integer case Type_Info_Rune: return .Rune case Type_Info_Float: return .Float case Type_Info_Complex: return .Complex case Type_Info_Quaternion: return .Quaternion case Type_Info_String: return .String case Type_Info_Boolean: return .Boolean case Type_Info_Any: return .Any case Type_Info_Type_Id: return .Type_Id case Type_Info_Pointer: return .Pointer case Type_Info_Multi_Pointer: return .Multi_Pointer case Type_Info_Procedure: return .Procedure case Type_Info_Array: return .Array case Type_Info_Enumerated_Array: return .Enumerated_Array case Type_Info_Dynamic_Array: return .Dynamic_Array case Type_Info_Slice: return .Slice case Type_Info_Tuple: return .Tuple case Type_Info_Struct: return .Struct case Type_Info_Union: return .Union case Type_Info_Enum: return .Enum case Type_Info_Map: return .Map case Type_Info_Bit_Set: return .Bit_Set case Type_Info_Simd_Vector: return .Simd_Vector case Type_Info_Relative_Pointer: return .Relative_Pointer case Type_Info_Relative_Slice: return .Relative_Slice case Type_Info_Matrix: return .Matrix } } return .Invalid } // TODO(bill): Better name underlying_type_kind :: proc(T: typeid) -> Type_Kind { return type_kind(runtime.typeid_base(T)) } // TODO(bill): Better name backing_type_kind :: proc(T: typeid) -> Type_Kind { return type_kind(runtime.typeid_core(T)) } type_info_base :: proc(info: ^Type_Info) -> ^Type_Info { if info == nil { return nil } base := info loop: for { #partial switch i in base.variant { case Type_Info_Named: base = i.base case: break loop } } return base } type_info_core :: proc(info: ^Type_Info) -> ^Type_Info { if info == nil { return nil } base := info loop: for { #partial switch i in base.variant { case Type_Info_Named: base = i.base case Type_Info_Enum: base = i.base case: break loop } } return base } type_info_base_without_enum :: type_info_core typeid_base :: proc(id: typeid) -> typeid { ti := type_info_of(id) ti = type_info_base(ti) return ti.id } typeid_core :: proc(id: typeid) -> typeid { ti := type_info_base_without_enum(type_info_of(id)) return ti.id } typeid_base_without_enum :: typeid_core any_base :: proc(v: any) -> any { v := v if v != nil { v.id = typeid_base(v.id) } return v } any_core :: proc(v: any) -> any { v := v if v != nil { v.id = typeid_core(v.id) } return v } typeid_elem :: proc(id: typeid) -> typeid { ti := type_info_of(id) if ti == nil { return nil } bits := 8*ti.size #partial switch v in ti.variant { case Type_Info_Complex: switch bits { case 64: return f32 case 128: return f64 } case Type_Info_Quaternion: switch bits { case 128: return f32 case 256: return f64 } case Type_Info_Pointer: return v.elem.id case Type_Info_Multi_Pointer: return v.elem.id case Type_Info_Array: return v.elem.id case Type_Info_Enumerated_Array: return v.elem.id case Type_Info_Slice: return v.elem.id case Type_Info_Dynamic_Array: return v.elem.id } return id } size_of_typeid :: proc(T: typeid) -> int { if ti := type_info_of(T); ti != nil { return ti.size } return 0 } align_of_typeid :: proc(T: typeid) -> int { if ti := type_info_of(T); ti != nil { return ti.align } return 1 } as_bytes :: proc(v: any) -> []byte { if v != nil { sz := size_of_typeid(v.id) return mem.slice_ptr((^byte)(v.data), sz) } return nil } any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) { return v.data, v.id } is_nil :: proc(v: any) -> bool { if v == nil { return true } data := as_bytes(v) if data == nil { return true } for v in data { if v != 0 { return false } } return true } length :: proc(val: any) -> int { if val == nil { return 0 } #partial switch a in type_info_of(val.id).variant { case Type_Info_Named: return length({val.data, a.base.id}) case Type_Info_Pointer: return length({val.data, a.elem.id}) case Type_Info_Array: return a.count case Type_Info_Enumerated_Array: return a.count case Type_Info_Slice: return (^mem.Raw_Slice)(val.data).len case Type_Info_Dynamic_Array: return (^mem.Raw_Dynamic_Array)(val.data).len case Type_Info_Map: return (^mem.Raw_Map)(val.data).entries.len case Type_Info_String: if a.is_cstring { return len((^cstring)(val.data)^) } else { return (^mem.Raw_String)(val.data).len } } return 0 } capacity :: proc(val: any) -> int { if val == nil { return 0 } #partial switch a in type_info_of(val.id).variant { case Type_Info_Named: return capacity({val.data, a.base.id}) case Type_Info_Pointer: return capacity({val.data, a.elem.id}) case Type_Info_Array: return a.count case Type_Info_Enumerated_Array: return a.count case Type_Info_Dynamic_Array: return (^mem.Raw_Dynamic_Array)(val.data).cap case Type_Info_Map: return (^mem.Raw_Map)(val.data).entries.cap } return 0 } index :: proc(val: any, i: int, loc := #caller_location) -> any { if val == nil { return nil } #partial switch a in type_info_of(val.id).variant { case Type_Info_Named: return index({val.data, a.base.id}, i, loc) case Type_Info_Pointer: ptr := (^rawptr)(val.data)^ if ptr == nil { return nil } return index({ptr, a.elem.id}, i, loc) case Type_Info_Multi_Pointer: ptr := (^rawptr)(val.data)^ if ptr == nil { return nil } return index({ptr, a.elem.id}, i, loc) case Type_Info_Array: runtime.bounds_check_error_loc(loc, i, a.count) offset := uintptr(a.elem.size * i) data := rawptr(uintptr(val.data) + offset) return any{data, a.elem.id} case Type_Info_Enumerated_Array: runtime.bounds_check_error_loc(loc, i, a.count) offset := uintptr(a.elem.size * i) data := rawptr(uintptr(val.data) + offset) return any{data, a.elem.id} case Type_Info_Slice: raw := (^mem.Raw_Slice)(val.data) runtime.bounds_check_error_loc(loc, i, raw.len) offset := uintptr(a.elem.size * i) data := rawptr(uintptr(raw.data) + offset) return any{data, a.elem.id} case Type_Info_Dynamic_Array: raw := (^mem.Raw_Dynamic_Array)(val.data) runtime.bounds_check_error_loc(loc, i, raw.len) offset := uintptr(a.elem.size * i) data := rawptr(uintptr(raw.data) + offset) return any{data, a.elem.id} case Type_Info_String: if a.is_cstring { return nil } raw := (^mem.Raw_String)(val.data) runtime.bounds_check_error_loc(loc, i, raw.len) offset := uintptr(size_of(u8) * i) data := rawptr(uintptr(raw.data) + offset) return any{data, typeid_of(u8)} } return nil } deref :: proc(val: any) -> any { if val != nil { ti := type_info_base(type_info_of(val.id)) if info, ok := ti.variant.(Type_Info_Pointer); ok { return any{ (^rawptr)(val.data)^, info.elem.id, } } } return val } // Struct_Tag represents the type of the string of a struct field // // Through convention, tags are the concatenation of optionally space separationed key:"value" pairs. // Each key is a non-empty string which contains no control characters other than space, quotes, and colon. Struct_Tag :: distinct string Struct_Field :: struct { name: string, type: ^Type_Info, tag: Struct_Tag, offset: uintptr, is_using: bool, } struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { if 0 <= i && i < len(s.names) { field.name = s.names[i] field.type = s.types[i] field.tag = Struct_Tag(s.tags[i]) field.offset = s.offsets[i] field.is_using = s.usings[i] } } return } struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { for fname, i in s.names { if fname == name { field.name = s.names[i] field.type = s.types[i] field.tag = Struct_Tag(s.tags[i]) field.offset = s.offsets[i] field.is_using = s.usings[i] break } } } return } struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any { if a == nil { return nil } ti := runtime.type_info_base(type_info_of(a.id)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { for name, i in s.names { if name == field { return any{ rawptr(uintptr(a.data) + s.offsets[i]), s.types[i].id, } } if allow_using && s.usings[i] { f := any{ rawptr(uintptr(a.data) + s.offsets[i]), s.types[i].id, } if res := struct_field_value_by_name(f, field, allow_using); res != nil { return res } } } } return nil } struct_field_names :: proc(T: typeid) -> []string { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { return s.names } return nil } struct_field_types :: proc(T: typeid) -> []^Type_Info { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { return s.types } return nil } struct_field_tags :: proc(T: typeid) -> []Struct_Tag { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { return transmute([]Struct_Tag)s.tags } return nil } struct_field_offsets :: proc(T: typeid) -> []uintptr { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { return s.offsets } return nil } struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) { ti := runtime.type_info_base(type_info_of(T)) if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { return soa_zip( name = s.names, type = s.types, tag = transmute([]Struct_Tag)s.tags, offset = s.offsets, is_using = s.usings, ) } return nil } struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) { value, _ = struct_tag_lookup(tag, key) return } struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) { for t := tag; t != ""; /**/ { i := 0 for i < len(t) && t[i] == ' ' { // Skip whitespace i += 1 } t = t[i:] if len(t) == 0 { break } i = 0 loop: for i < len(t) { switch t[i] { case ':', '"': break loop case 0x00 ..< ' ', 0x7f ..= 0x9f: // break if control character is found break loop } i += 1 } if i == 0 { break } if i+1 >= len(t) { break } if t[i] != ':' || t[i+1] != '"' { break } name := string(t[:i]) t = t[i+1:] i = 1 for i < len(t) && t[i] != '"' { // find closing quote if t[i] == '\\' { i += 1 // Skip escaped characters } i += 1 } if i >= len(t) { break } val := string(t[:i+1]) t = t[i+1:] if key == name { return Struct_Tag(val[1:i]), true } } return } enum_string :: proc(a: any) -> string { if a == nil { return "" } ti := runtime.type_info_base(type_info_of(a.id)) if e, ok := ti.variant.(runtime.Type_Info_Enum); ok { v, _ := as_i64(a) for value, i in e.values { if value == Type_Info_Enum_Value(v) { return e.names[i] } } } else { panic("expected an enum to reflect.enum_string") } return "" } // Given a enum type and a value name, get the enum value. enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) { ti := type_info_base(type_info_of(Enum_Type)) if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok { for value_name, i in eti.names { if value_name != name { continue } v := eti.values[i] value = Enum_Type(v) ok = true return } } return } enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) { ti := runtime.type_info_base(type_info_of(Enum_Type)) if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok { for value_name, i in eti.names { if value_name != name { continue } value = eti.values[i] ok = true return } } return } enum_field_names :: proc(Enum_Type: typeid) -> []string { ti := runtime.type_info_base(type_info_of(Enum_Type)) if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok { return eti.names } return nil } enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value { ti := runtime.type_info_base(type_info_of(Enum_Type)) if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok { return eti.values } return nil } Enum_Field :: struct { name: string, value: Type_Info_Enum_Value, } enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) { ti := runtime.type_info_base(type_info_of(Enum_Type)) if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok { return soa_zip(name=eti.names, value=eti.values) } return nil } union_variant_type_info :: proc(a: any) -> ^Type_Info { id := union_variant_typeid(a) return type_info_of(id) } type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool { return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]) } union_variant_typeid :: proc(a: any) -> typeid { if a == nil { return nil } ti := runtime.type_info_base(type_info_of(a.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if type_info_union_is_pure_maybe(info) { if a.data != nil { return info.variants[0].id } return nil } tag_ptr := uintptr(a.data) + info.tag_offset tag_any := any{rawptr(tag_ptr), info.tag_type.id} tag: i64 = --- switch i in tag_any { case u8: tag = i64(i) case i8: tag = i64(i) case u16: tag = i64(i) case i16: tag = i64(i) case u32: tag = i64(i) case i32: tag = i64(i) case u64: tag = i64(i) case i64: tag = i case: unimplemented() } if a.data != nil && tag != 0 { i := tag if info.no_nil else tag-1 return info.variants[i].id } return nil } panic("expected a union to reflect.union_variant_typeid") } get_union_variant_raw_tag :: proc(a: any) -> i64 { if a == nil { return -1 } ti := runtime.type_info_base(type_info_of(a.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if type_info_union_is_pure_maybe(info) { return 1 if a.data != nil else 0 } tag_ptr := uintptr(a.data) + info.tag_offset tag_any := any{rawptr(tag_ptr), info.tag_type.id} tag: i64 = --- switch i in tag_any { case u8: tag = i64(i) case i8: tag = i64(i) case u16: tag = i64(i) case i16: tag = i64(i) case u32: tag = i64(i) case i32: tag = i64(i) case u64: tag = i64(i) case i64: tag = i case: unimplemented() } return tag } panic("expected a union to reflect.get_union_variant_raw_tag") } set_union_variant_raw_tag :: proc(a: any, tag: i64) { if a == nil { return } ti := runtime.type_info_base(type_info_of(a.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if type_info_union_is_pure_maybe(info) { // Cannot do anything return } tag_ptr := uintptr(a.data) + info.tag_offset tag_any := any{rawptr(tag_ptr), info.tag_type.id} switch i in &tag_any { case u8: i = u8(tag) case i8: i = i8(tag) case u16: i = u16(tag) case i16: i = i16(tag) case u32: i = u32(tag) case i32: i = i32(tag) case u64: i = u64(tag) case i64: i = tag case: unimplemented() } return } panic("expected a union to reflect.set_union_variant_raw_tag") } set_union_variant_typeid :: proc(a: any, id: typeid) { if a == nil { return } ti := runtime.type_info_base(type_info_of(a.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if type_info_union_is_pure_maybe(info) { // Cannot do anything return } if id == nil && !info.no_nil { set_union_variant_raw_tag(a, 0) return } for variant, i in info.variants { if variant.id == id { tag := i64(i) if !info.no_nil { tag += 1 } set_union_variant_raw_tag(a, tag) return } } return } panic("expected a union to reflect.set_union_variant_typeid") } set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) { if a == nil { return } ti := runtime.type_info_base(type_info_of(a.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if type_info_union_is_pure_maybe(info) { // Cannot do anything return } if tag_ti == nil && !info.no_nil { set_union_variant_raw_tag(a, 0) return } for variant, i in info.variants { if variant == tag_ti { tag := i64(i) if !info.no_nil { tag += 1 } set_union_variant_raw_tag(a, tag) return } } return } panic("expected a union to reflect.set_union_variant_type_info") } set_union_value :: proc(dst: any, value: any) -> bool { if dst == nil { return false } ti := runtime.type_info_base(type_info_of(dst.id)) if info, ok := ti.variant.(runtime.Type_Info_Union); ok { if value.id == nil { mem.zero(dst.data, ti.size) return true } if ti.id == runtime.typeid_base(value.id) { mem.copy(dst.data, value.data, ti.size) return true } if type_info_union_is_pure_maybe(info) { if variant := info.variants[0]; variant.id == value.id { mem.copy(dst.data, value.data, variant.size) return true } return false } for variant, i in info.variants { if variant.id == value.id { tag := i64(i) if !info.no_nil { tag += 1 } mem.copy(dst.data, value.data, variant.size) set_union_variant_raw_tag(dst, tag) return true } } return false } panic("expected a union to reflect.set_union_variant_typeid") } as_bool :: proc(a: any) -> (value: bool, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_Boolean: valid = true switch v in a { case bool: value = v case b8: value = bool(v) case b16: value = bool(v) case b32: value = bool(v) case b64: value = bool(v) case: valid = false } } return } as_int :: proc(a: any) -> (value: int, valid: bool) { v: i64 v, valid = as_i64(a) value = int(v) return } as_uint :: proc(a: any) -> (value: uint, valid: bool) { v: u64 v, valid = as_u64(a) value = uint(v) return } as_i64 :: proc(a: any) -> (value: i64, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_Integer: valid = true switch v in a { case i8: value = i64(v) case i16: value = i64(v) case i32: value = i64(v) case i64: value = v case i128: value = i64(v) case int: value = i64(v) case u8: value = i64(v) case u16: value = i64(v) case u32: value = i64(v) case u64: value = i64(v) case u128: value = i64(v) case uint: value = i64(v) case uintptr: value = i64(v) case u16le: value = i64(v) case u32le: value = i64(v) case u64le: value = i64(v) case u128le: value = i64(v) case i16le: value = i64(v) case i32le: value = i64(v) case i64le: value = i64(v) case i128le: value = i64(v) case u16be: value = i64(v) case u32be: value = i64(v) case u64be: value = i64(v) case u128be: value = i64(v) case i16be: value = i64(v) case i32be: value = i64(v) case i64be: value = i64(v) case i128be: value = i64(v) case: valid = false } case Type_Info_Rune: r := a.(rune) value = i64(r) valid = true case Type_Info_Float: valid = true switch v in a { case f32: value = i64(v) case f64: value = i64(v) case f32le: value = i64(v) case f64le: value = i64(v) case f32be: value = i64(v) case f64be: value = i64(v) case: valid = false } case Type_Info_Boolean: valid = true switch v in a { case bool: value = i64(v) case b8: value = i64(v) case b16: value = i64(v) case b32: value = i64(v) case b64: value = i64(v) case: valid = false } case Type_Info_Complex: switch v in a { case complex64: if imag(v) == 0 { value = i64(real(v)) valid = true } case complex128: if imag(v) == 0 { value = i64(real(v)) valid = true } } case Type_Info_Quaternion: switch v in a { case quaternion128: if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 { value = i64(real(v)) valid = true } case quaternion256: if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 { value = i64(real(v)) valid = true } } } return } as_u64 :: proc(a: any) -> (value: u64, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_Integer: valid = true switch v in a { case i8: value = u64(v) case i16: value = u64(v) case i32: value = u64(v) case i64: value = u64(v) case i128: value = u64(v) case int: value = u64(v) case u8: value = u64(v) case u16: value = u64(v) case u32: value = u64(v) case u64: value = (v) case u128: value = u64(v) case uint: value = u64(v) case uintptr:value = u64(v) case u16le: value = u64(v) case u32le: value = u64(v) case u64le: value = u64(v) case u128le: value = u64(v) case i16le: value = u64(v) case i32le: value = u64(v) case i64le: value = u64(v) case i128le: value = u64(v) case u16be: value = u64(v) case u32be: value = u64(v) case u64be: value = u64(v) case u128be: value = u64(v) case i16be: value = u64(v) case i32be: value = u64(v) case i64be: value = u64(v) case i128be: value = u64(v) case: valid = false } case Type_Info_Rune: r := a.(rune) value = u64(r) valid = true case Type_Info_Float: valid = true switch v in a { case f16: value = u64(v) case f32: value = u64(v) case f64: value = u64(v) case f32le: value = u64(v) case f64le: value = u64(v) case f32be: value = u64(v) case f64be: value = u64(v) case: valid = false } case Type_Info_Boolean: valid = true switch v in a { case bool: value = u64(v) case b8: value = u64(v) case b16: value = u64(v) case b32: value = u64(v) case b64: value = u64(v) case: valid = false } case Type_Info_Complex: switch v in a { case complex64: if imag(v) == 0 { value = u64(real(v)) valid = true } case complex128: if imag(v) == 0 { value = u64(real(v)) valid = true } } case Type_Info_Quaternion: switch v in a { case quaternion128: if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 { value = u64(real(v)) valid = true } case quaternion256: if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 { value = u64(real(v)) valid = true } } } return } as_f64 :: proc(a: any) -> (value: f64, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_Integer: valid = true switch v in a { case i8: value = f64(v) case i16: value = f64(v) case i32: value = f64(v) case i64: value = f64(v) case i128: value = f64(v) case u8: value = f64(v) case u16: value = f64(v) case u32: value = f64(v) case u64: value = f64(v) case u128: value = f64(v) case u16le: value = f64(v) case u32le: value = f64(v) case u64le: value = f64(v) case u128le:value = f64(v) case i16le: value = f64(v) case i32le: value = f64(v) case i64le: value = f64(v) case i128le:value = f64(v) case u16be: value = f64(v) case u32be: value = f64(v) case u64be: value = f64(v) case u128be:value = f64(v) case i16be: value = f64(v) case i32be: value = f64(v) case i64be: value = f64(v) case i128be:value = f64(v) case: valid = false } case Type_Info_Rune: r := a.(rune) value = f64(i32(r)) valid = true case Type_Info_Float: valid = true switch v in a { case f16: value = f64(v) case f32: value = f64(v) case f64: value = (v) case f32le: value = f64(v) case f64le: value = f64(v) case f32be: value = f64(v) case f64be: value = f64(v) case: valid = false } case Type_Info_Boolean: valid = true switch v in a { case bool: value = f64(i32(v)) case b8: value = f64(i32(v)) case b16: value = f64(i32(v)) case b32: value = f64(i32(v)) case b64: value = f64(i32(v)) case: valid = false } case Type_Info_Complex: switch v in a { case complex64: if imag(v) == 0 { value = f64(real(v)) valid = true } case complex128: if imag(v) == 0 { value = real(v) valid = true } } case Type_Info_Quaternion: switch v in a { case quaternion128: if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 { value = f64(real(v)) valid = true } case quaternion256: if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 { value = real(v) valid = true } } } return } as_string :: proc(a: any) -> (value: string, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_String: valid = true switch v in a { case string: value = v case cstring: value = string(v) case: valid = false } } return } relative_pointer_to_absolute :: proc(a: any) -> rawptr { if a == nil { return nil } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_Relative_Pointer: return relative_pointer_to_absolute_raw(a.data, info.base_integer.id) } return nil } relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr { _handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) { if ptr^ == 0 { return nil } when intrinsics.type_is_unsigned(T) { return rawptr(uintptr(ptr) + uintptr(ptr^)) } else { return rawptr(uintptr(ptr) + uintptr(i64(ptr^))) } } ptr_any := any{data, base_integer_id} ptr: rawptr switch i in &ptr_any { case u8: ptr = _handle(&i) case u16: ptr = _handle(&i) case u32: ptr = _handle(&i) case u64: ptr = _handle(&i) case i8: ptr = _handle(&i) case i16: ptr = _handle(&i) case i32: ptr = _handle(&i) case i64: ptr = _handle(&i) case u16le: ptr = _handle(&i) case u32le: ptr = _handle(&i) case u64le: ptr = _handle(&i) case i16le: ptr = _handle(&i) case i32le: ptr = _handle(&i) case i64le: ptr = _handle(&i) case u16be: ptr = _handle(&i) case u32be: ptr = _handle(&i) case u64be: ptr = _handle(&i) case i16be: ptr = _handle(&i) case i32be: ptr = _handle(&i) case i64be: ptr = _handle(&i) } return ptr } as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_Pointer: valid = true value = a.data case Type_Info_String: valid = true switch v in a { case cstring: value = rawptr(v) case: valid = false } case Type_Info_Relative_Pointer: valid = true value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id) } return } as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) { if a == nil { return } a := a ti := runtime.type_info_core(type_info_of(a.id)) a.id = ti.id #partial switch info in ti.variant { case Type_Info_String: valid = true switch v in a { case string: value = raw_data(v) case cstring: value = rawptr(v) // just in case case: valid = false } case Type_Info_Array: valid = true value = a.data case Type_Info_Slice: valid = true value = (^mem.Raw_Slice)(a.data).data case Type_Info_Dynamic_Array: valid = true value = (^mem.Raw_Dynamic_Array)(a.data).data } return } eq :: equal ne :: not_equal DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32 not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool { return !equal(a, b, including_indirect_array_recursion, recursion_level) } equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool { if a == nil && b == nil { return true } if a.id != b.id { return false } if a.data == b.data { return true } including_indirect_array_recursion := including_indirect_array_recursion if recursion_level >= DEFAULT_EQUAL_MAX_RECURSION_LEVEL { including_indirect_array_recursion = false } t := type_info_of(a.id) if .Comparable not_in t.flags && !including_indirect_array_recursion { return false } if t.size == 0 { return true } if .Simple_Compare in t.flags { return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0 } t = runtime.type_info_core(t) switch v in t.variant { case Type_Info_Named: unreachable() case Type_Info_Tuple: unreachable() case Type_Info_Any: if !including_indirect_array_recursion { return false } va := (^any)(a.data) vb := (^any)(b.data) return equal(va, vb, including_indirect_array_recursion, recursion_level+1) case Type_Info_Map: return false case Type_Info_Relative_Slice: return false case Type_Info_Boolean, Type_Info_Integer, Type_Info_Rune, Type_Info_Float, Type_Info_Complex, Type_Info_Quaternion, Type_Info_Type_Id, Type_Info_Pointer, Type_Info_Multi_Pointer, Type_Info_Procedure, Type_Info_Bit_Set, Type_Info_Enum, Type_Info_Simd_Vector, Type_Info_Relative_Pointer, Type_Info_Matrix: return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0 case Type_Info_String: if v.is_cstring { x := string((^cstring)(a.data)^) y := string((^cstring)(b.data)^) return x == y } else { x := (^string)(a.data)^ y := (^string)(b.data)^ return x == y } return true case Type_Info_Array: for i in 0..