1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h"
6
7#include <algorithm>
8
9#include "net/base/io_buffer.h"
10#include "net/base/net_errors.h"
11#include "storage/browser/fileapi/file_system_backend.h"
12
13namespace chromeos {
14namespace file_system_provider {
15
16BufferingFileStreamReader::BufferingFileStreamReader(
17    scoped_ptr<storage::FileStreamReader> file_stream_reader,
18    int preloading_buffer_length,
19    int64 max_bytes_to_read)
20    : file_stream_reader_(file_stream_reader.Pass()),
21      preloading_buffer_length_(preloading_buffer_length),
22      max_bytes_to_read_(max_bytes_to_read),
23      bytes_read_(0),
24      preloading_buffer_(new net::IOBuffer(preloading_buffer_length)),
25      preloading_buffer_offset_(0),
26      preloaded_bytes_(0),
27      weak_ptr_factory_(this) {
28}
29
30BufferingFileStreamReader::~BufferingFileStreamReader() {
31}
32
33int BufferingFileStreamReader::Read(net::IOBuffer* buffer,
34                                    int buffer_length,
35                                    const net::CompletionCallback& callback) {
36  // Return as much as available in the internal buffer. It may be less than
37  // |buffer_length|, what is valid.
38  const int bytes_read =
39      CopyFromPreloadingBuffer(make_scoped_refptr(buffer), buffer_length);
40  if (bytes_read)
41    return bytes_read;
42
43  // If the internal buffer is empty, and more bytes than the internal buffer
44  // size is requested, then call the internal file stream reader directly.
45  if (buffer_length >= preloading_buffer_length_) {
46    const int result = file_stream_reader_->Read(
47        buffer,
48        buffer_length,
49        base::Bind(&BufferingFileStreamReader::OnReadCompleted,
50                   weak_ptr_factory_.GetWeakPtr(),
51                   callback));
52    DCHECK_EQ(result, net::ERR_IO_PENDING);
53    return result;
54  }
55
56  // Nothing copied, so contents have to be preloaded.
57  Preload(base::Bind(&BufferingFileStreamReader::OnReadCompleted,
58                     weak_ptr_factory_.GetWeakPtr(),
59                     base::Bind(&BufferingFileStreamReader::OnPreloadCompleted,
60                                weak_ptr_factory_.GetWeakPtr(),
61                                make_scoped_refptr(buffer),
62                                buffer_length,
63                                callback)));
64
65  return net::ERR_IO_PENDING;
66}
67
68int64 BufferingFileStreamReader::GetLength(
69    const net::Int64CompletionCallback& callback) {
70  const int64 result = file_stream_reader_->GetLength(callback);
71  DCHECK_EQ(net::ERR_IO_PENDING, result);
72
73  return result;
74}
75
76int BufferingFileStreamReader::CopyFromPreloadingBuffer(
77    scoped_refptr<net::IOBuffer> buffer,
78    int buffer_length) {
79  const int read_bytes = std::min(buffer_length, preloaded_bytes_);
80
81  memcpy(buffer->data(),
82         preloading_buffer_->data() + preloading_buffer_offset_,
83         read_bytes);
84  preloading_buffer_offset_ += read_bytes;
85  preloaded_bytes_ -= read_bytes;
86
87  return read_bytes;
88}
89
90void BufferingFileStreamReader::Preload(
91    const net::CompletionCallback& callback) {
92  const int preload_bytes =
93      std::min(static_cast<int64>(preloading_buffer_length_),
94               max_bytes_to_read_ - bytes_read_);
95
96  const int result = file_stream_reader_->Read(
97      preloading_buffer_.get(), preload_bytes, callback);
98  DCHECK_EQ(result, net::ERR_IO_PENDING);
99}
100
101void BufferingFileStreamReader::OnPreloadCompleted(
102    scoped_refptr<net::IOBuffer> buffer,
103    int buffer_length,
104    const net::CompletionCallback& callback,
105    int result) {
106  if (result < 0) {
107    callback.Run(result);
108    return;
109  }
110
111  preloading_buffer_offset_ = 0;
112  preloaded_bytes_ = result;
113
114  callback.Run(CopyFromPreloadingBuffer(buffer, buffer_length));
115}
116
117void BufferingFileStreamReader::OnReadCompleted(
118    const net::CompletionCallback& callback,
119    int result) {
120  if (result < 0) {
121    callback.Run(result);
122    return;
123  }
124
125  // If more bytes than declared in |max_bytes_to_read_| was read in total, then
126  // emit an
127  // error.
128  if (result > max_bytes_to_read_ - bytes_read_) {
129    callback.Run(net::ERR_FAILED);
130    return;
131  }
132
133  bytes_read_ += result;
134  DCHECK_LE(bytes_read_, max_bytes_to_read_);
135
136  callback.Run(result);
137}
138
139}  // namespace file_system_provider
140}  // namespace chromeos
141