blob: b881dc53bfa8ec102b4f0974c819aef123f404ee [file]
// Copyright 2021 Google LLC
//
// 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
//
// http://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 RIEGELI_BYTES_PREFIX_LIMITING_READER_H_
#define RIEGELI_BYTES_PREFIX_LIMITING_READER_H_
#include <stddef.h>
#include <memory>
#include <optional>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/chain.h"
#include "riegeli/base/dependency.h"
#include "riegeli/base/initializer.h"
#include "riegeli/base/moving_dependency.h"
#include "riegeli/base/object.h"
#include "riegeli/base/types.h"
#include "riegeli/bytes/reader.h"
namespace riegeli {
class BackwardWriter;
class Writer;
// Template parameter independent part of `PrefixLimitingReader`.
class PrefixLimitingReaderBase : public Reader {
public:
class Options {
public:
Options() noexcept {}
Options(const Options& that) = default;
Options& operator=(const Options& that) = default;
// The base position of the original `Reader`. It must be at least as large
// as the initial position.
//
// `std::nullopt` means the current position.
//
// Default: `std::nullopt`.
Options& set_base_pos(std::optional<Position> base_pos) &
ABSL_ATTRIBUTE_LIFETIME_BOUND {
base_pos_ = base_pos;
return *this;
}
Options&& set_base_pos(std::optional<Position> base_pos) &&
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return std::move(set_base_pos(base_pos));
}
std::optional<Position> base_pos() const { return base_pos_; }
private:
std::optional<Position> base_pos_;
};
// Returns the original `Reader`. Unchanged by `Close()`.
virtual Reader* SrcReader() const ABSL_ATTRIBUTE_LIFETIME_BOUND = 0;
// Returns the base position of the original `Reader`.
Position base_pos() const { return base_pos_; }
bool ToleratesReadingAhead() override;
bool SupportsRandomAccess() override;
bool SupportsRewind() override;
bool SupportsSize() override;
bool SupportsNewReader() override;
protected:
using Reader::Reader;
PrefixLimitingReaderBase(PrefixLimitingReaderBase&& that) noexcept;
PrefixLimitingReaderBase& operator=(PrefixLimitingReaderBase&& that) noexcept;
void Reset(Closed);
void Reset();
void Initialize(Reader* src, std::optional<Position> base_pos);
ABSL_ATTRIBUTE_COLD absl::Status AnnotateOverSrc(absl::Status status);
// Sets cursor of `src` to cursor of `*this`.
void SyncBuffer(Reader& src);
// Sets buffer pointers of `*this` to buffer pointers of `src`, adjusting
// `start()` to hide data already read. Fails `*this` if `src` failed.
void MakeBuffer(Reader& src);
void Done() override;
ABSL_ATTRIBUTE_COLD absl::Status AnnotateStatusImpl(
absl::Status status) override;
bool PullSlow(size_t min_length, size_t recommended_length) override;
using Reader::ReadSlow;
bool ReadSlow(size_t length, char* dest) override;
bool ReadSlow(size_t length, Chain& dest) override;
bool ReadSlow(size_t length, absl::Cord& dest) override;
using Reader::CopySlow;
bool CopySlow(Position length, Writer& dest) override;
bool CopySlow(size_t length, BackwardWriter& dest) override;
using Reader::ReadSomeSlow;
bool ReadSomeSlow(size_t max_length, char* dest) override;
using Reader::CopySomeSlow;
bool CopySomeSlow(size_t max_length, Writer& dest) override;
void ReadHintSlow(size_t min_length, size_t recommended_length) override;
bool SeekSlow(Position new_pos) override;
std::optional<Position> SizeImpl() override;
std::unique_ptr<Reader> NewReaderImpl(Position initial_pos) override;
private:
// This template is defined and used only in prefix_limiting_reader.cc.
template <typename Dest>
bool ReadInternal(size_t length, Dest& dest);
Position base_pos_ = 0;
// Invariants if `is_open()`:
// `start() >= SrcReader()->cursor()`
// `limit() == SrcReader()->limit()`
// `limit_pos() == SrcReader()->limit_pos() - base_pos_`
};
// A `Reader` which reads from another `Reader`, hiding data before a base
// position, and reporting positions shifted so that the base position appears
// as 0.
//
// `PositionShiftingReader` can be used for shifting positions in the other
// direction.
//
// The `Src` template parameter specifies the type of the object providing and
// possibly owning the original `Reader`. `Src` must support
// `Dependency<Reader*, Src>`, e.g. `Reader*` (not owned, default),
// `ChainReader<>` (owned), `std::unique_ptr<Reader>` (owned),
// `Any<Reader*>` (maybe owned).
//
// By relying on CTAD the template argument can be deduced as `TargetT` of the
// type of the first constructor argument.
//
// The original `Reader` must not be accessed until the `PrefixLimitingReader`
// is closed or no longer used.
template <typename Src = Reader*>
class PrefixLimitingReader : public PrefixLimitingReaderBase {
public:
// Creates a closed `PrefixLimitingReader`.
explicit PrefixLimitingReader(Closed) noexcept
: PrefixLimitingReaderBase(kClosed) {}
// Will read from the original `Reader` provided by `src`.
explicit PrefixLimitingReader(Initializer<Src> src,
Options options = Options());
PrefixLimitingReader(PrefixLimitingReader&& that) = default;
PrefixLimitingReader& operator=(PrefixLimitingReader&& that) = default;
// Makes `*this` equivalent to a newly constructed `PrefixLimitingReader`.
// This avoids constructing a temporary `PrefixLimitingReader` and moving
// from it.
ABSL_ATTRIBUTE_REINITIALIZES void Reset(Closed);
ABSL_ATTRIBUTE_REINITIALIZES void Reset(Initializer<Src> src,
Options options = Options());
// Returns the object providing and possibly owning the original `Reader`.
// Unchanged by `Close()`.
Src& src() ABSL_ATTRIBUTE_LIFETIME_BOUND { return src_.manager(); }
const Src& src() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return src_.manager();
}
Reader* SrcReader() const ABSL_ATTRIBUTE_LIFETIME_BOUND override {
return src_.get();
}
protected:
void Done() override;
void SetReadAllHintImpl(bool read_all_hint) override;
void VerifyEndImpl() override;
bool SyncImpl(SyncType sync_type) override;
private:
class Mover;
// The object providing and possibly owning the original `Reader`.
MovingDependency<Reader*, Src, Mover> src_;
};
explicit PrefixLimitingReader(Closed)
-> PrefixLimitingReader<DeleteCtad<Closed>>;
template <typename Src>
explicit PrefixLimitingReader(Src&& src,
PrefixLimitingReaderBase::Options options =
PrefixLimitingReaderBase::Options())
-> PrefixLimitingReader<TargetT<Src>>;
// Implementation details follow.
inline PrefixLimitingReaderBase::PrefixLimitingReaderBase(
PrefixLimitingReaderBase&& that) noexcept
: Reader(static_cast<Reader&&>(that)), base_pos_(that.base_pos_) {}
inline PrefixLimitingReaderBase& PrefixLimitingReaderBase::operator=(
PrefixLimitingReaderBase&& that) noexcept {
Reader::operator=(static_cast<Reader&&>(that));
base_pos_ = that.base_pos_;
return *this;
}
inline void PrefixLimitingReaderBase::Reset(Closed) {
Reader::Reset(kClosed);
base_pos_ = 0;
}
inline void PrefixLimitingReaderBase::Reset() {
Reader::Reset();
// `base_pos_` will be set by `Initialize()`.
}
inline void PrefixLimitingReaderBase::Initialize(
Reader* src, std::optional<Position> base_pos) {
RIEGELI_ASSERT_NE(src, nullptr)
<< "Failed precondition of PrefixLimitingReader: null Reader pointer";
if (base_pos == std::nullopt) {
base_pos_ = src->pos();
} else {
RIEGELI_ASSERT_LE(*base_pos, src->pos())
<< "Failed precondition of PrefixLimitingReader: "
"current position below the base position";
base_pos_ = *base_pos;
}
MakeBuffer(*src);
}
inline void PrefixLimitingReaderBase::SyncBuffer(Reader& src) {
src.set_cursor(cursor());
}
inline void PrefixLimitingReaderBase::MakeBuffer(Reader& src) {
RIEGELI_ASSERT_GE(src.pos(), base_pos_)
<< "PrefixLimitingReader source changed position unexpectedly";
set_buffer(src.cursor(), src.available());
set_limit_pos(src.limit_pos() - base_pos_);
if (ABSL_PREDICT_FALSE(!src.ok())) {
FailWithoutAnnotation(AnnotateOverSrc(src.status()));
}
}
template <typename Src>
inline PrefixLimitingReader<Src>::PrefixLimitingReader(Initializer<Src> src,
Options options)
: src_(std::move(src)) {
Initialize(src_.get(), options.base_pos());
}
template <typename Src>
class PrefixLimitingReader<Src>::Mover {
public:
static auto member() { return &PrefixLimitingReader::src_; }
explicit Mover(PrefixLimitingReader& self, PrefixLimitingReader& that)
: uses_buffer_(self.start() != nullptr) {
// Buffer pointers are already moved so `SyncBuffer()` is called on `self`.
// `src_` is not moved yet so `src_` is taken from `that`.
if (uses_buffer_) self.SyncBuffer(*that.src_);
}
void Done(PrefixLimitingReader& self) {
if (uses_buffer_) self.MakeBuffer(*self.src_);
}
private:
bool uses_buffer_;
};
template <typename Src>
inline void PrefixLimitingReader<Src>::Reset(Closed) {
PrefixLimitingReaderBase::Reset(kClosed);
src_.Reset();
}
template <typename Src>
inline void PrefixLimitingReader<Src>::Reset(Initializer<Src> src,
Options options) {
PrefixLimitingReaderBase::Reset();
src_.Reset(std::move(src));
Initialize(src_.get(), options.base_pos());
}
template <typename Src>
void PrefixLimitingReader<Src>::Done() {
PrefixLimitingReaderBase::Done();
if (src_.IsOwning()) {
if (ABSL_PREDICT_FALSE(!src_->Close())) {
FailWithoutAnnotation(AnnotateOverSrc(src_->status()));
}
}
}
template <typename Src>
void PrefixLimitingReader<Src>::SetReadAllHintImpl(bool read_all_hint) {
if (src_.IsOwning()) {
SyncBuffer(*src_);
src_->SetReadAllHint(read_all_hint);
MakeBuffer(*src_);
}
}
template <typename Src>
void PrefixLimitingReader<Src>::VerifyEndImpl() {
if (!src_.IsOwning()) {
PrefixLimitingReaderBase::VerifyEndImpl();
} else if (ABSL_PREDICT_TRUE(ok())) {
SyncBuffer(*src_);
src_->VerifyEnd();
MakeBuffer(*src_);
}
}
template <typename Src>
bool PrefixLimitingReader<Src>::SyncImpl(SyncType sync_type) {
if (ABSL_PREDICT_FALSE(!ok())) return false;
SyncBuffer(*src_);
bool sync_ok = true;
if (sync_type != SyncType::kFromObject || src_.IsOwning()) {
sync_ok = src_->Sync(sync_type);
}
MakeBuffer(*src_);
return sync_ok;
}
} // namespace riegeli
#endif // RIEGELI_BYTES_PREFIX_LIMITING_READER_H_