如果你只是从 HugeStream 中顺序读取数据,那么它只需要读取每个子流(并将其附加到本地文件中,并将读取的数据返回给调用者)直到子流耗尽,然后继续下一个子流。如果使用 Seek 操作在数据中“向后”跳转,则必须从本地缓存文件开始读取;当你到达缓存文件的末尾时,你必须从你离开的地方继续读取当前的子流。
到目前为止,这一切都非常简单地实现 - 您只需将 Read 调用间接到适当的流,并在每个流用完数据时切换流。
引用文章的低效之处在于,它每次在您阅读时都会遍历所有流,以找出从哪里继续阅读。为了改进这一点,您需要仅在需要时打开子流,并跟踪当前打开的流,以便您可以继续从当前流中读取更多数据,直到它耗尽。然后打开下一个流作为您的“当前”流并继续。这很简单,因为你有一个线性的流序列,所以你只需一个接一个地遍历它们。即类似:
int currentStreamIndex = 0;
Stream currentStream = childStreams[currentStreamIndex++];
...
public override int Read(byte[] buffer, int offset, int count)
{
while (count > 0)
{
// Read what we can from the current stream
int numBytesRead = currentSteam.Read(buffer, offset, count);
count -= numBytesRead;
offset += numBytesRead;
// If we haven't satisfied the read request, we have exhausted the child stream.
// Move on to the next stream and loop around to read more data.
if (count > 0)
{
// If we run out of child streams to read from, we're at the end of the HugeStream, and there is no more data to read
if (currentStreamIndex >= numberOfChildStreams)
break;
// Otherwise, close the current child-stream and open the next one
currentStream.Close();
currentStream = childStreams[currentStreamIndex++];
}
}
// Here, you'd write the data you've just read (into buffer) to your local cache stream
}
为了允许向后搜索,您只需要引入一个新的本地文件流,您在阅读时将所有数据复制到该文件流中(请参阅上面我的伪代码中的注释)。您需要引入一个状态,以便您知道您正在从缓存文件而不是当前子流中读取,然后直接访问缓存(寻找等很容易,因为缓存代表从 HugeStream 读取的数据的整个历史,因此 HugeStream 和 Cache 之间的查找偏移量是相同的 - 您只需重定向任何 Read 调用即可从缓存流中获取数据)
如果读取或回溯到缓存流的末尾,则需要从当前子流中继续读取数据。只需回到上面的逻辑并继续将数据附加到您的缓存流。
如果您希望能够在 HugeStream 中支持完全随机访问,则需要支持寻找“转发”(超出缓存流的当前端)。如果您事先不知道子流的长度,您别无选择,只能继续将数据读取到缓存中,直到达到查找偏移量。如果您知道所有流的大小,那么您可以直接更有效地寻找正确的位置,但是您必须设计一种有效的方法来将您读取的数据存储到缓存文件并记录缓存的哪些部分文件包含有效数据,但实际上尚未从数据库中读取 - 这有点高级。
我希望这对您有意义,并让您更好地了解如何继续......
(您不需要实现比 Read 和 Seek 接口更多的东西就可以让它工作)。