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