-- 3b43586da865534cf86401d2cae09c65c60b8474 by Abseil Team <absl-team@google.com>: Introduce CordRepRingReader class PiperOrigin-RevId: 353070937 -- 0bff6e4bcca34fdd1e6610da5fb3c37fd49b2940 by Abseil Team <absl-team@google.com>: Fix docstring typo "Exmaple" -> "Example" PiperOrigin-RevId: 352927688 -- 1ef4e0a1100cfa7bc9d9e8f155acf0e469348b56 by Abseil Team <absl-team@google.com>: Refactor tree initialization of ChunkIterator and CordReader PiperOrigin-RevId: 352916786 -- 919c3eb175b87294184a405785eef4fab520d47e by Abseil Team <absl-team@google.com>: Disable `preserve_most` when compiling with sanitizers. PiperOrigin-RevId: 352890630 GitOrigin-RevId: 3b43586da865534cf86401d2cae09c65c60b8474 Change-Id: I8a733494b353af69a46862a4019a7f9b40148f49pull/889/head
parent
22771d4719
commit
3a2d6572d0
8 changed files with 344 additions and 3 deletions
@ -0,0 +1,173 @@ |
||||
// 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/debugging/leak_check.h" |
||||
#include "absl/strings/internal/cord_internal.h" |
||||
#include "absl/strings/internal/cord_rep_ring.h" |
||||
#include "absl/strings/internal/cord_rep_ring_reader.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace cord_internal { |
||||
namespace { |
||||
|
||||
using testing::Eq; |
||||
|
||||
// Creates a flat for testing
|
||||
CordRep* MakeFlat(absl::string_view s) { |
||||
CordRepFlat* flat = CordRepFlat::New(s.length()); |
||||
memcpy(flat->Data(), s.data(), s.length()); |
||||
flat->length = s.length(); |
||||
return flat; |
||||
} |
||||
|
||||
CordRepRing* FromFlats(Span<absl::string_view const> flats) { |
||||
CordRepRing* ring = CordRepRing::Create(MakeFlat(flats[0]), flats.size() - 1); |
||||
for (int i = 1; i < flats.size(); ++i) { |
||||
ring = CordRepRing::Append(ring, MakeFlat(flats[i])); |
||||
} |
||||
return ring; |
||||
} |
||||
|
||||
std::array<absl::string_view, 12> TestFlats() { |
||||
return {"abcdefghij", "klmnopqrst", "uvwxyz", "ABCDEFGHIJ", |
||||
"KLMNOPQRST", "UVWXYZ", "1234567890", "~!@#$%^&*()_", |
||||
"+-=", "[]\\{}|;':", ",/<>?", "."}; |
||||
} |
||||
|
||||
TEST(CordRingReaderTest, DefaultInstance) { |
||||
CordRepRingReader reader; |
||||
EXPECT_FALSE(static_cast<bool>(reader)); |
||||
EXPECT_THAT(reader.ring(), Eq(nullptr)); |
||||
#ifndef NDEBUG |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.length(), ".*"); |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.consumed(), ".*"); |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.remaining(), ".*"); |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*"); |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.Seek(0), ".*"); |
||||
#endif |
||||
} |
||||
|
||||
TEST(CordRingReaderTest, Reset) { |
||||
CordRepRingReader reader; |
||||
auto flats = TestFlats(); |
||||
CordRepRing* ring = FromFlats(flats); |
||||
|
||||
absl::string_view first = reader.Reset(ring); |
||||
EXPECT_THAT(first, Eq(flats[0])); |
||||
EXPECT_TRUE(static_cast<bool>(reader)); |
||||
EXPECT_THAT(reader.ring(), Eq(ring)); |
||||
EXPECT_THAT(reader.index(), Eq(ring->head())); |
||||
EXPECT_THAT(reader.length(), Eq(ring->length)); |
||||
EXPECT_THAT(reader.consumed(), Eq(flats[0].length())); |
||||
EXPECT_THAT(reader.remaining(), Eq(ring->length - reader.consumed())); |
||||
|
||||
reader.Reset(); |
||||
EXPECT_FALSE(static_cast<bool>(reader)); |
||||
EXPECT_THAT(reader.ring(), Eq(nullptr)); |
||||
|
||||
CordRep::Unref(ring); |
||||
} |
||||
|
||||
TEST(CordRingReaderTest, Next) { |
||||
CordRepRingReader reader; |
||||
auto flats = TestFlats(); |
||||
CordRepRing* ring = FromFlats(flats); |
||||
CordRepRing::index_type head = ring->head(); |
||||
|
||||
reader.Reset(ring); |
||||
size_t consumed = reader.consumed(); |
||||
size_t remaining = reader.remaining(); |
||||
for (int i = 1; i < flats.size(); ++i) { |
||||
consumed += flats[i].length(); |
||||
remaining -= flats[i].length(); |
||||
absl::string_view next = reader.Next(); |
||||
ASSERT_THAT(next, Eq(flats[i])); |
||||
ASSERT_THAT(reader.index(), Eq(ring->advance(head, i))); |
||||
ASSERT_THAT(reader.consumed(), Eq(consumed)); |
||||
ASSERT_THAT(reader.remaining(), Eq(remaining)); |
||||
} |
||||
|
||||
#ifndef NDEBUG |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.Next(), ".*"); |
||||
#endif |
||||
|
||||
CordRep::Unref(ring); |
||||
} |
||||
|
||||
TEST(CordRingReaderTest, SeekForward) { |
||||
CordRepRingReader reader; |
||||
auto flats = TestFlats(); |
||||
CordRepRing* ring = FromFlats(flats); |
||||
CordRepRing::index_type head = ring->head(); |
||||
|
||||
reader.Reset(ring); |
||||
size_t consumed = 0; |
||||
size_t remaining = ring->length;; |
||||
for (int i = 0; i < flats.size(); ++i) { |
||||
size_t offset = consumed; |
||||
consumed += flats[i].length(); |
||||
remaining -= flats[i].length(); |
||||
for (int off = 0; off < flats[i].length(); ++off) { |
||||
absl::string_view chunk = reader.Seek(offset + off); |
||||
ASSERT_THAT(chunk, Eq(flats[i].substr(off))); |
||||
ASSERT_THAT(reader.index(), Eq(ring->advance(head, i))); |
||||
ASSERT_THAT(reader.consumed(), Eq(consumed)); |
||||
ASSERT_THAT(reader.remaining(), Eq(remaining)); |
||||
} |
||||
} |
||||
|
||||
CordRep::Unref(ring); |
||||
} |
||||
|
||||
TEST(CordRingReaderTest, SeekBackward) { |
||||
CordRepRingReader reader; |
||||
auto flats = TestFlats(); |
||||
CordRepRing* ring = FromFlats(flats); |
||||
CordRepRing::index_type head = ring->head(); |
||||
|
||||
reader.Reset(ring); |
||||
size_t consumed = ring->length; |
||||
size_t remaining = 0; |
||||
for (int i = flats.size() - 1; i >= 0; --i) { |
||||
size_t offset = consumed - flats[i].length(); |
||||
for (int off = 0; off < flats[i].length(); ++off) { |
||||
absl::string_view chunk = reader.Seek(offset + off); |
||||
ASSERT_THAT(chunk, Eq(flats[i].substr(off))); |
||||
ASSERT_THAT(reader.index(), Eq(ring->advance(head, i))); |
||||
ASSERT_THAT(reader.consumed(), Eq(consumed)); |
||||
ASSERT_THAT(reader.remaining(), Eq(remaining)); |
||||
} |
||||
consumed -= flats[i].length(); |
||||
remaining += flats[i].length(); |
||||
} |
||||
#ifndef NDEBUG |
||||
EXPECT_DEATH_IF_SUPPORTED(reader.Seek(ring->length), ".*"); |
||||
#endif |
||||
CordRep::Unref(ring); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace cord_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,114 @@ |
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ |
||||
#define ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_ |
||||
|
||||
#include <cassert> |
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
|
||||
#include "absl/strings/internal/cord_internal.h" |
||||
#include "absl/strings/internal/cord_rep_ring.h" |
||||
#include "absl/strings/string_view.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace cord_internal { |
||||
|
||||
// CordRepRingReader provides basic navigation over CordRepRing data.
|
||||
class CordRepRingReader { |
||||
public: |
||||
// Returns true if this instance is not empty.
|
||||
explicit operator bool() const { return ring_ != nullptr; } |
||||
|
||||
// Returns the ring buffer reference for this instance, or nullptr if empty.
|
||||
CordRepRing* ring() const { return ring_; } |
||||
|
||||
// Returns the current node index inside the ring buffer for this instance.
|
||||
// The returned value is undefined if this instance is empty.
|
||||
CordRepRing::index_type index() const { return index_; } |
||||
|
||||
// Returns the length of the referenced ring buffer.
|
||||
// Requires the current instance to be non empty.
|
||||
size_t length() const { |
||||
assert(ring_); |
||||
return ring_->length; |
||||
} |
||||
|
||||
// Returns the end offset of the last navigated-to chunk, which represents the
|
||||
// total bytes 'consumed' relative to the start of the ring. The returned
|
||||
// value is never zero. For example, initializing a reader with a ring buffer
|
||||
// with a first chunk of 19 bytes will return consumed() = 19.
|
||||
// Requires the current instance to be non empty.
|
||||
size_t consumed() const { |
||||
assert(ring_); |
||||
return ring_->entry_end_offset(index_); |
||||
} |
||||
|
||||
// Returns the number of bytes remaining beyond the last navigated-to chunk.
|
||||
// Requires the current instance to be non empty.
|
||||
size_t remaining() const { |
||||
assert(ring_); |
||||
return length() - consumed(); |
||||
} |
||||
|
||||
// Resets this instance to an empty value
|
||||
void Reset() { ring_ = nullptr; } |
||||
|
||||
// Resets this instance to the start of `ring`. `ring` must not be null.
|
||||
// Returns a reference into the first chunk of the provided ring.
|
||||
absl::string_view Reset(CordRepRing* ring) { |
||||
assert(ring); |
||||
ring_ = ring; |
||||
index_ = ring_->head(); |
||||
return ring_->entry_data(index_); |
||||
} |
||||
|
||||
// Navigates to the next chunk inside the reference ring buffer.
|
||||
// Returns a reference into the navigated-to chunk.
|
||||
// Requires remaining() to be non zero.
|
||||
absl::string_view Next() { |
||||
assert(remaining()); |
||||
index_ = ring_->advance(index_); |
||||
return ring_->entry_data(index_); |
||||
} |
||||
|
||||
// Navigates to the chunk at offset `offset`.
|
||||
// Returns a reference into the navigated-to chunk, adjusted for the relative
|
||||
// position of `offset` into that chunk. For example, calling Seek(13) on a
|
||||
// ring buffer containing 2 chunks of 10 and 20 bytes respectively will return
|
||||
// a string view into the second chunk starting at offset 3 with a size of 17.
|
||||
// Requires `offset` to be less than `length()`
|
||||
absl::string_view Seek(size_t offset) { |
||||
assert(offset < length()); |
||||
size_t current = ring_->entry_end_offset(index_); |
||||
CordRepRing::index_type hint = (offset >= current) ? index_ : ring_->head(); |
||||
const CordRepRing::Position head = ring_->Find(hint, offset); |
||||
index_ = head.index; |
||||
auto data = ring_->entry_data(head.index); |
||||
data.remove_prefix(head.offset); |
||||
return data; |
||||
} |
||||
|
||||
private: |
||||
CordRepRing* ring_ = nullptr; |
||||
CordRepRing::index_type index_; |
||||
}; |
||||
|
||||
} // namespace cord_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STRINGS_INTERNAL_CORD_REP_RING_READER_H_
|
Loading…
Reference in new issue