AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.3.1
Field.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2021 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 24
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 using std::string;
72 
76 
83 
84 class Field
85 {
86 protected:
87  const char* _data;
88  size_t _len;
89 public:
90  Field() : _data(NULL), _len(0) {;}
91  Field(const char *data_)
92  {
93  _data = data_;
94  _len = ::strlen(data_);
95  }
96  Field(const char *data_,size_t len_)
97  {
98  _data = data_;
99  _len = len_;
100  }
101  Field(const Field& rhs)
102  {
103  _data = rhs._data;
104  _len = rhs._len;
105  }
106  Field& operator=(const Field& rhs)
107  {
108  _data = rhs._data;
109  _len = rhs._len;
110  return *this;
111  }
112  Field(const std::string& string_)
113  {
114  _data = string_.c_str();
115  _len = string_.length();
116  }
117 
118  bool contains(const char* searchString, size_t len) const
119  {
120  const char *dataEnd = _data+_len;
121  return std::search(_data,dataEnd,searchString,searchString+len) != dataEnd;
122  }
123 
126  bool empty () const
127  {
128  return _len == 0;
129  }
130 
134  operator std::string () const
135  {
136  return _len?std::string(_data, _len):std::string();
137  }
138 
144  bool operator==(const Field& rhs_) const
145  {
146  if ( _len == rhs_._len )
147  return ::memcmp(_data, rhs_._data, _len) == 0;
148  return false;
149  }
150 
156  bool operator==(const char* rhs_) const
157  {
158  if (!_data || !rhs_)
159  {
160  return (!_data && !rhs_);
161  }
162  return ::strncmp(_data, rhs_, _len) == 0 && rhs_[_len] == '\0';
163  }
164 
165  bool operator<(const Field& rhs) const;
166 
172  bool operator!=(const Field& rhs_) const
173  {
174  if ( _len == rhs_._len )
175  return ::memcmp(_data, rhs_._data, _len) != 0;
176  return true;
177  }
178 
184  bool operator!=(const char* rhs_) const
185  {
186  return strncmp(_data, rhs_, _len) != 0 || rhs_[_len] != '\0';
187  }
188 
194  bool operator!=(const std::string& rhs_) const
195  {
196  return rhs_.compare(0, rhs_.length(), _data, _len) != 0;
197  }
198 
204  bool operator==(const std::string& rhs_) const
205  {
206  return rhs_.compare(0, rhs_.length(), _data, _len) == 0;
207  }
208 
212  void deepCopy(const Field& orig_)
213  {
214  delete[] _data;
215  if (orig_._len > 0)
216  {
217  _data = new char[orig_._len];
218  ::memcpy(static_cast<void*>(const_cast<char*>(_data)),
219  orig_._data, orig_._len);
220  _len = orig_._len;
221  }
222  else
223  {
224  _data = NULL;
225  _len = 0;
226  }
227  }
228 
231  Field deepCopy() const
232  {
233  Field newField;
234  newField.deepCopy(*this);
235  return newField;
236  }
237 
241  void clear()
242  {
243  if (!_data || !_len) return;
244  delete[] _data;
245  _len = 0;
246  _data = NULL;
247  }
248 
251  const char* data() const
252  {
253  return _data;
254  }
255 
258  size_t len() const
259  {
260  return _len;
261  }
262 
263  // assign a new range into this Message::Field
264  void assign(const char*ptr, size_t len)
265  {
266  _data = ptr;
267  _len = len;
268  }
269 
270  // compute a hash value
271  inline size_t hash_function() const
272  {
273  size_t n_ = _len;
274  const char* p_ = _data;
275  size_t h = 0,c;
276  while (n_ != 0)
277  {
278  c = (unsigned long)*p_;
279  h += (h << 5) + c;
280  ++p_,--n_;
281  }
282  return h;
283  }
284 
285  struct FieldHash
286  {
287  size_t operator()(const Field& f) const
288  {
289  return f.hash_function();
290  }
291 
292  bool operator()(const Field& f1, const Field& f2) const
293  {
294  if (f1.len() < f2.len()) return true;
295  if (f1.len() > f2.len()) return false;
296  // Length is the same, don't compare empty
297  if (f1.len() == 0) return true;
298  return ::memcmp(f1.data(), f2.data(), f2.len())<0;
299  }
300  };
301 
302  // Determine if the Field represents a timestamp
303  static bool isTimestamp(const Field& field_)
304  {
305  return (field_.len() >= AMPS_TIMESTAMP_LEN
306  && field_.len() <= AMPS_TIMESTAMP_LEN_LONG
307  && field_.data()[field_.len()-1] == 'Z'
308  && field_.data()[8] == 'T');
309  }
310 
311  // Get sequence number from a Field that is a bookmark
312  static void parseBookmark(const Field& field_,
313  amps_uint64_t& publisherId_,
314  amps_uint64_t& sequenceNumber_)
315  {
316  publisherId_ = sequenceNumber_ = (amps_uint64_t)0;
317  const char* data = field_.data();
318  size_t len = field_.len();
319  // Can't parse a timestamp
320  if (isTimestamp(field_)) return;
321  size_t i=0;
322  for( ; i<len; ++i)
323  {
324  if(data[i] == '|')
325  {
326  break;
327  }
328  publisherId_ *= 10;
329  publisherId_ += (amps_uint64_t)(data[i] - '0');
330  }
331  for(i=i+1; i<len; ++i)
332  {
333  if(data[i] == '|')
334  {
335  break;
336  }
337  sequenceNumber_ *= 10;
338  sequenceNumber_ += (amps_uint64_t)(data[i] - '0');
339  }
340  }
341 
342 };
343 
344 class BookmarkRange : public Field
345 {
346 public:
347  static bool isRange(const Field& bookmark_)
348  {
349  return memchr(bookmark_.data(), ':', bookmark_.len()) != NULL;
350  }
351 
352  BookmarkRange()
353  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
354  , _capacity(AMPS_UNSET_INDEX)
355  {
356  }
357 
358  // Parse it for a range, set everything empty if not a valid range
359  BookmarkRange(const Field& field_)
360  : Field(), _start(), _end(), _open(AMPS_UNSET_INDEX)
361  , _capacity(field_.len())
362  {
363  set(field_);
364  }
365 
366  void set(const Field& field_)
367  {
368  // Are we already the same
369  if (_data == field_.data() || operator==(field_))
370  {
371  return;
372  }
373  // Reset self
374  notValid();
375  // Make self a copy
376  deepCopy(field_);
377  _capacity = _len;
378  bool foundSeparator = false;
379  bool foundClose = false;
380  for (size_t i=0; i<_len; ++i)
381  {
382  switch (_data[i])
383  {
384  case '(':
385  case '[':
386  {
387  // Is this within the range?
388  if (foundClose || _open != AMPS_UNSET_INDEX)
389  {
390  notValid();
391  return;
392  }
393  _open = i;
394  }
395  break;
396  // Valid bookmark characters [0-9|,TZ]
397  case '0':
398  case '1':
399  case '2':
400  case '3':
401  case '4':
402  case '5':
403  case '6':
404  case '7':
405  case '8':
406  case '9':
407  case '|':
408  case ',':
409  case 'T':
410  case 'Z':
411  {
412  // Is this within the range?
413  if (foundClose || _open==AMPS_UNSET_INDEX)
414  {
415  notValid();
416  return;
417  }
418  else if (foundSeparator) // Part of end?
419  {
420  if (!_end.data()) // Start of end?
421  {
422  _end.assign(_data + i, 0);
423  }
424  }
425  else if (!_start.data()) // Start of start?
426  {
427  _start.assign(_data + i, 0);
428  }
429  }
430  break;
431  case ':':
432  {
433  // Is this within the range and do we have a start?
434  if (foundSeparator || foundClose || _open==AMPS_UNSET_INDEX
435  || !_start.data())
436  {
437  notValid();
438  return;
439  }
440  foundSeparator = true;
441  // Do we need to set start length?
442  if (_start.len() == 0)
443  {
444  // Length is here, minus beginning of start - 1
445  _start.assign(_start.data(), i-(size_t)(_start.data()-_data));
446  }
447  }
448  break;
449  case ']':
450  case ')':
451  {
452  // Is this within the range and do we have an end?
453  if (foundClose || _open==AMPS_UNSET_INDEX || !_end.data())
454  {
455  notValid();
456  return;
457  }
458  foundClose = true;
459  _len = i+1;
460  // Do we need to set end length?
461  if (_end.len() == 0)
462  {
463  // Length is here, minus beginning of end - 1
464  _end.assign(_end.data(), i-(size_t)(_end.data()-_data));
465  }
466  }
467  break;
468  case ' ':
469  {
470  // Do we need to set end length?
471  if (_end.data() && _end.len() == 0)
472  {
473  // Length is here, minus beginning of end - 1
474  _end.assign(_end.data(), i-(size_t)(_end.data()-_data));
475  }
476  // Else do we need to set start length?
477  else if (_start.data() && _start.len() == 0)
478  {
479  // Length is here, minus beginning of start - 1
480  _start.assign(_start.data(), i-(size_t)(_start.data()-_data));
481  }
482  }
483  break;
484  default:
485  {
486  notValid();
487  }
488  break;
489  }
490  }
491  // If we didn't find everything clear self
492  if (_start.empty() || _end.empty())
493  {
494  notValid();
495  }
496  }
497 
498  // Centralized place to clear self
499  void notValid()
500  {
501  if (!_data || !_len) return;
502  delete[] _data;
503  _data = 0;
504  _len = 0;
505  _start.assign(0, 0);
506  _end.assign(0, 0);
507  _open = AMPS_UNSET_INDEX;
508  _capacity = 0;
509  }
510 
513  bool isValid() const { return !empty(); }
514 
517  bool isStartInclusive() const { return _data[_open]=='['; }
518 
521  void makeStartExclusive() { const_cast<char*>(_data)[_open]='('; }
522 
525  bool isEndInclusive() const { return _data[_len-1]==']'; }
526 
529  void makeEndExclusive() { const_cast<char*>(_data)[_len-1]=')'; }
530 
533  const Field& getStart() const { return _start; }
534 
535  const Field& getEnd() const { return _end; }
536 
539  void replaceStart(const Field& start_, bool makeExclusive_ = false)
540  {
541  // Best case, it fits in our existing self. Since we may do this more
542  // than once, just add start+end+open+close+separator
543  if (_capacity >= (start_.len() + _end.len() + 3))
544  {
545  char* data = const_cast<char*>(_data);
546  if (makeExclusive_) data[_open] = '(';
547  if (_open) // Move open to beginning if not there
548  {
549  data[0] = _data[_open];
550  _open=0;
551  }
552  if ((size_t)(_end.data() - _data - 2) < start_.len())
553  {
554  size_t newLen = start_.len() + _end.len() + 3;
555  // Need to move end to make room for new start
556  // This copies _end and close
557  // Last char of _start will be at _start.len() because of open
558  for (size_t dest = newLen-1, src = _len-1;
559  src > _start.len(); --src, --dest)
560  {
561  data[dest] = _data[src];
562  // Find separator, we're done
563  if (data[src] == ':')
564  {
565  _end.assign(data+dest+1, _end.len());
566  break;
567  }
568  }
569  _len = newLen;
570  }
571  // Copy in new start after _open
572  ::memcpy(static_cast<void*>(data+1), start_.data(), start_.len());
573  _start.assign(data+1, start_.len());
574  }
575  else // We need to resize and copy everything over
576  {
577  // Let's set min resize at 4 bookmarks + 3 commas + 3
578  // Some MSVC versions have issues with max so use ?:
579  _capacity = (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL
580  >= start_.len() + _end.len() + 3UL)
581  ? (4UL * AMPS_MAX_BOOKMARK_LEN + 6UL)
582  : (start_.len() + _end.len() + 3UL);
583  char* data = new char[_capacity];
584  if (makeExclusive_) data[0] = '(';
585  else data[0] = _data[_open];
586  _open = 0;
587  ::memcpy(static_cast<void*>(data+1), start_.data(), start_.len());
588  _start.assign(data+1, start_.len());
589  data[start_.len() + 1] = ':';
590  ::memcpy(static_cast<void*>(data+start_.len()+2), _end.data(),
591  _end.len());
592  _end.assign(data+start_.len()+2, _end.len());
593  size_t len = start_.len()+3+_end.len();
594  data[len-1] = _data[_len-1];
595  clear();
596  assign(data, len);
597  }
598  }
599 
600 private:
601  Field _start;
602  Field _end;
603  size_t _open;
604  size_t _capacity;
605 };
606 
607 }
608 
609 #endif
610 
bool operator==(const char *rhs_) const
String comparison operator Returns `true&#39; if self and rhs are equivalent, `false&#39; otherwise...
Definition: Field.hpp:156
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:194
bool operator!=(const Field &rhs_) const
Comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:172
bool operator==(const Field &rhs_) const
Comparison operator Returns `true&#39; if self and rhs are equivalent, `false&#39; otherwise.
Definition: Field.hpp:144
void clear()
Deletes the data associated with this Field, should only be used on Fields that were created as deepC...
Definition: Field.hpp:241
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:251
bool empty() const
Returns &#39;true&#39; if empty, &#39;false&#39; otherwise.
Definition: Field.hpp:126
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:258
bool operator!=(const char *rhs_) const
String comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:184
Field represents the value of a single field in a Message.
Definition: Field.hpp:84
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:212
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:204
Field deepCopy() const
Makes a deep copy of self, returns it.
Definition: Field.hpp:231