mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-22 19:53:52 +00:00
Merge branch 'master' of git://github.com/korslund/mangle
This commit is contained in:
commit
b08c2f78dc
7 changed files with 503 additions and 20 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;
|
||||
}
|
43
stream/clients/io_stream.hpp
Normal file
43
stream/clients/io_stream.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef MANGLE_STREAM_IOSTREAM_H
|
||||
#define MANGLE_STREAM_IOSTREAM_H
|
||||
|
||||
#include <assert.h>
|
||||
#include "../stream.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Stream {
|
||||
|
||||
/** This file contains classes for wrapping an std::istream or
|
||||
std::ostream around a Mangle::Stream.
|
||||
|
||||
This allows you to use Mangle streams in places that require std
|
||||
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.
|
||||
*/
|
||||
|
||||
// An istream wrapping a readable Mangle::Stream. Has extra
|
||||
// optimizations for pointer-based streams.
|
||||
class MangleIStream : public std::istream
|
||||
{
|
||||
std::streambuf *buf;
|
||||
public:
|
||||
MangleIStream(StreamPtr inp);
|
||||
~MangleIStream();
|
||||
};
|
||||
|
||||
// An ostream wrapping a writable Mangle::Stream.
|
||||
class MangleOStream : public std::ostream
|
||||
{
|
||||
std::streambuf *buf;
|
||||
public:
|
||||
MangleOStream(StreamPtr inp);
|
||||
~MangleOStream();
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.cpp
|
||||
$(GCC) $^ -o $@
|
||||
|
||||
file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp
|
||||
$(GCC) $< -o $@
|
||||
|
||||
|
|
176
stream/tests/iostream_test.cpp
Normal file
176
stream/tests/iostream_test.cpp
Normal file
|
@ -0,0 +1,176 @@
|
|||
#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 << "\nTesting 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 << "\nWriting 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";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
32
stream/tests/output/iostream_test.out
Normal file
32
stream/tests/output/iostream_test.out
Normal file
|
@ -0,0 +1,32 @@
|
|||
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
|
||||
|
||||
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