digitalmars.D - My stream concept
- Regan Heath <regan netwin.co.nz> Aug 02 2004
- "Matthew" <admin stlsoft.dot.dot.dot.dot.org> Aug 02 2004
- parabolis <parabolis softhome.net> Aug 02 2004
- Regan Heath <regan netwin.co.nz> Aug 02 2004
- parabolis <parabolis softhome.net> Aug 02 2004
- Regan Heath <regan netwin.co.nz> Aug 02 2004
- Ben Hinkle <bhinkle4 juno.com> Aug 02 2004
- Regan Heath <regan netwin.co.nz> Aug 02 2004
- "Vathix" <vathixSpamFix dprogramming.com> Aug 02 2004
- Regan Heath <regan netwin.co.nz> Aug 02 2004
- Sean Kelly <sean f4.ca> Aug 02 2004
------------HThpJ5CpXXRdSLQVK4P5jO
Content-Type: text/plain; format=flowed; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
Typical stream implementations I have seen use the following heirarchy:
class Stream {
}
class FileStream : Stream {
}
class SocketStream : Stream {
}
in other words you have to design/derive a class from Stream for each type
of Stream you want to implement.
I always thought this was kinda backwards, and limited, in fact after
Arcane Jill said something along the lines of "anything that reads and
writes bytes _is_ a stream" I got to thinking surely I can use templates
to make that statement true.
The way I have done it is to define some interfaces:
interface InputStream
{
ulong read(void* buffer, ulong length);
ulong available();
bool eof();
}
interface OutputStream
{
ulong write(void* buffer, ulong length);
}
and a Stream and BufferedStream template class. Any object that supports
those interfaces (as does my RawFile class) can then be extended by the
two template classes to give full stream functionality by some simple
aliases, example:
alias Stream!(RawFile) FileStream;
alias BufferedStream!(FileStream,2048) File;
I have attached the code, it is far from complete and probably buggy as
heck as I have not done any testing (or unittests - I know I'm bad) this
was simply proof of concept.
Any and all comments are welcome/desired.
Regan
--
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
------------HThpJ5CpXXRdSLQVK4P5jO
Content-Disposition: attachment; filename=syserror.d
Content-Type: application/octet-stream; name=syserror.d
Content-Transfer-Encoding: 8bit
module lib.syserror;
private import std.string;
private import std.c.windows.windows;
private import std.c.stdarg;
extern (Windows) {
DWORD FormatMessageA(
DWORD dwFlags,
LPCVOID lpSource,
DWORD dwMessageId,
DWORD dwLanguageId,
LPTSTR lpBuffer,
DWORD nSize,
va_list *Arguments
);
static uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
static uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
static uint FORMAT_MESSAGE_FROM_STRING = 0x00000400;
static uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
static uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
static uint FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
static uint FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
WORD MAKELANGID(WORD p, WORD s) { return (((cast(WORD)s) << 10) |
cast(WORD)p); }
WORD PRIMARYLANGID(WORD lgid) { return (cast(WORD)lgid & 0x3ff); }
WORD SUBLANGID(WORD lgid) { return (cast(WORD)lgid >> 10); }
static uint LANG_NEUTRAL = 0x00;
static uint SUBLANG_DEFAULT = 0x01;
alias HGLOBAL HLOCAL;
HLOCAL LocalFree(HLOCAL hMem);
}
extern (C) char *strerror(int);
class SysError
{
static char[] msg(uint errcode)
{
char[] text;
version(Windows)
{
LPVOID lpMsgBuf;
DWORD r;
r = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
null,
errcode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
cast(LPTSTR)&lpMsgBuf,
0,
null
);
/* Remove \r\n from error string */
if (r >= 2) r-= 2;
text = (cast(char *)lpMsgBuf)[0..r];
LocalFree(cast(HLOCAL)lpMsgBuf);
}
else
{
char* pemsg;
uint r;
pemsg = strerror(errcode);
r = strlen(pemsg);
/* Remove \r\n from error string */
if (pemsg[r-1] == '\n') r--;
if (pemsg[r-1] == '\r') r--;
text = pemsg[0..r];
}
return text;
}
}
------------HThpJ5CpXXRdSLQVK4P5jO
Content-Disposition: attachment; filename=file.d
Content-Type: application/octet-stream; name=file.d
Content-Transfer-Encoding: 8bit
module lib.file;
import std.c.windows.windows;
import std.file;
import lib.stream;
import lib.syserror;
extern (Windows)
{
export {
BOOL PeekNamedPipe(
HANDLE hNamedPipe,
LPVOID lpBuffer,
DWORD nBufferSize,
LPDWORD lpBytesRead,
LPDWORD lpTotalBytesAvail,
LPDWORD lpBytesLeftThisMessage
);
}
}
enum FileMode {
NONE = 0x00,
READ = 0x01,
CREATE = 0x02,
APPEND = 0x04,
WRITE = 0x08,
NEW = 0x10
}
private enum FileFlag {
NONE = 0x0,
EOF = 0x1
}
class FileException : Exception
{
char[] message;
uint code;
this(char[] _message, DWORD _code)
{
super(SysError.msg(_code));
message = _message;
code = _code;
}
char[] toString()
{
return message ~ ", " ~ super.toString();
}
void print()
{
printf("%.*s\n",toString());
}
}
class RawFile : InputStream, OutputStream, Seekable
{
this(char[] file = null, FileMode mode = FileMode.NONE)
{
open(file,mode);
}
~this()
{
close();
}
void open(char[] file = null, FileMode mode = FileMode.NONE)
{
DWORD access,flags,share;
if (file is null) return ;
close();
if (mode & FileMode.READ) { access |= GENERIC_READ; flags |= OPEN_EXISTING; }
if (mode & FileMode.CREATE) { access |= GENERIC_WRITE; flags |=
CREATE_ALWAYS; }
if (mode & FileMode.APPEND) { access |= GENERIC_WRITE; flags |= OPEN_ALWAYS; }
if (mode & FileMode.WRITE) { access |= GENERIC_WRITE; }
if (mode & FileMode.NEW) { access |= GENERIC_WRITE; flags |= CREATE_NEW; }
h = CreateFileA(
std.file.toMBSz(file)
,access /* GENERIC_READ|GENERIC_WRITE */
,share /* FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE */
,null /* SECURITY_ATRIBUTES */
,flags /* CREATE_NEW|CREATE_ALWAYS|OPEN_EXISTING|OPEN_ALWA
S|TRUNCATE_EXISTING */
,0 /* FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|
FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|
FILE_ATTRIBUTE_TEMPORARY *//*
FILE_FLAG_WRITE_THROUGH|FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING|FILE_FLAG_RANDOM_ACCESS|
FILE_FLAG_SEQUENTIAL_SCAN|FILE_FLAG_DELETE_ON_CLOSE|FILE_FLAG_BACKUP_SEMANTICS
FILE_FLAG_POSIX_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE
FLAG_OPEN_NO_RECALL */
/* SECURITY_ANONYMOUS|SECURITY_IDENTIFICATION|SECURITY_IMPERSONATION|SECURITY_DELEGATION
SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY */
,null); /*handle: copy attributes of this file*/
if (h == INVALID_HANDLE_VALUE)
throw new FileException("Failed to open ("~file~")",GetLastError());
return ;
}
void close()
{
if (h == INVALID_HANDLE_VALUE) return ;
CloseHandle(h);
}
ulong read(void* buffer, ulong length)
{
DWORD bytes;
if (ReadFile(h,buffer,length,&bytes,null) == 0)
throw new FileException("read failed",GetLastError());
if (bytes != length)
flags |= FileFlag.EOF;
return bytes;
}
ulong available()
{
DWORD szLow,szHigh;
LONG cLow,cHigh;
ulong size,cur;
cLow = SetFilePointer(h,0,&cHigh,cast(LONG)SeekPos.CURRENT);
if (cLow == 0xFFFFFFFF && GetLastError() != 0)
throw new FileException("available seek failed",GetLastError());
szLow = GetFileSize(h,&szHigh);
if (szLow == 0xFFFFFFFF && GetLastError() != 0)
throw new FileException("available getsize failed",GetLastError());
size = szLow|(szHigh<<32);
cur = cLow|(cHigh<32);
if (cur > size)
return 0;
return size-cur;
}
bool eof()
{
return (flags & FileFlag.EOF) != 0;
}
ulong write(void *buffer, ulong length)
{
DWORD bytes;
if (WriteFile(h,buffer,length,&bytes,null) == 0)
throw new FileException("write failed",GetLastError());
return bytes;
}
ulong seek(long bytes, SeekPos origin)
{
LONG dLow,dHigh;
splitLong(bytes,dLow,dHigh);
dLow = SetFilePointer(h,dLow,&dHigh,origin);
if (dLow == 0xFFFFFFFF && GetLastError() != 0)
throw new FileException("seek failed",GetLastError());
return combineInt(dLow,dHigh);
}
ulong seekBack(long bytes)
{
return seek(-bytes,SeekPos.CURRENT);
}
ulong seekFwd(long bytes)
{
return seek(bytes,SeekPos.CURRENT);
}
ulong seekStart()
{
return seek(0,SeekPos.BEGIN);
}
ulong seekEnd()
{
return seek(0,SeekPos.END);
}
private:
HANDLE h = INVALID_HANDLE_VALUE;
FileFlag flags;
ulong combineInt(LONG low, LONG high)
{
return low|(high<<32);
}
void splitLong(ulong x, LONG low, LONG high)
{
low = (x&0xFFFFFFFF);
high = (x>>32);
}
}
alias Stream!(RawFile) FileStream;
alias BufferedStream!(FileStream,2048) File;
/*
"r" - READ - read, fails if file does not exist.
"w" - CREATE - write, overwrite existing.
"a" - APPEND - write, create if not exist.
"r+" - READ|WRITE - read, write, fails if file does not exist.
"w+" - READ|CREATE - read, write, overwrite existing.
"a+" - READ|APPEND - read, append, create if not exist.
"" - WRITE|NEW - write, fail if file exist.
*/
/*
WUC - write, update, create
RWUC - read, write, update, create
WAUC "a" - write, append, update, create
RWAUC "a+" - read, write, append, update, create
WOC "w" - write, overwrite, create
RWOC "w+" - read, write, overwrite, create
RC - read, create
RWC - read, write, create
RU "r" - read, existing
WU - write, existing
RWU "r+" - read, write, existing
RAU - read, append, existing
WAU - write, append, existing
RWAU - read, write, append, existing
WO - write, overwrite
RWO - read, write, overwrite
*/
/*
The open() routine seems to controls four aspects:
Access: Read, Write, Both, Neither
File-Exists action: Use, Overwrite, Fail
File-Not-Exists action: Create, Fail
Initial Seek: Start-of-File (normal), End-of-File (append)
This leads to 48 different combinations, some of which are not useful (eg.
neither read nor write, fails if exists and if not-exists, read starting
from end of file, etc...). So after removing the useless combinations, we
are left with 20 possible ones.
I've devised six codes that encompass these combinations:
R : read access from start of file
W : write access from start of file
A : write access from end of file
U : use existing file
O : overwrite existing file
C : create non-existing file
So the useful combinations that either open a file or fail are (I've also
show Regan's equivalent codes) ...
RWUC - read, write, use existing, create if not existing.
RAUC "a+" - read, append (to existing), create if not exist.
RWOC "w+" - read, write, overwrite existing(, create if not existing).
RWU "r+" - read, write, fails if file does not exist.
RAU - read, append to existing, fail if not exist.
RWC - read, write, fail if exists, create if not existing.
RWO - read, write, overwrite if exists, fail if not existing.
RAO - read, append if exists, fail if not existing.
WUC - write, use if exists, create if not existing.
AUC "a" - write(, append if exists), create if not exist.
WOC "w" - write, overwrite existing(, create if not existing).
RUC - read, use if exists, create if not existing.
ROC - read, overwrite if exists, create if not existing. (Used to
always create an empty file).
RU "r" - read, fails if file does not exist.
RC - read, fail if exists, create if not existing. (Used to create
an empty file if it doesn't already exist).
RO - read, overwrite if exists, fail if not existing. (Used to wipe
an existing file).
WU - write, use if file exists, fail if not existing.
AU - write, append if file exists, fail if not existing.
WC "" - write, fail if file exists(, create if not existing).
WO - write, overwrite if exists, fail if not existing.(Used to
replace an existing file).
*/
------------HThpJ5CpXXRdSLQVK4P5jO
Content-Disposition: attachment; filename=stream.d
Content-Type: application/octet-stream; name=stream.d
Content-Transfer-Encoding: 8bit
module lib.stream;
version (Windows) {
static char[] EOL = "\r\n";
}
else {
static char[] EOL = "\n";
}
interface InputStream
{
ulong read(void* buffer, ulong length);
ulong available();
bool eof();
}
interface OutputStream
{
ulong write(void* buffer, ulong length);
}
enum SeekPos
{
BEGIN,
CURRENT,
END
}
interface Seekable
{
ulong seek(long bytes, SeekPos origin);
ulong seekBack(long bytes);
ulong seekFwd(long bytes);
ulong seekStart();
ulong seekEnd();
}
class Stream(alias T) : T
{
this()
{
}
alias T.read read;
bool read(out byte x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out ubyte x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out short x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out ushort x) { if (super.read(&x,x.sizeof) == x.sizeof) return
true; return false; }
bool read(out int x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out uint x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out long x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out ulong x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out float x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out double x) { if (super.read(&x,x.sizeof) == x.sizeof) return
true; return false; }
bool read(out real x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out ireal x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out creal x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out char x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out wchar x) { if (super.read(&x,x.sizeof) == x.sizeof) return true;
return false; }
bool read(out char[] x) {
uint len;
if (!this.read(len))
return false;
x.length = len;
if (!super.read(x,len*char.sizeof))
return false;
return true;
}
bool read(out wchar[] x) {
uint len;
if (!this.read(len))
return false;
x.length = len;
if (!super.read(x,len*wchar.sizeof))
return false;
return true;
}
bool read(out dchar[] x) {
uint len;
if (!this.read(len))
return false;
x.length = len;
if (!super.read(x,len*dchar.sizeof))
return false;
return true;
}
bool readLine(inout char[] line)
{
uint i = 0;
if (super.eof())
return false;
if (line.length == 0)
line.length = 128;
while(true) {
foreach(inout char c; line[i..line.length]) {
if (!this.read(c)) break;
i++;
if (c == '\r') this.read(c);
if (c == '\n') break;
}
if (i == 0) return false;
if (i != line.length) {
if (line[i-1] == '\n') {
line.length = i-1;
break;
}
}
line.length = line.length*2;
}
return true;
}
alias T.write write;
void write(byte x) { super.write(&x,x.sizeof); }
void write(ubyte x) { super.write(&x,x.sizeof); }
void write(short x) { super.write(&x,x.sizeof); }
void write(ushort x) { super.write(&x,x.sizeof); }
void write(int x) { super.write(&x,x.sizeof); }
void write(uint x) { super.write(&x,x.sizeof); }
void write(long x) { super.write(&x,x.sizeof); }
void write(ulong x) { super.write(&x,x.sizeof); }
void write(float x) { super.write(&x,x.sizeof); }
void write(double x) { super.write(&x,x.sizeof); }
void write(real x) { super.write(&x,x.sizeof); }
void write(ireal x) { super.write(&x,x.sizeof); }
void write(creal x) { super.write(&x,x.sizeof); }
void write(char x) { super.write(&x,x.sizeof); }
void write(wchar x) { super.write(&x,x.sizeof); }
void write(char[] x) {
this.write(x.length);
super.write(x,x.length*char.sizeof);
}
void write(wchar[] x) {
this.write(x.length);
super.write(x,x.length*wchar.sizeof);
}
void write(dchar[] x) {
this.write(x.length);
super.write(x,x.length*dchar.sizeof);
}
void writeLine(char[] x){
super.write(x~EOL,(x.length+EOL.length)*char.sizeof);
}
}
class BufferedStream(alias T,uint K) : T
{
this()
{
}
ulong available()
{
ulong avail;
if (pread <= pwrite) avail = pwrite-pread;
else {
avail = buffer.length-pread;
avail += pwrite;
}
return super.available() + avail;
}
alias T.read read;
ulong read(ubyte[] data)
{
ulong end,bytes,r;
while (bytes != data.length) {
if (pwrite >= pread) end = pwrite;
else end = buffer.length;
if (end-pread) {
if (end-pread > data.length-bytes)
end = pread+data.length-bytes;
data[bytes..bytes+end-pread] = buffer[pread..end];
bytes += end-pread;
pread = end;
if (pwrite == buffer.length)
pwrite = 0;
}
else {
if (pread > pwrite) end = pread;
else end = buffer.length;
r = super.read(buffer[pwrite..end],end-pwrite);
if (r == 0) break;
pwrite += r;
if (pread == buffer.length)
pread = 0;
}
}
return bytes;
}
alias T.write write;
ulong write(ubyte[] data)
{
return super.write(data,data.length);
}
private:
ubyte[K] buffer;
uint pread;
uint pwrite;
}
------------HThpJ5CpXXRdSLQVK4P5jO--
Aug 02 2004
"Regan Heath" <regan netwin.co.nz> wrote in message news:opsb4onie15a2sq9 digitalmars.com...Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired.
They are remarkably similar to two interfaces - IInputStream & IOutputStream - that I've recently written for a client's networking project. I can't include the code, of course. ;) I agree with the general principle you outline. IStream is one of the elegant parts of COM. (IStorage one of the hideous ones.<g>).
Aug 02 2004
Regan Heath wrote:Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regan
I am not entirely clear on how what you are doing is different from the typical stream implementations you have seen. Look through the thread "OT scanf in Java" for a long post of mine towards the top that explains why the Stream implementations that you think of as being backwards actually do exactly what you want. To give a hint you should ask yourself how you can combine these streams: (--Stream means InputStream or OutputStream) File--Stream Network--Stream Memory--Stream Buffered--Stream Compress--Stream CRC32 Digest--Stream (eg CRC32 or MD5) Image--Stream Video--Stream Audio--Stream Sometimes I will want my data to come from a File, other times I want it to come from the Network. Sometimes I want a digest of the compressed data other times I want a digest of the uncompressed data. These actions are performed in the order I want based on the order in which I construct them: new ImageStream( DigestStream( CompressStream ( FileStream ) ) ) This would read file data, decompresses the data and then compute the CRC32 of the uncompressed data and read image data from it. However: new ImageStream( CompressStream ( DigestStream( FileStream ) ) ) This would read file data, compute the CRC32 of the uncompressed data and then decompress the data and read image data from it. I suspect the class you want to implement will either duplicate the way Stream libraries work or you will find yourself writing seperate classes to handle the two cases above which is bad. Just try to write out all the possible combinations that these 9 classes have already accomplished and you will see what I mean.
Aug 02 2004
On Mon, 02 Aug 2004 20:21:28 -0400, parabolis <parabolis softhome.net> wrote:Regan Heath wrote:Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regan
I am not entirely clear on how what you are doing is different from the typical stream implementations you have seen.
I have written the code to read different types etc once in my Stream template, and can apply it to _any_ class at all that implements one read method of the form: ulong read(void* buffer, ulong length); the same applies to writing. So, if you added methods in the forms given in my interfaces to std.socket you could say: alias Stream!(Socket) SocketStream; alias BufferedStream!(SocketStream,2048) BufferedSocket; and you have a socket you can instantiate like so: SocketStream s = new SocketStream(); which is a buffered stream.Look through the thread "OT scanf in Java" for a long post of mine towards the top that explains why the Stream implementations that you think of as being backwards actually do exactly what you want. To give a hint you should ask yourself how you can combine these streams:
Combination is the next step, what I have so far is not intended to handle combinations.(--Stream means InputStream or OutputStream) File--Stream Network--Stream Memory--Stream Buffered--Stream Compress--Stream CRC32 Digest--Stream (eg CRC32 or MD5) Image--Stream Video--Stream Audio--Stream Sometimes I will want my data to come from a File, other times I want it to come from the Network. Sometimes I want a digest of the compressed data other times I want a digest of the uncompressed data. These actions are performed in the order I want based on the order in which I construct them: new ImageStream( DigestStream( CompressStream ( FileStream ) ) ) This would read file data, decompresses the data and then compute the CRC32 of the uncompressed data and read image data from it. However: new ImageStream( CompressStream ( DigestStream( FileStream ) ) ) This would read file data, compute the CRC32 of the uncompressed data and then decompress the data and read image data from it.
My next step is to implement what I call 'filters' they are like your CompressStream etc above, they will link one stream to another and filter the data passing through them.I suspect the class you want to implement will either duplicate the way Stream libraries work or you will find yourself writing seperate classes to handle the two cases above which is bad. Just try to write out all the possible combinations that these 9 classes have already accomplished and you will see what I mean.
As I said above, combinations are my next step. I may end up handling them in the same way as you have shown above but I'd rather not, I dislike instantiation in the form: new ImageStream( CompressStream ( DigestStream( FileStream ) ) ) Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
Regan Heath wrote:As I said above, combinations are my next step. I may end up handling them in the same way as you have shown above but I'd rather not, I dislike instantiation in the form: new ImageStream( CompressStream ( DigestStream( FileStream ) ) )
What will your eq. version of the above be?
Aug 02 2004
On Mon, 02 Aug 2004 21:00:57 -0400, parabolis <parabolis softhome.net> wrote:Regan Heath wrote:As I said above, combinations are my next step. I may end up handling them in the same way as you have shown above but I'd rather not, I dislike instantiation in the form: new ImageStream( CompressStream ( DigestStream( FileStream ) ) )
What will your eq. version of the above be?
I'm not at all sure yet. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
Regan Heath wrote:Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regan
cool idea(s). I like the "bolt-in" (or it bolt-on?) stuff. One possible problem, though, is that if read(out int x), for example, is in Stream!(File) and it call super.read(...) then the BufferedStream!(FileStream) subclass will not read ints from the buffer (the super.read goes directly to RawFile.read - not BufferedStream.read). I guess I'm a little lost about how the buffering works. At one point there was the suggestion of moving the default implementations of all the read(out int x) etc from Stream (the original one in std/stream.d) to a mixin so that instead of subclassing to get the default implementation you mix it in. I don't know how that approach would change around your code but it's worth thinking about. To be more concrete, here's how that would work: template DefaultInputStream() { void read(out int x) {readExact(...);} char[] readLine() {...} ... } template DefaultOutputStream() { void write(int x) {...} void writeLine(char[] x) {...} ... } class Stream : InputStream, OutputStream { mixin DefaultInputStream; mixin DefaultOutputStream; ... } Anyhow, keep pluggin'. -Ben
Aug 02 2004
On Mon, 02 Aug 2004 22:19:19 -0400, Ben Hinkle <bhinkle4 juno.com> wrote:Regan Heath wrote:Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regan
cool idea(s). I like the "bolt-in" (or it bolt-on?) stuff.
Yeah, I think they're a great idea.One possible problem, though, is that if read(out int x), for example, is in Stream!(File) and it call super.read(...) then the BufferedStream!(FileStream) subclass will not read ints from the buffer (the super.read goes directly to RawFile.read - not BufferedStream.read). I guess I'm a little lost about how the buffering works.
You're right. :( I think to fix it I change: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; to alias BufferedStream!(RawFile,2048) File; alias Stream!(File) FileStream; and then change the signatures of the methods in BufferedStream from ulong read(ubyte[] data) to ulong read(void* buffer, ulong length) etc. This highlights an 'inclusion order' weakness to this design which needs to be fixed.At one point there was the suggestion of moving the default implementations of all the read(out int x) etc from Stream (the original one in std/stream.d) to a mixin so that instead of subclassing to get the default implementation you mix it in. I don't know how that approach would change around your code but it's worth thinking about.
I think this might just be the solution to the inclusion order weakness in my current design. I'm going to turn my Stream template class into a pair of mixins.To be more concrete, here's how that would work: template DefaultInputStream() { void read(out int x) {readExact(...);} char[] readLine() {...} ... } template DefaultOutputStream() { void write(int x) {...} void writeLine(char[] x) {...} ... } class Stream : InputStream, OutputStream { mixin DefaultInputStream; mixin DefaultOutputStream; ... } Anyhow, keep pluggin'.
Thanks. I'll post version 2 when I have one. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
and then change the signatures of the methods in BufferedStream from ulong read(ubyte[] data) to ulong read(void* buffer, ulong length) etc.
Why not use void[] for general read and write?
Aug 02 2004
On Tue, 3 Aug 2004 00:19:15 -0400, Vathix <vathixSpamFix dprogramming.com> wrote:and then change the signatures of the methods in BufferedStream from ulong read(ubyte[] data) to ulong read(void* buffer, ulong length) etc.
Why not use void[] for general read and write?
So you can go: bool read(out byte x) { if (this.read(&x,x.sizeof) ..etc.. bool read(out ubyte x) { if (this.read(&x,x.sizeof) ..etc.. bool read(out short x) { if (this.read(&x,x.sizeof) ..etc.. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
In article <cemsn6$kb9$1 digitaldaemon.com>, Ben Hinkle says...At one point there was the suggestion of moving the default implementations of all the read(out int x) etc from Stream (the original one in std/stream.d) to a mixin so that instead of subclassing to get the default implementation you mix it in.
This is kind of how the stream implementation I was working on is done. If I find the time to finish it I'll post it. Sean
Aug 02 2004









"Matthew" <admin stlsoft.dot.dot.dot.dot.org> 