1 /** @file thinker.cpp Base for all thinkers.
2 *
3 * @authors Copyright (c) 2014-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 *
5 * @par License
6 * GPL: http://www.gnu.org/licenses/gpl.html
7 *
8 * <small>This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. This program is distributed in the hope that it
12 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14 * Public License for more details. You should have received a copy of the GNU
15 * General Public License along with this program; if not, write to the Free
16 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA</small>
18 */
19
20 #include "doomsday/world/thinker.h"
21
22 #include <de/math.h>
23 #include <de/memory.h>
24 #include <de/memoryzone.h>
25
26 using namespace de;
27
DENG2_PIMPL_NOREF(Thinker)28 DENG2_PIMPL_NOREF(Thinker)
29 {
30 dsize size;
31 thinker_s *base; // owned
32 IData *data; // owned, optional
33
34 Impl(AllocMethod alloc, dsize sizeInBytes, IData *data_)
35 : size(max<dsize>(sizeInBytes, sizeof(thinker_s)))
36 , base(0)
37 , data(data_)
38 {
39 if (alloc == AllocateStandard)
40 {
41 base = reinterpret_cast<thinker_s *>(M_Calloc(size));
42 base->_flags = THINKF_STD_MALLOC;
43 }
44 else // using memory zone
45 {
46 base = reinterpret_cast<thinker_s *>(Z_Calloc(size, PU_MAP, NULL));
47 }
48
49 if (data) data->setThinker(base);
50 }
51
52 Impl(Impl const &other)
53 : size(other.size)
54 , base(reinterpret_cast<thinker_s *>(other.base->_flags & THINKF_STD_MALLOC?
55 M_MemDup(other.base, size) :
56 Z_MemDup(other.base, size)))
57 , data(other.data? other.data->duplicate() : 0)
58 {
59 base->d = data;
60 if (data) data->setThinker(base);
61 }
62
63 Impl(thinker_s *podThinkerToTake, dsize sizeInBytes)
64 : size(sizeInBytes)
65 , base(podThinkerToTake)
66 , data(reinterpret_cast<IData *>(podThinkerToTake->d)) // also take ownership of the private data
67 {}
68
69 ~Impl()
70 {
71 release();
72 }
73
74 void release()
75 {
76 if (base)
77 {
78 if (isStandardAllocated())
79 {
80 M_Free(base);
81 }
82 else
83 {
84 Z_Free(base);
85 }
86 }
87
88 // Get rid of the private data, too.
89 delete data;
90 }
91
92 bool isStandardAllocated() const
93 {
94 return base && (base->_flags & THINKF_STD_MALLOC);
95 }
96
97 void relinquish()
98 {
99 base = 0;
100 data = 0;
101 size = 0;
102 }
103
104 static void clearBaseToZero(thinker_s *base, dsize size)
105 {
106 bool const stdAlloc = CPP_BOOL(base->_flags & THINKF_STD_MALLOC);
107 memset(base, 0, size);
108 if (stdAlloc) base->_flags |= THINKF_STD_MALLOC;
109 }
110 };
111
112 #define STRUCT_MEMBER_ACCESSORS() \
113 prev (*this, offsetof(thinker_s, prev )) \
114 , next (*this, offsetof(thinker_s, next )) \
115 , function(*this, offsetof(thinker_s, function)) \
116 , id (*this, offsetof(thinker_s, id ))
117
Thinker(dsize sizeInBytes,IData * data)118 Thinker::Thinker(dsize sizeInBytes, IData *data)
119 : d(new Impl(AllocateStandard, sizeInBytes, data))
120 , STRUCT_MEMBER_ACCESSORS()
121 {
122 // Default to no public thinker callback.
123 function = Thinker_NoOperation;
124 }
125
Thinker(AllocMethod alloc,dsize sizeInBytes,Thinker::IData * data)126 Thinker::Thinker(AllocMethod alloc, dsize sizeInBytes, Thinker::IData *data)
127 : d(new Impl(alloc, sizeInBytes, data))
128 , STRUCT_MEMBER_ACCESSORS()
129 {
130 // Default to no public thinker callback.
131 function = Thinker_NoOperation;
132 }
133
Thinker(Thinker const & other)134 Thinker::Thinker(Thinker const &other)
135 : d(new Impl(*other.d))
136 , STRUCT_MEMBER_ACCESSORS()
137 {}
138
Thinker(thinker_s const & podThinker,dsize sizeInBytes,AllocMethod alloc)139 Thinker::Thinker(thinker_s const &podThinker, dsize sizeInBytes, AllocMethod alloc)
140 : d(new Impl(alloc, sizeInBytes, 0))
141 , STRUCT_MEMBER_ACCESSORS()
142 {
143 DENG2_ASSERT(d->size == sizeInBytes);
144 memcpy(d->base, &podThinker, sizeInBytes);
145
146 // Retain the original allocation flag, though.
147 d->base->_flags &= ~THINKF_STD_MALLOC;
148 if (alloc == AllocateStandard) d->base->_flags |= THINKF_STD_MALLOC;
149
150 if (podThinker.d)
151 {
152 setData(reinterpret_cast<IData *>(podThinker.d)->duplicate());
153 }
154 }
155
Thinker(thinker_s * podThinkerToTake,de::dsize sizeInBytes)156 Thinker::Thinker(thinker_s *podThinkerToTake, de::dsize sizeInBytes)
157 : d(new Impl(podThinkerToTake, sizeInBytes))
158 , STRUCT_MEMBER_ACCESSORS()
159 {}
160
operator =(Thinker const & other)161 Thinker &Thinker::operator = (Thinker const &other)
162 {
163 d.reset(new Impl(*other.d));
164 return *this;
165 }
166
enable(bool yes)167 void Thinker::enable(bool yes)
168 {
169 applyFlagOperation(d->base->_flags, duint32(THINKF_DISABLED), yes? SetFlags : UnsetFlags);
170 }
171
zap()172 void Thinker::zap()
173 {
174 delete d->data;
175 d->data = 0;
176
177 Impl::clearBaseToZero(d->base, d->size);
178 }
179
isDisabled() const180 bool Thinker::isDisabled() const
181 {
182 return (d->base->_flags & THINKF_DISABLED) != 0;
183 }
184
base()185 thinker_s &Thinker::base()
186 {
187 return *d->base;
188 }
189
base() const190 thinker_s const &Thinker::base() const
191 {
192 return *d->base;
193 }
194
hasData() const195 bool Thinker::hasData() const
196 {
197 return d->data != 0;
198 }
199
data()200 Thinker::IData &Thinker::data()
201 {
202 DENG2_ASSERT(hasData());
203 return *d->data;
204 }
205
data() const206 Thinker::IData const &Thinker::data() const
207 {
208 DENG2_ASSERT(hasData());
209 return *d->data;
210 }
211
sizeInBytes() const212 dsize Thinker::sizeInBytes() const
213 {
214 return d->size;
215 }
216
take()217 thinker_s *Thinker::take()
218 {
219 DENG2_ASSERT(d->base->d == d->data);
220
221 thinker_s *th = d->base;
222 d->relinquish();
223 return th;
224 }
225
destroy(thinker_s * thinkerBase)226 void Thinker::destroy(thinker_s *thinkerBase)
227 {
228 DENG2_ASSERT(thinkerBase != 0);
229
230 release(*thinkerBase);
231
232 if (thinkerBase->_flags & THINKF_STD_MALLOC)
233 {
234 M_Free(thinkerBase);
235 }
236 else
237 {
238 Z_Free(thinkerBase);
239 }
240 }
241
release(thinker_s & thinkerBase)242 void Thinker::release(thinker_s &thinkerBase)
243 {
244 delete reinterpret_cast<IData *>(thinkerBase.d);
245 thinkerBase.d = 0;
246 }
247
zap(thinker_s & thinkerBase,dsize sizeInBytes)248 void Thinker::zap(thinker_s &thinkerBase, dsize sizeInBytes)
249 {
250 delete reinterpret_cast<IData *>(thinkerBase.d);
251 Impl::clearBaseToZero(&thinkerBase, sizeInBytes);
252 }
253
setData(Thinker::IData * data)254 void Thinker::setData(Thinker::IData *data)
255 {
256 if (d->data) delete d->data;
257
258 d->data = data;
259 d->base->d = data;
260
261 if (data)
262 {
263 data->setThinker(*this);
264 }
265 }
266
Thinker_InStasis(thinker_s const * thinker)267 dd_bool Thinker_InStasis(thinker_s const *thinker)
268 {
269 if (!thinker) return false;
270 return (thinker->_flags & THINKF_DISABLED) != 0;
271 }
272
Thinker_SetStasis(thinker_t * thinker,dd_bool on)273 void Thinker_SetStasis(thinker_t *thinker, dd_bool on)
274 {
275 if (thinker)
276 {
277 applyFlagOperation(thinker->_flags, duint32(THINKF_DISABLED),
278 on? SetFlags : UnsetFlags);
279 }
280 }
281
Thinker_NoOperation(void *)282 void Thinker_NoOperation(void *)
283 {
284 // do nothing
285 }
286