Skip to content

Commit

Permalink
MonetaryAmount from std::string_view should be be constructible with …
Browse files Browse the repository at this point in the history
…no amount when currency is non default
  • Loading branch information
sjanel committed Oct 28, 2023
1 parent e095a10 commit 715d209
Show file tree
Hide file tree
Showing 16 changed files with 71 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/api/common/src/ssl_sha.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ inline EVPMDCTXUniquePtr InitEVPMDCTXUniquePtr(ShaType shaType) {
}

inline string EVPBinToHex(const EVPMDCTXUniquePtr& mdctx) {
unsigned int len;
unsigned int len = 0;
unsigned char binData[EVP_MAX_MD_SIZE];
EVP_DigestFinal_ex(mdctx.get(), binData, &len);
return BinToHex(std::span<const unsigned char>(binData, len));
Expand Down
6 changes: 3 additions & 3 deletions src/main/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

int main(int argc, const char* argv[]) {
try {
auto cmdLineOptionsVector = cct::CoincenterCommands::ParseOptions(argc, argv);
const auto cmdLineOptionsVector = cct::CoincenterCommands::ParseOptions(argc, argv);

if (!cmdLineOptionsVector.empty()) {
cct::CoincenterCommands coincenterCommands(cmdLineOptionsVector);
auto programName = std::filesystem::path(argv[0]).filename().string();
const cct::CoincenterCommands coincenterCommands(cmdLineOptionsVector);
const auto programName = std::filesystem::path(argv[0]).filename().string();

cct::ProcessCommandsFromCLI(programName, coincenterCommands, cmdLineOptionsVector.front(),
cct::settings::RunMode::kProd);
Expand Down
38 changes: 20 additions & 18 deletions src/objects/include/currencycode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,39 +69,40 @@ class CurrencyCodeIterator {
using pointer = const char *;
using reference = const char &;

constexpr auto operator<=>(const CurrencyCodeIterator &) const noexcept = default;
bool operator==(const CurrencyCodeIterator &) const noexcept = default;
constexpr std::strong_ordering operator<=>(const CurrencyCodeIterator &) const noexcept = default;

CurrencyCodeIterator &operator++() noexcept { // Prefix increment
constexpr bool operator==(const CurrencyCodeIterator &) const noexcept = default;

constexpr CurrencyCodeIterator &operator++() noexcept { // Prefix increment
++_pos;
return *this;
}

CurrencyCodeIterator &operator--() noexcept { // Prefix decrement
constexpr CurrencyCodeIterator &operator--() noexcept { // Prefix decrement
--_pos;
return *this;
}

CurrencyCodeIterator operator++(int) noexcept { // Postfix increment
constexpr CurrencyCodeIterator operator++(int) noexcept { // Postfix increment
CurrencyCodeIterator oldSelf = *this;
++*this;
return oldSelf;
}

CurrencyCodeIterator operator--(int) noexcept { // Postfix decrement
constexpr CurrencyCodeIterator operator--(int) noexcept { // Postfix decrement
CurrencyCodeIterator oldSelf = *this;
--*this;
return oldSelf;
}

char operator*() const noexcept { return CurrencyCodeBase::CharAt(_data, static_cast<int>(_pos)); }
constexpr char operator*() const noexcept { return CurrencyCodeBase::CharAt(_data, static_cast<int>(_pos)); }
// operator-> cannot be implemented here - we would need a const char * but it's not possible.

private:
friend class CurrencyCode;

// Default constructor needed for an iterator in C++20
explicit CurrencyCodeIterator(uint64_t data = 0, uint64_t pos = 0) noexcept : _data(data), _pos(pos) {}
constexpr explicit CurrencyCodeIterator(uint64_t data = 0, uint64_t pos = 0) noexcept : _data(data), _pos(pos) {}

uint64_t _data;
uint64_t _pos;
Expand Down Expand Up @@ -137,8 +138,8 @@ class CurrencyCode {
_data = CurrencyCodeBase::StrToBmp(acronym);
}

const_iterator begin() const { return const_iterator(_data); }
const_iterator end() const { return const_iterator(_data, size()); }
constexpr const_iterator begin() const { return const_iterator(_data); }
constexpr const_iterator end() const { return const_iterator(_data, size()); }

constexpr uint64_t size() const {
uint64_t sz = 0;
Expand All @@ -165,7 +166,7 @@ class CurrencyCode {
return false;
}
for (uint32_t charPos = 0; charPos < kMaxLen; ++charPos) {
char ch = (*this)[charPos];
const char ch = (*this)[charPos];
if (ch == CurrencyCodeBase::kFirstAuthorizedLetter) {
return curStr.size() == charPos;
}
Expand All @@ -178,16 +179,16 @@ class CurrencyCode {

/// Append currency string representation to given string.
void appendStrTo(string &str) const {
auto len = size();
const auto len = size();
str.append(len, '\0');
append(str.end() - len);
}

/// Append currency string representation to given output iterator
template <class OutputIt>
OutputIt append(OutputIt it) const {
constexpr OutputIt append(OutputIt it) const {
for (uint32_t charPos = 0; charPos < kMaxLen; ++charPos) {
char ch = (*this)[charPos];
const char ch = (*this)[charPos];
if (ch == CurrencyCodeBase::kFirstAuthorizedLetter) {
break;
}
Expand All @@ -205,13 +206,13 @@ class CurrencyCode {
constexpr char operator[](uint32_t pos) const { return CurrencyCodeBase::CharAt(_data, static_cast<int>(pos)); }

/// Note that this respects the lexicographical order - chars are encoded from the most significant bits first
constexpr auto operator<=>(const CurrencyCode &) const noexcept = default;
constexpr std::strong_ordering operator<=>(const CurrencyCode &) const noexcept = default;

constexpr bool operator==(const CurrencyCode &) const noexcept = default;

friend std::ostream &operator<<(std::ostream &os, const CurrencyCode &cur) {
for (uint32_t charPos = 0; charPos < kMaxLen; ++charPos) {
char ch = cur[charPos];
const char ch = cur[charPos];
if (ch == CurrencyCodeBase::kFirstAuthorizedLetter) {
break;
}
Expand Down Expand Up @@ -257,7 +258,7 @@ class CurrencyCode {

/// Append currency string representation to given string, with a space before (used by MonetaryAmount)
void appendStrWithSpaceTo(string &str) const {
auto len = size();
const auto len = size();
str.append(len + 1UL, ' ');
append(str.end() - len);
}
Expand All @@ -269,7 +270,8 @@ class CurrencyCode {
template <>
struct fmt::formatter<cct::CurrencyCode> {
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
const auto it = ctx.begin();
const auto end = ctx.end();
if (it != end && *it != '}') {
throw format_error("invalid format");
}
Expand Down
4 changes: 2 additions & 2 deletions src/objects/include/currencyexchange.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include <compare>
#include <cstdint>
#include <ostream>
#include <string_view>

#include "cct_string.hpp"
#include "currencycode.hpp"
Expand Down Expand Up @@ -50,7 +50,7 @@ class CurrencyExchange {
bool isFiat() const { return _isFiat; }

// Compare by standard code first.
constexpr auto operator<=>(const CurrencyExchange &) const noexcept = default;
constexpr std::strong_ordering operator<=>(const CurrencyExchange &) const noexcept = default;

constexpr bool operator==(const CurrencyExchange &) const noexcept = default;

Expand Down
18 changes: 8 additions & 10 deletions src/objects/include/exchangename.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ class ExchangeName {
ExchangeName(std::string_view exchangeName, std::string_view keyName);

std::string_view name() const {
std::size_t underscore = underscorePos();
const std::size_t underscore = underscorePos();
return std::string_view(_nameWithKey.data(), underscore == string::npos ? _nameWithKey.size() : underscore);
}

std::string_view keyName() const {
std::size_t underscore = underscorePos();
return std::string_view(_nameWithKey.begin() + (underscore == string::npos ? _nameWithKey.size() : underscore + 1U),
_nameWithKey.end());
const std::size_t underscore = underscorePos();
return {_nameWithKey.begin() + (underscore == string::npos ? _nameWithKey.size() : underscore + 1U),
_nameWithKey.end()};
}

bool isKeyNameDefined() const { return underscorePos() != string::npos; }
Expand All @@ -45,10 +45,7 @@ class ExchangeName {

bool operator==(const ExchangeName &) const noexcept = default;

friend std::ostream &operator<<(std::ostream &os, const ExchangeName &v) {
os << v.str();
return os;
}
friend std::ostream &operator<<(std::ostream &os, const ExchangeName &rhs) { return os << rhs.str(); }

using trivially_relocatable = is_trivially_relocatable<string>::type;

Expand All @@ -64,7 +61,7 @@ class ExchangeName {
};

using ExchangeNameSpan = std::span<const ExchangeName>;
using ExchangeNames = SmallVector<ExchangeName, kTypicalNbPrivateAccounts>;
using ExchangeNames = SmallVector<ExchangeName, 1>;

inline std::string_view ToString(std::string_view exchangeName) { return exchangeName; }
inline std::string_view ToString(const ExchangeName &exchangeName) { return exchangeName.str(); }
Expand Down Expand Up @@ -95,7 +92,8 @@ struct fmt::formatter<cct::ExchangeName> {
bool printKeyName = false;

constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
auto it = ctx.begin();
const auto end = ctx.end();
if (it == end || *it == '}') {
printExchangeName = true;
printKeyName = true;
Expand Down
3 changes: 1 addition & 2 deletions src/objects/include/loadconfiguration.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#pragma once

#include <cstdint>
#include <string_view>

#include "cct_const.hpp"

namespace cct {
class LoadConfiguration {
public:
Expand Down
4 changes: 2 additions & 2 deletions src/objects/include/market.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <array>
#include <ostream>
#include <utility>

#include "cct_format.hpp"
#include "cct_string.hpp"
Expand Down Expand Up @@ -70,7 +69,8 @@ class Market {
template <>
struct fmt::formatter<cct::Market> {
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
auto it = ctx.begin();
const auto end = ctx.end();
if (it != end && *it != '}') {
throw format_error("invalid format");
}
Expand Down
1 change: 0 additions & 1 deletion src/objects/include/marketorderbook.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

#include <limits>
#include <optional>
#include <span>
#include <string_view>
Expand Down
1 change: 1 addition & 0 deletions src/objects/include/monetaryamount.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class MonetaryAmount {
/// Constructs a new MonetaryAmount from a string, containing an optional CurrencyCode.
/// - If a currency is not present, assume default CurrencyCode
/// - If the currency is too long to fit in a CurrencyCode, exception will be raised
/// - If only a currency is given, invalid_argument exception will be raised
/// - If given string is empty, it is equivalent to a default constructor
///
/// A space can be present or not between the amount and the currency code.
Expand Down
1 change: 0 additions & 1 deletion src/objects/include/priceoptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "cct_string.hpp"
#include "monetaryamount.hpp"
#include "priceoptionsdef.hpp"
#include "timedef.hpp"

namespace cct {
class PriceOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/objects/include/volumeandpricenbdecimals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace cct {
struct VolAndPriNbDecimals {
constexpr bool operator==(const VolAndPriNbDecimals &o) const = default;
constexpr bool operator==(const VolAndPriNbDecimals &) const noexcept = default;

int8_t volNbDecimals = std::numeric_limits<uintmax_t>::digits10;
int8_t priNbDecimals = std::numeric_limits<uintmax_t>::digits10;
Expand Down
3 changes: 2 additions & 1 deletion src/objects/include/wallet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ class Wallet {
template <>
struct fmt::formatter<cct::Wallet> {
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
auto it = ctx.begin();
const auto end = ctx.end();
if (it != end && *it != '}') {
throw format_error("invalid format");
}
Expand Down
4 changes: 4 additions & 0 deletions src/objects/src/monetaryamount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "cct_config.hpp"
#include "cct_exception.hpp"
#include "cct_invalid_argument_exception.hpp"
#include "currencycode.hpp"
#include "mathhelpers.hpp"
#include "stringhelpers.hpp"
Expand Down Expand Up @@ -155,6 +156,9 @@ MonetaryAmount::MonetaryAmount(std::string_view amountCurrencyStr) {
std::string_view currencyStr(last, endIt);
RemoveTrailingSpaces(currencyStr);
RemovePrefixSpaces(currencyStr);
if (!currencyStr.empty() && amountStr.empty()) {
throw invalid_argument("Cannot construct MonetaryAmount with a currency without any amount");
}
_curWithDecimals = CurrencyCode(currencyStr);
sanitizeDecimals(nbDecimals, maxNbDecimals());
}
Expand Down
14 changes: 14 additions & 0 deletions src/objects/test/currencycode_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,23 @@ TEST(CurrencyCodeTest, UpperConversion) {
EXPECT_EQ(CurrencyCode("etc").str(), "ETC");
}

namespace {
constexpr bool HasZ(CurrencyCode cur) {
for (char ch : cur) {
if (ch == 'Z') {
return true;
}
}
return false;
}
} // namespace

TEST(CurrencyCodeTest, Constexpr) {
static_assert(CurrencyCode("doge") == CurrencyCode("DOGE"));
static_assert(CurrencyCode("XRP").code() != 0);

static_assert(!HasZ(CurrencyCode("LONGCUR")));
static_assert(HasZ(CurrencyCode("GTZFD")));
}

TEST(CurrencyCodeTest, Iterator) {
Expand Down
6 changes: 6 additions & 0 deletions src/objects/test/monetaryamount_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <optional>

#include "cct_exception.hpp"
#include "cct_invalid_argument_exception.hpp"
#include "cct_string.hpp"
#include "currencycode.hpp"
#include "mathhelpers.hpp"
Expand Down Expand Up @@ -180,6 +181,8 @@ TEST(MonetaryAmountTest, OverflowProtectionMultiplication) {
MonetaryAmount("0.00426622338114037", cur));
EXPECT_EQ(MonetaryAmount("38.0566894350664") * MonetaryAmount("0.00008795", cur),
MonetaryAmount("0.00334708583581405", cur));
EXPECT_EQ((-1) * MonetaryAmount("-9223372036854775807", cur), MonetaryAmount("922337203685477580", cur));
EXPECT_EQ((-1) * MonetaryAmount("-922337203685477580", cur), MonetaryAmount("922337203685477580", cur));
}
}

Expand Down Expand Up @@ -238,10 +241,13 @@ TEST(MonetaryAmountTest, StringConstructor) {
EXPECT_EQ(MonetaryAmount("-210.50 CAKE"), MonetaryAmount("-210.50", "CAKE"));
EXPECT_EQ(MonetaryAmount("05AUD"), MonetaryAmount(5, "AUD"));
EXPECT_EQ(MonetaryAmount("746REPV2"), MonetaryAmount("746", "REPV2"));

EXPECT_THROW(MonetaryAmount("usdt"), invalid_argument);
}

TEST(MonetaryAmountTest, StringConstructorAmbiguity) {
EXPECT_EQ(MonetaryAmount("804.621INCH"), MonetaryAmount("804.621", "INCH"));
EXPECT_EQ(MonetaryAmount("804.62 1INCH"), MonetaryAmount("804.62", "1INCH"));
EXPECT_EQ(MonetaryAmount("804.62", "1INCH"), MonetaryAmount("804.62", "1INCH"));
}

Expand Down
8 changes: 6 additions & 2 deletions src/tech/include/stringhelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <concepts>
#include <cstring>
#include <string_view>
#include <system_error>

#include "cct_config.hpp"
#include "cct_exception.hpp"
Expand All @@ -30,9 +31,12 @@ inline string ToString(std::integral auto i) {

template <std::integral Integral>
Integral FromString(std::string_view str) {
Integral ret;
Integral ret{};
if (auto [ptr, errc] = std::from_chars(str.data(), str.data() + str.size(), ret); CCT_UNLIKELY(errc != std::errc())) {
throw exception("Unable to decode string into integral");
if (errc == std::errc::result_out_of_range) {
throw exception("'{}' would produce an out of range integral", str);
}
throw exception("Unable to decode '{}' into integral", str);
}
return ret;
}
Expand Down

0 comments on commit 715d209

Please sign in to comment.