aboutsummaryrefslogtreecommitdiffstats
path: root/include/jau
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-10-16 16:08:23 +0200
committerSven Gothel <[email protected]>2020-10-16 16:08:23 +0200
commitd5c6787a8d7e9c123b6c30dd877af58057df4204 (patch)
treed719b90b1a374de02f354faa2fdeac19e762b7ce /include/jau
parentda13a199446d143732cc6d0d996501bac1c1d4f8 (diff)
Move jaucpp files to root-folder
Diffstat (limited to 'include/jau')
-rw-r--r--include/jau/basic_algos.hpp78
-rw-r--r--include/jau/basic_types.hpp433
-rw-r--r--include/jau/cow_vector.hpp62
-rw-r--r--include/jau/debug.hpp162
-rw-r--r--include/jau/dfa_utf8_decode.hpp55
-rw-r--r--include/jau/environment.hpp267
-rw-r--r--include/jau/function_def.hpp413
-rw-r--r--include/jau/java_uplink.hpp93
-rw-r--r--include/jau/jni/helper_jni.hpp434
-rw-r--r--include/jau/jni/jni_mem.hpp198
-rw-r--r--include/jau/ordered_atomic.hpp218
-rw-r--r--include/jau/ringbuffer.hpp491
-rw-r--r--include/jau/ringbuffer_if.hpp217
-rw-r--r--include/jau/version.hpp37
14 files changed, 3158 insertions, 0 deletions
diff --git a/include/jau/basic_algos.hpp b/include/jau/basic_algos.hpp
new file mode 100644
index 0000000..796a1ce
--- /dev/null
+++ b/include/jau/basic_algos.hpp
@@ -0,0 +1,78 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_BASIC_ALGOS_HPP_
+#define JAU_BASIC_ALGOS_HPP_
+
+#include <mutex>
+
+namespace jau {
+
+ /**
+ * Custom for_each template, using indices instead of iterators,
+ * allowing container to be modified within lambda 'callback'.
+ * <p>
+ * Method performs UnaryFunction on all elements [0..n-1],
+ * where n is being retrieved once before the loop!
+ * </p>
+ */
+ template<class InputArray, class UnaryFunction>
+ constexpr UnaryFunction for_each_idx(InputArray &array, UnaryFunction f)
+ {
+ const size_t size = array.size();
+ for (size_t i = 0; i < size; i++) {
+ f(array[i]);
+ }
+ return f; // implicit move since C++11
+ }
+
+ /**
+ * Custom for_each template, using indices instead of iterators,
+ * allowing container to be modified within lambda 'callback'.
+ * <p>
+ * Method performs UnaryFunction on all elements [0..n-1],
+ * where n is being retrieved once before the loop!
+ * </p>
+ * <p>
+ * This method also utilizes a given mutex to ensure thread-safety,
+ * by operating within an RAII-style std::lock_guard block.
+ * </p>
+ */
+ template<class Mutex, class InputArray, class UnaryFunction>
+ constexpr UnaryFunction for_each_idx_mtx(Mutex &mtx, InputArray &array, UnaryFunction f)
+ {
+ const std::lock_guard<Mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+
+ const size_t size = array.size();
+ for (size_t i = 0; i < size; i++) {
+ f(array[i]);
+ }
+ return f; // implicit move since C++11
+ }
+
+
+} // namespace jau
+
+#endif /* JAU_BASIC_ALGOS_HPP_ */
diff --git a/include/jau/basic_types.hpp b/include/jau/basic_types.hpp
new file mode 100644
index 0000000..cdd59d9
--- /dev/null
+++ b/include/jau/basic_types.hpp
@@ -0,0 +1,433 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_BASIC_TYPES_HPP_
+#define JAU_BASIC_TYPES_HPP_
+
+#include <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+#include <vector>
+
+extern "C" {
+ #include <endian.h>
+ #include <byteswap.h>
+}
+
+namespace jau {
+
+ /**
+ * Returns current monotonic time in milliseconds.
+ */
+ int64_t getCurrentMilliseconds() noexcept;
+
+ #define E_FILE_LINE __FILE__,__LINE__
+
+ class RuntimeException : public std::exception {
+ private:
+ std::string msg;
+ std::string backtrace;
+
+ protected:
+ RuntimeException(std::string const type, std::string const m, const char* file, int line) noexcept;
+
+ public:
+ RuntimeException(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("RuntimeException", m, file, line) {}
+
+ virtual ~RuntimeException() noexcept { }
+
+ RuntimeException(const RuntimeException &o) = default;
+ RuntimeException(RuntimeException &&o) = default;
+ RuntimeException& operator=(const RuntimeException &o) = default;
+ RuntimeException& operator=(RuntimeException &&o) = default;
+
+ std::string get_backtrace() const noexcept { return backtrace; }
+
+ virtual const char* what() const noexcept override;
+ };
+
+ class InternalError : public RuntimeException {
+ public:
+ InternalError(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("InternalError", m, file, line) {}
+ };
+
+ class OutOfMemoryError : public RuntimeException {
+ public:
+ OutOfMemoryError(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("OutOfMemoryError", m, file, line) {}
+ };
+
+ class NullPointerException : public RuntimeException {
+ public:
+ NullPointerException(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("NullPointerException", m, file, line) {}
+ };
+
+ class IllegalArgumentException : public RuntimeException {
+ public:
+ IllegalArgumentException(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("IllegalArgumentException", m, file, line) {}
+ };
+
+ class IllegalStateException : public RuntimeException {
+ public:
+ IllegalStateException(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("IllegalStateException", m, file, line) {}
+ };
+
+ class UnsupportedOperationException : public RuntimeException {
+ public:
+ UnsupportedOperationException(std::string const m, const char* file, int line) noexcept
+ : RuntimeException("UnsupportedOperationException", m, file, line) {}
+ };
+
+ class IndexOutOfBoundsException : public RuntimeException {
+ public:
+ IndexOutOfBoundsException(const int index, const int length, const char* file, int line) noexcept
+ : RuntimeException("IndexOutOfBoundsException", "Index "+std::to_string(index)+", data length "+std::to_string(length), file, line) {}
+
+ IndexOutOfBoundsException(const int index, const int count, const int length, const char* file, int line) noexcept
+ : RuntimeException("IndexOutOfBoundsException", "Index "+std::to_string(index)+", count "+std::to_string(count)+", data length "+std::to_string(length), file, line) {}
+ };
+
+ /**
+ // *************************************************
+ // *************************************************
+ // *************************************************
+ */
+
+ struct __attribute__((packed)) uint128_t {
+ uint8_t data[16];
+
+ bool operator==(uint128_t const &o) const noexcept {
+ if( this == &o ) {
+ return true;
+ }
+ return !std::memcmp(data, o.data, 16);
+ }
+ bool operator!=(uint128_t const &o) const noexcept
+ { return !(*this == o); }
+ };
+
+ inline uint128_t bswap(uint128_t const & source) noexcept {
+ uint128_t dest;
+ uint8_t const * const s = source.data;
+ uint8_t * const d = dest.data;
+ for(int i=0; i<16; i++) {
+ d[i] = s[15-i];
+ }
+ return dest;
+ }
+
+ /**
+ * On the i386 the host byte order is Least Significant Byte first (LSB) or Little-Endian,
+ * whereas the network byte order, as used on the Internet, is Most Significant Byte first (MSB) or Big-Endian.
+ * See #include <arpa/inet.h>
+ *
+ * Bluetooth is LSB or Little-Endian!
+ */
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ inline uint16_t be_to_cpu(uint16_t const & n) noexcept {
+ return n;
+ }
+ inline uint16_t cpu_to_be(uint16_t const & h) noexcept {
+ return h;
+ }
+ inline uint16_t le_to_cpu(uint16_t const & l) noexcept {
+ return bswap_16(l);
+ }
+ inline uint16_t cpu_to_le(uint16_t const & h) noexcept {
+ return bswap_16(h);
+ }
+
+ inline uint32_t be_to_cpu(uint32_t const & n) noexcept {
+ return n;
+ }
+ inline uint32_t cpu_to_be(uint32_t const & h) noexcept {
+ return h;
+ }
+ inline uint32_t le_to_cpu(uint32_t const & l) noexcept {
+ return bswap_32(l);
+ }
+ inline uint32_t cpu_to_le(uint32_t const & h) noexcept {
+ return bswap_32(h);
+ }
+
+ inline uint128_t be_to_cpu(uint128_t const & n) noexcept {
+ return n;
+ }
+ inline uint128_t cpu_to_be(uint128_t const & h) noexcept {
+ return n;
+ }
+ inline uint128_t le_to_cpu(uint128_t const & l) noexcept {
+ return bswap(l);
+ }
+ inline uint128_t cpu_to_le(uint128_t const & h) noexcept {
+ return bswap(h);
+ }
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ inline uint16_t be_to_cpu(uint16_t const & n) noexcept {
+ return bswap_16(n);
+ }
+ inline uint16_t cpu_to_be(uint16_t const & h) noexcept {
+ return bswap_16(h);
+ }
+ inline uint16_t le_to_cpu(uint16_t const & l) noexcept {
+ return l;
+ }
+ inline uint16_t cpu_to_le(uint16_t const & h) noexcept {
+ return h;
+ }
+
+ inline uint32_t be_to_cpu(uint32_t const & n) noexcept {
+ return bswap_32(n);
+ }
+ inline uint32_t cpu_to_be(uint32_t const & h) noexcept {
+ return bswap_32(h);
+ }
+ inline uint32_t le_to_cpu(uint32_t const & l) noexcept {
+ return l;
+ }
+ inline uint32_t cpu_to_le(uint32_t const & h) noexcept {
+ return h;
+ }
+
+ inline uint128_t be_to_cpu(uint128_t const & n) noexcept {
+ return bswap(n);
+ }
+ inline uint128_t cpu_to_be(uint128_t const & h) noexcept {
+ return bswap(h);
+ }
+ inline uint128_t le_to_cpu(uint128_t const & l) noexcept {
+ return l;
+ }
+ inline uint128_t cpu_to_le(uint128_t const & h) noexcept {
+ return h;
+ }
+#else
+ #error "Unexpected __BYTE_ORDER"
+#endif
+
+ inline void put_uint8(uint8_t * buffer, int const byte_offset, const uint8_t v) noexcept
+ {
+ uint8_t * p = (uint8_t *) ( buffer + byte_offset );
+ *p = v;
+ }
+ inline uint8_t get_uint8(uint8_t const * buffer, int const byte_offset) noexcept
+ {
+ uint8_t const * p = (uint8_t const *) ( buffer + byte_offset );
+ return *p;
+ }
+ inline int8_t get_int8(uint8_t const * buffer, int const byte_offset) noexcept
+ {
+ int8_t const * p = (int8_t const *) ( buffer + byte_offset );
+ return *p;
+ }
+ inline void put_uint16(uint8_t * buffer, int const byte_offset, const uint16_t v) noexcept
+ {
+ uint16_t * p = (uint16_t *) ( buffer + byte_offset );
+ *p = v;
+ }
+ inline void put_uint16(uint8_t * buffer, int const byte_offset, const uint16_t v, bool littleEndian) noexcept
+ {
+ uint16_t * p = (uint16_t *) ( buffer + byte_offset );
+ *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v);
+ }
+ inline uint16_t get_uint16(uint8_t const * buffer, int const byte_offset) noexcept
+ {
+ uint16_t const * p = (uint16_t const *) ( buffer + byte_offset );
+ return *p;
+ }
+ inline uint16_t get_uint16(uint8_t const * buffer, int const byte_offset, bool littleEndian) noexcept
+ {
+ uint16_t const * p = (uint16_t const *) ( buffer + byte_offset );
+ return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p);
+ }
+
+ inline void put_uint32(uint8_t * buffer, int const byte_offset, const uint32_t v) noexcept
+ {
+ uint32_t * p = (uint32_t *) ( buffer + byte_offset );
+ *p = v;
+ }
+ inline void put_uint32(uint8_t * buffer, int const byte_offset, const uint32_t v, bool littleEndian) noexcept
+ {
+ uint32_t * p = (uint32_t *) ( buffer + byte_offset );
+ *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v);
+ }
+ inline uint32_t get_uint32(uint8_t const * buffer, int const byte_offset) noexcept
+ {
+ uint32_t const * p = (uint32_t const *) ( buffer + byte_offset );
+ return *p;
+ }
+ inline uint32_t get_uint32(uint8_t const * buffer, int const byte_offset, bool littleEndian) noexcept
+ {
+ uint32_t const * p = (uint32_t const *) ( buffer + byte_offset );
+ return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p);
+ }
+
+ inline void put_uint128(uint8_t * buffer, int const byte_offset, const uint128_t v) noexcept
+ {
+ uint128_t * p = (uint128_t *) ( buffer + byte_offset );
+ *p = v;
+ }
+ inline void put_uint128(uint8_t * buffer, int const byte_offset, const uint128_t v, bool littleEndian) noexcept
+ {
+ uint128_t * p = (uint128_t *) ( buffer + byte_offset );
+ *p = littleEndian ? cpu_to_le(v) : cpu_to_be(v);
+ }
+ inline uint128_t get_uint128(uint8_t const * buffer, int const byte_offset) noexcept
+ {
+ uint128_t const * p = (uint128_t const *) ( buffer + byte_offset );
+ return *p;
+ }
+ inline uint128_t get_uint128(uint8_t const * buffer, int const byte_offset, bool littleEndian) noexcept
+ {
+ uint128_t const * p = (uint128_t const *) ( buffer + byte_offset );
+ return littleEndian ? le_to_cpu(*p) : be_to_cpu(*p);
+ }
+
+ inline void set_bit_uint32(const uint8_t nr, uint32_t &mask)
+ {
+ if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, E_FILE_LINE); }
+ mask |= 1 << (nr & 31);
+ }
+
+ inline void clear_bit_uint32(const uint8_t nr, uint32_t &mask)
+ {
+ if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, E_FILE_LINE); }
+ mask |= ~(1 << (nr & 31));
+ }
+
+ inline uint32_t test_bit_uint32(const uint8_t nr, const uint32_t mask)
+ {
+ if( nr > 31 ) { throw IndexOutOfBoundsException(nr, 32, E_FILE_LINE); }
+ return mask & (1 << (nr & 31));
+ }
+
+ inline void set_bit_uint64(const uint8_t nr, uint64_t &mask)
+ {
+ if( nr > 63 ) { throw IndexOutOfBoundsException(nr, 64, E_FILE_LINE); }
+ mask |= 1 << (nr & 63);
+ }
+
+ inline void clear_bit_uint64(const uint8_t nr, uint64_t &mask)
+ {
+ if( nr > 63 ) { throw IndexOutOfBoundsException(nr, 64, E_FILE_LINE); }
+ mask |= ~(1 << (nr & 63));
+ }
+
+ inline uint64_t test_bit_uint64(const uint8_t nr, const uint64_t mask)
+ {
+ if( nr > 63 ) { throw IndexOutOfBoundsException(nr, 64, E_FILE_LINE); }
+ return mask & (1 << (nr & 63));
+ }
+
+ /**
+ * Returns a C++ String taken from buffer with maximum length of min(max_len, max_len).
+ * <p>
+ * The maximum length only delimits the string length and does not contain the EOS null byte.
+ * An EOS null byte will will be added.
+ * </p>
+ * <p>
+ * The source string within buffer is not required to contain an EOS null byte;
+ * </p>
+ */
+ std::string get_string(const uint8_t *buffer, int const buffer_len, int const max_len) noexcept;
+
+ /**
+ * Merge the given 'uuid16' into a 'base_uuid' copy at the given little endian 'uuid16_le_octet_index' position.
+ * <p>
+ * The given 'uuid16' value will be added with the 'base_uuid' copy at the given position.
+ * </p>
+ * <pre>
+ * base_uuid: 00000000-0000-1000-8000-00805F9B34FB
+ * uuid16: DCBA
+ * uuid16_le_octet_index: 12
+ * result: 0000DCBA-0000-1000-8000-00805F9B34FB
+ *
+ * LE: low-mem - FB349B5F8000-0080-0010-0000-ABCD0000 - high-mem
+ * ^ index 12
+ * LE: uuid16 -> value.data[12+13]
+ *
+ * BE: low-mem - 0000DCBA-0000-1000-8000-00805F9B34FB - high-mem
+ * ^ index 2
+ * BE: uuid16 -> value.data[2+3]
+ * </pre>
+ */
+ uint128_t merge_uint128(uint16_t const uuid16, uint128_t const & base_uuid, int const uuid16_le_octet_index);
+
+ /**
+ * Merge the given 'uuid32' into a 'base_uuid' copy at the given little endian 'uuid32_le_octet_index' position.
+ * <p>
+ * The given 'uuid32' value will be added with the 'base_uuid' copy at the given position.
+ * </p>
+ * <pre>
+ * base_uuid: 00000000-0000-1000-8000-00805F9B34FB
+ * uuid32: 87654321
+ * uuid32_le_octet_index: 12
+ * result: 87654321-0000-1000-8000-00805F9B34FB
+ *
+ * LE: low-mem - FB349B5F8000-0080-0010-0000-12345678 - high-mem
+ * ^ index 12
+ * LE: uuid32 -> value.data[12..15]
+ *
+ * BE: low-mem - 87654321-0000-1000-8000-00805F9B34FB - high-mem
+ * ^ index 0
+ * BE: uuid32 -> value.data[0..3]
+ * </pre>
+ */
+ uint128_t merge_uint128(uint32_t const uuid32, uint128_t const & base_uuid, int const uuid32_le_octet_index);
+
+ std::string uint8HexString(const uint8_t v, const bool leading0X=true) noexcept;
+ std::string uint16HexString(const uint16_t v, const bool leading0X=true) noexcept;
+ std::string uint32HexString(const uint32_t v, const bool leading0X=true) noexcept;
+ std::string uint64HexString(const uint64_t v, const bool leading0X=true) noexcept;
+ std::string aptrHexString(const void * v, const bool leading0X=true) noexcept;
+
+ /**
+ * If lsbFirst is true, orders LSB left -> MSB right, usual for byte streams.
+ * <p>
+ * Otherwise orders MSB left -> LSB right, usual for readable integer values.
+ * </p>
+ */
+ std::string bytesHexString(const uint8_t * bytes, const int offset, const int length, const bool lsbFirst, const bool leading0X=true) noexcept;
+
+ std::string int32SeparatedString(const int32_t v, const char separator=',') noexcept;
+ std::string uint32SeparatedString(const uint32_t v, const char separator=',') noexcept;
+ std::string uint64SeparatedString(const uint64_t v, const char separator=',') noexcept;
+
+ /** trim in place */
+ void trimInPlace(std::string &s) noexcept;
+
+ /** trim copy */
+ std::string trimCopy(const std::string &s) noexcept;
+
+} // namespace jau
+
+#endif /* JAU_BASIC_TYPES_HPP_ */
diff --git a/include/jau/cow_vector.hpp b/include/jau/cow_vector.hpp
new file mode 100644
index 0000000..4356881
--- /dev/null
+++ b/include/jau/cow_vector.hpp
@@ -0,0 +1,62 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_COW_VECTOR_HPP_
+#define JAU_COW_VECTOR_HPP_
+
+#include <cstring>
+#include <string>
+#include <cstdint>
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <vector>
+#include <algorithm>
+
+#include <jau/debug.hpp>
+#include <jau/basic_types.hpp>
+#include <jau/ordered_atomic.hpp>
+
+
+namespace jau {
+
+/**
+ * Implementation of a Copy-On-Write (COW) Vector,
+ * exposing <i>lock-free</i> SC-DRF atomic synchronization.
+ * <p>
+ * </p>
+ * See also:
+ * <pre>
+ * - Sequentially Consistent (SC) ordering or SC-DRF (data race free) <https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering>
+ * - std::memory_order <https://en.cppreference.com/w/cpp/atomic/memory_order>
+ * </pre>
+ */
+template <typename T> class cow_vector : public std::vector<T> {
+};
+
+} /* namespace jau */
+
+#endif /* JAU_COW_VECTOR_HPP_ */
diff --git a/include/jau/debug.hpp b/include/jau/debug.hpp
new file mode 100644
index 0000000..3bc498b
--- /dev/null
+++ b/include/jau/debug.hpp
@@ -0,0 +1,162 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_DEBUG_HPP_
+#define JAU_DEBUG_HPP_
+
+#include <memory>
+#include <vector>
+
+#include <cstdint>
+#include <cinttypes>
+#include <cstring>
+#include <string>
+#include <cstdio>
+#include <cstdarg>
+
+extern "C" {
+ #include <errno.h>
+}
+
+#include <jau/environment.hpp>
+
+// #define PERF_PRINT_ON 1
+
+namespace jau {
+
+ /**
+ * Returns a de-mangled backtrace string separated by newline excluding this function.
+ * @param skip_frames number of stack frames to skip, default is one frame for this function.
+ * @return the de-mangled backtrace, separated by newline
+ */
+ std::string get_backtrace(int skip_frames=1) noexcept;
+
+ /**
+ * Prints the de-mangled backtrace string separated by newline excluding this function to stderr, using get_backtrace().
+ * @param skip_frames number of stack frames to skip, default is two frames for this function and for get_backtrace().
+ */
+ void print_backtrace(int skip_frames=2) noexcept;
+
+ void DBG_PRINT_impl(const char * format, ...) noexcept;
+
+ /** Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] Debug: '. */
+ #define DBG_PRINT(...) { if( jau::environment::get().DEBUG ) { jau::DBG_PRINT_impl(__VA_ARGS__); } }
+
+ /** Use for environment-variable environment::DEBUG_JNI conditional debug messages, prefix '[elapsed_time] Debug: '. */
+ #define DBG_JNI_PRINT(...) { if( jau::environment::get().DEBUG_JNI ) { jau::DBG_PRINT_impl(__VA_ARGS__); } }
+
+ void WORDY_PRINT_impl(const char * format, ...) noexcept;
+
+ /**
+ * Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time] Wordy: '.
+ * <p>
+ * 'Wordy' is the shorter English form of the Latin word 'verbosus', from which the word 'verbosity' is sourced.
+ * </p>
+ */
+ #define WORDY_PRINT(...) { if( jau::environment::get().VERBOSE ) { jau::WORDY_PRINT_impl(__VA_ARGS__); } }
+
+
+ #define PERF_TS_T0_BASE() const uint64_t _t0 = jau::getCurrentMilliseconds()
+
+ #define PERF_TS_TD_BASE(m) { const uint64_t _td = jau::getCurrentMilliseconds() - _t0; \
+ fprintf(stderr, "[%'9" PRIu64 "] PERF %s done in %d ms,\n", jau::environment::getElapsedMillisecond(), (m), (int)_td); }
+ #ifdef PERF_PRINT_ON
+ #define PERF_TS_T0() PERF_TS_T0_BASE()
+ #define PERF_TS_TD(m) PERF_TS_TD_BASE(m)
+ #else
+ #define PERF_TS_T0()
+ #define PERF_TS_TD(m)
+ #endif
+ #ifdef PERF2_PRINT_ON
+ #define PERF2_TS_T0() PERF_TS_T0_BASE()
+ #define PERF2_TS_TD(m) PERF_TS_TD_BASE(m)
+ #else
+ #define PERF2_TS_T0()
+ #define PERF2_TS_TD(m)
+ #endif
+ #ifdef PERF3_PRINT_ON
+ #define PERF3_TS_T0() PERF_TS_T0_BASE()
+ #define PERF3_TS_TD(m) PERF_TS_TD_BASE(m)
+ #else
+ #define PERF3_TS_T0()
+ #define PERF3_TS_TD(m)
+ #endif
+
+ /** Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line: '. Function also appends last errno and strerror(errno). */
+ void ABORT_impl(const char *func, const char *file, const int line, const char * format, ...) noexcept;
+
+ /** Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ FILE:LINE: '. Function also appends last errno and strerror(errno). */
+ #define ABORT(...) { jau::ABORT_impl(__func__, __FILE__, __LINE__, __VA_ARGS__); }
+
+ /** Use for unconditional error messages, prefix '[elapsed_time] Error @ file:line: '. Function also appends last errno and strerror(errno). */
+ void ERR_PRINTv(const char *func, const char *file, const int line, const char * format, va_list args) noexcept;
+
+ void ERR_PRINT_impl(const char *prefix, const bool backtrace, const char *func, const char *file, const int line, const char * format, ...) noexcept;
+
+ /** Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE: '. Function also appends last errno and strerror(errno). */
+ #define ERR_PRINT(...) { jau::ERR_PRINT_impl("Error", true /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
+
+ /** Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE: '. Function also appends last errno and strerror(errno). */
+ #define IRQ_PRINT(...) { jau::ERR_PRINT_impl("Interrupted", false /* backtrace */, __func__, __FILE__, __LINE__, __VA_ARGS__); }
+
+ /** Use for unconditional warning messages, prefix '[elapsed_time] Warning @ file:line: ' */
+ void WARN_PRINTv(const char *func, const char *file, const int line, const char * format, va_list args) noexcept;
+
+ void WARN_PRINT_impl(const char *func, const char *file, const int line, const char * format, ...) noexcept;
+
+ /** Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE: ' */
+ #define WARN_PRINT(...) { jau::WARN_PRINT_impl(__func__, __FILE__, __LINE__, __VA_ARGS__); }
+
+ /** Use for unconditional informal messages, prefix '[elapsed_time] Info: '. */
+ void INFO_PRINT(const char * format, ...) noexcept;
+
+ /** Use for unconditional plain messages, prefix '[elapsed_time] '. */
+ void PLAIN_PRINT(const char * format, ...) noexcept;
+
+
+ void COND_PRINT_impl(const char * format, ...) noexcept;
+
+ /** Use for conditional plain messages, prefix '[elapsed_time] '. */
+ #define COND_PRINT(C, ...) { if( C ) { jau::COND_PRINT_impl(__VA_ARGS__); } }
+
+
+ template<class ListElemType>
+ inline void printSharedPtrList(std::string prefix, std::vector<std::shared_ptr<ListElemType>> & list) noexcept {
+ fprintf(stderr, "%s: Start: %zd elements\n", prefix.c_str(), (size_t)list.size());
+ int idx = 0;
+ for (auto it = list.begin(); it != list.end(); idx++) {
+ std::shared_ptr<ListElemType> & e = *it;
+ if ( nullptr != e ) {
+ fprintf(stderr, "%s[%d]: useCount %zd, mem %p\n", prefix.c_str(), idx, (size_t)e.use_count(), e.get());
+ } else {
+ fprintf(stderr, "%s[%d]: NULL\n", prefix.c_str(), idx);
+ }
+ ++it;
+ }
+ }
+
+} // namespace jau
+
+#endif /* JAU_DEBUG_HPP_ */
diff --git a/include/jau/dfa_utf8_decode.hpp b/include/jau/dfa_utf8_decode.hpp
new file mode 100644
index 0000000..308858e
--- /dev/null
+++ b/include/jau/dfa_utf8_decode.hpp
@@ -0,0 +1,55 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2008-2010 Bjoern Hoehrmann <[email protected]> (see details below)
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_DFA_UTF8_DECODE_HPP_
+#define JAU_DFA_UTF8_DECODE_HPP_
+
+#define DFA_UTF8_ACCEPT 0
+#define DFA_UTF8_REJECT 12
+
+#include <string>
+#include <cstdint>
+#include <cinttypes>
+
+namespace jau {
+ uint32_t dfa_utf8_decode(uint32_t & state, uint32_t & codep, const uint32_t byte_value);
+
+ /**
+ * Returns all valid consecutive UTF-8 characters within buffer
+ * in the range up to buffer_size or until EOS.
+ * <p>
+ * In case a non UTF-8 character has been detected,
+ * the content will be cut off and the decoding loop ends.
+ * </p>
+ * <p>
+ * Method utilizes a finite state machine detecting variable length UTF-8 codes.
+ * See Bjoern Hoehrmann's site <http://bjoern.hoehrmann.de/utf-8/decoder/dfa/> for details.
+ * </p>
+ */
+ std::string dfa_utf8_decode(const uint8_t *buffer, const size_t buffer_size);
+} /* namespace jau */
+
+#endif /* JAU_DFA_UTF8_DECODE_HPP_ */
diff --git a/include/jau/environment.hpp b/include/jau/environment.hpp
new file mode 100644
index 0000000..33a05e6
--- /dev/null
+++ b/include/jau/environment.hpp
@@ -0,0 +1,267 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_ENV_HPP_
+#define JAU_ENV_HPP_
+
+#include <cstdint>
+#include <cinttypes>
+#include <cstring>
+#include <string>
+#include <cstdio>
+
+extern "C" {
+ #include <errno.h>
+}
+
+#include <jau/basic_types.hpp>
+
+namespace jau {
+
+ /**
+ * Base jau environment class,
+ * merely to tag all environment settings by inheritance and hence documentation.
+ * <p>
+ * See main environment {@link environment} and
+ * {@link environment::getExplodingProperties(const std::string & prefixDomain)}.
+ * </p>
+ */
+ class root_environment {
+ };
+
+ /**
+ * Main jau environment class,
+ * supporting environment variable access and fetching elapsed time using its stored startup-time.
+ */
+ class environment : public root_environment {
+ private:
+ environment() noexcept;
+
+ static bool debug;
+
+ static void envSet(std::string prefixDomain, std::string basepair) noexcept;
+ static void envExplodeProperties(std::string prefixDomain, std::string list) noexcept;
+
+ public:
+ /**
+ * Module startup time t0 in monotonic time in milliseconds.
+ */
+ static const uint64_t startupTimeMilliseconds;
+
+ /**
+ * Returns current elapsed monotonic time in milliseconds since module startup, see {@link #startupTimeMilliseconds}.
+ */
+ static uint64_t getElapsedMillisecond() noexcept {
+ return getCurrentMilliseconds() - startupTimeMilliseconds;
+ }
+
+ /**
+ * Returns the value of the environment's variable 'name'.
+ * <p>
+ * Note that only '[org.]tinyb.*' and 'direct_bt.*' Java JVM properties are passed via 'org.tinyb.BluetoothFactory'
+ * </p>
+ * <p>
+ * Implementation attempts to also find a Unix conform environment name,
+ * e.g. 'direct_bt_debug' if ''direct_bt.debug' wasn't found.</br>
+ *
+ * Dots are not allowed as valid Unix envrionment variable identifier.
+ * If the property 'name' isn't found and if the 'name' contains a dot ('.'),
+ * all dots ('.') will be replaced y underscore ('_') and looked up again.</br>
+ * This allows Unix shell user to set the property 'direct_bt_debug' instead of 'direct_bt.debug'.
+ * </p>
+ */
+ static std::string getProperty(const std::string & name) noexcept;
+
+ /**
+ * Returns the value of the environment's variable 'name',
+ * or the 'default_value' if the environment variable's value is null.
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * and hence attempts to also find a Unix conform name,
+ * e.g. 'direct_bt_debug' if ''direct_bt.debug' wasn't found.
+ * </p>
+ */
+ static std::string getProperty(const std::string & name, const std::string & default_value) noexcept;
+
+ /**
+ * Returns the boolean value of the environment's variable 'name',
+ * or the 'default_value' if the environment variable's value is null.
+ * <p>
+ * If the environment variable is set (value != null),
+ * true is determined if the value equals 'true'.
+ * </p>
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * and hence attempts to also find a Unix conform name,
+ * e.g. 'direct_bt_debug' if ''direct_bt.debug' wasn't found.
+ * </p>
+ */
+ static bool getBooleanProperty(const std::string & name, const bool default_value) noexcept;
+
+ /**
+ * Returns the int32_t value of the environment's variable 'name',
+ * or the 'default_value' if the environment variable's value is null
+ * or not within int32_t value range or within the given value range.
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * and hence attempts to also find a Unix conform name,
+ * e.g. 'direct_bt_debug' if ''direct_bt.debug' wasn't found.
+ * </p>
+ */
+ static int32_t getInt32Property(const std::string & name, const int32_t default_value,
+ const int32_t min_allowed=INT32_MIN, const int32_t max_allowed=INT32_MAX) noexcept;
+
+ /**
+ * Returns the uint32_t value of the environment's variable 'name',
+ * or the 'default_value' if the environment variable's value is null
+ * or not within uint32_t value range or within the given value range.
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * and hence attempts to also find a Unix conform name,
+ * e.g. 'direct_bt_debug' if ''direct_bt.debug' wasn't found.
+ * </p>
+ */
+ static uint32_t getUint32Property(const std::string & name, const uint32_t default_value,
+ const uint32_t min_allowed=0, const uint32_t max_allowed=UINT32_MAX) noexcept;
+
+ /**
+ * Fetches exploding variable-name (prefixDomain) values.
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * and hence attempts to also find a Unix conform name,
+ * e.g. 'direct_bt_debug' if ''direct_bt.debug' wasn't found.
+ * </p>
+ * <p>
+ * If the value of a prefixDomain is neither 'true' or 'false',
+ * it is treated as a list of sub-variable names including their optional value separated by comma ','.
+ * <p>
+ * If the value is not given for the sub-variable name, a boolean "true" will be used per default.
+ * </p>
+ * <p>
+ * Example 1
+ * <pre>
+ * Input Environment:
+ * "direct_bt.debug" := "jni,adapter.event,gatt.data=false,hci.event,mgmt.event=true"
+ *
+ * Result Environment:
+ * "direct_bt.debug.jni" := "true"
+ * "direct_bt.debug.adapter.event" := "true"
+ * "direct_bt.debug.gatt.data" := "false"
+ * "direct_bt.debug.hci.event" := "true"
+ * "direct_bt.debug.mgmt.event" := "true"
+ * "direct_bt.debug" := "true" (will be overwritten)
+ * </pre>
+ * Example 2
+ * <pre>
+ * Input Environment:
+ * "direct_bt.gatt" := "cmd.read.timeout=20000,cmd.write.timeout=20001,ringsize=256"
+ *
+ * Result Environment:
+ * "direct_bt.gatt.cmd.read.timeout" := "20000"
+ * "direct_bt.gatt.cmd.write.timeout" := "20001"
+ * "direct_bt.gatt.ringsize" := "256"
+ * "direct_bt.gatt" := "true" (will be overwritten)
+ * </pre>
+ * </p>
+ * <p>
+ * Each sub-variable name/value pair will be trimmed and if not zero-length
+ * appended to the prefixDomain with a dot '.'.</br>
+ *
+ * Each new variable name will be set in the environment with value 'true'.</br>
+ *
+ * The prefixDomain will also be set to the new value 'true', hence gets overwritten.</br>
+ *
+ * This is supported for DEBUG 'direct_bt.debug' and VERBOSE 'direct_bt.verbose', per default.
+ * </p>
+ *
+ * @param prefixDomain
+ * @return
+ */
+ static bool getExplodingProperties(const std::string & prefixDomain) noexcept;
+
+ static environment& get() noexcept {
+ /**
+ * Thread safe starting with C++11 6.7:
+ *
+ * If control enters the declaration concurrently while the variable is being initialized,
+ * the concurrent execution shall wait for completion of the initialization.
+ *
+ * (Magic Statics)
+ *
+ * Avoiding non-working double checked locking.
+ */
+ static environment e;
+ return e;
+ }
+
+ /**
+ * Debug logging enabled or disabled.
+ * <p>
+ * Environment variable 'direct_bt.debug', boolean, default 'false'.
+ * </p>
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * </p>
+ * <p>
+ * Exploding variable-name values are implemented here,
+ * see {@link #getExplodingProperties(const std::string & prefixDomain)}.
+ * </p>
+ */
+ const bool DEBUG;
+
+ /**
+ * JNI Debug logging enabled or disabled.
+ * <p>
+ * Environment variable 'direct_bt.debug.jni', boolean, default 'false'.
+ * </p>
+ * <p>
+ * Implementation uses getBooleanProperty().
+ * </p>
+ */
+ const bool DEBUG_JNI;
+
+ /**
+ * Verbose info logging enabled or disabled.
+ * <p>
+ * Environment variable 'direct_bt.verbose', boolean, default 'false'.
+ * </p>
+ * <p>
+ * Implementation uses {@link #getProperty(const std::string & name)}
+ * </p>
+ * <p>
+ * VERBOSE is also enabled if DEBUG is enabled!
+ * </p>
+ * <p>
+ * Exploding variable-name values are implemented here,
+ * see {@link #getExplodingProperties(const std::string & prefixDomain)}.
+ * </p>
+ */
+ const bool VERBOSE;
+ };
+
+} // namespace jau
+
+#endif /* JAU_ENV_HPP_ */
+
diff --git a/include/jau/function_def.hpp b/include/jau/function_def.hpp
new file mode 100644
index 0000000..d155c95
--- /dev/null
+++ b/include/jau/function_def.hpp
@@ -0,0 +1,413 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_FUNCTION_HPP_
+#define JAU_FUNCTION_HPP_
+
+#include <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+#include <vector>
+#include <functional>
+
+#include <jau/basic_types.hpp>
+
+namespace jau {
+
+ /**
+ * One goal to _produce_ the member-function type instance
+ * is to be class type agnostic for storing in the toolkit.
+ * This is essential to utilize a function-callback API,
+ * where only the provider of an instance knows about its class type.
+ *
+ * Further we can't utilize std::function and std::bind,
+ * as std::function doesn't provide details about the
+ * member-function-call identity and hence lacks of
+ * the equality operator and
+ * std::bind doesn't even specify a return type.
+ *
+ * A capturing lambda in C++-11, does produce decoration code
+ * accessing the captured elements, i.e. an anonymous helper class.
+ * Due to this fact, the return type is an undefined lambda specific
+ * and hence we can't use it to feed the function invocation
+ * into ClassFunction<> using a well specified type.
+ *
+ * <pre>
+ template<typename R, typename C, typename... A>
+ inline ClassFunction<R, A...>
+ bindClassFunction(C *base, R(C::*mfunc)(A...)) {
+ return ClassFunction<R, A...>(
+ (void*)base,
+ (void*)(*((void**)&mfunc)),
+ [&](A... args)->R{ (base->*mfunc)(args...); });
+ ^
+ | Capturing lambda function-pointer are undefined!
+ }
+ </pre>
+ *
+ * Hence we need to manually produce the on-the-fly invocation data type
+ * to capture details on the caller's class type for the member-function-call,
+ * which are then being passed to the ClassFunction<> anonymously
+ * while still being able to perform certain operations
+ * like equality operation for identity.
+ */
+ template<typename R, typename... A>
+ class InvocationFunc {
+ protected:
+ InvocationFunc() noexcept {}
+
+ public:
+ virtual ~InvocationFunc() noexcept {}
+
+ InvocationFunc(const InvocationFunc &o) noexcept = default;
+ InvocationFunc(InvocationFunc &&o) noexcept = default;
+ InvocationFunc& operator=(const InvocationFunc &o) noexcept = default;
+ InvocationFunc& operator=(InvocationFunc &&o) noexcept = default;
+
+ /** Poor man's RTTI */
+ virtual int getType() const noexcept = 0;
+
+ virtual InvocationFunc<R, A...> * clone() const noexcept = 0;
+
+ virtual R invoke(A... args) = 0;
+
+ virtual bool operator==(const InvocationFunc<R, A...>& rhs) const noexcept = 0;
+
+ virtual bool operator!=(const InvocationFunc<R, A...>& rhs) const noexcept = 0;
+
+ virtual std::string toString() const = 0;
+ };
+
+ template<typename R, typename C, typename... A>
+ class NullInvocationFunc : public InvocationFunc<R, A...> {
+ public:
+ NullInvocationFunc() noexcept { }
+
+ int getType() const noexcept override { return 0; }
+
+ InvocationFunc<R, A...> clone() const noexcept override { return NullInvocationFunc(); }
+
+ R invoke(A...) override {
+ return (R)0;
+ }
+
+ bool operator==(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ return getType() == rhs.getType();
+ }
+
+ bool operator!=(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ return !( *this == rhs );
+ }
+
+ std::string toString() const override {
+ return "NullInvocation";
+ }
+ };
+
+ template<typename R, typename C, typename... A>
+ class ClassInvocationFunc : public InvocationFunc<R, A...> {
+ private:
+ C* base;
+ R(C::*member)(A...);
+
+ public:
+ ClassInvocationFunc(C *_base, R(C::*_member)(A...)) noexcept
+ : base(_base), member(_member) {
+ }
+
+ int getType() const noexcept override { return 1; }
+
+ InvocationFunc<R, A...> * clone() const noexcept override { return new ClassInvocationFunc(*this); }
+
+ R invoke(A... args) override {
+ return (base->*member)(args...);
+ }
+
+ bool operator==(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ if( &rhs == this ) {
+ return true;
+ }
+ if( getType() != rhs.getType() ) {
+ return false;
+ }
+ const ClassInvocationFunc<R, C, A...> * prhs = static_cast<const ClassInvocationFunc<R, C, A...>*>(&rhs);
+ return base == prhs->base && member == prhs->member;
+ }
+
+ bool operator!=(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ return !( *this == rhs );
+ }
+
+ std::string toString() const override {
+ // hack to convert member pointer to void *: '*((void**)&member)'
+ return "ClassInvocation "+uint64HexString((uint64_t)base)+"->"+aptrHexString( *((void**)&member) );
+ }
+ };
+
+ template<typename R, typename... A>
+ class PlainInvocationFunc : public InvocationFunc<R, A...> {
+ private:
+ R(*function)(A...);
+
+ public:
+ PlainInvocationFunc(R(*_function)(A...)) noexcept
+ : function(_function) {
+ }
+
+ int getType() const noexcept override { return 2; }
+
+ InvocationFunc<R, A...> * clone() const noexcept override { return new PlainInvocationFunc(*this); }
+
+ R invoke(A... args) override {
+ return (*function)(args...);
+ }
+
+ bool operator==(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ if( &rhs == this ) {
+ return true;
+ }
+ if( getType() != rhs.getType() ) {
+ return false;
+ }
+ const PlainInvocationFunc<R, A...> * prhs = static_cast<const PlainInvocationFunc<R, A...>*>(&rhs);
+ return function == prhs->function;
+ }
+
+ bool operator!=(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ return !( *this == rhs );
+ }
+
+ std::string toString() const override {
+ // hack to convert function pointer to void *: '*((void**)&function)'
+ return "PlainInvocation "+aptrHexString( *((void**)&function) );
+ }
+ };
+
+ template<typename R, typename I, typename... A>
+ class CaptureInvocationFunc : public InvocationFunc<R, A...> {
+ private:
+ I data;
+ R(*function)(I&, A...);
+ bool dataIsIdentity;
+
+ public:
+ /** Utilizes copy-ctor from 'const I& _data' */
+ CaptureInvocationFunc(const I& _data, R(*_function)(I&, A...), bool dataIsIdentity) noexcept
+ : data(_data), function(_function), dataIsIdentity(dataIsIdentity) {
+ }
+
+ /** Utilizes move-ctor from moved 'I&& _data' */
+ CaptureInvocationFunc(I&& _data, R(*_function)(I&, A...), bool dataIsIdentity) noexcept
+ : data(std::move(_data)), function(_function), dataIsIdentity(dataIsIdentity) {
+ }
+
+ int getType() const noexcept override { return 3; }
+
+ InvocationFunc<R, A...> * clone() const noexcept override { return new CaptureInvocationFunc(*this); }
+
+ R invoke(A... args) override {
+ return (*function)(data, args...);
+ }
+
+ bool operator==(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ if( &rhs == this ) {
+ return true;
+ }
+ if( getType() != rhs.getType() ) {
+ return false;
+ }
+ const CaptureInvocationFunc<R, I, A...> * prhs = static_cast<const CaptureInvocationFunc<R, I, A...>*>(&rhs);
+ return dataIsIdentity == prhs->dataIsIdentity && function == prhs->function && ( !dataIsIdentity || data == prhs->data );
+ }
+
+ bool operator!=(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ return !( *this == rhs );
+ }
+
+ std::string toString() const override {
+ // hack to convert function pointer to void *: '*((void**)&function)'
+ return "CaptureInvocation "+aptrHexString( *((void**)&function) );
+ }
+ };
+
+ template<typename R, typename... A>
+ class StdInvocationFunc : public InvocationFunc<R, A...> {
+ private:
+ uint64_t id;
+ std::function<R(A...)> function;
+
+ public:
+ StdInvocationFunc(uint64_t _id, std::function<R(A...)> _function) noexcept
+ : id(_id), function(_function) {
+ }
+ StdInvocationFunc(uint64_t _id) noexcept
+ : id(_id), function() {
+ }
+
+ int getType() const noexcept override { return 10; }
+
+ InvocationFunc<R, A...> * clone() const noexcept override { return new StdInvocationFunc(*this); }
+
+ R invoke(A... args) override {
+ return function(args...);
+ }
+
+ bool operator==(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ if( &rhs == this ) {
+ return true;
+ }
+ if( getType() != rhs.getType() ) {
+ return false;
+ }
+ const StdInvocationFunc<R, A...> * prhs = static_cast<const StdInvocationFunc<R, A...>*>(&rhs);
+ return id == prhs->id;
+ }
+
+ bool operator!=(const InvocationFunc<R, A...>& rhs) const noexcept override
+ {
+ return !( *this == rhs );
+ }
+
+ std::string toString() const override {
+ return "StdInvocation "+uint64HexString( id );
+ }
+ };
+
+ template<typename R, typename... A>
+ class FunctionDef {
+ private:
+ std::shared_ptr<InvocationFunc<R, A...>> func;
+
+ public:
+ /**
+ * Constructs an instance with a null function.
+ */
+ FunctionDef() noexcept
+ : func(std::shared_ptr<InvocationFunc<R, A...>>( new NullInvocationFunc<R, A...>() ))
+ {};
+
+ /**
+ * Constructs an instance using the shared InvocationFunc<R, A...> function.
+ */
+ FunctionDef(std::shared_ptr<InvocationFunc<R, A...>> _func) noexcept
+ : func(_func) { }
+
+ /**
+ * Constructs an instance by wrapping the given naked InvocationFunc<R, A...> function pointer
+ * in a shared_ptr and taking ownership.
+ * <p>
+ * A convenience method.
+ * </p.
+ */
+ FunctionDef(InvocationFunc<R, A...> * _funcPtr) noexcept
+ : func(std::shared_ptr<InvocationFunc<R, A...>>(_funcPtr)) { }
+
+ FunctionDef(const FunctionDef &o) noexcept = default;
+ FunctionDef(FunctionDef &&o) noexcept = default;
+ FunctionDef& operator=(const FunctionDef &o) noexcept = default;
+ FunctionDef& operator=(FunctionDef &&o) noexcept= default;
+
+ bool operator==(const FunctionDef<R, A...>& rhs) const noexcept
+ { return *func == *rhs.func; }
+
+ bool operator!=(const FunctionDef<R, A...>& rhs) const noexcept
+ { return *func != *rhs.func; }
+
+ /** Returns the shared InvocationFunc<R, A...> function */
+ std::shared_ptr<InvocationFunc<R, A...>> getFunction() noexcept { return func; }
+
+ /** Returns a new instance of the held InvocationFunc<R, A...> function. */
+ InvocationFunc<R, A...> * cloneFunction() const noexcept { return func->clone(); }
+
+ std::string toString() const {
+ return "FunctionDef["+func->toString()+"]";
+ }
+
+ R invoke(A... args) {
+ return func->invoke(args...);
+ }
+ };
+
+ template<typename R, typename C, typename... A>
+ inline FunctionDef<R, A...>
+ bindMemberFunc(C *base, R(C::*mfunc)(A...)) noexcept {
+ return FunctionDef<R, A...>( new ClassInvocationFunc<R, C, A...>(base, mfunc) );
+ }
+
+ template<typename R, typename... A>
+ inline FunctionDef<R, A...>
+ bindPlainFunc(R(*func)(A...)) noexcept {
+ return FunctionDef<R, A...>( new PlainInvocationFunc<R, A...>(func) );
+ }
+
+ /**
+ * <code>const I& data</code> will be copied into the InvocationFunc<..> specialization
+ * and hence captured by copy.
+ * <p>
+ * The function call will have the reference of the copied data being passed for efficiency.
+ * </p>
+ */
+ template<typename R, typename I, typename... A>
+ inline FunctionDef<R, A...>
+ bindCaptureFunc(const I& data, R(*func)(I&, A...), bool dataIsIdentity=true) noexcept {
+ return FunctionDef<R, A...>( new CaptureInvocationFunc<R, I, A...>(data, func, dataIsIdentity) );
+ }
+
+ /**
+ * <code>I&& data</code> will be moved into the InvocationFunc<..> specialization.
+ * <p>
+ * The function call will have the reference of the copied data being passed for efficiency.
+ * </p>
+ */
+ template<typename R, typename I, typename... A>
+ inline FunctionDef<R, A...>
+ bindCaptureFunc(I&& data, R(*func)(I&, A...), bool dataIsIdentity=true) noexcept {
+ return FunctionDef<R, A...>( new CaptureInvocationFunc<R, I, A...>(std::move(data), func, dataIsIdentity) );
+ }
+
+ template<typename R, typename... A>
+ inline FunctionDef<R, A...>
+ bindStdFunc(uint64_t id, std::function<R(A...)> func) noexcept {
+ return FunctionDef<R, A...>( new StdInvocationFunc<R, A...>(id, func) );
+ }
+ template<typename R, typename... A>
+ inline FunctionDef<R, A...>
+ bindStdFunc(uint64_t id) noexcept {
+ return FunctionDef<R, A...>( new StdInvocationFunc<R, A...>(id) );
+ }
+
+} // namespace jau
+
+#endif /* JAU_FUNCTION_HPP_ */
diff --git a/include/jau/java_uplink.hpp b/include/jau/java_uplink.hpp
new file mode 100644
index 0000000..c9be15c
--- /dev/null
+++ b/include/jau/java_uplink.hpp
@@ -0,0 +1,93 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_JAVA_UPLINK_HPP_
+#define JAU_JAVA_UPLINK_HPP_
+
+#include <string>
+#include <memory>
+
+#include <jau/basic_types.hpp>
+
+namespace jau {
+
+ /**
+ * Pure virtual JavaAnon, hiding Java JNI details from API,
+ * to be implemented by JNI module.
+ * <p>
+ * One implementation is JavaGlobalObj within the JNI module,
+ * wrapping a JNIGlobalRef instance.
+ * </p>
+ */
+ class JavaAnon {
+ public:
+ virtual ~JavaAnon() noexcept { }
+ virtual std::string toString() const noexcept { return "JavaAnon[???]"; }
+
+ /** Clears the java reference, i.e. nulling it, without deleting the global reference via JNI. */
+ virtual void clear() noexcept = 0;
+ };
+
+ /**
+ * Sharing the anonymous Java object (JavaAnon),
+ * i.e. exposing the Java object uplink to the C++ implementation.
+ */
+ class JavaUplink {
+ private:
+ std::shared_ptr<JavaAnon> javaObjectRef;
+
+ public:
+ virtual std::string toString() const noexcept { return "JavaUplink["+jau::aptrHexString(this)+"]"; }
+
+ virtual std::string get_java_class() const noexcept = 0;
+
+ std::string javaObjectToString() const noexcept { return nullptr == javaObjectRef ? "JavaAnon[null]" : javaObjectRef->toString(); }
+
+ std::shared_ptr<JavaAnon> getJavaObject() noexcept { return javaObjectRef; }
+
+ /** Assigns a new shared JavaAnon reference, replaced item might be deleted via JNI from dtor */
+ void setJavaObject(std::shared_ptr<JavaAnon> objRef) noexcept { javaObjectRef = objRef; }
+
+ /** Clears the java reference, i.e. nulling it, without deleting the global reference via JNI. */
+ void clearJavaObject() noexcept {
+ if( nullptr != javaObjectRef ) {
+ javaObjectRef->clear();
+ }
+ }
+
+ /**
+ * Throws an IllegalStateException if isValid() == false
+ */
+ virtual void checkValid() const {}
+
+ virtual ~JavaUplink() noexcept {
+ javaObjectRef = nullptr;
+ }
+ };
+
+} /* namespace jau */
+
+
+#endif /* JAU_JAVA_UPLINK_HPP_ */
diff --git a/include/jau/jni/helper_jni.hpp b/include/jau/jni/helper_jni.hpp
new file mode 100644
index 0000000..9605252
--- /dev/null
+++ b/include/jau/jni/helper_jni.hpp
@@ -0,0 +1,434 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Author: Andrei Vasiliu <[email protected]>
+ * Copyright (c) 2016 Intel Corporation.
+ *
+ * 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.
+ */
+
+#ifndef JAU_HELPER_JNI_HPP_
+#define JAU_HELPER_JNI_HPP_
+
+#include <vector>
+#include <memory>
+#include <functional>
+#include <jni.h>
+
+#include <jau/java_uplink.hpp>
+#include <jau/basic_types.hpp>
+
+#include <jau/jni/jni_mem.hpp>
+
+namespace jau {
+
+ //
+ // C++ <-> java exceptions
+ //
+
+ /**
+ * Return true if a java exception occurred, otherwise false.
+ * <p>
+ * In case of an exception, the information might be logged to stderr.
+ * </p>
+ * <p>
+ * In case of an exception, user shall release resourced in their JNI code
+ * and leave immediately.
+ * </p>
+ */
+ bool java_exception_check(JNIEnv *env, const char* file, int line);
+
+ /**
+ * Throws a C++ exception if a java exception occurred, otherwise do nothing.
+ * <p>
+ * In case of an exception, the information might be logged to stderr.
+ * </p>
+ * <p>
+ * In case of an exception and hence thrown C++ exception,
+ * might want to catch all and handle it via {@link #rethrow_and_raise_java_exception(JNIEnv*)}.
+ * </p>
+ */
+ void java_exception_check_and_throw(JNIEnv *env, const char* file, int line);
+
+ void print_native_caught_exception_fwd2java(const std::exception &e, const char* file, int line);
+ void print_native_caught_exception_fwd2java(const std::string &msg, const char* file, int line);
+ void print_native_caught_exception_fwd2java(const char * cmsg, const char* file, int line);
+
+ void raise_java_exception(JNIEnv *env, const std::exception &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const std::runtime_error &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::RuntimeException &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::InternalError &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::NullPointerException &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::IllegalArgumentException &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const std::invalid_argument &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::IllegalStateException &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::UnsupportedOperationException &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::IndexOutOfBoundsException &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const std::bad_alloc &e, const char* file, int line);
+ void raise_java_exception(JNIEnv *env, const jau::OutOfMemoryError &e, const char* file, int line);
+
+ /**
+ * Re-throw current exception and raise respective java exception
+ * using any matching function above.
+ */
+ void rethrow_and_raise_java_exception_jauimpl(JNIEnv *env, const char* file, int line);
+
+ /**
+ * Re-throw current exception and raise respective java exception
+ * using any matching function above.
+ */
+ #define rethrow_and_raise_java_exception_jau(E) rethrow_and_raise_java_exception_jauimpl((E), __FILE__, __LINE__)
+ // inline void rethrow_and_raise_java_exception_jau(JNIEnv *env) { rethrow_and_raise_java_exception_jauimpl(env, __FILE__, __LINE__); }
+
+ //
+ // Basic
+ //
+
+ jfieldID getField(JNIEnv *env, jobject obj, const char* field_name, const char* field_signature);
+ inline jfieldID getInstanceField(JNIEnv *env, jobject obj) {
+ return getField(env, obj, "nativeInstance", "J");
+ }
+
+ jclass search_class(JNIEnv *env, const char *clazz_name);
+ jclass search_class(JNIEnv *env, jobject obj);
+ jclass search_class(JNIEnv *env, JavaUplink &object);
+
+ jmethodID search_method(JNIEnv *env, jclass clazz, const char *method_name,
+ const char *prototype, bool is_static);
+ jfieldID search_field(JNIEnv *env, jclass clazz, const char *field_name,
+ const char *type, bool is_static);
+
+ bool from_jboolean_to_bool(jboolean val);
+ std::string from_jstring_to_string(JNIEnv *env, jstring str);
+ jstring from_string_to_jstring(JNIEnv *env, const std::string & str);
+
+ jobject get_new_arraylist(JNIEnv *env, unsigned int size, jmethodID *add);
+
+ //
+ // C++ JavaAnon implementation
+ //
+
+ /**
+ * Implementation for JavaAnon,
+ * by simply wrapping a JNIGlobalRef instance.
+ */
+ class JavaGlobalObj : public JavaAnon {
+ private:
+ JNIGlobalRef javaObjectRef;
+ jmethodID mNotifyDeleted;
+
+ public:
+ static inline void check(const std::shared_ptr<JavaAnon> & shref, const char* file, int line) {
+ if( nullptr == shref ) {
+ throw RuntimeException("JavaGlobalObj::check: Null shared-JavaAnonObj", file, line);
+ }
+ const jobject obj = static_cast<const JavaGlobalObj*>(shref.get())->getObject();
+ if( nullptr == obj ) {
+ throw RuntimeException("JavaGlobalObj::check: Null object", file, line);
+ }
+ }
+ static bool isValid(const std::shared_ptr<JavaAnon> & shref) noexcept {
+ if( nullptr == shref ) {
+ return false;
+ }
+ const jobject obj = static_cast<const JavaGlobalObj*>(shref.get())->getObject();
+ if( nullptr == obj ) {
+ return false;
+ }
+ return true;
+ }
+ JavaGlobalObj(jobject obj, jmethodID mNotifyDeleted) noexcept
+ : javaObjectRef(obj), mNotifyDeleted(mNotifyDeleted) { }
+
+ JavaGlobalObj(const JavaGlobalObj &o) noexcept = default;
+ JavaGlobalObj(JavaGlobalObj &&o) noexcept = default;
+ JavaGlobalObj& operator=(const JavaGlobalObj &o) noexcept = default;
+ JavaGlobalObj& operator=(JavaGlobalObj &&o) noexcept = default;
+
+ virtual ~JavaGlobalObj() noexcept;
+
+ std::string toString() const noexcept override {
+ const uint64_t ref = (uint64_t)(void*)javaObjectRef.getObject();
+ return "JavaGlobalObj["+uint64HexString(ref, true)+"]";
+ }
+
+ /** Clears the java reference, i.e. nulling it, without deleting the global reference via JNI. */
+ void clear() noexcept override { javaObjectRef.clear(); }
+
+ JNIGlobalRef & getJavaObject() noexcept { return javaObjectRef; }
+
+ /* Provides access to the stored GlobalRef as an jobject. */
+ jobject getObject() const noexcept { return javaObjectRef.getObject(); }
+ /* Provides access to the stored GlobalRef as a jclass. */
+ jclass getClass() const noexcept { return javaObjectRef.getClass(); }
+
+ /* Provides access to the stored GlobalRef as an getJavaObject. */
+ static JNIGlobalRef GetJavaObject(const std::shared_ptr<JavaAnon> & shref) noexcept {
+ return static_cast<JavaGlobalObj*>(shref.get())->getJavaObject();
+ }
+ /* Provides access to the stored GlobalRef as an jobject. */
+ static jobject GetObject(const std::shared_ptr<JavaAnon> & shref) noexcept {
+ return static_cast<JavaGlobalObj*>(shref.get())->getObject();
+ }
+
+ /* Provides access to the stored GlobalRef as a jclass. */
+ static jclass GetClass(const std::shared_ptr<JavaAnon> & shref) noexcept {
+ return static_cast<JavaGlobalObj*>(shref.get())->getClass();
+ }
+ };
+
+ //
+ // C++ JavaUplink <-> java access, assuming it implementats JavaUplink: field "long nativeInstance" and native method 'void checkValid()' etc
+ //
+
+ template <typename T>
+ T *getJavaUplinkObject(JNIEnv *env, jobject obj)
+ {
+ jlong instance = env->GetLongField(obj, getInstanceField(env, obj));
+ T *t = reinterpret_cast<T *>(instance);
+ if (t == nullptr) {
+ throw std::runtime_error("Trying to acquire null NativeObject");
+ }
+ t->checkValid();
+ return t;
+ }
+
+ template <typename T>
+ T *getJavaUplinkObjectUnchecked(JNIEnv *env, jobject obj)
+ {
+ jlong instance = env->GetLongField(obj, getInstanceField(env, obj));
+ return reinterpret_cast<T *>(instance);
+ }
+
+ template <typename T>
+ void setJavaUplinkObject(JNIEnv *env, jobject obj, T *t)
+ {
+ if (t == nullptr) {
+ throw std::runtime_error("Trying to create null NativeObject");
+ }
+ jlong instance = reinterpret_cast<jlong>(t);
+ env->SetLongField(obj, getInstanceField(env, obj), instance);
+ }
+
+ //
+ // C++ JavaAnon <-> java access, all generic
+ //
+
+ template <typename T>
+ T *castInstance(jlong instance)
+ {
+ T *t = reinterpret_cast<T *>(instance);
+ if (t == nullptr) {
+ throw std::runtime_error("Trying to cast null object");
+ }
+ return t;
+ }
+
+ template <typename T>
+ T *getObjectRef(JNIEnv *env, jobject obj, const char* field_name)
+ {
+ jlong jobj = env->GetLongField(obj, getField(env, obj, field_name, "J"));
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ return reinterpret_cast<T *>(jobj);
+ }
+
+ template <typename T>
+ void setObjectRef(JNIEnv *env, jobject obj, T *t, const char* field_name)
+ {
+ jlong jobj = reinterpret_cast<jlong>(t);
+ env->SetLongField(obj, getField(env, obj, field_name, "J"), jobj);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ }
+
+ template <typename T>
+ T *getInstance(JNIEnv *env, jobject obj)
+ {
+ jlong instance = env->GetLongField(obj, getInstanceField(env, obj));
+ T *t = reinterpret_cast<T *>(instance);
+ if (t == nullptr) {
+ throw std::runtime_error("Trying to acquire null object");
+ }
+ return t;
+ }
+
+ template <typename T>
+ T *getInstanceUnchecked(JNIEnv *env, jobject obj)
+ {
+ jlong instance = env->GetLongField(obj, getInstanceField(env, obj));
+ return reinterpret_cast<T *>(instance);
+ }
+
+ template <typename T>
+ void setInstance(JNIEnv *env, jobject obj, T *t)
+ {
+ if (t == nullptr) {
+ throw std::runtime_error("Trying to create null object");
+ }
+ jlong instance = reinterpret_cast<jlong>(t);
+ env->SetLongField(obj, getInstanceField(env, obj), instance);
+ }
+
+ inline void clearInstance(JNIEnv *env, jobject obj) {
+ env->SetLongField(obj, getInstanceField(env, obj), 0);
+ }
+
+ template <typename T>
+ jobject generic_clone(JNIEnv *env, jobject obj)
+ {
+ T *obj_generic = getInstance<T>(env, obj);
+ T *copy_generic = obj_generic->clone();
+
+ jclass generic_class = search_class(env, *copy_generic);
+ jmethodID generic_ctor = search_method(env, generic_class, "<init>", "(J)V", false);
+
+ jobject result = env->NewObject(generic_class, generic_ctor, (jlong)copy_generic);
+ if (!result)
+ {
+ throw std::runtime_error("cannot create instance of class");
+ }
+
+ return result;
+ }
+
+ //
+ // C++ <-> java type mapping
+ //
+
+ template <typename T>
+ jobject convert_vector_sharedptr_to_jarraylist(JNIEnv *env, std::vector<std::shared_ptr<T>>& array)
+ {
+ unsigned int array_size = array.size();
+
+ jmethodID arraylist_add;
+ jobject result = get_new_arraylist(env, array_size, &arraylist_add);
+
+ if (0 == array_size) {
+ return result;
+ }
+
+ for (unsigned int i = 0; i < array_size; ++i) {
+ std::shared_ptr<T> elem = array[i];
+ std::shared_ptr<JavaAnon> objref = elem->getJavaObject();
+ if ( nullptr == objref ) {
+ throw InternalError("JavaUplink element of array has no valid java-object: "+elem->toString(), E_FILE_LINE);
+ }
+ env->CallBooleanMethod(result, arraylist_add, JavaGlobalObj::GetObject(objref));
+ }
+ return result;
+ }
+
+ template <typename T>
+ jobject convert_vector_uniqueptr_to_jarraylist(JNIEnv *env, std::vector<std::unique_ptr<T>>& array,
+ const char *ctor_prototype)
+ {
+ unsigned int array_size = array.size();
+
+ jmethodID arraylist_add;
+ jobject result = get_new_arraylist(env, array_size, &arraylist_add);
+
+ if (array_size == 0)
+ {
+ return result;
+ }
+
+ jclass clazz = search_class(env, T::java_class().c_str());
+ jmethodID clazz_ctor = search_method(env, clazz, "<init>", ctor_prototype, false);
+
+ for (unsigned int i = 0; i < array_size; ++i)
+ {
+ T *elem = array[i].release();
+ jobject object = env->NewObject(clazz, clazz_ctor, (jlong)elem);
+ if (!object)
+ {
+ throw jau::InternalError("cannot create instance of class", E_FILE_LINE);
+ }
+ env->CallBooleanMethod(result, arraylist_add, object);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ }
+ return result;
+ }
+
+ template <typename T>
+ jobject convert_vector_uniqueptr_to_jarraylist(JNIEnv *env, std::vector<std::unique_ptr<T>>& array,
+ const char *ctor_prototype, std::function<jobject(JNIEnv*, jclass, jmethodID, T*)> ctor)
+ {
+ unsigned int array_size = array.size();
+
+ jmethodID arraylist_add;
+ jobject result = get_new_arraylist(env, array_size, &arraylist_add);
+
+ if (array_size == 0)
+ {
+ return result;
+ }
+
+ jclass clazz = search_class(env, T::java_class().c_str());
+ jmethodID clazz_ctor = search_method(env, clazz, "<init>", ctor_prototype, false);
+
+ for (unsigned int i = 0; i < array_size; ++i)
+ {
+ T *elem = array[i].release();
+ jobject object = ctor(env, clazz, clazz_ctor, elem);
+ if (!object)
+ {
+ throw std::runtime_error("cannot create instance of class\n");
+ }
+ env->CallBooleanMethod(result, arraylist_add, object);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ }
+ return result;
+ }
+
+ template <typename T>
+ jobject convert_vector_sharedptr_to_jarraylist(JNIEnv *env, std::vector<std::shared_ptr<T>>& array,
+ const char *ctor_prototype, std::function<jobject(JNIEnv*, jclass, jmethodID, T*)> ctor)
+ {
+ unsigned int array_size = array.size();
+
+ jmethodID arraylist_add;
+ jobject result = get_new_arraylist(env, array_size, &arraylist_add);
+
+ if (array_size == 0)
+ {
+ return result;
+ }
+
+ jclass clazz = search_class(env, T::java_class().c_str());
+ jmethodID clazz_ctor = search_method(env, clazz, "<init>", ctor_prototype, false);
+
+ for (unsigned int i = 0; i < array_size; ++i)
+ {
+ T *elem = array[i].get();
+ jobject object = ctor(env, clazz, clazz_ctor, elem);
+ if (!object)
+ {
+ throw std::runtime_error("cannot create instance of class\n");
+ }
+ env->CallBooleanMethod(result, arraylist_add, object);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ }
+ return result;
+ }
+
+} // namespace direct_bt
+
+#endif /* JAU_HELPER_JNI_HPP_ */
diff --git a/include/jau/jni/jni_mem.hpp b/include/jau/jni/jni_mem.hpp
new file mode 100644
index 0000000..31fd662
--- /dev/null
+++ b/include/jau/jni/jni_mem.hpp
@@ -0,0 +1,198 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * Author: Petre Eftime <[email protected]>
+ * Copyright (c) 2016 Intel Corporation.
+ *
+ * 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.
+ */
+
+#ifndef JAU_JNIMEM__HPP_
+#define JAU_JNIMEM__HPP_
+
+#include <jni.h>
+#include <stdexcept>
+
+#include <jau/basic_types.hpp>
+
+extern JavaVM* vm;
+
+
+/*
+ * This class provides a lifetime-managed JNIEnv object, which attaches or
+ * detaches the current thread from the JVM automatically
+ */
+class JNIEnvContainer {
+private:
+ JNIEnv *env = nullptr;
+ bool needsDetach = false;
+
+public:
+ /* Attaches this thread to the JVM if it is not already attached */
+ JNIEnvContainer();
+ /* Detaches this thread to the JVM if it is attached */
+ ~JNIEnvContainer();
+
+ /* Provides access to the local thread's JNIEnv object */
+ JNIEnv *operator*();
+ /* Provides access to the local thread's JNIEnv object's methods */
+ JNIEnv *operator->();
+
+ /* Attaches this thread to the JVM if it is not already attached */
+ void attach();
+ /* Detaches this thread to the JVM if it is attached */
+ void detach();
+};
+
+/* Each thread has a local jni_env variable of JNIEnvContainer type */
+extern thread_local JNIEnvContainer jni_env;
+
+/*
+ * This class provides a lifetime-managed GlobalRef variable,
+ * which is automatically deleted when it goes out of scope.
+ *
+ * RAII-style acquire and relinquish via destructor
+ */
+class JNIGlobalRef {
+private:
+ jobject object;
+
+public:
+ static inline void check(jobject object, const char* file, int line) {
+ if( nullptr == object ) {
+ throw jau::RuntimeException("JNIGlobalRef::check: Null jobject", file, line);
+ }
+ }
+
+ /* Creates a GlobalRef using a nullptr for API convenience, lazy assignment. */
+ JNIGlobalRef() noexcept;
+
+ /* Creates a GlobalRef from an object passed to it */
+ JNIGlobalRef(jobject object);
+
+ JNIGlobalRef(const JNIGlobalRef &o);
+ JNIGlobalRef(JNIGlobalRef &&o) noexcept;
+
+ JNIGlobalRef& operator=(const JNIGlobalRef &o);
+ JNIGlobalRef& operator=(JNIGlobalRef &&o) noexcept;
+
+ /* Deletes the stored GlobalRef */
+ ~JNIGlobalRef() noexcept;
+
+ /** Clears the java reference, i.e. nulling it, without deleting the global reference via JNI. */
+ void clear() noexcept;
+
+ /* Provides access to the stored GlobalRef as an jobject. */
+ jobject operator*() noexcept { return object; }
+
+ /* Provides access to the stored GlobalRef as an jobject. */
+ jobject getObject() const noexcept { return object; }
+ /* Provides access to the stored GlobalRef as a jclass. */
+ jclass getClass() const noexcept { return (jclass)object; }
+
+ bool operator==(const JNIGlobalRef& rhs) const noexcept;
+
+ bool operator!=(const JNIGlobalRef& rhs) const noexcept
+ { return !( *this == rhs ); }
+};
+
+/*
+ * This class provides a lifetime-managed 'PrimitiveArrayCritical' pinned heap,
+ * which is automatically released when it goes out of scope.
+ *
+ * RAII-style acquire and relinquish via destructor
+ */
+template <typename T, typename U>
+class JNICriticalArray {
+public:
+ enum Mode : jint {
+ /** Like default 0: If 'isCopy': Update the java array data with the copy and free the copy. */
+ UPDATE_AND_RELEASE = 0,
+
+ /** Like JNI_COMMIT: If 'isCopy': Update the java array data with the copy, but do not free the copy. */
+ UPDATE_NO_RELEASE = JNI_COMMIT,
+
+ /** Like default JNI_ABORT: If 'isCopy': Do not update the java array data with the copy, but free the copy. */
+ NO_UPDATE_AND_RELEASE = JNI_ABORT,
+ };
+
+private:
+ JNIEnv *env;
+ Mode mode = UPDATE_AND_RELEASE;
+ U jarray = nullptr;
+ T* narray = nullptr;
+ jboolean isCopy = false;
+
+public:
+ JNICriticalArray(JNIEnv *env) : env(env) {}
+
+ JNICriticalArray(const JNICriticalArray &o) = delete;
+ JNICriticalArray(JNICriticalArray &&o) = delete;
+ JNICriticalArray& operator=(const JNICriticalArray &o) = delete;
+ JNICriticalArray& operator=(JNICriticalArray &&o) = delete;
+
+ /**
+ * Release the acquired primitive array, RAII style.
+ */
+ ~JNICriticalArray() {
+ release();
+ }
+
+ /**
+ * Manual release of the acquired primitive array,
+ * usually one likes to simply do this via the destructor, RAII style.
+ */
+ void release() {
+ if( nullptr != narray ) {
+ env->ReleasePrimitiveArrayCritical(jarray, narray, mode);
+ this->jarray = nullptr;
+ this->narray = nullptr;
+ this->env = nullptr;
+ }
+ }
+
+ /**
+ * Acquired the primitive array.
+ */
+ T* get(U jarray, Mode mode=UPDATE_AND_RELEASE) {
+ if( nullptr == jarray ) {
+ return nullptr;
+ }
+ T* narray = static_cast<T*>( env->GetPrimitiveArrayCritical(jarray, &isCopy) );
+ if( nullptr != narray ) {
+ this->mode = mode;
+ this->jarray = jarray;
+ this->narray = narray;
+ return narray;
+ }
+ return nullptr;
+ }
+
+ /**
+ * Returns true if the primitive array had been acquired
+ * and the JVM utilizes a copy of the underlying java array.
+ */
+ bool getIsCopy() const { return isCopy; }
+};
+
+#endif /* JAU_JNIMEM__HPP_ */
+
diff --git a/include/jau/ordered_atomic.hpp b/include/jau/ordered_atomic.hpp
new file mode 100644
index 0000000..b4a6ba8
--- /dev/null
+++ b/include/jau/ordered_atomic.hpp
@@ -0,0 +1,218 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_ORDERED_ATOMIC_HPP_
+#define JAU_ORDERED_ATOMIC_HPP_
+
+#include <atomic>
+#include <memory>
+
+namespace jau {
+
+#ifndef CXX_ALWAYS_INLINE
+# define CXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
+#endif
+
+/**
+ * std::atomic<T> type with predefined fixed std::memory_order,
+ * not allowing changing the memory model on usage and applying the set order to all operator.
+ * <p>
+ * See also:
+ * <pre>
+ * - Sequentially Consistent (SC) ordering or SC-DRF (data race free) <https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering>
+ * - std::memory_order <https://en.cppreference.com/w/cpp/atomic/memory_order>
+ * </pre>
+ * </p>
+ */
+template <typename _Tp, std::memory_order _MO> struct ordered_atomic : private std::atomic<_Tp> {
+ private:
+ typedef std::atomic<_Tp> super;
+
+ public:
+ ordered_atomic() noexcept = default;
+ ~ordered_atomic() noexcept = default;
+ ordered_atomic(const ordered_atomic&) = delete;
+ ordered_atomic& operator=(const ordered_atomic&) = delete;
+ ordered_atomic& operator=(const ordered_atomic&) volatile = delete;
+
+ constexpr ordered_atomic(_Tp __i) noexcept
+ : super(__i)
+ { }
+
+ CXX_ALWAYS_INLINE
+ operator _Tp() const noexcept
+ { return super::load(_MO); }
+
+ CXX_ALWAYS_INLINE
+ operator _Tp() const volatile noexcept
+ { return super::load(_MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator=(_Tp __i) noexcept
+ { super::store(__i, _MO); return __i; }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator=(_Tp __i) volatile noexcept
+ { super::store(__i, _MO); return __i; }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator++(int) noexcept // postfix ++
+ { return super::fetch_add(1, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator++(int) volatile noexcept // postfix ++
+ { return super::fetch_add(1, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator--(int) noexcept // postfix --
+ { return super::fetch_sub(1, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator--(int) volatile noexcept // postfix --
+ { return super::fetch_sub(1, _MO); }
+
+#if 0 /* def _GLIBCXX_ATOMIC_BASE_H */
+
+ // prefix ++, -- impossible w/o using GCC __atomic builtins and access to _M_i .. etc
+
+ CXX_ALWAYS_INLINE
+ _Tp operator++() noexcept // prefix ++
+ { return __atomic_add_fetch(&_M_i, 1, int(_MO)); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator++() volatile noexcept // prefix ++
+ { return __atomic_add_fetch(&_M_i, 1, int(_MO)); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator--() noexcept // prefix --
+ { return __atomic_sub_fetch(&_M_i, 1, int(_MO)); }
+
+ CXX_ALWAYS_INLINE
+ _Tp operator--() volatile noexcept // prefix --
+ { return __atomic_sub_fetch(&_M_i, 1, int(_MO)); }
+
+#endif /* 0 _GLIBCXX_ATOMIC_BASE_H */
+
+ CXX_ALWAYS_INLINE
+ bool is_lock_free() const noexcept
+ { return super::is_lock_free(); }
+
+ CXX_ALWAYS_INLINE
+ bool is_lock_free() const volatile noexcept
+ { return super::is_lock_free(); }
+
+ static constexpr bool is_always_lock_free = super::is_always_lock_free;
+
+ CXX_ALWAYS_INLINE
+ void store(_Tp __i) noexcept
+ { super::store(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ void store(_Tp __i) volatile noexcept
+ { super::store(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp load() const noexcept
+ { return super::load(_MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp load() const volatile noexcept
+ { return super::load(_MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp exchange(_Tp __i) noexcept
+ { return super::exchange(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp exchange(_Tp __i) volatile noexcept
+ { return super::exchange(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ bool compare_exchange_weak(_Tp& __e, _Tp __i) noexcept
+ { return super::compare_exchange_weak(__e, __i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ bool compare_exchange_weak(_Tp& __e, _Tp __i) volatile noexcept
+ { return super::compare_exchange_weak(__e, __i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ bool compare_exchange_strong(_Tp& __e, _Tp __i) noexcept
+ { return super::compare_exchange_strong(__e, __i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ bool compare_exchange_strong(_Tp& __e, _Tp __i) volatile noexcept
+ { return super::compare_exchange_strong(__e, __i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_add(_Tp __i) noexcept
+ { return super::fetch_add(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_add(_Tp __i) volatile noexcept
+ { return super::fetch_add(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_sub(_Tp __i) noexcept
+ { return super::fetch_sub(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_sub(_Tp __i) volatile noexcept
+ { return super::fetch_sub(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_and(_Tp __i) noexcept
+ { return super::fetch_and(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_and(_Tp __i) volatile noexcept
+ { return super::fetch_and(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_or(_Tp __i) noexcept
+ { return super::fetch_or(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_or(_Tp __i) volatile noexcept
+ { return super::fetch_or(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_xor(_Tp __i) noexcept
+ { return super::fetch_xor(__i, _MO); }
+
+ CXX_ALWAYS_INLINE
+ _Tp fetch_xor(_Tp __i) volatile noexcept
+ { return super::fetch_xor(__i, _MO); }
+
+ };
+
+ /** SC atomic integral scalar integer. Memory-Model (MM) guaranteed sequential consistency (SC) between acquire (read) and release (write) */
+ typedef ordered_atomic<int, std::memory_order::memory_order_seq_cst> sc_atomic_int;
+
+ /** Relaxed non-SC atomic integral scalar integer. Memory-Model (MM) only guarantees the atomic value, _no_ sequential consistency (SC) between acquire (read) and release (write). */
+ typedef ordered_atomic<int, std::memory_order::memory_order_relaxed> relaxed_atomic_int;
+
+} /* namespace jau */
+
+#endif /* JAU_ORDERED_ATOMIC_HPP_ */
diff --git a/include/jau/ringbuffer.hpp b/include/jau/ringbuffer.hpp
new file mode 100644
index 0000000..3c2646b
--- /dev/null
+++ b/include/jau/ringbuffer.hpp
@@ -0,0 +1,491 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_RINGBUFFER_HPP_
+#define JAU_RINGBUFFER_HPP_
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <algorithm>
+
+#include <cstring>
+#include <string>
+#include <cstdint>
+
+#include <jau/debug.hpp>
+#include <jau/ordered_atomic.hpp>
+#include <jau/basic_types.hpp>
+#include <jau/ringbuffer_if.hpp>
+
+namespace jau {
+
+/**
+ * Simple implementation of {@link ringbuffer_if},
+ * exposing <i>lock-free</i>
+ * {@link #get() get*(..)} and {@link #put(Object) put*(..)} methods.
+ * <p>
+ * Implementation utilizes the <i>Always Keep One Slot Open</i>,
+ * hence implementation maintains an internal array of <code>capacity</code> <i>plus one</i>!
+ * </p>
+ * <p>
+ * Implementation is thread safe if:
+ * <ul>
+ * <li>{@link #get() get*(..)} operations from multiple threads.</li>
+ * <li>{@link #put(Object) put*(..)} operations from multiple threads.</li>
+ * <li>{@link #get() get*(..)} and {@link #put(Object) put*(..)} thread may be the same.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Following methods acquire the global multi-read _and_ -write mutex:
+ * <ul>
+ * <li>{@link #resetFull(Object[])}</li>
+ * <li>{@link #clear()}</li>
+ * <li>{@link #growEmptyBuffer(Object[])}</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Characteristics:
+ * <ul>
+ * <li>Read position points to the last read element.</li>
+ * <li>Write position points to the last written element.</li>
+ * </ul>
+ * <table border="1">
+ * <tr><td>Empty</td><td>writePos == readPos</td><td>size == 0</td></tr>
+ * <tr><td>Full</td><td>writePos == readPos - 1</td><td>size == capacity</td></tr>
+ * </table>
+ * </p>
+ * See also:
+ * <pre>
+ * - Sequentially Consistent (SC) ordering or SC-DRF (data race free) <https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering>
+ * - std::memory_order <https://en.cppreference.com/w/cpp/atomic/memory_order>
+ * </pre>
+ */
+template <typename T, std::nullptr_t nullelem> class ringbuffer : public ringbuffer_if<T> {
+ private:
+ std::mutex syncRead, syncMultiRead; // Memory-Model (MM) guaranteed sequential consistency (SC) between acquire and release
+ std::mutex syncWrite, syncMultiWrite; // ditto
+ std::condition_variable cvRead;
+ std::condition_variable cvWrite;
+
+ /* final */ int capacityPlusOne; // not final due to grow
+ /* final */ T * array; // Synchronized due to MM's data-race-free SC (SC-DRF) between [atomic] acquire/release
+ sc_atomic_int readPos; // Memory-Model (MM) guaranteed sequential consistency (SC) between acquire (read) and release (write)
+ sc_atomic_int writePos; // ditto
+ relaxed_atomic_int size; // Non-SC atomic size, only atomic value itself is synchronized.
+
+ T * newArray(const int count) noexcept {
+ return new T[count];
+ }
+ void freeArray(T * a) noexcept {
+ delete[] a;
+ }
+
+ void cloneFrom(const bool allocArrayAndCapacity, const ringbuffer & source) noexcept {
+ if( allocArrayAndCapacity ) {
+ capacityPlusOne = source.capacityPlusOne;
+ if( nullptr != array ) {
+ freeArray(array, true);
+ }
+ array = newArray(capacityPlusOne);
+ } else if( capacityPlusOne != source.capacityPlusOne ) {
+ throw InternalError("capacityPlusOne not equal: this "+toString()+", source "+source.toString(), E_FILE_LINE);
+ }
+
+ readPos = source.readPos;
+ writePos = source.writePos;
+ size = source.size;
+ int localWritePos = readPos;
+ for(int i=0; i<size; i++) {
+ localWritePos = (localWritePos + 1) % capacityPlusOne;
+ array[localWritePos] = source.array[localWritePos];
+ }
+ if( writePos != localWritePos ) {
+ throw InternalError("copy segment error: this "+toString()+", localWritePos "+std::to_string(localWritePos)+"; source "+source.toString(), E_FILE_LINE);
+ }
+ }
+
+ void clearImpl() noexcept {
+ // clear all elements, zero size
+ const int _size = size; // fast access
+ if( 0 < _size ) {
+ int localReadPos = readPos;
+ for(int i=0; i<_size; i++) {
+ localReadPos = (localReadPos + 1) % capacityPlusOne;
+ array[localReadPos] = nullelem;
+ }
+ if( writePos != localReadPos ) {
+ // Avoid exception, abort!
+ ABORT("copy segment error: this %s, readPos %d/%d; writePos %d", toString().c_str(), readPos.load(), localReadPos, writePos.load());
+ }
+ readPos = localReadPos;
+ size = 0;
+ }
+ }
+
+ void resetImpl(const T * copyFrom, const int copyFromCount) noexcept {
+ clearImpl();
+
+ // fill with copyFrom elements
+ if( nullptr != copyFrom && 0 < copyFromCount ) {
+ if( copyFromCount > capacityPlusOne-1 ) {
+ // new blank resized array
+ capacityPlusOne = copyFromCount + 1;
+ array = newArray(capacityPlusOne);
+ readPos = 0;
+ writePos = 0;
+ }
+ int localWritePos = writePos;
+ for(int i=0; i<copyFromCount; i++) {
+ localWritePos = (localWritePos + 1) % capacityPlusOne;
+ array[localWritePos] = copyFrom[i];
+ size++;
+ }
+ writePos = localWritePos;
+ }
+ }
+
+ T getImpl(const bool blocking, const bool peek, const int timeoutMS) noexcept {
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead); // acquire syncMultiRead, _not_ sync'ing w/ putImpl
+
+ const int oldReadPos = readPos; // SC-DRF acquire atomic readPos, sync'ing with putImpl
+ int localReadPos = oldReadPos;
+ if( localReadPos == writePos ) { // SC-DRF acquire atomic writePos, sync'ing with putImpl
+ if( blocking ) {
+ std::unique_lock<std::mutex> lockRead(syncRead); // SC-DRF w/ putImpl via same lock
+ while( localReadPos == writePos ) {
+ if( 0 == timeoutMS ) {
+ cvRead.wait(lockRead);
+ } else {
+ std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
+ std::cv_status s = cvRead.wait_until(lockRead, t0 + std::chrono::milliseconds(timeoutMS));
+ if( std::cv_status::timeout == s && localReadPos == writePos ) {
+ return nullelem;
+ }
+ }
+ }
+ } else {
+ return nullelem;
+ }
+ }
+ localReadPos = (localReadPos + 1) % capacityPlusOne;
+ T r = array[localReadPos]; // SC-DRF
+ if( !peek ) {
+ array[localReadPos] = nullelem;
+ {
+ std::unique_lock<std::mutex> lockWrite(syncWrite); // SC-DRF w/ putImpl via same lock
+ size--;
+ readPos = localReadPos; // SC-DRF release atomic readPos
+ cvWrite.notify_all(); // notify waiting putter
+ }
+ } else {
+ readPos = oldReadPos; // SC-DRF release atomic readPos (complete acquire-release even @ peek)
+ }
+ return r;
+ }
+
+ bool putImpl(const T &e, const bool sameRef, const bool blocking, const int timeoutMS) noexcept {
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite); // acquire syncMultiRead, _not_ sync'ing w/ getImpl
+
+ int localWritePos = writePos; // SC-DRF acquire atomic writePos, sync'ing with getImpl
+ localWritePos = (localWritePos + 1) % capacityPlusOne;
+ if( localWritePos == readPos ) { // SC-DRF acquire atomic readPos, sync'ing with getImpl
+ if( blocking ) {
+ std::unique_lock<std::mutex> lockWrite(syncWrite); // SC-DRF w/ getImpl via same lock
+ while( localWritePos == readPos ) {
+ if( 0 == timeoutMS ) {
+ cvWrite.wait(lockWrite);
+ } else {
+ std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
+ std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS));
+ if( std::cv_status::timeout == s && localWritePos == readPos ) {
+ return false;
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+ if( !sameRef ) {
+ array[localWritePos] = e; // SC-DRF
+ }
+ {
+ std::unique_lock<std::mutex> lockRead(syncRead); // SC-DRF w/ getImpl via same lock
+ size++;
+ writePos = localWritePos; // SC-DRF release atomic writePos
+ cvRead.notify_all(); // notify waiting getter
+ }
+ return true;
+ }
+
+ int dropImpl (const int count) noexcept {
+ // locks ringbuffer completely (read/write), hence no need for local copy nor wait/sync etc
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiRead, lockMultiWrite);
+
+ const int dropCount = std::min(count, size.load());
+ if( 0 == dropCount ) {
+ return 0;
+ }
+ for(int i=0; i<dropCount; i++) {
+ readPos = (readPos + 1) % capacityPlusOne;
+ // T r = array[localReadPos];
+ array[readPos] = nullelem;
+ size--;
+ }
+ return dropCount;
+ }
+
+ public:
+ std::string toString() const noexcept override {
+ const std::string es = isEmpty() ? ", empty" : "";
+ const std::string fs = isFull() ? ", full" : "";
+ return "ringbuffer<?>[size "+std::to_string(size)+" / "+std::to_string(capacityPlusOne-1)+
+ ", writePos "+std::to_string(writePos)+", readPos "+std::to_string(readPos)+es+fs+"]";
+ }
+
+ void dump(FILE *stream, std::string prefix) const noexcept override {
+ fprintf(stream, "%s %s {\n", prefix.c_str(), toString().c_str());
+ for(int i=0; i<capacityPlusOne; i++) {
+ // fprintf(stream, "\t[%d]: %p\n", i, array[i].get()); // FIXME
+ }
+ fprintf(stream, "}\n");
+ }
+
+ /**
+ * Create a full ring buffer instance w/ the given array's net capacity and content.
+ * <p>
+ * Example for a 10 element Integer array:
+ * <pre>
+ * Integer[] source = new Integer[10];
+ * // fill source with content ..
+ * ringbuffer<Integer> rb = new ringbuffer<Integer>(source);
+ * </pre>
+ * </p>
+ * <p>
+ * {@link #isFull()} returns true on the newly created full ring buffer.
+ * </p>
+ * <p>
+ * Implementation will allocate an internal array with size of array <code>copyFrom</code> <i>plus one</i>,
+ * and copy all elements from array <code>copyFrom</code> into the internal array.
+ * </p>
+ * @param copyFrom mandatory source array determining ring buffer's net {@link #capacity()} and initial content.
+ * @throws IllegalArgumentException if <code>copyFrom</code> is <code>nullptr</code>
+ */
+ ringbuffer(const std::vector<T> & copyFrom) noexcept
+ : capacityPlusOne(copyFrom.size() + 1), array(newArray(capacityPlusOne)),
+ readPos(0), writePos(0), size(0)
+ {
+ resetImpl(copyFrom.data(), copyFrom.size());
+ }
+
+ ringbuffer(const T * copyFrom, const int copyFromSize) noexcept
+ : capacityPlusOne(copyFromSize + 1), array(newArray(capacityPlusOne)),
+ readPos(0), writePos(0), size(0)
+ {
+ resetImpl(copyFrom, copyFromSize);
+ }
+
+ /**
+ * Create an empty ring buffer instance w/ the given net <code>capacity</code>.
+ * <p>
+ * Example for a 10 element Integer array:
+ * <pre>
+ * ringbuffer<Integer> rb = new ringbuffer<Integer>(10, Integer[].class);
+ * </pre>
+ * </p>
+ * <p>
+ * {@link #isEmpty()} returns true on the newly created empty ring buffer.
+ * </p>
+ * <p>
+ * Implementation will allocate an internal array of size <code>capacity</code> <i>plus one</i>.
+ * </p>
+ * @param arrayType the array type of the created empty internal array.
+ * @param capacity the initial net capacity of the ring buffer
+ */
+ ringbuffer(const int capacity) noexcept
+ : capacityPlusOne(capacity + 1), array(newArray(capacityPlusOne)),
+ readPos(0), writePos(0), size(0)
+ { }
+
+ ~ringbuffer() noexcept {
+ freeArray(array);
+ }
+
+ ringbuffer(const ringbuffer &_source) noexcept
+ : capacityPlusOne(_source.capacityPlusOne), array(newArray(capacityPlusOne)),
+ readPos(0), writePos(0), size(0)
+ {
+ std::unique_lock<std::mutex> lockMultiReadS(_source.syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWriteS(_source.syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiReadS, lockMultiWriteS); // *this instance does not exist yet
+ cloneFrom(false, _source);
+ }
+
+ ringbuffer& operator=(const ringbuffer &_source) noexcept {
+ std::unique_lock<std::mutex> lockMultiReadS(_source.syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWriteS(_source.syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock); // same for *this instance!
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock);
+ std::lock(lockMultiReadS, lockMultiWriteS, lockMultiRead, lockMultiWrite);
+
+ if( this == &_source ) {
+ return *this;
+ }
+ if( capacityPlusOne != _source.capacityPlusOne ) {
+ cloneFrom(true, _source);
+ } else {
+ clearImpl(); // clear
+ cloneFrom(false, _source);
+ }
+ return *this;
+ }
+
+ ringbuffer(ringbuffer &&o) noexcept = default;
+ ringbuffer& operator=(ringbuffer &&o) noexcept = default;
+
+ int capacity() const noexcept override { return capacityPlusOne-1; }
+
+ void clear() noexcept override {
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiRead, lockMultiWrite);
+ clearImpl();
+ }
+
+ void reset(const T * copyFrom, const int copyFromCount) noexcept override {
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiRead, lockMultiWrite);
+ resetImpl(copyFrom, copyFromCount);
+ }
+
+ void reset(const std::vector<T> & copyFrom) noexcept override {
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiRead, lockMultiWrite);
+ resetImpl(copyFrom.data(), copyFrom.size());
+ }
+
+ int getSize() const noexcept override { return size; }
+
+ int getFreeSlots() const noexcept override { return capacityPlusOne - 1 - size; }
+
+ bool isEmpty() const noexcept override { return writePos == readPos; /* 0 == size */ }
+
+ bool isFull() const noexcept override { return ( writePos + 1 ) % capacityPlusOne == readPos ; /* capacityPlusOne - 1 == size */; }
+
+ T get() noexcept override { return getImpl(false, false, 0); }
+
+ T getBlocking(const int timeoutMS=0) noexcept override {
+ return getImpl(true, false, timeoutMS);
+ }
+
+ T peek() noexcept override {
+ return getImpl(false, true, 0);
+ }
+
+ T peekBlocking(const int timeoutMS=0) noexcept override {
+ return getImpl(true, true, timeoutMS);
+ }
+
+ int drop(const int count) noexcept override {
+ return dropImpl(count);
+ }
+
+ bool put(const T & e) noexcept override {
+ return putImpl(e, false, false, 0);
+ }
+
+ bool putBlocking(const T & e, const int timeoutMS=0) noexcept override {
+ return !putImpl(e, false, true, timeoutMS);
+ }
+
+ bool putSame() noexcept override {
+ return putImpl(nullelem, true, false, 0);
+ }
+
+ bool putSameBlocking(const int timeoutMS=0) noexcept override {
+ return putImpl(nullelem, true, true, timeoutMS);
+ }
+
+ void waitForFreeSlots(const int count) noexcept override {
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockRead(syncRead, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiWrite, lockRead);
+
+ while( capacityPlusOne - 1 - size < count ) {
+ cvRead.wait(lockRead);
+ }
+ }
+
+ void recapacity(const int newCapacity) override {
+ std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops
+ std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock); // otherwise RAII-style relinquish via destructor
+ std::lock(lockMultiRead, lockMultiWrite);
+ const int _size = size; // fast access
+
+ if( capacityPlusOne == newCapacity+1 ) {
+ return;
+ }
+ if( _size > newCapacity ) {
+ throw IllegalArgumentException("amount "+std::to_string(newCapacity)+" < size, "+toString(), E_FILE_LINE);
+ }
+ if( 0 > newCapacity ) {
+ throw IllegalArgumentException("amount "+std::to_string(newCapacity)+" < 0, "+toString(), E_FILE_LINE);
+ }
+
+ // save current data
+ int oldCapacityPlusOne = capacityPlusOne;
+ T * oldArray = array;
+ int oldReadPos = readPos;
+
+ // new blank resized array
+ capacityPlusOne = newCapacity + 1;
+ array = newArray(capacityPlusOne);
+ readPos = 0;
+ writePos = 0;
+
+ // copy saved data
+ if( nullptr != oldArray && 0 < _size ) {
+ int localWritePos = writePos;
+ for(int i=0; i<_size; i++) {
+ localWritePos = (localWritePos + 1) % capacityPlusOne;
+ oldReadPos = (oldReadPos + 1) % oldCapacityPlusOne;
+ array[localWritePos] = oldArray[oldReadPos];
+ }
+ writePos = localWritePos;
+ }
+ freeArray(oldArray); // and release
+ }
+};
+
+} /* namespace jau */
+
+#endif /* JAU_RINGBUFFER_HPP_ */
diff --git a/include/jau/ringbuffer_if.hpp b/include/jau/ringbuffer_if.hpp
new file mode 100644
index 0000000..d30990b
--- /dev/null
+++ b/include/jau/ringbuffer_if.hpp
@@ -0,0 +1,217 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_RINGBUFFER_IF_HPP_
+#define JAU_RINGBUFFER_IF_HPP_
+
+#include <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+
+#include <vector>
+
+#include <jau/basic_types.hpp>
+
+namespace jau {
+
+/**
+ * Ring buffer interface, a.k.a circular buffer.
+ * <p>
+ * Caller can chose whether to block until get / put is able to proceed or not.
+ * </p>
+ * <p>
+ * Caller can chose whether to pass an empty array and clear references at get,
+ * or using a preset array for circular access of same objects.
+ * </p>
+ * <p>
+ * Synchronization and hence thread safety details belong to the implementation.
+ * </p>
+ */
+template <typename T> class ringbuffer_if {
+ public:
+ virtual ~ringbuffer_if() noexcept {}
+
+ /** Returns a short string representation incl. size/capacity and internal r/w index (impl. dependent). */
+ virtual std::string toString() const noexcept = 0;
+
+ /** Debug functionality - Dumps the contents of the internal array. */
+ virtual void dump(FILE *stream, std::string prefix) const noexcept = 0;
+
+ /** Returns the net capacity of this ring buffer. */
+ virtual int capacity() const noexcept = 0;
+
+ /**
+ * Releasing all elements by assigning <code>null</code>.
+ * <p>
+ * {@link #isEmpty()} will return <code>true</code> and
+ * {@link #getSize()} will return <code>0</code> after calling this method.
+ * </p>
+ */
+ virtual void clear() noexcept = 0;
+
+ /**
+ * {@link #clear()} all elements and add all <code>copyFrom</code> elements thereafter.
+ * @param copyFrom Mandatory array w/ length {@link #capacity()} to be copied into the internal array.
+ */
+ virtual void reset(const T * copyFrom, const int copyFromCount) noexcept = 0;
+ virtual void reset(const std::vector<T> & copyFrom) noexcept = 0;
+
+ /** Returns the number of elements in this ring buffer. */
+ virtual int getSize() const noexcept = 0;
+
+ /** Returns the number of free slots available to put. */
+ virtual int getFreeSlots() const noexcept = 0;
+
+ /** Returns true if this ring buffer is empty, otherwise false. */
+ virtual bool isEmpty() const noexcept = 0;
+
+ /** Returns true if this ring buffer is full, otherwise false. */
+ virtual bool isFull() const noexcept = 0;
+
+ /**
+ * Dequeues the oldest enqueued element if available, otherwise null.
+ * <p>
+ * The returned ring buffer slot will be set to <code>null</code> to release the reference
+ * and move ownership to the caller.
+ * </p>
+ * <p>
+ * Method is non blocking and returns immediately;.
+ * </p>
+ * @return the oldest put element if available, otherwise null.
+ */
+ virtual T get() noexcept = 0;
+
+ /**
+ * Dequeues the oldest enqueued element.
+ * <p>
+ * The returned ring buffer slot will be set to <code>null</code> to release the reference
+ * and move ownership to the caller.
+ * </p>
+ * <p>
+ * <code>timeoutMS</code> defaults to zero,
+ * i.e. infinitive blocking until an element available via put.<br>
+ * Otherwise this methods blocks for the given milliseconds.
+ * </p>
+ * @return the oldest put element or <code>null</code> if timeout occurred.
+ * @throws InterruptedException
+ */
+ virtual T getBlocking(const int timeoutMS=0) noexcept = 0;
+
+ /**
+ * Peeks the next element at the read position w/o modifying pointer, nor blocking.
+ * @return <code>null</code> if empty, otherwise the element which would be read next.
+ */
+ virtual T peek() noexcept = 0;
+
+ /**
+ * Peeks the next element at the read position w/o modifying pointer, but with blocking.
+ * <p>
+ * <code>timeoutMS</code> defaults to zero,
+ * i.e. infinitive blocking until an element available via put.<br>
+ * Otherwise this methods blocks for the given milliseconds.
+ * </p>
+ * @return <code>null</code> if empty or timeout occurred, otherwise the element which would be read next.
+ */
+ virtual T peekBlocking(const int timeoutMS=0) noexcept = 0;
+
+ /**
+ * Drops up to {@code count} oldest enqueued elements.
+ * <p>
+ * Method is non blocking and returns immediately;.
+ * </p>
+ * @param count maximum number of elements to drop from ringbuffer.
+ * @return actual number of dropped elements.
+ */
+ virtual int drop(const int count) noexcept = 0;
+
+ /**
+ * Enqueues the given element.
+ * <p>
+ * Returns true if successful, otherwise false in case buffer is full.
+ * </p>
+ * <p>
+ * Method is non blocking and returns immediately;.
+ * </p>
+ */
+ virtual bool put(const T & e) noexcept = 0;
+
+ /**
+ * Enqueues the given element.
+ * <p>
+ * <code>timeoutMS</code> defaults to zero,
+ * i.e. infinitive blocking until a free slot becomes available via get.<br>
+ * Otherwise this methods blocks for the given milliseconds.
+ * </p>
+ * <p>
+ * Returns true if successful, otherwise false in case timeout occurred.
+ * </p>
+ */
+ virtual bool putBlocking(const T & e, const int timeoutMS=0) noexcept = 0;
+
+ /**
+ * Enqueues the same element at it's write position, if not full.
+ * <p>
+ * Returns true if successful, otherwise false in case buffer is full.
+ * </p>
+ * <p>
+ * Method is non blocking and returns immediately;.
+ * </p>
+ * @throws InterruptedException
+ */
+ virtual bool putSame() noexcept = 0;
+
+ /**
+ * Enqueues the same element at it's write position, if not full.
+ * <p>
+ * <code>timeoutMS</code> defaults to zero,
+ * i.e. infinitive blocking until a free slot becomes available via get.<br>
+ * Otherwise this methods blocks for the given milliseconds.
+ * </p>
+ * <p>
+ * Returns true if successful, otherwise false in case timeout occurred.
+ * </p>
+ * @throws InterruptedException
+ */
+ virtual bool putSameBlocking(const int timeoutMS=0) noexcept = 0;
+
+ /**
+ * Blocks until at least <code>count</code> free slots become available.
+ * @throws InterruptedException
+ */
+ virtual void waitForFreeSlots(const int count) noexcept = 0;
+
+ /**
+ * Resizes this ring buffer's capacity.
+ * <p>
+ * New capacity must be greater than current size.
+ * </p>
+ */
+ virtual void recapacity(const int newCapacity) = 0;
+};
+
+} /* namespace jau */
+
+#endif /* JAU_RINGBUFFER_IF_HPP_ */
diff --git a/include/jau/version.hpp b/include/jau/version.hpp
new file mode 100644
index 0000000..bd85a98
--- /dev/null
+++ b/include/jau/version.hpp
@@ -0,0 +1,37 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2020 Gothel Software e.K.
+ * Copyright (c) 2020 ZAFENA AB
+ *
+ * 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.
+ */
+
+#ifndef JAU_VERSION_HPP_
+#define JAU_VERSION_HPP_
+
+namespace jau {
+
+ extern const char* VERSION;
+ extern const char* VERSION_SHORT;
+ extern const char* VERSION_API;
+
+} /* namespace jau */
+
+#endif /* JAU_VERSION_HPP_ */