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) {