From 188890c357199a12f630da644bc3f15486222d7a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 11 Dec 2021 15:57:30 -0800 Subject: [PATCH] Support user readable table names. --- impeller/archivist/archivable.h | 8 +- impeller/archivist/archive.h | 18 ++-- .../archivist/archive_class_registration.cc | 87 +++++++----------- .../archivist/archive_class_registration.h | 15 ++-- impeller/archivist/archive_location.cc | 47 +++++----- impeller/archivist/archive_location.h | 88 +++++++------------ impeller/archivist/archive_vector.cc | 16 ++-- impeller/archivist/archive_vector.h | 4 +- impeller/archivist/archivist_unittests.cc | 78 ++++++++++++++-- 9 files changed, 186 insertions(+), 175 deletions(-) diff --git a/impeller/archivist/archivable.h b/impeller/archivist/archivable.h index 783c138920471..5018d5a16e93d 100644 --- a/impeller/archivist/archivable.h +++ b/impeller/archivist/archivable.h @@ -12,13 +12,9 @@ namespace impeller { struct ArchiveDef { - using Member = uint64_t; - using Members = std::vector; - - const ArchiveDef* isa = nullptr; const std::string table_name; const bool auto_key = true; - const Members members; + const std::vector members; }; class ArchiveLocation; @@ -31,6 +27,8 @@ using PrimaryKey = std::optional; /// class Archivable { public: + virtual ~Archivable() = default; + virtual PrimaryKey GetPrimaryKey() const = 0; virtual bool Write(ArchiveLocation& item) const = 0; diff --git a/impeller/archivist/archive.h b/impeller/archivist/archive.h index 18d7b9ad1cd66..c2e2eb7a48184 100644 --- a/impeller/archivist/archive.h +++ b/impeller/archivist/archive.h @@ -27,25 +27,25 @@ class Archive { bool IsValid() const; template ::value>> - bool Write(const T& archivable) { - const ArchiveDef& def = T::ArchiveDefinition; + class = std::enable_if_t::value>> + [[nodiscard]] bool Write(const T& archivable) { + const ArchiveDef& def = T::kArchiveDefinition; return ArchiveInstance(def, archivable).has_value(); } template ::value>> - bool Read(PrimaryKey name, T& archivable) { - const ArchiveDef& def = T::ArchiveDefinition; + class = std::enable_if_t::value>> + [[nodiscard]] bool Read(PrimaryKey name, T& archivable) { + const ArchiveDef& def = T::kArchiveDefinition; return UnarchiveInstance(def, name, archivable); } using UnarchiveStep = std::function; template ::value>> - size_t Read(UnarchiveStep stepper) { - const ArchiveDef& def = T::ArchiveDefinition; + class = std::enable_if_t::value>> + [[nodiscard]] size_t Read(UnarchiveStep stepper) { + const ArchiveDef& def = T::kArchiveDefinition; return UnarchiveInstances(def, stepper); } diff --git a/impeller/archivist/archive_class_registration.cc b/impeller/archivist/archive_class_registration.cc index 10ef0951a8981..0a715eabaf8b4 100644 --- a/impeller/archivist/archive_class_registration.cc +++ b/impeller/archivist/archive_class_registration.cc @@ -11,66 +11,41 @@ namespace impeller { -static const char* const ArchiveColumnPrefix = "col_"; -static const char* const ArchivePrimaryKeyColumnName = "primary_key"; +static constexpr const char* kArchivePrimaryKeyColumnName = "primary_key"; ArchiveClassRegistration::ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition) - : database_(database), class_name_(definition.table_name) { - /* - * 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(); - member_count_ += membersInCurrent; - MemberColumnMap map; - for (const auto& member : current->members) { - map[member] = currentMember++; - } - class_map_[current->table_name] = map; - current = current->isa; + : database_(database), definition_(std::move(definition)) { + for (size_t i = 0; i < definition.members.size(); i++) { + // The first index entry is the primary key. So add one to the index. + column_map_[definition.members[i]] = i + 1; } - - is_ready_ = CreateTable(definition.auto_key); + is_valid_ = CreateTable(); } const std::string& ArchiveClassRegistration::GetClassName() const { - return class_name_; + return definition_.table_name; } size_t ArchiveClassRegistration::GetMemberCount() const { - return member_count_; + return column_map_.size(); } bool ArchiveClassRegistration::IsValid() const { - return is_ready_; + return is_valid_; } std::optional ArchiveClassRegistration::FindColumnIndex( - const std::string& className, - ArchiveDef::Member member) const { - auto found = class_map_.find(className); - - if (found == class_map_.end()) { - return std::nullopt; - } - - const auto& memberToColumns = found->second; - - auto foundColumn = memberToColumns.find(member); - - if (foundColumn == memberToColumns.end()) { + const std::string& member) const { + auto found = column_map_.find(member); + if (found == column_map_.end()) { return std::nullopt; } - - return foundColumn->second; + return found->second; } -bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { - if (class_name_.size() == 0 || member_count_ == 0) { +bool ArchiveClassRegistration::CreateTable() { + if (definition_.table_name.empty() || definition_.members.empty()) { return false; } @@ -80,16 +55,17 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { * Table names cannot participate in parameter substitution, so we prepare * a statement and check its validity before running. */ - stream << "CREATE TABLE IF NOT EXISTS " << class_name_.c_str() << " (" - << ArchivePrimaryKeyColumnName; + stream << "CREATE TABLE IF NOT EXISTS " << definition_.table_name << " (" + << kArchivePrimaryKeyColumnName; - if (autoIncrement) { + if (definition_.auto_key) { stream << " INTEGER PRIMARY KEY AUTOINCREMENT, "; } else { stream << " INTEGER PRIMARY KEY, "; } - for (size_t i = 0, columns = member_count_; i < columns; i++) { - stream << ArchiveColumnPrefix << std::to_string(i + 1); + + for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { + stream << definition_.members[i]; if (i != columns - 1) { stream << ", "; } @@ -112,19 +88,19 @@ bool ArchiveClassRegistration::CreateTable(bool autoIncrement) { ArchiveStatement ArchiveClassRegistration::CreateQueryStatement( bool single) const { std::stringstream stream; - stream << "SELECT " << ArchivePrimaryKeyColumnName << ", "; - for (size_t i = 0, members = member_count_; i < members; i++) { - stream << ArchiveColumnPrefix << std::to_string(i + 1); - if (i != members - 1) { + stream << "SELECT " << kArchivePrimaryKeyColumnName << ", "; + for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { + stream << definition_.members[i]; + if (i != columns - 1) { stream << ","; } } - stream << " FROM " << class_name_; + stream << " FROM " << definition_.table_name; if (single) { - stream << " WHERE " << ArchivePrimaryKeyColumnName << " = ?"; + stream << " WHERE " << kArchivePrimaryKeyColumnName << " = ?"; } else { - stream << " ORDER BY " << ArchivePrimaryKeyColumnName << " ASC"; + stream << " ORDER BY " << kArchivePrimaryKeyColumnName << " ASC"; } stream << ";"; @@ -134,10 +110,11 @@ ArchiveStatement ArchiveClassRegistration::CreateQueryStatement( ArchiveStatement ArchiveClassRegistration::CreateInsertStatement() const { std::stringstream stream; - stream << "INSERT OR REPLACE INTO " << class_name_ << " VALUES ( ?, "; - for (size_t i = 0; i < member_count_; i++) { + stream << "INSERT OR REPLACE INTO " << definition_.table_name + << " VALUES ( ?, "; + for (size_t i = 0, columns = definition_.members.size(); i < columns; i++) { stream << "?"; - if (i != member_count_ - 1) { + if (i != columns - 1) { stream << ", "; } } diff --git a/impeller/archivist/archive_class_registration.h b/impeller/archivist/archive_class_registration.h index eec4ecf2ea1ca..d290a46f5bbb2 100644 --- a/impeller/archivist/archive_class_registration.h +++ b/impeller/archivist/archive_class_registration.h @@ -17,8 +17,7 @@ class ArchiveClassRegistration { bool IsValid() const; - std::optional FindColumnIndex(const std::string& className, - ArchiveDef::Member member) const; + std::optional FindColumnIndex(const std::string& member) const; const std::string& GetClassName() const; @@ -29,20 +28,18 @@ class ArchiveClassRegistration { ArchiveStatement CreateQueryStatement(bool single) const; private: - using MemberColumnMap = std::map; - using ClassMap = std::map; + using MemberColumnMap = std::map; friend class ArchiveDatabase; ArchiveClassRegistration(ArchiveDatabase& database, ArchiveDef definition); - bool CreateTable(bool autoIncrement); + bool CreateTable(); ArchiveDatabase& database_; - ClassMap class_map_; - std::string class_name_; - size_t member_count_ = 0; - bool is_ready_ = false; + const ArchiveDef definition_; + MemberColumnMap column_map_; + bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(ArchiveClassRegistration); }; diff --git a/impeller/archivist/archive_location.cc b/impeller/archivist/archive_location.cc index 1945d6492c3c9..906174809b712 100644 --- a/impeller/archivist/archive_location.cc +++ b/impeller/archivist/archive_location.cc @@ -16,38 +16,37 @@ ArchiveLocation::ArchiveLocation(Archive& context, : context_(context), statement_(statement), registration_(registration), - primary_key_(name), - current_class_(registration.GetClassName()) {} + primary_key_(name) {} PrimaryKey ArchiveLocation::GetPrimaryKey() const { return primary_key_; } -bool ArchiveLocation::Write(ArchiveDef::Member member, +bool ArchiveLocation::Write(const std::string& member, const std::string& item) { - auto index = registration_.FindColumnIndex(current_class_, member); + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::WriteIntegral(ArchiveDef::Member member, int64_t item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::WriteIntegral(const std::string& member, int64_t item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::Write(ArchiveDef::Member member, double item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Write(const std::string& member, double item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::Write(ArchiveDef::Member member, const Allocation& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Write(const std::string& member, const Allocation& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.WriteValue(index.value(), item) : false; } -bool ArchiveLocation::Write(ArchiveDef::Member member, +bool ArchiveLocation::Write(const std::string& member, const ArchiveDef& otherDef, const Archivable& other) { - auto index = registration_.FindColumnIndex(current_class_, member); + auto index = registration_.FindColumnIndex(member); if (!index.has_value()) { return false; @@ -76,13 +75,13 @@ bool ArchiveLocation::Write(ArchiveDef::Member member, std::optional ArchiveLocation::WriteVectorKeys( std::vector&& members) { ArchiveVector vector(std::move(members)); - return context_.ArchiveInstance(ArchiveVector::ArchiveDefinition, vector); + return context_.ArchiveInstance(ArchiveVector::kArchiveDefinition, vector); } bool ArchiveLocation::ReadVectorKeys(PrimaryKey name, std::vector& members) { ArchiveVector vector; - if (!context_.UnarchiveInstance(ArchiveVector::ArchiveDefinition, name, + if (!context_.UnarchiveInstance(ArchiveVector::kArchiveDefinition, name, vector)) { return false; } @@ -91,30 +90,30 @@ bool ArchiveLocation::ReadVectorKeys(PrimaryKey name, return true; } -bool ArchiveLocation::Read(ArchiveDef::Member member, std::string& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Read(const std::string& member, std::string& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::ReadIntegral(ArchiveDef::Member member, int64_t& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::ReadIntegral(const std::string& member, int64_t& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::Read(ArchiveDef::Member member, double& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Read(const std::string& member, double& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::Read(ArchiveDef::Member member, Allocation& item) { - auto index = registration_.FindColumnIndex(current_class_, member); +bool ArchiveLocation::Read(const std::string& member, Allocation& item) { + auto index = registration_.FindColumnIndex(member); return index.has_value() ? statement_.ReadValue(index.value(), item) : false; } -bool ArchiveLocation::Read(ArchiveDef::Member member, +bool ArchiveLocation::Read(const std::string& member, const ArchiveDef& otherDef, Archivable& other) { - auto index = registration_.FindColumnIndex(current_class_, member); + auto index = registration_.FindColumnIndex(member); /* * Make sure a member is present at that column diff --git a/impeller/archivist/archive_location.h b/impeller/archivist/archive_location.h index af2188689415c..63428dfa94dfe 100644 --- a/impeller/archivist/archive_location.h +++ b/impeller/archivist/archive_location.h @@ -22,46 +22,45 @@ class ArchiveLocation { public: PrimaryKey GetPrimaryKey() const; - template ::value>> - bool Write(ArchiveDef::Member member, T item) { + template ::value>> + bool Write(const std::string& member, T item) { return WriteIntegral(member, static_cast(item)); } - bool Write(ArchiveDef::Member member, double item); + bool Write(const std::string& member, double item); - bool Write(ArchiveDef::Member member, const std::string& item); + bool Write(const std::string& member, const std::string& item); - bool Write(ArchiveDef::Member member, const Allocation& allocation); + bool Write(const std::string& member, const Allocation& allocation); template ::value>> - bool WriteArchivable(ArchiveDef::Member member, const T& other) { + class = std::enable_if_t::value>> + bool WriteArchivable(const std::string& member, const T& other) { const ArchiveDef& otherDef = T::ArchiveDefinition; return Write(member, otherDef, other); } - template ::value>> - bool WriteEnum(ArchiveDef::Member member, const T& item) { + template ::value>> + bool WriteEnum(const std::string& member, const T& item) { return WriteIntegral(member, static_cast(item)); } template ::value>> - bool Write(ArchiveDef::Member member, const std::vector& items) { + class = std::enable_if_t::value>> + bool Write(const std::string& member, const std::vector& items) { /* * All items in the vector are individually encoded and their keys noted */ std::vector members; members.reserve(items.size()); - const ArchiveDef& itemDefinition = T::ArchiveDefinition; + const ArchiveDef& itemDefinition = T::kArchiveDefinition; for (const auto& item : items) { - int64_t added = 0; - bool result = context_.ArchiveInstance(itemDefinition, item, added); - if (!result) { + auto row_id = context_.ArchiveInstance(itemDefinition, item); + if (!row_id.has_value()) { return false; } - members.emplace_back(added); + members.emplace_back(row_id.value()); } /* @@ -76,41 +75,29 @@ class ArchiveLocation { return WriteIntegral(member, vectorInsert.value()); } - template ::value && - std::is_base_of::value>> - bool WriteSuper(const Current& thiz) { - std::string oldClass = current_class_; - current_class_ = Super::ArchiveDefinition.className; - auto success = thiz.Super::serialize(*this); - current_class_ = oldClass; - return success; - } - - template ::value>> - bool Read(ArchiveDef::Member member, T& item) { + template ::value>> + bool Read(const std::string& member, T& item) { int64_t decoded = 0; auto result = ReadIntegral(member, decoded); item = static_cast(decoded); return result; } - bool Read(ArchiveDef::Member member, double& item); + bool Read(const std::string& member, double& item); - bool Read(ArchiveDef::Member member, std::string& item); + bool Read(const std::string& member, std::string& item); - bool Read(ArchiveDef::Member member, Allocation& allocation); + bool Read(const std::string& member, Allocation& allocation); template ::value>> - bool ReadArchivable(ArchiveDef::Member member, T& other) { + class = std::enable_if_t::value>> + bool ReadArchivable(const std::string& member, T& other) { const ArchiveDef& otherDef = T::ArchiveDefinition; return decode(member, otherDef, other); } - template ::value>> - bool ReadEnum(ArchiveDef::Member member, T& item) { + template ::value>> + bool ReadEnum(const std::string& member, T& item) { int64_t desugared = 0; if (ReadIntegral(member, desugared)) { item = static_cast(desugared); @@ -120,8 +107,8 @@ class ArchiveLocation { } template ::value>> - bool Read(ArchiveDef::Member member, std::vector& items) { + class = std::enable_if_t::value>> + bool Read(const std::string& member, std::vector& items) { /* * From the member, find the foreign key of the vector */ @@ -138,7 +125,7 @@ class ArchiveLocation { return false; } - const ArchiveDef& otherDef = T::ArchiveDefinition; + const ArchiveDef& otherDef = T::kArchiveDefinition; for (const auto& key : keys) { items.emplace_back(); @@ -150,24 +137,11 @@ class ArchiveLocation { return true; } - template ::value && - std::is_base_of::value>> - bool ReadSuper(Current& thiz) { - std::string oldClass = current_class_; - current_class_ = Super::ArchiveDefinition.className; - auto success = thiz.Super::deserialize(*this); - current_class_ = oldClass; - return success; - } - private: Archive& context_; ArchiveStatement& statement_; const ArchiveClassRegistration& registration_; PrimaryKey primary_key_; - std::string current_class_; friend class Archive; @@ -176,19 +150,19 @@ class ArchiveLocation { const ArchiveClassRegistration& registration, PrimaryKey name); - bool WriteIntegral(ArchiveDef::Member member, int64_t item); + bool WriteIntegral(const std::string& member, int64_t item); - bool ReadIntegral(ArchiveDef::Member member, int64_t& item); + bool ReadIntegral(const std::string& member, int64_t& item); std::optional WriteVectorKeys(std::vector&& members); bool ReadVectorKeys(PrimaryKey name, std::vector& members); - bool Write(ArchiveDef::Member member, + bool Write(const std::string& member, const ArchiveDef& otherDef, const Archivable& other); - bool Read(ArchiveDef::Member member, + bool Read(const std::string& member, const ArchiveDef& otherDef, Archivable& other); diff --git a/impeller/archivist/archive_vector.cc b/impeller/archivist/archive_vector.cc index 697d93e32b924..57a83724aa154 100644 --- a/impeller/archivist/archive_vector.cc +++ b/impeller/archivist/archive_vector.cc @@ -10,16 +10,16 @@ namespace impeller { -ArchiveVector::ArchiveVector(std::vector&& keys) +ArchiveVector::ArchiveVector(std::vector keys) : keys_(std::move(keys)) {} ArchiveVector::ArchiveVector() {} -const ArchiveDef ArchiveVector::ArchiveDefinition = { - /* .superClass = */ nullptr, - /* .className = */ "_IPLR_meta_vector_items_", - /* .autoAssignName = */ true, - /* .members = */ {0}, +static constexpr const char* kVectorKeys = "keys"; + +ArchiveDef ArchiveVector::kArchiveDefinition = { + .table_name = "IPLR_vectors", + .members = {kVectorKeys}, }; PrimaryKey ArchiveVector::GetPrimaryKey() const { @@ -39,12 +39,12 @@ bool ArchiveVector::Write(ArchiveLocation& item) const { stream << ","; } } - return item.Write(0, stream.str()); + return item.Write(kVectorKeys, stream.str()); } bool ArchiveVector::Read(ArchiveLocation& item) { std::string flattened; - if (!item.Read(0, flattened)) { + if (!item.Read(kVectorKeys, flattened)) { return false; } diff --git a/impeller/archivist/archive_vector.h b/impeller/archivist/archive_vector.h index e7fd59bd156c9..556dd3c31c21a 100644 --- a/impeller/archivist/archive_vector.h +++ b/impeller/archivist/archive_vector.h @@ -11,7 +11,7 @@ namespace impeller { class ArchiveVector : public Archivable { public: - static const ArchiveDef ArchiveDefinition; + static ArchiveDef kArchiveDefinition; PrimaryKey GetPrimaryKey() const override; @@ -28,7 +28,7 @@ class ArchiveVector : public Archivable { ArchiveVector(); - ArchiveVector(std::vector&& keys); + ArchiveVector(std::vector keys); FML_DISALLOW_COPY_AND_ASSIGN(ArchiveVector); }; diff --git a/impeller/archivist/archivist_unittests.cc b/impeller/archivist/archivist_unittests.cc index 5e8d17a5b6106..fdf9608e451a0 100644 --- a/impeller/archivist/archivist_unittests.cc +++ b/impeller/archivist/archivist_unittests.cc @@ -20,6 +20,8 @@ class Sample : public Archivable { public: Sample(uint64_t count = 42) : some_data_(count) {} + Sample(Sample&&) = default; + uint64_t GetSomeData() const { return some_data_; } // |Archivable| @@ -27,16 +29,16 @@ class Sample : public Archivable { // |Archivable| bool Write(ArchiveLocation& item) const override { - return item.Write(999, some_data_); + return item.Write("some_data", some_data_); }; // |Archivable| bool Read(ArchiveLocation& item) override { name_ = item.GetPrimaryKey(); - return item.Read(999, some_data_); + return item.Read("some_data", some_data_); }; - static const ArchiveDef ArchiveDefinition; + static const ArchiveDef kArchiveDefinition; private: uint64_t some_data_; @@ -45,11 +47,58 @@ class Sample : public Archivable { FML_DISALLOW_COPY_AND_ASSIGN(Sample); }; -const ArchiveDef Sample::ArchiveDefinition = { - .isa = nullptr, +const ArchiveDef Sample::kArchiveDefinition = { .table_name = "Sample", .auto_key = false, - .members = {999}, + .members = {"some_data"}, +}; + +class SampleWithVector : public Archivable { + public: + SampleWithVector() = default; + + // |Archivable| + PrimaryKey GetPrimaryKey() const override { return std::nullopt; } + + // |Archivable| + bool Write(ArchiveLocation& item) const override { + std::vector samples; + for (size_t i = 0; i < 50u; i++) { + samples.emplace_back(Sample{1988 + i}); + } + return item.Write("hello", "world") && item.Write("samples", samples); + }; + + // |Archivable| + bool Read(ArchiveLocation& item) override { + std::string str; + auto str_result = item.Read("hello", str); + std::vector samples; + auto vec_result = item.Read("samples", samples); + + if (!str_result || str != "world" || !vec_result || samples.size() != 50) { + return false; + } + + size_t current = 1988; + for (const auto& sample : samples) { + if (sample.GetSomeData() != current++) { + return false; + } + } + return true; + }; + + static const ArchiveDef kArchiveDefinition; + + private: + std::vector samples_; + FML_DISALLOW_COPY_AND_ASSIGN(SampleWithVector); +}; + +const ArchiveDef SampleWithVector::kArchiveDefinition = { + .table_name = "SampleWithVector", + .members = {"hello", "samples"}, }; using ArchiveTest = ArchivistFixture; @@ -131,5 +180,22 @@ TEST_F(ArchiveTest, ReadDataWithNames) { } } +TEST_F(ArchiveTest, CanReadWriteVectorOfArchivables) { + Archive archive(GetArchiveFileName().c_str()); + ASSERT_TRUE(archive.IsValid()); + + SampleWithVector sample_with_vector; + ASSERT_TRUE(archive.Write(sample_with_vector)); + bool read_success = false; + ASSERT_EQ( + archive.Read([&](ArchiveLocation& location) -> bool { + SampleWithVector other_sample_with_vector; + read_success = other_sample_with_vector.Read(location); + return true; // Always keep continuing but assert that we only get one. + }), + 1u); + ASSERT_TRUE(read_success); +} + } // namespace testing } // namespace impeller