1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-23 09:23:52 +00:00

Completed and refactored io_stream, split into hpp/cpp.

This commit is contained in:
Nicolay Korslund 2010-09-07 13:12:21 +02:00
parent 57c5b3b75d
commit d41b3c017d
5 changed files with 296 additions and 154 deletions

View file

@ -0,0 +1,221 @@
#include "io_stream.hpp"
// This seems to work
#ifndef EOF
#define EOF -1
#endif
using namespace Mangle::Stream;
#define BSIZE 1024
// Streambuf for normal stream reading
class _istreambuf : public std::streambuf
{
StreamPtr client;
char buf[BSIZE];
public:
_istreambuf(StreamPtr strm) : client(strm)
{
// Make sure we picked the right class
assert(client->isReadable);
assert(!client->hasPtr);
// Tell streambuf to delegate reading operations to underflow()
setg(NULL,NULL,NULL);
// Disallow writing
setp(NULL,NULL);
}
/* Underflow is called when there is no more info to read in the
input buffer. We need to refill buf with new data (if any), and
set up the internal pointers with setg() to reflect the new
state.
*/
int underflow()
{
// Read some more data
size_t read = client->read(buf, BSIZE);
assert(read <= BSIZE);
// If we're out of data, then EOF
if(read == 0)
return EOF;
// Otherwise, set up input buffer
setg(buf, buf, buf+read);
// Return the first char
return *((unsigned char*)buf);
}
// Seek stream, if the source supports it. Ignores the second
// parameter as Mangle doesn't separate input and output pointers.
std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in)
{
// Does this stream know how to seek?
if(!client->isSeekable || !client->hasPosition)
// If not, signal an error.
return -1;
// Set stream position and reset the buffer.
client->seek(pos);
setg(NULL,NULL,NULL);
return client->tell();
}
};
// Streambuf optimized for pointer-based input streams
class _ptrstreambuf : public std::streambuf
{
StreamPtr client;
public:
_ptrstreambuf(StreamPtr strm) : client(strm)
{
// Make sure we picked the right class
assert(client->isReadable);
assert(client->hasPtr);
// seekpos() does all the work
seekpos(0);
}
// Underflow is only called when we're at the end of the file
int underflow() { return EOF; }
// Seek to a new position within the memory stream. This bypasses
// client->seek() entirely so isSeekable doesn't have to be set.
std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in)
{
// All pointer streams need a size
assert(client->hasSize);
// Figure out how much will be left of the stream after seeking
size_t size = client->size() - pos;
// Get a pointer
char* ptr = (char*)client->getPtr(pos,size);
// And use it
setg(ptr,ptr,ptr+size);
return pos;
}
};
// Streambuf for stream writing
class _ostreambuf : public std::streambuf
{
StreamPtr client;
char buf[BSIZE];
public:
_ostreambuf(StreamPtr strm) : client(strm)
{
// Make sure we picked the right class
assert(client->isWritable);
// Inform streambuf about our nice buffer
setp(buf, buf+BSIZE);
// Disallow reading
setg(NULL,NULL,NULL);
}
/* Sync means to flush (write) all current data to the output
stream. It will also set up the entire output buffer to be usable
again.
*/
int sync()
{
// Get the number of bytes that streambuf wants us to write
int num = pptr() - pbase();
assert(num >= 0);
// Is there any work to do?
if(num == 0) return 0;
if((int)client->write(pbase(), num) != num)
// Inform caller that writing failed
return -1;
// Reset output buffer pointers
setp(buf, buf+BSIZE);
// No error
return 0;
}
/* Called whenever the output buffer is full.
*/
int overflow(int c)
{
// First, write all existing data
if(sync()) return EOF;
// Put the requested character in the next round of output
if(c != EOF)
{
*pptr() = c;
pbump(1);
}
// No error
return 0;
}
// Seek stream, if the source supports it.
std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::out)
{
if(!client->isSeekable || !client->hasPosition)
return -1;
// Flush data and reset buffers
sync();
// Set stream position
client->seek(pos);
return client->tell();
}
};
MangleIStream::MangleIStream(StreamPtr inp)
: std::istream(NULL)
{
assert(inp->isReadable);
// Pick the right streambuf implementation based on whether the
// input supports pointers or not.
if(inp->hasPtr)
buf = new _ptrstreambuf(inp);
else
buf = new _istreambuf(inp);
rdbuf(buf);
}
MangleIStream::~MangleIStream()
{
delete buf;
}
MangleOStream::MangleOStream(StreamPtr out)
: std::ostream(NULL)
{
assert(out->isWritable);
buf = new _ostreambuf(out);
rdbuf(buf);
}
MangleOStream::~MangleOStream()
{
// Make sure we don't have lingering data on exit
flush();
delete buf;
}

View file

@ -4,173 +4,39 @@
#include <assert.h>
#include "../stream.hpp"
#include <iostream>
#include <vector>
// This seems to work (TODO: test)
#ifndef EOF
#define EOF -1
#endif
namespace Mangle {
namespace Stream {
/** This file contains classes for wrapping an std::istream or
std::ostream around a Mangle::Stream. Not to be confused with
servers/std_(o)stream.hpp, which do the opposite (wrap a
Mangle::Stream around an std::istream/ostream.)
std::ostream around a Mangle::Stream.
This allows you to use Mangle streams in places that require std
streams. The std::iostream interface is horrible and NOT
designed for easy subclassing. Defining your custom streams as
Mangle streams and then wrapping them here will usually be much
easier.
streams.
This is much easier than trying to make your own custom streams
into iostreams. The std::iostream interface is horrible and NOT
designed for easy subclassing. Create a Mangle::Stream instead,
and use this wrapper.
*/
class IOStreamBuffer : public std::streambuf
{
StreamPtr client;
std::vector<char> ibuf, obuf;
public:
IOStreamBuffer(StreamPtr strm) : client(strm)
{
// Set up input buffer
setg(NULL,NULL,NULL);
if(client->isReadable)
{
if(client->hasPtr)
{
assert(client->hasSize);
// If the client supports direct pointer reading, then
// this is really easy. No internal buffer is needed.
char *ptr = (char*) client->getPtr();
// Set up the entire file as the input buffer
setg(ptr,ptr,ptr+client->size());
}
else
{
// We need to do some manual slave labor. Set up an
// empty input buffer and let underflow() handle it.
ibuf.resize(1024);
}
}
// Only create output buffer if the stream is writable
if(client->isWritable)
{
obuf.resize(1024);
/* Set beginning and end pointers, tells streambuf to write
to this area and call overflow() when it's full.
Several examples use size-1, but the documentation for
streambuf clearly states that the end pointers is just
_past_ the last accessible position.
*/
char *beg = &obuf[0];
setp(beg, beg+obuf.size());
}
else
// Writing not permitted
setp(NULL, NULL);
}
/* Underflow is called when there is no more info to read in the
input buffer. We need to refill ibuf with data (if any), and
set up the internal pointers with setg() to reflect the new
state.
*/
int underflow()
{
assert(client->isReadable);
// If we've exhausted a pointer stream, then there's no more to
// be had.
if(client->hasPtr)
return EOF;
// Read some more data
assert(ibuf.size());
char *iptr = &ibuf[0];
size_t read = client->read(iptr, ibuf.size());
// If we're out of data, then EOF
if(read == 0)
return EOF;
// Otherwise, set up input buffer
setg(iptr, iptr, iptr+read);
// Return the first char
return *((unsigned char*)iptr);
}
/* Sync means to flush (write) all current data to the output
stream. It will also set up the entire output buffer to be
usable again.
*/
int sync()
{
assert(client->isWritable);
assert(obuf.size() > 0);
// Get the number of bytes that streambuf wants us to write
int num = pptr() - pbase();
assert(num >= 0);
// Nothing to do
if(num == 0) return 0;
if((int)client->write(pbase(), num) != num)
return -1;
// Reset output buffer pointers
char *beg = &obuf[0];
setp(beg, beg+obuf.size());
return 0;
}
int overflow(int c=EOF)
{
// First, write all existing data
if(sync()) return EOF;
// Put the requested character in the output
if(c != EOF)
{
*pptr() = c;
pbump(1);
}
return 0;
}
};
// An istream wrapping a readable Mangle::Stream. Has extra
// optimizations for pointer-based streams.
class MangleIStream : public std::istream
{
IOStreamBuffer buf;
std::streambuf *buf;
public:
MangleIStream(StreamPtr inp)
: std::istream(&buf)
, buf(inp)
{
assert(inp->isReadable);
}
MangleIStream(StreamPtr inp);
~MangleIStream();
};
// An ostream wrapping a writable Mangle::Stream.
class MangleOStream : public std::ostream
{
IOStreamBuffer buf;
std::streambuf *buf;
public:
MangleOStream(StreamPtr inp)
: std::ostream(&buf)
, buf(inp)
{
assert(inp->isWritable);
}
~MangleOStream() { flush(); }
MangleOStream(StreamPtr inp);
~MangleOStream();
};
}} // namespaces

View file

@ -12,8 +12,8 @@ ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream.
audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp
$(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE)
iostream_test: iostream_test.cpp ../clients/io_stream.hpp
$(GCC) $< -o $@
iostream_test: iostream_test.cpp ../clients/io_stream.cpp
$(GCC) $^ -o $@
file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp
$(GCC) $< -o $@

View file

@ -46,7 +46,7 @@ public:
void test2()
{
cout << "Testing binary reading from non-memory:\n";
cout << "\nTesting binary reading from non-memory:\n";
StreamPtr input(new Dummy);
MangleIStream inp(input);
@ -95,7 +95,7 @@ struct Dummy2 : Stream
void test3()
{
cout << "Writing to dummy stream:\n";
cout << "\nWriting to dummy stream:\n";
cout << " Pure dummy test:\n";
StreamPtr output(new Dummy2);
@ -123,10 +123,54 @@ void test3()
out << "blah bleh blob";
}
struct Dummy3 : Stream
{
int pos;
Dummy3() : pos(0)
{
hasPosition = true;
isSeekable = true;
}
size_t read(void*, size_t num)
{
cout << " Reading " << num << " bytes from " << pos << endl;
pos += num;
return num;
}
void seek(size_t npos) { pos = npos; }
size_t tell() const { return pos; }
};
void test4()
{
cout << "\nTesting seeking;\n";
StreamPtr input(new Dummy3);
cout << " Direct reading:\n";
input->read(0,10);
input->read(0,5);
MangleIStream inp(input);
cout << " Reading from istream:\n";
char buf[20];
inp.read(buf, 20);
inp.read(buf, 20);
inp.read(buf, 20);
cout << " Seeking to 30 and reading again:\n";
inp.seekg(30);
inp.read(buf, 20);
}
int main()
{
test1();
test2();
test3();
test4();
return 0;
}

View file

@ -3,8 +3,10 @@ Got: hello
Got: you
Got: world
Got: you
Testing binary reading from non-memory:
Done
Writing to dummy stream:
Pure dummy test:
Got: t e s t i n g
@ -19,3 +21,12 @@ Writing to dummy stream:
Got: z
Writing some more and exiting:
Got: b l a h b l e h b l o b
Testing seeking;
Direct reading:
Reading 10 bytes from 0
Reading 5 bytes from 10
Reading from istream:
Reading 1024 bytes from 15
Seeking to 30 and reading again:
Reading 1024 bytes from 30