This is a follow-up to #30884.
Remove a number of dead code paths, and improve the code organization and documentation, in AutoFile
.
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
For detailed information about the code coverage, see the test coverage report.
See the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
454@@ -455,7 +455,6 @@ class AutoFile
455 }
456
457 bool Commit();
re-ACK caac06f784c5d94c6a5f7d0b586f9ddbbe55c369
Only change is fixup up the doxygen dev docs
683@@ -684,10 +684,6 @@ bool BlockManager::UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos
684
685 // Write undo data
686 long fileOutPos = fileout.tell();
687- if (fileOutPos < 0) {
688- LogError("%s: ftell failed\n", __func__);
689- return false;
690- }
691 pos.nPos = (unsigned int)fileOutPos;
Does it still make sense for AutoFile::tell()
to return a int64_t
instead of a uint64_t
or unsigned int
?
Now that we’ve removed the invalid usages, maybe it’s a good opportunity to transition AutoFile
(and maybe FlatFilePos
) to std::optional<uint64_t> m_position;
I think this is a bit of a philosophical question.
Both int64_t
and uint64_t
have sufficient range to represent the data here (because ftell
returns a long
, which matches int64_t
on all our supported platforms, plus we know the result cannot be negative), so both are perfectly functional.
The advantage of uint64_t
(and presumably the reason you’re suggesting this) is because it signals that the number cannot be negative.
On the other hand, signed types have the advantage that they actually model integers (because signed overflow is UB, the compiler - and humans - may treat signed types as being mathematical integers without wraparound behavior), while unsigned types explicitly model modular arithmetic. Bjarne Stroustroup’s reasoning for wanting std::span
to have a signed size despite not being allowed to be negative is worth a read, I think.
I don’t think either of these arguments are very strong, but I think it’s enough to not bother changing the return type.
The advantage of uint64_t (and presumably the reason you’re suggesting this) is because it signals that the number cannot be negative.
Yes, to make it stricter and closer aligned to the problem domain.
signed types have the advantage that they actually model integers
But here we’re modelling a size where potential negative values are confusing (especially since we’re storing it as unsigned int going forward). The negative values were already handled, it seems to me that they lost their usefulness after this change (checking for <0 after that change would warn).
signed overflow is UB
We’re already potentially down-casting the size to <64 bits - is overflow something we care about here?
@l0rinc All the same arguments apply to the size of spans, which cannot be negative, and yet people strongly (and unsuccessfully, but only barely) argued that it should have a signed size type. I don’t believe the concern is about specific worries around overflow and its behavior; it is about the fact that sizes shouldn’t be thought of as modular arithmetic, and that C++ has no type to model natural numbers - the best approximation is signed types which model integers.
And again, I also see the advantages of the other side. But given that both types suffice, and there are advantages in both ways, I don’t think it’s worth arguing over.
Mixing signed and unsigned numbers is a common source of confusion and bugs. Most coding guidelines recommend against mixing them.
But we are mixing them, that’s my main concern
0pos.nPos = (unsigned int)fileOutPos
I’d be fine with using a signed position in both cases, but if one is signed and the other is not, most of us will assume that we have to check the signed value for negatives (as the existence of this PR demonstrates). But changing the usages is outside the scope of this PR, thanks for explaining your reasoning (and for the interesting article), please resolve the comment.
437 int64_t tell();
438
439+ /** Wrapper around FileCommit(). */
440+ bool Commit();
441+
442+ /** Wrapper around TruncateFile(). */
429@@ -430,9 +430,18 @@ class AutoFile
430 /** Implementation detail, only used internally. */
431 std::size_t detail_fread(Span<std::byte> dst);
432
433+ /** Wrapper around fseek(). Will throw if seeking is not possible. */
434 void seek(int64_t offset, int origin);
435+
436+ /** Find position within the file. Will throw if unknown. */
437 int64_t tell();
Slightly unrelated, but since we’re modifying its usage here: streams_tests
doesn’t seem to cover this method at all.
(+ Commit, Truncate and many std::ios_base::failure
throws seem also uncovered)
Concept ACK
Could be the right time to do some minor cleanup (return unsigned) and maybe to cover missing method and exceptions with tests.
ACK caac06f784c5d94c6a5f7d0b586f9ddbbe55c369
My only remaining concern is lack of related test coverage, but it’s not a blocker