blob: 79b8fe48c13773ef0ca4a510899f0378dfbb742d [file] [log] [blame]
// 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
//
// 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.
#ifndef IREE_BASE_FLATBUFFER_UTIL_H_
#define IREE_BASE_FLATBUFFER_UTIL_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "base/memory.h"
#include "base/status.h"
#include "flatbuffers/flatbuffers.h"
namespace iree {
// Wraps a FlatBuffer String in an absl::string_view.
// Returns empty-string ("") for nullptr values.
inline absl::string_view WrapString(const ::flatbuffers::String* value) {
return value ? absl::string_view{value->data(), value->size()} : "";
}
// Base type for FlatBufferFile<T>. See below.
class FlatBufferFileBase {
public:
using Identifier = absl::optional<const char*>;
virtual ~FlatBufferFileBase();
protected:
template <typename T>
friend class FlatBufferFile;
using VerifierFn = bool (*)(const char* identifier,
::flatbuffers::Verifier* verifier);
FlatBufferFileBase() = default;
const void* root_ptr() const { return root_ptr_; }
// Redirections of template static methods on FlatBufferFile so we can put the
// implementations in a shared compilation unit.
// See FlatBufferFile<T> for doc comments.
Status Create(const void* root_ptr, std::function<void()> deleter);
Status CreateWithBackingBuffer(const void* root_ptr,
::flatbuffers::DetachedBuffer backing_buffer);
Status Wrap(const void* root);
Status FromBuffer(Identifier identifier,
absl::Span<const uint8_t> buffer_data,
std::function<void()> deleter, size_t root_type_size,
VerifierFn verifier_fn);
// Initializes from an STL byte based container (string and vector of
// char/byte should be compatible).
template <typename Container>
Status FromContainer(Identifier identifier, Container container,
size_t root_type_size, VerifierFn verifier_fn);
Status WrapBuffer(Identifier identifier,
absl::Span<const uint8_t> buffer_data,
size_t root_type_size, VerifierFn verifier_fn);
Status LoadFile(Identifier identifier, std::string path,
size_t root_type_size, VerifierFn verifier_fn);
private:
const void* root_ptr_ = nullptr;
std::function<void()> deleter_;
};
// Immutable root FlatBuffer type wrapper with support for loading and backing
// buffer management.
//
// Immutable and thread-safe.
template <typename T>
class FlatBufferFile final : public FlatBufferFileBase {
public:
// Creates a FlatBufferFile from an in-memory root pointer.
// The provided |deleter| will be called when the FlatBufferFile is destructed
// and can be used to deallocate/clean up resources.
//
// This assumes that the root pointer has already been verified as valid.
// If verification is required instead use FromBuffer on the original buffer.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> Create(
const T* root, std::function<void()> deleter);
// Creates a FlatBufferFile from an in-memory root pointer and the detached
// backing buffer storing it.
//
// Example:
// FlatBufferBuilder fbb;
// MyTypeBuilder mtb(fbb);
// fbb.Finish(mtb.Finish());
// auto my_type = FlatBufferFile<MyType>::CreateWithBackingBuffer(
// fbb.Release());
// my_type->foo();
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> CreateWithBackingBuffer(
::flatbuffers::DetachedBuffer backing_buffer);
// Wraps a caller-owned in-memory root pointer.
// The provided |root| must remain valid for the lifetime of the returned
// FlatBufferFile.
//
// This assumes that the root pointer has already been verified as valid.
// If verification is required instead use FromBuffer on the original buffer.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> Wrap(const T* root);
// Creates a FlatBufferFile wrapping an external data buffer with a deleter
// function that will be called when the FlatBufferFile is destructed.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> FromBuffer(
Identifier identifier, absl::Span<const uint8_t> buffer_data,
std::function<void()> deleter);
// Creates a FlatBufferFile from a serialized data buffer.
// The FlatBufferFile takes ownership of the vector.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> FromBuffer(
Identifier identifier, std::vector<uint8_t> buffer_data);
// Loads a FlatBufferFile from an external buffer owned by the caller.
// The buffer must remain valid until the Pipeline is destroyed.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> WrapBuffer(
Identifier identifier, absl::Span<const uint8_t> buffer_data);
// Loads the FlatBufferFile from a serialized byte-based STL container.
template <typename Container>
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> FromContainer(
Identifier identifier, Container buffer_data);
// Loads a FlatBufferFile from a serialized string.
// The FlatBufferFile takes ownership of the string.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> FromString(
Identifier identifier, std::string buffer_data) {
return FromContainer(identifier, std::move(buffer_data));
}
// Loads a FlatBufferFile from a serialized byte vector.
// The FlatBufferFile takes ownership of the vector.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> FromVector(
Identifier identifier, std::vector<uint8_t> buffer_data) {
return FromContainer(identifier, std::move(buffer_data));
}
// Loads a FlatBufferFile from a serialized file on the file system.
// This will attempt to mmap the file and is the preferred way of loading as
// only those pages that contain requested tables will be read.
static StatusOr<std::unique_ptr<FlatBufferFile<T>>> LoadFile(
Identifier identifier, std::string path);
// Returns a vector of file references that share the same underlying data
// buffer. The buffer will be kept alive until the last file is released.
static StatusOr<std::vector<std::unique_ptr<FlatBufferFile<T>>>>
CreateShareGroup(std::unique_ptr<FlatBufferFile<T>> file, int count);
~FlatBufferFile() override = default;
// Typed root pointer of the file.
const T* root() const { return reinterpret_cast<const T*>(root_ptr()); }
private:
FlatBufferFile() = default;
// Conforms to VerifierFn.
static bool VerifierFnT(const char* identifier,
::flatbuffers::Verifier* verifier) {
return verifier->VerifyBuffer<T>(identifier);
}
};
template <typename Container>
Status FlatBufferFileBase::FromContainer(Identifier identifier,
Container container,
size_t root_type_size,
VerifierFn verifier_fn) {
static_assert(sizeof(*container.data()) == 1,
"Expected container of byte sized elements");
auto buffer_data = absl::MakeConstSpan(
// Double static_cast through void is safer than reinterpret_cast.
static_cast<const uint8_t*>(static_cast<const void*>(container.data())),
container.size());
// Use a baton to keep the container alive until the FlatBufferFileBase is
// destroyed.
auto buffer_data_baton = IreeMoveToLambda(container);
return FromBuffer(
identifier, buffer_data,
[buffer_data_baton]() {
// Keeping the container alive.
(void)buffer_data_baton.value;
},
root_type_size, verifier_fn);
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::Create(
const T* root, std::function<void()> deleter) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
RETURN_IF_ERROR(base_file->Create(root, std::move(deleter)));
return std::move(flat_buffer_file);
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>>
FlatBufferFile<T>::CreateWithBackingBuffer(
::flatbuffers::DetachedBuffer backing_buffer) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
auto* root_ptr = ::flatbuffers::GetRoot<T>(backing_buffer.data());
RETURN_IF_ERROR(
base_file->CreateWithBackingBuffer(root_ptr, std::move(backing_buffer)));
return std::move(flat_buffer_file);
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::Wrap(
const T* root) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
RETURN_IF_ERROR(base_file->Wrap(root));
return std::move(flat_buffer_file);
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::FromBuffer(
Identifier identifier, absl::Span<const uint8_t> buffer_data,
std::function<void()> deleter) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
RETURN_IF_ERROR(base_file->FromBuffer(
identifier, buffer_data, std::move(deleter), sizeof(T), VerifierFnT));
return std::move(flat_buffer_file);
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::FromBuffer(
Identifier identifier, std::vector<uint8_t> buffer_data) {
auto* buffer_data_ptr = new decltype(buffer_data);
(*buffer_data_ptr) = std::move(buffer_data);
return FromBuffer(identifier, absl::MakeConstSpan(*buffer_data_ptr),
[buffer_data_ptr]() { delete buffer_data_ptr; });
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::WrapBuffer(
Identifier identifier, absl::Span<const uint8_t> buffer_data) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
RETURN_IF_ERROR(
base_file->WrapBuffer(identifier, buffer_data, sizeof(T), VerifierFnT));
return std::move(flat_buffer_file);
}
// static
template <typename T>
template <typename Container>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::FromContainer(
Identifier identifier, Container buffer_data) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
RETURN_IF_ERROR(base_file->FromContainer(identifier, std::move(buffer_data),
sizeof(T), VerifierFnT));
return std::move(flat_buffer_file);
}
// static
template <typename T>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::LoadFile(
Identifier identifier, std::string path) {
std::unique_ptr<FlatBufferFile<T>> flat_buffer_file{new FlatBufferFile<T>};
auto* base_file = static_cast<FlatBufferFileBase*>(flat_buffer_file.get());
RETURN_IF_ERROR(
base_file->LoadFile(identifier, std::move(path), sizeof(T), VerifierFnT));
return std::move(flat_buffer_file);
}
// static
template <typename T>
StatusOr<std::vector<std::unique_ptr<FlatBufferFile<T>>>>
FlatBufferFile<T>::CreateShareGroup(std::unique_ptr<FlatBufferFile<T>> file,
int count) {
// Create a shared_ptr wrapper for the base file that will be.
std::shared_ptr<FlatBufferFile<T>> shared_file{file.release()};
// Create N files. We wrap and keep the shared_ptr alive in the deleter
// capture. By wrapping we avoid reverifying the entire buffer.
std::vector<std::unique_ptr<FlatBufferFile<T>>> list;
for (int i = 0; i < count; ++i) {
ASSIGN_OR_RETURN(auto new_file, FlatBufferFile<T>::Create(
shared_file->root(), [shared_file]() {
// Each new file keeps a reference to
// the shared file to keep it alive.
(void)shared_file;
}));
list.push_back(std::move(new_file));
}
return std::move(list);
}
} // namespace iree
#endif // IREE_BASE_FLATBUFFER_UTIL_H_