AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.5.4
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 
253  static Field stringCopy(const char* str_)
254  {
255 #ifdef _WIN32
256  Field newField(_strdup(str_));
257 #else
258  Field newField(strdup(str_));
259 #endif
260  return newField;
261  }
262 
266  void clear()
267  {
268  if (!_data || !_len)
269  {
270  return;
271  }
272  free((void*)_data);
273  _len = 0;
274  _data = NULL;
275  }
276 
279  const char* data() const
280  {
281  return _data;
282  }
283 
286  size_t len() const
287  {
288  return _len;
289  }
290 
291  // assign a new range into this Message::Field
292  void assign(const char* ptr, size_t len)
293  {
294  _data = ptr;
295  _len = len;
296  }
297 
298  // compute a hash value
299  inline size_t hash_function() const
300  {
301  size_t n_ = _len;
302  const char* p_ = _data;
303  size_t h = 0, c;
304  while (n_ != 0)
305  {
306  c = (unsigned long) * p_;
307  h += (h << 5) + c;
308  ++p_, --n_;
309  }
310  return h;
311  }
312 
313  struct FieldHash
314  {
315  size_t operator()(const Field& f) const
316  {
317  return f.hash_function();
318  }
319 
320  bool operator()(const Field& f1, const Field& f2) const
321  {
322  if (f1.len() < f2.len())
323  {
324  return true;
325  }
326  if (f1.len() > f2.len())
327  {
328  return false;
329  }
330  // Length is the same, don't compare empty
331  if (f1.len() == 0)
332  {
333  return true;
334  }
335  return ::memcmp(f1.data(), f2.data(), f2.len()) < 0;
336  }
337  };
338 
339  // Determine if the Field represents a timestamp
340  static bool isTimestamp(const Field& field_)
341  {
342  return (field_.len() >= AMPS_TIMESTAMP_LEN
343  && field_.len() <= AMPS_TIMESTAMP_LEN_LONG
344  && field_.data()[8] == 'T');
345  }
346 
347  static std::vector<Field> parseBookmarkList(const Field& field_)
348  {
349  std::vector<Field> list;
350  const char* start = field_.data();
351  size_t remain = field_.len();
352  const char* comma = (const char*)memchr((const void*)start,
353  (int)',', remain);
354  while (comma)
355  {
356  size_t len = (size_t)(comma - start);
357  if (len != 0)
358  {
359  list.emplace_back(Field(start, len));
360  }
361  start = ++comma;
362  remain = field_.len() - (size_t)(start - field_.data());
363  comma = (const char*)memchr((const void*)start,
364  (int)',', remain);
365  }
366  if (remain != 0)
367  {
368  list.emplace_back(Field(start, remain));
369  }
370  return list;
371  }
372 
373  // Get sequence number from a Field that is a bookmark
374  static void parseBookmark(const Field& field_,
375  amps_uint64_t& publisherId_,
376  amps_uint64_t& sequenceNumber_)
377  {
378  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
379  if (field_.empty())
380  {
381  return;
382  }
383  const char* data = field_.data();
384  size_t len = field_.len();
385  // Can't parse a timestamp
386  if (isTimestamp(field_))
387  {
388  return;
389  }
390  size_t i = 0;
391  for ( ; i < len; ++i)
392  {
393  if (!isdigit(data[i]))
394  {
395  break;
396  }
397  publisherId_ *= 10;
398  publisherId_ += (amps_uint64_t)(data[i] - '0');
399  }
400  // Make sure it's just the | separator
401  if (i < len && data[i] != '|')
402  {
403  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
404  return;
405  }
406  for (i = i + 1; i < len; ++i)
407  {
408  if (!isdigit(data[i]))
409  {
410  break;
411  }
412  sequenceNumber_ *= 10;
413  sequenceNumber_ += (amps_uint64_t)(data[i] - '0');
414  }
415  }
416 
417  };
418 
419  class BookmarkRange : public Field
420  {
421  public:
422  static bool isRange(const Field& bookmark_)
423  {
424  return bookmark_.data() && bookmark_.len()
425  ? memchr(bookmark_.data(), ':', bookmark_.len()) != NULL
426  : false;
427  }
428 
429  BookmarkRange()
430  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
431  , _capacity(AMPS_UNSET_INDEX)
432  {
433  }
434 
435  // Parse it for a range, set everything empty if not a valid range
436  BookmarkRange(const Field& field_)
437  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
438  , _capacity(field_.len())
439  {
440  set(field_);
441  }
442 
443  void set(const Field& field_)
444  {
445  // Are we already the same
446  if (_data == field_.data() || operator==(field_))
447  {
448  return;
449  }
450  // Reset self
451  notValid();
452  // Make self a copy
453  deepCopy(field_);
454  _capacity = _len;
455  bool foundSeparator = false;
456  bool foundClose = false;
457  for (size_t i = 0; i < _len; ++i)
458  {
459  switch (_data[i])
460  {
461  case '(':
462  case '[':
463  {
464  // Is this within the range?
465  if (foundClose || _open != AMPS_UNSET_INDEX)
466  {
467  notValid();
468  return;
469  }
470  _open = i;
471  }
472  break;
473  // Valid bookmark characters [0-9|,TZ]
474  case '0':
475  case '1':
476  case '2':
477  case '3':
478  case '4':
479  case '5':
480  case '6':
481  case '7':
482  case '8':
483  case '9':
484  case '|':
485  case ',':
486  case 'T':
487  case 'Z':
488  {
489  // Is this within the range?
490  if (foundClose || _open == AMPS_UNSET_INDEX)
491  {
492  notValid();
493  return;
494  }
495  else if (foundSeparator) // Part of end?
496  {
497  if (!_end.data()) // Start of end?
498  {
499  _end.assign(_data + i, 0);
500  }
501  }
502  else if (!_start.data()) // Start of start?
503  {
504  _start.assign(_data + i, 0);
505  }
506  }
507  break;
508  case ':':
509  {
510  // Is this within the range and do we have a start?
511  if (foundSeparator || foundClose || _open == AMPS_UNSET_INDEX
512  || !_start.data())
513  {
514  notValid();
515  return;
516  }
517  foundSeparator = true;
518  // Do we need to set start length?
519  if (_start.len() == 0)
520  {
521  // Length is here, minus beginning of start - 1
522  _start.assign(_start.data(), i - (size_t)(_start.data() - _data));
523  }
524  }
525  break;
526  case ']':
527  case ')':
528  {
529  // Is this within the range and do we have an end?
530  if (foundClose || _open == AMPS_UNSET_INDEX || !_end.data())
531  {
532  notValid();
533  return;
534  }
535  foundClose = true;
536  _len = i + 1;
537  // Do we need to set end length?
538  if (_end.len() == 0)
539  {
540  // Length is here, minus beginning of end - 1
541  _end.assign(_end.data(), i - (size_t)(_end.data() - _data));
542  }
543  }
544  break;
545  case ' ':
546  {
547  // Do we need to set end length?
548  if (_end.data() && _end.len() == 0)
549  {
550  // Length is here, minus beginning of end - 1
551  _end.assign(_end.data(), i - (size_t)(_end.data() - _data));
552  }
553  // Else do we need to set start length?
554  else if (_start.data() && _start.len() == 0)
555  {
556  // Length is here, minus beginning of start - 1
557  _start.assign(_start.data(), i - (size_t)(_start.data() - _data));
558  }
559  }
560  break;
561  default:
562  {
563  notValid();
564  }
565  break;
566  }
567  }
568  // If we didn't find everything clear self
569  if (_start.empty() || _end.empty())
570  {
571  notValid();
572  }
573  }
574 
575  // Centralized place to clear self
576  void notValid()
577  {
578  if (!_data || !_len)
579  {
580  return;
581  }
582  free((void*)_data);
583  _data = 0;
584  _len = 0;
585  _start.assign(0, 0);
586  _end.assign(0, 0);
587  _open = AMPS_UNSET_INDEX;
588  _capacity = 0;
589  }
590 
593  bool isValid() const
594  {
595  return !empty();
596  }
597 
600  bool isStartInclusive() const
601  {
602  return _data[_open] == '[';
603  }
604 
607  void makeStartExclusive()
608  {
609  const_cast<char*>(_data)[_open] = '(';
610  }
611 
614  bool isEndInclusive() const
615  {
616  return _data[_len - 1] == ']';
617  }
618 
621  void makeEndExclusive()
622  {
623  const_cast<char*>(_data)[_len - 1] = ')';
624  }
625 
628  const Field& getStart() const
629  {
630  return _start;
631  }
632 
633  const Field& getEnd() const
634  {
635  return _end;
636  }
637 
640  void replaceStart(const Field& start_, bool makeExclusive_ = false)
641  {
642  // Best case, it fits in our existing self. Since we may do this more
643  // than once, just add start+end+open+close+separator
644  if (_capacity >= (start_.len() + _end.len() + 3))
645  {
646  char* data = const_cast<char*>(_data);
647  if (makeExclusive_)
648  {
649  data[_open] = '(';
650  }
651  if (_open) // Move open to beginning if not there
652  {
653  data[0] = _data[_open];
654  _open = 0;
655  }
656  if ((size_t)(_end.data() - _data - 2) < start_.len())
657  {
658  size_t newLen = start_.len() + _end.len() + 3;
659  // Need to move end to make room for new start
660  // This copies _end and close
661  // Last char of _start will be at _start.len() because of open
662  for (size_t dest = newLen - 1, src = _len - 1;
663  src > _start.len(); --src, --dest)
664  {
665  data[dest] = _data[src];
666  // Find separator, we're done
667  if (data[src] == ':')
668  {
669  _end.assign(data + dest + 1, _end.len());
670  break;
671  }
672  }
673  _len = newLen;
674  }
675  // Copy in new start after _open
676  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
677  _start.assign(data + 1, start_.len());
678  // Possibly move end left
679  if ((size_t)(_end.data() - _start.data()) > _start.len() + 1UL)
680  {
681  // Copy to just after start starting with ':'
682  for (size_t dest = _start.len() + 1, src = (size_t)(_end.data() - data - 1);
683  src < _len; ++dest, ++src)
684  {
685  data[dest] = data[src];
686  if (data[src] == ']' || data[src] == ')')
687  {
688  _end.assign(data + _start.len() + 2, _end.len());
689  break;
690  }
691  }
692  _len = _start.len() + _end.len() + 3;
693  }
694  }
695  else // We need to resize and copy everything over
696  {
697  // Let's set min resize at 4 bookmarks + 3 commas + 3
698  // Some MSVC versions have issues with max so use ?:
699  _capacity = (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL
700  >= start_.len() + _end.len() + 3UL)
701  ? (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL)
702  : (start_.len() + _end.len() + 3UL);
703  char* data = (char*)malloc(_capacity);
704  if (!data)
705  {
706  throw AMPSException("Failed to allocate memory for replacement field", AMPS_E_MEMORY);
707  }
708  if (makeExclusive_)
709  {
710  data[0] = '(';
711  }
712  else
713  {
714  data[0] = _data[_open];
715  }
716  _open = 0;
717  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
718  _start.assign(data + 1, start_.len());
719  data[start_.len() + 1] = ':';
720  ::memcpy(static_cast<void*>(data + start_.len() + 2), _end.data(),
721  _end.len());
722  _end.assign(data + start_.len() + 2, _end.len());
723  size_t len = start_.len() + 3 + _end.len();
724  data[len - 1] = _data[_len - 1];
725  clear();
726  assign(data, len);
727  }
728  }
729 
730  private:
731  Field _start;
732  Field _end;
733  size_t _open;
734  size_t _capacity;
735  };
736 
737 }
738 
739 #endif
740 
bool operator==(const char *rhs_) const
String comparison operator Returns `true&#39; if self and rhs are equivalent, `false&#39; otherwise...
Definition: Field.hpp:161
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: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 Field &rhs_) const
Comparison operator Returns `true&#39; if self and rhs are equivalent, `false&#39; 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:266
static Field stringCopy(const char *str_)
Makes a copy of str_ in a new Field.
Definition: Field.hpp:253
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:279
Base class for all exceptions in AMPS.
Definition: AMPSException.hpp:40
A memory error occurred.
Definition: amps.h:225
bool empty() const
Returns &#39;true&#39; if empty, &#39;false&#39; otherwise.
Definition: Field.hpp:129
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:286
bool operator!=(const char *rhs_) const
String comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:191
Field represents the value of a single field in a Message.
Definition: Field.hpp:87
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:219
Definition: AMPSException.hpp:32
bool operator==(const std::string &rhs_) const
String comparison operator Returns `true&#39; if self and rhs are equivalent.
Definition: Field.hpp:211
Field deepCopy() const
Makes a deep copy of self, returns it.
Definition: Field.hpp:243