1 /*********************                                                        */
2 /*! \file safe_print.cpp
3  ** \verbatim
4  ** Top contributors (to current version):
5  **   Andres Noetzli, Andres Noetzli
6  ** This file is part of the CVC4 project.
7  ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8  ** in the top-level source directory) and their institutional affiliations.
9  ** All rights reserved.  See the file COPYING in the top-level source
10  ** directory for licensing information.\endverbatim
11  **
12  ** \brief Definition of print functions that are safe to use in a signal
13  ** handler.
14  **
15  ** Signal handlers only allow a very limited set of operations, e.g. dynamic
16  ** memory allocation is not possible. This set of functions can be used to
17  ** print information from a signal handler. All output is written to file
18  ** descriptors using the async-signal-safe write() function.
19  **/
20 
21 #include "safe_print.h"
22 
23 #include <unistd.h>
24 
25 /* Size of buffers used */
26 #define BUFFER_SIZE 20
27 
28 namespace CVC4 {
29 
30 template <>
safe_print(int fd,const std::string & msg)31 void safe_print(int fd, const std::string& msg) {
32   // Print characters one by one instead of using
33   // string::data()/string::c_str() to avoid allocations (pre-c++11)
34   for (size_t i = 0; i < msg.length(); i++) {
35     if (write(fd, &(msg[i]), 1) != 1) {
36       abort();
37     }
38   }
39 }
40 
41 template <>
safe_print(int fd,const int64_t & _i)42 void safe_print(int fd, const int64_t& _i) {
43   char buf[BUFFER_SIZE];
44   int64_t i = _i;
45 
46   if (i == 0) {
47     safe_print(fd, "0");
48     return;
49   } else if (i < 0) {
50     safe_print(fd, "-");
51     i *= -1;
52   }
53 
54   // This loop fills the buffer from the end. The number of elements in the
55   // buffer is BUFER_SIZE - idx - 1 and they start at position idx + 1.
56   ssize_t idx = BUFFER_SIZE - 1;
57   while (i != 0 && idx >= 0) {
58     buf[idx] = '0' + i % 10;
59     i /= 10;
60     idx--;
61   }
62 
63   ssize_t nbyte = BUFFER_SIZE - idx - 1;
64   if (write(fd, buf + idx + 1, nbyte) != nbyte) {
65     abort();
66   }
67 }
68 
69 template <>
safe_print(int fd,const int32_t & i)70 void safe_print(int fd, const int32_t& i) {
71   safe_print<int64_t>(fd, i);
72 }
73 
74 template <>
safe_print(int fd,const uint64_t & _i)75 void safe_print(int fd, const uint64_t& _i) {
76   char buf[BUFFER_SIZE];
77   uint64_t i = _i;
78 
79   if (i == 0) {
80     safe_print(fd, "0");
81     return;
82   }
83 
84   // This loop fills the buffer from the end. The number of elements in the
85   // buffer is BUFER_SIZE - idx - 1 and they start at position idx + 1.
86   ssize_t idx = BUFFER_SIZE - 1;
87   while (i != 0 && idx >= 0) {
88     buf[idx] = '0' + i % 10;
89     i /= 10;
90     idx--;
91   }
92 
93   ssize_t nbyte = BUFFER_SIZE - idx - 1;
94   if (write(fd, buf + idx + 1, nbyte) != nbyte) {
95     abort();
96   }
97 }
98 
99 template <>
safe_print(int fd,const uint32_t & i)100 void safe_print(int fd, const uint32_t& i) {
101   safe_print<uint64_t>(fd, i);
102 }
103 
104 template <>
safe_print(int fd,const double & _d)105 void safe_print(int fd, const double& _d) {
106   // Note: this print function for floating-point values is optimized for
107   // simplicity, not correctness or performance.
108   char buf[BUFFER_SIZE];
109   double d = _d;
110 
111   ssize_t i = 0;
112   int64_t v = static_cast<int64_t>(d);
113   d -= v;
114 
115   if (d < 0.0) {
116     d *= -1.0;
117   }
118 
119   safe_print<int64_t>(fd, v);
120   safe_print(fd, ".");
121 
122   // Print decimal digits as long as the remaining value is larger than zero
123   // and print at least one digit.
124   while (i == 0 || (d > 0.0 && i < BUFFER_SIZE)) {
125     d *= 10.0;
126     char c = static_cast<char>(d);
127     buf[i] = '0' + c;
128     d -= c;
129     i++;
130   }
131 
132   if (write(fd, buf, i) != i) {
133     abort();
134   }
135 }
136 
137 template <>
safe_print(int fd,const float & f)138 void safe_print(int fd, const float& f) {
139   safe_print<double>(fd, (double)f);
140 }
141 
142 template <>
safe_print(int fd,const bool & b)143 void safe_print(int fd, const bool& b) {
144   if (b) {
145     safe_print(fd, "true");
146   } else {
147     safe_print(fd, "false");
148   }
149 }
150 
151 template <>
safe_print(int fd,void * const & addr)152 void safe_print(int fd, void* const& addr) {
153   safe_print_hex(fd, (uint64_t)addr);
154 }
155 
156 template <>
safe_print(int fd,const timespec & t)157 void safe_print(int fd, const timespec& t) {
158   safe_print<uint64_t>(fd, t.tv_sec);
159   safe_print(fd, ".");
160   safe_print_right_aligned(fd, t.tv_nsec, 9);
161 }
162 
safe_print_hex(int fd,uint64_t i)163 void safe_print_hex(int fd, uint64_t i) {
164   char buf[BUFFER_SIZE];
165 
166   safe_print(fd, "0x");
167   if (i == 0) {
168     safe_print(fd, "0");
169     return;
170   }
171 
172   // This loop fills the buffer from the end. The number of elements in the
173   // buffer is BUFER_SIZE - idx - 1 and they start at position idx + 1.
174   ssize_t idx = BUFFER_SIZE - 1;
175   while (i != 0 && idx >= 0) {
176     char current = i % 16;
177     if (current <= 9) {
178       buf[idx] = '0' + current;
179     } else {
180       buf[idx] = 'a' + current - 10;
181     }
182     i /= 16;
183     idx--;
184   }
185 
186   ssize_t nbyte = BUFFER_SIZE - idx - 1;
187   if (write(fd, buf + idx + 1, nbyte) != nbyte) {
188     abort();
189   }
190 }
191 
safe_print_right_aligned(int fd,uint64_t i,ssize_t width)192 void safe_print_right_aligned(int fd, uint64_t i, ssize_t width) {
193   char buf[BUFFER_SIZE];
194 
195   // Make sure that the result fits in the buffer
196   width = (width < BUFFER_SIZE) ? width : BUFFER_SIZE;
197 
198   for (ssize_t j = 0; j < width; j++) {
199     buf[j] = '0';
200   }
201 
202   ssize_t idx = width - 1;
203   while (i != 0 && idx >= 0) {
204     buf[idx] = '0' + i % 10;
205     i /= 10;
206     idx--;
207   }
208 
209   if (write(fd, buf, width) != width) {
210     abort();
211   }
212 }
213 
214 } /* CVC4 namespace */
215