Home | History | Annotate | Download | only in speech
      1 // Copyright (c) 2012 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 "content/browser/speech/chunked_byte_buffer.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/logging.h"
     12 
     13 namespace {
     14 
     15 static const size_t kHeaderLength = sizeof(uint32);
     16 
     17 COMPILE_ASSERT(sizeof(size_t) >= kHeaderLength,
     18                ChunkedByteBufferNotSupportedOnThisArchitecture);
     19 
     20 uint32 ReadBigEndian32(const uint8* buffer) {
     21   return (static_cast<uint32>(buffer[3])) |
     22          (static_cast<uint32>(buffer[2]) <<  8) |
     23          (static_cast<uint32>(buffer[1]) << 16) |
     24          (static_cast<uint32>(buffer[0]) << 24);
     25 }
     26 
     27 }  // namespace
     28 
     29 namespace content {
     30 
     31 ChunkedByteBuffer::ChunkedByteBuffer()
     32     : partial_chunk_(new Chunk()),
     33       total_bytes_stored_(0) {
     34 }
     35 
     36 ChunkedByteBuffer::~ChunkedByteBuffer() {
     37   Clear();
     38 }
     39 
     40 void ChunkedByteBuffer::Append(const uint8* start, size_t length) {
     41   size_t remaining_bytes = length;
     42   const uint8* next_data = start;
     43 
     44   while (remaining_bytes > 0) {
     45     DCHECK(partial_chunk_ != NULL);
     46     size_t insert_length = 0;
     47     bool header_completed = false;
     48     bool content_completed = false;
     49     std::vector<uint8>* insert_target;
     50 
     51     if (partial_chunk_->header.size() < kHeaderLength) {
     52       const size_t bytes_to_complete_header =
     53           kHeaderLength - partial_chunk_->header.size();
     54       insert_length = std::min(bytes_to_complete_header, remaining_bytes);
     55       insert_target = &partial_chunk_->header;
     56       header_completed = (remaining_bytes >= bytes_to_complete_header);
     57     } else {
     58       DCHECK_LT(partial_chunk_->content->size(),
     59                 partial_chunk_->ExpectedContentLength());
     60       const size_t bytes_to_complete_chunk =
     61           partial_chunk_->ExpectedContentLength() -
     62           partial_chunk_->content->size();
     63       insert_length = std::min(bytes_to_complete_chunk, remaining_bytes);
     64       insert_target = partial_chunk_->content.get();
     65       content_completed = (remaining_bytes >= bytes_to_complete_chunk);
     66     }
     67 
     68     DCHECK_GT(insert_length, 0U);
     69     DCHECK_LE(insert_length, remaining_bytes);
     70     DCHECK_LE(next_data + insert_length, start + length);
     71     insert_target->insert(insert_target->end(),
     72                           next_data,
     73                           next_data + insert_length);
     74     next_data += insert_length;
     75     remaining_bytes -= insert_length;
     76 
     77     if (header_completed) {
     78       DCHECK_EQ(partial_chunk_->header.size(), kHeaderLength);
     79       if (partial_chunk_->ExpectedContentLength() == 0) {
     80         // Handle zero-byte chunks.
     81         chunks_.push_back(partial_chunk_.release());
     82         partial_chunk_.reset(new Chunk());
     83       } else {
     84         partial_chunk_->content->reserve(
     85             partial_chunk_->ExpectedContentLength());
     86       }
     87     } else if (content_completed) {
     88       DCHECK_EQ(partial_chunk_->content->size(),
     89                 partial_chunk_->ExpectedContentLength());
     90       chunks_.push_back(partial_chunk_.release());
     91       partial_chunk_.reset(new Chunk());
     92     }
     93   }
     94   DCHECK_EQ(next_data, start + length);
     95   total_bytes_stored_ += length;
     96 }
     97 
     98 void ChunkedByteBuffer::Append(const std::string& string) {
     99   Append(reinterpret_cast<const uint8*>(string.data()), string.size());
    100 }
    101 
    102 bool ChunkedByteBuffer::HasChunks() const {
    103   return !chunks_.empty();
    104 }
    105 
    106 scoped_ptr< std::vector<uint8> > ChunkedByteBuffer::PopChunk() {
    107   if (chunks_.empty())
    108     return scoped_ptr< std::vector<uint8> >();
    109   scoped_ptr<Chunk> chunk(*chunks_.begin());
    110   chunks_.weak_erase(chunks_.begin());
    111   DCHECK_EQ(chunk->header.size(), kHeaderLength);
    112   DCHECK_EQ(chunk->content->size(), chunk->ExpectedContentLength());
    113   total_bytes_stored_ -= chunk->content->size();
    114   total_bytes_stored_ -= kHeaderLength;
    115   return chunk->content.Pass();
    116 }
    117 
    118 void ChunkedByteBuffer::Clear() {
    119   chunks_.clear();
    120   partial_chunk_.reset(new Chunk());
    121   total_bytes_stored_ = 0;
    122 }
    123 
    124 ChunkedByteBuffer::Chunk::Chunk()
    125     : content(new std::vector<uint8>()) {
    126 }
    127 
    128 ChunkedByteBuffer::Chunk::~Chunk() {
    129 }
    130 
    131 size_t ChunkedByteBuffer::Chunk::ExpectedContentLength() const {
    132   DCHECK_EQ(header.size(), kHeaderLength);
    133   return static_cast<size_t>(ReadBigEndian32(&header[0]));
    134 }
    135 
    136 }  // namespace content
    137