From 82772695a621f7f07ce654f676d14ca70b908e63 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Sun, 30 Nov 2025 06:40:15 -0500 Subject: chore: safer crc32 helper call. Signed-off-by: Amlal El Mahrouss --- include/ocl/fix/parser.hpp | 143 +++++++++++++++---------------------------- include/ocl/hashing/hash.hpp | 31 +++++++--- 2 files changed, 71 insertions(+), 103 deletions(-) diff --git a/include/ocl/fix/parser.hpp b/include/ocl/fix/parser.hpp index 71e0eb6..60a4f9a 100644 --- a/include/ocl/fix/parser.hpp +++ b/include/ocl/fix/parser.hpp @@ -9,6 +9,7 @@ #define _OCL_FIX_PARSER_HPP #include +#include #include #include #include @@ -16,48 +17,27 @@ namespace ocl::fix { - template class basic_visitor; - template struct basic_range; - template class basic_range_data; /// @brief Buffer+Length structure - template - using range_ptr_t = basic_range; + using range_ptr_t = basic_range*; namespace detail { - template - const char_type* begin_fix() noexcept; - - template <> - inline const char* begin_fix() noexcept + inline const char* begin_fix() noexcept { return "FIX.4.2"; } - - template <> - inline const char16_t* begin_fix() noexcept - { - return u"FIX.4.2"; - } - - template <> - inline const char8_t* begin_fix() noexcept - { - return u8"FIX.4.2"; - } } // namespace detail - template struct basic_range final { - char_type* bytes_{nullptr}; - size_t length_{}; + char* bytes_{nullptr}; + size_t length_{}; bool is_valid() noexcept { @@ -72,26 +52,23 @@ namespace ocl::fix /// @brief Convert basic_range to usable string. /// @note This function assumes that the basic_range is valid and contains ASCII bytes. - template - inline std::basic_string to_string(basic_range& basic_range) noexcept + inline std::string to_string(basic_range& basic_range) noexcept { - if (basic_range.length_ < 0) - return std::basic_string{}; + if (basic_range.length_ < 1) + return std::string{}; - return std::basic_string(basic_range.bytes_, basic_range.length_); + return std::string(basic_range.bytes_, basic_range.length_); } /// @brief a basic_range object containing the FIX packet values. - template class basic_range_data final { public: - std::size_t magic_len_{}; - std::basic_string magic_{}; - std::size_t body_len_{}; - std::vector, std::basic_string>> body_{}; + std::size_t magic_len_{}; + std::string magic_{}; + string_hash_map message_{}; - static inline const char_type* begin = detail::begin_fix(); + static inline const char* begin = detail::begin_fix(); explicit basic_range_data() = default; ~basic_range_data() = default; @@ -99,24 +76,21 @@ namespace ocl::fix basic_range_data& operator=(const basic_range_data&) = default; basic_range_data(const basic_range_data&) = default; - std::basic_string operator[](const std::basic_string& key) + std::string operator[](const std::string& key) { if (key.empty()) - return std::basic_string{}; - - auto it = std::find_if(this->body_.begin(), this->body_.end(), [&key](const std::pair, std::basic_string>& in) { - return in.first == key; - }); + return std::string{}; - if (it != this->body_.cend()) + auto it = message_.find(key); + if (it != message_.end()) return it->second; - return std::basic_string{}; + return std::string{}; } bool is_valid() { - return magic_.starts_with(basic_range_data::begin); + return this->operator[]("8").empty() == false; } explicit operator bool() @@ -126,14 +100,13 @@ namespace ocl::fix }; /// @brief basic_visitor object which returns a fix::basic_range_data instance. - template class basic_visitor final { public: /// AMLALE: Yeah... - static constexpr const int soh = '\x01'; - static constexpr const char_type eq = '='; - static constexpr uint32_t base = 10U; + static constexpr const int soh = '\x01'; + static constexpr const char eq = '='; + static constexpr uint32_t base = 10U; explicit basic_visitor() = default; ~basic_visitor() = default; @@ -141,7 +114,7 @@ namespace ocl::fix basic_visitor& operator=(const basic_visitor&) = default; basic_visitor(const basic_visitor&) = default; - basic_range operator()(const std::basic_string& in) + basic_range_data operator()(const std::string& in) { return this->visit(in); } @@ -149,77 +122,57 @@ namespace ocl::fix /// @brief Visit a FIX message and parse it into a basic_range_data object. /// @param in The input FIX message as a string. /// @warning This function may throw exceptions. - basic_range_data visit(const std::basic_string& in) + basic_range_data visit(const std::string& in) { - basic_range_data ret{}; + basic_range_data ret{}; if (in.empty()) return ret; - std::basic_string key; + std::string key; std::size_t off = 0UL; - while (true) + while (off < in.size()) { - if (in.size() < off) + // Find the '=' separator + std::size_t eq_pos = in.find(eq, off); + if (eq_pos == std::string::npos) break; - if (in[off] != basic_visitor::eq) - { - if (in[off] == basic_visitor::soh) - { - ++off; - continue; - } - - key += in[off]; - ++off; - continue; - } + // Extract tag (everything from current position to '=') + std::string tag = in.substr(off, eq_pos - off); - if (in.size() < (off + 1)) - break; + // Find the SOH delimiter after the value (or end of string) + std::size_t soh_pos = in.find(soh, eq_pos + 1); + if (soh_pos == std::string::npos) + soh_pos = in.size(); - std::basic_string val = in.substr(off + 1); - - if (val.find(basic_visitor::soh) != std::basic_string::npos) - { - val.erase(val.find(basic_visitor::soh)); - } + // Extract value (everything from after '=' to SOH) + std::string value = in.substr(eq_pos + 1, soh_pos - eq_pos - 1); + // Store first value as magic (should be BeginString) if (ret.magic_.empty()) { - ret.magic_ = val; - ret.magic_len_ = ret.magic_.size(); + ret.magic_ = value; + ret.magic_len_ = value.size(); } - ret.body_.emplace_back(std::make_pair(key, val)); + // Store tag-value pair + ret.message_[tag] = value; - off += val.size() + 1; - - key.clear(); + // Move to position after the SOH + off = soh_pos + 1; } - ret.body_len_ = ret.body_.size(); - return ret; } }; - template - inline void must_pass(basic_range_data& basic_range, error_handler& handler) - { - if (!basic_range.is_valid()) - { - handler.template error("Invalid FIX Message."); - } - } - - using fix_tag_type = std::basic_string; + using fix_tag_type = std::string; - using range_data = basic_range_data; - using visitor = basic_visitor; + using range_data = basic_range_data; + using visitor = basic_visitor; } // namespace ocl::fix #endif // ifndef _OCL_FIX_PARSER_HPP \ No newline at end of file diff --git a/include/ocl/hashing/hash.hpp b/include/ocl/hashing/hash.hpp index c64daf4..3a29979 100644 --- a/include/ocl/hashing/hash.hpp +++ b/include/ocl/hashing/hash.hpp @@ -13,6 +13,7 @@ #include #include #include +#include /// @brief Crc32 implementation in C++ /// @author Amlal El Mahrouss (amlal@nekernel.org) @@ -57,9 +58,8 @@ namespace ocl 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; - template - static std::uint32_t - crc32(buffer_type in, size_t len) noexcept + static std::size_t + crc32(const char* in, size_t len) noexcept { if (!in || *in == 0) return 0; @@ -73,6 +73,9 @@ namespace ocl } }; + template + using string_hash_map = std::unordered_map>; + template using hash_map = std::unordered_map>; } // namespace ocl @@ -82,20 +85,32 @@ namespace std template <> struct hash final { - hash() = default; + hash() = default; ~hash() = default; template - inline std::size_t operator()(T* in_) + inline std::size_t operator()(T* in_) const { - return ocl::hash_trait::crc32((char*)in_, sizeof(T)); + return ocl::hash_trait::crc32(reinterpret_cast(in_), sizeof(T)); } - inline std::size_t operator()(const std::string& in_) + inline std::size_t operator()(const std::string& in_) const { - return ocl::hash_trait::crc32(in_.c_str(), in_.size()); + return ocl::hash_trait::crc32(in_.c_str(), in_.size()); } }; + + // Source - https://stackoverflow.com/a/68521441 + // Posted by StoryTeller - Unslander Monica, modified by community. See post 'Timeline' for change history + // Retrieved 2025-11-30, License - CC BY-SA 4.0 + + template + struct is_invocable_hash : std::bool_constant < + requires(Fn fn, ArgTypes... arg_types) + { + {std::forward(fn)(std::forward(arg_types)...)}->std::same_as>; + }>{}; + } // namespace std #endif // !_OCL_CRC32_HPP \ No newline at end of file -- cgit v1.2.3