+lldpd (0.7.7)
+ * Fixes:
+ + Various bugs related to fixed point number handling (for
+ coordinates in LLDP-MED)
+
lldpd (0.7.6)
* Features:
+ Provide a way to build packages for OSX.
lib_LTLIBRARIES = liblldpctl.la
include_HEADERS = lldpctl.h
-liblldpctl_la_SOURCES = lldpctl.h private.h errors.c connection.c atom.c atom-private.c
+liblldpctl_la_SOURCES = lldpctl.h private.h errors.c connection.c atom.c atom-private.c \
+ fixedpoint.h fixedpoint.c
liblldpctl_la_LIBADD = $(top_builddir)/src/libcommon-daemon-lib.la
liblldpctl_la_LDFLAGS = -export-symbols-regex '^lldpctl_' -version-info 5:0:1
#include "../lldpd-structs.h"
#include "../log.h"
#include "private.h"
-
-#define ntohll(x) \
- (((u_int64_t)(ntohl((int)(((x) << 32) >> 32))) << 32) | \
- (unsigned int)ntohl(((int)((x) >> 32))))
+#include "fixedpoint.h"
/* Translation from constants to string */
static lldpctl_map_t lldpd_protocol_map[] = {
}
static const char*
-fixed_precision(lldpctl_atom_t *atom,
- u_int64_t value, int intpart, int floatpart, int displaysign,
- const char *negsuffix, const char *possuffix)
-{
- char *buf;
- u_int64_t tmp = value;
- int negative = 0, n;
- u_int32_t integer = 0;
- if (value & (1ULL << (intpart + floatpart - 1))) {
- negative = 1;
- tmp = ~value;
- tmp += 1;
+read_fixed_precision(lldpctl_atom_t *atom,
+ char *buffer, unsigned shift,
+ unsigned intbits, unsigned fltbits, const char *suffix)
+{
+ struct fp_number fp = fp_buftofp((unsigned char*)buffer, intbits, fltbits, shift);
+ char *result = fp_fptostr(fp, suffix);
+ if (result == NULL) {
+ SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
+ return NULL;
}
- integer = (u_int32_t)((tmp &
- (((1ULL << intpart)-1) << floatpart)) >> floatpart);
- tmp = (tmp & ((1<< floatpart) - 1))*10000/(1ULL << floatpart);
- if ((buf = _lldpctl_alloc_in_atom(atom, 64)) == NULL)
+ char *stored = _lldpctl_alloc_in_atom(atom, strlen(result) + 1);
+ if (stored == NULL) {
+ free(result);
return NULL;
- n = snprintf(buf, 64, "%s%u.%04llu%s",
- displaysign?(negative?"-":"+"):"",
- integer, (unsigned long long int)tmp,
- (negative && negsuffix)?negsuffix:
- (!negative && possuffix)?possuffix:"");
- if (n > -1 && n < 64)
- return buf;
- SET_ERROR(atom->conn, LLDPCTL_ERR_NOMEM);
- return NULL;
+ }
+ strlcpy(stored, result, strlen(result) + 1);
+ return stored;
}
static const char*
struct _lldpctl_atom_med_location_t *m =
(struct _lldpctl_atom_med_location_t *)atom;
char *value;
- u_int64_t l;
/* Local and remote port */
switch (key) {
m->location->data[15]);
case lldpctl_k_med_location_latitude:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
- memcpy(&l, m->location->data, sizeof(u_int64_t));
- l = (ntohll(l) & 0x03FFFFFFFF000000ULL) >> 24;
- return fixed_precision(atom, l, 9, 25, 0, " S", " N");
+ return read_fixed_precision(atom, m->location->data,
+ 0, 9, 25, "NS");
case lldpctl_k_med_location_longitude:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
- memcpy(&l, m->location->data + 5, sizeof(u_int64_t));
- l = (ntohll(l) & 0x03FFFFFFFF000000ULL) >> 24;
- return fixed_precision(atom, l, 9, 25, 0, " W", " E");
+ return read_fixed_precision(atom, m->location->data,
+ 40, 9, 25, "EW");
case lldpctl_k_med_location_altitude:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
- l = 0;
- memcpy(&l, m->location->data + 10, 5);
- l = (ntohll(l) & 0x3FFFFFFF000000ULL) >> 24;
- return fixed_precision(atom, l, 22, 8, 1, NULL, NULL);
+ return read_fixed_precision(atom, m->location->data,
+ 84, 22, 8, NULL);
case lldpctl_k_med_location_altitude_unit:
if (m->location->format != LLDP_MED_LOCFORMAT_COORD) break;
switch (m->location->data[10] & 0xf0) {
return NULL;
}
-static void
-write_fixed_precision(uint8_t *where, double l,
- int precisionnb, int intnb, int floatnb)
-{
- int intpart, floatpart, precision = 6;
- if (l > 0) {
- intpart = (int)l;
- floatpart = (l - intpart) * (1 << floatnb);
- } else {
- intpart = -(int)l;
- floatpart = (-(l + intpart)) * (1 << floatnb);
- intpart = ~intpart; intpart += 1;
- floatpart = ~floatpart; floatpart += 1;
- }
- if ((1 << precisionnb) - 1 < precision)
- precision = (1 << precisionnb) - 1;
- /* We need to write precision, int part and float part. */
- do {
- int obit, i, o;
- unsigned int ints[3] = { precision, intpart, floatpart };
- unsigned int bits[3] = { precisionnb, intnb, floatnb };
- for (i = 0, obit = 8, o = 0; i < 3;) {
- if (obit > bits[i]) {
- where[o] = where[o] |
- ((ints[i] & ((1 << bits[i]) - 1)) << (obit - bits[i]));
- obit -= bits[i];
- i++;
- } else {
- where[o] = where[o] |
- ((ints[i] >> (bits[i] - obit)) & ((1 << obit) - 1));
- bits[i] -= obit;
- obit = 8;
- o++;
- }
- }
- } while(0);
-}
-
static lldpctl_atom_t*
_lldpctl_atom_set_str_med_location(lldpctl_atom_t *atom, lldpctl_key_t key,
const char *value)
{
struct _lldpctl_atom_med_location_t *mloc =
(struct _lldpctl_atom_med_location_t *)atom;
- double l;
+ struct fp_number fp;
char *end;
/* Only local port can be modified */
case lldpctl_k_med_location_latitude:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
- l = strtod(value, &end);
+ fp = fp_strtofp(value, &end, 9, 25);
if (!end) goto bad;
if (end && *end != '\0') {
if (*(end+1) != '\0') goto bad;
- if (*end == 'S') l = -l;
+ if (*end == 'S') fp = fp_negate(fp);
else if (*end != 'N') goto bad;
}
- write_fixed_precision((uint8_t*)mloc->location->data, l, 6, 9, 25);
+ fp_fptobuf(fp, (unsigned char*)mloc->location->data, 0);
return atom;
case lldpctl_k_med_location_longitude:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
- l = strtod(value, &end);
+ fp = fp_strtofp(value, &end, 9, 25);
if (!end) goto bad;
if (end && *end != '\0') {
if (*(end+1) != '\0') goto bad;
- if (*end == 'W') l = -l;
+ if (*end == 'W') fp = fp_negate(fp);
else if (*end != 'E') goto bad;
}
- write_fixed_precision((uint8_t*)mloc->location->data + 5, l, 6, 9, 25);
+ fp_fptobuf(fp, (unsigned char*)mloc->location->data, 40);
return atom;
case lldpctl_k_med_location_altitude:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
if (mloc->location->data == NULL || mloc->location->data_len != 16) goto bad;
- l = strtod(value, &end);
+ fp = fp_strtofp(value, &end, 22, 8);
if (!end || *end != '\0') goto bad;
- write_fixed_precision((uint8_t*)mloc->location->data + 11, l, 2, 22, 8);
+ fp_fptobuf(fp, (unsigned char*)mloc->location->data, 84);
return atom;
case lldpctl_k_med_location_altitude_unit:
if (mloc->location->format != LLDP_MED_LOCFORMAT_COORD) goto bad;
--- /dev/null
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2013 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "fixedpoint.h"
+
+/* This is not a general purpose fixed point library. First, there is no
+ * arithmetic. Second, some functions assume that the total precision does not
+ * exceed 64 bits.
+ */
+
+#ifdef ENABLE_LLDPMED
+
+#define ntohll(x) \
+ (((u_int64_t)(ntohl((int)(((x) << 32) >> 32))) << 32) | \
+ (unsigned int)ntohl(((int)((x) >> 32))))
+
+/**
+ * Convert a string to fixed point number.
+ *
+ * @param repr String to convert.
+ * @param end If not NULL, will contain a pointer to the character after the
+ * last character used in the conversion.
+ * @param intbits Number of bits to represent the integer part.
+ * @param fltbits Number of bits to represent the float part.
+ * @return A fixed point number.
+ *
+ * If there is an overflow, there will be a truncation. Moreover, the fraction
+ * part will be rounded to the nearest possible power of two representation. The
+ * point will depend on the number of decimal provided with the fraction
+ * part.
+ */
+struct fp_number
+fp_strtofp(const char *repr, char **end,
+ unsigned intbits, unsigned fltbits)
+{
+ char *endptr = NULL, *e2;
+ struct fp_number result = {
+ .integer = { 0, intbits },
+ .fraction = { 0, fltbits, 0 }
+ };
+ result.integer.value = strtoll(repr, &endptr, 10);
+ if (result.integer.value >= (1LL << (intbits - 1)))
+ result.integer.value = (1LL << (intbits - 1)) - 1;
+ else if (result.integer.value < ~(1LL << (intbits - 1)) + 1)
+ result.integer.value = ~(1LL << (intbits - 1)) + 1;
+ if (*endptr == '.') {
+ long long precision = 1;
+ e2 = endptr + 1;
+ result.fraction.value = strtoll(e2, &endptr, 10);
+ /* Convert to a representation in power of two. Get the
+ * precision from the number of digits provided. This is NOT the
+ * value of the higher bits in the binary representation: we
+ * consider that if the user inputs, 0.9375, it means to
+ * represent anything between 0 and 0.9999 with the same
+ * precision. Therefore, we don't have only 4 bits of precision
+ * but 14. */
+ while (e2++ != endptr) precision *= 10;
+ result.fraction.value <<= fltbits;
+ result.fraction.value /= precision;
+ result.fraction.precision = sizeof(precision) * 8 -
+ __builtin_clzll(precision - 1);
+ if (result.fraction.precision > fltbits)
+ result.fraction.precision = fltbits;
+ }
+ if (end) *end = endptr;
+ return result;
+}
+
+/**
+ * Get a string representation of a fixed point number.
+ *
+ * @param fp Fixed point number.
+ * @param suffix If not NULL, use the first character when positive and the
+ * second one when negative instead of prefixing by `-`.
+ * @return the string representation
+ *
+ * Since we convert from binary to decimal, we are as precise as the binary
+ * representation.
+ */
+char *
+fp_fptostr(struct fp_number fp, const char *suffix)
+{
+ char *result = NULL;
+ char *frac = NULL;
+ int negative = (fp.integer.value < 0);
+ if (fp.fraction.value == 0)
+ frac = strdup("");
+ else {
+ long long decimal = fp.fraction.value;
+ long long precision = 1;
+ int len = 0;
+ while ((1LL << fp.fraction.precision) > precision) {
+ precision *= 10;
+ len += 1;
+ }
+ /* We did round-up, when converting from decimal. We round-down
+ * to have some coherency. */
+ precision /= 10; len -= 1;
+ if (precision == 0) precision = 1;
+ decimal *= precision;
+ decimal >>= fp.fraction.bits;
+ if (asprintf(&frac, ".%0*llu", len, decimal) == -1)
+ return NULL;
+ }
+ if (asprintf(&result, "%s%llu%s%c",
+ (suffix == NULL && negative) ? "-" : "",
+ (negative) ? (-fp.integer.value) : fp.integer.value,
+ frac,
+ (suffix && !negative) ? suffix[0] :
+ (suffix && negative) ? suffix[1] : ' ') == -1) {
+ free(frac);
+ return NULL;
+ }
+ free(frac);
+ if (!suffix) result[strlen(result) - 1] = '\0';
+ return result;
+}
+
+/**
+ * Turn a fixed point number into its representation in a buffer.
+ *
+ * @param fp Fixed point number.
+ * @param buf Output buffer.
+ * @param shift Number of bits to skip at the beginning of the buffer.
+ *
+ * The representation of a fixed point number is the precision (always 6 bits
+ * because we assume that int part + frac part does not exceed 64 bits), the
+ * integer part and the fractional part.
+ */
+void
+fp_fptobuf(struct fp_number fp, unsigned char *buf, unsigned shift)
+{
+ unsigned long long value = (fp.integer.value >= 0) ?
+ ((fp.integer.value << fp.fraction.bits) + fp.fraction.value) :
+ (~(((unsigned long long)(-fp.integer.value) << fp.fraction.bits) +
+ fp.fraction.value) + 1);
+ unsigned long long ints[] = { fp.integer.bits + fp.fraction.precision,
+ value };
+ unsigned int bits[] = { 6,
+ fp.integer.bits + fp.fraction.bits };
+
+ unsigned i, obit, o;
+ for (i = 0, obit = 8 - (shift % 8), o = shift / 8; i < 2;) {
+ if (obit > bits[i]) {
+ /* We need to clear bits that will be overwritten but do not touch other bits */
+ buf[o] = buf[o] & (~((1 << obit) - 1) |
+ ((1 << (obit - bits[i])) - 1));
+ buf[o] = buf[o] |
+ ((ints[i] & ((1 << bits[i]) - 1)) << (obit - bits[i]));
+ obit -= bits[i];
+ i++;
+ } else {
+ /* As in the other branch... */
+ buf[o] = buf[o] & (~((1 << obit) - 1));
+ buf[o] = buf[o] |
+ ((ints[i] >> (bits[i] - obit)) & ((1 << obit) - 1));
+ bits[i] -= obit;
+ obit = 8;
+ o++;
+ }
+ }
+}
+
+/**
+ * Parse a fixed point number from a buffer.
+ *
+ * @param buf Input buffer
+ * @param intbits Number of bits used for integer part.
+ * @param fltbits Number of bits used for fractional part.
+ * @param shift Number of bits to skip at the beginning of the buffer.
+ *
+ * @return the parsed fixed point number.
+ *
+ * The representation is the same as for @c fp_fptobuf().
+ */
+struct fp_number
+fp_buftofp(const unsigned char *buf,
+ unsigned intbits, unsigned fltbits,
+ unsigned shift)
+{
+ unsigned long long value = 0, precision = 0;
+ unsigned long long *ints[] = { &precision,
+ &value };
+ unsigned int bits[] = { 6,
+ intbits + fltbits };
+
+ unsigned o, ibit, i;
+ for (o = 0, ibit = 8 - (shift % 8), i = shift / 8; o < 2;) {
+ if (ibit > bits[o]) {
+ *ints[o] = *ints[o] | ((buf[i] >> (ibit - bits[o])) & ((1ULL << bits[o]) - 1));
+ ibit -= bits[o];
+ o++;
+ } else {
+ *ints[o] = *ints[o] | ((buf[i] & ((1ULL << ibit) - 1)) << (bits[o] - ibit));
+ bits[o] -= ibit;
+ ibit = 8;
+ i++;
+ }
+ }
+
+ /* Don't handle too low precision */
+ if (precision > intbits)
+ precision -= intbits;
+ else
+ precision = intbits;
+
+ int negative = !!(value & (1ULL << (intbits + fltbits - 1)));
+ if (negative) value = (~value + 1) & ((1ULL << (intbits + fltbits - 1)) - 1);
+ struct fp_number result = {
+ .integer = { value >> fltbits, intbits },
+ .fraction = { value & ((1ULL << fltbits) - 1), fltbits, precision }
+ };
+ if (negative) result.integer.value = -result.integer.value;
+
+ return result;
+}
+
+/**
+ * Negate a fixed point number.
+ */
+struct fp_number
+fp_negate(struct fp_number fp)
+{
+ unsigned intbits = fp.integer.bits;
+ struct fp_number result = fp;
+ result.integer.value = -result.integer.value;
+ if (result.integer.value >= (1LL << (intbits - 1)))
+ result.integer.value = (1LL << (intbits - 1)) - 1;
+ else if (result.integer.value < ~(1LL << (intbits - 1)) + 1)
+ result.integer.value = ~(1LL << (intbits - 1)) + 1;
+ return result;
+}
+
+#endif
--- /dev/null
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if ! defined FIXEDPOINT_H && defined ENABLE_LLDPMED
+#define FIXEDPOINT_H
+
+struct fp_number {
+ struct {
+ long long value;
+ unsigned bits;
+ } integer;
+ struct {
+ long long value;
+ unsigned bits;
+ unsigned precision;
+ } fraction;
+};
+struct fp_number fp_strtofp(const char *, char **, unsigned, unsigned);
+struct fp_number fp_buftofp(const unsigned char *, unsigned, unsigned, unsigned);
+struct fp_number fp_negate(struct fp_number);
+char *fp_fptostr(struct fp_number, const char *);
+void fp_fptobuf(struct fp_number, unsigned char *, unsigned);
+
+#endif
AM_CFLAGS = -I $(top_srcdir)/include
-TESTS = check_marshal check_lldp check_cdp check_sonmp check_edp
+TESTS = check_marshal check_lldp check_cdp check_sonmp check_edp check_fixedpoint
if HAVE_CHECK
$(top_srcdir)/src/daemon/lldpd.h \
common.h common.c
+check_fixedpoint_SOURCES = check_fixedpoint.c \
+ $(top_srcdir)/src/lib/fixedpoint.h \
+ $(top_srcdir)/src/lib/fixedpoint.c
+
AM_CFLAGS += @CHECK_CFLAGS@
LDADD = $(top_builddir)/src/daemon/liblldpd.la @CHECK_LIBS@ @LIBEVENT_LDFLAGS@
--- /dev/null
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <check.h>
+#include <stdlib.h>
+
+#include "../src/lib/fixedpoint.h"
+
+#ifdef ENABLE_LLDPMED
+
+START_TEST(test_string_parsing_suffix) {
+ char *end;
+ fp_strtofp("4541T", &end, 14, 8);
+ ck_assert_int_eq(*end, 'T');
+ fp_strtofp("4541.U", &end, 14, 8);
+ ck_assert_int_eq(*end, 'U');
+ fp_strtofp("4541.676V", &end, 14, 8);
+ ck_assert_int_eq(*end, 'V');
+}
+END_TEST
+
+START_TEST(test_string_parsing_positive_int) {
+ struct fp_number fp = fp_strtofp("4541T", NULL, 14, 8);
+ ck_assert_int_eq(fp.integer.bits, 14);
+ ck_assert_int_eq(fp.integer.value, 4541);
+ ck_assert_int_eq(fp.fraction.bits, 8);
+ ck_assert_int_eq(fp.fraction.value, 0);
+ ck_assert_int_eq(fp.fraction.precision, 0);
+}
+END_TEST
+
+START_TEST(test_string_parsing_negative_int) {
+ struct fp_number fp = fp_strtofp("-4214N", NULL, 14, 8);
+ ck_assert_int_eq(fp.integer.bits, 14);
+ ck_assert_int_eq(fp.integer.value, -4214);
+ ck_assert_int_eq(fp.fraction.bits, 8);
+ ck_assert_int_eq(fp.fraction.value, 0);
+ ck_assert_int_eq(fp.fraction.precision, 0);
+}
+END_TEST
+
+START_TEST(test_string_parsing_positive_int_overflow) {
+ struct fp_number fp1 = fp_strtofp("4098", NULL, 13, 8);
+ struct fp_number fp2 = fp_strtofp("4096", NULL, 13, 8);
+ struct fp_number fp3 = fp_strtofp("4095", NULL, 13, 8);
+ struct fp_number fp4 = fp_strtofp("4094", NULL, 13, 8);
+ ck_assert_int_eq(fp1.integer.value, 4095);
+ ck_assert_int_eq(fp2.integer.value, 4095);
+ ck_assert_int_eq(fp3.integer.value, 4095);
+ ck_assert_int_eq(fp4.integer.value, 4094);
+}
+END_TEST
+
+START_TEST(test_string_parsing_negative_int_overflow) {
+ struct fp_number fp1 = fp_strtofp("-4097", NULL, 13, 8);
+ struct fp_number fp2 = fp_strtofp("-4096", NULL, 13, 8);
+ struct fp_number fp3 = fp_strtofp("-4095", NULL, 13, 8);
+ struct fp_number fp4 = fp_strtofp("-4094", NULL, 13, 8);
+ ck_assert_int_eq(fp1.integer.value, -4096);
+ ck_assert_int_eq(fp2.integer.value, -4096);
+ ck_assert_int_eq(fp3.integer.value, -4095);
+ ck_assert_int_eq(fp4.integer.value, -4094);
+}
+END_TEST
+
+START_TEST(test_string_parsing_positive_float) {
+ struct fp_number fp1 = fp_strtofp("1542.6250E", NULL, 13, 20);
+ ck_assert_int_eq(fp1.integer.value, 1542);
+ ck_assert_int_eq(fp1.fraction.precision, 14);
+ ck_assert_int_eq((fp1.fraction.value * 10000) >> fp1.fraction.bits, 6250);
+
+ struct fp_number fp2 = fp_strtofp("1542.06250E", NULL, 13, 4);
+ ck_assert_int_eq(fp2.integer.value, 1542);
+ ck_assert_int_eq(fp2.fraction.precision, 4);
+ ck_assert_int_eq((fp2.fraction.value * 10000) >> fp2.fraction.bits, 625);
+}
+END_TEST
+
+START_TEST(test_string_parsing_negative_float) {
+ struct fp_number fp = fp_strtofp("-11542.6250N", NULL, 15, 4);
+ ck_assert_int_eq(fp.integer.value, -11542);
+ ck_assert_int_eq(fp.fraction.precision, 4);
+ ck_assert_int_eq((fp.fraction.value * 10000) >> fp.fraction.bits, 6250);
+}
+END_TEST
+
+START_TEST(test_string_parsing_no_fract_part) {
+ struct fp_number fp = fp_strtofp("11542.", NULL, 15, 4);
+ ck_assert_int_eq(fp.integer.value, 11542);
+ ck_assert_int_eq(fp.fraction.value, 0);
+ ck_assert_int_eq(fp.fraction.precision, 1);
+}
+END_TEST
+
+START_TEST(test_string_parsing_no_int_part) {
+ struct fp_number fp = fp_strtofp(".6250E", NULL, 13, 4);
+ ck_assert_int_eq(fp.integer.value, 0);
+ ck_assert_int_eq(fp.fraction.precision, 4);
+ ck_assert_int_eq((fp.fraction.value * 10000) >> fp.fraction.bits, 6250);
+}
+END_TEST
+
+
+START_TEST(test_string_representation_positive_int) {
+ struct fp_number fp1 = fp_strtofp("214", NULL, 9, 9);
+ struct fp_number fp2 = fp_strtofp("11178.0000", NULL, 15, 9);
+ ck_assert_str_eq(fp_fptostr(fp1, NULL), "214");
+ ck_assert_str_eq(fp_fptostr(fp2, NULL), "11178");
+ ck_assert_str_eq(fp_fptostr(fp2, "ES"), "11178E");
+}
+END_TEST
+
+START_TEST(test_string_representation_negative_int) {
+ struct fp_number fp1 = fp_strtofp("-214", NULL, 9, 9);
+ struct fp_number fp2 = fp_strtofp("-11178.0000", NULL, 15, 9);
+ ck_assert_str_eq(fp_fptostr(fp1, NULL), "-214");
+ ck_assert_str_eq(fp_fptostr(fp2, NULL), "-11178");
+ ck_assert_str_eq(fp_fptostr(fp2, "ES"), "11178S");
+}
+END_TEST
+
+START_TEST(test_string_representation_positive_float) {
+ struct fp_number fp = fp_strtofp("214.6250", NULL, 9, 20);
+ ck_assert_str_eq(fp_fptostr(fp, NULL), "214.6250");
+ ck_assert_str_eq(fp_fptostr(fp, "ES"), "214.6250E");
+}
+END_TEST
+
+START_TEST(test_string_representation_positive_float_with_leading_zero) {
+ struct fp_number fp = fp_strtofp("214.06250", NULL, 9, 24);
+ ck_assert_str_eq(fp_fptostr(fp, NULL), "214.06250");
+ ck_assert_str_eq(fp_fptostr(fp, "ES"), "214.06250E");
+}
+END_TEST
+
+START_TEST(test_string_representation_negative_float) {
+ struct fp_number fp1 = fp_strtofp("-214.625", NULL, 22, 10);
+ struct fp_number fp2 = fp_strtofp("-415.5", NULL, 22, 4);
+ ck_assert_str_eq(fp_fptostr(fp1, NULL), "-214.625");
+ ck_assert_str_eq(fp_fptostr(fp2, NULL), "-415.5");
+ ck_assert_str_eq(fp_fptostr(fp2, "ES"), "415.5S");
+}
+END_TEST
+
+START_TEST(test_buffer_representation_positive_float) {
+ unsigned char buffer[5] = {};
+ unsigned char expected[] = { 0x21 << 2, 47 << 1, 0x68, 0x00, 0x00 };
+ /* 47.2031250 = 47 + 2**-3 + 2**-4 + 2**-6, precision = 9+24 */
+ struct fp_number fp = fp_strtofp("47.2031250", NULL, 9, 25);
+ fp_fptobuf(fp, buffer, 0);
+ fail_unless(memcmp(buffer, expected, sizeof(expected)) == 0);
+}
+END_TEST
+
+START_TEST(test_buffer_representation_negative_float) {
+ unsigned char buffer[5] = {};
+ unsigned char expected[] = { (0x21 << 2) | 3, 0xa1, 0x98, 0x00, 0x00 };
+ /* 47.2031250 = 000101111.0011010000000000000000000 */
+ /* -47.2031250 = 111010000.1100101111111111111111111 + 1 */
+ /* -47.2031250 = 111010000.1100110000000000000000000 */
+ struct fp_number fp = fp_strtofp("-47.2031250", NULL, 9, 25);
+ fp_fptobuf(fp, buffer, 0);
+ fail_unless(memcmp(buffer, expected, sizeof(expected)) == 0);
+}
+END_TEST
+
+START_TEST(test_buffer_representation_with_shift) {
+ unsigned char buffer[] = { 0x77, 0xc6, 0x0, 0x0, 0x0, 0x0, 0xc7 };
+ unsigned char expected[] = { 0x77, 0xc8, 0x45, 0xe6, 0x80, 0x00, 0x07 };
+ struct fp_number fp = fp_strtofp("47.2031250", NULL, 9, 25);
+ fp_fptobuf(fp, buffer, 12);
+ fail_unless(memcmp(buffer, expected, sizeof(buffer)) == 0);
+}
+END_TEST
+
+START_TEST(test_buffer_representation_altitude) {
+ unsigned char buffer[5] = {};
+ unsigned char expected[] = { (22 + 4) << 2, 0, 0, 14 << 4 | 1 << 3, 0 };
+ struct fp_number fp = fp_strtofp("14.5", NULL, 22, 8);
+ fp_fptobuf(fp, buffer, 0);
+ fail_unless(memcmp(buffer, expected, sizeof(buffer)) == 0);
+}
+END_TEST
+
+START_TEST(test_buffer_parsing_positive_float) {
+ unsigned char buffer[] = { 0x21 << 2, 47 << 1, 0x68, 0x00, 0x00 };
+ struct fp_number fp = fp_buftofp(buffer, 9, 25, 0);
+ ck_assert_int_eq(fp.integer.value, 47);
+ ck_assert_int_eq(fp.integer.bits, 9);
+ ck_assert_int_eq((fp.fraction.value * 10000000) >> fp.fraction.bits, 2031250);
+ ck_assert_int_eq(fp.fraction.bits, 25);
+ ck_assert_int_eq(fp.fraction.precision, 24);
+}
+END_TEST
+
+START_TEST(test_buffer_parsing_negative_float) {
+ unsigned char buffer[] = { (0x21 << 2) | 3, 0xa1, 0x98, 0x00, 0x00 };
+ struct fp_number fp = fp_buftofp(buffer, 9, 25, 0);
+ ck_assert_int_eq(fp.integer.value, -47);
+ ck_assert_int_eq(fp.integer.bits, 9);
+ ck_assert_int_eq((fp.fraction.value * 10000000) >> fp.fraction.bits, 2031250);
+ ck_assert_int_eq(fp.fraction.bits, 25);
+ ck_assert_int_eq(fp.fraction.precision, 24);
+}
+END_TEST
+
+/* This is some corner case */
+START_TEST(test_buffer_parsing_positive_float_2) {
+ unsigned char buffer[] = { 0x40, 0x9c, 0x80, 0x00, 0x00 };
+ struct fp_number fp = fp_buftofp(buffer, 9, 25, 0);
+ ck_assert_int_eq(fp.integer.value, 78);
+}
+END_TEST
+
+START_TEST(test_buffer_parsing_positive_float_with_shift) {
+ unsigned char buffer[] = { 0x77, 0xc8, 0x45, 0xe6, 0x80, 0x00, 0x07 };
+ struct fp_number fp = fp_buftofp(buffer, 9, 25, 12);
+ ck_assert_int_eq(fp.integer.value, 47);
+ ck_assert_int_eq(fp.integer.bits, 9);
+ ck_assert_int_eq((fp.fraction.value * 10000000) >> fp.fraction.bits, 2031250);
+ ck_assert_int_eq(fp.fraction.bits, 25);
+ ck_assert_int_eq(fp.fraction.precision, 24);
+}
+END_TEST
+
+START_TEST(test_buffer_parsing_negative_float_with_shift) {
+ unsigned char buffer[] = { 0x00, 0xff, (0x21 << 2) | 3, 0xa1, 0x98, 0x00, 0x00 };
+ struct fp_number fp = fp_buftofp(buffer, 9, 25, 16);
+ ck_assert_int_eq(fp.integer.value, -47);
+ ck_assert_int_eq(fp.integer.bits, 9);
+ ck_assert_int_eq((fp.fraction.value * 10000000) >> fp.fraction.bits, 2031250);
+ ck_assert_int_eq(fp.fraction.bits, 25);
+ ck_assert_int_eq(fp.fraction.precision, 24);
+}
+END_TEST
+
+START_TEST(test_negate_positive) {
+ struct fp_number fp = fp_strtofp("14.5", NULL, 9, 25);
+ struct fp_number nfp = fp_negate(fp);
+ ck_assert_int_eq(nfp.integer.value, -14);
+ ck_assert_int_eq(fp.fraction.value, nfp.fraction.value);
+ ck_assert_str_eq(fp_fptostr(nfp, NULL), "-14.5");
+}
+END_TEST
+
+START_TEST(test_negate_negative) {
+ struct fp_number fp = fp_strtofp("-14.5", NULL, 9, 25);
+ struct fp_number nfp = fp_negate(fp);
+ ck_assert_int_eq(nfp.integer.value, 14);
+ ck_assert_int_eq(fp.fraction.value, nfp.fraction.value);
+ ck_assert_str_eq(fp_fptostr(nfp, NULL), "14.5");
+}
+END_TEST
+
+#endif
+
+Suite *
+fixedpoint_suite(void)
+{
+ Suite *s = suite_create("Fixed point representation");
+
+#ifdef ENABLE_LLDPMED
+ TCase *tc_fp = tcase_create("Fixed point representation");
+ tcase_add_test(tc_fp, test_string_parsing_suffix);
+ tcase_add_test(tc_fp, test_string_parsing_positive_int);
+ tcase_add_test(tc_fp, test_string_parsing_negative_int);
+ tcase_add_test(tc_fp, test_string_parsing_no_fract_part);
+ tcase_add_test(tc_fp, test_string_parsing_no_int_part);
+ tcase_add_test(tc_fp, test_string_parsing_positive_int_overflow);
+ tcase_add_test(tc_fp, test_string_parsing_negative_int_overflow);
+ tcase_add_test(tc_fp, test_string_parsing_positive_float);
+ tcase_add_test(tc_fp, test_string_parsing_negative_float);
+ tcase_add_test(tc_fp, test_string_representation_positive_int);
+ tcase_add_test(tc_fp, test_string_representation_negative_int);
+ tcase_add_test(tc_fp, test_string_representation_positive_float);
+ tcase_add_test(tc_fp, test_string_representation_positive_float_with_leading_zero);
+ tcase_add_test(tc_fp, test_string_representation_negative_float);
+ tcase_add_test(tc_fp, test_buffer_representation_positive_float);
+ tcase_add_test(tc_fp, test_buffer_representation_negative_float);
+ tcase_add_test(tc_fp, test_buffer_representation_with_shift);
+ tcase_add_test(tc_fp, test_buffer_representation_altitude);
+ tcase_add_test(tc_fp, test_buffer_parsing_positive_float);
+ tcase_add_test(tc_fp, test_buffer_parsing_positive_float_2);
+ tcase_add_test(tc_fp, test_buffer_parsing_negative_float);
+ tcase_add_test(tc_fp, test_buffer_parsing_positive_float_with_shift);
+ tcase_add_test(tc_fp, test_buffer_parsing_negative_float_with_shift);
+ tcase_add_test(tc_fp, test_negate_positive);
+ tcase_add_test(tc_fp, test_negate_negative);
+ suite_add_tcase(s, tc_fp);
+#endif
+
+ return s;
+}
+
+int
+main()
+{
+ int number_failed;
+ Suite *s = fixedpoint_suite();
+ SRunner *sr = srunner_create(s);
+ srunner_run_all(sr, CK_ENV);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}