Example 4

From: Skybuck Flying (nospam_at_hotmail.com)
Date: 04/11/04


Date: Sun, 11 Apr 2004 14:23:18 +0200

Example 4.

Suppose one doesn't want to reconstruct the memory streams to change the
offset.

Or one wants to pass the memory stream to different classess... whatever
your code architecture/design might be, it might be desired to prevent
having to reconstruct the memory stream.

So this example show how to use the seek functionality to write/read at a
specific offsets... and thereby indirectly writing/reading to a specific
location in the buffer.

It does have the drawback that one could overwrite stuff or read non
existing data from locations where one doesn't want to write/read :D

So the special constructor of example 0.02 does offer some benefits: namely
range/bound checking :D

This example does not have any range/bound checking... the complete buffer
can be seeked/written/read from.

That might be an advantage or a disadvantage :D

Code:

program Project1;

{$APPTYPE CONSOLE}

{

Test program for writing/reading to/from streams for buffers :D

version 0.01 created on 11 april 2004 by Skybuck Flying :)

It's probably still possible to get better performance by making
smarter use of memory stream class. It's probably possible to make
the memory stream point to the buffer so the memory stream doesn't have
to create it's own internal buffers and so that a copy is not needed.

Let's try to do this in version 0.02 :)

version 0.02

Take note: It might be needed to seek(0) for the receive stream before
writing
to it, so it starts to write to the begin of the stream :D

Ok now we try to improve the code, by reducing some copies, and memory
requirements
etc.

Ok, that was pretty simple. It's possible to create memory streams which
point
to buffers. This creates fixed-sized memory streams which can not be
resized.

We have just reduced one copy, and memory requirements :D

In fact I believe we are now writing directly into the buffer which is
pretty
cool.

The true performance still has to be measured, but I am happy :)

The only loss of functionality... is writing to a specific location in the
memory
stream.

Maybe even that can be achieved by using a different constructor for the
memory
stream :)

OH YEAH BABY ! THAT IS POSSIBLE ! :)

That means we can leave room etc for headers etc :D and again zero copies
needed.

Well actually just one... fields to buffers.... and fields from buffers.

Or we could eliminate the fields and do just this: Write( 240 ); etc ;)
Take your pick ;)

With typecasts we could have written directly into the buffers, via fields.
Like Psomething(buffer).SomeField1 := 340;
Like Psomething(buffer).SomeField2 := 35343;

This clear code/functionality is now completely lost.

Instead we would have to do:
Writer.Write( 340 );
Writer.Write( 35343 );

I wonder if these two examples are actually the same performance wise...

Probably not since write is a function anyway...

Or we can do this:

SomeField1 := 340;
SomeField2 := 35343;

Writer.Write( SomeField1 );
Writer.Write( SomeField2 );

One copy is not so bad :)

Ok... in the next example... we will look how to write the contents
to a specific location inside the buffer :D

I am realllllllllly starting to luvvvvvvv .NET :D

It's just sooooo goood/super startrek like =D

version 0.03

This will probably be my last example on how to work with buffers (=array of
byte)
in .NET :D

Since the last thing I am interested in is how to write stuff to a specific
location
inside the buffer =D

So in this example we will leave room for 10 bytes from the beginning of the
buffer
to leave room for say a header. :P

The way to do it is quite simple... just specify the location when creating
the memory stream.... the offset in the constructor...

We will use two new variables for each send/receive

SendBufferOffset specifies where we want the writing/reading to begin :D

SendBufferBytes specifies how many bytes are remaining in the buffer

(It's like a buffer size... but I call it buffer bytes... since it is not
really
the length of the buffer... )

We also need to modify the simulation code... which simulates the
sending/receiving
of the buffers... the simple for loop...

it should now copy from the send offset to the receive offset =D

And there you have it folks... a nice example how to fricking blast the net
with ultra cool stuff :PPPPPP********

Now go make something cool :D

Hmmm I just realized we might not even have to set the offset inside the
constructor

We could also set the offset via seeking into the stream.

This would safe us from having to recreate the memory stream, over and over
again to change the offset.

So I will make one last example how to change the offset without using the
special
constructor :D

version 0.04

Let's go back to the way things were in version 0.02...

This time we will use the seek functionality of the stream so that writing
and reading will start at an offset into the buffer :)

Ok... this example now uses seek...

This example does have drawbacks since we no longer have automatic range
checking.

Example 0.03 had some very cool advantages: It should be impossible to
overwrite
anything before the offset... and it should be impossible to write further
than
the bytes specified in the constructor.

So the special constructor in 0.03 should provid us with automatic range
checking, bounds
checking etc :D

This example 0.04 is therefore a little bit more dangerous and will require
the user
to make sure not to overwrite important stuff ;)

The advantage of example 0.04 could be that the memory stream can be shared
among
different classess... so it doesn't have to be reconstructed etc...

Or... each class could just constructor it's own memory stream and only
buffers
have to be passed between classess. :)

Or even special wrappers ;)

( mReceiveBufferBytes and mSendBuffersBytes have been commented out )

}

uses
 SysUtils,
 System.IO;

var
 mSendBuffer : array of byte;
 mReceiveBuffer : array of byte;

 mSendBufferOffset : integer;
 mReceiveBufferOffset : integer;

// mSendBufferBytes : integer;
// mReceiveBufferBytes : integer;

 mSendStream : Stream;
 mReceiveStream : Stream;

 mSendWriter : BinaryWriter;
 mReceiveReader : BinaryReader;

 { fields to send }
 mSendByte : byte;
 mSendWord : word;
 mSendLongWord : longword;

 { fields to receive }
 mReceiveByte : byte;
 mReceiveWord : word;
 mReceiveLongWord : longword;

 i : integer;

begin
 writeln('program started');

 // create buffers
 SetLength( mSendBuffer, 2000 );
 SetLength( mReceiveBuffer, 2000 );

 // create streams
 // this will create a empty memory stream which can grow/shrink
// mSendStream := MemoryStream.Create;
// mReceiveStream := MemoryStream.Create;

 // specify where we want the writing and reading to begin, in the buffers.
 mSendBufferOffset := 10; // this will actually be byte 11 ;)
 mReceiveBufferOffset := 10; // this will actually be byte 11 ;)

// mSendBufferBytes := 1990; // we still have room for 1990 bytes ;)
// mReceiveBufferBytes := 1990; // we still have room for 1990 bytes ;)

 // this will create a fixed sized memory stream which can not grow/shrink
 // and is located at a specify point into the buffer up to a certain
ammount of bytes.
// mSendStream := MemoryStream.Create( mSendBuffer, mSendBufferOffset,
mSendBufferBytes );
// mReceiveStream := MemoryStream.Create( mReceiveBuffer,
mReceiveBufferOffset, mReceiveBufferBytes );

 // this will create a fixed sized memory stream which can not grow/shrink
 mSendStream := MemoryStream.Create( mSendBuffer );
 mReceiveStream := MemoryStream.Create( mReceiveBuffer );

 // create binary writer and reader
 mSendWriter := BinaryWriter.Create( mSendStream );
 mReceiveReader := BinaryReader.Create( mReceiveStream );

 // set fields to send
 mSendByte := 240;
 mSendWord := 65123;
 mSendLongWord := 3123123;

 // *** new code ***

 // let's seek into the stream so we can start writing at a specific offset.
 mSendStream.Seek( mSendBufferOffset, SeekOrigin.Begin );

 // write fields to stream
 mSendWriter.Write( mSendByte );
 mSendWriter.Write( mSendWord );
 mSendWriter.Write( mSendLongword );

 // seek begin of stream
// mSendStream.Seek( 0, SeekOrigin.Begin );

 // read steam into buffer, offset 0, length 1+2+4 = 7

// copy should not be needed, reduced one copy.
// mSendStream.Read( mSendBuffer, 0, 7 );

 // copy buffer to receive buffer. (simulates sending/receive of buffer}
 for i:=0 to 7 do
 begin
  mReceiveBuffer[i+mReceiveBufferOffset] :=
mSendBuffer[i+mSendBufferOffset];
 end;

 // read the buffer into the stream, offset 0, length 7
 // incorrect: reads bytes into buffer :)
// mReceiveStream.Read( mReceiveBuffer, 0, 7 );

 // put receive buffer into receive stream
 // write buffer into stream, at offset 0, count 7 bytes
 // copy should not be needed, reduced one copy.
// mReceiveStream.Write( mReceiveBuffer, 0, 7 );

 // *** new code ***

 // let's seek into the stream so we can start reading at a specific offset.

 // seek begin of stream
 mReceiveStream.Seek( mReceiveBufferOffset, SeekOrigin.Begin );

 // read fields from stream
 mReceiveByte := mReceiveReader.ReadByte;
 mReceiveWord := mReceiveReader.ReadUInt16;
 mReceiveLongWord := mReceiveReader.ReadUInt32;

 // display received fields
 writeln('receive byte: ' + IntToStr(mReceiveByte) );
 writeln('receive word: ' + IntToStr(mReceiveWord) );
 writeln('receive longword: ' + IntToStr(mReceiveLongWord) );

 writeln('press enter to continue');
 readln;

 // free binary writer and reader
 mReceiveReader.Free;
 mSendWriter.Free;

 // free streams
 mReceiveStream.Free;
 mSendStream.Free;

 // free buffers
 SetLength( mReceiveBuffer, 0 );
 SetLength( mSendBuffer, 0 );

 writeln('program finished');
end.

Skybuck.



Relevant Pages

  • Example 3.
    ... It also shows how to read stuff from a specific location inside a buffer. ... smarter use of memory stream class. ... the memory stream point to the buffer so the memory stream doesn't have ... SetLength(mReceiveBuffer, 2000); ...
    (alt.comp.lang.borland-delphi)
  • Re: writing sound from memory stream to secondarybuffer
    ... > sound into a MemoryStream. ... the stream is passed ... > I have evidence that the memory stream contains the sound data; ... > there so it can be parsed by the device and read the buffer for play. ...
    (microsoft.public.win32.programmer.directx.audio)
  • Re: Fav. Memory Stream Impl.
    ... put and reach the size of the byte buffer, ... I am not looking for a bounded memory stream, ... still valid when an input stream or output stream ...
    (comp.lang.java.programmer)
  • Re: Data from one stream to another
    ... This offset parameter to both Stream.Read and Stream.Write indicate to ... these methods at what index in the buffer to read or write. ... stream might not be seekable. ... overloaded CopyStream that can specify an 'int count' argument to limit ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Data from one stream to another
    ... This offset parameter to both Stream.Read and Stream.Write indicate to ... these methods at what index in the buffer to read or write. ... stream might not be seekable. ... overloaded CopyStream that can specify an 'int count' argument to limit ...
    (microsoft.public.dotnet.languages.csharp)