|  | // 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_); | 
|  | SbMemoryCopy(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 |