microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
bintime.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include "../microfmt.hpp"
11#include <cstddef>
12#include <cstdint>
13#include <string_view>
14#include <type_traits>
15
16namespace microfmt {
17
18// ============================================================================
19// FreeBSD Time Type Traits
20// ============================================================================
21
22namespace detail {
23
24// Primary detection: checks for struct with .sec and .frac members
25template <typename T, typename = void> struct is_bintime_struct : std::false_type {};
26
27template <typename T>
28struct is_bintime_struct<T, std::void_t<decltype(std::declval<T>().sec), decltype(std::declval<T>().frac)>>
29 : std::true_type {};
30
31} // namespace detail
32
41template <typename T> struct is_bintime : detail::is_bintime_struct<T> {};
42
47template <typename T>
49
56enum class time_precision : uint8_t { sec = 0, ms = 3, us = 6, ns = 9, ps = 12 };
57
58// ============================================================================
59// Zero-Float Scaling Helpers
60// ============================================================================
61
62namespace detail {
63
71inline uint64_t bintime_frac_to_decimal(uint64_t frac, time_precision prec) noexcept {
72 uint64_t multiplier = 1'000'000'000;
73 switch (prec) {
75 multiplier = 1'000;
76 break;
78 multiplier = 1'000'000;
79 break;
81 multiplier = 1'000'000'000;
82 break;
84 multiplier = 1'000'000'000'000ULL;
85 break;
87 default:
88 return 0;
89 }
90
91 constexpr uint64_t half_mask = 0xFFFFFFFFULL;
92 const uint64_t frac_low = frac & half_mask;
93 const uint64_t frac_high = frac >> 32;
94 const uint64_t multiplier_low = multiplier & half_mask;
95 const uint64_t multiplier_high = multiplier >> 32;
96
97 const uint64_t low_product = frac_low * multiplier_low;
98 const uint64_t middle = frac_high * multiplier_low + (low_product >> 32);
99 const uint64_t middle_low = middle & half_mask;
100 const uint64_t middle_high = middle >> 32;
101 const uint64_t cross = middle_low + frac_low * multiplier_high;
102
103 return frac_high * multiplier_high + middle_high + (cross >> 32);
104}
105
113inline uint32_t sbintime_frac_to_decimal(uint32_t frac32, time_precision prec) noexcept {
114 uint64_t multiplier = 1'000'000'000;
115 switch (prec) {
117 multiplier = 1'000;
118 break;
120 multiplier = 1'000'000;
121 break;
123 multiplier = 1'000'000'000;
124 break;
127 default:
128 multiplier = 1'000'000'000;
129 break;
130 }
131 return static_cast<uint32_t>((static_cast<uint64_t>(frac32) * multiplier) >> 32);
132}
133
134} // namespace detail
135
136// ============================================================================
137// Formatter for Any `is_bintime` Matching Type (e.g. native struct bintime)
138// ============================================================================
139
148template <typename T> struct formatter<T, std::enable_if_t<is_bintime_v<T>>> {
156 bool show_unit{true};
157
162 constexpr void parse(format_parse_context &ctx) noexcept {
163 auto spec = ctx.spec();
164 for (char c : spec) {
165 if (c == 'm' || c == '3')
166 precision = time_precision::ms;
167 else if (c == 'u' || c == '6')
168 precision = time_precision::us;
169 else if (c == 'n' || c == '9')
170 precision = time_precision::ns;
171 else if (c == 'p' || c == '1')
172 precision = time_precision::ps;
173 else if (c == 'r' || c == 'R')
174 show_unit = false;
175 }
176 }
177
183 // time-field arithmetic converts between C/POSIX integer widths whose ranges are bounded by the format.
184 RELOCO_BEGIN_UNSAFE_CODE
185 void format(const T &bt, const sink &out) const noexcept {
186 int64_t sec = static_cast<int64_t>(bt.sec);
187 uint64_t frac = static_cast<uint64_t>(bt.frac);
188
189 if (sec < 0) {
190 out.put('-');
191 detail::format_unsigned<detail::radix::decimal>(out, static_cast<uint64_t>(-sec), false, 0);
192 } else {
193 detail::format_unsigned<detail::radix::decimal>(out, static_cast<uint64_t>(sec), false, 0);
194 }
195
196 if (precision != time_precision::sec) {
197 out.put('.');
198 uint64_t dec_frac = detail::bintime_frac_to_decimal(frac, precision);
199 detail::format_unsigned<detail::radix::decimal>(out, dec_frac, false, static_cast<int>(precision));
200 }
201
202 if (show_unit) {
203 out.put('s');
204 }
205 }
207};
208
209// ============================================================================
210// sbintime_t Wrapper View (disambiguates from standard int64_t)
211// ============================================================================
212
219template <typename T> struct sbintime_view {
223 int64_t sbt{0};
224};
225
232template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
233[[nodiscard]] constexpr auto as_sbintime(T val) noexcept {
234 return sbintime_view<T>{static_cast<int64_t>(val)};
235}
236
245template <typename T> struct formatter<sbintime_view<T>> {
253 bool show_unit{true};
254
259 constexpr void parse(format_parse_context &ctx) noexcept {
260 auto spec = ctx.spec();
261 for (char c : spec) {
262 if (c == 'm' || c == '3')
263 precision = time_precision::ms;
264 else if (c == 'u' || c == '6')
265 precision = time_precision::us;
266 else if (c == 'n' || c == '9')
267 precision = time_precision::ns;
268 else if (c == 'r' || c == 'R')
269 show_unit = false;
270 }
271 }
272
278 void format(const sbintime_view<T> &sv, const sink &out) const noexcept {
279 const int64_t val = sv.sbt;
280 uint64_t magnitude = 0;
281 if (val < 0) {
282 out.put('-');
283 magnitude = static_cast<uint64_t>(-(val + 1)) + 1;
284 } else {
285 magnitude = static_cast<uint64_t>(val);
286 }
287
288 const uint64_t sec = magnitude >> 32;
289 const uint32_t frac = static_cast<uint32_t>(magnitude & UINT64_C(0xFFFFFFFF));
290
291 detail::format_unsigned<detail::radix::decimal>(out, sec, false, 0);
292
293 if (precision != time_precision::sec) {
294 out.put('.');
295 uint32_t dec_frac = detail::sbintime_frac_to_decimal(frac, precision);
296 detail::format_unsigned<detail::radix::decimal>(out, dec_frac, false, static_cast<int>(precision));
297 }
298
299 if (show_unit) {
300 out.put('s');
301 }
302 }
303};
304
305} // namespace microfmt
Definition microfmt.hpp:881
uint64_t bintime_frac_to_decimal(uint64_t frac, time_precision prec) noexcept
Converts a bintime fractional part into decimal-scaling digits.
Definition bintime.hpp:71
uint32_t sbintime_frac_to_decimal(uint32_t frac32, time_precision prec) noexcept
Converts an sbintime_t fractional part into decimal-scaling digits.
Definition bintime.hpp:113
Definition assert.hpp:19
constexpr auto as_sbintime(T val) noexcept
Wraps an integral sbintime_t value in an sbintime_view.
Definition bintime.hpp:233
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
time_precision
Fractional-second precision selectors for time formatting.
Definition bintime.hpp:56
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
constexpr bool is_bintime_v
Convenience variable template for is_bintime.
Definition bintime.hpp:48
Definition bintime.hpp:25
constexpr void parse(format_parse_context &ctx) noexcept
Parses precision and unit flags from the format specifier.
Definition bintime.hpp:162
RELOCO_BEGIN_UNSAFE_CODE void format(const T &bt, const sink &out) const noexcept
Renders a binary time value as sec.frac with optional s unit.
Definition bintime.hpp:185
void format(const sbintime_view< T > &sv, const sink &out) const noexcept
Renders an sbintime_t as sec.frac with optional s unit.
Definition bintime.hpp:278
constexpr void parse(format_parse_context &ctx) noexcept
Parses precision and unit flags from the format specifier.
Definition bintime.hpp:259
Definition microfmt.hpp:985
Customization point trait detecting FreeBSD-style binary time types.
Definition bintime.hpp:41
Non-owning view disambiguating an sbintime_t value from a plain int64_t.
Definition bintime.hpp:219
int64_t sbt
Raw 64-bit sbintime_t value (signed fixed-point, 32.32).
Definition bintime.hpp:223