A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

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

refactor: RAII scoped open/close for ZipFile (#1433)

## Summary

**What is the goal of this PR?**

Added `ScopedOpenClose` RAII guard to eliminate repetitive
`wasOpen`/`close()` boilerplate across all ZipFile methods.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_

authored by

Zach Nelson and committed by
GitHub
b3b43bb3 5349e817

+43 -129
+43 -129
lib/ZipFile/ZipFile.cpp
··· 18 18 constexpr uint16_t ZIP_METHOD_STORED = 0; 19 19 constexpr uint16_t ZIP_METHOD_DEFLATED = 8; 20 20 21 + // RAII zip: opens the zip if not already open, closes on destruction only if 22 + // it performed the open. Removes the wasOpen/close boilerplate from every method. 23 + class ScopedOpenClose final { 24 + public: 25 + [[nodiscard]] explicit ScopedOpenClose(ZipFile& zf) : zf(zf), needsClose(!zf.isOpen()) { 26 + if (needsClose) ok = zf.open(); 27 + } 28 + ~ScopedOpenClose() { 29 + if (needsClose && ok) zf.close(); 30 + } 31 + ScopedOpenClose(const ScopedOpenClose&) = delete; 32 + ScopedOpenClose& operator=(const ScopedOpenClose&) = delete; 33 + ScopedOpenClose(ScopedOpenClose&&) = delete; 34 + ScopedOpenClose& operator=(ScopedOpenClose&&) = delete; 35 + explicit operator bool() const { return ok || !needsClose; } 36 + 37 + private: 38 + ZipFile& zf; 39 + bool needsClose = false; 40 + bool ok = true; // true when zip was already open (no open() call needed) 41 + }; 42 + 21 43 int zipReadCallback(uzlib_uncomp* uncomp) { 22 44 auto* ctx = reinterpret_cast<ZipInflateCtx*>(uncomp); 23 45 if (ctx->fileRemaining == 0) return -1; ··· 35 57 } // namespace 36 58 37 59 bool ZipFile::loadAllFileStatSlims() { 38 - const bool wasOpen = isOpen(); 39 - if (!wasOpen && !open()) { 40 - return false; 41 - } 60 + const ScopedOpenClose zip{*this}; 61 + if (!zip) return false; 42 62 43 - if (!loadZipDetails()) { 44 - if (!wasOpen) { 45 - close(); 46 - } 47 - return false; 48 - } 63 + if (!loadZipDetails()) return false; 49 64 50 65 file.seek(zipDetails.centralDirOffset); 51 66 ··· 89 104 lastCentralDirPos = zipDetails.centralDirOffset; 90 105 lastCentralDirPosValid = true; 91 106 92 - if (!wasOpen) { 93 - close(); 94 - } 95 107 return true; 96 108 } 97 109 ··· 105 117 return false; 106 118 } 107 119 108 - const bool wasOpen = isOpen(); 109 - if (!wasOpen && !open()) { 110 - return false; 111 - } 120 + const ScopedOpenClose zip{*this}; 121 + if (!zip) return false; 112 122 113 - if (!loadZipDetails()) { 114 - if (!wasOpen) { 115 - close(); 116 - } 117 - return false; 118 - } 123 + if (!loadZipDetails()) return false; 119 124 120 125 // Phase 1: Try scanning from cursor position first 121 126 uint32_t startPos = lastCentralDirPosValid ? lastCentralDirPos : zipDetails.centralDirOffset; ··· 179 184 file.seekCur(m + k); 180 185 } 181 186 182 - if (!wasOpen) { 183 - close(); 184 - } 185 187 return found; 186 188 } 187 189 188 190 long ZipFile::getDataOffset(const FileStatSlim& fileStat) { 189 - const bool wasOpen = isOpen(); 190 - if (!wasOpen && !open()) { 191 - return -1; 192 - } 191 + const ScopedOpenClose zip{*this}; 192 + if (!zip) return -1; 193 193 194 194 constexpr auto localHeaderSize = 30; 195 195 ··· 198 198 199 199 file.seek(fileOffset); 200 200 const size_t read = file.read(pLocalHeader, localHeaderSize); 201 - if (!wasOpen) { 202 - close(); 203 - } 204 201 205 202 if (read != localHeaderSize) { 206 203 LOG_ERR("ZIP", "Something went wrong reading the local header"); ··· 223 220 return true; 224 221 } 225 222 226 - const bool wasOpen = isOpen(); 227 - if (!wasOpen && !open()) { 228 - return false; 229 - } 223 + const ScopedOpenClose zip{*this}; 224 + if (!zip) return false; 230 225 231 226 const size_t fileSize = file.size(); 232 227 if (fileSize < 22) { 233 228 LOG_ERR("ZIP", "File too small to be a valid zip"); 234 - if (!wasOpen) { 235 - close(); 236 - } 237 229 return false; // Minimum EOCD size is 22 bytes 238 230 } 239 231 ··· 243 235 const auto buffer = static_cast<uint8_t*>(malloc(scanRange)); 244 236 if (!buffer) { 245 237 LOG_ERR("ZIP", "Failed to allocate memory for EOCD scan buffer"); 246 - if (!wasOpen) { 247 - close(); 248 - } 249 238 return false; 250 239 } 251 240 ··· 265 254 if (foundOffset == -1) { 266 255 LOG_ERR("ZIP", "EOCD signature not found in zip file"); 267 256 free(buffer); 268 - if (!wasOpen) { 269 - close(); 270 - } 271 257 return false; 272 258 } 273 259 ··· 280 266 zipDetails.isSet = true; 281 267 282 268 free(buffer); 283 - if (!wasOpen) { 284 - close(); 285 - } 286 269 return true; 287 270 } 288 271 ··· 317 300 return 0; 318 301 } 319 302 320 - const bool wasOpen = isOpen(); 321 - if (!wasOpen && !open()) { 322 - return 0; 323 - } 303 + const ScopedOpenClose zip{*this}; 304 + if (!zip) return 0; 324 305 325 - if (!loadZipDetails()) { 326 - if (!wasOpen) { 327 - close(); 328 - } 329 - return 0; 330 - } 306 + if (!loadZipDetails()) return 0; 331 307 332 308 file.seek(zipDetails.centralDirOffset); 333 309 ··· 384 360 file.seekCur(m + k); 385 361 } 386 362 387 - if (!wasOpen) { 388 - close(); 389 - } 390 - 391 363 return matched; 392 364 } 393 365 394 366 uint8_t* ZipFile::readFileToMemory(const char* filename, size_t* size, const bool trailingNullByte) { 395 - const bool wasOpen = isOpen(); 396 - if (!wasOpen && !open()) { 397 - return nullptr; 398 - } 367 + const ScopedOpenClose zip{*this}; 368 + if (!zip) return nullptr; 399 369 400 370 FileStatSlim fileStat = {}; 401 - if (!loadFileStatSlim(filename, &fileStat)) { 402 - if (!wasOpen) { 403 - close(); 404 - } 405 - return nullptr; 406 - } 371 + if (!loadFileStatSlim(filename, &fileStat)) return nullptr; 407 372 408 373 const long fileOffset = getDataOffset(fileStat); 409 - if (fileOffset < 0) { 410 - if (!wasOpen) { 411 - close(); 412 - } 413 - return nullptr; 414 - } 374 + if (fileOffset < 0) return nullptr; 415 375 416 376 file.seek(fileOffset); 417 377 ··· 421 381 const auto data = static_cast<uint8_t*>(malloc(dataSize)); 422 382 if (data == nullptr) { 423 383 LOG_ERR("ZIP", "Failed to allocate memory for output buffer (%zu bytes)", dataSize); 424 - if (!wasOpen) { 425 - close(); 426 - } 427 384 return nullptr; 428 385 } 429 386 430 387 if (fileStat.method == ZIP_METHOD_STORED) { 431 388 // no deflation, just read content 432 389 const size_t dataRead = file.read(data, inflatedDataSize); 433 - if (!wasOpen) { 434 - close(); 435 - } 436 390 437 391 if (dataRead != inflatedDataSize) { 438 392 LOG_ERR("ZIP", "Failed to read data"); ··· 446 400 const auto deflatedData = static_cast<uint8_t*>(malloc(deflatedDataSize)); 447 401 if (deflatedData == nullptr) { 448 402 LOG_ERR("ZIP", "Failed to allocate memory for decompression buffer"); 449 - if (!wasOpen) { 450 - close(); 451 - } 452 403 return nullptr; 453 404 } 454 405 455 406 const size_t dataRead = file.read(deflatedData, deflatedDataSize); 456 - if (!wasOpen) { 457 - close(); 458 - } 459 407 460 408 if (dataRead != deflatedDataSize) { 461 409 LOG_ERR("ZIP", "Failed to read data, expected %d got %d", deflatedDataSize, dataRead); ··· 482 430 // Continue out of block with data set 483 431 } else { 484 432 LOG_ERR("ZIP", "Unsupported compression method"); 485 - if (!wasOpen) { 486 - close(); 487 - } 488 433 return nullptr; 489 434 } 490 435 ··· 494 439 } 495 440 496 441 bool ZipFile::readFileToStream(const char* filename, Print& out, const size_t chunkSize) { 497 - const bool wasOpen = isOpen(); 498 - if (!wasOpen && !open()) { 499 - return false; 500 - } 442 + const ScopedOpenClose zip{*this}; 443 + if (!zip) return false; 501 444 502 445 FileStatSlim fileStat = {}; 503 - if (!loadFileStatSlim(filename, &fileStat)) { 504 - return false; 505 - } 446 + if (!loadFileStatSlim(filename, &fileStat)) return false; 506 447 507 448 const long fileOffset = getDataOffset(fileStat); 508 - if (fileOffset < 0) { 509 - return false; 510 - } 449 + if (fileOffset < 0) return false; 511 450 512 451 file.seek(fileOffset); 513 452 const auto deflatedDataSize = fileStat.compressedSize; ··· 518 457 const auto buffer = static_cast<uint8_t*>(malloc(chunkSize)); 519 458 if (!buffer) { 520 459 LOG_ERR("ZIP", "Failed to allocate memory for buffer"); 521 - if (!wasOpen) { 522 - close(); 523 - } 524 460 return false; 525 461 } 526 462 ··· 530 466 if (dataRead == 0) { 531 467 LOG_ERR("ZIP", "Could not read more bytes"); 532 468 free(buffer); 533 - if (!wasOpen) { 534 - close(); 535 - } 536 469 return false; 537 470 } 538 471 ··· 540 473 remaining -= dataRead; 541 474 } 542 475 543 - if (!wasOpen) { 544 - close(); 545 - } 546 476 free(buffer); 547 477 return true; 548 478 } ··· 551 481 auto* fileReadBuffer = static_cast<uint8_t*>(malloc(chunkSize)); 552 482 if (!fileReadBuffer) { 553 483 LOG_ERR("ZIP", "Failed to allocate memory for zip file read buffer"); 554 - if (!wasOpen) { 555 - close(); 556 - } 557 484 return false; 558 485 } 559 486 ··· 561 488 if (!outputBuffer) { 562 489 LOG_ERR("ZIP", "Failed to allocate memory for output buffer"); 563 490 free(fileReadBuffer); 564 - if (!wasOpen) { 565 - close(); 566 - } 567 491 return false; 568 492 } 569 493 ··· 577 501 LOG_ERR("ZIP", "Failed to init inflate reader"); 578 502 free(outputBuffer); 579 503 free(fileReadBuffer); 580 - if (!wasOpen) { 581 - close(); 582 - } 583 504 return false; 584 505 } 585 506 ctx.reader.setReadCallback(zipReadCallback); ··· 623 544 // InflateStatus::Ok: output buffer full, continue 624 545 } 625 546 626 - if (!wasOpen) { 627 - close(); 628 - } 629 547 free(outputBuffer); 630 548 free(fileReadBuffer); 631 549 return success; // ctx.reader destructor frees the ring buffer 632 - } 633 - 634 - if (!wasOpen) { 635 - close(); 636 550 } 637 551 638 552 LOG_ERR("ZIP", "Unsupported compression method");