| // 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 "net/base/chunked_upload_data_stream.h" | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "net/base/io_buffer.h" | 
 | #include "net/base/net_errors.h" | 
 |  | 
 | #include "starboard/client_porting/poem/string_poem.h" | 
 | #include "starboard/memory.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | ChunkedUploadDataStream::Writer::~Writer() = default; | 
 |  | 
 | bool ChunkedUploadDataStream::Writer::AppendData(const char* data, | 
 |                                                  int data_len, | 
 |                                                  bool is_done) { | 
 |   if (!upload_data_stream_) | 
 |     return false; | 
 |   upload_data_stream_->AppendData(data, data_len, is_done); | 
 |   return true; | 
 | } | 
 |  | 
 | ChunkedUploadDataStream::Writer::Writer( | 
 |     base::WeakPtr<ChunkedUploadDataStream> upload_data_stream) | 
 |     : upload_data_stream_(upload_data_stream) {} | 
 |  | 
 | ChunkedUploadDataStream::ChunkedUploadDataStream(int64_t identifier) | 
 |     : UploadDataStream(true, identifier), | 
 |       read_index_(0), | 
 |       read_offset_(0), | 
 |       all_data_appended_(false), | 
 |       read_buffer_len_(0), | 
 |       weak_factory_(this) {} | 
 |  | 
 | ChunkedUploadDataStream::~ChunkedUploadDataStream() = default; | 
 |  | 
 | std::unique_ptr<ChunkedUploadDataStream::Writer> | 
 | ChunkedUploadDataStream::CreateWriter() { | 
 |   return base::WrapUnique(new Writer(weak_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void ChunkedUploadDataStream::AppendData( | 
 |     const char* data, int data_len, bool is_done) { | 
 |   DCHECK(!all_data_appended_); | 
 |   DCHECK(data_len > 0 || is_done); | 
 |   if (data_len > 0) { | 
 |     DCHECK(data); | 
 |     upload_data_.push_back( | 
 |         std::make_unique<std::vector<char>>(data, data + data_len)); | 
 |   } | 
 |   all_data_appended_ = is_done; | 
 |  | 
 |   if (!read_buffer_.get()) | 
 |     return; | 
 |  | 
 |   int result = ReadChunk(read_buffer_.get(), read_buffer_len_); | 
 |   // Shouldn't get an error or ERR_IO_PENDING. | 
 |   DCHECK_GE(result, 0); | 
 |   read_buffer_ = NULL; | 
 |   read_buffer_len_ = 0; | 
 |   OnReadCompleted(result); | 
 | } | 
 |  | 
 | int ChunkedUploadDataStream::InitInternal(const NetLogWithSource& net_log) { | 
 |   // ResetInternal should already have been called. | 
 |   DCHECK(!read_buffer_.get()); | 
 |   DCHECK_EQ(0u, read_index_); | 
 |   DCHECK_EQ(0u, read_offset_); | 
 |   return OK; | 
 | } | 
 |  | 
 | int ChunkedUploadDataStream::ReadInternal(IOBuffer* buf, int buf_len) { | 
 |   DCHECK_LT(0, buf_len); | 
 |   DCHECK(!read_buffer_.get()); | 
 |  | 
 |   int result = ReadChunk(buf, buf_len); | 
 |   if (result == ERR_IO_PENDING) { | 
 |     read_buffer_ = buf; | 
 |     read_buffer_len_ = buf_len; | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | void ChunkedUploadDataStream::ResetInternal() { | 
 |   read_buffer_ = NULL; | 
 |   read_buffer_len_ = 0; | 
 |   read_index_ = 0; | 
 |   read_offset_ = 0; | 
 | } | 
 |  | 
 | int ChunkedUploadDataStream::ReadChunk(IOBuffer* buf, int buf_len) { | 
 |   // Copy as much data as possible from |upload_data_| to |buf|. | 
 |   int bytes_read = 0; | 
 |   while (read_index_ < upload_data_.size() && bytes_read < buf_len) { | 
 |     std::vector<char>* data = upload_data_[read_index_].get(); | 
 |     size_t bytes_to_read = | 
 |         std::min(static_cast<size_t>(buf_len - bytes_read), | 
 |                  data->size() - read_offset_); | 
 |     memcpy(buf->data() + bytes_read, data->data() + read_offset_, | 
 |                  bytes_to_read); | 
 |     bytes_read += bytes_to_read; | 
 |     read_offset_ += bytes_to_read; | 
 |     if (read_offset_ == data->size()) { | 
 |       read_index_++; | 
 |       read_offset_ = 0; | 
 |     } | 
 |   } | 
 |   DCHECK_LE(bytes_read, buf_len); | 
 |  | 
 |   // If no data was written, and not all data has been appended, return | 
 |   // ERR_IO_PENDING. The read will be completed in the next call to AppendData. | 
 |   if (bytes_read == 0 && !all_data_appended_) | 
 |     return ERR_IO_PENDING; | 
 |  | 
 |   if (read_index_ == upload_data_.size() && all_data_appended_) | 
 |     SetIsFinalChunk(); | 
 |   return bytes_read; | 
 | } | 
 |  | 
 | }  // namespace net |