// tiny_nextptr.h - Safe load/store for header-aware next pointers // // Context: // - Tiny classes 0–6 place a 1-byte header immediately before the user pointer // - Freelist "next" is stored inside the block at an offset that depends on class // - Many hot paths currently cast to void** at base+1, which is unaligned and UB in C // // This header centralizes the offset calculation and uses memcpy-based loads/stores // to avoid undefined behavior from unaligned pointer access. Compilers will optimize // these to efficient byte moves on x86_64 while remaining standards-compliant. #ifndef TINY_NEXTPTR_H #define TINY_NEXTPTR_H #include #include #include "hakmem_build_flags.h" // Compute freelist next-pointer offset within a block for the given class. // - Class 7 (1024B) is headerless → next at offset 0 (block base) // - Classes 0–6 have 1-byte header → next at offset 1 static inline __attribute__((always_inline)) size_t tiny_next_off(int class_idx) { #if HAKMEM_TINY_HEADER_CLASSIDX return (class_idx == 7) ? 0 : 1; #else (void)class_idx; return 0; #endif } // Safe load of next pointer from a block base static inline __attribute__((always_inline)) void* tiny_next_load(const void* base, int class_idx) { size_t off = tiny_next_off(class_idx); #if HAKMEM_TINY_HEADER_CLASSIDX if (__builtin_expect(off != 0, 0)) { void* next = NULL; const uint8_t* p = (const uint8_t*)base + off; memcpy(&next, p, sizeof(void*)); return next; } #endif // Either headers are disabled, or this class uses offset 0 (aligned) return *(void* const*)base; } // Safe store of next pointer into a block base static inline __attribute__((always_inline)) void tiny_next_store(void* base, int class_idx, void* next) { size_t off = tiny_next_off(class_idx); #if HAKMEM_TINY_HEADER_CLASSIDX if (__builtin_expect(off != 0, 0)) { uint8_t* p = (uint8_t*)base + off; memcpy(p, &next, sizeof(void*)); return; } #endif *(void**)base = next; } #endif // TINY_NEXTPTR_H