1 /**
2   	Taken from Phobos, tweaked for convenience
3 
4 	Copyright: Copyright the respective authors, 2008-
5 	License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 		Authors:   $(WEB erdani.org, Andrei Alexandrescu),
7 		$(WEB bartoszmilewski.wordpress.com, Bartosz Milewski),
8 		Don Clugston,
9 		Shin Fujishiro,
10 		Kenji Hara
11 */
12 
13 module memutils.unique;
14 
15 import memutils.allocators;
16 import memutils.constants;
17 import memutils.utils;
18 import std.conv : to;
19 
20 enum DebugUnique = true;
21 
22 // TODO: Move release() into Embed!, and add a releaseCheck() for refCounted (cannot release > 1 reference) 
23 struct Unique(T, ALLOC = void)
24 {
25 	/** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
26 	alias TR = RefTypeOf!T;
27 	
28 public:
29 	/**
30     Constructor that takes an rvalue.
31     It will ensure uniqueness, as long as the rvalue
32     isn't just a view on an lvalue (e.g., a cast).
33     Typical usage:
34     ----
35     Unique!Foo f = new Foo;
36     ----
37     */
38 	this(inout TR p)
39 	{
40 		opAssign(cast(TR)p);
41 	}
42 	/**
43     Constructor that takes an lvalue. It nulls its source.
44     The nulling will ensure uniqueness as long as there
45     are no previous aliases to the source.
46     */
47 	this(ref TR p)
48 	{
49 		opAssign(p);
50 	}
51 	
52 	/**
53     Constructor that takes a $(D Unique) of a type that is convertible to our type.
54 
55     Typically used to transfer a $(D Unique) rvalue of derived type to
56     a $(D Unique) of base type.
57     Example:
58     ---
59     class C : Object {}
60 
61     Unique!C uc = new C;
62     Unique!Object uo = uc.release;
63     ---
64     */
65 	this(U)(Unique!U u)
66 		if (is(u.TR:TR))
67 	{
68 		// logTrace("Unique constructor converting from ", U.stringof);
69 		opAssign(u._p);
70 		u._p = null;
71 	}
72 	
73 	void free()
74 	{
75 		TR p = null;
76 		opAssign(p);
77 	}
78 	
79 	void opAssign()(auto ref TR p)
80 	{
81 		if (_p) destroy(this);
82 		if (!p) return;
83 		//logTrace("Unique ctor of ", T.stringof, " : ", ptr.to!string);
84 		static if (HasDebugAllocations && DebugUnique) {
85 			ptree._defaultInitialize();
86 			if(cast(void*)p in ptree)
87 			{
88 				logDebug("Already owned pointer: " ~ p.to!string ~ " of type " ~ T.stringof);
89 				assert(false);
90 			}
91 			ptree.insert(cast(void*)p);
92 		}
93 		_p = p;
94 		p = null;
95 	}
96 	/*
97     void opAssign(U)(in Unique!U p)
98     {
99         debug(Unique) logTrace("Unique opAssign converting from ", U.stringof);
100         // first delete any resource we own
101         destroy(this);
102         _p = cast(TR)u._p;
103         cast(TR)u._p = null;
104     }*/
105 	
106 	/// Transfer ownership from a $(D Unique) of a type that is convertible to our type.
107 	void opAssign(U)(Unique!U u)
108 		if (is(u.TR:TR))
109 	{
110 		opAssign(u._p);
111 		u._p = null;
112 	}
113 	
114 	~this()
115 	{
116 		//logDebug("Unique destructor of ", T.stringof, " : ", ptr);
117 		import std.c.string : memset;
118 
119 
120 		static if (ALLOC.stringof != "void") {
121 			if (_p) {
122 				//logTrace("ptr in ptree: ", ptr in ptree);
123 
124 				static if (HasDebugAllocations && DebugUnique) {
125 					ptree._defaultInitialize();
126 					assert(ptr in ptree);
127 					ptree.remove(ptr);
128 				}
129 
130 				ObjectAllocator!(T, ALLOC).free(_p);
131 
132 				//static if (HasDebugAllocations && DebugUnique)
133 				//	debug memset(ptr, 0, AllocSize!T);
134 			}
135 		}
136 		else {
137 			if (_p) {
138 				//logTrace("ptr in ptree: ", ptr in ptree);
139 
140 				static if (HasDebugAllocations && DebugUnique) {
141 					ptree._defaultInitialize();
142 					if (ptr !in ptree){ 
143 						logDebug("Unknown pointer: " ~ ptr.to!string ~ " of type " ~ T.stringof);
144 						assert(false);
145 					}
146 					ptree.remove(ptr);
147 				}
148 
149 				static if (is(TR == T*)) .destroy(*_p);
150 				else .destroy(_p);
151 				//delete _p;
152 
153 				//static if (HasDebugAllocations && DebugUnique) {
154 				//	debug memset(ptr, 0, AllocSize!T);
155 				//}
156 			}
157 		}
158 
159 		_p = null;
160 	}
161 	/** Returns whether the resource exists. */
162 	@property bool isEmpty() const
163 	{
164 		return _p is null;
165 	}
166 	
167 	/** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. */
168 	TR release()
169 	{
170 		//logTrace("Release");
171 		if (!_p) return null;
172 		auto ret = _p;
173         drop();
174 		return ret;
175 	}
176 	
177 	void drop()
178 	{
179 		//logTrace("Drop");
180 		if (!_p) return;
181 		static if (HasDebugAllocations && DebugUnique) {
182 			ptree._defaultInitialize();
183 			assert(ptr in ptree);
184 			ptree.remove(ptr);
185 		}
186 		_p = null;
187 	}
188 	
189 	/** Forwards member access to contents. */
190 	TR opDot() { return _p; }
191 	const(TR) opDot() const { return _p; }
192 	
193 	TR opUnary(string op)() if (op == "*") { return _p; }
194 	const(TR) opUnary(string op)() const if (op == "*") { return _p; }
195 	
196 	TR get() { return _p; }
197 	
198 	bool opCast(T : bool)() const {
199 		return !isEmpty;
200 	}
201 
202 	/**
203     Postblit operator is undefined to prevent the cloning of $(D Unique) objects.
204     */
205 	@disable this(this);
206 	
207 private:
208 	TR _p;
209 
210 	@property void* ptr() const {
211 		return cast(void*)_p;
212 	}
213 
214 	static if (HasDebugAllocations && DebugUnique) {
215 		import memutils.rbtree;
216 		static RBTree!(void*, "a < b", true, Malloc) ptree;
217 	}
218 }
219 
220 auto unique(T)(T obj) {
221 	return Unique!T(obj);
222 }