libdap
Updated for version 3.17.0
|
00001 // -*- mode: c++; c-basic-offset:4 -*- 00002 00003 // This file is part of libdap, A C++ implementation of the OPeNDAP Data 00004 // Access Protocol. 00005 00006 // Copyright (c) 2009 OPeNDAP, Inc. 00007 // Author: James Gallagher <jgallagher@opendap.org> 00008 // 00009 // This library is free software; you can redistribute it and/or 00010 // modify it under the terms of the GNU Lesser General Public 00011 // License as published by the Free Software Foundation; either 00012 // version 2.1 of the License, or (at your option) any later version. 00013 // 00014 // This library is distributed in the hope that it will be useful, 00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 // Lesser General Public License for more details. 00018 // 00019 // You should have received a copy of the GNU Lesser General Public 00020 // License along with this library; if not, write to the Free Software 00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00022 // 00023 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. 00024 // 00025 // Portions of this code were taken verbatim from Josuttis, 00026 // "The C++ Standard Library," p.672 00027 00028 #include "config.h" 00029 00030 #include <arpa/inet.h> 00031 00032 #include <stdint.h> 00033 00034 #include <string> 00035 #include <streambuf> 00036 00037 #include <cstring> 00038 00039 //#define DODS_DEBUG 00040 00041 #include "chunked_stream.h" 00042 #include "chunked_ostream.h" 00043 #include "debug.h" 00044 00045 namespace libdap { 00046 00047 // flush the characters in the buffer 00053 std::streambuf::int_type 00054 chunked_outbuf::data_chunk() 00055 { 00056 DBG(cerr << "In chunked_outbuf::data_chunk" << endl); 00057 00058 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump 00059 00060 // Since this is called by sync() (e.g., flush()), return 0 and do nothing 00061 // when there's no data to send. 00062 if (num == 0) 00063 return 0; 00064 00065 // here, write out the chunk headers: CHUNKTYPE and CHUNKSIZE 00066 // as a 32-bit unsigned int. Here I assume that num is never 00067 // more than 2^24 because that was tested in the constructor 00068 00069 // Trick: This method always writes CHUNK_DATA type chunks so 00070 // the chunk type is always 0x00, and given that num never has 00071 // anything bigger than 24-bits, the high order byte is always 00072 // 0x00. Of course bit-wise OR with 0x00 isn't going to do 00073 // much anyway... Here's the general idea all the same: 00074 // 00075 // unsigned int chunk_header = (unsigned int)num | CHUNK_type; 00076 uint32_t header = num; 00077 #if !BYTE_ORDER_PREFIX 00078 // Add encoding of host's byte order. jhrg 11/24/13 00079 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN; 00080 // network byte order for the header 00081 htonl(header); 00082 #endif 00083 00084 d_os.write((const char *)&header, sizeof(int32_t)); 00085 00086 // Should bad() throw an error? 00087 // Are these functions fast or would the bits be faster? 00088 d_os.write(d_buffer, num); 00089 if (d_os.eof() || d_os.bad()) 00090 return traits_type::eof(); 00091 00092 pbump(-num); 00093 return num; 00094 } 00095 00107 std::streambuf::int_type 00108 chunked_outbuf::end_chunk() 00109 { 00110 DBG(cerr << "In chunked_outbuf::end_chunk" << endl); 00111 00112 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump 00113 00114 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE 00115 // as a 32-bit unsigned int. Here I assume that num is never 00116 // more than 2^24 because that was tested in the constructor 00117 00118 uint32_t header = (uint32_t)num | CHUNK_END; 00119 00120 #if !BYTE_ORDER_PREFIX 00121 // Add encoding of host's byte order. jhrg 11/24/13 00122 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN; 00123 // network byte order for the header 00124 htonl(header); 00125 #endif 00126 00127 // Write out the CHUNK_END header with the byte count. 00128 // This should be called infrequently, so it's probably not worth 00129 // optimizing away chunk_header 00130 d_os.write((const char *)&header, sizeof(uint32_t)); 00131 00132 // Should bad() throw an error? 00133 // Are these functions fast or would the bits be faster? 00134 d_os.write(d_buffer, num); 00135 if (d_os.eof() || d_os.bad()) 00136 return traits_type::eof(); 00137 00138 pbump(-num); 00139 return num; 00140 } 00141 00149 std::streambuf::int_type 00150 chunked_outbuf::err_chunk(const std::string &m) 00151 { 00152 DBG(cerr << "In chunked_outbuf::err_chunk" << endl); 00153 std::string msg = m; 00154 00155 // Figure out how many chars are in the buffer - these will be 00156 // ignored. 00157 int32_t num = pptr() - pbase(); // num needs to be signed for the call to pbump 00158 00159 // write out the chunk headers: CHUNKTYPE and CHUNKSIZE 00160 // as a 32-bit unsigned int. Here I assume that num is never 00161 // more than 2^24 because that was tested in the constructor 00162 if (msg.length() > 0x00FFFFFF) 00163 msg = "Error message too long"; 00164 00165 uint32_t header = (uint32_t)msg.length() | CHUNK_ERR; 00166 00167 #if !BYTE_ORDER_PREFIX 00168 // Add encoding of host's byte order. jhrg 11/24/13 00169 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN; 00170 // network byte order for the header 00171 htonl(header); 00172 #endif 00173 00174 // Write out the CHUNK_END header with the byte count. 00175 // This should be called infrequently, so it's probably not worth 00176 // optimizing away chunk_header 00177 d_os.write((const char *)&header, sizeof(uint32_t)); 00178 00179 // Should bad() throw an error? 00180 // Are these functions fast or would the bits be faster? 00181 d_os.write(msg.data(), msg.length()); 00182 if (d_os.eof() || d_os.bad()) 00183 return traits_type::eof(); 00184 00185 // Reset the buffer pointer, effectively ignoring what's in there now 00186 pbump(-num); 00187 00188 // return the number of characters ignored 00189 return num; 00190 } 00191 00204 std::streambuf::int_type 00205 chunked_outbuf::overflow(int c) 00206 { 00207 DBG(cerr << "In chunked_outbuf::overflow" << endl); 00208 00209 // Note that the buffer and eptr() were set so that when pptr() is 00210 // at the end of the buffer, there is actually one more character 00211 // available in the buffer. 00212 if (!traits_type::eq_int_type(c, traits_type::eof())) { 00213 *pptr() = traits_type::not_eof(c); 00214 pbump(1); 00215 } 00216 // flush the buffer 00217 if (data_chunk() == traits_type::eof()) { 00218 //Error 00219 return traits_type::eof(); 00220 } 00221 00222 return traits_type::not_eof(c); 00223 } 00224 00225 /* 00226 00227 d_buffer 00228 | 00229 v 00230 |--------------------------------------------|.... 00231 | | . 00232 |--------------------------------------------|.... 00233 ^ ^ ^ 00234 | | | 00235 pbase() pptr() epptr() 00236 00237 */ 00238 00246 std::streamsize 00247 chunked_outbuf::xsputn(const char *s, std::streamsize num) 00248 { 00249 DBG(cerr << "In chunked_outbuf::xsputn: num: " << num << endl); 00250 00251 // if the current block of data will fit in the buffer, put it there. 00252 // else, there is at least a complete chunk between what's in the buffer 00253 // and what's in 's'; send a chunk header, the stuff in the buffer and 00254 // bytes from 's' to make a complete chunk. Then iterate over 's' sending 00255 // more chunks until there's less than a complete chunk left in 's'. Put 00256 // the bytes remaining 's' in the buffer. Return the number of bytes sent 00257 // or 0 if an error is encountered. 00258 00259 int32_t bytes_in_buffer = pptr() - pbase(); // num needs to be signed for the call to pbump 00260 00261 // Will num bytes fit in the buffer? The location of epptr() is one back from 00262 // the actual end of the buffer, so the next char written will trigger a write 00263 // of the buffer as a new data chunk. 00264 if (bytes_in_buffer + num < d_buf_size) { 00265 DBG2(cerr << ":xsputn: buffering num: " << num << endl); 00266 memcpy(pptr(), s, num); 00267 pbump(num); 00268 return traits_type::not_eof(num); 00269 } 00270 00271 // If here, write a chunk header and a chunk's worth of data by combining the 00272 // data in the buffer and some data from 's'. 00273 uint32_t header = d_buf_size; 00274 #if !BYTE_ORDER_PREFIX 00275 // Add encoding of host's byte order. jhrg 11/24/13 00276 if (!d_big_endian) header |= CHUNK_LITTLE_ENDIAN; 00277 // network byte order for the header 00278 htonl(header); 00279 #endif 00280 d_os.write((const char *)&header, sizeof(int32_t)); // Data chunk's CHUNK_TYPE is 0x00000000 00281 00282 // Reset the pptr() and epptr() now in case of an error exit. See the 'if' 00283 // at teh end of this for the only code from here down that will modify the 00284 // pptr() value. 00285 setp(d_buffer, d_buffer + (d_buf_size - 1)); 00286 00287 d_os.write(d_buffer, bytes_in_buffer); 00288 if (d_os.eof() || d_os.bad()) 00289 return traits_type::not_eof(0); 00290 00291 int bytes_to_fill_out_buffer = d_buf_size - bytes_in_buffer; 00292 d_os.write(s, bytes_to_fill_out_buffer); 00293 if (d_os.eof() || d_os.bad()) 00294 return traits_type::not_eof(0); 00295 s += bytes_to_fill_out_buffer; 00296 uint32_t bytes_still_to_send = num - bytes_to_fill_out_buffer; 00297 00298 // Now send all the remaining data in s until the amount remaining doesn't 00299 // fill a complete chunk and buffer those data. 00300 while (bytes_still_to_send >= d_buf_size) { 00301 // This is header for a chunk of d_buf_size bytes; the size was set above 00302 d_os.write((const char *) &header, sizeof(int32_t)); 00303 d_os.write(s, d_buf_size); 00304 if (d_os.eof() || d_os.bad()) return traits_type::not_eof(0); 00305 s += d_buf_size; 00306 bytes_still_to_send -= d_buf_size; 00307 } 00308 00309 if (bytes_still_to_send > 0) { 00310 // if the code is here, one or more chunks have been sent, the 00311 // buffer is empty and there are < d_buf_size bytes to send. Buffer 00312 // them. 00313 memcpy(d_buffer, s, bytes_still_to_send); 00314 pbump(bytes_still_to_send); 00315 } 00316 00317 // Unless an error was detected while writing to the stream, the code must 00318 // have sent num bytes. 00319 return traits_type::not_eof(num); 00320 } 00321 00327 std::streambuf::int_type 00328 chunked_outbuf::sync() 00329 { 00330 DBG(cerr << "In chunked_outbuf::sync" << endl); 00331 00332 if (data_chunk() == traits_type::eof()) { 00333 // Error 00334 return traits_type::not_eof(-1); 00335 } 00336 return traits_type::not_eof(0); 00337 } 00338 00339 } // namespace libdap