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
amps.h
Core type and function declarations for the AMPS C client.
AMPS_E_MEMORY
@ AMPS_E_MEMORY
A memory error occurred.
Definition: amps.h:225
AMPS::AMPSException
Base class for all exceptions in AMPS.
Definition: AMPSException.hpp:41
AMPS::Field
Field represents the value of a single field in a Message.
Definition: Field.hpp:88
AMPS::Field::operator==
bool operator==(const Field &rhs_) const
Comparison operator Returns ‘true’ if self and rhs are equivalent, ‘false’ otherwise.
Definition: Field.hpp:147
AMPS::Field::clear
void clear()
Deletes the data associated with this Field, should only be used on Fields that were created as deepC...
Definition: Field.hpp:266
AMPS::Field::operator==
bool operator==(const char *rhs_) const
String comparison operator Returns ‘true’ if self and rhs are equivalent, ‘false’ otherwise.
Definition: Field.hpp:161
AMPS::Field::empty
bool empty() const
Returns 'true' if empty, 'false' otherwise.
Definition: Field.hpp:129
AMPS::Field::deepCopy
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:219
AMPS::Field::len
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:286
AMPS::Field::operator!=
bool operator!=(const std::string &rhs_) const
String comparison operator Returns ‘true’ if self and rhs are not equivalent.
Definition: Field.hpp:201
AMPS::Field::operator!=
bool operator!=(const Field &rhs_) const
Comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:177
AMPS::Field::operator==
bool operator==(const std::string &rhs_) const
String comparison operator Returns ‘true’ if self and rhs are equivalent.
Definition: Field.hpp:211
AMPS::Field::stringCopy
static Field stringCopy(const char *str_)
Makes a copy of str_ in a new Field.
Definition: Field.hpp:253
AMPS::Field::data
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:279
AMPS::Field::deepCopy
Field deepCopy() const
Makes a deep copy of self, returns it.
Definition: Field.hpp:243
AMPS::Field::operator!=
bool operator!=(const char *rhs_) const
String comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:191