Skip to content

Commit 6c257cd

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]>
1 parent 3767353 commit 6c257cd

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

src/env-inl.h

Lines changed: 98 additions & 0 deletions
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

Lines changed: 18 additions & 0 deletions
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;
@@ -905,6 +906,23 @@ void Environment::BuildEmbedderGraph(Isolate* isolate,
905906
});
906907
}
907908

909+
char* Environment::Reallocate(char* data, size_t old_size, size_t size) {
910+
// If we know that the allocator is our ArrayBufferAllocator, we can let
911+
// if reallocate directly.
912+
if (isolate_data()->uses_node_allocator()) {
913+
return static_cast<char*>(
914+
isolate_data()->node_allocator()->Reallocate(data, old_size, size));
915+
}
916+
// Generic allocators do not provide a reallocation method; we need to
917+
// allocate a new chunk of memory and copy the data over.
918+
char* new_data = AllocateUnchecked(size);
919+
if (new_data == nullptr) return nullptr;
920+
memcpy(new_data, data, std::min(size, old_size));
921+
if (size > old_size)
922+
memset(new_data + old_size, 0, size - old_size);
923+
Free(data, old_size);
924+
return new_data;
925+
}
908926

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

src/env.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,38 @@ enum class DebugCategory {
476476
CATEGORY_COUNT
477477
};
478478

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

698730
inline IsolateData* isolate_data() const;
699731

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

0 commit comments

Comments
 (0)