blob: 148d4569d4b076d6c579bb964a4e8ee87963d5dd [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 THIRD_PARTY_MLIR_EDGE_IREE_BASE_FLATBUFFER_UTIL_H_
#define THIRD_PARTY_MLIR_EDGE_IREE_BASE_FLATBUFFER_UTIL_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "third_party/absl/strings/string_view.h"
#include "third_party/absl/types/optional.h"
#include "third_party/absl/types/span.h"
#include "third_party/flatbuffers/include/flatbuffers/flatbuffers.h"
#include "third_party/mlir_edge/iree/base/status.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);
Status WrapBuffer(Identifier identifier,
absl::Span<const uint8_t> buffer_data,
size_t root_type_size, VerifierFn verifier_fn);
Status FromString(Identifier identifier, std::string buffer_data,
size_t root_type_size, VerifierFn verifier_fn);
Status LoadFile(Identifier identifier, absl::string_view 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 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);
// 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, absl::string_view 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);
}
};
// 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>
StatusOr<std::unique_ptr<FlatBufferFile<T>>> FlatBufferFile<T>::FromString(
Identifier identifier, std::string 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->FromString(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, absl::string_view 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, 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 // THIRD_PARTY_MLIR_EDGE_IREE_BASE_FLATBUFFER_UTIL_H_