blob: 52ff8d943b2e339b9b63b2c3a25c1f666a729e92 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:typed_data';
import 'package:fidl_fuchsia_ledger/fidl.dart' as ledger;
import 'package:fidl_fuchsia_mem/fidl.dart';
import 'package:zircon/zircon.dart' show ZX, ReadResult;
import 'document/change.dart';
import 'document/values/key_value.dart';
import 'uint8list_ops.dart';
// ignore_for_file: one_member_abstracts
/// Factory that creates ledger proxies and bindings.
abstract class LedgerObjectsFactory {
/// Returns a new PageSnapshotProxy.
ledger.PageSnapshotProxy newPageSnapshotProxy();
/// Returns a new PageWatcherBinding.
ledger.PageWatcherBinding newPageWatcherBinding();
}
/// Real implementation of LedgerObjectsFactory.
class LedgerObjectsFactoryImpl implements LedgerObjectsFactory {
@override
ledger.PageSnapshotProxy newPageSnapshotProxy() =>
new ledger.PageSnapshotProxy();
@override
ledger.PageWatcherBinding newPageWatcherBinding() =>
new ledger.PageWatcherBinding();
}
/// Throws an exception containing [operation] if the status is not `ok`.
void checkStatus(ledger.Status status, String operation) {
if (status != ledger.Status.ok) {
throw new Exception('Ledger operation `$operation` failed.');
}
}
/// Returns data stored in [buffer].
Uint8List readBuffer(Buffer buffer) {
ReadResult readResult = buffer.vmo.read(buffer.size);
if (readResult.status != ZX.OK) {
throw new Exception('Unable to read from vmo `${readResult.status}`.');
}
if (readResult.bytes.lengthInBytes != buffer.size) {
throw new Exception('Unexpected count of bytes read.');
}
return new Uint8List.view(readResult.bytes.buffer);
}
/// Helper method for the [getFullEntries] method.
Future<Null> _getFullEntriesRecursively(
ledger.PageSnapshot snapshot,
List<ledger.Entry> result,
List<int> keyPrefix, {
ledger.Token token,
}) async {
Completer<ledger.Status> statusCompleter = new Completer<ledger.Status>();
List<ledger.Entry> entries;
ledger.Token nextToken;
snapshot.getEntries(keyPrefix ?? new Uint8List(0), token,
(ledger.Status status, List<ledger.Entry> entriesResult,
ledger.Token nextTokenResult) {
entries = entriesResult;
nextToken = nextTokenResult;
statusCompleter.complete(status);
});
ledger.Status status = await statusCompleter.future;
if (status != ledger.Status.ok && status != ledger.Status.partialResult) {
throw new Exception(
'PageSnapshot::GetEntries() returned an error status: $status');
}
result.addAll(entries.takeWhile((entry) => hasPrefix(entry.key, keyPrefix)));
if (status == ledger.Status.partialResult &&
hasPrefix(entries[entries.length - 1].key, keyPrefix)) {
return _getFullEntriesRecursively(
snapshot,
result,
keyPrefix,
token: nextToken,
);
}
}
/// Gets the full list of [Entry] objects from a given [PageSnapshot].
///
/// This will continuously call the [PageSnapshot.getEntries] method in case the
/// returned status code is [Status.partialResult].
Future<List<ledger.Entry>> getFullEntries(
ledger.PageSnapshot snapshot, {
List<int> keyPrefix,
}) async {
List<ledger.Entry> entries = <ledger.Entry>[];
await _getFullEntriesRecursively(snapshot, entries, keyPrefix);
return entries;
}
/// Returns all the KV pairs stored in [pageSnapshotProxy] whose key start
/// with [keyPrefix].
/// The KV are ordered by key in ascending order.
Future<List<KeyValue>> getEntriesFromSnapshotWithPrefix(
ledger.PageSnapshotProxy pageSnapshotProxy, Uint8List keyPrefix) async {
final keyValues = <KeyValue>[];
List<ledger.Entry> entries =
await getFullEntries(pageSnapshotProxy, keyPrefix: keyPrefix);
for (final entry in entries) {
Uint8List k = entry.key;
Uint8List v = readBuffer(entry.value);
keyValues.add(new KeyValue(k, v));
}
return keyValues;
}
/// Returns Change with the same content as a pageChange.
Change getChangeFromPageChange(ledger.PageChange pageChange) {
return new Change(
pageChange.changedEntries
.map((ledger.Entry entry) =>
new KeyValue(entry.key, readBuffer(entry.value)))
.toList(),
pageChange.deletedKeys);
}
/// Returns from [mergeResultProvider] the list of KV conflicts.
/// TODO: Change the API so that it returns chunks.
Future<List<ledger.DiffEntry>> getConflictingDiff(
ledger.MergeResultProvider mergeResultProvider) async {
// TODO: implement.
final diff = <ledger.DiffEntry>[];
return diff;
}