fields.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
  2. * See the LICENSE file for information about the license.
  3. *
  4. * Protocol fields implementation. */
  5. #include "app.h"
  6. /* Create a new field of the specified type. Without populating its
  7. * type-specific value. */
  8. static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) {
  9. ProtoViewField *f = malloc(sizeof(*f));
  10. f->type = type;
  11. f->name = strdup(name);
  12. return f;
  13. }
  14. /* Free a field an associated data. */
  15. static void field_free(ProtoViewField *f) {
  16. free(f->name);
  17. switch(f->type) {
  18. case FieldTypeStr: free(f->str); break;
  19. case FieldTypeBytes: free(f->bytes); break;
  20. default: break; // Nothing to free for other types.
  21. }
  22. free(f);
  23. }
  24. /* Return the type of the field as string. */
  25. const char *field_get_type_name(ProtoViewField *f) {
  26. switch(f->type) {
  27. case FieldTypeStr: return "str";
  28. case FieldTypeSignedInt: return "int";
  29. case FieldTypeUnsignedInt: return "uint";
  30. case FieldTypeBinary: return "bin";
  31. case FieldTypeHex: return "hex";
  32. case FieldTypeBytes: return "bytes";
  33. case FieldTypeFloat: return "float";
  34. }
  35. return "unknown";
  36. }
  37. /* Set a string representation of the specified field in buf. */
  38. int field_to_string(char *buf, size_t len, ProtoViewField *f) {
  39. switch(f->type) {
  40. case FieldTypeStr:
  41. return snprintf(buf,len,"%s", f->str);
  42. case FieldTypeSignedInt:
  43. return snprintf(buf,len,"%lld", (long long) f->value);
  44. case FieldTypeUnsignedInt:
  45. return snprintf(buf,len,"%llu", (unsigned long long) f->uvalue);
  46. case FieldTypeBinary:
  47. {
  48. uint64_t test_bit = (1 << (f->len-1));
  49. uint64_t idx = 0;
  50. while(idx < len-1 && test_bit) {
  51. buf[idx++] = (f->uvalue & test_bit) ? '1' : '0';
  52. test_bit >>= 1;
  53. }
  54. buf[idx] = 0;
  55. return idx;
  56. }
  57. case FieldTypeHex:
  58. return snprintf(buf, len, "%*llX", (int)(f->len+7)/8, f->uvalue);
  59. case FieldTypeFloat:
  60. return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue);
  61. case FieldTypeBytes:
  62. {
  63. uint64_t idx = 0;
  64. while(idx < len-1 && idx < f->len) {
  65. const char *charset = "0123456789ABCDEF";
  66. uint32_t nibble = idx & 1 ?
  67. (f->bytes[idx/2] & 0xf) :
  68. (f->bytes[idx/2] >> 4);
  69. buf[idx++] = charset[nibble];
  70. }
  71. buf[idx] = 0;
  72. return idx;
  73. }
  74. }
  75. return 0;
  76. }
  77. /* Set the field value from its string representation in 'buf'.
  78. * The field type must already be set and the field should be valid.
  79. * The string represenation 'buf' must be null termianted. Note that
  80. * even when representing binary values containing zero, this values
  81. * are taken as representations, so that would be the string "00" as
  82. * the Bytes type representation.
  83. *
  84. * The function returns true if the filed was successfully set to the
  85. * new value, otherwise if the specified value is invalid for the
  86. * field type, false is returned. */
  87. bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) {
  88. // Initialize values to zero since the Flipper sscanf() implementation
  89. // is fuzzy... may populate only part of the value.
  90. long long val = 0;
  91. unsigned long long uval = 0;
  92. float fval = 0;
  93. switch(f->type) {
  94. case FieldTypeStr:
  95. free(f->str);
  96. f->len = len;
  97. f->str = malloc(len+1);
  98. memcpy(f->str,buf,len+1);
  99. break;
  100. case FieldTypeSignedInt:
  101. if (!sscanf(buf,"%lld",&val)) return false;
  102. f->value = val;
  103. break;
  104. case FieldTypeUnsignedInt:
  105. if (!sscanf(buf,"%llu",&uval)) return false;
  106. f->uvalue = uval;
  107. break;
  108. case FieldTypeBinary:
  109. {
  110. uint64_t bit_to_set = (1 << (len-1));
  111. uint64_t idx = 0;
  112. uval = 0;
  113. while(buf[idx]) {
  114. if (buf[idx] == '1') uval |= bit_to_set;
  115. else if (buf[idx] != '0') return false;
  116. bit_to_set >>= 1;
  117. idx++;
  118. }
  119. f->uvalue = uval;
  120. }
  121. break;
  122. case FieldTypeHex:
  123. if (!sscanf(buf,"%llx",&uval) &&
  124. !sscanf(buf,"%llX",&uval)) return false;
  125. f->uvalue = uval;
  126. break;
  127. case FieldTypeFloat:
  128. if (!sscanf(buf,"%f",&fval)) return false;
  129. f->fvalue = fval;
  130. break;
  131. case FieldTypeBytes:
  132. {
  133. if (len > f->len) return false;
  134. uint64_t idx = 0;
  135. while(buf[idx]) {
  136. uint8_t nibble = 0;
  137. char c = toupper(buf[idx]);
  138. if (c >= '0' && c <= '9') nibble = c-'0';
  139. else if (c >= 'A' && c <= 'F') nibble = 10+(c-'A');
  140. else return false;
  141. if (idx & 1) {
  142. f->bytes[idx/2] =
  143. (f->bytes[idx/2] & 0xF0) | nibble;
  144. } else {
  145. f->bytes[idx/2] =
  146. (f->bytes[idx/2] & 0x0F) | (nibble<<4);
  147. }
  148. idx++;
  149. }
  150. buf[idx] = 0;
  151. }
  152. break;
  153. }
  154. return true;
  155. }
  156. /* Increment the specified field value of 'incr'. If the field type
  157. * does not support increments false is returned, otherwise the
  158. * action is performed. */
  159. bool field_incr_value(ProtoViewField *f, int incr) {
  160. switch(f->type) {
  161. case FieldTypeStr: return false;
  162. case FieldTypeSignedInt: {
  163. /* Wrap around depending on the number of bits (f->len)
  164. * the integer was declared to have. */
  165. int64_t max = (1ULL << (f->len-1))-1;
  166. int64_t min = -max-1;
  167. int64_t v = (int64_t)f->value + incr;
  168. if (v > max) v = min+(v-max-1);
  169. if (v < min) v = max+(v-min+1);
  170. f->value = v;
  171. break;
  172. }
  173. case FieldTypeBinary:
  174. case FieldTypeHex:
  175. case FieldTypeUnsignedInt: {
  176. /* Wrap around like for the unsigned case, but here
  177. * is simpler. */
  178. uint64_t max = (1ULL << f->len)-1; // Broken for 64 bits.
  179. uint64_t uv = (uint64_t)f->value + incr;
  180. if (uv > max) uv = uv & max;
  181. f->uvalue = uv;
  182. break;
  183. }
  184. case FieldTypeFloat:
  185. f->fvalue += incr;
  186. break;
  187. case FieldTypeBytes: {
  188. // For bytes we only support single unit increments.
  189. if (incr != -1 && incr != 1) return false;
  190. for (int j = f->len-1; j >= 0; j--) {
  191. uint8_t nibble = (j&1) ? (f->bytes[j/2] & 0x0F) :
  192. ((f->bytes[j/2] & 0xF0) >> 4);
  193. nibble += incr;
  194. nibble &= 0x0F;
  195. f->bytes[j/2] = (j&1) ? ((f->bytes[j/2] & 0xF0) | nibble) :
  196. ((f->bytes[j/2] & 0x0F) | (nibble<<4));
  197. /* Propagate the operation on overflow of this nibble. */
  198. if ((incr == 1 && nibble == 0) ||
  199. (incr == -1 && nibble == 0xf))
  200. {
  201. continue;
  202. }
  203. break; // Otherwise stop the loop here.
  204. }
  205. break;
  206. }
  207. }
  208. return true;
  209. }
  210. /* Free a field set and its contained fields. */
  211. void fieldset_free(ProtoViewFieldSet *fs) {
  212. for (uint32_t j = 0; j < fs->numfields; j++)
  213. field_free(fs->fields[j]);
  214. free(fs->fields);
  215. free(fs);
  216. }
  217. /* Allocate and init an empty field set. */
  218. ProtoViewFieldSet *fieldset_new(void) {
  219. ProtoViewFieldSet *fs = malloc(sizeof(*fs));
  220. fs->numfields = 0;
  221. fs->fields = NULL;
  222. return fs;
  223. }
  224. /* Append an already allocated field at the end of the specified field set. */
  225. static void fieldset_add_field(ProtoViewFieldSet *fs, ProtoViewField *field) {
  226. fs->numfields++;
  227. fs->fields = realloc(fs->fields,sizeof(ProtoViewField*)*fs->numfields);
  228. fs->fields[fs->numfields-1] = field;
  229. }
  230. /* Allocate and append an integer field. */
  231. void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits) {
  232. ProtoViewField *f = field_new(FieldTypeSignedInt,name);
  233. f->value = val;
  234. f->len = bits;
  235. fieldset_add_field(fs,f);
  236. }
  237. /* Allocate and append an unsigned field. */
  238. void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) {
  239. ProtoViewField *f = field_new(FieldTypeUnsignedInt,name);
  240. f->uvalue = uval;
  241. f->len = bits;
  242. fieldset_add_field(fs,f);
  243. }
  244. /* Allocate and append a hex field. This is an unsigned number but
  245. * with an hex representation. */
  246. void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) {
  247. ProtoViewField *f = field_new(FieldTypeHex,name);
  248. f->uvalue = uval;
  249. f->len = bits;
  250. fieldset_add_field(fs,f);
  251. }
  252. /* Allocate and append a bin field. This is an unsigned number but
  253. * with a binary representation. */
  254. void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) {
  255. ProtoViewField *f = field_new(FieldTypeBinary,name);
  256. f->uvalue = uval;
  257. f->len = bits;
  258. fieldset_add_field(fs,f);
  259. }
  260. /* Allocate and append a string field. */
  261. void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s) {
  262. ProtoViewField *f = field_new(FieldTypeStr,name);
  263. f->str = strdup(s);
  264. f->len = strlen(s);
  265. fieldset_add_field(fs,f);
  266. }
  267. /* Allocate and append a bytes field. Note that 'count' is specified in
  268. * nibbles (bytes*2). */
  269. void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count_nibbles) {
  270. uint32_t numbytes = (count_nibbles+count_nibbles%2)/2;
  271. ProtoViewField *f = field_new(FieldTypeBytes,name);
  272. f->bytes = malloc(numbytes);
  273. memcpy(f->bytes,bytes,numbytes);
  274. f->len = count_nibbles;
  275. fieldset_add_field(fs,f);
  276. }
  277. /* Allocate and append a float field. */
  278. void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot) {
  279. ProtoViewField *f = field_new(FieldTypeFloat,name);
  280. f->fvalue = val;
  281. f->len = digits_after_dot;
  282. fieldset_add_field(fs,f);
  283. }