Skip to content

Commit

Permalink
Optimizations: General memoization
Browse files Browse the repository at this point in the history
  • Loading branch information
moffatman committed Feb 28, 2022
1 parent f9157f9 commit f8f6503
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 13 deletions.
73 changes: 73 additions & 0 deletions lib/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,79 @@ extension SafeWhere<T> on Iterable<T> {
T? tryLastWhere(bool Function(T v) f) => cast<T?>().lastWhere((v) => f(v!), orElse: () => null);
}

extension BinarySafeWhere<T> on List<T> {
int binarySearchTryFirstIndexWhere(bool Function(T v) f) {
int min = 0;
int max = length - 1;
while (min < max) {
final int mid = min + ((max - min) >> 1);
final T element = this[mid];
final T next = this[mid + 1];
final bool elementPasses = f(element);
final bool nextElementPasses = f(next);
if (!elementPasses && nextElementPasses) {
return mid + 1;
}
else if (elementPasses) {
max = mid;
}
else {
min = mid + 1;
}
}
print(first);
print(f(first));
if (f(first)) {
return 0;
}
else if (f(last)) {
return length - 1;
}
return -1;
}
T? binarySearchTryFirstWhere(bool Function(T v) f) {
final index = binarySearchTryFirstIndexWhere(f);
if (index == -1) {
return null;
}
return this[index];
}
int binarySearchTryLastIndexWhere(bool Function(T v) f) {
int min = 0;
int max = length - 1;
while (min < max) {
final int mid = min + ((max - min) >> 1);
final T element = this[mid];
final T next = this[mid + 1];
final bool elementPasses = f(element);
final bool nextElementPasses = f(next);
if (elementPasses && !nextElementPasses) {
return mid;
}
else if (elementPasses) {
min = mid + 1;
}
else {
max = mid;
}
}
if (f(last)) {
return length - 1;
}
else if (f(first)) {
return 0;
}
return -1;
}
T? binarySearchTryLastWhere(bool Function(T v) f) {
final index = binarySearchTryLastIndexWhere(f);
if (index == -1) {
return null;
}
return this[index];
}
}

class ExpiringMutexResource<T> {
final Future<T> Function() _initializer;
final Future Function(T resource) _deinitializer;
Expand Down
5 changes: 4 additions & 1 deletion lib/widgets/post_spans.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,12 @@ class PostNodeSpan extends PostSpan {
List<PostSpan> children;
PostNodeSpan(this.children);

final Map<String, List<int>> _referencedPostIds = {};
@override
List<int> referencedPostIds(String forBoard) {
return children.expand((child) => child.referencedPostIds(forBoard)).toList();
return _referencedPostIds.putIfAbsent(forBoard, () {
return children.expand((child) => child.referencedPostIds(forBoard)).toList();
});
}

@override
Expand Down
22 changes: 10 additions & 12 deletions lib/widgets/refreshable_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,6 @@ class RefreshableListController<T extends Filterable> {
final BehaviorSubject<void> _scrollStream = BehaviorSubject();
final BehaviorSubject<void> slowScrollUpdates = BehaviorSubject();
late final StreamSubscription<List<void>> _slowScrollSubscription;
int currentIndex = 0;
double? topOffset;
double? bottomOffset;
String? contentId;
Expand Down Expand Up @@ -683,18 +682,15 @@ class RefreshableListController<T extends Filterable> {
}
}
void _onSlowScroll(void update) {
int lastCached = -1;
for (final entry in _items.asMap().entries) {
if (entry.value.cachedOffset == null) {
_tryCachingItem(entry.key, entry.value);
if (entry.value.cachedOffset != null) {
lastCached = entry.key;
}
}
double? scrollableViewportHeight;
for (final item in _items) {
if (item.hasGoodState) {
scrollableViewportHeight ??= Scrollable.of(item.context!)!.position.pixels;
if (item.cachedOffset! - scrollableViewportHeight > 0) {
currentIndex = _items.indexOf(item);
}
for (int i = 0; i < lastCached; i++) {
if (_items[i].cachedOffset == null) {
_tryCachingItem(i, _items[i]);
}
}
}
Expand Down Expand Up @@ -732,7 +728,6 @@ class RefreshableListController<T extends Filterable> {
cb.completeError(Exception('page changed'));
}
_itemCacheCallbacks.clear();
currentIndex = 0;
}
void setItems(List<T> items) {
_items = items.map((item) => _RefreshableListItem(item)).toList();
Expand Down Expand Up @@ -826,7 +821,10 @@ class RefreshableListController<T extends Filterable> {
}
return -1;
}
T? get firstVisibleItem => firstVisibleIndex < 0 ? null : _items[firstVisibleIndex].item;
T? get firstVisibleItem {
final index = firstVisibleIndex;
return index < 0 ? null : _items[index].item;
}
T? get lastVisibleItem {
if (scrollController?.hasOnePosition ?? false) {
return _items.tryLastWhere((i) {
Expand Down
84 changes: 84 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
build:
dependency: transitive
description:
Expand Down Expand Up @@ -176,6 +183,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
cross_file:
dependency: transitive
description:
Expand Down Expand Up @@ -591,6 +605,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
node_preamble:
dependency: transitive
description:
name: node_preamble
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
package_config:
dependency: transitive
description:
Expand Down Expand Up @@ -794,6 +815,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
shelf_static:
dependency: transitive
description:
name: shelf_static
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
shelf_web_socket:
dependency: transitive
description:
Expand All @@ -820,6 +855,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.10"
source_span:
dependency: transitive
description:
Expand Down Expand Up @@ -862,6 +911,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test:
dependency: "direct main"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.20.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.11"
timing:
dependency: transitive
description:
Expand Down Expand Up @@ -1016,6 +1086,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "8.2.0"
watcher:
dependency: transitive
description:
Expand All @@ -1030,6 +1107,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
webview_flutter:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies:
uni_links: ^0.5.1
crypto: ^3.0.1
fluttertoast: ^8.0.8
test: ^1.20.1
flutter:
sdk: flutter

Expand Down
25 changes: 25 additions & 0 deletions test/test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:test/test.dart';
import 'package:chan/util.dart';

bool id(bool x) => x;

void main() {
group('BinarySearch', () {
test('firstwhere', () {
for (int length = 1; length < 90; length++) {
for (int switchpoint = 0; switchpoint <= length; switchpoint++) {
final List<bool> list = List.generate(length, (i) => i >= switchpoint);
expect(list.binarySearchTryFirstIndexWhere(id), switchpoint == length ? -1 : switchpoint);
}
}
});
test('lastwhere', () {
for (int length = 1; length < 90; length++) {
for (int switchpoint = 0; switchpoint <= length; switchpoint++) {
final List<bool> list = List.generate(length, (i) => i <= switchpoint);
expect(list.binarySearchTryLastIndexWhere(id), switchpoint == length ? length - 1 : switchpoint);
}
}
});
});
}

0 comments on commit f8f6503

Please sign in to comment.