1 //
2 // libsemigroups - C++ library for semigroups and monoids
3 // Copyright (C) 2019 James D. Mitchell
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #include "libsemigroups/report.hpp"
20 
21 #include <iostream>
22 
23 #include "libsemigroups/libsemigroups-config.hpp"
24 
25 namespace libsemigroups {
26   detail::Reporter        REPORTER;
27   detail::ThreadIdManager THREAD_ID_MANAGER;
28 
29   namespace detail {
30 
ThreadIdManager()31     ThreadIdManager::ThreadIdManager() : _mtx(), _next_tid(0), _thread_map() {
32       tid(std::this_thread::get_id());
33     }
34 
reset()35     void ThreadIdManager::reset() {
36       // Only do this from the main thread
37       LIBSEMIGROUPS_ASSERT(tid(std::this_thread::get_id()) == 0);
38       // Delete all thread_ids
39       _thread_map.clear();
40       _next_tid = 0;
41       // Reinsert the main thread's id
42       tid(std::this_thread::get_id());
43     }
44 
tid(std::thread::id t)45     size_t ThreadIdManager::tid(std::thread::id t) {
46       std::lock_guard<std::mutex> lg(_mtx);
47       auto                        it = _thread_map.find(t);
48       if (it != _thread_map.end()) {
49         return (*it).second;
50       } else {
51         // Don't check the assert below because on a single thread machine
52         // (such as those used by appveyor), for an fp-semigroup more than 1
53         // thread will be used, and this assertion will fail.
54         // LIBSEMIGROUPS_ASSERT(_next_tid <=
55         // std::thread::hardware_concurrency());
56         _thread_map.emplace(t, _next_tid++);
57         return _next_tid - 1;
58       }
59     }
60 
Reporter(bool report)61     Reporter::Reporter(bool report)
62         : _last_msg(), _mtx(), _msg(), _options(), _report(report) {}
63 
64 #ifdef LIBSEMIGROUPS_FMT_ENABLED
color(fmt::color c)65     Reporter& Reporter::color(fmt::color c) {
66       if (_report) {
67         size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id());
68         resize(tid + 1);
69         _options[tid].color = c;
70       }
71       return *this;
72     }
73 
thread_color()74     Reporter& Reporter::thread_color() {
75       if (_report) {
76         std::lock_guard<std::mutex> lg(_mtx);
77         size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id());
78         resize(tid + 1);
79         _options[tid].color = thread_colors[tid % thread_colors.size()];
80       }
81       return *this;
82     }
83 #endif
84 
prefix()85     Reporter& Reporter::prefix() {
86       if (_report) {
87         std::lock_guard<std::mutex> lg(_mtx);
88         size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id());
89         resize(tid + 1);
90         _options[tid].prefix = "";
91       }
92       return *this;
93     }
94 
flush_right()95     Reporter& Reporter::flush_right() {
96       if (_report) {
97         std::lock_guard<std::mutex> lg(_mtx);
98         size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id());
99         resize(tid + 1);
100         _options[tid].flush_right = true;
101       }
102       return *this;
103     }
104 
flush()105     void Reporter::flush() {
106       if (_report) {
107         std::lock_guard<std::mutex> lg(_mtx);
108         size_t tid = THREAD_ID_MANAGER.tid(std::this_thread::get_id());
109         size_t pad = 0;
110         _msg[tid]  = _options[tid].prefix + _msg[tid];
111         if (_options[tid].flush_right
112             && _last_msg[tid].size() + unicode_string_length(_msg[tid]) < 80) {
113           pad = (80 - _last_msg[tid].size()) - unicode_string_length(_msg[tid]);
114           _msg[tid] = std::string(pad, ' ') + _msg[tid];
115         }
116 #ifdef LIBSEMIGROUPS_VERBOSE
117         if (_msg[tid].back() != '\n') {
118           _msg[tid] += "\n";
119         }
120 #endif
121         _msg[tid] = wrap(_options[tid].prefix.length(), _msg[tid]);
122 #ifdef LIBSEMIGROUPS_FMT_ENABLED
123         fmt::print(fg(_options[tid].color), _msg[tid]);
124 #else
125         std::cout << _msg[tid];
126 #endif
127         _options[tid] = Options();
128       }
129     }
130 
resize(size_t n)131     void Reporter::resize(size_t n) {
132       if (n > _msg.size()) {
133         _last_msg.resize(n);
134         _msg.resize(n);
135         _options.resize(n);
136       }
137     }
138   }  // namespace detail
139 }  // namespace libsemigroups
140