Merge pull request #463 from theunkn0wn1/feature/grpcio_tools_protoc
use grpcio-tools protoc instead of system protoc
diff --git a/README.md b/README.md
index 16d77ab..501675d 100644
--- a/README.md
+++ b/README.md
@@ -82,9 +82,13 @@
Note: Mac OS X by default aliases 'clang' as 'gcc', while not actually
supporting the same command line options as gcc does. To run tests on
-Mac OS X, use: "scons CC=clang CXX=clang". Same way can be used to run
+Mac OS X, use: `scons CC=clang CXX=clang`. Same way can be used to run
tests with different compilers on any platform.
+For embedded platforms, there is currently support for running the tests
+on STM32 discovery board and [simavr](https://github.com/buserror/simavr)
+AVR simulator. Use `scons PLATFORM=STM32` and `scons PLATFORM=AVR` to run
+these tests.
Build systems and integration
diff --git a/docs/reference.rst b/docs/reference.rst
index 89d1f0f..39bc611 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -59,6 +59,8 @@
This is only to be used when the decoder on the
receiving side cannot process packed scalar
arrays. Such example is older protobuf.js.
+PB_CONVERT_DOUBLE_FLOAT Convert doubles to floats for platforms that do
+ not support 64-bit doubles. Mainly AVR.
============================ ================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow
diff --git a/examples/using_double_on_avr/Makefile b/examples/using_double_on_avr/Makefile
deleted file mode 100644
index 874a64b..0000000
--- a/examples/using_double_on_avr/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-# Include the nanopb provided Makefile rules
-include ../../extra/nanopb.mk
-
-# Compiler flags to enable all warnings & debug info
-CFLAGS = -Wall -Werror -g -O0
-CFLAGS += -I$(NANOPB_DIR)
-
-all: run_tests
-
-.SUFFIXES:
-
-clean:
- rm -f test_conversions encode_double decode_double doubleproto.pb.c doubleproto.pb.h
-
-test_conversions: test_conversions.c double_conversion.c
- $(CC) $(CFLAGS) -o $@ $^
-
-%: %.c double_conversion.c doubleproto.pb.c
- $(CC) $(CFLAGS) -o $@ $^ $(NANOPB_CORE)
-
-run_tests: test_conversions encode_double decode_double
- ./test_conversions
- ./encode_double | ./decode_double
-
diff --git a/examples/using_double_on_avr/README.txt b/examples/using_double_on_avr/README.txt
deleted file mode 100644
index d9fcdfc..0000000
--- a/examples/using_double_on_avr/README.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Nanopb example "using_double_on_avr"
-====================================
-
-Some processors/compilers, such as AVR-GCC, do not support the double
-datatype. Instead, they have sizeof(double) == 4. Because protocol
-binary format uses the double encoding directly, this causes trouble
-if the protocol in .proto requires double fields.
-
-This directory contains a solution to this problem. It uses uint64_t
-to store the raw wire values, because its size is correct on all
-platforms. The file double_conversion.c provides functions that
-convert these values to/from floats, without relying on compiler
-support.
-
-To use this method, you need to make some modifications to your code:
-
-1) Change all 'double' fields into 'fixed64' in the .proto.
-
-2) Whenever writing to a 'double' field, use float_to_double().
-
-3) Whenever reading a 'double' field, use double_to_float().
-
-The conversion routines are as accurate as the float datatype can
-be. Furthermore, they should handle all special values (NaN, inf, denormalized
-numbers) correctly. There are testcases in test_conversions.c.
diff --git a/examples/using_double_on_avr/decode_double.c b/examples/using_double_on_avr/decode_double.c
deleted file mode 100644
index 5802eca..0000000
--- a/examples/using_double_on_avr/decode_double.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Decodes a double value into a float variable.
- * Used to read double values with AVR code, which doesn't support double directly.
- */
-
-#include <stdio.h>
-#include <pb_decode.h>
-#include "double_conversion.h"
-#include "doubleproto.pb.h"
-
-int main()
-{
- uint8_t buffer[32];
- size_t count = fread(buffer, 1, sizeof(buffer), stdin);
- pb_istream_t stream = pb_istream_from_buffer(buffer, count);
-
- AVRDoubleMessage message;
- pb_decode(&stream, AVRDoubleMessage_fields, &message);
-
- float v1 = double_to_float(message.field1);
- float v2 = double_to_float(message.field2);
-
- printf("Values: %f %f\n", v1, v2);
-
- if (v1 == 1234.5678f &&
- v2 == 0.00001f)
- {
- return 0;
- }
- else
- {
- return 1;
- }
-}
diff --git a/examples/using_double_on_avr/double_conversion.c b/examples/using_double_on_avr/double_conversion.c
deleted file mode 100644
index cf79b9a..0000000
--- a/examples/using_double_on_avr/double_conversion.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/* Conversion routines for platforms that do not support 'double' directly. */
-
-#include "double_conversion.h"
-#include <math.h>
-
-typedef union {
- float f;
- uint32_t i;
-} conversion_t;
-
-/* Note: IEE 754 standard specifies float formats as follows:
- * Single precision: sign, 8-bit exp, 23-bit frac.
- * Double precision: sign, 11-bit exp, 52-bit frac.
- */
-
-uint64_t float_to_double(float value)
-{
- conversion_t in;
- in.f = value;
- uint8_t sign;
- int16_t exponent;
- uint64_t mantissa;
-
- /* Decompose input value */
- sign = (in.i >> 31) & 1;
- exponent = ((in.i >> 23) & 0xFF) - 127;
- mantissa = in.i & 0x7FFFFF;
-
- if (exponent == 128)
- {
- /* Special value (NaN etc.) */
- exponent = 1024;
- }
- else if (exponent == -127)
- {
- if (!mantissa)
- {
- /* Zero */
- exponent = -1023;
- }
- else
- {
- /* Denormalized */
- mantissa <<= 1;
- while (!(mantissa & 0x800000))
- {
- mantissa <<= 1;
- exponent--;
- }
- mantissa &= 0x7FFFFF;
- }
- }
-
- /* Combine fields */
- mantissa <<= 29;
- mantissa |= (uint64_t)(exponent + 1023) << 52;
- mantissa |= (uint64_t)sign << 63;
-
- return mantissa;
-}
-
-float double_to_float(uint64_t value)
-{
- uint8_t sign;
- int16_t exponent;
- uint32_t mantissa;
- conversion_t out;
-
- /* Decompose input value */
- sign = (value >> 63) & 1;
- exponent = ((value >> 52) & 0x7FF) - 1023;
- mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */
-
- /* Figure if value is in range representable by floats. */
- if (exponent == 1024)
- {
- /* Special value */
- exponent = 128;
- }
- else if (exponent > 127)
- {
- /* Too large */
- if (sign)
- return -INFINITY;
- else
- return INFINITY;
- }
- else if (exponent < -150)
- {
- /* Too small */
- if (sign)
- return -0.0f;
- else
- return 0.0f;
- }
- else if (exponent < -126)
- {
- /* Denormalized */
- mantissa |= 0x1000000;
- mantissa >>= (-126 - exponent);
- exponent = -127;
- }
-
- /* Round off mantissa */
- mantissa = (mantissa + 1) >> 1;
-
- /* Check if mantissa went over 2.0 */
- if (mantissa & 0x800000)
- {
- exponent += 1;
- mantissa &= 0x7FFFFF;
- mantissa >>= 1;
- }
-
- /* Combine fields */
- out.i = mantissa;
- out.i |= (uint32_t)(exponent + 127) << 23;
- out.i |= (uint32_t)sign << 31;
-
- return out.f;
-}
-
-
diff --git a/examples/using_double_on_avr/double_conversion.h b/examples/using_double_on_avr/double_conversion.h
deleted file mode 100644
index 62b6a8a..0000000
--- a/examples/using_double_on_avr/double_conversion.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* AVR-GCC does not have real double datatype. Instead its double
- * is equal to float, i.e. 32 bit value. If you need to communicate
- * with other systems that use double in their .proto files, you
- * need to do some conversion.
- *
- * These functions use bitwise operations to mangle floats into doubles
- * and then store them in uint64_t datatype.
- */
-
-#ifndef DOUBLE_CONVERSION
-#define DOUBLE_CONVERSION
-
-#include <stdint.h>
-
-/* Convert native 4-byte float into a 8-byte double. */
-extern uint64_t float_to_double(float value);
-
-/* Convert 8-byte double into native 4-byte float.
- * Values are rounded to nearest, 0.5 away from zero.
- * Overflowing values are converted to Inf or -Inf.
- */
-extern float double_to_float(uint64_t value);
-
-
-#endif
-
diff --git a/examples/using_double_on_avr/doubleproto.proto b/examples/using_double_on_avr/doubleproto.proto
deleted file mode 100644
index 72d3f9c..0000000
--- a/examples/using_double_on_avr/doubleproto.proto
+++ /dev/null
@@ -1,15 +0,0 @@
-// A message containing doubles, as used by other applications.
-syntax = "proto2";
-
-message DoubleMessage {
- required double field1 = 1;
- required double field2 = 2;
-}
-
-// A message containing doubles, but redefined using uint64_t.
-// For use in AVR code.
-message AVRDoubleMessage {
- required fixed64 field1 = 1;
- required fixed64 field2 = 2;
-}
-
diff --git a/examples/using_double_on_avr/encode_double.c b/examples/using_double_on_avr/encode_double.c
deleted file mode 100644
index cd532d4..0000000
--- a/examples/using_double_on_avr/encode_double.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Encodes a float value into a double on the wire.
- * Used to emit doubles from AVR code, which doesn't support double directly.
- */
-
-#include <stdio.h>
-#include <pb_encode.h>
-#include "double_conversion.h"
-#include "doubleproto.pb.h"
-
-int main()
-{
- AVRDoubleMessage message = {
- float_to_double(1234.5678f),
- float_to_double(0.00001f)
- };
-
- uint8_t buffer[32];
- pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
-
- pb_encode(&stream, AVRDoubleMessage_fields, &message);
- fwrite(buffer, 1, stream.bytes_written, stdout);
-
- return 0;
-}
-
diff --git a/examples/using_double_on_avr/test_conversions.c b/examples/using_double_on_avr/test_conversions.c
deleted file mode 100644
index 22620a6..0000000
--- a/examples/using_double_on_avr/test_conversions.c
+++ /dev/null
@@ -1,56 +0,0 @@
-#include "double_conversion.h"
-#include <math.h>
-#include <stdio.h>
-
-static const double testvalues[] = {
- 0.0, -0.0, 0.1, -0.1,
- M_PI, -M_PI, 123456.789, -123456.789,
- INFINITY, -INFINITY, NAN, INFINITY - INFINITY,
- 1e38, -1e38, 1e39, -1e39,
- 1e-38, -1e-38, 1e-39, -1e-39,
- 3.14159e-37,-3.14159e-37, 3.14159e-43, -3.14159e-43,
- 1e-60, -1e-60, 1e-45, -1e-45,
- 0.99999999999999, -0.99999999999999, 127.999999999999, -127.999999999999
-};
-
-#define TESTVALUES_COUNT (sizeof(testvalues)/sizeof(testvalues[0]))
-
-int main()
-{
- int status = 0;
- int i;
- for (i = 0; i < TESTVALUES_COUNT; i++)
- {
- double orig = testvalues[i];
- float expected_float = (float)orig;
- double expected_double = (double)expected_float;
-
- float got_float = double_to_float(*(uint64_t*)&orig);
- uint64_t got_double = float_to_double(got_float);
-
- uint32_t e1 = *(uint32_t*)&expected_float;
- uint32_t g1 = *(uint32_t*)&got_float;
- uint64_t e2 = *(uint64_t*)&expected_double;
- uint64_t g2 = got_double;
-
- if (g1 != e1)
- {
- printf("%3d double_to_float fail: %08x != %08x\n", i, g1, e1);
- status = 1;
- }
-
- if (g2 != e2)
- {
- printf("%3d float_to_double fail: %016llx != %016llx\n", i,
- (unsigned long long)g2,
- (unsigned long long)e2);
- status = 1;
- }
- }
-
- return status;
-}
-
-
-
-
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 65acd9c..87802cd 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -1475,7 +1475,6 @@
if [msg for msg in self.messages if hasattr(msg,'msgid')]:
yield '/* Message IDs (where set with "msgid" option) */\n'
- yield '#ifdef PB_MSGID\n'
for msg in self.messages:
if hasattr(msg,'msgid'):
yield '#define PB_MSG_%d %s\n' % (msg.msgid, msg.name)
@@ -1497,7 +1496,6 @@
if hasattr(msg,'msgid'):
yield '#define %s_msgid %d\n' % (msg.name, msg.msgid)
yield '\n'
- yield '#endif\n\n'
yield '#ifdef __cplusplus\n'
yield '} /* extern "C" */\n'
@@ -1565,12 +1563,13 @@
if has_double:
yield '\n'
+ yield '#ifndef PB_CONVERT_DOUBLE_FLOAT\n'
yield '/* On some platforms (such as AVR), double is really float.\n'
- yield ' * Using double on these platforms is not directly supported\n'
- yield ' * by nanopb, but see example_avr_double.\n'
- yield ' * To get rid of this error, remove any double fields from your .proto.\n'
+ yield ' * To be able to encode/decode double on these platforms, you need.\n'
+ yield ' * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line.\n'
yield ' */\n'
yield 'PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n'
+ yield '#endif\n'
yield '\n'
yield '/* @@protoc_insertion_point(eof) */\n'
diff --git a/pb.h b/pb.h
index f9d368e..74cc7e9 100644
--- a/pb.h
+++ b/pb.h
@@ -45,6 +45,10 @@
* Such example is older protobuf.js. */
/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */
+/* Enable conversion of doubles to floats for platforms that do not
+ * support 64-bit doubles. Most commonly AVR. */
+/* #define PB_CONVERT_DOUBLE_FLOAT 1 */
+
/******************************************************************
* You usually don't need to change anything below this line. *
* Feel free to look around and use the defined macros, though. *
diff --git a/pb_decode.c b/pb_decode.c
index ac586e7..62a7d97 100644
--- a/pb_decode.c
+++ b/pb_decode.c
@@ -274,7 +274,12 @@
if (!pb_decode_varint32(stream, &length))
return false;
- return pb_read(stream, NULL, length);
+ if ((size_t)length != length)
+ {
+ PB_RETURN_ERROR(stream, "size too large");
+ }
+
+ return pb_read(stream, NULL, (size_t)length);
}
bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof)
@@ -358,8 +363,8 @@
if (substream->bytes_left < size)
PB_RETURN_ERROR(stream, "parent stream too short");
- substream->bytes_left = size;
- stream->bytes_left -= size;
+ substream->bytes_left = (size_t)size;
+ stream->bytes_left -= (size_t)size;
return true;
}
@@ -476,7 +481,7 @@
{
/* We memset to zero so that any callbacks are set to NULL.
* pb_dec_submessage() will set any default values. */
- memset(field->pData, 0, field->data_size);
+ memset(field->pData, 0, (size_t)field->data_size);
}
return decode_basic_field(stream, field);
@@ -851,7 +856,7 @@
else
{
/* Initialize to zeros */
- memset(field->pData, 0, field->data_size);
+ memset(field->pData, 0, (size_t)field->data_size);
}
}
}
@@ -1039,9 +1044,9 @@
* seeking to the end of the field array. Usually we
* are already close to end after decoding.
*/
- unsigned req_field_count;
+ pb_size_t req_field_count;
pb_type_t last_type;
- unsigned i;
+ pb_size_t i;
do {
req_field_count = iter.required_field_index;
last_type = iter.type;
@@ -1067,7 +1072,7 @@
if ((req_field_count & 31) != 0)
{
if (fields_seen.bitfield[req_field_count >> 5] !=
- (allbits >> (32 - (req_field_count & 31))))
+ (allbits >> (uint8_t)(32 - (req_field_count & 31))))
{
PB_RETURN_ERROR(stream, "missing required field");
}
@@ -1410,6 +1415,13 @@
static bool checkreturn pb_dec_fixed(pb_istream_t *stream, const pb_field_iter_t *field)
{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+ {
+ return pb_decode_double_as_float(stream, (float*)field->pData);
+ }
+#endif
+
if (field->data_size == sizeof(uint32_t))
{
return pb_decode_fixed32(stream, field->pData);
@@ -1460,7 +1472,7 @@
}
dest->size = (pb_size_t)size;
- return pb_read(stream, dest->bytes, size);
+ return pb_read(stream, dest->bytes, (size_t)size);
}
static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field)
@@ -1473,7 +1485,7 @@
return false;
/* Space for null terminator */
- alloc_size = size + 1;
+ alloc_size = (size_t)(size + 1);
if (alloc_size < size)
PB_RETURN_ERROR(stream, "size too large");
@@ -1495,7 +1507,7 @@
}
dest[size] = 0;
- return pb_read(stream, dest, size);
+ return pb_read(stream, dest, (size_t)size);
}
static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field)
@@ -1536,13 +1548,76 @@
if (size == 0)
{
/* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */
- memset(field->pData, 0, field->data_size);
+ memset(field->pData, 0, (size_t)field->data_size);
return true;
}
if (size != field->data_size)
PB_RETURN_ERROR(stream, "incorrect fixed length bytes size");
- return pb_read(stream, (pb_byte_t*)field->pData, field->data_size);
+ return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size);
}
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+bool pb_decode_double_as_float(pb_istream_t *stream, float *dest)
+{
+ uint8_t sign;
+ int exponent;
+ uint32_t mantissa;
+ uint64_t value;
+ union { float f; uint32_t i; } out;
+
+ if (!pb_decode_fixed64(stream, &value))
+ return false;
+
+ /* Decompose input value */
+ sign = (uint8_t)((value >> 63) & 1);
+ exponent = (int)((value >> 52) & 0x7FF) - 1023;
+ mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */
+
+ /* Figure if value is in range representable by floats. */
+ if (exponent == 1024)
+ {
+ /* Special value */
+ exponent = 128;
+ }
+ else if (exponent > 127)
+ {
+ /* Too large, convert to infinity */
+ exponent = 128;
+ mantissa = 0;
+ }
+ else if (exponent < -150)
+ {
+ /* Too small, convert to zero */
+ exponent = -127;
+ mantissa = 0;
+ }
+ else if (exponent < -126)
+ {
+ /* Denormalized */
+ mantissa |= 0x1000000;
+ mantissa >>= (-126 - exponent);
+ exponent = -127;
+ }
+
+ /* Round off mantissa */
+ mantissa = (mantissa + 1) >> 1;
+
+ /* Check if mantissa went over 2.0 */
+ if (mantissa & 0x800000)
+ {
+ exponent += 1;
+ mantissa &= 0x7FFFFF;
+ mantissa >>= 1;
+ }
+
+ /* Combine fields */
+ out.i = mantissa;
+ out.i |= (uint32_t)(exponent + 127) << 23;
+ out.i |= (uint32_t)sign << 31;
+
+ *dest = out.f;
+ return true;
+}
+#endif
diff --git a/pb_decode.h b/pb_decode.h
index 0498a83..b64d95a 100644
--- a/pb_decode.h
+++ b/pb_decode.h
@@ -177,6 +177,11 @@
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
#endif
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+/* Decode a double value into float variable. */
+bool pb_decode_double_as_float(pb_istream_t *stream, float *dest);
+#endif
+
/* Make a limited-length substream for reading a PB_WT_STRING field. */
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
diff --git a/pb_encode.c b/pb_encode.c
index 8856981..093173b 100644
--- a/pb_encode.c
+++ b/pb_encode.c
@@ -795,6 +795,13 @@
static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field)
{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+ {
+ return pb_encode_float_as_double(stream, *(float*)field->pData);
+ }
+#endif
+
if (field->data_size == sizeof(uint32_t))
{
return pb_encode_fixed32(stream, field->pData);
@@ -829,13 +836,13 @@
PB_RETURN_ERROR(stream, "bytes size exceeded");
}
- return pb_encode_string(stream, bytes->bytes, bytes->size);
+ return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size);
}
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field)
{
size_t size = 0;
- size_t max_size = field->data_size;
+ size_t max_size = (size_t)field->data_size;
const char *str = (const char*)field->pData;
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
@@ -890,5 +897,54 @@
static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field)
{
- return pb_encode_string(stream, (const pb_byte_t*)field->pData, field->data_size);
+ return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size);
}
+
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+bool pb_encode_float_as_double(pb_ostream_t *stream, float value)
+{
+ union { float f; uint32_t i; } in;
+ uint8_t sign;
+ int exponent;
+ uint64_t mantissa;
+
+ in.f = value;
+
+ /* Decompose input value */
+ sign = (uint8_t)((in.i >> 31) & 1);
+ exponent = (int)((in.i >> 23) & 0xFF) - 127;
+ mantissa = in.i & 0x7FFFFF;
+
+ if (exponent == 128)
+ {
+ /* Special value (NaN etc.) */
+ exponent = 1024;
+ }
+ else if (exponent == -127)
+ {
+ if (!mantissa)
+ {
+ /* Zero */
+ exponent = -1023;
+ }
+ else
+ {
+ /* Denormalized */
+ mantissa <<= 1;
+ while (!(mantissa & 0x800000))
+ {
+ mantissa <<= 1;
+ exponent--;
+ }
+ mantissa &= 0x7FFFFF;
+ }
+ }
+
+ /* Combine fields */
+ mantissa <<= 29;
+ mantissa |= (uint64_t)(exponent + 1023) << 52;
+ mantissa |= (uint64_t)sign << 63;
+
+ return pb_encode_fixed64(stream, &mantissa);
+}
+#endif
diff --git a/pb_encode.h b/pb_encode.h
index 66f6b2f..88e246a 100644
--- a/pb_encode.h
+++ b/pb_encode.h
@@ -165,6 +165,12 @@
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
#endif
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+/* Encode a float value so that it appears like a double in the encoded
+ * message. */
+bool pb_encode_float_as_double(pb_ostream_t *stream, float value);
+#endif
+
/* Encode a submessage field.
* You need to pass the pb_field_t array and pointer to struct, just like
* with pb_encode(). This internally encodes the submessage twice, first to
diff --git a/tests/SConstruct b/tests/SConstruct
index 0158b1d..18c1b04 100644
--- a/tests/SConstruct
+++ b/tests/SConstruct
@@ -8,6 +8,7 @@
CXX Name of C++ compiler
CCFLAGS Flags to pass to the C compiler
CXXFLAGS Flags to pass to the C++ compiler
+LINKFLAGS Flags to pass to linker
For example, for a clang build, use:
scons CC=clang CXX=clang++
@@ -18,6 +19,8 @@
if ARGUMENTS.get('PLATFORM') == 'STM32':
set_stm32_platform(env)
+elif ARGUMENTS.get('PLATFORM') == 'AVR':
+ set_avr_platform(env)
# Limit memory usage. This is to catch problems like issue #338
try:
@@ -32,6 +35,7 @@
if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
if 'CCFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CCFLAGS'])
if 'CXXFLAGS' in ARGUMENTS: env.Append(CXXFLAGS = ARGUMENTS['CXXFLAGS'])
+if 'LINKFLAGS' in ARGUMENTS: env.Append(LINKFLAGS = ARGUMENTS['LINKFLAGS'])
# Add the builders defined in site_init.py
add_nanopb_builders(env)
@@ -122,9 +126,14 @@
# GNU Compiler Collection
# Debug info, warnings as errors
- env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror ')
+ env.Append(CFLAGS = '-g -Wall -Werror ')
env.Append(CORECFLAGS = '-Wextra')
+ # Pedantic ANSI C. On AVR this doesn't work because we use large
+ # enums in some of the tests.
+ if env.get("EMBEDDED") != "AVR":
+ env.Append(CFLAGS = '-ansi -pedantic')
+
# Profiling and coverage
if not env.get("EMBEDDED"):
env.Append(CFLAGS = '-fprofile-arcs -ftest-coverage ')
@@ -163,7 +172,7 @@
elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
elif 'cl' in env['CXX']:
- env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116')
+ env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116 /wd4127')
if not env.get("EMBEDDED"):
valgrind = env.WhereIs('valgrind')
diff --git a/tests/alltypes/decode_alltypes.c b/tests/alltypes/decode_alltypes.c
index f00e1a7..532694e 100644
--- a/tests/alltypes/decode_alltypes.c
+++ b/tests/alltypes/decode_alltypes.c
@@ -17,7 +17,7 @@
{
int status = 0;
- /* Uses _init_default to just make sure that it works. */
+ /* Uses _init_default to just make sure that the macro works. */
AllTypes alltypes = AllTypes_init_default;
/* Fill with garbage to better detect initialization errors */
@@ -278,6 +278,9 @@
return status == 0;
}
+#ifdef __cplusplus
+extern "C"
+#endif
int main(int argc, char **argv)
{
uint8_t buffer[1024];
diff --git a/tests/alltypes/encode_alltypes.c b/tests/alltypes/encode_alltypes.c
index 15ea7b8..c1c2aa3 100644
--- a/tests/alltypes/encode_alltypes.c
+++ b/tests/alltypes/encode_alltypes.c
@@ -8,6 +8,9 @@
#include "alltypes.pb.h"
#include "test_helpers.h"
+#ifdef __cplusplus
+extern "C"
+#endif
int main(int argc, char **argv)
{
int mode = (argc > 1) ? atoi(argv[1]) : 0;
diff --git a/tests/alltypes_callback/decode_alltypes_callback.c b/tests/alltypes_callback/decode_alltypes_callback.c
index 576ce30..83f4968 100644
--- a/tests/alltypes_callback/decode_alltypes_callback.c
+++ b/tests/alltypes_callback/decode_alltypes_callback.c
@@ -21,7 +21,7 @@
if (!pb_decode_varint(stream, &value))
return false;
- TEST((int64_t)value == (long)*arg);
+ TEST((int64_t)value == (intptr_t)*arg);
return true;
}
@@ -31,7 +31,7 @@
if (!pb_decode_svarint(stream, &value))
return false;
- TEST(value == (long)*arg);
+ TEST(value == (intptr_t)*arg);
return true;
}
@@ -55,6 +55,28 @@
return true;
}
+static bool read_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ {
+ float value;
+ if (!pb_decode_double_as_float(stream, &value))
+ return false;
+
+ TEST(memcmp(&value, *arg, sizeof(float)) == 0);
+ return true;
+ }
+#endif
+
+ uint64_t value;
+ if (!pb_decode_fixed64(stream, &value))
+ return false;
+
+ TEST(value == *(uint64_t*)*arg);
+ return true;
+}
+
static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t buf[16] = {0};
@@ -132,6 +154,30 @@
return true;
}
+static bool read_repeated_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ {
+ float** expectedf = (float**)arg;
+ float value;
+ if (!pb_decode_double_as_float(stream, &value))
+ return false;
+
+ TEST(memcmp(&value, (*expectedf)++, sizeof(float)) == 0);
+ return true;
+ }
+#endif
+
+ uint64_t** expected = (uint64_t**)arg;
+ uint64_t value;
+ if (!pb_decode_fixed64(stream, &value))
+ return false;
+
+ TEST(*(*expected)++ == value);
+ return true;
+}
+
static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t*** expected = (uint8_t***)arg;
@@ -269,7 +315,7 @@
alltypes.req_sfixed64.funcs.decode = &read_fixed64;
alltypes.req_sfixed64.arg = &req_sfixed64;
- alltypes.req_double.funcs.decode = &read_fixed64;
+ alltypes.req_double.funcs.decode = &read_double;
alltypes.req_double.arg = &req_double;
alltypes.req_string.funcs.decode = &read_string;
@@ -323,7 +369,7 @@
alltypes.rep_sfixed64.funcs.decode = &read_repeated_fixed64;
alltypes.rep_sfixed64.arg = rep_sfixed64;
- alltypes.rep_double.funcs.decode = &read_repeated_fixed64;
+ alltypes.rep_double.funcs.decode = &read_repeated_double;
alltypes.rep_double.arg = rep_double;
alltypes.rep_string.funcs.decode = &read_repeated_string;
@@ -384,7 +430,7 @@
alltypes.opt_sfixed64.funcs.decode = &read_fixed64;
alltypes.opt_sfixed64.arg = &opt_sfixed64;
- alltypes.opt_double.funcs.decode = &read_fixed64;
+ alltypes.opt_double.funcs.decode = &read_double;
alltypes.opt_double.arg = &opt_double;
alltypes.opt_string.funcs.decode = &read_string;
diff --git a/tests/alltypes_callback/encode_alltypes_callback.c b/tests/alltypes_callback/encode_alltypes_callback.c
index b206783..5f75256 100644
--- a/tests/alltypes_callback/encode_alltypes_callback.c
+++ b/tests/alltypes_callback/encode_alltypes_callback.c
@@ -13,13 +13,13 @@
static bool write_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
- pb_encode_varint(stream, (long)*arg);
+ pb_encode_varint(stream, (intptr_t)*arg);
}
static bool write_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
- pb_encode_svarint(stream, (long)*arg);
+ pb_encode_svarint(stream, (intptr_t)*arg);
}
static bool write_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -34,6 +34,18 @@
pb_encode_fixed64(stream, *arg);
}
+static bool write_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ return pb_encode_tag_for_field(stream, field) &&
+ pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+ return pb_encode_tag_for_field(stream, field) &&
+ pb_encode_fixed64(stream, *arg);
+}
+
static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
@@ -65,7 +77,7 @@
pb_encode_tag_for_field(stream, field) &&
pb_encode_varint(stream, 0) &&
pb_encode_tag_for_field(stream, field) &&
- pb_encode_varint(stream, (long)*arg);
+ pb_encode_varint(stream, (intptr_t)*arg);
}
static bool write_repeated_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -79,7 +91,7 @@
pb_encode_tag_for_field(stream, field) &&
pb_encode_svarint(stream, 0) &&
pb_encode_tag_for_field(stream, field) &&
- pb_encode_svarint(stream, (long)*arg);
+ pb_encode_svarint(stream, (intptr_t)*arg);
}
static bool write_repeated_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -110,6 +122,31 @@
pb_encode_fixed64(stream, *arg);
}
+static bool write_repeated_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+ uint64_t dummy = 0;
+
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+ pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+ /* Make it a packed field */
+ return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+ pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, *arg);
+}
+
static bool write_repeated_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
@@ -246,7 +283,7 @@
alltypes.req_sfixed64.funcs.encode = &write_fixed64;
alltypes.req_sfixed64.arg = &req_sfixed64;
- alltypes.req_double.funcs.encode = &write_fixed64;
+ alltypes.req_double.funcs.encode = &write_double;
alltypes.req_double.arg = &req_double;
alltypes.req_string.funcs.encode = &write_string;
@@ -303,7 +340,7 @@
alltypes.rep_sfixed64.funcs.encode = &write_repeated_fixed64;
alltypes.rep_sfixed64.arg = &rep_sfixed64;
- alltypes.rep_double.funcs.encode = &write_repeated_fixed64;
+ alltypes.rep_double.funcs.encode = &write_repeated_double;
alltypes.rep_double.arg = &rep_double;
alltypes.rep_string.funcs.encode = &write_repeated_string;
@@ -364,7 +401,7 @@
alltypes.opt_sfixed64.funcs.encode = &write_fixed64;
alltypes.opt_sfixed64.arg = &opt_sfixed64;
- alltypes.opt_double.funcs.encode = &write_fixed64;
+ alltypes.opt_double.funcs.encode = &write_double;
alltypes.opt_double.arg = &opt_double;
alltypes.opt_string.funcs.encode = &write_string;
diff --git a/tests/alltypes_proto3_callback/decode_alltypes_callback.c b/tests/alltypes_proto3_callback/decode_alltypes_callback.c
index 2b3c2f3..ffa40aa 100644
--- a/tests/alltypes_proto3_callback/decode_alltypes_callback.c
+++ b/tests/alltypes_proto3_callback/decode_alltypes_callback.c
@@ -21,7 +21,7 @@
if (!pb_decode_varint(stream, &value))
return false;
- TEST((int64_t)value == (long)*arg);
+ TEST((int64_t)value == (intptr_t)*arg);
return true;
}
@@ -31,7 +31,7 @@
if (!pb_decode_svarint(stream, &value))
return false;
- TEST(value == (long)*arg);
+ TEST(value == (intptr_t)*arg);
return true;
}
@@ -55,6 +55,28 @@
return true;
}
+static bool read_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ {
+ float value;
+ if (!pb_decode_double_as_float(stream, &value))
+ return false;
+
+ TEST(memcmp(&value, *arg, sizeof(float)) == 0);
+ return true;
+ }
+#endif
+
+ uint64_t value;
+ if (!pb_decode_fixed64(stream, &value))
+ return false;
+
+ TEST(value == *(uint64_t*)*arg);
+ return true;
+}
+
static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t buf[16] = {0};
@@ -131,6 +153,30 @@
return true;
}
+static bool read_repeated_double(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ {
+ float** expectedf = (float**)arg;
+ float value;
+ if (!pb_decode_double_as_float(stream, &value))
+ return false;
+
+ TEST(memcmp(&value, (*expectedf)++, sizeof(float)) == 0);
+ return true;
+ }
+#endif
+
+ uint64_t** expected = (uint64_t**)arg;
+ uint64_t value;
+ if (!pb_decode_fixed64(stream, &value))
+ return false;
+
+ TEST(*(*expected)++ == value);
+ return true;
+}
+
static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
uint8_t*** expected = (uint8_t***)arg;
@@ -257,7 +303,7 @@
alltypes.rep_sfixed64.funcs.decode = &read_repeated_fixed64;
alltypes.rep_sfixed64.arg = rep_sfixed64;
- alltypes.rep_double.funcs.decode = &read_repeated_fixed64;
+ alltypes.rep_double.funcs.decode = &read_repeated_double;
alltypes.rep_double.arg = rep_double;
alltypes.rep_string.funcs.decode = &read_repeated_string;
@@ -318,7 +364,7 @@
alltypes.sng_sfixed64.funcs.decode = &read_fixed64;
alltypes.sng_sfixed64.arg = &sng_sfixed64;
- alltypes.sng_double.funcs.decode = &read_fixed64;
+ alltypes.sng_double.funcs.decode = &read_double;
alltypes.sng_double.arg = &sng_double;
alltypes.sng_string.funcs.decode = &read_string;
diff --git a/tests/alltypes_proto3_callback/encode_alltypes_callback.c b/tests/alltypes_proto3_callback/encode_alltypes_callback.c
index 8c7bdd6..6b51aac 100644
--- a/tests/alltypes_proto3_callback/encode_alltypes_callback.c
+++ b/tests/alltypes_proto3_callback/encode_alltypes_callback.c
@@ -13,13 +13,13 @@
static bool write_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
- pb_encode_varint(stream, (long)*arg);
+ pb_encode_varint(stream, (intptr_t)*arg);
}
static bool write_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
- pb_encode_svarint(stream, (long)*arg);
+ pb_encode_svarint(stream, (intptr_t)*arg);
}
static bool write_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -34,6 +34,18 @@
pb_encode_fixed64(stream, *arg);
}
+static bool write_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ return pb_encode_tag_for_field(stream, field) &&
+ pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+ return pb_encode_tag_for_field(stream, field) &&
+ pb_encode_fixed64(stream, *arg);
+}
+
static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
@@ -65,7 +77,7 @@
pb_encode_tag_for_field(stream, field) &&
pb_encode_varint(stream, 0) &&
pb_encode_tag_for_field(stream, field) &&
- pb_encode_varint(stream, (long)*arg);
+ pb_encode_varint(stream, (intptr_t)*arg);
}
static bool write_repeated_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -79,7 +91,7 @@
pb_encode_tag_for_field(stream, field) &&
pb_encode_svarint(stream, 0) &&
pb_encode_tag_for_field(stream, field) &&
- pb_encode_svarint(stream, (long)*arg);
+ pb_encode_svarint(stream, (intptr_t)*arg);
}
static bool write_repeated_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
@@ -110,6 +122,31 @@
pb_encode_fixed64(stream, *arg);
}
+static bool write_repeated_double(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
+{
+ uint64_t dummy = 0;
+
+#ifdef PB_CONVERT_DOUBLE_FLOAT
+ if (sizeof(double) == sizeof(float))
+ return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+ pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, 0.0f) &&
+ pb_encode_float_as_double(stream, *(float*)*arg);
+#endif
+
+ /* Make it a packed field */
+ return pb_encode_tag(stream, PB_WT_STRING, field->tag) &&
+ pb_encode_varint(stream, 5 * 8) && /* Number of bytes */
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, &dummy) &&
+ pb_encode_fixed64(stream, *arg);
+}
+
static bool write_repeated_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
return pb_encode_tag_for_field(stream, field) &&
@@ -235,7 +272,7 @@
alltypes.rep_sfixed64.funcs.encode = &write_repeated_fixed64;
alltypes.rep_sfixed64.arg = &rep_sfixed64;
- alltypes.rep_double.funcs.encode = &write_repeated_fixed64;
+ alltypes.rep_double.funcs.encode = &write_repeated_double;
alltypes.rep_double.arg = &rep_double;
alltypes.rep_string.funcs.encode = &write_repeated_string;
@@ -296,7 +333,7 @@
alltypes.sng_sfixed64.funcs.encode = &write_fixed64;
alltypes.sng_sfixed64.arg = &sng_sfixed64;
- alltypes.sng_double.funcs.encode = &write_fixed64;
+ alltypes.sng_double.funcs.encode = &write_double;
alltypes.sng_double.arg = &sng_double;
alltypes.sng_string.funcs.encode = &write_string;
diff --git a/tests/backwards_compatibility/alltypes_legacy.c b/tests/backwards_compatibility/alltypes_legacy.c
index 1c5d75d..53d0239 100644
--- a/tests/backwards_compatibility/alltypes_legacy.c
+++ b/tests/backwards_compatibility/alltypes_legacy.c
@@ -28,12 +28,13 @@
-
+#ifndef PB_CONVERT_DOUBLE_FLOAT
/* On some platforms (such as AVR), double is really float.
* Using double on these platforms is not directly supported
* by nanopb, but see example_avr_double.
* To get rid of this error, remove any double fields from your .proto.
*/
PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)
+#endif
/* @@protoc_insertion_point(eof) */
diff --git a/tests/common/unittests.h b/tests/common/unittests.h
index c2b470a..6724fed 100644
--- a/tests/common/unittests.h
+++ b/tests/common/unittests.h
@@ -1,5 +1,19 @@
#include <stdio.h>
+#ifdef UNITTESTS_SHORT_MSGS
+/* Short debug messages for platforms with limited memory */
+#define COMMENT(x) printf("\n----" x "----\n");
+#define TEST(x) \
+ if (!(x)) { \
+ fprintf(stderr, "FAIL: Line %d\n", __LINE__); \
+ status = 1; \
+ } else { \
+ printf("OK: Line %d\n", __LINE__); \
+ }
+
+#else
+
+/* Elaborate debug messages for normal development */
#define COMMENT(x) printf("\n----" x "----\n");
#define STR(x) #x
#define STR2(x) STR(x)
@@ -10,5 +24,5 @@
} else { \
printf("\033[32;1mOK:\033[22;39m " #x "\n"); \
}
-
+#endif
diff --git a/tests/cxx_descriptor/message_descriptor.cc b/tests/cxx_descriptor/message_descriptor.cc
index cfa6848..4437301 100644
--- a/tests/cxx_descriptor/message_descriptor.cc
+++ b/tests/cxx_descriptor/message_descriptor.cc
@@ -2,7 +2,7 @@
#include "message.pb.h"
#include "unittests.h"
-int main() {
+extern "C" int main() {
using namespace nanopb;
#if __cplusplus >= 201103L
diff --git a/tests/encode_unittests/encode_unittests.c b/tests/encode_unittests/encode_unittests.c
index 06188c9..58bd142 100644
--- a/tests/encode_unittests/encode_unittests.c
+++ b/tests/encode_unittests/encode_unittests.c
@@ -214,6 +214,7 @@
pb_field_iter_t field;
COMMENT("Test pb_enc_fixed using float")
+ field.type = PB_LTYPE_FIXED32;
field.data_size = sizeof(fvalue);
field.pData = &fvalue;
fvalue = 0.0f;
@@ -224,6 +225,7 @@
TEST(WRITES(pb_enc_fixed(&s, &field), "\x4e\x61\x3c\xcb"))
COMMENT("Test pb_enc_fixed using double")
+ field.type = PB_LTYPE_FIXED64;
field.data_size = sizeof(dvalue);
field.pData = &dvalue;
dvalue = 0.0;
diff --git a/tests/float_double_conversion/SConscript b/tests/float_double_conversion/SConscript
new file mode 100644
index 0000000..21c9a81
--- /dev/null
+++ b/tests/float_double_conversion/SConscript
@@ -0,0 +1,21 @@
+Import("env")
+
+env.NanopbProto("doublemsg")
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_CONVERT_DOUBLE_FLOAT': 1})
+
+if opts.get('EMBEDDED') == 'AVR':
+ opts.Append(CFLAGS = "-Wno-overflow")
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_fldbl.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_fldbl.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_fldbl.o", "$NANOPB/pb_common.c")
+
+# Build and run test
+test = opts.Program(["float_double_conversion.c", "doublemsg.pb.c", "pb_encode_fldbl.o", "pb_decode_fldbl.o", "pb_common_fldbl.o"])
+env.RunTest(test)
diff --git a/tests/float_double_conversion/doublemsg.proto b/tests/float_double_conversion/doublemsg.proto
new file mode 100644
index 0000000..d022692
--- /dev/null
+++ b/tests/float_double_conversion/doublemsg.proto
@@ -0,0 +1,5 @@
+syntax = "proto2";
+
+message DoubleMsg {
+ required double value = 1;
+}
diff --git a/tests/float_double_conversion/float_double_conversion.c b/tests/float_double_conversion/float_double_conversion.c
new file mode 100644
index 0000000..70a3664
--- /dev/null
+++ b/tests/float_double_conversion/float_double_conversion.c
@@ -0,0 +1,81 @@
+#define _USE_MATH_DEFINES 1
+#undef __STRICT_ANSI__
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <pb_decode.h>
+#include <pb_encode.h>
+#include "doublemsg.pb.h"
+#include "unittests.h"
+
+/* This message mimics how DoubleMsg would appear on e.g. AVR. */
+typedef struct {
+ float value;
+} FloatMsg;
+PB_BIND(DoubleMsg, FloatMsg, AUTO)
+
+static const double testvalues[] = {
+ 0.0, -0.0, 0.1, -0.1,
+ M_PI, -M_PI, 123456.789, -123456.789,
+#if defined(NAN) && defined(INFINITY)
+ INFINITY, -INFINITY, NAN, INFINITY - INFINITY,
+#endif
+ 1e38, -1e38, 1e39, -1e39,
+ 1e-38, -1e-38, 1e-39, -1e-39,
+ 3.14159e-37,-3.14159e-37, 3.14159e-43, -3.14159e-43,
+ 1e-60, -1e-60, 1e-45, -1e-45,
+ 0.99999999999999, -0.99999999999999, 127.999999999999, -127.999999999999
+};
+
+#define TESTVALUES_COUNT (sizeof(testvalues)/sizeof(testvalues[0]))
+
+int main()
+{
+ uint8_t buf[16];
+ size_t msglen;
+ int status = 0;
+ int i;
+ for (i = 0; i < TESTVALUES_COUNT; i++)
+ {
+ double orig_double = testvalues[i];
+ float expected_float = (float)orig_double;
+ double expected_double = (double)expected_float;
+
+ printf("\n---- Testcase: %f ----\n", expected_float);
+
+ {
+ /* Encode the original double */
+ pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf));
+ DoubleMsg msg = { 0.0 };
+ msg.value = orig_double;
+ TEST(pb_encode(&stream, &DoubleMsg_msg, &msg));
+ msglen = stream.bytes_written;
+ TEST(msglen == 9);
+ }
+
+ {
+ /* Decode as float */
+ pb_ostream_t ostream;
+ pb_istream_t stream = pb_istream_from_buffer(buf, msglen);
+ FloatMsg msg = { 0.0f };
+ TEST(pb_decode(&stream, &FloatMsg_msg, &msg));
+ TEST(memcmp(&msg.value, &expected_float, sizeof(float)) == 0);
+
+ /* Re-encode */
+ ostream = pb_ostream_from_buffer(buf, sizeof(buf));
+ TEST(pb_encode(&ostream, &FloatMsg_msg, &msg));
+ msglen = ostream.bytes_written;
+ TEST(msglen == 9);
+ }
+
+ {
+ /* Decode as double */
+ pb_istream_t stream = pb_istream_from_buffer(buf, msglen);
+ DoubleMsg msg = { 0.0 };
+ TEST(pb_decode(&stream, &DoubleMsg_msg, &msg));
+ TEST(memcmp(&msg.value, &expected_double, sizeof(double)) == 0);
+ }
+ }
+
+ return status;
+}
diff --git a/tests/fuzztest/SConscript b/tests/fuzztest/SConscript
index 52033e1..7723d99 100644
--- a/tests/fuzztest/SConscript
+++ b/tests/fuzztest/SConscript
@@ -36,14 +36,6 @@
iterations = 10000
env.RunTest(fuzz, ARGS = [str(seed), str(iterations)])
-fuzzstub = malloc_env.Program(["fuzzstub.c",
- "alltypes_pointer.pb.c",
- "alltypes_static.pb.c",
- "$COMMON/pb_encode_with_malloc.o",
- "$COMMON/pb_decode_with_malloc.o",
- "$COMMON/pb_common_with_malloc.o",
- "$COMMON/malloc_wrappers.o"])
-
generate_message = malloc_env.Program(["generate_message.c",
"alltypes_static.pb.c",
"$COMMON/pb_encode.o",
diff --git a/tests/fuzztest/fuzzstub.c b/tests/fuzztest/fuzzstub.c
deleted file mode 100644
index ec9e2af..0000000
--- a/tests/fuzztest/fuzzstub.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* Fuzz testing for the nanopb core.
- * This can be used with external fuzzers, e.g. radamsa.
- * It performs most of the same checks as fuzztest, but does not feature data generation.
- */
-
-#include <pb_decode.h>
-#include <pb_encode.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <time.h>
-#include <malloc_wrappers.h>
-#include "alltypes_static.pb.h"
-#include "alltypes_pointer.pb.h"
-
-#define BUFSIZE 4096
-
-static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success)
-{
- pb_istream_t stream;
- bool status;
-
- alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes));
- stream = pb_istream_from_buffer(buffer, msglen);
- status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg);
-
- if (!status && assert_success)
- {
- /* Anything that was successfully encoded, should be decodeable.
- * One exception: strings without null terminator are encoded up
- * to end of buffer, but refused on decode because the terminator
- * would not fit. */
- if (strcmp(stream.errmsg, "string overflow") != 0)
- assert(status);
- }
-
- free_with_check(msg);
- return status;
-}
-
-static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success)
-{
- pb_istream_t stream;
- bool status;
- alltypes_pointer_AllTypes *msg;
-
- msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
- memset(msg, 0, sizeof(alltypes_pointer_AllTypes));
- stream = pb_istream_from_buffer(buffer, msglen);
-
- assert(get_alloc_count() == 0);
- status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg);
-
- if (assert_success)
- assert(status);
-
- pb_release(alltypes_pointer_AllTypes_fields, msg);
- assert(get_alloc_count() == 0);
-
- free_with_check(msg);
-
- return status;
-}
-
-/* Do a decode -> encode -> decode -> encode roundtrip */
-static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
-{
- bool status;
- uint8_t *buf2 = malloc_with_check(BUFSIZE);
- uint8_t *buf3 = malloc_with_check(BUFSIZE);
- size_t msglen2, msglen3;
- alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes));
- alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes));
- memset(msg1, 0, sizeof(alltypes_static_AllTypes));
- memset(msg2, 0, sizeof(alltypes_static_AllTypes));
-
- {
- pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
- status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1);
- assert(status);
- }
-
- {
- pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
- status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1);
- assert(status);
- msglen2 = stream.bytes_written;
- }
-
- {
- pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
- status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2);
- assert(status);
- }
-
- {
- pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
- status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2);
- assert(status);
- msglen3 = stream.bytes_written;
- }
-
- assert(msglen2 == msglen3);
- assert(memcmp(buf2, buf3, msglen2) == 0);
-
- free_with_check(msg1);
- free_with_check(msg2);
- free_with_check(buf2);
- free_with_check(buf3);
-}
-
-/* Do decode -> encode -> decode -> encode roundtrip */
-static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen)
-{
- bool status;
- uint8_t *buf2 = malloc_with_check(BUFSIZE);
- uint8_t *buf3 = malloc_with_check(BUFSIZE);
- size_t msglen2, msglen3;
- alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
- alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes));
- memset(msg1, 0, sizeof(alltypes_pointer_AllTypes));
- memset(msg2, 0, sizeof(alltypes_pointer_AllTypes));
-
- {
- pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
- status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1);
- assert(status);
- }
-
- {
- pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE);
- status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1);
- assert(status);
- msglen2 = stream.bytes_written;
- }
-
- {
- pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
- status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2);
- assert(status);
- }
-
- {
- pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE);
- status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2);
- assert(status);
- msglen3 = stream.bytes_written;
- }
-
- assert(msglen2 == msglen3);
- assert(memcmp(buf2, buf3, msglen2) == 0);
-
- pb_release(alltypes_pointer_AllTypes_fields, msg1);
- pb_release(alltypes_pointer_AllTypes_fields, msg2);
- free_with_check(msg1);
- free_with_check(msg2);
- free_with_check(buf2);
- free_with_check(buf3);
-}
-
-static void run_iteration()
-{
- uint8_t *buffer = malloc_with_check(BUFSIZE);
- size_t msglen;
- bool status;
-
- msglen = fread(buffer, 1, BUFSIZE, stdin);
-
- status = do_static_decode(buffer, msglen, false);
-
- if (status)
- do_static_roundtrip(buffer, msglen);
-
- status = do_pointer_decode(buffer, msglen, false);
-
- if (status)
- do_pointer_roundtrip(buffer, msglen);
-
- free_with_check(buffer);
-}
-
-int main(int argc, char **argv)
-{
- run_iteration();
-
- return 0;
-}
-
diff --git a/tests/fuzztest/fuzztest.c b/tests/fuzztest/fuzztest.c
index 7e9ddb7..93065ae 100644
--- a/tests/fuzztest/fuzztest.c
+++ b/tests/fuzztest/fuzztest.c
@@ -8,11 +8,17 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
-#include <time.h>
#include <malloc_wrappers.h>
+#include "test_helpers.h"
#include "alltypes_static.pb.h"
#include "alltypes_pointer.pb.h"
+#ifdef __AVR__
+#define BUFSIZE 2048
+#else
+#define BUFSIZE 4096
+#endif
+
static uint64_t random_seed;
/* Uses xorshift64 here instead of rand() for both speed and
@@ -198,8 +204,6 @@
}
}
-#define BUFSIZE 4096
-
static bool do_static_encode(uint8_t *buffer, size_t *msglen)
{
pb_ostream_t stream;
@@ -444,19 +448,49 @@
assert(get_alloc_count() == 0);
}
+static void run_stub()
+{
+ uint8_t *buffer = malloc_with_check(BUFSIZE);
+ size_t msglen;
+ bool status;
+
+ SET_BINARY_MODE(stdin);
+ msglen = fread(buffer, 1, BUFSIZE, stdin);
+
+ status = do_static_decode(buffer, msglen, false);
+
+ if (status)
+ do_static_roundtrip(buffer, msglen);
+
+ status = do_pointer_decode(buffer, msglen, false);
+
+ if (status)
+ do_pointer_roundtrip(buffer, msglen);
+
+ free_with_check(buffer);
+}
+
int main(int argc, char **argv)
{
int i;
int iterations;
- random_seed = atol(argv[1]);
- iterations = atol(argv[2]);
- if (iterations == 0) iterations = 10000;
- fprintf(stderr, "Random seed: %u, iterations: %d\n", (unsigned)random_seed, iterations);
-
- for (i = 0; i < iterations; i++)
+ if (argc >= 2)
{
- run_iteration();
+ /* Run in stand-alone mode */
+ random_seed = atol(argv[1]);
+ iterations = (argc >= 3) ? atol(argv[2]) : 10000;
+ fprintf(stderr, "Random seed: %u, iterations: %d\n", (unsigned)random_seed, iterations);
+
+ for (i = 0; i < iterations; i++)
+ {
+ run_iteration();
+ }
+ }
+ else
+ {
+ /* Run as a stub for afl-fuzz and similar */
+ run_stub();
}
return 0;
diff --git a/tests/fuzztest/generate_message.c b/tests/fuzztest/generate_message.c
index 73959fe..b77068a 100644
--- a/tests/fuzztest/generate_message.c
+++ b/tests/fuzztest/generate_message.c
@@ -66,7 +66,7 @@
static void generate_message()
{
alltypes_static_AllTypes msg;
- uint8_t buf[8192];
+ uint8_t buf[4096];
pb_ostream_t stream = {0};
do {
@@ -83,16 +83,15 @@
int main(int argc, char **argv)
{
- if (argc > 1)
+ if (argc < 2)
{
- random_seed = atol(argv[1]);
+ fprintf(stderr, "Usage: generate_message <seed>\n");
+ return 1;
}
- else
- {
- random_seed = time(NULL);
- }
-
- fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed);
+
+ random_seed = atol(argv[1]);
+
+ fprintf(stderr, "Random seed: %u\n", (unsigned)random_seed);
generate_message();
diff --git a/tests/fuzztest/run_radamsa.sh b/tests/fuzztest/run_radamsa.sh
deleted file mode 100755
index 52cd40a..0000000
--- a/tests/fuzztest/run_radamsa.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-TMP=`tempfile`
-
-echo $TMP
-while true
-do
- radamsa sample_data/* > $TMP
- $1 < $TMP
- test $? -gt 127 && break
-done
-
diff --git a/tests/msgid/SConscript b/tests/msgid/SConscript
new file mode 100644
index 0000000..549a569
--- /dev/null
+++ b/tests/msgid/SConscript
@@ -0,0 +1,22 @@
+# Test the 'msgid' feature for identifying encoded messages
+
+Import('env')
+
+env.NanopbProto('msgid_example')
+
+enc = env.Program(['encode_msgid.c',
+ 'msgid_example.pb.c',
+ '$COMMON/pb_encode.o',
+ '$COMMON/pb_common.o'])
+
+dec = env.Program(['decode_msgid.c',
+ 'msgid_example.pb.c',
+ '$COMMON/pb_decode.o',
+ '$COMMON/pb_common.o'])
+
+env.RunTest("message1.pb", enc, ARGS = ['1'])
+env.RunTest("message1.txt", [dec, 'message1.pb'])
+env.RunTest("message2.pb", enc, ARGS = ['2'])
+env.RunTest("message2.txt", [dec, 'message2.pb'])
+env.RunTest("message3.pb", enc, ARGS = ['3'])
+env.RunTest("message3.txt", [dec, 'message3.pb'])
diff --git a/tests/msgid/decode_msgid.c b/tests/msgid/decode_msgid.c
new file mode 100644
index 0000000..2623f8c
--- /dev/null
+++ b/tests/msgid/decode_msgid.c
@@ -0,0 +1,105 @@
+/* Decode a message using msgid prefix to identify message type */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pb_decode.h>
+#include "msgid_example.pb.h"
+#include "test_helpers.h"
+
+/* This function reads the prefix written by sending side. */
+bool read_prefix(pb_istream_t *stream, int *msgid)
+{
+ uint8_t prefix = 0;
+
+ if (!pb_read(stream, &prefix, 1))
+ return false;
+
+ *msgid = prefix;
+ return true;
+}
+
+/* Main function will call one of these functions based on the prefix */
+
+bool handle_MyMessage1(pb_istream_t *stream)
+{
+ MyMessage1 msg = MyMessage1_init_default;
+
+ if (!pb_decode(stream, MyMessage1_fields, &msg))
+ return false;
+
+ printf("Got MyMessage1: intvalue = %d\n", (int)msg.intvalue);
+ return true;
+}
+
+bool handle_MyMessage2(pb_istream_t *stream)
+{
+ MyMessage2 msg = MyMessage2_init_default;
+
+ if (!pb_decode(stream, MyMessage2_fields, &msg))
+ return false;
+
+ printf("Got MyMessage2: intvalue = %d, strvalue = %s\n",
+ (int)msg.intvalue, msg.strvalue);
+ return true;
+}
+
+bool handle_MyMessage3(pb_istream_t *stream)
+{
+ MyMessage3 msg = MyMessage3_init_default;
+
+ if (!pb_decode(stream, MyMessage3_fields, &msg))
+ return false;
+
+ printf("Got MyMessage3: boolvalue = %d\n", (int)msg.boolvalue);
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ uint8_t buffer[128];
+ pb_istream_t stream;
+ size_t count;
+ bool status = false;
+ int prefix;
+
+ /* Read the data into buffer */
+ SET_BINARY_MODE(stdin);
+ count = fread(buffer, 1, sizeof(buffer), stdin);
+
+ if (!feof(stdin))
+ {
+ printf("Message does not fit in buffer\n");
+ return 1;
+ }
+
+ stream = pb_istream_from_buffer(buffer, count);
+
+ if (!read_prefix(&stream, &prefix))
+ {
+ printf("Failed to read prefix: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+
+ /* Call message handler based on prefix.
+ * We could write the switch cases manually, comparing against
+ * the MyMessageX_msgid defines. However, this uses the automatically
+ * generated X-macro construct to make the switch case.
+ */
+
+ switch (prefix)
+ {
+ #define PB_MSG(id,len,name) case id: status = handle_ ## name(&stream); break;
+ MSGID_EXAMPLE_MESSAGES
+ #undef PB_MSG
+
+ default: printf("Unknown prefix: %d\n", prefix); return 1;
+ }
+
+ if (!status)
+ {
+ printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/tests/msgid/encode_msgid.c b/tests/msgid/encode_msgid.c
new file mode 100644
index 0000000..4fa1279
--- /dev/null
+++ b/tests/msgid/encode_msgid.c
@@ -0,0 +1,90 @@
+/* Encode a message using msgid field as prefix */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "msgid_example.pb.h"
+#include "test_helpers.h"
+
+/* This function writes the message id as a prefix to the message, allowing
+ * the receiving side to identify message type. Here we use uint8_t to store
+ * it, but e.g. varint or some custom header struct would work just as well.
+ */
+bool write_prefix(pb_ostream_t *stream, int msgid)
+{
+ uint8_t prefix = msgid;
+ return pb_write(stream, &prefix, 1);
+}
+
+/* The main logic will call one of these functions.
+ * Normally which function you call would be selected based on what message
+ * you want to send, here it is decided based on command line parameter.
+ */
+
+bool encode_MyMessage1(pb_ostream_t *stream)
+{
+ MyMessage1 msg = MyMessage1_init_default;
+ msg.intvalue = 1234;
+ return write_prefix(stream, MyMessage1_msgid)
+ && pb_encode(stream, MyMessage1_fields, &msg);
+}
+
+bool encode_MyMessage2(pb_ostream_t *stream)
+{
+ MyMessage2 msg = MyMessage2_init_default;
+ msg.intvalue = 9999;
+ strcpy(msg.strvalue, "Msg2");
+ return write_prefix(stream, MyMessage2_msgid)
+ && pb_encode(stream, MyMessage2_fields, &msg);
+}
+
+bool encode_MyMessage3(pb_ostream_t *stream)
+{
+ MyMessage3 msg = MyMessage3_init_default;
+ msg.boolvalue = true;
+ return write_prefix(stream, MyMessage3_msgid)
+ && pb_encode(stream, MyMessage3_fields, &msg);
+}
+
+int main(int argc, char **argv)
+{
+ uint8_t buffer[128];
+ pb_ostream_t stream;
+ bool status = false;
+ int option;
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "Usage: encode_msgid [number]\n");
+ return 1;
+ }
+ option = atoi(argv[1]);
+
+ stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+ if (option == 1)
+ {
+ status = encode_MyMessage1(&stream);
+ }
+ else if (option == 2)
+ {
+ status = encode_MyMessage2(&stream);
+ }
+ else if (option == 3)
+ {
+ status = encode_MyMessage3(&stream);
+ }
+
+ if (status)
+ {
+ SET_BINARY_MODE(stdout);
+ fwrite(buffer, 1, stream.bytes_written, stdout);
+ return 0;
+ }
+ else
+ {
+ fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream));
+ return 1;
+ }
+}
diff --git a/tests/msgid/msgid_example.proto b/tests/msgid/msgid_example.proto
new file mode 100644
index 0000000..137292f
--- /dev/null
+++ b/tests/msgid/msgid_example.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+import "nanopb.proto";
+
+message MyMessage1
+{
+ option (nanopb_msgopt).msgid = 1;
+
+ int32 intvalue = 1;
+}
+
+message MyMessage2
+{
+ option (nanopb_msgopt).msgid = 2;
+
+ int32 intvalue = 1;
+ string strvalue = 2 [(nanopb).max_size = 16];
+}
+
+message MyMessage3
+{
+ option (nanopb_msgopt).msgid = 3;
+
+ bool boolvalue = 1;
+}
diff --git a/tests/site_scons/platforms/avr/__init__.py b/tests/site_scons/platforms/avr/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/site_scons/platforms/avr/__init__.py
diff --git a/tests/site_scons/platforms/avr/avr.py b/tests/site_scons/platforms/avr/avr.py
new file mode 100644
index 0000000..83d3249
--- /dev/null
+++ b/tests/site_scons/platforms/avr/avr.py
@@ -0,0 +1,23 @@
+# Compiler settings for running the tests on a simulated atmega1284.
+
+def set_avr_platform(env):
+ native = env.Clone()
+ native.Append(LIBS = ["simavr", "libelf"], CFLAGS = "-Wall -Werror -g")
+ runner = native.Program("build/run_test", "site_scons/platforms/avr/run_test.c")
+
+ env.Replace(EMBEDDED = "AVR")
+ env.Replace(CC = "avr-gcc",
+ CXX = "avr-g++",
+ LD = "avr-gcc")
+ env.Replace(TEST_RUNNER = "build/run_test")
+ env.Append(CFLAGS = "-mmcu=atmega1284 -Dmain=app_main -Os")
+ env.Append(CXXFLAGS = "-mmcu=atmega1284 -Dmain=app_main -Os")
+ env.Append(CPPDEFINES = {'PB_CONVERT_DOUBLE_FLOAT': 1, 'UNITTESTS_SHORT_MSGS': 1})
+ env.Append(LINKFLAGS = "-mmcu=atmega1284")
+ env.Append(LINKFLAGS = "build/avr_io.o -Wl,-Map,avr.map")
+ avr_io = env.Object("build/avr_io.o", "site_scons/platforms/avr/avr_io.c")
+
+ # These fake defines just ensure that the needed platform files get built.
+ env.Depends("build/common/pb_common.o", runner)
+ env.Depends("build/common/pb_common.o", avr_io)
+
diff --git a/tests/site_scons/platforms/avr/avr_io.c b/tests/site_scons/platforms/avr/avr_io.c
new file mode 100644
index 0000000..eff00da
--- /dev/null
+++ b/tests/site_scons/platforms/avr/avr_io.c
@@ -0,0 +1,58 @@
+/* This wrapper file initializes stdio to UART connection and
+ * receives the list of command line arguments.
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#undef main
+extern int app_main(int argc, const char **argv);
+
+struct {
+ uint8_t argc;
+ char args[3][16];
+} g_args;
+
+int uart_putchar(char c, FILE *stream)
+{
+ loop_until_bit_is_set(UCSR0A, UDRE0);
+ UDR0 = c;
+ return 0;
+}
+
+int uart_getchar(FILE *stream)
+{
+ while (bit_is_clear(UCSR0A, RXC0) && bit_is_clear(UCSR0A, FE0));
+ if (UCSR0A & _BV(FE0)) return _FDEV_EOF; /* Break = EOF */
+ return UDR0;
+}
+
+FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
+
+int main(void)
+{
+ const char *argv[4] = {"main", g_args.args[0], g_args.args[1], g_args.args[2]};
+ int status;
+
+ UBRR0 = (8000000 / (16UL * 9600)) - 1; /* 9600 bps with default 8 MHz clock */
+ UCSR0B = _BV(TXEN0) | _BV(RXEN0);
+
+ stdout = stdin = stderr = &uart_str;
+
+ fread((char*)&g_args, 1, sizeof(g_args), stdin);
+
+ status = app_main(g_args.argc + 1, argv);
+
+ DDRB = 3;
+ if (status)
+ PORTB = 1;
+ else
+ PORTB = 2;
+
+ cli();
+ sleep_mode();
+ return status;
+}
+
diff --git a/tests/site_scons/platforms/avr/run_test.c b/tests/site_scons/platforms/avr/run_test.c
new file mode 100644
index 0000000..35cfa55
--- /dev/null
+++ b/tests/site_scons/platforms/avr/run_test.c
@@ -0,0 +1,195 @@
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+#include <simavr/sim_avr.h>
+#include <simavr/sim_gdb.h>
+#include <simavr/avr_ioport.h>
+#include <simavr/sim_elf.h>
+#include <simavr/avr_uart.h>
+
+static avr_t *g_avr;
+static avr_irq_t *g_uart_irq;
+
+static struct {
+ uint8_t argc;
+ char args[3][16];
+} g_args;
+static int g_args_idx;
+static bool g_uart_xon;
+static bool g_status_ok;
+
+static void uart_tx_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ fputc(value, stdout);
+ fflush(stdout);
+}
+
+static bool stdin_can_read()
+{
+ struct timeval tv;
+ fd_set fds;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
+ return (FD_ISSET(0, &fds));
+}
+
+static void avr_logger(avr_t * avr, const int level, const char * format, va_list ap)
+{
+ if ((!avr && level <= LOG_WARNING) || (avr && avr->log >= level)) {
+ vfprintf(stderr , format, ap);
+ }
+}
+
+static void uart_xon_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ g_uart_xon = true;
+ int v;
+
+ if (feof(stdin))
+ {
+ avr_raise_irq(&g_uart_irq[1], UART_INPUT_FE);
+ return;
+ }
+
+ while (g_uart_xon)
+ {
+ if (g_args_idx < sizeof(g_args))
+ {
+ v = ((char*)&g_args)[g_args_idx++];
+ }
+ else if (stdin_can_read())
+ {
+ v = fgetc(stdin);
+ }
+ else
+ {
+ break;
+ }
+
+ if (v != EOF)
+ {
+ avr_raise_irq(&g_uart_irq[1], v);
+ }
+ else
+ {
+ avr_raise_irq(&g_uart_irq[1], UART_INPUT_FE);
+ break;
+ }
+ }
+}
+
+static void uart_xoff_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ g_uart_xon = false;
+}
+
+void init_uart()
+{
+ const char *irq_names[2] = {"8<uart_in", "8>uart_out"};
+ g_uart_irq = avr_alloc_irq(&g_avr->irq_pool, 0, 2, irq_names);
+ avr_irq_register_notify(&g_uart_irq[0], &uart_tx_hook, NULL);
+
+ uint32_t flags = 0;
+ avr_ioctl(g_avr, AVR_IOCTL_UART_GET_FLAGS('0'), &flags);
+ flags &= ~AVR_UART_FLAG_STDIO;
+ avr_ioctl(g_avr, AVR_IOCTL_UART_SET_FLAGS('0'), &flags);
+
+ avr_irq_t *src = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUTPUT);
+ avr_irq_t *dst = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_INPUT);
+ avr_connect_irq(src, &g_uart_irq[0]);
+ avr_connect_irq(&g_uart_irq[1], dst);
+
+ avr_irq_t *xon = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUT_XON);
+ avr_irq_t *xoff = avr_io_getirq(g_avr, AVR_IOCTL_UART_GETIRQ('0'), UART_IRQ_OUT_XOFF);
+ avr_irq_register_notify(xon, uart_xon_hook, NULL);
+ avr_irq_register_notify(xoff, uart_xoff_hook, NULL);
+}
+
+static void status_ok_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+ g_status_ok = value;
+}
+
+int main(int argc, char *argv[])
+{
+ avr_global_logger_set(&avr_logger);
+ g_avr = avr_make_mcu_by_name("atmega1284");
+ if (!g_avr)
+ {
+ fprintf(stderr, "avr_make_mcu_by_name failed\n");
+ return 1;
+ }
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "Usage: %s [-g] binary [args ...]\n", argv[0]);
+ return 2;
+ }
+
+ const char *filename = argv[1];
+ bool enable_gdb = false;
+ int argc_offset = 2;
+
+ if (strcmp(filename, "-g") == 0)
+ {
+ enable_gdb = true;
+ argc_offset = 3;
+ filename = argv[2];
+ }
+
+ elf_firmware_t firmware;
+ elf_read_firmware(filename, &firmware);
+ avr_init(g_avr);
+ avr_load_firmware(g_avr, &firmware);
+ g_avr->frequency = 8000000;
+
+ if (enable_gdb)
+ {
+ g_avr->state = cpu_Stopped;
+ g_avr->gdb_port = 1234;
+ avr_gdb_init(g_avr);
+ }
+
+ init_uart();
+
+ avr_irq_register_notify(avr_io_getirq(g_avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 1), status_ok_hook, NULL);
+
+ // Pass the rest of arguments to application inside simulator
+ g_args.argc = argc - argc_offset;
+ if (g_args.argc > 3) g_args.argc = 3;
+ for (int i = 0; i < g_args.argc; i++)
+ {
+ strncpy(g_args.args[i], argv[i + argc_offset], 15);
+ }
+
+ while (1)
+ {
+ int state = avr_run(g_avr);
+ if (state == cpu_Done)
+ break;
+
+ if (state == cpu_Crashed)
+ {
+ fprintf(stderr, "CPU Crashed\n");
+ return 3;
+ }
+ }
+
+ if (g_status_ok)
+ {
+ return 0;
+ }
+ else
+ {
+ fprintf(stderr, "Received error status from simulation\n");
+ return 5;
+ }
+}
diff --git a/tests/site_scons/platforms/stm32/stm32.py b/tests/site_scons/platforms/stm32/stm32.py
index 185dd29..2f2e956 100644
--- a/tests/site_scons/platforms/stm32/stm32.py
+++ b/tests/site_scons/platforms/stm32/stm32.py
@@ -4,13 +4,13 @@
# code is run from RAM also.
def set_stm32_platform(env):
- env.Replace(EMBEDDED = "1")
+ env.Replace(EMBEDDED = "STM32")
env.Replace(CC = "arm-none-eabi-gcc",
CXX = "arm-none-eabi-g++",
LD = "arm-none-eabi-gcc")
env.Replace(TEST_RUNNER = "site_scons/platforms/stm32/run_test.sh")
- env.Append(CFLAGS = "-mcpu=cortex-m3 -mthumb")
- env.Append(CXXFLAGS = "-mcpu=cortex-m3 -mthumb")
+ env.Append(CFLAGS = "-mcpu=cortex-m3 -mthumb -Os")
+ env.Append(CXXFLAGS = "-mcpu=cortex-m3 -mthumb -Os")
env.Append(LINKFLAGS = "-mcpu=cortex-m3 -mthumb")
env.Append(LINKFLAGS = "site_scons/platforms/stm32/vectors.c")
env.Append(LINKFLAGS = "--specs=rdimon.specs --specs=nano.specs")
diff --git a/tests/site_scons/site_init.py b/tests/site_scons/site_init.py
index cbdf92a..0e485ac 100644
--- a/tests/site_scons/site_init.py
+++ b/tests/site_scons/site_init.py
@@ -2,6 +2,7 @@
import sys
import re
from platforms.stm32.stm32 import set_stm32_platform
+from platforms.avr.avr import set_avr_platform
try:
# Make terminal colors work on windows