Skip to content

Commit 2826076

Browse files
committed
src: add allocation utils to env
Add a RAII utility for managing blocks of memory that have been allocated with the `ArrayBuffer::Allocator` for a given `Isolate`. PR-URL: #26207 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Backport-PR-URL: #26302 Reviewed-By: Michaël Zasso <[email protected]>
1 parent 238fa57 commit 2826076

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

src/env-inl.h

+98
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,104 @@ inline IsolateData* Environment::isolate_data() const {
715715
return isolate_data_;
716716
}
717717

718+
inline char* Environment::AllocateUnchecked(size_t size) {
719+
return static_cast<char*>(
720+
isolate_data()->allocator()->AllocateUninitialized(size));
721+
}
722+
723+
inline char* Environment::Allocate(size_t size) {
724+
char* ret = AllocateUnchecked(size);
725+
CHECK_NE(ret, nullptr);
726+
return ret;
727+
}
728+
729+
inline void Environment::Free(char* data, size_t size) {
730+
if (data != nullptr)
731+
isolate_data()->allocator()->Free(data, size);
732+
}
733+
734+
inline AllocatedBuffer Environment::AllocateManaged(size_t size, bool checked) {
735+
char* data = checked ? Allocate(size) : AllocateUnchecked(size);
736+
if (data == nullptr) size = 0;
737+
return AllocatedBuffer(this, uv_buf_init(data, size));
738+
}
739+
740+
inline AllocatedBuffer::AllocatedBuffer(Environment* env, uv_buf_t buf)
741+
: env_(env), buffer_(buf) {}
742+
743+
inline void AllocatedBuffer::Resize(size_t len) {
744+
char* new_data = env_->Reallocate(buffer_.base, buffer_.len, len);
745+
CHECK_IMPLIES(len > 0, new_data != nullptr);
746+
buffer_ = uv_buf_init(new_data, len);
747+
}
748+
749+
inline uv_buf_t AllocatedBuffer::release() {
750+
uv_buf_t ret = buffer_;
751+
buffer_ = uv_buf_init(nullptr, 0);
752+
return ret;
753+
}
754+
755+
inline char* AllocatedBuffer::data() {
756+
return buffer_.base;
757+
}
758+
759+
inline const char* AllocatedBuffer::data() const {
760+
return buffer_.base;
761+
}
762+
763+
inline size_t AllocatedBuffer::size() const {
764+
return buffer_.len;
765+
}
766+
767+
inline AllocatedBuffer::AllocatedBuffer(Environment* env)
768+
: env_(env), buffer_(uv_buf_init(nullptr, 0)) {}
769+
770+
inline AllocatedBuffer::AllocatedBuffer(AllocatedBuffer&& other)
771+
: AllocatedBuffer() {
772+
*this = std::move(other);
773+
}
774+
775+
inline AllocatedBuffer& AllocatedBuffer::operator=(AllocatedBuffer&& other) {
776+
clear();
777+
env_ = other.env_;
778+
buffer_ = other.release();
779+
return *this;
780+
}
781+
782+
inline AllocatedBuffer::~AllocatedBuffer() {
783+
clear();
784+
}
785+
786+
inline void AllocatedBuffer::clear() {
787+
uv_buf_t buf = release();
788+
env_->Free(buf.base, buf.len);
789+
}
790+
791+
// It's a bit awkward to define this Buffer::New() overload here, but it
792+
// avoids a circular dependency with node_internals.h.
793+
namespace Buffer {
794+
v8::MaybeLocal<v8::Object> New(Environment* env,
795+
char* data,
796+
size_t length,
797+
bool uses_malloc);
798+
}
799+
800+
inline v8::MaybeLocal<v8::Object> AllocatedBuffer::ToBuffer() {
801+
CHECK_NOT_NULL(env_);
802+
v8::MaybeLocal<v8::Object> obj = Buffer::New(env_, data(), size(), false);
803+
if (!obj.IsEmpty()) release();
804+
return obj;
805+
}
806+
807+
inline v8::Local<v8::ArrayBuffer> AllocatedBuffer::ToArrayBuffer() {
808+
CHECK_NOT_NULL(env_);
809+
uv_buf_t buf = release();
810+
return v8::ArrayBuffer::New(env_->isolate(),
811+
buf.base,
812+
buf.len,
813+
v8::ArrayBufferCreationMode::kInternalized);
814+
}
815+
718816
inline void Environment::ThrowError(const char* errmsg) {
719817
ThrowError(v8::Exception::Error, errmsg);
720818
}

src/env.cc

+18
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
namespace node {
2222

2323
using errors::TryCatchScope;
24+
using v8::ArrayBuffer;
2425
using v8::Boolean;
2526
using v8::Context;
2627
using v8::EmbedderGraph;
@@ -924,6 +925,23 @@ void Environment::BuildEmbedderGraph(Isolate* isolate,
924925
});
925926
}
926927

928+
char* Environment::Reallocate(char* data, size_t old_size, size_t size) {
929+
// If we know that the allocator is our ArrayBufferAllocator, we can let
930+
// if reallocate directly.
931+
if (isolate_data()->uses_node_allocator()) {
932+
return static_cast<char*>(
933+
isolate_data()->node_allocator()->Reallocate(data, old_size, size));
934+
}
935+
// Generic allocators do not provide a reallocation method; we need to
936+
// allocate a new chunk of memory and copy the data over.
937+
char* new_data = AllocateUnchecked(size);
938+
if (new_data == nullptr) return nullptr;
939+
memcpy(new_data, data, std::min(size, old_size));
940+
if (size > old_size)
941+
memset(new_data + old_size, 0, size - old_size);
942+
Free(data, old_size);
943+
return new_data;
944+
}
927945

928946
// Not really any better place than env.cc at this moment.
929947
void BaseObject::DeleteMe(void* data) {

src/env.h

+41
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,38 @@ enum class DebugCategory {
474474
CATEGORY_COUNT
475475
};
476476

477+
// A unique-pointer-ish object that is compatible with the JS engine's
478+
// ArrayBuffer::Allocator.
479+
struct AllocatedBuffer {
480+
public:
481+
explicit inline AllocatedBuffer(Environment* env = nullptr);
482+
inline AllocatedBuffer(Environment* env, uv_buf_t buf);
483+
inline ~AllocatedBuffer();
484+
inline void Resize(size_t len);
485+
486+
inline uv_buf_t release();
487+
inline char* data();
488+
inline const char* data() const;
489+
inline size_t size() const;
490+
inline void clear();
491+
492+
inline v8::MaybeLocal<v8::Object> ToBuffer();
493+
inline v8::Local<v8::ArrayBuffer> ToArrayBuffer();
494+
495+
inline AllocatedBuffer(AllocatedBuffer&& other);
496+
inline AllocatedBuffer& operator=(AllocatedBuffer&& other);
497+
AllocatedBuffer(const AllocatedBuffer& other) = delete;
498+
AllocatedBuffer& operator=(const AllocatedBuffer& other) = delete;
499+
500+
private:
501+
Environment* env_;
502+
// We do not pass this to libuv directly, but uv_buf_t is a convenient way
503+
// to represent a chunk of memory, and plays nicely with other parts of core.
504+
uv_buf_t buffer_;
505+
506+
friend class Environment;
507+
};
508+
477509
class Environment {
478510
public:
479511
class AsyncHooks {
@@ -695,6 +727,15 @@ class Environment {
695727

696728
inline IsolateData* isolate_data() const;
697729

730+
// Utilites that allocate memory using the Isolate's ArrayBuffer::Allocator.
731+
// In particular, using AllocateManaged() will provide a RAII-style object
732+
// with easy conversion to `Buffer` and `ArrayBuffer` objects.
733+
inline AllocatedBuffer AllocateManaged(size_t size, bool checked = true);
734+
inline char* Allocate(size_t size);
735+
inline char* AllocateUnchecked(size_t size);
736+
char* Reallocate(char* data, size_t old_size, size_t size);
737+
inline void Free(char* data, size_t size);
738+
698739
inline bool printed_error() const;
699740
inline void set_printed_error(bool value);
700741

0 commit comments

Comments
 (0)