AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.5.0
Field.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2025 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  free((void*)_data);
221  if (orig_._len > 0)
222  {
223  _data = (char*)malloc(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  static Field stringCopy(const char* str_)
248  {
249 #ifdef _WIN32
250  Field newField(_strdup(str_));
251 #else
252  Field newField(strdup(str_));
253 #endif
254  return newField;
255  }
256 
260  void clear()
261  {
262  if (!_data || !_len)
263  {
264  return;
265  }
266  free((void*)_data);
267  _len = 0;
268  _data = NULL;
269  }
270 
273  const char* data() const
274  {
275  return _data;
276  }
277 
280  size_t len() const
281  {
282  return _len;
283  }
284 
285  // assign a new range into this Message::Field
286  void assign(const char* ptr, size_t len)
287  {
288  _data = ptr;
289  _len = len;
290  }
291 
292  // compute a hash value
293  inline size_t hash_function() const
294  {
295  size_t n_ = _len;
296  const char* p_ = _data;
297  size_t h = 0, c;
298  while (n_ != 0)
299  {
300  c = (unsigned long) * p_;
301  h += (h << 5) + c;
302  ++p_, --n_;
303  }
304  return h;
305  }
306 
307  struct FieldHash
308  {
309  size_t operator()(const Field& f) const
310  {
311  return f.hash_function();
312  }
313 
314  bool operator()(const Field& f1, const Field& f2) const
315  {
316  if (f1.len() < f2.len())
317  {
318  return true;
319  }
320  if (f1.len() > f2.len())
321  {
322  return false;
323  }
324  // Length is the same, don't compare empty
325  if (f1.len() == 0)
326  {
327  return true;
328  }
329  return ::memcmp(f1.data(), f2.data(), f2.len()) < 0;
330  }
331  };
332 
333  // Determine if the Field represents a timestamp
334  static bool isTimestamp(const Field& field_)
335  {
336  return (field_.len() >= AMPS_TIMESTAMP_LEN
337  && field_.len() <= AMPS_TIMESTAMP_LEN_LONG
338  && field_.data()[8] == 'T');
339  }
340 
341  static std::vector<Field> parseBookmarkList(const Field& field_)
342  {
343  std::vector<Field> list;
344  const char* start = field_.data();
345  size_t remain = field_.len();
346  const char* comma = (const char*)memchr((const void*)start,
347  (int)',', remain);
348  while (comma)
349  {
350  size_t len = (size_t)(comma - start);
351  if (len != 0)
352  {
353 #ifdef AMPS_USE_EMPLACE
354  list.emplace_back(Field(start, len));
355 #else
356  list.push_back(Field(start, len));
357 #endif
358  }
359  start = ++comma;
360  remain = field_.len() - (size_t)(start - field_.data());
361  comma = (const char*)memchr((const void*)start,
362  (int)',', remain);
363  }
364  if (remain != 0)
365  {
366 #ifdef AMPS_USE_EMPLACE
367  list.emplace_back(Field(start, remain));
368 #else
369  list.push_back(Field(start, remain));
370 #endif
371  }
372  return list;
373  }
374 
375  // Get sequence number from a Field that is a bookmark
376  static void parseBookmark(const Field& field_,
377  amps_uint64_t& publisherId_,
378  amps_uint64_t& sequenceNumber_)
379  {
380  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
381  if (field_.empty())
382  {
383  return;
384  }
385  const char* data = field_.data();
386  size_t len = field_.len();
387  // Can't parse a timestamp
388  if (isTimestamp(field_))
389  {
390  return;
391  }
392  size_t i = 0;
393  for ( ; i < len; ++i)
394  {
395  if (!isdigit(data[i]))
396  {
397  break;
398  }
399  publisherId_ *= 10;
400  publisherId_ += (amps_uint64_t)(data[i] - '0');
401  }
402  // Make sure it's just the | separator
403  if (i < len && data[i] != '|')
404  {
405  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
406  return;
407  }
408  for (i = i + 1; i < len; ++i)
409  {
410  if (!isdigit(data[i]))
411  {
412  break;
413  }
414  sequenceNumber_ *= 10;
415  sequenceNumber_ += (amps_uint64_t)(data[i] - '0');
416  }
417  }
418 
419  };
420 
421  class BookmarkRange : public Field
422  {
423  public:
424  static bool isRange(const Field& bookmark_)
425  {
426  return memchr(bookmark_.data(), ':', bookmark_.len()) != NULL;
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 (makeExclusive_)
705  {
706  data[0] = '(';
707  }
708  else
709  {
710  data[0] = _data[_open];
711  }
712  _open = 0;
713  ::memcpy(static_cast<void*>(data + 1), start_.data(), start_.len());
714  _start.assign(data + 1, start_.len());
715  data[start_.len() + 1] = ':';
716  ::memcpy(static_cast<void*>(data + start_.len() + 2), _end.data(),
717  _end.len());
718  _end.assign(data + start_.len() + 2, _end.len());
719  size_t len = start_.len() + 3 + _end.len();
720  data[len - 1] = _data[_len - 1];
721  clear();
722  assign(data, len);
723  }
724  }
725 
726  private:
727  Field _start;
728  Field _end;
729  size_t _open;
730  size_t _capacity;
731  };
732 
733 }
734 
735 #endif
736 
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:260
static Field stringCopy(const char *str_)
Makes a copy of str_ in a new Field.
Definition: Field.hpp:247
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:273
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:280
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:103
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