diff options
author | Sven Gothel <[email protected]> | 2020-10-16 16:08:23 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-10-16 16:08:23 +0200 |
commit | d5c6787a8d7e9c123b6c30dd877af58057df4204 (patch) | |
tree | d719b90b1a374de02f354faa2fdeac19e762b7ce /include | |
parent | da13a199446d143732cc6d0d996501bac1c1d4f8 (diff) |
Move jaucpp files to root-folder
Diffstat (limited to 'include')
-rw-r--r-- | include/cppunit/cppunit.h | 110 | ||||
-rw-r--r-- | include/jau/basic_algos.hpp | 78 | ||||
-rw-r--r-- | include/jau/basic_types.hpp | 433 | ||||
-rw-r--r-- | include/jau/cow_vector.hpp | 62 | ||||
-rw-r--r-- | include/jau/debug.hpp | 162 | ||||
-rw-r--r-- | include/jau/dfa_utf8_decode.hpp | 55 | ||||
-rw-r--r-- | include/jau/environment.hpp | 267 | ||||
-rw-r--r-- | include/jau/function_def.hpp | 413 | ||||
-rw-r--r-- | include/jau/java_uplink.hpp | 93 | ||||
-rw-r--r-- | include/jau/jni/helper_jni.hpp | 434 | ||||
-rw-r--r-- | include/jau/jni/jni_mem.hpp | 198 | ||||
-rw-r--r-- | include/jau/ordered_atomic.hpp | 218 | ||||
-rw-r--r-- | include/jau/ringbuffer.hpp | 491 | ||||
-rw-r--r-- | include/jau/ringbuffer_if.hpp | 217 | ||||
-rw-r--r-- | include/jau/version.hpp | 37 |
15 files changed, 3268 insertions, 0 deletions
diff --git a/include/cppunit/cppunit.h b/include/cppunit/cppunit.h new file mode 100644 index 0000000..09c60fb --- /dev/null +++ b/include/cppunit/cppunit.h @@ -0,0 +1,110 @@ +#ifndef CPPUNIT_H +#define CPPUNIT_H + +// Required headers, or just use #include <bits/stdc++.h> +#include <iostream> +#include <sstream> +#include <cstring> +#include <string> +#include <ctime> +#include <cmath> + + +// CPlusPlusUnit - C++ Unit testing TDD framework (github.com/cppunit/cppunit) +class Cppunit { + + private: + static float machineFloatEpsilon() { + float x = 1.0f, res; + do { + res = x; + } while (1.0f + (x /= 2.0f) > 1.0f); + return res; + } + + static double machineDoubleEpsilon() { + double x = 1.0, res; + do { + res = x; + } while (1.0 + (x /= 2.0) > 1.0); + return res; + } + + public: + + #define PRINTM(m) print(m, __FILE__, __LINE__, __FUNCTION__); + #define CHECK(a,b) check<long long>("", a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__); + #define CHECKM(m,a,b) check<long long>(m, a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__); + #define CHECKD(m,a,b) checkDelta<double>(m, a, b, doubleEpsilon, #a, #b, __FILE__, __LINE__, __FUNCTION__); + #define CHECKDD(m,a,b,c) checkDelta<double>(m, a, b, c, #a, #b, __FILE__, __LINE__, __FUNCTION__); + #define CHECKT(a) check<bool>("", a, true, #a, "true", __FILE__, __LINE__, __FUNCTION__); + #define CHECKTM(m,a) check<bool>(m, a, true, #a, "true", __FILE__, __LINE__, __FUNCTION__); + #define CHECKS(a,b) check<cs>("", a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__); + #define CHECKSM(m,a,b) check<cs>(m, a, b, #a, #b, __FILE__, __LINE__, __FUNCTION__); + + typedef const std::string& cs; + + int checks, fails; std::ostringstream serr; std::istringstream *in; + float floatEpsilon; + double doubleEpsilon; + + Cppunit() + : checks(0), fails(0), floatEpsilon(machineFloatEpsilon()), doubleEpsilon(machineDoubleEpsilon()) + {} + + virtual ~Cppunit() {} + + void test_cin(cs s){ in = new std::istringstream(s); std::cin.rdbuf(in->rdbuf()); } + + void fail_hdr(cs stra, cs strb, cs file, int line, cs func) { + serr << "==================================================" << std::endl; + serr << "FAIL: " << func << std::endl; + serr << "--------------------------------------------------" << std::endl; + serr << "File \"" << file << "\", line " << line << " in " << func << std::endl; + serr << " Checking " << stra << " == " << strb << std::endl; + } + + static void print(cs m, cs file, int line, cs func) { + std::cerr << std::endl << m << "; file \"" << file << "\", line " << line << " in " << func << std::endl; + } + + template <typename T> void check(cs m, T a, T b, cs stra, cs strb, cs file, int line, cs func) { + checks++; if (a == b) { std::cout << "."; return; } + fails++; std::cout << "F"; fail_hdr(stra, strb, file, line, func); + serr << " Error: " << m << ": \"" << a << "\" ! = \"" << b << "\"" << std::endl << std::endl; + } + + template <typename T> void checkDelta(cs m, T a, T b, T d, cs stra, cs strb, cs file, int line, cs func) { + checks++; if ( labs ( a - b ) < d ) { std::cout << "."; return; } + fails++; std::cout << "F"; fail_hdr(stra, strb, file, line, func); + serr << " Error: " << m << ": \"" << a << "\" ! = \"" << b << "\" (delta " << d << ")" << std::endl << std::endl; + } + + virtual void single_test() {} + virtual void test_list() { single_test(); } + double dclock() { return double(clock()) / CLOCKS_PER_SEC; } + int status() { + std::cout << std::endl; if (fails) std::cout << serr.str(); + std::cout << "--------------------------------------------------" << std::endl; + std::cout << "Ran " << checks << " checks in " << dclock() << "s" << std::endl << std::endl; + if (fails) std::cout << "FAILED (failures=" << fails << ")"; else std::cout << "OK" << std::endl; + return fails > 0; + } + int run() { std::streambuf* ocin = std::cin.rdbuf(); test_list(); std::cin.rdbuf(ocin); return status(); } +}; + +template<> void Cppunit::checkDelta<float>(cs m, float a, float b, float epsilon, cs stra, cs strb, cs file, int line, cs func) { + checks++; if ( fabsf( a - b ) < epsilon ) { std::cout << "."; return; } + fails++; std::cout << "F"; fail_hdr(stra, strb, file, line, func); + serr << " Error: " << m << ": \"" << a << "\" ! = \"" << b << "\" (epsilon " << epsilon << ")" << std::endl << std::endl; +} + +template<> void Cppunit::checkDelta<double>(cs m, double a, double b, double epsilon, cs stra, cs strb, cs file, int line, cs func) { + checks++; if ( fabs( a - b ) < epsilon ) { std::cout << "."; return; } + fails++; std::cout << "F"; fail_hdr(stra, strb, file, line, func); + serr << " Error: " << m << ": \"" << a << "\" ! = \"" << b << "\" (epsilon " << epsilon << ")" << std::endl << std::endl; +} + + +#endif // CPPUNIT_H + 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_ */ |