blob: 879671e9f9d06c7c3e8883bd0588a4a669a3dee1 [file] [log] [blame]
// Copyright 2017 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/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.h"
#include <algorithm>
#include <cstring>
#include <limits>
#include "chrome/browser/resources/chromeos/zip_archiver/cpp/javascript_compressor_requestor_interface.h"
#include "ppapi/cpp/logging.h"
namespace {
// We need at least 256KB for MiniZip.
const int64_t kMaximumDataChunkSize = 512 * 1024;
} // namespace
CompressorIOJavaScriptStream::CompressorIOJavaScriptStream(
JavaScriptCompressorRequestorInterface* requestor)
: requestor_(requestor), buffer_offset_(-1), buffer_data_length_(0) {
pthread_mutex_init(&shared_state_lock_, nullptr);
pthread_cond_init(&available_data_cond_, nullptr);
pthread_cond_init(&data_written_cond_, nullptr);
pthread_mutex_lock(&shared_state_lock_);
available_data_ = false;
buffer_ = new char[kMaximumDataChunkSize];
pthread_mutex_unlock(&shared_state_lock_);
}
CompressorIOJavaScriptStream::~CompressorIOJavaScriptStream() {
pthread_mutex_lock(&shared_state_lock_);
delete buffer_;
pthread_mutex_unlock(&shared_state_lock_);
pthread_cond_destroy(&data_written_cond_);
pthread_cond_destroy(&available_data_cond_);
pthread_mutex_destroy(&shared_state_lock_);
}
int64_t CompressorIOJavaScriptStream::Flush() {
pthread_mutex_lock(&shared_state_lock_);
if (buffer_data_length_ == 0) {
pthread_mutex_unlock(&shared_state_lock_);
return 0;
}
// Copy the data in buffer_ to array_buffer.
pp::VarArrayBuffer array_buffer(buffer_data_length_);
char* array_buffer_data = static_cast<char*>(array_buffer.Map());
memcpy(array_buffer_data, buffer_, buffer_data_length_);
array_buffer.Unmap();
requestor_->WriteChunkRequest(buffer_offset_, buffer_data_length_,
array_buffer);
pthread_cond_wait(&data_written_cond_, &shared_state_lock_);
int64_t written_bytes = written_bytes_;
if (written_bytes < buffer_data_length_) {
pthread_mutex_unlock(&shared_state_lock_);
return -1 /* Error */;
}
// Reset the offset and length to the default values.
buffer_offset_ = -1;
buffer_data_length_ = 0;
pthread_mutex_unlock(&shared_state_lock_);
return written_bytes;
}
int64_t CompressorIOJavaScriptStream::Write(int64_t zip_offset,
int64_t zip_length,
const char* zip_buffer) {
pthread_mutex_lock(&shared_state_lock_);
// The offset from which the data should be written onto the archive.
int64_t current_offset = zip_offset;
int64_t left_length = zip_length;
const char* buffer_pointer = zip_buffer;
do {
// Flush the buffer if the data in the buffer cannot be reused.
// The following is the brief explanation about the conditions of the
// following if statement.
// 1: The buffer is not in the initial state (empty).
// The buffer should have some data to flush.
// 2: This write operation is not to append the data to the buffer.
// 3: The buffer overflows if we append the data to the buffer.
// If we can append the new data to the current data in the buffer,
// we should not flush the buffer.
// 4: The index to write is outside the buffer.
// If we want to write data outside the range, we first need to flush
// the buffer, and then cache the data in the buffer.
if (buffer_offset_ >= 0 && /* 1 */
(current_offset != buffer_offset_ + buffer_data_length_ || /* 2 */
buffer_data_length_ + left_length > kMaximumDataChunkSize) && /* 3 */
(current_offset < buffer_offset_ ||
buffer_offset_ + buffer_data_length_ <
current_offset + left_length) /* 4 */) {
pthread_mutex_unlock(&shared_state_lock_);
int64_t bytes_to_write = buffer_data_length_;
if (Flush() != bytes_to_write)
return -1;
pthread_mutex_lock(&shared_state_lock_);
}
// How many bytes we should copy to buffer_ in this iteration.
int64_t copy_length = std::min(left_length, kMaximumDataChunkSize);
// Set up the buffer_offset_ if the buffer_ has no data.
if (buffer_offset_ == -1 /* initial state */)
buffer_offset_ = current_offset;
// Calculate the relative offset from left_length.
int64_t offset_in_buffer = current_offset - buffer_offset_;
// Copy data from zip_buffer, which is pointed by buffer_pointer, to
// buffer_.
memcpy(buffer_ + offset_in_buffer, buffer_pointer, copy_length);
buffer_pointer += copy_length;
buffer_data_length_ =
std::max(buffer_data_length_, offset_in_buffer + copy_length);
current_offset += copy_length;
left_length -= copy_length;
} while (left_length > 0);
pthread_mutex_unlock(&shared_state_lock_);
return zip_length;
}
int64_t CompressorIOJavaScriptStream::WriteChunkDone(int64_t written_bytes) {
pthread_mutex_lock(&shared_state_lock_);
written_bytes_ = written_bytes;
pthread_cond_signal(&data_written_cond_);
pthread_mutex_unlock(&shared_state_lock_);
return written_bytes;
}
int64_t CompressorIOJavaScriptStream::Read(int64_t bytes_to_read,
char* destination_buffer) {
pthread_mutex_lock(&shared_state_lock_);
destination_buffer_ = destination_buffer;
requestor_->ReadFileChunkRequest(bytes_to_read);
while (!available_data_) {
pthread_cond_wait(&available_data_cond_, &shared_state_lock_);
}
int64_t read_bytes = read_bytes_;
available_data_ = false;
pthread_mutex_unlock(&shared_state_lock_);
return read_bytes;
}
int64_t CompressorIOJavaScriptStream::ReadFileChunkDone(
int64_t read_bytes,
pp::VarArrayBuffer* array_buffer) {
pthread_mutex_lock(&shared_state_lock_);
// JavaScript sets a negative value in read_bytes if an error occurred while
// reading a chunk.
if (read_bytes >= 0) {
char* array_buffer_data = static_cast<char*>(array_buffer->Map());
memcpy(destination_buffer_, array_buffer_data, read_bytes);
array_buffer->Unmap();
}
read_bytes_ = read_bytes;
available_data_ = true;
pthread_cond_signal(&available_data_cond_);
pthread_mutex_unlock(&shared_state_lock_);
return read_bytes;
}