1 /** 2 Sets 3 4 This module implements @nogc BTree-backed sets, replacement for std::set. 5 6 Copyright: 7 Copyright © 2023-2025, Kitsunebi Games 8 Copyright © 2023-2025, Inochi2D Project 9 10 Copyright: Guillaume Piolat 2015-2024. 11 Copyright: Copyright (C) 2008- by Steven Schveighoffer. Other code 12 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. 13 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 14 Authors: 15 Steven Schveighoffer, 16 $(HTTP erdani.com, Andrei Alexandrescu), 17 Guillaume Piolat, 18 Luna Nielsen 19 */ 20 module nulib.collections.set; 21 import nulib.collections.internal.btree; 22 import numem; 23 24 @nogc: 25 26 /** 27 An associative container which contains sets of objects of type $(D Key). 28 29 Notes: 30 Weak types do not own the memory of their contents. 31 It is up to you to free any indicies. 32 */ 33 alias weak_set(Key) = SetImpl!(Key, (a, b) => a < b, false, false); 34 35 /** 36 An associative container which contains sets of objects of type $(D Key). 37 */ 38 alias set(Key) = SetImpl!(Key, (a, b) => a < b, false, true); 39 40 /** 41 An associative container which contains sets of objects of type $(D Key). 42 43 A multiset may contain multiple entries with the same $(D Key) value. 44 45 Notes: 46 Weak types do not own the memory of their contents. 47 It is up to you to free any indicies. 48 */ 49 alias weak_multiset(Key) = SetImpl!(Key, (a, b) => a < b, true, true); 50 51 /** 52 An associative container which contains sets of objects of type $(D Key). 53 54 A multiset may contain multiple entries with the same $(D Key) value. 55 */ 56 alias multiset(Key) = SetImpl!(Key, (a, b) => a < b, true, true); 57 58 /** 59 Set, designed to replace std::set usage. 60 O(log(n)) insertion, removal, and search time. 61 `Set` is designed to operate even without initialization through `makeSet`. 62 */ 63 struct SetImpl(K, alias less = (a, b) => a < b, bool allowDuplicates = false, bool ownsMemory = false) { 64 public: 65 @nogc: 66 67 @trusted 68 this(int dummy) { 69 } 70 71 @disable this(this); 72 73 @trusted 74 ~this() { 75 } 76 77 /// Insert an element in the container. 78 /// If allowDuplicates is false, this can fail and return `false` 79 /// if the already contains an element with equivalent key. 80 /// Returns: `true` if the insertion took place. 81 @trusted 82 bool insert(K key) { 83 ubyte whatever = 0; 84 return _tree.insert(key, whatever); 85 } 86 87 /// Removes an element from the container. 88 /// Returns: `true` if the removal took place. 89 @trusted 90 bool remove(K key) { 91 92 // Delete memory if this map owns it. 93 static if (ownsMemory) { 94 if (key in _tree) { 95 nogc_delete(_tree[key]); 96 } 97 } 98 99 return _tree.remove(key) != 0; 100 } 101 102 /// Removes all elements from the set. 103 @trusted 104 void clearContents() { 105 nogc_delete(_tree); 106 // _tree reset to .init, still valid 107 } 108 109 /// Returns: `true` if the element is present. 110 @trusted 111 bool opBinaryRight(string op)(K key) inout if (op == "in") { 112 return (key in _tree) !is null; 113 } 114 115 /// Returns: `true` if the element is present. 116 @trusted 117 bool opIndex(K key) const { 118 return (key in _tree) !is null; 119 } 120 121 /// Returns: `true` if the element is present. 122 @trusted 123 bool contains(K key) const { 124 return (key in _tree) !is null; 125 } 126 127 /// Returns: Number of elements in the set. 128 @trusted 129 size_t length() const { 130 return _tree.length(); 131 } 132 133 /// Returns: `ttue` is the set has no element. 134 @trusted 135 bool empty() const { 136 return _tree.empty(); 137 } 138 139 // Iterate by value only 140 141 /// Fetch a forward range on all keys. 142 @trusted 143 auto byKey() { 144 return _tree.byKey(); 145 } 146 147 /// ditto 148 @trusted 149 auto byKey() const { 150 return _tree.byKey(); 151 } 152 153 // default opSlice is like byKey for sets, since the value is a fake value. 154 alias opSlice = byKey; 155 156 private: 157 158 // dummy type 159 alias V = ubyte; 160 161 alias InternalTree = BTree!(K, V, less, allowDuplicates, false); 162 InternalTree _tree; 163 164 } 165 166 @("set: instantiation") 167 unittest { 168 // It should be possible to use most function of an uninitialized Set 169 // All except functions returning a range will work. 170 set!(string) set; 171 172 assert(set.length == 0); 173 assert(set.empty); 174 set.clearContents(); 175 assert(!set.contains("toto")); 176 177 auto range = set[]; 178 assert(range.empty); 179 foreach (e; range) { 180 } 181 182 // Finally create the internal state 183 set.insert("titi"); 184 assert(set.contains("titi")); 185 } 186 187 @("set: insertion, deletion and testing") 188 unittest { 189 set!(string) keywords; 190 191 assert(keywords.insert("public")); 192 assert(keywords.insert("private")); 193 assert(!keywords.insert("private")); 194 195 assert(keywords.remove("public")); 196 assert(!keywords.remove("non-existent")); 197 198 assert(keywords.contains("private")); 199 assert(!keywords.contains("public")); 200 }