From c610b1d62deb15edca4779c72dc0a459af88877d Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 21 Mar 2021 12:33:18 +0100 Subject: [PATCH] Move the value reading helpers out of Opcode --- src/sfizz/Opcode.cpp | 46 ++++++------------ src/sfizz/StringViewHelpers.h | 88 ++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 32 deletions(-) diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index 406032dc..0f1adfb2 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -147,21 +147,20 @@ absl::optional readInt_(OpcodeSpec spec, absl::string_view v) { using Limits = std::numeric_limits; - size_t numberEnd = 0; - - if (numberEnd < v.size() && (v[numberEnd] == '+' || v[numberEnd] == '-')) - ++numberEnd; - - while (numberEnd < v.size() && absl::ascii_isdigit(v[numberEnd])) - ++numberEnd; - - if (numberEnd == 0 && (spec.flags & kCanBeNote)) - return readNoteValue(v); - - v = v.substr(0, numberEnd); - int64_t returnedValue; - if (!absl::SimpleAtoi(v, &returnedValue)) + bool readValueSuccess = false; + + if (readLeadingInt(v, &returnedValue)) + readValueSuccess = true; + + if (!readValueSuccess && (spec.flags & kCanBeNote)) { + if (absl::optional noteValue = readNoteValue(v)) { + returnedValue = *noteValue; + readValueSuccess = true; + } + } + + if (!readValueSuccess) return absl::nullopt; if (returnedValue > static_cast(spec.bounds.getEnd())) { @@ -201,23 +200,8 @@ INSTANTIATE_FOR_INTEGRAL(int64_t) template absl::optional readFloat_(OpcodeSpec spec, absl::string_view v) { - size_t numberEnd = 0; - - if (numberEnd < v.size() && (v[numberEnd] == '+' || v[numberEnd] == '-')) - ++numberEnd; - while (numberEnd < v.size() && absl::ascii_isdigit(v[numberEnd])) - ++numberEnd; - - if (numberEnd < v.size() && v[numberEnd] == '.') { - ++numberEnd; - while (numberEnd < v.size() && absl::ascii_isdigit(v[numberEnd])) - ++numberEnd; - } - - v = v.substr(0, numberEnd); - - float returnedValue; - if (!absl::SimpleAtof(v, &returnedValue)) + T returnedValue; + if (!readLeadingFloat(v, &returnedValue)) return absl::nullopt; if (spec.flags & kWrapPhase) diff --git a/src/sfizz/StringViewHelpers.h b/src/sfizz/StringViewHelpers.h index 11026f34..b865f8a3 100644 --- a/src/sfizz/StringViewHelpers.h +++ b/src/sfizz/StringViewHelpers.h @@ -11,7 +11,9 @@ */ #pragma once -#include "absl/strings/string_view.h" +#include +#include +#include #include /** @@ -101,3 +103,87 @@ uint64_t hashNumber(Int i, uint64_t h = Fnv1aBasis) "The hashed object must be of arithmetic type"); return hash(absl::string_view(reinterpret_cast(&i), sizeof(i)), h); } + +/** + * @brief Read a floating-point number from a string non-permissively + * + * @param[in] input a string to read from + * @param[out] result address of the value where the result is stored + * @return whether the conversion is successful + */ +template +bool readFloat(absl::string_view input, F* result); + +template <> +inline bool readFloat(absl::string_view input, float* result) +{ + return absl::SimpleAtof(input, result); +} + +template <> +inline bool readFloat(absl::string_view input, double* result) +{ + return absl::SimpleAtod(input, result); +} + +/** + * @brief Read an integer from a string, permitting extra trailing characters. + * + * @param[in] input a string to read from + * @param[out] result address of the value where the result is stored + * @param[out] rest optional address which receives the unprocessed tail of the input + * @return whether the conversion is successful + */ +template +bool readLeadingInt(absl::string_view input, I* result, absl::string_view* rest = nullptr) +{ + size_t numberEnd = 0; + + if (numberEnd < input.size() && (input[numberEnd] == '+' || input[numberEnd] == '-')) + ++numberEnd; + + while (numberEnd < input.size() && absl::ascii_isdigit(input[numberEnd])) + ++numberEnd; + + if (!absl::SimpleAtoi(input.substr(0, numberEnd), result)) + return false; + + if (rest) + *rest = input.substr(numberEnd); + + return true; +} + +/** + * @brief Read a floating-point number from a string, permitting extra + * trailing characters. + * + * @param[in] input a string to read from + * @param[out] result address of the value where the result is stored + * @param[out] rest optional address which receives the unprocessed tail of the input + * @return whether the conversion is successful + */ +template +bool readLeadingFloat(absl::string_view input, F* result, absl::string_view* rest = nullptr) +{ + size_t numberEnd = 0; + + if (numberEnd < input.size() && (input[numberEnd] == '+' || input[numberEnd] == '-')) + ++numberEnd; + while (numberEnd < input.size() && absl::ascii_isdigit(input[numberEnd])) + ++numberEnd; + + if (numberEnd < input.size() && input[numberEnd] == '.') { + ++numberEnd; + while (numberEnd < input.size() && absl::ascii_isdigit(input[numberEnd])) + ++numberEnd; + } + + if (!readFloat(input.substr(0, numberEnd), result)) + return false; + + if (rest) + *rest = input.substr(numberEnd); + + return true; +}