blob: 560881d004ff23d593d3cbbe4b072df1e6760d13 [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.
#include "riegeli/bytes/prefix_limiting_writer.h"
#include <stddef.h>
#include <limits>
#include <optional>
#include <utility>
#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "riegeli/base/arithmetic.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/buffering.h"
#include "riegeli/base/byte_fill.h"
#include "riegeli/base/chain.h"
#include "riegeli/base/external_ref.h"
#include "riegeli/base/status.h"
#include "riegeli/base/types.h"
#include "riegeli/bytes/prefix_limiting_reader.h"
#include "riegeli/bytes/reader.h"
#include "riegeli/bytes/writer.h"
namespace riegeli {
void PrefixLimitingWriterBase::Done() {
if (ABSL_PREDICT_TRUE(ok())) {
Writer& dest = *DestWriter();
SyncBuffer(dest);
}
Writer::Done();
associated_reader_.Reset();
}
absl::Status PrefixLimitingWriterBase::AnnotateStatusImpl(absl::Status status) {
if (is_open()) {
Writer& dest = *DestWriter();
SyncBuffer(dest);
status = dest.AnnotateStatus(std::move(status));
MakeBuffer(dest);
}
// The status might have been annotated by `dest` with the original position.
// Clarify that the current position is the relative position instead of
// delegating to `Writer::AnnotateStatusImpl()`.
return AnnotateOverDest(std::move(status));
}
absl::Status PrefixLimitingWriterBase::AnnotateOverDest(absl::Status status) {
if (is_open() && base_pos_ > 0) {
return Annotate(status,
absl::StrCat("with relative position at byte ", pos()));
}
return status;
}
bool PrefixLimitingWriterBase::PushSlow(size_t min_length,
size_t recommended_length) {
RIEGELI_ASSERT_LT(available(), min_length)
<< "Failed precondition of Writer::PushSlow(): "
"enough space available, use Push() instead";
if (ABSL_PREDICT_FALSE(!ok())) return false;
Writer& dest = *DestWriter();
SyncBuffer(dest);
const bool push_ok = dest.Push(min_length, recommended_length);
MakeBuffer(dest);
return push_ok;
}
bool PrefixLimitingWriterBase::WriteSlow(absl::string_view src) {
RIEGELI_ASSERT_LT(available(), src.size())
<< "Failed precondition of Writer::WriteSlow(string_view): "
"enough space available, use Write(string_view) instead";
return WriteInternal(src);
}
bool PrefixLimitingWriterBase::WriteSlow(ExternalRef src) {
RIEGELI_ASSERT_LT(UnsignedMin(available(), kMaxBytesToCopy), src.size())
<< "Failed precondition of Writer::WriteSlow(ExternalRef): "
"enough space available, use Write(ExternalRef) instead";
return WriteInternal(std::move(src));
}
bool PrefixLimitingWriterBase::WriteSlow(const Chain& src) {
RIEGELI_ASSERT_LT(UnsignedMin(available(), kMaxBytesToCopy), src.size())
<< "Failed precondition of Writer::WriteSlow(Chain): "
"enough space available, use Write(Chain) instead";
return WriteInternal(src);
}
bool PrefixLimitingWriterBase::WriteSlow(Chain&& src) {
RIEGELI_ASSERT_LT(UnsignedMin(available(), kMaxBytesToCopy), src.size())
<< "Failed precondition of Writer::WriteSlow(Chain&&): "
"enough space available, use Write(Chain&&) instead";
return WriteInternal(std::move(src));
}
bool PrefixLimitingWriterBase::WriteSlow(const absl::Cord& src) {
RIEGELI_ASSERT_LT(UnsignedMin(available(), kMaxBytesToCopy), src.size())
<< "Failed precondition of Writer::WriteSlow(Cord): "
"enough space available, use Write(Cord) instead";
return WriteInternal(src);
}
bool PrefixLimitingWriterBase::WriteSlow(absl::Cord&& src) {
RIEGELI_ASSERT_LT(UnsignedMin(available(), kMaxBytesToCopy), src.size())
<< "Failed precondition of Writer::WriteSlow(Cord&&): "
"enough space available, use Write(Cord&&) instead";
return WriteInternal(std::move(src));
}
bool PrefixLimitingWriterBase::WriteSlow(ByteFill src) {
RIEGELI_ASSERT_LT(UnsignedMin(available(), kMaxBytesToCopy), src.size())
<< "Failed precondition of Writer::WriteSlow(ByteFill): "
"enough space available, use Write(ByteFill) instead";
return WriteInternal(src);
}
template <typename Src>
inline bool PrefixLimitingWriterBase::WriteInternal(Src&& src) {
if (ABSL_PREDICT_FALSE(!ok())) return false;
Writer& dest = *DestWriter();
SyncBuffer(dest);
const bool write_ok = dest.Write(std::forward<Src>(src));
MakeBuffer(dest);
return write_ok;
}
bool PrefixLimitingWriterBase::SupportsRandomAccess() {
Writer* const dest = DestWriter();
return dest != nullptr && dest->SupportsRandomAccess();
}
bool PrefixLimitingWriterBase::SeekSlow(Position new_pos) {
RIEGELI_ASSERT_NE(new_pos, pos())
<< "Failed precondition of Writer::SeekSlow(): "
"position unchanged, use Seek() instead";
if (ABSL_PREDICT_FALSE(!ok())) return false;
Writer& dest = *DestWriter();
SyncBuffer(dest);
bool seek_ok;
if (ABSL_PREDICT_FALSE(new_pos >
std::numeric_limits<Position>::max() - base_pos_)) {
dest.Seek(std::numeric_limits<Position>::max());
seek_ok = false;
} else {
seek_ok = dest.Seek(new_pos + base_pos_);
}
MakeBuffer(dest);
return seek_ok;
}
std::optional<Position> PrefixLimitingWriterBase::SizeImpl() {
if (ABSL_PREDICT_FALSE(!ok())) return std::nullopt;
Writer& dest = *DestWriter();
SyncBuffer(dest);
const std::optional<Position> size = dest.Size();
MakeBuffer(dest);
if (ABSL_PREDICT_FALSE(size == std::nullopt)) return std::nullopt;
return SaturatingSub(*size, base_pos_);
}
bool PrefixLimitingWriterBase::SupportsTruncate() {
Writer* const dest = DestWriter();
return dest != nullptr && dest->SupportsTruncate();
}
bool PrefixLimitingWriterBase::TruncateImpl(Position new_size) {
if (ABSL_PREDICT_FALSE(!ok())) return false;
Writer& dest = *DestWriter();
SyncBuffer(dest);
bool truncate_ok;
if (ABSL_PREDICT_FALSE(new_size >
std::numeric_limits<Position>::max() - base_pos_)) {
dest.Seek(std::numeric_limits<Position>::max());
truncate_ok = false;
} else {
truncate_ok = dest.Truncate(new_size + base_pos_);
}
MakeBuffer(dest);
return truncate_ok;
}
bool PrefixLimitingWriterBase::SupportsReadMode() {
Writer* const dest = DestWriter();
return dest != nullptr && dest->SupportsReadMode();
}
Reader* PrefixLimitingWriterBase::ReadModeImpl(Position initial_pos) {
if (ABSL_PREDICT_FALSE(!ok())) return nullptr;
Writer& dest = *DestWriter();
SyncBuffer(dest);
Reader* const reader = dest.ReadMode(SaturatingAdd(initial_pos, base_pos_));
MakeBuffer(dest);
if (ABSL_PREDICT_FALSE(reader == nullptr)) return nullptr;
return associated_reader_.ResetReader(
reader, PrefixLimitingReaderBase::Options().set_base_pos(base_pos_));
}
} // namespace riegeli