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