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 /*!
34   \class SoOutput SoOutput.h Inventor/SoOutput.h
35   \brief The SoOutput class is an abstraction of an output stream.
36 
37   \ingroup general
38 
39   SoOutput offers the ability to write basic types to a file or a
40   memory buffer in either ASCII or binary format.
41 
42   \sa SoInput, SoWriteAction
43 */
44 
45 /*!
46   \page compression_overview File compression
47 
48   Coin can support reading and writing Inventor and VRML files compressed
49   with either gzip or bzip2 (as of yet - more formats may come in the
50   future).  This functionality is of course only enabled when Coin can link
51   with the corresponding compression libraries.
52 
53   Reading of compressed files happens in a transparent manner - there is
54   nothing the developer has to do to support this.
55 
56   Writing of compressed files is controlled through the
57   SoOutput::setCompression method.
58 
59   The list of available compression methods can be fetched with the
60   SoOutput::getAvailableCompressionMethods method.
61 
62   You can not use file compression together with I/O to memory buffers,
63   except for reading from memory buffers containing gzip-compressed files.
64 
65   For backwards compatibility with Coin 2.0 and Coin 1.0, compressed files
66   must not be used.  Compressed files works only from Coin 2.1 and
67   upwards.
68 
69   \since Coin 2.1
70   \sa SoOutput::setCompression, SoOutput::getAvailableCompressionMethods
71 */
72 
73 // *************************************************************************
74 
75 /*! \file SoOutput.h */
76 #include <Inventor/SoOutput.h>
77 
78 #ifdef HAVE_CONFIG_H
79 #include "config.h"
80 #endif // HAVE_CONFIG_H
81 
82 #include <cassert>
83 #include <cstring>
84 
85 #ifdef HAVE_WINDOWS_H
86 #include <windows.h>
87 #endif // HAVE_WINDOWS_H
88 
89 #include <Inventor/C/tidbits.h>
90 #include <Inventor/errors/SoDebugError.h>
91 #include <Inventor/SbName.h>
92 #include <Inventor/SbString.h>
93 #include <Inventor/lists/SbList.h>
94 #include <Inventor/lists/SoFieldList.h>
95 #include <Inventor/fields/SoFieldContainer.h>
96 #include <Inventor/fields/SoField.h>
97 
98 #include "tidbitsp.h"
99 #include "misc/SbHash.h"
100 #include "glue/zlib.h"
101 #include "glue/bzip2.h"
102 #include "io/SoOutput_Writer.h"
103 #include "io/SoWriterefCounter.h"
104 
105 // *************************************************************************
106 
107 /*! \enum SoOutput::Stage
108   Enumerates the possible stages of a write operation (writing needs to be
109   done in mutiple passes).
110 
111   \sa setStage(), getStage()
112 */
113 /*! \var SoOutput::COUNT_REFS
114   Not writing, just counting the internal references in the scene graph.
115 */
116 /*! \var SoOutput::WRITE
117   Signifies that actual data export should take place during this pass.
118 */
119 
120 /*! \enum SoOutput::Annotations
121   Values from this enum is used for debugging purposes to annotate the
122   output from a write operation.
123 */
124 /*! \var SoOutput::ADDRESSES
125   Annotate output with pointer address information.
126 */
127 /*! \var SoOutput::REF_COUNTS
128   Annotate output with reference counts of the objects written.
129 */
130 
131 /*! \var SbBool SoOutput::wroteHeader
132   Indicates whether or not the file format header has been written out.
133   As long as this is \a FALSE, the header will be written once upon the
134   first invocation of any write method in the class.
135 */
136 
137 // *************************************************************************
138 
139 // FIXME: need to fix EOL on other platforms? 19990621 mortene.
140 static const char EOLSTR[] = "\n";
141 
142 // FIXME: I guess this should be modified on non-32 bit platforms? Or?
143 // Wouldn't that puck up cross-platform compatibility of binary files?
144 // 19990627 mortene.
145 static const size_t HOSTWORDSIZE = 4;
146 
147 // *************************************************************************
148 
149 // helper classes for storing ROUTEs
150 class SoOutputROUTE {
151 public:
152   SoFieldContainer * from, * to;
153   SbName fromfield, tofield;
154 };
155 
156 class SoOutputROUTEList : public SbList<SoOutputROUTE> {
157 public:
SoOutputROUTEList(void)158   SoOutputROUTEList(void) : SbList<SoOutputROUTE>() { }
SoOutputROUTEList(const int sizehint)159   SoOutputROUTEList(const int sizehint) : SbList<SoOutputROUTE>(sizehint) { }
SoOutputROUTEList(const SoOutputROUTEList & l)160   SoOutputROUTEList(const SoOutputROUTEList & l) : SbList<SoOutputROUTE>(l) { }
161 
set(const int index,SoOutputROUTE item)162   void set(const int index, SoOutputROUTE item) { (*this)[index] = item; }
163 };
164 
165 // *************************************************************************
166 
SbHashFunc(const SoOutput * key)167 unsigned int SbHashFunc(const SoOutput * key) {
168   return SbHashFunc(reinterpret_cast<size_t>(key));
169 }
170 
171 // FIXME: should use a real set datatype -- the object mapped to is
172 // just a dummy. 20050524 mortene.
173 typedef SbHash<const char *, void *> BogusSet;
174 
175 class SoOutputP {
176 public:
SoOutputP(void)177   SoOutputP(void) {
178     this->writer = NULL;
179   }
~SoOutputP()180   ~SoOutputP() {
181     delete this->writer;
182   }
183 
184   SbBool binarystream;
185   SbBool usercalledopenfile;
186   SbString fltprecision;
187   SbString dblprecision;
188   int indentlevel;
189   SbBool writecompact;
190   SbBool disabledwriting;
191   SbString * headerstring;
192   SoOutput::Stage stage;
193   uint32_t annotationbits;
194   SbList <SoProto*> protostack;
195   SbList <BogusSet *> defstack;
196   SbList <SoOutputROUTEList *> routestack;
197   SoWriterefCounter * counter;
198 
199   SbName compmethod;
200   float complevel;
201 
pushRoutes(const SbBool copyprev)202   void pushRoutes(const SbBool copyprev) {
203     const int oldidx = this->routestack.getLength() - 1;
204     assert(oldidx >= 0);
205     SoOutputROUTEList * newlist;
206     SoOutputROUTEList * oldlist = this->routestack[oldidx];
207     if (copyprev && oldlist && oldlist->getLength()) {
208       newlist = new SoOutputROUTEList(*oldlist);
209     }
210     else newlist = new SoOutputROUTEList;
211     this->routestack.push(newlist);
212   }
getCurrentRoutes(const SbBool createifnull)213   SoOutputROUTEList * getCurrentRoutes(const SbBool createifnull) {
214     const int n = this->routestack.getLength();
215     assert(n);
216     SoOutputROUTEList * list = this->routestack[n-1];
217     if (list == NULL && createifnull) {
218       list = new SoOutputROUTEList;
219       this->routestack[n-1] = list;
220     }
221     return list;
222   }
223 
popRoutes(void)224   void popRoutes(void) {
225     const int idx = this->routestack.getLength()-1;
226     assert(idx >= 0);
227     delete this->routestack[idx];
228     this->routestack.remove(idx);
229   }
230 
pushDefNames(const SbBool copyprev)231   void pushDefNames(const SbBool copyprev) {
232     const int n = this->defstack.getLength();
233     assert(n);
234     BogusSet * prev = this->defstack[n-1];
235     if (copyprev && prev) {
236       this->defstack.append(new BogusSet(*prev));
237     }
238     else this->defstack.append(NULL);
239   }
popDefNames(void)240   void popDefNames(void) {
241     assert(this->defstack.getLength());
242     delete this->defstack[this->defstack.getLength()-1];
243     this->defstack.pop();
244   }
getCurrentDefNames(const SbBool createifnull)245   BogusSet * getCurrentDefNames(const SbBool createifnull) {
246     const int idx = this->defstack.getLength() - 1;
247     assert(idx >= 0);
248     BogusSet * dict = this->defstack[idx];
249     if (createifnull && dict == NULL) {
250       dict = new BogusSet;
251       this->defstack[idx] = dict;
252     }
253     return dict;
254   }
255 
getWriter(void)256   SoOutput_Writer * getWriter(void) {
257     if (this->writer == NULL) {
258       this->writer = SoOutput_Writer::createWriter(coin_get_stdout(), FALSE,
259                                                    this->compmethod, this->complevel);
260     }
261     return this->writer;
262   }
setWriter(SoOutput_Writer * writerptr)263   void setWriter(SoOutput_Writer * writerptr) {
264     delete this->writer;
265     this->writer = writerptr;
266   }
267 private:
268   SoOutput_Writer * writer;
269 
270 };
271 
272 static SbList <SbName> * SoOutput_compmethods = NULL;
273 
274 // *************************************************************************
275 
276 static void
SoOutput_compression_list_cleanup(void)277 SoOutput_compression_list_cleanup(void)
278 {
279   delete SoOutput_compmethods;
280   SoOutput_compmethods = NULL;
281 }
282 
283 static void
SoOutput_compression_list_init(void)284 SoOutput_compression_list_init(void)
285 {
286   if (SoOutput_compmethods) return;
287 
288   SoOutput_compmethods = new SbList <SbName>;
289   if (cc_zlibglue_available()) {
290     SoOutput_compmethods->append(SbName("GZIP"));
291   }
292   if (cc_bzglue_available()) {
293     SoOutput_compmethods->append(SbName("BZIP2"));
294   }
295   coin_atexit((coin_atexit_f*) SoOutput_compression_list_cleanup, CC_ATEXIT_NORMAL);
296 }
297 
298 #define PRIVATE(obj) (obj->pimpl)
299 
300 /*!
301   The default constructor makes an SoOutput instance which will write
302   to the standard output.
303 
304   \sa setFilePointer(), openFile(), setBuffer()
305 */
SoOutput(void)306 SoOutput::SoOutput(void)
307 {
308   this->constructorCommon();
309   PRIVATE(this)->defstack.append(NULL);
310 
311   SoWriterefCounter::create(this, NULL);
312   PRIVATE(this)->counter = SoWriterefCounter::instance(this);
313 }
314 
315 /*!
316   Constructs an SoOutput which has a copy of the set of named
317   references from \a dictOut.
318 */
SoOutput(SoOutput * dictOut)319 SoOutput::SoOutput(SoOutput * dictOut)
320 {
321   assert(dictOut != NULL);
322   this->constructorCommon();
323 
324   BogusSet * olddef = PRIVATE(dictOut)->getCurrentDefNames(FALSE);
325   PRIVATE(this)->defstack.append(olddef ? new BogusSet(*olddef) : NULL);
326 
327   SoWriterefCounter::create(this, dictOut);
328   PRIVATE(this)->counter = SoWriterefCounter::instance(this);
329 }
330 
331 /*!
332   \COININTERNAL
333   Common constructor actions.
334  */
335 void
constructorCommon(void)336 SoOutput::constructorCommon(void)
337 {
338   PRIVATE(this) = new SoOutputP;
339 
340   PRIVATE(this)->usercalledopenfile = FALSE;
341   PRIVATE(this)->binarystream = FALSE;
342   PRIVATE(this)->fltprecision = "%.8g";
343   PRIVATE(this)->dblprecision = "%.16lg";
344   PRIVATE(this)->disabledwriting = FALSE;
345   this->wroteHeader = FALSE;
346   PRIVATE(this)->writecompact = FALSE;
347   PRIVATE(this)->headerstring = NULL;
348   PRIVATE(this)->indentlevel = 0;
349   PRIVATE(this)->annotationbits = 0x00;
350   PRIVATE(this)->routestack.append(NULL);
351 
352   PRIVATE(this)->compmethod = SbName("NONE");
353   PRIVATE(this)->complevel = 0.0f;;
354 }
355 
356 /*!
357   Destructor.
358 */
~SoOutput(void)359 SoOutput::~SoOutput(void)
360 {
361   SoWriterefCounter::destruct(this);
362   this->reset();
363   delete PRIVATE(this)->headerstring;
364   delete PRIVATE(this);
365 }
366 
367 /*!
368   Set up a new file pointer which we will write to.
369 
370   Important note: do \e not use this method when the Coin library has
371   been compiled as an MSWindows DLL, as passing FILE* instances back
372   or forth to DLLs is dangerous and will most likely cause a
373   crash. This is an intrinsic limitation for MSWindows DLLs.
374 
375   \sa openFile(), setBuffer(), getFilePointer()
376  */
377 void
setFilePointer(FILE * newFP)378 SoOutput::setFilePointer(FILE * newFP)
379 {
380   this->reset();
381   PRIVATE(this)->setWriter(SoOutput_Writer::createWriter(newFP, FALSE,
382                                                          PRIVATE(this)->compmethod,
383                                                          PRIVATE(this)->complevel));
384 }
385 
386 /*!
387   Returns the current filepointer. If we're writing to a memory
388   buffer, \c NULL is returned.
389 
390   Important note: do \e not use this method when the Coin library has
391   been compiled as an MSWindows DLL, as passing FILE* instances back
392   or forth to DLLs is dangerous and will most likely cause a
393   crash. This is an intrinsic limitation for MSWindows DLLs.
394 
395   \sa setFilePointer()
396  */
397 FILE *
getFilePointer(void) const398 SoOutput::getFilePointer(void) const
399 {
400   return PRIVATE(this)->getWriter()->getFilePointer();
401 }
402 
403 /*!
404   Opens a file for writing. If the file can not be opened or is not
405   writeable, \a FALSE will be returned.
406 
407   Files opened by this method will automatically be closed if the
408   user supplies another filepointer, another filename for writing,
409   or if the SoOutput instance is deleted.
410 
411   \sa setFilePointer(), setBuffer(), closeFile()
412  */
413 SbBool
openFile(const char * const fileName)414 SoOutput::openFile(const char * const fileName)
415 {
416   this->reset();
417 
418   FILE * newfile = fopen(fileName, "wb");
419   if (newfile) {
420     PRIVATE(this)->setWriter(SoOutput_Writer::createWriter(newfile, TRUE,
421                                                            PRIVATE(this)->compmethod,
422                                                            PRIVATE(this)->complevel));
423     PRIVATE(this)->usercalledopenfile = TRUE;
424   }
425   else {
426     SoDebugError::postWarning("SoOutput::openFile",
427                               "Couldn't open file '%s' for writing.",
428                               fileName);
429   }
430   return newfile != NULL;
431 }
432 
433 /*!
434   Closes the currently opened file, but only if the file was passed to
435   SoOutput through the openFile() method.
436 
437   \sa openFile()
438  */
439 void
closeFile(void)440 SoOutput::closeFile(void)
441 {
442   if (PRIVATE(this)->usercalledopenfile) {
443     PRIVATE(this)->setWriter(NULL);
444     PRIVATE(this)->usercalledopenfile = FALSE;
445   }
446 }
447 
448 /*!
449 
450   Sets the compression method and level used when writing the file. \a
451   compmethod is the compression library/method to use when
452   compressing. \a level is the compression level, where 0.0 means no
453   compression and 1.0 means maximum compression.
454 
455   Currently \e BZIP2, \e GZIP are the only compression methods
456   supported, and you have to compile Coin with zlib and bzip2-support
457   to enable them.
458 
459   Supply \a compmethod = \e NONE or \e level = 0.0 if you want to
460   disable compression. The compression is disabled by default.
461 
462   Please note that it's not possible to compress when writing to a
463   memory buffer.
464 
465   This method will return \e TRUE if the compression method selected
466   is available. If it's not available, \e FALSE will be returned and
467   compression is disabled.
468 
469   \sa getAvailableCompressionMethods()
470   \since Coin 2.1
471  */
472 SbBool
setCompression(const SbName & compmethod,const float level)473 SoOutput::setCompression(const SbName & compmethod, const float level)
474 {
475   PRIVATE(this)->complevel = level;
476   PRIVATE(this)->compmethod = compmethod;
477 
478   if (compmethod == "GZIP") {
479     if (cc_zlibglue_available()) return TRUE;
480     SoDebugError::postWarning("SoOutput::setCompression",
481                               "Requested GZIP compression, but zlib is not available.");
482 
483   }
484   if (compmethod == "BZIP2") {
485     if (cc_bzglue_available()) return TRUE;
486     SoDebugError::postWarning("SoOutput::setCompression",
487                               "Requested BZIP2 compression, but libbz2 is not available.");
488   }
489 
490   PRIVATE(this)->compmethod = SbName("NONE");
491   PRIVATE(this)->complevel = 0.0f;
492 
493   if (compmethod == "NONE" || level == 0.0f) return TRUE;
494   SoDebugError::postWarning("SoOutput::setCompression",
495                             "Unsupported compression method: %s",
496                             compmethod.getString());
497   return FALSE;
498 }
499 
500 /*!
501   Returns the array of available compression methods. The number
502   of elements in the array will be stored in \a num.
503 
504   \sa setCompression()
505   \since Coin 2.1
506  */
507 const SbName *
getAvailableCompressionMethods(unsigned int & num)508 SoOutput::getAvailableCompressionMethods(unsigned int & num)
509 {
510   SoOutput_compression_list_init();
511   num = SoOutput_compmethods->getLength();
512   return SoOutput_compmethods->getArrayPtr();
513 }
514 
515 /*!
516   Sets up a memory buffer of size \a initSize for writing.
517   Writing will start at \a bufPointer + \a offset.
518 
519   If the buffer is filled up, \a reallocFunc is called to get more
520   memory. If \a reallocFunc returns \a NULL, further writing is
521   disabled.
522 
523   Important note: remember that the resultant memory buffer after
524   write operations have completed may reside somewhere else in memory
525   than on \a bufPointer if \a reallocFunc is set. It is a good idea to
526   make it a habit to always use getBuffer() to retrieve the memory
527   buffer pointer after write operations.
528 
529   Here's a complete, stand-alone usage example which shows how to
530   write a scene graph to a memory buffer:
531 
532   \code
533   #include <Inventor/SoDB.h>
534   #include <Inventor/actions/SoWriteAction.h>
535   #include <Inventor/nodes/SoCone.h>
536   #include <Inventor/nodes/SoSeparator.h>
537 
538   static char * buffer;
539   static size_t buffer_size = 0;
540 
541   static void *
542   buffer_realloc(void * bufptr, size_t size)
543   {
544     buffer = (char *)realloc(bufptr, size);
545     buffer_size = size;
546     return buffer;
547   }
548 
549   static SbString
550   buffer_writeaction(SoNode * root)
551   {
552     SoOutput out;
553     buffer = (char *)malloc(1024);
554     buffer_size = 1024;
555     out.setBuffer(buffer, buffer_size, buffer_realloc);
556 
557     SoWriteAction wa(&out);
558     wa.apply(root);
559 
560     SbString s(buffer);
561     free(buffer);
562     return s;
563   }
564 
565   int
566   main(int argc, char ** argv)
567   {
568     SoDB::init();
569 
570     SoSeparator * root = new SoSeparator;
571     root->ref();
572 
573     root->addChild(new SoCone);
574 
575     SbString s = buffer_writeaction(root);
576     (void)fprintf(stdout, "%s\n", s.getString());
577 
578     root->unref();
579     return 0;
580   }
581   \endcode
582 
583   \sa getBuffer(), getBufferSize(), resetBuffer()
584 */
585 void
setBuffer(void * bufPointer,size_t initSize,SoOutputReallocCB * reallocFunc,int32_t offset)586 SoOutput::setBuffer(void * bufPointer, size_t initSize,
587                     SoOutputReallocCB * reallocFunc, int32_t offset)
588 {
589   this->reset();
590   assert(initSize > 0 && "invalid argument");
591   PRIVATE(this)->setWriter(new SoOutput_MemBufferWriter(bufPointer, initSize,
592                                                         reallocFunc, offset));
593 }
594 
595 /*!
596   Returns the current buffer in \a bufPointer and the current
597   write position of the buffer in \a nBytes. If we're writing into a
598   file and not a memory buffer, \a FALSE is returned and the other return
599   values will be undefined.
600 
601   \sa getBufferSize()
602  */
603 SbBool
getBuffer(void * & bufPointer,size_t & nBytes) const604 SoOutput::getBuffer(void *& bufPointer, size_t & nBytes) const
605 {
606   if (PRIVATE(this)->getWriter()->getType() == SoOutput_Writer::MEMBUFFER) {
607     SoOutput_MemBufferWriter * w = (SoOutput_MemBufferWriter*) PRIVATE(this)->getWriter();
608     bufPointer = w->buf;
609     nBytes = (size_t) w->offset;
610     return TRUE;
611   }
612   return FALSE;
613 }
614 
615 /*!
616   Returns total size of memory buffer.
617 
618   \sa getBuffer()
619  */
620 size_t
getBufferSize(void) const621 SoOutput::getBufferSize(void) const
622 {
623   if (PRIVATE(this)->getWriter()->getType() == SoOutput_Writer::MEMBUFFER) {
624     SoOutput_MemBufferWriter * w = (SoOutput_MemBufferWriter*) PRIVATE(this)->getWriter();
625     return w->bufsize;
626   }
627   return 0;
628 }
629 
630 /*!
631   Reset the memory buffer write pointer back to the beginning of the
632   buffer.
633  */
634 void
resetBuffer(void)635 SoOutput::resetBuffer(void)
636 {
637   assert(this->isToBuffer());
638   if (PRIVATE(this)->getWriter()->getType() == SoOutput_Writer::MEMBUFFER) {
639     SoOutput_MemBufferWriter * w = (SoOutput_MemBufferWriter*) PRIVATE(this)->getWriter();
640     w->offset = w->startoffset;
641   }
642 }
643 
644 /*!
645   Set whether or not to write the output as a binary stream.
646 
647   \sa isBinary()
648  */
649 // FIXME: write doc on endianness, netformat etc -- best thing would
650 // be to document the format completely in BNF. 19990627 mortene.
651 void
setBinary(const SbBool flag)652 SoOutput::setBinary(const SbBool flag)
653 {
654   PRIVATE(this)->binarystream = flag;
655 }
656 
657 /*!
658   Returns a flag which indicates whether or not we're writing the output
659   as a binary stream.
660 
661   \sa setBinary()
662  */
663 SbBool
isBinary(void) const664 SoOutput::isBinary(void) const
665 {
666   return PRIVATE(this)->binarystream;
667 }
668 
669 /*!
670   Set the output file header string.
671 
672   \sa resetHeaderString(), getDefaultASCIIHeader(), getDefaultBinaryHeader()
673  */
674 void
setHeaderString(const SbString & str)675 SoOutput::setHeaderString(const SbString & str)
676 {
677   if (PRIVATE(this)->headerstring) *(PRIVATE(this)->headerstring) = str;
678   else PRIVATE(this)->headerstring = new SbString(str);
679 }
680 
681 /*!
682   Reset the header string to the default one.
683 
684   \sa setHeaderString(), getDefaultASCIIHeader(), getDefaultBinaryHeader()
685  */
686 void
resetHeaderString(void)687 SoOutput::resetHeaderString(void)
688 {
689   delete PRIVATE(this)->headerstring;
690   PRIVATE(this)->headerstring = NULL;
691 }
692 
693 /*!
694   Return the default header string written to ASCII files.
695 
696   \sa setHeaderString(), resetHeaderString(), getDefaultBinaryHeader()
697  */
698 SbString
getDefaultASCIIHeader(void)699 SoOutput::getDefaultASCIIHeader(void)
700 {
701   return SbString("#Inventor V2.1 ascii");
702 }
703 
704 /*!
705   Return the default header string written to binary files.
706 
707   \sa setHeaderString(), resetHeaderString(), getDefaultASCIIHeader()
708  */
709 SbString
getDefaultBinaryHeader(void)710 SoOutput::getDefaultBinaryHeader(void)
711 {
712   return SbString("#Inventor V2.1 binary");
713 }
714 
715 /*!
716   Set the precision used when writing floating point numbers to ASCII
717   files. \a precision should be between 0 and 8.  The double precision
718   will be set to \a precision * 2.
719 */
720 void
setFloatPrecision(const int precision)721 SoOutput::setFloatPrecision(const int precision)
722 {
723   const int fltnum = SbClamp(precision, 0, 8);
724   const int dblnum = precision * 2;
725 
726   PRIVATE(this)->fltprecision.sprintf("%%.%dg", fltnum);
727   PRIVATE(this)->dblprecision.sprintf("%%.%dlg", dblnum);
728 }
729 
730 /*!
731   Sets an indicator on the current stage. This is necessary to do as writing
732   has to be done in multiple stages to account for the export of
733   references/connections within the scene graphs.
734 
735   This method is basically just used from within SoWriteAction, and
736   should usually not be of interest to the application programmer. Do
737   not use it unless you \e really know what you are doing.
738 
739   \sa getStage()
740 */
741 void
setStage(Stage stage)742 SoOutput::setStage(Stage stage)
743 {
744   PRIVATE(this)->stage = stage;
745 }
746 
747 /*!
748   Returns an indicator on the current write stage. Writing is done in two
749   passes, one to count and check connections, one to do the actual ascii or
750   binary export of data.
751 
752   You should not need to use this method, as it is meant for internal
753   purposes in Coin.
754 
755   \sa setStage()
756 */
757 SoOutput::Stage
getStage(void) const758 SoOutput::getStage(void) const
759 {
760   return PRIVATE(this)->stage;
761 }
762 
763 
764 /*!
765   Write the character in \a c.
766 
767   For binary write, the character plus 3 padding zero characters will be
768   written.
769  */
770 void
write(const char c)771 SoOutput::write(const char c)
772 {
773   this->writeBytesWithPadding(&c, 1);
774 }
775 
776 /*!
777   Write the character string pointed to by \a s.
778 
779   For binary write, a 4-byte MSB-ordered integer with the string length,
780   plus the string plus padding zero characters to get on a 4-byte boundary
781   (if necessary) will be written.
782  */
783 void
write(const char * s)784 SoOutput::write(const char * s)
785 {
786   const size_t writelen = strlen(s);
787   if (this->isBinary()) { this->write((int)writelen); }
788   this->writeBytesWithPadding(s, (int)writelen);
789 }
790 
791 /*!
792   Write the character string in \a s. The string will be written with
793   apostrophes. Cast SbString to char * to write without apostrophes.
794 
795   If we are supposed to write in binary format, no apostrophes will be
796   added, and writing will be done in the exact same manner as with
797   SoOutput::write(const char * s).
798  */
799 void
write(const SbString & s)800 SoOutput::write(const SbString & s)
801 {
802   if (this->isBinary()) {
803     this->write(s.getString());
804   }
805   else {
806     // Backslash-quote all apostrophe-characters, i.e. " -> \".
807     //
808     // Note that VRML97 also needs backslashes themselves to be
809     // backslash-quoted (like in e.g. C strings), but this is taken
810     // care of upstream (in SoSFString's write method), since we can't
811     // know here whether or not we're writing a VRML97 node.
812     //
813     // FIXME: SbString should have had a replaceAll() method, so we
814     // wouldn't have to spell out the iteration loop below. 20040614 mortene.
815     SbString ws("\"");
816     for (int i=0;i<s.getLength();i++) {
817       if (s[i] == '"') ws += "\\";
818       ws += s[i];
819     }
820     ws += "\"";
821     this->write(ws.getString());
822   }
823 }
824 
825 /*!
826   Write the character string in \a n. The name will be enclosed by
827   apostrophes. If you want to write an SbName instance without the
828   apostrophes, cast the argument to a char *.
829 
830   If we are supposed to write in binary format, no apostrophes will be
831   added, and writing will be done in the exact same manner as with
832   SoOutput::write(const char * s).
833  */
834 void
write(const SbName & n)835 SoOutput::write(const SbName & n)
836 {
837   // Simply use SoOutput::write(const SbString &).
838   SbString s(n.getString());
839   this->write(s);
840 }
841 
842 /*!
843   Write \a i as a character string, or as an architecture independent binary
844   pattern if the setBinary() flag is activated.
845  */
846 void
write(const int i)847 SoOutput::write(const int i)
848 {
849   if (!this->isBinary()) {
850     // Use portable locale, to make sure we don't write thousands
851     // separators for integers.
852     cc_string storedlocale;
853     SbBool changed = coin_locale_set_portable(&storedlocale);
854 
855     SbString s;
856     s.sprintf("%d", i);
857     this->writeBytesWithPadding(s.getString(), s.getLength());
858 
859     if (changed) { coin_locale_reset(&storedlocale); }
860   }
861   else {
862     // FIXME: breaks on 64-bit architectures, which is pretty
863     // lame... 19990621 mortene.
864     assert(sizeof(int) == sizeof(int32_t));
865     int32_t val = i;
866     this->writeBinaryArray(&val, 1);
867   }
868 }
869 
870 /*!
871   Write \a i as a character string, or as an architecture independent binary
872   pattern if the setBinary() flag is activated.
873  */
874 void
write(const unsigned int i)875 SoOutput::write(const unsigned int i)
876 {
877   if (!this->isBinary()) {
878     SbString s;
879     s.sprintf("0x%x", i);
880     this->writeBytesWithPadding(s.getString(), s.getLength());
881   }
882   else {
883     assert(sizeof(i) == sizeof(int32_t));
884     char buff[sizeof(i)];
885     this->convertInt32((int32_t)i, buff);
886     this->writeBytesWithPadding(buff, sizeof(i));
887   }
888 }
889 
890 /*!
891   Write \a s as a character string, or as an architecture independent binary
892   pattern if the setBinary() flag is activated.
893  */
894 void
write(const short s)895 SoOutput::write(const short s)
896 {
897   if (!this->isBinary()) {
898     // Use portable locale, to make sure we don't write thousands
899     // separators for integers.
900     cc_string storedlocale;
901     SbBool changed = coin_locale_set_portable(&storedlocale);
902 
903     SbString str;
904     str.sprintf("%hd", s);
905     this->writeBytesWithPadding(str.getString(), str.getLength());
906 
907     if (changed) { coin_locale_reset(&storedlocale); }
908   }
909   else {
910     this->write((int)s);
911   }
912 }
913 
914 /*!
915   Write \a s as a character string, or as an architecture independent binary
916   pattern if the setBinary() flag is activated. If we're writing in ASCII
917   format, the value will be written in base 16 (hexadecimal).
918  */
919 void
write(const unsigned short s)920 SoOutput::write(const unsigned short s)
921 {
922   if (!this->isBinary()) {
923     SbString str;
924     str.sprintf("0x%hx", s);
925     this->writeBytesWithPadding(str.getString(), str.getLength());
926   }
927   else {
928     this->write((unsigned int)s);
929   }
930 }
931 
932 /*!
933   Write \a f as a character string.
934  */
935 void
write(const float f)936 SoOutput::write(const float f)
937 {
938   if (!this->isBinary()) {
939     // Use portable locale, to make sure we don't write thousands
940     // separators for integers.
941     cc_string storedlocale;
942     SbBool changed = coin_locale_set_portable(&storedlocale);
943 
944     SbString s;
945     s.sprintf(PRIVATE(this)->fltprecision.getString(), f);
946 
947     // make sure scientific exponential is written in a platform independent way
948     // always with three digits
949     int pos = s.find("e");
950     if(pos>0)
951       {
952 	SbString exponential;
953 	exponential.sprintf("%03d",atoi(s.getSubString(pos+2).getString()));
954 	s=s.getSubString(0,pos+1)+exponential;
955 
956       }
957 
958     this->writeBytesWithPadding(s.getString(), s.getLength());
959 
960     if (changed) { coin_locale_reset(&storedlocale); }
961   }
962   else {
963     char buff[sizeof(f)];
964     this->convertFloat(f, buff);
965     this->writeBytesWithPadding(buff, sizeof(f));
966   }
967 }
968 
969 /*!
970   Write \a d as a character string.
971  */
972 void
write(const double d)973 SoOutput::write(const double d)
974 {
975   if (!this->isBinary()) {
976     // Use portable locale, to make sure we don't write thousands
977     // separators for integers.
978     cc_string storedlocale;
979     SbBool changed = coin_locale_set_portable(&storedlocale);
980 
981     SbString s;
982     s.sprintf(PRIVATE(this)->dblprecision.getString(), d);
983 
984     // make sure scientific exponential is written in a platform independent way
985     // always with three digits
986     int pos = s.find("e");
987     if(pos>0)
988       {
989 	SbString exponential;
990 	exponential.sprintf("%03d",atoi(s.getSubString(pos+2).getString()));
991 	s=s.getSubString(0,pos+1)+exponential;
992       }
993 
994 
995     this->writeBytesWithPadding(s.getString(), s.getLength());
996 
997     if (changed) { coin_locale_reset(&storedlocale); }
998   }
999   else {
1000     char buff[sizeof(d)];
1001     this->convertDouble(d, buff);
1002     this->writeBytesWithPadding(buff, sizeof(d));
1003   }
1004 }
1005 
1006 #ifdef __CYGWIN__
1007 #include <boost/static_assert.hpp>
1008 
1009 void
write(long int i)1010 SoOutput::write(long int i)
1011 {
1012   BOOST_STATIC_ASSERT(sizeof(long int) == sizeof(int));
1013   write(static_cast<int>(i));
1014 }
1015 
1016 void
write(unsigned long int i)1017 SoOutput::write(unsigned long int i)
1018 {
1019   BOOST_STATIC_ASSERT(sizeof(unsigned long int) == sizeof(unsigned int));
1020   write(static_cast<unsigned int>(i));
1021 }
1022 #endif //__CYGWIN__
1023 
1024 /*!
1025   Write the given number of bytes to either a file or a memory buffer in
1026   binary format.
1027  */
1028 void
writeBinaryArray(const unsigned char * constc,const int length)1029 SoOutput::writeBinaryArray(const unsigned char * constc, const int length)
1030 {
1031   // Note: the "length" argument should really have been "size_t", but
1032   // this is in the public API, so I've chosen to keep it as this (for
1033   // now).  -mortene.
1034 
1035   if (PRIVATE(this)->disabledwriting) return;
1036 
1037   this->checkHeader();
1038 
1039   size_t wrote = PRIVATE(this)->getWriter()->write((const char*) constc,
1040                                                    (size_t) length,
1041                                                    PRIVATE(this)->binarystream);
1042   if (wrote != (size_t)length) {
1043     SoDebugError::postWarning("SoOutput::writeBinaryArray",
1044                               "Couldn't write to file/memory buffer");
1045     PRIVATE(this)->disabledwriting = TRUE;
1046   }
1047 }
1048 
1049 /*!
1050   Write an \a length array of int32_t values in binary format.
1051  */
1052 void
writeBinaryArray(const int32_t * const l,const int length)1053 SoOutput::writeBinaryArray(const int32_t * const l, const int length)
1054 {
1055   // Slooooow. We can do much better by using convertInt32Array().
1056 
1057   char val[sizeof(int32_t)];
1058   for (int i=0; i < length; i++) {
1059     this->convertInt32(l[i], val);
1060     this->writeBytesWithPadding(val, sizeof(int32_t));
1061   }
1062 }
1063 
1064 /*!
1065   Write an array of float values in binary format.
1066  */
1067 void
writeBinaryArray(const float * const f,const int length)1068 SoOutput::writeBinaryArray(const float * const f, const int length)
1069 {
1070   // Slooooow. We can do much better by using convertFloatArray().
1071 
1072   char val[sizeof(float)];
1073   for (int i=0; i < length; i++) {
1074     this->convertFloat(f[i], val);
1075     this->writeBytesWithPadding(val, sizeof(float));
1076   }
1077 }
1078 
1079 /*!
1080   Write an array of double values in binary format.
1081  */
1082 void
writeBinaryArray(const double * const d,const int length)1083 SoOutput::writeBinaryArray(const double * const d, const int length)
1084 {
1085   // Slooooow. We can do much better by using convertDoubleArray().
1086 
1087   char val[sizeof(double)];
1088   for (int i=0; i < length; i++) {
1089     this->convertDouble(d[i], val);
1090     this->writeBytesWithPadding(val, sizeof(double));
1091   }
1092 }
1093 
1094 /*!
1095   Increase indentation level in the file.
1096 
1097   \sa decrementIndent(), indent()
1098  */
1099 void
incrementIndent(const int levels)1100 SoOutput::incrementIndent(const int levels)
1101 {
1102   PRIVATE(this)->indentlevel += levels;
1103 }
1104 
1105 /*!
1106   Decrease indentation level in the file.
1107 
1108   \sa incrementIndent(), indent()
1109  */
1110 void
decrementIndent(const int levels)1111 SoOutput::decrementIndent(const int levels)
1112 {
1113   PRIVATE(this)->indentlevel -= levels;
1114 #if COIN_DEBUG
1115   if (PRIVATE(this)->indentlevel < 0) {
1116     SoDebugError::postInfo("SoOutput::decrementIndent",
1117                            "indentation level < 0!");
1118     PRIVATE(this)->indentlevel = 0;
1119   }
1120 #endif // COIN_DEBUG
1121 }
1122 
1123 /*!
1124   Call this method after writing a newline to a file to indent the next
1125   line to the correct position.
1126 
1127   \sa incrementIndent(), decrementIndent()
1128  */
1129 void
indent(void)1130 SoOutput::indent(void)
1131 {
1132 #if COIN_DEBUG
1133   if (this->isBinary()) {
1134     SoDebugError::postWarning("SoOutput::indent",
1135                               "Don't try to indent when you're doing binary "
1136                               "format output.");
1137     return;
1138   }
1139 #endif // COIN_DEBUG
1140 
1141   if (PRIVATE(this)->writecompact) return;
1142 
1143   static int oldstyle = -1;
1144   if (oldstyle == -1) {
1145     oldstyle = coin_getenv("COIN_OLDSTYLE_FORMATTING") ? 1 : 0;
1146   }
1147 
1148   // Keep the old ugly-bugly formatting style around, in case someone,
1149   // for some obscure reason, needs it.
1150   if (oldstyle) {
1151     int i = PRIVATE(this)->indentlevel;
1152     while (i > 1) {
1153       this->write('\t');
1154       i -= 2;
1155     }
1156 
1157     if (i == 1) this->write("  ");
1158   }
1159   // More sensible formatting.
1160   else {
1161     for (int i=0; i < PRIVATE(this)->indentlevel; i++) { this->write("  "); }
1162   }
1163 }
1164 
1165 /*!
1166   Reset all value and make ready for using another filepointer or buffer.
1167 */
1168 void
reset(void)1169 SoOutput::reset(void)
1170 {
1171   this->closeFile();
1172 
1173   while (PRIVATE(this)->routestack.getLength()) {
1174     delete PRIVATE(this)->routestack[0];
1175     PRIVATE(this)->routestack.removeFast(0);
1176   }
1177   PRIVATE(this)->routestack.append(NULL);
1178 
1179   PRIVATE(this)->protostack.truncate(0);
1180   while (PRIVATE(this)->defstack.getLength()) {
1181     delete PRIVATE(this)->defstack[0];
1182     PRIVATE(this)->defstack.removeFast(0);
1183   }
1184   PRIVATE(this)->defstack.append(NULL);
1185 
1186   PRIVATE(this)->disabledwriting = FALSE;
1187   this->wroteHeader = FALSE;
1188   PRIVATE(this)->indentlevel = 0;
1189 }
1190 
1191 /*!
1192   Set up the output to be more compact than with the default write routines.
1193 */
1194 void
setCompact(SbBool flag)1195 SoOutput::setCompact(SbBool flag)
1196 {
1197   // FIXME: go through output code and make the output more
1198   // compact. 19990623 morten.
1199 #if COIN_DEBUG
1200   if (!PRIVATE(this)->writecompact && flag) {
1201     SoDebugError::postWarning("SoOutput::setCompact",
1202                               "compact export is not implemented in Coin yet");
1203   }
1204 #endif // COIN_DEBUG
1205 
1206   PRIVATE(this)->writecompact = flag;
1207 }
1208 
1209 /*!
1210   Returns whether or not the write routines tries to compact the data when
1211   writing it (i.e. using less whitespace, etc).
1212 
1213   Note that "compact" in this sense does \e not mean "bitwise compression",
1214   as it could easily be mistaken for.
1215 */
1216 SbBool
isCompact(void) const1217 SoOutput::isCompact(void) const
1218 {
1219   return PRIVATE(this)->writecompact;
1220 }
1221 
1222 /*!
1223   Set up annotation of different aspects of the output data. This is not
1224   useful for much else than debugging purposes, I s'pose.
1225 */
1226 void
setAnnotation(uint32_t bits)1227 SoOutput::setAnnotation(uint32_t bits)
1228 {
1229   // FIXME: go through output code and insert annotations where applicable.
1230   // 19990623 morten.
1231 #if COIN_DEBUG
1232   if (PRIVATE(this)->annotationbits != bits) {
1233     SoDebugError::postWarning("SoOutput::setAnnotation",
1234                               "annotated export is not implemented in Coin "
1235                               "yet");
1236   }
1237 #endif // COIN_DEBUG
1238 
1239   PRIVATE(this)->annotationbits = bits;
1240 }
1241 
1242 /*!
1243   Returns the current annotation debug bitflag settings.
1244 */
1245 uint32_t
getAnnotation(void)1246 SoOutput::getAnnotation(void)
1247 {
1248   return PRIVATE(this)->annotationbits;
1249 }
1250 
1251 /*!
1252   Check that the current memory buffer has enough space to contain the
1253   given number of bytes needed for the next write operation.
1254 
1255   Returns \a FALSE if there's not enough space left, otherwise \a TRUE.
1256 
1257   Note that there will automatically be made an attempt at allocating
1258   more memory if the realloction callback function argument of
1259   setBuffer() was not \a NULL.
1260 */
1261 SbBool
makeRoomInBuf(size_t bytes)1262 SoOutput::makeRoomInBuf(size_t bytes)
1263 {
1264   assert(PRIVATE(this)->getWriter()->getType() == SoOutput_Writer::MEMBUFFER);
1265 
1266   SoOutput_MemBufferWriter * w =
1267     (SoOutput_MemBufferWriter*) PRIVATE(this)->getWriter();
1268 
1269   return w->makeRoomInBuf(bytes);
1270 }
1271 
1272 /*!
1273   \COININTERNAL
1274 
1275   Write the given number of bytes from the array, pad with zeroes to get
1276   on a 4-byte boundary if file format is binary.
1277 */
1278 void
writeBytesWithPadding(const char * const p,const size_t nr)1279 SoOutput::writeBytesWithPadding(const char * const p, const size_t nr)
1280 {
1281   this->writeBinaryArray((const unsigned char *)p, (int)nr);
1282 
1283   // Pad binary writes to a 4-byte boundary if necessary.
1284   if (this->isBinary()) {
1285     // Static buffer filled with enough bytes of all-zero bits.
1286     static unsigned char padbytes[HOSTWORDSIZE] = "X";
1287     if (padbytes[0] == 'X')
1288       for (size_t i=0; i < HOSTWORDSIZE; i++) padbytes[i] = '\0';
1289 
1290     size_t writeposition = this->bytesInBuf();
1291     if (PRIVATE(this)->getWriter()->getType() == SoOutput_Writer::MEMBUFFER) {
1292       writeposition -= ((SoOutput_MemBufferWriter*)PRIVATE(this)->getWriter())->startoffset;
1293     }
1294     size_t padsize = HOSTWORDSIZE - (writeposition % HOSTWORDSIZE);
1295     if (padsize == HOSTWORDSIZE) padsize = 0;
1296     this->writeBinaryArray(padbytes, (int)padsize);
1297   }
1298 }
1299 
1300 /*!
1301   \COININTERNAL
1302 
1303   If the file header hasn't been written yet, write it out now.
1304 */
1305 void
checkHeader(void)1306 SoOutput::checkHeader(void)
1307 {
1308   if (!this->wroteHeader) {
1309     // NB: this flag _must_ be set before we do any writing, or we'll
1310     // end up in an eternal double-recursive loop.
1311     this->wroteHeader = TRUE;
1312 
1313     SbString h;
1314     if (PRIVATE(this)->headerstring) h = *(PRIVATE(this)->headerstring);
1315     else if (this->isBinary()) h = SoOutput::getDefaultBinaryHeader();
1316     else h = SoOutput::getDefaultASCIIHeader();
1317 
1318     if (this->isBinary()) h = this->padHeader(h);
1319     h += EOLSTR;
1320     if (!this->isBinary()) h += EOLSTR;
1321     // Note: SoField::get() and SoFieldContainer::get() depends on the
1322     // fact that the header identification line ends in "\n\n".
1323 
1324     // Write as char * to avoid the addition of any "s.
1325     const unsigned char * str = (const unsigned char *)h.getString();
1326     const size_t len = strlen(h.getString());
1327     this->writeBinaryArray(str, (int)len);
1328   }
1329 }
1330 
1331 /*!
1332   Returns \a TRUE of we're set up to write to a memory buffer.
1333 */
1334 SbBool
isToBuffer(void) const1335 SoOutput::isToBuffer(void) const
1336 {
1337   return PRIVATE(this)->getWriter()->getType() == SoOutput_Writer::MEMBUFFER;
1338 }
1339 
1340 /*!
1341   Returns current write position.
1342 
1343   Note that for memory buffer writing, this includes the offset from
1344   SoOutput::setBuffer(), if any.
1345 */
1346 size_t
bytesInBuf(void) const1347 SoOutput::bytesInBuf(void) const
1348 {
1349   return PRIVATE(this)->getWriter()->bytesInBuf();
1350 
1351 //   if (this->isToBuffer()) { return PRIVATE(this)->bufferoffset; }
1352 //   else { return ftell(PRIVATE(this)->filep); }
1353 }
1354 
1355 /*!
1356   Makes a unique id for \a base and adds a mapping into our dictionary.
1357 */
1358 int
addReference(const SoBase * base)1359 SoOutput::addReference(const SoBase * base)
1360 {
1361   return PRIVATE(this)->counter->addReference(base);
1362 }
1363 
1364 /*!
1365   Returns the unique identifier for \a base or -1 if not found.
1366 */
1367 int
findReference(const SoBase * base) const1368 SoOutput::findReference(const SoBase * base) const
1369 {
1370   return PRIVATE(this)->counter->findReference(base);
1371 }
1372 
1373 /*!
1374   Sets the reference for \a base manually.
1375 */
1376 void
setReference(const SoBase * base,int refid)1377 SoOutput::setReference(const SoBase * base, int refid)
1378 {
1379   PRIVATE(this)->counter->setReference(base, refid);
1380 }
1381 
1382 /*!
1383   Adds \a name to the set of currently DEF'ed node names so far in the output
1384   process.
1385 */
1386 void
addDEFNode(SbName name)1387 SoOutput::addDEFNode(SbName name)
1388 {
1389   void * value = NULL;
1390   BogusSet * defnames = PRIVATE(this)->getCurrentDefNames(TRUE);
1391   defnames->put(name.getString(), value);
1392 }
1393 
1394 /*!
1395   Checks whether \a name is already DEF'ed at this point in the output process.
1396   Returns TRUE if \a name is DEF'ed.
1397 */
1398 SbBool
lookupDEFNode(SbName name)1399 SoOutput::lookupDEFNode(SbName name)
1400 {
1401   void * value;
1402   BogusSet * defnames = PRIVATE(this)->getCurrentDefNames(TRUE);
1403   return defnames->get(name.getString(), value);
1404 }
1405 
1406 /*!
1407   Removes \a name from the set of DEF'ed node names. Used after the last
1408   reference to a DEF'ed node if we want to reuse the DEF at a later point
1409   in the file.
1410 */
1411 void
removeDEFNode(SbName name)1412 SoOutput::removeDEFNode(SbName name)
1413 {
1414   BogusSet * defnames = PRIVATE(this)->getCurrentDefNames(FALSE);
1415   assert(defnames);
1416 #if COIN_DEBUG
1417   SbBool ret = defnames->erase(name.getString());
1418   assert(ret && "Tried to remove nonexisting DEFnode");
1419 #else
1420   (void)defnames->erase(name.getString());
1421 #endif
1422 }
1423 
1424 /*!
1425   \COININTERNAL
1426 
1427   \COIN_FUNCTION_EXTENSION
1428 
1429   \since Coin 2.0
1430 */
1431 void
pushProto(SoProto * proto)1432 SoOutput::pushProto(SoProto * proto)
1433 {
1434   // FIXME: try to find a better/nicer way to handle PROTO export without
1435   // adding new methods in SoOutput. For instance, is it possible to
1436   // add elements in the SoWriteAction state stack? pederb, 2002-06-12
1437 
1438   PRIVATE(this)->pushRoutes(FALSE);
1439   PRIVATE(this)->protostack.push(proto);
1440   PRIVATE(this)->pushDefNames(FALSE);
1441 }
1442 
1443 /*!
1444   \COININTERNAL
1445 
1446   \COIN_FUNCTION_EXTENSION
1447 
1448   \since Coin 2.0
1449 */
1450 SoProto *
getCurrentProto(void) const1451 SoOutput::getCurrentProto(void) const
1452 {
1453   // FIXME: try to find a better/nicer way to handle PROTO export without
1454   // adding new methods in SoOutput. For instance, is it possible to
1455   // add elements in the SoWriteAction state stack? pederb, 2002-06-12
1456 
1457   if (PRIVATE(this)->protostack.getLength()) {
1458     return PRIVATE(this)->protostack[PRIVATE(this)->protostack.getLength()-1];
1459   }
1460   return NULL;
1461 }
1462 
1463 /*!
1464   \COININTERNAL
1465 
1466   \COIN_FUNCTION_EXTENSION
1467 
1468   \since Coin 2.0
1469 */
1470 void
popProto(void)1471 SoOutput::popProto(void)
1472 {
1473   // FIXME: try to find a better/nicer way to handle PROTO export without
1474   // adding new methods in SoOutput. For instance, is it possible to
1475   // add elements in the SoWriteAction state stack? pederb, 2002-06-12
1476 
1477   assert(PRIVATE(this)->protostack.getLength());
1478   PRIVATE(this)->protostack.pop();
1479   PRIVATE(this)->popDefNames();
1480   PRIVATE(this)->popRoutes();
1481 }
1482 
1483 /*!
1484   \COININTERNAL
1485 
1486   \COIN_FUNCTION_EXTENSION
1487 
1488   \since Coin 2.0
1489 */
1490 
1491 void
addRoute(SoFieldContainer * from,const SbName & fromfield,SoFieldContainer * to,const SbName & tofield)1492 SoOutput::addRoute(SoFieldContainer * from, const SbName & fromfield,
1493                    SoFieldContainer * to, const SbName & tofield)
1494 {
1495   SoOutputROUTEList * list = PRIVATE(this)->getCurrentRoutes(TRUE);
1496   assert(list);
1497   SoOutputROUTE r;
1498   r.from = from;
1499   r.fromfield = fromfield;
1500   r.to = to;
1501   r.tofield = tofield;
1502   list->append(r);
1503 }
1504 
1505 /*!
1506   \COININTERNAL
1507 
1508   \COIN_FUNCTION_EXTENSION
1509 
1510   \since Coin 2.0
1511 */
1512 void
resolveRoutes(void)1513 SoOutput::resolveRoutes(void)
1514 {
1515   // FIXME: try to find a better/nicer way to handle ROUTE export without
1516   // adding new methods in SoOutput. For instance, is it possible to
1517   // add elements in the SoWriteAction state stack? pederb, 2002-06-12
1518 
1519   SoOutputROUTEList * list = PRIVATE(this)->getCurrentRoutes(FALSE);
1520   if (list && list->getLength()) {
1521     const int n = list->getLength();
1522     for (int i = 0; i < n; i++) {
1523       SoOutputROUTE r = (*list)[i];
1524 
1525       SoFieldContainer * fromc = r.from;
1526       SoFieldContainer * toc = r.to;
1527 
1528       SbName fromname = r.fromfield;
1529       SbName toname = r.tofield;
1530 
1531       this->indent();
1532       this->write("ROUTE ");
1533       this->write(PRIVATE(this)->counter->getWriteName(fromc).getString());
1534       this->write('.');
1535       this->write(fromname.getString());
1536       this->write(" TO ");
1537       this->write(PRIVATE(this)->counter->getWriteName(toc).getString());
1538       this->write('.');
1539       this->write(toname.getString());
1540       this->write("\n");
1541 
1542 #if COIN_DEBUG
1543       if (SoWriterefCounter::debugWriterefs()) {
1544         int writerefcount = PRIVATE(this)->counter->getWriteref(fromc);
1545         SoDebugError::postInfo("SoOutput::resolveRoutes",
1546                                "%p/%s/'%s': %d -> %d",
1547                                fromc,
1548                                fromc->getTypeId().getName().getString(),
1549                                fromc->getName().getString(),
1550                                writerefcount, writerefcount - 1);
1551       }
1552 #endif // COIN_DEBUG
1553 
1554 #if COIN_DEBUG
1555       if (SoWriterefCounter::debugWriterefs()) {
1556         int writerefcount = PRIVATE(this)->counter->getWriteref(toc);
1557         SoDebugError::postInfo("SoOutput::resolveRoutes",
1558                                "%p/%s/'%s': %d -> %d",
1559                                toc,
1560                                toc->getTypeId().getName().getString(),
1561                                toc->getName().getString(),
1562                                writerefcount, writerefcount - 1);
1563       }
1564 #endif // COIN_DEBUG
1565 
1566       // remove write references again
1567       PRIVATE(this)->counter->decrementWriteref(fromc);
1568       PRIVATE(this)->counter->decrementWriteref(toc);
1569 
1570     }
1571     list->truncate(0);
1572   }
1573 }
1574 
1575 /*!
1576   Convert the short integer in \a s to most-significant-byte first format
1577   and put the resulting bytes sequentially at \a to.
1578 
1579   \sa SoInput::convertShort()
1580 */
1581 void
convertShort(short s,char * to)1582 SoOutput::convertShort(short s, char * to)
1583 {
1584   assert(sizeof(s) == sizeof(uint16_t));
1585   *((uint16_t *)to) = coin_hton_uint16((uint16_t)s);
1586 }
1587 
1588 /*!
1589   Convert the 32-bit integer in \a l to most-significant-byte first format
1590   and put the resulting bytes sequentially at \a to.
1591 
1592   \sa SoInput::convertInt32()
1593 */
1594 void
convertInt32(int32_t l,char * to)1595 SoOutput::convertInt32(int32_t l, char * to)
1596 {
1597   assert(sizeof(l) == sizeof(uint32_t));
1598   *((uint32_t *)to) = coin_hton_uint32(l);
1599 }
1600 
1601 /*!
1602   Convert the single-precision floating point number in \a f to
1603   most-significant-byte first format and put the resulting bytes
1604   sequentially at \a to.
1605 
1606   \sa SoInput::convertFloat()
1607 */
1608 void
convertFloat(float f,char * to)1609 SoOutput::convertFloat(float f, char * to)
1610 {
1611   coin_hton_float_bytes(f, to);
1612 }
1613 
1614 /*!
1615   Convert the double-precision floating point number in \a d to
1616   most-significant-byte first format and put the resulting bytes
1617   sequentially at \a to.
1618 
1619   \sa SoInput::convertDouble()
1620 */
1621 void
convertDouble(double d,char * to)1622 SoOutput::convertDouble(double d, char * to)
1623 {
1624   coin_hton_double_bytes(d, to);
1625 }
1626 
1627 /*!
1628   Convert \a len short integer values from the array at \a from into
1629   the array at \a to from native host format to network independent
1630   format (i.e. most significant byte first).
1631 */
1632 void
convertShortArray(short * from,char * to,int len)1633 SoOutput::convertShortArray(short * from, char * to, int len)
1634 {
1635   for (int i=0; i < len; i++) {
1636     this->convertShort(*from++, to);
1637     to += sizeof(short);
1638   }
1639 }
1640 
1641 /*!
1642   Convert \a len 32-bit integer values from the array at \a from into
1643   the array at \a to from native host format to network independent
1644   format (i.e. most significant byte first).
1645 */
1646 void
convertInt32Array(int32_t * from,char * to,int len)1647 SoOutput::convertInt32Array(int32_t * from, char * to, int len)
1648 {
1649   for (int i=0; i < len; i++) {
1650     this->convertInt32(*from++, to);
1651     to += sizeof(int32_t);
1652   }
1653 }
1654 
1655 /*!
1656   Convert \a len single-precision floating point values from the array at
1657   \a from into the array at \a to from native host format to network
1658   independent format (i.e. most significant byte first).
1659 */
1660 void
convertFloatArray(float * from,char * to,int len)1661 SoOutput::convertFloatArray(float * from, char * to, int len)
1662 {
1663   for (int i=0; i < len; i++) {
1664     this->convertFloat(*from++, to);
1665     to += sizeof(float);
1666   }
1667 }
1668 
1669 /*!
1670   Convert \a len double-precision floating point values from the array at
1671   \a from into the array at \a to from native host format to network
1672   independent format (i.e. most significant byte first).
1673 */
1674 void
convertDoubleArray(double * from,char * to,int len)1675 SoOutput::convertDoubleArray(double * from, char * to, int len)
1676 {
1677   for (int i=0; i < len; i++) {
1678     this->convertDouble(*from++, to);
1679     to += sizeof(double);
1680   }
1681 }
1682 
1683 /*!
1684   Pads the header we're writing so it contains the correct amount of bytes
1685   for the alignment of the following binary writes.
1686 */
1687 SbString
padHeader(const SbString & inString)1688 SoOutput::padHeader(const SbString & inString)
1689 {
1690   SbString h = inString;
1691   const size_t EOLLEN = strlen(EOLSTR);
1692   int hlen = h.getLength();
1693   size_t pad = HOSTWORDSIZE - ((hlen + EOLLEN) % HOSTWORDSIZE);
1694   pad = pad == HOSTWORDSIZE ? 0 : pad;
1695   for (size_t i=0; i < pad; i++) h += ' ';
1696 
1697   return h;
1698 }
1699 
1700 //
1701 // Used only by SoBase::writeHeader().
1702 //
1703 void
removeSoBase2IdRef(const SoBase * base)1704 SoOutput::removeSoBase2IdRef(const SoBase * base)
1705 {
1706   PRIVATE(this)->counter->removeSoBase2IdRef(base);
1707 }
1708 
1709 // FIXME: temporary workaround needed to test if we are currently
1710 // exporting a VRML97 or an Inventor file. Used from
1711 // SoBase::writeHeader(). pederb, 2003-02-18
1712 SbString
SoOutput_getHeaderString(const SoOutputP * pout)1713 SoOutput_getHeaderString(const SoOutputP * pout)
1714 {
1715   if (pout->headerstring) return *(pout->headerstring);
1716   else return SoOutput::getDefaultASCIIHeader();
1717 }
1718 
1719 #undef PRIVATE
1720