• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MikkelSchubert / adapterremoval / #37

28 Jul 2024 09:48PM UTC coverage: 86.839% (-0.4%) from 87.26%
#37

push

travis-ci

MikkelSchubert
allow bool arguments without a sink variable

2151 of 2477 relevant lines covered (86.84%)

16470.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.05
/src/strutils.cpp
1
/*************************************************************************\
2
 * AdapterRemoval - cleaning next-generation sequencing reads            *
3
 *                                                                       *
4
 * Copyright (C) 2015 by Mikkel Schubert - mikkelsch@gmail.com           *
5
 *                                                                       *
6
 * This program is free software: you can redistribute it and/or modify  *
7
 * it under the terms of the GNU General Public License as published by  *
8
 * the Free Software Foundation, either version 3 of the License, or     *
9
 * (at your option) any later version.                                   *
10
 *                                                                       *
11
 * This program is distributed in the hope that it will be useful,       *
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 * GNU General Public License for more details.                          *
15
 *                                                                       *
16
 * You should have received a copy of the GNU General Public License     *
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
18
\*************************************************************************/
19
#include "strutils.hpp" // declarations
20
#include "debug.hpp"    // for AR_REQUIRE
21
#include <algorithm>    // for min, reverse, max
22
#include <cctype>       // for isprint, isalnum, tolower, toupper
23
#include <chrono>       // for system_clock
24
#include <cmath>        // for log10, pow, round
25
#include <cstdint>      // for uint64_t, int64_t
26
#include <iomanip>      // for operator<<, setprecision
27
#include <limits>       // for numeric_limits
28
#include <sstream>      // for ostringstream, operator<<, basic_ostream, bas...
29
#include <stdexcept>    // for invalid_argument
30
#include <unistd.h>     // for STDOUT_FILENO
31
#include <vector>       // for vector, swap
32

33
namespace adapterremoval {
34

35
namespace {
36

37
string_vec
38
indent(string_vec lines, size_t n_indent, bool indent_first)
85✔
39
{
40
  const std::string indentation(n_indent, ' ');
170✔
41
  for (auto it = lines.begin(); it != lines.end(); ++it) {
673✔
42
    if (!it->empty() && (indent_first || it != lines.begin())) {
324✔
43
      it->insert(0, indentation);
161✔
44
    }
45
  }
46

47
  return lines;
255✔
48
}
85✔
49

50
} // namespace
51

52
size_t
53
levenshtein(const std::string& s, const std::string& t)
17✔
54
{
55
  std::vector<size_t> v0(t.size() + 1, 0);
34✔
56
  std::vector<size_t> v1(t.size() + 1, 0);
34✔
57

58
  for (size_t i = 0; i < v0.size(); ++i) {
156✔
59
    v0.at(i) = i;
122✔
60
  }
61

62
  for (size_t i = 0; i < s.size(); ++i) {
116✔
63
    v1.at(0) = i + 1;
82✔
64

65
    for (size_t j = 0; j < t.size(); ++j) {
428✔
66
      const auto del = v0.at(j + 1) + 1;
346✔
67
      const auto ins = v1.at(j) + 1;
346✔
68
      const auto sub = s.at(i) == t.at(j) ? v0.at(j) : v0.at(j) + 1;
692✔
69

70
      v1.at(j + 1) = std::min(del, std::min(ins, sub));
692✔
71
    }
72

73
    std::swap(v0, v1);
82✔
74
  }
75

76
  return v0.back();
51✔
77
}
34✔
78

79
std::string
80
timestamp(const char* format, const bool milliseconds)
3✔
81
{
82
  AR_REQUIRE(format);
3✔
83
  using namespace std::chrono;
3✔
84

85
  const auto now = system_clock::now();
3✔
86
  const auto in_time_t = system_clock::to_time_t(now);
3✔
87

88
  tm in_localtime{};
3✔
89
  std::ostringstream ss;
3✔
90
  ss << std::put_time(localtime_r(&in_time_t, &in_localtime), format);
3✔
91

92
  if (milliseconds) {
3✔
93
    const auto ms =
3✔
94
      duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
6✔
95
    ss << '.' << std::setfill('0') << std::setw(3) << (ms.count() % 1000);
12✔
96
  }
97

98
  return ss.str();
6✔
99
}
3✔
100

101
unsigned
102
str_to_unsigned(const std::string& s)
38✔
103
{
104
  std::istringstream stream(s);
38✔
105
  int64_t temp = 0;
38✔
106

107
  if (!(stream >> temp)) {
114✔
108
    throw std::invalid_argument("value is not a valid number");
8✔
109
  }
110

111
  // Failing on trailing, non-numerical values
112
  std::string trailing;
30✔
113
  if (stream >> trailing) {
60✔
114
    throw std::invalid_argument("value contains trailing text");
4✔
115
  }
116

117
  if (temp < 0 || temp > std::numeric_limits<unsigned>::max()) {
26✔
118
    throw std::invalid_argument("numerical value overflows");
6✔
119
  }
120

121
  return static_cast<unsigned>(temp);
40✔
122
}
38✔
123

124
std::string
125
to_lower(const std::string& str)
14✔
126
{
127
  std::string lowercase = str;
14✔
128
  for (auto& current : lowercase) {
200✔
129
    current = to_lower(current);
56✔
130
  }
131

132
  return lowercase;
14✔
133
}
134

135
std::string
136
to_upper(const std::string& str)
2✔
137
{
138
  std::string uppercase = str;
2✔
139
  for (auto& current : uppercase) {
44✔
140
    current = to_upper(current);
16✔
141
  }
142

143
  return uppercase;
2✔
144
}
145

146
/** Returns true if str1 ends with str2 (case sensitive) */
147
bool
148
ends_with(const std::string& str1, const std::string& str2)
13✔
149
{
150
  if (str1.size() < str2.size()) {
39✔
151
    return false;
152
  }
153

154
  auto it1 = str1.rbegin();
11✔
155
  auto it2 = str2.rbegin();
11✔
156
  for (; it2 != str2.rend(); ++it1, ++it2) {
113✔
157
    if (*it1 != *it2) {
63✔
158
      return false;
159
    }
160
  }
161

162
  return true;
163
}
164

165
string_vec
166
split_lines(const std::string& text)
154✔
167
{
168
  string_vec lines;
154✔
169

170
  size_t start = 0;
154✔
171
  size_t end = std::string::npos;
154✔
172
  do {
176✔
173
    end = text.find('\n', start);
176✔
174

175
    lines.push_back(text.substr(start, end - start));
528✔
176

177
    start = end + 1;
176✔
178
  } while (end != std::string::npos);
176✔
179

180
  return lines;
154✔
181
}
×
182

183
std::string
184
indent_lines(const std::string& lines, size_t indentation)
11✔
185
{
186
  return join_text(indent(split_lines(lines), indentation, true), "\n");
33✔
187
}
188

189
string_vec
190
wrap_text(const std::string& value, size_t max_width, size_t ljust)
109✔
191
{
192
  size_t current_width = 0;
109✔
193
  size_t current_ljust = 0;
109✔
194
  std::istringstream lines_in(value);
109✔
195
  string_vec lines_out;
109✔
196

197
  std::string substr;
109✔
198
  while (lines_in >> substr) {
1,072✔
199
    if (current_width) {
427✔
200
      if (current_ljust + current_width + 1 + substr.length() > max_width) {
652✔
201
        current_ljust = ljust;
28✔
202
        lines_out.emplace_back(current_ljust, ' ');
28✔
203
        lines_out.back().append(substr);
56✔
204
        current_width = substr.length();
56✔
205
      } else {
206
        lines_out.back().push_back(' ');
596✔
207
        lines_out.back().append(substr);
596✔
208
        current_width += substr.length() + 1;
596✔
209
      }
210
    } else {
211
      lines_out.push_back(substr);
101✔
212
      current_width += substr.length();
202✔
213
    }
214
  }
215

216
  return lines_out;
218✔
217
}
109✔
218

219
///////////////////////////////////////////////////////////////////////////////
220
// Implementations for 'cli_formatter'
221

222
cli_formatter::cli_formatter()
44✔
223
  : m_indent_first(true)
44✔
224
  , m_ljust(0)
44✔
225
  , m_columns(DEFAULT_MAX_COLUMNS)
44✔
226
  , m_indentation(4)
44✔
227
{
228
}
44✔
229

230
cli_formatter&
231
cli_formatter::set_column_width(size_t value)
44✔
232
{
233
  m_columns = value;
44✔
234

235
  return *this;
44✔
236
}
237

238
cli_formatter&
239
cli_formatter::set_ljust(size_t value)
32✔
240
{
241
  m_ljust = value;
32✔
242

243
  return *this;
32✔
244
}
245

246
cli_formatter&
247
cli_formatter::set_indent(size_t value)
44✔
248
{
249
  m_indentation = value;
44✔
250

251
  return *this;
44✔
252
}
253

254
cli_formatter&
255
cli_formatter::set_indent_first_line(bool value)
12✔
256
{
257
  m_indent_first = value;
12✔
258

259
  return *this;
12✔
260
}
261

262
std::string
263
cli_formatter::format(const std::string& value) const
74✔
264
{
265
  std::ostringstream lines_out;
74✔
266

267
  for (const auto& line : split_lines(value)) {
592✔
268
    const auto block = wrap_text(line, m_columns, m_ljust);
74✔
269

270
    lines_out << join_text(indent(block, m_indentation, m_indent_first), "\n");
370✔
271
  }
148✔
272

273
  return lines_out.str();
148✔
274
}
74✔
275

276
std::string
277
shell_escape(const std::string& s)
46✔
278
{
279
  if (s.empty()) {
92✔
280
    return "''";
2✔
281
  }
282

283
  for (const auto c : s) {
474✔
284
    // Conservative list of safe values; better safe than sorry
285
    if (!isalnum(c) && c != '_' && c != '.' && c != '/' && c != '-') {
96✔
286
      return log_escape(s);
30✔
287
    }
288
  }
289

290
  return s;
15✔
291
}
292

293
std::string
294
log_escape(const std::string& s)
43✔
295
{
296
  std::string out;
43✔
297
  out.push_back('\'');
43✔
298

299
  for (const auto c : s) {
1,248✔
300
    switch (c) {
269✔
301
      case '\'':
4✔
302
        out.append("\\'");
4✔
303
        break;
304
      case '\\':
2✔
305
        out.append("\\\\");
2✔
306
        break;
307
      case '\b':
2✔
308
        out.append("\\b");
2✔
309
        break;
310
      case '\f':
2✔
311
        out.append("\\f");
2✔
312
        break;
313
      case '\n':
2✔
314
        out.append("\\n");
2✔
315
        break;
316
      case '\r':
2✔
317
        out.append("\\r");
2✔
318
        break;
319
      case '\t':
2✔
320
        out.append("\\t");
2✔
321
        break;
322
      default:
253✔
323
        if (!std::isprint(c)) {
253✔
324
          std::ostringstream ss;
7✔
325
          ss << "\\x" << std::hex << static_cast<int>(c);
14✔
326

327
          out.append(ss.str());
28✔
328

329
        } else {
7✔
330
          out.push_back(c);
246✔
331
        }
332
    }
333
  }
334

335
  out.push_back('\'');
43✔
336

337
  return out;
43✔
338
}
×
339

340
std::string
341
shell_escape_command(const string_vec& values)
2✔
342
{
343
  std::ostringstream ss;
2✔
344
  for (size_t i = 0; i < values.size(); ++i) {
14✔
345
    if (i) {
5✔
346
      ss << ' ';
4✔
347
    }
348

349
    ss << shell_escape(values.at(i));
20✔
350
  }
351

352
  return ss.str();
4✔
353
}
2✔
354

355
std::string
356
format_thousand_sep(size_t count)
5✔
357
{
358
  if (!count) {
5✔
359
    return "0";
2✔
360
  }
361

362
  std::string ss;
4✔
363
  for (size_t i = 0; count; ++i) {
24✔
364
    ss.push_back('0' + (count % 10));
20✔
365
    count /= 10;
20✔
366
    if (count && i && i % 3 == 2) {
20✔
367
      ss.push_back(',');
4✔
368
    }
369
  }
370

371
  std::reverse(ss.begin(), ss.end());
12✔
372

373
  return ss;
4✔
374
}
5✔
375

376
std::string
377
format_rough_number(size_t value, size_t out_digits)
37✔
378
{
379
  AR_REQUIRE(out_digits > 0);
41✔
380
  if (value == 0) {
36✔
381
    return "0";
6✔
382
  }
383

384
  auto rounded = static_cast<double>(value);
33✔
385
  auto in_digits = static_cast<size_t>(std::log10(rounded));
33✔
386
  if (out_digits > in_digits) {
33✔
387
    return std::to_string(value);
5✔
388
  }
389

390
  // Round to desired number of significant digits
391
  const auto tmp = std::pow(10, in_digits - out_digits + 1);
28✔
392
  rounded = std::round(rounded / tmp) * tmp;
28✔
393

394
  // Rounding up may result in the number of digits increasing
395
  in_digits = static_cast<size_t>(std::log10(rounded));
28✔
396

397
  const std::string units = "KMGTP";
56✔
398
  const size_t unit = std::min<size_t>(units.size(), in_digits / 3);
84✔
399
  const double scaled = rounded / std::pow(10.0, unit * 3);
28✔
400
  const size_t precision =
28✔
401
    out_digits - std::min<size_t>(out_digits, in_digits - unit * 3 + 1);
56✔
402

403
  std::ostringstream ss;
28✔
404
  ss << std::fixed << std::setprecision(precision) << scaled;
84✔
405

406
  if (unit) {
28✔
407
    ss << " " << units.at(unit - 1);
52✔
408
  }
409

410
  return ss.str();
28✔
411
}
64✔
412

413
std::string
414
format_fraction(uint64_t num, uint64_t denom, size_t precision)
13✔
415
{
416
  if (denom) {
13✔
417
    const double fraction = static_cast<double>(num) / denom;
9✔
418

419
    std::ostringstream ss;
9✔
420
    ss << std::fixed << std::setprecision(precision) << fraction;
27✔
421

422
    return ss.str();
9✔
423
  } else {
9✔
424
    return "NA";
8✔
425
  }
426
}
427

428
std::string
429
format_percentage(uint64_t num, uint64_t denom, size_t precision)
6✔
430
{
431
  return format_fraction(num * 100, denom, precision);
6✔
432
}
433

434
} // namespace adapterremoval
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc