llfix
Low-latency FIX engine
outgoing_fix_message.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  - Encoded message layout : <header=t8,t9,t35,t34,t49,t52,t56> + <optional header tag369> + <optional static header tags> + <optional header tags> + <body> + <optional trailer tags> + <checksum/t10>
27 */
28 #include <cassert>
29 #include <cstdint>
30 #include <cstddef>
31 #include <string>
32 #include <string_view>
33 #include <vector>
34 
35 #include "core/compiler/builtin_functions.h"
36 #include "core/compiler/unused.h"
37 #include "core/compiler/hints_branch_predictor.h"
38 #include "core/compiler/hints_hot_code.h"
39 
40 #include "core/os/assert_msg.h"
41 
42 #include "core/utilities/converters.h"
43 #include "core/utilities/object_cache.h"
44 
45 #include "electronic_trading/common/fixed_point.h"
46 #include "electronic_trading/session/sequence_store.h"
47 
48 #include "fix_constants.h"
49 #include "fix_string.h"
50 #include "fix_session_settings.h"
51 #include "fix_utilities.h"
52 
53 namespace llfix
54 {
55 
71 enum class FixMessageComponent
72 {
73  HEADER,
74  BODY,
75  TRAILER
76 };
77 
78 struct OutgoingValue
79 {
80  uint32_t tag = 0;
81  FixString* value = nullptr;
82  char tag_str[16];
83  std::size_t tag_str_length=0;
84 };
85 
86 struct OutgoingStaticValue
87 {
88  uint32_t tag = 0;
89  char tag_str[16];
90  std::size_t tag_str_length=0;
91  std::string value;
92 };
93 
100 {
101  public:
102 
103  OutgoingFixMessage() = default;
104  ~OutgoingFixMessage() = default;
105 
106  bool initialise(FixSessionSettings* session_settings_instance, SequenceStore* session_sequence_store_instance)
107  {
108  m_session_settings = session_settings_instance;
109  m_session_sequence_store = session_sequence_store_instance;
110 
111  m_body_vector.reserve(INITIAL_BODY_TAG_PLACEHOLDER_COUNT);
112 
113  for (std::size_t i = 0; i < INITIAL_BODY_TAG_PLACEHOLDER_COUNT; i++)
114  {
115  add_placeholder_to_body_vector();
116  }
117 
118  m_header_vector.reserve(INITIAL_BODY_TAG_PLACEHOLDER_COUNT);
119 
120  for (std::size_t i = 0; i < INITIAL_BODY_TAG_PLACEHOLDER_COUNT; i++)
121  {
122  add_placeholder_to_header_vector();
123  }
124 
125  m_trailer_vector.reserve(INITIAL_BODY_TAG_PLACEHOLDER_COUNT);
126 
127  for (std::size_t i = 0; i < INITIAL_BODY_TAG_PLACEHOLDER_COUNT; i++)
128  {
129  add_placeholder_to_trailer_vector();
130  }
131 
132  return m_fix_string_cache.create(256);
133  }
134 
143  void set_msg_type(char c)
144  {
145  m_msg_type[0] = c;
146  m_msg_type_len = 1;
147  }
148 
159  void set_msg_type(std::string_view buffer)
160  {
161  m_msg_type_len = buffer.length();
162  assert(m_msg_type_len <= FixConstants::MAX_SUPPORTED_MESSAGE_TYPE_LENGTH);
163  llfix_builtin_memcpy(m_msg_type, buffer.data(), m_msg_type_len);
164  }
165 
191  template<FixMessageComponent component = FixMessageComponent::BODY, typename T>
192  void set_tag(uint32_t tag, T val, std::size_t decimal_points = 0)
193  {
194  FixString* str_value = m_fix_string_cache.allocate();
195 
196  if constexpr (std::is_same_v<T, const char*>)
197  {
198  LLFIX_UNUSED(decimal_points);
199  str_value->copy_from(val);
200  }
201  else if constexpr (std::is_same_v<T, std::string>)
202  {
203  LLFIX_UNUSED(decimal_points);
204  str_value->copy_from(val.c_str());
205  }
206  else if constexpr (std::is_same_v<T, char>)
207  {
208  LLFIX_UNUSED(decimal_points);
209  str_value->copy_from(val);
210  }
211  else if constexpr (std::is_same_v<T, std::string_view>)
212  {
213  LLFIX_UNUSED(decimal_points);
214  str_value->copy_from(val);
215  }
216  else if constexpr (std::is_same_v<T, FixString*>)
217  {
218  LLFIX_UNUSED(decimal_points);
219  str_value->copy_from(val->to_string_view());
220  }
221  else if constexpr (std::is_same_v<T, bool>)
222  {
223  LLFIX_UNUSED(decimal_points);
224  str_value->data()[0] = val == true ? FixConstants::FIX_BOOLEAN_TRUE : FixConstants::FIX_BOOLEAN_FALSE;
225  str_value->set_length(1);
226  }
227  else if constexpr (std::is_same_v<T, FixedPoint>)
228  {
229  LLFIX_UNUSED(decimal_points);
230  auto len = val.to_chars(str_value->data());
231  str_value->set_length(static_cast<uint32_t>(len));
232  }
233  else if constexpr (std::is_floating_point<T>::value)
234  {
235  llfix_assert_msg(decimal_points > 0, "When you pass double/float to set_tag, you should also specify decimal points");
236  auto length = Converters::double_to_chars(val, str_value->data(), str_value->capacity(), decimal_points);
237  str_value->set_length(static_cast<uint32_t>(length));
238  }
239  else if constexpr (std::is_integral<T>::value && std::is_signed<T>::value)
240  {
241  LLFIX_UNUSED(decimal_points);
242  auto length = Converters::int_to_chars(val, str_value->data());
243  str_value->set_length(static_cast<uint32_t>(length));
244  }
245  else if constexpr (std::is_integral<T>::value && sizeof(T) == sizeof(uint64_t))
246  {
247  LLFIX_UNUSED(decimal_points);
248  auto length = Converters::unsigned_int_to_chars<uint64_t>(val, str_value->data());
249  str_value->set_length(static_cast<uint32_t>(length));
250  }
251  else if constexpr (std::is_integral<T>::value && sizeof(T) == sizeof(uint32_t))
252  {
253  LLFIX_UNUSED(decimal_points);
254  auto length = Converters::unsigned_int_to_chars<uint32_t>(val, str_value->data());
255  str_value->set_length(static_cast<uint32_t>(length));
256  }
257  else
258  {
259  static_assert(always_false_v<T>, "set_tag unsupported type");
260  }
261 
262  set_tag_internal<component>(tag, str_value);
263  }
264 
276  template<FixMessageComponent component = FixMessageComponent::BODY>
277  void set_binary_tag(uint32_t tag, const char* buffer, std::size_t data_length)
278  {
279  FixString* str_value = m_fix_string_cache.allocate();
280  str_value->copy_from(buffer, data_length);
281  set_tag_internal<component>(tag, str_value);
282  }
283 
297  template<FixMessageComponent component = FixMessageComponent::BODY>
298  void set_timestamp_tag(uint32_t tag)
299  {
300  if (m_fix_string_send_time_set == false)
301  {
302  FixUtilities::encode_current_time(&m_fix_string_send_time, m_session_settings->timestamp_subseconds_precision);
303  m_fix_string_send_time_set = true;
304  }
305 
306  set_tag_internal<component>(tag, &m_fix_string_send_time);
307  }
308 
309  void set_additional_static_header_tag(uint32_t tag, const std::string& val)
310  {
311  OutgoingStaticValue node;
312  node.tag = tag;
313  node.value = val;
314 
315  node.tag_str_length = Converters::unsigned_int_to_chars<uint32_t>(node.tag, &(node.tag_str[0]));
316 
317  m_additional_static_header_tags.push_back(node);
318  }
319 
325  std::string get_sending_time()
326  {
327  if(m_fix_string_send_time_set == false)
328  {
329  FixUtilities::encode_current_time(&m_fix_string_send_time, m_session_settings->timestamp_subseconds_precision);
330  m_fix_string_send_time_set = true;
331  }
332 
333  return m_fix_string_send_time.to_string();
334  }
335 
336  LLFIX_HOT void encode(char* target_buffer, std::size_t target_buffer_length, const uint32_t sequence_no, std::size_t& encoded_length)
337  {
338  assert(target_buffer != nullptr && target_buffer_length > 0);
339 
341  // 1. t34 / SEQUENCE NO
342  auto fix_str_seq_no_length = Converters::unsigned_int_to_chars<uint32_t>(sequence_no, m_fix_string_seq_no.data());
343  m_fix_string_seq_no.set_length(static_cast<uint32_t>(fix_str_seq_no_length));
344 
346  // 2. BODY LENGTH
347 
348  /*
349  Excluding t8 and t9
350 
351  For each of 5 (35 34 49 52 56) -> 2 (Tag) 1(equal sign) 1 (delimiter) -> 4*5 -> 20
352 
353  Total = 20
354  */
355  int body_length = 20;
356 
357  if (m_fix_string_send_time_set == false)
358  {
359  FixUtilities::encode_current_time(&m_fix_string_send_time, m_session_settings->timestamp_subseconds_precision);
360  m_fix_string_send_time_set = true;
361  }
362 
363  body_length += static_cast<int>(m_fix_string_send_time.length());
364 
365  body_length += static_cast<int>(m_msg_type_len);
366  body_length += static_cast<int>(m_fix_string_seq_no.length());
367  body_length += static_cast<int>(m_session_settings->sender_comp_id.length());
368  body_length += static_cast<int>(m_session_settings->target_comp_id.length());
369 
370  if(m_session_settings->include_last_processed_seqnum_in_header)
371  {
372  auto last_processed_seq_num_str_len = Converters::unsigned_int_to_chars<uint32_t>(m_session_sequence_store->get_incoming_seq_no(), m_last_processed_seq_num.data());
373  m_last_processed_seq_num.set_length(static_cast<uint32_t>(last_processed_seq_num_str_len));
374  body_length += static_cast<int>(5+ last_processed_seq_num_str_len); // 5 -> 369= and delimiter
375  }
376 
377  for(std::size_t i =0; i < m_header_pointer; i++)
378  {
379  m_header_vector[i].tag_str_length = Converters::unsigned_int_to_chars<uint32_t>(m_header_vector[i].tag, &(m_header_vector[i].tag_str[0]));
380  body_length += static_cast<int>((2 + m_header_vector[i].tag_str_length + m_header_vector[i].value->length())); // 2 is delimiter and equals sign
381  }
382 
383  for(std::size_t i =0; i < m_body_pointer; i++)
384  {
385  m_body_vector[i].tag_str_length = Converters::unsigned_int_to_chars<uint32_t>(m_body_vector[i].tag, &(m_body_vector[i].tag_str[0]));
386  body_length += static_cast<int>((2 + m_body_vector[i].tag_str_length + m_body_vector[i].value->length())); // 2 is delimiter and equals sign
387  }
388 
389  for(std::size_t i =0; i < m_trailer_pointer; i++)
390  {
391  m_trailer_vector[i].tag_str_length = Converters::unsigned_int_to_chars<uint32_t>(m_trailer_vector[i].tag, &(m_trailer_vector[i].tag_str[0]));
392  body_length += static_cast<int>((2 + m_trailer_vector[i].tag_str_length + m_trailer_vector[i].value->length())); // 2 is delimiter and equals sign
393  }
394 
395  for(const auto & additional_header_tag : m_additional_static_header_tags)
396  {
397  body_length += static_cast<int>((2 + additional_header_tag.tag_str_length + additional_header_tag.value.length())); // 2 is delimiter and equals sign
398  }
399 
400  auto fix_str_body_len_length = Converters::unsigned_int_to_chars<uint32_t>(body_length, m_fix_string_body_length.data());
401  m_fix_string_body_length.set_length(static_cast<uint32_t>(fix_str_body_len_length));
403  // 3. WRITE ALL INTO THE TARGET BUFFER
404  encoded_length = 0;
405 
406  auto write_to_buffer = [&target_buffer, &encoded_length](const char* buffer, std::size_t buffer_len)
407  {
408  assert(buffer_len);
409  llfix_builtin_memcpy(target_buffer + encoded_length, buffer, buffer_len);
410  encoded_length += buffer_len;
411  };
412 
413  // t8
414  write_to_buffer("8=", 2);
415  write_to_buffer(m_session_settings->begin_string.c_str(), m_session_settings->begin_string.length());
416  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
417 
418  // t9
419  write_to_buffer("9=", 2);
420  write_to_buffer(m_fix_string_body_length.c_str(), m_fix_string_body_length.length());
421  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
422 
423  // t35
424  write_to_buffer("35=", 3);
425  write_to_buffer(&m_msg_type[0], m_msg_type_len);
426  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
427 
428  // t34
429  write_to_buffer("34=", 3);
430  write_to_buffer(m_fix_string_seq_no.c_str(), m_fix_string_seq_no.length());
431  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
432 
433  // t49
434  write_to_buffer("49=", 3);
435  write_to_buffer(m_session_settings->sender_comp_id.c_str(), m_session_settings->sender_comp_id.length());
436  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
437 
438  // t52
439  write_to_buffer("52=", 3);
440  write_to_buffer(m_fix_string_send_time.data(), m_fix_string_send_time.length());
441  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
442 
443  // t56
444  write_to_buffer("56=", 3);
445  write_to_buffer(m_session_settings->target_comp_id.c_str(), m_session_settings->target_comp_id.length());
446  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
447 
448  if(m_session_settings->include_last_processed_seqnum_in_header)
449  {
450  // t369
451  write_to_buffer("369=", 4);
452  write_to_buffer(m_last_processed_seq_num.c_str(), m_last_processed_seq_num.length());
453  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
454  }
455 
456  // ADDITIONAL STATIC HEADER TAGS
457  for(const auto & additional_header_tag : m_additional_static_header_tags)
458  {
459  write_to_buffer(&additional_header_tag.tag_str[0], additional_header_tag.tag_str_length);
460 
461  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
462 
463  write_to_buffer(additional_header_tag.value.c_str(), additional_header_tag.value.length());
464 
465  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
466  }
467 
468  // HEADER TAGS
469  for(std::size_t i=0; i<m_header_pointer;i++)
470  {
471  write_to_buffer(&m_header_vector[i].tag_str[0], m_header_vector[i].tag_str_length);
472 
473  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
474 
475  write_to_buffer(m_header_vector[i].value->c_str(), m_header_vector[i].value->length());
476 
477  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
478  }
479 
480  // BODY TAGS
481  for(std::size_t i=0; i<m_body_pointer;i++)
482  {
483  write_to_buffer(&m_body_vector[i].tag_str[0], m_body_vector[i].tag_str_length);
484 
485  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
486 
487  write_to_buffer(m_body_vector[i].value->c_str(), m_body_vector[i].value->length());
488 
489  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
490  }
491 
492  // TRAILER TAGS
493  for(std::size_t i=0; i<m_trailer_pointer;i++)
494  {
495  write_to_buffer(&m_trailer_vector[i].tag_str[0], m_trailer_vector[i].tag_str_length);
496 
497  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
498 
499  write_to_buffer(m_trailer_vector[i].value->c_str(), m_trailer_vector[i].value->length());
500 
501  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
502  }
503 
505  // 4. CALCULATE CHECKSUM AND WRITE IT AS T10
506  write_to_buffer("10=", 3);
507 
508  if(llfix_likely(m_session_settings->enable_simd_avx2))
509  {
510  FixUtilities::encode_checksum_simd_avx2(target_buffer, encoded_length-3, target_buffer + encoded_length);
511  }
512  else
513  {
514  FixUtilities::encode_checksum_no_simd(target_buffer, encoded_length-3, target_buffer + encoded_length);
515  }
516  encoded_length += FixConstants::FIX_CHECKSUM_LENGTH;
517 
518  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
519 
521  // 5. PREPARE FOR NEXT ENCODE CALL
522  m_header_pointer = 0;
523  m_body_pointer = 0;
524  m_trailer_pointer = 0;
525  m_fix_string_cache.reset_pointer();
526  m_fix_string_send_time_set = false;
527  }
528 
529  std::string to_string()
530  {
531  std::string ret;
532 
533  for(std::size_t i=0; i< m_header_pointer; i++)
534  {
535  try
536  {
537  ret += std::to_string(m_header_vector[i].tag) + "=" + m_header_vector[i].value->to_string() + '|';
538  }
539  catch (...)
540  {
541  return "An error occurred during OutgoingFixMessage::to_string call";
542  }
543  }
544 
545  for(std::size_t i=0; i< m_body_pointer; i++)
546  {
547  try
548  {
549  ret += std::to_string(m_body_vector[i].tag) + "=" + m_body_vector[i].value->to_string() + '|';
550  }
551  catch (...)
552  {
553  return "An error occurred during OutgoingFixMessage::to_string call";
554  }
555  }
556 
557  for(std::size_t i=0; i< m_trailer_pointer; i++)
558  {
559  try
560  {
561  ret += std::to_string(m_trailer_vector[i].tag) + "=" + m_trailer_vector[i].value->to_string() + '|';
562  }
563  catch (...)
564  {
565  return "An error occurred during OutgoingFixMessage::to_string call";
566  }
567  }
568 
569  return ret;
570  }
571 
572  // Used for resending previously sent messages while responding to an incoming 35=2
573  void load_from_buffer(char* buffer, std::size_t buffer_size)
574  {
575  assert(buffer);
576  assert(buffer_size > 0);
577 
578  std::size_t buffer_read{ 0 };
579  bool looking_for_equals{ true };
580 
581  std::size_t current_tag_start{ 0 };
582  std::size_t current_tag_length{ 0 };
583 
584  std::size_t current_value_start{ 0 };
585  std::size_t current_value_length{ 0 };
586 
587  bool is_replacing_a_non_retransmittable_admin_message {false};
588 
589  while (true)
590  {
591  if (looking_for_equals)
592  {
593  if (buffer[buffer_read] == FixConstants::FIX_EQUALS)
594  {
595  looking_for_equals = false;
596 
597  current_value_start = buffer_read + 1;
598  current_value_length = 0;
599  }
600  else
601  {
602  current_tag_length++;
603  }
604  }
605  else // looking for delimiter
606  {
607  if (buffer[buffer_read] == FixConstants::FIX_DELIMITER)
608  {
609  uint32_t tag = Converters::chars_to_unsigned_int<uint32_t>(buffer + current_tag_start, current_tag_length);
610 
611  if (tag != FixConstants::TAG_BEGIN_STRING && tag != FixConstants::TAG_BODY_LENGTH && tag != FixConstants::TAG_SENDER_COMP_ID && tag != FixConstants::TAG_TARGET_COMP_ID && tag != FixConstants::TAG_CHECKSUM)
612  {
613  bool is_one_of_static_header_tags = is_static_header_tag(tag);
614 
615  if (is_one_of_static_header_tags == false)
616  {
617  auto str_val = m_fix_string_cache.allocate();
618  str_val->copy_from(buffer + current_value_start, current_value_length);
619 
620  if (tag == FixConstants::TAG_MSG_TYPE) // We need to preserve original t35/msgtype
621  {
622  is_replacing_a_non_retransmittable_admin_message = FixUtilities::is_a_non_retransmittable_admin_message_type(str_val);
623 
624  if(is_replacing_a_non_retransmittable_admin_message)
625  {
626  set_msg_type(FixConstants::MSG_TYPE_SEQUENCE_RESET);
627  }
628  else
629  {
630  if (str_val->length() == 1)
631  {
632  set_msg_type(str_val->data()[0]);
633  }
634  else
635  {
636  set_msg_type(str_val->data());
637  }
638  }
639  }
640  else if (tag == FixConstants::TAG_SENDING_TIME) // We want to record orig send time in t122/orig sending time
641  {
642  set_tag<FixMessageComponent::HEADER>(FixConstants::TAG_ORIG_SENDING_TIME, str_val);
643  }
644  else if(tag == FixConstants::TAG_MSG_SEQ_NUM)
645  {
646  if(is_replacing_a_non_retransmittable_admin_message)
647  {
648  uint32_t sequence_no = Converters::chars_to_unsigned_int<uint32_t>(buffer + current_value_start, current_value_length);
649  sequence_no++;
650  set_tag(FixConstants::TAG_NEW_SEQ_NO, sequence_no);
651  }
652  // Else case of seq num is handled by FixClient/FixServer::resend_messages_to_server
653  }
654  else // Anything else preserved in the same order
655  {
656  if(is_replacing_a_non_retransmittable_admin_message == false)
657  {
658  set_tag(tag, str_val);
659  }
660  }
661  }
662  }
663 
664  looking_for_equals = true;
665 
666  current_tag_start = buffer_read + 1;
667  current_tag_length = 0;
668  }
669  else
670  {
671  current_value_length++;
672  }
673  }
674 
675  buffer_read++;
676 
677  if (buffer_read == buffer_size)
678  {
679  break;
680  }
681  }
682  }
683 
685  // Iterator support, only for body tags
686  auto begin()
687  {
688  return m_body_vector.begin();
689  }
690 
691  auto end()
692  {
693  return m_body_vector.begin() + m_body_pointer;
694  }
696 
697  #ifdef LLFIX_UNIT_TEST
698  std::size_t get_msg_type_length() const { return m_msg_type_len; }
699  #endif
700 
701  private:
702  // HEADER
703  char m_msg_type[FixConstants::MAX_SUPPORTED_MESSAGE_TYPE_LENGTH];
704  std::size_t m_msg_type_len = 1;
705 
706  FixString m_fix_string_send_time;
707  bool m_fix_string_send_time_set = false;
708  FixString m_fix_string_seq_no;
709  FixString m_fix_string_body_length;
710  FixString m_last_processed_seq_num;
711 
712  std::vector<OutgoingStaticValue> m_additional_static_header_tags;
713  std::vector<OutgoingValue> m_header_vector;
714  std::size_t m_header_pointer = 0;
715 
716  void add_placeholder_to_header_vector()
717  {
718  OutgoingValue placeholder;
719  m_header_vector.push_back(placeholder);
720  }
721 
722  // BODY
723  std::vector<OutgoingValue> m_body_vector;
724  std::size_t m_body_pointer = 0;
725  static inline constexpr std::size_t INITIAL_BODY_TAG_PLACEHOLDER_COUNT = 256;
726 
727  void add_placeholder_to_body_vector()
728  {
729  OutgoingValue placeholder;
730  m_body_vector.push_back(placeholder);
731  }
732 
733  // TRAILER
734  std::vector<OutgoingValue> m_trailer_vector;
735  std::size_t m_trailer_pointer = 0;
736 
737  void add_placeholder_to_trailer_vector()
738  {
739  OutgoingValue placeholder;
740  m_trailer_vector.push_back(placeholder);
741  }
742 
743  // Fix string cache
744  ObjectCache<FixString> m_fix_string_cache;
745 
746  // Session specific instances
747  SequenceStore* m_session_sequence_store = nullptr;
748  FixSessionSettings* m_session_settings = nullptr;
749 
750  bool is_static_header_tag(uint32_t tag) const
751  {
752  for (const auto& item : m_additional_static_header_tags)
753  {
754  if (item.tag == tag)
755  {
756  return true;
757  }
758  }
759  return false;
760  }
761 
762  template<FixMessageComponent component = FixMessageComponent::BODY>
763  void set_tag_internal(uint32_t tag, FixString* value)
764  {
765  OutgoingValue node;
766  node.tag = tag;
767  node.value = value;
768 
769  if constexpr (component == FixMessageComponent::BODY)
770  {
771  if (llfix_unlikely(m_body_pointer + 1 == m_body_vector.size()))
772  {
773  add_placeholder_to_body_vector();
774  }
775 
776  m_body_vector[m_body_pointer] = node;
777  m_body_pointer++;
778  }
779  else if constexpr (component == FixMessageComponent::HEADER)
780  {
781  if (llfix_unlikely(m_header_pointer + 1 == m_header_vector.size()))
782  {
783  add_placeholder_to_header_vector();
784  }
785 
786  m_header_vector[m_header_pointer] = node;
787  m_header_pointer++;
788  }
789  else if constexpr (component == FixMessageComponent::TRAILER)
790  {
791  if (llfix_unlikely(m_trailer_pointer + 1 == m_trailer_vector.size()))
792  {
793  add_placeholder_to_trailer_vector();
794  }
795 
796  m_trailer_vector[m_trailer_pointer] = node;
797  m_trailer_pointer++;
798  }
799  }
800 
801  template <typename>
802  static constexpr bool always_false_v = false;
803 
804  OutgoingFixMessage(const OutgoingFixMessage& other) = delete;
805  OutgoingFixMessage& operator= (const OutgoingFixMessage& other) = delete;
806  OutgoingFixMessage(OutgoingFixMessage&& other) = delete;
807  OutgoingFixMessage& operator=(OutgoingFixMessage&& other) = delete;
808 };
809 
810 } // namespace
llfix::OutgoingFixMessage
FIX message builder and encoder for outbound messages.
Definition: outgoing_fix_message.h:99
llfix::OutgoingFixMessage::set_msg_type
void set_msg_type(char c)
Sets FIX MsgType (tag 35) using a single character.
Definition: outgoing_fix_message.h:143
llfix::OutgoingFixMessage::set_tag
void set_tag(uint32_t tag, T val, std::size_t decimal_points=0)
Appends a FIX tag to the specified message component.
Definition: outgoing_fix_message.h:192
llfix::OutgoingFixMessage::set_timestamp_tag
void set_timestamp_tag(uint32_t tag)
Appends a FIX timestamp tag using the current session time.
Definition: outgoing_fix_message.h:298
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::OutgoingFixMessage::set_msg_type
void set_msg_type(std::string_view buffer)
Sets FIX MsgType (tag 35) using a string view.
Definition: outgoing_fix_message.h:159
llfix::OutgoingFixMessage::set_binary_tag
void set_binary_tag(uint32_t tag, const char *buffer, std::size_t data_length)
Appends a FIX tag using the provided buffer with raw data.
Definition: outgoing_fix_message.h:277
llfix::SequenceStore
Persistent FIX sequence number store backed by a memory-mapped file.
Definition: sequence_store.h:44
llfix::OutgoingFixMessage::get_sending_time
std::string get_sending_time()
Returns the currently cached tag 52 (SendingTime) value.
Definition: outgoing_fix_message.h:325