mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-22 21:53:50 +00:00
Created std::istream/ostream wrapper + test.
This commit is contained in:
parent
053074babb
commit
57c5b3b75d
6 changed files with 361 additions and 20 deletions
177
stream/clients/io_stream.hpp
Normal file
177
stream/clients/io_stream.hpp
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
#ifndef MANGLE_STREAM_IOSTREAM_H
|
||||||
|
#define MANGLE_STREAM_IOSTREAM_H
|
||||||
|
|
||||||
|
#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.)
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MangleIStream : public std::istream
|
||||||
|
{
|
||||||
|
IOStreamBuffer buf;
|
||||||
|
public:
|
||||||
|
MangleIStream(StreamPtr inp)
|
||||||
|
: std::istream(&buf)
|
||||||
|
, buf(inp)
|
||||||
|
{
|
||||||
|
assert(inp->isReadable);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MangleOStream : public std::ostream
|
||||||
|
{
|
||||||
|
IOStreamBuffer buf;
|
||||||
|
public:
|
||||||
|
MangleOStream(StreamPtr inp)
|
||||||
|
: std::ostream(&buf)
|
||||||
|
, buf(inp)
|
||||||
|
{
|
||||||
|
assert(inp->isWritable);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MangleOStream() { flush(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}} // namespaces
|
||||||
|
#endif
|
|
@ -25,11 +25,7 @@ class StdOStream : public Stream
|
||||||
hasPosition = true;
|
hasPosition = true;
|
||||||
hasSize = true;
|
hasSize = true;
|
||||||
isWritable = true;
|
isWritable = true;
|
||||||
}
|
isReadable = false;
|
||||||
|
|
||||||
size_t read(void*,size_t)
|
|
||||||
{
|
|
||||||
assert(0&&"reading not supported by StdOStream");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t write(const void* buf, size_t len)
|
size_t write(const void* buf, size_t len)
|
||||||
|
|
|
@ -24,28 +24,38 @@ class Stream
|
||||||
bool hasSize;
|
bool hasSize;
|
||||||
|
|
||||||
/// If true, write() works. Writing through pointer operations is
|
/// If true, write() works. Writing through pointer operations is
|
||||||
/// not supported.
|
/// not (yet) supported.
|
||||||
bool isWritable;
|
bool isWritable;
|
||||||
|
|
||||||
|
/// If true, read() and eof() works.
|
||||||
|
bool isReadable;
|
||||||
|
|
||||||
/// If true, the getPtr() functions work
|
/// If true, the getPtr() functions work
|
||||||
bool hasPtr;
|
bool hasPtr;
|
||||||
|
|
||||||
/// Initialize all bools to false by default
|
/// Initialize all bools to false by default, except isReadable.
|
||||||
Stream() :
|
Stream() :
|
||||||
isSeekable(false), hasPosition(false), hasSize(false),
|
isSeekable(false), hasPosition(false), hasSize(false),
|
||||||
isWritable(false), hasPtr(false) {}
|
isWritable(false), isReadable(true), hasPtr(false) {}
|
||||||
|
|
||||||
/// Virtual destructor
|
/// Virtual destructor
|
||||||
virtual ~Stream() {}
|
virtual ~Stream() {}
|
||||||
|
|
||||||
/** Read a given number of bytes from the stream. Returns the actual
|
/** Read a given number of bytes from the stream. Returns the actual
|
||||||
number read. If the return value is less than count, then the
|
number read. If the return value is less than count, then the
|
||||||
stream is empty or an error occured.
|
stream is empty or an error occured. Only required for readable
|
||||||
|
streams.
|
||||||
*/
|
*/
|
||||||
virtual size_t read(void* buf, size_t count) = 0;
|
virtual size_t read(void* buf, size_t count) { assert(0); return 0; }
|
||||||
|
|
||||||
/** Write a given number of bytes from the stream. Semantics is
|
/** Write a given number of bytes from the stream. Semantics is
|
||||||
similar to read(). Only valid if isWritable is true
|
similar to read(). Only valid if isWritable is true.
|
||||||
|
|
||||||
|
The returned value is the number of bytes written. However in
|
||||||
|
most cases, unlike for read(), a write-count less than requested
|
||||||
|
usually indicates an error. The implementation should throw such
|
||||||
|
errors as exceptions rather than expect the caller to handle
|
||||||
|
them.
|
||||||
|
|
||||||
Since most implementations do NOT support writing we default to
|
Since most implementations do NOT support writing we default to
|
||||||
an assert(0) here.
|
an assert(0) here.
|
||||||
|
@ -57,18 +67,20 @@ class Stream
|
||||||
|
|
||||||
/// Seek to an absolute position in this stream. Not all streams are
|
/// Seek to an absolute position in this stream. Not all streams are
|
||||||
/// seekable.
|
/// seekable.
|
||||||
virtual void seek(size_t pos) = 0;
|
virtual void seek(size_t pos) { assert(0); }
|
||||||
|
|
||||||
/// Get the current position in the stream. Non-seekable streams are
|
/// Get the current position in the stream. Non-seekable streams are
|
||||||
/// not required to keep track of this.
|
/// not required to keep track of this.
|
||||||
virtual size_t tell() const = 0;
|
virtual size_t tell() const { assert(0); return 0; }
|
||||||
|
|
||||||
/// Return the total size of the stream. For streams where this is
|
/// Return the total size of the stream. For streams hasSize is
|
||||||
/// not applicable, size() should return zero.
|
/// false, size() should fail in some way, since it is an error to
|
||||||
virtual size_t size() const = 0;
|
/// call it in those cases.
|
||||||
|
virtual size_t size() const { assert(0); return 0; }
|
||||||
|
|
||||||
/// Returns true if the stream is empty
|
/// Returns true if the stream is empty. Required for readable
|
||||||
virtual bool eof() const = 0;
|
/// streams.
|
||||||
|
virtual bool eof() const { assert(0); return 0; }
|
||||||
|
|
||||||
/// Return a pointer to the entire stream. This function (and the
|
/// Return a pointer to the entire stream. This function (and the
|
||||||
/// other getPtr() variants below) should only be implemented for
|
/// other getPtr() variants below) should only be implemented for
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
GCC=g++ -I../ -Wall
|
GCC=g++ -I../ -Wall -Werror
|
||||||
|
|
||||||
all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test
|
all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test iostream_test
|
||||||
|
|
||||||
I_OGRE=$(shell pkg-config --cflags OGRE)
|
I_OGRE=$(shell pkg-config --cflags OGRE)
|
||||||
L_OGRE=$(shell pkg-config --libs OGRE)
|
L_OGRE=$(shell pkg-config --libs OGRE)
|
||||||
|
@ -12,6 +12,9 @@ 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
|
||||||
|
$(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 $@
|
||||||
|
|
||||||
|
|
132
stream/tests/iostream_test.cpp
Normal file
132
stream/tests/iostream_test.cpp
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "../clients/io_stream.hpp"
|
||||||
|
#include "../servers/memory_stream.hpp"
|
||||||
|
|
||||||
|
using namespace Mangle::Stream;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void test1()
|
||||||
|
{
|
||||||
|
cout << "Testing ASCII reading from memory:\n";
|
||||||
|
StreamPtr input(new MemoryStream("hello you world you", 19));
|
||||||
|
MangleIStream inp(input);
|
||||||
|
|
||||||
|
string str;
|
||||||
|
while(!inp.eof())
|
||||||
|
{
|
||||||
|
inp >> str;
|
||||||
|
cout << "Got: " << str << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dummy : public Stream
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Dummy() : count(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read(void *ptr, size_t num)
|
||||||
|
{
|
||||||
|
char *p = (char*)ptr;
|
||||||
|
char *start = p;
|
||||||
|
for(; (count < 2560) && (p-start < (int)num); count++)
|
||||||
|
{
|
||||||
|
*p = count / 10;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return p-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eof() const { return count == 2560; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void test2()
|
||||||
|
{
|
||||||
|
cout << "Testing binary reading from non-memory:\n";
|
||||||
|
|
||||||
|
StreamPtr input(new Dummy);
|
||||||
|
MangleIStream inp(input);
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
while(!inp.eof())
|
||||||
|
{
|
||||||
|
unsigned char buf[5];
|
||||||
|
inp.read((char*)buf,5);
|
||||||
|
|
||||||
|
// istream doesn't set eof() until we read _beyond_ the end of
|
||||||
|
// the stream, so we need an extra check.
|
||||||
|
if(inp.gcount() == 0) break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(int i=0;i<5;i++)
|
||||||
|
cout << (int)buf[i] << " ";
|
||||||
|
cout << endl;
|
||||||
|
*/
|
||||||
|
|
||||||
|
assert(buf[4] == buf[0]);
|
||||||
|
assert(buf[0] == x/2);
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
cout << " Done\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dummy2 : Stream
|
||||||
|
{
|
||||||
|
Dummy2()
|
||||||
|
{
|
||||||
|
isWritable = true;
|
||||||
|
isReadable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write(const void *ptr, size_t num)
|
||||||
|
{
|
||||||
|
const char *p = (const char*)ptr;
|
||||||
|
cout << " Got: ";
|
||||||
|
for(unsigned i=0;i<num;i++)
|
||||||
|
cout << *(p++) << " ";
|
||||||
|
cout << endl;
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void test3()
|
||||||
|
{
|
||||||
|
cout << "Writing to dummy stream:\n";
|
||||||
|
|
||||||
|
cout << " Pure dummy test:\n";
|
||||||
|
StreamPtr output(new Dummy2);
|
||||||
|
output->write("testing", 7);
|
||||||
|
|
||||||
|
cout << " Running through MangleOStream:\n";
|
||||||
|
MangleOStream out(output);
|
||||||
|
out << "hello";
|
||||||
|
out << " - are you ok?";
|
||||||
|
cout << " Flushing:\n";
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
cout << " Writing a hell of a lot of characters:\n";
|
||||||
|
for(int i=0; i<127; i++)
|
||||||
|
out << "xxxxxxxx"; // 127 * 8 = 1016
|
||||||
|
out << "fffffff"; // +7 = 1023
|
||||||
|
cout << " Just one more:\n";
|
||||||
|
out << "y";
|
||||||
|
cout << " And oooone more:\n";
|
||||||
|
out << "z";
|
||||||
|
|
||||||
|
cout << " Flushing again:\n";
|
||||||
|
out.flush();
|
||||||
|
cout << " Writing some more and exiting:\n";
|
||||||
|
out << "blah bleh blob";
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
return 0;
|
||||||
|
}
|
21
stream/tests/output/iostream_test.out
Normal file
21
stream/tests/output/iostream_test.out
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
Testing ASCII reading from memory:
|
||||||
|
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
|
||||||
|
Running through MangleOStream:
|
||||||
|
Flushing:
|
||||||
|
Got: h e l l o - a r e y o u o k ?
|
||||||
|
Writing a hell of a lot of characters:
|
||||||
|
Just one more:
|
||||||
|
And oooone more:
|
||||||
|
Got: x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x f f f f f f f y
|
||||||
|
Flushing again:
|
||||||
|
Got: z
|
||||||
|
Writing some more and exiting:
|
||||||
|
Got: b l a h b l e h b l o b
|
Loading…
Reference in a new issue