1 /*
2  * ebusd - daemon for communication with eBUS heating systems.
3  * Copyright (C) 2014-2021 John Baier <ebusd@ebusd.eu>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include "lib/ebus/datatype.h"
24 #include <math.h>
25 #include <iostream>
26 #include <sstream>
27 #include <iomanip>
28 #include <vector>
29 #include <cstring>
30 #ifdef HAVE_CONTRIB
31 #  include "lib/ebus/contrib/contrib.h"
32 #endif
33 
34 namespace ebusd {
35 
36 using std::dec;
37 using std::hex;
38 using std::fixed;
39 using std::setfill;
40 using std::setprecision;
41 using std::setw;
42 using std::endl;
43 
44 
dump(OutputFormat outputFormat,size_t length,bool appendDivisor,ostream * output) const45 bool DataType::dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const {
46   if (outputFormat & OF_JSON) {
47     *output << "\"type\": \"" << m_id << "\", \"isbits\": "
48             << (getBitCount() < 8 ? "true" : "false");
49     if (outputFormat & OF_ALL_ATTRS) {
50       *output << ", \"isadjustable\": " << (isAdjustableLength() ? "true" : "false");
51       *output << ", \"isignored\": " << (isIgnored() ? "true" : "false");
52     }
53     *output << ", \"length\": ";
54     if (isAdjustableLength() && length == REMAIN_LEN) {
55       *output << "-1";
56     } else {
57       *output << static_cast<unsigned>(length);
58     }
59   } else {
60     *output << m_id;
61     if (isAdjustableLength()) {
62       *output << LENGTH_SEPARATOR;
63       if (length == REMAIN_LEN) {
64         *output << "*";
65       } else {
66         *output << static_cast<unsigned>(length);
67       }
68     }
69     if (appendDivisor) {
70       *output << FIELD_SEPARATOR;
71     }
72   }
73   return false;
74 }
75 
76 
dump(OutputFormat outputFormat,size_t length,bool appendDivisor,ostream * output) const77 bool StringDataType::dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const {
78   DataType::dump(outputFormat, length, appendDivisor, output);
79   if ((outputFormat & OF_JSON) && (outputFormat & OF_ALL_ATTRS)) {
80     *output << ", \"result\": \"" << (isIgnored() ? "void" : "string") << "\"";
81   }
82   return false;
83 }
84 
readRawValue(size_t,size_t,const SymbolString &,unsigned int *) const85 result_t StringDataType::readRawValue(size_t, size_t, const SymbolString&, unsigned int*) const {
86   return RESULT_EMPTY;
87 }
88 
readSymbols(size_t offset,size_t length,const SymbolString & input,OutputFormat outputFormat,ostream * output) const89 result_t StringDataType::readSymbols(size_t offset, size_t length, const SymbolString& input,
90                                      OutputFormat outputFormat, ostream* output) const {
91   size_t start = 0, count = length;
92   int incr = 1;
93   symbol_t symbol;
94   bool terminated = false;
95   if (count == REMAIN_LEN && input.getDataSize() > offset) {
96     count = input.getDataSize() - offset;
97   } else if (offset + count > input.getDataSize()) {
98     return RESULT_ERR_INVALID_POS;
99   }
100   if (hasFlag(REV)) {  // reverted binary representation (most significant byte first)
101     start = length - 1;
102     incr = -1;
103   }
104 
105   if (outputFormat & OF_JSON) {
106     *output << '"';
107   }
108   *output << setfill('0') << (m_isHex ? hex : dec);
109   for (size_t index = start, i = 0; i < count; index += incr, i++) {
110     symbol = input.dataAt(offset + index);
111     if (m_isHex) {
112       if (i > 0) {
113         *output << ' ';
114       }
115       *output << setw(2) << static_cast<unsigned>(symbol);
116     } else {
117       if (symbol == 0x00) {
118         terminated = true;
119       } else if (!terminated) {
120         if (symbol < 0x20) {
121           symbol = (symbol_t)m_replacement;
122         } else if (!isprint(symbol)) {
123           symbol = '?';
124         } else if (outputFormat & OF_JSON) {
125           if (symbol == '"' || symbol == '\\') {
126             *output << '\\';  // escape
127           }
128         }
129         *output << static_cast<char>(symbol);
130       }
131     }
132   }
133   if (outputFormat & OF_JSON) {
134     *output << '"';
135   }
136   return RESULT_OK;
137 }
138 
writeSymbols(size_t offset,size_t length,istringstream * input,SymbolString * output,size_t * usedLength) const139 result_t StringDataType::writeSymbols(size_t offset, size_t length, istringstream* input,
140                                       SymbolString* output, size_t* usedLength) const {
141   size_t start = 0, count = length;
142   bool remainder = count == REMAIN_LEN && hasFlag(ADJ);
143   int incr = 1;
144   unsigned int value = 0;
145   string token;
146 
147   if (hasFlag(REV)) {  // reverted binary representation (most significant byte first)
148     start = length - 1;
149     incr = -1;
150   }
151   if (isIgnored() && !hasFlag(REQ)) {
152     if (remainder) {
153       count = 1;
154     }
155     for (size_t index = start, i = 0; i < count; index += incr, i++) {
156       output->dataAt(offset + index) = (symbol_t)m_replacement;  // fill up with replacement
157     }
158     if (usedLength != nullptr) {
159       *usedLength = count;
160     }
161     return RESULT_OK;
162   }
163   result_t result;
164   size_t i = 0, index;
165   for (index = start; i < count; index += incr, i++) {
166     if (m_isHex) {
167       while (!input->eof() && input->peek() == ' ') {
168         input->get();
169       }
170       if (input->eof()) {  // no more digits
171         value = m_replacement;  // fill up with replacement
172       } else {
173         token.clear();
174         token.push_back((symbol_t)input->get());
175         if (input->eof()) {
176           return RESULT_ERR_INVALID_NUM;  // too short hex value
177         }
178         token.push_back((symbol_t)input->get());
179         if (input->eof()) {
180           return RESULT_ERR_INVALID_NUM;  // too short hex value
181         }
182         value = parseInt(token.c_str(), 16, 0, 0xff, &result);
183         if (result != RESULT_OK) {
184           return result;  // invalid hex value
185         }
186       }
187     } else {
188       if (input->eof()) {
189         value = m_replacement;
190       } else {
191         value = input->get();
192         if (input->eof() || value < 0x20) {
193           value = m_replacement;
194         }
195       }
196     }
197     if (remainder && input->eof() && i > 0) {
198       if (value == 0x00 && !m_isHex) {
199         output->dataAt(offset + index) = 0;
200         index += incr;
201       }
202       break;
203     }
204     if (value > 0xff) {
205       return RESULT_ERR_OUT_OF_RANGE;  // value out of range
206     }
207     output->dataAt(offset + index) = (symbol_t)value;
208   }
209 
210   if (!remainder && i < count) {
211     return RESULT_ERR_EOF;  // input too short
212   }
213   if (usedLength != nullptr) {
214     *usedLength = (index-start)*incr;
215   }
216   return RESULT_OK;
217 }
218 
219 
dump(OutputFormat outputFormat,size_t length,bool appendDivisor,ostream * output) const220 bool DateTimeDataType::dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const {
221   DataType::dump(outputFormat, length, appendDivisor, output);
222   if ((outputFormat & OF_JSON) && (outputFormat & OF_ALL_ATTRS)) {
223     *output << ", \"result\": \"" << (hasDate() ? hasTime() ? "datetime" : "date" : "time") << "\"";
224   }
225   return false;
226 }
227 
readRawValue(size_t,size_t,const SymbolString &,unsigned int *) const228 result_t DateTimeDataType::readRawValue(size_t, size_t, const SymbolString&, unsigned int*) const {
229   return RESULT_EMPTY;
230 }
231 
readSymbols(size_t offset,size_t length,const SymbolString & input,OutputFormat outputFormat,ostream * output) const232 result_t DateTimeDataType::readSymbols(size_t offset, size_t length, const SymbolString& input,
233                                        OutputFormat outputFormat, ostream* output) const {
234   size_t start = 0, count = length;
235   int incr = 1;
236   symbol_t symbol, last = 0, hour = 0;
237   unsigned long minutes = 0;
238   if (count == REMAIN_LEN && input.getDataSize() > offset) {
239     count = input.getDataSize() - offset;
240   } else if (offset + count > input.getDataSize()) {
241     return RESULT_ERR_INVALID_POS;
242   }
243   if (hasFlag(REV)) {  // reverted binary representation (most significant byte first)
244     start = length - 1;
245     incr = -1;
246   }
247 
248   if (outputFormat & OF_JSON) {
249     *output << '"';
250   }
251   int type = (m_hasDate?2:0) | (m_hasTime?1:0);
252   for (size_t index = start, i = 0; i < count; index += incr, i++) {
253     if (length == 4 && i == 2 && m_hasDate && !m_hasTime) {
254       continue;  // skip weekday in between
255     }
256     symbol = input.dataAt(offset + index);
257     if (hasFlag(BCD) && (hasFlag(REQ) || symbol != m_replacement)) {
258       if ((symbol & 0xf0) > 0x90 || (symbol & 0x0f) > 0x09) {
259         return RESULT_ERR_OUT_OF_RANGE;  // invalid BCD
260       }
261       symbol = (symbol_t)((symbol >> 4) * 10 + (symbol & 0x0f));
262     }
263     switch (type) {
264       case 2:  // date only
265         if (!hasFlag(REQ) && symbol == m_replacement) {
266           if (i + 1 != length) {
267             *output << NULL_VALUE << ".";
268             break;
269           } else if (last == m_replacement) {
270             if (length == 2) {  // number of days since 01.01.1900
271               *output << NULL_VALUE << ".";
272             }
273             *output << NULL_VALUE;
274             break;
275           }
276         }
277         if (length == 2) {  // number of days since 01.01.1900
278           if (i == 0) {
279             break;
280           }
281           int mjd = last + symbol*256 + 15020;  // 01.01.1900
282           int y = static_cast<int>((mjd-15078.2)/365.25);
283           int m = static_cast<int>((mjd-14956.1-static_cast<int>(y*365.25))/30.6001);
284           int d = mjd-14956-static_cast<int>(y*365.25)-static_cast<int>(m*30.6001);
285           m--;
286           if (m >= 13) {
287             y++;
288             m -= 12;
289           }
290           *output << dec << setfill('0') << setw(2) << static_cast<unsigned>(d) << "."
291                   << setw(2) << static_cast<unsigned>(m) << "." << static_cast<unsigned>(y + 1900);
292           break;
293         }
294         if (i + 1 == length) {
295           *output << (2000 + symbol);
296         } else if (symbol < 1 || (i == 0 && symbol > 31) || (i == 1 && symbol > 12)) {
297           return RESULT_ERR_OUT_OF_RANGE;  // invalid date
298         } else {
299           *output << setw(2) << dec << setfill('0') << static_cast<unsigned>(symbol) << ".";
300         }
301         break;
302 
303       case 1:  // time only
304         if (!hasFlag(REQ) && symbol == m_replacement) {
305           if (length == 1) {  // truncated time
306             *output << NULL_VALUE << ":" << NULL_VALUE;
307             break;
308           }
309           if (i > 0) {
310             *output << ":";
311           }
312           *output << NULL_VALUE;
313           break;
314         }
315         if (hasFlag(SPE)) {  // minutes since midnight
316           if (i == 0) {
317             last = symbol;
318             continue;
319           }
320           minutes = symbol*256 + last;
321           if (minutes > 24*60) {
322             return RESULT_ERR_OUT_OF_RANGE;  // invalid value
323           }
324           unsigned int minutesHour = (unsigned int)(minutes / 60);
325           if (minutesHour > 24) {
326             return RESULT_ERR_OUT_OF_RANGE;  // invalid hour
327           }
328           *output << setw(2) << dec << setfill('0') << minutesHour;
329           symbol = (symbol_t)(minutes % 60);
330         } else if (length == 1) {  // truncated time
331           if (m_bitCount < 8) {
332             symbol = (symbol_t)(symbol & ((1 << m_bitCount) - 1));
333           }
334           if (i == 0) {
335             symbol = (symbol_t)(symbol/(60/m_resolution));  // convert to hours
336             index -= incr;  // repeat for minutes
337             count++;
338           } else {
339             symbol = (symbol_t)((symbol % (60/m_resolution)) * m_resolution);  // convert to minutes
340           }
341         }
342         if (i == 0) {
343           if (symbol > 24) {
344             return RESULT_ERR_OUT_OF_RANGE;  // invalid hour
345           }
346           hour = symbol;
347         } else if (symbol > 59 || (hour == 24 && symbol > 0)) {
348           return RESULT_ERR_OUT_OF_RANGE;  // invalid time
349         }
350         if (i > 0) {
351           *output << ":";
352         }
353         *output << setw(2) << dec << setfill('0') << static_cast<unsigned>(symbol);
354         break;
355 
356       case 3:  // date+time
357         if (length != 4) {
358           return RESULT_ERR_INVALID_POS;
359         }
360         // number of minutes since 01.01.2009
361         minutes |= symbol*(1<<(8*i));
362         if (i<3) {
363           break;
364         }
365         int mjd = static_cast<int>(minutes/(24*60)) + 54832;  // 01.01.2009
366         int y = static_cast<int>((mjd-15078.2)/365.25);
367         int m = static_cast<int>((mjd-14956.1-static_cast<int>(y*365.25))/30.6001);
368         int d = mjd-14956-static_cast<int>(y*365.25)-static_cast<int>(m*30.6001);
369         m--;
370         if (m >= 13) {
371           y++;
372           m -= 12;
373         }
374         *output << dec << setfill('0') << setw(2) << static_cast<unsigned>(d) << "."
375                 << setw(2) << static_cast<unsigned>(m) << "." << static_cast<unsigned>(y + 1900);
376         m = static_cast<int>(minutes%(24*60));
377         d = m/60;
378         *output << " " << setw(2) << dec << setfill('0') << static_cast<unsigned>(d);
379         m -= d*60;
380         *output << ":" << setw(2) << dec << setfill('0') << static_cast<unsigned>(m);
381         break;
382     }
383     last = symbol;
384   }
385   if (outputFormat & OF_JSON) {
386     *output << '"';
387   }
388   return RESULT_OK;
389 }
390 
writeSymbols(size_t offset,size_t length,istringstream * input,SymbolString * output,size_t * usedLength) const391 result_t DateTimeDataType::writeSymbols(size_t offset, size_t length, istringstream* input,
392                                         SymbolString* output, size_t* usedLength) const {
393   size_t start = 0, count = length;
394   bool remainder = count == REMAIN_LEN && hasFlag(ADJ);
395   int incr = 1;
396   unsigned int value = 0, last = 0, lastLast = 0;
397   string token;
398 
399   if (hasFlag(REV)) {  // reverted binary representation (most significant byte first)
400     start = length - 1;
401     incr = -1;
402   }
403   if (isIgnored() && !hasFlag(REQ)) {
404     if (remainder) {
405       count = 1;
406     }
407     for (size_t index = start, i = 0; i < count; index += incr, i++) {
408       output->dataAt(offset + index) = (symbol_t)m_replacement;  // fill up with replacement
409     }
410     if (usedLength != nullptr) {
411       *usedLength = count;
412     }
413     return RESULT_OK;
414   }
415   result_t result;
416   size_t i = 0, index;
417   int type = m_hasDate ? 2 : (m_hasTime ? 1 : 0);
418   bool skip = false;
419   for (index = start; i < count; index += skip ? 0 : incr, i++) {
420     skip = false;
421     switch (type) {
422       case 2:  // date only
423         if (length == 4 && i == 2 && !m_hasTime) {
424           continue;  // skip weekday in between
425         }
426         if (input->eof() || !getline(*input, token, m_hasTime && i==2 ? ' ' : '.')) {
427           return RESULT_ERR_EOF;  // incomplete
428         }
429         if (!hasFlag(REQ) && token == NULL_VALUE) {
430           value = m_replacement;
431           break;
432         }
433         value = parseInt(token.c_str(), 10, 0, 2099, &result);
434         if (result != RESULT_OK) {
435           return result;  // invalid date part
436         }
437         if (length == 2) {  // number of days since 01.01.1900
438           skip = true;
439           if (i == 0) {
440             count++;
441           } else if (i + 1 == count) {
442             int y = (value < 100 ? value + 2000 : value) - 1900;
443             int l = last <= 2 ? 1 : 0;
444             int mjd = 14956 + lastLast + static_cast<int>((y-l)*365.25) + static_cast<int>((last+1+l*12)*30.6001);
445             value = mjd - 15020;  // 01.01.1900
446             output->dataAt(offset + index) = (symbol_t)(value&0xff);
447             value >>=  8;
448             index += incr;
449             skip = false;
450             break;
451           }
452         }
453         if (i + 1 == (m_hasTime ? count-1 : count)) {
454           if (length == 4) {
455             int y = (value < 100 ? value + 2000 : value) - 1900;
456             int l = last <= 2 ? 1 : 0;
457             int mjd = 14956 + lastLast + static_cast<int>((y-l)*365.25) + static_cast<int>((last+1+l*12)*30.6001);
458             if (m_hasTime) {
459               if (mjd < 54832) {  // 01.01.2009
460                 return RESULT_ERR_OUT_OF_RANGE;  // invalid date
461               }
462               last = mjd - 54832;
463               index = start + incr;
464               i = 1;
465               type = 1;
466               skip = true; // switch to second pass for parsing the time
467             } else {
468               // calculate local week day
469               int daysSinceSunday = (mjd + 3) % 7;  // Sun=0
470               if (hasFlag(SPE)) {
471                 output->dataAt(offset + index - incr) = (symbol_t) ((6 + daysSinceSunday) % 7);  // Sun=0x06
472               } else {
473                 // Sun=0x07
474                 output->dataAt(offset + index - incr) = (symbol_t) (daysSinceSunday == 0 ? 7 : daysSinceSunday);
475               }
476             }
477           }
478           if (value >= 2000) {
479             value -= 2000;
480           }
481           if (value > 99) {
482             return RESULT_ERR_OUT_OF_RANGE;  // invalid year
483           }
484         } else if (value < 1 || (i == 0 && value > 31) || (i == 1 && value > 12)) {
485           return RESULT_ERR_OUT_OF_RANGE;  // invalid date part
486         }
487         break;
488 
489       case 1:  // time only
490         if (input->eof() || !getline(*input, token, LENGTH_SEPARATOR)) {
491           return RESULT_ERR_EOF;  // incomplete
492         }
493         if (!hasFlag(REQ) && token == NULL_VALUE) {
494           value = m_replacement;
495           if (length == 1) {  // truncated time
496             if (i == 0) {
497               skip = true;  // repeat for minutes
498               count++;
499               break;
500             }
501             if (last != m_replacement) {
502               return RESULT_ERR_INVALID_NUM;  // invalid truncated time minutes
503             }
504           }
505           break;
506         }
507         value = parseInt(token.c_str(), 10, 0, 59, &result);
508         if (result != RESULT_OK) {
509           return result;  // invalid time part
510         }
511         if ((i == 0 && value > 24) || (i > 0 && (last == 24 && value > 0) )) {
512           return RESULT_ERR_OUT_OF_RANGE;  // invalid time part
513         }
514         if (hasFlag(SPE)) {  // minutes since midnight
515           if (i == 0) {
516             skip = true;  // repeat for minutes
517             break;
518           }
519           value += last*60;
520           output->dataAt(offset + index) = (symbol_t)(value&0xff);
521           value >>=  8;
522           index += incr;
523         } else if (length == 1) {  // truncated time
524           if (i == 0) {
525             skip = true;  // repeat for minutes
526             count++;
527             break;
528           }
529           value = (last * 60 + value + m_resolution/2)/m_resolution;
530           if (value > 24 * 6) {
531             return RESULT_ERR_OUT_OF_RANGE;  // invalid time
532           }
533         } else if (m_hasDate) {
534           if (i + 1 == count) {
535             last = (lastLast * 24 + last) * 60 + value;
536             value = last & 0xff;
537             last >>= 8;
538             index = start;
539             i = 0;
540             type = 3;
541           } else {
542             last = lastLast;
543             skip = true;
544           }
545         }
546         break;
547 
548       case 3: // date and time in store phase
549         value = lastLast & 0xff;
550         last = lastLast >> 8;
551         break;
552     }
553     lastLast = last;
554     last = value;
555     if (!skip) {
556       if (hasFlag(BCD) && (hasFlag(REQ) || value != m_replacement)) {
557         if (value > 99) {
558           return RESULT_ERR_OUT_OF_RANGE;  // invalid BCD
559         }
560         value = ((value / 10) << 4) | (value % 10);
561       }
562       if (value > 0xff) {
563         return RESULT_ERR_OUT_OF_RANGE;  // value out of range
564       }
565       output->dataAt(offset + index) = (symbol_t)value;
566     }
567   }
568 
569   if (!remainder && i < count) {
570     return RESULT_ERR_EOF;  // input too short
571   }
572   if (usedLength != nullptr) {
573     *usedLength = (index-start)*incr;
574   }
575   return RESULT_OK;
576 }
577 
578 
calcPrecision(int divisor)579 size_t NumberDataType::calcPrecision(int divisor) {
580   size_t precision = 0;
581   if (divisor > 1) {
582     for (unsigned int exp = 1; exp < MAX_DIVISOR; exp *= 10, precision++) {
583       if (exp >= (unsigned int)divisor) {
584         break;
585       }
586     }
587   }
588   return precision;
589 }
590 
dump(OutputFormat outputFormat,size_t length,bool appendDivisor,ostream * output) const591 bool NumberDataType::dump(OutputFormat outputFormat, size_t length, bool appendDivisor, ostream* output) const {
592   if (m_bitCount < 8) {
593     DataType::dump(outputFormat, m_bitCount, appendDivisor, output);
594   } else {
595     DataType::dump(outputFormat, length, appendDivisor, output);
596   }
597   if ((outputFormat & OF_JSON) && (outputFormat & OF_ALL_ATTRS)) {
598     *output << ", \"result\": \"number\"";
599   }
600   if (!appendDivisor) {
601     return false;
602   }
603   bool ret = false;
604   if (m_baseType) {
605     if (m_baseType->m_divisor != m_divisor) {
606       if (outputFormat & OF_JSON) {
607         *output << ", \"divisor\": ";
608       }
609       *output << (m_divisor / m_baseType->m_divisor);
610       ret = true;
611     }
612   } else if (m_divisor != 1) {
613     if (outputFormat & OF_JSON) {
614       *output << ", \"divisor\": ";
615     }
616     *output << m_divisor;
617     ret = true;
618   }
619   if (ret && (outputFormat & OF_JSON) && (outputFormat & OF_ALL_ATTRS)) {
620     *output << ", \"precision\": " << static_cast<unsigned>(getPrecision());
621   }
622   return ret;
623 }
624 
derive(int divisor,size_t bitCount,const NumberDataType ** derived) const625 result_t NumberDataType::derive(int divisor, size_t bitCount, const NumberDataType** derived) const {
626   if (divisor == 0) {
627     divisor = 1;
628   }
629   if (m_divisor != 1) {
630     if (divisor == 1) {
631       divisor = m_divisor;
632     } else if (divisor < 0) {
633       if (m_divisor > 1) {
634         return RESULT_ERR_INVALID_ARG;
635       }
636       divisor *= -m_divisor;
637     } else if (m_divisor < 0) {
638       if (divisor > 1) {
639         return RESULT_ERR_INVALID_ARG;
640       }
641       divisor *= -m_divisor;
642     } else {
643       divisor *= m_divisor;
644     }
645   }
646   if (divisor == m_divisor && bitCount == m_bitCount) {
647     *derived = this;
648     return RESULT_OK;
649   }
650   if (-MAX_DIVISOR > divisor || divisor > MAX_DIVISOR) {
651     return RESULT_ERR_OUT_OF_RANGE;
652   }
653   if (bitCount <= 0 || bitCount == m_bitCount) {
654     bitCount = m_bitCount;
655   } else if (isAdjustableLength()) {
656     if (m_bitCount < 8) {
657       if (bitCount+m_firstBit > 8) {
658         return RESULT_ERR_OUT_OF_RANGE;
659       }
660     } else if ((bitCount%8) == 0) {
661       return RESULT_ERR_INVALID_ARG;
662     }
663   } else {
664     return RESULT_ERR_INVALID_ARG;
665   }
666   if (m_bitCount < 8) {
667     *derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement,
668                                   m_firstBit, divisor, m_baseType ? m_baseType : this);
669   } else {
670     *derived = new NumberDataType(m_id, bitCount, m_flags, m_replacement,
671                                   m_minValue, m_maxValue, divisor, m_baseType ? m_baseType : this);
672   }
673   DataTypeList::getInstance()->addCleanup(*derived);
674   return RESULT_OK;
675 }
676 
readRawValue(size_t offset,size_t length,const SymbolString & input,unsigned int * value) const677 result_t NumberDataType::readRawValue(size_t offset, size_t length, const SymbolString& input,
678                                       unsigned int* value) const {
679   size_t start = 0, count = length;
680   int incr = 1;
681   symbol_t symbol;
682 
683   if (offset + length > input.getDataSize()) {
684     return RESULT_ERR_INVALID_POS;  // not enough data available
685   }
686   if (hasFlag(REV)) {  // reverted binary representation (most significant byte first)
687     start = length - 1;
688     incr = -1;
689   }
690 
691   *value = 0;
692   unsigned int exp = 1;
693   for (size_t index = start, i = 0; i < count; index += incr, i++) {
694     symbol = input.dataAt(offset + index);
695     if (hasFlag(BCD)) {
696       if (!hasFlag(REQ) && symbol == (m_replacement & 0xff)) {
697         *value = m_replacement;
698         return RESULT_OK;
699       }
700       if (!hasFlag(HCD)) {
701         if ((symbol & 0xf0) > 0x90 || (symbol & 0x0f) > 0x09) {
702           return RESULT_ERR_OUT_OF_RANGE;  // invalid BCD
703         }
704         symbol = (symbol_t)((symbol >> 4) * 10 + (symbol & 0x0f));
705       } else if (symbol > 0x63) {
706         return RESULT_ERR_OUT_OF_RANGE;  // invalid HCD
707       }
708       *value += symbol * exp;
709       exp *= 100;
710     } else {
711       *value |= symbol * exp;
712       exp <<=  8;
713     }
714   }
715   if (m_firstBit > 0) {
716     *value >>=  m_firstBit;
717   }
718   if (m_bitCount < 8) {
719     *value &= (1 << m_bitCount) - 1;
720   }
721 
722   return RESULT_OK;
723 }
724 
readSymbols(size_t offset,size_t length,const SymbolString & input,OutputFormat outputFormat,ostream * output) const725 result_t NumberDataType::readSymbols(size_t offset, size_t length, const SymbolString& input,
726                                      OutputFormat outputFormat, ostream* output) const {
727   unsigned int value = 0;
728   int signedValue;
729 
730   result_t result = readRawValue(offset, length, input, &value);
731   if (result != RESULT_OK) {
732     return result;
733   }
734   *output << setw(0) << dec;  // initialize output
735 
736   if (!hasFlag(REQ) && value == m_replacement) {
737     if (outputFormat & OF_JSON) {
738       *output << "null";
739     } else {
740       *output << NULL_VALUE;
741     }
742     return RESULT_OK;
743   }
744 
745   bool negative;
746   if (hasFlag(SIG)) {  // signed value
747     negative = (value & (1 << (m_bitCount - 1))) != 0;
748     if (negative) {  // negative signed value
749       if (value < m_minValue) {
750         return RESULT_ERR_OUT_OF_RANGE;  // value out of range
751       }
752     } else if (value > m_maxValue) {
753       return RESULT_ERR_OUT_OF_RANGE;  // value out of range
754     }
755   } else if (value < m_minValue || value > m_maxValue) {
756     return RESULT_ERR_OUT_OF_RANGE;  // value out of range
757   } else {
758     negative = false;
759   }
760   if (m_bitCount == 32) {
761     if (hasFlag(EXP)) {  // IEEE 754 binary32
762       float val;
763 #ifdef HAVE_DIRECT_FLOAT_FORMAT
764 #  if HAVE_DIRECT_FLOAT_FORMAT == 2
765       value = __builtin_bswap32(value);
766 #  endif
767       symbol_t* pval = reinterpret_cast<symbol_t*>(&value);
768       val = *reinterpret_cast<float*>(pval);
769 #else
770       int exp = (value >> 23) & 0xff;  // 8 bits, signed
771       if (exp == 0) {
772         val = 0.0;
773       } else {
774         exp -= 127;
775         unsigned int sig = value & ((1 << 23) - 1);
776         val = (1.0f + static_cast<float>(sig / exp2(23))) * static_cast<float>(exp2(exp));
777         if (negative) {
778           val = -val;
779         }
780       }
781 #endif
782       if (val != val) {  // !isnan(val)
783         if (outputFormat & OF_JSON) {
784           *output << "null";
785         } else {
786           *output << NULL_VALUE;
787         }
788         return RESULT_OK;
789       }
790       if (val != 0.0) {
791         if (m_divisor < 0) {
792           val *= static_cast<float>(-m_divisor);
793         } else if (m_divisor > 1) {
794           val /= static_cast<float>(m_divisor);
795         }
796       }
797       if (m_precision != 0) {
798         *output << fixed << setprecision(static_cast<int>(m_precision+6));
799       } else if (val == 0) {
800         *output << fixed << setprecision(1);
801       }
802       *output << static_cast<double>(val);
803       return RESULT_OK;
804     }
805     if (!negative) {
806       if (m_divisor < 0) {
807         *output << (static_cast<float>(value) * static_cast<float>(-m_divisor));
808       } else if (m_divisor <= 1) {
809         *output << value;
810       } else {
811         *output << setprecision(static_cast<int>(m_precision))
812                 << fixed << (static_cast<float>(value) / static_cast<float>(m_divisor));
813       }
814       return RESULT_OK;
815     }
816     signedValue = static_cast<int>(value);  // negative signed value
817   } else if (negative) {  // negative signed value
818     signedValue = static_cast<int>(value) - (1 << m_bitCount);
819   } else {
820     signedValue = static_cast<int>(value);
821   }
822   if (m_divisor < 0) {
823     *output << fixed << setprecision(0)
824             << (static_cast<float>(signedValue) * static_cast<float>(-m_divisor));
825   } else if (m_divisor <= 1) {
826     if (hasFlag(FIX) && hasFlag(BCD)) {
827       if (outputFormat & OF_JSON) {
828         *output << '"' << setw(static_cast<int>(length * 2))
829                 << setfill('0') << signedValue << setw(0) << '"';
830         return RESULT_OK;
831       }
832       *output << setw(static_cast<int>(length * 2)) << setfill('0');
833     }
834     *output << signedValue << setw(0);
835   } else {
836     *output << setprecision(static_cast<int>(m_precision))
837             << fixed << (static_cast<float>(signedValue) / static_cast<float>(m_divisor));
838   }
839   return RESULT_OK;
840 }
841 
writeRawValue(unsigned int value,size_t offset,size_t length,SymbolString * output,size_t * usedLength) const842 result_t NumberDataType::writeRawValue(unsigned int value, size_t offset, size_t length,
843                                        SymbolString* output, size_t* usedLength) const {
844   size_t start = 0, count = length;
845   int incr = 1;
846   symbol_t symbol;
847 
848   if (m_bitCount < 8 && (value & ~((1 << m_bitCount) - 1)) != 0) {
849     return RESULT_ERR_OUT_OF_RANGE;
850   }
851   if (m_firstBit > 0) {
852     value <<=  m_firstBit;
853   }
854 
855   if (hasFlag(REV)) {  // reverted binary representation (most significant byte first)
856     start = length - 1;
857     incr = -1;
858   }
859 
860   for (size_t index = start, i = 0, exp = 1; i < count; index += incr, i++) {
861     if (hasFlag(BCD)) {
862       if (!hasFlag(REQ) && value == m_replacement) {
863         symbol = m_replacement & 0xff;
864       } else {
865         symbol = (symbol_t)((value / exp) % 100);
866         if (!hasFlag(HCD)) {
867           symbol = (symbol_t)(((symbol / 10) << 4) | (symbol % 10));
868         }
869       }
870       exp *= 100;
871     } else {
872       symbol = (value / exp) & 0xff;
873       exp <<=  8;
874     }
875     if (index == start && (m_bitCount % 8) != 0 && offset + index < output->getCalculatedDataSize()) {
876       output->dataAt(offset + index) |= symbol;
877     } else {
878       output->dataAt(offset + index) = symbol;
879     }
880   }
881   if (usedLength != nullptr) {
882     *usedLength = length;
883   }
884   return RESULT_OK;
885 }
886 
writeSymbols(size_t offset,size_t length,istringstream * input,SymbolString * output,size_t * usedLength) const887 result_t NumberDataType::writeSymbols(size_t offset, size_t length, istringstream* input,
888                                       SymbolString* output, size_t* usedLength) const {
889   unsigned int value;
890 
891   const string inputStr = input->str();
892   if (!hasFlag(REQ) && (isIgnored() || inputStr == NULL_VALUE)) {
893     value = m_replacement;  // replacement value
894   } else if (inputStr.empty()) {
895     return RESULT_ERR_EOF;  // input too short
896   } else if (hasFlag(EXP)) {  // IEEE 754 binary32
897     const char* str = inputStr.c_str();
898     char* strEnd = nullptr;
899     double dvalue = strtod(str, &strEnd);
900     if (strEnd == nullptr || strEnd == str || *strEnd != 0) {
901       return RESULT_ERR_INVALID_NUM;  // invalid value
902     }
903     if (m_divisor < 0) {
904       dvalue /= -m_divisor;
905     } else if (m_divisor > 1) {
906       dvalue *= m_divisor;
907     }
908 #ifdef HAVE_DIRECT_FLOAT_FORMAT
909     float val = static_cast<float>(dvalue);
910     symbol_t* pval = reinterpret_cast<symbol_t*>(&val);
911     value = *reinterpret_cast<int32_t*>(pval);
912 #  if HAVE_DIRECT_FLOAT_FORMAT == 2
913     value = __builtin_bswap32(value);
914 #  endif
915 #else
916     value = 0;
917     if (dvalue != 0) {
918       bool negative = dvalue < 0;
919       if (negative) {
920         dvalue = -dvalue;
921       }
922       int exp = ilogb(dvalue);
923       if (exp < -126 || exp > 127) {
924         return RESULT_ERR_INVALID_NUM;  // invalid value
925       }
926       dvalue = scalbln(dvalue, -exp) - 1.0;
927       unsigned int sig = (unsigned int)(dvalue * exp2(23));
928       exp += 127;
929       value = (exp << 23) | sig;
930       if (negative) {
931         value |= 0x80000000;
932       }
933     }
934 #endif
935   } else {
936     const char* str = inputStr.c_str();
937     char* strEnd = nullptr;
938     if (m_divisor == 1) {
939       if (hasFlag(SIG)) {
940         long signedValue = strtol(str, &strEnd, 10);
941         if (signedValue < 0 && m_bitCount != 32) {
942           value = (unsigned int)(signedValue + (1 << m_bitCount));
943         } else {
944           value = (unsigned int)signedValue;
945         }
946       } else {
947         value = (unsigned int)strtoul(str, &strEnd, 10);
948       }
949       if (strEnd == nullptr || strEnd == str || (*strEnd != 0 && *strEnd != '.')) {
950         return RESULT_ERR_INVALID_NUM;  // invalid value
951       }
952     } else {
953       double dvalue = strtod(str, &strEnd);
954       if (strEnd == nullptr || strEnd == str || *strEnd != 0) {
955         return RESULT_ERR_INVALID_NUM;  // invalid value
956       }
957       if (m_divisor < 0) {
958         dvalue = round(dvalue / -m_divisor);
959       } else {
960         dvalue = round(dvalue * m_divisor);
961       }
962       if (hasFlag(SIG)) {
963         if (dvalue < -exp2((8 * static_cast<double>(length)) - 1)
964             || dvalue >= exp2((8 * static_cast<double>(length)) - 1)) {
965           return RESULT_ERR_OUT_OF_RANGE;  // value out of range
966         }
967         if (dvalue < 0 && m_bitCount != 32) {
968           value = static_cast<int>(dvalue + (1 << m_bitCount));
969         } else {
970           value = static_cast<int>(dvalue);
971         }
972       } else {
973         if (dvalue < 0.0 || dvalue >= exp2(8 * static_cast<double>(length))) {
974           return RESULT_ERR_OUT_OF_RANGE;  // value out of range
975         }
976         value = (unsigned int)dvalue;
977       }
978     }
979 
980     if (hasFlag(SIG)) {  // signed value
981       if ((value & (1 << (m_bitCount - 1))) != 0) {  // negative signed value
982         if (value < m_minValue) {
983           return RESULT_ERR_OUT_OF_RANGE;  // value out of range
984         }
985       } else if (value > m_maxValue) {
986         return RESULT_ERR_OUT_OF_RANGE;  // value out of range
987       }
988     } else if (value < m_minValue || value > m_maxValue) {
989       return RESULT_ERR_OUT_OF_RANGE;  // value out of range
990     }
991   }
992 
993   return writeRawValue(value, offset, length, output, usedLength);
994 }
995 
996 
997 DataTypeList DataTypeList::s_instance;
998 
999 #ifdef HAVE_CONTRIB
1000 bool DataTypeList::s_contrib_initialized = libebus_contrib_register();
1001 #endif
1002 
1003 
DataTypeList()1004 DataTypeList::DataTypeList() {
1005   add(new StringDataType("STR", MAX_LEN*8, ADJ, ' '));  // >= 1 byte character string filled up with space
1006   // unsigned decimal in BCD, 0000 - 9999 (fixed length)
1007   add(new NumberDataType("PIN", 16, FIX|BCD|REV, 0xffff, 0, 0x9999, 1));
1008   add(new NumberDataType("UCH", 8, 0, 0xff, 0, 0xfe, 1));  // unsigned integer, 0 - 254
1009   add(new StringDataType("IGN", MAX_LEN*8, IGN|ADJ, 0));  // >= 1 byte ignored data
1010   // >= 1 byte character string filled up with 0x00 (null terminated string)
1011   add(new StringDataType("NTS", MAX_LEN*8, ADJ, 0));
1012   // >= 1 byte hex digit string, usually separated by space, e.g. 0a 1b 2c 3d
1013   add(new StringDataType("HEX", MAX_LEN*8, ADJ, 0, true));
1014   // date with weekday in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x31,0x12,WW,0x99,
1015   // WW is weekday Mon=0x01 - Sun=0x07, replacement 0xff)
1016   add(new DateTimeDataType("BDA", 32, BCD, 0xff, true, false, 0));
1017   add(new DateTimeDataType("BDA:4", 32, BCD|DUP, 0xff, true, false, 0));
1018   // date in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x31,0x12,0x99, replacement 0xff)
1019   add(new DateTimeDataType("BDA:3", 24, BCD, 0xff, true, false, 0));
1020   // date with zero-based weekday in BCD, 01.01.2000 - 31.12.2099 (0x01,0x01,WZ,0x00 - 0x31,0x12,WZ,0x99,
1021   // WZ is zero-based weekday Mon=0x00 - Sun=0x06, replacement 0xff)
1022   add(new DateTimeDataType("BDZ", 32, BCD|SPE, 0xff, true, false, 0));
1023   // date with weekday, 01.01.2000 - 31.12.2099 (0x01,0x01,WW,0x00 - 0x1f,0x0c,WW,0x63,
1024   // WW is weekday Mon=0x01 - Sun=0x07, replacement 0xff)
1025   add(new DateTimeDataType("HDA", 32, 0, 0xff, true, false, 0));
1026   add(new DateTimeDataType("HDA:4", 32, DUP, 0xff, true, false, 0));
1027   // date, 01.01.2000 - 31.12.2099 (0x01,0x01,0x00 - 0x1f,0x0c,0x63, replacement 0xff)
1028   add(new DateTimeDataType("HDA:3", 24, 0, 0xff, true, false, 0));
1029   // date, days since 01.01.1900, 01.01.1900 - 06.06.2079 (0x00,0x00 - 0xff,0xff)
1030   add(new DateTimeDataType("DAY", 16, 0, 0xff, true, false, 0));
1031   // date+time in minutes since 01.01.2009, 01.01.2009 - 31.12.2099 (0x00,0x00,0x00,0x00 - 0x02,0xda,0x4e,0x1f)
1032   add(new DateTimeDataType("DTM", 32, REQ, 0x100, true, true, 0));
1033   // time in BCD, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x59,0x59,0x23)
1034   add(new DateTimeDataType("BTI", 24, BCD|REV, 0xff, false, true, 0));
1035   // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x17,0x3b,0x3b)
1036   add(new DateTimeDataType("HTI", 24, 0, 0xff, false, true, 0));
1037   // time, 00:00:00 - 23:59:59 (0x00,0x00,0x00 - 0x3b,0x3b,0x17, replacement 0x63) [Vaillant type]
1038   add(new DateTimeDataType("VTI", 24, REV, 0x63, false, true, 0));
1039   // time as hh:mm in BCD, 00:00 - 23:59 (0x00,0x00 - 0x59,0x23, replacement 0xff)
1040   add(new DateTimeDataType("BTM", 16, BCD|REV, 0xff, false, true, 0));
1041   // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x17,0x3b)
1042   add(new DateTimeDataType("HTM", 16, 0, 0xff, false, true, 0));
1043   // time as hh:mm, 00:00 - 23:59 (0x00,0x00 - 0x3b,0x17, replacement 0xff) [Vaillant type]
1044   add(new DateTimeDataType("VTM", 16, REV, 0xff, false, true, 0));
1045   // time, minutes since last midnight, 00:00 - 24:00 (minutes + hour * 60 as integer)
1046   add(new DateTimeDataType("MIN", 16, SPE, 0xff, false, true, 0));
1047   // truncated time (only multiple of 10 minutes), 00:00 - 24:00 (minutes div 10 + hour * 6 as integer)
1048   add(new DateTimeDataType("TTM", 8, 0, 0x90, false, true, 10));
1049   // truncated time (only multiple of 30 minutes), 00:00 - 24:00 (minutes div 30 + hour * 2 as integer)
1050   add(new DateTimeDataType("TTH", 6, 0, 0, false, true, 30));
1051   // truncated time (only multiple of 15 minutes), 00:00 - 24:00 (minutes div 15 + hour * 4 as integer)
1052   add(new DateTimeDataType("TTQ", 7, 0, 0, false, true, 15));
1053   add(new NumberDataType("BDY", 8, DAY, 0x07, 0, 6, 1));  // weekday, "Mon" - "Sun" (0x00 - 0x06) [eBUS type]
1054   add(new NumberDataType("HDY", 8, DAY, 0x00, 1, 7, 1));  // weekday, "Mon" - "Sun" (0x01 - 0x07) [Vaillant type]
1055   add(new NumberDataType("BCD", 8, BCD, 0xff, 0, 99, 1));  // unsigned decimal in BCD, 0 - 99
1056   add(new NumberDataType("BCD:1", 8, BCD|DUP, 0xff, 0, 99, 1));  // unsigned decimal in BCD, 0 - 99
1057   add(new NumberDataType("BCD:2", 16, BCD, 0xffff, 0, 9999, 1));  // unsigned decimal in BCD, 0 - 9999
1058   add(new NumberDataType("BCD:3", 24, BCD, 0xffffff, 0, 999999, 1));  // unsigned decimal in BCD, 0 - 999999
1059   add(new NumberDataType("BCD:4", 32, BCD, 0xffffffff, 0, 99999999, 1));  // unsigned decimal in BCD, 0 - 99999999
1060   add(new NumberDataType("HCD", 32, HCD|BCD|REQ, 0, 0, 99999999, 1));  // unsigned decimal in HCD, 0 - 99999999
1061   add(new NumberDataType("HCD:4", 32, HCD|BCD|REQ|DUP, 0, 0, 99999999, 1));  // unsigned decimal in HCD, 0 - 99999999
1062   add(new NumberDataType("HCD:1", 8, HCD|BCD|REQ, 0, 0, 99, 1));  // unsigned decimal in HCD, 0 - 99
1063   add(new NumberDataType("HCD:2", 16, HCD|BCD|REQ, 0, 0, 9999, 1));  // unsigned decimal in HCD, 0 - 9999
1064   add(new NumberDataType("HCD:3", 24, HCD|BCD|REQ, 0, 0, 999999, 1));  // unsigned decimal in HCD, 0 - 999999
1065   add(new NumberDataType("SCH", 8, SIG, 0x80, 0x81, 0x7f, 1));  // signed integer, -127 - +127
1066   add(new NumberDataType("D1B", 8, SIG, 0x80, 0x81, 0x7f, 1));  // signed integer, -127 - +127
1067   // unsigned number (fraction 1/2), 0 - 100 (0x00 - 0xc8, replacement 0xff)
1068   add(new NumberDataType("D1C", 8, 0, 0xff, 0x00, 0xc8, 2));
1069   // signed number (fraction 1/256), -127.99 - +127.99
1070   add(new NumberDataType("D2B", 16, SIG, 0x8000, 0x8001, 0x7fff, 256));
1071   // signed number (fraction 1/16), -2047.9 - +2047.9
1072   add(new NumberDataType("D2C", 16, SIG, 0x8000, 0x8001, 0x7fff, 16));
1073   // signed number (fraction 1/1000), -32.767 - +32.767, little endian
1074   add(new NumberDataType("FLT", 16, SIG, 0x8000, 0x8001, 0x7fff, 1000));
1075   // signed number (fraction 1/1000), -32.767 - +32.767, big endian
1076   add(new NumberDataType("FLR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1000));
1077   // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), little endian
1078   add(new NumberDataType("EXP", 32, SIG|EXP, 0x7f800000, 0x00000000, 0xffffffff, 1));
1079   // signed number (IEEE 754 binary32: 1 bit sign, 8 bits exponent, 23 bits significand), big endian
1080   add(new NumberDataType("EXR", 32, SIG|EXP|REV, 0x7f800000, 0x00000000, 0xffffffff, 1));
1081   // unsigned integer, 0 - 65534, little endian
1082   add(new NumberDataType("UIN", 16, 0, 0xffff, 0, 0xfffe, 1));
1083   // unsigned integer, 0 - 65534, big endian
1084   add(new NumberDataType("UIR", 16, REV, 0xffff, 0, 0xfffe, 1));
1085   // signed integer, -32767 - +32767, little endian
1086   add(new NumberDataType("SIN", 16, SIG, 0x8000, 0x8001, 0x7fff, 1));
1087   // signed integer, -32767 - +32767, big endian
1088   add(new NumberDataType("SIR", 16, SIG|REV, 0x8000, 0x8001, 0x7fff, 1));
1089   // unsigned 3 bytes int, 0 - 16777214, little endian
1090   add(new NumberDataType("U3N", 24, 0, 0xffffff, 0, 0xfffffe, 1));
1091   // unsigned 3 bytes int, 0 - 16777214, big endian
1092   add(new NumberDataType("U3R", 24, REV, 0xffffff, 0, 0xfffffe, 1));
1093   // signed 3 bytes int, -8388607 - +8388607, little endian
1094   add(new NumberDataType("S3N", 24, SIG, 0x800000, 0x800001, 0xffffff, 1));
1095   // signed 3 bytes int, -8388607 - +8388607, big endian
1096   add(new NumberDataType("S3R", 24, SIG|REV, 0x800000, 0x800001, 0xffffff, 1));
1097   // unsigned integer, 0 - 4294967294, little endian
1098   add(new NumberDataType("ULG", 32, 0, 0xffffffff, 0, 0xfffffffe, 1));
1099   // unsigned integer, 0 - 4294967294, big endian
1100   add(new NumberDataType("ULR", 32, REV, 0xffffffff, 0, 0xfffffffe, 1));
1101   // signed integer, -2147483647 - +2147483647, little endian
1102   add(new NumberDataType("SLG", 32, SIG, 0x80000000, 0x80000001, 0xffffffff, 1));
1103   // signed integer, -2147483647 - +2147483647, big endian
1104   add(new NumberDataType("SLR", 32, SIG|REV, 0x80000000, 0x80000001, 0xffffffff, 1));
1105   add(new NumberDataType("BI0", 7, ADJ|REQ, 0, 0, 1));  // bit 0 (up to 7 bits until bit 6)
1106   add(new NumberDataType("BI1", 7, ADJ|REQ, 0, 1, 1));  // bit 1 (up to 7 bits until bit 7)
1107   add(new NumberDataType("BI2", 6, ADJ|REQ, 0, 2, 1));  // bit 2 (up to 6 bits until bit 7)
1108   add(new NumberDataType("BI3", 5, ADJ|REQ, 0, 3, 1));  // bit 3 (up to 5 bits until bit 7)
1109   add(new NumberDataType("BI4", 4, ADJ|REQ, 0, 4, 1));  // bit 4 (up to 4 bits until bit 7)
1110   add(new NumberDataType("BI5", 3, ADJ|REQ, 0, 5, 1));  // bit 5 (up to 3 bits until bit 7)
1111   add(new NumberDataType("BI6", 2, ADJ|REQ, 0, 6, 1));  // bit 6 (up to 2 bits until bit 7)
1112   add(new NumberDataType("BI7", 1, REQ, 0, 7, 1));  // bit 7
1113 }
1114 
getInstance()1115 DataTypeList* DataTypeList::getInstance() {
1116   return &s_instance;
1117 }
1118 
dump(OutputFormat outputFormat,bool appendDivisor,ostream * output) const1119 void DataTypeList::dump(OutputFormat outputFormat, bool appendDivisor, ostream* output) const {
1120   bool json = outputFormat & OF_JSON;
1121   string sep = "\n";
1122   for (const auto &it: m_typesById) {
1123     const DataType *dataType = it.second;
1124     if (dataType->hasFlag(DUP)) {
1125       continue;
1126     }
1127     if (json) {
1128       *output << sep << "  {";
1129     }
1130     if ((dataType->getBitCount() % 8) != 0) {
1131       dataType->dump(outputFormat, dataType->getBitCount(), appendDivisor, output);
1132     } else {
1133       dataType->dump(outputFormat, dataType->getBitCount() / 8, appendDivisor, output);
1134     }
1135     if (json) {
1136       *output << "}";
1137       sep = ",\n";
1138     } else {
1139       *output << "\n";
1140     }
1141   }
1142 }
1143 
clear()1144 void DataTypeList::clear() {
1145   for (auto& it : m_cleanupTypes) {
1146     delete it;
1147   }
1148   m_cleanupTypes.clear();
1149   m_typesById.clear();
1150 }
1151 
add(const DataType * dataType)1152 result_t DataTypeList::add(const DataType* dataType) {
1153   if (m_typesById.find(dataType->getId()) != m_typesById.end()) {
1154     return RESULT_ERR_DUPLICATE_NAME;  // duplicate key
1155   }
1156   m_typesById[dataType->getId()] = dataType;
1157   m_cleanupTypes.push_back(dataType);
1158   return RESULT_OK;
1159 }
1160 
get(const string & id,size_t length) const1161 const DataType* DataTypeList::get(const string& id, size_t length) const {
1162   if (length > 0) {
1163     ostringstream str;
1164     str << id << LENGTH_SEPARATOR << static_cast<unsigned>(length);
1165     auto it = m_typesById.find(str.str());
1166     if (it != m_typesById.end()) {
1167       return it->second;
1168     }
1169   }
1170   auto it = m_typesById.find(id);
1171   if (it == m_typesById.end()) {
1172     return nullptr;
1173   }
1174   if (length > 0 && !it->second->isAdjustableLength()) {
1175     return nullptr;
1176   }
1177   return it->second;
1178 }
1179 
1180 }  // namespace ebusd
1181