AMPS C/C++ Client Class Reference AMPS C/C++ Client Version 5.3.5.1

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
amps.h
Core type and function declarations for the AMPS C client.
AMPS::Field
Field represents the value of a single field in a Message.
Definition: Field.hpp:87
AMPS::Field::operator==
bool operator==(const Field &rhs_) const
Comparison operator Returns ‘true’ if self and rhs are equivalent, ‘false’ otherwise.
Definition: Field.hpp:146
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:260
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:160
AMPS::Field::empty
bool empty() const
Returns 'true' if empty, 'false' otherwise.
Definition: Field.hpp:128
AMPS::Field::deepCopy
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:218
AMPS::Field::len
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:280
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:200
AMPS::Field::operator!=
bool operator!=(const Field &rhs_) const
Comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:176
AMPS::Field::operator==
bool operator==(const std::string &rhs_) const
String comparison operator Returns ‘true’ if self and rhs are equivalent.
Definition: Field.hpp:210
AMPS::Field::stringCopy
static Field stringCopy(const char *str_)
Makes a copy of str_ in a new Field.
Definition: Field.hpp:247
AMPS::Field::data
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:273
AMPS::Field::deepCopy
Field deepCopy() const
Makes a deep copy of self, returns it.
Definition: Field.hpp:237
AMPS::Field::operator!=
bool operator!=(const char *rhs_) const
String comparison operator Returns true if self and rhs are not equivalent.
Definition: Field.hpp:190