blob: b9f00b82686300d5395104ed968093772abce836 [file]
// Copyright 2019 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_TENSORFLOW_IO_FILE_READER_H_
#define RIEGELI_TENSORFLOW_IO_FILE_READER_H_
#include <stddef.h>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/chain.h"
#include "riegeli/base/dependency.h"
#include "riegeli/base/initializer.h"
#include "riegeli/base/maker.h"
#include "riegeli/base/object.h"
#include "riegeli/base/sized_shared_buffer.h"
#include "riegeli/base/types.h"
#include "riegeli/bytes/buffer_options.h"
#include "riegeli/bytes/path_ref.h"
#include "riegeli/bytes/reader.h"
#include "tensorflow/compiler/xla/tsl/platform/env.h"
#include "tensorflow/compiler/xla/tsl/platform/file_system.h"
namespace riegeli {
class BackwardWriter;
class Writer;
namespace tensorflow {
// Template parameter independent part of `FileReader`.
class FileReaderBase : public Reader {
public:
class Options : public BufferOptionsBase<Options> {
public:
Options() noexcept {}
Options(const Options& that) = default;
Options& operator=(const Options& that) = default;
// `FileReader` has a larger `kDefaultMaxBufferSize` (1M) because remote
// file access may have high latency of each operation.
static constexpr size_t kDefaultMaxBufferSize = size_t{1} << 20;
// Overrides the TensorFlow environment.
//
// `nullptr` is interpreted as `tsl::Env::Default()`.
//
// Default: `nullptr`.
Options& set_env(tsl::Env* env) & ABSL_ATTRIBUTE_LIFETIME_BOUND {
env_ = env;
return *this;
}
Options&& set_env(tsl::Env* env) && ABSL_ATTRIBUTE_LIFETIME_BOUND {
return std::move(set_env(env));
}
tsl::Env* env() const { return env_; }
// Reading will start from this position.
//
// Default: 0.
Options& set_initial_pos(Position initial_pos) &
ABSL_ATTRIBUTE_LIFETIME_BOUND {
initial_pos_ = initial_pos;
return *this;
}
Options&& set_initial_pos(Position initial_pos) &&
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return std::move(set_initial_pos(initial_pos));
}
Position initial_pos() const { return initial_pos_; }
// If `true`, supports reading up to the end of the file, then retrying when
// the file has grown. This disables caching the file size.
//
// Default: `false`.
Options& set_growing_source(bool growing_source) &
ABSL_ATTRIBUTE_LIFETIME_BOUND {
growing_source_ = growing_source;
return *this;
}
Options&& set_growing_source(bool growing_source) &&
ABSL_ATTRIBUTE_LIFETIME_BOUND {
return std::move(set_growing_source(growing_source));
}
bool growing_source() const { return growing_source_; }
private:
tsl::Env* env_ = nullptr;
Position initial_pos_ = 0;
bool growing_source_ = false;
};
// Returns the `tsl::RandomAccessFile` being read from. If the
// `tsl::RandomAccessFile` is owned then changed to `nullptr` by
// `Close()`, otherwise unchanged.
virtual tsl::RandomAccessFile* SrcFile() const
ABSL_ATTRIBUTE_LIFETIME_BOUND = 0;
// Returns the name of the `tsl::RandomAccessFile` being read from.
// Unchanged by `Close()`.
absl::string_view filename() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return filename_;
}
bool ToleratesReadingAhead() override {
return buffer_sizer_.read_all_hint() ||
FileReaderBase::SupportsRandomAccess();
}
bool SupportsRandomAccess() override { return file_system_ != nullptr; }
bool SupportsNewReader() override {
return FileReaderBase::SupportsRandomAccess();
}
protected:
explicit FileReaderBase(Closed) noexcept : Reader(kClosed) {}
explicit FileReaderBase(BufferOptions buffer_options, tsl::Env* env,
bool growing_source);
FileReaderBase(FileReaderBase&& that) noexcept;
FileReaderBase& operator=(FileReaderBase&& that) noexcept;
void Reset(Closed);
void Reset(BufferOptions buffer_options, tsl::Env* env, bool growing_source);
void Initialize(tsl::RandomAccessFile* src, Position initial_pos);
bool InitializeFilename(tsl::RandomAccessFile* src);
bool InitializeFilename(PathInitializer filename);
std::unique_ptr<tsl::RandomAccessFile> OpenFile();
void InitializePos(Position initial_pos);
void Done() override;
absl::Status AnnotateStatusImpl(absl::Status status) override;
void SetReadAllHintImpl(bool read_all_hint) 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;
bool SyncImpl(SyncType sync_type) override;
bool SeekSlow(Position new_pos) override;
std::optional<Position> SizeImpl() override;
std::unique_ptr<Reader> NewReaderImpl(Position initial_pos) override;
std::unique_ptr<Reader> NewReaderCurrentPosImpl() override;
private:
ABSL_ATTRIBUTE_COLD bool FailOperation(const absl::Status& status,
absl::string_view operation);
ABSL_ATTRIBUTE_COLD static absl::Status NoRandomAccessStatus();
void set_exact_size(std::optional<Position> exact_size) {
buffer_sizer_.set_exact_size(exact_size);
}
std::optional<Position> exact_size() const {
return buffer_sizer_.exact_size();
}
// Discards buffer contents.
void SyncBuffer();
// Clears `buffer_`. Reads `length` bytes from `*src`, from the physical file
// position which is `limit_pos()`, to `dest[]`.
//
// Increments `limit_pos()` by the length read. Returns `true` on success.
bool ReadToDest(size_t length, tsl::RandomAccessFile* src, char* dest);
// Reads `flat_buffer.size()` bytes from `*src`, from the physical file
// position which is `limit_pos()`, preferably to `flat_buffer.data()`. Newly
// read data are adjacent to previously available data in `buffer_`, if any.
// `cursor_index` is the amount of already read data before previously
// available data.
//
// Increments `limit_pos()` by the length read. Sets buffer pointers. Returns
// `true` on success.
//
// Precondition: `flat_buffer` is a suffix of `buffer_`
bool ReadToBuffer(size_t cursor_index, tsl::RandomAccessFile* src,
absl::Span<char> flat_buffer);
// Implementation of `CopySlow(Writer&)` in terms of `Writer::Push()` and
// `ReadToDest()`. Does not use buffer pointers.
//
// Precondition: `length > 0`
bool CopyUsingPush(Position length, tsl::RandomAccessFile* src, Writer& dest);
std::string filename_{kDefaultFilename};
// Invariant: if `is_open()` then `env_ != nullptr`
tsl::Env* env_ = nullptr;
tsl::FileSystem* file_system_ = nullptr;
bool growing_source_ = false;
ReadBufferSizer buffer_sizer_;
// If `buffer_` is not empty, it contains buffered data, read directly before
// the physical source position which is `limit_pos()`. Otherwise buffered
// data are in memory managed by the `tsl::RandomAccessFile`. In any
// case `start()` points to them.
SizedSharedBuffer buffer_;
// Invariants if `!buffer_.empty()`:
// `start() == buffer_.data()`
// `start_to_limit() == buffer_.size()`
};
// A `Reader` which reads from a `tsl::RandomAccessFile`.
//
// It supports random access and `NewReader()` if the
// `tsl::RandomAccessFile` supports
// `tsl::RandomAccessFile::Name()` and the name is not empty.
//
// The `Src` template parameter specifies the type of the object providing and
// possibly owning the `tsl::RandomAccessFile` being read from. `Src`
// must support `Dependency<tsl::RandomAccessFile*, Src>`, e.g.
// `std::unique_ptr<tsl::RandomAccessFile>` (owned, default),
// `tsl::RandomAccessFile*` (not owned),
// `Any<tsl::RandomAccessFile*>` (maybe owned).
//
// By relying on CTAD the template argument can be deduced as `TargetT` of the
// type of the first constructor argument.
//
// The `tsl::RandomAccessFile` must not be closed until the
// `FileReader` is closed or no longer used.
template <typename Src = std::unique_ptr<tsl::RandomAccessFile>>
class FileReader : public FileReaderBase {
public:
// Creates a closed `FileReader`.
explicit FileReader(Closed) noexcept : FileReaderBase(kClosed) {}
// Will read from the `tsl::RandomAccessFile` provided by `src`.
explicit FileReader(Initializer<Src> src, const Options& options = Options());
// Opens a `tsl::RandomAccessFile` for reading.
//
// If opening the file fails, `FileReader` will be failed and closed.
explicit FileReader(PathInitializer filename,
const Options& options = Options());
FileReader(FileReader&& that) = default;
FileReader& operator=(FileReader&& that) = default;
// Makes `*this` equivalent to a newly constructed `FileReader`. This avoids
// constructing a temporary `FileReader` and moving from it.
ABSL_ATTRIBUTE_REINITIALIZES void Reset(Closed);
ABSL_ATTRIBUTE_REINITIALIZES void Reset(Initializer<Src> src,
const Options& options = Options());
ABSL_ATTRIBUTE_REINITIALIZES
void Reset(PathInitializer filename, const Options& options = Options());
// Returns the object providing and possibly owning the
// `tsl::RandomAccessFile` being read from. If the
// `tsl::RandomAccessFile` is owned then changed to `nullptr` by
// `Close()`, otherwise unchanged.
Src& src() ABSL_ATTRIBUTE_LIFETIME_BOUND { return src_.manager(); }
const Src& src() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return src_.manager();
}
tsl::RandomAccessFile* SrcFile() const
ABSL_ATTRIBUTE_LIFETIME_BOUND override {
return src_.get();
}
protected:
void Done() override;
private:
using FileReaderBase::Initialize;
void Initialize(PathInitializer filename, const Options& options);
// The object providing and possibly owning the
// `tsl::RandomAccessFile` being read from.
Dependency<tsl::RandomAccessFile*, Src> src_;
};
explicit FileReader(Closed) -> FileReader<DeleteCtad<Closed>>;
template <typename Src>
explicit FileReader(Src&& src, const FileReaderBase::Options& options =
FileReaderBase::Options())
-> FileReader<std::conditional_t<
std::is_convertible_v<Src&&, PathInitializer>,
std::unique_ptr<tsl::RandomAccessFile>, TargetT<Src>>>;
// Implementation details follow.
inline FileReaderBase::FileReaderBase(BufferOptions buffer_options,
tsl::Env* env, bool growing_source)
: env_(env != nullptr ? env : tsl::Env::Default()),
growing_source_(growing_source),
buffer_sizer_(buffer_options) {}
inline FileReaderBase::FileReaderBase(FileReaderBase&& that) noexcept
: Reader(static_cast<Reader&&>(that)),
filename_(std::exchange(that.filename_, std::string(kDefaultFilename))),
env_(that.env_),
file_system_(that.file_system_),
growing_source_(that.growing_source_),
buffer_sizer_(that.buffer_sizer_),
buffer_(std::move(that.buffer_)) {}
inline FileReaderBase& FileReaderBase::operator=(
FileReaderBase&& that) noexcept {
Reader::operator=(static_cast<Reader&&>(that));
filename_ = std::exchange(that.filename_, std::string(kDefaultFilename));
env_ = that.env_;
file_system_ = that.file_system_;
growing_source_ = that.growing_source_;
buffer_sizer_ = that.buffer_sizer_;
buffer_ = std::move(that.buffer_);
return *this;
}
inline void FileReaderBase::Reset(Closed) {
Reader::Reset(kClosed);
filename_ = std::string(kDefaultFilename);
env_ = nullptr;
file_system_ = nullptr;
growing_source_ = false;
buffer_sizer_.Reset();
buffer_ = SizedSharedBuffer();
}
inline void FileReaderBase::Reset(BufferOptions buffer_options, tsl::Env* env,
bool growing_source) {
Reader::Reset();
// `filename_` will be set by `InitializeFilename()`.
env_ = env != nullptr ? env : tsl::Env::Default();
file_system_ = nullptr;
growing_source_ = growing_source;
buffer_sizer_.Reset(buffer_options);
buffer_.Clear();
}
inline void FileReaderBase::Initialize(tsl::RandomAccessFile* src,
Position initial_pos) {
RIEGELI_ASSERT_NE(src, nullptr)
<< "Failed precondition of FileReader: null RandomAccessFile pointer";
if (ABSL_PREDICT_FALSE(!InitializeFilename(src))) return;
InitializePos(initial_pos);
}
template <typename Src>
inline FileReader<Src>::FileReader(Initializer<Src> src, const Options& options)
: FileReaderBase(options.buffer_options(), options.env(),
options.growing_source()),
src_(std::move(src)) {
Initialize(src_.get(), options.initial_pos());
}
template <typename Src>
inline FileReader<Src>::FileReader(PathInitializer filename,
const Options& options)
: FileReaderBase(options.buffer_options(), options.env(),
options.growing_source()) {
Initialize(std::move(filename), options);
}
template <typename Src>
inline void FileReader<Src>::Reset(Closed) {
FileReaderBase::Reset(kClosed);
src_.Reset();
}
template <typename Src>
inline void FileReader<Src>::Reset(Initializer<Src> src,
const Options& options) {
FileReaderBase::Reset(options.buffer_options(), options.env(),
options.growing_source());
src_.Reset(std::move(src));
Initialize(src_.get(), options.initial_pos());
}
template <typename Src>
inline void FileReader<Src>::Reset(PathInitializer filename,
const Options& options) {
FileReaderBase::Reset(options.buffer_options(), options.env(),
options.growing_source());
Initialize(std::move(filename), options);
}
template <typename Src>
inline void FileReader<Src>::Initialize(PathInitializer filename,
const Options& options) {
if (ABSL_PREDICT_FALSE(!InitializeFilename(std::move(filename)))) return;
std::unique_ptr<tsl::RandomAccessFile> src = OpenFile();
if (ABSL_PREDICT_FALSE(src == nullptr)) return;
src_.Reset(riegeli::Maker(src.release()));
InitializePos(options.initial_pos());
}
template <typename Src>
void FileReader<Src>::Done() {
FileReaderBase::Done();
if (src_.IsOwning()) {
// The only way to close a `tsl::RandomAccessFile` is to delete it.
src_.Reset();
}
}
} // namespace tensorflow
} // namespace riegeli
#endif // RIEGELI_TENSORFLOW_IO_FILE_READER_H_