AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.4.3
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 (_len == strlen(rhs_)) && (::strncmp(_data, 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 (_len != strlen(rhs_)) || (::memcmp(_data, 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  if (field_.empty())
369  {
370  return;
371  }
372  const char* data = field_.data();
373  size_t len = field_.len();
374  // Can't parse a timestamp
375  if (isTimestamp(field_))
376  {
377  return;
378  }
379  size_t i = 0;
380  for ( ; i < len; ++i)
381  {
382  if (!isdigit(data[i]))
383  {
384  break;
385  }
386  publisherId_ *= 10;
387  publisherId_ += (amps_uint64_t)(data[i] - '0');
388  }
389  // Make sure it's just the | separator
390  if (i < len && data[i] != '|')
391  {
392  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
393  return;
394  }
395  for (i = i + 1; i < len; ++i)
396  {
397  if (!isdigit(data[i]))
398  {
399  break;
400  }
401  sequenceNumber_ *= 10;
402  sequenceNumber_ += (amps_uint64_t)(data[i] - '0');
403  }
404  }
405 
406  };
407 
408  class BookmarkRange : public Field
409  {
410  public:
411  static bool isRange(const Field& bookmark_)
412  {
413  return memchr(bookmark_.data(), ':', bookmark_.len()) != NULL;
414  }
415 
416  BookmarkRange()
417  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
418  , _capacity(AMPS_UNSET_INDEX)
419  {
420  }
421 
422  // Parse it for a range, set everything empty if not a valid range
423  BookmarkRange(const Field& field_)
424  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
425  , _capacity(field_.len())
426  {
427  set(field_);
428  }
429 
430  void set(const Field& field_)
431  {
432  // Are we already the same
433  if (_data == field_.data() || operator==(field_))
434  {
435  return;
436  }
437  // Reset self
438  notValid();
439  // Make self a copy
440  deepCopy(field_);
441  _capacity = _len;
442  bool foundSeparator = false;
443  bool foundClose = false;
444  for (size_t i = 0; i < _len; ++i)
445  {
446  switch (_data[i])
447  {
448  case '(':
449  case '[':
450  {
451  // Is this within the range?
452  if (foundClose || _open != AMPS_UNSET_INDEX)
453  {
454  notValid();
455  return;
456  }
457  _open = i;
458  }
459  break;
460  // Valid bookmark characters [0-9|,TZ]
461  case '0':
462  case '1':
463  case '2':
464  case '3':
465  case '4':
466  case '5':
467  case '6':
468  case '7':
469  case '8':
470  case '9':
471  case '|':
472  case ',':
473  case 'T':
474  case 'Z':
475  {
476  // Is this within the range?
477  if (foundClose || _open == AMPS_UNSET_INDEX)
478  {
479  notValid();
480  return;
481  }
482  else if (foundSeparator) // Part of end?
483  {
484  if (!_end.data()) // Start of end?
485  {
486  _end.assign(_data + i, 0);
487  }
488  }
489  else if (!_start.data()) // Start of start?
490  {
491  _start.assign(_data + i, 0);
492  }
493  }
494  break;
495  case ':':
496  {
497  // Is this within the range and do we have a start?
498  if (foundSeparator || foundClose || _open == AMPS_UNSET_INDEX
499  || !_start.data())
500  {
501  notValid();
502  return;
503  }
504  foundSeparator = true;
505  // Do we need to set start length?
506  if (_start.len() == 0)
507  {
508  // Length is here, minus beginning of start - 1
509  _start.assign(_start.data(), i - (size_t)(_start.data() - _data));
510  }
511  }
512  break;
513  case ']':
514  case ')':
515  {
516  // Is this within the range and do we have an end?
517  if (foundClose || _open == AMPS_UNSET_INDEX || !_end.data())
518  {
519  notValid();
520  return;
521  }
522  foundClose = true;
523  _len = i + 1;
524  // Do we need to set end length?
525  if (_end.len() == 0)
526  {
527  // Length is here, minus beginning of end - 1
528  _end.assign(_end.data(), i - (size_t)(_end.data() - _data));
529  }
530  }
531  break;
532  case ' ':
533  {
534  // Do we need to set end length?
535  if (_end.data() && _end.len() == 0)
536  {
537  // Length is here, minus beginning of end - 1
538  _end.assign(_end.data(), i - (size_t)(_end.data() - _data));
539  }
540  // Else do we need to set start length?
541  else if (_start.data() && _start.len() == 0)
542  {
543  // Length is here, minus beginning of start - 1
544  _start.assign(_start.data(), i - (size_t)(_start.data() - _data));
545  }
546  }
547  break;
548  default:
549  {
550  notValid();
551  }
552  break;
553  }
554  }
555  // If we didn't find everything clear self
556  if (_start.empty() || _end.empty())
557  {
558  notValid();
559  }
560  }
561 
562  // Centralized place to clear self
563  void notValid()
564  {
565  if (!_data || !_len)
566  {
567  return;
568  }
569  delete[] _data;
570  _data = 0;
571  _len = 0;
572  _start.assign(0, 0);
573  _end.assign(0, 0);
574  _open = AMPS_UNSET_INDEX;
575  _capacity = 0;
576  }
577 
580  bool isValid() const
581  {
582  return !empty();
583  }
584 
587  bool isStartInclusive() const
588  {
589  return _data[_open] == '[';
590  }
591 
594  void makeStartExclusive()
595  {
596  const_cast<char*>(_data)[_open] = '(';
597  }
598 
601  bool isEndInclusive() const
602  {
603  return _data[_len - 1] == ']';
604  }
605 
608  void makeEndExclusive()
609  {
610  const_cast<char*>(_data)[_len - 1] = ')';
611  }
612 
615  const Field& getStart() const
616  {
617  return _start;
618  }
619 
620  const Field& getEnd() const
621  {
622  return _end;
623  }
624 
627  void replaceStart(const Field& start_, bool makeExclusive_ = false)
628  {
629  // Best case, it fits in our existing self. Since we may do this more
630  // than once, just add start+end+open+close+separator
631  if (_capacity >= (start_.len() + _end.len() + 3))
632  {
633  char* data = const_cast<char*>(_data);
634  if (makeExclusive_)
635  {
636  data[_open] = '(';
637  }
638  if (_open) // Move open to beginning if not there
639  {
640  data[0] = _data[_open];
641  _open = 0;
642  }
643  if ((size_t)(_end.data() - _data - 2) < start_.len())
644  {
645  size_t newLen = start_.len() + _end.len() + 3;
646  // Need to move end to make room for new start
647  // This copies _end and close
648  // Last char of _start will be at _start.len() because of open
649  for (size_t dest = newLen - 1, src = _len - 1;
650  src > _start.len(); --src, --dest)
651  {
652  data[dest] = _data[src];
653  // Find separator, we're done
654  if (data[src] == ':')
655  {
656  _end.assign(data + dest + 1, _end.len());
657  break;
658  }
659  }
660  _len = newLen;
661  }
662  // Copy in new start after _open
663  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
664  _start.assign(data + 1, start_.len());
665  // Possibly move end left
666  if ((size_t)(_end.data() - _start.data()) > _start.len() + 1UL)
667  {
668  // Copy to just after start starting with ':'
669  for (size_t dest = _start.len() + 1, src = (size_t)(_end.data() - data - 1);
670  src < _len; ++dest, ++src)
671  {
672  data[dest] = data[src];
673  if (data[src] == ']' || data[src] == ')')
674  {
675  _end.assign(data + _start.len() + 2, _end.len());
676  break;
677  }
678  }
679  _len = _start.len() + _end.len() + 3;
680  }
681  }
682  else // We need to resize and copy everything over
683  {
684  // Let's set min resize at 4 bookmarks + 3 commas + 3
685  // Some MSVC versions have issues with max so use ?:
686  _capacity = (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL
687  >= start_.len() + _end.len() + 3UL)
688  ? (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL)
689  : (start_.len() + _end.len() + 3UL);
690  char* data = new char[_capacity];
691  if (makeExclusive_)
692  {
693  data[0] = '(';
694  }
695  else
696  {
697  data[0] = _data[_open];
698  }
699  _open = 0;
700  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
701  _start.assign(data + 1, start_.len());
702  data[start_.len() + 1] = ':';
703  ::memcpy(static_cast<void*>(data + start_.len() + 2), _end.data(),
704  _end.len());
705  _end.assign(data + start_.len() + 2, _end.len());
706  size_t len = start_.len() + 3 + _end.len();
707  data[len - 1] = _data[_len - 1];
708  clear();
709  assign(data, len);
710  }
711  }
712 
713  private:
714  Field _start;
715  Field _end;
716  size_t _open;
717  size_t _capacity;
718  };
719 
720 }
721 
722 #endif
723 
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