/* * File: fix/parser.hpp * Purpose: Financial Information Exchange parser in C++ * Author: Amlal El Mahrouss (founder@snu.systems) * Copyright 2025, Amlal El Mahrouss and SNU Systems Corp all rights reserved. */ #ifndef _SNU_FIX_PARSER_HPP #define _SNU_FIX_PARSER_HPP #include #include #include #include #include namespace snu::fix { template struct visitor; template struct range; template struct range_data; /// @brief Buffer+Length structure template using range_ptr_t = range; namespace detail { template const char_type* begin_fix(); template <> inline const char* begin_fix() { return "FIX."; } template <> inline const char16_t* begin_fix() { return u"FIX."; } template <> inline const char8_t* begin_fix() { return u8"FIX."; } } // namespace detail template struct range final { char_type* bytes_; size_t length_; bool is_valid() { return bytes_ && length_ > 0; } operator bool() { return this->is_valid(); } }; /// @brief Convert range to usable string. /// @note This function assumes that the range is valid and contains ASCII bytes. template inline std::basic_string to_string(range& range) noexcept { if (range.length_ < 0) return std::basic_string{}; return std::basic_string(range.ascii_bytes_, range.length_); } /// @brief a range object containing the FIX packet values. template class range_data final { public: std::basic_string msg_magic_; std::size_t msg_body_len_; std::vector, std::basic_string>> msg_body_; static inline const char_type* begin = detail::begin_fix(); explicit range_data() = default; ~range_data() = default; range_data& operator=(const range_data&) = default; range_data(const range_data&) = default; std::basic_string operator[](const std::basic_string& key) { if (key.empty()) { return std::basic_string{}; } for (const auto& pair : msg_body_) { if (pair.first == key) { return pair.second; } } return std::basic_string{}; } bool is_valid() { return msg_magic_.starts_with(range_data::begin); } operator bool() { return this->is_valid(); } }; /// @brief visitor object which returns a fix::range_data instance. template class visitor final { public: static constexpr const char_type soh = '|'; static constexpr const char_type eq = '='; static constexpr uint32_t base = 10U; explicit visitor() = default; ~visitor() = default; visitor& operator=(const visitor&) = default; visitor(const visitor&) = default; range operator()(const std::basic_string& in) { return this->visit(in); } range_data visit(const std::basic_string& in) { thread_local range_data ret{}; if (in.empty()) return ret; thread_local std::basic_string in_tmp{}; in_tmp.reserve(in.size()); try { for (auto& ch : in) { if (ch != visitor::soh) { in_tmp += ch; continue; } std::basic_string key = in_tmp.substr(0, in_tmp.find(visitor::eq)); std::basic_string val = in_tmp.substr(in_tmp.find(visitor::eq) + 1); if (ret.msg_magic_.empty()) { ret.msg_magic_ = val; } else { ret.msg_body_.emplace_back(std::make_pair(key, val)); ret.msg_body_len_ += in_tmp.size(); } in_tmp.clear(); } } catch (...) { in_tmp.clear(); return ret; } in_tmp.clear(); return ret; } }; } // namespace snu::fix #endif // ifndef _SNU_FIX_PARSER_HPP