1 module tagion.mobile.Recycle; 2 3 struct Recycle(T) { 4 pragma(msg, "fixme(cbr): Why is it offset by one with (START_INDEX = 1) ?"); 5 enum START_INDEX = 1; 6 7 enum to_index = (uint i) => cast(const(uint))(i + START_INDEX); 8 enum to_doc_id = (uint i) => cast(const(uint))(i - START_INDEX); 9 10 private { 11 T[] _active; 12 const(uint)[] _reuse; 13 } 14 15 /// Create an object of T and return it's index in '_active' 16 const(uint) create(T x) { 17 if (_reuse.length > 0) { 18 const reuse_id = _reuse[$ - 1]; 19 _reuse.length--; 20 _active[reuse_id] = x; 21 return to_index(reuse_id); 22 } 23 _active ~= x; 24 return to_index(cast(uint) _active.length - 1); 25 } 26 27 bool put(T x, const uint id) { 28 if (exists(id)) { 29 const doc_id = to_doc_id(id); 30 _active[doc_id] = x; 31 return true; 32 } 33 return false; 34 } 35 36 /// Erase by index 37 void erase(const uint id) 38 in { 39 const doc_id = to_doc_id(id); 40 assert(doc_id >= 0); 41 assert(doc_id < _active.length); 42 } 43 do { 44 const doc_id = to_doc_id(id); 45 import std.algorithm.searching : count; 46 47 _active[doc_id] = T.init; 48 // Check for avoiding the multiple append the same id 49 if (_reuse.count(doc_id) is 0) { 50 _reuse ~= doc_id; 51 } 52 } 53 54 /// overloading function call operator 55 T opCall(const uint id) 56 in { 57 const doc_id = to_doc_id(id); 58 assert(doc_id < _active.length); 59 assert(_active[doc_id]!is T.init); 60 } 61 do { 62 const doc_id = to_doc_id(id); 63 return _active[doc_id]; 64 } 65 66 /// Checking for existence by id 67 bool exists(const uint id) const pure nothrow { 68 const doc_id = to_doc_id(id); 69 if (doc_id < _active.length) { 70 return _active[doc_id]!is T.init; 71 } 72 return false; 73 } 74 } 75 76 pragma(msg, "fixme(cbr): This unittest does not pass (", __FILE__, ":", __LINE__, ")"); 77 version (none) unittest { 78 import std.stdio; 79 import tagion.hibon.Document : Document; 80 81 // import std.stdio : writeln; 82 /** 83 * create Documents' recycler; 84 * get the indexes with calling 'create()' method 85 * of the Recycle object 86 */ 87 Recycle!Document recycler; 88 immutable(ubyte[]) doc1_data = [1, 2, 3]; 89 auto doc1 = Document(doc1_data); 90 91 const doc_id = recycler.create(doc1); 92 assert(doc_id is 0); 93 94 recycler.erase(doc_id); 95 assert(!recycler.exists(doc_id)); 96 97 const doc1_id = recycler.create(doc1); 98 99 immutable(ubyte[]) doc2_data = [2, 3, 4]; 100 auto doc2 = Document(doc2_data); 101 const doc2_id = recycler.create(doc2); 102 103 immutable(ubyte[]) doc3_data = [3, 4, 5]; 104 auto doc3 = Document(doc3_data); 105 const doc3_id = recycler.create(doc3); 106 107 // test for calling Documents by id's 108 assert(doc1 is recycler(doc1_id)); 109 assert(doc2 is recycler(doc2_id)); 110 assert(doc3 is recycler(doc3_id)); 111 112 // test for calling exists() method 113 assert(recycler.exists(doc1_id)); 114 assert(recycler.exists(doc2_id)); 115 assert(recycler.exists(doc3_id)); 116 117 // test for calling erase() method 118 recycler.erase(doc2_id); 119 assert(!recycler.exists(doc2_id)); 120 recycler.erase(doc1_id); 121 assert(!recycler.exists(doc1_id)); 122 123 // create a new Documents doc4 and doc5 124 immutable(ubyte[]) doc4_data = [5, 6, 7]; 125 auto doc4 = Document(doc4_data); 126 const doc4_id = recycler.create(doc4); 127 128 immutable(ubyte[]) doc5_data = [6, 7, 8]; 129 auto doc5 = Document(doc5_data); 130 const doc5_id = recycler.create(doc5); 131 132 /** 133 * And check indexes for equality with 134 * created before doc1 and doc2 135 */ 136 assert(doc1_id is doc4_id); 137 assert(doc2_id is doc5_id); 138 139 /** 140 * Check ref changes 141 */ 142 import std.algorithm; 143 144 immutable(ubyte)[] doc6_data = [5, 6, 7]; 145 auto doc6 = Document(doc6_data); 146 const doc6_id = recycler.create(doc6); 147 doc6_data ~= 8; 148 auto same_doc = recycler(doc6_id); 149 assert(equal(doc6.serialize, same_doc.serialize)); 150 }