otsdaq  v2_01_00
StringMacros.cc
1 #include "otsdaq-core/Macros/StringMacros.h"
2 
3 
4 using namespace ots;
5 
6 //==============================================================================
7 //wildCardMatch
8 // find needle in haystack
9 // allow needle to have leading and/or trailing wildcard '*'
10 // consider priority in matching, no mater the order in the haystack:
11 // - 0: no match!
12 // - 1: highest priority is exact match
13 // - 2: next highest is partial TRAILING-wildcard match
14 // - 3: next highest is partial LEADING-wildcard match
15 // - 4: lowest priority is partial full-wildcard match
16 // return priority found by reference
17 bool StringMacros::wildCardMatch(const std::string& needle, const std::string& haystack,
18  unsigned int* priorityIndex)
19 try
20 {
21 // __COUT__ << "\t\t wildCardMatch: " << needle <<
22 // " =in= " << haystack << " ??? " <<
23 // std::endl;
24 
25  //empty needle
26  if(needle.size() == 0)
27  {
28  if(priorityIndex) *priorityIndex = 1; //consider an exact match, to stop higher level loops
29  return true; //if empty needle, always "found"
30  }
31 
32  //only wildcard
33  if(needle == "*")
34  {
35  if(priorityIndex) *priorityIndex = 5; //only wildcard, is lowest priority
36  return true; //if empty needle, always "found"
37  }
38 
39  //no wildcards
40  if(needle == haystack)
41  {
42  if(priorityIndex) *priorityIndex = 1; //an exact match
43  return true;
44  }
45 
46  //trailing wildcard
47  if(needle[needle.size()-1] == '*' &&
48  needle.substr(0,needle.size()-1) ==
49  haystack.substr(0,needle.size()-1))
50  {
51  if(priorityIndex) *priorityIndex = 2; //trailing wildcard match
52  return true;
53  }
54 
55  //leading wildcard
56  if(needle[0] == '*' && needle.substr(1) ==
57  haystack.substr(haystack.size() - (needle.size()-1)))
58  {
59  if(priorityIndex) *priorityIndex = 3; //leading wildcard match
60  return true;
61  }
62 
63  //leading wildcard and trailing wildcard
64  if(needle[0] == '*' &&
65  needle[needle.size()-1] == '*' &&
66  std::string::npos != haystack.find(needle.substr(1,needle.size()-2)))
67  {
68  if(priorityIndex) *priorityIndex = 4; //leading and trailing wildcard match
69  return true;
70  }
71 
72  //else no match
73  if(priorityIndex) *priorityIndex = 0; //no match
74  return false;
75 }
76 catch(...)
77 {
78  if(priorityIndex) *priorityIndex = 0; //no match
79  return false; //if out of range
80 }
81 
82 
83 //========================================================================================================================
84 //inWildCardSet ~
85 // returns true if needle is in haystack (considering wildcards)
86 bool StringMacros::inWildCardSet(const std::string needle, const std::set<std::string>& haystack)
87 {
88  for(const auto& haystackString : haystack)
89  //use wildcard match, flip needle parameter.. because we want haystack to have the wildcards
90  if(StringMacros::wildCardMatch(haystackString,needle)) return true;
91  return false;
92 }
93 
94 //==============================================================================
95 //decodeURIComponent
96 // converts all %## to the ascii character
97 std::string StringMacros::decodeURIComponent(const std::string &data)
98 {
99  std::string decodeURIString(data.size(),0); //init to same size
100  unsigned int j=0;
101  for(unsigned int i=0;i<data.size();++i,++j)
102  {
103  if(data[i] == '%')
104  {
105  //high order hex nibble digit
106  if(data[i+1] > '9') //then ABCDEF
107  decodeURIString[j] += (data[i+1]-55)*16;
108  else
109  decodeURIString[j] += (data[i+1]-48)*16;
110 
111  //low order hex nibble digit
112  if(data[i+2] > '9') //then ABCDEF
113  decodeURIString[j] += (data[i+2]-55);
114  else
115  decodeURIString[j] += (data[i+2]-48);
116 
117  i+=2; //skip to next char
118  }
119  else
120  decodeURIString[j] = data[i];
121  }
122  decodeURIString.resize(j);
123  return decodeURIString;
124 }
125 
126 //==============================================================================
127 //convertEnvironmentVariables ~
128 // static recursive function
129 //
130 // allows environment variables entered as $NAME or ${NAME}
131 std::string StringMacros::convertEnvironmentVariables(const std::string& data)
132 {
133  size_t begin = data.find("$");
134  if(begin != std::string::npos)
135  {
136  size_t end;
137  std::string envVariable;
138  std::string converted = data; //make copy to modify
139 
140  if(data[begin+1] == '{')//check if using ${NAME} syntax
141  {
142  end = data.find("}",begin+2);
143  envVariable = data.substr(begin+2, end-begin-2);
144  ++end; //replace the closing } too!
145  }
146  else //else using $NAME syntax
147  {
148  //end is first non environment variable character
149  for(end = begin+1; end < data.size(); ++end)
150  if(!((data[end] >= '0' && data[end] <= '9') ||
151  (data[end] >= 'A' && data[end] <= 'Z') ||
152  (data[end] >= 'a' && data[end] <= 'z') ||
153  data[end] == '-' || data[end] == '_' ||
154  data[end] == '.' || data[end] == ':'))
155  break; //found end
156  envVariable = data.substr(begin+1, end-begin-1);
157  }
158  //__COUTV__(data);
159  //__COUTV__(envVariable);
160  char *envResult = getenv(envVariable.c_str());
161 
162  if(envResult)
163  {
164  //proceed recursively
165  return convertEnvironmentVariables(converted.replace(begin,end-begin,envResult));
166  }
167  else
168  {
169  __SS__ <<
170  ("The environmental variable '" + envVariable +
171  "' is not set! Please make sure you set it before continuing!") << std::endl;
172  __SS_THROW__;
173  }
174  }
175  //else no environment variables found in string
176  //__COUT__ << "Result: " << data << __E__;
177  return data;
178 }
179 
180 //==============================================================================
181 //isNumber ~~
182 // returns true if one or many numbers separated by operations (+,-,/,*) is
183 // present in the string.
184 // Numbers can be hex ("0x.."), binary("b..."), or base10.
185 bool StringMacros::isNumber(const std::string& s)
186 {
187  //extract set of potential numbers and operators
188  std::vector<std::string> numbers;
189  std::vector<char> ops;
190 
191  StringMacros::getVectorFromString(s,numbers,
192  /*delimiter*/ std::set<char>({'+','-','*','/'}),
193  /*whitespace*/ std::set<char>({' ','\t','\n','\r'}),
194  &ops);
195 
196  //__COUTV__(StringMacros::vectorToString(numbers));
197  //__COUTV__(StringMacros::vectorToString(ops));
198 
199  for(const auto& number:numbers)
200  {
201  if(number.size() == 0) continue; //skip empty numbers
202 
203  if(number.find("0x") == 0) //indicates hex
204  {
205  //__COUT__ << "0x found" << std::endl;
206  for(unsigned int i=2;i<number.size();++i)
207  {
208  if(!((number[i] >= '0' && number[i] <= '9') ||
209  (number[i] >= 'A' && number[i] <= 'F') ||
210  (number[i] >= 'a' && number[i] <= 'f')
211  ))
212  {
213  //__COUT__ << "prob " << number[i] << std::endl;
214  return false;
215  }
216  }
217  //return std::regex_match(number.substr(2), std::regex("^[0-90-9a-fA-F]+"));
218  }
219  else if(number[0] == 'b') //indicates binary
220  {
221  //__COUT__ << "b found" << std::endl;
222 
223  for(unsigned int i=1;i<number.size();++i)
224  {
225  if(!((number[i] >= '0' && number[i] <= '1')
226  ))
227  {
228  //__COUT__ << "prob " << number[i] << std::endl;
229  return false;
230  }
231  }
232  }
233  else
234  {
235  //__COUT__ << "base 10 " << std::endl;
236  for(unsigned int i=0;i<number.size();++i)
237  if(!((number[i] >= '0' && number[i] <= '9') ||
238  number[i] == '.' ||
239  number[i] == '+' ||
240  number[i] == '-'))
241  return false;
242  //Note: std::regex crashes in unresolvable ways (says Ryan.. also, stop using libraries)
243  //return std::regex_match(s, std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
244  }
245  }
246 
247  //__COUT__ << "yes " << std::endl;
248 
249  //all numbers are numbers
250  return true;
251 } //end isNumber()
252 
253 //==============================================================================
254 // validateValueForDefaultStringDataType
255 //
256 std::string StringMacros::validateValueForDefaultStringDataType(const std::string& value,
257  bool doConvertEnvironmentVariables)
258 try
259 {
260  return doConvertEnvironmentVariables?
261  StringMacros::convertEnvironmentVariables(value):
262  value;
263 }
264 catch(const std::runtime_error& e)
265 {
266  __SS__ << "Failed to validate value for default string data type. " << __E__ << e.what() << __E__;
267  __SS_THROW__;
268 }
269 
270 //==============================================================================
271 //getSetFromString
272 // extracts the set of elements from string that uses a delimiter
273 // ignoring whitespace
274 void StringMacros::getSetFromString(const std::string& inputString,
275  std::set<std::string>& setToReturn, const std::set<char>& delimiter,
276  const std::set<char>& whitespace)
277 {
278  unsigned int i=0;
279  unsigned int j=0;
280 
281  //go through the full string extracting elements
282  //add each found element to set
283  for(;j<inputString.size();++j)
284  if((whitespace.find(inputString[j]) != whitespace.end() || //ignore leading white space or delimiter
285  delimiter.find(inputString[j]) != delimiter.end())
286  && i == j)
287  ++i;
288  else if((whitespace.find(inputString[j]) != whitespace.end() || //trailing white space or delimiter indicates end
289  delimiter.find(inputString[j]) != delimiter.end())
290  && i != j) // assume end of element
291  {
292  //__COUT__ << "Set element found: " <<
293  // inputString.substr(i,j-i) << std::endl;
294 
295  setToReturn.emplace(inputString.substr(i,j-i));
296 
297  //setup i and j for next find
298  i = j+1;
299  }
300 
301  if(i != j) //last element check (for case when no concluding ' ' or delimiter)
302  setToReturn.emplace(inputString.substr(i,j-i));
303 } //end getSetFromString()
304 
305 //==============================================================================
306 //getVectorFromString
307 // extracts the list of elements from string that uses a delimiter
308 // ignoring whitespace
309 // optionally returns the list of delimiters encountered, which may be useful
310 // for extracting which operator was used
311 //
312 // Note: lists are returned as vectors
313 // Note: the size() of delimiters will be one less than the size() of the returned values
314 void StringMacros::getVectorFromString(const std::string& inputString,
315  std::vector<std::string>& listToReturn, const std::set<char>& delimiter,
316  const std::set<char>& whitespace, std::vector<char>* listOfDelimiters)
317 {
318  unsigned int i=0;
319  unsigned int j=0;
320  std::set<char>::iterator delimeterSearchIt;
321  char lastDelimiter;
322  bool isDelimiter;
323 
324  //__COUT__ << inputString << __E__;
325 
326  //go through the full string extracting elements
327  //add each found element to set
328  for(;j<inputString.size();++j)
329  {
330  //__COUT__ << (char)inputString[j] << __E__;
331 
332  delimeterSearchIt = delimiter.find(inputString[j]);
333  isDelimiter = delimeterSearchIt != delimiter.end();
334 
335  //__COUT__ << (char)inputString[j] << " " << (char)lastDelimiter << __E__;
336 
337  if((whitespace.find(inputString[j]) != whitespace.end() || //ignore leading white space or delimiter
338  isDelimiter)
339  && i == j)
340  ++i;
341  else if((whitespace.find(inputString[j]) != whitespace.end() || //trailing white space or delimiter indicates end
342  isDelimiter)
343  && i != j) // assume end of element
344  {
345  //__COUT__ << "Set element found: " <<
346  // inputString.substr(i,j-i) << std::endl;
347 
348  if(listOfDelimiters && listToReturn.size())
349  listOfDelimiters->push_back(lastDelimiter);
350  listToReturn.push_back(inputString.substr(i,j-i));
351 
352 
353  //setup i and j for next find
354  i = j+1;
355  }
356 
357  if(isDelimiter)
358  lastDelimiter = *delimeterSearchIt;
359  }
360 
361  if(i != j) //last element check (for case when no concluding ' ' or delimiter)
362  {
363  if(listOfDelimiters && listToReturn.size())
364  listOfDelimiters->push_back(lastDelimiter);
365  listToReturn.push_back(inputString.substr(i,j-i));
366  }
367 
368  //assert that there is one less delimiter than values
369  if(listOfDelimiters && listToReturn.size() - 1 != listOfDelimiters->size())
370  {
371  __SS__ << "There is a mismatch in delimiters to entries (should be one less delimiter): " <<
372  listOfDelimiters->size() <<
373  " vs " << listToReturn.size() << __E__ <<
374  "Entries: " <<
375  StringMacros::vectorToString(listToReturn) << __E__ <<
376  "Delimiters: " <<
377  StringMacros::vectorToString(*listOfDelimiters) << __E__;
378  __SS_THROW__;
379  }
380 
381 } //end getVectorFromString()
382 
383 //==============================================================================
384 //getMapFromString
385 // extracts the map of name-value pairs from string that uses two s
386 // ignoring whitespace
387 void StringMacros::getMapFromString(const std::string& inputString,
388  std::map<std::string,std::string>& mapToReturn,
389  const std::set<char>& pairPairDelimiter, const std::set<char>& nameValueDelimiter,
390  const std::set<char>& whitespace)
391 try
392 {
393  unsigned int i=0;
394  unsigned int j=0;
395  std::string name;
396  bool needValue = false;
397 
398  //go through the full string extracting map pairs
399  //add each found pair to map
400  for(;j<inputString.size();++j)
401  if(!needValue) //finding name
402  {
403  if((whitespace.find(inputString[j]) != whitespace.end() || //ignore leading white space or delimiter
404  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end())
405  && i == j)
406  ++i;
407  else if((whitespace.find(inputString[j]) != whitespace.end() || //trailing white space or delimiter indicates end
408  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end())
409  && i != j) // assume end of map name
410  {
411  //__COUT__ << "Map name found: " <<
412  // inputString.substr(i,j-i) << std::endl;
413 
414  name = inputString.substr(i,j-i); //save name, for concluding pair
415 
416  needValue = true; //need value now
417 
418  //setup i and j for next find
419  i = j+1;
420  }
421  }
422  else //finding value
423  {
424  if((whitespace.find(inputString[j]) != whitespace.end() || //ignore leading white space or delimiter
425  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end())
426  && i == j)
427  ++i;
428  else if((whitespace.find(inputString[j]) != whitespace.end() || //trailing white space or delimiter indicates end
429  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end())
430  && i != j) // assume end of value name
431  {
432  //__COUT__ << "Map value found: " <<
433  // inputString.substr(i,j-i) << std::endl;
434 
435  auto /*pair<it,success>*/ emplaceReturn = mapToReturn.emplace(std::pair<std::string,std::string>(
436  name,
437  validateValueForDefaultStringDataType(
438  inputString.substr(i,j-i)) //value
439  ));
440 
441  if(!emplaceReturn.second)
442  {
443  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i,j-i) <<
444  "') and keeping current value ('" << emplaceReturn.first->second << "'). " << __E__;
445  }
446 
447  needValue = false; //need name now
448 
449  //setup i and j for next find
450  i = j+1;
451  }
452  }
453 
454  if(i != j) //last value (for case when no concluding ' ' or delimiter)
455  {
456  auto /*pair<it,success>*/ emplaceReturn = mapToReturn.emplace(std::pair<std::string,std::string>(
457  name,
458  validateValueForDefaultStringDataType(
459  inputString.substr(i,j-i)) //value
460  ));
461 
462  if(!emplaceReturn.second)
463  {
464  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i,j-i) <<
465  "') and keeping current value ('" << emplaceReturn.first->second << "'). " << __E__;
466  }
467  }
468 }
469 catch(const std::runtime_error &e)
470 {
471  __SS__ << "Error while extracting a map from the string '" <<
472  inputString << "'... is it a valid map?" << __E__ << e.what() << __E__;
473  __SS_THROW__;
474 }
475 
476 
477 //==============================================================================
478 //mapToString
479 std::string StringMacros::mapToString(const std::map<std::string,uint8_t>& mapToReturn,
480  const std::string& primaryDelimeter, const std::string& secondaryDelimeter)
481 {
482  std::stringstream ss;
483  bool first = true;
484  for(auto& mapPair:mapToReturn)
485  {
486  if(first) first = false;
487  else ss << primaryDelimeter;
488  ss << mapPair.first << secondaryDelimeter << (unsigned int)mapPair.second;
489  }
490  return ss.str();
491 }
492 
493 //==============================================================================
494 //setToString
495 std::string StringMacros::setToString(const std::set<uint8_t>& setToReturn, const std::string& delimeter)
496 {
497  std::stringstream ss;
498  bool first = true;
499  for(auto& setValue:setToReturn)
500  {
501  if(first) first = false;
502  else ss << delimeter;
503  ss << (unsigned int)setValue;
504  }
505  return ss.str();
506 }
507 
508 //==============================================================================
509 //vectorToString
510 std::string StringMacros::vectorToString(const std::vector<uint8_t>& setToReturn, const std::string& delimeter)
511 {
512  std::stringstream ss;
513  bool first = true;
514  for(auto& setValue:setToReturn)
515  {
516  if(first) first = false;
517  else ss << delimeter;
518  ss << (unsigned int)setValue;
519  }
520  return ss.str();
521 }
522 
523 
524 #ifdef __GNUG__
525 #include <cstdlib>
526 #include <memory>
527 #include <cxxabi.h>
528 
529 
530 //==============================================================================
531 //demangleTypeName
532 std::string StringMacros::demangleTypeName(const char* name)
533 {
534  int status = -4; // some arbitrary value to eliminate the compiler warning
535 
536  // enable c++11 by passing the flag -std=c++11 to g++
537  std::unique_ptr<char, void(*)(void*)> res {
538  abi::__cxa_demangle(name, NULL, NULL, &status),
539  std::free
540  };
541 
542  return (status==0) ? res.get() : name ;
543 }
544 
545 #else //does nothing if not g++
546 //==============================================================================
547 //getMapFromString
548 //
549 std::string StringMacros::demangleTypeName(const char* name)
550 {
551  return name;
552 }
553 
554 
555 #endif
556 
557 
558 
559 
560 
561 
562