diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ce0fb4f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +set(headers + src/asyncmdnsresults.h + src/asyncmdnssearch.h +) + +set(sources + src/asyncmdnssearch.cpp +) + +set(dependencies + mdns + + cpputils + espchrono + espcpputils + espwifistack + expected + fmt +) + +idf_component_register( + INCLUDE_DIRS + src + SRCS + ${headers} + ${sources} + REQUIRES + ${dependencies} +) + +target_compile_options(${COMPONENT_TARGET} + PRIVATE + -fstack-reuse=all + -fstack-protector-all + -Wno-unused-function + -Wno-deprecated-declarations + -Wno-missing-field-initializers + -Wno-parentheses +) diff --git a/src/asyncmdnsresults.h b/src/asyncmdnsresults.h new file mode 100644 index 0000000..62e2593 --- /dev/null +++ b/src/asyncmdnsresults.h @@ -0,0 +1,121 @@ +#pragma once + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +class AsyncMdnsResults +{ + template + struct linked_list_iter + { + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + + constexpr linked_list_iter(pointer ptr) noexcept : m_ptr{ptr} {} + + reference operator*() const { return *m_ptr; } + pointer operator->() { return m_ptr; } + + linked_list_iter &operator++() { m_ptr = m_ptr->next; return *this; } + + friend bool operator== (const linked_list_iter& a, const linked_list_iter& b) { return a.m_ptr == b.m_ptr; }; + friend bool operator!= (const linked_list_iter& a, const linked_list_iter& b) { return a.m_ptr != b.m_ptr; }; + + private: + pointer m_ptr; + }; + +public: + using value_type = mdns_result_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = linked_list_iter; + using const_iterator = linked_list_iter; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + // not providing reverse_iterator + // not providing const_reverse_iterator + + AsyncMdnsResults() noexcept = default; + AsyncMdnsResults(mdns_result_t *results, std::size_t num_results) noexcept : + m_results{results}, m_num_results{num_results} + {} + + AsyncMdnsResults(const AsyncMdnsResults &) noexcept = delete; + AsyncMdnsResults(AsyncMdnsResults &&other) noexcept : + m_results{std::move(other.m_results)}, m_num_results{std::move(other.m_num_results)} + { + other.m_results = {}; + other.m_num_results = {}; + } + + AsyncMdnsResults &operator=(const AsyncMdnsResults &) noexcept = delete; + AsyncMdnsResults &operator=(AsyncMdnsResults &&other) + { + if (&other != this) + { + free(); + m_results = std::move(other.m_results); + other.m_results = {}; + m_num_results = std::move(other.m_num_results); + other.m_num_results = {}; + } + return *this; + } + + ~AsyncMdnsResults() + { + free(); + } + + + // Iterators. + constexpr iterator begin() noexcept { return iterator(data()); } + constexpr const_iterator begin() const noexcept { return const_iterator(data()); } + constexpr iterator end() noexcept { return iterator(nullptr); } + constexpr const_iterator end() const noexcept { return const_iterator(nullptr); } + // not providing rbegin() + // not providing rend() + constexpr const_iterator cbegin() const noexcept { return const_iterator(data()); } + constexpr const_iterator cend() const noexcept { return const_iterator(nullptr); } + // not providing crbegin(); + // not providing crend(); + + // Capacity. + constexpr size_type size() const noexcept { return m_num_results; } + constexpr size_type max_size() const noexcept { return size(); } + constexpr bool empty() const noexcept { return data() == nullptr; } + + // Element access. + // not providing operator[] + // not providing at() + // not providing at() + constexpr reference front() noexcept { return *data(); } + constexpr const_reference front() const noexcept { return *data(); } + // not providing back() + constexpr pointer data() noexcept { return m_results; } + constexpr const_pointer data() const noexcept { return m_results; } + +private: + void free() + { + if (m_results) + { + mdns_query_results_free(m_results); + m_results = {}; + } + + m_num_results = {}; + } + + mdns_result_t *m_results{}; + std::size_t m_num_results{}; +}; diff --git a/src/asyncmdnssearch.cpp b/src/asyncmdnssearch.cpp new file mode 100644 index 0000000..2b68c5b --- /dev/null +++ b/src/asyncmdnssearch.cpp @@ -0,0 +1,74 @@ +#include "asyncmdnssearch.h" + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +namespace { +constexpr const char * const TAG = "ASYNCMDNS"; +} // namespace + +AsyncMdnsSearch::AsyncMdnsSearch(AsyncMdnsSearch &&other) : + m_mdnsScan{std::move(other.m_mdnsScan)} +{ + other.m_mdnsScan = {}; +} + +AsyncMdnsSearch::~AsyncMdnsSearch() +{ + if (searchStarted()) + deleteSearch(); +} + +AsyncMdnsSearch &AsyncMdnsSearch::operator=(AsyncMdnsSearch &&other) +{ + m_mdnsScan = std::move(other.m_mdnsScan); + other.m_mdnsScan = {}; + return *this; +} + +tl::expected AsyncMdnsSearch::startSearch(const char *name, const char *service, const char *proto, uint16_t type, std::chrono::milliseconds timeout, size_t max_results) +{ + ESP_LOGI(TAG, "starting search..."); + + if (searchStarted()) + return tl::make_unexpected("last scan not finished yet"); + + if (!max_results) + return tl::make_unexpected("max_results should be greater than 0"); + + m_mdnsScan = mdns_query_async_new(name, service, proto, type, timeout.count(), max_results); + if (!searchStarted()) + return tl::make_unexpected("mdns_query_async_new() returned invalid"); + + return {}; +} + +std::optional AsyncMdnsSearch::getResults() +{ + if (!searchStarted()) + { + ESP_LOGW(TAG, "called without a running search"); + return std::nullopt; + } + + mdns_result_t *results{}; + uint8_t num_results{}; + if (!mdns_query_async_get_result(m_mdnsScan, 0, &results, &num_results)) + return std::nullopt; + + deleteSearch(); + + return AsyncMdnsResults{results, num_results}; +} + +void AsyncMdnsSearch::deleteSearch() +{ + if (!m_mdnsScan) + ESP_LOGW(TAG, "no search started!"); + + mdns_query_async_delete(m_mdnsScan); + m_mdnsScan = nullptr; +} diff --git a/src/asyncmdnssearch.h b/src/asyncmdnssearch.h new file mode 100644 index 0000000..8626ead --- /dev/null +++ b/src/asyncmdnssearch.h @@ -0,0 +1,36 @@ +#pragma once + +// system includes +#include +#include +#include + +// esp-idf includes +#include + +// 3rdparty lib includes +#include + +// local includes +#include "asyncmdnsresults.h" + +class AsyncMdnsSearch +{ +public: + AsyncMdnsSearch() = default; + AsyncMdnsSearch(const AsyncMdnsSearch &) = delete; + AsyncMdnsSearch(AsyncMdnsSearch &&other); + ~AsyncMdnsSearch(); + + AsyncMdnsSearch &operator=(const AsyncMdnsSearch &) = delete; + AsyncMdnsSearch &operator=(AsyncMdnsSearch &&other); + + bool searchStarted() const { return m_mdnsScan; } + + tl::expected startSearch(const char *name, const char *service, const char *proto, uint16_t type, std::chrono::milliseconds timeout, size_t max_results); + std::optional getResults(); + void deleteSearch(); + +private: + mdns_search_once_t *m_mdnsScan{}; +};