wireforge 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. wireforge/__init__.py +3 -0
  2. wireforge/_data/cmake/WireForgeConfig.cmake +29 -0
  3. wireforge/_data/cmake/WireForgeConfigVersion.cmake +15 -0
  4. wireforge/_data/cmake/WireForgeGenerate.cmake +64 -0
  5. wireforge/_data/runtime/LICENSE +21 -0
  6. wireforge/_data/runtime/include/wireforge/array_access.hpp +35 -0
  7. wireforge/_data/runtime/include/wireforge/bit_utils.hpp +226 -0
  8. wireforge/_data/runtime/include/wireforge/byte_io.hpp +387 -0
  9. wireforge/_data/runtime/include/wireforge/checksum.hpp +201 -0
  10. wireforge/_data/runtime/include/wireforge/codec_support.hpp +412 -0
  11. wireforge/_data/runtime/include/wireforge/config.hpp +41 -0
  12. wireforge/_data/runtime/include/wireforge/delegate.hpp +101 -0
  13. wireforge/_data/runtime/include/wireforge/dispatcher.hpp +339 -0
  14. wireforge/_data/runtime/include/wireforge/expr_support.hpp +38 -0
  15. wireforge/_data/runtime/include/wireforge/frame_decoder.hpp +262 -0
  16. wireforge/_data/runtime/include/wireforge/frame_layout.hpp +20 -0
  17. wireforge/_data/runtime/include/wireforge/link.hpp +223 -0
  18. wireforge/_data/runtime/include/wireforge/lock.hpp +146 -0
  19. wireforge/_data/runtime/include/wireforge/ring_buffer.hpp +226 -0
  20. wireforge/_data/runtime/include/wireforge/span.hpp +116 -0
  21. wireforge/_data/runtime/include/wireforge/static_vector.hpp +264 -0
  22. wireforge/_data/runtime/include/wireforge/status.hpp +75 -0
  23. wireforge/_data/runtime/include/wireforge/transport.hpp +95 -0
  24. wireforge/_data/runtime/include/wireforge/validation.hpp +153 -0
  25. wireforge/_data/runtime/include/wireforge/version.hpp +22 -0
  26. wireforge/_data/runtime/requirements.yaml +438 -0
  27. wireforge/_data/schema/deviations.schema.json +47 -0
  28. wireforge/_data/schema/fragment.schema.json +79 -0
  29. wireforge/_data/schema/llr_ids.schema.json +25 -0
  30. wireforge/_data/schema/project.schema.json +1240 -0
  31. wireforge/_data/schema/runtime_requirements.schema.json +34 -0
  32. wireforge/_data/ui/assets/index-6Pomu6U4.js +44 -0
  33. wireforge/_data/ui/assets/index-GVdDyUAG.css +1 -0
  34. wireforge/_data/ui/index.html +13 -0
  35. wireforge/bundle.py +177 -0
  36. wireforge/ci_check.py +155 -0
  37. wireforge/cli.py +617 -0
  38. wireforge/codec.py +897 -0
  39. wireforge/compat.py +536 -0
  40. wireforge/coverage_points.py +64 -0
  41. wireforge/cpp.py +273 -0
  42. wireforge/cpp_codec.py +440 -0
  43. wireforge/cpp_interface.py +874 -0
  44. wireforge/cpp_tests.py +1993 -0
  45. wireforge/differential.py +586 -0
  46. wireforge/docs_markdown.py +72 -0
  47. wireforge/expr.py +598 -0
  48. wireforge/findings.py +187 -0
  49. wireforge/fmt.py +351 -0
  50. wireforge/generate.py +644 -0
  51. wireforge/golden.py +253 -0
  52. wireforge/hlr_import.py +112 -0
  53. wireforge/impact.py +249 -0
  54. wireforge/importers.py +402 -0
  55. wireforge/issues.py +27 -0
  56. wireforge/layout.py +681 -0
  57. wireforge/lifecycle.py +1266 -0
  58. wireforge/lifecycle_results.py +205 -0
  59. wireforge/lifecycle_rules.py +78 -0
  60. wireforge/lifecycle_runtime.py +375 -0
  61. wireforge/llr.py +693 -0
  62. wireforge/llr_ids.py +104 -0
  63. wireforge/loader.py +422 -0
  64. wireforge/mcp_server.py +357 -0
  65. wireforge/model.py +322 -0
  66. wireforge/plain.py +15 -0
  67. wireforge/py.typed +0 -0
  68. wireforge/reqif.py +289 -0
  69. wireforge/resources.py +43 -0
  70. wireforge/results.py +377 -0
  71. wireforge/runtime_reqs.py +147 -0
  72. wireforge/schema.py +151 -0
  73. wireforge/server/__init__.py +1 -0
  74. wireforge/server/app.py +619 -0
  75. wireforge/server/oidc.py +99 -0
  76. wireforge/server/storage.py +146 -0
  77. wireforge/service.py +1354 -0
  78. wireforge/templates/command/project.yaml +38 -0
  79. wireforge/templates/raw/project.yaml +28 -0
  80. wireforge/templates/telemetry/project.yaml +51 -0
  81. wireforge/test_cases.py +475 -0
  82. wireforge/test_project.py +478 -0
  83. wireforge/trace.py +442 -0
  84. wireforge/validate.py +1241 -0
  85. wireforge/vcs.py +44 -0
  86. wireforge/vectors.py +222 -0
  87. wireforge/versioning.py +137 -0
  88. wireforge/wireshark.py +792 -0
  89. wireforge/wireshark_runtime.lua +725 -0
  90. wireforge-0.1.0.dist-info/METADATA +91 -0
  91. wireforge-0.1.0.dist-info/RECORD +96 -0
  92. wireforge-0.1.0.dist-info/WHEEL +5 -0
  93. wireforge-0.1.0.dist-info/entry_points.txt +2 -0
  94. wireforge-0.1.0.dist-info/licenses/LICENSE +98 -0
  95. wireforge-0.1.0.dist-info/licenses/runtime/LICENSE +21 -0
  96. wireforge-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,387 @@
1
+ /**
2
+ * @file byte_io.hpp
3
+ * @brief Host-independent reading and writing of integers, floats and bit groups.
4
+ */
5
+ #ifndef WIREFORGE_BYTE_IO_HPP
6
+ #define WIREFORGE_BYTE_IO_HPP
7
+
8
+ #include "wireforge/bit_utils.hpp"
9
+ #include "wireforge/span.hpp"
10
+ #include "wireforge/status.hpp"
11
+
12
+ #include <cstddef>
13
+ #include <cstdint>
14
+ #include <cstring>
15
+ #include <limits>
16
+
17
+ namespace wireforge {
18
+
19
+ /// @brief Byte order of multi-byte values on the wire.
20
+ enum class Endian : std::uint8_t {
21
+ Big, ///< Most significant byte first.
22
+ Little, ///< Least significant byte first.
23
+ };
24
+
25
+ static_assert(std::numeric_limits<float>::is_iec559 && sizeof(float) == 4U,
26
+ "f32 requires IEEE-754 binary32");
27
+ static_assert(std::numeric_limits<double>::is_iec559 && sizeof(double) == 8U,
28
+ "f64 requires IEEE-754 binary64");
29
+
30
+ /// @brief Largest integer width in bytes.
31
+ inline constexpr std::size_t kMaxWidth = 8U;
32
+
33
+ /**
34
+ * @brief Stores the low @p width bytes of @p value at the start of @p out.
35
+ *
36
+ * Uses shifts only, so the result never depends on the host byte order.
37
+ * @param out Destination; must hold at least @p width bytes.
38
+ * @param value Value to store.
39
+ * @param width Number of bytes, 1..8.
40
+ * @param endian Byte order.
41
+ */
42
+ constexpr void store_unsigned(Span<std::uint8_t> out, std::uint64_t value, std::size_t width,
43
+ Endian endian) noexcept {
44
+ for (std::size_t index = 0U; index < width; ++index) {
45
+ const std::size_t byte_index = endian == Endian::Big ? width - 1U - index : index;
46
+ out[byte_index] = static_cast<std::uint8_t>((value >> (8U * index)) & 0xFFU);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @brief Loads an unsigned integer of @p width bytes from the start of @p input.
52
+ * @param input Source; must hold at least @p width bytes.
53
+ * @param width Number of bytes, 1..8.
54
+ * @param endian Byte order.
55
+ * @return The value, zero-extended to 64 bits.
56
+ */
57
+ [[nodiscard]] constexpr std::uint64_t load_unsigned(Span<const std::uint8_t> input,
58
+ std::size_t width, Endian endian) noexcept {
59
+ std::uint64_t value = 0U;
60
+ for (std::size_t index = 0U; index < width; ++index) {
61
+ const std::size_t byte_index = endian == Endian::Big ? index : width - 1U - index;
62
+ value = (value << 8U) | input[byte_index];
63
+ }
64
+ return value;
65
+ }
66
+
67
+ /// @brief true when @p width is a supported integer width (1..8 bytes).
68
+ [[nodiscard]] constexpr bool valid_width(std::size_t width) noexcept {
69
+ return width >= 1U && width <= kMaxWidth;
70
+ }
71
+
72
+ /// @brief Number of bits in @p width bytes.
73
+ [[nodiscard]] constexpr unsigned width_bits(std::size_t width) noexcept {
74
+ return narrow_to<unsigned>(width * 8U);
75
+ }
76
+
77
+ /**
78
+ * @brief Sequential writer over a caller-provided buffer.
79
+ *
80
+ * Every write returns a Status; a failing write changes neither the position nor the buffer.
81
+ * All functions except the float writers are constexpr.
82
+ */
83
+ class ByteWriter {
84
+ public:
85
+ /// @brief Writes into @p buffer starting at its first byte.
86
+ explicit constexpr ByteWriter(Span<std::uint8_t> buffer) noexcept : buffer_(buffer) {}
87
+
88
+ /// @brief Number of bytes written so far.
89
+ [[nodiscard]] constexpr std::size_t position() const noexcept { return position_; }
90
+ /// @brief Size of the buffer.
91
+ [[nodiscard]] constexpr std::size_t capacity() const noexcept { return buffer_.size(); }
92
+ /// @brief Bytes still free.
93
+ [[nodiscard]] constexpr std::size_t remaining() const noexcept {
94
+ return buffer_.size() - position_;
95
+ }
96
+ /// @brief The bytes written so far.
97
+ [[nodiscard]] constexpr Span<const std::uint8_t> written() const noexcept {
98
+ return Span<const std::uint8_t>(buffer_.first(position_));
99
+ }
100
+
101
+ /**
102
+ * @brief Writes an unsigned integer.
103
+ * @param value Value; must fit @p width bytes.
104
+ * @param width Number of bytes, 1..8.
105
+ * @param endian Byte order.
106
+ * @return Status::OutOfRange when @p value does not fit, Status::BufferTooSmall when the
107
+ * buffer is full, Status::InvalidState for an unsupported @p width.
108
+ */
109
+ constexpr Status write_unsigned(std::uint64_t value, std::size_t width,
110
+ Endian endian) noexcept {
111
+ if (!valid_width(width)) {
112
+ return Status::InvalidState;
113
+ }
114
+ if (!fits_unsigned(value, width_bits(width))) {
115
+ return Status::OutOfRange;
116
+ }
117
+ if (remaining() < width) {
118
+ return Status::BufferTooSmall;
119
+ }
120
+ store_unsigned(buffer_.subspan(position_), value, width, endian);
121
+ position_ += width;
122
+ return Status::Ok;
123
+ }
124
+
125
+ /**
126
+ * @brief Writes a two's complement integer.
127
+ * @param value Value; must fit @p width bytes as a signed integer.
128
+ * @param width Number of bytes, 1..8.
129
+ * @param endian Byte order.
130
+ * @return As write_unsigned().
131
+ */
132
+ constexpr Status write_signed(std::int64_t value, std::size_t width, Endian endian) noexcept {
133
+ if (!valid_width(width)) {
134
+ return Status::InvalidState;
135
+ }
136
+ if (!fits_signed(value, width_bits(width))) {
137
+ return Status::OutOfRange;
138
+ }
139
+ return write_unsigned(to_twos_complement(value, width_bits(width)), width, endian);
140
+ }
141
+
142
+ /**
143
+ * @brief Overwrites @p width bytes at @p offset inside the already written region.
144
+ *
145
+ * Used to fill in length or checksum fields after the rest of a frame has been written.
146
+ * @return Status::InvalidLength when [offset, offset + width) is not fully written yet;
147
+ * otherwise as write_unsigned().
148
+ */
149
+ constexpr Status write_unsigned_at(std::size_t offset, std::uint64_t value, std::size_t width,
150
+ Endian endian) const noexcept {
151
+ if (!valid_width(width)) {
152
+ return Status::InvalidState;
153
+ }
154
+ if (!fits_unsigned(value, width_bits(width))) {
155
+ return Status::OutOfRange;
156
+ }
157
+ if (offset > position_ || position_ - offset < width) {
158
+ return Status::InvalidLength;
159
+ }
160
+ store_unsigned(buffer_.subspan(offset), value, width, endian);
161
+ return Status::Ok;
162
+ }
163
+
164
+ /**
165
+ * @brief Writes an IEEE-754 binary32 value through its bit pattern.
166
+ * @return Status::BufferTooSmall when fewer than 4 bytes are free.
167
+ */
168
+ Status write_f32(float value, Endian endian) noexcept {
169
+ std::uint32_t bits = 0U;
170
+ std::memcpy(&bits, &value, sizeof(bits));
171
+ return write_unsigned(bits, sizeof(bits), endian);
172
+ }
173
+
174
+ /**
175
+ * @brief Writes an IEEE-754 binary64 value through its bit pattern.
176
+ * @return Status::BufferTooSmall when fewer than 8 bytes are free.
177
+ */
178
+ Status write_f64(double value, Endian endian) noexcept {
179
+ std::uint64_t bits = 0U;
180
+ std::memcpy(&bits, &value, sizeof(bits));
181
+ return write_unsigned(bits, sizeof(bits), endian);
182
+ }
183
+
184
+ /**
185
+ * @brief Copies @p bytes.
186
+ * @return Status::BufferTooSmall when they do not fit.
187
+ */
188
+ constexpr Status write_bytes(Span<const std::uint8_t> bytes) noexcept {
189
+ if (remaining() < bytes.size()) {
190
+ return Status::BufferTooSmall;
191
+ }
192
+ for (std::size_t index = 0U; index < bytes.size(); ++index) {
193
+ buffer_[position_ + index] = bytes[index];
194
+ }
195
+ position_ += bytes.size();
196
+ return Status::Ok;
197
+ }
198
+
199
+ /**
200
+ * @brief Writes @p count copies of @p value.
201
+ * @return Status::BufferTooSmall when they do not fit.
202
+ */
203
+ constexpr Status write_fill(std::uint8_t value, std::size_t count) noexcept {
204
+ if (remaining() < count) {
205
+ return Status::BufferTooSmall;
206
+ }
207
+ for (std::size_t index = 0U; index < count; ++index) {
208
+ buffer_[position_ + index] = value;
209
+ }
210
+ position_ += count;
211
+ return Status::Ok;
212
+ }
213
+
214
+ /**
215
+ * @brief Writes the container of a complete bit group.
216
+ * @return Status::InvalidState when @p packer is not complete; otherwise as write_unsigned().
217
+ */
218
+ constexpr Status write_bits(const BitPacker& packer, Endian endian) noexcept {
219
+ if (!packer.complete()) {
220
+ return Status::InvalidState;
221
+ }
222
+ return write_unsigned(packer.container(), packer.total_bits() / 8U, endian);
223
+ }
224
+
225
+ private:
226
+ Span<std::uint8_t> buffer_;
227
+ std::size_t position_ = 0U;
228
+ };
229
+
230
+ /**
231
+ * @brief Sequential reader over a caller-provided buffer.
232
+ *
233
+ * A failing read changes neither the position nor its output argument. All functions except the
234
+ * float readers are constexpr.
235
+ */
236
+ class ByteReader {
237
+ public:
238
+ /// @brief Reads from @p buffer starting at its first byte.
239
+ explicit constexpr ByteReader(Span<const std::uint8_t> buffer) noexcept : buffer_(buffer) {}
240
+
241
+ /// @brief Number of bytes consumed so far.
242
+ [[nodiscard]] constexpr std::size_t position() const noexcept { return position_; }
243
+ /// @brief Size of the buffer.
244
+ [[nodiscard]] constexpr std::size_t size() const noexcept { return buffer_.size(); }
245
+ /// @brief Bytes not consumed yet.
246
+ [[nodiscard]] constexpr std::size_t remaining() const noexcept {
247
+ return buffer_.size() - position_;
248
+ }
249
+ /// @brief The bytes not consumed yet.
250
+ [[nodiscard]] constexpr Span<const std::uint8_t> rest() const noexcept {
251
+ return buffer_.subspan(position_);
252
+ }
253
+
254
+ /**
255
+ * @brief Reads an unsigned integer.
256
+ * @param[out] value Result, zero-extended.
257
+ * @param width Number of bytes, 1..8.
258
+ * @param endian Byte order.
259
+ * @return Status::Truncated when fewer than @p width bytes remain, Status::InvalidState for an
260
+ * unsupported @p width.
261
+ */
262
+ constexpr Status read_unsigned(std::uint64_t& value, std::size_t width,
263
+ Endian endian) noexcept {
264
+ if (!valid_width(width)) {
265
+ return Status::InvalidState;
266
+ }
267
+ if (remaining() < width) {
268
+ return Status::Truncated;
269
+ }
270
+ value = load_unsigned(buffer_.subspan(position_), width, endian);
271
+ position_ += width;
272
+ return Status::Ok;
273
+ }
274
+
275
+ /**
276
+ * @brief Reads a two's complement integer and sign-extends it.
277
+ * @return As read_unsigned().
278
+ */
279
+ constexpr Status read_signed(std::int64_t& value, std::size_t width, Endian endian) noexcept {
280
+ std::uint64_t raw = 0U;
281
+ const Status status = read_unsigned(raw, width, endian);
282
+ if (status == Status::Ok) {
283
+ value = sign_extend(raw, width_bits(width));
284
+ }
285
+ return status;
286
+ }
287
+
288
+ /**
289
+ * @brief Reads @p width bytes at absolute @p offset without moving the position.
290
+ * @return Status::Truncated when the range is outside the buffer; otherwise as
291
+ * read_unsigned().
292
+ */
293
+ constexpr Status peek_unsigned_at(std::size_t offset, std::uint64_t& value, std::size_t width,
294
+ Endian endian) const noexcept {
295
+ if (!valid_width(width)) {
296
+ return Status::InvalidState;
297
+ }
298
+ if (offset > buffer_.size() || buffer_.size() - offset < width) {
299
+ return Status::Truncated;
300
+ }
301
+ value = load_unsigned(buffer_.subspan(offset), width, endian);
302
+ return Status::Ok;
303
+ }
304
+
305
+ /**
306
+ * @brief Reads an IEEE-754 binary32 value from its bit pattern.
307
+ * @return Status::Truncated when fewer than 4 bytes remain.
308
+ */
309
+ Status read_f32(float& value, Endian endian) noexcept {
310
+ std::uint64_t raw = 0U;
311
+ const Status status = read_unsigned(raw, sizeof(float), endian);
312
+ if (status == Status::Ok) {
313
+ const auto bits = static_cast<std::uint32_t>(raw);
314
+ std::memcpy(&value, &bits, sizeof(value));
315
+ }
316
+ return status;
317
+ }
318
+
319
+ /**
320
+ * @brief Reads an IEEE-754 binary64 value from its bit pattern.
321
+ * @return Status::Truncated when fewer than 8 bytes remain.
322
+ */
323
+ Status read_f64(double& value, Endian endian) noexcept {
324
+ std::uint64_t bits = 0U;
325
+ const Status status = read_unsigned(bits, sizeof(double), endian);
326
+ if (status == Status::Ok) {
327
+ std::memcpy(&value, &bits, sizeof(value));
328
+ }
329
+ return status;
330
+ }
331
+
332
+ /**
333
+ * @brief Copies out.size() bytes into @p out.
334
+ * @return Status::Truncated when not enough bytes remain.
335
+ */
336
+ constexpr Status read_bytes(Span<std::uint8_t> out) noexcept {
337
+ if (remaining() < out.size()) {
338
+ return Status::Truncated;
339
+ }
340
+ for (std::size_t index = 0U; index < out.size(); ++index) {
341
+ out[index] = buffer_[position_ + index];
342
+ }
343
+ position_ += out.size();
344
+ return Status::Ok;
345
+ }
346
+
347
+ /**
348
+ * @brief Skips @p count bytes.
349
+ * @return Status::Truncated when not enough bytes remain.
350
+ */
351
+ constexpr Status skip(std::size_t count) noexcept {
352
+ if (remaining() < count) {
353
+ return Status::Truncated;
354
+ }
355
+ position_ += count;
356
+ return Status::Ok;
357
+ }
358
+
359
+ /**
360
+ * @brief Reads the container of a bit group and prepares an unpacker for its fields.
361
+ * @param[out] unpacker Replaced by an unpacker over the container on success.
362
+ * @param total_bits Width of the group: 8, 16, ..., 64.
363
+ * @param order Placement of the first field.
364
+ * @param endian Byte order of the container.
365
+ * @return Status::InvalidState for an invalid group width; otherwise as read_unsigned().
366
+ */
367
+ constexpr Status read_bits(BitUnpacker& unpacker, unsigned total_bits, BitOrder order,
368
+ Endian endian) noexcept {
369
+ if (!BitUnpacker(0U, total_bits, order).valid()) {
370
+ return Status::InvalidState;
371
+ }
372
+ std::uint64_t container = 0U;
373
+ const Status status = read_unsigned(container, total_bits / 8U, endian);
374
+ if (status == Status::Ok) {
375
+ unpacker = BitUnpacker(container, total_bits, order);
376
+ }
377
+ return status;
378
+ }
379
+
380
+ private:
381
+ Span<const std::uint8_t> buffer_;
382
+ std::size_t position_ = 0U;
383
+ };
384
+
385
+ } // namespace wireforge
386
+
387
+ #endif // WIREFORGE_BYTE_IO_HPP
@@ -0,0 +1,201 @@
1
+ /**
2
+ * @file checksum.hpp
3
+ * @brief Checksum algorithm requirements and a parametric CRC engine.
4
+ *
5
+ * A checksum algorithm is a type @c C with only static members:
6
+ * @code
7
+ * struct MyChecksum {
8
+ * static constexpr std::size_t kSize = 2; // bytes of the value on the wire, 1..8
9
+ * struct State { ... }; // copyable running state
10
+ * static State init() noexcept;
11
+ * static void update(State&, wireforge::Span<const std::uint8_t>) noexcept; // any chunking
12
+ * static std::uint64_t finalize(const State&) noexcept;
13
+ * };
14
+ * static_assert(wireforge::is_checksum_v<MyChecksum>);
15
+ * @endcode
16
+ * The finalized value is written with the byte order of the message that carries it.
17
+ */
18
+ #ifndef WIREFORGE_CHECKSUM_HPP
19
+ #define WIREFORGE_CHECKSUM_HPP
20
+
21
+ #include "wireforge/array_access.hpp"
22
+ #include "wireforge/bit_utils.hpp"
23
+ #include "wireforge/config.hpp"
24
+ #include "wireforge/span.hpp"
25
+
26
+ #include <array>
27
+ #include <cstddef>
28
+ #include <cstdint>
29
+ #include <type_traits>
30
+ #include <utility>
31
+
32
+ namespace wireforge {
33
+
34
+ /// @cond INTERNAL
35
+ namespace detail {
36
+
37
+ template <typename C>
38
+ using ChecksumUpdate = decltype(C::update(std::declval<typename C::State&>(),
39
+ std::declval<Span<const std::uint8_t>>()));
40
+ template <typename C>
41
+ using ChecksumFinalize = decltype(C::finalize(std::declval<const typename C::State&>()));
42
+
43
+ template <typename C>
44
+ inline constexpr bool kChecksumSignatures =
45
+ std::is_same_v<decltype(C::init()), typename C::State> &&
46
+ std::is_same_v<ChecksumFinalize<C>, std::uint64_t> &&
47
+ std::is_convertible_v<decltype(C::kSize), std::size_t>;
48
+
49
+ template <typename C> inline constexpr bool kChecksumSize = C::kSize >= 1U && C::kSize <= 8U;
50
+
51
+ template <typename C>
52
+ inline constexpr bool kChecksumNoexcept =
53
+ noexcept(C::init()) &&
54
+ noexcept(
55
+ C::update(std::declval<typename C::State&>(), std::declval<Span<const std::uint8_t>>())) &&
56
+ noexcept(C::finalize(std::declval<const typename C::State&>()));
57
+
58
+ } // namespace detail
59
+ /// @endcond
60
+
61
+ /// @brief Detects whether @p C satisfies the checksum requirements (see the file description).
62
+ template <typename C, typename = void> struct is_checksum : std::false_type {};
63
+
64
+ /// @cond INTERNAL
65
+ template <typename C>
66
+ struct is_checksum<C, std::void_t<typename C::State, decltype(C::kSize), decltype(C::init()),
67
+ detail::ChecksumUpdate<C>, detail::ChecksumFinalize<C>>>
68
+ : std::bool_constant<detail::kChecksumSignatures<C> && detail::kChecksumSize<C> &&
69
+ detail::kChecksumNoexcept<C>> {};
70
+ /// @endcond
71
+
72
+ /// @brief true when @p C is a checksum algorithm.
73
+ template <typename C> inline constexpr bool is_checksum_v = is_checksum<C>::value;
74
+
75
+ /**
76
+ * @brief Computes a checksum over one contiguous range.
77
+ * @tparam C Checksum algorithm.
78
+ * @param bytes Input data.
79
+ * @return The finalized checksum value.
80
+ */
81
+ template <typename C>
82
+ [[nodiscard]] std::uint64_t compute_checksum(Span<const std::uint8_t> bytes) noexcept {
83
+ static_assert(is_checksum_v<C>, "C must satisfy the checksum contract");
84
+ typename C::State state = C::init();
85
+ C::update(state, bytes);
86
+ return C::finalize(state);
87
+ }
88
+
89
+ /// @cond INTERNAL
90
+ namespace detail {
91
+
92
+ inline constexpr std::uint64_t kCrcTopBit = std::uint64_t{1U} << 63U;
93
+
94
+ // Eight shift/xor steps of a left-aligned (bit 63 = top) CRC register.
95
+ [[nodiscard]] constexpr std::uint64_t crc_step_byte(std::uint64_t reg,
96
+ std::uint64_t aligned_poly) noexcept {
97
+ for (unsigned bit = 0U; bit < 8U; ++bit) {
98
+ reg = (reg & kCrcTopBit) != 0U ? (reg << 1U) ^ aligned_poly : reg << 1U;
99
+ }
100
+ return reg;
101
+ }
102
+
103
+ [[nodiscard]] constexpr std::array<std::uint64_t, 256>
104
+ make_crc_table(std::uint64_t aligned_poly) noexcept {
105
+ std::array<std::uint64_t, 256> table{};
106
+ std::uint64_t index = 0U;
107
+ for (std::uint64_t& entry : table) {
108
+ entry = crc_step_byte(index << 56U, aligned_poly);
109
+ ++index;
110
+ }
111
+ return table;
112
+ }
113
+
114
+ // One read-only table per left-aligned polynomial, shared by every CRC that uses it.
115
+ template <std::uint64_t AlignedPoly>
116
+ inline constexpr std::array<std::uint64_t, 256> kCrcTable = make_crc_table(AlignedPoly);
117
+
118
+ } // namespace detail
119
+ /// @endcond
120
+
121
+ /**
122
+ * @brief Parametric CRC in the Rocksoft model; satisfies the checksum requirements.
123
+ *
124
+ * The parameters are the ones listed in CRC catalogues ("width", "poly", "init", "refin",
125
+ * "refout", "xorout"). The table mode uses a shared 2 KiB constant table per polynomial; the
126
+ * table-less mode is slower and needs no table.
127
+ * @tparam Width CRC width in bits, 1..64.
128
+ * @tparam Poly Generator polynomial, normal (non-reflected) form.
129
+ * @tparam Init Initial register value.
130
+ * @tparam RefIn Reflect each input byte.
131
+ * @tparam RefOut Reflect the final register.
132
+ * @tparam XorOut Value XOR-ed into the result.
133
+ * @tparam UseTable Use the lookup table (default from config::kCrcUseTable).
134
+ */
135
+ template <unsigned Width, std::uint64_t Poly, std::uint64_t Init, bool RefIn, bool RefOut,
136
+ std::uint64_t XorOut, bool UseTable = config::kCrcUseTable>
137
+ class Crc {
138
+ static_assert(Width >= 1U && Width <= kMaxBits, "CRC width must be 1..64");
139
+ static_assert(fits_unsigned(Poly, Width) && fits_unsigned(Init, Width) &&
140
+ fits_unsigned(XorOut, Width),
141
+ "CRC parameters must fit the width");
142
+
143
+ static constexpr unsigned kShift = kMaxBits - Width;
144
+ static constexpr std::uint64_t kAlignedPoly = std::uint64_t{Poly} << kShift;
145
+
146
+ public:
147
+ /// @brief Bytes of the CRC value on the wire.
148
+ static constexpr std::size_t kSize = (Width + 7U) / 8U;
149
+ /// @brief CRC width in bits.
150
+ static constexpr unsigned kWidth = Width;
151
+
152
+ /// @brief Running state of an incremental computation.
153
+ struct State {
154
+ std::uint64_t reg; ///< Register, left-aligned and non-reflected.
155
+ };
156
+
157
+ /// @brief Starts a computation.
158
+ [[nodiscard]] static constexpr State init() noexcept {
159
+ return State{std::uint64_t{Init} << kShift};
160
+ }
161
+
162
+ /**
163
+ * @brief Feeds @p bytes into @p state; may be called any number of times.
164
+ */
165
+ static constexpr void update(State& state, Span<const std::uint8_t> bytes) noexcept {
166
+ for (const std::uint8_t byte : bytes) {
167
+ const std::uint8_t input = RefIn ? static_cast<std::uint8_t>(reflect(byte, 8U)) : byte;
168
+ if constexpr (UseTable) {
169
+ const auto index = static_cast<std::uint8_t>((state.reg >> 56U) ^ input);
170
+ state.reg = (state.reg << 8U) ^ array_at(detail::kCrcTable<kAlignedPoly>, index);
171
+ } else {
172
+ state.reg =
173
+ detail::crc_step_byte(state.reg ^ (std::uint64_t{input} << 56U), kAlignedPoly);
174
+ }
175
+ }
176
+ }
177
+
178
+ /// @brief Returns the CRC of all bytes fed into @p state.
179
+ [[nodiscard]] static constexpr std::uint64_t finalize(const State& state) noexcept {
180
+ const std::uint64_t value = state.reg >> kShift;
181
+ return (RefOut ? reflect(value, Width) : value) ^ XorOut;
182
+ }
183
+
184
+ /// @brief Computes the CRC of @p bytes in one call.
185
+ [[nodiscard]] static constexpr std::uint64_t compute(Span<const std::uint8_t> bytes) noexcept {
186
+ State state = init();
187
+ update(state, bytes);
188
+ return finalize(state);
189
+ }
190
+ };
191
+
192
+ /// @brief CRC-32/ISO-HDLC (the common "CRC-32"); check value 0xCBF43926.
193
+ using Crc32 = Crc<32U, 0x04C11DB7U, 0xFFFFFFFFU, true, true, 0xFFFFFFFFU>;
194
+ /// @brief CRC-16/CCITT-FALSE (CRC-16/IBM-3740); check value 0x29B1.
195
+ using Crc16CcittFalse = Crc<16U, 0x1021U, 0xFFFFU, false, false, 0x0000U>;
196
+
197
+ static_assert(is_checksum_v<Crc32>, "Crc32 must satisfy the checksum contract");
198
+
199
+ } // namespace wireforge
200
+
201
+ #endif // WIREFORGE_CHECKSUM_HPP