params: Overhaul param system to use a layered approach without locking

- Instead we disable interrups on Nuttx where needed
- No lock is held during param export. Params can be changed
  concurrently and we rely on the fact that another export will be
  triggered in that case.
This commit is contained in:
Thomas Debrunner 2023-03-06 15:15:49 +01:00 committed by Beat Küng
parent 70178b66d8
commit bc872822bc
14 changed files with 961 additions and 1118 deletions

View File

@ -156,7 +156,7 @@ add_custom_target(parameters_header DEPENDS px4_parameters.hpp)
set(SRCS)
list(APPEND SRCS parameters.cpp)
list(APPEND SRCS parameters.cpp atomic_transaction.cpp)
if(BUILD_TESTING)
list(APPEND SRCS param_translation_unit_tests.cpp)

View File

@ -0,0 +1,82 @@
/****************************************************************************
*
* 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 "ParamLayer.h"
class ConstLayer : public ParamLayer
{
public:
ConstLayer() = default;
bool store(param_t param, param_value_u value) override
{
return false;
}
bool contains(param_t param) const override
{
return param < PARAM_COUNT;
}
param_value_u get(param_t param) const override
{
if (param >= PARAM_COUNT) {
return {0};
}
return px4::parameters[param].val;
}
void reset(param_t param) override
{
// Do nothing
}
void refresh(param_t param) override
{
// Do nothing
}
int size() const override
{
return PARAM_COUNT;
}
int byteSize() const override
{
return PARAM_COUNT * sizeof(param_info_s);
}
};

View File

@ -0,0 +1,232 @@
/****************************************************************************
*
* 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 "ParamLayer.h"
#include <px4_platform_common/atomic.h>
class DynamicSparseLayer : public ParamLayer
{
public:
DynamicSparseLayer(ParamLayer *parent, int n_prealloc = 32, int n_grow = 4) : ParamLayer(parent),
_n_slots(n_prealloc), _n_grow(n_grow)
{
Slot *slots = (Slot *)malloc(sizeof(Slot) * n_prealloc);
if (slots == nullptr) {
PX4_ERR("Failed to allocate memory for dynamic sparse layer");
_n_slots = 0;
return;
}
for (int i = 0; i < _n_slots; i++) {
slots[i] = {UINT16_MAX, param_value_u{}};
}
_slots.store(slots);
}
virtual ~DynamicSparseLayer()
{
if (_slots.load()) {
free(_slots.load());
}
}
bool store(param_t param, param_value_u value) override
{
AtomicTransaction transaction;
Slot *slots = _slots.load();
const int index = _getIndex(param);
if (index < _next_slot) { // already exists
slots[index].value = value;
} else if (_next_slot < _n_slots) {
slots[_next_slot++] = {param, value};
_sort();
} else {
if (!_grow(transaction)) {
return false;
}
_slots.load()[_next_slot++] = {param, value};
_sort();
}
return true;
}
bool contains(param_t param) const override
{
const AtomicTransaction transaction;
return _getIndex(param) < _next_slot;
}
param_value_u get(param_t param) const override
{
const AtomicTransaction transaction;
Slot *slots = _slots.load();
const int index = _getIndex(param);
if (index < _next_slot) { // exists in our data structure
return slots[index].value;
}
return _parent->get(param);
}
void reset(param_t param) override
{
const AtomicTransaction transaction;
int index = _getIndex(param);
Slot *slots = _slots.load();
if (index < _next_slot) {
slots[index] = {UINT16_MAX, param_value_u{}};
_sort();
_next_slot--;
}
}
void refresh(param_t param) override
{
_parent->refresh(param);
}
int size() const override
{
return _next_slot;
}
int byteSize() const override
{
return _n_slots * sizeof(Slot);
}
private:
struct Slot {
param_t param;
param_value_u value;
};
static int _slotCompare(const void *a, const void *b)
{
return ((int)((Slot *)a)->param) - ((int)((Slot *)b)->param);
}
void _sort()
{
qsort(_slots.load(), _n_slots, sizeof(Slot), _slotCompare);
}
int _getIndex(param_t param) const
{
int left = 0;
int right = _next_slot - 1;
Slot *slots = _slots.load();
while (left <= right) {
int mid = (left + right) / 2;
if (slots[mid].param == param) {
return mid;
} else if (slots[mid].param < param) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return _next_slot;
}
bool _grow(AtomicTransaction &transaction)
{
if (_n_slots == 0) {
return false;
}
int max_retries = 5;
// As malloc uses locking, so we need to re-enable IRQ's during malloc/free and
// then atomically exchange the buffer
while (_next_slot >= _n_slots && max_retries-- > 0) {
Slot *previous_slots = nullptr;
Slot *new_slots = nullptr;
do {
previous_slots = _slots.load();
transaction.unlock();
if (new_slots) {
free(new_slots);
}
new_slots = (Slot *) malloc(sizeof(Slot) * (_n_slots + _n_grow));
transaction.lock();
if (new_slots == nullptr) {
return false;
}
} while (!_slots.compare_exchange(&previous_slots, new_slots));
memcpy(new_slots, previous_slots, sizeof(Slot) * _n_slots);
for (int i = _n_slots; i < _n_slots + _n_grow; i++) {
new_slots[i] = {UINT16_MAX, param_value_u{}};
}
_n_slots += _n_grow;
transaction.unlock();
free(previous_slots);
transaction.lock();
}
return _next_slot < _n_slots;
}
int _next_slot = 0;
int _n_slots = 0;
const int _n_grow;
px4::atomic<Slot *> _slots{nullptr};
};

View File

@ -0,0 +1,119 @@
/****************************************************************************
*
* 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 <px4_platform_common/atomic_bitset.h>
#include "ParamLayer.h"
class ExhaustiveLayer : public ParamLayer
{
public:
ExhaustiveLayer(ParamLayer *parent) : ParamLayer(parent)
{
// refresh all values
for (param_t i = 0; i < PARAM_COUNT; i++) {
_values[i] = parent->get(i);
}
}
bool store(param_t param, param_value_u value) override
{
if (param >= PARAM_COUNT) {
return false;
}
{
const AtomicTransaction transaction;
_values[param] = value;
_ownership_set.set(param);
}
return true;
}
bool contains(param_t param) const override
{
return param < PARAM_COUNT && _ownership_set[param];
}
param_value_u get(param_t param) const override
{
if (param >= PARAM_COUNT) {
return {0};
}
const AtomicTransaction transaction;
// We assume to have the correct values for all params, even without ownership.
// We expect that refresh was called when underlying defaults changed
return _values[param];
}
void reset(param_t param) override
{
if (param >= PARAM_COUNT) {
return;
}
const AtomicTransaction transaction;
_values[param] = _parent->get(param);
_ownership_set.set(param, false);
}
void refresh(param_t param) override
{
_parent->refresh(param);
// in case we don't have ownership, and it changed below, we have to refresh our cache.
{
const AtomicTransaction transaction;
if (!contains(param)) {
_values[param] = _parent->get(param);
}
}
}
int size() const override
{
return _ownership_set.count();
}
int byteSize() const override
{
return PARAM_COUNT * sizeof(param_value_u);
}
private:
param_value_u _values[PARAM_COUNT];
px4::AtomicBitset<PARAM_COUNT> _ownership_set;
};

View File

@ -0,0 +1,72 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#ifndef PX4_PARAMLAYER_H
#define PX4_PARAMLAYER_H
#include "atomic_transaction.h"
#include "param.h"
#include <stdlib.h>
class ParamLayer
{
public:
static constexpr param_t PARAM_COUNT = sizeof(px4::parameters) / sizeof(param_info_s);
ParamLayer(ParamLayer *parent = nullptr) : _parent(parent) {}
ParamLayer(const ParamLayer &) = delete;
ParamLayer &operator=(const ParamLayer &) = delete;
ParamLayer(ParamLayer &&) = delete;
ParamLayer &operator=(ParamLayer &&) = delete;
virtual bool store(param_t param, param_value_u value) = 0;
virtual bool contains(param_t param) const = 0;
virtual param_value_u get(param_t param) const = 0;
virtual void reset(param_t param) = 0;
virtual void refresh(param_t param) = 0;
virtual int size() const = 0;
virtual int byteSize() const = 0;
protected:
ParamLayer *const _parent;
};
#endif //PX4_PARAMLAYER_H

View File

@ -0,0 +1,156 @@
/****************************************************************************
*
* 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 <stdlib.h>
#include "ParamLayer.h"
template <int N_SLOTS>
class StaticSparseLayer : public ParamLayer
{
public:
StaticSparseLayer(ParamLayer *parent) : ParamLayer(parent)
{
for (int i = 0; i < N_SLOTS; i++) {
_slots[i] = {UINT16_MAX, param_value_u{}};
}
}
virtual ~StaticSparseLayer() = default;
bool store(param_t param, param_value_u value) override
{
const AtomicTransaction transaction;
const int index = _getIndex(param);
if (index < _next_slot) { // already exists
_slots[index].value = value;
} else if (_next_slot < N_SLOTS) {
_slots[_next_slot++] = {param, value};
_sort();
} else {
return false;
}
return true;
}
bool contains(param_t param) const override
{
const AtomicTransaction transaction;
return _getIndex(param) < _next_slot;
}
param_value_u get(param_t param) const override
{
const AtomicTransaction transaction;
const int index = _getIndex(param);
if (index < _next_slot) { // exists in this layer
return _slots[index].value;
}
return _parent->get(param);
}
void reset(param_t param) override
{
const AtomicTransaction transaction;
int index = _getIndex(param);
if (index < _next_slot) {
_slots[index] = {UINT16_MAX, param_value_u{}};
_sort();
_next_slot--;
}
}
void refresh(param_t param) override
{
_parent->refresh(param);
}
int size() const override
{
return _next_slot;
}
int byteSize() const override
{
return N_SLOTS * sizeof(Slot);
}
private:
struct Slot {
param_t param;
param_value_u value;
};
static int _slotCompare(const void *a, const void *b)
{
return ((int)((Slot *)a)->param) - ((int)((Slot *)b)->param);
}
void _sort()
{
qsort(_slots, N_SLOTS, sizeof(Slot), &_slotCompare);
}
int _getIndex(param_t param) const
{
int left = 0;
int right = _next_slot - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (_slots[mid].param == param) {
return mid;
} else if (_slots[mid].param < param) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return _next_slot;
}
Slot _slots[N_SLOTS];
int _next_slot = 0;
};

View File

@ -0,0 +1,38 @@
/****************************************************************************
*
* 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 "atomic_transaction.h"
#ifdef __PX4_POSIX
_MutexHolder AtomicTransaction::_mutex_holder = _MutexHolder {};
#endif

View File

@ -0,0 +1,105 @@
/****************************************************************************
*
* 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
#ifdef __PX4_NUTTX
#include "px4_platform_common/micro_hal.h"
#endif
#ifdef __PX4_POSIX
#include <pthread.h>
class _MutexHolder
{
public:
pthread_mutex_t _mutex;
pthread_mutexattr_t _mutex_attr;
_MutexHolder()
{
pthread_mutexattr_init(&_mutex_attr);
pthread_mutexattr_settype(&_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_mutex, &_mutex_attr);
}
~_MutexHolder()
{
pthread_mutex_destroy(&_mutex);
}
};
#endif
class AtomicTransaction
{
private:
#ifdef __PX4_NUTTX
irqstate_t _irq_state;
#endif
#ifdef __PX4_POSIX
static _MutexHolder _mutex_holder;
#endif
public:
AtomicTransaction()
{
lock();
}
~AtomicTransaction()
{
unlock();
}
void lock()
{
#ifdef __PX4_NUTTX
_irq_state = px4_enter_critical_section();
#endif
#ifdef __PX4_POSIX
pthread_mutex_lock(&_mutex_holder._mutex);
#endif
}
void unlock()
{
#ifdef __PX4_NUTTX
px4_leave_critical_section(_irq_state);
#endif
#ifdef __PX4_POSIX
pthread_mutex_unlock(&_mutex_holder._mutex);
#endif
}
};

View File

@ -42,7 +42,7 @@ add_library(flashparams
flashparams.cpp
)
add_dependencies(flashparams prebuild_targets)
add_dependencies(flashparams prebuild_targets parameters_header)
target_compile_definitions(flashparams PRIVATE -DMODULE_NAME="flashparams")
target_compile_options(flashparams
PRIVATE

View File

@ -46,6 +46,7 @@
#include <px4_platform_common/log.h>
#include <px4_platform_common/posix.h>
#include <px4_platform_common/shutdown.h>
#include <parameters/px4_parameters.hpp>
#include <string.h>
#include <stdbool.h>
@ -54,7 +55,6 @@
#include <parameters/param.h>
#include "../uthash/utarray.h"
#include <lib/tinybson/tinybson.h>
#include "flashparams.h"
#include "flashfs.h"
@ -78,7 +78,6 @@ struct param_wbuf_s {
static int
param_export_internal(param_filter_func filter)
{
struct param_wbuf_s *s = nullptr;
bson_encoder_s encoder{};
int result = -1;
@ -86,28 +85,22 @@ param_export_internal(param_filter_func filter)
bson_encoder_init_buf(&encoder, nullptr, 0);
/* no modified parameters -> we are done */
if (param_values == nullptr) {
result = 0;
goto out;
}
while ((s = (struct param_wbuf_s *)utarray_next(param_values, s)) != nullptr) {
for (param_t param = 0; param < user_config.PARAM_COUNT; param++) {
int32_t i;
float f;
if (filter && !filter(s->param)) {
if (filter && !filter(param)) {
continue;
}
/* append the appropriate BSON type object */
switch (param_type(s->param)) {
switch (param_type(param)) {
case PARAM_TYPE_INT32:
i = s->val.i;
i = user_config.get(param).i;
if (bson_encoder_append_int32(&encoder, param_name(s->param), i)) {
if (bson_encoder_append_int32(&encoder, param_name(param), i)) {
debug("BSON append failed for '%s'", param_name(s->param));
goto out;
}
@ -115,9 +108,9 @@ param_export_internal(param_filter_func filter)
break;
case PARAM_TYPE_FLOAT:
f = s->val.f;
f = user_config.get(param).f;
if (bson_encoder_append_double(&encoder, param_name(s->param), f)) {
if (bson_encoder_append_double(&encoder, param_name(param), (double)f)) {
debug("BSON append failed for '%s'", param_name(s->param));
goto out;
}

View File

@ -49,6 +49,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include "../DynamicSparseLayer.h"
__BEGIN_DECLS
@ -57,9 +58,9 @@ __BEGIN_DECLS
* the param_values and 2 functions to be global
*/
__EXPORT extern UT_array *param_values;
__EXPORT extern DynamicSparseLayer user_config;
__EXPORT int param_set_external(param_t param, const void *val, bool mark_saved, bool notify_changes);
__EXPORT const void *param_get_value_ptr_external(param_t param);
__EXPORT void param_get_external(param_t param, void *val);
/* The interface hooks to the Flash based storage. The caller is responsible for locking */
__EXPORT int flash_param_save(param_filter_func filter);

View File

@ -60,7 +60,7 @@
#include <px4_platform_common/posix.h>
#include <px4_platform_common/sem.h>
#include <px4_platform_common/shutdown.h>
#include "uthash/utarray.h"
#include <px4_platform_common/micro_hal.h>
using namespace time_literals;
@ -69,6 +69,13 @@ using namespace time_literals;
#include <uORB/topics/actuator_armed.h>
#include <uORB/Subscription.hpp>
#include "ExhaustiveLayer.h"
#include "ConstLayer.h"
#include "DynamicSparseLayer.h"
#include "StaticSparseLayer.h"
#include "atomic_transaction.h"
/* Include functions common to user and kernel sides */
#include "parameters_common.cpp"
@ -96,103 +103,27 @@ static px4::atomic_bool autosave_scheduled{false};
static bool autosave_disabled = false;
static px4::AtomicBitset<param_info_count> params_active; // params found
static px4::AtomicBitset<param_info_count> params_changed; // params non-default
static px4::Bitset<param_info_count> params_custom_default; // params with runtime default value
static px4::AtomicBitset<param_info_count> params_unsaved;
// Storage for modified parameters.
struct param_wbuf_s {
union param_value_u val;
param_t param;
};
/** flexible array holding modified parameter values */
UT_array *param_values{nullptr};
UT_array *param_custom_default_values{nullptr};
const UT_icd param_icd = {sizeof(param_wbuf_s), nullptr, nullptr, nullptr};
static ConstLayer firmware_defaults;
static DynamicSparseLayer runtime_defaults{&firmware_defaults};
DynamicSparseLayer user_config{&runtime_defaults};
/** parameter update topic handle */
static orb_advert_t param_topic = nullptr;
static unsigned int param_instance = 0;
// the following implements an RW-lock using 2 semaphores (used as mutexes). It gives
// priority to readers, meaning a writer could suffer from starvation, but in our use-case
// we only have short periods of reads and writes are rare.
static px4_sem_t param_sem; ///< this protects against concurrent access to param_values
static int reader_lock_holders = 0;
static px4_sem_t reader_lock_holders_lock; ///< this protects against concurrent access to reader_lock_holders
static perf_counter_t param_export_perf;
static perf_counter_t param_find_perf;
static perf_counter_t param_get_perf;
static perf_counter_t param_set_perf;
static px4_sem_t param_sem_save; ///< this protects against concurrent param saves (file or flash access).
///< we use a separate lock to allow concurrent param reads and saves.
///< a param_set could still be blocked by a param save, because it
///< needs to take the reader lock
/** lock the parameter store for read access */
static void
param_lock_reader()
{
do {} while (px4_sem_wait(&reader_lock_holders_lock) != 0);
++reader_lock_holders;
if (reader_lock_holders == 1) {
// the first reader takes the lock, the next ones are allowed to just continue
do {} while (px4_sem_wait(&param_sem) != 0);
}
px4_sem_post(&reader_lock_holders_lock);
}
/** lock the parameter store for write access */
static void
param_lock_writer()
{
do {} while (px4_sem_wait(&param_sem) != 0);
}
/** unlock the parameter store */
static void
param_unlock_reader()
{
do {} while (px4_sem_wait(&reader_lock_holders_lock) != 0);
--reader_lock_holders;
if (reader_lock_holders == 0) {
// the last reader releases the lock
px4_sem_post(&param_sem);
}
px4_sem_post(&reader_lock_holders_lock);
}
/** unlock the parameter store */
static void
param_unlock_writer()
{
px4_sem_post(&param_sem);
}
/** assert that the parameter store is locked */
static void
param_assert_locked()
{
/* XXX */
}
static pthread_mutex_t file_mutex =
PTHREAD_MUTEX_INITIALIZER; ///< this protects against concurrent param saves (file or flash access).
void
param_init()
{
px4_sem_init(&param_sem, 0, 1);
px4_sem_init(&param_sem_save, 0, 1);
px4_sem_init(&reader_lock_holders_lock, 0, 1);
param_export_perf = perf_alloc(PC_ELAPSED, "param: export");
param_find_perf = perf_alloc(PC_COUNT, "param: find");
param_get_perf = perf_alloc(PC_COUNT, "param: get");
@ -203,48 +134,6 @@ param_init()
#endif
}
/**
* Compare two modified parameter structures to determine ordering.
*
* This function is suitable for passing to qsort or bsearch.
*/
static int
param_compare_values(const void *a, const void *b)
{
struct param_wbuf_s *pa = (struct param_wbuf_s *)a;
struct param_wbuf_s *pb = (struct param_wbuf_s *)b;
if (pa->param < pb->param) {
return -1;
}
if (pa->param > pb->param) {
return 1;
}
return 0;
}
/**
* Locate the modified parameter structure for a parameter, if it exists.
*
* @param param The parameter being searched.
* @return The structure holding the modified value, or
* nullptr if the parameter has not been modified.
*/
static param_wbuf_s *
param_find_changed(param_t param)
{
param_assert_locked();
if (params_changed[param] && (param_values != nullptr)) {
param_wbuf_s key{};
key.param = param;
return (param_wbuf_s *)utarray_find(param_values, &key, param_compare_values);
}
return nullptr;
}
void
param_notify_changes()
@ -256,8 +145,8 @@ param_notify_changes()
pup.find_count = perf_event_count(param_find_perf);
pup.export_count = perf_event_count(param_export_perf);
pup.active = params_active.count();
pup.changed = params_changed.count();
pup.custom_default = params_custom_default.count();
pup.changed = user_config.size();
pup.custom_default = runtime_defaults.size();
pup.timestamp = hrt_absolute_time();
if (param_topic == nullptr) {
@ -372,51 +261,6 @@ param_value_unsaved(param_t param)
return handle_in_range(param) ? params_unsaved[param] : false;
}
/**
* Obtain a pointer to the storage allocated for a parameter.
*
* @param param The parameter whose storage is sought.
* @return A pointer to the parameter value, or nullptr
* if the parameter does not exist.
*/
static const void *
param_get_value_ptr(param_t param)
{
param_assert_locked();
if (handle_in_range(param)) {
/* work out whether we're fetching the default or a written value */
struct param_wbuf_s *s = param_find_changed(param);
if (s != nullptr) {
return &s->val;
} else {
if (params_custom_default[param] && param_custom_default_values) {
// get default from custom default storage
param_wbuf_s key{};
key.param = param;
param_wbuf_s *pbuf = (param_wbuf_s *)utarray_find(param_custom_default_values, &key, param_compare_values);
if (pbuf != nullptr) {
return &pbuf->val;
}
}
// otherwise return static default value
switch (param_type(param)) {
case PARAM_TYPE_INT32:
return &px4::parameters[param].val.i;
case PARAM_TYPE_FLOAT:
return &px4::parameters[param].val.f;
}
}
}
return nullptr;
}
int
param_get(param_t param, void *val)
{
@ -434,28 +278,18 @@ param_get(param_t param, void *val)
int result = PX4_ERROR;
if (val) {
if (!params_changed[param] && !params_custom_default[param]) {
// if parameter is unchanged (static default value) copy immediately and avoid locking
switch (param_type(param)) {
case PARAM_TYPE_INT32:
memcpy(val, &px4::parameters[param].val.i, sizeof(px4::parameters[param].val.i));
return PX4_OK;
case PARAM_TYPE_FLOAT:
memcpy(val, &px4::parameters[param].val.f, sizeof(px4::parameters[param].val.f));
return PX4_OK;
}
auto retrieve_value = user_config.get(param);
switch (param_type(param)) {
case PARAM_TYPE_INT32:
*(int32_t *)val = retrieve_value.i;
return PX4_OK;
case PARAM_TYPE_FLOAT:
*(float *)val = retrieve_value.f;
return PX4_OK;
}
param_lock_reader();
const void *v = param_get_value_ptr(param);
if (v) {
memcpy(val, v, param_size(param));
result = PX4_OK;
}
param_unlock_reader();
}
return result;
@ -470,26 +304,13 @@ param_get_default_value_internal(param_t param, void *default_val)
}
if (default_val) {
if (params_custom_default[param] && param_custom_default_values) {
// get default from custom default storage
param_wbuf_s key{};
key.param = param;
param_wbuf_s *pbuf = (param_wbuf_s *)utarray_find(param_custom_default_values, &key, param_compare_values);
if (pbuf != nullptr) {
memcpy(default_val, &pbuf->val, param_size(param));
return PX4_OK;
}
}
// otherwise return static default value
switch (param_type(param)) {
case PARAM_TYPE_INT32:
memcpy(default_val, &px4::parameters[param].val.i, param_size(param));
*(int32_t *) default_val = runtime_defaults.get(param).i;
return PX4_OK;
case PARAM_TYPE_FLOAT:
memcpy(default_val, &px4::parameters[param].val.f, param_size(param));
*(float *) default_val = runtime_defaults.get(param).f;
return PX4_OK;
}
}
@ -504,26 +325,7 @@ param_get_default_value(param_t param, void *default_val)
return PX4_ERROR;
}
int ret = 0;
if (!params_custom_default[param]) {
// return static default value
switch (param_type(param)) {
case PARAM_TYPE_INT32:
memcpy(default_val, &px4::parameters[param].val.i, sizeof(px4::parameters[param].val.i));
return PX4_OK;
case PARAM_TYPE_FLOAT:
memcpy(default_val, &px4::parameters[param].val.f, sizeof(px4::parameters[param].val.f));
return PX4_OK;
}
} else {
param_lock_reader();
ret = param_get_default_value_internal(param, default_val);
param_unlock_reader();
}
int ret = param_get_default_value_internal(param, default_val);
return ret;
}
@ -533,49 +335,20 @@ bool param_value_is_default(param_t param)
return true;
}
if (!params_changed[param] && !params_custom_default[param]) {
// no value saved and no custom default
if (!user_config.contains(param)) {
// if user config does not contain it, consider it default.
return true;
} else {
// the param_values dynamic array might carry things that have been set
// back to default, so we don't rely on the params_changed bitset here
// compare with default value
switch (param_type(param)) {
case PARAM_TYPE_INT32: {
param_lock_reader();
int32_t default_value = 0;
if (param_get_default_value_internal(param, &default_value) == PX4_OK) {
const void *v = param_get_value_ptr(param);
if (v) {
bool unchanged = (*static_cast<const int32_t *>(v) == default_value);
param_unlock_reader();
return unchanged;
}
}
param_unlock_reader();
return user_config.get(param).i == runtime_defaults.get(param).i;
}
break;
case PARAM_TYPE_FLOAT: {
param_lock_reader();
float default_value = 0;
if (param_get_default_value_internal(param, &default_value) == PX4_OK) {
const void *v = param_get_value_ptr(param);
if (v) {
bool unchanged = (fabsf(*static_cast<const float *>(v) - default_value) <= FLT_EPSILON);
param_unlock_reader();
return unchanged;
}
}
param_unlock_reader();
return user_config.get(param).f - runtime_defaults.get(param).f < FLT_EPSILON;
}
break;
}
}
@ -602,11 +375,12 @@ autosave_worker(void *arg)
}
}
param_lock_writer();
last_autosave_timestamp = hrt_absolute_time();
autosave_scheduled.store(false);
disabled = autosave_disabled;
param_unlock_writer();
{
const AtomicTransaction transaction;
last_autosave_timestamp = hrt_absolute_time();
autosave_scheduled.store(false);
disabled = autosave_disabled;
}
if (disabled) {
return;
@ -653,15 +427,14 @@ param_autosave()
void
param_control_autosave(bool enable)
{
param_lock_writer();
AtomicTransaction transaction;
autosave_disabled = !enable;
if (!enable && autosave_scheduled.load()) {
work_cancel(LPWORK, &autosave_work);
autosave_scheduled.store(false);
transaction.unlock();
work_cancel(LPWORK, &autosave_work);
}
autosave_disabled = !enable;
param_unlock_writer();
}
static int
@ -679,84 +452,49 @@ param_set_internal(param_t param, const void *val, bool mark_saved, bool notify_
int result = -1;
bool param_changed = false;
param_lock_writer();
bool param_store_success = false;
perf_begin(param_set_perf);
// create the parameter store if it doesn't exist
if (param_values == nullptr) {
utarray_new(param_values, &param_icd);
switch (param_type(param)) {
case PARAM_TYPE_INT32: {
if (user_config.get(param).i != *(int32_t *)val) {
param_changed = true;
}
// mark all parameters unchanged (default)
params_changed.reset();
params_unsaved.reset();
param_store_success = user_config.store(param, param_value_u{.i = *(int32_t *)val});
params_unsaved.set(param, !mark_saved);
break;
}
case PARAM_TYPE_FLOAT: {
if (fabsf(user_config.get(param).f - * (float *) val) > FLT_EPSILON) {
param_changed = true;
}
param_store_success = user_config.store(param, param_value_u{.f = *(float *) val});
params_unsaved.set(param, !mark_saved);
break;
}
default: {
PX4_ERR("param_set invalid param type for %s", param_name(param));
break;
}
}
if (param_values == nullptr) {
PX4_ERR("failed to allocate modified values array");
goto out;
if (!param_store_success) {
PX4_ERR("param_set failed to store param %s", param_name(param));
result = PX4_ERROR;
} else {
param_wbuf_s *s = param_find_changed(param);
if (s == nullptr) {
/* construct a new parameter */
param_wbuf_s buf{};
buf.param = param;
param_changed = true;
/* add it to the array and sort */
utarray_push_back(param_values, &buf);
utarray_sort(param_values, param_compare_values);
params_changed.set(param, true);
/* find it after sorting */
s = param_find_changed(param);
}
if (s == nullptr) {
PX4_ERR("error param_values storage slot invalid");
} else {
/* update the changed value */
switch (param_type(param)) {
case PARAM_TYPE_INT32:
if (s->val.i != *(int32_t *)val) {
s->val.i = *(int32_t *)val;
param_changed = true;
}
params_changed.set(param, true);
params_unsaved.set(param, !mark_saved);
result = PX4_OK;
break;
case PARAM_TYPE_FLOAT:
if (fabsf(s->val.f - * (float *)val) > FLT_EPSILON) {
s->val.f = *(float *)val;
param_changed = true;
}
params_changed.set(param, true);
params_unsaved.set(param, !mark_saved);
result = PX4_OK;
break;
default:
PX4_ERR("param_set invalid param type for %s", param_name(param));
break;
}
}
if ((result == PX4_OK) && param_changed && !mark_saved) { // this is false when importing parameters
param_autosave();
}
result = PX4_OK;
}
if ((result == PX4_OK) && param_changed && !mark_saved) { // this is false when importing parameters
param_autosave();
}
out:
perf_end(param_set_perf);
param_unlock_writer();
/*
* If we set something, now that we have unlocked, go ahead and advertise that
@ -775,9 +513,9 @@ int param_set_external(param_t param, const void *val, bool mark_saved, bool not
return param_set_internal(param, val, mark_saved, notify_changes);
}
const void *param_get_value_ptr_external(param_t param)
void param_get_external(param_t param, void *val)
{
return param_get_value_ptr(param);
param_get(param, val);
}
#endif
@ -821,90 +559,46 @@ int param_set_default_value(param_t param, const void *val)
int result = PX4_ERROR;
param_lock_writer();
if (param_custom_default_values == nullptr) {
utarray_new(param_custom_default_values, &param_icd);
// mark all parameters unchanged (default)
params_custom_default.reset();
if (param_custom_default_values == nullptr) {
PX4_ERR("failed to allocate custom default values array");
param_unlock_writer();
return PX4_ERROR;
}
}
// check if param being set to default value
bool setting_to_static_default = false;
switch (param_type(param)) {
case PARAM_TYPE_INT32:
setting_to_static_default = (px4::parameters[param].val.i == *(int32_t *)val);
setting_to_static_default = (firmware_defaults.get(param).i == *(int32_t *)val);
break;
case PARAM_TYPE_FLOAT:
setting_to_static_default = (fabsf(px4::parameters[param].val.f - * (float *)val) <= FLT_EPSILON);
setting_to_static_default = (fabsf(firmware_defaults.get(param).f - * (float *)val) <= FLT_EPSILON);
break;
}
// find if custom default value is already set
param_wbuf_s *s = nullptr;
{
param_wbuf_s key{};
key.param = param;
s = (param_wbuf_s *)utarray_find(param_custom_default_values, &key, param_compare_values);
}
if (setting_to_static_default) {
if (s != nullptr) {
// param in memory and set to non-default value, clear
int pos = utarray_eltidx(param_custom_default_values, s);
utarray_erase(param_custom_default_values, pos, 1);
}
runtime_defaults.reset(param);
// do nothing if param not already set and being set to default
params_custom_default.set(param, false);
result = PX4_OK;
} else {
if (s == nullptr) {
// construct a new parameter default value
param_wbuf_s buf{};
buf.param = param;
// add it to the array and sort
utarray_push_back(param_custom_default_values, &buf);
utarray_sort(param_custom_default_values, param_compare_values);
// find it after sorting
s = (param_wbuf_s *)utarray_find(param_custom_default_values, &buf, param_compare_values);
}
if (s != nullptr) {
// update the default value
switch (param_type(param)) {
case PARAM_TYPE_INT32:
s->val.i = *(int32_t *)val;
params_custom_default.set(param, true);
result = PX4_OK;
break;
case PARAM_TYPE_FLOAT:
s->val.f = *(float *)val;
params_custom_default.set(param, true);
result = PX4_OK;
break;
default:
switch (param_type(param)) {
case PARAM_TYPE_INT32: {
const bool store_ok = runtime_defaults.store(param, param_value_u{.i = *(int32_t *) val});
user_config.refresh(param);
result = store_ok ? PX4_OK : PX4_ERROR;
break;
}
case PARAM_TYPE_FLOAT: {
const bool store_ok = runtime_defaults.store(param, param_value_u{.f = *(float *) val});
user_config.refresh(param);
result = store_ok ? PX4_OK : PX4_ERROR;
break;
}
default:
break;
}
}
param_unlock_writer();
if ((result == PX4_OK) && param_used(param)) {
// send notification if param is already in use
@ -914,38 +608,23 @@ int param_set_default_value(param_t param, const void *val)
return result;
}
static int param_reset_internal(param_t param, bool notify = true)
static int param_reset_internal(param_t param, bool notify = true, bool autosave = true)
{
param_wbuf_s *s = nullptr;
bool param_found = false;
param_lock_writer();
bool param_found = user_config.contains(param);
if (handle_in_range(param)) {
/* look for a saved value */
s = param_find_changed(param);
/* if we found one, erase it */
if (s != nullptr) {
int pos = utarray_eltidx(param_values, s);
utarray_erase(param_values, pos, 1);
}
params_changed.set(param, false);
params_unsaved.set(param, true);
param_found = true;
user_config.reset(param);
}
param_autosave();
if (autosave) {
param_autosave();
}
param_unlock_writer();
if (s != nullptr && notify) {
if (param_found && notify) {
param_notify_changes();
}
return (!param_found);
return param_found;
}
int param_reset(param_t param) { return param_reset_internal(param, true); }
@ -954,23 +633,14 @@ int param_reset_no_notification(param_t param) { return param_reset_internal(par
static void
param_reset_all_internal(bool auto_save)
{
param_lock_writer();
if (param_values != nullptr) {
utarray_free(param_values);
params_changed.reset();
for (param_t param = 0; handle_in_range(param); param++) {
param_reset_internal(param, false, false);
}
/* mark as reset / deleted */
param_values = nullptr;
if (auto_save) {
param_autosave();
}
param_unlock_writer();
param_notify_changes();
}
@ -1097,17 +767,19 @@ static int param_verify(int fd);
int param_save_default()
{
PX4_DEBUG("param_save_default");
// take the file lock
if (pthread_mutex_trylock(&file_mutex) != 0) {
PX4_ERR("param_save_default: file lock failed (already locked)");
return PX4_ERROR;
}
int shutdown_lock_ret = px4_shutdown_lock();
if (shutdown_lock_ret != 0) {
PX4_ERR("px4_shutdown_lock() failed (%i)", shutdown_lock_ret);
}
// take the file lock
do {} while (px4_sem_wait(&param_sem_save) != 0);
param_lock_reader();
int res = PX4_ERROR;
const char *filename = param_get_default_file();
@ -1174,8 +846,7 @@ int param_save_default()
}
}
param_unlock_reader();
px4_sem_post(&param_sem_save);
pthread_mutex_unlock(&file_mutex);
if (shutdown_lock_ret == 0) {
px4_shutdown_unlock();
@ -1336,9 +1007,10 @@ param_export(const char *filename, param_filter_func filter)
}
// take the file lock
do {} while (px4_sem_wait(&param_sem_save) != 0);
param_lock_reader();
if (pthread_mutex_trylock(&file_mutex) != 0) {
PX4_ERR("param_export: file lock failed (already locked)");
return PX4_ERROR;
}
int fd = ::open(filename, O_RDWR | O_CREAT, PX4_O_MODE_666);
int result = PX4_ERROR;
@ -1354,8 +1026,7 @@ param_export(const char *filename, param_filter_func filter)
perf_end(param_export_perf);
param_unlock_reader();
px4_sem_post(&param_sem_save);
pthread_mutex_unlock(&file_mutex);
if (shutdown_lock_ret == 0) {
px4_shutdown_unlock();
@ -1370,7 +1041,6 @@ static int param_export_internal(int fd, param_filter_func filter)
PX4_DEBUG("param_export_internal");
int result = -1;
param_wbuf_s *s = nullptr;
bson_encoder_s encoder{};
uint8_t bson_buffer[256];
@ -1383,50 +1053,42 @@ static int param_export_internal(int fd, param_filter_func filter)
goto out;
}
// no modified parameters, export empty BSON document
if (param_values == nullptr) {
result = 0;
goto out;
}
while ((s = (struct param_wbuf_s *)utarray_next(param_values, s)) != nullptr) {
if (filter && !filter(s->param)) {
for (param_t param = 0; handle_in_range(param); param++) {
if (filter && !filter(param)) {
continue;
}
// don't export default values
switch (param_type(s->param)) {
switch (param_type(param)) {
case PARAM_TYPE_INT32: {
int32_t default_value = 0;
param_get_default_value_internal(s->param, &default_value);
int32_t default_value = runtime_defaults.get(param).i;
if (s->val.i == default_value) {
PX4_DEBUG("skipping %s %" PRIi32 " export", param_name(s->param), default_value);
if (user_config.get(param).i == default_value) {
PX4_DEBUG("skipping %s %" PRIi32 " export", param_name(param), default_value);
continue;
}
}
break;
case PARAM_TYPE_FLOAT: {
float default_value = 0;
param_get_default_value_internal(s->param, &default_value);
float default_value = runtime_defaults.get(param).f;
if (fabsf(s->val.f - default_value) <= FLT_EPSILON) {
PX4_DEBUG("skipping %s %.3f export", param_name(s->param), (double)default_value);
if (fabsf(user_config.get(param).f - default_value) <= FLT_EPSILON) {
PX4_DEBUG("skipping %s %.3f export", param_name(param), (double)default_value);
continue;
}
}
break;
}
const char *name = param_name(s->param);
const size_t size = param_size(s->param);
const char *name = param_name(param);
const size_t size = param_size(param);
/* append the appropriate BSON type object */
switch (param_type(s->param)) {
switch (param_type(param)) {
case PARAM_TYPE_INT32: {
const int32_t i = s->val.i;
PX4_DEBUG("exporting: %s (%d) size: %lu val: %" PRIi32, name, s->param, (long unsigned int)size, i);
const int32_t i = user_config.get(param).i;
PX4_DEBUG("exporting: %s (%d) size: %lu val: %" PRIi32, name, param, (long unsigned int)size, i);
if (bson_encoder_append_int32(&encoder, name, i) != 0) {
PX4_ERR("BSON append failed for '%s'", name);
@ -1436,8 +1098,8 @@ static int param_export_internal(int fd, param_filter_func filter)
break;
case PARAM_TYPE_FLOAT: {
const double f = (double)s->val.f;
PX4_DEBUG("exporting: %s (%d) size: %lu val: %.3f", name, s->param, (long unsigned int)size, (double)f);
const double f = (double)user_config.get(param).f;
PX4_DEBUG("exporting: %s (%d) size: %lu val: %.3f", name, param, (long unsigned int)size, (double)f);
if (bson_encoder_append_double(&encoder, name, f) != 0) {
PX4_ERR("BSON append failed for '%s'", name);
@ -1447,7 +1109,7 @@ static int param_export_internal(int fd, param_filter_func filter)
break;
default:
PX4_ERR("%s unrecognized parameter type %d, skipping export", name, param_type(s->param));
PX4_ERR("%s unrecognized parameter type %d, skipping export", name, param_type(param));
}
}
@ -1606,7 +1268,7 @@ param_foreach(void (*func)(void *arg, param_t param), void *arg, bool only_chang
for (param = 0; handle_in_range(param); param++) {
/* if requested, skip unchanged values */
if (only_changed && (param_find_changed(param) == nullptr)) {
if (only_changed && (!user_config.contains(param))) {
continue;
}
@ -1622,8 +1284,6 @@ uint32_t param_hash_check()
{
uint32_t param_hash = 0;
param_lock_reader();
/* compute the CRC32 over all string param names and 4 byte values */
for (param_t param = 0; handle_in_range(param); param++) {
if (!param_used(param) || param_is_volatile(param)) {
@ -1631,13 +1291,12 @@ uint32_t param_hash_check()
}
const char *name = param_name(param);
const void *val = param_get_value_ptr(param);
auto value = user_config.get(param).i;
const void *val = (void *)&value;
param_hash = crc32part((const uint8_t *)name, strlen(name), param_hash);
param_hash = crc32part((const uint8_t *)val, param_size(param), param_hash);
}
param_unlock_reader();
return param_hash;
}
@ -1658,16 +1317,12 @@ void param_print_status()
#endif /* FLASH_BASED_PARAMS */
if (param_values != nullptr) {
PX4_INFO("storage array: %d/%d elements (%zu bytes total)",
utarray_len(param_values), param_values->n, param_values->n * sizeof(UT_icd));
}
PX4_INFO("storage array: %d/%d elements (%zu bytes total)",
user_config.size(), firmware_defaults.size(), (size_t)user_config.byteSize());
if (param_custom_default_values != nullptr) {
PX4_INFO("storage array (custom defaults): %d/%d elements (%zu bytes total)",
utarray_len(param_custom_default_values), param_custom_default_values->n,
param_custom_default_values->n * sizeof(UT_icd));
}
PX4_INFO("storage array (custom defaults): %d/%d elements (%zu bytes total)",
runtime_defaults.size(), firmware_defaults.size(), (size_t)runtime_defaults.byteSize());
PX4_INFO("auto save: %s", autosave_disabled ? "off" : "on");

View File

@ -1,376 +0,0 @@
utarray: dynamic array macros for C
===================================
Troy D. Hanson <thanson@users.sourceforge.net>
v1.9.5, November 2011
include::sflogo.txt[]
include::topnav_utarray.txt[]
Introduction
------------
include::toc.txt[]
A set of general-purpose dynamic array macros for C structures are included with
uthash in `utarray.h`. To use these macros in your own C program, just
copy `utarray.h` into your source directory and use it in your programs.
#include "utarray.h"
The dynamic array supports basic operations such as push, pop, and erase on the
array elements. These array elements can be any simple datatype or structure.
The array <<operations,operations>> are based loosely on the C++ STL vector methods.
Internally the dynamic array contains a contiguous memory region into which
the elements are copied. This buffer is grown as needed using `realloc` to
accomodate all the data that is pushed into it.
Download
~~~~~~~~
To download the `utarray.h` header file, follow the link on the
http://uthash.sourceforge.net[uthash home page].
BSD licensed
~~~~~~~~~~~~
This software is made available under the
link:license.html[revised BSD license].
It is free and open source.
Platforms
~~~~~~~~~
The 'utarray' macros have been tested on:
* Linux,
* Mac OS X,
* Windows, using Visual Studio 2008 and Visual Studio 2010
Usage
-----
Declaration
~~~~~~~~~~~
The array itself has the data type `UT_array`, regardless of the type of
elements to be stored in it. It is declared like,
UT_array *nums;
New and free
~~~~~~~~~~~~
The next step is to create the array using `utarray_new`. Later when you're
done with the array, `utarray_free` will free it and all its elements.
Push, pop, etc
~~~~~~~~~~~~~~
The central features of the utarray involve putting elements into it, taking
them out, and iterating over them. There are several <<operations,operations>>
to pick from that deal with either single elements or ranges of elements at a
time. In the examples below we will use only the push operation to insert
elements.
Elements
--------
Support for dynamic arrays of integers or strings is especially easy. These are
best shown by example:
Integers
~~~~~~~~
This example makes a utarray of integers, pushes 0-9 into it, then prints it.
Lastly it frees it.
.Integer elements
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utarray.h"
int main() {
UT_array *nums;
int i, *p;
utarray_new(nums,&ut_int_icd);
for(i=0; i < 10; i++) utarray_push_back(nums,&i);
for(p=(int*)utarray_front(nums);
p!=NULL;
p=(int*)utarray_next(nums,p)) {
printf("%d\n",*p);
}
utarray_free(nums);
return 0;
}
-------------------------------------------------------------------------------
The second argument to `utarray_push_back` is always a 'pointer' to the type
(so a literal cannot be used). So for integers, it is an `int*`.
Strings
~~~~~~~
In this example we make a utarray of strings, push two strings into it, print
it and free it.
.String elements
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utarray.h"
int main() {
UT_array *strs;
char *s, **p;
utarray_new(strs,&ut_str_icd);
s = "hello"; utarray_push_back(strs, &s);
s = "world"; utarray_push_back(strs, &s);
p = NULL;
while ( (p=(char**)utarray_next(strs,p))) {
printf("%s\n",*p);
}
utarray_free(strs);
return 0;
}
-------------------------------------------------------------------------------
In this example, since the element is a `char*`, we pass a pointer to it
(`char**`) as the second argument to `utarray_push_back`. Note that "push" makes
a copy of the source string and pushes that copy into the array.
About UT_icd
~~~~~~~~~~~~
Arrays be made of any type of element, not just integers and strings. The
elements can be basic types or structures. Unless you're dealing with integers
and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need
to define a `UT_icd` helper structure. This structure contains everything that
utarray needs to initialize, copy or destruct elements.
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
The three function pointers `init`, `copy`, and `dtor` have these prototypes:
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
The `sz` is just the size of the element being stored in the array.
The `init` function will be invoked whenever utarray needs to initialize an
empty element. This only happens as a byproduct of `utarray_resize` or
`utarray_extend_back`. If `init` is `NULL`, it defaults to zero filling the
new element using memset.
The `copy` function is used whenever an element is copied into the array.
It is invoked during `utarray_push_back`, `utarray_insert`, `utarray_inserta`,
or `utarray_concat`. If `copy` is `NULL`, it defaults to a bitwise copy using
memcpy.
The `dtor` function is used to clean up an element that is being removed from
the array. It may be invoked due to `utarray_resize`, `utarray_pop_back`,
`utarray_erase`, `utarray_clear`, `utarray_done` or `utarray_free`. If the
elements need no cleanup upon destruction, `dtor` may be `NULL`.
Scalar types
~~~~~~~~~~~~
The next example uses `UT_icd` with all its defaults to make a utarray of
`long` elements. This example pushes two longs, prints them, and frees the
array.
.long elements
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utarray.h"
UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };
int main() {
UT_array *nums;
long l, *p;
utarray_new(nums, &long_icd);
l=1; utarray_push_back(nums, &l);
l=2; utarray_push_back(nums, &l);
p=NULL;
while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p);
utarray_free(nums);
return 0;
}
-------------------------------------------------------------------------------
Structures
~~~~~~~~~~
Structures can be used as utarray elements. If the structure requires no
special effort to initialize, copy or destruct, we can use `UT_icd` with all
its defaults. This example shows a structure that consists of two integers. Here
we push two values, print them and free the array.
.Structure (simple)
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utarray.h"
typedef struct {
int a;
int b;
} intpair_t;
UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};
int main() {
UT_array *pairs;
intpair_t ip, *p;
utarray_new(pairs,&intpair_icd);
ip.a=1; ip.b=2; utarray_push_back(pairs, &ip);
ip.a=10; ip.b=20; utarray_push_back(pairs, &ip);
for(p=(intpair_t*)utarray_front(pairs);
p!=NULL;
p=(intpair_t*)utarray_next(pairs,p)) {
printf("%d %d\n", p->a, p->b);
}
utarray_free(pairs);
return 0;
}
-------------------------------------------------------------------------------
The real utility of `UT_icd` is apparent when the elements of the utarray are
structures that require special work to initialize, copy or destruct.
For example, when a structure contains pointers to related memory areas that
need to be copied when the structure is copied (and freed when the structure is
freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`.
Here we take an example of a structure that contains an integer and a string.
When this element is copied (such as when an element is pushed into the array),
we want to "deep copy" the `s` pointer (so the original element and the new
element point to their own copies of `s`). When an element is destructed, we
want to "deep free" its copy of `s`. Lastly, this example is written to work
even if `s` has the value `NULL`.
.Structure (complex)
-------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include "utarray.h"
typedef struct {
int a;
char *s;
} intchar_t;
void intchar_copy(void *_dst, const void *_src) {
intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
dst->a = src->a;
dst->s = src->s ? strdup(src->s) : NULL;
}
void intchar_dtor(void *_elt) {
intchar_t *elt = (intchar_t*)_elt;
if (elt->s) free(elt->s);
}
UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};
int main() {
UT_array *intchars;
intchar_t ic, *p;
utarray_new(intchars, &intchar_icd);
ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic);
ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic);
p=NULL;
while( (p=(intchar_t*)utarray_next(intchars,p))) {
printf("%d %s\n", p->a, (p->s ? p->s : "null"));
}
utarray_free(intchars);
return 0;
}
-------------------------------------------------------------------------------
[[operations]]
Reference
---------
This table lists all the utarray operations. These are loosely based on the C++
vector class.
Operations
~~~~~~~~~~
[width="100%",cols="50<m,40<",grid="none",options="none"]
|===============================================================================
| utarray_new(UT_array *a, UT_icd *icd)| allocate a new array
| utarray_free(UT_array *a) | free an allocated array
| utarray_init(UT_array *a,UT_icd *icd)| init an array (non-alloc)
| utarray_done(UT_array *a) | dispose of an array (non-allocd)
| utarray_reserve(UT_array *a,int n) | ensure space available for 'n' more elements
| utarray_push_back(UT_array *a,void *p) | push element p onto a
| utarray_pop_back(UT_array *a) | pop last element from a
| utarray_extend_back(UT_array *a) | push empty element onto a
| utarray_len(UT_array *a) | get length of a
| utarray_eltptr(UT_array *a,int j) | get pointer of element from index
| utarray_eltidx(UT_array *a,void *e) | get index of element from pointer
| utarray_insert(UT_array *a,void *p, int j) | insert element p to index j
| utarray_inserta(UT_array *a,UT_array *w, int j) | insert array w into array a at index j
| utarray_resize(UT_array *dst,int num) | extend or shrink array to num elements
| utarray_concat(UT_array *dst,UT_array *src) | copy src to end of dst array
| utarray_erase(UT_array *a,int pos,int len) | remove len elements from a[pos]..a[pos+len-1]
| utarray_clear(UT_array *a) | clear all elements from a, setting its length to zero
| utarray_sort(UT_array *a,cmpfcn *cmp) | sort elements of a using comparison function
| utarray_find(UT_array *a,void *v, cmpfcn *cmp) | find element v in utarray (must be sorted)
| utarray_front(UT_array *a) | get first element of a
| utarray_next(UT_array *a,void *e) | get element of a following e (front if e is NULL)
| utarray_prev(UT_array *a,void *e) | get element of a before e (back if e is NULL)
| utarray_back(UT_array *a) | get last element of a
|===============================================================================
Notes
~~~~~
1. `utarray_new` and `utarray_free` are used to allocate a new array and free it,
while `utarray_init` and `utarray_done` can be used if the UT_array is already
allocated and just needs to be initialized or have its internal resources
freed.
2. `utarray_reserve` takes the "delta" of elements to reserve (not the total
desired capacity of the array-- this differs from the C++ STL "reserve" notion)
3. `utarray_sort` expects a comparison function having the usual `strcmp` -like
convention where it accepts two elements (a and b) and returns a negative
value if a precedes b, 0 if a and b sort equally, and positive if b precedes a.
This is an example of a comparison function:
int intsort(const void *a,const void*b) {
int _a = *(int*)a;
int _b = *(int*)b;
return _a - _b;
}
4. `utarray_find` uses a binary search to locate an element having a certain value
according to the given comparison function. The utarray must be first sorted
using the same comparison function. An example of using `utarray_find` with
a utarray of strings is included in `tests/test61.c`.
5. A 'pointer' to a particular element (obtained using `utarray_eltptr` or
`utarray_front`, `utarray_next`, `utarray_prev`, `utarray_back`) becomes invalid whenever
another element is inserted into the utarray. This is because the internal
memory management may need to `realloc` the element storage to a new address.
For this reason, it's usually better to refer to an element by its integer
'index' in code whose duration may include element insertion.
// vim: set nowrap syntax=asciidoc:

View File

@ -1,234 +0,0 @@
/*
Copyright (c) 2008-2012, Troy D. Hanson http://uthash.sourceforge.net
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
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.
*/
/* a dynamic array implementation using macros
* see http://uthash.sourceforge.net/utarray
*/
#ifndef UTARRAY_H
#define UTARRAY_H
#define UTARRAY_VERSION 1.9.6
#ifdef __GNUC__
#define _UNUSED_ __attribute__ ((__unused__))
#else
#define _UNUSED_
#endif
#include <stddef.h> /* size_t */
#include <string.h> /* memset, etc */
#include <stdlib.h> /* exit */
// FIXME: this needs to be checked: we need to handle OOM properly instead of just exiting
#define oom() system_exit(-1)
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
typedef struct {
unsigned i,n;/* i: index of next available slot, n: num slots */
UT_icd icd; /* initializer, copy and destructor functions */
char *d; /* n slots of size icd->sz*/
} UT_array;
#define utarray_init(a,_icd) do { \
memset(a,0,sizeof(UT_array)); \
(a)->icd=*_icd; \
} while(0)
#define utarray_done(a) do { \
if ((a)->n) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
free((a)->d); \
} \
(a)->n=0; \
} while(0)
#define utarray_new(a,_icd) do { \
a=(UT_array*)malloc(sizeof(UT_array)); \
utarray_init(a,_icd); \
} while(0)
#define utarray_free(a) do { \
utarray_done(a); \
free(a); \
} while(0)
#define utarray_reserve(a,by) do { \
if (((a)->i+by) > ((a)->n)) { \
while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \
} \
} while(0)
#define utarray_push_back(a,p) do { \
utarray_reserve(a,1); \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \
else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \
} while(0)
#define utarray_pop_back(a) do { \
if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \
else { (a)->i--; } \
} while(0)
#define utarray_extend_back(a) do { \
utarray_reserve(a,1); \
if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \
else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \
(a)->i++; \
} while(0)
#define utarray_len(a) ((a)->i)
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) )))
#define utarray_insert(a,p,j) do { \
utarray_reserve(a,1); \
if (j > (a)->i) break; \
if ((j) < (a)->i) { \
memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \
else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \
(a)->i++; \
} while(0)
#define utarray_inserta(a,w,j) do { \
if (utarray_len(w) == 0) break; \
if (j > (a)->i) break; \
utarray_reserve(a,utarray_len(w)); \
if ((j) < (a)->i) { \
memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
_utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { \
size_t _ut_i; \
for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
(a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \
} \
} else { \
memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
utarray_len(w)*((a)->icd.sz)); \
} \
(a)->i += utarray_len(w); \
} while(0)
#define utarray_resize(dst,num) do { \
size_t _ut_i; \
if (dst->i > (size_t)(num)) { \
if ((dst)->icd.dtor) { \
for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \
(dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \
} \
} \
} else if (dst->i < (size_t)(num)) { \
utarray_reserve(dst,num-dst->i); \
if ((dst)->icd.init) { \
for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \
(dst)->icd.init(utarray_eltptr(dst,_ut_i)); \
} \
} else { \
memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \
} \
} \
dst->i = num; \
} while(0)
#define utarray_concat(dst,src) do { \
utarray_inserta((dst),(src),utarray_len(dst)); \
} while(0)
#define utarray_erase(a,pos,len) do { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < len; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \
} \
} \
if ((a)->i > (pos+len)) { \
memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \
(((a)->i)-(pos+len))*((a)->icd.sz)); \
} \
(a)->i -= (len); \
} while(0)
#define utarray_renew(a,u) do { \
if (a) utarray_clear(a); \
else utarray_new((a),(u)); \
} while(0)
#define utarray_clear(a) do { \
if ((a)->i > 0) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
(a)->i = 0; \
} \
} while(0)
#define utarray_sort(a,cmp) do { \
qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \
} while(0)
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1)
/* last we pre-define a few icd for common utarrays of ints and strings */
static void utarray_str_cpy(void *dst, const void *src) {
char **_src = (char**)src, **_dst = (char**)dst;
*_dst = (*_src == NULL) ? NULL : strdup(*_src);
}
static void utarray_str_dtor(void *elt) {
char **eltc = (char**)elt;
if (*eltc) free(*eltc);
}
static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL};
static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL};
#endif /* UTARRAY_H */