next
Version:
The React Framework
153 lines (151 loc) • 5.52 kB
JavaScript
// Utility type. Prefix<[A, B, C, D]> matches [A], [A, B], [A, B, C] etc.
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "createTupleMap", {
enumerable: true,
get: function() {
return createTupleMap;
}
});
function createTupleMap() {
let rootEntry = {
parent: null,
key: null,
hasValue: false,
value: null,
map: null
};
// To optimize successive lookups, we cache the last accessed keypath.
// Although it's not encoded in the type, these are both null or
// both non-null. It uses object equality, so to take advantage of this
// optimization, you must pass the same array instance to each successive
// method call, and you must also not mutate the array between calls.
let lastAccessedEntry = null;
let lastAccessedKeys = null;
function getOrCreateEntry(keys) {
if (lastAccessedKeys === keys) {
return lastAccessedEntry;
}
// Go through each level of keys until we find the entry that matches,
// or create a new one if it doesn't already exist.
let entry = rootEntry;
for(let i = 0; i < keys.length; i++){
const key = keys[i];
let map = entry.map;
if (map !== null) {
const existingEntry = map.get(key);
if (existingEntry !== undefined) {
// Found a match. Keep going.
entry = existingEntry;
continue;
}
} else {
map = new Map();
entry.map = map;
}
// No entry exists yet at this level. Create a new one.
const newEntry = {
parent: entry,
key,
value: null,
hasValue: false,
map: null
};
map.set(key, newEntry);
entry = newEntry;
}
lastAccessedKeys = keys;
lastAccessedEntry = entry;
return entry;
}
function getEntryIfExists(keys) {
if (lastAccessedKeys === keys) {
return lastAccessedEntry;
}
// Go through each level of keys until we find the entry that matches, or
// return null if no match exists.
let entry = rootEntry;
for(let i = 0; i < keys.length; i++){
const key = keys[i];
let map = entry.map;
if (map !== null) {
const existingEntry = map.get(key);
if (existingEntry !== undefined) {
// Found a match. Keep going.
entry = existingEntry;
continue;
}
}
// No entry exists at this level.
return null;
}
lastAccessedKeys = keys;
lastAccessedEntry = entry;
return entry;
}
function set(keys, value) {
const entry = getOrCreateEntry(keys);
entry.hasValue = true;
entry.value = value;
}
function get(keys) {
const entry = getEntryIfExists(keys);
if (entry === null || !entry.hasValue) {
return null;
}
return entry.value;
}
function deleteEntry(keys) {
const entry = getEntryIfExists(keys);
if (entry === null || !entry.hasValue) {
return;
}
// Found a match. Delete it from the cache.
const deletedEntry = entry;
deletedEntry.hasValue = false;
deletedEntry.value = null;
// Check if we can garbage collect the entry.
if (deletedEntry.map === null) {
// Since this entry has no value, and also no child entries, we can
// garbage collect it. Remove it from its parent, and keep garbage
// collecting the parents until we reach a non-empty entry.
// Unlike a `set` operation, these are no longer valid because the entry
// itself is being modified, not just the value it contains.
lastAccessedEntry = null;
lastAccessedKeys = null;
let parent = deletedEntry.parent;
let key = deletedEntry.key;
while(parent !== null){
const parentMap = parent.map;
if (parentMap !== null) {
parentMap.delete(key);
if (parentMap.size === 0) {
// We just removed the last entry in the parent map.
parent.map = null;
if (parent.value === null) {
// The parent node has no child entries, nor does it have a value
// on itself. It can be garbage collected. Keep going.
key = parent.key;
parent = parent.parent;
continue;
}
}
}
break;
}
}
}
return {
set,
get,
delete: deleteEntry
};
}
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
Object.defineProperty(exports.default, '__esModule', { value: true });
Object.assign(exports.default, exports);
module.exports = exports.default;
}
//# sourceMappingURL=tuple-map.js.map