| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763 |
- // This is the runtime code required by the compiler
- // IMPORTANT NOTE(bill): Do not change the order of any of this data
- // The compiler relies upon this _exact_ order
- package runtime
- import "intrinsics"
- _ :: intrinsics;
- // Naming Conventions:
- // In general, Ada_Case for types and snake_case for values
- //
- // Package Name: snake_case (but prefer single word)
- // Import Name: snake_case (but prefer single word)
- // Types: Ada_Case
- // Enum Values: Ada_Case
- // Procedures: snake_case
- // Local Variables: snake_case
- // Constant Variables: SCREAMING_SNAKE_CASE
- // IMPORTANT NOTE(bill): `type_info_of` cannot be used within a
- // #shared_global_scope due to the internals of the compiler.
- // This could change at a later date if the all these data structures are
- // implemented within the compiler rather than in this "preload" file
- // NOTE(bill): This must match the compiler's
- Calling_Convention :: enum u8 {
- Invalid = 0,
- Odin = 1,
- Contextless = 2,
- Pure = 3,
- CDecl = 4,
- Std_Call = 5,
- Fast_Call = 6,
- None = 7,
- Pure_None = 8,
- }
- Type_Info_Enum_Value :: distinct i64;
- Platform_Endianness :: enum u8 {
- Platform = 0,
- Little = 1,
- Big = 2,
- }
- Type_Info_Struct_Soa_Kind :: enum u8 {
- None = 0,
- Fixed = 1,
- Slice = 2,
- Dynamic = 3,
- }
- // Variant Types
- Type_Info_Named :: struct {name: string, base: ^Type_Info};
- Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness};
- Type_Info_Rune :: struct {};
- Type_Info_Float :: struct {endianness: Platform_Endianness};
- Type_Info_Complex :: struct {};
- Type_Info_Quaternion :: struct {};
- Type_Info_String :: struct {is_cstring: bool};
- Type_Info_Boolean :: struct {};
- Type_Info_Any :: struct {};
- Type_Info_Type_Id :: struct {};
- Type_Info_Pointer :: struct {
- elem: ^Type_Info, // nil -> rawptr
- };
- Type_Info_Procedure :: struct {
- params: ^Type_Info, // Type_Info_Tuple
- results: ^Type_Info, // Type_Info_Tuple
- variadic: bool,
- convention: Calling_Convention,
- };
- Type_Info_Array :: struct {
- elem: ^Type_Info,
- elem_size: int,
- count: int,
- };
- Type_Info_Enumerated_Array :: struct {
- elem: ^Type_Info,
- index: ^Type_Info,
- elem_size: int,
- count: int,
- min_value: Type_Info_Enum_Value,
- max_value: Type_Info_Enum_Value,
- };
- Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int};
- Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int};
- Type_Info_Tuple :: struct { // Only really used for procedures
- types: []^Type_Info,
- names: []string,
- };
- Type_Info_Struct :: struct {
- types: []^Type_Info,
- names: []string,
- offsets: []uintptr,
- usings: []bool,
- tags: []string,
- is_packed: bool,
- is_raw_union: bool,
- custom_align: bool,
- // These are only set iff this structure is an SOA structure
- soa_kind: Type_Info_Struct_Soa_Kind,
- soa_base_type: ^Type_Info,
- soa_len: int,
- };
- Type_Info_Union :: struct {
- variants: []^Type_Info,
- tag_offset: uintptr,
- tag_type: ^Type_Info,
- custom_align: bool,
- no_nil: bool,
- maybe: bool,
- };
- Type_Info_Enum :: struct {
- base: ^Type_Info,
- names: []string,
- values: []Type_Info_Enum_Value,
- };
- Type_Info_Map :: struct {
- key: ^Type_Info,
- value: ^Type_Info,
- generated_struct: ^Type_Info,
- };
- Type_Info_Bit_Field :: struct {
- names: []string,
- bits: []i32,
- offsets: []i32,
- };
- Type_Info_Bit_Set :: struct {
- elem: ^Type_Info,
- underlying: ^Type_Info, // Possibly nil
- lower: i64,
- upper: i64,
- };
- Type_Info_Opaque :: struct {
- elem: ^Type_Info,
- };
- Type_Info_Simd_Vector :: struct {
- elem: ^Type_Info,
- elem_size: int,
- count: int,
- is_x86_mmx: bool,
- };
- Type_Info_Relative_Pointer :: struct {
- pointer: ^Type_Info,
- base_integer: ^Type_Info,
- };
- Type_Info_Relative_Slice :: struct {
- slice: ^Type_Info,
- base_integer: ^Type_Info,
- };
- Type_Info :: struct {
- size: int,
- align: int,
- id: typeid,
- variant: union {
- Type_Info_Named,
- Type_Info_Integer,
- Type_Info_Rune,
- Type_Info_Float,
- Type_Info_Complex,
- Type_Info_Quaternion,
- Type_Info_String,
- Type_Info_Boolean,
- Type_Info_Any,
- Type_Info_Type_Id,
- Type_Info_Pointer,
- Type_Info_Procedure,
- Type_Info_Array,
- Type_Info_Enumerated_Array,
- Type_Info_Dynamic_Array,
- Type_Info_Slice,
- Type_Info_Tuple,
- Type_Info_Struct,
- Type_Info_Union,
- Type_Info_Enum,
- Type_Info_Map,
- Type_Info_Bit_Field,
- Type_Info_Bit_Set,
- Type_Info_Opaque,
- Type_Info_Simd_Vector,
- Type_Info_Relative_Pointer,
- Type_Info_Relative_Slice,
- },
- }
- // NOTE(bill): This must match the compiler's
- Typeid_Kind :: enum u8 {
- Invalid,
- Integer,
- Rune,
- Float,
- Complex,
- Quaternion,
- String,
- Boolean,
- Any,
- Type_Id,
- Pointer,
- Procedure,
- Array,
- Enumerated_Array,
- Dynamic_Array,
- Slice,
- Tuple,
- Struct,
- Union,
- Enum,
- Map,
- Bit_Field,
- Bit_Set,
- Opaque,
- Simd_Vector,
- Relative_Pointer,
- Relative_Slice,
- }
- #assert(len(Typeid_Kind) < 32);
- Typeid_Bit_Field :: bit_field #align align_of(uintptr) {
- index: 8*size_of(uintptr) - 8,
- kind: 5, // Typeid_Kind
- named: 1,
- special: 1, // signed, cstring, etc
- reserved: 1,
- }
- #assert(size_of(Typeid_Bit_Field) == size_of(uintptr));
- // NOTE(bill): only the ones that are needed (not all types)
- // This will be set by the compiler
- type_table: []Type_Info;
- args__: []cstring;
- // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
- @builtin
- Maybe :: union(T: typeid) #maybe {T};
- Source_Code_Location :: struct {
- file_path: string,
- line, column: int,
- procedure: string,
- hash: u64,
- }
- Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
- // Allocation Stuff
- Allocator_Mode :: enum byte {
- Alloc,
- Free,
- Free_All,
- Resize,
- Query_Features,
- Query_Info,
- }
- Allocator_Mode_Set :: distinct bit_set[Allocator_Mode];
- Allocator_Query_Info :: struct {
- pointer: rawptr,
- size: Maybe(int),
- alignment: Maybe(int),
- }
- Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
- size, alignment: int,
- old_memory: rawptr, old_size: int, flags: u64 = 0, location: Source_Code_Location = #caller_location) -> rawptr;
- Allocator :: struct {
- procedure: Allocator_Proc,
- data: rawptr,
- }
- // Logging stuff
- Logger_Level :: enum uint {
- Debug = 0,
- Info = 10,
- Warning = 20,
- Error = 30,
- Fatal = 40,
- }
- Logger_Option :: enum {
- Level,
- Date,
- Time,
- Short_File_Path,
- Long_File_Path,
- Line,
- Procedure,
- Terminal_Color,
- Thread_Id,
- }
- Logger_Options :: bit_set[Logger_Option];
- Logger_Proc :: #type proc(data: rawptr, level: Logger_Level, text: string, options: Logger_Options, location := #caller_location);
- Logger :: struct {
- procedure: Logger_Proc,
- data: rawptr,
- lowest_level: Logger_Level,
- options: Logger_Options,
- }
- Context :: struct {
- allocator: Allocator,
- temp_allocator: Allocator,
- assertion_failure_proc: Assertion_Failure_Proc,
- logger: Logger,
- thread_id: int,
- user_data: any,
- user_ptr: rawptr,
- user_index: int,
- }
- Raw_String :: struct {
- data: ^byte,
- len: int,
- }
- Raw_Slice :: struct {
- data: rawptr,
- len: int,
- }
- Raw_Dynamic_Array :: struct {
- data: rawptr,
- len: int,
- cap: int,
- allocator: Allocator,
- }
- Raw_Map :: struct {
- hashes: []int,
- entries: Raw_Dynamic_Array,
- }
- INITIAL_MAP_CAP :: 16;
- Map_Key :: struct {
- hash: u64,
- /* NOTE(bill)
- size_of(Map_Key) == 16 Bytes on 32-bit systems
- size_of(Map_Key) == 24 Bytes on 64-bit systems
- This does mean that an extra word is wasted for each map when a string is not used on 64-bit systems
- however, this is probably not a huge problem in terms of memory usage
- */
- key: struct #raw_union {
- str: string,
- val: u64,
- },
- }
- Map_Find_Result :: struct {
- hash_index: int,
- entry_prev: int,
- entry_index: int,
- }
- Map_Entry_Header :: struct {
- key: Map_Key,
- next: int,
- /*
- value: Value_Type,
- */
- }
- Map_Header :: struct {
- m: ^Raw_Map,
- is_key_string: bool,
- entry_size: int,
- entry_align: int,
- value_offset: uintptr,
- value_size: int,
- }
- /////////////////////////////
- // Init Startup Procedures //
- /////////////////////////////
- // IMPORTANT NOTE(bill): Do not call this unless you want to explicitly set up the entry point and how it gets called
- // This is probably only useful for freestanding targets
- foreign {
- @(link_name="__$startup_runtime")
- _startup_runtime :: proc "contextless" () ---
- }
- /////////////////////////////
- /////////////////////////////
- /////////////////////////////
- type_info_base :: proc "contextless" (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 "contextless" (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 Type_Info_Opaque: base = i.elem;
- case: break loop;
- }
- }
- return base;
- }
- type_info_base_without_enum :: type_info_core;
- __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
- data := transmute(Typeid_Bit_Field)id;
- n := int(data.index);
- if n < 0 || n >= len(type_table) {
- n = 0;
- }
- return &type_table[n];
- }
- typeid_base :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_of(id);
- ti = type_info_base(ti);
- return ti.id;
- }
- typeid_core :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_base_without_enum(type_info_of(id));
- return ti.id;
- }
- typeid_base_without_enum :: typeid_core;
- @(default_calling_convention = "none")
- foreign {
- @(link_name="llvm.assume")
- assume :: proc(cond: bool) ---;
- @(link_name="llvm.debugtrap")
- debug_trap :: proc() ---;
- @(link_name="llvm.trap")
- trap :: proc() -> ! ---;
- @(link_name="llvm.readcyclecounter")
- read_cycle_counter :: proc() -> u64 ---;
- }
- default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, options: Logger_Options, location := #caller_location) {
- // Nothing
- }
- default_logger :: proc() -> Logger {
- return Logger{default_logger_proc, nil, Logger_Level.Debug, nil};
- }
- default_context :: proc "contextless" () -> Context {
- c: Context;
- __init_context(&c);
- return c;
- }
- @private
- __init_context_from_ptr :: proc "contextless" (c: ^Context, other: ^Context) {
- if c == nil {
- return;
- }
- c^ = other^;
- __init_context(c);
- }
- @private
- __init_context :: proc "contextless" (c: ^Context) {
- if c == nil {
- return;
- }
- // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contexless" calling convention
- c.allocator.procedure = default_allocator_proc;
- c.allocator.data = nil;
- c.temp_allocator.procedure = default_temp_allocator_proc;
- c.temp_allocator.data = &global_default_temp_allocator_data;
- c.thread_id = current_thread_id(); // NOTE(bill): This is "contextless" so it is okay to call
- c.assertion_failure_proc = default_assertion_failure_proc;
- c.logger.procedure = default_logger_proc;
- c.logger.data = nil;
- }
- @thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
- @builtin
- init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
- default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator);
- }
- default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
- print_caller_location(loc);
- print_string(" ");
- print_string(prefix);
- if len(message) > 0 {
- print_string(": ");
- print_string(message);
- }
- print_byte('\n');
- debug_trap();
- }
- @builtin
- copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
- n := max(0, min(len(dst), len(src)));
- if n > 0 {
- mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
- }
- return n;
- }
- @builtin
- copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
- n := max(0, min(len(dst), len(src)));
- if n > 0 {
- mem_copy(raw_data(dst), raw_data(src), n);
- }
- return n;
- }
- @builtin
- copy :: proc{copy_slice, copy_from_string};
- @builtin
- unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
- bounds_check_error_loc(loc, index, len(array));
- n := len(array)-1;
- if index != n {
- array[index] = array[n];
- }
- pop(array);
- }
- @builtin
- ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
- bounds_check_error_loc(loc, index, len(array));
- if index+1 < len(array) {
- copy(array[index:], array[index+1:]);
- }
- pop(array);
- }
- @builtin
- remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
- slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
- n := max(hi-lo, 0);
- if n > 0 {
- if hi != len(array) {
- copy(array[lo:], array[hi:]);
- }
- (^Raw_Dynamic_Array)(array).len -= n;
- }
- }
- @builtin
- pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
- assert(len(array) > 0, "", loc);
- res = array[len(array)-1];
- (^Raw_Dynamic_Array)(array).len -= 1;
- return res;
- }
- @builtin
- pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
- if len(array) == 0 {
- return;
- }
- res, ok = array[len(array)-1], true;
- (^Raw_Dynamic_Array)(array).len -= 1;
- return;
- }
- @builtin
- pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
- assert(len(array) > 0, "", loc);
- res = array[0];
- if len(array) > 1 {
- copy(array[0:], array[1:]);
- }
- (^Raw_Dynamic_Array)(array).len -= 1;
- return res;
- }
- @builtin
- pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
- if len(array) == 0 {
- return;
- }
- res, ok = array[0], true;
- if len(array) > 1 {
- copy(array[0:], array[1:]);
- }
- (^Raw_Dynamic_Array)(array).len -= 1;
- return;
- }
- @builtin
- clear :: proc{clear_dynamic_array, clear_map};
- @builtin
- reserve :: proc{reserve_dynamic_array, reserve_map};
- @builtin
- resize :: proc{resize_dynamic_array};
- @builtin
- free :: proc{mem_free};
- @builtin
- free_all :: proc{mem_free_all};
- @builtin
- delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
- mem_free(raw_data(str), allocator, loc);
- }
- @builtin
- delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
- mem_free((^byte)(str), allocator, loc);
- }
- @builtin
- delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
- mem_free(raw_data(array), array.allocator, loc);
- }
- @builtin
- delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
- mem_free(raw_data(array), allocator, loc);
- }
- @builtin
- delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
- raw := transmute(Raw_Map)m;
- delete_slice(raw.hashes);
- mem_free(raw.entries.data, raw.entries.allocator, loc);
- }
- @builtin
- delete :: proc{
- delete_string,
- delete_cstring,
- delete_dynamic_array,
- delete_slice,
- delete_map,
- };
- @builtin
- new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
- ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
- if ptr != nil { ptr^ = T{}; }
- return ptr;
- }
- @builtin
- new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
- ptr := (^T)(mem_alloc(size_of(T), align_of(T), allocator, loc));
- if ptr != nil { ptr^ = data; }
- return ptr;
- }
- make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
- make_slice_error_loc(loc, len);
- data := mem_alloc(size_of(E)*len, alignment, allocator, loc);
- if data == nil && size_of(E) != 0 {
- return nil;
- }
- // mem_zero(data, size_of(E)*len);
- s := Raw_Slice{data, len};
- return transmute(T)s;
- }
- @builtin
- make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
- return make_aligned(T, len, align_of(E), allocator, loc);
- }
- @builtin
- make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
- return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
- }
- @builtin
- make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
- return make_dynamic_array_len_cap(T, len, len, allocator, loc);
- }
- @builtin
- make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
- make_dynamic_array_error_loc(loc, len, cap);
- data := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
- s := Raw_Dynamic_Array{data, len, cap, allocator};
- if data == nil && size_of(E) != 0 {
- s.len, s.cap = 0, 0;
- }
- // mem_zero(data, size_of(E)*cap);
- return transmute(T)s;
- }
- @builtin
- make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
- make_map_expr_error_loc(loc, cap);
- context.allocator = allocator;
- m: T;
- reserve_map(&m, cap);
- return m;
- }
- @builtin
- make :: proc{
- make_slice,
- make_dynamic_array,
- make_dynamic_array_len,
- make_dynamic_array_len_cap,
- make_map,
- };
- @builtin
- clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
- if m == nil {
- return;
- }
- raw_map := (^Raw_Map)(m);
- entries := (^Raw_Dynamic_Array)(&raw_map.entries);
- entries.len = 0;
- for _, i in raw_map.hashes {
- raw_map.hashes[i] = -1;
- }
- }
- @builtin
- reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
- if m != nil {
- __dynamic_map_reserve(__get_map_header(m), capacity);
- }
- }
- @builtin
- delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
- if m != nil {
- __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key));
- }
- }
- @builtin
- append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
- if array == nil {
- return;
- }
- arg_len := 1;
- if cap(array) < len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- a := (^Raw_Dynamic_Array)(array);
- if size_of(E) != 0 {
- data := (^E)(a.data);
- assert(data != nil);
- val := arg;
- mem_copy(ptr_offset(data, a.len), &val, size_of(E));
- }
- a.len += arg_len;
- }
- }
- @builtin
- append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
- if array == nil {
- return;
- }
- arg_len := len(args);
- if arg_len <= 0 {
- return;
- }
- if cap(array) < len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- a := (^Raw_Dynamic_Array)(array);
- if size_of(E) != 0 {
- data := (^E)(a.data);
- assert(data != nil);
- mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
- }
- a.len += arg_len;
- }
- }
- @builtin
- append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
- args := transmute([]E)arg;
- append_elems(array=array, args=args, loc=loc);
- }
- @builtin
- reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- old_cap := cap(array);
- if capacity <= old_cap {
- return true;
- }
- if array.allocator.procedure == nil {
- array.allocator = context.allocator;
- }
- assert(array.allocator.procedure != nil);
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
- if field_count == 0 {
- return true;
- }
- cap_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 1)*size_of(rawptr));
- assert(cap_ptr^ == old_cap);
- old_size := 0;
- new_size := 0;
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
- old_size = align_forward_int(old_size, type.align);
- new_size = align_forward_int(new_size, type.align);
- old_size += type.size * old_cap;
- new_size += type.size * capacity;
- }
- old_size = align_forward_int(old_size, max_align);
- new_size = align_forward_int(new_size, max_align);
- old_data := (^rawptr)(array)^;
- new_data := array.allocator.procedure(
- array.allocator.data, .Alloc, new_size, max_align,
- nil, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
- cap_ptr^ = capacity;
- old_offset := 0;
- new_offset := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
- old_offset = align_forward_int(old_offset, type.align);
- new_offset = align_forward_int(new_offset, type.align);
- new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset));
- old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset));
- mem_copy(new_data_elem, old_data_elem, type.size * old_cap);
- (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem;
- old_offset += type.size * old_cap;
- new_offset += type.size * capacity;
- }
- array.allocator.procedure(
- array.allocator.data, .Free, 0, max_align,
- old_data, old_size, 0, loc,
- );
- return true;
- }
- @builtin
- append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
- if array == nil {
- return;
- }
- arg_len := 1;
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
- if field_count == 0 {
- return;
- }
- data := (^rawptr)(array)^;
- len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
- soa_offset := 0;
- item_offset := 0;
- arg_copy := arg;
- arg_ptr := &arg_copy;
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
- soa_offset = align_forward_int(soa_offset, type.align);
- item_offset = align_forward_int(item_offset, type.align);
- dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^));
- src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
- mem_copy(dst, src, type.size);
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
- len_ptr^ += arg_len;
- }
- }
- @builtin
- append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
- if array == nil {
- return;
- }
- arg_len := len(args);
- if arg_len == 0 {
- return;
- }
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- if arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(len(si.offsets) - 3);
- if field_count == 0 {
- return;
- }
- data := (^rawptr)(array)^;
- len_ptr := cast(^int)rawptr(uintptr(array) + (field_count + 0)*size_of(rawptr));
- soa_offset := 0;
- item_offset := 0;
- args_ptr := &args[0];
- max_align := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- max_align = max(max_align, type.align);
- soa_offset = align_forward_int(soa_offset, type.align);
- item_offset = align_forward_int(item_offset, type.align);
- dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * len_ptr^);
- src := uintptr(args_ptr) + uintptr(item_offset);
- for j in 0..<arg_len {
- d := rawptr(dst + uintptr(j*type.size));
- s := rawptr(src + uintptr(j*size_of(E)));
- mem_copy(d, s, type.size);
- }
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
- len_ptr^ += arg_len;
- }
- }
- @builtin
- append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
- for arg in args {
- append(array = array, args = transmute([]E)(arg), loc = loc);
- }
- }
- @builtin append :: proc{append_elem, append_elems, append_elem_string};
- @builtin append_soa :: proc{append_soa_elem, append_soa_elems};
- @builtin
- append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
- if array == nil {
- return;
- }
- resize(array, len(array)+1);
- }
- @builtin
- insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
- if array == nil {
- return;
- }
- n := len(array);
- m :: 1;
- resize(array, n+m, loc);
- if n+m <= len(array) {
- when size_of(E) != 0 {
- copy(array[index+m:], array[index:]);
- array[index] = arg;
- }
- ok = true;
- }
- return;
- }
- @builtin
- insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
- if array == nil {
- return;
- }
- if len(args) == 0 {
- ok = true;
- return;
- }
- n := len(array);
- m := len(args);
- resize(array, n+m, loc);
- if n+m <= len(array) {
- when size_of(E) != 0 {
- copy(array[index+m:], array[index:]);
- copy(array[index:], args);
- }
- ok = true;
- }
- return;
- }
- @builtin
- insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
- if array == nil {
- return;
- }
- if len(args) == 0 {
- ok = true;
- return;
- }
- n := len(array);
- m := len(args);
- resize(array, n+m, loc);
- if n+m <= len(array) {
- copy(array[index+m:], array[index:]);
- copy(array[index:], args);
- ok = true;
- }
- return;
- }
- @builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string};
- @builtin
- clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
- if array != nil {
- (^Raw_Dynamic_Array)(array).len = 0;
- }
- }
- @builtin
- reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- a := (^Raw_Dynamic_Array)(array);
- if capacity <= a.cap {
- return true;
- }
- if a.allocator.procedure == nil {
- a.allocator = context.allocator;
- }
- assert(a.allocator.procedure != nil);
- old_size := a.cap * size_of(E);
- new_size := capacity * size_of(E);
- allocator := a.allocator;
- new_data := allocator.procedure(
- allocator.data, .Resize, new_size, align_of(E),
- a.data, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
- a.data = new_data;
- a.cap = capacity;
- return true;
- }
- @builtin
- resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- a := (^Raw_Dynamic_Array)(array);
- if length <= a.cap {
- a.len = max(length, 0);
- return true;
- }
- if a.allocator.procedure == nil {
- a.allocator = context.allocator;
- }
- assert(a.allocator.procedure != nil);
- old_size := a.cap * size_of(E);
- new_size := length * size_of(E);
- allocator := a.allocator;
- new_data := allocator.procedure(
- allocator.data, .Resize, new_size, align_of(E),
- a.data, old_size, 0, loc,
- );
- if new_data == nil {
- return false;
- }
- a.data = new_data;
- a.len = length;
- a.cap = length;
- return true;
- }
- @builtin
- incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
- s^ |= {elem};
- return s^;
- }
- @builtin
- incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
- for elem in elems {
- s^ |= {elem};
- }
- return s^;
- }
- @builtin
- incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
- s^ |= other;
- return s^;
- }
- @builtin
- excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
- s^ &~= {elem};
- return s^;
- }
- @builtin
- excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
- for elem in elems {
- s^ &~= {elem};
- }
- return s^;
- }
- @builtin
- excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
- s^ &~= other;
- return s^;
- }
- @builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
- @builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
- @builtin
- card :: proc(s: $S/bit_set[$E; $U]) -> int {
- when size_of(S) == 1 {
- foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
- return int(count_ones(transmute(u8)s));
- } else when size_of(S) == 2 {
- foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
- return int(count_ones(transmute(u16)s));
- } else when size_of(S) == 4 {
- foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
- return int(count_ones(transmute(u32)s));
- } else when size_of(S) == 8 {
- foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
- return int(count_ones(transmute(u64)s));
- } else when size_of(S) == 16 {
- foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
- return int(count_ones(transmute(u128)s));
- } else {
- #panic("Unhandled card bit_set size");
- }
- }
- @builtin
- raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
- return (^E)(a);
- }
- @builtin
- raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
- ptr := (transmute(Raw_Slice)s).data;
- return (^E)(ptr);
- }
- @builtin
- raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
- ptr := (transmute(Raw_Dynamic_Array)s).data;
- return (^E)(ptr);
- }
- @builtin
- raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
- return (transmute(Raw_String)s).data;
- }
- @builtin
- raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data};
- @builtin
- @(disabled=ODIN_DISABLE_ASSERT)
- assert :: proc(condition: bool, message := "", loc := #caller_location) {
- if !condition {
- proc(message: string, loc: Source_Code_Location) {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- p("runtime assertion", message, loc);
- }(message, loc);
- }
- }
- @builtin
- @(disabled=ODIN_DISABLE_ASSERT)
- panic :: proc(message: string, loc := #caller_location) -> ! {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- p("panic", message, loc);
- }
- @builtin
- @(disabled=ODIN_DISABLE_ASSERT)
- unimplemented :: proc(message := "", loc := #caller_location) -> ! {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- p("not yet implemented", message, loc);
- }
- @builtin
- @(disabled=ODIN_DISABLE_ASSERT)
- unreachable :: proc(message := "", loc := #caller_location) -> ! {
- p := context.assertion_failure_proc;
- if p == nil {
- p = default_assertion_failure_proc;
- }
- if message != "" {
- p("internal error", message, loc);
- } else {
- p("internal error", "entered unreachable code", loc);
- }
- }
- // Dynamic Array
- __dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
- array := (^Raw_Dynamic_Array)(array_);
- array.allocator = context.allocator;
- assert(array.allocator.procedure != nil);
- if cap > 0 {
- __dynamic_array_reserve(array_, elem_size, elem_align, cap, loc);
- array.len = len;
- }
- }
- __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
- array := (^Raw_Dynamic_Array)(array_);
- // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
- // assuming that appending/reserving will set the allocator, if it is not already set.
- if array.allocator.procedure == nil {
- array.allocator = context.allocator;
- }
- assert(array.allocator.procedure != nil);
- if cap <= array.cap {
- return true;
- }
- old_size := array.cap * elem_size;
- new_size := cap * elem_size;
- allocator := array.allocator;
- new_data := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, 0, loc);
- if new_data != nil || elem_size == 0 {
- array.data = new_data;
- array.cap = cap;
- return true;
- }
- return false;
- }
- __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
- array := (^Raw_Dynamic_Array)(array_);
- ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
- if ok {
- array.len = len;
- }
- return ok;
- }
- __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
- items: rawptr, item_count: int, loc := #caller_location) -> int {
- array := (^Raw_Dynamic_Array)(array_);
- if items == nil {
- return 0;
- }
- if item_count <= 0 {
- return 0;
- }
- ok := true;
- if array.cap <= array.len+item_count {
- cap := 2 * array.cap + max(8, item_count);
- ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
- }
- // TODO(bill): Better error handling for failed reservation
- if !ok {
- return array.len;
- }
- assert(array.data != nil);
- data := uintptr(array.data) + uintptr(elem_size*array.len);
- mem_copy(rawptr(data), items, elem_size * item_count);
- array.len += item_count;
- return array.len;
- }
- __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
- array := (^Raw_Dynamic_Array)(array_);
- ok := true;
- if array.cap <= array.len+1 {
- cap := 2 * array.cap + max(8, 1);
- ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc);
- }
- // TODO(bill): Better error handling for failed reservation
- if !ok {
- return array.len;
- }
- assert(array.data != nil);
- data := uintptr(array.data) + uintptr(elem_size*array.len);
- mem_zero(rawptr(data), elem_size);
- array.len += 1;
- return array.len;
- }
- // Map
- __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
- header := Map_Header{m = (^Raw_Map)(m)};
- Entry :: struct {
- key: Map_Key,
- next: int,
- value: V,
- };
- header.is_key_string = intrinsics.type_is_string(K);
- header.entry_size = int(size_of(Entry));
- header.entry_align = int(align_of(Entry));
- header.value_offset = uintptr(offset_of(Entry, value));
- header.value_size = int(size_of(V));
- return header;
- }
- __get_map_key :: proc "contextless" (k: $K) -> Map_Key {
- key := k;
- map_key: Map_Key;
- T :: intrinsics.type_core_type(K);
- when intrinsics.type_is_integer(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
- sz :: 8*size_of(T);
- when sz == 8 { map_key.key.val = u64(( ^u8)(&key)^); }
- else when sz == 16 { map_key.key.val = u64((^u16)(&key)^); }
- else when sz == 32 { map_key.key.val = u64((^u32)(&key)^); }
- else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); }
- else { #panic("Unhandled integer size"); }
- } else when intrinsics.type_is_rune(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
- map_key.key.val = u64((^rune)(&key)^);
- } else when intrinsics.type_is_pointer(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
- map_key.key.val = u64(uintptr((^rawptr)(&key)^));
- } else when intrinsics.type_is_float(T) {
- map_key.hash = default_hash_ptr(&key, size_of(T));
- sz :: 8*size_of(T);
- when sz == 32 { map_key.key.val = u64((^u32)(&key)^); }
- else when sz == 64 { map_key.key.val = u64((^u64)(&key)^); }
- else { #panic("Unhandled float size"); }
- } else when intrinsics.type_is_string(T) {
- #assert(T == string);
- str := (^string)(&key)^;
- map_key.hash = default_hash_string(str);
- map_key.key.str = str;
- } else {
- #panic("Unhandled map key type");
- }
- return map_key;
- }
- _fnv64a :: proc "contextless" (data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 {
- h: u64 = seed;
- for b in data {
- h = (h ~ u64(b)) * 0x100000001b3;
- }
- return h;
- }
- default_hash :: inline proc "contextless" (data: []byte) -> u64 {
- return _fnv64a(data);
- }
- default_hash_string :: inline proc "contextless" (s: string) -> u64 {
- return default_hash(transmute([]byte)(s));
- }
- default_hash_ptr :: inline proc "contextless" (data: rawptr, size: int) -> u64 {
- s := Raw_Slice{data, size};
- return default_hash(transmute([]byte)(s));
- }
- source_code_location_hash :: proc(s: Source_Code_Location) -> u64 {
- hash := _fnv64a(transmute([]byte)s.file_path);
- hash = hash ~ (u64(s.line) * 0x100000001b3);
- hash = hash ~ (u64(s.column) * 0x100000001b3);
- return hash;
- }
- __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
- array := (^Raw_Slice)(array_);
- if new_count < array.len {
- return true;
- }
- assert(allocator.procedure != nil);
- old_size := array.len*size_of(T);
- new_size := new_count*size_of(T);
- new_data := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc);
- if new_data == nil {
- return false;
- }
- array.data = new_data;
- array.len = new_count;
- return true;
- }
- __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
- __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc);
- old_len := len(m.hashes);
- __slice_resize(&m.hashes, cap, m.entries.allocator, loc);
- for i in old_len..<len(m.hashes) {
- m.hashes[i] = -1;
- }
- }
- __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
- new_header: Map_Header = header;
- nm := Raw_Map{};
- nm.entries.allocator = m.entries.allocator;
- new_header.m = &nm;
- c := context;
- if m.entries.allocator.procedure != nil {
- c.allocator = m.entries.allocator;
- }
- context = c;
- __dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
- __slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
- for i in 0 ..< new_count {
- nm.hashes[i] = -1;
- }
- for i in 0 ..< m.entries.len {
- if len(nm.hashes) == 0 {
- __dynamic_map_grow(new_header, loc);
- }
- entry_header := __dynamic_map_get_entry(header, i);
- data := uintptr(entry_header);
- fr := __dynamic_map_find(new_header, entry_header.key);
- j := __dynamic_map_add_entry(new_header, entry_header.key, loc);
- if fr.entry_prev < 0 {
- nm.hashes[fr.hash_index] = j;
- } else {
- e := __dynamic_map_get_entry(new_header, fr.entry_prev);
- e.next = j;
- }
- e := __dynamic_map_get_entry(new_header, j);
- e.next = fr.entry_index;
- ndata := uintptr(e);
- mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size);
- if __dynamic_map_full(new_header) {
- __dynamic_map_grow(new_header, loc);
- }
- }
- delete(m.hashes, m.entries.allocator, loc);
- free(m.entries.data, m.entries.allocator, loc);
- header.m^ = nm;
- }
- __dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr {
- index := __dynamic_map_find(h, key).entry_index;
- if index >= 0 {
- data := uintptr(__dynamic_map_get_entry(h, index));
- return rawptr(data + h.value_offset);
- }
- return nil;
- }
- __dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check {
- index: int;
- assert(value != nil);
- if len(h.m.hashes) == 0 {
- __dynamic_map_reserve(h, INITIAL_MAP_CAP, loc);
- __dynamic_map_grow(h, loc);
- }
- fr := __dynamic_map_find(h, key);
- if fr.entry_index >= 0 {
- index = fr.entry_index;
- } else {
- index = __dynamic_map_add_entry(h, key, loc);
- if fr.entry_prev >= 0 {
- entry := __dynamic_map_get_entry(h, fr.entry_prev);
- entry.next = index;
- } else {
- h.m.hashes[fr.hash_index] = index;
- }
- }
- {
- e := __dynamic_map_get_entry(h, index);
- e.key = key;
- val := (^byte)(uintptr(e) + h.value_offset);
- mem_copy(val, value, h.value_size);
- }
- if __dynamic_map_full(h) {
- __dynamic_map_grow(h, loc);
- }
- }
- __dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
- // TODO(bill): Determine an efficient growing rate
- new_count := max(4*m.entries.cap + 7, INITIAL_MAP_CAP);
- __dynamic_map_rehash(h, new_count, loc);
- }
- __dynamic_map_full :: inline proc(using h: Map_Header) -> bool {
- return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
- }
- __dynamic_map_hash_equal :: proc(h: Map_Header, a, b: Map_Key) -> bool {
- if a.hash == b.hash {
- if h.is_key_string {
- return a.key.str == b.key.str;
- } else {
- return a.key.val == b.key.val;
- }
- return true;
- }
- return false;
- }
- __dynamic_map_find :: proc(using h: Map_Header, key: Map_Key) -> Map_Find_Result #no_bounds_check {
- fr := Map_Find_Result{-1, -1, -1};
- if n := u64(len(m.hashes)); n > 0 {
- fr.hash_index = int(key.hash % n);
- fr.entry_index = m.hashes[fr.hash_index];
- for fr.entry_index >= 0 {
- entry := __dynamic_map_get_entry(h, fr.entry_index);
- if __dynamic_map_hash_equal(h, entry.key, key) {
- return fr;
- }
- fr.entry_prev = fr.entry_index;
- fr.entry_index = entry.next;
- }
- }
- return fr;
- }
- __dynamic_map_add_entry :: proc(using h: Map_Header, key: Map_Key, loc := #caller_location) -> int {
- prev := m.entries.len;
- c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align, loc);
- if c != prev {
- end := __dynamic_map_get_entry(h, c-1);
- end.key = key;
- end.next = -1;
- }
- return prev;
- }
- __dynamic_map_delete_key :: proc(using h: Map_Header, key: Map_Key) {
- fr := __dynamic_map_find(h, key);
- if fr.entry_index >= 0 {
- __dynamic_map_erase(h, fr);
- }
- }
- __dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
- assert(0 <= index && index < m.entries.len);
- return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size));
- }
- __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
- if fr.entry_prev < 0 {
- m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
- } else {
- prev := __dynamic_map_get_entry(h, fr.entry_prev);
- curr := __dynamic_map_get_entry(h, fr.entry_index);
- prev.next = curr.next;
- }
- if (fr.entry_index == m.entries.len-1) {
- // NOTE(bill): No need to do anything else, just pop
- } else {
- old := __dynamic_map_get_entry(h, fr.entry_index);
- end := __dynamic_map_get_entry(h, m.entries.len-1);
- mem_copy(old, end, entry_size);
- if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 {
- last_entry := __dynamic_map_get_entry(h, last.entry_prev);
- last_entry.next = fr.entry_index;
- } else {
- m.hashes[last.hash_index] = fr.entry_index;
- }
- }
- // TODO(bill): Is this correct behaviour?
- m.entries.len -= 1;
- }
|