Index: chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc |
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc b/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..039f0a6baa30f280e04379c8093b7f7c5647c703 |
--- /dev/null |
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.cc |
@@ -0,0 +1,184 @@ |
+// 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/chromeos/file_system_provider/fileapi/buffering_file_stream_writer.h" |
+ |
+#include <algorithm> |
+ |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+ |
+namespace chromeos { |
+namespace file_system_provider { |
+ |
+BufferingFileStreamWriter::BufferingFileStreamWriter( |
+ scoped_ptr<storage::FileStreamWriter> file_stream_writer, |
+ int intermediate_buffer_length) |
+ : file_stream_writer_(file_stream_writer.Pass()), |
+ intermediate_buffer_length_(intermediate_buffer_length), |
+ intermediate_buffer_(new net::IOBuffer(intermediate_buffer_length_)), |
+ buffered_bytes_(0), |
+ weak_ptr_factory_(this) { |
+} |
+ |
+BufferingFileStreamWriter::~BufferingFileStreamWriter() { |
+ if (buffered_bytes_) |
+ LOG(ERROR) << "File stream writer not flushed. Data will be lost."; |
+} |
+ |
+int BufferingFileStreamWriter::Write(net::IOBuffer* buffer, |
+ int buffer_length, |
+ const net::CompletionCallback& callback) { |
+ // If |buffer_length| is larger than the intermediate buffer, then call the |
+ // inner file stream writer directly. Note, that the intermediate buffer |
+ // (used for buffering) must be flushed first. |
+ if (buffer_length > intermediate_buffer_length_) { |
+ if (buffered_bytes_) { |
+ FlushIntermediateBuffer( |
+ base::Bind(&BufferingFileStreamWriter:: |
+ OnFlushIntermediateBufferForDirectWriteCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ make_scoped_refptr(buffer), |
+ buffer_length, |
+ callback)); |
+ } else { |
+ // Nothing to flush, so skip it. |
+ OnFlushIntermediateBufferForDirectWriteCompleted( |
+ make_scoped_refptr(buffer), buffer_length, callback, net::OK); |
+ } |
+ return net::ERR_IO_PENDING; |
+ } |
+ |
+ // Buffer consecutive writes to larger chunks. |
+ const int buffer_bytes = |
+ std::min(intermediate_buffer_length_ - buffered_bytes_, buffer_length); |
+ |
+ CopyToIntermediateBuffer( |
+ make_scoped_refptr(buffer), 0 /* buffer_offset */, buffer_bytes); |
+ const int bytes_left = buffer_length - buffer_bytes; |
+ |
+ if (buffered_bytes_ == intermediate_buffer_length_) { |
+ FlushIntermediateBuffer( |
+ base::Bind(&BufferingFileStreamWriter:: |
+ OnFlushIntermediateBufferForBufferedWriteCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ make_scoped_refptr(buffer), |
+ buffer_bytes, |
+ bytes_left, |
+ callback)); |
+ return net::ERR_IO_PENDING; |
+ } |
+ |
+ // Optimistically return a success. |
+ return buffer_length; |
+} |
+ |
+int BufferingFileStreamWriter::Cancel(const net::CompletionCallback& callback) { |
+ // Since there is no any asynchronous call in this class other than on |
+ // |file_stream_writer_|, then there must be an in-flight operation going on. |
+ return file_stream_writer_->Cancel(callback); |
+} |
+ |
+int BufferingFileStreamWriter::Flush(const net::CompletionCallback& callback) { |
+ // Flush all the buffered bytes first, then invoke Flush() on the inner file |
+ // stream writer. |
+ FlushIntermediateBuffer(base::Bind( |
+ &BufferingFileStreamWriter::OnFlushIntermediateBufferForFlushCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ callback)); |
+ return net::ERR_IO_PENDING; |
+} |
+ |
+void BufferingFileStreamWriter::CopyToIntermediateBuffer( |
+ scoped_refptr<net::IOBuffer> buffer, |
+ int buffer_offset, |
+ int buffer_length) { |
+ DCHECK_GE(intermediate_buffer_length_, buffer_length + buffered_bytes_); |
+ memcpy(intermediate_buffer_->data() + buffered_bytes_, |
+ buffer->data() + buffer_offset, |
+ buffer_length); |
+ buffered_bytes_ += buffer_length; |
+} |
+ |
+void BufferingFileStreamWriter::FlushIntermediateBuffer( |
+ const net::CompletionCallback& callback) { |
+ const int result = file_stream_writer_->Write( |
+ intermediate_buffer_.get(), |
+ buffered_bytes_, |
+ base::Bind(&BufferingFileStreamWriter::OnFlushIntermediateBufferCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ buffered_bytes_, |
+ callback)); |
+ DCHECK_EQ(net::ERR_IO_PENDING, result); |
+} |
+ |
+void BufferingFileStreamWriter::OnFlushIntermediateBufferCompleted( |
+ int length, |
+ const net::CompletionCallback& callback, |
+ int result) { |
+ if (result < 0) { |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ DCHECK_EQ(length, result) << "Partial writes are not supported."; |
+ buffered_bytes_ = 0; |
+ |
+ callback.Run(net::OK); |
+} |
+ |
+void |
+BufferingFileStreamWriter::OnFlushIntermediateBufferForDirectWriteCompleted( |
+ scoped_refptr<net::IOBuffer> buffer, |
+ int length, |
+ const net::CompletionCallback& callback, |
+ int result) { |
+ if (result < 0) { |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ // The following logic is only valid if the intermediate buffer is empty. |
+ DCHECK_EQ(0, buffered_bytes_); |
+ |
+ const int write_result = |
+ file_stream_writer_->Write(buffer.get(), length, callback); |
+ DCHECK_EQ(net::ERR_IO_PENDING, write_result); |
+} |
+ |
+void |
+BufferingFileStreamWriter::OnFlushIntermediateBufferForBufferedWriteCompleted( |
+ scoped_refptr<net::IOBuffer> buffer, |
+ int buffered_bytes, |
+ int bytes_left, |
+ const net::CompletionCallback& callback, |
+ int result) { |
+ if (result < 0) { |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ // Copy the rest of bytes to the buffer. |
+ DCHECK_EQ(net::OK, result); |
+ DCHECK_EQ(0, buffered_bytes_); |
+ DCHECK_GE(intermediate_buffer_length_, bytes_left); |
+ CopyToIntermediateBuffer(buffer, buffered_bytes, bytes_left); |
+ |
+ callback.Run(buffered_bytes + bytes_left); |
+} |
+ |
+void BufferingFileStreamWriter::OnFlushIntermediateBufferForFlushCompleted( |
+ const net::CompletionCallback& callback, |
+ int result) { |
+ if (result < 0) { |
+ callback.Run(result); |
+ return; |
+ } |
+ |
+ const int flush_result = file_stream_writer_->Flush(callback); |
+ DCHECK_EQ(net::ERR_IO_PENDING, flush_result); |
+} |
+ |
+} // namespace file_system_provider |
+} // namespace chromeos |