Skip to content

Commit 26cf54c

Browse files
authored
add string-keyed set and unordered_set (alibaba#1207)
1 parent f26b81a commit 26cf54c

2 files changed

Lines changed: 194 additions & 6 deletions

File tree

common/string-keyed.h

Lines changed: 128 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ limitations under the License.
2121
#include <photon/common/hash_combine.h>
2222
#include <cstring>
2323
#include <unordered_map>
24+
#include <unordered_set>
2425
#include <map>
26+
#include <set>
2527

2628
// string_key is a string_view with dedicated storage,
2729
// primarily used as stored keys in std::map and std::unordered_map,
@@ -192,10 +194,8 @@ template<class T,
192194
using unordered_map_string_key_case_insensitive = basic_map_string_key<
193195
std::unordered_map<string_key, T, Hasher, KeyEqual, Alloc>>;
194196

195-
template<class T,
196-
class Pred = std::less<string_key>,
197-
class Alloc = std::allocator<std::pair<const string_key,T>>>
198-
class map_string_key : public basic_map_string_key<
197+
template<class T, class Pred, class Alloc>
198+
class ordered_map_string_key : public basic_map_string_key<
199199
std::map<string_key, T, Pred, Alloc>>
200200
{
201201
public:
@@ -225,6 +225,11 @@ class map_string_key : public basic_map_string_key<
225225
}
226226
};
227227

228+
template<class T,
229+
class Pred = std::less<string_key>,
230+
class Alloc = std::allocator<std::pair<const string_key,T>>>
231+
using map_string_key = ordered_map_string_key<T, Pred, Alloc>;
232+
228233
class Less_CaseInsensitive {
229234
public:
230235
bool operator()(std::string_view a, std::string_view b) const {
@@ -239,8 +244,125 @@ class Less_CaseInsensitive {
239244
template<class T,
240245
class Pred = Less_CaseInsensitive,
241246
class Alloc = std::allocator<std::pair<const string_key,T>>>
242-
using map_string_key_case_insensitive = basic_map_string_key<
243-
std::map<string_key, T, Pred, Alloc>>;
247+
using map_string_key_case_insensitive = ordered_map_string_key<
248+
T, Pred, Alloc>;
249+
250+
// The basic class for sets with string keys, aims to avoid temp
251+
// std::string construction in queries, by accepting string_views.
252+
// S is the underlying set, either std::set or std::unordered_set
253+
template <class S>
254+
class basic_set_string_key : public S
255+
{
256+
public:
257+
using base = S;
258+
using typename base::const_iterator;
259+
using typename base::iterator;
260+
using typename base::size_type;
261+
using base::base;
262+
263+
using base::erase;
264+
265+
using key_type = std::string_view;
266+
using value_type = string_key;
267+
268+
iterator find ( const key_type& k )
269+
{
270+
return base::find((const string_key&)k);
271+
}
272+
const_iterator find ( const key_type& k ) const
273+
{
274+
return base::find((const string_key&)k);
275+
}
276+
size_type count ( const key_type& k ) const
277+
{
278+
return base::count((const string_key&)k);
279+
}
280+
std::pair<iterator,iterator> equal_range ( const key_type& k )
281+
{
282+
return base::equal_range((const string_key&)k);
283+
}
284+
std::pair<const_iterator,const_iterator> equal_range ( const key_type& k ) const
285+
{
286+
return base::equal_range((const string_key&)k);
287+
}
288+
std::pair<iterator,bool> emplace ( const key_type& k )
289+
{
290+
return base::emplace((const string_key&)k);
291+
}
292+
template <class... Args>
293+
std::pair<iterator,bool> emplace ( const key_type& k, Args&&... args )
294+
{
295+
return base::emplace((const string_key&)k, std::forward<Args>(args)...);
296+
}
297+
template <class... Args>
298+
iterator emplace_hint ( const_iterator position, const key_type& k, Args&&... args )
299+
{
300+
return base::emplace_hint(position, (const string_key&)k, std::forward<Args>(args)...);
301+
}
302+
std::pair<iterator,bool> insert ( const key_type& k )
303+
{
304+
return emplace(k);
305+
}
306+
iterator insert ( const_iterator hint, const key_type& k )
307+
{
308+
return emplace_hint(hint, k);
309+
}
310+
size_type erase ( const std::string_view& k )
311+
{
312+
return base::erase((const string_key&)k);
313+
}
314+
};
315+
316+
template<class Hasher = std::hash<std::string_view>,
317+
class KeyEqual = std::equal_to<std::string_view>,
318+
class Alloc = std::allocator<string_key>>
319+
using unordered_set_string_key = basic_set_string_key<
320+
std::unordered_set<string_key, Hasher, KeyEqual, Alloc>>;
321+
322+
template<class Hasher = Hasher_CaseInsensitive,
323+
class KeyEqual = Equal_CaseInsensitive,
324+
class Alloc = std::allocator<string_key>>
325+
using unordered_set_string_key_case_insensitive = basic_set_string_key<
326+
std::unordered_set<string_key, Hasher, KeyEqual, Alloc>>;
327+
328+
// ordered set with string keys, provides lower_bound/upper_bound
329+
template <class S>
330+
class ordered_set_string_key : public basic_set_string_key<S>
331+
{
332+
public:
333+
using base = basic_set_string_key<S>;
334+
using typename base::key_type;
335+
using typename base::const_iterator;
336+
using typename base::iterator;
337+
using base::base;
338+
339+
iterator lower_bound (const key_type& k)
340+
{
341+
return base::lower_bound((const string_key&)k);
342+
}
343+
const_iterator lower_bound (const key_type& k) const
344+
{
345+
return base::lower_bound((const string_key&)k);
346+
}
347+
iterator upper_bound (const key_type& k)
348+
{
349+
return base::upper_bound((const string_key&)k);
350+
}
351+
const_iterator upper_bound (const key_type& k) const
352+
{
353+
return base::upper_bound((const string_key&)k);
354+
}
355+
};
356+
357+
template<class Pred = std::less<string_key>,
358+
class Alloc = std::allocator<string_key>>
359+
using set_string_key = ordered_set_string_key<
360+
std::set<string_key, Pred, Alloc>>;
361+
362+
template<class Pred = Less_CaseInsensitive,
363+
class Alloc = std::allocator<string_key>>
364+
using set_string_key_case_insensitive = ordered_set_string_key<
365+
std::set<string_key, Pred, Alloc>>;
244366

245367
// the String Key-Value (Mutable), stored together
246368
// in a consecutive area, so as to save one allocation

common/test/test.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,72 @@ TEST(string_key, case_insensitive) {
13491349
test_map_case_insensitive<map_string_kv_case_insensitive>();
13501350
}
13511351

1352+
template<typename S> static
1353+
void basic_set_test(S& test_set) {
1354+
const auto& const_set = test_set;
1355+
std::string prefix = "seggwrg90if908234j5rlkmx.c,bnmi7890wer1234rbdfb";
1356+
1357+
for (int i = 0; i < 100; i++) {
1358+
std::string s = prefix + std::to_string(i);
1359+
test_set.insert(s);
1360+
}
1361+
EXPECT_EQ(test_set.size(), 100);
1362+
1363+
for (int i = 0; i < 100; i++) {
1364+
std::string s = prefix + std::to_string(i);
1365+
EXPECT_NE(test_set.find(s), test_set.end());
1366+
EXPECT_EQ(test_set.count(s), 1);
1367+
EXPECT_NE(const_set.find(s), const_set.end());
1368+
EXPECT_EQ(const_set.count(s), 1);
1369+
}
1370+
for (int i = 100; i < 200; i++) {
1371+
std::string s = prefix + std::to_string(i);
1372+
EXPECT_EQ(test_set.find(s), test_set.end());
1373+
EXPECT_EQ(test_set.count(s), 0);
1374+
}
1375+
1376+
test_set.erase(prefix + "50");
1377+
EXPECT_EQ(test_set.size(), 99);
1378+
EXPECT_EQ(test_set.count(prefix + "50"), 0);
1379+
1380+
test_set.clear();
1381+
EXPECT_EQ(test_set.size(), 0);
1382+
}
1383+
1384+
TEST(string_key, unordered_set_string_key) {
1385+
unordered_set_string_key<> test_set;
1386+
basic_set_test(test_set);
1387+
}
1388+
1389+
TEST(string_key, set_string_key) {
1390+
set_string_key<> test_set;
1391+
basic_set_test(test_set);
1392+
1393+
std::string prefix = "seggwrg90if908234j5rlkmx.c,bnmi7890wer1234rbdfb";
1394+
EXPECT_EQ(test_set.lower_bound(prefix + "2"), test_set.find(prefix + "2"));
1395+
EXPECT_EQ(test_set.lower_bound(prefix + "1"), test_set.find(prefix + "2"));
1396+
EXPECT_EQ(test_set.upper_bound(prefix + "22"), test_set.find(prefix + "23"));
1397+
1398+
const auto& const_set = test_set;
1399+
EXPECT_EQ(const_set.lower_bound(prefix + "2"), const_set.find(prefix + "2"));
1400+
EXPECT_EQ(const_set.upper_bound(prefix + "22"), const_set.find(prefix + "23"));
1401+
}
1402+
1403+
template<typename S> static
1404+
void test_set_case_insensitive() {
1405+
S s;
1406+
s.insert("asdf");
1407+
auto it = s.find("ASDF");
1408+
EXPECT_NE(it, s.end());
1409+
EXPECT_EQ(*it, "asdf");
1410+
EXPECT_EQ(s.count("kuherqf"), 0);
1411+
}
1412+
1413+
TEST(string_key, set_case_insensitive) {
1414+
test_set_case_insensitive<unordered_set_string_key_case_insensitive<>>();
1415+
test_set_case_insensitive<set_string_key_case_insensitive<>>();
1416+
}
1417+
13521418
TEST(RangeLock, Basic) {
13531419
RangeLock m;
13541420

0 commit comments

Comments
 (0)