11#pragma once
2+ #include < cassert>
23#include < charconv>
4+ #include < cmath>
35#include " id.h"
46#include " absl/strings/str_cat.h"
5- #include " absl/strings/str_format.h"
67#include " absl/time/time.h"
78
89namespace spectator {
@@ -11,6 +12,10 @@ namespace detail {
1112
1213#include " valid_chars.inc"
1314
15+ // IEEE 754 double in fixed notation requires at most 1076 chars
16+ // (sign + 1074 fractional digits + decimal point for minimum subnormal).
17+ static constexpr size_t kMaxFixedDoubleLen = 1076 ;
18+
1419inline std::string as_string (std::string_view v) {
1520 return {v.data (), v.size ()};
1621}
@@ -45,6 +50,14 @@ inline std::string create_prefix(const Id& id, std::string_view type_name) {
4550 return res;
4651}
4752
53+ // Single thread-local send buffer shared across all StatelessMeter instantiations.
54+ // Non-template so all Pub types resolve to the same storage slot per thread.
55+ // Not re-entrant: callers must complete send() before the buffer is safe to reuse.
56+ inline std::string& tl_send_buf () {
57+ thread_local std::string buf;
58+ return buf;
59+ }
60+
4861template <typename T>
4962T restrict (T amount, T min, T max) {
5063 auto r = amount;
@@ -66,51 +79,59 @@ class StatelessMeter {
6679 }
6780 virtual ~StatelessMeter () = default ;
6881 std::string GetPrefix () {
69- if (value_prefix_.empty ()) {
70- value_prefix_ = detail::create_prefix (*id_, Type ());
71- }
82+ ensure_prefix ();
7283 return value_prefix_;
7384 }
7485 [[nodiscard]] IdPtr MeterId () const noexcept { return id_; }
7586 [[nodiscard]] virtual std::string_view Type () = 0;
7687
7788 protected:
7889 void send (double value) {
79- if (value_prefix_.empty ()) {
80- value_prefix_ = detail::create_prefix (*id_, Type ());
90+ ensure_prefix ();
91+ auto & tl_msg = detail::tl_send_buf ();
92+ tl_msg.assign (value_prefix_);
93+
94+ // Early exit: match absl::StrFormat("%f") behaviour for special values.
95+ if (std::isnan (value)) {
96+ tl_msg.append (" nan" );
97+ publisher_->send (tl_msg);
98+ return ;
99+ }
100+
101+ if (std::isinf (value)) {
102+ tl_msg.append (value > 0 ? " inf" : " -inf" );
103+ publisher_->send (tl_msg);
104+ return ;
81105 }
106+
82107 // std::to_chars with fixed format: no trailing zeros, no scientific notation,
83108 // ~5-10x faster than absl::StrFormat("%s%f",...) + erase.
84109 // Stack buffer covers typical values; heap fallback for extreme cases (subnormals).
85110 char num_buf[64 ];
86111 auto [ptr, ec] = std::to_chars (num_buf, num_buf + sizeof (num_buf), value,
87112 std::chars_format::fixed);
88- // thread_local retains capacity after warmup — zero allocation per send.
89- thread_local std::string tl_msg;
90- tl_msg.assign (value_prefix_);
91113 if (ec == std::errc{}) {
92114 tl_msg.append (num_buf, ptr);
93115 } else {
94- // Fallback for extreme values (subnormals): write into tl_msg via resize.
95- // We do not take this pathway normally as it will issue a write of \0's into
96- // the 1076 chars every time
116+ // Fallback for subnormal values, which require up to 1076 chars in fixed
117+ // notation. NaN/Inf are handled above, so this branch is subnormals only.
97118 auto off = tl_msg.size ();
98- tl_msg.resize (off + 1076 );
99- auto [hp, hec] = std::to_chars (tl_msg.data () + off,
100- tl_msg.data () + tl_msg.size (), value,
101- std::chars_format::fixed);
102- tl_msg.resize (static_cast <size_t >(hp - tl_msg.data ()));
119+ tl_msg.resize (off + detail::kMaxFixedDoubleLen );
120+ auto [heap_ptr, heap_ec] = std::to_chars (tl_msg.data () + off,
121+ tl_msg.data () + tl_msg.size (), value,
122+ std::chars_format::fixed);
123+ assert (heap_ec == std::errc{});
124+ tl_msg.resize (static_cast <size_t >(heap_ptr - tl_msg.data ()));
103125 }
104126 publisher_->send (tl_msg);
105127 }
106128
107129 void send_uint (uint64_t value) {
108- if (value_prefix_.empty ()) {
109- value_prefix_ = detail::create_prefix (*id_, Type ());
110- }
130+ ensure_prefix ();
111131 char num_buf[24 ];
112132 auto [ptr, ec] = std::to_chars (num_buf, num_buf + sizeof (num_buf), value);
113- thread_local std::string tl_msg;
133+ assert (ec == std::errc{});
134+ auto & tl_msg = detail::tl_send_buf ();
114135 tl_msg.assign (value_prefix_);
115136 tl_msg.append (num_buf, ptr);
116137 publisher_->send (tl_msg);
@@ -120,6 +141,12 @@ class StatelessMeter {
120141 IdPtr id_;
121142 Pub* publisher_;
122143 std::string value_prefix_;
144+
145+ void ensure_prefix () {
146+ if (value_prefix_.empty ()) {
147+ value_prefix_ = detail::create_prefix (*id_, Type ());
148+ }
149+ }
123150};
124151
125152template <typename Pub>
0 commit comments