wip
This commit is contained in:
parent
7fd631541b
commit
0afae184f4
9
.clang-format
Normal file
9
.clang-format
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
ColumnLimit: 100
|
||||
IndentWidth: 4
|
||||
AlignConsecutiveAssignments: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
...
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
build
|
||||
.cache
|
||||
|
@ -2,16 +2,15 @@ cmake_minimum_required (VERSION 3.15)
|
||||
project (simplesync)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
option(BUILD_TESTS "Build all tests." OFF)
|
||||
option(BUILD_PYLIB "Build python lib." OFF)
|
||||
|
||||
add_definitions(-D__LINUX_BUILD)
|
||||
|
||||
include_directories(src)
|
||||
include_directories(include)
|
||||
|
||||
set(SRCS src/simplesync.hpp)
|
||||
|
||||
# Build the shared library
|
||||
set(SRCS )#src/simplesync.hpp)
|
||||
|
||||
# Build the static library
|
||||
add_library(simplesync INTERFACE ${SRCS})
|
||||
@ -58,6 +57,20 @@ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/simplesync.hpp
|
||||
export( EXPORT simplesyncTargets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/simplesync-targets.cmake)
|
||||
|
||||
if (BUILD_PYLIB)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
pybind11
|
||||
GIT_REPOSITORY https://github.com/pybind/pybind11
|
||||
GIT_TAG v2.11.1
|
||||
)
|
||||
FetchContent_MakeAvailable(pybind11)
|
||||
find_package(pybind11 CONFIG)
|
||||
|
||||
pybind11_add_module(pysimplesync src/python_module.cpp)
|
||||
|
||||
endif()
|
||||
|
||||
if (BUILD_TESTS)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
|
287
LICENSE.txt
Normal file
287
LICENSE.txt
Normal file
@ -0,0 +1,287 @@
|
||||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
|
||||
below) which is provided under the terms of this Licence. Any use of the Work,
|
||||
other than as authorised under this Licence is prohibited (to the extent such
|
||||
use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This Licence
|
||||
does not define the extent of modification or dependence on the Original Work
|
||||
required in order to classify a work as a Derivative Work; this extent is
|
||||
determined by copyright law applicable in the country mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which is
|
||||
meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright vested
|
||||
in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case may
|
||||
be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make effective
|
||||
the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
|
||||
any patents held by the Licensor, to the extent necessary to make use of the
|
||||
rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates, in
|
||||
a notice following the copyright notice attached to the Work, a repository where
|
||||
the Source Code is easily and freely accessible for as long as the Licensor
|
||||
continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from
|
||||
any exception or limitation to the exclusive rights of the rights owners in the
|
||||
Work, of the exhaustion of those rights or of other applicable limitations
|
||||
thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and a
|
||||
copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of the
|
||||
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions on
|
||||
the Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed under
|
||||
a Compatible Licence, this Distribution or Communication can be done under the
|
||||
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
|
||||
Licence’ refers to the licences listed in the appendix attached to this Licence.
|
||||
Should the Licensee's obligations under the Compatible Licence conflict with
|
||||
his/her obligations under this Licence, the obligations of the Compatible
|
||||
Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the Work,
|
||||
the Licensee will provide a machine-readable copy of the Source Code or indicate
|
||||
a repository where this Source will be easily and freely available for as long
|
||||
as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings
|
||||
to the Work are owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’ basis
|
||||
and without warranties of any kind concerning the Work, including without
|
||||
limitation merchantability, fitness for a particular purpose, absence of defects
|
||||
or errors, accuracy, non-infringement of intellectual property rights other than
|
||||
copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition
|
||||
for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the use
|
||||
of the Work, including without limitation, damages for loss of goodwill, work
|
||||
stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such damage.
|
||||
However, the Licensor will be liable under statutory product liability laws as
|
||||
far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional agreement,
|
||||
defining obligations or services consistent with this Licence. However, if
|
||||
accepting obligations, You may act only on your own behalf and on your sole
|
||||
responsibility, not on behalf of the original Licensor or any other Contributor,
|
||||
and only if You agree to indemnify, defend, and hold each Contributor harmless
|
||||
for any liability incurred by, or claims asserted against such Contributor by
|
||||
the fact You have accepted any warranty or additional liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
|
||||
placed under the bottom of a window displaying the text of this Licence or by
|
||||
affirming consent in any other similar way, in accordance with the rules of
|
||||
applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this Licence,
|
||||
such as the use of the Work, the creation by You of a Derivative Work or the
|
||||
Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of electronic
|
||||
communication by You (for example, by offering to download the Work from a
|
||||
remote location) the distribution channel or media (for example, a website) must
|
||||
at least provide to the public the information requested by the applicable law
|
||||
regarding the Licensor, the Licence and the way it may be accessible, concluded,
|
||||
stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions of
|
||||
this Licence or updated versions of the Appendix, so far this is required and
|
||||
reasonable, without reducing the scope of the rights granted by the Licence. New
|
||||
versions of the Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty on
|
||||
the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive jurisdiction
|
||||
of the competent court where the Licensor resides or conducts its primary
|
||||
business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the above
|
||||
licences without producing a new version of the EUPL, as long as they provide
|
||||
the rights granted in Article 2 of this Licence and protect the covered Source
|
||||
Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a new
|
||||
EUPL version.
|
@ -1,4 +1,4 @@
|
||||
## Build
|
||||
```
|
||||
mkdir -p build && (cd build && cmake -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . ) && build/simplesync_test
|
||||
mkdir -p build && (cd build && cmake -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. && cmake --build . ) && build/simplesync_test
|
||||
```
|
||||
|
250
include/nonstd_functional.hpp
Normal file
250
include/nonstd_functional.hpp
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2019 winterscar
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifdef _NONSTD_FUNCTIONAL_HHP
|
||||
#define _NONSTD_FUNCTIONAL_HHP
|
||||
#include <Arduino.h>
|
||||
// extern void * operator new(size_t size, void * ptr);
|
||||
void* operator new(size_t size, void* ptr) {
|
||||
return ptr;
|
||||
}
|
||||
namespace nonstd {
|
||||
|
||||
template <class T>
|
||||
struct tag {
|
||||
using type = T;
|
||||
};
|
||||
template <class Tag>
|
||||
using type_t = typename Tag::type;
|
||||
|
||||
using size_t = decltype(sizeof(int));
|
||||
|
||||
// move
|
||||
|
||||
template <class T>
|
||||
T&& move(T& t) {
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
// forward
|
||||
|
||||
template <class T>
|
||||
struct remove_reference : tag<T> {};
|
||||
template <class T>
|
||||
struct remove_reference<T&> : tag<T> {};
|
||||
template <class T>
|
||||
using remove_reference_t = type_t<remove_reference<T>>;
|
||||
|
||||
template <class T>
|
||||
T&& forward(remove_reference_t<T>& t) {
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
template <class T>
|
||||
T&& forward(remove_reference_t<T>&& t) {
|
||||
return static_cast<T&&>(t);
|
||||
}
|
||||
|
||||
// decay
|
||||
|
||||
template <class T>
|
||||
struct remove_const : tag<T> {};
|
||||
template <class T>
|
||||
struct remove_const<T const> : tag<T> {};
|
||||
|
||||
template <class T>
|
||||
struct remove_volatile : tag<T> {};
|
||||
template <class T>
|
||||
struct remove_volatile<T volatile> : tag<T> {};
|
||||
|
||||
template <class T>
|
||||
struct remove_cv : remove_const<type_t<remove_volatile<T>>> {};
|
||||
|
||||
template <class T>
|
||||
struct decay3 : remove_cv<T> {};
|
||||
template <class R, class... Args>
|
||||
struct decay3<R(Args...)> : tag<R (*)(Args...)> {};
|
||||
template <class T>
|
||||
struct decay2 : decay3<T> {};
|
||||
template <class T, size_t N>
|
||||
struct decay2<T[N]> : tag<T*> {};
|
||||
|
||||
template <class T>
|
||||
struct decay : decay2<remove_reference_t<T>> {};
|
||||
|
||||
template <class T>
|
||||
using decay_t = type_t<decay<T>>;
|
||||
|
||||
// is_convertible
|
||||
|
||||
template <class T>
|
||||
T declval(); // no implementation
|
||||
|
||||
template <class T, T t>
|
||||
struct integral_constant {
|
||||
static constexpr T value = t;
|
||||
constexpr integral_constant(){};
|
||||
constexpr operator T() const { return value; }
|
||||
constexpr T operator()() const { return value; }
|
||||
};
|
||||
template <bool b>
|
||||
using bool_t = integral_constant<bool, b>;
|
||||
using true_type = bool_t<true>;
|
||||
using false_type = bool_t<false>;
|
||||
|
||||
template <class...>
|
||||
struct voider : tag<void> {};
|
||||
template <class... Ts>
|
||||
using void_t = type_t<voider<Ts...>>;
|
||||
|
||||
namespace details {
|
||||
template <template <class...> class Z, class, class... Ts>
|
||||
struct can_apply : false_type {};
|
||||
template <template <class...> class Z, class... Ts>
|
||||
struct can_apply<Z, void_t<Z<Ts...>>, Ts...> : true_type {};
|
||||
} // namespace details
|
||||
template <template <class...> class Z, class... Ts>
|
||||
using can_apply = details::can_apply<Z, void, Ts...>;
|
||||
|
||||
namespace details {
|
||||
template <class From, class To>
|
||||
using try_convert = decltype(To{declval<From>()});
|
||||
}
|
||||
template <class From, class To>
|
||||
struct is_convertible : can_apply<details::try_convert, From, To> {};
|
||||
template <>
|
||||
struct is_convertible<void, void> : true_type {};
|
||||
|
||||
// enable_if
|
||||
|
||||
template <bool, class = void>
|
||||
struct enable_if {};
|
||||
template <class T>
|
||||
struct enable_if<true, T> : tag<T> {};
|
||||
template <bool b, class T = void>
|
||||
using enable_if_t = type_t<enable_if<b, T>>;
|
||||
|
||||
// res_of
|
||||
|
||||
namespace details {
|
||||
template <class G, class... Args>
|
||||
using invoke_t = decltype(declval<G>()(declval<Args>()...));
|
||||
|
||||
template <class Sig, class = void>
|
||||
struct res_of {};
|
||||
template <class G, class... Args>
|
||||
struct res_of<G(Args...), void_t<invoke_t<G, Args...>>> : tag<invoke_t<G, Args...>> {};
|
||||
} // namespace details
|
||||
template <class Sig>
|
||||
using res_of = details::res_of<Sig>;
|
||||
template <class Sig>
|
||||
using res_of_t = type_t<res_of<Sig>>;
|
||||
|
||||
// aligned_storage
|
||||
|
||||
template <size_t size, size_t align>
|
||||
struct alignas(align) aligned_storage_t {
|
||||
char buff[size];
|
||||
};
|
||||
|
||||
// is_same
|
||||
|
||||
template <class A, class B>
|
||||
struct is_same : false_type {};
|
||||
template <class A>
|
||||
struct is_same<A, A> : true_type {};
|
||||
|
||||
template <class Sig, size_t sz, size_t algn>
|
||||
struct small_task;
|
||||
|
||||
template <class R, class... Args, size_t sz, size_t algn>
|
||||
struct small_task<R(Args...), sz, algn> {
|
||||
struct vtable_t {
|
||||
void (*mover)(void* src, void* dest);
|
||||
void (*destroyer)(void*);
|
||||
R (*invoke)(void const* t, Args&&... args);
|
||||
template <class T>
|
||||
static vtable_t const* get() {
|
||||
static const vtable_t table = {
|
||||
[](void* src, void* dest) { new (dest) T(move(*static_cast<T*>(src))); },
|
||||
[](void* t) { static_cast<T*>(t)->~T(); },
|
||||
[](void const* t, Args&&... args) -> R { return (*static_cast<T const*>(t))(forward<Args>(args)...); }};
|
||||
return &table;
|
||||
}
|
||||
};
|
||||
vtable_t const* table = nullptr;
|
||||
aligned_storage_t<sz, algn> data;
|
||||
template <class F, class dF = decay_t<F>, enable_if_t<!is_same<dF, small_task>{}>* = nullptr,
|
||||
enable_if_t<is_convertible<res_of_t<dF&(Args...)>, R>{}>* = nullptr>
|
||||
small_task(F&& f) : table(vtable_t::template get<dF>()) {
|
||||
static_assert(sizeof(dF) <= sz, "object too large");
|
||||
static_assert(alignof(dF) <= algn, "object too aligned");
|
||||
new (&data) dF(forward<F>(f));
|
||||
}
|
||||
~small_task() {
|
||||
if (table) table->destroyer(&data);
|
||||
}
|
||||
small_task(const small_task& o) : table(o.table) { data = o.data; }
|
||||
small_task(small_task&& o) : table(o.table) {
|
||||
if (table) table->mover(&o.data, &data);
|
||||
}
|
||||
small_task() {}
|
||||
small_task& operator=(const small_task& o) {
|
||||
this->~small_task();
|
||||
new (this) small_task(move(o));
|
||||
return *this;
|
||||
}
|
||||
small_task& operator=(small_task&& o) {
|
||||
this->~small_task();
|
||||
new (this) small_task(move(o));
|
||||
return *this;
|
||||
}
|
||||
explicit operator bool() const { return table; }
|
||||
R operator()(Args... args) const { return table->invoke(&data, forward<Args>(args)...); }
|
||||
};
|
||||
|
||||
template <class R, class... Args, size_t sz, size_t algn>
|
||||
inline bool operator==(const small_task<R(Args...), sz, algn>& __f, nullptr_t) {
|
||||
return !static_cast<bool>(__f);
|
||||
}
|
||||
|
||||
/// @overload
|
||||
template <class R, class... Args, size_t sz, size_t algn>
|
||||
inline bool operator==(nullptr_t, const small_task<R(Args...), sz, algn>& __f) {
|
||||
return !static_cast<bool>(__f);
|
||||
}
|
||||
|
||||
template <class R, class... Args, size_t sz, size_t algn>
|
||||
inline bool operator!=(const small_task<R(Args...), sz, algn>& __f, nullptr_t) {
|
||||
return static_cast<bool>(__f);
|
||||
}
|
||||
|
||||
/// @overload
|
||||
template <class R, class... Args, size_t sz, size_t algn>
|
||||
inline bool operator!=(nullptr_t, const small_task<R(Args...), sz, algn>& __f) {
|
||||
return static_cast<bool>(__f);
|
||||
}
|
||||
|
||||
template <class Sig>
|
||||
using function = small_task<Sig, sizeof(void*) * 4, alignof(void*)>;
|
||||
} // namespace nonstd
|
||||
|
||||
#endif /* _NONSTD_FUNCTIONAL_HHP */
|
349
include/simplesync.hpp
Normal file
349
include/simplesync.hpp
Normal file
@ -0,0 +1,349 @@
|
||||
#ifndef SIMPLESYNC_HPP
|
||||
#define SIMPLESYNC_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#ifdef __AVR__
|
||||
#include "include/nonstd_functional.hpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace simplesync {
|
||||
|
||||
enum error_t {
|
||||
BUFFER_OVERFLOW = (-1),
|
||||
INVALID_COMMAND = (-2),
|
||||
ID_INVALID = (-3),
|
||||
OUT_OF_MEMORY = (-4),
|
||||
};
|
||||
|
||||
inline int cobs_encode(uint8_t *buf_in, unsigned int buf_in_size, uint8_t *buf_out,
|
||||
unsigned int buf_out_size) {
|
||||
unsigned int i_out = 1;
|
||||
unsigned int last0 = 0;
|
||||
for (unsigned int i_in = 0; i_in < buf_in_size; i_in += 1, i_out += 1) {
|
||||
if (i_out >= buf_out_size) return OUT_OF_MEMORY;
|
||||
if ((buf_in[i_in] != 0x00) && ((i_out - last0) < 0xFF)) {
|
||||
buf_out[i_out] = buf_in[i_in];
|
||||
} else {
|
||||
buf_out[last0] = i_out - last0;
|
||||
last0 = i_out;
|
||||
}
|
||||
}
|
||||
buf_out[last0] = i_out - last0;
|
||||
buf_out[i_out] = 0x00;
|
||||
return i_out + 1;
|
||||
}
|
||||
|
||||
inline int cobs_decode(uint8_t *buf_in, unsigned int &buf_in_size_used, uint8_t *buf_out,
|
||||
unsigned int &buf_out_size_used) {
|
||||
unsigned int i_out = 0;
|
||||
unsigned int next0 = 0;
|
||||
bool skip_next0 = true;
|
||||
unsigned int i_in;
|
||||
for (i_in = 0; i_in < buf_in_size_used || buf_in[i_in] == 0x00; i_in += 1) {
|
||||
if (i_out >= buf_out_size_used) {
|
||||
buf_out_size_used = i_out;
|
||||
buf_in_size_used = i_in;
|
||||
return OUT_OF_MEMORY;
|
||||
}
|
||||
if (next0 == i_in) {
|
||||
if (buf_in[i_in] == 0x00) break;
|
||||
if (!skip_next0) buf_out[i_out++] = 0x00;
|
||||
next0 = i_in + buf_in[i_in];
|
||||
skip_next0 = (next0 - i_in) == 0xFF;
|
||||
} else {
|
||||
buf_out[i_out++] = buf_in[i_in];
|
||||
}
|
||||
}
|
||||
buf_out_size_used = i_out;
|
||||
buf_in_size_used = i_in;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline unsigned int encode_zigzag(int input) {
|
||||
return (input << 1) ^ (input >> (sizeof(int) * 8 - 1));
|
||||
}
|
||||
|
||||
inline unsigned int decode_zigzag(int input) { return (input >> 1) ^ (-(input & 1)); }
|
||||
|
||||
inline unsigned int encode_unsigned_varint(unsigned int input, uint8_t *out) {
|
||||
int offset = 0;
|
||||
while (input > 0x7F) {
|
||||
out[offset++] = input & 0x7F;
|
||||
input >>= 7;
|
||||
}
|
||||
out[offset++] = input | 0x80;
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline unsigned int encode_varint(int input, uint8_t *out) {
|
||||
return encode_unsigned_varint(encode_zigzag(input), out);
|
||||
}
|
||||
|
||||
inline unsigned int decode_unsigned_varint(uint8_t *input, unsigned int &out) {
|
||||
int offset = 0;
|
||||
out = 0;
|
||||
for (;; offset += 1) {
|
||||
out |= ((unsigned int)(input[offset] & 0x7F)) << (7 * offset);
|
||||
if (input[offset] & 0x80) break;
|
||||
}
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
inline unsigned int decode_varint(uint8_t *input, int &out) {
|
||||
unsigned int out_uint;
|
||||
int offset = decode_unsigned_varint(input, out_uint);
|
||||
out = decode_zigzag(out_uint);
|
||||
return offset;
|
||||
}
|
||||
|
||||
template <typename time_t, typename timedelta_t, unsigned int BUF_SIZE = 256,
|
||||
unsigned int str_len_max = 32>
|
||||
class SimpleSync {
|
||||
public:
|
||||
class NumberInterface {
|
||||
public:
|
||||
time_t last_send;
|
||||
char *key;
|
||||
timedelta_t update_interval;
|
||||
bool send_requested = true;
|
||||
int value;
|
||||
NumberInterface(SimpleSync &s, char *key, timedelta_t update_interval)
|
||||
: key(key), update_interval(update_interval) {
|
||||
s.number_interfaces.push_back(this);
|
||||
}
|
||||
};
|
||||
|
||||
class StringInterface {
|
||||
public:
|
||||
time_t last_send;
|
||||
char *key;
|
||||
timedelta_t update_interval;
|
||||
bool send_requested = true;
|
||||
char value[str_len_max + 1];
|
||||
StringInterface(SimpleSync &s, char *key, timedelta_t update_interval)
|
||||
: key(key), update_interval(update_interval) {
|
||||
value[str_len_max] = 0x00;
|
||||
s.string_interfaces.push_back(this);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<NumberInterface *> number_interfaces;
|
||||
std::vector<StringInterface *> string_interfaces;
|
||||
|
||||
std::function<int(uint8_t *, unsigned int)> write_pkg;
|
||||
std::function<void(NumberInterface *&, char *, int)> number_update;
|
||||
std::function<void(StringInterface *&, char *, char *)> string_update;
|
||||
SimpleSync(std::function<int(uint8_t *, unsigned int)> write_pkg,
|
||||
std::function<void(NumberInterface *&, char *, int)> number_update,
|
||||
std::function<void(StringInterface *&, char *, char *)> string_update)
|
||||
: write_pkg(write_pkg), number_update(number_update), string_update(string_update) {}
|
||||
|
||||
int parse_pkg(uint8_t *buf, unsigned int buf_size) {
|
||||
for (unsigned int parsed = 0; parsed < buf_size;) {
|
||||
switch (buf[parsed]) {
|
||||
case 0x00: {
|
||||
for (auto ni : number_interfaces) ni->send_requested = true;
|
||||
for (auto ni : string_interfaces) ni->send_requested = true;
|
||||
} break;
|
||||
case 0x01: {
|
||||
char *key = (char *)&buf[parsed + 1];
|
||||
parsed += 1 + strlen(key) + 1;
|
||||
int value;
|
||||
parsed += decode_varint(&buf[parsed], value);
|
||||
NumberInterface *ni_ptr = nullptr;
|
||||
for (auto ni : number_interfaces) {
|
||||
if (strcmp(ni->key, key) == 0) {
|
||||
ni_ptr = ni;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (number_update) {
|
||||
number_update(ni_ptr, key, value);
|
||||
}
|
||||
if (ni_ptr) {
|
||||
ni_ptr->value = value;
|
||||
}
|
||||
} break;
|
||||
case 0x02: {
|
||||
char *key = (char *)&buf[parsed + 1];
|
||||
parsed += 1 + strlen(key) + 1;
|
||||
char *value = (char *)&buf[parsed];
|
||||
unsigned int strl = strlen(value) + 1;
|
||||
parsed += strl;
|
||||
StringInterface *stri_ptr = nullptr;
|
||||
for (StringInterface *si : string_interfaces) {
|
||||
if (strcmp(si->key, key) == 0) {
|
||||
stri_ptr = si;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (string_update) {
|
||||
string_update(stri_ptr, key, value);
|
||||
}
|
||||
if (stri_ptr) {
|
||||
memcpy(stri_ptr->value, value, str_len_max > strl ? strl : str_len_max);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// Error: Unknown datatype, ignore rest of the packet
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_all_interfaces() const {
|
||||
uint8_t buf[] = {0x00, 0x00};
|
||||
unsigned int buf_len_used = sizeof(buf);
|
||||
uint8_t cobs_buf[10];
|
||||
unsigned int cobs_buf_needed = cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
}
|
||||
|
||||
void update(time_t time) {
|
||||
uint8_t buf[BUF_SIZE];
|
||||
unsigned int buf_len_used = 0;
|
||||
for (auto ni : number_interfaces) {
|
||||
if (ni->send_requested || ni->last_send > time ||
|
||||
ni->last_send + ni->update_interval <= time) {
|
||||
unsigned int strl = strlen(ni->key) + 1;
|
||||
unsigned int buf_len_needed = 1 + strl + 8 * sizeof(int) / 7;
|
||||
if (BUF_SIZE < buf_len_needed) continue; // Error: update to large, skip interface
|
||||
if (BUF_SIZE < buf_len_used + buf_len_needed) {
|
||||
write_pkg(buf, buf_len_used);
|
||||
buf_len_used = 0;
|
||||
}
|
||||
buf[buf_len_used++] = 0x01;
|
||||
memcpy((char *)&buf[buf_len_used], ni->key, strl);
|
||||
buf_len_used += strl;
|
||||
buf_len_used += encode_varint(ni->value, &buf[buf_len_used]);
|
||||
ni->last_send = time;
|
||||
ni->send_requested = false;
|
||||
}
|
||||
}
|
||||
for (auto ni : string_interfaces) {
|
||||
if (ni->send_requested || ni->last_send > time ||
|
||||
ni->last_send + ni->update_interval <= time) {
|
||||
unsigned int strl = strlen(ni->key) + 1;
|
||||
unsigned int strl_value = strlen(ni->value) + 1;
|
||||
unsigned int buf_len_needed = 1 + strl + strl_value;
|
||||
buf_len_needed = buf_len_needed / 0xFF + 3; // COBS overhead
|
||||
if (BUF_SIZE < buf_len_needed) continue; // Error: update to large, skip interface
|
||||
if (BUF_SIZE < buf_len_used + buf_len_needed) {
|
||||
uint8_t cobs_buf[BUF_SIZE];
|
||||
unsigned int cobs_buf_needed =
|
||||
cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
buf_len_used = 0;
|
||||
}
|
||||
buf[buf_len_used++] = 0x02;
|
||||
memcpy((char *)&buf[buf_len_used], ni->key, strl);
|
||||
buf_len_used += strl;
|
||||
memcpy((char *)&buf[buf_len_used], ni->value, strl_value);
|
||||
buf_len_used += strl_value;
|
||||
ni->last_send = time;
|
||||
ni->send_requested = false;
|
||||
}
|
||||
}
|
||||
uint8_t cobs_buf[BUF_SIZE];
|
||||
unsigned int cobs_buf_needed = cobs_encode(buf, buf_len_used, cobs_buf, BUF_SIZE);
|
||||
write_pkg(cobs_buf, cobs_buf_needed);
|
||||
}
|
||||
|
||||
int parse_stream_buf(uint8_t *buf, unsigned int buf_size) {
|
||||
int parsed = 0;
|
||||
for (unsigned int i = 0; i < buf_size; i += 1) {
|
||||
if (buf[i] == 0x00) {
|
||||
unsigned int buf_used = buf_size - parsed;
|
||||
uint8_t packet_buf[BUF_SIZE];
|
||||
unsigned int packed_buf_used = sizeof(packet_buf);
|
||||
if (cobs_decode(buf + parsed, buf_used, packet_buf, packed_buf_used) == 0) {
|
||||
parse_pkg(packet_buf, packed_buf_used);
|
||||
}
|
||||
parsed = i + 1;
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t stream_buf[BUF_SIZE];
|
||||
unsigned int stream_buf_used = 0;
|
||||
void handle_stream_buf(unsigned int buf_new_size) {
|
||||
for (unsigned int i = stream_buf_used - buf_new_size; i < stream_buf_used; i += 1) {
|
||||
if (stream_buf[i] == 0x00) {
|
||||
unsigned int stream_buf_pkg_delimiter = i;
|
||||
uint8_t packet_buf[BUF_SIZE];
|
||||
unsigned int packed_buf_used = sizeof(packet_buf);
|
||||
if (cobs_decode(stream_buf, stream_buf_pkg_delimiter, packet_buf,
|
||||
packed_buf_used) == 0) {
|
||||
parse_pkg(packet_buf, packed_buf_used);
|
||||
}
|
||||
i += 1;
|
||||
memmove(stream_buf, stream_buf + i, i);
|
||||
stream_buf_used -= i;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int handle_stream(uint8_t *buf_new, unsigned int buf_new_size) {
|
||||
if (buf_new_size + stream_buf_used >= BUF_SIZE) {
|
||||
/* ERROR: buffer overflow -> discared data */
|
||||
stream_buf_used = 0;
|
||||
return BUFFER_OVERFLOW;
|
||||
}
|
||||
memcpy(stream_buf + stream_buf_used, buf_new, buf_new_size);
|
||||
stream_buf_used += buf_new_size;
|
||||
handle_stream_buf(buf_new_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ARDUINO
|
||||
SimpleSync(Print *ostream, std::function<void(NumberInterface *&, char *, int)> number_update,
|
||||
std::function<void(StringInterface *&, char *, char *)> string_update)
|
||||
: write_pkg([ostream](uint8_t *buf, unsigned int buf_size) {
|
||||
if (ostream) {
|
||||
ostream->write(buf, buf_size);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}),
|
||||
number_update(number_update), string_update(string_update) {}
|
||||
int handle_stream(Stream &in_stream) {
|
||||
unsigned int buf_new_size = in_stream.available();
|
||||
if (buf_new_size + stream_buf_used > BUF_SIZE) {
|
||||
/* ERROR: buffer overflow -> discared data */
|
||||
stream_buf_used = 0;
|
||||
return BUFFER_OVERFLOW;
|
||||
}
|
||||
in_stream.readBytes(stream_buf + stream_buf_used, buf_new_size);
|
||||
handle_stream_buf(buf_new_size);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename time_t, typename timedelta_t, unsigned int buf_len = 256,
|
||||
unsigned int str_len_max = 32>
|
||||
class SimpleSyncStatic : public SimpleSync<time_t, timedelta_t, buf_len, str_len_max> {
|
||||
public:
|
||||
SimpleSyncStatic(std::function<int(uint8_t *, unsigned int)> write_pkg)
|
||||
: SimpleSync<time_t, timedelta_t, buf_len, str_len_max>(write_pkg, nullptr, nullptr){};
|
||||
#ifdef ARDUINO
|
||||
SimpleSyncStatic(Print *ostream)
|
||||
: SimpleSync<time_t, timedelta_t, buf_len, str_len_max>(ostream, nullptr, nullptr) {}
|
||||
#endif
|
||||
};
|
||||
} // namespace simplesync
|
||||
#endif // SIMPLESYNC_HPP
|
29
library.json
Normal file
29
library.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "SimpleSync",
|
||||
"version": "0.0.1",
|
||||
"description": "simple messaging protocol",
|
||||
"repository":
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://git.nilsschulte.de/nils/simplesync.git"
|
||||
},
|
||||
"authors":
|
||||
[
|
||||
{
|
||||
"name": "Nils Schulte",
|
||||
"email": "git@nilsschulte.de",
|
||||
"url": "https://nilsschulte.de/",
|
||||
"maintainer": "true"
|
||||
}
|
||||
],
|
||||
"license": "EUPL-1.2",
|
||||
"homepage": "https://git.nilsschulte.de/nils/simplesync/",
|
||||
"build": {
|
||||
"srcFilter": [
|
||||
"-<python_module.cpp>",
|
||||
"-<test>"
|
||||
]
|
||||
},
|
||||
"frameworks": "*",
|
||||
"platforms": "*"
|
||||
}
|
18
src/python_module.cpp
Normal file
18
src/python_module.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include <chrono>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/chrono.h>
|
||||
|
||||
#include "simplesync.hpp"
|
||||
|
||||
namespace py = pybind11;
|
||||
typedef simplesync::SimpleSync<std::chrono::time_point<std::chrono::steady_clock>,
|
||||
std::chrono::nanoseconds>
|
||||
SimpleS;
|
||||
|
||||
|
||||
PYBIND11_MODULE(simplesync, m) {
|
||||
py::class_<SimpleS>(m, "SimpleSync")
|
||||
.def(py::init([]() { return std::unique_ptr<SimpleS>(new SimpleS(nullptr,nullptr,nullptr)); }))
|
||||
.def(update);
|
||||
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
#ifndef SIMPLESYNC_HPP
|
||||
#define SIMPLESYNC_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace simplesync {
|
||||
typedef uint32_t id_t;
|
||||
enum error_t {
|
||||
BUFFER_OVERFLOW = (-1),
|
||||
INVALID_COMMAND = (-2),
|
||||
ID_INVALID = (-3),
|
||||
OUT_OF_MEMORY = (-4),
|
||||
};
|
||||
enum command_t {
|
||||
DESCRIBE_IF = 0,
|
||||
UPDATE_DESCTIPTION = 1,
|
||||
REQUEST_DATA = 2,
|
||||
UPDATE_DATA = 3,
|
||||
};
|
||||
enum data_type_t {
|
||||
BOOL = 0,
|
||||
STATE = 1,
|
||||
NUMBER = 2,
|
||||
UNIT = 3,
|
||||
STRING = 4,
|
||||
};
|
||||
|
||||
inline unsigned int encode_zigzag(int input) {
|
||||
return (input << 1) ^ (input >> (sizeof(int) * 8 - 1));
|
||||
}
|
||||
|
||||
inline unsigned int decode_zigzag(int input) {
|
||||
return (input >> 1) ^ (-(input & 1));
|
||||
}
|
||||
|
||||
inline unsigned int encode_unsigned_varint(unsigned int input, uint8_t *out) {
|
||||
int offset = 0;
|
||||
while (input > 0x7F) {
|
||||
out[offset++] = input & 0x7F;
|
||||
input >>= 7;
|
||||
}
|
||||
out[offset++] = input | 0x80;
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline unsigned int encode_varint(int input, uint8_t *out) {
|
||||
return encode_unsigned_varint(encode_zigzag(input), out);
|
||||
}
|
||||
|
||||
inline unsigned int decode_unsigned_varint(uint8_t *input, unsigned int &out) {
|
||||
int offset = 0;
|
||||
out = 0;
|
||||
for (;; offset += 1) {
|
||||
out |= ((unsigned int)(input[offset] & 0x7F)) << (7 * offset);
|
||||
if (input[offset] & 0x80)
|
||||
break;
|
||||
}
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
inline unsigned int decode_varint(uint8_t *input, int &out) {
|
||||
unsigned int out_uint;
|
||||
int offset = decode_unsigned_varint(input, out_uint);
|
||||
out = decode_zigzag(out_uint);
|
||||
return offset;
|
||||
}
|
||||
|
||||
class SimpleSync;
|
||||
class Interface {
|
||||
public:
|
||||
bool sync_requested = false;
|
||||
bool description_requested = false;
|
||||
|
||||
protected:
|
||||
friend class SimpleSync;
|
||||
Interface *next = nullptr;
|
||||
char *name;
|
||||
bool allocated_name = false;
|
||||
id_t idx;
|
||||
data_type_t data_type;
|
||||
Interface(const char *name, data_type_t data_type, SimpleSync &sync);
|
||||
int describe(uint8_t *buf, unsigned int buf_size) {
|
||||
int offset = 0;
|
||||
offset += encode_unsigned_varint(idx, buf + offset);
|
||||
offset += encode_unsigned_varint(data_type, buf + offset);
|
||||
unsigned int name_len = strlen(name);
|
||||
offset += encode_unsigned_varint(name_len, buf + offset);
|
||||
if (offset + name_len >= buf_size)
|
||||
return BUFFER_OVERFLOW;
|
||||
memcpy(buf + offset, name, name_len);
|
||||
offset += name_len;
|
||||
return offset;
|
||||
};
|
||||
|
||||
int parse_description(uint8_t *buf) {
|
||||
int offset = 0;
|
||||
offset += decode_unsigned_varint(buf + offset, idx);
|
||||
|
||||
unsigned int data_type_int;
|
||||
offset += decode_unsigned_varint(buf + offset, data_type_int);
|
||||
data_type = data_type_t(data_type_int);
|
||||
|
||||
unsigned int name_len;
|
||||
offset += decode_unsigned_varint(buf + offset, name_len);
|
||||
name = (char *)realloc((void *)name, name_len);
|
||||
if (name == nullptr)
|
||||
return OUT_OF_MEMORY;
|
||||
allocated_name = true;
|
||||
memcpy(name, buf + offset, name_len);
|
||||
offset += name_len;
|
||||
return offset;
|
||||
};
|
||||
|
||||
virtual ~Interface() { free(name); };
|
||||
|
||||
public:
|
||||
// static int parse_command_type(uint8_t* buf, command_t& command, unsigned
|
||||
// int& data_length) {
|
||||
// command = command_t(buf[0]);
|
||||
// int command_size = 1;
|
||||
// switch (command_t(buf[0])) {
|
||||
// case (STATE):
|
||||
// case (STRING):
|
||||
// case (INTERFACE):
|
||||
// command_size += sizeof(uint32_t);
|
||||
// memcpy(&data_length, &buf[1], sizeof(uint32_t)); // FIXME():
|
||||
// handle endianess break;
|
||||
// case (REQUEST_IF):
|
||||
// data_length = 0;
|
||||
// break;
|
||||
// case (BOOLEAN):
|
||||
// data_length = 1;
|
||||
// break;
|
||||
// case (NUMBER32):
|
||||
// data_length = 4;
|
||||
// break;
|
||||
// case (NUMBER64):
|
||||
// data_length = 8;
|
||||
// break;
|
||||
// }
|
||||
// return command_size;
|
||||
// switch (command) {
|
||||
// case(DESCRIBE_IF):
|
||||
// break;
|
||||
// case(UPDATE_DATA):
|
||||
// break;
|
||||
// case(REQUEST_DATA):
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
virtual int update(uint8_t *buf) = 0;
|
||||
virtual int write_update(uint8_t *buf, unsigned int buf_size) = 0;
|
||||
};
|
||||
|
||||
class NumberPointerInterface : public Interface {
|
||||
int *pointer = nullptr;
|
||||
|
||||
public:
|
||||
NumberPointerInterface(const char *name, int *pointer, SimpleSync &sync)
|
||||
: Interface(name, data_type_t::NUMBER, sync), pointer(pointer){};
|
||||
virtual int update(uint8_t *buf) { return decode_varint(buf, *pointer); };
|
||||
virtual int write_update(uint8_t *buf, unsigned int buf_size) {
|
||||
if (buf_size < sizeof(int) * 8 / 7)
|
||||
return BUFFER_OVERFLOW;
|
||||
return encode_varint(*pointer, buf);
|
||||
};
|
||||
};
|
||||
|
||||
inline int parse_command(uint8_t *buf, command_t &command) {
|
||||
command = command_t(buf[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int write_command(uint8_t *buf, command_t command) {
|
||||
buf[0] = (uint8_t)command;
|
||||
return 1;
|
||||
}
|
||||
|
||||
class SimpleSync {
|
||||
friend class Interface;
|
||||
Interface *first = nullptr;
|
||||
|
||||
public:
|
||||
unsigned int num_interfaces() {
|
||||
unsigned int ret = 0;
|
||||
for (Interface *c = first; c != nullptr; c = c->next)
|
||||
ret += 1;
|
||||
return ret;
|
||||
}
|
||||
int request_interface_descriptions(uint8_t *buf) {
|
||||
buf[0] = command_t::DESCRIBE_IF;
|
||||
buf[1] = 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int update(uint8_t *buf_in, unsigned int buf_size) {
|
||||
int offset = 0;
|
||||
|
||||
command_t command;
|
||||
offset += parse_command(&buf_in[offset], command);
|
||||
id_t if_id;
|
||||
offset += decode_unsigned_varint(&buf_in[offset], if_id);
|
||||
if ((unsigned int)offset >= buf_size)
|
||||
return BUFFER_OVERFLOW;
|
||||
if (if_id == 0) {
|
||||
if (command == command_t::DESCRIBE_IF) {
|
||||
for (Interface *current = first; current->next != nullptr;
|
||||
current = current->next) {
|
||||
current->description_requested = true;
|
||||
}
|
||||
} else if (command == command_t::REQUEST_DATA) {
|
||||
for (Interface *current = first; current->next != nullptr;
|
||||
current = current->next) {
|
||||
current->sync_requested = true;
|
||||
}
|
||||
} else if (command == command_t::UPDATE_DATA) {
|
||||
return ID_INVALID;
|
||||
} else {
|
||||
return INVALID_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
if (command == command_t::DESCRIBE_IF) {
|
||||
for (Interface *current = first; current->next != nullptr;
|
||||
current = current->next) {
|
||||
if (if_id == current->idx) {
|
||||
current->description_requested = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (command == command_t::REQUEST_DATA) {
|
||||
for (Interface *current = first; current->next != nullptr;
|
||||
current = current->next) {
|
||||
if (if_id == current->idx) {
|
||||
current->sync_requested = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (command == command_t::UPDATE_DATA) {
|
||||
for (Interface *current = first;; current = current->next) {
|
||||
if (current == nullptr)
|
||||
return ID_INVALID;
|
||||
|
||||
if (if_id == current->idx) {
|
||||
int data_offset = current->update(&buf_in[offset]);
|
||||
if (data_offset < 0) {
|
||||
return BUFFER_OVERFLOW;
|
||||
}
|
||||
offset += data_offset;
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
} else if (command == command_t::UPDATE_DESCTIPTION) {
|
||||
for (Interface *current = first;; current = current->next) {
|
||||
if (current == nullptr) {
|
||||
// FIXME new description
|
||||
return ID_INVALID;
|
||||
}
|
||||
|
||||
if (if_id == current->idx) {
|
||||
int data_offset = current->parse_description(&buf_in[offset]);
|
||||
if (data_offset < 0) {
|
||||
return BUFFER_OVERFLOW;
|
||||
}
|
||||
offset += data_offset;
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
int generate_message(uint8_t *buf, unsigned int buf_size) {
|
||||
if (first == nullptr)
|
||||
return 0;
|
||||
int offset = 0;
|
||||
int new_offset = 0;
|
||||
for (Interface *current = first; current != nullptr;
|
||||
current = current->next) {
|
||||
id_t if_id = current->idx;
|
||||
|
||||
if (current->description_requested) {
|
||||
offset += write_command(buf + offset, UPDATE_DESCTIPTION);
|
||||
offset += encode_unsigned_varint(if_id, buf + offset);
|
||||
new_offset = current->describe(buf + offset, buf_size - offset);
|
||||
} else if (current->sync_requested) {
|
||||
offset += write_command(buf + offset, UPDATE_DATA);
|
||||
offset += encode_unsigned_varint(if_id, buf + offset);
|
||||
new_offset = current->write_update(buf + offset, buf_size - offset);
|
||||
}
|
||||
if (new_offset > 0) {
|
||||
offset += new_offset;
|
||||
if (current->description_requested) {
|
||||
current->description_requested = false;
|
||||
} else if (current->sync_requested) {
|
||||
current->sync_requested = false;
|
||||
}
|
||||
return offset;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
inline Interface::Interface(const char *name, const data_type_t data_type,
|
||||
SimpleSync &sync)
|
||||
: name(nullptr), data_type(data_type) {
|
||||
unsigned int name_len = strlen(name);
|
||||
this->name = (char *)realloc((void *)this->name, name_len);
|
||||
memcpy(this->name, name, name_len);
|
||||
idx = sync.num_interfaces() + 16;
|
||||
if (sync.first == nullptr) {
|
||||
sync.first = this;
|
||||
return;
|
||||
}
|
||||
Interface *last = sync.first;
|
||||
for (; last->next != nullptr; last = last->next)
|
||||
;
|
||||
last->next = this;
|
||||
}
|
||||
} // namespace simplesync
|
||||
#endif // SIMPLESYNC_HPP
|
72
src/utils.h
Normal file
72
src/utils.h
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
inline int cobs_encode(uint8_t *buf_in, unsigned int buf_in_size, uint8_t *buf_out,
|
||||
unsigned int buf_out_size) {
|
||||
unsigned int i_out = 1;
|
||||
unsigned int last0 = 0;
|
||||
for (unsigned int i_in = 0; i_in < buf_in_size; i_in += 1, i_out += 1) {
|
||||
if (i_out >= buf_out_size) return OUT_OF_MEMORY;
|
||||
if ((buf_in[i_in] != 0x00) && ((i_out - last0) < 0xFF)) {
|
||||
buf_out[i_out] = buf_in[i_in];
|
||||
} else {
|
||||
buf_out[last0] = i_out - last0;
|
||||
last0 = i_out;
|
||||
}
|
||||
}
|
||||
buf_out[last0] = i_out - last0;
|
||||
buf_out[i_out] = 0x00;
|
||||
return i_out + 1;
|
||||
}
|
||||
|
||||
inline int cobs_decode(uint8_t *buf_in, unsigned int buf_in_size, uint8_t *buf_out,
|
||||
unsigned int buf_out_size) {
|
||||
unsigned int i_out = 0;
|
||||
unsigned int next0 = 0;
|
||||
bool skip_next0 = true;
|
||||
for (unsigned int i_in = 0; i_in < buf_in_size || buf_in[i_in] == 0x00; i_in += 1) {
|
||||
if (i_out >= buf_out_size) return OUT_OF_MEMORY;
|
||||
if (next0 == i_in) {
|
||||
if (!skip_next0) buf_out[i_out++] = 0x00;
|
||||
next0 = buf_in[i_in];
|
||||
skip_next0 = (next0 - i_in) == 0xFF;
|
||||
} else
|
||||
buf_out[i_out++] = buf_in[i_in];
|
||||
}
|
||||
return i_out-1;
|
||||
}
|
||||
|
||||
inline unsigned int encode_zigzag(int input) {
|
||||
return (input << 1) ^ (input >> (sizeof(int) * 8 - 1));
|
||||
}
|
||||
|
||||
inline unsigned int decode_zigzag(int input) { return (input >> 1) ^ (-(input & 1)); }
|
||||
|
||||
inline unsigned int encode_unsigned_varint(unsigned int input, uint8_t *out) {
|
||||
int offset = 0;
|
||||
while (input > 0x7F) {
|
||||
out[offset++] = input & 0x7F;
|
||||
input >>= 7;
|
||||
}
|
||||
out[offset++] = input | 0x80;
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline unsigned int encode_varint(int input, uint8_t *out) {
|
||||
return encode_unsigned_varint(encode_zigzag(input), out);
|
||||
}
|
||||
|
||||
inline unsigned int decode_unsigned_varint(uint8_t *input, unsigned int &out) {
|
||||
int offset = 0;
|
||||
out = 0;
|
||||
for (;; offset += 1) {
|
||||
out |= ((unsigned int)(input[offset] & 0x7F)) << (7 * offset);
|
||||
if (input[offset] & 0x80) break;
|
||||
}
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
inline unsigned int decode_varint(uint8_t *input, int &out) {
|
||||
unsigned int out_uint;
|
||||
int offset = decode_unsigned_varint(input, out_uint);
|
||||
out = decode_zigzag(out_uint);
|
||||
return offset;
|
||||
}
|
@ -1,74 +1,81 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "simplesync.hpp"
|
||||
|
||||
|
||||
TEST(simplesync_test, varint) {
|
||||
|
||||
uint8_t buf[1024];
|
||||
for (int i = -999999; i < 999999; i += 29) {
|
||||
int offset = simplesync::encode_varint(i, buf);
|
||||
ASSERT_LE(1, offset);
|
||||
for (int o = 0; o < offset - 1; o += 1)
|
||||
EXPECT_FALSE(buf[o] & 0x80)
|
||||
<< "offset:" << offset << " " << (uint32_t)(buf[o] & 0x7F);
|
||||
EXPECT_TRUE(buf[offset - 1] & 0x80)
|
||||
<< "offset:" << offset << " " << (uint32_t)(buf[offset - 1] & 0x7F);
|
||||
int out;
|
||||
offset = simplesync::decode_varint(buf, out);
|
||||
ASSERT_LE(1, offset);
|
||||
ASSERT_EQ(out, i);
|
||||
}
|
||||
uint8_t buf[1024];
|
||||
for (int i = -999999; i < 999999; i += 29) {
|
||||
int offset = simplesync::encode_varint(i, buf);
|
||||
ASSERT_LE(1, offset);
|
||||
for (int o = 0; o < offset - 1; o += 1)
|
||||
EXPECT_FALSE(buf[o] & 0x80) << "offset:" << offset << " " << (uint32_t)(buf[o] & 0x7F);
|
||||
EXPECT_TRUE(buf[offset - 1] & 0x80)
|
||||
<< "offset:" << offset << " " << (uint32_t)(buf[offset - 1] & 0x7F);
|
||||
int out;
|
||||
offset = simplesync::decode_varint(buf, out);
|
||||
ASSERT_LE(1, offset);
|
||||
ASSERT_EQ(out, i);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(simplesync_test, encode_decode) {
|
||||
TEST(simplesync_test, cobs_encode) {
|
||||
uint8_t buf_raw[] = {0x11, 0x22, 0x00, 0x33};
|
||||
uint8_t buf_coded[] = {0x03, 0x11, 0x22, 0x02, 0x33, 0x00};
|
||||
|
||||
uint8_t buf[1024];
|
||||
unsigned int buf_size = sizeof(buf);
|
||||
uint8_t buf[sizeof(buf_coded) + 1];
|
||||
unsigned int buf_used = simplesync::cobs_encode(buf_raw, sizeof(buf_raw), buf, sizeof(buf));
|
||||
ASSERT_EQ(buf_used, sizeof(buf_coded));
|
||||
ASSERT_EQ(0, memcmp(buf_coded, buf, sizeof(buf_coded)));
|
||||
|
||||
simplesync::SimpleSync s;
|
||||
|
||||
int number = 42;
|
||||
simplesync::NumberPointerInterface number_if("n1", &number, s);
|
||||
ASSERT_EQ(s.num_interfaces(), 1);
|
||||
|
||||
number_if.sync_requested = true;
|
||||
int buf_used = s.generate_message(buf, buf_size);
|
||||
ASSERT_LE(1, buf_used);
|
||||
|
||||
number = 0;
|
||||
|
||||
int buf_parsed = s.update(buf, buf_used);
|
||||
ASSERT_LE(0, buf_parsed);
|
||||
ASSERT_EQ(buf_parsed, buf_used);
|
||||
ASSERT_EQ(number, 42);
|
||||
unsigned int buf_out_used = sizeof(buf);
|
||||
unsigned int buf_in_used = sizeof(buf_coded);
|
||||
int error = simplesync::cobs_decode(buf_coded, buf_in_used, buf, buf_out_used);
|
||||
ASSERT_EQ(error, 0);
|
||||
ASSERT_EQ(buf_used, sizeof(buf_coded));
|
||||
ASSERT_EQ(buf_out_used, sizeof(buf_raw));
|
||||
ASSERT_EQ(0, memcmp(buf_raw, buf, sizeof(buf_raw)));
|
||||
}
|
||||
|
||||
/*#include <BSONPP.h>
|
||||
uint8_t stream_buf[1024];
|
||||
unsigned int buf_size = sizeof(stream_buf);
|
||||
unsigned int buf_used = 0;
|
||||
|
||||
TEST(simplesync_test, encode_decode) {
|
||||
buf_used = 0;
|
||||
typedef simplesync::SimpleSyncStatic<int, int, 999> S;
|
||||
S s([](uint8_t *buf, unsigned int buf_size) {
|
||||
memcpy(stream_buf + buf_used, buf, buf_size);
|
||||
buf_used += buf_size;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const char* test_str_key = "stringKey";
|
||||
const char* test_str_value = "Just a medium length string";
|
||||
// This is the buffer to be used by the library.
|
||||
uint8_t buffer[256];
|
||||
BSONPP doc(buffer, sizeof(buffer));
|
||||
doc.append(test_str_key, "Just a medium length string");
|
||||
doc.append("num", 10234234);
|
||||
S::NumberInterface n1(s, (char *)"n1", 0);
|
||||
S::StringInterface s1(s, (char *)"s1", 0);
|
||||
S::NumberInterface n2(s, (char *)"n2", 2);
|
||||
|
||||
// To read BSON from a buffer pass in the buffer, length and false. By default
|
||||
// when passed a buffer it clears the buffer and initialises a new BSON
|
||||
// object. Set false to avoid that.
|
||||
BSONPP parsed(buffer, sizeof(buffer), false);
|
||||
n1.value = 4;
|
||||
strcpy(s1.value, "hallo");
|
||||
n2.value = 2;
|
||||
|
||||
char *val;
|
||||
ASSERT_EQ(BSONPP_SUCCESS, parsed.get(test_str_key, &val));
|
||||
EXPECT_STREQ(val, test_str_value);
|
||||
|
||||
// ASSERT_TRUE(strcmp(decoded.str, "Hello") == 0);
|
||||
}*/
|
||||
for (int time = 0; time < 5; time += 1) {
|
||||
buf_used = 0;
|
||||
s.update(time);
|
||||
s.update(time);
|
||||
unsigned int parsed = 0;
|
||||
for (unsigned int i = 1; i <= buf_used; i += 1)
|
||||
parsed += s.parse_stream_buf(stream_buf + parsed, i - parsed);
|
||||
ASSERT_EQ(parsed, buf_used);
|
||||
}
|
||||
ASSERT_EQ(n1.value, 4);
|
||||
ASSERT_EQ(n2.value, 2);
|
||||
ASSERT_STREQ(s1.value,"hallo");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user