From 580f51de8f0c0da6394658a37ceb64ab4f607060 Mon Sep 17 00:00:00 2001 From: "Matthew \"strager\" Glazar" Date: Sat, 4 Nov 2023 20:56:26 -0400 Subject: [PATCH] refactor(container): document Linked_Bump_Allocator and Raw_Bump_Vector --- .../container/linked-bump-allocator.h | 31 +++++++++++++++++++ src/quick-lint-js/container/vector.h | 31 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/quick-lint-js/container/linked-bump-allocator.h b/src/quick-lint-js/container/linked-bump-allocator.h index 5995e737bb..4bb1a79296 100644 --- a/src/quick-lint-js/container/linked-bump-allocator.h +++ b/src/quick-lint-js/container/linked-bump-allocator.h @@ -39,6 +39,7 @@ class Linked_Bump_Allocator final : public Memory_Resource { ~Linked_Bump_Allocator() override; + // Deallocate previously-allocated memory. void release(); struct Rewind_State { @@ -67,6 +68,7 @@ class Linked_Bump_Allocator final : public Memory_Resource { Rewind_State rewind_; }; + // See rewind(). Rewind_State prepare_for_rewind() { return Rewind_State{ .chunk_ = this->chunk_, @@ -75,21 +77,31 @@ class Linked_Bump_Allocator final : public Memory_Resource { }; } + // Deallocate all allocations made since the creation of the Rewind_State + // (returned by this->prepare_for_rewind()). void rewind(Rewind_State&& r); + // Calls this->prepare_for_rewind() immediately then this->rewind() on + // destruction. [[nodiscard]] Rewind_Guard make_rewind_guard() { return Rewind_Guard(this); } + // Allocate space for an instance of T, then construct T. template T* new_object(Args&&... args) { return new (this->allocate_bytes(sizeof(T), alignof(T))) T(std::forward(args)...); } + // Allocate space for an instance of T, then construct T via copy or move + // construction. template T* new_object_copy(T&& value) { return this->new_object(std::forward(value)); } + // Allocate space for objects.size() instances of T, then construct result[i] + // via copy or move construction from objects[i] for i from 0 to + // objects.size()-1. template Span new_objects_copy(Span objects) { Span new_objects = this->allocate_uninitialized_span( @@ -99,6 +111,8 @@ class Linked_Bump_Allocator final : public Memory_Resource { return new_objects; } + // Allocate space for object.size() instances of T. Does not construct any + // T-s. template [[nodiscard]] Span allocate_uninitialized_span(std::size_t size) { std::size_t byte_size = size * sizeof(T); @@ -107,11 +121,15 @@ class Linked_Bump_Allocator final : public Memory_Resource { return Span(items, narrow_cast(size)); } + // Allocate space for object.size() instances of T, then default-construct + // object.size() instances. template [[nodiscard]] Span allocate_span(Span_Size size) { return this->allocate_span(narrow_cast(size)); } + // Allocate space for object.size() instances of T, then default-construct + // object.size() instances. template [[nodiscard]] Span allocate_span(std::size_t size) { Span items = this->allocate_uninitialized_span(size); @@ -123,6 +141,11 @@ class Linked_Bump_Allocator final : public Memory_Resource { return items; } + // Given previously-allocated space for old_size instances of T, allocate + // adjacent space for (new_size-old_size) instances of T after the old + // allocation and return true. + // + // If adjacent space is not available, do nothing and return false. template bool try_grow_array_in_place(T* array, std::size_t old_size, std::size_t new_size) { @@ -131,6 +154,7 @@ class Linked_Bump_Allocator final : public Memory_Resource { new_size * sizeof(T)); } + // For testing only. std::size_t remaining_bytes_in_current_chunk() const { return narrow_cast(this->chunk_end_ - this->next_allocation_); } @@ -149,6 +173,8 @@ class Linked_Bump_Allocator final : public Memory_Resource { friend class Linked_Bump_Allocator; }; + // In debug modes, cause all allocations to fail with a precondition failure + // until the Disable_Guard is destructed. [[nodiscard]] Disable_Guard disable() { return Disable_Guard(this); } protected: @@ -165,6 +191,11 @@ class Linked_Bump_Allocator final : public Memory_Resource { static inline constexpr std::size_t default_chunk_size = 4096 - sizeof(Chunk); + // Given previously-allocated space of old_byte_size bytes, allocate adjacent + // space for (new_byte_size-old_byte_size) bytes after the old allocation and + // return true. + // + // If adjacent space is not available, do nothing and return false. bool try_grow_array_in_place_impl(char* array, std::size_t old_byte_size, std::size_t new_byte_size); diff --git a/src/quick-lint-js/container/vector.h b/src/quick-lint-js/container/vector.h index 34506ddf1c..a2b1701b58 100644 --- a/src/quick-lint-js/container/vector.h +++ b/src/quick-lint-js/container/vector.h @@ -92,6 +92,10 @@ class Uninstrumented_Vector : private Vector { using Bump_Vector_Size = std::ptrdiff_t; +// Like std::pmr::vector. Some differences: +// +// * No exception safety. +// * Extended interface for convenience. template class Raw_Bump_Vector { public: @@ -108,12 +112,18 @@ class Raw_Bump_Vector { static_assert(is_winkable_v); + // Create an empty vector. explicit Raw_Bump_Vector(Linked_Bump_Allocator *allocator) : allocator_(allocator) {} Raw_Bump_Vector(const Raw_Bump_Vector &) = delete; Raw_Bump_Vector &operator=(const Raw_Bump_Vector &) = delete; + // Move items from another Raw_Bump_Vector by taking its allocation. + // + // This function does not move individual items. + // + // Postcondition: other.empty() Raw_Bump_Vector(Raw_Bump_Vector &&other) : data_(other.data_), data_end_(other.data_end_), @@ -124,8 +134,15 @@ class Raw_Bump_Vector { other.capacity_end_ = nullptr; } + // Destruct items in the container, as in this->clear(), then release the + // memory. + // + // If the allocator is a Linked_Bump_Allocator, then memory is only released + // if this Raw_Bump_Vector's capacity is the last thing allocated with that + // allocator. ~Raw_Bump_Vector() { this->clear(); } + // Return the pointer given in Raw_Bump_Vector's constructor. Linked_Bump_Allocator *get_allocator() const { return this->allocator_; } bool empty() const { return this->data_ == this->data_end_; } @@ -163,11 +180,15 @@ class Raw_Bump_Vector { return this->data_end_[-1]; } + // Precondition: index is in bounds. This means that '&(*this)[this->size()]' + // (indexing one past the end) is invalid. To get one past the end, call + // this->end() instead or write '&this->data()[this->size()]'. T &operator[](size_type index) { QLJS_ASSERT(index < this->size()); return this->data_[index]; } + // Precondition: (none) void reserve(size_type new_capacity) { QLJS_ASSERT(new_capacity >= 0); if (this->capacity() < new_capacity) { @@ -175,6 +196,7 @@ class Raw_Bump_Vector { } } + // Precondition: new_capacity > this->capacity() void reserve_grow(size_type new_capacity) { QLJS_ASSERT(new_capacity > this->capacity()); if (this->data_) { @@ -266,6 +288,10 @@ class Raw_Bump_Vector { return span; } + // Call the destructor of each item, then deallocate memory used for the + // items. + // + // Postcondition: this->empty() void clear() { if (this->data_) { std::destroy(this->data_, this->data_end_); @@ -278,6 +304,10 @@ class Raw_Bump_Vector { } } + // If new_size > this->size(): default-construct new items at the end. + // If new_size < this->size(): destruct items at the end. + // + // Postcondition: this->size() == new_size void resize(size_type new_size) { size_type old_size = this->size(); if (new_size == old_size) { @@ -327,6 +357,7 @@ class Raw_Bump_Vector { } private: + // Growth strategy. void reserve_grow_by_at_least(size_type minimum_new_entries) { size_type old_capacity = this->capacity(); constexpr size_type minimum_capacity = 4;