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