fields.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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 only the auxiliary data of a field, used to represent the
  15. * current type. Name and type are not touched. */
  16. static void field_free_aux_data(ProtoViewField* f) {
  17. switch(f->type) {
  18. case FieldTypeStr:
  19. free(f->str);
  20. break;
  21. case FieldTypeBytes:
  22. free(f->bytes);
  23. break;
  24. default:
  25. break; // Nothing to free for other types.
  26. }
  27. }
  28. /* Free a field an associated data. */
  29. static void field_free(ProtoViewField* f) {
  30. field_free_aux_data(f);
  31. free(f->name);
  32. free(f);
  33. }
  34. /* Return the type of the field as string. */
  35. const char* field_get_type_name(ProtoViewField* f) {
  36. switch(f->type) {
  37. case FieldTypeStr:
  38. return "str";
  39. case FieldTypeSignedInt:
  40. return "int";
  41. case FieldTypeUnsignedInt:
  42. return "uint";
  43. case FieldTypeBinary:
  44. return "bin";
  45. case FieldTypeHex:
  46. return "hex";
  47. case FieldTypeBytes:
  48. return "bytes";
  49. case FieldTypeFloat:
  50. return "float";
  51. }
  52. return "unknown";
  53. }
  54. /* Set a string representation of the specified field in buf. */
  55. int field_to_string(char* buf, size_t len, ProtoViewField* f) {
  56. switch(f->type) {
  57. case FieldTypeStr:
  58. return snprintf(buf, len, "%s", f->str);
  59. case FieldTypeSignedInt:
  60. return snprintf(buf, len, "%lld", (long long)f->value);
  61. case FieldTypeUnsignedInt:
  62. return snprintf(buf, len, "%llu", (unsigned long long)f->uvalue);
  63. case FieldTypeBinary: {
  64. uint64_t test_bit = (1 << (f->len - 1));
  65. uint64_t idx = 0;
  66. while(idx < len - 1 && test_bit) {
  67. buf[idx++] = (f->uvalue & test_bit) ? '1' : '0';
  68. test_bit >>= 1;
  69. }
  70. buf[idx] = 0;
  71. return idx;
  72. }
  73. case FieldTypeHex:
  74. return snprintf(buf, len, "%*llX", (int)(f->len + 7) / 8, f->uvalue);
  75. case FieldTypeFloat:
  76. return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue);
  77. case FieldTypeBytes: {
  78. uint64_t idx = 0;
  79. while(idx < len - 1 && idx < f->len) {
  80. const char* charset = "0123456789ABCDEF";
  81. uint32_t nibble = idx & 1 ? (f->bytes[idx / 2] & 0xf) : (f->bytes[idx / 2] >> 4);
  82. buf[idx++] = charset[nibble];
  83. }
  84. buf[idx] = 0;
  85. return idx;
  86. }
  87. }
  88. return 0;
  89. }
  90. /* Set the field value from its string representation in 'buf'.
  91. * The field type must already be set and the field should be valid.
  92. * The string represenation 'buf' must be null termianted. Note that
  93. * even when representing binary values containing zero, this values
  94. * are taken as representations, so that would be the string "00" as
  95. * the Bytes type representation.
  96. *
  97. * The function returns true if the filed was successfully set to the
  98. * new value, otherwise if the specified value is invalid for the
  99. * field type, false is returned. */
  100. bool field_set_from_string(ProtoViewField* f, char* buf, size_t len) {
  101. // Initialize values to zero since the Flipper sscanf() implementation
  102. // is fuzzy... may populate only part of the value.
  103. long long val = 0;
  104. unsigned long long uval = 0;
  105. float fval = 0;
  106. switch(f->type) {
  107. case FieldTypeStr:
  108. free(f->str);
  109. f->len = len;
  110. f->str = malloc(len + 1);
  111. memcpy(f->str, buf, len + 1);
  112. break;
  113. case FieldTypeSignedInt:
  114. if(!sscanf(buf, "%lld", &val)) return false;
  115. f->value = val;
  116. break;
  117. case FieldTypeUnsignedInt:
  118. if(!sscanf(buf, "%llu", &uval)) return false;
  119. f->uvalue = uval;
  120. break;
  121. case FieldTypeBinary: {
  122. uint64_t bit_to_set = (1 << (len - 1));
  123. uint64_t idx = 0;
  124. uval = 0;
  125. while(buf[idx]) {
  126. if(buf[idx] == '1')
  127. uval |= bit_to_set;
  128. else if(buf[idx] != '0')
  129. return false;
  130. bit_to_set >>= 1;
  131. idx++;
  132. }
  133. f->uvalue = uval;
  134. } break;
  135. case FieldTypeHex:
  136. if(!sscanf(buf, "%llx", &uval) && !sscanf(buf, "%llX", &uval)) return false;
  137. f->uvalue = uval;
  138. break;
  139. case FieldTypeFloat:
  140. if(!sscanf(buf, "%f", &fval)) return false;
  141. f->fvalue = fval;
  142. break;
  143. case FieldTypeBytes: {
  144. if(len > f->len) return false;
  145. uint64_t idx = 0;
  146. while(buf[idx]) {
  147. uint8_t nibble = 0;
  148. char c = toupper(buf[idx]);
  149. if(c >= '0' && c <= '9')
  150. nibble = c - '0';
  151. else if(c >= 'A' && c <= 'F')
  152. nibble = 10 + (c - 'A');
  153. else
  154. return false;
  155. if(idx & 1) {
  156. f->bytes[idx / 2] = (f->bytes[idx / 2] & 0xF0) | nibble;
  157. } else {
  158. f->bytes[idx / 2] = (f->bytes[idx / 2] & 0x0F) | (nibble << 4);
  159. }
  160. idx++;
  161. }
  162. buf[idx] = 0;
  163. } break;
  164. }
  165. return true;
  166. }
  167. /* Set the 'dst' field to contain a copy of the value of the 'src'
  168. * field. The field name is not modified. */
  169. void field_set_from_field(ProtoViewField* dst, ProtoViewField* src) {
  170. field_free_aux_data(dst);
  171. dst->type = src->type;
  172. dst->len = src->len;
  173. switch(src->type) {
  174. case FieldTypeStr:
  175. dst->str = strdup(src->str);
  176. break;
  177. case FieldTypeBytes:
  178. dst->bytes = malloc(src->len);
  179. memcpy(dst->bytes, src->bytes, dst->len);
  180. break;
  181. case FieldTypeSignedInt:
  182. dst->value = src->value;
  183. break;
  184. case FieldTypeUnsignedInt:
  185. case FieldTypeBinary:
  186. case FieldTypeHex:
  187. dst->uvalue = src->uvalue;
  188. break;
  189. case FieldTypeFloat:
  190. dst->fvalue = src->fvalue;
  191. break;
  192. }
  193. }
  194. /* Increment the specified field value of 'incr'. If the field type
  195. * does not support increments false is returned, otherwise the
  196. * action is performed. */
  197. bool field_incr_value(ProtoViewField* f, int incr) {
  198. switch(f->type) {
  199. case FieldTypeStr:
  200. return false;
  201. case FieldTypeSignedInt: {
  202. /* Wrap around depending on the number of bits (f->len)
  203. * the integer was declared to have. */
  204. int64_t max = (1ULL << (f->len - 1)) - 1;
  205. int64_t min = -max - 1;
  206. int64_t v = (int64_t)f->value + incr;
  207. if(v > max) v = min + (v - max - 1);
  208. if(v < min) v = max + (v - min + 1);
  209. f->value = v;
  210. break;
  211. }
  212. case FieldTypeBinary:
  213. case FieldTypeHex:
  214. case FieldTypeUnsignedInt: {
  215. /* Wrap around like for the unsigned case, but here
  216. * is simpler. */
  217. uint64_t max = (1ULL << f->len) - 1; // Broken for 64 bits.
  218. uint64_t uv = (uint64_t)f->value + incr;
  219. if(uv > max) uv = uv & max;
  220. f->uvalue = uv;
  221. break;
  222. }
  223. case FieldTypeFloat:
  224. f->fvalue += incr;
  225. break;
  226. case FieldTypeBytes: {
  227. // For bytes we only support single unit increments.
  228. if(incr != -1 && incr != 1) return false;
  229. for(int j = f->len - 1; j >= 0; j--) {
  230. uint8_t nibble = (j & 1) ? (f->bytes[j / 2] & 0x0F) : ((f->bytes[j / 2] & 0xF0) >> 4);
  231. nibble += incr;
  232. nibble &= 0x0F;
  233. f->bytes[j / 2] = (j & 1) ? ((f->bytes[j / 2] & 0xF0) | nibble) :
  234. ((f->bytes[j / 2] & 0x0F) | (nibble << 4));
  235. /* Propagate the operation on overflow of this nibble. */
  236. if((incr == 1 && nibble == 0) || (incr == -1 && nibble == 0xf)) {
  237. continue;
  238. }
  239. break; // Otherwise stop the loop here.
  240. }
  241. break;
  242. }
  243. }
  244. return true;
  245. }
  246. /* Free a field set and its contained fields. */
  247. void fieldset_free(ProtoViewFieldSet* fs) {
  248. for(uint32_t j = 0; j < fs->numfields; j++)
  249. field_free(fs->fields[j]);
  250. free(fs->fields);
  251. free(fs);
  252. }
  253. /* Allocate and init an empty field set. */
  254. ProtoViewFieldSet* fieldset_new(void) {
  255. ProtoViewFieldSet* fs = malloc(sizeof(*fs));
  256. fs->numfields = 0;
  257. fs->fields = NULL;
  258. return fs;
  259. }
  260. /* Append an already allocated field at the end of the specified field set. */
  261. static void fieldset_add_field(ProtoViewFieldSet* fs, ProtoViewField* field) {
  262. fs->numfields++;
  263. fs->fields = realloc(fs->fields, sizeof(ProtoViewField*) * fs->numfields);
  264. fs->fields[fs->numfields - 1] = field;
  265. }
  266. /* Allocate and append an integer field. */
  267. void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits) {
  268. ProtoViewField* f = field_new(FieldTypeSignedInt, name);
  269. f->value = val;
  270. f->len = bits;
  271. fieldset_add_field(fs, f);
  272. }
  273. /* Allocate and append an unsigned field. */
  274. void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) {
  275. ProtoViewField* f = field_new(FieldTypeUnsignedInt, name);
  276. f->uvalue = uval;
  277. f->len = bits;
  278. fieldset_add_field(fs, f);
  279. }
  280. /* Allocate and append a hex field. This is an unsigned number but
  281. * with an hex representation. */
  282. void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) {
  283. ProtoViewField* f = field_new(FieldTypeHex, name);
  284. f->uvalue = uval;
  285. f->len = bits;
  286. fieldset_add_field(fs, f);
  287. }
  288. /* Allocate and append a bin field. This is an unsigned number but
  289. * with a binary representation. */
  290. void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) {
  291. ProtoViewField* f = field_new(FieldTypeBinary, name);
  292. f->uvalue = uval;
  293. f->len = bits;
  294. fieldset_add_field(fs, f);
  295. }
  296. /* Allocate and append a string field. The string 's' does not need to point
  297. * to a null terminated string, but must have at least 'len' valid bytes
  298. * starting from the pointer. The field object will be correctly null
  299. * terminated. */
  300. void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s, size_t len) {
  301. ProtoViewField* f = field_new(FieldTypeStr, name);
  302. f->len = len;
  303. f->str = malloc(len + 1);
  304. memcpy(f->str, s, len);
  305. f->str[len] = 0;
  306. fieldset_add_field(fs, f);
  307. }
  308. /* Allocate and append a bytes field. Note that 'count' is specified in
  309. * nibbles (bytes*2). */
  310. void fieldset_add_bytes(
  311. ProtoViewFieldSet* fs,
  312. const char* name,
  313. const uint8_t* bytes,
  314. uint32_t count_nibbles) {
  315. uint32_t numbytes = (count_nibbles + count_nibbles % 2) / 2;
  316. ProtoViewField* f = field_new(FieldTypeBytes, name);
  317. f->bytes = malloc(numbytes);
  318. memcpy(f->bytes, bytes, numbytes);
  319. f->len = count_nibbles;
  320. fieldset_add_field(fs, f);
  321. }
  322. /* Allocate and append a float field. */
  323. void fieldset_add_float(
  324. ProtoViewFieldSet* fs,
  325. const char* name,
  326. float val,
  327. uint32_t digits_after_dot) {
  328. ProtoViewField* f = field_new(FieldTypeFloat, name);
  329. f->fvalue = val;
  330. f->len = digits_after_dot;
  331. fieldset_add_field(fs, f);
  332. }
  333. /* For each field of the destination filedset 'dst', look for a matching
  334. * field name/type in the source fieldset 'src', and if one is found copy
  335. * its value into the 'dst' field. */
  336. void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src) {
  337. for(uint32_t j = 0; j < dst->numfields; j++) {
  338. for(uint32_t i = 0; i < src->numfields; i++) {
  339. if(dst->fields[j]->type == src->fields[i]->type &&
  340. !strcmp(dst->fields[j]->name, src->fields[i]->name)) {
  341. field_set_from_field(dst->fields[j], src->fields[i]);
  342. }
  343. }
  344. }
  345. }