1/*
2 * Copyright (c) 2018 ARM Limited
3 * All rights reserved
4 *
5 * Copyright (c) 2001-2005 The Regents of The University of Michigan
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met: redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer;
12 * redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution;
15 * neither the name of the copyright holders nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Authors: Nathan Binkert
32 *          Steve Reinhardt
33 */
34
35#ifndef __BASE_STR_HH__
36#define __BASE_STR_HH__
37
38#include <cstring>
39#include <limits>
40#include <locale>
41#include <stdexcept>
42#include <string>
43#include <type_traits>
44#include <vector>
45
46#include "base/logging.hh"
47
48inline void
49eat_lead_white(std::string &s)
50{
51    std::string::size_type off = s.find_first_not_of(' ');
52    if (off != std::string::npos) {
53        std::string::iterator begin = s.begin();
54        s.erase(begin, begin + off);
55    }
56}
57
58inline void
59eat_end_white(std::string &s)
60{
61    std::string::size_type off = s.find_last_not_of(' ');
62    if (off != std::string::npos)
63        s.erase(s.begin() + off + 1, s.end());
64}
65
66inline void
67eat_white(std::string &s)
68{
69    eat_lead_white(s);
70    eat_end_white(s);
71}
72
73inline std::string
74to_lower(const std::string &s)
75{
76    std::string lower;
77    int len = s.size();
78
79    lower.reserve(len);
80
81    for (const auto &c : s)
82        lower.push_back(std::tolower(c));
83
84    return lower;
85}
86
87// Split the string s into lhs and rhs on the first occurence of the
88// character c.
89bool
90split_first(const std::string &s, std::string &lhs, std::string &rhs, char c);
91
92// Split the string s into lhs and rhs on the last occurence of the
93// character c.
94bool
95split_last(const std::string &s, std::string &lhs, std::string &rhs, char c);
96
97// Tokenize the string <s> splitting on the character <token>, and
98// place the result in the string vector <vector>.  If <ign> is true,
99// then empty result strings (due to trailing tokens, or consecutive
100// tokens) are skipped.
101void
102tokenize(std::vector<std::string> &vector, const std::string &s,
103         char token, bool ign = true);
104
105/**
106 * @{
107 *
108 * @name String to number helper functions for signed and unsigned
109 *       integeral type, as well as enums and floating-point types.
110 */
111template <class T>
112typename std::enable_if<std::is_integral<T>::value &&
113                        std::is_signed<T>::value, T>::type
114__to_number(const std::string &value)
115{
116    // start big and narrow it down if needed, determine the base dynamically
117    long long r = std::stoll(value, nullptr, 0);
118    if (r < std::numeric_limits<T>::min() || r > std::numeric_limits<T>::max())
119        throw std::out_of_range("Out of range");
120    return static_cast<T>(r);
121}
122
123template <class T>
124typename std::enable_if<std::is_integral<T>::value &&
125                        !std::is_signed<T>::value, T>::type
126__to_number(const std::string &value)
127{
128    // start big and narrow it down if needed, determine the base dynamically
129    unsigned long long r = std::stoull(value, nullptr, 0);
130    if (r > std::numeric_limits<T>::max())
131        throw std::out_of_range("Out of range");
132    return static_cast<T>(r);
133}
134
135template <class T>
136typename std::enable_if<std::is_enum<T>::value, T>::type
137__to_number(const std::string &value)
138{
139    auto r = __to_number<typename std::underlying_type<T>::type>(value);
140    return static_cast<T>(r);
141}
142
143template <class T>
144typename std::enable_if<std::is_floating_point<T>::value, T>::type
145__to_number(const std::string &value)
146{
147    // start big and narrow it down if needed
148    long double r = std::stold(value);
149    if (r < std::numeric_limits<T>::min() || r > std::numeric_limits<T>::max())
150        throw std::out_of_range("Out of range");
151    return static_cast<T>(r);
152}
153/** @} */
154
155/**
156 * Turn a string representation of a number, either integral or
157 * floating point, into an actual number.
158 *
159 * @param value The string representing the number
160 * @param retval The resulting value
161 * @return True if the parsing was successful
162 */
163template <class T>
164inline bool
165to_number(const std::string &value, T &retval)
166{
167    try {
168        retval = __to_number<T>(value);
169        return true;
170    } catch (const std::out_of_range&) {
171        return false;
172    } catch (const std::invalid_argument&) {
173        return false;
174    } catch (...) {
175        panic("Unrecognized exception.\n");
176    }
177}
178
179/**
180 * Turn a string representation of a boolean into a boolean value.
181 */
182inline bool
183to_bool(const std::string &value, bool &retval)
184{
185    std::string s = to_lower(value);
186
187    if (s == "true") {
188        retval = true;
189        return true;
190    } else if (s == "false") {
191        retval = false;
192        return true;
193    }
194
195    return false;
196}
197
198// Put quotes around string arg if it contains spaces.
199inline std::string
200quote(const std::string &s)
201{
202    std::string ret;
203    bool quote = s.find(' ') != std::string::npos;
204
205    if (quote)
206        ret = '"';
207
208    ret += s;
209
210    if (quote)
211        ret += '"';
212
213    return ret;
214}
215
216
217/**
218 * Return true if 's' starts with the prefix string 'prefix'.
219 */
220inline bool
221startswith(const char *s, const char *prefix)
222{
223    return (strncmp(s, prefix, strlen(prefix)) == 0);
224}
225
226
227/**
228 * Return true if 's' starts with the prefix string 'prefix'.
229 */
230inline bool
231startswith(const std::string &s, const char *prefix)
232{
233    return (s.compare(0, strlen(prefix), prefix) == 0);
234}
235
236
237/**
238 * Return true if 's' starts with the prefix string 'prefix'.
239 */
240inline bool
241startswith(const std::string &s, const std::string &prefix)
242{
243    return (s.compare(0, prefix.size(), prefix) == 0);
244}
245
246
247#endif //__BASE_STR_HH__
248