Blame chunked_istream.h

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 _chunked_istream_h
Packit a4aae4
#define _chunked_istream_h
Packit a4aae4
Packit a4aae4
#include "chunked_stream.h"
Packit a4aae4
Packit a4aae4
#include <stdint.h>
Packit a4aae4
Packit a4aae4
#include <streambuf>
Packit a4aae4
#include <istream>
Packit a4aae4
#include <stdexcept>
Packit a4aae4
#include <string>
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
class chunked_inbuf: public std::streambuf {
Packit a4aae4
private:
Packit a4aae4
	std::istream &d_is;
Packit a4aae4
Packit a4aae4
	uint32_t d_buf_size;	// Size of the data buffer
Packit a4aae4
	char *d_buffer;			// data buffer
Packit a4aae4
Packit a4aae4
	// In the original implementation of this class, the byte order of the data stream
Packit a4aae4
	// was passed in via constructors. When BYTE_ORDER_PREFIX is defined that is the
Packit a4aae4
	// case. However, when it is not defined, the byte order is read from the chunk
Packit a4aae4
	// header's high order byte (in bit position 2 - see chunked_stream.h). jhrg 11/24/13
Packit a4aae4
Packit a4aae4
	bool d_twiddle_bytes; 	// receiver-makes-right encoding (byte order)...
Packit a4aae4
	bool d_set_twiddle;
Packit a4aae4
Packit a4aae4
	// If an error chunk is read, save the message here
Packit a4aae4
	std::string d_error_message;
Packit a4aae4
	bool d_error;
Packit a4aae4
Packit a4aae4
	/**
Packit a4aae4
	 * @brief allocate the internal buffer.
Packit a4aae4
	 * Allocate d_buf_size + putBack characters for the read buffer.
Packit a4aae4
	 * @param size How much can the buffer hold? Does not include the putBack
Packit a4aae4
	 * chars.
Packit a4aae4
	 */
Packit a4aae4
	void m_buffer_alloc() {
Packit a4aae4
		delete d_buffer;
Packit a4aae4
		d_buffer = new char[d_buf_size];
Packit a4aae4
		setg(d_buffer, 	// beginning of put back area
Packit a4aae4
			 d_buffer, 	// read position
Packit a4aae4
		     d_buffer); // end position
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
public:
Packit a4aae4
	/**
Packit a4aae4
	 * @brief Build a chunked input buffer.
Packit a4aae4
	 *
Packit a4aae4
	 * This reads from a chunked stream, extracting an entire chunk and storing it in a
Packit a4aae4
	 * buffer in one operation. If the chunked_inbuf reads a chunk header that indicates
Packit a4aae4
	 * the next chunk is going  be bigger than its current buffer size, the object will
Packit a4aae4
	 * make the buffer larger. This object support 128 characters of 'put back' space. Since
Packit a4aae4
	 * DAP4 uses receiver makes right, the buffer must be told if it should 'twiddle' the
Packit a4aae4
	 * header size information. In DAP4 the byte order is sent using a one-byte code _before_
Packit a4aae4
	 * the chunked transmission starts.
Packit a4aae4
	 *
Packit a4aae4
	 * @note In the current implementation, the byte order of the sender is read from the
Packit a4aae4
	 * first chunk header. The method twiddle_bytes() returns false until the first chunk is
Packit a4aae4
	 * read, then it returns the correct value. Only the first chunk_header is tested for the
Packit a4aae4
	 * byte order flag; all subsequent chunks are assumed to use the same byte order.
Packit a4aae4
	 *
Packit a4aae4
	 * @param is Use this as a data source
Packit a4aae4
	 * @param size The size of the input buffer. This should match the likely chunk size.
Packit a4aae4
	 * If it is smaller than a chunk, it will be resized.
Packit a4aae4
	 * @param twiddle_bytes Should the header bytes be twiddled? True if this host and the
Packit a4aae4
	 * send use a different byte-order. The sender's byte order must be sent out-of-band.
Packit a4aae4
	 */
Packit a4aae4
#if BYTE_ORDER_PREFIX
Packit a4aae4
	chunked_inbuf(std::istream &is, int size, bool twiddle_bytes = false)
Packit a4aae4
        : d_is(is), d_buf_size(size), d_buffer(0), d_twiddle_bytes(twiddle_bytes), d_error(false) {
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
		m_buffer_alloc();
Packit a4aae4
	}
Packit a4aae4
#else
Packit a4aae4
    chunked_inbuf(std::istream &is, int size)
Packit a4aae4
        : d_is(is), d_buf_size(size), d_buffer(0), d_twiddle_bytes(false), d_set_twiddle(false), d_error(false) {
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
        m_buffer_alloc();
Packit a4aae4
    }
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
	virtual ~chunked_inbuf() {
Packit a4aae4
		delete[] d_buffer;
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	int_type read_next_chunk();
Packit a4aae4
Packit a4aae4
	int bytes_in_buffer() const { return (egptr() - gptr()); }
Packit a4aae4
Packit a4aae4
	// d_twiddle_bytes is false initially and is set to the correct value
Packit a4aae4
	// once the first chunk is read.
Packit a4aae4
	bool twiddle_bytes() const { return d_twiddle_bytes; }
Packit a4aae4
Packit a4aae4
	bool error() const { return d_error; }
Packit a4aae4
	std::string error_message() const { return d_error_message; }
Packit a4aae4
Packit a4aae4
protected:
Packit a4aae4
	virtual int_type underflow();
Packit a4aae4
Packit a4aae4
	virtual std::streamsize xsgetn(char* s, std::streamsize num);
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
class chunked_istream: public std::istream {
Packit a4aae4
protected:
Packit a4aae4
	chunked_inbuf d_cbuf;
Packit a4aae4
public:
Packit a4aae4
#if BYTE_ORDER_PREFIX
Packit a4aae4
	chunked_istream(std::istream &is, int size, bool twiddle_bytes = false) : std::istream(&d_cbuf), d_cbuf(is, size, twiddle_bytes) { }
Packit a4aae4
#else
Packit a4aae4
    chunked_istream(std::istream &is, int size) : std::istream(&d_cbuf), d_cbuf(is, size) { }
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
	int read_next_chunk() { return d_cbuf.read_next_chunk(); }
Packit a4aae4
Packit a4aae4
	/**
Packit a4aae4
	 * How many bytes have been read from the stream and are now in the internal buffer?
Packit a4aae4
	 * @return Number of buffered bytes.
Packit a4aae4
	 */
Packit a4aae4
	int bytes_in_buffer() const { return d_cbuf.bytes_in_buffer(); }
Packit a4aae4
Packit a4aae4
	/**
Packit a4aae4
	 * Should the receiver twiddle the bytes to match the local machine's byte order?
Packit a4aae4
	 * Since DAP4 uses 'receiver makes right' encoding, the onus is on the client to
Packit a4aae4
	 * reorder the bytes if it is, e.g., a big endian machine reading data from a little
Packit a4aae4
	 * endian server.
Packit a4aae4
	 *
Packit a4aae4
	 * @return True if the client (caller) should swap bytes in multi-byte integers, etc.,
Packit a4aae4
	 * and false if not. This does not directly tell the endian nature of the remote server,
Packit a4aae4
	 * although that can be inferred.
Packit a4aae4
	 */
Packit a4aae4
	bool twiddle_bytes() const { return d_cbuf.twiddle_bytes(); }
Packit a4aae4
	bool error() const { return d_cbuf.error(); }
Packit a4aae4
	std::string error_message() const { return d_cbuf.error_message(); }
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
#endif	// _chunked_istream_h