text: // Copyright (c) 2006-2008 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/upload_data_stream.h"
  
  #include "base/logging.h"
  #include "net/base/io_buffer.h"
  #include "net/base/net_errors.h"
  
  namespace net {
  
  UploadDataStream::UploadDataStream(const UploadData* data)
      : data_(data),
        buf_(new IOBuffer(kBufSize)),
        buf_len_(0),
        next_element_(data->elements().begin()),
        next_element_offset_(0),
        next_element_remaining_(0),
        total_size_(data->GetContentLength()),
        current_position_(0) {
    FillBuf();
  }
  
  UploadDataStream::~UploadDataStream() {
  }
  
  void UploadDataStream::DidConsume(size_t num_bytes) {
    // TODO(vandebo): Change back to a DCHECK when issue 27870 is resolved.
    CHECK(num_bytes <= buf_len_);
  
    buf_len_ -= num_bytes;
    if (buf_len_)
      memmove(buf_->data(), buf_->data() + num_bytes, buf_len_);
  
    FillBuf();
  
    current_position_ += num_bytes;
  }
  
  void UploadDataStream::FillBuf() {
    std::vector<UploadData::Element>::const_iterator end =
        data_->elements().end();
  
    while (buf_len_ < kBufSize && next_element_ != end) {
      bool advance_to_next_element = false;
  
      const UploadData::Element& element = *next_element_;
  
      size_t size_remaining = kBufSize - buf_len_;
      if (element.type() == UploadData::TYPE_BYTES) {
        const std::vector<char>& d = element.bytes();
        size_t count = d.size() - next_element_offset_;
  
        size_t bytes_copied = std::min(count, size_remaining);
  
        memcpy(buf_->data() + buf_len_, &d[next_element_offset_], bytes_copied);
        buf_len_ += bytes_copied;
  
        if (bytes_copied == count) {
          advance_to_next_element = true;
        } else {
          next_element_offset_ += bytes_copied;
        }
      } else {
        DCHECK(element.type() == UploadData::TYPE_FILE);
  
        if (!next_element_stream_.IsOpen()) {
          int flags = base::PLATFORM_FILE_OPEN |
                      base::PLATFORM_FILE_READ;
          int rv = next_element_stream_.Open(element.file_path(), flags);
          // If the file does not exist, that's technically okay.. we'll just
          // upload an empty file.  This is for consistency with Mozilla.
          DLOG_IF(WARNING, rv != OK) << "Failed to open \""
                                     << element.file_path().value()
                                     << "\" for reading: " << rv;
  
          next_element_remaining_ = 0;  // Default to reading nothing.
          if (rv == OK) {
            uint64 offset = element.file_range_offset();
            if (offset && next_element_stream_.Seek(FROM_BEGIN, offset) < 0) {
              DLOG(WARNING) << "Failed to seek \"" << element.file_path().value()
                            << "\" to offset: " << offset;
            } else {
              next_element_remaining_ = element.file_range_length();
            }
          }
        }
  
        int rv = 0;
        int count = static_cast<int>(std::min(
            static_cast<uint64>(size_remaining), next_element_remaining_));
        if (count > 0 &&
            (rv = next_element_stream_.Read(buf_->data() + buf_len_,
                                            count, NULL)) > 0) {
          buf_len_ += rv;
          next_element_remaining_ -= rv;
        } else {
          advance_to_next_element = true;
        }
      }
  
      if (advance_to_next_element) {
        ++next_element_;
        next_element_offset_ = 0;
        next_element_stream_.Close();
      }
    }
  }
  
  }  // namespace net
  