Compare commits

...

36 Commits

Author SHA1 Message Date
Petteri Aimonen
b7add1e577 Fix crash in pb_release() if called twice on same message.
There was a double-free bug in pb_release() because it didn't set size fields
to zero after deallocation. Most commonly this happens if pb_decode() fails,
internally calls pb_release() and then application code also calls pb_release().
2014-09-11 18:16:01 +03:00
Petteri Aimonen
2f05a35b5f Publishing nanopb-0.2.9 2014-08-09 22:01:04 +03:00
Petteri Aimonen
4f76e64929 Update changelog 2014-08-04 19:13:39 +03:00
Petteri Aimonen
ec3bff4ba1 Generate #defines for initializing message structures.
Usage like:
MyMessage foo = MyMessage_init_default;

MyMessage_init_default will initialize to default values defined in .proto.

MyMessage_init_zero will initialize to null/zero values. Same results as {}
or {0}, but will avoid compiler warnings by initializing everything explicitly.

Update issue 79
Status: FixedInGit
2014-08-04 18:40:40 +03:00
Petteri Aimonen
1d7f60fec3 Add skip_message option to generator.
Update issue 121
Status: FixedInGit
2014-07-20 14:56:12 +03:00
Petteri Aimonen
5749606f5d Add support for inverted patterns in test framework. 2014-07-20 14:55:47 +03:00
Petteri Aimonen
eaa3c7b157 Cleanup and comment the code of network_server example.
Update issue 123
Status: FixedInGit
2014-07-20 14:44:41 +03:00
Petteri Aimonen
3cf9668c75 Do not automatically add a dot with generator -e option.
Now -e option in generator is more versatile. Especially it avoids
double-dot problem with some build systems.

Given foobar.proto, we now get:
-e .pb  =>  foobar.pb.c (default)
-e _pb  =>  foobar_pb.c
-e ''   =>  foobar.c

Note that if you have used -e option previously, you will have to prepend
. to the argument to get the same filenames as before.

Update issue 122
Status: FixedInGit
2014-07-20 14:25:11 +03:00
Petteri Aimonen
7f97ad549e Give better messages about the .options file path.
Update issue 124
Status: FixedInGit
2014-07-20 14:18:21 +03:00
Petteri Aimonen
f2f9f8a9ed Fix problem with .options file and extension fields.
The options for an extension field were being looked up under wrong name
(MessageName instead of MessageName.fieldname).

Fixed the problem and added regression test. Created a new subfolder for
regression test cases.

Update issue 125
Status: FixedInGit
2014-07-20 14:02:56 +03:00
Petteri Aimonen
788d2825b0 Add unit tests for allocate_field(). 2014-06-02 21:20:57 +03:00
Petteri Aimonen
99bc1d4f97 Make clearer that size = 0 in allocate_field() is not allowed.
Back in design phase the code used realloc() for freeing the memory
also. However, this is not entirely portable, and therefore the finished
implementation used free() separately.

There were some remnants of the size = 0 code in the allocate_field()
code, which made it somewhat confusing. This change makes it clearer
that size = 0 is not allowed (and not used by nanopb).
2014-06-02 21:12:38 +03:00
Petteri Aimonen
8a857a7f75 Don't use SIZE_MAX macro, as it is not in C89.
Update issue 120
Status: FixedInGit
2014-06-02 21:09:06 +03:00
Petteri Aimonen
8611958a7f Add PB_PACKED_STRUCT support for Keil MDK-ARM toolchain
Patch from Jon Read.

Update issue 119
Status: FixedInGit
2014-05-30 13:45:48 +03:00
Petteri Aimonen
2e9797af58 Setting version to 0.2.9-dev 2014-05-20 19:52:09 +03:00
Petteri Aimonen
2c51fb7771 Update changelog for 0.2.8 2014-05-20 19:46:48 +03:00
Petteri Aimonen
916bcb3643 Publishing nanopb-0.2.8 2014-05-20 19:35:00 +03:00
Petteri Aimonen
9cf788de54 Fix bug in alltypes test case that made fuzzing difficult. 2014-05-17 20:28:33 +03:00
Petteri Aimonen
5ef128616b Fix security issue with PB_ENABLE_MALLOC.
The multiplication in allocate_field could potentially overflow,
leading to allocating too little memory. This could subsequently
allow an attacker to cause a write past the buffer, overwriting
other memory contents.

The attack is possible if untrusted message data is decoded using
nanopb, and the message type includes a pointer-type string or bytes
field, or a repeated numeric field. Submessage fields are not
affected.

This issue only affects systems that have been compiled with
PB_ENABLE_MALLOC enabled. Only version nanopb-0.2.7 is affected,
as prior versions do not include this functionality.

Update issue 117
Status: FixedInGit
2014-05-17 20:06:55 +03:00
Petteri Aimonen
ba2ab9ea65 Docs update, remove malloc from limitations list 2014-04-26 20:11:54 +03:00
Petteri Aimonen
e6a57e512f Add option to not add timestamps to .pb.h and .pb.c preambles.
Patch by rusnakp.

Update issue 115
Status: FixedInGit
2014-04-18 15:40:40 +03:00
Petteri Aimonen
d177af1639 Fix typos in scons command line options 2014-04-15 20:30:50 +03:00
Petteri Aimonen
3b36235cef Remove -O0 from tests CFLAGS so that optimized builds can be tested also 2014-04-15 20:27:38 +03:00
Petteri Aimonen
1d249a48ea Fix bug in missing_fields test case 2014-04-09 19:39:12 +03:00
Petteri Aimonen
3e83d81b09 Use -fsanitize=undefined when running tests with clang 2014-04-09 19:28:57 +03:00
Petteri Aimonen
938c7ac3f3 Setting version to 0.2.8-dev 2014-04-07 20:45:04 +03:00
Petteri Aimonen
6d74c66ada Publishing nanopb-0.2.7 2014-04-07 20:30:42 +03:00
Petteri Aimonen
c998ffe117 Update changelog 2014-04-07 20:30:12 +03:00
Petteri Aimonen
a8de6acf2d Add rule for building coverage summary using lcov.
Also modified a few tests to be more compatible with coverage information,
so that they use the same pb_encode/decode.c instead of making a copy.
2014-04-05 13:26:37 +03:00
Petteri Aimonen
7880f308ea Fix unused parameter warning when building without errmsg. 2014-04-05 13:25:44 +03:00
Petteri Aimonen
b63e582bdb Add a convenience function pb_get_encoded_size()
There is minimal size penalty from this, and it is probably much more
intuitive to use than PB_OSTREAM_SIZING itself.

This has been suggested before also, but I ended up refusing it back
them. Reconsidering it now, I see that an intuitive API is much better
than any amount of documentation explaining a non-intuitive API.

Update issue 16
Status: FixedInGit
2014-04-05 11:26:39 +03:00
Petteri Aimonen
e5b855fec5 Add a 'found' field to pb_extension_t.
Update issue 112
Status: FixedInGit
2014-04-05 11:11:05 +03:00
Petteri Aimonen
70dee34da6 Add some missing 'static' specifiers
Update issue 91
Status: FixedInGit
2014-04-02 21:08:15 +03:00
Petteri Aimonen
99434724d0 Fix splint warnings, add splint test case 2014-04-02 21:07:30 +03:00
Petteri Aimonen
6c90e824c4 Fix compile error when default value given for extension field.
Update issue 111
Status: FixedInGit
2014-04-01 16:47:53 +03:00
Petteri Aimonen
f4949119ad Add stdlib.h to pb_syshdr.h for dynamic allocation 2014-03-18 16:13:54 +02:00
40 changed files with 591 additions and 188 deletions

View File

@@ -1,3 +1,30 @@
nanopb-0.2.9 (2014-08-09)
NOTE: If you are using the -e option with the generator, you have
to prepend . to the argument to get the same behaviour as before.
Do not automatically add a dot with generator -e option. (issue 122)
Fix problem with .options file and extension fields. (issue 125)
Don't use SIZE_MAX macro, as it is not in C89. (issue 120)
Generate #defines for initializing message structures. (issue 79)
Add skip_message option to generator. (issue 121)
Add PB_PACKED_STRUCT support for Keil MDK-ARM toolchain (issue 119)
Give better messages about the .options file path. (issue 124)
Improved tests
nanopb-0.2.8 (2014-05-20)
Fix security issue with PB_ENABLE_MALLOC. (issue 117)
Add option to not add timestamps to .pb.h and .pb.c preambles. (issue 115)
Documentation updates
Improved tests
nanopb-0.2.7 (2014-04-07)
Fix bug with default values for extension fields (issue 111)
Fix some MISRA-C warnings (issue 91)
Implemented optional malloc() support (issue 80)
Changed pointer-type bytes field datatype
Add a "found" field to pb_extension_t (issue 112)
Add convenience function pb_get_encoded_size() (issue 16)
nanopb-0.2.6 (2014-02-15) nanopb-0.2.6 (2014-02-15)
Fix generator error with bytes callback fields (issue 99) Fix generator error with bytes callback fields (issue 99)
Fix warnings about large integer constants (issue 102) Fix warnings about large integer constants (issue 102)

View File

@@ -47,7 +47,6 @@ Features and limitations
**Limitations** **Limitations**
#) User must provide callbacks when decoding arrays or strings without maximum size. Malloc support could be added as a separate module.
#) Some speed has been sacrificed for code size. #) Some speed has been sacrificed for code size.
#) Encoding is focused on writing to streams. For memory buffers only it could be made more efficient. #) Encoding is focused on writing to streams. For memory buffers only it could be made more efficient.
#) The deprecated Protocol Buffers feature called "groups" is not supported. #) The deprecated Protocol Buffers feature called "groups" is not supported.

View File

@@ -24,10 +24,6 @@ __BIG_ENDIAN__ Set this if your platform stores integers and
floats in big-endian format. Mixed-endian floats in big-endian format. Mixed-endian
systems (different layout for ints and floats) systems (different layout for ints and floats)
are currently not supported. are currently not supported.
NANOPB_INTERNALS Set this to expose the field encoder functions
that are hidden since nanopb-0.1.3. Starting
with nanopb-0.2.4, this flag does nothing. Use
the newer functions that have better interface.
PB_ENABLE_MALLOC Set this to enable dynamic allocation support PB_ENABLE_MALLOC Set this to enable dynamic allocation support
in the decoder. in the decoder.
PB_MAX_REQUIRED_FIELDS Maximum number of required fields to check for PB_MAX_REQUIRED_FIELDS Maximum number of required fields to check for

View File

@@ -23,9 +23,13 @@
#include "fileproto.pb.h" #include "fileproto.pb.h"
#include "common.h" #include "common.h"
/* This callback function will be called once for each filename received
* from the server. The filenames will be printed out immediately, so that
* no memory has to be allocated for them.
*/
bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void **arg) bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void **arg)
{ {
FileInfo fileinfo; FileInfo fileinfo = {};
if (!pb_decode(stream, FileInfo_fields, &fileinfo)) if (!pb_decode(stream, FileInfo_fields, &fileinfo))
return false; return false;
@@ -35,14 +39,20 @@ bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void **ar
return true; return true;
} }
/* This function sends a request to socket 'fd' to list the files in
* directory given in 'path'. The results received from server will
* be printed to stdout.
*/
bool listdir(int fd, char *path) bool listdir(int fd, char *path)
{ {
ListFilesRequest request; /* Construct and send the request to server */
ListFilesResponse response; {
pb_istream_t input = pb_istream_from_socket(fd); ListFilesRequest request = {};
pb_ostream_t output = pb_ostream_from_socket(fd); pb_ostream_t output = pb_ostream_from_socket(fd);
uint8_t zero = 0; uint8_t zero = 0;
/* In our protocol, path is optional. If it is not given,
* the server will list the root directory. */
if (path == NULL) if (path == NULL)
{ {
request.has_path = false; request.has_path = false;
@@ -59,15 +69,25 @@ bool listdir(int fd, char *path)
strcpy(request.path, path); strcpy(request.path, path);
} }
/* Encode the request. It is written to the socket immediately
* through our custom stream. */
if (!pb_encode(&output, ListFilesRequest_fields, &request)) if (!pb_encode(&output, ListFilesRequest_fields, &request))
{ {
fprintf(stderr, "Encoding failed.\n"); fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&output));
return false; return false;
} }
/* We signal the end of request with a 0 tag. */ /* We signal the end of request with a 0 tag. */
pb_write(&output, &zero, 1); pb_write(&output, &zero, 1);
}
/* Read back the response from server */
{
ListFilesResponse response = {};
pb_istream_t input = pb_istream_from_socket(fd);
/* Give a pointer to our callback function, which will handle the
* filenames as they arrive. */
response.file.funcs.decode = &printfile_callback; response.file.funcs.decode = &printfile_callback;
if (!pb_decode(&input, ListFilesResponse_fields, &response)) if (!pb_decode(&input, ListFilesResponse_fields, &response))
@@ -76,11 +96,14 @@ bool listdir(int fd, char *path)
return false; return false;
} }
/* If the message from server decodes properly, but directory was
* not found on server side, we get path_error == true. */
if (response.path_error) if (response.path_error)
{ {
fprintf(stderr, "Server reported error.\n"); fprintf(stderr, "Server reported error.\n");
return false; return false;
} }
}
return true; return true;
} }
@@ -96,6 +119,7 @@ int main(int argc, char **argv)
sockfd = socket(AF_INET, SOCK_STREAM, 0); sockfd = socket(AF_INET, SOCK_STREAM, 0);
/* Connect to server running on localhost:1234 */
memset(&servaddr, 0, sizeof(servaddr)); memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
@@ -107,9 +131,11 @@ int main(int argc, char **argv)
return 1; return 1;
} }
/* Send the directory listing request */
if (!listdir(sockfd, path)) if (!listdir(sockfd, path))
return 2; return 2;
/* Close connection */
close(sockfd); close(sockfd);
return 0; return 0;

View File

@@ -23,11 +23,16 @@
#include "fileproto.pb.h" #include "fileproto.pb.h"
#include "common.h" #include "common.h"
/* This callback function will be called once during the encoding.
* It will write out any number of FileInfo entries, without consuming unnecessary memory.
* This is accomplished by fetching the filenames one at a time and encoding them
* immediately.
*/
bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{ {
DIR *dir = (DIR*) *arg; DIR *dir = (DIR*) *arg;
struct dirent *file; struct dirent *file;
FileInfo fileinfo; FileInfo fileinfo = {};
while ((file = readdir(dir)) != NULL) while ((file = readdir(dir)) != NULL)
{ {
@@ -35,9 +40,12 @@ bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * cons
strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name)); strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name));
fileinfo.name[sizeof(fileinfo.name) - 1] = '\0'; fileinfo.name[sizeof(fileinfo.name) - 1] = '\0';
/* This encodes the header for the field, based on the constant info
* from pb_field_t. */
if (!pb_encode_tag_for_field(stream, field)) if (!pb_encode_tag_for_field(stream, field))
return false; return false;
/* This encodes the data for the field, based on our FileInfo structure. */
if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo)) if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
return false; return false;
} }
@@ -45,13 +53,18 @@ bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * cons
return true; return true;
} }
/* Handle one arriving client connection.
* Clients are expected to send a ListFilesRequest, terminated by a '0'.
* Server will respond with a ListFilesResponse message.
*/
void handle_connection(int connfd) void handle_connection(int connfd)
{ {
ListFilesRequest request; DIR *directory = NULL;
ListFilesResponse response;
/* Decode the message from the client and open the requested directory. */
{
ListFilesRequest request = {};
pb_istream_t input = pb_istream_from_socket(connfd); pb_istream_t input = pb_istream_from_socket(connfd);
pb_ostream_t output = pb_ostream_from_socket(connfd);
DIR *directory;
if (!pb_decode(&input, ListFilesRequest_fields, &request)) if (!pb_decode(&input, ListFilesRequest_fields, &request))
{ {
@@ -60,19 +73,26 @@ void handle_connection(int connfd)
} }
directory = opendir(request.path); directory = opendir(request.path);
printf("Listing directory: %s\n", request.path); printf("Listing directory: %s\n", request.path);
}
/* List the files in the directory and transmit the response to client */
{
ListFilesResponse response = {};
pb_ostream_t output = pb_ostream_from_socket(connfd);
if (directory == NULL) if (directory == NULL)
{ {
perror("opendir"); perror("opendir");
/* Directory was not found, transmit error status */
response.has_path_error = true; response.has_path_error = true;
response.path_error = true; response.path_error = true;
response.file.funcs.encode = NULL; response.file.funcs.encode = NULL;
} }
else else
{ {
/* Directory was found, transmit filenames */
response.has_path_error = false; response.has_path_error = false;
response.file.funcs.encode = &listdir_callback; response.file.funcs.encode = &listdir_callback;
response.file.arg = directory; response.file.arg = directory;
@@ -80,18 +100,22 @@ void handle_connection(int connfd)
if (!pb_encode(&output, ListFilesResponse_fields, &response)) if (!pb_encode(&output, ListFilesResponse_fields, &response))
{ {
printf("Encoding failed.\n"); printf("Encoding failed: %s\n", PB_GET_ERROR(&output));
} }
} }
if (directory != NULL)
closedir(directory);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int listenfd, connfd; int listenfd, connfd;
struct sockaddr_in servaddr; struct sockaddr_in servaddr;
int reuse = 1; int reuse = 1;
/* Listen on localhost:1234 for TCP connections */
listenfd = socket(AF_INET, SOCK_STREAM, 0); listenfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
memset(&servaddr, 0, sizeof(servaddr)); memset(&servaddr, 0, sizeof(servaddr));
@@ -112,6 +136,7 @@ int main(int argc, char **argv)
for(;;) for(;;)
{ {
/* Wait for a client */
connfd = accept(listenfd, NULL, NULL); connfd = accept(listenfd, NULL, NULL);
if (connfd < 0) if (connfd < 0)
@@ -128,4 +153,6 @@ int main(int argc, char **argv)
close(connfd); close(connfd);
} }
return 0;
} }

View File

@@ -53,6 +53,16 @@ typedef int bool;
#endif #endif
/* stdlib.h subset */
#ifdef PB_ENABLE_MALLOC
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#else
void *realloc(void *ptr, size_t size);
void free(void *ptr);
#endif
#endif
/* string.h subset */ /* string.h subset */
#ifdef HAVE_STRING_H #ifdef HAVE_STRING_H
#include <string.h> #include <string.h>

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python #!/usr/bin/python
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
nanopb_version = "nanopb-0.2.7-dev" nanopb_version = "nanopb-0.2.9"
import sys import sys
@@ -292,28 +292,37 @@ class Field:
result = None result = None
return result return result
def default_decl(self, declaration_only = False): def get_initializer(self, null_init):
'''Return definition for this field's default value.''' '''Return literal expression for this field's default value.'''
if self.default is None:
return None
ctype, default = self.ctype, self.default if self.pbtype == 'MESSAGE':
array_decl = '' if null_init:
return '%s_init_zero' % self.ctype
else:
return '%s_init_default' % self.ctype
if self.default is None or null_init:
if self.pbtype == 'STRING':
return '""'
elif self.pbtype == 'BYTES':
return '{0, {0}}'
elif self.pbtype == 'ENUM':
return '(%s)0' % self.ctype
else:
return '0'
default = str(self.default)
if self.pbtype == 'STRING': if self.pbtype == 'STRING':
if self.allocation != 'STATIC': default = default.encode('utf-8').encode('string_escape')
return None # Not implemented
array_decl = '[%d]' % self.max_size
default = str(self.default).encode('string_escape')
default = default.replace('"', '\\"') default = default.replace('"', '\\"')
default = '"' + default + '"' default = '"' + default + '"'
elif self.pbtype == 'BYTES': elif self.pbtype == 'BYTES':
if self.allocation != 'STATIC': data = default.decode('string_escape')
return None # Not implemented
data = self.default.decode('string_escape')
data = ['0x%02x' % ord(c) for c in data] data = ['0x%02x' % ord(c) for c in data]
if len(data) == 0:
default = '{0, {0}}'
else:
default = '{%d, {%s}}' % (len(data), ','.join(data)) default = '{%d, {%s}}' % (len(data), ','.join(data))
elif self.pbtype in ['FIXED32', 'UINT32']: elif self.pbtype in ['FIXED32', 'UINT32']:
default += 'u' default += 'u'
@@ -322,6 +331,25 @@ class Field:
elif self.pbtype in ['SFIXED64', 'INT64']: elif self.pbtype in ['SFIXED64', 'INT64']:
default += 'll' default += 'll'
return default
def default_decl(self, declaration_only = False):
'''Return definition for this field's default value.'''
if self.default is None:
return None
ctype = self.ctype
default = self.get_initializer(False)
array_decl = ''
if self.pbtype == 'STRING':
if self.allocation != 'STATIC':
return None # Not implemented
array_decl = '[%d]' % self.max_size
elif self.pbtype == 'BYTES':
if self.allocation != 'STATIC':
return None # Not implemented
if declaration_only: if declaration_only:
return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl) return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl)
else: else:
@@ -351,6 +379,8 @@ class Field:
result += '0)' result += '0)'
elif self.pbtype in ['BYTES', 'STRING'] and self.allocation != 'STATIC': elif self.pbtype in ['BYTES', 'STRING'] and self.allocation != 'STATIC':
result += '0)' # Arbitrary size default values not implemented result += '0)' # Arbitrary size default values not implemented
elif self.rules == 'OPTEXT':
result += '0)' # Default value for extensions is not implemented
else: else:
result += '&%s_default)' % (self.struct_name + self.name) result += '&%s_default)' % (self.struct_name + self.name)
@@ -551,6 +581,32 @@ class Message:
result += types + '\n' result += types + '\n'
return result return result
def get_initializer(self, null_init):
if not self.ordered_fields:
return '{0}'
parts = []
for field in self.ordered_fields:
if field.allocation == 'STATIC':
if field.rules == 'REPEATED':
parts.append('0')
parts.append('{'
+ ', '.join([field.get_initializer(null_init)] * field.max_count)
+ '}')
elif field.rules == 'OPTIONAL':
parts.append('false')
parts.append(field.get_initializer(null_init))
else:
parts.append(field.get_initializer(null_init))
elif field.allocation == 'POINTER':
parts.append('NULL')
elif field.allocation == 'CALLBACK':
if field.pbtype == 'EXTENSION':
parts.append('NULL')
else:
parts.append('{{NULL}, NULL}')
return '{' + ', '.join(parts) + '}'
def default_decl(self, declaration_only = False): def default_decl(self, declaration_only = False):
result = "" result = ""
for field in self.fields: for field in self.fields:
@@ -637,13 +693,17 @@ def parse_file(fdesc, file_options):
for names, message in iterate_messages(fdesc, base_name): for names, message in iterate_messages(fdesc, base_name):
message_options = get_nanopb_suboptions(message, file_options, names) message_options = get_nanopb_suboptions(message, file_options, names)
if message_options.skip_message:
continue
messages.append(Message(names, message, message_options)) messages.append(Message(names, message, message_options))
for enum in message.enum_type: for enum in message.enum_type:
enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name) enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name)
enums.append(Enum(names, enum, enum_options)) enums.append(Enum(names, enum, enum_options))
for names, extension in iterate_extensions(fdesc, base_name): for names, extension in iterate_extensions(fdesc, base_name):
field_options = get_nanopb_suboptions(extension, file_options, names) field_options = get_nanopb_suboptions(extension, file_options, names + extension.name)
if field_options.type != nanopb_pb2.FT_IGNORE: if field_options.type != nanopb_pb2.FT_IGNORE:
extensions.append(ExtensionField(names, extension, field_options)) extensions.append(ExtensionField(names, extension, field_options))
@@ -705,6 +765,9 @@ def generate_header(dependencies, headername, enums, messages, extensions, optio
''' '''
yield '/* Automatically generated nanopb header */\n' yield '/* Automatically generated nanopb header */\n'
if options.notimestamp:
yield '/* Generated by %s */\n\n' % (nanopb_version)
else:
yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
symbol = make_identifier(headername) symbol = make_identifier(headername)
@@ -719,7 +782,7 @@ def generate_header(dependencies, headername, enums, messages, extensions, optio
for dependency in dependencies: for dependency in dependencies:
noext = os.path.splitext(dependency)[0] noext = os.path.splitext(dependency)[0]
yield options.genformat % (noext + '.' + options.extension + '.h') yield options.genformat % (noext + options.extension + '.h')
yield '\n' yield '\n'
yield '#ifdef __cplusplus\n' yield '#ifdef __cplusplus\n'
@@ -746,6 +809,15 @@ def generate_header(dependencies, headername, enums, messages, extensions, optio
yield msg.default_decl(True) yield msg.default_decl(True)
yield '\n' yield '\n'
yield '/* Initializer values for message structs */\n'
for msg in messages:
identifier = '%s_init_default' % msg.name
yield '#define %-40s %s\n' % (identifier, msg.get_initializer(False))
for msg in messages:
identifier = '%s_init_zero' % msg.name
yield '#define %-40s %s\n' % (identifier, msg.get_initializer(True))
yield '\n'
yield '/* Field tags (for use in manual encoding/decoding) */\n' yield '/* Field tags (for use in manual encoding/decoding) */\n'
for msg in sort_dependencies(messages): for msg in sort_dependencies(messages):
for field in msg.fields: for field in msg.fields:
@@ -778,6 +850,9 @@ def generate_source(headername, enums, messages, extensions, options):
'''Generate content for a source file.''' '''Generate content for a source file.'''
yield '/* Automatically generated nanopb constant definitions */\n' yield '/* Automatically generated nanopb constant definitions */\n'
if options.notimestamp:
yield '/* Generated by %s */\n\n' % (nanopb_version)
else:
yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime())
yield options.genformat % (headername) yield options.genformat % (headername)
yield '\n' yield '\n'
@@ -951,8 +1026,8 @@ optparser = OptionParser(
"Output will be written to file.pb.h and file.pb.c.") "Output will be written to file.pb.h and file.pb.c.")
optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[], optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
help="Exclude file from generated #include list.") help="Exclude file from generated #include list.")
optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default="pb", optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default=".pb",
help="Set extension to use instead of 'pb' for generated files. [default: %default]") help="Set extension to use instead of '.pb' for generated files. [default: %default]")
optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options", optparser.add_option("-f", "--options-file", dest="options_file", metavar="FILE", default="%s.options",
help="Set name of a separate generator options file.") help="Set name of a separate generator options file.")
optparser.add_option("-Q", "--generated-include-format", dest="genformat", optparser.add_option("-Q", "--generated-include-format", dest="genformat",
@@ -961,6 +1036,8 @@ optparser.add_option("-Q", "--generated-include-format", dest="genformat",
optparser.add_option("-L", "--library-include-format", dest="libformat", optparser.add_option("-L", "--library-include-format", dest="libformat",
metavar="FORMAT", default='#include <%s>\n', metavar="FORMAT", default='#include <%s>\n',
help="Set format string to use for including the nanopb pb.h header. [default: %default]") help="Set format string to use for including the nanopb pb.h header. [default: %default]")
optparser.add_option("-T", "--no-timestamp", dest="notimestamp", action="store_true", default=False,
help="Don't add timestamp to .pb.h and .pb.c preambles")
optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, optparser.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
help="Don't print anything except errors.") help="Don't print anything except errors.")
optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, optparser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
@@ -990,11 +1067,13 @@ def process_file(filename, fdesc, options):
fdesc = descriptor.FileDescriptorSet.FromString(data).file[0] fdesc = descriptor.FileDescriptorSet.FromString(data).file[0]
# Check if there is a separate .options file # Check if there is a separate .options file
had_abspath = False
try: try:
optfilename = options.options_file % os.path.splitext(filename)[0] optfilename = options.options_file % os.path.splitext(filename)[0]
except TypeError: except TypeError:
# No %s specified, use the filename as-is # No %s specified, use the filename as-is
optfilename = options.options_file optfilename = options.options_file
had_abspath = True
if os.path.isfile(optfilename): if os.path.isfile(optfilename):
if options.verbose: if options.verbose:
@@ -1002,7 +1081,14 @@ def process_file(filename, fdesc, options):
Globals.separate_options = read_options_file(open(optfilename, "rU")) Globals.separate_options = read_options_file(open(optfilename, "rU"))
else: else:
# If we are given a full filename and it does not exist, give an error.
# However, don't give error when we automatically look for .options file
# with the same name as .proto.
if options.verbose or had_abspath:
sys.stderr.write('Options file not found: ' + optfilename)
Globals.separate_options = [] Globals.separate_options = []
Globals.matched_namemasks = set() Globals.matched_namemasks = set()
# Parse the file # Parse the file
@@ -1011,8 +1097,8 @@ def process_file(filename, fdesc, options):
# Decide the file names # Decide the file names
noext = os.path.splitext(filename)[0] noext = os.path.splitext(filename)[0]
headername = noext + '.' + options.extension + '.h' headername = noext + options.extension + '.h'
sourcename = noext + '.' + options.extension + '.c' sourcename = noext + options.extension + '.c'
headerbasename = os.path.basename(headername) headerbasename = os.path.basename(headername)
# List of .proto files that should not be included in the C header file # List of .proto files that should not be included in the C header file

View File

@@ -37,6 +37,9 @@ message NanoPBOptions {
// Note: this cannot be used on CPUs that break on unaligned // Note: this cannot be used on CPUs that break on unaligned
// accesses to variables. // accesses to variables.
optional bool packed_struct = 5 [default = false]; optional bool packed_struct = 5 [default = false];
// Skip this message
optional bool skip_message = 6 [default = false];
} }
// Extensions to protoc 'Descriptor' type in order to define options // Extensions to protoc 'Descriptor' type in order to define options

16
pb.h
View File

@@ -46,7 +46,7 @@
/* Version of the nanopb library. Just in case you want to check it in /* Version of the nanopb library. Just in case you want to check it in
* your own program. */ * your own program. */
#define NANOPB_VERSION nanopb-0.2.7-dev #define NANOPB_VERSION nanopb-0.2.9
/* Include all the system headers needed by nanopb. You will need the /* Include all the system headers needed by nanopb. You will need the
* definitions of the following: * definitions of the following:
@@ -80,8 +80,8 @@
# define PB_PACKED_STRUCT_START # define PB_PACKED_STRUCT_START
# define PB_PACKED_STRUCT_END # define PB_PACKED_STRUCT_END
# define pb_packed __attribute__((packed)) # define pb_packed __attribute__((packed))
#elif defined(__ICCARM__) #elif defined(__ICCARM__) || defined(__CC_ARM)
/* For IAR ARM compiler */ /* For IAR ARM and Keil MDK-ARM compilers */
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") # define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") # define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
# define pb_packed # define pb_packed
@@ -341,6 +341,10 @@ struct _pb_extension_t {
* If this extension does not match a field, the next handler is * If this extension does not match a field, the next handler is
* automatically called. */ * automatically called. */
pb_extension_t *next; pb_extension_t *next;
/* The decoder sets this to true if the extension was found.
* Ignored for encoding. */
bool found;
}; };
/* Memory allocation functions to use. You can define pb_realloc and /* Memory allocation functions to use. You can define pb_realloc and
@@ -496,7 +500,11 @@ struct _pb_extension_t {
* messages if not used. * messages if not used.
*/ */
#ifdef PB_NO_ERRMSG #ifdef PB_NO_ERRMSG
#define PB_RETURN_ERROR(stream,msg) return false #define PB_RETURN_ERROR(stream,msg) \
do {\
UNUSED(stream); \
return false; \
} while(0)
#define PB_GET_ERROR(stream) "(errmsg disabled)" #define PB_GET_ERROR(stream) "(errmsg disabled)"
#else #else
#define PB_RETURN_ERROR(stream,msg) \ #define PB_RETURN_ERROR(stream,msg) \

View File

@@ -13,7 +13,6 @@
#define checkreturn __attribute__((warn_unused_result)) #define checkreturn __attribute__((warn_unused_result))
#endif #endif
#define NANOPB_INTERNALS
#include "pb.h" #include "pb.h"
#include "pb_decode.h" #include "pb_decode.h"
@@ -130,7 +129,7 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count)
* This is an optimization for the varint decoding. */ * This is an optimization for the varint decoding. */
static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf)
{ {
if (!stream->bytes_left) if (stream->bytes_left == 0)
PB_RETURN_ERROR(stream, "end-of-stream"); PB_RETURN_ERROR(stream, "end-of-stream");
#ifndef PB_BUFFER_ONLY #ifndef PB_BUFFER_ONLY
@@ -174,7 +173,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
if (!pb_readbyte(stream, &byte)) if (!pb_readbyte(stream, &byte))
return false; return false;
if (!(byte & 0x80)) if ((byte & 0x80) == 0)
{ {
/* Quick case, 1 byte value */ /* Quick case, 1 byte value */
result = byte; result = byte;
@@ -397,7 +396,7 @@ static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag)
{ {
return true; return true;
} }
pb_field_next(iter); (void)pb_field_next(iter);
} while (iter->field_index != start); } while (iter->field_index != start);
return false; return false;
@@ -435,7 +434,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
if (!pb_make_string_substream(stream, &substream)) if (!pb_make_string_substream(stream, &substream))
return false; return false;
while (substream.bytes_left && *size < iter->pos->array_size) while (substream.bytes_left > 0 && *size < iter->pos->array_size)
{ {
void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size);
if (!func(&substream, iter->pos, pItem)) if (!func(&substream, iter->pos, pItem))
@@ -471,16 +470,32 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
#ifdef PB_ENABLE_MALLOC #ifdef PB_ENABLE_MALLOC
/* Allocate storage for the field and store the pointer at iter->pData. /* Allocate storage for the field and store the pointer at iter->pData.
* array_size is the number of entries to reserve in an array. */ * array_size is the number of entries to reserve in an array.
* Zero size is not allowed, use pb_free() for releasing.
*/
static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size)
{ {
void *ptr = *(void**)pData; void *ptr = *(void**)pData;
size_t size = array_size * data_size;
/* Check for multiplication overflows.
* This code avoids the costly division if the sizes are small enough.
* Multiplication is safe as long as only half of bits are set
* in either multiplicand.
*/
const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4);
if (data_size >= check_limit || array_size >= check_limit)
{
const size_t size_max = (size_t)-1;
if (size_max / array_size < data_size)
{
PB_RETURN_ERROR(stream, "size too large");
}
}
/* Allocate new or expand previous allocation */ /* Allocate new or expand previous allocation */
/* Note: on failure the old pointer will remain in the structure, /* Note: on failure the old pointer will remain in the structure,
* the message must be freed by caller also on error return. */ * the message must be freed by caller also on error return. */
ptr = pb_realloc(ptr, size); ptr = pb_realloc(ptr, array_size * data_size);
if (ptr == NULL) if (ptr == NULL)
PB_RETURN_ERROR(stream, "realloc failed"); PB_RETURN_ERROR(stream, "realloc failed");
@@ -671,7 +686,6 @@ static bool checkreturn default_extension_decoder(pb_istream_t *stream,
{ {
const pb_field_t *field = (const pb_field_t*)extension->type->arg; const pb_field_t *field = (const pb_field_t*)extension->type->arg;
pb_field_iterator_t iter; pb_field_iterator_t iter;
bool dummy;
if (field->tag != tag) if (field->tag != tag)
return true; return true;
@@ -682,7 +696,7 @@ static bool checkreturn default_extension_decoder(pb_istream_t *stream,
iter.required_field_index = 0; iter.required_field_index = 0;
iter.dest_struct = extension->dest; iter.dest_struct = extension->dest;
iter.pData = extension->dest; iter.pData = extension->dest;
iter.pSize = &dummy; iter.pSize = &extension->found;
return decode_field(stream, wire_type, &iter); return decode_field(stream, wire_type, &iter);
} }
@@ -695,7 +709,7 @@ static bool checkreturn decode_extension(pb_istream_t *stream,
pb_extension_t *extension = *(pb_extension_t* const *)iter->pData; pb_extension_t *extension = *(pb_extension_t* const *)iter->pData;
size_t pos = stream->bytes_left; size_t pos = stream->bytes_left;
while (extension && pos == stream->bytes_left) while (extension != NULL && pos == stream->bytes_left)
{ {
bool status; bool status;
if (extension->type->decode) if (extension->type->decode)
@@ -722,7 +736,7 @@ static bool checkreturn find_extension_field(pb_field_iterator_t *iter)
do { do {
if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION) if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION)
return true; return true;
pb_field_next(iter); (void)pb_field_next(iter);
} while (iter->field_index != start); } while (iter->field_index != start);
return false; return false;
@@ -798,7 +812,7 @@ static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_str
bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
{ {
uint8_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 7) / 8] = {0}; /* Used to check for required fields */ uint8_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 7) / 8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint32_t extension_range_start = 0; uint32_t extension_range_start = 0;
pb_field_iterator_t iter; pb_field_iterator_t iter;
@@ -874,7 +888,7 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[
} while (pb_field_next(&iter)); } while (pb_field_next(&iter));
/* Fixup if last field was also required. */ /* Fixup if last field was also required. */
if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag) if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0)
req_field_count++; req_field_count++;
/* Check the whole bytes */ /* Check the whole bytes */
@@ -948,16 +962,20 @@ void pb_release(const pb_field_t fields[], void *dest_struct)
pb_free(*pItem); pb_free(*pItem);
*pItem++ = NULL; *pItem++ = NULL;
} }
*(pb_size_t*)iter.pSize = 0;
} }
else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
{ {
/* Release fields in submessages */ /* Release fields in submessages */
void *pItem = *(void**)iter.pData; void *pItem = *(void**)iter.pData;
size_t count = (pItem ? 1 : 0); if (pItem)
{
pb_size_t count = 1;
if (PB_HTYPE(type) == PB_HTYPE_REPEATED) if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
{ {
count = *(size_t*)iter.pSize; count = *(pb_size_t*)iter.pSize;
*(pb_size_t*)iter.pSize = 0;
} }
while (count--) while (count--)
@@ -966,6 +984,7 @@ void pb_release(const pb_field_t fields[], void *dest_struct)
pItem = (uint8_t*)pItem + iter.pos->data_size; pItem = (uint8_t*)pItem + iter.pos->data_size;
} }
} }
}
/* Release main item */ /* Release main item */
pb_free(*(void**)iter.pData); pb_free(*(void**)iter.pData);
@@ -1033,7 +1052,7 @@ bool pb_decode_fixed64(pb_istream_t *stream, void *dest)
#endif #endif
} }
bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
uint64_t value; uint64_t value;
if (!pb_decode_varint(stream, &value)) if (!pb_decode_varint(stream, &value))
@@ -1051,7 +1070,7 @@ bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, vo
return true; return true;
} }
bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
uint64_t value; uint64_t value;
if (!pb_decode_varint(stream, &value)) if (!pb_decode_varint(stream, &value))
@@ -1067,7 +1086,7 @@ bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, v
return true; return true;
} }
bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
int64_t value; int64_t value;
if (!pb_decode_svarint(stream, &value)) if (!pb_decode_svarint(stream, &value))
@@ -1083,19 +1102,19 @@ bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, v
return true; return true;
} }
bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
UNUSED(field); UNUSED(field);
return pb_decode_fixed32(stream, dest); return pb_decode_fixed32(stream, dest);
} }
bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
UNUSED(field); UNUSED(field);
return pb_decode_fixed64(stream, dest); return pb_decode_fixed64(stream, dest);
} }
bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
uint32_t size; uint32_t size;
pb_bytes_array_t *bdest; pb_bytes_array_t *bdest;
@@ -1124,7 +1143,7 @@ bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, voi
return pb_read(stream, bdest->bytes, size); return pb_read(stream, bdest->bytes, size);
} }
bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
uint32_t size; uint32_t size;
size_t alloc_size; size_t alloc_size;
@@ -1156,7 +1175,7 @@ bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, vo
return status; return status;
} }
bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest) static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
{ {
bool status; bool status;
pb_istream_t substream; pb_istream_t substream;

View File

@@ -3,7 +3,6 @@
* 2011 Petteri Aimonen <jpa@kapsi.fi> * 2011 Petteri Aimonen <jpa@kapsi.fi>
*/ */
#define NANOPB_INTERNALS
#include "pb.h" #include "pb.h"
#include "pb_encode.h" #include "pb_encode.h"
@@ -379,6 +378,17 @@ bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const
return pb_encode_submessage(stream, fields, src_struct); return pb_encode_submessage(stream, fields, src_struct);
} }
bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct)
{
pb_ostream_t stream = PB_OSTREAM_SIZING;
if (!pb_encode(&stream, fields, src_struct))
return false;
*size = stream.bytes_written;
return true;
}
/******************** /********************
* Helper functions * * Helper functions *
********************/ ********************/
@@ -405,9 +415,9 @@ bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
{ {
uint64_t zigzagged; uint64_t zigzagged;
if (value < 0) if (value < 0)
zigzagged = (uint64_t)(~(value << 1)); zigzagged = ~((uint64_t)value << 1);
else else
zigzagged = (uint64_t)(value << 1); zigzagged = (uint64_t)value << 1;
return pb_encode_varint(stream, zigzagged); return pb_encode_varint(stream, zigzagged);
} }
@@ -448,7 +458,7 @@ bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
{ {
uint64_t tag = wiretype | (field_number << 3); uint64_t tag = ((uint64_t)field_number << 3) | wiretype;
return pb_encode_varint(stream, tag); return pb_encode_varint(stream, tag);
} }
@@ -544,7 +554,7 @@ bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fie
/* Field encoders */ /* Field encoders */
bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
int64_t value = 0; int64_t value = 0;
@@ -562,7 +572,7 @@ bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, co
return pb_encode_varint(stream, (uint64_t)value); return pb_encode_varint(stream, (uint64_t)value);
} }
bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
uint64_t value = 0; uint64_t value = 0;
@@ -576,7 +586,7 @@ bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, c
return pb_encode_varint(stream, value); return pb_encode_varint(stream, value);
} }
bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
int64_t value = 0; int64_t value = 0;
@@ -590,19 +600,19 @@ bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, c
return pb_encode_svarint(stream, value); return pb_encode_svarint(stream, value);
} }
bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
UNUSED(field); UNUSED(field);
return pb_encode_fixed64(stream, src); return pb_encode_fixed64(stream, src);
} }
bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
UNUSED(field); UNUSED(field);
return pb_encode_fixed32(stream, src); return pb_encode_fixed32(stream, src);
} }
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src; const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src;
@@ -621,7 +631,7 @@ bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, con
return pb_encode_string(stream, bytes->bytes, bytes->size); return pb_encode_string(stream, bytes->bytes, bytes->size);
} }
bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
/* strnlen() is not always available, so just use a loop */ /* strnlen() is not always available, so just use a loop */
size_t size = 0; size_t size = 0;
@@ -647,7 +657,7 @@ bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, co
return pb_encode_string(stream, (const uint8_t*)src, size); return pb_encode_string(stream, (const uint8_t*)src, size);
} }
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src) static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{ {
if (field->ptr == NULL) if (field->ptr == NULL)
PB_RETURN_ERROR(stream, "invalid field descriptor"); PB_RETURN_ERROR(stream, "invalid field descriptor");

View File

@@ -71,6 +71,10 @@ bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_
*/ */
bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct); bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
/* Encode the message to get the size of the encoded data, but do not store
* the data. */
bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct);
/************************************** /**************************************
* Functions for manipulating streams * * Functions for manipulating streams *
**************************************/ **************************************/

View File

@@ -4,3 +4,18 @@ all:
clean: clean:
scons -c scons -c
coverage:
rm -rf build coverage
# LCOV does not like the newer gcov format
scons CC=gcc-4.6 CXX=gcc-4.6
# We are only interested in pb_encode.o and pb_decode.o
find build -name '*.gcda' -and \! \( -name '*pb_encode*' -or -name '*pb_decode*' \) -exec rm '{}' \;
# Collect the data
mkdir build/coverage
lcov --base-directory . --directory build/ --gcov-tool gcov-4.6 -c -o build/coverage/nanopb.info
# Generate HTML
genhtml -o build/coverage build/coverage/nanopb.info

View File

@@ -19,8 +19,8 @@ env = Environment(ENV = os.environ, tools = ['default', 'nanopb'])
# Allow overriding the compiler with scons CC=??? # Allow overriding the compiler with scons CC=???
if 'CC' in ARGUMENTS: env.Replace(CC = ARGUMENTS['CC']) if 'CC' in ARGUMENTS: env.Replace(CC = ARGUMENTS['CC'])
if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX']) if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
if 'CFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CFLAGS']) if 'CCFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CCFLAGS'])
if 'CXXFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CXXFLAGS']) if 'CXXFLAGS' in ARGUMENTS: env.Append(CXXFLAGS = ARGUMENTS['CXXFLAGS'])
# Add the builders defined in site_init.py # Add the builders defined in site_init.py
add_nanopb_builders(env) add_nanopb_builders(env)
@@ -33,13 +33,16 @@ env.Append(PROTOCPATH = '#../generator')
# Check the compilation environment, unless we are just cleaning up. # Check the compilation environment, unless we are just cleaning up.
if not env.GetOption('clean'): if not env.GetOption('clean'):
def check_ccflags(context, flags): def check_ccflags(context, flags, linkflags = ''):
'''Check if given CCFLAGS are supported''' '''Check if given CCFLAGS are supported'''
context.Message('Checking support for CCFLAGS="%s"... ' % flags) context.Message('Checking support for CCFLAGS="%s"... ' % flags)
oldflags = context.env['CCFLAGS'] oldflags = context.env['CCFLAGS']
oldlinkflags = context.env['CCFLAGS']
context.env.Append(CCFLAGS = flags) context.env.Append(CCFLAGS = flags)
context.env.Append(LINKFLAGS = linkflags)
result = context.TryCompile("int main() {return 0;}", '.c') result = context.TryCompile("int main() {return 0;}", '.c')
context.env.Replace(CCFLAGS = oldflags) context.env.Replace(CCFLAGS = oldflags)
context.env.Replace(LINKFLAGS = oldlinkflags)
context.Result(result) context.Result(result)
return result return result
@@ -50,6 +53,7 @@ if not env.GetOption('clean'):
stdint = conf.CheckCHeader('stdint.h') stdint = conf.CheckCHeader('stdint.h')
stddef = conf.CheckCHeader('stddef.h') stddef = conf.CheckCHeader('stddef.h')
string = conf.CheckCHeader('string.h') string = conf.CheckCHeader('string.h')
stdlib = conf.CheckCHeader('stdlib.h')
if not stdbool or not stdint or not stddef or not string: if not stdbool or not stdint or not stddef or not string:
conf.env.Append(CPPDEFINES = {'PB_SYSTEM_HEADER': '\\"pb_syshdr.h\\"'}) conf.env.Append(CPPDEFINES = {'PB_SYSTEM_HEADER': '\\"pb_syshdr.h\\"'})
conf.env.Append(CPPPATH = "#../extra") conf.env.Append(CPPPATH = "#../extra")
@@ -58,6 +62,7 @@ if not env.GetOption('clean'):
if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1}) if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1})
if stddef: conf.env.Append(CPPDEFINES = {'HAVE_STDDEF_H': 1}) if stddef: conf.env.Append(CPPDEFINES = {'HAVE_STDDEF_H': 1})
if string: conf.env.Append(CPPDEFINES = {'HAVE_STRING_H': 1}) if string: conf.env.Append(CPPDEFINES = {'HAVE_STRING_H': 1})
if stdlib: conf.env.Append(CPPDEFINES = {'HAVE_STDLIB_H': 1})
# Check if we can use pkg-config to find protobuf include path # Check if we can use pkg-config to find protobuf include path
status, output = conf.TryAction('pkg-config protobuf --variable=includedir > $TARGET') status, output = conf.TryAction('pkg-config protobuf --variable=includedir > $TARGET')
@@ -81,6 +86,13 @@ if not env.GetOption('clean'):
if conf.CheckCCFLAGS(extra): if conf.CheckCCFLAGS(extra):
conf.env.Append(CORECFLAGS = extra) conf.env.Append(CORECFLAGS = extra)
# Check if we can use undefined behaviour sanitizer (only with clang)
extra = '-fsanitize=undefined '
if 'clang' in env['CC']:
if conf.CheckCCFLAGS(extra, linkflags = extra):
conf.env.Append(CORECFLAGS = extra)
conf.env.Append(LINKFLAGS = extra)
# End the config stuff # End the config stuff
env = conf.Finish() env = conf.Finish()
@@ -89,15 +101,15 @@ if 'gcc' in env['CC']:
# GNU Compiler Collection # GNU Compiler Collection
# Debug info, warnings as errors # Debug info, warnings as errors
env.Append(CFLAGS = '-ansi -pedantic -g -O0 -Wall -Werror --coverage -fstack-protector-all') env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror -fprofile-arcs -ftest-coverage -fstack-protector-all')
env.Append(CORECFLAGS = '-Wextra') env.Append(CORECFLAGS = '-Wextra')
env.Append(LINKFLAGS = '--coverage') env.Append(LINKFLAGS = '-g --coverage')
# We currently need uint64_t anyway, even though ANSI C90 otherwise.. # We currently need uint64_t anyway, even though ANSI C90 otherwise..
env.Append(CFLAGS = '-Wno-long-long') env.Append(CFLAGS = '-Wno-long-long')
elif 'clang' in env['CC']: elif 'clang' in env['CC']:
# CLang # CLang
env.Append(CFLAGS = '-ansi -g -O0 -Wall -Werror') env.Append(CFLAGS = '-ansi -g -Wall -Werror')
env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion') env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion')
elif 'cl' in env['CC']: elif 'cl' in env['CC']:
# Microsoft Visual C++ # Microsoft Visual C++
@@ -117,10 +129,10 @@ elif 'tcc' in env['CC']:
env.SetDefault(CORECFLAGS = '') env.SetDefault(CORECFLAGS = '')
if 'clang++' in env['CXX']: if 'clang' in env['CXX']:
env.Append(CXXFLAGS = '-g -O0 -Wall -Werror -Wextra -Wno-missing-field-initializers') env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
elif 'g++' in env['CXX']: elif 'g++' in env['CXX'] or 'gcc' in env['CXX']:
env.Append(CXXFLAGS = '-g -O0 -Wall -Werror -Wextra -Wno-missing-field-initializers') env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers')
elif 'cl' in env['CXX']: elif 'cl' in env['CXX']:
env.Append(CXXFLAGS = '/Zi /W2 /WX') env.Append(CXXFLAGS = '/Zi /W2 /WX')
@@ -129,6 +141,6 @@ import os.path
env['VARIANT_DIR'] = 'build' env['VARIANT_DIR'] = 'build'
env['BUILD'] = '#' + env['VARIANT_DIR'] env['BUILD'] = '#' + env['VARIANT_DIR']
env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common' env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common'
for subdir in Glob('*/SConscript'): for subdir in Glob('*/SConscript') + Glob('regression/*/SConscript'):
SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir))) SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir)))

View File

@@ -19,10 +19,12 @@
the decoding and checks the fields. */ the decoding and checks the fields. */
bool check_alltypes(pb_istream_t *stream, int mode) bool check_alltypes(pb_istream_t *stream, int mode)
{ {
AllTypes alltypes; /* Uses _init_default to just make sure that it works. */
AllTypes alltypes = AllTypes_init_default;
/* Fill with garbage to better detect initialization errors */ /* Fill with garbage to better detect initialization errors */
memset(&alltypes, 0xAA, sizeof(alltypes)); memset(&alltypes, 0xAA, sizeof(alltypes));
alltypes.extensions = 0;
if (!pb_decode(stream, AllTypes_fields, &alltypes)) if (!pb_decode(stream, AllTypes_fields, &alltypes))
return false; return false;

View File

@@ -13,7 +13,7 @@ int main(int argc, char **argv)
int mode = (argc > 1) ? atoi(argv[1]) : 0; int mode = (argc > 1) ? atoi(argv[1]) : 0;
/* Initialize the structure with constants */ /* Initialize the structure with constants */
AllTypes alltypes = {0}; AllTypes alltypes = AllTypes_init_zero;
alltypes.req_int32 = -1001; alltypes.req_int32 = -1001;
alltypes.req_int64 = -1002; alltypes.req_int64 = -1002;

View File

@@ -220,6 +220,7 @@ bool check_alltypes(pb_istream_t *stream, int mode)
/* Fill with garbage to better detect initialization errors */ /* Fill with garbage to better detect initialization errors */
memset(&alltypes, 0xAA, sizeof(alltypes)); memset(&alltypes, 0xAA, sizeof(alltypes));
alltypes.extensions = 0;
alltypes.req_int32.funcs.decode = &read_varint; alltypes.req_int32.funcs.decode = &read_varint;
alltypes.req_int32.arg = (void*)-1001; alltypes.req_int32.arg = (void*)-1001;

View File

@@ -19,6 +19,7 @@ bool check_alltypes(pb_istream_t *stream, int mode)
/* Fill with garbage to better detect initialization errors */ /* Fill with garbage to better detect initialization errors */
memset(&alltypes, 0xAA, sizeof(alltypes)); memset(&alltypes, 0xAA, sizeof(alltypes));
alltypes.extensions = 0;
if (!pb_decode(stream, AllTypes_fields, &alltypes)) if (!pb_decode(stream, AllTypes_fields, &alltypes))
return false; return false;

View File

@@ -16,7 +16,7 @@
bool print_person(pb_istream_t *stream) bool print_person(pb_istream_t *stream)
{ {
int i; int i;
Person person; Person person = Person_init_zero;
if (!pb_decode(stream, Person_fields, &person)) if (!pb_decode(stream, Person_fields, &person))
return false; return false;

View File

@@ -12,7 +12,7 @@
bool print_person(pb_istream_t *stream) bool print_person(pb_istream_t *stream)
{ {
int i; int i;
Person person; Person person = Person_init_zero;
if (!pb_decode(stream, Person_fields, &person)) if (!pb_decode(stream, Person_fields, &person))
return false; return false;

View File

@@ -4,8 +4,6 @@ Import("env")
# Take copy of the files for custom build. # Take copy of the files for custom build.
c = Copy("$TARGET", "$SOURCE") c = Copy("$TARGET", "$SOURCE")
env.Command("pb_encode.c", "#../pb_encode.c", c)
env.Command("pb_decode.c", "#../pb_decode.c", c)
env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c) env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c) env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c)
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c) env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
@@ -15,9 +13,15 @@ env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
opts = env.Clone() opts = env.Clone()
opts.Append(CPPDEFINES = {'PB_BUFFER_ONLY': 1}) opts.Append(CPPDEFINES = {'PB_BUFFER_ONLY': 1})
# Build new version of core
strict = opts.Clone()
strict.Append(CFLAGS = strict['CORECFLAGS'])
strict.Object("pb_decode_bufonly.o", "$NANOPB/pb_decode.c")
strict.Object("pb_encode_bufonly.o", "$NANOPB/pb_encode.c")
# Now build and run the test normally. # Now build and run the test normally.
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"]) enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_bufonly.o"])
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"]) dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_bufonly.o"])
env.RunTest(enc) env.RunTest(enc)
env.RunTest([dec, "encode_alltypes.output"]) env.RunTest([dec, "encode_alltypes.output"])

View File

@@ -1,4 +1,5 @@
/* This includes the whole .c file to get access to static functions. */ /* This includes the whole .c file to get access to static functions. */
#define PB_ENABLE_MALLOC
#include "pb_decode.c" #include "pb_decode.c"
#include <stdio.h> #include <stdio.h>
@@ -299,6 +300,28 @@ int main()
dest.submsg.data_count == 5) dest.submsg.data_count == 5)
} }
{
pb_istream_t s = {0};
void *data = NULL;
COMMENT("Testing allocate_field")
TEST(allocate_field(&s, &data, 10, 10) && data != NULL);
TEST(allocate_field(&s, &data, 10, 20) && data != NULL);
{
void *oldvalue = data;
size_t very_big = (size_t)-1;
size_t somewhat_big = very_big / 2 + 1;
size_t not_so_big = (size_t)1 << (4 * sizeof(size_t));
TEST(!allocate_field(&s, &data, very_big, 2) && data == oldvalue);
TEST(!allocate_field(&s, &data, somewhat_big, 2) && data == oldvalue);
TEST(!allocate_field(&s, &data, not_so_big, not_so_big) && data == oldvalue);
}
pb_free(data);
}
if (status != 0) if (status != 0)
fprintf(stdout, "\n\nSome tests FAILED!\n"); fprintf(stdout, "\n\nSome tests FAILED!\n");

View File

@@ -282,6 +282,15 @@ int main()
"\x09\x0A\x07\x0A\x05\x01\x02\x03\x04\x05")) "\x09\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
} }
{
IntegerContainer msg = {{5, {1,2,3,4,5}}};
size_t size;
COMMENT("Test pb_get_encoded_size.")
TEST(pb_get_encoded_size(&size, IntegerContainer_fields, &msg) &&
size == 9);
}
{ {
uint8_t buffer[10]; uint8_t buffer[10];
pb_ostream_t s; pb_ostream_t s;

View File

@@ -49,7 +49,9 @@ int main(int argc, char **argv)
} }
/* Check that the extensions decoded properly */ /* Check that the extensions decoded properly */
TEST(ext1.found)
TEST(extensionfield1 == 12345) TEST(extensionfield1 == 12345)
TEST(ext2.found)
TEST(strcmp(extensionfield2.test1, "test") == 0) TEST(strcmp(extensionfield2.test1, "test") == 0)
TEST(extensionfield2.test2 == 54321) TEST(extensionfield2.test2 == 54321)

View File

@@ -1,7 +1,7 @@
import 'alltypes.proto'; import 'alltypes.proto';
extend AllTypes { extend AllTypes {
optional int32 AllTypes_extensionfield1 = 255; optional int32 AllTypes_extensionfield1 = 255 [default = 5];
} }
message ExtensionMessage { message ExtensionMessage {

View File

@@ -5,8 +5,6 @@ Import("env")
# Take copy of the files for custom build. # Take copy of the files for custom build.
c = Copy("$TARGET", "$SOURCE") c = Copy("$TARGET", "$SOURCE")
env.Command("pb_encode.c", "#../pb_encode.c", c)
env.Command("pb_decode.c", "#../pb_decode.c", c)
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c) env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c) env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
@@ -16,9 +14,15 @@ env.NanopbProto(["alltypes", "alltypes.options"])
opts = env.Clone() opts = env.Clone()
opts.Append(CPPDEFINES = {'PB_FIELD_16BIT': 1}) opts.Append(CPPDEFINES = {'PB_FIELD_16BIT': 1})
# Build new version of core
strict = opts.Clone()
strict.Append(CFLAGS = strict['CORECFLAGS'])
strict.Object("pb_decode_fields16.o", "$NANOPB/pb_decode.c")
strict.Object("pb_encode_fields16.o", "$NANOPB/pb_encode.c")
# Now build and run the test normally. # Now build and run the test normally.
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"]) enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields16.o"])
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"]) dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields16.o"])
env.RunTest(enc) env.RunTest(enc)
env.RunTest([dec, "encode_alltypes.output"]) env.RunTest([dec, "encode_alltypes.output"])

View File

@@ -107,5 +107,7 @@ message AllTypes {
// Just to make sure that the size of the fields has been calculated // Just to make sure that the size of the fields has been calculated
// properly, i.e. otherwise a bug in last field might not be detected. // properly, i.e. otherwise a bug in last field might not be detected.
required int32 end = 10099; required int32 end = 10099;
extensions 200 to 255;
} }

View File

@@ -5,8 +5,6 @@ Import("env")
# Take copy of the files for custom build. # Take copy of the files for custom build.
c = Copy("$TARGET", "$SOURCE") c = Copy("$TARGET", "$SOURCE")
env.Command("pb_encode.c", "#../pb_encode.c", c)
env.Command("pb_decode.c", "#../pb_decode.c", c)
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c) env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c) env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
@@ -16,9 +14,15 @@ env.NanopbProto(["alltypes", "alltypes.options"])
opts = env.Clone() opts = env.Clone()
opts.Append(CPPDEFINES = {'PB_FIELD_32BIT': 1}) opts.Append(CPPDEFINES = {'PB_FIELD_32BIT': 1})
# Build new version of core
strict = opts.Clone()
strict.Append(CFLAGS = strict['CORECFLAGS'])
strict.Object("pb_decode_fields32.o", "$NANOPB/pb_decode.c")
strict.Object("pb_encode_fields32.o", "$NANOPB/pb_encode.c")
# Now build and run the test normally. # Now build and run the test normally.
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"]) enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields32.o"])
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"]) dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields32.o"])
env.RunTest(enc) env.RunTest(enc)
env.RunTest([dec, "encode_alltypes.output"]) env.RunTest([dec, "encode_alltypes.output"])

View File

@@ -107,5 +107,7 @@ message AllTypes {
// Just to make sure that the size of the fields has been calculated // Just to make sure that the size of the fields has been calculated
// properly, i.e. otherwise a bug in last field might not be detected. // properly, i.e. otherwise a bug in last field might not be detected.
required int32 end = 13432099; required int32 end = 13432099;
extensions 200 to 255;
} }

View File

@@ -8,6 +8,7 @@
int main() int main()
{ {
uint8_t buffer[512]; uint8_t buffer[512];
size_t size;
/* Create a message with one missing field */ /* Create a message with one missing field */
{ {
@@ -19,12 +20,14 @@ int main()
printf("Encode failed.\n"); printf("Encode failed.\n");
return 1; return 1;
} }
size = stream.bytes_written;
} }
/* Test that it decodes properly if we don't require that field */ /* Test that it decodes properly if we don't require that field */
{ {
MissingField msg = {0}; MissingField msg = {0};
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer)); pb_istream_t stream = pb_istream_from_buffer(buffer, size);
if (!pb_decode(&stream, MissingField_fields, &msg)) if (!pb_decode(&stream, MissingField_fields, &msg))
{ {
@@ -36,7 +39,7 @@ int main()
/* Test that it does *not* decode properly if we require the field */ /* Test that it does *not* decode properly if we require the field */
{ {
AllFields msg = {0}; AllFields msg = {0};
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer)); pb_istream_t stream = pb_istream_from_buffer(buffer, size);
if (pb_decode(&stream, AllFields_fields, &msg)) if (pb_decode(&stream, AllFields_fields, &msg))
{ {

View File

@@ -4,8 +4,6 @@ Import("env")
# Take copy of the files for custom build. # Take copy of the files for custom build.
c = Copy("$TARGET", "$SOURCE") c = Copy("$TARGET", "$SOURCE")
env.Command("pb_encode.c", "#../pb_encode.c", c)
env.Command("pb_decode.c", "#../pb_decode.c", c)
env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c) env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c)
env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c) env.Command("alltypes.pb.c", "$BUILD/alltypes/alltypes.pb.c", c)
env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c) env.Command("encode_alltypes.c", "$BUILD/alltypes/encode_alltypes.c", c)
@@ -15,9 +13,15 @@ env.Command("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
opts = env.Clone() opts = env.Clone()
opts.Append(CPPDEFINES = {'PB_NO_ERRMSG': 1}) opts.Append(CPPDEFINES = {'PB_NO_ERRMSG': 1})
# Build new version of core
strict = opts.Clone()
strict.Append(CFLAGS = strict['CORECFLAGS'])
strict.Object("pb_decode_noerr.o", "$NANOPB/pb_decode.c")
strict.Object("pb_encode_noerr.o", "$NANOPB/pb_encode.c")
# Now build and run the test normally. # Now build and run the test normally.
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"]) enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_noerr.o"])
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"]) dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_noerr.o"])
env.RunTest(enc) env.RunTest(enc)
env.RunTest([dec, "encode_alltypes.output"]) env.RunTest([dec, "encode_alltypes.output"])

View File

@@ -5,3 +5,6 @@ pb_callback_t int32_callback;
\sEnumValue1 = 1 \sEnumValue1 = 1
Message5_EnumValue1 Message5_EnumValue1
} pb_packed my_packed_struct; } pb_packed my_packed_struct;
! skipped_field
! SkippedMessage

View File

@@ -63,11 +63,15 @@ message my_packed_struct
} }
// Message with ignored field // Message with ignored field
// Note: doesn't really test if the field is missing in the output,
// but atleast tests that the output compiles.
message Message6 message Message6
{ {
required int32 field1 = 1; required int32 field1 = 1;
optional int32 field2 = 2 [(nanopb).type = FT_IGNORE]; optional int32 skipped_field = 2 [(nanopb).type = FT_IGNORE];
} }
// Message that is skipped
message SkippedMessage
{
option (nanopb_msgopt).skip_message = true;
required int32 foo = 1;
}

View File

@@ -0,0 +1,9 @@
# Regression test for Issue 125: Wrong identifier name for extension fields
Import("env")
env.NanopbProto(["extensionbug", "extensionbug.options"])
env.Object('extensionbug.pb.c')
env.Match(['extensionbug.pb.h', 'extensionbug.expected'])

View File

@@ -0,0 +1,3 @@
pb_extension_type_t Message2_extras
uint32_t field2

View File

@@ -0,0 +1,4 @@
* type:FT_IGNORE
Message2.extras type:FT_STATIC
Message2.field2 type:FT_STATIC

View File

@@ -0,0 +1,16 @@
message Message1
{
optional uint32 fieldA = 1;
extensions 30 to max;
}
message Message2
{
extend Message1
{
optional Message2 extras = 30;
}
optional uint32 field1 = 1;
optional uint32 field2 = 2;
}

View File

@@ -85,9 +85,20 @@ def add_nanopb_builders(env):
data = open(str(source[0]), 'rU').read() data = open(str(source[0]), 'rU').read()
patterns = open(str(source[1])) patterns = open(str(source[1]))
for pattern in patterns: for pattern in patterns:
if pattern.strip() and not re.search(pattern.strip(), data, re.MULTILINE): if pattern.strip():
invert = False
if pattern.startswith('! '):
invert = True
pattern = pattern[2:]
status = re.search(pattern.strip(), data, re.MULTILINE)
if not status and not invert:
print '\033[31m[FAIL]\033[0m Pattern not found in ' + str(source[0]) + ': ' + pattern print '\033[31m[FAIL]\033[0m Pattern not found in ' + str(source[0]) + ': ' + pattern
return 1 return 1
elif status and invert:
print '\033[31m[FAIL]\033[0m Pattern should not exist, but does in ' + str(source[0]) + ': ' + pattern
return 1
else: else:
print '\033[32m[ OK ]\033[0m All patterns found in ' + str(source[0]) print '\033[32m[ OK ]\033[0m All patterns found in ' + str(source[0])
return 0 return 0

13
tests/splint/SConscript Normal file
View File

@@ -0,0 +1,13 @@
# Check the nanopb core using splint
Import('env')
p = env.WhereIs('splint')
if p:
env.Command('pb_decode.splint', '$NANOPB/pb_decode.c',
'splint -f splint/splint.rc $SOURCE 2> $TARGET')
env.Command('pb_encode.splint', '$NANOPB/pb_encode.c',
'splint -f splint/splint.rc $SOURCE 2> $TARGET')

37
tests/splint/splint.rc Normal file
View File

@@ -0,0 +1,37 @@
+checks
+partial
+matchanyintegral
+strictlib
-isoreserved # to be fixed in 0.3
-nullassign
-predboolint
-predboolptr
+ptrnegate
-switchloopbreak
+ignoresigns
-infloopsuncon
-type
# splint's memory checks don't quite work without annotations
-mustfreeonly
-compmempass
-nullret
-observertrans
-statictrans
-compdestroy
-nullpass
-nullstate
-compdef
-usereleased
-temptrans
-dependenttrans
-kepttrans
-branchstate
-immediatetrans
# These tests give false positives, compiler typically has
# better warnings for these.
-noret
-noeffect
-usedef