1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
39 #include <netinet/in.h>
41 #include <anna/core/RuntimeException.hpp>
42 #include <anna/config/defines.hpp>
43 #include <anna/core/tracing/Logger.hpp>
44 #include <anna/core/functions.hpp>
45 #include <anna/core/util/Second.hpp>
46 #include <anna/core/util/Millisecond.hpp>
47 #include <anna/core/util/Microsecond.hpp>
49 #include <anna/comm/CompatCodec.hpp>
50 #include <anna/comm/functions.hpp>
51 #include <anna/comm/Variable.hpp>
56 bool comm::CompatCodec::st_initScramble(false);
59 template <class T, class M> comm::Variable* insert(const char* name, const short int id, M& theVector, T& value)
60 throw(RuntimeException) {
61 comm::Variable* result = theVector.find(id);
64 throw RuntimeException(functions::asString("Variable Id %d alreay used", id), ANNA_FILE_LOCATION);
66 theVector.add(result = new comm::Variable(id, name, value));
68 String msg("insert <T, M> | ");
69 msg << result->asString();
70 Logger::debug(msg, ANNA_FILE_LOCATION);
76 template <class T, class M> comm::Variable* insertRef(const char* name, const short int id, M& theVector, T& value)
77 throw(RuntimeException) {
78 comm::Variable* result = theVector.find(id);
81 throw RuntimeException(functions::asString("Variable Id %d alreay used", id), ANNA_FILE_LOCATION);
83 theVector.add(result = new comm::Variable(id, name, value.refValue()));
85 String msg("insert <T, M> | ");
86 msg << result->asString();
87 Logger::debug(msg, ANNA_FILE_LOCATION);
92 comm::CompatCodec::CompatCodec(const comm::CompatCodec::Type type, const bool scramble) :
93 comm::Message(comm::Message::StatusCodeBuffer::None),
97 if(a_scramble == true && st_initScramble == false) {
98 st_initScramble = true;
103 comm::CompatCodec::~CompatCodec() {
104 for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
110 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, std::string& value)
111 throw(RuntimeException) {
112 return insert(name, id, a_variables, value);
115 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, int& value)
116 throw(RuntimeException) {
117 const int backup(value);
118 const Variable* result = insert(name, id, a_variables, value);
123 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, S64& value)
124 throw(RuntimeException) {
125 const S64 backup(value);
126 const Variable* result = insert(name, id, a_variables, value);
131 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, bool& value)
132 throw(RuntimeException) {
133 const bool backup(value);
134 const Variable* result = insert(name, id, a_variables, value);
139 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, DataBlock& value)
140 throw(RuntimeException) {
141 return insert(name, id, a_variables, value);
144 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, float& value)
145 throw(RuntimeException) {
146 const float backup(value);
147 const Variable* result = insert(name, id, a_variables, value);
152 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, double& value)
153 throw(RuntimeException) {
154 const double backup(value);
155 const Variable* result = insert(name, id, a_variables, value);
160 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Second& value)
161 throw(RuntimeException) {
162 const Second backup(value);
163 const Variable* result = insertRef(name, id, a_variables, value);
168 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Millisecond& value)
169 throw(RuntimeException) {
170 const Millisecond backup(value);
171 const Variable* result = insertRef(name, id, a_variables, value);
176 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Microsecond& value)
177 throw(RuntimeException) {
178 const Microsecond backup(value);
179 const Variable* result = insertRef(name, id, a_variables, value);
184 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, comm::CompatCodec& value)
185 throw(RuntimeException) {
187 String msg("comm::CompatCodec::attach | Variable: ");
188 msg << name << " | Can not link with itself";
189 throw RuntimeException(msg, ANNA_FILE_LOCATION);
192 return insert(name, id, a_variables, value);
195 void comm::CompatCodec::setNull(const short int id, const bool isNull)
196 throw(RuntimeException) {
197 Variable* variable = a_variables.find(id);
200 throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
202 switch(variable->isNull()) {
205 if(isNull == false) a_nullCounter --;
210 if(isNull == true) a_nullCounter ++;
215 variable->setNull(isNull);
218 void comm::CompatCodec::setNull(const comm::Variable* variable, const bool isNull)
220 switch(variable->isNull()) {
223 if(isNull == false) a_nullCounter --;
228 if(isNull == true) a_nullCounter ++;
233 const_cast <comm::Variable*>(variable)->setNull(isNull);
236 bool comm::CompatCodec::isNull(const short int id) const
237 throw(RuntimeException) {
238 const Variable* variable = a_variables.find(id);
241 throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
243 return variable->isNull();
246 const comm::Variable& comm::CompatCodec::find(const short int id) const
247 throw(RuntimeException) {
248 const Variable* variable = a_variables.find(id);
251 throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
256 //-----------------------------------------------------------------------
257 // Codificacin de los datos en un bloque de memoria.
259 // mensaje ::= <byte0><byte1><byte2> <dato> ... <dato>
261 // dato ::= <byteA><byteB><byteC> [<cadena | int | long | bloque>]
262 // cadena ::= <byteLSB><byteMSB> < contenido > </0>
263 // int ::= <byteLSB> <byte> <byte> <byteMSB>
264 // long ::= <byteLSB> <byte> <byte> <byteMSB>
265 // bloque ::= <int> < contenido >
267 // (0) byte0 = Si es distinto de cero => contiene la clave usada para codificar
269 // (2) byte1 = Nmero de datos que contiene el mensaje.
270 // Recordar que slo se transfieren a este bloque de memoria los datos que
271 // tengan asignado algn value.
272 // (1) byte2 = Typeentificador del mensaje interno.
273 // (3) Los bytes A y B contienen del identificador del dato.
274 // (4) Los cuatro primeros bits del byteC indican el tipo de dato. En los
275 // tipos de datos boolean su contenido se guarda en el bit mas significativo
277 // (5) En las cadenas los dos primeros byres indican la longitud de la cadena
278 // incluyendo el indicador de fin de cadena, luego aparece el contenido
279 // de la cadena y finalmente el 0. El cero se incluye para optimizar
280 // la recogida de datos.
281 //-----------------------------------------------------------------------
282 const DataBlock& comm::CompatCodec::code()
283 throw(RuntimeException) {
284 register unsigned char c(0);
286 iterator maxii(a_variables.end());
287 register Variable* variable;
290 char aux [sizeof(S64)];
292 if(a_scramble == true) // (1)
293 while(c == 0) c = rand() % 0xff;
295 DataBlock& self = *this;
298 self += (char)(a_variables.size() - a_nullCounter); // (2)
299 self += comm::CompatCodec::getType(); // (1)
301 for(ii = begin(); ii != maxii; ii ++) {
302 variable = CompatCodec::variable(ii);
304 if(a_nullCounter > 0 && variable->isNull() == true)
307 switch(variable->getType()) {
308 case Variable::Type::String:
309 self += variable->codec();
310 stringLen = anna_strlen(string = variable->getStringValue()) + 1;
311 self.append(comm::functions::codeShort(aux, stringLen), sizeof(short int));
312 self.append(string, stringLen);
314 case Variable::Type::Integer:
315 self += variable->codec();
316 self.append(comm::functions::codeInteger(aux, variable->getInteger()), sizeof(int));
318 case Variable::Type::Integer64:
319 self += variable->codec();
320 self.append(comm::functions::codeInteger64(aux, variable->getInteger64()), sizeof(S64));
322 case Variable::Type::Boolean:
323 self.append(comm::functions::codeShort(aux, variable->getId()), sizeof(short int)); // (3)
324 c = Variable::Type::Boolean;
326 if(variable->getBoolean() == true) // (4)
331 case Variable::Type::Block:
332 self += variable->codec();
333 self.append(comm::functions::codeInteger(aux, variable->getDataBlock().getSize()), sizeof(int));
334 self += variable->getDataBlock();
336 case Variable::Type::Float:
337 self += variable->codec();
338 self.append(comm::functions::codeFloat(aux, variable->getFloat()), sizeof(float));
340 case Variable::Type::Double:
341 self += variable->codec();
342 self.append(comm::functions::codeDouble(aux, variable->getDouble()), sizeof(float));
344 case Variable::Type::Custom:
345 self += variable->codec();
347 const DataBlock& codec = reinterpret_cast <CompatCodec*>(variable->getCustom())->code();
348 self.append(comm::functions::codeInteger(aux, codec.getSize()), sizeof(int));
357 // Se sacan aqui para evitar qtene que comprobar el estado de las trazas en cada paso
358 for(ii = begin(); ii != maxii; ii ++)
359 Logger::debug(CompatCodec::variable(ii)->asString(), ANNA_FILE_LOCATION);
360 Logger::write(Logger::Debug, "comm::CompatCodec::code", self, ANNA_FILE_LOCATION)
363 if(a_scramble == true) {
364 char* data = const_cast <char*>(self.getData());
365 int size = self.getSize();
367 for(register int i = 1, key = data [0]; i < size; i ++)
374 //-------------------------------------------------------------------------------------------
375 void comm::CompatCodec::decode(const DataBlock& dataBlock)
376 throw(RuntimeException) {
377 const char* data = dataBlock.getData();
378 const int size = dataBlock.getSize();
381 throw RuntimeException("Can not decode an empty DataBlock", ANNA_FILE_LOCATION);
384 for(register int i = 1, key(data [0]); i < size; i ++)
385 const_cast <char*>(data)[i] ^= key ++;
387 LOGDEBUG(Logger::write(Logger::Debug, "comm::CompatCodec::decode", dataBlock, ANNA_FILE_LOCATION));
388 const int maxdata = data [1];
390 if(maxdata != a_variables.size())
391 normalDecode(data, size, maxdata);
392 else if(optimizedDecode(data, size) == false)
393 normalDecode(data, size, maxdata);
397 for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
398 Logger::debug(CompatCodec::variable(ii)->asString(), ANNA_FILE_LOCATION);
402 //-------------------------------------------------------------------------------------------
403 // Decodifica los buffers que pueden contener variables nulas.
404 //-------------------------------------------------------------------------------------------
405 void comm::CompatCodec::normalDecode(const char* data, const int size, const int maxdata)
406 throw(RuntimeException) {
407 // Mientras no se demuestre lo contrario todas las variables son nulas
408 a_nullCounter = a_variables.size();
410 for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
411 CompatCodec::variable(ii)->setNull(true);
416 bool hasError = false;
417 const char* top = data + size;
420 for(int ndata = 0; ndata < maxdata && data < top; ndata ++) {
421 id = comm::functions::decodeShort(data);
422 data += sizeof(short int);
424 if((variable = a_variables.find(id)) == NULL) {
431 switch(*data & 0x7f) {
432 case Variable::Type::String:
433 nbytes = comm::functions::decodeShort(++ data);
434 variable->setValue(data += sizeof(short int));
437 case Variable::Type::Integer:
438 variable->setInteger(comm::functions::decodeInteger(++ data));
441 case Variable::Type::Integer64:
442 variable->setValue(comm::functions::decodeInteger64(++ data));
445 case Variable::Type::Boolean:
446 variable->setBoolean((*data & 0x80) ? true : false);
449 case Variable::Type::Block:
450 nbytes = comm::functions::decodeInteger(++ data);
451 variable->setDataBlock(DataBlock(data += sizeof(int), nbytes, false));
454 case Variable::Type::Float:
455 variable->setFloat(comm::functions::decodeFloat(++ data));
456 data += sizeof(float);
458 case Variable::Type::Double:
459 variable->setDouble(comm::functions::decodeDouble(++ data));
460 data += sizeof(double);
462 case Variable::Type::Custom:
463 nbytes = comm::functions::decodeInteger(++ data);
465 DataBlock dataBlock(data += sizeof(int), nbytes, false);
466 reinterpret_cast <CompatCodec*>(variable->getCustom())->decode(dataBlock);
473 if(hasError == true) {
474 string msg("comm::CompatCodec::normalDecode | Buffer: ");
475 msg += functions::asString(DataBlock(data, size, false));
476 msg += functions::asText(" | Id is not defined: ", id);
477 Logger::error(msg, ANNA_FILE_LOCATION);
480 if(a_nullCounter < 0) {
483 string msg("comm::CompatCodec::normalDecode | Buffer: ");
484 msg += functions::asString(DataBlock(data, size, false));
485 msg += " | NullCounter is less than zero";
486 Logger::warning(msg, ANNA_FILE_LOCATION);
491 //-------------------------------------------------------------------------------------------
492 // Decodifica optimizadamente buffers que no tengan variables nulas.
493 // (1) El codigo de la variable
494 //-------------------------------------------------------------------------------------------
495 bool comm::CompatCodec::optimizedDecode(const char* data, const int size)
496 throw(RuntimeException) {
499 const char* top = data + size;
502 for(iterator ii = begin(), maxii = end(); ii != maxii && data < top; ii ++) {
503 data += sizeof(short int);
504 variable = CompatCodec::variable(ii);
506 switch(*data & 0x7f) {
507 case Variable::Type::String:
508 nbytes = comm::functions::decodeShort(++ data);
509 variable->setValue(data += sizeof(short int));
512 case Variable::Type::Integer:
513 variable->setInteger(comm::functions::decodeInteger(++ data));
516 case Variable::Type::Integer64:
517 variable->setValue(comm::functions::decodeInteger64(++ data));
520 case Variable::Type::Boolean:
521 variable->setBoolean((*data & 0x80) ? true : false);
524 case Variable::Type::Block:
525 nbytes = comm::functions::decodeInteger(++ data);
526 variable->setDataBlock(DataBlock(data += sizeof(int), nbytes, false));
529 case Variable::Type::Float:
530 variable->setFloat(comm::functions::decodeFloat(++ data));
531 data += sizeof(float);
533 case Variable::Type::Double:
534 variable->setDouble(comm::functions::decodeDouble(++ data));
535 data += sizeof(double);
537 case Variable::Type::Custom:
538 nbytes = comm::functions::decodeInteger(++ data);
540 DataBlock dataBlock(data += sizeof(int), nbytes, false);
541 reinterpret_cast <CompatCodec*>(variable->getCustom())->decode(dataBlock);
548 return (data == top);
551 comm::CompatCodec::Type comm::CompatCodec::getType(const DataBlock& dataBlock)
552 throw(RuntimeException) {
553 const int size(dataBlock.getSize());
556 throw RuntimeException("DataBlock is not valid", ANNA_FILE_LOCATION);
558 const char* data(dataBlock.getData());
559 comm::CompatCodec::Type result;
560 result = (data [0] != 0) ? ((data [0] + 1) ^ data [2]) : data [2];
561 LOGDEBUG(Logger::write(Logger::Debug, "comm::CompatCodec::getType", result, ANNA_FILE_LOCATION));
565 comm::CompatCodec::VariableContainer::VariableContainer() {
568 a_variables = new Variable* [a_maxSize];
569 anna_memset(a_variables, 0, sizeof(Variable*) * a_maxSize);
572 void comm::CompatCodec::VariableContainer::add(comm::Variable* variable)
574 if(a_size == a_maxSize) {
575 int maxSize = (a_maxSize << 1) - (a_maxSize >> 1);
576 Variable** variables = new Variable* [maxSize];
577 anna_memset(variables, 0, sizeof(Variable*) * maxSize);
578 anna_memcpy(variables, a_variables, sizeof(Variable*) * a_size);
580 a_variables = variables;
584 a_variables [a_size ++] = variable;
587 comm::Variable* comm::CompatCodec::VariableContainer::find(const int id)
589 for(register int ii = 0; ii < a_size; ii ++) {
590 if(a_variables [ii]->getId() == id)
591 return a_variables [ii];
597 const comm::Variable* comm::CompatCodec::VariableContainer::find(const int id) const
599 for(register int ii = 0; ii < a_size; ii ++) {
600 if(a_variables [ii]->getId() == id)
601 return a_variables [ii];
607 void comm::CompatCodec::VariableContainer::clear()
609 delete [] a_variables;
610 a_maxSize = a_size = 0;