/* * Copyright (c) 2019 Foundries.io * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include "lwm2m_object.h" #include "lwm2m_util.h" #define SHIFT_LEFT(v, o, m) (((v) << (o)) & (m)) #define SHIFT_RIGHT(v, o, m) (((v) >> (o)) & (m)) #define PRECISION64_LEN 17U #define PRECISION64 100000000000000000ULL #define PRECISION32 1000000000UL /* convert from float to binary32 */ int lwm2m_float_to_b32(double *in, uint8_t *b32, size_t len) { int32_t e = -1, v, f = 0; int32_t val1 = (int32_t)*in; int32_t val2 = (*in - (int32_t)*in) * PRECISION32; int i; if (len != 4) { return -EINVAL; } /* handle zero value special case */ if (val1 == 0 && val2 == 0) { memset(b32, 0, len); return 0; } /* sign handled later */ v = abs(val1); /* add whole value to fraction */ while (v > 0) { f >>= 1; if (v & 1) { f |= (1 << 23); } v >>= 1; e++; } /* sign handled later */ v = abs(val2); /* add decimal to fraction */ i = e; while (v > 0 && i < 23) { v *= 2; if (!f && e < 0 && v < PRECISION32) { /* handle -e */ e--; continue; } else if (v >= PRECISION32) { v -= PRECISION32; f |= 1 << (22 - i); } if (v == 0) { break; } i++; } /* adjust exponent for bias */ e += 127; memset(b32, 0, len); /* sign: bit 31 */ if (val1 == 0) { b32[0] = val2 < 0 ? 0x80 : 0; } else { b32[0] = val1 < 0 ? 0x80 : 0; } /* exponent: bits 30-23 */ b32[0] |= e >> 1; b32[1] = (e & 1) << 7; /* fraction: bits 22-0 */ /* NOTE: ignore the "hidden" bit 23 in fraction */ b32[1] |= (f >> 16) & 0x7F; b32[2] = (f >> 8) & 0xFF; b32[3] = f & 0xFF; return 0; } /* convert from float to binary64 */ int lwm2m_float_to_b64(double *in, uint8_t *b64, size_t len) { int64_t v, f = 0; int32_t e = -1; int64_t val1 = (int64_t)*in; int64_t val2 = (*in - (int64_t)*in) * PRECISION64; int i; if (len != 8) { return -EINVAL; } /* handle zero value special case */ if (val1 == 0 && val2 == 0) { memset(b64, 0, len); return 0; } /* sign handled later */ v = llabs(val1); /* add whole value to fraction */ while (v > 0) { f >>= 1; if (v & 1) { f |= ((int64_t)1 << 52); } v >>= 1; e++; } /* sign handled later */ v = llabs(val2); /* add decimal to fraction */ i = e; while (v > 0 && i < 52) { v *= 2; if (!f && e < 0 && v < PRECISION64) { /* handle -e */ e--; continue; } else if (v >= PRECISION64) { v -= PRECISION64; f |= (int64_t)1 << (51 - i); } if (v == 0) { break; } i++; } /* adjust exponent for bias */ e += 1023; memset(b64, 0, len); /* sign: bit 63 */ if (val1 == 0) { b64[0] = val2 < 0 ? 0x80 : 0; } else { b64[0] = val1 < 0 ? 0x80 : 0; } /* exponent: bits 62-52 */ b64[0] |= (e >> 4); b64[1] = ((e & 0xF) << 4); /* fraction: bits 51-0 */ /* NOTE: ignore the "hidden" bit 52 in fraction */ b64[1] |= ((f >> 48) & 0xF); b64[2] = (f >> 40) & 0xFF; b64[3] = (f >> 32) & 0xFF; b64[4] = (f >> 24) & 0xFF; b64[5] = (f >> 16) & 0xFF; b64[6] = (f >> 8) & 0xFF; b64[7] = f & 0xFF; return 0; } /* convert from binary32 to float */ int lwm2m_b32_to_float(uint8_t *b32, size_t len, double *out) { int32_t f, k, i, e; bool sign = false; int32_t val1, val2; if (len != 4) { return -EINVAL; } val1 = 0; val2 = 0; /* calc sign: bit 31 */ sign = SHIFT_RIGHT(b32[0], 7, 0x1); /* calc exponent: bits 30-23 */ e = SHIFT_LEFT(b32[0], 1, 0xFF); e += SHIFT_RIGHT(b32[1], 7, 0x1); /* remove bias */ e -= 127; /* enable "hidden" fraction bit 24 which is always 1 */ f = ((int32_t)1 << 23); /* calc fraction: bits 22-0 */ f += ((int32_t)(b32[1] & 0x7F) << 16); f += ((int32_t)b32[2] << 8); f += b32[3]; /* handle whole number */ if (e > -1) { /* precision overflow */ if (e > 23) { e = 23; } val1 = (f >> (23 - e)) * (sign ? -1 : 1); } /* calculate the rest of the decimal */ k = PRECISION32; /* account for -e */ while (e < -1) { k /= 2; e++; } for (i = 22 - e; i >= 0; i--) { k /= 2; if (f & (1 << i)) { val2 += k; } } if (sign) { *out = (double)val1 - (double)val2 / PRECISION32; } else { *out = (double)val1 + (double)val2 / PRECISION32; } return 0; } /* convert from binary64 to float */ int lwm2m_b64_to_float(uint8_t *b64, size_t len, double *out) { int64_t f, k; int i, e; bool sign = false; int64_t val1, val2; if (len != 8) { return -EINVAL; } val1 = 0LL; val2 = 0LL; /* calc sign: bit 63 */ sign = SHIFT_RIGHT(b64[0], 7, 0x1); /* get exponent: bits 62-52 */ e = SHIFT_LEFT((uint16_t)b64[0], 4, 0x7F0); e += SHIFT_RIGHT(b64[1], 4, 0xF); /* remove bias */ e -= 1023; /* enable "hidden" fraction bit 53 which is always 1 */ f = ((int64_t)1 << 52); /* get fraction: bits 51-0 */ f += ((int64_t)(b64[1] & 0xF) << 48); f += ((int64_t)b64[2] << 40); f += ((int64_t)b64[3] << 32); f += ((int64_t)b64[4] << 24); f += ((int64_t)b64[5] << 16); f += ((int64_t)b64[6] << 8); f += b64[7]; /* handle whole number */ if (e > -1) { /* precision overflow */ if (e > 52) { e = 52; } val1 = (f >> (52 - e)) * (sign ? -1 : 1); } /* calculate the rest of the decimal */ k = PRECISION64; /* account for -e */ while (e < -1) { k /= 2; e++; } for (i = 51 - e; i >= 0; i--) { k /= 2; if (f & ((int64_t)1 << i)) { val2 += k; } } if (sign) { *out = (double)val1 - (double)val2 / PRECISION64; } else { *out = (double)val1 + (double)val2 / PRECISION64; } return 0; } int lwm2m_atof(const char *input, double *out) { char *pos, *end, buf[24]; long val; int64_t base = PRECISION64, sign = 1; int64_t val1, val2; if (!input || !out) { return -EINVAL; } strncpy(buf, input, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; if (strchr(buf, '-')) { sign = -1; } pos = strchr(buf, '.'); if (pos) { *pos = '\0'; } errno = 0; val = strtol(buf, &end, 10); if (errno || *end) { return -EINVAL; } val1 = (int64_t)val; val2 = 0; if (!pos) { *out = (double)val1; return 0; } while (*(++pos) && base > 1 && isdigit((unsigned char)*pos) != 0) { val2 = val2 * 10 + (*pos - '0'); base /= 10; } val2 *= sign * base; *out = (double)val1 + (double)val2 / PRECISION64; return !*pos || base == 1 ? 0 : -EINVAL; } int lwm2m_ftoa(double *input, char *out, size_t outlen, int8_t dec_limit) { size_t len; char buf[PRECISION64_LEN + 1]; int64_t val1 = (int64_t)*input; int64_t val2 = (*input - (int64_t)*input) * PRECISION64; len = snprintk(buf, sizeof(buf), "%0*lld", PRECISION64_LEN, (long long)llabs(val2)); if (len != PRECISION64_LEN) { strcpy(buf, "0"); } else { /* Round the value to the specified decimal point. */ if (dec_limit > 0 && dec_limit < sizeof(buf) && buf[dec_limit] != '\0') { bool round_up = buf[dec_limit] >= '5'; buf[dec_limit] = '\0'; len = dec_limit; while (round_up && dec_limit > 0) { dec_limit--; buf[dec_limit]++; if (buf[dec_limit] > '9') { buf[dec_limit] = '0'; } else { round_up = false; } } if (round_up) { if (*input < 0) { val1--; } else { val1++; } } } /* clear ending zeroes, but leave 1 if needed */ while (len > 1U && buf[len - 1] == '0') { buf[--len] = '\0'; } } return snprintk(out, outlen, "%s%lld.%s", /* handle negative val2 when val1 is 0 */ (val1 == 0 && val2 < 0) ? "-" : "", (long long)val1, buf); } uint16_t lwm2m_atou16(const uint8_t *buf, uint16_t buflen, uint16_t *len) { uint16_t val = 0U; uint16_t pos = 0U; /* we should get a value first - consume all numbers */ while (pos < buflen && isdigit(buf[pos]) != 0) { val = val * 10U + (buf[pos] - '0'); pos++; } *len = pos; return val; } int lwm2m_string_to_path(const char *pathstr, struct lwm2m_obj_path *path, char delim) { uint16_t value, len; int i, tokstart = -1, toklen; int end_index = strlen(pathstr) - 1; (void)memset(path, 0, sizeof(*path)); for (i = 0; i <= end_index; i++) { /* search for first numeric */ if (tokstart == -1) { if (isdigit((unsigned char)pathstr[i]) == 0) { continue; } tokstart = i; } /* find delimiter char or end of string */ if (pathstr[i] == delim || i == end_index) { toklen = i - tokstart + 1; /* don't process delimiter char */ if (pathstr[i] == delim) { toklen--; } if (toklen <= 0) { continue; } value = lwm2m_atou16(&pathstr[tokstart], toklen, &len); /* increase the path level for each token found */ path->level++; switch (path->level) { case LWM2M_PATH_LEVEL_OBJECT: path->obj_id = value; break; case LWM2M_PATH_LEVEL_OBJECT_INST: path->obj_inst_id = value; break; case LWM2M_PATH_LEVEL_RESOURCE: path->res_id = value; break; case LWM2M_PATH_LEVEL_RESOURCE_INST: path->res_inst_id = value; break; default: return -EINVAL; } tokstart = -1; } } return 0; } bool lwm2m_obj_path_equal(const struct lwm2m_obj_path *a, const struct lwm2m_obj_path *b) { uint8_t level = a->level; if (a->level != b->level) { return false; } if (level >= LWM2M_PATH_LEVEL_OBJECT && (a->obj_id != b->obj_id)) { return false; } if (level >= LWM2M_PATH_LEVEL_OBJECT_INST && (a->obj_inst_id != b->obj_inst_id)) { return false; } if (level >= LWM2M_PATH_LEVEL_RESOURCE && (a->res_id != b->res_id)) { return false; } if (level >= LWM2M_PATH_LEVEL_RESOURCE_INST && (a->res_inst_id != b->res_inst_id)) { return false; } return true; } /* for debugging: to print IP addresses */ char *lwm2m_sprint_ip_addr(const struct sockaddr *addr) { static char buf[NET_IPV6_ADDR_LEN]; if (addr->sa_family == AF_INET6) { return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf, sizeof(buf)); } if (addr->sa_family == AF_INET) { return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf, sizeof(buf)); } return "::"; } static uint8_t to_hex_digit(uint8_t digit) { if (digit >= 10U) { return digit - 10U + 'a'; } return digit + '0'; } char *sprint_token(const uint8_t *token, uint8_t tkl) { static char buf[32]; char *ptr = buf; if (token && tkl != 0) { int i; tkl = MIN(tkl, sizeof(buf) / 2 - 1); for (i = 0; i < tkl; i++) { *ptr++ = to_hex_digit(token[i] >> 4); *ptr++ = to_hex_digit(token[i] & 0x0F); } *ptr = '\0'; } else { strcpy(buf, "[no-token]"); } return buf; }