37
37
#include < velocypack/Buffer.h>
38
38
#include < velocypack/Iterator.h>
39
39
#include < velocypack/Slice.h>
40
+ #include < velocypack/StringRef.h>
40
41
#include < velocypack/velocypack-aliases.h>
41
42
42
43
#include < ctime>
43
44
#include < iomanip>
44
- #include < regex>
45
45
46
46
using namespace arangodb ::consensus;
47
47
using namespace arangodb ::basics;
48
48
49
- // / Non-Emptyness of string
50
- struct NotEmpty {
51
- bool operator ()(const std::string& s) { return !s.empty (); }
52
- };
53
-
54
- // / @brief Split strings by separator
55
- inline static std::vector<std::string> split (const std::string& str, char separator) {
56
- std::vector<std::string> result;
57
- if (str.empty ()) {
58
- return result;
59
- }
60
- std::regex reg (" /+" );
61
- std::string key = std::regex_replace (str, reg, " /" );
62
-
63
- if (!key.empty () && key.front () == ' /' ) {
64
- key.erase (0 , 1 );
65
- }
66
- if (!key.empty () && key.back () == ' /' ) {
67
- key.pop_back ();
68
- }
69
-
70
- std::string::size_type p = 0 ;
71
- std::string::size_type q;
72
- while ((q = key.find (separator, p)) != std::string::npos) {
73
- result.emplace_back (key, p, q - p);
74
- p = q + 1 ;
75
- }
76
- result.emplace_back (key, p);
77
- result.erase (std::find_if (result.rbegin (), result.rend (), NotEmpty ()).base (),
78
- result.end ());
79
- return result;
80
- }
81
-
82
49
// / Build endpoint from URL
83
- inline static bool endpointPathFromUrl (std::string const & url,
84
- std::string& endpoint, std::string& path) {
50
+ static bool endpointPathFromUrl (std::string const & url,
51
+ std::string& endpoint, std::string& path) {
85
52
std::stringstream ep;
86
53
path = " /" ;
87
54
size_t pos = 7 ;
@@ -405,7 +372,7 @@ check_ret_t Store::check(VPackSlice const& slice, CheckMode mode) const {
405
372
for (auto const & precond : VPackObjectIterator (slice)) { // Preconditions
406
373
407
374
std::string key = precond.key .copyString ();
408
- std::vector<std::string> pv = split (key, ' / ' );
375
+ std::vector<std::string> pv = split (key);
409
376
410
377
Node const * node = &Node::dummyNode ();
411
378
@@ -578,7 +545,7 @@ bool Store::read(VPackSlice const& query, Builder& ret) const {
578
545
MUTEX_LOCKER (storeLocker, _storeLock); // Freeze KV-Store for read
579
546
if (query_strs.size () == 1 ) {
580
547
auto const & path = query_strs[0 ];
581
- std::vector<std::string> pv = split (path, ' / ' );
548
+ std::vector<std::string> pv = split (path);
582
549
// Build surrounding object structure:
583
550
size_t e = _node.exists (pv).size (); // note: e <= pv.size()!
584
551
size_t i = 0 ;
@@ -599,7 +566,7 @@ bool Store::read(VPackSlice const& query, Builder& ret) const {
599
566
// Create response tree
600
567
Node copy (" copy" );
601
568
for (auto const & path : query_strs) {
602
- std::vector<std::string> pv = split (path, ' / ' );
569
+ std::vector<std::string> pv = split (path);
603
570
size_t e = _node.exists (pv).size ();
604
571
if (e == pv.size ()) { // existing
605
572
copy (pv) = _node (pv);
@@ -652,7 +619,7 @@ void Store::dumpToBuilder(Builder& builder) const {
652
619
MUTEX_LOCKER (storeLocker, _storeLock);
653
620
toBuilder (builder, true );
654
621
655
- std::map<std::string, int64_t > clean {} ;
622
+ std::map<std::string, int64_t > clean;
656
623
for (auto const & i : _timeTable) {
657
624
auto ts = std::chrono::duration_cast<std::chrono::seconds>(
658
625
i.first .time_since_epoch ()).count ();
@@ -688,41 +655,78 @@ void Store::dumpToBuilder(Builder& builder) const {
688
655
689
656
// / Apply transaction to key value store. Guarded by caller
690
657
bool Store::applies (arangodb::velocypack::Slice const & transaction) {
691
- std::vector<std::string> keys;
658
+ _storeLock.assertLockedByCurrentThread ();
659
+
660
+ auto it = VPackObjectIterator (transaction);
661
+
692
662
std::vector<std::string> abskeys;
693
- std::vector<size_t > idx;
694
- std::regex reg (" /+" );
663
+ abskeys.reserve (it.size ());
664
+
665
+ std::vector<std::pair<size_t , VPackSlice>> idx;
666
+ idx.reserve (it.size ());
667
+
695
668
size_t counter = 0 ;
669
+ while (it.valid ()) {
670
+ VPackStringRef key = it.key ().stringRef ();
671
+
672
+ // push back an empty string first, so we can avoid a later move
673
+ abskeys.emplace_back ();
674
+
675
+ // and now work on this string
676
+ std::string& absoluteKey = abskeys.back ();
677
+ absoluteKey.reserve (key.size ());
678
+
679
+ // turn the path into an absolute path, collapsing all duplicate
680
+ // forward slashes into a single forward slash
681
+ char const * p = key.data ();
682
+ char const * e = key.data () + key.size ();
683
+ char last = ' \0 ' ;
684
+ if (p == e || *p != ' /' ) {
685
+ // key must start with '/'
686
+ absoluteKey.push_back (' /' );
687
+ last = ' /' ;
688
+ }
689
+ while (p < e) {
690
+ char current = *p;
691
+ if (current != ' /' || last != ' /' ) {
692
+ // avoid repeated forward slashes
693
+ absoluteKey.push_back (current);
694
+ last = current;
695
+ }
696
+ ++p;
697
+ }
696
698
697
- for (const auto & atom : VPackObjectIterator (transaction)) {
698
- std::string key (atom.key .copyString ());
699
- keys.push_back (key);
700
- key = std::regex_replace (key, reg, " /" );
701
- abskeys.push_back (((key[0 ] == ' /' ) ? key : std::string (" /" ) + key));
702
- idx.push_back (counter++);
703
- }
699
+ TRI_ASSERT (!absoluteKey.empty ());
700
+ TRI_ASSERT (absoluteKey[0 ] == ' /' );
701
+ TRI_ASSERT (absoluteKey.find (" //" ) == std::string::npos);
704
702
705
- sort (idx.begin (), idx.end (),
706
- [&abskeys](size_t i1, size_t i2) { return abskeys[i1] < abskeys[i2]; });
703
+ idx.emplace_back (counter++, it.value ());
707
704
708
- _storeLock.assertLockedByCurrentThread ();
705
+ it.next ();
706
+ }
707
+
708
+ std::sort (idx.begin (), idx.end (),
709
+ [&abskeys](std::pair<size_t , VPackSlice> const & i1, std::pair<size_t , VPackSlice> const & i2) noexcept {
710
+ return abskeys[i1.first ] < abskeys[i2.first ];
711
+ });
709
712
710
713
for (const auto & i : idx) {
711
- std::string const & key = keys.at (i);
712
- Slice value = transaction.get (key);
714
+ Slice value = i.second ;
713
715
714
716
if (value.isObject () && value.hasKey (" op" )) {
715
- if (value.get (" op" ).isEqualString (" delete" ) ||
716
- value.get (" op" ).isEqualString (" replace" ) ||
717
- value.get (" op" ).isEqualString (" erase" )) {
718
- if (!_node.has (abskeys.at (i))) {
717
+ Slice const op = value.get (" op" );
718
+
719
+ if (op.isEqualString (" delete" ) ||
720
+ op.isEqualString (" replace" ) ||
721
+ op.isEqualString (" erase" )) {
722
+ if (!_node.has (abskeys.at (i.first ))) {
719
723
continue ;
720
724
}
721
725
}
722
- auto uri = Node::normalize (abskeys.at (i));
723
- if (value. get ( " op " ) .isEqualString (" observe" )) {
726
+ auto uri = Node::normalize (abskeys.at (i. first ));
727
+ if (op .isEqualString (" observe" )) {
724
728
bool found = false ;
725
- if (value.hasKey ( " url " ) && value. get (" url" ).isString ()) {
729
+ if (value.get (" url" ).isString ()) {
726
730
auto url = value.get (" url" ).copyString ();
727
731
auto ret = _observerTable.equal_range (url);
728
732
for (auto it = ret.first ; it != ret.second ; ++it) {
@@ -736,8 +740,8 @@ bool Store::applies(arangodb::velocypack::Slice const& transaction) {
736
740
_observedTable.emplace (std::pair<std::string, std::string>(uri, url));
737
741
}
738
742
}
739
- } else if (value. get ( " op " ) .isEqualString (" unobserve" )) {
740
- if (value.hasKey ( " url " ) && value. get (" url" ).isString ()) {
743
+ } else if (op .isEqualString (" unobserve" )) {
744
+ if (value.get (" url" ).isString ()) {
741
745
auto url = value.get (" url" ).copyString ();
742
746
auto ret = _observerTable.equal_range (url);
743
747
for (auto it = ret.first ; it != ret.second ; ++it) {
@@ -755,10 +759,10 @@ bool Store::applies(arangodb::velocypack::Slice const& transaction) {
755
759
}
756
760
}
757
761
} else {
758
- _node.hasAsWritableNode (abskeys.at (i)).first .applieOp (value);
762
+ _node.hasAsWritableNode (abskeys.at (i. first )).first .applieOp (value);
759
763
}
760
764
} else {
761
- _node.hasAsWritableNode (abskeys.at (i)).first .applies (value);
765
+ _node.hasAsWritableNode (abskeys.at (i. first )).first .applies (value);
762
766
}
763
767
}
764
768
@@ -884,3 +888,42 @@ void Store::removeTTL(std::string const& uri) {
884
888
}
885
889
}
886
890
}
891
+
892
+ // / @brief Split strings by forward slashes, omitting empty strings
893
+ std::vector<std::string> Store::split (std::string const & str) {
894
+ std::vector<std::string> result;
895
+
896
+ char const * p = str.data ();
897
+ char const * e = str.data () + str.size ();
898
+
899
+ // strip leading forward slashes
900
+ while (p != e && *p == ' /' ) {
901
+ ++p;
902
+ }
903
+
904
+ // strip trailing forward slashes
905
+ while (p != e && *(e - 1 ) == ' /' ) {
906
+ --e;
907
+ }
908
+
909
+ char const * start = nullptr ;
910
+ while (p != e) {
911
+ if (*p == ' /' ) {
912
+ if (start != nullptr ) {
913
+ // had already found something
914
+ result.emplace_back (start, p - start);
915
+ start = nullptr ;
916
+ }
917
+ } else {
918
+ if (start == nullptr ) {
919
+ start = p;
920
+ }
921
+ }
922
+ ++p;
923
+ }
924
+ if (start != nullptr ) {
925
+ result.emplace_back (start, p - start);
926
+ }
927
+
928
+ return result;
929
+ }
0 commit comments