Commit 1474b109 authored by Andrei Alexandrescu's avatar Andrei Alexandrescu Committed by Sara Golemon

Add StringKeyed(Unordered){Set,Map} to folly

Summary: We're using StringKeyed* from common/datastruct to avoid unnecessary string creation whenever we're looking up string keys. C++14 does offer a solution, see e.g. http://stackoverflow.com/questions/10536788/avoiding-key-construction-for-stdmapfind. That is not supported by current compilers.

Test Plan: unittests

Reviewed By: pavlo@fb.com

Subscribers: trunkagent, net-systems@, folly-diffs@, yfeldblum

FB internal diff: D1825700

Signature: t1:1825700:1423086724:530550c3c80e33c80900f31c0ade05c66b22cbe8
parent 7eaace04
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
// @author: Pavlo Kushnir (pavlo)
#ifndef FOLLY_EXPERIMENTAL_STRINGKEYEDCOMMON_H_
#define FOLLY_EXPERIMENTAL_STRINGKEYEDCOMMON_H_
#include <memory>
#include <folly/Range.h>
namespace folly {
template <class Alloc>
StringPiece stringPieceDup(StringPiece piece, const Alloc& alloc) {
auto size = piece.size();
auto keyDup = typename Alloc::template rebind<char>::other(alloc)
.allocate(size);
memcpy(keyDup, piece.data(), size * sizeof(typename StringPiece::value_type));
return StringPiece(keyDup, size);
}
template <class Alloc>
void stringPieceDel(StringPiece piece, const Alloc& alloc) {
typename Alloc::template rebind<char>::other(alloc)
.deallocate(const_cast<char*>(piece.data()), piece.size());
}
} // folly
#endif /* FOLLY_EXPERIMENTAL_STRINGKEYEDCOMMON_H_ */
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
// @author: Pavlo Kushnir (pavlo)
#ifndef FOLLY_EXPERIMENTAL_STRINGKEYEDMAP_H_
#define FOLLY_EXPERIMENTAL_STRINGKEYEDMAP_H_
#include <initializer_list>
#include <memory>
#include <map>
#include <folly/Range.h>
#include <folly/experimental/StringKeyedCommon.h>
namespace folly {
/**
* Wrapper class for map<string, Value> that can
* perform lookup operations with StringPiece, not only string.
*
* It uses kind of hack: string pointed by StringPiece is copied when
* StringPiece is inserted into map
*/
template <class Value,
class Compare = std::less<StringPiece>,
class Alloc = std::allocator<std::pair<const StringPiece, Value>>>
class StringKeyedMap
: private std::map<StringPiece, Value, Compare, Alloc> {
private:
using Base = std::map<StringPiece, Value, Compare, Alloc>;
public:
typedef typename Base::key_type key_type;
typedef typename Base::mapped_type mapped_type;
typedef typename Base::value_type value_type;
typedef typename Base::key_compare key_compare;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::iterator iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::reverse_iterator reverse_iterator;
typedef typename Base::const_reverse_iterator const_reverse_iterator;
typedef typename Base::difference_type difference_type;
typedef typename Base::size_type size_type;
using Base::get_allocator;
// Ctors in the same order as
// http://cplusplus.com/reference/map/map/map/
explicit StringKeyedMap(
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
: Base(comp, alloc) {
}
explicit StringKeyedMap(const allocator_type& alloc)
: Base(alloc) {
}
template <class InputIterator>
explicit StringKeyedMap(
InputIterator b, InputIterator e,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
: Base(comp, alloc) {
for (; b != e; ++b) {
// emplace() will carry the duplication
emplace(b->first, b->second);
}
}
StringKeyedMap(const StringKeyedMap& rhs)
: StringKeyedMap(rhs, rhs.get_allocator()) {
}
StringKeyedMap(const StringKeyedMap& rhs, const allocator_type& a)
: StringKeyedMap(rhs.begin(), rhs.end(), rhs.key_comp(), a) {
}
StringKeyedMap(StringKeyedMap&& other) noexcept
: Base(std::move(other)) {
}
StringKeyedMap(StringKeyedMap&& other, const allocator_type& a) noexcept
: Base(std::move(other)/*, a*/ /* not supported by gcc */) {
}
StringKeyedMap(std::initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
: StringKeyedMap(il.begin(), il.end(), comp, alloc) {
}
StringKeyedMap& operator=(const StringKeyedMap& other) & {
if (this == &other) {
return *this;
}
return *this = StringKeyedMap(other);
}
StringKeyedMap& operator=(StringKeyedMap&& other) & noexcept {
assert(this != &other);
clear();
Base::operator=(std::move(other));
return *this;
}
using Base::empty;
using Base::size;
using Base::max_size;
using Base::begin;
using Base::end;
using Base::rbegin;
using Base::rend;
using Base::cbegin;
using Base::cend;
using Base::crbegin;
using Base::crend;
// no need for copy/move overload as StringPiece is small struct
mapped_type& operator[](StringPiece key) {
auto it = find(key);
if (it != end()) {
return it->second;
}
// operator[] will create new (key, value) pair
// we need to allocate memory for key
return Base::operator[](stringPieceDup(key, get_allocator()));
}
using Base::at;
using Base::find;
using Base::lower_bound;
using Base::upper_bound;
template <class... Args>
std::pair<iterator, bool> emplace(StringPiece key, Args&&... args) {
auto it = find(key);
if (it != end()) {
return {it, false};
}
return Base::emplace(stringPieceDup(key, get_allocator()),
std::forward<Args>(args)...);
}
std::pair<iterator, bool> insert(value_type val) {
auto it = find(val.first);
if (it != end()) {
return {it, false};
}
return Base::insert(
std::make_pair(stringPieceDup(val.first, get_allocator()),
std::move(val.second)));
}
iterator erase(const_iterator position) {
auto key = position->first;
auto result = Base::erase(position);
stringPieceDel(key, get_allocator());
return result;
}
size_type erase(StringPiece key) {
auto it = find(key);
if (it == end()) {
return 0;
}
erase(it);
return 1;
}
void clear() noexcept {
for (auto& it : *this) {
stringPieceDel(it.first, get_allocator());
}
Base::clear();
}
~StringKeyedMap() {
// Here we assume that map doesn't use keys in destructor
for (auto& it : *this) {
stringPieceDel(it.first, get_allocator());
}
}
};
} // folly
#endif /* FOLLY_EXPERIMENTAL_STRINGKEYEDMAP_H_ */
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
// @author: Pavlo Kushnir (pavlo)
#ifndef FOLLY_EXPERIMENTAL_STRINGKEYEDSET_H_
#define FOLLY_EXPERIMENTAL_STRINGKEYEDSET_H_
#include <initializer_list>
#include <memory>
#include <set>
#include <folly/Range.h>
#include <folly/experimental/StringKeyedCommon.h>
namespace folly {
/**
* Wrapper class for set<string> that can
* perform lookup operations with StringPiece, not only string.
*
* It uses kind of hack: string pointed by StringPiece is copied when
* StringPiece is inserted into set
*/
template <class Compare = std::less<StringPiece>,
class Alloc = std::allocator<StringPiece>>
class StringKeyedSetBase
: private std::set<StringPiece, Compare, Alloc> {
private:
using Base = std::set<StringPiece, Compare, Alloc>;
public:
typedef typename Base::key_type key_type;
typedef typename Base::value_type value_type;
typedef typename Base::key_compare key_compare;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::iterator iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::reverse_iterator reverse_iterator;
typedef typename Base::const_reverse_iterator const_reverse_iterator;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
explicit StringKeyedSetBase(
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
: Base(comp, alloc) {
}
explicit StringKeyedSetBase(const allocator_type& alloc)
: Base(alloc) {
}
template <class InputIterator>
StringKeyedSetBase(
InputIterator b, InputIterator e,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
: Base(comp, alloc) {
for (; b != e; ++b) {
emplace(*b);
}
}
StringKeyedSetBase(const StringKeyedSetBase& rhs)
: StringKeyedSetBase(rhs, rhs.get_allocator()) {
}
StringKeyedSetBase(const StringKeyedSetBase& rhs,
const allocator_type& a)
: StringKeyedSetBase(rhs.begin(), rhs.end(), rhs.key_comp(), a) {
}
StringKeyedSetBase(StringKeyedSetBase&& other) noexcept
: Base(std::move(other)) {
assert(other.empty());
}
StringKeyedSetBase(StringKeyedSetBase&& other,
const allocator_type& alloc) noexcept
: Base(std::move(other), alloc) {
assert(other.empty());
}
StringKeyedSetBase(
std::initializer_list<value_type> il,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type())
: StringKeyedSetBase(il.begin(), il.end(), comp, alloc) {
}
StringKeyedSetBase& operator=(const StringKeyedSetBase& other) {
if (this == &other) {
return *this;
}
return *this = StringKeyedSetBase(other);
}
StringKeyedSetBase& operator=(StringKeyedSetBase&& other) noexcept {
assert(this != &other);
clear();
Base::operator=(std::move(other));
assert(other.empty());
return *this;
}
using Base::empty;
using Base::size;
using Base::max_size;
using Base::begin;
using Base::end;
using Base::cbegin;
using Base::cend;
using Base::find;
using Base::lower_bound;
using Base::upper_bound;
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
auto key = StringPiece(std::forward<Args>(args)...);
auto it = find(key);
if (it != end()) {
return {it, false};
}
return Base::emplace(stringPieceDup(key, get_allocator()));
}
std::pair<iterator, bool> insert(value_type val) {
auto it = find(val);
if (it != end()) {
return {it, false};
}
return Base::insert(stringPieceDup(val, get_allocator()));
}
iterator erase(const_iterator position) {
auto key = *position;
auto result = Base::erase(position);
stringPieceDel(key, get_allocator());
return result;
}
size_type erase(StringPiece key) {
auto it = find(key);
if (it == end()) {
return 0;
}
erase(it);
return 1;
}
void clear() noexcept {
for (auto it : *this) {
stringPieceDel(it, get_allocator());
}
Base::clear();
}
using Base::get_allocator;
~StringKeyedSetBase() {
// Here we assume that set doesn't use keys in destructor
for (auto it : *this) {
stringPieceDel(it, get_allocator());
}
}
};
using StringKeyedSet = StringKeyedSetBase<>;
} // folly
#endif /* FOLLY_EXPERIMENTAL_STRINGKEYEDSET_H_ */
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
// @author: Pavlo Kushnir (pavlo)
#ifndef FOLLY_EXPERIMENTAL_STRINGKEYEDUNORDEREDMAP_H_
#define FOLLY_EXPERIMENTAL_STRINGKEYEDUNORDEREDMAP_H_
#include <initializer_list>
#include <memory>
#include <unordered_map>
#include <folly/Range.h>
#include <folly/experimental/StringKeyedCommon.h>
namespace folly {
/**
* Wrapper class for unordered_map<string, Value> that can
* perform lookup operations with StringPiece, not only string.
*
* It uses kind of hack: string pointed by StringPiece is copied when
* StringPiece is inserted into map
*/
template <class Value,
class Hash = StringPieceHash,
class Eq = std::equal_to<StringPiece>,
class Alloc = std::allocator<std::pair<const StringPiece, Value>>>
class StringKeyedUnorderedMap
: private std::unordered_map<StringPiece, Value, Hash, Eq, Alloc> {
private:
using Base = std::unordered_map<StringPiece, Value, Hash, Eq, Alloc>;
public:
typedef typename Base::key_type key_type;
typedef typename Base::mapped_type mapped_type;
typedef typename Base::value_type value_type;
typedef typename Base::hasher hasher;
typedef typename Base::key_equal key_equal;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::iterator iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
explicit StringKeyedUnorderedMap() {}
explicit StringKeyedUnorderedMap(
size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: Base(n, hf, eql, alloc) {
}
explicit StringKeyedUnorderedMap(const allocator_type& a)
: Base(a) {
}
template <class InputIterator>
StringKeyedUnorderedMap(InputIterator b, InputIterator e) {
for (; b != e; ++b) {
// insert() will carry the duplication
emplace(b->first, b->second);
}
}
template <class InputIterator>
StringKeyedUnorderedMap(
InputIterator b, InputIterator e,
size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: Base(n, hf, eql, alloc) {
for (; b != e; ++b) {
// insert() will carry the duplication
emplace(b->first, b->second);
}
}
StringKeyedUnorderedMap(const StringKeyedUnorderedMap& rhs)
: StringKeyedUnorderedMap(rhs.begin(), rhs.end(),
rhs.bucket_count(),
rhs.hash_function(),
rhs.key_eq(),
rhs.get_allocator()) {
}
StringKeyedUnorderedMap(StringKeyedUnorderedMap&& rhs) noexcept
: StringKeyedUnorderedMap(std::move(rhs), rhs.get_allocator()) {
}
StringKeyedUnorderedMap(StringKeyedUnorderedMap&& other,
const allocator_type& a) noexcept
: Base(std::move(other)/*, a*/ /* not supported by gcc */) {
}
StringKeyedUnorderedMap(std::initializer_list<value_type> il)
: StringKeyedUnorderedMap(il.begin(), il.end()) {
}
StringKeyedUnorderedMap(
std::initializer_list<value_type> il,
size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: StringKeyedUnorderedMap(il.begin(), il.end(), n, hf, eql, alloc) {
}
StringKeyedUnorderedMap& operator=(const StringKeyedUnorderedMap& other) & {
if (this == &other) {
return *this;
}
return *this = StringKeyedUnorderedMap(other);
}
StringKeyedUnorderedMap&
operator=(StringKeyedUnorderedMap&& other) & noexcept {
assert(this != &other);
clear();
Base::operator=(std::move(other));
return *this;
}
using Base::empty;
using Base::size;
using Base::max_size;
using Base::begin;
using Base::end;
using Base::cbegin;
using Base::cend;
bool operator==(const StringKeyedUnorderedMap& rhs) {
const Base& lhs = *this;
return lhs == rhs;
}
// No need for copy/move overload as StringPiece is small struct.
mapped_type& operator[](StringPiece key) {
auto it = find(key);
if (it != end()) {
return it->second;
}
// operator[] will create new (key, value) pair
// we need to allocate memory for key
return Base::operator[](stringPieceDup(key, get_allocator()));
}
using Base::at;
using Base::find;
template <class... Args>
std::pair<iterator, bool> emplace(StringPiece key, Args&&... args) {
auto it = find(key);
if (it != end()) {
return {it, false};
}
return Base::emplace(stringPieceDup(key, get_allocator()),
std::forward<Args>(args)...);
}
std::pair<iterator, bool> insert(value_type val) {
auto it = find(val.first);
if (it != end()) {
return {it, false};
}
auto valCopy = std::make_pair(stringPieceDup(val.first, get_allocator()),
std::move(val.second));
return Base::insert(valCopy);
}
iterator erase(const_iterator position) {
auto key = position->first;
auto result = Base::erase(position);
stringPieceDel(key, get_allocator());
return result;
}
size_type erase(StringPiece key) {
auto it = find(key);
if (it == end()) {
return 0;
}
erase(it);
return 1;
}
void clear() noexcept {
for (auto& it : *this) {
stringPieceDel(it.first, get_allocator());
}
Base::clear();
}
using Base::reserve;
using Base::hash_function;
using Base::key_eq;
using Base::get_allocator;
using Base::bucket_count;
using Base::max_bucket_count;
using Base::bucket_size;
using Base::bucket;
~StringKeyedUnorderedMap() {
// Here we assume that unordered_map doesn't use keys in destructor
for (auto& it : *this) {
stringPieceDel(it.first, get_allocator());
}
}
};
} // folly
#endif /* FOLLY_EXPERIMENTAL_STRINGKEYEDUNORDEREDMAP_H_ */
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
// @author: Pavlo Kushnir (pavlo)
#ifndef FOLLY_EXPERIMENTAL_STRINGKEYEDUNORDEREDSET_H_
#define FOLLY_EXPERIMENTAL_STRINGKEYEDUNORDEREDSET_H_
#include <initializer_list>
#include <memory>
#include <unordered_set>
#include <folly/Range.h>
#include <folly/experimental/StringKeyedCommon.h>
namespace folly {
/**
* Wrapper class for unordered_set<string> that can
* perform lookup operations with StringPiece, not only string.
*
* It uses kind of hack: string pointed by StringPiece is copied when
* StringPiece is inserted into set
*/
template <class Hasher = StringPieceHash,
class Eq = std::equal_to<StringPiece>,
class Alloc = std::allocator<folly::StringPiece>>
class BasicStringKeyedUnorderedSet
: private std::unordered_set<StringPiece, Hasher, Eq, Alloc> {
using Base = std::unordered_set<StringPiece, Hasher, Eq, Alloc>;
public:
typedef typename Base::key_type key_type;
typedef typename Base::value_type value_type;
typedef typename Base::hasher hasher;
typedef typename Base::key_equal key_equal;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::iterator iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
// Constructors in the same order as in
// http://cplusplus.com/reference/unordered_set/unordered_set/unordered_set/
explicit BasicStringKeyedUnorderedSet() {
}
explicit BasicStringKeyedUnorderedSet(
size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: Base(n, hf, eql, alloc) {
}
explicit BasicStringKeyedUnorderedSet(const allocator_type& alloc)
: Base(alloc) {
}
template <class InputIterator>
BasicStringKeyedUnorderedSet(InputIterator b, InputIterator e) {
for (; b != e; ++b) {
emplace(*b);
}
}
template <class InputIterator>
BasicStringKeyedUnorderedSet(
InputIterator b, InputIterator e,
size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: Base(n, hf, eql, alloc) {
for (; b != e; ++b) {
emplace(*b);
}
}
BasicStringKeyedUnorderedSet(const BasicStringKeyedUnorderedSet& rhs)
: BasicStringKeyedUnorderedSet(rhs, rhs.get_allocator()) {
}
BasicStringKeyedUnorderedSet(const BasicStringKeyedUnorderedSet& rhs,
const allocator_type& a)
: BasicStringKeyedUnorderedSet(rhs.begin(),
rhs.end(),
rhs.bucket_count(),
rhs.hash_function(),
rhs.key_eq(),
a) {
}
BasicStringKeyedUnorderedSet(BasicStringKeyedUnorderedSet&& rhs) noexcept
: Base(std::move(rhs)) {
assert(rhs.empty());
}
BasicStringKeyedUnorderedSet(BasicStringKeyedUnorderedSet&& rhs,
const allocator_type& a) noexcept
: Base(std::move(rhs)/* , a */ /* not supported by gcc */) {
assert(rhs.empty());
}
BasicStringKeyedUnorderedSet(std::initializer_list<value_type> il)
: BasicStringKeyedUnorderedSet(il.begin(), il.end()) {
}
BasicStringKeyedUnorderedSet(
std::initializer_list<value_type> il,
size_type n,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type())
: BasicStringKeyedUnorderedSet(il.begin(), il.end(), n, hf, eql, alloc) {
}
BasicStringKeyedUnorderedSet&
operator=(const BasicStringKeyedUnorderedSet& rhs) & {
if (this == &rhs) {
return *this;
}
// Cost is as bad as a full copy, so to it via copy + move
return *this = BasicStringKeyedUnorderedSet(rhs);
}
BasicStringKeyedUnorderedSet&
operator=(BasicStringKeyedUnorderedSet&& rhs) & noexcept {
assert(this != &rhs);
clear();
Base::operator=(std::move(rhs));
return *this;
}
using Base::empty;
using Base::size;
using Base::max_size;
using Base::begin;
using Base::end;
using Base::cbegin;
using Base::cend;
using Base::find;
bool operator==(const BasicStringKeyedUnorderedSet& rhs) const {
const Base& lhs = *this;
return lhs == rhs;
}
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args) {
auto key = StringPiece(std::forward<Args>(args)...);
auto it = find(key);
return it != end()
? std::make_pair(it, false)
: Base::emplace(stringPieceDup(key, get_allocator()));
}
std::pair<iterator, bool> insert(value_type val) {
auto it = find(val);
return it != end()
? std::make_pair(it, false)
: Base::insert(stringPieceDup(val, get_allocator()));
}
iterator erase(const_iterator position) {
auto key = *position;
auto result = Base::erase(position);
stringPieceDel(key, get_allocator());
return result;
}
size_type erase(folly::StringPiece key) {
auto it = find(key);
if (it == end()) {
return 0;
}
erase(it);
return 1;
}
void clear() noexcept {
for (auto& it : *this) {
stringPieceDel(it, get_allocator());
}
Base::clear();
}
using Base::reserve;
using Base::hash_function;
using Base::key_eq;
using Base::get_allocator;
using Base::bucket_count;
using Base::max_bucket_count;
using Base::bucket_size;
using Base::bucket;
~BasicStringKeyedUnorderedSet() {
// Here we assume that unordered_set doesn't use keys in destructor
for (auto& it : *this) {
stringPieceDel(it, get_allocator());
}
}
};
typedef BasicStringKeyedUnorderedSet<> StringKeyedUnorderedSet;
} // folly
#endif /* FOLLY_EXPERIMENTAL_STRINGKEYEDUNORDEREDSET_H_ */
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
#include <folly/Benchmark.h>
#include <folly/Range.h>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <folly/experimental/StringKeyedMap.h>
#include <folly/experimental/StringKeyedSet.h>
#include <folly/experimental/StringKeyedUnorderedMap.h>
#include <folly/experimental/StringKeyedUnorderedSet.h>
using folly::StringKeyedMap;
using folly::StringKeyedSet;
using folly::StringKeyedUnorderedMap;
using folly::StringKeyedUnorderedSet;
using folly::StringPiece;
using std::map;
using std::to_string;
using std::set;
using std::string;
using std::unordered_map;
using std::unordered_set;
static map<string, int> m;
static StringKeyedMap<int> skm;
static set<string> s;
static StringKeyedSet sks;
static unordered_map<string, int> um;
static StringKeyedUnorderedMap<int> skum;
static unordered_set<string> us;
static StringKeyedUnorderedSet skus;
static const string lookup("123");
static const folly::StringPiece lookupPiece(lookup);
static void initBenchmarks() {
for (int i = 0; i < 1000; ++i) {
auto iStr = to_string(i);
m[iStr] = i;
skm.insert(make_pair(iStr, i));
um[iStr] = i;
skum.insert(make_pair(iStr, i));
s.insert(iStr);
sks.insert(iStr);
us.insert(iStr);
skus.insert(iStr);
}
}
BENCHMARK(std_map_benchmark_find) {
folly::doNotOptimizeAway(m.find(lookupPiece.str())->second);
}
BENCHMARK_RELATIVE(sk_map_benchmark_find) {
folly::doNotOptimizeAway(skm.find(lookupPiece)->second);
}
BENCHMARK(std_map_benchmark_erase_emplace) {
m.erase(lookup);
m.emplace(lookup, 123);
}
BENCHMARK_RELATIVE(sk_map_benchmark_erase_emplace) {
skm.erase(lookup);
skm.emplace(lookup, 123);
}
BENCHMARK(std_unordered_map_benchmark_find) {
folly::doNotOptimizeAway(um.find(lookupPiece.str())->second);
}
BENCHMARK_RELATIVE(sk_unordered_map_benchmark_find) {
folly::doNotOptimizeAway(skum.find(lookupPiece)->second);
}
BENCHMARK(std_unordered_map_benchmark_erase_emplace) {
um.erase(lookup);
um.emplace(lookup, 123);
}
BENCHMARK_RELATIVE(sk_unordered_map_benchmark_erase_emplace) {
skum.erase(lookup);
skum.emplace(lookup, 123);
}
BENCHMARK(std_set_benchmark_find) {
folly::doNotOptimizeAway(s.find(lookupPiece.str()));
}
BENCHMARK_RELATIVE(sk_set_benchmark_find) {
folly::doNotOptimizeAway(sks.find(lookupPiece));
}
BENCHMARK(std_set_benchmark_erase_emplace) {
s.erase(lookup);
s.emplace(lookup);
}
BENCHMARK_RELATIVE(sk_set_benchmark_erase_emplace) {
sks.erase(lookup);
sks.emplace(lookup);
}
BENCHMARK(std_unordered_set_benchmark_find) {
folly::doNotOptimizeAway(us.find(lookupPiece.str()));
}
BENCHMARK_RELATIVE(sk_unordered_set_benchmark_find) {
folly::doNotOptimizeAway(skus.find(lookupPiece));
}
BENCHMARK(std_unordered_set_benchmark_erase_emplace) {
us.erase(lookup);
us.emplace(lookup);
}
BENCHMARK_RELATIVE(sk_unordered_set_benchmark_erase_emplace) {
skus.erase(lookup);
skus.emplace(lookup);
}
int main(int argc, char **argv) {
initBenchmarks();
folly::runBenchmarks();
}
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2013-present Facebook. All Rights Reserved.
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <string>
#include <list>
#include <folly/Range.h>
#include <folly/experimental/StringKeyedMap.h>
#include <folly/experimental/StringKeyedSet.h>
#include <folly/experimental/StringKeyedUnorderedMap.h>
#include <folly/experimental/StringKeyedUnorderedSet.h>
using folly::StringKeyedMap;
using folly::StringKeyedSetBase;
using folly::StringKeyedUnorderedMap;
using folly::BasicStringKeyedUnorderedSet;
using folly::StringPiece;
using std::string;
static unsigned long long allocated = 0;
static unsigned long long freed = 0;
template <typename Alloc>
struct MemoryLeakCheckerAllocator {
typedef typename Alloc::value_type value_type;
typedef value_type *pointer;
typedef value_type const *const_pointer;
typedef value_type &reference;
typedef value_type const *const_reference;
typedef std::ptrdiff_t difference_type;
typedef std::size_t size_type;
explicit MemoryLeakCheckerAllocator() {
}
explicit MemoryLeakCheckerAllocator(Alloc alloc)
: alloc_(alloc) {
}
template <class UAlloc>
MemoryLeakCheckerAllocator(const MemoryLeakCheckerAllocator<UAlloc>& other)
: alloc_(other.allocator()) {
}
value_type* allocate(size_t n, const void* hint = nullptr) {
auto p = alloc_.allocate(n, hint);
allocated += n * sizeof(value_type);
return p;
}
void deallocate(value_type* p, size_t n) {
alloc_.deallocate(p, n);
freed += n * sizeof(value_type);
}
size_t max_size() const {
return alloc_.max_size();
}
template <class... Args>
void construct(value_type* p, Args&&... args) {
alloc_.construct(p, std::forward<Args>(args)...);
}
void destroy(value_type* p) {
alloc_.destroy(p);
}
template <class U>
struct rebind {
typedef MemoryLeakCheckerAllocator<
typename std::allocator_traits<Alloc>::template rebind_alloc<U>
> other;
};
const Alloc& allocator() const {
return alloc_;
}
bool operator!=(const MemoryLeakCheckerAllocator& other) const {
return alloc_ != other.alloc_;
}
bool operator==(const MemoryLeakCheckerAllocator& other) const {
return alloc_ == other.alloc_;
}
private:
Alloc alloc_;
};
typedef MemoryLeakCheckerAllocator<std::allocator<char>> KeyLeakChecker;
typedef MemoryLeakCheckerAllocator<
std::allocator<std::pair<const StringPiece, int>>> ValueLeakChecker;
typedef StringKeyedUnorderedMap<int, folly::StringPieceHash,
std::equal_to<StringPiece>, ValueLeakChecker>
LeakCheckedUnorderedMap;
typedef StringKeyedSetBase<std::less<StringPiece>, ValueLeakChecker> LeakCheckedSet;
typedef StringKeyedMap<int, std::less<StringPiece>,
ValueLeakChecker> LeakCheckedMap;
using LeakCheckedUnorderedSet = BasicStringKeyedUnorderedSet<
folly::StringPieceHash,
std::equal_to<folly::StringPiece>,
ValueLeakChecker>;
TEST(StringKeyedUnorderedMapTest, sanity) {
LeakCheckedUnorderedMap map;
EXPECT_TRUE(map.empty());
EXPECT_EQ(map.size(), 0);
{
string s("hello");
StringPiece piece(s, 3);
map.insert({s, 1});
EXPECT_FALSE(map.emplace(s, 2).second);
EXPECT_TRUE(map.emplace(piece, 3).second);
}
EXPECT_EQ(map.size(), 2);
map = map;
EXPECT_EQ(map.find("hello")->second, 1);
EXPECT_EQ(map.find("lo")->second, 3);
map.erase(map.find("hello"));
EXPECT_EQ(map.size(), 1);
for (auto& it : map) {
EXPECT_EQ(it.first, "lo");
}
}
TEST(StringKeyedUnorderedMapTest, constructors) {
LeakCheckedUnorderedMap map {
{"hello", 1},
{"lo", 3}
};
LeakCheckedUnorderedMap map2(map);
EXPECT_EQ(map2.size(), 2);
EXPECT_TRUE(map2 == map);
map2.erase("lo");
for (auto& it : map2) {
EXPECT_EQ(it.first, "hello");
}
map2.clear();
EXPECT_TRUE(map2.empty());
map2.emplace("key1", 1);
LeakCheckedUnorderedMap map3(move(map2));
EXPECT_EQ(map3.size(), 1);
EXPECT_EQ(map3["key1"], 1);
EXPECT_EQ(map3["key0"], 0);
EXPECT_EQ(map3.size(), 2);
map3.reserve(1000);
EXPECT_EQ(map3.size(), 2);
LeakCheckedUnorderedMap map4 {
{"key0", 0},
{"key1", 1}
};
EXPECT_EQ(map4.erase("key0"), 1);
EXPECT_EQ(map4.size(), 1);
EXPECT_EQ(map4.find("key0"), map4.end());
map3 = map4;
EXPECT_EQ(map3.size(), 1);
EXPECT_EQ(map4.size(), 1);
EXPECT_EQ(map4.max_size(), map3.max_size());
map4 = std::move(map3);
EXPECT_EQ(map4.size(), 1);
EXPECT_EQ(map4.at("key1"), 1);
}
TEST(StringKeyedSetTest, sanity) {
LeakCheckedSet set;
EXPECT_TRUE(set.empty());
EXPECT_EQ(set.size(), 0);
{
string s("hello");
StringPiece piece(s, 3);
set.insert(s);
EXPECT_FALSE(set.emplace(s).second);
EXPECT_TRUE(set.emplace(piece).second);
}
EXPECT_EQ(set.size(), 2);
set = set;
EXPECT_NE(set.find(StringPiece("hello")), set.end());
EXPECT_NE(set.find("lo"), set.end());
auto it = set.begin();
EXPECT_EQ(*it, "hello");
EXPECT_EQ(*(++it), "lo");
EXPECT_EQ(++it, set.end());
set.erase(set.find("hello"));
EXPECT_EQ(set.size(), 1);
for (auto it : set) {
EXPECT_EQ(it, "lo");
}
}
TEST(StringKeyedSetTest, constructors) {
LeakCheckedSet set {
"hello",
"lo"
};
LeakCheckedSet set2(set);
EXPECT_EQ(set2.size(), 2);
set2.erase("lo");
for (auto it : set2) {
EXPECT_EQ(it, "hello");
}
set2.clear();
EXPECT_TRUE(set2.empty());
set2.emplace("key1");
LeakCheckedSet set3(move(set2));
EXPECT_EQ(set3.size(), 1);
EXPECT_EQ(set3.insert("key1").second, false);
EXPECT_EQ(set3.emplace("key0").second, true);
EXPECT_EQ(set3.size(), 2);
EXPECT_EQ(set3.size(), 2);
LeakCheckedSet set4 {
"key0",
"key1"
};
EXPECT_EQ(set4.erase("key0"), 1);
EXPECT_EQ(set4.size(), 1);
EXPECT_EQ(set4.find("key0"), set4.end());
set3 = set4;
EXPECT_EQ(set3.size(), 1);
EXPECT_EQ(set4.size(), 1);
EXPECT_EQ(set4.max_size(), set3.max_size());
set4 = std::move(set3);
EXPECT_EQ(set4.size(), 1);
EXPECT_NE(set4.find("key1"), set4.end());
}
TEST(StringKeyedUnorderedSetTest, sanity) {
LeakCheckedUnorderedSet set;
EXPECT_TRUE(set.empty());
EXPECT_EQ(set.size(), 0);
{
string s("hello");
StringPiece piece(s, 3);
set.insert(s);
EXPECT_FALSE(set.emplace(s).second);
EXPECT_TRUE(set.emplace(piece).second);
}
EXPECT_EQ(set.size(), 2);
set = set;
EXPECT_NE(set.find("hello"), set.end());
EXPECT_NE(set.find("lo"), set.end());
set.erase(set.find("hello"));
EXPECT_EQ(set.size(), 1);
for (auto it : set) {
EXPECT_EQ(it, "lo");
}
}
TEST(StringKeyedUnorderedSetTest, constructors) {
LeakCheckedUnorderedSet s1;
EXPECT_TRUE(s1.empty());
LeakCheckedUnorderedSet s2(10);
EXPECT_TRUE(s2.empty());
EXPECT_GE(s2.bucket_count(), 10);
std::list<StringPiece> lst { "abc", "def" };
LeakCheckedUnorderedSet s3(lst.begin(), lst.end());
EXPECT_EQ(s3.size(), 2);
EXPECT_NE(s3.find("abc"), s3.end());
EXPECT_NE(s3.find("def"), s3.end());
EXPECT_TRUE(s3 == (LeakCheckedUnorderedSet{"abc", "def"}));
LeakCheckedUnorderedSet s4(const_cast<LeakCheckedUnorderedSet&>(s3));
EXPECT_TRUE(s4 == s3);
LeakCheckedUnorderedSet s5(const_cast<LeakCheckedUnorderedSet&>(s3),
ValueLeakChecker());
EXPECT_TRUE(s5 == s3);
LeakCheckedUnorderedSet s6(std::move(s3));
EXPECT_TRUE(s3.empty());
EXPECT_TRUE(s6 == s5);
LeakCheckedUnorderedSet s7(std::move(s6), s6.get_allocator());
EXPECT_TRUE(s6.empty());
EXPECT_TRUE(s7 == s5);
LeakCheckedUnorderedSet s8 {
"hello",
"lo"
};
EXPECT_EQ(s8.size(), 2);
EXPECT_NE(s8.find("hello"), s8.end());
EXPECT_NE(s8.find("lo"), s8.end());
LeakCheckedUnorderedSet s9({
"hello",
"lo"
}, 10);
EXPECT_EQ(s9.size(), 2);
EXPECT_NE(s9.find("hello"), s9.end());
EXPECT_NE(s9.find("lo"), s9.end());
LeakCheckedUnorderedSet set2(s8);
EXPECT_EQ(set2.size(), 2);
set2.erase("lo");
for (auto it : set2) {
EXPECT_EQ(it, "hello");
}
set2.clear();
EXPECT_TRUE(set2.empty());
set2.emplace("key1");
LeakCheckedUnorderedSet set3(move(set2));
EXPECT_EQ(set3.size(), 1);
EXPECT_EQ(set3.insert("key1").second, false);
EXPECT_EQ(set3.emplace("key0").second, true);
EXPECT_EQ(set3.size(), 2);
set3.reserve(1000);
EXPECT_EQ(set3.size(), 2);
LeakCheckedUnorderedSet set4 {
"key0",
"key1"
};
EXPECT_EQ(set4.erase("key0"), 1);
EXPECT_EQ(set4.size(), 1);
EXPECT_EQ(set4.find("key0"), set4.end());
set3 = set4;
EXPECT_EQ(set3.size(), 1);
EXPECT_EQ(set4.size(), 1);
EXPECT_EQ(set4.max_size(), set3.max_size());
set4 = std::move(set3);
EXPECT_EQ(set4.size(), 1);
EXPECT_NE(set4.find("key1"), set4.end());
}
TEST(StringKeyedMapTest, sanity) {
LeakCheckedMap map;
EXPECT_TRUE(map.empty());
EXPECT_EQ(map.size(), 0);
{
string s("hello");
StringPiece piece(s, 3);
map.insert({s, 1});
EXPECT_FALSE(map.emplace(s, 2).second);
EXPECT_TRUE(map.emplace(piece, 3).second);
}
EXPECT_EQ(map.size(), 2);
map = map;
EXPECT_EQ(map.find("hello")->second, 1);
EXPECT_EQ(map.find("lo")->second, 3);
auto it = map.begin();
EXPECT_EQ(it->first, "hello");
EXPECT_EQ((++it)->first, "lo");
EXPECT_EQ(++it, map.end());
map.erase(map.find("hello"));
EXPECT_EQ(map.size(), 1);
for (auto& it : map) {
EXPECT_EQ(it.first, "lo");
}
}
TEST(StringKeyedMapTest, constructors) {
LeakCheckedMap map {
{"hello", 1},
{"lo", 3}
};
LeakCheckedMap map2(map);
EXPECT_EQ(map2.size(), 2);
map2.erase("lo");
for (auto& it : map2) {
EXPECT_EQ(it.first, "hello");
}
map2.clear();
EXPECT_TRUE(map2.empty());
map2.emplace("key1", 1);
LeakCheckedMap map3(move(map2));
EXPECT_EQ(map3.size(), 1);
EXPECT_EQ(map3["key1"], 1);
EXPECT_EQ(map3["key0"], 0);
EXPECT_EQ(map3.size(), 2);
LeakCheckedMap map4 {
{"key0", 0},
{"key1", 1}
};
EXPECT_EQ(map4.erase("key0"), 1);
EXPECT_EQ(map4.size(), 1);
EXPECT_EQ(map4.find("key0"), map4.end());
map3 = map4;
EXPECT_EQ(map3.size(), 1);
EXPECT_EQ(map4.size(), 1);
EXPECT_EQ(map4.max_size(), map3.max_size());
map4 = std::move(map3);
EXPECT_EQ(map4.size(), 1);
EXPECT_EQ(map4.at("key1"), 1);
}
int main(int argc, char **argv) {
FLAGS_logtostderr = true;
google::InitGoogleLogging(argv[0]);
testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);
return RUN_ALL_TESTS();
}
// This MUST be the LAST test.
TEST(StringKeyed, memory_balance) {
auto balance = allocated < freed
? freed - allocated
: allocated - freed;
LOG(INFO) << "allocated: " << allocated
<< " freed: " << freed
<< " balance: " << balance
<< (
allocated < freed
? " negative (huh?)"
: freed < allocated
? " positive (leak)" : ""
);
EXPECT_EQ(allocated, freed);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment