Remove dynamic exceptions
[anna.git] / source / comm / CompatCodec.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
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 //
7
8
9 #include <stdlib.h>
10 #include <time.h>
11 #include <netinet/in.h>
12
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>
20
21 #include <anna/comm/CompatCodec.hpp>
22 #include <anna/comm/functions.hpp>
23 #include <anna/comm/Variable.hpp>
24
25 using namespace std;
26 using namespace anna;
27
28 bool comm::CompatCodec::st_initScramble(false);
29
30 // static
31 template <class T, class M> comm::Variable* insert(const char* name, const short int id, M& theVector, T& value)
32 noexcept(false) {
33   comm::Variable* result = theVector.find(id);
34
35   if(result != NULL)
36     throw RuntimeException(functions::asString("Variable Id %d alreay used", id), ANNA_FILE_LOCATION);
37
38   theVector.add(result = new comm::Variable(id, name, value));
39   LOGDEBUG(
40     String msg("insert <T, M> | ");
41     msg <<  result->asString();
42     Logger::debug(msg, ANNA_FILE_LOCATION);
43   );
44   return result;
45 }
46
47 // static
48 template <class T, class M> comm::Variable* insertRef(const char* name, const short int id, M& theVector, T& value)
49 noexcept(false) {
50   comm::Variable* result = theVector.find(id);
51
52   if(result != NULL)
53     throw RuntimeException(functions::asString("Variable Id %d alreay used", id), ANNA_FILE_LOCATION);
54
55   theVector.add(result = new comm::Variable(id, name, value.refValue()));
56   LOGDEBUG(
57     String msg("insert <T, M> | ");
58     msg <<  result->asString();
59     Logger::debug(msg, ANNA_FILE_LOCATION);
60   );
61   return result;
62 }
63
64 comm::CompatCodec::CompatCodec(const comm::CompatCodec::Type type, const bool scramble)  :
65   comm::Message(comm::Message::StatusCodeBuffer::None),
66   a_type(type),
67   a_scramble(scramble),
68   a_nullCounter(0) {
69   if(a_scramble == true && st_initScramble == false) {
70     st_initScramble = true;
71     srand(time(NULL));
72   }
73 }
74
75 comm::CompatCodec::~CompatCodec() {
76   for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
77     delete variable(ii);
78
79   a_variables.clear();
80 }
81
82 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, std::string& value)
83 noexcept(false) {
84   return insert(name, id, a_variables, value);
85 }
86
87 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, int& value)
88 noexcept(false) {
89   const int backup(value);
90   const Variable* result = insert(name, id, a_variables, value);
91   value = backup;
92   return result;
93 }
94
95 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, S64& value)
96 noexcept(false) {
97   const S64 backup(value);
98   const Variable* result = insert(name, id, a_variables, value);
99   value = backup;
100   return result;
101 }
102
103 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, bool& value)
104 noexcept(false) {
105   const bool backup(value);
106   const Variable* result = insert(name, id, a_variables, value);
107   value = backup;
108   return result;
109 }
110
111 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, DataBlock& value)
112 noexcept(false) {
113   return insert(name, id, a_variables, value);
114 }
115
116 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, float& value)
117 noexcept(false) {
118   const float backup(value);
119   const Variable* result = insert(name, id, a_variables, value);
120   value = backup;
121   return result;
122 }
123
124 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, double& value)
125 noexcept(false) {
126   const double backup(value);
127   const Variable* result = insert(name, id, a_variables, value);
128   value = backup;
129   return result;
130 }
131
132 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Second& value)
133 noexcept(false) {
134   const Second backup(value);
135   const Variable* result = insertRef(name, id, a_variables, value);
136   value = backup;
137   return result;
138 }
139
140 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Millisecond& value)
141 noexcept(false) {
142   const Millisecond backup(value);
143   const Variable* result = insertRef(name, id, a_variables, value);
144   value = backup;
145   return result;
146 }
147
148 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, Microsecond& value)
149 noexcept(false) {
150   const Microsecond backup(value);
151   const Variable* result = insertRef(name, id, a_variables, value);
152   value = backup;
153   return result;
154 }
155
156 const comm::Variable* comm::CompatCodec::attach(const char* name, const short int id, comm::CompatCodec& value)
157 noexcept(false) {
158   if(&value == this) {
159     String msg("comm::CompatCodec::attach | Variable: ");
160     msg << name << " | Can not link with itself";
161     throw RuntimeException(msg, ANNA_FILE_LOCATION);
162   }
163
164   return insert(name, id, a_variables, value);
165 }
166
167 void comm::CompatCodec::setNull(const short int id, const bool isNull)
168 noexcept(false) {
169   Variable* variable = a_variables.find(id);
170
171   if(variable == NULL)
172     throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
173
174   if(variable->isNull()) {
175     if(isNull == false) a_nullCounter --;
176   }
177   else {
178     if(isNull == true) a_nullCounter ++;
179   }
180
181   variable->setNull(isNull);
182 }
183
184 void comm::CompatCodec::setNull(const comm::Variable* variable, const bool isNull)
185 {
186   if(variable->isNull()) {
187     if(isNull == false) a_nullCounter --;
188   }
189   else {
190     if(isNull == true) a_nullCounter ++;
191   }
192
193   const_cast <comm::Variable*>(variable)->setNull(isNull);
194 }
195
196 bool comm::CompatCodec::isNull(const short int id) const
197 noexcept(false) {
198   const Variable* variable = a_variables.find(id);
199
200   if(variable == NULL)
201     throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
202
203   return variable->isNull();
204 }
205
206 const comm::Variable& comm::CompatCodec::find(const short int id) const
207 noexcept(false) {
208   const Variable* variable = a_variables.find(id);
209
210   if(variable == NULL)
211     throw RuntimeException(functions::asString("Id %d is not defined", id), ANNA_FILE_LOCATION);
212
213   return *variable;
214 }
215
216 //-----------------------------------------------------------------------
217 // Codificacin de los datos en un bloque de memoria.
218 //
219 // mensaje ::= <byte0><byte1><byte2> <dato> ... <dato>
220 //
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 >
226 //
227 // (0) byte0 = Si es distinto de cero => contiene la clave usada para codificar
228 //     el mensaje.
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
236 //     de este byte.
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 noexcept(false) {
244   unsigned char c(0);
245   iterator ii;
246   iterator maxii(a_variables.end());
247   Variable* variable;
248   int stringLen;
249   const char* string;
250   char aux [sizeof(S64)];
251
252   if(a_scramble == true)     // (1)
253     while(c == 0) c = rand() % 0xff;
254
255   DataBlock& self = *this;
256   self.clear();
257   self += c;
258   self += (char)(a_variables.size() - a_nullCounter);          // (2)
259   self += comm::CompatCodec::getType();                              // (1)
260
261   for(ii = begin(); ii != maxii; ii ++) {
262     variable = CompatCodec::variable(ii);
263
264     if(a_nullCounter > 0 && variable->isNull() == true)
265       continue;
266
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);
273       break;
274     case Variable::Type::Integer:
275       self += variable->codec();
276       self.append(comm::functions::codeInteger(aux, variable->getInteger()), sizeof(int));
277       break;
278     case Variable::Type::Integer64:
279       self += variable->codec();
280       self.append(comm::functions::codeInteger64(aux, variable->getInteger64()), sizeof(S64));
281       break;
282     case Variable::Type::Boolean:
283       self.append(comm::functions::codeShort(aux, variable->getId()), sizeof(short int));      // (3)
284       c = Variable::Type::Boolean;
285
286       if(variable->getBoolean() == true)    // (4)
287         c |= 0x80;
288
289       self += c;
290       break;
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();
295       break;
296     case Variable::Type::Float:
297       self += variable->codec();
298       self.append(comm::functions::codeFloat(aux, variable->getFloat()), sizeof(float));
299       break;
300     case Variable::Type::Double:
301       self += variable->codec();
302       self.append(comm::functions::codeDouble(aux, variable->getDouble()), sizeof(float));
303       break;
304     case Variable::Type::Custom:
305       self += variable->codec();
306       {
307         const DataBlock& codec = reinterpret_cast <CompatCodec*>(variable->getCustom())->code();
308         self.append(comm::functions::codeInteger(aux, codec.getSize()), sizeof(int));
309         self += codec;
310       }
311       break;
312     default: break;
313     }
314   }
315
316   LOGDEBUG(
317
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)
322   );
323
324   if(a_scramble == true) {
325     char* data = const_cast <char*>(self.getData());
326     int size = self.getSize();
327
328     for(int i = 1, key = data [0]; i < size; i ++)
329       data [i] ^= key ++;
330   }
331
332   return self;
333 }
334
335 //-------------------------------------------------------------------------------------------
336 void comm::CompatCodec::decode(const DataBlock& dataBlock)
337 noexcept(false) {
338   const char* data = dataBlock.getData();
339   const int size = dataBlock.getSize();
340
341   if(size < 1)
342     throw RuntimeException("Can not decode an empty DataBlock", ANNA_FILE_LOCATION);
343
344   if(data [0] != 0)
345     for(int i = 1, key(data [0]); i < size; i ++)
346       const_cast <char*>(data)[i] ^= key ++;
347
348   LOGDEBUG(Logger::write(Logger::Debug, "comm::CompatCodec::decode", dataBlock, ANNA_FILE_LOCATION));
349   const int maxdata = data [1];
350
351   if(maxdata != a_variables.size())
352     normalDecode(data, size, maxdata);
353   else if(optimizedDecode(data, size) == false)
354     normalDecode(data, size, maxdata);
355
356   LOGDEBUG(
357
358     for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
359     Logger::debug(CompatCodec::variable(ii)->asString(), ANNA_FILE_LOCATION);
360   );
361 }
362
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 noexcept(false) {
368   // Mientras no se demuestre lo contrario todas las variables son nulas
369   a_nullCounter = a_variables.size();
370
371   for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
372     CompatCodec::variable(ii)->setNull(true);
373
374   short int id;
375   int nbytes;
376   Variable* variable;
377   bool hasError = false;
378   const char* top = data + size;
379   data += 3;
380
381   for(int ndata = 0; ndata < maxdata && data < top; ndata ++) {
382     id = comm::functions::decodeShort(data);
383     data += sizeof(short int);
384
385     if((variable = a_variables.find(id)) == NULL) {
386       hasError = true;
387       break;
388     }
389
390     a_nullCounter --;
391
392     switch(*data  & 0x7f) {
393     case Variable::Type::String:
394       nbytes = comm::functions::decodeShort(++ data);
395       variable->setValue(data += sizeof(short int));
396       data += nbytes;
397       break;
398     case Variable::Type::Integer:
399       variable->setInteger(comm::functions::decodeInteger(++ data));
400       data += sizeof(int);
401       break;
402     case Variable::Type::Integer64:
403       variable->setValue(comm::functions::decodeInteger64(++ data));
404       data += sizeof(S64);
405       break;
406     case  Variable::Type::Boolean:
407       variable->setBoolean((*data & 0x80) ? true : false);
408       data ++;
409       break;
410     case Variable::Type::Block:
411       nbytes = comm::functions::decodeInteger(++ data);
412       variable->setDataBlock(DataBlock(data += sizeof(int), nbytes, false));
413       data += nbytes;
414       break;
415     case Variable::Type::Float:
416       variable->setFloat(comm::functions::decodeFloat(++ data));
417       data += sizeof(float);
418       break;
419     case Variable::Type::Double:
420       variable->setDouble(comm::functions::decodeDouble(++ data));
421       data += sizeof(double);
422       break;
423     case Variable::Type::Custom:
424       nbytes = comm::functions::decodeInteger(++ data);
425       {
426         DataBlock dataBlock(data += sizeof(int), nbytes, false);
427         reinterpret_cast <CompatCodec*>(variable->getCustom())->decode(dataBlock);
428       }
429       data += nbytes;
430       break;
431     }
432   }
433
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);
439   }
440
441   if(a_nullCounter < 0) {
442     a_nullCounter = 0;
443     LOGWARNING(
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);
448     );
449   }
450 }
451
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 noexcept(false) {
458   int nbytes;
459   Variable* variable;
460   const char* top = data + size;
461   data += 3;
462
463   for(iterator ii = begin(), maxii = end(); ii != maxii && data < top; ii ++) {
464     data += sizeof(short int);
465     variable = CompatCodec::variable(ii);
466
467     switch(*data & 0x7f) {
468     case Variable::Type::String:
469       nbytes = comm::functions::decodeShort(++ data);
470       variable->setValue(data += sizeof(short int));
471       data += nbytes;
472       break;
473     case Variable::Type::Integer:
474       variable->setInteger(comm::functions::decodeInteger(++ data));
475       data += sizeof(int);
476       break;
477     case Variable::Type::Integer64:
478       variable->setValue(comm::functions::decodeInteger64(++ data));
479       data += sizeof(S64);
480       break;
481     case  Variable::Type::Boolean:
482       variable->setBoolean((*data & 0x80) ? true : false);
483       data ++;
484       break;
485     case Variable::Type::Block:
486       nbytes = comm::functions::decodeInteger(++ data);
487       variable->setDataBlock(DataBlock(data += sizeof(int), nbytes, false));
488       data += nbytes;
489       break;
490     case Variable::Type::Float:
491       variable->setFloat(comm::functions::decodeFloat(++ data));
492       data += sizeof(float);
493       break;
494     case Variable::Type::Double:
495       variable->setDouble(comm::functions::decodeDouble(++ data));
496       data += sizeof(double);
497       break;
498     case Variable::Type::Custom:
499       nbytes = comm::functions::decodeInteger(++ data);
500       {
501         DataBlock dataBlock(data += sizeof(int), nbytes, false);
502         reinterpret_cast <CompatCodec*>(variable->getCustom())->decode(dataBlock);
503       }
504       data += nbytes;
505       break;
506     }
507   }
508
509   return (data == top);
510 }
511
512 comm::CompatCodec::Type comm::CompatCodec::getType(const DataBlock& dataBlock)
513 noexcept(false) {
514   const int size(dataBlock.getSize());
515
516   if(size <= 1)
517     throw RuntimeException("DataBlock is not valid", ANNA_FILE_LOCATION);
518
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));
523   return result;
524 }
525
526 comm::CompatCodec::VariableContainer::VariableContainer() {
527   a_maxSize = 16;
528   a_size = 0;
529   a_variables = new Variable* [a_maxSize];
530   anna_memset(a_variables, 0, sizeof(Variable*) * a_maxSize);
531 }
532
533 void comm::CompatCodec::VariableContainer::add(comm::Variable* variable)
534 {
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);
540     delete a_variables;
541     a_variables = variables;
542     a_maxSize = maxSize;
543   }
544
545   a_variables [a_size ++] = variable;
546 }
547
548 comm::Variable* comm::CompatCodec::VariableContainer::find(const int id)
549 {
550   for(int ii = 0; ii < a_size; ii ++) {
551     if(a_variables [ii]->getId() == id)
552       return a_variables [ii];
553   }
554
555   return NULL;
556 }
557
558 const comm::Variable* comm::CompatCodec::VariableContainer::find(const int id) const
559 {
560   for(int ii = 0; ii < a_size; ii ++) {
561     if(a_variables [ii]->getId() == id)
562       return a_variables [ii];
563   }
564
565   return NULL;
566 }
567
568 void comm::CompatCodec::VariableContainer::clear()
569 {
570   delete [] a_variables;
571   a_maxSize = a_size = 0;
572 }
573