Spot the defect (1/2): Custom Pipeline Component Sample "FixMsg" can fail without any apparent reason

Part 1 Part 2

Last week, I was trying to help Matthew with his custom pipeline component (see this newsgroup thread) .

To make a long story short, Matthew started from the custom pipeline component provided with BizTalk 2004 SDK (it gets installed into <BizTalk Install Dir>\SDK\Samples\Pipelines\CustomComponent) and specified a GUID to be put at the beginning of every file. Then, he composed this custom component (at decode stage) with a flat file disassembler. When executed, the following error showed up:

"SomePipeline.ReceivePipeline" Source: "Microsoft.BizTalk.Pipeline" Receive Location: "<some path>\*.000" Reason: Index was outside the bounds of the array.

We finally narrowed down the problem to be in the Read() routine of FixMsg, line 213, in (FixMsgStream.cs), The code reads as follow:

[ Some irrelevant code here ]

 

private byte[] prependData = null;

private byte[] appendData = null;

private int prependDataOffset = 0;

private int appendDataOffset = 0;

 

[ Some irrelevant code here ]

override public int Read(byte[] buffer, int offset, int count)

{

  int ret = 0;

  int bytesRead = 0;

  if (prependData!=null)

  {

     bytesRead = count > prependData.Length ? prependData.Length : count;

     Array.Copy(prependData, prependDataOffset, buffer, offset, bytesRead);

     if (bytesRead==prependData.Length)

       prependData = null; // prependData consumed

                       

     prependDataOffset += bytesRead;

     offset += bytesRead;

     count -= bytesRead;

     ret += bytesRead;

  }

  bytesRead = stm.Read(buffer, offset, count); // <-- This is the offending line (213)

  offset += bytesRead;

  count -= bytesRead;

  ret += bytesRead;

  if ((bytesRead==0) && (appendData!=null))

  {

    // End of stream

    bytesRead = count > appendData.Length ? appendData.Length : count;

    Array.Copy(appendData, appendDataOffset, buffer, offset, bytesRead);

    if (bytesRead==appendData.Length)

       appendData = null; // appendData consumed

                       

    appendDataOffset += bytesRead;

    ret += bytesRead;

  }

  return ret;

}

 

This Read() method above is supposed to implement the standard IStream.Read() function. prependData and appendData are two arrays of bytes, containing the data to prepend (put before the very first character of the message) and append (put after the very last character of the message). Observe that these two arrays can be empty (or null) which turns this custom pipeline component into a no-operation.

Matthew specified an hardcoded GUID for appendData so the length turned out to be 36 bytes (well, actually, he did put a comma “,” after so the size was actually 37 but this is irrelevant to the problem so let's ignore it for now).

Can you figure out what is wrong with that code without actually reading the newsgroup thread? Can you suggest ways for fixing this?
Hint: What happens if Read() is called twice, first with count = 10 and then count = 1024?