Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering an arbitrary data processing object with a byte stream

I have a need to provide the ability to plug an arbitrary C# class written by a third party into an existing program, so that it can process a stream of bytes.

This object (or its type) would be registered with a sending and receiving object. It would read bytes from the sending object, process them in some way, and pass them to the receiving object, like this:

Sender ==> Filter ==> Receiver

The prototype API for the filter class looks something like this:

// Reads the specified number of bytes from the receiver
public byte[] ReadBytes(int numberOfBytes)

// Writes the specified number of bytes to the sender.
public void WriteBytes(byte[] data)

How can I provide a relatively simple mechanism for registering the Filter object with the Sender and Receiver objects, and then hook up the methods so that the data bytes can be passed into the Filter object, and out the other side?


Caveats:

  1. My organization is somewhat allergic to Open Source, so frameworks like MEF are probably out.
  2. If you recommend a GoF pattern, please explain in detail how it relates to this specific problem.
like image 791
Robert Harvey Avatar asked Jun 09 '26 14:06

Robert Harvey


2 Answers

I'm not sure that I fully understand the problem, however from what you have described I'd probably have the filter and Sender both implement (or provide an instance of) Stream with CanRead returning false.

The filter would require an instance of Sender to read bytes from, and so filter would need to inherit from some base class that allowed someone to supply a source Stream, something along the lines of:

public abstract class FilterBase : Stream
{
    private readonly Stream sender;
    protected Stream Sender
    {
        get { return sender; }
    }

    public FilterBase(Stream sender)
    {
        this.sender = sender;
    }
}

To "Register" the filter with a sender, you simply provide an instance of Stream from which the filter should read bytes.

Similarly, to "Register" the filter with the Receiver simply give it an instance of Stream (the filter) from which it should read bytes. (Alternatively you could provide a Type and have the reciever / sender create the filter instance - this is the bit I'm fuzzy on).

This easily allows you to chain any number of filters together.

For convenience FilterBase can implement many of the Stream methods and properties (most of them will just throw NotSupportedException anyway).

Alternatively you could define your own IStream (or IFilter) interface which uses just the methods that the reciever uses, but I'd still use the same chaining pattern where you "Register" a sender / reciever with a filter by providing a IStream instance.

Of course this only works if Receiver pulls bytes from Sender - if Sender instead pushes bytes to Receiver then your filter will need to implement the Write methods of stream instead of the Read methods and Receiver needs to inherit from (or provide a) Stream.

You can also do both if needed by having both Sender and Reciever inherit from (or provide a) Stream. This might get a bit confusing for anyone implementing a filter - the Write methods are messages to be pushed to the reciever and the Read methods are requests to read from the sender. You might want to create your own interface(s) to make it clear what the roles are, however again I'd still follow the same pattern - just with different method names.

like image 143
Justin Avatar answered Jun 12 '26 03:06

Justin


You could encapsulate your API, along with the sender and receiver objects, in a StreamContext class and then pass the context object to the filter, which can implement an IFilter interface:

public class StreamContext
{ 
    private Sender sender;
    private Receiver receiver;

    public byte[] ReadBytes(int numberOfBytes) 
    { 
        return readBytesFromSender(numberOfBytes); 
    }

    public void WriteBytes(byte[] data) 
    { 
        writeToReceiver(data); 
    }
}

public interface IFilter
{
    void Filter(StreamContext context);
}
like image 25
Mark Cidade Avatar answered Jun 12 '26 02:06

Mark Cidade



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!