【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

Streamの一部を取り出す

Streamの一部を取り出すため、別のStreamでラップしてみました。C#とF#の両方のコードをパブリックドメインで貼っておきます。C#とF#の対比や、カスタムStreamの例として使えるのではないかと思います。F#の構文はC#と掛け離れていますが、簡潔なので慣れれば便利そうです。

// C#
using System;
using System.IO;

public class SubStream : Stream
{
    private Stream s;
    private long start, length, pos;

    public SubStream(Stream s, long length)
    {
        this.s = s;
        this.start = s.Position;
        this.length = length;
    }

    public override long Length { get { return length; } }
    public override bool CanRead { get { return pos < length; } }
    public override bool CanWrite { get { return false; } }
    public override bool CanSeek { get { return true; } }
    public override void Flush() { }

    public override long Position
    {
        get { return pos; }
        set { s.Position = start + (pos = value); }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (!CanRead) return 0;
        count = (int)Math.Min(length - pos, count);
        int ret = s.Read(buffer, offset, count);
        pos += ret;
        return ret;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Begin: Position = offset; break;
            case SeekOrigin.Current: Position += offset; break;
            case SeekOrigin.End: Position = length + offset; break;
        }
        return pos;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }
}
// F#
open System
open System.IO

type SubStream(s:Stream, length:int64) =
    inherit Stream()
    
    let start = s.Position
    let mutable pos = 0L
    
    override x.Length   = length
    override x.CanRead  = pos < length
    override x.CanWrite = false
    override x.CanSeek  = true
    override x.Flush()  = ()
    
    override x.Position
        with get() = pos
        and set(v) = pos <- v
                     s.Position <- start + pos
    
    override x.Read(buffer, offset, count) =
        if not x.CanRead then 0 else
            let count' = int <| Math.Min(length - pos, int64 count)
            let ret = s.Read(buffer, offset, count')
            pos <- pos + (int64 ret)
            ret
    
    override x.Seek(offset, origin) =
        match origin with
        | SeekOrigin.Begin   -> x.Position <- offset
        | SeekOrigin.Current -> x.Position <- pos + offset
        | SeekOrigin.End     -> x.Position <- length + offset
        | _ -> ()
        pos
    
    override x.Write(_, _, _) = raise <| new NotImplementedException()
    override x.SetLength(_)   = raise <| new NotImplementedException()