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