Suggestions for std.stream (esp. for 64bit)
Daniel Gibson
metalcaedes at gmail.com
Tue Oct 5 16:46:37 PDT 2010
I'm using some of std.stream's classes (and also SocketStream) in a
project of mine (it currently uses D1 but std.Stream hasn't changed much
so all this is valid for D2 as well).
std.stream is mostly what I expect from classes for Streams (having
experience with Java's equivalents). However there are a few things that
I think could/should be improved.
1. (important for 64bit D): write(char[]) and read(char[]) are too
platform specific.
These methods (and the wchar counterparts) write (or read) "a string,
together with its length". This is fine (even though I would rather
expect writeString/readString to do this magic, but nevermind), *but*
the length is written as a size_t - making it heavily platform dependant.
This means that you can't use write(char[]) to write into a file on a
x86 system and later read that file on an amd64 system. Also consider
SocketStream.. you can't use SocketStream.write(char[]) to communicate
between a x86 and an amd64 box (when an 64bit executable is used an the
latter).
This could easily be fixed by using uint or ulong instead of size_t on
all platforms. (uint is probably ok, Java even uses short in a similar
method (java.io.DataOutput.writeUTF() - never use this, it's no real
UTF-8)).
Unfortunately the libphobos of GDC (that already supports 64bit targets)
has been using size_t for ages, so in D1 it should maybe stay like that
to avoid breaking compatibility (on the other hand probably no GDC user
who thinks at least a bit cross-platform uses write(char[]) anyway - in
that case just use uint so it's compatible with existing 32bit binaries
from DMD).
But at least for D2/phobos2 that should be changed.
2. The documentation says for write(): "Outside of byte, ubyte, and
char, the format is implementation-specific and should only be used in
conjunction with read. Throw WriteException on error."
So how do I write files for other programs to read? How do I communicate
with servers/clients not written in D with a SocketStream?
Fortunately the documentation exaggerates. Apart from write(char[]) and
write(wchar[]) the claim is not true.
The simple types are platform specific, not specific to D/std.stream -
so a program written in C (or any other language) is probably able to
read that data (if it supports the type - may be tricky with real and
maybe the imaginary types).
And by using the EndianStream, most of the types probably can be read by
other platforms (again, real and the imaginary types might cause
trouble.. and maybe floating point types in general, if the other
platform doesn't support IEEE 754 floats - but integer types are
definitely safe).
This is bad, because if someone wants to use SocketStream he's confused
by that statement until he looks at the source to find out that it's not
so implementation specific after all.
So please document how exactly write( (w)char[]) encodes the length and
also make clear that write( <basic type> ) does no strange voodoo, but
just dumps the bytes of the value (in platform specific big/little
endian order).
3. InputStream's read( <type> val) often is inconvenient.
If you want to read an int (or any other basic type) from an InputStream
s you have to do:
int foo; s.read(foo);
This doesn't look soo horrible.. but maybe you want to pass that value
directly to a function?
int foo; s.read(foo); bar(foo);
This is inconvenient. Java has something like Stream.readInt(), so you
can write
bar( s.readInt() );
That's much shorter and you don't need to invent a name for that value
you only want to use once anyway.
For D I'd suggest a templated method for that:
T read(T)() { T ret; readExact(&ret, ret.sizeof); return ret; }
So you could write
int foo = s.read!int; // instead of int foo; s.read(foo);
or even
auto foo = s.read!int;
and
bar( s.read!int ); // instead of "int foo; s.read(foo); bar(foo);"
(The implementation should probably make sure T is a basic type or maybe
a struct, but no array or Object or pointer. Also special cases for
char[] and wchar[] might be needed for consistency.)
4. Minor inconsistencies:
In InputStream there is a read(ubyte[]) method, but no
readExact(ubyte[]) method.
I'd suggest adding
void readExact(ubyte[] buf) { readExact(buf.ptr, buf.length); }
and, for convenience
ubyte[] readExact(size_t len) {
ubyte[] ret = new ubyte[len];
readExact(ret.ptr, len);
return ret;
}
Also there is a write(ubyte[]) method in OutputStream, but no
writeExact(ubyte[]) method, so I'd suggest adding
void writeExact(ubyte[] buf) { // maybe "const(ubyte[]) buf" for D2
writeExact(buf.ptr, buf.length);
}
All code above is untested and thus to be considered pseudo-code ;-)
Cheers,
- Daniel
More information about the Digitalmars-d
mailing list