libdap  Updated for version 3.20.4
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 #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 
109 std::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 
153 std::streambuf::int_type
154 chunked_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 
210 std::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 
252 std::streamsize
253 chunked_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 
335 std::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 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: AlarmHandler.h:35
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.