blob: 15aa45b5504eb6f9259f2377517a0e6a39ada547 [file]
// Copyright 2018 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/chunk_encoding/deferred_encoder.h"
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/message_lite.h"
#include "riegeli/base/arithmetic.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/bytes_ref.h"
#include "riegeli/base/chain.h"
#include "riegeli/base/external_ref.h"
#include "riegeli/bytes/chain_writer.h"
#include "riegeli/bytes/writer.h"
#include "riegeli/chunk_encoding/chunk_encoder.h"
#include "riegeli/chunk_encoding/constants.h"
#include "riegeli/messages/serialize_message.h"
namespace riegeli {
void DeferredEncoder::Clear() {
ChunkEncoder::Clear();
base_encoder_->Clear();
records_writer_.Reset();
limits_.clear();
}
bool DeferredEncoder::AddRecord(const google::protobuf::MessageLite& record,
SerializeMessageOptions serialize_options) {
if (ABSL_PREDICT_FALSE(!ok())) return false;
const size_t size = serialize_options.GetByteSize(record);
if (ABSL_PREDICT_FALSE(num_records_ ==
UnsignedMin(limits_.max_size(), kMaxNumRecords))) {
return Fail(absl::ResourceExhaustedError("Too many records"));
}
if (ABSL_PREDICT_FALSE(size > std::numeric_limits<uint64_t>::max() -
decoded_data_size_)) {
return Fail(absl::ResourceExhaustedError("Decoded data size too large"));
}
++num_records_;
decoded_data_size_ += IntCast<uint64_t>(size);
if (absl::Status status =
SerializeMessage(record, records_writer_, serialize_options);
ABSL_PREDICT_FALSE(!status.ok())) {
return Fail(std::move(status));
}
limits_.push_back(IntCast<size_t>(records_writer_.pos()));
return true;
}
bool DeferredEncoder::AddRecord(BytesRef record) {
return AddRecordImpl(record);
}
bool DeferredEncoder::AddRecord(ExternalRef record) {
return AddRecordImpl(std::move(record));
}
bool DeferredEncoder::AddRecord(const Chain& record) {
return AddRecordImpl(record);
}
bool DeferredEncoder::AddRecord(Chain&& record) {
return AddRecordImpl(std::move(record));
}
bool DeferredEncoder::AddRecord(const absl::Cord& record) {
return AddRecordImpl(record);
}
bool DeferredEncoder::AddRecord(absl::Cord&& record) {
return AddRecordImpl(std::move(record));
}
template <typename Record>
bool DeferredEncoder::AddRecordImpl(Record&& record) {
if (ABSL_PREDICT_FALSE(!ok())) return false;
if (ABSL_PREDICT_FALSE(num_records_ ==
UnsignedMin(limits_.max_size(), kMaxNumRecords))) {
return Fail(absl::ResourceExhaustedError("Too many records"));
}
if (ABSL_PREDICT_FALSE(record.size() > std::numeric_limits<uint64_t>::max() -
decoded_data_size_)) {
return Fail(absl::ResourceExhaustedError("Decoded data size too large"));
}
++num_records_;
decoded_data_size_ += IntCast<uint64_t>(record.size());
if (ABSL_PREDICT_FALSE(
!records_writer_.Write(std::forward<Record>(record)))) {
return Fail(records_writer_.status());
}
limits_.push_back(IntCast<size_t>(records_writer_.pos()));
return true;
}
bool DeferredEncoder::AddRecords(Chain records, std::vector<size_t> limits) {
RIEGELI_ASSERT_EQ(limits.empty() ? 0u : limits.back(), records.size())
<< "Failed precondition of ChunkEncoder::AddRecords(): "
"record end positions do not match concatenated record values";
if (ABSL_PREDICT_FALSE(!ok())) return false;
if (ABSL_PREDICT_FALSE(limits.size() >
UnsignedMin(limits_.max_size(), kMaxNumRecords) -
num_records_)) {
return Fail(absl::ResourceExhaustedError("Too many records"));
}
num_records_ += IntCast<uint64_t>(limits.size());
decoded_data_size_ += IntCast<uint64_t>(records.size());
if (ABSL_PREDICT_FALSE(!records_writer_.Write(std::move(records)))) {
return Fail(records_writer_.status());
}
if (limits_.empty()) {
limits_ = std::move(limits);
} else {
const size_t base = limits_.back();
for (size_t& limit : limits) limit += base;
limits_.insert(limits_.cend(), limits.begin(), limits.end());
}
return true;
}
bool DeferredEncoder::EncodeAndClose(Writer& dest, ChunkType& chunk_type,
uint64_t& num_records,
uint64_t& decoded_data_size) {
if (ABSL_PREDICT_FALSE(!ok())) return false;
if (ABSL_PREDICT_FALSE(!records_writer_.Close())) {
return Fail(records_writer_.status());
}
if (ABSL_PREDICT_FALSE(!base_encoder_->AddRecords(
std::move(records_writer_.dest()), std::move(limits_))) ||
ABSL_PREDICT_FALSE(!base_encoder_->EncodeAndClose(
dest, chunk_type, num_records, decoded_data_size))) {
Fail(base_encoder_->status());
}
return Close();
}
} // namespace riegeli