llfix
Low-latency FIX engine
fix_session.h
1 // DISCLAIMER_PLACEHOLDER
2 #pragma once
3 
4 #include <cstdint>
5 #include <cstddef>
6 #include <atomic>
7 #include <string>
8 #include <string_view>
9 #include <sstream>
10 #include <type_traits>
11 #include <memory>
12 #include <new>
13 #include <mutex> // For std::lock_guard
14 
15 #include <vector>
16 #include <unordered_map>
17 
18 #include "core/compiler/hints_hot_code.h"
19 #include "core/compiler/hints_branch_predictor.h"
20 
21 #include "core/os/vdso.h"
22 
23 #include "core/utilities/logger.h"
24 #include "core/utilities/object_cache.h"
25 #include "core/utilities/spsc_bounded_queue.h"
26 #include "core/utilities/std_string_utilities.h"
27 #include "core/utilities/configuration.h"
28 #include "core/utilities/userspace_spinlock.h"
29 
30 #include "electronic_trading/common/message_serialiser.h"
31 
32 #include "electronic_trading/session/session_state.h"
33 #include "electronic_trading/session/sequence_store.h"
34 #include "electronic_trading/session/session_schedule_validator.h"
35 #include "electronic_trading/session/throttler.h"
36 
37 #include "electronic_trading/managed_instance/managed_instance_session.h"
38 #include "electronic_trading/managed_instance/modifying_admin_command.h"
39 
40 #include "fix_constants.h"
41 #include "fix_string.h"
42 #include "fix_string_view.h"
43 #include "fix_utilities.h"
44 #include "fix_session_settings.h"
45 #include "fix_parser_error_codes.h"
46 #include "incoming_fix_repeating_group_specs.h"
47 #include "incoming_fix_message.h"
48 #include "outgoing_fix_message.h"
49 
50 #ifdef LLFIX_ENABLE_DICTIONARY // VOLTRON_EXCLUDE
51 #include "fix_dictionary.h"
52 #include "fix_dictionary_loader.h"
53 #include "fix_dictionary_validator.h"
54 #endif // VOLTRON_EXCLUDE
55 
56 #ifdef LLFIX_ENABLE_BINARY_FIELDS // VOLTRON_EXCLUDE
57 #include "incoming_fix_binary_field_specs.h"
58 #endif // VOLTRON_EXCLUDE
59 
60 namespace llfix
61 {
62 
63 using MessageSerialiserType = MessageSerialiser<FixMessageSequenceNumberExtractor>;
64 
73 class FixSession : public ManagedInstanceSession
74 {
75  public :
76 
77  FixSession() = default;
78 
79  virtual ~FixSession()
80  {
81  m_sequence_store.save_to_disc();
82 
83  if (m_tx_encode_buffer)
84  {
85  Allocator::deallocate(m_tx_encode_buffer, m_settings.tx_encode_buffer_capacity);
86  m_tx_encode_buffer = nullptr;
87  }
88  }
89 
90  bool initialise(const std::string& name, const FixSessionSettings& settings)
91  {
92  m_name = name;
93  m_lock.initialise();
94 
95  if(m_name.length()==0)
96  {
97  LLFIX_LOG_ERROR("Session names can't be empty");
98  return false;
99  }
100 
101  if(m_name.length()>32)
102  {
103  LLFIX_LOG_ERROR("Session names can have max 32 characters. Failed name : " + m_name);
104  return false;
105  }
106 
107  if (settings.validate() == false)
108  {
109  LLFIX_LOG_ERROR("FixSessionSettings for session " + m_name + " validation failed : " + settings.validation_error);
110  return false;
111  }
112 
113  try // memory resources
114  {
115  m_settings = settings;
116 
117  m_incoming_throttler_exceed_count = 0;
118 
119  set_state(SessionState::DISCONNECTED);
120 
121  if (m_settings.schedule_week_days.length() > 0) m_schedule_validator.add_allowed_weekdays_from_dash_separated_string(m_settings.schedule_week_days);
122  m_schedule_validator.set_start_and_end_times(m_settings.start_hour_utc, m_settings.start_minute_utc, m_settings.end_hour_utc, m_settings.end_minute_utc);
123 
124  if (m_admin_commands.create(128) == false)
125  {
126  LLFIX_LOG_ERROR("Failed to create admin commands queue for session " + m_name);
127  return false;
128  }
129 
130  if(m_settings.throttle_limit>0)
131  {
132  if (m_throttler.initialise(m_settings.throttle_window_in_milliseconds * 1'000'000, m_settings.throttle_limit) == false)
133  {
134  LLFIX_LOG_ERROR("Session " + m_name + " : failed to initialise throttler. Check the throttle_limit config.");
135  return false;
136  }
137  }
138 
139  if (open_or_create_sequence_store(m_settings.sequence_store_file_path) == false)
140  {
141  LLFIX_LOG_ERROR("Session " + m_name + " : failed to initialise sequence store. Check the sequence_store_file_path config.");
142  return false;
143  }
144 
145  if(m_settings.max_serialised_file_size > 0)
146  {
147  if (m_incoming_messages_serialiser.initialise(m_settings.incoming_message_serialisation_path, m_settings.max_serialised_file_size, false) == false)
148  {
149  LLFIX_LOG_ERROR("Session " + m_name + " : failed to initialise incoming message serialiser. Check serialisation path and serialised file max size configs.");
150  return false;
151  }
152 
153  if (m_outgoing_messages_serialiser.initialise(m_settings.outgoing_message_serialisation_path, m_settings.max_serialised_file_size, m_settings.replay_messages_on_incoming_resend_request, m_settings.replay_message_cache_initial_size) == false)
154  {
155  LLFIX_LOG_ERROR("Session " + m_name + " : failed to initialise incoming message serialiser. Check serialisation path and serialised file max size configs.");
156  return false;
157  }
158  }
159 
160  if (m_outgoing_fix_message.initialise(&m_settings, get_sequence_store()) == false)
161  {
162  LLFIX_LOG_ERROR("Session " + m_name + " : OutgoingFixMessage creation failed.");
163  return false;
164  }
165 
166  if (m_incoming_fix_message.initialise() == false)
167  {
168  LLFIX_LOG_ERROR("Session " + m_name + " : IncomingFixMessage creation failed.");
169  return false;
170  }
171 
172  if (m_fix_string_view_cache.create(1024) == false)
173  {
174  LLFIX_LOG_ERROR("Session " + m_name + " : FixStringView cache creation failed.");
175  return false;
176  }
177 
178  m_tx_encode_buffer = reinterpret_cast<char*>(Allocator::allocate(m_settings.tx_encode_buffer_capacity));
179 
180  if (m_tx_encode_buffer == nullptr)
181  {
182  LLFIX_LOG_ERROR("Session " + m_name + " : TX encode buffer allocation failed");
183  return false;
184  }
185  }
186  catch (const std::bad_alloc&)
187  {
188  LLFIX_LOG_FATAL("Session " + m_name + " : Insufficient memory.");
189  return false;
190  }
191 
192  try // stoi
193  {
194  if (m_settings.additional_static_header_tags.length() > 0)
195  {
196  auto tag_value_pairs = StringUtilities::split(m_settings.additional_static_header_tags, ',');
197 
198  for (auto pair : tag_value_pairs)
199  {
200  auto tokens = StringUtilities::split(pair, '=');
201 
202  if (tokens.size() != 2)
203  {
204  LLFIX_LOG_ERROR("Invalid FixSession config value for 'additional_static_header_tags' : " + m_settings.additional_static_header_tags);
205  return false;
206  }
207 
208  int tag = std::stoi(tokens[0]);
209 
210  if (tag <= 0)
211  {
212  LLFIX_LOG_ERROR("Invalid FixSession config value for 'additional_static_header_tags' : " + m_settings.additional_static_header_tags + " , tag numbers must be positive integers.");
213  return false;
214  }
215 
216  m_outgoing_fix_message.set_additional_static_header_tag(static_cast<uint32_t>(tag), tokens[1]);
217  }
218  }
219  }
220  catch (...)
221  {
222  LLFIX_LOG_ERROR("Session " + m_name + " : failed to initialise, check config value for 'additional_static_header_tags'.");
223  return false;
224  }
225 
226  #ifdef LLFIX_ENABLE_DICTIONARY
227  bool dictionary_load_success = false;
228 
229  try // new s and dict loading
230  {
231  dictionary_load_success = initialise_dictionary_validator();
232  }
233  catch(...)
234  {}
235 
236  if(dictionary_load_success==false)
237  {
238  LLFIX_LOG_ERROR("Session " + m_name + " : dictionary loading failed.");
239  return false;
240  }
241  #endif
242 
243  LLFIX_LOG_INFO("Session " + m_name + " : session config loaded =>\n" + m_settings.to_string());
244 
245  return true;
246  }
247 
256  std::string get_name() const override { return m_name; }
257 
258  FixSessionSettings* settings() { return &m_settings; }
259 
260  std::string get_display_text() const
261  {
262  std::stringstream ret;
263 
264  ret << "Session state : " << convert_session_state_to_string(static_cast<SessionState>(m_state.load())) << '\n';
265  ret << "Incoming seq no : " << m_sequence_store.get_incoming_seq_no() << '\n';
266  ret << "Outgoing seq no : " << m_sequence_store.get_outgoing_seq_no() << '\n';
267 
268  return ret.str();
269  }
270 
272  // STATE
273 
274  void set_state(SessionState state)
275  {
276  m_state = static_cast<int>(state);
277  }
278 
321  SessionState get_state()
322  {
323  return static_cast<SessionState>(m_state.load());
324  }
325 
327  // SEQUENCE STORE
328  bool open_or_create_sequence_store(const std::string_view& path)
329  {
330  return m_sequence_store.open(path);
331  }
332 
342  {
343  return &m_sequence_store;
344  }
345 
346  uint32_t get_last_processed_sequence_number() const { return m_sequence_store.get_incoming_seq_no(); }
347 
349  // THROTTLER
350  Throttler* throttler() { return &m_throttler; }
351 
352  uint32_t get_incoming_throttler_exceed_count() const { return m_incoming_throttler_exceed_count; }
353  void increment_incoming_throttler_exceed_count() { m_incoming_throttler_exceed_count++; }
354 
356  // SERIALISERS
357  MessageSerialiserType* get_incoming_message_serialiser() { return &m_incoming_messages_serialiser; }
358  MessageSerialiserType* get_outgoing_message_serialiser() { return &m_outgoing_messages_serialiser; }
359  bool serialisation_enabled() const { return m_settings.max_serialised_file_size>0; }
360  void reinitialise_outgoing_serialiser() { m_outgoing_messages_serialiser.initialise(m_settings.outgoing_message_serialisation_path, m_settings.max_serialised_file_size, m_settings.replay_messages_on_incoming_resend_request, m_settings.replay_message_cache_initial_size); }
361 
363  // OUTGOING TEST REQUEST
364  bool expecting_response_for_outgoing_test_request() const { return m_expecting_response_for_outgoing_test_request; }
365  void set_expecting_response_for_outgoing_test_request(bool b) { m_expecting_response_for_outgoing_test_request =b; }
366 
367  uint64_t outgoing_test_request_timestamp_nanoseconds() const { return m_outgoing_test_request_timestamp_nanoseconds; }
368  void set_outgoing_test_request_timestamp_nanoseconds(uint64_t val) { m_outgoing_test_request_timestamp_nanoseconds = val; }
369 
371  // OUTGOING RESEND REQUEST
372  bool needs_to_send_resend_request() const { return m_needs_to_send_resend_request; }
373  void set_needs_to_send_resend_request(bool b) { m_needs_to_send_resend_request = b; }
374 
375  uint32_t get_outgoing_resend_request_begin_no() const { return m_outgoing_resend_request_begin_no; }
376  uint32_t get_outgoing_resend_request_end_no() const { return m_outgoing_resend_request_end_no; }
377 
378  void queue_outgoing_resend_request(uint32_t sequence_store_incoming_seq_no, uint32_t live_incoming_seq_no)
379  {
380  m_needs_to_send_resend_request = true;
381  m_sequence_store.set_incoming_seq_no(sequence_store_incoming_seq_no - 1);
382  m_outgoing_resend_request_begin_no = sequence_store_incoming_seq_no;
383  m_outgoing_resend_request_end_no = live_incoming_seq_no;
384  }
385 
386  uint64_t outgoing_resend_request_timestamp_nanoseconds() const { return m_outgoing_resend_request_timestamp_nanoseconds; }
387  void set_outgoing_resend_request_timestamp_nanoseconds(uint64_t val) { m_outgoing_resend_request_timestamp_nanoseconds = val; }
388 
390  // INCOMING RESEND REQUEST
391  bool needs_responding_to_incoming_resend_request() const { return m_needs_responding_to_incoming_resend_request; }
392  void set_needs_responding_to_incoming_resend_request(bool b) { m_needs_responding_to_incoming_resend_request = b; }
393 
394  uint32_t get_incoming_resend_request_begin_no() const { return m_incoming_resend_request_begin_no; }
395  uint32_t get_incoming_resend_request_end_no() const { return m_incoming_resend_request_end_no; }
396 
398  // INCOMING TEST REQUEST
399  bool needs_responding_to_incoming_test_request() const { return m_needs_responding_to_incoming_test_request; }
400  void set_needs_responding_to_incoming_test_request(bool b) { m_needs_responding_to_incoming_test_request = b; }
401 
402  FixString* get_incoming_test_request_id() { return &m_incoming_test_request_id; }
403 
405  // LOGOUTS
406  bool received_logout_response() const { return m_received_logout_response; }
407  void set_received_logout_response(bool b) { m_received_logout_response = b; }
408 
410  // TIMESTAMPS
411  uint64_t last_received_message_timestamp_nanoseconds() const { return m_last_received_message_timestamp_nanoseconds; }
412  void set_last_received_message_timestamp_nanoseconds(uint64_t val) { m_last_received_message_timestamp_nanoseconds = val; }
413 
414  uint64_t last_sent_message_timestamp_nanoseconds() const { return m_last_sent_message_timestamp_nanoseconds; }
415  void set_last_sent_message_timestamp_nanoseconds(uint64_t val) { m_last_sent_message_timestamp_nanoseconds = val; }
416 
418  // ATTRIBUTES
436  template <typename T>
437  bool add_attribute(const std::string& attribute, const T& value)
438  {
439  bool ret{true};
440 
441  try
442  {
443  if constexpr (std::is_same_v<T, std::string>)
444  {
445  m_attributes.add_attribute(attribute, value);
446  }
447  else if constexpr (std::is_arithmetic_v<T>)
448  {
449  m_attributes.add_attribute(attribute, std::to_string(value));
450  }
451  else
452  {
453  std::ostringstream oss;
454  oss << value;
455  m_attributes.add_attribute(attribute, oss.str());
456  }
457  }
458  catch(...)
459  {
460  ret = false;
461  }
462 
463  return ret;
464  }
465 
476  bool get_attribute(const std::string& attribute, std::string& value) const
477  {
478  if(m_attributes.does_attribute_exist(attribute))
479  {
480  value = m_attributes.get_string_value(attribute);
481  return true;
482  }
483 
484  return false;
485  }
486 
488  // SETTINGS GENERAL
489  uint64_t get_heartbeart_interval_in_nanoseconds() const { return m_settings.heartbeat_interval_in_nanoseconds; }
490 
491  uint64_t get_outgoing_test_request_interval_in_nanoseconds() const
492  {
493  return m_settings.outgoing_test_request_interval_in_nanoseconds;
494  }
495 
496  bool enable_simd_avx2() const { return m_settings.enable_simd_avx2; }
497  VDSO::SubsecondPrecision get_timestamp_subsecond_precision() const { return m_settings.timestamp_subseconds_precision; }
498 
499  void set_heartbeart_interval_in_nanoseconds(uint64_t value)
500  {
501  m_settings.heartbeat_interval_in_nanoseconds = value;
502  }
503 
504  void set_outgoing_test_request_interval_in_nanoseconds(uint64_t value)
505  {
506  m_settings.outgoing_test_request_interval_in_nanoseconds = value;
507  }
508 
509  void set_enable_simd_avx2(bool b) { m_settings.enable_simd_avx2 = b; }
510 
511  int get_protocol_version() const { return m_settings.protocol_version; }
512 
514  // SETTINGS HEADERS
515  const std::string& get_begin_string() const { return m_settings.begin_string; }
516  const std::string& get_compid() const { return m_settings.sender_comp_id; }
517  const std::string& get_target_compid() const { return m_settings.target_comp_id; }
518  bool include_last_processed_seqnum_in_header() const { return m_settings.include_last_processed_seqnum_in_header; }
519 
520  void set_begin_string(const std::string& str) { m_settings.begin_string = str; }
521  void set_compid(const std::string_view& identifier) { m_settings.sender_comp_id = identifier; }
522  void set_target_compid(const std::string_view& identifier) { m_settings.target_comp_id = identifier; }
523 
525  // SETTINGS LOGONS
526  const std::string& get_default_app_ver_id() const { return m_settings.default_app_ver_id; }
527  const std::string& get_username() const { return m_settings.logon_username; }
528  const std::string& get_password() const { return m_settings.logon_password; }
529  std::string logon_message_new_password() const { return m_settings.logon_message_new_password; }
530  bool logon_reset_sequence_numbers_flag() const { return m_settings.logon_reset_sequence_numbers; }
531  bool logon_include_next_expected_seq_no() const { return m_settings.logon_include_next_expected_seq_no; }
532 
534  // SETTINGS VALIDATIONS
535  bool validations_enabled() const { return m_settings.validations_enabled; }
536  void set_validations_enabled(bool b) { m_settings.validations_enabled = b; }
537 
538  bool validate_repeating_groups_enabled() const { return m_settings.validate_repeating_groups; }
539  void set_validate_repeating_groups_enabled(bool b) { m_settings.validate_repeating_groups = b; }
540 
542  // SETTINGS INCOMING RESEND REQUESTS
543  bool replay_messages_on_incoming_resend_request() const { return m_settings.replay_messages_on_incoming_resend_request; }
544  bool include_t97_during_resends() const { return m_settings.include_t97_during_resends; }
545  std::size_t max_resend_range() const { return m_settings.max_resend_range; }
546  void set_replay_messages_on_incoming_resend_request (bool b) { m_settings.replay_messages_on_incoming_resend_request=b; }
547 
549  // SETTINGS OUTGOING RESEND REQUESTS
550  int outgoing_resend_request_expire_secs() const { return m_settings.outgoing_resend_request_expire_in_secs; }
551 
553  // ADMIN MESSAGE BUILDERS
554  void build_heartbeat_message(OutgoingFixMessage* message, FixString* test_request_id)
555  {
556  message->set_msg_type(FixConstants::MSG_TYPE_HEARTBEAT);
557 
558  if (test_request_id != nullptr)
559  {
560  message->set_tag(FixConstants::TAG_TEST_REQ_ID, test_request_id);
561  }
562  }
563 
564  void build_test_request_message(OutgoingFixMessage* message)
565  {
566  message->set_msg_type(FixConstants::MSG_TYPE_TEST_REQUEST);
567  m_outgoing_test_request_id++;
568  message->set_tag(FixConstants::TAG_TEST_REQ_ID, m_outgoing_test_request_id);
569  }
570 
571  void build_resend_request_message(OutgoingFixMessage* message, const std::string& end_no)
572  {
573  message->set_msg_type(FixConstants::MSG_TYPE_RESEND_REQUEST);
574  message->set_tag(FixConstants::TAG_BEGIN_SEQ_NO, m_outgoing_resend_request_begin_no);
575  message->set_tag(FixConstants::TAG_END_SEQ_NO, end_no);
576  }
577 
578  void build_logout_message(OutgoingFixMessage* message, const std::string& reason_text = "")
579  {
580  message->set_msg_type(FixConstants::MSG_TYPE_LOGOUT);
581 
582  if (reason_text.length() > 0)
583  {
584  message->set_tag(FixConstants::TAG_TEXT, reason_text);
585  }
586  }
587 
588  void build_session_level_reject_message(OutgoingFixMessage* message, uint32_t reject_reason_code, const char* reject_reason_text, uint32_t error_tag=0)
589  {
590  message->set_msg_type(FixConstants::MSG_TYPE_REJECT);
591 
592  if (m_incoming_fix_message.has_tag(FixConstants::TAG_MSG_SEQ_NUM))
593  {
594  message->set_tag(FixConstants::TAG_REF_SEQ_NUM, m_incoming_fix_message.get_tag_value_as<uint32_t>(FixConstants::TAG_MSG_SEQ_NUM));
595  }
596 
597  if(error_tag != 0)
598  {
599  message->set_tag(FixConstants::TAG_REF_TAG, error_tag);
600  }
601 
602  if (m_incoming_fix_message.has_tag(FixConstants::TAG_MSG_TYPE))
603  {
604  message->set_tag(FixConstants::TAG_REF_MSG_TYPE, m_incoming_fix_message.get_tag_value_as<std::string>(FixConstants::TAG_MSG_TYPE));
605  }
606 
607  message->set_tag(FixConstants::TAG_SESSION_REJECT_REASON, reject_reason_code);
608  message->set_tag(FixConstants::TAG_TEXT, reject_reason_text);
609  }
610 
611  void build_gap_fill_message(OutgoingFixMessage* message)
612  {
613  message->set_msg_type(FixConstants::MSG_TYPE_SEQUENCE_RESET);
614 
615  // TAG36/NEW SEQ NO
616  // We don't support partial gap fill.
617  // Any ResendRequest is answered by fast-forwarding the counterparty
618  // to the next outbound sequence number (last sent + 1).
619  const auto next_outgoing_sequence_no = m_sequence_store.get_outgoing_seq_no() + 1;
620  message->set_tag(FixConstants::TAG_NEW_SEQ_NO, next_outgoing_sequence_no);
621 
622  // OPTIONAL TAG 123/GAP FILL FLAG
623  message->set_tag(FixConstants::TAG_GAP_FILL_FLAG, FixConstants::FIX_BOOLEAN_TRUE);
624  }
625 
626  void build_sequence_reset_message(OutgoingFixMessage* message, uint32_t desired_sequence_no)
627  {
628  m_sequence_store.set_outgoing_seq_no(desired_sequence_no);
629 
630  // MSG TYPE
631  message->set_msg_type(FixConstants::MSG_TYPE_SEQUENCE_RESET);
632 
633  // TAG36/NEW SEQ NO
634  message->set_tag(FixConstants::TAG_NEW_SEQ_NO, desired_sequence_no + 1); // +1 is becasue send functions will increment by 1 so the other side should expect +1
635  }
636 
638  // RX PROCESSING METHODS
639  bool process_incoming_sequence_reset_message(const IncomingFixMessage& message)
640  {
641  // We don't distinguish between hard gap fills ( without 123=Y, initiated from the other side )
642  // and soft gap fills (with 123=Y, responses to our outgoing 35=2s)
643  if (message.has_tag(FixConstants::TAG_NEW_SEQ_NO))
644  {
645  if (message.is_tag_value_numeric(FixConstants::TAG_NEW_SEQ_NO))
646  {
647  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming sequence reset message tag36(new seq no) : " + message.get_tag_value_as<std::string>(FixConstants::TAG_NEW_SEQ_NO));
648 
649  auto new_incoming_seq_no = message.get_tag_value_as<uint32_t>(FixConstants::TAG_NEW_SEQ_NO);
650 
651  if(new_incoming_seq_no==0)
652  {
653  LLFIX_LOG_DEBUG("Session " + m_name + " : received invalid new tag sequence number(0) for sequence reset.");
654  return false;
655  }
656 
657  if (new_incoming_seq_no <= m_sequence_store.get_incoming_seq_no()) // <= because process_incoming_fix_message will do +1
658  {
659  LLFIX_LOG_DEBUG("Session " + m_name + " : the sequence reset can only increase the sequence number.");
660  return false;
661  }
662 
663  m_sequence_store.set_incoming_seq_no(new_incoming_seq_no - 1); // t36 is what next seq no will be , applying -1 as process_incoming_fix_message will do +1
664  set_state(SessionState::LOGGED_ON);
665  }
666  else
667  {
668  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming sequence reset message has invalid tag36(new seq no).");
669  }
670  }
671  else
672  {
673  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming sequence reset message does not have tag36(new seq no).");
674  }
675 
676  return true;
677  }
678 
679  void process_resend_request(const IncomingFixMessage& message)
680  {
681  if (message.has_tag(FixConstants::TAG_BEGIN_SEQ_NO) && message.has_tag(FixConstants::TAG_END_SEQ_NO))
682  {
683  bool is_tag_7_numeric = message.is_tag_value_numeric(FixConstants::TAG_BEGIN_SEQ_NO);
684  bool is_tag_16_numeric = message.is_tag_value_numeric(FixConstants::TAG_END_SEQ_NO);
685 
686  if (is_tag_7_numeric && is_tag_16_numeric)
687  {
688  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming resend request tag7(begin seq no) : " + message.get_tag_value_as<std::string>(FixConstants::TAG_BEGIN_SEQ_NO) + " tag16(end seq no) : " + message.get_tag_value_as<std::string>(FixConstants::TAG_END_SEQ_NO));
689 
690  set_state(llfix::SessionState::IN_RETRANSMISSION_INITIATED_BY_PEER);
691 
692  m_incoming_resend_request_begin_no = message.get_tag_value_as<uint32_t>(FixConstants::TAG_BEGIN_SEQ_NO);
693 
694  m_incoming_resend_request_end_no = message.get_tag_value_as<uint32_t>(FixConstants::TAG_END_SEQ_NO);
695 
696  m_needs_responding_to_incoming_resend_request = true;
697  }
698  else
699  {
700  if (is_tag_7_numeric == false)
701  {
702  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming resend request message has invalid tag7(begin seq no).");
703  }
704 
705  if (is_tag_16_numeric == false)
706  {
707  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming resend request message has invalid tag16(end seq no).");
708  }
709  }
710  }
711  else
712  {
713  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming resend request message does not have one or both of tag7(begin seq no) and tag16(end seq no).");
714  }
715  }
716 
717  void process_test_request_message(const IncomingFixMessage& message)
718  {
719  LLFIX_LOG_DEBUG("Session " + m_name + " : incoming test request");
720 
721  if (m_expecting_response_for_outgoing_test_request == false)
722  {
723  m_needs_responding_to_incoming_test_request = true;
724 
725  if (message.has_tag(FixConstants::TAG_TEST_REQ_ID))
726  {
727  auto test_request_id = message.get_tag_value(FixConstants::TAG_TEST_REQ_ID);
728  m_incoming_test_request_id.copy_from(test_request_id->data(), test_request_id->length());
729  }
730  }
731  }
732 
734  // VALIDATION METHODS
735  bool is_now_valid_session_datetime() const
736  {
737  return m_schedule_validator.is_now_valid_datetime();
738  }
739 
740  static void validate_header_tags_order(uint32_t tag, uint32_t tag_index, uint32_t& parser_reject_code)
741  {
742  if (tag_index < 4)
743  {
744  if (tag_index == 1)
745  {
746  if (llfix_unlikely(tag != FixConstants::TAG_BEGIN_STRING))
747  {
748  parser_reject_code = FixParserErrorCodes::OUT_OF_ORDER_HEADER_FIELDS;
749  }
750  }
751  else if (tag_index == 2)
752  {
753  if (llfix_unlikely(tag != FixConstants::TAG_BODY_LENGTH))
754  {
755  parser_reject_code = FixParserErrorCodes::OUT_OF_ORDER_HEADER_FIELDS;
756  }
757  }
758  else if (tag_index == 3)
759  {
760  if (llfix_unlikely(tag != FixConstants::TAG_MSG_TYPE))
761  {
762  parser_reject_code = FixParserErrorCodes::OUT_OF_ORDER_HEADER_FIELDS;
763  }
764  }
765  }
766  }
767 
768  static void validate_tag_format(const char* buffer, std::size_t buffer_len, bool& is_numeric, uint32_t& parser_reject_code)
769  {
770  if (llfix_unlikely(buffer_len == 0 || buffer_len>10))
771  {
772  parser_reject_code = FixConstants::FIX_ERROR_CODE_INVALID_TAG_NUMBER;
773  is_numeric = false;
774  return;
775  }
776 
777  for (std::size_t i = 0; i < buffer_len; i++)
778  {
779  char c = buffer[i];
780 
781  if (llfix_unlikely(c < '0' || c > '9'))
782  {
783  parser_reject_code = FixConstants::FIX_ERROR_CODE_INVALID_TAG_NUMBER;
784  is_numeric = false;
785  return;
786  }
787  }
788  }
789 
790  static void validate_tag9_and_tag35(IncomingFixMessage* incoming_message, int tag_10_start_index, int tag_35_tag_start_index, uint32_t& parser_reject_code)
791  {
792  if (llfix_likely(tag_35_tag_start_index != -1))
793  {
794  if (llfix_likely(incoming_message->has_tag(FixConstants::TAG_BODY_LENGTH)))
795  {
796  if (llfix_likely(incoming_message->is_tag_value_numeric(FixConstants::TAG_BODY_LENGTH)))
797  {
798  auto msg_body_len_val = incoming_message->get_tag_value_as<uint32_t>(FixConstants::TAG_BODY_LENGTH);
799 
800  if (llfix_unlikely(static_cast<int>(msg_body_len_val) != (tag_10_start_index - tag_35_tag_start_index)))
801  {
802  parser_reject_code = FixParserErrorCodes::WRONG_BODY_LENGTH;
803  }
804  }
805  else
806  {
807  parser_reject_code = FixParserErrorCodes::INVALID_TAG_9;
808  }
809  }
810  else
811  {
812  parser_reject_code = FixParserErrorCodes::NO_TAG_9;
813  }
814  }
815  else
816  {
817  parser_reject_code = FixParserErrorCodes::NO_TAG_35;
818  }
819  }
820 
821  bool validate_fix_message(const IncomingFixMessage& incoming_message, const char* buffer_message, std::size_t buffer_message_length, uint32_t reject_reason_code, uint32_t& out_reject_message_code)
822  {
823  // TAG34 EXISTENCE
824  if (incoming_message.has_tag(FixConstants::TAG_MSG_SEQ_NUM) == false)
825  {
826  set_last_error_tag(0);
827  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with no tag34(sequence number) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
828  return false;
829  }
830 
831  // TAG34 FORMAT
832  if (incoming_message.is_tag_value_numeric(FixConstants::TAG_MSG_SEQ_NUM) == false)
833  {
834  set_last_error_tag(0);
835  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with invalid tag34(sequence number) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
836  return false;
837  }
838 
839  // ISSUES FOUND DURING PARSING
840  if (llfix_unlikely(reject_reason_code != static_cast<uint32_t>(-1)))
841  {
842  set_last_error_tag(0);
843 
844  if (reject_reason_code > FixConstants::FIX_MAX_ERROR_CODE) // Codes above 99 are our internal ones
845  {
846  std::string log_message = "Session " + m_name + " " + FixParserErrorCodes::get_internal_parser_error_description(reject_reason_code) + " : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length);
847  LLFIX_LOG_DEBUG(log_message);
848  }
849  else
850  {
851  out_reject_message_code = reject_reason_code;
852  }
853 
854  return false;
855  }
856 
857  // TAG8 EXISTENCE
858  if (incoming_message.has_tag(FixConstants::TAG_BEGIN_STRING) == false)
859  {
860  set_last_error_tag(0);
861  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with no tag8(begin string) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
862  return false;
863  }
864 
865  // FIX PROTOCOL VERSION
866  if (!incoming_message.get_tag_value(FixConstants::TAG_BEGIN_STRING)->equals(m_settings.begin_string.c_str()))
867  {
868  set_last_error_tag(0);
869  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with invalid begin string : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
870  return false;
871  }
872 
873  // TAG49 EXISTENCE
874  if (incoming_message.has_tag(FixConstants::TAG_SENDER_COMP_ID) == false)
875  {
876  set_last_error_tag(0);
877  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with no tag49(compid) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
878  return false;
879  }
880 
881  // COMPID VALIDATION
882  if (!incoming_message.get_tag_value(FixConstants::TAG_SENDER_COMP_ID)->equals(m_settings.target_comp_id.c_str()))
883  {
884  set_last_error_tag(0);
885  out_reject_message_code = FixConstants::FIX_ERROR_CODE_COMPID_PROBLEM;
886  return false;
887  }
888 
889  // TAG56 EXISTENCE
890  if (incoming_message.has_tag(FixConstants::TAG_TARGET_COMP_ID) == false)
891  {
892  set_last_error_tag(0);
893  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with no tag56(target compid) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
894  return false;
895  }
896 
897  // TARGET COMPID VALIDATION
898  if (!incoming_message.get_tag_value(FixConstants::TAG_TARGET_COMP_ID)->equals(m_settings.sender_comp_id.c_str()))
899  {
900  set_last_error_tag(0);
901  out_reject_message_code = FixConstants::FIX_ERROR_CODE_COMPID_PROBLEM;
902  return false;
903  }
904 
905  // TAG10 FORMAT
906  if (incoming_message.is_tag_value_numeric(FixConstants::TAG_CHECKSUM) == false)
907  {
908  set_last_error_tag(0);
909  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with invalid tag10(checksum) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
910  return false;
911  }
912 
913  // CHECKSUM VALIDATION
914  const uint32_t actual_checksum = incoming_message.get_tag_value_as<uint32_t>(FixConstants::TAG_CHECKSUM);
915 
916  if (m_settings.enable_simd_avx2)
917  {
918  if (FixUtilities::validate_checksum_simd_avx2(buffer_message, buffer_message_length - 7, actual_checksum) == false) // 7 is length of tag10 + its value + = + SOH
919  {
920  set_last_error_tag(0);
921  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with invalid checksum : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
922  return false;
923  }
924  }
925  else
926  {
927  if (FixUtilities::validate_checksum_no_simd(buffer_message, buffer_message_length - 7, actual_checksum) == false) // 7 is length of tag10 + its value + = + SOH
928  {
929  set_last_error_tag(0);
930  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with invalid checksum : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
931  return false;
932  }
933  }
934 
935  // TAG52 EXISTENCE
936  if (incoming_message.has_tag(FixConstants::TAG_SENDING_TIME) == false)
937  {
938  set_last_error_tag(0);
939  LLFIX_LOG_DEBUG("Session " + m_name + " received a message with no tag52(sending time) : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
940  return false;
941  }
942 
943  // STALENESS VALIDATION
944  if(m_settings.max_allowed_message_age_seconds > 0)
945  {
946  auto sending_time = incoming_message.get_tag_value(FixConstants::TAG_SENDING_TIME);
947 
948  if(sending_time->is_timestamp() == false)
949  {
950  set_last_error_tag(FixConstants::TAG_SENDING_TIME);
951  out_reject_message_code = FixConstants::FIX_ERROR_CODE_FORMAT_INCORRECT_FOR_TAG;
952  LLFIX_LOG_DEBUG("Session " + m_name + " received an invalid timestamp : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
953  return false;
954  }
955 
956  if(FixUtilities::is_utc_timestamp_stale(sending_time->to_string_view(), static_cast<int>(m_settings.max_allowed_message_age_seconds)))
957  {
958  set_last_error_tag(0);
959  out_reject_message_code = FixConstants::FIX_ERROR_CODE_SENDING_TIME_ACCURACY_PROBLEM;
960  LLFIX_LOG_DEBUG("Session " + m_name + " received a stale message : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
961  return false;
962  }
963  }
964 
965  #ifdef LLFIX_ENABLE_DICTIONARY
966  // DICTIONARY VALIDATIONS
967  if (m_settings.dictionary_validations_enabled == true)
968  {
969  if (m_validator_id >= 0)
970  {
971  if (get_validator(m_validator_id).validate(m_incoming_fix_message, out_reject_message_code) == false)
972  {
973  set_last_error_tag(get_validator(m_validator_id).get_last_error_tag());
974  return false;
975  }
976 
977  // DICTIONARY VALIDATIONS - REPEATING GROUPS
978  if (validate_repeating_groups_enabled())
979  {
980  if (get_validator(m_validator_id).validate_repeating_groups(m_incoming_fix_message, out_reject_message_code) == false)
981  {
982  set_last_error_tag(get_validator(m_validator_id).get_last_error_tag());
983  return false;
984  }
985  }
986  }
987  }
988  #endif
989 
990  return true;
991  }
992 
993  uint32_t get_last_error_tag() const { return m_last_error_tag; }
994 
996  // OTHERS
997  IncomingFixMessage* get_incoming_fix_message() { return &m_incoming_fix_message; }
998  OutgoingFixMessage* get_outgoing_fix_message() { return &m_outgoing_fix_message; }
999 
1000  ObjectCache<FixStringView>* get_fix_string_view_cache() { return &m_fix_string_view_cache; }
1001  char* get_tx_encode_buffer() { return m_tx_encode_buffer; }
1002  SPSCBoundedQueue<ModifyingAdminCommand*>* get_admin_commands() { return &m_admin_commands; }
1003 
1004  void reset_flags()
1005  {
1006  m_needs_responding_to_incoming_test_request = false;
1007  m_expecting_response_for_outgoing_test_request = false;
1008  m_needs_responding_to_incoming_resend_request = false;
1009  m_needs_to_send_resend_request = false;
1010  m_received_logout_response = false;
1011 
1012  m_incoming_throttler_exceed_count = 0;
1013  }
1014 
1015  static bool is_a_hard_sequence_reset_message(const IncomingFixMessage& message)
1016  {
1017  if (message.has_tag(FixConstants::TAG_MSG_TYPE) == false)
1018  {
1019  return false; // Not even a valid message
1020  }
1021 
1022  if (message.get_tag_value_as<char>(FixConstants::TAG_MSG_TYPE) != FixConstants::MSG_TYPE_SEQUENCE_RESET)
1023  {
1024  return false; // Not even a sequence reset message
1025  }
1026 
1027  if (message.has_tag(FixConstants::TAG_GAP_FILL_FLAG))
1028  {
1029  if (message.get_tag_value_as<char>(FixConstants::TAG_GAP_FILL_FLAG) == FixConstants::FIX_BOOLEAN_TRUE)
1030  {
1031  return false; // Then it is a soft gap fill
1032  }
1033  else
1034  {
1035  return true;
1036  }
1037  }
1038  else
1039  {
1040  return true;
1041  }
1042  }
1043 
1044  void lock()
1045  {
1046  m_lock.lock();
1047  }
1048 
1049  void unlock()
1050  {
1051  m_lock.unlock();
1052  }
1053 
1054  static IncomingFixRepeatingGroupSpecs& get_repeating_group_specs()
1055  {
1056  return m_repeating_group_specs;
1057  }
1058 
1059  #ifdef LLFIX_ENABLE_DICTIONARY
1060  LLFIX_FORCE_INLINE static FixDictionaryValidator::Validator& get_validator(uint32_t validator_id)
1061  {
1062  assert(m_validators.find(validator_id) != m_validators.end());
1063  return m_validators[validator_id];
1064  }
1065  #endif
1066 
1067  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1068  static IncomingFixBinaryFieldSpecs& get_binary_field_specs()
1069  {
1070  return m_binary_field_specs;
1071  }
1072  #endif
1073 
1074  private:
1075  std::atomic<int> m_state = static_cast<int>(SessionState::NONE);
1076  std::string m_name;
1077  SequenceStore m_sequence_store;
1078  Throttler m_throttler;
1079  MessageSerialiserType m_outgoing_messages_serialiser;
1080  MessageSerialiserType m_incoming_messages_serialiser;
1081  Configuration m_attributes;
1082 
1083  FixSessionSettings m_settings;
1084 
1085  uint64_t m_last_sent_message_timestamp_nanoseconds = 0;
1086  uint64_t m_last_received_message_timestamp_nanoseconds = 0;
1087  uint32_t m_incoming_throttler_exceed_count = 0;
1088 
1089  char* m_tx_encode_buffer = nullptr;
1090 
1091  OutgoingFixMessage m_outgoing_fix_message;
1092 
1093  IncomingFixMessage m_incoming_fix_message;
1094  ObjectCache<FixStringView> m_fix_string_view_cache;
1095 
1096  UserspaceSpinlock<> m_lock;
1097 
1098  // INCOMING TEST REQUEST RELATED
1099  FixString m_incoming_test_request_id;
1100  bool m_needs_responding_to_incoming_test_request = false;
1101 
1102  // OUTGOING TEST REQUEST RELATED
1103  uint32_t m_outgoing_test_request_id = 0;
1104  uint64_t m_outgoing_test_request_timestamp_nanoseconds = 0;
1105  bool m_expecting_response_for_outgoing_test_request = false;
1106 
1107  // RESEND REQUEST/ MESSAGE REPLAYING RELATED ( WHEN INITIATED BY THE SERVER SIDE )
1108  bool m_needs_responding_to_incoming_resend_request = false;
1109  uint32_t m_incoming_resend_request_begin_no = 0;
1110  uint32_t m_incoming_resend_request_end_no = 0;
1111 
1112  // RESEND REQUEST/ MESSAGE REPLAYING RELATED ( WHEN INITIATED BY SELF )
1113  bool m_needs_to_send_resend_request = false;
1114  uint32_t m_outgoing_resend_request_begin_no = 0;
1115  uint32_t m_outgoing_resend_request_end_no = 0;
1116 
1117  uint64_t m_outgoing_resend_request_timestamp_nanoseconds = 0;
1118 
1119  // LOGOUT RELATED
1120  bool m_received_logout_response = false;
1121 
1122  // ADMIN COMMANDS
1123  SPSCBoundedQueue<ModifyingAdminCommand*> m_admin_commands;
1124 
1125  // VALIDATIONS
1126  SessionScheduleValidator m_schedule_validator;
1127  uint32_t m_last_error_tag=0;
1128 
1129  static inline IncomingFixRepeatingGroupSpecs m_repeating_group_specs;
1130 
1131  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1132  static inline IncomingFixBinaryFieldSpecs m_binary_field_specs;
1133  #endif
1134 
1135  #ifdef LLFIX_ENABLE_DICTIONARY
1136  static inline UserspaceSpinlock<> m_validators_initialisation_lock;
1137  static inline std::unordered_map<std::string, int> m_path_validator_id_table;
1138  static inline int m_latest_validator_id = 0;
1139  static inline std::unordered_map<int, FixDictionaryValidator::Validator> m_validators;
1140 
1141  int m_validator_id = -1;
1142  std::unique_ptr<std::vector<std::string>> m_dictionary_load_errors;
1143 
1144  void on_dictionary_load_error(const std::string& message)
1145  {
1146  m_dictionary_load_errors->push_back(message);
1147  }
1148 
1149  bool initialise_dictionary_validator()
1150  {
1151  const std::lock_guard<UserspaceSpinlock<>> lock(m_validators_initialisation_lock);
1152 
1153  if (m_settings.application_dictionary_path.length() == 0)
1154  return true;
1155 
1157  // CHECK IF ALREADY LOADED
1158  bool already_loaded_dictionary = false;
1159 
1160  if (m_path_validator_id_table.find(m_settings.application_dictionary_path) != m_path_validator_id_table.end())
1161  {
1162  m_validator_id = m_path_validator_id_table[m_settings.application_dictionary_path];
1163  already_loaded_dictionary = true;
1164  }
1165  else
1166  {
1167  m_latest_validator_id++;
1168  m_validator_id = m_latest_validator_id;
1169  }
1171  // LOAD DICTIONARIES
1172  m_dictionary_load_errors.reset(new std::vector<std::string>);
1173  std::unique_ptr<FixDictionaryLoader> dictionary_loader(new FixDictionaryLoader);
1174 
1175  auto get_dictionary_load_errors = [&]()
1176  {
1177  std::string ret;
1178 
1179  auto dictionary_load_errors = *dictionary_loader->errors();
1180 
1181  for (const auto& error : dictionary_load_errors)
1182  {
1183  ret += error + "\n";
1184  }
1185 
1186  return ret;
1187  };
1188 
1189  if (m_settings.transport_dictionary_path.length() > 0)
1190  {
1191  if (dictionary_loader->load_from(m_settings.transport_dictionary_path, true) == false)
1192  {
1193  LLFIX_LOG_ERROR("Session " + m_name + " transport dictionary loading errors :" + get_dictionary_load_errors());
1194  return false;
1195  }
1196  }
1197 
1198  if (m_settings.application_dictionary_path.length() > 0)
1199  {
1200  if (dictionary_loader->load_from(m_settings.application_dictionary_path, false) == false)
1201  {
1202  LLFIX_LOG_ERROR("Session " + m_name + " application dictionary loading errors :" + get_dictionary_load_errors());
1203  return false;
1204  }
1205  }
1207  // CHECK BEGIN STRING
1208  std::string dictionary_begin_string;
1209 
1210  if (dictionary_loader->get_dictionary()->get_begin_string(dictionary_begin_string))
1211  {
1212  if (m_settings.begin_string != dictionary_begin_string)
1213  {
1214  LLFIX_LOG_ERROR("Session " + m_name + " : begin string specified in config file (" + m_settings.begin_string + ") does not match the dictionary (" + dictionary_begin_string + ")");
1215  return false;
1216  }
1217  }
1218  else
1219  {
1220  LLFIX_LOG_ERROR("Session " + m_name + " failed to retrieve begin string from dictionary.");
1221  return false;
1222  }
1224  // LOAD VALIDATOR
1225  FixDictionaryValidator::Validator validator;
1226 
1227  if (already_loaded_dictionary == false)
1228  {
1229  auto fields = *dictionary_loader->get_dictionary()->fields();
1230 
1231  // 1. FIELD DEFINITIONS
1232  for (const auto& field : fields)
1233  {
1234  FixDictionaryValidator::FieldDefinition field_definition;
1235  field_definition.m_type = get_validator_field_type(field.second.type);
1236 
1237  if (field_definition.m_type != FixDictionaryValidator::FieldType::NONE)
1238  {
1239  for (const auto& allowed_value : field.second.values)
1240  {
1241  field_definition.add_allowed_value(allowed_value);
1242  }
1243 
1244  validator.specify_field_definition(field.second.tag, field_definition);
1245  }
1246  }
1247 
1248  // 2. MESSAGE DEFINITIONS
1249  load_validator_message_definitions(validator, dictionary_loader->get_dictionary(), fields);
1250 
1251  // 3. REPEATING GROUP DEFINITIONS
1252  load_validator_repeating_group_definitions(validator, dictionary_loader->get_dictionary(), fields);
1253 
1254  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1255  // 4. BINARY FIELDS DEFINITIONS
1256  load_binary_fields(dictionary_loader->get_dictionary(), fields);
1257  #endif
1258  }
1259 
1260  // CHECK DICTIONARY ERRORS
1261  for (const auto& dict_error : *m_dictionary_load_errors)
1262  {
1263  LLFIX_LOG_ERROR("Session " + m_name + " dictionary error : " + dict_error);
1264  }
1265 
1266  if (m_dictionary_load_errors->size() > 0)
1267  {
1268  return false;
1269  }
1271  // SAVE VALIDATOR
1272  if (already_loaded_dictionary == false)
1273  {
1274  m_validators[m_validator_id] = validator;
1275  m_path_validator_id_table[m_settings.application_dictionary_path] = m_validator_id;
1276  }
1277 
1278  return true;
1279  }
1280 
1281  void get_all_non_repeating_group_fields_of_component_recursively(std::unordered_map<std::string, Component>& components, const std::string& component_name, bool is_parent_required, std::vector<MessageField>& target, int& depth)
1282  {
1283  depth++;
1284 
1285  if (components.find(component_name) != components.end())
1286  {
1287  Component& component = components[component_name];
1288 
1289  for (const auto& field : component.fields)
1290  {
1291  MessageField cp;
1292  cp.name = field.name;
1293 
1294  // A field in a component may be required. But if at the same time its parent is non-required,
1295  // then we modify requiredness of a field
1296  cp.required = field.required && is_parent_required;
1297 
1298  target.push_back(cp);
1299  }
1300 
1301  for (const auto& component_field : component.components)
1302  {
1303  get_all_non_repeating_group_fields_of_component_recursively(components, component_field.name, component_field.required && is_parent_required, target, depth);
1304  depth--;
1305  }
1306  }
1307  else
1308  {
1309  on_dictionary_load_error("Could not find component " + component_name);
1310  }
1311  }
1312 
1313  void load_validator_message_definitions(FixDictionaryValidator::Validator& target_validator, FixDictionary* dictionary, std::unordered_map<std::string, Field>& fields)
1314  {
1315  auto messages = dictionary->messages();
1316  auto components = *dictionary->components();
1317  auto header = dictionary->header();
1318  auto trailer = dictionary->trailer();
1319 
1320  auto add_field_to_message_definition = [&](const std::string& field_name, bool required, FixDictionaryValidator::MessageDefinition& target)
1321  {
1322  if (fields.find(field_name) != fields.end())
1323  {
1324  if (required)
1325  {
1326  auto current_tag = fields[field_name].tag;
1327  if(current_tag != FixConstants::TAG_BEGIN_STRING && current_tag != FixConstants::TAG_BODY_LENGTH && current_tag != FixConstants::TAG_MSG_SEQ_NUM && current_tag != FixConstants::TAG_MSG_TYPE && current_tag != FixConstants::TAG_SENDER_COMP_ID && current_tag != FixConstants::TAG_SENDING_TIME && current_tag != FixConstants::TAG_TARGET_COMP_ID) // Already being checked by parser & validate_fix_message
1328  target.add_required_field({ current_tag });
1329  else
1330  target.add_non_required_field({ current_tag }); // Still need to add them not to fail "is this tag for this message" validation
1331  }
1332  else
1333  {
1334  target.add_non_required_field({ fields[field_name].tag });
1335  }
1336  }
1337  else
1338  {
1339  on_dictionary_load_error("Could not find field " + field_name);
1340  }
1341  };
1342 
1343  std::vector<MessageField> header_component_fields;
1344  for (const auto& component : header->components)
1345  {
1346  int depth = 0;
1347  get_all_non_repeating_group_fields_of_component_recursively(components, component.name, component.required, header_component_fields, depth);
1348  }
1349 
1350  std::vector<MessageField> trailer_component_fields;
1351  for (const auto& component : trailer->components)
1352  {
1353  int depth = 0;
1354  get_all_non_repeating_group_fields_of_component_recursively(components, component.name, component.required, trailer_component_fields, depth);
1355  }
1356 
1357  for (const auto& message : *messages)
1358  {
1359  auto message_type = message.second.msg_type;
1360  FixDictionaryValidator::MessageDefinition message_definition;
1361 
1362  // MESSAGE FIELDS
1363  for (auto& field : message.second.fields)
1364  {
1365  add_field_to_message_definition(field.name, field.required, message_definition);
1366  }
1367 
1368  // (NON REPEATING GROUP) COMPONENT FIELDS
1369  for (const auto& component : message.second.components)
1370  {
1371  std::vector<MessageField> component_fields;
1372  int depth = 0;
1373  get_all_non_repeating_group_fields_of_component_recursively(components, component.name, component.required, component_fields, depth);
1374 
1375  for (const auto& field : component_fields)
1376  {
1377  add_field_to_message_definition(field.name, (component.required && field.required), message_definition);
1378  }
1379  }
1380 
1381  // HEADER FIELDS FOR ALL MSG TYPES
1382  for (const auto& field : header->fields)
1383  {
1384  if (fields.find(field.name) != fields.end())
1385  {
1386  add_field_to_message_definition(field.name, field.required, message_definition);
1387  }
1388  else
1389  {
1390  on_dictionary_load_error("Could not find field " + field.name);
1391  }
1392  }
1393 
1394  // HEADER (NON REPEATING GROUP) COMPONENT FIELDS FOR ALL MSG TYPES
1395  for (const auto& field : header_component_fields)
1396  {
1397  if (fields.find(field.name) != fields.end())
1398  {
1399  add_field_to_message_definition(field.name, field.required, message_definition);
1400  }
1401  else
1402  {
1403  on_dictionary_load_error("Could not find field " + field.name);
1404  }
1405  }
1406 
1407  // TRAILER FIELDS FOR ALL MSG TYPES
1408  for (const auto& field : trailer->fields)
1409  {
1410  if (fields.find(field.name) != fields.end())
1411  {
1412  add_field_to_message_definition(field.name, field.required, message_definition);
1413  }
1414  else
1415  {
1416  on_dictionary_load_error("Could not find field " + field.name);
1417  }
1418  }
1419 
1420  // TRAILER (NON REPEATING GROUP) COMPONENT FIELDS FOR ALL MSG TYPES
1421  for (const auto& field : trailer_component_fields)
1422  {
1423  if (fields.find(field.name) != fields.end())
1424  {
1425  add_field_to_message_definition(field.name, field.required, message_definition);
1426  }
1427  else
1428  {
1429  on_dictionary_load_error("Could not find field " + field.name);
1430  }
1431  }
1432 
1433  target_validator.specify_message_definition(message_type, message_definition);
1434  }
1435  }
1436 
1437  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1438  void load_binary_fields(FixDictionary* dictionary, std::unordered_map<std::string, Field>& fields)
1439  {
1440  auto messages = *dictionary->messages();
1441  auto header = *dictionary->header();
1442  auto trailer = *dictionary->trailer();
1443  auto components = *dictionary->components();
1444 
1445  for (auto& message : messages)
1446  {
1447  // MESSAGE'S OWN FIELDS
1448  process_field_vector(message.second.fields, message.first, fields);
1449 
1450  // MESSAGE COMPONENTS
1451  for (auto component : message.second.components)
1452  {
1453  process_component_binary_fields_recursively(component, components, fields, message.first);
1454  }
1455 
1456  // MESSAGE GROUP FIELDS
1457  for (auto group_field : message.second.groups)
1458  {
1459  process_group_binary_fields_recursively(group_field, components, fields, message.first);
1460  }
1461 
1462  // HEADER FIELDS
1463  process_field_vector(header.fields, message.first, fields);
1464 
1465  // HEADER COMPONENTS
1466  for (auto component : header.components)
1467  {
1468  process_component_binary_fields_recursively(component, components, fields, message.first);
1469  }
1470 
1471  // HEADER GROUP FIELDS
1472  for (auto group : header.groups)
1473  {
1474  process_group_binary_fields_recursively(group, components, fields, message.first);
1475  }
1476 
1477  // TRAILER FIELDS
1478  process_field_vector(trailer.fields, message.first, fields);
1479 
1480  // HEADER COMPONENTS
1481  for (auto component : trailer.components)
1482  {
1483  process_component_binary_fields_recursively(component, components, fields, message.first);
1484  }
1485 
1486  // TRAILER GROUP FIELDS
1487  for (auto group : trailer.groups)
1488  {
1489  process_group_binary_fields_recursively(group, components, fields, message.first);
1490  }
1491  }
1492  }
1493 
1494  void process_component_binary_fields_recursively(ComponentField& component, std::unordered_map<std::string, Component>& components, std::unordered_map<std::string, Field>& fields, const std::string& message_type)
1495  {
1496  if (components.find(component.name) != components.end())
1497  {
1498  process_field_vector(components[component.name].fields, message_type, fields);
1499 
1500  for (auto& nested_component : components[component.name].components)
1501  {
1502  process_component_binary_fields_recursively(nested_component, components, fields, message_type);
1503  }
1504 
1505  for (auto& nested_group_field : components[component.name].groups)
1506  {
1507  process_group_binary_fields_recursively(nested_group_field, components, fields, message_type);
1508  }
1509 
1510  }
1511  else
1512  {
1513  on_dictionary_load_error("Could not find component " + component.name);
1514  }
1515  }
1516 
1517  void process_group_binary_fields_recursively(GroupField& group_field, std::unordered_map<std::string, Component>& components, std::unordered_map<std::string, Field>& fields, const std::string& message_type)
1518  {
1519  process_field_vector(group_field.fields, message_type, fields);
1520 
1521  for (auto& nested_component : group_field.component_fields)
1522  {
1523  process_component_binary_fields_recursively(nested_component, components, fields, message_type);
1524  }
1525 
1526  for (auto& nested_group_field : group_field.group_fields)
1527  {
1528  process_group_binary_fields_recursively(nested_group_field, components, fields, message_type);
1529  }
1530  }
1531 
1532  void process_field_vector(std::vector<MessageField>& field_vector, const std::string& message_type, std::unordered_map<std::string, Field>& fields)
1533  {
1534  int index{ 0 };
1535  uint32_t current_length_tag = 0;
1536  int current_length_index = 0;
1537 
1538  for (auto& field : field_vector)
1539  {
1540  index++;
1541 
1542  if (fields.find(field.name) != fields.end())
1543  {
1544  auto current_field_type = fields[field.name].type;
1545 
1546  if (current_field_type == FieldType::LENGTH)
1547  {
1548  current_length_tag = fields[field.name].tag;
1549  current_length_index = index;
1550  }
1551  else if (current_field_type == FieldType::DATA)
1552  {
1553  if (index == current_length_index + 1)
1554  {
1555  m_binary_field_specs.specify_binary_field(message_type, current_length_tag, fields[field.name].tag);
1556  }
1557  }
1558  }
1559  else
1560  {
1561  on_dictionary_load_error("Could not find field " + field.name);
1562  }
1563  }
1564  }
1565  #endif
1566 
1567  void load_validator_repeating_group_definitions(FixDictionaryValidator::Validator& target_validator, FixDictionary* dictionary, std::unordered_map<std::string, Field>& fields)
1568  {
1569  auto messages = *dictionary->messages();
1570  auto header = *dictionary->header();
1571  auto trailer = *dictionary->trailer();
1572  auto components = *dictionary->components();
1573 
1574  std::vector<uint32_t> count_tags_stack;
1575  count_tags_stack.reserve(4);
1576 
1577  for (const auto& message : messages)
1578  {
1579  // MESSAGE'S OWN COMPONENTS
1580  for (const auto& component : message.second.components)
1581  {
1582  if (components.find(component.name) != components.end())
1583  {
1584  int depth{ 0 };
1585  process_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message.first, components[component.name], component.required, depth);
1586  }
1587  else
1588  {
1589  on_dictionary_load_error("Could not find component " + component.name);
1590  }
1591  }
1592 
1593  // HEADER COMPONENTS
1594  for (const auto& component : header.components)
1595  {
1596  if (components.find(component.name) != components.end())
1597  {
1598  int depth{ 0 };
1599  process_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message.first, components[component.name], component.required, depth);
1600  }
1601  else
1602  {
1603  on_dictionary_load_error("Could not find component " + component.name);
1604  }
1605  }
1606 
1607  // TRAILER COMPONENTS
1608  for (const auto& component : trailer.components)
1609  {
1610  if (components.find(component.name) != components.end())
1611  {
1612  int depth{ 0 };
1613  process_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message.first, components[component.name], component.required, depth);
1614  }
1615  else
1616  {
1617  on_dictionary_load_error("Could not find component " + component.name);
1618  }
1619  }
1620 
1621  // MESSAGE'S OWN GROUP FIELDS
1622  for (const auto& group_field : message.second.groups)
1623  {
1624  int depth{ 0 };
1625  process_group_field_rg_tags_recursively(target_validator, components, fields, count_tags_stack, message.first, group_field, group_field.required, depth);
1626  }
1627 
1628  // HEADER GROUP FIELDS
1629  for (const auto& group_field : header.groups)
1630  {
1631  int depth{ 0 };
1632  process_group_field_rg_tags_recursively(target_validator, components, fields, count_tags_stack, message.first, group_field, group_field.required, depth);
1633  }
1634 
1635  // TRAILER GROUP FIELDS
1636  for (const auto& group_field : trailer.groups)
1637  {
1638  int depth{ 0 };
1639  process_group_field_rg_tags_recursively(target_validator, components, fields, count_tags_stack, message.first, group_field, group_field.required, depth);
1640  }
1641  }
1642  }
1643 
1644  // RG component : a component with at least one rg definition either directly or in its nested components
1645  void process_rg_component_tags_recursively(FixDictionaryValidator::Validator& target_validator, std::unordered_map<std::string, Component>& components, std::unordered_map<std::string, Field>& fields, std::vector<uint32_t>& count_tags_stack, const std::string& message_type, const Component& component, bool is_parent_required, int& depth)
1646  {
1647  depth++;
1648 
1649  for (const auto& group : component.groups)
1650  {
1651  int group_traversal_depth{ 0 };
1652  process_group_field_rg_tags_recursively(target_validator, components, fields, count_tags_stack, message_type, group, group.required && is_parent_required, group_traversal_depth);
1653  }
1654 
1655  for (const auto& nested_component_field : component.components)
1656  {
1657  if (components.find(nested_component_field.name) != components.end())
1658  {
1659  process_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message_type, components[nested_component_field.name], nested_component_field.required && is_parent_required, depth);
1660  depth--;
1661  }
1662  else
1663  {
1664  on_dictionary_load_error("Could not find component " + nested_component_field.name);
1665  }
1666  }
1667  }
1668 
1669  // Non RG component : a component with no rg definitions but only fields
1670  void process_non_rg_component_tags_recursively(FixDictionaryValidator::Validator& target_validator, std::unordered_map<std::string, Component>& components, std::unordered_map<std::string, Field>& fields, std::vector<uint32_t>& count_tags_stack, const std::string& message_type, const Component& component, bool is_parent_required, int& depth)
1671  {
1672  depth++;
1673 
1674  for (const auto& field : component.fields)
1675  {
1676  process_repeating_group_tag(target_validator, fields, count_tags_stack, field.name, message_type, field.required && is_parent_required, false);
1677  }
1678 
1679  for (const auto& nested_component : component.components)
1680  {
1681  if (components.find(nested_component.name) != components.end())
1682  {
1683  process_non_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message_type, components[nested_component.name], nested_component.required && is_parent_required, depth);
1684  depth--;
1685  }
1686  else
1687  {
1688  on_dictionary_load_error("Could not find component " + nested_component.name);
1689  }
1690  }
1691  }
1692 
1693  void process_group_field_rg_tags_recursively(FixDictionaryValidator::Validator& target_validator, std::unordered_map<std::string, Component>& components, std::unordered_map<std::string, Field>& fields, std::vector<uint32_t>& count_tags_stack, const std::string& message_type, const GroupField& group_field , bool is_parent_required, int& depth)
1694  {
1695  depth++;
1696 
1697  auto count_tag = fields[group_field.name].tag;
1698 
1699  count_tags_stack.push_back(count_tag);
1700 
1701  process_repeating_group_tag(target_validator, fields, count_tags_stack, group_field.name, message_type, group_field.required && is_parent_required, true);
1702 
1703  for (const auto& field : group_field.fields)
1704  {
1705  process_repeating_group_tag(target_validator, fields, count_tags_stack, field.name, message_type, field.required && is_parent_required, false);
1706  }
1707 
1708  for (const auto& nested_component : group_field.component_fields)
1709  {
1710  if (components.find(nested_component.name) != components.end())
1711  {
1712  int nested_component_traversal_depth{ 0 };
1713  process_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message_type, components[nested_component.name], nested_component.required && is_parent_required, nested_component_traversal_depth);
1714 
1715  nested_component_traversal_depth = 0;
1716  process_non_rg_component_tags_recursively(target_validator, components, fields, count_tags_stack, message_type, components[nested_component.name], nested_component.required && is_parent_required, nested_component_traversal_depth);
1717  }
1718  else
1719  {
1720  on_dictionary_load_error("Could not find component " + nested_component.name);
1721  }
1722  }
1723 
1724  for (const auto& nested_group : group_field.group_fields)
1725  {
1726  process_group_field_rg_tags_recursively(target_validator, components, fields, count_tags_stack, message_type, nested_group, nested_group.required && is_parent_required, depth);
1727  depth--;
1728  }
1729 
1730  count_tags_stack.pop_back();
1731  }
1732 
1733  void process_repeating_group_tag(FixDictionaryValidator::Validator& target_validator, std::unordered_map<std::string, Field>& fields, std::vector<uint32_t>& count_tags_stack, const std::string& field_name, const std::string& message_type, bool required, bool is_count_tag)
1734  {
1735  if (fields.find(field_name) != fields.end())
1736  {
1737  uint32_t current_tag = fields[field_name].tag;
1738 
1739  if (is_count_tag)
1740  {
1741  m_repeating_group_specs.add_count_tag(message_type, current_tag);
1742 
1743  // If any other count tag is already in the stack, it means that this is a nested repeating group. In that case we need to add this tag as a repeating group tag for all parent count tags in the stack (except itself)
1744  for (auto it = count_tags_stack.begin(); it != count_tags_stack.end() - 1; ++it)
1745  {
1746  m_repeating_group_specs.add_repeating_group_tag(message_type, *it, current_tag);
1747  }
1748 
1749  if (required)
1750  {
1751  target_validator.specify_repeating_group_count_tag(message_type, current_tag);
1752  }
1753  }
1754  else
1755  {
1756  for (const auto& parent_count_tag : count_tags_stack)
1757  {
1758  m_repeating_group_specs.add_repeating_group_tag(message_type, parent_count_tag, current_tag);
1759  }
1760  }
1761  }
1762  else
1763  {
1764  on_dictionary_load_error("Could not find field " + field_name);
1765  }
1766  }
1767 
1768  static FixDictionaryValidator::FieldType get_validator_field_type(FieldType type)
1769  {
1770  switch (type)
1771  {
1772  // CHAR
1773  case FieldType::CHAR: return FixDictionaryValidator::FieldType::CHAR;
1774  // BOOL
1775  case FieldType::BOOLEAN: return FixDictionaryValidator::FieldType::BOOL;
1776 
1777  // INT
1778  case FieldType::INT: return FixDictionaryValidator::FieldType::INT;
1779  case FieldType::TAGNUM: return FixDictionaryValidator::FieldType::INT;
1780  case FieldType::SEQNUM: return FixDictionaryValidator::FieldType::INT;
1781  case FieldType::NUMINGROUP: return FixDictionaryValidator::FieldType::INT;
1782  case FieldType::LENGTH: return FixDictionaryValidator::FieldType::INT;
1783 
1784  // FLOAT
1785  case FieldType::FLOAT: return FixDictionaryValidator::FieldType::FLOAT;
1786  case FieldType::AMT: return FixDictionaryValidator::FieldType::FLOAT;
1787  case FieldType::PRICE: return FixDictionaryValidator::FieldType::FLOAT;
1788  case FieldType::PRICEOFFSET: return FixDictionaryValidator::FieldType::FLOAT;
1789  case FieldType::QTY: return FixDictionaryValidator::FieldType::FLOAT;
1790  case FieldType::QUANTITY: return FixDictionaryValidator::FieldType::FLOAT;
1791  case FieldType::PERCENTAGE: return FixDictionaryValidator::FieldType::FLOAT;
1792 
1793  // TIMESTAMPS
1794  case FieldType::UTCTIMESTAMP: return FixDictionaryValidator::FieldType::TIMESTAMP;
1795  case FieldType::TZTIMESTAMP: return FixDictionaryValidator::FieldType::TIMESTAMP;
1796 
1797  // TIME ONLY
1798  case FieldType::UTCTIMEONLY: return FixDictionaryValidator::FieldType::TIME_ONLY;
1799  case FieldType::TZTIMEONLY: return FixDictionaryValidator::FieldType::TIME_ONLY;
1800  case FieldType::UTCTIME: return FixDictionaryValidator::FieldType::TIME_ONLY;
1801  case FieldType::TIME: return FixDictionaryValidator::FieldType::TIME_ONLY;
1802  case FieldType::LOCALMKTTIME: return FixDictionaryValidator::FieldType::TIME_ONLY;
1803 
1804  // DATE ONLY
1805  case FieldType::UTCDATEONLY: return FixDictionaryValidator::FieldType::DATE_ONLY;
1806  case FieldType::UTCDATE: return FixDictionaryValidator::FieldType::DATE_ONLY;
1807  case FieldType::DATE: return FixDictionaryValidator::FieldType::DATE_ONLY;
1808  case FieldType::LOCALMKTDATE: return FixDictionaryValidator::FieldType::DATE_ONLY;
1809 
1810  // DATE OTHER
1811  case FieldType::MONTHYEAR: return FixDictionaryValidator::FieldType::STRING;
1812  case FieldType::DAYOFMONTH: return FixDictionaryValidator::FieldType::INT;
1813 
1814  // BYTE ARRAY
1815  case FieldType::DATA: return FixDictionaryValidator::FieldType::DATA;
1816  case FieldType::XMLDATA: return FixDictionaryValidator::FieldType::DATA;
1817 
1818  // STRING
1819  case FieldType::STRING: return FixDictionaryValidator::FieldType::STRING;
1820  case FieldType::MULTIPLESTRINGVALUE: return FixDictionaryValidator::FieldType::STRING;
1821  case FieldType::MULTIPLEVALUESTRING: return FixDictionaryValidator::FieldType::STRING;
1822  case FieldType::MULTIPLEVALUECHAR: return FixDictionaryValidator::FieldType::STRING;
1823  case FieldType::MULTIPLECHARVALUE: return FixDictionaryValidator::FieldType::STRING;
1824  case FieldType::COUNTRY: return FixDictionaryValidator::FieldType::STRING;
1825  case FieldType::CURRENCY: return FixDictionaryValidator::FieldType::STRING;
1826  case FieldType::EXCHANGE: return FixDictionaryValidator::FieldType::STRING;
1827  case FieldType::LANGUAGE: return FixDictionaryValidator::FieldType::STRING;
1828  case FieldType::RESERVED100PLUS: return FixDictionaryValidator::FieldType::STRING;
1829  case FieldType::RESERVED1000PLUS: return FixDictionaryValidator::FieldType::STRING;
1830  case FieldType::RESERVED4000PLUS: return FixDictionaryValidator::FieldType::STRING;
1831  case FieldType::PATTERN: return FixDictionaryValidator::FieldType::STRING;
1832  case FieldType::TENOR: return FixDictionaryValidator::FieldType::STRING;
1833  case FieldType::XID: return FixDictionaryValidator::FieldType::STRING;
1834  case FieldType::XIDREF: return FixDictionaryValidator::FieldType::STRING;
1835 
1836  //
1837  case FieldType::NONE: return FixDictionaryValidator::FieldType::NONE;
1838  default: return FixDictionaryValidator::FieldType::NONE;
1839  }
1840  }
1841  #endif
1842 
1843  void set_last_error_tag(uint32_t tag)
1844  {
1845  m_last_error_tag = tag;
1846  }
1847 
1848  FixSession(const FixSession& other) = delete;
1849  FixSession& operator= (const FixSession& other) = delete;
1850  FixSession(FixSession&& other) = delete;
1851  FixSession& operator=(FixSession&& other) = delete;
1852 };
1853 
1854 } // namespace
llfix::SequenceStore::set_incoming_seq_no
void set_incoming_seq_no(uint32_t n)
Set the incoming FIX sequence number.
Definition: sequence_store.h:120
llfix::SequenceStore::set_outgoing_seq_no
void set_outgoing_seq_no(uint32_t n)
Set the outgoing FIX sequence number.
Definition: sequence_store.h:96
llfix::FixSession::get_sequence_store
SequenceStore * get_sequence_store()
Provides access to the session's sequence store.
Definition: fix_session.h:341
llfix::FixSession::get_state
SessionState get_state()
Retrieves the current state of the FIX session.
Definition: fix_session.h:321
llfix::IncomingFixMessage::has_tag
bool has_tag(uint32_t tag) const
Checks whether a FIX tag exists and is valid.
Definition: incoming_fix_message.h:62
llfix::FixSession
Represents a single FIX protocol session.
Definition: fix_session.h:73
llfix::FixSession::get_name
std::string get_name() const override
Returns the logical name of the FIX session.
Definition: fix_session.h:256
llfix::SequenceStore::get_incoming_seq_no
uint32_t get_incoming_seq_no() const
Get the incoming FIX sequence number.
Definition: sequence_store.h:129
llfix::SequenceStore
Persistent FIX sequence number store backed by a memory-mapped file.
Definition: sequence_store.h:22
llfix::FixSession::get_attribute
bool get_attribute(const std::string &attribute, std::string &value) const
Retrieves a session attribute value.
Definition: fix_session.h:476
llfix::FixUtilities::fix_to_human_readible
static std::string fix_to_human_readible(const char *buffer, std::size_t buffer_length)
Converts a FIX message buffer to a human-readable string.
Definition: fix_utilities.h:86
llfix::SequenceStore::get_outgoing_seq_no
uint32_t get_outgoing_seq_no() const
Get the outgoing FIX sequence number.
Definition: sequence_store.h:105
llfix::FixSession::add_attribute
bool add_attribute(const std::string &attribute, const T &value)
Adds or updates a session-scoped attribute.
Definition: fix_session.h:437
llfix::IncomingFixMessage::get_tag_value_as
T get_tag_value_as(uint32_t tag, std::size_t decimal_points=0) const
Retrieves a FIX tag value converted to the requested type.
Definition: incoming_fix_message.h:238