Skip to content

Instantly share code, notes, and snippets.

@laytan
Created September 6, 2024 18:58
Show Gist options
  • Save laytan/5fd05cb48a570209dbe602a3220359aa to your computer and use it in GitHub Desktop.
Save laytan/5fd05cb48a570209dbe602a3220359aa to your computer and use it in GitHub Desktop.
Odin Deep Clone (WIP)
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