blob: 553d564577c45a69092ef2ba028c2b929098c941 [file] [log] [blame]
// Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file
// for details. 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:collection';
import 'package:multicast_dns/src/resource_record.dart';
/// Cache for resource records that have been received.
///
/// There can be multiple entries for the same name and type.
///
/// The cache is updated with a list of records, because it needs to remove
/// all entries that correspond to the name and type of the name/type
/// combinations of records that should be updated. For example, a host may
/// remove one of its IP addresses and report the remaining address as a
/// response - then we need to clear all previous entries for that host before
/// updating the cache.
class ResourceRecordCache {
/// Creates a new ResourceRecordCache.
ResourceRecordCache();
final Map<int, SplayTreeMap<String, List<ResourceRecord>>> _cache =
<int, SplayTreeMap<String, List<ResourceRecord>>>{};
/// The number of entries in the cache.
int get entryCount {
int count = 0;
for (final SplayTreeMap<String, List<ResourceRecord>> map
in _cache.values) {
for (final List<ResourceRecord> records in map.values) {
count += records?.length;
}
}
return count;
}
/// Update the records in this cache.
void updateRecords(List<ResourceRecord> records) {
// TODO(karlklose): include flush bit in the record and only flush if
// necessary.
// Clear the cache for all name/type combinations to be updated.
final Map<int, Set<String>> seenRecordTypes = <int, Set<String>>{};
for (ResourceRecord record in records) {
// TODO(dnfield): Update this to use set literal syntax when we're able to bump the SDK constraint.
seenRecordTypes[record.resourceRecordType] ??=
Set<String>(); // ignore: prefer_collection_literals
if (seenRecordTypes[record.resourceRecordType].add(record.name)) {
_cache[record.resourceRecordType] ??=
SplayTreeMap<String, List<ResourceRecord>>();
_cache[record.resourceRecordType]
[record.name] = <ResourceRecord>[record];
} else {
_cache[record.resourceRecordType][record.name].add(record);
}
}
}
/// Get a record from this cache.
void lookup<T extends ResourceRecord>(
String name, int type, List<T> results) {
assert(ResourceRecordType.debugAssertValid(type));
final int time = DateTime.now().millisecondsSinceEpoch;
final SplayTreeMap<String, List<ResourceRecord>> candidates = _cache[type];
if (candidates == null) {
return;
}
final List<ResourceRecord> candidateRecords = candidates[name];
if (candidateRecords == null) {
return;
}
candidateRecords
.removeWhere((ResourceRecord candidate) => candidate.validUntil < time);
results.addAll(candidateRecords.cast<T>());
}
}