YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/gutil/strings/human_readable.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2007 Google Inc. All Rights Reserved.
2
//
3
// The following only applies to changes made to this file as part of YugaByte development.
4
//
5
// Portions Copyright (c) YugaByte, Inc.
6
//
7
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8
// in compliance with the License.  You may obtain a copy of the License at
9
//
10
// http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing, software distributed under the License
13
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14
// or implied.  See the License for the specific language governing permissions and limitations
15
// under the License.
16
//
17
18
#include "yb/gutil/strings/human_readable.h"
19
20
#include <stddef.h>
21
#include <string.h>
22
23
#include <glog/logging.h>
24
#include "yb/gutil/stringprintf.h"
25
#include "yb/gutil/strings/strip.h"
26
27
namespace {
28
29
template <typename T>
30
1.84k
const char* GetNegStr(T* value) {
31
1.84k
  if (*value < 0) {
32
0
    *value = -(*value);
33
0
    return "-";
34
1.84k
  } else {
35
1.84k
    return "";
36
1.84k
  }
37
1.84k
}
Unexecuted instantiation: human_readable.cc:_ZN12_GLOBAL__N_19GetNegStrIdEEPKcPT_
human_readable.cc:_ZN12_GLOBAL__N_19GetNegStrIxEEPKcPT_
Line
Count
Source
30
1.84k
const char* GetNegStr(T* value) {
31
1.84k
  if (*value < 0) {
32
0
    *value = -(*value);
33
0
    return "-";
34
1.84k
  } else {
35
1.84k
    return "";
36
1.84k
  }
37
1.84k
}
38
39
}  // namespace
40
41
0
bool HumanReadableNumBytes::LessThan(const string &a, const string &b) {
42
0
  int64 a_bytes, b_bytes;
43
0
  if (!HumanReadableNumBytes::ToInt64(a, &a_bytes))
44
0
    a_bytes = 0;
45
0
  if (!HumanReadableNumBytes::ToInt64(b, &b_bytes))
46
0
    b_bytes = 0;
47
0
  return (a_bytes < b_bytes);
48
0
}
49
50
2
bool HumanReadableNumBytes::ToInt64(const string &str, int64 *num_bytes) {
51
2
  const char *cstr = str.c_str();
52
2
  bool neg = (*cstr == '-');
53
2
  if (neg) {
54
0
    cstr++;
55
0
  }
56
2
  char *end;
57
2
  double d = strtod(cstr, &end);
58
  // If this didn't consume the entire string, fail.
59
2
  if (end + 1 < str.c_str() + str.size()) {
60
0
    return false;
61
0
  }
62
2
  int64 scale = 1;
63
2
  switch (*end) {
64
    // NB: an int64 can only go up to <8 EB.
65
0
    case 'E':  scale <<= 10; FALLTHROUGH_INTENDED;  // Fall through...
66
0
    case 'P':  scale <<= 10; FALLTHROUGH_INTENDED;
67
0
    case 'T':  scale <<= 10; FALLTHROUGH_INTENDED;
68
0
    case 'G':  scale <<= 10; FALLTHROUGH_INTENDED;
69
2
    case 'M':  scale <<= 10; FALLTHROUGH_INTENDED;
70
2
    case 'K':  FALLTHROUGH_INTENDED;
71
2
    case 'k':  scale <<= 10; FALLTHROUGH_INTENDED;
72
2
    case 'B':  FALLTHROUGH_INTENDED;
73
2
    case '\0': break;          // To here.
74
0
    default:
75
0
      return false;
76
2
  }
77
2
  d *= scale;
78
  // We have to cast kint64max to double to avoid a warning on implicit cast that changes the value.
79
2
  if (d > static_cast<double>(kint64max) || d < 0)
80
0
    return false;
81
2
  *num_bytes = static_cast<int64>(d + 0.5);
82
2
  if (neg) {
83
0
    *num_bytes = -*num_bytes;
84
0
  }
85
2
  return true;
86
2
}
87
88
0
bool HumanReadableNumBytes::ToDouble(const string &str, double *num_bytes) {
89
0
  char *end;
90
0
  double d = strtod(str.c_str(), &end);
91
  // If this didn't consume the entire string, fail.
92
0
  if (end + 1 < str.c_str() + str.size())
93
0
    return false;
94
0
  const char scale = *end;
95
0
  switch (scale) {
96
0
    case 'Y':  d *= 1024.0; FALLTHROUGH_INTENDED;  // That's a yotta bytes!
97
0
    case 'Z':  d *= 1024.0; FALLTHROUGH_INTENDED;
98
0
    case 'E':  d *= 1024.0; FALLTHROUGH_INTENDED;
99
0
    case 'P':  d *= 1024.0; FALLTHROUGH_INTENDED;
100
0
    case 'T':  d *= 1024.0; FALLTHROUGH_INTENDED;
101
0
    case 'G':  d *= 1024.0; FALLTHROUGH_INTENDED;
102
0
    case 'M':  d *= 1024.0; FALLTHROUGH_INTENDED;
103
0
    case 'K':  FALLTHROUGH_INTENDED;
104
0
    case 'k':  d *= 1024.0; FALLTHROUGH_INTENDED;
105
0
    case 'B':  FALLTHROUGH_INTENDED;
106
0
    case '\0': break;         // to here.
107
0
    default:
108
0
      return false;
109
0
  }
110
0
  *num_bytes = d;
111
0
  return true;
112
0
}
113
114
0
string HumanReadableNumBytes::DoubleToString(double num_bytes) {
115
0
  const char *neg_str = GetNegStr(&num_bytes);
116
0
  static const char units[] = "BKMGTPEZY";
117
0
  double scaled = num_bytes;
118
0
  size_t i = 0;
119
0
  for (; i < arraysize(units) && scaled >= 1024.0; ++i) {
120
0
    scaled /= 1024.0;
121
0
  }
122
0
  if (i == arraysize(units)) {
123
0
    return StringPrintf("%s%g", neg_str, num_bytes);
124
0
  } else {
125
0
    return StringPrintf("%s%.2f%c", neg_str, scaled, units[i]);
126
0
  }
127
0
}
128
129
1.84k
string HumanReadableNumBytes::ToString(int64 num_bytes) {
130
1.84k
  if (num_bytes == kint64min) {
131
    // Special case for number with not representable nagation.
132
0
    return "-8E";
133
0
  }
134
135
1.84k
  const char *neg_str = GetNegStr(&num_bytes);
136
137
  // Special case for bytes.
138
1.84k
  if (num_bytes < GG_LONGLONG(1024)) {
139
    // No fractions for bytes.
140
1.01k
    return StringPrintf("%s%" PRId64 "B", neg_str, num_bytes);
141
1.01k
  }
142
143
827
  static const char units[] = "KMGTPE";  // int64 only goes up to E.
144
827
  const char* unit = units;
145
1.47k
  while (num_bytes >= GG_LONGLONG(1024) * GG_LONGLONG(1024)) {
146
650
    num_bytes /= GG_LONGLONG(1024);
147
650
    ++unit;
148
650
    CHECK(unit < units + arraysize(units));
149
650
  }
150
151
827
  return StringPrintf(((*unit == 'K')
152
183
                       ? "%s%.1f%c"
153
644
                       : "%s%.2f%c"), neg_str, num_bytes / 1024.0, *unit);
154
827
}
155
156
0
string HumanReadableNumBytes::ToStringWithoutRounding(int64 num_bytes) {
157
0
  if (num_bytes == kint64min) {
158
    // Special case for number with not representable nagation.
159
0
    return "-8E";
160
0
  }
161
162
0
  const char *neg_str = GetNegStr(&num_bytes);
163
0
  static const char units[] = "BKMGTPE";  // int64 only goes up to E.
164
165
0
  int64 num_units = num_bytes;
166
0
  size_t unit_type = 0;
167
0
  for (; unit_type < arraysize(units); unit_type++) {
168
0
    if (num_units % 1024 != 0) {
169
      // Not divisible by the next unit.
170
0
      break;
171
0
    }
172
173
0
    int64 next_units = num_units >> 10;
174
0
    if (next_units == 0) {
175
      // Less than the next unit.
176
0
      break;
177
0
    }
178
179
0
    num_units = next_units;
180
0
  }
181
0
  return StringPrintf("%s%" PRId64 "%c", neg_str, num_units, units[unit_type]);
182
0
}
183
184
0
string HumanReadableInt::ToString(int64 value) {
185
0
  string s;
186
0
  if (value < 0) {
187
0
    s += "-";
188
0
    value = -value;
189
0
  }
190
0
  if (value < GG_LONGLONG(1000)) {
191
0
    StringAppendF(&s, "%" PRId64, value);
192
0
  } else if (value >= GG_LONGLONG(1000000000000000)) {
193
    // Number bigger than 1E15; use that notation.
194
0
    StringAppendF(&s, "%0.3G", static_cast<double>(value));
195
0
  } else {
196
0
    static const char units[] = "kMBT";
197
0
    const char *unit = units;
198
0
    while (value >= GG_LONGLONG(1000000)) {
199
0
      value /= GG_LONGLONG(1000);
200
0
      ++unit;
201
0
      CHECK(unit < units + arraysize(units));
202
0
    }
203
0
    StringAppendF(&s, "%.2f%c", value / 1000.0, *unit);
204
0
  }
205
0
  return s;
206
0
}
207
208
0
string HumanReadableNum::ToString(int64 value) {
209
0
  return HumanReadableInt::ToString(value);
210
0
}
211
212
0
string HumanReadableNum::DoubleToString(double value) {
213
0
  string s;
214
0
  if (value < 0) {
215
0
    s += "-";
216
0
    value = -value;
217
0
  }
218
0
  if (value < 1.0) {
219
0
    StringAppendF(&s, "%.3f", value);
220
0
  } else if (value < 10) {
221
0
    StringAppendF(&s, "%.2f", value);
222
0
  } else if (value < 1e2) {
223
0
    StringAppendF(&s, "%.1f", value);
224
0
  } else if (value < 1e3) {
225
0
    StringAppendF(&s, "%.0f", value);
226
0
  } else if (value >= 1e15) {
227
    // Number bigger than 1E15; use that notation.
228
0
    StringAppendF(&s, "%0.3G", value);
229
0
  } else {
230
0
    static const char units[] = "kMBT";
231
0
    const char *unit = units;
232
0
    while (value >= 1e6) {
233
0
      value /= 1e3;
234
0
      ++unit;
235
0
      CHECK(unit < units + arraysize(units));
236
0
    }
237
0
    StringAppendF(&s, "%.2f%c", value / 1000.0, *unit);
238
0
  }
239
0
  return s;
240
0
}
241
242
0
bool HumanReadableNum::ToDouble(const string &str, double *value) {
243
0
  char *end;
244
0
  double d = strtod(str.c_str(), &end);
245
  // Allow the string to contain at most one extra character:
246
0
  if (end + 1 < str.c_str() + str.size())
247
0
    return false;
248
0
  const char scale = *end;
249
0
  if ((scale == 'k') || (scale == 'K')) {
250
0
    d *= 1e3;
251
0
  } else if (scale == 'M') {
252
0
    d *= 1e6;
253
0
  } else if (scale == 'B') {
254
0
    d *= 1e9;
255
0
  } else if (scale == 'T') {
256
0
    d *= 1e12;
257
0
  } else if (scale != '\0') {
258
0
    return false;
259
0
  }
260
0
  *value = d;
261
0
  return true;
262
0
}
263
264
0
bool HumanReadableInt::ToInt64(const string &str, int64 *value) {
265
0
  char *end;
266
0
  double d = strtod(str.c_str(), &end);
267
  // We have to cast kint64max to double to avoid a warning on implicit cast that changes the value.
268
0
  if (d > static_cast<double>(kint64max) || d < kint64min)
269
0
    return false;
270
0
  if (*end == 'k') {
271
0
    d *= 1000;
272
0
  } else if (*end == 'M') {
273
0
    d *= 1e6;
274
0
  } else if (*end == 'B') {
275
0
    d *= 1e9;
276
0
  } else if (*end == 'T') {
277
0
    d *= 1e12;
278
0
  } else if (*end != '\0') {
279
0
    return false;
280
0
  }
281
0
  *value = static_cast<int64>(d < 0 ? d - 0.5 : d + 0.5);
282
0
  return true;
283
0
}
284
285
// Abbreviations used here are acceptable English abbreviations
286
// without the ending period (".") for brevity, except for uncommon
287
// abbreviations, in which case the entire word is spelled out. ("mo"
288
// and "mos" are not good abbreviations for "months" -- with or
289
// without the period). If needed, one can add a
290
// HumanReadableTime::ToStringShort() for shorter abbreviations or one
291
// for always spelling out the unit, HumanReadableTime::ToStringLong().
292
1
string HumanReadableElapsedTime::ToShortString(double seconds) {
293
1
  string human_readable;
294
295
1
  if (seconds < 0) {
296
0
    human_readable = "-";
297
0
    seconds = -seconds;
298
0
  }
299
300
  // Start with ns and keep going up to years.
301
1
  if (seconds < 0.000001) {
302
0
    StringAppendF(&human_readable, "%0.3g ns", seconds * 1000000000.0);
303
0
    return human_readable;
304
0
  }
305
1
  if (seconds < 0.001) {
306
0
    StringAppendF(&human_readable, "%0.3g us", seconds * 1000000.0);
307
0
    return human_readable;
308
0
  }
309
1
  if (seconds < 1.0) {
310
1
    StringAppendF(&human_readable, "%0.3g ms", seconds * 1000.0);
311
1
    return human_readable;
312
1
  }
313
0
  if (seconds < 60.0) {
314
0
    StringAppendF(&human_readable, "%0.3g s", seconds);
315
0
    return human_readable;
316
0
  }
317
0
  seconds /= 60.0;
318
0
  if (seconds < 60.0) {
319
0
    StringAppendF(&human_readable, "%0.3g min", seconds);
320
0
    return human_readable;
321
0
  }
322
0
  seconds /= 60.0;
323
0
  if (seconds < 24.0) {
324
0
    StringAppendF(&human_readable, "%0.3g h", seconds);
325
0
    return human_readable;
326
0
  }
327
0
  seconds /= 24.0;
328
0
  if (seconds < 30.0) {
329
0
    StringAppendF(&human_readable, "%0.3g days", seconds);
330
0
    return human_readable;
331
0
  }
332
0
  if (seconds < 365.2425) {
333
0
    StringAppendF(&human_readable, "%0.3g months", seconds / 30.436875);
334
0
    return human_readable;
335
0
  }
336
0
  seconds /= 365.2425;
337
0
  StringAppendF(&human_readable, "%0.3g years", seconds);
338
0
  return human_readable;
339
0
}
340
341
0
bool HumanReadableElapsedTime::ToDouble(const string& str, double* value) {
342
0
  struct TimeUnits {
343
0
    const char* unit;  // unit name
344
0
    double seconds;    // number of seconds in that unit (minutes => 60)
345
0
  };
346
347
  // These must be sorted in decreasing length.  In particulary, a
348
  // string must exist before and of its substrings or the substring
349
  // will match;
350
0
  static const TimeUnits kUnits[] = {
351
    // Long forms
352
0
    { "nanosecond", 0.000000001 },
353
0
    { "microsecond", 0.000001 },
354
0
    { "millisecond", 0.001 },
355
0
    { "second", 1.0 },
356
0
    { "minute", 60.0 },
357
0
    { "hour", 3600.0 },
358
0
    { "day", 86400.0 },
359
0
    { "week", 7 * 86400.0 },
360
0
    { "month", 30 * 86400.0 },
361
0
    { "year", 365 * 86400.0 },
362
363
    // Abbreviated forms
364
0
    { "nanosec", 0.000000001 },
365
0
    { "microsec", 0.000001 },
366
0
    { "millisec", 0.001 },
367
0
    { "sec", 1.0 },
368
0
    { "min", 60.0 },
369
0
    { "hr", 3600.0 },
370
0
    { "dy", 86400.0 },
371
0
    { "wk", 7 * 86400.0 },
372
0
    { "mon", 30 * 86400.0 },
373
0
    { "yr", 365 * 86400.0 },
374
375
    // nano -> n
376
0
    { "nsecond", 0.000000001 },
377
0
    { "nsec", 0.000000001 },
378
    // micro -> u
379
0
    { "usecond", 0.000001 },
380
0
    { "usec", 0.000001 },
381
    // milli -> m
382
0
    { "msecond", 0.001 },
383
0
    { "msec", 0.001 },
384
385
    // Ultra-short form
386
0
    { "ns", 0.000000001 },
387
0
    { "us", 0.000001 },
388
0
    { "ms", 0.001 },
389
0
    { "s", 1.0 },
390
0
    { "m", 60.0 },
391
0
    { "h", 3600.0 },
392
0
    { "d", 86400.0 },
393
0
    { "w", 7 * 86400.0 },
394
0
    { "M", 30 * 86400.0 },  // upper-case M to disambiguate with minute
395
0
    { "y", 365 * 86400.0 }
396
0
  };
397
398
0
  char* unit_start;     // Start of unit name.
399
0
  double work_value = 0;
400
0
  int sign = 1;
401
0
  const char* interval_start = SkipLeadingWhiteSpace(str.c_str());
402
0
  if (*interval_start == '-') {
403
0
    sign = -1;
404
0
    interval_start = SkipLeadingWhiteSpace(interval_start + 1);
405
0
  } else if (*interval_start == '+') {
406
0
    interval_start = SkipLeadingWhiteSpace(interval_start + 1);
407
0
  }
408
0
  if (!*interval_start) {
409
    // Empty string and strings with just a sign are illegal.
410
0
    return false;
411
0
  }
412
0
  do {
413
    // Leading signs on individual values are not allowed.
414
0
    if (*interval_start == '-' || *interval_start == '+') {
415
0
      return false;
416
0
    }
417
0
    double factor = strtod(interval_start, &unit_start);
418
0
    if (interval_start == unit_start) {
419
      // Illegally formatted value, no values consumed by strtod.
420
0
      return false;
421
0
    }
422
0
    unit_start = SkipLeadingWhiteSpace(unit_start);
423
0
    bool found_unit = false;
424
0
    for (size_t i = 0; !found_unit && i < ARRAYSIZE(kUnits); ++i) {
425
0
      const size_t unit_len = strlen(kUnits[i].unit);
426
0
      if (strncmp(unit_start, kUnits[i].unit, unit_len) == 0) {
427
0
        work_value += factor * kUnits[i].seconds;
428
0
        interval_start = unit_start + unit_len;
429
        // Allowing pluralization of any unit (except empty string)
430
0
        if (unit_len > 0 && *interval_start == 's') {
431
0
            interval_start++;
432
0
        }
433
0
        found_unit = true;
434
0
      }
435
0
    }
436
0
    if (!found_unit) {
437
0
      return false;
438
0
    }
439
0
    interval_start = SkipLeadingWhiteSpace(interval_start);
440
0
  } while (*interval_start);
441
442
0
  *value = sign * work_value;
443
0
  return true;
444
0
}