mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-04 03:40:07 +00:00
Javascript: Move javascript implementation into a separate module
This commit is contained in:
parent
637226addc
commit
df6f3f8e28
69 changed files with 941 additions and 1351 deletions
600
modules/juce_javascript/choc/text/choc_StringUtilities.h
Normal file
600
modules/juce_javascript/choc/text/choc_StringUtilities.h
Normal file
|
|
@ -0,0 +1,600 @@
|
|||
//
|
||||
// ██████ ██ ██ ██████ ██████
|
||||
// ██ ██ ██ ██ ██ ██ ** Classy Header-Only Classes **
|
||||
// ██ ███████ ██ ██ ██
|
||||
// ██ ██ ██ ██ ██ ██ https://github.com/Tracktion/choc
|
||||
// ██████ ██ ██ ██████ ██████
|
||||
//
|
||||
// CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license:
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any purpose with or
|
||||
// without fee is hereby granted, provided that the above copyright notice and this permission
|
||||
// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#ifndef CHOC_STRING_UTILS_HEADER_INCLUDED
|
||||
#define CHOC_STRING_UTILS_HEADER_INCLUDED
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <cwctype>
|
||||
#include "../platform/choc_Assert.h"
|
||||
|
||||
namespace choc::text
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
inline bool isWhitespace (char c) { return c == ' ' || (c <= 13 && c >= 9); }
|
||||
inline bool isDigit (char c) { return static_cast<uint32_t> (c - '0') < 10; }
|
||||
|
||||
/// Replaces all occurrences of a one or more substrings.
|
||||
/// The arguments must be a sequence of pairs of strings, where the first of each pair is the string to
|
||||
/// look for, followed by its replacement.
|
||||
template <typename StringType, typename... OtherReplacements>
|
||||
[[nodiscard]] std::string replace (StringType textToSearch,
|
||||
std::string_view firstSubstringToReplace, std::string_view firstReplacement,
|
||||
OtherReplacements&&... otherPairsOfStringsToReplace);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its start and end.
|
||||
[[nodiscard]] std::string trim (std::string textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its start and end.
|
||||
[[nodiscard]] std::string_view trim (std::string_view textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its start and end.
|
||||
[[nodiscard]] std::string_view trim (const char* textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its start.
|
||||
[[nodiscard]] std::string trimStart (std::string textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its start.
|
||||
[[nodiscard]] std::string_view trimStart (std::string_view textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its start.
|
||||
[[nodiscard]] std::string_view trimStart (const char* textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its end.
|
||||
[[nodiscard]] std::string trimEnd (std::string textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its end.
|
||||
[[nodiscard]] std::string_view trimEnd (std::string_view textToTrim);
|
||||
|
||||
/// Returns a string with any whitespace trimmed from its end.
|
||||
[[nodiscard]] std::string_view trimEnd (const char* textToTrim);
|
||||
|
||||
/// If the string begins with one or more instances of the given character, this
|
||||
/// skips past them, returning the remainder of the string.
|
||||
[[nodiscard]] std::string_view trimCharacterAtStart (std::string_view textToTrim, char characterToSkip);
|
||||
|
||||
/// If the given character is at the start and end of the string, it trims it away.
|
||||
[[nodiscard]] std::string removeOuterCharacter (std::string text, char outerChar);
|
||||
|
||||
[[nodiscard]] inline std::string removeDoubleQuotes (std::string text) { return removeOuterCharacter (std::move (text), '"'); }
|
||||
[[nodiscard]] inline std::string removeSingleQuotes (std::string text) { return removeOuterCharacter (std::move (text), '\''); }
|
||||
|
||||
[[nodiscard]] inline std::string addDoubleQuotes (std::string text) { return "\"" + std::move (text) + "\""; }
|
||||
[[nodiscard]] inline std::string addSingleQuotes (std::string text) { return "'" + std::move (text) + "'"; }
|
||||
|
||||
[[nodiscard]] std::string toLowerCase (std::string);
|
||||
[[nodiscard]] std::string toUpperCase (std::string);
|
||||
|
||||
template <typename IsDelimiterChar>
|
||||
[[nodiscard]] std::vector<std::string> splitString (std::string_view textToSplit,
|
||||
IsDelimiterChar&& isDelimiterChar,
|
||||
bool includeDelimitersInResult);
|
||||
|
||||
template <typename CharStartsDelimiter, typename CharIsInDelimiterBody>
|
||||
[[nodiscard]] std::vector<std::string> splitString (std::string_view textToSplit,
|
||||
CharStartsDelimiter&& isDelimiterStart,
|
||||
CharIsInDelimiterBody&& isDelimiterBody,
|
||||
bool includeDelimitersInResult);
|
||||
|
||||
[[nodiscard]] std::vector<std::string> splitString (std::string_view textToSplit,
|
||||
char delimiterCharacter,
|
||||
bool includeDelimitersInResult);
|
||||
|
||||
[[nodiscard]] std::vector<std::string> splitAtWhitespace (std::string_view text,
|
||||
bool keepDelimiters = false);
|
||||
|
||||
/// Splits a string at newline characters, returning an array of strings.
|
||||
[[nodiscard]] std::vector<std::string> splitIntoLines (std::string_view text,
|
||||
bool includeNewLinesInResult);
|
||||
|
||||
/// Joins some kind of array of strings into a single string, adding the given separator
|
||||
/// between them (but not adding it at the start or end)
|
||||
template <typename ArrayOfStrings>
|
||||
[[nodiscard]] std::string joinStrings (const ArrayOfStrings& strings,
|
||||
std::string_view separator);
|
||||
|
||||
/// Returns true if this text contains the given sub-string.
|
||||
bool contains (std::string_view text, std::string_view possibleSubstring);
|
||||
/// Returns true if this text starts with the given character.
|
||||
bool startsWith (std::string_view text, char possibleStart);
|
||||
/// Returns true if this text starts with the given sub-string.
|
||||
bool startsWith (std::string_view text, std::string_view possibleStart);
|
||||
/// Returns true if this text ends with the given sub-string.
|
||||
bool endsWith (std::string_view text, char possibleEnd);
|
||||
/// Returns true if this text ends with the given sub-string.
|
||||
bool endsWith (std::string_view text, std::string_view possibleEnd);
|
||||
|
||||
/// Calculates the Levenstein distance between two strings.
|
||||
template <typename StringType>
|
||||
size_t getLevenshteinDistance (const StringType& string1,
|
||||
const StringType& string2);
|
||||
|
||||
/// Converts a hex character to a number 0-15, or -1 if it's not a valid hex digit.
|
||||
int hexDigitToInt (uint32_t unicodeChar);
|
||||
|
||||
/// Returns a hex string for the given value.
|
||||
/// If the minimum number of digits is non-zero, it will be zero-padded to fill this length;
|
||||
template <typename IntegerType>
|
||||
std::string createHexString (IntegerType value, int minNumDigits = 0);
|
||||
|
||||
/// Returns a truncated, easy-to-read version of a time as hours, seconds or milliseconds,
|
||||
/// depending on its magnitude. The use-cases include things like logging or console app output.
|
||||
std::string getDurationDescription (std::chrono::duration<double, std::micro>);
|
||||
|
||||
/// Returns an easy-to-read description of a size in bytes. Depending on the magnitude,
|
||||
/// it might choose different units such as GB, MB, KB or just bytes.
|
||||
std::string getByteSizeDescription (uint64_t sizeInBytes);
|
||||
|
||||
/// Encodes a string as a legal URI, using percent-encoding (aka URL encoding)
|
||||
std::string percentEncodeURI (std::string_view text);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// _ _ _ _
|
||||
// __| | ___ | |_ __ _ (_)| | ___
|
||||
// / _` | / _ \| __| / _` || || |/ __|
|
||||
// | (_| || __/| |_ | (_| || || |\__ \ _ _ _
|
||||
// \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_)
|
||||
//
|
||||
// Code beyond this point is implementation detail...
|
||||
//
|
||||
//==============================================================================
|
||||
|
||||
inline int hexDigitToInt (uint32_t c)
|
||||
{
|
||||
auto d1 = c - static_cast<uint32_t> ('0'); if (d1 < 10u) return static_cast<int> (d1);
|
||||
auto d2 = d1 + static_cast<uint32_t> ('0' - 'a'); if (d2 < 6u) return static_cast<int> (d2 + 10);
|
||||
auto d3 = d2 + static_cast<uint32_t> ('a' - 'A'); if (d3 < 6u) return static_cast<int> (d3 + 10);
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename IntegerType>
|
||||
std::string createHexString (IntegerType v, int minNumDigits)
|
||||
{
|
||||
static_assert (std::is_integral<IntegerType>::value, "Need to pass integers into this method");
|
||||
auto value = static_cast<typename std::make_unsigned<IntegerType>::type> (v);
|
||||
CHOC_ASSERT (minNumDigits <= 32);
|
||||
|
||||
char hex[40];
|
||||
const auto end = hex + sizeof (hex) - 1;
|
||||
auto d = end;
|
||||
*d = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
*--d = "0123456789abcdef"[static_cast<uint32_t> (value) & 15u];
|
||||
value = static_cast<decltype (value)> (value >> 4);
|
||||
--minNumDigits;
|
||||
|
||||
if (value == 0 && minNumDigits <= 0)
|
||||
return std::string (d, end);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename StringType, typename... OtherReplacements>
|
||||
std::string replace (StringType textToSearch, std::string_view firstToReplace, std::string_view firstReplacement,
|
||||
OtherReplacements&&... otherPairsOfStringsToReplace)
|
||||
{
|
||||
static_assert ((sizeof... (otherPairsOfStringsToReplace) & 1u) == 0,
|
||||
"This function expects a list of pairs of strings as its arguments");
|
||||
|
||||
if constexpr (std::is_same<const StringType, const std::string_view>::value || std::is_same<const StringType, const char* const>::value)
|
||||
{
|
||||
return replace (std::string (textToSearch), firstToReplace, firstReplacement,
|
||||
std::forward<OtherReplacements> (otherPairsOfStringsToReplace)...);
|
||||
}
|
||||
else if constexpr (sizeof... (otherPairsOfStringsToReplace) == 0)
|
||||
{
|
||||
size_t pos = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
pos = textToSearch.find (firstToReplace, pos);
|
||||
|
||||
if (pos == std::string::npos)
|
||||
return textToSearch;
|
||||
|
||||
textToSearch.replace (pos, firstToReplace.length(), firstReplacement);
|
||||
pos += firstReplacement.length();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return replace (replace (std::move (textToSearch), firstToReplace, firstReplacement),
|
||||
std::forward<OtherReplacements> (otherPairsOfStringsToReplace)...);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string trim (std::string text) { return trimStart (trimEnd (std::move (text))); }
|
||||
inline std::string_view trim (std::string_view text) { return trimStart (trimEnd (std::move (text))); }
|
||||
|
||||
inline std::string_view trim (const char* text) { return trim (std::string_view (text)); }
|
||||
inline std::string_view trimStart (const char* text) { return trimStart (std::string_view (text)); }
|
||||
inline std::string_view trimEnd (const char* text) { return trimEnd (std::string_view (text)); }
|
||||
|
||||
inline std::string trimStart (std::string text)
|
||||
{
|
||||
auto i = text.begin();
|
||||
|
||||
if (i == text.end()) return {};
|
||||
if (! isWhitespace (*i)) return text;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
++i;
|
||||
|
||||
if (i == text.end()) return {};
|
||||
if (! isWhitespace (*i)) return { i, text.end() };
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string_view trimStart (std::string_view text)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (auto c : text)
|
||||
{
|
||||
if (! isWhitespace (c))
|
||||
{
|
||||
text.remove_prefix (i);
|
||||
return text;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline std::string trimEnd (std::string text)
|
||||
{
|
||||
for (auto i = text.end();;)
|
||||
{
|
||||
if (i == text.begin())
|
||||
return {};
|
||||
|
||||
--i;
|
||||
|
||||
if (! isWhitespace (*i))
|
||||
{
|
||||
text.erase (i + 1, text.end());
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string_view trimEnd (std::string_view text)
|
||||
{
|
||||
for (auto i = text.length(); i != 0; --i)
|
||||
if (! isWhitespace (text[i - 1]))
|
||||
return text.substr (0, i);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline std::string_view trimCharacterAtStart (std::string_view textToTrim, char characterToSkip)
|
||||
{
|
||||
for (size_t i = 0; i < textToTrim.length(); ++i)
|
||||
if (textToTrim[i] != characterToSkip)
|
||||
return textToTrim.substr (i);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline std::string removeOuterCharacter (std::string t, char outerChar)
|
||||
{
|
||||
if (t.length() >= 2 && t.front() == outerChar && t.back() == outerChar)
|
||||
return t.substr (1, t.length() - 2);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
inline std::string toLowerCase (std::string s)
|
||||
{
|
||||
std::transform (s.begin(), s.end(), s.begin(), [] (auto c) { return static_cast<char> (std::tolower (static_cast<unsigned char> (c))); });
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string toUpperCase (std::string s)
|
||||
{
|
||||
std::transform (s.begin(), s.end(), s.begin(), [] (auto c) { return static_cast<char> (std::toupper (static_cast<unsigned char> (c))); });
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename CharStartsDelimiter, typename CharIsInDelimiterBody>
|
||||
std::vector<std::string> splitString (std::string_view source,
|
||||
CharStartsDelimiter&& isDelimiterStart,
|
||||
CharIsInDelimiterBody&& isDelimiterBody,
|
||||
bool keepDelimiters)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
auto tokenStart = source.begin();
|
||||
auto pos = tokenStart;
|
||||
|
||||
while (pos != source.end())
|
||||
{
|
||||
if (isDelimiterStart (*pos))
|
||||
{
|
||||
auto delimiterStart = pos++;
|
||||
|
||||
while (pos != source.end() && isDelimiterBody (*pos))
|
||||
++pos;
|
||||
|
||||
if (pos != source.begin())
|
||||
tokens.push_back ({ tokenStart, keepDelimiters ? pos : delimiterStart });
|
||||
|
||||
tokenStart = pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != source.begin())
|
||||
tokens.push_back ({ tokenStart, pos });
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
template <typename IsDelimiterChar>
|
||||
std::vector<std::string> splitString (std::string_view source, IsDelimiterChar&& isDelimiterChar, bool keepDelimiters)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
auto tokenStart = source.begin();
|
||||
auto pos = tokenStart;
|
||||
|
||||
while (pos != source.end())
|
||||
{
|
||||
if (isDelimiterChar (*pos))
|
||||
{
|
||||
tokens.push_back ({ tokenStart, keepDelimiters ? pos + 1 : pos });
|
||||
tokenStart = ++pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != source.begin())
|
||||
tokens.push_back ({ tokenStart, pos });
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
inline std::vector<std::string> splitString (std::string_view text, char delimiterCharacter, bool keepDelimiters)
|
||||
{
|
||||
return splitString (text, [=] (char c) { return c == delimiterCharacter; }, keepDelimiters);
|
||||
}
|
||||
|
||||
inline std::vector<std::string> splitAtWhitespace (std::string_view text, bool keepDelimiters)
|
||||
{
|
||||
return splitString (text,
|
||||
[] (char c) { return isWhitespace (c); },
|
||||
[] (char c) { return isWhitespace (c); },
|
||||
keepDelimiters);
|
||||
}
|
||||
|
||||
inline std::vector<std::string> splitIntoLines (std::string_view text, bool includeNewLinesInResult)
|
||||
{
|
||||
return splitString (text, '\n', includeNewLinesInResult);
|
||||
}
|
||||
|
||||
template <typename ArrayOfStrings>
|
||||
inline std::string joinStrings (const ArrayOfStrings& strings, std::string_view sep)
|
||||
{
|
||||
if (strings.empty())
|
||||
return {};
|
||||
|
||||
auto spaceNeeded = sep.length() * strings.size();
|
||||
|
||||
for (auto& s : strings)
|
||||
spaceNeeded += s.length();
|
||||
|
||||
std::string result (strings.front());
|
||||
result.reserve (spaceNeeded);
|
||||
|
||||
for (size_t i = 1; i < strings.size(); ++i)
|
||||
{
|
||||
result += sep;
|
||||
result += strings[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool contains (std::string_view t, std::string_view s) { return t.find (s) != std::string::npos; }
|
||||
inline bool startsWith (std::string_view t, char s) { return ! t.empty() && t.front() == s; }
|
||||
inline bool endsWith (std::string_view t, char s) { return ! t.empty() && t.back() == s; }
|
||||
|
||||
inline bool startsWith (std::string_view t, std::string_view s)
|
||||
{
|
||||
auto len = s.length();
|
||||
return t.length() >= len && t.substr (0, len) == s;
|
||||
}
|
||||
|
||||
inline bool endsWith (std::string_view t, std::string_view s)
|
||||
{
|
||||
auto len1 = t.length(), len2 = s.length();
|
||||
return len1 >= len2 && t.substr (len1 - len2) == s;
|
||||
}
|
||||
|
||||
inline std::string getDurationDescription (std::chrono::duration<double, std::micro> d)
|
||||
{
|
||||
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds> (d).count();
|
||||
|
||||
if (microseconds < 0) return "-" + getDurationDescription (-d);
|
||||
if (microseconds == 0) return "0 sec";
|
||||
|
||||
std::string result;
|
||||
|
||||
auto addLevel = [&] (int64_t size, std::string_view units, int64_t decimalScale, int64_t modulo) -> bool
|
||||
{
|
||||
if (microseconds < size)
|
||||
return false;
|
||||
|
||||
if (! result.empty())
|
||||
result += ' ';
|
||||
|
||||
auto scaled = (microseconds * decimalScale + size / 2) / size;
|
||||
auto whole = scaled / decimalScale;
|
||||
|
||||
if (modulo != 0)
|
||||
whole = whole % modulo;
|
||||
|
||||
result += std::to_string (whole);
|
||||
|
||||
if (auto fraction = scaled % decimalScale)
|
||||
{
|
||||
result += '.';
|
||||
result += static_cast<char> ('0' + (fraction / 10));
|
||||
|
||||
if (fraction % 10 != 0)
|
||||
result += static_cast<char> ('0' + (fraction % 10));
|
||||
}
|
||||
|
||||
result += (whole == 1 && units.length() > 3 && units.back() == 's') ? units.substr (0, units.length() - 1) : units;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool hours = addLevel (60000000ll * 60ll, " hours", 1, 0);
|
||||
bool mins = addLevel (60000000ll, " min", 1, hours ? 60 : 0);
|
||||
|
||||
if (hours)
|
||||
return result;
|
||||
|
||||
if (mins)
|
||||
{
|
||||
addLevel (1000000, " sec", 1, 60);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! addLevel (1000000, " sec", 100, 0))
|
||||
if (! addLevel (1000, " ms", 100, 0))
|
||||
addLevel (1, " microseconds", 100, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename StringType>
|
||||
size_t getLevenshteinDistance (const StringType& string1, const StringType& string2)
|
||||
{
|
||||
if (string1.empty()) return string2.length();
|
||||
if (string2.empty()) return string1.length();
|
||||
|
||||
auto calculate = [] (size_t* costs, size_t numCosts, const StringType& s1, const StringType& s2) -> size_t
|
||||
{
|
||||
for (size_t i = 0; i < numCosts; ++i)
|
||||
costs[i] = i;
|
||||
|
||||
size_t p1 = 0;
|
||||
|
||||
for (auto c1 : s1)
|
||||
{
|
||||
auto corner = p1;
|
||||
*costs = p1 + 1;
|
||||
size_t p2 = 0;
|
||||
|
||||
for (auto c2 : s2)
|
||||
{
|
||||
auto upper = costs[p2 + 1];
|
||||
costs[p2 + 1] = c1 == c2 ? corner : (std::min (costs[p2], std::min (upper, corner)) + 1);
|
||||
++p2;
|
||||
corner = upper;
|
||||
}
|
||||
|
||||
++p1;
|
||||
}
|
||||
|
||||
return costs[numCosts - 1];
|
||||
};
|
||||
|
||||
auto sizeNeeded = string2.length() + 1;
|
||||
constexpr size_t maxStackSize = 96;
|
||||
|
||||
if (sizeNeeded <= maxStackSize)
|
||||
{
|
||||
size_t costs[maxStackSize];
|
||||
return calculate (costs, sizeNeeded, string1, string2);
|
||||
}
|
||||
|
||||
std::unique_ptr<size_t[]> costs (new size_t[sizeNeeded]);
|
||||
return calculate (costs.get(), sizeNeeded, string1, string2);
|
||||
}
|
||||
|
||||
inline std::string getByteSizeDescription (uint64_t size)
|
||||
{
|
||||
auto intToStringWith1DecPlace = [] (uint64_t n, uint64_t divisor) -> std::string
|
||||
{
|
||||
auto scaled = (n * 10 + divisor / 2) / divisor;
|
||||
auto result = std::to_string (scaled / 10);
|
||||
|
||||
if (auto fraction = scaled % 10)
|
||||
{
|
||||
result += '.';
|
||||
result += static_cast<char> ('0' + fraction);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
static constexpr uint64_t maxValue = std::numeric_limits<uint64_t>::max() / 10;
|
||||
|
||||
if (size >= 0x40000000) return intToStringWith1DecPlace (std::min (maxValue, size), 0x40000000) + " GB";
|
||||
if (size >= 0x100000) return intToStringWith1DecPlace (size, 0x100000) + " MB";
|
||||
if (size >= 0x400) return intToStringWith1DecPlace (size, 0x400) + " KB";
|
||||
if (size != 1) return std::to_string (size) + " bytes";
|
||||
|
||||
return "1 byte";
|
||||
}
|
||||
|
||||
inline std::string percentEncodeURI (std::string_view text)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve (text.length());
|
||||
|
||||
for (auto c : text)
|
||||
{
|
||||
if (std::string_view ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.~").find (c) != std::string_view::npos)
|
||||
{
|
||||
result += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += '%';
|
||||
result += "0123456789abcdef"[static_cast<uint8_t> (c) >> 4];
|
||||
result += "0123456789abcdef"[static_cast<uint8_t> (c) & 15u];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace choc::text
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue