1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-22 18:53:50 +00:00

Created std::istream/ostream wrapper + test.

This commit is contained in:
Nicolay Korslund 2010-09-07 11:08:09 +02:00
parent 053074babb
commit 57c5b3b75d
6 changed files with 361 additions and 20 deletions

View 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

View file

@ -25,11 +25,7 @@ class StdOStream : public Stream
hasPosition = true;
hasSize = true;
isWritable = true;
}
size_t read(void*,size_t)
{
assert(0&&"reading not supported by StdOStream");
isReadable = false;
}
size_t write(const void* buf, size_t len)

View file

@ -24,28 +24,38 @@ class Stream
bool hasSize;
/// If true, write() works. Writing through pointer operations is
/// not supported.
/// not (yet) supported.
bool isWritable;
/// If true, read() and eof() works.
bool isReadable;
/// If true, the getPtr() functions work
bool hasPtr;
/// Initialize all bools to false by default
/// Initialize all bools to false by default, except isReadable.
Stream() :
isSeekable(false), hasPosition(false), hasSize(false),
isWritable(false), hasPtr(false) {}
isWritable(false), isReadable(true), hasPtr(false) {}
/// Virtual destructor
virtual ~Stream() {}
/** Read a given number of bytes from the stream. Returns the actual
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
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
an assert(0) here.
@ -57,18 +67,20 @@ class Stream
/// Seek to an absolute position in this stream. Not all streams are
/// 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
/// 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
/// not applicable, size() should return zero.
virtual size_t size() const = 0;
/// Return the total size of the stream. For streams hasSize is
/// false, size() should fail in some way, since it is an error to
/// call it in those cases.
virtual size_t size() const { assert(0); return 0; }
/// Returns true if the stream is empty
virtual bool eof() const = 0;
/// Returns true if the stream is empty. Required for readable
/// streams.
virtual bool eof() const { assert(0); return 0; }
/// Return a pointer to the entire stream. This function (and the
/// other getPtr() variants below) should only be implemented for

View file

@ -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)
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
$(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
$(GCC) $< -o $@

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

View 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