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