Chromium Code Reviews| Index: chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.cc |
| diff --git a/chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.cc b/chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..493cd15f744824e98ca0917092a9a41cc1ca6006 |
| --- /dev/null |
| +++ b/chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.cc |
| @@ -0,0 +1,133 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.h" |
| + |
| +#include <algorithm> |
| + |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/numerics/safe_conversions.h" |
| +#include "net/base/io_buffer.h" |
| +#include "net/base/net_errors.h" |
| + |
| +using webkit_blob::FileStreamReader; |
| + |
| +namespace { |
| + |
| +const int kDesiredNumberOfBuffers = 2; // So we are always one buffer ahead. |
| +const int kBufferSize = 1024*1024; // 1MB to minimize transaction costs. |
| + |
| +} // namespace |
| + |
| +ReadaheadFileStreamReader::ReadaheadFileStreamReader( |
| + FileStreamReader* underlying) |
| + : underlying_(underlying), |
| + underlying_error_(0), |
| + current_offset_(0), |
| + underlying_has_pending_read_(false), |
| + weak_factory_(this) { |
| +} |
| + |
| +ReadaheadFileStreamReader::~ReadaheadFileStreamReader() {} |
| + |
| +int ReadaheadFileStreamReader::Read( |
| + net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) { |
| + DCHECK(!pending_read_.get()); |
| + |
| + // Dispatch a request to fill up our buffers if needed. |
| + if (underlying_.get() && !underlying_has_pending_read_ && |
| + buffers_.size() < kDesiredNumberOfBuffers) { |
| + ReadFromUnderlying(); |
| + } |
| + |
| + // Consume from our existing buffers and return immediately if possible. |
| + if (!buffers_.empty()) |
| + return ConsumeFromBuffer(buf, buf_len); |
| + |
| + // Pass through the stored underlying error or EOF if existent. |
| + if (!underlying_.get()) |
| + return underlying_error_; |
| + |
| + // We are waiting for an underlying read to complete, so save the request. |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
DCHECK(!pending_read.get());
tommycli
2014/03/04 00:39:40
Done.
|
| + pending_read_.reset(new Request(buf, buf_len, callback)); |
| + return net::ERR_IO_PENDING; |
| +} |
| + |
| +int64 ReadaheadFileStreamReader::GetLength( |
| + const net::Int64CompletionCallback& callback) { |
| + return underlying_->GetLength(callback); |
| +} |
| + |
| +ReadaheadFileStreamReader::Request::Request( |
| + net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) |
| + : buf(buf), buf_len(buf_len), callback(callback) { |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
http://google-styleguide.googlecode.com/svn/trunk/
tommycli
2014/03/04 00:39:40
Done.
|
| +} |
| + |
| +ReadaheadFileStreamReader::Request::~Request() {} |
| + |
| +int ReadaheadFileStreamReader::ConsumeFromBuffer(net::IOBuffer* buf, |
| + int buf_len) { |
| + DCHECK(!buffers_.empty()); |
| + |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
Probably want a while loop here - there might only
tommycli
2014/03/04 00:39:40
Done.
|
| + net::DrainableIOBuffer* head_buffer = buffers_.front().get(); |
| + |
| + DCHECK(head_buffer->BytesRemaining() > 0); |
| + |
| + int copy_len = std::min(buf_len, head_buffer->BytesRemaining()); |
| + std::copy(head_buffer->data(), head_buffer->data() + copy_len, buf->data()); |
| + head_buffer->DidConsume(copy_len); |
| + |
| + if (head_buffer->BytesRemaining() == 0) { |
| + buffers_.pop(); |
| + |
| + // Get a new buffer to replace the one we just used up. |
| + if (!underlying_has_pending_read_ && underlying_.get()) |
| + ReadFromUnderlying(); |
| + } |
| + |
| + return copy_len; |
| +} |
| + |
| +void ReadaheadFileStreamReader::ReadFromUnderlying() { |
| + DCHECK(!underlying_has_pending_read_); |
| + underlying_has_pending_read_ = true; |
| + |
| + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize)); |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
Why not make a DrainableIOBuffer here instead of c
tommycli
2014/03/04 00:39:40
Added a comment. DrainableIOBuffer just wraps the
vandebo (ex-Chrome)
2014/03/04 18:51:32
Right. I figured that out, but forgot to remove m
tommycli
2014/03/04 21:14:58
Done.
|
| + int result = underlying_->Read( |
| + buf, |
| + kBufferSize, |
| + base::Bind(&ReadaheadFileStreamReader::OnFinishReadFromUnderlying, |
| + weak_factory_.GetWeakPtr(), buf)); |
| + |
| + if (result != net::ERR_IO_PENDING) { |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
If we get ERR_IO_PENDING, you'll leave the request
tommycli
2014/03/04 00:39:40
As intended. If we get ERR_IO_PENDING, OnFinishRea
|
| + OnFinishReadFromUnderlying(buf, result); |
| + } |
| +} |
| + |
| +void ReadaheadFileStreamReader::OnFinishReadFromUnderlying(net::IOBuffer* buf, |
| + int result) { |
| + DCHECK(result != net::ERR_IO_PENDING); |
| + DCHECK(underlying_has_pending_read_); |
| + underlying_has_pending_read_ = false; |
| + |
| + if (result <= 0) { |
| + underlying_.reset(); |
| + underlying_error_ = result; |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
Doesn't this leave request hanging? You probably
tommycli
2014/03/04 00:39:40
Done. I didn't use a FinishRequest, as I couldn't
|
| + return; |
| + } |
| + |
| + scoped_refptr<net::DrainableIOBuffer> drainable_buffer( |
| + new net::DrainableIOBuffer(buf, result)); |
| + buffers_.push(drainable_buffer); |
| + |
|
vandebo (ex-Chrome)
2014/03/03 22:24:54
Should this kick off another read if buffers_.size
tommycli
2014/03/04 00:39:40
The ConsumeFromBuffer call within this if-block sh
|
| + // If there's a read request waiting for the underlying FileStreamReader to |
| + // finish reading, fulfill that request now. |
| + if (pending_read_.get()) { |
| + // Free up pending read immediately, as the read completion callback often |
| + // dispatches another read. |
| + scoped_ptr<Request> request(pending_read_.Pass()); |
| + |
| + request->callback.Run(ConsumeFromBuffer(request->buf, request->buf_len)); |
| + } |
| +} |