1#include "duckdb/common/string_util.hpp"
2
3#include <algorithm>
4#include <cctype>
5#include <iomanip>
6#include <memory>
7#include <sstream>
8#include <stdarg.h>
9#include <string.h>
10#include <string>
11
12using namespace duckdb;
13using namespace std;
14
15bool StringUtil::Contains(const string &haystack, const string &needle) {
16 return (haystack.find(needle) != string::npos);
17}
18
19void StringUtil::LTrim(string &str) {
20 auto it = str.begin();
21 while (isspace(*it)) {
22 it++;
23 }
24 str.erase(str.begin(), it);
25}
26
27// Remove trailing ' ', '\f', '\n', '\r', '\t', '\v'
28void StringUtil::RTrim(string &str) {
29 str.erase(find_if(str.rbegin(), str.rend(), [](int ch) { return ch > 0 && !isspace(ch); }).base(), str.end());
30}
31
32void StringUtil::Trim(string &str) {
33 StringUtil::LTrim(str);
34 StringUtil::RTrim(str);
35}
36
37bool StringUtil::StartsWith(const string &str, const string &prefix) {
38 if (prefix.size() > str.size()) {
39 return false;
40 }
41 return equal(prefix.begin(), prefix.end(), str.begin());
42}
43
44bool StringUtil::EndsWith(const string &str, const string &suffix) {
45 if (suffix.size() > str.size())
46 return false;
47 return equal(suffix.rbegin(), suffix.rend(), str.rbegin());
48}
49
50string StringUtil::Repeat(const string &str, idx_t n) {
51 ostringstream os;
52 if (n == 0 || str.empty()) {
53 return (os.str());
54 }
55 for (int i = 0; i < static_cast<int>(n); i++) {
56 os << str;
57 }
58 return (os.str());
59}
60
61vector<string> StringUtil::Split(const string &str, char delimiter) {
62 stringstream ss(str);
63 vector<string> lines;
64 string temp;
65 while (getline(ss, temp, delimiter)) {
66 lines.push_back(temp);
67 } // WHILE
68 return (lines);
69}
70
71string StringUtil::Join(const vector<string> &input, const string &separator) {
72 return StringUtil::Join(input, input.size(), separator, [](const string &s) { return s; });
73}
74
75string StringUtil::Prefix(const string &str, const string &prefix) {
76 vector<string> lines = StringUtil::Split(str, '\n');
77 if (lines.empty())
78 return ("");
79
80 ostringstream os;
81 for (idx_t i = 0, cnt = lines.size(); i < cnt; i++) {
82 if (i > 0)
83 os << endl;
84 os << prefix << lines[i];
85 } // FOR
86 return (os.str());
87}
88
89// http://ubuntuforums.org/showpost.php?p=10215516&postcount=5
90string StringUtil::FormatSize(idx_t bytes) {
91 double BASE = 1024;
92 double KB = BASE;
93 double MB = KB * BASE;
94 double GB = MB * BASE;
95
96 ostringstream os;
97
98 if (bytes >= GB) {
99 os << fixed << setprecision(2) << (bytes / GB) << " GB";
100 } else if (bytes >= MB) {
101 os << fixed << setprecision(2) << (bytes / MB) << " MB";
102 } else if (bytes >= KB) {
103 os << fixed << setprecision(2) << (bytes / KB) << " KB";
104 } else {
105 os << to_string(bytes) + " bytes";
106 }
107 return (os.str());
108}
109
110string StringUtil::Upper(const string &str) {
111 string copy(str);
112 transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) { return toupper(c); });
113 return (copy);
114}
115
116string StringUtil::Lower(const string &str) {
117 string copy(str);
118 transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) { return tolower(c); });
119 return (copy);
120}
121
122// http://stackoverflow.com/a/8098080
123string StringUtil::Format(const string fmt_str, ...) {
124 // Reserve two times as much as the length of the fmt_str
125 int final_n, n = ((int)fmt_str.size()) * 2;
126 string str;
127 unique_ptr<char[]> formatted;
128 va_list ap;
129
130 while (1) {
131 // Wrap the plain char array into the unique_ptr
132 formatted.reset(new char[n + 1]);
133 strcpy(&formatted[0], fmt_str.c_str());
134 va_start(ap, fmt_str);
135 final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
136 va_end(ap);
137 if (final_n < 0 || final_n >= n)
138 n += abs(final_n - n + 1);
139 else
140 break;
141 }
142 return string(formatted.get());
143}
144
145string StringUtil::VFormat(const string fmt_str, va_list args) {
146 va_list args_copy;
147
148 unique_ptr<char[]> formatted;
149 // make a copy of the args as we can only use it once
150 va_copy(args_copy, args);
151
152 // first get the amount of characters we need
153 const auto n = vsnprintf(nullptr, 0, fmt_str.c_str(), args) + 1;
154
155 // now allocate the string and do the actual printing
156 formatted.reset(new char[n]);
157 (void)vsnprintf(&formatted[0], n, fmt_str.c_str(), args_copy);
158 return string(formatted.get());
159}
160
161vector<string> StringUtil::Split(const string &input, const string &split) {
162 vector<string> splits;
163
164 idx_t last = 0;
165 idx_t input_len = input.size();
166 idx_t split_len = split.size();
167 while (last <= input_len) {
168 idx_t next = input.find(split, last);
169 if (next == string::npos) {
170 next = input_len;
171 }
172
173 // Push the substring [last, next) on to splits
174 string substr = input.substr(last, next - last);
175 if (substr.empty() == false) {
176 splits.push_back(substr);
177 }
178 last = next + split_len;
179 }
180 return splits;
181}
182
183string StringUtil::Replace(string source, const string &from, const string &to) {
184 if (from.empty())
185 return source;
186 ;
187 idx_t start_pos = 0;
188 while ((start_pos = source.find(from, start_pos)) != string::npos) {
189 source.replace(start_pos, from.length(), to);
190 start_pos += to.length(); // In case 'to' contains 'from', like
191 // replacing 'x' with 'yx'
192 }
193 return source;
194}
195