|
|
|
@ -7,13 +7,21 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
internal class ValueStore : IPriorityValueOwner |
|
|
|
{ |
|
|
|
private struct Entry |
|
|
|
{ |
|
|
|
internal int PropertyId; |
|
|
|
internal object Value; |
|
|
|
} |
|
|
|
|
|
|
|
private readonly AvaloniaObject _owner; |
|
|
|
private readonly Dictionary<AvaloniaProperty, object> _values = |
|
|
|
new Dictionary<AvaloniaProperty, object>(); |
|
|
|
private Entry[] _entries; |
|
|
|
|
|
|
|
public ValueStore(AvaloniaObject owner) |
|
|
|
{ |
|
|
|
_owner = owner; |
|
|
|
|
|
|
|
// The last item in the list is always int.MaxValue
|
|
|
|
_entries = new[] { new Entry { PropertyId = int.MaxValue, Value = null } }; |
|
|
|
} |
|
|
|
|
|
|
|
public IDisposable AddBinding( |
|
|
|
@ -23,7 +31,7 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
PriorityValue priorityValue; |
|
|
|
|
|
|
|
if (_values.TryGetValue(property, out var v)) |
|
|
|
if (TryGetValue(property, out var v)) |
|
|
|
{ |
|
|
|
priorityValue = v as PriorityValue; |
|
|
|
|
|
|
|
@ -31,13 +39,13 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
priorityValue = CreatePriorityValue(property); |
|
|
|
priorityValue.SetValue(v, (int)BindingPriority.LocalValue); |
|
|
|
_values[property] = priorityValue; |
|
|
|
SetValueInternal(property, priorityValue); |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
priorityValue = CreatePriorityValue(property); |
|
|
|
_values.Add(property, priorityValue); |
|
|
|
AddValueInternal(property, priorityValue); |
|
|
|
} |
|
|
|
|
|
|
|
return priorityValue.Add(source, (int)priority); |
|
|
|
@ -47,7 +55,7 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
PriorityValue priorityValue; |
|
|
|
|
|
|
|
if (_values.TryGetValue(property, out var v)) |
|
|
|
if (TryGetValue(property, out var v)) |
|
|
|
{ |
|
|
|
priorityValue = v as PriorityValue; |
|
|
|
|
|
|
|
@ -55,7 +63,7 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
if (priority == (int)BindingPriority.LocalValue) |
|
|
|
{ |
|
|
|
_values[property] = Validate(property, value); |
|
|
|
SetValueInternal(property, Validate(property, value)); |
|
|
|
Changed(property, priority, v, value); |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -63,7 +71,7 @@ namespace Avalonia |
|
|
|
{ |
|
|
|
priorityValue = CreatePriorityValue(property); |
|
|
|
priorityValue.SetValue(v, (int)BindingPriority.LocalValue); |
|
|
|
_values[property] = priorityValue; |
|
|
|
SetValueInternal(property, priorityValue); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -76,14 +84,14 @@ namespace Avalonia |
|
|
|
|
|
|
|
if (priority == (int)BindingPriority.LocalValue) |
|
|
|
{ |
|
|
|
_values.Add(property, Validate(property, value)); |
|
|
|
AddValueInternal(property, Validate(property, value)); |
|
|
|
Changed(property, priority, AvaloniaProperty.UnsetValue, value); |
|
|
|
return; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
priorityValue = CreatePriorityValue(property); |
|
|
|
_values.Add(property, priorityValue); |
|
|
|
AddValueInternal(property, priorityValue); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -100,13 +108,22 @@ namespace Avalonia |
|
|
|
_owner.PriorityValueChanged(property, priority, oldValue, newValue); |
|
|
|
} |
|
|
|
|
|
|
|
public IDictionary<AvaloniaProperty, object> GetSetValues() => _values; |
|
|
|
public IDictionary<AvaloniaProperty, object> GetSetValues() |
|
|
|
{ |
|
|
|
var dict = new Dictionary<AvaloniaProperty, object>(_entries.Length - 1); |
|
|
|
for (int i = 0; i < _entries.Length - 1; ++i) |
|
|
|
{ |
|
|
|
dict.Add(AvaloniaPropertyRegistry.Instance.FindRegistered(_entries[i].PropertyId), _entries[i].Value); |
|
|
|
} |
|
|
|
|
|
|
|
return dict; |
|
|
|
} |
|
|
|
|
|
|
|
public object GetValue(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
var result = AvaloniaProperty.UnsetValue; |
|
|
|
|
|
|
|
if (_values.TryGetValue(property, out var value)) |
|
|
|
if (TryGetValue(property, out var value)) |
|
|
|
{ |
|
|
|
result = (value is PriorityValue priorityValue) ? priorityValue.Value : value; |
|
|
|
} |
|
|
|
@ -116,12 +133,12 @@ namespace Avalonia |
|
|
|
|
|
|
|
public bool IsAnimating(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
return _values.TryGetValue(property, out var value) && value is PriorityValue priority && priority.IsAnimating; |
|
|
|
return TryGetValue(property, out var value) && value is PriorityValue priority && priority.IsAnimating; |
|
|
|
} |
|
|
|
|
|
|
|
public bool IsSet(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var value)) |
|
|
|
if (TryGetValue(property, out var value)) |
|
|
|
{ |
|
|
|
return ((value as PriorityValue)?.Value ?? value) != AvaloniaProperty.UnsetValue; |
|
|
|
} |
|
|
|
@ -131,7 +148,7 @@ namespace Avalonia |
|
|
|
|
|
|
|
public void Revalidate(AvaloniaProperty property) |
|
|
|
{ |
|
|
|
if (_values.TryGetValue(property, out var value)) |
|
|
|
if (TryGetValue(property, out var value)) |
|
|
|
{ |
|
|
|
(value as PriorityValue)?.Revalidate(); |
|
|
|
} |
|
|
|
@ -178,5 +195,103 @@ namespace Avalonia |
|
|
|
(_deferredSetter = new DeferredSetter<object>()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private bool TryGetValue(AvaloniaProperty property, out object value) |
|
|
|
{ |
|
|
|
(int index, bool found) = TryFindEntry(property.Id); |
|
|
|
if (!found) |
|
|
|
{ |
|
|
|
value = null; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
value = _entries[index].Value; |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
private void AddValueInternal(AvaloniaProperty property, object value) |
|
|
|
{ |
|
|
|
Entry[] entries = new Entry[_entries.Length + 1]; |
|
|
|
|
|
|
|
for (int i = 0; i < _entries.Length; ++i) |
|
|
|
{ |
|
|
|
if (_entries[i].PropertyId > property.Id) |
|
|
|
{ |
|
|
|
if (i > 0) |
|
|
|
{ |
|
|
|
Array.Copy(_entries, 0, entries, 0, i); |
|
|
|
} |
|
|
|
|
|
|
|
entries[i] = new Entry { PropertyId = property.Id, Value = value }; |
|
|
|
Array.Copy(_entries, i, entries, i + 1, _entries.Length - i); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_entries = entries; |
|
|
|
} |
|
|
|
|
|
|
|
private void SetValueInternal(AvaloniaProperty property, object value) |
|
|
|
{ |
|
|
|
_entries[TryFindEntry(property.Id).Item1].Value = value; |
|
|
|
} |
|
|
|
|
|
|
|
private (int, bool) TryFindEntry(int propertyId) |
|
|
|
{ |
|
|
|
if (_entries.Length <= 12) |
|
|
|
{ |
|
|
|
// For small lists, we use an optimized linear search. Since the last item in the list
|
|
|
|
// is always int.MaxValue, we can skip a conditional branch in each iteration.
|
|
|
|
// By unrolling the loop, we can skip another unconditional branch in each iteration.
|
|
|
|
|
|
|
|
if (_entries[0].PropertyId >= propertyId) return (0, _entries[0].PropertyId == propertyId); |
|
|
|
if (_entries[1].PropertyId >= propertyId) return (1, _entries[1].PropertyId == propertyId); |
|
|
|
if (_entries[2].PropertyId >= propertyId) return (2, _entries[2].PropertyId == propertyId); |
|
|
|
if (_entries[3].PropertyId >= propertyId) return (3, _entries[3].PropertyId == propertyId); |
|
|
|
if (_entries[4].PropertyId >= propertyId) return (4, _entries[4].PropertyId == propertyId); |
|
|
|
if (_entries[5].PropertyId >= propertyId) return (5, _entries[5].PropertyId == propertyId); |
|
|
|
if (_entries[6].PropertyId >= propertyId) return (6, _entries[6].PropertyId == propertyId); |
|
|
|
if (_entries[7].PropertyId >= propertyId) return (7, _entries[7].PropertyId == propertyId); |
|
|
|
if (_entries[8].PropertyId >= propertyId) return (8, _entries[8].PropertyId == propertyId); |
|
|
|
if (_entries[9].PropertyId >= propertyId) return (9, _entries[9].PropertyId == propertyId); |
|
|
|
if (_entries[10].PropertyId >= propertyId) return (10, _entries[10].PropertyId == propertyId); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
int low = 0; |
|
|
|
int high = _entries.Length; |
|
|
|
int id; |
|
|
|
|
|
|
|
while (high - low > 3) |
|
|
|
{ |
|
|
|
int pivot = (high + low) / 2; |
|
|
|
id = _entries[pivot].PropertyId; |
|
|
|
|
|
|
|
if (propertyId == id) |
|
|
|
return (pivot, true); |
|
|
|
|
|
|
|
if (propertyId <= id) |
|
|
|
high = pivot; |
|
|
|
else |
|
|
|
low = pivot + 1; |
|
|
|
} |
|
|
|
|
|
|
|
do |
|
|
|
{ |
|
|
|
id = _entries[low].PropertyId; |
|
|
|
|
|
|
|
if (id == propertyId) |
|
|
|
return (low, true); |
|
|
|
|
|
|
|
if (id > propertyId) |
|
|
|
break; |
|
|
|
|
|
|
|
++low; |
|
|
|
} |
|
|
|
while (low < high); |
|
|
|
} |
|
|
|
|
|
|
|
return (0, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|