75d1366dbd
Retains the existing code as fallback for settings without WeakMap for now. Fixes issue #5144 BUG= http://dartbug.com/5144 R=sigmund@google.com Review URL: https://codereview.chromium.org/1599393003 .
145 lines
3.8 KiB
Dart
145 lines
3.8 KiB
Dart
// Copyright (c) 2012, 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.
|
|
|
|
patch class Expando<T> {
|
|
/* patch */ Expando([String this.name])
|
|
: _data = new List(_minSize),
|
|
_used = 0;
|
|
|
|
static const _minSize = 8;
|
|
static final _deletedEntry = new _WeakProperty(null, null);
|
|
|
|
/* patch */ T operator[](Object object) {
|
|
_checkType(object);
|
|
|
|
var mask = _size - 1;
|
|
var idx = object._identityHashCode & mask;
|
|
var wp = _data[idx];
|
|
|
|
while (wp != null) {
|
|
if (identical(wp.key, object)) {
|
|
return wp.value;
|
|
} else if (wp.key == null) {
|
|
// This entry has been cleared by the GC.
|
|
_data[idx] = _deletedEntry;
|
|
}
|
|
idx = (idx + 1) & mask;
|
|
wp = _data[idx];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/* patch */ void operator[]=(Object object, T value) {
|
|
_checkType(object);
|
|
|
|
var mask = _size - 1;
|
|
var idx = object._identityHashCode & mask;
|
|
var empty_idx = -1;
|
|
var wp = _data[idx];
|
|
|
|
while (wp != null) {
|
|
if (identical(wp.key, object)) {
|
|
if (value != null) {
|
|
// Update the associated value.
|
|
wp.value = value;
|
|
} else {
|
|
// Mark the entry as deleted.
|
|
_data[idx] = _deletedEntry;
|
|
}
|
|
return;
|
|
} else if ((empty_idx < 0) && identical(wp, _deletedEntry)) {
|
|
empty_idx = idx; // Insert at this location if not found.
|
|
} else if (wp.key == null) {
|
|
// This entry has been cleared by the GC.
|
|
_data[idx] = _deletedEntry;
|
|
if (empty_idx < 0) {
|
|
empty_idx = idx; // Insert at this location if not found.
|
|
}
|
|
}
|
|
idx = (idx + 1) & mask;
|
|
wp = _data[idx];
|
|
}
|
|
|
|
if (value == null) {
|
|
// Not entering a null value. We just needed to make sure to clear an
|
|
// existing value if it existed.
|
|
return;
|
|
}
|
|
|
|
if (empty_idx >= 0) {
|
|
// We will be reusing the empty slot below.
|
|
_used--;
|
|
idx = empty_idx;
|
|
}
|
|
|
|
if (_used < _limit) {
|
|
_data[idx] = new _WeakProperty(object, value);
|
|
_used++;
|
|
return;
|
|
}
|
|
|
|
// Grow/reallocate if too many slots have been used.
|
|
_rehash();
|
|
this[object] = value; // Recursively add the value.
|
|
}
|
|
|
|
_rehash() {
|
|
// Determine the population count of the map to allocate an appropriately
|
|
// sized map below.
|
|
var count = 0;
|
|
var old_data = _data;
|
|
var len = old_data.length;
|
|
for (var i = 0; i < len; i++) {
|
|
var entry = old_data[i];
|
|
if ((entry != null) && (entry.key != null)) {
|
|
// Only count non-cleared entries.
|
|
count++;
|
|
}
|
|
}
|
|
|
|
var new_size = _size;
|
|
if (count <= (new_size >> 2)) {
|
|
new_size = new_size >> 1;
|
|
} else if (count > (new_size >> 1)) {
|
|
new_size = new_size << 1;
|
|
}
|
|
new_size = (new_size < _minSize) ? _minSize : new_size;
|
|
|
|
// Reset the mappings to empty so that we can just add the existing
|
|
// valid entries.
|
|
_data = new List(new_size);
|
|
_used = 0;
|
|
|
|
for (var i = 0; i < old_data.length; i++) {
|
|
var entry = old_data[i];
|
|
if (entry != null) {
|
|
// Ensure that the entry.key is not cleared between checking for it and
|
|
// inserting it into the new table.
|
|
var val = entry.value;
|
|
var key = entry.key;
|
|
if (key != null) {
|
|
this[key] = val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static _checkType(object) {
|
|
if ((object == null) ||
|
|
(object is bool) ||
|
|
(object is num) ||
|
|
(object is String)) {
|
|
throw new ArgumentError.value(object,
|
|
"Expandos are not allowed on strings, numbers, booleans or null");
|
|
}
|
|
}
|
|
|
|
get _size => _data.length;
|
|
get _limit => (3 * (_size ~/ 4));
|
|
|
|
List _data;
|
|
int _used; // Number of used (active and deleted) slots.
|
|
}
|