VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
helpers.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 helpers.h
26 * @brief String, number, hash and formatting utility functions.
27 *
28 * @details
29 * The @c Helpers namespace groups portable, stateless helper functions used throughout
30 * VLink for common tasks such as string splitting, type conversion, human-readable size
31 * formatting, and compile-time string matching.
32 *
33 * @note
34 * - All functions are @c noexcept; errors are indicated by default-constructed return values.
35 * - Encoding conversion functions (@c string_local_to_utf8, @c string_utf8_to_local) use
36 * @c iconv on POSIX and the Windows ANSI code page on Windows.
37 *
38 * @par Example
39 * @code
40 * auto parts = vlink::Helpers::get_split_string("a,b,c", ',');
41 * // parts == {"a", "b", "c"}
42 *
43 * std::string s = vlink::Helpers::format_file_size(1536);
44 * // s == "1.50KB"
45 * @endcode
46 */
47
48#pragma once
49
50#include <cstdint>
51#include <filesystem>
52#include <string>
53#include <string_view>
54#include <utility>
55#include <vector>
56
57#include "./macros.h"
58
59namespace vlink {
60
61/**
62 * @namespace vlink::Helpers
63 * @brief Stateless string, number, hash and formatting helper functions.
64 */
65namespace Helpers { // NOLINT(readability-identifier-naming)
66
67/**
68 * @brief Converts a decimal string to @c int.
69 *
70 * @param str Decimal integer string.
71 * @param dv Default value returned when conversion fails. Default: 0.
72 * @return Parsed integer, or @p dv on failure.
73 */
74[[nodiscard]] VLINK_EXPORT int to_int(const std::string& str, int dv = 0) noexcept;
75
76/**
77 * @brief Converts a decimal string to @c int64_t with an optional byte offset.
78 *
79 * @param str Decimal integer string.
80 * @param dv Default value returned when conversion fails. Default: 0.
81 * @param offset Starting character offset within @p str. Default: 0.
82 * @return Parsed 64-bit integer, or @p dv on failure.
83 */
84[[nodiscard]] VLINK_EXPORT int64_t to_long(const std::string& str, int64_t dv = 0, int offset = 0) noexcept;
85
86/**
87 * @brief Converts a @c double to a string with a specified number of decimal places.
88 *
89 * @param value Value to convert.
90 * @param precision Number of decimal places. Default: 2.
91 * @return Formatted string representation.
92 */
93[[nodiscard]] VLINK_EXPORT std::string double_to_string(double value, int precision = 2) noexcept;
94
95/**
96 * @brief Combines two 64-bit hash values into one using a mixing function.
97 *
98 * @details
99 * Based on a Murmur-inspired mix to spread entropy across both inputs.
100 * Suitable for building composite hash keys.
101 *
102 * @param a First hash value.
103 * @param b Second hash value.
104 * @return Combined hash value.
105 */
106[[nodiscard]] VLINK_EXPORT uint64_t hash_combine(uint64_t a, uint64_t b) noexcept;
107
108/**
109 * @brief Replaces all occurrences of @p from in @p str with @p to in-place.
110 *
111 * @param str String to modify.
112 * @param from Substring to search for.
113 * @param to Replacement substring.
114 */
115VLINK_EXPORT void replace_string(std::string& str, const std::string& from, const std::string& to) noexcept;
116
117/**
118 * @brief Strips leading and trailing whitespace from a string.
119 *
120 * @param str Input string.
121 * @return Trimmed copy.
122 */
123[[nodiscard]] VLINK_EXPORT std::string trim_string(const std::string& str) noexcept;
124
125/**
126 * @brief Converts a UTF-8 @c std::string to a @c std::wstring.
127 *
128 * @param input UTF-8 encoded string.
129 * @return Wide-character string, or empty on failure.
130 */
131[[nodiscard]] VLINK_EXPORT std::wstring string_to_wstring(const std::string& input) noexcept;
132
133/**
134 * @brief Converts a @c std::wstring to a UTF-8 @c std::string.
135 *
136 * @param input Wide-character string.
137 * @return UTF-8 encoded string, or empty on failure.
138 */
139[[nodiscard]] VLINK_EXPORT std::string wstring_to_string(const std::wstring& input) noexcept;
140
141/**
142 * @brief Converts a locally-encoded string to UTF-8.
143 *
144 * @details
145 * On POSIX, uses the current locale's @c iconv to convert from the system encoding.
146 * On Windows, reads the active ANSI code page and converts via Win32 APIs.
147 *
148 * @param local_str Locally-encoded (system locale) string.
149 * @return UTF-8 encoded string, or the original string if conversion is not needed.
150 */
151[[nodiscard]] VLINK_EXPORT std::string string_local_to_utf8(const std::string& local_str) noexcept;
152
153/**
154 * @brief Converts a UTF-8 string to the locally-encoded system string.
155 *
156 * @param utf8_str UTF-8 encoded string.
157 * @return Locally-encoded (system locale) string.
158 */
159[[nodiscard]] VLINK_EXPORT std::string string_utf8_to_local(const std::string& utf8_str) noexcept;
160
161/**
162 * @brief Converts a @c std::filesystem::path to a UTF-8 @c std::string portably.
163 *
164 * @details
165 * On Windows, @c path::string() returns the ANSI path. This function always
166 * returns a UTF-8 string regardless of platform.
167 *
168 * @param path File-system path.
169 * @return UTF-8 path string.
170 */
171[[nodiscard]] VLINK_EXPORT std::string path_to_string(const std::filesystem::path& path) noexcept;
172
173/**
174 * @brief Splits a string by a delimiter character and returns the parts as a vector.
175 *
176 * @param str Input string.
177 * @param f Delimiter character.
178 * @return Vector of substrings (may include empty strings for consecutive delimiters).
179 */
180[[nodiscard]] VLINK_EXPORT std::vector<std::string> get_split_string(const std::string& str, char f) noexcept;
181
182/**
183 * @brief Splits a @c string_view by a delimiter character returning non-owning views.
184 *
185 * @details
186 * Each element is a @c string_view into the original @p str. The caller must ensure
187 * @p str outlives all returned views.
188 *
189 * @param str Input string view.
190 * @param f Delimiter character.
191 * @return Vector of @c string_view over the substrings.
192 */
193[[nodiscard]] VLINK_EXPORT std::vector<std::string_view> get_split_string_view(std::string_view str, char f) noexcept;
194
195/**
196 * @brief Splits a string at the first occurrence of a delimiter and returns a pair.
197 *
198 * @details
199 * Returns @c {"", ""} if the delimiter is not found.
200 *
201 * @param str Input string.
202 * @param f Delimiter character.
203 * @return Pair of the left and right substrings around the first delimiter.
204 */
205[[nodiscard]] VLINK_EXPORT std::pair<std::string, std::string> get_pair_string(const std::string& str, char f) noexcept;
206
207/**
208 * @brief Splits a @c string_view at the first occurrence of a delimiter returning non-owning views.
209 *
210 * @details
211 * Each element is a @c string_view into the original @p str. The caller must ensure
212 * @p str outlives all returned views.
213 * Returns @c {"", ""} if @p str is empty.
214 * If the delimiter is not found, the entire string is returned as @c first with an empty @c second.
215 *
216 * @param str Input string view.
217 * @param f Delimiter character.
218 * @return Pair of @c string_view over the left and right substrings around the first delimiter.
219 */
220[[nodiscard]] VLINK_EXPORT std::pair<std::string_view, std::string_view> get_pair_string_view(std::string_view str,
221 char f) noexcept;
222
223/**
224 * @brief Computes a 32-bit FNV-1a-style hash code for a string.
225 *
226 * @param str Input string.
227 * @return 32-bit hash value.
228 */
229[[nodiscard]] VLINK_EXPORT uint32_t get_hash_code(const std::string& str) noexcept;
230
231/**
232 * @brief Formats a duration in milliseconds as a human-readable time string.
233 *
234 * @details
235 * Example: 125000 ms -> @c "00:02:05" or @c "00:02:05:000" when @p show_millis is @c true.
236 *
237 * @param milliseconds Duration in milliseconds.
238 * @param show_millis If @c true, append the sub-second milliseconds component.
239 * @return Human-readable time string.
240 */
241[[nodiscard]] VLINK_EXPORT std::string format_milliseconds(int64_t milliseconds, bool show_millis) noexcept;
242
243/**
244 * @brief Formats a nanosecond-precision epoch timestamp as a date-time string.
245 *
246 * @details
247 * Output format: @c "YYYY-MM-DD HH:MM:SS.mmm".
248 *
249 * @param nanoseconds_since_epoch Unix timestamp in nanoseconds.
250 * @return Formatted date-time string.
251 */
252[[nodiscard]] VLINK_EXPORT std::string format_date(int64_t nanoseconds_since_epoch) noexcept;
253
254/**
255 * @brief Formats a time difference in milliseconds as a human-readable string.
256 *
257 * @details
258 * Produces strings such as @c "01:23:45:000" or @c "00:00:00:250".
259 *
260 * @param milliseconds Time difference in milliseconds.
261 * @return Human-readable time-diff string.
262 */
263[[nodiscard]] VLINK_EXPORT std::string format_time_diff(int32_t milliseconds) noexcept;
264
265/**
266 * @brief Formats a signed 64-bit integer as a @c "0x..." hexadecimal string.
267 *
268 * @param hex_number Value to format.
269 * @return Hexadecimal string prefixed with @c "0x".
270 */
271[[nodiscard]] VLINK_EXPORT std::string format_hex_number(int64_t hex_number) noexcept;
272
273/**
274 * @brief Formats an unsigned 64-bit integer as a @c "0x..." hexadecimal string.
275 *
276 * @param hex_number Value to format.
277 * @return Hexadecimal string prefixed with @c "0x".
278 */
279[[nodiscard]] VLINK_EXPORT std::string format_hex_number(uint64_t hex_number) noexcept;
280
281/**
282 * @brief Formats a byte count as a human-readable size string.
283 *
284 * @details
285 * Selects the appropriate unit (B, KB, MB, GB, TB) and formats to 2 decimal places.
286 * Example: @c 1536 -> @c "1.50KB".
287 *
288 * @param size Size in bytes.
289 * @return Human-readable size string.
290 */
291[[nodiscard]] VLINK_EXPORT std::string format_file_size(size_t size) noexcept;
292
293/**
294 * @brief Formats a byte-per-second rate as a human-readable string.
295 *
296 * @details
297 * Selects the appropriate unit (B/s, KB/s, MB/s, GB/s) and formats to 2 decimal places.
298 * Example: @c 1048576 -> @c "1.00MB/s".
299 *
300 * @param size Rate in bytes per second.
301 * @return Human-readable rate string.
302 */
303[[nodiscard]] VLINK_EXPORT std::string format_rate_size(size_t size) noexcept;
304
305/**
306 * @brief Converts a date string to a Unix millisecond timestamp.
307 *
308 * @details
309 * Accepts ISO 8601 style dates such as @c "2026-03-18" or @c "2026-03-18 12:00:00".
310 *
311 * @param date Date string to parse.
312 * @return Unix timestamp in milliseconds, or -1 on parse failure.
313 */
314[[nodiscard]] VLINK_EXPORT int64_t convert_date_to_timestamp(const std::string& date) noexcept;
315
316/**
317 * @brief Compile-time check whether @p str starts with the literal @p target.
318 *
319 * @tparam SizeT Size of the string literal @p target (deduced; includes null terminator).
320 * @param str String to test.
321 * @param target String literal prefix to check for.
322 * @return @c true if @p str begins with @p target.
323 */
324template <uint8_t SizeT>
325[[nodiscard]] bool has_startwith(const std::string& str, const char (&target)[SizeT]) noexcept;
326
327/**
328 * @brief Compile-time check whether @p str ends with the literal @p target.
329 *
330 * @tparam SizeT Size of the string literal @p target (deduced; includes null terminator).
331 * @param str String to test.
332 * @param target String literal suffix to check for.
333 * @return @c true if @p str ends with @p target.
334 */
335template <uint8_t SizeT>
336[[nodiscard]] bool has_endwith(const std::string& str, const char (&target)[SizeT]) noexcept;
337
338/**
339 * @brief Checks whether a @c string_view starts with a given prefix (constexpr-friendly).
340 *
341 * @details
342 * Falls back to @c std::string_view::starts_with on C++20; otherwise uses manual comparison.
343 *
344 * @param str String to test.
345 * @param target Prefix to check for.
346 * @return @c true if @p str starts with @p target.
347 */
348[[nodiscard]] constexpr bool has_startwith(std::string_view str, std::string_view target) noexcept;
349
350/**
351 * @brief Checks whether a @c string_view ends with a given suffix (constexpr-friendly).
352 *
353 * @details
354 * Falls back to @c std::string_view::ends_with on C++20; otherwise uses manual comparison.
355 *
356 * @param str String to test.
357 * @param target Suffix to check for.
358 * @return @c true if @p str ends with @p target.
359 */
360[[nodiscard]] constexpr bool has_endwith(std::string_view str, std::string_view target) noexcept;
361
362/**
363 * @brief Checks whether a @c string_view contains a given substring.
364 *
365 * @param sv String to search.
366 * @param needle Substring to find.
367 * @return @c true if @p sv contains @p needle. Returns @c true if @p needle is empty.
368 */
369[[nodiscard]] constexpr bool contains_substring(std::string_view sv, std::string_view needle) noexcept;
370
371////////////////////////////////////////////////////////////////
372/// Details
373////////////////////////////////////////////////////////////////
374
375template <uint8_t SizeT>
376inline bool has_startwith(const std::string& str, const char (&target)[SizeT]) noexcept {
377 if constexpr (SizeT == 0) {
378 (void)str;
379 return false;
380 }
381
382 return str.compare(0, SizeT - 1, target) == 0;
383}
384
385template <uint8_t SizeT>
386inline bool has_endwith(const std::string& str, const char (&target)[SizeT]) noexcept {
387 if constexpr (SizeT == 0) {
388 (void)str;
389 return false;
390 }
391
392 if VUNLIKELY (str.size() < SizeT - 1) {
393 return false;
394 }
395
396 return str.compare(str.size() - (SizeT - 1), SizeT - 1, target) == 0;
397}
398
399inline constexpr bool has_startwith(std::string_view str, std::string_view target) noexcept {
400#if __cplusplus >= 202002L
401 return str.starts_with(target);
402#else
403 return target.size() <= str.size() && str.substr(0, target.size()) == target;
404#endif
405}
406
407inline constexpr bool has_endwith(std::string_view str, std::string_view target) noexcept {
408#if __cplusplus >= 202002L
409 return str.ends_with(target);
410#else
411 return target.size() <= str.size() && str.substr(str.size() - target.size(), target.size()) == target;
412#endif
413}
414
415inline constexpr bool contains_substring(std::string_view sv, std::string_view needle) noexcept {
416 if (needle.empty()) {
417 return true;
418 }
419
420 if (sv.size() < needle.size()) {
421 return false;
422 }
423
424 for (size_t i = 0; i <= sv.size() - needle.size(); ++i) {
425 bool match = true;
426
427 for (size_t j = 0; j < needle.size(); ++j) {
428 if (sv[i + j] != needle[j]) {
429 match = false;
430
431 break;
432 }
433 }
434
435 if (match) {
436 return true;
437 }
438 }
439
440 return false;
441}
442
443} // namespace Helpers
444
445} // namespace vlink
Platform-independent macro definitions for the VLink library.
#define VUNLIKELY(...)
Shorthand alias for VLINK_UNLIKELY. Hints that the expression is unlikely true.
Definition macros.h:302
#define VLINK_EXPORT
Definition macros.h:85
STL namespace.