/
dict.dart
167 lines (139 loc) · 4.14 KB
/
dict.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import 'dart:async';
import 'dart:ffi';
import 'package:cbl_ffi/cbl_ffi.dart';
import '../../support/utils.dart';
import '../decoder.dart';
import '../encoder.dart';
import 'collection.dart';
import 'value.dart';
class MDict extends MCollection {
MDict()
: _dict = null,
_values = {},
_length = 0,
_valuesHasAllKeys = true;
MDict.asCopy(MDict original, {bool? isMutable})
: _dict = original._dict,
_values = Map.fromEntries(original._values.entries
.map((entry) => MapEntry(entry.key, entry.value.clone()))),
_length = original._length,
_valuesHasAllKeys = original._valuesHasAllKeys,
super.asCopy(original, isMutable: isMutable ?? original.isMutable);
MDict.asChild(MValue slot, MCollection parent, {bool? isMutable})
:
// ignore: cast_nullable_to_non_nullable
_dict = (slot.value as CollectionFLValue).value.cast(),
_values = {},
// ignore: cast_nullable_to_non_nullable
_length = (slot.value as CollectionFLValue).length,
_valuesHasAllKeys = false,
super.asChild(
slot,
parent,
isMutable: isMutable ?? parent.hasMutableChildren,
);
final Pointer<FLDict>? _dict;
final Map<String, MValue> _values;
int _length;
/// Whether [_values] contains all the keys of [_dict].
bool _valuesHasAllKeys;
int get length => _length;
bool contains(String key) => _getValue(key).isNotEmpty;
MValue? get(String key) {
final value = _getValue(key);
return value.isNotEmpty ? value : null;
}
void set(String key, Object? native) {
assert(isMutable);
mutate();
final value = _getValue(key);
if (value.isEmpty) {
_length++;
}
value.setNative(native, this);
}
void remove(String key) {
assert(isMutable);
final value = _getValue(key);
if (value.isNotEmpty) {
mutate();
value.setEmpty(this);
_length--;
}
}
void clear() {
assert(isMutable);
if (_length == 0) {
return;
}
// Clear out all entires.
mutate();
for (final value in _values.values) {
value.removeFromParent();
}
_values.clear();
_length = 0;
// Shadow all keys in _dict with empty MValue.
if (!_valuesHasAllKeys) {
_valuesHasAllKeys = true;
for (final key in context!.decoder.dictKeyIterable(_dict!)) {
_values[key] = MValue.empty();
}
}
}
@override
FutureOr<void> performEncodeTo(FleeceEncoder encoder) {
if (!isMutated) {
encoder.writeValue(_dict!.cast());
} else {
return syncOrAsync(() sync* {
encoder.beginDict(length);
for (final entry in iterable) {
encoder.writeKey(entry.key);
if (entry.value.hasValue) {
encoder.writeLoadedValue(entry.value.value!);
} else {
yield entry.value.encodeTo(encoder);
}
}
encoder.endDict();
}());
}
}
@override
Iterable<MValue> get values => _values.values;
Iterable<MapEntry<String, MValue>> get iterable sync* {
// Iterate over entries in _values.
for (final entry in _values.entries) {
// Empty MValues represent that the entry was removed.
if (entry.value.isNotEmpty) {
yield entry;
}
}
// _values shadows all keys in _dict so there is no use in iterating _dict.
if (_valuesHasAllKeys) {
return;
}
// Iterate over entries in _dict.
for (final entry in context!.decoder.dictIterable(_dict!)) {
// Skip over entries which are shadowed by _values
if (_values.containsKey(entry.key)) {
continue;
}
// Cache the value to speed up lookups later.
final key = entry.key;
final value = _values[key] = MValue.withValue(entry.value);
yield MapEntry(key, value);
}
_valuesHasAllKeys = true;
}
MValue _getValue(String key) =>
_values[key] ??= (_loadValue(key) ?? MValue.empty())..updateParent(this);
MValue? _loadValue(String key) {
final dict = _dict;
if (dict == null) {
return null;
}
return context!.decoder.loadValueFromDict(dict, key)?.let(MValue.withValue);
}
}