1 // This file is part of Desktop App Toolkit,
2 // a set of libraries for developing nice desktop applications.
3 //
4 // For license and copyright information please follow this link:
5 // https://github.com/desktop-app/legal/blob/master/LEGAL
6 //
7 #pragma once
8 
9 #include "base/basic_types.h"
10 #include "base/flat_map.h"
11 #include "base/optional.h"
12 #include <crl/crl_time.h>
13 #include <QtCore/QString>
14 #include <QtCore/QByteArray>
15 
16 namespace Storage {
17 namespace Cache {
18 
19 struct Key {
20 	uint64 high = 0;
21 	uint64 low = 0;
22 
validKey23 	[[nodiscard]] bool valid() const {
24 		return (high != 0) || (low != 0);
25 	}
26 	explicit operator bool() const {
27 		return valid();
28 	}
29 };
30 
31 inline bool operator==(const Key &a, const Key &b) {
32 	return (a.high == b.high) && (a.low == b.low);
33 }
34 
35 inline bool operator!=(const Key &a, const Key &b) {
36 	return !(a == b);
37 }
38 
39 inline bool operator<(const Key &a, const Key &b) {
40 	return std::tie(a.high, a.low) < std::tie(b.high, b.low);
41 }
42 
43 struct Error {
44 	enum class Type {
45 		None,
46 		IO,
47 		WrongKey,
48 		LockFailed,
49 	};
50 	Type type = Type::None;
51 	QString path;
52 
53 	static Error NoError();
54 };
55 
NoError()56 inline Error Error::NoError() {
57 	return Error();
58 }
59 
60 namespace details {
61 
62 using RecordType = uint8;
63 using PlaceId = std::array<uint8, 7>;
64 using EntrySize = std::array<uint8, 3>;
65 using RecordsCount = std::array<uint8, 3>;
66 
67 constexpr auto kRecordSizeUnknown = size_type(-1);
68 constexpr auto kRecordSizeInvalid = size_type(-2);
69 constexpr auto kBundledRecordsLimit
70 	= size_type(1 << (RecordsCount().size() * 8));
71 constexpr auto kDataSizeLimit = size_type(1 << (EntrySize().size() * 8));
72 
73 struct Settings {
74 	size_type maxBundledRecords = 16 * 1024;
75 	size_type readBlockSize = 8 * 1024 * 1024;
76 	size_type maxDataSize = (kDataSizeLimit - 1);
77 	crl::time writeBundleDelay = 15 * 60 * crl::time(1000);
78 	size_type staleRemoveChunk = 256;
79 
80 	int64 compactAfterExcess = 8 * 1024 * 1024;
81 	int64 compactAfterFullSize = 0;
82 	size_type compactChunkSize = 16 * 1024;
83 
84 	bool trackEstimatedTime = true;
85 	int64 totalSizeLimit = 1024 * 1024 * 1024;
86 	size_type totalTimeLimit = 31 * 24 * 60 * 60; // One month in seconds.
87 	crl::time pruneTimeout = 5 * crl::time(1000);
88 	crl::time maxPruneCheckTimeout = 3600 * crl::time(1000);
89 
90 	bool clearOnWrongKey = false;
91 };
92 
93 struct SettingsUpdate {
94 	int64 totalSizeLimit = Settings().totalSizeLimit;
95 	size_type totalTimeLimit = Settings().totalTimeLimit;
96 };
97 
98 struct TaggedValue {
99 	TaggedValue() = default;
100 	TaggedValue(QByteArray &&bytes, uint8 tag);
101 
102 	QByteArray bytes;
103 	uint8 tag = 0;
104 };
105 
106 struct TaggedSummary {
107 	size_type count = 0;
108 	int64 totalSize = 0;
109 };
110 struct Stats {
111 	TaggedSummary full;
112 	base::flat_map<uint8, TaggedSummary> tagged;
113 	bool clearing = false;
114 };
115 
116 using Version = int32;
117 
118 QString ComputeBasePath(const QString &original);
119 QString VersionFilePath(const QString &base);
120 std::optional<Version> ReadVersionValue(const QString &base);
121 bool WriteVersionValue(const QString &base, Version value);
122 
123 template <typename Record>
124 constexpr auto GoodForEncryption = ((sizeof(Record) & 0x0F) == 0);
125 
126 enum class Format : uint32 {
127 	Format_0,
128 };
129 
130 struct BasicHeader {
131 	BasicHeader();
132 
133 	static constexpr auto kTrackEstimatedTime = 0x01U;
134 
getFormatBasicHeader135 	Format getFormat() const {
136 		return static_cast<Format>(format);
137 	}
setFormatBasicHeader138 	void setFormat(Format format) {
139 		this->format = static_cast<uint32>(format);
140 	}
141 
142 	uint32 format : 8;
143 	uint32 flags : 24;
144 	uint32 systemTime = 0;
145 	uint32 reserved1 = 0;
146 	uint32 reserved2 = 0;
147 };
148 
149 struct EstimatedTimePoint {
150 	uint32 relative1 = 0;
151 	uint32 relative2 = 0;
152 	uint32 system = 0;
153 
setRelativeEstimatedTimePoint154 	void setRelative(uint64 value) {
155 		relative1 = uint32(value & 0xFFFFFFFFU);
156 		relative2 = uint32((value >> 32) & 0xFFFFFFFFU);
157 	}
getRelativeEstimatedTimePoint158 	uint64 getRelative() const {
159 		return uint64(relative1) | (uint64(relative2) << 32);
160 	}
161 };
162 
163 struct Store {
164 	static constexpr auto kType = RecordType(0x01);
165 
166 	void setSize(size_type size);
167 	size_type getSize() const;
168 
169 	RecordType type = kType;
170 	uint8 tag = 0;
171 	EntrySize size = { { 0 } };
172 	PlaceId place = { { 0 } };
173 	uint32 checksum = 0;
174 	Key key;
175 };
176 
177 struct StoreWithTime : Store {
178 	EstimatedTimePoint time;
179 	uint32 reserved = 0;
180 };
181 
182 struct MultiStore {
183 	static constexpr auto kType = RecordType(0x02);
184 
185 	explicit MultiStore(size_type count = 0);
186 
187 	RecordType type = kType;
188 	RecordsCount count = { { 0 } };
189 	uint32 reserved1 = 0;
190 	uint32 reserved2 = 0;
191 	uint32 reserved3 = 0;
192 
193 	using Part = Store;
194 	size_type validateCount() const;
195 };
196 struct MultiStoreWithTime : MultiStore {
197 	using MultiStore::MultiStore;
198 
199 	using Part = StoreWithTime;
200 };
201 
202 struct MultiRemove {
203 	static constexpr auto kType = RecordType(0x03);
204 
205 	explicit MultiRemove(size_type count = 0);
206 
207 	RecordType type = kType;
208 	RecordsCount count = { { 0 } };
209 	uint32 reserved1 = 0;
210 	uint32 reserved2 = 0;
211 	uint32 reserved3 = 0;
212 
213 	using Part = Key;
214 	size_type validateCount() const;
215 };
216 
217 struct MultiAccess {
218 	static constexpr auto kType = RecordType(0x04);
219 
220 	explicit MultiAccess(
221 		EstimatedTimePoint time,
222 		size_type count = 0);
223 
224 	RecordType type = kType;
225 	RecordsCount count = { { 0 } };
226 	EstimatedTimePoint time;
227 
228 	using Part = Key;
229 	size_type validateCount() const;
230 };
231 
232 } // namespace details
233 } // namespace Cache
234 } // namespace Storage
235 
236 namespace std {
237 
238 template <>
239 struct hash<Storage::Cache::Key> {
240 	size_t operator()(const Storage::Cache::Key &key) const {
241 		return (hash<uint64>()(key.high) ^ hash<uint64>()(key.low));
242 	}
243 };
244 
245 } // namespace std
246