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