libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
chunked_ostream.cc
Go to the documentation of this file.
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 <streambuf>
35#include <string>
36
37#include <cstring>
38
39// #define DODS_DEBUG
40
41#include "chunked_ostream.h"
42#include "chunked_stream.h"
43#include "debug.h"
44
45namespace libdap {
46
47// flush the characters in the buffer
53std::streambuf::int_type chunked_outbuf::data_chunk() {
54 DBG(cerr << "In chunked_outbuf::data_chunk" << endl);
55
56 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
57
58 // Since this is called by sync() (e.g., flush()), return 0 and do nothing
59 // when there's no data to send.
60 if (num == 0)
61 return 0;
62
63 // here, write out the chunk headers: CHUNKTYPE and CHUNKSIZE
64 // as a 32-bit unsigned int. Here I assume that num is never
65 // more than 2^24 because that was tested in the constructor
66
67 // Trick: This method always writes CHUNK_DATA type chunks so
68 // the chunk type is always 0x00, and given that num never has
69 // anything bigger than 24-bits, the high order byte is always
70 // 0x00. Of course bit-wise OR with 0x00 isn't going to do
71 // much anyway... Here's the general idea all the same:
72 //
73 // unsigned int chunk_header = (unsigned int)num | CHUNK_type;
74 uint32_t header = num;
75
76 // Add encoding of host's byte order. jhrg 11/24/13
77 if (!d_big_endian)
78 header |= CHUNK_LITTLE_ENDIAN;
79
80 // network byte order for the header
81 header = htonl(header);
82
83 d_os.write((const char *)&header, sizeof(int32_t));
84
85 // Should bad() throw an error?
86 // Are these functions fast or would the bits be faster?
87 d_os.write(d_buffer, num);
88 if (d_os.eof() || d_os.bad())
89 return traits_type::eof();
90
91 pbump(-num);
92 return num;
93}
94
106std::streambuf::int_type chunked_outbuf::end_chunk() {
107 DBG(cerr << "In chunked_outbuf::end_chunk" << endl);
108
109 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
110
111 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE
112 // as a 32-bit unsigned int. Here I assume that num is never
113 // more than 2^24 because that was tested in the constructor
114
115 uint32_t header = (uint32_t)num | CHUNK_END;
116
117 // Add encoding of host's byte order. jhrg 11/24/13
118 if (!d_big_endian)
119 header |= CHUNK_LITTLE_ENDIAN;
120
121 // network byte order for the header
122 header = htonl(header);
123
124 // Write out the CHUNK_END header with the byte count.
125 // This should be called infrequently, so it's probably not worth
126 // optimizing away chunk_header
127 d_os.write((const char *)&header, sizeof(uint32_t));
128
129 // Should bad() throw an error?
130 // Are these functions fast or would the bits be faster?
131 d_os.write(d_buffer, num);
132 if (d_os.eof() || d_os.bad())
133 return traits_type::eof();
134
135 pbump(-num);
136 return num;
137}
138
146std::streambuf::int_type chunked_outbuf::err_chunk(const std::string &m) {
147 DBG(cerr << "In chunked_outbuf::err_chunk" << endl);
148 std::string msg = m;
149
150 // Figure out how many chars are in the buffer - these will be
151 // ignored.
152 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump
153
154 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE
155 // as a 32-bit unsigned int. Here I assume that num is never
156 // more than 2^24 because that was tested in the constructor
157 if (msg.length() > 0x00FFFFFF)
158 msg = "Error message too long";
159
160 uint32_t header = (uint32_t)msg.length() | CHUNK_ERR;
161
162 // Add encoding of host's byte order. jhrg 11/24/13
163 if (!d_big_endian)
164 header |= CHUNK_LITTLE_ENDIAN;
165
166 // network byte order for the header
167 header = htonl(header);
168
169 // Write out the CHUNK_END header with the byte count.
170 // This should be called infrequently, so it's probably not worth
171 // optimizing away chunk_header
172 d_os.write((const char *)&header, sizeof(uint32_t));
173
174 // Should bad() throw an error?
175 // Are these functions fast or would the bits be faster?
176 d_os.write(msg.data(), msg.length());
177 if (d_os.eof() || d_os.bad())
178 return traits_type::eof();
179
180 // Reset the buffer pointer, effectively ignoring what's in there now
181 pbump(-num);
182
183 // return the number of characters ignored
184 return num;
185}
186
199std::streambuf::int_type chunked_outbuf::overflow(int c) {
200 DBG(cerr << "In chunked_outbuf::overflow" << endl);
201
202 // Note that the buffer and eptr() were set so that when pptr() is
203 // at the end of the buffer, there is actually one more character
204 // available in the buffer.
205 if (!traits_type::eq_int_type(c, traits_type::eof())) {
206 *pptr() = traits_type::not_eof(c);
207 pbump(1);
208 }
209 // flush the buffer
210 if (data_chunk() == traits_type::eof()) {
211 // Error
212 return traits_type::eof();
213 }
214
215 return traits_type::not_eof(c);
216}
217
218/*
219
220 d_buffer
221 |
222 v
223 |--------------------------------------------|....
224 | | .
225 |--------------------------------------------|....
226 ^ ^ ^
227 | | |
228 pbase() pptr() epptr()
229
230 */
231
239std::streamsize chunked_outbuf::xsputn(const char *s, std::streamsize num) {
240 DBG(cerr << "In chunked_outbuf::xsputn: num: " << num << endl);
241
242 // if the current block of data will fit in the buffer, put it there.
243 // else, there is at least a complete chunk between what's in the buffer
244 // and what's in 's'; send a chunk header, the stuff in the buffer and
245 // bytes from 's' to make a complete chunk. Then iterate over 's' sending
246 // more chunks until there's less than a complete chunk left in 's'. Put
247 // the bytes remaining 's' in the buffer. Return the number of bytes sent
248 // or 0 if an error is encountered.
249
250 int32_t bytes_in_buffer = pptr() - pbase(); // num needs to be signed for the call to pbump
251
252 // Will num bytes fit in the buffer? The location of epptr() is one back from
253 // the actual end of the buffer, so the next char written will trigger a write
254 // of the buffer as a new data chunk.
255 if (bytes_in_buffer + num < d_buf_size) {
256 DBG2(cerr << ":xsputn: buffering num: " << num << endl);
257 memcpy(pptr(), s, num);
258 pbump(num);
259 return traits_type::not_eof(num);
260 }
261
262 // If here, write a chunk header and a chunk's worth of data by combining the
263 // data in the buffer and some data from 's'.
264 uint32_t header = d_buf_size;
265
266 // Add encoding of host's byte order. jhrg 11/24/13
267 if (!d_big_endian)
268 header |= CHUNK_LITTLE_ENDIAN;
269
270 // network byte order for the header
271 header = htonl(header);
272
273 d_os.write((const char *)&header, sizeof(int32_t)); // Data chunk's CHUNK_TYPE is 0x00000000
274
275 // Reset the pptr() and epptr() now in case of an error exit. See the 'if'
276 // at the end of this for the only code from here down that will modify the
277 // pptr() value.
278 setp(d_buffer, d_buffer + (d_buf_size - 1));
279
280 d_os.write(d_buffer, bytes_in_buffer);
281 if (d_os.eof() || d_os.bad())
282 return traits_type::not_eof(0);
283
284 int bytes_to_fill_out_buffer = d_buf_size - bytes_in_buffer;
285 d_os.write(s, bytes_to_fill_out_buffer);
286 if (d_os.eof() || d_os.bad())
287 return traits_type::not_eof(0);
288 s += bytes_to_fill_out_buffer;
289 uint32_t bytes_still_to_send = num - bytes_to_fill_out_buffer;
290
291 // Now send all the remaining data in s until the amount remaining doesn't
292 // fill a complete chunk and buffer those data.
293 while (bytes_still_to_send >= d_buf_size) {
294 // This is header for a chunk of d_buf_size bytes; the size was set above
295 d_os.write((const char *)&header, sizeof(int32_t));
296 d_os.write(s, d_buf_size);
297 if (d_os.eof() || d_os.bad())
298 return traits_type::not_eof(0);
299 s += d_buf_size;
300 bytes_still_to_send -= d_buf_size;
301 }
302
303 if (bytes_still_to_send > 0) {
304 // if the code is here, one or more chunks have been sent, the
305 // buffer is empty and there are < d_buf_size bytes to send. Buffer
306 // them.
307 memcpy(d_buffer, s, bytes_still_to_send);
308 pbump(bytes_still_to_send);
309 }
310
311 // Unless an error was detected while writing to the stream, the code must
312 // have sent num bytes.
313 return traits_type::not_eof(num);
314}
315
321std::streambuf::int_type chunked_outbuf::sync() {
322 DBG(cerr << "In chunked_outbuf::sync" << endl);
323
324 if (data_chunk() == traits_type::eof()) {
325 // Error
326 return traits_type::not_eof(-1);
327 }
328 return traits_type::not_eof(0);
329}
330
331} // namespace libdap
#define CHUNK_ERR
#define CHUNK_LITTLE_ENDIAN
#define CHUNK_END
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,...
#define DBG(x)
Definition debug.h:58
#define DBG2(x)
Definition debug.h:74
top level DAP object to house generic methods
Definition AISConnect.cc:30