|
23 | 23 |
|
24 | 24 | namespace note { |
25 | 25 |
|
| 26 | +/// Optional seed-supplying function for `Notecard::ping()` / |
| 27 | +/// `StaticNotecard::ping()`. The probe's 16-character nonce is derived |
| 28 | +/// from this seed via an internal xorshift32 PRNG; passing `nullptr` |
| 29 | +/// (the default) seeds from the HAL clock (`hal().millis()`), which is |
| 30 | +/// what production callers want. Tests inject a deterministic seed |
| 31 | +/// function so consecutive nonces are predictable. |
| 32 | +using PingSeedFn = uint32_t(*)(); |
| 33 | + |
| 34 | + |
26 | 35 | #if NOTE_SINGLETON |
27 | 36 | namespace detail { |
28 | 37 | /// Shared NcT* (type-erased to void*) for all `request_traits<T>` under |
@@ -704,25 +713,28 @@ class Notecard { |
704 | 713 | /// safe to call at any point in the lifecycle, including before any |
705 | 714 | /// other transaction has run. |
706 | 715 | /// |
| 716 | + /// The 16-character nonce is generated by an xorshift32 PRNG seeded |
| 717 | + /// from `seed_fn()` (or, when null, from the HAL clock). Tests can |
| 718 | + /// inject a deterministic seed function to make consecutive nonces |
| 719 | + /// predictable; production callers should leave `seed_fn` at its |
| 720 | + /// default so consecutive pings carry different nonces. |
| 721 | + /// |
707 | 722 | /// Returns success when the nonce matches; a transport error when |
708 | 723 | /// the transport itself failed; or `Error::Json` when the response |
709 | 724 | /// is missing a `text` field or its `text` value does not match the |
710 | 725 | /// sent nonce. |
711 | | - Result<void> ping(uint32_t timeout_ms = 500) { |
| 726 | + Result<void> ping(uint32_t timeout_ms = 500, PingSeedFn seed_fn = nullptr) { |
712 | 727 | if (!transport_) |
713 | 728 | return make_error(Error::NotReady, NOTE_ERR("no transport configured")); |
714 | 729 |
|
715 | | - if (ping_prng_ == 0) |
716 | | - ping_prng_ = 0x2545F491u ^ hal().millis(); |
| 730 | + uint32_t seed = (seed_fn ? seed_fn() : hal().millis()) ^ 0x2545F491u; |
717 | 731 |
|
718 | 732 | char nonce[16]; |
719 | 733 | for (int i = 0; i < 16; ++i) { |
720 | | - uint32_t x = ping_prng_; |
721 | | - x ^= x << 13; |
722 | | - x ^= x >> 17; |
723 | | - x ^= x << 5; |
724 | | - ping_prng_ = x; |
725 | | - nonce[i] = static_cast<char>('A' + (x % 26)); |
| 734 | + seed ^= seed << 13; |
| 735 | + seed ^= seed >> 17; |
| 736 | + seed ^= seed << 5; |
| 737 | + nonce[i] = static_cast<char>('A' + (seed % 26)); |
726 | 738 | } |
727 | 739 |
|
728 | 740 | // Hand-build the request so we do not pull in snprintf on AVR. |
@@ -1313,11 +1325,6 @@ class Notecard { |
1313 | 1325 | TransactionTiming timing_{}; |
1314 | 1326 | uint32_t next_request_id_ = 1; |
1315 | 1327 | bool request_ids_enabled_ = true; |
1316 | | - // xorshift32 state for the ping() nonce generator. Zero means |
1317 | | - // "uninitialised"; ping() seeds it lazily from the host clock on |
1318 | | - // first use so two Notecards constructed in quick succession do |
1319 | | - // not share a sequence. |
1320 | | - uint32_t ping_prng_ = 0; |
1321 | 1328 | #if !NOTE_NO_MD5 |
1322 | 1329 | // Static, shared across all Notecards: avoids a self-reference inside |
1323 | 1330 | // Notecard (pointer to own member), which broke every move-assignment |
|
0 commit comments