Compare commits
6 Commits
code_reduc
...
nanopb-0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5af2c97ecd | ||
|
|
feddc0115c | ||
|
|
f9501ca185 | ||
|
|
b943290886 | ||
|
|
d1ca88d20e | ||
|
|
9fbe9a5de3 |
@@ -149,17 +149,13 @@ Encodes the contents of a structure as a protocol buffers message and writes it
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
pb_encode_varint
|
.. sidebar:: Encoding fields manually
|
||||||
----------------
|
|
||||||
Encodes an unsigned integer in the varint_ format. ::
|
|
||||||
|
|
||||||
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
|
The functions with names *pb_encode_\** are used when dealing with callback fields. The typical reason for using callbacks is to have an array of unlimited size. In that case, `pb_encode`_ will call your callback function, which in turn will call *pb_encode_\** functions repeatedly to write out values.
|
||||||
|
|
||||||
:stream: Output stream to write to. 1-10 bytes will be written.
|
The tag of a field must be encoded separately with `pb_encode_tag_for_field`_. After that, you can call exactly one of the content-writing functions to encode the payload of the field. For repeated fields, you can repeat this process multiple times.
|
||||||
:value: Value to encode.
|
|
||||||
:returns: True on success, false on IO error.
|
|
||||||
|
|
||||||
.. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints
|
Writing packed arrays is a little bit more involved: you need to use `pb_encode_tag` and specify `PB_WT_STRING` as the wire type. Then you need to know exactly how much data you are going to write, and use `pb_encode_varint`_ to write out the number of bytes before writing the actual data. Substreams can be used to determine the number of bytes beforehand; see `pb_encode_submessage`_ source code for an example.
|
||||||
|
|
||||||
pb_encode_tag
|
pb_encode_tag
|
||||||
-------------
|
-------------
|
||||||
@@ -169,7 +165,7 @@ Starts a field in the Protocol Buffers binary format: encodes the field number a
|
|||||||
|
|
||||||
:stream: Output stream to write to. 1-5 bytes will be written.
|
:stream: Output stream to write to. 1-5 bytes will be written.
|
||||||
:wiretype: PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT
|
:wiretype: PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT
|
||||||
:field_number: Identifier for the field, defined in the .proto file.
|
:field_number: Identifier for the field, defined in the .proto file. You can get it from field->tag.
|
||||||
:returns: True on success, false on IO error.
|
:returns: True on success, false on IO error.
|
||||||
|
|
||||||
pb_encode_tag_for_field
|
pb_encode_tag_for_field
|
||||||
@@ -195,107 +191,71 @@ STRING, BYTES, SUBMESSAGE PB_WT_STRING
|
|||||||
FIXED32 PB_WT_32BIT
|
FIXED32 PB_WT_32BIT
|
||||||
========================= ============
|
========================= ============
|
||||||
|
|
||||||
|
pb_encode_varint
|
||||||
|
----------------
|
||||||
|
Encodes a signed or unsigned integer in the varint_ format. Works for fields of type `bool`, `enum`, `int32`, `int64`, `uint32` and `uint64`::
|
||||||
|
|
||||||
|
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
|
||||||
|
|
||||||
|
:stream: Output stream to write to. 1-10 bytes will be written.
|
||||||
|
:value: Value to encode. Just cast e.g. int32_t directly to uint64_t.
|
||||||
|
:returns: True on success, false on IO error.
|
||||||
|
|
||||||
|
.. _varint: http://code.google.com/apis/protocolbuffers/docs/encoding.html#varints
|
||||||
|
|
||||||
|
pb_encode_svarint
|
||||||
|
-----------------
|
||||||
|
Encodes a signed integer in the 'zig-zagged' format. Works for fields of type `sint32` and `sint64`::
|
||||||
|
|
||||||
|
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
|
||||||
|
|
||||||
|
(parameters are the same as for `pb_encode_varint`_
|
||||||
|
|
||||||
pb_encode_string
|
pb_encode_string
|
||||||
----------------
|
----------------
|
||||||
Writes the length of a string as varint and then contents of the string. Used for writing fields with wire type PB_WT_STRING. ::
|
Writes the length of a string as varint and then contents of the string. Works for fields of type `bytes` and `string`::
|
||||||
|
|
||||||
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
|
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
:stream: Output stream to write to.
|
:stream: Output stream to write to.
|
||||||
:buffer: Pointer to string data.
|
:buffer: Pointer to string data.
|
||||||
:size: Number of bytes in the string.
|
:size: Number of bytes in the string. Pass `strlen(s)` for strings.
|
||||||
:returns: True on success, false on IO error.
|
:returns: True on success, false on IO error.
|
||||||
|
|
||||||
.. sidebar:: Field encoders
|
pb_encode_fixed32
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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*.
|
|
||||||
|
|
||||||
Each field encoder only encodes the contents of the field. The tag must be encoded separately with `pb_encode_tag_for_field`_.
|
|
||||||
|
|
||||||
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*.
|
|
||||||
|
|
||||||
pb_enc_varint
|
|
||||||
-------------
|
|
||||||
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`_. ::
|
|
||||||
|
|
||||||
bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
|
||||||
|
|
||||||
:stream: Output stream to write to.
|
|
||||||
:field: Field description structure. Only *data_size* matters.
|
|
||||||
:src: Pointer to start of the field data.
|
|
||||||
:returns: True on success, false on IO error.
|
|
||||||
|
|
||||||
pb_enc_svarint
|
|
||||||
--------------
|
|
||||||
Field encoder for PB_LTYPE_SVARINT. Similar to `pb_enc_varint`_, except first zig-zag encodes the value for more efficient negative number encoding. ::
|
|
||||||
|
|
||||||
bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
|
||||||
|
|
||||||
(parameters are the same as for `pb_enc_varint`_)
|
|
||||||
|
|
||||||
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)*.
|
|
||||||
|
|
||||||
pb_enc_fixed32
|
|
||||||
--------------
|
|
||||||
Field encoder for PB_LTYPE_FIXED32. Writes the data in little endian order. On big endian computers, reverses the order of bytes. ::
|
|
||||||
|
|
||||||
bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
|
||||||
|
|
||||||
:stream: Output stream to write to.
|
|
||||||
:field: Not used.
|
|
||||||
:src: Pointer to start of the field data.
|
|
||||||
:returns: True on success, false on IO error.
|
|
||||||
|
|
||||||
pb_enc_fixed64
|
|
||||||
--------------
|
|
||||||
Field encoder for PB_LTYPE_FIXED64. Writes the data in little endian order. On big endian computers, reverses the order of bytes. ::
|
|
||||||
|
|
||||||
bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
|
||||||
|
|
||||||
(parameters are the same as for `pb_enc_fixed32`_)
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
pb_enc_bytes
|
|
||||||
------------
|
|
||||||
Field encoder for PB_LTYPE_BYTES. Just calls `pb_encode_string`_. ::
|
|
||||||
|
|
||||||
bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
|
||||||
|
|
||||||
:stream: Output stream to write to.
|
|
||||||
:field: Not used.
|
|
||||||
:src: Pointer to a structure similar to pb_bytes_array_t.
|
|
||||||
:returns: True on success, false on IO error.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
pb_enc_string
|
|
||||||
-------------
|
|
||||||
Field encoder for PB_LTYPE_STRING. Determines size of string with strlen() and then calls `pb_encode_string`_. ::
|
|
||||||
|
|
||||||
bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
|
||||||
|
|
||||||
:stream: Output stream to write to.
|
|
||||||
:field: Not used.
|
|
||||||
:src: Pointer to a null-terminated string.
|
|
||||||
:returns: True on success, false on IO error.
|
|
||||||
|
|
||||||
pb_enc_submessage
|
|
||||||
-----------------
|
-----------------
|
||||||
Field encoder for PB_LTYPE_SUBMESSAGE. Calls `pb_encode`_ to perform the actual encoding. ::
|
Writes 4 bytes to stream and swaps bytes on big-endian architectures. Works for fields of type `fixed32`, `sfixed32` and `float`::
|
||||||
|
|
||||||
bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
:stream: Output stream to write to.
|
||||||
|
:value: Pointer to a 4-bytes large C variable, for example `uint32_t foo;`.
|
||||||
|
:returns: True on success, false on IO error.
|
||||||
|
|
||||||
|
pb_encode_fixed64
|
||||||
|
-----------------
|
||||||
|
Writes 8 bytes to stream and swaps bytes on big-endian architecture. Works for fields of type `fixed64`, `sfixed64` and `double`::
|
||||||
|
|
||||||
|
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
:stream: Output stream to write to.
|
||||||
|
:value: Pointer to a 8-bytes large C variable, for example `uint64_t foo;`.
|
||||||
|
:returns: True on success, false on IO error.
|
||||||
|
|
||||||
|
pb_encode_submessage
|
||||||
|
--------------------
|
||||||
|
Encodes a submessage field, including the size header for it. Works for fields of any message type::
|
||||||
|
|
||||||
|
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
:stream: Output stream to write to.
|
:stream: Output stream to write to.
|
||||||
:field: Field description structure. The *ptr* field must be a pointer to a field description array for the submessage.
|
:fields: Pointer to the autogenerated field description array for the submessage type, e.g. `MyMessage_fields`.
|
||||||
:src: Pointer to the structure where submessage data is.
|
:src: Pointer to the structure where submessage data is.
|
||||||
:returns: True on success, false on IO errors, pb_encode errors or if submessage size changes between calls.
|
:returns: True on success, false on IO errors, pb_encode errors or if submessage size changes between calls.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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 garbage will be written to the output before the problem is detected.
|
||||||
|
|
||||||
pb_decode.h
|
pb_decode.h
|
||||||
===========
|
===========
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ bool printfile_callback(pb_istream_t *stream, const pb_field_t *field, void *arg
|
|||||||
if (!pb_decode(stream, FileInfo_fields, &fileinfo))
|
if (!pb_decode(stream, FileInfo_fields, &fileinfo))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
printf("%-10lld %s\n", fileinfo.inode, fileinfo.name);
|
printf("%-10lld %s\n", (long long)fileinfo.inode, fileinfo.name);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
||||||
{
|
{
|
||||||
int fd = (int)stream->state;
|
int fd = (intptr_t)stream->state;
|
||||||
return send(fd, buf, count, 0) == count;
|
return send(fd, buf, count, 0) == count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
|
static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
|
||||||
{
|
{
|
||||||
int fd = (int)stream->state;
|
int fd = (intptr_t)stream->state;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
@@ -38,12 +38,12 @@ static bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count)
|
|||||||
|
|
||||||
pb_ostream_t pb_ostream_from_socket(int fd)
|
pb_ostream_t pb_ostream_from_socket(int fd)
|
||||||
{
|
{
|
||||||
pb_ostream_t stream = {&write_callback, (void*)fd, SIZE_MAX, 0};
|
pb_ostream_t stream = {&write_callback, (void*)(intptr_t)fd, SIZE_MAX, 0};
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
pb_istream_t pb_istream_from_socket(int fd)
|
pb_istream_t pb_istream_from_socket(int fd)
|
||||||
{
|
{
|
||||||
pb_istream_t stream = {&read_callback, (void*)fd, SIZE_MAX};
|
pb_istream_t stream = {&read_callback, (void*)(intptr_t)fd, SIZE_MAX};
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, const void
|
|||||||
if (!pb_encode_tag_for_field(stream, field))
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!pb_enc_submessage(stream, field, &fileinfo))
|
if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,27 @@
|
|||||||
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
|
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
|
||||||
|
|
||||||
import google.protobuf.descriptor_pb2 as descriptor
|
try:
|
||||||
import nanopb_pb2
|
import google.protobuf.descriptor_pb2 as descriptor
|
||||||
|
except:
|
||||||
|
print
|
||||||
|
print "*************************************************************"
|
||||||
|
print "*** Could not import the Google protobuf Python libraries ***"
|
||||||
|
print "*** Try installing package 'python-protobuf' or similar. ***"
|
||||||
|
print "*************************************************************"
|
||||||
|
print
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
import nanopb_pb2
|
||||||
|
except:
|
||||||
|
print
|
||||||
|
print "***************************************************************"
|
||||||
|
print "*** Could not import the precompiled nanopb_pb2.py. ***"
|
||||||
|
print "*** Run 'make' in the 'generator' folder to update the file.***"
|
||||||
|
print "***************************************************************"
|
||||||
|
print
|
||||||
|
raise
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
# Values are tuple (c type, pb ltype)
|
# Values are tuple (c type, pb ltype)
|
||||||
@@ -422,7 +442,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# List of .proto files that should not be included in the C header file
|
# List of .proto files that should not be included in the C header file
|
||||||
# even if they are mentioned in the source .proto.
|
# even if they are mentioned in the source .proto.
|
||||||
excludes = ['nanopb.proto']
|
excludes = ['nanopb.proto', 'google/protobuf/descriptor.proto']
|
||||||
dependencies = [d for d in fdesc.file[0].dependency if d not in excludes]
|
dependencies = [d for d in fdesc.file[0].dependency if d not in excludes]
|
||||||
|
|
||||||
header = open(headername, 'w')
|
header = open(headername, 'w')
|
||||||
|
|||||||
5
pb.h
5
pb.h
@@ -17,6 +17,11 @@
|
|||||||
#define pb_packed
|
#define pb_packed
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
|
||||||
|
#ifndef UNUSED
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* List of possible field types. These are used in the autogenerated code.
|
/* List of possible field types. These are used in the autogenerated code.
|
||||||
* Least-significant 4 bits tell the scalar type
|
* Least-significant 4 bits tell the scalar type
|
||||||
* Most-significant 4 bits specify repeated/required/packed etc.
|
* Most-significant 4 bits specify repeated/required/packed etc.
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
|
|||||||
{
|
{
|
||||||
uint64_t temp;
|
uint64_t temp;
|
||||||
bool status = pb_decode_varint(stream, &temp);
|
bool status = pb_decode_varint(stream, &temp);
|
||||||
*dest = temp;
|
*dest = (uint32_t)temp;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +448,7 @@ static void endian_copy(void *dest, void *src, size_t destsize, size_t srcsize)
|
|||||||
#ifdef __BIG_ENDIAN__
|
#ifdef __BIG_ENDIAN__
|
||||||
memcpy(dest, (char*)src + (srcsize - destsize), destsize);
|
memcpy(dest, (char*)src + (srcsize - destsize), destsize);
|
||||||
#else
|
#else
|
||||||
|
UNUSED(srcsize);
|
||||||
memcpy(dest, src, destsize);
|
memcpy(dest, src, destsize);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -480,6 +481,7 @@ bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, v
|
|||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
#else
|
#else
|
||||||
|
UNUSED(field);
|
||||||
return pb_read(stream, (uint8_t*)dest, 4);
|
return pb_read(stream, (uint8_t*)dest, 4);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -496,6 +498,7 @@ bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, v
|
|||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
#else
|
#else
|
||||||
|
UNUSED(field);
|
||||||
return pb_read(stream, (uint8_t*)dest, 8);
|
return pb_read(stream, (uint8_t*)dest, 8);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -524,7 +527,7 @@ bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, vo
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Check length, noting the null terminator */
|
/* Check length, noting the null terminator */
|
||||||
if (size > field->data_size - 1)
|
if (size + 1 > field->data_size)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
status = pb_read(stream, (uint8_t*)dest, size);
|
status = pb_read(stream, (uint8_t*)dest, size);
|
||||||
|
|||||||
192
pb_encode.c
192
pb_encode.c
@@ -3,6 +3,7 @@
|
|||||||
* 2011 Petteri Aimonen <jpa@kapsi.fi>
|
* 2011 Petteri Aimonen <jpa@kapsi.fi>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define NANOPB_INTERNALS
|
||||||
#include "pb.h"
|
#include "pb.h"
|
||||||
#include "pb_encode.h"
|
#include "pb_encode.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -75,7 +76,7 @@ bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count
|
|||||||
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
|
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
|
||||||
const void *pData, size_t count, pb_encoder_t func)
|
const void *pData, size_t count, pb_encoder_t func)
|
||||||
{
|
{
|
||||||
int i;
|
size_t i;
|
||||||
const void *p;
|
const void *p;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
@@ -211,7 +212,7 @@ bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
|
|||||||
|
|
||||||
while (value)
|
while (value)
|
||||||
{
|
{
|
||||||
buffer[i] = (value & 0x7F) | 0x80;
|
buffer[i] = (uint8_t)((value & 0x7F) | 0x80);
|
||||||
value >>= 7;
|
value >>= 7;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -220,6 +221,40 @@ bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
|
|||||||
return pb_write(stream, buffer, i);
|
return pb_write(stream, buffer, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
|
||||||
|
{
|
||||||
|
uint64_t zigzagged;
|
||||||
|
if (value < 0)
|
||||||
|
zigzagged = ~(value << 1);
|
||||||
|
else
|
||||||
|
zigzagged = value << 1;
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, zigzagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
|
||||||
|
{
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
uint8_t *bytes = value;
|
||||||
|
uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||||
|
return pb_write(stream, lebytes, 4);
|
||||||
|
#else
|
||||||
|
return pb_write(stream, (uint8_t*)value, 4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
|
||||||
|
{
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
uint8_t *bytes[8] = value;
|
||||||
|
uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4],
|
||||||
|
bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||||
|
return pb_write(stream, lebytes, 8);
|
||||||
|
#else
|
||||||
|
return pb_write(stream, (uint8_t*)value, 8);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number)
|
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number)
|
||||||
{
|
{
|
||||||
int tag = wiretype | (field_number << 3);
|
int tag = wiretype | (field_number << 3);
|
||||||
@@ -265,94 +300,14 @@ bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, s
|
|||||||
return pb_write(stream, buffer, size);
|
return pb_write(stream, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Field encoders */
|
bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
|
||||||
|
|
||||||
/* Copy srcsize bytes from src so that values are casted properly.
|
|
||||||
* On little endian machine, copy to start of dest
|
|
||||||
* On big endian machine, copy to end of dest
|
|
||||||
* destsize must always be larger than srcsize
|
|
||||||
*
|
|
||||||
* Note: This is the reverse of the endian_copy in pb_decode.c.
|
|
||||||
*/
|
|
||||||
static void endian_copy(void *dest, const void *src, size_t destsize, size_t srcsize)
|
|
||||||
{
|
|
||||||
#ifdef __BIG_ENDIAN__
|
|
||||||
memcpy((char*)dest + (destsize - srcsize), src, srcsize);
|
|
||||||
#else
|
|
||||||
memcpy(dest, src, srcsize);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
|
||||||
uint64_t value = 0;
|
|
||||||
endian_copy(&value, src, sizeof(value), field->data_size);
|
|
||||||
return pb_encode_varint(stream, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
|
||||||
uint64_t value = 0;
|
|
||||||
uint64_t zigzagged;
|
|
||||||
uint64_t signbitmask, xormask;
|
|
||||||
endian_copy(&value, src, sizeof(value), field->data_size);
|
|
||||||
|
|
||||||
signbitmask = (uint64_t)0x80 << (field->data_size * 8 - 8);
|
|
||||||
xormask = ((uint64_t)-1) >> (64 - field->data_size * 8);
|
|
||||||
if (value & signbitmask)
|
|
||||||
zigzagged = ((value ^ xormask) << 1) | 1;
|
|
||||||
else
|
|
||||||
zigzagged = value << 1;
|
|
||||||
|
|
||||||
return pb_encode_varint(stream, zigzagged);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
|
||||||
#ifdef __BIG_ENDIAN__
|
|
||||||
uint8_t bytes[8] = {0};
|
|
||||||
memcpy(bytes, src, 8);
|
|
||||||
uint8_t lebytes[8] = {bytes[7], bytes[6], bytes[5], bytes[4],
|
|
||||||
bytes[3], bytes[2], bytes[1], bytes[0]};
|
|
||||||
return pb_write(stream, lebytes, 8);
|
|
||||||
#else
|
|
||||||
return pb_write(stream, (uint8_t*)src, 8);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
|
||||||
#ifdef __BIG_ENDIAN__
|
|
||||||
uint8_t bytes[4] = {0};
|
|
||||||
memcpy(bytes, src, 4);
|
|
||||||
uint8_t lebytes[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
|
|
||||||
return pb_write(stream, lebytes, 4);
|
|
||||||
#else
|
|
||||||
return pb_write(stream, (uint8_t*)src, 4);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
|
||||||
pb_bytes_array_t *bytes = (pb_bytes_array_t*)src;
|
|
||||||
return pb_encode_string(stream, bytes->bytes, bytes->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
|
||||||
return pb_encode_string(stream, (uint8_t*)src, strlen((char*)src));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
|
||||||
{
|
{
|
||||||
|
/* First calculate the message size using a non-writing substream. */
|
||||||
pb_ostream_t substream = {0};
|
pb_ostream_t substream = {0};
|
||||||
size_t size;
|
size_t size;
|
||||||
bool status;
|
bool status;
|
||||||
|
|
||||||
if (field->ptr == NULL)
|
if (!pb_encode(&substream, fields, src_struct))
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!pb_encode(&substream, (pb_field_t*)field->ptr, src))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size = substream.bytes_written;
|
size = substream.bytes_written;
|
||||||
@@ -373,7 +328,7 @@ bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field
|
|||||||
substream.max_size = size;
|
substream.max_size = size;
|
||||||
substream.bytes_written = 0;
|
substream.bytes_written = 0;
|
||||||
|
|
||||||
status = pb_encode(&substream, (pb_field_t*)field->ptr, src);
|
status = pb_encode(&substream, fields, src_struct);
|
||||||
|
|
||||||
stream->bytes_written += substream.bytes_written;
|
stream->bytes_written += substream.bytes_written;
|
||||||
stream->state = substream.state;
|
stream->state = substream.state;
|
||||||
@@ -384,3 +339,68 @@ bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Field encoders */
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
switch (field->data_size)
|
||||||
|
{
|
||||||
|
case 1: value = *(uint8_t*)src; break;
|
||||||
|
case 2: value = *(uint16_t*)src; break;
|
||||||
|
case 4: value = *(uint32_t*)src; break;
|
||||||
|
case 8: value = *(uint64_t*)src; break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
switch (field->data_size)
|
||||||
|
{
|
||||||
|
case 4: value = *(int32_t*)src; break;
|
||||||
|
case 8: value = *(int64_t*)src; break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_svarint(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
UNUSED(field);
|
||||||
|
return pb_encode_fixed64(stream, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
UNUSED(field);
|
||||||
|
return pb_encode_fixed32(stream, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
pb_bytes_array_t *bytes = (pb_bytes_array_t*)src;
|
||||||
|
UNUSED(field);
|
||||||
|
return pb_encode_string(stream, bytes->bytes, bytes->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
UNUSED(field);
|
||||||
|
return pb_encode_string(stream, (uint8_t*)src, strlen((char*)src));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
if (field->ptr == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return pb_encode_submessage(stream, (pb_field_t*)field->ptr, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
47
pb_encode.h
47
pb_encode.h
@@ -48,25 +48,56 @@ bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_
|
|||||||
* You may want to use these from your caller or callbacks.
|
* You may want to use these from your caller or callbacks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
|
/* Encode field header based on LTYPE and field number defined in the field structure.
|
||||||
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number);
|
* Call this from the callback before writing out field contents. */
|
||||||
/* Encode tag based on LTYPE and field number defined in the field structure. */
|
|
||||||
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
|
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
|
||||||
/* Write length as varint and then the contents of buffer. */
|
|
||||||
|
/* Encode field header by manually specifing wire type. You need to use this if
|
||||||
|
* you want to write out packed arrays from a callback field. */
|
||||||
|
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number);
|
||||||
|
|
||||||
|
/* Encode an integer in the varint format.
|
||||||
|
* This works for bool, enum, int32, int64, uint32 and uint64 field types. */
|
||||||
|
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
|
||||||
|
|
||||||
|
/* Encode an integer in the zig-zagged svarint format.
|
||||||
|
* This works for sint32 and sint64. */
|
||||||
|
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
|
||||||
|
|
||||||
|
/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
|
||||||
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
|
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
/* --- Field encoders ---
|
/* Encode a fixed32, sfixed32 or float value.
|
||||||
* Each encoder writes the content for the field.
|
* You need to pass a pointer to a 4-byte wide C variable. */
|
||||||
* The tag/wire type has been written already.
|
bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
/* Encode a fixed64, sfixed64 or double value.
|
||||||
|
* You need to pass a pointer to a 8-byte wide C variable. */
|
||||||
|
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
/* Encode a submessage field.
|
||||||
|
* You need to pass the pb_field_t array and pointer to struct, just like with pb_encode().
|
||||||
|
* This internally encodes the submessage twice, first to calculate message size and then to actually write it out.
|
||||||
|
*/
|
||||||
|
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
|
/* --- Internal functions ---
|
||||||
|
* These functions are not terribly useful for the average library user, but
|
||||||
|
* are exported to make the unit testing and extending nanopb easier.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef NANOPB_INTERNALS
|
||||||
bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
|
||||||
bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This function is not recommended for new programs. Use pb_encode_submessage()
|
||||||
|
* instead, it has the same functionality with a less confusing interface. */
|
||||||
bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
bool pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#define NANOPB_INTERNALS
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "pb_encode.h"
|
#include "pb_encode.h"
|
||||||
@@ -123,7 +125,6 @@ int main()
|
|||||||
uint8_t buffer[30];
|
uint8_t buffer[30];
|
||||||
pb_ostream_t s;
|
pb_ostream_t s;
|
||||||
uint8_t value = 1;
|
uint8_t value = 1;
|
||||||
int8_t svalue = -1;
|
|
||||||
int32_t max = INT32_MAX;
|
int32_t max = INT32_MAX;
|
||||||
int32_t min = INT32_MIN;
|
int32_t min = INT32_MIN;
|
||||||
int64_t lmax = INT64_MAX;
|
int64_t lmax = INT64_MAX;
|
||||||
@@ -132,8 +133,6 @@ int main()
|
|||||||
|
|
||||||
COMMENT("Test pb_enc_varint and pb_enc_svarint")
|
COMMENT("Test pb_enc_varint and pb_enc_svarint")
|
||||||
TEST(WRITES(pb_enc_varint(&s, &field, &value), "\x01"));
|
TEST(WRITES(pb_enc_varint(&s, &field, &value), "\x01"));
|
||||||
TEST(WRITES(pb_enc_svarint(&s, &field, &svalue), "\x01"));
|
|
||||||
TEST(WRITES(pb_enc_svarint(&s, &field, &value), "\x02"));
|
|
||||||
|
|
||||||
field.data_size = sizeof(max);
|
field.data_size = sizeof(max);
|
||||||
TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f"));
|
TEST(WRITES(pb_enc_svarint(&s, &field, &max), "\xfe\xff\xff\xff\x0f"));
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *a
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t value = 42;
|
uint32_t value = 42;
|
||||||
return pb_enc_fixed32(stream, field, &value);
|
return pb_encode_fixed32(stream, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
|
bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
|
||||||
@@ -38,7 +38,7 @@ bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *a
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint64_t value = 42;
|
uint64_t value = 42;
|
||||||
return pb_enc_fixed64(stream, field, &value);
|
return pb_encode_fixed64(stream, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
|
|||||||
Reference in New Issue
Block a user