解读Lucene.Net ——二、 InputStream 之二

lucene_inputstream

InputStream类有两个子类,RAMInputStream和FSInputStream。

RAMInputStream这个类比较简单,由于父类已经实现了ICloneable接口,这里只是一个形式上的实现。RAMInputStream内聚了一个RAMFile对象。它最复杂的部分,就是重写抽象方法ReadInternal。

代码 2-12

public override void  ReadInternal(byte[] dest, int destOffset, int len)
{
    int remainder = len;
    int start = pointer;
    while (remainder != 0)
    {
        int bufferNumber = start / BUFFER_SIZE;
        int bufferOffset = start % BUFFER_SIZE;
        int bytesInBuffer = BUFFER_SIZE - bufferOffset;
        int bytesToCopy = bytesInBuffer >= remainder?remainder:bytesInBuffer;
        byte[] buffer = (byte[]) file.buffers[bufferNumber];
        Array.Copy(buffer, bufferOffset, dest, destOffset, bytesToCopy);
        destOffset += bytesToCopy;
        start += bytesToCopy;
        remainder -= bytesToCopy;
    }
    pointer += len;
}

ReadInternal的主要逻辑在循环里边。ReadInternal方法在InputStream类中两个方法中被调用,分别是ReadBytes方法和Refill方法。在Refill方法中把buffer传入了ReadInternal方法,传入的另一个重要参数是bufferLength。可以看出,在Refill的调用中传入ReadInternal的就是InputStream的缓冲区和缓冲区的结尾。ReadInternal的循环就是遍历缓冲区的一个过程。如果是第一次调用Refill方法,

int bufferNumber = start / BUFFER_SIZE;
在这次调用中得到的值是0,bufferOffset 也是0。bytesInBuffer 就是1024。

int bytesToCopy = bytesInBuffer >= remainder?remainder:bytesInBuffer;
则是计算应该读取的实际长度。

byte[] buffer = (byte[]) file.buffers[bufferNumber];这里用到了RAMFile对象,RAMFile实际上是一个内存文件映像,它的buffers存储了文件的分段,感觉就像是一个文件写在不同的硬盘扇区上。这里得到了起始段。然后把这个段写入InputStream的缓冲区buffer。

destOffset += bytesToCopy;计算了缓冲区的结束点,start += bytesToCopy;则是计算了当前文件指针指向读取点的地址。最后remainder -= bytesToCopy;计算出还剩余多少字节需要拷贝。循环结束后把指针指向了结束点。

如果第二次调用Refill方法,那么pointer已经改变了,通过计算,就得到现在应该读取文件的哪个地方,然后重复上述过程。

 

ReadBytes则是通过SeekInternal方法主动调整了pointer的值。过程和上面的一样。

 

而FSInputStream类则相对复杂,这个类写在了FSDirectory.cs文件中。它是和FSDirectory类配合使用的一组处理方法。它里面有一个内嵌类Descriptor,这个类继承自BinaryReader,是用来读取传进来的文件的。在构造函数中传入一个文件:

代码 2-13

public FSInputStream(System.IO.FileInfo path)
{
    file = new Descriptor(this, path, System.IO.FileAccess.Read);
    length = file.BaseStream.Length;
}

代码 2-14

        internal class Descriptor : System.IO.BinaryReader
        {
            private void  InitBlock(FSInputStream enclosingInstance)
            {
                this.enclosingInstance = enclosingInstance;
            }
            private FSInputStream enclosingInstance;
            public FSInputStream Enclosing_Instance
            {
                get
                {
                    return enclosingInstance;
                }
            }

            public long position;
            public Descriptor(FSInputStream enclosingInstance, System.IO.FileInfo file, System.IO.FileAccess fileAccess)
                : base(new System.IO.FileStream(file.FullName, System.IO.FileMode.Open, fileAccess, System.IO.FileShare.ReadWrite))
            {
            } 
        }


 

file = new Descriptor(this, path, System.IO.FileAccess.Read);实际上就是打开该文件。2-14的代码是通过转换器转换过来的,和下面代码效果一样:

代码 2-15

internal class Descriptor : System.IO.BinaryReader
{
    public Descriptor1(FSInputStream enclosingInstance, System.IO.FileInfo file, System.IO.FileAccess fileAccess)
        : base(new System.IO.FileStream(file.FullName, System.IO.FileMode.Open, fileAccess, System.IO.FileShare.ReadWrite))
    {
    }
}

取得当前位置值需要用Descriptor 的BaseStream.Position。

 

代码 2-16

        public override void  ReadInternal(byte[] b, int offset, int len)
        {
            lock (file)
            {
                long position = GetFilePointer();
                if (position != file.position)
                {
                    file.BaseStream.Seek(position, System.IO.SeekOrigin.Begin);
                    file.position = position;
                }
                int total = 0;
                do
                {
                    int i = file.Read(b, offset + total, len - total);
                    if (i <= 0)
                        throw new System.IO.IOException("read past EOF");
                    file.position += i;
                    total += i;
                }
                while (total < len);
            }
        }

代码2-16重写父类ReadInternal方法,先是锁定了一个文件读取对象,long position = GetFilePointer();则是得到了当前指针,如果计算出的指针和实际指针不一致,则把指针定位到计算出的指针。然后会通过Read方法把字节读入缓冲区。

该类实现了自己的深拷贝,在拷贝后,拷贝出的对象会有一个标记clone.isClone = true;完整代码:

代码 2- 17

public override System.Object Clone()
{
    FSInputStream clone = (FSInputStream) base.Clone();
    clone.isClone = true;
    return clone;
}

在Close方法中,则判断了,如果打开了文件,并且该对象不是拷贝,则关闭文件读取。最后让GC不回收该类。

代码 2- 18

public override void  Close()
{
    if (!isClone && file != null)
        file.Close();
    System.GC.SuppressFinalize(this);
}

因为这个类的读取,指针由BinaryReader维护,所以SeekInternal并没有任何动作。在RAMDirectory和FSDirectory类的OpenFile方法中分别使用了RAMInputStream和FSInputStream这两种文件操作方式。

posted @ 2008-08-14 00:43  Birdshover  阅读(1124)  评论(3编辑  收藏  举报