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