uorb: compress format definitions

Reduces flash usage by ~16KB.

- compress formats at build-time into a single string with all formats
- then at runtime iteratively decompress using
  https://github.com/atomicobject/heatshrink
This commit is contained in:
Beat Küng
2023-11-08 00:31:26 -05:00
committed by Thomas Stastny
parent 142e44c418
commit 1ad5a9de08
27 changed files with 1736 additions and 252 deletions
+6 -2
View File
@@ -54,6 +54,8 @@ set(SRCS_COMMON
uORBCommon.hpp
uORBCommunicator.hpp
uORBManager.hpp
uORBMessageFields.cpp
uORBMessageFields.hpp
uORBUtils.cpp
uORBUtils.hpp
uORBDeviceMaster.hpp
@@ -76,7 +78,7 @@ if (NOT DEFINED CONFIG_BUILD_FLAT AND "${PX4_PLATFORM}" MATCHES "nuttx")
${SRCS_COMMON}
${SRCS_KERNEL}
)
target_link_libraries(uORB_kernel PRIVATE cdev uorb_msgs nuttx_mm)
target_link_libraries(uORB_kernel PRIVATE cdev uorb_msgs nuttx_mm heatshrink)
target_compile_options(uORB_kernel PRIVATE ${MAX_CUSTOM_OPT_LEVEL} -D__KERNEL__)
# User side library in nuttx kernel/protected build
@@ -102,9 +104,11 @@ else()
target_link_libraries(uORB PRIVATE cdev)
endif()
target_link_libraries(uORB PRIVATE uorb_msgs)
target_link_libraries(uORB PRIVATE uorb_msgs heatshrink)
target_compile_options(uORB PRIVATE ${MAX_CUSTOM_OPT_LEVEL})
if(PX4_TESTING)
add_subdirectory(uORB_tests)
endif()
px4_add_functional_gtest(SRC uORBMessageFieldsTest.cpp LINKLIBS uORB)
+23 -36
View File
@@ -40,6 +40,7 @@
#include "uORBManager.hpp"
#include "uORBCommon.hpp"
#include "uORBMessageFields.hpp"
#include <lib/drivers/device/Device.hpp>
@@ -196,7 +197,7 @@ int orb_get_interval(int handle, unsigned *interval)
const char *orb_get_c_type(unsigned char short_type)
{
// this matches with the uorb o_fields generator
// this matches with the uorb type_map_short python data
switch (short_type) {
case 0x82: return "int8_t";
@@ -239,25 +240,29 @@ void orb_print_message_internal(const orb_metadata *meta, const void *data, bool
const uint8_t *data_ptr = (const uint8_t *)data;
int data_offset = 0;
for (int format_idx = 0; meta->o_fields[format_idx] != 0;) {
const char *end_field = strchr(meta->o_fields + format_idx, ';');
// Find message format
char format_buffer[128];
uORB::MessageFormatReader format_reader(format_buffer, sizeof(format_buffer));
if (!end_field) {
PX4_ERR("Format error in %s", meta->o_fields);
return;
}
if (!format_reader.readUntilFormat(meta->o_id)) {
PX4_ERR("Failed to get uorb format");
return;
}
const char *c_type = orb_get_c_type(meta->o_fields[format_idx]);
const int end_field_idx = end_field - meta->o_fields;
int field_length = 0;
while (format_reader.readNextField(field_length)) {
const char *c_type = orb_get_c_type(format_buffer[0]);
int array_idx = -1;
int field_name_idx = -1;
for (int field_idx = format_idx; field_idx != end_field_idx; ++field_idx) {
if (meta->o_fields[field_idx] == '[') {
for (int field_idx = 0; field_idx < field_length; ++field_idx) {
if (format_buffer[field_idx] == '[') {
array_idx = field_idx + 1;
} else if (meta->o_fields[field_idx] == ' ') {
} else if (format_buffer[field_idx] == ' ') {
field_name_idx = field_idx + 1;
break;
}
@@ -266,19 +271,10 @@ void orb_print_message_internal(const orb_metadata *meta, const void *data, bool
int array_size = 1;
if (array_idx >= 0) {
array_size = strtol(meta->o_fields + array_idx, nullptr, 10);
array_size = strtol(format_buffer + array_idx, nullptr, 10);
}
char field_name[80];
size_t field_name_len = end_field_idx - field_name_idx;
if (field_name_len >= sizeof(field_name)) {
PX4_ERR("field name too long %s (max: %u)", meta->o_fields, (unsigned)sizeof(field_name));
return;
}
memcpy(field_name, meta->o_fields + field_name_idx, field_name_len);
field_name[field_name_len] = '\0';
const char *field_name = format_buffer + field_name_idx;
if (c_type) { // built-in type
bool dont_print = false;
@@ -458,17 +454,10 @@ void orb_print_message_internal(const orb_metadata *meta, const void *data, bool
} else {
// extract the topic name
char topic_name[80];
const size_t topic_name_len = array_size > 1 ? array_idx - format_idx - 1 : field_name_idx - format_idx - 1;
if (topic_name_len >= sizeof(topic_name)) {
PX4_ERR("topic name too long in %s (max: %u)", meta->o_name, (unsigned)sizeof(topic_name));
return;
}
memcpy(topic_name, meta->o_fields + format_idx, topic_name_len);
topic_name[topic_name_len] = '\0';
// Get the topic name
const size_t topic_name_len = array_size > 1 ? array_idx - 1 : field_name_idx - 1;
format_buffer[topic_name_len] = '\0';
const char *topic_name = format_buffer;
// find the metadata
const orb_metadata *const *topics = orb_get_topics();
@@ -499,7 +488,5 @@ void orb_print_message_internal(const orb_metadata *meta, const void *data, bool
data_offset += found_topic->o_size;
}
}
format_idx = end_field_idx + 1;
}
}
+2 -6
View File
@@ -51,7 +51,6 @@ struct orb_metadata {
const char *o_name; /**< unique object name */
const uint16_t o_size; /**< object size */
const uint16_t o_size_no_padding; /**< object size w/o padding at the end (for logger) */
const char *o_fields; /**< semicolon separated list of fields (with type) */
orb_id_size_t o_id; /**< ORB_ID enum */
};
@@ -100,15 +99,13 @@ typedef const struct orb_metadata *orb_id_t;
* @param _name The name of the topic.
* @param _struct The structure the topic provides.
* @param _size_no_padding Struct size w/o padding at the end
* @param _fields All fields in a semicolon separated list e.g: "float[3] position;bool armed"
* @param _orb_id_enum ORB ID enum e.g.: ORB_ID::vehicle_status
*/
#define ORB_DEFINE(_name, _struct, _size_no_padding, _fields, _orb_id_enum) \
#define ORB_DEFINE(_name, _struct, _size_no_padding, _orb_id_enum) \
const struct orb_metadata __orb_##_name = { \
#_name, \
sizeof(_struct), \
_size_no_padding, \
_fields, \
_orb_id_enum \
}; struct hack
@@ -236,7 +233,7 @@ extern int orb_set_interval(int handle, unsigned interval) __EXPORT;
extern int orb_get_interval(int handle, unsigned *interval) __EXPORT;
/**
* Returns the C type string from a short type in o_fields metadata, or nullptr
* Returns the C type string from a short type in message fields metadata, or nullptr
* if not a short type
*/
const char *orb_get_c_type(unsigned char short_type);
@@ -248,7 +245,6 @@ const char *orb_get_c_type(unsigned char short_type);
*/
void orb_print_message_internal(const struct orb_metadata *meta, const void *data, bool print_topic_name);
__END_DECLS
/* Diverse uORB header defines */ //XXX: move to better location
+342
View File
@@ -0,0 +1,342 @@
/****************************************************************************
*
* Copyright (c) 2023 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include "uORBMessageFields.hpp"
#include <px4_platform_common/log.h>
namespace uORB
{
MessageFormatReader::State MessageFormatReader::readMore()
{
if (_state == State::Complete || _state == State::Failure) {
return _state;
}
if (_buffer_length == _buffer_capacity) {
_state = State::Failure;
PX4_ERR("buffer too small");
return _state;
}
const uint8_t *compressed_formats = orb_compressed_message_formats();
const unsigned compressed_formats_size = orb_compressed_message_formats_size();
if (_buffer_length == 0 && _compressed_formats_idx == compressed_formats_size) {
_state = State::Complete;
return _state;
}
const unsigned max_num_iterations = 5; // Safeguard, we're not expected to do more than a few iterations
for (unsigned iteration = 0; iteration < max_num_iterations; ++iteration) {
switch (_state) {
case State::ReadOrbIDs: {
int num_orb_ids = _buffer[0];
const unsigned orb_ids_size = 1 + num_orb_ids * sizeof(orb_id_size_t);
if (_buffer_length > orb_ids_size) {
int num_dependent_orb_ids = _buffer[orb_ids_size];
const unsigned orb_ids_dependent_size = 1 + num_dependent_orb_ids * sizeof(orb_id_size_t);
if (_buffer_length >= orb_ids_size + orb_ids_dependent_size) {
orb_id_size_t orb_id;
_state = State::ReadingFormat;
_format_length = 0;
_orb_ids.clear();
for (int i = 0; i < num_orb_ids; ++i) {
memcpy(&orb_id, &_buffer[1 + sizeof(orb_id_size_t) * i], sizeof(orb_id_size_t));
_orb_ids.push_back(orb_id);
}
_orb_ids_dependencies.clear();
for (int i = 0; i < num_dependent_orb_ids; ++i) {
memcpy(&orb_id, &_buffer[orb_ids_size + 1 + sizeof(orb_id_size_t) * i],
sizeof(orb_id_size_t));
_orb_ids_dependencies.push_back(orb_id);
}
memmove(_buffer, _buffer + orb_ids_size + orb_ids_dependent_size,
_buffer_length - orb_ids_size - orb_ids_dependent_size);
_buffer_length -= orb_ids_size + orb_ids_dependent_size;
return State::ReadOrbIDs;
}
}
if (_buffer_length == _buffer_capacity) {
_state = State::Failure;
PX4_ERR("buffer too small");
return _state;
}
break;
}
case State::ReadingFormat: {
const bool got_new_data = _format_length < _buffer_length;
for (; _format_length < _buffer_length; ++_format_length) {
if (_buffer[_format_length] == '\0') {
_state = State::FormatComplete;
return _state;
}
}
if (got_new_data) {
return _state;
}
}
break;
case State::FormatComplete:
if (_format_length != 0) {
PX4_ERR("Invalid API calls"); // Missing call to clearFormatFromBuffer or clearFormatAndRestoreLeftover
_state = State::Failure;
return _state;
}
_state = State::ReadOrbIDs;
break;
case State::Failure:
case State::Complete:
return _state;
}
// Decompress more data
size_t count = 0;
if (heatshrink_decoder_sink(&_hsd, &compressed_formats[_compressed_formats_idx],
compressed_formats_size - _compressed_formats_idx, &count) < 0) {
_state = State::Failure;
return _state;
}
_compressed_formats_idx += count;
if (_compressed_formats_idx == compressed_formats_size) {
const HSD_finish_res fres = heatshrink_decoder_finish(&_hsd);
if (fres != HSDR_FINISH_MORE && fres != HSDR_FINISH_DONE) {
_state = State::Failure;
return _state;
}
}
const HSD_poll_res pres = heatshrink_decoder_poll(&_hsd, reinterpret_cast<uint8_t *>(&_buffer[_buffer_length]),
_buffer_capacity - _buffer_length, &count);
_buffer_length += count;
if (HSDR_POLL_EMPTY != pres && HSDR_POLL_MORE != pres) {
_state = State::Failure;
return _state;
}
if (_compressed_formats_idx == compressed_formats_size) {
const HSD_finish_res fres = heatshrink_decoder_finish(&_hsd);
if (HSDR_FINISH_DONE != fres && HSDR_FINISH_MORE != fres) {
_state = State::Failure;
return _state;
}
}
}
// Not expected to get here
PX4_ERR("logic error");
_state = State::Failure;
return _state;
}
void MessageFormatReader::clearFormatFromBuffer()
{
if (_state == State::FormatComplete) {
++_format_length; // Include null char
memmove(_buffer, _buffer + _format_length, _buffer_length - _format_length);
_buffer_length -= _format_length;
} else {
// Full buffer is occupied with format
_buffer_length = 0;
}
_format_length = 0;
}
int MessageFormatReader::expandMessageFormat(char *format, unsigned len, unsigned buf_len)
{
++len; // Include null char
int format_idx = 0;
while (format[format_idx] != 0) {
const char *c_type = orb_get_c_type(format[format_idx]);
if (c_type) {
// Replace 1 char type with expanded c_type
const int c_type_len = (int)strlen(c_type);
if (len + c_type_len - 1 > buf_len) {
return -1;
}
memmove(format + format_idx + c_type_len, format + format_idx + 1, len - format_idx - 1);
memcpy(format + format_idx, c_type, c_type_len);
format_idx += c_type_len - 1;
len += c_type_len - 1;
}
// Go to next field
const char *end_field = strchr(format + format_idx, ';');
if (!end_field) {
PX4_ERR("Format error in %s", format);
return -1;
}
format_idx = (int)(end_field - format + 1);
}
if (format_idx + 1 != (int)len) {
PX4_ERR("logic error");
return -1;
}
return format_idx;
}
bool MessageFormatReader::readUntilFormat(orb_id_size_t orb_id)
{
bool done = false;
bool found_format = false;
while (!done && !found_format) {
switch (readMore()) {
case State::ReadOrbIDs:
for (const orb_id_size_t current_orb_id : orbIDs()) {
if (current_orb_id == orb_id) {
found_format = true;
}
}
break;
case State::ReadingFormat:
case State::FormatComplete:
clearFormatFromBuffer();
break;
case State::Complete:
case State::Failure:
done = true;
break;
default:
break;
}
}
return found_format;
}
bool MessageFormatReader::readNextField(int &field_length)
{
if (field_length > 0) {
// Move left-over part to beginning
++field_length; // include null
memmove(_buffer, _buffer + field_length, _buffer_length - field_length);
_buffer_length -= field_length;
_format_length -= field_length;
}
auto findFieldEnd = [&]() {
// Find ';'
bool found = false;
for (field_length = 0; field_length < (int)_format_length; ++field_length) {
if (_buffer[field_length] == ';') {
_buffer[field_length] = '\0';
found = true;
break;
}
}
return found;
};
// We might still have a field in the buffer
if (findFieldEnd()) {
return true;
}
bool done = false;
bool ret = false;
while (!done) {
switch (readMore()) {
case State::ReadingFormat:
if (findFieldEnd()) {
ret = true;
done = true;
}
break;
case State::FormatComplete: {
ret = findFieldEnd(); // Expected to return true here
done = true;
break;
}
case State::ReadOrbIDs: // Arrived at the next format -> we're done
case State::Complete:
case State::Failure:
done = true;
break;
}
}
return ret;
}
} // namespace uORB
+157
View File
@@ -0,0 +1,157 @@
/****************************************************************************
*
* Copyright (c) 2023 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#pragma once
#include <uORB/topics/uORBMessageFieldsGenerated.hpp>
#include <uORB/uORB.h>
#include <containers/Array.hpp>
#include <cstring>
#define HEATSHRINK_DYNAMIC_ALLOC 0
#include <lib/heatshrink/heatshrink/heatshrink_decoder.h>
namespace uORB
{
class MessageFormatReader
{
public:
enum class State {
ReadOrbIDs,
ReadingFormat,
FormatComplete,
Failure,
Complete
};
MessageFormatReader(char *buffer, unsigned buffer_capacity)
: _buffer(buffer), _buffer_capacity(buffer_capacity)
{
heatshrink_decoder_reset(&_hsd);
static_assert(orb_compressed_heatshrink_window_length == HEATSHRINK_STATIC_WINDOW_BITS, "window length mismatch");
static_assert(orb_compressed_heatshrink_lookahead_length == HEATSHRINK_STATIC_LOOKAHEAD_BITS,
"lookahead length mismatch");
_buffer[0] = 0;
}
/**
* Read and decompress more data into the given buffer (from the constructor).
* Call iteratively until completed or a failure happens.
* @return current state
*/
State readMore();
/**
* Read until the start of a format given an ORB ID
* @return true on success
*/
bool readUntilFormat(orb_id_size_t orb_id);
/**
* Iteratively read fields for the current format
* @param field_length [in,out] field length, set to 0 initially
* @return true while there is a field
*/
bool readNextField(int &field_length);
/**
* Current length of the buffer
*/
uint32_t bufferLength() const { return _buffer_length; }
/**
* Clear the buffer during ReadingFormat (if it does not need to be accumulated) or FormatComplete.
* After FormatComplete either this or clearFormatAndRestoreLeftover must be called.
*/
void clearFormatFromBuffer();
/**
* When FormatComplete, this can be called to move the remaining part after the format to the end of the buffer,
* allowing the buffer to be modified.
* @return length of the left-over part.
*/
unsigned moveLeftoverToBufferEnd()
{
_buffer_length -= _format_length + 1;
memmove(_buffer + _buffer_capacity - _buffer_length, _buffer + _format_length + 1, _buffer_length);
return _buffer_length;
}
/**
* After calling moveLeftoverToBufferEnd(), this must be called.
*/
void clearFormatAndRestoreLeftover()
{
memmove(_buffer, _buffer + _buffer_capacity - _buffer_length, _buffer_length);
_format_length = 0;
}
/**
* Get the (partial if ReadingFormat or complete if FormatComplete) format length in the buffer
*/
unsigned formatLength() const { return _format_length; }
/**
* In ReadOrbIDs, ReadingFormat or FormatComplete states, this returns the orb ID's accociated with the format.
*/
const px4::Array<orb_id_size_t, orb_compressed_max_num_orb_ids> &orbIDs() const { return _orb_ids; }
/**
* In ReadOrbIDs, ReadingFormat or FormatComplete states, this returns the dependent orb ID's accociated with the
* format (for nested format definitions).
*/
const px4::Array<orb_id_size_t, orb_compressed_max_num_orb_id_dependencies> &orbIDsDependencies() const { return _orb_ids_dependencies; }
/**
* Expand a tokenized format (after decompressing it)
* @param format tokenized format, expanded in-place
* @param len Length of the format, format[len] == '\0' must hold
* @param buf_len total length of format. Must be long enough for expanded format.
* @return expanded format length, or <0 on error
*/
static int expandMessageFormat(char *format, unsigned len, unsigned buf_len);
private:
State _state{State::ReadOrbIDs};
px4::Array<orb_id_size_t, orb_compressed_max_num_orb_ids> _orb_ids;
px4::Array<orb_id_size_t, orb_compressed_max_num_orb_id_dependencies> _orb_ids_dependencies;
unsigned _compressed_formats_idx{0};
char *_buffer{nullptr};
const unsigned _buffer_capacity;
uint32_t _buffer_length{0};
unsigned _format_length{0};
heatshrink_decoder _hsd;
};
} // namespace uORB
@@ -0,0 +1,234 @@
/****************************************************************************
*
* Copyright (C) 2023 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include "uORBMessageFields.hpp"
#include <gtest/gtest.h>
#include <containers/Bitset.hpp>
#include <px4_platform_common/param.h>
#include <uORB/topics/uORBTopics.hpp>
#include <string>
// To run: make tests TESTFILTER=uORBMessageFields
class uORBMessageFieldsTest : public ::testing::Test
{
public:
void SetUp() override
{
param_control_autosave(false);
}
};
TEST_F(uORBMessageFieldsTest, decompressed_formats_match)
{
char buffer[1500];
uORB::MessageFormatReader format_reader(buffer, sizeof(buffer));
px4::Bitset<ORB_TOPICS_COUNT> formats_found;
px4::Bitset<ORB_TOPICS_COUNT> dependencies;
static const char *all_formats[] = ORB_DECOMPRESSED_MESSAGE_FIELDS;
int format_idx = 0;
bool done = false;
while (!done) {
switch (format_reader.readMore()) {
case uORB::MessageFormatReader::State::FormatComplete: {
const unsigned format_length = format_reader.formatLength();
EXPECT_GT(format_length, 0);
// Move the left-over (the part after the format if any) to the end of the buffer
const unsigned leftover_length = format_reader.moveLeftoverToBufferEnd();
for (const orb_id_size_t orb_id : format_reader.orbIDs()) {
// Ensure each orb_id is set only once
EXPECT_FALSE(formats_found[orb_id]);
formats_found.set(orb_id);
dependencies.set(orb_id, false); // Clear dependency
}
for (const orb_id_size_t orb_id : format_reader.orbIDsDependencies()) {
dependencies.set(orb_id);
}
// Compare format
ASSERT_LT(format_idx, sizeof(all_formats) / sizeof(all_formats[0]));
const std::string format(buffer, format_length);
const std::string format_expected(all_formats[format_idx]);
EXPECT_EQ(format, format_expected);
const int ret = uORB::MessageFormatReader::expandMessageFormat(buffer, format_length,
sizeof(buffer) - leftover_length);
EXPECT_GE(ret, 0);
++format_idx;
// Move left-over back
format_reader.clearFormatAndRestoreLeftover();
break;
}
break;
case uORB::MessageFormatReader::State::Failure:
PX4_ERR("Failed to read formats");
done = true;
ASSERT_FALSE(true);
break;
case uORB::MessageFormatReader::State::Complete:
done = true;
break;
default:
break;
}
}
// Check that all formats are found
for (size_t i = 0; i < formats_found.size(); ++i) {
EXPECT_TRUE(formats_found[i]);
}
// Expect dependencies to be cleared. If this is not the case, the format ordering is incorrect.
EXPECT_EQ(dependencies.count(), 0);
}
TEST_F(uORBMessageFieldsTest, decompress_formats_iterative)
{
char buffer[64];
uORB::MessageFormatReader format_reader(buffer, sizeof(buffer));
px4::Bitset<ORB_TOPICS_COUNT> formats_found;
static const char *all_formats[] = ORB_DECOMPRESSED_MESSAGE_FIELDS;
int format_idx = 0;
std::string current_format;
bool done = false;
while (!done) {
switch (format_reader.readMore()) {
case uORB::MessageFormatReader::State::ReadingFormat:
current_format += std::string(buffer, format_reader.formatLength());
format_reader.clearFormatFromBuffer();
break;
case uORB::MessageFormatReader::State::FormatComplete: {
current_format += std::string(buffer, format_reader.formatLength());
format_reader.clearFormatFromBuffer();
EXPECT_FALSE(current_format.empty());
for (const orb_id_size_t orb_id : format_reader.orbIDs()) {
// Ensure each orb_id is set only once
EXPECT_FALSE(formats_found[orb_id]);
formats_found.set(orb_id);
}
// Compare format
ASSERT_LT(format_idx, sizeof(all_formats) / sizeof(all_formats[0]));
const std::string format_expected(all_formats[format_idx]);
EXPECT_EQ(current_format, format_expected);
++format_idx;
current_format.clear();
break;
}
break;
case uORB::MessageFormatReader::State::Failure:
PX4_ERR("Failed to read formats");
done = true;
ASSERT_FALSE(true);
break;
case uORB::MessageFormatReader::State::Complete:
done = true;
break;
default:
break;
}
}
// Check that all formats are found
for (size_t i = 0; i < formats_found.size(); ++i) {
EXPECT_TRUE(formats_found[i]);
}
}
TEST_F(uORBMessageFieldsTest, decompress_formats_buffer_too_short)
{
char buffer[64];
static_assert(uORB::orb_tokenized_fields_max_length > sizeof(buffer), "Test expects smaller buffer");
uORB::MessageFormatReader format_reader(buffer, sizeof(buffer));
bool done = false;
while (!done) {
switch (format_reader.readMore()) {
case uORB::MessageFormatReader::State::Failure:
case uORB::MessageFormatReader::State::Complete:
done = true;
break;
default:
break;
}
}
EXPECT_EQ(format_reader.readMore(), uORB::MessageFormatReader::State::Failure);
}
TEST_F(uORBMessageFieldsTest, decompress_specific_format)
{
char format[512];
char buffer[128];
uORB::MessageFormatReader format_reader(buffer, sizeof(buffer));
ASSERT_TRUE(format_reader.readUntilFormat((orb_id_size_t)ORB_ID::orb_test));
int field_length = 0;
int format_length = 0;
while (format_reader.readNextField(field_length)) {
format_length += snprintf(format + format_length, sizeof(buffer) - format_length - 1, "%s;", buffer);
}
ASSERT_GT(uORB::MessageFormatReader::expandMessageFormat(format, format_length, sizeof(format)), 0);
const std::string expected_format = "uint64_t timestamp;int32_t val;uint8_t[4] _padding0;";
ASSERT_EQ(expected_format, format);
}