1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*!
21 * \file utils.h
22 * \brief Defines some common utility function..
23 */
24#ifndef TVM_SUPPORT_UTILS_H_
25#define TVM_SUPPORT_UTILS_H_
26
27#include <stdio.h>
28#ifndef _WIN32
29#include <sys/types.h>
30#ifndef __hexagon__
31#include <sys/wait.h>
32#endif // __hexagon__
33#endif // _WIN32
34
35#include <tvm/runtime/container/string.h>
36
37#include <algorithm>
38#include <array>
39#include <cctype>
40#include <cstdlib>
41#include <memory>
42#include <sstream>
43#include <string>
44#include <vector>
45
46namespace tvm {
47namespace support {
48/*!
49 * \brief TVMPOpen wrapper of popen between windows / unix.
50 * \param command executed command
51 * \param type "r" is for reading or "w" for writing.
52 * \return normal standard stream
53 */
54#ifndef __hexagon__
55inline FILE* TVMPOpen(const char* command, const char* type) {
56#if defined(_WIN32)
57 return _popen(command, type);
58#else
59 return popen(command, type);
60#endif
61}
62#endif // __hexagon__
63
64/*!
65 * \brief TVMPClose wrapper of pclose between windows / linux
66 * \param stream the stream needed to be close.
67 * \return exit status
68 */
69#ifndef __hexagon__
70inline int TVMPClose(FILE* stream) {
71#if defined(_WIN32)
72 return _pclose(stream);
73#else
74 return pclose(stream);
75#endif
76}
77#endif // __hexagon__
78
79/*!
80 * \brief TVMWifexited wrapper of WIFEXITED between windows / linux
81 * \param status The status field that was filled in by the wait or waitpid function
82 * \return the exit code of the child process
83 */
84#ifndef __hexagon__
85inline int TVMWifexited(int status) {
86#if defined(_WIN32)
87 return (status != 3);
88#else
89 return WIFEXITED(status);
90#endif
91}
92#endif // __hexagon__
93
94/*!
95 * \brief TVMWexitstatus wrapper of WEXITSTATUS between windows / linux
96 * \param status The status field that was filled in by the wait or waitpid function.
97 * \return the child process exited normally or not
98 */
99#ifndef __hexagon__
100inline int TVMWexitstatus(int status) {
101#if defined(_WIN32)
102 return status;
103#else
104 return WEXITSTATUS(status);
105#endif
106}
107#endif // __hexagon__
108
109/*!
110 * \brief IsNumber check whether string is a number.
111 * \param str input string
112 * \return result of operation.
113 */
114inline bool IsNumber(const std::string& str) {
115 return !str.empty() &&
116 std::find_if(str.begin(), str.end(), [](char c) { return !std::isdigit(c); }) == str.end();
117}
118
119/*!
120 * \brief split Split the string based on delimiter
121 * \param str Input string
122 * \param delim The delimiter.
123 * \return vector of strings which are splitted.
124 */
125inline std::vector<std::string> Split(const std::string& str, char delim) {
126 std::string item;
127 std::istringstream is(str);
128 std::vector<std::string> ret;
129 while (std::getline(is, item, delim)) {
130 ret.push_back(item);
131 }
132 return ret;
133}
134
135/*!
136 * \brief Check whether the string starts with a given prefix.
137 * \param str The given string.
138 * \param prefix The given prefix.
139 * \return Whether the prefix matched.
140 */
141inline bool StartsWith(const String& str, const char* prefix) {
142 size_t n = str.length();
143 for (size_t i = 0; i < n; i++) {
144 if (prefix[i] == '\0') return true;
145 if (str.data()[i] != prefix[i]) return false;
146 }
147 // return true if the str is equal to the prefix
148 return prefix[n] == '\0';
149}
150
151/*!
152 * \brief EndsWith check whether the strings ends with
153 * \param value The full string
154 * \param end The end substring
155 * \return bool The result.
156 */
157inline bool EndsWith(std::string const& value, std::string const& end) {
158 if (end.size() <= value.size()) {
159 return std::equal(end.rbegin(), end.rend(), value.rbegin());
160 }
161 return false;
162}
163
164/*!
165 * \brief Execute the command
166 * \param cmd The command we want to execute
167 * \param err_msg The error message if we have
168 * \return executed output status
169 */
170#ifndef __hexagon__
171inline int Execute(std::string cmd, std::string* err_msg) {
172 std::array<char, 128> buffer;
173 std::string result;
174 cmd += " 2>&1";
175 FILE* fd = TVMPOpen(cmd.c_str(), "r");
176 while (fgets(buffer.data(), buffer.size(), fd) != nullptr) {
177 *err_msg += buffer.data();
178 }
179 int status = TVMPClose(fd);
180 if (TVMWifexited(status)) {
181 return TVMWexitstatus(status);
182 }
183 return 255;
184}
185#endif // __hexagon__
186
187/*!
188 * \brief hash an object and combines uint64_t key with previous keys
189 *
190 * This hash function is stable across platforms.
191 *
192 * \param key The left operand.
193 * \param value The right operand.
194 * \return the combined result.
195 */
196template <typename T, std::enable_if_t<std::is_convertible<T, uint64_t>::value, bool> = true>
197inline uint64_t HashCombine(uint64_t key, const T& value) {
198 // XXX: do not use std::hash in this function. This hash must be stable
199 // across different platforms and std::hash is implementation dependent.
200 return key ^ (uint64_t(value) + 0x9e3779b9 + (key << 6) + (key >> 2));
201}
202
203/*!
204 * \brief Return whether a boolean flag is set as an environment variable.
205 *
206 * Returns true if the environment variable is set to a non-zero
207 * integer, or to a non-empty string that is not an integer.
208 *
209 * Returns false if the environment variable is unset, if the
210 * environment variable is set to the integer zero, or if the
211 * environment variable is an empty string.
212 */
213inline bool BoolEnvironmentVar(const char* varname) {
214 const char* var = std::getenv(varname);
215 if (!var) {
216 return false;
217 }
218
219 int x = 0;
220 std::istringstream is(var);
221 if (is >> x) {
222 return x;
223 }
224
225 return *var;
226}
227
228} // namespace support
229} // namespace tvm
230#endif // TVM_SUPPORT_UTILS_H_
231