AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.4.2
Field.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2024 60East Technologies Inc., All Rights Reserved.
4 //
5 // This computer software is owned by 60East Technologies Inc. and is
6 // protected by U.S. copyright laws and other laws and by international
7 // treaties. This computer software is furnished by 60East Technologies
8 // Inc. pursuant to a written license agreement and may be used, copied,
9 // transmitted, and stored only in accordance with the terms of such
10 // license agreement and with the inclusion of the above copyright notice.
11 // This computer software or any other copies thereof may not be provided
12 // or otherwise made available to any other person.
13 //
14 // U.S. Government Restricted Rights. This computer software: (a) was
15 // developed at private expense and is in all respects the proprietary
16 // information of 60East Technologies Inc.; (b) was not developed with
17 // government funds; (c) is a trade secret of 60East Technologies Inc.
18 // for all purposes of the Freedom of Information Act; and (d) is a
19 // commercial item and thus, pursuant to Section 12.212 of the Federal
20 // Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
21 // Government's use, duplication or disclosure of the computer software
22 // is subject to the restrictions set forth by 60East Technologies Inc..
23 //
25 #ifndef __AMPS_FIELD_HPP__
26 #define __AMPS_FIELD_HPP__
27 
28 #define _AMPS_SKIP_AMPSPLUSPLUS
29 #include <amps/amps.h>
30 #undef _AMPS_SKIP_AMPSPLUSPLUS
31 #include <algorithm>
32 #include <string>
33 #include <string.h>
34 #include <vector>
35 
36 #define AMPS_UNSET_INDEX (size_t)-1
37 #define AMPS_TIMESTAMP_LEN 16
38 #define AMPS_TIMESTAMP_LEN_LONG 23
39 #define AMPS_MAX_BOOKMARK_LEN 42
40 // Used in bookmark store recovery to look for corruption, NOT FIXED IN AMPS
41 #define AMPS_MAX_SUBID_LEN 1048576 // 1MB, NOT FIXED IN AMPS
42 #ifdef _WIN32
43  #if (_MSC_VER >= 1400) // VS2005 or higher
44  #define AMPS_snprintf(buf_, sz_, ...) _snprintf_s(buf_, sz_, _TRUNCATE, __VA_ARGS__)
45  #define AMPS_snprintf_amps_uint64_t(buf,sz,val) sprintf_s(buf,sz,"%I64u",val)
46  #ifdef _WIN64
47  #define AMPS_snprintf_sizet(buf,sz,val) sprintf_s(buf,sz,"%lu",val)
48  #else
49  #define AMPS_snprintf_sizet(buf,sz,val) sprintf_s(buf,sz,"%u",val)
50  #endif
51  #else // VS2003 or older
52  #define AMPS_snprintf _snprintf
53  #ifdef _WIN64
54  #define AMPS_snprintf_sizet(buf,sz,val) _sprintf(buf,sz,"%lu",val)
55  #else
56  #define AMPS_snprintf_sizet(buf,sz,val) _sprintf(buf,sz,"%u",val)
57  #endif
58  #endif
59 #else
60  #define AMPS_snprintf snprintf
61  #if defined(__x86_64__) || defined(__aarch64__)
62  #define AMPS_snprintf_amps_uint64_t(buf,sz,val) snprintf(buf,sz,"%lu", (unsigned long)val)
63  #define AMPS_snprintf_sizet(buf,sz,val) snprintf(buf,sz,"%lu",val)
64  #else
65  #define AMPS_snprintf_amps_uint64_t(buf,sz,val) snprintf(buf,sz,"%llu",val)
66  #define AMPS_snprintf_sizet(buf,sz,val) snprintf(buf,sz,"%u",val)
67  #endif
68 #endif
69 
70 namespace AMPS
71 {
72 
73  using std::string;
74 
78 
85 
86  class Field
87  {
88  protected:
89  const char* _data;
90  size_t _len;
91  public:
92  Field() : _data(NULL), _len(0) {;}
93  Field(const char* data_)
94  {
95  _data = data_;
96  _len = ::strlen(data_);
97  }
98  Field(const char* data_, size_t len_)
99  {
100  _data = data_;
101  _len = len_;
102  }
103  Field(const Field& rhs)
104  {
105  _data = rhs._data;
106  _len = rhs._len;
107  }
108  Field& operator=(const Field& rhs)
109  {
110  _data = rhs._data;
111  _len = rhs._len;
112  return *this;
113  }
114  Field(const std::string& string_)
115  {
116  _data = string_.c_str();
117  _len = string_.length();
118  }
119 
120  bool contains(const char* searchString, size_t len) const
121  {
122  const char* dataEnd = _data + _len;
123  return std::search(_data, dataEnd, searchString, searchString + len) != dataEnd;
124  }
125 
128  bool empty () const
129  {
130  return _len == 0;
131  }
132 
136  operator std::string () const
137  {
138  return _len ? std::string(_data, _len) : std::string();
139  }
140 
146  bool operator==(const Field& rhs_) const
147  {
148  if ( _len == rhs_._len )
149  {
150  return ::memcmp(_data, rhs_._data, _len) == 0;
151  }
152  return false;
153  }
154 
160  bool operator==(const char* rhs_) const
161  {
162  if (!_data || !rhs_)
163  {
164  return (!_data && !rhs_);
165  }
166  return ::strncmp(_data, rhs_, _len) == 0 && rhs_[_len] == '\0';
167  }
168 
169  bool operator<(const Field& rhs) const;
170 
176  bool operator!=(const Field& rhs_) const
177  {
178  if ( _len == rhs_._len )
179  {
180  return ::memcmp(_data, rhs_._data, _len) != 0;
181  }
182  return true;
183  }
184 
190  bool operator!=(const char* rhs_) const
191  {
192  return strncmp(_data, rhs_, _len) != 0 || rhs_[_len] != '\0';
193  }
194 
200  bool operator!=(const std::string& rhs_) const
201  {
202  return rhs_.compare(0, rhs_.length(), _data, _len) != 0;
203  }
204 
210  bool operator==(const std::string& rhs_) const
211  {
212  return rhs_.compare(0, rhs_.length(), _data, _len) == 0;
213  }
214 
218  void deepCopy(const Field& orig_)
219  {
220  delete[] _data;
221  if (orig_._len > 0)
222  {
223  _data = new char[orig_._len];
224  ::memcpy(static_cast<void*>(const_cast<char*>(_data)),
225  orig_._data, orig_._len);
226  _len = orig_._len;
227  }
228  else
229  {
230  _data = NULL;
231  _len = 0;
232  }
233  }
234 
237  Field deepCopy() const
238  {
239  Field newField;
240  newField.deepCopy(*this);
241  return newField;
242  }
243 
247  void clear()
248  {
249  if (!_data || !_len)
250  {
251  return;
252  }
253  delete[] _data;
254  _len = 0;
255  _data = NULL;
256  }
257 
260  const char* data() const
261  {
262  return _data;
263  }
264 
267  size_t len() const
268  {
269  return _len;
270  }
271 
272  // assign a new range into this Message::Field
273  void assign(const char* ptr, size_t len)
274  {
275  _data = ptr;
276  _len = len;
277  }
278 
279  // compute a hash value
280  inline size_t hash_function() const
281  {
282  size_t n_ = _len;
283  const char* p_ = _data;
284  size_t h = 0, c;
285  while (n_ != 0)
286  {
287  c = (unsigned long) * p_;
288  h += (h << 5) + c;
289  ++p_, --n_;
290  }
291  return h;
292  }
293 
294  struct FieldHash
295  {
296  size_t operator()(const Field& f) const
297  {
298  return f.hash_function();
299  }
300 
301  bool operator()(const Field& f1, const Field& f2) const
302  {
303  if (f1.len() < f2.len())
304  {
305  return true;
306  }
307  if (f1.len() > f2.len())
308  {
309  return false;
310  }
311  // Length is the same, don't compare empty
312  if (f1.len() == 0)
313  {
314  return true;
315  }
316  return ::memcmp(f1.data(), f2.data(), f2.len()) < 0;
317  }
318  };
319 
320  // Determine if the Field represents a timestamp
321  static bool isTimestamp(const Field& field_)
322  {
323  return (field_.len() >= AMPS_TIMESTAMP_LEN
324  && field_.len() <= AMPS_TIMESTAMP_LEN_LONG
325  && field_.data()[8] == 'T');
326  }
327 
328  static std::vector<Field> parseBookmarkList(const Field& field_)
329  {
330  std::vector<Field> list;
331  const char* start = field_.data();
332  size_t remain = field_.len();
333  const char* comma = (const char*)memchr((const void*)start,
334  (int)',', remain);
335  while (comma)
336  {
337  size_t len = (size_t)(comma - start);
338  if (len != 0)
339  {
340 #ifdef AMPS_USE_EMPLACE
341  list.emplace_back(Field(start, len));
342 #else
343  list.push_back(Field(start, len));
344 #endif
345  }
346  start = ++comma;
347  remain = field_.len() - (size_t)(start - field_.data());
348  comma = (const char*)memchr((const void*)start,
349  (int)',', remain);
350  }
351  if (remain != 0)
352  {
353 #ifdef AMPS_USE_EMPLACE
354  list.emplace_back(Field(start, remain));
355 #else
356  list.push_back(Field(start, remain));
357 #endif
358  }
359  return list;
360  }
361 
362  // Get sequence number from a Field that is a bookmark
363  static void parseBookmark(const Field& field_,
364  amps_uint64_t& publisherId_,
365  amps_uint64_t& sequenceNumber_)
366  {
367  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
368  const char* data = field_.data();
369  size_t len = field_.len();
370  // Can't parse a timestamp
371  if (isTimestamp(field_))
372  {
373  return;
374  }
375  size_t i = 0;
376  for ( ; i < len; ++i)
377  {
378  if (!isdigit(data[i]))
379  {
380  break;
381  }
382  publisherId_ *= 10;
383  publisherId_ += (amps_uint64_t)(data[i] - '0');
384  }
385  // Make sure it's just the | separator
386  if (i < len && data[i] != '|')
387  {
388  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
389  return;
390  }
391  for (i = i + 1; i < len; ++i)
392  {
393  if (!isdigit(data[i]))
394  {
395  break;
396  }
397  sequenceNumber_ *= 10;
398  sequenceNumber_ += (amps_uint64_t)(data[i] - '0');
399  }
400  }
401 
402  };
403 
404  class BookmarkRange : public Field
405  {
406  public:
407  static bool isRange(const Field& bookmark_)
408  {
409  return memchr(bookmark_.data(), ':', bookmark_.len()) != NULL;
410  }
411 
412  BookmarkRange()
413  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
414  , _capacity(AMPS_UNSET_INDEX)
415  {
416  }
417 
418  // Parse it for a range, set everything empty if not a valid range
419  BookmarkRange(const Field& field_)
420  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
421  , _capacity(field_.len())
422  {
423  set(field_);
424  }
425 
426  void set(const Field& field_)
427  {
428  // Are we already the same
429  if (_data == field_.data() || operator==(field_))
430  {
431  return;
432  }
433  // Reset self
434  notValid();
435  // Make self a copy
436  deepCopy(field_);
437  _capacity = _len;
438  bool foundSeparator = false;
439  bool foundClose = false;
440  for (size_t i = 0; i < _len; ++i)
441  {
442  switch (_data[i])
443  {
444  case '(':
445  case '[':
446  {
447  // Is this within the range?
448  if (foundClose || _open != AMPS_UNSET_INDEX)
449  {
450  notValid();
451  return;
452  }
453  _open = i;
454  }
455  break;
456  // Valid bookmark characters [0-9|,TZ]
457  case '0':
458  case '1':
459  case '2':
460  case '3':
461  case '4':
462  case '5':
463  case '6':
464  case '7':
465  case '8':
466  case '9':
467  case '|':
468  case ',':
469  case 'T':
470  case 'Z':
471  {
472  // Is this within the range?
473  if (foundClose || _open == AMPS_UNSET_INDEX)
474  {
475  notValid();
476  return;
477  }
478  else if (foundSeparator) // Part of end?
479  {
480  if (!_end.data()) // Start of end?
481  {
482  _end.assign(_data + i, 0);
483  }
484  }
485  else if (!_start.data()) // Start of start?
486  {
487  _start.assign(_data + i, 0);
488  }
489  }
490  break;
491  case ':':
492  {
493  // Is this within the range and do we have a start?
494  if (foundSeparator || foundClose || _open == AMPS_UNSET_INDEX
495  || !_start.data())
496  {
497  notValid();
498  return;
499  }
500  foundSeparator = true;
501  // Do we need to set start length?
502  if (_start.len() == 0)
503  {
504  // Length is here, minus beginning of start - 1
505  _start.assign(_start.data(), i - (size_t)(_start.data() - _data));
506  }
507  }
508  break;
509  case ']':
510  case ')':
511  {
512  // Is this within the range and do we have an end?
513  if (foundClose || _open == AMPS_UNSET_INDEX || !_end.data())
514  {
515  notValid();
516  return;
517  }
518  foundClose = true;
519  _len = i + 1;
520  // Do we need to set end length?
521  if (_end.len() == 0)
522  {
523  // Length is here, minus beginning of end - 1
524  _end.assign(_end.data(), i - (size_t)(_end.data() - _data));
525  }
526  }
527  break;
528  case ' ':
529  {
530  // Do we need to set end length?
531  if (_end.data() && _end.len() == 0)
532  {
533  // Length is here, minus beginning of end - 1
534  _end.assign(_end.data(), i - (size_t)(_end.data() - _data));
535  }
536  // Else do we need to set start length?
537  else if (_start.data() && _start.len() == 0)
538  {
539  // Length is here, minus beginning of start - 1
540  _start.assign(_start.data(), i - (size_t)(_start.data() - _data));
541  }
542  }
543  break;
544  default:
545  {
546  notValid();
547  }
548  break;
549  }
550  }
551  // If we didn't find everything clear self
552  if (_start.empty() || _end.empty())
553  {
554  notValid();
555  }
556  }
557 
558  // Centralized place to clear self
559  void notValid()
560  {
561  if (!_data || !_len)
562  {
563  return;
564  }
565  delete[] _data;
566  _data = 0;
567  _len = 0;
568  _start.assign(0, 0);
569  _end.assign(0, 0);
570  _open = AMPS_UNSET_INDEX;
571  _capacity = 0;
572  }
573 
576  bool isValid() const
577  {
578  return !empty();
579  }
580 
583  bool isStartInclusive() const
584  {
585  return _data[_open] == '[';
586  }
587 
590  void makeStartExclusive()
591  {
592  const_cast<char*>(_data)[_open] = '(';
593  }
594 
597  bool isEndInclusive() const
598  {
599  return _data[_len - 1] == ']';
600  }
601 
604  void makeEndExclusive()
605  {
606  const_cast<char*>(_data)[_len - 1] = ')';
607  }
608 
611  const Field& getStart() const
612  {
613  return _start;
614  }
615 
616  const Field& getEnd() const
617  {
618  return _end;
619  }
620 
623  void replaceStart(const Field& start_, bool makeExclusive_ = false)
624  {
625  // Best case, it fits in our existing self. Since we may do this more
626  // than once, just add start+end+open+close+separator
627  if (_capacity >= (start_.len() + _end.len() + 3))
628  {
629  char* data = const_cast<char*>(_data);
630  if (makeExclusive_)
631  {
632  data[_open] = '(';
633  }
634  if (_open) // Move open to beginning if not there
635  {
636  data[0] = _data[_open];
637  _open = 0;
638  }
639  if ((size_t)(_end.data() - _data - 2) < start_.len())
640  {
641  size_t newLen = start_.len() + _end.len() + 3;
642  // Need to move end to make room for new start
643  // This copies _end and close
644  // Last char of _start will be at _start.len() because of open
645  for (size_t dest = newLen - 1, src = _len - 1;
646  src > _start.len(); --src, --dest)
647  {
648  data[dest] = _data[src];
649  // Find separator, we're done
650  if (data[src] == ':')
651  {
652  _end.assign(data + dest + 1, _end.len());
653  break;
654  }
655  }
656  _len = newLen;
657  }
658  // Copy in new start after _open
659  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
660  _start.assign(data + 1, start_.len());
661  // Possibly move end left
662  if ((size_t)(_end.data() - _start.data()) > _start.len() + 1UL)
663  {
664  // Copy to just after start starting with ':'
665  for (size_t dest = _start.len() + 1, src = (size_t)(_end.data() - data - 1);
666  src < _len; ++dest, ++src)
667  {
668  data[dest] = data[src];
669  if (data[src] == ']' || data[src] == ')')
670  {
671  _end.assign(data + _start.len() + 2, _end.len());
672  break;
673  }
674  }
675  _len = _start.len() + _end.len() + 3;
676  }
677  }
678  else // We need to resize and copy everything over
679  {
680  // Let's set min resize at 4 bookmarks + 3 commas + 3
681  // Some MSVC versions have issues with max so use ?:
682  _capacity = (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL
683  >= start_.len() + _end.len() + 3UL)
684  ? (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL)
685  : (start_.len() + _end.len() + 3UL);
686  char* data = new char[_capacity];
687  if (makeExclusive_)
688  {
689  data[0] = '(';
690  }
691  else
692  {
693  data[0] = _data[_open];
694  }
695  _open = 0;
696  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
697  _start.assign(data + 1, start_.len());
698  data[start_.len() + 1] = ':';
699  ::memcpy(static_cast<void*>(data + start_.len() + 2), _end.data(),
700  _end.len());
701  _end.assign(data + start_.len() + 2, _end.len());
702  size_t len = start_.len() + 3 + _end.len();
703  data[len - 1] = _data[_len - 1];
704  clear();
705  assign(data, len);
706  }
707  }
708 
709  private:
710  Field _start;
711  Field _end;
712  size_t _open;
713  size_t _capacity;
714  };
715 
716 }
717 
718 #endif
719 
bool operator==(const char *rhs_) const
String comparison operator Returns `true&#39; if self and rhs are equivalent, `false&#39; otherwise...
Definition: Field.hpp:160
Core type and function declarations for the AMPS C client.
bool operator!=(const std::string &rhs_) const
String comparison operator Returns `true&#39; if self and rhs are not equivalent.
Definition: Field.hpp:200
bool operator!=(const Field &rhs_) const
Comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:176
bool operator==(const Field &rhs_) const
Comparison operator Returns `true&#39; if self and rhs are equivalent, `false&#39; otherwise.
Definition: Field.hpp:146
void clear()
Deletes the data associated with this Field, should only be used on Fields that were created as deepC...
Definition: Field.hpp:247
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:260
bool empty() const
Returns &#39;true&#39; if empty, &#39;false&#39; otherwise.
Definition: Field.hpp:128
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:267
bool operator!=(const char *rhs_) const
String comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:190
Field represents the value of a single field in a Message.
Definition: Field.hpp:86
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:218
Definition: ampsplusplus.hpp:102
bool operator==(const std::string &rhs_) const
String comparison operator Returns `true&#39; if self and rhs are equivalent.
Definition: Field.hpp:210
Field deepCopy() const
Makes a deep copy of self, returns it.
Definition: Field.hpp:237