libdap  Updated for version 3.20.6
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 
45 namespace libdap {
46 
47 // flush the characters in the buffer
53 std::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 
107 std::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 
149 std::streambuf::int_type
150 chunked_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 
204 std::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 
246 std::streamsize
247 chunked_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 
328 std::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 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
Definition: AISConnect.cc:30
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 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.
int_type data_chunk()
Write out the contents of the buffer as a chunk.