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 }