ruble 0.0.3.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gdbinit +21 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +96 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/CMakeLists.txt +4 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +98 -0
- data/LICENSE.txt +21 -0
- data/README.md +63 -0
- data/Rakefile +41 -0
- data/ext/ruble/.gitignore +5 -0
- data/ext/ruble/CMakeLists.txt +157 -0
- data/ext/ruble/RuBLEHelpers.cmake +240 -0
- data/ext/ruble/bindings/Adapter.cpp +193 -0
- data/ext/ruble/bindings/Adapter.hpp +85 -0
- data/ext/ruble/bindings/Characteristic.cpp +171 -0
- data/ext/ruble/bindings/Characteristic.hpp +132 -0
- data/ext/ruble/bindings/Descriptor.cpp +34 -0
- data/ext/ruble/bindings/Descriptor.hpp +69 -0
- data/ext/ruble/bindings/Peripheral.cpp +212 -0
- data/ext/ruble/bindings/Peripheral.hpp +108 -0
- data/ext/ruble/bindings/RuBLE.cpp +115 -0
- data/ext/ruble/bindings/Service.cpp +112 -0
- data/ext/ruble/bindings/Service.hpp +61 -0
- data/ext/ruble/bindings/common.hpp +48 -0
- data/ext/ruble/bindings/globals.cpp +43 -0
- data/ext/ruble/cmake.mk +62 -0
- data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
- data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
- data/ext/ruble/concerns/Rubyable.cpp +4 -0
- data/ext/ruble/concerns/Rubyable.hpp +46 -0
- data/ext/ruble/config.h.in +25 -0
- data/ext/ruble/containers/ByteArray.cpp +64 -0
- data/ext/ruble/containers/ByteArray.hpp +161 -0
- data/ext/ruble/containers/Callback.hpp +52 -0
- data/ext/ruble/containers/NamedBitSet.hpp +140 -0
- data/ext/ruble/containers/NamedBitSet.ipp +71 -0
- data/ext/ruble/extconf.rb +30 -0
- data/ext/ruble/management/Registry.cpp +63 -0
- data/ext/ruble/management/Registry.hpp +170 -0
- data/ext/ruble/management/RegistryFactory.hpp +113 -0
- data/ext/ruble/management/RubyQueue.cpp +152 -0
- data/ext/ruble/management/RubyQueue.hpp +69 -0
- data/ext/ruble/modularize.diff +28 -0
- data/ext/ruble/types/SimpleBLE.hpp +21 -0
- data/ext/ruble/types/declarations.hpp +91 -0
- data/ext/ruble/types/helpers.hpp +12 -0
- data/ext/ruble/types/ruby.hpp +36 -0
- data/ext/ruble/types/stl.hpp +41 -0
- data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
- data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
- data/ext/ruble/utils/async.cpp +10 -0
- data/ext/ruble/utils/async.hpp +76 -0
- data/ext/ruble/utils/containers.hpp +41 -0
- data/ext/ruble/utils/exception_handling.cpp +50 -0
- data/ext/ruble/utils/exception_handling.hpp +53 -0
- data/ext/ruble/utils/garbage_collection.cpp +82 -0
- data/ext/ruble/utils/garbage_collection.hpp +22 -0
- data/ext/ruble/utils/hash.cpp +83 -0
- data/ext/ruble/utils/hash.hpp +52 -0
- data/ext/ruble/utils/hexadecimal.hpp +116 -0
- data/ext/ruble/utils/human_type_names.hpp +38 -0
- data/ext/ruble/utils/inspection.cpp +24 -0
- data/ext/ruble/utils/inspection.hpp +108 -0
- data/ext/ruble/utils/ruby.hpp +103 -0
- data/ext/ruble/utils/ruby_context.hpp +73 -0
- data/lib/ruble/build/.rubocop.yml +19 -0
- data/lib/ruble/build/boost.rb +34 -0
- data/lib/ruble/build/cmake.rb +134 -0
- data/lib/ruble/build/core_ext.rb +5 -0
- data/lib/ruble/build/data/bundler.rb +24 -0
- data/lib/ruble/build/data/extension.rb +101 -0
- data/lib/ruble/build/data/os.rb +21 -0
- data/lib/ruble/build/data/rice.rb +24 -0
- data/lib/ruble/build/data.rb +22 -0
- data/lib/ruble/build/extconf.rb +76 -0
- data/lib/ruble/build/github_repo.rb +129 -0
- data/lib/ruble/build/simpleble.rb +56 -0
- data/lib/ruble/build.rb +28 -0
- data/lib/ruble/version.rb +7 -0
- data/lib/ruble.rb +46 -0
- data/lib/tasks/dev/dev_tasks.rb +130 -0
- data/lib/tasks/dev/pager.rb +218 -0
- data/lib/tasks/dev/paths.rb +30 -0
- data/lib/tasks/dev/state_hash.rb +65 -0
- data/lib/tasks/dev.rake +41 -0
- data/lib/tasks/simpleble.rake +29 -0
- data/sig/rubble.rbs +4 -0
- metadata +263 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
#cmakedefine EXTENSION_NAME "@EXTENSION_NAME@"
|
2
|
+
#cmakedefine EXTENSION_VERSION "@EXTENSION_VERSION@"
|
3
|
+
#cmakedefine RUBY_VERSION "@RUBY_VERSION@"
|
4
|
+
#cmakedefine RICE_VERSION "@RICE_VERSION@"
|
5
|
+
|
6
|
+
// TODO: defer to single point of truth re: min ruby version elsewhere.
|
7
|
+
// e.g. minimum version already in `.gemspec` and CMakeLists
|
8
|
+
|
9
|
+
#cmakedefine RUBLE_DEBUG 1
|
10
|
+
#cmakedefine HAVE_VALGRIND 1
|
11
|
+
|
12
|
+
#cmakedefine BOOST_STACKTRACE_USE_BACKTRACE 1
|
13
|
+
#cmakedefine BOOST_STACKTRACE_USE_ADDR2LINE 1
|
14
|
+
#cmakedefine BOOST_STACKTRACE_USE_BASIC 1
|
15
|
+
#cmakedefine BOOST_STACKTRACE_USE_WINDBG 1
|
16
|
+
#cmakedefine BOOST_STACKTRACE_USE_WINDBG_CACHED 1
|
17
|
+
#cmakedefine BOOST_STACKTRACE_USE_NOOP 1
|
18
|
+
#cmakedefine BOOST_STACKTRACE_LINK 1
|
19
|
+
#cmakedefine BOOST_STACKTRACE_ADDR2LINE_LOCATION "@BOOST_STACKTRACE_ADDR2LINE_LOCATION@"
|
20
|
+
#cmakedefine BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE "@BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE@"
|
21
|
+
|
22
|
+
#ifdef BOOST_STACKTRACE_LINK
|
23
|
+
#define BOOST_ENABLE_ASSERT_DEBUG_HANDLER 1
|
24
|
+
#endif
|
25
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#include "types/declarations.hpp"
|
2
|
+
#include "containers/ByteArray.hpp"
|
3
|
+
|
4
|
+
#include "utils/containers.hpp"
|
5
|
+
|
6
|
+
namespace RuBLE {
|
7
|
+
|
8
|
+
ByteArray::ByteArray(Object obj) : ByteArray() {
|
9
|
+
if (obj.is_a(rb_cByteArray)) {
|
10
|
+
*this = { *ByteArray_DO(obj).get() };
|
11
|
+
} else if(obj.is_a(rb_cNumeric)) {
|
12
|
+
*this = { rb_num2ulong(obj) };
|
13
|
+
} else if (obj.is_a(rb_cString) || obj.is_a(rb_cSymbol)) {
|
14
|
+
*this = {Rice::String(obj).str()};
|
15
|
+
} else if(obj.is_a(rb_cNilClass) || obj.is_a(rb_cTrueClass) || obj.is_a(rb_cFalseClass)) {
|
16
|
+
*this = { obj.test() };
|
17
|
+
} else {
|
18
|
+
throw std::invalid_argument("Cannot convert "s + obj.inspect().str() + " to a ByteArray");
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
ByteArray ByteArray::from_ruby(Object obj) {
|
23
|
+
return { std::move(obj) };
|
24
|
+
}
|
25
|
+
|
26
|
+
std::string ByteArray::to_s() const {
|
27
|
+
std::ostringstream oss;
|
28
|
+
oss << Utils::basic_object_inspect_start(*this) << " ";
|
29
|
+
oss << Utils::to_hex_data(_data);
|
30
|
+
oss << " (" << Utils::ruby_quote_string(_data) << ")>";
|
31
|
+
return oss.str();
|
32
|
+
}
|
33
|
+
|
34
|
+
std::string ByteArray::inspect() const { return to_s(); }
|
35
|
+
|
36
|
+
void ByteArray::ruby_mark() const {
|
37
|
+
Rubyable::ruby_mark();
|
38
|
+
}
|
39
|
+
|
40
|
+
void Init_ByteArray() {
|
41
|
+
rb_cByteArray = define_class_under<ByteArray>(rb_mRuBLE, "ByteArray")
|
42
|
+
.define_singleton_function("new", &ByteArray::from_ruby)
|
43
|
+
.define_method("get", &ByteArray::to_string)
|
44
|
+
.define_method("to_s", &ByteArray::to_s)
|
45
|
+
.define_method("inspect", &ByteArray::inspect)
|
46
|
+
.define_method("data", &ByteArray::data)
|
47
|
+
.define_method("to_i", &ByteArray::to_i<>)
|
48
|
+
.define_method("%", &ByteArray::operator%<>)
|
49
|
+
.define_method("&", &ByteArray::operator&<>)
|
50
|
+
.define_method("|", &ByteArray::operator|<>)
|
51
|
+
.define_method("~", &ByteArray::operator~)
|
52
|
+
.define_method("!", &ByteArray::operator!)
|
53
|
+
.define_method("<=>", &ByteArray::operator<=><>)
|
54
|
+
;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
//namespace Rice::detail {
|
59
|
+
// bool From_Ruby<RuBLE::ByteArray>::can_convert_from_ruby(VALUE obj) {
|
60
|
+
// return std::find(supported_ruby_types.cbegin(),
|
61
|
+
// supported_ruby_types.cend(),
|
62
|
+
// rb_type(obj)) != supported_ruby_types.cend();
|
63
|
+
// }
|
64
|
+
//}
|
@@ -0,0 +1,161 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "types/declarations.hpp"
|
4
|
+
#include "types/ruby.hpp"
|
5
|
+
#include "types/helpers.hpp"
|
6
|
+
#include "utils/containers.hpp"
|
7
|
+
#include "utils/hexadecimal.hpp"
|
8
|
+
#include "utils/human_type_names.hpp"
|
9
|
+
#include "concerns/Rubyable.hpp"
|
10
|
+
#include <utility>
|
11
|
+
#include <array>
|
12
|
+
#include <algorithm>
|
13
|
+
#include <compare>
|
14
|
+
#include <cstdlib>
|
15
|
+
#include <boost/endian/conversion.hpp>
|
16
|
+
|
17
|
+
namespace RuBLE {
|
18
|
+
template<typename T>
|
19
|
+
concept UnsignedIntegral = std::is_integral_v<T> && std::is_unsigned_v<T>;
|
20
|
+
|
21
|
+
// TODO: Need to test this out
|
22
|
+
class ByteArray : public Rubyable<ByteArray> {
|
23
|
+
public:
|
24
|
+
using DataObject = ByteArray_DO;
|
25
|
+
private:
|
26
|
+
SimpleBLE::ByteArray _data;
|
27
|
+
|
28
|
+
public:
|
29
|
+
using char_type = SimpleBLE::ByteArray::value_type;
|
30
|
+
using iterator_diff_type = typename std::iterator_traits<SimpleBLE::ByteArray::const_iterator>::difference_type;
|
31
|
+
|
32
|
+
static constexpr const std::size_t char_size = sizeof(char_type);
|
33
|
+
|
34
|
+
template<typename T>
|
35
|
+
static constexpr const std::size_t chars_per_val = ([]() -> std::size_t {
|
36
|
+
std::size_t quot = sizeof(T) / char_size;
|
37
|
+
std::size_t rem = sizeof(T) % char_size;
|
38
|
+
return quot + (rem > 0 ? 1 : 0);
|
39
|
+
})();
|
40
|
+
|
41
|
+
using default_integral_type = uint64_t;
|
42
|
+
|
43
|
+
[[nodiscard]] constexpr ByteArray();
|
44
|
+
constexpr ByteArray(SimpleBLE::ByteArray byteArray);
|
45
|
+
|
46
|
+
ByteArray(Rice::Object obj);
|
47
|
+
template<UnsignedIntegral T>
|
48
|
+
ByteArray(const T &data) : ByteArray() {
|
49
|
+
// TODO: modernize by using std::span
|
50
|
+
_data.assign(chars_per_val<T>, '\0');
|
51
|
+
UnsignedIntegral auto &dstVal = *reinterpret_cast<T *>(_data.data());
|
52
|
+
dstVal = boost::endian::native_to_little(data);
|
53
|
+
}
|
54
|
+
|
55
|
+
constexpr ByteArray(const char_type &chr);
|
56
|
+
constexpr ByteArray(const bool &b);
|
57
|
+
|
58
|
+
static ByteArray from_ruby(Rice::Object obj);
|
59
|
+
|
60
|
+
[[nodiscard]] constexpr std::size_t length() const { return _data.size(); }
|
61
|
+
[[nodiscard]] constexpr std::size_t byte_count() const { return length() * char_size; }
|
62
|
+
|
63
|
+
template<UnsignedIntegral T>
|
64
|
+
constexpr explicit operator T() const { return to_i<T>(); }
|
65
|
+
|
66
|
+
template<UnsignedIntegral T = default_integral_type>
|
67
|
+
constexpr T operator|(const T &other) const { return to_i<T>() | other; }
|
68
|
+
|
69
|
+
template<UnsignedIntegral T = default_integral_type>
|
70
|
+
constexpr T operator&(const T &other) const { return to_i<T>() & other; }
|
71
|
+
|
72
|
+
template<UnsignedIntegral T = default_integral_type>
|
73
|
+
constexpr T operator^(const T &other) const { return to_i<T>() ^ other; }
|
74
|
+
|
75
|
+
template<UnsignedIntegral T = default_integral_type>
|
76
|
+
constexpr T operator%(const T &other) const { return to_i<T>() % other; }
|
77
|
+
|
78
|
+
constexpr bool operator!() const { return to_i<>() != 0; }
|
79
|
+
|
80
|
+
constexpr auto operator~() const { return ~to_i<>(); }
|
81
|
+
|
82
|
+
template<typename T = default_integral_type>
|
83
|
+
constexpr int operator<=>(const T &other) const {
|
84
|
+
std::strong_ordering result = to_i<T>() <=> other;
|
85
|
+
if (result == std::strong_ordering::less) return -1;
|
86
|
+
if (result == std::strong_ordering::greater) return 1;
|
87
|
+
return 0;
|
88
|
+
}
|
89
|
+
|
90
|
+
constexpr std::strong_ordering operator<=>(const ByteArray &other) const {
|
91
|
+
return _data <=> other._data;
|
92
|
+
}
|
93
|
+
constexpr bool operator==(const ByteArray &other) const {
|
94
|
+
auto comparison = *this <=> other;
|
95
|
+
return comparison == std::strong_ordering::equivalent || comparison == std::strong_ordering::equal;
|
96
|
+
}
|
97
|
+
|
98
|
+
constexpr bool operator!=(const ByteArray &other) const { return !(*this == other); }
|
99
|
+
|
100
|
+
[[nodiscard]] constexpr std::string to_string() const { return _data; }
|
101
|
+
[[nodiscard]] std::string to_s() const;
|
102
|
+
[[nodiscard]] std::string inspect() const;
|
103
|
+
|
104
|
+
[[nodiscard]] constexpr const SimpleBLE::ByteArray &data() const { return _data; }
|
105
|
+
|
106
|
+
[[nodiscard]] constexpr const std::byte &operator[](iterator_diff_type pos) const {
|
107
|
+
assert(pos < byte_count() && pos >= 0);
|
108
|
+
const void *bytesVoid = _data.data();
|
109
|
+
const std::byte *bytes = static_cast<const std::byte*>(bytesVoid);
|
110
|
+
return bytes[pos];
|
111
|
+
}
|
112
|
+
|
113
|
+
constexpr operator const SimpleBLE::ByteArray &() const { return _data; }
|
114
|
+
// TODO: test me!
|
115
|
+
template<UnsignedIntegral T = default_integral_type>
|
116
|
+
[[nodiscard]] T to_i() const {
|
117
|
+
if (sizeof(T) < char_size * length()) {
|
118
|
+
static constexpr const auto errfmt = "Integer type %s holds only %ld bytes, "\
|
119
|
+
"while %s contains %ld bytes of data. Result will be truncated.";
|
120
|
+
rb_warn(errfmt, std::string(Utils::human_type_name<T>()).c_str(), sizeof(T),
|
121
|
+
std::string(Utils::human_type_name(*this)).c_str(), char_size * length());
|
122
|
+
}
|
123
|
+
const UnsignedIntegral auto &val = *reinterpret_cast<const T*>(_data.data());
|
124
|
+
return boost::endian::little_to_native(val);
|
125
|
+
}
|
126
|
+
|
127
|
+
void ruby_mark() const;
|
128
|
+
};
|
129
|
+
|
130
|
+
constexpr ByteArray::ByteArray() : _data(), Rubyable<ByteArray>() {}
|
131
|
+
|
132
|
+
constexpr ByteArray::ByteArray(SimpleBLE::ByteArray byteArray) :
|
133
|
+
_data(std::move(byteArray)), Rubyable<ByteArray>() {}
|
134
|
+
|
135
|
+
constexpr ByteArray::ByteArray(const ByteArray::char_type &chr) : ByteArray(std::string(chr, 1)) {}
|
136
|
+
|
137
|
+
constexpr ByteArray::ByteArray(const bool &b) : ByteArray(b ? '1' : '0') {}
|
138
|
+
|
139
|
+
|
140
|
+
void Init_ByteArray();
|
141
|
+
}
|
142
|
+
|
143
|
+
//namespace Rice::detail {
|
144
|
+
// template<>
|
145
|
+
// class From_Ruby<RuBLE::ByteArray> {
|
146
|
+
// public:
|
147
|
+
// static constexpr const auto supported_ruby_types =
|
148
|
+
// std::to_array({T_BIGNUM, T_FIXNUM, T_SYMBOL, T_STRING, T_NIL, T_TRUE, T_FALSE});
|
149
|
+
// static bool can_convert_from_ruby(VALUE obj);
|
150
|
+
// bool is_convertible(VALUE value) { // NOLINT(*-convert-member-functions-to-static)
|
151
|
+
// return can_convert_from_ruby(value);
|
152
|
+
// }
|
153
|
+
// RuBLE::ByteArray convert(VALUE value) { // NOLINT(*-convert-member-functions-to-static)
|
154
|
+
// return RuBLE::ByteArray::from_ruby(value);
|
155
|
+
// }
|
156
|
+
// };
|
157
|
+
//}
|
158
|
+
|
159
|
+
constexpr std::ostream &operator<<(std::ostream &os, const RuBLE::ByteArray &cba) {
|
160
|
+
return os << cba.data();
|
161
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "types/declarations.hpp"
|
3
|
+
#include "types/ruby.hpp"
|
4
|
+
#include "utils/RubyCallbackTraits.hpp"
|
5
|
+
#include "utils/garbage_collection.hpp"
|
6
|
+
|
7
|
+
using namespace std::string_literals;
|
8
|
+
namespace RuBLE {
|
9
|
+
struct Callback {
|
10
|
+
protected:
|
11
|
+
std::size_t _arg_count;
|
12
|
+
VALUE _cb = Qnil;
|
13
|
+
public:
|
14
|
+
explicit Callback(const std::size_t &arg_count) : _arg_count(arg_count) {}
|
15
|
+
|
16
|
+
// TODO: confirm this is a proc (taking the right number of args)
|
17
|
+
constexpr void set(VALUE cb) {
|
18
|
+
Ruby::CallbackTraits::assert_accepts_n_args(cb, _arg_count, true);
|
19
|
+
_cb = cb;
|
20
|
+
}
|
21
|
+
|
22
|
+
[[maybe_unused]] [[nodiscard]] constexpr const std::size_t &arg_count() const { return _arg_count; }
|
23
|
+
|
24
|
+
[[maybe_unused]] constexpr void clear() { set(Qnil); }
|
25
|
+
|
26
|
+
template<class... Types>
|
27
|
+
Object fire(const Types& ...args) {
|
28
|
+
if constexpr (DEBUG) assert(sizeof...(Types) == arg_count());
|
29
|
+
|
30
|
+
Object cb(_cb);
|
31
|
+
if (!cb.test()) return Qnil;
|
32
|
+
const std::initializer_list<Object> argList { args... };
|
33
|
+
Rice::Array argArray(argList.begin(), argList.end());
|
34
|
+
return cb.vcall("call", argArray);
|
35
|
+
}
|
36
|
+
|
37
|
+
constexpr operator bool() const {
|
38
|
+
return _cb != Qnil && _cb != Qfalse;
|
39
|
+
}
|
40
|
+
|
41
|
+
constexpr void ruby_mark() const {
|
42
|
+
if (*this) rb_gc_mark(_cb);
|
43
|
+
}
|
44
|
+
};
|
45
|
+
}
|
46
|
+
|
47
|
+
namespace Rice {
|
48
|
+
template<>
|
49
|
+
constexpr void ruby_mark<RuBLE::Callback>(RuBLE::Callback *cb) { cb->ruby_mark(); }
|
50
|
+
}
|
51
|
+
|
52
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <cstddef>
|
4
|
+
#include <map>
|
5
|
+
#include <bitset>
|
6
|
+
#include <utility>
|
7
|
+
#include <functional>
|
8
|
+
#include <variant>
|
9
|
+
#include <string_view>
|
10
|
+
#include <ranges>
|
11
|
+
#include "utils/containers.hpp"
|
12
|
+
|
13
|
+
#include "types/helpers.hpp"
|
14
|
+
|
15
|
+
namespace RuBLE {
|
16
|
+
template<const auto &FIELD_NAMES> class NamedBitSet;
|
17
|
+
}
|
18
|
+
|
19
|
+
template<const auto &FIELDS> struct std::hash<RuBLE::NamedBitSet<FIELDS>> {
|
20
|
+
constexpr std::size_t operator()(const RuBLE::NamedBitSet<FIELDS> &nbs) const noexcept;
|
21
|
+
};
|
22
|
+
|
23
|
+
namespace RuBLE {
|
24
|
+
// this namespace from https://en.cppreference.com/w/cpp/utility/variant/visit
|
25
|
+
namespace visit {
|
26
|
+
template<class... Ts>
|
27
|
+
struct overloaded : Ts... { using Ts::operator()...; };
|
28
|
+
#if !defined(__cpp_deduction_guides) || __cpp_deduction_guides < 201907L
|
29
|
+
// explicit deduction guide (not needed as of C++20)
|
30
|
+
template<class... Ts>
|
31
|
+
overloaded(Ts...) -> overloaded<Ts...>;
|
32
|
+
#endif
|
33
|
+
}
|
34
|
+
|
35
|
+
namespace FlagCheck {
|
36
|
+
using ConstFlag = bool;
|
37
|
+
using StaticFn = const std::function<bool()>;
|
38
|
+
template<class T> using ConstMemFn = std::function<bool(const std::remove_cvref_t<T>&)>;
|
39
|
+
|
40
|
+
template<class T = nullptr_t>
|
41
|
+
using Fn = std::variant<ConstFlag, StaticFn, ConstMemFn<T>>;
|
42
|
+
|
43
|
+
using MapKey = std::string_view;
|
44
|
+
template<class T = nullptr_t> using MapType = std::map<MapKey, Fn<T>>;
|
45
|
+
|
46
|
+
template<class R, class T>
|
47
|
+
concept Map = requires(const R &r, T &t) {
|
48
|
+
{ std::get<0>(*r.begin()) } -> std::convertible_to<const MapKey>;
|
49
|
+
{ std::get<1>(*r.begin()) } -> std::convertible_to<Fn<T>>;
|
50
|
+
};
|
51
|
+
|
52
|
+
#ifdef NBSDEBUG
|
53
|
+
template<class T = nullptr_t> using Pair = std::pair<const MapKey, Fn<T>>;
|
54
|
+
template<class T> using MapPairType = FlagCheck::Pair<T>;
|
55
|
+
template <class R> using RangeIteratorType = std::result_of_t<decltype(&R::cbegin)(R)>;
|
56
|
+
template<class R> using RangeItem = std::result_of_t<decltype(&RangeIteratorType<R>::operator*)(RangeIteratorType<R>)>;
|
57
|
+
template<class R> using RangeItemNoRef = std::remove_reference_t<RangeItem<R>>;
|
58
|
+
template<class R> using RangeItemKey = RangeItemNoRef<R>::first_type;
|
59
|
+
template<class R> using RangeItemValue = RangeItemNoRef<R>::second_type;
|
60
|
+
static_assert(std::is_same_v<MapType<int>::value_type, Pair<int>>);
|
61
|
+
static_assert(std::is_convertible_v<RangeItem<MapType<int>>, MapPairType<int>>);
|
62
|
+
static_assert(std::is_convertible_v<RangeItemKey<MapType<int>>, const MapKey&>);
|
63
|
+
static_assert(Map<MapType<int>, int>);
|
64
|
+
#endif
|
65
|
+
|
66
|
+
template<class T>
|
67
|
+
bool test_flag(const FlagCheck::Fn<T> &fn, const auto &obj = nullptr) {
|
68
|
+
bool result = std::visit(visit::overloaded{
|
69
|
+
[]() -> bool { return false; },
|
70
|
+
[](nullptr_t) -> bool { return false; },
|
71
|
+
[](const FlagCheck::ConstFlag &val) -> bool { return val; },
|
72
|
+
[](const FlagCheck::StaticFn &fn) -> bool { return fn(); },
|
73
|
+
[&obj](const std::function<bool(const T&)> &fn) -> bool {
|
74
|
+
return fn(obj);
|
75
|
+
},
|
76
|
+
}, fn);
|
77
|
+
return result;
|
78
|
+
}
|
79
|
+
|
80
|
+
}
|
81
|
+
template<const auto& FIELD_NAMES>
|
82
|
+
class NamedBitSet {
|
83
|
+
public:
|
84
|
+
static constexpr auto FIELDS = std::to_array<const std::string_view>(FIELD_NAMES);
|
85
|
+
static constexpr const std::size_t N = FIELDS.size();
|
86
|
+
using BitSet = std::bitset<N>;
|
87
|
+
using BitRef = BitSet::reference;
|
88
|
+
using PairType = std::pair<std::string_view, std::size_t>;
|
89
|
+
static constexpr auto PAIRS = ([]<std::size_t... I>(std::index_sequence<I...>) -> std::array<PairType, N> {
|
90
|
+
return {std::make_pair(FIELD_NAMES[I], I)...};
|
91
|
+
})(std::make_index_sequence<N>{});
|
92
|
+
|
93
|
+
static constexpr std::size_t index_of(const std::string_view &name);
|
94
|
+
|
95
|
+
private:
|
96
|
+
BitSet _bits = 0;
|
97
|
+
|
98
|
+
public:
|
99
|
+
constexpr const BitSet &bits() const;
|
100
|
+
|
101
|
+
constexpr BitRef operator[](const std::string_view &name);
|
102
|
+
|
103
|
+
constexpr bool operator[](const std::string_view &name) const;
|
104
|
+
|
105
|
+
// Ensure T is an integral type that can hold all N bits
|
106
|
+
template<typename T> requires std::is_integral_v<T> && (std::numeric_limits<T>::max() >= (1 << N) - 1)
|
107
|
+
constexpr explicit operator T() const { return static_cast<T>(_bits.to_ullong()); }
|
108
|
+
|
109
|
+
[[nodiscard]] constexpr std::vector<std::string_view> flag_names() const;
|
110
|
+
|
111
|
+
[[nodiscard]] constexpr std::vector<std::string> flag_name_strs() const;
|
112
|
+
|
113
|
+
constexpr NamedBitSet(): _bits(0) {}
|
114
|
+
|
115
|
+
constexpr NamedBitSet(const std::initializer_list<const std::string_view> &list);
|
116
|
+
|
117
|
+
constexpr NamedBitSet(const auto &rangeList);
|
118
|
+
constexpr NamedBitSet(const BitSet &bitSet);
|
119
|
+
// Takes a map (or equivalent) of 'std::string_view's to one of: boolean literal, static fn, const mem fn, mem fn
|
120
|
+
template<typename ObjT, FlagCheck::Map<ObjT> R>
|
121
|
+
constexpr NamedBitSet(R &r, ObjT &&obj);
|
122
|
+
|
123
|
+
[[nodiscard]] constexpr std::string to_s() const;
|
124
|
+
};
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
#ifdef RUBLE_DEBUG
|
129
|
+
void NamedBitSetDemo();
|
130
|
+
#endif
|
131
|
+
}
|
132
|
+
|
133
|
+
template<const auto &FIELDS>
|
134
|
+
constexpr std::size_t std::hash<RuBLE::NamedBitSet<FIELDS>>::operator()(const RuBLE::NamedBitSet<FIELDS> &nbs) const noexcept {
|
135
|
+
std::size_t h1 = typeid(nbs).hash_code();
|
136
|
+
static auto hasher = std::hash<decltype(RuBLE::NamedBitSet<FIELDS>::BitSet)>{};
|
137
|
+
return h1 ^ (hasher(nbs.bits()) << 1);
|
138
|
+
}
|
139
|
+
|
140
|
+
#include "NamedBitSet.ipp"
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "types/declarations.hpp"
|
4
|
+
#include "NamedBitSet.hpp"
|
5
|
+
|
6
|
+
#include <string>
|
7
|
+
#include "utils/containers.hpp"
|
8
|
+
using namespace std::string_literals;
|
9
|
+
|
10
|
+
namespace RuBLE {
|
11
|
+
template<const auto &FIELD_NAMES>
|
12
|
+
constexpr std::string NamedBitSet<FIELD_NAMES>::to_s() const { return Utils::join(flag_names(), "|"); }
|
13
|
+
|
14
|
+
template<const auto &FIELD_NAMES>
|
15
|
+
template<typename ObjT, FlagCheck::Map<ObjT> R>
|
16
|
+
[[maybe_unused]]
|
17
|
+
constexpr NamedBitSet<FIELD_NAMES>::NamedBitSet(R &r, ObjT &&obj) {
|
18
|
+
for (const auto &[name, fn] : r) (*this)[name] = FlagCheck::test_flag<ObjT>(fn, obj);
|
19
|
+
}
|
20
|
+
|
21
|
+
template<const auto &FIELD_NAMES>
|
22
|
+
constexpr NamedBitSet<FIELD_NAMES>::NamedBitSet(const NamedBitSet::BitSet &bitSet) : _bits(bitSet) {}
|
23
|
+
|
24
|
+
template<const auto &FIELD_NAMES>
|
25
|
+
constexpr NamedBitSet<FIELD_NAMES>::NamedBitSet(const auto &rangeList) {
|
26
|
+
for (const auto &sv: rangeList) (*this)[sv] = true;
|
27
|
+
}
|
28
|
+
|
29
|
+
template<const auto &FIELD_NAMES>
|
30
|
+
constexpr NamedBitSet<FIELD_NAMES>::NamedBitSet(const std::initializer_list<const std::string_view> &list) {
|
31
|
+
for (const auto &sv: list) (*this)[sv] = true;
|
32
|
+
}
|
33
|
+
|
34
|
+
template<const auto &FIELD_NAMES>
|
35
|
+
constexpr std::vector<std::string> NamedBitSet<FIELD_NAMES>::flag_name_strs() const {
|
36
|
+
const auto names = flag_names();
|
37
|
+
return { names.begin(), names.end() };
|
38
|
+
}
|
39
|
+
|
40
|
+
template<const auto &FIELD_NAMES>
|
41
|
+
constexpr std::vector<std::string_view> NamedBitSet<FIELD_NAMES>::flag_names() const {
|
42
|
+
std::vector<std::string_view> result;
|
43
|
+
for (const auto &[name, idx]: PAIRS) {
|
44
|
+
if (_bits[idx]) result.push_back(name);
|
45
|
+
}
|
46
|
+
return result;
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
template<const auto &FIELD_NAMES>
|
51
|
+
constexpr bool NamedBitSet<FIELD_NAMES>::operator[](const std::string_view &name) const {
|
52
|
+
return _bits[index_of(name)];
|
53
|
+
}
|
54
|
+
|
55
|
+
template<const auto &FIELD_NAMES>
|
56
|
+
constexpr NamedBitSet<FIELD_NAMES>::BitRef NamedBitSet<FIELD_NAMES>::operator[](const std::string_view &name) {
|
57
|
+
return _bits[index_of(name)];
|
58
|
+
}
|
59
|
+
|
60
|
+
template<const auto &FIELD_NAMES>
|
61
|
+
constexpr const NamedBitSet<FIELD_NAMES>::BitSet &NamedBitSet<FIELD_NAMES>::bits() const { return _bits; }
|
62
|
+
|
63
|
+
template<const auto &FIELD_NAMES>
|
64
|
+
constexpr std::size_t NamedBitSet<FIELD_NAMES>::index_of(const std::string_view &name) {
|
65
|
+
for (const auto &[field, idx]: PAIRS) {
|
66
|
+
if (field == name) return idx;
|
67
|
+
}
|
68
|
+
std::string errmsg = "Invalid field name "s + std::string(name.data()) + " in call to index_of";
|
69
|
+
throw std::invalid_argument(errmsg);
|
70
|
+
}
|
71
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# FIXME: This won't work because extensions aren't built from inside the gem dir when installed
|
4
|
+
# ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __dir__)
|
5
|
+
# require "bundler/setup" # Set up gems listed in the Gemfile.
|
6
|
+
Bundler.require(:build)
|
7
|
+
require_relative '../../lib/ruble/build'
|
8
|
+
|
9
|
+
require 'mkmf-rice'
|
10
|
+
# TODO(?): see what https://rubygems.org/gems/autobuild has to offer
|
11
|
+
|
12
|
+
# noinspection RubyMismatchedArgumentType
|
13
|
+
THIS_DIR = Pathname.new(__dir__)
|
14
|
+
|
15
|
+
@generator = RuBLE::Build::Extconf.instance
|
16
|
+
require 'debug' if @generator.debug_mode?
|
17
|
+
|
18
|
+
@generator.write_build_config(path: THIS_DIR / 'build-config.cmake')
|
19
|
+
|
20
|
+
# TODO: need to confirm the values (for install dir, compile args, etc) outputted in Makefile
|
21
|
+
# are the same values CMake gets from parsing 'build-config.json'
|
22
|
+
# TODO: confirm dummy_makefile isn't missing any needed fields
|
23
|
+
# currently, I don't see where the build dir is set
|
24
|
+
|
25
|
+
create_makefile 'ruble'
|
26
|
+
File.open(THIS_DIR / 'Makefile', 'at') do |f|
|
27
|
+
#f.puts 'include cmake.mk'
|
28
|
+
end
|
29
|
+
|
30
|
+
# TODO: does setting TARGET_ENTRY in makefile do anything? (if so, use it)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#include "types/declarations.hpp"
|
2
|
+
#include "bindings/Adapter.hpp"
|
3
|
+
#include "bindings/Peripheral.hpp"
|
4
|
+
#include "management/Registry.hpp"
|
5
|
+
#include "management/RegistryFactory.hpp"
|
6
|
+
#include "bindings/Service.hpp"
|
7
|
+
#include "bindings/Characteristic.hpp"
|
8
|
+
|
9
|
+
namespace RuBLE {
|
10
|
+
|
11
|
+
void Init_Registries() {
|
12
|
+
rb_cAdapterRegistry = define_class_under<AdapterRegistry>(rb_mRuBLE, "AdapterRegistry")
|
13
|
+
.define_method("size", &AdapterRegistry::size)
|
14
|
+
.define_method("to_s", &AdapterRegistry::to_s);
|
15
|
+
define_map_under<AdapterRegistry::Collection>(rb_cAdapterRegistry, "Map");
|
16
|
+
|
17
|
+
if (!adapterRegistryFactory) adapterRegistryFactory = AdapterRegistryFactory::instance();
|
18
|
+
if (!adapterRegistry) adapterRegistry = adapterRegistryFactory->registry();
|
19
|
+
rb_cAdapterRegistry.define_singleton_attr("registry", &*adapterRegistry, Rice::AttrAccess::Read);
|
20
|
+
rb_cAdapterRegistry.iv_set("@registry", *adapterRegistry->_registry);
|
21
|
+
|
22
|
+
rb_cPeripheralRegistry = define_class_under<PeripheralRegistry>(rb_mRuBLE, "PeripheralRegistry")
|
23
|
+
.define_method("to_s", &PeripheralRegistry::to_s)
|
24
|
+
.define_method("size", &PeripheralRegistry::size);
|
25
|
+
|
26
|
+
rb_cAdapter
|
27
|
+
.define_attr("peripheral_registry", &Adapter::_peripheral_registry, Rice::AttrAccess::Read);
|
28
|
+
|
29
|
+
if (!peripheralRegistryFactory) peripheralRegistryFactory = PeripheralRegistryFactory::instance();
|
30
|
+
|
31
|
+
rb_cServiceRegistry = define_class_under<ServiceRegistry>(rb_mRuBLE, "ServiceRegistry")
|
32
|
+
.define_method("to_s", &ServiceRegistry::to_s)
|
33
|
+
.define_method("size", &ServiceRegistry::size);
|
34
|
+
|
35
|
+
define_map_under<Peripheral::ServiceMap>(rb_cPeripheral, "ServiceMap");
|
36
|
+
rb_cPeripheral
|
37
|
+
.define_method("services", &Peripheral::services)
|
38
|
+
.define_method("[]", &Peripheral::operator[])
|
39
|
+
.define_attr("service_registry", &Peripheral::_service_registry, Rice::AttrAccess::Read)
|
40
|
+
;
|
41
|
+
if (!serviceRegistryFactory) serviceRegistryFactory = ServiceRegistryFactory::instance();
|
42
|
+
|
43
|
+
rb_cCharacteristicRegistry = define_class_under<CharacteristicRegistry>(rb_mRuBLE, "CharacteristicRegistry")
|
44
|
+
.define_method("to_s", &ServiceRegistry::to_s)
|
45
|
+
.define_method("size", &ServiceRegistry::size);
|
46
|
+
|
47
|
+
define_map_under<CharacteristicRegistry::Collection>(rb_cService, "CharacteristicMap");
|
48
|
+
rb_cService
|
49
|
+
.define_method("characteristics", &Service::characteristics)
|
50
|
+
.define_method("[]", &Service::operator[])
|
51
|
+
.define_attr("characteristic_registry", &Service::_characteristic_registry, Rice::AttrAccess::Read);
|
52
|
+
|
53
|
+
if (!characteristicRegistryFactory) characteristicRegistryFactory = CharacteristicRegistryFactory ::instance();
|
54
|
+
define_map_under<Characteristic::DescriptorMap>(rb_cCharacteristic, "DescriptorMap");
|
55
|
+
rb_cCharacteristic
|
56
|
+
.define_method("descriptors", &Characteristic::descriptors)
|
57
|
+
.define_method("[]", &Characteristic::operator[])
|
58
|
+
.define_attr("descriptors", &Characteristic::_descriptors, Rice::AttrAccess::Read);
|
59
|
+
|
60
|
+
}
|
61
|
+
|
62
|
+
}
|
63
|
+
|