/// <summary>
        ///This function is called both for reading and writing to the Centera and it signifies
        ///that the block has been to the SDK (when writing) or received from the SDK (when reading).
        ///
        ///@param info		The FPStreamInfo structure containing the data and control information.
        /// </summary>
        public virtual long BlockTransferred(ref FPStreamInfo info)
        {
            //Console.WriteLine(this.GetHashCode() + " Block transferred OK");
            if (info.mReadFlag == (byte)FPStream.StreamDirection.OutputFromCentera)
            {
                if (info.mTransferLen > 0)
                {
                    if (info.mTransferLen > bufferSize)
                    {
                        localBuffer = new byte[info.mTransferLen];
                        bufferSize  = (int)info.mTransferLen;
                    }

                    unsafe
                    {
                        Marshal.Copy((IntPtr)info.mBuffer, localBuffer, 0, (int)info.mTransferLen);
                    }
                }

                ProcessReturnedData(localBuffer, (int)info.mTransferLen, userStream);
                info.mStreamPos += info.mTransferLen;
            }

            return(0);
        }
        /// <summary>
        ///Allows the SDK to set a marker at the current stream position. This effectively
        ///means that data up to this point has been successfully transferred to the Centera.
        ///
        ///@param info		The FPStreamInfo structure containing the data and control information.
        /// </summary>
        public virtual long SetMark(ref FPStreamInfo info)
        {
            //Console.WriteLine(this.GetHashCode() + " Mark set at " + info.mStreamPos);
            info.mMarkerPos = info.mStreamPos;

            return(0);
        }
 /// <summary>
 ///The SDK has signalled that the operation is complete. Allocated buffers should now be freed
 ///up.
 ///
 ///@param info		The FPStreamInfo structure containing the data and control information.
 /// </summary>
 public virtual long TransferComplete(ref FPStreamInfo info)
 {
     // If we are writing to Centera, free the unmanaged buffer that we allocated
     //Console.WriteLine(this.GetHashCode() + " Transfer complete");
     if (info.mReadFlag == (byte)FPStream.StreamDirection.InputToCentera)
     {
         unsafe
         {
             Marshal.FreeHGlobal((IntPtr)info.mBuffer);
             info.mBuffer = null;
         }
     }
     return(0);
 }
        /// <summary>
        ///Allows the SDK to request that the application resends data from the previously set
        ///marker position due to problems in sending that data to the Centera. If using
        ///a "normal" GenericStream that uses a Stream based class for transfer, this will be
        ///achieved using the Seek member, and the derived class must implement this.
        ///
        ///Note: up to 100MB of data could be required to be resent!
        ///
        ///@param info		The FPStreamInfo structure containing the data and control information.
        /// </summary>
        public virtual long ResetMark(ref FPStreamInfo info)
        {
            // The stream the user supplies must override Seek in order for this to work!!
            ////Console.WriteLine(this.GetHashCode() + " Mark reset to " + info.mMarkerPos);

            if (userStream == null)
            {
                return(-1);
            }

            userStream.Seek(info.mMarkerPos, SeekOrigin.Begin);
            info.mStreamPos = info.mMarkerPos;
            return(0);
        }
        /// <summary>
        ///This function is only required when writing to the Centera.
        ///Allocate a buffer and populate it with the next chunk of data to be sent.
        ///
        ///@param info		The FPStreamInfo structure containing the data and control information.
        /// </summary>
        public virtual unsafe long PrepareBuffer(ref FPStreamInfo info)
        {
            //Console.WriteLine(this.GetHashCode() + " prepare buffer " + info.ToString());
            // We allocate the buffer if this is the first time through
            if (info.mBuffer == null)
            {
                // Create a heap memory buffer that the stream can use
                info.mBuffer = Marshal.AllocHGlobal(bufferSize).ToPointer();
            }

            int dataSize = PopulateBuffer(ref localBuffer, bufferSize, userStream);

            // Copy the localBuffer into the Heap Allocated mBuffer for transfer.
            Marshal.Copy(localBuffer, 0, (IntPtr)info.mBuffer, dataSize);

            // Update the stream position and the amount transferred
            info.mStreamPos  += dataSize;
            info.mTransferLen = dataSize;

            if (info.mStreamLen == -1)
            {
                if (dataSize < bufferSize)
                {
                    //Console.WriteLine("\tStream processed " + dataSize + "/" + bufferSize);
                    info.mAtEOF = 1;
                }
            }
            else if (info.mStreamLen <= info.mStreamPos)
            {
                //Console.WriteLine("\tStream processed " + info.mStreamLen + "/" + info.mStreamPos);
                info.mAtEOF = 1;
            }
            else
            {
                //Console.WriteLine("\tStream not exhausted " + info.mStreamLen + "/" + info.mStreamPos);
            }

            return(0);
        }