1 /*
2  * Copyright (c) 2019 Foundries.io
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <ctype.h>
11 #include <inttypes.h>
12 #include "lwm2m_object.h"
13 #include "lwm2m_util.h"
14 
15 #define SHIFT_LEFT(v, o, m) (((v) << (o)) & (m))
16 #define SHIFT_RIGHT(v, o, m) (((v) >> (o)) & (m))
17 
18 #define PRECISION64_LEN 17U
19 #define PRECISION64 100000000000000000ULL
20 
21 #define PRECISION32 1000000000UL
22 
23 /* convert from float to binary32 */
lwm2m_float_to_b32(double * in,uint8_t * b32,size_t len)24 int lwm2m_float_to_b32(double *in, uint8_t *b32, size_t len)
25 {
26 	int32_t e = -1, v, f = 0;
27 	int32_t val1 = (int32_t)*in;
28 	int32_t val2 = (*in - (int32_t)*in) * PRECISION32;
29 	int i;
30 
31 	if (len != 4) {
32 		return -EINVAL;
33 	}
34 
35 	/* handle zero value special case */
36 	if (val1 == 0 && val2 == 0) {
37 		memset(b32, 0, len);
38 		return 0;
39 	}
40 
41 	/* sign handled later */
42 	v = abs(val1);
43 
44 	/* add whole value to fraction */
45 	while (v > 0) {
46 		f >>= 1;
47 
48 		if (v & 1) {
49 			f |= (1 << 23);
50 		}
51 
52 		v >>= 1;
53 		e++;
54 	}
55 
56 	/* sign handled later */
57 	v = abs(val2);
58 
59 	/* add decimal to fraction */
60 	i = e;
61 	while (v > 0 && i < 23) {
62 		v *= 2;
63 		if (!f && e < 0 && v < PRECISION32) {
64 			/* handle -e */
65 			e--;
66 			continue;
67 		} else if (v >= PRECISION32) {
68 			v -= PRECISION32;
69 			f |= 1 << (22 - i);
70 		}
71 
72 		if (v == 0) {
73 			break;
74 		}
75 
76 		i++;
77 	}
78 
79 	/* adjust exponent for bias */
80 	e += 127;
81 
82 	memset(b32, 0, len);
83 
84 	/* sign: bit 31 */
85 	if (val1 == 0) {
86 		b32[0] = val2 < 0 ? 0x80 : 0;
87 	} else {
88 		b32[0] = val1 < 0 ? 0x80 : 0;
89 	}
90 
91 	/* exponent: bits 30-23 */
92 	b32[0] |= e >> 1;
93 	b32[1] = (e & 1) << 7;
94 
95 	/* fraction: bits 22-0 */
96 	/* NOTE: ignore the "hidden" bit 23 in fraction */
97 	b32[1] |= (f >> 16) & 0x7F;
98 	b32[2] = (f >> 8) & 0xFF;
99 	b32[3] = f & 0xFF;
100 
101 	return 0;
102 }
103 
104 /* convert from float to binary64 */
lwm2m_float_to_b64(double * in,uint8_t * b64,size_t len)105 int lwm2m_float_to_b64(double *in, uint8_t *b64, size_t len)
106 {
107 	int64_t v, f = 0;
108 	int32_t e = -1;
109 	int64_t val1 = (int64_t)*in;
110 	int64_t val2 = (*in - (int64_t)*in) * PRECISION64;
111 	int i;
112 
113 	if (len != 8) {
114 		return -EINVAL;
115 	}
116 
117 	/* handle zero value special case */
118 	if (val1 == 0 && val2 == 0) {
119 		memset(b64, 0, len);
120 		return 0;
121 	}
122 
123 	/* sign handled later */
124 	v = llabs(val1);
125 
126 	/* add whole value to fraction */
127 	while (v > 0) {
128 		f >>= 1;
129 
130 		if (v & 1) {
131 			f |= ((int64_t)1 << 52);
132 		}
133 
134 		v >>= 1;
135 		e++;
136 	}
137 
138 	/* sign handled later */
139 	v = llabs(val2);
140 
141 	/* add decimal to fraction */
142 	i = e;
143 	while (v > 0 && i < 52) {
144 		v *= 2;
145 		if (!f && e < 0 && v < PRECISION64) {
146 			/* handle -e */
147 			e--;
148 			continue;
149 		} else if (v >= PRECISION64) {
150 			v -= PRECISION64;
151 			f |= (int64_t)1 << (51 - i);
152 		}
153 
154 		if (v == 0) {
155 			break;
156 		}
157 
158 		i++;
159 	}
160 
161 	/* adjust exponent for bias */
162 	e += 1023;
163 
164 	memset(b64, 0, len);
165 
166 	/* sign: bit 63 */
167 	if (val1 == 0) {
168 		b64[0] = val2 < 0 ? 0x80 : 0;
169 	} else {
170 		b64[0] = val1 < 0 ? 0x80 : 0;
171 	}
172 
173 	/* exponent: bits 62-52 */
174 	b64[0] |= (e >> 4);
175 	b64[1] = ((e & 0xF) << 4);
176 
177 	/* fraction: bits 51-0 */
178 	/* NOTE: ignore the "hidden" bit 52 in fraction */
179 	b64[1] |= ((f >> 48) & 0xF);
180 	b64[2] = (f >> 40) & 0xFF;
181 	b64[3] = (f >> 32) & 0xFF;
182 	b64[4] = (f >> 24) & 0xFF;
183 	b64[5] = (f >> 16) & 0xFF;
184 	b64[6] = (f >> 8) & 0xFF;
185 	b64[7] = f & 0xFF;
186 
187 	return 0;
188 }
189 
190 /* convert from binary32 to float */
lwm2m_b32_to_float(uint8_t * b32,size_t len,double * out)191 int lwm2m_b32_to_float(uint8_t *b32, size_t len, double *out)
192 {
193 	int32_t f, k, i, e;
194 	bool sign = false;
195 	int32_t val1, val2;
196 
197 	if (len != 4) {
198 		return -EINVAL;
199 	}
200 
201 	val1 = 0;
202 	val2 = 0;
203 
204 	/* calc sign: bit 31 */
205 	sign = SHIFT_RIGHT(b32[0], 7, 0x1);
206 
207 	/* calc exponent: bits 30-23 */
208 	e  = SHIFT_LEFT(b32[0], 1, 0xFF);
209 	e += SHIFT_RIGHT(b32[1], 7, 0x1);
210 	/* remove bias */
211 	e -= 127;
212 
213 	/* enable "hidden" fraction bit 24 which is always 1 */
214 	f  = ((int32_t)1 << 23);
215 	/* calc fraction: bits 22-0 */
216 	f += ((int32_t)(b32[1] & 0x7F) << 16);
217 	f += ((int32_t)b32[2] << 8);
218 	f += b32[3];
219 
220 	/* handle whole number */
221 	if (e > -1) {
222 		/* precision overflow */
223 		if (e > 23) {
224 			e = 23;
225 		}
226 
227 		val1 = (f >> (23 - e)) * (sign ? -1 : 1);
228 	}
229 
230 	/* calculate the rest of the decimal */
231 	k = PRECISION32;
232 
233 	/* account for -e */
234 	while (e < -1) {
235 		k /= 2;
236 		e++;
237 	}
238 
239 	for (i = 22 - e; i >= 0; i--) {
240 		k /= 2;
241 		if (f & (1 << i)) {
242 			val2 += k;
243 
244 		}
245 	}
246 
247 	if (sign) {
248 		*out = (double)val1 - (double)val2 / PRECISION32;
249 	} else {
250 		*out = (double)val1 + (double)val2 / PRECISION32;
251 	}
252 
253 	return 0;
254 }
255 
256 /* convert from binary64 to float */
lwm2m_b64_to_float(uint8_t * b64,size_t len,double * out)257 int lwm2m_b64_to_float(uint8_t *b64, size_t len, double *out)
258 {
259 	int64_t f, k;
260 	int i, e;
261 	bool sign = false;
262 	int64_t val1, val2;
263 
264 	if (len != 8) {
265 		return -EINVAL;
266 	}
267 
268 	val1 = 0LL;
269 	val2 = 0LL;
270 
271 	/* calc sign: bit 63 */
272 	sign = SHIFT_RIGHT(b64[0], 7, 0x1);
273 
274 	/* get exponent: bits 62-52 */
275 	e  = SHIFT_LEFT((uint16_t)b64[0], 4, 0x7F0);
276 	e += SHIFT_RIGHT(b64[1], 4, 0xF);
277 	/* remove bias */
278 	e -= 1023;
279 
280 	/* enable "hidden" fraction bit 53 which is always 1 */
281 	f  = ((int64_t)1 << 52);
282 	/* get fraction: bits 51-0 */
283 	f += ((int64_t)(b64[1] & 0xF) << 48);
284 	f += ((int64_t)b64[2] << 40);
285 	f += ((int64_t)b64[3] << 32);
286 	f += ((int64_t)b64[4] << 24);
287 	f += ((int64_t)b64[5] << 16);
288 	f += ((int64_t)b64[6] << 8);
289 	f += b64[7];
290 
291 	/* handle whole number */
292 	if (e > -1) {
293 		/* precision overflow */
294 		if (e > 52) {
295 			e = 52;
296 		}
297 
298 		val1 = (f >> (52 - e)) * (sign ? -1 : 1);
299 	}
300 
301 	/* calculate the rest of the decimal */
302 	k = PRECISION64;
303 
304 	/* account for -e */
305 	while (e < -1) {
306 		k /= 2;
307 		e++;
308 	}
309 
310 	for (i = 51 - e; i >= 0; i--) {
311 		k /= 2;
312 		if (f & ((int64_t)1 << i)) {
313 			val2 += k;
314 
315 		}
316 	}
317 
318 	if (sign) {
319 		*out = (double)val1 - (double)val2 / PRECISION64;
320 	} else {
321 		*out = (double)val1 + (double)val2 / PRECISION64;
322 	}
323 
324 	return 0;
325 }
326 
lwm2m_atof(const char * input,double * out)327 int lwm2m_atof(const char *input, double *out)
328 {
329 	char *pos, *end, buf[24];
330 	long val;
331 	int64_t base = PRECISION64, sign = 1;
332 	int64_t val1, val2;
333 
334 	if (!input || !out) {
335 		return -EINVAL;
336 	}
337 
338 	strncpy(buf, input, sizeof(buf) - 1);
339 	buf[sizeof(buf) - 1] = '\0';
340 
341 	if (strchr(buf, '-')) {
342 		sign = -1;
343 	}
344 
345 	pos = strchr(buf, '.');
346 	if (pos) {
347 		*pos = '\0';
348 	}
349 
350 	errno = 0;
351 	val = strtol(buf, &end, 10);
352 	if (errno || *end) {
353 		return -EINVAL;
354 	}
355 
356 	val1 = (int64_t)val;
357 	val2 = 0;
358 
359 	if (!pos) {
360 		*out = (double)val1;
361 		return 0;
362 	}
363 
364 	while (*(++pos) && base > 1 && isdigit((unsigned char)*pos) != 0) {
365 		val2 = val2 * 10 + (*pos - '0');
366 		base /= 10;
367 	}
368 
369 	val2 *= sign * base;
370 
371 	*out = (double)val1 + (double)val2 / PRECISION64;
372 
373 	return !*pos || base == 1 ? 0 : -EINVAL;
374 }
375 
lwm2m_ftoa(double * input,char * out,size_t outlen,int8_t dec_limit)376 int lwm2m_ftoa(double *input, char *out, size_t outlen, int8_t dec_limit)
377 {
378 	size_t len;
379 	char buf[PRECISION64_LEN + 1];
380 	int64_t val1 = (int64_t)*input;
381 	int64_t val2 = (*input - (int64_t)*input) * PRECISION64;
382 
383 	len = snprintk(buf, sizeof(buf), "%0*lld", PRECISION64_LEN,
384 		       (long long)llabs(val2));
385 	if (len != PRECISION64_LEN) {
386 		strcpy(buf, "0");
387 	} else {
388 		/* Round the value to the specified decimal point. */
389 		if (dec_limit > 0 && dec_limit < sizeof(buf) &&
390 		    buf[dec_limit] != '\0') {
391 			bool round_up = buf[dec_limit] >= '5';
392 
393 			buf[dec_limit] = '\0';
394 			len = dec_limit;
395 
396 			while (round_up && dec_limit > 0) {
397 				dec_limit--;
398 				buf[dec_limit]++;
399 
400 				if (buf[dec_limit] > '9') {
401 					buf[dec_limit] = '0';
402 				} else {
403 					round_up = false;
404 				}
405 			}
406 
407 			if (round_up) {
408 				if (*input < 0) {
409 					val1--;
410 				} else {
411 					val1++;
412 				}
413 			}
414 		}
415 
416 		/* clear ending zeroes, but leave 1 if needed */
417 		while (len > 1U && buf[len - 1] == '0') {
418 			buf[--len] = '\0';
419 		}
420 	}
421 
422 	return snprintk(out, outlen, "%s%lld.%s",
423 			/* handle negative val2 when val1 is 0 */
424 			(val1 == 0 && val2 < 0) ? "-" : "", (long long)val1, buf);
425 }
426 
lwm2m_atou16(const uint8_t * buf,uint16_t buflen,uint16_t * len)427 uint16_t lwm2m_atou16(const uint8_t *buf, uint16_t buflen, uint16_t *len)
428 {
429 	uint16_t val = 0U;
430 	uint16_t pos = 0U;
431 
432 	/* we should get a value first - consume all numbers */
433 	while (pos < buflen && isdigit(buf[pos]) != 0) {
434 		val = val * 10U + (buf[pos] - '0');
435 		pos++;
436 	}
437 
438 	*len = pos;
439 	return val;
440 }
441 
lwm2m_string_to_path(const char * pathstr,struct lwm2m_obj_path * path,char delim)442 int lwm2m_string_to_path(const char *pathstr, struct lwm2m_obj_path *path,
443 			  char delim)
444 {
445 	uint16_t value, len;
446 	int i, tokstart = -1, toklen;
447 	int end_index = strlen(pathstr) - 1;
448 
449 	(void)memset(path, 0, sizeof(*path));
450 	for (i = 0; i <= end_index; i++) {
451 		/* search for first numeric */
452 		if (tokstart == -1) {
453 			if (isdigit((unsigned char)pathstr[i]) == 0) {
454 				continue;
455 			}
456 
457 			tokstart = i;
458 		}
459 
460 		/* find delimiter char or end of string */
461 		if (pathstr[i] == delim || i == end_index) {
462 			toklen = i - tokstart + 1;
463 
464 			/* don't process delimiter char */
465 			if (pathstr[i] == delim) {
466 				toklen--;
467 			}
468 
469 			if (toklen <= 0) {
470 				continue;
471 			}
472 
473 			value = lwm2m_atou16(&pathstr[tokstart], toklen, &len);
474 			/* increase the path level for each token found */
475 			path->level++;
476 			switch (path->level) {
477 			case LWM2M_PATH_LEVEL_OBJECT:
478 				path->obj_id = value;
479 				break;
480 
481 			case LWM2M_PATH_LEVEL_OBJECT_INST:
482 				path->obj_inst_id = value;
483 				break;
484 
485 			case LWM2M_PATH_LEVEL_RESOURCE:
486 				path->res_id = value;
487 				break;
488 
489 			case LWM2M_PATH_LEVEL_RESOURCE_INST:
490 				path->res_inst_id = value;
491 				break;
492 
493 			default:
494 				return -EINVAL;
495 
496 			}
497 
498 			tokstart = -1;
499 		}
500 	}
501 
502 	return 0;
503 }
504 
lwm2m_obj_path_equal(const struct lwm2m_obj_path * a,const struct lwm2m_obj_path * b)505 bool lwm2m_obj_path_equal(const struct lwm2m_obj_path *a, const struct lwm2m_obj_path *b)
506 {
507 	uint8_t level = a->level;
508 
509 	if (a->level != b->level) {
510 		return false;
511 	}
512 
513 	if (level >= LWM2M_PATH_LEVEL_OBJECT && (a->obj_id != b->obj_id)) {
514 		return false;
515 	}
516 
517 	if (level >= LWM2M_PATH_LEVEL_OBJECT_INST && (a->obj_inst_id != b->obj_inst_id)) {
518 		return false;
519 	}
520 
521 	if (level >= LWM2M_PATH_LEVEL_RESOURCE && (a->res_id != b->res_id)) {
522 		return false;
523 	}
524 
525 	if (level >= LWM2M_PATH_LEVEL_RESOURCE_INST && (a->res_inst_id != b->res_inst_id)) {
526 		return false;
527 	}
528 
529 	return true;
530 }
531 
532 /* for debugging: to print IP addresses */
lwm2m_sprint_ip_addr(const struct sockaddr * addr)533 char *lwm2m_sprint_ip_addr(const struct sockaddr *addr)
534 {
535 	static char buf[NET_IPV6_ADDR_LEN];
536 
537 	if (addr->sa_family == AF_INET6) {
538 		return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf, sizeof(buf));
539 	}
540 
541 	if (addr->sa_family == AF_INET) {
542 		return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf, sizeof(buf));
543 	}
544 
545 	return "::";
546 }
547 
to_hex_digit(uint8_t digit)548 static uint8_t to_hex_digit(uint8_t digit)
549 {
550 	if (digit >= 10U) {
551 		return digit - 10U + 'a';
552 	}
553 
554 	return digit + '0';
555 }
556 
sprint_token(const uint8_t * token,uint8_t tkl)557 char *sprint_token(const uint8_t *token, uint8_t tkl)
558 {
559 	static char buf[32];
560 	char *ptr = buf;
561 
562 	if (token && tkl != 0) {
563 		int i;
564 
565 		tkl = MIN(tkl, sizeof(buf) / 2 - 1);
566 
567 		for (i = 0; i < tkl; i++) {
568 			*ptr++ = to_hex_digit(token[i] >> 4);
569 			*ptr++ = to_hex_digit(token[i] & 0x0F);
570 		}
571 
572 		*ptr = '\0';
573 	} else {
574 		strcpy(buf, "[no-token]");
575 	}
576 
577 	return buf;
578 }
579