ruble 0.0.3.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gdbinit +21 -0
  3. data/.gitignore +18 -0
  4. data/.rubocop.yml +96 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +6 -0
  7. data/CMakeLists.txt +4 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +21 -0
  10. data/Gemfile.lock +98 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +63 -0
  13. data/Rakefile +41 -0
  14. data/ext/ruble/.gitignore +5 -0
  15. data/ext/ruble/CMakeLists.txt +157 -0
  16. data/ext/ruble/RuBLEHelpers.cmake +240 -0
  17. data/ext/ruble/bindings/Adapter.cpp +193 -0
  18. data/ext/ruble/bindings/Adapter.hpp +85 -0
  19. data/ext/ruble/bindings/Characteristic.cpp +171 -0
  20. data/ext/ruble/bindings/Characteristic.hpp +132 -0
  21. data/ext/ruble/bindings/Descriptor.cpp +34 -0
  22. data/ext/ruble/bindings/Descriptor.hpp +69 -0
  23. data/ext/ruble/bindings/Peripheral.cpp +212 -0
  24. data/ext/ruble/bindings/Peripheral.hpp +108 -0
  25. data/ext/ruble/bindings/RuBLE.cpp +115 -0
  26. data/ext/ruble/bindings/Service.cpp +112 -0
  27. data/ext/ruble/bindings/Service.hpp +61 -0
  28. data/ext/ruble/bindings/common.hpp +48 -0
  29. data/ext/ruble/bindings/globals.cpp +43 -0
  30. data/ext/ruble/cmake.mk +62 -0
  31. data/ext/ruble/concerns/CharacteristicValueTracker.cpp +18 -0
  32. data/ext/ruble/concerns/CharacteristicValueTracker.hpp +40 -0
  33. data/ext/ruble/concerns/Rubyable.cpp +4 -0
  34. data/ext/ruble/concerns/Rubyable.hpp +46 -0
  35. data/ext/ruble/config.h.in +25 -0
  36. data/ext/ruble/containers/ByteArray.cpp +64 -0
  37. data/ext/ruble/containers/ByteArray.hpp +161 -0
  38. data/ext/ruble/containers/Callback.hpp +52 -0
  39. data/ext/ruble/containers/NamedBitSet.hpp +140 -0
  40. data/ext/ruble/containers/NamedBitSet.ipp +71 -0
  41. data/ext/ruble/extconf.rb +30 -0
  42. data/ext/ruble/management/Registry.cpp +63 -0
  43. data/ext/ruble/management/Registry.hpp +170 -0
  44. data/ext/ruble/management/RegistryFactory.hpp +113 -0
  45. data/ext/ruble/management/RubyQueue.cpp +152 -0
  46. data/ext/ruble/management/RubyQueue.hpp +69 -0
  47. data/ext/ruble/modularize.diff +28 -0
  48. data/ext/ruble/types/SimpleBLE.hpp +21 -0
  49. data/ext/ruble/types/declarations.hpp +91 -0
  50. data/ext/ruble/types/helpers.hpp +12 -0
  51. data/ext/ruble/types/ruby.hpp +36 -0
  52. data/ext/ruble/types/stl.hpp +41 -0
  53. data/ext/ruble/utils/RubyCallbackTraits.cpp +28 -0
  54. data/ext/ruble/utils/RubyCallbackTraits.hpp +48 -0
  55. data/ext/ruble/utils/async.cpp +10 -0
  56. data/ext/ruble/utils/async.hpp +76 -0
  57. data/ext/ruble/utils/containers.hpp +41 -0
  58. data/ext/ruble/utils/exception_handling.cpp +50 -0
  59. data/ext/ruble/utils/exception_handling.hpp +53 -0
  60. data/ext/ruble/utils/garbage_collection.cpp +82 -0
  61. data/ext/ruble/utils/garbage_collection.hpp +22 -0
  62. data/ext/ruble/utils/hash.cpp +83 -0
  63. data/ext/ruble/utils/hash.hpp +52 -0
  64. data/ext/ruble/utils/hexadecimal.hpp +116 -0
  65. data/ext/ruble/utils/human_type_names.hpp +38 -0
  66. data/ext/ruble/utils/inspection.cpp +24 -0
  67. data/ext/ruble/utils/inspection.hpp +108 -0
  68. data/ext/ruble/utils/ruby.hpp +103 -0
  69. data/ext/ruble/utils/ruby_context.hpp +73 -0
  70. data/lib/ruble/build/.rubocop.yml +19 -0
  71. data/lib/ruble/build/boost.rb +34 -0
  72. data/lib/ruble/build/cmake.rb +134 -0
  73. data/lib/ruble/build/core_ext.rb +5 -0
  74. data/lib/ruble/build/data/bundler.rb +24 -0
  75. data/lib/ruble/build/data/extension.rb +101 -0
  76. data/lib/ruble/build/data/os.rb +21 -0
  77. data/lib/ruble/build/data/rice.rb +24 -0
  78. data/lib/ruble/build/data.rb +22 -0
  79. data/lib/ruble/build/extconf.rb +76 -0
  80. data/lib/ruble/build/github_repo.rb +129 -0
  81. data/lib/ruble/build/simpleble.rb +56 -0
  82. data/lib/ruble/build.rb +28 -0
  83. data/lib/ruble/version.rb +7 -0
  84. data/lib/ruble.rb +46 -0
  85. data/lib/tasks/dev/dev_tasks.rb +130 -0
  86. data/lib/tasks/dev/pager.rb +218 -0
  87. data/lib/tasks/dev/paths.rb +30 -0
  88. data/lib/tasks/dev/state_hash.rb +65 -0
  89. data/lib/tasks/dev.rake +41 -0
  90. data/lib/tasks/simpleble.rake +29 -0
  91. data/sig/rubble.rbs +4 -0
  92. 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
+