diff --git a/LICENSE.md b/LICENSE.md index fddf6bb54..b6367f96e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -6,7 +6,7 @@ Licensed Work: Dragonfly including the software components, or any portion of them, and any modification. -Change Date: September 1, 2028 +Change Date: March 1, 2029 Change License: [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), as published by the diff --git a/src/core/qlist.cc b/src/core/qlist.cc index 8f3c218de..5302c586c 100644 --- a/src/core/qlist.cc +++ b/src/core/qlist.cc @@ -60,7 +60,7 @@ namespace dfly { namespace { -static_assert(sizeof(QList) == 32); +static_assert(sizeof(QList) == 24); enum IterDir : uint8_t { FWD = 1, REV = 0 }; @@ -305,13 +305,12 @@ QList::QList(int fill, int compress) : fill_(fill), compress_(compress), bookmar QList::QList(QList&& other) : head_(other.head_), - tail_(other.tail_), count_(other.count_), len_(other.len_), fill_(other.fill_), compress_(other.compress_), bookmark_count_(other.bookmark_count_) { - other.head_ = other.tail_ = nullptr; + other.head_ = nullptr; other.len_ = other.count_ = 0; } @@ -323,14 +322,13 @@ QList& QList::operator=(QList&& other) { if (this != &other) { Clear(); head_ = other.head_; - tail_ = other.tail_; len_ = other.len_; count_ = other.count_; fill_ = other.fill_; compress_ = other.compress_; bookmark_count_ = other.bookmark_count_; - other.head_ = other.tail_ = nullptr; + other.head_ = nullptr; other.len_ = other.count_ = 0; } return *this; @@ -348,28 +346,24 @@ void QList::Clear() { len_--; current = next; } - head_ = tail_ = nullptr; + head_ = nullptr; count_ = 0; } void QList::Push(string_view value, Where where) { /* The head and tail should never be compressed (we don't attempt to decompress them) */ - if (head_) + if (head_) { DCHECK(head_->encoding != QUICKLIST_NODE_ENCODING_LZF); - if (tail_) - DCHECK(tail_->encoding != QUICKLIST_NODE_ENCODING_LZF); - + DCHECK(head_->prev->encoding != QUICKLIST_NODE_ENCODING_LZF); + } PushSentinel(value, where); } string QList::Pop(Where where) { DCHECK_GT(count_, 0u); - quicklistNode* node; - if (where == HEAD) { - node = head_; - } else { - DCHECK_EQ(TAIL, where); - node = tail_; + quicklistNode* node = head_; + if (where == TAIL) { + node = head_->prev; } /* The head and tail should never be compressed */ @@ -402,7 +396,7 @@ void QList::AppendListpack(unsigned char* zl) { node->count = lpLength(node->entry); node->sz = lpBytes(zl); - InsertNode(tail_, node, AFTER); + InsertNode(_Tail(), node, AFTER); count_ += node->count; } @@ -414,7 +408,7 @@ void QList::AppendPlain(unsigned char* data, size_t sz) { node->sz = sz; node->container = QUICKLIST_NODE_CONTAINER_PLAIN; - InsertNode(tail_, node, AFTER); + InsertNode(_Tail(), node, AFTER); ++count_; } @@ -469,7 +463,11 @@ void QList::Iterate(IterateFunc cb, long start, long end) const { } bool QList::PushSentinel(string_view value, Where where) { - quicklistNode* orig = where == HEAD ? head_ : tail_; + quicklistNode* orig = head_; + if (where == TAIL && orig) { + orig = orig->prev; + } + InsertOpt opt = where == HEAD ? BEFORE : AFTER; size_t sz = value.size(); @@ -507,23 +505,27 @@ void QList::InsertNode(quicklistNode* old_node, quicklistNode* new_node, InsertO if (old_node->next) old_node->next->prev = new_node; old_node->next = new_node; + if (head_->prev == old_node) // if old_node is tail, update the tail to the new node. + head_->prev = new_node; } - if (tail_ == old_node) - tail_ = new_node; } else { new_node->next = old_node; if (old_node) { new_node->prev = old_node->prev; - if (old_node->prev) + // if old_node is not head, link its prev to the new node. + // head->prev is tail, so we don't need to update it. + if (old_node != head_) old_node->prev->next = new_node; old_node->prev = new_node; } if (head_ == old_node) head_ = new_node; } + /* If this insert creates the only element so far, initialize head/tail. */ if (len_ == 0) { - head_ = tail_ = new_node; + head_ = new_node; + head_->prev = new_node; } /* Update len first, so in Compress we know exactly len */ @@ -698,7 +700,7 @@ void QList::Compress(quicklistNode* node) { return; /* The head and tail should never be compressed (we should not attempt to recompress them) */ - assert(head_->recompress == 0 && tail_->recompress == 0); + DCHECK(head_->recompress == 0 && head_->prev->recompress == 0); /* If length is less than our compress depth (from both sides), * we can't compress anything. */ @@ -709,7 +711,7 @@ void QList::Compress(quicklistNode* node) { * Note: because we do length checks at the *top* of this function, * we can skip explicit null checks below. Everything exists. */ quicklistNode* forward = head_; - quicklistNode* reverse = tail_; + quicklistNode* reverse = head_->prev; int depth = 0; int in_depth = 0; while (depth++ < compress_) { @@ -837,12 +839,10 @@ void QList::DelNode(quicklistNode* node) { if (node->prev) node->prev->next = node->next; - if (node == tail_) { - tail_ = node->prev; - } - if (node == head_) { head_ = node->next; + } else if (node == head_->prev) { // tail + head_->prev = node->prev; } /* Update len first, so in Compress we know exactly len */ @@ -892,7 +892,7 @@ auto QList::GetIterator(Where where) const -> Iterator { it.offset_ = 0; it.direction_ = FWD; } else { - it.current_ = tail_; + it.current_ = _Tail(); it.offset_ = -1; it.direction_ = REV; } @@ -918,7 +918,7 @@ auto QList::GetIterator(long idx) const -> Iterator { seek_index = count_ - 1 - index; } - n = seek_forward ? head_ : tail_; + n = seek_forward ? head_ : head_->prev; while (ABSL_PREDICT_TRUE(n)) { if ((accum + n->count) > seek_index) { break; @@ -1119,9 +1119,8 @@ bool QList::Iterator::Next() { } else { /* Reverse traversal, Jumping to end of previous node */ DCHECK_EQ(REV, direction_); - - current_ = current_->prev; offset_ = -1; + current_ = (current_ == owner_->head_) ? nullptr : current_->prev; } return current_ ? Next() : false; diff --git a/src/core/qlist.h b/src/core/qlist.h index e59ed9b56..c39377b53 100644 --- a/src/core/qlist.h +++ b/src/core/qlist.h @@ -144,7 +144,7 @@ class QList { } const quicklistNode* Tail() const { - return tail_; + return _Tail(); } void set_fill(int fill) { @@ -158,6 +158,10 @@ class QList { return compress_ != 0; } + quicklistNode* _Tail() const { + return head_ ? head_->prev : nullptr; + } + // Returns false if used existing sentinel, true if a new sentinel was created. bool PushSentinel(std::string_view value, Where where); @@ -176,7 +180,7 @@ class QList { bool DelPackedIndex(quicklistNode* node, uint8_t** p); quicklistNode* head_ = nullptr; - quicklistNode* tail_ = nullptr; + uint32_t count_ = 0; /* total count of all entries in all listpacks */ uint32_t len_ = 0; /* number of quicklistNodes */ signed int fill_ : QL_FILL_BITS; /* fill factor for individual nodes */ diff --git a/src/core/qlist_test.cc b/src/core/qlist_test.cc index 3623bd759..fdf7185e8 100644 --- a/src/core/qlist_test.cc +++ b/src/core/qlist_test.cc @@ -90,7 +90,7 @@ static int ql_verify(const QList& ql, uint32_t nc, uint32_t count, uint32_t head node_size = 0; while (node) { node_size += node->count; - node = node->prev; + node = (node == ql.Head()) ? nullptr : node->prev; } if (node_size != ql.Size()) { LOG(ERROR) << "has different forward count than reverse count! " @@ -165,7 +165,6 @@ TEST_F(QListTest, Basic) { EXPECT_EQ(0, ql_.Size()); ql_.Push("abc", QList::HEAD); EXPECT_EQ(1, ql_.Size()); - EXPECT_TRUE(ql_.Head()->prev == nullptr); EXPECT_TRUE(ql_.Tail() == ql_.Head()); auto it = ql_.GetIterator(QList::HEAD); @@ -517,6 +516,10 @@ TEST_P(OptionsTest, DelNeg100From500) { for (int i = 0; i < 500; i++) ql_.Push(StrCat("hello", i + 1), QList::TAIL); ql_.Erase(-100, 100); + + QList::Iterator it = ql_.GetIterator(QList::TAIL); + ASSERT_TRUE(it.Next()); + ASSERT_EQ("hello400", it.Get()); ASSERT_EQ(0, ql_verify(ql_, 13, 400, 32, 16)); }