mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Import the archivist framework.
This commit is contained in:
parent
cf4340e556
commit
c5c8f1395f
@ -9,6 +9,7 @@ config("impeller_public_config") {
|
||||
group("impeller") {
|
||||
public_deps = [
|
||||
"aiks",
|
||||
"archivist",
|
||||
"base",
|
||||
"compiler",
|
||||
"display_list",
|
||||
@ -24,6 +25,7 @@ executable("impeller_unittests") {
|
||||
|
||||
deps = [
|
||||
"aiks:aiks_unittests",
|
||||
"archivist:archivist_unittests",
|
||||
"base:base_unittests",
|
||||
"compiler:compiler_unittests",
|
||||
"display_list:display_list_unittests",
|
||||
|
||||
39
engine/src/flutter/impeller/archivist/BUILD.gn
Normal file
39
engine/src/flutter/impeller/archivist/BUILD.gn
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("../tools/impeller.gni")
|
||||
|
||||
impeller_component("archivist") {
|
||||
# Only the umbrella header is public since all other TU's are implementation
|
||||
# detail that will be compiled away in release modes.
|
||||
# Because they are implementation details, they may expose dependency headers.
|
||||
public = [ "archive.h" ]
|
||||
|
||||
sources = [
|
||||
"archive.cc",
|
||||
"archive_class_registration.cc",
|
||||
"archive_class_registration.h",
|
||||
"archive_database.cc",
|
||||
"archive_database.h",
|
||||
"archive_statement.cc",
|
||||
"archive_statement.h",
|
||||
"archive_transaction.cc",
|
||||
"archive_transaction.h",
|
||||
"archive_vector.cc",
|
||||
"archive_vector.h",
|
||||
]
|
||||
|
||||
public_deps = [ "../base" ]
|
||||
|
||||
deps = [ "//third_party/sqlite" ]
|
||||
}
|
||||
|
||||
impeller_component("archivist_unittests") {
|
||||
testonly = true
|
||||
sources = [ "archivist_unittests.cc" ]
|
||||
deps = [
|
||||
":archivist",
|
||||
"//flutter/testing",
|
||||
]
|
||||
}
|
||||
317
engine/src/flutter/impeller/archivist/archive.cc
Normal file
317
engine/src/flutter/impeller/archivist/archive.cc
Normal file
@ -0,0 +1,317 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/archivist/archive.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "impeller/archivist/archive_class_registration.h"
|
||||
#include "impeller/archivist/archive_database.h"
|
||||
#include "impeller/archivist/archive_statement.h"
|
||||
#include "impeller/archivist/archive_vector.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
Archive::Archive(const std::string& path, bool recreate)
|
||||
: _db(std::make_unique<ArchiveDatabase>(path, recreate)) {}
|
||||
|
||||
Archive::~Archive() {
|
||||
FML_DCHECK(_transactionCount == 0) << "There must be no pending transactions";
|
||||
}
|
||||
|
||||
bool Archive::isReady() const {
|
||||
return _db->isReady();
|
||||
}
|
||||
|
||||
bool Archive::archiveInstance(const ArchiveDef& definition,
|
||||
const ArchiveSerializable& archivable,
|
||||
int64_t& lastInsertIDOut) {
|
||||
if (!isReady()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto transaction = _db->acquireTransaction(_transactionCount);
|
||||
|
||||
const auto* registration = _db->registrationForDefinition(definition);
|
||||
|
||||
if (registration == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto statement = registration->insertStatement();
|
||||
|
||||
if (!statement.isReady() || !statement.reset()) {
|
||||
/*
|
||||
* Must be able to reset the statement for a new write
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
auto itemName = archivable.archiveName();
|
||||
|
||||
/*
|
||||
* The lifecycle of the archive item is tied to this scope and there is no
|
||||
* way for the user to create an instance of an archive item. So its safe
|
||||
* for its members to be references. It does not manage the lifetimes of
|
||||
* anything.
|
||||
*/
|
||||
ArchiveItem item(*this, statement, *registration, itemName);
|
||||
|
||||
/*
|
||||
* We need to bind the primary key only if the item does not provide its own
|
||||
*/
|
||||
if (!definition.autoAssignName &&
|
||||
!statement.bind(ArchiveClassRegistration::NameIndex, itemName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!archivable.serialize(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (statement.run() != ArchiveStatement::Result::Done) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t lastInsert = _db->lastInsertRowID();
|
||||
|
||||
if (!definition.autoAssignName &&
|
||||
lastInsert != static_cast<int64_t>(itemName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lastInsertIDOut = lastInsert;
|
||||
|
||||
/*
|
||||
* If any of the nested calls fail, we would have already checked for the
|
||||
* failure and returned.
|
||||
*/
|
||||
transaction.markWritesSuccessful();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Archive::unarchiveInstance(const ArchiveDef& definition,
|
||||
ArchiveSerializable::ArchiveName name,
|
||||
ArchiveSerializable& archivable) {
|
||||
UnarchiveStep stepper = [&archivable](ArchiveItem& item) {
|
||||
archivable.deserialize(item);
|
||||
return false /* no-more after single read */;
|
||||
};
|
||||
|
||||
return unarchiveInstances(definition, stepper, name) == 1;
|
||||
}
|
||||
|
||||
size_t Archive::unarchiveInstances(const ArchiveDef& definition,
|
||||
Archive::UnarchiveStep stepper,
|
||||
ArchiveSerializable::ArchiveName name) {
|
||||
if (!isReady()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto* registration = _db->registrationForDefinition(definition);
|
||||
|
||||
if (registration == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bool isQueryingSingle = name != ArchiveNameAuto;
|
||||
|
||||
auto statement = registration->queryStatement(isQueryingSingle);
|
||||
|
||||
if (!statement.isReady() || !statement.reset()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isQueryingSingle) {
|
||||
/*
|
||||
* If a single statement is being queried for, bind the name as a statement
|
||||
* argument.
|
||||
*/
|
||||
if (!statement.bind(ArchiveClassRegistration::NameIndex, name)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (statement.columnCount() !=
|
||||
registration->memberCount() + 1 /* primary key */) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Acquire a transaction but never mark it successful since we will never
|
||||
* be committing any writes to the database during unarchiving.
|
||||
*/
|
||||
auto transaction = _db->acquireTransaction(_transactionCount);
|
||||
|
||||
size_t itemsRead = 0;
|
||||
|
||||
while (statement.run() == ArchiveStatement::Result::Row) {
|
||||
itemsRead++;
|
||||
|
||||
/*
|
||||
* Prepare a fresh archive item for the given statement
|
||||
*/
|
||||
ArchiveItem item(*this, statement, *registration, name);
|
||||
|
||||
if (!stepper(item)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isQueryingSingle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return itemsRead;
|
||||
}
|
||||
|
||||
ArchiveItem::ArchiveItem(Archive& context,
|
||||
ArchiveStatement& statement,
|
||||
const ArchiveClassRegistration& registration,
|
||||
ArchiveSerializable::ArchiveName name)
|
||||
: _context(context),
|
||||
_statement(statement),
|
||||
_registration(registration),
|
||||
_name(name),
|
||||
_currentClass(registration.className()) {}
|
||||
|
||||
ArchiveSerializable::ArchiveName ArchiveItem::name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
bool ArchiveItem::encode(ArchiveSerializable::Member member,
|
||||
const std::string& item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.bind(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::encodeIntegral(ArchiveSerializable::Member member,
|
||||
int64_t item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.bind(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::encode(ArchiveSerializable::Member member, double item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.bind(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::encode(ArchiveSerializable::Member member,
|
||||
const Allocation& item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.bind(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::encode(ArchiveSerializable::Member member,
|
||||
const ArchiveDef& otherDef,
|
||||
const ArchiveSerializable& other) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
|
||||
if (!found.second) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to fully archive the other instance first because it could
|
||||
* have a name that is auto assigned. In that case, we cannot ask it before
|
||||
* archival (via `other.archiveName()`).
|
||||
*/
|
||||
int64_t lastInsert = 0;
|
||||
if (!_context.archiveInstance(otherDef, other, lastInsert)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind the name of the serialiable
|
||||
*/
|
||||
if (!_statement.bind(found.first, lastInsert)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<bool, int64_t> ArchiveItem::encodeVectorKeys(
|
||||
std::vector<int64_t>&& members) {
|
||||
ArchiveVector vector(std::move(members));
|
||||
int64_t vectorID = 0;
|
||||
if (!_context.archiveInstance(ArchiveVector::ArchiveDefinition, //
|
||||
vector, //
|
||||
vectorID)) {
|
||||
return {false, 0};
|
||||
}
|
||||
return {true, vectorID};
|
||||
}
|
||||
|
||||
bool ArchiveItem::decodeVectorKeys(ArchiveSerializable::ArchiveName name,
|
||||
std::vector<int64_t>& members) {
|
||||
ArchiveVector vector;
|
||||
|
||||
if (!_context.unarchiveInstance(ArchiveVector::ArchiveDefinition, name,
|
||||
vector)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& keys = vector.keys();
|
||||
|
||||
std::move(keys.begin(), keys.end(), std::back_inserter(members));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArchiveItem::decode(ArchiveSerializable::Member member,
|
||||
std::string& item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.column(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::decodeIntegral(ArchiveSerializable::Member member,
|
||||
int64_t& item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.column(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::decode(ArchiveSerializable::Member member, double& item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.column(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::decode(ArchiveSerializable::Member member, Allocation& item) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
return found.second ? _statement.column(found.first, item) : false;
|
||||
}
|
||||
|
||||
bool ArchiveItem::decode(ArchiveSerializable::Member member,
|
||||
const ArchiveDef& otherDef,
|
||||
ArchiveSerializable& other) {
|
||||
auto found = _registration.findColumn(_currentClass, member);
|
||||
|
||||
/*
|
||||
* Make sure a member is present at that column
|
||||
*/
|
||||
if (!found.second) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to find the foreign key in the current items row
|
||||
*/
|
||||
int64_t foreignKey = 0;
|
||||
if (!_statement.column(found.first, foreignKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the other item and unarchive by this foreign key
|
||||
*/
|
||||
if (!_context.unarchiveInstance(otherDef, foreignKey, other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
282
engine/src/flutter/impeller/archivist/archive.h
Normal file
282
engine/src/flutter/impeller/archivist/archive.h
Normal file
@ -0,0 +1,282 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/base/allocation.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ArchiveItem;
|
||||
class ArchiveClassRegistration;
|
||||
class ArchiveDatabase;
|
||||
class ArchiveStatement;
|
||||
|
||||
class ArchiveSerializable {
|
||||
public:
|
||||
using Member = uint64_t;
|
||||
using Members = std::vector<Member>;
|
||||
using ArchiveName = uint64_t;
|
||||
|
||||
virtual ArchiveName archiveName() const = 0;
|
||||
|
||||
virtual bool serialize(ArchiveItem& item) const = 0;
|
||||
|
||||
virtual bool deserialize(ArchiveItem& item) = 0;
|
||||
};
|
||||
|
||||
struct ArchiveDef {
|
||||
const ArchiveDef* superClass;
|
||||
const std::string className;
|
||||
const bool autoAssignName;
|
||||
const ArchiveSerializable::Members members;
|
||||
};
|
||||
|
||||
static const ArchiveSerializable::ArchiveName ArchiveNameAuto = 0;
|
||||
|
||||
class Archive {
|
||||
public:
|
||||
Archive(const std::string& path, bool recreate);
|
||||
|
||||
~Archive();
|
||||
|
||||
bool isReady() const;
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
bool archive(const T& archivable) {
|
||||
const ArchiveDef& def = T::ArchiveDefinition;
|
||||
int64_t unusedLast = 0;
|
||||
return archiveInstance(def, archivable, unusedLast);
|
||||
}
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
bool unarchive(ArchiveSerializable::ArchiveName name, T& archivable) {
|
||||
const ArchiveDef& def = T::ArchiveDefinition;
|
||||
return unarchiveInstance(def, name, archivable);
|
||||
}
|
||||
|
||||
using UnarchiveStep = std::function<bool /*continue*/ (ArchiveItem&)>;
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
size_t unarchive(UnarchiveStep stepper) {
|
||||
const ArchiveDef& def = T::ArchiveDefinition;
|
||||
return unarchiveInstances(def, stepper, ArchiveNameAuto);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ArchiveDatabase> _db;
|
||||
int64_t _transactionCount = 0;
|
||||
|
||||
friend class ArchiveItem;
|
||||
|
||||
bool archiveInstance(const ArchiveDef& definition,
|
||||
const ArchiveSerializable& archivable,
|
||||
int64_t& lastInsertID);
|
||||
bool unarchiveInstance(const ArchiveDef& definition,
|
||||
ArchiveSerializable::ArchiveName name,
|
||||
ArchiveSerializable& archivable);
|
||||
size_t unarchiveInstances(const ArchiveDef& definition,
|
||||
UnarchiveStep stepper,
|
||||
ArchiveSerializable::ArchiveName optionalName);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(Archive);
|
||||
};
|
||||
|
||||
class ArchiveItem {
|
||||
public:
|
||||
template <class T, class = std::enable_if<std::is_integral<T>::value>>
|
||||
bool encode(ArchiveSerializable::Member member, T item) {
|
||||
return encodeIntegral(member, static_cast<int64_t>(item));
|
||||
}
|
||||
|
||||
bool encode(ArchiveSerializable::Member member, double item);
|
||||
|
||||
bool encode(ArchiveSerializable::Member member, const std::string& item);
|
||||
|
||||
bool encode(ArchiveSerializable::Member member, const Allocation& allocation);
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
bool encodeArchivable(ArchiveSerializable::Member member, const T& other) {
|
||||
const ArchiveDef& otherDef = T::ArchiveDefinition;
|
||||
return encode(member, otherDef, other);
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if<std::is_enum<T>::value>>
|
||||
bool encodeEnum(ArchiveSerializable::Member member, const T& item) {
|
||||
return encodeIntegral(member, static_cast<int64_t>(item));
|
||||
}
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
bool encode(ArchiveSerializable::Member member, const std::vector<T>& items) {
|
||||
/*
|
||||
* All items in the vector are individually encoded and their keys noted
|
||||
*/
|
||||
std::vector<int64_t> members;
|
||||
members.reserve(items.size());
|
||||
|
||||
const ArchiveDef& itemDefinition = T::ArchiveDefinition;
|
||||
for (const auto& item : items) {
|
||||
int64_t added = 0;
|
||||
bool result = _context.archiveInstance(itemDefinition, item, added);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
members.emplace_back(added);
|
||||
}
|
||||
|
||||
/*
|
||||
* The keys are flattened into the vectors table. Write to that table
|
||||
*/
|
||||
auto vectorInsert = encodeVectorKeys(std::move(members));
|
||||
|
||||
if (!vectorInsert.first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return encodeIntegral(member, vectorInsert.second);
|
||||
}
|
||||
|
||||
template <class Super,
|
||||
class Current,
|
||||
class = std::enable_if<
|
||||
std::is_base_of<ArchiveSerializable, Super>::value &&
|
||||
std::is_base_of<ArchiveSerializable, Current>::value>>
|
||||
bool encodeSuper(const Current& thiz) {
|
||||
std::string oldClass = _currentClass;
|
||||
_currentClass = Super::ArchiveDefinition.className;
|
||||
auto success = thiz.Super::serialize(*this);
|
||||
_currentClass = oldClass;
|
||||
return success;
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if<std::is_integral<T>::value>>
|
||||
bool decode(ArchiveSerializable::Member member, T& item) {
|
||||
int64_t decoded = 0;
|
||||
auto result = decodeIntegral(member, decoded);
|
||||
item = static_cast<T>(decoded);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool decode(ArchiveSerializable::Member member, double& item);
|
||||
|
||||
bool decode(ArchiveSerializable::Member member, std::string& item);
|
||||
|
||||
bool decode(ArchiveSerializable::Member member, Allocation& allocation);
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
bool decodeArchivable(ArchiveSerializable::Member member, T& other) {
|
||||
const ArchiveDef& otherDef = T::ArchiveDefinition;
|
||||
return decode(member, otherDef, other);
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if<std::is_enum<T>::value>>
|
||||
bool decodeEnum(ArchiveSerializable::Member member, T& item) {
|
||||
int64_t desugared = 0;
|
||||
if (decodeIntegral(member, desugared)) {
|
||||
item = static_cast<T>(desugared);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <
|
||||
class T,
|
||||
class = std::enable_if<std::is_base_of<ArchiveSerializable, T>::value>>
|
||||
bool decode(ArchiveSerializable::Member member, std::vector<T>& items) {
|
||||
/*
|
||||
* From the member, find the foreign key of the vector
|
||||
*/
|
||||
int64_t vectorForeignKey = 0;
|
||||
if (!decodeIntegral(member, vectorForeignKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get vector keys
|
||||
*/
|
||||
std::vector<int64_t> keys;
|
||||
if (!decodeVectorKeys(vectorForeignKey, keys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ArchiveDef& otherDef = T::ArchiveDefinition;
|
||||
for (const auto& key : keys) {
|
||||
items.emplace_back();
|
||||
|
||||
if (!_context.unarchiveInstance(otherDef, key, items.back())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Super,
|
||||
class Current,
|
||||
class = std::enable_if<
|
||||
std::is_base_of<ArchiveSerializable, Super>::value &&
|
||||
std::is_base_of<ArchiveSerializable, Current>::value>>
|
||||
bool decodeSuper(Current& thiz) {
|
||||
std::string oldClass = _currentClass;
|
||||
_currentClass = Super::ArchiveDefinition.className;
|
||||
auto success = thiz.Super::deserialize(*this);
|
||||
_currentClass = oldClass;
|
||||
return success;
|
||||
}
|
||||
|
||||
ArchiveSerializable::ArchiveName name() const;
|
||||
|
||||
private:
|
||||
Archive& _context;
|
||||
ArchiveStatement& _statement;
|
||||
const ArchiveClassRegistration& _registration;
|
||||
ArchiveSerializable::ArchiveName _name;
|
||||
std::string _currentClass;
|
||||
|
||||
friend class Archive;
|
||||
|
||||
ArchiveItem(Archive& context,
|
||||
ArchiveStatement& statement,
|
||||
const ArchiveClassRegistration& registration,
|
||||
ArchiveSerializable::ArchiveName name);
|
||||
|
||||
bool encodeIntegral(ArchiveSerializable::Member member, int64_t item);
|
||||
|
||||
bool decodeIntegral(ArchiveSerializable::Member member, int64_t& item);
|
||||
|
||||
std::pair<bool, int64_t> encodeVectorKeys(std::vector<int64_t>&& members);
|
||||
|
||||
bool decodeVectorKeys(ArchiveSerializable::ArchiveName name,
|
||||
std::vector<int64_t>& members);
|
||||
|
||||
bool encode(ArchiveSerializable::Member member,
|
||||
const ArchiveDef& otherDef,
|
||||
const ArchiveSerializable& other);
|
||||
|
||||
bool decode(ArchiveSerializable::Member member,
|
||||
const ArchiveDef& otherDef,
|
||||
ArchiveSerializable& other);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ArchiveItem);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
@ -0,0 +1,150 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/archivist/archive_class_registration.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "impeller/archivist/archive_database.h"
|
||||
#include "impeller/archivist/archive_statement.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
static const char* const ArchiveColumnPrefix = "item";
|
||||
static const char* const ArchivePrimaryKeyColumnName = "name";
|
||||
static const char* const ArchiveTablePrefix = "RL_";
|
||||
|
||||
ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database,
|
||||
ArchiveDef definition)
|
||||
: _database(database), _className(definition.className), _memberCount(0) {
|
||||
/*
|
||||
* Each class in the archive class hierarchy is assigned an entry in the
|
||||
* class map.
|
||||
*/
|
||||
const ArchiveDef* current = &definition;
|
||||
size_t currentMember = 1;
|
||||
while (current != nullptr) {
|
||||
auto membersInCurrent = current->members.size();
|
||||
_memberCount += membersInCurrent;
|
||||
MemberColumnMap map;
|
||||
for (const auto& member : current->members) {
|
||||
map[member] = currentMember++;
|
||||
}
|
||||
_classMap[current->className] = map;
|
||||
current = current->superClass;
|
||||
}
|
||||
|
||||
_isReady = createTable(definition.autoAssignName);
|
||||
}
|
||||
|
||||
const std::string& ArchiveClassRegistration::className() const {
|
||||
return _className;
|
||||
}
|
||||
|
||||
size_t ArchiveClassRegistration::memberCount() const {
|
||||
return _memberCount;
|
||||
}
|
||||
|
||||
bool ArchiveClassRegistration::isReady() const {
|
||||
return _isReady;
|
||||
}
|
||||
|
||||
ArchiveClassRegistration::ColumnResult ArchiveClassRegistration::findColumn(
|
||||
const std::string& className,
|
||||
ArchiveSerializable::Member member) const {
|
||||
auto found = _classMap.find(className);
|
||||
|
||||
if (found == _classMap.end()) {
|
||||
return {0, false};
|
||||
}
|
||||
|
||||
const auto& memberToColumns = found->second;
|
||||
|
||||
auto foundColumn = memberToColumns.find(member);
|
||||
|
||||
if (foundColumn == memberToColumns.end()) {
|
||||
return {0, false};
|
||||
}
|
||||
|
||||
return {foundColumn->second, true};
|
||||
}
|
||||
|
||||
bool ArchiveClassRegistration::createTable(bool autoIncrement) {
|
||||
if (_className.size() == 0 || _memberCount == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
/*
|
||||
* Table names cannot participate in parameter substitution, so we prepare
|
||||
* a statement and check its validity before running.
|
||||
*/
|
||||
stream << "CREATE TABLE IF NOT EXISTS " << ArchiveTablePrefix
|
||||
<< _className.c_str() << " (" << ArchivePrimaryKeyColumnName;
|
||||
|
||||
if (autoIncrement) {
|
||||
stream << " INTEGER PRIMARY KEY AUTOINCREMENT, ";
|
||||
} else {
|
||||
stream << " INTEGER PRIMARY KEY, ";
|
||||
}
|
||||
for (size_t i = 0, columns = _memberCount; i < columns; i++) {
|
||||
stream << ArchiveColumnPrefix << std::to_string(i + 1);
|
||||
if (i != columns - 1) {
|
||||
stream << ", ";
|
||||
}
|
||||
}
|
||||
stream << ");";
|
||||
|
||||
auto statement = _database.acquireStatement(stream.str());
|
||||
|
||||
if (!statement.isReady()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!statement.reset()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return statement.run() == ArchiveStatement::Result::Done;
|
||||
}
|
||||
|
||||
ArchiveStatement ArchiveClassRegistration::queryStatement(bool single) const {
|
||||
std::stringstream stream;
|
||||
stream << "SELECT " << ArchivePrimaryKeyColumnName << ", ";
|
||||
for (size_t i = 0, members = _memberCount; i < members; i++) {
|
||||
stream << ArchiveColumnPrefix << std::to_string(i + 1);
|
||||
if (i != members - 1) {
|
||||
stream << ",";
|
||||
}
|
||||
}
|
||||
stream << " FROM " << ArchiveTablePrefix << _className;
|
||||
|
||||
if (single) {
|
||||
stream << " WHERE " << ArchivePrimaryKeyColumnName << " = ?";
|
||||
} else {
|
||||
stream << " ORDER BY " << ArchivePrimaryKeyColumnName << " ASC";
|
||||
}
|
||||
|
||||
stream << ";";
|
||||
|
||||
return _database.acquireStatement(stream.str());
|
||||
}
|
||||
|
||||
ArchiveStatement ArchiveClassRegistration::insertStatement() const {
|
||||
std::stringstream stream;
|
||||
stream << "INSERT OR REPLACE INTO " << ArchiveTablePrefix << _className
|
||||
<< " VALUES ( ?, ";
|
||||
for (size_t i = 0; i < _memberCount; i++) {
|
||||
stream << "?";
|
||||
if (i != _memberCount - 1) {
|
||||
stream << ", ";
|
||||
}
|
||||
}
|
||||
stream << ");";
|
||||
|
||||
return _database.acquireStatement(stream.str());
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
@ -0,0 +1,49 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/archivist/archive.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ArchiveClassRegistration {
|
||||
public:
|
||||
using ColumnResult = std::pair<size_t, bool>;
|
||||
ColumnResult findColumn(const std::string& className,
|
||||
ArchiveSerializable::Member member) const;
|
||||
|
||||
const std::string& className() const;
|
||||
|
||||
size_t memberCount() const;
|
||||
|
||||
bool isReady() const;
|
||||
|
||||
ArchiveStatement insertStatement() const;
|
||||
|
||||
ArchiveStatement queryStatement(bool single) const;
|
||||
|
||||
static const size_t NameIndex = 0;
|
||||
|
||||
private:
|
||||
using MemberColumnMap = std::map<ArchiveSerializable::Member, size_t>;
|
||||
using ClassMap = std::map<std::string, MemberColumnMap>;
|
||||
|
||||
friend class ArchiveDatabase;
|
||||
|
||||
ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition);
|
||||
|
||||
bool createTable(bool autoIncrement);
|
||||
|
||||
ArchiveDatabase& _database;
|
||||
ClassMap _classMap;
|
||||
std::string _className;
|
||||
size_t _memberCount;
|
||||
bool _isReady;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ArchiveClassRegistration);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
121
engine/src/flutter/impeller/archivist/archive_database.cc
Normal file
121
engine/src/flutter/impeller/archivist/archive_database.cc
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/archivist/archive_database.h"
|
||||
|
||||
#include "third_party/sqlite/sqlite3.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "impeller/archivist/archive.h"
|
||||
#include "impeller/archivist/archive_class_registration.h"
|
||||
#include "impeller/archivist/archive_statement.h"
|
||||
#include "impeller/base/validation.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
#define DB_HANDLE reinterpret_cast<sqlite3*>(_db)
|
||||
|
||||
ArchiveDatabase::ArchiveDatabase(const std::string& filename, bool recreate) {
|
||||
if (recreate) {
|
||||
::remove(filename.c_str());
|
||||
}
|
||||
|
||||
if (::sqlite3_initialize() != SQLITE_OK) {
|
||||
VALIDATION_LOG << "Could not initialize sqlite.";
|
||||
return;
|
||||
}
|
||||
|
||||
sqlite3* db = nullptr;
|
||||
auto res = ::sqlite3_open(filename.c_str(), &db);
|
||||
_db = db;
|
||||
|
||||
if (res != SQLITE_OK || _db == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_beginTransaction = std::unique_ptr<ArchiveStatement>(
|
||||
new ArchiveStatement(_db, "BEGIN TRANSACTION;"));
|
||||
|
||||
if (!_beginTransaction->isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_endTransaction = std::unique_ptr<ArchiveStatement>(
|
||||
new ArchiveStatement(_db, "END TRANSACTION;"));
|
||||
|
||||
if (!_endTransaction->isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_rollbackTransaction = std::unique_ptr<ArchiveStatement>(
|
||||
new ArchiveStatement(_db, "ROLLBACK TRANSACTION;"));
|
||||
|
||||
if (!_rollbackTransaction->isReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_ready = true;
|
||||
}
|
||||
|
||||
ArchiveDatabase::~ArchiveDatabase() {
|
||||
::sqlite3_close(DB_HANDLE);
|
||||
}
|
||||
|
||||
bool ArchiveDatabase::isReady() const {
|
||||
return _ready;
|
||||
}
|
||||
|
||||
int64_t ArchiveDatabase::lastInsertRowID() {
|
||||
return ::sqlite3_last_insert_rowid(DB_HANDLE);
|
||||
}
|
||||
|
||||
static inline const ArchiveClassRegistration* RegistrationIfReady(
|
||||
const ArchiveClassRegistration* registration) {
|
||||
if (registration == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return registration->isReady() ? registration : nullptr;
|
||||
}
|
||||
|
||||
const ArchiveClassRegistration* ArchiveDatabase::registrationForDefinition(
|
||||
const ArchiveDef& definition) {
|
||||
auto found = _registrations.find(definition.className);
|
||||
if (found != _registrations.end()) {
|
||||
/*
|
||||
* This class has already been registered.
|
||||
*/
|
||||
return RegistrationIfReady(found->second.get());
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a new class registration for the given class definition.
|
||||
*/
|
||||
auto registration = std::unique_ptr<ArchiveClassRegistration>(
|
||||
new ArchiveClassRegistration(*this, definition));
|
||||
auto res =
|
||||
_registrations.emplace(definition.className, std::move(registration));
|
||||
|
||||
/*
|
||||
* If the new class registation is ready, return it to the caller.
|
||||
*/
|
||||
return res.second ? RegistrationIfReady((*(res.first)).second.get())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
ArchiveStatement ArchiveDatabase::acquireStatement(
|
||||
const std::string& statementString) const {
|
||||
return ArchiveStatement{_db, statementString};
|
||||
}
|
||||
|
||||
ArchiveTransaction ArchiveDatabase::acquireTransaction(
|
||||
int64_t& transactionCount) {
|
||||
return ArchiveTransaction{transactionCount, //
|
||||
*_beginTransaction, //
|
||||
*_endTransaction, //
|
||||
*_rollbackTransaction};
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
50
engine/src/flutter/impeller/archivist/archive_database.h
Normal file
50
engine/src/flutter/impeller/archivist/archive_database.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/archivist/archive_transaction.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ArchiveStatement;
|
||||
class ArchiveClassRegistration;
|
||||
struct ArchiveDef;
|
||||
|
||||
class ArchiveDatabase {
|
||||
public:
|
||||
ArchiveDatabase(const std::string& filename, bool recreate);
|
||||
|
||||
~ArchiveDatabase();
|
||||
|
||||
bool isReady() const;
|
||||
|
||||
int64_t lastInsertRowID();
|
||||
|
||||
const ArchiveClassRegistration* registrationForDefinition(
|
||||
const ArchiveDef& definition);
|
||||
|
||||
ArchiveTransaction acquireTransaction(int64_t& transactionCount);
|
||||
|
||||
private:
|
||||
void* _db = nullptr;
|
||||
bool _ready = false;
|
||||
std::map<std::string, std::unique_ptr<ArchiveClassRegistration>>
|
||||
_registrations;
|
||||
std::unique_ptr<ArchiveStatement> _beginTransaction;
|
||||
std::unique_ptr<ArchiveStatement> _endTransaction;
|
||||
std::unique_ptr<ArchiveStatement> _rollbackTransaction;
|
||||
|
||||
friend class ArchiveClassRegistration;
|
||||
|
||||
ArchiveStatement acquireStatement(const std::string& statementString) const;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ArchiveDatabase);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
179
engine/src/flutter/impeller/archivist/archive_statement.cc
Normal file
179
engine/src/flutter/impeller/archivist/archive_statement.cc
Normal file
@ -0,0 +1,179 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/archivist/archive_statement.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "third_party/sqlite/sqlite3.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
#define STATEMENT_HANDLE reinterpret_cast<::sqlite3_stmt*>(_statement)
|
||||
|
||||
ArchiveStatement::ArchiveStatement(void* db, const std::string& statememt) {
|
||||
::sqlite3_stmt* statementHandle = nullptr;
|
||||
auto res = ::sqlite3_prepare_v2(reinterpret_cast<sqlite3*>(db), //
|
||||
statememt.c_str(), //
|
||||
static_cast<int>(statememt.size()), //
|
||||
&statementHandle, //
|
||||
nullptr);
|
||||
_statement = statementHandle;
|
||||
_ready = res == SQLITE_OK && _statement != nullptr;
|
||||
}
|
||||
|
||||
ArchiveStatement::ArchiveStatement(ArchiveStatement&& other)
|
||||
: _statement(other._statement), _ready(other._ready) {
|
||||
other._statement = nullptr;
|
||||
other._ready = false;
|
||||
}
|
||||
|
||||
ArchiveStatement::~ArchiveStatement() {
|
||||
if (_statement != nullptr) {
|
||||
auto res = ::sqlite3_finalize(STATEMENT_HANDLE);
|
||||
FML_CHECK(res == SQLITE_OK) << "Unable to finalize the archive.";
|
||||
}
|
||||
}
|
||||
|
||||
bool ArchiveStatement::isReady() const {
|
||||
return _ready;
|
||||
}
|
||||
|
||||
bool ArchiveStatement::reset() {
|
||||
if (::sqlite3_reset(STATEMENT_HANDLE) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::sqlite3_clear_bindings(STATEMENT_HANDLE) != SQLITE_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr int ToParam(size_t index) {
|
||||
/*
|
||||
* sqlite parameters begin from 1
|
||||
*/
|
||||
return static_cast<int>(index + 1);
|
||||
}
|
||||
|
||||
static constexpr int ToColumn(size_t index) {
|
||||
/*
|
||||
* sqlite columns begin from 1
|
||||
*/
|
||||
return static_cast<int>(index);
|
||||
}
|
||||
|
||||
size_t ArchiveStatement::columnCount() {
|
||||
return ::sqlite3_column_count(STATEMENT_HANDLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind Variants
|
||||
*/
|
||||
bool ArchiveStatement::bind(size_t index, const std::string& item) {
|
||||
return ::sqlite3_bind_text(STATEMENT_HANDLE, //
|
||||
ToParam(index), //
|
||||
item.data(), //
|
||||
static_cast<int>(item.size()), //
|
||||
SQLITE_TRANSIENT) == SQLITE_OK;
|
||||
}
|
||||
|
||||
bool ArchiveStatement::bindIntegral(size_t index, int64_t item) {
|
||||
return ::sqlite3_bind_int64(STATEMENT_HANDLE, //
|
||||
ToParam(index), //
|
||||
item) == SQLITE_OK;
|
||||
}
|
||||
|
||||
bool ArchiveStatement::bind(size_t index, double item) {
|
||||
return ::sqlite3_bind_double(STATEMENT_HANDLE, //
|
||||
ToParam(index), //
|
||||
item) == SQLITE_OK;
|
||||
}
|
||||
|
||||
bool ArchiveStatement::bind(size_t index, const Allocation& item) {
|
||||
return ::sqlite3_bind_blob(STATEMENT_HANDLE, //
|
||||
ToParam(index), //
|
||||
item.GetBuffer(), //
|
||||
static_cast<int>(item.GetLength()), //
|
||||
SQLITE_TRANSIENT) == SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Column Variants
|
||||
*/
|
||||
bool ArchiveStatement::columnIntegral(size_t index, int64_t& item) {
|
||||
item = ::sqlite3_column_int64(STATEMENT_HANDLE, ToColumn(index));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArchiveStatement::column(size_t index, double& item) {
|
||||
item = ::sqlite3_column_double(STATEMENT_HANDLE, ToColumn(index));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* For cases where byte sizes of column data is necessary, the
|
||||
* recommendations in https://www.sqlite.org/c3ref/column_blob.html regarding
|
||||
* type conversions are followed.
|
||||
*
|
||||
* TL;DR: Access blobs then bytes.
|
||||
*/
|
||||
|
||||
bool ArchiveStatement::column(size_t index, std::string& item) {
|
||||
/*
|
||||
* Get the character data
|
||||
*/
|
||||
auto chars = reinterpret_cast<const char*>(
|
||||
::sqlite3_column_text(STATEMENT_HANDLE, ToColumn(index)));
|
||||
|
||||
/*
|
||||
* Get the length of the string (in bytes)
|
||||
*/
|
||||
size_t textByteSize =
|
||||
::sqlite3_column_bytes(STATEMENT_HANDLE, ToColumn(index));
|
||||
|
||||
std::string text(chars, textByteSize);
|
||||
item.swap(text);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArchiveStatement::column(size_t index, Allocation& item) {
|
||||
/*
|
||||
* Get a blob pointer
|
||||
*/
|
||||
auto blob = reinterpret_cast<const uint8_t*>(
|
||||
::sqlite3_column_blob(STATEMENT_HANDLE, ToColumn(index)));
|
||||
|
||||
/*
|
||||
* Decode the number of bytes in the blob
|
||||
*/
|
||||
size_t byteSize = ::sqlite3_column_bytes(STATEMENT_HANDLE, ToColumn(index));
|
||||
|
||||
/*
|
||||
* Reszie the host allocation and move the blob contents into it
|
||||
*/
|
||||
if (!item.Truncate(byteSize, false /* npot */)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memmove(item.GetBuffer(), blob, byteSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiveStatement::Result ArchiveStatement::run() {
|
||||
switch (::sqlite3_step(STATEMENT_HANDLE)) {
|
||||
case SQLITE_DONE:
|
||||
return Result::Done;
|
||||
case SQLITE_ROW:
|
||||
return Result::Row;
|
||||
default:
|
||||
return Result::Failure;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
71
engine/src/flutter/impeller/archivist/archive_statement.h
Normal file
71
engine/src/flutter/impeller/archivist/archive_statement.h
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/base/allocation.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ArchiveStatement {
|
||||
public:
|
||||
~ArchiveStatement();
|
||||
|
||||
ArchiveStatement(ArchiveStatement&& message);
|
||||
|
||||
bool isReady() const;
|
||||
|
||||
bool reset();
|
||||
|
||||
bool bind(size_t index, const std::string& item);
|
||||
|
||||
template <class T, class = std::enable_if<std::is_integral<T>::value>>
|
||||
bool bind(size_t index, T item) {
|
||||
return bindIntegral(index, static_cast<int64_t>(item));
|
||||
}
|
||||
|
||||
bool bind(size_t index, double item);
|
||||
|
||||
bool bind(size_t index, const Allocation& item);
|
||||
|
||||
template <class T, class = std::enable_if<std::is_integral<T>::value>>
|
||||
bool column(size_t index, T& item) {
|
||||
return columnIntegral(index, item);
|
||||
}
|
||||
|
||||
bool column(size_t index, double& item);
|
||||
|
||||
bool column(size_t index, std::string& item);
|
||||
|
||||
bool column(size_t index, Allocation& item);
|
||||
|
||||
size_t columnCount();
|
||||
|
||||
enum class Result {
|
||||
Done,
|
||||
Row,
|
||||
Failure,
|
||||
};
|
||||
|
||||
Result run();
|
||||
|
||||
private:
|
||||
void* _statement = nullptr;
|
||||
bool _ready = false;
|
||||
|
||||
friend class ArchiveDatabase;
|
||||
|
||||
ArchiveStatement(void* db, const std::string& statememt);
|
||||
|
||||
bool bindIntegral(size_t index, int64_t item);
|
||||
|
||||
bool columnIntegral(size_t index, int64_t& item);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ArchiveStatement);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
52
engine/src/flutter/impeller/archivist/archive_transaction.cc
Normal file
52
engine/src/flutter/impeller/archivist/archive_transaction.cc
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/archivist/archive_transaction.h"
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "impeller/archivist/archive_statement.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
ArchiveTransaction::ArchiveTransaction(int64_t& transactionCount,
|
||||
ArchiveStatement& beginStatement,
|
||||
ArchiveStatement& endStatement,
|
||||
ArchiveStatement& rollbackStatement)
|
||||
: _endStatement(endStatement),
|
||||
_rollbackStatement(rollbackStatement),
|
||||
_transactionCount(transactionCount) {
|
||||
if (_transactionCount == 0) {
|
||||
_cleanup = beginStatement.run() == ArchiveStatement::Result::Done;
|
||||
}
|
||||
_transactionCount++;
|
||||
}
|
||||
|
||||
ArchiveTransaction::ArchiveTransaction(ArchiveTransaction&& other)
|
||||
: _endStatement(other._endStatement),
|
||||
_rollbackStatement(other._rollbackStatement),
|
||||
_transactionCount(other._transactionCount),
|
||||
_cleanup(other._cleanup),
|
||||
_successful(other._successful) {
|
||||
other._abandoned = true;
|
||||
}
|
||||
|
||||
ArchiveTransaction::~ArchiveTransaction() {
|
||||
if (_abandoned) {
|
||||
return;
|
||||
}
|
||||
|
||||
FML_CHECK(_transactionCount != 0);
|
||||
if (_transactionCount == 1 && _cleanup) {
|
||||
auto res = _successful ? _endStatement.run() : _rollbackStatement.run();
|
||||
FML_CHECK(res == ArchiveStatement::Result::Done)
|
||||
<< "Must be able to commit the nested transaction";
|
||||
}
|
||||
_transactionCount--;
|
||||
}
|
||||
|
||||
void ArchiveTransaction::markWritesSuccessful() {
|
||||
_successful = true;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
40
engine/src/flutter/impeller/archivist/archive_transaction.h
Normal file
40
engine/src/flutter/impeller/archivist/archive_transaction.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ArchiveStatement;
|
||||
|
||||
class ArchiveTransaction {
|
||||
public:
|
||||
ArchiveTransaction(ArchiveTransaction&& transaction);
|
||||
|
||||
~ArchiveTransaction();
|
||||
|
||||
void markWritesSuccessful();
|
||||
|
||||
private:
|
||||
ArchiveStatement& _endStatement;
|
||||
ArchiveStatement& _rollbackStatement;
|
||||
int64_t& _transactionCount;
|
||||
bool _cleanup = false;
|
||||
bool _successful = false;
|
||||
bool _abandoned = false;
|
||||
|
||||
friend class ArchiveDatabase;
|
||||
|
||||
ArchiveTransaction(int64_t& transactionCount,
|
||||
ArchiveStatement& beginStatement,
|
||||
ArchiveStatement& endStatement,
|
||||
ArchiveStatement& rollbackStatement);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ArchiveTransaction);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
58
engine/src/flutter/impeller/archivist/archive_vector.cc
Normal file
58
engine/src/flutter/impeller/archivist/archive_vector.cc
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "impeller/archivist/archive_vector.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace impeller {
|
||||
|
||||
ArchiveVector::ArchiveVector(std::vector<int64_t>&& keys)
|
||||
: _keys(std::move(keys)) {}
|
||||
|
||||
ArchiveVector::ArchiveVector() {}
|
||||
|
||||
const ArchiveDef ArchiveVector::ArchiveDefinition = {
|
||||
/* .superClass = */ nullptr,
|
||||
/* .className = */ "Meta_Vector",
|
||||
/* .autoAssignName = */ true,
|
||||
/* .members = */ {0},
|
||||
};
|
||||
|
||||
ArchiveSerializable::ArchiveName ArchiveVector::archiveName() const {
|
||||
return ArchiveNameAuto;
|
||||
}
|
||||
|
||||
const std::vector<int64_t> ArchiveVector::keys() const {
|
||||
return _keys;
|
||||
}
|
||||
|
||||
bool ArchiveVector::serialize(ArchiveItem& item) const {
|
||||
std::stringstream stream;
|
||||
for (size_t i = 0, count = _keys.size(); i < count; i++) {
|
||||
stream << _keys[i];
|
||||
if (i != count - 1) {
|
||||
stream << ",";
|
||||
}
|
||||
}
|
||||
return item.encode(0, stream.str());
|
||||
}
|
||||
|
||||
bool ArchiveVector::deserialize(ArchiveItem& item) {
|
||||
std::string flattened;
|
||||
if (!item.decode(0, flattened)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream stream(flattened);
|
||||
int64_t single = 0;
|
||||
while (stream >> single) {
|
||||
_keys.emplace_back(single);
|
||||
stream.ignore();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
36
engine/src/flutter/impeller/archivist/archive_vector.h
Normal file
36
engine/src/flutter/impeller/archivist/archive_vector.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "impeller/archivist/archive.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
class ArchiveVector : public ArchiveSerializable {
|
||||
public:
|
||||
static const ArchiveDef ArchiveDefinition;
|
||||
|
||||
ArchiveName archiveName() const override;
|
||||
|
||||
const std::vector<int64_t> keys() const;
|
||||
|
||||
bool serialize(ArchiveItem& item) const override;
|
||||
|
||||
bool deserialize(ArchiveItem& item) override;
|
||||
|
||||
private:
|
||||
std::vector<int64_t> _keys;
|
||||
|
||||
friend class ArchiveItem;
|
||||
|
||||
ArchiveVector();
|
||||
|
||||
ArchiveVector(std::vector<int64_t>&& keys);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ArchiveVector);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
158
engine/src/flutter/impeller/archivist/archivist_unittests.cc
Normal file
158
engine/src/flutter/impeller/archivist/archivist_unittests.cc
Normal file
@ -0,0 +1,158 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "impeller/archivist/archive.h"
|
||||
|
||||
namespace impeller {
|
||||
namespace testing {
|
||||
|
||||
static ArchiveSerializable::ArchiveName LastSample = 0;
|
||||
|
||||
class Sample : public ArchiveSerializable {
|
||||
public:
|
||||
Sample(uint64_t count = 42) : _someData(count), _name(++LastSample) {}
|
||||
|
||||
uint64_t someData() const { return _someData; }
|
||||
|
||||
ArchiveName archiveName() const override { return _name; }
|
||||
|
||||
bool serialize(ArchiveItem& item) const override {
|
||||
return item.encode(999, _someData);
|
||||
};
|
||||
|
||||
bool deserialize(ArchiveItem& item) override {
|
||||
_name = item.name();
|
||||
return item.decode(999, _someData);
|
||||
};
|
||||
|
||||
static const ArchiveDef ArchiveDefinition;
|
||||
|
||||
private:
|
||||
uint64_t _someData;
|
||||
ArchiveName _name;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(Sample);
|
||||
};
|
||||
|
||||
const ArchiveDef Sample::ArchiveDefinition = {
|
||||
.superClass = nullptr,
|
||||
.className = "Sample",
|
||||
.autoAssignName = false,
|
||||
.members = {999},
|
||||
};
|
||||
|
||||
TEST(ArchiveTest, SimpleInitialization) {
|
||||
auto name = "/tmp/sample.db";
|
||||
{
|
||||
Archive archive(name, true);
|
||||
ASSERT_TRUE(archive.isReady());
|
||||
}
|
||||
ASSERT_EQ(::remove(name), 0);
|
||||
}
|
||||
|
||||
TEST(ArchiveTest, AddStorageClass) {
|
||||
auto name = "/tmp/sample2.db";
|
||||
{
|
||||
Archive archive(name, true);
|
||||
ASSERT_TRUE(archive.isReady());
|
||||
}
|
||||
ASSERT_EQ(::remove(name), 0);
|
||||
}
|
||||
|
||||
TEST(ArchiveTest, AddData) {
|
||||
auto name = "/tmp/sample3.db";
|
||||
{
|
||||
Archive archive(name, true);
|
||||
ASSERT_TRUE(archive.isReady());
|
||||
Sample sample;
|
||||
ASSERT_TRUE(archive.archive(sample));
|
||||
}
|
||||
ASSERT_EQ(::remove(name), 0);
|
||||
}
|
||||
|
||||
TEST(ArchiveTest, AddDataMultiple) {
|
||||
auto name = "/tmp/sample4.db";
|
||||
{
|
||||
Archive archive(name, true);
|
||||
ASSERT_TRUE(archive.isReady());
|
||||
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
Sample sample(i + 1);
|
||||
ASSERT_TRUE(archive.archive(sample));
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(::remove(name), 0);
|
||||
}
|
||||
|
||||
TEST(ArchiveTest, ReadData) {
|
||||
auto name = "/tmp/sample5.db";
|
||||
{
|
||||
Archive archive(name, true);
|
||||
ASSERT_TRUE(archive.isReady());
|
||||
|
||||
size_t count = 50;
|
||||
|
||||
std::vector<ArchiveSerializable::ArchiveName> keys;
|
||||
std::vector<uint64_t> values;
|
||||
|
||||
keys.reserve(count);
|
||||
values.reserve(count);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
Sample sample(i + 1);
|
||||
keys.push_back(sample.archiveName());
|
||||
values.push_back(sample.someData());
|
||||
ASSERT_TRUE(archive.archive(sample));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
Sample sample;
|
||||
ASSERT_TRUE(archive.unarchive(keys[i], sample));
|
||||
ASSERT_EQ(values[i], sample.someData());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(::remove(name), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This shouldn't be slow. Need to cache compiled statements.
|
||||
*/
|
||||
TEST(ArchiveTest, ReadDataWithNames) {
|
||||
auto name = "/tmp/sample6.db";
|
||||
{
|
||||
Archive archive(name, true);
|
||||
ASSERT_TRUE(archive.isReady());
|
||||
|
||||
size_t count = 8;
|
||||
|
||||
std::vector<ArchiveSerializable::ArchiveName> keys;
|
||||
std::vector<uint64_t> values;
|
||||
|
||||
keys.reserve(count);
|
||||
values.reserve(count);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
Sample sample(i + 1);
|
||||
keys.push_back(sample.archiveName());
|
||||
values.push_back(sample.someData());
|
||||
ASSERT_TRUE(archive.archive(sample));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
Sample sample;
|
||||
ASSERT_TRUE(archive.unarchive(keys[i], sample));
|
||||
ASSERT_EQ(values[i], sample.someData());
|
||||
ASSERT_EQ(keys[i], sample.archiveName());
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(::remove(name), 0);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user