@@ -19,23 +19,92 @@ namespace memory {
1919
2020namespace script {
2121
22- constexpr int64_t MALLOC_REPLACER_SIZE_OFFSET = sizeof (size_t );
23- constexpr uint64_t MALLOC_REPLACER_MAX_ALLOC = 0xFFFFFF00 ;
22+ constexpr uint64_t MALLOC_REPLACER_MAX_ALLOC = 0xFFFFFF00 ; // 4GiB
23+
24+ namespace details {
25+ struct control_block {
26+ static constexpr auto SIZE_FIELD_BITSIZE{48 };
27+ static constexpr auto BASE_OFFSET_FIELD_BITSIZE{16 };
28+ static constexpr uint64_t BLOCK_SIZE_MASK{(1UL << SIZE_FIELD_BITSIZE) - 1 };
29+ static constexpr uint64_t BASE_OFFSET_MASK{(1UL << BASE_OFFSET_FIELD_BITSIZE) - 1 };
30+
31+ static constexpr uint64_t max_size () noexcept {
32+ return 1UL << SIZE_FIELD_BITSIZE;
33+ }
34+
35+ static constexpr uint64_t max_alignment () noexcept {
36+ return 1UL << BASE_OFFSET_FIELD_BITSIZE;
37+ }
38+
39+ uint64_t raw () const noexcept {
40+ return (static_cast <std::uint64_t >(base_offset) << SIZE_FIELD_BITSIZE) | (static_cast <std::uint64_t >(size) & BLOCK_SIZE_MASK);
41+ }
42+
43+ static control_block from_raw (std::uint64_t raw) noexcept {
44+ return control_block{.size = raw & BLOCK_SIZE_MASK, .base_offset = static_cast <uint16_t >((raw >> SIZE_FIELD_BITSIZE) & BASE_OFFSET_MASK)};
45+ }
46+
47+ uint64_t size : SIZE_FIELD_BITSIZE;
48+ uint16_t base_offset : BASE_OFFSET_FIELD_BITSIZE;
49+ };
50+
51+ inline bool is_power_of_2 (uint64_t v) noexcept {
52+ return v && !(v & (v - 1 ));
53+ }
54+
55+ static_assert (sizeof (control_block) == sizeof (uint64_t ), " Control block's size must be equal to uint64" );
56+
57+ } // namespace details
2458
2559inline void * alloc (size_t size) noexcept {
26- if (unlikely (size > MALLOC_REPLACER_MAX_ALLOC - MALLOC_REPLACER_SIZE_OFFSET)) {
27- php_warning (" attempt to allocate too much memory by malloc replacer : %lu" , size);
60+ const size_t cb_size{sizeof (details::control_block)};
61+ if (unlikely (size > std::min (details::control_block::max_size (), MALLOC_REPLACER_MAX_ALLOC) - cb_size)) {
62+ php_warning (" attempt to allocate too much memory by malloc replacer, requested : %lu" , size);
63+ return nullptr ;
64+ }
65+ const size_t total_size{size + cb_size};
66+ void * base{RuntimeAllocator::get ().alloc_script_memory (total_size)};
67+ if (unlikely (base == nullptr )) {
68+ php_warning (" not enough script memory to allocate, requested : %lu, actual requested: %lu" , size, total_size);
69+ return base;
70+ }
71+ *(reinterpret_cast <std::uint64_t *>(base)) = details::control_block{.size = total_size, .base_offset = cb_size}.raw ();
72+ return reinterpret_cast <void *>(reinterpret_cast <uint64_t >(base) + cb_size); // NOLINT
73+ }
74+
75+ inline void * alloc_aligned (size_t size, std::align_val_t alignment) noexcept {
76+ // Check that provided alignment is power of two
77+ const size_t align{static_cast <uint64_t >(alignment)};
78+ if (unlikely (align == 0 || !details::is_power_of_2 (align) || align >= details::control_block::max_alignment ())) {
79+ php_warning (" allocation alignment have to be non-zero power of two and not greater than %lu, got : %lu" , details::control_block::max_alignment (), align);
2880 return nullptr ;
2981 }
30- const size_t real_size{size + MALLOC_REPLACER_SIZE_OFFSET};
31- void * ptr{RuntimeAllocator::get ().alloc_script_memory (real_size)};
3282
33- if (unlikely (ptr == nullptr )) {
34- php_warning (" not enough script memory to allocate: %lu" , size);
35- return ptr;
83+ // Check that memory is enough
84+ const size_t cb_size{sizeof (details::control_block)};
85+ if (unlikely (size > std::min (details::control_block::max_size (), MALLOC_REPLACER_MAX_ALLOC) - (align - 1 ) - cb_size)) {
86+ php_warning (" attempt to allocate too much memory by malloc replacer, requested : %lu" , size);
87+ return nullptr ;
88+ }
89+
90+ // Request mem from underlying memory manager
91+ const size_t total_size{size + (align - 1 ) + cb_size};
92+ void * base{RuntimeAllocator::get ().alloc_script_memory (total_size)};
93+ if (unlikely (base == nullptr )) {
94+ php_warning (" not enough script memory to allocate, requested : %lu, actual requested: %lu" , size, total_size);
95+ return base;
3696 }
37- *static_cast <size_t *>(ptr) = real_size;
38- return static_cast <std::byte*>(ptr) + MALLOC_REPLACER_SIZE_OFFSET;
97+
98+ const uint64_t base_u{reinterpret_cast <uint64_t >(base)};
99+ // The smallest multiple of `align` greater than or equal to requested memory
100+ const uint64_t aligned_u{((base_u + cb_size) + (align - 1 )) & ~(align - 1 )};
101+ const uint64_t base_offset_u{aligned_u - base_u};
102+
103+ // Save control block
104+ *(reinterpret_cast <std::uint64_t *>(aligned_u - cb_size)) = // NOLINT
105+ details::control_block{.size = total_size, .base_offset = static_cast <std::uint16_t >(base_offset_u)}.raw ();
106+
107+ return reinterpret_cast <void *>(aligned_u); // NOLINT
39108}
40109
41110inline void * calloc (size_t num, size_t size) noexcept {
@@ -47,10 +116,17 @@ inline void* calloc(size_t num, size_t size) noexcept {
47116}
48117
49118inline void free (void * ptr) noexcept {
50- if (likely (ptr != nullptr )) {
51- void * real_ptr{static_cast <std::byte*>(ptr) - MALLOC_REPLACER_SIZE_OFFSET};
52- RuntimeAllocator::get ().free_script_memory (real_ptr, *static_cast <size_t *>(real_ptr));
119+ if (unlikely (ptr == nullptr )) {
120+ return ;
53121 }
122+
123+ const size_t cb_size{sizeof (details::control_block)};
124+ const auto mem{reinterpret_cast <uint64_t >(ptr)};
125+
126+ const auto cb{details::control_block::from_raw (*reinterpret_cast <uint64_t *>(mem - cb_size))}; // NOLINT
127+ void * base{reinterpret_cast <void *>(mem - cb.base_offset )}; // NOLINT
128+
129+ RuntimeAllocator::get ().free_script_memory (base, cb.size );
54130}
55131
56132inline void * realloc (void * ptr, size_t new_size) noexcept {
@@ -63,13 +139,18 @@ inline void* realloc(void* ptr, size_t new_size) noexcept {
63139 return nullptr ;
64140 }
65141
66- void * real_ptr{static_cast <std::byte*>(ptr) - sizeof (size_t )};
67- const size_t old_size{*static_cast <size_t *>(real_ptr)};
142+ const size_t cb_size{sizeof (details::control_block)};
143+ const auto mem{reinterpret_cast <uint64_t >(ptr)};
144+
145+ const auto cb{details::control_block::from_raw (*reinterpret_cast <uint64_t *>(mem - cb_size))}; // NOLINT
146+
147+ void * old_base{reinterpret_cast <void *>(mem - cb.base_offset )}; // NOLINT
148+ const size_t old_size{cb.size };
68149
69150 void * new_ptr{kphp::memory::script::alloc (new_size)};
70151 if (likely (new_ptr != nullptr )) {
71152 std::memcpy (new_ptr, ptr, std::min (new_size, old_size));
72- RuntimeAllocator::get ().free_script_memory (real_ptr , old_size);
153+ RuntimeAllocator::get ().free_script_memory (old_base , old_size);
73154 }
74155 return new_ptr;
75156}
0 commit comments