Skip to content

Commit

Permalink
Added IMapEmpty and ISetEmpty
Browse files Browse the repository at this point in the history
  • Loading branch information
LowLevelSubmarine committed Mar 27, 2024
1 parent f5cc545 commit 211dbf1
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 26 deletions.
3 changes: 2 additions & 1 deletion lib/src/ilist/ilist.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import "l_add.dart";
import "l_add_all.dart";
import "l_flat.dart";

/// This is an [IList] which is always empty.
@immutable
class IListEmpty<T> // ignore: must_be_immutable
extends IList<T> {
/// Creates a empty list.
/// Creates a empty list. In most cases, you should use `const IList.empty()`.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IListEmpty()` which is not constant.
Expand Down
61 changes: 50 additions & 11 deletions lib/src/imap/imap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,54 @@ import "m_add_all.dart";
import "m_flat.dart";
import "m_replace.dart";

/// This is an [IMap] which is always empty.
@immutable
class IMapEmpty<K, V> // ignore: must_be_immutable
extends IMap<K, V> {
/// Creates a empty map. In most cases, you should use `const IMap.empty()`.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IMapEmpty()` which is not constant.
@literal
const IMapEmpty([this.config = const ConfigMap()])
: super._gen();

@override
final ConfigMap config;

/// A empty map is always flushed, by definition.
@override
bool get isFlushed => true;

/// Nothing happens when you flush a empty map, by definition.
@override
IMapEmpty<K, V> get flush => this;

@override
int get _counter => 0;

@override
M<K, V> get _m => MFlat<K, V>.unsafe({});

/// Hash codes must be the same for objects that are equal to each other
/// according to operator ==.
@override
int? get _hashCode {
return isDeepEquals
? hash2(const MapEquality<dynamic, dynamic>().hash({}), config.hashCode)
: hash2(identityHashCode(_m), config.hashCode);
}

@override
set _hashCode(int? value) {}

@override
bool same(IMap<K, V>? other) =>
(other != null) &&
(other is IMapEmpty) &&
(config == other.config);
}

/// This is an [IMap] which can be made constant.
/// Note: Don't ever use it without the "const" keyword, because it will be unsafe.
///
Expand All @@ -35,15 +83,6 @@ class IMapConst<K, V> // ignore: must_be_immutable
[this.config = const ConfigMap()])
: super._gen();

/// Creates a empty constant map.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `IMapConst.empty()` which is not constant.
@literal
const IMapConst.empty([this.config = const ConfigMap()])
: _map = const {},
super._gen();

final Map<K, V> _map;

@override
Expand Down Expand Up @@ -182,9 +221,9 @@ abstract class IMap<K, V> // ignore: must_be_immutable
IMap.withConfig(map, defaultConfig);

/// Create an empty [IMap].
/// Use it with const: `const IMap.empty()` (It's always an [IMapConst]).
/// Use it with const: `const IMap.empty()` (It's always an [IMapEmpty]).
@literal
const factory IMap.empty() = IMapConst<K, V>.empty;
const factory IMap.empty() = IMapEmpty<K, V>;

const IMap._gen();

Expand Down
61 changes: 50 additions & 11 deletions lib/src/iset/iset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,54 @@ import "s_add.dart";
import "s_add_all.dart";
import "s_flat.dart";

/// This is an [ISet] which is always empty.
@immutable
class ISetEmpty<T> // ignore: must_be_immutable
extends ISet<T> {
/// Creates a empty set. In most cases, you should use `const ISet.empty()`.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `ISetEmpty()` which is not constant.
@literal
const ISetEmpty([this.config = const ConfigSet()])
: super._gen();

@override
final ConfigSet config;

/// A empty set is always flushed, by definition.
@override
bool get isFlushed => true;

/// Nothing happens when you flush a empty set, by definition.
@override
ISetEmpty<T> get flush => this;

@override
int get _counter => 0;

@override
S<T> get _s => SFlat<T>.unsafe({});

/// Hash codes must be the same for objects that are equal to each other
/// according to operator ==.
@override
int? get _hashCode {
return isDeepEquals
? hash2(const SetEquality<dynamic>().hash({}), config.hashCode)
: hash2(identityHashCode(_s), config.hashCode);
}

@override
set _hashCode(int? value) {}

@override
bool same(ISet<T>? other) =>
(other != null) &&
(other is ISetEmpty) &&
(config == other.config);
}

/// This is an [ISet] which can be made constant.
/// Note: Don't ever use it without the "const" keyword, because it will be unsafe.
///
Expand All @@ -37,15 +85,6 @@ class ISetConst<T> // ignore: must_be_immutable
[this.config = const ConfigSet()])
: super._gen();

/// Creates a empty constant set.
///
/// IMPORTANT: You must always use the `const` keyword.
/// It's always wrong to use an `ISetConst.empty()` which is not constant.
@literal
const ISetConst.empty([this.config = const ConfigSet()])
: _set = const {},
super._gen();

final Set<T> _set;

@override
Expand Down Expand Up @@ -197,9 +236,9 @@ abstract class ISet<T> // ignore: must_be_immutable
ISet.withConfig(iterable, defaultConfig);

/// Create an empty [ISet].
/// Use it with const: `const ISet.empty()` (It's always an [ISetConst]).
/// Use it with const: `const ISet.empty()` (It's always an [ISetEmpty]).
@literal
const factory ISet.empty() = ISetConst<T>.empty;
const factory ISet.empty() = ISetEmpty<T>;

const ISet._gen();

Expand Down
7 changes: 4 additions & 3 deletions test/ilist/ilist_empty_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ void main() {
ImmutableCollection.autoFlush = false;
});


test("Runtime Type", () {
expect(const IListEmpty(), isA<IListEmpty>());
expect(const IListEmpty(), isA<IListEmpty>());
Expand All @@ -24,13 +23,15 @@ void main() {
});

test("Make sure the IListEmpty can be modified and later iterated", () {
// LAddAll
IList<String> list = const IList.empty();
list = list.addAll(["a", "b", "c"]);
list.forEach((_) { });

// LAdd
list = const IList.empty();
list = list.add("d");
list.forEach((_) { });
list = list.remove("a");
list.forEach((_) { });
});

test("Make sure the internal list is List<int>, and not List<Never>", () {
Expand Down
63 changes: 63 additions & 0 deletions test/imap/imap_empty_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Developed by Marcelo Glasberg (2021) https://glasberg.dev and https://github.com/marcglasberg
// and Philippe Fanaro https://github.com/psygo
// For more info, see: https://pub.dartlang.org/packages/fast_immutable_collections
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:test/test.dart';

void main() {
setUp(() {
ImmutableCollection.resetAllConfigurations();
ImmutableCollection.autoFlush = false;
});

test("Runtime Type", () {
expect(const IMapEmpty(), isA<IMapEmpty>());
expect(const IMapEmpty(), isA<IMapEmpty>());
expect(const IMapEmpty<String, String>(), isA<IMapEmpty<String, String>>());
expect(const IMapEmpty<int, String>(), isA<IMapEmpty<int, String>>());

expect(const IMapEmpty(), isA<IMap>());
expect(const IMapEmpty(), isA<IMap>());
expect(const IMapEmpty<String, String>(), isA<IMap<String, String>>());
expect(const IMapEmpty<int, String>(), isA<IMap<int, String>>());
});

test("Make sure the IMapEmpty can be modified and later iterated", () {
// MAddAll
IMap<String, int> map = const IMap.empty();
map = map.addEntries([
const MapEntry("a", 1),
const MapEntry("b", 2),
const MapEntry("c", 3)
]);
map.forEach((_, __) { });

// MAdd
map = const IMap.empty();
map = map.add("d", 4);
map.forEach((_, __) { });

// MReplace
map = const IMap.empty();
map = map.add("d", 42);
map.forEach((_, __) { });
});

test("Make sure the internal map is Map<int, String>, and not Map<Never>", () {
const m1 = IMapEmpty<String, int>();
expect(m1.runtimeType.toString(), 'IMapEmpty<String, int>');

const m2 = IMapConst<String, int>({'a': 1, 'b': 2, 'c': 3});
expect(m2.runtimeType.toString(), 'IMapConst<String, int>');

final m3 = m1.addAll(m2);
expect(m3.runtimeType.toString(), 'IMapImpl<String, int>');

final result = m3.where((String key, int value) => value == 2);
expect(result, {'b': 2}.lock);
});

test(".same() is working properly", () {
expect(const IMap.empty().same(const IMap.empty()), isTrue);
});
}
54 changes: 54 additions & 0 deletions test/iset/iset_empty_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Developed by Marcelo Glasberg (2021) https://glasberg.dev and https://github.com/marcglasberg
// and Philippe Fanaro https://github.com/psygo
// For more info, see: https://pub.dartlang.org/packages/fast_immutable_collections
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:test/test.dart';

void main() {
setUp(() {
ImmutableCollection.resetAllConfigurations();
ImmutableCollection.autoFlush = false;
});

test("Runtime Type", () {
expect(const ISetEmpty(), isA<ISetEmpty>());
expect(const ISetEmpty(), isA<ISetEmpty>());
expect(const ISetEmpty<String>(), isA<ISetEmpty<String>>());
expect(const ISetEmpty<int>(), isA<ISetEmpty<int>>());

expect(const ISetEmpty(), isA<ISet>());
expect(const ISetEmpty(), isA<ISet>());
expect(const ISetEmpty<String>(), isA<ISet<String>>());
expect(const ISetEmpty<int>(), isA<ISet<int>>());
});

test("Make sure the ISetEmpty can be modified and later iterated", () {
// SAddAll
ISet<String> set = const ISet.empty();
set = set.addAll(["a", "b", "c"]);
set.forEach((_) { });

// SAdd
set = const ISet.empty();
set = set.add("d");
set.forEach((_) { });
});

test("Make sure the internal set is Set<int>, and not Set<Never>", () {
const s1 = ISetEmpty<int>();
expect(s1.runtimeType.toString(), 'ISetEmpty<int>');

const s2 = ISetConst<int>({1, 2, 3});
expect(s2.runtimeType.toString(), 'ISetConst<int>');

final s3 = s1.addAll(s2);
expect(s3.runtimeType.toString(), 'ISetImpl<int>');

final result = s3.where((int i) => i == 2).toSet();
expect(result, [2]);
});

test(".same() is working properly", () {
expect(const ISet.empty().same(const ISet.empty()), isTrue);
});
}

0 comments on commit 211dbf1

Please sign in to comment.