Repository: duckdb/duckdb
File: src/common/string_util.cpp (C++)

Code before:

string StringUtil::BytesToHumanReadableString(idx_t bytes, idx_t multiplier) {
	D_ASSERT(multiplier == 1000 || multiplier == 1024);
	idx_t array[6] = {};
	const char *unit[2][6] = {{"bytes", "KiB", "MiB", "GiB", "TiB", "PiB"}, {"bytes", "kB", "MB", "GB", "TB", "PB"}};

	const int sel = (multiplier == 1000);

	array[0] = bytes;
	for (idx_t i = 1; i < 6; i++) {
		array[i] = array[i - 1] / multiplier;
		array[i - 1] %= multiplier;
	}

	for (idx_t i = 5; i >= 1; i--) {
		if (array[i]) {
			// Map 0 -> 0 and (multiplier-1) -> 9
			idx_t fractional_part = (array[i - 1] * 10) / multiplier;
			return to_string(array[i]) + "." + to_string(fractional_part) + " " + unit[sel][i];
		}
	}

	return to_string(array[0]) + (bytes == 1 ? " byte" : " bytes");
}

idx_t StringUtil::ParseFormattedBytes(const string &arg) {
	if (arg[0] == '-' || arg == "null" || arg == "none") {
		// infinite
		return NumericLimits<idx_t>::Maximum();
	}
	// split based on the number/non-number
	idx_t idx = 0;
	while (StringUtil::CharacterIsSpace(arg[idx])) {
		idx++;
	}
	idx_t num_start = idx;
	while ((arg[idx] >= '0' && arg[idx] <= '9') || arg[idx] == '.' || arg[idx] == 'e' || arg[idx] == 'E' ||
	       arg[idx] == '-') {
		idx++;
	}
	if (idx == num_start) {
		throw NonNumericMemoryException("Memory must have a number (e.g. 1GB)");
	}
	string number = arg.substr(num_start, idx - num_start);

	// try to parse the number
	double limit = Cast::Operation<string_t, double>(string_t(number));

	// now parse the memory limit unit (e.g. bytes, gb, etc)
	while (StringUtil::CharacterIsSpace(arg[idx])) {
		idx++;

Review comment (suggestion):
Instead of throwing an exception in the function, could you implement this as:
```c++
bool StringUtil::TryParseFormattedBytes(const string &arg, idx_t result) {
    // ....
}

idx_t StringUtil::ParseFormattedBytes(const string &arg) {
    idx_t result;
    if (!TryParseFormattedBytes(arg, result)) {
        // throw
    }
}
```
This avoids having to do `try`/`catch` in other places, and the need to add the new exception class `NonNumericMemoryException`.

Code after:

string StringUtil::BytesToHumanReadableString(idx_t bytes, idx_t multiplier) {
	D_ASSERT(multiplier == 1000 || multiplier == 1024);
	idx_t array[6] = {};
	const char *unit[2][6] = {{"bytes", "KiB", "MiB", "GiB", "TiB", "PiB"}, {"bytes", "kB", "MB", "GB", "TB", "PB"}};

	const int sel = (multiplier == 1000);

	array[0] = bytes;
	for (idx_t i = 1; i < 6; i++) {
		array[i] = array[i - 1] / multiplier;
		array[i - 1] %= multiplier;
	}

	for (idx_t i = 5; i >= 1; i--) {
		if (array[i]) {
			// Map 0 -> 0 and (multiplier-1) -> 9
			idx_t fractional_part = (array[i - 1] * 10) / multiplier;
			return to_string(array[i]) + "." + to_string(fractional_part) + " " + unit[sel][i];
		}
	}

	return to_string(array[0]) + (bytes == 1 ? " byte" : " bytes");
}

string StringUtil::TryParseFormattedBytes(const string &arg, idx_t &result) {
	// split based on the number/non-number
	idx_t idx = 0;
	while (StringUtil::CharacterIsSpace(arg[idx])) {
		idx++;
	}
	idx_t num_start = idx;
	while ((arg[idx] >= '0' && arg[idx] <= '9') || arg[idx] == '.' || arg[idx] == 'e' || arg[idx] == 'E' ||
	       arg[idx] == '-') {
		idx++;
	}
	if (idx == num_start) {
		return "Memory must have a number (e.g. 1GB)";
	}
	string number = arg.substr(num_start, idx - num_start);

	// try to parse the number
	double limit;
	bool success = TryCast::Operation<string_t, double>(string_t(number), limit);
	if (!success) {
		return StringUtil::Format("Invalid memory limit: '%s'", number);
	}

	// now parse the memory limit unit (e.g. bytes, gb, etc)
	while (StringUtil::CharacterIsSpace(arg[idx])) {
		idx++;