#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_INDEX_H_
#define NET_DISK_CACHE_SIMPLE_SIMPLE_INDEX_H_
#include <stdint.h>
#include <list>
#include <memory>
#include <vector>
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "net/base/cache_type.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_errors.h"
#include "net/base/net_export.h"
#include "net/disk_cache/disk_cache.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/application_status_listener.h"
#endif
namespace base {
class Pickle;
class PickleIterator;
}
namespace disk_cache {
class BackendCleanupTracker;
class SimpleIndexDelegate;
class SimpleIndexFile;
struct SimpleIndexLoadResult;
class NET_EXPORT_PRIVATE EntryMetadata {
public:
EntryMetadata();
EntryMetadata(base::Time last_used_time,
base::StrictNumeric<uint64_t> entry_size);
EntryMetadata(uint32_t trailer_prefetch_size,
base::StrictNumeric<uint64_t> entry_size);
base::Time GetLastUsedTime() const;
void SetLastUsedTime(base::Time last_used_time);
uint32_t GetTrailerPrefetchSize() const;
void SetTrailerPrefetchSize(uint32_t size);
uint32_t RawTimeForSorting() const {
return last_used_time_seconds_since_epoch_;
}
uint64_t GetEntrySize() const;
bool SetEntrySize(base::StrictNumeric<uint64_t> entry_size);
uint8_t GetInMemoryData() const;
void SetInMemoryData(uint8_t val);
void Serialize(net::CacheType cache_type, base::Pickle* pickle) const;
bool Deserialize(net::CacheType cache_type,
base::PickleIterator* it,
bool app_cache_has_trailer_prefetch_size);
static base::TimeDelta GetLowerEpsilonForTimeComparisons() {
return base::Seconds(1);
}
static base::TimeDelta GetUpperEpsilonForTimeComparisons() {
return base::TimeDelta();
}
static const int kOnDiskSizeBytes = 16;
private:
friend class SimpleIndexFileTest;
FRIEND_TEST_ALL_PREFIXES(SimpleIndexFileTest, ReadV8Format);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexFileTest, ReadV8FormatAppCache);
union {
uint32_t last_used_time_seconds_since_epoch_;
uint32_t trailer_prefetch_size_;
};
uint32_t entry_size_256b_chunks_ : 30;
uint32_t in_memory_data_ : 2;
};
static_assert(sizeof(EntryMetadata) == 8, "incorrect metadata size");
class NET_EXPORT_PRIVATE SimpleIndex final {
public:
enum IndexInitMethod {
INITIALIZE_METHOD_RECOVERED = 0,
INITIALIZE_METHOD_LOADED = 1,
INITIALIZE_METHOD_NEWCACHE = 2,
INITIALIZE_METHOD_MAX = 3,
};
enum IndexWriteToDiskReason : uint32_t {
INDEX_WRITE_REASON_SHUTDOWN = 0,
INDEX_WRITE_REASON_STARTUP_MERGE = 1,
INDEX_WRITE_REASON_IDLE = 2,
INDEX_WRITE_REASON_ANDROID_STOPPED = 3,
INDEX_WRITE_REASON_MAX = 4,
};
typedef std::vector<uint64_t> HashList;
SimpleIndex(const scoped_refptr<base::SequencedTaskRunner>& task_runner,
scoped_refptr<BackendCleanupTracker> cleanup_tracker,
SimpleIndexDelegate* delegate,
net::CacheType cache_type,
std::unique_ptr<SimpleIndexFile> simple_index_file);
~SimpleIndex();
void Initialize(base::Time cache_mtime);
void SetMaxSize(uint64_t max_bytes);
uint64_t max_size() const { return max_size_; }
void Insert(uint64_t entry_hash);
void Remove(uint64_t entry_hash);
bool Has(uint64_t entry_hash) const;
bool UseIfExists(uint64_t entry_hash);
uint8_t GetEntryInMemoryData(uint64_t entry_hash) const;
void SetEntryInMemoryData(uint64_t entry_hash, uint8_t value);
void WriteToDisk(IndexWriteToDiskReason reason);
int32_t GetTrailerPrefetchSize(uint64_t entry_hash) const;
void SetTrailerPrefetchSize(uint64_t entry_hash, int32_t size);
bool UpdateEntrySize(uint64_t entry_hash,
base::StrictNumeric<uint64_t> entry_size);
using EntrySet = absl::flat_hash_map<uint64_t, EntryMetadata>;
static bool InsertInEntrySet(uint64_t entry_hash,
const EntryMetadata& entry_metadata,
EntrySet* entry_set);
void InsertEntryForTesting(uint64_t entry_hash,
const EntryMetadata& entry_metadata);
void ExecuteWhenReady(net::CompletionOnceCallback callback);
std::unique_ptr<HashList> GetEntriesBetween(const base::Time initial_time,
const base::Time end_time);
std::unique_ptr<HashList> GetAllHashes();
int32_t GetEntryCount() const;
uint64_t GetCacheSize() const;
uint64_t GetCacheSizeBetween(const base::Time initial_time,
const base::Time end_time) const;
bool initialized() const { return initialized_; }
IndexInitMethod init_method() const { return init_method_; }
base::Time GetLastUsedTime(uint64_t entry_hash);
void SetLastUsedTimeForTest(uint64_t entry_hash, const base::Time last_used);
#if BUILDFLAG(IS_ANDROID)
void set_app_status_listener_getter(
ApplicationStatusListenerGetter app_status_listener_getter) {
app_status_listener_getter_ = app_status_listener_getter;
}
#endif
bool HasPendingWrite() const;
private:
friend class SimpleIndexTest;
FRIEND_TEST_ALL_PREFIXES(SimpleIndexTest, IndexSizeCorrectOnMerge);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexTest, DiskWriteQueued);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexTest, DiskWriteExecuted);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexTest, DiskWritePostponed);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexAppCacheTest, DiskWriteQueued);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexCodeCacheTest, DisableEvictBySize);
FRIEND_TEST_ALL_PREFIXES(SimpleIndexCodeCacheTest, EnableEvictBySize);
void StartEvictionIfNeeded();
void EvictionDone(int result);
void PostponeWritingToDisk();
bool UpdateEntryIteratorSize(EntrySet::iterator* it,
base::StrictNumeric<uint64_t> entry_size);
void MergeInitializingSet(std::unique_ptr<SimpleIndexLoadResult> load_result);
#if BUILDFLAG(IS_ANDROID)
void OnApplicationStateChange(base::android::ApplicationState state);
std::unique_ptr<base::android::ApplicationStatusListener>
owned_app_status_listener_;
ApplicationStatusListenerGetter app_status_listener_getter_;
#endif
scoped_refptr<BackendCleanupTracker> cleanup_tracker_;
raw_ptr<SimpleIndexDelegate> delegate_;
EntrySet entries_set_;
const net::CacheType cache_type_;
uint64_t cache_size_ = 0;
uint64_t max_size_ = 0;
uint64_t high_watermark_ = 0;
uint64_t low_watermark_ = 0;
bool eviction_in_progress_ = false;
base::TimeTicks eviction_start_time_;
absl::flat_hash_set<uint64_t> removed_entries_;
bool initialized_ = false;
IndexInitMethod init_method_ = INITIALIZE_METHOD_MAX;
std::unique_ptr<SimpleIndexFile> index_file_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
SEQUENCE_CHECKER(sequence_checker_);
base::OneShotTimer write_to_disk_timer_;
base::RepeatingClosure write_to_disk_cb_;
typedef std::list<net::CompletionOnceCallback> CallbackList;
CallbackList to_run_when_initialized_;
bool app_on_background_ = false;
const bool prioritized_caching_enabled_;
const int caching_prioritization_factor_;
const uint64_t caching_prioritization_period_in_seconds_;
base::WeakPtrFactory<SimpleIndex> weak_ptr_factory_{this};
};
}
#endif