// Copyright (c) 2010 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 "pdf/chunk_stream.h" #define __STDC_LIMIT_MACROS #ifdef _WIN32 #include <limits.h> #else #include <stdint.h> #endif #include <algorithm> #include "base/basictypes.h" namespace chrome_pdf { ChunkStream::ChunkStream() { } ChunkStream::~ChunkStream() { } void ChunkStream::Clear() { chunks_.clear(); data_.clear(); } void ChunkStream::Preallocate(size_t stream_size) { data_.reserve(stream_size); } size_t ChunkStream::GetSize() { return data_.size(); } bool ChunkStream::WriteData(size_t offset, void* buffer, size_t size) { if (SIZE_MAX - size < offset) return false; if (data_.size() < offset + size) data_.resize(offset + size); memcpy(&data_[offset], buffer, size); if (chunks_.empty()) { chunks_[offset] = size; return true; } std::map<size_t, size_t>::iterator start = chunks_.upper_bound(offset); if (start != chunks_.begin()) --start; // start now points to the key equal or lower than offset. if (start->first + start->second < offset) ++start; // start element is entirely before current chunk, skip it. std::map<size_t, size_t>::iterator end = chunks_.upper_bound(offset + size); if (start == end) { // No chunks to merge. chunks_[offset] = size; return true; } --end; size_t new_offset = std::min<size_t>(start->first, offset); size_t new_size = std::max<size_t>(end->first + end->second, offset + size) - new_offset; chunks_.erase(start, ++end); chunks_[new_offset] = new_size; return true; } bool ChunkStream::ReadData(size_t offset, size_t size, void* buffer) const { if (!IsRangeAvailable(offset, size)) return false; memcpy(buffer, &data_[offset], size); return true; } bool ChunkStream::GetMissedRanges( size_t offset, size_t size, std::vector<std::pair<size_t, size_t> >* ranges) const { if (IsRangeAvailable(offset, size)) return false; ranges->clear(); if (chunks_.empty()) { ranges->push_back(std::pair<size_t, size_t>(offset, size)); return true; } std::map<size_t, size_t>::const_iterator start = chunks_.upper_bound(offset); if (start != chunks_.begin()) --start; // start now points to the key equal or lower than offset. if (start->first + start->second < offset) ++start; // start element is entirely before current chunk, skip it. std::map<size_t, size_t>::const_iterator end = chunks_.upper_bound(offset + size); if (start == end) { // No data in the current range available. ranges->push_back(std::pair<size_t, size_t>(offset, size)); return true; } size_t cur_offset = offset; std::map<size_t, size_t>::const_iterator it; for (it = start; it != end; ++it) { if (cur_offset < it->first) { size_t new_size = it->first - cur_offset; ranges->push_back(std::pair<size_t, size_t>(cur_offset, new_size)); cur_offset = it->first + it->second; } else if (cur_offset < it->first + it->second) { cur_offset = it->first + it->second; } } // Add last chunk. if (cur_offset < offset + size) ranges->push_back(std::pair<size_t, size_t>(cur_offset, offset + size - cur_offset)); return true; } bool ChunkStream::IsRangeAvailable(size_t offset, size_t size) const { if (chunks_.empty()) return false; if (SIZE_MAX - size < offset) return false; std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset); if (it == chunks_.begin()) return false; // No chunks includes offset byte. --it; // Now it starts equal or before offset. return (it->first + it->second) >= (offset + size); } size_t ChunkStream::GetFirstMissingByte() const { if (chunks_.empty()) return 0; std::map<size_t, size_t>::const_iterator begin = chunks_.begin(); return begin->first > 0 ? 0 : begin->second; } size_t ChunkStream::GetLastByteBefore(size_t offset) const { if (chunks_.empty()) return 0; std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset); if (it == chunks_.begin()) return 0; --it; return it->first + it->second; } size_t ChunkStream::GetFirstByteAfter(size_t offset) const { if (chunks_.empty()) return 0; std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset); if (it == chunks_.end()) return data_.size(); return it->first; } } // namespace chrome_pdf