mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-23 14:23:53 +00:00
Completed and refactored io_stream, split into hpp/cpp.
This commit is contained in:
parent
57c5b3b75d
commit
d41b3c017d
5 changed files with 296 additions and 154 deletions
221
stream/clients/io_stream.cpp
Normal file
221
stream/clients/io_stream.cpp
Normal 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;
|
||||||
|
}
|
|
@ -4,173 +4,39 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "../stream.hpp"
|
#include "../stream.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// This seems to work (TODO: test)
|
|
||||||
#ifndef EOF
|
|
||||||
#define EOF -1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Mangle {
|
namespace Mangle {
|
||||||
namespace Stream {
|
namespace Stream {
|
||||||
|
|
||||||
/** This file contains classes for wrapping an std::istream or
|
/** This file contains classes for wrapping an std::istream or
|
||||||
std::ostream around a Mangle::Stream. Not to be confused with
|
std::ostream around a Mangle::Stream.
|
||||||
servers/std_(o)stream.hpp, which do the opposite (wrap a
|
|
||||||
Mangle::Stream around an std::istream/ostream.)
|
|
||||||
|
|
||||||
This allows you to use Mangle streams in places that require std
|
This allows you to use Mangle streams in places that require std
|
||||||
streams. The std::iostream interface is horrible and NOT
|
streams.
|
||||||
designed for easy subclassing. Defining your custom streams as
|
|
||||||
Mangle streams and then wrapping them here will usually be much
|
This is much easier than trying to make your own custom streams
|
||||||
easier.
|
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
|
class MangleIStream : public std::istream
|
||||||
{
|
{
|
||||||
IOStreamBuffer buf;
|
std::streambuf *buf;
|
||||||
public:
|
public:
|
||||||
MangleIStream(StreamPtr inp)
|
MangleIStream(StreamPtr inp);
|
||||||
: std::istream(&buf)
|
~MangleIStream();
|
||||||
, buf(inp)
|
|
||||||
{
|
|
||||||
assert(inp->isReadable);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An ostream wrapping a writable Mangle::Stream.
|
||||||
class MangleOStream : public std::ostream
|
class MangleOStream : public std::ostream
|
||||||
{
|
{
|
||||||
IOStreamBuffer buf;
|
std::streambuf *buf;
|
||||||
public:
|
public:
|
||||||
MangleOStream(StreamPtr inp)
|
MangleOStream(StreamPtr inp);
|
||||||
: std::ostream(&buf)
|
~MangleOStream();
|
||||||
, buf(inp)
|
|
||||||
{
|
|
||||||
assert(inp->isWritable);
|
|
||||||
}
|
|
||||||
|
|
||||||
~MangleOStream() { flush(); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespaces
|
}} // namespaces
|
||||||
|
|
|
@ -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
|
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)
|
$(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE)
|
||||||
|
|
||||||
iostream_test: iostream_test.cpp ../clients/io_stream.hpp
|
iostream_test: iostream_test.cpp ../clients/io_stream.cpp
|
||||||
$(GCC) $< -o $@
|
$(GCC) $^ -o $@
|
||||||
|
|
||||||
file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp
|
file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp
|
||||||
$(GCC) $< -o $@
|
$(GCC) $< -o $@
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
|
|
||||||
void test2()
|
void test2()
|
||||||
{
|
{
|
||||||
cout << "Testing binary reading from non-memory:\n";
|
cout << "\nTesting binary reading from non-memory:\n";
|
||||||
|
|
||||||
StreamPtr input(new Dummy);
|
StreamPtr input(new Dummy);
|
||||||
MangleIStream inp(input);
|
MangleIStream inp(input);
|
||||||
|
@ -95,7 +95,7 @@ struct Dummy2 : Stream
|
||||||
|
|
||||||
void test3()
|
void test3()
|
||||||
{
|
{
|
||||||
cout << "Writing to dummy stream:\n";
|
cout << "\nWriting to dummy stream:\n";
|
||||||
|
|
||||||
cout << " Pure dummy test:\n";
|
cout << " Pure dummy test:\n";
|
||||||
StreamPtr output(new Dummy2);
|
StreamPtr output(new Dummy2);
|
||||||
|
@ -123,10 +123,54 @@ void test3()
|
||||||
out << "blah bleh blob";
|
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()
|
int main()
|
||||||
{
|
{
|
||||||
test1();
|
test1();
|
||||||
test2();
|
test2();
|
||||||
test3();
|
test3();
|
||||||
|
test4();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,10 @@ Got: hello
|
||||||
Got: you
|
Got: you
|
||||||
Got: world
|
Got: world
|
||||||
Got: you
|
Got: you
|
||||||
|
|
||||||
Testing binary reading from non-memory:
|
Testing binary reading from non-memory:
|
||||||
Done
|
Done
|
||||||
|
|
||||||
Writing to dummy stream:
|
Writing to dummy stream:
|
||||||
Pure dummy test:
|
Pure dummy test:
|
||||||
Got: t e s t i n g
|
Got: t e s t i n g
|
||||||
|
@ -19,3 +21,12 @@ Writing to dummy stream:
|
||||||
Got: z
|
Got: z
|
||||||
Writing some more and exiting:
|
Writing some more and exiting:
|
||||||
Got: b l a h b l e h b l o b
|
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
|
||||||
|
|
Loading…
Reference in a new issue