+
+static inline void* untag_ptr(uint64_t ptr) {
+ if (ptr < 4096) return (void*)ptr;
+ if (sizeof(void*) == 4) {
+ // For 32-bit systems, store pointers as 64-bit ints and use the 31st bit
+ return (void*)(uintptr_t)ptr;
+ } else {
+ // For 64-bit systems, assume the top byte is used for tagging, then
+ // use bit 9 ^ bit 10.
+ uint64_t tenth_bit = (((uintptr_t)ptr) & (1ULL << 54)) >> 54;
+ uintptr_t p = (ptr & ~(1ULL << 55)) | (tenth_bit << 55);
+#ifdef LDK_DEBUG_BUILD
+ // On debug builds we also use the 11th bit as a debug flag
+ uintptr_t eleventh_bit = (((uintptr_t)ptr) & (1ULL << 53)) >> 53;
+ CHECK(tenth_bit != eleventh_bit);
+ p ^= 1ULL << 53;
+#endif
+ return (void*)p;
+ }
+}
+static inline bool ptr_is_owned(uint64_t ptr) {
+ if(ptr < 4096) return true;
+ if (sizeof(void*) == 4) {
+ return ptr & (1ULL << 32);
+ } else {
+ uintptr_t ninth_bit = (((uintptr_t)ptr) & (1ULL << 55)) >> 55;
+ uintptr_t tenth_bit = (((uintptr_t)ptr) & (1ULL << 54)) >> 54;
+#ifdef LDK_DEBUG_BUILD
+ // On debug builds we also use the 11th bit as a debug flag
+ uintptr_t eleventh_bit = (((uintptr_t)ptr) & (1ULL << 53)) >> 53;
+ CHECK(tenth_bit != eleventh_bit);
+#endif
+ return (ninth_bit ^ tenth_bit) ? true : false;
+ }
+}
+static inline uint64_t tag_ptr(const void* ptr, bool is_owned) {
+ if ((uintptr_t)ptr < 4096) return (uint64_t)ptr;
+ if (sizeof(void*) == 4) {
+ return (((uint64_t)ptr) | ((is_owned ? 1ULL : 0) << 32));
+ } else {
+ CHECK(sizeof(uintptr_t) == 8);
+ uintptr_t tenth_bit = (((uintptr_t)ptr) & (1ULL << 54)) >> 54;
+ uintptr_t t = (((uintptr_t)ptr) | (((is_owned ? 1ULL : 0ULL) ^ tenth_bit) << 55));
+#ifdef LDK_DEBUG_BUILD
+ uintptr_t ninth_bit = (((uintptr_t)ptr) & (1ULL << 55)) >> 55;
+ uintptr_t eleventh_bit = (((uintptr_t)ptr) & (1ULL << 53)) >> 53;
+ CHECK(ninth_bit == tenth_bit);
+ CHECK(ninth_bit == eleventh_bit);
+ t ^= 1ULL << 53;
+#endif
+ CHECK(ptr_is_owned(t) == is_owned);
+ CHECK(untag_ptr(t) == ptr);
+ return t;
+ }
+}
+