mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-07-24 15:05:22 -04:00
debugging
This commit is contained in:
parent
f780a60d69
commit
0e4606f35e
20 changed files with 521 additions and 321 deletions
|
@ -12,22 +12,25 @@ import '../../../veilid_support.dart';
|
|||
@immutable
|
||||
class DHTLogStateData<T> extends Equatable {
|
||||
const DHTLogStateData(
|
||||
{required this.elements,
|
||||
required this.tail,
|
||||
required this.count,
|
||||
{required this.length,
|
||||
required this.window,
|
||||
required this.windowTail,
|
||||
required this.windowSize,
|
||||
required this.follow});
|
||||
// The view of the elements in the dhtlog
|
||||
// Span is from [tail-length, tail)
|
||||
final IList<OnlineElementState<T>> elements;
|
||||
// One past the end of the last element
|
||||
final int tail;
|
||||
// The total number of elements to try to keep in 'elements'
|
||||
final int count;
|
||||
// If we should have the tail following the log
|
||||
// The total number of elements in the whole log
|
||||
final int length;
|
||||
// The view window of the elements in the dhtlog
|
||||
// Span is from [tail - window.length, tail)
|
||||
final IList<OnlineElementState<T>> window;
|
||||
// The position of the view window, one past the last element
|
||||
final int windowTail;
|
||||
// The total number of elements to try to keep in the window
|
||||
final int windowSize;
|
||||
// If we have the window following the log
|
||||
final bool follow;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [elements, tail, count, follow];
|
||||
List<Object?> get props => [length, window, windowTail, windowSize, follow];
|
||||
}
|
||||
|
||||
typedef DHTLogState<T> = AsyncValue<DHTLogStateData<T>>;
|
||||
|
@ -58,13 +61,16 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||
// If tail is positive, the position is absolute from the head of the log
|
||||
// If follow is enabled, the tail offset will update when the log changes
|
||||
Future<void> setWindow(
|
||||
{int? tail, int? count, bool? follow, bool forceRefresh = false}) async {
|
||||
{int? windowTail,
|
||||
int? windowSize,
|
||||
bool? follow,
|
||||
bool forceRefresh = false}) async {
|
||||
await _initWait();
|
||||
if (tail != null) {
|
||||
_tail = tail;
|
||||
if (windowTail != null) {
|
||||
_windowTail = windowTail;
|
||||
}
|
||||
if (count != null) {
|
||||
_count = count;
|
||||
if (windowSize != null) {
|
||||
_windowSize = windowSize;
|
||||
}
|
||||
if (follow != null) {
|
||||
_follow = follow;
|
||||
|
@ -82,8 +88,13 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||
|
||||
Future<void> _refreshInner(void Function(AsyncValue<DHTLogStateData<T>>) emit,
|
||||
{bool forceRefresh = false}) async {
|
||||
final avElements = await operate(
|
||||
(reader) => loadElementsFromReader(reader, _tail, _count));
|
||||
late final AsyncValue<IList<OnlineElementState<T>>> avElements;
|
||||
late final int length;
|
||||
await _log.operate((reader) async {
|
||||
length = reader.length;
|
||||
avElements =
|
||||
await loadElementsFromReader(reader, _windowTail, _windowSize);
|
||||
});
|
||||
final err = avElements.asError;
|
||||
if (err != null) {
|
||||
emit(AsyncValue.error(err.error, err.stackTrace));
|
||||
|
@ -94,9 +105,13 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||
emit(const AsyncValue.loading());
|
||||
return;
|
||||
}
|
||||
final elements = avElements.asData!.value;
|
||||
final window = avElements.asData!.value;
|
||||
emit(AsyncValue.data(DHTLogStateData(
|
||||
elements: elements, tail: _tail, count: _count, follow: _follow)));
|
||||
length: length,
|
||||
window: window,
|
||||
windowTail: _windowTail,
|
||||
windowSize: _windowSize,
|
||||
follow: _follow)));
|
||||
}
|
||||
|
||||
// Tail is one past the last element to load
|
||||
|
@ -105,6 +120,9 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||
{bool forceRefresh = false}) async {
|
||||
try {
|
||||
final length = reader.length;
|
||||
if (length == 0) {
|
||||
return const AsyncValue.data(IList.empty());
|
||||
}
|
||||
final end = ((tail - 1) % length) + 1;
|
||||
final start = (count < end) ? end - count : 0;
|
||||
|
||||
|
@ -138,18 +156,18 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||
_sspUpdate.busyUpdate<T, DHTLogState<T>>(busy, (emit) async {
|
||||
// apply follow
|
||||
if (_follow) {
|
||||
if (_tail <= 0) {
|
||||
if (_windowTail <= 0) {
|
||||
// Negative tail is already following tail changes
|
||||
} else {
|
||||
// Positive tail is measured from the head, so apply deltas
|
||||
_tail = (_tail + _tailDelta - _headDelta) % upd.length;
|
||||
_windowTail = (_windowTail + _tailDelta - _headDelta) % upd.length;
|
||||
}
|
||||
} else {
|
||||
if (_tail <= 0) {
|
||||
if (_windowTail <= 0) {
|
||||
// Negative tail is following tail changes so apply deltas
|
||||
var posTail = _tail + upd.length;
|
||||
var posTail = _windowTail + upd.length;
|
||||
posTail = (posTail + _tailDelta - _headDelta) % upd.length;
|
||||
_tail = posTail - upd.length;
|
||||
_windowTail = posTail - upd.length;
|
||||
} else {
|
||||
// Positive tail is measured from head so not following tail
|
||||
}
|
||||
|
@ -202,7 +220,7 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||
var _tailDelta = 0;
|
||||
|
||||
// Cubit window into the DHTLog
|
||||
var _tail = 0;
|
||||
var _count = DHTShortArray.maxElements;
|
||||
var _windowTail = 0;
|
||||
var _windowSize = DHTShortArray.maxElements;
|
||||
var _follow = true;
|
||||
}
|
||||
|
|
|
@ -451,48 +451,53 @@ class _DHTLogSpine {
|
|||
///////////////////////////////////////////
|
||||
// API for public interfaces
|
||||
|
||||
Future<_DHTLogPosition?> lookupPosition(int pos) async {
|
||||
assert(_spineMutex.isLocked, 'should be locked');
|
||||
return _spineCacheMutex.protect(() async {
|
||||
// Check if our position is in bounds
|
||||
final endPos = length;
|
||||
if (pos < 0 || pos >= endPos) {
|
||||
throw IndexError.withLength(pos, endPos);
|
||||
}
|
||||
Future<_DHTLogPosition?> lookupPositionBySegmentNumber(
|
||||
int segmentNumber, int segmentPos) async =>
|
||||
_spineCacheMutex.protect(() async {
|
||||
// Get the segment shortArray
|
||||
final openedSegment = _openedSegments[segmentNumber];
|
||||
late final DHTShortArray shortArray;
|
||||
if (openedSegment != null) {
|
||||
openedSegment.openCount++;
|
||||
shortArray = openedSegment.shortArray;
|
||||
} else {
|
||||
final newShortArray = (_spineRecord.writer == null)
|
||||
? await _openSegment(segmentNumber)
|
||||
: await _openOrCreateSegment(segmentNumber);
|
||||
if (newShortArray == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate absolute position, ring-buffer style
|
||||
final absolutePosition = (_head + pos) % _positionLimit;
|
||||
_openedSegments[segmentNumber] =
|
||||
_OpenedSegment._(shortArray: newShortArray);
|
||||
|
||||
// Determine the segment number and position within the segment
|
||||
final segmentNumber = absolutePosition ~/ DHTShortArray.maxElements;
|
||||
final segmentPos = absolutePosition % DHTShortArray.maxElements;
|
||||
|
||||
// Get the segment shortArray
|
||||
final openedSegment = _openedSegments[segmentNumber];
|
||||
late final DHTShortArray shortArray;
|
||||
if (openedSegment != null) {
|
||||
openedSegment.openCount++;
|
||||
shortArray = openedSegment.shortArray;
|
||||
} else {
|
||||
final newShortArray = (_spineRecord.writer == null)
|
||||
? await _openSegment(segmentNumber)
|
||||
: await _openOrCreateSegment(segmentNumber);
|
||||
if (newShortArray == null) {
|
||||
return null;
|
||||
shortArray = newShortArray;
|
||||
}
|
||||
|
||||
_openedSegments[segmentNumber] =
|
||||
_OpenedSegment._(shortArray: newShortArray);
|
||||
return _DHTLogPosition._(
|
||||
dhtLogSpine: this,
|
||||
shortArray: shortArray,
|
||||
pos: segmentPos,
|
||||
segmentNumber: segmentNumber);
|
||||
});
|
||||
|
||||
shortArray = newShortArray;
|
||||
}
|
||||
Future<_DHTLogPosition?> lookupPosition(int pos) async {
|
||||
assert(_spineMutex.isLocked, 'should be locked');
|
||||
|
||||
return _DHTLogPosition._(
|
||||
dhtLogSpine: this,
|
||||
shortArray: shortArray,
|
||||
pos: segmentPos,
|
||||
segmentNumber: segmentNumber);
|
||||
});
|
||||
// Check if our position is in bounds
|
||||
final endPos = length;
|
||||
if (pos < 0 || pos >= endPos) {
|
||||
throw IndexError.withLength(pos, endPos);
|
||||
}
|
||||
|
||||
// Calculate absolute position, ring-buffer style
|
||||
final absolutePosition = (_head + pos) % _positionLimit;
|
||||
|
||||
// Determine the segment number and position within the segment
|
||||
final segmentNumber = absolutePosition ~/ DHTShortArray.maxElements;
|
||||
final segmentPos = absolutePosition % DHTShortArray.maxElements;
|
||||
|
||||
return lookupPositionBySegmentNumber(segmentNumber, segmentPos);
|
||||
}
|
||||
|
||||
Future<void> _segmentClosed(int segmentNumber) async {
|
||||
|
@ -660,6 +665,34 @@ class _DHTLogSpine {
|
|||
final oldHead = _head;
|
||||
final oldTail = _tail;
|
||||
await _updateHead(headData);
|
||||
|
||||
// Lookup tail position segments that have changed
|
||||
// and force their short arrays to refresh their heads
|
||||
final segmentsToRefresh = <_DHTLogPosition>[];
|
||||
int? lastSegmentNumber;
|
||||
for (var curTail = oldTail;
|
||||
curTail != _tail;
|
||||
curTail = (curTail + 1) % _positionLimit) {
|
||||
final segmentNumber = curTail ~/ DHTShortArray.maxElements;
|
||||
final segmentPos = curTail % DHTShortArray.maxElements;
|
||||
if (segmentNumber == lastSegmentNumber) {
|
||||
continue;
|
||||
}
|
||||
lastSegmentNumber = segmentNumber;
|
||||
final dhtLogPosition =
|
||||
await lookupPositionBySegmentNumber(segmentNumber, segmentPos);
|
||||
if (dhtLogPosition == null) {
|
||||
throw Exception('missing segment in dht log');
|
||||
}
|
||||
segmentsToRefresh.add(dhtLogPosition);
|
||||
}
|
||||
|
||||
// Refresh the segments that have probably changed
|
||||
await segmentsToRefresh.map((p) async {
|
||||
await p.shortArray.refresh();
|
||||
await p.close();
|
||||
}).wait;
|
||||
|
||||
sendUpdate(oldHead, oldTail);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -185,6 +185,17 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||
/// Get the record pointer foir this shortarray
|
||||
OwnedDHTRecordPointer get recordPointer => _head.recordPointer;
|
||||
|
||||
/// Refresh this DHTShortArray
|
||||
/// Useful if you aren't 'watching' the array and want to poll for an update
|
||||
Future<void> refresh() async {
|
||||
if (!isOpen) {
|
||||
throw StateError('short array is not open"');
|
||||
}
|
||||
await _head.operate((head) async {
|
||||
await head._loadHead();
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs a closure allowing read-only access to the shortarray
|
||||
Future<T> operate<T>(
|
||||
Future<T> Function(DHTShortArrayReadOperations) closure) async {
|
||||
|
|
|
@ -23,8 +23,8 @@ class TableDBArrayUpdate extends Equatable {
|
|||
List<Object?> get props => [headDelta, tailDelta, length];
|
||||
}
|
||||
|
||||
class TableDBArray {
|
||||
TableDBArray({
|
||||
class _TableDBArrayBase {
|
||||
_TableDBArrayBase({
|
||||
required String table,
|
||||
required VeilidCrypto crypto,
|
||||
}) : _table = table,
|
||||
|
@ -32,14 +32,14 @@ class TableDBArray {
|
|||
_initWait.add(_init);
|
||||
}
|
||||
|
||||
static Future<TableDBArray> make({
|
||||
required String table,
|
||||
required VeilidCrypto crypto,
|
||||
}) async {
|
||||
final out = TableDBArray(table: table, crypto: crypto);
|
||||
await out._initWait();
|
||||
return out;
|
||||
}
|
||||
// static Future<TableDBArray> make({
|
||||
// required String table,
|
||||
// required VeilidCrypto crypto,
|
||||
// }) async {
|
||||
// final out = TableDBArray(table: table, crypto: crypto);
|
||||
// await out._initWait();
|
||||
// return out;
|
||||
// }
|
||||
|
||||
Future<void> initWait() async {
|
||||
await _initWait();
|
||||
|
@ -99,27 +99,27 @@ class TableDBArray {
|
|||
|
||||
bool get isOpen => _open;
|
||||
|
||||
Future<void> add(Uint8List value) async {
|
||||
Future<void> _add(Uint8List value) async {
|
||||
await _initWait();
|
||||
return _writeTransaction((t) async => _addInner(t, value));
|
||||
}
|
||||
|
||||
Future<void> addAll(List<Uint8List> values) async {
|
||||
Future<void> _addAll(List<Uint8List> values) async {
|
||||
await _initWait();
|
||||
return _writeTransaction((t) async => _addAllInner(t, values));
|
||||
}
|
||||
|
||||
Future<void> insert(int pos, Uint8List value) async {
|
||||
Future<void> _insert(int pos, Uint8List value) async {
|
||||
await _initWait();
|
||||
return _writeTransaction((t) async => _insertInner(t, pos, value));
|
||||
}
|
||||
|
||||
Future<void> insertAll(int pos, List<Uint8List> values) async {
|
||||
Future<void> _insertAll(int pos, List<Uint8List> values) async {
|
||||
await _initWait();
|
||||
return _writeTransaction((t) async => _insertAllInner(t, pos, values));
|
||||
}
|
||||
|
||||
Future<Uint8List> get(int pos) async {
|
||||
Future<Uint8List> _get(int pos) async {
|
||||
await _initWait();
|
||||
return _mutex.protect(() async {
|
||||
if (!_open) {
|
||||
|
@ -129,7 +129,7 @@ class TableDBArray {
|
|||
});
|
||||
}
|
||||
|
||||
Future<List<Uint8List>> getRange(int start, [int? end]) async {
|
||||
Future<List<Uint8List>> _getRange(int start, [int? end]) async {
|
||||
await _initWait();
|
||||
return _mutex.protect(() async {
|
||||
if (!_open) {
|
||||
|
@ -139,12 +139,12 @@ class TableDBArray {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> remove(int pos, {Output<Uint8List>? out}) async {
|
||||
Future<void> _remove(int pos, {Output<Uint8List>? out}) async {
|
||||
await _initWait();
|
||||
return _writeTransaction((t) async => _removeInner(t, pos, out: out));
|
||||
}
|
||||
|
||||
Future<void> removeRange(int start, int end,
|
||||
Future<void> _removeRange(int start, int end,
|
||||
{Output<List<Uint8List>>? out}) async {
|
||||
await _initWait();
|
||||
return _writeTransaction(
|
||||
|
@ -374,7 +374,9 @@ class TableDBArray {
|
|||
|
||||
Future<Uint8List?> _loadEntry(int entry) async {
|
||||
final encryptedValue = await _tableDB.load(0, _entryKey(entry));
|
||||
return (encryptedValue == null) ? null : _crypto.decrypt(encryptedValue);
|
||||
return (encryptedValue == null)
|
||||
? null
|
||||
: await _crypto.decrypt(encryptedValue);
|
||||
}
|
||||
|
||||
Future<int> _getIndexEntry(int pos) async {
|
||||
|
@ -631,77 +633,170 @@ class TableDBArray {
|
|||
StreamController.broadcast();
|
||||
}
|
||||
|
||||
extension TableDBArrayExt on TableDBArray {
|
||||
/// Convenience function:
|
||||
/// Like get but also parses the returned element as JSON
|
||||
Future<T?> getJson<T>(
|
||||
T Function(dynamic) fromJson,
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TableDBArray extends _TableDBArrayBase {
|
||||
TableDBArray({
|
||||
required super.table,
|
||||
required super.crypto,
|
||||
});
|
||||
|
||||
static Future<TableDBArray> make({
|
||||
required String table,
|
||||
required VeilidCrypto crypto,
|
||||
}) async {
|
||||
final out = TableDBArray(table: table, crypto: crypto);
|
||||
await out._initWait();
|
||||
return out;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Public interface
|
||||
|
||||
Future<void> add(Uint8List value) => _add(value);
|
||||
|
||||
Future<void> addAll(List<Uint8List> values) => _addAll(values);
|
||||
|
||||
Future<void> insert(int pos, Uint8List value) => _insert(pos, value);
|
||||
|
||||
Future<void> insertAll(int pos, List<Uint8List> values) =>
|
||||
_insertAll(pos, values);
|
||||
|
||||
Future<Uint8List?> get(
|
||||
int pos,
|
||||
) =>
|
||||
get(
|
||||
pos,
|
||||
).then((out) => jsonDecodeOptBytes(fromJson, out));
|
||||
_get(pos);
|
||||
|
||||
/// Convenience function:
|
||||
/// Like getRange but also parses the returned elements as JSON
|
||||
Future<List<T>?> getRangeJson<T>(T Function(dynamic) fromJson, int start,
|
||||
[int? end]) =>
|
||||
getRange(start, end ?? _length).then((out) => out.map(fromJson).toList());
|
||||
Future<List<Uint8List>> getRange(int start, [int? end]) =>
|
||||
_getRange(start, end);
|
||||
|
||||
/// Convenience function:
|
||||
/// Like get but also parses the returned element as a protobuf object
|
||||
Future<T?> getProtobuf<T extends GeneratedMessage>(
|
||||
T Function(List<int>) fromBuffer,
|
||||
int pos,
|
||||
) =>
|
||||
get(pos).then(fromBuffer);
|
||||
Future<void> remove(int pos, {Output<Uint8List>? out}) =>
|
||||
_remove(pos, out: out);
|
||||
|
||||
/// Convenience function:
|
||||
/// Like getRange but also parses the returned elements as protobuf objects
|
||||
Future<List<T>?> getRangeProtobuf<T extends GeneratedMessage>(
|
||||
T Function(List<int>) fromBuffer, int start, [int? end]) =>
|
||||
getRange(start, end ?? _length)
|
||||
.then((out) => out.map(fromBuffer).toList());
|
||||
|
||||
/// Convenience function:
|
||||
/// Like add but for a JSON value
|
||||
Future<void> addJson<T>(T value) async => add(jsonEncodeBytes(value));
|
||||
|
||||
/// Convenience function:
|
||||
/// Like add but for a Protobuf value
|
||||
Future<void> addProtobuf<T extends GeneratedMessage>(T value) =>
|
||||
add(value.writeToBuffer());
|
||||
|
||||
/// Convenience function:
|
||||
/// Like addAll but for a JSON value
|
||||
Future<void> addAllJson<T>(List<T> values) async =>
|
||||
addAll(values.map(jsonEncodeBytes).toList());
|
||||
|
||||
/// Convenience function:
|
||||
/// Like addAll but for a Protobuf value
|
||||
Future<void> addAllProtobuf<T extends GeneratedMessage>(
|
||||
List<T> values) async =>
|
||||
addAll(values.map((x) => x.writeToBuffer()).toList());
|
||||
|
||||
/// Convenience function:
|
||||
/// Like insert but for a JSON value
|
||||
Future<void> insertJson<T>(int pos, T value) async =>
|
||||
insert(pos, jsonEncodeBytes(value));
|
||||
|
||||
/// Convenience function:
|
||||
/// Like insert but for a Protobuf value
|
||||
Future<void> insertProtobuf<T extends GeneratedMessage>(
|
||||
int pos, T value) async =>
|
||||
insert(pos, value.writeToBuffer());
|
||||
|
||||
/// Convenience function:
|
||||
/// Like insertAll but for a JSON value
|
||||
Future<void> insertAllJson<T>(int pos, List<T> values) async =>
|
||||
insertAll(pos, values.map(jsonEncodeBytes).toList());
|
||||
|
||||
/// Convenience function:
|
||||
/// Like insertAll but for a Protobuf value
|
||||
Future<void> insertAllProtobuf<T extends GeneratedMessage>(
|
||||
int pos, List<T> values) async =>
|
||||
insertAll(pos, values.map((x) => x.writeToBuffer()).toList());
|
||||
Future<void> removeRange(int start, int end,
|
||||
{Output<List<Uint8List>>? out}) =>
|
||||
_removeRange(start, end, out: out);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TableDBArrayJson<T> extends _TableDBArrayBase {
|
||||
TableDBArrayJson(
|
||||
{required super.table,
|
||||
required super.crypto,
|
||||
required T Function(dynamic) fromJson})
|
||||
: _fromJson = fromJson;
|
||||
|
||||
static Future<TableDBArrayJson<T>> make<T>(
|
||||
{required String table,
|
||||
required VeilidCrypto crypto,
|
||||
required T Function(dynamic) fromJson}) async {
|
||||
final out =
|
||||
TableDBArrayJson<T>(table: table, crypto: crypto, fromJson: fromJson);
|
||||
await out._initWait();
|
||||
return out;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Public interface
|
||||
|
||||
Future<void> add(T value) => _add(jsonEncodeBytes(value));
|
||||
|
||||
Future<void> addAll(List<T> values) async =>
|
||||
_addAll(values.map(jsonEncodeBytes).toList());
|
||||
|
||||
Future<void> insert(int pos, T value) async =>
|
||||
_insert(pos, jsonEncodeBytes(value));
|
||||
|
||||
Future<void> insertAll(int pos, List<T> values) async =>
|
||||
_insertAll(pos, values.map(jsonEncodeBytes).toList());
|
||||
|
||||
Future<T?> get(
|
||||
int pos,
|
||||
) =>
|
||||
_get(pos).then((out) => jsonDecodeOptBytes(_fromJson, out));
|
||||
|
||||
Future<List<T>> getRange(int start, [int? end]) =>
|
||||
_getRange(start, end).then((out) => out.map(_fromJson).toList());
|
||||
|
||||
Future<void> remove(int pos, {Output<T>? out}) async {
|
||||
final outJson = (out != null) ? Output<Uint8List>() : null;
|
||||
await _remove(pos, out: outJson);
|
||||
if (outJson != null && outJson.value != null) {
|
||||
out!.save(jsonDecodeBytes(_fromJson, outJson.value!));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeRange(int start, int end, {Output<List<T>>? out}) async {
|
||||
final outJson = (out != null) ? Output<List<Uint8List>>() : null;
|
||||
await _removeRange(start, end, out: outJson);
|
||||
if (outJson != null && outJson.value != null) {
|
||||
out!.save(
|
||||
outJson.value!.map((x) => jsonDecodeBytes(_fromJson, x)).toList());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
final T Function(dynamic) _fromJson;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TableDBArrayProtobuf<T extends GeneratedMessage>
|
||||
extends _TableDBArrayBase {
|
||||
TableDBArrayProtobuf(
|
||||
{required super.table,
|
||||
required super.crypto,
|
||||
required T Function(List<int>) fromBuffer})
|
||||
: _fromBuffer = fromBuffer;
|
||||
|
||||
static Future<TableDBArrayProtobuf<T>> make<T extends GeneratedMessage>(
|
||||
{required String table,
|
||||
required VeilidCrypto crypto,
|
||||
required T Function(List<int>) fromBuffer}) async {
|
||||
final out = TableDBArrayProtobuf<T>(
|
||||
table: table, crypto: crypto, fromBuffer: fromBuffer);
|
||||
await out._initWait();
|
||||
return out;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Public interface
|
||||
|
||||
Future<void> add(T value) => _add(value.writeToBuffer());
|
||||
|
||||
Future<void> addAll(List<T> values) async =>
|
||||
_addAll(values.map((x) => x.writeToBuffer()).toList());
|
||||
|
||||
Future<void> insert(int pos, T value) async =>
|
||||
_insert(pos, value.writeToBuffer());
|
||||
|
||||
Future<void> insertAll(int pos, List<T> values) async =>
|
||||
_insertAll(pos, values.map((x) => x.writeToBuffer()).toList());
|
||||
|
||||
Future<T?> get(
|
||||
int pos,
|
||||
) =>
|
||||
_get(pos).then(_fromBuffer);
|
||||
|
||||
Future<List<T>> getRange(int start, [int? end]) =>
|
||||
_getRange(start, end).then((out) => out.map(_fromBuffer).toList());
|
||||
|
||||
Future<void> remove(int pos, {Output<T>? out}) async {
|
||||
final outProto = (out != null) ? Output<Uint8List>() : null;
|
||||
await _remove(pos, out: outProto);
|
||||
if (outProto != null && outProto.value != null) {
|
||||
out!.save(_fromBuffer(outProto.value!));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeRange(int start, int end, {Output<List<T>>? out}) async {
|
||||
final outProto = (out != null) ? Output<List<Uint8List>>() : null;
|
||||
await _removeRange(start, end, out: outProto);
|
||||
if (outProto != null && outProto.value != null) {
|
||||
out!.save(outProto.value!.map(_fromBuffer).toList());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
final T Function(List<int>) _fromBuffer;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
|
||||
import '../../../veilid_support.dart';
|
||||
|
||||
@immutable
|
||||
class TableDBArrayStateData<T> extends Equatable {
|
||||
const TableDBArrayStateData(
|
||||
class TableDBArrayProtobufStateData<T extends GeneratedMessage>
|
||||
extends Equatable {
|
||||
const TableDBArrayProtobufStateData(
|
||||
{required this.elements,
|
||||
required this.tail,
|
||||
required this.count,
|
||||
|
@ -30,16 +32,17 @@ class TableDBArrayStateData<T> extends Equatable {
|
|||
List<Object?> get props => [elements, tail, count, follow];
|
||||
}
|
||||
|
||||
typedef TableDBArrayState<T> = AsyncValue<TableDBArrayStateData<T>>;
|
||||
typedef TableDBArrayBusyState<T> = BlocBusyState<TableDBArrayState<T>>;
|
||||
typedef TableDBArrayProtobufState<T extends GeneratedMessage>
|
||||
= AsyncValue<TableDBArrayProtobufStateData<T>>;
|
||||
typedef TableDBArrayProtobufBusyState<T extends GeneratedMessage>
|
||||
= BlocBusyState<TableDBArrayProtobufState<T>>;
|
||||
|
||||
class TableDBArrayCubit<T> extends Cubit<TableDBArrayBusyState<T>>
|
||||
with BlocBusyWrapper<TableDBArrayState<T>> {
|
||||
TableDBArrayCubit({
|
||||
required Future<TableDBArray> Function() open,
|
||||
required T Function(List<int> data) decodeElement,
|
||||
}) : _decodeElement = decodeElement,
|
||||
super(const BlocBusyState(AsyncValue.loading())) {
|
||||
class TableDBArrayProtobufCubit<T extends GeneratedMessage>
|
||||
extends Cubit<TableDBArrayProtobufBusyState<T>>
|
||||
with BlocBusyWrapper<TableDBArrayProtobufState<T>> {
|
||||
TableDBArrayProtobufCubit({
|
||||
required Future<TableDBArrayProtobuf<T>> Function() open,
|
||||
}) : super(const BlocBusyState(AsyncValue.loading())) {
|
||||
_initWait.add(() async {
|
||||
// Open table db array
|
||||
_array = await open();
|
||||
|
@ -81,7 +84,7 @@ class TableDBArrayCubit<T> extends Cubit<TableDBArrayBusyState<T>>
|
|||
busy((emit) async => _refreshInner(emit, forceRefresh: forceRefresh));
|
||||
|
||||
Future<void> _refreshInner(
|
||||
void Function(AsyncValue<TableDBArrayStateData<T>>) emit,
|
||||
void Function(AsyncValue<TableDBArrayProtobufStateData<T>>) emit,
|
||||
{bool forceRefresh = false}) async {
|
||||
final avElements = await _loadElements(_tail, _count);
|
||||
final err = avElements.asError;
|
||||
|
@ -95,7 +98,7 @@ class TableDBArrayCubit<T> extends Cubit<TableDBArrayBusyState<T>>
|
|||
return;
|
||||
}
|
||||
final elements = avElements.asData!.value;
|
||||
emit(AsyncValue.data(TableDBArrayStateData(
|
||||
emit(AsyncValue.data(TableDBArrayProtobufStateData(
|
||||
elements: elements, tail: _tail, count: _count, follow: _follow)));
|
||||
}
|
||||
|
||||
|
@ -110,8 +113,7 @@ class TableDBArrayCubit<T> extends Cubit<TableDBArrayBusyState<T>>
|
|||
}
|
||||
final end = ((tail - 1) % length) + 1;
|
||||
final start = (count < end) ? end - count : 0;
|
||||
final allItems =
|
||||
(await _array.getRange(start, end)).map(_decodeElement).toIList();
|
||||
final allItems = (await _array.getRange(start, end)).toIList();
|
||||
return AsyncValue.data(allItems);
|
||||
} on Exception catch (e, st) {
|
||||
return AsyncValue.error(e, st);
|
||||
|
@ -128,7 +130,7 @@ class TableDBArrayCubit<T> extends Cubit<TableDBArrayBusyState<T>>
|
|||
_headDelta += upd.headDelta;
|
||||
_tailDelta += upd.tailDelta;
|
||||
|
||||
_sspUpdate.busyUpdate<T, TableDBArrayState<T>>(busy, (emit) async {
|
||||
_sspUpdate.busyUpdate<T, TableDBArrayProtobufState<T>>(busy, (emit) async {
|
||||
// apply follow
|
||||
if (_follow) {
|
||||
if (_tail <= 0) {
|
||||
|
@ -165,14 +167,14 @@ class TableDBArrayCubit<T> extends Cubit<TableDBArrayBusyState<T>>
|
|||
await super.close();
|
||||
}
|
||||
|
||||
Future<R?> operate<R>(Future<R?> Function(TableDBArray) closure) async {
|
||||
Future<R?> operate<R>(
|
||||
Future<R?> Function(TableDBArrayProtobuf<T>) closure) async {
|
||||
await _initWait();
|
||||
return closure(_array);
|
||||
}
|
||||
|
||||
final WaitSet<void> _initWait = WaitSet();
|
||||
late final TableDBArray _array;
|
||||
final T Function(List<int> data) _decodeElement;
|
||||
late final TableDBArrayProtobuf<T> _array;
|
||||
StreamSubscription<void>? _subscription;
|
||||
bool _wantsCloseArray = false;
|
||||
final _sspUpdate = SingleStatelessProcessor();
|
|
@ -16,6 +16,6 @@ export 'src/persistent_queue.dart';
|
|||
export 'src/protobuf_tools.dart';
|
||||
export 'src/table_db.dart';
|
||||
export 'src/table_db_array.dart';
|
||||
export 'src/table_db_array_cubit.dart';
|
||||
export 'src/table_db_array_protobuf_cubit.dart';
|
||||
export 'src/veilid_crypto.dart';
|
||||
export 'src/veilid_log.dart' hide veilidLoggy;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue