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

MikkelSchubert / adapterremoval / #38

07 Aug 2024 07:47PM UTC coverage: 83.365% (-3.5%) from 86.839%
#38

push

travis-ci

MikkelSchubert
additional tests

2190 of 2627 relevant lines covered (83.37%)

15528.72 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)
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_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
/** Returns true if str1 ends with str2 (case sensitive) */
145
bool
146
ends_with(const std::string_view str1, const std::string_view str2)
13✔
147
{
148
  if (str1.size() < str2.size()) {
13✔
149
    return false;
150
  }
151

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

160
  return true;
161
}
162

163
string_vec
164
split_lines(const std::string_view text)
154✔
165
{
166
  string_vec lines;
154✔
167

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

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

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

178
  return lines;
154✔
179
}
×
180

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

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

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

214
  return lines_out;
218✔
215
}
109✔
216

217
///////////////////////////////////////////////////////////////////////////////
218
// Implementations for 'cli_formatter'
219

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

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

233
  return *this;
44✔
234
}
235

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

241
  return *this;
32✔
242
}
243

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

249
  return *this;
44✔
250
}
251

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

257
  return *this;
12✔
258
}
259

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

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

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

271
  return lines_out.str();
148✔
272
}
74✔
273

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

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

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

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

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

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

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

333
  out.push_back('\'');
43✔
334

335
  return out;
43✔
336
}
×
337

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

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

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

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

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

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

371
  return ss;
4✔
372
}
5✔
373

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

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

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

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

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

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

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

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

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

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

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

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

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