blob: 2f151f9678738767cb7536ada71a3078a88e9a3a [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
/**
* Store of bytes associated with string keys.
*
* Each key must be not longer than 100 characters and consist of only `[a-z]`,
* `[0-9]`, `.` and `_` characters. The key cannot be an empty string, the
* literal `.`, or contain the sequence `..`.
*
* Note that associations are not guaranteed to be persistent. The value
* associated with a key can change or become `null` at any point in time.
*
* TODO(scheglov) Research using asynchronous API.
*/
abstract class ByteStore {
/**
* Return the bytes associated with the given [key].
* Return `null` if the association does not exist.
*/
List<int> get(String key);
/**
* Associate the given [bytes] with the [key].
*/
void put(String key, List<int> bytes);
}
/**
* [ByteStore] which stores data only in memory.
*/
class MemoryByteStore implements ByteStore {
final Map<String, List<int>> _map = {};
@override
List<int> get(String key) {
return _map[key];
}
@override
void put(String key, List<int> bytes) {
_map[key] = bytes;
}
}
/**
* A wrapper around [ByteStore] which adds an in-memory LRU cache to it.
*/
class MemoryCachingByteStore implements ByteStore {
final ByteStore _store;
final int _maxSizeBytes;
final _map = new LinkedHashMap<String, List<int>>();
int _currentSizeBytes = 0;
MemoryCachingByteStore(this._store, this._maxSizeBytes);
@override
List<int> get(String key) {
List<int> bytes = _map.remove(key);
if (bytes == null) {
bytes = _store.get(key);
if (bytes != null) {
_map[key] = bytes;
_currentSizeBytes += bytes.length;
_evict();
}
} else {
_map[key] = bytes;
}
return bytes;
}
@override
void put(String key, List<int> bytes) {
_store.put(key, bytes);
_currentSizeBytes -= _map[key]?.length ?? 0;
_map[key] = bytes;
_currentSizeBytes += bytes.length;
_evict();
}
void _evict() {
while (_currentSizeBytes > _maxSizeBytes) {
if (_map.isEmpty) {
// Should be impossible, since _currentSizeBytes should always match
// _map. But recover anyway.
assert(false);
_currentSizeBytes = 0;
break;
}
String key = _map.keys.first;
List<int> bytes = _map.remove(key);
_currentSizeBytes -= bytes.length;
}
}
}
/**
* [ByteStore] which does not store any data.
*/
class NullByteStore implements ByteStore {
@override
List<int> get(String key) => null;
@override
void put(String key, List<int> bytes) {}
}