1/*
2 * Copyright (c) 2011-2015, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30#include "Message.h"
31#include "Socket.h"
32#include "Iterator.hpp"
33#include <asio.hpp>
34#include <vector>
35#include <numeric>
36#include <cassert>
37
38using std::string;
39
40CMessage::CMessage(MsgType ucMsgId) : _ucMsgId(ucMsgId), _uiIndex(0)
41{
42}
43
44CMessage::CMessage() : _ucMsgId(MsgType::EInvalid), _uiIndex(0)
45{
46}
47
48// Msg Id
49CMessage::MsgType CMessage::getMsgId() const
50{
51    return _ucMsgId;
52}
53
54bool CMessage::isValidAccess(size_t offset, size_t size) const
55{
56    return offset + size <= getMessageDataSize();
57}
58
59// Data
60void CMessage::writeData(const void *pvData, size_t size)
61{
62    assert(isValidAccess(_uiIndex, size));
63
64    auto first = MAKE_ARRAY_ITERATOR(static_cast<const uint8_t *>(pvData), size);
65    auto last = first + size;
66    auto destFirst = begin(mData) + _uiIndex;
67
68    std::copy(first, last, destFirst);
69
70    _uiIndex += size;
71}
72
73void CMessage::readData(void *pvData, size_t size)
74{
75    assert(isValidAccess(_uiIndex, size));
76
77    auto first = begin(mData) + _uiIndex;
78    auto last = first + size;
79    auto destFirst = MAKE_ARRAY_ITERATOR(static_cast<uint8_t *>(pvData), size);
80
81    std::copy(first, last, destFirst);
82
83    _uiIndex += size;
84}
85
86void CMessage::writeString(const string &strData)
87{
88    // Size
89    uint32_t size = static_cast<uint32_t>(strData.length());
90
91    writeData(&size, sizeof(size));
92
93    // Content
94    writeData(strData.c_str(), size);
95}
96
97void CMessage::readString(string &strData)
98{
99    // Size
100    uint32_t uiSize;
101
102    readData(&uiSize, sizeof(uiSize));
103
104    // Data
105    std::vector<char> string(uiSize + 1);
106
107    // Content
108    readData(string.data(), uiSize);
109
110    // NULL-terminate string
111    string.back() = '\0';
112
113    // Output
114    strData = string.data();
115}
116
117size_t CMessage::getStringSize(const string &strData) const
118{
119    // Return string length plus room to store its length
120    return strData.length() + sizeof(uint32_t);
121}
122
123// Remaining data size
124size_t CMessage::getRemainingDataSize() const
125{
126    return getMessageDataSize() - _uiIndex;
127}
128
129// Send/Receive
130CMessage::Result CMessage::serialize(Socket &&socket, bool bOut, string &strError)
131{
132    asio::ip::tcp::socket &asioSocket = socket.get();
133
134    if (bOut) {
135        asio::error_code ec;
136
137        // Make room for data to send
138        allocateData(getDataSize());
139
140        // Get data from derived
141        fillDataToSend();
142
143        // Finished providing data?
144        assert(_uiIndex == getMessageDataSize());
145
146        // First send sync word
147        uint16_t uiSyncWord = SYNC_WORD;
148
149        if (!asio::write(asioSocket, asio::buffer(&uiSyncWord, sizeof(uiSyncWord)), ec)) {
150
151            if (ec == asio::error::eof) {
152                return peerDisconnected;
153            }
154            return error;
155        }
156
157        // Size
158        uint32_t uiSize = (uint32_t)(sizeof(_ucMsgId) + getMessageDataSize());
159
160        if (!asio::write(asioSocket, asio::buffer(&uiSize, sizeof(uiSize)), ec)) {
161
162            strError += string("Size write failed: ") + ec.message();
163            return error;
164        }
165
166        // Msg Id
167        if (!asio::write(asioSocket, asio::buffer(&_ucMsgId, sizeof(_ucMsgId)), ec)) {
168
169            strError += string("Msg write failed: ") + ec.message();
170            return error;
171        }
172
173        // Data
174        if (!asio::write(asioSocket, asio::buffer(mData), ec)) {
175
176            strError = string("Data write failed: ") + ec.message();
177            return error;
178        }
179
180        // Checksum
181        uint8_t ucChecksum = computeChecksum();
182
183        if (!asio::write(asioSocket, asio::buffer(&ucChecksum, sizeof(ucChecksum)), ec)) {
184
185            strError = string("Checksum write failed: ") + ec.message();
186            return error;
187        }
188
189    } else {
190        // First read sync word
191        uint16_t uiSyncWord = 0;
192        asio::error_code ec;
193
194        if (!asio::read(asioSocket, asio::buffer(&uiSyncWord, sizeof(uiSyncWord)), ec)) {
195            strError = string("Sync read failed: ") + ec.message();
196            if (ec == asio::error::eof) {
197                return peerDisconnected;
198            }
199            return error;
200        }
201
202        // Check Sync word
203        if (uiSyncWord != SYNC_WORD) {
204
205            strError = "Sync word incorrect";
206            return error;
207        }
208
209        // Size
210        uint32_t uiSize = 0;
211
212        if (!asio::read(asioSocket, asio::buffer(&uiSize, sizeof(uiSize)), ec)) {
213            strError = string("Size read failed: ") + ec.message();
214            return error;
215        }
216
217        // Msg Id
218        if (!asio::read(asioSocket, asio::buffer(&_ucMsgId, sizeof(_ucMsgId)), ec)) {
219            strError = string("Msg id read failed: ") + ec.message();
220            return error;
221        }
222
223        // Data
224
225        // Allocate
226        allocateData(uiSize - sizeof(_ucMsgId));
227
228        // Data receive
229        if (!asio::read(asioSocket, asio::buffer(mData), ec)) {
230            strError = string("Data read failed: ") + ec.message();
231            return error;
232        }
233
234        // Checksum
235        uint8_t ucChecksum = 0;
236
237        if (!asio::read(asioSocket, asio::buffer(&ucChecksum, sizeof(ucChecksum)), ec)) {
238            strError = string("Checksum read failed: ") + ec.message();
239            return error;
240        }
241        // Compare
242        if (ucChecksum != computeChecksum()) {
243
244            strError = "Received checksum != computed checksum";
245            return error;
246        }
247
248        // Collect data in derived
249        collectReceivedData();
250    }
251
252    return success;
253}
254
255// Checksum
256uint8_t CMessage::computeChecksum() const
257{
258    return accumulate(begin(mData), end(mData), static_cast<uint8_t>(_ucMsgId));
259}
260
261// Allocation of room to store the message
262void CMessage::allocateData(size_t size)
263{
264    // Remove previous one
265    mData.clear();
266
267    // Do allocate
268    mData.resize(size);
269
270    // Reset Index
271    _uiIndex = 0;
272}
273