fields.c 12 KB

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