init
This commit is contained in:
commit
7fd631541b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build
|
77
CMakeLists.txt
Executable file
77
CMakeLists.txt
Executable file
@ -0,0 +1,77 @@
|
||||
cmake_minimum_required (VERSION 3.15)
|
||||
project (simplesync)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic")
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
option(BUILD_TESTS "Build all tests." OFF)
|
||||
|
||||
add_definitions(-D__LINUX_BUILD)
|
||||
|
||||
include_directories(src)
|
||||
|
||||
set(SRCS src/simplesync.hpp)
|
||||
|
||||
# Build the shared library
|
||||
|
||||
# Build the static library
|
||||
add_library(simplesync INTERFACE ${SRCS})
|
||||
set_target_properties(simplesync PROPERTIES OUTPUT_NAME "simplesync")
|
||||
target_include_directories(simplesync
|
||||
INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
|
||||
install(TARGETS simplesync
|
||||
EXPORT simplesyncTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
add_library(simplesync::simplesync ALIAS simplesync)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}/simplesync
|
||||
CACHE PATH "Location of header files" )
|
||||
set(SYSCONFIG_INSTALL_DIR ${CMAKE_INSTALL_SYSCONFDIR}/simplesync
|
||||
CACHE PATH "Location of configuration files" )
|
||||
#...
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file(cmake/simplesync-config.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/simplesync-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simplesync
|
||||
PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR)
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/simplesync-config-version.cmake
|
||||
VERSION 1.0.1
|
||||
COMPATIBILITY SameMajorVersion )
|
||||
install(EXPORT simplesyncTargets
|
||||
FILE simplesync-targets.cmake
|
||||
NAMESPACE simplesync::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simplesync )
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/simplesync-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/simplesync-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/simplesync )
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/simplesync.hpp
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/simplesync)
|
||||
export( EXPORT simplesyncTargets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/simplesync-targets.cmake)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG f8d7d77
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
enable_testing()
|
||||
|
||||
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR} .)
|
||||
|
||||
add_executable(${PROJECT_NAME}_test test/simplesync_test.cpp)
|
||||
target_link_libraries(${PROJECT_NAME}_test gtest gtest_main simplesync)
|
||||
endif()
|
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
||||
## Build
|
||||
```
|
||||
mkdir -p build && (cd build && cmake -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . ) && build/simplesync_test
|
||||
```
|
8
cmake/simplesync-config.cmake.in
Normal file
8
cmake/simplesync-config.cmake.in
Normal file
@ -0,0 +1,8 @@
|
||||
@PACKAGE_INIT@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/simplesync-targets.cmake")
|
||||
set_and_check(simplesync_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||
#set_and_check(simplesync_SYSCONFIG_DIR "@PACKAGE_SYSCONFIG_INSTALL_DIR@")
|
||||
set_and_check(simplesync_LIBRARY_DIRS "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@")
|
||||
set(simplesync_LIBRARIES "@PROJECT_NAME@")
|
||||
|
||||
check_required_components(simplesync)
|
326
src/simplesync.hpp
Normal file
326
src/simplesync.hpp
Normal file
@ -0,0 +1,326 @@
|
||||
#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
|
74
test/simplesync_test.cpp
Normal file
74
test/simplesync_test.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include <cstdint>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(simplesync_test, encode_decode) {
|
||||
|
||||
uint8_t buf[1024];
|
||||
unsigned int buf_size = sizeof(buf);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*#include <BSONPP.h>
|
||||
|
||||
TEST(simplesync_test, encode_decode) {
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
}*/
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user