VLink  2.0.0
A high-performance communication middleware
fast_stream.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file fast_stream.h
26  * @brief Allocation-light @c std::ostream backed by a growable string buffer.
27  *
28  * @details
29  * @c FastStream is the zero-copy log assembly buffer used inside the VLink logger. Inheriting
30  * from @c std::ostream means every @c operator<< overload and manipulator works unchanged; the
31  * difference lies in the @c StringBuf backing storage and in @c take_view, which yields a
32  * @c std::string_view directly into the buffer for hand-off to the active sink without an
33  * intermediate copy.
34  *
35  * @par Supported operators
36  *
37  * | Operation | Source | Notes |
38  * | --------------------- | -------------------------------- | ----------------------------------------------- |
39  * | @c operator<< | @c std::ostream interface | All integral, floating, pointer types |
40  * | Standard manipulators | @c std::ios_base::fmtflags etc. | @c std::hex, @c std::setw, @c std::setfill, ... |
41  * | @c write_raw | direct buffer write | Bypasses locale and format flags |
42  * | @c append_to | append current contents | Does not reset the buffer |
43  * | @c take_view | hand-off as @c string_view | View invalidated by the next write |
44  *
45  * @par Growth policy
46  *
47  * | Stage | Capacity |
48  * | ------------------ | --------------------------------- |
49  * | Initial | @c kDefaultCapacity (@c 256) |
50  * | Doubling | until @c kMaxExpandSize (@c 8192) |
51  * | Linear increments | @c kMaxExpandSize step thereafter |
52  *
53  * @par Example
54  * @code
55  * vlink::FastStream stream;
56  * stream << "sensor_id=" << 42 << " value=" << 3.14;
57  * std::string_view view = stream.take_view(); // valid until next write
58  * write_to_sink(view);
59  * stream.reset();
60  *
61  * stream.write_raw("LITERAL", 7); // bypass formatting
62  * @endcode
63  *
64  * @note Not thread-safe; the logger keeps one @c FastStream per thread via @c thread_local.
65  * Views returned by @c take_view become invalid on the next stream operation or @c reset.
66  */
67 
68 #pragma once
69 
70 #include <ostream>
71 #include <string>
72 #include <string_view>
73 
74 #include "./macros.h"
75 
76 namespace vlink {
77 
78 /**
79  * @class FastStream
80  * @brief Logger-friendly @c std::ostream with a custom growable string buffer.
81  *
82  * @details
83  * Routes every write through an embedded @c StringBuf that owns a @c std::string. The buffer
84  * grows by doubling up to 8 KiB and by linear 8 KiB increments thereafter. @c take_view yields
85  * a non-owning view into the buffer, so a single log line incurs at most one allocation when
86  * the buffer must grow and zero allocations on the steady-state path.
87  */
88 class VLINK_EXPORT FastStream : public std::ostream {
89  public:
90  /**
91  * @brief Constructs the stream with the default initial backing capacity.
92  *
93  * @details
94  * Initial capacity is @c kDefaultCapacity (@c 256 bytes); the buffer grows on demand.
95  */
96  FastStream() noexcept;
97 
98  /**
99  * @brief Destructor; releases the underlying string buffer.
100  */
101  ~FastStream() noexcept override;
102 
103  /**
104  * @brief Empties the buffer and clears the stream's error state.
105  *
106  * @details
107  * Retains the allocated capacity so subsequent messages avoid reallocation.
108  */
109  void reset() noexcept;
110 
111  /**
112  * @brief Appends the current buffer contents to @p target without resetting the stream.
113  *
114  * @param target Destination string; contents are appended in place.
115  */
116  void append_to(std::string& target) const noexcept;
117 
118  /**
119  * @brief Returns a non-owning view of the current buffer contents.
120  *
121  * @details
122  * Primary zero-copy hand-off used by the logger. The view remains valid until the next write
123  * to this stream or an explicit @c reset.
124  *
125  * @warning Do not retain the view beyond the next stream operation; doing so dereferences
126  * freed or moved storage.
127  *
128  * @return View covering the current buffer.
129  */
130  std::string_view take_view();
131 
132  /**
133  * @brief Returns the number of bytes currently held in the buffer.
134  *
135  * @return Buffer length in bytes.
136  */
137  [[nodiscard]] size_t size() const noexcept;
138 
139  /**
140  * @brief Returns the current allocated capacity of the backing buffer.
141  *
142  * @return Capacity in bytes.
143  */
144  [[nodiscard]] size_t capacity() const noexcept;
145 
146  /**
147  * @brief Trims the underlying buffer to its current backing size.
148  *
149  * @details
150  * Does not shrink to the formatted message length; the put pointer is reset to the start.
151  */
152  void shrink_to_fit() noexcept;
153 
154  /**
155  * @brief Writes raw bytes into the buffer without going through @c std::ostream formatting.
156  *
157  * @details
158  * Faster than @c std::ostream::write for pre-formatted C strings because it skips the locale
159  * and format-flag plumbing.
160  *
161  * @param data Source pointer; non-null when @p len is non-zero.
162  * @param len Number of bytes to write.
163  * @return Reference to @c *this for chaining.
164  */
165  FastStream& write_raw(const char* data, size_t len);
166 
167  private:
168  static constexpr size_t kDefaultCapacity{256};
169  static constexpr size_t kMinCapacity{64};
170  static constexpr size_t kMaxExpandSize{8192};
171 
172  class VLINK_EXPORT StringBuf final : public std::streambuf {
173  public:
174  explicit StringBuf(size_t initial_capacity = kDefaultCapacity);
175 
176  void reset() noexcept;
177 
178  void shrink_to_fit() noexcept;
179 
180  void append_to(std::string& target) const noexcept;
181 
182  [[nodiscard]] std::string_view take_view();
183 
184  [[nodiscard]] size_t size() const noexcept;
185 
186  [[nodiscard]] size_t capacity() const noexcept;
187 
188  [[nodiscard]] int_type overflow(int_type ch) override;
189 
190  std::streamsize xsputn(const char* s, std::streamsize n) override;
191 
192  private:
193  void grow_buffer(size_t required_size);
194 
195  void advance_pptr(size_t count) noexcept;
196 
197  std::string buffer_;
198 
200  };
201 
202  StringBuf buf_;
203 
205 };
206 
207 } // namespace vlink
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174