Posted: 08/03/08 01:32:15
I'm a D beginner, and this took me a little while to get right. This topic may be old, but maybe sometime this month another beginner like me might want to know this. In any case, I still have questions about this topic!
First of all, I have a file that is binary data. At the start of the file is an int saying how many structs are in the file, and following that is the data that would fill an array of those structs.
My first attempt was like this:
auto inputStream = new DataInput( new FileInput("data/Data") );
int howManyStructs = inputStream.getInt();
MyStruct[] structArray = new MyStruct[howManyStructs];
Stdout.format("array length: {}", structArray.length).newline;
int numberRead = inputStream.read(structArray);
Stdout.format("number read: {}", numberRead).newline;
This failed miserably, because the stream was now offset by an additional int.sizeof. The read() method expects there to be a leading int, skips that int, and then starts reading. So if I take that int from the stream and then call read, it skips an "int" and then starts reading. Compounding this was that if I didn't read that first int, how was I to know how big of an array to allocate? I could just allocate an arbitrarily large one, skip the getInt() call, and then just call read(), but that feels like a dodgy practise. Furthermore, the get() method isn't useful for this because the get() method takes the initial int as the number of bytes to read, not as the number of structs; I'd wind up under-reading by a very large amount.
Now, if I could rewind the buffer after having done that getInt(), then read() would be great. But I don't know how to.
So I looked at DataStream?.d to see if they call a method similar to C's fread that I could use. Sure enough, it does. Which lead to my next approach:
auto file = new FileConduit("data/Data");
auto buffer = new Buffer(file);
int firstInt = 99998888; // just some dummy value
buffer.readExact(&firstInt, firstInt.sizeof);
Stdout.format("int read by Buffer: {}", firstInt).newline;
MyStruct[] structArray = new MyStruct[firstInt];
buffer.readExact(structArray.ptr, MyStruct.sizeof * firstInt);
I had wanted to use file.input.read() so that I wouldn't have to take the additional step of making buffer, but the problem with using file.input.read() is that it'd behave like DataInput?'s read() method (i.e. tries to put the entire file contents into the array), and that didn't really improve my situation from my first attempt. So in any case, what I ended up doing was making an fread-alike in D.
A question that I still have is whether I could've gotten around concocting such code to simulate fread, given the same binary data file. Because of the leading integer in the data file, TypedStream? couldn't be used. FileConduit? grants a seek() method, but the utility of that is not so great for me because I don't know how to make FileConduit? read a specific amount (e.g. just the leading int) and I end up needing to make a Buffer anyway. Is a nicer way possible within the tango library?