libdap Updated for version 3.20.5
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#if !BYTE_ORDER_PREFIX
78 // Add encoding of host's byte order. jhrg 11/24/13
79 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
80#if HEADER_IN_NETWORK_BYTE_ORDER
81 // network byte order for the header
82 header = htonl(header);
83#endif
84#endif
85
86 d_os.write((const char *)&header, sizeof(int32_t));
87
88 // Should bad() throw an error?
89 // Are these functions fast or would the bits be faster?
90 d_os.write(d_buffer, num);
91 if (d_os.eof() || d_os.bad())
92 return traits_type::eof();
93
94 pbump(-num);
95 return num;
96}
97
109std::streambuf::int_type
111{
112 DBG(cerr << "In chunked_outbuf::end_chunk" << endl);
113
114 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
115
116 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE
117 // as a 32-bit unsigned int. Here I assume that num is never
118 // more than 2^24 because that was tested in the constructor
119
120 uint32_t header = (uint32_t)num | CHUNK_END;
121
122#if !BYTE_ORDER_PREFIX
123 // Add encoding of host's byte order. jhrg 11/24/13
124 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
125#if HEADER_IN_NETWORK_BYTE_ORDER
126 // network byte order for the header
127 htonl(header);
128#endif
129#endif
130
131 // Write out the CHUNK_END header with the byte count.
132 // This should be called infrequently, so it's probably not worth
133 // optimizing away chunk_header
134 d_os.write((const char *)&header, sizeof(uint32_t));
135
136 // Should bad() throw an error?
137 // Are these functions fast or would the bits be faster?
138 d_os.write(d_buffer, num);
139 if (d_os.eof() || d_os.bad())
140 return traits_type::eof();
141
142 pbump(-num);
143 return num;
144}
145
153std::streambuf::int_type
154chunked_outbuf::err_chunk(const std::string &m)
155{
156 DBG(cerr << "In chunked_outbuf::err_chunk" << endl);
157 std::string msg = m;
158
159 // Figure out how many chars are in the buffer - these will be
160 // ignored.
161 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
162
163 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE
164 // as a 32-bit unsigned int. Here I assume that num is never
165 // more than 2^24 because that was tested in the constructor
166 if (msg.length() > 0x00FFFFFF)
167 msg = "Error message too long";
168
169 uint32_t header = (uint32_t)msg.length() | CHUNK_ERR;
170
171#if !BYTE_ORDER_PREFIX
172 // Add encoding of host's byte order. jhrg 11/24/13
173 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
174#if HEADER_IN_NETWORK_BYTE_ORDER
175 // network byte order for the header
176 header = htonl(header);
177#endif
178#endif
179
180 // Write out the CHUNK_END header with the byte count.
181 // This should be called infrequently, so it's probably not worth
182 // optimizing away chunk_header
183 d_os.write((const char *)&header, sizeof(uint32_t));
184
185 // Should bad() throw an error?
186 // Are these functions fast or would the bits be faster?
187 d_os.write(msg.data(), msg.length());
188 if (d_os.eof() || d_os.bad())
189 return traits_type::eof();
190
191 // Reset the buffer pointer, effectively ignoring what's in there now
192 pbump(-num);
193
194 // return the number of characters ignored
195 return num;
196}
197
210std::streambuf::int_type
212{
213 DBG(cerr << "In chunked_outbuf::overflow" << endl);
214
215 // Note that the buffer and eptr() were set so that when pptr() is
216 // at the end of the buffer, there is actually one more character
217 // available in the buffer.
218 if (!traits_type::eq_int_type(c, traits_type::eof())) {
219 *pptr() = traits_type::not_eof(c);
220 pbump(1);
221 }
222 // flush the buffer
223 if (data_chunk() == traits_type::eof()) {
224 //Error
225 return traits_type::eof();
226 }
227
228 return traits_type::not_eof(c);
229}
230
231/*
232
233 d_buffer
234 |
235 v
236 |--------------------------------------------|....
237 | | .
238 |--------------------------------------------|....
239 ^ ^ ^
240 | | |
241 pbase() pptr() epptr()
242
243 */
244
252std::streamsize
253chunked_outbuf::xsputn(const char *s, std::streamsize num)
254{
255 DBG(cerr << "In chunked_outbuf::xsputn: num: " << num << endl);
256
257 // if the current block of data will fit in the buffer, put it there.
258 // else, there is at least a complete chunk between what's in the buffer
259 // and what's in 's'; send a chunk header, the stuff in the buffer and
260 // bytes from 's' to make a complete chunk. Then iterate over 's' sending
261 // more chunks until there's less than a complete chunk left in 's'. Put
262 // the bytes remaining 's' in the buffer. Return the number of bytes sent
263 // or 0 if an error is encountered.
264
265 int32_t bytes_in_buffer = pptr() - pbase(); // num needs to be signed for the call to pbump
266
267 // Will num bytes fit in the buffer? The location of epptr() is one back from
268 // the actual end of the buffer, so the next char written will trigger a write
269 // of the buffer as a new data chunk.
270 if (bytes_in_buffer + num < d_buf_size) {
271 DBG2(cerr << ":xsputn: buffering num: " << num << endl);
272 memcpy(pptr(), s, num);
273 pbump(num);
274 return traits_type::not_eof(num);
275 }
276
277 // If here, write a chunk header and a chunk's worth of data by combining the
278 // data in the buffer and some data from 's'.
279 uint32_t header = d_buf_size;
280#if !BYTE_ORDER_PREFIX
281 // Add encoding of host's byte order. jhrg 11/24/13
282 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN;
283#if HEADER_IN_NETWORK_BYTE_ORDER
284 // network byte order for the header
285 header = htonl(header);
286#endif
287#endif
288 d_os.write((const char *)&header, sizeof(int32_t)); // Data chunk's CHUNK_TYPE is 0x00000000
289
290 // Reset the pptr() and epptr() now in case of an error exit. See the 'if'
291 // at teh end of this for the only code from here down that will modify the
292 // pptr() value.
293 setp(d_buffer, d_buffer + (d_buf_size - 1));
294
295 d_os.write(d_buffer, bytes_in_buffer);
296 if (d_os.eof() || d_os.bad())
297 return traits_type::not_eof(0);
298
299 int bytes_to_fill_out_buffer = d_buf_size - bytes_in_buffer;
300 d_os.write(s, bytes_to_fill_out_buffer);
301 if (d_os.eof() || d_os.bad())
302 return traits_type::not_eof(0);
303 s += bytes_to_fill_out_buffer;
304 uint32_t bytes_still_to_send = num - bytes_to_fill_out_buffer;
305
306 // Now send all the remaining data in s until the amount remaining doesn't
307 // fill a complete chunk and buffer those data.
308 while (bytes_still_to_send >= d_buf_size) {
309 // This is header for a chunk of d_buf_size bytes; the size was set above
310 d_os.write((const char *) &header, sizeof(int32_t));
311 d_os.write(s, d_buf_size);
312 if (d_os.eof() || d_os.bad()) return traits_type::not_eof(0);
313 s += d_buf_size;
314 bytes_still_to_send -= d_buf_size;
315 }
316
317 if (bytes_still_to_send > 0) {
318 // if the code is here, one or more chunks have been sent, the
319 // buffer is empty and there are < d_buf_size bytes to send. Buffer
320 // them.
321 memcpy(d_buffer, s, bytes_still_to_send);
322 pbump(bytes_still_to_send);
323 }
324
325 // Unless an error was detected while writing to the stream, the code must
326 // have sent num bytes.
327 return traits_type::not_eof(num);
328}
329
335std::streambuf::int_type
337{
338 DBG(cerr << "In chunked_outbuf::sync" << endl);
339
340 if (data_chunk() == traits_type::eof()) {
341 // Error
342 return traits_type::not_eof(-1);
343 }
344 return traits_type::not_eof(0);
345}
346
347} // 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