Compare commits
23 Commits
284971fe8d
...
concurrent
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e5a5970a3 | |||
| 46d93c6c9f | |||
| 662a2fc464 | |||
| 194389ef6d | |||
| 3cfdda7365 | |||
| 3ec1ecfb5b | |||
| be51728570 | |||
| 41e54e5859 | |||
| 9cd108cf20 | |||
| 22208fbf51 | |||
| badaa01deb | |||
| e8a066abcd | |||
| 427e7411c1 | |||
| df457a1602 | |||
| 1dfcaf4952 | |||
| b468a6d581 | |||
| b68e8f7a65 | |||
| 1fb5356825 | |||
| 01f7b15818 | |||
| 8efd2cf447 | |||
| 101164902a | |||
| d3ffc19610 | |||
| 3f7f67e198 |
@@ -224,6 +224,7 @@ if (DEFINED LIBPROJECT)
|
||||
endif()
|
||||
|
||||
if (TESTS)
|
||||
set(PIP_ROOT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -665,7 +666,7 @@ if (PIP_TESTS_LIST)
|
||||
message(" * ${_test}")
|
||||
endforeach()
|
||||
else()
|
||||
message(" Tests: skip (tests off)")
|
||||
message(" Tests: skip (tests off)")
|
||||
endif()
|
||||
message("")
|
||||
message(" Utilites:")
|
||||
|
||||
@@ -23,3 +23,18 @@
|
||||
PICloudClient::PICloudClient() {
|
||||
}
|
||||
|
||||
|
||||
PICloudClient::~PICloudClient() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool PICloudClient::openDevice() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool PICloudClient::closeDevice() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,23 @@
|
||||
#include "pip_cloud_export.h"
|
||||
#include "piiodevice.h"
|
||||
|
||||
class PIEthernet;
|
||||
|
||||
class PIP_CLOUD_EXPORT PICloudClient {
|
||||
|
||||
class PIP_CLOUD_EXPORT PICloudClient : public PIIODevice
|
||||
{
|
||||
PIIODEVICE(PICloudClient)
|
||||
public:
|
||||
//!
|
||||
explicit PICloudClient();
|
||||
virtual ~PICloudClient();
|
||||
|
||||
protected:
|
||||
bool openDevice();
|
||||
bool closeDevice();
|
||||
|
||||
private:
|
||||
|
||||
PIEthernet * eth;
|
||||
};
|
||||
|
||||
#endif // PICCLOUDCLIENT_H
|
||||
|
||||
@@ -108,7 +108,7 @@ void PICodeParser::parseFile(const PIString & file, bool follow_includes) {
|
||||
piForeachC (Enum & c, enums) {
|
||||
piCout << PIStringAscii("enum") << c.name << c.meta;
|
||||
piForeachC (EnumeratorInfo & e, c.members)
|
||||
piCout << " " << e.name << "=" << e.value << e.meta;
|
||||
piCout << " " << e.name << '=' << e.value << e.meta;
|
||||
}
|
||||
piCout << "\n\nTypedefs:";
|
||||
piForeachC (Typedef & c, typedefs)
|
||||
@@ -119,7 +119,7 @@ void PICodeParser::parseFile(const PIString & file, bool follow_includes) {
|
||||
void PICodeParser::parseFiles(const PIStringList & files, bool follow_includes) {
|
||||
clear();
|
||||
piForeachC (PIString & f, files)
|
||||
parseFileInternal(f, follow_includes);
|
||||
parseFileInternal(f, follow_includes);
|
||||
/*piCout << "\n\nDefines:";
|
||||
piForeachC (Define & m, defines)
|
||||
piCout << PIStringAscii("define") << m.first << m.second;
|
||||
@@ -277,11 +277,11 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) {
|
||||
int nl = pfc.size_s();
|
||||
if (pl == nl) break;
|
||||
pl = nl;
|
||||
if (pfc.left(9) == "namespace") {
|
||||
if (pfc.left(9) == PIStringAscii("namespace")) {
|
||||
pfc.cutLeft(pfc.find('{') + 1);
|
||||
continue;
|
||||
}
|
||||
if (pfc.left(8) == "template") {
|
||||
if (pfc.left(8) == PIStringAscii("template")) {
|
||||
pfc.cutLeft(8);
|
||||
pfc.takeRange('<', '>');
|
||||
bool def = !isDeclaration(pfc, 0, &end);
|
||||
@@ -290,11 +290,11 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) {
|
||||
else pfc.takeSymbol();
|
||||
continue;
|
||||
}
|
||||
if (pfc.left(5) == PIStringAscii("class") || pfc.left(6) == "struct" || pfc.left(5) == "union") {
|
||||
if (pfc.left(5) == PIStringAscii("class") || pfc.left(6) == PIStringAscii("struct") || pfc.left(5) == PIStringAscii("union")) {
|
||||
int dind = pfc.find('{', 0), find = pfc.find(';', 0);
|
||||
if (dind < 0 && find < 0) {pfc.cutLeft(6); continue;}
|
||||
if (dind < 0 || find < dind) {pfc.cutLeft(6); continue;}
|
||||
ccmn = pfc.left(dind) + "{\n" + pfc.mid(dind).takeRange('{', '}') + "\n}\n";
|
||||
ccmn = pfc.left(dind) + PIStringAscii("{\n") + pfc.mid(dind).takeRange('{', '}') + PIStringAscii("\n}\n");
|
||||
pfc.remove(0, ccmn.size());
|
||||
parseClass(0, ccmn);
|
||||
continue;
|
||||
@@ -363,7 +363,7 @@ PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc)
|
||||
cur_def_vis = (is_class ? Private : Public);
|
||||
PIString cn = cd.mid(6).trim();
|
||||
bool has_name = !cn.isEmpty();
|
||||
if (cn.isEmpty()) cn = "<unnamed_" + PIString::fromNumber(anon_num++) + '>';
|
||||
if (cn.isEmpty()) cn = PIStringAscii("<unnamed_") + PIString::fromNumber(anon_num++) + '>';
|
||||
//piCout << "found " << typename_ << cn;
|
||||
if (cn.isEmpty()) return 0;
|
||||
Entity * e = new Entity();
|
||||
@@ -393,7 +393,7 @@ PIString PICodeParser::parseClass(Entity * parent, PIString & fc) {
|
||||
int ps = -1;
|
||||
bool def = false;
|
||||
PIString prev_namespace = cur_namespace, stmp;
|
||||
cur_namespace = ce->name + "::";
|
||||
cur_namespace = ce->name + PIStringAscii("::");
|
||||
//piCout << "parse class" << ce->name << "namespace" << cur_namespace;
|
||||
//piCout << "\nparse class" << ce->name << "namespace" << cur_namespace;
|
||||
while (!fc.isEmpty()) {
|
||||
@@ -425,7 +425,7 @@ PIString PICodeParser::parseClass(Entity * parent, PIString & fc) {
|
||||
}
|
||||
if (cw == PIStringAscii("friend")) {fc.cutLeft(fc.find(';') + 1); continue;}
|
||||
if (cw == PIStringAscii("typedef")) {ce->typedefs << parseTypedef(fc.takeLeft(fc.find(';'))); typedefs << ce->typedefs.back(); typedefs.back().first.insert(0, cur_namespace); if (ce->typedefs.back().first.isEmpty()) ce->typedefs.pop_back(); fc.takeSymbol(); continue;}
|
||||
if (cw == "template") {
|
||||
if (cw == PIStringAscii("template")) {
|
||||
fc.takeRange('<', '>');
|
||||
def = !isDeclaration(fc, 0, &end);
|
||||
fc.cutLeft(end);
|
||||
@@ -453,7 +453,7 @@ PICodeParser::MetaMap PICodeParser::parseMeta(PIString & fc) {
|
||||
if (fc.isEmpty()) return ret;
|
||||
PIStringList ml = fc.split(',');
|
||||
piForeachC (PIString & m, ml) {
|
||||
int i = m.find("=");
|
||||
int i = m.find('=');
|
||||
if (i < 0) continue;
|
||||
PIString mv = m.mid(i + 1).trim();
|
||||
if (mv.startsWith('\"')) mv.cutLeft(1);
|
||||
@@ -466,6 +466,8 @@ PICodeParser::MetaMap PICodeParser::parseMeta(PIString & fc) {
|
||||
|
||||
|
||||
bool PICodeParser::parseEnum(Entity * parent, const PIString & name, PIString fc, const MetaMap & meta) {
|
||||
static const PIString s_ss = PIStringAscii(" ");
|
||||
static const PIString s_M = PIStringAscii("$M");
|
||||
//piCout << PIStringAscii("enum") << name << fc;
|
||||
Enum e(name);
|
||||
e.meta = meta;
|
||||
@@ -474,12 +476,12 @@ bool PICodeParser::parseEnum(Entity * parent, const PIString & name, PIString fc
|
||||
int cv = -1, ind = 0;
|
||||
piForeach (PIString & v, vl) {
|
||||
MetaMap meta;
|
||||
int mi = v.find(PIStringAscii("$M"));
|
||||
int mi = v.find(s_M);
|
||||
if (mi >= 0) {
|
||||
meta = tmp_meta.value(v.takeMid(mi, 5));
|
||||
v.replaceAll(" ", ' ');
|
||||
v.replaceAll(s_ss, ' ');
|
||||
}
|
||||
vn = v; ind = v.find("=");
|
||||
vn = v; ind = v.find('=');
|
||||
if (ind > 0) {cv = v.right(v.size_s() - ind - 1).toInt(); vn = v.left(ind);}
|
||||
if (ind < 0) ++cv;
|
||||
e.members << EnumeratorInfo(vn.trim(), cv, meta);
|
||||
@@ -512,8 +514,27 @@ PICodeParser::Typedef PICodeParser::parseTypedef(PIString fc) {
|
||||
|
||||
|
||||
bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
static const PIString s_operator = PIStringAscii("operator");
|
||||
static const PIString s_ss = PIStringAscii(" ");
|
||||
static const PIString s_cs = PIStringAscii(", ");
|
||||
static const PIString s_sb = PIStringAscii(" (");
|
||||
static const PIString s_sM = PIStringAscii(" $M");
|
||||
static const PIString s_M = PIStringAscii("$M");
|
||||
static const PIString s_T = PIStringAscii("$T");
|
||||
static const PIString s_inline_s = PIStringAscii("inline ");
|
||||
static const PIString s_static_s = PIStringAscii("static ");
|
||||
static const PIString s_virtual_s = PIStringAscii("virtual ");
|
||||
static const PIString s_void = PIStringAscii("void");
|
||||
static const PIString s_using = PIStringAscii("using");
|
||||
static const PIString s_s5 = PIStringAscii(" ");
|
||||
static const PIString s_s_const_s = PIStringAscii(" const ");
|
||||
static const PIString s_s_static_s = PIStringAscii(" static ");
|
||||
static const PIString s_s_mutable_s = PIStringAscii(" mutable ");
|
||||
static const PIString s_s_volatile_s = PIStringAscii(" volatile ");
|
||||
static const PIString s_s_extern_s = PIStringAscii(" extern ");
|
||||
|
||||
if (fc.trim().isEmpty()) return true;
|
||||
if (fc.find("operator") >= 0) return true;
|
||||
if (fc.find(s_operator) >= 0) return true;
|
||||
tmp_temp.clear();
|
||||
//piCout << "parse member" << fc;
|
||||
int ts = fc.find('<'), te = 0;
|
||||
@@ -521,22 +542,22 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
while (ts >= 0) {
|
||||
ctemp = fc.mid(ts).takeRange('<', '>');
|
||||
if (ctemp.isEmpty()) {te = ts + 1; ts = fc.find('<', te); continue;}
|
||||
crepl = PIStringAscii("$T") + PIString::fromNumber(tmp_temp.size_s()).expandLeftTo(3, '0');
|
||||
crepl = s_T + PIString::fromNumber(tmp_temp.size_s()).expandLeftTo(3, '0');
|
||||
fc.replace(ts, ctemp.size_s() + 2, crepl);
|
||||
tmp_temp[crepl] = '<' + ctemp + '>';
|
||||
ts = fc.find('<', te);
|
||||
}
|
||||
fc.replaceAll('\n', ' ').replaceAll('\t', ' ').replaceAll(" ", ' ').replaceAll(", ", ',').replaceAll(PIStringAscii(" ("), '(').replaceAll(PIStringAscii(" $M"), PIStringAscii("$M"));
|
||||
fc.replaceAll('\n', ' ').replaceAll('\t', ' ').replaceAll(s_ss, ' ').replaceAll(s_cs, ',').replaceAll(s_sb, '(').replaceAll(s_sM, s_M);
|
||||
//piCout << "parse member" << fc;
|
||||
PIStringList tl, al;
|
||||
Member me;
|
||||
//piCout << fc;
|
||||
if (fc.contains('(')) {
|
||||
MetaMap meta;
|
||||
int ind = fc.find(PIStringAscii("$M"));
|
||||
int ind = fc.find(s_M);
|
||||
if (ind >= 0) {
|
||||
meta = tmp_meta.value(fc.takeMid(ind, 5));
|
||||
fc.replaceAll(PIStringAscii(" "), ' ').replaceAll(PIStringAscii(" ("), '(');
|
||||
fc.replaceAll(s_ss, ' ').replaceAll(s_sb, '(');
|
||||
}
|
||||
fc.cutRight(fc.size_s() - fc.findLast(')') - 1);
|
||||
te = fc.find('(');
|
||||
@@ -550,26 +571,26 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
me.arguments_full = fc.takeMid(ts + 2).cutRight(1).split(',');
|
||||
me.type = fc.cutRight(1).trim();
|
||||
me.visibility = cur_def_vis;
|
||||
if (me.type.find(PIStringAscii("inline ")) >= 0) {
|
||||
if (me.type.find(s_inline_s) >= 0) {
|
||||
me.attributes |= Inline;
|
||||
me.type.removeAll(PIStringAscii("inline "));
|
||||
me.type.removeAll(s_inline_s);
|
||||
}
|
||||
if (me.type.find(PIStringAscii("static ")) >= 0) {
|
||||
if (me.type.find(s_static_s) >= 0) {
|
||||
me.attributes |= Static;
|
||||
me.type.removeAll(PIStringAscii("static "));
|
||||
me.type.removeAll(s_static_s);
|
||||
}
|
||||
if (me.type.find(PIStringAscii("virtual ")) >= 0) {
|
||||
if (me.type.find(s_virtual_s) >= 0) {
|
||||
me.attributes |= Virtual;
|
||||
me.type.removeAll(PIStringAscii("virtual "));
|
||||
me.type.removeAll(s_virtual_s);
|
||||
}
|
||||
normalizeEntityNamespace(me.type);
|
||||
int i = 0;
|
||||
//piCout << me.arguments_full;
|
||||
piForeach (PIString & a, me.arguments_full)
|
||||
if ((i = a.find("=")) > 0)
|
||||
if ((i = a.find('=')) > 0)
|
||||
a.cutRight(a.size_s() - i).trim();
|
||||
for (int j = 0; j < me.arguments_full.size_s(); ++j)
|
||||
if (me.arguments_full[j] == PIStringAscii("void")) {
|
||||
if (me.arguments_full[j] == s_void) {
|
||||
me.arguments_full.remove(j);
|
||||
--j;
|
||||
}
|
||||
@@ -590,7 +611,7 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
parent->functions << me;
|
||||
} else {
|
||||
if (fc.endsWith(';')) fc.cutRight(1);
|
||||
if (fc.startsWith(PIStringAscii("using")) || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true;
|
||||
if (fc.startsWith(s_using) || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true;
|
||||
int bits = extractMemberBits(fc);
|
||||
tl = fc.split(',');
|
||||
//piCout << "member" << fc << tl;
|
||||
@@ -599,8 +620,8 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
bool vn = true;
|
||||
ctemp = tl.front().trim();
|
||||
PIString meta_t;
|
||||
if (ctemp.contains(PIStringAscii("$M")))
|
||||
meta_t = ctemp.takeMid(ctemp.find(PIStringAscii("$M")));
|
||||
if (ctemp.contains(s_M))
|
||||
meta_t = ctemp.takeMid(ctemp.find(s_M));
|
||||
for (ts = ctemp.size_s() - 1; ts > 0; --ts) {
|
||||
if (vn) {if (!_isCChar(ctemp[ts]) && !ctemp[ts].isDigit() && ctemp[ts] != '[' && ctemp[ts] != ']') vn = false;}
|
||||
else {if (_isCChar(ctemp[ts]) || ctemp[ts].isDigit()) break;}
|
||||
@@ -609,26 +630,26 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
|
||||
me.visibility = cur_def_vis;
|
||||
ctemp += meta_t;
|
||||
restoreTmpTemp(&me);
|
||||
PIString type = PIStringAscii(" ") + me.type;
|
||||
if (type.find(PIStringAscii(" const ")) >= 0) {
|
||||
PIString type = s_s5 + me.type;
|
||||
if (type.find(s_s_const_s) >= 0) {
|
||||
me.attributes |= Const;
|
||||
type.replaceAll(PIStringAscii(" const "), ' ');
|
||||
type.replaceAll(s_s_const_s, ' ');
|
||||
}
|
||||
if (type.find(PIStringAscii(" static ")) >= 0) {
|
||||
if (type.find(s_s_static_s) >= 0) {
|
||||
me.attributes |= Static;
|
||||
type.replaceAll(PIStringAscii(" static "), ' ');
|
||||
type.replaceAll(s_s_static_s, ' ');
|
||||
}
|
||||
if (type.find(PIStringAscii(" mutable ")) >= 0) {
|
||||
if (type.find(s_s_mutable_s) >= 0) {
|
||||
me.attributes |= Mutable;
|
||||
type.replaceAll(PIStringAscii(" mutable "), ' ');
|
||||
type.replaceAll(s_s_mutable_s, ' ');
|
||||
}
|
||||
if (type.find(PIStringAscii(" volatile ")) >= 0) {
|
||||
if (type.find(s_s_volatile_s) >= 0) {
|
||||
me.attributes |= Volatile;
|
||||
type.replaceAll(PIStringAscii(" volatile "), ' ');
|
||||
type.replaceAll(s_s_volatile_s, ' ');
|
||||
}
|
||||
if (type.find(PIStringAscii(" extern ")) >= 0) {
|
||||
if (type.find(s_s_extern_s) >= 0) {
|
||||
me.attributes |= Extern;
|
||||
type.replaceAll(PIStringAscii(" extern "), ' ');
|
||||
type.replaceAll(s_s_extern_s, ' ');
|
||||
}
|
||||
type.trim();
|
||||
normalizeEntityNamespace(type);
|
||||
@@ -677,6 +698,14 @@ int PICodeParser::extractMemberBits(PIString & fc) {
|
||||
|
||||
|
||||
void PICodeParser::normalizeEntityNamespace(PIString & n) {
|
||||
static const PIString s_const_s = PIStringAscii("const ");
|
||||
static const PIString s_static_s = PIStringAscii("static ");
|
||||
static const PIString s_mutable_s = PIStringAscii("mutable ");
|
||||
static const PIString s_volatile_s = PIStringAscii("volatile ");
|
||||
static const PIString s_s_const_s = PIStringAscii(" const ");
|
||||
static const PIString s_s_static_s = PIStringAscii(" static ");
|
||||
static const PIString s_s_mutable_s = PIStringAscii(" mutable ");
|
||||
static const PIString s_s_volatile_s = PIStringAscii(" volatile ");
|
||||
PIString suff, pref;
|
||||
for (int i = n.size_s() - 1; i > 0; --i)
|
||||
if (_isCChar(n[i]) || n[i].isDigit()) {
|
||||
@@ -685,10 +714,10 @@ void PICodeParser::normalizeEntityNamespace(PIString & n) {
|
||||
break;
|
||||
}
|
||||
n.push_front(' ');
|
||||
if (n.find(PIStringAscii(" static ")) >= 0) {n.replaceAll(PIStringAscii(" static "), ""); pref += PIStringAscii("static ");}
|
||||
if (n.find(PIStringAscii(" const ")) >= 0) {n.replaceAll(PIStringAscii(" const "), ""); pref += PIStringAscii("const ");}
|
||||
if (n.find(PIStringAscii(" mutable ")) >= 0) {n.replaceAll(PIStringAscii(" mutable "), ""); pref += PIStringAscii("mutable ");}
|
||||
if (n.find(PIStringAscii(" volatile ")) >= 0) {n.replaceAll(PIStringAscii(" volatile "), ""); pref += PIStringAscii("volatile ");}
|
||||
if (n.find(s_s_const_s) >= 0) {n.replaceAll(s_s_const_s, ""); pref += s_const_s;}
|
||||
if (n.find(s_s_static_s) >= 0) {n.replaceAll(s_s_static_s, ""); pref += s_static_s;}
|
||||
if (n.find(s_s_mutable_s) >= 0) {n.replaceAll(s_s_mutable_s, ""); pref += s_mutable_s;}
|
||||
if (n.find(s_s_volatile_s) >= 0) {n.replaceAll(s_s_volatile_s, ""); pref += s_volatile_s;}
|
||||
n.trim();
|
||||
int f = 0;
|
||||
piForeachC (Entity * e, entities) {
|
||||
@@ -697,27 +726,29 @@ void PICodeParser::normalizeEntityNamespace(PIString & n) {
|
||||
return;
|
||||
}
|
||||
if ((f = e->name.find(n)) >= 0)
|
||||
if (e->name.mid(f - 1, 1) == PIChar(':'))
|
||||
if (e->name.at(f - 1) == PIChar(':'))
|
||||
if (e->name.find(cur_namespace) >= 0) {
|
||||
n = pref + e->name + suff;
|
||||
return;
|
||||
}
|
||||
}
|
||||
piForeachC (Enum & e, enums)
|
||||
if ((f = e.name.find(n)) >= 0)
|
||||
if (e.name.mid(f - 1, 1) == PIChar(':'))
|
||||
if (e.name.find(cur_namespace) >= 0) {
|
||||
//piCout << "change" << n << "to" << e.name + suff;
|
||||
n = pref + e.name + suff;
|
||||
return;
|
||||
piForeachC (Enum & e, enums) {
|
||||
if ((f = e.name.find(n)) >= 0)
|
||||
if (e.name.at(f - 1) == PIChar(':'))
|
||||
if (e.name.find(cur_namespace) >= 0) {
|
||||
//piCout << "change" << n << "to" << e.name + suff;
|
||||
n = pref + e.name + suff;
|
||||
return;
|
||||
}
|
||||
}
|
||||
piForeachC (Typedef & e, typedefs)
|
||||
if ((f = e.first.find(n)) >= 0)
|
||||
if (e.first.mid(f - 1, 1) == PIChar(':'))
|
||||
if (e.first.find(cur_namespace) >= 0) {
|
||||
//piCout << "change" << n << "to" << e.name + suff;
|
||||
n = pref + e.first + suff;
|
||||
return;
|
||||
piForeachC (Typedef & e, typedefs) {
|
||||
if ((f = e.first.find(n)) >= 0)
|
||||
if (e.first.at(f - 1) == PIChar(':'))
|
||||
if (e.first.find(cur_namespace) >= 0) {
|
||||
//piCout << "change" << n << "to" << e.name + suff;
|
||||
n = pref + e.first + suff;
|
||||
return;
|
||||
}
|
||||
}
|
||||
n = (pref + n + suff).trim();
|
||||
}
|
||||
@@ -864,9 +895,9 @@ bool PICodeParser::isDeclaration(const PIString & fc, int start, int * end) {
|
||||
bool PICodeParser::isMainFile(const PIString & fc) {
|
||||
int si = 0;
|
||||
while (si >= 0) {
|
||||
int csi = fc.find(" main", si);
|
||||
if (csi < 0) csi = fc.find("\tmain", si);
|
||||
if (csi < 0) csi = fc.find("\nmain", si);
|
||||
int csi = fc.find(PIStringAscii(" main"), si);
|
||||
if (csi < 0) csi = fc.find(PIStringAscii("\tmain"), si);
|
||||
if (csi < 0) csi = fc.find(PIStringAscii("\nmain"), si);
|
||||
if (csi < 0) return false;
|
||||
si = csi;
|
||||
int fi = fc.find('(', si + 5);
|
||||
@@ -874,7 +905,7 @@ bool PICodeParser::isMainFile(const PIString & fc) {
|
||||
if (fi - si < 10) {
|
||||
PIString ms(fc.mid(si, fi - si + 1));
|
||||
ms.removeAll(' ').removeAll('\t').removeAll('\n');
|
||||
if (ms == "main(") return true;
|
||||
if (ms == PIStringAscii("main(")) return true;
|
||||
}
|
||||
si += 5;
|
||||
}
|
||||
@@ -925,7 +956,7 @@ PIString PICodeParser::procMacros(PIString fc) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (mif.left(4) == "else" && ifcnt == 0) {
|
||||
if (mif.left(4) == PIStringAscii("else") && ifcnt == 0) {
|
||||
//piCout << "main else" << skip << grab;
|
||||
if (grab) pfc << procMacros(nfc);
|
||||
if (skip && !cond_ok) {skip = false; grab = true;}
|
||||
@@ -959,7 +990,7 @@ bool PICodeParser::parseDirective(PIString d) {
|
||||
if (d.isEmpty()) return true;
|
||||
PIString dname = d.takeCWord();
|
||||
//piCout << "parseDirective" << d;
|
||||
if (dname == "include") {
|
||||
if (dname == PIStringAscii("include")) {
|
||||
d.replaceAll('<', '\"').replaceAll('>', '\"');
|
||||
PIString cf = cur_file, ifc = d.takeRange('\"', '\"');
|
||||
if (with_includes) {
|
||||
@@ -982,7 +1013,7 @@ bool PICodeParser::parseDirective(PIString d) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (dname == "undef") {
|
||||
if (dname == PIStringAscii("undef")) {
|
||||
PIString mname = d.takeCWord();
|
||||
for (int i = 0; i < defines.size_s(); ++i)
|
||||
if (defines[i].first == mname) {defines.remove(i); --i;}
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
#ifndef PICOMPRESS_H
|
||||
#define PICOMPRESS_H
|
||||
|
||||
#include "pip_compress_export.h"
|
||||
#include "pibytearray.h"
|
||||
|
||||
PIP_EXPORT PIByteArray piCompress(const PIByteArray & ba, int level = 6);
|
||||
PIP_COMPRESS_EXPORT PIByteArray piCompress(const PIByteArray & ba, int level = 6);
|
||||
|
||||
PIP_EXPORT PIByteArray piDecompress(const PIByteArray & zba);
|
||||
PIP_COMPRESS_EXPORT PIByteArray piDecompress(const PIByteArray & zba);
|
||||
|
||||
#endif // PICOMPRESS_H
|
||||
@@ -28,147 +28,347 @@
|
||||
* \fn PIVector::PIVector();
|
||||
* Contructs an empty vector
|
||||
|
||||
* \fn PIVector::PIVector(ullong size, const Type & value = Type());
|
||||
* \fn PIVector::PIVector(size_t size, const T & value = T());
|
||||
* \brief Contructs vector with size "size" filled elements "value"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::PIVector
|
||||
|
||||
* \fn const Type & PIVector::at(ullong index) const;
|
||||
* \fn const T & PIVector::at(size_t index) const;
|
||||
* \brief Read-only access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::at_c
|
||||
* \sa \a operator[]
|
||||
|
||||
* \fn Type & PIVector::at(ullong index);
|
||||
* \fn T & PIVector::at(size_t index);
|
||||
* \brief Full access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::at
|
||||
* \sa \a operator[]
|
||||
|
||||
* \fn const Type * PIVector::data(ullong index = 0) const;
|
||||
* \fn const T * PIVector::data(size_t index = 0) const;
|
||||
* \brief Read-only pointer to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::data_c
|
||||
|
||||
* \fn Type * PIVector::data(ullong index = 0);
|
||||
* \fn T * PIVector::data(size_t index = 0);
|
||||
* \brief Pointer to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::data
|
||||
|
||||
* \fn ullong PIVector::size() const;
|
||||
* \fn size_t PIVector::size() const;
|
||||
* \brief Elements count
|
||||
|
||||
* \fn int PIVector::size_s() const;
|
||||
* \fn ssize_t PIVector::size_s() const;
|
||||
* \brief Elements count
|
||||
|
||||
* \fn bool PIVector::isEmpty() const;
|
||||
* \brief Return \c "true" if vector is empty, i.e. size = 0
|
||||
|
||||
* \fn bool PIVector::has(const Type & t) const;
|
||||
* \fn bool PIVector::has(const T & t) const;
|
||||
|
||||
* \fn bool PIVector::contains(const Type & v) const;
|
||||
* \fn bool PIVector::contains(const T & v) const;
|
||||
* \brief Return \c "true" if vector has at least one element equal "t"
|
||||
|
||||
* \fn int PIVector::etries(const Type & t) const;
|
||||
* \fn int PIVector::etries(const T & t) const;
|
||||
* \brief Return how many times element "t" appears in vector
|
||||
|
||||
* \fn static int PIVector::compare_func(const Type * t0, const Type * t1);
|
||||
* \brief Standard compare function for type "Type". Return 0 if t0 = t1, -1 if t0 < t1 and 1 if t0 > t1.
|
||||
* \fn ssize_t PIVector::indexOf(const T & t) const;
|
||||
* \brief Return index of first element equal "t" or -1 if there is no such element
|
||||
|
||||
* \fn void PIVector::resize(ullong size, const Type & new_type = Type());
|
||||
* \fn ssize_t PIVector::lastIndexOf(const T & t) const;
|
||||
* \brief Return index of last element equal "t" or -1 if there is no such element
|
||||
|
||||
* \fn static int PIVector::compare_func(const T * t0, const T * t1);
|
||||
* \brief Standard compare function for type "T". Return 0 if t0 = t1, -1 if t0 < t1 and 1 if t0 > t1.
|
||||
|
||||
* \fn void PIVector::resize(size_t size, const T & new_type = T());
|
||||
* \brief Resize vector to size "size"
|
||||
* \details Elements removed from end of vector if new size < old size, or added new elements = "new_type" if new size > old size.\n
|
||||
* Example: \snippet picontainers.cpp PIVector::resize
|
||||
* \sa \a size(), \a clear()
|
||||
|
||||
* \fn PIVector<T> & PIVector::enlarge(ullong size);
|
||||
* \fn PIVector & PIVector::enlarge(size_t size);
|
||||
* \brief Increase vector size with "size" elements
|
||||
|
||||
* \fn void PIVector::clear();
|
||||
* \brief Clear vector. Equivalent to call <tt>"resize(0)"</tt>
|
||||
|
||||
* \fn PIVector<T> & PIVector::sort(CompareFunc compare = compare_func);
|
||||
* \fn PIVector & PIVector::sort(CompareFunc compare = compare_func);
|
||||
* \brief Sort vector using quick sort algorithm and standard compare function
|
||||
* \details Example: \snippet picontainers.cpp PIVector::sort_0
|
||||
* With custom compare function: \snippet picontainers.cpp PIVector::sort_1
|
||||
|
||||
* \fn PIVector<T> & PIVector::fill(const Type & t);
|
||||
* \fn PIVector & PIVector::fill(const T & t);
|
||||
* \brief Fill vector with elements "t" leave size is unchanged and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::fill
|
||||
|
||||
* \fn Type & PIVector::back();
|
||||
* \fn PIVector & PIVector::assign(const T & t = T());
|
||||
* \brief Synonym of \a fill(t)
|
||||
|
||||
* \fn PIVector & PIVector::assign(size_t new_size, const T & t);
|
||||
* \brief Resize to "new_size", then fill with "t"
|
||||
|
||||
* \fn T & PIVector::back();
|
||||
* \brief Last element of the vector
|
||||
|
||||
* \fn const Type & PIVector::back() const;
|
||||
* \fn const T & PIVector::back() const;
|
||||
* \brief Last element of the vector
|
||||
|
||||
* \fn Type & PIVector::front();
|
||||
* \fn T & PIVector::front();
|
||||
* \brief First element of the vector
|
||||
|
||||
* \fn const Type & PIVector::front() const;
|
||||
* \fn const T & PIVector::front() const;
|
||||
* \brief First element of the vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::push_back(const Type & t);
|
||||
* \fn PIVector & PIVector::push_back(const T & t);
|
||||
* \brief Add new element "t" at the end of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::push_front(const Type & t);
|
||||
* \fn PIVector & PIVector::push_front(const T & t);
|
||||
* \brief Add new element "t" at the beginning of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::pop_back();
|
||||
* \fn PIVector & PIVector::pop_back();
|
||||
* \brief Remove one element from the end of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::pop_front();
|
||||
* \fn PIVector & PIVector::pop_front();
|
||||
* \brief Remove one element from the beginning of vector and return reference to vector
|
||||
|
||||
* \fn Type PIVector::take_back();
|
||||
* \fn T PIVector::take_back();
|
||||
* \brief Remove one element from the end of vector and return it
|
||||
|
||||
* \fn Type PIVector::take_front();
|
||||
* \fn T PIVector::take_front();
|
||||
* \brief Remove one element from the beginning of vector and return it
|
||||
|
||||
* \fn PIVector<T> & PIVector::remove(uint index);
|
||||
* \fn PIVector & PIVector::remove(size_t index);
|
||||
* \brief Remove one element by index "index" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::remove_0
|
||||
* \sa \a removeOne(), \a removeAll()
|
||||
|
||||
* \fn PIVector<T> & PIVector::remove(uint index, uint count);
|
||||
* \fn PIVector & PIVector::remove(size_t index, size_t count);
|
||||
* \brief Remove "count" elements by first index "index" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::remove_1
|
||||
* \sa \a removeOne(), \a removeAll()
|
||||
|
||||
* \fn PIVector<T> & PIVector::removeOne(const Type & v);
|
||||
* \fn PIVector & PIVector::removeOne(const T & v);
|
||||
* \brief Remove no more than one element equal "v" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::removeOne
|
||||
* \sa \a remove(), \a removeAll()
|
||||
|
||||
* \fn PIVector<T> & PIVector::removeAll(const Type & v);
|
||||
* \fn PIVector & PIVector::removeAll(const T & v);
|
||||
* \brief Remove all elements equal "v" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::removeAll
|
||||
* \sa \a remove(), \a removeOne()
|
||||
|
||||
* \fn PIVector<T> & PIVector::insert(uint pos, const Type & t);
|
||||
* \fn PIVector & PIVector::insert(size_t pos, const T & t);
|
||||
* \brief Insert element "t" after index "pos" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::insert_0
|
||||
|
||||
* \fn PIVector<T> & PIVector::insert(uint pos, const PIVector<T> & t);
|
||||
* \fn PIVector & PIVector::insert(size_t pos, const PIVector & t);
|
||||
* \brief Insert other vector "t" after index "pos" and return reference to vector
|
||||
* \details Example: \snippet picontainers.cpp PIVector::insert_1
|
||||
|
||||
* \fn Type & PIVector::operator [](uint index);
|
||||
* \fn T & PIVector::operator [](size_t index);
|
||||
* \brief Full access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::()
|
||||
* \sa \a at()
|
||||
|
||||
* \fn const Type & PIVector::operator [](uint index) const;
|
||||
* \fn const T & PIVector::operator [](size_t index) const;
|
||||
* \brief Read-only access to element by index "index"
|
||||
* \details Example: \snippet picontainers.cpp PIVector::()_c
|
||||
* \sa \a at()
|
||||
|
||||
* \fn PIVector<T> & PIVector::operator <<(const Type & t);
|
||||
* \fn PIVector & PIVector::operator <<(const T & t);
|
||||
* \brief Add new element "t" at the end of vector and return reference to vector
|
||||
|
||||
* \fn PIVector<T> & PIVector::operator <<(const PIVector<T> & t);
|
||||
* \fn PIVector & PIVector::operator <<(const PIVector & t);
|
||||
* \brief Add vector "t" at the end of vector and return reference to vector
|
||||
|
||||
* \fn bool PIVector::operator ==(const PIVector<T> & t);
|
||||
* \fn bool PIVector::operator ==(const PIVector & t);
|
||||
* \brief Compare with vector "t"
|
||||
|
||||
* \fn bool PIVector::operator !=(const PIVector<T> & t);
|
||||
* \fn bool PIVector::operator !=(const PIVector & t);
|
||||
* \brief Compare with vector "t"
|
||||
|
||||
* */
|
||||
|
||||
|
||||
|
||||
|
||||
/** \class PIMap
|
||||
* \brief Associative array
|
||||
* \details This class used to store Key = Value array of any
|
||||
* type of data. \a value() returns value for key and leave map
|
||||
* unchaged in any case. \a operator [] create entry in map if
|
||||
* there is no entry for given key. You can retrieve all
|
||||
* keys by method \a keys() and all values by methos \a values().
|
||||
* To iterate all entries use class PIMapIterator, or methods
|
||||
* \a makeIterator() and \a makeReverseIterator().
|
||||
|
||||
* \fn PIMap::PIMap();
|
||||
* \brief Contructs an empty map
|
||||
|
||||
* \fn PIMap::PIMap(const PIMap & other);
|
||||
* \brief Contructs a copy of "other"
|
||||
|
||||
* \fn PIMap & PIMap::operator =(const PIMap & other);
|
||||
* \brief Copy operator
|
||||
|
||||
* \fn PIMap::PIMap(const PIMap & other);
|
||||
* \brief Contructs a copy of "other"
|
||||
|
||||
* \fn PIMapIterator PIMap::makeIterator() const
|
||||
* \brief Returns PIMapIterator for this map
|
||||
|
||||
* \fn PIMapIterator PIMap::makeReverseIterator() const
|
||||
* \brief Returns reverse PIMapIterator for this map
|
||||
|
||||
|
||||
* \fn size_t PIMap::size() const
|
||||
* \brief Returns entries count
|
||||
|
||||
* \fn int PIMap::size_s() const
|
||||
* \brief Returns entries count
|
||||
|
||||
* \fn size_t PIMap::length() const
|
||||
* \brief Returns entries count
|
||||
|
||||
* \fn bool PIMap::isEmpty() const
|
||||
* \brief Returns if map is empty
|
||||
|
||||
|
||||
* \fn T & PIMap::operator [](const Key & key)
|
||||
* \brief Returns value for key "key". If there is no key in map, create one.
|
||||
|
||||
* \fn const T PIMap::operator [](const Key & key) const
|
||||
* \brief Returns value for key "key". If there is no key in map, returns default T().
|
||||
|
||||
* \fn T & PIMap::at(const Key & key)
|
||||
* \brief Equivalent to operator []
|
||||
|
||||
* \fn const T PIMap::at(const Key & key) const
|
||||
* \brief Equivalent to operator []
|
||||
|
||||
|
||||
* \fn PIMap & PIMap::operator <<(const PIMap & other)
|
||||
* \brief Insert all etries of "other" to this map. Override existing values.
|
||||
|
||||
* \fn bool PIMap::operator ==(const PIMap & t) const
|
||||
* \brief Compare operator
|
||||
|
||||
* \fn bool PIMap::operator !=(const PIMap & t) const
|
||||
* \brief Compare operator
|
||||
|
||||
* \fn bool PIMap::contains(const Key & key) const
|
||||
* \brief Returns "true" if map contains entry with key "key"
|
||||
|
||||
|
||||
* \fn PIMap & PIMap::reserve(size_t new_size)
|
||||
* \brief Reserve space for "new_size" entries
|
||||
|
||||
* \fn PIMap & PIMap::removeOne(const Key & key)
|
||||
* \brief Remove entry with key "key"
|
||||
|
||||
* \fn PIMap & PIMap::remove(const Key & key)
|
||||
* \brief Equivalent \a removeOne(key)
|
||||
|
||||
* \fn PIMap & PIMap::erase(const Key & key)
|
||||
* \brief Equivalent \a removeOne(key)
|
||||
|
||||
* \fn PIMap & PIMap::clear()
|
||||
* \brief Clear map
|
||||
|
||||
|
||||
* \fn void PIMap::swap(PIMap & other)
|
||||
* \brief Swap map with "other"
|
||||
|
||||
|
||||
* \fn PIMap & PIMap::insert(const Key & key, const T & value)
|
||||
* \brief Insert or rewrite entry with key "key" and value "value"
|
||||
|
||||
* \fn const T PIMap::value(const Key & key, const T & default = T())
|
||||
* \brief Returns value for key "key". If there is no key in map, returns "default".
|
||||
|
||||
* \fn PIVector<T> PIMap::values() const
|
||||
* \brief Returns all values as PIVector
|
||||
|
||||
* \fn Key PIMap::key(const T & value, const Key & default = Key()) const
|
||||
* \brief Returns key for first founded value "value". If there is no such value in map, returns "default".
|
||||
|
||||
* \fn PIVector<Key> PIMap::keys() const
|
||||
* \brief Returns all keys as PIVector
|
||||
|
||||
* */
|
||||
|
||||
|
||||
|
||||
|
||||
/** \class PIMapIterator
|
||||
* \brief Helper class to iterate over PIMap
|
||||
* \details This class used to access keys and values in PIMap.
|
||||
* You can use constructor to create iterator, or use \a PIMap::makeIterator()
|
||||
* and \a PIMap::makeReverseIterator() methods.
|
||||
*
|
||||
* First usage variant:
|
||||
* \code
|
||||
* PIMap<int, PIString> m;
|
||||
* m[1] = "one";
|
||||
* m[2] = "two";
|
||||
* m[4] = "four";
|
||||
*
|
||||
* auto it = m.makeIterator();
|
||||
* while (it.next()) {
|
||||
* piCout << it.key() << it.value();
|
||||
* }
|
||||
* // 1 one
|
||||
* // 2 two
|
||||
* // 4 four
|
||||
* \endcode
|
||||
*
|
||||
* Using hasNext():
|
||||
* \code
|
||||
* while (it.hasNext()) {
|
||||
* it.next();
|
||||
* \endcode
|
||||
*
|
||||
* Using constructor:
|
||||
* \code
|
||||
* PIMapIterator<int, PIString> it(m);
|
||||
* \endcode
|
||||
*
|
||||
* Write access:
|
||||
* \code
|
||||
* while (it.next()) {
|
||||
* it.valueRef().append("_!");
|
||||
* piCout << it.key() << it.value();
|
||||
* }
|
||||
*
|
||||
* // 1 one_!
|
||||
* // 2 two_!
|
||||
* // 4 four_!
|
||||
* \endcode
|
||||
*
|
||||
* Reverse iterator:
|
||||
* \code
|
||||
* auto it = m.makeReverseIterator();
|
||||
* while (it.next()) {
|
||||
* piCout << it.key() << it.value();
|
||||
* }
|
||||
*
|
||||
* // 4 four
|
||||
* // 2 two
|
||||
* // 1 one
|
||||
* \endcode
|
||||
|
||||
* \fn PIMapIterator(const PIMap & map, bool reverse = false)
|
||||
* \brief Contructs iterator for "map". Current position is invalid.
|
||||
|
||||
* \fn const Key & PIMapIterator::key() const
|
||||
* \brief Returns current entry key
|
||||
|
||||
* \fn const T & PIMapIterator::value() const
|
||||
* \brief Returns current entry value
|
||||
|
||||
* \fn T & PIMapIterator::valueRef() const
|
||||
* \brief Returns reference to current entry value
|
||||
|
||||
* \fn bool PIMapIterator::hasNext()
|
||||
* \brief Returns if iterator can jump to next entry
|
||||
|
||||
* \fn bool PIMapIterator::next()
|
||||
* \brief Jump to next entry and return if new position is valid.
|
||||
|
||||
* */
|
||||
|
||||
@@ -67,10 +67,16 @@ void piQuickSort(T * a, ssize_t N) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename Key, typename T>
|
||||
class PIMapIterator;
|
||||
|
||||
|
||||
template <typename Key, typename T>
|
||||
class PIMap {
|
||||
template <typename Key1, typename T1> friend PIByteArray & operator >>(PIByteArray & s, PIMap<Key1, T1> & v);
|
||||
template <typename Key1, typename T1> friend PIByteArray & operator <<(PIByteArray & s, const PIMap<Key1, T1> & v);
|
||||
template <typename Key1, typename T1> friend class PIMapIterator;
|
||||
public:
|
||||
PIMap() {;}
|
||||
PIMap(const PIMap<Key, T> & other) {*this = other;}
|
||||
@@ -183,6 +189,9 @@ public:
|
||||
const_reverse_iterator constRbegin() const {return const_reverse_iterator(this, size() - 1);}
|
||||
const_reverse_iterator constRend() const {return const_reverse_iterator(this, -1);}
|
||||
|
||||
PIMapIterator<Key, T> makeIterator() const {return PIMapIterator<Key, T>(*this);}
|
||||
PIMapIterator<Key, T> makeReverseIterator() const {return PIMapIterator<Key, T>(*this, true);}
|
||||
|
||||
size_t size() const {return pim_content.size();}
|
||||
int size_s() const {return pim_content.size_s();}
|
||||
size_t length() const {return pim_content.size();}
|
||||
@@ -327,6 +336,41 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
template <typename Key, typename T>
|
||||
class PIMapIterator {
|
||||
typedef PIMap<Key, T> MapType;
|
||||
public:
|
||||
PIMapIterator(const PIMap<Key, T> & map, bool reverse = false): m(map), pos(-1), rev(reverse) {
|
||||
if (rev) pos = m.size_s();
|
||||
}
|
||||
const Key & key() const {return const_cast<MapType & >(m)._key(pos);}
|
||||
const T & value() const {return const_cast<MapType & >(m)._value(pos);}
|
||||
T & valueRef() const {return const_cast<MapType & >(m)._value(pos);}
|
||||
inline bool hasNext() const {
|
||||
if (rev) {
|
||||
return pos > 0;
|
||||
} else {
|
||||
return pos < (m.size_s() - 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool next() {
|
||||
if (rev) {
|
||||
--pos;
|
||||
return pos >= 0;
|
||||
} else {
|
||||
++pos;
|
||||
return pos < m.size_s();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
const MapType & m;
|
||||
ssize_t pos;
|
||||
bool rev;
|
||||
};
|
||||
|
||||
|
||||
#ifdef PIP_STD_IOSTREAM
|
||||
template<typename Key, typename Type>
|
||||
inline std::ostream & operator <<(std::ostream & s, const PIMap<Key, Type> & v) {
|
||||
|
||||
@@ -54,12 +54,18 @@ public:
|
||||
int id;
|
||||
T data;
|
||||
};
|
||||
template <typename T>
|
||||
struct ChunkConst {
|
||||
ChunkConst(int i, const T & d): id(i), data(d) {}
|
||||
int id;
|
||||
const T & data;
|
||||
};
|
||||
|
||||
//! Returns chunk with ID "id" and value "data" for write to stream
|
||||
template <typename T> static Chunk<T> chunk(int id, const T & data) {return Chunk<T>(id, data);}
|
||||
template <typename T> static ChunkConst<T> chunk(int id, const T & data) {return ChunkConst<T>(id, data);}
|
||||
|
||||
//! Add data to this chunk strean with ID "id" and value "data"
|
||||
template <typename T> PIChunkStream & add(int id, const T & data) {*this << Chunk<T>(id, data); return *this;}
|
||||
template <typename T> PIChunkStream & add(int id, const T & data) {*this << ChunkConst<T>(id, data); return *this;}
|
||||
|
||||
void setSource(const PIByteArray & data);
|
||||
void setSource(PIByteArray * data);
|
||||
@@ -112,6 +118,7 @@ private:
|
||||
PIMap<int, PIByteArray> data_map;
|
||||
|
||||
template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c);
|
||||
template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::ChunkConst<T> & c);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -133,5 +140,24 @@ PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c
|
||||
}
|
||||
return s;
|
||||
}
|
||||
template <typename T>
|
||||
PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::ChunkConst<T> & c) {
|
||||
PIByteArray ba;
|
||||
ba << c.data;
|
||||
switch (s.version_) {
|
||||
case PIChunkStream::Version_1:
|
||||
(*(s.data_)) << c.id << ba;
|
||||
break;
|
||||
case PIChunkStream::Version_2:
|
||||
if (s.data_->isEmpty())
|
||||
(*(s.data_)) << uchar(uchar(s.version_) | 0x80);
|
||||
PIChunkStream::writeVInt(*(s.data_), c.id);
|
||||
PIChunkStream::writeVInt(*(s.data_), ba.size());
|
||||
s.data_->append(ba);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // PICHUNKSTREAM_H
|
||||
|
||||
@@ -658,6 +658,26 @@ PIString & PIString::replaceAll(const PIString & what, const PIString & with) {
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::replaceAll(const PIString & what, const char with) {
|
||||
if (what.isEmpty()) return *this;
|
||||
int l = what.length(), dl = what.length() - 1;
|
||||
for (int i = 0; i < length() - l + 1; ++i) {
|
||||
bool match = true;
|
||||
for (int j = 0; j < l; ++j) {
|
||||
if (at(j + i) != what[j]) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) continue;
|
||||
if (dl > 0) PIDeque<PIChar>::remove(i, dl);
|
||||
at(i) = PIChar(with);
|
||||
//i -= l;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
PIString & PIString::replaceAll(const char what, const char with) {
|
||||
int l = length();
|
||||
for (int i = 0; i < l; ++i) {
|
||||
|
||||
@@ -283,6 +283,11 @@ public:
|
||||
* \sa \a replace(), \a replaced() */
|
||||
PIString & replaceAll(const PIString & what, const PIString & with);
|
||||
|
||||
/*! \brief Replace all founded substrings "what" with symbol "with" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::replaceAll
|
||||
* \sa \a replace(), \a replaced() */
|
||||
PIString & replaceAll(const PIString & what, const char with);
|
||||
|
||||
/*! \brief Replace all founded symbols "what" with symbol "with" and return this string
|
||||
* \details Example: \snippet pistring.cpp PIString::replaceAll
|
||||
* \sa \a replace(), \a replaced() */
|
||||
|
||||
@@ -214,9 +214,10 @@ PIByteArray PIIntrospection::packThreads() {
|
||||
if (p) {
|
||||
p->mutex.lock();
|
||||
PIMap<PIThread*, PIIntrospectionThreads::ThreadInfo> & tm(p->threads);
|
||||
for (PIMap<PIThread*, PIIntrospectionThreads::ThreadInfo>::iterator i = tm.begin(); i != tm.end(); ++i) {
|
||||
i.value().classname = PIStringAscii(i.key()->className());
|
||||
i.value().name = i.key()->name();
|
||||
auto it = tm.makeIterator();
|
||||
while (it.next()) {
|
||||
it.valueRef().classname = PIStringAscii(it.key()->className());
|
||||
it.valueRef().name = it.key()->name();
|
||||
}
|
||||
ret << tm.values();
|
||||
p->mutex.unlock();
|
||||
|
||||
@@ -235,7 +235,7 @@ PIByteArray PIFile::readAll(bool forceRead) {
|
||||
llong s = size();
|
||||
if (s < 0) return a;
|
||||
a.resize(s);
|
||||
s = readAll(a.data());
|
||||
fread(a.data(), 1, s, PRIVATE->fd);
|
||||
seek(cp);
|
||||
if (s >= 0) a.resize(s);
|
||||
return a;
|
||||
|
||||
@@ -388,8 +388,9 @@ bool PIConnection::removeDevice(const PIString & full_path) {
|
||||
}
|
||||
bounded_extractors.remove(dev);
|
||||
channels_.remove(dev);
|
||||
for (auto it = channels_.begin(); it != channels_.end(); it++)
|
||||
it.value().removeAll(dev);
|
||||
auto it = channels_.makeIterator();
|
||||
while (it.next())
|
||||
it.valueRef().removeAll(dev);
|
||||
__device_pool__->lock();
|
||||
if (diags_.value(dev, 0) != 0)
|
||||
delete diags_.value(dev);
|
||||
@@ -411,8 +412,9 @@ void PIConnection::removeAllDevices() {
|
||||
s.value()->unlock();
|
||||
}
|
||||
channels_.remove(d);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(d);
|
||||
auto it = channels_.makeIterator();
|
||||
while (it.next())
|
||||
it.valueRef().removeAll(d);
|
||||
if (diags_.value(d, 0) != 0)
|
||||
delete diags_.value(d);
|
||||
diags_.remove(d);
|
||||
@@ -566,8 +568,9 @@ void PIConnection::removeAllFilters() {
|
||||
for (auto i = extractors.constBegin(); i != extractors.constEnd(); i++) {
|
||||
if (i.value() == 0) continue;
|
||||
channels_.remove(i.value()->extractor);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(i.value()->extractor);
|
||||
auto it = channels_.makeIterator();
|
||||
while (it.next())
|
||||
it.valueRef().removeAll(i.value()->extractor);
|
||||
if (diags_.value(i.value()->extractor, 0) != 0)
|
||||
delete diags_.value(i.value()->extractor);
|
||||
diags_.remove(i.value()->extractor);
|
||||
@@ -658,8 +661,9 @@ bool PIConnection::removeChannel(const PIString & name0) {
|
||||
if (pe0 != 0) dev0 = pe0;
|
||||
if (dev0 == 0) return false;
|
||||
channels_.remove(dev0);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(dev0);
|
||||
auto it = channels_.makeIterator();
|
||||
while (it.next())
|
||||
it.valueRef().removeAll(dev0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1240,8 +1244,9 @@ void PIConnection::Sender::tick(void * , int) {
|
||||
void PIConnection::unboundExtractor(PIPacketExtractor * pe) {
|
||||
if (pe == 0) return;
|
||||
channels_.remove(pe);
|
||||
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it)
|
||||
it.value().removeAll(pe);
|
||||
auto it = channels_.makeIterator();
|
||||
while (it.next())
|
||||
it.valueRef().removeAll(pe);
|
||||
bounded_extractors.remove(pe);
|
||||
PIVector<PIIODevice * > k = bounded_extractors.keys();
|
||||
piForeach (PIIODevice * i, k) {
|
||||
|
||||
@@ -36,13 +36,12 @@ PIByteArray PIResources::get(const PIString & name) {
|
||||
|
||||
|
||||
void PIResources::dump() {
|
||||
PIMap<PIString, PIResourcesStorage::Section * > & sm(PIResourcesStorage::instance()->sections);
|
||||
PIMap<PIString, PIResourcesStorage::Section * >::iterator si;
|
||||
for (si = sm.begin(); si != sm.end(); ++si) {
|
||||
auto si = PIResourcesStorage::instance()->sections.makeIterator();
|
||||
while (si.next()) {
|
||||
piCout << "Section [" << si.key() << "]";
|
||||
if (!si.value()) continue;
|
||||
PIMap<PIString, PIByteArray * >::iterator fi;
|
||||
for (fi = si.value()->entries.begin(); fi != si.value()->entries.end(); ++fi) {
|
||||
auto fi = si.value()->entries.makeIterator();
|
||||
while (fi.next()) {
|
||||
PIString s = fi.key() + ": ";
|
||||
s << (fi.value() ? fi.value()->size_s() : 0) << " b";
|
||||
piCout << " " << s;
|
||||
|
||||
@@ -31,8 +31,8 @@ PIResourcesStorage::Section::~Section() {
|
||||
|
||||
|
||||
void PIResourcesStorage::Section::add(const PIResourcesStorage::Section & s) {
|
||||
PIMap<PIString, PIByteArray * >::const_iterator i;
|
||||
for (i = s.entries.begin(); i != s.entries.end(); ++i) {
|
||||
auto i = s.entries.makeIterator();
|
||||
while (i.next()) {
|
||||
if (!i.value()) continue;
|
||||
if (entries.value(i.key(), 0)) continue;
|
||||
entries[i.key()] = i.value();
|
||||
@@ -83,10 +83,10 @@ void PIResourcesStorage::registerSection(const uchar * rc_data, const uchar * rc
|
||||
piForeachC (PIResourcesStorage::__RCEntry & e, el) {
|
||||
ebs[e.section] << e;
|
||||
}
|
||||
PIMap<PIString, PIVector<PIResourcesStorage::__RCEntry> >::iterator it;
|
||||
for (it = ebs.begin(); it != ebs.end(); ++it) {
|
||||
auto it = ebs.makeIterator();
|
||||
while (it.next()) {
|
||||
PIResourcesStorage::Section s;
|
||||
PIVector<PIResourcesStorage::__RCEntry> & itv(it.value());
|
||||
const PIVector<PIResourcesStorage::__RCEntry> & itv(it.value());
|
||||
piForeachC (PIResourcesStorage::__RCEntry & e, itv) {
|
||||
//piCout << "add" << e.name << e.alias << PIString::readableSize(e.size);
|
||||
PIByteArray * eba = new PIByteArray(&(rc_data[e.offset]), e.size);
|
||||
@@ -114,8 +114,8 @@ PIByteArray PIResourcesStorage::get(const PIString & section_name, const PIStrin
|
||||
|
||||
|
||||
PIByteArray PIResourcesStorage::get(const PIString & entry_name) const {
|
||||
PIMap<PIString, Section * >::const_iterator i;
|
||||
for (i = sections.begin(); i != sections.end(); ++i) {
|
||||
auto i = sections.makeIterator();
|
||||
while (i.next()) {
|
||||
if (!i.value()) continue;
|
||||
PIByteArray * ba = i.value()->entries.value(entry_name, 0);
|
||||
if (!ba) continue;
|
||||
|
||||
@@ -331,9 +331,10 @@ void PISystemMonitor::run() {
|
||||
tstat.cpu_load_system = piClampf(tstat.cpu_load_system, 0.f, 100.f);
|
||||
tstat.cpu_load_user = piClampf(tstat.cpu_load_user , 0.f, 100.f);
|
||||
|
||||
for (PIMap<llong, ThreadStats>::iterator i = cur_tm.begin(); i != cur_tm.end(); ++i) {
|
||||
auto i = cur_tm.makeIterator();
|
||||
while (i.next()) {
|
||||
if (!last_tm.contains(i.key())) continue;
|
||||
ThreadStats & ts_new(i.value());
|
||||
ThreadStats & ts_new(i.valueRef());
|
||||
ThreadStats & ts_old(last_tm[i.key()]);
|
||||
ts_new.cpu_load_kernel = calcThreadUsage(ts_new.kernel_time, ts_old.kernel_time);
|
||||
ts_new.cpu_load_user = calcThreadUsage(ts_new.user_time, ts_old.user_time);
|
||||
|
||||
@@ -20,43 +20,43 @@
|
||||
#ifndef PIBLOCKINGDEQUEUE_H
|
||||
#define PIBLOCKINGDEQUEUE_H
|
||||
|
||||
#include "pideque.h"
|
||||
#include <queue>
|
||||
#include "piconditionvar.h"
|
||||
|
||||
/**
|
||||
* @brief A Queue that supports operations that wait for the queue to become non-empty when retrieving an element, and
|
||||
* wait for space to become available in the queue when storing an element.
|
||||
*/
|
||||
template <typename T>
|
||||
class PIBlockingDequeue: private PIDeque<T> {
|
||||
template <typename T, template<typename = T, typename...> class Queue_ = std::deque, typename ConditionVariable_ = PIConditionVariable>
|
||||
class PIBlockingDequeue {
|
||||
public:
|
||||
typedef Queue_<T> QueueType;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
explicit inline PIBlockingDequeue(size_t capacity = SIZE_MAX,
|
||||
PIConditionVariable* cond_var_add = new PIConditionVariable(),
|
||||
PIConditionVariable* cond_var_rem = new PIConditionVariable())
|
||||
: cond_var_add(cond_var_add), cond_var_rem(cond_var_rem), max_size(capacity) { }
|
||||
explicit PIBlockingDequeue(size_t capacity = SIZE_MAX)
|
||||
: cond_var_add(new ConditionVariable_()), cond_var_rem(new ConditionVariable_()), max_size(capacity) { }
|
||||
|
||||
/**
|
||||
* @brief Copy constructor. Initialize queue with copy of other queue elements. Not thread-safe for other queue.
|
||||
* @brief Copy constructor. Initialize queue with copy of other container elements. Not thread-safe for other queue.
|
||||
*/
|
||||
explicit inline PIBlockingDequeue(const PIDeque<T>& other) : cond_var_add(new PIConditionVariable()), cond_var_rem(new PIConditionVariable()) {
|
||||
template<typename Iterable,
|
||||
typename std::enable_if<!std::is_arithmetic<Iterable>::value, int>::type = 0>
|
||||
explicit PIBlockingDequeue(const Iterable& other): PIBlockingDequeue() {
|
||||
mutex.lock();
|
||||
max_size = SIZE_MAX;
|
||||
PIDeque<T>::append(other);
|
||||
for (const T& t : other) data_queue.push_back(t);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Thread-safe copy constructor. Initialize queue with copy of other queue elements.
|
||||
*/
|
||||
inline PIBlockingDequeue(PIBlockingDequeue<T> & other) : cond_var_add(new PIConditionVariable()), cond_var_rem(new PIConditionVariable()) {
|
||||
explicit PIBlockingDequeue(PIBlockingDequeue<T>& other): PIBlockingDequeue() {
|
||||
other.mutex.lock();
|
||||
mutex.lock();
|
||||
max_size = other.max_size;
|
||||
PIDeque<T>::append(static_cast<PIDeque<T>&>(other));
|
||||
data_queue = other.data_queue;
|
||||
mutex.unlock();
|
||||
other.mutex.unlock();
|
||||
}
|
||||
@@ -71,10 +71,11 @@ public:
|
||||
*
|
||||
* @param v the element to add
|
||||
*/
|
||||
void put(const T & v) {
|
||||
template<typename Type>
|
||||
void put(Type && v) {
|
||||
mutex.lock();
|
||||
cond_var_rem->wait(mutex, [&]() { return PIDeque<T>::size() < max_size; });
|
||||
PIDeque<T>::push_back(v);
|
||||
cond_var_rem->wait(mutex, [&]() { return data_queue.size() < max_size; });
|
||||
data_queue.push_back(std::forward<Type>(v));
|
||||
mutex.unlock();
|
||||
cond_var_add->notifyOne();
|
||||
}
|
||||
@@ -86,13 +87,14 @@ public:
|
||||
* @param v the element to add
|
||||
* @return true if the element was added to this queue, else false
|
||||
*/
|
||||
bool offer(const T & v) {
|
||||
template<typename Type>
|
||||
bool offer(Type && v) {
|
||||
mutex.lock();
|
||||
if (PIDeque<T>::size() >= max_size) {
|
||||
if (data_queue.size() >= max_size) {
|
||||
mutex.unlock();
|
||||
return false;
|
||||
}
|
||||
PIDeque<T>::push_back(v);
|
||||
data_queue.push_back(std::forward<Type>(v));
|
||||
mutex.unlock();
|
||||
cond_var_add->notifyOne();
|
||||
return true;
|
||||
@@ -106,10 +108,11 @@ public:
|
||||
* @param timeoutMs how long to wait before giving up, in milliseconds
|
||||
* @return true if successful, or false if the specified waiting time elapses before space is available
|
||||
*/
|
||||
bool offer(const T & v, int timeoutMs) {
|
||||
template<typename Type>
|
||||
bool offer(Type && v, int timeoutMs) {
|
||||
mutex.lock();
|
||||
bool isOk = cond_var_rem->waitFor(mutex, timeoutMs, [&]() { return PIDeque<T>::size() < max_size; } );
|
||||
if (isOk) PIDeque<T>::push_back(v);
|
||||
bool isOk = cond_var_rem->waitFor(mutex, timeoutMs, [&]() { return data_queue.size() < max_size; } );
|
||||
if (isOk) data_queue.push_back(std::forward<Type>(v));
|
||||
mutex.unlock();
|
||||
if (isOk) cond_var_add->notifyOne();
|
||||
return isOk;
|
||||
@@ -121,10 +124,10 @@ public:
|
||||
* @return the head of this queue
|
||||
*/
|
||||
T take() {
|
||||
T t;
|
||||
mutex.lock();
|
||||
cond_var_add->wait(mutex, [&]() { return !PIDeque<T>::isEmpty(); });
|
||||
t = T(PIDeque<T>::take_front());
|
||||
cond_var_add->wait(mutex, [&]() { return data_queue.size() != 0; });
|
||||
T t = std::move(data_queue.front());
|
||||
data_queue.pop_front();
|
||||
mutex.unlock();
|
||||
cond_var_rem->notifyOne();
|
||||
return t;
|
||||
@@ -140,11 +143,17 @@ public:
|
||||
* return value is retrieved value
|
||||
* @return the head of this queue, or defaultVal if the specified waiting time elapses before an element is available
|
||||
*/
|
||||
T poll(int timeoutMs, const T & defaultVal = T(), bool * isOk = nullptr) {
|
||||
T t;
|
||||
template<typename Type = T>
|
||||
T poll(int timeoutMs, Type && defaultVal = Type(), bool * isOk = nullptr) {
|
||||
mutex.lock();
|
||||
bool isNotEmpty = cond_var_add->waitFor(mutex, timeoutMs, [&]() { return !PIDeque<T>::isEmpty(); });
|
||||
t = isNotEmpty ? T(PIDeque<T>::take_front()) : defaultVal;
|
||||
bool isNotEmpty = cond_var_add->waitFor(mutex, timeoutMs, [&]() { return data_queue.size() != 0; });
|
||||
T t;
|
||||
if (isNotEmpty) {
|
||||
t = std::move(data_queue.front());
|
||||
data_queue.pop_front();
|
||||
} else {
|
||||
t = std::forward<Type>(defaultVal);
|
||||
}
|
||||
mutex.unlock();
|
||||
if (isNotEmpty) cond_var_rem->notifyOne();
|
||||
if (isOk) *isOk = isNotEmpty;
|
||||
@@ -160,11 +169,17 @@ public:
|
||||
* return value is retrieved value
|
||||
* @return the head of this queue, or defaultVal if the specified waiting time elapses before an element is available
|
||||
*/
|
||||
T poll(const T & defaultVal = T(), bool * isOk = nullptr) {
|
||||
template<typename Type = T>
|
||||
T poll(Type && defaultVal = Type(), bool * isOk = nullptr) {
|
||||
T t;
|
||||
mutex.lock();
|
||||
bool isNotEmpty = !PIDeque<T>::isEmpty();
|
||||
t = isNotEmpty ? PIDeque<T>::take_front() : defaultVal;
|
||||
bool isNotEmpty = data_queue.size() != 0;
|
||||
if (isNotEmpty) {
|
||||
t = std::move(data_queue.front());
|
||||
data_queue.pop_front();
|
||||
} else {
|
||||
t = std::forward<Type>(defaultVal);
|
||||
}
|
||||
mutex.unlock();
|
||||
if (isNotEmpty) cond_var_rem->notifyOne();
|
||||
if (isOk) *isOk = isNotEmpty;
|
||||
@@ -193,7 +208,7 @@ public:
|
||||
*/
|
||||
size_t remainingCapacity() {
|
||||
mutex.lock();
|
||||
size_t c = max_size - PIDeque<T>::size();
|
||||
size_t c = max_size - data_queue.size();
|
||||
mutex.unlock();
|
||||
return c;
|
||||
}
|
||||
@@ -203,7 +218,7 @@ public:
|
||||
*/
|
||||
size_t size() {
|
||||
mutex.lock();
|
||||
size_t s = PIDeque<T>::size();
|
||||
size_t s = data_queue.size();
|
||||
mutex.unlock();
|
||||
return s;
|
||||
}
|
||||
@@ -211,10 +226,14 @@ public:
|
||||
/**
|
||||
* @brief Removes all available elements from this queue and adds them to other given queue.
|
||||
*/
|
||||
size_t drainTo(PIDeque<T>& other, size_t maxCount = SIZE_MAX) {
|
||||
template<typename Appendable>
|
||||
size_t drainTo(Appendable& other, size_t maxCount = SIZE_MAX) {
|
||||
mutex.lock();
|
||||
size_t count = ((maxCount > PIDeque<T>::size()) ? PIDeque<T>::size() : maxCount);
|
||||
for (size_t i = 0; i < count; ++i) other.push_back(PIDeque<T>::take_front());
|
||||
size_t count = maxCount > data_queue.size() ? data_queue.size() : maxCount;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
other.push_back(std::move(data_queue.front()));
|
||||
data_queue.pop_front();
|
||||
}
|
||||
mutex.unlock();
|
||||
return count;
|
||||
}
|
||||
@@ -225,18 +244,23 @@ public:
|
||||
size_t drainTo(PIBlockingDequeue<T>& other, size_t maxCount = SIZE_MAX) {
|
||||
mutex.lock();
|
||||
other.mutex.lock();
|
||||
size_t count = maxCount > PIDeque<T>::size() ? PIDeque<T>::size() : maxCount;
|
||||
size_t otherRemainingCapacity = other.max_size - static_cast<PIDeque<T> >(other).size();
|
||||
size_t count = maxCount > data_queue.size() ? data_queue.size() : maxCount;
|
||||
size_t otherRemainingCapacity = other.max_size - data_queue.size();
|
||||
if (count > otherRemainingCapacity) count = otherRemainingCapacity;
|
||||
for (size_t i = 0; i < count; ++i) other.push_back(PIDeque<T>::take_front());
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
other.data_queue.push_back(std::move(data_queue.front()));
|
||||
data_queue.pop_front();
|
||||
}
|
||||
other.mutex.unlock();
|
||||
mutex.unlock();
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
PIMutex mutex;
|
||||
PIConditionVariable * cond_var_add, * cond_var_rem;
|
||||
// TODO change to type without point
|
||||
ConditionVariable_ *cond_var_add, *cond_var_rem;
|
||||
QueueType data_queue;
|
||||
size_t max_size;
|
||||
|
||||
};
|
||||
|
||||
0
lib/main/thread/piexecutor.cpp
Normal file
0
lib/main/thread/piexecutor.cpp
Normal file
200
lib/main/thread/piexecutor.h
Normal file
200
lib/main/thread/piexecutor.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
|
||||
Stephan Fomenko
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PIEXECUTOR_H
|
||||
#define PIEXECUTOR_H
|
||||
|
||||
#include "piblockingdequeue.h"
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
/**
|
||||
* @brief Wrapper for custom invoke operator available function types.
|
||||
* @note Source from: "Энтони Уильямс, Параллельное программирование на С++ в действии. Практика разработки многопоточных
|
||||
* программ. Пер. с англ. Слинкин А. А. - M.: ДМК Пресс, 2012 - 672c.: ил." (page 387)
|
||||
*/
|
||||
class FunctionWrapper {
|
||||
struct ImplBase {
|
||||
virtual void call() = 0;
|
||||
virtual ~ImplBase() = default;
|
||||
};
|
||||
|
||||
std::unique_ptr<ImplBase> impl;
|
||||
|
||||
template<typename F>
|
||||
struct ImplType: ImplBase {
|
||||
F f;
|
||||
explicit ImplType(F&& f): f(std::forward<F>(f)) {}
|
||||
void call() final { f(); }
|
||||
};
|
||||
public:
|
||||
template<typename F, typename = std::enable_if<!std::is_same<F, FunctionWrapper>::value> >
|
||||
explicit FunctionWrapper(F&& f): impl(new ImplType<F>(std::forward<F>(f))) {}
|
||||
|
||||
void operator()() { impl->call(); }
|
||||
|
||||
explicit operator bool() const noexcept { return static_cast<bool>(impl); }
|
||||
|
||||
FunctionWrapper() = default;
|
||||
FunctionWrapper(FunctionWrapper&& other) noexcept : impl(std::move(other.impl)) {}
|
||||
FunctionWrapper& operator=(FunctionWrapper&& other) noexcept {
|
||||
impl = std::move(other.impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
FunctionWrapper(const FunctionWrapper& other) = delete;
|
||||
FunctionWrapper& operator=(const FunctionWrapper&) = delete;
|
||||
};
|
||||
|
||||
template <typename Thread_ = PIThread, typename Dequeue_ = PIBlockingDequeue<FunctionWrapper>>
|
||||
class PIThreadPoolExecutorTemplate {
|
||||
public:
|
||||
NO_COPY_CLASS(PIThreadPoolExecutorTemplate)
|
||||
explicit PIThreadPoolExecutorTemplate(size_t corePoolSize = 1) : isShutdown_(false) { makePool(corePoolSize); }
|
||||
|
||||
virtual ~PIThreadPoolExecutorTemplate() {
|
||||
shutdownNow();
|
||||
while (threadPool.size() > 0) delete threadPool.take_back();
|
||||
}
|
||||
|
||||
template<typename FunctionType>
|
||||
std::future<typename std::result_of<FunctionType()>::type> submit(FunctionType&& callable) {
|
||||
typedef typename std::result_of<FunctionType()>::type ResultType;
|
||||
|
||||
if (!isShutdown_) {
|
||||
std::packaged_task<ResultType()> callable_task(std::forward<FunctionType>(callable));
|
||||
auto future = callable_task.get_future();
|
||||
FunctionWrapper functionWrapper(callable_task);
|
||||
taskQueue.offer(std::move(functionWrapper));
|
||||
return future;
|
||||
} else {
|
||||
return std::future<ResultType>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FunctionType>
|
||||
void execute(FunctionType&& runnable) {
|
||||
if (!isShutdown_) {
|
||||
FunctionWrapper function_wrapper(std::forward<FunctionType>(runnable));
|
||||
taskQueue.offer(std::move(function_wrapper));
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
isShutdown_ = true;
|
||||
}
|
||||
|
||||
void shutdownNow() {
|
||||
isShutdown_ = true;
|
||||
for (size_t i = 0; i < threadPool.size(); ++i) threadPool[i]->stop();
|
||||
}
|
||||
|
||||
bool isShutdown() const {
|
||||
return isShutdown_;
|
||||
}
|
||||
|
||||
bool awaitTermination(int timeoutMs) {
|
||||
PITimeMeasurer measurer;
|
||||
for (size_t i = 0; i < threadPool.size(); ++i) {
|
||||
int dif = timeoutMs - (int)measurer.elapsed_m();
|
||||
if (dif < 0) return false;
|
||||
if (!threadPool[i]->waitForFinish(dif)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::atomic_bool isShutdown_;
|
||||
Dequeue_ taskQueue;
|
||||
PIVector<Thread_*> threadPool;
|
||||
|
||||
template<typename Function>
|
||||
PIThreadPoolExecutorTemplate(size_t corePoolSize, Function&& onBeforeStart) : isShutdown_(false) {
|
||||
makePool(corePoolSize, std::forward<Function>(onBeforeStart));
|
||||
}
|
||||
|
||||
void makePool(size_t corePoolSize, std::function<void(Thread_*)>&& onBeforeStart = [](Thread_*){}) {
|
||||
for (size_t i = 0; i < corePoolSize; ++i) {
|
||||
auto* thread = new Thread_([&, i](){
|
||||
auto runnable = taskQueue.poll(100);
|
||||
if (runnable) {
|
||||
runnable();
|
||||
}
|
||||
if (isShutdown_ && taskQueue.size() == 0) threadPool[i]->stop();
|
||||
});
|
||||
threadPool.push_back(thread);
|
||||
onBeforeStart(thread);
|
||||
thread->start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef PIThreadPoolExecutorTemplate<> PIThreadPoolExecutor;
|
||||
|
||||
#ifdef DOXYGEN
|
||||
/**
|
||||
* @brief Thread pools address two different problems: they usually provide improved performance when executing large
|
||||
* numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and
|
||||
* managing the resources, including threads, consumed when executing a collection of tasks.
|
||||
*/
|
||||
class PIThreadPoolExecutor {
|
||||
public:
|
||||
explicit PIThreadPoolExecutor(size_t corePoolSize);
|
||||
|
||||
virtual ~PIThreadPoolExecutor();
|
||||
|
||||
/**
|
||||
* @brief Submits a Runnable task for execution and returns a Future representing that task. The Future's get method
|
||||
* will return null upon successful completion.
|
||||
*
|
||||
* @tparam FunctionType - custom type of function with operator() and return type
|
||||
* @tparam R - derived from FunctionType return type
|
||||
*
|
||||
* @param callable - the task to submit
|
||||
* @return a future representing pending completion of the task
|
||||
*/
|
||||
std::future<R> submit(FunctionType&& callable);
|
||||
|
||||
/**
|
||||
* @brief Executes the given task sometime in the future. The task execute in an existing pooled thread. If the task
|
||||
* cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been
|
||||
* reached.
|
||||
*
|
||||
* @tparam FunctionType - custom type of function with operator() and return type
|
||||
*
|
||||
* @param runnable not empty function for thread pool execution
|
||||
*/
|
||||
void execute(FunctionType&& runnable);
|
||||
|
||||
/**
|
||||
* @brief Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be
|
||||
* accepted. Invocation has no additional effect if already shut down. This method does not wait for previously
|
||||
* submitted tasks to complete execution. Use awaitTermination to do that.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
void shutdownNow();
|
||||
|
||||
bool isShutdown() const;
|
||||
|
||||
bool awaitTermination(int timeoutMs);
|
||||
};
|
||||
#endif //DOXYGEN
|
||||
|
||||
#endif //PIEXECUTOR_H
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "pitimer.h"
|
||||
#include "pipipelinethread.h"
|
||||
#include "pigrabberbase.h"
|
||||
#include "pithreadpoolexecutor.h"
|
||||
#include "piexecutor.h"
|
||||
#include "piconditionvar.h"
|
||||
|
||||
#endif // PITHREADMODULE_H
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
|
||||
Stephan Fomenko
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pithreadpoolexecutor.h"
|
||||
#include "pisysteminfo.h"
|
||||
|
||||
/*! \class PIThreadPoolExecutor
|
||||
* @brief Thread pools address two different problems: they usually provide improved performance when executing large
|
||||
* numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and
|
||||
* managing the resources, including threads, consumed when executing a collection of tasks.
|
||||
*/
|
||||
|
||||
|
||||
PIThreadPoolExecutor::PIThreadPoolExecutor(size_t corePoolSize, PIBlockingDequeue<std::function<void()> > * taskQueue_) : isShutdown_(false) {
|
||||
queue_own = false;
|
||||
if (corePoolSize <= 0)
|
||||
corePoolSize = PISystemInfo::instance()->processorsCount;
|
||||
if (!taskQueue_) {
|
||||
taskQueue = new PIBlockingDequeue<std::function<void()> >();
|
||||
queue_own = true;
|
||||
}
|
||||
for (size_t i = 0; i < corePoolSize; ++i) {
|
||||
PIThread * thread = new PIThread([&, i](){
|
||||
auto runnable = taskQueue->poll(100, std::function<void()>());
|
||||
if (runnable) {
|
||||
runnable();
|
||||
}
|
||||
if (isShutdown_ && taskQueue->size() == 0) threadPool[i]->stop();
|
||||
});
|
||||
threadPool.push_back(thread);
|
||||
thread->start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PIThreadPoolExecutor::awaitTermination(int timeoutMs) {
|
||||
PITimeMeasurer measurer;
|
||||
for (size_t i = 0; i < threadPool.size(); ++i) {
|
||||
int dif = timeoutMs - (int)measurer.elapsed_m();
|
||||
if (dif < 0) return false;
|
||||
if (!threadPool[i]->waitForFinish(dif)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PIThreadPoolExecutor::shutdownNow() {
|
||||
isShutdown_ = true;
|
||||
for (size_t i = 0; i < threadPool.size(); ++i) threadPool[i]->stop();
|
||||
}
|
||||
|
||||
|
||||
PIThreadPoolExecutor::~PIThreadPoolExecutor() {
|
||||
shutdownNow();
|
||||
while (threadPool.size() > 0) delete threadPool.take_back();
|
||||
if (queue_own)
|
||||
delete taskQueue;
|
||||
}
|
||||
|
||||
|
||||
void PIThreadPoolExecutor::execute(const std::function<void()> & runnable) {
|
||||
if (!isShutdown_) taskQueue->offer(runnable);
|
||||
}
|
||||
|
||||
|
||||
bool PIThreadPoolExecutor::isShutdown() const {
|
||||
return isShutdown_;
|
||||
}
|
||||
|
||||
|
||||
void PIThreadPoolExecutor::shutdown() {
|
||||
isShutdown_ = true;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
|
||||
Stephan Fomenko
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PITHREADPOOLEXECUTOR_H
|
||||
#define PITHREADPOOLEXECUTOR_H
|
||||
|
||||
#include "piblockingdequeue.h"
|
||||
#include <atomic>
|
||||
|
||||
|
||||
class PIP_EXPORT PIThreadPoolExecutor {
|
||||
public:
|
||||
explicit PIThreadPoolExecutor(size_t corePoolSize = -1, PIBlockingDequeue<std::function<void()> > * taskQueue_ = 0);
|
||||
|
||||
virtual ~PIThreadPoolExecutor();
|
||||
|
||||
/**
|
||||
* @brief Executes the given task sometime in the future. The task execute in an existing pooled thread. If the task
|
||||
* cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been
|
||||
* reached.
|
||||
*
|
||||
* @param runnable not empty function for thread pool execution
|
||||
*/
|
||||
void execute(const std::function<void()> & runnable);
|
||||
|
||||
void shutdownNow();
|
||||
|
||||
/**
|
||||
* @brief Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be
|
||||
* accepted. Invocation has no additional effect if already shut down. This method does not wait for previously
|
||||
* submitted tasks to complete execution. Use awaitTermination to do that.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
bool isShutdown() const;
|
||||
|
||||
bool awaitTermination(int timeoutMs);
|
||||
|
||||
private:
|
||||
std::atomic_bool isShutdown_;
|
||||
PIBlockingDequeue<std::function<void()> > * taskQueue;
|
||||
PIVector<PIThread*> threadPool;
|
||||
bool queue_own;
|
||||
|
||||
};
|
||||
|
||||
#endif // PITHREADPOOLEXECUTOR_H
|
||||
67
main.cpp
67
main.cpp
@@ -34,32 +34,81 @@ int main() {
|
||||
class db {
|
||||
public:
|
||||
db() {
|
||||
name = PIStringAscii("sflner;ljner.vjnrevsg;j35m4;gberjg2mnv");
|
||||
for (int i=0; i<10000; ++i)
|
||||
x << sin(double(i)/180.0);
|
||||
//printf("jkfkhg\n");
|
||||
}
|
||||
private:
|
||||
PIString name;
|
||||
PIVector<double> x;
|
||||
};
|
||||
|
||||
inline PIByteArray & operator <<(PIByteArray & ba, const db & v) {
|
||||
PIChunkStream cs;
|
||||
cs.add(1, v.name).add(2, v.x);
|
||||
ba << cs.data();
|
||||
return ba;
|
||||
}
|
||||
inline PIByteArray & operator >>(PIByteArray & ba, db & v) {
|
||||
PIByteArray src; ba >> src; PIChunkStream cs(src);
|
||||
while (!cs.atEnd()) {
|
||||
switch (cs.read()) {
|
||||
case 1: cs.get(v.name); break;
|
||||
case 2: cs.get(v.x); break;
|
||||
}
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
||||
PIEthernet eth;
|
||||
|
||||
#include "picodeparser.h"
|
||||
int main() {
|
||||
/*PIString s(" 324 654 sf 5fdwg sdfsdf sdfefg");
|
||||
piCout << s;
|
||||
piCout << s.replaceAll(' ', '1');*/
|
||||
piDebug = false;
|
||||
//PICodeParser cp;
|
||||
//cp.parseFile("SH_plugin_base.h");
|
||||
|
||||
PIMap<int, PIString> m;
|
||||
m[1] = "one";
|
||||
m[2] = "two";
|
||||
|
||||
auto it = m.makeIterator();
|
||||
while (it.next()) {
|
||||
//it.next();
|
||||
it.valueRef() << "_!";
|
||||
piCout << it.key() << it.value();
|
||||
}
|
||||
|
||||
/*eth.__meta_data
|
||||
piForeachC (auto & i, cp.enums) {
|
||||
i.
|
||||
}*/
|
||||
|
||||
/*piDebug = false;
|
||||
double min = -1, max = -1, mean = 0;
|
||||
for (int i = 0; i < 1; ++i) {
|
||||
PICodeParser cp;
|
||||
const int iterations = 50;
|
||||
db d, d2;
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
//PICodeParser cp;
|
||||
PITimeMeasurer tm;
|
||||
cp.parseFile("SH_plugin_base.h");
|
||||
for (int j = 0; j < 100; ++j) {
|
||||
PIByteArray ba;
|
||||
ba << d;
|
||||
}
|
||||
//cp.parseFile("SH_plugin_base.h");
|
||||
double ms = tm.elapsed_m();
|
||||
if (min < 0) min = ms;
|
||||
if (max < 0) max = ms;
|
||||
min = piMin(min, ms);
|
||||
max = piMax(max, ms);
|
||||
mean += ms;
|
||||
|
||||
PIByteArray ba;
|
||||
ba << d;
|
||||
d2.name.clear();
|
||||
d2.x.clear();
|
||||
ba >> d2;
|
||||
}
|
||||
piDebug = true;
|
||||
piCout << min << (mean / 50) << max;
|
||||
piCout << d2.name << d2.x.size_s();
|
||||
piCout << min << (mean / iterations) << max;*/
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ macro(pip_test NAME LIBS)
|
||||
file(GLOB _CPPS "${NAME}/*.cpp")
|
||||
file(GLOB _HDRS "${NAME}/*.h")
|
||||
set(_target pip_${NAME}_test)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PIP_ROOT_BINARY_DIR}")
|
||||
add_executable(${_target} ${_CPPS} ${_HDRS})
|
||||
target_link_libraries(${_target} pip ${LIBS} gtest_main gmock_main)
|
||||
add_test(NAME ${_target} COMMAND tests)
|
||||
add_custom_target(${_target}_perform ALL COMMAND ${_target})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
list(APPEND PIP_TESTS_LIST "${NAME}")
|
||||
set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
@@ -1,110 +1,363 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "testutil.h"
|
||||
#include "piblockingdequeue.h"
|
||||
|
||||
class MockConditionVar: public PIConditionVariable {
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Ne;
|
||||
using ::testing::Matcher;
|
||||
using ::testing::Expectation;
|
||||
using ::testing::Sequence;
|
||||
using ::testing::NiceMock;
|
||||
|
||||
class MockConditionVar {
|
||||
public:
|
||||
bool isWaitCalled = false;
|
||||
bool isWaitForCalled = false;
|
||||
bool isTrueCondition = false;
|
||||
int timeout = -1;
|
||||
|
||||
void wait(PIMutex& lk) override {
|
||||
isWaitCalled = true;
|
||||
}
|
||||
|
||||
void wait(PIMutex& lk, const std::function<bool()>& condition) override {
|
||||
isWaitCalled = true;
|
||||
lk.lock();
|
||||
isTrueCondition = condition();
|
||||
lk.unlock();
|
||||
}
|
||||
|
||||
bool waitFor(PIMutex& lk, int timeoutMs) override {
|
||||
isWaitForCalled = true;
|
||||
timeout = timeoutMs;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool waitFor(PIMutex& lk, int timeoutMs, const std::function<bool()>& condition) override {
|
||||
isWaitForCalled = true;
|
||||
lk.lock();
|
||||
isTrueCondition = condition();
|
||||
timeout = timeoutMs;
|
||||
lk.unlock();
|
||||
return isTrueCondition;
|
||||
}
|
||||
MOCK_METHOD1(wait, void(PIMutex&));
|
||||
MOCK_METHOD2(wait, void(PIMutex&, const std::function<bool()>&));
|
||||
MOCK_METHOD2(waitFor, bool(PIMutex&, int));
|
||||
MOCK_METHOD3(waitFor, bool(PIMutex&, int, const std::function<bool()>&));
|
||||
MOCK_METHOD0(notifyOne, void());
|
||||
};
|
||||
|
||||
TEST(BlockingDequeueUnitTest, put_is_block_when_capacity_reach) {
|
||||
size_t capacity = 0;
|
||||
auto conditionVarAdd = new MockConditionVar();
|
||||
auto conditionVarRem = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
|
||||
dequeue.put(11);
|
||||
ASSERT_TRUE(conditionVarRem->isWaitCalled);
|
||||
ASSERT_FALSE(conditionVarRem->isTrueCondition);
|
||||
struct QueueElement {
|
||||
bool is_empty;
|
||||
int value;
|
||||
int copy_count;
|
||||
|
||||
QueueElement(): is_empty(true), value(0), copy_count(0) { }
|
||||
explicit QueueElement(int value): is_empty(false), value(value), copy_count(0) { }
|
||||
|
||||
QueueElement(const QueueElement& other) {
|
||||
this->is_empty = other.is_empty;
|
||||
this->value = other.value;
|
||||
this->copy_count = 0;
|
||||
const_cast<int&>(other.copy_count)++;
|
||||
}
|
||||
QueueElement(QueueElement&& other) noexcept : QueueElement() {
|
||||
std::swap(is_empty, other.is_empty);
|
||||
std::swap(value, other.value);
|
||||
std::swap(copy_count, other.copy_count);
|
||||
}
|
||||
|
||||
bool operator==(const QueueElement &rhs) const {
|
||||
return is_empty == rhs.is_empty &&
|
||||
value == rhs.value;
|
||||
}
|
||||
|
||||
bool operator!=(const QueueElement &rhs) const {
|
||||
return !(rhs == *this);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const QueueElement& el) {
|
||||
return os << "{ is_empty:" << el.is_empty << ", value:" << el.value << ", copy_count:" << el.copy_count << " }";
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MockDequeBase {
|
||||
public:
|
||||
MOCK_METHOD1_T(push_back_rval, void(T));
|
||||
MOCK_METHOD1_T(push_back, void(const T&));
|
||||
MOCK_METHOD0(size, size_t());
|
||||
MOCK_METHOD0_T(front, T());
|
||||
MOCK_METHOD0(pop_front, void());
|
||||
|
||||
void push_back(T&& t) {
|
||||
push_back_rval(t);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MockDeque: public NiceMock<MockDequeBase<T>> {};
|
||||
|
||||
class PIBlockingDequeuePrepare: public PIBlockingDequeue<QueueElement, MockDeque, NiceMock<MockConditionVar>> {
|
||||
public:
|
||||
typedef PIBlockingDequeue<QueueElement, MockDeque, NiceMock<MockConditionVar>> SuperClass;
|
||||
|
||||
explicit PIBlockingDequeuePrepare(size_t capacity = SIZE_MAX): SuperClass(capacity) { }
|
||||
|
||||
template<typename Iterable,
|
||||
typename std::enable_if<!std::is_arithmetic<Iterable>::value, int>::type = 0>
|
||||
explicit PIBlockingDequeuePrepare(const Iterable& other): SuperClass(other) { }
|
||||
|
||||
MockConditionVar* getCondVarAdd() { return this->cond_var_add; }
|
||||
MockConditionVar* getCondVarRem() { return this->cond_var_rem; }
|
||||
MockDeque<QueueElement>& getQueue() { return this->data_queue; }
|
||||
size_t getMaxSize() { return max_size; }
|
||||
};
|
||||
|
||||
class BlockingDequeueUnitTest: public ::testing::Test {
|
||||
public:
|
||||
int timeout = 100;
|
||||
size_t capacity;
|
||||
PIBlockingDequeuePrepare dequeue;
|
||||
QueueElement element;
|
||||
|
||||
BlockingDequeueUnitTest(): capacity(1), dequeue(capacity), element(11) {}
|
||||
|
||||
void offer2_is_wait_predicate(bool isCapacityReach);
|
||||
void put_is_wait_predicate(bool isCapacityReach);
|
||||
void take_is_wait_predicate(bool isEmpty);
|
||||
};
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, construct_default_is_max_size_eq_size_max) {
|
||||
PIBlockingDequeuePrepare dequeue;
|
||||
ASSERT_EQ(dequeue.getMaxSize(), SIZE_MAX);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, offer_timedout_is_false_when_capacity_reach) {
|
||||
size_t capacity = 0;
|
||||
int timeout = 11;
|
||||
auto conditionVarAdd = new MockConditionVar();
|
||||
auto conditionVarRem = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
|
||||
ASSERT_FALSE(dequeue.offer(11, timeout));
|
||||
TEST_F(BlockingDequeueUnitTest, construct_from_constant_is_max_size_eq_capacity) {
|
||||
PIBlockingDequeuePrepare dequeue(2);
|
||||
ASSERT_EQ(dequeue.getMaxSize(), 2);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, offer_timedout_is_block_when_capacity_reach) {
|
||||
size_t capacity = 0;
|
||||
int timeout = 11;
|
||||
auto conditionVarAdd = new MockConditionVar();
|
||||
auto conditionVarRem = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
|
||||
dequeue.offer(11, timeout);
|
||||
EXPECT_TRUE(conditionVarRem->isWaitForCalled);
|
||||
EXPECT_EQ(timeout, conditionVarRem->timeout);
|
||||
ASSERT_FALSE(conditionVarRem->isTrueCondition);
|
||||
TEST_F(BlockingDequeueUnitTest, construct_from_capacity_is_max_size_eq_capacity) {
|
||||
ASSERT_EQ(dequeue.getMaxSize(), capacity);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, offer_is_true_before_capacity_reach) {
|
||||
size_t capacity = 1;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
ASSERT_TRUE(dequeue.offer(10));
|
||||
TEST_F(BlockingDequeueUnitTest, construct_from_iterable) {
|
||||
std::vector<QueueElement> iterable;
|
||||
iterable.emplace_back(11);
|
||||
iterable.emplace_back(22);
|
||||
PIBlockingDequeuePrepare dequeue(iterable);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, offer_is_false_when_capacity_reach) {
|
||||
size_t capacity = 1;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
dequeue.offer(11);
|
||||
ASSERT_FALSE(dequeue.offer(10));
|
||||
void BlockingDequeueUnitTest::put_is_wait_predicate(bool isCapacityReach) {
|
||||
std::function<bool()> conditionVarPredicate;
|
||||
EXPECT_CALL(*dequeue.getCondVarRem(), wait(_, _))
|
||||
.WillOnce([&](PIMutex& m, const std::function<bool()>& predicate){ conditionVarPredicate = predicate; });
|
||||
dequeue.put(element);
|
||||
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(isCapacityReach ? capacity : capacity - 1));
|
||||
ASSERT_EQ(conditionVarPredicate(), !isCapacityReach);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, put_is_wait_predicate_true) {
|
||||
put_is_wait_predicate(false);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, put_is_wait_predicate_false_when_capacity_reach) {
|
||||
put_is_wait_predicate(true);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, put_is_insert_by_copy) {
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back( Eq(element) ))
|
||||
.WillOnce(Return());
|
||||
dequeue.put(element);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, put_is_insert_by_move) {
|
||||
QueueElement copyElement = element;
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back_rval( Eq(element) ))
|
||||
.WillOnce(Return());
|
||||
dequeue.put(std::move(copyElement));
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, put_is_notify_about_insert) {
|
||||
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
|
||||
.WillOnce(Return());
|
||||
dequeue.put(element);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_insert_by_copy) {
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back( Eq(element) ))
|
||||
.WillOnce(Return());
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity - 1));
|
||||
dequeue.offer(element);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_insert_by_move) {
|
||||
QueueElement copyElement = element;
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back_rval( Eq(element) ))
|
||||
.WillOnce(Return());
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity - 1));
|
||||
dequeue.offer(std::move(copyElement));
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_not_insert_when_capacity_reach) {
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back(_))
|
||||
.Times(0);
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity));
|
||||
dequeue.offer(element);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_true_when_insert) {
|
||||
ON_CALL(dequeue.getQueue(), push_back(_))
|
||||
.WillByDefault(Return());
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity - 1));
|
||||
ASSERT_TRUE(dequeue.offer(element));
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_false_when_capacity_reach) {
|
||||
ON_CALL(dequeue.getQueue(), push_back(_))
|
||||
.WillByDefault(Return());
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity));
|
||||
ASSERT_FALSE(dequeue.offer(element));
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_notify_about_insert) {
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity - 1));
|
||||
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
|
||||
.WillOnce(Return());
|
||||
dequeue.offer(element);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer1_is_not_notify_about_insert_when_capacity_reach) {
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(capacity));
|
||||
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
|
||||
.Times(0);
|
||||
dequeue.offer(element);
|
||||
}
|
||||
|
||||
void BlockingDequeueUnitTest::offer2_is_wait_predicate(bool isCapacityReach) {
|
||||
std::function<bool()> conditionVarPredicate;
|
||||
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
|
||||
.WillOnce([&](PIMutex& m, int timeout, const std::function<bool()>& predicate) {
|
||||
conditionVarPredicate = predicate;
|
||||
return isCapacityReach;
|
||||
});
|
||||
dequeue.offer(element, timeout);
|
||||
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(isCapacityReach ? capacity : capacity - 1));
|
||||
ASSERT_EQ(conditionVarPredicate(), !isCapacityReach);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_wait_predicate_true) {
|
||||
offer2_is_wait_predicate(false);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_wait_predicate_false_when_capacity_reach) {
|
||||
offer2_is_wait_predicate(true);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_insert_by_copy) {
|
||||
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back( Eq(element) ))
|
||||
.WillOnce(Return());
|
||||
dequeue.offer(element, timeout);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_insert_by_move) {
|
||||
QueueElement copyElement = element;
|
||||
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back_rval( Eq(element) ))
|
||||
.WillOnce(Return());
|
||||
dequeue.offer(std::move(copyElement), timeout);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_not_insert_when_timeout) {
|
||||
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
|
||||
.WillOnce(Return(false));
|
||||
EXPECT_CALL(dequeue.getQueue(), push_back(_))
|
||||
.Times(0);
|
||||
dequeue.offer(element, timeout);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_true_when_insert) {
|
||||
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
|
||||
.WillByDefault(Return(true));
|
||||
ASSERT_TRUE(dequeue.offer(element, timeout));
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_false_when_timeout) {
|
||||
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
|
||||
.WillByDefault(Return(false));
|
||||
ASSERT_FALSE(dequeue.offer(element, timeout));
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_notify_about_insert) {
|
||||
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
|
||||
.WillByDefault(Return(true));
|
||||
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
|
||||
.WillOnce(Return());
|
||||
dequeue.offer(element, timeout);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, offer2_is_not_notify_about_insert_when_timeout) {
|
||||
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
|
||||
.WillByDefault(Return(false));
|
||||
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
|
||||
.Times(0);
|
||||
dequeue.offer(element, timeout);
|
||||
}
|
||||
|
||||
void BlockingDequeueUnitTest::take_is_wait_predicate(bool isEmpty) {
|
||||
std::function<bool()> conditionVarPredicate;
|
||||
EXPECT_CALL(*dequeue.getCondVarAdd(), wait(_, _))
|
||||
.WillOnce([&](PIMutex& m, const std::function<bool()>& predicate) { conditionVarPredicate = predicate; });
|
||||
dequeue.take();
|
||||
|
||||
ON_CALL(dequeue.getQueue(), size)
|
||||
.WillByDefault(Return(isEmpty ? 0 : 1));
|
||||
ASSERT_EQ(conditionVarPredicate(), !isEmpty);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, take_is_wait_predicate_true) {
|
||||
take_is_wait_predicate(false);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, take_is_wait_predicate_false_when_queue_empty) {
|
||||
take_is_wait_predicate(true);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, take_is_get_and_remove) {
|
||||
Expectation front = EXPECT_CALL(dequeue.getQueue(), front())
|
||||
.WillOnce(Return(element));
|
||||
EXPECT_CALL(dequeue.getQueue(), pop_front())
|
||||
.After(front)
|
||||
.WillOnce(Return());
|
||||
|
||||
QueueElement takenElement = dequeue.take();
|
||||
ASSERT_EQ(element, takenElement);
|
||||
}
|
||||
|
||||
TEST_F(BlockingDequeueUnitTest, take_is_notify_about_remove) {
|
||||
EXPECT_CALL(*dequeue.getCondVarRem(), notifyOne)
|
||||
.WillOnce(Return());
|
||||
dequeue.take();
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO change take_is_block_when_empty to prevent segfault
|
||||
TEST(DISABLED_BlockingDequeueUnitTest, take_is_block_when_empty) {
|
||||
size_t capacity = 1;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
// May cause segfault because take front of empty queue
|
||||
dequeue.take();
|
||||
EXPECT_TRUE(conditionVar->isWaitCalled);
|
||||
ASSERT_FALSE(conditionVar->isTrueCondition);
|
||||
EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitCalled);
|
||||
ASSERT_FALSE(dequeue.getCondVarAdd()->isTrueCondition);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, take_is_not_block_when_not_empty) {
|
||||
size_t capacity = 1;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
dequeue.take();
|
||||
|
||||
EXPECT_TRUE(conditionVar->isWaitCalled);
|
||||
ASSERT_TRUE(conditionVar->isTrueCondition);
|
||||
EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitCalled);
|
||||
ASSERT_TRUE(dequeue.getCondVarAdd()->isTrueCondition);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
|
||||
size_t capacity = 1;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.take(), 111);
|
||||
@@ -112,8 +365,7 @@ TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
|
||||
|
||||
TEST(BlockingDequeueUnitTest, take_is_last) {
|
||||
size_t capacity = 10;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
EXPECT_TRUE(dequeue.offer(111));
|
||||
EXPECT_TRUE(dequeue.offer(222));
|
||||
ASSERT_EQ(dequeue.take(), 111);
|
||||
@@ -123,25 +375,22 @@ TEST(BlockingDequeueUnitTest, take_is_last) {
|
||||
TEST(BlockingDequeueUnitTest, poll_is_not_block_when_empty) {
|
||||
size_t capacity = 1;
|
||||
bool isOk;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.poll(111, &isOk);
|
||||
EXPECT_FALSE(conditionVar->isWaitForCalled);
|
||||
EXPECT_FALSE(dequeue.getCondVarAdd()->isWaitForCalled);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, poll_is_default_value_when_empty) {
|
||||
size_t capacity = 1;
|
||||
bool isOk;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
ASSERT_EQ(dequeue.poll(111, &isOk), 111);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
|
||||
size_t capacity = 1;
|
||||
bool isOk;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.poll(-1, &isOk), 111);
|
||||
}
|
||||
@@ -149,47 +398,42 @@ TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
|
||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_block_when_empty) {
|
||||
size_t capacity = 1;
|
||||
int timeout = 11;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.poll(timeout, 111);
|
||||
EXPECT_TRUE(conditionVar->isWaitForCalled);
|
||||
EXPECT_EQ(timeout, conditionVar->timeout);
|
||||
ASSERT_FALSE(conditionVar->isTrueCondition);
|
||||
EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitForCalled);
|
||||
EXPECT_EQ(timeout, dequeue.getCondVarAdd()->timeout);
|
||||
ASSERT_FALSE(dequeue.getCondVarAdd()->isTrueCondition);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_default_value_when_empty) {
|
||||
size_t capacity = 1;
|
||||
int timeout = 11;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
ASSERT_EQ(dequeue.poll(timeout, 111), 111);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_not_block_when_not_empty) {
|
||||
size_t capacity = 1;
|
||||
int timeout = 11;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
dequeue.poll(timeout, -1);
|
||||
|
||||
EXPECT_TRUE(conditionVar->isWaitForCalled);
|
||||
ASSERT_TRUE(conditionVar->isTrueCondition);
|
||||
EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitForCalled);
|
||||
ASSERT_TRUE(dequeue.getCondVarAdd()->isTrueCondition);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_offer_value_when_not_empty) {
|
||||
size_t capacity = 1;
|
||||
int timeout = 11;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.poll(timeout, -1), 111);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
|
||||
size_t capacity = 10;
|
||||
auto conditionVar = new MockConditionVar();
|
||||
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
dequeue.offer(222);
|
||||
ASSERT_EQ(dequeue.poll(10, -1), 111);
|
||||
@@ -198,13 +442,13 @@ TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
|
||||
|
||||
TEST(BlockingDequeueUnitTest, capacity_is_eq_constructor_capacity) {
|
||||
size_t capacity = 10;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
ASSERT_EQ(dequeue.capacity(), capacity);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
|
||||
size_t capacity = 2;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
ASSERT_EQ(dequeue.remainingCapacity(), capacity);
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.remainingCapacity(), capacity - 1);
|
||||
@@ -212,7 +456,7 @@ TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
|
||||
|
||||
TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
|
||||
size_t capacity = 1;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.remainingCapacity(), 0);
|
||||
@@ -220,7 +464,7 @@ TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
|
||||
|
||||
TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
|
||||
size_t capacity = 1;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
ASSERT_EQ(dequeue.size(), 0);
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.size(), 1);
|
||||
@@ -228,7 +472,7 @@ TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
|
||||
|
||||
TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
|
||||
size_t capacity = 1;
|
||||
PIBlockingDequeue<int> dequeue(capacity);
|
||||
PIBlockingDequeuePrepare<int> dequeue(capacity);
|
||||
dequeue.offer(111);
|
||||
dequeue.offer(111);
|
||||
ASSERT_EQ(dequeue.size(), capacity);
|
||||
@@ -236,29 +480,31 @@ TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
|
||||
|
||||
TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) {
|
||||
size_t capacity = 10;
|
||||
PIDeque<int> refDeque;
|
||||
std::deque<int> refDeque;
|
||||
for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10);
|
||||
PIBlockingDequeue<int> blockingDequeue(refDeque);
|
||||
PIDeque<int> deque;
|
||||
PIBlockingDequeuePrepare<int> blockingDequeue(refDeque);
|
||||
PIBlockingDequeuePrepare<int>::QueueType deque;
|
||||
blockingDequeue.drainTo(deque);
|
||||
ASSERT_EQ(blockingDequeue.size(), 0);
|
||||
ASSERT_TRUE(deque == refDeque);
|
||||
// FIXME
|
||||
// ASSERT_TRUE(deque == refDeque);
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_size_when_all_moved) {
|
||||
size_t capacity = 10;
|
||||
PIDeque<int> refDeque;
|
||||
std::deque<int> refDeque;
|
||||
for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10);
|
||||
PIBlockingDequeue<int> blockingDequeue(refDeque);
|
||||
PIDeque<int> deque;
|
||||
PIBlockingDequeuePrepare<int> blockingDequeue(refDeque);
|
||||
PIBlockingDequeuePrepare<int>::QueueType deque;
|
||||
ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size());
|
||||
}
|
||||
|
||||
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) {
|
||||
size_t capacity = 10;
|
||||
PIDeque<int> refDeque;
|
||||
std::deque<int> refDeque;
|
||||
for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10);
|
||||
PIBlockingDequeue<int> blockingDequeue(refDeque);
|
||||
PIDeque<int> deque;
|
||||
PIBlockingDequeuePrepare<int> blockingDequeue(refDeque);
|
||||
PIBlockingDequeuePrepare<int>::QueueType deque;
|
||||
ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "pithread.h"
|
||||
#include "testutil.h"
|
||||
|
||||
class ConditionVariable : public ::testing::Test, public TestUtil {
|
||||
class ConditionVariableIntegrationTest : public ::testing::Test, public TestUtil {
|
||||
public:
|
||||
PIMutex m;
|
||||
PIConditionVariable* variable;
|
||||
@@ -19,30 +19,30 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_block) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_block) {
|
||||
createThread();
|
||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_block_when_notifyOne_before_wait) {
|
||||
variable->notifyOne();
|
||||
createThread();
|
||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_block_when_notifyAll_before_wait) {
|
||||
variable->notifyAll();
|
||||
createThread();
|
||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_unblock_when_notifyOne_after_wait) {
|
||||
createThread();
|
||||
variable->notifyOne();
|
||||
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_unblock_when_notifyAll_after_wait) {
|
||||
PIVector<PIThread*> threads;
|
||||
|
||||
for (int i = 0; i < THREAD_COUNT; ++i) {
|
||||
@@ -61,7 +61,7 @@ TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
|
||||
piForeach(PIThread* thread, threads) delete thread;
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_one_unblock_when_notifyOne) {
|
||||
PIVector<PIThread*> threads;
|
||||
|
||||
for (int i = 0; i < THREAD_COUNT; ++i) {
|
||||
@@ -77,7 +77,7 @@ TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
|
||||
ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1);
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_is_protected_unblock_when_notifyOne) {
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
variable->wait(m);
|
||||
@@ -89,7 +89,7 @@ TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
|
||||
ASSERT_FALSE(m.tryLock());
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_condition_is_block) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_condition_is_block) {
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
variable->wait(m, [](){ return false; });
|
||||
@@ -98,7 +98,7 @@ TEST_F(ConditionVariable, wait_condition_is_block) {
|
||||
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_condition_is_check_condition_before_block) {
|
||||
bool isConditionChecked = false;
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
@@ -113,7 +113,7 @@ TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
|
||||
m.unlock();
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_condition_is_check_condition_when_notifyOne) {
|
||||
bool isConditionChecked;
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
@@ -133,7 +133,7 @@ TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
|
||||
m.unlock();
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) {
|
||||
TEST_F(ConditionVariableIntegrationTest, wait_condition_is_unblock_when_condition_and_notifyOne) {
|
||||
bool condition = false;
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
@@ -147,7 +147,7 @@ TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne
|
||||
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
|
||||
TEST_F(ConditionVariableIntegrationTest, DISABLED_waitFor_is_block_before_timeout) {
|
||||
createThread([&](){
|
||||
PITimeMeasurer measurer;
|
||||
m.lock();
|
||||
@@ -159,7 +159,7 @@ TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
|
||||
EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3));
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
|
||||
TEST_F(ConditionVariableIntegrationTest, waitFor_is_unblock_when_timeout) {
|
||||
std::atomic_bool isUnblock(false);
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
@@ -172,7 +172,7 @@ TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
|
||||
ASSERT_TRUE(isUnblock);
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
|
||||
TEST_F(ConditionVariableIntegrationTest, waitFor_is_false_when_timeout) {
|
||||
bool waitRet = true;
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
@@ -183,7 +183,7 @@ TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
|
||||
ASSERT_FALSE(waitRet);
|
||||
}
|
||||
|
||||
TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) {
|
||||
TEST_F(ConditionVariableIntegrationTest, waitFor_is_unblock_when_condition_and_notifyOne) {
|
||||
bool condition = false;
|
||||
createThread([&](){
|
||||
m.lock();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "pithreadpoolexecutor.h"
|
||||
#include "testutil.h"
|
||||
#include "pimutex.h"
|
||||
|
||||
const int WAIT_THREAD_TIME_MS = 30;
|
||||
#include "piexecutor.h"
|
||||
|
||||
TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) {
|
||||
PIMutex m;
|
||||
@@ -14,11 +13,13 @@ TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) {
|
||||
m.unlock();
|
||||
});
|
||||
piMSleep(WAIT_THREAD_TIME_MS);
|
||||
m.lock();
|
||||
ASSERT_EQ(invokedRunnables, 1);
|
||||
m.unlock();
|
||||
}
|
||||
|
||||
TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) {
|
||||
bool isRunnableInvoke = false;
|
||||
volatile bool isRunnableInvoke = false;
|
||||
PIThreadPoolExecutor executorService(1);
|
||||
executorService.shutdown();
|
||||
executorService.execute([&]() {
|
||||
@@ -29,7 +30,7 @@ TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) {
|
||||
}
|
||||
|
||||
TEST(ExcutorIntegrationTest, execute_is_execute_before_shutdown) {
|
||||
bool isRunnableInvoke = false;
|
||||
volatile bool isRunnableInvoke = false;
|
||||
PIThreadPoolExecutor executorService(1);
|
||||
executorService.execute([&]() {
|
||||
piMSleep(WAIT_THREAD_TIME_MS);
|
||||
|
||||
131
tests/concurrent/ExecutorUnitTest.cpp
Normal file
131
tests/concurrent/ExecutorUnitTest.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "testutil.h"
|
||||
#include "piexecutor.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::SetArgReferee;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::DeleteArg;
|
||||
using ::testing::Return;
|
||||
using ::testing::ByMove;
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::ByRef;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Ge;
|
||||
using ::testing::Pointee;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NiceMock;
|
||||
|
||||
typedef std::function<void()> VoidFunc;
|
||||
|
||||
namespace std {
|
||||
inline bool operator ==(const VoidFunc& s, const VoidFunc& v) {
|
||||
// TODO VoidFunc operator ==
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class MockThread {
|
||||
public:
|
||||
VoidFunc runnnable;
|
||||
|
||||
MockThread(VoidFunc runnnable) : runnnable(runnnable) { }
|
||||
|
||||
MOCK_METHOD0(start, bool());
|
||||
MOCK_METHOD0(stop, void());
|
||||
MOCK_METHOD1(waitForStart, bool(int timeout_msecs));
|
||||
MOCK_METHOD1(waitForFinish, bool(int timeout_msecs));
|
||||
};
|
||||
|
||||
class MockDeque : public PIBlockingDequeue<FunctionWrapper> {
|
||||
public:
|
||||
MOCK_METHOD1(offer, bool(const FunctionWrapper&));
|
||||
MOCK_METHOD0(take, FunctionWrapper());
|
||||
MOCK_METHOD1(poll, FunctionWrapper(int));
|
||||
MOCK_METHOD0(capacity, size_t());
|
||||
MOCK_METHOD0(remainingCapacity, size_t());
|
||||
};
|
||||
|
||||
typedef PIThreadPoolExecutorTemplate<NiceMock<MockThread>, MockDeque> PIThreadPoolExecutorMoc_t;
|
||||
|
||||
class PIThreadPoolExecutorMoc : public PIThreadPoolExecutorMoc_t {
|
||||
public:
|
||||
explicit PIThreadPoolExecutorMoc(size_t corePoolSize) : PIThreadPoolExecutorMoc_t(corePoolSize) { }
|
||||
|
||||
template<typename Function>
|
||||
explicit PIThreadPoolExecutorMoc(size_t corePoolSize, Function onBeforeStart) : PIThreadPoolExecutorMoc_t(corePoolSize, onBeforeStart) { }
|
||||
|
||||
PIVector<testing::NiceMock<MockThread>*>* getThreadPool() { return &threadPool; }
|
||||
bool isShutdown() { return isShutdown_; }
|
||||
MockDeque* getTaskQueue() { return &taskQueue; }
|
||||
};
|
||||
|
||||
TEST(ExecutorUnitTest, is_corePool_created) {
|
||||
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
|
||||
ASSERT_EQ(THREAD_COUNT, executor.getThreadPool()->size());
|
||||
}
|
||||
|
||||
TEST(ExecutorUnitTest, is_corePool_started) {
|
||||
PIThreadPoolExecutorMoc executor(THREAD_COUNT, [](MockThread* thread){
|
||||
EXPECT_CALL(*thread, start())
|
||||
.WillOnce(Return(true));
|
||||
});
|
||||
EXPECT_EQ(THREAD_COUNT, executor.getThreadPool()->size());
|
||||
}
|
||||
|
||||
TEST(ExecutorUnitTest, submit_is_added_to_taskQueue) {
|
||||
VoidFunc voidFunc = [](){};
|
||||
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
|
||||
// TODO add check of offered
|
||||
EXPECT_CALL(*executor.getTaskQueue(), offer)
|
||||
.WillOnce(Return(true));
|
||||
executor.submit(voidFunc);
|
||||
}
|
||||
|
||||
TEST(ExecutorUnitTest, submit_is_return_valid_future) {
|
||||
VoidFunc voidFunc = [](){};
|
||||
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
|
||||
// TODO add check of offered
|
||||
EXPECT_CALL(*executor.getTaskQueue(), offer)
|
||||
.WillOnce(Return(true));
|
||||
auto future = executor.submit(voidFunc);
|
||||
EXPECT_TRUE(future.valid());
|
||||
}
|
||||
|
||||
TEST(ExecutorUnitTest, execute_is_added_to_taskQueue) {
|
||||
VoidFunc voidFunc = [](){};
|
||||
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
|
||||
// TODO add check of offered
|
||||
EXPECT_CALL(*executor.getTaskQueue(), offer)
|
||||
.WillOnce(Return(true));
|
||||
executor.execute(voidFunc);
|
||||
}
|
||||
|
||||
TEST(ExecutorUnitTest, is_corePool_execute_queue_elements) {
|
||||
bool is_executed = false;
|
||||
PIThreadPoolExecutorMoc executor(1);
|
||||
EXPECT_EQ(executor.getThreadPool()->size(), 1);
|
||||
EXPECT_CALL(*executor.getTaskQueue(), poll(Ge(0)))
|
||||
.WillOnce([&is_executed](int){
|
||||
return FunctionWrapper([&is_executed](){ is_executed = true; });
|
||||
});
|
||||
executor.getThreadPool()->at(0)->runnnable();
|
||||
ASSERT_TRUE(is_executed);
|
||||
}
|
||||
|
||||
TEST(ExecutorUnitTest, shutdown_is_stop_threads) {
|
||||
// Exclude stop calls when executor deleting
|
||||
auto* executor = new PIThreadPoolExecutorMoc(THREAD_COUNT, [](MockThread* thread){
|
||||
testing::Mock::AllowLeak(thread);
|
||||
EXPECT_CALL(*thread, stop())
|
||||
.WillOnce(Return());
|
||||
});
|
||||
testing::Mock::AllowLeak(executor);
|
||||
testing::Mock::AllowLeak(executor->getTaskQueue());
|
||||
|
||||
EXPECT_CALL(*executor->getTaskQueue(), poll(Ge(0)))
|
||||
.WillRepeatedly([](int){ return FunctionWrapper(); });
|
||||
executor->shutdown();
|
||||
executor->getThreadPool()->forEach([](MockThread* thread){ thread->runnnable(); });
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "piconditionvar.h"
|
||||
#include "pithread.h"
|
||||
#include "testutil.h"
|
||||
|
||||
class ConditionLock : public ::testing::Test, public TestUtil {
|
||||
class MutexIntegartionTest : public ::testing::Test, public TestUtil {
|
||||
public:
|
||||
PIMutex* m = new PIMutex();
|
||||
};
|
||||
|
||||
TEST_F(ConditionLock, lock_is_protect) {
|
||||
TEST_F(MutexIntegartionTest, lock_is_protect) {
|
||||
m->lock();
|
||||
bool* isProtect = new bool(true);
|
||||
|
||||
@@ -22,7 +20,7 @@ TEST_F(ConditionLock, lock_is_protect) {
|
||||
ASSERT_TRUE(*isProtect);
|
||||
}
|
||||
|
||||
TEST_F(ConditionLock, unlock_is_release) {
|
||||
TEST_F(MutexIntegartionTest, unlock_is_release) {
|
||||
m->lock();
|
||||
bool* isReleased = new bool(false);
|
||||
m->unlock();
|
||||
@@ -35,7 +33,7 @@ TEST_F(ConditionLock, unlock_is_release) {
|
||||
ASSERT_TRUE(*isReleased);
|
||||
}
|
||||
|
||||
TEST_F(ConditionLock, tryLock_is_false_when_locked) {
|
||||
TEST_F(MutexIntegartionTest, tryLock_is_false_when_locked) {
|
||||
createThread([&](){
|
||||
m->lock();
|
||||
piMSleep(WAIT_THREAD_TIME_MS);
|
||||
@@ -43,11 +41,11 @@ TEST_F(ConditionLock, tryLock_is_false_when_locked) {
|
||||
ASSERT_FALSE(m->tryLock());
|
||||
}
|
||||
|
||||
TEST_F(ConditionLock, tryLock_is_true_when_unlocked) {
|
||||
TEST_F(MutexIntegartionTest, tryLock_is_true_when_unlocked) {
|
||||
ASSERT_TRUE(m->tryLock());
|
||||
}
|
||||
|
||||
TEST_F(ConditionLock, tryLock_is_recursive_lock_enable) {
|
||||
TEST_F(MutexIntegartionTest, tryLock_is_recursive_lock_enable) {
|
||||
m->lock();
|
||||
ASSERT_TRUE(m->tryLock());
|
||||
}
|
||||
@@ -1,60 +1,68 @@
|
||||
#ifndef AWRCANFLASHER_TESTUTIL_H
|
||||
#define AWRCANFLASHER_TESTUTIL_H
|
||||
|
||||
#include "pithread.h"
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests
|
||||
* write "Start thread timeout reach!" message. You can reduce it if you want increase test performance.
|
||||
*/
|
||||
const int WAIT_THREAD_TIME_MS = 40;
|
||||
|
||||
const int THREAD_COUNT = 5;
|
||||
|
||||
class TestUtil: public PIObject {
|
||||
PIOBJECT(TestUtil)
|
||||
public:
|
||||
double threadStartTime;
|
||||
PIThread* thread = new PIThread();
|
||||
std::atomic_bool isRunning;
|
||||
std::function<void()> adapterFunctionDefault;
|
||||
|
||||
TestUtil() : isRunning(false) {}
|
||||
|
||||
bool createThread(const std::function<void()>& fun = nullptr, PIThread* thread_ = nullptr) {
|
||||
std::function<void()> actualFun = fun == nullptr ? adapterFunctionDefault : fun;
|
||||
if (thread_ == nullptr) thread_ = thread;
|
||||
thread_->startOnce([=](void*){
|
||||
isRunning = true;
|
||||
actualFun();
|
||||
});
|
||||
return waitThread(thread_);
|
||||
}
|
||||
|
||||
bool waitThread(PIThread* thread_, bool runningStatus = true) {
|
||||
PITimeMeasurer measurer;
|
||||
bool isTimeout = !thread_->waitForStart(WAIT_THREAD_TIME_MS);
|
||||
while (!isRunning) {
|
||||
isTimeout = WAIT_THREAD_TIME_MS <= measurer.elapsed_m();
|
||||
if (isTimeout) break;
|
||||
piUSleep(100);
|
||||
}
|
||||
|
||||
threadStartTime = measurer.elapsed_m();
|
||||
|
||||
if (isTimeout) piCout << "Start thread timeout reach!";
|
||||
|
||||
if (threadStartTime > 1) {
|
||||
piCout << "Start time" << threadStartTime << "ms";
|
||||
} else if (threadStartTime > 0.001) {
|
||||
piCout << "Start time" << threadStartTime * 1000 << "mcs";
|
||||
} else {
|
||||
piCout << "Start time" << threadStartTime * 1000 * 1000 << "ns";
|
||||
}
|
||||
|
||||
return !isTimeout;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //AWRCANFLASHER_TESTUTIL_H
|
||||
#ifndef AWRCANFLASHER_TESTUTIL_H
|
||||
#define AWRCANFLASHER_TESTUTIL_H
|
||||
|
||||
#include "pithread.h"
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
void print_type_info() {
|
||||
std::cout << typeid(T).name() << " is a "
|
||||
<< (std::is_const<typename std::remove_reference<T>::type>::value ? "const " : "")
|
||||
<< (std::is_lvalue_reference<T>::value ? "lvalue" : "rvalue")
|
||||
<< " reference" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests
|
||||
* write "Start thread timeout reach!" message. You can reduce it if you want increase test performance.
|
||||
*/
|
||||
const int WAIT_THREAD_TIME_MS = 30;
|
||||
|
||||
const int THREAD_COUNT = 2;
|
||||
|
||||
class TestUtil: public PIObject {
|
||||
PIOBJECT(TestUtil)
|
||||
public:
|
||||
double threadStartTime;
|
||||
PIThread* thread = new PIThread();
|
||||
std::atomic_bool isRunning;
|
||||
std::function<void()> adapterFunctionDefault;
|
||||
|
||||
TestUtil() : isRunning(false) {}
|
||||
|
||||
bool createThread(const std::function<void()>& fun = nullptr, PIThread* thread_ = nullptr) {
|
||||
std::function<void()> actualFun = fun == nullptr ? adapterFunctionDefault : fun;
|
||||
if (thread_ == nullptr) thread_ = thread;
|
||||
thread_->startOnce([=](void*){
|
||||
isRunning = true;
|
||||
actualFun();
|
||||
});
|
||||
return waitThread(thread_);
|
||||
}
|
||||
|
||||
bool waitThread(PIThread* thread_, bool runningStatus = true) {
|
||||
PITimeMeasurer measurer;
|
||||
bool isTimeout = !thread_->waitForStart(WAIT_THREAD_TIME_MS);
|
||||
while (!isRunning) {
|
||||
isTimeout = WAIT_THREAD_TIME_MS <= measurer.elapsed_m();
|
||||
if (isTimeout) break;
|
||||
piUSleep(100);
|
||||
}
|
||||
|
||||
threadStartTime = measurer.elapsed_m();
|
||||
|
||||
if (isTimeout) piCout << "Start thread timeout reach!";
|
||||
|
||||
if (threadStartTime > 1) {
|
||||
piCout << "Start time" << threadStartTime << "ms";
|
||||
} else if (threadStartTime > 0.001) {
|
||||
piCout << "Start time" << threadStartTime * 1000 << "mcs";
|
||||
} else {
|
||||
piCout << "Start time" << threadStartTime * 1000 * 1000 << "ns";
|
||||
}
|
||||
|
||||
return !isTimeout;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //AWRCANFLASHER_TESTUTIL_H
|
||||
|
||||
28
utils/cloud_dispatcher/dispatcherclient.cpp
Normal file
28
utils/cloud_dispatcher/dispatcherclient.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "dispatcherclient.h"
|
||||
|
||||
|
||||
DispatcherClient::DispatcherClient(PIEthernet * eth_) {
|
||||
eth = eth_;
|
||||
eth->startThreadedRead();
|
||||
CONNECTU(eth, threadedReadEvent, this, readed);
|
||||
CONNECTU(eth, disconnected, this, disconnected);
|
||||
piCoutObj << "client connected" << eth->sendAddress();
|
||||
}
|
||||
|
||||
|
||||
DispatcherClient::~DispatcherClient() {
|
||||
}
|
||||
|
||||
|
||||
void DispatcherClient::disconnected(bool withError) {
|
||||
piCoutObj << "client disconnected" << eth->sendAddress();
|
||||
disconnectEvent(this);
|
||||
}
|
||||
|
||||
|
||||
void DispatcherClient::readed(uchar *data, int size) {
|
||||
PIByteArray ba(data, size);
|
||||
piCoutObj << "readed" << ba.toHex();
|
||||
eth->write(ba);
|
||||
}
|
||||
|
||||
21
utils/cloud_dispatcher/dispatcherclient.h
Normal file
21
utils/cloud_dispatcher/dispatcherclient.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef DISPATCHERCLIENT_H
|
||||
#define DISPATCHERCLIENT_H
|
||||
|
||||
#include "piethernet.h"
|
||||
|
||||
|
||||
class DispatcherClient: public PIObject {
|
||||
PIOBJECT(DispatcherClient)
|
||||
public:
|
||||
DispatcherClient(PIEthernet * eth_);
|
||||
~DispatcherClient();
|
||||
EVENT_HANDLER2(void, readed, uchar * , data, int, size);
|
||||
EVENT_HANDLER1(void, disconnected, bool, withError);
|
||||
EVENT1(disconnectEvent, DispatcherClient *, client)
|
||||
|
||||
private:
|
||||
PIEthernet * eth;
|
||||
};
|
||||
|
||||
|
||||
#endif // DISPATCHERCLIENT_H
|
||||
32
utils/cloud_dispatcher/dispatcherserver.cpp
Normal file
32
utils/cloud_dispatcher/dispatcherserver.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "dispatcherserver.h"
|
||||
|
||||
|
||||
DispatcherServer::DispatcherServer(PIEthernet::Address addr) {
|
||||
eth = new PIEthernet(PIEthernet::TCP_Server);
|
||||
eth->setParameter(PIEthernet::ReuseAddress);
|
||||
CONNECTU(eth, newConnection, this, newConnection);
|
||||
eth->listen(addr, true);
|
||||
piCoutObj << eth << "server started" << addr;
|
||||
}
|
||||
|
||||
|
||||
DispatcherServer::~DispatcherServer() {
|
||||
eth->close();
|
||||
piCoutObj << "server stoped";
|
||||
delete eth;
|
||||
}
|
||||
|
||||
|
||||
void DispatcherServer::disconnectClient(DispatcherClient *client) {
|
||||
piCoutObj << "remove client" << client;
|
||||
clients.removeOne(client);
|
||||
delete client;
|
||||
}
|
||||
|
||||
|
||||
void DispatcherServer::newConnection(PIEthernet *cl) {
|
||||
DispatcherClient * client = new DispatcherClient(cl);
|
||||
piCoutObj << "add client" << client;
|
||||
CONNECTU(client, disconnectEvent, this, disconnectClient);
|
||||
clients.push_back(client);
|
||||
}
|
||||
20
utils/cloud_dispatcher/dispatcherserver.h
Normal file
20
utils/cloud_dispatcher/dispatcherserver.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef DISPATCHERSERVER_H
|
||||
#define DISPATCHERSERVER_H
|
||||
|
||||
#include "dispatcherclient.h"
|
||||
|
||||
|
||||
class DispatcherServer: public PIObject {
|
||||
PIOBJECT(DispatcherServer)
|
||||
public:
|
||||
DispatcherServer(PIEthernet::Address addr);
|
||||
~DispatcherServer();
|
||||
EVENT_HANDLER1(void, newConnection, PIEthernet * , cl);
|
||||
EVENT_HANDLER1(void, disconnectClient, DispatcherClient *, client);
|
||||
|
||||
private:
|
||||
PIEthernet * eth;
|
||||
PIVector<DispatcherClient*> clients;
|
||||
};
|
||||
|
||||
#endif // DISPATCHERSERVER_H
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
#include "pip.h"
|
||||
#include "picrypt.h"
|
||||
#include "dispatcherserver.h"
|
||||
|
||||
|
||||
using namespace PICoutManipulators;
|
||||
|
||||
PIString ip = "0.0.0.0";
|
||||
int port = 10101;
|
||||
PIEthernet::Address addr = PIEthernet::Address("0.0.0.0", 10101);
|
||||
|
||||
void usage() {
|
||||
piCout << Bold << "PIP Cloud Dispatcher";
|
||||
@@ -49,10 +49,11 @@ int main (int argc, char * argv[]) {
|
||||
return 0;
|
||||
}
|
||||
if (cli.hasArgument("ip"))
|
||||
ip = cli.argumentValue("ip");
|
||||
addr.setIP(cli.argumentValue("ip"));
|
||||
if (cli.hasArgument("port"))
|
||||
port = cli.argumentValue("port").toInt();
|
||||
|
||||
addr.setPort(cli.argumentValue("port").toInt());
|
||||
DispatcherServer server(addr);
|
||||
WAIT_FOR_EXIT
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,8 @@ void makeClassInfo(PIFile & f, const PICodeParser::Entity * e) {
|
||||
f << "\tci->name = \"" << e->name << "\";\n";
|
||||
f << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n";
|
||||
if (!e->meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = e->meta.begin(); i != e->meta.end(); ++i)
|
||||
auto i = e->meta.makeIterator();
|
||||
while (i.next())
|
||||
f << "\tci->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
f << "\t(*classesInfo)[ci->name] = ci;\n";
|
||||
@@ -176,14 +177,16 @@ void makeEnumInfo(PIFile & f, const PICodeParser::Enum * e) {
|
||||
f << "\t(*enumsInfo)[\"" << e->name << "\"] = ei;\n";
|
||||
f << "\tei->name = \"" << e->name << "\";\n";
|
||||
if (!e->meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = e->meta.begin(); i != e->meta.end(); ++i)
|
||||
auto i = e->meta.makeIterator();
|
||||
while (i.next())
|
||||
f << "\tei->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
piForeachC (PICodeParser::EnumeratorInfo & m, e->members) {
|
||||
f << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n";
|
||||
if (!m.meta.isEmpty()) {
|
||||
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i)
|
||||
auto i = m.meta.makeIterator();
|
||||
while (i.next())
|
||||
f << "\tei->members.back().meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,8 +583,9 @@ int main(int argc, char * argv[]) {
|
||||
qt_filters["platforms"] = platforms;
|
||||
qt_filters["styles" ] = styles ;
|
||||
|
||||
for (PIMap<PIString, PIStringList>::iterator it = qt_filters.begin(); it != qt_filters.end(); ++it)
|
||||
it.value().forEachInplace([](PIString i)->PIString{
|
||||
auto it = qt_filters.makeIterator();
|
||||
while (it.next())
|
||||
it.valueRef().forEachInplace([](PIString i)->PIString{
|
||||
if (!i.startsWith("*")) i.prepend("*");
|
||||
if (!i.endsWith("*")) i.append("*");
|
||||
return i;
|
||||
|
||||
Reference in New Issue
Block a user