[VM] Introduce ListQueue class
TEST=list_queue_test.cc Change-Id: Ie85055475f35a9fdc4f31e61d5f1f5d04eb707a8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420220 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Derek Xu <derekx@google.com>
This commit is contained in:
@@ -64,6 +64,7 @@ builtin_impl_tests = [
|
||||
"eventhandler_test.cc",
|
||||
"file_test.cc",
|
||||
"hashmap_test.cc",
|
||||
"list_queue_test.cc",
|
||||
"priority_heap_test.cc",
|
||||
"snapshot_utils_test.cc",
|
||||
"test_utils.cc",
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "platform/list_queue.h"
|
||||
#include "vm/unit_test.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
void ExpectContentsToBe(const ListQueue<intptr_t>& actual,
|
||||
const std::vector<intptr_t>& expected) {
|
||||
intptr_t i = 0;
|
||||
actual.ForEach([&expected, &i](intptr_t actual_element) {
|
||||
EXPECT(actual_element == expected[i]);
|
||||
++i;
|
||||
});
|
||||
}
|
||||
|
||||
VM_UNIT_TEST_CASE(ListQueue_PublicMethods) {
|
||||
ListQueue<intptr_t> l;
|
||||
intptr_t front = -1;
|
||||
|
||||
l.PushBack(1);
|
||||
ExpectContentsToBe(l, {1});
|
||||
EXPECT(l.Length() == 1);
|
||||
|
||||
front = l.PopFront();
|
||||
EXPECT(front == 1);
|
||||
ExpectContentsToBe(l, {});
|
||||
EXPECT(l.Length() == 0);
|
||||
|
||||
l.PushBack(2);
|
||||
ExpectContentsToBe(l, {2});
|
||||
EXPECT(l.Length() == 1);
|
||||
|
||||
l.PushBack(3);
|
||||
ExpectContentsToBe(l, {2, 3});
|
||||
EXPECT(l.Length() == 2);
|
||||
|
||||
front = l.PopFront();
|
||||
EXPECT(front == 2);
|
||||
ExpectContentsToBe(l, {3});
|
||||
EXPECT(l.Length() == 1);
|
||||
|
||||
l.PushBack(4);
|
||||
ExpectContentsToBe(l, {3, 4});
|
||||
EXPECT(l.Length() == 2);
|
||||
}
|
||||
|
||||
VM_UNIT_TEST_CASE(ListQueue_Grow) {
|
||||
const intptr_t kInitialCapacity = ListQueue<intptr_t>::kInitialCapacity;
|
||||
|
||||
ListQueue<intptr_t> l;
|
||||
l.PushBack(1);
|
||||
ExpectContentsToBe(l, {1});
|
||||
l.PopFront();
|
||||
ExpectContentsToBe(l, {});
|
||||
|
||||
// Force |l| to grow by adding more than |kInitialCapacity| elements to it.
|
||||
for (intptr_t i = 0; i <= kInitialCapacity + 3; ++i) {
|
||||
l.PushBack(123);
|
||||
}
|
||||
|
||||
ExpectContentsToBe(l, std::vector<intptr_t>(kInitialCapacity + 4, 123));
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef RUNTIME_PLATFORM_LIST_QUEUE_H_
|
||||
#define RUNTIME_PLATFORM_LIST_QUEUE_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "platform/globals.h"
|
||||
|
||||
// A queue backed by a circular buffer similar to dart:collection's ListQueue.
|
||||
template <typename T>
|
||||
class ListQueue {
|
||||
public:
|
||||
static constexpr intptr_t kInitialCapacity = 64;
|
||||
|
||||
ListQueue()
|
||||
: buffer_(std::make_unique<T[]>(kInitialCapacity)),
|
||||
capacity_(kInitialCapacity) {}
|
||||
|
||||
void PushBack(T&& element) {
|
||||
buffer_[tail_] = std::move(element);
|
||||
tail_ = (tail_ + 1) % capacity_;
|
||||
if (head_ == tail_) {
|
||||
Grow();
|
||||
}
|
||||
++length_;
|
||||
}
|
||||
|
||||
T&& PopFront() {
|
||||
ASSERT(head_ != tail_);
|
||||
T&& element = std::move(buffer_[head_]);
|
||||
head_ = (head_ + 1) % capacity_;
|
||||
--length_;
|
||||
return std::move(element);
|
||||
}
|
||||
|
||||
// The number of elements currently in the queue.
|
||||
intptr_t Length() { return length_; }
|
||||
|
||||
void ForEach(std::function<void(const T&)> callback) const {
|
||||
for (intptr_t i = head_; i != tail_; i = (i + 1) % capacity_) {
|
||||
callback(buffer_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T[]> buffer_;
|
||||
intptr_t capacity_;
|
||||
intptr_t length_ = 0;
|
||||
intptr_t head_ = 0;
|
||||
intptr_t tail_ = 0;
|
||||
|
||||
void Grow() {
|
||||
intptr_t split = capacity_ - head_;
|
||||
|
||||
intptr_t new_capacity = capacity_ << 1;
|
||||
std::unique_ptr<T[]> new_buffer = std::make_unique<T[]>(new_capacity);
|
||||
|
||||
for (intptr_t i = 0; i < split; ++i) {
|
||||
new_buffer[i] = std::move(buffer_[head_ + i]);
|
||||
}
|
||||
for (intptr_t i = split; i < split + head_; ++i) {
|
||||
new_buffer[i] = std::move(buffer_[i - split]);
|
||||
}
|
||||
|
||||
head_ = 0;
|
||||
tail_ = capacity_;
|
||||
buffer_.swap(new_buffer);
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ListQueue);
|
||||
};
|
||||
|
||||
#endif // RUNTIME_PLATFORM_LIST_QUEUE_H_
|
||||
@@ -18,6 +18,7 @@ platform_sources = [
|
||||
"growable_array.h",
|
||||
"hashmap.cc",
|
||||
"hashmap.h",
|
||||
"list_queue.h",
|
||||
"lockers.h",
|
||||
"memory_sanitizer.h",
|
||||
"safe_stack.h",
|
||||
|
||||
Reference in New Issue
Block a user