[READ-ONLY] Mirror of https://github.com/jmrplens/kleidos. Kleidos — Hardware BLE password manager for ESP32-S3 (M5StickC Plus2, StickS3, M5Stack Gray, T-Deck, Cardputer) jmrplens.github.io/kleidos/
0

Configure Feed

Select the types of activity you want to include in your feed.

Fix branch-clone, rounding, cast, and remaining long-tail findings

- bugprone-branch-clone (12): fold genuinely identical switch arms
(typing status map, PM1 power levels, wake-cause names, glyph
fallback), merge the duplicated small-panel atlas ladder, rebuild
scaleFor as per-panel constexpr scale tables (lookup-table idiom),
restructure the modal confirm poll around explicit hold/press
predicates (also clears the DeMorgan finding), and NOLINT the one
per-SoC gated ADC calibration switch with rationale.
- bugprone-casting-through-void (13): use direct reinterpret_cast (the
documented pro-type-reinterpret-cast deviation) for byte/sockaddr
views; keep the SPI slot two-step through void* with rationale (it
exists to satisfy -Werror=cast-align next to the static_asserts).
- bugprone-incorrect-roundings (9): std::lroundf instead of +0.5 casts.
- cert-dcl50-cpp (5): rationale NOLINTs on the printf-style formatters
(a parameter pack cannot produce the va_list vsnprintf needs).
- cppcoreguidelines-pro-type-const-cast: const-correct the WordReader
ctx (API + trampoline + tests, const_cast deleted); NOLINT the two
std <cstring>-convention mutable overloads.
- Long tail: explicit void* casts + sizeof(T) rationale in rtos_queue,
range-for loop conversions, 0x80-mask instead of int8_t reinterpret
for the AXP2101 gauge, uint8_t slot subscript, parenthesized -1 pin
macros, extended the __wrap_log_printf ABI suppression to the
reserved-identifier aliases, integer-center tween targets.

Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4

José M. Requena Plens (Jul 10, 2026, 8:11 AM +0200) 69938c3b 9c5cf5ed

+147 -181
+1 -1
src/ble/ble_facade.cpp
··· 81 81 */ 82 82 TypingStatus mapTypingStatus( BleTypingStatus s ) { 83 83 switch ( s ) { 84 + // IDLE means "not started yet", which the public API reports as Pending. 84 85 case BleTypingStatus::IDLE: 85 - return TypingStatus::Pending; 86 86 case BleTypingStatus::PENDING: 87 87 return TypingStatus::Pending; 88 88 case BleTypingStatus::RUNNING:
+2 -1
src/ble/bond/name_store_task.cpp
··· 211 211 return false; 212 212 } 213 213 214 - SyncSlot& slot = s_syncSlots[static_cast<size_t>( slotIndex )]; 214 + SyncSlot& slot = 215 + s_syncSlots[cmd.slot]; // slotIndex checked >= 0 above; cmd.slot is its uint8_t value 215 216 if ( !rtos::takeSemaphore( slot.done, rtos::milliseconds( timeoutMs ) ) ) { 216 217 bool completedAtDeadline = false; 217 218 bool lateResult = false;
+5 -5
src/ble/stack/ble_stack_task.cpp
··· 169 169 publishStateChange( mapRuntimeState( BleHidManager::getState() ) ); 170 170 break; 171 171 case CancelTyping: 172 - // Body cannot be interrupted yet (B4 will introduce a 173 - // cooperative cancel). cancelTyping() on the FSM side 174 - // already invalidated the token, so any in-flight body 175 - // returns through the stale-token check. 176 - break; 172 + // Deliberate no-op, folded into the catch-all below: the typing 173 + // body cannot be interrupted yet (B4 will introduce a cooperative 174 + // cancel). cancelTyping() on the FSM side already invalidated the 175 + // token, so any in-flight body returns through the stale-token 176 + // check. 177 177 default: 178 178 // Typing / SetTargetHost / SetLayout / RenameBond not yet 179 179 // routed via the queue. Counter on
+4 -4
src/ble/stack/nimble_host_raw.cpp
··· 659 659 } 660 660 661 661 ble_hs_adv_fields rsp{}; 662 - rsp.name = static_cast<const uint8_t*>( static_cast<const void*>( s_deviceName.data() ) ); 663 - rsp.name_len = static_cast<uint8_t>( kleidos::platform::str::length( s_deviceName ) ); 664 - rsp.name_is_complete = 1; 665 - rsp.appearance = kHidKeyboardAppearance; 662 + rsp.name = reinterpret_cast<const uint8_t*>( s_deviceName.data() ); 663 + rsp.name_len = static_cast<uint8_t>( kleidos::platform::str::length( s_deviceName ) ); 664 + rsp.name_is_complete = 1; 665 + rsp.appearance = kHidKeyboardAppearance; 666 666 rsp.appearance_is_present = 1; 667 667 if ( const int rc = ble_gap_adv_rsp_set_fields( &rsp ); rc != 0 ) { 668 668 KLEIDOS_LOGE( kTag, "adv_rsp_set_fields failed: %d", rc );
+1 -1
src/boards/board_gray_log_wrap.cpp
··· 17 17 #include <stdio.h> 18 18 19 19 /// Pass-through linker stub that forwards to vprintf so the wrapped symbol resolves. 20 - // NOLINTNEXTLINE(readability-identifier-naming) -- the __wrap_ prefix is the linker --wrap ABI contract 20 + // NOLINTNEXTLINE(readability-identifier-naming,bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) -- the __wrap_ prefix is the linker --wrap ABI contract 21 21 extern "C" int __wrap_log_printf( const char* fmt, ... ) { 22 22 va_list ap{}; 23 23 va_start( ap, fmt );
+3 -3
src/crypto/export_envelope.cpp
··· 43 43 bool deriveSubKey( const std::array<uint8_t, kAesKeySize>& master, const char* label, 44 44 std::array<uint8_t, kAesKeySize>& out ) { 45 45 const size_t labelLen = std::strlen( label ); 46 - return VaultCrypto::hmacSha256( 47 - master.data(), master.size(), 48 - static_cast<const uint8_t*>( static_cast<const void*>( label ) ), labelLen, out.data() ); 46 + return VaultCrypto::hmacSha256( master.data(), master.size(), 47 + reinterpret_cast<const uint8_t*>( label ), labelLen, 48 + out.data() ); 49 49 } 50 50 51 51 /**
+1 -2
src/crypto/vault_crypto.cpp
··· 791 791 std::array<uint32_t, kShaDigestWords> digest{}; 792 792 std::array<uint8_t, kShaDigest> u1{}; 793 793 std::array<uint8_t, kShaDigest> t{}; 794 - const auto* digestBytes = 795 - static_cast<const uint8_t*>( static_cast<const void*>( digest.data() ) ); 794 + const auto* digestBytes = reinterpret_cast<const uint8_t*>( digest.data() ); 796 795 797 796 esp_sha_acquire_hardware(); 798 797 esp_sha_set_mode( SHA2_256 );
+2 -2
src/drivers/introspection/regmap_registry.cpp
··· 56 56 } 57 57 58 58 void resetForTest() { 59 - for ( size_t i = 0; i < kMaxRegisteredChips; ++i ) { 60 - s_chips[i] = nullptr; 59 + for ( const ChipRegmap*& chip : s_chips ) { 60 + chip = nullptr; 61 61 } 62 62 s_count = 0; 63 63 }
+4 -3
src/drivers/power/axp2101.cpp
··· 70 70 KLEIDOS_LOGW( kTag, "battery level read failed" ); 71 71 return false; 72 72 } 73 - const int signedLevel = static_cast<int8_t>( value ); 74 - if ( signedLevel < 0 ) { 73 + // The gauge reports 0x80..0xFF (sign bit set) while it has no valid 74 + // estimate yet; treat those as "no reading" instead of a huge percentage. 75 + if ( ( value & 0x80U ) != 0U ) { 75 76 return false; 76 77 } 77 - percent = detail::clampPercent( signedLevel ); 78 + percent = detail::clampPercent( static_cast<int>( value ) ); 78 79 return true; 79 80 } 80 81
+1
src/hal/common/display_hal_tft.cpp
··· 199 199 print( buffer.c_str() ); 200 200 } 201 201 202 + // NOLINTNEXTLINE(cert-dcl50-cpp) -- C-variadic by necessity: forwards a va_list to vsnprintf; a parameter pack cannot produce one 202 203 void printf( const char* fmt, ... ) { 203 204 str::InplaceString<127> buffer; 204 205 va_list args{};
+12 -12
src/hal/common/platform_defaults.h
··· 264 264 #define AUDIO_USE_DAC 0 265 265 #endif // AUDIO_USE_DAC 266 266 #ifndef PIN_AUDIO_MCLK 267 - #define PIN_AUDIO_MCLK -1 267 + #define PIN_AUDIO_MCLK ( -1 ) 268 268 #endif // PIN_AUDIO_MCLK 269 269 #ifndef PIN_AUDIO_BCLK 270 - #define PIN_AUDIO_BCLK -1 270 + #define PIN_AUDIO_BCLK ( -1 ) 271 271 #endif // PIN_AUDIO_BCLK 272 272 #ifndef PIN_AUDIO_WS 273 - #define PIN_AUDIO_WS -1 273 + #define PIN_AUDIO_WS ( -1 ) 274 274 #endif // PIN_AUDIO_WS 275 275 #ifndef PIN_AUDIO_DOUT 276 - #define PIN_AUDIO_DOUT -1 276 + #define PIN_AUDIO_DOUT ( -1 ) 277 277 #endif // PIN_AUDIO_DOUT 278 278 #ifndef PIN_AUDIO_DIN 279 279 #define PIN_AUDIO_DIN ( ( ( ( -1 ) ) ) ) ··· 409 409 #define BAT_ADC_RATIO_DENOMINATOR 1 410 410 #endif // BAT_ADC_RATIO_DENOMINATOR 411 411 #ifndef PIN_I2C_SDA 412 - #define PIN_I2C_SDA -1 412 + #define PIN_I2C_SDA ( -1 ) 413 413 #endif // PIN_I2C_SDA 414 414 #ifndef PIN_I2C_SCL 415 - #define PIN_I2C_SCL -1 415 + #define PIN_I2C_SCL ( -1 ) 416 416 #endif // PIN_I2C_SCL 417 417 #ifndef PIN_I2C_PORT 418 - #define PIN_I2C_PORT -1 418 + #define PIN_I2C_PORT ( -1 ) 419 419 #endif // PIN_I2C_PORT 420 420 #ifndef PIN_KEYBOARD_INT 421 421 #define PIN_KEYBOARD_INT ( ( ( ( -1 ) ) ) ) ··· 463 463 #define PIN_TFT_DC ( ( ( ( -1 ) ) ) ) 464 464 #endif // PIN_TFT_DC 465 465 #ifndef PIN_DISPLAY_SCK 466 - #define PIN_DISPLAY_SCK -1 466 + #define PIN_DISPLAY_SCK ( -1 ) 467 467 #endif // PIN_DISPLAY_SCK 468 468 #ifndef PIN_DISPLAY_MISO 469 - #define PIN_DISPLAY_MISO -1 469 + #define PIN_DISPLAY_MISO ( -1 ) 470 470 #endif // PIN_DISPLAY_MISO 471 471 #ifndef PIN_DISPLAY_MOSI 472 - #define PIN_DISPLAY_MOSI -1 472 + #define PIN_DISPLAY_MOSI ( -1 ) 473 473 #endif // PIN_DISPLAY_MOSI 474 474 #ifndef PIN_DISPLAY_CS 475 475 #define PIN_DISPLAY_CS PIN_TFT_CS ··· 478 478 #define PIN_DISPLAY_DC PIN_TFT_DC 479 479 #endif // PIN_DISPLAY_DC 480 480 #ifndef PIN_DISPLAY_RST 481 - #define PIN_DISPLAY_RST -1 481 + #define PIN_DISPLAY_RST ( -1 ) 482 482 #endif // PIN_DISPLAY_RST 483 483 #ifndef PIN_DISPLAY_BUSY 484 - #define PIN_DISPLAY_BUSY -1 484 + #define PIN_DISPLAY_BUSY ( -1 ) 485 485 #endif // PIN_DISPLAY_BUSY 486 486 #ifndef PIN_DISPLAY_BL 487 487 #define PIN_DISPLAY_BL PIN_TFT_BL
+2 -4
src/hal/common/power_manager_m5_pm1.cpp
··· 245 245 s_pm1.setLdoPowerHold( false ); 246 246 break; 247 247 case 1: 248 - // L1: IMU powered. 249 - s_pm1.setLdoEnable( true ); 250 - break; 251 248 case 2: 252 249 default: 253 - // L2/L3: keep default active rails enabled. 250 + // L1 (IMU powered) and L2/L3 (default active rails) all keep the 251 + // LDO enabled; only shutdown (L0) releases the power hold. 254 252 s_pm1.setLdoEnable( true ); 255 253 break; 256 254 }
+23 -77
src/hal/display/text_renderer.cpp
··· 22 22 #endif // KLEIDOS_FONT_SANS && !DISPLAY_MONOCHROME 23 23 24 24 #include <array> 25 + #include <cmath> 25 26 #include <cstddef> 26 27 #include <cstdint> 27 28 ··· 39 40 * @return Integer scale factor (always >= 1). 40 41 */ 41 42 int32_t scaleFor( Font font, float textSize ) { 42 - int32_t baseScale = 1; 43 - if constexpr ( device::kScreenW <= 160 && device::kScreenH >= 220 ) { 44 - using enum Font; 45 - switch ( font ) { 46 - case Font::SMALL: 47 - baseScale = 1; 48 - break; 49 - case Font::BODY: 50 - baseScale = 1; 51 - break; 52 - case Font::HEADING: 53 - baseScale = 2; 54 - break; 55 - case Font::LARGE: 56 - baseScale = 5; 57 - break; 58 - case Font::MONO: 59 - baseScale = 1; 60 - break; 61 - } 62 - } else if constexpr ( device::kScreenW <= 260 && device::kScreenH <= 160 ) { 63 - switch ( font ) { 64 - case Font::SMALL: 65 - baseScale = 1; 66 - break; 67 - case Font::BODY: 68 - baseScale = 1; 69 - break; 70 - case Font::HEADING: 71 - baseScale = 2; 72 - break; 73 - case Font::LARGE: 74 - baseScale = 4; 75 - break; 76 - case Font::MONO: 77 - baseScale = 1; 78 - break; 79 - } 80 - } else { 81 - switch ( font ) { 82 - case Font::SMALL: 83 - baseScale = 1; 84 - break; 85 - case Font::BODY: 86 - baseScale = 2; 87 - break; 88 - case Font::HEADING: 89 - baseScale = 3; 90 - break; 91 - case Font::LARGE: 92 - baseScale = 5; 93 - break; 94 - case Font::MONO: 95 - baseScale = 2; 96 - break; 97 - } 98 - } 99 - if ( textSize > 1.75f ) { 100 - ++baseScale; 101 - } else if ( textSize > 1.15f && font != Font::LARGE ) { 43 + // Per-role base scale ladders, indexed by Font enumerator order 44 + // (SMALL, BODY, HEADING, LARGE, MONO). One ladder per panel class. 45 + using ScaleLadder = std::array<int32_t, 5>; 46 + constexpr ScaleLadder kStickLadder = { 1, 1, 2, 5, 1 }; // portrait <=160 wide 47 + constexpr ScaleLadder kCardputerLadder = { 1, 1, 2, 4, 1 }; // landscape <=260x160 48 + constexpr ScaleLadder kLargePanelLadder = { 1, 2, 3, 5, 2 }; // 320x240 class 49 + 50 + constexpr ScaleLadder kLadder = 51 + ( device::kScreenW <= 160 && device::kScreenH >= 220 ) 52 + ? kStickLadder 53 + : ( ( device::kScreenW <= 260 && device::kScreenH <= 160 ) ? kCardputerLadder 54 + : kLargePanelLadder ); 55 + 56 + const auto roleIndex = static_cast<size_t>( font ); 57 + int32_t baseScale = roleIndex < kLadder.size() ? kLadder[roleIndex] : 1; 58 + if ( textSize > 1.75f || ( textSize > 1.15f && font != Font::LARGE ) ) { 102 59 ++baseScale; 103 60 } 104 61 return baseScale < 1 ? 1 : baseScale; ··· 133 90 int32_t fixedScale( float textSize ) { 134 91 constexpr float kMinimumScale = 0.25f; 135 92 const float clamped = textSize < kMinimumScale ? kMinimumScale : textSize; 136 - return static_cast<int32_t>( clamped * static_cast<float>( kFixedOne ) + 0.5f ); 93 + return static_cast<int32_t>( std::lroundf( clamped * static_cast<float>( kFixedOne ) ) ); 137 94 } 138 95 139 96 int32_t scaledFloor( int32_t value, int32_t scale ) { ··· 329 286 * @return Pointer to a static `AaFont` atlas, or `nullptr` when unavailable. 330 287 */ 331 288 const AaFont* aaFontFor( Font font ) { 332 - if constexpr ( device::kScreenW <= 260 && device::kScreenH <= 160 ) { 333 - switch ( font ) { 334 - case Font::SMALL: 335 - return aaSans10(); 336 - case Font::BODY: 337 - return aaSans12(); 338 - case Font::HEADING: 339 - return aaSans16(); 340 - case Font::LARGE: 341 - return aaSans24(); 342 - case Font::MONO: 343 - return aaMono12(); 344 - } 345 - } else if constexpr ( device::kScreenW <= 160 && device::kScreenH >= 220 ) { 289 + // Cardputer landscape (<=260x160) and Stick portrait (<=160 wide) panels 290 + // share the small atlas ladder; every larger panel uses the big ladder. 291 + if constexpr ( ( device::kScreenW <= 260 && device::kScreenH <= 160 ) 292 + || ( device::kScreenW <= 160 && device::kScreenH >= 220 ) ) { 346 293 switch ( font ) { 347 294 case Font::SMALL: 348 295 return aaSans10(); ··· 455 402 return roleAtlas; 456 403 } 457 404 458 - const auto targetHeight = static_cast<int32_t>( targetHeightF + 0.5F ); 405 + const auto targetHeight = static_cast<int32_t>( std::lroundf( targetHeightF ) ); 459 406 const AaFont* chosen = nearestBakedInFamily( role, roleAtlas, targetHeight ); 460 407 outResidual = targetHeightF / static_cast<float>( chosen->lineHeight ); 461 408 return chosen; ··· 950 897 case '\'': 951 898 return kApostrophe.data(); 952 899 case '?': 953 - return kUnknown.data(); 954 900 default: 955 901 return kUnknown.data(); 956 902 }
+6
src/platform/adc.cpp
··· 141 141 } 142 142 143 143 using enum CalibrationScheme; 144 + // NOLINTBEGIN(bugprone-branch-clone): the branches are distinct per SoC — 145 + // each body is emptied by its ADC_CALI_SCHEME_*_SUPPORTED gate on chips 146 + // lacking that scheme (S3 has curve fitting only, classic ESP32 line 147 + // fitting only), so on any single target one branch coincidentally equals 148 + // the empty None branch. 144 149 switch ( state.calibrationScheme ) { 145 150 case CurveFitting: 146 151 #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED ··· 155 160 case CalibrationScheme::None: 156 161 break; 157 162 } 163 + // NOLINTEND(bugprone-branch-clone) 158 164 159 165 state.calibration = nullptr; 160 166 state.calibrationScheme = CalibrationScheme::None;
+1 -2
src/platform/http_server.cpp
··· 272 272 273 273 sockaddr_in peer = {}; 274 274 if ( socklen_t peerLen = sizeof( peer ); 275 - getpeername( socketFd, static_cast<sockaddr*>( static_cast<void*>( &peer ) ), &peerLen ) 276 - != 0 ) { 275 + getpeername( socketFd, reinterpret_cast<sockaddr*>( &peer ), &peerLen ) != 0 ) { 277 276 return 0; 278 277 } 279 278 return peer.sin_addr.s_addr;
+1
src/platform/inplace_string.h
··· 114 114 * @param fmt `printf` format string; writes an empty string if nullptr. 115 115 * @return Number of characters written, excluding the terminating NUL. 116 116 */ 117 + // NOLINTNEXTLINE(cert-dcl50-cpp) -- C-variadic by necessity: forwards a va_list to vsnprintf; a parameter pack cannot produce one 117 118 size_t format( const char* fmt, ... ) __attribute__( ( __format__( __printf__, 2, 3 ) ) ) { 118 119 va_list args{}; 119 120 va_start( args, fmt );
+7 -4
src/platform/rtos_queue.h
··· 144 144 inline bool queueSend( QueueHandle handle, const T& item, Ticks waitTicks = noWait() ) { 145 145 static_assert( std::is_trivially_copyable_v<T>, 146 146 "FreeRTOS queues copy bytes; use pointers for non-trivial types" ); 147 - return queueSendRaw( handle, &item, waitTicks ); 147 + return queueSendRaw( handle, static_cast<const void*>( &item ), waitTicks ); 148 148 } 149 149 150 150 /** ··· 160 160 inline bool queueSendFromIsr( QueueHandle handle, const T& item, IsrYield& yield ) { 161 161 static_assert( std::is_trivially_copyable_v<T>, 162 162 "FreeRTOS queues copy bytes; use pointers for non-trivial types" ); 163 - return queueSendFromIsrRaw( handle, &item, yield ); 163 + return queueSendFromIsrRaw( handle, static_cast<const void*>( &item ), yield ); 164 164 } 165 165 166 166 /** ··· 176 176 inline bool queueReceive( QueueHandle handle, T& out, Ticks waitTicks = waitForever() ) { 177 177 static_assert( std::is_trivially_copyable_v<T>, 178 178 "FreeRTOS queues copy bytes; use pointers for non-trivial types" ); 179 - return queueReceiveRaw( handle, &out, waitTicks ); 179 + return queueReceiveRaw( handle, static_cast<void*>( &out ), waitTicks ); 180 180 } 181 181 182 182 /** ··· 192 192 inline bool queueReceiveFromIsr( QueueHandle handle, T& out, IsrYield& yield ) { 193 193 static_assert( std::is_trivially_copyable_v<T>, 194 194 "FreeRTOS queues copy bytes; use pointers for non-trivial types" ); 195 - return queueReceiveFromIsrRaw( handle, &out, yield ); 195 + return queueReceiveFromIsrRaw( handle, static_cast<void*>( &out ), yield ); 196 196 } 197 197 198 198 /** ··· 235 235 */ 236 236 bool create( QueueDepth depth ) { 237 237 reset(); 238 + // When T is itself a pointer type the queue stores the pointer value, 239 + // so sizeof(T) == sizeof(pointer) is the intended element size. 240 + // NOLINTNEXTLINE(bugprone-sizeof-expression) 238 241 handle_ = createQueueRaw( depth, sizeof( T ) ); 239 242 return handle_ != nullptr; 240 243 }
+1
src/platform/safe_string.cpp
··· 121 121 122 122 } // namespace 123 123 124 + // NOLINTNEXTLINE(cert-dcl50-cpp) -- C-variadic by necessity: forwards a va_list to vsnprintf; a parameter pack cannot produce one 124 125 size_t format( char* dest, size_t destSize, const char* fmt, ... ) { 125 126 va_list args{}; 126 127 va_start( args, fmt );
+2
src/platform/safe_string.h
··· 344 344 * @return Pointer to the first match, or nullptr if not found / @p str is null. 345 345 */ 346 346 inline char* findChar( char* str, char ch ) { 347 + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) -- re-applies the caller's own mutability (std <cstring> overload convention) 347 348 return const_cast<char*>( findChar( static_cast<const char*>( str ), ch ) ); 348 349 } 349 350 ··· 358 359 * @return Pointer to the last match, or nullptr if not found / @p str is null. 359 360 */ 360 361 inline char* findLastChar( char* str, char ch ) { 362 + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) -- re-applies the caller's own mutability (std <cstring> overload convention) 361 363 return const_cast<char*>( findLastChar( static_cast<const char*>( str ), ch ) ); 362 364 } 363 365
+1
src/platform/serial_transport.cpp
··· 306 306 return written + write( "\n", 1 ); 307 307 } 308 308 309 + // NOLINTNEXTLINE(cert-dcl50-cpp) -- C-variadic by necessity: forwards a va_list to vsnprintf; a parameter pack cannot produce one 309 310 int printf( const char* format, ... ) { 310 311 if ( format == nullptr ) { 311 312 return 0;
-1
src/platform/sleep.cpp
··· 166 166 case CoProcessor: 167 167 return "Co-processor"; 168 168 case WakeCause::Other: 169 - return "Unknown"; 170 169 default: 171 170 return "Unknown"; 172 171 }
+6 -1
src/platform/spi.cpp
··· 273 273 274 274 namespace { 275 275 spi_transaction_t* nativeTransaction( QueuedTransaction& transaction ) { 276 + // Two-step cast through void* on purpose: storage.data() is statically an 277 + // unsigned char* (alignment 1), so a direct reinterpret_cast trips 278 + // -Werror=cast-align even though the static_asserts above guarantee the 279 + // slot is large and aligned enough for spi_transaction_t. 280 + // NOLINTNEXTLINE(bugprone-casting-through-void) 276 281 return static_cast<spi_transaction_t*>( static_cast<void*>( transaction.storage.data() ) ); 277 282 } 278 283 } // namespace ··· 332 337 } 333 338 // Recover the owning slot: `storage` is the first member, so the native 334 339 // descriptor pointer is the slot pointer. 335 - completed = static_cast<QueuedTransaction*>( static_cast<void*>( native ) ); 340 + completed = reinterpret_cast<QueuedTransaction*>( native ); 336 341 completed->inFlight = false; 337 342 #else 338 343 (void)device;
+5 -9
src/platform/udp_socket.cpp
··· 102 102 bindAddress.sin_family = AF_INET; 103 103 bindAddress.sin_addr.s_addr = htonl( INADDR_ANY ); 104 104 bindAddress.sin_port = htons( port ); 105 - if ( bind( socketFd, static_cast<const sockaddr*>( static_cast<const void*>( &bindAddress ) ), 106 - sizeof( bindAddress ) ) 105 + if ( bind( socketFd, reinterpret_cast<const sockaddr*>( &bindAddress ), sizeof( bindAddress ) ) 107 106 < 0 ) { 108 107 lastError_ = errno; 109 108 return false; ··· 122 121 123 122 sockaddr_storage sourceAddress{}; 124 123 socklen_t sourceLength = sizeof( sourceAddress ); 125 - const int received = 126 - recvfrom( socketFd, buffer, length, 0, 127 - static_cast<sockaddr*>( static_cast<void*>( &sourceAddress ) ), &sourceLength ); 124 + const int received = recvfrom( socketFd, buffer, length, 0, 125 + reinterpret_cast<sockaddr*>( &sourceAddress ), &sourceLength ); 128 126 if ( received < 0 ) { 129 127 const int error = errno; 130 128 result.error = error; ··· 136 134 result.status = ReceiveStatus::Data; 137 135 result.bytes = received; 138 136 if ( sourceAddress.ss_family == AF_INET && sourceLength >= sizeof( sockaddr_in ) ) { 139 - const auto* remote = 140 - static_cast<const sockaddr_in*>( static_cast<const void*>( &sourceAddress ) ); 137 + const auto* remote = reinterpret_cast<const sockaddr_in*>( &sourceAddress ); 141 138 result.remote.address = remote->sin_addr.s_addr; 142 139 result.remote.port = ntohs( remote->sin_port ); 143 140 } ··· 156 153 remoteAddress.sin_addr.s_addr = remote.address; 157 154 remoteAddress.sin_port = htons( remote.port ); 158 155 if ( const int sent = 159 - sendto( socketFd, data, length, 0, 160 - static_cast<const sockaddr*>( static_cast<const void*>( &remoteAddress ) ), 156 + sendto( socketFd, data, length, 0, reinterpret_cast<const sockaddr*>( &remoteAddress ), 161 157 sizeof( remoteAddress ) ); 162 158 sent < 0 || static_cast<size_t>( sent ) != length ) { 163 159 lastError_ = sent < 0 ? errno : EMSGSIZE;
+2
src/platform/wifi_access_point.cpp
··· 260 260 if ( apNetif_ != nullptr ) { 261 261 esp_netif_ip_info_t ipInfo{}; 262 262 if ( esp_netif_get_ip_info( toNetif( apNetif_ ), &ipInfo ) == ESP_OK ) { 263 + // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast) -- casts live inside ESP-IDF's IP2STR macro 263 264 str::format( output.ipAddress.data(), sizeof( output.ipAddress ), IPSTR, 264 265 IP2STR( &ipInfo.ip ) ); 266 + // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast) 265 267 } 266 268 } 267 269
+4
src/platform/wifi_station.cpp
··· 199 199 if ( staNetif_ != nullptr ) { 200 200 esp_netif_ip_info_t ipInfo{}; 201 201 if ( esp_netif_get_ip_info( toNetif( staNetif_ ), &ipInfo ) == ESP_OK ) { 202 + // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast) -- casts live inside ESP-IDF's IP2STR macro 202 203 str::format( output.ipAddress.data(), sizeof( output.ipAddress ), IPSTR, 203 204 IP2STR( &ipInfo.ip ) ); 205 + // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast) 204 206 } 205 207 } 206 208 output.connected = hasIp_; ··· 261 263 262 264 if ( const auto* event = static_cast<const ip_event_got_ip_t*>( eventData ); 263 265 event != nullptr ) { 266 + // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast) -- casts live inside ESP-IDF's IP2STR macro 264 267 str::format( self->ipAddress_.data(), sizeof( self->ipAddress_ ), IPSTR, 265 268 IP2STR( &event->ip_info.ip ) ); 269 + // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast) 266 270 } 267 271 self->hasIp_ = true; 268 272 if ( self->ipReadySemaphore_.has_value() ) {
+9 -4
src/states/char_grid_editor_controller.cpp
··· 17 17 #include "ui/core/theme.h" 18 18 #include "ui/render/raster_canvas.h" 19 19 20 + #include <cmath> 20 21 #include <optional> 21 22 #include <utility> 22 23 ··· 92 93 if ( boxAnimActive_ ) { 93 94 const uint32_t now = platformClock::millis32(); 94 95 props.useAnimBox = true; 95 - props.animBoxCx = static_cast<int16_t>( boxX_.value( now ) + 0.5f ); 96 - props.animBoxCy = static_cast<int16_t>( boxY_.value( now ) + 0.5f ); 96 + props.animBoxCx = static_cast<int16_t>( std::lroundf( boxX_.value( now ) ) ); 97 + props.animBoxCy = static_cast<int16_t>( std::lroundf( boxY_.value( now ) ) ); 97 98 } 98 99 props.atMax = atMax; 99 100 props.flashMax = ( flashMaxMs_ != 0 ); ··· 381 382 boxAnimActive_ = false; // no box to slide (Row phase / no focus) 382 383 return; 383 384 } 384 - const auto targetCx = static_cast<float>( box.x + box.w / 2 ); 385 - const auto targetCy = static_cast<float>( box.y + box.h / 2 ); 385 + // Integer pixel center on purpose: it matches the un-animated focus box 386 + // grid math, so the slide lands exactly on the static box position. 387 + const int32_t centerX = box.x + ( box.w / 2 ); 388 + const int32_t centerY = box.y + ( box.h / 2 ); 389 + const auto targetCx = static_cast<float>( centerX ); 390 + const auto targetCy = static_cast<float>( centerY ); 386 391 387 392 // Slide only for a cell→cell move on a color panel; mono / e-paper and band 388 393 // changes snap (Tween::snap → instant, no ghosting).
+2 -1
src/states/stick_vault_view.cpp
··· 23 23 #include "ui/render/raster_canvas.h" 24 24 #include "ui/widgets/clock_chip.h" 25 25 26 + #include <cmath> 26 27 #include <utility> 27 28 28 29 namespace kble = kleidos::ble; ··· 512 513 513 514 // Active pill: the only bright element — "you are here". Drawn at the tween's 514 515 // current left edge so a page flip slides it between segments. 515 - const auto pillX = static_cast<int16_t>( tabPillX_.value( nowMs ) + 0.5f ); 516 + const auto pillX = static_cast<int16_t>( std::lroundf( tabPillX_.value( nowMs ) ) ); 516 517 const ui::Rect pill{ pillX, trackY, geo.pillW, ui::kTabTrackH }; 517 518 #ifdef ESP_PLATFORM 518 519 if ( ui::aa::available() ) {
+2 -1
src/ui/components/char_wheel_component.cpp
··· 13 13 14 14 #include <algorithm> 15 15 #include <array> 16 + #include <cmath> 16 17 #include <cstdint> 17 18 #include <utility> 18 19 ··· 429 430 props.useRingOffset ? props.ringOffset : static_cast<float>( props.wheelIndex ); 430 431 g.centerIdx = static_cast<int32_t>( centerF >= 0.0f ? centerF + 0.5f : centerF - 0.5f ); 431 432 g.fracCells = centerF - static_cast<float>( g.centerIdx ); 432 - g.shiftPx = static_cast<int16_t>( g.fracCells * static_cast<float>( cellW ) + 0.5f ); 433 + g.shiftPx = static_cast<int16_t>( std::lroundf( g.fracCells * static_cast<float>( cellW ) ) ); 433 434 434 435 // The centered selection box: the mockup's headline box — a translucent navy 435 436 // fill framed by an equal-weight cyan AA border with clearly-rounded corners.
+7 -7
src/ui/core/gradient.cpp
··· 49 49 if ( clamped > 1.0F ) { 50 50 clamped = 1.0F; 51 51 } 52 - return static_cast<uint32_t>( clamped * maxCode + 0.5F ); 52 + return static_cast<uint32_t>( std::lroundf( clamped * maxCode ) ); 53 53 } 54 54 55 55 uint16_t toRgb565( const Srgb& s ) { ··· 207 207 buildOklabLut( stops.data(), stops.size(), out.data(), out.size() ); 208 208 return out; 209 209 }(); 210 - const float t = clamp01( fraction ); 211 - const auto idx = 212 - static_cast<std::size_t>( t * static_cast<float>( kSelectionBandEntries - 1 ) + 0.5F ); 210 + const float t = clamp01( fraction ); 211 + const auto idx = static_cast<std::size_t>( 212 + std::lroundf( t * static_cast<float>( kSelectionBandEntries - 1 ) ) ); 213 213 return s_lut[idx < kSelectionBandEntries ? idx : kSelectionBandEntries - 1]; 214 214 } 215 215 ··· 227 227 buildOklabLut( stops.data(), stops.size(), out.data(), out.size() ); 228 228 return out; 229 229 }(); 230 - const float t = clamp01( fraction ); 231 - const auto idx = 232 - static_cast<std::size_t>( t * static_cast<float>( kHeaderDepthEntries - 1 ) + 0.5F ); 230 + const float t = clamp01( fraction ); 231 + const auto idx = static_cast<std::size_t>( 232 + std::lroundf( t * static_cast<float>( kHeaderDepthEntries - 1 ) ) ); 233 233 return s_lut[idx < kHeaderDepthEntries ? idx : kHeaderDepthEntries - 1]; 234 234 } 235 235
+11 -13
src/ui/popup/popup_system.cpp
··· 1319 1319 // Single exit: poll every input source, then decide whether to 1320 1320 // resolve the modal. Each subpath returns a tri-state ('PENDING' 1321 1321 // means "no decision this iteration"); only one `break` follows. 1322 + // processHoldToConfirmStep already calls resolveModal itself when the 1323 + // sustained hold completes, so that arm must not resolve again below. 1324 + const bool holdConfirmed = 1325 + holdToConfirm && btnConfirm 1326 + && processHoldToConfirmStep( btnConfirm, holdToConfirm, holdState, kHoldThresholdMs ); 1327 + const bool pressConfirmed = !holdToConfirm && btnConfirm && btnConfirm->wasPressed(); 1328 + 1322 1329 ConfirmResult decision = ConfirmResult::PENDING; 1323 - if ( holdToConfirm && btnConfirm 1324 - && processHoldToConfirmStep( btnConfirm, holdToConfirm, holdState, 1325 - kHoldThresholdMs ) ) { 1326 - // processHoldToConfirmStep already resolved the modal; signal 1327 - // the loop to exit by reporting CONFIRM (already resolved). 1328 - decision = ConfirmResult::CONFIRM; 1329 - } else if ( !holdToConfirm && btnConfirm && btnConfirm->wasPressed() ) { 1330 + if ( holdConfirmed || pressConfirmed ) { 1330 1331 decision = ConfirmResult::CONFIRM; 1331 - } 1332 - 1333 - if ( decision == ConfirmResult::PENDING && btnCancel && btnCancel->wasPressed() ) { 1332 + } else if ( btnCancel && btnCancel->wasPressed() ) { 1334 1333 decision = ConfirmResult::CANCEL; 1335 1334 } 1336 1335 1337 1336 if ( decision != ConfirmResult::PENDING ) { 1338 - // processHoldToConfirmStep already called resolveModal on the 1339 - // CONFIRM arm; only resolve here for the other arms. 1340 - if ( !( holdToConfirm && btnConfirm && decision == ConfirmResult::CONFIRM ) ) { 1337 + // The hold arm already resolved the modal; resolve the others. 1338 + if ( !holdConfirmed ) { 1341 1339 s_queue.resolveModal( decision ); 1342 1340 } 1343 1341 // Resolved (or timed out below): fall through to the single exit.
+4 -6
src/vault/export_passphrase.cpp
··· 19 19 constexpr size_t kMaxWordLen = 32; 20 20 21 21 /// Trampoline adapting WordlistProvider::readLine to the passgen::WordReader signature. 22 - bool providerWordReader( uint32_t index, char* buf, size_t bufLen, void* ctx ) { 22 + bool providerWordReader( uint32_t index, char* buf, size_t bufLen, const void* ctx ) { 23 23 const auto* p = static_cast<const WordlistProvider*>( ctx ); 24 24 return p->readLine( p->name, index, buf, bufLen, p->ctx ); 25 25 } 26 26 } // namespace 27 27 28 - size_t generateDiceware( WordReader reader, void* userCtx, uint32_t lineCount, uint32_t wordCount, 29 - char separator, char* out, size_t outCap ) { 28 + size_t generateDiceware( WordReader reader, const void* userCtx, uint32_t lineCount, 29 + uint32_t wordCount, char separator, char* out, size_t outCap ) { 30 30 if ( out == nullptr || outCap == 0 ) { 31 31 return 0; 32 32 } ··· 90 90 if ( provider.hasWordlist( provider.name, provider.ctx ) ) { 91 91 const uint32_t lines = provider.lineCount( provider.name, provider.ctx ); 92 92 if ( lines >= kMinExportWordlistLines ) { 93 - // const_cast: void* cannot hold const; providerWordReader only reads the provider. 94 - void* provCtx = const_cast<void*>( static_cast<const void*>( &provider ) ); 95 - const size_t n = generateDiceware( providerWordReader, provCtx, lines, 93 + const size_t n = generateDiceware( providerWordReader, &provider, lines, 96 94 kExportDicewareWords, kDicewareSeparator, out, cap ); 97 95 if ( n > 0 ) { 98 96 return n;
+3 -3
src/vault/export_passphrase.h
··· 48 48 * @retval true A word was written. 49 49 * @retval false Index out of range or a read error. 50 50 */ 51 - using WordReader = bool ( * )( uint32_t index, char* buf, size_t bufLen, void* userCtx ); 51 + using WordReader = bool ( * )( uint32_t index, char* buf, size_t bufLen, const void* userCtx ); 52 52 53 53 /** 54 54 * @brief Generate a diceware passphrase into @p out. ··· 71 71 * @warning SECURITY: @p out holds credential-grade material; the caller owns 72 72 * zeroizing it. The internal per-word scratch is wiped before return. 73 73 */ 74 - size_t generateDiceware( WordReader reader, void* userCtx, uint32_t lineCount, uint32_t wordCount, 75 - char separator, char* out, size_t outCap ); 74 + size_t generateDiceware( WordReader reader, const void* userCtx, uint32_t lineCount, 75 + uint32_t wordCount, char separator, char* out, size_t outCap ); 76 76 77 77 /** 78 78 * @brief Shannon entropy (bits) of a diceware passphrase.
+2 -2
src/vault/record_codec.h
··· 178 178 return value < limit ? value : limit; 179 179 } 180 180 181 - /// @brief View a `char*` string as raw bytes (no aliasing through casts). 181 + /// @brief View a `char*` string as raw bytes (char-to-byte aliasing is well-defined). 182 182 static const uint8_t* asBytes( const char* p ) noexcept { 183 - return static_cast<const uint8_t*>( static_cast<const void*>( p ) ); 183 + return reinterpret_cast<const uint8_t*>( p ); 184 184 } 185 185 186 186 uint8_t* buf_ = nullptr;
+6 -8
src/vault/vault_keys.cpp
··· 38 38 const std::array<uint8_t, kHmacSaltSize>& hmacSalt, DerivedKeys& out ) { 39 39 constexpr size_t kEncLabelLen = sizeof( kEncKeyLabel ) - 1; // exclude NUL 40 40 41 - bool ok = VaultCrypto::hmacSha256( 42 - masterKey.data(), masterKey.size(), 43 - static_cast<const uint8_t*>( static_cast<const void*>( kEncKeyLabel ) ), kEncLabelLen, 44 - out.enc.data() ); 41 + bool ok = VaultCrypto::hmacSha256( masterKey.data(), masterKey.size(), 42 + reinterpret_cast<const uint8_t*>( kEncKeyLabel ), 43 + kEncLabelLen, out.enc.data() ); 45 44 if ( ok ) { 46 45 ok = deriveMacKey( masterKey, hmacSalt, out.mac ); 47 46 } ··· 55 54 std::array<uint8_t, kPinVerifierSize>& out ) { 56 55 constexpr size_t kPinLabelLen = sizeof( kPinVerifierLabel ) - 1; // exclude NUL 57 56 58 - const bool ok = VaultCrypto::hmacSha256( 59 - masterKey.data(), masterKey.size(), 60 - static_cast<const uint8_t*>( static_cast<const void*>( kPinVerifierLabel ) ), kPinLabelLen, 61 - out.data() ); 57 + const bool ok = VaultCrypto::hmacSha256( masterKey.data(), masterKey.size(), 58 + reinterpret_cast<const uint8_t*>( kPinVerifierLabel ), 59 + kPinLabelLen, out.data() ); 62 60 if ( !ok ) { 63 61 VaultCrypto::secureWipe( out.data(), out.size() ); 64 62 }
+4 -4
test/vault/test_export_passphrase/test_export_passphrase.cpp
··· 40 40 41 41 // --- Function declarations ------------------------------------------------- 42 42 43 - static bool fakeWordReader( uint32_t index, char* buf, size_t bufLen, void* userCtx ); 44 - static bool fakeShortBuf( uint32_t index, char* buf, size_t bufLen, void* userCtx ); 43 + static bool fakeWordReader( uint32_t index, char* buf, size_t bufLen, const void* userCtx ); 44 + static bool fakeShortBuf( uint32_t index, char* buf, size_t bufLen, const void* userCtx ); 45 45 static size_t countChar( const char* s, char c ); 46 46 static void test_small_wordlist_entropy_below_export_floor(); 47 47 ··· 233 233 234 234 // --- Helper definitions ---------------------------------------------------- 235 235 236 - static bool fakeWordReader( uint32_t index, char* buf, size_t bufLen, void* ) { 236 + static bool fakeWordReader( uint32_t index, char* buf, size_t bufLen, const void* ) { 237 237 if ( index >= kFakeWordCount || bufLen == 0 ) 238 238 return false; 239 239 const std::string word = fakeWordAt( index ); ··· 243 243 } 244 244 245 245 // Reader that always fails, to exercise the mid-generation failure path. 246 - static bool fakeShortBuf( uint32_t, char*, size_t, void* ) { 246 + static bool fakeShortBuf( uint32_t, char*, size_t, const void* ) { 247 247 return false; 248 248 } 249 249