1/*
2 * Copyright (c) 2011-2014, 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 <assert.h>
32#include "Socket.h"
33#include "RemoteProcessorProtocol.h"
34#include <string.h>
35#include <assert.h>
36#include <errno.h>
37
38using std::string;
39
40CMessage::CMessage(uint8_t ucMsgId) : _ucMsgId(ucMsgId), _pucData(NULL), _uiDataSize(0), _uiIndex(0)
41{
42}
43
44CMessage::CMessage() : _ucMsgId((uint8_t)-1), _pucData(NULL), _uiDataSize(0), _uiIndex(0)
45{
46}
47
48CMessage::~CMessage()
49{
50    delete [] _pucData;
51}
52
53// Msg Id
54uint8_t CMessage::getMsgId() const
55{
56    return _ucMsgId;
57}
58
59// Data
60void CMessage::writeData(const void* pvData, size_t uiSize)
61{
62    assert(_uiIndex + uiSize <= _uiDataSize);
63
64    // Copy
65    memcpy(&_pucData[_uiIndex], pvData, uiSize);
66
67    // Index
68    _uiIndex += uiSize;
69}
70
71void CMessage::readData(void* pvData, size_t uiSize)
72{
73    assert(_uiIndex + uiSize <= _uiDataSize);
74
75    // Copy
76    memcpy(pvData, &_pucData[_uiIndex], uiSize);
77
78    // Index
79    _uiIndex += uiSize;
80}
81
82void CMessage::writeString(const string& strData)
83{
84    // Size
85    uint32_t uiSize = strData.length();
86
87    writeData(&uiSize, sizeof(uiSize));
88
89    // Content
90    writeData(strData.c_str(), uiSize);
91}
92
93void CMessage::readString(string& strData)
94{
95    // Size
96    uint32_t uiSize;
97
98    readData(&uiSize, sizeof(uiSize));
99
100    // Data
101    char pcData[uiSize + 1];
102
103    // Content
104    readData(pcData, uiSize);
105
106    // NULL-terminate string
107    pcData[uiSize] = '\0';
108
109    // Output
110    strData = pcData;
111}
112
113size_t CMessage::getStringSize(const string& strData) const
114{
115    // Return string length plus room to store its length
116    return strData.length() + sizeof(uint32_t);
117}
118
119// Remaining data size
120size_t CMessage::getRemainingDataSize() const
121{
122    return _uiDataSize - _uiIndex;
123}
124
125// Send/Receive
126CMessage::Result CMessage::serialize(CSocket* pSocket, bool bOut, string& strError)
127{
128    if (bOut) {
129
130        // Make room for data to send
131        allocateData(getDataSize());
132
133        // Get data from derived
134        fillDataToSend();
135
136        // Finished providing data?
137        assert(_uiIndex == _uiDataSize);
138
139        // First send sync word
140        uint16_t uiSyncWord = SYNC_WORD;
141
142        if (!pSocket->write(&uiSyncWord, sizeof(uiSyncWord))) {
143
144            if (pSocket->hasPeerDisconnected()) {
145                return peerDisconnected;
146            }
147            return error;
148        }
149
150        // Size
151        uint32_t uiSize = (uint32_t)(sizeof(_ucMsgId) + _uiDataSize);
152
153        if (!pSocket->write(&uiSize, sizeof(uiSize))) {
154
155            strError += string("Size write failed: ") + strerror(errno);
156            return error;
157        }
158
159        // Msg Id
160        if (!pSocket->write(&_ucMsgId, sizeof(_ucMsgId))) {
161
162            strError += string("Msg write failed: ") + strerror(errno);
163            return error;
164        }
165
166        // Data
167        if (!pSocket->write(_pucData, _uiDataSize)) {
168
169            strError = string("Data write failed: ") + strerror(errno);
170            return error;
171        }
172
173        // Checksum
174        uint8_t ucChecksum = computeChecksum();
175
176        if (!pSocket->write(&ucChecksum, sizeof(ucChecksum))) {
177
178            strError = string("Checksum write failed: ") + strerror(errno);
179            return error;
180        }
181
182    } else {
183        // First read sync word
184        uint16_t uiSyncWord;
185
186        if (!pSocket->read(&uiSyncWord, sizeof(uiSyncWord))) {
187
188            strError = string("Sync read failed: ") + strerror(errno);
189            if (pSocket->hasPeerDisconnected()) {
190                return peerDisconnected;
191            }
192            return error;
193        }
194
195        // Check Sync word
196        if (uiSyncWord != SYNC_WORD) {
197
198            strError = "Sync word incorrect";
199            return error;
200        }
201
202        // Size
203        uint32_t uiSize;
204
205        if (!pSocket->read(&uiSize, sizeof(uiSize))) {
206
207            strError = string("Size read failed: ") + strerror(errno);
208            return error;
209        }
210
211        // Msg Id
212        if (!pSocket->read(&_ucMsgId, sizeof(_ucMsgId))) {
213
214            strError = string("Msg id read failed: ") + strerror(errno);
215            return error;
216        }
217
218        // Data
219
220        // Allocate
221        allocateData(uiSize - sizeof(_ucMsgId));
222
223        // Data receive
224        if (!pSocket->read(_pucData, _uiDataSize)) {
225
226            strError = string("Data read failed: ") + strerror(errno);
227            return error;
228        }
229
230        // Checksum
231        uint8_t ucChecksum;
232
233        if (!pSocket->read(&ucChecksum, sizeof(ucChecksum))) {
234
235            strError = string("Checksum read failed: ") + strerror(errno);
236            return error;
237        }
238        // Compare
239        if (ucChecksum != computeChecksum()) {
240
241            strError = "Received checksum != computed checksum";
242            return error;
243        }
244
245        // Collect data in derived
246        collectReceivedData();
247    }
248
249    return success;
250}
251
252// Checksum
253uint8_t CMessage::computeChecksum() const
254{
255    uint8_t uiChecksum = _ucMsgId;
256
257    uint32_t uiIndex;
258
259    for (uiIndex = 0; uiIndex < _uiDataSize; uiIndex++) {
260
261        uiChecksum += _pucData[uiIndex];
262    }
263
264    return uiChecksum;
265}
266
267// Allocation of room to store the message
268void CMessage::allocateData(size_t uiSize)
269{
270    // Remove previous one
271    if (_pucData) {
272
273        delete [] _pucData;
274    }
275    // Do allocate
276    _pucData = new uint8_t[uiSize];
277
278    // Record size
279    _uiDataSize = uiSize;
280
281    // Reset Index
282    _uiIndex = 0;
283}
284