Home | History | Annotate | Download | only in pdf
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "pdf/chunk_stream.h"
      6 
      7 #define __STDC_LIMIT_MACROS
      8 #ifdef _WIN32
      9 #include <limits.h>
     10 #else
     11 #include <stdint.h>
     12 #endif
     13 
     14 #include <algorithm>
     15 
     16 #include "base/basictypes.h"
     17 
     18 namespace chrome_pdf {
     19 
     20 ChunkStream::ChunkStream() {
     21 }
     22 
     23 ChunkStream::~ChunkStream() {
     24 }
     25 
     26 void ChunkStream::Clear() {
     27   chunks_.clear();
     28   data_.clear();
     29 }
     30 
     31 void ChunkStream::Preallocate(size_t stream_size) {
     32   data_.reserve(stream_size);
     33 }
     34 
     35 size_t ChunkStream::GetSize() {
     36   return data_.size();
     37 }
     38 
     39 bool ChunkStream::WriteData(size_t offset, void* buffer, size_t size) {
     40   if (SIZE_MAX - size < offset)
     41     return false;
     42 
     43   if (data_.size() < offset + size)
     44     data_.resize(offset + size);
     45 
     46   memcpy(&data_[offset], buffer, size);
     47 
     48   if (chunks_.empty()) {
     49     chunks_[offset] = size;
     50     return true;
     51   }
     52 
     53   std::map<size_t, size_t>::iterator start = chunks_.upper_bound(offset);
     54   if (start != chunks_.begin())
     55     --start;  // start now points to the key equal or lower than offset.
     56   if (start->first + start->second < offset)
     57     ++start;  // start element is entirely before current chunk, skip it.
     58 
     59   std::map<size_t, size_t>::iterator end = chunks_.upper_bound(offset + size);
     60   if (start == end) {  // No chunks to merge.
     61     chunks_[offset] = size;
     62     return true;
     63   }
     64 
     65   --end;
     66 
     67   size_t new_offset = std::min<size_t>(start->first, offset);
     68   size_t new_size =
     69       std::max<size_t>(end->first + end->second, offset + size) - new_offset;
     70 
     71   chunks_.erase(start, ++end);
     72 
     73   chunks_[new_offset] = new_size;
     74 
     75   return true;
     76 }
     77 
     78 bool ChunkStream::ReadData(size_t offset, size_t size, void* buffer) const {
     79   if (!IsRangeAvailable(offset, size))
     80     return false;
     81 
     82   memcpy(buffer, &data_[offset], size);
     83   return true;
     84 }
     85 
     86 bool ChunkStream::GetMissedRanges(
     87     size_t offset, size_t size,
     88     std::vector<std::pair<size_t, size_t> >* ranges) const {
     89   if (IsRangeAvailable(offset, size))
     90     return false;
     91 
     92   ranges->clear();
     93   if (chunks_.empty()) {
     94     ranges->push_back(std::pair<size_t, size_t>(offset, size));
     95     return true;
     96   }
     97 
     98   std::map<size_t, size_t>::const_iterator start = chunks_.upper_bound(offset);
     99   if (start != chunks_.begin())
    100     --start;  // start now points to the key equal or lower than offset.
    101   if (start->first + start->second < offset)
    102     ++start;  // start element is entirely before current chunk, skip it.
    103 
    104   std::map<size_t, size_t>::const_iterator end =
    105       chunks_.upper_bound(offset + size);
    106   if (start == end) {  // No data in the current range available.
    107     ranges->push_back(std::pair<size_t, size_t>(offset, size));
    108     return true;
    109   }
    110 
    111   size_t cur_offset = offset;
    112   std::map<size_t, size_t>::const_iterator it;
    113   for (it = start; it != end; ++it) {
    114     if (cur_offset < it->first) {
    115       size_t new_size = it->first - cur_offset;
    116       ranges->push_back(std::pair<size_t, size_t>(cur_offset, new_size));
    117       cur_offset = it->first + it->second;
    118     } else if (cur_offset < it->first + it->second) {
    119       cur_offset = it->first + it->second;
    120     }
    121   }
    122 
    123   // Add last chunk.
    124   if (cur_offset < offset + size)
    125     ranges->push_back(std::pair<size_t, size_t>(cur_offset,
    126         offset + size - cur_offset));
    127 
    128   return true;
    129 }
    130 
    131 bool ChunkStream::IsRangeAvailable(size_t offset, size_t size) const {
    132   if (chunks_.empty())
    133     return false;
    134 
    135   if (SIZE_MAX - size < offset)
    136     return false;
    137 
    138   std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset);
    139   if (it == chunks_.begin())
    140     return false;  // No chunks includes offset byte.
    141 
    142   --it;  // Now it starts equal or before offset.
    143   return (it->first + it->second) >= (offset + size);
    144 }
    145 
    146 size_t ChunkStream::GetFirstMissingByte() const {
    147   if (chunks_.empty())
    148     return 0;
    149   std::map<size_t, size_t>::const_iterator begin = chunks_.begin();
    150   return begin->first > 0 ? 0 : begin->second;
    151 }
    152 
    153 size_t ChunkStream::GetLastByteBefore(size_t offset) const {
    154   if (chunks_.empty())
    155     return 0;
    156   std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset);
    157   if (it == chunks_.begin())
    158     return 0;
    159   --it;
    160   return it->first + it->second;
    161 }
    162 
    163 size_t ChunkStream::GetFirstByteAfter(size_t offset) const {
    164   if (chunks_.empty())
    165     return 0;
    166   std::map<size_t, size_t>::const_iterator it = chunks_.upper_bound(offset);
    167   if (it == chunks_.end())
    168     return data_.size();
    169   return it->first;
    170 }
    171 
    172 }  // namespace chrome_pdf
    173