pvxslibs 1.5.0__cp310-cp310-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pvxslibs/__init__.py +0 -0
- pvxslibs/dbd/pvxsIoc.dbd +8 -0
- pvxslibs/include/pvxs/client.h +1094 -0
- pvxslibs/include/pvxs/data.h +948 -0
- pvxslibs/include/pvxs/iochooks.h +170 -0
- pvxslibs/include/pvxs/log.h +148 -0
- pvxslibs/include/pvxs/netcommon.h +82 -0
- pvxslibs/include/pvxs/nt.h +208 -0
- pvxslibs/include/pvxs/server.h +238 -0
- pvxslibs/include/pvxs/sharedArray.h +748 -0
- pvxslibs/include/pvxs/sharedpv.h +121 -0
- pvxslibs/include/pvxs/source.h +290 -0
- pvxslibs/include/pvxs/srvcommon.h +148 -0
- pvxslibs/include/pvxs/unittest.h +327 -0
- pvxslibs/include/pvxs/util.h +354 -0
- pvxslibs/include/pvxs/version.h +97 -0
- pvxslibs/include/pvxs/versionNum.h +6 -0
- pvxslibs/ioc.py +10 -0
- pvxslibs/lib/__init__.py +0 -0
- pvxslibs/lib/event_core_dsoinfo.py +14 -0
- pvxslibs/lib/event_pthread_dsoinfo.py +14 -0
- pvxslibs/lib/libevent_core.so +0 -0
- pvxslibs/lib/libevent_core.so.2.2.0 +0 -0
- pvxslibs/lib/libevent_pthread.so +0 -0
- pvxslibs/lib/libevent_pthread.so.2.2.0 +0 -0
- pvxslibs/lib/libpvxs.so +0 -0
- pvxslibs/lib/libpvxs.so.1.5 +0 -0
- pvxslibs/lib/libpvxsIoc.so +0 -0
- pvxslibs/lib/libpvxsIoc.so.1.5 +0 -0
- pvxslibs/lib/pvxsIoc_dsoinfo.py +14 -0
- pvxslibs/lib/pvxs_dsoinfo.py +14 -0
- pvxslibs/path.py +12 -0
- pvxslibs/test/__init__.py +0 -0
- pvxslibs/test/test_load.py +30 -0
- pvxslibs/version.py +32 -0
- pvxslibs-1.5.0.dist-info/METADATA +44 -0
- pvxslibs-1.5.0.dist-info/RECORD +40 -0
- pvxslibs-1.5.0.dist-info/WHEEL +5 -0
- pvxslibs-1.5.0.dist-info/licenses/LICENSE +26 -0
- pvxslibs-1.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright - See the COPYRIGHT that is included with this distribution.
|
|
3
|
+
* pvxs is distributed subject to a Software License Agreement found
|
|
4
|
+
* in file LICENSE that is included with this distribution.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#ifndef PVXS_UNITTEST_H
|
|
8
|
+
#define PVXS_UNITTEST_H
|
|
9
|
+
|
|
10
|
+
/** @file pvxs/unittest.h
|
|
11
|
+
*
|
|
12
|
+
* C++ helpers for use with epicsUnitTest.h
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
#include <sstream>
|
|
16
|
+
#include <vector>
|
|
17
|
+
#include <functional>
|
|
18
|
+
#include <type_traits>
|
|
19
|
+
|
|
20
|
+
#include <pvxs/version.h>
|
|
21
|
+
#include <pvxs/util.h>
|
|
22
|
+
|
|
23
|
+
namespace pvxs {
|
|
24
|
+
|
|
25
|
+
/** Prepare for testing. Call after testPlan()
|
|
26
|
+
* @since 1.2.0 If linked with pvxsIoc library, PVA server started
|
|
27
|
+
* by ``iocInit()`` will use "isolated" configuration.
|
|
28
|
+
*/
|
|
29
|
+
PVXS_API
|
|
30
|
+
void testSetup();
|
|
31
|
+
|
|
32
|
+
/** Free some internal global allocations to avoid false positives in
|
|
33
|
+
* valgrind (or similar) tools looking for memory leaks.
|
|
34
|
+
*
|
|
35
|
+
* Calls libevent_global_shutdown() when available (libevent >=2.1).
|
|
36
|
+
*
|
|
37
|
+
* @warning This function is optional.
|
|
38
|
+
* If you don't understand the intended use case, then do not call it!
|
|
39
|
+
*
|
|
40
|
+
* @pre Caller must release all resources explicitly allocated through PVXS (on all threads).
|
|
41
|
+
* @post Invalidates internal state.
|
|
42
|
+
* Use of __any__ API functions afterwards is undefined!
|
|
43
|
+
*/
|
|
44
|
+
PVXS_API
|
|
45
|
+
void cleanup_for_valgrind();
|
|
46
|
+
|
|
47
|
+
/** A single test case (or diagnostic line).
|
|
48
|
+
*
|
|
49
|
+
* Acts as an output string to accumulate test comment.
|
|
50
|
+
* Multi-line output results in one test line, and subsequent diagnostic lines.
|
|
51
|
+
*
|
|
52
|
+
* Test line is printed when an active (non-moved) testCase is destroyed.
|
|
53
|
+
*/
|
|
54
|
+
class PVXS_API testCase
|
|
55
|
+
{
|
|
56
|
+
enum {
|
|
57
|
+
Nothing, // after move()'d
|
|
58
|
+
Diag, // no test, just print
|
|
59
|
+
Pass,
|
|
60
|
+
Fail,
|
|
61
|
+
} result;
|
|
62
|
+
std::ostringstream msg;
|
|
63
|
+
public:
|
|
64
|
+
//! new diag message
|
|
65
|
+
testCase();
|
|
66
|
+
//! new test case
|
|
67
|
+
explicit testCase(bool result);
|
|
68
|
+
testCase(const testCase&) = delete;
|
|
69
|
+
testCase& operator=(const testCase&) = delete;
|
|
70
|
+
testCase(testCase&&) noexcept;
|
|
71
|
+
testCase& operator=(testCase&&) noexcept;
|
|
72
|
+
~testCase();
|
|
73
|
+
|
|
74
|
+
//! true when passing
|
|
75
|
+
explicit operator bool() const { return result==Pass; }
|
|
76
|
+
|
|
77
|
+
//! Override current pass/fail result
|
|
78
|
+
testCase& setPass(bool v) {
|
|
79
|
+
result = v ? Pass : Fail;
|
|
80
|
+
return *this;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//! Override current pass/fail result if input matches a regular expression
|
|
84
|
+
//! @since 0.2.1 Expression syntax is POSIX extended.
|
|
85
|
+
//! @since 0.1.1 Added
|
|
86
|
+
testCase& setPassMatch(const std::string& expr, const std::string& inp);
|
|
87
|
+
|
|
88
|
+
//! Append to message
|
|
89
|
+
template<typename T>
|
|
90
|
+
inline testCase& operator<<(const T& v) {
|
|
91
|
+
msg<<v;
|
|
92
|
+
return *this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Access to underlying std::ostream used to accumulate notes.
|
|
96
|
+
* When our operator<< isn't enough.
|
|
97
|
+
* @since 1.1.4
|
|
98
|
+
*/
|
|
99
|
+
inline
|
|
100
|
+
std::ostream& stream() { return msg; }
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
namespace detail {
|
|
104
|
+
|
|
105
|
+
// control how testEq() and testNotEq() print things
|
|
106
|
+
template<typename T, typename Enable=void>
|
|
107
|
+
struct test_print {
|
|
108
|
+
template<class C>
|
|
109
|
+
static inline void op(C& strm, const T& v) {
|
|
110
|
+
strm<<v;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
template <>
|
|
114
|
+
struct test_print<std::string> {
|
|
115
|
+
template<class C>
|
|
116
|
+
static inline void op(C& strm, const std::string& v) {
|
|
117
|
+
strm<<'"'<<escape(v)<<'"';
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
template <>
|
|
121
|
+
struct test_print<const char*> {
|
|
122
|
+
template<class C>
|
|
123
|
+
static inline void op(C& strm, const char* v) {
|
|
124
|
+
strm<<'"'<<escape(v)<<'"';
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
template<typename E>
|
|
128
|
+
struct test_print<std::vector<E>, typename std::enable_if<sizeof(E)==1>::type> {
|
|
129
|
+
template<class C>
|
|
130
|
+
static inline void op(C& strm, const std::vector<E>& v) {
|
|
131
|
+
strm<<'"'<<escape((const char*)v.data(), v.size())<<'"';
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
template <typename E>
|
|
135
|
+
struct test_print<E, typename std::enable_if<std::is_same<E, char>::value||std::is_same<E, signed char>::value||std::is_same<E, unsigned char>::value>::type> {
|
|
136
|
+
template<class C>
|
|
137
|
+
static inline void op(C& strm, char v) {
|
|
138
|
+
strm<<int(v);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
template<typename LHS, typename RHS>
|
|
143
|
+
testCase testEq(const char *sLHS, const LHS& lhs, const char *sRHS, const RHS& rhs)
|
|
144
|
+
{
|
|
145
|
+
testCase ret(lhs==rhs);
|
|
146
|
+
ret<<sLHS<<" (";
|
|
147
|
+
test_print<LHS>::op(ret, lhs);
|
|
148
|
+
ret<<") == "<<sRHS<<" (";
|
|
149
|
+
test_print<RHS>::op(ret, rhs);
|
|
150
|
+
ret<<") ";
|
|
151
|
+
return ret;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
template<typename LHS, typename RHS>
|
|
155
|
+
testCase testNotEq(const char *sLHS, const LHS& lhs, const char *sRHS, const RHS& rhs)
|
|
156
|
+
{
|
|
157
|
+
testCase ret(lhs!=rhs);
|
|
158
|
+
ret<<sLHS<<" (";
|
|
159
|
+
test_print<LHS>::op(ret, lhs);
|
|
160
|
+
ret<<") != "<<sRHS<<" (";
|
|
161
|
+
test_print<RHS>::op(ret, rhs);
|
|
162
|
+
ret<<") ";
|
|
163
|
+
return ret;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
template<typename T>
|
|
167
|
+
struct as_str {
|
|
168
|
+
static const char* op(const T& v) { return v; }
|
|
169
|
+
};
|
|
170
|
+
template<> struct as_str<std::string> {
|
|
171
|
+
static const char* op(const std::string& v) { return v.c_str(); }
|
|
172
|
+
};
|
|
173
|
+
template<> struct as_str<const std::string> {
|
|
174
|
+
static const char* op(const std::string& v) { return v.c_str(); }
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
template<typename T>
|
|
178
|
+
const char* asStr(const T& v) { return as_str<T>::op(v); }
|
|
179
|
+
|
|
180
|
+
PVXS_API
|
|
181
|
+
testCase _testStrTest(unsigned op, const char *sLHS, const char* lhs, const char *sRHS, const char* rhs);
|
|
182
|
+
|
|
183
|
+
PVXS_API
|
|
184
|
+
testCase _testStrMatch(const char *spat, const std::string& pat, const char *sstr, const std::string& str);
|
|
185
|
+
|
|
186
|
+
template<typename LHS, typename RHS>
|
|
187
|
+
testCase testArrEq(const char *sLHS, const LHS& lhs, const char *sRHS, const RHS& rhs)
|
|
188
|
+
{
|
|
189
|
+
bool eq = lhs.size()==rhs.size();
|
|
190
|
+
testCase ret;
|
|
191
|
+
ret<<sLHS<<" (";
|
|
192
|
+
test_print<LHS>::op(ret, lhs);
|
|
193
|
+
ret<<") == "<<sRHS<<" (";
|
|
194
|
+
test_print<RHS>::op(ret, rhs);
|
|
195
|
+
ret<<")\n";
|
|
196
|
+
for(size_t i=0; i<lhs.size() && i<rhs.size(); i++) {
|
|
197
|
+
if(lhs[i]!=rhs[i]) {
|
|
198
|
+
eq = false;
|
|
199
|
+
ret<<" ["<<i<<"] -> ";
|
|
200
|
+
test_print<typename std::decay<decltype (lhs[i])>::type>::op(ret, lhs[i]);
|
|
201
|
+
ret<<" != ";
|
|
202
|
+
test_print<typename std::decay<decltype (rhs[i])>::type>::op(ret, rhs[i]);
|
|
203
|
+
ret<<"\n";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
ret.setPass(eq);
|
|
207
|
+
return ret;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
} // namespace detail
|
|
211
|
+
|
|
212
|
+
/** Assert that an exception is thrown.
|
|
213
|
+
*
|
|
214
|
+
* @tparam Exception The exception type which should be thrown
|
|
215
|
+
* @param fn A callable
|
|
216
|
+
*
|
|
217
|
+
* @returns A testCase which passes if an Exception instance was caught,
|
|
218
|
+
* and false otherwise (wrong type, or no exception).
|
|
219
|
+
*
|
|
220
|
+
* @code
|
|
221
|
+
* testThrows<std::runtime_error>([]() {
|
|
222
|
+
* testShow()<<"Now you see me";
|
|
223
|
+
* throw std::runtime_error("I happened");
|
|
224
|
+
* testShow()<<"Now you don't";
|
|
225
|
+
* })<<"some message";
|
|
226
|
+
* @endcode
|
|
227
|
+
*/
|
|
228
|
+
template<class Exception, typename FN>
|
|
229
|
+
testCase testThrows(FN fn)
|
|
230
|
+
{
|
|
231
|
+
testCase ret(false);
|
|
232
|
+
try {
|
|
233
|
+
fn();
|
|
234
|
+
ret<<"Unexpected success - ";
|
|
235
|
+
}catch(Exception& e){
|
|
236
|
+
ret.setPass(true)<<"Expected exception \""<<e.what()<<"\" - ";
|
|
237
|
+
}catch(std::exception& e){
|
|
238
|
+
ret<<"Unexpected exception "<<typeid(e).name()<<" \""<<e.what()<<"\" - ";
|
|
239
|
+
}
|
|
240
|
+
return ret;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Assert that an exception is throw with a certain message.
|
|
244
|
+
*
|
|
245
|
+
* @tparam Exception The exception type which should be thrown
|
|
246
|
+
* @param expr A regular expression
|
|
247
|
+
* @param fn A callable
|
|
248
|
+
*
|
|
249
|
+
* @returns A testCase which passes if an Exception instance was caught
|
|
250
|
+
* and std::exception::what() matched the provided regular expression.
|
|
251
|
+
*
|
|
252
|
+
* @code
|
|
253
|
+
* testThrowsMatch<std::runtime_error>("happened", []() {
|
|
254
|
+
* testShow()<<"Now you see me";
|
|
255
|
+
* throw std::runtime_error("I happened");
|
|
256
|
+
* testShow()<<"Now you don't";
|
|
257
|
+
* })<<"some message";
|
|
258
|
+
* @endcode
|
|
259
|
+
*
|
|
260
|
+
* @since 0.1.1
|
|
261
|
+
*/
|
|
262
|
+
template<class Exception, typename FN>
|
|
263
|
+
testCase testThrowsMatch(const std::string& expr, FN fn)
|
|
264
|
+
{
|
|
265
|
+
testCase ret(false);
|
|
266
|
+
try {
|
|
267
|
+
fn();
|
|
268
|
+
ret<<"Unexpected success - ";
|
|
269
|
+
}catch(Exception& e){
|
|
270
|
+
ret.setPassMatch(expr, e.what())<<"Expected matching (\""<<expr<<"\") exception \""<<e.what()<<"\" - ";
|
|
271
|
+
}catch(std::exception& e){
|
|
272
|
+
ret<<"Unexpected exception "<<typeid(e).name()<<" \""<<e.what()<<"\" - ";
|
|
273
|
+
}
|
|
274
|
+
return ret;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
} // namespace pvxs
|
|
278
|
+
|
|
279
|
+
//! Macro which assert that an expression evaluate to 'true'.
|
|
280
|
+
//! Evaluates to a pvxs::testCase
|
|
281
|
+
#define testTrue(EXPR) ::pvxs::testCase(EXPR)<<(" " #EXPR)
|
|
282
|
+
|
|
283
|
+
//! Macro which assert that an expression evaluate to 'true'.
|
|
284
|
+
//! Evaluates to a pvxs::testCase
|
|
285
|
+
#define testFalse(EXPR) ::pvxs::testCase(!(EXPR))<<(" !" #EXPR)
|
|
286
|
+
|
|
287
|
+
//! Macro which asserts equality between LHS and RHS.
|
|
288
|
+
//! Evaluates to a pvxs::testCase
|
|
289
|
+
//! Roughly equivalent to @code testOk((LHS)==(RHS), "..."); @endcode
|
|
290
|
+
#define testEq(LHS, RHS) ::pvxs::detail::testEq(#LHS, LHS, #RHS, RHS)
|
|
291
|
+
|
|
292
|
+
//! Macro which asserts in-equality between LHS and RHS.
|
|
293
|
+
//! Evaluates to a pvxs::testCase
|
|
294
|
+
//! Roughly equivalent to @code testOk((LHS)!=(RHS), "..."); @endcode
|
|
295
|
+
#define testNotEq(LHS, RHS) ::pvxs::detail::testNotEq(#LHS, LHS, #RHS, RHS)
|
|
296
|
+
|
|
297
|
+
//! Macro which asserts equality between LHS and RHS.
|
|
298
|
+
//! Evaluates to a pvxs::testCase
|
|
299
|
+
//! Functionally equivalent to testEq() with two std::string instances.
|
|
300
|
+
//! Prints diff-like output which is friendlier to multi-line strings.
|
|
301
|
+
#define testStrEq(LHS, RHS) ::pvxs::detail::_testStrTest(1, #LHS, ::pvxs::detail::asStr(LHS), #RHS, ::pvxs::detail::asStr(RHS))
|
|
302
|
+
|
|
303
|
+
//! Macro which asserts inequality between LHS and RHS.
|
|
304
|
+
//! Evaluates to a pvxs::testCase
|
|
305
|
+
//! Functionally equivalent to testNotEq() with two std::string instances.
|
|
306
|
+
//! Prints diff-like output which is friendlier to multi-line strings.
|
|
307
|
+
//! @since 0.2.0
|
|
308
|
+
#define testStrNotEq(LHS, RHS) ::pvxs::detail::_testStrTest(0, #LHS, ::pvxs::detail::asStr(LHS), #RHS, ::pvxs::detail::asStr(RHS))
|
|
309
|
+
|
|
310
|
+
//! Macro which asserts that STR matches the regular expression EXPR
|
|
311
|
+
//! Evaluates to a pvxs::testCase
|
|
312
|
+
//! @since 0.2.1 Expression syntax is POSIX extended.
|
|
313
|
+
//! @since 0.1.1
|
|
314
|
+
#define testStrMatch(EXPR, STR) ::pvxs::detail::_testStrMatch(#EXPR, EXPR, #STR, STR)
|
|
315
|
+
|
|
316
|
+
//! Macro which asserts equality between LHS and RHS.
|
|
317
|
+
//! Evaluates to a pvxs::testCase
|
|
318
|
+
//! Functionally equivalent to testEq() for objects with .size() and operator[].
|
|
319
|
+
//! Prints element by element differences
|
|
320
|
+
#define testArrEq(LHS, RHS) ::pvxs::detail::testArrEq(#LHS, LHS, #RHS, RHS)
|
|
321
|
+
|
|
322
|
+
//! Macro which prints diagnostic (non-test) lines.
|
|
323
|
+
//! Evaluates to a pvxs::testCase
|
|
324
|
+
//! Roughly equivalent to @code testDiag("..."); @endcode
|
|
325
|
+
#define testShow() ::pvxs::testCase()
|
|
326
|
+
|
|
327
|
+
#endif // PVXS_UNITTEST_H
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright - See the COPYRIGHT that is included with this distribution.
|
|
3
|
+
* pvxs is distributed subject to a Software License Agreement found
|
|
4
|
+
* in file LICENSE that is included with this distribution.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#ifndef PVXS_UTIL_H
|
|
8
|
+
#define PVXS_UTIL_H
|
|
9
|
+
|
|
10
|
+
#include <map>
|
|
11
|
+
#include <array>
|
|
12
|
+
#include <deque>
|
|
13
|
+
#include <functional>
|
|
14
|
+
#include <iosfwd>
|
|
15
|
+
#include <type_traits>
|
|
16
|
+
#include <stdexcept>
|
|
17
|
+
#include <memory>
|
|
18
|
+
|
|
19
|
+
#include <osiSock.h>
|
|
20
|
+
#include <epicsEvent.h>
|
|
21
|
+
#include <epicsMutex.h>
|
|
22
|
+
#include <epicsGuard.h>
|
|
23
|
+
|
|
24
|
+
#include <pvxs/version.h>
|
|
25
|
+
|
|
26
|
+
namespace pvxs {
|
|
27
|
+
|
|
28
|
+
namespace detail {
|
|
29
|
+
// ref. wrapper to mark string for escaping
|
|
30
|
+
class Escaper
|
|
31
|
+
{
|
|
32
|
+
const char* val;
|
|
33
|
+
size_t count;
|
|
34
|
+
friend
|
|
35
|
+
PVXS_API
|
|
36
|
+
std::ostream& operator<<(std::ostream& strm, const Escaper& esc);
|
|
37
|
+
public:
|
|
38
|
+
PVXS_API explicit Escaper(const char* v);
|
|
39
|
+
constexpr explicit Escaper(const char* v, size_t l) :val(v),count(l) {}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
PVXS_API
|
|
43
|
+
std::ostream& operator<<(std::ostream& strm, const Escaper& esc);
|
|
44
|
+
|
|
45
|
+
} // namespace detail
|
|
46
|
+
|
|
47
|
+
//! Print string to output stream with non-printable characters escaped.
|
|
48
|
+
//!
|
|
49
|
+
//! Outputs (almost) C-style escapes.
|
|
50
|
+
//! Prefers short escapes for newline, tab, quote, etc ("\\n").
|
|
51
|
+
//! Falls back to hex escape (eg. "\xab").
|
|
52
|
+
//!
|
|
53
|
+
//! Unlike C, hex escapes are always 2 chars. eg. the output "\xabcase"
|
|
54
|
+
//! would need to be manually changed to "\xab""case" to be used as C source.
|
|
55
|
+
//!
|
|
56
|
+
//! @code
|
|
57
|
+
//! std::string blah("this \"is a test\"");
|
|
58
|
+
//! std::cout<<pvxs::escape(blah);
|
|
59
|
+
//! @endcode
|
|
60
|
+
inline detail::Escaper escape(const std::string& s) {
|
|
61
|
+
return detail::Escaper(s.c_str(), s.size());
|
|
62
|
+
}
|
|
63
|
+
//! Print nil terminated char array to output stream with non-printable characters escaped.
|
|
64
|
+
//! @code
|
|
65
|
+
//! std::cout<<pvxs::escape("this \"is a test\"");
|
|
66
|
+
//! @endcode
|
|
67
|
+
inline detail::Escaper escape(const char* s) {
|
|
68
|
+
return detail::Escaper(s);
|
|
69
|
+
}
|
|
70
|
+
//! Print fixed length char array to output stream with non-printable characters escaped.
|
|
71
|
+
//! @code
|
|
72
|
+
//! std::cout<<pvxs::escape("this \"is a test\"", 6);
|
|
73
|
+
//! // prints 'this \"'
|
|
74
|
+
//! @endcode
|
|
75
|
+
inline detail::Escaper escape(const char* s,size_t n) {
|
|
76
|
+
return detail::Escaper(s,n);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
struct ServerGUID : public std::array<uint8_t, 12> {};
|
|
80
|
+
|
|
81
|
+
PVXS_API
|
|
82
|
+
std::ostream& operator<<(std::ostream&, const ServerGUID&);
|
|
83
|
+
|
|
84
|
+
#if !defined(__rtems__) && !defined(vxWorks)
|
|
85
|
+
|
|
86
|
+
/** Portable process signal handling in CLI tools.
|
|
87
|
+
*
|
|
88
|
+
* @code
|
|
89
|
+
* epicsEvent evt;
|
|
90
|
+
* SigInt handle([&evt]() {
|
|
91
|
+
* evt.trigger();
|
|
92
|
+
* });
|
|
93
|
+
* ... setup network operations
|
|
94
|
+
* evt.wait();
|
|
95
|
+
* // completion, or SIGINT
|
|
96
|
+
* @endcode
|
|
97
|
+
*
|
|
98
|
+
* Saves existing handler, which are restored by dtor.
|
|
99
|
+
*
|
|
100
|
+
* @since 1.1.0 "handler" action runs in thread context.
|
|
101
|
+
* Safe to take locks etc.
|
|
102
|
+
* Previously handler action ran in signal handler context.
|
|
103
|
+
*/
|
|
104
|
+
class PVXS_API SigInt {
|
|
105
|
+
public:
|
|
106
|
+
//! Install signal handler.
|
|
107
|
+
SigInt(const std::function<void()>&& handler);
|
|
108
|
+
SigInt(const SigInt&) = delete;
|
|
109
|
+
~SigInt();
|
|
110
|
+
struct Pvt;
|
|
111
|
+
private:
|
|
112
|
+
std::shared_ptr<Pvt> pvt;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
#else // !defined(__rtems__) && !defined(vxWorks)
|
|
116
|
+
|
|
117
|
+
class SigInt {
|
|
118
|
+
const std::function<void()> handler;
|
|
119
|
+
public:
|
|
120
|
+
SigInt(std::function<void()>&& handler) :handler(std::move(handler)) {}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
#endif // !defined(__rtems__) && !defined(vxWorks)
|
|
124
|
+
|
|
125
|
+
//! return a snapshot of internal instance counters
|
|
126
|
+
PVXS_API
|
|
127
|
+
std::map<std::string, size_t> instanceSnapshot();
|
|
128
|
+
|
|
129
|
+
//! See Indented
|
|
130
|
+
struct indent {};
|
|
131
|
+
|
|
132
|
+
PVXS_API
|
|
133
|
+
std::ostream& operator<<(std::ostream& strm, const indent&);
|
|
134
|
+
|
|
135
|
+
//! Scoped indentation for std::ostream
|
|
136
|
+
struct PVXS_API Indented {
|
|
137
|
+
explicit Indented(std::ostream& strm, int depth=1);
|
|
138
|
+
Indented(const Indented&) = delete;
|
|
139
|
+
Indented(Indented&& o) noexcept
|
|
140
|
+
:strm(o.strm)
|
|
141
|
+
,depth(o.depth)
|
|
142
|
+
{
|
|
143
|
+
o.strm = nullptr;
|
|
144
|
+
o.depth = 0;
|
|
145
|
+
}
|
|
146
|
+
~Indented();
|
|
147
|
+
private:
|
|
148
|
+
std::ostream *strm;
|
|
149
|
+
int depth;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
struct PVXS_API Detailed {
|
|
153
|
+
explicit Detailed(std::ostream& strm, int lvl=1);
|
|
154
|
+
Detailed(const Detailed&) = delete;
|
|
155
|
+
Detailed(Detailed&& o) noexcept
|
|
156
|
+
:strm(o.strm)
|
|
157
|
+
,lvl(o.lvl)
|
|
158
|
+
{
|
|
159
|
+
o.strm = nullptr;
|
|
160
|
+
o.lvl = 0;
|
|
161
|
+
}
|
|
162
|
+
~Detailed();
|
|
163
|
+
static
|
|
164
|
+
int level(std::ostream& strm);
|
|
165
|
+
private:
|
|
166
|
+
std::ostream *strm;
|
|
167
|
+
int lvl;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/** Describe build and runtime configuration of current system.
|
|
171
|
+
*
|
|
172
|
+
* Print information which may be using for when troubleshooting,
|
|
173
|
+
* or creating a bug report.
|
|
174
|
+
*
|
|
175
|
+
* Printed by CLI "pvxinfo -D" and iocsh "pvxs_target_information".
|
|
176
|
+
*
|
|
177
|
+
* @returns The same ostream passed as argument.
|
|
178
|
+
*/
|
|
179
|
+
PVXS_API
|
|
180
|
+
std::ostream& target_information(std::ostream&);
|
|
181
|
+
|
|
182
|
+
/** Print version information for PVXS library and dependencies
|
|
183
|
+
*
|
|
184
|
+
* As shown by eg. ``pvxget -V``
|
|
185
|
+
*
|
|
186
|
+
* @returns The same ostream passed as argument.
|
|
187
|
+
*
|
|
188
|
+
* @since 1.2.3
|
|
189
|
+
*/
|
|
190
|
+
PVXS_API
|
|
191
|
+
std::ostream& version_information(std::ostream&);
|
|
192
|
+
|
|
193
|
+
/** Thread-safe, bounded, multi-producer, multi-consumer FIFO queue.
|
|
194
|
+
*
|
|
195
|
+
* Queue value_type must be movable. If T is also copy constructable,
|
|
196
|
+
* then push(const T&) may be used.
|
|
197
|
+
*
|
|
198
|
+
* As an exception, the destructor is not re-entrant. Concurrent calls
|
|
199
|
+
* to methods during destruction will result in undefined behavior.
|
|
200
|
+
*
|
|
201
|
+
* @code
|
|
202
|
+
* MPMCFIFO<std::function<void()>> Q;
|
|
203
|
+
* ...
|
|
204
|
+
* while(auto work = Q.pop()) { // Q.push(nullptr) to break loop
|
|
205
|
+
* work();
|
|
206
|
+
* }
|
|
207
|
+
* @endcode
|
|
208
|
+
*
|
|
209
|
+
* @since 0.2.0
|
|
210
|
+
*/
|
|
211
|
+
template<typename T>
|
|
212
|
+
class MPMCFIFO {
|
|
213
|
+
mutable epicsMutex lock;
|
|
214
|
+
epicsEvent notifyW, notifyR;
|
|
215
|
+
std::deque<T> Q;
|
|
216
|
+
const size_t nlimit;
|
|
217
|
+
unsigned nwriters=0u, nreaders=0u;
|
|
218
|
+
|
|
219
|
+
typedef epicsGuard<epicsMutex> Guard;
|
|
220
|
+
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
|
221
|
+
public:
|
|
222
|
+
//! Template parameter
|
|
223
|
+
typedef T value_type;
|
|
224
|
+
|
|
225
|
+
//! Construct a new queue
|
|
226
|
+
//! @param limit If non-zero, then emplace()/push() will block while while
|
|
227
|
+
//! queue size is greater than or equal to this limit.
|
|
228
|
+
explicit MPMCFIFO(size_t limit=0u)
|
|
229
|
+
:nlimit(limit)
|
|
230
|
+
{}
|
|
231
|
+
//! Destructor is not re-entrant
|
|
232
|
+
~MPMCFIFO() {}
|
|
233
|
+
|
|
234
|
+
//! Poll number of elements in the work queue at this moment.
|
|
235
|
+
size_t size() const {
|
|
236
|
+
Guard G(lock);
|
|
237
|
+
return Q.size();
|
|
238
|
+
}
|
|
239
|
+
size_t max_size() const {
|
|
240
|
+
return nlimit ? nlimit : Q.max_size();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Construct a new element into the queue.
|
|
244
|
+
*
|
|
245
|
+
* Will block while full.
|
|
246
|
+
*/
|
|
247
|
+
template<typename ...Args>
|
|
248
|
+
void emplace(Args&&... args) {
|
|
249
|
+
bool wakeupR, wakeupW;
|
|
250
|
+
{
|
|
251
|
+
Guard G(lock);
|
|
252
|
+
// while full, wait for reader to consume an entry
|
|
253
|
+
while(nlimit && Q.size()>=nlimit) {
|
|
254
|
+
nwriters++;
|
|
255
|
+
{
|
|
256
|
+
UnGuard U(G);
|
|
257
|
+
notifyW.wait();
|
|
258
|
+
}
|
|
259
|
+
nwriters--;
|
|
260
|
+
}
|
|
261
|
+
// notify reader when queue becomes not empty
|
|
262
|
+
wakeupR = Q.empty() && nreaders;
|
|
263
|
+
Q.emplace_back(std::forward<Args>(args)...);
|
|
264
|
+
// wakeup next writer if there is still space
|
|
265
|
+
wakeupW = nwriters && Q.size()<nlimit;
|
|
266
|
+
}
|
|
267
|
+
if(wakeupR)
|
|
268
|
+
notifyR.signal();
|
|
269
|
+
if(wakeupW)
|
|
270
|
+
notifyW.signal();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
//! Move a new element to the queue
|
|
274
|
+
void push(T&& ent) {
|
|
275
|
+
// delegate to T::T(T&&)
|
|
276
|
+
emplace(std::move(ent));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
//! Copy a new element to the queue
|
|
280
|
+
void push(const T& ent) {
|
|
281
|
+
// delegate to T::T(const T&)
|
|
282
|
+
emplace(ent);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Remove an element from the queue.
|
|
286
|
+
*
|
|
287
|
+
* Blocks while queue is empty.
|
|
288
|
+
*/
|
|
289
|
+
T pop() {
|
|
290
|
+
bool wakeupW, wakeupR;
|
|
291
|
+
T ret;
|
|
292
|
+
{
|
|
293
|
+
Guard G(lock);
|
|
294
|
+
// wait for queue to become not empty
|
|
295
|
+
while(Q.empty()) {
|
|
296
|
+
nreaders++;
|
|
297
|
+
{
|
|
298
|
+
UnGuard U(G);
|
|
299
|
+
notifyR.wait();
|
|
300
|
+
}
|
|
301
|
+
nreaders--;
|
|
302
|
+
}
|
|
303
|
+
// wakeup a writer since the queue will have an empty entry
|
|
304
|
+
wakeupW = nwriters;
|
|
305
|
+
ret = std::move(Q.front());
|
|
306
|
+
Q.pop_front();
|
|
307
|
+
// wakeup next reader if entries remain
|
|
308
|
+
wakeupR = !Q.empty() && nreaders;
|
|
309
|
+
}
|
|
310
|
+
if(wakeupR)
|
|
311
|
+
notifyR.signal();
|
|
312
|
+
if(wakeupW)
|
|
313
|
+
notifyW.signal();
|
|
314
|
+
return ret;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
struct Timer;
|
|
319
|
+
|
|
320
|
+
#ifdef PVXS_EXPERT_API_ENABLED
|
|
321
|
+
|
|
322
|
+
//! Timer associated with a client::Context or server::Server
|
|
323
|
+
//! @since 0.2.0
|
|
324
|
+
struct PVXS_API Timer {
|
|
325
|
+
struct Pvt;
|
|
326
|
+
|
|
327
|
+
//! dtor implicitly cancel()s
|
|
328
|
+
~Timer();
|
|
329
|
+
//! Explicit cancel.
|
|
330
|
+
//! @returns true if the timer was running, and now is not.
|
|
331
|
+
bool cancel();
|
|
332
|
+
|
|
333
|
+
explicit operator bool() const { return pvt.operator bool(); }
|
|
334
|
+
|
|
335
|
+
private:
|
|
336
|
+
std::shared_ptr<Pvt> pvt;
|
|
337
|
+
friend struct Pvt;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
//! Allocate a pair of connected stream sockets
|
|
341
|
+
//! \since 1.1.4
|
|
342
|
+
PVXS_API
|
|
343
|
+
void compat_socketpair(SOCKET sock[2]);
|
|
344
|
+
|
|
345
|
+
//! Setup socket for non-blocking I/O
|
|
346
|
+
//! \since 1.1.4
|
|
347
|
+
PVXS_API
|
|
348
|
+
void compat_make_socket_nonblocking(SOCKET sock);
|
|
349
|
+
|
|
350
|
+
#endif // PVXS_EXPERT_API_ENABLED
|
|
351
|
+
|
|
352
|
+
} // namespace pvxs
|
|
353
|
+
|
|
354
|
+
#endif // PVXS_UTIL_H
|