This is an automated email from the ASF dual-hosted git repository.
wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push:
new 53026cde Small FlatMap optimization with default initialization (#2620)
53026cde is described below
commit 53026cdea1441d5cb67c0ee9c491cda8481a54c4
Author: Bright Chen <[email protected]>
AuthorDate: Mon Dec 9 09:57:38 2024 +0800
Small FlatMap optimization with default initialization (#2620)
* Small FlatMap optimization
* Support optional
---
src/brpc/acceptor.cpp | 7 +-
src/brpc/builtin/hotspots_service.cpp | 1 -
src/brpc/controller.h | 2 -
src/brpc/details/hpack.cpp | 6 +-
src/brpc/details/naming_service_thread.cpp | 4 +-
src/brpc/extension.h | 3 +-
src/brpc/extension_inl.h | 9 -
src/brpc/http_header.cpp | 1 -
src/brpc/kvmap.h | 20 +-
src/brpc/partition_channel.cpp | 4 -
src/brpc/policy/http2_rpc_protocol.cpp | 6 +-
src/brpc/policy/locality_aware_load_balancer.h | 4 +-
src/brpc/policy/rtmp_protocol.cpp | 12 +-
src/brpc/server.cpp | 43 +-
src/brpc/server_id.cpp | 4 +-
src/brpc/uri.cpp | 6 -
src/brpc/uri.h | 1 -
src/bthread/mutex.cpp | 4 +-
src/butil/containers/flat_map.h | 145 ++++--
src/butil/containers/flat_map_inl.h | 447 ++++++++--------
src/butil/containers/optional.h | 560 +++++++++++++++++++++
src/butil/iobuf_profiler.cpp | 9 +-
src/butil/iobuf_profiler.h | 2 +-
src/butil/memory/aligned_memory.h | 2 +-
src/butil/memory/manual_constructor.h | 130 ++---
src/butil/object_pool_inl.h | 22 +-
src/butil/resource_pool_inl.h | 22 +-
src/butil/single_threaded_pool.h | 9 +-
src/butil/type_traits.h | 15 +-
src/bvar/mvariable.cpp | 4 +-
src/bvar/variable.cpp | 5 +-
src/mcpack2pb/mcpack2pb.cpp | 3 +-
test/BUILD.bazel | 3 +-
test/CMakeLists.txt | 4 +-
test/Makefile | 3 +-
test/brpc_controller_unittest.cpp | 2 +-
test/brpc_extension_unittest.cpp | 2 +-
test/brpc_http_message_unittest.cpp | 18 +-
test/flat_map_unittest.cpp | 236 ++++++---
test/optional_unittest.cpp | 298 +++++++++++
..._guard_unittest.cc => scope_guard_unittest.cpp} | 0
41 files changed, 1502 insertions(+), 576 deletions(-)
diff --git a/src/brpc/acceptor.cpp b/src/brpc/acceptor.cpp
index e8e6dcbe..616c1a30 100644
--- a/src/brpc/acceptor.cpp
+++ b/src/brpc/acceptor.cpp
@@ -165,9 +165,8 @@ void Acceptor::StopAccept(int /*closewait_ms*/) {
int Acceptor::Initialize() {
if (_socket_map.init(INITIAL_CONNECTION_CAP) != 0) {
- LOG(FATAL) << "Fail to initialize FlatMap, size="
+ LOG(WARNING) << "Fail to initialize FlatMap, size="
<< INITIAL_CONNECTION_CAP;
- return -1;
}
return 0;
}
@@ -217,10 +216,6 @@ void Acceptor::ListConnections(std::vector<SocketId>*
conn_list,
conn_list->reserve(ConnectionCount() + 10);
std::unique_lock<butil::Mutex> mu(_map_mutex);
- if (!_socket_map.initialized()) {
- // Optional. Uninitialized FlatMap should be iteratable.
- return;
- }
// Copy all the SocketId (protected by mutex) into a temporary
// container to avoid dealing with sockets inside the mutex.
size_t ntotal = 0;
diff --git a/src/brpc/builtin/hotspots_service.cpp
b/src/brpc/builtin/hotspots_service.cpp
index f714843d..8d4d97bc 100644
--- a/src/brpc/builtin/hotspots_service.cpp
+++ b/src/brpc/builtin/hotspots_service.cpp
@@ -68,7 +68,6 @@ static DisplayType StringToDisplayType(const std::string&
val) {
static std::once_flag flag;
std::call_once(flag, []() {
display_type_map = new butil::CaseIgnoredFlatMap<DisplayType>;
- display_type_map->init(10);
(*display_type_map)["dot"] = DisplayType::kDot;
#if defined(OS_LINUX)
(*display_type_map)["flame"] = DisplayType::kFlameGraph;
diff --git a/src/brpc/controller.h b/src/brpc/controller.h
index 2a4ec6b5..d9799f88 100644
--- a/src/brpc/controller.h
+++ b/src/brpc/controller.h
@@ -266,7 +266,6 @@ public:
UserFieldsMap* request_user_fields() {
if (!_request_user_fields) {
_request_user_fields = new UserFieldsMap;
- _request_user_fields->init(29);
}
return _request_user_fields;
}
@@ -276,7 +275,6 @@ public:
UserFieldsMap* response_user_fields() {
if (!_response_user_fields) {
_response_user_fields = new UserFieldsMap;
- _response_user_fields->init(29);
}
return _response_user_fields;
}
diff --git a/src/brpc/details/hpack.cpp b/src/brpc/details/hpack.cpp
index 559e9c6c..687a6145 100644
--- a/src/brpc/details/hpack.cpp
+++ b/src/brpc/details/hpack.cpp
@@ -241,12 +241,10 @@ int IndexTable::Init(const IndexTableOptions& options) {
_need_indexes = options.need_indexes;
if (_need_indexes) {
if (_name_index.init(num_headers * 2) != 0) {
- LOG(ERROR) << "Fail to init _name_index";
- return -1;
+ LOG(WARNING) << "Fail to init _name_index";
}
if (_header_index.init(num_headers * 2) != 0) {
- LOG(ERROR) << "Fail to init _name_index";
- return -1;
+ LOG(WARNING) << "Fail to init _name_index";
}
}
if (options.static_table_size > 0) {
diff --git a/src/brpc/details/naming_service_thread.cpp
b/src/brpc/details/naming_service_thread.cpp
index 89648e77..4891b3f1 100644
--- a/src/brpc/details/naming_service_thread.cpp
+++ b/src/brpc/details/naming_service_thread.cpp
@@ -441,9 +441,7 @@ int GetNamingServiceThread(
return -1;
}
if (g_nsthread_map->init(64) != 0) {
- mu.unlock();
- LOG(ERROR) << "Fail to init g_nsthread_map";
- return -1;
+ LOG(WARNING) << "Fail to init g_nsthread_map";
}
}
NamingServiceThread*& ptr = (*g_nsthread_map)[key];
diff --git a/src/brpc/extension.h b/src/brpc/extension.h
index 9d6c94bc..7190ac91 100644
--- a/src/brpc/extension.h
+++ b/src/brpc/extension.h
@@ -49,8 +49,7 @@ public:
private:
friend class butil::GetLeakySingleton<Extension<T> >;
- Extension();
- ~Extension();
+ Extension() = default;
butil::CaseIgnoredFlatMap<T*> _instance_map;
butil::Mutex _map_mutex;
};
diff --git a/src/brpc/extension_inl.h b/src/brpc/extension_inl.h
index 8eede30d..8a081c7f 100644
--- a/src/brpc/extension_inl.h
+++ b/src/brpc/extension_inl.h
@@ -29,15 +29,6 @@ Extension<T>* Extension<T>::instance() {
return butil::get_leaky_singleton<Extension<T> >();
}
-template <typename T>
-Extension<T>::Extension() {
- _instance_map.init(29);
-}
-
-template <typename T>
-Extension<T>::~Extension() {
-}
-
template <typename T>
int Extension<T>::Register(const std::string& name, T* instance) {
if (NULL == instance) {
diff --git a/src/brpc/http_header.cpp b/src/brpc/http_header.cpp
index aef40b17..9cc82b7b 100644
--- a/src/brpc/http_header.cpp
+++ b/src/brpc/http_header.cpp
@@ -31,7 +31,6 @@ HttpHeader::HttpHeader()
, _method(HTTP_METHOD_GET)
, _version(1, 1)
, _first_set_cookie(NULL) {
- CHECK_EQ(0, _headers.init(29));
// NOTE: don't forget to clear the field in Clear() as well.
}
diff --git a/src/brpc/kvmap.h b/src/brpc/kvmap.h
index 1fd70412..489ed2e6 100644
--- a/src/brpc/kvmap.h
+++ b/src/brpc/kvmap.h
@@ -39,14 +39,20 @@ public:
// Get value of a key(case-sensitive)
// Return pointer to the value, NULL on not found.
const std::string* Get(const char* key) const { return _entries.seek(key);
}
- const std::string* Get(const std::string& key) const { return
_entries.seek(key); }
+ const std::string* Get(const std::string& key) const {
+ return _entries.seek(key);
+ }
// Set value of a key
- void Set(const std::string& key, const std::string& value) { GetOrAdd(key)
= value; }
- void Set(const std::string& key, const char* value) { GetOrAdd(key) =
value; }
+ void Set(const std::string& key, const std::string& value) {
+ _entries[key] = value;
+ }
+ void Set(const std::string& key, const char* value) { _entries[key] =
value; }
// Convert other types to string as well
template <typename T>
- void Set(const std::string& key, const T& value) { GetOrAdd(key) =
std::to_string(value); }
+ void Set(const std::string& key, const T& value) {
+ _entries[key] = std::to_string(value);
+ }
// Remove a key
void Remove(const char* key) { _entries.erase(key); }
@@ -60,12 +66,6 @@ public:
size_t Count() const { return _entries.size(); }
private:
- std::string& GetOrAdd(const std::string& key) {
- if (!_entries.initialized()) {
- _entries.init(29);
- }
- return _entries[key];
- }
Map _entries;
};
diff --git a/src/brpc/partition_channel.cpp b/src/brpc/partition_channel.cpp
index c15fddd1..f6a37bc2 100644
--- a/src/brpc/partition_channel.cpp
+++ b/src/brpc/partition_channel.cpp
@@ -400,10 +400,6 @@ public:
if (options) {
_options = *options;
}
- if (_part_chan_map.init(32, 70) != 0) {
- LOG(ERROR) << "Fail to init _part_chan_map";
- return -1;
- }
return 0;
}
diff --git a/src/brpc/policy/http2_rpc_protocol.cpp
b/src/brpc/policy/http2_rpc_protocol.cpp
index f9b01206..1fd93bf7 100644
--- a/src/brpc/policy/http2_rpc_protocol.cpp
+++ b/src/brpc/policy/http2_rpc_protocol.cpp
@@ -362,12 +362,10 @@ H2Context::~H2Context() {
int H2Context::Init() {
if (_pending_streams.init(64, 70) != 0) {
- LOG(ERROR) << "Fail to init _pending_streams";
- return -1;
+ LOG(WARNING) << "Fail to init _pending_streams";
}
if (_hpacker.Init(_unack_local_settings.header_table_size) != 0) {
- LOG(ERROR) << "Fail to init _hpacker";
- return -1;
+ LOG(WARNING) << "Fail to init _hpacker";
}
return 0;
}
diff --git a/src/brpc/policy/locality_aware_load_balancer.h
b/src/brpc/policy/locality_aware_load_balancer.h
index f4392f4f..4129a2d5 100644
--- a/src/brpc/policy/locality_aware_load_balancer.h
+++ b/src/brpc/policy/locality_aware_load_balancer.h
@@ -118,7 +118,9 @@ private:
butil::FlatMap<SocketId, size_t> server_map;
Servers() {
- CHECK_EQ(0, server_map.init(1024, 70));
+ if (server_map.init(1024, 70) != 0) {
+ LOG(WARNING) << "Fail to init server_map";
+ }
}
// Add diff to left_weight of all parent nodes of node `index'.
diff --git a/src/brpc/policy/rtmp_protocol.cpp
b/src/brpc/policy/rtmp_protocol.cpp
index 99a3e085..8b251eb2 100644
--- a/src/brpc/policy/rtmp_protocol.cpp
+++ b/src/brpc/policy/rtmp_protocol.cpp
@@ -731,8 +731,12 @@ RtmpContext::RtmpContext(const RtmpClientOptions* copt,
const Server* server)
_service = server->options().rtmp_service;
}
_free_ms_ids.reserve(32);
- CHECK_EQ(0, _mstream_map.init(1024, 70));
- CHECK_EQ(0, _trans_map.init(1024, 70));
+ if (_mstream_map.init(1024, 70) != 0) {
+ LOG(FATAL) << "Fail to initialize _mstream_map";
+ }
+ if (_trans_map.init(1024, 70) != 0) {
+ LOG(FATAL) << "Fail to initialize _trans_map";
+ }
memset(static_cast<void*>(_cstream_ctx), 0, sizeof(_cstream_ctx));
}
@@ -1766,7 +1770,9 @@ static pthread_once_t s_cmd_handlers_init_once =
PTHREAD_ONCE_INIT;
static void InitCommandHandlers() {
// Dispatch commands based on "Command Name".
s_cmd_handlers = new CommandHandlerMap;
- CHECK_EQ(0, s_cmd_handlers->init(64, 70));
+ if (s_cmd_handlers->init(64, 70) != 0) {
+ LOG(WARNING) << "Fail to init s_cmd_handlers";
+ }
(*s_cmd_handlers)[RTMP_AMF0_COMMAND_CONNECT] = &RtmpChunkStream::OnConnect;
(*s_cmd_handlers)[RTMP_AMF0_COMMAND_ON_BW_DONE] =
&RtmpChunkStream::OnBWDone;
(*s_cmd_handlers)[RTMP_AMF0_COMMAND_RESULT] = &RtmpChunkStream::OnResult;
diff --git a/src/brpc/server.cpp b/src/brpc/server.cpp
index 2a5fb119..9ab79fc3 100644
--- a/src/brpc/server.cpp
+++ b/src/brpc/server.cpp
@@ -118,8 +118,6 @@ DEFINE_bool(enable_threads_service, false, "Enable
/threads");
DECLARE_int32(usercode_backup_threads);
DECLARE_bool(usercode_in_pthread);
-const int INITIAL_SERVICE_CAP = 64;
-const int INITIAL_CERT_MAP = 64;
// NOTE: never make s_ncore extern const whose ctor seq against other
// compilation units is undefined.
const int s_ncore = sysconf(_SC_NPROCESSORS_ONLN);
@@ -664,22 +662,6 @@ int Server::InitializeOnce() {
if (_status != UNINITIALIZED) {
return 0;
}
- if (_fullname_service_map.init(INITIAL_SERVICE_CAP) != 0) {
- LOG(ERROR) << "Fail to init _fullname_service_map";
- return -1;
- }
- if (_service_map.init(INITIAL_SERVICE_CAP) != 0) {
- LOG(ERROR) << "Fail to init _service_map";
- return -1;
- }
- if (_method_map.init(INITIAL_SERVICE_CAP * 2) != 0) {
- LOG(ERROR) << "Fail to init _method_map";
- return -1;
- }
- if (_ssl_ctx_map.init(INITIAL_CERT_MAP) != 0) {
- LOG(ERROR) << "Fail to init _ssl_ctx_map";
- return -1;
- }
_status = READY;
return 0;
}
@@ -2030,17 +2012,6 @@ int Server::AddCertificate(const CertInfo& cert) {
}
bool Server::AddCertMapping(CertMaps& bg, const SSLContext& ssl_ctx) {
- if (!bg.cert_map.initialized()
- && bg.cert_map.init(INITIAL_CERT_MAP) != 0) {
- LOG(ERROR) << "Fail to init _cert_map";
- return false;
- }
- if (!bg.wildcard_cert_map.initialized()
- && bg.wildcard_cert_map.init(INITIAL_CERT_MAP) != 0) {
- LOG(ERROR) << "Fail to init _wildcard_cert_map";
- return false;
- }
-
for (size_t i = 0; i < ssl_ctx.filters.size(); ++i) {
const char* hostname = ssl_ctx.filters[i].c_str();
CertMap* cmap = NULL;
@@ -2111,8 +2082,8 @@ int Server::ResetCertificates(const
std::vector<CertInfo>& certs) {
}
SSLContextMap tmp_map;
- if (tmp_map.init(INITIAL_CERT_MAP) != 0) {
- LOG(ERROR) << "Fail to initialize tmp_map";
+ if (tmp_map.init(certs.size() + 1) != 0) {
+ LOG(ERROR) << "Fail to init tmp_map";
return -1;
}
@@ -2156,16 +2127,6 @@ int Server::ResetCertificates(const
std::vector<CertInfo>& certs) {
}
bool Server::ResetCertMappings(CertMaps& bg, const SSLContextMap& ctx_map) {
- if (!bg.cert_map.initialized()
- && bg.cert_map.init(INITIAL_CERT_MAP) != 0) {
- LOG(ERROR) << "Fail to init _cert_map";
- return false;
- }
- if (!bg.wildcard_cert_map.initialized()
- && bg.wildcard_cert_map.init(INITIAL_CERT_MAP) != 0) {
- LOG(ERROR) << "Fail to init _wildcard_cert_map";
- return false;
- }
bg.cert_map.clear();
bg.wildcard_cert_map.clear();
diff --git a/src/brpc/server_id.cpp b/src/brpc/server_id.cpp
index c3a595bd..d745d6c5 100644
--- a/src/brpc/server_id.cpp
+++ b/src/brpc/server_id.cpp
@@ -23,7 +23,9 @@ namespace brpc {
ServerId2SocketIdMapper::ServerId2SocketIdMapper() {
_tmp.reserve(128);
- CHECK_EQ(0, _nref_map.init(128));
+ if (_nref_map.init(128) != 0) {
+ LOG(WARNING) << "Fail to init _nref_map";
+ }
}
ServerId2SocketIdMapper::~ServerId2SocketIdMapper() {
diff --git a/src/brpc/uri.cpp b/src/brpc/uri.cpp
index f5d544c1..5391368c 100644
--- a/src/brpc/uri.cpp
+++ b/src/brpc/uri.cpp
@@ -72,9 +72,6 @@ static void ParseQueries(URI::QueryMap& query_map, const
std::string &query) {
}
for (QuerySplitter sp(query.c_str()); sp; ++sp) {
if (!sp.key().empty()) {
- if (!query_map.initialized()) {
- query_map.init(URI::QUERY_MAP_INITIAL_BUCKET);
- }
std::string key(sp.key().data(), sp.key().size());
std::string value(sp.value().data(), sp.value().size());
query_map[key] = value;
@@ -347,9 +344,6 @@ void URI::PrintWithoutHost(std::ostream& os) const {
}
void URI::InitializeQueryMap() const {
- if (!_query_map.initialized()) {
- CHECK_EQ(0, _query_map.init(QUERY_MAP_INITIAL_BUCKET));
- }
ParseQueries(_query_map, _query);
_query_was_modified = false;
_initialized_query_map = true;
diff --git a/src/brpc/uri.h b/src/brpc/uri.h
index 3fbb1548..f8d551ca 100644
--- a/src/brpc/uri.h
+++ b/src/brpc/uri.h
@@ -51,7 +51,6 @@ namespace brpc {
// interpretable as extension
class URI {
public:
- static const size_t QUERY_MAP_INITIAL_BUCKET = 16;
typedef butil::FlatMap<std::string, std::string> QueryMap;
typedef QueryMap::const_iterator QueryIterator;
diff --git a/src/bthread/mutex.cpp b/src/bthread/mutex.cpp
index 451a5c90..bd705806 100644
--- a/src/bthread/mutex.cpp
+++ b/src/bthread/mutex.cpp
@@ -161,7 +161,9 @@ void ContentionProfiler::init_if_needed() {
if (!_init) {
// Already output nanoseconds, always set cycles/second to 1000000000.
_disk_buf.append("--- contention\ncycles/second=1000000000\n");
- CHECK_EQ(0, _dedup_map.init(1024, 60));
+ if (_dedup_map.init(1024, 60) != 0) {
+ LOG(WARNING) << "Fail to initialize dedup_map";
+ }
_init = true;
}
}
diff --git a/src/butil/containers/flat_map.h b/src/butil/containers/flat_map.h
index 955753d2..9ae72db9 100644
--- a/src/butil/containers/flat_map.h
+++ b/src/butil/containers/flat_map.h
@@ -94,6 +94,7 @@
#define BUTIL_FLAT_MAP_H
#include <stdint.h>
+#include <cstddef>
#include <functional>
#include <iostream> // std::ostream
#include <type_traits> // std::aligned_storage
@@ -105,7 +106,7 @@
#include "butil/bit_array.h" // bit_array_*
#include "butil/strings/string_piece.h" // StringPiece
#include "butil/memory/scope_guard.h"
-#include "butil/memory/manual_constructor.h"
+#include "butil/containers/optional.h"
namespace butil {
@@ -121,6 +122,14 @@ struct BucketInfo {
double average_length;
};
+#ifndef BRPC_FLATMAP_DEFAULT_NBUCKET
+#ifdef FLAT_MAP_ROUND_BUCKET_BY_USE_NEXT_PRIME
+#define BRPC_FLATMAP_DEFAULT_NBUCKET 29U
+#else
+#define BRPC_FLATMAP_DEFAULT_NBUCKET 16U
+#endif
+#endif // BRPC_FLATMAP_DEFAULT_NBUCKET
+
// NOTE: Objects stored in FlatMap MUST be copyable.
template <typename _K, typename _T,
// Compute hash code from key.
@@ -144,34 +153,39 @@ public:
_Sparse, SparseFlatMapIterator<FlatMap, value_type>,
FlatMapIterator<FlatMap, value_type> >::type iterator;
typedef typename conditional<
- _Sparse, SparseFlatMapIterator<FlatMap, const value_type>,
+ _Sparse, SparseFlatMapIterator<FlatMap, const value_type>,
FlatMapIterator<FlatMap, const value_type> >::type const_iterator;
typedef _Hash hasher;
typedef _Equal key_equal;
+ static constexpr size_t DEFAULT_NBUCKET = BRPC_FLATMAP_DEFAULT_NBUCKET;
+
struct PositionHint {
size_t nbucket;
size_t offset;
bool at_entry;
key_type key;
};
-
+
explicit FlatMap(const hasher& hashfn = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
- ~FlatMap();
FlatMap(const FlatMap& rhs);
+ ~FlatMap();
+
FlatMap& operator=(const FlatMap& rhs);
void swap(FlatMap & rhs);
- // Must be called to initialize this map, otherwise insert/operator[]
- // crashes, and seek/erase fails.
- // `nbucket' is the initial number of buckets. `load_factor' is the
+ // FlatMap will be automatically initialized with small FlatMap
optimization,
+ // so this function only needs to be call when a large initial number of
+ // buckets or non-default `load_factor' is required.
+ // Returns 0 on success, -1 on error, but FlatMap can still be used
normally.
+ // `nbucket' is the initial number of buckets. `load_factor' is the
// maximum value of size()*100/nbucket, if the value is reached, nbucket
// will be doubled and all items stored will be rehashed which is costly.
// Choosing proper values for these 2 parameters reduces costs.
int init(size_t nbucket, u_int load_factor = 80);
-
- // Insert a pair of |key| and |value|. If size()*100/bucket_count() is
+
+ // Insert a pair of |key| and |value|. If size()*100/bucket_count() is
// more than load_factor(), a resize() will be done.
// Returns address of the inserted value, NULL on error.
mapped_type* insert(const key_type& key, const mapped_type& value);
@@ -198,7 +212,7 @@ public:
// Remove all items and return all allocated spaces to system.
void clear_and_reset_pool();
-
+
// Search for the value associated with |key|.
// If `_Multi=false', Search for any of multiple values associated with
|key|.
// Returns: address of the value.
@@ -217,7 +231,7 @@ public:
// insert() or operator[] if there're too many items.
// Returns successful or not.
bool resize(size_t nbucket);
-
+
// Iterators
iterator begin();
iterator end();
@@ -254,7 +268,7 @@ public:
void save_iterator(const const_iterator&, PositionHint*) const;
const_iterator restore_iterator(const PositionHint&) const;
- // True if init() was successfully called.
+ // Always returns true.
bool initialized() const { return _buckets != NULL; }
bool empty() const { return _size == 0; }
@@ -266,30 +280,42 @@ public:
BucketInfo bucket_info() const;
struct Bucket {
+ Bucket() : next((Bucket*)-1UL) {}
explicit Bucket(const _K& k) : next(NULL) {
- element_.Init(k);
+ element_space_.Init(k);
}
Bucket(const Bucket& other) : next(NULL) {
- element_.Init(other.element());
+ element_space_.Init(other.element());
}
bool is_valid() const { return next != (const Bucket*)-1UL; }
void set_invalid() { next = (Bucket*)-1UL; }
// NOTE: Only be called when is_valid() is true.
- Element& element() {
- return *element_;
- }
- const Element& element() const {
- return *element_;
+ Element& element() { return *element_space_; }
+ const Element& element() const { return *element_space_; }
+ void destroy_element() { element_space_.Destroy(); }
+
+ void swap(Bucket& rhs) {
+ if (!is_valid() && !rhs.is_valid()) {
+ return;
+ } else if (is_valid() && !rhs.is_valid()) {
+ rhs.element_space_.Init(std::move(element()));
+ destroy_element();
+ } else if (!is_valid() && rhs.is_valid()) {
+ element_space_.Init(std::move(rhs.element()));
+ rhs.destroy_element();
+ } else {
+ element().swap(rhs.element());
+ }
+ std::swap(next, rhs.next);
}
- Bucket *next;
+
+ Bucket* next;
private:
- ManualConstructor<Element> element_;
+ ManualConstructor<Element> element_space_;
};
- allocator_type& get_allocator() { return _pool.get_allocator(); }
-
private:
template <typename _Map, typename _Element> friend class FlatMapIterator;
template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
@@ -299,12 +325,13 @@ template <typename _Map, typename _Element> friend class
SparseFlatMapIterator;
: buckets(NULL), thumbnail(NULL), nbucket(0) {}
NewBucketsInfo(Bucket* b, uint64_t* t, size_t n)
: buckets(b), thumbnail(t), nbucket(n) {}
+
Bucket* buckets;
uint64_t* thumbnail;
size_t nbucket;
};
- NewBucketsInfo new_buckets_and_thumbnail(size_t size, size_t new_nbucket);
+ optional<NewBucketsInfo> new_buckets_and_thumbnail(size_t size, size_t
new_nbucket);
// For `_Multi=true'.
// Insert a new default-constructed associated with |key| always.
@@ -314,6 +341,9 @@ template <typename _Map, typename _Element> friend class
SparseFlatMapIterator;
template<bool Multi = _Multi>
typename std::enable_if<Multi, mapped_type&>::type operator[](const
key_type& key);
+ allocator_type& get_allocator() { return _pool.get_allocator(); }
+ allocator_type get_allocator() const { return _pool.get_allocator(); }
+
// True if buckets need to be resized before holding `size' elements.
bool is_too_crowded(size_t size) const {
return is_too_crowded(size, _nbucket, _load_factor);
@@ -322,8 +352,21 @@ template <typename _Map, typename _Element> friend class
SparseFlatMapIterator;
return size * 100 >= nbucket * load_factor;
}
- static void init_buckets_and_thumbnail(
- Bucket* buckets, uint64_t* thumbnail, size_t nbucket) {
+ void init_load_factor(u_int load_factor) {
+ if (_is_default_load_factor) {
+ _is_default_load_factor = false;
+ _load_factor = load_factor;
+ }
+ }
+
+ // True if using default buckets.
+ bool is_default_buckets() const {
+ return _buckets == (Bucket*)(&_default_buckets);
+ }
+
+ static void init_buckets_and_thumbnail(Bucket* buckets,
+ uint64_t* thumbnail,
+ size_t nbucket) {
for (size_t i = 0; i < nbucket; ++i) {
buckets[i].set_invalid();
}
@@ -332,12 +375,18 @@ template <typename _Map, typename _Element> friend class
SparseFlatMapIterator;
bit_array_clear(thumbnail, nbucket);
}
}
-
+
+ static const size_t default_nthumbnail = BIT_ARRAY_LEN(DEFAULT_NBUCKET);
+ // Note: need an extra bucket to let iterator know where buckets end.
+ // Small map optimization.
+ Bucket _default_buckets[DEFAULT_NBUCKET + 1];
+ uint64_t _default_thumbnail[default_nthumbnail];
size_t _size;
size_t _nbucket;
Bucket* _buckets;
uint64_t* _thumbnail;
u_int _load_factor;
+ bool _is_default_load_factor;
hasher _hashfn;
key_equal _eql;
SingleThreadedPool<sizeof(Bucket), 1024, 16, allocator_type> _pool;
@@ -348,7 +397,8 @@ template <typename _K, typename _T,
typename _Equal = DefaultEqualTo<_K>,
bool _Sparse = false,
typename _Alloc = PtAllocator>
-using MultiFlatMap = FlatMap<_K, _T, _Hash, _Equal, _Sparse, _Alloc, true>;
+using MultiFlatMap = FlatMap<
+ _K, _T, _Hash, _Equal, _Sparse, _Alloc, true>;
template <typename _K,
typename _Hash = DefaultHasher<_K>,
@@ -357,7 +407,8 @@ template <typename _K,
typename _Alloc = PtAllocator>
class FlatSet {
public:
- typedef FlatMap<_K, FlatMapVoid, _Hash, _Equal, _Sparse, _Alloc> Map;
+ typedef FlatMap<_K, FlatMapVoid, _Hash, _Equal, _Sparse,
+ _Alloc, false> Map;
typedef typename Map::key_type key_type;
typedef typename Map::value_type value_type;
typedef typename Map::Bucket Bucket;
@@ -408,15 +459,18 @@ private:
template <typename _K, typename _T,
typename _Hash = DefaultHasher<_K>,
- typename _Equal = DefaultEqualTo<_K> >
-class SparseFlatMap : public FlatMap<_K, _T, _Hash, _Equal, true> {
-};
+ typename _Equal = DefaultEqualTo<_K>,
+ typename _Alloc = PtAllocator,
+ bool _Multi = false>
+class SparseFlatMap : public FlatMap<
+ _K, _T, _Hash, _Equal, true, _Alloc, _Multi> {};
template <typename _K,
typename _Hash = DefaultHasher<_K>,
- typename _Equal = DefaultEqualTo<_K> >
-class SparseFlatSet : public FlatSet<_K, _Hash, _Equal, true> {
-};
+ typename _Equal = DefaultEqualTo<_K>,
+ typename _Alloc = PtAllocator>
+class SparseFlatSet : public FlatSet<
+ _K, _Hash, _Equal, true, _Alloc> {};
// Implement FlatMapElement
template <typename K, typename T>
@@ -431,6 +485,13 @@ public:
// POD) which is wrong generally.
explicit FlatMapElement(const K& k) : _key(k), _value(T()) {}
// ^^^^^^^^^^^
+
+ FlatMapElement(const FlatMapElement& rhs)
+ : _key(rhs._key), _value(rhs._value) {}
+
+ FlatMapElement(FlatMapElement&& rhs) noexcept
+ : _key(std::move(rhs._key)), _value(std::move(rhs._value)) {}
+
const K& first_ref() const { return _key; }
T& second_ref() { return _value; }
T&& second_movable_ref() { return std::move(_value); }
@@ -442,8 +503,13 @@ public:
inline static T&& second_movable_ref_from_value(value_type& v)
{ return std::move(v.second); }
+ void swap(FlatMapElement& rhs) {
+ std::swap(_key, rhs._key);
+ std::swap(_value, rhs._value);
+ }
+
private:
- const K _key;
+ K _key;
T _value;
};
@@ -451,7 +517,11 @@ template <typename K>
class FlatMapElement<K, FlatMapVoid> {
public:
typedef const K value_type;
+ // See the comment in the above FlatMapElement.
explicit FlatMapElement(const K& k) : _key(k) {}
+ FlatMapElement(const FlatMapElement& rhs) : _key(rhs._key) {}
+ FlatMapElement(FlatMapElement&& rhs) noexcept : _key(std::move(rhs._key))
{}
+
const K& first_ref() const { return _key; }
FlatMapVoid& second_ref() { return second_ref_from_value(_key); }
FlatMapVoid& second_movable_ref() { return second_ref(); }
@@ -470,8 +540,7 @@ private:
// Implement DefaultHasher and DefaultEqualTo
template <typename K>
-struct DefaultHasher : public BUTIL_HASH_NAMESPACE::hash<K> {
-};
+struct DefaultHasher : public BUTIL_HASH_NAMESPACE::hash<K> {};
template <>
struct DefaultHasher<std::string> {
diff --git a/src/butil/containers/flat_map_inl.h
b/src/butil/containers/flat_map_inl.h
index f37baba1..3b82b9ec 100644
--- a/src/butil/containers/flat_map_inl.h
+++ b/src/butil/containers/flat_map_inl.h
@@ -90,17 +90,12 @@ public:
FlatMapIterator() : _node(NULL), _entry(NULL) {}
FlatMapIterator(const Map* map, size_t pos) {
- if (map->initialized()) {
- _entry = map->_buckets + pos;
- find_and_set_valid_node();
- } else {
- _node = NULL;
- _entry = NULL;
- }
+ _entry = map->_buckets + pos;
+ find_and_set_valid_node();
}
FlatMapIterator(const FlatMapIterator<Map, NonConstValue>& rhs)
: _node(rhs._node), _entry(rhs._entry) {}
- ~FlatMapIterator() {} // required by style-checker
+ ~FlatMapIterator() = default; // required by style-checker
// *this == rhs
bool operator==(const FlatMapIterator& rhs) const
@@ -163,20 +158,14 @@ public:
SparseFlatMapIterator() : _node(NULL), _pos(0), _map(NULL) {}
SparseFlatMapIterator(const Map* map, size_t pos) {
- if (map->initialized()) {
- _map = map;
- _pos = pos;
- find_and_set_valid_node();
- } else {
- _node = NULL;
- _map = NULL;
- _pos = 0;
- }
+ _map = map;
+ _pos = pos;
+ find_and_set_valid_node();
}
SparseFlatMapIterator(const SparseFlatMapIterator<Map, NonConstValue>& rhs)
: _node(rhs._node), _pos(rhs._pos), _map(rhs._map)
{}
- ~SparseFlatMapIterator() {} // required by style-checker
+ ~SparseFlatMapIterator() = default; // required by style-checker
// *this == rhs
bool operator==(const SparseFlatMapIterator& rhs) const
@@ -223,90 +212,85 @@ friend class SparseFlatMapIterator<Map, ConstValue>;
size_t _pos;
const Map* _map;
};
-
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-FlatMap<_K, _T, _H, _E, _S, _A, _M>::FlatMap(const hasher& hashfn, const
key_equal& eql, const allocator_type& alloc)
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::FlatMap(const hasher& hashfn,
+ const key_equal& eql,
+ const allocator_type& alloc)
: _size(0)
- , _nbucket(0)
- , _buckets(NULL)
- , _thumbnail(NULL)
- , _load_factor(0)
+ , _nbucket(DEFAULT_NBUCKET)
+ , _buckets((Bucket*)(&_default_buckets))
+ , _thumbnail(_S ? _default_thumbnail : NULL)
+ , _load_factor(80)
+ , _is_default_load_factor(true)
, _hashfn(hashfn)
, _eql(eql)
- , _pool(alloc)
-{}
+ , _pool(alloc) {
+ init_buckets_and_thumbnail(_buckets, _thumbnail, _nbucket);
+}
+
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::FlatMap(const FlatMap& rhs)
+ : FlatMap(rhs._hashfn, rhs._eql, rhs.get_allocator()) {
+ init_buckets_and_thumbnail(_buckets, _thumbnail, _nbucket);
+ if (!rhs.empty()) {
+ operator=(rhs);
+ }
+}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
FlatMap<_K, _T, _H, _E, _S, _A, _M>::~FlatMap() {
clear();
- get_allocator().Free(_buckets);
- _buckets = NULL;
- bit_array_free(_thumbnail);
- _thumbnail = NULL;
+ if (!is_default_buckets()) {
+ get_allocator().Free(_buckets);
+ _buckets = NULL;
+ bit_array_free(_thumbnail);
+ _thumbnail = NULL;
+ }
_nbucket = 0;
_load_factor = 0;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-FlatMap<_K, _T, _H, _E, _S, _A, _M>::FlatMap(const FlatMap& rhs)
- : _size(0)
- , _nbucket(0)
- , _buckets(NULL)
- , _thumbnail(NULL)
- , _load_factor(rhs._load_factor)
- , _hashfn(rhs._hashfn)
- , _eql(rhs._eql) {
- operator=(rhs);
-}
-
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
FlatMap<_K, _T, _H, _E, _S, _A, _M>&
-FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator=(const FlatMap<_K, _T, _H, _E,
_S, _A, _M>& rhs) {
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator=(
+ const FlatMap<_K, _T, _H, _E, _S, _A, _M>& rhs) {
if (this == &rhs) {
return *this;
}
- // NOTE: assignment does not change _load_factor/_hashfn/_eql if |this| is
- // initialized
+
clear();
- if (!rhs.initialized()) {
+ if (rhs.empty()) {
return *this;
}
- _load_factor = rhs._load_factor;
- if (_buckets == NULL || is_too_crowded(rhs._size)) {
- NewBucketsInfo info = new_buckets_and_thumbnail(_size, rhs._nbucket);
- if (0 == info.nbucket) {
- LOG(ERROR) << "Invalid nbucket=0";
- return *this;
- }
- if (NULL == info.buckets) {
- LOG(ERROR) << "Fail to new buckets";
- return *this;
- }
- if (_S && NULL == info.thumbnail) {
- LOG(ERROR) << "Fail to new thumbnail";
- return *this;
- }
-
- _nbucket = info.nbucket;
- get_allocator().Free(_buckets);
- _buckets = info.buckets;
- if (_S) {
- bit_array_free(_thumbnail);
- _thumbnail = info.thumbnail;
+ // NOTE: assignment only changes _load_factor when it is default.
+ init_load_factor(rhs._load_factor);
+ if (is_too_crowded(rhs._size)) {
+ optional<NewBucketsInfo> info =
+ new_buckets_and_thumbnail(rhs._size, rhs._nbucket);
+ if (info.has_value()) {
+ _nbucket = info->nbucket;
+ if (!is_default_buckets()) {
+ get_allocator().Free(_buckets);
+ if (_S) {
+ bit_array_free(_thumbnail);
+ }
+ }
+ _buckets = info->buckets;
+ _thumbnail = info->thumbnail;
}
- }
- if (rhs.empty()) {
- // No need to copy, returns directly.
- return *this;
+ // Failed new of buckets or thumbnail is OK.
+ // Use old buckets and thumbnail even if map will be crowded.
}
if (_nbucket == rhs._nbucket) {
// For equivalent _nbucket, walking through _buckets instead of using
// iterators is more efficient.
for (size_t i = 0; i < rhs._nbucket; ++i) {
- if (!rhs._buckets[i].is_valid()) {
- _buckets[i].set_invalid();
- } else {
+ if (rhs._buckets[i].is_valid()) {
if (_S) {
bit_array_set(_thumbnail, i);
}
@@ -331,79 +315,77 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator=(const
FlatMap<_K, _T, _H, _E, _S,
return *this;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
int FlatMap<_K, _T, _H, _E, _S, _A, _M>::init(size_t nbucket, u_int
load_factor) {
- if (initialized()) {
- LOG(ERROR) << "Already initialized";
- return -1;
- }
- if (nbucket == 0) {
- LOG(WARNING) << "Fail to init FlatMap, nbucket=" << nbucket;
- return -1;
- }
- if (load_factor < 10 || load_factor > 100) {
- LOG(ERROR) << "Invalid load_factor=" << load_factor;
- return -1;
+ if (nbucket <= _nbucket || load_factor < 10 || load_factor > 100 ||
+ !_is_default_load_factor || !empty() || !is_default_buckets()) {
+ return 0;
}
- _size = 0;
- _load_factor = load_factor;
- NewBucketsInfo info = new_buckets_and_thumbnail(_size, nbucket);
- if (0 == info.nbucket) {
- LOG(ERROR) << "Invalid nbucket=0";
- return -1;
- }
- if (NULL == info.buckets) {
- LOG(ERROR) << "Fail to new buckets";
- return -1;
- }
- if (_S && NULL == info.thumbnail) {
- LOG(ERROR) << "Fail to new thumbnail";
- return -1;
- }
- _nbucket = info.nbucket;
- _buckets = info.buckets;
- if (_S) {
- _thumbnail = info.thumbnail;
- bit_array_clear(_thumbnail, _nbucket);
- }
- return 0;
+ init_load_factor(load_factor);
+ return resize(nbucket) ? 0 : -1;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-void FlatMap<_K, _T, _H, _E, _S, _A, _M>::swap(FlatMap<_K, _T, _H, _E, _S, _A,
_M> & rhs) {
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+void FlatMap<_K, _T, _H, _E, _S, _A, _M>::swap(
+ FlatMap<_K, _T, _H, _E, _S, _A, _M>& rhs) {
+ if (!is_default_buckets() && !rhs.is_default_buckets()) {
+ std::swap(rhs._buckets, _buckets);
+ std::swap(rhs._thumbnail, _thumbnail);
+ } else {
+ for (size_t i = 0; i < DEFAULT_NBUCKET; ++i) {
+ _default_buckets[i].swap(rhs._default_buckets[i]);
+ }
+ if (_S) {
+ for (size_t i = 0; i < default_nthumbnail; ++i) {
+ std::swap(_default_thumbnail[i], rhs._default_thumbnail[i]);
+ }
+ }
+ if (!is_default_buckets() && rhs.is_default_buckets()) {
+ rhs._buckets = _buckets;
+ rhs._thumbnail = _thumbnail;
+ _buckets = _default_buckets;
+ _thumbnail = _default_thumbnail;
+ } else if (is_default_buckets() && !rhs.is_default_buckets()) {
+ _buckets = rhs._buckets;
+ _thumbnail = rhs._thumbnail;
+ rhs._buckets = rhs._default_buckets;
+ rhs._thumbnail = rhs._thumbnail;
+ } // else both are default buckets which has been swapped, so no need
to swap `_buckets'.
+ }
+
std::swap(rhs._size, _size);
std::swap(rhs._nbucket, _nbucket);
- std::swap(rhs._buckets, _buckets);
- std::swap(rhs._thumbnail, _thumbnail);
+ std::swap(rhs._is_default_load_factor, _is_default_load_factor);
std::swap(rhs._load_factor, _load_factor);
std::swap(rhs._hashfn, _hashfn);
std::swap(rhs._eql, _eql);
rhs._pool.swap(_pool);
}
-template <typename _K, typename _T, typename _H,
- typename _E, bool _S, typename _A, bool _M>
-_T* FlatMap<_K, _T, _H, _E, _S, _A, _M>::insert(const key_type& key,
- const mapped_type& value) {
- mapped_type *p = &operator[]<_M>(key);
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+_T* FlatMap<_K, _T, _H, _E, _S, _A, _M>::insert(
+ const key_type& key, const mapped_type& value) {
+ mapped_type *p = &operator[](key);
*p = value;
return p;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
_T* FlatMap<_K, _T, _H, _E, _S, _A, _M>::insert(
const std::pair<key_type, mapped_type>& kv) {
return insert(kv.first, kv.second);
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
template <typename K2, bool Multi>
typename std::enable_if<!Multi, size_t >::type
FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key, _T* old_value) {
- if (!initialized()) {
- return 0;
- }
// TODO: Do we need auto collapsing here?
const size_t index = flatmap_mod(_hashfn(key), _nbucket);
Bucket& first_node = _buckets[index];
@@ -415,7 +397,7 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
_T* old_value) {
*old_value = first_node.element().second_movable_ref();
}
if (first_node.next == NULL) {
- first_node.element().~Element();
+ first_node.destroy_element();
first_node.set_invalid();
if (_S) {
bit_array_unset(_thumbnail, index);
@@ -423,7 +405,7 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
_T* old_value) {
} else {
// A seemingly correct solution is to copy the memory of *p to
// first_node directly like this:
- // first_node.element().~Element();
+ // first_node.destroy_element();
// first_node = *p;
// It works at most of the time, but is wrong generally.
// If _T references self inside like this:
@@ -439,7 +421,7 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
_T* old_value) {
const_cast<_K&>(first_node.element().first_ref()) =
p->element().first_ref();
first_node.element().second_ref() =
p->element().second_movable_ref();
- p->element().~Element();
+ p->destroy_element();
_pool.back(p);
}
--_size;
@@ -453,7 +435,7 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
_T* old_value) {
*old_value = p->element().second_movable_ref();
}
last_p->next = p->next;
- p->element().~Element();
+ p->destroy_element();
_pool.back(p);
--_size;
return 1UL;
@@ -464,14 +446,12 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
_T* old_value) {
return 0;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
template <typename K2, bool Multi>
typename std::enable_if<Multi, size_t >::type
-FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
- std::vector<mapped_type>*
old_values) {
- if (!initialized()) {
- return 0;
- }
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(
+ const K2& key, std::vector<mapped_type>* old_values) {
// TODO: Do we need auto collapsing here?
const size_t index = flatmap_mod(_hashfn(key), _nbucket);
Bucket& first_node = _buckets[index];
@@ -490,7 +470,7 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
}
Bucket* temp = p;
p = p->next;
- temp->element().~Element();
+ temp->destroy_element();
if (temp != &first_node) {
_pool.back(temp);
}
@@ -521,13 +501,14 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::erase(const K2& key,
const_cast<_K&>(first_node.element().first_ref()) =
new_head->element().first_ref();
first_node.element().second_ref() =
new_head->element().second_movable_ref();
- new_head->element().~Element();
+ new_head->destroy_element();
_pool.back(new_head);
}
return total - _size;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
void FlatMap<_K, _T, _H, _E, _S, _A, _M>::clear() {
if (0 == _size) {
return;
@@ -537,11 +518,11 @@ void FlatMap<_K, _T, _H, _E, _S, _A, _M>::clear() {
for (size_t i = 0; i < _nbucket; ++i) {
Bucket& first_node = _buckets[i];
if (first_node.is_valid()) {
- first_node.element().~Element();
+ first_node.destroy_element();
Bucket* p = first_node.next;
while (p) {
Bucket* next_p = p->next;
- p->element().~Element();
+ p->destroy_element();
_pool.back(p);
p = next_p;
}
@@ -554,18 +535,17 @@ void FlatMap<_K, _T, _H, _E, _S, _A, _M>::clear() {
}
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
void FlatMap<_K, _T, _H, _E, _S, _A, _M>::clear_and_reset_pool() {
clear();
_pool.reset();
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
template <typename K2>
_T* FlatMap<_K, _T, _H, _E, _S, _A, _M>::seek(const K2& key) const {
- if (!initialized()) {
- return NULL;
- }
Bucket& first_node = _buckets[flatmap_mod(_hashfn(key), _nbucket)];
if (!first_node.is_valid()) {
return NULL;
@@ -583,13 +563,11 @@ _T* FlatMap<_K, _T, _H, _E, _S, _A, _M>::seek(const K2&
key) const {
return NULL;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-template <typename K2>
-std::vector<_T*> FlatMap<_K, _T, _H, _E, _S, _A, _M>::seek_all(const K2& key)
const {
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+template <typename K2> std::vector<_T*>
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::seek_all(const K2& key) const {
std::vector<_T*> v;
- if (!initialized()) {
- return v;
- }
Bucket& first_node = _buckets[flatmap_mod(_hashfn(key), _nbucket)];
if (!first_node.is_valid()) {
return v;
@@ -607,12 +585,14 @@ std::vector<_T*> FlatMap<_K, _T, _H, _E, _S, _A,
_M>::seek_all(const K2& key) co
return v;
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
template<bool Multi>
typename std::enable_if<!Multi, _T&>::type
FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator[](const key_type& key) {
const size_t index = flatmap_mod(_hashfn(key), _nbucket);
Bucket& first_node = _buckets[index];
+ // LOG(INFO) << "index=" << index;
if (!first_node.is_valid()) {
++_size;
if (_S) {
@@ -627,12 +607,10 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator[](const
key_type& key) {
return p->element().second_ref();
}
if (NULL == p->next) {
- if (is_too_crowded(_size)) {
- if (resize(_nbucket + 1)) {
- return operator[](key);
- }
- // fail to resize is OK
+ if (is_too_crowded(_size) && resize(_nbucket + 1)) {
+ return operator[](key);
}
+ // Fail to resize is OK.
++_size;
Bucket* newp = new (_pool.get()) Bucket(key);
p->next = newp;
@@ -642,7 +620,8 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator[](const
key_type& key) {
}
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
template<bool Multi>
typename std::enable_if<Multi, _T&>::type
FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator[](const key_type& key) {
@@ -671,7 +650,7 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator[](const
key_type& key) {
if (need_scale && resize(_nbucket + 1)) {
return operator[](key);
}
- // fail to resize is OK
+ // Failed resize is OK.
}
++_size;
Bucket* newp = new (_pool.get()) Bucket(key);
@@ -680,7 +659,8 @@ FlatMap<_K, _T, _H, _E, _S, _A, _M>::operator[](const
key_type& key) {
return newp->element().second_ref();
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
void FlatMap<_K, _T, _H, _E, _S, _A, _M>::save_iterator(
const const_iterator& it, PositionHint* hint) const {
hint->nbucket = _nbucket;
@@ -694,9 +674,11 @@ void FlatMap<_K, _T, _H, _E, _S, _A, _M>::save_iterator(
}
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::const_iterator
-FlatMap<_K, _T, _H, _E, _S, _A, _M>::restore_iterator(const PositionHint&
hint) const {
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::restore_iterator(
+ const PositionHint& hint) const {
if (hint.nbucket != _nbucket) // resized
return begin(); // restart
@@ -728,60 +710,26 @@ FlatMap<_K, _T, _H, _E, _S, _A,
_M>::restore_iterator(const PositionHint& hint)
return const_iterator(this, hint.offset);
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-bool FlatMap<_K, _T, _H, _E, _S, _A, _M>::resize(size_t nbucket2) {
- nbucket2 = flatmap_round(nbucket2);
- if (_nbucket == nbucket2) {
- return false;
- }
-
- // NOTE: following functors must be kept after resizing otherwise the
- // internal state is lost.
- FlatMap new_map(_hashfn, _eql, get_allocator());
- if (new_map.init(nbucket2, _load_factor) != 0) {
- LOG(ERROR) << "Fail to init new_map, nbucket=" << nbucket2;
- return false;
- }
- for (iterator it = begin(); it != end(); ++it) {
- new_map[Element::first_ref_from_value(*it)] =
- Element::second_movable_ref_from_value(*it);
- }
- new_map.swap(*this);
- return true;
-}
-
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-BucketInfo FlatMap<_K, _T, _H, _E, _S, _A, _M>::bucket_info() const {
- size_t max_n = 0;
- size_t nentry = 0;
- for (size_t i = 0; i < _nbucket; ++i) {
- if (_buckets[i].is_valid()) {
- size_t n = 1;
- for (Bucket* p = _buckets[i].next; p; p = p->next, ++n);
- max_n = std::max(max_n, n);
- ++nentry;
- }
- }
- const BucketInfo info = { max_n, size() / (double)nentry };
- return info;
-}
-
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::NewBucketsInfo
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+optional<typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::NewBucketsInfo>
FlatMap<_K, _T, _H, _E, _S, _A, _M>::new_buckets_and_thumbnail(size_t size,
size_t
new_nbucket) {
do {
new_nbucket = flatmap_round(new_nbucket);
} while (is_too_crowded(size, new_nbucket, _load_factor));
+ if (_nbucket == new_nbucket) {
+ return nullopt;
+ }
// Note: need an extra bucket to let iterator know where buckets end.
- auto buckets = (Bucket*)get_allocator().Alloc(sizeof(Bucket) * (
- new_nbucket + 1/*note*/));
+ auto buckets = (Bucket*)get_allocator().Alloc(
+ sizeof(Bucket) * (new_nbucket + 1/*note*/));
auto guard = MakeScopeGuard([buckets, this]() {
get_allocator().Free(buckets);
});
if (NULL == buckets) {
LOG(FATAL) << "Fail to new Buckets";
- return {};
+ return nullopt;
}
uint64_t* thumbnail = NULL;
@@ -789,13 +737,72 @@ FlatMap<_K, _T, _H, _E, _S, _A,
_M>::new_buckets_and_thumbnail(size_t size,
thumbnail = bit_array_malloc(new_nbucket);
if (NULL == thumbnail) {
LOG(FATAL) << "Fail to new thumbnail";
- return {};
+ return nullopt;
}
}
guard.dismiss();
init_buckets_and_thumbnail(buckets, thumbnail, new_nbucket);
- return { buckets, thumbnail, new_nbucket };
+ return NewBucketsInfo{buckets, thumbnail, new_nbucket};
+}
+
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+bool FlatMap<_K, _T, _H, _E, _S, _A, _M>::resize(size_t nbucket) {
+ optional<NewBucketsInfo> info = new_buckets_and_thumbnail(_size, nbucket);
+ if (!info.has_value()) {
+ return false;
+ }
+
+ for (iterator it = begin(); it != end(); ++it) {
+ const key_type& key = Element::first_ref_from_value(*it);
+ const size_t index = flatmap_mod(_hashfn(key), info->nbucket);
+ Bucket& first_node = info->buckets[index];
+ if (!first_node.is_valid()) {
+ if (_S) {
+ bit_array_set(info->thumbnail, index);
+ }
+ new (&first_node) Bucket(key);
+ first_node.element().second_ref() =
+ Element::second_movable_ref_from_value(*it);
+ } else {
+ Bucket* newp = new (_pool.get()) Bucket(key);
+ newp->element().second_ref() =
+ Element::second_movable_ref_from_value(*it);
+ newp->next = first_node.next;
+ first_node.next = newp;
+ }
+ }
+ size_t saved_size = _size;
+ clear();
+ if (!is_default_buckets()) {
+ get_allocator().Free(_buckets);
+ if (_S) {
+ bit_array_free(_thumbnail);
+ }
+ }
+ _nbucket = info->nbucket;
+ _buckets = info->buckets;
+ _thumbnail = info->thumbnail;
+ _size = saved_size;
+
+ return true;
+}
+
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+BucketInfo FlatMap<_K, _T, _H, _E, _S, _A, _M>::bucket_info() const {
+ size_t max_n = 0;
+ size_t nentry = 0;
+ for (size_t i = 0; i < _nbucket; ++i) {
+ if (_buckets[i].is_valid()) {
+ size_t n = 1;
+ for (Bucket* p = _buckets[i].next; p; p = p->next, ++n);
+ max_n = std::max(max_n, n);
+ ++nentry;
+ }
+ }
+ return { max_n, size() / (double)nentry };
}
inline std::ostream& operator<<(std::ostream& os, const BucketInfo& info) {
@@ -803,23 +810,31 @@ inline std::ostream& operator<<(std::ostream& os, const
BucketInfo& info) {
<< " avgb=" << info.average_length << '}';
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::iterator FlatMap<_K, _T, _H, _E,
_S, _A, _M>::begin() {
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::iterator
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::begin() {
return iterator(this, 0);
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::iterator FlatMap<_K, _T, _H, _E,
_S, _A, _M>::end() {
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::iterator
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::end() {
return iterator(this, _nbucket);
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::const_iterator FlatMap<_K, _T,
_H, _E, _S, _A, _M>::begin() const {
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::const_iterator
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::begin() const {
return const_iterator(this, 0);
}
-template <typename _K, typename _T, typename _H, typename _E, bool _S,
typename _A, bool _M>
-typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::const_iterator FlatMap<_K, _T,
_H, _E, _S, _A, _M>::end() const {
+template <typename _K, typename _T, typename _H, typename _E,
+ bool _S, typename _A, bool _M>
+typename FlatMap<_K, _T, _H, _E, _S, _A, _M>::const_iterator
+FlatMap<_K, _T, _H, _E, _S, _A, _M>::end() const {
return const_iterator(this, _nbucket);
}
diff --git a/src/butil/containers/optional.h b/src/butil/containers/optional.h
new file mode 100644
index 00000000..ffa5cb3b
--- /dev/null
+++ b/src/butil/containers/optional.h
@@ -0,0 +1,560 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+#ifndef BUTIL_OPTIONAL_H
+#define BUTIL_OPTIONAL_H
+
+#include "butil/type_traits.h"
+#include "butil/memory/manual_constructor.h"
+
+// The `optional` for managing an optional contained value,
+// i.e. a value that may or may not be present, is a C++11
+// compatible version of the C++17 `std::optional` abstraction.
+// After C++17, `optional` is an alias for `std::optional`.
+
+#if __cplusplus >= 201703L
+namespace butil {
+using std::in_place_t;
+using std::in_place;
+using std::nullopt_t;
+using std::nullopt;
+using std::bad_optional_access;
+using std::optional;
+using std::make_optional;
+} // namespace butil
+#else
+namespace butil {
+
+// Tag type used to specify in-place construction of `optional'.
+struct in_place_t {};
+
+// Instance used for in-place construction of `optional',
+const in_place_t in_place;
+
+namespace internal {
+ // Tag type used as a constructor parameter type for `nullopt_t'.
+ struct optional_forbidden_t {};
+}
+
+// Used to indicate an optional object that does not contain a value.
+struct nullopt_t {
+ // It must not be default-constructible to avoid ambiguity for `opt = {}'.
+ explicit nullopt_t(internal::optional_forbidden_t) noexcept {}
+};
+
+// Instance to use for the construction of `optional`.
+const nullopt_t nullopt(internal::optional_forbidden_t{});
+
+// Thrown by `optional::value()' when accessing an optional object
+// that does not contain a value.
+class bad_optional_access : public std::exception {
+public:
+ bad_optional_access() = default;
+ ~bad_optional_access() override = default;
+ const char* what() const noexcept override {
+ return "optional has no value";
+ }
+};
+
+template <typename T>
+class optional;
+
+namespace internal {
+
+// Whether `T' is constructible or convertible from `optional<U>'.
+template <typename T, typename U>
+struct is_constructible_from_optional : integral_constant<
+ bool, std::is_constructible<T, optional<U>&>::value ||
+ std::is_constructible<T, optional<U>&&>::value ||
+ std::is_constructible<T, const optional<U>&>::value ||
+ std::is_constructible<T, const optional<U>&&>::value> {};
+
+// Whether `T' is constructible or convertible from `butil::optional<U>'.
+template <typename T, typename U>
+struct is_convertible_from_optional
+ : integral_constant<bool,
+ std::is_convertible<optional<U>&, T>::value ||
+ std::is_convertible<optional<U>&&, T>::value ||
+ std::is_convertible<const optional<U>&, T>::value ||
+ std::is_convertible<const optional<U>&&, T>::value> {};
+
+// Whether `T' is constructible or convertible or assignable from
`optional<U>'.
+template <typename T, typename U>
+struct is_assignable_from_optional
+ : integral_constant<bool,
+ std::is_assignable<T&, optional<U>&>::value ||
+ std::is_assignable<T&, optional<U>&&>::value ||
+ std::is_assignable<T&, const optional<U>&>::value ||
+ std::is_assignable<T&, const optional<U>&&>::value> {};
+
+// Whether `T' is constructible or convertible from `optional<U>'.
+template <typename T, typename U>
+struct is_constructible_convertible_from_optional
+ : integral_constant<bool,
+ is_constructible_from_optional<T, U>::value ||
+ is_convertible_from_optional<T, U>::value> {};
+
+// Whether `T' is constructible or convertible or assignable from
`optional<U>'.
+template <typename T, typename U>
+struct is_constructible_convertible_assignable_from_optional
+ : integral_constant<bool,
+ is_constructible_from_optional<T, U>::value ||
+ is_convertible_from_optional<T, U>::value ||
+ is_assignable_from_optional<T, U>::value> {};
+
+} // namespace internal
+
+template<typename T>
+class optional {
+public:
+ typedef T value_type;
+
+ static_assert(!is_same<typename remove_cvref<value_type>::type,
in_place_t>::value,
+ "Instantiation of optional with in_place_t is ill-formed");
+ static_assert(!is_same<typename remove_cvref<value_type>::type,
nullopt_t>::value,
+ "Instantiation of optional with nullopt_t is ill-formed");
+ static_assert(!is_reference<value_type>::value,
+ "Instantiation of optional with a reference type is
ill-formed");
+ static_assert(std::is_destructible<value_type>::value,
+ "Instantiation of optional with a non-destructible type is
ill-formed");
+ static_assert(!is_array<value_type>::value,
+ "Instantiation of optional with an array type is
ill-formed");
+
+ optional() : _engaged(false) {}
+
+ optional(nullopt_t) : optional() {}
+
+ optional(const optional& rhs) = default;
+
+ optional(optional&& rhs) noexcept = default;
+
+ template <typename U, typename std::enable_if<
+ !std::is_same<T, U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ !internal::is_constructible_convertible_from_optional<T, U>::value &&
+ std::is_convertible<const U&, T>::value, bool>::type = false>
+ optional(const optional<U>& rhs) : _engaged(rhs.has_value()) {
+ if (_engaged) {
+ _storage.Init(*rhs);
+ }
+ }
+
+ template <typename U, typename std::enable_if<
+ !std::is_same<T, U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ !internal::is_constructible_convertible_from_optional<T, U>::value &&
+ !std::is_convertible<const U&, T>::value, bool>::type = false>
+ explicit optional(const optional<U>& rhs) : _engaged(rhs.has_value()) {
+ if (_engaged) {
+ _storage.Init(*rhs);
+ }
+ }
+
+ template <typename U, typename std::enable_if<
+ !std::is_same<T, U>::value &&
+ std::is_constructible<T, U&&>::value &&
+ !internal::is_constructible_convertible_from_optional<T, U>::value &&
+ std::is_convertible<U&&, T>::value, bool>::type = false>
+ optional(optional<U>&& rhs) : _engaged(rhs.has_value()) {
+ if (_engaged) {
+ _storage.Init(std::move(*rhs));
+ rhs.reset();
+ }
+ }
+
+ template <typename U, typename std::enable_if<
+ !std::is_same<T, U>::value &&
+ std::is_constructible<T, U&&>::value &&
+ !internal::is_constructible_convertible_from_optional<T, U>::value &&
+ !std::is_convertible<U&&, T>::value, bool>::type = false>
+ explicit optional(optional<U>&& rhs) : _engaged(rhs.has_value()) {
+ if (_engaged) {
+ _storage.Init(std::move(*rhs));
+ rhs.reset();
+ }
+ }
+
+ optional(const T& value) : _engaged(true) {
+ _storage.Init(value);
+ }
+
+ optional(T&& value) : _engaged(true) {
+ _storage.Init(std::move(value));
+ }
+
+ template <typename... Args,
+ std::enable_if<std::is_constructible<T, Args&&...>::value>* =
nullptr>
+ explicit optional(const in_place_t, Args&&... args) : _engaged(true) {
+ _storage.Init(std::forward<Args>(args)...);
+ }
+
+ template <typename U, typename... Args, typename std::enable_if<
+ std::is_constructible<T, std::initializer_list<U>&,
Args&&...>::value>::type>
+ optional(in_place_t, std::initializer_list<U> il, Args&&... args)
+ : _engaged(true) {
+ _storage.Init(il, std::forward<Args>(args)...);
+ }
+
+ template <typename U = T, typename std::enable_if<
+ !std::is_same<in_place_t, typename std::decay<U>::type>::value &&
+ !std::is_same<optional<T>, typename std::decay<U>::type>::value &&
+ std::is_constructible<T, U&&>::value &&
+ std::is_convertible<U&&, T>::value, bool>::type = false>
+ optional(U&& v) : _engaged(true) {
+ _storage.Init(std::forward<U>(v));
+ }
+
+ template <typename U = T, typename std::enable_if<
+ !std::is_same<in_place_t, typename std::decay<U>::type>::value &&
+ !std::is_same<optional<T>, typename std::decay<U>::type>::value &&
+ std::is_constructible<T, U&&>::value &&
+ !std::is_convertible<U&&, T>::value, bool>::type = false>
+ explicit optional(U&& v) : _engaged(true) {
+ _storage.Init(std::forward<U>(v));
+ }
+
+ ~optional() {
+ reset();
+ }
+
+ optional& operator=(nullopt_t) noexcept {
+ reset();
+ return *this;
+ }
+
+ optional& operator=(const optional& rhs) = default;
+
+ optional& operator=(optional&& rhs) = default;
+
+ // Value assignment operators
+ template <typename U = T, typename = typename std::enable_if<
+ !std::is_same<optional<T>, typename std::decay<U>::type>::value &&
+ !std::is_same<optional<T>, typename remove_cvref<U>::type>::value &&
+ std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value
&&
+ (!std::is_scalar<T>::value || !std::is_same<T, typename
std::decay<U>::type>::value)>::type>
+ optional& operator=(U&& v) {
+ reset();
+ _storage.Init(std::forward<U>(v));
+ _engaged = true;
+ return *this;
+ }
+
+ template <typename U, typename = typename std::enable_if<
+ !std::is_same<T, U>::value &&
+ !internal::is_constructible_convertible_assignable_from_optional<T,
U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ std::is_assignable<T&, const U&>::value>::type>
+ optional& operator=(const optional<U>& rhs) {
+ if (rhs) {
+ operator=(*rhs);
+ } else {
+ reset();
+ }
+ return *this;
+ }
+
+ template <typename U, typename = typename std::enable_if<
+ !std::is_same<T, U>::value &&
+ !internal::is_constructible_convertible_assignable_from_optional<T,
U>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<T&, U>::value>::type>
+ optional& operator=(optional<U>&& rhs) {
+ if (rhs) {
+ operator=(std::move(*rhs));
+ } else {
+ reset();
+ }
+ return *this;
+ }
+
+ // Accesses the contained value.
+ T& operator*() {
+ if (!_engaged) {
+ throw bad_optional_access();
+ }
+ return *_storage;
+ }
+ const T& operator*() const {
+ if (!_engaged) {
+ throw bad_optional_access();
+ }
+ return *_storage;
+ }
+ T* operator->() {
+ return _storage.get();
+ }
+ const T* operator->() const {
+ return _storage.get();
+ }
+
+ explicit operator bool() const { return _engaged; }
+
+ bool has_value() const { return _engaged; }
+
+ // Returns the contained value if the `optional` is not empty,
+ // otherwise throws `bad_optional_access`.
+ const T& value() const & {
+ if (!_engaged) {
+ throw bad_optional_access();
+ }
+
+ return *_storage;
+ }
+ T& value() & {
+ if (!_engaged) {
+ throw bad_optional_access();
+ }
+
+ return *_storage;
+ }
+ T&& value() && {
+ if (!_engaged) {
+ throw bad_optional_access();
+ }
+
+ return std::move(*_storage);
+ }
+ const T&& value() const && {
+ if (!_engaged) {
+ throw bad_optional_access();
+ }
+
+ return std::move(*_storage);
+ }
+
+ // Returns the contained value if the `optional` is not empty,
+ // otherwise returns default `v'.
+ template <typename U>
+ constexpr T value_or(U&& v) const& {
+ static_assert(std::is_copy_constructible<value_type>::value,
+ "T can not be copy constructible");
+ static_assert(std::is_convertible<U&&, value_type>::value,
+ "U can not be convertible to T");
+
+ return static_cast<bool>(*this) ? **this :
static_cast<T>(std::forward<U>(v));
+ }
+ template <typename U>
+ T value_or(U&& v) && {
+ static_assert(std::is_move_constructible<value_type>::value,
+ "T can not be move constructible");
+ static_assert(std::is_convertible<U&&, value_type>::value,
+ "U can not be convertible to T");
+
+ return static_cast<bool>(*this) ?
+ std::move(**this) : static_cast<T>(std::forward<U>(v));
+ }
+
+ void swap(optional& rhs) noexcept {
+ if (_engaged && rhs._engaged) {
+ std::swap(**this, *rhs);
+ } else if (_engaged) {
+ rhs = std::move(*this);
+ reset();
+ } else if (rhs._engaged) {
+ *this = std::move(rhs);
+ rhs.reset();
+ }
+ }
+
+ // Destroys any contained value if the `optional` is not empty.
+ void reset() {
+ if (_engaged) {
+ _storage.Destroy();
+ _engaged = false;
+ }
+ }
+
+ // Constructs the contained value in-place.
+ // Return a reference to the new contained value.
+ template <typename... Args>
+ T& emplace(Args&&... args) {
+ static_assert(std::is_constructible<T, Args&&...>::value,
+ "T can not be constructible with these arguments");
+ reset();
+ _storage.Init(std::forward<Args>(args)...);
+ _engaged = true;
+ return *_storage;
+ }
+ template <typename U, typename... Args>
+ T& emplace(std::initializer_list<U> il, Args&&... args) {
+ static_assert(std::is_constructible<T, std::initializer_list<U>&,
Args&&...>::value,
+ "T can not be constructible with these arguments");
+
+ return emplace(il, std::forward<Args>(args)...);
+ }
+
+private:
+ bool _engaged;
+ ManualConstructor<T> _storage;
+};
+
+template <typename T>
+void swap(optional<T>& a, optional<T>& b) noexcept { a.swap(b); }
+
+// Creates a non-empty 'optional<T>`.
+template <typename T>
+optional<typename std::decay<T>::type> make_optional(T&& v) {
+ return optional<typename std::decay<T>::type>(std::forward<T>(v));
+}
+
+template <typename T, typename... Args>
+optional<T> make_optional(Args&&... args) {
+ return optional<T>(in_place, std::forward<Args>(args)...);
+}
+
+template <typename T, typename U, typename... Args>
+optional<T> make_optional(std::initializer_list<U> il, Args&&... args) {
+ return optional<T>(in_place, il, std::forward<Args>(args)...);
+}
+
+// Compares optional objects.
+// Supports comparisons between 'optional<T>` and 'optional<U>`,
+// between 'optional<T>` and `U', and between 'optional<T>` and `nullopt'.
+// Empty optionals are considered equal to each other and less than non-empty
optionals.
+template <typename T, typename U>
+bool operator==(const optional<T>& x, const optional<U>& y) {
+ return static_cast<bool>(x) != static_cast<bool>(y)
+ ? false : static_cast<bool>(x) == false ? true : *x == *y;
+}
+
+template <typename T, typename U>
+bool operator!=(const optional<T>& x, const optional<U>& y) {
+ return !(x == y);
+}
+
+template <typename T, typename U>
+bool operator<=(const optional<T>& x, const optional<U>& y) {
+ return !x ? true : !y ? false : *x <= *y;
+}
+
+template <typename T, typename U>
+bool operator>=(const optional<T>& x, const optional<U>& y) {
+ return !y ? true : !x ? false : *x >= *y;
+}
+
+template <typename T, typename U>
+bool operator<(const optional<T>& x, const optional<U>& y) {
+ return !(x >= y);
+}
+
+template <typename T, typename U>
+bool operator>(const optional<T>& x, const optional<U>& y) {
+ return !(x <= y);
+}
+
+template <typename T>
+bool operator==(const optional<T>& x, nullopt_t) { return !x; }
+
+template <typename T>
+bool operator==(nullopt_t, const optional<T>& x) { return !x; }
+
+template <typename T>
+bool operator!=(const optional<T>& x, nullopt_t) { return
static_cast<bool>(x); }
+
+template <typename T>
+bool operator!=(nullopt_t, const optional<T>& x) { return
static_cast<bool>(x);}
+
+template <typename T>
+bool operator<(const optional<T>&, nullopt_t) { return false; }
+
+template <typename T>
+bool operator<(nullopt_t, const optional<T>& x) { return static_cast<bool>(x);
}
+
+template <typename T>
+bool operator<=(const optional<T>& x, nullopt_t) { return !x; }
+
+template <typename T>
+bool operator<=(nullopt_t, const optional<T>&) { return true; }
+
+template <typename T>
+bool operator>(const optional<T>& x, nullopt_t) { return static_cast<bool>(x);
}
+
+template <typename T>
+bool operator>(nullopt_t, const optional<T>&) { return false; }
+
+template <typename T>
+bool operator>=(const optional<T>&, nullopt_t) { return true; }
+template <typename T>
+bool operator>=(nullopt_t, const optional<T>& x) { return !x; }
+
+template <typename T, typename U>
+bool operator==(const optional<T>& x, const U& v) {
+ return x ? *x == v : false;
+}
+template <typename T, typename U>
+bool operator==(const U& v, const optional<T>& x) {
+ return x == v;
+}
+template <typename T, typename U>
+bool operator!=(const optional<T>& x, const U& v) {
+ return x ? *x != v : true;
+}
+template <typename T, typename U>
+bool operator!=(const U& v, const optional<T>& x) {
+ return x != v;
+}
+
+template <typename T, typename U>
+bool operator<=(const optional<T>& x, const U& v) {
+ return x ? *x <= v : true;
+}
+template <typename T, typename U>
+bool operator<=(const U& v, const optional<T>& x) {
+ return x ? v <= *x : false;
+}
+template <typename T, typename U>
+bool operator<(const optional<T>& x, const U& v) {
+ return !(x >= v);
+}
+template <typename T, typename U>
+bool operator<(const U& v, const optional<T>& x) {
+ return !(v >= x);
+}
+
+template <typename T, typename U>
+bool operator>=(const optional<T>& x, const U& v) {
+ return x ? *x >= v : false;
+}
+template <typename T, typename U>
+bool operator>=(const U& v, const optional<T>& x) {
+ return x ? v >= *x : true;
+}
+
+template <typename T, typename U>
+bool operator>(const optional<T>& x, const U& v) {
+ return !(x <= v);
+}
+template <typename T, typename U>
+bool operator>(const U& v, const optional<T>& x) {
+ return !(v <= x);
+}
+
+} // namespace butil
+
+namespace std {
+template <typename T>
+struct hash<butil::optional<T>> {
+ std::size_t operator()(const butil::optional<T>& opt) const {
+ return hash<T>()(opt.value_or(T()));
+ }
+};
+
+} // namespace std
+
+#endif // __cplusplus >= 201703L
+
+#endif // BUTIL_OPTIONAL_H
diff --git a/src/butil/iobuf_profiler.cpp b/src/butil/iobuf_profiler.cpp
index 11353beb..8d213a72 100644
--- a/src/butil/iobuf_profiler.cpp
+++ b/src/butil/iobuf_profiler.cpp
@@ -99,8 +99,12 @@ IOBufProfiler::IOBufProfiler()
: butil::SimpleThread("IOBufProfiler")
, _stop(false)
, _sleep_ms(MIN_SLEEP_MS) {
- CHECK_EQ(0, _stack_map.init(1024));
- CHECK_EQ(0, _block_info_map.init(1024));
+ if (_stack_map.init(1024) != 0) {
+ LOG(WARNING) << "Fail to init _stack_map";
+ }
+ if (_block_info_map.init(1024) != 0) {
+ LOG(WARNING) << "Fail to init _block_info_map";
+ }
Start();
}
@@ -161,7 +165,6 @@ void IOBufProfiler::Dump(IOBufSample* s) {
} else {
BlockInfo& new_info = _block_info_map[s->block];
new_info.ref += s->count;
- CHECK_EQ(0, new_info.stack_count_map.init(64));
new_info.stack_count_map[*stack_ptr] = s->count;
}
} while (false);
diff --git a/src/butil/iobuf_profiler.h b/src/butil/iobuf_profiler.h
index 7178f20a..4686886f 100644
--- a/src/butil/iobuf_profiler.h
+++ b/src/butil/iobuf_profiler.h
@@ -54,7 +54,7 @@ struct IOBufSample {
size_t stack_hash_code() const;
private:
- friend ObjectPool<IOBufSample>;
+friend ObjectPool<IOBufSample>;
IOBufSample()
: next(NULL)
diff --git a/src/butil/memory/aligned_memory.h
b/src/butil/memory/aligned_memory.h
index c6aaaeef..fbc24612 100644
--- a/src/butil/memory/aligned_memory.h
+++ b/src/butil/memory/aligned_memory.h
@@ -54,7 +54,7 @@ struct AlignedMemory {};
// std::aligned_storage has been deprecated in C++23,
// because aligned_* are harmful to codebases and should not be used.
// For details, see
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1413r3.pdf
-#if (__cplusplus >= 201103L)
+#if __cplusplus >= 201103L
// In most places, use the C++11 keyword "alignas", which is preferred.
#define DECL_ALIGNED_BUFFER(buffer_name, byte_alignment, size) \
alignas(byte_alignment) uint8_t buffer_name[size]
diff --git a/src/butil/memory/manual_constructor.h
b/src/butil/memory/manual_constructor.h
index 266f6cca..111d5ece 100644
--- a/src/butil/memory/manual_constructor.h
+++ b/src/butil/memory/manual_constructor.h
@@ -24,104 +24,52 @@ namespace butil {
template <typename Type>
class ManualConstructor {
- public:
- // No constructor or destructor because one of the most useful uses of
- // this class is as part of a union, and members of a union cannot have
- // constructors or destructors. And, anyway, the whole point of this
- // class is to bypass these.
-
- // Support users creating arrays of ManualConstructor<>s. This ensures that
- // the array itself has the correct alignment.
- static void* operator new[](size_t size) {
+public:
+ // No constructor or destructor because one of the most useful uses of
+ // this class is as part of a union, and members of a union cannot have
+ // constructors or destructors. And, anyway, the whole point of this
+ // class is to bypass these.
+
+ // Support users creating arrays of ManualConstructor<>s. This ensures
that
+ // the array itself has the correct alignment.
+ static void* operator new[](size_t size) {
#if defined(COMPILER_MSVC)
- return AlignedAlloc(size, __alignof(Type));
+ return AlignedAlloc(size, __alignof(Type));
#else
- return AlignedAlloc(size, __alignof__(Type));
+ return AlignedAlloc(size, __alignof__(Type));
#endif
- }
- static void operator delete[](void* mem) {
- AlignedFree(mem);
- }
-
- inline Type* get() {
- return space_.template data_as<Type>();
- }
- inline const Type* get() const {
- return space_.template data_as<Type>();
- }
-
- inline Type* operator->() { return get(); }
- inline const Type* operator->() const { return get(); }
-
- inline Type& operator*() { return *get(); }
- inline const Type& operator*() const { return *get(); }
-
- template<typename Ctor>
- inline void InitBy(Ctor ctor) {
- ctor(space_.void_data());
- }
-
- // You can pass up to eight constructor arguments as arguments of Init().
- inline void Init() {
- new(space_.void_data()) Type;
- }
-
- template <typename T1>
- inline void Init(const T1& p1) {
- new(space_.void_data()) Type(p1);
- }
-
- template <typename T1, typename T2>
- inline void Init(const T1& p1, const T2& p2) {
- new(space_.void_data()) Type(p1, p2);
- }
-
- template <typename T1, typename T2, typename T3>
- inline void Init(const T1& p1, const T2& p2, const T3& p3) {
- new(space_.void_data()) Type(p1, p2, p3);
- }
-
- template <typename T1, typename T2, typename T3, typename T4>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4) {
- new(space_.void_data()) Type(p1, p2, p3, p4);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5) {
- new(space_.void_data()) Type(p1, p2, p3, p4, p5);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6) {
- new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7) {
- new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6, p7);
- }
-
- template <typename T1, typename T2, typename T3, typename T4, typename T5,
- typename T6, typename T7, typename T8>
- inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
- const T5& p5, const T6& p6, const T7& p7, const T8& p8) {
- new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6, p7, p8);
- }
-
- inline void Destroy() {
- get()->~Type();
- }
+ }
+
+ static void operator delete[](void* mem) {
+ AlignedFree(mem);
+ }
+
+ Type* get() {
+ return _space.template data_as<Type>();
+ }
+
+ const Type* get() const {
+ return _space.template data_as<Type>();
+ }
+
+ Type* operator->() { return get(); }
+ const Type* operator->() const { return get(); }
+
+ Type& operator*() { return *get(); }
+ const Type& operator*() const { return *get(); }
+
+ template<typename... Args>
+ void Init(Args&&... args) {
+ new (_space.void_data()) Type(std::forward<Args>(args)...);
+ }
+
+ void Destroy() { get()->~Type(); }
private:
#if defined(COMPILER_MSVC)
- AlignedMemory<sizeof(Type), __alignof(Type)> space_;
+ AlignedMemory<sizeof(Type), __alignof(Type)> _space;
#else
- AlignedMemory<sizeof(Type), __alignof__(Type)> space_;
+ AlignedMemory<sizeof(Type), __alignof__(Type)> _space;
#endif
};
diff --git a/src/butil/object_pool_inl.h b/src/butil/object_pool_inl.h
index 71b9b890..0055239d 100644
--- a/src/butil/object_pool_inl.h
+++ b/src/butil/object_pool_inl.h
@@ -25,11 +25,11 @@
#include <iostream> // std::ostream
#include <pthread.h> // pthread_mutex_t
#include <algorithm> // std::max, std::min
-#include "butil/atomicops.h" // butil::atomic
-#include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT
-#include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK
-#include "butil/thread_local.h" // BAIDU_THREAD_LOCAL
-#include "butil/memory/manual_constructor.h"
+#include "butil/atomicops.h" // butil::atomic
+#include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT
+#include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK
+#include "butil/thread_local.h" // BAIDU_THREAD_LOCAL
+#include "butil/memory/aligned_memory.h" // butil::AlignedMemory
#include <vector>
#ifdef BUTIL_OBJECT_POOL_NEED_FREE_ITEM_NUM
@@ -94,11 +94,12 @@ public:
typedef ObjectPoolFreeChunk<T, FREE_CHUNK_NITEM> FreeChunk;
typedef ObjectPoolFreeChunk<T, 0> DynamicFreeChunk;
+ typedef AlignedMemory<sizeof(T), __alignof__(T)> BlockItem;
// When a thread needs memory, it allocates a Block. To improve locality,
// items in the Block are only used by the thread.
// To support cache-aligned objects, align Block.items by cacheline.
struct BAIDU_CACHELINE_ALIGNMENT Block {
- ManualConstructor<T> items[BLOCK_NITEM];
+ BlockItem items[BLOCK_NITEM];
size_t nitem;
Block() : nitem(0) {}
@@ -160,12 +161,10 @@ public:
return _cur_free.ptrs[--_cur_free.nfree]; \
} \
T* obj = NULL; \
- auto ctor = [&](void* mem) { \
- obj = new (mem) T(__VA_ARGS__); \
- }; \
/* Fetch memory from local block */ \
if (_cur_block && _cur_block->nitem < BLOCK_NITEM) { \
- (_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
+ auto item = _cur_block->items + _cur_block->nitem; \
+ obj = new (item->void_data()) T(__VA_ARGS__); \
if (!ObjectPoolValidator<T>::validate(obj)) { \
obj->~T(); \
return NULL; \
@@ -176,7 +175,8 @@ public:
/* Fetch a Block from global */ \
_cur_block = add_block(&_cur_block_index); \
if (_cur_block != NULL) { \
- (_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
+ auto item = _cur_block->items + _cur_block->nitem; \
+ obj = new (item->void_data()) T(__VA_ARGS__); \
if (!ObjectPoolValidator<T>::validate(obj)) { \
obj->~T(); \
return NULL; \
diff --git a/src/butil/resource_pool_inl.h b/src/butil/resource_pool_inl.h
index 2ca858ad..316e37f3 100644
--- a/src/butil/resource_pool_inl.h
+++ b/src/butil/resource_pool_inl.h
@@ -25,11 +25,11 @@
#include <iostream> // std::ostream
#include <pthread.h> // pthread_mutex_t
#include <algorithm> // std::max, std::min
-#include "butil/atomicops.h" // butil::atomic
-#include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT
-#include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK
-#include "butil/thread_local.h" // thread_atexit
-#include "butil/memory/manual_constructor.h"
+#include "butil/atomicops.h" // butil::atomic
+#include "butil/macros.h" // BAIDU_CACHELINE_ALIGNMENT
+#include "butil/scoped_lock.h" // BAIDU_SCOPED_LOCK
+#include "butil/thread_local.h" // thread_atexit
+#include "butil/memory/aligned_memory.h" // butil::AlignedMemory
#include <vector>
#ifdef BUTIL_RESOURCE_POOL_NEED_FREE_ITEM_NUM
@@ -110,11 +110,12 @@ public:
typedef ResourcePoolFreeChunk<T, FREE_CHUNK_NITEM> FreeChunk;
typedef ResourcePoolFreeChunk<T, 0> DynamicFreeChunk;
+ typedef AlignedMemory<sizeof(T), __alignof__(T)> BlockItem;
// When a thread needs memory, it allocates a Block. To improve locality,
// items in the Block are only used by the thread.
// To support cache-aligned objects, align Block.items by cacheline.
struct BAIDU_CACHELINE_ALIGNMENT Block {
- ManualConstructor<T> items[BLOCK_NITEM];
+ BlockItem items[BLOCK_NITEM];
size_t nitem;
Block() : nitem(0) {}
@@ -182,13 +183,11 @@ public:
return unsafe_address_resource(free_id); \
} \
T* p = NULL; \
- auto ctor = [&](void* mem) { \
- p = new (mem) T(__VA_ARGS__); \
- }; \
/* Fetch memory from local block */ \
if (_cur_block && _cur_block->nitem < BLOCK_NITEM) { \
id->value = _cur_block_index * BLOCK_NITEM + _cur_block->nitem; \
- (_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
+ auto item = _cur_block->items + _cur_block->nitem; \
+ p = new (item->void_data()) T(__VA_ARGS__); \
if (!ResourcePoolValidator<T>::validate(p)) { \
p->~T(); \
return NULL; \
@@ -200,7 +199,8 @@ public:
_cur_block = add_block(&_cur_block_index); \
if (_cur_block != NULL) { \
id->value = _cur_block_index * BLOCK_NITEM + _cur_block->nitem; \
- (_cur_block->items + _cur_block->nitem)->InitBy(ctor); \
+ auto item = _cur_block->items + _cur_block->nitem; \
+ p = new (item->void_data()) T(__VA_ARGS__); \
if (!ResourcePoolValidator<T>::validate(p)) { \
p->~T(); \
return NULL; \
diff --git a/src/butil/single_threaded_pool.h b/src/butil/single_threaded_pool.h
index 591e423f..7f34b93c 100644
--- a/src/butil/single_threaded_pool.h
+++ b/src/butil/single_threaded_pool.h
@@ -61,10 +61,12 @@ public:
static const size_t NITEM = Block::NITEM;
static const size_t ITEM_SIZE = ITEM_SIZE_IN;
- SingleThreadedPool(const Allocator& alloc = Allocator())
+ explicit SingleThreadedPool(const Allocator& alloc = Allocator())
: _free_nodes(NULL), _blocks(NULL), _allocator(alloc) {}
~SingleThreadedPool() { reset(); }
+ DISALLOW_COPY_AND_ASSIGN(SingleThreadedPool);
+
void swap(SingleThreadedPool & other) {
std::swap(_free_nodes, other._free_nodes);
std::swap(_blocks, other._blocks);
@@ -132,12 +134,9 @@ public:
}
Allocator& get_allocator() { return _allocator; }
+ Allocator get_allocator() const { return _allocator; }
private:
- // You should not copy a pool.
- SingleThreadedPool(const SingleThreadedPool&);
- void operator=(const SingleThreadedPool&);
-
Node* _free_nodes;
Block* _blocks;
Allocator _allocator;
diff --git a/src/butil/type_traits.h b/src/butil/type_traits.h
index 3cf8473f..0ae91135 100644
--- a/src/butil/type_traits.h
+++ b/src/butil/type_traits.h
@@ -325,6 +325,17 @@ template <typename T> struct remove_pointer<T* const
volatile> {
typedef T type;
};
+// Shortcut for removing const, volatile and reference.
+#if __cplusplus >= 202002L
+template <typename T>
+using remove_cvref = std::remove_cvref<T>;
+#else
+template<typename T>
+struct remove_cvref {
+ typedef typename remove_cv<typename remove_reference<T>::type>::type type;
+};
+#endif
+
// is_reference is false except for reference types.
template<typename T> struct is_reference : false_type {};
template<typename T> struct is_reference<T&> : true_type {};
@@ -355,14 +366,14 @@ template <typename T> struct is_enum<const volatile T> :
is_enum<T> { };
// at compile time.
// If the callable is non-static member function,
// the first argument should be the class type.
-#if (__cplusplus >= 201703L)
+#if __cplusplus >= 201703L
// std::result_of is deprecated in C++17 and removed in C++20,
// use std::invoke_result instead.
template <typename>
struct result_of;
template <typename F, typename... Args>
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
-#elif (__cplusplus >= 201103L)
+#elif __cplusplus >= 201103L
template <typename F>
using result_of = std::result_of<F>;
#else
diff --git a/src/bvar/mvariable.cpp b/src/bvar/mvariable.cpp
index 925dccdf..d826893e 100644
--- a/src/bvar/mvariable.cpp
+++ b/src/bvar/mvariable.cpp
@@ -75,7 +75,9 @@ struct MVarMapWithLock : public MVarMap {
pthread_mutex_t mutex;
MVarMapWithLock() {
- CHECK_EQ(0, init(256, 80));
+ if (init(256) != 0) {
+ LOG(WARNING) << "Fail to init";
+ }
pthread_mutex_init(&mutex, NULL);
}
};
diff --git a/src/bvar/variable.cpp b/src/bvar/variable.cpp
index 4da0b1e0..e4f09355 100644
--- a/src/bvar/variable.cpp
+++ b/src/bvar/variable.cpp
@@ -76,7 +76,10 @@ struct VarMapWithLock : public VarMap {
pthread_mutex_t mutex;
VarMapWithLock() {
- CHECK_EQ(0, init(1024, 80));
+ if (init(1024) != 0) {
+ LOG(WARNING) << "Fail to init VarMap";
+ }
+
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
diff --git a/src/mcpack2pb/mcpack2pb.cpp b/src/mcpack2pb/mcpack2pb.cpp
index c810a011..7ab24e3e 100644
--- a/src/mcpack2pb/mcpack2pb.cpp
+++ b/src/mcpack2pb/mcpack2pb.cpp
@@ -32,8 +32,7 @@ static butil::FlatMap<std::string, MessageHandler>*
s_handler_map = NULL;
static void init_handler_map() {
s_handler_map = new butil::FlatMap<std::string, MessageHandler>;
if (s_handler_map->init(64, 50) != 0) {
- LOG(ERROR) << "Fail to init s_handler_map";
- exit(1);
+ LOG(WARNING) << "Fail to init s_handler_map";
}
}
void register_message_handler_or_die(const std::string& full_name,
diff --git a/test/BUILD.bazel b/test/BUILD.bazel
index 7f49fe71..3c4242da 100644
--- a/test/BUILD.bazel
+++ b/test/BUILD.bazel
@@ -139,7 +139,8 @@ TEST_BUTIL_SOURCES = [
#"popen_unittest.cpp",
"bounded_queue_unittest.cc",
"butil_unittest_main.cpp",
- "scope_guard_unittest.cc",
+ "scope_guard_unittest.cpp",
+ "optional_unittest.cpp",
] + select({
"@bazel_tools//tools/osx:darwin_x86_64": [],
"//conditions:default": [
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 7b1766e9..11f1ad3b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -174,8 +174,8 @@ SET(TEST_BUTIL_SOURCES
${PROJECT_SOURCE_DIR}/test/object_pool_unittest.cpp
${PROJECT_SOURCE_DIR}/test/test_switches.cc
${PROJECT_SOURCE_DIR}/test/scoped_locale.cc
- ${PROJECT_SOURCE_DIR}/test/scope_guard_unittest.cc
- ${PROJECT_SOURCE_DIR}/test/butil_unittest_main.cpp
+ ${PROJECT_SOURCE_DIR}/test/scope_guard_unittest.cpp
+ ${PROJECT_SOURCE_DIR}/test/optional_unittest.cpp
${PROJECT_SOURCE_DIR}/test/butil_unittest_main.cpp
)
diff --git a/test/Makefile b/test/Makefile
index f09b398d..874e6928 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -146,7 +146,8 @@ TEST_BUTIL_SOURCES = \
popen_unittest.cpp \
bounded_queue_unittest.cc \
butil_unittest_main.cpp \
- scope_guard_unittest.cc
+ scope_guard_unittest.cpp \
+ optional_unittest.cpp
ifeq ($(SYSTEM), Linux)
TEST_BUTIL_SOURCES += test_file_util_linux.cc \
diff --git a/test/brpc_controller_unittest.cpp
b/test/brpc_controller_unittest.cpp
index f7333281..3f410a25 100644
--- a/test/brpc_controller_unittest.cpp
+++ b/test/brpc_controller_unittest.cpp
@@ -130,7 +130,7 @@ TEST_F(ControllerTest, SessionKV) {
FLAGS_log_as_json = true;
}
- ASSERT_TRUE(endsWith(sink1, R"(,"@rid":"abcdEFG-456","M":"Session
ends.","Baidu":"NewStuff","Cisco":"33.330000","Apple":"1234567"})")) << sink1;
+ ASSERT_TRUE(endsWith(sink1, R"(,"@rid":"abcdEFG-456","M":"Session
ends.","Cisco":"33.330000","Apple":"1234567","Baidu":"NewStuff"})")) << sink1;
ASSERT_TRUE(startsWith(sink1, R"({"L":"I",)")) << sink1;
logging::SetLogSink(oldSink);
diff --git a/test/brpc_extension_unittest.cpp b/test/brpc_extension_unittest.cpp
index 7c01e5b1..eea238ee 100644
--- a/test/brpc_extension_unittest.cpp
+++ b/test/brpc_extension_unittest.cpp
@@ -68,5 +68,5 @@ TEST_F(ExtensionTest, basic) {
os.str("");
ConstIntExtension()->List(os, ':');
- ASSERT_EQ("bar:foo", os.str());
+ ASSERT_EQ("foo:bar", os.str());
}
diff --git a/test/brpc_http_message_unittest.cpp
b/test/brpc_http_message_unittest.cpp
index e9f25e8f..0d5a979a 100644
--- a/test/brpc_http_message_unittest.cpp
+++ b/test/brpc_http_message_unittest.cpp
@@ -610,27 +610,27 @@ TEST(HttpMessageTest, serialize_http_request) {
// user-set accept
header.SetHeader("accePT"/*intended uppercase*/, "blahblah");
MakeRawHttpRequest(&request, &header, ep, &content);
- ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\naccePT:
blahblah\r\nFoo: Bar\r\nHost: MyHost: 4321\r\nUser-Agent: brpc/1.0
curl/7.0\r\n\r\ndata", request);
+ ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nFoo: Bar\r\naccePT:
blahblah\r\nHost: MyHost: 4321\r\nUser-Agent: brpc/1.0 curl/7.0\r\n\r\ndata",
request);
// user-set UA
header.SetHeader("user-AGENT", "myUA");
MakeRawHttpRequest(&request, &header, ep, &content);
- ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\naccePT:
blahblah\r\nuser-AGENT: myUA\r\nFoo: Bar\r\nHost: MyHost: 4321\r\n\r\ndata",
request);
+ ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nFoo: Bar\r\naccePT:
blahblah\r\nHost: MyHost: 4321\r\nuser-AGENT: myUA\r\n\r\ndata", request);
// user-set Authorization
header.SetHeader("authorization", "myAuthString");
MakeRawHttpRequest(&request, &header, ep, &content);
- ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\naccePT:
blahblah\r\nuser-AGENT: myUA\r\nauthorization: myAuthString\r\nFoo:
Bar\r\nHost: MyHost: 4321\r\n\r\ndata", request);
+ ASSERT_EQ("POST / HTTP/1.1\r\nContent-Length: 4\r\nFoo: Bar\r\naccePT:
blahblah\r\nHost: MyHost: 4321\r\nuser-AGENT: myUA\r\nauthorization:
myAuthString\r\n\r\ndata", request);
header.SetHeader("Transfer-Encoding", "chunked");
MakeRawHttpRequest(&request, &header, ep, &content);
- ASSERT_EQ("POST / HTTP/1.1\r\naccePT: blahblah\r\nTransfer-Encoding:
chunked\r\nuser-AGENT: myUA\r\nauthorization: myAuthString\r\nFoo: Bar\r\nHost:
MyHost: 4321\r\n\r\ndata", request);
+ ASSERT_EQ("POST / HTTP/1.1\r\nFoo: Bar\r\naccePT:
blahblah\r\nTransfer-Encoding: chunked\r\nHost: MyHost: 4321\r\nuser-AGENT:
myUA\r\nauthorization: myAuthString\r\n\r\ndata", request);
// GET does not serialize content and user-set content-length is ignored.
header.set_method(brpc::HTTP_METHOD_GET);
header.SetHeader("Content-Length", "100");
MakeRawHttpRequest(&request, &header, ep, &content);
- ASSERT_EQ("GET / HTTP/1.1\r\naccePT: blahblah\r\nuser-AGENT:
myUA\r\nauthorization: myAuthString\r\nFoo: Bar\r\nHost: MyHost: 4321\r\n\r\n",
request);
+ ASSERT_EQ("GET / HTTP/1.1\r\nFoo: Bar\r\naccePT: blahblah\r\nHost: MyHost:
4321\r\nuser-AGENT: myUA\r\nauthorization: myAuthString\r\n\r\n", request);
}
TEST(HttpMessageTest, serialize_http_response) {
@@ -649,12 +649,12 @@ TEST(HttpMessageTest, serialize_http_response) {
// NULL content
header.SetHeader("Content-Length", "100");
MakeRawHttpResponse(&response, &header, NULL);
- ASSERT_EQ("HTTP/1.1 200 OK\r\nContent-Length: 100\r\nFoo: Bar\r\n\r\n",
response)
+ ASSERT_EQ("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 100\r\n\r\n",
response)
<< butil::ToPrintable(response);
header.SetHeader("Transfer-Encoding", "chunked");
MakeRawHttpResponse(&response, &header, NULL);
- ASSERT_EQ("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nFoo:
Bar\r\n\r\n", response)
+ ASSERT_EQ("HTTP/1.1 200 OK\r\nFoo: Bar\r\nTransfer-Encoding:
chunked\r\n\r\n", response)
<< butil::ToPrintable(response);
header.RemoveHeader("Transfer-Encoding");
@@ -667,7 +667,7 @@ TEST(HttpMessageTest, serialize_http_response) {
header.SetHeader("Content-Length", "100");
header.SetHeader("Transfer-Encoding", "chunked");
MakeRawHttpResponse(&response, &header, NULL);
- ASSERT_EQ("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nFoo:
Bar\r\n\r\n", response)
+ ASSERT_EQ("HTTP/1.1 200 OK\r\nFoo: Bar\r\nTransfer-Encoding:
chunked\r\n\r\n", response)
<< butil::ToPrintable(response);
header.RemoveHeader("Transfer-Encoding");
@@ -696,7 +696,7 @@ TEST(HttpMessageTest, serialize_http_response) {
// 2. User-set content-length is not ignored .
header.SetHeader("Content-Length", "100");
MakeRawHttpResponse(&response, &header, &content);
- ASSERT_EQ("HTTP/1.1 200 OK\r\nContent-Length: 100\r\nFoo: Bar\r\n\r\n",
response)
+ ASSERT_EQ("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 100\r\n\r\n",
response)
<< butil::ToPrintable(response);
}
diff --git a/test/flat_map_unittest.cpp b/test/flat_map_unittest.cpp
index c3368d36..a394218a 100644
--- a/test/flat_map_unittest.cpp
+++ b/test/flat_map_unittest.cpp
@@ -95,31 +95,49 @@ TEST_F(FlatMapTest, swap_pooled_allocator) {
TEST_F(FlatMapTest, copy_flat_map) {
typedef butil::FlatMap<std::string, std::string> Map;
- Map uninit_m1;
- ASSERT_FALSE(uninit_m1.initialized());
- ASSERT_TRUE(uninit_m1.empty());
+ Map default_init_m1;
+ ASSERT_TRUE(default_init_m1.initialized());
+ ASSERT_TRUE(default_init_m1.empty());
+ ASSERT_EQ(BRPC_FLATMAP_DEFAULT_NBUCKET, default_init_m1.bucket_count());
// self assignment does nothing.
- uninit_m1 = uninit_m1;
- ASSERT_FALSE(uninit_m1.initialized());
- ASSERT_TRUE(uninit_m1.empty());
- // Copy construct from uninitialized map.
- Map uninit_m2 = uninit_m1;
- ASSERT_FALSE(uninit_m2.initialized());
- ASSERT_TRUE(uninit_m2.empty());
- // assign uninitialized map to uninitialized map.
- Map uninit_m3;
- uninit_m3 = uninit_m1;
- ASSERT_FALSE(uninit_m3.initialized());
- ASSERT_TRUE(uninit_m3.empty());
- // assign uninitialized map to initialized map.
+ default_init_m1 = default_init_m1;
+ ASSERT_TRUE(default_init_m1.initialized());
+ ASSERT_TRUE(default_init_m1.empty());
+ ASSERT_EQ(BRPC_FLATMAP_DEFAULT_NBUCKET, default_init_m1.bucket_count());
+
+ Map default_init_m2 = default_init_m1;
+ ASSERT_TRUE(default_init_m2.initialized());
+ ASSERT_TRUE(default_init_m2.empty());
+ ASSERT_EQ(BRPC_FLATMAP_DEFAULT_NBUCKET, default_init_m1.bucket_count());
+
+ Map init_m3;
+ ASSERT_TRUE(init_m3.initialized());
+ // smaller than the default value, and the default buckets
+ // is continued to be used.
+ ASSERT_EQ(0, init_m3.init(8));
+ ASSERT_TRUE(init_m3.initialized());
+ ASSERT_EQ(BRPC_FLATMAP_DEFAULT_NBUCKET, init_m3.bucket_count());
+ ASSERT_EQ(init_m3._default_buckets, init_m3._buckets);
+ init_m3["hello"] = "world";
+ ASSERT_EQ(1u, init_m3.size());
+ init_m3 = default_init_m1;
+ ASSERT_TRUE(init_m3.initialized());
+ ASSERT_TRUE(init_m3.empty());
+
Map init_m4;
- ASSERT_EQ(0, init_m4.init(16));
ASSERT_TRUE(init_m4.initialized());
+ // Resize to a larger buckets, and then not using the default buckets.
+ ASSERT_EQ(0, init_m4.init(BRPC_FLATMAP_DEFAULT_NBUCKET + 1));
+ ASSERT_EQ(butil::flatmap_round(BRPC_FLATMAP_DEFAULT_NBUCKET + 1),
+ init_m4.bucket_count());
+ ASSERT_NE(init_m4._default_buckets, init_m4._buckets);
init_m4["hello"] = "world";
ASSERT_EQ(1u, init_m4.size());
- init_m4 = uninit_m1;
+ init_m4 = default_init_m1;
ASSERT_TRUE(init_m4.initialized());
ASSERT_TRUE(init_m4.empty());
+ ASSERT_EQ(butil::flatmap_round(BRPC_FLATMAP_DEFAULT_NBUCKET + 1),
+ init_m4.bucket_count());
Map m1;
ASSERT_EQ(0, m1.init(16));
@@ -173,7 +191,7 @@ TEST_F(FlatMapTest, copy_flat_map) {
const void* old_buckets4 = m4._buckets;
m4 = m1;
ASSERT_EQ(m1.bucket_count(), m4.bucket_count());
- ASSERT_NE(old_buckets4, m4._buckets);
+ ASSERT_EQ(old_buckets4, m4._buckets);
ASSERT_EQ(expected_count, m4.size());
ASSERT_EQ("world", m4["hello"]);
ASSERT_EQ("bar", m4["foo"]);
@@ -226,7 +244,7 @@ TEST_F(FlatMapTest, to_lower) {
for (int c = -128; c < 128; ++c) {
ASSERT_EQ((char)::tolower(c), butil::ascii_tolower(c)) << "c=" << c;
}
-
+
const size_t input_len = 102;
char input[input_len + 1];
char input2[input_len + 1];
@@ -379,7 +397,7 @@ TEST_F(FlatMapTest, flat_map_of_string) {
for (size_t i = 0; i < N; ++i) {
keys.push_back(butil::string_printf("up_latency_as_key_%lu", i));
}
-
+
tm1.start();
for (size_t i = 0; i < N; ++i) {
m1[keys[i]] += i;
@@ -441,12 +459,64 @@ TEST_F(FlatMapTest, flat_map_of_string) {
LOG(INFO) << "finding c_strings takes " << tm1.n_elapsed()/N
<< " " << tm2.n_elapsed()/N << " " << tm3.n_elapsed()/N
<< " " << tm1_2.n_elapsed()/N << " sum=" << sum;
-
+
for (size_t i = 0; i < N; ++i) {
ASSERT_EQ(i, m1[keys[i]]) << "i=" << i;
ASSERT_EQ(i, m2[keys[i]]);
ASSERT_EQ(i, m3[keys[i]]);
}
+
+ butil::FlatMap<std::string, std::string> m4;
+ m4["111"] = "222";
+ ASSERT_TRUE(m4.seek("111"));
+ ASSERT_EQ("222", *m4.seek("111"));
+ ASSERT_EQ(1UL, m4.size());
+ butil::FlatMap<std::string, std::string> m5;
+ m5["333"] = "444";
+ ASSERT_TRUE(m5.seek("333"));
+ ASSERT_EQ("444", *m5.seek("333"));
+ ASSERT_EQ(1UL, m5.size());
+
+ m4.swap(m5);
+ ASSERT_TRUE(m4.seek("333"));
+ ASSERT_EQ("444", *m4.seek("333"));
+ ASSERT_EQ(1UL, m4.size());
+ ASSERT_TRUE(m5.seek("111"));
+ ASSERT_EQ("222", *m5.seek("111"));
+ ASSERT_EQ(1UL, m5.size());
+
+ m4.resize(BRPC_FLATMAP_DEFAULT_NBUCKET + 1);
+ ASSERT_EQ(1UL, m4.size());
+ ASSERT_TRUE(m4.seek("333"));
+ ASSERT_EQ("444", *m4.seek("333"));
+ m4.swap(m5);
+ ASSERT_TRUE(m4.seek("111"));
+ ASSERT_EQ("222", *m4.seek("111"));
+ ASSERT_EQ(1UL, m4.size());
+ ASSERT_TRUE(m5.seek("333"));
+ ASSERT_EQ("444", *m5.seek("333"));
+ ASSERT_EQ(1UL, m5.size());
+
+ m5.swap(m4);
+ ASSERT_TRUE(m4.seek("333"));
+ ASSERT_EQ("444", *m4.seek("333"));
+ ASSERT_EQ(1UL, m4.size());
+ ASSERT_TRUE(m5.seek("111"));
+ ASSERT_EQ("222", *m5.seek("111"));
+ ASSERT_EQ(1UL, m5.size());
+
+ m5.resize(BRPC_FLATMAP_DEFAULT_NBUCKET + 1);
+ ASSERT_EQ(1UL, m5.size());
+ ASSERT_EQ("222", *m5.seek("111"));
+ ASSERT_EQ(1UL, m5.size());
+ m5.swap(m4);
+ ASSERT_TRUE(m4.seek("111"));
+ ASSERT_EQ("222", *m4.seek("111"));
+ ASSERT_EQ(1UL, m4.size());
+ ASSERT_TRUE(m5.seek("333"));
+ ASSERT_EQ("444", *m5.seek("333"));
+ ASSERT_EQ(1UL, m5.size());
+
}
TEST_F(FlatMapTest, fast_iterator) {
@@ -457,7 +527,7 @@ TEST_F(FlatMapTest, fast_iterator) {
M2 m2;
ASSERT_EQ(0, m1.init(16384));
- ASSERT_EQ(-1, m1.init(1));
+ ASSERT_EQ(0, m1.init(1));
ASSERT_EQ(0, m2.init(16384));
ASSERT_EQ(NULL, m1._thumbnail);
@@ -537,11 +607,11 @@ typedef butil::FlatMap<uint64_t, uint64_t>
PositionHintMap;
static void fill_position_hint_map(PositionHintMap* map,
std::vector<uint64_t>* keys) {
srand(time(NULL));
- const size_t N = 170;
+ const size_t N = 5;
if (!map->initialized()) {
ASSERT_EQ(0, map->init(N * 3 / 2, 80));
}
-
+
keys->reserve(N);
keys->clear();
map->clear();
@@ -553,7 +623,7 @@ static void fill_position_hint_map(PositionHintMap* map,
keys->push_back(key);
(*map)[key] = i;
}
- LOG(INFO) << map->bucket_info();
+ LOG(INFO) << map->bucket_info() << ", size=" << map->size();
}
struct CountOnPause {
@@ -601,7 +671,7 @@ struct RemoveInsertVisitedOnPause {
removed_keys.insert(removed_key);
break;
} while (true);
-
+
// Insert one
uint64_t inserted_key =
((rand() % hint.offset) + rand() * hint.nbucket);
@@ -844,14 +914,17 @@ struct Value {
Value() : x_(0) { ++n_con; }
Value(int x) : x_(x) { ++ n_con; }
Value (const Value& rhs) : x_(rhs.x_) { ++ n_cp_con; }
- ~Value() { ++ n_des; }
-
+ ~Value() {
+ ++ n_des;
+ // CHECK(false);
+ }
+
Value& operator= (const Value& rhs) {
x_ = rhs.x_;
++ n_cp;
return *this;
}
-
+
bool operator== (const Value& rhs) const { return x_ == rhs.x_; }
bool operator!= (const Value& rhs) const { return x_ != rhs.x_; }
@@ -900,16 +973,41 @@ TEST_F(FlatMapTest,
key_value_are_not_constructed_before_first_insertion) {
TEST_F(FlatMapTest, manipulate_uninitialized_map) {
butil::FlatMap<int, int> m;
- ASSERT_FALSE(m.initialized());
- for (butil::FlatMap<int,int>::iterator it = m.begin(); it != m.end();
++it) {
- LOG(INFO) << "nothing";
- }
+ ASSERT_TRUE(m.initialized());
ASSERT_EQ(NULL, m.seek(1));
ASSERT_EQ(0u, m.erase(1));
ASSERT_EQ(0u, m.size());
ASSERT_TRUE(m.empty());
- ASSERT_EQ(0u, m.bucket_count());
- ASSERT_EQ(0u, m.load_factor());
+ ASSERT_EQ(BRPC_FLATMAP_DEFAULT_NBUCKET, m.bucket_count());
+ ASSERT_EQ(80u, m.load_factor());
+ m[1] = 1;
+ ASSERT_EQ(1UL, m.size());
+ auto one = m.seek(1);
+ ASSERT_NE(nullptr, one);
+ ASSERT_EQ(1, *one);
+
+ butil::FlatMap<int, int> m2 = m;
+ one = m2.seek(1);
+ ASSERT_NE(nullptr, one);
+ ASSERT_EQ(1, *one);
+ m2[2] = 2;
+ ASSERT_EQ(2UL, m2.size());
+
+ m.swap(m2);
+ ASSERT_EQ(2UL, m.size());
+ ASSERT_EQ(1UL, m2.size());
+ auto two = m.seek(2);
+ ASSERT_NE(nullptr, two);
+ ASSERT_EQ(2, *two);
+
+ ASSERT_EQ(1UL, m2.erase(1));
+ ASSERT_EQ(0, m.init(32));
+ one = m.seek(1);
+ ASSERT_NE(nullptr, one);
+ ASSERT_EQ(1, *one);
+ two = m.seek(2);
+ ASSERT_NE(nullptr, two);
+ ASSERT_EQ(2, *two);
}
TEST_F(FlatMapTest, perf_small_string_map) {
@@ -948,7 +1046,7 @@ TEST_F(FlatMapTest, perf_small_string_map) {
m2["Request-Id"] = "true";
m2["Status-Code"] = "200";
tm2.stop();
-
+
LOG(INFO) << "flatmap=" << tm1.n_elapsed()
<< " ci_flatmap=" << tm4.n_elapsed()
<< " map=" << tm2.n_elapsed()
@@ -956,12 +1054,10 @@ TEST_F(FlatMapTest, perf_small_string_map) {
}
}
-
TEST_F(FlatMapTest, sanity) {
typedef butil::FlatMap<uint64_t, long> Map;
Map m;
-
- ASSERT_FALSE(m.initialized());
+ ASSERT_TRUE(m.initialized());
m.init(1000, 70);
ASSERT_TRUE(m.initialized());
ASSERT_EQ(0UL, m.size());
@@ -981,7 +1077,7 @@ TEST_F(FlatMapTest, sanity) {
long* p = m.seek(k1);
ASSERT_TRUE(p && *p == 10);
ASSERT_EQ(0UL, m._pool.count_allocated());
-
+
ASSERT_EQ(NULL, m.seek(k2));
// Override
@@ -990,7 +1086,7 @@ TEST_F(FlatMapTest, sanity) {
ASSERT_FALSE(m.empty());
p = m.seek(k1);
ASSERT_TRUE(p && *p == 100);
-
+
// Insert another
m[k3] = 20;
ASSERT_EQ(2UL, m.size());
@@ -1006,7 +1102,7 @@ TEST_F(FlatMapTest, sanity) {
ASSERT_FALSE(m.empty());
p = m.seek(k2);
ASSERT_TRUE(p && *p == 30);
-
+
ASSERT_EQ(NULL, m.seek(2049));
Map::iterator it = m.begin();
@@ -1061,9 +1157,13 @@ TEST_F(FlatMapTest, random_insert_erase) {
for (int i = 0; i < 100000; ++i) {
int k = rand() % 0xFFFF;
int p = rand() % 1000;
+ ht[0].insert(k, i);
+ // LOG(INFO) << "i=" << i << " k=" << k;
+
+ // ASSERT_EQ(n_con + n_cp_con, n_des * 2)
+ // << " n_con=" << n_con << " n_cp_con=" << n_cp_con << "
n_des=" << n_des << " n_cp=" << n_cp;
+ ref[0][k] = i;
if (p < 600) {
- ht[0].insert(k, i);
- ref[0][k] = i;
} else if(p < 999) {
ht[0].erase (k);
ref[0].erase (k);
@@ -1072,8 +1172,8 @@ TEST_F(FlatMapTest, random_insert_erase) {
ref[0].clear();
}
}
-
- LOG(INFO) << "Check j=" << j;
+
+ // LOG(INFO) << "Check j=" << j;
// bi-check
for (int i=0; i<2; ++i) {
for (Map::iterator it = ht[i].begin(); it != ht[i].end(); ++it)
@@ -1082,7 +1182,7 @@ TEST_F(FlatMapTest, random_insert_erase) {
ASSERT_TRUE (it2 != ref[i].end());
ASSERT_EQ (it2->second, it->second);
}
-
+
for (butil::hash_map<uint64_t, Value>::iterator it =
ref[i].begin();
it != ref[i].end(); ++it)
{
@@ -1095,11 +1195,10 @@ TEST_F(FlatMapTest, random_insert_erase) {
}
}
- // cout << "ht[0] = " << show(ht[0]) << endl
- // << "ht[1] = " << show(ht[1]) << endl;
- //ASSERT_EQ (ht[0]._pool->alloc_num(), 0ul);
- ASSERT_EQ (n_con + n_cp_con, n_des);
+ ASSERT_EQ (n_con + n_cp_con, n_des)
+ // todo delete
+ << "n_con=" << n_con << " n_cp_con=" << n_cp_con << " n_des=" << n_des
<< " n_cp=" << n_cp;
LOG(INFO) << "n_con:" << n_con << std::endl
<< "n_cp_con:" << n_cp_con << std::endl
@@ -1156,8 +1255,8 @@ void perf_insert_erase(bool random, const T& value) {
if (random) {
random_shuffle(keys.begin(), keys.end());
}
-
- id_map.clear();
+
+ id_map.clear();
id_tm.start();
for (size_t i = 0; i < keys.size(); ++i) {
id_map[keys[i]] = value;
@@ -1293,7 +1392,7 @@ void perf_seek(const T& value) {
butil::hash_map<uint64_t, T> hash_map;
butil::Timer id_tm, multi_id_tm, std_tm, pooled_tm,
std_unordered_tm, std_unordered_multi_tm, hash_tm;
-
+
id_map.init((size_t)(nkeys[NPASS-1] * 1.5));
multi_id_map.init((size_t)(nkeys[NPASS-1] * 1.5));
LOG(INFO) << "[ value = " << sizeof(T) << " bytes ]";
@@ -1303,8 +1402,8 @@ void perf_seek(const T& value) {
for (size_t i = 0; i < nkeys[pass]; ++i) {
keys.push_back(start + i);
}
-
- id_map.clear();
+
+ id_map.clear();
for (size_t i = 0; i < keys.size(); ++i) {
id_map[keys[i]] = value;
}
@@ -1428,25 +1527,6 @@ TEST_F(FlatMapTest, copy) {
m2 = m1;
ASSERT_FALSE(m1.is_too_crowded(m1.size()));
ASSERT_FALSE(m2.is_too_crowded(m1.size()));
-
- butil::FlatMap<int, int> m3;
- ASSERT_FALSE(m3.initialized());
- m1 = m3;
- ASSERT_TRUE(m1.empty());
- ASSERT_TRUE(m1.initialized());
-
- m3 = m2;
- ASSERT_TRUE(m3.initialized());
- ASSERT_TRUE(m3.seek(1));
- ASSERT_TRUE(m3.seek(2));
- ASSERT_FALSE(m3.seek(3));
-
- m3.clear();
- ASSERT_TRUE(m3.initialized());
- ASSERT_TRUE(m3.empty());
- butil::FlatMap<int, int> m4 = m3;
- ASSERT_TRUE(m4.initialized());
- ASSERT_TRUE(m4.empty());
}
TEST_F(FlatMapTest, multi) {
@@ -1487,8 +1567,8 @@ TEST_F(FlatMapTest, multi) {
int same_bucket_key = 1 + bucket_count;
butil::DefaultHasher<int> hasher;
ASSERT_EQ(butil::flatmap_mod(hasher(1), bucket_count),
- butil::flatmap_mod(hasher(same_bucket_key), bucket_count));
- ASSERT_EQ(0, map.erase(same_bucket_key));
+ butil::flatmap_mod(hasher(same_bucket_key), bucket_count));
+ ASSERT_EQ(0UL, map.erase(same_bucket_key));
Foo& f5 = map[same_bucket_key];
ASSERT_EQ(4UL, map.size());
ASSERT_EQ(&f5, map.seek(same_bucket_key));
diff --git a/test/optional_unittest.cpp b/test/optional_unittest.cpp
new file mode 100644
index 00000000..8038105c
--- /dev/null
+++ b/test/optional_unittest.cpp
@@ -0,0 +1,298 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+
+#include <gtest/gtest.h>
+#include "butil/containers/optional.h"
+
+namespace {
+
+butil::optional<int> empty_optional() {
+ return {};
+}
+
+TEST(OptionalTest, sanity) {
+ {
+ butil::optional<int> empty;
+ ASSERT_FALSE(empty);
+ }
+
+ {
+ butil::optional<int> empty{};
+ ASSERT_FALSE(empty);
+ }
+
+ {
+ butil::optional<int> empty = empty_optional();
+ ASSERT_FALSE(empty);
+ }
+
+ {
+ butil::optional<int> non_empty(42);
+ ASSERT_TRUE(non_empty);
+ ASSERT_TRUE(non_empty.has_value());
+ ASSERT_EQ(*non_empty, 42);
+ }
+
+ butil::optional<std::string> opt_string = "abc";
+ ASSERT_TRUE(opt_string);
+ ASSERT_EQ(*opt_string, "abc");
+}
+
+TEST(OptionalTest, nullopt) {
+ butil::optional<int> empty(butil::nullopt);
+ ASSERT_FALSE(empty);
+
+ empty = 1;
+ ASSERT_TRUE(empty);
+ ASSERT_EQ(1, empty);
+
+ empty = butil::nullopt;
+ ASSERT_FALSE(empty);
+}
+
+TEST(OptionalTest, copy) {
+ butil::optional<int> op(1);
+ ASSERT_TRUE(op);
+ ASSERT_EQ(1, op);
+
+ butil::optional<int> non_empty(op);
+ ASSERT_TRUE(non_empty);
+
+ op = butil::nullopt;
+ ASSERT_FALSE(op);
+
+ butil::optional<int> empty(op);
+ ASSERT_FALSE(empty);
+
+ non_empty = empty;
+ ASSERT_FALSE(non_empty);
+
+ op = 10;
+ non_empty = op;
+ ASSERT_TRUE(non_empty);
+ ASSERT_EQ(10, non_empty);
+}
+
+TEST(OptionalTest, move) {
+ butil::optional<int> empty;
+ ASSERT_FALSE(empty);
+ butil::optional<int> non_empty = 1;
+ ASSERT_TRUE(non_empty);
+ ASSERT_EQ(1, non_empty);
+
+ butil::optional<int> empty_move(std::move(empty));
+ ASSERT_FALSE(empty_move);
+
+ butil::optional<int> non_empty_move(std::move(non_empty));
+ ASSERT_TRUE(non_empty_move);
+ ASSERT_EQ(1, non_empty_move);
+
+ butil::optional<int> empty_move_assign;
+ empty_move_assign = std::move(empty);
+ ASSERT_FALSE(empty_move_assign);
+}
+
+struct Obj {};
+
+struct Convert {
+ Convert()
+ :default_ctor(false), move_ctor(false) { }
+ explicit Convert(const Obj&)
+ :default_ctor(true), move_ctor(false) { }
+ explicit Convert(Obj&&)
+ :default_ctor(true), move_ctor(true) { }
+
+ bool default_ctor;
+ bool move_ctor;
+};
+
+struct ConvertFromOptional {
+ ConvertFromOptional()
+ :default_ctor(false), move_ctor(false), from_optional(false) { }
+ ConvertFromOptional(const Obj&)
+ :default_ctor(true), move_ctor(false), from_optional(false) { }
+ ConvertFromOptional(Obj&&)
+ :default_ctor(true), move_ctor(true), from_optional(false) { }
+ ConvertFromOptional(
+ const butil::optional<Obj>&)
+ :default_ctor(true), move_ctor(false), from_optional(true) { }
+ ConvertFromOptional(butil::optional<Obj>&&)
+ :default_ctor(true), move_ctor(true), from_optional(true) { }
+
+ bool default_ctor;
+ bool move_ctor;
+ bool from_optional;
+};
+
+TEST(OptionalTest, convert) {
+ butil::optional<Obj> i_empty;
+ ASSERT_FALSE(i_empty);
+ butil::optional<Obj> i(butil::in_place);
+ ASSERT_TRUE(i);
+ {
+ butil::optional<Convert> empty(i_empty);
+ ASSERT_FALSE(empty);
+ butil::optional<Convert> opt_copy(i);
+ ASSERT_TRUE(opt_copy);
+ ASSERT_TRUE(opt_copy->default_ctor);
+ ASSERT_FALSE(opt_copy->move_ctor);
+
+ butil::optional<Convert> opt_move(butil::optional<Obj>{
butil::in_place });
+ ASSERT_TRUE(opt_move);
+ ASSERT_TRUE(opt_move->default_ctor);
+ ASSERT_TRUE(opt_move->move_ctor);
+ }
+
+ {
+ static_assert(
+ std::is_convertible<butil::optional<Obj>,
+ butil::optional<ConvertFromOptional>>::value,
+ "");
+ butil::optional<ConvertFromOptional> opt0 = i_empty;
+ ASSERT_TRUE(opt0);
+ ASSERT_TRUE(opt0->default_ctor);
+ ASSERT_FALSE(opt0->move_ctor);
+ ASSERT_TRUE(opt0->from_optional);
+ butil::optional<ConvertFromOptional> opt1 = butil::optional<Obj>();
+ ASSERT_TRUE(opt1);
+ ASSERT_TRUE(opt1->default_ctor);
+ ASSERT_TRUE(opt1->move_ctor);
+ ASSERT_TRUE(opt1->from_optional);
+ }
+}
+
+TEST(OptionalTest, value) {
+ butil::optional<double> opt_empty;
+ butil::optional<double> opt_double = 1.0;
+ ASSERT_THROW(opt_empty.value(), butil::bad_optional_access);
+ ASSERT_EQ(10.0, opt_empty.value_or(10));
+ ASSERT_EQ(1.0, opt_double.value());
+ ASSERT_EQ(1.0, opt_double.value_or(42));
+ ASSERT_EQ(10.0, butil::optional<double>().value_or(10));
+ ASSERT_EQ(1.0, butil::optional<double>(1).value_or(10));
+}
+
+TEST(OptionalTest, emplace) {
+ butil::optional<std::string> opt_string;
+ ASSERT_TRUE((std::is_same<std::string&,
decltype(opt_string.emplace("abc"))>::value));
+ std::string& str = opt_string.emplace("abc");
+ ASSERT_EQ(&str, &opt_string.value());
+}
+
+TEST(OptionalTest, swap) {
+ butil::optional<int> opt_empty, opt1 = 1, opt2 = 2;
+ ASSERT_FALSE(opt_empty);
+ ASSERT_TRUE(opt1);
+ ASSERT_EQ(1, opt1.value());
+ ASSERT_TRUE(opt2);
+ ASSERT_EQ(2, opt2.value());
+ swap(opt_empty, opt1);
+ ASSERT_FALSE(opt1);
+ ASSERT_TRUE(opt_empty);
+ ASSERT_EQ(1, opt_empty.value());
+ ASSERT_TRUE(opt2);
+ ASSERT_EQ(2, opt2.value());
+ swap(opt_empty, opt1);
+ ASSERT_FALSE(opt_empty);
+ ASSERT_TRUE(opt1);
+ ASSERT_EQ(1, opt1.value());
+ ASSERT_TRUE(opt2);
+ ASSERT_EQ(2, opt2.value());
+ swap(opt1, opt2);
+ ASSERT_FALSE(opt_empty);
+ ASSERT_TRUE(opt1);
+ ASSERT_EQ(2, opt1.value());
+ ASSERT_TRUE(opt2);
+ ASSERT_EQ(1, opt2.value());
+
+ ASSERT_TRUE(noexcept(opt1.swap(opt2)));
+ ASSERT_TRUE(noexcept(swap(opt1, opt2)));
+}
+
+TEST(OptionalTest, make_optional) {
+ auto opt_int = butil::make_optional(1);
+ ASSERT_TRUE((std::is_same<decltype(opt_int),
butil::optional<int>>::value));
+ ASSERT_EQ(1, opt_int);
+}
+
+TEST(OptionalTest, comparison) {
+ butil::optional<int> empty;
+ butil::optional<int> one = 1;
+ butil::optional<int> two = 2;
+ ASSERT_TRUE(empty == empty);
+ ASSERT_FALSE(empty == one);
+ ASSERT_FALSE(empty == two);
+ ASSERT_TRUE(empty == butil::nullopt);
+ ASSERT_TRUE(one == one);
+ ASSERT_FALSE(one == two);
+ ASSERT_FALSE(one == butil::nullopt);
+ ASSERT_TRUE(two == two);
+ ASSERT_FALSE(two == butil::nullopt);
+
+ ASSERT_FALSE(empty != empty);
+ ASSERT_TRUE(empty != one);
+ ASSERT_TRUE(empty != two);
+ ASSERT_FALSE(empty != butil::nullopt);
+ ASSERT_FALSE(one != one);
+ ASSERT_TRUE(one != two);
+ ASSERT_TRUE(one != butil::nullopt);
+ ASSERT_FALSE(two != two);
+ ASSERT_TRUE(two != butil::nullopt);
+
+ ASSERT_FALSE(empty < empty);
+ ASSERT_TRUE(empty < one);
+ ASSERT_TRUE(empty < two);
+ ASSERT_FALSE(empty < butil::nullopt);
+ ASSERT_FALSE(one < one);
+ ASSERT_TRUE(one < two);
+ ASSERT_FALSE(one < butil::nullopt);
+ ASSERT_FALSE(two < two);
+ ASSERT_FALSE(two < butil::nullopt);
+
+ ASSERT_TRUE(empty <= empty);
+ ASSERT_TRUE(empty <= one);
+ ASSERT_TRUE(empty <= two);
+ ASSERT_TRUE(empty <= butil::nullopt);
+ ASSERT_TRUE(one <= one);
+ ASSERT_TRUE(one <= two);
+ ASSERT_FALSE(one <= butil::nullopt);
+ ASSERT_TRUE(two <= two);
+ ASSERT_FALSE(two <= butil::nullopt);
+
+ ASSERT_FALSE(empty > empty);
+ ASSERT_FALSE(empty > one);
+ ASSERT_FALSE(empty > two);
+ ASSERT_FALSE(empty > butil::nullopt);
+ ASSERT_FALSE(one > one);
+ ASSERT_FALSE(one > two);
+ ASSERT_TRUE(one > butil::nullopt);
+ ASSERT_FALSE(two > two);
+ ASSERT_TRUE(two > butil::nullopt);
+
+ ASSERT_TRUE(empty >= empty);
+ ASSERT_FALSE(empty >= one);
+ ASSERT_FALSE(empty >= two);
+ ASSERT_TRUE(empty >= butil::nullopt);
+ ASSERT_TRUE(one >= one);
+ ASSERT_FALSE(one >= two);
+ ASSERT_TRUE(one >= butil::nullopt);
+ ASSERT_TRUE(two >= two);
+ ASSERT_TRUE(two >= butil::nullopt);
+}
+
+} // namespace
diff --git a/test/scope_guard_unittest.cc b/test/scope_guard_unittest.cpp
similarity index 100%
rename from test/scope_guard_unittest.cc
rename to test/scope_guard_unittest.cpp
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]