|
|
|
// Copyright 2020 The Abseil Authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <ctime>
|
|
|
|
#include <memory>
|
|
|
|
#include <random>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "absl/base/config.h"
|
|
|
|
#include "absl/base/internal/raw_logging.h"
|
|
|
|
#include "absl/base/macros.h"
|
|
|
|
#include "absl/debugging/leak_check.h"
|
|
|
|
#include "absl/strings/internal/cord_internal.h"
|
|
|
|
#include "absl/strings/internal/cord_rep_ring.h"
|
|
|
|
#include "absl/strings/str_cat.h"
|
|
|
|
#include "absl/strings/string_view.h"
|
|
|
|
|
|
|
|
extern thread_local bool cord_ring;
|
|
|
|
|
|
|
|
namespace absl {
|
|
|
|
ABSL_NAMESPACE_BEGIN
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using RandomEngine = std::mt19937_64;
|
|
|
|
|
|
|
|
using ::absl::cord_internal::CordRep;
|
|
|
|
using ::absl::cord_internal::CordRepConcat;
|
|
|
|
using ::absl::cord_internal::CordRepExternal;
|
|
|
|
using ::absl::cord_internal::CordRepFlat;
|
|
|
|
using ::absl::cord_internal::CordRepRing;
|
|
|
|
using ::absl::cord_internal::CordRepSubstring;
|
|
|
|
|
|
|
|
using ::absl::cord_internal::CONCAT;
|
|
|
|
using ::absl::cord_internal::EXTERNAL;
|
|
|
|
using ::absl::cord_internal::SUBSTRING;
|
|
|
|
|
|
|
|
using testing::ElementsAre;
|
|
|
|
using testing::ElementsAreArray;
|
|
|
|
using testing::Eq;
|
|
|
|
using testing::Ge;
|
|
|
|
using testing::Le;
|
|
|
|
using testing::Lt;
|
|
|
|
using testing::Ne;
|
|
|
|
using testing::SizeIs;
|
|
|
|
|
|
|
|
using index_type = CordRepRing::index_type;
|
|
|
|
|
|
|
|
enum InputShareMode { kPrivate, kShared, kSharedIndirect };
|
|
|
|
|
|
|
|
// TestParam class used by all test fixtures.
|
|
|
|
// Not all fixtures use all possible input combinations
|
|
|
|
struct TestParam {
|
|
|
|
TestParam() = default;
|
|
|
|
explicit TestParam(InputShareMode input_share_mode)
|
|
|
|
: input_share_mode(input_share_mode) {}
|
|
|
|
|
|
|
|
// Run the test with the 'rep under test' to be privately owned.
|
|
|
|
// Otherwise, the rep has a shared ref count of 2 or higher.
|
|
|
|
bool refcount_is_one = true;
|
|
|
|
|
|
|
|
// Run the test with the 'rep under test' being allocated with enough capacity
|
|
|
|
// to accommodate any modifications made to it. Otherwise, the rep has zero
|
|
|
|
// extra (reserve) capacity.
|
|
|
|
bool with_capacity = true;
|
|
|
|
|
|
|
|
// For test providing possibly shared input such as Append(.., CordpRep*),
|
|
|
|
// this field defines if that input is adopted with a refcount of one
|
|
|
|
// (privately owned / donated), or shared. For composite inputs such as
|
|
|
|
// 'substring of flat', we also have the 'shared indirect' value which means
|
|
|
|
// the top level node is not shared, but the contained child node is shared.
|
|
|
|
InputShareMode input_share_mode = kPrivate;
|
|
|
|
|
|
|
|
std::string ToString() const {
|
|
|
|
return absl::StrCat(refcount_is_one ? "Private" : "Shared",
|
|
|
|
with_capacity ? "" : "_NoCapacity",
|
|
|
|
(input_share_mode == kPrivate) ? ""
|
|
|
|
: (input_share_mode == kShared)
|
|
|
|
? "_SharedInput"
|
|
|
|
: "_IndirectSharedInput");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
using TestParams = std::vector<TestParam>;
|
|
|
|
|
|
|
|
// Matcher validating when mutable copies are required / performed.
|
|
|
|
MATCHER_P2(EqIfPrivate, param, rep,
|
|
|
|
absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) {
|
|
|
|
return param.refcount_is_one ? arg == rep : arg != rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Matcher validating when mutable copies are required / performed.
|
|
|
|
MATCHER_P2(EqIfPrivateAndCapacity, param, rep,
|
|
|
|
absl::StrCat("Equal 0x", absl::Hex(rep),
|
|
|
|
" if private and capacity")) {
|
|
|
|
return (param.refcount_is_one && param.with_capacity) ? arg == rep
|
|
|
|
: arg != rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") {
|
|
|
|
return param.input_share_mode == kPrivate ? arg == rep : arg != rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Matcher validating the core in-variants of the CordRepRing instance.
|
|
|
|
MATCHER(IsValidRingBuffer, "RingBuffer is valid") {
|
|
|
|
std::stringstream ss;
|
|
|
|
if (!arg->IsValid(ss)) {
|
|
|
|
*result_listener << "\nERROR: " << ss.str() << "\nRING = " << *arg;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the flats contained in the provided CordRepRing
|
|
|
|
std::vector<string_view> ToFlats(const CordRepRing* r) {
|
|
|
|
std::vector<string_view> flats;
|
|
|
|
flats.reserve(r->entries());
|
|
|
|
index_type pos = r->head();
|
|
|
|
do {
|
|
|
|
flats.push_back(r->entry_data(pos));
|
|
|
|
} while ((pos = r->advance(pos)) != r->tail());
|
|
|
|
return flats;
|
|
|
|
}
|
|
|
|
|
|
|
|
class not_a_string_view {
|
|
|
|
public:
|
|
|
|
explicit not_a_string_view(absl::string_view s)
|
|
|
|
: data_(s.data()), size_(s.size()) {}
|
|
|
|
explicit not_a_string_view(const void* data, size_t size)
|
|
|
|
: data_(data), size_(size) {}
|
|
|
|
|
|
|
|
not_a_string_view remove_prefix(size_t n) const {
|
|
|
|
return not_a_string_view(static_cast<const char*>(data_) + n, size_ - n);
|
|
|
|
}
|
|
|
|
|
|
|
|
not_a_string_view remove_suffix(size_t n) const {
|
|
|
|
return not_a_string_view(data_, size_ - n);
|
|
|
|
}
|
|
|
|
|
|
|
|
const void* data() const { return data_; }
|
|
|
|
size_t size() const { return size_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
const void* data_;
|
|
|
|
size_t size_;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool operator==(not_a_string_view lhs, not_a_string_view rhs) {
|
|
|
|
return lhs.data() == rhs.data() && lhs.size() == rhs.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& s, not_a_string_view rhs) {
|
|
|
|
return s << "{ data: " << rhs.data() << " size: " << rhs.size() << "}";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<not_a_string_view> ToRawFlats(const CordRepRing* r) {
|
|
|
|
std::vector<not_a_string_view> flats;
|
|
|
|
flats.reserve(r->entries());
|
|
|
|
index_type pos = r->head();
|
|
|
|
do {
|
|
|
|
flats.emplace_back(r->entry_data(pos));
|
|
|
|
} while ((pos = r->advance(pos)) != r->tail());
|
|
|
|
return flats;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the value contained in the provided CordRepRing
|
|
|
|
std::string ToString(const CordRepRing* r) {
|
|
|
|
std::string value;
|
|
|
|
value.reserve(r->length);
|
|
|
|
index_type pos = r->head();
|
|
|
|
do {
|
|
|
|
absl::string_view sv = r->entry_data(pos);
|
|
|
|
value.append(sv.data(), sv.size());
|
|
|
|
} while ((pos = r->advance(pos)) != r->tail());
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a flat for testing
|
|
|
|
CordRep* MakeFlat(absl::string_view s, size_t extra = 0) {
|
|
|
|
CordRepFlat* flat = CordRepFlat::New(s.length() + extra);
|
|
|
|
memcpy(flat->Data(), s.data(), s.length());
|
|
|
|
flat->length = s.length();
|
|
|
|
return flat;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates an external node for testing
|
|
|
|
CordRepExternal* MakeExternal(absl::string_view s) {
|
|
|
|
struct Rep : public CordRepExternal {
|
|
|
|
std::string s;
|
|
|
|
explicit Rep(absl::string_view s) : s(s) {
|
|
|
|
this->tag = EXTERNAL;
|
|
|
|
this->base = s.data();
|
|
|
|
this->length = s.length();
|
|
|
|
this->releaser_invoker = [](CordRepExternal* self) {
|
|
|
|
delete static_cast<Rep*>(self);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return new Rep(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
CordRepExternal* MakeFakeExternal(size_t length) {
|
|
|
|
struct Rep : public CordRepExternal {
|
|
|
|
std::string s;
|
|
|
|
explicit Rep(size_t len) {
|
|
|
|
this->tag = EXTERNAL;
|
|
|
|
this->base = this->storage;
|
|
|
|
this->length = len;
|
|
|
|
this->releaser_invoker = [](CordRepExternal* self) {
|
|
|
|
delete static_cast<Rep*>(self);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return new Rep(length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a flat or an external node for testing depending on the size.
|
|
|
|
CordRep* MakeLeaf(absl::string_view s, size_t extra = 0) {
|
|
|
|
if (s.size() <= absl::cord_internal::kMaxFlatLength) {
|
|
|
|
return MakeFlat(s, extra);
|
|
|
|
} else {
|
|
|
|
return MakeExternal(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a substring node
|
|
|
|
CordRepSubstring* MakeSubstring(size_t start, size_t len, CordRep* rep) {
|
|
|
|
auto* sub = new CordRepSubstring;
|
|
|
|
sub->tag = SUBSTRING;
|
|
|
|
sub->start = start;
|
|
|
|
sub->length = (len <= 0) ? rep->length - start + len : len;
|
|
|
|
sub->child = rep;
|
|
|
|
return sub;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a substring node removing the specified prefix
|
|
|
|
CordRepSubstring* RemovePrefix(size_t start, CordRep* rep) {
|
|
|
|
return MakeSubstring(start, rep->length - start, rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a substring node removing the specified suffix
|
|
|
|
CordRepSubstring* RemoveSuffix(size_t length, CordRep* rep) {
|
|
|
|
return MakeSubstring(0, rep->length - length, rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
CordRepConcat* MakeConcat(CordRep* left, CordRep* right, int depth = 0) {
|
|
|
|
auto* concat = new CordRepConcat;
|
|
|
|
concat->tag = CONCAT;
|
|
|
|
concat->length = left->length + right->length;
|
|
|
|
concat->left = left;
|
|
|
|
concat->right = right;
|
|
|
|
concat->set_depth(depth);
|
|
|
|
return concat;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Composition { kMix, kAppend, kPrepend };
|
|
|
|
|
|
|
|
Composition RandomComposition() {
|
|
|
|
RandomEngine rng(testing::GTEST_FLAG(random_seed));
|
|
|
|
return (rng() & 1) ? kMix : ((rng() & 1) ? kAppend : kPrepend);
|
|
|
|
}
|
|
|
|
|
|
|
|
absl::string_view ToString(Composition composition) {
|
|
|
|
switch (composition) {
|
|
|
|
case kAppend:
|
|
|
|
return "Append";
|
|
|
|
case kPrepend:
|
|
|
|
return "Prepend";
|
|
|
|
case kMix:
|
|
|
|
return "Mix";
|
|
|
|
}
|
|
|
|
assert(false);
|
|
|
|
return "???";
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr const char* kFox = "The quick brown fox jumps over the lazy dog";
|
|
|
|
constexpr const char* kFoxFlats[] = {"The ", "quick ", "brown ",
|
|
|
|
"fox ", "jumps ", "over ",
|
|
|
|
"the ", "lazy ", "dog"};
|
|
|
|
constexpr const char* kAlphabet = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
|
|
|
|
CordRepRing* FromFlats(Span<const char* const> flats,
|
|
|
|
Composition composition = kAppend) {
|
|
|
|
if (flats.empty()) return nullptr;
|
|
|
|
CordRepRing* ring = nullptr;
|
|
|
|
switch (composition) {
|
|
|
|
case kAppend:
|
|
|
|
ring = CordRepRing::Create(MakeLeaf(flats.front()), flats.size() - 1);
|
|
|
|
for (int i = 1; i < flats.size(); ++i) {
|
|
|
|
ring = CordRepRing::Append(ring, MakeLeaf(flats[i]));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kPrepend:
|
|
|
|
ring = CordRepRing::Create(MakeLeaf(flats.back()), flats.size() - 1);
|
|
|
|
for (int i = static_cast<int>(flats.size() - 2); i >= 0; --i) {
|
|
|
|
ring = CordRepRing::Prepend(ring, MakeLeaf(flats[i]));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kMix:
|
|
|
|
size_t middle1 = flats.size() / 2, middle2 = middle1;
|
|
|
|
ring = CordRepRing::Create(MakeLeaf(flats[middle1]), flats.size() - 1);
|
|
|
|
if (!flats.empty()) {
|
|
|
|
if ((flats.size() & 1) == 0) {
|
|
|
|
ring = CordRepRing::Prepend(ring, MakeLeaf(flats[--middle1]));
|
|
|
|
}
|
|
|
|
for (int i = 1; i <= middle1; ++i) {
|
|
|
|
ring = CordRepRing::Prepend(ring, MakeLeaf(flats[middle1 - i]));
|
|
|
|
ring = CordRepRing::Append(ring, MakeLeaf(flats[middle2 + i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
EXPECT_THAT(ToFlats(ring), ElementsAreArray(flats));
|
|
|
|
return ring;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& s, const TestParam& param) {
|
|
|
|
return s << param.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string TestParamToString(const testing::TestParamInfo<TestParam>& info) {
|
|
|
|
return info.param.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
class CordRingTest : public testing::Test {
|
|
|
|
public:
|
|
|
|
~CordRingTest() override {
|
|
|
|
for (CordRep* rep : unrefs_) {
|
|
|
|
CordRep::Unref(rep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* NeedsUnref(CordRepType* rep) {
|
|
|
|
assert(rep);
|
|
|
|
unrefs_.push_back(rep);
|
|
|
|
return rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* Ref(CordRepType* rep) {
|
|
|
|
CordRep::Ref(rep);
|
|
|
|
return NeedsUnref(rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<CordRep*> unrefs_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CordRingTestWithParam : public testing::TestWithParam<TestParam> {
|
|
|
|
public:
|
|
|
|
~CordRingTestWithParam() override {
|
|
|
|
for (CordRep* rep : unrefs_) {
|
|
|
|
CordRep::Unref(rep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CordRepRing* CreateWithCapacity(CordRep* child, size_t extra_capacity) {
|
|
|
|
if (!GetParam().with_capacity) extra_capacity = 0;
|
|
|
|
CordRepRing* ring = CordRepRing::Create(child, extra_capacity);
|
|
|
|
ring->SetCapacityForTesting(1 + extra_capacity);
|
|
|
|
return RefIfShared(ring);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Shared() const { return !GetParam().refcount_is_one; }
|
|
|
|
bool InputShared() const { return GetParam().input_share_mode == kShared; }
|
|
|
|
bool InputSharedIndirect() const {
|
|
|
|
return GetParam().input_share_mode == kSharedIndirect;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* NeedsUnref(CordRepType* rep) {
|
|
|
|
assert(rep);
|
|
|
|
unrefs_.push_back(rep);
|
|
|
|
return rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* Ref(CordRepType* rep) {
|
|
|
|
CordRep::Ref(rep);
|
|
|
|
return NeedsUnref(rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* RefIfShared(CordRepType* rep) {
|
|
|
|
return Shared() ? Ref(rep) : rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* RefIfInputShared(CordRepType* rep) {
|
|
|
|
return InputShared() ? Ref(rep) : rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename CordRepType>
|
|
|
|
CordRepType* RefIfInputSharedIndirect(CordRepType* rep) {
|
|
|
|
return InputSharedIndirect() ? Ref(rep) : rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<CordRep*> unrefs_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CordRingCreateTest : public CordRingTestWithParam {
|
|
|
|
public:
|
|
|
|
static TestParams CreateTestParams() {
|
|
|
|
TestParams params;
|
|
|
|
params.emplace_back(InputShareMode::kPrivate);
|
|
|
|
params.emplace_back(InputShareMode::kShared);
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CordRingSubTest : public CordRingTestWithParam {
|
|
|
|
public:
|
|
|
|
static TestParams CreateTestParams() {
|
|
|
|
TestParams params;
|
|
|
|
for (bool refcount_is_one : {true, false}) {
|
|
|
|
TestParam param;
|
|
|
|
param.refcount_is_one = refcount_is_one;
|
|
|
|
params.push_back(param);
|
|
|
|
}
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CordRingBuildTest : public CordRingTestWithParam {
|
|
|
|
public:
|
|
|
|
static TestParams CreateTestParams() {
|
|
|
|
TestParams params;
|
|
|
|
for (bool refcount_is_one : {true, false}) {
|
|
|
|
for (bool with_capacity : {true, false}) {
|
|
|
|
TestParam param;
|
|
|
|
param.refcount_is_one = refcount_is_one;
|
|
|
|
param.with_capacity = with_capacity;
|
|
|
|
params.push_back(param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CordRingCreateFromTreeTest : public CordRingTestWithParam {
|
|
|
|
public:
|
|
|
|
static TestParams CreateTestParams() {
|
|
|
|
TestParams params;
|
|
|
|
params.emplace_back(InputShareMode::kPrivate);
|
|
|
|
params.emplace_back(InputShareMode::kShared);
|
|
|
|
params.emplace_back(InputShareMode::kSharedIndirect);
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CordRingBuildInputTest : public CordRingTestWithParam {
|
|
|
|
public:
|
|
|
|
static TestParams CreateTestParams() {
|
|
|
|
TestParams params;
|
|
|
|
for (bool refcount_is_one : {true, false}) {
|
|
|
|
for (bool with_capacity : {true, false}) {
|
|
|
|
for (InputShareMode share_mode : {kPrivate, kShared, kSharedIndirect}) {
|
|
|
|
TestParam param;
|
|
|
|
param.refcount_is_one = refcount_is_one;
|
|
|
|
param.with_capacity = with_capacity;
|
|
|
|
param.input_share_mode = share_mode;
|
|
|
|
params.push_back(param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(WithParam, CordRingSubTest,
|
|
|
|
testing::ValuesIn(CordRingSubTest::CreateTestParams()),
|
|
|
|
TestParamToString);
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
|
|
WithParam, CordRingCreateTest,
|
|
|
|
testing::ValuesIn(CordRingCreateTest::CreateTestParams()),
|
|
|
|
TestParamToString);
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
|
|
WithParam, CordRingCreateFromTreeTest,
|
|
|
|
testing::ValuesIn(CordRingCreateFromTreeTest::CreateTestParams()),
|
|
|
|
TestParamToString);
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
|
|
WithParam, CordRingBuildTest,
|
|
|
|
testing::ValuesIn(CordRingBuildTest::CreateTestParams()),
|
|
|
|
TestParamToString);
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
|
|
WithParam, CordRingBuildInputTest,
|
|
|
|
testing::ValuesIn(CordRingBuildInputTest::CreateTestParams()),
|
|
|
|
TestParamToString);
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateTest, CreateFromFlat) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1)));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateTest, CreateFromRing) {
|
|
|
|
CordRepRing* ring = RefIfShared(FromFlats(kFoxFlats));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(ring));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringRing) {
|
|
|
|
CordRepRing* ring = RefIfInputSharedIndirect(FromFlats(kFoxFlats));
|
|
|
|
CordRep* sub = RefIfInputShared(MakeSubstring(2, 11, ring));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(sub));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfInputPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToString(result), string_view(kFox).substr(2, 11));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, CreateWithIllegalExtraCapacity) {
|
|
|
|
#if defined(ABSL_HAVE_EXCEPTIONS)
|
|
|
|
CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
|
|
|
|
try {
|
|
|
|
CordRepRing::Create(flat, CordRepRing::kMaxCapacity);
|
|
|
|
GTEST_FAIL() << "expected std::length_error exception";
|
|
|
|
} catch (const std::length_error&) {
|
|
|
|
}
|
|
|
|
#elif defined(GTEST_HAS_DEATH_TEST)
|
|
|
|
CordRep* flat = NeedsUnref(MakeFlat("Hello world"));
|
|
|
|
EXPECT_DEATH(CordRepRing::Create(flat, CordRepRing::kMaxCapacity), ".*");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfFlat) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
auto* flat = RefIfInputShared(MakeFlat(str1));
|
|
|
|
auto* child = RefIfInputSharedIndirect(MakeSubstring(4, 20, flat));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(20));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(4, 20)));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateTest, CreateFromExternal) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
auto* child = RefIfInputShared(MakeExternal(str1));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfExternal) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
auto* external = RefIfInputShared(MakeExternal(str1));
|
|
|
|
auto* child = RefIfInputSharedIndirect(MakeSubstring(1, 24, external));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(24));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1.substr(1, 24)));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateFromTreeTest, CreateFromSubstringOfLargeExternal) {
|
|
|
|
auto* external = RefIfInputShared(MakeFakeExternal(1 << 20));
|
|
|
|
auto str = not_a_string_view(external->base, 1 << 20)
|
|
|
|
.remove_prefix(1 << 19)
|
|
|
|
.remove_suffix(6);
|
|
|
|
auto* child =
|
|
|
|
RefIfInputSharedIndirect(MakeSubstring(1 << 19, (1 << 19) - 6, external));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str.size()));
|
|
|
|
EXPECT_THAT(ToRawFlats(result), ElementsAre(str));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, CreateFromConcat) {
|
|
|
|
CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"),
|
|
|
|
MakeFlat("nopqrstuv"), MakeFlat("wxyz")};
|
|
|
|
auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]);
|
|
|
|
auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3]));
|
|
|
|
auto* concat = RefIfInputShared(MakeConcat(left, right));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(concat));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(26));
|
|
|
|
EXPECT_THAT(ToString(result), Eq(kAlphabet));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, CreateFromSubstringConcat) {
|
|
|
|
for (size_t off = 0; off < 26; ++off) {
|
|
|
|
for (size_t len = 1; len < 26 - off; ++len) {
|
|
|
|
CordRep* flats[] = {MakeFlat("abcdefgh"), MakeFlat("ijklm"),
|
|
|
|
MakeFlat("nopqrstuv"), MakeFlat("wxyz")};
|
|
|
|
auto* left = MakeConcat(RefIfInputSharedIndirect(flats[0]), flats[1]);
|
|
|
|
auto* right = MakeConcat(flats[2], RefIfInputSharedIndirect(flats[3]));
|
|
|
|
auto* concat = MakeConcat(left, right);
|
|
|
|
auto* child = RefIfInputShared(MakeSubstring(off, len, concat));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
ASSERT_THAT(result->length, Eq(len));
|
|
|
|
ASSERT_THAT(ToString(result), string_view(kAlphabet).substr(off, len));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateTest, Properties) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(MakeFlat(str1), 120));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->head(), Eq(0));
|
|
|
|
EXPECT_THAT(result->tail(), Eq(1));
|
|
|
|
EXPECT_THAT(result->capacity(), Ge(120 + 1));
|
|
|
|
EXPECT_THAT(result->capacity(), Le(2 * 120 + 1));
|
|
|
|
EXPECT_THAT(result->entries(), Eq(1));
|
|
|
|
EXPECT_THAT(result->begin_pos(), Eq(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateTest, EntryForNewFlat) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
CordRep* child = MakeFlat(str1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child, 120));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->entry_child(0), Eq(child));
|
|
|
|
EXPECT_THAT(result->entry_end_pos(0), Eq(str1.length()));
|
|
|
|
EXPECT_THAT(result->entry_data_offset(0), Eq(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingCreateTest, EntryForNewFlatSubstring) {
|
|
|
|
absl::string_view str1 = "1234567890abcdefghijklmnopqrstuvwxyz";
|
|
|
|
CordRep* child = MakeFlat(str1);
|
|
|
|
CordRep* substring = MakeSubstring(10, 26, child);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(substring, 1));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->entry_child(0), Eq(child));
|
|
|
|
EXPECT_THAT(result->entry_end_pos(0), Eq(26));
|
|
|
|
EXPECT_THAT(result->entry_data_offset(0), Eq(10));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendFlat) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2)));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, PrependFlat) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2)));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendString) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendStringHavingExtra) {
|
|
|
|
absl::string_view str1 = "1234";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat(str1, 26), 0);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) {
|
|
|
|
absl::string_view str1 = "1234";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
|
|
|
|
// Create flat with at least one extra byte. We don't expect to have sized
|
|
|
|
// alloc and capacity rounding to grant us enough to not make it partial.
|
|
|
|
auto* flat = MakeFlat(str1, 1);
|
|
|
|
size_t avail = flat->flat()->Capacity() - flat->length;
|
|
|
|
ASSERT_THAT(avail, Lt(str2.size())) << " adjust test for larger flats!";
|
|
|
|
|
|
|
|
// Construct the flats we do expect using all of `avail`.
|
|
|
|
absl::string_view str1a = str2.substr(0, avail);
|
|
|
|
absl::string_view str2a = str2.substr(avail);
|
|
|
|
|
|
|
|
CordRepRing* ring = CreateWithCapacity(flat, 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
if (GetParam().refcount_is_one) {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a));
|
|
|
|
} else {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendStringHavingExtraInSubstring) {
|
|
|
|
absl::string_view str1 = "123456789_1234";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRep* flat = RemovePrefix(10, MakeFlat(str1, 26));
|
|
|
|
CordRepRing* ring = CreateWithCapacity(flat, 0);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(result->length, Eq(4 + str2.size()));
|
|
|
|
if (GetParam().refcount_is_one) {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2)));
|
|
|
|
} else {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendStringHavingSharedExtra) {
|
|
|
|
absl::string_view str1 = "123456789_1234";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
for (int shared_type = 0; shared_type < 2; ++shared_type) {
|
|
|
|
SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type));
|
|
|
|
|
|
|
|
// Create a flat that is shared in some way.
|
|
|
|
CordRep* flat = nullptr;
|
|
|
|
CordRep* flat1 = nullptr;
|
|
|
|
if (shared_type == 0) {
|
|
|
|
// Shared flat
|
|
|
|
flat = CordRep::Ref(MakeFlat(str1.substr(10), 100));
|
|
|
|
} else if (shared_type == 1) {
|
|
|
|
// Shared flat inside private substring
|
|
|
|
flat1 = CordRep::Ref(MakeFlat(str1));
|
|
|
|
flat = RemovePrefix(10, flat1);
|
|
|
|
} else {
|
|
|
|
// Private flat inside shared substring
|
|
|
|
flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100)));
|
|
|
|
}
|
|
|
|
|
|
|
|
CordRepRing* ring = CreateWithCapacity(flat, 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(result->length, Eq(4 + str2.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
|
|
|
|
|
|
|
|
CordRep::Unref(shared_type == 1 ? flat1 : flat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendStringWithExtra) {
|
|
|
|
absl::string_view str1 = "1234";
|
|
|
|
absl::string_view str2 = "1234567890";
|
|
|
|
absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2, 26));
|
|
|
|
result = CordRepRing::Append(result, str3);
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str1, StrCat(str2, str3)));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, PrependString) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
// Use external rep to avoid appending to first flat
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
if (GetParam().with_capacity && GetParam().refcount_is_one) {
|
|
|
|
EXPECT_THAT(result, Eq(ring));
|
|
|
|
} else {
|
|
|
|
EXPECT_THAT(result, Ne(ring));
|
|
|
|
}
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, PrependStringHavingExtra) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz1234";
|
|
|
|
absl::string_view str2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRep* flat = RemovePrefix(26, MakeFlat(str1));
|
|
|
|
CordRepRing* ring = CreateWithCapacity(flat, 0);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(result->length, Eq(4 + str2.size()));
|
|
|
|
if (GetParam().refcount_is_one) {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234")));
|
|
|
|
} else {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str2, "1234"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, PrependStringHavingSharedExtra) {
|
|
|
|
absl::string_view str1 = "123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
absl::string_view str2 = "abcdefghij";
|
|
|
|
absl::string_view str1a = str1.substr(10);
|
|
|
|
for (int shared_type = 1; shared_type < 2; ++shared_type) {
|
|
|
|
SCOPED_TRACE(absl::StrCat("Shared extra type ", shared_type));
|
|
|
|
|
|
|
|
// Create a flat that is shared in some way.
|
|
|
|
CordRep* flat = nullptr;
|
|
|
|
CordRep* flat1 = nullptr;
|
|
|
|
if (shared_type == 1) {
|
|
|
|
// Shared flat inside private substring
|
|
|
|
flat = RemovePrefix(10, flat1 = CordRep::Ref(MakeFlat(str1)));
|
|
|
|
} else {
|
|
|
|
// Private flat inside shared substring
|
|
|
|
flat = CordRep::Ref(RemovePrefix(10, MakeFlat(str1, 100)));
|
|
|
|
}
|
|
|
|
|
|
|
|
CordRepRing* ring = CreateWithCapacity(flat, 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result->length, Eq(str1a.size() + str2.size()));
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a));
|
|
|
|
CordRep::Unref(shared_type == 1 ? flat1 : flat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, PrependStringWithExtra) {
|
|
|
|
absl::string_view str1 = "1234";
|
|
|
|
absl::string_view str2 = "1234567890";
|
|
|
|
absl::string_view str3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeExternal(str1), 1);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2, 26));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
result = CordRepRing::Prepend(result, str3);
|
|
|
|
EXPECT_THAT(result->length, Eq(str1.size() + str2.size() + str3.size()));
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str3, str2), str1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendPrependStringMix) {
|
|
|
|
const auto& flats = kFoxFlats;
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4]), 8);
|
|
|
|
CordRepRing* result = ring;
|
|
|
|
for (int i = 1; i <= 4; ++i) {
|
|
|
|
result = CordRepRing::Prepend(result, flats[4 - i]);
|
|
|
|
result = CordRepRing::Append(result, flats[4 + i]);
|
|
|
|
}
|
|
|
|
NeedsUnref(result);
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToString(result), kFox);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendPrependStringMixWithExtra) {
|
|
|
|
const auto& flats = kFoxFlats;
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat(flats[4], 100), 8);
|
|
|
|
CordRepRing* result = ring;
|
|
|
|
for (int i = 1; i <= 4; ++i) {
|
|
|
|
result = CordRepRing::Prepend(result, flats[4 - i], 100);
|
|
|
|
result = CordRepRing::Append(result, flats[4 + i], 100);
|
|
|
|
}
|
|
|
|
NeedsUnref(result);
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
if (GetParam().refcount_is_one) {
|
|
|
|
EXPECT_THAT(ToFlats(result),
|
|
|
|
ElementsAre("The quick brown fox ", "jumps over the lazy dog"));
|
|
|
|
} else {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
|
|
|
|
"over the lazy dog"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendPrependStringMixWithPrependedExtra) {
|
|
|
|
const auto& flats = kFoxFlats;
|
|
|
|
CordRep* flat = MakeFlat(StrCat(std::string(50, '.'), flats[4]), 50);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(RemovePrefix(50, flat), 0);
|
|
|
|
CordRepRing* result = ring;
|
|
|
|
for (int i = 1; i <= 4; ++i) {
|
|
|
|
result = CordRepRing::Prepend(result, flats[4 - i], 100);
|
|
|
|
result = CordRepRing::Append(result, flats[4 + i], 100);
|
|
|
|
}
|
|
|
|
result = NeedsUnref(result);
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
if (GetParam().refcount_is_one) {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre(kFox));
|
|
|
|
} else {
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("The quick brown fox ", "jumps ",
|
|
|
|
"over the lazy dog"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingSubTest, SubRing) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
string_view all = kFox;
|
|
|
|
for (size_t offset = 0; offset < all.size() - 1; ++offset) {
|
|
|
|
CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
|
|
|
|
EXPECT_THAT(result, nullptr);
|
|
|
|
|
|
|
|
for (size_t len = 1; len < all.size() - offset; ++len) {
|
|
|
|
ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
ASSERT_THAT(ToString(result), Eq(all.substr(offset, len)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingSubTest, SubRingFromLargeExternal) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
std::string large_string(1 << 20, '.');
|
|
|
|
const char* flats[] = {
|
|
|
|
"abcdefghijklmnopqrstuvwxyz",
|
|
|
|
large_string.c_str(),
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
|
|
};
|
|
|
|
std::string buffer = absl::StrCat(flats[0], flats[1], flats[2]);
|
|
|
|
absl::string_view all = buffer;
|
|
|
|
for (size_t offset = 0; offset < 30; ++offset) {
|
|
|
|
CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
CordRepRing* result = CordRepRing::SubRing(ring, offset, 0);
|
|
|
|
EXPECT_THAT(result, nullptr);
|
|
|
|
|
|
|
|
for (size_t len = all.size() - 30; len < all.size() - offset; ++len) {
|
|
|
|
ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
auto str = ToString(result);
|
|
|
|
ASSERT_THAT(str, SizeIs(len));
|
|
|
|
ASSERT_THAT(str, Eq(all.substr(offset, len)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingSubTest, RemovePrefix) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
string_view all = kFox;
|
|
|
|
CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
CordRepRing* result = CordRepRing::RemovePrefix(ring, all.size());
|
|
|
|
EXPECT_THAT(result, nullptr);
|
|
|
|
|
|
|
|
for (size_t len = 1; len < all.size(); ++len) {
|
|
|
|
ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
result = NeedsUnref(CordRepRing::RemovePrefix(ring, len));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToString(result), Eq(all.substr(len)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingSubTest, RemovePrefixFromLargeExternal) {
|
|
|
|
CordRepExternal* external1 = MakeFakeExternal(1 << 20);
|
|
|
|
CordRepExternal* external2 = MakeFakeExternal(1 << 20);
|
|
|
|
CordRepRing* ring = CordRepRing::Create(external1, 1);
|
|
|
|
ring = CordRepRing::Append(ring, external2);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::RemovePrefix(ring, 1 << 16));
|
|
|
|
EXPECT_THAT(
|
|
|
|
ToRawFlats(result),
|
|
|
|
ElementsAre(
|
|
|
|
not_a_string_view(external1->base, 1 << 20).remove_prefix(1 << 16),
|
|
|
|
not_a_string_view(external2->base, 1 << 20)));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingSubTest, RemoveSuffix) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
string_view all = kFox;
|
|
|
|
CordRepRing* ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
CordRepRing* result = CordRepRing::RemoveSuffix(ring, all.size());
|
|
|
|
EXPECT_THAT(result, nullptr);
|
|
|
|
|
|
|
|
for (size_t len = 1; len < all.size(); ++len) {
|
|
|
|
ring = RefIfShared(FromFlats(flats, composition));
|
|
|
|
result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingSubTest, AppendRing) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats).subspan(1);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat(kFoxFlats[0]), flats.size());
|
|
|
|
CordRepRing* child = FromFlats(flats, composition);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, AppendRingWithFlatOffset) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
|
|
|
|
CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RemovePrefix(10, child);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ",
|
|
|
|
"over ", "the ", "lazy ", "dog"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, AppendRingWithBrokenOffset) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
|
|
|
|
CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RemovePrefix(21, child);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result),
|
|
|
|
ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, AppendRingWithFlatLength) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
|
|
|
|
CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RemoveSuffix(8, child);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
|
|
|
|
"fox ", "jumps ", "over ", "the "));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendRingWithBrokenFlatLength) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
|
|
|
|
CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RemoveSuffix(15, child);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
|
|
|
|
"fox ", "jumps ", "ov"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendRingMiddlePiece) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
|
|
|
|
CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = MakeSubstring(7, child->length - 27, child);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result),
|
|
|
|
ElementsAre("Head", "ck ", "brown ", "fox ", "jum"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildTest, AppendRingSinglePiece) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Head"), flats.size());
|
|
|
|
CordRep* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, AppendRingSinglePieceWithPrefix) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0);
|
|
|
|
CordRepRing* ring = CordRepRing::Create(MakeFlat("Head"), extra_capacity);
|
|
|
|
ring->SetCapacityForTesting(1 + extra_capacity);
|
|
|
|
ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend")));
|
|
|
|
assert(ring->IsValid(std::cout));
|
|
|
|
CordRepRing* child = RefIfInputSharedIndirect(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputShared(MakeSubstring(11, 3, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRing) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto fox = MakeSpan(kFoxFlats);
|
|
|
|
auto flats = MakeSpan(fox).subspan(0, fox.size() - 1);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat(fox.back()), flats.size());
|
|
|
|
CordRepRing* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingWithFlatOffset) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(10, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ",
|
|
|
|
"the ", "lazy ", "dog", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingWithBrokenOffset) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result),
|
|
|
|
ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingWithFlatLength) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(8, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
|
|
|
|
"jumps ", "over ", "the ", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingWithBrokenFlatLength) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputSharedIndirect(RemoveSuffix(15, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
|
|
|
|
"jumps ", "ov", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingMiddlePiece) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped =
|
|
|
|
RefIfInputSharedIndirect(MakeSubstring(7, child->length - 27, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result),
|
|
|
|
ElementsAre("ck ", "brown ", "fox ", "jum", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingSinglePiece) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CreateWithCapacity(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_P(CordRingBuildInputTest, PrependRingSinglePieceWithPrefix) {
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
size_t extra_capacity = 1 + (GetParam().with_capacity ? flats.size() : 0);
|
|
|
|
CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), extra_capacity);
|
|
|
|
ring->SetCapacityForTesting(1 + extra_capacity);
|
|
|
|
ring = RefIfShared(CordRepRing::Prepend(ring, MakeFlat("Prepend")));
|
|
|
|
CordRep* child = RefIfInputShared(FromFlats(flats, composition));
|
|
|
|
CordRep* stripped = RefIfInputSharedIndirect(MakeSubstring(11, 3, child));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
|
|
|
|
EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, Find) {
|
|
|
|
constexpr const char* flats[] = {
|
|
|
|
"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
|
|
|
|
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
|
|
|
|
"+-=", "[]\\{}|;':", ",/<>?", "."};
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
|
|
|
|
std::string value = ToString(ring);
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
CordRepRing::Position found = ring->Find(i);
|
|
|
|
auto data = ring->entry_data(found.index);
|
|
|
|
ASSERT_THAT(found.offset, Lt(data.length()));
|
|
|
|
ASSERT_THAT(data[found.offset], Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, FindWithHint) {
|
|
|
|
constexpr const char* flats[] = {
|
|
|
|
"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
|
|
|
|
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
|
|
|
|
"+-=", "[]\\{}|;':", ",/<>?", "."};
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
|
|
|
|
std::string value = ToString(ring);
|
|
|
|
|
|
|
|
#if defined(GTEST_HAS_DEATH_TEST)
|
|
|
|
// Test hint beyond valid position
|
|
|
|
index_type head = ring->head();
|
|
|
|
EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 0), ".*");
|
|
|
|
EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head), 9), ".*");
|
|
|
|
EXPECT_DEBUG_DEATH(ring->Find(ring->advance(head, 3), 24), ".*");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int flat_pos = 0;
|
|
|
|
size_t flat_offset = 0;
|
|
|
|
for (auto sflat : flats) {
|
|
|
|
string_view flat(sflat);
|
|
|
|
for (int offset = 0; offset < flat.length(); ++offset) {
|
|
|
|
for (int start = 0; start <= flat_pos; ++start) {
|
|
|
|
index_type hint = ring->advance(ring->head(), start);
|
|
|
|
CordRepRing::Position found = ring->Find(hint, flat_offset + offset);
|
|
|
|
ASSERT_THAT(found.index, Eq(ring->advance(ring->head(), flat_pos)));
|
|
|
|
ASSERT_THAT(found.offset, Eq(offset));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++flat_pos;
|
|
|
|
flat_offset += flat.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, FindInLargeRing) {
|
|
|
|
constexpr const char* flats[] = {
|
|
|
|
"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
|
|
|
|
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
|
|
|
|
"+-=", "[]\\{}|;':", ",/<>?", "."};
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
CordRepRing* ring = FromFlats(flats, composition);
|
|
|
|
for (int i = 0; i < 13; ++i) {
|
|
|
|
ring = CordRepRing::Append(ring, FromFlats(flats, composition));
|
|
|
|
}
|
|
|
|
NeedsUnref(ring);
|
|
|
|
std::string value = ToString(ring);
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
CordRepRing::Position pos = ring->Find(i);
|
|
|
|
auto data = ring->entry_data(pos.index);
|
|
|
|
ASSERT_THAT(pos.offset, Lt(data.length()));
|
|
|
|
ASSERT_THAT(data[pos.offset], Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, FindTail) {
|
|
|
|
constexpr const char* flats[] = {
|
|
|
|
"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
|
|
|
|
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
|
|
|
|
"+-=", "[]\\{}|;':", ",/<>?", "."};
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
|
|
|
|
std::string value = ToString(ring);
|
|
|
|
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
CordRepRing::Position pos = ring->FindTail(i + 1);
|
|
|
|
auto data = ring->entry_data(ring->retreat(pos.index));
|
|
|
|
ASSERT_THAT(pos.offset, Lt(data.length()));
|
|
|
|
ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, FindTailWithHint) {
|
|
|
|
constexpr const char* flats[] = {
|
|
|
|
"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
|
|
|
|
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
|
|
|
|
"+-=", "[]\\{}|;':", ",/<>?", "."};
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
CordRepRing* ring = NeedsUnref(FromFlats(flats, composition));
|
|
|
|
std::string value = ToString(ring);
|
|
|
|
|
|
|
|
// Test hint beyond valid position
|
|
|
|
#if defined(GTEST_HAS_DEATH_TEST)
|
|
|
|
index_type head = ring->head();
|
|
|
|
EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 1), ".*");
|
|
|
|
EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head), 10), ".*");
|
|
|
|
EXPECT_DEBUG_DEATH(ring->FindTail(ring->advance(head, 3), 26), ".*");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
CordRepRing::Position pos = ring->FindTail(i + 1);
|
|
|
|
auto data = ring->entry_data(ring->retreat(pos.index));
|
|
|
|
ASSERT_THAT(pos.offset, Lt(data.length()));
|
|
|
|
ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, FindTailInLargeRing) {
|
|
|
|
constexpr const char* flats[] = {
|
|
|
|
"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ",
|
|
|
|
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_",
|
|
|
|
"+-=", "[]\\{}|;':", ",/<>?", "."};
|
|
|
|
auto composition = RandomComposition();
|
|
|
|
SCOPED_TRACE(ToString(composition));
|
|
|
|
CordRepRing* ring = FromFlats(flats, composition);
|
|
|
|
for (int i = 0; i < 13; ++i) {
|
|
|
|
ring = CordRepRing::Append(ring, FromFlats(flats, composition));
|
|
|
|
}
|
|
|
|
NeedsUnref(ring);
|
|
|
|
std::string value = ToString(ring);
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
CordRepRing::Position pos = ring->FindTail(i + 1);
|
|
|
|
auto data = ring->entry_data(ring->retreat(pos.index));
|
|
|
|
ASSERT_THAT(pos.offset, Lt(data.length()));
|
|
|
|
ASSERT_THAT(data[data.length() - pos.offset - 1], Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, GetCharacter) {
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = CordRepRing::Create(MakeFlat("Tail"), flats.size());
|
|
|
|
CordRep* child = FromFlats(flats, kAppend);
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
|
|
|
|
std::string value = ToString(result);
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, GetCharacterWithSubstring) {
|
|
|
|
absl::string_view str1 = "abcdefghijklmnopqrstuvwxyz";
|
|
|
|
auto* child = MakeSubstring(4, 20, MakeFlat(str1));
|
|
|
|
CordRepRing* result = NeedsUnref(CordRepRing::Create(child));
|
|
|
|
ASSERT_THAT(result, IsValidRingBuffer());
|
|
|
|
std::string value = ToString(result);
|
|
|
|
for (int i = 0; i < value.length(); ++i) {
|
|
|
|
ASSERT_THAT(result->GetCharacter(i), Eq(value[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, IsFlatSingleFlat) {
|
|
|
|
for (bool external : {false, true}) {
|
|
|
|
SCOPED_TRACE(external ? "With External" : "With Flat");
|
|
|
|
absl::string_view str = "Hello world";
|
|
|
|
CordRep* rep = external ? MakeExternal(str) : MakeFlat(str);
|
|
|
|
CordRepRing* ring = NeedsUnref(CordRepRing::Create(rep));
|
|
|
|
|
|
|
|
// The ring is a single non-fragmented flat:
|
|
|
|
absl::string_view fragment;
|
|
|
|
EXPECT_TRUE(ring->IsFlat(nullptr));
|
|
|
|
EXPECT_TRUE(ring->IsFlat(&fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("Hello world"));
|
|
|
|
fragment = "";
|
|
|
|
EXPECT_TRUE(ring->IsFlat(0, 11, nullptr));
|
|
|
|
EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("Hello world"));
|
|
|
|
|
|
|
|
// Arbitrary ranges must check true as well.
|
|
|
|
EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("ello"));
|
|
|
|
EXPECT_TRUE(ring->IsFlat(6, 5, &fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("world"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, IsFlatMultiFlat) {
|
|
|
|
for (bool external : {false, true}) {
|
|
|
|
SCOPED_TRACE(external ? "With External" : "With Flat");
|
|
|
|
absl::string_view str1 = "Hello world";
|
|
|
|
absl::string_view str2 = "Halt and catch fire";
|
|
|
|
CordRep* rep1 = external ? MakeExternal(str1) : MakeFlat(str1);
|
|
|
|
CordRep* rep2 = external ? MakeExternal(str2) : MakeFlat(str2);
|
|
|
|
CordRepRing* ring = CordRepRing::Append(CordRepRing::Create(rep1), rep2);
|
|
|
|
NeedsUnref(ring);
|
|
|
|
|
|
|
|
// The ring is fragmented, IsFlat() on the entire cord must be false.
|
|
|
|
EXPECT_FALSE(ring->IsFlat(nullptr));
|
|
|
|
absl::string_view fragment = "Don't touch this";
|
|
|
|
EXPECT_FALSE(ring->IsFlat(&fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("Don't touch this"));
|
|
|
|
|
|
|
|
// Check for ranges exactly within both flats.
|
|
|
|
EXPECT_TRUE(ring->IsFlat(0, 11, &fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("Hello world"));
|
|
|
|
EXPECT_TRUE(ring->IsFlat(11, 19, &fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("Halt and catch fire"));
|
|
|
|
|
|
|
|
// Check for arbitrary partial range inside each flat.
|
|
|
|
EXPECT_TRUE(ring->IsFlat(1, 4, &fragment));
|
|
|
|
EXPECT_THAT(fragment, "ello");
|
|
|
|
EXPECT_TRUE(ring->IsFlat(26, 4, &fragment));
|
|
|
|
EXPECT_THAT(fragment, "fire");
|
|
|
|
|
|
|
|
// Check ranges spanning across both flats
|
|
|
|
fragment = "Don't touch this";
|
|
|
|
EXPECT_FALSE(ring->IsFlat(1, 18, &fragment));
|
|
|
|
EXPECT_FALSE(ring->IsFlat(10, 2, &fragment));
|
|
|
|
EXPECT_THAT(fragment, Eq("Don't touch this"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(CordRingTest, Dump) {
|
|
|
|
std::stringstream ss;
|
|
|
|
auto flats = MakeSpan(kFoxFlats);
|
|
|
|
CordRepRing* ring = NeedsUnref(FromFlats(flats, kPrepend));
|
|
|
|
ss << *ring;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
ABSL_NAMESPACE_END
|
|
|
|
} // namespace absl
|