Skip to content

Commit

Permalink
Added tileExistsInStore method to backend
Browse files Browse the repository at this point in the history
Added requirement for tiles & stores relations to abstract database models, with generic typing
Connected `removeOldestTilesAboveLimit` method to image provider
Removed 'queue' package
  • Loading branch information
JaffaKetchup committed Jan 4, 2024
1 parent 5d5ce62 commit 6fc0864
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 91 deletions.
10 changes: 10 additions & 0 deletions lib/src/backend/impls/objectbox/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ class _ObjectBoxBackendImpl implements ObjectBoxBackendInternal {
args: {'storeName': storeName},
))!['misses']! as int;

@override
Future<bool> tileExistsInStore({
required String storeName,
required String url,
}) async =>
(await _sendCmd(
type: _WorkerCmdType.tileExistsInStore,
args: {'storeName': storeName, 'url': url},
))!['exists'];

@override
Future<ObjectBoxTile?> readTile({
required String url,
Expand Down
6 changes: 4 additions & 2 deletions lib/src/backend/impls/objectbox/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:objectbox/objectbox.dart';
import '../../../interfaces/models.dart';

@Entity()
base class ObjectBoxStore extends BackendStore {
base class ObjectBoxStore extends BackendStore<ToMany<ObjectBoxTile>> {
@Id()
int id = 0;

Expand All @@ -24,6 +24,7 @@ base class ObjectBoxStore extends BackendStore {
@override
int misses;

@override
@Index()
@Backlink()
final tiles = ToMany<ObjectBoxTile>();
Expand All @@ -38,7 +39,7 @@ base class ObjectBoxStore extends BackendStore {
}

@Entity()
base class ObjectBoxTile extends BackendTile {
base class ObjectBoxTile extends BackendTile<ToMany<ObjectBoxStore>> {
@Id()
int id = 0;

Expand All @@ -55,6 +56,7 @@ base class ObjectBoxTile extends BackendTile {
@override
Uint8List bytes;

@override
@Index()
final stores = ToMany<ObjectBoxStore>();

Expand Down
27 changes: 24 additions & 3 deletions lib/src/backend/impls/objectbox/worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum _WorkerCmdType {
getStoreLength,
getStoreHits,
getStoreMisses,
tileExistsInStore,
readTile,
writeTile,
deleteTile,
Expand Down Expand Up @@ -59,6 +60,9 @@ Future<void> _worker(
//! UTIL FUNCTIONS !//

/// Convert store name to database store object
///
/// Returns `null` if store not found. Throw the [StoreNotExists] error if it
/// was required.
ObjectBoxStore? getStore(String storeName) {
final query = root
.box<ObjectBoxStore>()
Expand Down Expand Up @@ -296,15 +300,32 @@ Future<void> _worker(
},
);

break;
case _WorkerCmdType.tileExistsInStore:
final storeName = cmd.args['storeName']! as String;
final url = cmd.args['url']! as String;

final query =
(root.box<ObjectBoxTile>().query(ObjectBoxTile_.url.equals(url))
..linkMany(
ObjectBoxTile_.stores,
ObjectBoxStore_.name.equals(storeName),
))
.build();

sendRes(id: cmd.id, data: {'exists': query.count() == 1});

query.close();

break;
case _WorkerCmdType.readTile:
final url = cmd.args['url']! as String;

final query = root
.box<ObjectBoxTile>()
.query(ObjectBoxTile_.url.equals(cmd.args['url']! as String))
.query(ObjectBoxTile_.url.equals(url))
.build();

// TODO: Hits & misses

sendRes(id: cmd.id, data: {'tile': query.findUnique()});

query.close();
Expand Down
6 changes: 6 additions & 0 deletions lib/src/backend/interfaces/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ abstract interface class FMTCBackendInternal {
required String storeName,
});

/// Check whether the specified tile exists in the specified store
Future<bool> tileExistsInStore({
required String storeName,
required String url,
});

/// Get a raw tile by URL
Future<BackendTile?> readTile({
required String url,
Expand Down
6 changes: 4 additions & 2 deletions lib/src/backend/interfaces/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import 'dart:typed_data';

import 'package:meta/meta.dart';

abstract base class BackendStore {
abstract base class BackendStore<T extends Iterable<BackendTile<dynamic>>> {
abstract String name;
abstract int hits;
abstract int misses;
T get tiles;

/// Uses [name] for equality comparisons only (unless the two objects are
/// [identical])
Expand All @@ -22,10 +23,11 @@ abstract base class BackendStore {
int get hashCode => name.hashCode;
}

abstract base class BackendTile {
abstract base class BackendTile<S extends Iterable<BackendStore<dynamic>>> {
abstract String url;
abstract DateTime lastModified;
abstract Uint8List bytes;
S get stores;

/// Uses [url] for equality comparisons only (unless the two objects are
/// [identical])
Expand Down
3 changes: 3 additions & 0 deletions lib/src/misc/obscure_query_params.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright © Luka S (JaffaKetchup) under GPL-v3
// A full license can be found at .\LICENSE

import 'package:meta/meta.dart';

@internal
String obscureQueryParams({
required String url,
required Iterable<RegExp> obscuredQueryParams,
Expand Down
63 changes: 8 additions & 55 deletions lib/src/providers/image_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:http/http.dart';
import 'package:isar/isar.dart';
import 'package:queue/queue.dart';

import '../../flutter_map_tile_caching.dart';
import '../db/defs/metadata.dart';
import '../db/defs/store_descriptor.dart';
import '../db/defs/tile.dart';
import '../db/registry.dart';
import '../db/tools.dart';
import '../misc/obscure_query_params.dart';

/// A specialised [ImageProvider] dedicated to 'flutter_map_tile_caching'
Expand All @@ -32,22 +25,16 @@ class FMTCImageProvider extends ImageProvider<FMTCImageProvider> {
/// The coordinates of the tile to be fetched
final TileCoordinates coords;

FMTCBackend get _backend => FMTC.instance.settings.backend;

/// Configured root directory
// final String directory;

//static final _removeOldestQueue = Queue(timeout: const Duration(seconds: 1));
//static final _cacheHitsQueue = Queue();
//static final _cacheMissesQueue = Queue();

/// Create a specialised [ImageProvider] dedicated to 'flutter_map_tile_caching'
FMTCImageProvider({
required this.provider,
required this.options,
required this.coords,
});

// ignore: invalid_use_of_protected_member
FMTCBackendInternal get _backend => FMTC.instance.settings.backend.internal;

@override
ImageStreamCompleter loadImage(
FMTCImageProvider key,
Expand Down Expand Up @@ -112,7 +99,7 @@ class FMTCImageProvider extends ImageProvider<FMTCImageProvider> {
// Prepare a list of image bytes and prefill if there's already a cached
// tile available
Uint8List? bytes;
if (!needsCreating) bytes = Uint8List.fromList(existingTile.bytes);
if (!needsCreating) bytes = existingTile.bytes;

// If there is a cached tile that's in date available, use it
if (!needsCreating && !needsUpdating) {
Expand All @@ -132,10 +119,6 @@ class FMTCImageProvider extends ImageProvider<FMTCImageProvider> {
);
}

// From this point, a tile must exist (but it may be outdated). However, an
// outdated tile is better than no tile at all, so in the event of an error,
// always return the existing tile's bytes

// Setup a network request for the tile & handle network exceptions
final request = Request('GET', Uri.parse(networkUrl))
..headers.addAll(provider.headers);
Expand Down Expand Up @@ -230,16 +213,11 @@ class FMTCImageProvider extends ImageProvider<FMTCImageProvider> {

// Clear out old tiles if the maximum store length has been exceeded
if (needsCreating && provider.settings.maxStoreLength != 0) {
// TODO: Check if performance is acceptable without checking limit excess first
unawaited(
_removeOldestQueue.add(
() => compute(
_removeOldestTile,
[
provider.storeDirectory.storeName,
directory,
provider.settings.maxStoreLength,
],
),
_backend.removeOldestTilesAboveLimit(
storeName: provider.storeDirectory.storeName,
tilesLimit: provider.settings.maxStoreLength,
),
);
}
Expand Down Expand Up @@ -279,28 +257,3 @@ class FMTCImageProvider extends ImageProvider<FMTCImageProvider> {
@override
int get hashCode => Object.hash(coords, provider, options);
}

Future<void> _removeOldestTile(List<dynamic> args) async {
final db = Isar.openSync(
[DbStoreDescriptorSchema, DbTileSchema, DbMetadataSchema],
name: DatabaseTools.hash(args[0]).toString(),
directory: args[1],
inspector: false,
);

db.writeTxnSync(
() => db.tiles.deleteAllSync(
db.tiles
.where()
.anyLastModified()
.limit(
(db.tiles.countSync() - args[2]).clamp(0, double.maxFinite).toInt(),
)
.findAllSync()
.map((t) => t.id)
.toList(),
),
);

await db.close();
}
40 changes: 12 additions & 28 deletions lib/src/providers/tile_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class FMTCTileProvider extends TileProvider {
},
);

// ignore: invalid_use_of_protected_member
FMTCBackendInternal get _backend => FMTC.instance.settings.backend.internal;

/// Closes the open [httpClient] - this will make the provider unable to
/// perform network requests
@override
Expand All @@ -54,39 +57,20 @@ class FMTCTileProvider extends TileProvider {
provider: this,
options: options,
coords: coords,
directory: FMTC.instance.rootDirectory.directory.absolute.path,
);

/// Check whether a specified tile is cached in the current store synchronously
bool checkTileCached({
required TileCoordinates coords,
required TileLayer options,
}) =>
FMTCRegistry.instance(storeDirectory.storeName).tiles.getSync(
DatabaseTools.hash(
obscureQueryParams(
url: getTileUrl(coords, options),
obscuredQueryParams: settings.obscuredQueryParams,
),
),
) !=
null;

/// Check whether a specified tile is cached in the current store
/// asynchronously
Future<bool> checkTileCachedAsync({
Future<bool> checkTileCached({
required TileCoordinates coords,
required TileLayer options,
}) async =>
await FMTCRegistry.instance(storeDirectory.storeName).tiles.get(
DatabaseTools.hash(
obscureQueryParams(
url: getTileUrl(coords, options),
obscuredQueryParams: settings.obscuredQueryParams,
),
),
) !=
null;
}) =>
_backend.tileExistsInStore(
storeName: storeDirectory.storeName,
url: obscureQueryParams(
url: getTileUrl(coords, options),
obscuredQueryParams: settings.obscuredQueryParams,
),
);

@override
bool operator ==(Object other) =>
Expand Down
1 change: 0 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ dependencies:
objectbox_flutter_libs: any
path: ^1.9.0
path_provider: ^2.1.1
queue: ^3.1.0+2
stream_transform: ^2.1.0
watcher: ^1.1.0

Expand Down

0 comments on commit 6fc0864

Please sign in to comment.