VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
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 A high-performance @c std::ostream backed by a resizable @c std::string buffer.
27 *
28 * @details
29 * @c FastStream is a specialised output stream designed for zero-copy, low-latency
30 * log formatting inside the VLink Logger. It inherits from @c std::ostream, so any
31 * standard stream manipulator or @c operator<< overload works transparently.
32 *
33 * Key design points:
34 * - Backed by an internal @c StringBuf that stores data in a @c std::string
35 * (avoids heap fragmentation for short messages via a pre-allocated capacity).
36 * - @c take_view() returns a @c std::string_view into the internal buffer,
37 * enabling zero-copy hand-off to the logger sink without an extra copy.
38 * - @c write_raw() bypasses @c std::ostream formatting and writes bytes directly
39 * into the underlying buffer, suitable for pre-formatted C-strings.
40 * - Default capacity is 256 bytes; the buffer grows automatically in 8 KiB
41 * increments up to any size. @c shrink_to_fit() can reclaim excess memory
42 * after an unusually long message.
43 *
44 * @note
45 * - @c FastStream is @b not thread-safe. Each thread should own its own instance,
46 * which is the pattern used by the Logger (thread_local FastStream).
47 * - Calling @c take_view() invalidates any previously taken view after the next
48 * write or explicit @c reset(). Do not hold views across multiple log calls.
49 *
50 * @par Example
51 * @code
52 * vlink::FastStream stream;
53 * stream << "sensor_id=" << 42 << " value=" << 3.14;
54 * std::string_view view = stream.take_view(); // view valid until next write
55 * write_to_sink(view);
56 * @endcode
57 */
58
59#pragma once
60
61#include <ostream>
62#include <string>
63#include <string_view>
64
65#include "./macros.h"
66
67namespace vlink {
68
69/**
70 * @class FastStream
71 * @brief High-performance @c std::ostream with an embedded resizable string buffer.
72 *
73 * @details
74 * Used internally by @c Logger to accumulate a single log message and hand it
75 * off to the sink as a @c std::string_view without an extra copy. All
76 * @c std::ostream formatting (hex, precision, fill, etc.) is supported.
77 */
78class VLINK_EXPORT FastStream : public std::ostream {
79 public:
80 /**
81 * @brief Constructs a @c FastStream with a default initial buffer capacity.
82 *
83 * @details
84 * The default capacity is 256 bytes. The buffer grows automatically when
85 * the message length exceeds the current capacity.
86 */
87 FastStream() noexcept;
88
89 /**
90 * @brief Destructor. Releases the internal string buffer.
91 */
92 ~FastStream() noexcept override;
93
94 /**
95 * @brief Clears all buffered content and resets the stream error state.
96 *
97 * @details
98 * After @c reset() the stream is ready to receive a new message. The
99 * allocated memory of the underlying buffer is retained (no deallocation).
100 */
101 void reset() noexcept;
102
103 /**
104 * @brief Appends the current buffered content to an existing @c std::string.
105 *
106 * @details
107 * The internal buffer is @b not reset after this call. Use this method when
108 * the content needs to be collected into an external string incrementally.
109 *
110 * @param target The string to which buffered content is appended.
111 */
112 void append_to(std::string& target) const noexcept;
113
114 /**
115 * @brief Returns a @c std::string_view of the current buffer contents.
116 *
117 * @details
118 * The stream is not reset by this call. The returned view remains valid only
119 * until the next write to the stream or an explicit @c reset(). This is the
120 * primary zero-copy hand-off mechanism used by the Logger.
121 *
122 * @warning
123 * Do not store the returned view beyond the lifetime of the next stream
124 * operation. Treating it as a persistent string leads to undefined behaviour.
125 *
126 * @return A @c std::string_view over the internal buffer content.
127 */
128 std::string_view take_view() noexcept;
129
130 /**
131 * @brief Returns the current number of bytes stored in the buffer.
132 *
133 * @return Number of bytes written since the last @c reset().
134 */
135 [[nodiscard]] size_t size() const noexcept;
136
137 /**
138 * @brief Returns the current allocated capacity of the internal buffer in bytes.
139 *
140 * @return Capacity in bytes.
141 */
142 [[nodiscard]] size_t capacity() const noexcept;
143
144 /**
145 * @brief Releases any excess capacity allocated by the internal buffer.
146 *
147 * @details
148 * Useful after a particularly large log message to reclaim memory back to
149 * a sensible minimum (@c kMinCapacity = 64 bytes).
150 */
151 void shrink_to_fit() noexcept;
152
153 /**
154 * @brief Writes raw bytes directly into the buffer, bypassing @c std::ostream formatting.
155 *
156 * @details
157 * This is faster than @c std::ostream::write for pre-formatted C-strings
158 * because it avoids locale and format-flag overhead.
159 *
160 * @param data Pointer to the bytes to write. Must not be null if @p len > 0.
161 * @param len Number of bytes to write.
162 * @return A reference to @c *this to support chaining.
163 */
164 FastStream& write_raw(const char* data, size_t len) noexcept;
165
166 private:
167 static constexpr size_t kDefaultCapacity{256};
168 static constexpr size_t kMinCapacity{64};
169 static constexpr size_t kMaxExpandSize{8192};
170
171 class VLINK_EXPORT StringBuf final : public std::streambuf {
172 public:
173 explicit StringBuf(size_t initial_capacity = kDefaultCapacity) noexcept;
174
175 void reset() noexcept;
176
177 void shrink_to_fit() noexcept;
178
179 void append_to(std::string& target) const noexcept;
180
181 [[nodiscard]] std::string_view take_view() noexcept;
182
183 [[nodiscard]] size_t size() const noexcept;
184
185 [[nodiscard]] size_t capacity() const noexcept;
186
187 [[nodiscard]] int_type overflow(int_type ch) override;
188
189 std::streamsize xsputn(const char* s, std::streamsize n) override;
190
191 private:
192 void grow_buffer(size_t required_size) noexcept;
193
194 void advance_pptr(size_t count) noexcept;
195
196 std::string buffer_;
197
199 };
200
201 StringBuf buf_;
202
204};
205
206} // namespace vlink
Platform-independent macro definitions for the VLink library.
#define VLINK_EXPORT
Definition macros.h:85
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition macros.h:184
STL namespace.