diff --git a/common/relptr/relptr.odin b/common/relptr/relptr.odin new file mode 100644 index 0000000..27fbe72 --- /dev/null +++ b/common/relptr/relptr.odin @@ -0,0 +1,102 @@ +// Relative pointer +package relptr + +import "base:intrinsics" + +Ptr :: struct($T: typeid) { + offset: uintptr, +} + +Slice :: struct($T: typeid) { + offset: uintptr, + len: int, +} + +SOA_Slice :: struct($T: typeid) { + // Offset for each field of SOA struct + offsets: [len( + T, + ) when intrinsics.type_is_array(T) else intrinsics.type_struct_field_count(T)]uintptr, + len: int, +} + +from_rawptr :: #force_inline proc($T: typeid, addr: rawptr, base := context.user_ptr) -> Ptr(T) { + offset := uintptr(addr) - uintptr(base) + assert(offset >= 0, "ptr does not belong to this base") + + return Ptr(T){offset = offset} +} + +from_ptr :: #force_inline proc(addr: ^$T, base := context.user_ptr) -> Ptr(T) { + offset := uintptr(rawptr(addr)) - uintptr(base) + assert(offset >= 0, "ptr does not belong to this base") + + return Ptr(T){offset = offset} +} + +from_multi_ptr :: #force_inline proc(addr: [^]$T, base := context.user_ptr) -> Ptr(T) { + offset := uintptr(rawptr(addr)) - uintptr(base) + assert(offset >= 0, "ptr does not belong to this base") + + return Ptr(T){offset = offset} +} + +from_slice :: #force_inline proc(slice: []$T, base := context.user_ptr) -> Slice(T) { + offset := uintptr(rawptr(raw_data(slice))) - uintptr(base) + assert(offset >= 0, "ptr does not belong to this base") + + return Slice(T){offset = offset, len = len(slice)} +} + +from_soa_slice :: #force_inline proc(slice: #soa[]$T, base := context.user_ptr) -> SOA_Slice(T) { + slice := slice + + result: SOA_Slice(T) + + FIELD_COUNT :: + (len(T) when intrinsics.type_is_array(T) else intrinsics.type_struct_field_count(T)) + + // SOA slice is just an array of pointers to each member + a footer + src_ptrs := (transmute([^]uintptr)(&slice))[:FIELD_COUNT] + + for i in 0 ..< len(result.offsets) { + result.offsets[i] = src_ptrs[i] - uintptr(base) + } + result.len = len(slice) + + return result +} + +deref_ptr :: #force_inline proc(ptr: Ptr($T), base := context.user_ptr) -> ^T { + return transmute(^T)(uintptr(base) + ptr.offset) +} + +deref_multi_ptr :: #force_inline proc(ptr: Ptr($T), base := context.user_ptr) -> [^]T { + return transmute([^]T)(uintptr(base) + ptr.offset) +} + +deref_slice :: #force_inline proc(slice: Slice($T), base := context.user_ptr) -> []T { + return (transmute([^]T)(uintptr(base) + slice.offset))[:slice.len] +} + +deref_soa_slice :: #force_inline proc(slice: SOA_Slice($T), base := context.user_ptr) -> #soa[]T { + result: #soa[]T + + footer := raw_soa_footer_slice(&result) + + FIELD_COUNT :: + (len(T) when intrinsics.type_is_array(T) else intrinsics.type_struct_field_count(T)) + + // Just in case SOA layout changes + #assert(size_of(result) == (FIELD_COUNT * size_of(rawptr)) + size_of(footer)) + + // SOA slice is just an array of pointers to each member + a footer + result_ptrs := (transmute([^]uintptr)(&result))[:FIELD_COUNT] + for i in 0 ..< len(slice.offsets) { + result_ptrs[i] = uintptr(base) + slice.offsets[i] + } + + footer.len = slice.len + + return result +}