Index: remoting/protocol/message_decoder.cc |
diff --git a/remoting/protocol/message_decoder.cc b/remoting/protocol/message_decoder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4a31ee1d48e36a83f76c8f5b6c7e36d69c5d8057 |
--- /dev/null |
+++ b/remoting/protocol/message_decoder.cc |
@@ -0,0 +1,112 @@ |
+// 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 "remoting/protocol/message_decoder.h" |
+ |
+#include "base/logging.h" |
+#include "net/base/io_buffer.h" |
+#include "remoting/base/multiple_array_input_stream.h" |
+#include "remoting/proto/internal.pb.h" |
+#include "talk/base/byteorder.h" |
+ |
+namespace remoting { |
+ |
+MessageDecoder::MessageDecoder() |
+ : available_bytes_(0), |
+ next_payload_(0), |
+ next_payload_known_(false) { |
+} |
+ |
+MessageDecoder::~MessageDecoder() {} |
+ |
+void MessageDecoder::AddBuffer(scoped_refptr<net::IOBuffer> data, |
+ int data_size) { |
+ buffer_list_.push_back(new net::DrainableIOBuffer(data, data_size)); |
+ available_bytes_ += data_size; |
+} |
+ |
+MultipleArrayInputStream* MessageDecoder::CreateInputStreamFromData() { |
+ // Determine the payload size. If we already know it then skip this part. |
+ // We may not have enough data to determine the payload size so use a |
+ // utility function to find out. |
+ int next_payload = -1; |
+ if (!next_payload_known_ && GetPayloadSize(&next_payload)) { |
+ DCHECK_NE(-1, next_payload); |
+ next_payload_ = next_payload; |
+ next_payload_known_ = true; |
+ } |
+ |
+ // If the next payload size is still not known or we don't have enough |
+ // data for parsing then exit. |
+ if (!next_payload_known_ || available_bytes_ < next_payload_) |
+ return NULL; |
+ next_payload_known_ = false; |
+ |
+ // The following loop gather buffers in |buffer_list_| that sum up to |
+ // |next_payload_| bytes. These buffers are added to |stream|. |
+ |
+ // Create a MultipleArrayInputStream for parsing. |
+ // TODO(hclam): Avoid creating this object everytime. |
+ MultipleArrayInputStream* stream = new MultipleArrayInputStream(); |
+ while (next_payload_ > 0 && !buffer_list_.empty()) { |
+ scoped_refptr<net::DrainableIOBuffer> buffer = buffer_list_.front(); |
+ int read_bytes = std::min(buffer->BytesRemaining(), next_payload_); |
+ |
+ // This call creates a new instance of DrainableIOBuffer internally. |
+ // This will reference the same base pointer but maintain it's own |
+ // version of data pointer. |
+ stream->AddBuffer(buffer, read_bytes); |
+ |
+ // Adjust counters. |
+ buffer->DidConsume(read_bytes); |
+ next_payload_ -= read_bytes; |
+ available_bytes_ -= read_bytes; |
+ |
+ // If the front buffer is fully read then remove it from the queue. |
+ if (!buffer->BytesRemaining()) |
+ buffer_list_.pop_front(); |
+ } |
+ DCHECK_EQ(0, next_payload_); |
+ DCHECK_LE(0, available_bytes_); |
+ return stream; |
+} |
+ |
+static int GetHeaderSize(const std::string& header) { |
+ return header.length(); |
+} |
+ |
+bool MessageDecoder::GetPayloadSize(int* size) { |
+ // The header has a size of 4 bytes. |
+ const int kHeaderSize = sizeof(int32); |
+ |
+ if (available_bytes_ < kHeaderSize) |
+ return false; |
+ |
+ std::string header; |
+ while (GetHeaderSize(header) < kHeaderSize && !buffer_list_.empty()) { |
+ scoped_refptr<net::DrainableIOBuffer> buffer = buffer_list_.front(); |
+ |
+ // Find out how many bytes we need and how many bytes are available in this |
+ // buffer. |
+ int needed_bytes = kHeaderSize - GetHeaderSize(header); |
+ int available_bytes = buffer->BytesRemaining(); |
+ |
+ // Then append the required bytes into the header and advance the last |
+ // read position. |
+ int read_bytes = std::min(needed_bytes, available_bytes); |
+ header.append(buffer->data(), read_bytes); |
+ buffer->DidConsume(read_bytes); |
+ available_bytes_ -= read_bytes; |
+ |
+ // If the buffer is depleted then remove it from the queue. |
+ if (!buffer->BytesRemaining()) { |
+ buffer_list_.pop_front(); |
+ } |
+ } |
+ |
+ *size = talk_base::GetBE32(header.c_str()); |
+ return true; |
+} |
+ |
+} // namespace remoting |