Blame chunked_istream.cc

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) 2009 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
#include "config.h"
Packit a4aae4
Packit a4aae4
#include <stdint.h>
Packit a4aae4
#include <byteswap.h>
Packit a4aae4
#include <arpa/inet.h>
Packit a4aae4
Packit a4aae4
#include <cstring>
Packit a4aae4
#include <vector>
Packit a4aae4
Packit a4aae4
#include "chunked_stream.h"
Packit a4aae4
#include "chunked_istream.h"
Packit a4aae4
Packit a4aae4
#include "Error.h"
Packit a4aae4
Packit a4aae4
//#define DODS_DEBUG
Packit a4aae4
//#define DODS_DEBUG2
Packit a4aae4
#ifdef DODS_DEBUG
Packit a4aae4
#include <iostream>
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#include "util.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
/*
Packit a4aae4
  This code does not use a 'put back' buffer, but here's a picture of the
Packit a4aae4
  d_buffer pointer, eback(), gptr() and egptr() that can be used to see how
Packit a4aae4
  the I/O Stream library's streambuf class works. For the case with no
Packit a4aae4
  putback, just imagine it as zero and eliminate the leftmost extension. This
Packit a4aae4
  might also come in useful if the code was extended to support put back. I
Packit a4aae4
  removed that feature because I don't see it being used with our chunked
Packit a4aae4
  transmission protocol and it requires an extra call to memcopy() when data
Packit a4aae4
  are added to the internal buffer.
Packit a4aae4
Packit a4aae4
  d_buffer  d_buffer + putBack
Packit a4aae4
  |         |
Packit a4aae4
  v         v
Packit a4aae4
  |---------|--------------------------------------------|....
Packit a4aae4
  |         |                                            |   .
Packit a4aae4
  |---------|--------------------------------------------|....
Packit a4aae4
            ^                         ^                   ^
Packit a4aae4
            |                         |                   |
Packit a4aae4
            eback()                   gptr()              egptr()
Packit a4aae4
Packit a4aae4
 */
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Insert new characters into the buffer
Packit a4aae4
 * This specialization of underflow is called when the gptr() is advanced to
Packit a4aae4
 * the end of the input buffer. At that point it calls the underlying I/O stream
Packit a4aae4
 * to read the next chunk of data and transfers the data read to the internal
Packit a4aae4
 * buffer. If an error is found, EOF is returned. If an END chunk with zero
Packit a4aae4
 * bytes is found, an EOF is returned.
Packit a4aae4
 * @return The character at the gptr() or EOF
Packit a4aae4
 */
Packit a4aae4
std::streambuf::int_type
Packit a4aae4
chunked_inbuf::underflow()
Packit a4aae4
{
Packit a4aae4
    DBG(cerr << "underflow..." << endl);
Packit a4aae4
    DBG2(cerr << "eback(): " << (void*)eback() << ", gptr(): " << (void*)(gptr()-eback()) << ", egptr(): " << (void*)(egptr()-eback()) << endl);
Packit a4aae4
Packit a4aae4
	// return the next character; uflow() increments the puffer pointer.
Packit a4aae4
	if (gptr() < egptr())
Packit a4aae4
		return traits_type::to_int_type(*gptr());
Packit a4aae4
Packit a4aae4
	// gptr() == egptr() so read more data from the underlying input source.
Packit a4aae4
Packit a4aae4
	// To read data from the chunked stream, first read the header
Packit a4aae4
	uint32_t header;
Packit a4aae4
	d_is.read((char *) &header, 4);
Packit a4aae4
#if !BYTE_ORDER_PREFIX
Packit a4aae4
	// When the endian nature of the server is encoded in the chunk header, the header is
Packit a4aae4
	// sent using network byte order
Packit a4aae4
	ntohl(header);
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
	// There are two 'EOF' cases: One where the END chunk is zero bytes and one where
Packit a4aae4
	// it holds data. In the latter case, bytes those will be read and moved into the
Packit a4aae4
	// buffer. Once those data are consumed, we'll be back here again and this read()
Packit a4aae4
	// will return EOF. See below for the other case...
Packit a4aae4
	if (d_is.eof()) return traits_type::eof();
Packit a4aae4
#if BYTE_ORDER_PREFIX
Packit a4aae4
	if (d_twiddle_bytes) header = bswap_32(header);
Packit a4aae4
#else
Packit a4aae4
	// (header & CHUNK_LITTLE_ENDIAN) --> is the sender little endian
Packit a4aae4
	if (!d_set_twiddle) {
Packit a4aae4
	    d_twiddle_bytes = (is_host_big_endian() == (header & CHUNK_LITTLE_ENDIAN));
Packit a4aae4
	    d_set_twiddle = true;
Packit a4aae4
	}
Packit a4aae4
#endif
Packit a4aae4
	uint32_t chunk_size = header & CHUNK_SIZE_MASK;
Packit a4aae4
Packit a4aae4
	DBG(cerr << "underflow: chunk size from header: " << chunk_size << endl);
Packit a4aae4
	DBG(cerr << "underflow: chunk type from header: " << hex << (header & CHUNK_TYPE_MASK) << endl);
Packit a4aae4
	DBG(cerr << "underflow: chunk byte order from header: " << hex << (header & CHUNK_BIG_ENDIAN) << endl);
Packit a4aae4
Packit a4aae4
	// Handle the case where the buffer is not big enough to hold the incoming chunk
Packit a4aae4
	if (chunk_size > d_buf_size) {
Packit a4aae4
		d_buf_size = chunk_size;
Packit a4aae4
		m_buffer_alloc();
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	// If the END chunk has zero bytes, return EOF. See above for more information
Packit a4aae4
	if (chunk_size == 0 && (header & CHUNK_TYPE_MASK) == CHUNK_END) return traits_type::eof();
Packit a4aae4
Packit a4aae4
	// Read the chunk's data
Packit a4aae4
	d_is.read(d_buffer, chunk_size);
Packit a4aae4
	DBG2(cerr << "underflow: size read: " << d_is.gcount() << ", eof: " << d_is.eof() << ", bad: " << d_is.bad() << endl);
Packit a4aae4
	if (d_is.bad()) return traits_type::eof();
Packit a4aae4
Packit a4aae4
	DBG2(cerr << "eback(): " << (void*)eback() << ", gptr(): " << (void*)(gptr()-eback()) << ", egptr(): " << (void*)(egptr()-eback()) << endl);
Packit a4aae4
	setg(d_buffer, 						// beginning of put back area
Packit a4aae4
			d_buffer,                	// read position (gptr() == eback())
Packit a4aae4
			d_buffer + chunk_size);  	// end of buffer (egptr()) chunk_size == d_is.gcount() unless there's an error
Packit a4aae4
Packit a4aae4
	DBG2(cerr << "eback(): " << (void*)eback() << ", gptr(): " << (void*)(gptr()-eback()) << ", egptr(): " << (void*)(egptr()-eback()) << endl);
Packit a4aae4
Packit a4aae4
	switch (header & CHUNK_TYPE_MASK) {
Packit a4aae4
	case CHUNK_END:
Packit a4aae4
		DBG2(cerr << "Found end chunk" << endl);
Packit a4aae4
		return traits_type::to_int_type(*gptr());
Packit a4aae4
	case CHUNK_DATA:
Packit a4aae4
		return traits_type::to_int_type(*gptr());
Packit a4aae4
Packit a4aae4
	case CHUNK_ERR:
Packit a4aae4
		// this is pretty much the end of the show... Assume the buffer/chunk holds
Packit a4aae4
		// the error message text.
Packit a4aae4
		d_error = true;
Packit a4aae4
		d_error_message = string(d_buffer, chunk_size);
Packit a4aae4
		return traits_type::eof();
Packit a4aae4
	default:
Packit a4aae4
		d_error = true;
Packit a4aae4
		d_error_message = "Failed to read known chunk header type.";
Packit a4aae4
		return traits_type::eof();
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	return traits_type::eof();	// Can never get here; this quiets g++
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Read a block of data
Packit a4aae4
 * This specialization of xsgetn() reads \c num bytes and puts them in \c s
Packit a4aae4
 * first reading from the internal beffer and then from the stream. Any
Packit a4aae4
 * characters read from the last chunk that won't fit in to \c s are put
Packit a4aae4
 * in the buffer, otherwise all data are read directly into \c s, bypassing
Packit a4aae4
 * the internal buffer (and the extra copy operation that would imply). If
Packit a4aae4
 * the END chunk is found EOF is not returned and the final read of the
Packit a4aae4
 * underlying stream is not made; the next call to read(), get(), ..., will
Packit a4aae4
 * return EOF.
Packit a4aae4
 * @param s Address of a buffer to hold the data
Packit a4aae4
 * @param num Number of bytes to read
Packit a4aae4
 * @return NUmber of bytes actually transferred into \c s. Note that this
Packit a4aae4
 * number does not include the bytes read from the last chunk that won't
Packit a4aae4
 * fit into \c s so this will never return a number greater than num.
Packit a4aae4
 */
Packit a4aae4
std::streamsize
Packit a4aae4
chunked_inbuf::xsgetn(char* s, std::streamsize num)
Packit a4aae4
{
Packit a4aae4
	DBG(cerr << "xsgetn... num: " << num << endl);
Packit a4aae4
Packit a4aae4
	// if num is <= the chars currently in the buffer
Packit a4aae4
	if (num <= (egptr() - gptr())) {
Packit a4aae4
		memcpy(s, gptr(), num);
Packit a4aae4
		gbump(num);
Packit a4aae4
Packit a4aae4
		return traits_type::not_eof(num);
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	// else they asked for more
Packit a4aae4
	uint32_t bytes_left_to_read = num;
Packit a4aae4
Packit a4aae4
	// are there any bytes in the buffer? if so grab them first
Packit a4aae4
	if (gptr() < egptr()) {
Packit a4aae4
		int bytes_to_transfer = egptr() - gptr();
Packit a4aae4
		memcpy(s, gptr(), bytes_to_transfer);
Packit a4aae4
		gbump(bytes_to_transfer);
Packit a4aae4
		s += bytes_to_transfer;
Packit a4aae4
		bytes_left_to_read -= bytes_to_transfer;
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	// We need to get more bytes from the underlying stream; at this
Packit a4aae4
	// point the internal buffer is empty.
Packit a4aae4
Packit a4aae4
	// read the remaining bytes to transfer, a chunk at a time,
Packit a4aae4
	// and put any leftover stuff in the buffer.
Packit a4aae4
Packit a4aae4
	// note that when the code is here, gptr() == egptr(), so the
Packit a4aae4
	// next call to read() will fall through the previous tests and
Packit a4aae4
	// read at least one chunk here.
Packit a4aae4
	bool done = false;
Packit a4aae4
    while (!done) {
Packit a4aae4
        // Get a chunk header
Packit a4aae4
        uint32_t header;
Packit a4aae4
        d_is.read((char *) &header, 4);
Packit a4aae4
#if !BYTE_ORDER_PREFIX
Packit a4aae4
        ntohl(header);
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
        // There are two EOF cases: One where the END chunk is zero bytes and one where
Packit a4aae4
        // it holds data. In the latter case, those will be read and moved into the
Packit a4aae4
        // buffer. Once those data are consumed, we'll be back here again and this read()
Packit a4aae4
        // will return EOF. See below for the other case...
Packit a4aae4
        if (d_is.eof()) return traits_type::eof();
Packit a4aae4
#if BYTE_ORDER_PREFIX
Packit a4aae4
        if (d_twiddle_bytes) header = bswap_32(header);
Packit a4aae4
#else
Packit a4aae4
        // (header & CHUNK_LITTLE_ENDIAN) --> is the sender little endian
Packit a4aae4
        if (!d_set_twiddle) {
Packit a4aae4
            d_twiddle_bytes = (is_host_big_endian() == (header & CHUNK_LITTLE_ENDIAN));
Packit a4aae4
            d_set_twiddle = true;
Packit a4aae4
        }
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
	    uint32_t chunk_size = header & CHUNK_SIZE_MASK;
Packit a4aae4
		DBG(cerr << "xsgetn: chunk size from header: " << chunk_size << endl);
Packit a4aae4
		DBG(cerr << "xsgetn: chunk type from header: " << hex << (header & CHUNK_TYPE_MASK) << endl);
Packit a4aae4
		DBG(cerr << "xsgetn: chunk byte order from header: " << hex << (header & CHUNK_BIG_ENDIAN) << endl);
Packit a4aae4
Packit a4aae4
		// handle error chunks here
Packit a4aae4
	    if ((header & CHUNK_TYPE_MASK) == CHUNK_ERR) {
Packit a4aae4
			d_error = true;
Packit a4aae4
			// Note that d_buffer is not used to avoid calling resize if it is too
Packit a4aae4
			// small to hold the error message. At this point, there's not much reason
Packit a4aae4
			// to optimize transport efficiency, however.
Packit a4aae4
			std::vector<char> message(chunk_size);
Packit a4aae4
			d_is.read(&message[0], chunk_size);
Packit a4aae4
			d_error_message = string(&message[0], chunk_size);
Packit a4aae4
			// leave the buffer and gptr(), ..., in a consistent state (empty)
Packit a4aae4
			setg(d_buffer, d_buffer, d_buffer);
Packit a4aae4
	    }
Packit a4aae4
	    // And zero-length END chunks here.
Packit a4aae4
	    else if (chunk_size == 0 && (header & CHUNK_TYPE_MASK) == CHUNK_END) {
Packit a4aae4
	    	return traits_type::not_eof(num-bytes_left_to_read);
Packit a4aae4
	    }
Packit a4aae4
	    // The next case is complicated because we read some data from the current
Packit a4aae4
	    // chunk into 's' an some into the internal buffer.
Packit a4aae4
	    else if (chunk_size > bytes_left_to_read) {
Packit a4aae4
			d_is.read(s, bytes_left_to_read);
Packit a4aae4
			if (d_is.bad()) return traits_type::eof();
Packit a4aae4
Packit a4aae4
			// Now slurp up the remain part of the chunk and store it in the buffer
Packit a4aae4
			uint32_t bytes_leftover = chunk_size - bytes_left_to_read;
Packit a4aae4
			// expand the internal buffer if needed
Packit a4aae4
		    if (bytes_leftover > d_buf_size) {
Packit a4aae4
		        d_buf_size = chunk_size;
Packit a4aae4
		        m_buffer_alloc();
Packit a4aae4
		    }
Packit a4aae4
		    // read the remain stuff in to d_buffer
Packit a4aae4
			d_is.read(d_buffer, bytes_leftover);
Packit a4aae4
			if (d_is.bad()) return traits_type::eof();
Packit a4aae4
Packit a4aae4
			setg(d_buffer, 										// beginning of put back area
Packit a4aae4
				 d_buffer,                						// read position (gptr() == eback())
Packit a4aae4
				 d_buffer + bytes_leftover /*d_is.gcount()*/); 	// end of buffer (egptr())
Packit a4aae4
Packit a4aae4
			bytes_left_to_read = 0 /* -= d_is.gcount()*/;
Packit a4aae4
		}
Packit a4aae4
		else {
Packit a4aae4
			// expand the internal buffer if needed
Packit a4aae4
		    if (chunk_size > d_buf_size) {
Packit a4aae4
		        d_buf_size = chunk_size;
Packit a4aae4
		        m_buffer_alloc();
Packit a4aae4
		    }
Packit a4aae4
		    // If we get a chunk that's zero bytes, Don't call read()
Packit a4aae4
		    // to save the kernel context switch overhead.
Packit a4aae4
			if (chunk_size > 0) {
Packit a4aae4
				d_is.read(s, chunk_size);
Packit a4aae4
				if (d_is.bad()) return traits_type::eof();
Packit a4aae4
				bytes_left_to_read -= chunk_size /*d_is.gcount()*/;
Packit a4aae4
				s += chunk_size;
Packit a4aae4
			}
Packit a4aae4
		}
Packit a4aae4
Packit a4aae4
	    switch (header & CHUNK_TYPE_MASK) {
Packit a4aae4
	    case CHUNK_END:
Packit a4aae4
			DBG(cerr << "Found end chunk" << endl);
Packit a4aae4
	    	// in this case bytes_left_to_read can be > 0 because we ran out of data
Packit a4aae4
	    	// before reading all the requested bytes. The next read() call will return
Packit a4aae4
	    	// eof; this call returns the number of bytes read and transferred to 's'.
Packit a4aae4
	    	done = true;
Packit a4aae4
	    	break;
Packit a4aae4
	    case CHUNK_DATA:
Packit a4aae4
	    	done = bytes_left_to_read == 0;
Packit a4aae4
	        break;
Packit a4aae4
	    case CHUNK_ERR:
Packit a4aae4
			// this is pretty much the end of the show... The error message has
Packit a4aae4
	    	// already been read above
Packit a4aae4
			return traits_type::eof();
Packit a4aae4
	        break;
Packit a4aae4
		default:
Packit a4aae4
			d_error = true;
Packit a4aae4
			d_error_message = "Failed to read known chunk header type.";
Packit a4aae4
			return traits_type::eof();
Packit a4aae4
	    }
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	return traits_type::not_eof(num-bytes_left_to_read);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Read a chunk
Packit a4aae4
 * Normally the chunked nature of a chunked_istream/chunked_inbuf is
Packit a4aae4
 * hidden from the caller. This method provides a way to get one chunk
Packit a4aae4
 * from the stream by forcing its read and returning the size. A subsequent
Packit a4aae4
 * call to read() for that number of bytes will return all of the data in
Packit a4aae4
 * the chunk. If there is any data in the chunk_inbuf object's buffer, it is
Packit a4aae4
 * lost.
Packit a4aae4
 *
Packit a4aae4
 * @return The number of bytes read, which is exactly the size of the
Packit a4aae4
 * next chunk in the stream. Returns EOF on error.
Packit a4aae4
 */
Packit a4aae4
std::streambuf::int_type
Packit a4aae4
chunked_inbuf::read_next_chunk()
Packit a4aae4
{
Packit a4aae4
	// To read data from the chunked stream, first read the header
Packit a4aae4
	uint32_t header;
Packit a4aae4
	d_is.read((char *) &header, 4);
Packit a4aae4
#if !BYTE_ORDER_PREFIX
Packit a4aae4
    ntohl(header);
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
	// There are two 'EOF' cases: One where the END chunk is zero bytes and one where
Packit a4aae4
	// it holds data. In the latter case, bytes those will be read and moved into the
Packit a4aae4
	// buffer. Once those data are consumed, we'll be back here again and this read()
Packit a4aae4
	// will return EOF. See below for the other case...
Packit a4aae4
	if (d_is.eof()) return traits_type::eof();
Packit a4aae4
#if BYTE_ORDER_PREFIX
Packit a4aae4
    if (d_twiddle_bytes) header = bswap_32(header);
Packit a4aae4
#else
Packit a4aae4
    // (header & CHUNK_LITTLE_ENDIAN) --> is the sender little endian
Packit a4aae4
    if (!d_set_twiddle) {
Packit a4aae4
        d_twiddle_bytes = (is_host_big_endian() == (header & CHUNK_LITTLE_ENDIAN));
Packit a4aae4
        d_set_twiddle = true;
Packit a4aae4
    }
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
	uint32_t chunk_size = header & CHUNK_SIZE_MASK;
Packit a4aae4
Packit a4aae4
	DBG(cerr << "read_next_chunk: chunk size from header: " << chunk_size << endl);
Packit a4aae4
	DBG(cerr << "read_next_chunk: chunk type from header: " << hex << (header & CHUNK_TYPE_MASK) << endl);
Packit a4aae4
	DBG(cerr << "read_next_chunk: chunk byte order from header: " << hex << (header & CHUNK_BIG_ENDIAN) << endl);
Packit a4aae4
Packit a4aae4
	// Handle the case where the buffer is not big enough to hold the incoming chunk
Packit a4aae4
	if (chunk_size > d_buf_size) {
Packit a4aae4
		d_buf_size = chunk_size;
Packit a4aae4
		m_buffer_alloc();
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	// If the END chunk has zero bytes, return EOF. See above for more information
Packit a4aae4
	if (chunk_size == 0 && (header & CHUNK_TYPE_MASK) == CHUNK_END) return traits_type::eof();
Packit a4aae4
Packit a4aae4
	// Read the chunk's data
Packit a4aae4
	d_is.read(d_buffer, chunk_size);
Packit a4aae4
	DBG2(cerr << "read_next_chunk: size read: " << d_is.gcount() << ", eof: " << d_is.eof() << ", bad: " << d_is.bad() << endl);
Packit a4aae4
	if (d_is.bad()) return traits_type::eof();
Packit a4aae4
Packit a4aae4
	DBG2(cerr << "eback(): " << (void*)eback() << ", gptr(): " << (void*)(gptr()-eback()) << ", egptr(): " << (void*)(egptr()-eback()) << endl);
Packit a4aae4
	setg(d_buffer, 						// beginning of put back area
Packit a4aae4
			d_buffer,                	// read position (gptr() == eback())
Packit a4aae4
			d_buffer + chunk_size);  	// end of buffer (egptr()) chunk_size == d_is.gcount() unless there's an error
Packit a4aae4
Packit a4aae4
	DBG2(cerr << "eback(): " << (void*)eback() << ", gptr(): " << (void*)(gptr()-eback()) << ", egptr(): " << (void*)(egptr()-eback()) << endl);
Packit a4aae4
Packit a4aae4
	switch (header & CHUNK_TYPE_MASK) {
Packit a4aae4
	case CHUNK_END:
Packit a4aae4
		DBG(cerr << "Found end chunk" << endl);
Packit a4aae4
		return traits_type::not_eof(chunk_size);
Packit a4aae4
	case CHUNK_DATA:
Packit a4aae4
		return traits_type::not_eof(chunk_size);
Packit a4aae4
Packit a4aae4
	case CHUNK_ERR:
Packit a4aae4
		// this is pretty much the end of the show... Assume the buffer/chunk holds
Packit a4aae4
		// the error message text.
Packit a4aae4
		d_error = true;
Packit a4aae4
		d_error_message = string(d_buffer, chunk_size);
Packit a4aae4
		return traits_type::eof();
Packit a4aae4
	default:
Packit a4aae4
		d_error = true;
Packit a4aae4
		d_error_message = "Failed to read known chunk header type.";
Packit a4aae4
		return traits_type::eof();
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	return traits_type::eof();	// Can never get here; this quiets g++
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
}