修改pods

This commit is contained in:
2025-09-20 17:13:38 +08:00
parent 7787b3ee30
commit 28ff2b0264
5251 changed files with 345029 additions and 285168 deletions

View File

@@ -19,13 +19,11 @@
#include <cstdint>
#include <ctime>
#include <chrono>
#include <iosfwd>
#include <string>
#if !defined(_STLPORT_VERSION)
#include <chrono> // NOLINT(build/c++11)
#endif // !defined(_STLPORT_VERSION)
namespace firebase {
/**
@@ -117,7 +115,6 @@ class Timestamp {
*/
static Timestamp FromTimeT(time_t seconds_since_unix_epoch);
#if !defined(_STLPORT_VERSION)
/**
* Converts `std::chrono::time_point` to a `Timestamp`.
*
@@ -145,7 +142,6 @@ class Timestamp {
template <typename Clock = std::chrono::system_clock,
typename Duration = std::chrono::microseconds>
std::chrono::time_point<Clock, Duration> ToTimePoint() const;
#endif // !defined(_STLPORT_VERSION)
/**
* Returns a string representation of this `Timestamp` for logging/debugging
@@ -205,8 +201,6 @@ inline bool operator==(const Timestamp& lhs, const Timestamp& rhs) {
return !(lhs != rhs);
}
#if !defined(_STLPORT_VERSION)
// Make sure the header compiles even when included after `<windows.h>` without
// `NOMINMAX` defined. `push/pop_macro` pragmas are supported by Visual Studio
// as well as Clang and GCC.
@@ -239,8 +233,6 @@ std::chrono::time_point<Clock, Duration> Timestamp::ToTimePoint() const {
#pragma pop_macro("max")
#pragma pop_macro("min")
#endif // !defined(_STLPORT_VERSION)
} // namespace firebase
#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_TIMESTAMP_H_

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/api/document_reference.h"
#include <future> // NOLINT(build/c++11)
#include <future>
#include <memory>
#include "Firestore/core/src/api/collection_reference.h"

View File

@@ -18,7 +18,7 @@
#define FIRESTORE_CORE_SRC_API_FIRESTORE_H_
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include "Firestore/core/src/api/api_fwd.h"

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 Google
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/api/load_bundle_task.h"
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <utility>
#include "Firestore/core/src/util/autoid.h"

View File

@@ -19,7 +19,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include <utility>
#include <vector>

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/api/query_core.h"
#include <future> // NOLINT(build/c++11)
#include <future>
#include <memory>
#include <utility>
#include <vector>

View File

@@ -69,15 +69,15 @@ std::unique_ptr<LocalCacheSettings> Settings::CopyCacheSettings(
UNREACHABLE();
}
std::unique_ptr<MemoryGargabeCollectorSettings>
std::unique_ptr<MemoryGarbageCollectorSettings>
MemoryCacheSettings::CopyMemoryGcSettings(
const MemoryGargabeCollectorSettings& settings) {
const MemoryGarbageCollectorSettings& settings) {
if (settings.kind() ==
MemoryGargabeCollectorSettings::MemoryGcKind::kEagerGc) {
MemoryGarbageCollectorSettings::MemoryGcKind::kEagerGc) {
return absl::make_unique<MemoryEagerGcSettings>(
static_cast<const MemoryEagerGcSettings&>(settings));
} else if (settings.kind() ==
MemoryGargabeCollectorSettings::MemoryGcKind::kLruGc) {
MemoryGarbageCollectorSettings::MemoryGcKind::kLruGc) {
return absl::make_unique<MemoryLruGcSettings>(
static_cast<const MemoryLruGcSettings&>(settings));
}
@@ -107,7 +107,7 @@ MemoryLruGcSettings MemoryLruGcSettings::WithSizeBytes(int64_t size) const {
}
MemoryCacheSettings MemoryCacheSettings::WithMemoryGarbageCollectorSettings(
const MemoryGargabeCollectorSettings& settings) {
const MemoryGarbageCollectorSettings& settings) {
MemoryCacheSettings new_settings(*this);
new_settings.settings_ = CopyMemoryGcSettings(settings);
return new_settings;
@@ -157,18 +157,18 @@ bool operator==(const LocalCacheSettings& lhs, const LocalCacheSettings& rhs) {
UNREACHABLE();
}
bool operator==(const MemoryGargabeCollectorSettings& lhs,
const MemoryGargabeCollectorSettings& rhs) {
bool operator==(const MemoryGarbageCollectorSettings& lhs,
const MemoryGarbageCollectorSettings& rhs) {
if (lhs.kind() != rhs.kind()) {
return false;
}
if (lhs.kind() == MemoryGargabeCollectorSettings::MemoryGcKind::kEagerGc) {
if (lhs.kind() == MemoryGarbageCollectorSettings::MemoryGcKind::kEagerGc) {
return static_cast<const MemoryEagerGcSettings&>(lhs) ==
static_cast<const MemoryEagerGcSettings&>(rhs);
}
if (lhs.kind() == MemoryGargabeCollectorSettings::MemoryGcKind::kLruGc) {
if (lhs.kind() == MemoryGarbageCollectorSettings::MemoryGcKind::kLruGc) {
return static_cast<const MemoryLruGcSettings&>(lhs) ==
static_cast<const MemoryLruGcSettings&>(rhs);
}
@@ -268,7 +268,7 @@ int64_t Settings::cache_size_bytes() const {
auto* memory_cache_settings =
static_cast<MemoryCacheSettings*>(cache_settings_.get());
if (memory_cache_settings->gc_settings().kind() ==
MemoryGargabeCollectorSettings::MemoryGcKind::kLruGc) {
MemoryGarbageCollectorSettings::MemoryGcKind::kLruGc) {
return static_cast<const MemoryLruGcSettings&>(
memory_cache_settings->gc_settings())
.size_bytes();
@@ -289,14 +289,14 @@ bool Settings::gc_enabled() const {
auto* memory_cache_settings =
static_cast<MemoryCacheSettings*>(cache_settings_.get());
return memory_cache_settings->gc_settings().kind() ==
MemoryGargabeCollectorSettings::MemoryGcKind::kLruGc &&
MemoryGarbageCollectorSettings::MemoryGcKind::kLruGc &&
static_cast<const MemoryLruGcSettings&>(
memory_cache_settings->gc_settings())
.size_bytes() != CacheSizeUnlimited;
}
}
return persistence_enabled_ && cache_size_bytes_ != CacheSizeUnlimited;
return cache_size_bytes_ != CacheSizeUnlimited;
}
const LocalCacheSettings* Settings::local_cache_settings() const {

View File

@@ -97,7 +97,7 @@ class LocalCacheSettings {
friend class Settings;
public:
enum class Kind { kMemory, kPersistent };
enum class Kind { kMemory = 1, kPersistent };
virtual ~LocalCacheSettings() = default;
friend bool operator==(const LocalCacheSettings& lhs,
const LocalCacheSettings& rhs);
@@ -133,12 +133,13 @@ class PersistentCacheSettings : public LocalCacheSettings {
int64_t size_bytes_;
};
class MemoryGargabeCollectorSettings {
class MemoryGarbageCollectorSettings {
public:
enum class MemoryGcKind { kEagerGc, kLruGc };
virtual ~MemoryGargabeCollectorSettings() = default;
friend bool operator==(const MemoryGargabeCollectorSettings& lhs,
const MemoryGargabeCollectorSettings& rhs);
enum class MemoryGcKind { kEagerGc = 1, kLruGc };
virtual ~MemoryGarbageCollectorSettings() = default;
friend bool operator==(const MemoryGarbageCollectorSettings& lhs,
const MemoryGarbageCollectorSettings& rhs);
virtual size_t Hash() const = 0;
MemoryGcKind kind() const {
@@ -146,25 +147,25 @@ class MemoryGargabeCollectorSettings {
}
protected:
explicit MemoryGargabeCollectorSettings(MemoryGcKind kind) : kind_(kind) {
explicit MemoryGarbageCollectorSettings(MemoryGcKind kind) : kind_(kind) {
}
MemoryGcKind kind_;
};
class MemoryEagerGcSettings : public MemoryGargabeCollectorSettings {
class MemoryEagerGcSettings : public MemoryGarbageCollectorSettings {
public:
MemoryEagerGcSettings()
: MemoryGargabeCollectorSettings(
MemoryGargabeCollectorSettings::MemoryGcKind::kEagerGc) {
: MemoryGarbageCollectorSettings(
MemoryGarbageCollectorSettings::MemoryGcKind::kEagerGc) {
}
size_t Hash() const override;
};
class MemoryLruGcSettings : public MemoryGargabeCollectorSettings {
class MemoryLruGcSettings : public MemoryGarbageCollectorSettings {
public:
MemoryLruGcSettings()
: MemoryGargabeCollectorSettings(
MemoryGargabeCollectorSettings::MemoryGcKind::kLruGc),
: MemoryGarbageCollectorSettings(
MemoryGarbageCollectorSettings::MemoryGcKind::kLruGc),
size_bytes_(Settings::DefaultCacheSizeBytes) {
}
@@ -185,7 +186,7 @@ class MemoryCacheSettings : public LocalCacheSettings {
public:
MemoryCacheSettings()
: LocalCacheSettings(LocalCacheSettings::Kind::kMemory),
settings_(absl::make_unique<MemoryEagerGcSettings>()) {
settings_(absl::make_unique<MemoryLruGcSettings>()) {
}
MemoryCacheSettings(const MemoryCacheSettings& other);
MemoryCacheSettings& operator=(const MemoryCacheSettings& other);
@@ -193,17 +194,17 @@ class MemoryCacheSettings : public LocalCacheSettings {
size_t Hash() const override;
MemoryCacheSettings WithMemoryGarbageCollectorSettings(
const MemoryGargabeCollectorSettings& settings);
const MemoryGarbageCollectorSettings& settings);
const MemoryGargabeCollectorSettings& gc_settings() const {
const MemoryGarbageCollectorSettings& gc_settings() const {
return *settings_;
}
private:
static std::unique_ptr<MemoryGargabeCollectorSettings> CopyMemoryGcSettings(
const MemoryGargabeCollectorSettings& settings);
static std::unique_ptr<MemoryGarbageCollectorSettings> CopyMemoryGcSettings(
const MemoryGarbageCollectorSettings& settings);
std::unique_ptr<MemoryGargabeCollectorSettings> settings_;
std::unique_ptr<MemoryGarbageCollectorSettings> settings_;
};
bool operator!=(const Settings& lhs, const Settings& rhs);

View File

@@ -17,6 +17,7 @@
#include "Firestore/core/src/core/composite_filter.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "Firestore/core/src/core/field_filter.h"
@@ -141,16 +142,14 @@ const FieldFilter* CompositeFilter::Rep::FindFirstMatchingFilter(
return nullptr;
}
const std::vector<FieldFilter>& CompositeFilter::Rep::GetFlattenedFilters()
const {
return memoized_flattened_filters_->memoize([&]() {
std::vector<FieldFilter> flattened_filters;
for (const auto& filter : filters())
std::copy(filter.GetFlattenedFilters().begin(),
filter.GetFlattenedFilters().end(),
std::back_inserter(flattened_filters));
return flattened_filters;
});
std::shared_ptr<std::vector<FieldFilter>>
CompositeFilter::Rep::CalculateFlattenedFilters() const {
auto flattened_filters = std::make_shared<std::vector<FieldFilter>>();
for (const auto& filter : filters())
std::copy(filter.GetFlattenedFilters().begin(),
filter.GetFlattenedFilters().end(),
std::back_inserter(*flattened_filters));
return flattened_filters;
}
} // namespace core

View File

@@ -138,7 +138,8 @@ class CompositeFilter : public Filter {
return filters_.empty();
}
const std::vector<FieldFilter>& GetFlattenedFilters() const override;
std::shared_ptr<std::vector<FieldFilter>> CalculateFlattenedFilters()
const override;
std::vector<Filter> GetFilters() const override {
return filters();

View File

@@ -18,7 +18,7 @@
#define FIRESTORE_CORE_SRC_CORE_EVENT_LISTENER_H_
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <utility>
#include "Firestore/core/src/util/executor.h"

View File

@@ -16,6 +16,7 @@
#include "Firestore/core/src/core/field_filter.h"
#include <memory>
#include <utility>
#include "Firestore/core/src/core/array_contains_any_filter.h"
@@ -122,12 +123,12 @@ FieldFilter::FieldFilter(std::shared_ptr<const Filter::Rep> rep)
: Filter(std::move(rep)) {
}
const std::vector<FieldFilter>& FieldFilter::Rep::GetFlattenedFilters() const {
std::shared_ptr<std::vector<FieldFilter>>
FieldFilter::Rep::CalculateFlattenedFilters() const {
// This is already a field filter, so we return a vector of size one.
return memoized_flattened_filters_->memoize([&]() {
return std::vector<FieldFilter>{
FieldFilter(std::make_shared<const Rep>(*this))};
});
auto filters = std::make_shared<std::vector<FieldFilter>>();
filters->push_back(FieldFilter(std::make_shared<const Rep>(*this)));
return filters;
}
std::vector<Filter> FieldFilter::Rep::GetFilters() const {
@@ -156,7 +157,8 @@ bool FieldFilter::Rep::Matches(const model::Document& doc) const {
// Types do not have to match in NotEqual filters.
if (op_ == Operator::NotEqual) {
return MatchesComparison(Compare(lhs, *value_rhs_));
return lhs.which_value_type != google_firestore_v1_Value_null_value_tag &&
MatchesComparison(Compare(lhs, *value_rhs_));
}
// Only compare types with matching backend order (such as double and int).

View File

@@ -117,8 +117,6 @@ class FieldFilter : public Filter {
return false;
}
const std::vector<FieldFilter>& GetFlattenedFilters() const override;
std::vector<Filter> GetFilters() const override;
protected:
@@ -140,6 +138,9 @@ class FieldFilter : public Filter {
bool MatchesComparison(util::ComparisonResult comparison) const;
std::shared_ptr<std::vector<FieldFilter>> CalculateFlattenedFilters()
const override;
private:
friend class FieldFilter;

View File

@@ -35,12 +35,6 @@ std::ostream& operator<<(std::ostream& os, const Filter& filter) {
return os << filter.ToString();
}
Filter::Rep::Rep()
: memoized_flattened_filters_(
std::make_shared<
util::ThreadSafeMemoizer<std::vector<FieldFilter>>>()) {
}
} // namespace core
} // namespace firestore
} // namespace firebase

View File

@@ -17,6 +17,7 @@
#ifndef FIRESTORE_CORE_SRC_CORE_FILTER_H_
#define FIRESTORE_CORE_SRC_CORE_FILTER_H_
#include <functional>
#include <iosfwd>
#include <memory>
#include <string>
@@ -114,7 +115,7 @@ class Filter {
protected:
class Rep {
public:
Rep();
Rep() = default;
virtual ~Rep() = default;
@@ -147,20 +148,23 @@ class Filter {
virtual bool IsEmpty() const = 0;
virtual const std::vector<FieldFilter>& GetFlattenedFilters() const = 0;
virtual const std::vector<FieldFilter>& GetFlattenedFilters() const {
const auto func = std::bind(&Rep::CalculateFlattenedFilters, this);
return memoized_flattened_filters_.value(func);
}
virtual std::vector<Filter> GetFilters() const = 0;
protected:
virtual std::shared_ptr<std::vector<FieldFilter>>
CalculateFlattenedFilters() const = 0;
private:
/**
* Memoized list of all field filters that can be found by
* traversing the tree of filters contained in this composite filter.
*
* Use a `std::shared_ptr<ThreadSafeMemoizer>` rather than using
* `ThreadSafeMemoizer` directly so that this class is copyable
* (`ThreadSafeMemoizer` is not copyable because of its `std::once_flag`
* member variable, which is not copyable).
*/
mutable std::shared_ptr<util::ThreadSafeMemoizer<std::vector<FieldFilter>>>
mutable util::ThreadSafeMemoizer<const std::vector<FieldFilter>>
memoized_flattened_filters_;
};

View File

@@ -17,7 +17,7 @@
#include "Firestore/core/src/core/firestore_client.h"
#include <functional>
#include <future> // NOLINT(build/c++11)
#include <future>
#include <memory>
#include <string>
#include <utility>

View File

@@ -62,7 +62,10 @@ bool NotInFilter::Rep::Matches(const Document& doc) const {
return false;
}
absl::optional<google_firestore_v1_Value> maybe_lhs = doc->field(field());
return maybe_lhs && !Contains(array_value, *maybe_lhs);
return maybe_lhs &&
maybe_lhs->which_value_type !=
google_firestore_v1_Value_null_value_tag &&
!Contains(array_value, *maybe_lhs);
}
} // namespace core

View File

@@ -17,6 +17,7 @@
#include "Firestore/core/src/core/query.h"
#include <algorithm>
#include <memory>
#include <ostream>
#include "Firestore/core/src/core/bound.h"
@@ -91,44 +92,42 @@ absl::optional<Operator> Query::FindOpInsideFilters(
return absl::nullopt;
}
const std::vector<OrderBy>& Query::normalized_order_bys() const {
return memoized_normalized_order_bys_->memoize([&]() {
// Any explicit order by fields should be added as is.
std::vector<OrderBy> result = explicit_order_bys_;
std::set<FieldPath> fieldsNormalized;
for (const OrderBy& order_by : explicit_order_bys_) {
fieldsNormalized.insert(order_by.field());
std::shared_ptr<const std::vector<OrderBy>> Query::CalculateNormalizedOrderBys()
const {
// Any explicit order by fields should be added as is.
auto result = std::make_shared<std::vector<OrderBy>>(explicit_order_bys_);
std::set<FieldPath> fieldsNormalized;
for (const OrderBy& order_by : explicit_order_bys_) {
fieldsNormalized.insert(order_by.field());
}
// The order of the implicit ordering always matches the last explicit order
// by.
Direction last_direction = explicit_order_bys_.empty()
? Direction::Ascending
: explicit_order_bys_.back().direction();
// Any inequality fields not explicitly ordered should be implicitly ordered
// in a lexicographical order. When there are multiple inequality filters on
// the same field, the field should be added only once. Note:
// `std::set<model::FieldPath>` sorts the key field before other fields.
// However, we want the key field to be sorted last.
const std::set<model::FieldPath> inequality_fields = InequalityFilterFields();
for (const model::FieldPath& field : inequality_fields) {
if (fieldsNormalized.find(field) == fieldsNormalized.end() &&
!field.IsKeyFieldPath()) {
result->push_back(OrderBy(field, last_direction));
}
}
// The order of the implicit ordering always matches the last explicit order
// by.
Direction last_direction = explicit_order_bys_.empty()
? Direction::Ascending
: explicit_order_bys_.back().direction();
// Add the document key field to the last if it is not explicitly ordered.
if (fieldsNormalized.find(FieldPath::KeyFieldPath()) ==
fieldsNormalized.end()) {
result->push_back(OrderBy(FieldPath::KeyFieldPath(), last_direction));
}
// Any inequality fields not explicitly ordered should be implicitly ordered
// in a lexicographical order. When there are multiple inequality filters on
// the same field, the field should be added only once. Note:
// `std::set<model::FieldPath>` sorts the key field before other fields.
// However, we want the key field to be sorted last.
const std::set<model::FieldPath> inequality_fields =
InequalityFilterFields();
for (const model::FieldPath& field : inequality_fields) {
if (fieldsNormalized.find(field) == fieldsNormalized.end() &&
!field.IsKeyFieldPath()) {
result.push_back(OrderBy(field, last_direction));
}
}
// Add the document key field to the last if it is not explicitly ordered.
if (fieldsNormalized.find(FieldPath::KeyFieldPath()) ==
fieldsNormalized.end()) {
result.push_back(OrderBy(FieldPath::KeyFieldPath(), last_direction));
}
return result;
});
return result;
}
LimitType Query::limit_type() const {
@@ -296,14 +295,12 @@ std::string Query::ToString() const {
return absl::StrCat("Query(canonical_id=", CanonicalId(), ")");
}
const Target& Query::ToTarget() const& {
return memoized_target_->memoize(
[&]() { return ToTarget(normalized_order_bys()); });
std::shared_ptr<Target> Query::CalculateTarget() const {
return std::make_shared<Target>(ToTarget(normalized_order_bys()));
}
const Target& Query::ToAggregateTarget() const& {
return memoized_aggregate_target_->memoize(
[&]() { return ToTarget(explicit_order_bys_); });
std::shared_ptr<Target> Query::CalculateAggregateTarget() const {
return std::make_shared<Target>(ToTarget(explicit_order_bys_));
}
Target Query::ToTarget(const std::vector<OrderBy>& order_bys) const {

View File

@@ -17,6 +17,7 @@
#ifndef FIRESTORE_CORE_SRC_CORE_QUERY_H_
#define FIRESTORE_CORE_SRC_CORE_QUERY_H_
#include <functional>
#include <iosfwd>
#include <limits>
#include <memory>
@@ -148,7 +149,10 @@ class Query {
* This might include additional sort orders added implicitly to match the
* backend behavior.
*/
const std::vector<OrderBy>& normalized_order_bys() const;
const std::vector<OrderBy>& normalized_order_bys() const {
const auto func = std::bind(&Query::CalculateNormalizedOrderBys, this);
return memoized_normalized_order_bys_.value(func);
}
bool has_limit() const {
return limit_ != Target::kNoLimit;
@@ -246,7 +250,10 @@ class Query {
* Returns a `Target` instance this query will be mapped to in backend
* and local store.
*/
const Target& ToTarget() const&;
const Target& ToTarget() const& {
const auto func = std::bind(&Query::CalculateTarget, this);
return memoized_target_.value(func);
}
/**
* Returns a `Target` instance this query will be mapped to in backend
@@ -254,7 +261,10 @@ class Query {
* for non-aggregate queries, aggregate query targets do not contain
* normalized order-bys, they only contain explicit order-bys.
*/
const Target& ToAggregateTarget() const&;
const Target& ToAggregateTarget() const& {
const auto func = std::bind(&Query::CalculateAggregateTarget, this);
return memoized_aggregate_target_.value(func);
}
friend std::ostream& operator<<(std::ostream& os, const Query& query);
@@ -289,26 +299,21 @@ class Query {
Target ToTarget(const std::vector<OrderBy>& order_bys) const;
// For properties below, use a `std::shared_ptr<ThreadSafeMemoizer>` rather
// than using `ThreadSafeMemoizer` directly so that this class is copyable
// (`ThreadSafeMemoizer` is not copyable because of its `std::once_flag`
// member variable, which is not copyable).
// The memoized list of sort orders.
mutable std::shared_ptr<util::ThreadSafeMemoizer<std::vector<OrderBy>>>
memoized_normalized_order_bys_{
std::make_shared<util::ThreadSafeMemoizer<std::vector<OrderBy>>>()};
std::shared_ptr<const std::vector<OrderBy>> CalculateNormalizedOrderBys()
const;
mutable util::ThreadSafeMemoizer<const std::vector<OrderBy>>
memoized_normalized_order_bys_;
// The corresponding Target of this Query instance.
mutable std::shared_ptr<util::ThreadSafeMemoizer<Target>> memoized_target_{
std::make_shared<util::ThreadSafeMemoizer<Target>>()};
std::shared_ptr<Target> CalculateTarget() const;
mutable util::ThreadSafeMemoizer<Target> memoized_target_;
// The corresponding aggregate Target of this Query instance. Unlike targets
// for non-aggregate queries, aggregate query targets do not contain
// normalized order-bys, they only contain explicit order-bys.
mutable std::shared_ptr<util::ThreadSafeMemoizer<Target>>
memoized_aggregate_target_{
std::make_shared<util::ThreadSafeMemoizer<Target>>()};
std::shared_ptr<Target> CalculateAggregateTarget() const;
mutable util::ThreadSafeMemoizer<Target> memoized_aggregate_target_;
};
bool operator==(const Query& lhs, const Query& rhs);

View File

@@ -112,7 +112,7 @@ void QueryListener::OnError(Status error) {
}
/**
* Returns whether a snaphsot was raised.
* Returns whether a snapshot was raised.
*/
bool QueryListener::OnOnlineStateChanged(OnlineState online_state) {
online_state_ = online_state;

View File

@@ -219,8 +219,7 @@ Target::IndexBoundValue Target::GetAscendingBound(
switch (field_filter.op()) {
case FieldFilter::Operator::LessThan:
case FieldFilter::Operator::LessThanOrEqual:
filter_value =
model::GetLowerBound(field_filter.value().which_value_type);
filter_value = model::GetLowerBound(field_filter.value());
break;
case FieldFilter::Operator::Equal:
case FieldFilter::Operator::In:
@@ -284,8 +283,7 @@ Target::IndexBoundValue Target::GetDescendingBound(
switch (field_filter.op()) {
case FieldFilter::Operator::GreaterThanOrEqual:
case FieldFilter::Operator::GreaterThan:
filter_value =
model::GetUpperBound(field_filter.value().which_value_type);
filter_value = model::GetUpperBound(field_filter.value());
filter_inclusive = false;
break;
case FieldFilter::Operator::Equal:

View File

@@ -24,7 +24,7 @@
#import <Foundation/Foundation.h>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include <utility>

View File

@@ -24,7 +24,7 @@
#import <Foundation/Foundation.h>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <utility>
#include "Firestore/core/src/credentials/credentials_provider.h"

View File

@@ -18,7 +18,7 @@
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseAuth/Interop/FIRAuthInterop.h"
#import "FirebaseAuth/Interop/Public/FirebaseAuthInterop/FIRAuthInterop.h"
#include "Firestore/core/src/util/error_apple.h"
#include "Firestore/core/src/util/hard_assert.h"

View File

@@ -47,8 +47,8 @@ auto KeysView(const Range& range) -> KeysRange<decltype(std::begin(range))> {
}
template <typename Range, typename K>
auto KeysViewFrom(const Range& range,
const K& key) -> KeysRange<decltype(range.lower_bound(key))> {
auto KeysViewFrom(const Range& range, const K& key)
-> KeysRange<decltype(range.lower_bound(key))> {
auto keys_begin = util::make_iterator_first(range.lower_bound(key));
auto keys_end = util::make_iterator_first(std::end(range));
return util::make_range(keys_begin, keys_end);

View File

@@ -21,6 +21,7 @@
#include <string>
#include "Firestore/core/src/model/resource_path.h"
#include "Firestore/core/src/model/value_util.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
namespace firebase {
@@ -46,6 +47,7 @@ enum IndexType {
kReference = 37,
kGeopoint = 45,
kArray = 50,
kVector = 53,
kMap = 55,
kReferenceSegment = 60,
// A terminator that indicates that a truncatable value was not truncated.
@@ -105,6 +107,31 @@ void WriteIndexArray(const google_firestore_v1_ArrayValue& array_index_value,
}
}
void WriteIndexVector(const google_firestore_v1_MapValue& map_index_value,
DirectionalIndexByteEncoder* encoder) {
WriteValueTypeLabel(encoder, IndexType::kVector);
absl::optional<pb_size_t> valueIndex =
model::IndexOfKey(map_index_value, model::kRawVectorValueFieldKey,
model::kVectorValueFieldKey);
if (!valueIndex.has_value() ||
map_index_value.fields[valueIndex.value()].value.which_value_type !=
google_firestore_v1_Value_array_value_tag) {
return WriteIndexArray(model::MinArray().array_value, encoder);
}
auto value = map_index_value.fields[valueIndex.value()].value;
// Vectors sort first by length
WriteValueTypeLabel(encoder, IndexType::kNumber);
encoder->WriteLong(value.array_value.values_count);
// Vectors then sort by position value
WriteIndexString(model::kVectorValueFieldKey, encoder);
WriteIndexValueAux(value, encoder);
}
void WriteIndexMap(google_firestore_v1_MapValue map_index_value,
DirectionalIndexByteEncoder* encoder) {
WriteValueTypeLabel(encoder, IndexType::kMap);
@@ -183,6 +210,9 @@ void WriteIndexValueAux(const google_firestore_v1_Value& index_value,
if (model::IsMaxValue(index_value)) {
WriteValueTypeLabel(encoder, std::numeric_limits<int>::max());
break;
} else if (model::IsVectorValue(index_value)) {
WriteIndexVector(index_value.map_value, encoder);
break;
}
WriteIndexMap(index_value.map_value, encoder);
WriteTruncationMarker(encoder);

View File

@@ -0,0 +1,57 @@
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FIRESTORE_CORE_SRC_LOCAL_GLOBALS_CACHE_H_
#define FIRESTORE_CORE_SRC_LOCAL_GLOBALS_CACHE_H_
#include "Firestore/core/src/nanopb/byte_string.h"
using firebase::firestore::nanopb::ByteString;
namespace firebase {
namespace firestore {
namespace local {
/**
* General purpose cache for global values.
*
* Global state that cuts across components should be saved here. Following are
* contained herein:
*
* `sessionToken` tracks server interaction across Listen and Write streams.
* This facilitates cache synchronization and invalidation.
*/
class GlobalsCache {
public:
virtual ~GlobalsCache() = default;
/**
* Gets session token.
*/
virtual ByteString GetSessionToken() const = 0;
/**
* Sets session token.
*/
virtual void SetSessionToken(const ByteString& session_token) = 0;
};
} // namespace local
} // namespace firestore
} // namespace firebase
#endif // FIRESTORE_CORE_SRC_LOCAL_GLOBALS_CACHE_H_

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include "Firestore/core/src/local/leveldb_globals_cache.h"
#include "Firestore/core/src/local/leveldb_key.h"
#include "Firestore/core/src/local/leveldb_persistence.h"
namespace firebase {
namespace firestore {
namespace local {
namespace {
const char* kSessionToken = "session_token";
}
LevelDbGlobalsCache::LevelDbGlobalsCache(LevelDbPersistence* db)
: db_(NOT_NULL(db)) {
}
ByteString LevelDbGlobalsCache::GetSessionToken() const {
auto key = LevelDbGlobalKey::Key(kSessionToken);
std::string encoded;
auto done = db_->current_transaction()->Get(key, &encoded);
if (!done.ok()) {
return ByteString();
}
return ByteString(encoded);
}
void LevelDbGlobalsCache::SetSessionToken(const ByteString& session_token) {
auto key = LevelDbGlobalKey::Key(kSessionToken);
db_->current_transaction()->Put(key, session_token.ToString());
}
} // namespace local
} // namespace firestore
} // namespace firebase

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FIRESTORE_CORE_SRC_LOCAL_LEVELDB_GLOBALS_CACHE_H_
#define FIRESTORE_CORE_SRC_LOCAL_LEVELDB_GLOBALS_CACHE_H_
#include "Firestore/core/src/local/globals_cache.h"
namespace firebase {
namespace firestore {
namespace local {
class LevelDbPersistence;
class LevelDbGlobalsCache : public GlobalsCache {
public:
/** Creates a new bundle cache in the given LevelDB. */
explicit LevelDbGlobalsCache(LevelDbPersistence* db);
/**
* Gets session token.
*/
ByteString GetSessionToken() const override;
/**
* Sets session token.
*/
void SetSessionToken(const ByteString& session_token) override;
private:
// The LevelDbGlobalsCache is owned by LevelDbPersistence.
LevelDbPersistence* db_ = nullptr;
};
} // namespace local
} // namespace firestore
} // namespace firebase
#endif // FIRESTORE_CORE_SRC_LOCAL_LEVELDB_GLOBALS_CACHE_H_

View File

@@ -188,10 +188,15 @@ LevelDbIndexManager::LevelDbIndexManager(const User& user,
// The contract for this comparison expected by priority queue is
// `std::less`, but std::priority_queue's default order is descending.
// We change the order to be ascending by doing left >= right instead.
// Note: priority queue has to have a strict ordering, so here using unique_id
// to order Field Indexes having same `sequence_number` and `collection_group`
auto cmp = [](FieldIndex* left, FieldIndex* right) {
if (left->index_state().sequence_number() ==
right->index_state().sequence_number()) {
return left->collection_group() >= right->collection_group();
if (left->collection_group() == right->collection_group()) {
return left->unique_id() > right->unique_id();
}
return left->collection_group() > right->collection_group();
}
return left->index_state().sequence_number() >
right->index_state().sequence_number();

View File

@@ -39,6 +39,7 @@ namespace local {
namespace {
const char* kVersionGlobalTable = "version";
const char* kGlobalsTable = "globals";
const char* kMutationsTable = "mutation";
const char* kDocumentMutationsTable = "document_mutation";
const char* kMutationQueuesTable = "mutation_queue";
@@ -159,6 +160,11 @@ enum ComponentLabel {
*/
DataMigrationName = 25,
/**
* The name of a global.
*/
GlobalName = 26,
/**
* A path segment describes just a single segment in a resource path. Path
* segments that occur sequentially in a key represent successive segments in
@@ -245,6 +251,10 @@ class Reader {
return ReadLabeledString(ComponentLabel::BundleId);
}
std::string ReadGlobalName() {
return ReadLabeledString(ComponentLabel::GlobalName);
}
std::string ReadQueryName() {
return ReadLabeledString(ComponentLabel::QueryName);
}
@@ -718,6 +728,10 @@ class Writer {
WriteLabeledString(ComponentLabel::TableName, table_name);
}
void WriteGlobalName(absl::string_view global_name) {
WriteLabeledString(ComponentLabel::GlobalName, global_name);
}
void WriteBatchId(model::BatchId batch_id) {
WriteLabeledInt32(ComponentLabel::BatchId, batch_id);
}
@@ -1206,6 +1220,28 @@ bool LevelDbRemoteDocumentReadTimeKey::Decode(absl::string_view key) {
return reader.ok();
}
std::string LevelDbGlobalKey::KeyPrefix() {
Writer writer;
writer.WriteTableName(kGlobalsTable);
return writer.result();
}
std::string LevelDbGlobalKey::Key(absl::string_view global_name) {
Writer writer;
writer.WriteTableName(kGlobalsTable);
writer.WriteGlobalName(global_name);
writer.WriteTerminator();
return writer.result();
}
bool LevelDbGlobalKey::Decode(absl::string_view key) {
Reader reader{key};
reader.ReadTableNameMatching(kGlobalsTable);
global_name_ = reader.ReadGlobalName();
reader.ReadTerminator();
return reader.ok();
}
std::string LevelDbBundleKey::KeyPrefix() {
Writer writer;
writer.WriteTableName(kBundlesTable);

View File

@@ -768,6 +768,41 @@ class LevelDbNamedQueryKey {
std::string name_;
};
/**
* A key in the globals table, storing the name of the global value.
*/
class LevelDbGlobalKey {
public:
/**
* Creates a key prefix that points just before the first key of the table.
*/
static std::string KeyPrefix();
/**
* Creates a key that points to the key for the given name of global value.
*/
static std::string Key(absl::string_view global_name);
/**
* Decodes the given complete key, storing the decoded values in this
* instance.
*
* @return true if the key successfully decoded, false otherwise. If false is
* returned, this instance is in an undefined state until the next call to
* `Decode()`.
*/
ABSL_MUST_USE_RESULT
bool Decode(absl::string_view key);
/** The name that serves as identifier for global value for this entry. */
const std::string& global_name() const {
return global_name_;
}
private:
std::string global_name_;
};
/**
* A key in the index_configuration table, storing the index definition proto,
* and the collection (group) it applies to.

View File

@@ -126,6 +126,7 @@ LevelDbPersistence::LevelDbPersistence(std::unique_ptr<leveldb::DB> db,
reference_delegate_ =
absl::make_unique<LevelDbLruReferenceDelegate>(this, lru_params);
bundle_cache_ = absl::make_unique<LevelDbBundleCache>(this, &serializer_);
globals_cache_ = absl::make_unique<LevelDbGlobalsCache>(this);
// TODO(gsoltis): set up a leveldb transaction for these operations.
target_cache_->Start();
@@ -250,6 +251,10 @@ LevelDbTargetCache* LevelDbPersistence::target_cache() {
return target_cache_.get();
}
LevelDbGlobalsCache* LevelDbPersistence::globals_cache() {
return globals_cache_.get();
}
LevelDbRemoteDocumentCache* LevelDbPersistence::remote_document_cache() {
return document_cache_.get();
}

View File

@@ -25,6 +25,7 @@
#include "Firestore/core/src/credentials/user.h"
#include "Firestore/core/src/local/leveldb_bundle_cache.h"
#include "Firestore/core/src/local/leveldb_document_overlay_cache.h"
#include "Firestore/core/src/local/leveldb_globals_cache.h"
#include "Firestore/core/src/local/leveldb_index_manager.h"
#include "Firestore/core/src/local/leveldb_lru_reference_delegate.h"
#include "Firestore/core/src/local/leveldb_migrations.h"
@@ -84,6 +85,8 @@ class LevelDbPersistence : public Persistence {
LevelDbBundleCache* bundle_cache() override;
LevelDbGlobalsCache* globals_cache() override;
LevelDbDocumentOverlayCache* GetDocumentOverlayCache(
const credentials::User& user) override;
LevelDbOverlayMigrationManager* GetOverlayMigrationManager(
@@ -154,6 +157,7 @@ class LevelDbPersistence : public Persistence {
bool started_ = false;
std::unique_ptr<LevelDbBundleCache> bundle_cache_;
std::unique_ptr<LevelDbGlobalsCache> globals_cache_;
std::unordered_map<std::string, std::unique_ptr<LevelDbDocumentOverlayCache>>
document_overlay_caches_;
std::unordered_map<std::string,

View File

@@ -17,7 +17,7 @@
#include "Firestore/core/src/local/leveldb_remote_document_cache.h"
#include <string>
#include <thread> // NOLINT(build/c++11)
#include <thread>
#include <utility>
#include "Firestore/Protos/nanopb/firestore/local/maybe_document.nanopb.h"

View File

@@ -19,7 +19,7 @@
#include <memory>
#include <string>
#include <thread> // NOLINT(build/c++11)
#include <thread>
#include <vector>
#include "Firestore/core/src/core/query.h"

View File

@@ -585,7 +585,7 @@ LruResults LocalStore::CollectGarbage(LruGarbageCollector* garbage_collector) {
});
}
int LocalStore::Backfill() const {
size_t LocalStore::Backfill() const {
return persistence_->Run("Backfill Indexes", [&] {
return index_backfiller_->WriteIndexEntries(this);
});

View File

@@ -254,7 +254,7 @@ class LocalStore : public bundle::BundleCallback {
* Runs a single backfill operation and returns the number of documents
* processed.
*/
int Backfill() const;
size_t Backfill() const;
/**
* Returns whether the given bundle has already been loaded and its create

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/local/lru_garbage_collector.h"
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <queue>
#include <string>
#include <utility>

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Firestore/core/src/local/memory_globals_cache.h"
namespace firebase {
namespace firestore {
namespace local {
ByteString MemoryGlobalsCache::GetSessionToken() const {
return session_token_;
}
void MemoryGlobalsCache::SetSessionToken(const ByteString& session_token) {
session_token_ = session_token;
}
} // namespace local
} // namespace firestore
} // namespace firebase

View File

@@ -0,0 +1,49 @@
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FIRESTORE_CORE_SRC_LOCAL_MEMORY_GLOBALS_CACHE_H_
#define FIRESTORE_CORE_SRC_LOCAL_MEMORY_GLOBALS_CACHE_H_
#include <string>
#include "Firestore/core/src/local/globals_cache.h"
namespace firebase {
namespace firestore {
namespace local {
class MemoryGlobalsCache : public GlobalsCache {
public:
/**
* Gets session token.
*/
ByteString GetSessionToken() const override;
/**
* Sets session token.
*/
void SetSessionToken(const ByteString& session_token) override;
private:
ByteString session_token_;
};
} // namespace local
} // namespace firestore
} // namespace firebase
#endif // FIRESTORE_CORE_SRC_LOCAL_MEMORY_GLOBALS_CACHE_H_

View File

@@ -17,6 +17,7 @@
#ifndef FIRESTORE_CORE_SRC_LOCAL_MEMORY_MUTATION_QUEUE_H_
#define FIRESTORE_CORE_SRC_LOCAL_MEMORY_MUTATION_QUEUE_H_
#include <deque>
#include <set>
#include <vector>
@@ -59,7 +60,7 @@ class MemoryMutationQueue : public MutationQueue {
void RemoveMutationBatch(const model::MutationBatch& batch) override;
std::vector<model::MutationBatch> AllMutationBatches() override {
return queue_;
return std::vector<model::MutationBatch>(queue_.begin(), queue_.end());
}
std::vector<model::MutationBatch> AllMutationBatchesAffectingDocumentKeys(
@@ -128,7 +129,7 @@ class MemoryMutationQueue : public MutationQueue {
* Once the held write acknowledgements become visible they are removed from
* the head of the queue along with any tombstones that follow.
*/
std::vector<model::MutationBatch> queue_;
std::deque<model::MutationBatch> queue_;
/**
* The next value to use when assigning sequential IDs to each mutation

View File

@@ -102,6 +102,10 @@ MemoryBundleCache* MemoryPersistence::bundle_cache() {
return &bundle_cache_;
}
MemoryGlobalsCache* MemoryPersistence::globals_cache() {
return &globals_cache_;
}
MemoryDocumentOverlayCache* MemoryPersistence::GetDocumentOverlayCache(
const User& user) {
auto iter = document_overlay_caches_.find(user);

View File

@@ -27,6 +27,7 @@
#include "Firestore/core/src/credentials/user.h"
#include "Firestore/core/src/local/memory_bundle_cache.h"
#include "Firestore/core/src/local/memory_document_overlay_cache.h"
#include "Firestore/core/src/local/memory_globals_cache.h"
#include "Firestore/core/src/local/memory_index_manager.h"
#include "Firestore/core/src/local/memory_mutation_queue.h"
#include "Firestore/core/src/local/memory_remote_document_cache.h"
@@ -90,6 +91,8 @@ class MemoryPersistence : public Persistence {
MemoryBundleCache* bundle_cache() override;
MemoryGlobalsCache* globals_cache() override;
MemoryDocumentOverlayCache* GetDocumentOverlayCache(
const credentials::User& user) override;
@@ -138,6 +141,8 @@ class MemoryPersistence : public Persistence {
MemoryBundleCache bundle_cache_;
MemoryGlobalsCache globals_cache_;
DocumentOverlayCaches document_overlay_caches_;
MemoryOverlayMigrationManager overlay_migration_manager_;

View File

@@ -36,6 +36,7 @@ namespace local {
class BundleCache;
class DocumentOverlayCache;
class GlobalsCache;
class IndexManager;
class MutationQueue;
class OverlayMigrationManager;
@@ -86,6 +87,11 @@ class Persistence {
/** Releases any resources held during eager shutdown. */
virtual void Shutdown() = 0;
/**
* Returns GlobalCache representing a general purpose cache for global values.
*/
virtual GlobalsCache* globals_cache() = 0;
/**
* Returns a MutationQueue representing the persisted mutations for the given
* user.

View File

@@ -150,8 +150,18 @@ class BasePath {
std::equal(begin(), end(), potential_child.begin());
}
/**
* Compares the current path against another Path object. Paths are compared
* segment by segment, prioritizing numeric IDs (e.g., "__id123__") in numeric
* ascending order, followed by string segments in lexicographical order.
*/
util::ComparisonResult CompareTo(const T& rhs) const {
return util::CompareContainer(segments_, rhs.segments_);
size_t min_size = std::min(size(), rhs.size());
for (size_t i = 0; i < min_size; ++i) {
auto cmp = CompareSegments(segments_[i], rhs.segments_[i]);
if (!util::Same(cmp)) return cmp;
}
return util::Compare(size(), rhs.size());
}
friend bool operator==(const BasePath& lhs, const BasePath& rhs) {
@@ -174,6 +184,38 @@ class BasePath {
private:
SegmentsT segments_;
static const size_t kNumericIdPrefixLength = 4;
static const size_t kNumericIdSuffixLength = 2;
static const size_t kNumericIdTotalOverhead =
kNumericIdPrefixLength + kNumericIdSuffixLength;
static util::ComparisonResult CompareSegments(const std::string& lhs,
const std::string& rhs) {
bool isLhsNumeric = IsNumericId(lhs);
bool isRhsNumeric = IsNumericId(rhs);
if (isLhsNumeric && !isRhsNumeric) {
return util::ComparisonResult::Ascending;
} else if (!isLhsNumeric && isRhsNumeric) {
return util::ComparisonResult::Descending;
} else if (isLhsNumeric && isRhsNumeric) {
return util::Compare(ExtractNumericId(lhs), ExtractNumericId(rhs));
} else {
return util::Compare(lhs, rhs);
}
}
static bool IsNumericId(const std::string& segment) {
return segment.size() > kNumericIdTotalOverhead &&
segment.substr(0, kNumericIdPrefixLength) == "__id" &&
segment.substr(segment.size() - kNumericIdSuffixLength) == "__";
}
static int64_t ExtractNumericId(const std::string& segment) {
return std::stol(segment.substr(kNumericIdPrefixLength,
segment.size() - kNumericIdSuffixLength));
}
};
} // namespace impl

View File

@@ -20,6 +20,8 @@ namespace firebase {
namespace firestore {
namespace model {
std::atomic<int> FieldIndex::ref_count_{0};
util::ComparisonResult Segment::CompareTo(const Segment& rhs) const {
auto result = field_path().CompareTo(rhs.field_path());
if (result != util::ComparisonResult::Same) {

View File

@@ -243,7 +243,9 @@ class FieldIndex {
static util::ComparisonResult SemanticCompare(const FieldIndex& left,
const FieldIndex& right);
FieldIndex() : index_id_(UnknownId()) {
FieldIndex()
: index_id_(UnknownId()),
unique_id_(ref_count_.fetch_add(1, std::memory_order_acq_rel)) {
}
FieldIndex(int32_t index_id,
@@ -253,7 +255,50 @@ class FieldIndex {
: index_id_(index_id),
collection_group_(std::move(collection_group)),
segments_(std::move(segments)),
state_(std::move(state)) {
state_(std::move(state)),
unique_id_(ref_count_.fetch_add(1, std::memory_order_acq_rel)) {
}
// Copy constructor
FieldIndex(const FieldIndex& other)
: index_id_(other.index_id_),
collection_group_(other.collection_group_),
segments_(other.segments_),
state_(other.state_),
unique_id_(ref_count_.fetch_add(1, std::memory_order_acq_rel)) {
}
// Copy assignment operator
FieldIndex& operator=(const FieldIndex& other) {
if (this != &other) {
index_id_ = other.index_id_;
collection_group_ = other.collection_group_;
segments_ = other.segments_;
state_ = other.state_;
unique_id_ = ref_count_.fetch_add(1, std::memory_order_acq_rel);
}
return *this;
}
// Move constructor
FieldIndex(FieldIndex&& other) noexcept
: index_id_(other.index_id_),
collection_group_(std::move(other.collection_group_)),
segments_(std::move(other.segments_)),
state_(std::move(other.state_)),
unique_id_(ref_count_.fetch_add(1, std::memory_order_acq_rel)) {
}
// Move assignment operator
FieldIndex& operator=(FieldIndex&& other) noexcept {
if (this != &other) {
index_id_ = other.index_id_;
collection_group_ = std::move(other.collection_group_);
segments_ = std::move(other.segments_);
state_ = std::move(other.state_);
unique_id_ = ref_count_.fetch_add(1, std::memory_order_acq_rel);
}
return *this;
}
/**
@@ -285,6 +330,14 @@ class FieldIndex {
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
absl::optional<Segment> GetArraySegment() const;
/**
* Returns the unique identifier for this object, ensuring a strict ordering
* in the priority queue's comparison function.
*/
int unique_id() const {
return unique_id_;
}
/**
* A type that can be used as the "Compare" template parameter of ordered
* collections to have the elements ordered using
@@ -308,6 +361,10 @@ class FieldIndex {
std::string collection_group_;
std::vector<Segment> segments_;
IndexState state_;
int unique_id_;
// TODO(C++17): Replace with inline static std::atomic<int> ref_count_ = 0;
static std::atomic<int> ref_count_;
};
inline bool operator==(const FieldIndex& lhs, const FieldIndex& rhs) {

View File

@@ -38,26 +38,33 @@
namespace firebase {
namespace firestore {
namespace model {
namespace {
using nanopb::Message;
using util::ComparisonResult;
/** The smallest reference value. */
pb_bytes_array_s* kMinimumReferenceValue =
nanopb::MakeBytesArray("projects//databases//documents/");
/** The field type of a maximum proto value. */
const char* kRawMaxValueFieldKey = "__type__";
pb_bytes_array_s* kMaxValueFieldKey =
nanopb::MakeBytesArray(kRawMaxValueFieldKey);
/** The field type of a special object type. */
const char* kRawTypeValueFieldKey = "__type__";
pb_bytes_array_s* kTypeValueFieldKey =
nanopb::MakeBytesArray(kRawTypeValueFieldKey);
/** The field value of a maximum proto value. */
const char* kRawMaxValueFieldValue = "__max__";
pb_bytes_array_s* kMaxValueFieldValue =
nanopb::MakeBytesArray(kRawMaxValueFieldValue);
} // namespace
/** The type of a VectorValue proto. */
const char* kRawVectorTypeFieldValue = "__vector__";
pb_bytes_array_s* kVectorTypeFieldValue =
nanopb::MakeBytesArray(kRawVectorTypeFieldValue);
using nanopb::Message;
using util::ComparisonResult;
/** The value key of a VectorValue proto. */
const char* kRawVectorValueFieldKey = "value";
pb_bytes_array_s* kVectorValueFieldKey =
nanopb::MakeBytesArray(kRawVectorValueFieldKey);
TypeOrder GetTypeOrder(const google_firestore_v1_Value& value) {
switch (value.which_value_type) {
@@ -94,6 +101,8 @@ TypeOrder GetTypeOrder(const google_firestore_v1_Value& value) {
return TypeOrder::kServerTimestamp;
} else if (IsMaxValue(value)) {
return TypeOrder::kMaxValue;
} else if (IsVectorValue(value)) {
return TypeOrder::kVector;
}
return TypeOrder::kMap;
}
@@ -253,6 +262,43 @@ ComparisonResult CompareMaps(const google_firestore_v1_MapValue& left,
return util::Compare(left_map->fields_count, right_map->fields_count);
}
ComparisonResult CompareVectors(const google_firestore_v1_Value& left,
const google_firestore_v1_Value& right) {
HARD_ASSERT(IsVectorValue(left) && IsVectorValue(right),
"Cannot compare non-vector values as vectors.");
absl::optional<pb_size_t> leftIndex =
IndexOfKey(left.map_value, kRawVectorValueFieldKey, kVectorValueFieldKey);
absl::optional<pb_size_t> rightIndex = IndexOfKey(
right.map_value, kRawVectorValueFieldKey, kVectorValueFieldKey);
pb_size_t leftArrayLength = 0;
google_firestore_v1_Value leftArray;
if (leftIndex.has_value()) {
leftArray = left.map_value.fields[leftIndex.value()].value;
leftArrayLength = leftArray.array_value.values_count;
}
pb_size_t rightArrayLength = 0;
google_firestore_v1_Value rightArray;
if (leftIndex.has_value()) {
rightArray = right.map_value.fields[rightIndex.value()].value;
rightArrayLength = rightArray.array_value.values_count;
}
if (leftArrayLength == 0 && rightArrayLength == 0) {
return ComparisonResult::Same;
}
ComparisonResult lengthCompare =
util::Compare(leftArrayLength, rightArrayLength);
if (lengthCompare != ComparisonResult::Same) {
return lengthCompare;
}
return CompareArrays(leftArray, rightArray);
}
ComparisonResult Compare(const google_firestore_v1_Value& left,
const google_firestore_v1_Value& right) {
TypeOrder left_type = GetTypeOrder(left);
@@ -297,6 +343,9 @@ ComparisonResult Compare(const google_firestore_v1_Value& left,
case TypeOrder::kMap:
return CompareMaps(left.map_value, right.map_value);
case TypeOrder::kVector:
return CompareVectors(left, right);
case TypeOrder::kMaxValue:
return util::ComparisonResult::Same;
@@ -425,6 +474,7 @@ bool Equals(const google_firestore_v1_Value& lhs,
case TypeOrder::kArray:
return ArrayEquals(lhs.array_value, rhs.array_value);
case TypeOrder::kVector:
case TypeOrder::kMap:
return MapValueEquals(lhs.map_value, rhs.map_value);
@@ -539,106 +589,87 @@ std::string CanonicalId(const google_firestore_v1_ArrayValue& value) {
return CanonifyArray(value);
}
google_firestore_v1_Value GetLowerBound(pb_size_t value_tag) {
switch (value_tag) {
google_firestore_v1_Value GetLowerBound(
const google_firestore_v1_Value& value) {
switch (value.which_value_type) {
case google_firestore_v1_Value_null_value_tag:
return NullValue();
case google_firestore_v1_Value_boolean_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.boolean_value = false;
return value;
return MinBoolean();
}
case google_firestore_v1_Value_integer_value_tag:
case google_firestore_v1_Value_double_value_tag: {
return NaNValue();
return MinNumber();
}
case google_firestore_v1_Value_timestamp_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.timestamp_value.seconds = std::numeric_limits<int64_t>::min();
value.timestamp_value.nanos = 0;
return value;
return MinTimestamp();
}
case google_firestore_v1_Value_string_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.string_value = nullptr;
return value;
return MinString();
}
case google_firestore_v1_Value_bytes_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.bytes_value = nullptr;
return value;
return MinBytes();
}
case google_firestore_v1_Value_reference_value_tag: {
google_firestore_v1_Value result;
result.which_value_type = google_firestore_v1_Value_reference_value_tag;
result.reference_value = kMinimumReferenceValue;
return result;
return MinReference();
}
case google_firestore_v1_Value_geo_point_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.geo_point_value.latitude = -90.0;
value.geo_point_value.longitude = -180.0;
return value;
return MinGeoPoint();
}
case google_firestore_v1_Value_array_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.array_value.values = nullptr;
value.array_value.values_count = 0;
return value;
return MinArray();
}
case google_firestore_v1_Value_map_value_tag: {
google_firestore_v1_Value value;
value.which_value_type = value_tag;
value.map_value.fields = nullptr;
value.map_value.fields_count = 0;
return value;
if (IsVectorValue(value)) {
return MinVector();
}
return MinMap();
}
default:
HARD_FAIL("Invalid type value: %s", value_tag);
HARD_FAIL("Invalid type value: %s", value.which_value_type);
}
}
google_firestore_v1_Value GetUpperBound(pb_size_t value_tag) {
switch (value_tag) {
google_firestore_v1_Value GetUpperBound(
const google_firestore_v1_Value& value) {
switch (value.which_value_type) {
case google_firestore_v1_Value_null_value_tag:
return GetLowerBound(google_protobuf_BoolValue_value_tag);
return MinBoolean();
case google_firestore_v1_Value_boolean_value_tag:
return GetLowerBound(google_firestore_v1_Value_integer_value_tag);
return MinNumber();
case google_firestore_v1_Value_integer_value_tag:
case google_firestore_v1_Value_double_value_tag:
return GetLowerBound(google_firestore_v1_Value_timestamp_value_tag);
return MinTimestamp();
case google_firestore_v1_Value_timestamp_value_tag:
return GetLowerBound(google_firestore_v1_Value_string_value_tag);
return MinString();
case google_firestore_v1_Value_string_value_tag:
return GetLowerBound(google_firestore_v1_Value_bytes_value_tag);
return MinBytes();
case google_firestore_v1_Value_bytes_value_tag:
return GetLowerBound(google_firestore_v1_Value_reference_value_tag);
return MinReference();
case google_firestore_v1_Value_reference_value_tag:
return GetLowerBound(google_firestore_v1_Value_geo_point_value_tag);
return MinGeoPoint();
case google_firestore_v1_Value_geo_point_value_tag:
return GetLowerBound(google_firestore_v1_Value_array_value_tag);
return MinArray();
case google_firestore_v1_Value_array_value_tag:
return GetLowerBound(google_firestore_v1_Value_map_value_tag);
return MinVector();
case google_firestore_v1_Value_map_value_tag:
if (IsVectorValue(value)) {
return MinMap();
}
return MaxValue();
default:
HARD_FAIL("Invalid type value: %s", value_tag);
HARD_FAIL("Invalid type value: %s", value.which_value_type);
}
}
@@ -693,7 +724,7 @@ google_firestore_v1_Value MaxValue() {
"google_firestore_v1_MapValue_FieldsEntry should be "
"trivially-destructible; otherwise, it should use NoDestructor below.");
static google_firestore_v1_MapValue_FieldsEntry field_entry;
field_entry.key = kMaxValueFieldKey;
field_entry.key = kTypeValueFieldKey;
field_entry.value = value;
google_firestore_v1_MapValue map_value;
@@ -718,9 +749,9 @@ bool IsMaxValue(const google_firestore_v1_Value& value) {
// Comparing the pointer address, then actual content if addresses are
// different.
if (value.map_value.fields[0].key != kMaxValueFieldKey &&
if (value.map_value.fields[0].key != kTypeValueFieldKey &&
nanopb::MakeStringView(value.map_value.fields[0].key) !=
kRawMaxValueFieldKey) {
kRawTypeValueFieldKey) {
return false;
}
@@ -736,6 +767,65 @@ bool IsMaxValue(const google_firestore_v1_Value& value) {
kRawMaxValueFieldValue;
}
absl::optional<pb_size_t> IndexOfKey(
const google_firestore_v1_MapValue& mapValue,
const char* kRawTypeValueFieldKey,
pb_bytes_array_s* kTypeValueFieldKey) {
for (pb_size_t i = 0; i < mapValue.fields_count; i++) {
if (mapValue.fields[i].key == kTypeValueFieldKey ||
nanopb::MakeStringView(mapValue.fields[i].key) ==
kRawTypeValueFieldKey) {
return i;
}
}
return absl::nullopt;
}
bool IsVectorValue(const google_firestore_v1_Value& value) {
if (value.which_value_type != google_firestore_v1_Value_map_value_tag) {
return false;
}
if (value.map_value.fields_count < 2) {
return false;
}
absl::optional<pb_size_t> typeFieldIndex =
IndexOfKey(value.map_value, kRawTypeValueFieldKey, kTypeValueFieldKey);
if (!typeFieldIndex.has_value()) {
return false;
}
if (value.map_value.fields[typeFieldIndex.value()].value.which_value_type !=
google_firestore_v1_Value_string_value_tag) {
return false;
}
// Comparing the pointer address, then actual content if addresses are
// different.
if (value.map_value.fields[typeFieldIndex.value()].value.string_value !=
kVectorTypeFieldValue &&
nanopb::MakeStringView(
value.map_value.fields[typeFieldIndex.value()].value.string_value) !=
kRawVectorTypeFieldValue) {
return false;
}
absl::optional<pb_size_t> valueFieldIndex = IndexOfKey(
value.map_value, kRawVectorValueFieldKey, kVectorValueFieldKey);
if (!valueFieldIndex.has_value()) {
return false;
}
if (value.map_value.fields[valueFieldIndex.value()].value.which_value_type !=
google_firestore_v1_Value_array_value_tag) {
return false;
}
return true;
}
google_firestore_v1_Value NaNValue() {
google_firestore_v1_Value nan_value;
nan_value.which_value_type = google_firestore_v1_Value_double_value_tag;
@@ -748,6 +838,98 @@ bool IsNaNValue(const google_firestore_v1_Value& value) {
std::isnan(value.double_value);
}
google_firestore_v1_Value MinBoolean() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_boolean_value_tag;
lowerBound.boolean_value = false;
return lowerBound;
}
google_firestore_v1_Value MinNumber() {
return NaNValue();
}
google_firestore_v1_Value MinTimestamp() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_timestamp_value_tag;
lowerBound.timestamp_value.seconds = std::numeric_limits<int64_t>::min();
lowerBound.timestamp_value.nanos = 0;
return lowerBound;
}
google_firestore_v1_Value MinString() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_string_value_tag;
lowerBound.string_value = nullptr;
return lowerBound;
}
google_firestore_v1_Value MinBytes() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_bytes_value_tag;
lowerBound.bytes_value = nullptr;
return lowerBound;
}
google_firestore_v1_Value MinReference() {
google_firestore_v1_Value result;
result.which_value_type = google_firestore_v1_Value_reference_value_tag;
result.reference_value = kMinimumReferenceValue;
return result;
}
google_firestore_v1_Value MinGeoPoint() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_geo_point_value_tag;
lowerBound.geo_point_value.latitude = -90.0;
lowerBound.geo_point_value.longitude = -180.0;
return lowerBound;
}
google_firestore_v1_Value MinArray() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_array_value_tag;
lowerBound.array_value.values = nullptr;
lowerBound.array_value.values_count = 0;
return lowerBound;
}
google_firestore_v1_Value MinVector() {
google_firestore_v1_Value typeValue;
typeValue.which_value_type = google_firestore_v1_Value_string_value_tag;
typeValue.string_value = kVectorTypeFieldValue;
google_firestore_v1_MapValue_FieldsEntry* field_entries =
nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(2);
field_entries[0].key = kTypeValueFieldKey;
field_entries[0].value = typeValue;
google_firestore_v1_Value arrayValue;
arrayValue.which_value_type = google_firestore_v1_Value_array_value_tag;
arrayValue.array_value.values = nullptr;
arrayValue.array_value.values_count = 0;
field_entries[1].key = kVectorValueFieldKey;
field_entries[1].value = arrayValue;
google_firestore_v1_MapValue map_value;
map_value.fields_count = 2;
map_value.fields = field_entries;
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_map_value_tag;
lowerBound.map_value = map_value;
return lowerBound;
}
google_firestore_v1_Value MinMap() {
google_firestore_v1_Value lowerBound;
lowerBound.which_value_type = google_firestore_v1_Value_map_value_tag;
lowerBound.map_value.fields = nullptr;
lowerBound.map_value.fields_count = 0;
return lowerBound;
}
Message<google_firestore_v1_Value> RefValue(
const model::DatabaseId& database_id,
const model::DocumentKey& document_key) {

View File

@@ -23,6 +23,7 @@
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
#include "Firestore/core/src/nanopb/message.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
#include "absl/types/optional.h"
namespace firebase {
@@ -37,6 +38,25 @@ namespace model {
class DocumentKey;
class DatabaseId;
/** The smallest reference value. */
extern pb_bytes_array_s* kMinimumReferenceValue;
/** The field type of a special object type. */
extern const char* kRawTypeValueFieldKey;
extern pb_bytes_array_s* kTypeValueFieldKey;
/** The field value of a maximum proto value. */
extern const char* kRawMaxValueFieldValue;
extern pb_bytes_array_s* kMaxValueFieldValue;
/** The type of a VectorValue proto. */
extern const char* kRawVectorTypeFieldValue;
extern pb_bytes_array_s* kVectorTypeFieldValue;
/** The value key of a VectorValue proto. */
extern const char* kRawVectorValueFieldKey;
extern pb_bytes_array_s* kVectorValueFieldKey;
/**
* The order of types in Firestore. This order is based on the backend's
* ordering, but modified to support server timestamps.
@@ -52,8 +72,9 @@ enum class TypeOrder {
kReference = 7,
kGeoPoint = 8,
kArray = 9,
kMap = 10,
kMaxValue = 11
kVector = 10,
kMap = 11,
kMaxValue = 12
};
/** Returns the backend's type order of the given Value type. */
@@ -94,7 +115,7 @@ std::string CanonicalId(const google_firestore_v1_Value& value);
* The returned value might point to heap allocated memory that is owned by
* this function. To take ownership of this memory, call `DeepClone`.
*/
google_firestore_v1_Value GetLowerBound(pb_size_t value_tag);
google_firestore_v1_Value GetLowerBound(const google_firestore_v1_Value& value);
/**
* Returns the largest value for the given value type (exclusive).
@@ -102,7 +123,7 @@ google_firestore_v1_Value GetLowerBound(pb_size_t value_tag);
* The returned value might point to heap allocated memory that is owned by
* this function. To take ownership of this memory, call `DeepClone`.
*/
google_firestore_v1_Value GetUpperBound(pb_size_t value_tag);
google_firestore_v1_Value GetUpperBound(const google_firestore_v1_Value& value);
/**
* Generates the canonical ID for the provided array value (as used in Target
@@ -155,6 +176,22 @@ google_firestore_v1_Value MaxValue();
*/
bool IsMaxValue(const google_firestore_v1_Value& value);
/**
* Returns `true` if `value` represents a VectorValue..
*/
bool IsVectorValue(const google_firestore_v1_Value& value);
/**
* Returns the index of the specified key (`kRawTypeValueFieldKey`) in the
* map (`mapValue`). `kTypeValueFieldKey` is an alternative representation
* of the key specified in `kRawTypeValueFieldKey`.
* If the key is not found, then `absl::nullopt` is returned.
*/
absl::optional<pb_size_t> IndexOfKey(
const google_firestore_v1_MapValue& mapValue,
const char* kRawTypeValueFieldKey,
pb_bytes_array_s* kTypeValueFieldKey);
/**
* Returns `NaN` in its Protobuf representation.
*
@@ -166,6 +203,26 @@ google_firestore_v1_Value NaNValue();
/** Returns `true` if `value` is `NaN` in its Protobuf representation. */
bool IsNaNValue(const google_firestore_v1_Value& value);
google_firestore_v1_Value MinBoolean();
google_firestore_v1_Value MinNumber();
google_firestore_v1_Value MinTimestamp();
google_firestore_v1_Value MinString();
google_firestore_v1_Value MinBytes();
google_firestore_v1_Value MinReference();
google_firestore_v1_Value MinGeoPoint();
google_firestore_v1_Value MinArray();
google_firestore_v1_Value MinVector();
google_firestore_v1_Value MinMap();
/**
* Returns a Protobuf reference value representing the given location.
*

View File

@@ -17,7 +17,7 @@
#include "Firestore/core/src/nanopb/byte_string.h"
#include <cctype>
#include <cstdlib>
#include <cstdlib> // NOLINT(build/include_order)
#include <cstring>
#include <iomanip>
#include <ostream>

View File

@@ -179,6 +179,7 @@ inline NSData* _Nonnull MakeNSData(const ByteString& str) {
}
inline NSData* _Nonnull MakeNSData(const pb_bytes_array_t* _Nullable data) {
if (data == nil) return [[NSData alloc] init];
return [[NSData alloc] initWithBytes:data->bytes length:data->size];
}

View File

@@ -18,8 +18,7 @@
#if defined(__APPLE__)
#if TARGET_OS_IOS || TARGET_OS_TV || \
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
#import <UIKit/UIKit.h>
#endif
@@ -50,7 +49,7 @@ NetworkStatus ToNetworkStatus(SCNetworkReachabilityFlags flags) {
return NetworkStatus::Unavailable;
}
#if TARGET_OS_IPHONE || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IPHONE || TARGET_OS_VISION
if (flags & kSCNetworkReachabilityFlagsIsWWAN) {
return NetworkStatus::AvailableViaCellular;
}
@@ -113,8 +112,7 @@ class ConnectivityMonitorApple : public ConnectivityMonitor {
return;
}
#if TARGET_OS_IOS || TARGET_OS_TV || \
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
this->observer_ = [[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationWillEnterForegroundNotification
object:nil
@@ -126,8 +124,7 @@ class ConnectivityMonitorApple : public ConnectivityMonitor {
}
~ConnectivityMonitorApple() {
#if TARGET_OS_IOS || TARGET_OS_TV || \
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
[[NSNotificationCenter defaultCenter] removeObserver:this->observer_];
#endif
@@ -142,8 +139,7 @@ class ConnectivityMonitorApple : public ConnectivityMonitor {
}
}
#if TARGET_OS_IOS || TARGET_OS_TV || \
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
void OnEnteredForeground() {
SCNetworkReachabilityFlags flags{};
if (!SCNetworkReachabilityGetFlags(reachability_, &flags)) return;
@@ -171,8 +167,7 @@ class ConnectivityMonitorApple : public ConnectivityMonitor {
private:
SCNetworkReachabilityRef reachability_ = nil;
#if TARGET_OS_IOS || TARGET_OS_TV || \
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
id<NSObject> observer_ = nil;
#endif
};

View File

@@ -17,7 +17,7 @@
#ifndef FIRESTORE_CORE_SRC_REMOTE_EXPONENTIAL_BACKOFF_H_
#define FIRESTORE_CORE_SRC_REMOTE_EXPONENTIAL_BACKOFF_H_
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <memory>
#include "Firestore/core/src/util/async_queue.h"

View File

@@ -18,7 +18,7 @@
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRHeartbeatLogger.h"
#import "FirebaseCore/Extension/FIROptionsInternal.h"
#import "FirebaseCore/Sources/FIROptionsInternal.h"
#include "Firestore/core/src/util/string_apple.h"

View File

@@ -17,9 +17,9 @@
#ifndef FIRESTORE_CORE_SRC_REMOTE_GRPC_COMPLETION_H_
#define FIRESTORE_CORE_SRC_REMOTE_GRPC_COMPLETION_H_
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <functional>
#include <future> // NOLINT(build/c++11)
#include <future>
#include <memory>
#include <utility>

View File

@@ -19,7 +19,7 @@
#include <cstdlib>
#include <algorithm>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include <utility>

View File

@@ -16,8 +16,8 @@
#include "Firestore/core/src/remote/grpc_stream.h"
#include <chrono> // NOLINT(build/c++11)
#include <future> // NOLINT(build/c++11)
#include <chrono>
#include <future>
#include "Firestore/core/src/remote/grpc_connection.h"
#include "Firestore/core/src/remote/grpc_util.h"
@@ -98,7 +98,7 @@ GrpcStream::GrpcStream(
}
GrpcStream::~GrpcStream() {
LOG_DEBUG("GrpcStream('%s'): destroying stream", this);
LOG_DEBUG("GrpcStream('%x'): destroying stream", this);
HARD_ASSERT(completions_.empty(),
"GrpcStream is being destroyed without proper shutdown");
MaybeUnregister();
@@ -160,14 +160,14 @@ void GrpcStream::MaybeWrite(absl::optional<BufferedWrite> maybe_write) {
}
void GrpcStream::FinishImmediately() {
LOG_DEBUG("GrpcStream('%s'): finishing without notifying observers", this);
LOG_DEBUG("GrpcStream('%x'): finishing without notifying observers", this);
Shutdown();
UnsetObserver();
}
void GrpcStream::FinishAndNotify(const Status& status) {
LOG_DEBUG("GrpcStream('%s'): finishing and notifying observers", this);
LOG_DEBUG("GrpcStream('%x'): finishing and notifying observers", this);
Shutdown();
@@ -181,7 +181,7 @@ void GrpcStream::FinishAndNotify(const Status& status) {
}
void GrpcStream::Shutdown() {
LOG_DEBUG("GrpcStream('%s'): shutting down; completions: %s, is finished: %s",
LOG_DEBUG("GrpcStream('%x'): shutting down; completions: %s, is finished: %s",
this, completions_.size(), is_grpc_call_finished_);
MaybeUnregister();
@@ -216,7 +216,7 @@ void GrpcStream::MaybeUnregister() {
}
void GrpcStream::FinishGrpcCall(const OnSuccess& callback) {
LOG_DEBUG("GrpcStream('%s'): finishing the underlying call", this);
LOG_DEBUG("GrpcStream('%x'): finishing the underlying call", this);
HARD_ASSERT(!is_grpc_call_finished_, "FinishGrpcCall called twice");
is_grpc_call_finished_ = true;
@@ -229,7 +229,7 @@ void GrpcStream::FinishGrpcCall(const OnSuccess& callback) {
}
void GrpcStream::FastFinishCompletionsBlocking() {
LOG_DEBUG("GrpcStream('%s'): fast finishing %s completion(s)", this,
LOG_DEBUG("GrpcStream('%x'): fast finishing %s completion(s)", this,
completions_.size());
// TODO(varconst): reset buffered_writer_? Should not be necessary, because it
@@ -344,7 +344,7 @@ std::shared_ptr<GrpcCompletion> GrpcStream::NewCompletion(
} else {
// Use the same error-handling for all operations; all errors are
// unrecoverable.
LOG_DEBUG("GrpcStream('%s'): operation of type %s failed", this,
LOG_DEBUG("GrpcStream('%x'): operation of type %s failed", this,
completion->type());
OnOperationFailed();
}

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/remote/online_state_tracker.h"
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include "Firestore/core/src/util/executor.h"
#include "Firestore/core/src/util/hard_assert.h"

View File

@@ -85,14 +85,14 @@ void RemoteStore::Start() {
[this](ConnectivityMonitor::NetworkStatus network_status) {
if (network_status == ConnectivityMonitor::NetworkStatus::Unavailable) {
LOG_DEBUG(
"RemoteStore %s ignoring connectivity callback for unavailable "
"RemoteStore %x ignoring connectivity callback for unavailable "
"network",
this);
return;
}
if (CanUseNetwork()) {
LOG_DEBUG("RemoteStore %s restarting streams as connectivity changed",
LOG_DEBUG("RemoteStore %x restarting streams as connectivity changed",
this);
RestartNetwork();
}
@@ -139,7 +139,7 @@ void RemoteStore::DisableNetworkInternal() {
}
void RemoteStore::Shutdown() {
LOG_DEBUG("RemoteStore %s shutting down", this);
LOG_DEBUG("RemoteStore %x shutting down", this);
is_network_enabled_ = false;
DisableNetworkInternal();
@@ -514,7 +514,7 @@ void RemoteStore::HandleHandshakeError(const Status& status) {
if (Datastore::IsPermanentError(status)) {
std::string token = util::ToString(write_stream_->last_stream_token());
LOG_DEBUG(
"RemoteStore %s error before completed handshake; resetting "
"RemoteStore %x error before completed handshake; resetting "
"stream token %s: "
"error code: '%s', details: '%s'",
this, token, status.code(), status.error_message());
@@ -590,7 +590,7 @@ void RemoteStore::HandleCredentialChange() {
// Tear down and re-create our network streams. This will ensure we get a
// fresh auth token for the new user and re-fill the write pipeline with new
// mutations from the `LocalStore` (since mutations are per-user).
LOG_DEBUG("RemoteStore %s restarting streams for new credential", this);
LOG_DEBUG("RemoteStore %x restarting streams for new credential", this);
RestartNetwork();
}
}

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/remote/stream.h"
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <utility>
#include "Firestore/core/include/firebase/firestore/firestore_errors.h"
@@ -380,7 +380,7 @@ void Stream::Write(grpc::ByteBuffer&& message) {
std::string Stream::GetDebugDescription() const {
EnsureOnQueue();
return StringFormat("%s (%s)", GetDebugName(), this);
return StringFormat("%s (%x)", GetDebugName(), this);
}
} // namespace remote

View File

@@ -56,7 +56,7 @@ class WatchStreamCallback {
/**
* Called by the `WatchStream` with changes and the snapshot versions
* included in in the `WatchChange` responses sent back by the server.
* included in the `WatchChange` responses sent back by the server.
*/
virtual void OnWatchStreamChange(
const WatchChange& change,

View File

@@ -20,8 +20,6 @@
#if defined(__APPLE__)
#import <CoreFoundation/CoreFoundation.h>
#elif defined(_STLPORT_VERSION)
#include <ctime>
#endif
#include "Firestore/core/src/util/hard_assert.h"
@@ -76,29 +74,16 @@ Timestamp Timestamp::Now() {
auto nanos = static_cast<int32_t>(fraction * kNanosPerSecond);
return MakeNormalizedTimestamp(seconds, nanos);
#elif !defined(_STLPORT_VERSION)
// Use the standard <chrono> library from C++11 if possible.
return FromTimePoint(std::chrono::system_clock::now());
#else
// If <chrono> is unavailable, use clock_gettime from POSIX, which supports
// up to nanosecond resolution. Note that it's a non-standard function
// contained in <time.h>.
//
// Note: it's possible to check for availability of POSIX clock_gettime using
// macros (see "Availability" at https://linux.die.net/man/3/clock_gettime).
// However, the only platform where <chrono> isn't available is Android with
// STLPort standard library, where clock_gettime is known to be available.
timespec now;
clock_gettime(CLOCK_REALTIME, &now);
return MakeNormalizedTimestamp(now.tv_sec, now.tv_nsec);
#endif // !defined(_STLPORT_VERSION)
// Use the standard <chrono> library from C++11.
return FromTimePoint(std::chrono::system_clock::now());
#endif // defined(__APPLE__)
}
Timestamp Timestamp::FromTimeT(const time_t seconds_since_unix_epoch) {
return {seconds_since_unix_epoch, 0};
}
#if !defined(_STLPORT_VERSION)
Timestamp Timestamp::FromTimePoint(
const std::chrono::time_point<std::chrono::system_clock> time_point) {
namespace chr = std::chrono;
@@ -111,8 +96,6 @@ Timestamp Timestamp::FromTimePoint(
return result;
}
#endif // !defined(_STLPORT_VERSION)
std::string Timestamp::ToString() const {
return absl::StrCat("Timestamp(seconds=", seconds_,
", nanoseconds=", nanoseconds_, ")");

View File

@@ -18,10 +18,10 @@
#define FIRESTORE_CORE_SRC_UTIL_ASYNC_QUEUE_H_
#include <atomic>
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <functional>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <vector>
#include "Firestore/core/src/util/executor.h"

View File

@@ -17,9 +17,9 @@
#ifndef FIRESTORE_CORE_SRC_UTIL_BACKGROUND_QUEUE_H_
#define FIRESTORE_CORE_SRC_UTIL_BACKGROUND_QUEUE_H_
#include <condition_variable> // NOLINT(build/c++11)
#include <condition_variable>
#include <functional>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
namespace firebase {
namespace firestore {

View File

@@ -17,7 +17,7 @@
#ifndef FIRESTORE_CORE_SRC_UTIL_EXECUTOR_H_
#define FIRESTORE_CORE_SRC_UTIL_EXECUTOR_H_
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <functional>
#include <memory>
#include <string>
@@ -84,7 +84,7 @@ class Executor {
// object prevents any tasks from running that could observe a partially
// destroyed object graph.
//
// Requirements for implementors:
// Requirements for implementers:
// * Dispose implementations must be idempotent.
// * Dispose implementations must exclude concurrent execution of other
// methods.

View File

@@ -19,11 +19,11 @@
#include <dispatch/dispatch.h>
#include <chrono> // NOLINT(build/c++11)
#include <condition_variable> // NOLINT(build/c++11)
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/util/executor_std.h"
#include <future> // NOLINT(build/c++11)
#include <future>
#include <memory>
#include <sstream>

View File

@@ -19,12 +19,12 @@
#include <algorithm>
#include <atomic>
#include <condition_variable> // NOLINT(build/c++11)
#include <condition_variable>
#include <deque>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include <thread> // NOLINT(build/c++11)
#include <thread>
#include <utility>
#include <vector>

View File

@@ -45,8 +45,7 @@ Status Filesystem::ExcludeFromBackups(const Path& dir) {
}
StatusOr<Path> Filesystem::AppDataDir(absl::string_view app_name) {
#if TARGET_OS_IOS || TARGET_OS_OSX || \
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_OSX || TARGET_OS_VISION
NSArray<NSString*>* directories = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, YES);
return Path::FromNSString(directories[0]).AppendUtf8(app_name);
@@ -62,7 +61,7 @@ StatusOr<Path> Filesystem::AppDataDir(absl::string_view app_name) {
}
StatusOr<Path> Filesystem::LegacyDocumentsDir(absl::string_view app_name) {
#if TARGET_OS_IOS || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
#if TARGET_OS_IOS || TARGET_OS_VISION
NSArray<NSString*>* directories = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
return Path::FromNSString(directories[0]).AppendUtf8(app_name);

View File

@@ -132,7 +132,7 @@ Path Filesystem::TempDir() {
#if !__APPLE__
Status Filesystem::IsDirectory(const Path& path) {
struct stat buffer {};
struct stat buffer{};
if (::stat(path.c_str(), &buffer)) {
if (errno == ENOENT) {
// Expected common error case.
@@ -168,7 +168,7 @@ Status Filesystem::IsDirectory(const Path& path) {
}
StatusOr<int64_t> Filesystem::FileSize(const Path& path) {
struct stat st {};
struct stat st{};
if (::stat(path.c_str(), &st) == 0) {
return st.st_size;
} else {

View File

@@ -28,13 +28,11 @@
#include "Firestore/core/include/firebase/firestore/firestore_errors.h"
#if defined(__ANDROID__)
// Abseil does not support STLPort, so avoid their config.h here.
//
// TODO(b/163140650): Remove once the Firebase support floor moves to NDK R18.
//
// Meanwhile, NDK R16b (the current minimum) includes Clang 5.0.3 and GCC 4.9.
// While Clang supports `__cpp_exceptions` at that version, GCC does not. Both
// support `__EXCEPTIONS`.
// The firebase-cpp-sdk has issues compiling Abseil on Android in some cases;
// therefore, use `__EXCEPTIONS`, which is known to be set by clang in Android
// NDK r21e, instead of using `ABSL_HAVE_EXCEPTIONS`. In the future, consider
// using `__cpp_exceptions` instead, as the internet seems to suggest that it
// is more reliable with modern c++ compilers, such as those in NDK r21e.
#if __EXCEPTIONS
#define FIRESTORE_HAVE_EXCEPTIONS 1
#endif

View File

@@ -190,8 +190,8 @@ auto RankedInvokeHash(const Range& range, HashChoice<3>)
* value can itself be hashed.
*/
template <typename K>
auto RankedInvokeHash(const absl::optional<K>& option,
HashChoice<4>) -> decltype(InvokeHash(*option)) {
auto RankedInvokeHash(const absl::optional<K>& option, HashChoice<4>)
-> decltype(InvokeHash(*option)) {
return option ? InvokeHash(*option) : -1171;
}
@@ -202,8 +202,8 @@ size_t RankedInvokeHash(K value, HashChoice<5>) {
}
template <typename K>
auto RankedInvokeHash(const std::unique_ptr<K>& ptr,
HashChoice<6>) -> decltype(InvokeHash(*ptr)) {
auto RankedInvokeHash(const std::unique_ptr<K>& ptr, HashChoice<6>)
-> decltype(InvokeHash(*ptr)) {
return ptr ? InvokeHash(*ptr) : 23631;
}

View File

@@ -35,39 +35,46 @@ namespace {
const FIRLoggerService kFIRLoggerFirestore = @"[FirebaseFirestore]";
// Translates a C++ LogLevel to the equivalent Objective-C FIRLoggerLevel
FIRLoggerLevel ToFIRLoggerLevel(LogLevel level) {
switch (level) {
case kLogLevelDebug:
return FIRLoggerLevelDebug;
case kLogLevelNotice:
return FIRLoggerLevelNotice;
case kLogLevelWarning:
return FIRLoggerLevelWarning;
case kLogLevelError:
return FIRLoggerLevelError;
default:
// Unsupported log level. FIRSetLoggerLevel will deal with it.
return static_cast<FIRLoggerLevel>(-1);
}
}
// Actually logs a message via FIRLogger. This must be a C varargs function
// so that we can call FIRLogBasic which takes a `va_list`.
void LogMessageV(LogLevel level, NSString* format, ...) {
va_list list;
va_start(list, format);
FIRLogBasic(ToFIRLoggerLevel(level), kFIRLoggerFirestore, @"I-FST000001",
format, list);
switch (level) {
case kLogLevelDebug:
FIRLogBasicDebug(kFIRLoggerFirestore, @"I-FST000001", format, list);
break;
case kLogLevelNotice:
FIRLogBasicNotice(kFIRLoggerFirestore, @"I-FST000001", format, list);
break;
case kLogLevelWarning:
FIRLogBasicWarning(kFIRLoggerFirestore, @"I-FST000001", format, list);
break;
case kLogLevelError:
FIRLogBasicError(kFIRLoggerFirestore, @"I-FST000001", format, list);
break;
}
va_end(list);
}
} // namespace
void LogSetLevel(LogLevel level) {
FIRSetLoggerLevel(ToFIRLoggerLevel(level));
switch (level) {
case kLogLevelDebug:
FIRSetLoggerLevelDebug();
break;
case kLogLevelNotice:
FIRSetLoggerLevelNotice();
break;
case kLogLevelWarning:
FIRSetLoggerLevelWarning();
break;
case kLogLevelError:
FIRSetLoggerLevelError();
break;
}
}
// Note that FIRLogger's default level can be changed by persisting a
@@ -81,7 +88,17 @@ void LogSetLevel(LogLevel level) {
// defaults write firestore_util_test /google/firebase/debug_mode NO
bool LogIsLoggable(LogLevel level) {
return FIRIsLoggableLevel(ToFIRLoggerLevel(level), false);
switch (level) {
case kLogLevelDebug:
return FIRIsLoggableLevelDebug();
case kLogLevelNotice:
return FIRIsLoggableLevelNotice();
case kLogLevelWarning:
return FIRIsLoggableLevelWarning();
case kLogLevelError:
return FIRIsLoggableLevelError();
}
return false;
}
void LogMessage(LogLevel level, const std::string& message) {

View File

@@ -18,9 +18,9 @@
#define FIRESTORE_CORE_SRC_UTIL_SCHEDULE_H_
#include <algorithm>
#include <condition_variable> // NOLINT(build/c++11)
#include <condition_variable>
#include <deque>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <vector>
#include "Firestore/core/src/util/executor.h"

View File

@@ -16,6 +16,10 @@
#include "Firestore/core/src/util/string_format.h"
#include <string>
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
namespace firebase {
namespace firestore {
namespace util {
@@ -24,7 +28,13 @@ namespace internal {
static const char* kMissing = "<missing>";
static const char* kInvalid = "<invalid>";
std::string StringFormatPieces(
// Disable asan for this function because of the way it manages stack
// (nested closure) is flagged with stack underflow by clang on Ubuntu.
#if defined(_MSC_VER)
__declspec(no_sanitize_address) std::string StringFormatPieces(
#else
__attribute__((no_sanitize_address)) std::string StringFormatPieces(
#endif
const char* format, std::initializer_list<absl::string_view> pieces) {
std::string result;
@@ -33,7 +43,7 @@ std::string StringFormatPieces(
auto pieces_iter = pieces.begin();
auto pieces_end = pieces.end();
auto append_next_piece = [&](std::string* dest) {
auto append_next_string_piece = [&](std::string* dest) {
if (pieces_iter == pieces_end) {
dest->append(kMissing);
} else {
@@ -43,6 +53,17 @@ std::string StringFormatPieces(
}
};
auto append_next_hex_piece = [&](std::string* dest) {
if (pieces_iter == pieces_end) {
dest->append(kMissing);
} else {
std::string hex =
absl::BytesToHexString(absl::string_view(pieces_iter->data()));
dest->append(hex.data(), hex.size());
++pieces_iter;
}
};
auto append_specifier = [&](char spec) {
switch (spec) {
case '%':
@@ -51,7 +72,12 @@ std::string StringFormatPieces(
break;
case 's': {
append_next_piece(&result);
append_next_string_piece(&result);
break;
}
case 'x': {
append_next_hex_piece(&result);
break;
}

View File

@@ -62,11 +62,20 @@ struct FormatChoice<5> {};
* formatting of the value as an unsigned integer.
* * Otherwise the value is interpreted as anything absl::AlphaNum accepts.
*/
class FormatArg : public absl::AlphaNum {
class FormatArg final : public absl::AlphaNum {
public:
template <typename T>
FormatArg(T&& value) // NOLINT(runtime/explicit)
: FormatArg{std::forward<T>(value), internal::FormatChoice<0>{}} {
FormatArg(
T&& value,
// TODO(b/388888512) Remove the usage of StringifySink since it is not
// part of absl's public API. Moreover, subclassing AlphaNum is not
// supported either, so find a way to do this without these two caveats.
// See https://github.com/firebase/firebase-ios-sdk/pull/14331 for a
// partial proposal.
absl::strings_internal::StringifySink&& sink =
{}) // NOLINT(runtime/explicit)
: FormatArg{std::forward<T>(value), std::move(sink),
internal::FormatChoice<0>{}} {
}
private:
@@ -79,7 +88,9 @@ class FormatArg : public absl::AlphaNum {
*/
template <typename T,
typename = typename std::enable_if<std::is_same<bool, T>{}>::type>
FormatArg(T bool_value, internal::FormatChoice<0>)
FormatArg(T bool_value,
absl::strings_internal::StringifySink&&,
internal::FormatChoice<0>)
: AlphaNum(bool_value ? "true" : "false") {
}
@@ -90,7 +101,9 @@ class FormatArg : public absl::AlphaNum {
template <
typename T,
typename = typename std::enable_if<objc::is_objc_pointer<T>{}>::type>
FormatArg(T object, internal::FormatChoice<1>)
FormatArg(T object,
absl::strings_internal::StringifySink&&,
internal::FormatChoice<1>)
: AlphaNum(MakeStringView([object description])) {
}
@@ -98,7 +111,9 @@ class FormatArg : public absl::AlphaNum {
* Creates a FormatArg from any Objective-C Class type. Objective-C Class
* types are a special struct that aren't of a type derived from NSObject.
*/
FormatArg(Class object, internal::FormatChoice<1>)
FormatArg(Class object,
absl::strings_internal::StringifySink&&,
internal::FormatChoice<1>)
: AlphaNum(MakeStringView(NSStringFromClass(object))) {
}
#endif
@@ -108,7 +123,10 @@ class FormatArg : public absl::AlphaNum {
* handled specially to avoid ambiguity with generic pointers, which are
* handled differently.
*/
FormatArg(std::nullptr_t, internal::FormatChoice<2>) : AlphaNum("null") {
FormatArg(std::nullptr_t,
absl::strings_internal::StringifySink&&,
internal::FormatChoice<2>)
: AlphaNum("null") {
}
/**
@@ -116,7 +134,9 @@ class FormatArg : public absl::AlphaNum {
* handled specially to avoid ambiguity with generic pointers, which are
* handled differently.
*/
FormatArg(const char* string_value, internal::FormatChoice<3>)
FormatArg(const char* string_value,
absl::strings_internal::StringifySink&&,
internal::FormatChoice<3>)
: AlphaNum(string_value == nullptr ? "null" : string_value) {
}
@@ -125,8 +145,11 @@ class FormatArg : public absl::AlphaNum {
* hexadecimal integer literal.
*/
template <typename T>
FormatArg(T* pointer_value, internal::FormatChoice<4>)
: AlphaNum(absl::Hex(reinterpret_cast<uintptr_t>(pointer_value))) {
FormatArg(T* pointer_value,
absl::strings_internal::StringifySink&& sink,
internal::FormatChoice<4>)
: AlphaNum(absl::Hex(reinterpret_cast<uintptr_t>(pointer_value)),
std::move(sink)) {
}
/**
@@ -134,7 +157,9 @@ class FormatArg : public absl::AlphaNum {
* absl::AlphaNum accepts.
*/
template <typename T>
FormatArg(T&& value, internal::FormatChoice<5>)
FormatArg(T&& value,
absl::strings_internal::StringifySink&&,
internal::FormatChoice<5>)
: AlphaNum(std::forward<T>(value)) {
}
};

View File

@@ -16,7 +16,7 @@
#include "Firestore/core/src/util/task.h"
#include <chrono> // NOLINT(build/c++11)
#include <chrono>
#include <cstdint>
#include <utility>

View File

@@ -18,10 +18,10 @@
#define FIRESTORE_CORE_SRC_UTIL_TASK_H_
#include <atomic>
#include <condition_variable> // NOLINT(build/c++11)
#include <condition_variable>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <thread> // NOLINT(build/c++11)
#include <mutex>
#include <thread>
#include "Firestore/core/src/util/executor.h"

View File

@@ -17,7 +17,7 @@
#include "Firestore/core/src/util/testing_hooks.h"
#include <functional>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <type_traits>
#include <unordered_map>
#include <utility>

View File

@@ -19,7 +19,7 @@
#include <functional>
#include <memory>
#include <mutex> // NOLINT(build/c++11)
#include <mutex>
#include <string>
#include <unordered_map>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023 Google
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,10 @@
#define FIRESTORE_CORE_SRC_UTIL_THREAD_SAFE_MEMOIZER_H_
#include <functional>
#include <mutex> // NOLINT(build/c++11)
#include <vector>
#include <memory>
#include <utility>
#include "Firestore/core/src/util/hard_assert.h"
namespace firebase {
namespace firestore {
@@ -28,51 +30,135 @@ namespace util {
/**
* Stores a memoized value in a manner that is safe to be shared between
* multiple threads.
*
* TODO(b/299933587) Make `ThreadSafeMemoizer` copyable and moveable.
*/
template <typename T>
class ThreadSafeMemoizer {
public:
ThreadSafeMemoizer() = default;
~ThreadSafeMemoizer() {
// Call `std::call_once` in order to synchronize with the "active"
// invocation of `memoize()`. Without this synchronization, there is a data
// race between this destructor, which "reads" `memoized_value_` to destroy
// it, and the write to `memoized_value_` done by the "active" invocation of
// `memoize()`.
std::call_once(once_, [&]() {});
/**
* Creates a new ThreadSafeMemoizer with no memoized value.
*/
ThreadSafeMemoizer() {
std::atomic_store(&memoized_, std::shared_ptr<T>());
}
// This class cannot be copied or moved, because it has `std::once_flag`
// member.
ThreadSafeMemoizer(const ThreadSafeMemoizer&) = delete;
ThreadSafeMemoizer(ThreadSafeMemoizer&&) = delete;
ThreadSafeMemoizer& operator=(const ThreadSafeMemoizer&) = delete;
ThreadSafeMemoizer& operator=(ThreadSafeMemoizer&&) = delete;
/**
* Copy constructor: creates a new ThreadSafeMemoizer object with the same
* memoized value as the ThreadSafeMemoizer object referred to by the given
* reference.
*
* The runtime performance of this function is O(1).
*/
ThreadSafeMemoizer(const ThreadSafeMemoizer& other) {
operator=(other);
}
/**
* Memoize a value.
* Copy assignment operator: replaces this object's memoized value with the
* memoized value of the ThreadSafeMemoizer object referred to by the given
* reference.
*
* The std::function object specified by the first invocation of this
* function (the "active" invocation) will be invoked synchronously.
* None of the std::function objects specified by the subsequent
* invocations of this function (the "passive" invocations) will be
* invoked. All invocations, both "active" and "passive", will return a
* reference to the std::vector created by copying the return value from
* the std::function specified by the "active" invocation. It is,
* therefore, the "active" invocation's job to return the std::vector
* to memoize.
* The runtime performance of this function is O(1).
*/
const T& memoize(std::function<T()> func) {
std::call_once(once_, [&]() { memoized_value_ = func(); });
return memoized_value_;
ThreadSafeMemoizer& operator=(const ThreadSafeMemoizer& other) {
if (&other == this) {
return *this;
}
std::atomic_store(&memoized_, std::atomic_load(&other.memoized_));
return *this;
}
/**
* Move constructor: creates a new ThreadSafeMemoizer object with the same
* memoized value as the ThreadSafeMemoizer object referred to by the given
* reference, also clearing its memoized value.
*
* The runtime performance of this function is O(1).
*/
ThreadSafeMemoizer(ThreadSafeMemoizer&& other) noexcept {
operator=(std::move(other));
}
/**
* Move assignment operator: replaces this object's memoized value with the
* memoized value of the ThreadSafeMemoizer object referred to by the given
* reference, also clearing its memoized value.
*
* The runtime performance of this function is O(1).
*/
ThreadSafeMemoizer& operator=(ThreadSafeMemoizer&& other) noexcept {
std::atomic_store(&memoized_, std::atomic_load(&other.memoized_));
std::atomic_store(&other.memoized_, std::shared_ptr<T>());
return *this;
}
/**
* Return the memoized value, calculating it with the given function if
* needed.
*
* If this object _does_ have a memoized value then this function simply
* returns a reference to it and does _not_ call the given function.
*
* On the other hand, if this object does _not_ have a memoized value then
* the given function is called to calculate the value to memoize. The value
* returned by the function is stored internally as the "memoized value" and
* then returned. If multiple threads race in calls to this function then
* more than one of them may have their functions called but only one of them
* will be memoized, with the others being discarded.
*
* The given function will be called synchronously by this function either
* zero times or one time. No reference to the given function is retained by
* this object.
*
* The given function _must_ return an initialized `std::shared_ptr<T>`; that
* is, the returned `std::shared_ptr<T>` _must_ evaluate to `true` when
* converted to `bool`. It is undefined behavior if the returned
* `std::shared_ptr<T>` does _not_ satisfy this requirement.
*
* This function is thread-safe and may be called concurrently by multiple
* threads.
*
* The returned reference is "valid" only as long as this `ThreadSafeMemoizer`
* object is alive; namely, once this `ThreadSafeMemoizer` object's destructor
* starts running, the reference returned by this function is invalid and
* using it is undefined behavior.
*/
const T& value(const std::function<std::shared_ptr<T>()>& func) {
std::shared_ptr<T> old_memoized = std::atomic_load(&memoized_);
std::shared_ptr<T> new_memoized;
bool new_memoized_is_initialized = false;
while (true) {
if (old_memoized) {
return *old_memoized;
}
if (!new_memoized_is_initialized) {
new_memoized = func();
new_memoized_is_initialized = true;
HARD_ASSERT(new_memoized);
}
if (std::atomic_compare_exchange_weak(&memoized_, &old_memoized,
new_memoized)) {
return *new_memoized;
}
}
}
private:
std::once_flag once_;
T memoized_value_;
// NOTE: Always use the std::atomic_XXX() functions to access the memoized_
// std::shared_ptr to ensure thread safety.
// See https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic.
// TODO(c++20): Use std::atomic<std::shared_ptr<T>> instead of a bare
// std::shared_ptr<T> and the std::atomic_XXX() functions. The
// std::atomic_XXX() free functions are deprecated in C++20, and are also
// more error-prone than their std::atomic<std::shared_ptr<T>> member
// function counterparts.
std::shared_ptr<T> memoized_;
};
} // namespace util