|
Packit |
a4aae4 |
// -*- mode: c++; c-basic-offset:4 -*-
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
|
|
Packit |
a4aae4 |
// Access Protocol.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Copyright (c) 2013 OPeNDAP, Inc.
|
|
Packit |
a4aae4 |
// Author: James Gallagher <jgallagher@opendap.org>
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is free software; you can redistribute it and/or
|
|
Packit |
a4aae4 |
// modify it under the terms of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License as published by the Free Software Foundation; either
|
|
Packit |
a4aae4 |
// version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is distributed in the hope that it will be useful,
|
|
Packit |
a4aae4 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4aae4 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
a4aae4 |
// Lesser General Public License for more details.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You should have received a copy of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License along with this library; if not, write to the Free Software
|
|
Packit |
a4aae4 |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// Portions of this code were taken verbatim from Josuttis,
|
|
Packit |
a4aae4 |
// "The C++ Standard Library," p.672
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifndef _chunkedostream_h
|
|
Packit |
a4aae4 |
#define _chunkedostream_h
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "chunked_stream.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <streambuf>
|
|
Packit |
a4aae4 |
#include <ostream>
|
|
Packit |
a4aae4 |
#include <stdexcept> // std::out_of_range
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "util.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
namespace libdap {
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
class chunked_ostream;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief output buffer for a chunked stream
|
|
Packit |
a4aae4 |
* This performs buffered output encoding the data in the stream using
|
|
Packit |
a4aae4 |
* the simple chunking protocol defined for DAP4's binary data transmission.
|
|
Packit |
a4aae4 |
* Each block of data is prefixed by four bytes: A CHUNK TYPE byte followed
|
|
Packit |
a4aae4 |
* by three bytes that are the CHUNK SIZE. There are three CHUNK TYPES:
|
|
Packit |
a4aae4 |
* data, end and error, indicated by the code values 0x00, 0x01 and 0x02.
|
|
Packit |
a4aae4 |
* The size of a chunk is limited to 2^24 data bytes + 4 bytes for the
|
|
Packit |
a4aae4 |
* chunk header.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
class chunked_outbuf: public std::streambuf {
|
|
Packit |
a4aae4 |
friend class chunked_ostream;
|
|
Packit |
a4aae4 |
protected:
|
|
Packit |
a4aae4 |
std::ostream &d_os; // Write stuff here
|
|
Packit |
a4aae4 |
unsigned int d_buf_size; // Size of the data buffer
|
|
Packit |
a4aae4 |
char *d_buffer; // Data buffer
|
|
Packit |
a4aae4 |
bool d_big_endian;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
public:
|
|
Packit |
a4aae4 |
chunked_outbuf(std::ostream &os, unsigned int buf_size) : d_os(os), d_buf_size(buf_size), d_buffer(0) {
|
|
Packit |
a4aae4 |
if (d_buf_size & CHUNK_TYPE_MASK)
|
|
Packit |
a4aae4 |
throw std::out_of_range("A chunked_outbuf (or chunked_ostream) was built using a buffer larger than 0x00ffffff");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_big_endian = is_host_big_endian();
|
|
Packit |
a4aae4 |
d_buffer = new char[buf_size];
|
|
Packit |
a4aae4 |
// Trick: making the pointers think the buffer is one char smaller than it
|
|
Packit |
a4aae4 |
// really is ensures that overflow() will be called when there's space for
|
|
Packit |
a4aae4 |
// one more character.
|
|
Packit |
a4aae4 |
setp(d_buffer, d_buffer + (buf_size - 1));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
virtual ~chunked_outbuf() {
|
|
Packit |
a4aae4 |
// call end_chunk() and not sync()
|
|
Packit |
a4aae4 |
end_chunk();
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete[] d_buffer;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
protected:
|
|
Packit |
a4aae4 |
// data_chunk and end_chunk might not be needed because they
|
|
Packit |
a4aae4 |
// are called via flush() and ~chunked_outbuf(), resp. jhrg 9/13/13
|
|
Packit |
a4aae4 |
int_type data_chunk(); // sync() and overflow() call this
|
|
Packit |
a4aae4 |
int_type end_chunk();
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
int_type err_chunk(const std::string &msg;;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
virtual std::streamsize xsputn(const char *s, std::streamsize num);
|
|
Packit |
a4aae4 |
// Manipulate the buffer pointers using pbump() after filling the buffer
|
|
Packit |
a4aae4 |
// and then call data_chunk(). Leave remainder in buffer. Or copy logic
|
|
Packit |
a4aae4 |
// for data_chunk() into loop in this code.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
virtual int_type overflow(int c);
|
|
Packit |
a4aae4 |
virtual int_type sync();
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief A C++ stream class for chunked data.
|
|
Packit |
a4aae4 |
* This class uses the chunked_outbuf class to provide for chunked
|
|
Packit |
a4aae4 |
* binary serialization of data as specified by DAP4. Information
|
|
Packit |
a4aae4 |
* to be serialized is broken into 'chunks' that are no more than
|
|
Packit |
a4aae4 |
* 2^24 bytes in length. Each chunk is prefixed by a 4 byte header
|
|
Packit |
a4aae4 |
* that indicates the type of chunk and size (number of bytes in the
|
|
Packit |
a4aae4 |
* chunk body). There are three types of chunk: Data; End; and Error.
|
|
Packit |
a4aae4 |
* In normal operation, a DAP4 data document/response is serialized as
|
|
Packit |
a4aae4 |
* a sequence of DATA chunks followed by one END chunk (which may be
|
|
Packit |
a4aae4 |
* zero bytes in length). If, during serialization, an error is detected,
|
|
Packit |
a4aae4 |
* the currently buffered (but not sent) data are discarded and an
|
|
Packit |
a4aae4 |
* ERROR chunk is sent with an error message.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* This class sends the END chunk when its destructor is called.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* Calling flush() on the ostream object will force a DATA chunk to be
|
|
Packit |
a4aae4 |
* sent with the currently buffered data. Normal operation is to wait
|
|
Packit |
a4aae4 |
* for the buffer to fill before sending a DATA chunk.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @see chunked_outbuf
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
class chunked_ostream: public std::ostream {
|
|
Packit |
a4aae4 |
protected:
|
|
Packit |
a4aae4 |
chunked_outbuf d_cbuf;
|
|
Packit |
a4aae4 |
public:
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Get a chunked_ostream with a buffer.
|
|
Packit |
a4aae4 |
* @note The buffer size must not be more than 2^24 bytes (0x00ffffff)
|
|
Packit |
a4aae4 |
* @param buf_size The size of the buffer in bytes.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
chunked_ostream(std::ostream &os, unsigned int buf_size) : std::ostream(&d_cbuf), d_cbuf(os, buf_size) { }
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief Send an end chunk.
|
|
Packit |
a4aae4 |
* Normally, an end chunk is sent by closing the chunked_ostream, but this
|
|
Packit |
a4aae4 |
* method can be used to force sending it without closing the stream. Subsequent
|
|
Packit |
a4aae4 |
* calls to send data will send data chunks.
|
|
Packit |
a4aae4 |
* @note An end chunk is sent when the stream is closed.
|
|
Packit |
a4aae4 |
* @return EOF on error or the number of bytes sent in the chunk body.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
int_type write_end_chunk() { return d_cbuf.end_chunk(); }
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief Send the current contents of the buffer as a data chunk.
|
|
Packit |
a4aae4 |
* Normally, the chunked_ostream object waits until the buffer is full before sending
|
|
Packit |
a4aae4 |
* the next data chunk. This will force a send with whatever is in the buffer (e.g.,
|
|
Packit |
a4aae4 |
* the DMR text). Data added after this call will be sent in subsequent chunks.
|
|
Packit |
a4aae4 |
* @note Calling flush() on the stream forces a data chunk to be sent.
|
|
Packit |
a4aae4 |
* @return EOF on error, otherwise the number of bytes sent in the chunk body.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
int_type write_data_chunk() { return d_cbuf.data_chunk(); }
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief Send an error message down the stream.
|
|
Packit |
a4aae4 |
* When called, this method dumps all the data currently in the buffer and
|
|
Packit |
a4aae4 |
* sends the error message text instead, using a chunk type of CHUNK_ERR. The
|
|
Packit |
a4aae4 |
* write buffer is maintained, however, so the stream ibject can still be used.
|
|
Packit |
a4aae4 |
* @param msg The error message text
|
|
Packit |
a4aae4 |
* @return The number of bytes 'dumped' from the write buffer.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
int_type write_err_chunk(const std::string &msg) { return d_cbuf.err_chunk(msg); }
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#endif // _chunkedostream_h
|