Created
September 6, 2024 18:58
-
-
Save laytan/5fd05cb48a570209dbe602a3220359aa to your computer and use it in GitHub Desktop.
Odin Deep Clone (WIP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
deep_clone :: proc(value: $T, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (clone: T) { | |
internal_deep_clone :: proc(value: any, clone: any, ptrs: ^map[rawptr]rawptr) { | |
// Forgive me father for I have sinned. | |
using reflect | |
value_core := any_core(value) | |
clone_core := any_core(clone) | |
assert(value_core.id == clone_core.id) | |
fti := type_info_of(value_core.id) | |
switch ti in fti.variant { | |
case Type_Info_Named, Type_Info_Enum, Type_Info_Parameters, Type_Info_Soa_Pointer: | |
unreachable() | |
case Type_Info_Multi_Pointer, Type_Info_Relative_Pointer, Type_Info_Relative_Multi_Pointer: | |
fmt.panicf("can not deep clone %v", ti) | |
case Type_Info_Pointer: | |
// NOTE: we can't follow through the pointer using rtti so this is technically bad. | |
// if ti.elem == nil { | |
// panic("can not deep clone rawptr") | |
// } | |
(^rawptr)(clone_core.data)^ = value_core.data | |
return | |
case Type_Info_Procedure: | |
(^rawptr)(clone_core.data)^ = value_core.data | |
return | |
case Type_Info_Type_Id: | |
(^typeid)(clone_core.data)^ = (^typeid)(value_core.data)^ | |
return | |
case Type_Info_Rune: | |
(^rune)(clone_core.data)^ = (^rune)(value_core.data)^ | |
return | |
case Type_Info_Integer: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
return | |
case Type_Info_Float: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
return | |
case Type_Info_Complex: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
return | |
case Type_Info_Quaternion: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
return | |
case Type_Info_Boolean: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
return | |
case Type_Info_Bit_Set: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
return | |
case Type_Info_Simd_Vector: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
assert(!is_pointer_internally(ti.elem), "is this a thing?") | |
return | |
case Type_Info_Matrix: | |
runtime.mem_copy(clone_core.data, value_core.data, fti.size) | |
assert(!is_pointer_internally(ti.elem), "is this a thing?") | |
return | |
case Type_Info_Struct: | |
fields := struct_fields_zipped(value_core.id) | |
for field in fields { | |
value_field := any{rawptr(uintptr(value.data) + field.offset), field.type.id} | |
clone_field := any{rawptr(uintptr(clone.data) + field.offset), field.type.id} | |
internal_deep_clone(value_field, clone_field, ptrs) | |
} | |
return | |
case Type_Info_String: | |
if ti.is_cstring { | |
raw := (^cstring)(value_core.data)^ | |
if cloned, has_cloned := ptrs[rawptr(raw)]; has_cloned { | |
(^cstring)(clone_core.data)^ = cstring(cloned) | |
return | |
} | |
as_string := string(raw) | |
bytes, err := make([]byte, len(as_string)+1) | |
if err != nil { | |
panic("allocation failure") | |
} | |
copy(bytes, as_string) | |
ptrs[rawptr(raw)] = raw_data(bytes) | |
(^cstring)(clone_core.data)^ = cstring(raw_data(bytes)) | |
return | |
} else { | |
value_raw := (^runtime.Raw_String)(value_core.data) | |
clone_raw := (^runtime.Raw_String)(value_core.data) | |
clone_raw.len = value_raw.len | |
if cloned, has_cloned := ptrs[value_raw.data]; has_cloned { | |
clone_raw.data = ([^]byte)(cloned) | |
return | |
} | |
bytes, err := make([]byte, value_raw.len) | |
if err != nil { | |
panic("allocation failure") | |
} | |
copy(bytes, (^string)(value_raw)^) | |
ptrs[value_raw.data] = raw_data(bytes) | |
clone_raw.data = raw_data(bytes) | |
return | |
} | |
case Type_Info_Array: | |
for i in 0..<ti.count { | |
value_field := index(value_core, i) | |
clone_field := index(clone_core, i) | |
internal_deep_clone(value_field, clone_field, ptrs) | |
} | |
return | |
case Type_Info_Enumerated_Array: | |
for i in 0..<ti.count { | |
value_field := index(value_core, i) | |
clone_field := index(clone_core, i) | |
internal_deep_clone(value_field, clone_field, ptrs) | |
} | |
return | |
case Type_Info_Dynamic_Array: | |
value_raw := (^runtime.Raw_Dynamic_Array)(value_core.data) | |
clone_raw := (^runtime.Raw_Dynamic_Array)(value_core.data) | |
if cloned, has_cloned := ptrs[value_raw.data]; has_cloned { | |
clone_raw^ = value_raw^ | |
clone_raw.data = cloned | |
return | |
} | |
if err := runtime._make_dynamic_array_len_cap(clone_raw, ti.elem_size, ti.elem.align, value_raw.len, value_raw.cap); err != nil { | |
panic("allocation failed") | |
} | |
ptrs[value_raw.data] = clone_raw.data | |
for i in 0..<value_raw.len { | |
value_field := index(value_core, i) | |
clone_field := index(clone_core, i) | |
internal_deep_clone(value_field, clone_field, ptrs) | |
} | |
return | |
case Type_Info_Slice: | |
// NOTE: this may not be correct if you've sliced this from the base like `[1:]`. | |
value_raw := (^runtime.Raw_Slice)(value_core.data) | |
clone_raw := (^runtime.Raw_Slice)(value_core.data) | |
if cloned, has_cloned := ptrs[value_raw.data]; has_cloned { | |
clone_raw^ = value_raw^ | |
clone_raw.data = cloned | |
return | |
} | |
data, err := runtime.mem_alloc(ti.elem_size*value_raw.len, ti.elem.align) | |
if err != nil { | |
panic("allocation failed") | |
} | |
ptr := raw_data(data) | |
clone_raw.data = ptr | |
clone_raw.len = value_raw.len | |
ptrs[value_raw.data] = clone_raw.data | |
for i in 0..<value_raw.len { | |
value_field := index(value_core, i) | |
clone_field := index(clone_core, i) | |
internal_deep_clone(value_field, clone_field, ptrs) | |
} | |
return | |
// TODO: | |
case Type_Info_Any: | |
case Type_Info_Union: | |
case Type_Info_Map: | |
case Type_Info_Bit_Field: | |
} | |
fmt.panicf("unhandled deep clone of %v", fti) | |
} | |
context.allocator = allocator | |
context.temp_allocator = temp_allocator | |
ptrs: map[rawptr]rawptr | |
ptrs.allocator = context.temp_allocator | |
internal_deep_clone(value, clone, &ptrs) | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment