aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 06:59:49 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 06:59:49 +0000
commit540217503587066e5b15b4671e91c6d4acaf6a4f (patch)
tree8f78338f2989299f5ed8e666e44a3d4b2fe593ed
parent0f2d3e09f9d5780ad353fe62fbcf4c0424f40de7 (diff)
parente2379c6e49f0d429956865c6917d08b0a3430670 (diff)
downloadnanopb-c-android13-mainline-wifi-release.tar.gz
Change-Id: Ieaed0b40e53e09c3914dab44f97cea2334bfc23b
-rw-r--r--.github/workflows/spm.yml15
-rw-r--r--.gitignore30
-rw-r--r--.travis.yml61
-rw-r--r--AUTHORS.txt13
-rw-r--r--Android.bp5
-rw-r--r--CHANGELOG.txt50
-rw-r--r--CMakeLists.txt59
-rw-r--r--METADATA9
-rw-r--r--MODULE_LICENSE_BSD_LIKE (renamed from MODULE_LICENSE_PUBLIC_DOMAIN)0
-rw-r--r--OWNERS4
-rw-r--r--Package.swift63
-rw-r--r--README.android12
-rw-r--r--README.md16
-rw-r--r--README.version3
-rw-r--r--docs/concepts.rst5
-rw-r--r--docs/migration.rst35
-rw-r--r--docs/reference.rst4
-rw-r--r--docs/security.rst1
-rw-r--r--examples/network_server/common.c3
-rw-r--r--examples/simple/rules.mk24
-rw-r--r--examples/simple/simple.pb.c19
-rw-r--r--examples/simple/simple.pb.h51
-rw-r--r--extra/FindNanopb.cmake6
-rw-r--r--extra/nanopb-config-version.cmake.in22
-rw-r--r--extra/nanopb-config.cmake2
-rwxr-xr-xextra/poetry/poetry_build.sh9
-rw-r--r--extra/poetry/pyproject.toml26
-rwxr-xr-xgenerator/nanopb_generator.py221
-rw-r--r--generator/proto/__init__.py19
-rw-r--r--generator/proto/nanopb.proto9
-rw-r--r--library.json2
-rw-r--r--pb.h57
-rw-r--r--pb_decode.c98
-rw-r--r--pb_decode.h7
-rw-r--r--pb_encode.c62
-rw-r--r--pb_encode.h2
-rw-r--r--spm-test/objc/c-header.c3
-rw-r--r--spm-test/objc/objc-header.m3
-rw-r--r--spm-test/objc/objc-module.m1
-rw-r--r--spm-test/objc/objc-qualified.m3
-rw-r--r--spm-test/swift/main.swift1
l---------spm_headers/nanopb/pb.h1
l---------spm_headers/nanopb/pb_common.h1
l---------spm_headers/nanopb/pb_decode.h1
l---------spm_headers/nanopb/pb_encode.h1
-rw-r--r--spm_headers/pb.h1
-rw-r--r--spm_headers/pb_common.h1
-rw-r--r--spm_headers/pb_decode.h1
-rw-r--r--spm_headers/pb_encode.h1
-rw-r--r--tests/SConstruct18
-rw-r--r--tests/alltypes/decode_alltypes.c1
-rw-r--r--tests/encode_arrays_unpacked/SConscript28
-rw-r--r--tests/enum_minmax/SConscript8
-rw-r--r--tests/enum_minmax/enumminmax.proto22
-rw-r--r--tests/enum_minmax/enumminmax_unittests.c16
-rw-r--r--tests/fuzztest/fuzztest.c34
-rw-r--r--tests/mem_release/mem_release.c71
-rw-r--r--tests/mem_release/mem_release.proto6
-rw-r--r--tests/multiple_files/subdir/multifile2.proto5
-rw-r--r--tests/multiple_files/test_multiple_files.c3
-rw-r--r--tests/regression/issue_338/SConscript7
-rw-r--r--tests/regression/issue_338/bigvalue.proto204
-rw-r--r--tests/regression/issue_342/SConscript21
-rw-r--r--tests/regression/issue_342/extensions.proto11
-rw-r--r--tests/regression/issue_342/test_extensions.c52
-rw-r--r--tests/regression/issue_363/SConscript14
-rw-r--r--tests/regression/issue_363/oneofmsg.proto10
-rw-r--r--tests/regression/issue_363/test_oneofmsg.c42
-rw-r--r--tests/regression/issue_376/SConscript14
-rw-r--r--tests/regression/issue_376/fixed_array.options1
-rw-r--r--tests/regression/issue_376/fixed_array.proto11
-rw-r--r--tests/regression/issue_376/test_fixarray.c40
-rw-r--r--tests/regression/issue_380/SConscript9
-rw-r--r--tests/regression/issue_380/manglenames.options1
-rw-r--r--tests/regression/issue_380/manglenames.proto16
-rw-r--r--tests/regression/issue_395/SConscript14
-rw-r--r--tests/regression/issue_395/test.c38
-rw-r--r--tests/regression/issue_395/test.options2
-rw-r--r--tests/regression/issue_395/test.proto37
-rw-r--r--tests/regression/issue_504/SConscript12
-rw-r--r--tests/regression/issue_504/test.c47
-rw-r--r--tests/regression/issue_504/test.proto13
-rw-r--r--tests/regression/issue_547/SConscript21
-rw-r--r--tests/regression/issue_547/test.c28
-rw-r--r--tests/regression/issue_547/test.proto7
-rw-r--r--tests/regression/issue_611/SConscript8
-rw-r--r--tests/regression/issue_611/uenum.expected3
-rw-r--r--tests/regression/issue_611/uenum.proto28
-rw-r--r--tests/regression/issue_647/SConscript12
-rw-r--r--tests/regression/issue_647/repro.c16
-rw-r--r--tests/regression/issue_647/repro.proto10
-rw-r--r--tests/typename_mangling/SConscript20
-rw-r--r--tests/typename_mangling/test_flatten.c22
-rw-r--r--tests/typename_mangling/test_strip_package.c19
-rw-r--r--tests/typename_mangling/with_package.options5
-rw-r--r--tests/typename_mangling/with_package.proto38
-rwxr-xr-xtools/set_version.sh2
97 files changed, 1838 insertions, 272 deletions
diff --git a/.github/workflows/spm.yml b/.github/workflows/spm.yml
new file mode 100644
index 0000000..999851a
--- /dev/null
+++ b/.github/workflows/spm.yml
@@ -0,0 +1,15 @@
+name: spm
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ swift-build-run:
+ runs-on: macOS-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Build
+ run: swift build
+ - name: Run
+ run: swift test
diff --git a/.gitignore b/.gitignore
index c9b568f..e5d09f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,30 @@
+*.gcda
+*.gcno
+*.gcov
+*.o
+*.pb.c
+*.pb.h
+*.pb
*.pyc
-*.swp
+*_pb2.py
+*~
+*.tar.gz
+.sconsign.dblite
+config.log
+.sconf_temp
+tests/build
+julkaisu.txt
+dist
+docs/*.html
+docs/generator_flow.png
+examples/simple/simple
+examples/network_server/client
+examples/network_server/server
+examples/using_double_on_avr/decode_double
+examples/using_double_on_avr/encode_double
+examples/using_double_on_avr/test_conversions
+examples/using_union_messages/decode
+examples/using_union_messages/encode
+generator/nanopb_pb2.pyc
+!generator-bin/**/*
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..20688b0
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,61 @@
+# Travis CI has no ability to handle 3 langauges (c, c++, python)
+# and it overrides $CC/$CXX if language is set to c/c++ (only one, not both).
+#
+# Set language to python since at least the result of that is something useful.
+language: python
+
+python:
+ - "2.7"
+ - "3.4"
+
+# Manage the C/C++ compiler manually
+env:
+ - CC=gcc CXX=g++
+ - CC=gcc-4.8 CXX=g++-4.8
+ - CC=gcc-4.9 CXX=g++-4.9
+ - CC=gcc-5 CXX=g++-5
+ - CC=clang CXX=clang++
+
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - gcc-4.8
+ - g++-4.8
+ - gcc-4.9
+ - g++-4.9
+ - gcc-5
+ - g++-5
+ - scons
+
+
+before_install:
+ - export PATH=$HOME/.local/bin:$HOME/protobuf/bin:$PATH
+ - export MAKEFLAGS=-j$(nproc)
+ - $CC --version
+ - $CXX --version
+ - python --version
+ - lsb_release -a
+
+# Seems to be issues with concurrent builds
+#cache:
+# directories:
+# - $HOME/protobuf
+
+# Rather then compile protobuf 3 from source, use the binaries now available
+# to speed up build time and reduce surprises until Ubuntu adds protobuf3
+# packages to the repository.
+install:
+ - mkdir -p $HOME/protobuf && pushd $HOME/protobuf
+ && curl -LO 'https://github.com/google/protobuf/releases/download/v3.4.0/protoc-3.4.0-linux-x86_64.zip'
+ && unzip protoc-3.4.0-linux-x86_64.zip
+ && popd
+ - curl -L 'https://github.com/google/protobuf/releases/download/v3.4.0/protobuf-python-3.4.0.tar.gz' | tar xzf -
+ && pushd protobuf-3.4.0/python
+ && python setup.py build && python setup.py install
+ && popd
+
+script:
+ - pushd generator/proto && make && popd
+ - pushd tests && scons CC=$CC CXX=$CXX && popd
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 0034abf..707faaa 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -42,4 +42,15 @@ Edward Z. Yang <ezyang@mit.edu>
Robbie Shade <rjshade@google.com>
Andrew Ballinger <andrewballinger@stratisopt.com>
Hamina, Juha-Pekka <Juha-Pekka.Hamina@nordicsemi.no>
-
+Jason Bishop <jason.bishop@bigassfans.com>
+matejcik <ja@matejcik.cz>
+Tobias Müller <Tobias_Mueller@twam.info>
+Jari Vetoniemi <mailroxas@gmail.com>
+Gabriel Staples <ercaguy@gmail.com>
+Amarnath <amarnath.h.96@gmail.com>
+Michal Rostecki <mrostecki@suse.de>
+Pei Wang <wangpei10@baidu.com>
+Noah Pendleton <2538614+noahp@users.noreply.github.com>
+Pavol Rusnak <pavol@rusnak.io>
+Franck <franck.sehedic@ledger.fr>
+Paul Beusterien <paulbeusterien@google.com>
diff --git a/Android.bp b/Android.bp
index bd07587..e942ad9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -74,6 +74,11 @@ cc_defaults {
cc_library_static {
name: "libprotobuf-c-nano",
defaults: ["libprotobuf-c-nano-defaults"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth",
+ ],
+ min_sdk_version: "30",
}
cc_library_static {
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index f7811a4..44e952b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,4 +1,52 @@
-nanopb-0.3.9.1 (2017-04-xx)
+nanopb-0.3.9.8 (2021-03-22)
+ Fix invalid free() with oneof (#647, GHSA-7mv5-5mxh-qg88)
+ Don't generate lines with trailing spaces (#622)
+ Verify stream size before allocating string / bytes (#620)
+
+nanopb-0.3.9.7 (2020-11-25)
+ Fix memory leak with oneofs and PB_ENABLE_MALLOC (#615, GHSA-85rr-4rh9-hhwh)
+ Fix unsigned enums not working correctly inside OneOf (#611)
+ Add '--version' option to nanopb_generator.py (#607)
+ SwiftPM rule updates (#567, #585)
+
+nanopb-0.3.9.6 (2020-06-23)
+ Fix buffer overflow when encoding bytes with size set to 65535 (#547, GHSA-3p39-mfxg-hrq4)
+ Fix proto3 submessage improperly considered empty (#504)
+ Fix ImportError when using generator/protoc with Python 3
+ Add build rules for Swift package manager (#549)
+
+nanopb-0.3.9.5 (2020-02-02)
+ Fix invalid free() after failed realloc() (GHSA-gcx3-7m76-287p)
+ Add workaround for avr-libc realloc() bug (#475)
+ Fix empty submessages getting encoded in proto3 mode (#395)
+ Avoid overflows in allocation for packed fields.
+
+nanopb-0.3.9.4 (2019-10-13)
+ Fix undefined behavior with bool fields (#434)
+ Fix enum min/max defines when values are not in order (#405)
+ Fix network_server socket example with zero-length strings (#421)
+ Don't call stream read callback with count=0 (#421)
+ Add compile time flag PB_ENCODE_ARRAYS_UNPACKED (#427)
+
+nanopb-0.3.9.3 (2019-03-08)
+ Fix fixed size and callback repeated fields inside proto3 submessages (#376, #382, #386)
+ Fix incorrect PB_STATIC_ASSERT for bytes inside oneof (#363)
+ Fix generator error with mangle_names option (#380)
+ Generator: Allow comma separated options in plugin mode (#343)
+
+nanopb-0.3.9.2 (2018-11-10)
+ Erroneous free() when using callbacks combined with PB_ENABLE_MALLOC (#346)
+ Fix possible null-pointer dereference in decode_callback_field (#342)
+ Fix FindNanopb.cmake on Windows (#335)
+ Fix large generator memory usage with oneof fields (#338)
+ Fix error in splint test (#359)
+ Allow cmake to build as a shared library (#352, #353)
+ Add --no-strip-path command line option (#326)
+ Option for flattening nested protobuf names (#333)
+ Documentation fixes (#329, #350, #358)
+ Better error messages (#351)
+
+nanopb-0.3.9.1 (2018-04-14)
Fix handling of special characters in string/bytes default values (issue #322)
Fix encoding of negative numbers with PB_WITHOUT_64BIT (#285)
Fix _zero initializer for enums that don't begin at 0. (#295)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2625a66..b16fbbf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,10 +2,14 @@ cmake_minimum_required(VERSION 2.8.12)
project(nanopb C)
-set(nanopb_VERSION_STRING nanopb-0.3.9.1)
+set(nanopb_VERSION_STRING nanopb-0.3.9.8)
+set(nanopb_SOVERSION 0)
string(REPLACE "nanopb-" "" nanopb_VERSION ${nanopb_VERSION_STRING})
+option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
+option(BUILD_STATIC_LIBS "Build static libraries" ON)
+
option(nanopb_BUILD_RUNTIME "Build the headers and libraries needed at runtime" ON)
option(nanopb_BUILD_GENERATOR "Build the protoc plugin for code generation" ON)
option(nanopb_MSVC_STATIC_RUNTIME "Link static runtime libraries" ON)
@@ -31,7 +35,7 @@ if(MSVC AND nanopb_MSVC_STATIC_RUNTIME)
endif()
if(NOT DEFINED CMAKE_INSTALL_CMAKEDIR)
- set(CMAKE_INSTALL_CMAKEDIR "lib/cmake/nanopb")
+ set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/nanopb")
endif()
if(nanopb_BUILD_GENERATOR)
@@ -62,25 +66,46 @@ if(nanopb_BUILD_GENERATOR)
endif()
if(nanopb_BUILD_RUNTIME)
- add_library(protobuf-nanopb STATIC
- pb.h
- pb_common.h
- pb_common.c
- pb_encode.h
- pb_encode.c
- pb_decode.h
- pb_decode.c)
-
- target_include_directories(protobuf-nanopb INTERFACE
- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
- )
+ if(BUILD_SHARED_LIBS)
+ add_library(protobuf-nanopb SHARED
+ pb.h
+ pb_common.h
+ pb_common.c
+ pb_encode.h
+ pb_encode.c
+ pb_decode.h
+ pb_decode.c)
+ set_target_properties(protobuf-nanopb PROPERTIES
+ SOVERSION ${nanopb_SOVERSION})
+ install(TARGETS protobuf-nanopb EXPORT nanopb-targets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ target_include_directories(protobuf-nanopb INTERFACE
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ )
+ endif()
+
+ if(BUILD_STATIC_LIBS)
+ add_library(protobuf-nanopb-static STATIC
+ pb.h
+ pb_common.h
+ pb_common.c
+ pb_encode.h
+ pb_encode.c
+ pb_decode.h
+ pb_decode.c)
+ set_target_properties(protobuf-nanopb-static PROPERTIES
+ OUTPUT_NAME protobuf-nanopb)
+ install(TARGETS protobuf-nanopb-static EXPORT nanopb-targets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ target_include_directories(protobuf-nanopb-static INTERFACE
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+ )
+ endif()
configure_file(extra/nanopb-config-version.cmake.in
nanopb-config-version.cmake @ONLY)
- install(TARGETS protobuf-nanopb EXPORT nanopb-targets
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
-
install(EXPORT nanopb-targets
DESTINATION ${CMAKE_INSTALL_CMAKEDIR}
NAMESPACE nanopb::)
diff --git a/METADATA b/METADATA
index d97975c..e25a888 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,12 @@
+name: "nanopb"
+description: "Nanopb - Protocol Buffers for Embedded Systems"
+
third_party {
+ url {
+ type: GIT
+ value: "https://github.com/nanopb/nanopb"
+ }
+ version: "0.3.9.8"
+ last_upgrade_date { year: 2021 month: 12 day: 6 }
license_type: NOTICE
}
diff --git a/MODULE_LICENSE_PUBLIC_DOMAIN b/MODULE_LICENSE_BSD_LIKE
index e69de29..e69de29 100644
--- a/MODULE_LICENSE_PUBLIC_DOMAIN
+++ b/MODULE_LICENSE_BSD_LIKE
diff --git a/OWNERS b/OWNERS
index e0fd5b8..bf734b3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,3 @@
-# Default code reviewers picked from top 3 or more developers.
-# Please update this list if you find better candidates.
shanyu@google.com
-jeffbailey@google.com
rtenneti@google.com
+krzysio@google.com
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..1c62f98
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,63 @@
+// swift-tools-version:5.0
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "nanopb",
+ products: [
+ .library(
+ name: "nanopb",
+ targets: ["nanopb"]
+ )
+ ],
+
+ targets: [
+ .target(
+ name: "nanopb",
+ path: ".",
+ sources: [
+ "pb.h",
+ "pb_common.h",
+ "pb_common.c",
+ "pb_decode.h",
+ "pb_decode.c",
+ "pb_encode.h",
+ "pb_encode.c"
+ ],
+ publicHeadersPath: "spm_headers",
+ cSettings: [
+ .define("PB_FIELD_32BIT", to: "1"),
+ .define("PB_NO_PACKED_STRUCTS", to: "1"),
+ .define("PB_ENABLE_MALLOC", to: "1"),
+ ]
+ ),
+ .testTarget(
+ name: "swift-test",
+ dependencies: [
+ "nanopb",
+ ],
+ path: "spm-test/swift",
+ cSettings: [
+ .headerSearchPath("../"),
+ .define("PB_FIELD_32BIT", to: "1"),
+ .define("PB_NO_PACKED_STRUCTS", to: "1"),
+ .define("PB_ENABLE_MALLOC", to: "1"),
+ ]
+ ),
+ .testTarget(
+ name: "objc-test",
+ dependencies: [
+ "nanopb",
+ ],
+ path: "spm-test/objc",
+ cSettings: [
+ .headerSearchPath("../"),
+ .define("PB_FIELD_32BIT", to: "1"),
+ .define("PB_NO_PACKED_STRUCTS", to: "1"),
+ .define("PB_ENABLE_MALLOC", to: "1"),
+ ]
+ )
+ ]
+)
+
diff --git a/README.android b/README.android
deleted file mode 100644
index 5ce0117..0000000
--- a/README.android
+++ /dev/null
@@ -1,12 +0,0 @@
-URL: https://jpa.kapsi.fi/nanopb/
-Version: 0.3.9.1
-License: zlib
-Description: "nanopb-c: A plugin for Google Protobuf compiler that generates C code"
-
-To use with Android modify Android.mk adding .proto files to
-LOCAL_SRC_FILES and set LOCAL_PROTOC_OPTIMIZE_TYPE:
-
- LOCAL_SRC_FILES += simple.proto
- LOCAL_PROTOC_OPTIMIZE_TYPE := nanopb-c
-
-Then look at https://jpa.kapsi.fi/nanopb/ Documentation for how to use nanopb-c.
diff --git a/README.md b/README.md
index 07860f0..1a73cdd 100644
--- a/README.md
+++ b/README.md
@@ -18,8 +18,8 @@ Using the nanopb library
------------------------
To use the nanopb library, you need to do two things:
-1. Compile your .proto files for nanopb, using protoc.
-2. Include pb_encode.c, pb_decode.c and pb_common.c in your project.
+1. Compile your .proto files for nanopb, using `protoc`.
+2. Include *pb_encode.c*, *pb_decode.c* and *pb_common.c* in your project.
The easiest way to get started is to study the project in "examples/simple".
It contains a Makefile, which should work directly under most Linux systems.
@@ -30,23 +30,23 @@ README.txt in that folder.
Using the Protocol Buffers compiler (protoc)
--------------------------------------------
-The nanopb generator is implemented as a plugin for the Google's own protoc
+The nanopb generator is implemented as a plugin for the Google's own `protoc`
compiler. This has the advantage that there is no need to reimplement the
basic parsing of .proto files. However, it does mean that you need the
Google's protobuf library in order to run the generator.
If you have downloaded a binary package for nanopb (either Windows, Linux or
-Mac OS X version), the 'protoc' binary is included in the 'generator-bin'
+Mac OS X version), the `protoc` binary is included in the 'generator-bin'
folder. In this case, you are ready to go. Simply run this command:
generator-bin/protoc --nanopb_out=. myprotocol.proto
However, if you are using a git checkout or a plain source distribution, you
-need to provide your own version of protoc and the Google's protobuf library.
-On Linux, the necessary packages are protobuf-compiler and python-protobuf.
+need to provide your own version of `protoc` and the Google's protobuf library.
+On Linux, the necessary packages are `protobuf-compiler` and `python-protobuf`.
On Windows, you can either build Google's protobuf library from source or use
one of the binary distributions of it. In either case, if you use a separate
-protoc, you need to manually give the path to nanopb generator:
+`protoc`, you need to manually give the path to nanopb generator:
protoc --plugin=protoc-gen-nanopb=nanopb/generator/protoc-gen-nanopb ...
@@ -57,7 +57,7 @@ Running the tests
If you want to perform further development of the nanopb core, or to verify
its functionality using your compiler and platform, you'll want to run the
test suite. The build rules for the test suite are implemented using Scons,
-so you need to have that installed. To run the tests:
+so you need to have that installed (ex: `sudo apt install scons` on Ubuntu). To run the tests:
cd tests
scons
diff --git a/README.version b/README.version
deleted file mode 100644
index b0e5805..0000000
--- a/README.version
+++ /dev/null
@@ -1,3 +0,0 @@
-URL: https://github.com/nanopb/nanopb/archive/0.3.9.1.tar.gz
-Version: 0.3.9.1
-BugComponent: 31808
diff --git a/docs/concepts.rst b/docs/concepts.rst
index 1f9aec1..168e564 100644
--- a/docs/concepts.rst
+++ b/docs/concepts.rst
@@ -317,6 +317,11 @@ The user is expected to set the filed manually using the correct field tag::
Notice that neither ``which_payload`` field nor the unused fileds in ``payload``
will consume any space in the resulting encoded message.
+When a C union is used to represent a ``oneof`` section, the union cannot have
+callback fields or nested callback fields. Otherwise, the decoding process may
+fail. If callbacks must be used inside a ``oneof`` section, the generator
+option *no_unions* should be set to *true* for that section.
+
.. _`oneof`: https://developers.google.com/protocol-buffers/docs/reference/proto2-spec#oneof_and_oneof_field
Extension fields
diff --git a/docs/migration.rst b/docs/migration.rst
index fdc1d8c..d358600 100644
--- a/docs/migration.rst
+++ b/docs/migration.rst
@@ -11,7 +11,40 @@ are included, in order to make it easier to find this document.
.. contents ::
-Nanopb-0.3.9.1, 0.4.0 (2018-xx-xx)
+Nanopb-0.3.9.4, 0.4.0 (2019-xx-xx)
+==================================
+
+Fix generation of min/max defines for enum types
+------------------------------------------------
+
+**Rationale:** Nanopb generator makes #defines for enum minimum and maximum
+value. Previously these defines incorrectly had the first and last enum value,
+instead of the actual minimum and maximum. (issue #405)
+
+**Changes:** Minimum define now always has the smallest value, and maximum
+define always has the largest value.
+
+**Required actions:** If these defines are used and enum values in .proto file
+are not defined in ascending order, user code behaviour may change. Check that
+user code doesn't expect the old, incorrect first/last behaviour.
+
+Fix undefined behavior related to bool fields
+---------------------------------------------
+
+**Rationale:** In C99, `bool` variables are not allowed to have other values
+than `true` and `false`. Compilers use this fact in optimization, and constructs
+like `int foo = msg.has_field ? 100 : 0` will give unexpected results otherwise.
+Previously nanopb didn't enforce that decoded bool fields had valid values.
+
+**Changes:** Bool fields are now handled separately as `PB_LTYPE_BOOL`. The
+`LTYPE` descriptor numbers for other field types were renumbered.
+
+**Required actions:** Source code files must be recompiled, but regenerating
+`.pb.h`/`.pb.c` files from `.proto` is not required. If user code directly uses
+the nanopb internal field representation (search for `PB_LTYPE_VARINT` in source),
+it may need updating.
+
+Nanopb-0.3.9.1, 0.4.0 (2018-04-14)
==================================
Fix handling of string and bytes default values
diff --git a/docs/reference.rst b/docs/reference.rst
index 40a69ad..89d1f0f 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -55,6 +55,10 @@ PB_SYSTEM_HEADER Replace the standard header files with a single
for example *#define PB_SYSTEM_HEADER "foo.h"*.
PB_WITHOUT_64BIT Disable 64-bit support, for old compilers or
for a slight speedup on 8-bit platforms.
+PB_ENCODE_ARRAYS_UNPACKED Don't encode scalar arrays as packed.
+ This is only to be used when the decoder on the
+ receiving side cannot process packed scalar
+ arrays. Such example is older protobuf.js.
============================ ================================================
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow
diff --git a/docs/security.rst b/docs/security.rst
index d854612..6f7152e 100644
--- a/docs/security.rst
+++ b/docs/security.rst
@@ -58,6 +58,7 @@ untrusted data has been maliciously crafted:
- The *count* fields of arrays will not exceed the array size.
- The *size* field of bytes will not exceed the allocated size.
- All string fields will have null terminator.
+ - bool fields will have valid true/false values (since nanopb-0.3.9.4)
5. After pb_encode() returns successfully, the resulting message is a valid
protocol buffers message. (Except if user-defined callbacks write incorrect
diff --git a/examples/network_server/common.c b/examples/network_server/common.c
index 04a5aa8..05048f6 100644
--- a/examples/network_server/common.c
+++ b/examples/network_server/common.c
@@ -19,6 +19,9 @@ static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
int fd = (intptr_t)stream->state;
int result;
+ if (count == 0)
+ return true;
+
result = recv(fd, buf, count, MSG_WAITALL);
if (result == 0)
diff --git a/examples/simple/rules.mk b/examples/simple/rules.mk
deleted file mode 100644
index 1e4221d..0000000
--- a/examples/simple/rules.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-NANOPB_DIR := external/nanopb-0.3.9.1-linux-x86
-
-LOCAL_DIR := $(GET_LOCAL_DIR)
-
-MODULE := $(LOCAL_DIR)
-
-MODULE_SRCS += \
- $(LOCAL_DIR)/simple.c \
- $(LOCAL_DIR)/simple.pb.c \
- $(NANOPB_DIR)/pb_encode.c \
- $(NANOPB_DIR)/pb_decode.c \
- $(NANOPB_DIR)/pb_common.c
-
-MODULE_CPPFLAGS += -std=c++11
-
-MODULE_INCLUDES += \
- $(NANOPB_DIR)
-
-$(LOCAL_DIR)/simple.pb.c: $(LOCAL_DIR)/simple.proto
- $(PROTOC) $(PROTOC_OPTS) --nanopb_out=$(LOCAL_DIR) $(LOCAL_DIR)/simple.proto
-
-include $(NANOPB_DIR)/extra/nanopb.mk
-include make/module.mk
-
diff --git a/examples/simple/simple.pb.c b/examples/simple/simple.pb.c
deleted file mode 100644
index 481aa7d..0000000
--- a/examples/simple/simple.pb.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Automatically generated nanopb constant definitions */
-/* Generated by nanopb-0.3.9.1 at Fri May 25 16:28:54 2018. */
-
-#include "simple.pb.h"
-
-/* @@protoc_insertion_point(includes) */
-#if PB_PROTO_HEADER_VERSION != 30
-#error Regenerate this file with the current version of nanopb generator.
-#endif
-
-
-
-const pb_field_t SimpleMessage_fields[2] = {
- PB_FIELD( 1, INT32 , REQUIRED, STATIC , FIRST, SimpleMessage, lucky_number, lucky_number, 0),
- PB_LAST_FIELD
-};
-
-
-/* @@protoc_insertion_point(eof) */
diff --git a/examples/simple/simple.pb.h b/examples/simple/simple.pb.h
deleted file mode 100644
index f4614db..0000000
--- a/examples/simple/simple.pb.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Automatically generated nanopb header */
-/* Generated by nanopb-0.3.9.1 at Fri May 25 16:28:54 2018. */
-
-#ifndef PB_SIMPLE_PB_H_INCLUDED
-#define PB_SIMPLE_PB_H_INCLUDED
-#include <pb.h>
-
-/* @@protoc_insertion_point(includes) */
-#if PB_PROTO_HEADER_VERSION != 30
-#error Regenerate this file with the current version of nanopb generator.
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Struct definitions */
-typedef struct _SimpleMessage {
- int32_t lucky_number;
-/* @@protoc_insertion_point(struct:SimpleMessage) */
-} SimpleMessage;
-
-/* Default values for struct fields */
-
-/* Initializer values for message structs */
-#define SimpleMessage_init_default {0}
-#define SimpleMessage_init_zero {0}
-
-/* Field tags (for use in manual encoding/decoding) */
-#define SimpleMessage_lucky_number_tag 1
-
-/* Struct field encoding specification for nanopb */
-extern const pb_field_t SimpleMessage_fields[2];
-
-/* Maximum encoded size of messages (where known) */
-#define SimpleMessage_size 11
-
-/* Message IDs (where set with "msgid" option) */
-#ifdef PB_MSGID
-
-#define SIMPLE_MESSAGES \
-
-
-#endif
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-/* @@protoc_insertion_point(eof) */
-
-#endif
diff --git a/extra/FindNanopb.cmake b/extra/FindNanopb.cmake
index bba341d..7730251 100644
--- a/extra/FindNanopb.cmake
+++ b/extra/FindNanopb.cmake
@@ -149,7 +149,11 @@ function(NANOPB_GENERATE_CPP SRCS HDRS)
set(GENERATOR_PATH ${CMAKE_BINARY_DIR}/nanopb/generator)
set(NANOPB_GENERATOR_EXECUTABLE ${GENERATOR_PATH}/nanopb_generator.py)
- set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb)
+ if (WIN32)
+ set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb.bat)
+ else()
+ set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb)
+ endif()
set(GENERATOR_CORE_DIR ${GENERATOR_PATH}/proto)
set(GENERATOR_CORE_SRC
diff --git a/extra/nanopb-config-version.cmake.in b/extra/nanopb-config-version.cmake.in
index 1b51027..f9292a8 100644
--- a/extra/nanopb-config-version.cmake.in
+++ b/extra/nanopb-config-version.cmake.in
@@ -1,11 +1,11 @@
-set(PACKAGE_VERSION "@nanopb_VERSION@")
-
-# Check whether the requested PACKAGE_FIND_VERSION is compatible
-if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
- set(PACKAGE_VERSION_COMPATIBLE FALSE)
-else()
- set(PACKAGE_VERSION_COMPATIBLE TRUE)
- if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
- set(PACKAGE_VERSION_EXACT TRUE)
- endif()
-endif()
+set(PACKAGE_VERSION "@nanopb_VERSION@")
+
+# Check whether the requested PACKAGE_FIND_VERSION is compatible
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+ set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+ set(PACKAGE_VERSION_COMPATIBLE TRUE)
+ if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+ set(PACKAGE_VERSION_EXACT TRUE)
+ endif()
+endif()
diff --git a/extra/nanopb-config.cmake b/extra/nanopb-config.cmake
index 42d977a..4f726a6 100644
--- a/extra/nanopb-config.cmake
+++ b/extra/nanopb-config.cmake
@@ -1 +1 @@
-include(${CMAKE_CURRENT_LIST_DIR}/nanopb-targets.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/nanopb-targets.cmake)
diff --git a/extra/poetry/poetry_build.sh b/extra/poetry/poetry_build.sh
new file mode 100755
index 0000000..d428d39
--- /dev/null
+++ b/extra/poetry/poetry_build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+rm -rf nanopb
+mkdir nanopb
+cp -pr ../../generator nanopb/
+touch nanopb/__init__.py nanopb/generator/__init__.py
+cp -pr ../../README.md .
+sed -i -e 's/\(version =.*\)-dev.*/\1-dev'$(git rev-list HEAD --count)'"/' pyproject.toml
+poetry build
diff --git a/extra/poetry/pyproject.toml b/extra/poetry/pyproject.toml
new file mode 100644
index 0000000..e3a5586
--- /dev/null
+++ b/extra/poetry/pyproject.toml
@@ -0,0 +1,26 @@
+[tool.poetry]
+name = "nanopb"
+version = "0.3.9.8"
+description = "Nanopb is a small code-size Protocol Buffers implementation in ansi C. It is especially suitable for use in microcontrollers, but fits any memory restricted system."
+authors = ["Petteri Aimonen <jpa@npb.mail.kapsi.fi>"]
+license = "Zlib"
+repository = "https://github.com/nanopb/nanopb/"
+readme = "README.md"
+homepage = "https://jpa.kapsi.fi/nanopb/"
+documentation = "https://jpa.kapsi.fi/nanopb/docs/index.html"
+keywords = ["protobuf", "protoc"]
+classifiers = ["Topic :: Software Development :: Build Tools"]
+
+[tool.poetry.scripts]
+nanopb_generator = "nanopb.generator.nanopb_generator:main_cli"
+protoc-gen-nanopb = "nanopb.generator.nanopb_generator:main_plugin"
+
+[tool.poetry.dependencies]
+python = ">=2.7"
+protobuf = ">=3.6"
+
+[tool.poetry.dev-dependencies]
+
+[build-system]
+requires = ["poetry>=0.12"]
+build-backend = "poetry.masonry.api"
diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 5b74ca1..b4f1d83 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -3,17 +3,20 @@
from __future__ import unicode_literals
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
-nanopb_version = "nanopb-0.3.9.1"
+nanopb_version = "nanopb-0.3.9.8"
import sys
import re
import codecs
+import copy
from functools import reduce
try:
# Add some dummy imports to keep packaging tools happy.
import google, distutils.util # bbfreeze seems to need these
import pkg_resources # pyinstaller / protobuf 2.5 seem to need these
+ import proto.nanopb_pb2 as nanopb_pb2 # pyinstaller seems to need this
+ import proto.plugin_pb2 as plugin_pb2
except:
# Don't care, we will error out later if it is actually important.
pass
@@ -31,8 +34,7 @@ except:
raise
try:
- import proto.nanopb_pb2 as nanopb_pb2
- import proto.plugin_pb2 as plugin_pb2
+ from .proto import nanopb_pb2, plugin_pb2
except TypeError:
sys.stderr.write('''
****************************************************************************
@@ -44,9 +46,16 @@ except TypeError:
*** which protoc ***
*** protoc --version ***
*** python -c 'import google.protobuf; print(google.protobuf.__file__)' ***
+ *** If you are not able to find the python protobuf version using the ***
+ *** above command, use this command. ***
+ *** pip freeze | grep -i protobuf ***
****************************************************************************
''' + '\n')
raise
+except (ValueError, SystemError, ImportError):
+ # Probably invoked directly instead of via installed scripts.
+ import proto.nanopb_pb2 as nanopb_pb2
+ import proto.plugin_pb2 as plugin_pb2
except:
sys.stderr.write('''
********************************************************************
@@ -95,11 +104,14 @@ try:
except NameError:
strtypes = (str, )
+
class Names:
'''Keeps a set of nested names and formats them to C identifier.'''
def __init__(self, parts = ()):
if isinstance(parts, Names):
parts = parts.parts
+ elif isinstance(parts, strtypes):
+ parts = (parts,)
self.parts = tuple(parts)
def __str__(self):
@@ -108,6 +120,8 @@ class Names:
def __add__(self, other):
if isinstance(other, strtypes):
return Names(self.parts + (other,))
+ elif isinstance(other, Names):
+ return Names(self.parts + other.parts)
elif isinstance(other, tuple):
return Names(self.parts + other)
else:
@@ -183,12 +197,15 @@ class Enum:
'''desc is EnumDescriptorProto'''
self.options = enum_options
- self.names = names + desc.name
+ self.names = names
+
+ # by definition, `names` include this enum's name
+ base_name = Names(names.parts[:-1])
if enum_options.long_names:
- self.values = [(self.names + x.name, x.number) for x in desc.value]
- else:
self.values = [(names + x.name, x.number) for x in desc.value]
+ else:
+ self.values = [(base_name + x.name, x.number) for x in desc.value]
self.value_longnames = [self.names + x.name for x in desc.value]
self.packed = enum_options.packed_enum
@@ -212,9 +229,12 @@ class Enum:
result += ' %s;' % self.names
- result += '\n#define _%s_MIN %s' % (self.names, self.values[0][0])
- result += '\n#define _%s_MAX %s' % (self.names, self.values[-1][0])
- result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, self.values[-1][0])
+ # sort the enum by value
+ sorted_values = sorted(self.values, key = lambda x: (x[1], x[0]))
+
+ result += '\n#define _%s_MIN %s' % (self.names, sorted_values[0][0])
+ result += '\n#define _%s_MAX %s' % (self.names, sorted_values[-1][0])
+ result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, sorted_values[-1][0])
if not self.options.long_names:
# Define the long names always so that enum value references
@@ -336,7 +356,7 @@ class Field:
if field_options.type == nanopb_pb2.FT_STATIC and not can_be_static:
raise Exception("Field '%s' is defined as static, but max_size or "
"max_count is not given." % self.name)
-
+
if field_options.fixed_count and self.max_count is None:
raise Exception("Field '%s' is defined as fixed count, "
"but max_count is not given." % self.name)
@@ -606,7 +626,15 @@ class Field:
'''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly.
Returns numeric value or a C-expression for assert.'''
check = []
- if self.pbtype == 'MESSAGE' and self.allocation == 'STATIC':
+
+ need_check = False
+
+ if self.pbtype == 'BYTES' and self.allocation == 'STATIC' and self.max_size > 251:
+ need_check = True
+ elif self.pbtype == 'MESSAGE' and self.allocation == 'STATIC':
+ need_check = True
+
+ if need_check:
if self.rules == 'REPEATED':
check.append('pb_membersize(%s, %s[0])' % (self.struct_name, self.name))
elif self.rules == 'ONEOF':
@@ -616,9 +644,6 @@ class Field:
check.append('pb_membersize(%s, %s.%s)' % (self.struct_name, self.union_name, self.name))
else:
check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name))
- elif self.pbtype == 'BYTES' and self.allocation == 'STATIC':
- if self.max_size > 251:
- check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name))
return FieldMaxSize([self.tag, self.max_size, self.max_count],
check,
@@ -640,6 +665,13 @@ class Field:
if encsize is not None:
# Include submessage length prefix
encsize += varint_max_size(encsize.upperlimit())
+ else:
+ my_msg = dependencies.get(str(self.struct_name))
+ if my_msg and submsg.protofile == my_msg.protofile:
+ # The dependency is from the same file and size cannot be
+ # determined for it, thus we know it will not be possible
+ # in runtime either.
+ return None
if encsize is None:
# Submessage or its size cannot be found.
@@ -719,8 +751,8 @@ class ExtensionRange(Field):
return EncodedSize(0)
class ExtensionField(Field):
- def __init__(self, struct_name, desc, field_options):
- self.fullname = struct_name + desc.name
+ def __init__(self, fullname, desc, field_options):
+ self.fullname = fullname
self.extendee_name = names_from_type_name(desc.extendee)
Field.__init__(self, self.fullname + 'struct', desc, field_options)
@@ -845,24 +877,33 @@ class OneOf(Field):
def encoded_size(self, dependencies):
'''Returns the size of the largest oneof field.'''
- largest = EncodedSize(0)
- symbols = set()
+ largest = 0
+ symbols = []
for f in self.fields:
size = EncodedSize(f.encoded_size(dependencies))
- if size.value is None:
+ if size is None or size.value is None:
return None
elif size.symbols:
- symbols.add(EncodedSize(f.submsgname + 'size').symbols[0])
- elif size.value > largest.value:
- largest = size
+ symbols.append((f.tag, size.symbols[0]))
+ elif size.value > largest:
+ largest = size.value
if not symbols:
+ # Simple case, all sizes were known at generator time
return largest
- symbols = list(symbols)
- symbols.append(str(largest))
- max_size = lambda x, y: '({0} > {1} ? {0} : {1})'.format(x, y)
- return reduce(max_size, symbols)
+ if largest > 0:
+ # Some sizes were known, some were not
+ symbols.insert(0, (0, largest))
+
+ if len(symbols) == 1:
+ # Only one symbol was needed
+ return EncodedSize(5, [symbols[0][1]])
+ else:
+ # Use sizeof(union{}) construct to find the maximum size of
+ # submessages.
+ union_def = ' '.join('char f%d[%s];' % s for s in symbols)
+ return EncodedSize(5, ['sizeof(union{%s})' % union_def])
# ---------------------------------------------------------------------------
# Generation of messages (structures)
@@ -969,6 +1010,15 @@ class Message:
result += default + '\n'
return result
+ def all_fields(self):
+ '''Iterate over all fields in this message, including nested OneOfs.'''
+ for f in self.fields:
+ if isinstance(f, OneOf):
+ for f2 in f.fields:
+ yield f2
+ else:
+ yield f
+
def count_required_fields(self):
'''Returns number of required fields inside this message'''
count = 0
@@ -1021,7 +1071,7 @@ class Message:
# Processing of entire .proto files
# ---------------------------------------------------------------------------
-def iterate_messages(desc, names = Names()):
+def iterate_messages(desc, flatten = False, names = Names()):
'''Recursively find all messages. For each, yield name, DescriptorProto.'''
if hasattr(desc, 'message_type'):
submsgs = desc.message_type
@@ -1030,19 +1080,22 @@ def iterate_messages(desc, names = Names()):
for submsg in submsgs:
sub_names = names + submsg.name
- yield sub_names, submsg
+ if flatten:
+ yield Names(submsg.name), submsg
+ else:
+ yield sub_names, submsg
- for x in iterate_messages(submsg, sub_names):
+ for x in iterate_messages(submsg, flatten, sub_names):
yield x
-def iterate_extensions(desc, names = Names()):
+def iterate_extensions(desc, flatten = False, names = Names()):
'''Recursively find all extensions.
For each, yield name, FieldDescriptorProto.
'''
for extension in desc.extension:
yield names, extension
- for subname, subdesc in iterate_messages(desc, names):
+ for subname, subdesc in iterate_messages(desc, flatten, names):
for extension in subdesc.extension:
yield subname, extension
@@ -1104,43 +1157,79 @@ class ProtoFile:
self.messages = []
self.extensions = []
+ mangle_names = self.file_options.mangle_names
+ flatten = mangle_names == nanopb_pb2.M_FLATTEN
+ strip_prefix = None
+ if mangle_names == nanopb_pb2.M_STRIP_PACKAGE:
+ strip_prefix = "." + self.fdesc.package
+
+ def create_name(names):
+ if mangle_names == nanopb_pb2.M_NONE:
+ return base_name + names
+ elif mangle_names == nanopb_pb2.M_STRIP_PACKAGE:
+ return Names(names)
+ else:
+ single_name = names
+ if isinstance(names, Names):
+ single_name = names.parts[-1]
+ return Names(single_name)
+
+ def mangle_field_typename(typename):
+ if mangle_names == nanopb_pb2.M_FLATTEN:
+ return "." + typename.split(".")[-1]
+ elif strip_prefix is not None and typename.startswith(strip_prefix):
+ return typename[len(strip_prefix):]
+ else:
+ return typename
+
if self.fdesc.package:
base_name = Names(self.fdesc.package.split('.'))
else:
base_name = Names()
for enum in self.fdesc.enum_type:
- enum_options = get_nanopb_suboptions(enum, self.file_options, base_name + enum.name)
- self.enums.append(Enum(base_name, enum, enum_options))
+ name = create_name(enum.name)
+ enum_options = get_nanopb_suboptions(enum, self.file_options, name)
+ self.enums.append(Enum(name, enum, enum_options))
- for names, message in iterate_messages(self.fdesc, base_name):
- message_options = get_nanopb_suboptions(message, self.file_options, names)
+ for names, message in iterate_messages(self.fdesc, flatten):
+ name = create_name(names)
+ message_options = get_nanopb_suboptions(message, self.file_options, name)
if message_options.skip_message:
continue
- self.messages.append(Message(names, message, message_options))
+ message = copy.deepcopy(message)
+ for field in message.field:
+ if field.type in (FieldD.TYPE_MESSAGE, FieldD.TYPE_ENUM):
+ field.type_name = mangle_field_typename(field.type_name)
+
+ self.messages.append(Message(name, message, message_options))
for enum in message.enum_type:
- enum_options = get_nanopb_suboptions(enum, message_options, names + enum.name)
- self.enums.append(Enum(names, enum, enum_options))
+ name = create_name(names + enum.name)
+ enum_options = get_nanopb_suboptions(enum, message_options, name)
+ self.enums.append(Enum(name, enum, enum_options))
- for names, extension in iterate_extensions(self.fdesc, base_name):
- field_options = get_nanopb_suboptions(extension, self.file_options, names + extension.name)
+ for names, extension in iterate_extensions(self.fdesc, flatten):
+ name = create_name(names + extension.name)
+ field_options = get_nanopb_suboptions(extension, self.file_options, name)
if field_options.type != nanopb_pb2.FT_IGNORE:
- self.extensions.append(ExtensionField(names, extension, field_options))
+ self.extensions.append(ExtensionField(name, extension, field_options))
def add_dependency(self, other):
for enum in other.enums:
self.dependencies[str(enum.names)] = enum
+ enum.protofile = other
for msg in other.messages:
self.dependencies[str(msg.name)] = msg
+ msg.protofile = other
# Fix field default values where enum short names are used.
for enum in other.enums:
if not enum.options.long_names:
for message in self.messages:
- for field in message.fields:
+ for field in message.all_fields():
if field.default in enum.value_longnames:
idx = enum.value_longnames.index(field.default)
field.default = enum.values[idx][0]
@@ -1149,7 +1238,7 @@ class ProtoFile:
for enum in other.enums:
if not enum.has_negative():
for message in self.messages:
- for field in message.fields:
+ for field in message.all_fields():
if field.pbtype == 'ENUM' and field.ctype == enum.names:
field.pbtype = 'UENUM'
@@ -1350,7 +1439,7 @@ class ProtoFile:
msgs = '_'.join(str(n) for n in checks_msgnames)
yield '/* If you get an error here, it means that you need to define PB_FIELD_32BIT\n'
yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
- yield ' * \n'
+ yield ' *\n'
yield ' * The reason you need to do this is that some of your messages contain tag\n'
yield ' * numbers or field sizes that are larger than what can fit in 8 or 16 bit\n'
yield ' * field descriptors.\n'
@@ -1367,7 +1456,7 @@ class ProtoFile:
msgs = '_'.join(str(n) for n in checks_msgnames)
yield '/* If you get an error here, it means that you need to define PB_FIELD_16BIT\n'
yield ' * compile-time option. You can do that in pb.h or on compiler command line.\n'
- yield ' * \n'
+ yield ' *\n'
yield ' * The reason you need to do this is that some of your messages contain tag\n'
yield ' * numbers or field sizes that are larger than what can fit in the default\n'
yield ' * 8 bit descriptors.\n'
@@ -1378,7 +1467,7 @@ class ProtoFile:
# Add check for sizeof(double)
has_double = False
for msg in self.messages:
- for field in msg.fields:
+ for field in msg.all_fields():
if field.ctype == 'double':
has_double = True
@@ -1490,6 +1579,8 @@ optparser = OptionParser(
usage = "Usage: nanopb_generator.py [options] file.pb ...",
epilog = "Compile file.pb from file.proto by: 'protoc -ofile.pb file.proto'. " +
"Output will be written to file.pb.h and file.pb.c.")
+optparser.add_option("--version", dest="version", action="store_true",
+ help="Show version info and exit")
optparser.add_option("-x", dest="exclude", metavar="FILE", action="append", default=[],
help="Exclude file from generated #include list.")
optparser.add_option("-e", "--extension", dest="extension", metavar="EXTENSION", default=".pb",
@@ -1512,6 +1603,10 @@ optparser.add_option("-Q", "--generated-include-format", dest="genformat",
optparser.add_option("-L", "--library-include-format", dest="libformat",
metavar="FORMAT", default='#include <%s>\n',
help="Set format string to use for including the nanopb pb.h header. [default: %default]")
+optparser.add_option("--strip-path", dest="strip_path", action="store_true", default=True,
+ help="Strip directory path from #included .pb.h file name [default: %default]")
+optparser.add_option("--no-strip-path", dest="strip_path", action="store_false",
+ help="Opposite of --strip-path")
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,
@@ -1589,7 +1684,11 @@ def process_file(filename, fdesc, options, other_files = {}):
noext = os.path.splitext(filename)[0]
headername = noext + options.extension + options.header_extension
sourcename = noext + options.extension + options.source_extension
- headerbasename = os.path.basename(headername)
+
+ if options.strip_path:
+ headerbasename = os.path.basename(headername)
+ else:
+ headerbasename = headername
# List of .proto files that should not be included in the C header file
# even if they are mentioned in the source .proto.
@@ -1615,6 +1714,10 @@ def main_cli():
options, filenames = optparser.parse_args()
+ if options.version:
+ print(nanopb_version)
+ sys.exit(0)
+
if not filenames:
optparser.print_help()
sys.exit(1)
@@ -1628,6 +1731,7 @@ def main_cli():
sys.exit(1)
if options.verbose:
+ sys.stderr.write("Nanopb version %s\n" % nanopb_version)
sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
% (google.protobuf.__file__, google.protobuf.__version__))
@@ -1672,11 +1776,34 @@ def main_plugin():
import shlex
args = shlex.split(params)
+
+ if len(args) == 1 and ',' in args[0]:
+ # For compatibility with other protoc plugins, support options
+ # separated by comma.
+ lex = shlex.shlex(params)
+ lex.whitespace_split = True
+ lex.whitespace = ','
+ args = list(lex)
+
+ optparser.usage = "Usage: protoc --nanopb_out=[options][,more_options]:outdir file.proto"
+ optparser.epilog = "Output will be written to file.pb.h and file.pb.c."
+
+ if '-h' in args or '--help' in args:
+ # By default optparser prints help to stdout, which doesn't work for
+ # protoc plugins.
+ optparser.print_help(sys.stderr)
+ sys.exit(1)
+
options, dummy = optparser.parse_args(args)
+ if options.version:
+ sys.stderr.write('%s\n' % (nanopb_version))
+ sys.exit(0)
+
Globals.verbose_options = options.verbose
if options.verbose:
+ sys.stderr.write("Nanopb version %s\n" % nanopb_version)
sys.stderr.write('Google Python protobuf library imported from %s, version %s\n'
% (google.protobuf.__file__, google.protobuf.__version__))
diff --git a/generator/proto/__init__.py b/generator/proto/__init__.py
index e69de29..96b2ee0 100644
--- a/generator/proto/__init__.py
+++ b/generator/proto/__init__.py
@@ -0,0 +1,19 @@
+'''This file automatically rebuilds the proto definitions for Python.'''
+
+import os
+import os.path
+import sys
+import subprocess
+
+dirname = os.path.dirname(__file__)
+protosrc = os.path.join(dirname, "nanopb.proto")
+protodst = os.path.join(dirname, "nanopb_pb2.py")
+
+if os.path.isfile(protosrc):
+ src_date = os.path.getmtime(protosrc)
+ if not os.path.isfile(protodst) or os.path.getmtime(protodst) < src_date:
+ cmd = ["protoc", "--python_out=.", "nanopb.proto", "plugin.proto"]
+ status = subprocess.call(cmd, cwd = dirname)
+ if status != 0:
+ sys.stderr.write("Failed to build nanopb_pb2.py: " + ' '.join(cmd) + "\n")
+
diff --git a/generator/proto/nanopb.proto b/generator/proto/nanopb.proto
index 0c05a2b..95a57c4 100644
--- a/generator/proto/nanopb.proto
+++ b/generator/proto/nanopb.proto
@@ -27,6 +27,12 @@ enum IntSize {
IS_64 = 64;
}
+enum TypenameMangling {
+ M_NONE = 0; // Default, no typename mangling
+ M_STRIP_PACKAGE = 1; // Strip current package name
+ M_FLATTEN = 2; // Only use last path component
+}
+
// This is the inner options message, which basically defines options for
// a field. When it is used in message or file scope, it applies to all
// fields.
@@ -83,6 +89,9 @@ message NanoPBOptions {
// Generate repeated field with fixed count
optional bool fixed_count = 16 [default = false];
+
+ // Mangle long names
+ optional TypenameMangling mangle_names = 17 [default = M_NONE];
}
// Extensions to protoc 'Descriptor' type in order to define options
diff --git a/library.json b/library.json
index b070e54..cbd1528 100644
--- a/library.json
+++ b/library.json
@@ -1,6 +1,6 @@
{
"name": "Nanopb",
- "version": "0.3.9.1",
+ "version": "0.3.9.8",
"keywords": "protocol buffers, protobuf, google",
"description": "Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.",
"repository": {
diff --git a/pb.h b/pb.h
index 52d24de..7e8a694 100644
--- a/pb.h
+++ b/pb.h
@@ -38,6 +38,11 @@
/* #define PB_OLD_CALLBACK_STYLE */
+/* Don't encode scalar arrays as packed. This is only to be used when
+ * the decoder on the receiving side cannot process packed scalar arrays.
+ * Such example is older protobuf.js. */
+/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */
+
/******************************************************************
* You usually don't need to change anything below this line. *
* Feel free to look around and use the defined macros, though. *
@@ -46,7 +51,7 @@
/* Version of the nanopb library. Just in case you want to check it in
* your own program. */
-#define NANOPB_VERSION nanopb-0.3.9.1
+#define NANOPB_VERSION nanopb-0.3.9.8
/* Include all the system headers needed by nanopb. You will need the
* definitions of the following:
@@ -122,11 +127,6 @@
#define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
#define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##LINE##COUNTER
#endif
-#ifndef STATIC_ASSERT
-#define STATIC_ASSERT PB_STATIC_ASSERT
-#define STATIC_ASSERT_MSG PB_STATIC_ASSERT_MSG
-#define STATIC_ASSERT_MSG_ PB_STATIC_ASSERT_MSG_
-#endif
#else
#define PB_STATIC_ASSERT(COND,MSG)
#endif
@@ -150,39 +150,40 @@ typedef uint_least8_t pb_type_t;
/**** Field data types ****/
/* Numeric types */
-#define PB_LTYPE_VARINT 0x00 /* int32, int64, enum, bool */
-#define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */
-#define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */
-#define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */
-#define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */
+#define PB_LTYPE_BOOL 0x00 /* bool */
+#define PB_LTYPE_VARINT 0x01 /* int32, int64, enum, bool */
+#define PB_LTYPE_UVARINT 0x02 /* uint32, uint64 */
+#define PB_LTYPE_SVARINT 0x03 /* sint32, sint64 */
+#define PB_LTYPE_FIXED32 0x04 /* fixed32, sfixed32, float */
+#define PB_LTYPE_FIXED64 0x05 /* fixed64, sfixed64, double */
/* Marker for last packable field type. */
-#define PB_LTYPE_LAST_PACKABLE 0x04
+#define PB_LTYPE_LAST_PACKABLE 0x05
/* Byte array with pre-allocated buffer.
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
-#define PB_LTYPE_BYTES 0x05
+#define PB_LTYPE_BYTES 0x06
/* String with pre-allocated buffer.
* data_size is the maximum length. */
-#define PB_LTYPE_STRING 0x06
+#define PB_LTYPE_STRING 0x07
/* Submessage
* submsg_fields is pointer to field descriptions */
-#define PB_LTYPE_SUBMESSAGE 0x07
+#define PB_LTYPE_SUBMESSAGE 0x08
/* Extension pseudo-field
* The field contains a pointer to pb_extension_t */
-#define PB_LTYPE_EXTENSION 0x08
+#define PB_LTYPE_EXTENSION 0x09
/* Byte array with inline, pre-allocated byffer.
* data_size is the length of the inline, allocated buffer.
* This differs from PB_LTYPE_BYTES by defining the element as
* pb_byte_t[data_size] rather than pb_bytes_array_t. */
-#define PB_LTYPE_FIXED_LENGTH_BYTES 0x09
+#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0A
/* Number of declared LTYPES */
-#define PB_LTYPES_COUNT 0x0A
+#define PB_LTYPES_COUNT 0x0B
#define PB_LTYPE_MASK 0x0F
/**** Field repetition rules ****/
@@ -194,7 +195,7 @@ typedef uint_least8_t pb_type_t;
#define PB_HTYPE_MASK 0x30
/**** Field allocation types ****/
-
+
#define PB_ATYPE_STATIC 0x00
#define PB_ATYPE_POINTER 0x80
#define PB_ATYPE_CALLBACK 0x40
@@ -242,7 +243,7 @@ struct pb_field_s {
pb_ssize_t size_offset; /* Offset of array size or has-boolean, relative to data */
pb_size_t data_size; /* Data size in bytes for a single item */
pb_size_t array_size; /* Maximum number of entries in array */
-
+
/* Field definitions for submessage
* OR default value for all other non-array, non-callback types
* If null, then field will zeroed. */
@@ -308,8 +309,8 @@ struct pb_callback_s {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
} funcs;
-#endif
-
+#endif
+
/* Free arg for use by callback */
void *arg;
};
@@ -339,7 +340,7 @@ struct pb_extension_type_s {
*/
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
uint32_t tag, pb_wire_type_t wire_type);
-
+
/* Called once after all regular fields have been encoded.
* If you have something to write, do so and return true.
* If you do not have anything to write, just return true.
@@ -347,7 +348,7 @@ struct pb_extension_type_s {
* Set to NULL for default handler.
*/
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
-
+
/* Free field for use by the callback. */
const void *arg;
};
@@ -356,11 +357,11 @@ struct pb_extension_s {
/* Type describing the extension field. Usually you'll initialize
* this to a pointer to the automatically generated structure. */
const pb_extension_type_t *type;
-
+
/* Destination for the decoded data. This must match the datatype
* of the extension field. */
void *dest;
-
+
/* Pointer to the next extension handler, or NULL.
* If this extension does not match a field, the next handler is
* automatically called. */
@@ -467,7 +468,7 @@ struct pb_extension_s {
#define PB_SINGULAR_CALLBACK(tag, st, m, fd, ltype, ptr) \
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
fd, 0, pb_membersize(st, m), 0, ptr}
-
+
#define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \
fd, 0, pb_membersize(st, m), 0, ptr}
@@ -491,7 +492,7 @@ struct pb_extension_s {
PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr)
/* The mapping from protobuf types to LTYPEs is done using these macros. */
-#define PB_LTYPE_MAP_BOOL PB_LTYPE_VARINT
+#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
diff --git a/pb_decode.c b/pb_decode.c
index 4b80e81..4efe8a3 100644
--- a/pb_decode.c
+++ b/pb_decode.c
@@ -34,6 +34,7 @@ static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_
static bool checkreturn find_extension_field(pb_field_iter_t *iter);
static void pb_field_set_to_default(pb_field_iter_t *iter);
static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct);
+static bool checkreturn pb_dec_bool(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);
static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof);
static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
@@ -65,6 +66,7 @@ static void pb_release_single_field(const pb_field_iter_t *iter);
* Order in the array must match pb_action_t LTYPE numbering.
*/
static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
+ &pb_dec_bool,
&pb_dec_varint,
&pb_dec_uvarint,
&pb_dec_svarint,
@@ -99,6 +101,9 @@ static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t co
bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
{
+ if (count == 0)
+ return true;
+
#ifndef PB_BUFFER_ONLY
if (buf == NULL && stream->callback != buf_read)
{
@@ -459,14 +464,17 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
}
case PB_HTYPE_ONEOF:
- *(pb_size_t*)iter->pSize = iter->pos->tag;
- if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+ if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE &&
+ *(pb_size_t*)iter->pSize != iter->pos->tag)
{
/* We memset to zero so that any callbacks are set to NULL.
- * Then set any default values. */
+ * This is because the callbacks might otherwise have values
+ * from some other union field. */
memset(iter->pData, 0, iter->pos->data_size);
pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData);
}
+ *(pb_size_t*)iter->pSize = iter->pos->tag;
+
return func(stream, iter->pos, iter->pData);
default:
@@ -486,6 +494,16 @@ static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t
if (data_size == 0 || array_size == 0)
PB_RETURN_ERROR(stream, "invalid size");
+#ifdef __AVR__
+ /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284
+ * Realloc to size of 1 byte can cause corruption of the malloc structures.
+ */
+ if (data_size == 1 && array_size == 1)
+ {
+ data_size = 2;
+ }
+#endif
+
/* 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
@@ -592,12 +610,25 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
while (substream.bytes_left)
{
+ if (*size == PB_SIZE_MAX)
+ {
+#ifndef PB_NO_ERRMSG
+ stream->errmsg = "too many array entries";
+#endif
+ status = false;
+ break;
+ }
+
if ((size_t)*size + 1 > allocated_size)
{
/* Allocate more storage. This tries to guess the
* number of remaining entries. Round the division
* upwards. */
- allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1;
+ size_t remain = (substream.bytes_left - 1) / iter->pos->data_size + 1;
+ if (remain < PB_SIZE_MAX - allocated_size)
+ allocated_size += remain;
+ else
+ allocated_size += 1;
if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size))
{
@@ -615,15 +646,6 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
break;
}
- if (*size == PB_SIZE_MAX)
- {
-#ifndef PB_NO_ERRMSG
- stream->errmsg = "too many array entries";
-#endif
- status = false;
- break;
- }
-
(*size)++;
}
if (!pb_close_string_substream(stream, &substream))
@@ -640,11 +662,11 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
if (*size == PB_SIZE_MAX)
PB_RETURN_ERROR(stream, "too many array entries");
- (*size)++;
- if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size))
+ if (!allocate_field(stream, iter->pData, iter->pos->data_size, (size_t)(*size + 1)))
return false;
- pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1);
+ pItem = *(char**)iter->pData + iter->pos->data_size * (*size);
+ (*size)++;
initialize_pointer_field(pItem, iter);
return func(stream, iter->pos, pItem);
}
@@ -658,15 +680,20 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
{
pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
-
#ifdef PB_OLD_CALLBACK_STYLE
- void *arg = pCallback->arg;
+ void *arg;
#else
- void **arg = &(pCallback->arg);
+ void **arg;
#endif
if (pCallback == NULL || pCallback->funcs.decode == NULL)
return pb_skip_field(stream, wire_type);
+
+#ifdef PB_OLD_CALLBACK_STYLE
+ arg = pCallback->arg;
+#else
+ arg = &(pCallback->arg);
+#endif
if (wire_type == PB_WT_STRING)
{
@@ -1123,7 +1150,14 @@ static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter)
* This shouldn't fail unless the pb_field_t structure is corrupted. */
if (!pb_field_iter_find(iter, new_tag))
PB_RETURN_ERROR(stream, "iterator error");
-
+
+ if (PB_ATYPE(iter->pos->type) == PB_ATYPE_POINTER)
+ {
+ /* Initialize the pointer to NULL to make sure it is valid
+ * even in case of error return. */
+ *(void**)iter->pData = NULL;
+ }
+
return true;
}
@@ -1153,7 +1187,7 @@ static void pb_release_single_field(const pb_field_iter_t *iter)
ext = ext->next;
}
}
- else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+ else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && PB_ATYPE(type) != PB_ATYPE_CALLBACK)
{
/* Release fields in submessage or submsg array */
void *pItem = iter->pData;
@@ -1237,6 +1271,11 @@ void pb_release(const pb_field_t fields[], void *dest_struct)
/* Field decoders */
+bool pb_decode_bool(pb_istream_t *stream, bool *dest)
+{
+ return pb_dec_bool(stream, NULL, (void*)dest);
+}
+
bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest)
{
pb_uint64_t value;
@@ -1286,6 +1325,17 @@ bool pb_decode_fixed64(pb_istream_t *stream, void *dest)
}
#endif
+static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ uint32_t value;
+ PB_UNUSED(field);
+ if (!pb_decode_varint32(stream, &value))
+ return false;
+
+ *(bool*)dest = (value != 0);
+ return true;
+}
+
static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
{
pb_uint64_t value;
@@ -1409,6 +1459,9 @@ static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *fie
#ifndef PB_ENABLE_MALLOC
PB_RETURN_ERROR(stream, "no malloc support");
#else
+ if (stream->bytes_left < size)
+ PB_RETURN_ERROR(stream, "end-of-stream");
+
if (!allocate_field(stream, dest, alloc_size, 1))
return false;
bdest = *(pb_bytes_array_t**)dest;
@@ -1444,6 +1497,9 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *fi
#ifndef PB_ENABLE_MALLOC
PB_RETURN_ERROR(stream, "no malloc support");
#else
+ if (stream->bytes_left < size)
+ PB_RETURN_ERROR(stream, "end-of-stream");
+
if (!allocate_field(stream, dest, alloc_size, 1))
return false;
dest = *(void**)dest;
diff --git a/pb_decode.h b/pb_decode.h
index 398b24a..3577c20 100644
--- a/pb_decode.h
+++ b/pb_decode.h
@@ -134,7 +134,7 @@ bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *ta
/* Skip the field payload data, given the wire type. */
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
-/* Decode an integer in the varint format. This works for bool, enum, int32,
+/* Decode an integer in the varint format. This works for enum, int32,
* int64, uint32 and uint64 field types. */
#ifndef PB_WITHOUT_64BIT
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
@@ -142,10 +142,13 @@ bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
#define pb_decode_varint pb_decode_varint32
#endif
-/* Decode an integer in the varint format. This works for bool, enum, int32,
+/* Decode an integer in the varint format. This works for enum, int32,
* and uint32 field types. */
bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest);
+/* Decode a bool value in varint format. */
+bool pb_decode_bool(pb_istream_t *stream, bool *dest);
+
/* Decode an integer in the zig-zagged svarint format. This works for sint32
* and sint64. */
#ifndef PB_WITHOUT_64BIT
diff --git a/pb_encode.c b/pb_encode.c
index 089172c..371d256 100644
--- a/pb_encode.c
+++ b/pb_encode.c
@@ -28,6 +28,7 @@ static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *fie
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
static void *pb_const_cast(const void *p);
+static bool checkreturn pb_enc_bool(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);
static bool checkreturn pb_enc_uvarint(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);
@@ -52,6 +53,7 @@ static bool checkreturn pb_encode_negative_varint(pb_ostream_t *stream, pb_uint6
* Order in the array must match pb_action_t LTYPE numbering.
*/
static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
+ &pb_enc_bool,
&pb_enc_varint,
&pb_enc_uvarint,
&pb_enc_svarint,
@@ -100,10 +102,13 @@ pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize)
bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
{
- if (stream->callback != NULL)
+ if (count > 0 && stream->callback != NULL)
{
- if (stream->bytes_written + count > stream->max_size)
+ if (stream->bytes_written + count < stream->bytes_written ||
+ stream->bytes_written + count > stream->max_size)
+ {
PB_RETURN_ERROR(stream, "stream full");
+ }
#ifdef PB_BUFFER_ONLY
if (!buf_write(stream, buf, count))
@@ -122,20 +127,39 @@ bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t cou
* Encode a single field *
*************************/
+/* Read a bool value without causing undefined behavior even if the value
+ * is invalid. See issue #434 and
+ * https://stackoverflow.com/questions/27661768/weird-results-for-conditional
+ */
+static bool safe_read_bool(const void *pSize)
+{
+ const char *p = (const char *)pSize;
+ size_t i;
+ for (i = 0; i < sizeof(bool); i++)
+ {
+ if (p[i] != 0)
+ return true;
+ }
+ return false;
+}
+
/* Encode a static array. Handles the size calculations and possible packing. */
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
const void *pData, size_t count, pb_encoder_t func)
{
size_t i;
const void *p;
+#ifndef PB_ENCODE_ARRAYS_UNPACKED
size_t size;
-
+#endif
+
if (count == 0)
return true;
if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
PB_RETURN_ERROR(stream, "array max size exceeded");
+#ifndef PB_ENCODE_ARRAYS_UNPACKED
/* We always pack arrays if the datatype allows it. */
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
{
@@ -180,6 +204,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
}
}
else
+#endif
{
p = pData;
for (i = 0; i < count; i++)
@@ -225,17 +250,20 @@ static bool pb_check_proto3_default_value(const pb_field_t *field, const void *p
else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
{
/* Repeated fields inside proto3 submessage: present if count != 0 */
- return *(const pb_size_t*)pSize == 0;
+ if (field->size_offset != 0)
+ return *(const pb_size_t*)pSize == 0;
+ else if (PB_ATYPE(type) == PB_ATYPE_STATIC)
+ return false; /* Fixed length array */
}
else if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
{
/* Oneof fields */
return *(const pb_size_t*)pSize == 0;
}
- else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->size_offset)
+ else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->size_offset != 0)
{
/* Proto2 optional fields inside proto3 submessage */
- return *(const bool*)pSize == false;
+ return safe_read_bool(pSize) == false;
}
/* Rest is proto3 singular fields */
@@ -279,6 +307,12 @@ static bool pb_check_proto3_default_value(const pb_field_t *field, const void *p
return true;
}
}
+
+ /* Compares pointers to NULL in case of FT_POINTER */
+ if (PB_ATYPE(type) == PB_ATYPE_POINTER && PB_LTYPE(type) > PB_LTYPE_LAST_PACKABLE)
+ {
+ return !*(const void**)((uintptr_t)pData);
+ }
{
/* Catch-all branch that does byte-per-byte comparison for zero value.
@@ -351,7 +385,7 @@ static bool checkreturn encode_basic_field(pb_ostream_t *stream,
break;
case PB_HTYPE_OPTIONAL:
- if (*(const bool*)pSize)
+ if (safe_read_bool(pSize))
{
if (!pb_encode_tag_for_field(stream, field))
return false;
@@ -643,6 +677,7 @@ bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t
pb_wire_type_t wiretype;
switch (PB_LTYPE(field->type))
{
+ case PB_LTYPE_BOOL:
case PB_LTYPE_VARINT:
case PB_LTYPE_UVARINT:
case PB_LTYPE_SVARINT:
@@ -731,6 +766,13 @@ bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fie
/* Field encoders */
+static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ uint32_t value = safe_read_bool(src) ? 1 : 0;
+ PB_UNUSED(field);
+ return pb_encode_varint(stream, value);
+}
+
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
pb_int64_t value = 0;
@@ -810,6 +852,7 @@ static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *f
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
{
const pb_bytes_array_t *bytes = NULL;
+ size_t allocsize;
bytes = (const pb_bytes_array_t*)src;
@@ -819,8 +862,9 @@ static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *fie
return pb_encode_string(stream, NULL, 0);
}
- if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
- PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
+ allocsize = PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size);
+ if (allocsize < bytes->size ||
+ (PB_ATYPE(field->type) == PB_ATYPE_STATIC && allocsize > field->data_size))
{
PB_RETURN_ERROR(stream, "bytes size exceeded");
}
diff --git a/pb_encode.h b/pb_encode.h
index 8bf78dd..b1d822f 100644
--- a/pb_encode.h
+++ b/pb_encode.h
@@ -123,7 +123,7 @@ bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
* structure. Call this from the callback before writing out field contents. */
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
-/* Encode field header by manually specifing wire type. You need to use this
+/* Encode field header by manually specifying wire type. You need to use this
* if you want to write out packed arrays from a callback field. */
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
diff --git a/spm-test/objc/c-header.c b/spm-test/objc/c-header.c
new file mode 100644
index 0000000..17d6795
--- /dev/null
+++ b/spm-test/objc/c-header.c
@@ -0,0 +1,3 @@
+#include "pb.h"
+#include "pb_common.h"
+#include "pb_decode.h"
diff --git a/spm-test/objc/objc-header.m b/spm-test/objc/objc-header.m
new file mode 100644
index 0000000..860f42b
--- /dev/null
+++ b/spm-test/objc/objc-header.m
@@ -0,0 +1,3 @@
+#import "pb.h"
+#import <pb_common.h>
+#include "pb_decode.h"
diff --git a/spm-test/objc/objc-module.m b/spm-test/objc/objc-module.m
new file mode 100644
index 0000000..18b2833
--- /dev/null
+++ b/spm-test/objc/objc-module.m
@@ -0,0 +1 @@
+@import nanopb;
diff --git a/spm-test/objc/objc-qualified.m b/spm-test/objc/objc-qualified.m
new file mode 100644
index 0000000..ada0ba1
--- /dev/null
+++ b/spm-test/objc/objc-qualified.m
@@ -0,0 +1,3 @@
+#import "nanopb/pb.h"
+#import <nanopb/pb_common.h>
+#include "nanopb/pb_decode.h"
diff --git a/spm-test/swift/main.swift b/spm-test/swift/main.swift
new file mode 100644
index 0000000..97cf622
--- /dev/null
+++ b/spm-test/swift/main.swift
@@ -0,0 +1 @@
+import nanopb
diff --git a/spm_headers/nanopb/pb.h b/spm_headers/nanopb/pb.h
new file mode 120000
index 0000000..e2be14d
--- /dev/null
+++ b/spm_headers/nanopb/pb.h
@@ -0,0 +1 @@
+../../pb.h \ No newline at end of file
diff --git a/spm_headers/nanopb/pb_common.h b/spm_headers/nanopb/pb_common.h
new file mode 120000
index 0000000..9449ad8
--- /dev/null
+++ b/spm_headers/nanopb/pb_common.h
@@ -0,0 +1 @@
+../../pb_common.h \ No newline at end of file
diff --git a/spm_headers/nanopb/pb_decode.h b/spm_headers/nanopb/pb_decode.h
new file mode 120000
index 0000000..d8288c7
--- /dev/null
+++ b/spm_headers/nanopb/pb_decode.h
@@ -0,0 +1 @@
+../../pb_decode.h \ No newline at end of file
diff --git a/spm_headers/nanopb/pb_encode.h b/spm_headers/nanopb/pb_encode.h
new file mode 120000
index 0000000..3236a5f
--- /dev/null
+++ b/spm_headers/nanopb/pb_encode.h
@@ -0,0 +1 @@
+../../pb_encode.h \ No newline at end of file
diff --git a/spm_headers/pb.h b/spm_headers/pb.h
new file mode 100644
index 0000000..ee67ff0
--- /dev/null
+++ b/spm_headers/pb.h
@@ -0,0 +1 @@
+#include "nanopb/pb.h"
diff --git a/spm_headers/pb_common.h b/spm_headers/pb_common.h
new file mode 100644
index 0000000..656424e
--- /dev/null
+++ b/spm_headers/pb_common.h
@@ -0,0 +1 @@
+#include "nanopb/pb_common.h"
diff --git a/spm_headers/pb_decode.h b/spm_headers/pb_decode.h
new file mode 100644
index 0000000..dee5064
--- /dev/null
+++ b/spm_headers/pb_decode.h
@@ -0,0 +1 @@
+#include "nanopb/pb_decode.h"
diff --git a/spm_headers/pb_encode.h b/spm_headers/pb_encode.h
new file mode 100644
index 0000000..3e8b73b
--- /dev/null
+++ b/spm_headers/pb_encode.h
@@ -0,0 +1 @@
+#include "nanopb/pb_encode.h"
diff --git a/tests/SConstruct b/tests/SConstruct
index f998024..504f1b8 100644
--- a/tests/SConstruct
+++ b/tests/SConstruct
@@ -16,6 +16,14 @@ scons CC=clang CXX=clang++
import os
env = Environment(ENV = os.environ, tools = ['default', 'nanopb'])
+# Limit memory usage. This is to catch problems like issue #338
+try:
+ import resource
+ soft, hard = resource.getrlimit(resourse.RLIMIT_AS)
+ resource.setrlimit(resource.RLIMIT_AS, (100*1024*1024, hard))
+except:
+ pass
+
# Allow overriding the compiler with scons CC=???
if 'CC' in ARGUMENTS: env.Replace(CC = ARGUMENTS['CC'])
if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
@@ -95,7 +103,7 @@ if not env.GetOption('clean'):
# Check if we can use undefined behaviour sanitizer (only with clang)
# TODO: Fuzz test triggers the bool sanitizer, figure out whether to
# modify the fuzz test or to keep ignoring the check.
- extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer -fsanitize-recover=bool '
+ extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer '
if 'clang' in env['CC']:
if conf.CheckCCFLAGS(extra, linkflags = extra):
conf.env.Append(CORECFLAGS = extra)
@@ -128,6 +136,12 @@ elif 'cl' in env['CC']:
# More strict checks on the nanopb core
env.Append(CORECFLAGS = '/W4')
+
+ # Disable warning about sizeof(union{}) construct that is used in
+ # message size macros, in e.g. multiple_files testcase. The C construct
+ # itself is valid, but quite rare, which causes Visual C++ to give a warning
+ # about it.
+ env.Append(CFLAGS = '/wd4116')
elif 'tcc' in env['CC']:
# Tiny C Compiler
env.Append(CFLAGS = '-Wall -Werror -g')
@@ -139,7 +153,7 @@ if 'clang' in env['CXX']:
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')
+ env.Append(CXXFLAGS = '/Zi /W2 /WX /wd4116')
# Now include the SConscript files from all subdirectories
import os.path
diff --git a/tests/alltypes/decode_alltypes.c b/tests/alltypes/decode_alltypes.c
index b74121f..3b0f524 100644
--- a/tests/alltypes/decode_alltypes.c
+++ b/tests/alltypes/decode_alltypes.c
@@ -198,6 +198,7 @@ bool check_alltypes(pb_istream_t *stream, int mode)
TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag);
TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0);
TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059);
+ TEST(alltypes.oneof.oneof_msg1.substuff3 == 3);
}
else if (mode == 2)
{
diff --git a/tests/encode_arrays_unpacked/SConscript b/tests/encode_arrays_unpacked/SConscript
new file mode 100644
index 0000000..bd36d9d
--- /dev/null
+++ b/tests/encode_arrays_unpacked/SConscript
@@ -0,0 +1,28 @@
+# Run the alltypes test case, but compile with PB_ENCODE_ARRAYS_UNPACKED=1
+
+Import("env")
+
+# Take copy of the files for custom build.
+c = Copy("$TARGET", "$SOURCE")
+env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", 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("decode_alltypes.c", "$BUILD/alltypes/decode_alltypes.c", c)
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_ENCODE_ARRAYS_UNPACKED': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_unpacked.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_unpacked.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_unpacked.o", "$NANOPB/pb_common.c")
+
+# Now build and run the test normally.
+enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_unpacked.o", "pb_common_unpacked.o"])
+dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_unpacked.o", "pb_common_unpacked.o"])
+
+env.RunTest(enc)
+env.RunTest([dec, "encode_alltypes.output"])
diff --git a/tests/enum_minmax/SConscript b/tests/enum_minmax/SConscript
new file mode 100644
index 0000000..6a66cbd
--- /dev/null
+++ b/tests/enum_minmax/SConscript
@@ -0,0 +1,8 @@
+# Test that different sizes of enum fields are properly encoded and decoded.
+
+Import('env')
+
+env.NanopbProto('enumminmax')
+
+p = env.Program(["enumminmax_unittests.c",])
+env.RunTest(p)
diff --git a/tests/enum_minmax/enumminmax.proto b/tests/enum_minmax/enumminmax.proto
new file mode 100644
index 0000000..482a8db
--- /dev/null
+++ b/tests/enum_minmax/enumminmax.proto
@@ -0,0 +1,22 @@
+/* Test out-of-order enum values.
+ */
+
+syntax = "proto3";
+
+enum Language {
+ UNKNOWN = 0;
+ ENGLISH_EN_GB = 12;
+ ENGLISH_EN_US = 1;
+ FRENCH_FR_FR = 2;
+ ITALIAN_IT_IT = 3;
+ GERMAN_DE_DE = 4;
+ SPANISH_ES_AR = 13;
+ SPANISH_ES_ES = 5;
+ SPANISH_ES_MX = 14;
+ SWEDISH_SV_SE = 6;
+ DUTCH_NL_NL = 7;
+ KOREAN_KO_KR = 8;
+ JAPANESE_JA_JP = 9;
+ CHINESE_SIMPLIFIED_ZH_CN = 10;
+ CHINESE_TRADITIONAL_ZH_TW = 11;
+}
diff --git a/tests/enum_minmax/enumminmax_unittests.c b/tests/enum_minmax/enumminmax_unittests.c
new file mode 100644
index 0000000..ccb10a4
--- /dev/null
+++ b/tests/enum_minmax/enumminmax_unittests.c
@@ -0,0 +1,16 @@
+#include "unittests.h"
+#include "enumminmax.pb.h"
+
+int main()
+{
+ int status = 0;
+
+ COMMENT("Verify min/max on unsorted enum");
+ {
+ TEST(_Language_MIN == Language_UNKNOWN);
+ TEST(_Language_MAX == Language_SPANISH_ES_MX);
+ TEST(_Language_ARRAYSIZE == (Language_SPANISH_ES_MX+1));
+ }
+
+ return status;
+}
diff --git a/tests/fuzztest/fuzztest.c b/tests/fuzztest/fuzztest.c
index ee851ec..0dc2382 100644
--- a/tests/fuzztest/fuzztest.c
+++ b/tests/fuzztest/fuzztest.c
@@ -171,6 +171,33 @@ static void rand_mess(uint8_t *buf, size_t count)
/* Some default data to put in the message */
static const alltypes_static_AllTypes initval = alltypes_static_AllTypes_init_default;
+/* Check the invariants defined in security model on decoded structure */
+static void sanity_check_static(alltypes_static_AllTypes *msg)
+{
+ bool truebool = true;
+ bool falsebool = false;
+
+ /* TODO: Add more checks, or rather, generate them automatically */
+ assert(strlen(msg->req_string) < sizeof(msg->req_string));
+ assert(strlen(msg->opt_string) < sizeof(msg->opt_string));
+ if (msg->rep_string_count > 0)
+ {
+ assert(strlen(msg->rep_string[0]) < sizeof(msg->rep_string[0]));
+ }
+ assert(memcmp(&msg->req_bool, &truebool, sizeof(bool)) == 0 ||
+ memcmp(&msg->req_bool, &falsebool, sizeof(bool)) == 0);
+ assert(memcmp(&msg->has_opt_bool, &truebool, sizeof(bool)) == 0 ||
+ memcmp(&msg->has_opt_bool, &falsebool, sizeof(bool)) == 0);
+ assert(memcmp(&msg->opt_bool, &truebool, sizeof(bool)) == 0 ||
+ memcmp(&msg->opt_bool, &falsebool, sizeof(bool)) == 0);
+ assert(msg->rep_bool_count <= pb_arraysize(alltypes_static_AllTypes, rep_bool));
+ if (msg->rep_bool_count > 0)
+ {
+ assert(memcmp(&msg->rep_bool[0], &truebool, sizeof(bool)) == 0 ||
+ memcmp(&msg->rep_bool[0], &falsebool, sizeof(bool)) == 0);
+ }
+}
+
#define BUFSIZE 4096
static bool do_static_encode(uint8_t *buffer, size_t *msglen)
@@ -230,6 +257,11 @@ static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success
rand_fill((uint8_t*)msg, sizeof(alltypes_static_AllTypes));
stream = pb_istream_from_buffer(buffer, msglen);
status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg);
+
+ if (status)
+ {
+ sanity_check_static(msg);
+ }
if (!status && assert_success)
{
@@ -285,6 +317,7 @@ static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1);
assert(status);
+ sanity_check_static(msg1);
}
{
@@ -298,6 +331,7 @@ static void do_static_roundtrip(uint8_t *buffer, size_t msglen)
pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2);
status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2);
assert(status);
+ sanity_check_static(msg2);
}
{
diff --git a/tests/mem_release/mem_release.c b/tests/mem_release/mem_release.c
index dc6f87d..fae4516 100644
--- a/tests/mem_release/mem_release.c
+++ b/tests/mem_release/mem_release.c
@@ -133,13 +133,16 @@ static bool test_OneofMessage()
}
}
- /* Encode second with SubMessage, invoking 'merge' behaviour */
+ /* Encode second with SubMessage, replacing the oneof item */
{
OneofMessage msg = OneofMessage_init_zero;
+ char *teststr = "1";
msg.which_msgs = OneofMessage_msg2_tag;
msg.first = 999;
msg.msgs.msg2.dynamic_str = "ABCD";
+ msg.msgs.msg2.dynamic_str_arr_count = 1;
+ msg.msgs.msg2.dynamic_str_arr = &teststr;
msg.last = 888;
if (!pb_encode(&stream, OneofMessage_fields, &msg))
@@ -148,6 +151,25 @@ static bool test_OneofMessage()
return false;
}
}
+
+ /* Encode second SubMessage, invoking submessage merge behavior */
+ {
+ OneofMessage msg = OneofMessage_init_zero;
+ char *teststr = "2";
+ msg.which_msgs = OneofMessage_msg2_tag;
+
+ msg.first = 99;
+ msg.msgs.msg2.dynamic_str = "EFGH";
+ msg.msgs.msg2.dynamic_str_arr_count = 1;
+ msg.msgs.msg2.dynamic_str_arr = &teststr;
+ msg.last = 88;
+
+ if (!pb_encode(&stream, OneofMessage_fields, &msg))
+ {
+ fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
+ return false;
+ }
+ }
msgsize = stream.bytes_written;
}
@@ -160,13 +182,16 @@ static bool test_OneofMessage()
return false;
}
- TEST(msg.first == 999);
+ TEST(msg.first == 99);
TEST(msg.which_msgs == OneofMessage_msg2_tag);
TEST(msg.msgs.msg2.dynamic_str);
- TEST(strcmp(msg.msgs.msg2.dynamic_str, "ABCD") == 0);
- TEST(msg.msgs.msg2.dynamic_str_arr == NULL);
+ TEST(strcmp(msg.msgs.msg2.dynamic_str, "EFGH") == 0);
+ TEST(msg.msgs.msg2.dynamic_str_arr != NULL);
+ TEST(msg.msgs.msg2.dynamic_str_arr_count == 2);
+ TEST(strcmp(msg.msgs.msg2.dynamic_str_arr[0], "1") == 0);
+ TEST(strcmp(msg.msgs.msg2.dynamic_str_arr[1], "2") == 0);
TEST(msg.msgs.msg2.dynamic_submsg == NULL);
- TEST(msg.last == 888);
+ TEST(msg.last == 88);
pb_release(OneofMessage_fields, &msg);
TEST(get_alloc_count() == 0);
@@ -177,9 +202,43 @@ static bool test_OneofMessage()
return true;
}
+static bool dummy_decode_cb(pb_istream_t *stream, const pb_field_t *field, void **arg)
+{
+ return false;
+}
+
+/* Garbage input */
+static bool test_Garbage()
+{
+ const uint8_t buffer[] = "I'm only happy when it rains";
+ const size_t msgsize = sizeof(buffer);
+
+ {
+ OneofMessage msg = OneofMessage_init_zero;
+ pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
+ TEST(!pb_decode(&stream, OneofMessage_fields, &msg));
+ }
+
+ {
+ TestMessage msg = TestMessage_init_zero;
+ pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
+ TEST(!pb_decode(&stream, TestMessage_fields, &msg));
+ }
+
+ {
+ RepeatedMessage msg = RepeatedMessage_init_zero;
+ pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
+ msg.subs.arg = NULL;
+ msg.subs.funcs.decode = dummy_decode_cb;
+ TEST(!pb_decode(&stream, RepeatedMessage_fields, &msg));
+ }
+
+ return true;
+}
+
int main()
{
- if (test_TestMessage() && test_OneofMessage())
+ if (test_TestMessage() && test_OneofMessage() && test_Garbage())
return 0;
else
return 1;
diff --git a/tests/mem_release/mem_release.proto b/tests/mem_release/mem_release.proto
index 0816dc2..b0addbd 100644
--- a/tests/mem_release/mem_release.proto
+++ b/tests/mem_release/mem_release.proto
@@ -33,3 +33,9 @@ message OneofMessage
}
required int32 last = 4;
}
+
+message RepeatedMessage
+{
+ required int32 first = 1;
+ repeated SubMessage subs = 2;
+}
diff --git a/tests/multiple_files/subdir/multifile2.proto b/tests/multiple_files/subdir/multifile2.proto
index 847a929..99f094e 100644
--- a/tests/multiple_files/subdir/multifile2.proto
+++ b/tests/multiple_files/subdir/multifile2.proto
@@ -9,9 +9,14 @@ message Callback2Message {
required SubMessage submsg = 2;
}
+message SmallMessage {
+ required bool dummy = 1;
+}
+
message OneofMessage {
oneof msgs {
StaticMessage tstmsg = 1;
+ SmallMessage msg2 = 2;
}
}
diff --git a/tests/multiple_files/test_multiple_files.c b/tests/multiple_files/test_multiple_files.c
index 70a3e59..a4a600a 100644
--- a/tests/multiple_files/test_multiple_files.c
+++ b/tests/multiple_files/test_multiple_files.c
@@ -23,7 +23,8 @@ int main()
{
subdir_SubdirMessage foo = subdir_SubdirMessage_init_default;
TEST(foo.foo == 15);
- /* TEST(subdir_OneofMessage_size == 27); */ /* TODO: Issue #172 */
+ TEST(subdir_OneofMessage_size >= 27); /* Note: not perfectly accurate due to issue 172 */
+ TEST(subdir_OneofMessage_size <= 30);
}
return status;
diff --git a/tests/regression/issue_338/SConscript b/tests/regression/issue_338/SConscript
new file mode 100644
index 0000000..7b219b8
--- /dev/null
+++ b/tests/regression/issue_338/SConscript
@@ -0,0 +1,7 @@
+# Check that generator doesn't exceed memory limits
+
+Import('env')
+
+env.NanopbProto('bigvalue')
+
+
diff --git a/tests/regression/issue_338/bigvalue.proto b/tests/regression/issue_338/bigvalue.proto
new file mode 100644
index 0000000..3ff284a
--- /dev/null
+++ b/tests/regression/issue_338/bigvalue.proto
@@ -0,0 +1,204 @@
+syntax = "proto3";
+
+package enterprise;
+
+message BigData {
+ repeated BigEvent events = 1;
+}
+
+message BigEvent {
+ oneof event {
+ BigValue1 data1 = 1;
+ BigValue2 data2 = 2;
+ BigValue3 data3 = 3;
+ BigValue4 data4 = 4;
+ BigValue5 data5 = 5;
+ BigValue6 data6 = 6;
+ BigValue7 data7 = 7;
+ BigValue8 data8 = 8;
+ BigValue9 data9 = 9;
+ BigValue10 data10 = 10;
+ BigValue11 data11 = 11;
+ BigValue12 data12 = 12;
+ BigValue13 data13 = 13;
+ BigValue14 data14 = 14;
+ BigValue15 data15 = 15;
+ BigValue16 data16 = 16;
+ BigValue17 data17 = 17;
+ BigValue18 data18 = 18;
+ BigValue19 data19 = 19;
+ BigValue20 data20 = 20;
+ BigValue21 data21 = 21;
+ BigValue22 data22 = 22;
+ BigValue23 data23 = 23;
+ BigValue24 data24 = 24;
+ BigValue25 data25 = 25;
+ BigValue26 data26 = 26;
+ BigValue27 data27 = 27;
+ BigValue28 data28 = 28;
+ BigValue29 data29 = 29;
+ BigValue30 data30 = 30;
+ BigValue31 data31 = 31;
+ BigValue32 data32 = 32;
+ }
+}
+
+message BigValue1 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue2 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue3 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue4 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue5 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue6 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue7 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue8 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue9 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue10 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue11 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue12 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue13 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue14 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue15 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue16 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue17 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue18 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue19 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue20 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue21 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue22 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue23 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue24 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue25 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue26 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue27 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue28 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue29 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue30 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue31 {
+ int32 key = 1;
+ bytes value = 2;
+}
+
+message BigValue32 {
+ int32 key = 1;
+ bytes value = 2;
+}
diff --git a/tests/regression/issue_342/SConscript b/tests/regression/issue_342/SConscript
new file mode 100644
index 0000000..e15d022
--- /dev/null
+++ b/tests/regression/issue_342/SConscript
@@ -0,0 +1,21 @@
+# Regression test for #342:
+# Possible null-pointer dereference in pb_decode.c
+
+Import("env")
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_OLD_CALLBACK_STYLE': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_decode_oldcallback.o", "$NANOPB/pb_decode.c")
+strict.Object("pb_encode_oldcallback.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_oldcallback.o", "$NANOPB/pb_common.c")
+
+opts.NanopbProto("extensions")
+testprog = opts.Program(["test_extensions.c", "extensions.pb.c", "pb_encode_oldcallback.o", "pb_decode_oldcallback.o", "pb_common_oldcallback.o"])
+
+opts.RunTest(testprog)
+
diff --git a/tests/regression/issue_342/extensions.proto b/tests/regression/issue_342/extensions.proto
new file mode 100644
index 0000000..04b60ab
--- /dev/null
+++ b/tests/regression/issue_342/extensions.proto
@@ -0,0 +1,11 @@
+syntax = "proto2";
+
+message BaseMessage {
+ extensions 100 to 200;
+}
+
+extend BaseMessage {
+ optional string string_extension = 100;
+}
+
+
diff --git a/tests/regression/issue_342/test_extensions.c b/tests/regression/issue_342/test_extensions.c
new file mode 100644
index 0000000..1a48855
--- /dev/null
+++ b/tests/regression/issue_342/test_extensions.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "extensions.pb.h"
+#include "unittests.h"
+
+static bool write_string(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
+{
+ return pb_encode_tag_for_field(stream, field) &&
+ pb_encode_string(stream, (const void*)"abc", 3);
+}
+
+int main(int argc, char **argv)
+{
+ int status = 0;
+ uint8_t buffer[64];
+ pb_size_t msglen = 0;
+
+ {
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ pb_callback_t callback_def = {{0}};
+ pb_extension_t ext = {0};
+ BaseMessage msg = {0};
+
+ callback_def.funcs.encode = &write_string;
+ ext.type = &string_extension;
+ ext.dest = &callback_def;
+ msg.extensions = &ext;
+
+ TEST(pb_encode(&stream, BaseMessage_fields, &msg));
+
+ msglen = stream.bytes_written;
+ TEST(msglen > 3);
+ }
+
+ {
+ pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+ pb_extension_t ext = {0};
+ BaseMessage msg = {0};
+
+ ext.type = &string_extension;
+ /* Note: ext.dest remains null to trigger buf #342 */
+ msg.extensions = &ext;
+
+ TEST(pb_decode(&stream, BaseMessage_fields, &msg));
+ }
+
+ return status;
+}
+
diff --git a/tests/regression/issue_363/SConscript b/tests/regression/issue_363/SConscript
new file mode 100644
index 0000000..4ce3c6d
--- /dev/null
+++ b/tests/regression/issue_363/SConscript
@@ -0,0 +1,14 @@
+# Regression test for #363:
+# Incorrect PB_STATIC_ASSERT for bytes inside oneof
+
+Import("env")
+
+env.NanopbProto("oneofmsg.proto")
+testprog = env.Program(["test_oneofmsg.c",
+ "oneofmsg.pb.c",
+ "$COMMON/pb_encode.o",
+ "$COMMON/pb_decode.o",
+ "$COMMON/pb_common.o"])
+
+env.RunTest(testprog)
+
diff --git a/tests/regression/issue_363/oneofmsg.proto b/tests/regression/issue_363/oneofmsg.proto
new file mode 100644
index 0000000..735bed8
--- /dev/null
+++ b/tests/regression/issue_363/oneofmsg.proto
@@ -0,0 +1,10 @@
+syntax = "proto2";
+
+import "nanopb.proto";
+
+message BodyMessage {
+ oneof body_type {
+ bytes device_data_crypted = 1 [(nanopb).max_size = 252];
+ bytes device_config_crypted = 2 [(nanopb).max_size = 252];
+ }
+}
diff --git a/tests/regression/issue_363/test_oneofmsg.c b/tests/regression/issue_363/test_oneofmsg.c
new file mode 100644
index 0000000..c218192
--- /dev/null
+++ b/tests/regression/issue_363/test_oneofmsg.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "oneofmsg.pb.h"
+#include "unittests.h"
+
+int main(int argc, char **argv)
+{
+ int status = 0;
+ uint8_t buffer[512];
+ pb_size_t msglen = 0;
+
+ {
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ BodyMessage msg = BodyMessage_init_zero;
+
+ msg.which_body_type = BodyMessage_device_data_crypted_tag;
+ msg.body_type.device_data_crypted.size = 252;
+ memset(msg.body_type.device_data_crypted.bytes, 0xAA, 252);
+
+ TEST(pb_encode(&stream, BodyMessage_fields, &msg));
+
+ msglen = stream.bytes_written;
+ TEST(msglen > 252);
+ }
+
+ {
+ pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+ BodyMessage msg = BodyMessage_init_zero;
+
+ TEST(pb_decode(&stream, BodyMessage_fields, &msg));
+
+ TEST(msg.which_body_type == BodyMessage_device_data_crypted_tag);
+ TEST(msg.body_type.device_data_crypted.size == 252);
+ TEST(msg.body_type.device_data_crypted.bytes[251] == 0xAA);
+ }
+
+ return status;
+}
+
diff --git a/tests/regression/issue_376/SConscript b/tests/regression/issue_376/SConscript
new file mode 100644
index 0000000..017a3ec
--- /dev/null
+++ b/tests/regression/issue_376/SConscript
@@ -0,0 +1,14 @@
+# Regression test for #376:
+# Problem with fixed array inside proto3 submessage
+
+Import("env")
+
+env.NanopbProto(["fixed_array.proto", "fixed_array.options"])
+testprog = env.Program(["test_fixarray.c",
+ "fixed_array.pb.c",
+ "$COMMON/pb_encode.o",
+ "$COMMON/pb_decode.o",
+ "$COMMON/pb_common.o"])
+
+env.RunTest(testprog)
+
diff --git a/tests/regression/issue_376/fixed_array.options b/tests/regression/issue_376/fixed_array.options
new file mode 100644
index 0000000..60fd687
--- /dev/null
+++ b/tests/regression/issue_376/fixed_array.options
@@ -0,0 +1 @@
+SubMessage.data fixed_count:true,max_count:8
diff --git a/tests/regression/issue_376/fixed_array.proto b/tests/regression/issue_376/fixed_array.proto
new file mode 100644
index 0000000..6d86f80
--- /dev/null
+++ b/tests/regression/issue_376/fixed_array.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+message MainMessage {
+ SubMessage submsg = 1;
+}
+
+message SubMessage {
+ repeated int32 data = 1;
+}
+
+
diff --git a/tests/regression/issue_376/test_fixarray.c b/tests/regression/issue_376/test_fixarray.c
new file mode 100644
index 0000000..f321b6d
--- /dev/null
+++ b/tests/regression/issue_376/test_fixarray.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "fixed_array.pb.h"
+#include "unittests.h"
+
+int main(int argc, char **argv)
+{
+ int status = 0;
+ uint8_t buffer[64];
+ pb_size_t msglen = 0;
+
+ {
+ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ MainMessage msg = MainMessage_init_zero;
+
+ msg.submsg.data[0] = 0;
+ msg.submsg.data[4] = 5;
+
+ TEST(pb_encode(&stream, MainMessage_fields, &msg));
+
+ msglen = stream.bytes_written;
+ TEST(msglen > 5);
+ }
+
+ {
+ pb_istream_t stream = pb_istream_from_buffer(buffer, msglen);
+ MainMessage msg = MainMessage_init_zero;
+
+ TEST(pb_decode(&stream, MainMessage_fields, &msg));
+
+ TEST(msg.submsg.data[0] == 0);
+ TEST(msg.submsg.data[4] == 5);
+ }
+
+ return status;
+}
+
diff --git a/tests/regression/issue_380/SConscript b/tests/regression/issue_380/SConscript
new file mode 100644
index 0000000..4606d9b
--- /dev/null
+++ b/tests/regression/issue_380/SConscript
@@ -0,0 +1,9 @@
+# Regression test for #380:
+# mangle_names:M_STRIP_PACKAGE is broken when message name (partially)
+# matches package name
+
+Import("env")
+
+env.NanopbProto(["manglenames.proto", "manglenames.options"])
+env.Object("manglenames.pb.o", "manglenames.pb.c")
+
diff --git a/tests/regression/issue_380/manglenames.options b/tests/regression/issue_380/manglenames.options
new file mode 100644
index 0000000..d9b4e41
--- /dev/null
+++ b/tests/regression/issue_380/manglenames.options
@@ -0,0 +1 @@
+* mangle_names:M_STRIP_PACKAGE
diff --git a/tests/regression/issue_380/manglenames.proto b/tests/regression/issue_380/manglenames.proto
new file mode 100644
index 0000000..186161d
--- /dev/null
+++ b/tests/regression/issue_380/manglenames.proto
@@ -0,0 +1,16 @@
+syntax = "proto2";
+package A;
+
+message A {
+ message B {
+ optional uint32 val = 1;
+ }
+ optional B b = 1;
+}
+
+message AP {
+ message B {
+ optional uint32 val = 1;
+ }
+ optional B m = 1;
+}
diff --git a/tests/regression/issue_395/SConscript b/tests/regression/issue_395/SConscript
new file mode 100644
index 0000000..8bc1030
--- /dev/null
+++ b/tests/regression/issue_395/SConscript
@@ -0,0 +1,14 @@
+# Regression test for #395:
+# Unexpected empty submessage in proto3 mode
+
+Import("env")
+
+env.NanopbProto(["test.proto", "test.options"])
+testprog = env.Program(["test.c",
+ "test.pb.c",
+ "$COMMON/pb_encode.o",
+ "$COMMON/pb_decode.o",
+ "$COMMON/pb_common.o"])
+
+env.RunTest(testprog)
+
diff --git a/tests/regression/issue_395/test.c b/tests/regression/issue_395/test.c
new file mode 100644
index 0000000..9578caf
--- /dev/null
+++ b/tests/regression/issue_395/test.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include <pb_decode.h>
+#include "test.pb.h"
+#include "unittests.h"
+
+int main(int argc, char **argv)
+{
+ int status = 0;
+ uint8_t buffer[512] = {0};
+ int i;
+ pb_ostream_t ostream;
+
+ Reply reply = Reply_init_zero;
+ Reply_Result request_result = Reply_Result_OK;
+
+ ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+ reply.result = request_result;
+ if (!pb_encode(&ostream, Reply_fields, &reply)) {
+ fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&ostream));
+ return 1;
+ }
+
+ printf("response payload (%d):", (int)ostream.bytes_written);
+ for (i = 0; i < ostream.bytes_written; i++) {
+ printf("%02X", buffer[i]);
+ }
+ printf("\n");
+
+ TEST(ostream.bytes_written == 2);
+ TEST(buffer[0] == 0x08);
+ TEST(buffer[1] == 0x01);
+
+ return status;
+}
+
diff --git a/tests/regression/issue_395/test.options b/tests/regression/issue_395/test.options
new file mode 100644
index 0000000..5594796
--- /dev/null
+++ b/tests/regression/issue_395/test.options
@@ -0,0 +1,2 @@
+SubSubAMessage.somestring max_size:64
+SubSubBMessage.somestring max_size:64
diff --git a/tests/regression/issue_395/test.proto b/tests/regression/issue_395/test.proto
new file mode 100644
index 0000000..91bf371
--- /dev/null
+++ b/tests/regression/issue_395/test.proto
@@ -0,0 +1,37 @@
+syntax = "proto3";
+
+message Error
+{
+ int32 code = 1;
+ string message = 2;
+}
+
+message SubSubAMessage
+{
+ string somestring = 1;
+}
+
+message SubSubBMessage
+{
+ string somestring = 1;
+}
+
+message SubMessage
+{
+ SubSubAMessage subsubmessageA = 1;
+ repeated SubSubBMessage subsubmessageB = 2;
+}
+
+message Reply
+{
+ enum Result
+ {
+ ERROR = 0;
+ OK = 1;
+ SOME_A = 2;
+ }
+
+ Result result = 1;
+ Error error = 2;
+ SubMessage submessage = 3;
+}
diff --git a/tests/regression/issue_504/SConscript b/tests/regression/issue_504/SConscript
new file mode 100644
index 0000000..c303171
--- /dev/null
+++ b/tests/regression/issue_504/SConscript
@@ -0,0 +1,12 @@
+# Regression test for #504:
+# Non empty submessage considered empty on FT_POINTER fields with address aligned on 0x100
+
+Import('env', 'malloc_env')
+
+env.NanopbProto(["test.proto"])
+test = malloc_env.Program(["test.c",
+ "test.pb.c",
+ "$COMMON/pb_encode.o",
+ "$COMMON/pb_common.o"])
+
+env.RunTest(test)
diff --git a/tests/regression/issue_504/test.c b/tests/regression/issue_504/test.c
new file mode 100644
index 0000000..1ca86db
--- /dev/null
+++ b/tests/regression/issue_504/test.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pb_encode.h>
+#include "test.pb.h"
+#include "unittests.h"
+
+const char STR[] = "test str";
+#define ALIGN 0x100
+
+int main(int argc, char **argv)
+{
+ int status = 0;
+ uint8_t buffer[512] = {0};
+ int i;
+ pb_ostream_t ostream;
+ MyMessage msg = MyMessage_init_zero;
+ char *pStr, *pStrAligned;
+ ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
+
+ /* copy STR to a malloced 0x100 aligned address */
+ pStr = malloc(sizeof(STR) + ALIGN);
+ pStrAligned = (char*)((uintptr_t)(pStr + ALIGN) & ~(ALIGN - 1));
+ memcpy(pStrAligned, STR, sizeof(STR));
+
+ msg.submessage.somestring = pStrAligned;
+ printf("%p: '%s'\n", msg.submessage.somestring, msg.submessage.somestring);
+
+ if (!pb_encode(&ostream, MyMessage_fields, &msg)) {
+ fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&ostream));
+ return 1;
+ }
+
+ free(pStr);
+ msg.submessage.somestring = NULL;
+
+ printf("response payload (%d):", (int)ostream.bytes_written);
+ for (i = 0; i < ostream.bytes_written; i++) {
+ printf("%02X", buffer[i]);
+ }
+ printf("\n");
+
+ TEST(ostream.bytes_written != 0);
+
+ return status;
+}
+
diff --git a/tests/regression/issue_504/test.proto b/tests/regression/issue_504/test.proto
new file mode 100644
index 0000000..a5b0d9c
--- /dev/null
+++ b/tests/regression/issue_504/test.proto
@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+import "nanopb.proto";
+
+message MyMessage
+{
+ SubMessage submessage = 1;
+}
+
+message SubMessage
+{
+ string somestring = 1 [(nanopb).type = FT_POINTER];
+}
diff --git a/tests/regression/issue_547/SConscript b/tests/regression/issue_547/SConscript
new file mode 100644
index 0000000..c0a19fa
--- /dev/null
+++ b/tests/regression/issue_547/SConscript
@@ -0,0 +1,21 @@
+# Regression test for issue #547:
+# Buffer overflow when encoding bytes with size set to 65535
+
+Import("env")
+
+env.NanopbProto("test.proto")
+
+# Define the compilation options
+opts = env.Clone()
+opts.Append(CPPDEFINES = {'PB_FIELD_32BIT': 1})
+
+# Build new version of core
+strict = opts.Clone()
+strict.Append(CFLAGS = strict['CORECFLAGS'])
+strict.Object("pb_encode_fields32.o", "$NANOPB/pb_encode.c")
+strict.Object("pb_common_fields32.o", "$NANOPB/pb_common.c")
+
+# Build and run test
+test = opts.Program(["test.c", "test.pb.c", "pb_encode_fields32.o", "pb_common_fields32.o"])
+
+env.RunTest(test)
diff --git a/tests/regression/issue_547/test.c b/tests/regression/issue_547/test.c
new file mode 100644
index 0000000..9530302
--- /dev/null
+++ b/tests/regression/issue_547/test.c
@@ -0,0 +1,28 @@
+#include <string.h>
+#include <pb_encode.h>
+#include <unittests.h>
+#include "test.pb.h"
+
+int main()
+{
+ pb_byte_t buf[512];
+ MyMessage msg = MyMessage_init_zero;
+ pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf));
+
+ msg.mybytes.size = 0xFFFFFFFF;
+
+ if (pb_encode(&stream, MyMessage_fields, &msg))
+ {
+ fprintf(stderr, "Failure: expected pb_encode() to fail.\n");
+ return 1;
+ }
+ else if (strcmp(PB_GET_ERROR(&stream), "bytes size exceeded") != 0)
+ {
+ fprintf(stderr, "Unexpected encoding error: %s\n", PB_GET_ERROR(&stream));
+ return 2;
+ }
+ else
+ {
+ return 0;
+ }
+}
diff --git a/tests/regression/issue_547/test.proto b/tests/regression/issue_547/test.proto
new file mode 100644
index 0000000..56daa41
--- /dev/null
+++ b/tests/regression/issue_547/test.proto
@@ -0,0 +1,7 @@
+syntax = "proto2";
+
+import "nanopb.proto";
+
+message MyMessage {
+ required bytes mybytes = 1 [(nanopb).max_size = 512];
+}
diff --git a/tests/regression/issue_611/SConscript b/tests/regression/issue_611/SConscript
new file mode 100644
index 0000000..6295107
--- /dev/null
+++ b/tests/regression/issue_611/SConscript
@@ -0,0 +1,8 @@
+# Regression test for #611:
+# Enum messages inside OneOf generated as ENUM instead of UENUM
+
+Import("env")
+
+env.NanopbProto("uenum.proto")
+env.Match(["uenum.pb.c", "uenum.expected"])
+env.Object("uenum.pb.c")
diff --git a/tests/regression/issue_611/uenum.expected b/tests/regression/issue_611/uenum.expected
new file mode 100644
index 0000000..0806f24
--- /dev/null
+++ b/tests/regression/issue_611/uenum.expected
@@ -0,0 +1,3 @@
+[^U]ENUM.*ONEOF.*MyEnum
+UENUM.*ONEOF.*MyUEnum
+
diff --git a/tests/regression/issue_611/uenum.proto b/tests/regression/issue_611/uenum.proto
new file mode 100644
index 0000000..f6df218
--- /dev/null
+++ b/tests/regression/issue_611/uenum.proto
@@ -0,0 +1,28 @@
+syntax = "proto2";
+
+enum my_enum {
+ E0 = 0;
+ E240 = 240;
+ E1 = -1;
+}
+
+enum my_uenum {
+ U0 = 0;
+ U240 = 240;
+}
+
+message my_message_regular {
+ required my_enum MyEnum = 1;
+ required my_uenum MyUEnum = 2;
+}
+
+message my_message_oneof {
+ required uint32 Data1 = 1;
+ oneof Data {
+ bool null = 2;
+ my_enum MyEnum = 3;
+ my_uenum MyUEnum = 4;
+ }
+}
+
+
diff --git a/tests/regression/issue_647/SConscript b/tests/regression/issue_647/SConscript
new file mode 100644
index 0000000..70eedf8
--- /dev/null
+++ b/tests/regression/issue_647/SConscript
@@ -0,0 +1,12 @@
+# Regression test for #647:
+# Ill-formed oneof message leads to calling free on an arbitrary pointer
+
+Import("env")
+
+env.NanopbProto("repro.proto")
+
+test = env.Program(["repro.c", "repro.pb.c",
+ "$COMMON/pb_decode_with_malloc.o",
+ "$COMMON/pb_common_with_malloc.o",
+ "$COMMON/malloc_wrappers.o"])
+env.RunTest(test)
diff --git a/tests/regression/issue_647/repro.c b/tests/regression/issue_647/repro.c
new file mode 100644
index 0000000..48d9570
--- /dev/null
+++ b/tests/regression/issue_647/repro.c
@@ -0,0 +1,16 @@
+#include <pb_decode.h>
+#include <unittests.h>
+#include <malloc_wrappers.h>
+#include "repro.pb.h"
+
+int main() {
+ const uint8_t data[] = {0x08, 0x08, 0x2d};
+ int status = 0;
+ Repro repro = Repro_init_zero;
+
+ pb_istream_t stream = pb_istream_from_buffer(data, sizeof(data));
+ TEST(!pb_decode(&stream, Repro_fields, &repro));
+ TEST(get_alloc_count() == 0);
+
+ return status;
+}
diff --git a/tests/regression/issue_647/repro.proto b/tests/regression/issue_647/repro.proto
new file mode 100644
index 0000000..1fe7777
--- /dev/null
+++ b/tests/regression/issue_647/repro.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+
+import "nanopb.proto";
+
+message Repro {
+ oneof value_type {
+ bool boolean_value = 1;
+ bytes bytes_value = 5 [(nanopb).type = FT_POINTER];
+ }
+}
diff --git a/tests/typename_mangling/SConscript b/tests/typename_mangling/SConscript
new file mode 100644
index 0000000..219e6e2
--- /dev/null
+++ b/tests/typename_mangling/SConscript
@@ -0,0 +1,20 @@
+# Test mangle_names option
+
+Import('env')
+
+def set_mangling(type):
+ def command(target, source, env):
+ with open(str(source[0])) as src, open(str(target[0]), "w") as dst:
+ dst.write("* mangle_names:{}\n".format(type))
+ dst.write(src.read())
+ return command
+
+env.Command("strip_package.options", "with_package.options", set_mangling("M_STRIP_PACKAGE"))
+env.Command("strip_package.proto", "with_package.proto", Copy("$TARGET", "$SOURCE"))
+env.NanopbProto(["strip_package", "strip_package.options"])
+env.Program(["test_strip_package.c", "strip_package.pb.c"])
+
+env.Command("flatten.options", "with_package.options", set_mangling("M_FLATTEN"))
+env.Command("flatten.proto", "with_package.proto", Copy("$TARGET", "$SOURCE"))
+env.NanopbProto(["flatten", "flatten.options"])
+env.Program(["test_flatten.c", "flatten.pb.c"])
diff --git a/tests/typename_mangling/test_flatten.c b/tests/typename_mangling/test_flatten.c
new file mode 100644
index 0000000..dbc126e
--- /dev/null
+++ b/tests/typename_mangling/test_flatten.c
@@ -0,0 +1,22 @@
+/*
+ * Tests if expected names are generated when M_FLATTEN is used.
+ */
+
+#include <stdio.h>
+#include "unittests.h"
+#include "flatten.pb.h"
+
+int main()
+{
+ TopLevelMessage msg = {0};
+ NestedMessage nmsg = msg.nested;
+ NestedLevel2 nmsg2 = nmsg.nested;
+ NestedLevel3 nmsg3 = nmsg2.nested;
+ nmsg3.nothing = 42;
+
+ msg.short_if_none = ShortIfNone_IfNone_A;
+ msg.short_if_strip_package = ShortIfStripPackage_IfPackage_A;
+ msg.short_if_flatten = IfFlatten_A;
+
+ return nmsg3.nothing; /* this sets `nmsg3` as used, to prevent warning */
+}
diff --git a/tests/typename_mangling/test_strip_package.c b/tests/typename_mangling/test_strip_package.c
new file mode 100644
index 0000000..329de54
--- /dev/null
+++ b/tests/typename_mangling/test_strip_package.c
@@ -0,0 +1,19 @@
+/*
+ * Tests if expected names are generated when M_STRIP_PACKAGE is used.
+ */
+
+#include <stdio.h>
+#include "unittests.h"
+#include "strip_package.pb.h"
+
+int main()
+{
+ TopLevelMessage msg = {0};
+ TopLevelMessage_NestedMessage_NestedLevel2_NestedLevel3 nmsg = msg.nested.nested.nested;
+
+ msg.short_if_none = TopLevelMessage_ShortIfNone_IfNone_A;
+ msg.short_if_strip_package = TopLevelMessage_IfPackage_A;
+ msg.short_if_flatten = TopLevelMessage_ShortIfFlatten_IfFlatten_A;
+
+ return nmsg.nothing; /* marks nmsg as used */
+}
diff --git a/tests/typename_mangling/with_package.options b/tests/typename_mangling/with_package.options
new file mode 100644
index 0000000..115c94d
--- /dev/null
+++ b/tests/typename_mangling/with_package.options
@@ -0,0 +1,5 @@
+* long_names:true
+
+com.example.nanopb.TopLevelMessage.ShortIfNone long_names:false
+TopLevelMessage.ShortIfStripPackage long_names:false
+ShortIfFlatten long_names: false
diff --git a/tests/typename_mangling/with_package.proto b/tests/typename_mangling/with_package.proto
new file mode 100644
index 0000000..c5af6e1
--- /dev/null
+++ b/tests/typename_mangling/with_package.proto
@@ -0,0 +1,38 @@
+syntax = "proto2";
+
+package com.example.nanopb;
+
+message TopLevelMessage {
+ required uint32 base_field = 1;
+ required NestedMessage nested = 2;
+ optional ShortIfNone short_if_none = 3;
+ optional ShortIfStripPackage short_if_strip_package = 4;
+ optional ShortIfFlatten short_if_flatten = 5;
+
+ message NestedMessage {
+ required NestedLevel2 nested = 1;
+
+ message NestedLevel2 {
+ required NestedLevel3 nested = 1;
+
+ message NestedLevel3 {
+ required uint32 nothing = 1;
+ }
+ }
+ }
+
+ enum ShortIfNone {
+ IfNone_A = 1;
+ IfNone_B = 2;
+ }
+
+ enum ShortIfStripPackage {
+ IfPackage_A = 1;
+ IfPackage_B = 2;
+ }
+
+ enum ShortIfFlatten {
+ IfFlatten_A = 1;
+ IfFlatten_B = 2;
+ }
+}
diff --git a/tools/set_version.sh b/tools/set_version.sh
index 0adb597..6aa25eb 100755
--- a/tools/set_version.sh
+++ b/tools/set_version.sh
@@ -12,3 +12,5 @@ VERSION_ONLY=$(echo $1 | sed 's/nanopb-//')
if [[ $1 != *dev ]]
then sed -i -e 's/"version":\s*"[^"]*"/"version": "'$VERSION_ONLY'"/' library.json
fi
+
+sed -i -e 's/version =.*/version = "'$VERSION_ONLY'"/' extra/poetry/pyproject.toml