libdap Updated for version 3.20.10
libdap4 is an implementation of OPeNDAP's DAP protocol.
chunked_ostream.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2009 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24//
25// Portions of this code were taken verbatim from Josuttis,
26// "The C++ Standard Library," p.672
27
28#include "config.h"
29
30#include <arpa/inet.h>
31
32#include <stdint.h>
33
34#include <string>
35#include <streambuf>
36
37#include <cstring>
38
39//#define DODS_DEBUG
40
41#include "chunked_stream.h"
42#include "chunked_ostream.h"
43#include "debug.h"
44
45namespace libdap {
46
47// flush the characters in the buffer
53std::streambuf::int_type
55{
56 DBG(cerr << "In chunked_outbuf::data_chunk" << endl);
57
58 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
59
60 // Since this is called by sync() (e.g., flush()), return 0 and do nothing
61 // when there's no data to send.
62 if (num == 0)
63 return 0;
64
65 // here, write out the chunk headers: CHUNKTYPE and CHUNKSIZE
66 // as a 32-bit unsigned int. Here I assume that num is never
67 // more than 2^24 because that was tested in the constructor
68
69 // Trick: This method always writes CHUNK_DATA type chunks so
70 // the chunk type is always 0x00, and given that num never has
71 // anything bigger than 24-bits, the high order byte is always
72 // 0x00. Of course bit-wise OR with 0x00 isn't going to do
73 // much anyway... Here's the general idea all the same:
74 //
75 // unsigned int chunk_header = (unsigned int)num | CHUNK_type;
76 uint32_t header = num;
77
78 // Add encoding of host's byte order. jhrg 11/24/13
79 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
80
81 // network byte order for the header
82 header = htonl(header);
83
84 d_os.write((const char *)&header, sizeof(int32_t));
85
86 // Should bad() throw an error?
87 // Are these functions fast or would the bits be faster?
88 d_os.write(d_buffer, num);
89 if (d_os.eof() || d_os.bad())
90 return traits_type::eof();
91
92 pbump(-num);
93 return num;
94}
95
107std::streambuf::int_type
109{
110 DBG(cerr << "In chunked_outbuf::end_chunk" << endl);
111
112 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
113
114 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE
115 // as a 32-bit unsigned int. Here I assume that num is never
116 // more than 2^24 because that was tested in the constructor
117
118 uint32_t header = (uint32_t)num | CHUNK_END;
119
120
121 // Add encoding of host's byte order. jhrg 11/24/13
122 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
123
124 // network byte order for the header
125 header = htonl(header);
126
127 // Write out the CHUNK_END header with the byte count.
128 // This should be called infrequently, so it's probably not worth
129 // optimizing away chunk_header
130 d_os.write((const char *)&header, sizeof(uint32_t));
131
132 // Should bad() throw an error?
133 // Are these functions fast or would the bits be faster?
134 d_os.write(d_buffer, num);
135 if (d_os.eof() || d_os.bad())
136 return traits_type::eof();
137
138 pbump(-num);
139 return num;
140}
141
149std::streambuf::int_type
150chunked_outbuf::err_chunk(const std::string &m)
151{
152 DBG(cerr << "In chunked_outbuf::err_chunk" << endl);
153 std::string msg = m;
154
155 // Figure out how many chars are in the buffer - these will be
156 // ignored.
157 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
158
159 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE
160 // as a 32-bit unsigned int. Here I assume that num is never
161 // more than 2^24 because that was tested in the constructor
162 if (msg.length() > 0x00FFFFFF)
163 msg = "Error message too long";
164
165 uint32_t header = (uint32_t)msg.length() | CHUNK_ERR;
166
167 // Add encoding of host's byte order. jhrg 11/24/13
168 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
169
170 // network byte order for the header
171 header = htonl(header);
172
173
174 // Write out the CHUNK_END header with the byte count.
175 // This should be called infrequently, so it's probably not worth
176 // optimizing away chunk_header
177 d_os.write((const char *)&header, sizeof(uint32_t));
178
179 // Should bad() throw an error?
180 // Are these functions fast or would the bits be faster?
181 d_os.write(msg.data(), msg.length());
182 if (d_os.eof() || d_os.bad())
183 return traits_type::eof();
184
185 // Reset the buffer pointer, effectively ignoring what's in there now
186 pbump(-num);
187
188 // return the number of characters ignored
189 return num;
190}
191
204std::streambuf::int_type
206{
207 DBG(cerr << "In chunked_outbuf::overflow" << endl);
208
209 // Note that the buffer and eptr() were set so that when pptr() is
210 // at the end of the buffer, there is actually one more character
211 // available in the buffer.
212 if (!traits_type::eq_int_type(c, traits_type::eof())) {
213 *pptr() = traits_type::not_eof(c);
214 pbump(1);
215 }
216 // flush the buffer
217 if (data_chunk() == traits_type::eof()) {
218 //Error
219 return traits_type::eof();
220 }
221
222 return traits_type::not_eof(c);
223}
224
225/*
226
227 d_buffer
228 |
229 v
230 |--------------------------------------------|....
231 | | .
232 |--------------------------------------------|....
233 ^ ^ ^
234 | | |
235 pbase() pptr() epptr()
236
237 */
238
246std::streamsize
247chunked_outbuf::xsputn(const char *s, std::streamsize num)
248{
249 DBG(cerr << "In chunked_outbuf::xsputn: num: " << num << endl);
250
251 // if the current block of data will fit in the buffer, put it there.
252 // else, there is at least a complete chunk between what's in the buffer
253 // and what's in 's'; send a chunk header, the stuff in the buffer and
254 // bytes from 's' to make a complete chunk. Then iterate over 's' sending
255 // more chunks until there's less than a complete chunk left in 's'. Put
256 // the bytes remaining 's' in the buffer. Return the number of bytes sent
257 // or 0 if an error is encountered.
258
259 int32_t bytes_in_buffer = pptr() - pbase(); // num needs to be signed for the call to pbump
260
261 // Will num bytes fit in the buffer? The location of epptr() is one back from
262 // the actual end of the buffer, so the next char written will trigger a write
263 // of the buffer as a new data chunk.
264 if (bytes_in_buffer + num < d_buf_size) {
265 DBG2(cerr << ":xsputn: buffering num: " << num << endl);
266 memcpy(pptr(), s, num);
267 pbump(num);
268 return traits_type::not_eof(num);
269 }
270
271 // If here, write a chunk header and a chunk's worth of data by combining the
272 // data in the buffer and some data from 's'.
273 uint32_t header = d_buf_size;
274
275 // Add encoding of host's byte order. jhrg 11/24/13
276 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
277
278 // network byte order for the header
279 header = htonl(header);
280
281 d_os.write((const char *)&header, sizeof(int32_t)); // Data chunk's CHUNK_TYPE is 0x00000000
282
283 // Reset the pptr() and epptr() now in case of an error exit. See the 'if'
284 // at the end of this for the only code from here down that will modify the
285 // pptr() value.
286 setp(d_buffer, d_buffer + (d_buf_size - 1));
287
288 d_os.write(d_buffer, bytes_in_buffer);
289 if (d_os.eof() || d_os.bad())
290 return traits_type::not_eof(0);
291
292 int bytes_to_fill_out_buffer = d_buf_size - bytes_in_buffer;
293 d_os.write(s, bytes_to_fill_out_buffer);
294 if (d_os.eof() || d_os.bad())
295 return traits_type::not_eof(0);
296 s += bytes_to_fill_out_buffer;
297 uint32_t bytes_still_to_send = num - bytes_to_fill_out_buffer;
298
299 // Now send all the remaining data in s until the amount remaining doesn't
300 // fill a complete chunk and buffer those data.
301 while (bytes_still_to_send >= d_buf_size) {
302 // This is header for a chunk of d_buf_size bytes; the size was set above
303 d_os.write((const char *) &header, sizeof(int32_t));
304 d_os.write(s, d_buf_size);
305 if (d_os.eof() || d_os.bad()) return traits_type::not_eof(0);
306 s += d_buf_size;
307 bytes_still_to_send -= d_buf_size;
308 }
309
310 if (bytes_still_to_send > 0) {
311 // if the code is here, one or more chunks have been sent, the
312 // buffer is empty and there are < d_buf_size bytes to send. Buffer
313 // them.
314 memcpy(d_buffer, s, bytes_still_to_send);
315 pbump(bytes_still_to_send);
316 }
317
318 // Unless an error was detected while writing to the stream, the code must
319 // have sent num bytes.
320 return traits_type::not_eof(num);
321}
322
328std::streambuf::int_type
330{
331 DBG(cerr << "In chunked_outbuf::sync" << endl);
332
333 if (data_chunk() == traits_type::eof()) {
334 // Error
335 return traits_type::not_eof(-1);
336 }
337 return traits_type::not_eof(0);
338}
339
340} // namespace libdap
virtual std::streamsize xsputn(const char *s, std::streamsize num)
Write bytes to the chunked stream Write the bytes in s to the chunked stream.
int_type end_chunk()
Send an end chunk.
int_type data_chunk()
Write out the contents of the buffer as a chunk.
int_type err_chunk(const std::string &msg)
Send an error chunk While building up the next chunk, send an error chunk, ignoring the data currentl...
virtual int_type sync()
Synchronize the stream with its data sink.
virtual int_type overflow(int c)
Virtual method called when the internal buffer would overflow. When the internal buffer fills,...
top level DAP object to house generic methods