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.
5 #include "remoting/base/protocol_decoder.h"
7 #include "remoting/base/multiple_array_input_stream.h"
8 #include "talk/base/byteorder.h"
12 ProtocolDecoder::ProtocolDecoder()
13 : last_read_position_(0),
16 next_payload_known_(false) {
19 void ProtocolDecoder::ParseClientMessages(scoped_refptr
<media::DataBuffer
> data
,
20 ClientMessageList
* messages
) {
21 ParseMessages
<ClientMessage
>(data
, messages
);
24 void ProtocolDecoder::ParseHostMessages(scoped_refptr
<media::DataBuffer
> data
,
25 HostMessageList
* messages
) {
26 ParseMessages
<HostMessage
>(data
, messages
);
30 void ProtocolDecoder::ParseMessages(scoped_refptr
<media::DataBuffer
> data
,
31 std::vector
<T
*>* messages
) {
32 // If this is the first data in the processing queue, then set the
33 // last read position to 0.
34 if (data_list_
.empty())
35 last_read_position_
= 0;
37 // First enqueue the data received.
38 data_list_
.push_back(data
);
39 available_bytes_
+= data
->GetDataSize();
41 // Then try to parse one message until we can't parse anymore.
43 while (ParseOneMessage
<T
>(&message
)) {
44 messages
->push_back(message
);
49 bool ProtocolDecoder::ParseOneMessage(T
** message
) {
50 // Determine the payload size. If we already know it, then skip this
52 // We have the value set to -1 for checking later.
53 int next_payload
= -1;
54 if (!next_payload_known_
&& GetPayloadSize(&next_payload
)) {
55 DCHECK_NE(-1, next_payload
);
56 next_payload_
= next_payload
;
57 next_payload_known_
= true;
60 // If the next payload size is still not known or we don't have enough
61 // data for parsing then exit.
62 if (!next_payload_known_
|| available_bytes_
< next_payload_
)
64 next_payload_known_
= false;
66 // Extract data from |data_list_| used to form a full protocol buffer.
68 std::deque
<const uint8
*> buffer_pointers
;
69 std::deque
<int> buffer_sizes
;
70 while (next_payload_
> 0 && !data_list_
.empty()) {
71 scoped_refptr
<media::DataBuffer
> buffer
= data_list_
.front();
72 size_t read_bytes
= std::min(buffer
->GetDataSize() - last_read_position_
,
75 buffers
.push_back(buffer
);
76 buffer_pointers
.push_back(buffer
->GetData() + last_read_position_
);
77 buffer_sizes
.push_back(read_bytes
);
80 last_read_position_
+= read_bytes
;
81 next_payload_
-= read_bytes
;
82 available_bytes_
-= read_bytes
;
84 // If the front buffer is fully read, remove it from the queue.
85 if (buffer
->GetDataSize() == last_read_position_
) {
86 data_list_
.pop_front();
87 last_read_position_
= 0;
90 DCHECK_EQ(0UL, next_payload_
);
91 DCHECK_EQ(buffers
.size(), buffer_pointers
.size());
92 DCHECK_EQ(buffers
.size(), buffer_sizes
.size());
94 // Create a MultipleArrayInputStream for parsing.
95 MultipleArrayInputStream
stream(buffers
.size());
96 for (size_t i
= 0; i
< buffers
.size(); ++i
) {
97 stream
.SetBuffer(i
, buffer_pointers
[i
], buffer_sizes
[i
]);
100 // And finally it is parsing.
102 bool ret
= (*message
)->ParseFromZeroCopyStream(&stream
);
108 bool ProtocolDecoder::GetPayloadSize(int* size
) {
109 // The header has a size of 4 bytes.
110 const size_t kHeaderSize
= sizeof(int32
);
112 if (available_bytes_
< kHeaderSize
)
116 while (header
.length() < kHeaderSize
&& !data_list_
.empty()) {
117 scoped_refptr
<media::DataBuffer
> buffer
= data_list_
.front();
119 // Find out how many bytes we need and how many bytes are available in this
121 int needed_bytes
= kHeaderSize
- header
.length();
122 int available_bytes
= buffer
->GetDataSize() - last_read_position_
;
124 // Then append the required bytes into the header and advance the last
126 int read_bytes
= std::min(needed_bytes
, available_bytes
);
128 reinterpret_cast<const char*>(buffer
->GetData()) + last_read_position_
,
130 last_read_position_
+= read_bytes
;
131 available_bytes_
-= read_bytes
;
133 // If the buffer is depleted then remove it from the queue.
134 if (last_read_position_
== buffer
->GetDataSize()) {
135 last_read_position_
= 0;
136 data_list_
.pop_front();
140 if (header
.length() == kHeaderSize
) {
141 *size
= talk_base::GetBE32(header
.c_str());
144 NOTREACHED() << "Unable to extract payload size";
148 } // namespace remoting