Commit 1fa9b9fa by Wenjie Huang

move sampler.cpp to csrc

parent 66045271
......@@ -100,3 +100,20 @@ endif()
if (WITH_MTMETIS)
target_link_libraries(${PROJECT_NAME} PRIVATE mtmetis)
endif()
# add libsampler.so
set(SAMLPER_NAME "${PROJECT_NAME}_sampler")
file(GLOB_RECURSE SAMPLER_SRCS "csrc/sampler/*.cpp")
add_library(${SAMLPER_NAME} SHARED ${SAMPLER_SRCS})
target_include_directories(${SAMLPER_NAME} PRIVATE "csrc/sampler/include")
target_link_libraries(${SAMLPER_NAME} PRIVATE ${TORCH_LIBRARIES})
target_compile_definitions(${SAMLPER_NAME} PRIVATE -DTORCH_EXTENSION_NAME=lib${SAMLPER_NAME})
if(WITH_PYTHON)
find_library(TORCH_PYTHON_LIBRARY torch_python PATHS "${TORCH_INSTALL_PREFIX}/lib")
target_link_libraries(${SAMLPER_NAME} PRIVATE ${TORCH_PYTHON_LIBRARY})
endif()
#include <iostream>
#include <omp.h>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <torch/extension.h>
#include <time.h>
#include <random>
#include <parallel_hashmap/phmap.h>
// #include <boost/thread/mutex.hpp>
// #define MTX boost::mutex
#define MTX std::mutex
using namespace std;
namespace py = pybind11;
namespace th = torch;
typedef int64_t NodeIDType;
typedef int64_t EdgeIDType;
typedef float WeightType;
typedef float TimeStampType;
#define EXTRAARGS , phmap::priv::hash_default_hash<K>, \
phmap::priv::hash_default_eq<K>, \
std::allocator<K>, 4, MTX
template <class K>
using HashT = phmap::parallel_flat_hash_set<K EXTRAARGS>;
template <class K, class V>
using HashM = phmap::parallel_flat_hash_map<K, V EXTRAARGS>;
class TemporalNeighborBlock;
class TemporalGraphBlock;
TemporalNeighborBlock& get_neighbors(string graph_name, th::Tensor row, th::Tensor col, int64_t num_nodes, int is_distinct, optional<th::Tensor> eid, optional<th::Tensor> edge_weight, optional<th::Tensor> time);
th::Tensor heads_unique(th::Tensor array, th::Tensor heads, int threads);
int nodeIdToInOut(NodeIDType nid, int pid, const vector<NodeIDType>& part_ptr);
int nodeIdToPartId(NodeIDType nid, const vector<NodeIDType>& part_ptr);
vector<th::Tensor> divide_nodes_to_part(th::Tensor nodes, const vector<NodeIDType>& part_ptr, int threads);
// vector<int64_t> sample_multinomial(vector<WeightType> weights, int num_samples, bool replacement, default_random_engine e);
NodeIDType sample_multinomial(const vector<WeightType>& weights, default_random_engine& e);
template<typename T>
inline py::array vec2npy(const std::vector<T> &vec)
{
// need to let python garbage collector handle C++ vector memory
// see https://github.com/pybind/pybind11/issues/1042
// non-copy value transfer
auto v = new std::vector<T>(vec);
auto capsule = py::capsule(v, [](void *v)
{ delete reinterpret_cast<std::vector<T> *>(v); });
return py::array(v->size(), v->data(), capsule);
// return py::array(vec.size(), vec.data());
}
/*
* NeighborSampler Utils
*/
class TemporalNeighborBlock
{
public:
vector<vector<NodeIDType>> neighbors;
vector<vector<TimeStampType>> timestamp;
vector<vector<EdgeIDType>> eid;
vector<vector<WeightType>> edge_weight;
vector<phmap::parallel_flat_hash_map<NodeIDType, int64_t>> inverted_index;
vector<int64_t> deg;
vector<phmap::parallel_flat_hash_set<NodeIDType>> neighbors_set;
bool with_eid = false;
bool weighted = false;
bool with_timestamp = false;
TemporalNeighborBlock(){}
// TemporalNeighborBlock(const TemporalNeighborBlock &tnb);
TemporalNeighborBlock(vector<vector<NodeIDType>>& neighbors,
vector<int64_t> &deg):
neighbors(neighbors), deg(deg){}
TemporalNeighborBlock(vector<vector<NodeIDType>>& neighbors,
vector<vector<WeightType>>& edge_weight,
vector<vector<EdgeIDType>>& eid,
vector<int64_t> &deg):
neighbors(neighbors), edge_weight(edge_weight),eid(eid), deg(deg)
{ this->with_eid=true;
this->weighted=true; }
TemporalNeighborBlock(vector<vector<NodeIDType>>& neighbors,
vector<vector<WeightType>>& edge_weight,
vector<vector<TimeStampType>>& timestamp,
vector<vector<EdgeIDType>>& eid,
vector<int64_t> &deg):
neighbors(neighbors), edge_weight(edge_weight), timestamp(timestamp),eid(eid), deg(deg)
{ this->with_eid=true;
this->weighted=true;
this->with_timestamp=true;}
py::array get_node_neighbor(NodeIDType node_id){
return vec2npy(neighbors[node_id]);
}
py::array get_node_neighbor_timestamp(NodeIDType node_id){
return vec2npy(timestamp[node_id]);
}
int64_t get_node_deg(NodeIDType node_id){
return deg[node_id];
}
bool empty(){
return this->deg.empty();
}
void update_edge_weight(th::Tensor row_or_eid, th::Tensor col, th::Tensor edge_weight);
void update_node_weight(th::Tensor nid, th::Tensor node_weight);
void update_all_node_weight(th::Tensor node_weight);
int64_t update_neighbors_with_time(th::Tensor row, th::Tensor col, th::Tensor time, th::Tensor eid, int is_distinct, std::optional<th::Tensor> edge_weight);
std::string serialize() const {
std::ostringstream oss;
// 序列化基本类型成员
oss << with_eid << " " << weighted << " " << with_timestamp << " ";
// 序列化 vector<vector<T>> 类型成员
auto serializeVecVec = [&oss](const auto& vecVec) {
for (const auto& vec : vecVec) {
oss << vec.size() << " ";
for (const auto& elem : vec) {
oss << elem << " ";
}
}
oss << "|"; // 添加一个分隔符以区分不同的 vector
};
serializeVecVec(neighbors);
serializeVecVec(timestamp);
serializeVecVec(eid);
serializeVecVec(edge_weight);
// 序列化 vector<int64_t> 类型成员
oss << deg.size() << " ";
for (const auto& d : deg) {
oss << d << " ";
}
oss << "|";
// 序列化 inverted_index
for (const auto& map : inverted_index) {
oss << map.size() << " ";
for (const auto& [key, value] : map) {
oss << key << " " << value << " ";
}
}
oss << "|";
// 序列化 neighbors_set
for (const auto& set : neighbors_set) {
oss << set.size() << " ";
for (const auto& elem : set) {
oss << elem << " ";
}
}
oss << "|";
return oss.str();
}
static TemporalNeighborBlock deserialize(const std::string& s) {
std::istringstream iss(s);
TemporalNeighborBlock tnb;
// 反序列化基本类型成员
iss >> tnb.with_eid >> tnb.weighted >> tnb.with_timestamp;
// 反序列化 vector<vector<T>> 类型成员
auto deserializeVecLong = [&iss](vector<vector<int64_t>>& vecVec) {
std::string segment;
std::getline(iss, segment, '|');
std::istringstream vec_iss(segment);
while (!vec_iss.eof()) {
size_t vec_size;
vec_iss >> vec_size;
if (vec_iss.eof()) break; // 防止多余的空白
vector<int64_t> vec(vec_size);
for (size_t i = 0; i < vec_size; ++i) {
vec_iss >> vec[i];
}
vecVec.push_back(vec);
}
};
auto deserializeVecFloat = [&iss](vector<vector<float>>& vecVec) {
std::string segment;
std::getline(iss, segment, '|');
std::istringstream vec_iss(segment);
while (!vec_iss.eof()) {
size_t vec_size;
vec_iss >> vec_size;
if (vec_iss.eof()) break; // 防止多余的空白
vector<float> vec(vec_size);
for (size_t i = 0; i < vec_size; ++i) {
vec_iss >> vec[i];
}
vecVec.push_back(vec);
}
};
deserializeVecLong(tnb.neighbors);
deserializeVecFloat(tnb.timestamp);
deserializeVecLong(tnb.eid);
deserializeVecFloat(tnb.edge_weight);
std::string segment;
// 反序列化 vector<int64_t> 类型成员
segment="";
std::getline(iss, segment, '|');
std::istringstream vec_iss(segment);
size_t vec_size;
vec_iss >> vec_size;
tnb.deg.resize(vec_size);
for (size_t i = 0; i < vec_size; ++i) {
vec_iss >> tnb.deg[i];
}
// 反序列化 inverted_index
segment="";
std::getline(iss, segment, '|');
std::istringstream map_iss(segment);
while (!map_iss.eof()) {
size_t map_size;
map_iss >> map_size;
if (map_iss.eof()) break;
phmap::parallel_flat_hash_map<NodeIDType, int64_t> map;
for (size_t i = 0; i < map_size; ++i) {
NodeIDType key;
int64_t value;
map_iss >> key >> value;
map[key] = value;
}
tnb.inverted_index.push_back(map);
}
// 反序列化 neighbors_set
std::getline(iss, segment, '|');
std::istringstream set_iss(segment);
while (!set_iss.eof()) {
size_t set_size;
set_iss >> set_size;
if (set_iss.eof()) break;
phmap::parallel_flat_hash_set<NodeIDType> set;
for (size_t i = 0; i < set_size; ++i) {
NodeIDType elem;
set_iss >> elem;
set.insert(elem);
}
tnb.neighbors_set.push_back(set);
}
return tnb;
}
};
class TemporalGraphBlock
{
public:
vector<NodeIDType> row;
vector<NodeIDType> col;
vector<EdgeIDType> eid;
vector<TimeStampType> delta_ts;
vector<int64_t> src_index;
vector<NodeIDType> sample_nodes;
vector<TimeStampType> sample_nodes_ts;
double sample_time = 0;
double tot_time = 0;
int64_t sample_edge_num = 0;
TemporalGraphBlock(){}
// TemporalGraphBlock(const TemporalGraphBlock &tgb);
TemporalGraphBlock(vector<NodeIDType> &_row, vector<NodeIDType> &_col,
vector<NodeIDType> &_sample_nodes):
row(_row), col(_col), sample_nodes(_sample_nodes){}
TemporalGraphBlock(vector<NodeIDType> &_row, vector<NodeIDType> &_col,
vector<NodeIDType> &_sample_nodes,
vector<TimeStampType> &_sample_nodes_ts):
row(_row), col(_col), sample_nodes(_sample_nodes),
sample_nodes_ts(_sample_nodes_ts){}
};
class T_TemporalGraphBlock
{
public:
th::Tensor row;
th::Tensor col;
th::Tensor eid;
th::Tensor delta_ts;
th::Tensor src_index;
th::Tensor sample_nodes;
th::Tensor sample_nodes_ts;
double sample_time = 0;
double tot_time = 0;
int64_t sample_edge_num = 0;
T_TemporalGraphBlock(){}
T_TemporalGraphBlock(th::Tensor &_row, th::Tensor &_col,
th::Tensor &_sample_nodes):
row(_row), col(_col), sample_nodes(_sample_nodes){}
};
// 辅助函数
template <typename T>
T* get_data_ptr(const th::Tensor& tensor) {
AT_ASSERTM(tensor.is_contiguous(), "Offset tensor must be contiguous");
AT_ASSERTM(tensor.dim() == 1, "Offset tensor must be one-dimensional");
return tensor.data_ptr<T>();
}
template <typename T>
torch::Tensor vecToTensor(const std::vector<T>& vec) {
// 确定数据类型
torch::ScalarType dtype;
if (std::is_same<T, int64_t>::value) {
dtype = torch::kInt64;
} else if (std::is_same<T, float>::value) {
dtype = torch::kFloat32;
} else {
throw std::runtime_error("Unsupported data type");
}
// 创建Tensor
torch::Tensor tensor = torch::from_blob(
const_cast<T*>(vec.data()), /* 数据指针 */
{static_cast<long>(vec.size())}, /* 尺寸 */
dtype /* 数据类型 */
);
return tensor;//.clone(); // 克隆Tensor以拷贝数据
}
TemporalNeighborBlock& get_neighbors(
string graph_name, th::Tensor row, th::Tensor col, int64_t num_nodes, int is_distinct, optional<th::Tensor> eid, optional<th::Tensor> edge_weight, optional<th::Tensor> time)
{ //row、col、time按time升序排列,由时间早的到时间晚的
auto src = get_data_ptr<NodeIDType>(row);
auto dst = get_data_ptr<NodeIDType>(col);
EdgeIDType* eid_ptr = eid ? get_data_ptr<EdgeIDType>(eid.value()) : nullptr;
WeightType* ew = edge_weight ? get_data_ptr<WeightType>(edge_weight.value()) : nullptr;
TimeStampType* t = time ? get_data_ptr<TimeStampType>(time.value()) : nullptr;
int64_t edge_num = row.size(0);
static phmap::parallel_flat_hash_map<string, TemporalNeighborBlock> tnb_map;
if(tnb_map.count(graph_name)==1)
return tnb_map[graph_name];
tnb_map[graph_name] = TemporalNeighborBlock();
TemporalNeighborBlock& tnb = tnb_map[graph_name];
double start_time = omp_get_wtime();
//初始化
tnb.neighbors.resize(num_nodes);
tnb.deg.resize(num_nodes, 0);
//初始化optional相关
tnb.with_eid = eid.has_value();
tnb.weighted = edge_weight.has_value();
tnb.with_timestamp = time.has_value();
if (tnb.with_eid) tnb.eid.resize(num_nodes);
if (tnb.weighted) {
tnb.edge_weight.resize(num_nodes);
tnb.inverted_index.resize(num_nodes);
}
if (tnb.with_timestamp) tnb.timestamp.resize(num_nodes);
//计算, 条件判断移出循环优化执行效率
for(int64_t i=0; i<edge_num; i++){
//计算节点邻居
tnb.neighbors[dst[i]].emplace_back(src[i]);
}
//如果有eid,插入
if(tnb.with_eid)
for(int64_t i=0; i<edge_num; i++){
tnb.eid[dst[i]].emplace_back(eid_ptr[i]);
}
//如果有权重信息,插入节点与邻居边的权重和反向索引
if(tnb.weighted)
for(int64_t i=0; i<edge_num; i++){
tnb.edge_weight[dst[i]].emplace_back(ew[i]);
if(tnb.with_eid) tnb.inverted_index[dst[i]][eid_ptr[i]]=tnb.neighbors[dst[i]].size()-1;
else tnb.inverted_index[dst[i]][src[i]]=tnb.neighbors[dst[i]].size()-1;
}
//如果有时序信息,插入节点与邻居边的时间
if(tnb.with_timestamp)
for(int64_t i=0; i<edge_num; i++){
tnb.timestamp[dst[i]].emplace_back(t[i]);
}
if(is_distinct){
for(int64_t i=0; i<num_nodes; i++){
//收集单边去重节点度
phmap::parallel_flat_hash_set<NodeIDType> temp_s;
temp_s.insert(tnb.neighbors[i].begin(), tnb.neighbors[i].end());
tnb.neighbors_set.emplace_back(temp_s);
tnb.deg[i] = tnb.neighbors_set[i].size();
}
}
else{
for(int64_t i=0; i<num_nodes; i++){
//收集单边节点度
tnb.deg[i] = tnb.neighbors[i].size();
}
}
double end_time = omp_get_wtime();
cout<<"get_neighbors consume: "<<end_time-start_time<<"s"<<endl;
return tnb;
}
void TemporalNeighborBlock::update_edge_weight(
th::Tensor row_or_eid, th::Tensor col, th::Tensor edge_weight){
AT_ASSERTM(this->weighted, "This Graph has no edge weight infomation");
auto dst = get_data_ptr<NodeIDType>(col);
WeightType* ew = get_data_ptr<WeightType>(edge_weight);
NodeIDType* src;
EdgeIDType* eid_ptr;
if(this->with_eid) eid_ptr = get_data_ptr<EdgeIDType>(row_or_eid);
else src = get_data_ptr<NodeIDType>(row_or_eid);
int64_t edge_num = col.size(0);
for(int64_t i=0; i<edge_num; i++){
//修改节点与邻居边的权重
AT_ASSERTM(this->inverted_index[dst[i]].count(src[i])==1, "Unexist Edge Index: "+to_string(src[i])+", "+to_string(dst[i]));
int index;
if(this->with_eid) index = this->inverted_index[dst[i]][eid_ptr[i]];
else index = this->inverted_index[dst[i]][src[i]];
this->edge_weight[dst[i]][index] = ew[i];
}
}
void TemporalNeighborBlock:: update_node_weight(th::Tensor nid, th::Tensor node_weight){
AT_ASSERTM(this->weighted, "This Graph has no edge weight infomation");
auto dst = get_data_ptr<NodeIDType>(nid);
WeightType* nw = get_data_ptr<WeightType>(node_weight);
int64_t node_num = nid.size(0);
for(int64_t i=0; i<node_num; i++){
//修改节点与邻居边的权重
AT_ASSERTM(dst[i]<this->deg.size(), "Unexist Node Index: "+to_string(dst[i]));
if(this->inverted_index[dst[i]].empty())
return;
for(auto index : this->inverted_index[dst[i]]){
this->edge_weight[dst[i]][index.second] = nw[i];
}
}
}
void TemporalNeighborBlock:: update_all_node_weight(th::Tensor node_weight){
AT_ASSERTM(this->weighted, "This Graph has no edge weight infomation");
WeightType* nw = get_data_ptr<WeightType>(node_weight);
int64_t node_num = node_weight.size(0);
AT_ASSERTM(node_num==this->neighbors.size(), "The tensor node_weight size is not suitable node number.");
for(int64_t i=0; i<node_num; i++){
//修改节点与邻居边的权重
for(int j=0; j<this->neighbors[i].size();j++){
this->edge_weight[i][j] = nw[this->neighbors[i][j]];
}
}
}
int64_t TemporalNeighborBlock::update_neighbors_with_time(
th::Tensor row, th::Tensor col, th::Tensor time,th::Tensor eid, int is_distinct, std::optional<th::Tensor> edge_weight){
//row、col、time按time升序排列,由时间早的到时间晚的
AT_ASSERTM(this->empty(), "Empty TemporalNeighborBlock, please use get_neighbors_with_time");
AT_ASSERTM(this->with_timestamp == true, "This Graph has no time infomation!");
auto src = get_data_ptr<NodeIDType>(row);
auto dst = get_data_ptr<NodeIDType>(col);
auto eid_ptr = get_data_ptr<EdgeIDType>(eid);
auto t = get_data_ptr<TimeStampType>(time);
WeightType* ew = edge_weight ? get_data_ptr<WeightType>(edge_weight.value()) : nullptr;
int64_t edge_num = row.size(0);
int64_t num_nodes = this->neighbors.size();
//处理optional的值
if(edge_weight.has_value()){
AT_ASSERTM(this->weighted == true, "This Graph has no edge weight");
}
if(this->weighted){
AT_ASSERTM(edge_weight.has_value(), "This Graph need edge weight");
}
// double start_time = omp_get_wtime();
if(is_distinct){
for(int64_t i=0; i<edge_num; i++){
//如果有新节点
if(dst[i]>=num_nodes){
num_nodes = dst[i]+1;
this->neighbors.resize(num_nodes);
this->deg.resize(num_nodes, 0);
this->eid.resize(num_nodes);
this->timestamp.resize(num_nodes);
//初始化optional相关
if (this->weighted) {
this->edge_weight.resize(num_nodes);
this->inverted_index.resize(num_nodes);
}
}
//更新节点邻居
this->neighbors[dst[i]].emplace_back(src[i]);
//插入eid
this->eid[dst[i]].emplace_back(eid_ptr[i]);
//插入节点与邻居边的时间
this->timestamp[dst[i]].emplace_back(t[i]);
//如果有权重信息,插入节点与邻居边的权重和反向索引
if(this->weighted){
this->edge_weight[dst[i]].emplace_back(ew[i]);
if(this->with_eid) this->inverted_index[dst[i]][eid_ptr[i]]=this->neighbors[dst[i]].size()-1;
else this->inverted_index[dst[i]][src[i]]=this->neighbors[dst[i]].size()-1;
}
this->neighbors_set[dst[i]].insert(src[i]);
this->deg[dst[i]]=this->neighbors_set[dst[i]].size();
}
}
else{
for(int64_t i=0; i<edge_num; i++){
//更新节点邻居
this->neighbors[dst[i]].emplace_back(src[i]);
//插入eid
this->eid[dst[i]].emplace_back(eid_ptr[i]);
//插入节点与邻居边的时间
this->timestamp[dst[i]].emplace_back(t[i]);
//如果有权重信息,插入节点与邻居边的权重和反向索引
if(this->weighted){
this->edge_weight[dst[i]].emplace_back(ew[i]);
this->inverted_index[dst[i]][src[i]]=this->neighbors[dst[i]].size()-1;
}
this->deg[dst[i]]=this->neighbors[dst[i]].size();
}
}
// double end_time = omp_get_wtime();
// cout<<"update_neighbors consume: "<<end_time-start_time<<"s"<<endl;
return num_nodes;
}
class ParallelSampler
{
public:
TemporalNeighborBlock& tnb;
NodeIDType num_nodes;
EdgeIDType num_edges;
int threads;
vector<int> fanouts;
// vector<NodeIDType> part_ptr;
// int pid;
int num_layers;
string policy;
std::vector<TemporalGraphBlock> ret;
ParallelSampler(TemporalNeighborBlock& _tnb, NodeIDType _num_nodes, EdgeIDType _num_edges, int _threads,
vector<int>& _fanouts, int _num_layers, string _policy) :
tnb(_tnb), num_nodes(_num_nodes), num_edges(_num_edges), threads(_threads),
fanouts(_fanouts), num_layers(_num_layers), policy(_policy)
{
omp_set_num_threads(_threads);
ret.clear();
ret.resize(_num_layers);
}
void reset()
{
ret.clear();
ret.resize(num_layers);
}
void neighbor_sample_from_nodes(th::Tensor nodes, optional<th::Tensor> root_ts);
void neighbor_sample_from_nodes_static(th::Tensor nodes);
void neighbor_sample_from_nodes_static_layer(th::Tensor nodes, int cur_layer);
void neighbor_sample_from_nodes_with_before(th::Tensor nodes, th::Tensor root_ts);
void neighbor_sample_from_nodes_with_before_layer(th::Tensor nodes, th::Tensor root_ts, int cur_layer);
};
void ParallelSampler :: neighbor_sample_from_nodes(th::Tensor nodes, optional<th::Tensor> root_ts)
{
omp_set_num_threads(threads);
if(policy == "weighted")
AT_ASSERTM(tnb.weighted, "Tnb has no weight infomation!");
else if(policy == "recent")
AT_ASSERTM(tnb.with_timestamp, "Tnb has no timestamp infomation!");
else if(policy == "uniform")
;
else{
throw runtime_error("The policy \"" + policy + "\" is not exit!");
}
if(tnb.with_timestamp){
AT_ASSERTM(tnb.with_timestamp, "Tnb has no timestamp infomation!");
AT_ASSERTM(root_ts.has_value(), "Parameter mismatch!");
neighbor_sample_from_nodes_with_before(nodes, root_ts.value());
}
else{
neighbor_sample_from_nodes_static(nodes);
}
}
void ParallelSampler :: neighbor_sample_from_nodes_static_layer(th::Tensor nodes, int cur_layer){
py::gil_scoped_release release;
double tot_start_time = omp_get_wtime();
TemporalGraphBlock tgb = TemporalGraphBlock();
int fanout = fanouts[cur_layer];
ret[cur_layer] = TemporalGraphBlock();
auto nodes_data = get_data_ptr<NodeIDType>(nodes);
vector<phmap::parallel_flat_hash_set<NodeIDType>> node_s_threads(threads);
phmap::parallel_flat_hash_set<NodeIDType> node_s;
vector<vector<NodeIDType>> eid_threads(threads);//row_threads(threads),col_threads(threads);
AT_ASSERTM(tnb.with_eid, "Tnb has no eid infomation! We need eid!");
// double start_time = omp_get_wtime();
int reserve_capacity = int(ceil(nodes.size(0) / threads)) * fanout;
#pragma omp parallel
{
int tid = omp_get_thread_num();
unsigned int loc_seed = tid;
// row_threads[tid].reserve(reserve_capacity);
// col_threads[tid].reserve(reserve_capacity);
eid_threads[tid].reserve(reserve_capacity);
#pragma omp for schedule(static, int(ceil(static_cast<float>((nodes.size(0)) / threads))))
for(int64_t i=0; i<nodes.size(0); i++){
// int tid = omp_get_thread_num();
NodeIDType node = nodes_data[i];
vector<NodeIDType> nei(tnb.neighbors[node]);
vector<EdgeIDType> edge;
edge = tnb.eid[node];
double s_start_time = omp_get_wtime();
if(tnb.deg[node]>fanout){
phmap::flat_hash_set<NodeIDType> temp_s;
default_random_engine e(8);//(time(0));
uniform_int_distribution<> u(0, tnb.deg[node]-1);
while(temp_s.size()!=fanout){
//循环选择fanout个邻居
NodeIDType indice;
if(policy == "weighted"){//考虑边权重信息
const vector<WeightType>& ew = tnb.edge_weight[node];
indice = sample_multinomial(ew, e);
}
else if(policy == "uniform"){//均匀采样
indice = u(e);
}
auto chosen_n_iter = nei.begin() + indice;
auto rst = temp_s.insert(*chosen_n_iter);
if(rst.second){ //不重复
auto chosen_e_iter = edge.begin() + indice;
eid_threads[tid].emplace_back(*chosen_e_iter);
node_s_threads[tid].insert(*chosen_n_iter);
}
}
// row_threads[tid].insert(row_threads[tid].end(),temp_s.begin(),temp_s.end());
// col_threads[tid].insert(col_threads[tid].end(), fanout, node);
}
else{
node_s_threads[tid].insert(nei.begin(), nei.end());
// row_threads[tid].insert(row_threads[tid].end(),nei.begin(),nei.end());
// col_threads[tid].insert(col_threads[tid].end(), tnb.deg[node], node);
eid_threads[tid].insert(eid_threads[tid].end(),edge.begin(), edge.end());
}
if(tid==0)
ret[0].sample_time += omp_get_wtime() - s_start_time;
}
}
// double end_time = omp_get_wtime();
// cout<<"neighbor_sample_from_nodes parallel part consume: "<<end_time-start_time<<"s"<<endl;
int size = 0;
vector<int> each_begin(threads);
for(int i = 0; i<threads; i++){
int s = eid_threads[i].size();
each_begin[i]=size;
size += s;
}
// ret[cur_layer].row.resize(size);
// ret[cur_layer].col.resize(size);
ret[cur_layer].eid.resize(size);
#pragma omp parallel for schedule(static, 1)
for(int i = 0; i<threads; i++){
// copy(row_threads[i].begin(), row_threads[i].end(), ret[cur_layer].row.begin()+each_begin[i]);
// copy(col_threads[i].begin(), col_threads[i].end(), ret[cur_layer].col.begin()+each_begin[i]);
copy(eid_threads[i].begin(), eid_threads[i].end(), ret[cur_layer].eid.begin()+each_begin[i]);
}
for(int i = 0; i<threads; i++)
node_s.insert(node_s_threads[i].begin(), node_s_threads[i].end());
ret[cur_layer].sample_nodes.assign(node_s.begin(), node_s.end());
ret[0].tot_time += omp_get_wtime() - tot_start_time;
ret[0].sample_edge_num += ret[cur_layer].eid.size();
py::gil_scoped_acquire acquire;
}
void ParallelSampler :: neighbor_sample_from_nodes_static(th::Tensor nodes){
for(int i=0;i<num_layers;i++){
if(i==0) neighbor_sample_from_nodes_static_layer(nodes, i);
else neighbor_sample_from_nodes_static_layer(vecToTensor<NodeIDType>(ret[i-1].sample_nodes), i);
}
}
void ParallelSampler :: neighbor_sample_from_nodes_with_before_layer(
th::Tensor nodes, th::Tensor root_ts, int cur_layer){
py::gil_scoped_release release;
double tot_start_time = omp_get_wtime();
ret[cur_layer] = TemporalGraphBlock();
auto nodes_data = get_data_ptr<NodeIDType>(nodes);
auto ts_data = get_data_ptr<TimeStampType>(root_ts);
int fanout = fanouts[cur_layer];
// HashT<pair<NodeIDType,TimeStampType> > node_s;
vector<TemporalGraphBlock> tgb_i(threads);
default_random_engine e(8);//(time(0));
// double start_time = omp_get_wtime();
int reserve_capacity = int(ceil(nodes.size(0) / threads)) * fanout;
#pragma omp parallel
{
int tid = omp_get_thread_num();
unsigned int loc_seed = tid;
tgb_i[tid].sample_nodes.reserve(reserve_capacity);
tgb_i[tid].sample_nodes_ts.reserve(reserve_capacity);
tgb_i[tid].delta_ts.reserve(reserve_capacity);
tgb_i[tid].eid.reserve(reserve_capacity);
tgb_i[tid].src_index.reserve(reserve_capacity);
#pragma omp for schedule(static, int(ceil(static_cast<float>((nodes.size(0)) / threads))))
for(int64_t i=0; i<nodes.size(0); i++){
// int tid = omp_get_thread_num();
NodeIDType node = nodes_data[i];
TimeStampType rtts = ts_data[i];
int end_index = lower_bound(tnb.timestamp[node].begin(), tnb.timestamp[node].end(), rtts)-tnb.timestamp[node].begin();
// cout<<node<<" "<<end_index<<" "<<tnb.deg[node]<<endl;
double s_start_time = omp_get_wtime();
if ((policy == "recent") || (end_index <= fanout)){
int start_index = max(0, end_index-fanout);
tgb_i[tid].src_index.insert(tgb_i[tid].src_index.end(), end_index-start_index, i);
tgb_i[tid].sample_nodes.insert(tgb_i[tid].sample_nodes.end(), tnb.neighbors[node].begin()+start_index, tnb.neighbors[node].begin()+end_index);
tgb_i[tid].sample_nodes_ts.insert(tgb_i[tid].sample_nodes_ts.end(), tnb.timestamp[node].begin()+start_index, tnb.timestamp[node].begin()+end_index);
tgb_i[tid].eid.insert(tgb_i[tid].eid.end(), tnb.eid[node].begin()+start_index, tnb.eid[node].begin()+end_index);
for(int cid = start_index; cid < end_index;cid++){
tgb_i[tid].delta_ts.emplace_back(rtts-tnb.timestamp[node][cid]);
}
}
else{
//可选邻居边大于扇出的话需要随机选择fanout个邻居
tgb_i[tid].src_index.insert(tgb_i[tid].src_index.end(), fanout, i);
uniform_int_distribution<> u(0, end_index-1);
//cout<<end_index<<endl;
// cout<<"start:"<<start_index<<" end:"<<end_index<<endl;
for(int i=0; i<fanout;i++){
int cid;
if(policy == "uniform")
cid = u(e);
// cid = rand_r(&loc_seed) % (end_index);
else if(policy == "weighted"){
const vector<WeightType>& ew = tnb.edge_weight[node];
cid = sample_multinomial(ew, e);
}
tgb_i[tid].sample_nodes.emplace_back(tnb.neighbors[node][cid]);
tgb_i[tid].sample_nodes_ts.emplace_back(tnb.timestamp[node][cid]);
tgb_i[tid].delta_ts.emplace_back(rtts-tnb.timestamp[node][cid]);
tgb_i[tid].eid.emplace_back(tnb.eid[node][cid]);
}
}
if(tid==0)
ret[0].sample_time += omp_get_wtime() - s_start_time;
}
}
// double end_time = omp_get_wtime();
// cout<<"neighbor_sample_from_nodes parallel part consume: "<<end_time-start_time<<"s"<<endl;
// start_time = omp_get_wtime();
int size = 0;
vector<int> each_begin(threads);
for(int i = 0; i<threads; i++){
int s = tgb_i[i].eid.size();
each_begin[i]=size;
size += s;
}
ret[cur_layer].eid.resize(size);
ret[cur_layer].src_index.resize(size);
ret[cur_layer].delta_ts.resize(size);
ret[cur_layer].sample_nodes.resize(size);
ret[cur_layer].sample_nodes_ts.resize(size);
#pragma omp parallel for schedule(static, 1)
for(int i = 0; i<threads; i++){
copy(tgb_i[i].eid.begin(), tgb_i[i].eid.end(), ret[cur_layer].eid.begin()+each_begin[i]);
copy(tgb_i[i].src_index.begin(), tgb_i[i].src_index.end(), ret[cur_layer].src_index.begin()+each_begin[i]);
copy(tgb_i[i].delta_ts.begin(), tgb_i[i].delta_ts.end(), ret[cur_layer].delta_ts.begin()+each_begin[i]);
copy(tgb_i[i].sample_nodes.begin(), tgb_i[i].sample_nodes.end(), ret[cur_layer].sample_nodes.begin()+each_begin[i]);
copy(tgb_i[i].sample_nodes_ts.begin(), tgb_i[i].sample_nodes_ts.end(), ret[cur_layer].sample_nodes_ts.begin()+each_begin[i]);
}
// end_time = omp_get_wtime();
// cout<<"end union consume: "<<end_time-start_time<<"s"<<endl;
ret[0].tot_time += omp_get_wtime() - tot_start_time;
ret[0].sample_edge_num += ret[cur_layer].eid.size();
py::gil_scoped_acquire acquire;
}
void ParallelSampler :: neighbor_sample_from_nodes_with_before(th::Tensor nodes, th::Tensor root_ts){
for(int i=0;i<num_layers;i++){
if(i==0) neighbor_sample_from_nodes_with_before_layer(nodes, root_ts, i);
else neighbor_sample_from_nodes_with_before_layer(vecToTensor<NodeIDType>(ret[i-1].sample_nodes),
vecToTensor<TimeStampType>(ret[i-1].sample_nodes_ts), i);
}
}
/*-------------------------------------------------------------------------------------**
**------------Utils--------------------------------------------------------------------**
**-------------------------------------------------------------------------------------*/
th::Tensor heads_unique(th::Tensor array, th::Tensor heads, int threads){
auto array_ptr = array.data_ptr<NodeIDType>();
phmap::parallel_flat_hash_set<NodeIDType> s(array_ptr, array_ptr+array.numel());
if(heads.numel()==0) return th::tensor(vector<NodeIDType>(s.begin(), s.end()));
AT_ASSERTM(heads.is_contiguous(), "Offset tensor must be contiguous");
AT_ASSERTM(heads.dim() == 1, "0ffset tensor must be one-dimensional");
auto heads_ptr = heads.data_ptr<NodeIDType>();
#pragma omp parallel for num_threads(threads)
for(int64_t i=0; i<heads.size(0); i++){
if(s.count(heads_ptr[i])==1){
#pragma omp critical(erase)
s.erase(heads_ptr[i]);
}
}
vector<NodeIDType> ret;
ret.reserve(s.size()+heads.numel());
ret.assign(heads_ptr, heads_ptr+heads.numel());
ret.insert(ret.end(), s.begin(), s.end());
// cout<<"s: "<<s.size()<<" array: "<<array.size()<<endl;
return th::tensor(ret);
}
int nodeIdToPartId(NodeIDType nid, const vector<NodeIDType>& part_ptr){
int partitionId = -1;
for(int i=0;i<part_ptr.size()-1;i++){
if(nid>=part_ptr[i]&&nid<part_ptr[i+1]){
partitionId = i;
break;
}
}
if(partitionId<0) throw "nid 不存在对应的分区";
return partitionId;
}
//0:inner; 1:outer
int nodeIdToInOut(NodeIDType nid, int pid, const vector<NodeIDType>& part_ptr){
if(nid>=part_ptr[pid]&&nid<part_ptr[pid+1]){
return 0;
}
return 1;
}
vector<th::Tensor> divide_nodes_to_part(
th::Tensor nodes, const vector<NodeIDType>& part_ptr, int threads){
double start_time = omp_get_wtime();
AT_ASSERTM(nodes.is_contiguous(), "Offset tensor must be contiguous");
AT_ASSERTM(nodes.dim() == 1, "0ffset tensor must be one-dimensional");
auto nodes_id = nodes.data_ptr<NodeIDType>();
vector<vector<vector<NodeIDType>>> node_part_threads;
vector<th::Tensor> result(part_ptr.size()-1);
//初始化点的分区,每个分区按线程划分避免冲突
for(int i = 0; i<threads; i++){
vector<vector<NodeIDType>> node_parts;
for(int j=0;j<part_ptr.size()-1;j++){
node_parts.push_back(vector<NodeIDType>());
}
node_part_threads.push_back(node_parts);
}
#pragma omp parallel for num_threads(threads) default(shared)
for(int64_t i=0; i<nodes.size(0); i++){
int tid = omp_get_thread_num();
int pid = nodeIdToPartId(nodes_id[i], part_ptr);
node_part_threads[tid][pid].emplace_back(nodes_id[i]);
}
#pragma omp parallel for num_threads(part_ptr.size()-1) default(shared)
for(int i = 0; i<part_ptr.size()-1; i++){
vector<NodeIDType> temp;
for(int j=0;j<threads;j++){
temp.insert(temp.end(), node_part_threads[j][i].begin(), node_part_threads[j][i].end());
}
result[i]=th::tensor(temp);
}
double end_time = omp_get_wtime();
// cout<<"end divide consume: "<<end_time-start_time<<"s"<<endl;
return result;
}
NodeIDType sample_multinomial(const vector<WeightType>& weights, default_random_engine& e){
NodeIDType sample_indice;
vector<WeightType> cumulative_weights;
partial_sum(weights.begin(), weights.end(), back_inserter(cumulative_weights));
AT_ASSERTM(cumulative_weights.back() > 0, "Edge weight sum should be greater than 0.");
uniform_real_distribution<WeightType> distribution(0.0, cumulative_weights.back());
WeightType random_value = distribution(e);
auto it = lower_bound(cumulative_weights.begin(), cumulative_weights.end(), random_value);
sample_indice = distance(cumulative_weights.begin(), it);
return sample_indice;
}
/*------------Python Bind--------------------------------------------------------------*/
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
{
m
.def("get_neighbors",
&get_neighbors,
py::return_value_policy::reference)
.def("heads_unique",
&heads_unique,
py::return_value_policy::reference)
.def("divide_nodes_to_part",
&divide_nodes_to_part,
py::return_value_policy::reference);
py::class_<TemporalGraphBlock>(m, "TemporalGraphBlock")
.def(py::init<vector<NodeIDType> &, vector<NodeIDType> &,
vector<NodeIDType> &>())
.def("row", [](const TemporalGraphBlock &tgb) { return vecToTensor<NodeIDType>(tgb.row); })
.def("col", [](const TemporalGraphBlock &tgb) { return vecToTensor<NodeIDType>(tgb.col); })
.def("eid", [](const TemporalGraphBlock &tgb) { return vecToTensor<EdgeIDType>(tgb.eid); })
.def("delta_ts", [](const TemporalGraphBlock &tgb) { return vecToTensor<TimeStampType>(tgb.delta_ts); })
.def("src_index", [](const TemporalGraphBlock &tgb) { return vecToTensor<EdgeIDType>(tgb.src_index); })
.def("sample_nodes", [](const TemporalGraphBlock &tgb) { return vecToTensor<NodeIDType>(tgb.sample_nodes); })
.def("sample_nodes_ts", [](const TemporalGraphBlock &tgb) { return vecToTensor<TimeStampType>(tgb.sample_nodes_ts); })
.def_readonly("sample_time", &TemporalGraphBlock::sample_time, py::return_value_policy::reference)
.def_readonly("tot_time", &TemporalGraphBlock::tot_time, py::return_value_policy::reference)
.def_readonly("sample_edge_num", &TemporalGraphBlock::sample_edge_num, py::return_value_policy::reference);
py::class_<T_TemporalGraphBlock>(m, "T_TemporalGraphBlock")
.def(py::init<th::Tensor &, th::Tensor &,
th::Tensor &>())
.def_readonly("row", &T_TemporalGraphBlock::row, py::return_value_policy::reference)
.def_readonly("col", &T_TemporalGraphBlock::col, py::return_value_policy::reference)
.def_readonly("eid", &T_TemporalGraphBlock::eid, py::return_value_policy::reference)
.def_readonly("delta_ts", &T_TemporalGraphBlock::delta_ts, py::return_value_policy::reference)
.def_readonly("src_index", &T_TemporalGraphBlock::src_index, py::return_value_policy::reference)
.def_readonly("sample_nodes", &T_TemporalGraphBlock::sample_nodes, py::return_value_policy::reference)
.def_readonly("sample_nodes_ts", &T_TemporalGraphBlock::sample_nodes_ts, py::return_value_policy::reference)
.def_readonly("sample_time", &T_TemporalGraphBlock::sample_time, py::return_value_policy::reference)
.def_readonly("tot_time", &T_TemporalGraphBlock::tot_time, py::return_value_policy::reference)
.def_readonly("sample_edge_num", &T_TemporalGraphBlock::sample_edge_num, py::return_value_policy::reference);
py::class_<TemporalNeighborBlock>(m, "TemporalNeighborBlock")
.def(py::init<vector<vector<NodeIDType>>&,
vector<int64_t> &>())
.def(py::pickle(
[](const TemporalNeighborBlock& tnb) { return tnb.serialize(); },
[](const std::string& s) { return TemporalNeighborBlock::deserialize(s); }
))
.def("update_neighbors_with_time",
&TemporalNeighborBlock::update_neighbors_with_time)
.def("update_edge_weight",
&TemporalNeighborBlock::update_edge_weight)
.def("update_node_weight",
&TemporalNeighborBlock::update_node_weight)
.def("update_all_node_weight",
&TemporalNeighborBlock::update_all_node_weight)
// .def("get_node_neighbor",&TemporalNeighborBlock::get_node_neighbor)
// .def("get_node_deg", &TemporalNeighborBlock::get_node_deg)
.def_readonly("neighbors", &TemporalNeighborBlock::neighbors, py::return_value_policy::reference)
.def_readonly("timestamp", &TemporalNeighborBlock::timestamp, py::return_value_policy::reference)
.def_readonly("edge_weight", &TemporalNeighborBlock::edge_weight, py::return_value_policy::reference)
.def_readonly("eid", &TemporalNeighborBlock::eid, py::return_value_policy::reference)
.def_readonly("deg", &TemporalNeighborBlock::deg, py::return_value_policy::reference)
.def_readonly("with_eid", &TemporalNeighborBlock::with_eid, py::return_value_policy::reference)
.def_readonly("with_timestamp", &TemporalNeighborBlock::with_timestamp, py::return_value_policy::reference)
.def_readonly("weighted", &TemporalNeighborBlock::weighted, py::return_value_policy::reference);
py::class_<ParallelSampler>(m, "ParallelSampler")
.def(py::init<TemporalNeighborBlock &, NodeIDType, EdgeIDType, int,
vector<int>&, int, string>())
.def_readonly("ret", &ParallelSampler::ret, py::return_value_policy::reference)
.def("neighbor_sample_from_nodes", &ParallelSampler::neighbor_sample_from_nodes)
.def("reset", &ParallelSampler::reset)
.def("get_ret", [](const ParallelSampler &ps) { return ps.ret; });
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from conans import ConanFile, tools
import os
class SparseppConan(ConanFile):
name = "parallel_hashmap"
version = "1.27"
description = "A header-only, very fast and memory-friendly hash map"
# Indicates License type of the packaged library
license = "https://github.com/greg7mdp/parallel-hashmap/blob/master/LICENSE"
# Packages the license for the conanfile.py
exports = ["LICENSE"]
# Custom attributes for Bincrafters recipe conventions
source_subfolder = "source_subfolder"
def source(self):
source_url = "https://github.com/greg7mdp/parallel-hashmap"
tools.get("{0}/archive/{1}.tar.gz".format(source_url, self.version))
extracted_dir = self.name + "-" + self.version
#Rename to "source_folder" is a convention to simplify later steps
os.rename(extracted_dir, self.source_subfolder)
def package(self):
include_folder = os.path.join(self.source_subfolder, "parallel_hashmap")
self.copy(pattern="LICENSE")
self.copy(pattern="*", dst="include/parallel_hashmap", src=include_folder)
def package_id(self):
self.info.header_only()
#if !defined(spp_memory_h_guard)
#define spp_memory_h_guard
#include <cstdint>
#include <cstring>
#include <cstdlib>
#if defined(_WIN32) || defined( __CYGWIN__)
#define SPP_WIN
#endif
#ifdef SPP_WIN
#include <windows.h>
#include <Psapi.h>
#undef min
#undef max
#elif defined(__linux__)
#include <sys/types.h>
#include <sys/sysinfo.h>
#elif defined(__FreeBSD__)
#include <paths.h>
#include <fcntl.h>
#include <kvm.h>
#include <unistd.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#endif
namespace spp
{
uint64_t GetSystemMemory();
uint64_t GetTotalMemoryUsed();
uint64_t GetProcessMemoryUsed();
uint64_t GetPhysicalMemory();
uint64_t GetSystemMemory()
{
#ifdef SPP_WIN
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(&memInfo);
return static_cast<uint64_t>(memInfo.ullTotalPageFile);
#elif defined(__linux__)
struct sysinfo memInfo;
sysinfo (&memInfo);
auto totalVirtualMem = memInfo.totalram;
totalVirtualMem += memInfo.totalswap;
totalVirtualMem *= memInfo.mem_unit;
return static_cast<uint64_t>(totalVirtualMem);
#elif defined(__FreeBSD__)
kvm_t *kd;
u_int pageCnt;
size_t pageCntLen = sizeof(pageCnt);
u_int pageSize;
struct kvm_swap kswap;
uint64_t totalVirtualMem;
pageSize = static_cast<u_int>(getpagesize());
sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0);
totalVirtualMem = pageCnt * pageSize;
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
kvm_getswapinfo(kd, &kswap, 1, 0);
kvm_close(kd);
totalVirtualMem += kswap.ksw_total * pageSize;
return totalVirtualMem;
#else
return 0;
#endif
}
uint64_t GetTotalMemoryUsed()
{
#ifdef SPP_WIN
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(&memInfo);
return static_cast<uint64_t>(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile);
#elif defined(__linux__)
struct sysinfo memInfo;
sysinfo(&memInfo);
auto virtualMemUsed = memInfo.totalram - memInfo.freeram;
virtualMemUsed += memInfo.totalswap - memInfo.freeswap;
virtualMemUsed *= memInfo.mem_unit;
return static_cast<uint64_t>(virtualMemUsed);
#elif defined(__FreeBSD__)
kvm_t *kd;
u_int pageSize;
u_int pageCnt, freeCnt;
size_t pageCntLen = sizeof(pageCnt);
size_t freeCntLen = sizeof(freeCnt);
struct kvm_swap kswap;
uint64_t virtualMemUsed;
pageSize = static_cast<u_int>(getpagesize());
sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0);
sysctlbyname("vm.stats.vm.v_free_count", &freeCnt, &freeCntLen, NULL, 0);
virtualMemUsed = (pageCnt - freeCnt) * pageSize;
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
kvm_getswapinfo(kd, &kswap, 1, 0);
kvm_close(kd);
virtualMemUsed += kswap.ksw_used * pageSize;
return virtualMemUsed;
#else
return 0;
#endif
}
uint64_t GetProcessMemoryUsed()
{
#ifdef SPP_WIN
PROCESS_MEMORY_COUNTERS_EX pmc;
GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&pmc), sizeof(pmc));
return static_cast<uint64_t>(pmc.PrivateUsage);
#elif defined(__linux__)
auto parseLine =
[](char* line)->int
{
auto i = strlen(line);
while(*line < '0' || *line > '9')
{
line++;
}
line[i-3] = '\0';
i = atoi(line);
return i;
};
auto file = fopen("/proc/self/status", "r");
auto result = -1;
char line[128];
while(fgets(line, 128, file) != nullptr)
{
if(strncmp(line, "VmSize:", 7) == 0)
{
result = parseLine(line);
break;
}
}
fclose(file);
return static_cast<uint64_t>(result) * 1024;
#elif defined(__FreeBSD__)
struct kinfo_proc info;
size_t infoLen = sizeof(info);
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &infoLen, NULL, 0);
return static_cast<uint64_t>(info.ki_rssize * getpagesize());
#else
return 0;
#endif
}
uint64_t GetPhysicalMemory()
{
#ifdef SPP_WIN
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(&memInfo);
return static_cast<uint64_t>(memInfo.ullTotalPhys);
#elif defined(__linux__)
struct sysinfo memInfo;
sysinfo(&memInfo);
auto totalPhysMem = memInfo.totalram;
totalPhysMem *= memInfo.mem_unit;
return static_cast<uint64_t>(totalPhysMem);
#elif defined(__FreeBSD__)
u_long physMem;
size_t physMemLen = sizeof(physMem);
int mib[] = { CTL_HW, HW_PHYSMEM };
sysctl(mib, sizeof(mib) / sizeof(*mib), &physMem, &physMemLen, NULL, 0);
return physMem;
#else
return 0;
#endif
}
}
#endif // spp_memory_h_guard
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
#if !defined(phmap_bits_h_guard_)
#define phmap_bits_h_guard_
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://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.
//
// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
// with modifications.
//
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://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.
// ---------------------------------------------------------------------------
// The following guarantees declaration of the byte swap functions
#ifdef _MSC_VER
#include <stdlib.h> // NOLINT(build/include)
#elif defined(__APPLE__)
// Mac OS X / Darwin features
#include <libkern/OSByteOrder.h>
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#elif defined(__GLIBC__)
#include <byteswap.h> // IWYU pragma: export
#endif
#include <string.h>
#include <cstdint>
#include "phmap_config.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4514) // unreferenced inline function has been removed
#endif
// -----------------------------------------------------------------------------
// unaligned APIs
// -----------------------------------------------------------------------------
// Portable handling of unaligned loads, stores, and copies.
// On some platforms, like ARM, the copy functions can be more efficient
// then a load and a store.
// -----------------------------------------------------------------------------
#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||\
defined(MEMORY_SANITIZER)
#include <stdint.h>
extern "C" {
uint16_t __sanitizer_unaligned_load16(const void *p);
uint32_t __sanitizer_unaligned_load32(const void *p);
uint64_t __sanitizer_unaligned_load64(const void *p);
void __sanitizer_unaligned_store16(void *p, uint16_t v);
void __sanitizer_unaligned_store32(void *p, uint32_t v);
void __sanitizer_unaligned_store64(void *p, uint64_t v);
} // extern "C"
namespace phmap {
namespace bits {
inline uint16_t UnalignedLoad16(const void *p) {
return __sanitizer_unaligned_load16(p);
}
inline uint32_t UnalignedLoad32(const void *p) {
return __sanitizer_unaligned_load32(p);
}
inline uint64_t UnalignedLoad64(const void *p) {
return __sanitizer_unaligned_load64(p);
}
inline void UnalignedStore16(void *p, uint16_t v) {
__sanitizer_unaligned_store16(p, v);
}
inline void UnalignedStore32(void *p, uint32_t v) {
__sanitizer_unaligned_store32(p, v);
}
inline void UnalignedStore64(void *p, uint64_t v) {
__sanitizer_unaligned_store64(p, v);
}
} // namespace bits
} // namespace phmap
#define PHMAP_INTERNAL_UNALIGNED_LOAD16(_p) (phmap::bits::UnalignedLoad16(_p))
#define PHMAP_INTERNAL_UNALIGNED_LOAD32(_p) (phmap::bits::UnalignedLoad32(_p))
#define PHMAP_INTERNAL_UNALIGNED_LOAD64(_p) (phmap::bits::UnalignedLoad64(_p))
#define PHMAP_INTERNAL_UNALIGNED_STORE16(_p, _val) (phmap::bits::UnalignedStore16(_p, _val))
#define PHMAP_INTERNAL_UNALIGNED_STORE32(_p, _val) (phmap::bits::UnalignedStore32(_p, _val))
#define PHMAP_INTERNAL_UNALIGNED_STORE64(_p, _val) (phmap::bits::UnalignedStore64(_p, _val))
#else
namespace phmap {
namespace bits {
inline uint16_t UnalignedLoad16(const void *p) {
uint16_t t;
memcpy(&t, p, sizeof t);
return t;
}
inline uint32_t UnalignedLoad32(const void *p) {
uint32_t t;
memcpy(&t, p, sizeof t);
return t;
}
inline uint64_t UnalignedLoad64(const void *p) {
uint64_t t;
memcpy(&t, p, sizeof t);
return t;
}
inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
} // namespace bits
} // namespace phmap
#define PHMAP_INTERNAL_UNALIGNED_LOAD16(_p) (phmap::bits::UnalignedLoad16(_p))
#define PHMAP_INTERNAL_UNALIGNED_LOAD32(_p) (phmap::bits::UnalignedLoad32(_p))
#define PHMAP_INTERNAL_UNALIGNED_LOAD64(_p) (phmap::bits::UnalignedLoad64(_p))
#define PHMAP_INTERNAL_UNALIGNED_STORE16(_p, _val) (phmap::bits::UnalignedStore16(_p, _val))
#define PHMAP_INTERNAL_UNALIGNED_STORE32(_p, _val) (phmap::bits::UnalignedStore32(_p, _val))
#define PHMAP_INTERNAL_UNALIGNED_STORE64(_p, _val) (phmap::bits::UnalignedStore64(_p, _val))
#endif
// -----------------------------------------------------------------------------
// File: optimization.h
// -----------------------------------------------------------------------------
#if defined(__pnacl__)
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
#elif defined(__clang__)
// Clang will not tail call given inline volatile assembly.
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
#elif defined(__GNUC__)
// GCC will not tail call given inline volatile assembly.
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
#elif defined(_MSC_VER)
#include <intrin.h>
// The __nop() intrinsic blocks the optimisation.
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() __nop()
#else
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif
#ifdef PHMAP_HAVE_INTRINSIC_INT128
__extension__ typedef unsigned __int128 phmap_uint128;
inline uint64_t umul128(uint64_t a, uint64_t b, uint64_t* high)
{
auto result = static_cast<phmap_uint128>(a) * static_cast<phmap_uint128>(b);
*high = static_cast<uint64_t>(result >> 64);
return static_cast<uint64_t>(result);
}
#define PHMAP_HAS_UMUL128 1
#elif (defined(_MSC_VER))
#if defined(_M_X64)
#pragma intrinsic(_umul128)
inline uint64_t umul128(uint64_t a, uint64_t b, uint64_t* high)
{
return _umul128(a, b, high);
}
#define PHMAP_HAS_UMUL128 1
#endif
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#if defined(__GNUC__)
// Cache line alignment
#if defined(__i386__) || defined(__x86_64__)
#define PHMAP_CACHELINE_SIZE 64
#elif defined(__powerpc64__)
#define PHMAP_CACHELINE_SIZE 128
#elif defined(__aarch64__)
// We would need to read special register ctr_el0 to find out L1 dcache size.
// This value is a good estimate based on a real aarch64 machine.
#define PHMAP_CACHELINE_SIZE 64
#elif defined(__arm__)
// Cache line sizes for ARM: These values are not strictly correct since
// cache line sizes depend on implementations, not architectures. There
// are even implementations with cache line sizes configurable at boot
// time.
#if defined(__ARM_ARCH_5T__)
#define PHMAP_CACHELINE_SIZE 32
#elif defined(__ARM_ARCH_7A__)
#define PHMAP_CACHELINE_SIZE 64
#endif
#endif
#ifndef PHMAP_CACHELINE_SIZE
// A reasonable default guess. Note that overestimates tend to waste more
// space, while underestimates tend to waste more time.
#define PHMAP_CACHELINE_SIZE 64
#endif
#define PHMAP_CACHELINE_ALIGNED __attribute__((aligned(PHMAP_CACHELINE_SIZE)))
#elif defined(_MSC_VER)
#define PHMAP_CACHELINE_SIZE 64
#define PHMAP_CACHELINE_ALIGNED __declspec(align(PHMAP_CACHELINE_SIZE))
#else
#define PHMAP_CACHELINE_SIZE 64
#define PHMAP_CACHELINE_ALIGNED
#endif
#if PHMAP_HAVE_BUILTIN(__builtin_expect) || \
(defined(__GNUC__) && !defined(__clang__))
#define PHMAP_PREDICT_FALSE(x) (__builtin_expect(x, 0))
#define PHMAP_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
#else
#define PHMAP_PREDICT_FALSE(x) (x)
#define PHMAP_PREDICT_TRUE(x) (x)
#endif
// -----------------------------------------------------------------------------
// File: bits.h
// -----------------------------------------------------------------------------
#if defined(_MSC_VER)
// We can achieve something similar to attribute((always_inline)) with MSVC by
// using the __forceinline keyword, however this is not perfect. MSVC is
// much less aggressive about inlining, and even with the __forceinline keyword.
#define PHMAP_BASE_INTERNAL_FORCEINLINE __forceinline
#else
// Use default attribute inline.
#define PHMAP_BASE_INTERNAL_FORCEINLINE inline PHMAP_ATTRIBUTE_ALWAYS_INLINE
#endif
namespace phmap {
namespace base_internal {
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
int zeroes = 60;
if (n >> 32) zeroes -= 32, n >>= 32;
if (n >> 16) zeroes -= 16, n >>= 16;
if (n >> 8) zeroes -= 8, n >>= 8;
if (n >> 4) zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
#if defined(_MSC_VER) && defined(_M_X64)
// MSVC does not have __buitin_clzll. Use _BitScanReverse64.
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse64(&result, n)) {
return (int)(63 - result);
}
return 64;
#elif defined(_MSC_VER) && !defined(__clang__)
// MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
unsigned long result = 0; // NOLINT(runtime/int)
if ((n >> 32) && _BitScanReverse(&result, (unsigned long)(n >> 32))) {
return 31 - result;
}
if (_BitScanReverse(&result, (unsigned long)n)) {
return 63 - result;
}
return 64;
#elif defined(__GNUC__) || defined(__clang__)
// Use __builtin_clzll, which uses the following instructions:
// x86: bsr
// ARM64: clz
// PPC: cntlzd
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
"__builtin_clzll does not take 64-bit arg");
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
if (n == 0) {
return 64;
}
return __builtin_clzll(n);
#else
return CountLeadingZeros64Slow(n);
#endif
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
int zeroes = 28;
if (n >> 16) zeroes -= 16, n >>= 16;
if (n >> 8) zeroes -= 8, n >>= 8;
if (n >> 4) zeroes -= 4, n >>= 4;
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
if (_BitScanReverse(&result, n)) {
return (int)(31 - result);
}
return 32;
#elif defined(__GNUC__) || defined(__clang__)
// Use __builtin_clz, which uses the following instructions:
// x86: bsr
// ARM64: clz
// PPC: cntlzd
static_assert(sizeof(int) == sizeof(n),
"__builtin_clz does not take 32-bit arg");
// Handle 0 as a special case because __builtin_clz(0) is undefined.
if (n == 0) {
return 32;
}
return __builtin_clz(n);
#else
return CountLeadingZeros32Slow(n);
#endif
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
int c = 63;
n &= ~n + 1;
if (n & 0x00000000FFFFFFFF) c -= 32;
if (n & 0x0000FFFF0000FFFF) c -= 16;
if (n & 0x00FF00FF00FF00FF) c -= 8;
if (n & 0x0F0F0F0F0F0F0F0F) c -= 4;
if (n & 0x3333333333333333) c -= 2;
if (n & 0x5555555555555555) c -= 1;
return c;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64)
unsigned long result = 0; // NOLINT(runtime/int)
_BitScanForward64(&result, n);
return (int)result;
#elif defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
if (static_cast<uint32_t>(n) == 0) {
_BitScanForward(&result, (unsigned long)(n >> 32));
return result + 32;
}
_BitScanForward(&result, (unsigned long)n);
return result;
#elif defined(__GNUC__) || defined(__clang__)
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
"__builtin_ctzll does not take 64-bit arg");
return __builtin_ctzll(n);
#else
return CountTrailingZerosNonZero64Slow(n);
#endif
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
int c = 31;
n &= ~n + 1;
if (n & 0x0000FFFF) c -= 16;
if (n & 0x00FF00FF) c -= 8;
if (n & 0x0F0F0F0F) c -= 4;
if (n & 0x33333333) c -= 2;
if (n & 0x55555555) c -= 1;
return c;
}
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
#if defined(_MSC_VER) && !defined(__clang__)
unsigned long result = 0; // NOLINT(runtime/int)
_BitScanForward(&result, n);
return (int)result;
#elif defined(__GNUC__) || defined(__clang__)
static_assert(sizeof(int) == sizeof(n),
"__builtin_ctz does not take 32-bit arg");
return __builtin_ctz(n);
#else
return CountTrailingZerosNonZero32Slow(n);
#endif
}
#undef PHMAP_BASE_INTERNAL_FORCEINLINE
} // namespace base_internal
} // namespace phmap
// -----------------------------------------------------------------------------
// File: endian.h
// -----------------------------------------------------------------------------
namespace phmap {
// Use compiler byte-swapping intrinsics if they are available. 32-bit
// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0.
// For simplicity, we enable them all only for GCC 4.8.0 or later.
#if defined(__clang__) || \
(defined(__GNUC__) && \
((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
inline uint64_t gbswap_64(uint64_t host_int) {
return __builtin_bswap64(host_int);
}
inline uint32_t gbswap_32(uint32_t host_int) {
return __builtin_bswap32(host_int);
}
inline uint16_t gbswap_16(uint16_t host_int) {
return __builtin_bswap16(host_int);
}
#elif defined(_MSC_VER)
inline uint64_t gbswap_64(uint64_t host_int) {
return _byteswap_uint64(host_int);
}
inline uint32_t gbswap_32(uint32_t host_int) {
return _byteswap_ulong(host_int);
}
inline uint16_t gbswap_16(uint16_t host_int) {
return _byteswap_ushort(host_int);
}
#elif defined(__APPLE__)
inline uint64_t gbswap_64(uint64_t host_int) { return OSSwapInt16(host_int); }
inline uint32_t gbswap_32(uint32_t host_int) { return OSSwapInt32(host_int); }
inline uint16_t gbswap_16(uint16_t host_int) { return OSSwapInt64(host_int); }
#else
inline uint64_t gbswap_64(uint64_t host_int) {
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__)
// Adapted from /usr/include/byteswap.h. Not available on Mac.
if (__builtin_constant_p(host_int)) {
return __bswap_constant_64(host_int);
} else {
uint64_t result;
__asm__("bswap %0" : "=r"(result) : "0"(host_int));
return result;
}
#elif defined(__GLIBC__)
return bswap_64(host_int);
#else
return (((host_int & uint64_t{0xFF}) << 56) |
((host_int & uint64_t{0xFF00}) << 40) |
((host_int & uint64_t{0xFF0000}) << 24) |
((host_int & uint64_t{0xFF000000}) << 8) |
((host_int & uint64_t{0xFF00000000}) >> 8) |
((host_int & uint64_t{0xFF0000000000}) >> 24) |
((host_int & uint64_t{0xFF000000000000}) >> 40) |
((host_int & uint64_t{0xFF00000000000000}) >> 56));
#endif // bswap_64
}
inline uint32_t gbswap_32(uint32_t host_int) {
#if defined(__GLIBC__)
return bswap_32(host_int);
#else
return (((host_int & uint32_t{0xFF}) << 24) |
((host_int & uint32_t{0xFF00}) << 8) |
((host_int & uint32_t{0xFF0000}) >> 8) |
((host_int & uint32_t{0xFF000000}) >> 24));
#endif
}
inline uint16_t gbswap_16(uint16_t host_int) {
#if defined(__GLIBC__)
return bswap_16(host_int);
#else
return (((host_int & uint16_t{0xFF}) << 8) |
((host_int & uint16_t{0xFF00}) >> 8));
#endif
}
#endif // intrinics available
#ifdef PHMAP_IS_LITTLE_ENDIAN
// Definitions for ntohl etc. that don't require us to include
// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather
// than just #defining them because in debug mode, gcc doesn't
// correctly handle the (rather involved) definitions of bswap_32.
// gcc guarantees that inline functions are as fast as macros, so
// this isn't a performance hit.
inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); }
inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); }
inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); }
#elif defined PHMAP_IS_BIG_ENDIAN
// These definitions are simpler on big-endian machines
// These are functions instead of macros to avoid self-assignment warnings
// on calls such as "i = ghtnol(i);". This also provides type checking.
inline uint16_t ghtons(uint16_t x) { return x; }
inline uint32_t ghtonl(uint32_t x) { return x; }
inline uint64_t ghtonll(uint64_t x) { return x; }
#else
#error \
"Unsupported byte order: Either PHMAP_IS_BIG_ENDIAN or " \
"PHMAP_IS_LITTLE_ENDIAN must be defined"
#endif // byte order
inline uint16_t gntohs(uint16_t x) { return ghtons(x); }
inline uint32_t gntohl(uint32_t x) { return ghtonl(x); }
inline uint64_t gntohll(uint64_t x) { return ghtonll(x); }
// Utilities to convert numbers between the current hosts's native byte
// order and little-endian byte order
//
// Load/Store methods are alignment safe
namespace little_endian {
// Conversion functions.
#ifdef PHMAP_IS_LITTLE_ENDIAN
inline uint16_t FromHost16(uint16_t x) { return x; }
inline uint16_t ToHost16(uint16_t x) { return x; }
inline uint32_t FromHost32(uint32_t x) { return x; }
inline uint32_t ToHost32(uint32_t x) { return x; }
inline uint64_t FromHost64(uint64_t x) { return x; }
inline uint64_t ToHost64(uint64_t x) { return x; }
inline constexpr bool IsLittleEndian() { return true; }
#elif defined PHMAP_IS_BIG_ENDIAN
inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); }
inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); }
inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); }
inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); }
inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); }
inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); }
inline constexpr bool IsLittleEndian() { return false; }
#endif /* ENDIAN */
// Functions to do unaligned loads and stores in little-endian order.
inline uint16_t Load16(const void *p) {
return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p));
}
inline void Store16(void *p, uint16_t v) {
PHMAP_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
}
inline uint32_t Load32(const void *p) {
return ToHost32(PHMAP_INTERNAL_UNALIGNED_LOAD32(p));
}
inline void Store32(void *p, uint32_t v) {
PHMAP_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
}
inline uint64_t Load64(const void *p) {
return ToHost64(PHMAP_INTERNAL_UNALIGNED_LOAD64(p));
}
inline void Store64(void *p, uint64_t v) {
PHMAP_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
}
} // namespace little_endian
// Utilities to convert numbers between the current hosts's native byte
// order and big-endian byte order (same as network byte order)
//
// Load/Store methods are alignment safe
namespace big_endian {
#ifdef PHMAP_IS_LITTLE_ENDIAN
inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); }
inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); }
inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); }
inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); }
inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); }
inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); }
inline constexpr bool IsLittleEndian() { return true; }
#elif defined PHMAP_IS_BIG_ENDIAN
inline uint16_t FromHost16(uint16_t x) { return x; }
inline uint16_t ToHost16(uint16_t x) { return x; }
inline uint32_t FromHost32(uint32_t x) { return x; }
inline uint32_t ToHost32(uint32_t x) { return x; }
inline uint64_t FromHost64(uint64_t x) { return x; }
inline uint64_t ToHost64(uint64_t x) { return x; }
inline constexpr bool IsLittleEndian() { return false; }
#endif /* ENDIAN */
// Functions to do unaligned loads and stores in big-endian order.
inline uint16_t Load16(const void *p) {
return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p));
}
inline void Store16(void *p, uint16_t v) {
PHMAP_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
}
inline uint32_t Load32(const void *p) {
return ToHost32(PHMAP_INTERNAL_UNALIGNED_LOAD32(p));
}
inline void Store32(void *p, uint32_t v) {
PHMAP_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
}
inline uint64_t Load64(const void *p) {
return ToHost64(PHMAP_INTERNAL_UNALIGNED_LOAD64(p));
}
inline void Store64(void *p, uint64_t v) {
PHMAP_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
}
} // namespace big_endian
} // namespace phmap
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // phmap_bits_h_guard_
#if !defined(phmap_config_h_guard_)
#define phmap_config_h_guard_
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://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.
//
// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
// with modifications.
//
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://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.
// ---------------------------------------------------------------------------
#define PHMAP_VERSION_MAJOR 1
#define PHMAP_VERSION_MINOR 0
#define PHMAP_VERSION_PATCH 0
// Included for the __GLIBC__ macro (or similar macros on other systems).
#include <limits.h>
#ifdef __cplusplus
// Included for __GLIBCXX__, _LIBCPP_VERSION
#include <cstddef>
#endif // __cplusplus
#if defined(__APPLE__)
// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
// __IPHONE_8_0.
#include <Availability.h>
#include <TargetConditionals.h>
#endif
#define PHMAP_XSTR(x) PHMAP_STR(x)
#define PHMAP_STR(x) #x
#define PHMAP_VAR_NAME_VALUE(var) #var "=" PHMAP_STR(var)
// -----------------------------------------------------------------------------
// Some sanity checks
// -----------------------------------------------------------------------------
//#if defined(__CYGWIN__)
// #error "Cygwin is not supported."
//#endif
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__)
#error "phmap requires Visual Studio 2015 Update 2 or higher."
#endif
// We support gcc 4.7 and later.
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
#error "phmap requires gcc 4.7 or higher."
#endif
#endif
// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later.
// This corresponds to Apple Xcode version 4.5.
#if defined(__apple_build_version__) && __apple_build_version__ < 4211165
#error "phmap requires __apple_build_version__ of 4211165 or higher."
#endif
// Enforce C++11 as the minimum.
#if defined(__cplusplus) && !defined(_MSC_VER)
#if __cplusplus < 201103L
#error "C++ versions less than C++11 are not supported."
#endif
#endif
// We have chosen glibc 2.12 as the minimum
#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
#if !__GLIBC_PREREQ(2, 12)
#error "Minimum required version of glibc is 2.12."
#endif
#endif
#if defined(_STLPORT_VERSION)
#error "STLPort is not supported."
#endif
#if CHAR_BIT != 8
#error "phmap assumes CHAR_BIT == 8."
#endif
// phmap currently assumes that an int is 4 bytes.
#if INT_MAX < 2147483647
#error "phmap assumes that int is at least 4 bytes. "
#endif
// -----------------------------------------------------------------------------
// Compiler Feature Checks
// -----------------------------------------------------------------------------
#ifdef __has_builtin
#define PHMAP_HAVE_BUILTIN(x) __has_builtin(x)
#else
#define PHMAP_HAVE_BUILTIN(x) 0
#endif
#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703) || __cplusplus >= 201703
#define PHMAP_HAVE_CC17 1
#else
#define PHMAP_HAVE_CC17 0
#endif
#define PHMAP_BRANCHLESS 1
// ----------------------------------------------------------------
// Checks whether `std::is_trivially_destructible<T>` is supported.
// ----------------------------------------------------------------
#ifdef PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
#error PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
#elif defined(_LIBCPP_VERSION) || \
(!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
defined(_MSC_VER)
#define PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
#endif
// --------------------------------------------------------------
// Checks whether `std::is_trivially_default_constructible<T>` is
// supported.
// --------------------------------------------------------------
#if defined(PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
#error PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
#elif defined(PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
#error PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
(!defined(__clang__) && defined(__GNUC__) && \
(__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
(defined(_MSC_VER) && !defined(__NVCC__))
#define PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
#endif
// -------------------------------------------------------------------
// Checks whether C++11's `thread_local` storage duration specifier is
// supported.
// -------------------------------------------------------------------
#ifdef PHMAP_HAVE_THREAD_LOCAL
#error PHMAP_HAVE_THREAD_LOCAL cannot be directly set
#elif defined(__APPLE__)
#if __has_feature(cxx_thread_local) && \
!(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
#define PHMAP_HAVE_THREAD_LOCAL 1
#endif
#else // !defined(__APPLE__)
#define PHMAP_HAVE_THREAD_LOCAL 1
#endif
#if defined(__ANDROID__) && defined(__clang__)
#if __has_include(<android/ndk-version.h>)
#include <android/ndk-version.h>
#endif // __has_include(<android/ndk-version.h>)
#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
defined(__NDK_MINOR__) && \
((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
#undef PHMAP_HAVE_TLS
#undef PHMAP_HAVE_THREAD_LOCAL
#endif
#endif
// ------------------------------------------------------------
// Checks whether the __int128 compiler extension for a 128-bit
// integral type is supported.
// ------------------------------------------------------------
#ifdef PHMAP_HAVE_INTRINSIC_INT128
#error PHMAP_HAVE_INTRINSIC_INT128 cannot be directly set
#elif defined(__SIZEOF_INT128__)
#if (defined(__clang__) && !defined(_WIN32) && !defined(__aarch64__)) || \
(defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
(defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
#define PHMAP_HAVE_INTRINSIC_INT128 1
#elif defined(__CUDACC__)
#if __CUDACC_VER__ >= 70000
#define PHMAP_HAVE_INTRINSIC_INT128 1
#endif // __CUDACC_VER__ >= 70000
#endif // defined(__CUDACC__)
#endif
// ------------------------------------------------------------------
// Checks whether the compiler both supports and enables exceptions.
// ------------------------------------------------------------------
#ifdef PHMAP_HAVE_EXCEPTIONS
#error PHMAP_HAVE_EXCEPTIONS cannot be directly set.
#elif defined(__clang__)
#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
#define PHMAP_HAVE_EXCEPTIONS 1
#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
!(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \
!(defined(_MSC_VER) && !defined(_CPPUNWIND))
#define PHMAP_HAVE_EXCEPTIONS 1
#endif
// -----------------------------------------------------------------------
// Checks whether the platform has an mmap(2) implementation as defined in
// POSIX.1-2001.
// -----------------------------------------------------------------------
#ifdef PHMAP_HAVE_MMAP
#error PHMAP_HAVE_MMAP cannot be directly set
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
defined(__ASYLO__)
#define PHMAP_HAVE_MMAP 1
#endif
// -----------------------------------------------------------------------
// Checks the endianness of the platform.
// -----------------------------------------------------------------------
#if defined(PHMAP_IS_BIG_ENDIAN)
#error "PHMAP_IS_BIG_ENDIAN cannot be directly set."
#endif
#if defined(PHMAP_IS_LITTLE_ENDIAN)
#error "PHMAP_IS_LITTLE_ENDIAN cannot be directly set."
#endif
#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define PHMAP_IS_LITTLE_ENDIAN 1
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define PHMAP_IS_BIG_ENDIAN 1
#elif defined(_WIN32)
#define PHMAP_IS_LITTLE_ENDIAN 1
#else
#error "phmap endian detection needs to be set up for your compiler"
#endif
#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \
defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400
#define PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE 1
#else
#define PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE 0
#endif
// ---------------------------------------------------------------------------
// Checks whether C++17 std::any is available by checking whether <any> exists.
// ---------------------------------------------------------------------------
#ifdef PHMAP_HAVE_STD_ANY
#error "PHMAP_HAVE_STD_ANY cannot be directly set."
#endif
#ifdef __has_include
#if __has_include(<any>) && __cplusplus >= 201703L && \
!PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
#define PHMAP_HAVE_STD_ANY 1
#endif
#endif
#ifdef PHMAP_HAVE_STD_OPTIONAL
#error "PHMAP_HAVE_STD_OPTIONAL cannot be directly set."
#endif
#ifdef __has_include
#if __has_include(<optional>) && __cplusplus >= 201703L && \
!PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
#define PHMAP_HAVE_STD_OPTIONAL 1
#endif
#endif
#ifdef PHMAP_HAVE_STD_VARIANT
#error "PHMAP_HAVE_STD_VARIANT cannot be directly set."
#endif
#ifdef __has_include
#if __has_include(<variant>) && __cplusplus >= 201703L && \
!PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
#define PHMAP_HAVE_STD_VARIANT 1
#endif
#endif
#ifdef PHMAP_HAVE_STD_STRING_VIEW
#error "PHMAP_HAVE_STD_STRING_VIEW cannot be directly set."
#endif
#ifdef __has_include
#if __has_include(<string_view>) && __cplusplus >= 201703L && \
(!defined(_MSC_VER) || _MSC_VER >= 1920) // vs2019
#define PHMAP_HAVE_STD_STRING_VIEW 1
#endif
#endif
// #pragma message(PHMAP_VAR_NAME_VALUE(_MSVC_LANG))
#if defined(_MSC_VER) && _MSC_VER >= 1910 && PHMAP_HAVE_CC17
// #define PHMAP_HAVE_STD_ANY 1
#define PHMAP_HAVE_STD_OPTIONAL 1
#define PHMAP_HAVE_STD_VARIANT 1
#if !defined(PHMAP_HAVE_STD_STRING_VIEW) && _MSC_VER >= 1920
#define PHMAP_HAVE_STD_STRING_VIEW 1
#endif
#endif
#if PHMAP_HAVE_CC17
#define PHMAP_HAVE_SHARED_MUTEX 1
#endif
#ifndef PHMAP_HAVE_STD_STRING_VIEW
#define PHMAP_HAVE_STD_STRING_VIEW 0
#endif
// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
// SEH exception from emplace for variant<SomeStruct> when constructing the
// struct can throw. This defeats some of variant_test and
// variant_exception_safety_test.
#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
#define PHMAP_INTERNAL_MSVC_2017_DBG_MODE
#endif
// -----------------------------------------------------------------------------
// Sanitizer Attributes
// -----------------------------------------------------------------------------
//
// Sanitizer-related attributes are not "defined" in this file (and indeed
// are not defined as such in any file). To utilize the following
// sanitizer-related attributes within your builds, define the following macros
// within your build using a `-D` flag, along with the given value for
// `-fsanitize`:
//
// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8)
// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only)
// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+)
// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+)
// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only)
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// A function-like feature checking macro that is a wrapper around
// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a
// nonzero constant integer if the attribute is supported or 0 if not.
//
// It evaluates to zero if `__has_attribute` is not defined by the compiler.
// -----------------------------------------------------------------------------
#ifdef __has_attribute
#define PHMAP_HAVE_ATTRIBUTE(x) __has_attribute(x)
#else
#define PHMAP_HAVE_ATTRIBUTE(x) 0
#endif
// -----------------------------------------------------------------------------
// A function-like feature checking macro that accepts C++11 style attributes.
// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
// find `__has_cpp_attribute`, will evaluate to 0.
// -----------------------------------------------------------------------------
#if defined(__cplusplus) && defined(__has_cpp_attribute)
#define PHMAP_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
#else
#define PHMAP_HAVE_CPP_ATTRIBUTE(x) 0
#endif
// -----------------------------------------------------------------------------
// Function Attributes
// -----------------------------------------------------------------------------
#if PHMAP_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_PRINTF_ATTRIBUTE(string_index, first_to_check) \
__attribute__((__format__(__printf__, string_index, first_to_check)))
#define PHMAP_SCANF_ATTRIBUTE(string_index, first_to_check) \
__attribute__((__format__(__scanf__, string_index, first_to_check)))
#else
#define PHMAP_PRINTF_ATTRIBUTE(string_index, first_to_check)
#define PHMAP_SCANF_ATTRIBUTE(string_index, first_to_check)
#endif
#if PHMAP_HAVE_ATTRIBUTE(always_inline) || \
(defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
#define PHMAP_HAVE_ATTRIBUTE_ALWAYS_INLINE 1
#else
#define PHMAP_ATTRIBUTE_ALWAYS_INLINE
#endif
#if !defined(__INTEL_COMPILER) && (PHMAP_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)))
#define PHMAP_ATTRIBUTE_NOINLINE __attribute__((noinline))
#define PHMAP_HAVE_ATTRIBUTE_NOINLINE 1
#else
#define PHMAP_ATTRIBUTE_NOINLINE
#endif
#if PHMAP_HAVE_ATTRIBUTE(disable_tail_calls)
#define PHMAP_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
#define PHMAP_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
#elif defined(__GNUC__) && !defined(__clang__)
#define PHMAP_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
#define PHMAP_ATTRIBUTE_NO_TAIL_CALL \
__attribute__((optimize("no-optimize-sibling-calls")))
#else
#define PHMAP_ATTRIBUTE_NO_TAIL_CALL
#define PHMAP_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
#endif
#if (PHMAP_HAVE_ATTRIBUTE(weak) || \
(defined(__GNUC__) && !defined(__clang__))) && \
!(defined(__llvm__) && defined(_WIN32))
#undef PHMAP_ATTRIBUTE_WEAK
#define PHMAP_ATTRIBUTE_WEAK __attribute__((weak))
#define PHMAP_HAVE_ATTRIBUTE_WEAK 1
#else
#define PHMAP_ATTRIBUTE_WEAK
#define PHMAP_HAVE_ATTRIBUTE_WEAK 0
#endif
#if PHMAP_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
#else
#define PHMAP_ATTRIBUTE_NONNULL(...)
#endif
#if PHMAP_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_NORETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
#define PHMAP_ATTRIBUTE_NORETURN __declspec(noreturn)
#else
#define PHMAP_ATTRIBUTE_NORETURN
#endif
#if defined(__GNUC__) && defined(ADDRESS_SANITIZER)
#define PHMAP_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
#else
#define PHMAP_ATTRIBUTE_NO_SANITIZE_ADDRESS
#endif
#if defined(__GNUC__) && defined(MEMORY_SANITIZER)
#define PHMAP_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
#else
#define PHMAP_ATTRIBUTE_NO_SANITIZE_MEMORY
#endif
#if defined(__GNUC__) && defined(THREAD_SANITIZER)
#define PHMAP_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
#else
#define PHMAP_ATTRIBUTE_NO_SANITIZE_THREAD
#endif
#if defined(__GNUC__) && \
(defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER))
#define PHMAP_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
__attribute__((no_sanitize("undefined")))
#else
#define PHMAP_ATTRIBUTE_NO_SANITIZE_UNDEFINED
#endif
#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY)
#define PHMAP_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
#else
#define PHMAP_ATTRIBUTE_NO_SANITIZE_CFI
#endif
#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER)
#define PHMAP_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
__attribute__((no_sanitize("safe-stack")))
#else
#define PHMAP_ATTRIBUTE_NO_SANITIZE_SAFESTACK
#endif
#if PHMAP_HAVE_ATTRIBUTE(returns_nonnull) || \
(defined(__GNUC__) && \
(__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
!defined(__clang__))
#define PHMAP_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
#else
#define PHMAP_ATTRIBUTE_RETURNS_NONNULL
#endif
#ifdef PHMAP_HAVE_ATTRIBUTE_SECTION
#error PHMAP_HAVE_ATTRIBUTE_SECTION cannot be directly set
#elif (PHMAP_HAVE_ATTRIBUTE(section) || \
(defined(__GNUC__) && !defined(__clang__))) && \
!defined(__APPLE__) && PHMAP_HAVE_ATTRIBUTE_WEAK
#define PHMAP_HAVE_ATTRIBUTE_SECTION 1
#ifndef PHMAP_ATTRIBUTE_SECTION
#define PHMAP_ATTRIBUTE_SECTION(name) \
__attribute__((section(#name))) __attribute__((noinline))
#endif
#ifndef PHMAP_ATTRIBUTE_SECTION_VARIABLE
#define PHMAP_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
#endif
#ifndef PHMAP_DECLARE_ATTRIBUTE_SECTION_VARS
#define PHMAP_DECLARE_ATTRIBUTE_SECTION_VARS(name) \
extern char __start_##name[] PHMAP_ATTRIBUTE_WEAK; \
extern char __stop_##name[] PHMAP_ATTRIBUTE_WEAK
#endif
#ifndef PHMAP_DEFINE_ATTRIBUTE_SECTION_VARS
#define PHMAP_INIT_ATTRIBUTE_SECTION_VARS(name)
#define PHMAP_DEFINE_ATTRIBUTE_SECTION_VARS(name)
#endif
#define PHMAP_ATTRIBUTE_SECTION_START(name) \
(reinterpret_cast<void *>(__start_##name))
#define PHMAP_ATTRIBUTE_SECTION_STOP(name) \
(reinterpret_cast<void *>(__stop_##name))
#else // !PHMAP_HAVE_ATTRIBUTE_SECTION
#define PHMAP_HAVE_ATTRIBUTE_SECTION 0
#define PHMAP_ATTRIBUTE_SECTION(name)
#define PHMAP_ATTRIBUTE_SECTION_VARIABLE(name)
#define PHMAP_INIT_ATTRIBUTE_SECTION_VARS(name)
#define PHMAP_DEFINE_ATTRIBUTE_SECTION_VARS(name)
#define PHMAP_DECLARE_ATTRIBUTE_SECTION_VARS(name)
#define PHMAP_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
#define PHMAP_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
#endif // PHMAP_ATTRIBUTE_SECTION
#if PHMAP_HAVE_ATTRIBUTE(force_align_arg_pointer) || \
(defined(__GNUC__) && !defined(__clang__))
#if defined(__i386__)
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \
__attribute__((force_align_arg_pointer))
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
#elif defined(__x86_64__)
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
#else // !__i386__ && !__x86_64
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
#endif // __i386__
#else
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
#endif
#if PHMAP_HAVE_ATTRIBUTE(nodiscard)
#define PHMAP_MUST_USE_RESULT [[nodiscard]]
#elif defined(__clang__) && PHMAP_HAVE_ATTRIBUTE(warn_unused_result)
#define PHMAP_MUST_USE_RESULT __attribute__((warn_unused_result))
#else
#define PHMAP_MUST_USE_RESULT
#endif
#if PHMAP_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_HOT __attribute__((hot))
#else
#define PHMAP_ATTRIBUTE_HOT
#endif
#if PHMAP_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_COLD __attribute__((cold))
#else
#define PHMAP_ATTRIBUTE_COLD
#endif
#if defined(__clang__)
#if PHMAP_HAVE_CPP_ATTRIBUTE(clang::reinitializes)
#define PHMAP_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
#else
#define PHMAP_ATTRIBUTE_REINITIALIZES
#endif
#else
#define PHMAP_ATTRIBUTE_REINITIALIZES
#endif
#if PHMAP_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
#undef PHMAP_ATTRIBUTE_UNUSED
#define PHMAP_ATTRIBUTE_UNUSED __attribute__((__unused__))
#else
#define PHMAP_ATTRIBUTE_UNUSED
#endif
#if PHMAP_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
#else
#define PHMAP_ATTRIBUTE_INITIAL_EXEC
#endif
#if PHMAP_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_PACKED __attribute__((__packed__))
#else
#define PHMAP_ATTRIBUTE_PACKED
#endif
#if PHMAP_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
#define PHMAP_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes)))
#else
#define PHMAP_ATTRIBUTE_FUNC_ALIGN(bytes)
#endif
// ----------------------------------------------------------------------
// Figure out SSE support
// ----------------------------------------------------------------------
#ifndef PHMAP_HAVE_SSE2
#if defined(__SSE2__) || \
(defined(_MSC_VER) && \
(defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)))
#define PHMAP_HAVE_SSE2 1
#else
#define PHMAP_HAVE_SSE2 0
#endif
#endif
#ifndef PHMAP_HAVE_SSSE3
#if defined(__SSSE3__) || defined(__AVX2__)
#define PHMAP_HAVE_SSSE3 1
#else
#define PHMAP_HAVE_SSSE3 0
#endif
#endif
#if PHMAP_HAVE_SSSE3 && !PHMAP_HAVE_SSE2
#error "Bad configuration!"
#endif
#if PHMAP_HAVE_SSE2
#include <emmintrin.h>
#endif
#if PHMAP_HAVE_SSSE3
#include <tmmintrin.h>
#endif
// ----------------------------------------------------------------------
// constexpr if
// ----------------------------------------------------------------------
#if PHMAP_HAVE_CC17
#define PHMAP_IF_CONSTEXPR(expr) if constexpr ((expr))
#else
#define PHMAP_IF_CONSTEXPR(expr) if ((expr))
#endif
// ----------------------------------------------------------------------
// base/macros.h
// ----------------------------------------------------------------------
// PHMAP_ARRAYSIZE()
//
// Returns the number of elements in an array as a compile-time constant, which
// can be used in defining new arrays. If you use this macro on a pointer by
// mistake, you will get a compile-time error.
#define PHMAP_ARRAYSIZE(array) \
(sizeof(::phmap::macros_internal::ArraySizeHelper(array)))
namespace phmap {
namespace macros_internal {
// Note: this internal template function declaration is used by PHMAP_ARRAYSIZE.
// The function doesn't need a definition, as we only use its type.
template <typename T, size_t N>
auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
} // namespace macros_internal
} // namespace phmap
// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported.
#if defined(__clang__) && defined(__has_warning)
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
#define PHMAP_FALLTHROUGH_INTENDED [[clang::fallthrough]]
#endif
#elif defined(__GNUC__) && __GNUC__ >= 7
#define PHMAP_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
#endif
#ifndef PHMAP_FALLTHROUGH_INTENDED
#define PHMAP_FALLTHROUGH_INTENDED \
do { } while (0)
#endif
// PHMAP_DEPRECATED()
//
// Marks a deprecated class, struct, enum, function, method and variable
// declarations. The macro argument is used as a custom diagnostic message (e.g.
// suggestion of a better alternative).
//
// Example:
//
// class PHMAP_DEPRECATED("Use Bar instead") Foo {...};
// PHMAP_DEPRECATED("Use Baz instead") void Bar() {...}
//
// Every usage of a deprecated entity will trigger a warning when compiled with
// clang's `-Wdeprecated-declarations` option. This option is turned off by
// default, but the warnings will be reported by clang-tidy.
#if defined(__clang__) && __cplusplus >= 201103L
#define PHMAP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#ifndef PHMAP_DEPRECATED
#define PHMAP_DEPRECATED(message)
#endif
// PHMAP_BAD_CALL_IF()
//
// Used on a function overload to trap bad calls: any call that matches the
// overload will cause a compile-time error. This macro uses a clang-specific
// "enable_if" attribute, as described at
// http://clang.llvm.org/docs/AttributeReference.html#enable-if
//
// Overloads which use this macro should be bracketed by
// `#ifdef PHMAP_BAD_CALL_IF`.
//
// Example:
//
// int isdigit(int c);
// #ifdef PHMAP_BAD_CALL_IF
// int isdigit(int c)
// PHMAP_BAD_CALL_IF(c <= -1 || c > 255,
// "'c' must have the value of an unsigned char or EOF");
// #endif // PHMAP_BAD_CALL_IF
#if defined(__clang__)
#if __has_attribute(enable_if)
#define PHMAP_BAD_CALL_IF(expr, msg) \
__attribute__((enable_if(expr, "Bad call trap"), unavailable(msg)))
#endif
#endif
// PHMAP_ASSERT()
//
// In C++11, `assert` can't be used portably within constexpr functions.
// PHMAP_ASSERT functions as a runtime assert but works in C++11 constexpr
// functions. Example:
//
// constexpr double Divide(double a, double b) {
// return PHMAP_ASSERT(b != 0), a / b;
// }
//
// This macro is inspired by
// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
#if defined(NDEBUG)
#define PHMAP_ASSERT(expr) (false ? (void)(expr) : (void)0)
#else
#define PHMAP_ASSERT(expr) \
(PHMAP_PREDICT_TRUE((expr)) ? (void)0 \
: [] { assert(false && #expr); }()) // NOLINT
#endif
#ifdef PHMAP_HAVE_EXCEPTIONS
#define PHMAP_INTERNAL_TRY try
#define PHMAP_INTERNAL_CATCH_ANY catch (...)
#define PHMAP_INTERNAL_RETHROW do { throw; } while (false)
#else // PHMAP_HAVE_EXCEPTIONS
#define PHMAP_INTERNAL_TRY if (true)
#define PHMAP_INTERNAL_CATCH_ANY else if (false)
#define PHMAP_INTERNAL_RETHROW do {} while (false)
#endif // PHMAP_HAVE_EXCEPTIONS
#endif // phmap_config_h_guard_
#if !defined(phmap_dump_h_guard_)
#define phmap_dump_h_guard_
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// providing dump/load/mmap_load
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://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 <iostream>
#include <fstream>
#include <sstream>
#include "phmap.h"
namespace phmap
{
namespace type_traits_internal {
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20150801
template<typename T> struct IsTriviallyCopyable : public std::integral_constant<bool, __has_trivial_copy(T)> {};
#else
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
#endif
template <class T1, class T2>
struct IsTriviallyCopyable<std::pair<T1, T2>> {
static constexpr bool value = IsTriviallyCopyable<T1>::value && IsTriviallyCopyable<T2>::value;
};
}
namespace priv {
// ------------------------------------------------------------------------
// dump/load for raw_hash_set
// ------------------------------------------------------------------------
template <class Policy, class Hash, class Eq, class Alloc>
template<typename OutputArchive>
bool raw_hash_set<Policy, Hash, Eq, Alloc>::dump(OutputArchive& ar) const {
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
"value_type should be trivially copyable");
if (!ar.dump(size_)) {
std::cerr << "Failed to dump size_" << std::endl;
return false;
}
if (size_ == 0) {
return true;
}
if (!ar.dump(capacity_)) {
std::cerr << "Failed to dump capacity_" << std::endl;
return false;
}
if (!ar.dump(reinterpret_cast<char*>(ctrl_),
sizeof(ctrl_t) * (capacity_ + Group::kWidth + 1))) {
std::cerr << "Failed to dump ctrl_" << std::endl;
return false;
}
if (!ar.dump(reinterpret_cast<char*>(slots_),
sizeof(slot_type) * capacity_)) {
std::cerr << "Failed to dump slot_" << std::endl;
return false;
}
return true;
}
template <class Policy, class Hash, class Eq, class Alloc>
template<typename InputArchive>
bool raw_hash_set<Policy, Hash, Eq, Alloc>::load(InputArchive& ar) {
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
"value_type should be trivially copyable");
raw_hash_set<Policy, Hash, Eq, Alloc>().swap(*this); // clear any existing content
if (!ar.load(&size_)) {
std::cerr << "Failed to load size_" << std::endl;
return false;
}
if (size_ == 0) {
return true;
}
if (!ar.load(&capacity_)) {
std::cerr << "Failed to load capacity_" << std::endl;
return false;
}
// allocate memory for ctrl_ and slots_
initialize_slots();
if (!ar.load(reinterpret_cast<char*>(ctrl_),
sizeof(ctrl_t) * (capacity_ + Group::kWidth + 1))) {
std::cerr << "Failed to load ctrl" << std::endl;
return false;
}
if (!ar.load(reinterpret_cast<char*>(slots_),
sizeof(slot_type) * capacity_)) {
std::cerr << "Failed to load slot" << std::endl;
return false;
}
return true;
}
// ------------------------------------------------------------------------
// dump/load for parallel_hash_set
// ------------------------------------------------------------------------
template <size_t N,
template <class, class, class, class> class RefSet,
class Mtx_,
class Policy, class Hash, class Eq, class Alloc>
template<typename OutputArchive>
bool parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, Eq, Alloc>::dump(OutputArchive& ar) const {
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
"value_type should be trivially copyable");
if (! ar.dump(subcnt())) {
std::cerr << "Failed to dump meta!" << std::endl;
return false;
}
for (size_t i = 0; i < sets_.size(); ++i) {
auto& inner = sets_[i];
typename Lockable::UniqueLock m(const_cast<Inner&>(inner));
if (!inner.set_.dump(ar)) {
std::cerr << "Failed to dump submap " << i << std::endl;
return false;
}
}
return true;
}
template <size_t N,
template <class, class, class, class> class RefSet,
class Mtx_,
class Policy, class Hash, class Eq, class Alloc>
template<typename InputArchive>
bool parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, Eq, Alloc>::load(InputArchive& ar) {
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
"value_type should be trivially copyable");
size_t submap_count = 0;
if (!ar.load(&submap_count)) {
std::cerr << "Failed to load submap count!" << std::endl;
return false;
}
if (submap_count != subcnt()) {
std::cerr << "submap count(" << submap_count << ") != N(" << N << ")" << std::endl;
return false;
}
for (size_t i = 0; i < submap_count; ++i) {
auto& inner = sets_[i];
typename Lockable::UniqueLock m(const_cast<Inner&>(inner));
if (!inner.set_.load(ar)) {
std::cerr << "Failed to load submap " << i << std::endl;
return false;
}
}
return true;
}
} // namespace priv
// ------------------------------------------------------------------------
// BinaryArchive
// File is closed when archive object is destroyed
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
class BinaryOutputArchive {
public:
BinaryOutputArchive(const char *file_path) {
ofs_.open(file_path, std::ios_base::binary);
}
bool dump(const char *p, size_t sz) {
ofs_.write(p, sz);
return true;
}
template<typename V>
typename std::enable_if<type_traits_internal::IsTriviallyCopyable<V>::value, bool>::type
dump(const V& v) {
ofs_.write(reinterpret_cast<const char *>(&v), sizeof(V));
return true;
}
private:
std::ofstream ofs_;
};
class BinaryInputArchive {
public:
BinaryInputArchive(const char * file_path) {
ifs_.open(file_path, std::ios_base::binary);
}
bool load(char* p, size_t sz) {
ifs_.read(p, sz);
return true;
}
template<typename V>
typename std::enable_if<type_traits_internal::IsTriviallyCopyable<V>::value, bool>::type
load(V* v) {
ifs_.read(reinterpret_cast<char *>(v), sizeof(V));
return true;
}
private:
std::ifstream ifs_;
};
} // namespace phmap
#endif // phmap_dump_h_guard_
#if !defined(phmap_fwd_decl_h_guard_)
#define phmap_fwd_decl_h_guard_
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// ---------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4514) // unreferenced inline function has been removed
#pragma warning(disable : 4710) // function not inlined
#pragma warning(disable : 4711) // selected for automatic inline expansion
#endif
#include <memory>
#include <utility>
#if defined(PHMAP_USE_ABSL_HASH) && !defined(ABSL_HASH_HASH_H_)
namespace absl { template <class T> struct Hash; };
#endif
namespace phmap {
#if defined(PHMAP_USE_ABSL_HASH)
template <class T> using Hash = ::absl::Hash<T>;
#else
template <class T> struct Hash;
#endif
template <class T> struct EqualTo;
template <class T> struct Less;
template <class T> using Allocator = typename std::allocator<T>;
template<class T1, class T2> using Pair = typename std::pair<T1, T2>;
class NullMutex;
namespace priv {
// The hash of an object of type T is computed by using phmap::Hash.
template <class T, class E = void>
struct HashEq
{
using Hash = phmap::Hash<T>;
using Eq = phmap::EqualTo<T>;
};
template <class T>
using hash_default_hash = typename priv::HashEq<T>::Hash;
template <class T>
using hash_default_eq = typename priv::HashEq<T>::Eq;
// type alias for std::allocator so we can forward declare without including other headers
template <class T>
using Allocator = typename phmap::Allocator<T>;
// type alias for std::pair so we can forward declare without including other headers
template<class T1, class T2>
using Pair = typename phmap::Pair<T1, T2>;
} // namespace priv
// ------------- forward declarations for hash containers ----------------------------------
template <class T,
class Hash = phmap::priv::hash_default_hash<T>,
class Eq = phmap::priv::hash_default_eq<T>,
class Alloc = phmap::priv::Allocator<T>> // alias for std::allocator
class flat_hash_set;
template <class K, class V,
class Hash = phmap::priv::hash_default_hash<K>,
class Eq = phmap::priv::hash_default_eq<K>,
class Alloc = phmap::priv::Allocator<
phmap::priv::Pair<const K, V>>> // alias for std::allocator
class flat_hash_map;
template <class T,
class Hash = phmap::priv::hash_default_hash<T>,
class Eq = phmap::priv::hash_default_eq<T>,
class Alloc = phmap::priv::Allocator<T>> // alias for std::allocator
class node_hash_set;
template <class Key, class Value,
class Hash = phmap::priv::hash_default_hash<Key>,
class Eq = phmap::priv::hash_default_eq<Key>,
class Alloc = phmap::priv::Allocator<
phmap::priv::Pair<const Key, Value>>> // alias for std::allocator
class node_hash_map;
template <class T,
class Hash = phmap::priv::hash_default_hash<T>,
class Eq = phmap::priv::hash_default_eq<T>,
class Alloc = phmap::priv::Allocator<T>, // alias for std::allocator
size_t N = 4, // 2**N submaps
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
class parallel_flat_hash_set;
template <class K, class V,
class Hash = phmap::priv::hash_default_hash<K>,
class Eq = phmap::priv::hash_default_eq<K>,
class Alloc = phmap::priv::Allocator<
phmap::priv::Pair<const K, V>>, // alias for std::allocator
size_t N = 4, // 2**N submaps
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
class parallel_flat_hash_map;
template <class T,
class Hash = phmap::priv::hash_default_hash<T>,
class Eq = phmap::priv::hash_default_eq<T>,
class Alloc = phmap::priv::Allocator<T>, // alias for std::allocator
size_t N = 4, // 2**N submaps
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
class parallel_node_hash_set;
template <class Key, class Value,
class Hash = phmap::priv::hash_default_hash<Key>,
class Eq = phmap::priv::hash_default_eq<Key>,
class Alloc = phmap::priv::Allocator<
phmap::priv::Pair<const Key, Value>>, // alias for std::allocator
size_t N = 4, // 2**N submaps
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
class parallel_node_hash_map;
// ------------- forward declarations for btree containers ----------------------------------
template <typename Key, typename Compare = phmap::Less<Key>,
typename Alloc = phmap::Allocator<Key>>
class btree_set;
template <typename Key, typename Compare = phmap::Less<Key>,
typename Alloc = phmap::Allocator<Key>>
class btree_multiset;
template <typename Key, typename Value, typename Compare = phmap::Less<Key>,
typename Alloc = phmap::Allocator<phmap::priv::Pair<const Key, Value>>>
class btree_map;
template <typename Key, typename Value, typename Compare = phmap::Less<Key>,
typename Alloc = phmap::Allocator<phmap::priv::Pair<const Key, Value>>>
class btree_multimap;
} // namespace phmap
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // phmap_fwd_decl_h_guard_
#if !defined(phmap_utils_h_guard_)
#define phmap_utils_h_guard_
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// minimal header providing phmap::HashState
//
// use as: phmap::HashState().combine(0, _first_name, _last_name, _age);
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://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.
// ---------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4514) // unreferenced inline function has been removed
#pragma warning(disable : 4710) // function not inlined
#pragma warning(disable : 4711) // selected for automatic inline expansion
#endif
#include <cstdint>
#include <functional>
#include <tuple>
#include "phmap_bits.h"
// ---------------------------------------------------------------
// Absl forward declaration requires global scope.
// ---------------------------------------------------------------
#if defined(PHMAP_USE_ABSL_HASH) && !defined(phmap_fwd_decl_h_guard_) && !defined(ABSL_HASH_HASH_H_)
namespace absl { template <class T> struct Hash; };
#endif
namespace phmap
{
// ---------------------------------------------------------------
// ---------------------------------------------------------------
template<int n>
struct phmap_mix
{
inline size_t operator()(size_t) const;
};
template<>
struct phmap_mix<4>
{
inline size_t operator()(size_t a) const
{
static constexpr uint64_t kmul = 0xcc9e2d51UL;
// static constexpr uint64_t kmul = 0x3B9ACB93UL; // [greg] my own random prime
uint64_t l = a * kmul;
return static_cast<size_t>(l ^ (l >> 32));
}
};
#if defined(PHMAP_HAS_UMUL128)
template<>
struct phmap_mix<8>
{
// Very fast mixing (similar to Abseil)
inline size_t operator()(size_t a) const
{
static constexpr uint64_t k = 0xde5fb9d2630458e9ULL;
// static constexpr uint64_t k = 0x7C9D0BF0567102A5ULL; // [greg] my own random prime
uint64_t h;
uint64_t l = umul128(a, k, &h);
return static_cast<size_t>(h + l);
}
};
#else
template<>
struct phmap_mix<8>
{
inline size_t operator()(size_t a) const
{
a = (~a) + (a << 21); // a = (a << 21) - a - 1;
a = a ^ (a >> 24);
a = (a + (a << 3)) + (a << 8); // a * 265
a = a ^ (a >> 14);
a = (a + (a << 2)) + (a << 4); // a * 21
a = a ^ (a >> 28);
a = a + (a << 31);
return static_cast<size_t>(a);
}
};
#endif
// --------------------------------------------
template<int n>
struct fold_if_needed
{
inline size_t operator()(uint64_t) const;
};
template<>
struct fold_if_needed<4>
{
inline size_t operator()(uint64_t a) const
{
return static_cast<size_t>(a ^ (a >> 32));
}
};
template<>
struct fold_if_needed<8>
{
inline size_t operator()(uint64_t a) const
{
return static_cast<size_t>(a);
}
};
// ---------------------------------------------------------------
// see if class T has a hash_value() friend method
// ---------------------------------------------------------------
template<typename T>
struct has_hash_value
{
private:
typedef std::true_type yes;
typedef std::false_type no;
template<typename U> static auto test(int) -> decltype(hash_value(std::declval<const U&>()) == 1, yes());
template<typename> static no test(...);
public:
static constexpr bool value = std::is_same<decltype(test<T>(0)), yes>::value;
};
#if defined(PHMAP_USE_ABSL_HASH) && !defined(phmap_fwd_decl_h_guard_)
template <class T> using Hash = ::absl::Hash<T>;
#elif !defined(PHMAP_USE_ABSL_HASH)
// ---------------------------------------------------------------
// phmap::Hash
// ---------------------------------------------------------------
template <class T>
struct Hash
{
template <class U, typename std::enable_if<has_hash_value<U>::value, int>::type = 0>
size_t _hash(const T& val) const
{
return hash_value(val);
}
template <class U, typename std::enable_if<!has_hash_value<U>::value, int>::type = 0>
size_t _hash(const T& val) const
{
return std::hash<T>()(val);
}
inline size_t operator()(const T& val) const
{
return _hash<T>(val);
}
};
template <class T>
struct Hash<T *>
{
inline size_t operator()(const T *val) const noexcept
{
return static_cast<size_t>(reinterpret_cast<const uintptr_t>(val));
}
};
template<class ArgumentType, class ResultType>
struct phmap_unary_function
{
typedef ArgumentType argument_type;
typedef ResultType result_type;
};
template <>
struct Hash<bool> : public phmap_unary_function<bool, size_t>
{
inline size_t operator()(bool val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<char> : public phmap_unary_function<char, size_t>
{
inline size_t operator()(char val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<signed char> : public phmap_unary_function<signed char, size_t>
{
inline size_t operator()(signed char val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<unsigned char> : public phmap_unary_function<unsigned char, size_t>
{
inline size_t operator()(unsigned char val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<wchar_t> : public phmap_unary_function<wchar_t, size_t>
{
inline size_t operator()(wchar_t val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<int16_t> : public phmap_unary_function<int16_t, size_t>
{
inline size_t operator()(int16_t val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<uint16_t> : public phmap_unary_function<uint16_t, size_t>
{
inline size_t operator()(uint16_t val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<int32_t> : public phmap_unary_function<int32_t, size_t>
{
inline size_t operator()(int32_t val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<uint32_t> : public phmap_unary_function<uint32_t, size_t>
{
inline size_t operator()(uint32_t val) const noexcept
{ return static_cast<size_t>(val); }
};
template <>
struct Hash<int64_t> : public phmap_unary_function<int64_t, size_t>
{
inline size_t operator()(int64_t val) const noexcept
{ return fold_if_needed<sizeof(size_t)>()(static_cast<uint64_t>(val)); }
};
template <>
struct Hash<uint64_t> : public phmap_unary_function<uint64_t, size_t>
{
inline size_t operator()(uint64_t val) const noexcept
{ return fold_if_needed<sizeof(size_t)>()(val); }
};
template <>
struct Hash<float> : public phmap_unary_function<float, size_t>
{
inline size_t operator()(float val) const noexcept
{
// -0.0 and 0.0 should return same hash
uint32_t *as_int = reinterpret_cast<uint32_t *>(&val);
return (val == 0) ? static_cast<size_t>(0) :
static_cast<size_t>(*as_int);
}
};
template <>
struct Hash<double> : public phmap_unary_function<double, size_t>
{
inline size_t operator()(double val) const noexcept
{
// -0.0 and 0.0 should return same hash
uint64_t *as_int = reinterpret_cast<uint64_t *>(&val);
return (val == 0) ? static_cast<size_t>(0) :
fold_if_needed<sizeof(size_t)>()(*as_int);
}
};
#endif
template <class H, int sz> struct Combiner
{
H operator()(H seed, size_t value);
};
template <class H> struct Combiner<H, 4>
{
H operator()(H seed, size_t value)
{
return seed ^ (value + 0x9e3779b9 + (seed << 6) + (seed >> 2));
}
};
template <class H> struct Combiner<H, 8>
{
H operator()(H seed, size_t value)
{
return seed ^ (value + size_t(0xc6a4a7935bd1e995) + (seed << 6) + (seed >> 2));
}
};
// define HashState to combine member hashes... see example below
// -----------------------------------------------------------------------------
template <typename H>
class HashStateBase {
public:
template <typename T, typename... Ts>
static H combine(H state, const T& value, const Ts&... values);
static H combine(H state) { return state; }
};
template <typename H>
template <typename T, typename... Ts>
H HashStateBase<H>::combine(H seed, const T& v, const Ts&... vs)
{
return HashStateBase<H>::combine(Combiner<H, sizeof(H)>()(
seed, phmap::Hash<T>()(v)),
vs...);
}
using HashState = HashStateBase<size_t>;
// -----------------------------------------------------------------------------
#if !defined(PHMAP_USE_ABSL_HASH)
// define Hash for std::pair
// -------------------------
template<class T1, class T2>
struct Hash<std::pair<T1, T2>> {
size_t operator()(std::pair<T1, T2> const& p) const noexcept {
return phmap::HashState().combine(phmap::Hash<T1>()(p.first), p.second);
}
};
// define Hash for std::tuple
// --------------------------
template<class... T>
struct Hash<std::tuple<T...>> {
size_t operator()(std::tuple<T...> const& t) const noexcept {
return _hash_helper(t);
}
private:
template<size_t I = 0, class ...P>
typename std::enable_if<I == sizeof...(P), size_t>::type
_hash_helper(const std::tuple<P...> &) const noexcept { return 0; }
template<size_t I = 0, class ...P>
typename std::enable_if<I < sizeof...(P), size_t>::type
_hash_helper(const std::tuple<P...> &t) const noexcept {
const auto &el = std::get<I>(t);
using el_type = typename std::remove_cv<typename std::remove_reference<decltype(el)>::type>::type;
return Combiner<size_t, sizeof(size_t)>()(
phmap::Hash<el_type>()(el), _hash_helper<I + 1>(t));
}
};
#endif
} // namespace phmap
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // phmap_utils_h_guard_
......@@ -6,4 +6,10 @@ try:
from .lib import libstarrygl_ops as ops
except Exception as e:
logging.error(e)
logging.error("unable to import libstarrygl.so, some features may not be available.")
\ No newline at end of file
logging.error("unable to import libstarrygl.so, some features may not be available.")
try:
from .lib import libstarrygl_ops_sampler as sampler_ops
except Exception as e:
logging.error(e)
logging.error("unable to import libstarrygl_sampler.so, some features may not be available.")
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment