blob: 7ec681c1ea009d0910f1bd353c19277ec53400db [file] [log] [blame]
// Copyright 2022 The Centipede 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 "./centipede/blob_file.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string_view>
#include <vector>
#include "absl/status/status.h"
#include "absl/types/span.h"
#include "./centipede/defs.h"
#include "./centipede/logging.h"
#include "./centipede/remote_file.h"
#include "./centipede/util.h"
namespace centipede {
// TODO(ussuri): Return more informative statuses, at least with the file path
// included. That will require adjustments in the test: use
// `testing::status::StatusIs` instead of direct `absl::Status` comparisons).
// Simple implementations of `BlobFileReader` / `BlobFileWriter` based on
// `PackBytesForAppendFile()` / `UnpackBytesFromAppendFile()`.
// We expect to eventually replace this code with something more robust,
// and efficient, e.g. possibly https://github.com/google/riegeli.
// But the current implementation is fully functional.
class SimpleBlobFileReader : public BlobFileReader {
public:
~SimpleBlobFileReader() override {
if (file_ && !closed_) {
// Virtual resolution is off in dtors, so use a specific Close().
CHECK_OK(SimpleBlobFileReader::Close());
}
}
absl::Status Open(std::string_view path) override {
if (closed_) return absl::FailedPreconditionError("already closed");
if (file_) return absl::FailedPreconditionError("already open");
file_ = RemoteFileOpen(path, "r");
if (file_ == nullptr) return absl::UnknownError("can't open file");
// Read the entire file at once.
// It may be useful to read the file in chunks, but if we are going
// to migrate to something else, it's not important here.
ByteArray raw_bytes;
RemoteFileRead(file_, raw_bytes);
RemoteFileClose(file_); // close the file here, we won't need it.
UnpackBytesFromAppendFile(raw_bytes, &unpacked_blobs_);
return absl::OkStatus();
}
absl::Status Read(absl::Span<uint8_t> &blob) override {
if (closed_) return absl::FailedPreconditionError("already closed");
if (!file_) return absl::FailedPreconditionError("was not open");
if (next_to_read_blob_index_ == unpacked_blobs_.size())
return absl::OutOfRangeError("no more blobs");
if (next_to_read_blob_index_ != 0) // Clear the previous blob to save RAM.
unpacked_blobs_[next_to_read_blob_index_ - 1].clear();
blob = absl::Span<uint8_t>(unpacked_blobs_[next_to_read_blob_index_]);
++next_to_read_blob_index_;
return absl::OkStatus();
}
// Closes the file (it must be open).
absl::Status Close() override {
if (closed_) return absl::FailedPreconditionError("already closed");
if (!file_) return absl::FailedPreconditionError("was not open");
closed_ = true;
// Nothing to do here, we've already closed the underlying file (in Open()).
return absl::OkStatus();
}
private:
RemoteFile *file_ = nullptr;
bool closed_ = false;
std::vector<ByteArray> unpacked_blobs_;
size_t next_to_read_blob_index_ = 0;
};
// See SimpleBlobFileReader.
class SimpleBlobFileWriter : public BlobFileWriter {
public:
~SimpleBlobFileWriter() override {
if (file_ && !closed_) {
// Virtual resolution is off in dtors, so use a specific Close().
CHECK_OK(SimpleBlobFileWriter::Close());
}
}
absl::Status Open(std::string_view path, std::string_view mode) override {
CHECK(mode == "w" || mode == "a") << VV(mode);
if (closed_) return absl::FailedPreconditionError("already closed");
if (file_) return absl::FailedPreconditionError("already open");
file_ = RemoteFileOpen(path, mode.data());
if (file_ == nullptr) return absl::UnknownError("can't open file");
return absl::OkStatus();
}
absl::Status Write(absl::Span<const uint8_t> blob) override {
if (closed_) return absl::FailedPreconditionError("already closed");
if (!file_) return absl::FailedPreconditionError("was not open");
// TODO(kcc): [as-needed] This copy from a span to vector is clumsy. Change
// RemoteFileAppend to accept a span.
ByteArray bytes(blob.begin(), blob.end());
ByteArray packed = PackBytesForAppendFile(bytes);
RemoteFileAppend(file_, packed);
return absl::OkStatus();
}
absl::Status Close() override {
if (closed_) return absl::FailedPreconditionError("already closed");
if (!file_) return absl::FailedPreconditionError("was not open");
closed_ = true;
RemoteFileClose(file_);
return absl::OkStatus();
}
private:
RemoteFile *file_ = nullptr;
bool closed_ = false;
};
std::unique_ptr<BlobFileReader> DefaultBlobFileReaderFactory() {
return std::make_unique<SimpleBlobFileReader>();
}
std::unique_ptr<BlobFileWriter> DefaultBlobFileWriterFactory() {
return std::make_unique<SimpleBlobFileWriter>();
}
} // namespace centipede