Back Merge

Merge to current
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c9285f3..fb5a071 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,6 +24,7 @@
 option ( coveralls_send "Send data to coveralls site" OFF )
 option ( build_docs "Create docs using Doxygen" ${DOXYGEN_FOUND} )
 option ( build_shared_libs "Build Shared Libraries" ON)
+option ( no_floats "Build without floating point support" OFF )
 
 set ( dist_dir    ${CMAKE_BINARY_DIR}/dist )
 set ( prefix      ${CMAKE_INSTALL_PREFIX} )
@@ -80,6 +81,10 @@
   message ( FATAL_ERROR "unhandled compiler id: ${CMAKE_C_COMPILER_ID}" )
 endif ()
 
+if ( no_floats )
+   add_definitions(-DCBOR_NO_FLOAT)
+endif()
+
 if ( verbose )
   set ( CMAKE_VERBOSE_MAKEFILE ON )
 endif ()
diff --git a/README.md b/README.md
index 67913d9..9b57776 100644
--- a/README.md
+++ b/README.md
@@ -29,4 +29,14 @@
 
     ./build.sh
 
+Building including testing:
+
+    ./build.sh all test
+
+Generating a test coverage report (requires lcov[^1]; result in `build/lcov/index.html`):
+
+    ./build.sh all coveralls coverage_report
+
 License: MIT
+
+[^1]: Installation with homebrew: `brew install lcov`
diff --git a/cmake/CoverallsClear.cmake b/cmake/CoverallsClear.cmake
index eb68695..f6b0ace 100644
--- a/cmake/CoverallsClear.cmake
+++ b/cmake/CoverallsClear.cmake
@@ -20,5 +20,7 @@
 # Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
 #
 
+message ( "Clearing coverage data" )
 file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda)
-
+file(REMOVE ${PROJECT_BINARY_DIR}/*.gcov)
+file(REMOVE ${PROJECT_BINARY_DIR}/*.gcov_tmp)
diff --git a/cmake/CoverallsGenerateGcov.cmake b/cmake/CoverallsGenerateGcov.cmake
index bb8bd5f..2c26bd9 100644
--- a/cmake/CoverallsGenerateGcov.cmake
+++ b/cmake/CoverallsGenerateGcov.cmake
@@ -161,7 +161,7 @@
 	# If -p is not specified then the file is named only "the_file.c.gcov"
 	#
 	execute_process(
-		COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
+		COMMAND ${GCOV_EXECUTABLE} -c -p -o ${GCDA_DIR} ${GCDA}
 		WORKING_DIRECTORY ${COV_PATH}
 	)
 endforeach()
diff --git a/cmake/LCov.cmake b/cmake/LCov.cmake
index ebdc665..1ac9ec3 100644
--- a/cmake/LCov.cmake
+++ b/cmake/LCov.cmake
@@ -1,6 +1,12 @@
-# TODO: parameterize for reuse
-add_custom_target(coverage_report
-  COMMAND lcov --directory src/CMakeFiles/cn-cbor.dir --capture --output-file cn-cbor.info
-  COMMAND genhtml --output-directory lcov cn-cbor.info
-  COMMAND echo "Coverage report in: file://${CMAKE_BINARY_DIR}/lcov/index.html"
-)
+FIND_PROGRAM( LCOV_PATH lcov )
+FIND_PROGRAM( GENHTML_PATH genhtml )
+
+if (LCOV_PATH)
+  # message ( "lcov: ${LCOV_PATH}" )
+
+  add_custom_target(coverage_report
+    COMMAND "${LCOV_PATH}" --rc lcov_branch_coverage=1  --no-checksum --base-directory "${CMAKE_CURRENT_SOURCE_DIR}" --directory src/CMakeFiles/${PROJECT_NAME}.dir --no-external --capture --output-file ${PROJECT_NAME}.info
+    COMMAND "${GENHTML_PATH}" --rc genhtml_branch_coverage=1 --output-directory lcov ${PROJECT_NAME}.info
+    COMMAND echo "Coverage report in: file://${CMAKE_BINARY_DIR}/lcov/index.html"
+  )
+endif()
diff --git a/include/cn-cbor/cn-cbor.h b/include/cn-cbor/cn-cbor.h
index 35619d6..b251104 100644
--- a/include/cn-cbor/cn-cbor.h
+++ b/include/cn-cbor/cn-cbor.h
@@ -163,7 +163,10 @@
   /** An invalid parameter was passed to a function */
   CN_CBOR_ERR_INVALID_PARAMETER,
   /** Allocation failed */
-  CN_CBOR_ERR_OUT_OF_MEMORY
+  CN_CBOR_ERR_OUT_OF_MEMORY,
+  /** A float was encountered during parse but the library was built without
+      support for float types. */
+  CN_CBOR_ERR_FLOAT_NOT_SUPPORTED
 } cn_cbor_error;
 
 /**
@@ -298,11 +301,10 @@
  * @param[in]  cb         [description]
  * @return                -1 on fail, or number of bytes written
  */
-MYLIB_EXPORT
-ssize_t cbor_encoder_write(uint8_t *buf,
-                           size_t buf_offset,
-                           size_t buf_size,
-                           const cn_cbor *cb);
+ssize_t cn_cbor_encoder_write(uint8_t *buf,
+			      size_t buf_offset,
+			      size_t buf_size,
+			      const cn_cbor *cb);
 
 /**
  * Create a CBOR map.
@@ -408,7 +410,7 @@
  */
 MYLIB_EXPORT
 bool cn_cbor_mapput_string(cn_cbor* cb_map,
-                           char* key, cn_cbor* cb_value
+                           const char* key, cn_cbor* cb_value
                            CBOR_CONTEXT,
                            cn_cbor_errback *errp);
 
diff --git a/src/cn-cbor.c b/src/cn-cbor.c
index 4beb6b1..cfb2c91 100644
--- a/src/cn-cbor.c
+++ b/src/cn-cbor.c
@@ -43,7 +43,12 @@
   }
 }
 
+<<<<<<< HEAD
 static double decode_half(uint64_t half) {
+=======
+#ifndef CBOR_NO_FLOAT
+static double decode_half(int half) {
+>>>>>>> cabo/master
   int exp = (half >> 10) & 0x1f;
   int mant = half & 0x3ff;
   double val;
@@ -52,6 +57,7 @@
   else val = mant == 0 ? INFINITY : NAN;
   return half & 0x8000 ? -val : val;
 }
+#endif /* CBOR_NO_FLOAT */
 
 /* Fix these if you can't do non-aligned reads */
 #define ntoh8p(p) (*(unsigned char*)(p))
@@ -92,6 +98,7 @@
   int ai;
   uint64_t val;
   cn_cbor* cb = NULL;
+#ifndef CBOR_NO_FLOAT
   union {
     float f;
     uint32_t u;
@@ -100,6 +107,7 @@
     double d;
     uint64_t u;
   } u64;
+#endif /* CBOR_NO_FLOAT */
 
 again:
   TAKE(pos, ebuf, 1, ib = ntoh8p(pos) );
@@ -181,16 +189,31 @@
     case VAL_TRUE:  cb->type = CN_CBOR_TRUE;  break;
     case VAL_NIL:   cb->type = CN_CBOR_NULL;  break;
     case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break;
-    case AI_2: cb->type = CN_CBOR_DOUBLE; cb->v.dbl = decode_half(val); break;
+    case AI_2:
+#ifndef CBOR_NO_FLOAT
+      cb->type = CN_CBOR_DOUBLE;
+      cb->v.dbl = decode_half(val);
+#else /*  CBOR_NO_FLOAT */
+      CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /*  CBOR_NO_FLOAT */
+      break;
     case AI_4:
+#ifndef CBOR_NO_FLOAT
       cb->type = CN_CBOR_DOUBLE;
       u32.u = (uint32_t) val;
       cb->v.dbl = u32.f;
+#else /*  CBOR_NO_FLOAT */
+      CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /*  CBOR_NO_FLOAT */
       break;
     case AI_8:
+#ifndef CBOR_NO_FLOAT
       cb->type = CN_CBOR_DOUBLE;
       u64.u = val;
       cb->v.dbl = u64.d;
+#else /*  CBOR_NO_FLOAT */
+      CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /*  CBOR_NO_FLOAT */
       break;
     default: cb->v.uint = val;
     }
diff --git a/src/cn-create.c b/src/cn-create.c
index 9841848..a6f6b5b 100644
--- a/src/cn-create.c
+++ b/src/cn-create.c
@@ -133,7 +133,7 @@
 
 MYLIB_EXPORT
 bool cn_cbor_mapput_string(cn_cbor* cb_map,
-                           char* key, cn_cbor* cb_value
+                           const char* key, cn_cbor* cb_value
                            CBOR_CONTEXT,
                            cn_cbor_errback *errp)
 {
diff --git a/src/cn-encoder.c b/src/cn-encoder.c
index 48403d1..b52a996 100644
--- a/src/cn-encoder.c
+++ b/src/cn-encoder.c
@@ -56,6 +56,10 @@
 ensure_writable(1); \
 ws->buf[ws->offset++] = (b); \
 
+#define write_byte_ensured(b) \
+ensure_writable(1); \
+write_byte(b); \
+
 static uint8_t _xlate[] = {
   IB_FALSE,    /* CN_CBOR_FALSE */
   IB_TRUE,     /* CN_CBOR_TRUE */
@@ -116,24 +120,69 @@
   }
 }
 
+#ifndef CBOR_NO_FLOAT
 static void _write_double(cn_write_state *ws, double val)
 {
-  uint64_t be64;
-  /* Copy the same problematic implementation from the decoder. */
-  union {
-    double d;
-    uint64_t u;
-  } u64;
-  /* TODO: cast double to float and back, and see if it changes.
-     See cabo's ruby code for more:
-     https://github.com/cabo/cbor-ruby/blob/master/ext/cbor/packer.h */
+  float float_val = val;
+  if (float_val == val) {                /* 32 bits is enough and we aren't NaN */
+    uint32_t be32;
+    uint16_t be16, u16;
+    union {
+      float f;
+      uint32_t u;
+    } u32;
+    u32.f = float_val;
+    if ((u32.u & 0x1FFF) == 0) { /* worth trying half */
+      int s16 = (u32.u >> 16) & 0x8000;
+      int exp = (u32.u >> 23) & 0xff;
+      int mant = u32.u & 0x7fffff;
+      if (exp == 0 && mant == 0)
+        ;              /* 0.0, -0.0 */
+      else if (exp >= 113 && exp <= 142) /* normalized */
+        s16 += ((exp - 112) << 10) + (mant >> 13);
+      else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */
+        if (mant & ((1 << (126 - exp)) - 1))
+          goto float32;         /* loss of precision */
+        s16 += ((mant + 0x800000) >> (126 - exp));
+      } else if (exp == 255 && mant == 0) { /* Inf */
+        s16 += 0x7c00;
+      } else
+        goto float32;           /* loss of range */
 
-  ensure_writable(9);
-  u64.d = val;
-  be64 = hton64p((const uint8_t*)&u64.u);
+      ensure_writable(3);
+      u16 = s16;
+      be16 = hton16p((const uint8_t*)&u16);
 
-  write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8);
+      write_byte_and_data(IB_PRIM | 25, (const void*)&be16, 2);
+      return;
+    }
+  float32:
+    ensure_writable(5);
+    be32 = hton32p((const uint8_t*)&u32.u);
+
+    write_byte_and_data(IB_PRIM | 26, (const void*)&be32, 4);
+
+  } else if (val != val) {      /* NaN -- we always write a half NaN*/
+    ensure_writable(3);
+    write_byte_and_data(IB_PRIM | 25, (const void*)"\x7e\x00", 2);
+  } else {
+    uint64_t be64;
+    /* Copy the same problematic implementation from the decoder. */
+    union {
+      double d;
+      uint64_t u;
+    } u64;
+
+    u64.d = val;
+
+    ensure_writable(9);
+    be64 = hton64p((const uint8_t*)&u64.u);
+
+    write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8);
+
+  }
 }
+#endif /* CBOR_NO_FLOAT */
 
 // TODO: make public?
 typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
@@ -187,21 +236,21 @@
   switch (cb->type) {
   case CN_CBOR_ARRAY:
     if (is_indefinite(cb)) {
-      write_byte(IB_ARRAY | AI_INDEF);
+      write_byte_ensured(IB_ARRAY | AI_INDEF);
     } else {
       CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length));
     }
     break;
   case CN_CBOR_MAP:
     if (is_indefinite(cb)) {
-      write_byte(IB_MAP | AI_INDEF);
+      write_byte_ensured(IB_MAP | AI_INDEF);
     } else {
       CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length/2));
     }
     break;
   case CN_CBOR_BYTES_CHUNKED:
   case CN_CBOR_TEXT_CHUNKED:
-    write_byte(_xlate[cb->type] | AI_INDEF);
+    write_byte_ensured(_xlate[cb->type] | AI_INDEF);
     break;
 
   case CN_CBOR_TEXT:
@@ -216,7 +265,7 @@
   case CN_CBOR_TRUE:
   case CN_CBOR_NULL:
   case CN_CBOR_UNDEF:
-    write_byte(_xlate[cb->type]);
+    write_byte_ensured(_xlate[cb->type]);
     break;
 
   case CN_CBOR_TAG:
@@ -231,7 +280,9 @@
     break;
 
   case CN_CBOR_DOUBLE:
+#ifndef CBOR_NO_FLOAT
     CHECK(_write_double(ws, cb->v.dbl));
+#endif /* CBOR_NO_FLOAT */
     break;
 
   case CN_CBOR_INVALID:
@@ -245,14 +296,13 @@
   cn_write_state *ws = context;
   UNUSED_PARAM(cb);
   UNUSED_PARAM(depth);
-  write_byte(IB_BREAK);
+  write_byte_ensured(IB_BREAK);
 }
 
-MYLIB_EXPORT
-ssize_t cbor_encoder_write(uint8_t *buf,
-                           size_t buf_offset,
-                           size_t buf_size,
-                           const cn_cbor *cb)
+ssize_t cn_cbor_encoder_write(uint8_t *buf,
+			      size_t buf_offset,
+			      size_t buf_size,
+			      const cn_cbor *cb)
 {
   cn_write_state ws = { buf, buf_offset, buf_size };
   _visit(cb, _encoder_visitor, _encoder_breaker, &ws);
diff --git a/src/cn-error.c b/src/cn-error.c
index 6e2451b..ad48746 100644
--- a/src/cn-error.c
+++ b/src/cn-error.c
@@ -11,5 +11,6 @@
  "CN_CBOR_ERR_RESERVED_AI",
  "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
  "CN_CBOR_ERR_INVALID_PARAMETER",
- "CN_CBOR_ERR_OUT_OF_MEMORY"
+ "CN_CBOR_ERR_OUT_OF_MEMORY",
+ "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED"
 };
diff --git a/test/cbor_test.c b/test/cbor_test.c
index 05a93b7..8c53a93 100644
--- a/test/cbor_test.c
+++ b/test/cbor_test.c
@@ -76,6 +76,7 @@
     ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING], "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING");
     ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_INVALID_PARAMETER], "CN_CBOR_ERR_INVALID_PARAMETER");
     ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_OUT_OF_MEMORY], "CN_CBOR_ERR_OUT_OF_MEMORY");
+    ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_FLOAT_NOT_SUPPORTED], "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED");
 }
 
 CTEST(cbor, parse)
@@ -108,8 +109,17 @@
         "f6",	      // null
         "f7",	      // undefined
         "f8ff",     // simple(255)
+#ifndef CBOR_NO_FLOAT
+        "f93c00",     // 1.0
+        "f9bc00",     // -1.0
+        "f903ff",     // 6.097555160522461e-05
+        "f90400",     // 6.103515625e-05
+        "f907ff",     // 0.00012201070785522461
+        "f90800",     // 0.0001220703125
+        "fa47800000", // 65536.0
         "fb3ff199999999999a",     // 1.1
-        "fb7ff8000000000000",     // NaN
+        "f97e00",   // NaN
+#endif /* CBOR_NO_FLOAT */
         "5f42010243030405ff",     // (_ h'0102', h'030405')
         "7f61616161ff",           // (_ "a", "a")
         "9fff",                   // [_ ]
@@ -131,13 +141,84 @@
         ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
         ASSERT_NOT_NULL(cb);
 
-        enc_sz = cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+        enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
         ASSERT_DATA(b.ptr, b.sz, encoded, enc_sz);
         free(b.ptr);
         cn_cbor_free(cb CONTEXT_NULL);
     }
 }
 
+
+CTEST(cbor, parse_normalize)
+{
+    cn_cbor_errback err;
+    char *basic_tests[] = {
+      "00", "00",                       // 0
+      "1800", "00",
+      "1818", "1818",
+      "190000", "00",
+      "190018", "1818",
+      "1a00000000", "00",
+      "1b0000000000000000", "00",
+      "20", "20",                       // -1
+      "3800", "20",
+      "c600", "c600",                   // 6(0) (undefined tag)
+      "d80600", "c600",
+      "d9000600", "c600",
+    };
+    char *float_tests[] = {
+      "fb3ff0000000000000", "f93c00",   // 1.0
+      "fbbff0000000000000", "f9bc00",   // -1.0
+      "fb40f86a0000000000", "fa47c35000", // 100000.0
+      "fb7ff8000000000000", "f97e00",   // NaN
+      "fb3e70000000000000", "f90001",   // 5.960464477539063e-08
+      "fb3e78000000000000", "fa33c00000", //  8.940696716308594e-08
+      "fb3e80000000000000", "f90002",   // 1.1920928955078125e-07
+    };
+    const cn_cbor *cb;
+    buffer b, b2;
+    size_t i;
+    unsigned char encoded[1024];
+    ssize_t enc_sz;
+
+    for (i=0; i<sizeof(basic_tests)/sizeof(char*); i+=2) {
+        ASSERT_TRUE(parse_hex(basic_tests[i], &b));
+        ASSERT_TRUE(parse_hex(basic_tests[i+1], &b2));
+        err.err = CN_CBOR_NO_ERROR;
+        cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+        CTEST_LOG("%s: %s", basic_tests[i], cn_cbor_error_str[err.err]);
+        ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
+        ASSERT_NOT_NULL(cb);
+
+        enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+        ASSERT_DATA(b2.ptr, b2.sz, encoded, enc_sz);
+        free(b.ptr);
+        free(b2.ptr);
+        cn_cbor_free(cb CONTEXT_NULL);
+    }
+
+    for (i=0; i<sizeof(float_tests)/sizeof(char*); i+=2) {
+        ASSERT_TRUE(parse_hex(float_tests[i], &b));
+        ASSERT_TRUE(parse_hex(float_tests[i+1], &b2));
+        err.err = CN_CBOR_NO_ERROR;
+        cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+        CTEST_LOG("%s: %s", float_tests[i], cn_cbor_error_str[err.err]);
+#ifndef CBOR_NO_FLOAT
+        ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
+        ASSERT_NOT_NULL(cb);
+#else /* CBOR_NO_FLOAT */
+        ASSERT_EQUAL(err.err, CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+        ASSERT_NULL(cb);
+#endif /* CBOR_NO_FLOAT */
+
+        /* enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb); */
+        /* ASSERT_DATA(b2.ptr, b2.sz, encoded, enc_sz); */
+        free(b.ptr);
+        free(b2.ptr);
+        cn_cbor_free(cb CONTEXT_NULL);
+    }
+}
+
 typedef struct _cbor_failure
 {
     char *hex;
@@ -162,7 +243,7 @@
     uint8_t buf[10];
     cn_cbor inv = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
 
-    ASSERT_EQUAL(-1, cbor_encoder_write(buf, 0, sizeof(buf), &inv));
+    ASSERT_EQUAL(-1, cn_cbor_encoder_write(buf, 0, sizeof(buf), &inv));
 
     for (i=0; i<sizeof(tests)/sizeof(cbor_failure); i++) {
         ASSERT_TRUE(parse_hex(tests[i].hex, &b));
@@ -178,8 +259,10 @@
 // Decoder loses float size information
 CTEST(cbor, float)
 {
+#ifndef CBOR_NO_FLOAT
     cn_cbor_errback err;
     char *tests[] = {
+        "f90001", // 5.960464477539063e-08
         "f9c400", // -4.0
         "fa47c35000", // 100000.0
         "f97e00", // Half NaN, half beast
@@ -189,15 +272,21 @@
     const cn_cbor *cb;
     buffer b;
     size_t i;
+    unsigned char encoded[1024];
+    ssize_t enc_sz;
 
     for (i=0; i<sizeof(tests)/sizeof(char*); i++) {
         ASSERT_TRUE(parse_hex(tests[i], &b));
         cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
         ASSERT_NOT_NULL(cb);
 
+        enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+        ASSERT_DATA(b.ptr, b.sz, encoded, enc_sz);
+
         free(b.ptr);
         cn_cbor_free(cb CONTEXT_NULL);
     }
+#endif /* CBOR_NO_FLOAT */
 }
 
 CTEST(cbor, getset)
@@ -352,6 +441,6 @@
   ASSERT_NOT_NULL(cdata);
 
   ASSERT_TRUE(cn_cbor_mapput_int(map, 0, cdata, CONTEXT_NULL_COMMA NULL));
-  enc_sz = cbor_encoder_write(encoded, 0, sizeof(encoded), map);
+  enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), map);
   ASSERT_EQUAL(7, enc_sz);
 }
diff --git a/test/test.c b/test/test.c
index 8fc931f..263f9fe 100644
--- a/test/test.c
+++ b/test/test.c
@@ -75,6 +75,7 @@
   case CN_CBOR_NULL:   OUT("null");                      break;
   case CN_CBOR_TRUE:   OUT("true");                      break;
   case CN_CBOR_FALSE:  OUT("false");                     break;
+  case CN_CBOR_UNDEF:  OUT("simple(23)");                break;
   case CN_CBOR_INT:    PRF("%ld", cb->v.sint);           break;
   case CN_CBOR_UINT:   PRF("%lu", cb->v.uint);           break;
   case CN_CBOR_DOUBLE: PRF("%e", cb->v.dbl);             break;
@@ -97,6 +98,7 @@
   "CN_CBOR_ERR_RESERVED_AI",
   "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
   "CN_CBOR_ERR_OUT_OF_MEMORY",
+  "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED",
 };
 
 static void cn_cbor_decode_test(const unsigned char *buf, int len) {