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  LLFIX_ALIGN_CODE_32;
378  for(std::size_t i =0; i < m_header_pointer; i++)
379  {
380  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]));
381  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
382  }
383 
384  LLFIX_ALIGN_CODE_32;
385  for(std::size_t i =0; i < m_body_pointer; i++)
386  {
387  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]));
388  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
389  }
390 
391  LLFIX_ALIGN_CODE_32;
392  for(std::size_t i =0; i < m_trailer_pointer; i++)
393  {
394  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]));
395  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
396  }
397 
398  LLFIX_ALIGN_CODE_32;
399  for(const auto & additional_header_tag : m_additional_static_header_tags)
400  {
401  body_length += static_cast<int>((2 + additional_header_tag.tag_str_length + additional_header_tag.value.length())); // 2 is delimiter and equals sign
402  }
403 
404  auto fix_str_body_len_length = Converters::unsigned_int_to_chars<uint32_t>(body_length, m_fix_string_body_length.data());
405  m_fix_string_body_length.set_length(static_cast<uint32_t>(fix_str_body_len_length));
407  // 3. WRITE ALL INTO THE TARGET BUFFER
408  encoded_length = 0;
409 
410  auto write_to_buffer = [&target_buffer, &encoded_length](const char* buffer, std::size_t buffer_len)
411  {
412  assert(buffer_len);
413  llfix_builtin_memcpy(target_buffer + encoded_length, buffer, buffer_len);
414  encoded_length += buffer_len;
415  };
416 
417  // t8
418  write_to_buffer("8=", 2);
419  write_to_buffer(m_session_settings->begin_string.c_str(), m_session_settings->begin_string.length());
420  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
421 
422  // t9
423  write_to_buffer("9=", 2);
424  write_to_buffer(m_fix_string_body_length.c_str(), m_fix_string_body_length.length());
425  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
426 
427  // t35
428  write_to_buffer("35=", 3);
429  write_to_buffer(&m_msg_type[0], m_msg_type_len);
430  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
431 
432  // t34
433  write_to_buffer("34=", 3);
434  write_to_buffer(m_fix_string_seq_no.c_str(), m_fix_string_seq_no.length());
435  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
436 
437  // t49
438  write_to_buffer("49=", 3);
439  write_to_buffer(m_session_settings->sender_comp_id.c_str(), m_session_settings->sender_comp_id.length());
440  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
441 
442  // t52
443  write_to_buffer("52=", 3);
444  write_to_buffer(m_fix_string_send_time.data(), m_fix_string_send_time.length());
445  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
446 
447  // t56
448  write_to_buffer("56=", 3);
449  write_to_buffer(m_session_settings->target_comp_id.c_str(), m_session_settings->target_comp_id.length());
450  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
451 
452  if(m_session_settings->include_last_processed_seqnum_in_header)
453  {
454  // t369
455  write_to_buffer("369=", 4);
456  write_to_buffer(m_last_processed_seq_num.c_str(), m_last_processed_seq_num.length());
457  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
458  }
459 
460  // ADDITIONAL STATIC HEADER TAGS
461  LLFIX_ALIGN_CODE_32;
462  for(const auto & additional_header_tag : m_additional_static_header_tags)
463  {
464  write_to_buffer(&additional_header_tag.tag_str[0], additional_header_tag.tag_str_length);
465 
466  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
467 
468  write_to_buffer(additional_header_tag.value.c_str(), additional_header_tag.value.length());
469 
470  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
471  }
472 
473  // HEADER TAGS
474  LLFIX_ALIGN_CODE_32;
475  for(std::size_t i=0; i<m_header_pointer;i++)
476  {
477  write_to_buffer(&m_header_vector[i].tag_str[0], m_header_vector[i].tag_str_length);
478 
479  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
480 
481  write_to_buffer(m_header_vector[i].value->c_str(), m_header_vector[i].value->length());
482 
483  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
484  }
485 
486  // BODY TAGS
487  LLFIX_ALIGN_CODE_32;
488  for(std::size_t i=0; i<m_body_pointer;i++)
489  {
490  write_to_buffer(&m_body_vector[i].tag_str[0], m_body_vector[i].tag_str_length);
491 
492  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
493 
494  write_to_buffer(m_body_vector[i].value->c_str(), m_body_vector[i].value->length());
495 
496  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
497  }
498 
499  // TRAILER TAGS
500  LLFIX_ALIGN_CODE_32;
501  for(std::size_t i=0; i<m_trailer_pointer;i++)
502  {
503  write_to_buffer(&m_trailer_vector[i].tag_str[0], m_trailer_vector[i].tag_str_length);
504 
505  write_to_buffer(&FixConstants::FIX_EQUALS, 1);
506 
507  write_to_buffer(m_trailer_vector[i].value->c_str(), m_trailer_vector[i].value->length());
508 
509  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
510  }
511 
513  // 4. CALCULATE CHECKSUM AND WRITE IT AS T10
514  write_to_buffer("10=", 3);
515 
516  if(llfix_likely(m_session_settings->enable_simd_avx2))
517  {
518  FixUtilities::encode_checksum_simd_avx2(target_buffer, encoded_length-3, target_buffer + encoded_length);
519  }
520  else
521  {
522  FixUtilities::encode_checksum_no_simd(target_buffer, encoded_length-3, target_buffer + encoded_length);
523  }
524  encoded_length += FixConstants::FIX_CHECKSUM_LENGTH;
525 
526  write_to_buffer(&(FixConstants::FIX_DELIMITER), 1);
527 
529  // 5. PREPARE FOR NEXT ENCODE CALL
530  m_header_pointer = 0;
531  m_body_pointer = 0;
532  m_trailer_pointer = 0;
533  m_fix_string_cache.reset_pointer();
534  m_fix_string_send_time_set = false;
535  }
536 
537  std::string to_string()
538  {
539  std::string ret;
540 
541  for(std::size_t i=0; i< m_header_pointer; i++)
542  {
543  try
544  {
545  ret += std::to_string(m_header_vector[i].tag) + "=" + m_header_vector[i].value->to_string() + '|';
546  }
547  catch (...)
548  {
549  return "An error occurred during OutgoingFixMessage::to_string call";
550  }
551  }
552 
553  for(std::size_t i=0; i< m_body_pointer; i++)
554  {
555  try
556  {
557  ret += std::to_string(m_body_vector[i].tag) + "=" + m_body_vector[i].value->to_string() + '|';
558  }
559  catch (...)
560  {
561  return "An error occurred during OutgoingFixMessage::to_string call";
562  }
563  }
564 
565  for(std::size_t i=0; i< m_trailer_pointer; i++)
566  {
567  try
568  {
569  ret += std::to_string(m_trailer_vector[i].tag) + "=" + m_trailer_vector[i].value->to_string() + '|';
570  }
571  catch (...)
572  {
573  return "An error occurred during OutgoingFixMessage::to_string call";
574  }
575  }
576 
577  return ret;
578  }
579 
580  // Used for resending previously sent messages while responding to an incoming 35=2
581  void load_from_buffer(char* buffer, std::size_t buffer_size)
582  {
583  assert(buffer);
584  assert(buffer_size > 0);
585 
586  std::size_t buffer_read{ 0 };
587  bool looking_for_equals{ true };
588 
589  std::size_t current_tag_start{ 0 };
590  std::size_t current_tag_length{ 0 };
591 
592  std::size_t current_value_start{ 0 };
593  std::size_t current_value_length{ 0 };
594 
595  bool is_replacing_a_non_retransmittable_admin_message {false};
596 
597  while (true)
598  {
599  if (looking_for_equals)
600  {
601  if (buffer[buffer_read] == FixConstants::FIX_EQUALS)
602  {
603  looking_for_equals = false;
604 
605  current_value_start = buffer_read + 1;
606  current_value_length = 0;
607  }
608  else
609  {
610  current_tag_length++;
611  }
612  }
613  else // looking for delimiter
614  {
615  if (buffer[buffer_read] == FixConstants::FIX_DELIMITER)
616  {
617  uint32_t tag = Converters::chars_to_unsigned_int<uint32_t>(buffer + current_tag_start, current_tag_length);
618 
619  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)
620  {
621  bool is_one_of_static_header_tags = is_static_header_tag(tag);
622 
623  if (is_one_of_static_header_tags == false)
624  {
625  auto str_val = m_fix_string_cache.allocate();
626  str_val->copy_from(buffer + current_value_start, current_value_length);
627 
628  if (tag == FixConstants::TAG_MSG_TYPE) // We need to preserve original t35/msgtype
629  {
630  is_replacing_a_non_retransmittable_admin_message = FixUtilities::is_a_non_retransmittable_admin_message_type(str_val);
631 
632  if(is_replacing_a_non_retransmittable_admin_message)
633  {
634  set_msg_type(FixConstants::MSG_TYPE_SEQUENCE_RESET);
635  }
636  else
637  {
638  if (str_val->length() == 1)
639  {
640  set_msg_type(str_val->data()[0]);
641  }
642  else
643  {
644  set_msg_type(str_val->data());
645  }
646  }
647  }
648  else if (tag == FixConstants::TAG_SENDING_TIME) // We want to record orig send time in t122/orig sending time
649  {
650  set_tag<FixMessageComponent::HEADER>(FixConstants::TAG_ORIG_SENDING_TIME, str_val);
651  }
652  else if(tag == FixConstants::TAG_MSG_SEQ_NUM)
653  {
654  if(is_replacing_a_non_retransmittable_admin_message)
655  {
656  uint32_t sequence_no = Converters::chars_to_unsigned_int<uint32_t>(buffer + current_value_start, current_value_length);
657  sequence_no++;
658  set_tag(FixConstants::TAG_NEW_SEQ_NO, sequence_no);
659  }
660  // Else case of seq num is handled by FixClient/FixServer::resend_messages_to_server
661  }
662  else // Anything else preserved in the same order
663  {
664  if(is_replacing_a_non_retransmittable_admin_message == false)
665  {
666  set_tag(tag, str_val);
667  }
668  }
669  }
670  }
671 
672  looking_for_equals = true;
673 
674  current_tag_start = buffer_read + 1;
675  current_tag_length = 0;
676  }
677  else
678  {
679  current_value_length++;
680  }
681  }
682 
683  buffer_read++;
684 
685  if (buffer_read == buffer_size)
686  {
687  break;
688  }
689  }
690  }
691 
693  // Iterator support, only for body tags
694  auto begin()
695  {
696  return m_body_vector.begin();
697  }
698 
699  auto end()
700  {
701  return m_body_vector.begin() + m_body_pointer;
702  }
704 
705  #ifdef LLFIX_UNIT_TEST
706  std::size_t get_msg_type_length() const { return m_msg_type_len; }
707  #endif
708 
709  private:
710  // HEADER
711  char m_msg_type[FixConstants::MAX_SUPPORTED_MESSAGE_TYPE_LENGTH];
712  std::size_t m_msg_type_len = 1;
713 
714  FixString m_fix_string_send_time;
715  bool m_fix_string_send_time_set = false;
716  FixString m_fix_string_seq_no;
717  FixString m_fix_string_body_length;
718  FixString m_last_processed_seq_num;
719 
720  std::vector<OutgoingStaticValue> m_additional_static_header_tags;
721  std::vector<OutgoingValue> m_header_vector;
722  std::size_t m_header_pointer = 0;
723 
724  void add_placeholder_to_header_vector()
725  {
726  OutgoingValue placeholder;
727  m_header_vector.push_back(placeholder);
728  }
729 
730  // BODY
731  std::vector<OutgoingValue> m_body_vector;
732  std::size_t m_body_pointer = 0;
733  static inline constexpr std::size_t INITIAL_BODY_TAG_PLACEHOLDER_COUNT = 256;
734 
735  void add_placeholder_to_body_vector()
736  {
737  OutgoingValue placeholder;
738  m_body_vector.push_back(placeholder);
739  }
740 
741  // TRAILER
742  std::vector<OutgoingValue> m_trailer_vector;
743  std::size_t m_trailer_pointer = 0;
744 
745  void add_placeholder_to_trailer_vector()
746  {
747  OutgoingValue placeholder;
748  m_trailer_vector.push_back(placeholder);
749  }
750 
751  // Fix string cache
752  ObjectCache<FixString> m_fix_string_cache;
753 
754  // Session specific instances
755  SequenceStore* m_session_sequence_store = nullptr;
756  FixSessionSettings* m_session_settings = nullptr;
757 
758  bool is_static_header_tag(uint32_t tag) const
759  {
760  for (const auto& item : m_additional_static_header_tags)
761  {
762  if (item.tag == tag)
763  {
764  return true;
765  }
766  }
767  return false;
768  }
769 
770  template<FixMessageComponent component = FixMessageComponent::BODY>
771  void set_tag_internal(uint32_t tag, FixString* value)
772  {
773  OutgoingValue node;
774  node.tag = tag;
775  node.value = value;
776 
777  if constexpr (component == FixMessageComponent::BODY)
778  {
779  if (llfix_unlikely(m_body_pointer + 1 == m_body_vector.size()))
780  {
781  add_placeholder_to_body_vector();
782  }
783 
784  m_body_vector[m_body_pointer] = node;
785  m_body_pointer++;
786  }
787  else if constexpr (component == FixMessageComponent::HEADER)
788  {
789  if (llfix_unlikely(m_header_pointer + 1 == m_header_vector.size()))
790  {
791  add_placeholder_to_header_vector();
792  }
793 
794  m_header_vector[m_header_pointer] = node;
795  m_header_pointer++;
796  }
797  else if constexpr (component == FixMessageComponent::TRAILER)
798  {
799  if (llfix_unlikely(m_trailer_pointer + 1 == m_trailer_vector.size()))
800  {
801  add_placeholder_to_trailer_vector();
802  }
803 
804  m_trailer_vector[m_trailer_pointer] = node;
805  m_trailer_pointer++;
806  }
807  }
808 
809  template <typename>
810  static constexpr bool always_false_v = false;
811 
812  OutgoingFixMessage(const OutgoingFixMessage& other) = delete;
813  OutgoingFixMessage& operator= (const OutgoingFixMessage& other) = delete;
814  OutgoingFixMessage(OutgoingFixMessage&& other) = delete;
815  OutgoingFixMessage& operator=(OutgoingFixMessage&& other) = delete;
816 };
817 
818 } // 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