|
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 |
}
|