From 99feaff8beb497310aa462a76e026994535520e2 Mon Sep 17 00:00:00 2001 From: "Matthew \"strager\" Glazar" Date: Sun, 5 Nov 2023 19:40:22 -0500 Subject: [PATCH] refactor(lsp): prefer Vector over std::vector Reduce our use of std::vector to reduce binary bloat: https://github.com/quick-lint/quick-lint-js/issues/689 --- src/quick-lint-js/container/vector-profiler.h | 13 ++++++ src/quick-lint-js/container/vector.h | 25 ++++++++++ src/quick-lint-js/lsp/lsp-location.cpp | 46 ++++++++----------- src/quick-lint-js/lsp/lsp-location.h | 13 ++++-- src/quick-lint-js/lsp/lsp-message-parser.h | 7 +-- .../lsp/outgoing-json-rpc-message-queue.h | 4 +- 6 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/quick-lint-js/container/vector-profiler.h b/src/quick-lint-js/container/vector-profiler.h index d7e19aa0a6..87d53699e2 100644 --- a/src/quick-lint-js/container/vector-profiler.h +++ b/src/quick-lint-js/container/vector-profiler.h @@ -355,6 +355,14 @@ class Instrumented_Vector { return Span(this->data_); } + void swap(Instrumented_Vector &other) { + this->data_.swap(other.data_); + std::swap(this->debug_owner_, other.debug_owner_); + // TODO(strager): Add instrumentation specific to swapping. + this->add_instrumentation_entry(Vector_Instrumentation::Event::resize); + other.add_instrumentation_entry(Vector_Instrumentation::Event::resize); + } + private: QLJS_FORCE_INLINE void add_instrumentation_entry( Vector_Instrumentation::Event event) { @@ -370,6 +378,11 @@ class Instrumented_Vector { Vector data_; const char *debug_owner_; }; + +template +void swap(Instrumented_Vector &lhs, Instrumented_Vector &rhs) { + lhs.swap(rhs); +} #endif } diff --git a/src/quick-lint-js/container/vector.h b/src/quick-lint-js/container/vector.h index 72679b8dae..9f6e8c1715 100644 --- a/src/quick-lint-js/container/vector.h +++ b/src/quick-lint-js/container/vector.h @@ -90,8 +90,17 @@ class Uninstrumented_Vector : private Vector { using Vector::release_to_span; using Vector::release_to_string_view; using Vector::to_string_view; + + void swap(Uninstrumented_Vector &other) { + static_cast(*this).swap(static_cast(other)); + } }; +template +void swap(Uninstrumented_Vector &lhs, Uninstrumented_Vector &rhs) { + lhs.swap(rhs); +} + using Vector_Size = std::ptrdiff_t; // Like std::pmr::vector. Some differences: @@ -344,6 +353,17 @@ class Raw_Vector { void erase(value_type *item) { erase(item, item + 1); } + // Swap capacity pointers and sizes between *this and other. All items of + // *this and other are untouched. + // + // Precondition: this->get_allocator() == other.get_allocator() + void swap(Raw_Vector &other) { + QLJS_ALWAYS_ASSERT(this->get_allocator() == other.get_allocator()); + std::swap(this->data_, other.data_); + std::swap(this->data_end_, other.data_end_); + std::swap(this->capacity_end_, other.capacity_end_); + } + // If new_size > this->size(): default-construct new items at the end. // If new_size < this->size(): destruct items at the end. // @@ -415,6 +435,11 @@ class Raw_Vector { Memory_Resource *allocator_; }; +template +void swap(Raw_Vector &lhs, Raw_Vector &rhs) { + lhs.swap(rhs); +} + #if QLJS_FEATURE_VECTOR_PROFILING template using Vector = Instrumented_Vector>; diff --git a/src/quick-lint-js/lsp/lsp-location.cpp b/src/quick-lint-js/lsp/lsp-location.cpp index ef1a83e03a..b21e701f92 100644 --- a/src/quick-lint-js/lsp/lsp-location.cpp +++ b/src/quick-lint-js/lsp/lsp-location.cpp @@ -19,12 +19,10 @@ namespace { template void insert_back_transform(Input_It input_begin, Input_It input_end, Output &output, Transformer &&transformer) { - using Difference_Type = typename Output::difference_type; - std::size_t original_size = output.size(); - std::size_t final_size = - original_size + narrow_cast(input_end - input_begin); + Vector_Size original_size = output.size(); + Vector_Size final_size = original_size + input_end - input_begin; output.resize(final_size); - auto output_it = output.begin() + narrow_cast(original_size); + auto output_it = output.begin() + original_size; output_it = std::transform(input_begin, input_end, output_it, transformer); QLJS_ASSERT(output_it == output.end()); } @@ -66,9 +64,8 @@ const Char8 *LSP_Locator::from_position(LSP_Position position) const { return this->input_.null_terminator(); } - Offset_Type line_begin_offset = - this->offset_of_lines_[narrow_cast(line)]; - bool line_is_ascii = this->line_is_ascii_[narrow_cast(line)]; + Offset_Type line_begin_offset = this->offset_of_lines_[line]; + bool line_is_ascii = this->line_is_ascii_[line]; bool is_last_line = line == number_of_lines - 1; if (is_last_line) { // TODO(strager): Get rid of this narrow_cast. @@ -86,8 +83,7 @@ const Char8 *LSP_Locator::from_position(LSP_Position position) const { return advance_lsp_characters_in_utf_8(line_string, character); } } else { - Offset_Type line_end_offset = - this->offset_of_lines_[narrow_cast(line + 1)]; + Offset_Type line_end_offset = this->offset_of_lines_[line + 1]; Offset_Type line_length_including_terminator = line_end_offset - line_begin_offset; if (line_is_ascii) { @@ -139,24 +135,24 @@ void LSP_Locator::replace_text(LSP_Range range, String8_View replacement_text, narrow_cast(replacement_text.size()); QLJS_ASSERT(!this->offset_of_lines_.empty()); - std::size_t start_line = narrow_cast(range.start.line); - std::size_t end_line = std::min(this->offset_of_lines_.size() - 1, - narrow_cast(range.end.line)); + Vector_Size start_line = narrow_cast(range.start.line); + Vector_Size end_line = std::min(this->offset_of_lines_.size() - 1, + narrow_cast(range.end.line)); this->input_ = new_input; - std::swap(this->old_offset_of_lines_, this->offset_of_lines_); - std::swap(this->old_line_is_ascii_, this->line_is_ascii_); + swap(this->old_offset_of_lines_, this->offset_of_lines_); + swap(this->old_line_is_ascii_, this->line_is_ascii_); this->offset_of_lines_.reserve(this->old_offset_of_lines_.size()); this->offset_of_lines_.clear(); this->line_is_ascii_.reserve(this->old_line_is_ascii_.size()); this->line_is_ascii_.clear(); // Offsets before replacement: do not adjust. - this->offset_of_lines_.insert( - this->offset_of_lines_.end(), this->old_offset_of_lines_.begin(), + this->offset_of_lines_.append( + this->old_offset_of_lines_.begin(), this->old_offset_of_lines_.begin() + range.start.line + 1); - this->line_is_ascii_.insert( - this->line_is_ascii_.end(), this->old_line_is_ascii_.begin(), + this->line_is_ascii_.append( + this->old_line_is_ascii_.begin(), this->old_line_is_ascii_.begin() + range.start.line); // Offsets within replacement: re-parse newlines. @@ -183,8 +179,7 @@ void LSP_Locator::replace_text(LSP_Range range, String8_View replacement_text, [&](Offset_Type offset) -> Offset_Type { return offset + net_bytes_added; }); - this->line_is_ascii_.insert(this->line_is_ascii_.end(), - this->old_line_is_ascii_.begin() + + this->line_is_ascii_.append(this->old_line_is_ascii_.begin() + narrow_cast(end_line) + 1, this->old_line_is_ascii_.end()); @@ -216,8 +211,7 @@ void LSP_Locator::cache_offsets_of_lines() { QLJS_ASSERT(this->line_is_ascii_.empty()); constexpr int estimated_bytes_per_line = 64; - std::size_t estimated_lines = - narrow_cast(this->input_.size() / estimated_bytes_per_line); + Vector_Size estimated_lines = this->input_.size() / estimated_bytes_per_line; this->offset_of_lines_.reserve(estimated_lines); this->line_is_ascii_.reserve(estimated_lines); @@ -275,10 +269,8 @@ LSP_Locator::Offset_Type LSP_Locator::offset(const Char8 *source) const { } LSP_Position LSP_Locator::position(int line_number, Offset_Type offset) const { - Offset_Type beginning_of_line_offset = - this->offset_of_lines_[narrow_cast(line_number)]; - bool line_is_ascii = - this->line_is_ascii_[narrow_cast(line_number)]; + Offset_Type beginning_of_line_offset = this->offset_of_lines_[line_number]; + bool line_is_ascii = this->line_is_ascii_[line_number]; int character; if (line_is_ascii) { diff --git a/src/quick-lint-js/lsp/lsp-location.h b/src/quick-lint-js/lsp/lsp-location.h index 63616c4784..96dcdd7c53 100644 --- a/src/quick-lint-js/lsp/lsp-location.h +++ b/src/quick-lint-js/lsp/lsp-location.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -58,13 +59,17 @@ class LSP_Locator { LSP_Position position(int line_number, Offset_Type offset) const; Padded_String_View input_; - std::vector offset_of_lines_; - std::vector line_is_ascii_; + Vector offset_of_lines_{"LSP_Locator::offset_of_lines_", + new_delete_resource()}; + Vector line_is_ascii_{"LSP_Locator::line_is_ascii_", + new_delete_resource()}; // old_offset_of_lines_ and old_line_is_ascii_ are used for double buffering // of offset_of_lines_ and line_is_ascii_. This reduces allocations. - std::vector old_offset_of_lines_; - std::vector old_line_is_ascii_; + Vector old_offset_of_lines_{"LSP_Locator::old_offset_of_lines_", + new_delete_resource()}; + Vector old_line_is_ascii_{"LSP_Locator::old_line_is_ascii_", + new_delete_resource()}; }; } diff --git a/src/quick-lint-js/lsp/lsp-message-parser.h b/src/quick-lint-js/lsp/lsp-message-parser.h index 3a99302a8a..45eda29fe8 100644 --- a/src/quick-lint-js/lsp/lsp-message-parser.h +++ b/src/quick-lint-js/lsp/lsp-message-parser.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,8 @@ class LSP_Message_Parser_Base { static bool header_is(String8_View header_name, String8_View expected_header_name); - std::vector buffer_; + Vector buffer_{"LSP_Message_Parser_Base::buffer_", + new_delete_resource()}; // If !pending_message_content_length_.has_value(), buffer_ contains message // headers (and possibly message content and other messages afterwards). @@ -63,8 +65,7 @@ template class LSP_Message_Parser : private LSP_Message_Parser_Base { public: void append(String8_View data) { - this->buffer_.insert(this->buffer_.end(), data.data(), - data.data() + data.size()); + this->buffer_ += data; this->parse(); } diff --git a/src/quick-lint-js/lsp/outgoing-json-rpc-message-queue.h b/src/quick-lint-js/lsp/outgoing-json-rpc-message-queue.h index 2c81c536cc..a147f9bc29 100644 --- a/src/quick-lint-js/lsp/outgoing-json-rpc-message-queue.h +++ b/src/quick-lint-js/lsp/outgoing-json-rpc-message-queue.h @@ -8,6 +8,7 @@ #else #include +#include #include namespace quick_lint_js { @@ -30,7 +31,8 @@ class Outgoing_JSON_RPC_Message_Queue { void send(LSP_Endpoint_Remote&); private: - std::vector messages_; + Vector messages_{"Outgoing_JSON_RPC_Message_Queue::messages_", + new_delete_resource()}; }; }