Skip to content

Commit eb6ee17

Browse files
committed
Speed up Pointer::operator<().
Speed is more important than alphabetical order (which makes few sense in JSON in general, and with pointers especially). The use case is indexing in std containers, i.e. O(log n) with rbtree, so the faster comparison the better.
1 parent 0e34ed4 commit eb6ee17

File tree

2 files changed

+63
-59
lines changed

2 files changed

+63
-59
lines changed

include/rapidjson/pointer.h

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -366,20 +366,21 @@ class GenericPointer {
366366
if (!rhs.IsValid())
367367
return true;
368368

369-
const Token *lTok = tokens_, *const lEnd = lTok + tokenCount_,
370-
*rTok = rhs.tokens_, *const rEnd = rTok + rhs.tokenCount_;
371-
for (; lTok != lEnd && rTok != rEnd; ++lTok, ++rTok) {
372-
if (lTok->index != rTok->index)
373-
return lTok->index < rTok->index;
374-
375-
if (lTok->length > rTok->length)
376-
return std::memcmp(lTok->name, rTok->name, sizeof(Ch) * rTok->length) < 0;
377-
378-
int comp = std::memcmp(lTok->name, rTok->name, sizeof(Ch) * lTok->length);
379-
if (comp || lTok->length != rTok->length)
380-
return comp <= 0;
369+
if (tokenCount_ != rhs.tokenCount_)
370+
return tokenCount_ < rhs.tokenCount_;
371+
372+
for (size_t i = 0; i < tokenCount_; i++) {
373+
if (tokens_[i].index != rhs.tokens_[i].index)
374+
return tokens_[i].index < rhs.tokens_[i].index;
375+
376+
if (tokens_[i].length != rhs.tokens_[i].length)
377+
return tokens_[i].length < rhs.tokens_[i].length;
378+
379+
if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length))
380+
return cmp < 0;
381381
}
382-
return rTok != rEnd;
382+
383+
return false;
383384
}
384385

385386
//@}

test/unittest/pointertest.cpp

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "rapidjson/stringbuffer.h"
1818
#include "rapidjson/ostreamwrapper.h"
1919
#include <sstream>
20-
#include <set>
20+
#include <map>
2121

2222
using namespace rapidjson;
2323

@@ -1496,72 +1496,75 @@ TEST(Pointer, Ambiguity) {
14961496
}
14971497

14981498
TEST(Pointer, LessThan) {
1499-
static const char *pointers[] = {
1499+
static const struct {
1500+
const char *str;
1501+
bool valid;
1502+
} pointers[] = {
1503+
{ "/a/b", true },
1504+
{ "/a", true },
1505+
{ "/d/1", true },
1506+
{ "/d/2/z", true },
1507+
{ "/d/2/3", true },
1508+
{ "/d/2", true },
1509+
{ "/a/c", true },
1510+
{ "/e/f~g", false },
1511+
{ "/d/2/zz", true },
1512+
{ "/d/1", true },
1513+
{ "/d/2/z", true },
1514+
{ "/e/f~~g", false },
1515+
{ "/e/f~0g", true },
1516+
{ "/e/f~1g", true },
1517+
{ "/e/f.g", true },
1518+
{ "", true }
1519+
};
1520+
static const char *ordered_pointers[] = {
1521+
"",
1522+
"/a",
15001523
"/a/b",
15011524
"/a/c",
1502-
"/a",
15031525
"/d/1",
1504-
"/d/2/z",
1505-
"/d/2/3",
1506-
"/d/2",
1507-
"/d/2/zz",
15081526
"/d/1",
1509-
"/d/2/z",
1510-
"/e/f~g",
1511-
"/e/f~0g",
1512-
"/e/f~1g",
1513-
"/e/f~~g",
1514-
"#/e/f%2fg",
1527+
"/d/2",
15151528
"/e/f.g",
1516-
""
1517-
};
1518-
static const struct {
1519-
const char *str;
1520-
bool valid;
1521-
} ordered_pointers[] = {
1522-
{ "", true },
1523-
{ "/a", true },
1524-
{ "/a/b", true },
1525-
{ "/a/c", true },
1526-
{ "/d/1", true },
1527-
{ "/d/1", true },
1528-
{ "/d/2", true },
1529-
{ "/d/2/3", true },
1530-
{ "/d/2/z", true },
1531-
{ "/d/2/z", true },
1532-
{ "/d/2/zz", true },
1533-
{ "/e/f.g", true },
1534-
{ "/e/f~1g", true },
1535-
{ "/e/f~1g", true }, // was "#/e/f%2fg"
1536-
{ "/e/f~0g", true },
1537-
{ "/e/f~g", false },
1538-
{ "/e/f~~g", false }
1529+
"/e/f~1g",
1530+
"/e/f~0g",
1531+
"/d/2/3",
1532+
"/d/2/z",
1533+
"/d/2/z",
1534+
"/d/2/zz",
1535+
NULL, // was invalid "/e/f~g"
1536+
NULL // was invalid "/e/f~~g"
15391537
};
15401538
typedef MemoryPoolAllocator<> AllocatorType;
15411539
typedef GenericPointer<Value, AllocatorType> PointerType;
1542-
typedef std::multiset<PointerType> PointerSet;
1540+
typedef std::multimap<PointerType, size_t> PointerMap;
1541+
PointerMap map;
1542+
PointerMap::iterator it;
15431543
AllocatorType allocator;
1544-
PointerSet set;
15451544
size_t i;
15461545

15471546
EXPECT_EQ(sizeof(pointers) / sizeof(pointers[0]),
15481547
sizeof(ordered_pointers) / sizeof(ordered_pointers[0]));
15491548

15501549
for (i = 0; i < sizeof(pointers) / sizeof(pointers[0]); ++i) {
1551-
set.insert(PointerType(pointers[i], &allocator));
1550+
it = map.insert(PointerMap::value_type(PointerType(pointers[i].str, &allocator), i));
1551+
if (!it->first.IsValid()) {
1552+
EXPECT_EQ(++it, map.end());
1553+
}
15521554
}
15531555

1554-
i = 0;
1555-
for (PointerSet::iterator it = set.begin(); it != set.end(); ++it) {
1556+
for (i = 0, it = map.begin(); it != map.end(); ++it, ++i) {
1557+
EXPECT_TRUE(it->second < sizeof(pointers) / sizeof(pointers[0]));
1558+
EXPECT_EQ(it->first.IsValid(), pointers[it->second].valid);
15561559
EXPECT_TRUE(i < sizeof(ordered_pointers) / sizeof(ordered_pointers[0]));
1557-
EXPECT_EQ(it->IsValid(), ordered_pointers[i].valid);
1558-
if (it->IsValid()) {
1560+
EXPECT_EQ(it->first.IsValid(), !!ordered_pointers[i]);
1561+
if (it->first.IsValid()) {
15591562
std::stringstream ss;
15601563
OStreamWrapper os(ss);
1561-
EXPECT_TRUE(it->Stringify(os));
1562-
EXPECT_EQ(ss.str(), ordered_pointers[i].str);
1564+
EXPECT_TRUE(it->first.Stringify(os));
1565+
EXPECT_EQ(ss.str(), pointers[it->second].str);
1566+
EXPECT_EQ(ss.str(), ordered_pointers[i]);
15631567
}
1564-
i++;
15651568
}
15661569
}
15671570

0 commit comments

Comments
 (0)