1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-23 00:05:22 +03:00
betaflight-configurator/src/js/FirmwareCache.js
2018-05-02 14:47:41 +02:00

216 lines
5.6 KiB
JavaScript

'use strict';
/**
* Caching of previously downloaded firmwares and release descriptions
*
* Depends on LRUMap for which the docs can be found here:
* https://github.com/rsms/js-lru
*/
/**
* @typedef {object} Descriptor Release descriptor object
* @property {string} releaseUrl
* @property {string} name
* @property {string} version
* @property {string} url
* @property {string} file
* @property {string} target
* @property {string} date
* @property {string} notes
* @property {string} status
* @see buildBoardOptions() in {@link release_checker.js}
*/
/**
* @typedef {object} CacheItem
* @property {Descriptor} release
* @property {string} hexdata
*/
/**
* Manages caching of downloaded firmware files
*/
let FirmwareCache = (function () {
let onPutToCacheCallback,
onRemoveFromCacheCallback;
let JournalStorage = (function () {
let CACHEKEY = "firmware-cache-journal";
/**
* @param {Array} data LRU key-value pairs
*/
function persist(data) {
let obj = {};
obj[CACHEKEY] = data;
chrome.storage.local.set(obj);
}
/**
* @param {Function} callback
*/
function load(callback) {
chrome.storage.local.get(CACHEKEY, obj => {
let entries = typeof obj === "object" && obj.hasOwnProperty(CACHEKEY)
? obj[CACHEKEY]
: [];
callback(entries);
});
}
return {
persist: persist,
load: load,
};
})();
let journal = new LRUMap(100),
journalLoaded = false;
journal.shift = function () {
// remove cached data for oldest release
let oldest = LRUMap.prototype.shift.call(this);
if (oldest === undefined) {
return undefined;
}
let key = oldest[0];
let cacheKey = withCachePrefix(key);
chrome.storage.local.get(cacheKey, obj => {
/** @type {CacheItem} */
let cached = typeof obj === "object" && obj.hasOwnProperty(cacheKey)
? obj[cacheKey]
: null;
chrome.storage.local.remove(cacheKey, () => {
onRemoveFromCache(cached.release);
console.debug("Cache data removed: " + cacheKey);
});
});
return oldest;
};
/**
* @param {Descriptor} release
* @returns {string} A key used to store a release in the journal
*/
function keyOf(release) {
return release.file;
}
/**
* @param {string} key
* @returns {string} A key for storing cached data for a release
*/
function withCachePrefix(key) {
return "cache:" + key;
}
/**
* @param {Descriptor} release
* @returns {boolean}
*/
function has(release) {
if (!journalLoaded) {
console.warn("Cache not yet loaded");
return false;
}
return journal.has(keyOf(release));
}
/**
* @param {Descriptor} release
* @param {string} hexdata
*/
function put(release, hexdata) {
if (!journalLoaded) {
console.warn("Cache journal not yet loaded");
return;
}
let key = keyOf(release);
if (has(release)) {
console.debug("Firmware is already cached: " + key);
return;
}
journal.set(key, true);
JournalStorage.persist(journal.toJSON());
let obj = {};
obj[withCachePrefix(key)] = {
release: release,
hexdata: hexdata,
};
chrome.storage.local.set(obj, () => {
console.info("Release put to cache: " + key);
onPutToCache(release);
});
}
/**
* @param {Descriptor} release
* @param {Function} callback
*/
function get(release, callback) {
if (!journalLoaded) {
console.warn("Cache journal not yet loaded");
return undefined;
}
let key = keyOf(release);
if (!has(release)) {
console.debug("Firmware is not cached: " + key);
return;
}
let cacheKey = withCachePrefix(key);
chrome.storage.local.get(cacheKey, obj => {
/** @type {CacheItem} */
let cached = typeof obj === "object" && obj.hasOwnProperty(cacheKey)
? obj[cacheKey]
: null;
callback(cached);
});
}
/**
* @param {Descriptor} release
*/
function onPutToCache(release) {
if (typeof onPutToCacheCallback === "function") {
onPutToCacheCallback(release);
}
}
/**
* @param {Descriptor} release
*/
function onRemoveFromCache(release) {
if (typeof onRemoveFromCacheCallback === "function") {
onRemoveFromCacheCallback(release);
}
}
/**
* @param {Array} entries
*/
function onEntriesLoaded(entries) {
let pairs = [];
for (let entry of entries) {
pairs.push([entry.key, entry.value]);
}
journal.assign(pairs);
journalLoaded = true;
console.info("Firmware cache journal loaded; number of entries: " + entries.length);
}
return {
has: has,
put: put,
get: get,
onPutToCache: callback => onPutToCacheCallback = callback,
onRemoveFromCache: callback => onRemoveFromCacheCallback = callback,
load: () => {
JournalStorage.load(onEntriesLoaded);
},
flush: () => {
JournalStorage.persist(journal.toJSON());
journal.clear();
},
};
})();