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

MikkelSchubert / adapterremoval / #39

20 Aug 2024 01:31PM UTC coverage: 79.602% (-3.8%) from 83.365%
#39

push

travis-ci

MikkelSchubert
update schema URL to use github pages

2279 of 2863 relevant lines covered (79.6%)

14257.45 hits per line

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

99.04
/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)
91✔
39
{
40
  const std::string indentation(n_indent, ' ');
182✔
41
  for (auto it = lines.begin(); it != lines.end(); ++it) {
715✔
42
    if (!it->empty() && (indent_first || it != lines.begin())) {
348✔
43
      it->insert(0, indentation);
167✔
44
    }
45
  }
46

47
  return lines;
273✔
48
}
91✔
49

50
} // namespace
51

52
size_t
53
levenshtein(const std::string_view s, const std::string_view t)
17✔
54
{
55
  std::vector<size_t> v0(t.size() + 1, 0);
17✔
56
  std::vector<size_t> v1(t.size() + 1, 0);
17✔
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) {
58✔
63
    v1.at(0) = i + 1;
82✔
64

65
    for (size_t j = 0; j < t.size(); ++j) {
214✔
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(std::string str)
14✔
126
{
127
  for (auto& current : str) {
200✔
128
    current = to_lower(current);
56✔
129
  }
130

131
  return str;
14✔
132
}
133

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

141
  return str;
2✔
142
}
143

144
bool
145
starts_with(const std::string_view str1, const std::string_view str2)
19✔
146
{
147
  if (str1.size() < str2.size()) {
19✔
148
    return false;
149
  }
150

151
  return str1.substr(0, str2.size()) == str2;
17✔
152
}
153

154
bool
155
ends_with(const std::string_view str1, const std::string_view str2)
13✔
156
{
157
  if (str1.size() < str2.size()) {
13✔
158
    return false;
159
  }
160

161
  return str1.substr(str1.size() - str2.size()) == str2;
11✔
162
}
163

164
string_vec
165
split_text(const std::string_view text, const char separator)
180✔
166
{
167
  string_vec lines;
180✔
168

169
  size_t start = 0;
180✔
170
  size_t end = std::string::npos;
180✔
171
  do {
216✔
172
    end = text.find(separator, start);
216✔
173

174
    lines.push_back(std::string{ text.substr(start, end - start) });
648✔
175

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

179
  return lines;
180✔
180
}
×
181

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

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

196
  std::string substr;
115✔
197
  while (lines_in >> substr) {
1,120✔
198
    if (current_width) {
445✔
199
      if (current_ljust + current_width + 1 + substr.length() > max_width) {
676✔
200
        current_ljust = ljust;
28✔
201
        lines_out.emplace_back(current_ljust, ' ');
28✔
202
        lines_out.back().append(substr);
56✔
203
        current_width = substr.length();
56✔
204
      } else {
205
        lines_out.back().push_back(' ');
620✔
206
        lines_out.back().append(substr);
620✔
207
        current_width += substr.length() + 1;
620✔
208
      }
209
    } else {
210
      lines_out.push_back(substr);
107✔
211
      current_width += substr.length();
214✔
212
    }
213
  }
214

215
  return lines_out;
230✔
216
}
115✔
217

218
///////////////////////////////////////////////////////////////////////////////
219
// Implementations for 'cli_formatter'
220

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

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

234
  return *this;
50✔
235
}
236

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

242
  return *this;
32✔
243
}
244

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

250
  return *this;
50✔
251
}
252

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

258
  return *this;
18✔
259
}
260

261
std::string
262
cli_formatter::format(const std::string_view value) const
80✔
263
{
264
  std::ostringstream lines_out;
80✔
265

266
  for (const auto& line : split_lines(value)) {
720✔
267
    const auto block = wrap_text(line, m_columns, m_ljust);
80✔
268

269
    lines_out << join_text(indent(block, m_indentation, m_indent_first), "\n");
400✔
270
  }
160✔
271

272
  return lines_out.str();
160✔
273
}
80✔
274

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

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

289
  return std::string{ s };
30✔
290
}
291

292
std::string
293
log_escape(const std::string_view s)
46✔
294
{
295
  std::string out;
46✔
296
  out.push_back('\'');
46✔
297

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

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

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

334
  out.push_back('\'');
46✔
335

336
  return out;
46✔
337
}
×
338

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

348
    ss << shell_escape(values.at(i));
25✔
349
  }
350

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

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

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

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

372
  return ss;
4✔
373
}
5✔
374

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

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

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

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

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

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

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

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

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

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

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

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

433
} // 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