Panda3D
multiplexStreamBuf.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file multiplexStreamBuf.cxx
10  * @author drose
11  * @date 2000-11-27
12  */
13 
14 #include "multiplexStreamBuf.h"
15 
16 #if defined(WIN32_VC) || defined(WIN64_VC)
17 #define WINDOWS_LEAN_AND_MEAN
18 #include <windows.h>
19 #undef WINDOWS_LEAN_AND_MEAN
20 #endif
21 
22 // We use real assert() instead of nassert(), because we're likely to be
23 // invoked directly by pnotify.here, and we don't want to risk infinite
24 // recursion.
25 #include <assert.h>
26 
27 using std::string;
28 
29 /**
30  * Closes or deletes the relevant pointers, if _owns_obj is true.
31  */
32 void MultiplexStreamBuf::Output::
33 close() {
34  if (_owns_obj) {
35  switch (_output_type) {
36  case OT_ostream:
37  assert(_out != nullptr);
38  delete _out;
39  break;
40 
41  case OT_stdio:
42  assert(_fout != nullptr);
43  fclose(_fout);
44  break;
45 
46  default:
47  break;
48  }
49  }
50 }
51 
52 /**
53  * Dumps the indicated string to the appropriate place.
54  */
55 void MultiplexStreamBuf::Output::
56 write_string(const string &str) {
57  switch (_output_type) {
58  case OT_ostream:
59  assert(_out != nullptr);
60  _out->write(str.data(), str.length());
61  _out->flush();
62  break;
63 
64  case OT_stdio:
65  assert(_fout != nullptr);
66  fwrite(str.data(), str.length(), 1, _fout);
67  fflush(_fout);
68  break;
69 
70  case OT_system_debug:
71 #if defined(WIN32_VC) || defined(WIN64_VC)
72  OutputDebugString(str.c_str());
73 #endif
74  break;
75  }
76 }
77 
78 /**
79  *
80  */
81 MultiplexStreamBuf::
82 MultiplexStreamBuf() {
83 #ifndef PHAVE_IOSTREAM
84  // Older iostream implementations required this.
85  allocate();
86  setp(base(), ebuf());
87 #endif
88 }
89 
90 /**
91  *
92  */
93 MultiplexStreamBuf::
94 ~MultiplexStreamBuf() {
95  sync();
96 
97  // Make sure all of our owned pointers are freed.
98  Outputs::iterator oi;
99  for (oi = _outputs.begin(); oi != _outputs.end(); ++oi) {
100  Output &out = (*oi);
101  out.close();
102  }
103 }
104 
105 /**
106  * Adds the indicated output destinition to the set of things that will be
107  * written to when characters are output to the MultiplexStream.
108  */
110 add_output(MultiplexStreamBuf::BufferType buffer_type,
111  MultiplexStreamBuf::OutputType output_type,
112  std::ostream *out, FILE *fout, bool owns_obj) {
113 
114  Output o;
115  o._buffer_type = buffer_type;
116  o._output_type = output_type;
117  o._out = out;
118  o._fout = fout;
119  o._owns_obj = owns_obj;
120 
121  // Ensure that we have the mutex while we fiddle with the list of outputs.
122  _lock.lock();
123  _outputs.push_back(o);
124  _lock.unlock();
125 }
126 
127 
128 /**
129  * Forces out all output that hasn't yet been written.
130  */
132 flush() {
133  _lock.lock();
134  write_chars("", 0, true);
135  _lock.unlock();
136 }
137 
138 /**
139  * Called by the system ostream implementation when its internal buffer is
140  * filled, plus one character.
141  */
142 int MultiplexStreamBuf::
143 overflow(int ch) {
144  _lock.lock();
145 
146  std::streamsize n = pptr() - pbase();
147 
148  if (n != 0) {
149  write_chars(pbase(), n, false);
150  pbump(-n); // Reset pptr().
151  }
152 
153  if (ch != EOF) {
154  // Write one more character.
155  char c = ch;
156  write_chars(&c, 1, false);
157  }
158 
159  _lock.unlock();
160  return 0;
161 }
162 
163 /**
164  * Called by the system ostream implementation when the buffer should be
165  * flushed to output (for instance, on destruction).
166  */
167 int MultiplexStreamBuf::
168 sync() {
169  _lock.lock();
170 
171  std::streamsize n = pptr() - pbase();
172 
173  // We pass in false for the flush value, even though our transmitting
174  // ostream said to sync. This allows us to get better line buffering, since
175  // our transmitting ostream is often set unitbuf, and might call sync
176  // multiple times in one line. We still have an explicit flush() call to
177  // force the issue.
178  write_chars(pbase(), n, false);
179  pbump(-n);
180 
181  _lock.unlock();
182  return 0; // Return 0 for success, EOF to indicate write full.
183 }
184 
185 /**
186  * An internal function called by sync() and overflow() to store one or more
187  * characters written to the stream into the memory buffer.
188  *
189  * It is assumed that there is only one thread at a time running this code; it
190  * is the responsibility of the caller to grab the _lock mutex before calling
191  * this.
192  */
193 void MultiplexStreamBuf::
194 write_chars(const char *start, int length, bool flush) {
195  size_t orig = _line_buffer.length();
196  string latest;
197  if (length != 0) {
198  latest = string(start, length);
199  }
200  string line;
201 
202  if (flush) {
203  // If we're to flush the stream now, we dump the whole thing regardless of
204  // whether we have reached end-of-line.
205  line = _line_buffer + latest;
206  _line_buffer = "";
207 
208  } else {
209  // Otherwise, we check for the end-of-line character, for our ostreams
210  // that only want a complete line at a time.
211  _line_buffer += latest;
212  size_t eol = _line_buffer.rfind('\n', orig);
213  if (eol != string::npos) {
214  line = _line_buffer.substr(0, eol + 1);
215  _line_buffer = _line_buffer.substr(eol + 1);
216  }
217  }
218 
219  Outputs::iterator oi;
220  for (oi = _outputs.begin(); oi != _outputs.end(); ++oi) {
221  Output &out = (*oi);
222  switch (out._buffer_type) {
223  case BT_none:
224  // No buffering: send all new characters directly to the ostream.
225  if (!latest.empty()) {
226  out.write_string(latest);
227  }
228  break;
229 
230  case BT_line:
231  // Line buffering: send only when a complete line has been received.
232  if (!line.empty()) {
233  out.write_string(line);
234  }
235  break;
236  }
237  }
238 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void flush()
Forces out all output that hasn't yet been written.
void add_output(BufferType buffer_type, OutputType output_type, std::ostream *out=nullptr, FILE *fout=nullptr, bool owns_obj=false)
Adds the indicated output destinition to the set of things that will be written to when characters ar...