1 #ifndef UTILS_H
2 #define UTILS_H
3
4 #include <algorithm>
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <string>
10 #include <vector>
11 #include <Rcpp.h>
12 #include "optional.h"
13 #include "thread.h"
14 #include "timegm.h"
15
16 // A callback for deleting objects on the main thread using later(). This is
17 // needed when the object is an Rcpp object or contains one, because deleting
18 // such objects invoke R's memory management functions.
19 template <typename T>
deleter_main(void * obj)20 void deleter_main(void* obj) {
21 ASSERT_MAIN_THREAD()
22 // later() passes a void* to the callback, so we have to cast it.
23 T* typed_obj = reinterpret_cast<T*>(obj);
24
25 try {
26 delete typed_obj;
27 } catch (...) {}
28 }
29
30 // Does the same as deleter_main, but checks that it's running on the
31 // background thread (when thread debugging is enabled).
32 template <typename T>
deleter_background(void * obj)33 void deleter_background(void* obj) {
34 ASSERT_BACKGROUND_THREAD()
35 T* typed_obj = reinterpret_cast<T*>(obj);
36
37 try {
38 delete typed_obj;
39 } catch (...) {}
40 }
41
42 // It's not safe to call REprintf from the background thread but we need some
43 // way to output error messages. R CMD check does not it if the code uses the
44 // symbols stdout, stderr, and printf, so this function is a way to avoid
45 // those. It's to calling `fprintf(stderr, ...)`.
err_printf(const char * fmt,...)46 inline void err_printf(const char *fmt, ...) {
47 const size_t max_size = 4096;
48 char buf[max_size];
49
50 va_list args;
51 va_start(args, fmt);
52 int n = vsnprintf(buf, max_size, fmt, args);
53 va_end(args);
54
55 if (n == -1)
56 return;
57
58 ssize_t res = write(STDERR_FILENO, buf, n);
59 // This is here simply to avoid a warning about "ignoring return value" of
60 // the write(), on some compilers. (Seen with gcc 4.4.7 on RHEL 6)
61 res += 0;
62 }
63
64
65 // ============================================================================
66 // Logging
67 // ============================================================================
68
69 enum LogLevel {
70 LOG_OFF,
71 LOG_ERROR,
72 LOG_WARN,
73 LOG_INFO,
74 LOG_DEBUG
75 };
76
77 void debug_log(const std::string& msg, LogLevel level);
78
79 // ============================================================================
80
81
82 // Indexing into an empty vector causes assertion failures on some platforms
83 template <typename T>
safe_vec_addr(std::vector<T> & vec)84 T* safe_vec_addr(std::vector<T>& vec) {
85 return vec.size() ? &vec[0] : NULL;
86 }
87
88 // Indexing into an empty vector causes assertion failures on some platforms
safe_str_addr(const std::string & str)89 inline const char* safe_str_addr(const std::string& str) {
90 return str.size() ? &str[0] : NULL;
91 }
92
to_lower(const std::string & str)93 inline std::string to_lower(const std::string& str) {
94 std::string lowered = str;
95 std::transform(lowered.begin(), lowered.end(), lowered.begin(), tolower);
96 return lowered;
97 }
98
99 template <typename T>
toString(T x)100 std::string toString(T x) {
101 std::stringstream ss;
102 ss << x;
103 return ss.str();
104 }
105
106 // This is used for converting an Rcpp named vector (T2) to a std::map.
107 template <typename T1, typename T2>
toMap(T2 x)108 std::map<std::string, T1> toMap(T2 x) {
109 ASSERT_MAIN_THREAD()
110
111 std::map<std::string, T1> strmap;
112
113 if (x.size() == 0) {
114 return strmap;
115 }
116
117 Rcpp::CharacterVector names = x.names();
118 if (names.isNULL()) {
119 throw Rcpp::exception("Error converting R object to map<string, T>: vector does not have names.");
120 }
121
122 for (int i=0; i<x.size(); i++) {
123 std::string name = Rcpp::as<std::string>(names[i]);
124 T1 value = Rcpp::as<T1> (x[i]);
125 if (name == "") {
126 throw Rcpp::exception("Error converting R object to map<string, T>: element has empty name.");
127 }
128
129 strmap.insert(
130 std::pair<std::string, T1>(name, value)
131 );
132 }
133
134 return strmap;
135 }
136
137 // A wrapper for Rcpp::as. If the R value is NULL, this returns nullopt;
138 // otherwise it returns the usual value that Rcpp::as returns, wrapped in
139 // std::experimental::optional<T2>.
140 template <typename T1, typename T2>
optional_as(T2 value)141 std::experimental::optional<T1> optional_as(T2 value) {
142 if (value.isNULL()) {
143 return std::experimental::nullopt;
144 }
145 return std::experimental::optional<T1>( Rcpp::as<T1>(value) );
146 }
147
148 // A wrapper for Rcpp::wrap. If the C++ value is missing, this returns the
149 // R value NULL; otherwise it returns the usual value that Rcpp::wrap returns, after
150 // unwrapping from the std::experimental::optional<T>.
151 template <typename T>
optional_wrap(std::experimental::optional<T> value)152 Rcpp::RObject optional_wrap(std::experimental::optional<T> value) {
153 if (!value.has_value()) {
154 return R_NilValue;
155 }
156 return Rcpp::wrap(*value);
157 }
158
159
160 // as() and wrap() for ResponseHeaders. Since the ResponseHeaders typedef is
161 // in constants.h and this file doesn't include constants.h, we'll define them
162 // using the actual vector type instead of the ResponseHeaders typedef.
163 // (constants.h doesn't include Rcpp.h so we can't define these functions
164 // there.)
165 namespace Rcpp {
as(SEXP x)166 template <> inline std::vector<std::pair<std::string, std::string> > as(SEXP x) {
167 ASSERT_MAIN_THREAD()
168 Rcpp::CharacterVector headers(x);
169 Rcpp::CharacterVector names = headers.names();
170
171 if (names.isNULL()) {
172 throw Rcpp::exception("All values must be named.");
173 }
174
175 std::vector<std::pair<std::string, std::string> > result;
176
177 for (int i=0; i<headers.size(); i++) {
178 std::string name = Rcpp::as<std::string>(names[i]);
179 if (name == "") {
180 throw Rcpp::exception("All values must be named.");
181 }
182
183 std::string value = Rcpp::as<std::string>(headers[i]);
184
185 result.push_back(std::make_pair(name, value));
186 }
187
188 return result;
189 }
190
wrap(const std::vector<std::pair<std::string,std::string>> & x)191 template <> inline SEXP wrap(const std::vector<std::pair<std::string, std::string> > &x) {
192 ASSERT_MAIN_THREAD()
193
194 std::vector<std::string> values(x.size());
195 std::vector<std::string> names(x.size());
196
197 for (unsigned int i=0; i<x.size(); i++) {
198 names[i] = x[i].first;
199 values[i] = x[i].second;
200 }
201
202 Rcpp::CharacterVector result = Rcpp::wrap(values);
203 result.attr("names") = Rcpp::wrap(names);
204
205 return result;
206 }
207 }
208
209
210 // Return a date string in the format required for the HTTP Date header. For
211 // example: "Wed, 21 Oct 2015 07:28:00 GMT"
http_date_string(const time_t & t)212 inline std::string http_date_string(const time_t& t) {
213 struct tm timeptr;
214 #ifdef _WIN32
215 gmtime_s(&timeptr, &t);
216 #else
217 gmtime_r(&t, &timeptr);
218 #endif
219
220 std::string day_name;
221 switch(timeptr.tm_wday) {
222 case 0: day_name = "Sun"; break;
223 case 1: day_name = "Mon"; break;
224 case 2: day_name = "Tue"; break;
225 case 3: day_name = "Wed"; break;
226 case 4: day_name = "Thu"; break;
227 case 5: day_name = "Fri"; break;
228 case 6: day_name = "Sat"; break;
229 default: return "";
230 }
231
232 std::string month_name;
233 switch(timeptr.tm_mon) {
234 case 0: month_name = "Jan"; break;
235 case 1: month_name = "Feb"; break;
236 case 2: month_name = "Mar"; break;
237 case 3: month_name = "Apr"; break;
238 case 4: month_name = "May"; break;
239 case 5: month_name = "Jun"; break;
240 case 6: month_name = "Jul"; break;
241 case 7: month_name = "Aug"; break;
242 case 8: month_name = "Sep"; break;
243 case 9: month_name = "Oct"; break;
244 case 10: month_name = "Nov"; break;
245 case 11: month_name = "Dec"; break;
246 default: return "";
247 }
248
249 const int maxlen = 50;
250 char res[maxlen];
251 snprintf(res, maxlen, "%s, %02d %s %04d %02d:%02d:%02d GMT",
252 day_name.c_str(),
253 timeptr.tm_mday,
254 month_name.c_str(),
255 timeptr.tm_year + 1900,
256 timeptr.tm_hour,
257 timeptr.tm_min,
258 timeptr.tm_sec
259 );
260
261 return std::string(res);
262 }
263
264 // Given a date string of format "Wed, 21 Oct 2015 07:28:00 GMT", return a
265 // time_t representing that time. If the date is malformed, then return 0.
266 time_t parse_http_date_string(const std::string& date);
267
268 // Compares two strings in constant time. Returns true if they are the same;
269 // false otherwise.
constant_time_compare(const std::string & a,const std::string & b)270 inline bool constant_time_compare(const std::string& a, const std::string& b) {
271 if (a.length() != b.length())
272 return false;
273
274 volatile const char* ac = a.c_str();
275 volatile const char* bc = b.c_str();
276 volatile char result = 0;
277 int len = a.length();
278
279 for (int i=0; i<len; i++) {
280 result |= ac[i] ^ bc[i];
281 }
282
283 return (result == 0);
284 }
285
286 #endif
287