libdap  Updated for version 3.17.0
chunked_ostream.cc
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