| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:collection'; | 5 import 'dart:collection'; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * In-memory LRU cache for bytes. |
| 9 */ |
| 10 class BytesMemoryCache<K> { |
| 11 final int _maxSizeBytes; |
| 12 |
| 13 final _map = new LinkedHashMap<K, List<int>>(); |
| 14 int _currentSizeBytes = 0; |
| 15 |
| 16 BytesMemoryCache(this._maxSizeBytes); |
| 17 |
| 18 List<int> get(K key, List<int> getNotCached()) { |
| 19 List<int> bytes = _map.remove(key); |
| 20 if (bytes == null) { |
| 21 bytes = getNotCached(); |
| 22 if (bytes != null) { |
| 23 _map[key] = bytes; |
| 24 _currentSizeBytes += bytes.length; |
| 25 _evict(); |
| 26 } |
| 27 } else { |
| 28 _map[key] = bytes; |
| 29 } |
| 30 return bytes; |
| 31 } |
| 32 |
| 33 void put(K key, List<int> bytes) { |
| 34 _currentSizeBytes -= _map[key]?.length ?? 0; |
| 35 _map[key] = bytes; |
| 36 _currentSizeBytes += bytes.length; |
| 37 _evict(); |
| 38 } |
| 39 |
| 40 void _evict() { |
| 41 while (_currentSizeBytes > _maxSizeBytes) { |
| 42 if (_map.isEmpty) { |
| 43 // Should be impossible, since _currentSizeBytes should always match |
| 44 // _map. But recover anyway. |
| 45 assert(false); |
| 46 _currentSizeBytes = 0; |
| 47 break; |
| 48 } |
| 49 K key = _map.keys.first; |
| 50 List<int> bytes = _map.remove(key); |
| 51 _currentSizeBytes -= bytes.length; |
| 52 } |
| 53 } |
| 54 } |
| 55 |
| 56 /** |
| 8 * Store of bytes associated with string keys. | 57 * Store of bytes associated with string keys. |
| 9 * | 58 * |
| 10 * Each key must be not longer than 100 characters and consist of only `[a-z]`, | 59 * Each key must be not longer than 100 characters and consist of only `[a-z]`, |
| 11 * `[0-9]`, `.` and `_` characters. The key cannot be an empty string, the | 60 * `[0-9]`, `.` and `_` characters. The key cannot be an empty string, the |
| 12 * literal `.`, or contain the sequence `..`. | 61 * literal `.`, or contain the sequence `..`. |
| 13 * | 62 * |
| 14 * Note that associations are not guaranteed to be persistent. The value | 63 * Note that associations are not guaranteed to be persistent. The value |
| 15 * associated with a key can change or become `null` at any point in time. | 64 * associated with a key can change or become `null` at any point in time. |
| 16 * | 65 * |
| 17 * TODO(scheglov) Research using asynchronous API. | 66 * TODO(scheglov) Research using asynchronous API. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 44 void put(String key, List<int> bytes) { | 93 void put(String key, List<int> bytes) { |
| 45 _map[key] = bytes; | 94 _map[key] = bytes; |
| 46 } | 95 } |
| 47 } | 96 } |
| 48 | 97 |
| 49 /** | 98 /** |
| 50 * A wrapper around [ByteStore] which adds an in-memory LRU cache to it. | 99 * A wrapper around [ByteStore] which adds an in-memory LRU cache to it. |
| 51 */ | 100 */ |
| 52 class MemoryCachingByteStore implements ByteStore { | 101 class MemoryCachingByteStore implements ByteStore { |
| 53 final ByteStore _store; | 102 final ByteStore _store; |
| 54 final int _maxSizeBytes; | 103 final BytesMemoryCache<String> _cache; |
| 55 | 104 |
| 56 final _map = new LinkedHashMap<String, List<int>>(); | 105 MemoryCachingByteStore(this._store, int maxSizeBytes) |
| 57 int _currentSizeBytes = 0; | 106 : _cache = new BytesMemoryCache<String>(maxSizeBytes); |
| 58 | |
| 59 MemoryCachingByteStore(this._store, this._maxSizeBytes); | |
| 60 | 107 |
| 61 @override | 108 @override |
| 62 List<int> get(String key) { | 109 List<int> get(String key) { |
| 63 List<int> bytes = _map.remove(key); | 110 return _cache.get(key, () => _store.get(key)); |
| 64 if (bytes == null) { | |
| 65 bytes = _store.get(key); | |
| 66 if (bytes != null) { | |
| 67 _map[key] = bytes; | |
| 68 _currentSizeBytes += bytes.length; | |
| 69 _evict(); | |
| 70 } | |
| 71 } else { | |
| 72 _map[key] = bytes; | |
| 73 } | |
| 74 return bytes; | |
| 75 } | 111 } |
| 76 | 112 |
| 77 @override | 113 @override |
| 78 void put(String key, List<int> bytes) { | 114 void put(String key, List<int> bytes) { |
| 79 _store.put(key, bytes); | 115 _store.put(key, bytes); |
| 80 _currentSizeBytes -= _map[key]?.length ?? 0; | 116 _cache.put(key, bytes); |
| 81 _map[key] = bytes; | |
| 82 _currentSizeBytes += bytes.length; | |
| 83 _evict(); | |
| 84 } | |
| 85 | |
| 86 void _evict() { | |
| 87 while (_currentSizeBytes > _maxSizeBytes) { | |
| 88 if (_map.isEmpty) { | |
| 89 // Should be impossible, since _currentSizeBytes should always match | |
| 90 // _map. But recover anyway. | |
| 91 assert(false); | |
| 92 _currentSizeBytes = 0; | |
| 93 break; | |
| 94 } | |
| 95 String key = _map.keys.first; | |
| 96 List<int> bytes = _map.remove(key); | |
| 97 _currentSizeBytes -= bytes.length; | |
| 98 } | |
| 99 } | 117 } |
| 100 } | 118 } |
| 101 | 119 |
| 102 /** | 120 /** |
| 103 * [ByteStore] which does not store any data. | 121 * [ByteStore] which does not store any data. |
| 104 */ | 122 */ |
| 105 class NullByteStore implements ByteStore { | 123 class NullByteStore implements ByteStore { |
| 106 @override | 124 @override |
| 107 List<int> get(String key) => null; | 125 List<int> get(String key) => null; |
| 108 | 126 |
| 109 @override | 127 @override |
| 110 void put(String key, List<int> bytes) {} | 128 void put(String key, List<int> bytes) {} |
| 111 } | 129 } |
| OLD | NEW |