1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
11 #include <netinet/in.h>
13 #include <anna/core/RuntimeException.hpp>
14 #include <anna/config/defines.hpp>
15 #include <anna/core/tracing/Logger.hpp>
16 #include <anna/core/functions.hpp>
17 #include <anna/core/util/Second.hpp>
18 #include <anna/core/util/Millisecond.hpp>
19 #include <anna/core/util/Microsecond.hpp>
21 #include <anna/comm/CompatCodec.hpp>
22 #include <anna/comm/functions.hpp>
23 #include <anna/comm/Variable.hpp>
28 bool comm::CompatCodec::st_initScramble(false);
31 template <class T, class M> comm::Variable* insert(const char* name, const short int id, M& theVector, T& value)
32 throw(RuntimeException) {
33 comm::Variable* result = theVector.find(id);
36 throw RuntimeException(functions::asString("Variable Id %d alreay used", id), ANNA_FILE_LOCATION);
38 theVector.add(result = new comm::Variable(id, name, value));
40 String msg("insert <T, M> | ");
41 msg << result->asString();
42 Logger::debug(msg, ANNA_FILE_LOCATION);
48 template <class T, class M> comm::Variable* insertRef(const char* name, const short int id, M& theVector, T& value)
49 throw(RuntimeException) {
50 comm::Variable* result = theVector.find(id);
53 throw RuntimeException(functions::asString("Variable Id %d alreay used", id), ANNA_FILE_LOCATION);
55 theVector.add(result = new comm::Variable(id, name, value.refValue()));
57 String msg("insert <T, M> | ");
58 msg << result->asString();
59 Logger::debug(msg, ANNA_FILE_LOCATION);
64 comm::CompatCodec::CompatCodec(const comm::CompatCodec::Type type, const bool scramble) :
65 comm::Message(comm::Message::StatusCodeBuffer::None),
69 if(a_scramble == true && st_initScramble == false) {
70 st_initScramble = true;
75 comm::CompatCodec::~CompatCodec() {
76 for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
82 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, std::string& value)
83 throw(RuntimeException) {
84 return insert(name, id, a_variables, value);
87 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, int& value)
88 throw(RuntimeException) {
89 const int backup(value);
90 const Variable* result = insert(name, id, a_variables, value);
95 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, S64& value)
96 throw(RuntimeException) {
97 const S64 backup(value);
98 const Variable* result = insert(name, id, a_variables, value);
103 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, bool& value)
104 throw(RuntimeException) {
105 const bool backup(value);
106 const Variable* result = insert(name, id, a_variables, value);
111 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, DataBlock& value)
112 throw(RuntimeException) {
113 return insert(name, id, a_variables, value);
116 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, float& value)
117 throw(RuntimeException) {
118 const float backup(value);
119 const Variable* result = insert(name, id, a_variables, value);
124 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, double& value)
125 throw(RuntimeException) {
126 const double backup(value);
127 const Variable* result = insert(name, id, a_variables, value);
132 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Second& value)
133 throw(RuntimeException) {
134 const Second backup(value);
135 const Variable* result = insertRef(name, id, a_variables, value);
140 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Millisecond& value)
141 throw(RuntimeException) {
142 const Millisecond backup(value);
143 const Variable* result = insertRef(name, id, a_variables, value);
148 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Microsecond& value)
149 throw(RuntimeException) {
150 const Microsecond backup(value);
151 const Variable* result = insertRef(name, id, a_variables, value);
156 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, comm::CompatCodec& value)
157 throw(RuntimeException) {
159 String msg("comm::CompatCodec::attach | Variable: ");
160 msg << name << " | Can not link with itself";
161 throw RuntimeException(msg, ANNA_FILE_LOCATION);
164 return insert(name, id, a_variables, value);
167 void comm::CompatCodec::setNull(const short int id, const bool isNull)
168 throw(RuntimeException) {
169 Variable* variable = a_variables.find(id);
172 throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
174 if(variable->isNull()) {
175 if(isNull == false) a_nullCounter --;
178 if(isNull == true) a_nullCounter ++;
181 variable->setNull(isNull);
184 void comm::CompatCodec::setNull(const comm::Variable* variable, const bool isNull)
186 if(variable->isNull()) {
187 if(isNull == false) a_nullCounter --;
190 if(isNull == true) a_nullCounter ++;
193 const_cast <comm::Variable*>(variable)->setNull(isNull);
196 bool comm::CompatCodec::isNull(const short int id) const
197 throw(RuntimeException) {
198 const Variable* variable = a_variables.find(id);
201 throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
203 return variable->isNull();
206 const comm::Variable& comm::CompatCodec::find(const short int id) const
207 throw(RuntimeException) {
208 const Variable* variable = a_variables.find(id);
211 throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
216 //-----------------------------------------------------------------------
217 // Codificacin de los datos en un bloque de memoria.
219 // mensaje ::= <byte0><byte1><byte2> <dato> ... <dato>
221 // dato ::= <byteA><byteB><byteC> [<cadena | int | long | bloque>]
222 // cadena ::= <byteLSB><byteMSB> < contenido > </0>
223 // int ::= <byteLSB> <byte> <byte> <byteMSB>
224 // long ::= <byteLSB> <byte> <byte> <byteMSB>
225 // bloque ::= <int> < contenido >
227 // (0) byte0 = Si es distinto de cero => contiene la clave usada para codificar
229 // (2) byte1 = Nmero de datos que contiene el mensaje.
230 // Recordar que slo se transfieren a este bloque de memoria los datos que
231 // tengan asignado algn value.
232 // (1) byte2 = Typeentificador del mensaje interno.
233 // (3) Los bytes A y B contienen del identificador del dato.
234 // (4) Los cuatro primeros bits del byteC indican el tipo de dato. En los
235 // tipos de datos boolean su contenido se guarda en el bit mas significativo
237 // (5) En las cadenas los dos primeros byres indican la longitud de la cadena
238 // incluyendo el indicador de fin de cadena, luego aparece el contenido
239 // de la cadena y finalmente el 0. El cero se incluye para optimizar
240 // la recogida de datos.
241 //-----------------------------------------------------------------------
242 const DataBlock& comm::CompatCodec::code()
243 throw(RuntimeException) {
246 iterator maxii(a_variables.end());
250 char aux [sizeof(S64)];
252 if(a_scramble == true) // (1)
253 while(c == 0) c = rand() % 0xff;
255 DataBlock& self = *this;
258 self += (char)(a_variables.size() - a_nullCounter); // (2)
259 self += comm::CompatCodec::getType(); // (1)
261 for(ii = begin(); ii != maxii; ii ++) {
262 variable = CompatCodec::variable(ii);
264 if(a_nullCounter > 0 && variable->isNull() == true)
267 switch(variable->getType()) {
268 case Variable::Type::String:
269 self += variable->codec();
270 stringLen = anna_strlen(string = variable->getStringValue()) + 1;
271 self.append(comm::functions::codeShort(aux, stringLen), sizeof(short int));
272 self.append(string, stringLen);
274 case Variable::Type::Integer:
275 self += variable->codec();
276 self.append(comm::functions::codeInteger(aux, variable->getInteger()), sizeof(int));
278 case Variable::Type::Integer64:
279 self += variable->codec();
280 self.append(comm::functions::codeInteger64(aux, variable->getInteger64()), sizeof(S64));
282 case Variable::Type::Boolean:
283 self.append(comm::functions::codeShort(aux, variable->getId()), sizeof(short int)); // (3)
284 c = Variable::Type::Boolean;
286 if(variable->getBoolean() == true) // (4)
291 case Variable::Type::Block:
292 self += variable->codec();
293 self.append(comm::functions::codeInteger(aux, variable->getDataBlock().getSize()), sizeof(int));
294 self += variable->getDataBlock();
296 case Variable::Type::Float:
297 self += variable->codec();
298 self.append(comm::functions::codeFloat(aux, variable->getFloat()), sizeof(float));
300 case Variable::Type::Double:
301 self += variable->codec();
302 self.append(comm::functions::codeDouble(aux, variable->getDouble()), sizeof(float));
304 case Variable::Type::Custom:
305 self += variable->codec();
307 const DataBlock& codec = reinterpret_cast <CompatCodec*>(variable->getCustom())->code();
308 self.append(comm::functions::codeInteger(aux, codec.getSize()), sizeof(int));
318 // Se sacan aqui para evitar qtene que comprobar el estado de las trazas en cada paso
319 for(ii = begin(); ii != maxii; ii ++)
320 Logger::debug(CompatCodec::variable(ii)->asString(), ANNA_FILE_LOCATION);
321 Logger::write(Logger::Debug, "comm::CompatCodec::code", self, ANNA_FILE_LOCATION)
324 if(a_scramble == true) {
325 char* data = const_cast <char*>(self.getData());
326 int size = self.getSize();
328 for(int i = 1, key = data [0]; i < size; i ++)
335 //-------------------------------------------------------------------------------------------
336 void comm::CompatCodec::decode(const DataBlock& dataBlock)
337 throw(RuntimeException) {
338 const char* data = dataBlock.getData();
339 const int size = dataBlock.getSize();
342 throw RuntimeException("Can not decode an empty DataBlock", ANNA_FILE_LOCATION);
345 for(int i = 1, key(data [0]); i < size; i ++)
346 const_cast <char*>(data)[i] ^= key ++;
348 LOGDEBUG(Logger::write(Logger::Debug, "comm::CompatCodec::decode", dataBlock, ANNA_FILE_LOCATION));
349 const int maxdata = data [1];
351 if(maxdata != a_variables.size())
352 normalDecode(data, size, maxdata);
353 else if(optimizedDecode(data, size) == false)
354 normalDecode(data, size, maxdata);
358 for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
359 Logger::debug(CompatCodec::variable(ii)->asString(), ANNA_FILE_LOCATION);
363 //-------------------------------------------------------------------------------------------
364 // Decodifica los buffers que pueden contener variables nulas.
365 //-------------------------------------------------------------------------------------------
366 void comm::CompatCodec::normalDecode(const char* data, const int size, const int maxdata)
367 throw(RuntimeException) {
368 // Mientras no se demuestre lo contrario todas las variables son nulas
369 a_nullCounter = a_variables.size();
371 for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
372 CompatCodec::variable(ii)->setNull(true);
377 bool hasError = false;
378 const char* top = data + size;
381 for(int ndata = 0; ndata < maxdata && data < top; ndata ++) {
382 id = comm::functions::decodeShort(data);
383 data += sizeof(short int);
385 if((variable = a_variables.find(id)) == NULL) {
392 switch(*data & 0x7f) {
393 case Variable::Type::String:
394 nbytes = comm::functions::decodeShort(++ data);
395 variable->setValue(data += sizeof(short int));
398 case Variable::Type::Integer:
399 variable->setInteger(comm::functions::decodeInteger(++ data));
402 case Variable::Type::Integer64:
403 variable->setValue(comm::functions::decodeInteger64(++ data));
406 case Variable::Type::Boolean:
407 variable->setBoolean((*data & 0x80) ? true : false);
410 case Variable::Type::Block:
411 nbytes = comm::functions::decodeInteger(++ data);
412 variable->setDataBlock(DataBlock(data += sizeof(int), nbytes, false));
415 case Variable::Type::Float:
416 variable->setFloat(comm::functions::decodeFloat(++ data));
417 data += sizeof(float);
419 case Variable::Type::Double:
420 variable->setDouble(comm::functions::decodeDouble(++ data));
421 data += sizeof(double);
423 case Variable::Type::Custom:
424 nbytes = comm::functions::decodeInteger(++ data);
426 DataBlock dataBlock(data += sizeof(int), nbytes, false);
427 reinterpret_cast <CompatCodec*>(variable->getCustom())->decode(dataBlock);
434 if(hasError == true) {
435 string msg("comm::CompatCodec::normalDecode | Buffer: ");
436 msg += functions::asString(DataBlock(data, size, false));
437 msg += functions::asText(" | Id is not defined: ", id);
438 Logger::error(msg, ANNA_FILE_LOCATION);
441 if(a_nullCounter < 0) {
444 string msg("comm::CompatCodec::normalDecode | Buffer: ");
445 msg += functions::asString(DataBlock(data, size, false));
446 msg += " | NullCounter is less than zero";
447 Logger::warning(msg, ANNA_FILE_LOCATION);
452 //-------------------------------------------------------------------------------------------
453 // Decodifica optimizadamente buffers que no tengan variables nulas.
454 // (1) El codigo de la variable
455 //-------------------------------------------------------------------------------------------
456 bool comm::CompatCodec::optimizedDecode(const char* data, const int size)
457 throw(RuntimeException) {
460 const char* top = data + size;
463 for(iterator ii = begin(), maxii = end(); ii != maxii && data < top; ii ++) {
464 data += sizeof(short int);
465 variable = CompatCodec::variable(ii);
467 switch(*data & 0x7f) {
468 case Variable::Type::String:
469 nbytes = comm::functions::decodeShort(++ data);
470 variable->setValue(data += sizeof(short int));
473 case Variable::Type::Integer:
474 variable->setInteger(comm::functions::decodeInteger(++ data));
477 case Variable::Type::Integer64:
478 variable->setValue(comm::functions::decodeInteger64(++ data));
481 case Variable::Type::Boolean:
482 variable->setBoolean((*data & 0x80) ? true : false);
485 case Variable::Type::Block:
486 nbytes = comm::functions::decodeInteger(++ data);
487 variable->setDataBlock(DataBlock(data += sizeof(int), nbytes, false));
490 case Variable::Type::Float:
491 variable->setFloat(comm::functions::decodeFloat(++ data));
492 data += sizeof(float);
494 case Variable::Type::Double:
495 variable->setDouble(comm::functions::decodeDouble(++ data));
496 data += sizeof(double);
498 case Variable::Type::Custom:
499 nbytes = comm::functions::decodeInteger(++ data);
501 DataBlock dataBlock(data += sizeof(int), nbytes, false);
502 reinterpret_cast <CompatCodec*>(variable->getCustom())->decode(dataBlock);
509 return (data == top);
512 comm::CompatCodec::Type comm::CompatCodec::getType(const DataBlock& dataBlock)
513 throw(RuntimeException) {
514 const int size(dataBlock.getSize());
517 throw RuntimeException("DataBlock is not valid", ANNA_FILE_LOCATION);
519 const char* data(dataBlock.getData());
520 comm::CompatCodec::Type result;
521 result = (data [0] != 0) ? ((data [0] + 1) ^ data [2]) : data [2];
522 LOGDEBUG(Logger::write(Logger::Debug, "comm::CompatCodec::getType", result, ANNA_FILE_LOCATION));
526 comm::CompatCodec::VariableContainer::VariableContainer() {
529 a_variables = new Variable* [a_maxSize];
530 anna_memset(a_variables, 0, sizeof(Variable*) * a_maxSize);
533 void comm::CompatCodec::VariableContainer::add(comm::Variable* variable)
535 if(a_size == a_maxSize) {
536 int maxSize = (a_maxSize << 1) - (a_maxSize >> 1);
537 Variable** variables = new Variable* [maxSize];
538 anna_memset(variables, 0, sizeof(Variable*) * maxSize);
539 anna_memcpy(variables, a_variables, sizeof(Variable*) * a_size);
541 a_variables = variables;
545 a_variables [a_size ++] = variable;
548 comm::Variable* comm::CompatCodec::VariableContainer::find(const int id)
550 for(int ii = 0; ii < a_size; ii ++) {
551 if(a_variables [ii]->getId() == id)
552 return a_variables [ii];
558 const comm::Variable* comm::CompatCodec::VariableContainer::find(const int id) const
560 for(int ii = 0; ii < a_size; ii ++) {
561 if(a_variables [ii]->getId() == id)
562 return a_variables [ii];
568 void comm::CompatCodec::VariableContainer::clear()
570 delete [] a_variables;
571 a_maxSize = a_size = 0;