├── LSGraph ├── Makefile ├── README.md ├── include ├── BC.h ├── BFS.h ├── BitArray.h ├── Components.h ├── LSGraph.h ├── Map.cpp ├── Pagerank.h ├── TC.h ├── VertexSubset.cpp ├── bench_utils.h ├── graph.h ├── helpers.h ├── io_util.h ├── parallel.h ├── parallel_util.h ├── parse_command_line.h ├── rmat_util.h ├── sliding_queue.h ├── spdlog │ ├── async.h │ ├── async_logger.h │ ├── common.h │ ├── details │ │ ├── async_logger_impl.h │ │ ├── circular_q.h │ │ ├── console_globals.h │ │ ├── file_helper.h │ │ ├── fmt_helper.h │ │ ├── log_msg.h │ │ ├── logger_impl.h │ │ ├── mpmc_blocking_q.h │ │ ├── null_mutex.h │ │ ├── os.h │ │ ├── pattern_formatter.h │ │ ├── periodic_worker.h │ │ ├── registry.h │ │ └── thread_pool.h │ ├── fmt │ │ ├── bin_to_hex.h │ │ ├── bundled │ │ │ ├── LICENSE.rst │ │ │ ├── chrono.h │ │ │ ├── color.h │ │ │ ├── core.h │ │ │ ├── format-inl.h │ │ │ ├── format.h │ │ │ ├── locale.h │ │ │ ├── ostream.h │ │ │ ├── posix.h │ │ │ ├── printf.h │ │ │ ├── ranges.h │ │ │ └── time.h │ │ ├── fmt.h │ │ └── ostr.h │ ├── formatter.h │ ├── logger.h │ ├── sinks │ │ ├── android_sink.h │ │ ├── ansicolor_sink.h │ │ ├── base_sink.h │ │ ├── basic_file_sink.h │ │ ├── daily_file_sink.h │ │ ├── dist_sink.h │ │ ├── msvc_sink.h │ │ ├── null_sink.h │ │ ├── ostream_sink.h │ │ ├── rotating_file_sink.h │ │ ├── sink.h │ │ ├── stdout_color_sinks.h │ │ ├── stdout_sinks.h │ │ ├── syslog_sink.h │ │ ├── systemd_sink.h │ │ └── wincolor_sink.h │ ├── spdlog.h │ ├── tweakme.h │ └── version.h ├── tbassert.h └── util.h ├── lib ├── libcilkrts.so └── libcilkrts.so.5 ├── obj ├── LSGraph.o └── util.o └── src ├── HITree ├── HITree.h ├── HITree_nodes.h ├── blocks.h ├── buckets.h ├── common.h ├── conflicts.h ├── iterator.h └── linear_model.h ├── LSGraph.cc └── util.cc /LSGraph: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CGCL-codes/LSGraph/6a34627ad366caac2dbe1a4939bd2d2193f34367/LSGraph -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGETS= LSGraph 2 | 3 | ifdef D 4 | DEBUG=-g -DDEBUG_MODE 5 | OPT= 6 | else 7 | DEBUG= 8 | OPT=-Ofast -DNDEBUG 9 | endif 10 | 11 | ifdef NH 12 | ARCH= 13 | else 14 | #ARCH=-msse4.2 -D__SSE4_2_ 15 | ARCH=-march=native 16 | endif 17 | 18 | ifdef P 19 | PROFILE=-pg -no-pie # for bug in gprof. 20 | endif 21 | 22 | # sudo apt install libstdc++-8-dev 23 | CXX = clang++ -std=c++17 24 | CC = clang -std=gnu11 25 | LD= clang++ -std=c++17 26 | 27 | LOC_INCLUDE=include 28 | LOC_LIB=lib 29 | LOC_SRC=src 30 | OBJDIR=obj 31 | SER=ser 32 | CILK=1 33 | 34 | CXXFLAGS += -mcx16 -Wall $(DEBUG) -g $(PROFILE) $(OPT) $(ARCH) -DOPENMP=$(OPENMP) -DCILK=$(CILK) -DPARLAY_OPENCILK -m64 -I. -I$(LOC_INCLUDE) 35 | 36 | CFLAGS += -mcx16 -Wall $(DEBUG) $(PROFILE) $(OPT) $(ARCH) -DOPENMP=$(OPENMP) -DCILK=$(CILK) -DPARLAY_OPENCILK -m64 -I. -I$(LOC_INCLUDE) 37 | 38 | LDFLAGS += $(DEBUG) $(PROFILE) $(OPT) -L$(LOC_LIB) -lm -lpthread -lssl -lcrypto -ldl 39 | 40 | OPENMP?=0 41 | ifeq ($(OPENMP),1) 42 | CILK=0 43 | else 44 | CILK?=0 45 | endif 46 | 47 | ifeq ($(CILK),1) 48 | CFLAGS += -fcilkplus 49 | CXXFLAGS += -fcilkplus 50 | LDFLAGS += -lcilkrts 51 | endif 52 | 53 | ifeq ($(OPENMP),1) 54 | CFLAGS += -fopenmp 55 | CXXFLAGS += -fopenmp 56 | LDFLAGS += -lomp 57 | endif 58 | 59 | LDFLAGS +="-Wl,-rpath,lib/" 60 | all: LSGraph 61 | LSGraph: $(OBJDIR)/LSGraph.o \ 62 | $(OBJDIR)/util.o 63 | # dependencies between .o files and .cc (or .c) files 64 | $(OBJDIR)/LSGraph.o: $(LOC_SRC)/LSGraph.cc \ 65 | $(LOC_INCLUDE)/graph.h \ 66 | $(LOC_INCLUDE)/util.h 67 | # 68 | # generic build rules 69 | # 70 | 71 | $(TARGETS): 72 | $(LD) $^ $(LDFLAGS) -o $@ 73 | 74 | $(OBJDIR)/%.o: $(LOC_SRC)/%.cc | $(OBJDIR) 75 | $(CXX) $(CXXFLAGS) $(INCLUDE) -c -o $@ $< 76 | 77 | $(OBJDIR)/%.o: $(LOC_SRC)/%.c | $(OBJDIR) 78 | $(CXX) $(CFLAGS) $(INCLUDE) -c -o $@ $< 79 | 80 | $(OBJDIR)/%.o: $(LOC_SRC)/gqf/%.c | $(OBJDIR) 81 | $(CXX) $(CFLAGS) $(INCLUDE) -c -o $@ $< 82 | 83 | $(OBJDIR): 84 | @mkdir -p $(OBJDIR) 85 | 86 | $(SER): 87 | @mkdir -p $(SER) 88 | 89 | clean: 90 | rm -rf $(OBJDIR) core $(TARGETS) 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Locality-centric High-performance Streaming Graph Engine 2 | Open-source release for LSGraph(EuroSys'24) 3 | ## Dependencies 4 | - mimalloc 5 | - opencilk 6 | 7 | ## Usage 8 | ``` 9 | make 10 | ./LSGraph -f filenames -bs 10000000 11 | 12 | ``` 13 | -------------------------------------------------------------------------------- /include/BC.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Map.cpp" 3 | #include 4 | #include "parallel_util.h" 5 | 6 | // This code is part of the project "Ligra: A Lightweight Graph Processing 7 | // Framework for Shared Memory", presented at Principles and Practice of 8 | // Parallel Programming, 2013. 9 | // Copyright (c) 2013 Julian Shun and Guy Blelloch 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a 12 | // copy of this software and associated documentation files (the 13 | // "Software"), to deal in the Software without restriction, including 14 | // without limitation the rights (to use, copy, modify, merge, publish, 15 | // distribute, sublicense, and/or sell copies of the Software, and to 16 | // permit persons to whom the Software is furnished to do so, subject to 17 | // the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included 20 | // in all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 23 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | #include 30 | 31 | typedef double fType; 32 | typedef uint32_t uintE; 33 | 34 | struct BC_F { 35 | fType* NumPaths; 36 | bool* Visited; 37 | BC_F(fType* _NumPaths, bool* _Visited) : 38 | NumPaths(_NumPaths), Visited(_Visited) {} 39 | inline bool update(uintE s, uintE d){ //Update function for forward phase 40 | fType oldV = NumPaths[d]; 41 | NumPaths[d] += NumPaths[s]; 42 | return oldV == 0.0; 43 | } 44 | inline bool updateAtomic (uintE s, uintE d) { //atomic Update, basically an add 45 | volatile fType oldV, newV; 46 | do { 47 | oldV = NumPaths[d]; newV = oldV + NumPaths[s]; 48 | } while(!CAS(&NumPaths[d],oldV,newV)); 49 | return oldV == 0.0; 50 | } 51 | inline bool cond (uintE d) { return Visited[d] == 0; } //check if visited 52 | }; 53 | 54 | struct BC_Back_F { 55 | fType* Dependencies; 56 | bool* Visited; 57 | BC_Back_F(fType* _Dependencies, bool* _Visited) : 58 | Dependencies(_Dependencies), Visited(_Visited) {} 59 | inline bool update(uintE s, uintE d){ //Update function for backwards phase 60 | fType oldV = Dependencies[d]; 61 | Dependencies[d] += Dependencies[s]; 62 | return oldV == 0.0; 63 | } 64 | inline bool updateAtomic (uintE s, uintE d) { //atomic Update 65 | volatile fType oldV, newV; 66 | do { 67 | oldV = Dependencies[d]; 68 | newV = oldV + Dependencies[s]; 69 | } while(!CAS(&Dependencies[d],oldV,newV)); 70 | return oldV == 0.0; 71 | } 72 | inline bool cond (uintE d) { return Visited[d] == 0; } //check if visited 73 | }; 74 | 75 | //vertex map function to mark visited vertexSubset 76 | struct BC_Vertex_F { 77 | bool* Visited; 78 | BC_Vertex_F(bool* _Visited) : Visited(_Visited) {} 79 | inline bool operator() (uintE i) { 80 | Visited[i] = 1; 81 | return 1; 82 | } 83 | }; 84 | 85 | //vertex map function (used on backwards phase) to mark visited vertexSubset 86 | //and add to Dependencies score 87 | struct BC_Back_Vertex_F { 88 | bool* Visited; 89 | fType* Dependencies, *inverseNumPaths; 90 | BC_Back_Vertex_F(bool* _Visited, fType* _Dependencies, fType* _inverseNumPaths) : 91 | Visited(_Visited), Dependencies(_Dependencies), inverseNumPaths(_inverseNumPaths) {} 92 | inline bool operator() (uintE i) { 93 | Visited[i] = 1; 94 | Dependencies[i] += inverseNumPaths[i]; 95 | return 1; } 96 | }; 97 | 98 | template 99 | auto BC(Graph& G, const uintE& start, [[maybe_unused]] bool use_dense_forward=false) { 100 | size_t n = G.get_num_vertices(); 101 | fType* NumPaths = newA(fType,n); 102 | {parallel_for(uint64_t i=0;i Levels; 109 | Levels.push_back(Frontier); 110 | long round = 0; 111 | while (Frontier.not_empty()) { 112 | //printf("%u, %u\n", round, Frontier.get_n()); 113 | round++; 114 | VertexSubset output = edgeMap(G, Frontier, BC_F(NumPaths,Visited)); 115 | Frontier = output; 116 | Levels.push_back(Frontier); 117 | vertexMap(Frontier, BC_Vertex_F(Visited), false); // mark visited 118 | } 119 | 120 | fType* Dependencies = newA(fType,n); 121 | {parallel_for(uint64_t i=0;i=0;r--) { 135 | edgeMap(G, Levels[r+1], BC_Back_F(Dependencies,Visited), false); 136 | Levels[r+1].del(); 137 | vertexMap(Levels[r], BC_Back_Vertex_F(Visited,Dependencies,NumPaths), false); 138 | } 139 | 140 | parallel_for(uint32_t i = 0; i < n; i++) { 141 | Dependencies[i] = (Dependencies[i]-NumPaths[i])/NumPaths[i]; 142 | } 143 | Levels[0].del(); 144 | free(NumPaths); 145 | free(Visited); 146 | 147 | #if VERIFY 148 | // write out to file 149 | std::ofstream myfile; 150 | myfile.open ("bc.out"); 151 | for (int i = 0; i < n; i++) { 152 | myfile << Dependencies[i] << "\n"; 153 | } 154 | #endif 155 | 156 | return Dependencies; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /include/BFS.h: -------------------------------------------------------------------------------- 1 | #include "Map.cpp" 2 | // This code is part of the project "Ligra: A Lightweight Graph Processing 3 | // Framework for Shared Memory", presented at Principles and Practice of 4 | // Parallel Programming, 2013. 5 | // Copyright (c) 2013 Julian Shun and Guy Blelloch 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights (to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to 12 | // permit persons to whom the Software is furnished to do so, subject to 13 | // the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | struct BFS_F { 28 | int32_t* Parents; 29 | BFS_F(int32_t* _Parents) : Parents(_Parents) {} 30 | inline bool update (uint32_t s, uint32_t d) { //Update 31 | if(Parents[d] == -1) { Parents[d] = s; return 1; } 32 | else return 0; 33 | } 34 | inline bool updateAtomic (uint32_t s, uint32_t d){ //atomic version of Update 35 | return __sync_bool_compare_and_swap(&Parents[d],-1,s); 36 | } 37 | //cond function checks if vertex has been visited yet 38 | inline bool cond (uint32_t d) { return (Parents[d] == -1); } 39 | }; 40 | 41 | template 42 | int32_t* BFS_with_edge_map(Graph &G, uint32_t src) { 43 | long start = src; 44 | long n = G.get_num_vertices(); 45 | //creates Parents array, initialized to all -1, except for start 46 | int32_t* Parents = (int32_t *) malloc(n * sizeof(uint32_t)); 47 | parallel_for(long i=0;i depths(n, UINT32_MAX); 63 | for (uint32_t j = 0; j < n; j++) { 64 | uint32_t current_depth = 0; 65 | int32_t current_parent = j; 66 | if (Parents[j] < 0) { 67 | continue; 68 | } 69 | while (current_parent != Parents[current_parent]) { 70 | current_depth += 1; 71 | current_parent = Parents[current_parent]; 72 | } 73 | depths[j] = current_depth; 74 | } 75 | 76 | // write out to file 77 | std::ofstream myfile; 78 | myfile.open ("bfs.out"); 79 | for (int i = 0; i < n; i++) { 80 | myfile << depths[i] << "\n"; 81 | } 82 | myfile.close(); 83 | #endif 84 | return Parents; 85 | } 86 | -------------------------------------------------------------------------------- /include/BitArray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // #include "helpers.h" 3 | #include "parallel.h" 4 | #include 5 | 6 | #define GET_NTH_BIT(x, n) ((x >> n) & 1U) 7 | #define SET_NTH_BIT(x, n) (x |= (1U << n)) 8 | #define FLIP_NTH_BIT(x, n) (x ^= (1U << n)) 9 | 10 | inline bool bit_array_get(uint32_t *array, uint64_t i) { 11 | return GET_NTH_BIT(array[i/32], i%32); 12 | } 13 | inline void bit_array_prefetch(uint32_t *array, uint64_t i) { 14 | __builtin_prefetch(&array[i/32]); 15 | } 16 | inline void bit_array_set(uint32_t *array, uint64_t i) { 17 | SET_NTH_BIT(array[i/32], i%32); 18 | } 19 | inline void bit_array_flip(uint32_t *array, uint64_t i) { 20 | FLIP_NTH_BIT(array[i/32], i%32); 21 | } 22 | 23 | inline uint64_t bit_array_size(uint64_t size) { 24 | if (size == 0) { 25 | return 0; 26 | } 27 | if (size < 32) { 28 | size = 32; 29 | } 30 | uint64_t n = size / 32; 31 | if (n*32 < size) { 32 | n += 1; 33 | } 34 | return n * 4; 35 | 36 | } 37 | uint64_t bit_array_count(uint32_t *array, uint64_t length) { 38 | uint64_t count = 0; 39 | for (uint64_t i = 0; i < length/32; i++) { 40 | count += __builtin_popcount(array[i]); 41 | } 42 | return count; 43 | } 44 | 45 | bool is_not_empty(uint32_t *array, uint64_t length) { 46 | //uint64_t count = 0; 47 | for (uint64_t i = 0; i < length/32; i++) { 48 | if (array[i] > 0) 49 | return true; 50 | } 51 | return false; 52 | } 53 | 54 | class BitArray { 55 | public: 56 | uint32_t *array; 57 | uint64_t length; 58 | bool to_free; 59 | BitArray(uint32_t *arr, uint64_t size) { 60 | array = arr; 61 | length = size; 62 | to_free = false; 63 | } 64 | BitArray(uint64_t size) { 65 | uint64_t n = bit_array_size(size); 66 | array = (uint32_t *) malloc(n); 67 | memset(array, 0, n); 68 | length = n*8; 69 | to_free = true; 70 | } 71 | BitArray(const BitArray &other) { 72 | length = other.length; 73 | array = (uint32_t *) malloc(length/8); 74 | to_free = true; 75 | //memcpy(array, other.array, length/8); 76 | parallel_for(uint64_t i = 0; i < length/32; i++) { 77 | array[i] = other.array[i]; 78 | } 79 | } 80 | 81 | ~BitArray() { 82 | if (to_free) { 83 | free(array); 84 | } 85 | } 86 | bool get(uint64_t i) { 87 | return bit_array_get(array, i); 88 | } 89 | void prefetch(uint64_t i) { 90 | return bit_array_prefetch(array, i); 91 | } 92 | void set(uint64_t i) { 93 | bit_array_set(array, i); 94 | } 95 | void flip(uint64_t i) { 96 | bit_array_flip(array, i); 97 | } 98 | uint64_t count() { 99 | return bit_array_count(array, length); 100 | } 101 | bool not_empty() { 102 | return is_not_empty(array, length); 103 | } 104 | template void map(F &f) { 105 | parallel_for_256 (uint64_t i = 0; i < length; i++) { 106 | if (get(i)) { 107 | f.update(i); 108 | } 109 | } 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /include/Components.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Ligra: A Lightweight Graph Processing 2 | // Framework for Shared Memory", presented at Principles and Practice of 3 | // Parallel Programming, 2013. 4 | // Copyright (c) 2013 Julian Shun and Guy Blelloch 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights (to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included 15 | // in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | #include "Map.cpp" 25 | #include "parallel_util.h" 26 | 27 | #define newA(__E,__n) (__E*) malloc((__n)*sizeof(__E)) 28 | 29 | struct CC_Shortcut { 30 | uint32_t* IDs, *prevIDs; 31 | CC_Shortcut( uint32_t* _IDs, uint32_t* _prevIDs) : 32 | IDs(_IDs), prevIDs(_prevIDs) {} 33 | inline bool operator () ( uint32_t i) { 34 | uint32_t l = IDs[IDs[i]]; 35 | if(IDs[i] != l) IDs[i] = l; 36 | if(prevIDs[i] != IDs[i]) { 37 | prevIDs[i] = IDs[i]; 38 | return 1; } 39 | else return 0; 40 | } 41 | }; 42 | struct CC_Vertex_F { 43 | uint32_t* IDs, *prevIDs; 44 | CC_Vertex_F(uint32_t* _IDs, uint32_t* _prevIDs) : 45 | IDs(_IDs), prevIDs(_prevIDs) {} 46 | inline bool operator () (uint32_t i) { 47 | prevIDs[i] = IDs[i]; 48 | return 1; }}; 49 | 50 | 51 | struct CC_F { 52 | uint32_t* IDs, *prevIDs; 53 | CC_F( uint32_t* _IDs, uint32_t* _prevIDs) : 54 | IDs(_IDs), prevIDs(_prevIDs) {} 55 | inline bool update( uint32_t s, uint32_t d){ //Update function writes min ID 56 | uint32_t origID = IDs[d]; 57 | if(IDs[s] < origID) { 58 | IDs[d] = IDs[s]; 59 | if(origID == prevIDs[d]) return 1; 60 | } return 0; } 61 | inline bool updateAtomic ( uint32_t s, uint32_t d) { //atomic Update 62 | uint32_t origID = IDs[d]; 63 | return (writeMin(&IDs[d],IDs[s]) && origID == prevIDs[d]); 64 | } 65 | inline bool cond ([[maybe_unused]] uint32_t d) { return true; } //does nothing 66 | }; 67 | 68 | /* 69 | uint32_t *CC_shortcut(Graph &G) { 70 | long n = G.get_num_vertices(); 71 | uint32_t* IDs = newA( uint32_t,n), *prevIDs = newA( uint32_t,n); 72 | //initialize unique IDs 73 | parallel_for(long i=0;inot_empty()){ //iterate until IDS converge 81 | edgeMap(G, *Active, CC_F(IDs,prevIDs), false); 82 | delete Active; 83 | Active = new VertexSubset(0,n,true, true); 84 | vertexMap(*Active,CC_Shortcut(IDs,prevIDs)); 85 | Active->move_next_to_current(); 86 | } 87 | delete Active; 88 | free(prevIDs); 89 | return IDs; 90 | } 91 | */ 92 | template 93 | uint32_t *CC(Graph &G) { 94 | long n = G.get_num_vertices(); 95 | uint32_t* IDs = newA( uint32_t,n), *prevIDs = newA( uint32_t,n); 96 | //initialize unique IDs 97 | parallel_for(long i=0;i components_set; 115 | for (uint32_t i = 0; i < n; i++) { 116 | components_set.insert(IDs[i]); 117 | } 118 | printf("number of components is %lu\n", components_set.size()); 119 | #endif 120 | 121 | return IDs; 122 | } 123 | -------------------------------------------------------------------------------- /include/Map.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "graph.h" 3 | #include "VertexSubset.cpp" 4 | 5 | using namespace graphstore; 6 | 7 | template 8 | struct EDGE_MAP_SPARSE { 9 | Graph &G; 10 | VertexSubset &output_vs; 11 | F f; 12 | bool output; 13 | EDGE_MAP_SPARSE(Graph &G_, VertexSubset &output_vs_, F f_, bool output_) : 14 | G(G_), output_vs(output_vs_), f(f_), output(output_) {} 15 | inline bool update(uint32_t val) { 16 | G.map_sparse(f, output_vs, val, output); 17 | return false; 18 | } 19 | }; 20 | 21 | template 22 | VertexSubset EdgeMapSparse(Graph &G, VertexSubset &vs, F f, bool output) { 23 | // printf("edge map sparse\n"); 24 | vs.convert_to_sparse(); 25 | VertexSubset output_vs = VertexSubset(vs, false); 26 | struct EDGE_MAP_SPARSE v(G, output_vs, f, output); 27 | vs.map(v); 28 | output_vs.finalize(); 29 | return output_vs; 30 | } 31 | 32 | template 33 | VertexSubset EdgeMapDense(Graph &G, VertexSubset &vs, F f, bool output) { 34 | // printf("edge map dense\n"); 35 | vs.convert_to_dense(); 36 | VertexSubset output_vs = VertexSubset(vs, false); 37 | // needs a grainsize of at least 512 38 | // so writes to the bitvector storing the next vertex set are going to different cache lines 39 | if (vs.all) { 40 | parallel_for(uint64_t i_ = 0; i_ < G.get_num_vertices(); i_+=512) { 41 | uint64_t end = std::min(i_+512, (uint64_t) G.get_num_vertices()); 42 | for (uint64_t i = i_; i < end; i++) { 43 | if (f.cond(i) == 1) { 44 | //printf("processing row %lu\n", i); 45 | G.map_dense_vs_all(f, vs, output_vs, i, output); 46 | } 47 | } 48 | } 49 | } else { 50 | parallel_for(uint64_t i_ = 0; i_ < G.get_num_vertices(); i_+=512) { 51 | uint64_t end = std::min(i_+512, (uint64_t) G.get_num_vertices()); 52 | for (uint64_t i = i_; i < end; i++) { 53 | //for(uint64_t i = 0; i < G.get_num_vertices(); i++) { 54 | if (f.cond(i) == 1) { 55 | //printf("processing row %lu\n", i); 56 | G.map_dense_vs_not_all(f, vs, output_vs, i, output); 57 | } 58 | } 59 | } 60 | } 61 | return output_vs; 62 | 63 | } 64 | 65 | template 66 | VertexSubset edgeMap(Graph &G, VertexSubset &vs, F f, bool output = true, uint32_t threshold = 20) { 67 | //vs.print(); 68 | //printf("%u, %u, %u\n", G.rows, threshold, vs.get_n()); 69 | if (G.get_num_vertices()/threshold <= vs.get_n()) { 70 | return EdgeMapDense(G, vs, f, output); 71 | } else { 72 | return EdgeMapSparse(G, vs, f, output); 73 | } 74 | } 75 | 76 | template 77 | struct VERTEX_MAP { 78 | VertexSubset &output_vs; 79 | F f; 80 | VERTEX_MAP(VertexSubset &output_vs_, F f_) : output_vs(output_vs_), f(f_) {} 81 | inline bool update(uint32_t val) { 82 | if constexpr (output) { 83 | if (f(val) == 1) { 84 | output_vs.insert(val); 85 | } 86 | } else { 87 | f(val); 88 | } 89 | return false; 90 | } 91 | }; 92 | 93 | template 94 | inline VertexSubset vertexMap(VertexSubset &vs, F f, bool output = true) { 95 | //TODO the compilier should have been able to do this itself 96 | if (output) { 97 | VertexSubset output_vs = VertexSubset(vs, false); 98 | struct VERTEX_MAP v(output_vs, f); 99 | vs.map(v); 100 | output_vs.finalize(); 101 | return output_vs; 102 | } else { 103 | VertexSubset null_vs = VertexSubset(); 104 | struct VERTEX_MAP v(null_vs, f); 105 | vs.map(v); 106 | return null_vs; 107 | } 108 | } 109 | 110 | template 111 | inline VertexSubset vertexFilter(VertexSubset &vs, F f) { 112 | vs.convert_to_dense(); 113 | auto n = vs.get_n(); 114 | bool* d_out = newA(bool, n); 115 | parallel_for(uint64_t i = 0; i < n; i++) { 116 | d_out[i] = 0; 117 | } 118 | parallel_for(uint64_t i = 0; i < n; i++) { 119 | if(vs.ba->get(i)) d_out[i] = f(i); 120 | } 121 | auto vs_out = VertexSubset(d_out, n); 122 | free(d_out); 123 | return vs_out; 124 | } 125 | -------------------------------------------------------------------------------- /include/Pagerank.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Ligra: A Lightweight Graph Processing 2 | // Framework for Shared Memory", presented at Principles and Practice of 3 | // Parallel Programming, 2013. 4 | // Copyright (c) 2013 Julian Shun and Guy Blelloch 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights (to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included 15 | // in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // #include "ligra.h" 25 | #include "math.h" 26 | #pragma once 27 | #include "Map.cpp" 28 | #define newA(__E,__n) (__E*) malloc((__n)*sizeof(__E)) 29 | 30 | // template 31 | template 32 | struct PR_F { 33 | T* p_curr, *p_next; 34 | // vertex* V; 35 | // PR_F(double* _p_curr, double* _p_next, vertex* _V) : 36 | Graph& G; 37 | PR_F(T* _p_curr, T* _p_next, Graph& _G) : 38 | p_curr(_p_curr), p_next(_p_next), G(_G) {} 39 | inline bool update(uint32_t s, uint32_t d){ //update function applies PageRank equation 40 | // p_next[d] += p_curr[s]/V[s].getOutDegree(); 41 | p_next[d] += p_curr[s]; 42 | 43 | return 1; 44 | } 45 | inline bool updateAtomic ([[maybe_unused]] uint32_t s, [[maybe_unused]] uint32_t d) { //atomic Update 46 | 47 | printf("should never be called for now since its always dense\n"); 48 | while(1) { } 49 | /* 50 | uint64_t x = 0; 51 | uint64_t *y = (uint64_t *) x; 52 | *y = 0; 53 | */ 54 | // exit(-1); 55 | //__sync_fetch_and_add(&p_next[d],p_curr[s]/G.lines[s].get_n()); 56 | return 1; 57 | } 58 | inline bool cond ([[maybe_unused]] uint32_t d) { return 1; }}; // from ligra readme: for cond which always ret true, ret cond_true// return cond_true(d); }}; 59 | 60 | template 61 | struct PR_Vertex { 62 | T *p_curr; 63 | Graph& G; 64 | PR_Vertex(T* _p_curr, Graph& _G) : p_curr(_p_curr), G(_G) {} 65 | inline bool operator () (uint32_t i) { 66 | p_curr[i] = p_curr[i] /G.degree(i);// G.num_neighbors(i);; // damping*p_next[i] + addedConstant; 67 | return 1; 68 | } 69 | }; 70 | 71 | /* 72 | //vertex map function to update its p value according to PageRank equation 73 | struct PR_Vertex_F { 74 | double damping; 75 | double addedConstant; 76 | double* p_curr; 77 | double* p_next; 78 | PR_Vertex_F(double* _p_curr, double* _p_next, double _damping, intE n) : 79 | p_curr(_p_curr), p_next(_p_next), 80 | damping(_damping), addedConstant((1-_damping)*(1/(double)n)){} 81 | inline bool operator () (uintE i) { 82 | p_next[i] = p_next[i]; // damping*p_next[i] + addedConstant; 83 | return 1; 84 | } 85 | }; 86 | */ 87 | 88 | //resets p 89 | template 90 | struct PR_Vertex_Reset { 91 | T* p; 92 | PR_Vertex_Reset(T* _p) : 93 | p(_p) {} 94 | inline bool operator () (uint32_t i) { 95 | p[i] = 0.0; 96 | return 1; 97 | } 98 | }; 99 | /* 100 | template 101 | OT plusReduce(OT* A, intT n) { 102 | return reduce((intT)0,n,addF(),getA(A)); 103 | } 104 | */ 105 | template 106 | T* PR_S(Graph& G, long maxIters) { 107 | size_t n = G.get_num_vertices(); 108 | //const double damping = 0.85, epsilon = 0.0000001; 109 | 110 | //timer ss; ss.start(); 111 | //auto vtxs = G.fetch_all_vertices(); 112 | //ss.stop(); ss.reportTotal("snapshot time"); 113 | 114 | /* 115 | uint64_t test_edges = 0; 116 | ofstream ofile0; 117 | ofile0.open("degs_s.txt"); 118 | for(uint32_t i = 0; i < n; i++) { 119 | // printf("degree of %u = %u\n", i, vtxs[i].degree()); 120 | // ofile0 << vtxs[i].degree() << "\n"; 121 | test_edges += vtxs[i].degree(); 122 | } 123 | 124 | assert(test_edges == G.num_edges()); 125 | ofile0.close(); 126 | */ 127 | 128 | T one_over_n = 1/(double)n; 129 | T* p_curr = (T*) memalign(32, n*sizeof(T)); 130 | T* p_next = (T*) memalign(32, n*sizeof(T)); 131 | parallel_for(size_t i = 0; i < n; i++) { 132 | p_curr[i] = one_over_n; 133 | } 134 | VertexSubset Frontier = VertexSubset(0, n, true); 135 | // VertexSubset Frontier = VertexSubset((uint32_t)0, n); 136 | 137 | long iter = 0; 138 | printf("max iters %lu\n", maxIters); 139 | while(iter++ < maxIters) { 140 | //printf("\t running iteration %lu\n", iter); 141 | // using flat snapshot 142 | vertexMap(Frontier,PR_Vertex(p_curr, G), false); 143 | vertexMap(Frontier,PR_Vertex_Reset(p_next), false); 144 | edgeMap(G, Frontier,PR_F(p_curr,p_next,G), false); 145 | 146 | //vertexMap(Frontier,PR_Vertex_F(p_curr,p_next,damping,n)); 147 | //compute L1-norm between p_curr and p_next 148 | /* 149 | {parallel_for(long i=0;i(p_curr, n); 157 | // double L1_norm = pbbs::reduce(temp, pbbs::addm()); 158 | // p_curr = temp.to_array(); 159 | // if(L1_norm < epsilon) break; 160 | 161 | // reset p_curr 162 | std::swap(p_curr,p_next); 163 | } 164 | 165 | #if VERIFY 166 | std::ofstream ofile; 167 | ofile.open("p_curr_s.out"); 168 | for(uint32_t i = 0; i < n; i++) { 169 | ofile << p_curr[i] << "\n"; 170 | } 171 | ofile.close(); 172 | 173 | std::ofstream ofile2; 174 | ofile2.open("p_next_s.out"); 175 | for(uint32_t i = 0; i < n; i++) { 176 | ofile2 << p_next[i] << "\n"; 177 | } 178 | ofile2.close(); 179 | #endif 180 | free(p_next); 181 | 182 | // printf("p curr %p, p next %p\n", p_curr, p_next); 183 | return p_curr; 184 | } 185 | -------------------------------------------------------------------------------- /include/TC.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Ligra: A Lightweight Graph Processing 2 | // Framework for Shared Memory", presented at Principles and Practice of 3 | // Parallel Programming, 2013. 4 | // Copyright (c) 2013 Julian Shun and Guy Blelloch 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights (to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included 15 | // in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | // Triangle counting code (assumes a symmetric graph, so pass the "-s" 26 | // flag). This is not optimized (no ordering heuristic is used)--for 27 | // optimized code, see "Multicore Triangle Computations Without 28 | // Tuning", ICDE 2015. Currently only works with uncompressed graphs, 29 | // and not with compressed graphs. 30 | #include "Map.cpp" 31 | 32 | //assumes sorted neighbor lists 33 | template 34 | long countCommon(Graph &G, uint32_t a, uint32_t b, std::vector>& mp) { 35 | long ans=0; 36 | auto& nei_a = mp[a]; 37 | auto& nei_b = mp[b]; 38 | uint32_t i = 0, j = 0, a_v = nei_a[0], b_v = nei_b[0], size_a = nei_a.size(), size_b = nei_b.size(); 39 | while (i < size_a && j < size_b && a_v < a && b_v < b) { //count "directed" triangles 40 | if (a_v == b_v) { 41 | ++i;; 42 | ++j; 43 | a_v = nei_a[i]; 44 | b_v = nei_b[j]; 45 | ans++; 46 | } 47 | else if (a_v < b_v){ 48 | ++i; 49 | a_v = nei_a[i]; 50 | } 51 | else{ 52 | ++j; 53 | b_v = nei_b[j]; 54 | } 55 | } 56 | return ans; 57 | } 58 | 59 | template 60 | struct countF { //for edgeMap 61 | Graph &G; 62 | std::vector &counts; 63 | std::vector>& mp; 64 | countF(Graph &G_, std::vector &_counts,std::vector>& _mp) : G(G_), counts(_counts), mp(_mp) {} 65 | inline bool update (uint32_t s, uint32_t d) { 66 | if(s > d) {//only count "directed" triangles 67 | counts[8*getWorkerNum()] += countCommon(G,s,d,mp); 68 | //counts[8*getWorkerNum()] += G.count_common(s,d); 69 | } 70 | return 1; 71 | } 72 | inline bool updateAtomic (uint32_t s, uint32_t d) { 73 | if (s > d) { //only count "directed" triangles 74 | counts[8*getWorkerNum()] += countCommon(G,s,d,mp); 75 | //counts[8*getWorkerNum()] += G.count_common(s,d); 76 | } 77 | return 1; 78 | } 79 | inline bool cond ([[maybe_unused]] uint32_t d) { return true; } //does nothing 80 | }; 81 | 82 | // void TC(Graph& G){ 83 | template 84 | uint64_t TC(Graph& G, std::vector>&mp) { 85 | uint32_t n = G.get_num_vertices(); 86 | std::vector counts(getWorkers()*8, 0); 87 | VertexSubset Frontier(0,n,true); //frontier contains all vertices 88 | 89 | edgeMap(G,Frontier,countF(G,counts,mp), false); 90 | uint64_t count = 0; 91 | for (int i = 0; i < getWorkers(); i++) { 92 | count += counts[i*8]; 93 | } 94 | return count; 95 | #if VERIFY 96 | printf("triangle count = %ld\n",count); 97 | #endif 98 | } 99 | -------------------------------------------------------------------------------- /include/VertexSubset.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BitArray.h" 3 | #include "sliding_queue.h" 4 | 5 | class VertexSubset { 6 | public: 7 | bool all; 8 | bool is_sparse; 9 | uint64_t max_el; 10 | BitArray *ba = NULL; 11 | SlidingQueue *queue = NULL; 12 | QueueBuffer *queue_array = NULL; 13 | 14 | bool has(uint64_t i) { 15 | if (all) { 16 | return true; 17 | } 18 | if (is_sparse) { 19 | printf("shouldn't be calling has, is currently sparse\n"); 20 | exit(-1); 21 | return false; 22 | } else { 23 | return ba->get(i); 24 | } 25 | } 26 | bool has_dense_no_all(uint64_t i) { return ba->get(i); } 27 | void has_dense_no_all_prefetch(uint64_t i) { return ba->prefetch(i); } 28 | 29 | uint64_t get_n() { 30 | // printf("get_n: is_sparse = %d, remove_duplicates = %d\n", is_sparse, 31 | // remove_duplicates); 32 | if (all) { 33 | return max_el; 34 | } else if (is_sparse) { 35 | return queue->size(); 36 | } else { 37 | // printf("count = %lu\n", curr_ba->count()); 38 | return ba->count(); 39 | } 40 | } 41 | bool not_empty() { 42 | if (all) return true; 43 | else if (is_sparse) return queue->size() > 0; 44 | else return ba->not_empty(); 45 | } 46 | void print() { 47 | printf("is_sparse = %d\n", is_sparse); 48 | if (all) { 49 | printf("{0,...,%lu}\n", max_el); 50 | } else if (is_sparse) { 51 | const uint32_t start = queue->shared_out_start; 52 | const uint32_t end = queue->shared_out_end; 53 | printf("{"); 54 | for (uint32_t i = start; i < end; i++) { 55 | printf("%d, ", queue->shared[i]); 56 | } 57 | printf("}\n"); 58 | } else { 59 | printf("{"); 60 | for (uint32_t i = 0; i < max_el; i++) { 61 | if (ba->get(i)) { 62 | printf("%d, ", i); 63 | } 64 | } 65 | printf("}\n"); 66 | } 67 | } 68 | void insert(uint64_t i) { 69 | if (is_sparse) { 70 | queue_array[4 * getWorkerNum()].push_back(i); 71 | return; 72 | } else { 73 | return ba->set(i); 74 | } 75 | } 76 | void insert_dense(uint64_t i) { 77 | return ba->set(i); 78 | } 79 | void insert_sparse(uint64_t i) { 80 | queue_array[4 * getWorkerNum()].push_back(i); 81 | return; 82 | } 83 | template void map(F &f) { 84 | if (all) { 85 | parallel_for(uint64_t i = 0; i < max_el; i++) { f.update(i); } 86 | return; 87 | } 88 | if (is_sparse) { 89 | const uint32_t start = queue->shared_out_start; 90 | const uint32_t end = queue->shared_out_end; 91 | parallel_for(uint32_t i = start; i < end; i++) { 92 | f.update(queue->shared[i]); 93 | } 94 | } else { 95 | return ba->map(f); 96 | } 97 | } 98 | template void map_sparse(F &f) { 99 | //printf("queue in map = %p\n", queue); 100 | const uint32_t start = queue->shared_out_start; 101 | const uint32_t end = queue->shared_out_end; 102 | parallel_for(uint32_t i = start; i < end; i++) { 103 | f.update(queue->shared[i]); 104 | } 105 | } 106 | // used to retunr empty vertexsubsets when we have no output 107 | VertexSubset() {} 108 | 109 | VertexSubset(el_t e, uint64_t max_el_, bool all_ = false) 110 | : all(all_), is_sparse(true), max_el(max_el_) { 111 | if (all) { 112 | is_sparse = false; 113 | return; 114 | } 115 | queue = new SlidingQueue(max_el); 116 | queue->push_back(e); 117 | queue->slide_window(); 118 | } 119 | VertexSubset(bool *els, uint64_t len) 120 | : all(false), is_sparse(false), max_el(len) { 121 | ba = new BitArray(max_el); 122 | parallel_for_256(uint64_t i = 0; i < max_el; i++) { 123 | if (els[i]) { 124 | ba->set(i); 125 | } 126 | } 127 | } 128 | 129 | VertexSubset(const VertexSubset &other) 130 | : all(other.all), is_sparse(other.is_sparse), max_el(other.max_el), ba(other.ba), queue(other.queue) { 131 | //printf("queue = %p\n", queue); 132 | } 133 | VertexSubset& operator=(const VertexSubset& other) { 134 | all = other.all; 135 | is_sparse = other.is_sparse; 136 | max_el = other.max_el; 137 | ba = other.ba; 138 | queue = other.queue; 139 | queue_array = NULL; 140 | return *this; 141 | } 142 | 143 | // can't add anything to these once they have been copied, 144 | // just for keeping state like pushing past frontiers into a vector 145 | VertexSubset(const VertexSubset &other, bool copy_data) 146 | : all(other.all), is_sparse(other.is_sparse), max_el(other.max_el) { 147 | if (copy_data) { 148 | if (all) { 149 | return; 150 | } 151 | ba = NULL; 152 | queue = NULL; 153 | queue_array = NULL; 154 | if (is_sparse) { 155 | if (other.queue) { 156 | queue = new SlidingQueue(*other.queue, max_el); 157 | } 158 | if (other.queue_array) { 159 | queue_array = (QueueBuffer *)malloc( 160 | 4 * sizeof(QueueBuffer) * getWorkers()); 161 | for (int i = 0; i < getWorkers(); i++) { 162 | new (&queue_array[i * 4]) 163 | QueueBuffer(*queue, other.queue_array[i * 4].local_size); 164 | queue_array[i * 4].in = other.queue_array[i * 4].in; 165 | memcpy(queue_array[i * 4].local_queue, 166 | other.queue_array[i * 4].local_queue, 167 | queue_array[i * 4].in * sizeof(int32_t)); 168 | } 169 | } 170 | 171 | } else { 172 | if (other.ba) { 173 | ba = new BitArray(*other.ba); 174 | } 175 | } 176 | } else { // just create something similar where we will push the next set of data into 177 | // sparse and dense stay they way they are, will be changed by something else 178 | // all turns to dense, if we knew it was going to stay as all we would have no output and not use a new vertexsubset anyway 179 | if (is_sparse) { 180 | queue = new SlidingQueue(max_el); 181 | queue_array = (QueueBuffer *)malloc( 182 | 4 * sizeof(QueueBuffer) * getWorkers()); 183 | for (int i = 0; i < getWorkers(); i++) { 184 | new (&queue_array[i * 4]) QueueBuffer(*queue); 185 | } 186 | } else { 187 | all = false; 188 | ba = new BitArray(max_el); 189 | } 190 | } 191 | } 192 | void finalize() { 193 | if (is_sparse) { 194 | //queue->reset(); 195 | parallel_for_1(int i = 0; i < getWorkers(); i++) { 196 | queue_array[i * 4].flush(); 197 | queue_array[i * 4].~QueueBuffer(); 198 | } 199 | queue->slide_window(); 200 | free(queue_array); 201 | } 202 | } 203 | void del() { 204 | if (ba != NULL) { 205 | delete ba; 206 | } 207 | if (queue != NULL) { 208 | delete queue; 209 | //printf("deleteing queue %p\n", queue); 210 | queue = NULL; 211 | } 212 | } 213 | 214 | void convert_to_dense() { 215 | if (all || !is_sparse) { 216 | return; 217 | } 218 | // printf("converting sparse to dense\n"); 219 | is_sparse = false; 220 | ba = new BitArray(max_el); 221 | // need an atomic setter to work in parallel 222 | for (uint32_t i = queue->shared_out_start; i < queue->shared_out_end; i++) { 223 | ba->set(queue->shared[i]); 224 | } 225 | } 226 | void convert_to_sparse() { 227 | if (all || is_sparse) { 228 | return; 229 | } 230 | // printf("converting dense to sparse\n"); 231 | is_sparse = true; 232 | queue = new SlidingQueue(max_el); 233 | queue_array = (QueueBuffer *)malloc( 234 | 4 * sizeof(QueueBuffer) * getWorkers()); 235 | for (int i = 0; i < getWorkers(); i++) { 236 | new (&queue_array[i * 4]) QueueBuffer(*queue); 237 | } 238 | parallel_for(uint32_t i = 0; i < max_el; i++) { 239 | if (ba->get(i)) { 240 | queue_array[4 * getWorkerNum()].push_back(i); 241 | } 242 | } 243 | parallel_for(int i = 0; i < getWorkers(); i++) { 244 | queue_array[i * 4].flush(); 245 | } 246 | queue->slide_window(); 247 | } 248 | }; 249 | -------------------------------------------------------------------------------- /include/graph.h: -------------------------------------------------------------------------------- 1 | #ifndef _GRAPH_H_ 2 | #define _GRAPH_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | // #include "PMA.hpp" 14 | 15 | namespace graphstore { 16 | 17 | #define PREFETCH 1 18 | 19 | #if WEIGHTED 20 | #define NUM_IN_PLACE_NEIGHBORS 14 21 | #else 22 | #define NUM_IN_PLACE_NEIGHBORS 13 23 | #endif 24 | // #define MEDIUM_DEGREE (1ULL << 30) 25 | #define MEDIUM_DEGREE (1ULL << 10) 26 | 27 | #define LOCK_MASK (1ULL << 31) 28 | #define UNLOCK_MASK ~(1ULL << 31) 29 | 30 | // A 0 bit means the lock is free 31 | // A 1 bit means the lock is currently acquired 32 | static inline void lock(uint32_t *data) 33 | { 34 | while ((__sync_fetch_and_or(data, LOCK_MASK) & (1ULL << 31)) != 0) {} 35 | } 36 | 37 | static inline void unlock(uint32_t *data) 38 | { 39 | __sync_fetch_and_and(data, UNLOCK_MASK); 40 | } 41 | 42 | } 43 | #endif -------------------------------------------------------------------------------- /include/helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H 2 | #define HELPERS_H 3 | 4 | #if OPENMP == 1 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef debug 19 | #define dprintf(fmt, args...) fprintf(stderr, fmt, ##args) 20 | #else 21 | #define dprintf(fmt, args...) /* Don't do anything in release builds */ 22 | #endif 23 | 24 | #if OPENMP == 1 25 | #define par_for _Pragma("omp parallel for") for 26 | #else 27 | #define par_for cilk_for 28 | #endif 29 | 30 | 31 | 32 | 33 | typedef uint32_t el_t; 34 | 35 | /* 36 | // find index of first 1-bit (least significant bit) 37 | static inline int bsf_word(int word) { 38 | int result; 39 | __asm__ volatile("bsf %1, %0" : "=r"(result) : "r"(word)); 40 | return result; 41 | } 42 | 43 | static inline int bsr_word(int word) { 44 | int result; 45 | __asm__ volatile("bsr %1, %0" : "=r"(result) : "r"(word)); 46 | return result; 47 | } 48 | */ 49 | /* 50 | static int get_worker_number() { 51 | #if OPENMP == 1 52 | return omp_get_thread_num(); 53 | #else 54 | return __cilkrts_get_worker_number(); 55 | #endif 56 | } 57 | static int get_nworkers() { 58 | #if OPENMP == 1 59 | return omp_get_max_threads(); 60 | #else 61 | return __cilkrts_get_nworkers(); 62 | #endif 63 | } 64 | */ 65 | /* 66 | typedef struct _pair_int { 67 | uint32_t x; // length in array 68 | uint32_t y; // depth 69 | } pair_int; 70 | */ 71 | 72 | typedef struct _pair_els { 73 | el_t x; 74 | el_t y; 75 | } pair_els; 76 | 77 | /* 78 | typedef struct _pair_double { 79 | double x; 80 | double y; 81 | } pair_double; 82 | */ 83 | 84 | // int isPowerOfTwo(int x) { return ((x != 0) && !(x & (x - 1))); } 85 | 86 | // same as find_leaf, but does it for any level in the tree 87 | // index: index in array 88 | // len: length of sub-level. 89 | // int find_node(int index, int len) { return (index / len) * len; } 90 | 91 | inline 92 | double hsum_double_avx(__m256d v) { 93 | __m128d vlow = _mm256_castpd256_pd128(v); 94 | __m128d vhigh = _mm256_extractf128_pd(v, 1); // high 128 95 | vlow = _mm_add_pd(vlow, vhigh); // reduce down to 128 96 | 97 | __m128d high64 = _mm_unpackhi_pd(vlow, vlow); 98 | return _mm_cvtsd_f64(_mm_add_sd(vlow, high64)); // reduce to scalar 99 | } 100 | 101 | inline float horizontal_add (__m256 a) { 102 | __m256 t1 = _mm256_hadd_ps(a,a); 103 | __m256 t2 = _mm256_hadd_ps(t1,t1); 104 | __m128 t3 = _mm256_extractf128_ps(t2,1); 105 | __m128 t4 = _mm_add_ss(_mm256_castps256_ps128(t2),t3); 106 | return _mm_cvtss_f32(t4); 107 | } 108 | 109 | inline __m128i load_4_32(void * mem_addr) { 110 | return _mm_lddqu_si128((__m128i *)mem_addr); 111 | } 112 | /* 113 | inline __m128i load_4_24(void * mem_addr) { 114 | __m128i data = _mm_lddqu_si128((__m128i *)mem_addr); 115 | __mmask16 mask = 0x777; 116 | __m128i idx = _mm_setr_epi8(3,15,14,13, 2,12,11,10, 1,9,8,7, 0,6,5,4); 117 | return _mm_maskz_permutexvar_epi8(mask, idx, data); 118 | } 119 | */ 120 | inline __m128i load_4_16(void * mem_addr) { 121 | return _mm_lddqu_si128((__m128i *)mem_addr); 122 | } 123 | 124 | /* 125 | static long get_usecs() { 126 | struct timeval st; 127 | gettimeofday(&st,NULL); 128 | return st.tv_sec*1000000 + st.tv_usec; 129 | } 130 | static double get_static_upper_density_bound(uint32_t depth, uint32_t real_logN) { 131 | double upper; 132 | upper = 3.0 / 4.0 + ((.25 * depth) / bsr_word((1U << real_logN)/(1 << (bsr_word(real_logN))))); 133 | if (upper > ((float) (1 << bsr_word(real_logN)) - 1)/(1 << bsr_word(real_logN))) { 134 | upper = (((float) (1 << bsr_word(real_logN)) - 1)/(1 << bsr_word(real_logN)))+.001; 135 | } 136 | return upper; 137 | } 138 | */ 139 | inline void segfault() { 140 | uint64_t x = 0; 141 | uint64_t *y = (uint64_t *) x; 142 | *y = 0; 143 | } 144 | 145 | /* 146 | #include "table.h" 147 | double *upper_density_bound_table = (double *) table; 148 | #define GET_UPPER_DENSITY_BOUND(depth, real_logN) upper_density_bound_table[depth*32+real_logN] 149 | 150 | / I'm assuming that this is between 0 and 2^31 151 | uint64_t get_worker_num() { 152 | return __cilkrts_get_worker_number() + 1; 153 | // return 0; 154 | } 155 | */ 156 | #endif 157 | -------------------------------------------------------------------------------- /include/parallel.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARALLEL_H 2 | #define _PARALLEL_H 3 | 4 | // intel cilk+ 5 | #if CILK == 1 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #define parallel_for cilk_for 14 | #define parallel_main main 15 | #define parallel_for_1 _Pragma("cilk grainsize = 1") parallel_for 16 | #define parallel_for_2 _Pragma("cilk grainsize = 2") parallel_for 17 | #define parallel_for_4 _Pragma("cilk grainsize = 4") parallel_for 18 | #define parallel_for_8 _Pragma("cilk grainsize = 8") parallel_for 19 | #define parallel_for_16 _Pragma("cilk grainsize = 16") parallel_for 20 | #define parallel_for_32 _Pragma("cilk grainsize = 32") parallel_for 21 | #define parallel_for_64 _Pragma("cilk grainsize = 64") parallel_for 22 | #define parallel_for_128 _Pragma("cilk grainsize = 128") parallel_for 23 | #define parallel_for_256 _Pragma("cilk grainsize = 256") parallel_for 24 | #define parallel_for_512 _Pragma("cilk grainsize = 512") parallel_for 25 | #define parallel_for_1024 _Pragma("cilk grainsize = 1024") parallel_for 26 | 27 | [[maybe_unused]] static int getWorkers() { 28 | return __cilkrts_get_nworkers(); 29 | } 30 | 31 | [[maybe_unused]] static int getWorkerNum() { 32 | return __cilkrts_get_worker_number(); 33 | } 34 | 35 | // openmp 36 | #elif OPENMP == 1 37 | #include 38 | #define cilk_spawn 39 | #define cilk_sync 40 | #define parallel_main main 41 | #define parallel_for _Pragma("omp parallel for") for 42 | #define parallel_for_1 _Pragma("omp parallel for schedule (static,1)") for 43 | #define parallel_for_256 _Pragma("omp parallel for schedule (static,256)") for 44 | 45 | [[maybe_unused]] static int getWorkers() { return omp_get_max_threads(); } 46 | [[maybe_unused]] static void setWorkers(int n) { omp_set_num_threads(n); } 47 | [[maybe_unused]] static int getWorkerNum() { 48 | return omp_get_thread_num(); 49 | } 50 | 51 | // c++ 52 | #else 53 | #define cilk_spawn 54 | #define cilk_sync 55 | #define parallel_main main 56 | #define parallel_for for 57 | #define parallel_for_1 for 58 | #define parallel_for_256 for 59 | #define cilk_for for 60 | 61 | [[maybe_unused]] static int getWorkers() { return 1; } 62 | [[maybe_unused]] static void setWorkers([[maybe_unused]] int n) { } 63 | [[maybe_unused]] static int getWorkerNum() { return 0; } 64 | 65 | #endif 66 | 67 | #include 68 | 69 | #if defined(LONG) 70 | typedef long intT; 71 | typedef unsigned long uintT; 72 | #define INT_T_MAX LONG_MAX 73 | #define UINT_T_MAX ULONG_MAX 74 | #else 75 | typedef int intT; 76 | typedef unsigned int uintT; 77 | #define INT_T_MAX INT_MAX 78 | #define UINT_T_MAX UINT_MAX 79 | #endif 80 | 81 | #endif // _PARALLEL_H 82 | -------------------------------------------------------------------------------- /include/parallel_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | inline bool CAS(ET *ptr, ET oldv, ET newv) { 5 | if constexpr (sizeof(ET) == 1) { 6 | return __sync_bool_compare_and_swap((bool*)ptr, *((bool*)&oldv), *((bool*)&newv)); 7 | } else if constexpr (sizeof(ET) == 4) { 8 | return __sync_bool_compare_and_swap((int*)ptr, *((int*)&oldv), *((int*)&newv)); 9 | } else if constexpr (sizeof(ET) == 8) { 10 | return __sync_bool_compare_and_swap((long*)ptr, *((long*)&oldv), *((long*)&newv)); 11 | } 12 | else { 13 | std::cout << "CAS bad length : " << sizeof(ET) << std::endl; 14 | abort(); 15 | } 16 | } 17 | 18 | template 19 | inline bool writeMin(ET *a, ET b) { 20 | ET c; bool r=0; 21 | do c = *a; 22 | while (c > b && !(r=CAS(a,c,b))); 23 | return r; 24 | } 25 | -------------------------------------------------------------------------------- /include/parse_command_line.h: -------------------------------------------------------------------------------- 1 | // This code is part of the Problem Based Benchmark Suite (PBBS) 2 | // Copyright (c) 2011 Guy Blelloch and the PBBS team 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights (to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to 9 | // permit persons to whom the Software is furnished to do so, subject to 10 | // the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included 13 | // in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | struct commandLine { 31 | int argc; 32 | char** argv; 33 | std::string comLine; 34 | commandLine(int _c, char** _v, std::string _cl) 35 | : argc(_c), argv(_v), comLine(_cl) { 36 | if (getOption("-h") || getOption("-help")) 37 | badArgument(); 38 | } 39 | 40 | commandLine(int _c, char** _v) 41 | : argc(_c), argv(_v), comLine("bad arguments") { } 42 | 43 | void badArgument() { 44 | std::cout << "usage: " << argv[0] << " " << comLine << std::endl; 45 | exit(0); 46 | } 47 | 48 | // get an argument 49 | // i is indexed from the last argument = 0, second to last indexed 1, .. 50 | char* getArgument(int i) { 51 | if (argc < 2+i) badArgument(); 52 | return argv[argc-1-i]; 53 | } 54 | 55 | // looks for two filenames 56 | std::pair IOFileNames() { 57 | if (argc < 3) badArgument(); 58 | return std::pair(argv[argc-2],argv[argc-1]); 59 | } 60 | 61 | std::pair sizeAndFileName() { 62 | if (argc < 3) badArgument(); 63 | return std::pair(std::atoi(argv[argc-2]),(char*) argv[argc-1]); 64 | } 65 | 66 | bool getOption(std::string option) { 67 | for (int i = 1; i < argc; i++) 68 | if ((std::string) argv[i] == option) return true; 69 | return false; 70 | } 71 | 72 | char* getOptionValue(std::string option) { 73 | for (int i = 1; i < argc-1; i++) 74 | if ((std::string) argv[i] == option) return argv[i+1]; 75 | return NULL; 76 | } 77 | 78 | std::string getOptionValue(std::string option, std::string defaultValue) { 79 | for (int i = 1; i < argc-1; i++) 80 | if ((std::string) argv[i] == option) return (std::string) argv[i+1]; 81 | return defaultValue; 82 | } 83 | 84 | long getOptionLongValue(std::string option, long defaultValue) { 85 | for (int i = 1; i < argc-1; i++) 86 | if ((std::string) argv[i] == option) { 87 | long r = atol(argv[i+1]); 88 | if (r < 0) badArgument(); 89 | return r; 90 | } 91 | return defaultValue; 92 | } 93 | 94 | int getOptionIntValue(std::string option, int defaultValue) { 95 | for (int i = 1; i < argc-1; i++) 96 | if ((std::string) argv[i] == option) { 97 | int r = atoi(argv[i+1]); 98 | if (r < 0) badArgument(); 99 | return r; 100 | } 101 | return defaultValue; 102 | } 103 | 104 | double getOptionDoubleValue(std::string option, double defaultValue) { 105 | for (int i = 1; i < argc-1; i++) 106 | if ((std::string) argv[i] == option) { 107 | double val; 108 | if (sscanf(argv[i+1], "%lf", &val) == EOF) { 109 | badArgument(); 110 | } 111 | return val; 112 | } 113 | return defaultValue; 114 | } 115 | 116 | }; 117 | 118 | -------------------------------------------------------------------------------- /include/rmat_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This code is part of the Problem Based Benchmark Suite (PBBS) 4 | // Copyright (c) 2016 Guy Blelloch, Daniel Ferizovic, and the PBBS team 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a 7 | // copy of this software and associated documentation files (the 8 | // "Software"), to deal in the Software without restriction, including 9 | // without limitation the rights (to use, copy, modify, merge, publish, 10 | // distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to 12 | // the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included 15 | // in all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | 27 | #include 28 | // from numerical recipes 29 | inline uint64_t hash64(uint64_t u ) 30 | { 31 | uint64_t v = u * 3935559000370003845ul + 2691343689449507681ul; 32 | v ^= v >> 21; 33 | v ^= v << 37; 34 | v ^= v >> 4; 35 | v *= 4768777513237032717ul; 36 | v ^= v << 20; 37 | v ^= v >> 41; 38 | v ^= v << 5; 39 | return v; 40 | } 41 | 42 | 43 | // a 32-bit hash function 44 | inline uint32_t hash32(uint32_t a) { 45 | a = (a+0x7ed55d16) + (a<<12); 46 | a = (a^0xc761c23c) ^ (a>>19); 47 | a = (a+0x165667b1) + (a<<5); 48 | a = (a+0xd3a2646c) ^ (a<<9); 49 | a = (a+0xfd7046c5) + (a<<3); 50 | a = (a^0xb55a4f09) ^ (a>>16); 51 | return a; 52 | } 53 | 54 | 55 | // A cheap version of an inteface that should be improved 56 | // Allows forking a state into multiple states 57 | struct random_aspen { 58 | public: 59 | random_aspen(size_t seed) : state(seed) {}; 60 | random_aspen() : state(0) {}; 61 | random_aspen fork(uint64_t i) const { 62 | return random_aspen(hash64(hash64(i+state))); } 63 | random_aspen next() const { return fork(0);} 64 | size_t ith_rand(uint64_t i) const { 65 | return hash64(i+state);} 66 | size_t operator[] (size_t i) const {return ith_rand(i);} 67 | size_t rand() { return ith_rand(0);} 68 | private: 69 | uint64_t state = 0; 70 | }; 71 | 72 | // returns the log base 2 rounded up (works on ints or longs or unsigned versions) 73 | template 74 | size_t log2_up(T i) { 75 | size_t a=0; 76 | T b=i-1; 77 | while (b > 0) {b = b >> 1; a++;} 78 | return a; 79 | } 80 | 81 | template 82 | struct rMat { 83 | double a, ab, abc; 84 | intT n; 85 | intT h; 86 | rMat(intT _n, intT _seed, 87 | double _a, double _b, double _c) { 88 | n = _n; a = _a; ab = _a + _b; abc = _a+_b+_c; 89 | h = hash32((intT)_seed); 90 | if(abc > 1) { std::cout << "in rMat: a + b + c add to more than 1\n"; abort();} 91 | if((1UL << log2_up(n)) != n) { std::cout << "in rMat: n not a power of 2"; abort(); } 92 | } 93 | 94 | 95 | double hashDouble(intT i) { 96 | return ((double) (hash32((intT)i))/((double) std::numeric_limits::max()));} 97 | 98 | std::pair rMatRec(intT nn, intT randStart, intT randStride) { 99 | if (nn==1) return std::make_pair(0,0); 100 | else { 101 | std::pair x = rMatRec(nn/2, randStart + randStride, randStride); 102 | double r = hashDouble(randStart); 103 | if (r < a) return x; 104 | else if (r < ab) return std::make_pair(x.first,x.second+nn/2); 105 | else if (r < abc) return std::make_pair(x.first+nn/2, x.second); 106 | else return std::make_pair(x.first+nn/2, x.second+nn/2); 107 | } 108 | } 109 | 110 | std::pair operator() (intT i) { 111 | intT randStart = hash32((intT)(2*i)*h); 112 | intT randStride = hash32((intT)(2*i+1)*h); 113 | return rMatRec(n, randStart, randStride); 114 | } 115 | }; 116 | 117 | -------------------------------------------------------------------------------- /include/sliding_queue.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, The Regents of the University of California (Regents) 2 | // See LICENSE.txt for license details 3 | 4 | #ifndef SLIDING_QUEUE_H_ 5 | #define SLIDING_QUEUE_H_ 6 | 7 | #include 8 | #include 9 | // #include "platform_atomics.h" 10 | 11 | 12 | /* 13 | GAP Benchmark Suite 14 | Class: SlidingQueue 15 | Author: Scott Beamer 16 | 17 | Double-buffered queue so appends aren't seen until SlideWindow() called 18 | - Use QueueBuffer when used in parallel to avoid false sharing by doing 19 | bulk appends from thread-local storage 20 | */ 21 | 22 | 23 | template 24 | class QueueBuffer; 25 | 26 | template 27 | class SlidingQueue { 28 | public: 29 | T *shared; 30 | size_t shared_in; 31 | size_t shared_out_start; 32 | size_t shared_out_end; 33 | friend class QueueBuffer; 34 | 35 | explicit SlidingQueue(size_t shared_size) { 36 | shared = new T[shared_size]; 37 | reset(); 38 | } 39 | explicit SlidingQueue(const SlidingQueue &other, size_t shared_size) { 40 | shared_in = other.shared_in; 41 | shared_out_start = other.shared_out_start; 42 | shared_out_end = other.shared_out_end; 43 | shared = new T[shared_size]; 44 | //memcpy(shared, other.shared, shared_size * sizeof(T)); 45 | parallel_for_256(uint64_t i = 0; i < shared_size; i++) { 46 | shared[i] = other.shared[i]; 47 | } 48 | } 49 | 50 | ~SlidingQueue() { 51 | delete[] shared; 52 | } 53 | 54 | void push_back(T to_add) { 55 | shared[shared_in++] = to_add; 56 | } 57 | 58 | bool empty() const { 59 | return shared_out_start == shared_out_end; 60 | } 61 | 62 | void reset() { 63 | shared_out_start = 0; 64 | shared_out_end = 0; 65 | shared_in = 0; 66 | } 67 | 68 | void slide_window() { 69 | shared_out_start = shared_out_end; 70 | shared_out_end = shared_in; 71 | } 72 | 73 | typedef T* iterator; 74 | 75 | iterator begin() const { 76 | return shared + shared_out_start; 77 | } 78 | 79 | iterator end() const { 80 | return shared + shared_out_end; 81 | } 82 | 83 | size_t size() const { 84 | return end() - begin(); 85 | } 86 | }; 87 | 88 | 89 | template 90 | class QueueBuffer { 91 | public: 92 | size_t in; 93 | T *local_queue; 94 | SlidingQueue &sq; 95 | const size_t local_size; 96 | 97 | explicit QueueBuffer(SlidingQueue &master, size_t given_size = 16384) 98 | : sq(master), local_size(given_size) { 99 | in = 0; 100 | local_queue = new T[local_size]; 101 | } 102 | 103 | ~QueueBuffer() { 104 | delete[] local_queue; 105 | } 106 | 107 | void push_back(T to_add) { 108 | if (in == local_size) 109 | flush(); 110 | local_queue[in++] = to_add; 111 | } 112 | 113 | void flush() { 114 | T *shared_queue = sq.shared; 115 | size_t copy_start = __sync_fetch_and_add(&sq.shared_in, in); 116 | std::copy(local_queue, local_queue+in, shared_queue+copy_start); 117 | //printf("flushing %lu items\n", in); 118 | in = 0; 119 | } 120 | }; 121 | 122 | #endif // SLIDING_QUEUE_H_ 123 | -------------------------------------------------------------------------------- /include/spdlog/async.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright(c) 2018 Gabi Melman. 4 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 5 | // 6 | 7 | #pragma once 8 | 9 | // 10 | // Async logging using global thread pool 11 | // All loggers created here share same global thread pool. 12 | // Each log message is pushed to a queue along withe a shared pointer to the 13 | // logger. 14 | // If a logger deleted while having pending messages in the queue, it's actual 15 | // destruction will defer 16 | // until all its messages are processed by the thread pool. 17 | // This is because each message in the queue holds a shared_ptr to the 18 | // originating logger. 19 | 20 | #include "spdlog/async_logger.h" 21 | #include "spdlog/details/registry.h" 22 | #include "spdlog/details/thread_pool.h" 23 | 24 | #include 25 | #include 26 | 27 | namespace spdlog { 28 | 29 | namespace details { 30 | static const size_t default_async_q_size = 8192; 31 | } 32 | 33 | // async logger factory - creates async loggers backed with thread pool. 34 | // if a global thread pool doesn't already exist, create it with default queue 35 | // size of 8192 items and single thread. 36 | template 37 | struct async_factory_impl 38 | { 39 | template 40 | static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) 41 | { 42 | auto ®istry_inst = details::registry::instance(); 43 | 44 | // create global thread pool if not already exists.. 45 | std::lock_guard tp_lock(registry_inst.tp_mutex()); 46 | auto tp = registry_inst.get_tp(); 47 | if (tp == nullptr) 48 | { 49 | tp = std::make_shared(details::default_async_q_size, 1); 50 | registry_inst.set_tp(tp); 51 | } 52 | 53 | auto sink = std::make_shared(std::forward(args)...); 54 | auto new_logger = std::make_shared(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy); 55 | registry_inst.initialize_logger(new_logger); 56 | return new_logger; 57 | } 58 | }; 59 | 60 | using async_factory = async_factory_impl; 61 | using async_factory_nonblock = async_factory_impl; 62 | 63 | template 64 | inline std::shared_ptr create_async(std::string logger_name, SinkArgs &&... sink_args) 65 | { 66 | return async_factory::create(std::move(logger_name), std::forward(sink_args)...); 67 | } 68 | 69 | template 70 | inline std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&... sink_args) 71 | { 72 | return async_factory_nonblock::create(std::move(logger_name), std::forward(sink_args)...); 73 | } 74 | 75 | // set global thread pool. 76 | inline void init_thread_pool(size_t q_size, size_t thread_count) 77 | { 78 | auto tp = std::make_shared(q_size, thread_count); 79 | details::registry::instance().set_tp(std::move(tp)); 80 | } 81 | 82 | // get the global thread pool. 83 | inline std::shared_ptr thread_pool() 84 | { 85 | return details::registry::instance().get_tp(); 86 | } 87 | } // namespace spdlog 88 | -------------------------------------------------------------------------------- /include/spdlog/async_logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Very fast asynchronous logger (millions of logs per second on an average 9 | // desktop) 10 | // Uses pre allocated lockfree queue for maximum throughput even under large 11 | // number of threads. 12 | // Creates a single back thread to pop messages from the queue and log them. 13 | // 14 | // Upon each log write the logger: 15 | // 1. Checks if its log level is enough to log the message 16 | // 2. Push a new copy of the message to a queue (or block the caller until 17 | // space is available in the queue) 18 | // 3. will throw spdlog_ex upon log exceptions 19 | // Upon destruction, logs all remaining messages in the queue before 20 | // destructing.. 21 | 22 | #include "spdlog/common.h" 23 | #include "spdlog/logger.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace spdlog { 30 | 31 | // Async overflow policy - block by default. 32 | enum class async_overflow_policy 33 | { 34 | block, // Block until message can be enqueued 35 | overrun_oldest // Discard oldest message in the queue if full when trying to 36 | // add new item. 37 | }; 38 | 39 | namespace details { 40 | class thread_pool; 41 | } 42 | 43 | class async_logger final : public std::enable_shared_from_this, public logger 44 | { 45 | friend class details::thread_pool; 46 | 47 | public: 48 | template 49 | async_logger(std::string logger_name, It begin, It end, std::weak_ptr tp, 50 | async_overflow_policy overflow_policy = async_overflow_policy::block); 51 | 52 | async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, 53 | async_overflow_policy overflow_policy = async_overflow_policy::block); 54 | 55 | async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, 56 | async_overflow_policy overflow_policy = async_overflow_policy::block); 57 | 58 | std::shared_ptr clone(std::string new_name) override; 59 | 60 | protected: 61 | void sink_it_(details::log_msg &msg) override; 62 | void flush_() override; 63 | 64 | void backend_log_(const details::log_msg &incoming_log_msg); 65 | void backend_flush_(); 66 | 67 | private: 68 | std::weak_ptr thread_pool_; 69 | async_overflow_policy overflow_policy_; 70 | }; 71 | } // namespace spdlog 72 | 73 | #include "details/async_logger_impl.h" 74 | -------------------------------------------------------------------------------- /include/spdlog/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "spdlog/tweakme.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 22 | #include 23 | #include 24 | #endif 25 | 26 | #include "spdlog/details/null_mutex.h" 27 | 28 | #include "spdlog/fmt/fmt.h" 29 | 30 | // visual studio upto 2013 does not support noexcept nor constexpr 31 | #if defined(_MSC_VER) && (_MSC_VER < 1900) 32 | #define SPDLOG_NOEXCEPT throw() 33 | #define SPDLOG_CONSTEXPR 34 | #else 35 | #define SPDLOG_NOEXCEPT noexcept 36 | #define SPDLOG_CONSTEXPR constexpr 37 | #endif 38 | 39 | #if defined(__GNUC__) || defined(__clang__) 40 | #define SPDLOG_DEPRECATED __attribute__((deprecated)) 41 | #elif defined(_MSC_VER) 42 | #define SPDLOG_DEPRECATED __declspec(deprecated) 43 | #else 44 | #define SPDLOG_DEPRECATED 45 | #endif 46 | 47 | // disable thread local on msvc 2013 48 | #ifndef SPDLOG_NO_TLS 49 | #if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) 50 | #define SPDLOG_NO_TLS 1 51 | #endif 52 | #endif 53 | 54 | // Get the basename of __FILE__ (at compile time if possible) 55 | #if FMT_HAS_FEATURE(__builtin_strrchr) 56 | #define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep) 57 | #else 58 | #define SPDLOG_STRRCHR(str, sep) strrchr(str, sep) 59 | #endif //__builtin_strrchr not defined 60 | 61 | #ifdef _WIN32 62 | #define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1 63 | #else 64 | #define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1 65 | #endif 66 | 67 | #ifndef SPDLOG_FUNCTION 68 | #define SPDLOG_FUNCTION __FUNCTION__ 69 | #endif 70 | 71 | namespace spdlog { 72 | 73 | class formatter; 74 | 75 | namespace sinks { 76 | class sink; 77 | } 78 | 79 | using log_clock = std::chrono::system_clock; 80 | using sink_ptr = std::shared_ptr; 81 | using sinks_init_list = std::initializer_list; 82 | using log_err_handler = std::function; 83 | 84 | // string_view type - either std::string_view or fmt::string_view (pre c++17) 85 | #if defined(FMT_USE_STD_STRING_VIEW) 86 | using string_view_t = std::string_view; 87 | #else 88 | using string_view_t = fmt::string_view; 89 | #endif 90 | 91 | #if defined(SPDLOG_NO_ATOMIC_LEVELS) 92 | using level_t = details::null_atomic_int; 93 | #else 94 | using level_t = std::atomic; 95 | #endif 96 | 97 | #define SPDLOG_LEVEL_TRACE 0 98 | #define SPDLOG_LEVEL_DEBUG 1 99 | #define SPDLOG_LEVEL_INFO 2 100 | #define SPDLOG_LEVEL_WARN 3 101 | #define SPDLOG_LEVEL_ERROR 4 102 | #define SPDLOG_LEVEL_CRITICAL 5 103 | #define SPDLOG_LEVEL_OFF 6 104 | 105 | #if !defined(SPDLOG_ACTIVE_LEVEL) 106 | #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO 107 | #endif 108 | 109 | // Log level enum 110 | namespace level { 111 | enum level_enum 112 | { 113 | trace = SPDLOG_LEVEL_TRACE, 114 | debug = SPDLOG_LEVEL_DEBUG, 115 | info = SPDLOG_LEVEL_INFO, 116 | warn = SPDLOG_LEVEL_WARN, 117 | err = SPDLOG_LEVEL_ERROR, 118 | critical = SPDLOG_LEVEL_CRITICAL, 119 | off = SPDLOG_LEVEL_OFF, 120 | }; 121 | 122 | #if !defined(SPDLOG_LEVEL_NAMES) 123 | #define SPDLOG_LEVEL_NAMES \ 124 | { \ 125 | "trace", "debug", "info", "warning", "error", "critical", "off" \ 126 | } 127 | #endif 128 | static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; 129 | 130 | #if !defined(SPDLOG_SHORT_LEVEL_NAMES) 131 | #define SPDLOG_SHORT_LEVEL_NAMES {"T", "D", "I", "W", "E", "C", "O"} 132 | #endif 133 | static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; 134 | 135 | inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT 136 | { 137 | return level_string_views[l]; 138 | } 139 | 140 | inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT 141 | { 142 | return short_level_names[l]; 143 | } 144 | 145 | inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT 146 | { 147 | int level = 0; 148 | for (const auto &level_str : level_string_views) 149 | { 150 | if (level_str == name) 151 | { 152 | return static_cast(level); 153 | } 154 | level++; 155 | } 156 | return level::off; 157 | } 158 | 159 | using level_hasher = std::hash; 160 | } // namespace level 161 | 162 | // 163 | // Pattern time - specific time getting to use for pattern_formatter. 164 | // local time by default 165 | // 166 | enum class pattern_time_type 167 | { 168 | local, // log localtime 169 | utc // log utc 170 | }; 171 | 172 | // 173 | // Log exception 174 | // 175 | class spdlog_ex : public std::exception 176 | { 177 | public: 178 | explicit spdlog_ex(std::string msg) 179 | : msg_(std::move(msg)) 180 | { 181 | } 182 | 183 | spdlog_ex(const std::string &msg, int last_errno) 184 | { 185 | fmt::memory_buffer outbuf; 186 | fmt::format_system_error(outbuf, last_errno, msg); 187 | msg_ = fmt::to_string(outbuf); 188 | } 189 | 190 | const char *what() const SPDLOG_NOEXCEPT override 191 | { 192 | return msg_.c_str(); 193 | } 194 | 195 | private: 196 | std::string msg_; 197 | }; 198 | 199 | // 200 | // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) 201 | // 202 | #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) 203 | using filename_t = std::wstring; 204 | #else 205 | using filename_t = std::string; 206 | #endif 207 | 208 | struct source_loc 209 | { 210 | SPDLOG_CONSTEXPR source_loc() 211 | : filename{""} 212 | , line{0} 213 | , funcname{""} 214 | { 215 | } 216 | SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in) 217 | : filename{filename_in} 218 | , line{static_cast(line_in)} 219 | , funcname{funcname_in} 220 | { 221 | } 222 | 223 | SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT 224 | { 225 | return line == 0; 226 | } 227 | const char *filename; 228 | uint32_t line; 229 | const char *funcname; 230 | }; 231 | 232 | namespace details { 233 | // make_unique support for pre c++14 234 | 235 | #if __cplusplus >= 201402L // C++14 and beyond 236 | using std::make_unique; 237 | #else 238 | template 239 | std::unique_ptr make_unique(Args &&... args) 240 | { 241 | static_assert(!std::is_array::value, "arrays not supported"); 242 | return std::unique_ptr(new T(std::forward(args)...)); 243 | } 244 | #endif 245 | } // namespace details 246 | } // namespace spdlog 247 | -------------------------------------------------------------------------------- /include/spdlog/details/async_logger_impl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // async logger implementation 9 | // uses a thread pool to perform the actual logging 10 | 11 | #include "spdlog/details/thread_pool.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | template 18 | inline spdlog::async_logger::async_logger( 19 | std::string logger_name, It begin, It end, std::weak_ptr tp, async_overflow_policy overflow_policy) 20 | : logger(std::move(logger_name), begin, end) 21 | , thread_pool_(std::move(tp)) 22 | , overflow_policy_(overflow_policy) 23 | { 24 | } 25 | 26 | inline spdlog::async_logger::async_logger( 27 | std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, async_overflow_policy overflow_policy) 28 | : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy) 29 | { 30 | } 31 | 32 | inline spdlog::async_logger::async_logger( 33 | std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, async_overflow_policy overflow_policy) 34 | : async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) 35 | { 36 | } 37 | 38 | // send the log message to the thread pool 39 | inline void spdlog::async_logger::sink_it_(details::log_msg &msg) 40 | { 41 | #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) 42 | incr_msg_counter_(msg); 43 | #endif 44 | if (auto pool_ptr = thread_pool_.lock()) 45 | { 46 | pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); 47 | } 48 | else 49 | { 50 | throw spdlog_ex("async log: thread pool doesn't exist anymore"); 51 | } 52 | } 53 | 54 | // send flush request to the thread pool 55 | inline void spdlog::async_logger::flush_() 56 | { 57 | if (auto pool_ptr = thread_pool_.lock()) 58 | { 59 | pool_ptr->post_flush(shared_from_this(), overflow_policy_); 60 | } 61 | else 62 | { 63 | throw spdlog_ex("async flush: thread pool doesn't exist anymore"); 64 | } 65 | } 66 | 67 | // 68 | // backend functions - called from the thread pool to do the actual job 69 | // 70 | inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg) 71 | { 72 | try 73 | { 74 | for (auto &s : sinks_) 75 | { 76 | if (s->should_log(incoming_log_msg.level)) 77 | { 78 | s->log(incoming_log_msg); 79 | } 80 | } 81 | } 82 | SPDLOG_CATCH_AND_HANDLE 83 | 84 | if (should_flush_(incoming_log_msg)) 85 | { 86 | backend_flush_(); 87 | } 88 | } 89 | 90 | inline void spdlog::async_logger::backend_flush_() 91 | { 92 | try 93 | { 94 | for (auto &sink : sinks_) 95 | { 96 | sink->flush(); 97 | } 98 | } 99 | SPDLOG_CATCH_AND_HANDLE 100 | } 101 | 102 | inline std::shared_ptr spdlog::async_logger::clone(std::string new_name) 103 | { 104 | auto cloned = std::make_shared(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_); 105 | 106 | cloned->set_level(this->level()); 107 | cloned->flush_on(this->flush_level()); 108 | cloned->set_error_handler(this->error_handler()); 109 | return std::move(cloned); 110 | } 111 | -------------------------------------------------------------------------------- /include/spdlog/details/circular_q.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | // cirucal q view of std::vector. 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace spdlog { 12 | namespace details { 13 | template 14 | class circular_q 15 | { 16 | public: 17 | using item_type = T; 18 | 19 | explicit circular_q(size_t max_items) 20 | : max_items_(max_items + 1) // one item is reserved as marker for full q 21 | , v_(max_items_) 22 | { 23 | } 24 | 25 | // push back, overrun (oldest) item if no room left 26 | void push_back(T &&item) 27 | { 28 | v_[tail_] = std::move(item); 29 | tail_ = (tail_ + 1) % max_items_; 30 | 31 | if (tail_ == head_) // overrun last item if full 32 | { 33 | head_ = (head_ + 1) % max_items_; 34 | ++overrun_counter_; 35 | } 36 | } 37 | 38 | // Pop item from front. 39 | // If there are no elements in the container, the behavior is undefined. 40 | void pop_front(T &popped_item) 41 | { 42 | popped_item = std::move(v_[head_]); 43 | head_ = (head_ + 1) % max_items_; 44 | } 45 | 46 | bool empty() 47 | { 48 | return tail_ == head_; 49 | } 50 | 51 | bool full() 52 | { 53 | // head is ahead of the tail by 1 54 | return ((tail_ + 1) % max_items_) == head_; 55 | } 56 | 57 | size_t overrun_counter() const 58 | { 59 | return overrun_counter_; 60 | } 61 | 62 | private: 63 | size_t max_items_; 64 | typename std::vector::size_type head_ = 0; 65 | typename std::vector::size_type tail_ = 0; 66 | 67 | std::vector v_; 68 | 69 | size_t overrun_counter_ = 0; 70 | }; 71 | } // namespace details 72 | } // namespace spdlog 73 | -------------------------------------------------------------------------------- /include/spdlog/details/console_globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // Copyright(c) 2018 Gabi Melman. 4 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 5 | // 6 | 7 | #include "spdlog/details/null_mutex.h" 8 | #include 9 | #include 10 | 11 | #ifdef _WIN32 12 | 13 | #ifndef NOMINMAX 14 | #define NOMINMAX // prevent windows redefining min/max 15 | #endif 16 | 17 | #ifndef WIN32_LEAN_AND_MEAN 18 | #define WIN32_LEAN_AND_MEAN 19 | #endif 20 | 21 | #include 22 | #endif 23 | 24 | namespace spdlog { 25 | namespace details { 26 | struct console_stdout 27 | { 28 | static std::FILE *stream() 29 | { 30 | return stdout; 31 | } 32 | #ifdef _WIN32 33 | static HANDLE handle() 34 | { 35 | return ::GetStdHandle(STD_OUTPUT_HANDLE); 36 | } 37 | #endif 38 | }; 39 | 40 | struct console_stderr 41 | { 42 | static std::FILE *stream() 43 | { 44 | return stderr; 45 | } 46 | #ifdef _WIN32 47 | static HANDLE handle() 48 | { 49 | return ::GetStdHandle(STD_ERROR_HANDLE); 50 | } 51 | #endif 52 | }; 53 | 54 | struct console_mutex 55 | { 56 | using mutex_t = std::mutex; 57 | static mutex_t &mutex() 58 | { 59 | static mutex_t s_mutex; 60 | return s_mutex; 61 | } 62 | }; 63 | 64 | struct console_nullmutex 65 | { 66 | using mutex_t = null_mutex; 67 | static mutex_t &mutex() 68 | { 69 | static mutex_t s_mutex; 70 | return s_mutex; 71 | } 72 | }; 73 | } // namespace details 74 | } // namespace spdlog 75 | -------------------------------------------------------------------------------- /include/spdlog/details/file_helper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Helper class for file sinks. 9 | // When failing to open a file, retry several times(5) with a delay interval(10 ms). 10 | // Throw spdlog_ex exception on errors. 11 | 12 | #include "spdlog/details/log_msg.h" 13 | #include "spdlog/details/os.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace spdlog { 23 | namespace details { 24 | 25 | class file_helper 26 | { 27 | 28 | public: 29 | const int open_tries = 5; 30 | const int open_interval = 10; 31 | 32 | explicit file_helper() = default; 33 | 34 | file_helper(const file_helper &) = delete; 35 | file_helper &operator=(const file_helper &) = delete; 36 | 37 | ~file_helper() 38 | { 39 | close(); 40 | } 41 | 42 | void open(const filename_t &fname, bool truncate = false) 43 | { 44 | close(); 45 | auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); 46 | _filename = fname; 47 | for (int tries = 0; tries < open_tries; ++tries) 48 | { 49 | if (!os::fopen_s(&fd_, fname, mode)) 50 | { 51 | return; 52 | } 53 | 54 | details::os::sleep_for_millis(open_interval); 55 | } 56 | 57 | throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); 58 | } 59 | 60 | void reopen(bool truncate) 61 | { 62 | if (_filename.empty()) 63 | { 64 | throw spdlog_ex("Failed re opening file - was not opened before"); 65 | } 66 | open(_filename, truncate); 67 | } 68 | 69 | void flush() 70 | { 71 | std::fflush(fd_); 72 | } 73 | 74 | void close() 75 | { 76 | if (fd_ != nullptr) 77 | { 78 | std::fclose(fd_); 79 | fd_ = nullptr; 80 | } 81 | } 82 | 83 | void write(const fmt::memory_buffer &buf) 84 | { 85 | size_t msg_size = buf.size(); 86 | auto data = buf.data(); 87 | if (std::fwrite(data, 1, msg_size, fd_) != msg_size) 88 | { 89 | throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); 90 | } 91 | } 92 | 93 | size_t size() const 94 | { 95 | if (fd_ == nullptr) 96 | { 97 | throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); 98 | } 99 | return os::filesize(fd_); 100 | } 101 | 102 | const filename_t &filename() const 103 | { 104 | return _filename; 105 | } 106 | 107 | static bool file_exists(const filename_t &fname) 108 | { 109 | return os::file_exists(fname); 110 | } 111 | 112 | // 113 | // return file path and its extension: 114 | // 115 | // "mylog.txt" => ("mylog", ".txt") 116 | // "mylog" => ("mylog", "") 117 | // "mylog." => ("mylog.", "") 118 | // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") 119 | // 120 | // the starting dot in filenames is ignored (hidden files): 121 | // 122 | // ".mylog" => (".mylog". "") 123 | // "my_folder/.mylog" => ("my_folder/.mylog", "") 124 | // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") 125 | static std::tuple split_by_extension(const spdlog::filename_t &fname) 126 | { 127 | auto ext_index = fname.rfind('.'); 128 | 129 | // no valid extension found - return whole path and empty string as 130 | // extension 131 | if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) 132 | { 133 | return std::make_tuple(fname, spdlog::filename_t()); 134 | } 135 | 136 | // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" 137 | auto folder_index = fname.rfind(details::os::folder_sep); 138 | if (folder_index != filename_t::npos && folder_index >= ext_index - 1) 139 | { 140 | return std::make_tuple(fname, spdlog::filename_t()); 141 | } 142 | 143 | // finally - return a valid base and extension tuple 144 | return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); 145 | } 146 | 147 | private: 148 | std::FILE *fd_{nullptr}; 149 | filename_t _filename; 150 | }; 151 | } // namespace details 152 | } // namespace spdlog 153 | -------------------------------------------------------------------------------- /include/spdlog/details/fmt_helper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by gabi on 6/15/18. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include "spdlog/fmt/fmt.h" 10 | 11 | // Some fmt helpers to efficiently format and pad ints and strings 12 | namespace spdlog { 13 | namespace details { 14 | namespace fmt_helper { 15 | 16 | template 17 | inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer &buf) SPDLOG_NOEXCEPT 18 | { 19 | return spdlog::string_view_t(buf.data(), buf.size()); 20 | } 21 | 22 | template 23 | inline void append_buf(const fmt::basic_memory_buffer &buf, fmt::basic_memory_buffer &dest) 24 | { 25 | auto *buf_ptr = buf.data(); 26 | dest.append(buf_ptr, buf_ptr + buf.size()); 27 | } 28 | 29 | template 30 | inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer &dest) 31 | { 32 | auto *buf_ptr = view.data(); 33 | if (buf_ptr != nullptr) 34 | { 35 | dest.append(buf_ptr, buf_ptr + view.size()); 36 | } 37 | } 38 | 39 | template 40 | inline void append_int(T n, fmt::basic_memory_buffer &dest) 41 | { 42 | fmt::format_int i(n); 43 | dest.append(i.data(), i.data() + i.size()); 44 | } 45 | 46 | template 47 | inline unsigned count_digits(T n) 48 | { 49 | using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type; 50 | return static_cast(fmt::internal::count_digits(static_cast(n))); 51 | } 52 | 53 | template 54 | inline void pad2(int n, fmt::basic_memory_buffer &dest) 55 | { 56 | if (n > 99) 57 | { 58 | append_int(n, dest); 59 | } 60 | else if (n > 9) // 10-99 61 | { 62 | dest.push_back(static_cast('0' + n / 10)); 63 | dest.push_back(static_cast('0' + n % 10)); 64 | } 65 | else if (n >= 0) // 0-9 66 | { 67 | dest.push_back('0'); 68 | dest.push_back(static_cast('0' + n)); 69 | } 70 | else // negatives (unlikely, but just in case, let fmt deal with it) 71 | { 72 | fmt::format_to(dest, "{:02}", n); 73 | } 74 | } 75 | 76 | template 77 | inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer &dest) 78 | { 79 | static_assert(std::is_unsigned::value, "pad_uint must get unsigned T"); 80 | auto digits = count_digits(n); 81 | if (width > digits) 82 | { 83 | const char *zeroes = "0000000000000000000"; 84 | dest.append(zeroes, zeroes + width - digits); 85 | } 86 | append_int(n, dest); 87 | } 88 | 89 | template 90 | inline void pad3(T n, fmt::basic_memory_buffer &dest) 91 | { 92 | pad_uint(n, 3, dest); 93 | } 94 | 95 | template 96 | inline void pad6(T n, fmt::basic_memory_buffer &dest) 97 | { 98 | pad_uint(n, 6, dest); 99 | } 100 | 101 | template 102 | inline void pad9(T n, fmt::basic_memory_buffer &dest) 103 | { 104 | pad_uint(n, 9, dest); 105 | } 106 | 107 | // return fraction of a second of the given time_point. 108 | // e.g. 109 | // fraction(tp) -> will return the millis part of the second 110 | template 111 | inline ToDuration time_fraction(const log_clock::time_point &tp) 112 | { 113 | using std::chrono::duration_cast; 114 | using std::chrono::seconds; 115 | auto duration = tp.time_since_epoch(); 116 | auto secs = duration_cast(duration); 117 | return duration_cast(duration) - duration_cast(secs); 118 | } 119 | 120 | } // namespace fmt_helper 121 | } // namespace details 122 | } // namespace spdlog 123 | -------------------------------------------------------------------------------- /include/spdlog/details/log_msg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "spdlog/common.h" 9 | #include "spdlog/details/os.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace details { 16 | struct log_msg 17 | { 18 | 19 | log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view) 20 | : logger_name(loggers_name) 21 | , level(lvl) 22 | #ifndef SPDLOG_NO_DATETIME 23 | , time(os::now()) 24 | #endif 25 | 26 | #ifndef SPDLOG_NO_THREAD_ID 27 | , thread_id(os::thread_id()) 28 | #endif 29 | , source(loc) 30 | , payload(view) 31 | { 32 | } 33 | 34 | log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view) 35 | : log_msg(source_loc{}, loggers_name, lvl, view) 36 | { 37 | } 38 | 39 | log_msg(const log_msg &other) = default; 40 | 41 | const std::string *logger_name{nullptr}; 42 | level::level_enum level{level::off}; 43 | log_clock::time_point time; 44 | size_t thread_id{0}; 45 | size_t msg_id{0}; 46 | 47 | // wrapping the formatted text with color (updated by pattern_formatter). 48 | mutable size_t color_range_start{0}; 49 | mutable size_t color_range_end{0}; 50 | 51 | source_loc source; 52 | const string_view_t payload; 53 | }; 54 | } // namespace details 55 | } // namespace spdlog 56 | -------------------------------------------------------------------------------- /include/spdlog/details/mpmc_blocking_q.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright(c) 2018 Gabi Melman. 5 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 6 | // 7 | 8 | // multi producer-multi consumer blocking queue. 9 | // enqueue(..) - will block until room found to put the new message. 10 | // enqueue_nowait(..) - will return immediately with false if no room left in 11 | // the queue. 12 | // dequeue_for(..) - will block until the queue is not empty or timeout have 13 | // passed. 14 | 15 | #include "spdlog/details/circular_q.h" 16 | 17 | #include 18 | #include 19 | 20 | namespace spdlog { 21 | namespace details { 22 | 23 | template 24 | class mpmc_blocking_queue 25 | { 26 | public: 27 | using item_type = T; 28 | explicit mpmc_blocking_queue(size_t max_items) 29 | : q_(max_items) 30 | { 31 | } 32 | 33 | #ifndef __MINGW32__ 34 | // try to enqueue and block if no room left 35 | void enqueue(T &&item) 36 | { 37 | { 38 | std::unique_lock lock(queue_mutex_); 39 | pop_cv_.wait(lock, [this] { return !this->q_.full(); }); 40 | q_.push_back(std::move(item)); 41 | } 42 | push_cv_.notify_one(); 43 | } 44 | 45 | // enqueue immediately. overrun oldest message in the queue if no room left. 46 | void enqueue_nowait(T &&item) 47 | { 48 | { 49 | std::unique_lock lock(queue_mutex_); 50 | q_.push_back(std::move(item)); 51 | } 52 | push_cv_.notify_one(); 53 | } 54 | 55 | // try to dequeue item. if no item found. wait upto timeout and try again 56 | // Return true, if succeeded dequeue item, false otherwise 57 | bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) 58 | { 59 | { 60 | std::unique_lock lock(queue_mutex_); 61 | if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) 62 | { 63 | return false; 64 | } 65 | q_.pop_front(popped_item); 66 | } 67 | pop_cv_.notify_one(); 68 | return true; 69 | } 70 | 71 | #else 72 | // apparently mingw deadlocks if the mutex is released before cv.notify_one(), 73 | // so release the mutex at the very end each function. 74 | 75 | // try to enqueue and block if no room left 76 | void enqueue(T &&item) 77 | { 78 | std::unique_lock lock(queue_mutex_); 79 | pop_cv_.wait(lock, [this] { return !this->q_.full(); }); 80 | q_.push_back(std::move(item)); 81 | push_cv_.notify_one(); 82 | } 83 | 84 | // enqueue immediately. overrun oldest message in the queue if no room left. 85 | void enqueue_nowait(T &&item) 86 | { 87 | std::unique_lock lock(queue_mutex_); 88 | q_.push_back(std::move(item)); 89 | push_cv_.notify_one(); 90 | } 91 | 92 | // try to dequeue item. if no item found. wait upto timeout and try again 93 | // Return true, if succeeded dequeue item, false otherwise 94 | bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) 95 | { 96 | std::unique_lock lock(queue_mutex_); 97 | if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) 98 | { 99 | return false; 100 | } 101 | q_.pop_front(popped_item); 102 | pop_cv_.notify_one(); 103 | return true; 104 | } 105 | 106 | #endif 107 | 108 | size_t overrun_counter() 109 | { 110 | std::unique_lock lock(queue_mutex_); 111 | return q_.overrun_counter(); 112 | } 113 | 114 | private: 115 | std::mutex queue_mutex_; 116 | std::condition_variable push_cv_; 117 | std::condition_variable pop_cv_; 118 | spdlog::details::circular_q q_; 119 | }; 120 | } // namespace details 121 | } // namespace spdlog 122 | -------------------------------------------------------------------------------- /include/spdlog/details/null_mutex.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | // null, no cost dummy "mutex" and dummy "atomic" int 10 | 11 | namespace spdlog { 12 | namespace details { 13 | struct null_mutex 14 | { 15 | void lock() {} 16 | void unlock() {} 17 | bool try_lock() 18 | { 19 | return true; 20 | } 21 | }; 22 | 23 | struct null_atomic_int 24 | { 25 | int value; 26 | null_atomic_int() = default; 27 | 28 | explicit null_atomic_int(int val) 29 | : value(val) 30 | { 31 | } 32 | 33 | int load(std::memory_order) const 34 | { 35 | return value; 36 | } 37 | 38 | void store(int val) 39 | { 40 | value = val; 41 | } 42 | }; 43 | 44 | } // namespace details 45 | } // namespace spdlog 46 | -------------------------------------------------------------------------------- /include/spdlog/details/periodic_worker.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright(c) 2018 Gabi Melman. 4 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 5 | // 6 | 7 | #pragma once 8 | 9 | // periodic worker thread - periodically executes the given callback function. 10 | // 11 | // RAII over the owned thread: 12 | // creates the thread on construction. 13 | // stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first). 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | namespace spdlog { 21 | namespace details { 22 | 23 | class periodic_worker 24 | { 25 | public: 26 | periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) 27 | { 28 | active_ = (interval > std::chrono::seconds::zero()); 29 | if (!active_) 30 | { 31 | return; 32 | } 33 | 34 | worker_thread_ = std::thread([this, callback_fun, interval]() { 35 | for (;;) 36 | { 37 | std::unique_lock lock(this->mutex_); 38 | if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) 39 | { 40 | return; // active_ == false, so exit this thread 41 | } 42 | callback_fun(); 43 | } 44 | }); 45 | } 46 | 47 | periodic_worker(const periodic_worker &) = delete; 48 | periodic_worker &operator=(const periodic_worker &) = delete; 49 | 50 | // stop the worker thread and join it 51 | ~periodic_worker() 52 | { 53 | if (worker_thread_.joinable()) 54 | { 55 | { 56 | std::lock_guard lock(mutex_); 57 | active_ = false; 58 | } 59 | cv_.notify_one(); 60 | worker_thread_.join(); 61 | } 62 | } 63 | 64 | private: 65 | bool active_; 66 | std::thread worker_thread_; 67 | std::mutex mutex_; 68 | std::condition_variable cv_; 69 | }; 70 | } // namespace details 71 | } // namespace spdlog 72 | -------------------------------------------------------------------------------- /include/spdlog/details/thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "spdlog/details/fmt_helper.h" 4 | #include "spdlog/details/log_msg.h" 5 | #include "spdlog/details/mpmc_blocking_q.h" 6 | #include "spdlog/details/os.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | namespace details { 15 | 16 | using async_logger_ptr = std::shared_ptr; 17 | 18 | enum class async_msg_type 19 | { 20 | log, 21 | flush, 22 | terminate 23 | }; 24 | 25 | // Async msg to move to/from the queue 26 | // Movable only. should never be copied 27 | struct async_msg 28 | { 29 | async_msg_type msg_type; 30 | level::level_enum level; 31 | log_clock::time_point time; 32 | size_t thread_id; 33 | fmt::basic_memory_buffer raw; 34 | 35 | size_t msg_id; 36 | source_loc source; 37 | async_logger_ptr worker_ptr; 38 | 39 | async_msg() = default; 40 | ~async_msg() = default; 41 | 42 | // should only be moved in or out of the queue.. 43 | async_msg(const async_msg &) = delete; 44 | 45 | // support for vs2013 move 46 | #if defined(_MSC_VER) && _MSC_VER <= 1800 47 | async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type), 48 | level(other.level), 49 | time(other.time), 50 | thread_id(other.thread_id), 51 | raw(move(other.raw)), 52 | msg_id(other.msg_id), 53 | source(other.source), 54 | worker_ptr(std::move(other.worker_ptr)) 55 | { 56 | } 57 | 58 | async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT 59 | { 60 | msg_type = other.msg_type; 61 | level = other.level; 62 | time = other.time; 63 | thread_id = other.thread_id; 64 | raw = std::move(other.raw); 65 | msg_id = other.msg_id; 66 | source = other.source; 67 | worker_ptr = std::move(other.worker_ptr); 68 | return *this; 69 | } 70 | #else // (_MSC_VER) && _MSC_VER <= 1800 71 | async_msg(async_msg &&) = default; 72 | async_msg &operator=(async_msg &&) = default; 73 | #endif 74 | 75 | // construct from log_msg with given type 76 | async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m) 77 | : msg_type(the_type) 78 | , level(m.level) 79 | , time(m.time) 80 | , thread_id(m.thread_id) 81 | , msg_id(m.msg_id) 82 | , source(m.source) 83 | , worker_ptr(std::move(worker)) 84 | { 85 | fmt_helper::append_string_view(m.payload, raw); 86 | } 87 | 88 | async_msg(async_logger_ptr &&worker, async_msg_type the_type) 89 | : msg_type(the_type) 90 | , level(level::off) 91 | , time() 92 | , thread_id(0) 93 | , msg_id(0) 94 | , source() 95 | , worker_ptr(std::move(worker)) 96 | { 97 | } 98 | 99 | explicit async_msg(async_msg_type the_type) 100 | : async_msg(nullptr, the_type) 101 | { 102 | } 103 | 104 | // copy into log_msg 105 | log_msg to_log_msg() 106 | { 107 | log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size())); 108 | msg.time = time; 109 | msg.thread_id = thread_id; 110 | msg.msg_id = msg_id; 111 | msg.source = source; 112 | msg.color_range_start = 0; 113 | msg.color_range_end = 0; 114 | return msg; 115 | } 116 | }; 117 | 118 | class thread_pool 119 | { 120 | public: 121 | using item_type = async_msg; 122 | using q_type = details::mpmc_blocking_queue; 123 | 124 | thread_pool(size_t q_max_items, size_t threads_n) 125 | : q_(q_max_items) 126 | { 127 | // std::cout << "thread_pool() q_size_bytes: " << q_size_bytes << 128 | // "\tthreads_n: " << threads_n << std::endl; 129 | if (threads_n == 0 || threads_n > 1000) 130 | { 131 | throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid " 132 | "range is 1-1000)"); 133 | } 134 | for (size_t i = 0; i < threads_n; i++) 135 | { 136 | threads_.emplace_back(&thread_pool::worker_loop_, this); 137 | } 138 | } 139 | 140 | // message all threads to terminate gracefully join them 141 | ~thread_pool() 142 | { 143 | try 144 | { 145 | for (size_t i = 0; i < threads_.size(); i++) 146 | { 147 | post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block); 148 | } 149 | 150 | for (auto &t : threads_) 151 | { 152 | t.join(); 153 | } 154 | } 155 | catch (...) 156 | { 157 | } 158 | } 159 | 160 | thread_pool(const thread_pool &) = delete; 161 | thread_pool &operator=(thread_pool &&) = delete; 162 | 163 | void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy) 164 | { 165 | async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg); 166 | post_async_msg_(std::move(async_m), overflow_policy); 167 | } 168 | 169 | void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) 170 | { 171 | post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); 172 | } 173 | 174 | size_t overrun_counter() 175 | { 176 | return q_.overrun_counter(); 177 | } 178 | 179 | private: 180 | q_type q_; 181 | 182 | std::vector threads_; 183 | 184 | void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) 185 | { 186 | if (overflow_policy == async_overflow_policy::block) 187 | { 188 | q_.enqueue(std::move(new_msg)); 189 | } 190 | else 191 | { 192 | q_.enqueue_nowait(std::move(new_msg)); 193 | } 194 | } 195 | 196 | void worker_loop_() 197 | { 198 | while (process_next_msg_()) {}; 199 | } 200 | 201 | // process next message in the queue 202 | // return true if this thread should still be active (while no terminate msg 203 | // was received) 204 | bool process_next_msg_() 205 | { 206 | async_msg incoming_async_msg; 207 | bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); 208 | if (!dequeued) 209 | { 210 | return true; 211 | } 212 | 213 | switch (incoming_async_msg.msg_type) 214 | { 215 | case async_msg_type::log: 216 | { 217 | auto msg = incoming_async_msg.to_log_msg(); 218 | incoming_async_msg.worker_ptr->backend_log_(msg); 219 | return true; 220 | } 221 | case async_msg_type::flush: 222 | { 223 | incoming_async_msg.worker_ptr->backend_flush_(); 224 | return true; 225 | } 226 | 227 | case async_msg_type::terminate: 228 | { 229 | return false; 230 | } 231 | } 232 | assert(false && "Unexpected async_msg_type"); 233 | return true; 234 | } 235 | }; 236 | 237 | } // namespace details 238 | } // namespace spdlog 239 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bin_to_hex.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // 9 | // Support for logging binary data as hex 10 | // format flags: 11 | // {:X} - print in uppercase. 12 | // {:s} - don't separate each byte with space. 13 | // {:p} - don't print the position on each line start. 14 | // {:n} - don't split the output to lines. 15 | 16 | // 17 | // Examples: 18 | // 19 | // std::vector v(200, 0x0b); 20 | // logger->info("Some buffer {}", spdlog::to_hex(v)); 21 | // char buf[128]; 22 | // logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf))); 23 | 24 | namespace spdlog { 25 | namespace details { 26 | 27 | template 28 | class bytes_range 29 | { 30 | public: 31 | bytes_range(It range_begin, It range_end) 32 | : begin_(range_begin) 33 | , end_(range_end) 34 | { 35 | } 36 | 37 | It begin() const 38 | { 39 | return begin_; 40 | } 41 | It end() const 42 | { 43 | return end_; 44 | } 45 | 46 | private: 47 | It begin_, end_; 48 | }; 49 | } // namespace details 50 | 51 | // create a bytes_range that wraps the given container 52 | template 53 | inline details::bytes_range to_hex(const Container &container) 54 | { 55 | static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1"); 56 | using Iter = typename Container::const_iterator; 57 | return details::bytes_range(std::begin(container), std::end(container)); 58 | } 59 | 60 | // create bytes_range from ranges 61 | template 62 | inline details::bytes_range to_hex(const It range_begin, const It range_end) 63 | { 64 | return details::bytes_range(range_begin, range_end); 65 | } 66 | 67 | } // namespace spdlog 68 | 69 | namespace fmt { 70 | 71 | template 72 | struct formatter> 73 | { 74 | const std::size_t line_size = 100; 75 | const char delimiter = ' '; 76 | 77 | bool put_newlines = true; 78 | bool put_delimiters = true; 79 | bool use_uppercase = false; 80 | bool put_positions = true; // position on start of each line 81 | 82 | // parse the format string flags 83 | template 84 | auto parse(ParseContext &ctx) -> decltype(ctx.begin()) 85 | { 86 | auto it = ctx.begin(); 87 | while (*it && *it != '}') 88 | { 89 | switch (*it) 90 | { 91 | case 'X': 92 | use_uppercase = true; 93 | break; 94 | case 's': 95 | put_delimiters = false; 96 | break; 97 | case 'p': 98 | put_positions = false; 99 | break; 100 | case 'n': 101 | put_newlines = false; 102 | break; 103 | } 104 | 105 | ++it; 106 | } 107 | return it; 108 | } 109 | 110 | // format the given bytes range as hex 111 | template 112 | auto format(const spdlog::details::bytes_range &the_range, FormatContext &ctx) -> decltype(ctx.out()) 113 | { 114 | SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF"; 115 | SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef"; 116 | const char *hex_chars = use_uppercase ? hex_upper : hex_lower; 117 | 118 | std::size_t pos = 0; 119 | std::size_t column = line_size; 120 | auto inserter = ctx.begin(); 121 | 122 | for (auto &item : the_range) 123 | { 124 | auto ch = static_cast(item); 125 | pos++; 126 | 127 | if (put_newlines && column >= line_size) 128 | { 129 | column = put_newline(inserter, pos); 130 | 131 | // put first byte without delimiter in front of it 132 | *inserter++ = hex_chars[(ch >> 4) & 0x0f]; 133 | *inserter++ = hex_chars[ch & 0x0f]; 134 | column += 2; 135 | continue; 136 | } 137 | 138 | if (put_delimiters) 139 | { 140 | *inserter++ = delimiter; 141 | ++column; 142 | } 143 | 144 | *inserter++ = hex_chars[(ch >> 4) & 0x0f]; 145 | *inserter++ = hex_chars[ch & 0x0f]; 146 | column += 2; 147 | } 148 | return inserter; 149 | } 150 | 151 | // put newline(and position header) 152 | // return the next column 153 | template 154 | std::size_t put_newline(It inserter, std::size_t pos) 155 | { 156 | #ifdef _WIN32 157 | *inserter++ = '\r'; 158 | #endif 159 | *inserter++ = '\n'; 160 | 161 | if (put_positions) 162 | { 163 | fmt::format_to(inserter, "{:<04X}: ", pos - 1); 164 | return 7; 165 | } 166 | else 167 | { 168 | return 1; 169 | } 170 | } 171 | }; 172 | } // namespace fmt 173 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bundled/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2016, Victor Zverovich 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bundled/locale.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::locale support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_LOCALE_H_ 9 | #define FMT_LOCALE_H_ 10 | 11 | #include "format.h" 12 | #include 13 | 14 | FMT_BEGIN_NAMESPACE 15 | 16 | namespace internal { 17 | template 18 | typename buffer_context::type::iterator vformat_to( 19 | const std::locale &loc, basic_buffer &buf, 20 | basic_string_view format_str, 21 | basic_format_args::type> args) { 22 | typedef back_insert_range > range; 23 | return vformat_to>( 24 | buf, to_string_view(format_str), args, internal::locale_ref(loc)); 25 | } 26 | 27 | template 28 | std::basic_string vformat( 29 | const std::locale &loc, basic_string_view format_str, 30 | basic_format_args::type> args) { 31 | basic_memory_buffer buffer; 32 | internal::vformat_to(loc, buffer, format_str, args); 33 | return fmt::to_string(buffer); 34 | } 35 | } 36 | 37 | template 38 | inline std::basic_string vformat( 39 | const std::locale &loc, const S &format_str, 40 | basic_format_args::type> args) { 41 | return internal::vformat(loc, to_string_view(format_str), args); 42 | } 43 | 44 | template 45 | inline std::basic_string format( 46 | const std::locale &loc, const S &format_str, const Args &... args) { 47 | return internal::vformat( 48 | loc, to_string_view(format_str), 49 | *internal::checked_args(format_str, args...)); 50 | } 51 | 52 | template 53 | inline typename std::enable_if::value, 54 | OutputIt>::type 55 | vformat_to(OutputIt out, const std::locale &loc, const String &format_str, 56 | typename format_args_t::type args) { 57 | typedef output_range range; 58 | return vformat_to>( 59 | range(out), to_string_view(format_str), args, internal::locale_ref(loc)); 60 | } 61 | 62 | template 63 | inline typename std::enable_if< 64 | internal::is_string::value && 65 | internal::is_output_iterator::value, OutputIt>::type 66 | format_to(OutputIt out, const std::locale &loc, const S &format_str, 67 | const Args &... args) { 68 | internal::check_format_string(format_str); 69 | typedef typename format_context_t::type context; 70 | format_arg_store as{args...}; 71 | return vformat_to(out, loc, to_string_view(format_str), 72 | basic_format_args(as)); 73 | } 74 | 75 | FMT_END_NAMESPACE 76 | 77 | #endif // FMT_LOCALE_H_ 78 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bundled/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include "format.h" 12 | #include 13 | 14 | FMT_BEGIN_NAMESPACE 15 | namespace internal { 16 | 17 | template 18 | class formatbuf : public std::basic_streambuf { 19 | private: 20 | typedef typename std::basic_streambuf::int_type int_type; 21 | typedef typename std::basic_streambuf::traits_type traits_type; 22 | 23 | basic_buffer &buffer_; 24 | 25 | public: 26 | formatbuf(basic_buffer &buffer) : buffer_(buffer) {} 27 | 28 | protected: 29 | // The put-area is actually always empty. This makes the implementation 30 | // simpler and has the advantage that the streambuf and the buffer are always 31 | // in sync and sputc never writes into uninitialized memory. The obvious 32 | // disadvantage is that each call to sputc always results in a (virtual) call 33 | // to overflow. There is no disadvantage here for sputn since this always 34 | // results in a call to xsputn. 35 | 36 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 37 | if (!traits_type::eq_int_type(ch, traits_type::eof())) 38 | buffer_.push_back(static_cast(ch)); 39 | return ch; 40 | } 41 | 42 | std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { 43 | buffer_.append(s, s + count); 44 | return count; 45 | } 46 | }; 47 | 48 | template 49 | struct test_stream : std::basic_ostream { 50 | private: 51 | struct null; 52 | // Hide all operator<< from std::basic_ostream. 53 | void operator<<(null); 54 | }; 55 | 56 | // Checks if T has a user-defined operator<< (e.g. not a member of std::ostream). 57 | template 58 | class is_streamable { 59 | private: 60 | template 61 | static decltype( 62 | internal::declval&>() 63 | << internal::declval(), std::true_type()) test(int); 64 | 65 | template 66 | static std::false_type test(...); 67 | 68 | typedef decltype(test(0)) result; 69 | 70 | public: 71 | static const bool value = result::value; 72 | }; 73 | 74 | // Write the content of buf to os. 75 | template 76 | void write(std::basic_ostream &os, basic_buffer &buf) { 77 | const Char *data = buf.data(); 78 | typedef std::make_unsigned::type UnsignedStreamSize; 79 | UnsignedStreamSize size = buf.size(); 80 | UnsignedStreamSize max_size = 81 | internal::to_unsigned((std::numeric_limits::max)()); 82 | do { 83 | UnsignedStreamSize n = size <= max_size ? size : max_size; 84 | os.write(data, static_cast(n)); 85 | data += n; 86 | size -= n; 87 | } while (size != 0); 88 | } 89 | 90 | template 91 | void format_value(basic_buffer &buffer, const T &value) { 92 | internal::formatbuf format_buf(buffer); 93 | std::basic_ostream output(&format_buf); 94 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 95 | output << value; 96 | buffer.resize(buffer.size()); 97 | } 98 | } // namespace internal 99 | 100 | // Disable conversion to int if T has an overloaded operator<< which is a free 101 | // function (not a member of std::ostream). 102 | template 103 | struct convert_to_int { 104 | static const bool value = 105 | convert_to_int::value && 106 | !internal::is_streamable::value; 107 | }; 108 | 109 | // Formats an object of type T that has an overloaded ostream operator<<. 110 | template 111 | struct formatter::value && 114 | !internal::format_type< 115 | typename buffer_context::type, T>::value>::type> 116 | : formatter, Char> { 117 | 118 | template 119 | auto format(const T &value, Context &ctx) -> decltype(ctx.out()) { 120 | basic_memory_buffer buffer; 121 | internal::format_value(buffer, value); 122 | basic_string_view str(buffer.data(), buffer.size()); 123 | return formatter, Char>::format(str, ctx); 124 | } 125 | }; 126 | 127 | template 128 | inline void vprint(std::basic_ostream &os, 129 | basic_string_view format_str, 130 | basic_format_args::type> args) { 131 | basic_memory_buffer buffer; 132 | internal::vformat_to(buffer, format_str, args); 133 | internal::write(os, buffer); 134 | } 135 | /** 136 | \rst 137 | Prints formatted data to the stream *os*. 138 | 139 | **Example**:: 140 | 141 | fmt::print(cerr, "Don't {}!", "panic"); 142 | \endrst 143 | */ 144 | template 145 | inline typename std::enable_if::value>::type 146 | print(std::basic_ostream &os, const S &format_str, 147 | const Args & ... args) { 148 | internal::checked_args ca(format_str, args...); 149 | vprint(os, to_string_view(format_str), *ca); 150 | } 151 | FMT_END_NAMESPACE 152 | 153 | #endif // FMT_OSTREAM_H_ 154 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bundled/time.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - time formatting 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_TIME_H_ 9 | #define FMT_TIME_H_ 10 | 11 | #include "format.h" 12 | #include 13 | #include 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | // Prevents expansion of a preceding token as a function-style macro. 18 | // Usage: f FMT_NOMACRO() 19 | #define FMT_NOMACRO 20 | 21 | namespace internal{ 22 | inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } 23 | inline null<> localtime_s(...) { return null<>(); } 24 | inline null<> gmtime_r(...) { return null<>(); } 25 | inline null<> gmtime_s(...) { return null<>(); } 26 | } // namespace internal 27 | 28 | // Thread-safe replacement for std::localtime 29 | inline std::tm localtime(std::time_t time) { 30 | struct dispatcher { 31 | std::time_t time_; 32 | std::tm tm_; 33 | 34 | dispatcher(std::time_t t): time_(t) {} 35 | 36 | bool run() { 37 | using namespace fmt::internal; 38 | return handle(localtime_r(&time_, &tm_)); 39 | } 40 | 41 | bool handle(std::tm *tm) { return tm != FMT_NULL; } 42 | 43 | bool handle(internal::null<>) { 44 | using namespace fmt::internal; 45 | return fallback(localtime_s(&tm_, &time_)); 46 | } 47 | 48 | bool fallback(int res) { return res == 0; } 49 | 50 | #if !FMT_MSC_VER 51 | bool fallback(internal::null<>) { 52 | using namespace fmt::internal; 53 | std::tm *tm = std::localtime(&time_); 54 | if (tm) tm_ = *tm; 55 | return tm != FMT_NULL; 56 | } 57 | #endif 58 | }; 59 | dispatcher lt(time); 60 | // Too big time values may be unsupported. 61 | if (!lt.run()) 62 | FMT_THROW(format_error("time_t value out of range")); 63 | return lt.tm_; 64 | } 65 | 66 | // Thread-safe replacement for std::gmtime 67 | inline std::tm gmtime(std::time_t time) { 68 | struct dispatcher { 69 | std::time_t time_; 70 | std::tm tm_; 71 | 72 | dispatcher(std::time_t t): time_(t) {} 73 | 74 | bool run() { 75 | using namespace fmt::internal; 76 | return handle(gmtime_r(&time_, &tm_)); 77 | } 78 | 79 | bool handle(std::tm *tm) { return tm != FMT_NULL; } 80 | 81 | bool handle(internal::null<>) { 82 | using namespace fmt::internal; 83 | return fallback(gmtime_s(&tm_, &time_)); 84 | } 85 | 86 | bool fallback(int res) { return res == 0; } 87 | 88 | #if !FMT_MSC_VER 89 | bool fallback(internal::null<>) { 90 | std::tm *tm = std::gmtime(&time_); 91 | if (tm) tm_ = *tm; 92 | return tm != FMT_NULL; 93 | } 94 | #endif 95 | }; 96 | dispatcher gt(time); 97 | // Too big time values may be unsupported. 98 | if (!gt.run()) 99 | FMT_THROW(format_error("time_t value out of range")); 100 | return gt.tm_; 101 | } 102 | 103 | namespace internal { 104 | inline std::size_t strftime(char *str, std::size_t count, const char *format, 105 | const std::tm *time) { 106 | return std::strftime(str, count, format, time); 107 | } 108 | 109 | inline std::size_t strftime(wchar_t *str, std::size_t count, 110 | const wchar_t *format, const std::tm *time) { 111 | return std::wcsftime(str, count, format, time); 112 | } 113 | } 114 | 115 | template 116 | struct formatter { 117 | template 118 | auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { 119 | auto it = ctx.begin(); 120 | if (it != ctx.end() && *it == ':') 121 | ++it; 122 | auto end = it; 123 | while (end != ctx.end() && *end != '}') 124 | ++end; 125 | tm_format.reserve(internal::to_unsigned(end - it + 1)); 126 | tm_format.append(it, end); 127 | tm_format.push_back('\0'); 128 | return end; 129 | } 130 | 131 | template 132 | auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { 133 | basic_memory_buffer buf; 134 | std::size_t start = buf.size(); 135 | for (;;) { 136 | std::size_t size = buf.capacity() - start; 137 | std::size_t count = 138 | internal::strftime(&buf[start], size, &tm_format[0], &tm); 139 | if (count != 0) { 140 | buf.resize(start + count); 141 | break; 142 | } 143 | if (size >= tm_format.size() * 256) { 144 | // If the buffer is 256 times larger than the format string, assume 145 | // that `strftime` gives an empty result. There doesn't seem to be a 146 | // better way to distinguish the two cases: 147 | // https://github.com/fmtlib/fmt/issues/367 148 | break; 149 | } 150 | const std::size_t MIN_GROWTH = 10; 151 | buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); 152 | } 153 | return std::copy(buf.begin(), buf.end(), ctx.out()); 154 | } 155 | 156 | basic_memory_buffer tm_format; 157 | }; 158 | FMT_END_NAMESPACE 159 | 160 | #endif // FMT_TIME_H_ 161 | -------------------------------------------------------------------------------- /include/spdlog/fmt/fmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016-2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // 9 | // Include a bundled header-only copy of fmtlib or an external one. 10 | // By default spdlog include its own copy. 11 | // 12 | 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifndef FMT_HEADER_ONLY 15 | #define FMT_HEADER_ONLY 16 | #endif 17 | #ifndef FMT_USE_WINDOWS_H 18 | #define FMT_USE_WINDOWS_H 0 19 | #endif 20 | #include "bundled/core.h" 21 | #include "bundled/format.h" 22 | #else // external fmtlib 23 | #include 24 | #include 25 | #endif 26 | -------------------------------------------------------------------------------- /include/spdlog/fmt/ostr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's ostream support 9 | // 10 | #if !defined(SPDLOG_FMT_EXTERNAL) 11 | #ifndef FMT_HEADER_ONLY 12 | #define FMT_HEADER_ONLY 13 | #endif 14 | #include "bundled/ostream.h" 15 | #include "fmt.h" 16 | #else 17 | #include 18 | #endif 19 | -------------------------------------------------------------------------------- /include/spdlog/formatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "fmt/fmt.h" 9 | #include "spdlog/details/log_msg.h" 10 | 11 | namespace spdlog { 12 | 13 | class formatter 14 | { 15 | public: 16 | virtual ~formatter() = default; 17 | virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0; 18 | virtual std::unique_ptr clone() const = 0; 19 | }; 20 | } // namespace spdlog 21 | -------------------------------------------------------------------------------- /include/spdlog/logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015-2108 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // Thread safe logger (except for set_pattern(..), set_formatter(..) and 9 | // set_error_handler()) 10 | // Has name, log level, vector of std::shared sink pointers and formatter 11 | // Upon each log write the logger: 12 | // 1. Checks if its log level is enough to log the message and if yes: 13 | // 2. Call the underlying sinks to do the job. 14 | // 3. Each sink use its own private copy of a formatter to format the message 15 | // and send to its destination. 16 | // 17 | // The use of private formatter per sink provides the opportunity to cache some 18 | // formatted data, 19 | // and support customize format per each sink. 20 | 21 | #include "spdlog/common.h" 22 | #include "spdlog/formatter.h" 23 | #include "spdlog/sinks/sink.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace spdlog { 30 | 31 | class logger 32 | { 33 | public: 34 | logger(std::string name, sink_ptr single_sink); 35 | logger(std::string name, sinks_init_list sinks); 36 | 37 | template 38 | logger(std::string name, It begin, It end); 39 | 40 | virtual ~logger(); 41 | 42 | logger(const logger &) = delete; 43 | logger &operator=(const logger &) = delete; 44 | 45 | template 46 | void log(level::level_enum lvl, const char *fmt, const Args &... args); 47 | 48 | template 49 | void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args); 50 | 51 | void log(level::level_enum lvl, const char *msg); 52 | 53 | void log(source_loc loc, level::level_enum lvl, const char *msg); 54 | 55 | template 56 | void trace(const char *fmt, const Args &... args); 57 | 58 | template 59 | void debug(const char *fmt, const Args &... args); 60 | 61 | template 62 | void info(const char *fmt, const Args &... args); 63 | 64 | template 65 | void warn(const char *fmt, const Args &... args); 66 | 67 | template 68 | void error(const char *fmt, const Args &... args); 69 | 70 | template 71 | void critical(const char *fmt, const Args &... args); 72 | 73 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT 74 | #ifndef _WIN32 75 | #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows 76 | #else 77 | template 78 | void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); 79 | 80 | template 81 | void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args); 82 | 83 | template 84 | void trace(const wchar_t *fmt, const Args &... args); 85 | 86 | template 87 | void debug(const wchar_t *fmt, const Args &... args); 88 | 89 | template 90 | void info(const wchar_t *fmt, const Args &... args); 91 | 92 | template 93 | void warn(const wchar_t *fmt, const Args &... args); 94 | 95 | template 96 | void error(const wchar_t *fmt, const Args &... args); 97 | 98 | template 99 | void critical(const wchar_t *fmt, const Args &... args); 100 | #endif // _WIN32 101 | #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT 102 | 103 | template 104 | void log(level::level_enum lvl, const T &); 105 | 106 | // T can be statically converted to string_view 107 | template::value, T>::type * = nullptr> 108 | void log(source_loc loc, level::level_enum lvl, const T &); 109 | 110 | // T cannot be statically converted to string_view 111 | template::value, T>::type * = nullptr> 112 | void log(source_loc loc, level::level_enum lvl, const T &); 113 | 114 | template 115 | void trace(const T &msg); 116 | 117 | template 118 | void debug(const T &msg); 119 | 120 | template 121 | void info(const T &msg); 122 | 123 | template 124 | void warn(const T &msg); 125 | 126 | template 127 | void error(const T &msg); 128 | 129 | template 130 | void critical(const T &msg); 131 | 132 | bool should_log(level::level_enum msg_level) const; 133 | void set_level(level::level_enum log_level); 134 | 135 | static level::level_enum default_level(); 136 | level::level_enum level() const; 137 | const std::string &name() const; 138 | 139 | // set formatting for the sinks in this logger. 140 | // each sink will get a seperate instance of the formatter object. 141 | void set_formatter(std::unique_ptr formatter); 142 | void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); 143 | 144 | // flush functions 145 | void flush(); 146 | void flush_on(level::level_enum log_level); 147 | level::level_enum flush_level() const; 148 | 149 | // sinks 150 | const std::vector &sinks() const; 151 | std::vector &sinks(); 152 | 153 | // error handler 154 | void set_error_handler(log_err_handler err_handler); 155 | log_err_handler error_handler() const; 156 | 157 | // create new logger with same sinks and configuration. 158 | virtual std::shared_ptr clone(std::string logger_name); 159 | 160 | protected: 161 | virtual void sink_it_(details::log_msg &msg); 162 | virtual void flush_(); 163 | 164 | bool should_flush_(const details::log_msg &msg); 165 | 166 | // default error handler. 167 | // print the error to stderr with the max rate of 1 message/minute. 168 | void default_err_handler_(const std::string &msg); 169 | 170 | // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) 171 | void incr_msg_counter_(details::log_msg &msg); 172 | 173 | const std::string name_; 174 | std::vector sinks_; 175 | spdlog::level_t level_{spdlog::logger::default_level()}; 176 | spdlog::level_t flush_level_{level::off}; 177 | log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }}; 178 | std::atomic last_err_time_{0}; 179 | std::atomic msg_counter_{1}; 180 | }; 181 | } // namespace spdlog 182 | 183 | #include "details/logger_impl.h" 184 | -------------------------------------------------------------------------------- /include/spdlog/sinks/android_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/fmt_helper.h" 13 | #include "spdlog/details/null_mutex.h" 14 | #include "spdlog/details/os.h" 15 | #include "spdlog/sinks/base_sink.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #if !defined(SPDLOG_ANDROID_RETRIES) 24 | #define SPDLOG_ANDROID_RETRIES 2 25 | #endif 26 | 27 | namespace spdlog { 28 | namespace sinks { 29 | 30 | /* 31 | * Android sink (logging using __android_log_write) 32 | */ 33 | template 34 | class android_sink final : public base_sink 35 | { 36 | public: 37 | explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) 38 | : tag_(std::move(tag)) 39 | , use_raw_msg_(use_raw_msg) 40 | { 41 | } 42 | 43 | protected: 44 | void sink_it_(const details::log_msg &msg) override 45 | { 46 | const android_LogPriority priority = convert_to_android_(msg.level); 47 | fmt::memory_buffer formatted; 48 | if (use_raw_msg_) 49 | { 50 | details::fmt_helper::append_string_view(msg.payload, formatted); 51 | } 52 | else 53 | { 54 | sink::formatter_->format(msg, formatted); 55 | } 56 | formatted.push_back('\0'); 57 | const char *msg_output = formatted.data(); 58 | 59 | // See system/core/liblog/logger_write.c for explanation of return value 60 | int ret = __android_log_write(priority, tag_.c_str(), msg_output); 61 | int retry_count = 0; 62 | while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES)) 63 | { 64 | details::os::sleep_for_millis(5); 65 | ret = __android_log_write(priority, tag_.c_str(), msg_output); 66 | retry_count++; 67 | } 68 | 69 | if (ret < 0) 70 | { 71 | throw spdlog_ex("__android_log_write() failed", ret); 72 | } 73 | } 74 | 75 | void flush_() override {} 76 | 77 | private: 78 | static android_LogPriority convert_to_android_(spdlog::level::level_enum level) 79 | { 80 | switch (level) 81 | { 82 | case spdlog::level::trace: 83 | return ANDROID_LOG_VERBOSE; 84 | case spdlog::level::debug: 85 | return ANDROID_LOG_DEBUG; 86 | case spdlog::level::info: 87 | return ANDROID_LOG_INFO; 88 | case spdlog::level::warn: 89 | return ANDROID_LOG_WARN; 90 | case spdlog::level::err: 91 | return ANDROID_LOG_ERROR; 92 | case spdlog::level::critical: 93 | return ANDROID_LOG_FATAL; 94 | default: 95 | return ANDROID_LOG_DEFAULT; 96 | } 97 | } 98 | 99 | std::string tag_; 100 | bool use_raw_msg_; 101 | }; 102 | 103 | using android_sink_mt = android_sink; 104 | using android_sink_st = android_sink; 105 | } // namespace sinks 106 | 107 | // Create and register android syslog logger 108 | 109 | template 110 | inline std::shared_ptr android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog") 111 | { 112 | return Factory::template create(logger_name, tag); 113 | } 114 | 115 | template 116 | inline std::shared_ptr android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog") 117 | { 118 | return Factory::template create(logger_name, tag); 119 | } 120 | 121 | } // namespace spdlog 122 | -------------------------------------------------------------------------------- /include/spdlog/sinks/ansicolor_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2017 spdlog authors. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/console_globals.h" 13 | #include "spdlog/details/null_mutex.h" 14 | #include "spdlog/details/os.h" 15 | #include "spdlog/sinks/sink.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace spdlog { 23 | namespace sinks { 24 | 25 | /** 26 | * This sink prefixes the output with an ANSI escape sequence color code 27 | * depending on the severity 28 | * of the message. 29 | * If no color terminal detected, omit the escape codes. 30 | */ 31 | template 32 | class ansicolor_sink final : public sink 33 | { 34 | public: 35 | using mutex_t = typename ConsoleMutex::mutex_t; 36 | ansicolor_sink() 37 | : target_file_(TargetStream::stream()) 38 | , mutex_(ConsoleMutex::mutex()) 39 | 40 | { 41 | should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal(); 42 | colors_[level::trace] = white; 43 | colors_[level::debug] = cyan; 44 | colors_[level::info] = green; 45 | colors_[level::warn] = yellow + bold; 46 | colors_[level::err] = red + bold; 47 | colors_[level::critical] = bold + on_red; 48 | colors_[level::off] = reset; 49 | } 50 | 51 | ~ansicolor_sink() override = default; 52 | 53 | ansicolor_sink(const ansicolor_sink &other) = delete; 54 | ansicolor_sink &operator=(const ansicolor_sink &other) = delete; 55 | 56 | void set_color(level::level_enum color_level, const std::string &color) 57 | { 58 | std::lock_guard lock(mutex_); 59 | colors_[color_level] = color; 60 | } 61 | 62 | /// Formatting codes 63 | const std::string reset = "\033[m"; 64 | const std::string bold = "\033[1m"; 65 | const std::string dark = "\033[2m"; 66 | const std::string underline = "\033[4m"; 67 | const std::string blink = "\033[5m"; 68 | const std::string reverse = "\033[7m"; 69 | const std::string concealed = "\033[8m"; 70 | const std::string clear_line = "\033[K"; 71 | 72 | // Foreground colors 73 | const std::string black = "\033[30m"; 74 | const std::string red = "\033[31m"; 75 | const std::string green = "\033[32m"; 76 | const std::string yellow = "\033[33m"; 77 | const std::string blue = "\033[34m"; 78 | const std::string magenta = "\033[35m"; 79 | const std::string cyan = "\033[36m"; 80 | const std::string white = "\033[37m"; 81 | 82 | /// Background colors 83 | const std::string on_black = "\033[40m"; 84 | const std::string on_red = "\033[41m"; 85 | const std::string on_green = "\033[42m"; 86 | const std::string on_yellow = "\033[43m"; 87 | const std::string on_blue = "\033[44m"; 88 | const std::string on_magenta = "\033[45m"; 89 | const std::string on_cyan = "\033[46m"; 90 | const std::string on_white = "\033[47m"; 91 | 92 | void log(const details::log_msg &msg) override 93 | { 94 | // Wrap the originally formatted message in color codes. 95 | // If color is not supported in the terminal, log as is instead. 96 | std::lock_guard lock(mutex_); 97 | 98 | fmt::memory_buffer formatted; 99 | formatter_->format(msg, formatted); 100 | if (should_do_colors_ && msg.color_range_end > msg.color_range_start) 101 | { 102 | // before color range 103 | print_range_(formatted, 0, msg.color_range_start); 104 | // in color range 105 | print_ccode_(colors_[msg.level]); 106 | print_range_(formatted, msg.color_range_start, msg.color_range_end); 107 | print_ccode_(reset); 108 | // after color range 109 | print_range_(formatted, msg.color_range_end, formatted.size()); 110 | } 111 | else // no color 112 | { 113 | print_range_(formatted, 0, formatted.size()); 114 | } 115 | fflush(target_file_); 116 | } 117 | 118 | void flush() override 119 | { 120 | std::lock_guard lock(mutex_); 121 | fflush(target_file_); 122 | } 123 | 124 | void set_pattern(const std::string &pattern) final 125 | { 126 | std::lock_guard lock(mutex_); 127 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 128 | } 129 | 130 | void set_formatter(std::unique_ptr sink_formatter) override 131 | { 132 | std::lock_guard lock(mutex_); 133 | formatter_ = std::move(sink_formatter); 134 | } 135 | 136 | bool should_color() 137 | { 138 | return should_do_colors_; 139 | } 140 | 141 | private: 142 | void print_ccode_(const std::string &color_code) 143 | { 144 | fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); 145 | } 146 | void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) 147 | { 148 | fwrite(formatted.data() + start, sizeof(char), end - start, target_file_); 149 | } 150 | 151 | FILE *target_file_; 152 | mutex_t &mutex_; 153 | 154 | bool should_do_colors_; 155 | std::unordered_map colors_; 156 | }; 157 | 158 | using ansicolor_stdout_sink_mt = ansicolor_sink; 159 | using ansicolor_stdout_sink_st = ansicolor_sink; 160 | 161 | using ansicolor_stderr_sink_mt = ansicolor_sink; 162 | using ansicolor_stderr_sink_st = ansicolor_sink; 163 | 164 | } // namespace sinks 165 | 166 | } // namespace spdlog 167 | -------------------------------------------------------------------------------- /include/spdlog/sinks/base_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // base sink templated over a mutex (either dummy or real) 9 | // concrete implementation should override the sink_it_() and flush_() methods. 10 | // locking is taken care of in this class - no locking needed by the 11 | // implementers.. 12 | // 13 | 14 | #include "spdlog/common.h" 15 | #include "spdlog/details/log_msg.h" 16 | #include "spdlog/formatter.h" 17 | #include "spdlog/sinks/sink.h" 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | template 22 | class base_sink : public sink 23 | { 24 | public: 25 | base_sink() = default; 26 | base_sink(const base_sink &) = delete; 27 | base_sink &operator=(const base_sink &) = delete; 28 | 29 | void log(const details::log_msg &msg) final 30 | { 31 | std::lock_guard lock(mutex_); 32 | sink_it_(msg); 33 | } 34 | 35 | void flush() final 36 | { 37 | std::lock_guard lock(mutex_); 38 | flush_(); 39 | } 40 | 41 | void set_pattern(const std::string &pattern) final 42 | { 43 | std::lock_guard lock(mutex_); 44 | set_pattern_(pattern); 45 | } 46 | 47 | void set_formatter(std::unique_ptr sink_formatter) final 48 | { 49 | std::lock_guard lock(mutex_); 50 | set_formatter_(std::move(sink_formatter)); 51 | } 52 | 53 | protected: 54 | virtual void sink_it_(const details::log_msg &msg) = 0; 55 | virtual void flush_() = 0; 56 | 57 | virtual void set_pattern_(const std::string &pattern) 58 | { 59 | set_formatter_(details::make_unique(pattern)); 60 | } 61 | 62 | virtual void set_formatter_(std::unique_ptr sink_formatter) 63 | { 64 | formatter_ = std::move(sink_formatter); 65 | } 66 | Mutex mutex_; 67 | }; 68 | } // namespace sinks 69 | } // namespace spdlog 70 | -------------------------------------------------------------------------------- /include/spdlog/sinks/basic_file_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015-2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/file_helper.h" 13 | #include "spdlog/details/null_mutex.h" 14 | #include "spdlog/sinks/base_sink.h" 15 | 16 | #include 17 | #include 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | /* 22 | * Trivial file sink with single file as target 23 | */ 24 | template 25 | class basic_file_sink final : public base_sink 26 | { 27 | public: 28 | explicit basic_file_sink(const filename_t &filename, bool truncate = false) 29 | { 30 | file_helper_.open(filename, truncate); 31 | } 32 | 33 | const filename_t &filename() const 34 | { 35 | return file_helper_.filename(); 36 | } 37 | 38 | protected: 39 | void sink_it_(const details::log_msg &msg) override 40 | { 41 | fmt::memory_buffer formatted; 42 | sink::formatter_->format(msg, formatted); 43 | file_helper_.write(formatted); 44 | } 45 | 46 | void flush_() override 47 | { 48 | file_helper_.flush(); 49 | } 50 | 51 | private: 52 | details::file_helper file_helper_; 53 | }; 54 | 55 | using basic_file_sink_mt = basic_file_sink; 56 | using basic_file_sink_st = basic_file_sink; 57 | 58 | } // namespace sinks 59 | 60 | // 61 | // factory functions 62 | // 63 | template 64 | inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false) 65 | { 66 | return Factory::template create(logger_name, filename, truncate); 67 | } 68 | 69 | template 70 | inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false) 71 | { 72 | return Factory::template create(logger_name, filename, truncate); 73 | } 74 | 75 | } // namespace spdlog 76 | -------------------------------------------------------------------------------- /include/spdlog/sinks/daily_file_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/file_helper.h" 13 | #include "spdlog/details/null_mutex.h" 14 | #include "spdlog/fmt/fmt.h" 15 | #include "spdlog/sinks/base_sink.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace spdlog { 24 | namespace sinks { 25 | 26 | /* 27 | * Generator of daily log file names in format basename.YYYY-MM-DD.ext 28 | */ 29 | struct daily_filename_calculator 30 | { 31 | // Create filename for the form basename.YYYY-MM-DD 32 | static filename_t calc_filename(const filename_t &filename, const tm &now_tm) 33 | { 34 | filename_t basename, ext; 35 | std::tie(basename, ext) = details::file_helper::split_by_extension(filename); 36 | std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; 37 | fmt::format_to( 38 | w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext); 39 | return fmt::to_string(w); 40 | } 41 | }; 42 | 43 | /* 44 | * Rotating file sink based on date. rotates at midnight 45 | */ 46 | template 47 | class daily_file_sink final : public base_sink 48 | { 49 | public: 50 | // create daily file sink which rotates on given time 51 | daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false) 52 | : base_filename_(std::move(base_filename)) 53 | , rotation_h_(rotation_hour) 54 | , rotation_m_(rotation_minute) 55 | , truncate_(truncate) 56 | { 57 | if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) 58 | { 59 | throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); 60 | } 61 | auto now = log_clock::now(); 62 | file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_); 63 | rotation_tp_ = next_rotation_tp_(); 64 | } 65 | 66 | const filename_t &filename() const 67 | { 68 | return file_helper_.filename(); 69 | } 70 | 71 | protected: 72 | void sink_it_(const details::log_msg &msg) override 73 | { 74 | 75 | if (msg.time >= rotation_tp_) 76 | { 77 | file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_); 78 | rotation_tp_ = next_rotation_tp_(); 79 | } 80 | fmt::memory_buffer formatted; 81 | sink::formatter_->format(msg, formatted); 82 | file_helper_.write(formatted); 83 | } 84 | 85 | void flush_() override 86 | { 87 | file_helper_.flush(); 88 | } 89 | 90 | private: 91 | tm now_tm(log_clock::time_point tp) 92 | { 93 | time_t tnow = log_clock::to_time_t(tp); 94 | return spdlog::details::os::localtime(tnow); 95 | } 96 | 97 | log_clock::time_point next_rotation_tp_() 98 | { 99 | auto now = log_clock::now(); 100 | tm date = now_tm(now); 101 | date.tm_hour = rotation_h_; 102 | date.tm_min = rotation_m_; 103 | date.tm_sec = 0; 104 | auto rotation_time = log_clock::from_time_t(std::mktime(&date)); 105 | if (rotation_time > now) 106 | { 107 | return rotation_time; 108 | } 109 | return {rotation_time + std::chrono::hours(24)}; 110 | } 111 | 112 | filename_t base_filename_; 113 | int rotation_h_; 114 | int rotation_m_; 115 | log_clock::time_point rotation_tp_; 116 | details::file_helper file_helper_; 117 | bool truncate_; 118 | }; 119 | 120 | using daily_file_sink_mt = daily_file_sink; 121 | using daily_file_sink_st = daily_file_sink; 122 | 123 | } // namespace sinks 124 | 125 | // 126 | // factory functions 127 | // 128 | template 129 | inline std::shared_ptr daily_logger_mt( 130 | const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) 131 | { 132 | return Factory::template create(logger_name, filename, hour, minute, truncate); 133 | } 134 | 135 | template 136 | inline std::shared_ptr daily_logger_st( 137 | const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) 138 | { 139 | return Factory::template create(logger_name, filename, hour, minute, truncate); 140 | } 141 | } // namespace spdlog 142 | -------------------------------------------------------------------------------- /include/spdlog/sinks/dist_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2015 David Schury, Gabi Melman 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "base_sink.h" 13 | #include "spdlog/details/log_msg.h" 14 | #include "spdlog/details/null_mutex.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | // Distribution sink (mux). Stores a vector of sinks which get called when log 22 | // is called 23 | 24 | namespace spdlog { 25 | namespace sinks { 26 | 27 | template 28 | class dist_sink : public base_sink 29 | { 30 | public: 31 | dist_sink() = default; 32 | dist_sink(const dist_sink &) = delete; 33 | dist_sink &operator=(const dist_sink &) = delete; 34 | 35 | void add_sink(std::shared_ptr sink) 36 | { 37 | std::lock_guard lock(base_sink::mutex_); 38 | sinks_.push_back(sink); 39 | } 40 | 41 | void remove_sink(std::shared_ptr sink) 42 | { 43 | std::lock_guard lock(base_sink::mutex_); 44 | sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); 45 | } 46 | 47 | void set_sinks(std::vector> sinks) 48 | { 49 | std::lock_guard lock(base_sink::mutex_); 50 | sinks_ = std::move(sinks); 51 | } 52 | 53 | protected: 54 | void sink_it_(const details::log_msg &msg) override 55 | { 56 | 57 | for (auto &sink : sinks_) 58 | { 59 | if (sink->should_log(msg.level)) 60 | { 61 | sink->log(msg); 62 | } 63 | } 64 | } 65 | 66 | void flush_() override 67 | { 68 | for (auto &sink : sinks_) 69 | { 70 | sink->flush(); 71 | } 72 | } 73 | 74 | void set_pattern_(const std::string &pattern) override 75 | { 76 | set_formatter_(details::make_unique(pattern)); 77 | } 78 | 79 | void set_formatter_(std::unique_ptr sink_formatter) override 80 | { 81 | base_sink::formatter_ = std::move(sink_formatter); 82 | for (auto &sink : sinks_) 83 | { 84 | sink->set_formatter(base_sink::formatter_->clone()); 85 | } 86 | } 87 | std::vector> sinks_; 88 | }; 89 | 90 | using dist_sink_mt = dist_sink; 91 | using dist_sink_st = dist_sink; 92 | 93 | } // namespace sinks 94 | } // namespace spdlog 95 | -------------------------------------------------------------------------------- /include/spdlog/sinks/msvc_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Alexander Dalshov. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #if defined(_WIN32) 13 | 14 | #include "spdlog/details/null_mutex.h" 15 | #include "spdlog/sinks/base_sink.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | namespace spdlog { 23 | namespace sinks { 24 | /* 25 | * MSVC sink (logging using OutputDebugStringA) 26 | */ 27 | template 28 | class msvc_sink : public base_sink 29 | { 30 | public: 31 | explicit msvc_sink() {} 32 | 33 | protected: 34 | void sink_it_(const details::log_msg &msg) override 35 | { 36 | 37 | fmt::memory_buffer formatted; 38 | sink::formatter_->format(msg, formatted); 39 | OutputDebugStringA(fmt::to_string(formatted).c_str()); 40 | } 41 | 42 | void flush_() override {} 43 | }; 44 | 45 | using msvc_sink_mt = msvc_sink; 46 | using msvc_sink_st = msvc_sink; 47 | 48 | using windebug_sink_mt = msvc_sink_mt; 49 | using windebug_sink_st = msvc_sink_st; 50 | 51 | } // namespace sinks 52 | } // namespace spdlog 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/spdlog/sinks/null_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/null_mutex.h" 13 | #include "spdlog/sinks/base_sink.h" 14 | 15 | #include 16 | 17 | namespace spdlog { 18 | namespace sinks { 19 | 20 | template 21 | class null_sink : public base_sink 22 | { 23 | protected: 24 | void sink_it_(const details::log_msg &) override {} 25 | void flush_() override {} 26 | }; 27 | 28 | using null_sink_mt = null_sink; 29 | using null_sink_st = null_sink; 30 | 31 | } // namespace sinks 32 | 33 | template 34 | inline std::shared_ptr null_logger_mt(const std::string &logger_name) 35 | { 36 | auto null_logger = Factory::template create(logger_name); 37 | null_logger->set_level(level::off); 38 | return null_logger; 39 | } 40 | 41 | template 42 | inline std::shared_ptr null_logger_st(const std::string &logger_name) 43 | { 44 | auto null_logger = Factory::template create(logger_name); 45 | null_logger->set_level(level::off); 46 | return null_logger; 47 | } 48 | 49 | } // namespace spdlog 50 | -------------------------------------------------------------------------------- /include/spdlog/sinks/ostream_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/null_mutex.h" 13 | #include "spdlog/sinks/base_sink.h" 14 | 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | namespace sinks { 20 | template 21 | class ostream_sink final : public base_sink 22 | { 23 | public: 24 | explicit ostream_sink(std::ostream &os, bool force_flush = false) 25 | : ostream_(os) 26 | , force_flush_(force_flush) 27 | { 28 | } 29 | ostream_sink(const ostream_sink &) = delete; 30 | ostream_sink &operator=(const ostream_sink &) = delete; 31 | 32 | protected: 33 | void sink_it_(const details::log_msg &msg) override 34 | { 35 | fmt::memory_buffer formatted; 36 | sink::formatter_->format(msg, formatted); 37 | ostream_.write(formatted.data(), static_cast(formatted.size())); 38 | if (force_flush_) 39 | { 40 | ostream_.flush(); 41 | } 42 | } 43 | 44 | void flush_() override 45 | { 46 | ostream_.flush(); 47 | } 48 | 49 | std::ostream &ostream_; 50 | bool force_flush_; 51 | }; 52 | 53 | using ostream_sink_mt = ostream_sink; 54 | using ostream_sink_st = ostream_sink; 55 | 56 | } // namespace sinks 57 | } // namespace spdlog 58 | -------------------------------------------------------------------------------- /include/spdlog/sinks/rotating_file_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/file_helper.h" 13 | #include "spdlog/details/null_mutex.h" 14 | #include "spdlog/fmt/fmt.h" 15 | #include "spdlog/sinks/base_sink.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace spdlog { 25 | namespace sinks { 26 | 27 | // 28 | // Rotating file sink based on size 29 | // 30 | template 31 | class rotating_file_sink final : public base_sink 32 | { 33 | public: 34 | rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open=false) 35 | : base_filename_(std::move(base_filename)) 36 | , max_size_(max_size) 37 | , max_files_(max_files) 38 | { 39 | file_helper_.open(calc_filename(base_filename_, 0)); 40 | current_size_ = file_helper_.size(); // expensive. called only once 41 | if (rotate_on_open && current_size_ > 0) 42 | { 43 | rotate_(); 44 | } 45 | } 46 | 47 | // calc filename according to index and file extension if exists. 48 | // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". 49 | static filename_t calc_filename(const filename_t &filename, std::size_t index) 50 | { 51 | typename std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; 52 | if (index != 0u) 53 | { 54 | filename_t basename, ext; 55 | std::tie(basename, ext) = details::file_helper::split_by_extension(filename); 56 | fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); 57 | } 58 | else 59 | { 60 | fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename); 61 | } 62 | return fmt::to_string(w); 63 | } 64 | 65 | const filename_t &filename() const 66 | { 67 | return file_helper_.filename(); 68 | } 69 | 70 | protected: 71 | void sink_it_(const details::log_msg &msg) override 72 | { 73 | fmt::memory_buffer formatted; 74 | sink::formatter_->format(msg, formatted); 75 | current_size_ += formatted.size(); 76 | if (current_size_ > max_size_) 77 | { 78 | rotate_(); 79 | current_size_ = formatted.size(); 80 | } 81 | file_helper_.write(formatted); 82 | } 83 | 84 | void flush_() override 85 | { 86 | file_helper_.flush(); 87 | } 88 | 89 | private: 90 | // Rotate files: 91 | // log.txt -> log.1.txt 92 | // log.1.txt -> log.2.txt 93 | // log.2.txt -> log.3.txt 94 | // log.3.txt -> delete 95 | void rotate_() 96 | { 97 | using details::os::filename_to_str; 98 | file_helper_.close(); 99 | for (auto i = max_files_; i > 0; --i) 100 | { 101 | filename_t src = calc_filename(base_filename_, i - 1); 102 | if (!details::file_helper::file_exists(src)) 103 | { 104 | continue; 105 | } 106 | filename_t target = calc_filename(base_filename_, i); 107 | 108 | if (!rename_file(src, target)) 109 | { 110 | // if failed try again after a small delay. 111 | // this is a workaround to a windows issue, where very high rotation 112 | // rates can cause the rename to fail with permission denied (because of antivirus?). 113 | details::os::sleep_for_millis(100); 114 | if (!rename_file(src, target)) 115 | { 116 | file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit! 117 | current_size_ = 0; 118 | throw spdlog_ex( 119 | "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); 120 | } 121 | } 122 | } 123 | file_helper_.reopen(true); 124 | } 125 | 126 | // delete the target if exists, and rename the src file to target 127 | // return true on success, false otherwise. 128 | bool rename_file(const filename_t &src_filename, const filename_t &target_filename) 129 | { 130 | // try to delete the target file in case it already exists. 131 | (void)details::os::remove(target_filename); 132 | return details::os::rename(src_filename, target_filename) == 0; 133 | } 134 | 135 | filename_t base_filename_; 136 | std::size_t max_size_; 137 | std::size_t max_files_; 138 | std::size_t current_size_; 139 | details::file_helper file_helper_; 140 | }; 141 | 142 | using rotating_file_sink_mt = rotating_file_sink; 143 | using rotating_file_sink_st = rotating_file_sink; 144 | 145 | } // namespace sinks 146 | 147 | // 148 | // factory functions 149 | // 150 | 151 | template 152 | inline std::shared_ptr rotating_logger_mt( 153 | const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open=false) 154 | { 155 | return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); 156 | } 157 | 158 | template 159 | inline std::shared_ptr rotating_logger_st( 160 | const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false) 161 | { 162 | return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); 163 | } 164 | } // namespace spdlog 165 | -------------------------------------------------------------------------------- /include/spdlog/sinks/sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "spdlog/details/log_msg.h" 9 | #include "spdlog/details/pattern_formatter.h" 10 | #include "spdlog/formatter.h" 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | class sink 15 | { 16 | public: 17 | sink() = default; 18 | 19 | explicit sink(std::unique_ptr formatter) 20 | : formatter_{std::move(formatter)} 21 | { 22 | } 23 | 24 | virtual ~sink() = default; 25 | virtual void log(const details::log_msg &msg) = 0; 26 | virtual void flush() = 0; 27 | virtual void set_pattern(const std::string &pattern) = 0; 28 | virtual void set_formatter(std::unique_ptr sink_formatter) = 0; 29 | 30 | bool should_log(level::level_enum msg_level) const 31 | { 32 | return msg_level >= level_.load(std::memory_order_relaxed); 33 | } 34 | 35 | void set_level(level::level_enum log_level) 36 | { 37 | level_.store(log_level); 38 | } 39 | 40 | level::level_enum level() const 41 | { 42 | return static_cast(level_.load(std::memory_order_relaxed)); 43 | } 44 | 45 | protected: 46 | // sink log level - default is all 47 | level_t level_{level::trace}; 48 | 49 | // sink formatter - default is full format 50 | std::unique_ptr formatter_{details::make_unique()}; 51 | }; 52 | 53 | } // namespace sinks 54 | } // namespace spdlog 55 | -------------------------------------------------------------------------------- /include/spdlog/sinks/stdout_color_sinks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 spdlog 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #ifdef _WIN32 13 | #include "spdlog/sinks/wincolor_sink.h" 14 | #else 15 | #include "spdlog/sinks/ansicolor_sink.h" 16 | #endif 17 | 18 | namespace spdlog { 19 | namespace sinks { 20 | #ifdef _WIN32 21 | using stdout_color_sink_mt = wincolor_stdout_sink_mt; 22 | using stdout_color_sink_st = wincolor_stdout_sink_st; 23 | using stderr_color_sink_mt = wincolor_stderr_sink_mt; 24 | using stderr_color_sink_st = wincolor_stderr_sink_st; 25 | #else 26 | using stdout_color_sink_mt = ansicolor_stdout_sink_mt; 27 | using stdout_color_sink_st = ansicolor_stdout_sink_st; 28 | using stderr_color_sink_mt = ansicolor_stderr_sink_mt; 29 | using stderr_color_sink_st = ansicolor_stderr_sink_st; 30 | #endif 31 | } // namespace sinks 32 | 33 | template 34 | inline std::shared_ptr stdout_color_mt(const std::string &logger_name) 35 | { 36 | return Factory::template create(logger_name); 37 | } 38 | 39 | template 40 | inline std::shared_ptr stdout_color_st(const std::string &logger_name) 41 | { 42 | return Factory::template create(logger_name); 43 | } 44 | 45 | template 46 | inline std::shared_ptr stderr_color_mt(const std::string &logger_name) 47 | { 48 | return Factory::template create(logger_name); 49 | } 50 | 51 | template 52 | inline std::shared_ptr stderr_color_st(const std::string &logger_name) 53 | { 54 | return Factory::template create(logger_name); 55 | } 56 | } // namespace spdlog 57 | -------------------------------------------------------------------------------- /include/spdlog/sinks/stdout_sinks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/details/console_globals.h" 13 | #include "spdlog/details/null_mutex.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace spdlog { 20 | 21 | namespace sinks { 22 | 23 | template 24 | class stdout_sink final : public sink 25 | { 26 | public: 27 | using mutex_t = typename ConsoleMutex::mutex_t; 28 | stdout_sink() 29 | : mutex_(ConsoleMutex::mutex()) 30 | , file_(TargetStream::stream()) 31 | { 32 | } 33 | ~stdout_sink() override = default; 34 | 35 | stdout_sink(const stdout_sink &other) = delete; 36 | stdout_sink &operator=(const stdout_sink &other) = delete; 37 | 38 | void log(const details::log_msg &msg) override 39 | { 40 | std::lock_guard lock(mutex_); 41 | fmt::memory_buffer formatted; 42 | formatter_->format(msg, formatted); 43 | fwrite(formatted.data(), sizeof(char), formatted.size(), file_); 44 | fflush(TargetStream::stream()); 45 | } 46 | 47 | void flush() override 48 | { 49 | std::lock_guard lock(mutex_); 50 | fflush(file_); 51 | } 52 | 53 | void set_pattern(const std::string &pattern) override 54 | { 55 | std::lock_guard lock(mutex_); 56 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 57 | } 58 | 59 | void set_formatter(std::unique_ptr sink_formatter) override 60 | { 61 | std::lock_guard lock(mutex_); 62 | formatter_ = std::move(sink_formatter); 63 | } 64 | 65 | private: 66 | mutex_t &mutex_; 67 | FILE *file_; 68 | }; 69 | 70 | using stdout_sink_mt = stdout_sink; 71 | using stdout_sink_st = stdout_sink; 72 | 73 | using stderr_sink_mt = stdout_sink; 74 | using stderr_sink_st = stdout_sink; 75 | 76 | } // namespace sinks 77 | 78 | // factory methods 79 | template 80 | inline std::shared_ptr stdout_logger_mt(const std::string &logger_name) 81 | { 82 | return Factory::template create(logger_name); 83 | } 84 | 85 | template 86 | inline std::shared_ptr stdout_logger_st(const std::string &logger_name) 87 | { 88 | return Factory::template create(logger_name); 89 | } 90 | 91 | template 92 | inline std::shared_ptr stderr_logger_mt(const std::string &logger_name) 93 | { 94 | return Factory::template create(logger_name); 95 | } 96 | 97 | template 98 | inline std::shared_ptr stderr_logger_st(const std::string &logger_name) 99 | { 100 | return Factory::template create(logger_name); 101 | } 102 | } // namespace spdlog 103 | -------------------------------------------------------------------------------- /include/spdlog/sinks/syslog_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/sinks/base_sink.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | namespace sinks { 20 | /** 21 | * Sink that write to syslog using the `syscall()` library call. 22 | * 23 | * Locking is not needed, as `syslog()` itself is thread-safe. 24 | */ 25 | template 26 | class syslog_sink : public base_sink 27 | { 28 | public: 29 | // 30 | explicit syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) 31 | : ident_(std::move(ident)) 32 | { 33 | priorities_[static_cast(level::trace)] = LOG_DEBUG; 34 | priorities_[static_cast(level::debug)] = LOG_DEBUG; 35 | priorities_[static_cast(level::info)] = LOG_INFO; 36 | priorities_[static_cast(level::warn)] = LOG_WARNING; 37 | priorities_[static_cast(level::err)] = LOG_ERR; 38 | priorities_[static_cast(level::critical)] = LOG_CRIT; 39 | priorities_[static_cast(level::off)] = LOG_INFO; 40 | 41 | // set ident to be program name if empty 42 | ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility); 43 | } 44 | 45 | ~syslog_sink() override 46 | { 47 | ::closelog(); 48 | } 49 | 50 | syslog_sink(const syslog_sink &) = delete; 51 | syslog_sink &operator=(const syslog_sink &) = delete; 52 | 53 | protected: 54 | void sink_it_(const details::log_msg &msg) override 55 | { 56 | ::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.payload).c_str()); 57 | } 58 | 59 | void flush_() override {} 60 | 61 | private: 62 | std::array priorities_; 63 | // must store the ident because the man says openlog might use the pointer as 64 | // is and not a string copy 65 | const std::string ident_; 66 | 67 | // 68 | // Simply maps spdlog's log level to syslog priority level. 69 | // 70 | int syslog_prio_from_level(const details::log_msg &msg) const 71 | { 72 | return priorities_[static_cast(msg.level)]; 73 | } 74 | }; 75 | 76 | using syslog_sink_mt = syslog_sink; 77 | using syslog_sink_st = syslog_sink; 78 | } // namespace sinks 79 | 80 | // Create and register a syslog logger 81 | template 82 | inline std::shared_ptr syslog_logger_mt( 83 | const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3)) 84 | { 85 | return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility); 86 | } 87 | 88 | template 89 | inline std::shared_ptr syslog_logger_st( 90 | const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3)) 91 | { 92 | return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility); 93 | } 94 | } // namespace spdlog 95 | -------------------------------------------------------------------------------- /include/spdlog/sinks/systemd_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2019 ZVYAGIN.Alexander@gmail.com 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/sinks/base_sink.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | namespace sinks { 20 | 21 | inline int syslog_level(level::level_enum l) { 22 | switch(l) { 23 | case level::off: 24 | case level::trace: 25 | case level::debug: 26 | return LOG_DEBUG; 27 | case level::info: 28 | return LOG_INFO; 29 | case level::warn: 30 | return LOG_WARNING; 31 | case level::err: 32 | return LOG_ERR; 33 | case level::critical: 34 | return LOG_CRIT; 35 | default: 36 | throw std::invalid_argument("systemd_sink.h syslog_level()"); 37 | } 38 | } 39 | 40 | /** 41 | * Sink that write to systemd using the `sd_journal_print()` library call. 42 | * 43 | * Locking is not needed, as `sd_journal_print()` itself is thread-safe. 44 | */ 45 | template 46 | class systemd_sink : public base_sink 47 | { 48 | public: 49 | // 50 | explicit systemd_sink(void) {} 51 | 52 | ~systemd_sink() override {} 53 | 54 | systemd_sink(const systemd_sink &) = delete; 55 | systemd_sink &operator=(const systemd_sink &) = delete; 56 | 57 | protected: 58 | void sink_it_(const details::log_msg &msg) override 59 | { 60 | if( sd_journal_print( 61 | syslog_level(msg.level), 62 | "%.*s", 63 | static_cast(msg.payload.size()), 64 | msg.payload.data() 65 | ) 66 | ) 67 | throw spdlog_ex("Failed writing to systemd"); 68 | } 69 | 70 | void flush_() override {} 71 | }; 72 | 73 | using systemd_sink_mt = systemd_sink; 74 | using systemd_sink_st = systemd_sink; 75 | } // namespace sinks 76 | 77 | // Create and register a syslog logger 78 | template 79 | inline std::shared_ptr systemd_logger_mt( 80 | const std::string &logger_name) 81 | { 82 | return Factory::template create(logger_name); 83 | } 84 | 85 | template 86 | inline std::shared_ptr systemd_logger_st( 87 | const std::string &logger_name) 88 | { 89 | return Factory::template create(logger_name); 90 | } 91 | } // namespace spdlog 92 | -------------------------------------------------------------------------------- /include/spdlog/sinks/wincolor_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 spdlog 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #ifndef SPDLOG_H 9 | #include "spdlog/spdlog.h" 10 | #endif 11 | 12 | #include "spdlog/common.h" 13 | #include "spdlog/details/console_globals.h" 14 | #include "spdlog/details/null_mutex.h" 15 | #include "spdlog/sinks/sink.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace spdlog { 24 | namespace sinks { 25 | /* 26 | * Windows color console sink. Uses WriteConsoleA to write to the console with 27 | * colors 28 | */ 29 | template 30 | class wincolor_sink : public sink 31 | { 32 | public: 33 | const WORD BOLD = FOREGROUND_INTENSITY; 34 | const WORD RED = FOREGROUND_RED; 35 | const WORD GREEN = FOREGROUND_GREEN; 36 | const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE; 37 | const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; 38 | const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; 39 | 40 | wincolor_sink() 41 | : out_handle_(OutHandle::handle()) 42 | , mutex_(ConsoleMutex::mutex()) 43 | { 44 | colors_[level::trace] = WHITE; 45 | colors_[level::debug] = CYAN; 46 | colors_[level::info] = GREEN; 47 | colors_[level::warn] = YELLOW | BOLD; 48 | colors_[level::err] = RED | BOLD; // red bold 49 | colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background 50 | colors_[level::off] = 0; 51 | } 52 | 53 | ~wincolor_sink() override 54 | { 55 | this->flush(); 56 | } 57 | 58 | wincolor_sink(const wincolor_sink &other) = delete; 59 | wincolor_sink &operator=(const wincolor_sink &other) = delete; 60 | 61 | // change the color for the given level 62 | void set_color(level::level_enum level, WORD color) 63 | { 64 | std::lock_guard lock(mutex_); 65 | colors_[level] = color; 66 | } 67 | 68 | void log(const details::log_msg &msg) final override 69 | { 70 | std::lock_guard lock(mutex_); 71 | fmt::memory_buffer formatted; 72 | formatter_->format(msg, formatted); 73 | if (msg.color_range_end > msg.color_range_start) 74 | { 75 | // before color range 76 | print_range_(formatted, 0, msg.color_range_start); 77 | 78 | // in color range 79 | auto orig_attribs = set_console_attribs(colors_[msg.level]); 80 | print_range_(formatted, msg.color_range_start, msg.color_range_end); 81 | ::SetConsoleTextAttribute(out_handle_, 82 | orig_attribs); // reset to orig colors 83 | // after color range 84 | print_range_(formatted, msg.color_range_end, formatted.size()); 85 | } 86 | else // print without colors if color range is invalid 87 | { 88 | print_range_(formatted, 0, formatted.size()); 89 | } 90 | } 91 | 92 | void flush() final override 93 | { 94 | // windows console always flushed? 95 | } 96 | 97 | void set_pattern(const std::string &pattern) override final 98 | { 99 | std::lock_guard lock(mutex_); 100 | formatter_ = std::unique_ptr(new pattern_formatter(pattern)); 101 | } 102 | 103 | void set_formatter(std::unique_ptr sink_formatter) override final 104 | { 105 | std::lock_guard lock(mutex_); 106 | formatter_ = std::move(sink_formatter); 107 | } 108 | 109 | private: 110 | using mutex_t = typename ConsoleMutex::mutex_t; 111 | // set color and return the orig console attributes (for resetting later) 112 | WORD set_console_attribs(WORD attribs) 113 | { 114 | CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; 115 | ::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); 116 | WORD back_color = orig_buffer_info.wAttributes; 117 | // retrieve the current background color 118 | back_color &= static_cast(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)); 119 | // keep the background color unchanged 120 | ::SetConsoleTextAttribute(out_handle_, attribs | back_color); 121 | return orig_buffer_info.wAttributes; // return orig attribs 122 | } 123 | 124 | // print a range of formatted message to console 125 | void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) 126 | { 127 | auto size = static_cast(end - start); 128 | ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr); 129 | } 130 | 131 | HANDLE out_handle_; 132 | mutex_t &mutex_; 133 | std::unordered_map colors_; 134 | }; 135 | 136 | using wincolor_stdout_sink_mt = wincolor_sink; 137 | using wincolor_stdout_sink_st = wincolor_sink; 138 | 139 | using wincolor_stderr_sink_mt = wincolor_sink; 140 | using wincolor_stderr_sink_st = wincolor_sink; 141 | 142 | } // namespace sinks 143 | } // namespace spdlog 144 | -------------------------------------------------------------------------------- /include/spdlog/tweakme.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | /////////////////////////////////////////////////////////////////////////////// 9 | // 10 | // Edit this file to squeeze more performance, and to customize supported 11 | // features 12 | // 13 | /////////////////////////////////////////////////////////////////////////////// 14 | 15 | /////////////////////////////////////////////////////////////////////////////// 16 | // Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. 17 | // This clock is less accurate - can be off by dozens of millis - depending on 18 | // the kernel HZ. 19 | // Uncomment to use it instead of the regular clock. 20 | // 21 | // #define SPDLOG_CLOCK_COARSE 22 | /////////////////////////////////////////////////////////////////////////////// 23 | 24 | /////////////////////////////////////////////////////////////////////////////// 25 | // Uncomment if date/time logging is not needed and never appear in the log 26 | // pattern. 27 | // This will prevent spdlog from querying the clock on each log call. 28 | // 29 | // WARNING: If the log pattern contains any date/time while this flag is on, the 30 | // result is undefined. 31 | // You must set new pattern(spdlog::set_pattern(..") without any 32 | // date/time in it 33 | // 34 | // #define SPDLOG_NO_DATETIME 35 | /////////////////////////////////////////////////////////////////////////////// 36 | 37 | /////////////////////////////////////////////////////////////////////////////// 38 | // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). 39 | // This will prevent spdlog from querying the thread id on each log call. 40 | // 41 | // WARNING: If the log pattern contains thread id (i.e, %t) while this flag is 42 | // on, the result is undefined. 43 | // 44 | // #define SPDLOG_NO_THREAD_ID 45 | /////////////////////////////////////////////////////////////////////////////// 46 | 47 | /////////////////////////////////////////////////////////////////////////////// 48 | // Uncomment to prevent spdlog from using thread local storage. 49 | // 50 | // WARNING: if your program forks, UNCOMMENT this flag to prevent undefined 51 | // thread ids in the children logs. 52 | // 53 | // #define SPDLOG_NO_TLS 54 | /////////////////////////////////////////////////////////////////////////////// 55 | 56 | /////////////////////////////////////////////////////////////////////////////// 57 | // Uncomment if logger name logging is not needed. 58 | // This will prevent spdlog from copying the logger name on each log call. 59 | // 60 | // #define SPDLOG_NO_NAME 61 | /////////////////////////////////////////////////////////////////////////////// 62 | 63 | /////////////////////////////////////////////////////////////////////////////// 64 | // Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. 65 | // 66 | // #define SPDLOG_DEBUG_ON 67 | // #define SPDLOG_TRACE_ON 68 | /////////////////////////////////////////////////////////////////////////////// 69 | 70 | /////////////////////////////////////////////////////////////////////////////// 71 | // Uncomment to avoid spdlog's usage of atomic log levels 72 | // Use only if your code never modifies a logger's log levels concurrently by 73 | // different threads. 74 | // 75 | // #define SPDLOG_NO_ATOMIC_LEVELS 76 | /////////////////////////////////////////////////////////////////////////////// 77 | 78 | /////////////////////////////////////////////////////////////////////////////// 79 | // Uncomment to enable usage of wchar_t for file names on Windows. 80 | // 81 | // #define SPDLOG_WCHAR_FILENAMES 82 | /////////////////////////////////////////////////////////////////////////////// 83 | 84 | /////////////////////////////////////////////////////////////////////////////// 85 | // Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) 86 | // 87 | // #define SPDLOG_EOL ";-)\n" 88 | /////////////////////////////////////////////////////////////////////////////// 89 | 90 | /////////////////////////////////////////////////////////////////////////////// 91 | // Uncomment to use your own copy of the fmt library instead of spdlog's copy. 92 | // In this case spdlog will try to include so set your -I flag 93 | // accordingly. 94 | // 95 | // #define SPDLOG_FMT_EXTERNAL 96 | /////////////////////////////////////////////////////////////////////////////// 97 | 98 | /////////////////////////////////////////////////////////////////////////////// 99 | // Uncomment to enable wchar_t support (convert to utf8) 100 | // 101 | // #define SPDLOG_WCHAR_TO_UTF8_SUPPORT 102 | /////////////////////////////////////////////////////////////////////////////// 103 | 104 | /////////////////////////////////////////////////////////////////////////////// 105 | // Uncomment to prevent child processes from inheriting log file descriptors 106 | // 107 | // #define SPDLOG_PREVENT_CHILD_FD 108 | /////////////////////////////////////////////////////////////////////////////// 109 | 110 | /////////////////////////////////////////////////////////////////////////////// 111 | // Uncomment to enable message counting feature. 112 | // Use the %i in the logger pattern to display log message sequence id. 113 | // 114 | // #define SPDLOG_ENABLE_MESSAGE_COUNTER 115 | /////////////////////////////////////////////////////////////////////////////// 116 | 117 | /////////////////////////////////////////////////////////////////////////////// 118 | // Uncomment to customize level names (e.g. "MT TRACE") 119 | // 120 | // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", 121 | // "MY ERROR", "MY CRITICAL", "OFF" } 122 | /////////////////////////////////////////////////////////////////////////////// 123 | 124 | /////////////////////////////////////////////////////////////////////////////// 125 | // Uncomment to customize short level names (e.g. "MT") 126 | // These can be longer than one character. 127 | // 128 | // #define SPDLOG_SHORT_LEVEL_NAMES { "T", "D", "I", "W", "E", "C", "O" } 129 | /////////////////////////////////////////////////////////////////////////////// 130 | 131 | /////////////////////////////////////////////////////////////////////////////// 132 | // Uncomment to disable default logger creation. 133 | // This might save some (very) small initialization time if no default logger is needed. 134 | // 135 | // #define SPDLOG_DISABLE_DEFAULT_LOGGER 136 | /////////////////////////////////////////////////////////////////////////////// 137 | 138 | /////////////////////////////////////////////////////////////////////////////// 139 | // Uncomment and set to compile time level with zero cost (default is INFO). 140 | // Macros like SPDLOG_DEBUG(..), SPDLOG_INFO(..) will expand to empty statements if not enabled 141 | // 142 | // #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO 143 | /////////////////////////////////////////////////////////////////////////////// 144 | 145 | /////////////////////////////////////////////////////////////////////////////// 146 | // Uncomment (and change if desired) macro to use for function names. 147 | // This is compiler dependent. 148 | // __PRETTY_FUNCTION__ might be nicer in clang/gcc, and __FUNCTION__ in msvc. 149 | // Defaults to __FUNCTION__ (should work on all compilers) if not defined. 150 | // 151 | // #define SPDLOG_FUNCTION __PRETTY_FUNCTION__ 152 | /////////////////////////////////////////////////////////////////////////////// 153 | -------------------------------------------------------------------------------- /include/spdlog/version.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #define SPDLOG_VER_MAJOR 1 9 | #define SPDLOG_VER_MINOR 3 10 | #define SPDLOG_VER_PATCH 1 11 | 12 | #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) 13 | -------------------------------------------------------------------------------- /include/tbassert.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 MIT License by Tao B. Schardl 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | **/ 22 | 23 | #ifndef _TBASSERT_H_ 24 | #define _TBASSERT_H_ 1 25 | 26 | #ifndef NDEBUG 27 | 28 | /************************************************************************** 29 | * Library of debugging macros. 30 | *************************************************************************/ 31 | 32 | #include 33 | #include 34 | 35 | // Print a message to STREAM when DEBUG = 1. This macro takes the 36 | // same arguments as FPRINTF(). 37 | #define DEBUG_FPRINTF(STREAM, ...) \ 38 | do { \ 39 | fprintf(STREAM, "%s:%d (%s) ", \ 40 | __FILE__, __LINE__, __PRETTY_FUNCTION__); \ 41 | fprintf(STREAM, __VA_ARGS__); \ 42 | } while (0) 43 | 44 | // Print a message to STDERR when DEBUG = 1. This macro takes the 45 | // same arguments as PRINTF(). 46 | #define DEBUG_EPRINTF(...) DEBUG_FPRINTF(stderr, __VA_ARGS__); 47 | 48 | // If PREDICATE is true, do nothing. Otherwise, print an error with 49 | // the specified message to STDERR. This macro only operates when 50 | // DEBUG = 1. This macro takes a PREDICATE to evaluate followed by 51 | // the standard arguments to PRINTF(). 52 | #define DEBUG_ASSERT(PREDICATE, ...) \ 53 | do { \ 54 | if (!(PREDICATE)) { \ 55 | fprintf(stderr, "%s:%d (%s) Assertion " #PREDICATE " failed: ", \ 56 | __FILE__, __LINE__, __PRETTY_FUNCTION__); \ 57 | fprintf(stderr, __VA_ARGS__); \ 58 | abort(); \ 59 | } \ 60 | } while (0) 61 | 62 | #define tbassert DEBUG_ASSERT 63 | 64 | #else 65 | 66 | #define DEBUG_PRINTF(...) // Nothing. 67 | #define DEBUG_EPRINTF(...) // Nothing. 68 | #define DEBUG_ASSERT(...) // Nothing. 69 | #define tbassert(...) // Nothing. 70 | 71 | #endif 72 | 73 | #endif // _TBASSERT_H_ 74 | -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _UTIL_H_ 3 | #define _UTIL_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | // #include 13 | 14 | #include "spdlog/spdlog.h" 15 | 16 | #ifdef DEBUG_MODE 17 | #define PRINT_DEBUG 1 18 | #else 19 | #define PRINT_DEBUG 0 20 | #endif 21 | 22 | #define SDSL_BITVECTOR_BLOCK_SIZE 127 23 | extern std::shared_ptr console; 24 | 25 | #define DEBUG(x) do { \ 26 | if (PRINT_DEBUG) { std::cerr << x << std::endl; } \ 27 | } while (0) 28 | 29 | #define ERROR(x) do { \ 30 | { std::cerr << x << std::endl; } \ 31 | } while (0) 32 | 33 | #define PRINT(x) do { \ 34 | { std::cout << x << std::endl; } \ 35 | } while (0) 36 | 37 | #if PRINT_DEBUG == 1 38 | //#define dprintf(fmt, args...) fprintf(stderr, fmt, ##args) 39 | #define dprintf(fmt, args...) /* Don't do anything in release builds */ 40 | #else 41 | #define dprintf(fmt, args...) /* Don't do anything in release builds */ 42 | #endif 43 | 44 | // find index of first 1-bit (least significant bit) 45 | static inline int bsf_word(int word) { 46 | int result; 47 | __asm__ volatile("bsf %1, %0" : "=r"(result) : "r"(word)); 48 | return result; 49 | } 50 | 51 | static inline int bsr_word(int word) { 52 | int result; 53 | __asm__ volatile("bsr %1, %0" : "=r"(result) : "r"(word)); 54 | return result; 55 | } 56 | static inline uint32_t bsr_word(uint32_t word) { 57 | uint32_t result; 58 | __asm__ volatile("bsr %1, %0" : "=r"(result) : "r"(word)); 59 | return result; 60 | } 61 | static inline uint64_t bsr_word(uint64_t word) { 62 | uint64_t result; 63 | __asm__ volatile("bsr %1, %0" : "=r"(result) : "r"(word)); 64 | return result; 65 | } 66 | 67 | typedef struct _pair_int { 68 | uint32_t x; // length in array 69 | uint32_t y; // depth 70 | } pair_int; 71 | typedef struct _pair_uint { 72 | uint32_t x; // length in array 73 | uint32_t y; // depth 74 | } pair_uint; 75 | 76 | // three tuple uint 77 | struct trip_uint { 78 | uint32_t x; 79 | uint32_t y; 80 | uint32_t z; 81 | } ; 82 | 83 | namespace graphstore { 84 | float cal_time_elapsed(struct timeval* start, struct timeval* end); 85 | /* Print elapsed time using the start and end timeval */ 86 | void print_time_elapsed(std::string desc, struct timeval* start, struct 87 | timeval* end); 88 | std::vector get_random_permutation(uint32_t num); 89 | } 90 | #endif 91 | -------------------------------------------------------------------------------- /lib/libcilkrts.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CGCL-codes/LSGraph/6a34627ad366caac2dbe1a4939bd2d2193f34367/lib/libcilkrts.so -------------------------------------------------------------------------------- /lib/libcilkrts.so.5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CGCL-codes/LSGraph/6a34627ad366caac2dbe1a4939bd2d2193f34367/lib/libcilkrts.so.5 -------------------------------------------------------------------------------- /obj/LSGraph.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CGCL-codes/LSGraph/6a34627ad366caac2dbe1a4939bd2d2193f34367/obj/LSGraph.o -------------------------------------------------------------------------------- /obj/util.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CGCL-codes/LSGraph/6a34627ad366caac2dbe1a4939bd2d2193f34367/obj/util.o -------------------------------------------------------------------------------- /src/HITree/HITree.h: -------------------------------------------------------------------------------- 1 | #ifndef HITREE_H 2 | #define HITREE_H 3 | 4 | #include "HITree_nodes.h" 5 | 6 | namespace hitreespace { 7 | 8 | template 9 | class HITree { 10 | typedef std::pair KVT; 11 | private: 12 | TNode* root_; 13 | HyperParameter hyper_para_; 14 | public: 15 | HITree() : root_(nullptr) { } 16 | 17 | ~HITree() { 18 | if (root_ != nullptr) { 19 | delete root_; 20 | } 21 | } 22 | 23 | template 24 | void map(F &f){ 25 | root_->map_tree(f); 26 | } 27 | template 28 | void map_dense(F &f){ 29 | bool ret = false; 30 | root_->map_tree_dense(f, ret); 31 | // root_->map_tree_dense(f); 32 | } 33 | 34 | typename hitreespace::TNode::TNodeIterator begin() { 35 | return root_->begin(); 36 | } 37 | 38 | void bulk_load(const KT* ks, uint32_t size, int32_t bucket_size=-1, 39 | uint32_t aggregate_size=0) { 40 | assert_p(root_ == nullptr, "The index must be empty before bulk loading"); 41 | root_ = new TNode(); 42 | if (bucket_size == -1) { 43 | hyper_para_.max_bucket_size_ = compute_bucket_size(ks, size); 44 | } else { 45 | hyper_para_.max_bucket_size_ = std::min(std::max( 46 | static_cast(bucket_size), 47 | hyper_para_.kMinBucketSize), 48 | hyper_para_.kMaxBucketSize); 49 | } 50 | hyper_para_.aggregate_size_ = aggregate_size; 51 | root_->build(ks, size, 1, hyper_para_); 52 | } 53 | 54 | bool find(KT key) { 55 | return root_->find(key); 56 | } 57 | 58 | void print_hitree(std::vector&nei) { 59 | root_->print_hitree(nei); 60 | } 61 | 62 | KT get_first_key() { 63 | return root_->get_first_key(); 64 | } 65 | 66 | uint64_t get_ima() { 67 | return root_->get_ima(); 68 | } 69 | 70 | uint64_t get_size() { 71 | return root_->get_size(); 72 | } 73 | 74 | void visit_edges() { 75 | root_->visit_edges(); 76 | } 77 | uint32_t get_index_num() { 78 | return root_->index_num_; 79 | } 80 | 81 | void visit_edges_test(std::vector &array){ 82 | root_->visit_edges_test(array); 83 | } 84 | 85 | bool update(KVT kv) { 86 | return root_->update(kv); 87 | } 88 | 89 | uint32_t remove(KT key) { 90 | return root_->remove(key); 91 | } 92 | 93 | TNode* get_root() { 94 | return root_; 95 | } 96 | 97 | void insert(KT k) { 98 | root_->insert(k, 1, hyper_para_); 99 | } 100 | 101 | void print_stats() { 102 | TreeStat ts; 103 | ts.bucket_size_ = hyper_para_.max_bucket_size_; 104 | collect_tree_statistics(root_, 1, ts); 105 | ts.show(); 106 | } 107 | 108 | uint64_t model_size() { 109 | TreeStat ts; 110 | ts.bucket_size_ = hyper_para_.max_bucket_size_; 111 | collect_tree_statistics(root_, 1, ts); 112 | return ts.model_size_; 113 | } 114 | 115 | uint64_t index_size() { 116 | TreeStat ts; 117 | ts.bucket_size_ = hyper_para_.max_bucket_size_; 118 | collect_tree_statistics(root_, 1, ts); 119 | return ts.index_size_; 120 | } 121 | 122 | uint64_t num_confilicts() { 123 | TreeStat ts; 124 | ts.bucket_size_ = hyper_para_.max_bucket_size_; 125 | collect_tree_statistics(root_, 1, ts); 126 | return ts.node_conflicts_; 127 | } 128 | 129 | private: 130 | uint8_t compute_bucket_size(const KT* ks, uint32_t size) { 131 | uint32_t tail_conflicts = compute_tail_conflicts(ks, size, 132 | hyper_para_.kSizeAmplification, 133 | hyper_para_.kTailPercent); 134 | tail_conflicts = std::min(hyper_para_.kMaxBucketSize, tail_conflicts); 135 | tail_conflicts = std::max(hyper_para_.kMinBucketSize, tail_conflicts); 136 | return tail_conflicts; 137 | } 138 | 139 | uint32_t collect_tree_statistics(TNode* node, 140 | uint32_t depth, TreeStat& ts) { 141 | if (node->model_ != nullptr) { 142 | // Model node 143 | ts.num_model_nodes_ ++; 144 | ts.model_size_ += sizeof(TNode) + sizeof(LinearModel); 145 | ts.index_size_ += sizeof(TNode) + sizeof(LinearModel) 146 | + sizeof(BIT_TYPE) * 2 * BIT_LEN(node->capacity_) 147 | + sizeof(Entry) * node->capacity_; 148 | bool is_leaf_node = true; 149 | uint32_t tot_kvs = 0; 150 | uint32_t tot_conflicts = 0; 151 | uint32_t num_conflicts = 0; 152 | for (uint32_t i = 0; i < node->capacity_; ++ i) { 153 | uint8_t type = node->entry_type(i); 154 | if (type == Unsued) { 155 | // Empty slot 156 | continue; 157 | } else if (type == Edge) { 158 | // Data slot 159 | ts.num_data_model_ ++; 160 | ts.sum_depth_ += depth; 161 | tot_kvs ++; 162 | continue; 163 | } else if (type == Block) { 164 | // Bucket pointer 165 | ts.num_buckets_ ++; 166 | ts.num_data_bucket_ += node->entries_[i].bucket_->size_; 167 | ts.model_size_ += sizeof(Bucket); 168 | ts.index_size_ += sizeof(Bucket) 169 | + sizeof(KVT) * hyper_para_.max_bucket_size_; 170 | ts.sum_depth_ += (depth + 1) * node->entries_[i].bucket_->size_; 171 | tot_kvs += node->entries_[i].bucket_->size_; 172 | tot_conflicts += node->entries_[i].bucket_->size_ - 1; 173 | num_conflicts ++; 174 | } else if (type == HTNode) { 175 | // Child node pointer 176 | uint32_t num_kvs_child = collect_tree_statistics( 177 | node->entries_[i].child_, depth + 1, ts); 178 | tot_conflicts += num_kvs_child; 179 | num_conflicts ++; 180 | is_leaf_node = false; 181 | // Find the duplicated child node pointers 182 | uint32_t j = i + 1; 183 | for (; j < node->size_; ++ j, num_conflicts ++) { 184 | uint8_t type_j = node->entry_type(j); 185 | if (type_j != HTNode 186 | || node->entries_[j].child_ != node->entries_[i].child_) { 187 | break; 188 | } 189 | } 190 | i = j - 1; 191 | } 192 | } 193 | ts.node_conflicts_ += num_conflicts ? 194 | tot_conflicts * 1. / num_conflicts : 0; 195 | if (is_leaf_node) { 196 | ts.num_leaf_nodes_ ++; 197 | ts.max_depth_ = std::max(ts.max_depth_, depth); 198 | } 199 | return tot_conflicts; 200 | } else { 201 | // Dense node 202 | ts.num_dense_nodes_ ++; 203 | ts.num_data_dense_ ++; 204 | uint32_t tot_conflicts = 0; 205 | for (uint32_t i = 1; i < node->size_; ++ i) { 206 | if (!compare(node->entries_[i].kv_.first, 207 | node->entries_[i - 1].kv_.first)) { 208 | ts.num_data_dense_ ++; 209 | tot_conflicts ++; 210 | } 211 | } 212 | ts.sum_depth_ += depth * tot_conflicts; 213 | ts.node_conflicts_ += tot_conflicts; 214 | ts.model_size_ += sizeof(TNode); 215 | ts.index_size_ += sizeof(TNode) 216 | + sizeof(Entry) * node->capacity_; 217 | ts.num_leaf_nodes_ ++; 218 | ts.sum_depth_ += depth; 219 | ts.max_depth_ = std::max(ts.max_depth_, depth); 220 | return tot_conflicts; 221 | } 222 | } 223 | }; 224 | 225 | } 226 | #endif 227 | -------------------------------------------------------------------------------- /src/HITree/blocks.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_H 2 | #define BLOCK_H 3 | 4 | #include "iterator.h" 5 | #include "common.h" 6 | 7 | namespace hitreespace { 8 | 9 | // #define KT uint32_t 10 | template 11 | inline void make_block(KT* data, const KT* ks, uint32_t size) { 12 | data[0] = size; 13 | for (uint32_t i = 0; i < size; ++ i) { 14 | data[i+1] = ks[i]; 15 | } 16 | } 17 | 18 | template 19 | bool block_find(KT* data, KT key) { 20 | for (uint32_t i = 0; i < data[0]; ++ i) { 21 | if (compare(data[i+1], key)) { 22 | return true; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | template 29 | bool block_insert(KT* data, KT k) { 30 | uint32_t size = data[0]; 31 | KT* data_ = data+1; 32 | for (uint32_t i = 0; i < size; ++i) { 33 | if (compare(data_[i], k)) { 34 | return true; 35 | } 36 | } 37 | uint32_t capacity = (GRAIN / sizeof(KT)) - 1; 38 | if (size < capacity) { 39 | data_[size] = k; 40 | data[0] ++; 41 | return true; 42 | } else { 43 | return false; 44 | } 45 | } 46 | 47 | template 48 | uint32_t block_remove(KT* data, KT k) { 49 | bool copy = false; 50 | uint32_t size = data[0]; 51 | KT* data_ = data+1; 52 | for (uint32_t i = 0; i < size; ++ i) { 53 | if (compare(data_[i], k)) { 54 | copy = true; 55 | } 56 | if (copy && i + 1 < size) { 57 | data_[i] = data_[i + 1]; 58 | } 59 | } 60 | if (copy) { 61 | data[0] --; 62 | return 1; 63 | } else { 64 | return 0; 65 | } 66 | } 67 | 68 | template 69 | bool block_insert_sort(KT* data, KT k) { 70 | uint32_t capacity = (GRAIN / sizeof(KT)) - 1; 71 | 72 | uint32_t size = data[0]; 73 | KT* data_ = data+1; 74 | // must use int instead of uint32_t 75 | for (int i = 0; i < size; ++i) { 76 | if (compare(data_[i], k)) { 77 | return true; 78 | } else if (data_[i] > k) { 79 | if (size >= capacity) return false; 80 | // printf("%d %d\n", i, size); 81 | for (int j = size-1; j >= i; --j){ 82 | data_[j+1] = data_[j]; 83 | } 84 | data_[i] = k; 85 | data[0]++; 86 | return true; 87 | } 88 | } 89 | 90 | if (size < capacity) { 91 | data_[size] = k; 92 | data[0] ++; 93 | return true; 94 | } else { 95 | return false; 96 | } 97 | } 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/HITree/buckets.h: -------------------------------------------------------------------------------- 1 | #ifndef BUCKET_H 2 | #define BUCKET_H 3 | 4 | #include "iterator.h" 5 | #include "common.h" 6 | 7 | namespace hitreespace { 8 | 9 | template 10 | class Bucket { 11 | typedef std::pair KVT; 12 | public: 13 | KVT* data_; 14 | uint8_t size_; 15 | 16 | public: 17 | Bucket() : data_(nullptr), size_(0) { } 18 | 19 | Bucket(const KVT* kvs, uint32_t size, const uint8_t capacity) 20 | : size_(size) { 21 | data_ = new KVT[capacity]; 22 | for (uint32_t i = 0; i < size; ++ i) { 23 | data_[i] = kvs[i]; 24 | } 25 | } 26 | 27 | ~Bucket() { 28 | if (data_ != nullptr) { 29 | delete[] data_; 30 | data_ = nullptr; 31 | } 32 | } 33 | 34 | inline uint8_t size() const { return size_; } 35 | 36 | ResultIterator find(KT key) { 37 | for (uint32_t i = 0; i < size_; ++ i) { 38 | if (compare(data_[i].first, key)) { 39 | return {&data_[i]}; 40 | } 41 | } 42 | return {}; 43 | } 44 | 45 | bool update(KVT kv) { 46 | for (uint8_t i = 0; i < size_; ++ i) { 47 | if (compare(data_[i].first, kv.first)) { 48 | data_[i] = kv; 49 | return true; 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | uint32_t remove(KT key) { 56 | bool copy = false; 57 | for (uint8_t i = 0; i < size_; ++ i) { 58 | if (compare(data_[i].first, key)) { 59 | copy = true; 60 | } 61 | if (copy && i + 1 < size_) { 62 | data_[i] = data_[i + 1]; 63 | } 64 | } 65 | if (copy) { 66 | size_ --; 67 | return 1; 68 | } else { 69 | return 0; 70 | } 71 | } 72 | 73 | bool insert(KVT kv, const uint8_t capacity) { 74 | for (uint32_t i = 0; i < size_; ++i) { 75 | if (compare(data_[i].first, kv.first)) { 76 | return true; 77 | } 78 | } 79 | if (size_ < capacity) { 80 | data_[size_] = kv; 81 | size_ ++; 82 | return true; 83 | } else { 84 | return false; 85 | } 86 | } 87 | }; 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/HITree/conflicts.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFLICTS_H 2 | #define CONFLICTS_H 3 | 4 | #include "common.h" 5 | #include "linear_model.h" 6 | 7 | namespace hitreespace { 8 | 9 | struct ConflictsInfo { 10 | uint32_t* conflicts_; 11 | uint32_t* positions_; 12 | uint32_t num_conflicts_; 13 | uint32_t max_size_; 14 | uint32_t size_; 15 | 16 | ConflictsInfo(uint32_t size, uint32_t max_size) 17 | : max_size_(max_size), num_conflicts_(0) { 18 | size_ = size; 19 | conflicts_ = new uint32_t[size]; 20 | positions_ = new uint32_t[size]; 21 | } 22 | 23 | ~ConflictsInfo() { 24 | delete[] conflicts_; 25 | delete[] positions_; 26 | } 27 | 28 | void add_conflict(uint32_t position, uint32_t conflict) { 29 | conflicts_[num_conflicts_] = conflict; 30 | positions_[num_conflicts_] = position; 31 | num_conflicts_ ++; 32 | } 33 | }; 34 | 35 | template 36 | ConflictsInfo* build_linear_model(const KT* ks, uint32_t size, 37 | LinearModel*& model, 38 | double size_amp) { 39 | if (model != nullptr) { 40 | model->slope_ = model->intercept_ = 0; 41 | } else { 42 | model = new LinearModel(); 43 | } 44 | // OPT: Find a linear regression model that has the minimum conflict degree 45 | // The heuristics below is a simple method that scales the positions 46 | KT min_key = ks[0]; 47 | KT max_key = ks[size - 1]; 48 | KT key_space = max_key - min_key; 49 | if (compare(min_key, max_key)) { 50 | delete model; 51 | model = nullptr; 52 | return nullptr; 53 | } 54 | uint32_t max_size = static_cast(size * size_amp); 55 | max_size = PALIGN_UP(max_size, GRAIN / sizeof(KT)); 56 | LinearModelBuilder builder; 57 | uint32_t j = 0; 58 | for (uint32_t i = 0; i < size; ++ i) { 59 | KT key = ks[i]; 60 | // double y = max_size * (key - min_key) / key_space; 61 | double y = i; 62 | builder.add(key, y); 63 | } 64 | builder.build(model); 65 | if (compare(model->slope_, 0.)) { 66 | // Fail to build a linear model 67 | delete model; 68 | model = nullptr; 69 | return nullptr; 70 | } else { 71 | // model->intercept_ = -model->slope_ * (min_key) + 0.5; 72 | // int64_t predicted_size = model->predict(max_key) + 1; 73 | // printf("%d, %d\n", predicted_size, max_size); 74 | // if (predicted_size > 1) { 75 | // max_size = std::min(predicted_size, static_cast(max_size)); 76 | // } 77 | // model->intercept_ = -model->slope_ * (min_key) + 0.5; 78 | int64_t predicted_size = model->predict(max_key) + 1; 79 | // printf("%lf, %lf, %d, %d\n", model->slope_, model->intercept_, predicted_size, max_size); 80 | if (predicted_size > 1) { 81 | model->slope_ *= (1.0 * max_size / predicted_size); 82 | model->intercept_ = -model->slope_ * (min_key) + 0.5; 83 | // max_size = std::min(predicted_size, static_cast(max_size)); 84 | } else { 85 | // printf("predicted_size = %lld\n", predicted_size); 86 | } 87 | // if (predicted_size < size * 1.2) { 88 | // model->slope_ *= (1.0 * max_size / predicted_size); 89 | // model->intercept_ = -model->slope_ * (min_key) + 0.5; 90 | // } else { 91 | // // model->intercept_ = -model->slope_ * (min_key) + 0.5; 92 | // max_size = std::min(predicted_size, static_cast(max_size)); 93 | // } 94 | uint32_t first_pos = std::min(std::max(model->predict(min_key), 0L), 95 | static_cast(max_size - 1)); 96 | uint32_t last_pos = std::min(std::max(model->predict(max_key), 0L), 97 | static_cast(max_size - 1)); 98 | if (last_pos == first_pos) { 99 | // Model fails to predict since all predicted positions are rounded to the 100 | // same one 101 | model->slope_ = size / (max_key - min_key); 102 | model->intercept_ = -model->slope_ * (min_key) + 0.5; 103 | } 104 | ConflictsInfo* ci = new ConflictsInfo(size, max_size); 105 | uint32_t p_last = first_pos; 106 | uint32_t conflict = 1; 107 | for (uint32_t i = 1; i < size; ++ i) { 108 | uint32_t p = std::min(std::max(model->predict(ks[i]), 0L), 109 | static_cast(max_size - 1)); 110 | if (p == p_last) { 111 | conflict ++; 112 | } else { 113 | ci->add_conflict(p_last, conflict); 114 | p_last = p; 115 | conflict = 1; 116 | } 117 | } 118 | if (conflict > 0) { 119 | ci->add_conflict(p_last, conflict); 120 | } 121 | return ci; 122 | } 123 | } 124 | 125 | template 126 | uint32_t compute_tail_conflicts(const KT* ks, uint32_t size, 127 | double size_amp, float kTailPercent=0.99) { 128 | // The input keys should be ordered 129 | LinearModel* model = new LinearModel(); 130 | ConflictsInfo* ci = build_linear_model(ks, size, model, size_amp); 131 | delete model; 132 | 133 | if (ci == nullptr) { 134 | return 0; 135 | } else if (ci->num_conflicts_ == 0) { 136 | // printf("size = %d %f\n", size, size_amp); 137 | delete ci; 138 | return 0; 139 | } else { 140 | std::sort(ci->conflicts_, ci->conflicts_ + ci->num_conflicts_); 141 | uint32_t tail_conflicts = ci->conflicts_[std::max(0, int(ci->num_conflicts_ * kTailPercent) - 1)]; 142 | delete ci; 143 | return tail_conflicts - 1; 144 | } 145 | } 146 | 147 | } 148 | #endif 149 | -------------------------------------------------------------------------------- /src/HITree/iterator.h: -------------------------------------------------------------------------------- 1 | #ifndef ITERATOR_H 2 | #define ITERATOR_H 3 | 4 | #include "common.h" 5 | 6 | namespace hitreespace { 7 | 8 | template 9 | class ResultIterator { 10 | typedef std::pair KVT; 11 | private: 12 | KVT* kv_; 13 | 14 | public: 15 | ResultIterator() : kv_(nullptr) { } 16 | 17 | ResultIterator(KVT* kv) : kv_(kv) { } 18 | 19 | bool is_end() { return kv_ == nullptr; } 20 | 21 | KT key() { return kv_->first; } 22 | 23 | VT value() { return kv_->second; } 24 | 25 | VT* value_addr() { return &kv_->second; } 26 | 27 | KVT* kv() { return kv_; } 28 | 29 | ResultIterator& operator=(const ResultIterator& other) { 30 | if (this != &other) { 31 | kv_ = other.kv_; 32 | } 33 | return *this; 34 | } 35 | }; 36 | 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /src/HITree/linear_model.h: -------------------------------------------------------------------------------- 1 | #ifndef LINEAR_MODEL_H 2 | #define LINEAR_MODEL_H 3 | 4 | #include "common.h" 5 | 6 | namespace hitreespace { 7 | 8 | template 9 | class LinearModel { 10 | public: 11 | double slope_; 12 | double intercept_; 13 | 14 | LinearModel() : slope_(0), intercept_(0) { } 15 | 16 | inline int64_t predict(KT key) const { 17 | return static_cast(std::floor(slope_ * key + intercept_)); 18 | } 19 | 20 | inline double predict_double(KT key) const { 21 | return slope_ * static_cast(key) + intercept_; 22 | } 23 | }; 24 | 25 | template 26 | class LinearModelBuilder { 27 | public: 28 | int count_; 29 | double x_sum_; 30 | double y_sum_; 31 | double xx_sum_; 32 | double xy_sum_; 33 | KT x_min_; 34 | KT x_max_; 35 | double y_min_; 36 | double y_max_; 37 | 38 | LinearModelBuilder() : count_(0), x_sum_(0), y_sum_(0), xx_sum_(0), xy_sum_(0), 39 | x_min_(std::numeric_limits::max()), 40 | x_max_(std::numeric_limits::lowest()), 41 | y_min_(std::numeric_limits::max()), 42 | y_max_(std::numeric_limits::lowest()) { } 43 | 44 | inline void add(KT x, double y) { 45 | count_++; 46 | x_sum_ += static_cast(x); 47 | y_sum_ += static_cast(y); 48 | xx_sum_ += static_cast(x) * x; 49 | xy_sum_ += static_cast(x) * y; 50 | x_min_ = std::min(x, x_min_); 51 | x_max_ = std::max(x, x_max_); 52 | y_min_ = std::min(y, y_min_); 53 | y_max_ = std::max(y, y_max_); 54 | } 55 | 56 | // TODO: the calculated slope or intercept is too small or too large, the 57 | // precision is lost. 58 | void build(LinearModel *lrm) { 59 | if (count_ <= 1) { 60 | lrm->slope_ = 0; 61 | lrm->intercept_ = static_cast(y_sum_); 62 | return; 63 | } 64 | 65 | if (static_cast(count_) * xx_sum_ - x_sum_ * x_sum_ == 0) { 66 | // all values in a bucket have the same key. 67 | lrm->slope_ = 0; 68 | lrm->intercept_ = static_cast(y_sum_) / count_; 69 | return; 70 | } 71 | 72 | auto slope = static_cast( 73 | (static_cast(count_) * xy_sum_ - x_sum_ * y_sum_) / 74 | (static_cast(count_) * xx_sum_ - x_sum_ * x_sum_)); 75 | auto intercept = static_cast( 76 | (y_sum_ - static_cast(slope) * x_sum_) / count_); 77 | lrm->slope_ = slope; 78 | lrm->intercept_ = intercept; 79 | 80 | // If floating point precision errors, fit spline 81 | if (lrm->slope_ <= 0) { 82 | lrm->slope_ = 1. * (y_max_ - y_min_) / (x_max_ - x_min_); 83 | lrm->intercept_ = -static_cast(x_min_) * lrm->slope_; 84 | } 85 | } 86 | }; 87 | 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /src/util.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "util.h" 20 | 21 | namespace graphstore { 22 | /*return the integer representation of the base */ 23 | 24 | float cal_time_elapsed(struct timeval* start, struct timeval* end) 25 | { 26 | struct timeval elapsed; 27 | if (start->tv_usec > end->tv_usec) { 28 | end->tv_usec += 1000000; 29 | end->tv_sec--; 30 | } 31 | elapsed.tv_usec = end->tv_usec - start->tv_usec; 32 | elapsed.tv_sec = end->tv_sec - start->tv_sec; 33 | return (elapsed.tv_sec * 1000000 + elapsed.tv_usec)/1000000.f; 34 | } 35 | 36 | void print_time_elapsed(std::string desc, struct timeval* start, struct 37 | timeval* end) 38 | { 39 | struct timeval elapsed; 40 | if (start->tv_usec > end->tv_usec) { 41 | end->tv_usec += 1000000; 42 | end->tv_sec--; 43 | } 44 | elapsed.tv_usec = end->tv_usec - start->tv_usec; 45 | elapsed.tv_sec = end->tv_sec - start->tv_sec; 46 | float time_elapsed = (elapsed.tv_sec * 1000000 + elapsed.tv_usec)/1000000.f; 47 | std::cout << desc << "Total Time Elapsed: " << std::to_string(time_elapsed) << 48 | " seconds" << std::endl; 49 | std::cout << std::flush; 50 | } 51 | 52 | std::vector get_random_permutation(uint32_t num) { 53 | std::vector perm(num); 54 | std::vector vec(num); 55 | 56 | for (uint32_t i = 0; i < num; i++) 57 | vec[i] = i; 58 | 59 | uint32_t cnt{0}; 60 | while (vec.size()) { 61 | uint32_t n = vec.size(); 62 | srand(time(NULL)); 63 | uint32_t idx = rand() % n; 64 | uint32_t val = vec[idx]; 65 | std::swap(vec[idx], vec[n-1]); 66 | vec.pop_back(); 67 | perm[cnt++] = val; 68 | } 69 | return perm; 70 | } 71 | } 72 | --------------------------------------------------------------------------------