FastLED 3.10.6
Loading...
Searching...
No Matches
number.h
Go to the documentation of this file.
1#pragma once
2
3// fl::number -- 64-bit packed tagged numeric value.
4//
5// A purpose-built single-slot replacement for `fl::variant<i64, float,
6// double>` that fits the JSON / RPC / serialized-numeric use case at half
7// the storage cost. Designed for FastLED #3022; reusable beyond JSON for
8// anyone trafficking small numeric values whose precision needs are
9// dominated by integer ranges with optional fractional resolution.
10//
11// Layout (64 bits total):
12// Bits [63:61] tag -- 3-bit discriminator (8 representations)
13// Bits [60:0] payload -- 61-bit value, interpretation per `tag`
14//
15// The shared 64-bit slot means a `number` slots into the same union storage
16// as a `double` with zero size overhead -- no separate discriminator byte
17// gets padded into a 16-byte cell.
18//
19// Tag semantics (initial set; tags 4..7 reserved for future expansion):
20// SIGNED_INT -- payload is a 61-bit two's-complement signed integer
21// (range: ~ +/- 1.15e18). Common case for JSON / RPC
22// integers; covers microsecond timestamps and
23// LED-count-like values without truncation.
24// UNSIGNED_INT -- payload is a 61-bit unsigned integer.
25// (range: 0 .. ~2.3e18).
26// FLOAT_60 -- payload is a custom 60-bit IEEE-754-like float:
27// bit 60 sign
28// bits 59:49 11-bit exponent (bias 1023, same as
29// IEEE-754 double, but range clipped to fit)
30// bits 48:0 49-bit mantissa (implicit-1, like double)
31// Reuses the soft_float bit-codec for widening to/from
32// IEEE-754 double when an FPU is present.
33// This is the slot the "float60 with identifier tag"
34// note in the design refers to.
35// (4..7 reserved -- future tags for Q-format fixed-point, IEEE-half
36// on host, error/NaN sentinel for parse failures, etc.)
37//
38// This header defines only the type, tag enum, and basic accessors.
39// Arithmetic, parser, serializer, and JSON-side adoption land in
40// follow-up PRs so each step ships with verifiable LPC-side bloat
41// reductions without bundling unrelated design changes.
42
43#include "fl/stl/cstddef.h"
44#include "fl/stl/noexcept.h"
46#include "fl/stl/stdint.h"
47
48namespace fl {
49
50class number {
51public:
62
63 // 64-bit raw representation: tag in top 3 bits, payload in low 61.
64 fl::u64 raw;
65
66 static constexpr fl::u64 kPayloadMask = (fl::u64(1) << 61) - 1;
67 static constexpr int kPayloadBits = 61;
68 static constexpr int kTagShift = 61;
69
70 constexpr number() FL_NO_EXCEPT : raw(0) {}
71
72 // Direct construction from (tag, payload). Caller is responsible for
73 // ensuring the payload fits in 61 bits (or has been masked).
74 constexpr number(tag t, fl::u64 payload) FL_NO_EXCEPT
75 : raw((static_cast<fl::u64>(t) << kTagShift) |
76 (payload & kPayloadMask)) {}
77
78 // Factories for the initial tag set.
79
80 // Construct a SIGNED_INT number from any 64-bit signed integer that
81 // fits in 61 signed bits. Saturates at +/-(2^60 - 1) for out-of-range
82 // inputs (returns SIGNED_INT_MAX / MIN-of-61-bits) so the call site
83 // never silently aliases into a different tag's payload.
84 static number from_signed(fl::i64 v) FL_NO_EXCEPT {
85 constexpr fl::i64 kMax = (fl::i64(1) << 60) - 1;
86 constexpr fl::i64 kMin = -(fl::i64(1) << 60);
87 if (v > kMax) v = kMax;
88 if (v < kMin) v = kMin;
89 // Sign-extend into 61 bits, then mask.
90 const fl::u64 payload = static_cast<fl::u64>(v) & kPayloadMask;
92 }
93
94 // Construct an UNSIGNED_INT number; saturates at 2^61 - 1.
95 static number from_unsigned(fl::u64 v) FL_NO_EXCEPT {
96 if (v > kPayloadMask) v = kPayloadMask;
97 return number(tag::UNSIGNED_INT, v);
98 }
99
100 // Tag / payload accessors.
101 constexpr tag get_tag() const FL_NO_EXCEPT {
102 return static_cast<tag>(raw >> kTagShift);
103 }
104 constexpr fl::u64 payload() const FL_NO_EXCEPT {
105 return raw & kPayloadMask;
106 }
107
108 // Sign-extended payload for SIGNED_INT extraction. Caller is
109 // responsible for asserting `get_tag() == tag::SIGNED_INT`.
111 // Sign-extend bit 60 across the upper 3 bits.
112 const fl::u64 p = payload();
113 const fl::u64 sign_bit = p & (fl::u64(1) << 60);
114 return static_cast<fl::i64>(sign_bit ? (p | ~kPayloadMask) : p);
115 }
116};
117
118FL_STATIC_ASSERT(sizeof(number) == sizeof(fl::u64),
119 "fl::number must be exactly 64 bits to slot into JSON / RPC "
120 "value cells without padding overhead.");
121
122} // namespace fl
constexpr number(tag t, fl::u64 payload) FL_NO_EXCEPT
Definition number.h:74
constexpr number() FL_NO_EXCEPT
Definition number.h:70
fl::u64 raw
Definition number.h:64
static number from_signed(fl::i64 v) FL_NO_EXCEPT
Definition number.h:84
fl::i64 as_signed_payload() const FL_NO_EXCEPT
Definition number.h:110
constexpr tag get_tag() const FL_NO_EXCEPT
Definition number.h:101
static constexpr int kTagShift
Definition number.h:68
constexpr fl::u64 payload() const FL_NO_EXCEPT
Definition number.h:104
static constexpr fl::u64 kPayloadMask
Definition number.h:66
static constexpr int kPayloadBits
Definition number.h:67
static number from_unsigned(fl::u64 v) FL_NO_EXCEPT
Definition number.h:95
unsigned char u8
Definition stdint.h:131
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_STATIC_ASSERT(...)
Portable compile-time assertion wrapper.