1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 #include "io/SoWriterefCounter.h"
34
35 #include <cassert>
36
37 #include <Inventor/C/tidbits.h>
38 #include <Inventor/SoOutput.h>
39 #include <Inventor/errors/SoDebugError.h>
40 #include <Inventor/misc/SoBase.h>
41 #include <Inventor/nodes/SoNode.h>
42
43 #include "tidbitsp.h"
44 #include "threads/threadsutilp.h"
45 #include "misc/SbHash.h"
46
47 // *************************************************************************
48
49 class SoWriterefCounterBaseData {
50 public:
SoWriterefCounterBaseData()51 SoWriterefCounterBaseData() {
52 writeref = 0;
53 ingraph = FALSE;
54 }
55
56 int32_t writeref;
57 SbBool ingraph;
58 };
59
60 // *************************************************************************
61
62 typedef SbHash<const SoBase *, SoWriterefCounterBaseData *> SoBase2SoWriterefCounterBaseDataMap;
63
64 class SoWriterefCounterOutputData {
65 public:
66 SoBase2SoWriterefCounterBaseDataMap writerefdict;
67
SoWriterefCounterOutputData()68 SoWriterefCounterOutputData()
69 : writerefdict(1051), refcount(0) {
70 }
71
72 // need refcounter since dict can be shared among several SoOutputs
ref(void)73 void ref(void) {
74 this->refcount++;
75 }
unref(void)76 void unref(void) {
77 if (--this->refcount == 0) {
78 this->cleanup();
79 delete this;
80 }
81 }
debugCleanup(void)82 void debugCleanup(void) {
83 #if COIN_DEBUG
84 for(
85 SoBase2SoWriterefCounterBaseDataMap::const_iterator iter =
86 writerefdict.const_begin();
87 iter!=writerefdict.const_end();
88 ++iter
89 ) {
90 const SoBase * base = iter->key;
91
92 SbName name = base->getName();
93 if (name == "") name = "<noname>";
94
95 SoDebugError::postWarning("SoWriterefCounter::<cleanup>",
96 "Not removed from writerefdict: %p, %s:%s",
97 base, base->getTypeId().getName().getString(), name.getString());
98
99 }
100 #endif // COIN_DEBUG
101 this->cleanup();
102 }
103
104 protected:
~SoWriterefCounterOutputData()105 ~SoWriterefCounterOutputData() {
106 }
107
108 private:
109 int refcount;
110
cleanup(void)111 void cleanup(void) {
112 for(
113 SoBase2SoWriterefCounterBaseDataMap::const_iterator iter =
114 writerefdict.const_begin();
115 iter!=writerefdict.const_end();
116 ++iter
117 ) {
118 delete iter->obj;
119 }
120
121 this->writerefdict.clear();
122 }
123
124 };
125
126 // *************************************************************************
127
128 typedef SbHash<SoOutput *, SoWriterefCounter *> SoOutput2SoWriterefCounterMap;
129 typedef SbHash<const SoBase *, int> SoBase2Id;
130
131 class SoWriterefCounterP {
132 public:
SoWriterefCounterP(SoWriterefCounter * master,SoOutput * out,SoWriterefCounterP * dataCopy)133 SoWriterefCounterP(SoWriterefCounter * master, SoOutput * out, SoWriterefCounterP * dataCopy)
134 : master(master), out(out)
135 {
136 if (dataCopy) {
137 this->outputdata = dataCopy->outputdata;
138 this->sobase2id = new SoBase2Id(*dataCopy->sobase2id);
139 }
140 else {
141 this->outputdata = new SoWriterefCounterOutputData;
142 this->sobase2id = new SoBase2Id;
143 }
144 this->outputdata->ref();
145 this->nextreferenceid = 0;
146 }
~SoWriterefCounterP()147 ~SoWriterefCounterP() {
148 this->outputdata->unref();
149 delete this->sobase2id;
150 }
appendPostfix(const SoBase * base,SbString & name,int refid)151 void appendPostfix(const SoBase *base, SbString &name, int refid)
152 {
153 // Fix to avoid writing DEFs starting with an illegal
154 // character (e.g. '+')
155 if (name.getLength() == 0 &&
156 base->isOfType(SoNode::getClassTypeId()) &&
157 !SbName::isBaseNameStartChar((*refwriteprefix)[0])) {
158 name += "_";
159 }
160
161 name += SoWriterefCounterP::refwriteprefix->getString();
162 name.addIntString(refid);
163 }
164
165 SoWriterefCounter * master;
166 SoOutput * out;
167 SoWriterefCounterOutputData * outputdata;
168 SoBase2Id * sobase2id;
169 int nextreferenceid;
170
171 static void * mutex;
172 static SoOutput2SoWriterefCounterMap * outputdict;
173 static SoWriterefCounter * current; // used to be backwards compatible
174 static SbString * refwriteprefix;
175
atexit_cleanup(void)176 static void atexit_cleanup(void) {
177 current = NULL;
178 delete refwriteprefix;
179 refwriteprefix = NULL;
180 delete outputdict;
181 outputdict = NULL;
182 CC_MUTEX_DESTRUCT(mutex);
183 }
184
185 };
186
187 void * SoWriterefCounterP::mutex;
188 SoOutput2SoWriterefCounterMap * SoWriterefCounterP::outputdict;
189 SoWriterefCounter * SoWriterefCounterP::current = NULL; // used to be backwards compatible
190 SbString * SoWriterefCounterP::refwriteprefix;
191
192 #define PRIVATE(obj) obj->pimpl
193
194 // *************************************************************************
195
SoWriterefCounter(SoOutput * out,SoOutput * copyfrom)196 SoWriterefCounter::SoWriterefCounter(SoOutput * out, SoOutput * copyfrom)
197 {
198 SoWriterefCounterP * datafrom = NULL;
199 if (copyfrom) {
200 SoWriterefCounter * frominst = SoWriterefCounter::instance(copyfrom);
201 datafrom = frominst->pimpl;
202 }
203 PRIVATE(this) = new SoWriterefCounterP(this, out, datafrom);
204 }
205
~SoWriterefCounter()206 SoWriterefCounter::~SoWriterefCounter()
207 {
208 delete PRIVATE(this);
209 }
210
211 void
create(SoOutput * out,SoOutput * copyfrom)212 SoWriterefCounter::create(SoOutput * out, SoOutput * copyfrom)
213 {
214 SoWriterefCounter * inst = new SoWriterefCounter(out, copyfrom);
215 CC_MUTEX_LOCK(SoWriterefCounterP::mutex);
216 SbBool ret = SoWriterefCounterP::outputdict->put(out, inst);
217 assert(ret && "writeref instance already exists!");
218 CC_MUTEX_UNLOCK(SoWriterefCounterP::mutex);
219 }
220
221 void
destruct(SoOutput * out)222 SoWriterefCounter::destruct(SoOutput * out)
223 {
224 SoWriterefCounter * inst = SoWriterefCounter::instance(out);
225 assert(inst && "instance not found!");
226
227 CC_MUTEX_LOCK(SoWriterefCounterP::mutex);
228 (void) SoWriterefCounterP::outputdict->erase(out);
229 delete inst;
230 CC_MUTEX_UNLOCK(SoWriterefCounterP::mutex);
231 }
232
233 void
initClass(void)234 SoWriterefCounter::initClass(void)
235 {
236 CC_MUTEX_CONSTRUCT(SoWriterefCounterP::mutex);
237 SoWriterefCounterP::outputdict = new SoOutput2SoWriterefCounterMap;
238 SoWriterefCounterP::refwriteprefix = new SbString("+");
239 coin_atexit((coin_atexit_f*) SoWriterefCounterP::atexit_cleanup, CC_ATEXIT_NORMAL);
240 }
241
242 void
debugCleanup(void)243 SoWriterefCounter::debugCleanup(void)
244 {
245 PRIVATE(this)->sobase2id->clear();
246 PRIVATE(this)->outputdata->debugCleanup();
247 }
248
249 void
setInstancePrefix(const SbString & s)250 SoWriterefCounter::setInstancePrefix(const SbString & s)
251 {
252 (*SoWriterefCounterP::refwriteprefix) = s;
253 }
254
255 SoWriterefCounter *
instance(SoOutput * out)256 SoWriterefCounter::instance(SoOutput * out)
257 {
258 if (out == NULL) {
259 // to be backwards compatible with old code
260 return SoWriterefCounterP::current;
261 }
262
263 CC_MUTEX_LOCK(SoWriterefCounterP::mutex);
264
265 SoWriterefCounter * inst = NULL;
266
267 const SbBool ok = SoWriterefCounterP::outputdict->get(out, inst);
268 assert(ok && "no instance");
269
270 SoWriterefCounterP::current = inst;
271 CC_MUTEX_UNLOCK(SoWriterefCounterP::mutex);
272 return inst;
273 }
274
275 SbBool
shouldWrite(const SoBase * base) const276 SoWriterefCounter::shouldWrite(const SoBase * base) const
277 {
278 SoWriterefCounterBaseData * data;
279 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
280 return data->ingraph;
281 }
282 return FALSE;
283 }
284
285 SbBool
hasMultipleWriteRefs(const SoBase * base) const286 SoWriterefCounter::hasMultipleWriteRefs(const SoBase * base) const
287 {
288 SoWriterefCounterBaseData * data;
289 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
290 return data->writeref > 1;
291 }
292 return FALSE;
293 }
294
295 int
getWriteref(const SoBase * base) const296 SoWriterefCounter::getWriteref(const SoBase * base) const
297 {
298 SoWriterefCounterBaseData * data;
299 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
300 return data->writeref;
301 }
302 return 0;
303 }
304
305 void
setWriteref(const SoBase * base,const int ref)306 SoWriterefCounter::setWriteref(const SoBase * base, const int ref)
307 {
308 // for debugging
309 // SbName name = base->getName();
310 // fprintf(stderr,"writeref: %s, %d, ingraph: %d\n", name.getString(), ref,
311 // isInGraph(base));
312 // }
313
314 SoWriterefCounterBaseData * data;
315 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
316 data->writeref = ref;
317 }
318 else {
319 data = new SoWriterefCounterBaseData;
320 data->writeref = ref;
321 (void) PRIVATE(this)->outputdata->writerefdict.put(base, data);
322 }
323
324
325 if (ref == 0) {
326 SoOutput * out = PRIVATE(this)->out;
327 // for better debugging
328 this->removeWriteref(base);
329 // Ouch. Does this to avoid having two subsequent write actions on
330 // the same SoOutput to write "USE ..." when it should write a
331 // full node/subgraph specification on the second run. -mortene.
332 //
333 // FIXME: accessing out->removeSoBase2IdRef() directly takes a
334 // "friend SoBase" in the SoOutput class definition. Should fix
335 // with proper design for next major Coin release. 20020426 mortene.
336 if (out->findReference(base) != FIRSTWRITE) {
337 out->removeSoBase2IdRef(base);
338 }
339 }
340 if (ref < 0) {
341 SbName name = base->getName();
342 if (name == "") name = "<noname>";
343 SoDebugError::postWarning("SoWriterefCounter::setWriteref",
344 "writeref < 0 for %s <%p>", name.getString(), base);
345 }
346 }
347
348 void
decrementWriteref(const SoBase * base)349 SoWriterefCounter::decrementWriteref(const SoBase * base)
350 {
351 this->setWriteref(base, this->getWriteref(base) - 1);
352 }
353
354
355 SbBool
isInGraph(const SoBase * base) const356 SoWriterefCounter::isInGraph(const SoBase * base) const
357 {
358 SoWriterefCounterBaseData * data;
359 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
360 return data->ingraph;
361 }
362 return FALSE;
363 }
364
365 void
setInGraph(const SoBase * base,const SbBool ingraph)366 SoWriterefCounter::setInGraph(const SoBase * base, const SbBool ingraph)
367 {
368 SoWriterefCounterBaseData * data;
369 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
370 data->ingraph = ingraph;
371 }
372 else {
373 data = new SoWriterefCounterBaseData;
374 data->ingraph = ingraph;
375 (void)PRIVATE(this)->outputdata->writerefdict.put(base, data);
376 }
377 }
378
379 void
removeWriteref(const SoBase * base)380 SoWriterefCounter::removeWriteref(const SoBase * base)
381 {
382 SoWriterefCounterBaseData * data;
383 if (PRIVATE(this)->outputdata->writerefdict.get(base, data)) {
384 delete data;
385 (void) PRIVATE(this)->outputdata->writerefdict.erase(base);
386 }
387 else {
388 assert(0 && "writedata not found");
389 }
390 }
391
392 //
393 // If this environment variable is set to 1, we try to preserve
394 // the original node names as far as possible instead of appending
395 // a "+<refid>" suffix.
396 //
397 static SbBool
dont_mangle_output_names(const SoBase * base)398 dont_mangle_output_names(const SoBase *base)
399 {
400 static int COIN_DONT_MANGLE_OUTPUT_NAMES = -1;
401
402 // Always unmangle node names in VRML1 and VRML2
403 if (base->isOfType(SoNode::getClassTypeId()) &&
404 (((SoNode *)base)->getNodeType()==SoNode::VRML1 ||
405 ((SoNode *)base)->getNodeType()==SoNode::VRML2)) return TRUE;
406
407 if (COIN_DONT_MANGLE_OUTPUT_NAMES < 0) {
408 COIN_DONT_MANGLE_OUTPUT_NAMES = 0;
409 const char * env = coin_getenv("COIN_DONT_MANGLE_OUTPUT_NAMES");
410 if (env) COIN_DONT_MANGLE_OUTPUT_NAMES = atoi(env);
411 }
412 return COIN_DONT_MANGLE_OUTPUT_NAMES ? TRUE : FALSE;
413 }
414
415 SbName
getWriteName(const SoBase * base) const416 SoWriterefCounter::getWriteName(const SoBase * base) const
417 {
418 SoOutput * out = PRIVATE(this)->out;
419 SbName name = base->getName();
420 int refid = out->findReference(base);
421 SbBool firstwrite = (refid == (int) FIRSTWRITE);
422 SbBool multiref = this->hasMultipleWriteRefs(base);
423
424 // Find what node name to write
425 SbString writename;
426
427
428
429
430
431
432 if (!firstwrite) {
433 writename = name.getString();
434 // We have used a suffix when DEF'ing the node
435 if (refid != (int) NOSUFFIX) {
436 PRIVATE(this)->appendPostfix(base, writename, refid);
437 }
438 // Detects last USE of a node, enables reuse of DEF's
439 // if dont_mangle_output_names(base) is true;
440 // Try to keep the original node names as far as possible.
441 // Weaknesses (FIXME kintel 20020429):
442 // o We should try to reuse refid's as well.
443 // o We should try to let "important" (=toplevel?) nodes
444 // keep their original node names before some subnode "captures" it.
445 //
446 if (!multiref && dont_mangle_output_names(base)) out->removeDEFNode(SbName(writename));
447 }
448 else {
449 SbBool found = out->lookupDEFNode(name);
450 writename = name.getString();
451 if (!found && (!multiref || name.getLength() > 0)) {
452 // We can use the node without a suffix
453 if (multiref) out->addDEFNode(name);
454 out->setReference(base, (int) NOSUFFIX);
455 }
456 if ((found && name.getLength()>0) || (multiref && name.getLength()==0)) {
457 // Node name is already DEF'ed or an unnamed multiref => use a suffix.
458 PRIVATE(this)->appendPostfix(base, writename, out->addReference(base));
459 out->addDEFNode(SbName(writename));
460 }
461 }
462
463 return writename;
464 }
465
466 /*!
467 Makes a unique id for \a base and adds a mapping into our dictionary.
468 */
469 int
addReference(const SoBase * base)470 SoWriterefCounter::addReference(const SoBase * base)
471 {
472 if (!PRIVATE(this)->sobase2id) PRIVATE(this)->sobase2id = new SoBase2Id;
473 const int id = PRIVATE(this)->nextreferenceid++;
474 PRIVATE(this)->sobase2id->put(base, id);
475 return id;
476 }
477
478 /*!
479 Returns the unique identifier for \a base or -1 if not found.
480 */
481 int
findReference(const SoBase * base) const482 SoWriterefCounter::findReference(const SoBase * base) const
483 {
484 int id;
485 const SbBool ok =
486 PRIVATE(this)->sobase2id &&
487 PRIVATE(this)->sobase2id->get(base, id);
488 return ok ? id : -1;
489 }
490
491 /*!
492 Sets the reference for \a base manually.
493 */
494 void
setReference(const SoBase * base,int refid)495 SoWriterefCounter::setReference(const SoBase * base, int refid)
496 {
497 if (!PRIVATE(this)->sobase2id) PRIVATE(this)->sobase2id = new SoBase2Id;
498 PRIVATE(this)->sobase2id->put(base, refid);
499 }
500
501 void
removeSoBase2IdRef(const SoBase * base)502 SoWriterefCounter::removeSoBase2IdRef(const SoBase * base)
503 {
504 PRIVATE(this)->sobase2id->erase(base);
505 }
506
507 /*!
508 Returns TRUE if the user wants extra debugging information regarding
509 writerefs (COIN_DEBUG_WRITEREFS=1)
510 */
511 SbBool
debugWriterefs(void)512 SoWriterefCounter::debugWriterefs(void)
513 {
514 static int dbg = -1;
515 if (dbg == -1) {
516 const char * env = coin_getenv("COIN_DEBUG_WRITEREFS");
517 dbg = (env && (atoi(env) > 0)) ? 1 : 0;
518 }
519 return dbg;
520 }
521
522 /**********************************************************************/
523
524
525 #undef PRIVATE
526
527