yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
wasm_storage_quota.h
Go to the documentation of this file.
1#ifndef YAZE_APP_PLATFORM_WASM_STORAGE_QUOTA_H_
2#define YAZE_APP_PLATFORM_WASM_STORAGE_QUOTA_H_
3
4#ifdef __EMSCRIPTEN__
5
6#include <emscripten.h>
7#include <emscripten/val.h>
8#include <atomic>
9#include <cstdint>
10#include <functional>
11#include <map>
12#include <memory>
13#include <mutex>
14#include <string>
15#include <vector>
16
17namespace yaze {
18namespace app {
19namespace platform {
20
55class WasmStorageQuota {
56 public:
60 struct StorageInfo {
61 size_t used_bytes = 0;
62 size_t quota_bytes = 0;
63 float usage_percent = 0.0f;
64 bool persistent = false;
65 };
66
70 struct StorageItem {
71 std::string key;
72 size_t compressed_size = 0;
73 size_t original_size = 0;
74 double last_access_time = 0.0;
75 float compression_ratio = 0.0f;
76 };
77
85 void GetStorageInfo(std::function<void(const StorageInfo&)> callback);
86
97 void CompressAndStore(const std::string& key,
98 const std::vector<uint8_t>& data,
99 std::function<void(bool success)> callback);
100
109 void LoadAndDecompress(const std::string& key,
110 std::function<void(std::vector<uint8_t>)> callback);
111
120 void EvictOldest(int count, std::function<void(int evicted)> callback);
121
130 void EvictToTarget(float target_percent,
131 std::function<void(int evicted)> callback);
132
141 void UpdateAccessTime(const std::string& key);
142
150 void GetStoredItems(
151 std::function<void(std::vector<StorageItem>)> callback);
152
158 void DeleteItem(const std::string& key,
159 std::function<void(bool success)> callback);
160
167 void ClearAll(std::function<void()> callback);
168
173 static bool IsSupported();
174
179 static WasmStorageQuota& Get();
180
181 // Configuration constants
182 static constexpr float kDefaultTargetUsage = 70.0f;
183 static constexpr float kWarningThreshold = 80.0f;
184 static constexpr float kCriticalThreshold = 90.0f;
185 static constexpr size_t kMinQuotaBytes = 50 * 1024 * 1024;
186
187 private:
188 WasmStorageQuota() = default;
189
190 // Compression helpers (using browser's CompressionStream API)
191 void CompressData(const std::vector<uint8_t>& input,
192 std::function<void(std::vector<uint8_t>)> callback);
193 void DecompressData(const std::vector<uint8_t>& input,
194 std::function<void(std::vector<uint8_t>)> callback);
195
196 // Internal storage operations
197 void StoreCompressedData(const std::string& key,
198 const std::vector<uint8_t>& compressed_data,
199 size_t original_size,
200 std::function<void(bool)> callback);
201 void LoadCompressedData(const std::string& key,
202 std::function<void(std::vector<uint8_t>, size_t)> callback);
203
204 // Metadata management
205 void UpdateMetadata(const std::string& key, size_t compressed_size,
206 size_t original_size);
207 void LoadMetadata(std::function<void()> callback);
208 void SaveMetadata(std::function<void()> callback);
209
210 // Storage monitoring
211 void CheckQuotaAndEvict(size_t new_size_bytes,
212 std::function<void(bool)> callback);
213
214 // Thread safety
215 mutable std::mutex mutex_;
216
217 // Cached metadata (key -> last access time in ms)
218 std::map<std::string, double> access_times_;
219 std::map<std::string, StorageItem> item_metadata_;
220 std::atomic<bool> metadata_loaded_{false};
221
222 // Current storage state
223 StorageInfo last_storage_info_;
224 std::atomic<double> last_quota_check_time_{0.0};
225};
226
227// clang-format off
228
229// JavaScript bridge functions for storage quota API
230EM_JS(void, wasm_storage_quota_estimate, (int callback_id), {
231 if (!navigator.storage || !navigator.storage.estimate) {
232 // Call back with error values
233 Module.ccall('wasm_storage_quota_estimate_callback',
234 null, ['number', 'number', 'number', 'number'],
235 [callback_id, 0, 0, 0]);
236 return;
237 }
238
239 navigator.storage.estimate().then(function(estimate) {
240 var used = estimate.usage || 0;
241 var quota = estimate.quota || 0;
242 var persistent = estimate.persistent ? 1 : 0;
243 Module.ccall('wasm_storage_quota_estimate_callback',
244 null, ['number', 'number', 'number', 'number'],
245 [callback_id, used, quota, persistent]);
246 }).catch(function(error) {
247 console.error('[StorageQuota] Error estimating storage:', error);
248 Module.ccall('wasm_storage_quota_estimate_callback',
249 null, ['number', 'number', 'number', 'number'],
250 [callback_id, 0, 0, 0]);
251 });
252});
253
254// Compression using browser's CompressionStream API
255EM_JS(void, wasm_compress_data, (const uint8_t* data, size_t size, int callback_id), {
256 var input = new Uint8Array(Module.HEAPU8.buffer, data, size);
257
258 // Use CompressionStream if available (Chrome 80+, Firefox 113+)
259 if (typeof CompressionStream !== 'undefined') {
260 var stream = new CompressionStream('deflate');
261 var writer = stream.writable.getWriter();
262
263 writer.write(input).then(function() {
264 return writer.close();
265 }).then(function() {
266 return new Response(stream.readable).arrayBuffer();
267 }).then(function(compressed) {
268 var compressedArray = new Uint8Array(compressed);
269 var ptr = Module._malloc(compressedArray.length);
270 Module.HEAPU8.set(compressedArray, ptr);
271 Module.ccall('wasm_compress_callback',
272 null, ['number', 'number', 'number'],
273 [callback_id, ptr, compressedArray.length]);
274 }).catch(function(error) {
275 console.error('[StorageQuota] Compression error:', error);
276 Module.ccall('wasm_compress_callback',
277 null, ['number', 'number', 'number'],
278 [callback_id, 0, 0]);
279 });
280 } else {
281 // Fallback: No compression, return original data
282 console.warn('[StorageQuota] CompressionStream not available, storing uncompressed');
283 var ptr = Module._malloc(size);
284 Module.HEAPU8.set(input, ptr);
285 Module.ccall('wasm_compress_callback',
286 null, ['number', 'number', 'number'],
287 [callback_id, ptr, size]);
288 }
289});
290
291EM_JS(void, wasm_decompress_data, (const uint8_t* data, size_t size, int callback_id), {
292 var input = new Uint8Array(Module.HEAPU8.buffer, data, size);
293
294 if (typeof DecompressionStream !== 'undefined') {
295 var stream = new DecompressionStream('deflate');
296 var writer = stream.writable.getWriter();
297
298 writer.write(input).then(function() {
299 return writer.close();
300 }).then(function() {
301 return new Response(stream.readable).arrayBuffer();
302 }).then(function(decompressed) {
303 var decompressedArray = new Uint8Array(decompressed);
304 var ptr = Module._malloc(decompressedArray.length);
305 Module.HEAPU8.set(decompressedArray, ptr);
306 Module.ccall('wasm_decompress_callback',
307 null, ['number', 'number', 'number'],
308 [callback_id, ptr, decompressedArray.length]);
309 }).catch(function(error) {
310 console.error('[StorageQuota] Decompression error:', error);
311 Module.ccall('wasm_decompress_callback',
312 null, ['number', 'number', 'number'],
313 [callback_id, 0, 0]);
314 });
315 } else {
316 // Fallback: Assume data is uncompressed
317 var ptr = Module._malloc(size);
318 Module.HEAPU8.set(input, ptr);
319 Module.ccall('wasm_decompress_callback',
320 null, ['number', 'number', 'number'],
321 [callback_id, ptr, size]);
322 }
323});
324
325// Get current timestamp in milliseconds
326EM_JS(double, wasm_get_timestamp_ms, (), {
327 return Date.now();
328});
329
330// Check if compression APIs are available
331EM_JS(int, wasm_compression_available, (), {
332 return (typeof CompressionStream !== 'undefined' &&
333 typeof DecompressionStream !== 'undefined') ? 1 : 0;
334});
335
336// clang-format on
337
338} // namespace platform
339} // namespace app
340} // namespace yaze
341
342#else // !__EMSCRIPTEN__
343
344// Stub implementation for non-WASM builds
345#include <functional>
346#include <string>
347#include <vector>
348
349namespace yaze {
350namespace app {
351namespace platform {
352
354 public:
355 struct StorageInfo {
356 size_t used_bytes = 0;
357 size_t quota_bytes = 100 * 1024 * 1024; // 100MB default
358 float usage_percent = 0.0f;
359 bool persistent = false;
360 };
361
362 struct StorageItem {
363 std::string key;
364 size_t compressed_size = 0;
365 size_t original_size = 0;
366 double last_access_time = 0.0;
367 float compression_ratio = 1.0f;
368 };
369
370 void GetStorageInfo(std::function<void(const StorageInfo&)> callback) {
371 StorageInfo info;
372 callback(info);
373 }
374
375 void CompressAndStore(const std::string& key,
376 const std::vector<uint8_t>& data,
377 std::function<void(bool success)> callback) {
378 callback(false);
379 }
380
381 void LoadAndDecompress(const std::string& key,
382 std::function<void(std::vector<uint8_t>)> callback) {
383 callback(std::vector<uint8_t>());
384 }
385
386 void EvictOldest(int count, std::function<void(int evicted)> callback) {
387 callback(0);
388 }
389
390 void EvictToTarget(float target_percent,
391 std::function<void(int evicted)> callback) {
392 callback(0);
393 }
394
395 void UpdateAccessTime(const std::string& key) {}
396
398 std::function<void(std::vector<StorageItem>)> callback) {
399 callback(std::vector<StorageItem>());
400 }
401
402 void DeleteItem(const std::string& key,
403 std::function<void(bool success)> callback) {
404 callback(false);
405 }
406
407 void ClearAll(std::function<void()> callback) { callback(); }
408
409 static bool IsSupported() { return false; }
410
412 static WasmStorageQuota instance;
413 return instance;
414 }
415
416 static constexpr float kDefaultTargetUsage = 70.0f;
417 static constexpr float kWarningThreshold = 80.0f;
418 static constexpr float kCriticalThreshold = 90.0f;
419 static constexpr size_t kMinQuotaBytes = 50 * 1024 * 1024;
420};
421
422} // namespace platform
423} // namespace app
424} // namespace yaze
425
426#endif // __EMSCRIPTEN__
427
428#endif // YAZE_APP_PLATFORM_WASM_STORAGE_QUOTA_H_
void EvictOldest(int count, std::function< void(int evicted)> callback)
void GetStoredItems(std::function< void(std::vector< StorageItem >)> callback)
void UpdateAccessTime(const std::string &key)
void CompressAndStore(const std::string &key, const std::vector< uint8_t > &data, std::function< void(bool success)> callback)
void ClearAll(std::function< void()> callback)
void LoadAndDecompress(const std::string &key, std::function< void(std::vector< uint8_t >)> callback)
void DeleteItem(const std::string &key, std::function< void(bool success)> callback)
void EvictToTarget(float target_percent, std::function< void(int evicted)> callback)
void GetStorageInfo(std::function< void(const StorageInfo &)> callback)
EM_JS(void, CallJsAiDriver,(const char *history_json), { if(window.yaze &&window.yaze.ai &&window.yaze.ai.processAgentRequest) { window.yaze.ai.processAgentRequest(UTF8ToString(history_json));} else { console.error("AI Driver not found in window.yaze.ai.processAgentRequest");} })
absl::Status LoadMetadata(const Rom &rom, GameData &data)
Definition game_data.cc:136