/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/aliased_struct.h
248 строк
8 KB
James M Snell
quic: add aliased struct arenas
22 май 2026, 00:16
22 май 2026, 00:16
fdbfb68
Код
Авторство
О чём код?
#ifndef SRC_ALIASED_STRUCT_H_ #define SRC_ALIASED_STRUCT_H_ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include <memory> #include <vector> #include "node_internals.h" #include "v8.h" namespace node { // AliasedStruct is a utility that allows uses a V8 Backing Store // to be exposed to the C++/C side as a struct and to the // JavaScript side as an ArrayBuffer to efficiently share // data without marshalling. It is similar in nature to // AliasedBuffer. // // struct Foo { int x; } // // AliasedStruct<Foo> foo; // foo->x = 1; // // Local<ArrayBuffer> ab = foo.GetArrayBuffer(); template <typename T> class AliasedStruct final { public: template <typename... Args> explicit AliasedStruct(v8::Isolate* isolate, Args&&... args); inline AliasedStruct(const AliasedStruct& that); inline ~AliasedStruct(); inline AliasedStruct& operator=(AliasedStruct&& that) noexcept; v8::Local<v8::ArrayBuffer> GetArrayBuffer() const { return buffer_.Get(isolate_); } const T* Data() const { return ptr_; } T* Data() { return ptr_; } const T& operator*() const { return *ptr_; } T& operator*() { return *ptr_; } const T* operator->() const { return ptr_; } T* operator->() { return ptr_; } private: v8::Isolate* isolate_; std::shared_ptr<v8::BackingStore> store_; T* ptr_; v8::Global<v8::ArrayBuffer> buffer_; }; // --------------------------------------------------------------------------- // ArenaSlot — type-erased handle to a slot in an AliasedStructArena page. // This can be stored in headers where T is incomplete. The typed accessors // are provided via a thin typed wrapper (AliasedStructArena<T>::Slot). struct ArenaSlotBase { // Opaque page pointer — only the arena knows the concrete type. void* page = nullptr; void* ptr = nullptr; uint32_t index = 0; size_t byte_offset = 0; explicit operator bool() const { return ptr != nullptr; } // Returns the page's ArrayBuffer. Implemented below after ArenaPageHeader. v8::Local<v8::ArrayBuffer> GetArrayBuffer(v8::Isolate* isolate) const; size_t GetByteOffset() const { return byte_offset; } // Returns the page's cached DataView over the full page. // Callers use byte_offset to index into the correct slot region. v8::Local<v8::DataView> GetPageDataView(v8::Isolate* isolate) const; // Returns the page's cached BigUint64Array over the full page. // Callers use byte_offset / sizeof(uint64_t) to index into the // correct slot region. v8::Local<v8::BigUint64Array> GetPageBigUint64Array( v8::Isolate* isolate) const; }; // --------------------------------------------------------------------------- // AliasedStructArena<T> — pool allocator for AliasedStruct-style shared // memory. Instead of creating a separate ArrayBuffer + BackingStore per // instance, the arena pre-allocates pages of N slots backed by a single // ArrayBuffer each. Callers receive a Slot handle that provides the same // T*/operator-> interface as AliasedStruct, plus the ability to create a // JS typed-array view over just that slot's region of the page buffer. // // Pages target kPageBytes (default 16 KB) for L1 cache residency during // sequential access patterns. Slots are recycled via an intrusive // freelist, and empty pages are dropped when their last slot is released. // // Usage: // AliasedStructArena<MyState> arena; // auto slot = arena.Allocate(isolate); // slot->some_field = 42; // auto view = slot.GetArrayBuffer(isolate); // JS-visible view // ... // arena.Release(std::move(slot)); // return to freelist // template <typename T, size_t kPageBytes = 16384> class AliasedStructArena final { public: static constexpr size_t kSlotsPerPage = kPageBytes / sizeof(T); static_assert(kSlotsPerPage >= 4, "Page too small for type T"); static_assert(sizeof(T) >= sizeof(uint32_t), "T must be at least 4 bytes for freelist linkage"); AliasedStructArena() = default; ~AliasedStructArena() = default; AliasedStructArena(const AliasedStructArena&) = delete; AliasedStructArena& operator=(const AliasedStructArena&) = delete; struct Page { std::shared_ptr<v8::BackingStore> store; v8::Global<v8::ArrayBuffer> buffer; // Lazily created full-page views shared by all slots in // this page. Typically only one is used per arena. v8::Global<v8::DataView> data_view; v8::Global<v8::BigUint64Array> big_uint64_array; size_t page_byte_length = 0; T* base = nullptr; uint32_t free_head = 0; uint32_t used_count = 0; static constexpr uint32_t kNoFreeSlot = UINT32_MAX; void Init(v8::Isolate* isolate) { const v8::HandleScope handle_scope(isolate); const size_t total_bytes = kSlotsPerPage * sizeof(T); store = v8::ArrayBuffer::NewBackingStore(isolate, total_bytes); memset(store->Data(), 0, total_bytes); base = static_cast<T*>(store->Data()); page_byte_length = total_bytes; v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store); buffer = v8::Global<v8::ArrayBuffer>(isolate, ab); // Build freelist: each slot points to the next. for (uint32_t i = 0; i < kSlotsPerPage - 1; i++) { *reinterpret_cast<uint32_t*>(&base[i]) = i + 1; } *reinterpret_cast<uint32_t*>(&base[kSlotsPerPage - 1]) = kNoFreeSlot; free_head = 0; used_count = 0; } bool HasFreeSlots() const { return free_head != kNoFreeSlot; } }; // Typed slot handle — wraps ArenaSlotBase with T* accessors. class Slot : public ArenaSlotBase { public: Slot() = default; const T& operator*() const { return *static_cast<T*>(ptr); } T& operator*() { return *static_cast<T*>(ptr); } const T* operator->() const { return static_cast<T*>(ptr); } T* operator->() { return static_cast<T*>(ptr); } T* Data() { return static_cast<T*>(ptr); } const T* Data() const { return static_cast<T*>(ptr); } }; // Allocate a slot, placement-constructing T with the given args. // Creates a new page if all existing pages are full. template <typename... Args> Slot Allocate(v8::Isolate* isolate, Args&&... args); // Release a slot back to the arena freelist. Calls ~T() and zeros // the memory so that any JS views see clean data. void Release(Slot&& slot); // Release a slot given a type-erased ArenaSlotBase reference. // Convenience for callers that store ArenaSlotBase in headers where // T is incomplete. void ReleaseSlot(ArenaSlotBase& base); private: Page* FindOrCreatePage(v8::Isolate* isolate); std::vector<std::unique_ptr<Page>> pages_; }; // ArenaSlotBase accessors need to reach the v8::Globals inside a Page. // All AliasedStructArena<T>::Page types share the same leading layout. // The page_byte_length field allows lazy view creation without knowing T. namespace detail { struct ArenaPageHeader { std::shared_ptr<v8::BackingStore> store; v8::Global<v8::ArrayBuffer> buffer; v8::Global<v8::DataView> data_view; v8::Global<v8::BigUint64Array> big_uint64_array; size_t page_byte_length = 0; v8::Local<v8::DataView> GetDataView(v8::Isolate* isolate) { if (data_view.IsEmpty()) { const v8::HandleScope handle_scope(isolate); auto dv = v8::DataView::New(buffer.Get(isolate), 0, page_byte_length); data_view = v8::Global<v8::DataView>(isolate, dv); } return data_view.Get(isolate); } v8::Local<v8::BigUint64Array> GetBigUint64Array(v8::Isolate* isolate) { if (big_uint64_array.IsEmpty()) { const v8::HandleScope handle_scope(isolate); auto bu = v8::BigUint64Array::New( buffer.Get(isolate), 0, page_byte_length / sizeof(uint64_t)); big_uint64_array = v8::Global<v8::BigUint64Array>(isolate, bu); } return big_uint64_array.Get(isolate); } }; } // namespace detail inline v8::Local<v8::ArrayBuffer> ArenaSlotBase::GetArrayBuffer( v8::Isolate* isolate) const { DCHECK_NOT_NULL(page); auto* header = static_cast<detail::ArenaPageHeader*>(page); return header->buffer.Get(isolate); } inline v8::Local<v8::DataView> ArenaSlotBase::GetPageDataView( v8::Isolate* isolate) const { DCHECK_NOT_NULL(page); auto* header = static_cast<detail::ArenaPageHeader*>(page); return header->GetDataView(isolate); } inline v8::Local<v8::BigUint64Array> ArenaSlotBase::GetPageBigUint64Array( v8::Isolate* isolate) const { DCHECK_NOT_NULL(page); auto* header = static_cast<detail::ArenaPageHeader*>(page); return header->GetBigUint64Array(isolate); } } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_ALIASED_STRUCT_H_