├── .gitignore ├── LICENSE ├── LICENSE.nanopb ├── Makefile ├── README ├── docs ├── Makefile ├── concepts.rst ├── generator_flow.svg ├── index.rst ├── lsr.css ├── menu.rst └── reference.rst ├── example ├── Makefile ├── client.c ├── common.c ├── common.h ├── fileproto.proto └── server.c ├── generator ├── pb_pb2.py └── pbgen.py ├── include └── pb │ ├── decode.h │ ├── encode.h │ ├── pb.h │ └── pb.proto ├── src ├── decode.c └── encode.c └── tests ├── Makefile ├── callbacks.proto ├── decode_unittests.c ├── encode_unittests.c ├── person.proto ├── test_decode1.c ├── test_decode_callbacks.c ├── test_encode1.c ├── test_encode_callbacks.c ├── unittestproto.proto └── unittests.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .* 3 | !.gitignore 4 | *~ 5 | doc/*.html 6 | doc/*.png 7 | lib/libpb.a 8 | example/client 9 | example/server 10 | *.dSYM 11 | tests/breakpoints 12 | tests/decode_unittests 13 | tests/encode_unittests 14 | tests/person.pb* 15 | tests/test_decode1 16 | tests/test_decode_callbacks 17 | tests/test_encode1 18 | tests/test_encode_callbacks 19 | tests/testperson.pb 20 | example/fileproto.pb* 21 | *.pyc 22 | tests/callbacks.pb* 23 | tests/unittestproto.pb* 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Eguo Wang 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LICENSE.nanopb: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Petteri Aimonen 2 | 3 | This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. 4 | 5 | Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 6 | 7 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 8 | 9 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 10 | 11 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | 3 | #--------------------------------------------- 4 | # Uncomment exactly one of the lines labelled (A), (B), and (C) below 5 | # to switch between compilation modes. 6 | 7 | OPT ?= -O2 -DNDEBUG # (A) Production use (optimized mode) 8 | # OPT ?= -g2 # (B) Debug mode, w/ full line-level debugging symbols 9 | # OPT ?= -O2 -g2 -DNDEBUG # (C) Profiling mode: opt, but w/debugging symbols 10 | #--------------------------------------------- 11 | 12 | CFLAGS = -c -I./include $(OPT) 13 | 14 | LIBOBJECTS = ./src/decode.o ./src/encode.o 15 | LIBRARY = ./lib/libpb.a 16 | SUBDIRS = example tests docs 17 | 18 | 19 | all: $(LIBRARY) 20 | 21 | clean: 22 | -rm -rf ./lib/ ./src/*.o 23 | @for dir in $(SUBDIRS); do \ 24 | $(MAKE) -C $$dir clean || exit $$?; \ 25 | done 26 | 27 | $(LIBRARY): $(LIBOBJECTS) 28 | test -d lib || mkdir lib 29 | $(AR) -rs $@ $(LIBOBJECTS) 30 | 31 | # for libpb developer 32 | descriptor: 33 | -rm -f ./generator/pb_pb2.py 34 | protoc -I./include/pb -I/usr/include --python_out=./generator/ ./include/pb/pb.proto 35 | 36 | .c.o: 37 | $(CC) $(CFLAGS) $< -o $@ 38 | 39 | install: 40 | cp -vf $(LIBRARY) /usr/local/lib/ 41 | cp -vrf ./include/pb /usr/local/include/ 42 | cp -vf ./generator/pbgen.py /usr/local/bin/ 43 | chmod +x /usr/local/bin/pbgen.py 44 | cp -vf ./generator/pb_pb2.py /usr/local/bin/ 45 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | libpb is a small code-size Protocol Buffers implementation, 2 | Licensed under the MIT license (see LICENSE), and Based on 3 | nanopb (Homepage: http://kapsi.fi/~jpa/nanopb/ License: zlib). 4 | 5 | Changes: 6 | - Replaced "import nanopb.proto" with "import pb.proto" in .proto file if you need. 7 | - Rename "nanopb" ext to "pb", eg: required string number = 1 [(pb).max_size = 40]; 8 | - Rename nanopb_generator.py to pbgen.py 9 | See tests and exmple to learn more. 10 | 11 | To compile the library, you'll need these libraries: 12 | protobuf-compiler python-protobuf libprotobuf-dev 13 | 14 | Install libpb is simple: 15 | make 16 | sudo make install 17 | 18 | To run the tests, run make under the tests folder. 19 | If it completes without error, everything is fine. 20 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | HTML = index.html concepts.html reference.html 2 | IMG = generator_flow.png 3 | 4 | all: $(HTML) $(IMG) 5 | 6 | %.png: %.svg 7 | rsvg $< $@ 8 | 9 | %.html: %.rst 10 | rst2html --stylesheet=lsr.css --link-stylesheet $< $@ 11 | 12 | clean: 13 | -rm -f $(HTML) $(IMG) 14 | -------------------------------------------------------------------------------- /docs/concepts.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Nanopb: Basic concepts 3 | ====================== 4 | 5 | .. include :: menu.rst 6 | 7 | The things outlined here are the underlying concepts of the nanopb design. 8 | 9 | .. contents:: 10 | 11 | Proto files 12 | =========== 13 | All Protocol Buffers implementations use .proto files to describe the message format. 14 | The point of these files is to be a portable interface description language. 15 | 16 | Compiling .proto files for nanopb 17 | --------------------------------- 18 | Nanopb uses the Google's protoc compiler to parse the .proto file, and then a python script to generate the C header and source code from it:: 19 | 20 | user@host:~$ protoc -omessage.pb message.proto 21 | user@host:~$ python ../generator/nanopb_generator.py message.pb 22 | Writing to message.h and message.c 23 | user@host:~$ 24 | 25 | Compiling .proto files with nanopb options 26 | ------------------------------------------ 27 | Nanopb defines two extensions for message fields described in .proto files: *max_size* and *max_count*. 28 | These are the maximum size of a string and maximum count of items in an array:: 29 | 30 | required string name = 1 [(nanopb).max_size = 40]; 31 | repeated PhoneNumber phone = 4 [(nanopb).max_count = 5]; 32 | 33 | To use these extensions, you need to place an import statement in the beginning of the file:: 34 | 35 | import "nanopb.proto"; 36 | 37 | This file, in turn, requires the file *google/protobuf/descriptor.proto*. This is usually installed under */usr/include*. Therefore, to compile a .proto file which uses options, use a protoc command similar to:: 38 | 39 | protoc -I/usr/include -Inanopb/generator -I. -omessage.pb message.proto 40 | 41 | Streams 42 | ======= 43 | 44 | Nanopb uses streams for accessing the data in encoded format. 45 | The stream abstraction is very lightweight, and consists of a structure (*pb_ostream_t* or *pb_istream_t*) which contains a pointer to a callback function. 46 | 47 | There are a few generic rules for callback functions: 48 | 49 | #) Return false on IO errors. The encoding or decoding process will abort immediately. 50 | #) Use state to store your own data, such as a file descriptor. 51 | #) *bytes_written* and *bytes_left* are updated by pb_write and pb_read. 52 | #) Your callback may be used with substreams. In this case *bytes_left*, *bytes_written* and *max_size* have smaller values than the original stream. Don't use these values to calculate pointers. 53 | 54 | Output streams 55 | -------------- 56 | 57 | :: 58 | 59 | struct _pb_ostream_t 60 | { 61 | bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count); 62 | void *state; 63 | size_t max_size; 64 | size_t bytes_written; 65 | }; 66 | 67 | The *callback* for output stream may be NULL, in which case the stream simply counts the number of bytes written. In this case, *max_size* is ignored. 68 | 69 | Otherwise, if *bytes_written* + bytes_to_be_written is larger than *max_size*, pb_write returns false before doing anything else. If you don't want to limit the size of the stream, pass SIZE_MAX. 70 | 71 | **Example 1:** 72 | 73 | This is the way to get the size of the message without storing it anywhere:: 74 | 75 | Person myperson = ...; 76 | pb_ostream_t sizestream = {0}; 77 | pb_encode(&sizestream, Person_fields, &myperson); 78 | printf("Encoded size is %d\n", sizestream.bytes_written); 79 | 80 | **Example 2:** 81 | 82 | Writing to stdout:: 83 | 84 | bool callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) 85 | { 86 | FILE *file = (FILE*) stream->state; 87 | return fwrite(buf, 1, count, file) == count; 88 | } 89 | 90 | pb_ostream_t stdoutstream = {&callback, stdout, SIZE_MAX, 0}; 91 | 92 | Input streams 93 | ------------- 94 | For input streams, there are a few extra rules: 95 | 96 | #) If buf is NULL, read from stream but don't store the data. This is used to skip unknown input. 97 | #) You don't need to know the length of the message in advance. After getting EOF error when reading, set bytes_left to 0 and return false. Pb_decode will detect this and if the EOF was in a proper position, it will return true. 98 | 99 | Here is the structure:: 100 | 101 | struct _pb_istream_t 102 | { 103 | bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count); 104 | void *state; 105 | size_t bytes_left; 106 | }; 107 | 108 | The *callback* must always be a function pointer. *Bytes_left* is an upper limit on the number of bytes that will be read. You can use SIZE_MAX if your callback handles EOF as described above. 109 | 110 | **Example:** 111 | 112 | This function binds an input stream to stdin: 113 | 114 | :: 115 | 116 | bool callback(pb_istream_t *stream, uint8_t *buf, size_t count) 117 | { 118 | FILE *file = (FILE*)stream->state; 119 | bool status; 120 | 121 | if (buf == NULL) 122 | { 123 | while (count-- && fgetc(file) != EOF); 124 | return count == 0; 125 | } 126 | 127 | status = (fread(buf, 1, count, file) == count); 128 | 129 | if (feof(file)) 130 | stream->bytes_left = 0; 131 | 132 | return status; 133 | } 134 | 135 | pb_istream_t stdinstream = {&callback, stdin, SIZE_MAX}; 136 | 137 | Data types 138 | ========== 139 | 140 | Most Protocol Buffers datatypes have directly corresponding C datatypes, such as int32 is int32_t, float is float and bool is bool. However, the variable-length datatypes are more complex: 141 | 142 | 1) Strings, bytes and repeated fields of any type map to callback functions by default. 143 | 2) If there is a special option *(nanopb).max_size* specified in the .proto file, string maps to null-terminated char array and bytes map to a structure containing a char array and a size field. 144 | 3) If there is a special option *(nanopb).max_count* specified on a repeated field, it maps to an array of whatever type is being repeated. Another field will be created for the actual number of entries stored. 145 | 146 | =============================================================================== ======================= 147 | field in .proto autogenerated in .h 148 | =============================================================================== ======================= 149 | required string name = 1; pb_callback_t name; 150 | required string name = 1 [(nanopb).max_size = 40]; char name[40]; 151 | repeated string name = 1 [(nanopb).max_size = 40]; pb_callback_t name; 152 | repeated string name = 1 [(nanopb).max_size = 40, (nanopb).max_count = 5]; | size_t name_count; 153 | | char name[5][40]; 154 | required bytes data = 1 [(nanopb).max_size = 40]; | typedef struct { 155 | | size_t size; 156 | | uint8_t bytes[40]; 157 | | } Person_data_t; 158 | | Person_data_t data; 159 | =============================================================================== ======================= 160 | 161 | The maximum lengths are checked in runtime. If string/bytes/array exceeds the allocated length, *pb_decode* will return false. 162 | 163 | Field callbacks 164 | =============== 165 | When a field has dynamic length, nanopb cannot statically allocate storage for it. Instead, it allows you to handle the field in whatever way you want, using a callback function. 166 | 167 | The `pb_callback_t`_ structure contains a function pointer and a *void* pointer you can use for passing data to the callback. If the function pointer is NULL, the field will be skipped. The actual behavior of the callback function is different in encoding and decoding modes. 168 | 169 | .. _`pb_callback_t`: reference.html#pb-callback-t 170 | 171 | Encoding callbacks 172 | ------------------ 173 | :: 174 | 175 | bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg); 176 | 177 | When encoding, the callback should write out complete fields, including the wire type and field number tag. It can write as many or as few fields as it likes. For example, if you want to write out an array as *repeated* field, you should do it all in a single call. 178 | 179 | Usually you can use `pb_encode_tag_for_field`_ to encode the wire type and tag number of the field. However, if you want to encode a repeated field as a packed array, you must call `pb_encode_tag`_ instead to specify a wire type of *PB_WT_STRING*. 180 | 181 | If the callback is used in a submessage, it will be called multiple times during a single call to `pb_encode`_. In this case, it must produce the same amount of data every time. If the callback is directly in the main message, it is called only once. 182 | 183 | .. _`pb_encode`: reference.html#pb-encode 184 | .. _`pb_encode_tag_for_field`: reference.html#pb-encode-tag-for-field 185 | .. _`pb_encode_tag`: reference.html#pb-encode-tag 186 | 187 | This callback writes out a dynamically sized string:: 188 | 189 | bool write_string(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 190 | { 191 | char *str = get_string_from_somewhere(); 192 | if (!pb_encode_tag_for_field(stream, field)) 193 | return false; 194 | 195 | return pb_encode_string(stream, (uint8_t*)str, strlen(str)); 196 | } 197 | 198 | Decoding callbacks 199 | ------------------ 200 | :: 201 | 202 | bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg); 203 | 204 | When decoding, the callback receives a length-limited substring that reads the contents of a single field. The field tag has already been read. For *string* and *bytes*, the length value has already been parsed, and is available at *stream->bytes_left*. 205 | 206 | The callback will be called multiple times for repeated fields. For packed fields, you can either read multiple values until the stream ends, or leave it to `pb_decode`_ to call your function over and over until all values have been read. 207 | 208 | .. _`pb_decode`: reference.html#pb-decode 209 | 210 | This callback reads multiple integers and prints them:: 211 | 212 | bool read_ints(pb_istream_t *stream, const pb_field_t *field, void *arg) 213 | { 214 | while (stream->bytes_left) 215 | { 216 | uint64_t value; 217 | if (!pb_decode_varint(stream, &value)) 218 | return false; 219 | printf("%lld\n", value); 220 | } 221 | return true; 222 | } 223 | 224 | Field description array 225 | ======================= 226 | 227 | For using the *pb_encode* and *pb_decode* functions, you need an array of pb_field_t constants describing the structure you wish to encode. This description is usually autogenerated from .proto file. 228 | 229 | For example this submessage in the Person.proto file:: 230 | 231 | message Person { 232 | message PhoneNumber { 233 | required string number = 1 [(nanopb).max_size = 40]; 234 | optional PhoneType type = 2 [default = HOME]; 235 | } 236 | } 237 | 238 | generates this field description array for the structure *Person_PhoneNumber*:: 239 | 240 | const pb_field_t Person_PhoneNumber_fields[3] = { 241 | {1, PB_HTYPE_REQUIRED | PB_LTYPE_STRING, 242 | offsetof(Person_PhoneNumber, number), 0, 243 | pb_membersize(Person_PhoneNumber, number), 0, 0}, 244 | 245 | {2, PB_HTYPE_OPTIONAL | PB_LTYPE_VARINT, 246 | pb_delta(Person_PhoneNumber, type, number), 247 | pb_delta(Person_PhoneNumber, has_type, type), 248 | pb_membersize(Person_PhoneNumber, type), 0, 249 | &Person_PhoneNumber_type_default}, 250 | 251 | PB_LAST_FIELD 252 | }; 253 | 254 | 255 | Return values and error handling 256 | ================================ 257 | 258 | Most functions in nanopb return bool: *true* means success, *false* means failure. If this is enough for you, skip this section. 259 | 260 | For simplicity, nanopb doesn't define it's own error codes. This might be added if there is a compelling need for it. You can however deduce something about the error causes: 261 | 262 | 1) Running out of memory. Because everything is allocated from the stack, nanopb can't detect this itself. Encoding or decoding the same type of a message always takes the same amount of stack space. Therefore, if it works once, it works always. 263 | 2) Invalid field description. These are usually stored as constants, so if it works under the debugger, it always does. 264 | 3) IO errors in your own stream callbacks. Because encoding/decoding stops at the first error, you can overwrite the *state* field in the struct and store your own error code there. 265 | 4) Errors that happen in your callback functions. You can use the state field in the callback structure. 266 | 5) Exceeding the max_size or bytes_left of a stream. 267 | 6) Exceeding the max_size of a string or array field 268 | 7) Invalid protocol buffers binary message. It's not like you could recover from it anyway, so a simple failure should be enough. 269 | 270 | In my opinion, it is enough that 1. and 2. can be resolved using a debugger. 271 | 272 | However, you may be interested which of the remaining conditions caused the error. For 3. and 4., you can set and check the state. If you have to detect 5. and 6., you should convert the fields to callback type. Any remaining problem is of type 7. 273 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Nanopb: Protocol Buffers with small code size 3 | ============================================= 4 | 5 | .. include :: menu.rst 6 | 7 | Nanopb is an ANSI-C library for encoding and decoding messages in Google's `Protocol Buffers`__ format with minimal requirements for RAM and code space. 8 | It is primarily suitable for 32-bit microcontrollers. 9 | 10 | __ http://code.google.com/apis/protocolbuffers/ 11 | 12 | Overall structure 13 | ================= 14 | 15 | For the runtime program, you always need *pb.h* for type declarations. 16 | Depending on whether you want to encode, decode, or both, you also need *pb_encode.h/c* or *pb_decode.h/c*. 17 | 18 | The high-level encoding and decoding functions take an array of *pb_field_t* structures, which describes the fields of a message structure. Usually you want these autogenerated from a *.proto* file. The tool script *nanopb_generator.py* accomplishes this. 19 | 20 | .. image:: generator_flow.png 21 | 22 | So a typical project might include these files: 23 | 24 | 1) Nanopb runtime library: 25 | - pb.h 26 | - pb_decode.h and pb_decode.c (needed for decoding messages) 27 | - pb_encode.h and pb_encode.c (needed for encoding messages) 28 | 2) Protocol description (you can have many): 29 | - person.proto (just an example) 30 | - person.pb.c (autogenerated, contains initializers for const arrays) 31 | - person.pb.h (autogenerated, contains type declarations) 32 | 33 | Features and limitations 34 | ======================== 35 | 36 | **Features** 37 | 38 | #) Pure C runtime 39 | #) Small code size (2–10 kB depending on processor) 40 | #) Small ram usage (typically 200 bytes) 41 | #) Allows specifying maximum size for strings and arrays, so that they can be allocated statically. 42 | #) No malloc needed: everything can be allocated statically or on the stack. 43 | #) You can use either encoder or decoder alone to cut the code size in half. 44 | 45 | **Limitations** 46 | 47 | #) User must provide callbacks when decoding arrays or strings without maximum size. Malloc support could be added as a separate module. 48 | #) Some speed has been sacrificed for code size. For example varint calculations are always done in 64 bits. 49 | #) Encoding is focused on writing to streams. For memory buffers only it could be made more efficient. 50 | #) The deprecated Protocol Buffers feature called "groups" is not supported. 51 | #) Fields in the generated structs are ordered by the tag number, instead of the natural ordering in .proto file. 52 | #) Unknown fields are not preserved when decoding and re-encoding a message. 53 | #) Reflection (runtime introspection) is not supported. E.g. you can't request a field by giving its name in a string. 54 | #) Numeric arrays are always encoded as packed, even if not marked as packed in .proto. This causes incompatibility with decoders that do not support packed format. 55 | #) Cyclic references between messages are not supported. They could be supported in callback-mode if there was an option in the generator to set the mode. 56 | 57 | Getting started 58 | =============== 59 | 60 | For starters, consider this simple message:: 61 | 62 | message Example { 63 | required int32 value = 1; 64 | } 65 | 66 | Save this in *message.proto* and compile it:: 67 | 68 | user@host:~$ protoc -omessage.pb message.proto 69 | user@host:~$ python nanopb/generator/nanopb_generator.py message.pb 70 | 71 | You should now have in *message.pb.h*:: 72 | 73 | typedef struct { 74 | int32_t value; 75 | } Example; 76 | 77 | extern const pb_field_t Example_fields[2]; 78 | 79 | Now in your main program do this to encode a message:: 80 | 81 | Example mymessage = {42}; 82 | uint8_t buffer[10]; 83 | pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); 84 | pb_encode(&stream, Example_fields, &mymessage); 85 | 86 | After that, buffer will contain the encoded message. 87 | The number of bytes in the message is stored in *stream.bytes_written*. 88 | You can feed the message to *protoc --decode=Example message.proto* to verify its validity. 89 | 90 | For complete examples of the simple cases, see *tests/test_decode1.c* and *tests/test_encode1.c*. For an example with network interface, see the *example* subdirectory. 91 | 92 | Compiler requirements 93 | ===================== 94 | Nanopb should compile with most ansi-C compatible compilers. It however requires a few header files to be available: 95 | 96 | #) *string.h*, with these functions: *strlen*, *memcpy*, *memset* 97 | #) *stdint.h*, for definitions of *int32_t* etc. 98 | #) *stddef.h*, for definition of *size_t* 99 | #) *stdbool.h*, for definition of *bool* 100 | 101 | If these header files do not come with your compiler, you should be able to find suitable replacements online. Mostly the requirements are very simple, just a few basic functions and typedefs. 102 | 103 | Debugging and testing 104 | ===================== 105 | Extensive unittests are included under the *tests* folder. Just type *make* there to run the tests. 106 | 107 | This also generates a file called *breakpoints* which includes all lines returning *false* in nanopb. You can use this in gdb by typing *source breakpoints*, after which gdb will break on first nanopb error. 108 | 109 | Wishlist 110 | ======== 111 | #) A specialized encoder for encoding to a memory buffer. Should serialize in reverse order to avoid having to determine submessage size beforehand. 112 | #) A cleaner rewrite of the Python-based source generator. 113 | #) Better performance for 16- and 8-bit platforms: use smaller datatypes where possible. 114 | -------------------------------------------------------------------------------- /docs/lsr.css: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Peter Parente 3 | Date: 2008/01/22 4 | Version: 1.0 (modified) 5 | Copyright: This stylesheet has been placed in the public domain - free to edit and use for all uses. 6 | */ 7 | 8 | body { 9 | font: 100% sans-serif; 10 | background: #ffffff; 11 | color: black; 12 | margin: 2em; 13 | padding: 0em 2em; 14 | } 15 | 16 | p.topic-title { 17 | font-weight: bold; 18 | } 19 | 20 | table.docinfo { 21 | text-align: left; 22 | margin: 2em 0em; 23 | } 24 | 25 | a[href] { 26 | color: #436976; 27 | background-color: transparent; 28 | } 29 | 30 | a.toc-backref { 31 | text-decoration: none; 32 | } 33 | 34 | h1 a[href] { 35 | color: #003a6b; 36 | text-decoration: none; 37 | background-color: transparent; 38 | } 39 | 40 | a.strong { 41 | font-weight: bold; 42 | } 43 | 44 | img { 45 | margin: 0; 46 | border: 0; 47 | } 48 | 49 | p { 50 | margin: 0.5em 0 1em 0; 51 | line-height: 1.5em; 52 | } 53 | 54 | p a:visited { 55 | color: purple; 56 | background-color: transparent; 57 | } 58 | 59 | p a:active { 60 | color: red; 61 | background-color: transparent; 62 | } 63 | 64 | a:hover { 65 | text-decoration: none; 66 | } 67 | 68 | p img { 69 | border: 0; 70 | margin: 0; 71 | } 72 | 73 | p.rubric { 74 | font-weight: bold; 75 | font-style: italic; 76 | } 77 | 78 | em { 79 | font-style: normal; 80 | font-family: monospace; 81 | font-weight: bold; 82 | } 83 | 84 | pre { 85 | border-left: 3px double #aaa; 86 | padding: 5px 10px; 87 | background-color: #f6f6f6; 88 | } 89 | 90 | h1.title { 91 | color: #003a6b; 92 | font-size: 180%; 93 | margin-bottom: 0em; 94 | } 95 | 96 | h2.subtitle { 97 | color: #003a6b; 98 | border-bottom: 0px; 99 | } 100 | 101 | h1, h2, h3, h4, h5, h6 { 102 | color: #555; 103 | background-color: transparent; 104 | margin: 0em; 105 | padding-top: 0.5em; 106 | } 107 | 108 | h1 { 109 | font-size: 150%; 110 | margin-bottom: 0.5em; 111 | border-bottom: 2px solid #aaa; 112 | } 113 | 114 | h2 { 115 | font-size: 130%; 116 | margin-bottom: 0.5em; 117 | border-bottom: 1px solid #aaa; 118 | } 119 | 120 | h3 { 121 | font-size: 120%; 122 | margin-bottom: 0.5em; 123 | } 124 | 125 | h4 { 126 | font-size: 110%; 127 | font-weight: bold; 128 | margin-bottom: 0.5em; 129 | } 130 | 131 | h5 { 132 | font-size: 105%; 133 | font-weight: bold; 134 | margin-bottom: 0.5em; 135 | } 136 | 137 | h6 { 138 | font-size: 100%; 139 | font-weight: bold; 140 | margin-bottom: 0.5em; 141 | } 142 | 143 | dt { 144 | font-style: italic; 145 | } 146 | 147 | dd { 148 | margin-bottom: 1.5em; 149 | } 150 | 151 | div.admonition, div.note, div.tip, div.caution, div.important { 152 | margin: 2em 2em; 153 | padding: 0em 1em; 154 | border-top: 1px solid #aaa; 155 | border-left: 1px solid #aaa; 156 | border-bottom: 2px solid #555; 157 | border-right: 2px solid #555; 158 | } 159 | 160 | div.important { 161 | background: transparent url('../images/important.png') 10px 2px no-repeat; 162 | } 163 | 164 | div.caution { 165 | background: transparent url('../images/caution.png') 10px 2px no-repeat; 166 | } 167 | 168 | div.note { 169 | background: transparent url('../images/note.png') 10px 2px no-repeat; 170 | } 171 | 172 | div.tip { 173 | background: transparent url('../images/tip.png') 10px 2px no-repeat; 174 | } 175 | 176 | div.admonition-example { 177 | background: transparent url('../images/tip.png') 10px 2px no-repeat; 178 | } 179 | 180 | div.admonition-critical-example { 181 | background: transparent url('../images/important.png') 10px 2px no-repeat; 182 | } 183 | 184 | p.admonition-title { 185 | font-weight: bold; 186 | border-bottom: 1px solid #aaa; 187 | padding-left: 30px; 188 | } 189 | 190 | table.docutils { 191 | text-align: left; 192 | border: 1px solid gray; 193 | border-collapse: collapse; 194 | margin: 1.5em 0em; 195 | } 196 | 197 | table.docutils caption { 198 | font-style: italic; 199 | } 200 | 201 | table.docutils td, table.docutils th { 202 | padding: 0.25em 0.5em; 203 | } 204 | 205 | th.field-name { 206 | text-align: right; 207 | width: 15em; 208 | } 209 | 210 | table.docutils th { 211 | font-family: monospace; 212 | background-color: #f6f6f6; 213 | vertical-align: middle; 214 | } 215 | 216 | table.field-list { 217 | border: none; 218 | } 219 | 220 | div.sidebar { 221 | margin: 2em 2em 2em 0em; 222 | padding: 0em 1em; 223 | border-top: 1px solid #aaa; 224 | border-left: 1px solid #aaa; 225 | border-bottom: 2px solid #555; 226 | border-right: 2px solid #555; 227 | } 228 | 229 | p.sidebar-title { 230 | margin-bottom: 0em; 231 | color: #003a6b; 232 | border-bottom: 1px solid #aaa; 233 | font-weight: bold; 234 | } 235 | 236 | p.sidebar-subtitle { 237 | margin-top: 0em; 238 | font-style: italic; 239 | color: #003a6b; 240 | } 241 | -------------------------------------------------------------------------------- /docs/menu.rst: -------------------------------------------------------------------------------- 1 | .. sidebar :: Documentation index 2 | 3 | 1) `Overview`_ 4 | 2) `Concepts`_ 5 | 3) `API reference`_ 6 | 7 | .. _`Overview`: index.html 8 | .. _`Concepts`: concepts.html 9 | .. _`API reference`: reference.html 10 | 11 | -------------------------------------------------------------------------------- /docs/reference.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Nanopb: API reference 3 | ===================== 4 | 5 | .. include :: menu.rst 6 | 7 | .. contents :: 8 | 9 | pb.h 10 | ==== 11 | 12 | pb_type_t 13 | --------- 14 | Defines the encoder/decoder behaviour that should be used for a field. :: 15 | 16 | typedef enum { ... } pb_type_t; 17 | 18 | The low-order byte of the enumeration values defines the function that can be used for encoding and decoding the field data: 19 | 20 | ==================== ===== ================================================ 21 | LTYPE identifier Value Storage format 22 | ==================== ===== ================================================ 23 | PB_LTYPE_VARINT 0x00 Integer. 24 | PB_LTYPE_SVARINT 0x01 Integer, zigzag encoded. 25 | PB_LTYPE_FIXED 0x02 Integer or floating point. 26 | PB_LTYPE_BYTES 0x03 Structure with *size_t* field and byte array. 27 | PB_LTYPE_STRING 0x04 Null-terminated string. 28 | PB_LTYPE_SUBMESSAGE 0x05 Submessage structure. 29 | ==================== ===== ================================================ 30 | 31 | The high-order byte defines whether the field is required, optional, repeated or callback: 32 | 33 | ==================== ===== ================================================ 34 | HTYPE identifier Value Field handling 35 | ==================== ===== ================================================ 36 | PB_HTYPE_REQUIRED 0x00 Verify that field exists in decoded message. 37 | PB_HTYPE_OPTIONAL 0x10 Use separate *has_* boolean to specify 38 | whether the field is present. 39 | PB_HTYPE_ARRAY 0x20 A repeated field with preallocated array. 40 | Separate *_count* for number of items. 41 | PB_HTYPE_CALLBACK 0x30 A field with dynamic storage size, data is 42 | actually a pointer to a structure containing a 43 | callback function. 44 | ==================== ===== ================================================ 45 | 46 | pb_field_t 47 | ---------- 48 | Describes a single structure field with memory position in relation to others. The descriptions are usually autogenerated. :: 49 | 50 | typedef struct _pb_field_t pb_field_t; 51 | struct _pb_field_t { 52 | uint8_t tag; 53 | pb_type_t type; 54 | uint8_t data_offset; 55 | int8_t size_offset; 56 | uint8_t data_size; 57 | uint8_t array_size; 58 | const void *ptr; 59 | } pb_packed; 60 | 61 | :tag: Tag number of the field or 0 to terminate a list of fields. 62 | :type: LTYPE and HTYPE of the field. 63 | :data_offset: Offset of field data, relative to the end of the previous field. 64 | :size_offset: Offset of *bool* flag for optional fields or *size_t* count for arrays, relative to field data. 65 | :data_size: Size of a single data entry, in bytes. For PB_LTYPE_BYTES, the size of the byte array inside the containing structure. For PB_HTYPE_CALLBACK, size of the C data type if known. 66 | :array_size: Maximum number of entries in an array, if it is an array type. 67 | :ptr: Pointer to default value for optional fields, or to submessage description for PB_LTYPE_SUBMESSAGE. 68 | 69 | The *uint8_t* datatypes limit the maximum size of a single item to 255 bytes and arrays to 255 items. Compiler will warn "Initializer too large for type" if the limits are exceeded. The types can be changed to larger ones if necessary. 70 | 71 | pb_bytes_array_t 72 | ---------------- 73 | An byte array with a field for storing the length:: 74 | 75 | typedef struct { 76 | size_t size; 77 | uint8_t bytes[1]; 78 | } pb_bytes_array_t; 79 | 80 | In an actual array, the length of *bytes* may be different. 81 | 82 | pb_callback_t 83 | ------------- 84 | Part of a message structure, for fields with type PB_HTYPE_CALLBACK:: 85 | 86 | typedef struct _pb_callback_t pb_callback_t; 87 | struct _pb_callback_t { 88 | union { 89 | bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg); 90 | bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg); 91 | } funcs; 92 | 93 | void *arg; 94 | }; 95 | 96 | The *arg* is passed to the callback when calling. It can be used to store any information that the callback might need. 97 | 98 | When calling `pb_encode`_, *funcs.encode* is used, and similarly when calling `pb_decode`_, *funcs.decode* is used. The function pointers are stored in the same memory location but are of incompatible types. You can set the function pointer to NULL to skip the field. 99 | 100 | pb_wire_type_t 101 | -------------- 102 | Protocol Buffers wire types. These are used with `pb_encode_tag`_. :: 103 | 104 | typedef enum { 105 | PB_WT_VARINT = 0, 106 | PB_WT_64BIT = 1, 107 | PB_WT_STRING = 2, 108 | PB_WT_32BIT = 5 109 | } pb_wire_type_t; 110 | 111 | pb_encode.h 112 | =========== 113 | 114 | pb_ostream_from_buffer 115 | ---------------------- 116 | Constructs an output stream for writing into a memory buffer. This is just a helper function, it doesn't do anything you couldn't do yourself in a callback function. It uses an internal callback that stores the pointer in stream *state* field. :: 117 | 118 | pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize); 119 | 120 | :buf: Memory buffer to write into. 121 | :bufsize: Maximum number of bytes to write. 122 | :returns: An output stream. 123 | 124 | After writing, you can check *stream.bytes_written* to find out how much valid data there is in the buffer. 125 | 126 | pb_write 127 | -------- 128 | Writes data to an output stream. Always use this function, instead of trying to call stream callback manually. :: 129 | 130 | bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count); 131 | 132 | :stream: Output stream to write to. 133 | :buf: Pointer to buffer with the data to be written. 134 | :count: Number of bytes to write. 135 | :returns: True on success, false if maximum length is exceeded or an IO error happens. 136 | 137 | If an error happens, *bytes_written* is not incremented. Depending on the callback used, calling pb_write again after it has failed once may be dangerous. Nanopb itself never does this, instead it returns the error to user application. The builtin pb_ostream_from_buffer is safe to call again after failed write. 138 | 139 | pb_encode 140 | --------- 141 | Encodes the contents of a structure as a protocol buffers message and writes it to output stream. :: 142 | 143 | bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct); 144 | 145 | :stream: Output stream to write to. 146 | :fields: A field description array, usually autogenerated. 147 | :src_struct: Pointer to the data that will be serialized. 148 | :returns: True on success, false on IO error, on detectable errors in field description, or if a field encoder returns false. 149 | 150 | Normally pb_encode simply walks through the fields description array and serializes each field in turn. However, submessages must be serialized twice: first to calculate their size and then to actually write them to output. This causes some constraints for callback fields, which must return the same data on every call. 151 | 152 | pb_encode_varint 153 | ---------------- 154 | Encodes an unsigned integer in the varint_ format. :: 155 | 156 | bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); 157 | 158 | :stream: Output stream to write to. 1-10 bytes will be written. 159 | :value: Value to encode. 160 | :returns: True on success, false on IO error. 161 | 162 | .. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints 163 | 164 | pb_encode_tag 165 | ------------- 166 | Starts a field in the Protocol Buffers binary format: encodes the field number and the wire type of the data. :: 167 | 168 | bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number); 169 | 170 | :stream: Output stream to write to. 1-5 bytes will be written. 171 | :wiretype: PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT 172 | :field_number: Identifier for the field, defined in the .proto file. 173 | :returns: True on success, false on IO error. 174 | 175 | pb_encode_tag_for_field 176 | ----------------------- 177 | Same as `pb_encode_tag`_, except takes the parameters from a *pb_field_t* structure. :: 178 | 179 | bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field); 180 | 181 | :stream: Output stream to write to. 1-5 bytes will be written. 182 | :field: Field description structure. Usually autogenerated. 183 | :returns: True on success, false on IO error or unknown field type. 184 | 185 | This function only considers the LTYPE of the field. You can use it from your field callbacks, because the source generator writes correct LTYPE also for callback type fields. 186 | 187 | Wire type mapping is as follows: 188 | 189 | ========================= ============ 190 | LTYPEs Wire type 191 | ========================= ============ 192 | VARINT, SVARINT PB_WT_VARINT 193 | FIXED64 PB_WT_64BIT 194 | STRING, BYTES, SUBMESSAGE PB_WT_STRING 195 | FIXED32 PB_WT_32BIT 196 | ========================= ============ 197 | 198 | pb_encode_string 199 | ---------------- 200 | Writes the length of a string as varint and then contents of the string. Used for writing fields with wire type PB_WT_STRING. :: 201 | 202 | bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size); 203 | 204 | :stream: Output stream to write to. 205 | :buffer: Pointer to string data. 206 | :size: Number of bytes in the string. 207 | :returns: True on success, false on IO error. 208 | 209 | .. sidebar:: Field encoders 210 | 211 | The functions with names beginning with *pb_enc_* are called field encoders. Each PB_LTYPE has an own field encoder, which handles translating from C data into Protocol Buffers data. 212 | 213 | By using the *data_size* in the field description and by taking advantage of C casting rules, it has been possible to combine many data types to a single LTYPE. For example, *int32*, *uint32*, *int64*, *uint64*, *bool* and *enum* are all handled by *pb_enc_varint*. 214 | 215 | Each field encoder only encodes the contents of the field. The tag must be encoded separately with `pb_encode_tag_for_field`_. 216 | 217 | You can use the field encoders from your callbacks. Just be aware that the pb_field_t passed to the callback is not directly compatible with most of the encoders. Instead, you must create a new pb_field_t structure and set the data_size according to the data type you pass to *src*. 218 | 219 | pb_enc_varint 220 | ------------- 221 | Field encoder for PB_LTYPE_VARINT. Takes the first *field->data_size* bytes from src, casts them as *uint64_t* and calls `pb_encode_varint`_. :: 222 | 223 | bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 224 | 225 | :stream: Output stream to write to. 226 | :field: Field description structure. Only *data_size* matters. 227 | :src: Pointer to start of the field data. 228 | :returns: True on success, false on IO error. 229 | 230 | pb_enc_svarint 231 | -------------- 232 | Field encoder for PB_LTYPE_SVARINT. Similar to `pb_enc_varint`_, except first zig-zag encodes the value for more efficient negative number encoding. :: 233 | 234 | bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 235 | 236 | (parameters are the same as for `pb_enc_varint`_) 237 | 238 | The number is considered negative if the high-order bit of the value is set. On big endian computers, it is the highest bit of *\*src*. On little endian computers, it is the highest bit of *\*(src + field->data_size - 1)*. 239 | 240 | pb_enc_fixed32 241 | -------------- 242 | Field encoder for PB_LTYPE_FIXED32. Writes the data in little endian order. On big endian computers, reverses the order of bytes. :: 243 | 244 | bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src); 245 | 246 | :stream: Output stream to write to. 247 | :field: Not used. 248 | :src: Pointer to start of the field data. 249 | :returns: True on success, false on IO error. 250 | 251 | pb_enc_fixed64 252 | -------------- 253 | Field encoder for PB_LTYPE_FIXED64. Writes the data in little endian order. On big endian computers, reverses the order of bytes. :: 254 | 255 | bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src); 256 | 257 | (parameters are the same as for `pb_enc_fixed32`_) 258 | 259 | The same function is used for both integers and doubles. This breaks encoding of double values on architectures where they are mixed endian (primarily some arm processors with hardware FPU). 260 | 261 | pb_enc_bytes 262 | ------------ 263 | Field encoder for PB_LTYPE_BYTES. Just calls `pb_encode_string`_. :: 264 | 265 | bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src); 266 | 267 | :stream: Output stream to write to. 268 | :field: Not used. 269 | :src: Pointer to a structure similar to pb_bytes_array_t. 270 | :returns: True on success, false on IO error. 271 | 272 | This function expects a pointer to a structure with a *size_t* field at start, and a variable sized byte array after it. The platform-specific field offset is inferred from *pb_bytes_array_t*, which has a byte array of size 1. 273 | 274 | pb_enc_string 275 | ------------- 276 | Field encoder for PB_LTYPE_STRING. Determines size of string with strlen() and then calls `pb_encode_string`_. :: 277 | 278 | bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src); 279 | 280 | :stream: Output stream to write to. 281 | :field: Not used. 282 | :src: Pointer to a null-terminated string. 283 | :returns: True on success, false on IO error. 284 | 285 | pb_enc_submessage 286 | ----------------- 287 | Field encoder for PB_LTYPE_SUBMESSAGE. Calls `pb_encode`_ to perform the actual encoding. :: 288 | 289 | bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src); 290 | 291 | :stream: Output stream to write to. 292 | :field: Field description structure. The *ptr* field must be a pointer to a field description array for the submessage. 293 | :src: Pointer to the structure where submessage data is. 294 | :returns: True on success, false on IO errors, pb_encode errors or if submessage size changes between calls. 295 | 296 | In Protocol Buffers format, the submessage size must be written before the submessage contents. Therefore, this function has to encode the submessage twice in order to know the size beforehand. 297 | 298 | If the submessage contains callback fields, the callback function might misbehave and write out a different amount of data on the second call. This situation is recognized and *false* is returned, but it is up to the caller to ensure that the receiver of the message does not interpret it as valid data. 299 | 300 | pb_decode.h 301 | =========== 302 | 303 | pb_istream_from_buffer 304 | ---------------------- 305 | Helper function for creating an input stream that reads data from a memory buffer. :: 306 | 307 | pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize); 308 | 309 | :buf: Pointer to byte array to read from. 310 | :bufsize: Size of the byte array. 311 | :returns: An input stream ready to use. 312 | 313 | pb_read 314 | ------- 315 | Read data from input stream. Always use this function, don't try to call the stream callback directly. :: 316 | 317 | bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count); 318 | 319 | :stream: Input stream to read from. 320 | :buf: Buffer to store the data to, or NULL to just read data without storing it anywhere. 321 | :count: Number of bytes to read. 322 | :returns: True on success, false if *stream->bytes_left* is less than *count* or if an IO error occurs. 323 | 324 | End of file is signalled by *stream->bytes_left* being zero after pb_read returns false. 325 | 326 | pb_decode_varint 327 | ---------------- 328 | Read and decode a varint_ encoded integer. :: 329 | 330 | bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); 331 | 332 | :stream: Input stream to read from. 1-10 bytes will be read. 333 | :dest: Storage for the decoded integer. Value is undefined on error. 334 | :returns: True on success, false if value exceeds uint64_t range or an IO error happens. 335 | 336 | pb_skip_varint 337 | -------------- 338 | Skip a varint_ encoded integer without decoding it. :: 339 | 340 | bool pb_skip_varint(pb_istream_t *stream); 341 | 342 | :stream: Input stream to read from. Will read 1 byte at a time until the MSB is clear. 343 | :returns: True on success, false on IO error. 344 | 345 | pb_skip_string 346 | -------------- 347 | Skip a varint-length-prefixed string. This means skipping a value with wire type PB_WT_STRING. :: 348 | 349 | bool pb_skip_string(pb_istream_t *stream); 350 | 351 | :stream: Input stream to read from. 352 | :returns: True on success, false on IO error or length exceeding uint32_t. 353 | 354 | pb_decode 355 | --------- 356 | Read and decode all fields of a structure. Reads until EOF on input stream. :: 357 | 358 | bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct); 359 | 360 | :stream: Input stream to read from. 361 | :fields: A field description array. Usually autogenerated. 362 | :dest_struct: Pointer to structure where data will be stored. 363 | :returns: True on success, false on IO error, on detectable errors in field description, if a field encoder returns false or if a required field is missing. 364 | 365 | In Protocol Buffers binary format, EOF is only allowed between fields. If it happens anywhere else, pb_decode will return *false*. If pb_decode returns false, you cannot trust any of the data in the structure. 366 | 367 | In addition to EOF, the pb_decode implementation supports terminating a message with a 0 byte. This is compatible with the official Protocol Buffers because 0 is never a valid field tag. 368 | 369 | For optional fields, this function applies the default value and sets *has_* to false if the field is not present. 370 | 371 | Because of memory concerns, the detection of missing required fields is not perfect if the structure contains more than 32 fields. 372 | 373 | .. sidebar:: Field decoders 374 | 375 | The functions with names beginning with *pb_dec_* are called field decoders. Each PB_LTYPE has an own field decoder, which handles translating from Protocol Buffers data to C data. 376 | 377 | Each field decoder reads and decodes a single value. For arrays, the decoder is called repeatedly. 378 | 379 | You can use the decoders from your callbacks. Just be aware that the pb_field_t passed to the callback is not directly compatible with most of the field decoders. Instead, you must create a new pb_field_t structure and set the data_size according to the data type you pass to *dest*. 380 | 381 | pb_dec_varint 382 | ------------- 383 | Field decoder for PB_LTYPE_VARINT. :: 384 | 385 | bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) 386 | 387 | :stream: Input stream to read from. 1-10 bytes will be read. 388 | :field: Field description structure. Only *field->data_size* matters. 389 | :dest: Pointer to destination integer. Must have size of *field->data_size* bytes. 390 | :returns: True on success, false on IO errors or if `pb_decode_varint`_ fails. 391 | 392 | This function first calls `pb_decode_varint`_. It then copies the first bytes of the 64-bit result value to *dest*, or on big endian architectures, the last bytes. 393 | 394 | pb_dec_svarint 395 | -------------- 396 | Field decoder for PB_LTYPE_SVARINT. Similar to `pb_dec_varint`_, except that it performs zigzag-decoding on the value. :: 397 | 398 | bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest); 399 | 400 | (parameters are the same as `pb_dec_varint`_) 401 | 402 | pb_dec_fixed32 403 | -------------- 404 | Field decoder for PB_LTYPE_FIXED32. :: 405 | 406 | bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest); 407 | 408 | :stream: Input stream to read from. 1-10 bytes will be read. 409 | :field: Not used. 410 | :dest: Pointer to destination integer. Must have size of *field->data_size* bytes. 411 | :returns: True on success, false on IO errors or if `pb_decode_varint`_ fails. 412 | 413 | This function reads 4 bytes from the input stream. 414 | On big endian architectures, it then reverses the order of the bytes. 415 | Finally, it writes the bytes to *dest*. 416 | 417 | pb_dec_fixed64 418 | -------------- 419 | Field decoder for PB_LTYPE_FIXED64. :: 420 | 421 | bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest); 422 | 423 | Same as `pb_dec_fixed32`_, except this reads 8 bytes. 424 | 425 | pb_dec_bytes 426 | ------------ 427 | Field decoder for PB_LTYPE_BYTES. Reads a length-prefixed block of bytes. :: 428 | 429 | bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest); 430 | 431 | :stream: Input stream to read from. 432 | :field: Field description structure. Only *field->data_size* matters. 433 | :dest: Pointer to a structure similar to pb_bytes_array_t. 434 | :returns: True on success, false on IO error or if length exceeds the array size. 435 | 436 | This function expects a pointer to a structure with a *size_t* field at start, and a variable sized byte array after it. It will deduce the maximum size of the array from *field->data_size*. 437 | 438 | pb_dec_string 439 | ------------- 440 | Field decoder for PB_LTYPE_STRING. Reads a length-prefixed string. :: 441 | 442 | bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest); 443 | 444 | :stream: Input stream to read from. 445 | :field: Field description structure. Only *field->data_size* matters. 446 | :dest: Pointer to a character array of size *field->data_size*. 447 | :returns: True on success, false on IO error or if length exceeds the array size. 448 | 449 | This function null-terminates the string when successful. On error, the contents of the destination array is undefined. 450 | 451 | pb_dec_submessage 452 | ----------------- 453 | Field decoder for PB_LTYPE_SUBMESSAGE. Calls `pb_decode`_ to perform the actual decoding. :: 454 | 455 | bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest) 456 | 457 | :stream: Input stream to read from. 458 | :field: Field description structure. Only *field->ptr* matters. 459 | :dest: Pointer to the destination structure. 460 | :returns: True on success, false on IO error or if `pb_decode`_ fails. 461 | 462 | The *field->ptr* should be a pointer to *pb_field_t* array describing the submessage. 463 | 464 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-ansi -Wall -I../include -g -O0 2 | LDFLAGS = -L../lib -lpb 3 | 4 | all: server client 5 | 6 | clean: 7 | -rm -rf server client fileproto.pb.c fileproto.pb.h *.dSYM 8 | 9 | %: %.c fileproto.pb.h fileproto.pb.c 10 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< fileproto.pb.c common.c 11 | 12 | fileproto.pb.c fileproto.pb.h: fileproto.proto ../generator/pbgen.py 13 | protoc -I. -I../include -I/usr/include -ofileproto.pb $< 14 | python ../generator/pbgen.py fileproto.pb 15 | -------------------------------------------------------------------------------- /example/client.c: -------------------------------------------------------------------------------- 1 | /* This is a simple TCP client that connects to port 1234 and prints a list 2 | * of files in a given directory. 3 | * 4 | * It directly deserializes and serializes messages from network, minimizing 5 | * memory use. 6 | * 7 | * For flexibility, this example is implemented using posix api. 8 | * In a real embedded system you would typically use some other kind of 9 | * a communication and filesystem layer. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "fileproto.pb.h" 24 | #include "common.h" 25 | 26 | bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void *arg) 27 | { 28 | FileInfo fileinfo; 29 | 30 | if (!pb_decode(stream, FileInfo_fields, &fileinfo)) 31 | return false; 32 | 33 | printf("%-10lld %s\n", fileinfo.inode, fileinfo.name); 34 | 35 | return true; 36 | } 37 | 38 | bool listdir(int fd, char *path) 39 | { 40 | ListFilesRequest request; 41 | ListFilesResponse response; 42 | pb_istream_t input = pb_istream_from_socket(fd); 43 | pb_ostream_t output = pb_ostream_from_socket(fd); 44 | uint8_t zero = 0; 45 | 46 | if (path == NULL) 47 | { 48 | request.has_path = false; 49 | } 50 | else 51 | { 52 | request.has_path = true; 53 | if (strlen(path) + 1 > sizeof(request.path)) 54 | { 55 | fprintf(stderr, "Too long path.\n"); 56 | return false; 57 | } 58 | 59 | strcpy(request.path, path); 60 | } 61 | 62 | if (!pb_encode(&output, ListFilesRequest_fields, &request)) 63 | { 64 | fprintf(stderr, "Encoding failed.\n"); 65 | return false; 66 | } 67 | 68 | /* We signal the end of request with a 0 tag. */ 69 | pb_write(&output, &zero, 1); 70 | 71 | response.file.funcs.decode = &printfile_callback; 72 | 73 | if (!pb_decode(&input, ListFilesResponse_fields, &response)) 74 | { 75 | fprintf(stderr, "Decoding failed.\n"); 76 | return false; 77 | } 78 | 79 | if (response.path_error) 80 | { 81 | fprintf(stderr, "Server reported error.\n"); 82 | return false; 83 | } 84 | 85 | return true; 86 | } 87 | 88 | int main(int argc, char **argv) 89 | { 90 | int sockfd; 91 | struct sockaddr_in servaddr; 92 | char *path = NULL; 93 | 94 | if (argc > 1) 95 | path = argv[1]; 96 | 97 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 98 | 99 | memset(&servaddr, 0, sizeof(servaddr)); 100 | servaddr.sin_family = AF_INET; 101 | servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 102 | servaddr.sin_port = htons(1234); 103 | 104 | if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) 105 | { 106 | perror("connect"); 107 | return 1; 108 | } 109 | 110 | if (!listdir(sockfd, path)) 111 | return 2; 112 | 113 | close(sockfd); 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /example/common.c: -------------------------------------------------------------------------------- 1 | /* Simple binding of nanopb streams to TCP sockets. 2 | */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | 11 | static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) 12 | { 13 | int fd = (int)stream->state; 14 | return send(fd, buf, count, 0) == count; 15 | } 16 | 17 | static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count) 18 | { 19 | int fd = (int)stream->state; 20 | int result; 21 | 22 | if (buf == NULL) 23 | { 24 | /* Well, this is a really inefficient way to skip input. */ 25 | /* It is only used when there are unknown fields. */ 26 | char dummy; 27 | while (count-- && recv(fd, &dummy, 1, 0) == 1); 28 | return count == 0; 29 | } 30 | 31 | result = recv(fd, buf, count, MSG_WAITALL); 32 | 33 | if (result == 0) 34 | stream->bytes_left = 0; /* EOF */ 35 | 36 | return result == count; 37 | } 38 | 39 | pb_ostream_t pb_ostream_from_socket(int fd) 40 | { 41 | pb_ostream_t stream = {&write_callback, (void*)fd, SIZE_MAX, 0}; 42 | return stream; 43 | } 44 | 45 | pb_istream_t pb_istream_from_socket(int fd) 46 | { 47 | pb_istream_t stream = {&read_callback, (void*)fd, SIZE_MAX}; 48 | return stream; 49 | } 50 | -------------------------------------------------------------------------------- /example/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _PB_EXAMPLE_COMMON_H_ 2 | #define _PB_EXAMPLE_COMMON_H_ 3 | 4 | #include 5 | 6 | pb_ostream_t pb_ostream_from_socket(int fd); 7 | pb_istream_t pb_istream_from_socket(int fd); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /example/fileproto.proto: -------------------------------------------------------------------------------- 1 | import "pb/pb.proto"; 2 | 3 | // This defines protocol for a simple server that lists files. 4 | // 5 | // If you come from high-level programming background, the hardcoded 6 | // maximum lengths may disgust you. However, if your microcontroller only 7 | // has a few kB of ram to begin with, setting reasonable limits for 8 | // filenames is ok. 9 | // 10 | // On the other hand, using the callback interface, it is not necessary 11 | // to set a limit on the number of files in the response. 12 | 13 | message ListFilesRequest { 14 | optional string path = 1 [default = "/", (pb).max_size = 128]; 15 | } 16 | 17 | message FileInfo { 18 | required uint64 inode = 1; 19 | required string name = 2 [(pb).max_size = 128]; 20 | } 21 | 22 | message ListFilesResponse { 23 | optional bool path_error = 1 [default = false]; 24 | repeated FileInfo file = 2; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /example/server.c: -------------------------------------------------------------------------------- 1 | /* This is a simple TCP server that listens on port 1234 and provides lists 2 | * of files to clients, using a protocol defined in file_server.proto. 3 | * 4 | * It directly deserializes and serializes messages from network, minimizing 5 | * memory use. 6 | * 7 | * For flexibility, this example is implemented using posix api. 8 | * In a real embedded system you would typically use some other kind of 9 | * a communication and filesystem layer. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "fileproto.pb.h" 24 | #include "common.h" 25 | 26 | bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 27 | { 28 | DIR *dir = (DIR*) arg; 29 | struct dirent *file; 30 | FileInfo fileinfo; 31 | 32 | while ((file = readdir(dir)) != NULL) 33 | { 34 | fileinfo.inode = file->d_ino; 35 | strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name)); 36 | fileinfo.name[sizeof(fileinfo.name) - 1] = '\0'; 37 | 38 | if (!pb_encode_tag_for_field(stream, field)) 39 | return false; 40 | 41 | if (!pb_enc_submessage(stream, field, &fileinfo)) 42 | return false; 43 | } 44 | 45 | return true; 46 | } 47 | 48 | void handle_connection(int connfd) 49 | { 50 | ListFilesRequest request; 51 | ListFilesResponse response; 52 | pb_istream_t input = pb_istream_from_socket(connfd); 53 | pb_ostream_t output = pb_ostream_from_socket(connfd); 54 | DIR *directory; 55 | 56 | if (!pb_decode(&input, ListFilesRequest_fields, &request)) 57 | { 58 | printf("Decoding failed.\n"); 59 | return; 60 | } 61 | 62 | directory = opendir(request.path); 63 | 64 | printf("Listing directory: %s\n", request.path); 65 | 66 | if (directory == NULL) 67 | { 68 | perror("opendir"); 69 | 70 | response.has_path_error = true; 71 | response.path_error = true; 72 | response.file.funcs.encode = NULL; 73 | } 74 | else 75 | { 76 | response.has_path_error = false; 77 | response.file.funcs.encode = &listdir_callback; 78 | response.file.arg = directory; 79 | } 80 | 81 | if (!pb_encode(&output, ListFilesResponse_fields, &response)) 82 | { 83 | printf("Encoding failed.\n"); 84 | } 85 | } 86 | 87 | int main(int argc, char **argv) 88 | { 89 | int listenfd, connfd; 90 | struct sockaddr_in servaddr; 91 | int reuse = 1; 92 | 93 | listenfd = socket(AF_INET, SOCK_STREAM, 0); 94 | 95 | setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); 96 | 97 | memset(&servaddr, 0, sizeof(servaddr)); 98 | servaddr.sin_family = AF_INET; 99 | servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 100 | servaddr.sin_port = htons(1234); 101 | if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) 102 | { 103 | perror("bind"); 104 | return 1; 105 | } 106 | 107 | if (listen(listenfd, 5) != 0) 108 | { 109 | perror("listen"); 110 | return 1; 111 | } 112 | 113 | for(;;) 114 | { 115 | connfd = accept(listenfd, NULL, NULL); 116 | 117 | if (connfd < 0) 118 | { 119 | perror("accept"); 120 | return 1; 121 | } 122 | 123 | printf("Got connection.\n"); 124 | 125 | handle_connection(connfd); 126 | 127 | printf("Closing connection.\n"); 128 | 129 | close(connfd); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /generator/pb_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | 3 | from google.protobuf import descriptor 4 | from google.protobuf import message 5 | from google.protobuf import reflection 6 | from google.protobuf import descriptor_pb2 7 | # @@protoc_insertion_point(imports) 8 | 9 | 10 | import google.protobuf.descriptor_pb2 11 | 12 | DESCRIPTOR = descriptor.FileDescriptor( 13 | name='pb.proto', 14 | package='', 15 | serialized_pb='\n\x08pb.proto\x1a google/protobuf/descriptor.proto\"0\n\tPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05:6\n\x02pb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\n.PBOptions') 16 | 17 | 18 | PB_FIELD_NUMBER = 1010 19 | pb = descriptor.FieldDescriptor( 20 | name='pb', full_name='pb', index=0, 21 | number=1010, type=11, cpp_type=10, label=1, 22 | has_default_value=False, default_value=None, 23 | message_type=None, enum_type=None, containing_type=None, 24 | is_extension=True, extension_scope=None, 25 | options=None) 26 | 27 | 28 | _PBOPTIONS = descriptor.Descriptor( 29 | name='PBOptions', 30 | full_name='PBOptions', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | containing_type=None, 34 | fields=[ 35 | descriptor.FieldDescriptor( 36 | name='max_size', full_name='PBOptions.max_size', index=0, 37 | number=1, type=5, cpp_type=1, label=1, 38 | has_default_value=False, default_value=0, 39 | message_type=None, enum_type=None, containing_type=None, 40 | is_extension=False, extension_scope=None, 41 | options=None), 42 | descriptor.FieldDescriptor( 43 | name='max_count', full_name='PBOptions.max_count', index=1, 44 | number=2, type=5, cpp_type=1, label=1, 45 | has_default_value=False, default_value=0, 46 | message_type=None, enum_type=None, containing_type=None, 47 | is_extension=False, extension_scope=None, 48 | options=None), 49 | ], 50 | extensions=[ 51 | ], 52 | nested_types=[], 53 | enum_types=[ 54 | ], 55 | options=None, 56 | is_extendable=False, 57 | extension_ranges=[], 58 | serialized_start=46, 59 | serialized_end=94, 60 | ) 61 | 62 | DESCRIPTOR.message_types_by_name['PBOptions'] = _PBOPTIONS 63 | 64 | class PBOptions(message.Message): 65 | __metaclass__ = reflection.GeneratedProtocolMessageType 66 | DESCRIPTOR = _PBOPTIONS 67 | 68 | # @@protoc_insertion_point(class_scope:PBOptions) 69 | 70 | pb.message_type = _PBOPTIONS 71 | google.protobuf.descriptor_pb2.FieldOptions.RegisterExtension(pb) 72 | # @@protoc_insertion_point(module_scope) 73 | -------------------------------------------------------------------------------- /generator/pbgen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | '''Generate header file for libpb from a ProtoBuf FileDescriptorSet.''' 4 | 5 | import google.protobuf.descriptor_pb2 as descriptor 6 | import pb_pb2 7 | import os.path 8 | 9 | # Values are tuple (c type, pb ltype) 10 | FieldD = descriptor.FieldDescriptorProto 11 | datatypes = { 12 | FieldD.TYPE_BOOL: ('bool', 'PB_LTYPE_VARINT'), 13 | FieldD.TYPE_DOUBLE: ('double', 'PB_LTYPE_FIXED64'), 14 | FieldD.TYPE_FIXED32: ('uint32_t', 'PB_LTYPE_FIXED32'), 15 | FieldD.TYPE_FIXED64: ('uint64_t', 'PB_LTYPE_FIXED64'), 16 | FieldD.TYPE_FLOAT: ('float', 'PB_LTYPE_FIXED32'), 17 | FieldD.TYPE_INT32: ('int32_t', 'PB_LTYPE_VARINT'), 18 | FieldD.TYPE_INT64: ('int64_t', 'PB_LTYPE_VARINT'), 19 | FieldD.TYPE_SFIXED32: ('int32_t', 'PB_LTYPE_FIXED'), 20 | FieldD.TYPE_SFIXED64: ('int64_t', 'PB_LTYPE_FIXED'), 21 | FieldD.TYPE_SINT32: ('int32_t', 'PB_LTYPE_SVARINT'), 22 | FieldD.TYPE_SINT64: ('int64_t', 'PB_LTYPE_SVARINT'), 23 | FieldD.TYPE_UINT32: ('uint32_t', 'PB_LTYPE_VARINT'), 24 | FieldD.TYPE_UINT64: ('uint64_t', 'PB_LTYPE_VARINT') 25 | } 26 | 27 | class Names: 28 | '''Keeps a set of nested names and formats them to C identifier. 29 | You can subclass this with your own implementation. 30 | ''' 31 | def __init__(self, parts = ()): 32 | if isinstance(parts, Names): 33 | parts = parts.parts 34 | self.parts = tuple(parts) 35 | 36 | def __str__(self): 37 | return '_'.join(self.parts) 38 | 39 | def __add__(self, other): 40 | if isinstance(other, (str, unicode)): 41 | return Names(self.parts + (other,)) 42 | elif isinstance(other, tuple): 43 | return Names(self.parts + other) 44 | else: 45 | raise ValueError("Name parts should be of type str") 46 | 47 | def names_from_type_name(type_name): 48 | '''Parse Names() from FieldDescriptorProto type_name''' 49 | if type_name[0] != '.': 50 | raise NotImplementedError("Lookup of non-absolute type names is not supported") 51 | return Names(type_name[1:].split('.')) 52 | 53 | class Enum: 54 | def __init__(self, names, desc): 55 | '''desc is EnumDescriptorProto''' 56 | self.names = names + desc.name 57 | self.values = [(self.names + x.name, x.number) for x in desc.value] 58 | 59 | def __str__(self): 60 | result = 'typedef enum {\n' 61 | result += ',\n'.join([" %s = %d" % x for x in self.values]) 62 | result += '\n} %s;' % self.names 63 | return result 64 | 65 | class Field: 66 | def __init__(self, struct_name, desc): 67 | '''desc is FieldDescriptorProto''' 68 | self.tag = desc.number 69 | self.struct_name = struct_name 70 | self.name = desc.name 71 | self.default = None 72 | self.max_size = None 73 | self.max_count = None 74 | self.array_decl = "" 75 | 76 | # Parse libpb-specific field options 77 | if desc.options.HasExtension(pb_pb2.pb): 78 | ext = desc.options.Extensions[pb_pb2.pb] 79 | if ext.HasField("max_size"): 80 | self.max_size = ext.max_size 81 | if ext.HasField("max_count"): 82 | self.max_count = ext.max_count 83 | 84 | if desc.HasField('default_value'): 85 | self.default = desc.default_value 86 | 87 | # Decide HTYPE 88 | # HTYPE is the high-order nibble of libpb field description, 89 | # defining whether value is required/optional/repeated. 90 | is_callback = False 91 | if desc.label == FieldD.LABEL_REQUIRED: 92 | self.htype = 'PB_HTYPE_REQUIRED' 93 | elif desc.label == FieldD.LABEL_OPTIONAL: 94 | self.htype = 'PB_HTYPE_OPTIONAL' 95 | elif desc.label == FieldD.LABEL_REPEATED: 96 | if self.max_count is None: 97 | is_callback = True 98 | else: 99 | self.htype = 'PB_HTYPE_ARRAY' 100 | self.array_decl = '[%d]' % self.max_count 101 | else: 102 | raise NotImplementedError(desc.label) 103 | 104 | # Decide LTYPE and CTYPE 105 | # LTYPE is the low-order nibble of libpb field description, 106 | # defining how to decode an individual value. 107 | # CTYPE is the name of the c type to use in the struct. 108 | if datatypes.has_key(desc.type): 109 | self.ctype, self.ltype = datatypes[desc.type] 110 | elif desc.type == FieldD.TYPE_ENUM: 111 | self.ltype = 'PB_LTYPE_VARINT' 112 | self.ctype = names_from_type_name(desc.type_name) 113 | if self.default is not None: 114 | self.default = self.ctype + self.default 115 | elif desc.type == FieldD.TYPE_STRING: 116 | self.ltype = 'PB_LTYPE_STRING' 117 | if self.max_size is None: 118 | is_callback = True 119 | else: 120 | self.ctype = 'char' 121 | self.array_decl += '[%d]' % self.max_size 122 | elif desc.type == FieldD.TYPE_BYTES: 123 | self.ltype = 'PB_LTYPE_BYTES' 124 | if self.max_size is None: 125 | is_callback = True 126 | else: 127 | self.ctype = self.struct_name + self.name + 't' 128 | elif desc.type == FieldD.TYPE_MESSAGE: 129 | self.ltype = 'PB_LTYPE_SUBMESSAGE' 130 | self.ctype = self.submsgname = names_from_type_name(desc.type_name) 131 | else: 132 | raise NotImplementedError(desc.type) 133 | 134 | if is_callback: 135 | self.htype = 'PB_HTYPE_CALLBACK' 136 | self.ctype = 'pb_callback_t' 137 | self.array_decl = '' 138 | 139 | def __cmp__(self, other): 140 | return cmp(self.tag, other.tag) 141 | 142 | def __str__(self): 143 | if self.htype == 'PB_HTYPE_OPTIONAL': 144 | result = ' bool has_' + self.name + ';\n' 145 | elif self.htype == 'PB_HTYPE_ARRAY': 146 | result = ' size_t ' + self.name + '_count;\n' 147 | else: 148 | result = '' 149 | result += ' %s %s%s;' % (self.ctype, self.name, self.array_decl) 150 | return result 151 | 152 | def types(self): 153 | '''Return definitions for any special types this field might need.''' 154 | if self.ltype == 'PB_LTYPE_BYTES' and self.max_size is not None: 155 | result = 'typedef struct {\n' 156 | result += ' size_t size;\n' 157 | result += ' uint8_t bytes[%d];\n' % self.max_size 158 | result += '} %s;\n' % self.ctype 159 | else: 160 | result = None 161 | return result 162 | 163 | def default_decl(self, declaration_only = False): 164 | '''Return definition for this field's default value.''' 165 | if self.default is None: 166 | return None 167 | 168 | if self.ltype == 'PB_LTYPE_STRING': 169 | ctype = 'char' 170 | if self.max_size is None: 171 | return None # Not implemented 172 | else: 173 | array_decl = '[%d]' % (self.max_size + 1) 174 | default = str(self.default).encode('string_escape') 175 | default = default.replace('"', '\\"') 176 | default = '"' + default + '"' 177 | elif self.ltype == 'PB_LTYPE_BYTES': 178 | data = self.default.decode('string_escape') 179 | data = ['0x%02x' % ord(c) for c in data] 180 | 181 | if self.max_size is None: 182 | return None # Not implemented 183 | else: 184 | ctype = self.ctype 185 | 186 | default = '{%d, {%s}}' % (len(data), ','.join(data)) 187 | array_decl = '' 188 | else: 189 | ctype, default = self.ctype, self.default 190 | array_decl = '' 191 | 192 | if declaration_only: 193 | return 'extern const %s %s_default%s;' % (ctype, self.struct_name + self.name, array_decl) 194 | else: 195 | return 'const %s %s_default%s = %s;' % (ctype, self.struct_name + self.name, array_decl, default) 196 | 197 | def pb_field_t(self, prev_field_name): 198 | '''Return the pb_field_t initializer to use in the constant array. 199 | prev_field_name is the name of the previous field or None. 200 | ''' 201 | result = ' {%d, ' % self.tag 202 | result += self.htype 203 | if self.ltype is not None: 204 | result += ' | ' + self.ltype 205 | result += ',\n' 206 | 207 | if prev_field_name is None: 208 | result += ' offsetof(%s, %s),' % (self.struct_name, self.name) 209 | else: 210 | result += ' pb_delta_end(%s, %s, %s),' % (self.struct_name, self.name, prev_field_name) 211 | 212 | if self.htype == 'PB_HTYPE_OPTIONAL': 213 | result += '\n pb_delta(%s, has_%s, %s),' % (self.struct_name, self.name, self.name) 214 | elif self.htype == 'PB_HTYPE_ARRAY': 215 | result += '\n pb_delta(%s, %s_count, %s),' % (self.struct_name, self.name, self.name) 216 | else: 217 | result += ' 0,' 218 | 219 | 220 | if self.htype == 'PB_HTYPE_ARRAY': 221 | result += '\n pb_membersize(%s, %s[0]),' % (self.struct_name, self.name) 222 | result += ('\n pb_membersize(%s, %s) / pb_membersize(%s, %s[0]),' 223 | % (self.struct_name, self.name, self.struct_name, self.name)) 224 | elif self.htype != 'PB_HTYPE_CALLBACK' and self.ltype == 'PB_LTYPE_BYTES': 225 | result += '\n pb_membersize(%s, bytes),' % self.ctype 226 | result += ' 0,' 227 | else: 228 | result += '\n pb_membersize(%s, %s),' % (self.struct_name, self.name) 229 | result += ' 0,' 230 | 231 | if self.ltype == 'PB_LTYPE_SUBMESSAGE': 232 | result += '\n &%s_fields}' % self.submsgname 233 | elif self.default is None or self.htype == 'PB_HTYPE_CALLBACK': 234 | result += ' 0}' 235 | else: 236 | result += '\n &%s_default}' % (self.struct_name + self.name) 237 | 238 | return result 239 | 240 | class Message: 241 | def __init__(self, names, desc): 242 | self.name = names 243 | self.fields = [Field(self.name, f) for f in desc.field] 244 | self.ordered_fields = self.fields[:] 245 | self.ordered_fields.sort() 246 | 247 | def get_dependencies(self): 248 | '''Get list of type names that this structure refers to.''' 249 | return [str(field.ctype) for field in self.fields] 250 | 251 | def __str__(self): 252 | result = 'typedef struct {\n' 253 | result += '\n'.join([str(f) for f in self.ordered_fields]) 254 | result += '\n} %s;' % self.name 255 | return result 256 | 257 | def types(self): 258 | result = "" 259 | for field in self.fields: 260 | types = field.types() 261 | if types is not None: 262 | result += types + '\n' 263 | return result 264 | 265 | def default_decl(self, declaration_only = False): 266 | result = "" 267 | for field in self.fields: 268 | default = field.default_decl(declaration_only) 269 | if default is not None: 270 | result += default + '\n' 271 | return result 272 | 273 | def fields_declaration(self): 274 | result = 'extern const pb_field_t %s_fields[%d];' % (self.name, len(self.fields) + 1) 275 | return result 276 | 277 | def fields_definition(self): 278 | result = 'const pb_field_t %s_fields[%d] = {\n' % (self.name, len(self.fields) + 1) 279 | 280 | prev = None 281 | for field in self.ordered_fields: 282 | result += field.pb_field_t(prev) 283 | result += ',\n\n' 284 | prev = field.name 285 | 286 | result += ' PB_LAST_FIELD\n};' 287 | return result 288 | 289 | def iterate_messages(desc, names = Names()): 290 | '''Recursively find all messages. For each, yield name, DescriptorProto.''' 291 | if hasattr(desc, 'message_type'): 292 | submsgs = desc.message_type 293 | else: 294 | submsgs = desc.nested_type 295 | 296 | for submsg in submsgs: 297 | sub_names = names + submsg.name 298 | yield sub_names, submsg 299 | 300 | for x in iterate_messages(submsg, sub_names): 301 | yield x 302 | 303 | def parse_file(fdesc): 304 | '''Takes a FileDescriptorProto and returns tuple (enum, messages).''' 305 | 306 | enums = [] 307 | messages = [] 308 | 309 | if fdesc.package: 310 | base_name = Names(fdesc.package.split('.')) 311 | else: 312 | base_name = Names() 313 | 314 | for enum in fdesc.enum_type: 315 | enums.append(Enum(base_name, enum)) 316 | 317 | for names, message in iterate_messages(fdesc, base_name): 318 | messages.append(Message(names, message)) 319 | for enum in message.enum_type: 320 | enums.append(Enum(names, enum)) 321 | 322 | return enums, messages 323 | 324 | def toposort2(data): 325 | '''Topological sort. 326 | From http://code.activestate.com/recipes/577413-topological-sort/ 327 | This function is under the MIT license. 328 | ''' 329 | for k, v in data.items(): 330 | v.discard(k) # Ignore self dependencies 331 | extra_items_in_deps = reduce(set.union, data.values()) - set(data.keys()) 332 | data.update(dict([(item, set()) for item in extra_items_in_deps])) 333 | while True: 334 | ordered = set(item for item,dep in data.items() if not dep) 335 | if not ordered: 336 | break 337 | for item in sorted(ordered): 338 | yield item 339 | data = dict([(item, (dep - ordered)) for item,dep in data.items() 340 | if item not in ordered]) 341 | assert not data, "A cyclic dependency exists amongst %r" % data 342 | 343 | def sort_dependencies(messages): 344 | '''Sort a list of Messages based on dependencies.''' 345 | dependencies = {} 346 | message_by_name = {} 347 | for message in messages: 348 | dependencies[str(message.name)] = set(message.get_dependencies()) 349 | message_by_name[str(message.name)] = message 350 | 351 | for msgname in toposort2(dependencies): 352 | if msgname in message_by_name: 353 | yield message_by_name[msgname] 354 | 355 | def generate_header(headername, enums, messages): 356 | '''Generate content for a header file. 357 | Generates strings, which should be concatenated and stored to file. 358 | ''' 359 | 360 | yield '/* Automatically generated libpb header */\n' 361 | 362 | symbol = headername.replace('.', '_').upper() 363 | yield '#ifndef _PB_%s_\n' % symbol 364 | yield '#define _PB_%s_\n' % symbol 365 | yield '#include \n\n' 366 | 367 | yield '/* Enum definitions */\n' 368 | for enum in enums: 369 | yield str(enum) + '\n\n' 370 | 371 | yield '/* Struct definitions */\n' 372 | for msg in sort_dependencies(messages): 373 | yield msg.types() 374 | yield str(msg) + '\n\n' 375 | 376 | yield '/* Default values for struct fields */\n' 377 | for msg in messages: 378 | yield msg.default_decl(True) 379 | yield '\n' 380 | 381 | yield '/* Struct field encoding specification for lipb */\n' 382 | for msg in messages: 383 | yield msg.fields_declaration() + '\n' 384 | 385 | yield '\n#endif\n' 386 | 387 | def generate_source(headername, enums, messages): 388 | '''Generate content for a source file.''' 389 | 390 | yield '/* Automatically generated libpb constant definitions */\n' 391 | yield '#include "%s"\n\n' % headername 392 | 393 | for msg in messages: 394 | yield msg.default_decl(False) 395 | 396 | yield '\n\n' 397 | 398 | for msg in messages: 399 | yield msg.fields_definition() + '\n\n' 400 | 401 | if __name__ == '__main__': 402 | import sys 403 | import os.path 404 | 405 | if len(sys.argv) != 2: 406 | print "Usage: " + sys.argv[0] + " file.pb" 407 | print "where file.pb has been compiled from .proto by:" 408 | print "protoc -ofile.pb file.proto" 409 | print "Output fill be written to file.pb.h and file.pb.c" 410 | sys.exit(1) 411 | 412 | data = open(sys.argv[1]).read() 413 | fdesc = descriptor.FileDescriptorSet.FromString(data) 414 | enums, messages = parse_file(fdesc.file[0]) 415 | 416 | noext = os.path.splitext(sys.argv[1])[0] 417 | headername = noext + '.pb.h' 418 | sourcename = noext + '.pb.c' 419 | headerbasename = os.path.basename(headername) 420 | 421 | print "Writing to " + headername + " and " + sourcename 422 | 423 | header = open(headername, 'w') 424 | for part in generate_header(headerbasename, enums, messages): 425 | header.write(part) 426 | 427 | source = open(sourcename, 'w') 428 | for part in generate_source(headerbasename, enums, messages): 429 | source.write(part) 430 | 431 | 432 | -------------------------------------------------------------------------------- /include/pb/decode.h: -------------------------------------------------------------------------------- 1 | #ifndef _PB_DECODE_H_ 2 | #define _PB_DECODE_H_ 3 | 4 | /* decode.h: Functions to decode protocol buffers. Depends on decode.c. 5 | * The main function is pb_decode. You will also need to create an input 6 | * stream, which is easiest to do with pb_istream_t. 7 | * 8 | * You also need structures and their corresponding pb_field_t descriptions. 9 | * These are usually generated from .proto-files with a script. 10 | */ 11 | 12 | #include 13 | #include "pb.h" 14 | 15 | /* Lightweight input stream. 16 | * You can provide a callback function for reading or use 17 | * pb_istream_from_buffer. 18 | * 19 | * Rules for callback: 20 | * 1) Return false on IO errors. This will cause decoding to abort. 21 | * 22 | * 2) If buf is NULL, read but don't store bytes ("skip input"). 23 | * 24 | * 3) You can use state to store your own data (e.g. buffer pointer), 25 | * and rely on pb_read to verify that no-body reads past bytes_left. 26 | * 27 | * 4) Your callback may be used with substreams, in which case bytes_left 28 | * is different than from the main stream. Don't use bytes_left to compute 29 | * any pointers. 30 | */ 31 | struct _pb_istream_t 32 | { 33 | bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count); 34 | void *state; /* Free field for use by callback implementation */ 35 | size_t bytes_left; 36 | }; 37 | 38 | pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize); 39 | bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count); 40 | 41 | /* Decode from stream to destination struct. 42 | * Returns true on success, false on any failure. 43 | * The actual struct pointed to by dest must match the description in fields. 44 | */ 45 | bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct); 46 | 47 | /* --- Helper functions --- 48 | * You may want to use these from your caller or callbacks. 49 | */ 50 | 51 | bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); 52 | 53 | bool pb_skip_varint(pb_istream_t *stream); 54 | bool pb_skip_string(pb_istream_t *stream); 55 | 56 | /* --- Field decoders --- 57 | * Each decoder takes stream and field description, and a pointer to the field 58 | * in the destination struct (dest = struct_addr + field->data_offset). 59 | * For arrays, these functions are called repeatedly. 60 | */ 61 | 62 | bool pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); 63 | bool pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest); 64 | bool pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest); 65 | bool pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest); 66 | 67 | bool pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest); 68 | bool pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest); 69 | bool pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/pb/encode.h: -------------------------------------------------------------------------------- 1 | #ifndef _PB_ENCODE_H_ 2 | #define _PB_ENCODE_H_ 3 | 4 | /* encode.h: Functions to encode protocol buffers. Depends on encode.c. 5 | * The main function is pb_encode. You also need an output stream, structures 6 | * and their field descriptions (just like with pb_decode). 7 | */ 8 | 9 | #include 10 | #include "pb.h" 11 | 12 | /* Lightweight output stream. 13 | * You can provide callback for writing or use pb_ostream_from_buffer. 14 | * 15 | * Alternatively, callback can be NULL in which case the stream will just 16 | * count the number of bytes that would have been written. In this case 17 | * max_size is not checked. 18 | * 19 | * Rules for callback: 20 | * 1) Return false on IO errors. This will cause encoding to abort. 21 | * 22 | * 2) You can use state to store your own data (e.g. buffer pointer). 23 | * 24 | * 3) pb_write will update bytes_written after your callback runs. 25 | * 26 | * 4) Substreams will modify max_size and bytes_written. Don't use them to 27 | * calculate any pointers. 28 | */ 29 | struct _pb_ostream_t 30 | { 31 | bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count); 32 | void *state; /* Free field for use by callback implementation */ 33 | size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ 34 | size_t bytes_written; 35 | }; 36 | 37 | pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize); 38 | bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count); 39 | 40 | /* Encode struct to given output stream. 41 | * Returns true on success, false on any failure. 42 | * The actual struct pointed to by src_struct must match the description in fields. 43 | * All required fields in the struct are assumed to have been filled in. 44 | */ 45 | bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct); 46 | 47 | /* --- Helper functions --- 48 | * You may want to use these from your caller or callbacks. 49 | */ 50 | 51 | bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); 52 | bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number); 53 | /* Encode tag based on LTYPE and field number defined in the field structure. */ 54 | bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field); 55 | /* Write length as varint and then the contents of buffer. */ 56 | bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size); 57 | 58 | /* --- Field encoders --- 59 | * Each encoder writes the content for the field. 60 | * The tag/wire type has been written already. 61 | */ 62 | 63 | bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 64 | bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 65 | bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src); 66 | bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src); 67 | 68 | bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src); 69 | bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src); 70 | bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/pb/pb.h: -------------------------------------------------------------------------------- 1 | #ifndef _PB_H_ 2 | #define _PB_H_ 3 | 4 | /* pb.h: Common parts for libpb library. 5 | * Most of these are quite low-level stuff. For the high-level interface, 6 | * see encode.h or decode.h 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef __GNUC__ 14 | /* This just reduces memory requirements, but is not required. */ 15 | #define pb_packed __attribute__((packed)) 16 | #else 17 | #define pb_packed 18 | #endif 19 | 20 | /* List of possible field types. These are used in the autogenerated code. 21 | * Least-significant 4 bits tell the scalar type 22 | * Most-significant 4 bits specify repeated/required/packed etc. 23 | * 24 | * INT32 and UINT32 are treated the same, as are (U)INT64 and (S)FIXED* 25 | * These types are simply casted to correct field type when they are 26 | * assigned to the memory pointer. 27 | * SINT* is different, though, because it is zig-zag coded. 28 | */ 29 | 30 | typedef enum { 31 | /************************ 32 | * Field contents types * 33 | ************************/ 34 | 35 | /* Numeric types */ 36 | PB_LTYPE_VARINT = 0x00, /* int32, uint32, int64, uint64, bool, enum */ 37 | PB_LTYPE_SVARINT = 0x01, /* sint32, sint64 */ 38 | PB_LTYPE_FIXED32 = 0x02, /* fixed32, sfixed32, float */ 39 | PB_LTYPE_FIXED64 = 0x03, /* fixed64, sfixed64, double */ 40 | 41 | /* Marker for last packable field type. */ 42 | PB_LTYPE_LAST_PACKABLE = 0x03, 43 | 44 | /* Byte array with pre-allocated buffer. 45 | * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ 46 | PB_LTYPE_BYTES = 0x04, 47 | 48 | /* String with pre-allocated buffer. 49 | * data_size is the maximum length. */ 50 | PB_LTYPE_STRING = 0x05, 51 | 52 | /* Submessage 53 | * submsg_fields is pointer to field descriptions */ 54 | PB_LTYPE_SUBMESSAGE = 0x06, 55 | 56 | /* Number of declared LTYPES */ 57 | PB_LTYPES_COUNT = 7, 58 | 59 | /****************** 60 | * Modifier flags * 61 | ******************/ 62 | 63 | /* Just the basic, write data at data_offset */ 64 | PB_HTYPE_REQUIRED = 0x00, 65 | 66 | /* Write true at size_offset */ 67 | PB_HTYPE_OPTIONAL = 0x10, 68 | 69 | /* Read to pre-allocated array 70 | * Maximum number of entries is array_size, 71 | * actual number is stored at size_offset */ 72 | PB_HTYPE_ARRAY = 0x20, 73 | 74 | /* Works for all required/optional/repeated fields. 75 | * data_offset points to pb_callback_t structure. 76 | * LTYPE should be 0 (it is ignored, but sometimes 77 | * used to speculatively index an array). */ 78 | PB_HTYPE_CALLBACK = 0x30 79 | } pb_packed pb_type_t; 80 | 81 | #define PB_HTYPE(x) ((x) & 0xF0) 82 | #define PB_LTYPE(x) ((x) & 0x0F) 83 | 84 | /* This structure is used in auto-generated constants 85 | * to specify struct fields. 86 | * You can change field sizes here if you need structures 87 | * larger than 256 bytes or field tags larger than 256. 88 | * The compiler should complain if your .proto has such 89 | * structures ("initializer too large for type"). 90 | */ 91 | typedef struct _pb_field_t pb_field_t; 92 | struct _pb_field_t { 93 | uint8_t tag; 94 | pb_type_t type; 95 | uint8_t data_offset; /* Offset of field data, relative to previous field. */ 96 | int8_t size_offset; /* Offset of array size or has-boolean, relative to data */ 97 | uint8_t data_size; /* Data size in bytes for a single item */ 98 | uint8_t array_size; /* Maximum number of entries in array */ 99 | 100 | /* Field definitions for submessage 101 | * OR default value for all other non-array, non-callback types 102 | * If null, then field will zeroed. */ 103 | const void *ptr; 104 | } pb_packed; 105 | 106 | /* This structure is used for 'bytes' arrays. 107 | * It has the number of bytes in the beginning, and after that an array. 108 | * Note that actual structs used will have a different length of bytes array. 109 | */ 110 | typedef struct { 111 | size_t size; 112 | uint8_t bytes[1]; 113 | } pb_bytes_array_t; 114 | 115 | /* This structure is used for giving the callback function. 116 | * It is stored in the message structure and filled in by the method that 117 | * calls pb_decode. 118 | * 119 | * The decoding callback will be given a limited-length stream 120 | * If the wire type was string, the length is the length of the string. 121 | * If the wire type was a varint/fixed32/fixed64, the length is the length 122 | * of the actual value. 123 | * The function may be called multiple times (especially for repeated types, 124 | * but also otherwise if the message happens to contain the field multiple 125 | * times.) 126 | * 127 | * The encoding callback will receive the actual output stream. 128 | * It should write all the data in one call, including the field tag and 129 | * wire type. It can write multiple fields. 130 | * 131 | * The callback can be null if you want to skip a field. 132 | */ 133 | typedef struct _pb_istream_t pb_istream_t; 134 | typedef struct _pb_ostream_t pb_ostream_t; 135 | typedef struct _pb_callback_t pb_callback_t; 136 | struct _pb_callback_t { 137 | union { 138 | bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg); 139 | bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg); 140 | } funcs; 141 | 142 | /* Free arg for use by callback */ 143 | void *arg; 144 | }; 145 | 146 | /* Wire types. Library user needs these only in encoder callbacks. */ 147 | typedef enum { 148 | PB_WT_VARINT = 0, 149 | PB_WT_64BIT = 1, 150 | PB_WT_STRING = 2, 151 | PB_WT_32BIT = 5 152 | } pb_wire_type_t; 153 | 154 | /* These macros are used to declare pb_field_t's in the constant array. */ 155 | #define pb_membersize(st, m) (sizeof ((st*)0)->m) 156 | #define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) 157 | #define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) 158 | #define pb_delta_end(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2)) 159 | #define PB_LAST_FIELD {0,0,0,0} 160 | 161 | 162 | #endif 163 | -------------------------------------------------------------------------------- /include/pb/pb.proto: -------------------------------------------------------------------------------- 1 | // Custom options for defining: 2 | // - Maximum size of string/bytes 3 | // - Maximum number of elements in array 4 | // 5 | // These are used by libpb to generate statically allocable structures 6 | // for memory-limited environments. 7 | 8 | import "google/protobuf/descriptor.proto"; 9 | 10 | message PBOptions { 11 | optional int32 max_size = 1; 12 | optional int32 max_count = 2; 13 | } 14 | 15 | // Protocol Buffers extension number registry 16 | // -------------------------------- 17 | // Project: libpb 18 | // Contact: Eguo Wang 19 | // Web site: http://github.com/wangeguo/libpb 20 | // Extensions: 1010 (all types) 21 | // -------------------------------- 22 | 23 | extend google.protobuf.FieldOptions { 24 | optional PBOptions pb = 1010; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/decode.c: -------------------------------------------------------------------------------- 1 | /* decode.c -- decode a protobuf using minimal resources 2 | * 3 | * 2011 Petteri Aimonen 4 | * 2011 Eguo Wang 5 | */ 6 | 7 | 8 | #ifdef __GNUC__ 9 | /* Verify that we remember to check all return values for proper error propagation */ 10 | #define checkreturn __attribute__((warn_unused_result)) 11 | #else 12 | #define checkreturn 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn; 20 | 21 | /* --- Function pointers to field decoders --- 22 | * Order in the array must match pb_action_t LTYPE numbering. 23 | */ 24 | static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { 25 | &pb_dec_varint, 26 | &pb_dec_svarint, 27 | &pb_dec_fixed32, 28 | &pb_dec_fixed64, 29 | 30 | &pb_dec_bytes, 31 | &pb_dec_string, 32 | &pb_dec_submessage 33 | }; 34 | 35 | /************** 36 | * pb_istream * 37 | **************/ 38 | 39 | bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) 40 | { 41 | if (stream->bytes_left < count) 42 | return false; 43 | 44 | if (!stream->callback(stream, buf, count)) 45 | return false; 46 | 47 | stream->bytes_left -= count; 48 | return true; 49 | } 50 | 51 | static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count) 52 | { 53 | uint8_t *source = (uint8_t*)stream->state; 54 | 55 | if (buf != NULL) 56 | memcpy(buf, source, count); 57 | 58 | stream->state = source + count; 59 | return true; 60 | } 61 | 62 | pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize) 63 | { 64 | pb_istream_t stream; 65 | stream.callback = &buf_read; 66 | stream.state = buf; 67 | stream.bytes_left = bufsize; 68 | return stream; 69 | } 70 | 71 | /******************** 72 | * Helper functions * 73 | ********************/ 74 | 75 | static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) 76 | { 77 | uint64_t temp; 78 | bool status = pb_decode_varint(stream, &temp); 79 | *dest = temp; 80 | return status; 81 | } 82 | 83 | bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) 84 | { 85 | uint8_t byte; 86 | uint8_t bitpos = 0; 87 | *dest = 0; 88 | 89 | while (bitpos < 64 && pb_read(stream, &byte, 1)) 90 | { 91 | *dest |= (uint64_t)(byte & 0x7F) << bitpos; 92 | bitpos += 7; 93 | 94 | if (!(byte & 0x80)) 95 | return true; 96 | } 97 | 98 | return false; 99 | } 100 | 101 | bool checkreturn pb_skip_varint(pb_istream_t *stream) 102 | { 103 | uint8_t byte; 104 | do 105 | { 106 | if (!pb_read(stream, &byte, 1)) 107 | return false; 108 | } while (byte & 0x80); 109 | return true; 110 | } 111 | 112 | bool checkreturn pb_skip_string(pb_istream_t *stream) 113 | { 114 | uint32_t length; 115 | if (!pb_decode_varint32(stream, &length)) 116 | return false; 117 | 118 | return pb_read(stream, NULL, length); 119 | } 120 | 121 | /* Currently the wire type related stuff is kept hidden from 122 | * callbacks. They shouldn't need it. It's better for performance 123 | * to just assume the correct type and fail safely on corrupt message. 124 | */ 125 | 126 | static bool checkreturn skip(pb_istream_t *stream, pb_wire_type_t wire_type) 127 | { 128 | switch (wire_type) 129 | { 130 | case PB_WT_VARINT: return pb_skip_varint(stream); 131 | case PB_WT_64BIT: return pb_read(stream, NULL, 8); 132 | case PB_WT_STRING: return pb_skip_string(stream); 133 | case PB_WT_32BIT: return pb_read(stream, NULL, 4); 134 | default: return false; 135 | } 136 | } 137 | 138 | /* Read a raw value to buffer, for the purpose of passing it to callback as 139 | * a substream. Size is maximum size on call, and actual size on return. 140 | */ 141 | static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size) 142 | { 143 | size_t max_size = *size; 144 | switch (wire_type) 145 | { 146 | case PB_WT_VARINT: 147 | *size = 0; 148 | do 149 | { 150 | (*size)++; 151 | if (*size > max_size) return false; 152 | if (!pb_read(stream, buf, 1)) return false; 153 | } while (*buf++ & 0x80); 154 | return true; 155 | 156 | case PB_WT_64BIT: 157 | *size = 8; 158 | return pb_read(stream, buf, 8); 159 | 160 | case PB_WT_32BIT: 161 | *size = 4; 162 | return pb_read(stream, buf, 4); 163 | 164 | default: return false; 165 | } 166 | } 167 | 168 | /* Decode string length from stream and return a substream with limited length. 169 | * Before disposing the substream, remember to copy the substream->state back 170 | * to stream->state. 171 | */ 172 | static bool checkreturn make_string_substream(pb_istream_t *stream, pb_istream_t *substream) 173 | { 174 | uint32_t size; 175 | if (!pb_decode_varint32(stream, &size)) 176 | return false; 177 | 178 | *substream = *stream; 179 | if (substream->bytes_left < size) 180 | return false; 181 | 182 | substream->bytes_left = size; 183 | stream->bytes_left -= size; 184 | return true; 185 | } 186 | 187 | /* Iterator for pb_field_t list */ 188 | typedef struct { 189 | const pb_field_t *start; 190 | const pb_field_t *current; 191 | int field_index; 192 | void *dest_struct; 193 | void *pData; 194 | void *pSize; 195 | } pb_field_iterator_t; 196 | 197 | static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct) 198 | { 199 | iter->start = iter->current = fields; 200 | iter->field_index = 0; 201 | iter->pData = (char*)dest_struct + iter->current->data_offset; 202 | iter->pSize = (char*)iter->pData + iter->current->size_offset; 203 | iter->dest_struct = dest_struct; 204 | } 205 | 206 | static bool pb_field_next(pb_field_iterator_t *iter) 207 | { 208 | bool notwrapped = true; 209 | size_t prev_size = iter->current->data_size; 210 | 211 | if (PB_HTYPE(iter->current->type) == PB_HTYPE_ARRAY) 212 | prev_size *= iter->current->array_size; 213 | 214 | iter->current++; 215 | iter->field_index++; 216 | if (iter->current->tag == 0) 217 | { 218 | iter->current = iter->start; 219 | iter->field_index = 0; 220 | iter->pData = iter->dest_struct; 221 | prev_size = 0; 222 | notwrapped = false; 223 | } 224 | 225 | iter->pData = (char*)iter->pData + prev_size + iter->current->data_offset; 226 | iter->pSize = (char*)iter->pData + iter->current->size_offset; 227 | return notwrapped; 228 | } 229 | 230 | static bool checkreturn pb_field_find(pb_field_iterator_t *iter, int tag) 231 | { 232 | int start = iter->field_index; 233 | 234 | do { 235 | if (iter->current->tag == tag) 236 | return true; 237 | pb_field_next(iter); 238 | } while (iter->field_index != start); 239 | 240 | return false; 241 | } 242 | 243 | /************************* 244 | * Decode a single field * 245 | *************************/ 246 | 247 | static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) 248 | { 249 | pb_decoder_t func = PB_DECODERS[PB_LTYPE(iter->current->type)]; 250 | 251 | switch (PB_HTYPE(iter->current->type)) 252 | { 253 | case PB_HTYPE_REQUIRED: 254 | return func(stream, iter->current, iter->pData); 255 | 256 | case PB_HTYPE_OPTIONAL: 257 | *(bool*)iter->pSize = true; 258 | return func(stream, iter->current, iter->pData); 259 | 260 | case PB_HTYPE_ARRAY: 261 | if (wire_type == PB_WT_STRING 262 | && PB_LTYPE(iter->current->type) <= PB_LTYPE_LAST_PACKABLE) 263 | { 264 | /* Packed array */ 265 | size_t *size = (size_t*)iter->pSize; 266 | pb_istream_t substream; 267 | if (!make_string_substream(stream, &substream)) 268 | return false; 269 | 270 | while (substream.bytes_left && *size < iter->current->array_size) 271 | { 272 | void *pItem = (uint8_t*)iter->pData + iter->current->data_size * (*size); 273 | if (!func(&substream, iter->current, pItem)) 274 | return false; 275 | (*size)++; 276 | } 277 | return (substream.bytes_left == 0); 278 | } 279 | else 280 | { 281 | /* Repeated field */ 282 | size_t *size = (size_t*)iter->pSize; 283 | void *pItem = (uint8_t*)iter->pData + iter->current->data_size * (*size); 284 | if (*size >= iter->current->array_size) 285 | return false; 286 | 287 | (*size)++; 288 | return func(stream, iter->current, pItem); 289 | } 290 | 291 | case PB_HTYPE_CALLBACK: 292 | { 293 | pb_callback_t *pCallback = (pb_callback_t*)iter->pData; 294 | 295 | if (pCallback->funcs.decode == NULL) 296 | return skip(stream, wire_type); 297 | 298 | if (wire_type == PB_WT_STRING) 299 | { 300 | pb_istream_t substream; 301 | 302 | if (!make_string_substream(stream, &substream)) 303 | return false; 304 | 305 | while (substream.bytes_left) 306 | { 307 | if (!pCallback->funcs.decode(&substream, iter->current, pCallback->arg)) 308 | return false; 309 | } 310 | 311 | stream->state = substream.state; 312 | return true; 313 | } 314 | else 315 | { 316 | /* Copy the single scalar value to stack. 317 | * This is required so that we can limit the stream length, 318 | * which in turn allows to use same callback for packed and 319 | * not-packed fields. */ 320 | pb_istream_t substream; 321 | uint8_t buffer[10]; 322 | size_t size = sizeof(buffer); 323 | 324 | if (!read_raw_value(stream, wire_type, buffer, &size)) 325 | return false; 326 | substream = pb_istream_from_buffer(buffer, size); 327 | 328 | return pCallback->funcs.decode(&substream, iter->current, pCallback->arg); 329 | } 330 | } 331 | 332 | default: 333 | return false; 334 | } 335 | } 336 | 337 | /* Initialize message fields to default values, recursively */ 338 | static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct) 339 | { 340 | pb_field_iterator_t iter; 341 | pb_field_init(&iter, fields, dest_struct); 342 | 343 | /* Initialize size/has fields and apply default values */ 344 | do 345 | { 346 | if (iter.current->tag == 0) 347 | continue; 348 | 349 | /* Initialize the size field for optional/repeated fields to 0. */ 350 | if (PB_HTYPE(iter.current->type) == PB_HTYPE_OPTIONAL) 351 | { 352 | *(bool*)iter.pSize = false; 353 | } 354 | else if (PB_HTYPE(iter.current->type) == PB_HTYPE_ARRAY) 355 | { 356 | *(size_t*)iter.pSize = 0; 357 | continue; /* Array is empty, no need to initialize contents */ 358 | } 359 | 360 | /* Initialize field contents to default value */ 361 | if (PB_HTYPE(iter.current->type) == PB_HTYPE_CALLBACK) 362 | { 363 | continue; /* Don't overwrite callback */ 364 | } 365 | else if (PB_LTYPE(iter.current->type) == PB_LTYPE_SUBMESSAGE) 366 | { 367 | pb_message_set_to_defaults(iter.current->ptr, iter.pData); 368 | } 369 | else if (iter.current->ptr != NULL) 370 | { 371 | memcpy(iter.pData, iter.current->ptr, iter.current->data_size); 372 | } 373 | else 374 | { 375 | memset(iter.pData, 0, iter.current->data_size); 376 | } 377 | } while (pb_field_next(&iter)); 378 | } 379 | 380 | /********************* 381 | * Decode all fields * 382 | *********************/ 383 | 384 | bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) 385 | { 386 | uint32_t fields_seen = 0; /* Used to check for required fields */ 387 | pb_field_iterator_t iter; 388 | int i; 389 | 390 | pb_message_set_to_defaults(fields, dest_struct); 391 | 392 | pb_field_init(&iter, fields, dest_struct); 393 | 394 | while (stream->bytes_left) 395 | { 396 | uint32_t temp; 397 | int tag; 398 | pb_wire_type_t wire_type; 399 | if (!pb_decode_varint32(stream, &temp)) 400 | { 401 | if (stream->bytes_left == 0) 402 | break; /* It was EOF */ 403 | else 404 | return false; /* It was error */ 405 | } 406 | 407 | if (temp == 0) 408 | break; /* Special feature: allow 0-terminated messages. */ 409 | 410 | tag = temp >> 3; 411 | wire_type = (pb_wire_type_t)(temp & 7); 412 | 413 | if (!pb_field_find(&iter, tag)) 414 | { 415 | /* No match found, skip data */ 416 | if (!skip(stream, wire_type)) 417 | return false; 418 | continue; 419 | } 420 | 421 | fields_seen |= 1 << (iter.field_index & 31); 422 | 423 | if (!decode_field(stream, wire_type, &iter)) 424 | return false; 425 | } 426 | 427 | /* Check that all required fields (mod 31) were present. */ 428 | for (i = 0; fields[i].tag != 0; i++) 429 | { 430 | if (PB_HTYPE(fields[i].type) == PB_HTYPE_REQUIRED && 431 | !(fields_seen & (1 << (i & 31)))) 432 | { 433 | return false; 434 | } 435 | } 436 | 437 | return true; 438 | } 439 | 440 | /* Field decoders */ 441 | 442 | /* Copy destsize bytes from src so that values are casted properly. 443 | * On little endian machine, copy first n bytes of src 444 | * On big endian machine, copy last n bytes of src 445 | * srcsize must always be larger than destsize 446 | */ 447 | static void endian_copy(void *dest, void *src, size_t destsize, size_t srcsize) 448 | { 449 | #ifdef __BIG_ENDIAN__ 450 | memcpy(dest, (char*)src + (srcsize - destsize), destsize); 451 | #else 452 | memcpy(dest, src, destsize); 453 | #endif 454 | } 455 | 456 | bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) 457 | { 458 | uint64_t temp; 459 | bool status = pb_decode_varint(stream, &temp); 460 | endian_copy(dest, &temp, field->data_size, sizeof(temp)); 461 | return status; 462 | } 463 | 464 | bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest) 465 | { 466 | uint64_t temp; 467 | bool status = pb_decode_varint(stream, &temp); 468 | temp = (temp >> 1) ^ -(int64_t)(temp & 1); 469 | endian_copy(dest, &temp, field->data_size, sizeof(temp)); 470 | return status; 471 | } 472 | 473 | bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest) 474 | { 475 | #ifdef __BIG_ENDIAN__ 476 | uint8_t bytes[4] = {0}; 477 | bool status = pb_read(stream, bytes, 4); 478 | uint8_t bebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]}; 479 | memcpy(dest, bebytes, 4); 480 | return status; 481 | #else 482 | return pb_read(stream, (uint8_t*)dest, 4); 483 | #endif 484 | } 485 | 486 | bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest) 487 | { 488 | #ifdef __BIG_ENDIAN__ 489 | uint8_t bytes[8] = {0}; 490 | bool status = pb_read(stream, bytes, 8); 491 | uint8_t bebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4], 492 | bytes[3], bytes[2], bytes[1], bytes[0]}; 493 | memcpy(dest, bebytes, 8); 494 | return status; 495 | #else 496 | return pb_read(stream, (uint8_t*)dest, 8); 497 | #endif 498 | } 499 | 500 | bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest) 501 | { 502 | pb_bytes_array_t *x = (pb_bytes_array_t*)dest; 503 | 504 | uint32_t temp; 505 | if (!pb_decode_varint32(stream, &temp)) 506 | return false; 507 | x->size = temp; 508 | 509 | if (x->size > field->data_size) 510 | return false; 511 | 512 | return pb_read(stream, x->bytes, x->size); 513 | } 514 | 515 | bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest) 516 | { 517 | uint32_t size; 518 | bool status; 519 | if (!pb_decode_varint32(stream, &size)) 520 | return false; 521 | 522 | if (size > field->data_size - 1) 523 | return false; 524 | 525 | status = pb_read(stream, (uint8_t*)dest, size); 526 | *((uint8_t*)dest + size) = 0; 527 | return status; 528 | } 529 | 530 | bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest) 531 | { 532 | bool status; 533 | pb_istream_t substream; 534 | 535 | if (!make_string_substream(stream, &substream)) 536 | return false; 537 | 538 | if (field->ptr == NULL) 539 | return false; 540 | 541 | status = pb_decode(&substream, (pb_field_t*)field->ptr, dest); 542 | stream->state = substream.state; 543 | return status; 544 | } 545 | -------------------------------------------------------------------------------- /src/encode.c: -------------------------------------------------------------------------------- 1 | /* encode.c -- encode a protobuf using minimal resources 2 | * 3 | * 2011 Petteri Aimonen 4 | * 2011 Eguo Wang 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef __GNUC__ 12 | /* Verify that we remember to check all return values for proper error propagation */ 13 | #define checkreturn __attribute__((warn_unused_result)) 14 | #else 15 | #define checkreturn 16 | #endif 17 | 18 | 19 | typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn; 20 | 21 | /* --- Function pointers to field encoders --- 22 | * Order in the array must match pb_action_t LTYPE numbering. 23 | */ 24 | static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = { 25 | &pb_enc_varint, 26 | &pb_enc_svarint, 27 | &pb_enc_fixed32, 28 | &pb_enc_fixed64, 29 | 30 | &pb_enc_bytes, 31 | &pb_enc_string, 32 | &pb_enc_submessage 33 | }; 34 | 35 | /* pb_ostream_t implementation */ 36 | 37 | static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) 38 | { 39 | uint8_t *dest = (uint8_t*)stream->state; 40 | memcpy(dest, buf, count); 41 | stream->state = dest + count; 42 | return true; 43 | } 44 | 45 | pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize) 46 | { 47 | pb_ostream_t stream; 48 | stream.callback = &buf_write; 49 | stream.state = buf; 50 | stream.max_size = bufsize; 51 | stream.bytes_written = 0; 52 | return stream; 53 | } 54 | 55 | bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) 56 | { 57 | if (stream->callback != NULL) 58 | { 59 | if (stream->bytes_written + count > stream->max_size) 60 | return false; 61 | 62 | if (!stream->callback(stream, buf, count)) 63 | return false; 64 | } 65 | 66 | stream->bytes_written += count; 67 | return true; 68 | } 69 | 70 | /* Main encoding stuff */ 71 | 72 | /* Callbacks don't need this function because they usually know the data type 73 | * without examining the field structure. 74 | * Therefore it is static for now. 75 | */ 76 | static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, 77 | const void *pData, size_t count, pb_encoder_t func) 78 | { 79 | int i; 80 | const void *p; 81 | size_t size; 82 | 83 | if (count == 0) 84 | return true; 85 | 86 | if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) 87 | { 88 | if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) 89 | return false; 90 | 91 | /* Determine the total size of packed array. */ 92 | if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) 93 | { 94 | size = 4 * count; 95 | } 96 | else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) 97 | { 98 | size = 8 * count; 99 | } 100 | else 101 | { 102 | pb_ostream_t sizestream = {0}; 103 | p = pData; 104 | for (i = 0; i < count; i++) 105 | { 106 | if (!func(&sizestream, field, p)) 107 | return false; 108 | p = (const char*)p + field->data_size; 109 | } 110 | size = sizestream.bytes_written; 111 | } 112 | 113 | if (!pb_encode_varint(stream, size)) 114 | return false; 115 | 116 | if (stream->callback == NULL) 117 | return pb_write(stream, NULL, size); /* Just sizing.. */ 118 | 119 | /* Write the data */ 120 | p = pData; 121 | for (i = 0; i < count; i++) 122 | { 123 | if (!func(stream, field, p)) 124 | return false; 125 | p = (const char*)p + field->data_size; 126 | } 127 | } 128 | else 129 | { 130 | p = pData; 131 | for (i = 0; i < count; i++) 132 | { 133 | if (!pb_encode_tag_for_field(stream, field)) 134 | return false; 135 | if (!func(stream, field, p)) 136 | return false; 137 | p = (const char*)p + field->data_size; 138 | } 139 | } 140 | 141 | return true; 142 | } 143 | 144 | bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) 145 | { 146 | const pb_field_t *field = fields; 147 | const void *pData = src_struct; 148 | const void *pSize; 149 | size_t prev_size = 0; 150 | 151 | while (field->tag != 0) 152 | { 153 | pb_encoder_t func = PB_ENCODERS[PB_LTYPE(field->type)]; 154 | pData = (const char*)pData + prev_size + field->data_offset; 155 | pSize = (const char*)pData + field->size_offset; 156 | 157 | prev_size = field->data_size; 158 | if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY) 159 | prev_size *= field->array_size; 160 | 161 | switch (PB_HTYPE(field->type)) 162 | { 163 | case PB_HTYPE_REQUIRED: 164 | if (!pb_encode_tag_for_field(stream, field)) 165 | return false; 166 | if (!func(stream, field, pData)) 167 | return false; 168 | break; 169 | 170 | case PB_HTYPE_OPTIONAL: 171 | if (*(bool*)pSize) 172 | { 173 | if (!pb_encode_tag_for_field(stream, field)) 174 | return false; 175 | 176 | if (!func(stream, field, pData)) 177 | return false; 178 | } 179 | break; 180 | 181 | case PB_HTYPE_ARRAY: 182 | if (!encode_array(stream, field, pData, *(size_t*)pSize, func)) 183 | return false; 184 | break; 185 | 186 | case PB_HTYPE_CALLBACK: 187 | { 188 | pb_callback_t *callback = (pb_callback_t*)pData; 189 | if (callback->funcs.encode != NULL) 190 | { 191 | if (!callback->funcs.encode(stream, field, callback->arg)) 192 | return false; 193 | } 194 | break; 195 | } 196 | } 197 | 198 | field++; 199 | } 200 | 201 | return true; 202 | } 203 | 204 | /* Helper functions */ 205 | bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value) 206 | { 207 | uint8_t buffer[10]; 208 | int i = 0; 209 | 210 | if (value == 0) 211 | return pb_write(stream, (uint8_t*)&value, 1); 212 | 213 | while (value) 214 | { 215 | buffer[i] = (value & 0x7F) | 0x80; 216 | value >>= 7; 217 | i++; 218 | } 219 | buffer[i-1] &= 0x7F; /* Unset top bit on last byte */ 220 | 221 | return pb_write(stream, buffer, i); 222 | } 223 | 224 | bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number) 225 | { 226 | int tag = wiretype | (field_number << 3); 227 | return pb_encode_varint(stream, tag); 228 | } 229 | 230 | bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field) 231 | { 232 | pb_wire_type_t wiretype; 233 | switch (PB_LTYPE(field->type)) 234 | { 235 | case PB_LTYPE_VARINT: 236 | case PB_LTYPE_SVARINT: 237 | wiretype = PB_WT_VARINT; 238 | break; 239 | 240 | case PB_LTYPE_FIXED32: 241 | wiretype = PB_WT_32BIT; 242 | break; 243 | 244 | case PB_LTYPE_FIXED64: 245 | wiretype = PB_WT_64BIT; 246 | break; 247 | 248 | case PB_LTYPE_BYTES: 249 | case PB_LTYPE_STRING: 250 | case PB_LTYPE_SUBMESSAGE: 251 | wiretype = PB_WT_STRING; 252 | break; 253 | 254 | default: 255 | return false; 256 | } 257 | 258 | return pb_encode_tag(stream, wiretype, field->tag); 259 | } 260 | 261 | bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size) 262 | { 263 | if (!pb_encode_varint(stream, size)) 264 | return false; 265 | 266 | return pb_write(stream, buffer, size); 267 | } 268 | 269 | /* Field encoders */ 270 | 271 | /* Copy srcsize bytes from src so that values are casted properly. 272 | * On little endian machine, copy to start of dest 273 | * On big endian machine, copy to end of dest 274 | * destsize must always be larger than srcsize 275 | * 276 | * Note: This is the reverse of the endian_copy in pb_decode.c. 277 | */ 278 | static void endian_copy(void *dest, const void *src, size_t destsize, size_t srcsize) 279 | { 280 | #ifdef __BIG_ENDIAN__ 281 | memcpy((char*)dest + (destsize - srcsize), src, srcsize); 282 | #else 283 | memcpy(dest, src, srcsize); 284 | #endif 285 | } 286 | 287 | bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src) 288 | { 289 | uint64_t value = 0; 290 | endian_copy(&value, src, sizeof(value), field->data_size); 291 | return pb_encode_varint(stream, value); 292 | } 293 | 294 | bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) 295 | { 296 | uint64_t value = 0; 297 | uint64_t zigzagged; 298 | uint64_t signbitmask, xormask; 299 | endian_copy(&value, src, sizeof(value), field->data_size); 300 | 301 | signbitmask = (uint64_t)0x80 << (field->data_size * 8 - 8); 302 | xormask = ((uint64_t)-1) >> (64 - field->data_size * 8); 303 | if (value & signbitmask) 304 | zigzagged = ((value ^ xormask) << 1) | 1; 305 | else 306 | zigzagged = value << 1; 307 | 308 | return pb_encode_varint(stream, zigzagged); 309 | } 310 | 311 | bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src) 312 | { 313 | #ifdef __BIG_ENDIAN__ 314 | uint8_t bytes[8] = {0}; 315 | memcpy(bytes, src, 8); 316 | uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4], 317 | bytes[3], bytes[2], bytes[1], bytes[0]}; 318 | return pb_write(stream, lebytes, 8); 319 | #else 320 | return pb_write(stream, (uint8_t*)src, 8); 321 | #endif 322 | } 323 | 324 | bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src) 325 | { 326 | #ifdef __BIG_ENDIAN__ 327 | uint8_t bytes[4] = {0}; 328 | memcpy(bytes, src, 4); 329 | uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]}; 330 | return pb_write(stream, lebytes, 4); 331 | #else 332 | return pb_write(stream, (uint8_t*)src, 4); 333 | #endif 334 | } 335 | 336 | bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src) 337 | { 338 | pb_bytes_array_t *bytes = (pb_bytes_array_t*)src; 339 | return pb_encode_string(stream, bytes->bytes, bytes->size); 340 | } 341 | 342 | bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src) 343 | { 344 | return pb_encode_string(stream, (uint8_t*)src, strlen((char*)src)); 345 | } 346 | 347 | bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src) 348 | { 349 | pb_ostream_t substream = {0}; 350 | size_t size; 351 | bool status; 352 | 353 | if (field->ptr == NULL) 354 | return false; 355 | 356 | if (!pb_encode(&substream, (pb_field_t*)field->ptr, src)) 357 | return false; 358 | 359 | size = substream.bytes_written; 360 | 361 | if (!pb_encode_varint(stream, size)) 362 | return false; 363 | 364 | if (stream->callback == NULL) 365 | return pb_write(stream, NULL, size); /* Just sizing */ 366 | 367 | if (stream->bytes_written + size > stream->max_size) 368 | return false; 369 | 370 | /* Use a substream to verify that a callback doesn't write more than 371 | * what it did the first time. */ 372 | substream.callback = stream->callback; 373 | substream.state = stream->state; 374 | substream.max_size = size; 375 | substream.bytes_written = 0; 376 | 377 | status = pb_encode(&substream, (pb_field_t*)field->ptr, src); 378 | 379 | stream->bytes_written += substream.bytes_written; 380 | 381 | if (substream.bytes_written != size) 382 | return false; 383 | 384 | return status; 385 | } 386 | 387 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-ansi -Wall -Werror -I../include -g -O0 --coverage 2 | LDFLAGS=--coverage -L../lib -lpb 3 | DEPS = person.pb.h callbacks.pb.h unittests.h unittestproto.pb.h 4 | TESTS=test_decode1 test_encode1 decode_unittests encode_unittests \ 5 | test_encode_callbacks test_decode_callbacks 6 | 7 | all: breakpoints $(TESTS) run_unittests 8 | 9 | clean: 10 | rm -f $(TESTS) person.pb* *.o *.gcda *.gcno 11 | 12 | %.o: %.c 13 | %.o: %.c $(DEPS) 14 | $(CC) $(CFLAGS) -c -o $@ $< 15 | 16 | test_decode1: test_decode1.o person.pb.o 17 | test_encode1: test_encode1.o person.pb.o 18 | test_decode_callbacks: test_decode_callbacks.o callbacks.pb.o 19 | test_encode_callbacks: test_encode_callbacks.o callbacks.pb.o 20 | decode_unittests: decode_unittests.o unittestproto.pb.o 21 | encode_unittests: encode_unittests.o unittestproto.pb.o 22 | 23 | %.pb: %.proto 24 | protoc -I. -I../include/ -I/usr/include -o$@ $< 25 | 26 | %.pb.c %.pb.h: %.pb ../generator/pbgen.py 27 | python ../generator/pbgen.py $< 28 | 29 | breakpoints: *.c 30 | grep -n 'return false;' $^ | cut -d: -f-2 | xargs -n 1 echo b > $@ 31 | 32 | coverage: run_unittests 33 | gcov pb_encode.gcda 34 | gcov pb_decode.gcda 35 | 36 | run_unittests: decode_unittests encode_unittests test_encode1 test_decode1 test_encode_callbacks test_decode_callbacks 37 | rm -f *.gcda 38 | 39 | ./decode_unittests > /dev/null 40 | ./encode_unittests > /dev/null 41 | 42 | [ "`./test_encode1 | ./test_decode1`" = \ 43 | "`./test_encode1 | protoc --decode=Person -I. -I../include/ -I/usr/include person.proto`" ] 44 | 45 | [ "`./test_encode_callbacks | ./test_decode_callbacks`" = \ 46 | "`./test_encode_callbacks | protoc --decode=TestMessage callbacks.proto`" ] 47 | 48 | run_fuzztest: test_decode1 49 | bash -c 'I=1; while cat /dev/urandom | ./test_decode1 > /dev/null; do I=$$(($$I+1)); echo -en "\r$$I"; done' 50 | -------------------------------------------------------------------------------- /tests/callbacks.proto: -------------------------------------------------------------------------------- 1 | message SubMessage { 2 | optional string stringvalue = 1; 3 | repeated int32 int32value = 2; 4 | repeated fixed32 fixed32value = 3; 5 | repeated fixed64 fixed64value = 4; 6 | } 7 | 8 | message TestMessage { 9 | optional string stringvalue = 1; 10 | repeated int32 int32value = 2; 11 | repeated fixed32 fixed32value = 3; 12 | repeated fixed64 fixed64value = 4; 13 | optional SubMessage submsg = 5; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/decode_unittests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "unittests.h" 5 | #include "unittestproto.pb.h" 6 | 7 | #define S(x) pb_istream_from_buffer((uint8_t*)x, sizeof(x) - 1) 8 | 9 | bool stream_callback(pb_istream_t *stream, uint8_t *buf, size_t count) 10 | { 11 | if (stream->state != NULL) 12 | return false; /* Simulate error */ 13 | 14 | if (buf != NULL) 15 | memset(buf, 'x', count); 16 | return true; 17 | } 18 | 19 | /* Verifies that the stream passed to callback matches the byte array pointed to by arg. */ 20 | bool callback_check(pb_istream_t *stream, const pb_field_t *field, void *arg) 21 | { 22 | int i; 23 | uint8_t byte; 24 | pb_bytes_array_t *ref = (pb_bytes_array_t*) arg; 25 | 26 | for (i = 0; i < ref->size; i++) 27 | { 28 | if (!pb_read(stream, &byte, 1)) 29 | return false; 30 | 31 | if (byte != ref->bytes[i]) 32 | return false; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | int main() 39 | { 40 | int status = 0; 41 | 42 | { 43 | uint8_t buffer1[] = "foobartest1234"; 44 | uint8_t buffer2[sizeof(buffer1)]; 45 | pb_istream_t stream = pb_istream_from_buffer(buffer1, sizeof(buffer1)); 46 | 47 | COMMENT("Test pb_read and pb_istream_t"); 48 | TEST(pb_read(&stream, buffer2, 6)) 49 | TEST(memcmp(buffer2, "foobar", 6) == 0) 50 | TEST(stream.bytes_left == sizeof(buffer1) - 6) 51 | TEST(pb_read(&stream, buffer2 + 6, stream.bytes_left)) 52 | TEST(memcmp(buffer1, buffer2, sizeof(buffer1)) == 0) 53 | TEST(stream.bytes_left == 0) 54 | TEST(!pb_read(&stream, buffer2, 1)) 55 | } 56 | 57 | { 58 | uint8_t buffer[20]; 59 | pb_istream_t stream = {&stream_callback, NULL, 20}; 60 | 61 | COMMENT("Test pb_read with custom callback"); 62 | TEST(pb_read(&stream, buffer, 5)) 63 | TEST(memcmp(buffer, "xxxxx", 5) == 0) 64 | TEST(!pb_read(&stream, buffer, 50)) 65 | stream.state = (void*)1; /* Simulated error return from callback */ 66 | TEST(!pb_read(&stream, buffer, 5)) 67 | stream.state = NULL; 68 | TEST(pb_read(&stream, buffer, 15)) 69 | } 70 | 71 | { 72 | pb_istream_t s; 73 | uint64_t u; 74 | int64_t i; 75 | 76 | COMMENT("Test pb_decode_varint"); 77 | TEST((s = S("\x00"), pb_decode_varint(&s, &u) && u == 0)); 78 | TEST((s = S("\x01"), pb_decode_varint(&s, &u) && u == 1)); 79 | TEST((s = S("\xAC\x02"), pb_decode_varint(&s, &u) && u == 300)); 80 | TEST((s = S("\xFF\xFF\xFF\xFF\x0F"), pb_decode_varint(&s, &u) && u == UINT32_MAX)); 81 | TEST((s = S("\xFF\xFF\xFF\xFF\x0F"), pb_decode_varint(&s, (uint64_t*)&i) && i == UINT32_MAX)); 82 | TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), 83 | pb_decode_varint(&s, (uint64_t*)&i) && i == -1)); 84 | TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), 85 | pb_decode_varint(&s, &u) && u == UINT64_MAX)); 86 | } 87 | 88 | { 89 | pb_istream_t s; 90 | COMMENT("Test pb_skip_varint"); 91 | TEST((s = S("\x00""foobar"), pb_skip_varint(&s) && s.bytes_left == 6)) 92 | TEST((s = S("\xAC\x02""foobar"), pb_skip_varint(&s) && s.bytes_left == 6)) 93 | TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01""foobar"), 94 | pb_skip_varint(&s) && s.bytes_left == 6)) 95 | TEST((s = S("\xFF"), !pb_skip_varint(&s))) 96 | } 97 | 98 | { 99 | pb_istream_t s; 100 | COMMENT("Test pb_skip_string") 101 | TEST((s = S("\x00""foobar"), pb_skip_string(&s) && s.bytes_left == 6)) 102 | TEST((s = S("\x04""testfoobar"), pb_skip_string(&s) && s.bytes_left == 6)) 103 | TEST((s = S("\x04"), !pb_skip_string(&s))) 104 | TEST((s = S("\xFF"), !pb_skip_string(&s))) 105 | } 106 | 107 | { 108 | pb_istream_t s = S("\x01\xFF\xFF\x03"); 109 | pb_field_t f = {1, PB_LTYPE_VARINT, 0, 0, 4, 0, 0}; 110 | uint32_t d; 111 | COMMENT("Test pb_dec_varint using uint32_t") 112 | TEST(pb_dec_varint(&s, &f, &d) && d == 1) 113 | 114 | /* Verify that no more than data_size is written. */ 115 | d = 0; 116 | f.data_size = 1; 117 | TEST(pb_dec_varint(&s, &f, &d) && (d == 0xFF || d == 0xFF000000)) 118 | } 119 | 120 | { 121 | pb_istream_t s; 122 | pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 4, 0, 0}; 123 | int32_t d; 124 | 125 | COMMENT("Test pb_dec_svarint using int32_t") 126 | TEST((s = S("\x01"), pb_dec_svarint(&s, &f, &d) && d == -1)) 127 | TEST((s = S("\x02"), pb_dec_svarint(&s, &f, &d) && d == 1)) 128 | TEST((s = S("\xfe\xff\xff\xff\x0f"), pb_dec_svarint(&s, &f, &d) && d == INT32_MAX)) 129 | TEST((s = S("\xff\xff\xff\xff\x0f"), pb_dec_svarint(&s, &f, &d) && d == INT32_MIN)) 130 | } 131 | 132 | { 133 | pb_istream_t s; 134 | pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 8, 0, 0}; 135 | uint64_t d; 136 | 137 | COMMENT("Test pb_dec_svarint using uint64_t") 138 | TEST((s = S("\x01"), pb_dec_svarint(&s, &f, &d) && d == -1)) 139 | TEST((s = S("\x02"), pb_dec_svarint(&s, &f, &d) && d == 1)) 140 | TEST((s = S("\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_dec_svarint(&s, &f, &d) && d == INT64_MAX)) 141 | TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_dec_svarint(&s, &f, &d) && d == INT64_MIN)) 142 | } 143 | 144 | { 145 | pb_istream_t s; 146 | pb_field_t f = {1, PB_LTYPE_FIXED32, 0, 0, 4, 0, 0}; 147 | float d; 148 | 149 | COMMENT("Test pb_dec_fixed32 using float (failures here may be caused by imperfect rounding)") 150 | TEST((s = S("\x00\x00\x00\x00"), pb_dec_fixed32(&s, &f, &d) && d == 0.0f)) 151 | TEST((s = S("\x00\x00\xc6\x42"), pb_dec_fixed32(&s, &f, &d) && d == 99.0f)) 152 | TEST((s = S("\x4e\x61\x3c\xcb"), pb_dec_fixed32(&s, &f, &d) && d == -12345678.0f)) 153 | TEST((s = S("\x00"), !pb_dec_fixed32(&s, &f, &d) && d == -12345678.0f)) 154 | } 155 | 156 | { 157 | pb_istream_t s; 158 | pb_field_t f = {1, PB_LTYPE_FIXED64, 0, 0, 8, 0, 0}; 159 | double d; 160 | 161 | COMMENT("Test pb_dec_fixed64 using double (failures here may be caused by imperfect rounding)") 162 | TEST((s = S("\x00\x00\x00\x00\x00\x00\x00\x00"), pb_dec_fixed64(&s, &f, &d) && d == 0.0)) 163 | TEST((s = S("\x00\x00\x00\x00\x00\xc0\x58\x40"), pb_dec_fixed64(&s, &f, &d) && d == 99.0)) 164 | TEST((s = S("\x00\x00\x00\xc0\x29\x8c\x67\xc1"), pb_dec_fixed64(&s, &f, &d) && d == -12345678.0f)) 165 | } 166 | 167 | { 168 | pb_istream_t s; 169 | struct { size_t size; uint8_t bytes[5]; } d; 170 | pb_field_t f = {1, PB_LTYPE_BYTES, 0, 0, 5, 0, 0}; 171 | 172 | COMMENT("Test pb_dec_bytes") 173 | TEST((s = S("\x00"), pb_dec_bytes(&s, &f, &d) && d.size == 0)) 174 | TEST((s = S("\x01\xFF"), pb_dec_bytes(&s, &f, &d) && d.size == 1 && d.bytes[0] == 0xFF)) 175 | TEST((s = S("\x06xxxxxx"), !pb_dec_bytes(&s, &f, &d))) 176 | TEST((s = S("\x05xxxxx"), pb_dec_bytes(&s, &f, &d) && d.size == 5)) 177 | TEST((s = S("\x05xxxx"), !pb_dec_bytes(&s, &f, &d))) 178 | } 179 | 180 | { 181 | pb_istream_t s; 182 | pb_field_t f = {1, PB_LTYPE_STRING, 0, 0, 5, 0, 0}; 183 | char d[5]; 184 | 185 | COMMENT("Test pb_dec_string") 186 | TEST((s = S("\x00"), pb_dec_string(&s, &f, &d) && d[0] == '\0')) 187 | TEST((s = S("\x04xyzz"), pb_dec_string(&s, &f, &d) && strcmp(d, "xyzz") == 0)) 188 | TEST((s = S("\x05xyzzy"), !pb_dec_string(&s, &f, &d))) 189 | } 190 | 191 | { 192 | pb_istream_t s; 193 | IntegerArray dest; 194 | 195 | COMMENT("Testing pb_decode with repeated int32 field") 196 | TEST((s = S(""), pb_decode(&s, IntegerArray_fields, &dest) && dest.data_count == 0)) 197 | TEST((s = S("\x08\x01\x08\x02"), pb_decode(&s, IntegerArray_fields, &dest) 198 | && dest.data_count == 2 && dest.data[0] == 1 && dest.data[1] == 2)) 199 | s = S("\x08\x01\x08\x02\x08\x03\x08\x04\x08\x05\x08\x06\x08\x07\x08\x08\x08\x09\x08\x0A"); 200 | TEST(pb_decode(&s, IntegerArray_fields, &dest) && dest.data_count == 10 && dest.data[9] == 10) 201 | s = S("\x08\x01\x08\x02\x08\x03\x08\x04\x08\x05\x08\x06\x08\x07\x08\x08\x08\x09\x08\x0A\x08\x0B"); 202 | TEST(!pb_decode(&s, IntegerArray_fields, &dest)) 203 | } 204 | 205 | { 206 | pb_istream_t s; 207 | IntegerArray dest; 208 | 209 | COMMENT("Testing pb_decode with packed int32 field") 210 | TEST((s = S("\x0A\x00"), pb_decode(&s, IntegerArray_fields, &dest) 211 | && dest.data_count == 0)) 212 | TEST((s = S("\x0A\x01\x01"), pb_decode(&s, IntegerArray_fields, &dest) 213 | && dest.data_count == 1 && dest.data[0] == 1)) 214 | TEST((s = S("\x0A\x0A\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"), pb_decode(&s, IntegerArray_fields, &dest) 215 | && dest.data_count == 10 && dest.data[0] == 1 && dest.data[9] == 10)) 216 | TEST((s = S("\x0A\x0B\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"), !pb_decode(&s, IntegerArray_fields, &dest))) 217 | 218 | /* Test invalid wire data */ 219 | TEST((s = S("\x0A\xFF"), !pb_decode(&s, IntegerArray_fields, &dest))) 220 | TEST((s = S("\x0A\x01"), !pb_decode(&s, IntegerArray_fields, &dest))) 221 | } 222 | 223 | { 224 | pb_istream_t s; 225 | IntegerArray dest; 226 | 227 | COMMENT("Testing pb_decode with unknown fields") 228 | TEST((s = S("\x18\x0F\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest) 229 | && dest.data_count == 1 && dest.data[0] == 1)) 230 | TEST((s = S("\x19\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest) 231 | && dest.data_count == 1 && dest.data[0] == 1)) 232 | TEST((s = S("\x1A\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest) 233 | && dest.data_count == 1 && dest.data[0] == 1)) 234 | TEST((s = S("\x1B\x08\x01"), !pb_decode(&s, IntegerArray_fields, &dest))) 235 | TEST((s = S("\x1D\x00\x00\x00\x00\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest) 236 | && dest.data_count == 1 && dest.data[0] == 1)) 237 | } 238 | 239 | { 240 | pb_istream_t s; 241 | CallbackArray dest; 242 | struct { size_t size; uint8_t bytes[10]; } ref; 243 | dest.data.funcs.decode = &callback_check; 244 | dest.data.arg = &ref; 245 | 246 | COMMENT("Testing pb_decode with callbacks") 247 | /* Single varint */ 248 | ref.size = 1; ref.bytes[0] = 0x55; 249 | TEST((s = S("\x08\x55"), pb_decode(&s, CallbackArray_fields, &dest))) 250 | /* Packed varint */ 251 | ref.size = 3; ref.bytes[0] = ref.bytes[1] = ref.bytes[2] = 0x55; 252 | TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_fields, &dest))) 253 | /* Packed varint with loop */ 254 | ref.size = 1; ref.bytes[0] = 0x55; 255 | TEST((s = S("\x0A\x03\x55\x55\x55"), pb_decode(&s, CallbackArray_fields, &dest))) 256 | /* Single fixed32 */ 257 | ref.size = 4; ref.bytes[0] = ref.bytes[1] = ref.bytes[2] = ref.bytes[3] = 0xAA; 258 | TEST((s = S("\x0D\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_fields, &dest))) 259 | /* Single fixed64 */ 260 | ref.size = 8; memset(ref.bytes, 0xAA, 8); 261 | TEST((s = S("\x09\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"), pb_decode(&s, CallbackArray_fields, &dest))) 262 | /* Unsupported field type */ 263 | TEST((s = S("\x0B\x00"), !pb_decode(&s, CallbackArray_fields, &dest))) 264 | 265 | /* Just make sure that our test function works */ 266 | ref.size = 1; ref.bytes[0] = 0x56; 267 | TEST((s = S("\x08\x55"), !pb_decode(&s, CallbackArray_fields, &dest))) 268 | } 269 | 270 | { 271 | pb_istream_t s; 272 | IntegerArray dest; 273 | 274 | COMMENT("Testing pb_decode message termination") 275 | TEST((s = S(""), pb_decode(&s, IntegerArray_fields, &dest))) 276 | TEST((s = S("\x00"), pb_decode(&s, IntegerArray_fields, &dest))) 277 | TEST((s = S("\x08\x01"), pb_decode(&s, IntegerArray_fields, &dest))) 278 | TEST((s = S("\x08\x01\x00"), pb_decode(&s, IntegerArray_fields, &dest))) 279 | TEST((s = S("\x08"), !pb_decode(&s, IntegerArray_fields, &dest))) 280 | } 281 | 282 | if (status != 0) 283 | fprintf(stdout, "\n\nSome tests FAILED!\n"); 284 | 285 | return status; 286 | } 287 | -------------------------------------------------------------------------------- /tests/encode_unittests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "unittests.h" 5 | #include "unittestproto.pb.h" 6 | 7 | bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count) 8 | { 9 | /* Allow only 'x' to be written */ 10 | while (count--) 11 | { 12 | if (*buf++ != 'x') 13 | return false; 14 | } 15 | return true; 16 | } 17 | 18 | bool fieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 19 | { 20 | int value = 0x55; 21 | if (!pb_encode_tag_for_field(stream, field)) 22 | return false; 23 | return pb_encode_varint(stream, value); 24 | } 25 | 26 | bool crazyfieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 27 | { 28 | /* This callback writes different amount of data the second time. */ 29 | uint32_t *state = (uint32_t*)arg; 30 | *state <<= 8; 31 | if (!pb_encode_tag_for_field(stream, field)) 32 | return false; 33 | return pb_encode_varint(stream, *state); 34 | } 35 | 36 | /* Check that expression x writes data y. 37 | * Y is a string, which may contain null bytes. Null terminator is ignored. 38 | */ 39 | #define WRITES(x, y) \ 40 | memset(buffer, 0xAA, sizeof(buffer)), \ 41 | s = pb_ostream_from_buffer(buffer, sizeof(buffer)), \ 42 | (x) && \ 43 | memcmp(buffer, y, sizeof(y) - 1) == 0 && \ 44 | buffer[sizeof(y) - 1] == 0xAA 45 | 46 | int main() 47 | { 48 | int status = 0; 49 | 50 | { 51 | uint8_t buffer1[] = "foobartest1234"; 52 | uint8_t buffer2[sizeof(buffer1)]; 53 | pb_ostream_t stream = pb_ostream_from_buffer(buffer2, sizeof(buffer1)); 54 | 55 | COMMENT("Test pb_write and pb_ostream_t"); 56 | TEST(pb_write(&stream, buffer1, sizeof(buffer1))); 57 | TEST(memcmp(buffer1, buffer2, sizeof(buffer1)) == 0); 58 | TEST(!pb_write(&stream, buffer1, 1)); 59 | TEST(stream.bytes_written == sizeof(buffer1)); 60 | } 61 | 62 | { 63 | uint8_t buffer1[] = "xxxxxxx"; 64 | pb_ostream_t stream = {&streamcallback, 0, SIZE_MAX, 0}; 65 | 66 | COMMENT("Test pb_write with custom callback"); 67 | TEST(pb_write(&stream, buffer1, 5)); 68 | buffer1[0] = 'a'; 69 | TEST(!pb_write(&stream, buffer1, 5)); 70 | } 71 | 72 | { 73 | uint8_t buffer[30]; 74 | pb_ostream_t s; 75 | 76 | COMMENT("Test pb_encode_varint") 77 | TEST(WRITES(pb_encode_varint(&s, 0), "\0")); 78 | TEST(WRITES(pb_encode_varint(&s, 1), "\1")); 79 | TEST(WRITES(pb_encode_varint(&s, 0x7F), "\x7F")); 80 | TEST(WRITES(pb_encode_varint(&s, 0x80), "\x80\x01")); 81 | TEST(WRITES(pb_encode_varint(&s, UINT32_MAX), "\xFF\xFF\xFF\xFF\x0F")); 82 | TEST(WRITES(pb_encode_varint(&s, UINT64_MAX), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01")); 83 | } 84 | 85 | { 86 | uint8_t buffer[30]; 87 | pb_ostream_t s; 88 | 89 | COMMENT("Test pb_encode_tag") 90 | TEST(WRITES(pb_encode_tag(&s, PB_WT_STRING, 5), "\x2A")); 91 | TEST(WRITES(pb_encode_tag(&s, PB_WT_VARINT, 99), "\x98\x06")); 92 | } 93 | 94 | { 95 | uint8_t buffer[30]; 96 | pb_ostream_t s; 97 | pb_field_t field = {10, PB_LTYPE_SVARINT}; 98 | 99 | COMMENT("Test pb_encode_tag_for_field") 100 | TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x50")); 101 | 102 | field.type = PB_LTYPE_FIXED64; 103 | TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x51")); 104 | 105 | field.type = PB_LTYPE_STRING; 106 | TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x52")); 107 | 108 | field.type = PB_LTYPE_FIXED32; 109 | TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x55")); 110 | } 111 | 112 | { 113 | uint8_t buffer[30]; 114 | pb_ostream_t s; 115 | 116 | COMMENT("Test pb_encode_string") 117 | TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"abcd", 4), "\x04""abcd")); 118 | TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"abcd\x00", 5), "\x05""abcd\x00")); 119 | TEST(WRITES(pb_encode_string(&s, (const uint8_t*)"", 0), "\x00")); 120 | } 121 | 122 | { 123 | uint8_t buffer[30]; 124 | pb_ostream_t s; 125 | uint8_t value = 1; 126 | int8_t svalue = -1; 127 | int32_t max = INT32_MAX; 128 | int32_t min = INT32_MIN; 129 | int64_t lmax = INT64_MAX; 130 | int64_t lmin = INT64_MIN; 131 | pb_field_t field = {1, PB_LTYPE_VARINT, 0, 0, sizeof(value)}; 132 | 133 | COMMENT("Test pb_enc_varint and pb_enc_svarint") 134 | TEST(WRITES(pb_enc_varint(&s, &field, &value), "\x01")); 135 | TEST(WRITES(pb_enc_svarint(&s, &field, &svalue), "\x01")); 136 | TEST(WRITES(pb_enc_svarint(&s, &field, &value), "\x02")); 137 | 138 | field.data_size = sizeof(max); 139 | TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f")); 140 | TEST(WRITES(pb_enc_svarint(&s, &field, &min), "\xff\xff\xff\xff\x0f")); 141 | 142 | field.data_size = sizeof(lmax); 143 | TEST(WRITES(pb_enc_svarint(&s, &field, &lmax), "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01")); 144 | TEST(WRITES(pb_enc_svarint(&s, &field, &lmin), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01")); 145 | } 146 | 147 | { 148 | uint8_t buffer[30]; 149 | pb_ostream_t s; 150 | float fvalue; 151 | double dvalue; 152 | 153 | COMMENT("Test pb_enc_fixed32 using float") 154 | fvalue = 0.0f; 155 | TEST(WRITES(pb_enc_fixed32(&s, NULL, &fvalue), "\x00\x00\x00\x00")) 156 | fvalue = 99.0f; 157 | TEST(WRITES(pb_enc_fixed32(&s, NULL, &fvalue), "\x00\x00\xc6\x42")) 158 | fvalue = -12345678.0f; 159 | TEST(WRITES(pb_enc_fixed32(&s, NULL, &fvalue), "\x4e\x61\x3c\xcb")) 160 | 161 | COMMENT("Test pb_enc_fixed64 using double") 162 | dvalue = 0.0; 163 | TEST(WRITES(pb_enc_fixed64(&s, NULL, &dvalue), "\x00\x00\x00\x00\x00\x00\x00\x00")) 164 | dvalue = 99.0; 165 | TEST(WRITES(pb_enc_fixed64(&s, NULL, &dvalue), "\x00\x00\x00\x00\x00\xc0\x58\x40")) 166 | dvalue = -12345678.0; 167 | TEST(WRITES(pb_enc_fixed64(&s, NULL, &dvalue), "\x00\x00\x00\xc0\x29\x8c\x67\xc1")) 168 | } 169 | 170 | { 171 | uint8_t buffer[30]; 172 | pb_ostream_t s; 173 | struct { size_t size; uint8_t bytes[5]; } value = {5, {'x', 'y', 'z', 'z', 'y'}}; 174 | 175 | COMMENT("Test pb_enc_bytes") 176 | TEST(WRITES(pb_enc_bytes(&s, NULL, &value), "\x05xyzzy")) 177 | value.size = 0; 178 | TEST(WRITES(pb_enc_bytes(&s, NULL, &value), "\x00")) 179 | } 180 | 181 | { 182 | uint8_t buffer[30]; 183 | pb_ostream_t s; 184 | char value[] = "xyzzy"; 185 | 186 | COMMENT("Test pb_enc_string") 187 | TEST(WRITES(pb_enc_string(&s, NULL, &value), "\x05xyzzy")) 188 | value[0] = '\0'; 189 | TEST(WRITES(pb_enc_string(&s, NULL, &value), "\x00")) 190 | } 191 | 192 | { 193 | uint8_t buffer[10]; 194 | pb_ostream_t s; 195 | IntegerArray msg = {5, {1, 2, 3, 4, 5}}; 196 | 197 | COMMENT("Test pb_encode with int32 array") 198 | 199 | TEST(WRITES(pb_encode(&s, IntegerArray_fields, &msg), "\x0A\x05\x01\x02\x03\x04\x05")) 200 | 201 | msg.data_count = 0; 202 | TEST(WRITES(pb_encode(&s, IntegerArray_fields, &msg), "")) 203 | 204 | msg.data_count = 10; 205 | TEST(!pb_encode(&s, IntegerArray_fields, &msg)) 206 | } 207 | 208 | { 209 | uint8_t buffer[10]; 210 | pb_ostream_t s; 211 | FloatArray msg = {1, {99.0f}}; 212 | 213 | COMMENT("Test pb_encode with float array") 214 | 215 | TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg), 216 | "\x0A\x04\x00\x00\xc6\x42")) 217 | 218 | msg.data_count = 0; 219 | TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg), "")) 220 | 221 | msg.data_count = 3; 222 | TEST(!pb_encode(&s, FloatArray_fields, &msg)) 223 | } 224 | 225 | { 226 | uint8_t buffer[10]; 227 | pb_ostream_t s; 228 | CallbackArray msg; 229 | 230 | msg.data.funcs.encode = &fieldcallback; 231 | 232 | COMMENT("Test pb_encode with callback field.") 233 | TEST(WRITES(pb_encode(&s, CallbackArray_fields, &msg), "\x08\x55")) 234 | } 235 | 236 | { 237 | uint8_t buffer[10]; 238 | pb_ostream_t s; 239 | IntegerContainer msg = {{5, {1,2,3,4,5}}}; 240 | 241 | COMMENT("Test pb_encode with packed array in a submessage.") 242 | TEST(WRITES(pb_encode(&s, IntegerContainer_fields, &msg), 243 | "\x0A\x07\x0A\x05\x01\x02\x03\x04\x05")) 244 | } 245 | 246 | { 247 | uint8_t buffer[10]; 248 | pb_ostream_t s; 249 | CallbackContainer msg; 250 | CallbackContainerContainer msg2; 251 | uint32_t state = 1; 252 | 253 | msg.submsg.data.funcs.encode = &fieldcallback; 254 | msg2.submsg.submsg.data.funcs.encode = &fieldcallback; 255 | 256 | COMMENT("Test pb_encode with callback field in a submessage.") 257 | TEST(WRITES(pb_encode(&s, CallbackContainer_fields, &msg), "\x0A\x02\x08\x55")) 258 | TEST(WRITES(pb_encode(&s, CallbackContainerContainer_fields, &msg2), 259 | "\x0A\x04\x0A\x02\x08\x55")) 260 | 261 | /* Misbehaving callback: varying output between calls */ 262 | msg.submsg.data.funcs.encode = &crazyfieldcallback; 263 | msg.submsg.data.arg = &state; 264 | msg2.submsg.submsg.data.funcs.encode = &crazyfieldcallback; 265 | msg2.submsg.submsg.data.arg = &state; 266 | 267 | TEST(!pb_encode(&s, CallbackContainer_fields, &msg)) 268 | state = 1; 269 | TEST(!pb_encode(&s, CallbackContainerContainer_fields, &msg2)) 270 | } 271 | 272 | if (status != 0) 273 | fprintf(stdout, "\n\nSome tests FAILED!\n"); 274 | 275 | return status; 276 | } 277 | -------------------------------------------------------------------------------- /tests/person.proto: -------------------------------------------------------------------------------- 1 | import "pb/pb.proto"; 2 | 3 | message Person { 4 | required string name = 1 [(pb).max_size = 40]; 5 | required int32 id = 2; 6 | optional string email = 3 [(pb).max_size = 40]; 7 | 8 | enum PhoneType { 9 | MOBILE = 0; 10 | HOME = 1; 11 | WORK = 2; 12 | } 13 | 14 | message PhoneNumber { 15 | required string number = 1 [(pb).max_size = 40]; 16 | optional PhoneType type = 2 [default = HOME]; 17 | } 18 | 19 | repeated PhoneNumber phone = 4 [(pb).max_count = 5]; 20 | } 21 | -------------------------------------------------------------------------------- /tests/test_decode1.c: -------------------------------------------------------------------------------- 1 | /* A very simple decoding test case, using person.proto. 2 | * Produces output compatible with protoc --decode. 3 | * Reads the encoded data from stdin and prints the values 4 | * to stdout as text. 5 | * 6 | * Run e.g. ./test_encode1 | ./test_decode1 7 | */ 8 | 9 | #include 10 | #include 11 | #include "person.pb.h" 12 | 13 | /* This function is called once from main(), it handles 14 | the decoding and printing. */ 15 | bool print_person(pb_istream_t *stream) 16 | { 17 | int i; 18 | Person person; 19 | 20 | if (!pb_decode(stream, Person_fields, &person)) 21 | return false; 22 | 23 | /* Now the decoding is done, rest is just to print stuff out. */ 24 | 25 | printf("name: \"%s\"\n", person.name); 26 | printf("id: %d\n", person.id); 27 | 28 | if (person.has_email) 29 | printf("email: \"%s\"\n", person.email); 30 | 31 | for (i = 0; i < person.phone_count; i++) 32 | { 33 | Person_PhoneNumber *phone = &person.phone[i]; 34 | printf("phone {\n"); 35 | printf(" number: \"%s\"\n", phone->number); 36 | 37 | switch (phone->type) 38 | { 39 | case Person_PhoneType_WORK: 40 | printf(" type: WORK\n"); 41 | break; 42 | 43 | case Person_PhoneType_HOME: 44 | printf(" type: HOME\n"); 45 | break; 46 | 47 | case Person_PhoneType_MOBILE: 48 | printf(" type: MOBILE\n"); 49 | break; 50 | } 51 | printf("}\n"); 52 | } 53 | 54 | return true; 55 | } 56 | 57 | /* This binds the pb_istream_t to stdin */ 58 | bool callback(pb_istream_t *stream, uint8_t *buf, size_t count) 59 | { 60 | FILE *file = (FILE*)stream->state; 61 | bool status; 62 | 63 | if (buf == NULL) 64 | { 65 | /* Skipping data */ 66 | while (count-- && fgetc(file) != EOF); 67 | return count == 0; 68 | } 69 | 70 | status = (fread(buf, 1, count, file) == count); 71 | 72 | if (feof(file)) 73 | stream->bytes_left = 0; 74 | 75 | return status; 76 | } 77 | 78 | int main() 79 | { 80 | /* Maximum size is specified to prevent infinite length messages from 81 | * hanging this in the fuzz test. 82 | */ 83 | pb_istream_t stream = {&callback, stdin, 10000}; 84 | if (!print_person(&stream)) 85 | { 86 | printf("Parsing failed.\n"); 87 | return 1; 88 | } else { 89 | return 0; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/test_decode_callbacks.c: -------------------------------------------------------------------------------- 1 | /* Decoding testcase for callback fields. 2 | * Run e.g. ./test_encode_callbacks | ./test_decode_callbacks 3 | */ 4 | 5 | #include 6 | #include 7 | #include "callbacks.pb.h" 8 | 9 | bool print_string(pb_istream_t *stream, const pb_field_t *field, void *arg) 10 | { 11 | uint8_t buffer[1024] = {0}; 12 | 13 | /* We could read block-by-block to avoid the large buffer... */ 14 | if (stream->bytes_left > sizeof(buffer) - 1) 15 | return false; 16 | 17 | if (!pb_read(stream, buffer, stream->bytes_left)) 18 | return false; 19 | 20 | /* Print the string, in format comparable with protoc --decode. 21 | * Format comes from the arg defined in main(). 22 | */ 23 | printf((char*)arg, buffer); 24 | return true; 25 | } 26 | 27 | bool print_int32(pb_istream_t *stream, const pb_field_t *field, void *arg) 28 | { 29 | uint64_t value; 30 | if (!pb_decode_varint(stream, &value)) 31 | return false; 32 | 33 | printf((char*)arg, (int32_t)value); 34 | return true; 35 | } 36 | 37 | bool print_fixed32(pb_istream_t *stream, const pb_field_t *field, void *arg) 38 | { 39 | uint32_t value; 40 | if (!pb_dec_fixed32(stream, NULL, &value)) 41 | return false; 42 | 43 | printf((char*)arg, value); 44 | return true; 45 | } 46 | 47 | bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void *arg) 48 | { 49 | uint64_t value; 50 | if (!pb_dec_fixed64(stream, NULL, &value)) 51 | return false; 52 | 53 | printf((char*)arg, value); 54 | return true; 55 | } 56 | 57 | int main() 58 | { 59 | uint8_t buffer[1024]; 60 | size_t length = fread(buffer, 1, 1024, stdin); 61 | pb_istream_t stream = pb_istream_from_buffer(buffer, length); 62 | 63 | /* Note: empty initializer list initializes the struct with all-0. 64 | * This is recommended so that unused callbacks are set to NULL instead 65 | * of crashing at runtime. 66 | */ 67 | TestMessage testmessage = {}; 68 | 69 | testmessage.submsg.stringvalue.funcs.decode = &print_string; 70 | testmessage.submsg.stringvalue.arg = "submsg {\n stringvalue: \"%s\"\n"; 71 | testmessage.submsg.int32value.funcs.decode = &print_int32; 72 | testmessage.submsg.int32value.arg = " int32value: %d\n"; 73 | testmessage.submsg.fixed32value.funcs.decode = &print_fixed32; 74 | testmessage.submsg.fixed32value.arg = " fixed32value: %d\n"; 75 | testmessage.submsg.fixed64value.funcs.decode = &print_fixed64; 76 | testmessage.submsg.fixed64value.arg = " fixed64value: %lld\n}\n"; 77 | 78 | testmessage.stringvalue.funcs.decode = &print_string; 79 | testmessage.stringvalue.arg = "stringvalue: \"%s\"\n"; 80 | testmessage.int32value.funcs.decode = &print_int32; 81 | testmessage.int32value.arg = "int32value: %d\n"; 82 | testmessage.fixed32value.funcs.decode = &print_fixed32; 83 | testmessage.fixed32value.arg = "fixed32value: %d\n"; 84 | testmessage.fixed64value.funcs.decode = &print_fixed64; 85 | testmessage.fixed64value.arg = "fixed64value: %lld\n"; 86 | 87 | if (!pb_decode(&stream, TestMessage_fields, &testmessage)) 88 | return 1; 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /tests/test_encode1.c: -------------------------------------------------------------------------------- 1 | /* A very simple encoding test case using person.proto. 2 | * Just puts constant data in the fields and writes the 3 | * data to stdout. 4 | */ 5 | 6 | #include 7 | #include 8 | #include "person.pb.h" 9 | 10 | /* This binds the pb_ostream_t into the stdout stream */ 11 | bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count) 12 | { 13 | FILE *file = (FILE*) stream->state; 14 | return fwrite(buf, 1, count, file) == count; 15 | } 16 | 17 | int main() 18 | { 19 | /* Initialize the structure with constants */ 20 | Person person = {"Test Person 99", 99, true, "test@person.com", 21 | 1, {{"555-12345678", true, Person_PhoneType_MOBILE}}}; 22 | 23 | /* Prepare the stream, output goes directly to stdout */ 24 | pb_ostream_t stream = {&streamcallback, stdout, SIZE_MAX, 0}; 25 | 26 | /* Now encode it and check if we succeeded. */ 27 | if (pb_encode(&stream, Person_fields, &person)) 28 | return 0; /* Success */ 29 | else 30 | return 1; /* Failure */ 31 | } 32 | -------------------------------------------------------------------------------- /tests/test_encode_callbacks.c: -------------------------------------------------------------------------------- 1 | /* Encoding testcase for callback fields */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "callbacks.pb.h" 7 | 8 | bool encode_string(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 9 | { 10 | char *str = "Hello world!"; 11 | 12 | if (!pb_encode_tag_for_field(stream, field)) 13 | return false; 14 | 15 | return pb_encode_string(stream, (uint8_t*)str, strlen(str)); 16 | } 17 | 18 | bool encode_int32(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 19 | { 20 | if (!pb_encode_tag_for_field(stream, field)) 21 | return false; 22 | 23 | return pb_encode_varint(stream, 42); 24 | } 25 | 26 | bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 27 | { 28 | if (!pb_encode_tag_for_field(stream, field)) 29 | return false; 30 | 31 | uint32_t value = 42; 32 | return pb_enc_fixed32(stream, field, &value); 33 | } 34 | 35 | bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *arg) 36 | { 37 | if (!pb_encode_tag_for_field(stream, field)) 38 | return false; 39 | 40 | uint64_t value = 42; 41 | return pb_enc_fixed64(stream, field, &value); 42 | } 43 | 44 | int main() 45 | { 46 | uint8_t buffer[1024]; 47 | pb_ostream_t stream = pb_ostream_from_buffer(buffer, 1024); 48 | TestMessage testmessage = {}; 49 | 50 | testmessage.stringvalue.funcs.encode = &encode_string; 51 | testmessage.int32value.funcs.encode = &encode_int32; 52 | testmessage.fixed32value.funcs.encode = &encode_fixed32; 53 | testmessage.fixed64value.funcs.encode = &encode_fixed64; 54 | 55 | testmessage.has_submsg = true; 56 | testmessage.submsg.stringvalue.funcs.encode = &encode_string; 57 | testmessage.submsg.int32value.funcs.encode = &encode_int32; 58 | testmessage.submsg.fixed32value.funcs.encode = &encode_fixed32; 59 | testmessage.submsg.fixed64value.funcs.encode = &encode_fixed64; 60 | 61 | if (!pb_encode(&stream, TestMessage_fields, &testmessage)) 62 | return 1; 63 | 64 | if (fwrite(buffer, stream.bytes_written, 1, stdout) != 1) 65 | return 2; 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /tests/unittestproto.proto: -------------------------------------------------------------------------------- 1 | import 'pb/pb.proto'; 2 | 3 | message IntegerArray { 4 | repeated int32 data = 1 [(pb).max_count = 10]; 5 | } 6 | 7 | message FloatArray { 8 | repeated float data = 1 [(pb).max_count = 10]; 9 | } 10 | 11 | message CallbackArray { 12 | // We cheat a bit and use this message for testing other types, too. 13 | // Nanopb does not care about the actual defined data type for callback 14 | // fields. 15 | repeated int32 data = 1; 16 | } 17 | 18 | message IntegerContainer { 19 | required IntegerArray submsg = 1; 20 | } 21 | 22 | message CallbackContainer { 23 | required CallbackArray submsg = 1; 24 | } 25 | 26 | message CallbackContainerContainer { 27 | required CallbackContainer submsg = 1; 28 | } 29 | -------------------------------------------------------------------------------- /tests/unittests.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define COMMENT(x) printf("\n----" x "----\n"); 4 | #define STR(x) #x 5 | #define STR2(x) STR(x) 6 | #define TEST(x) \ 7 | if (!(x)) { \ 8 | fprintf(stderr, "\033[31;1mFAILED:\033[22;39m " __FILE__ ":" STR2(__LINE__) " " #x "\n"); \ 9 | status = 1; \ 10 | } else { \ 11 | printf("\033[32;1mOK:\033[22;39m " #x "\n"); \ 12 | } 13 | 14 | 15 | --------------------------------------------------------------------------------