Bug 1581049 - ModuloBuffer::Iterator::ReadInto(ModuloBuffer::Iterator&) - r=gregtatum

Some objects are copied byte-by-byte to/from `ModuloBuffer`s.
E.g., serialized `BlocksRingBuffer`s, or duplicate stacks. (And more to come.)

`Iterator::ReadInto(Iterator&)` optimizes these copies by using the minimum
number of `memcpy`s possible.

Differential Revision: https://phabricator.services.mozilla.com/D45838

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gerald Squelart 2019-09-17 01:54:03 +00:00
parent 6b49069f3e
commit 4c2ab062ce
2 changed files with 90 additions and 0 deletions

View File

@ -417,6 +417,27 @@ class ModuloBuffer {
mIndex += aLength;
}
// Read data into a mutable iterator and move both iterators ahead.
void ReadInto(Iterator</* IsBufferConst */ false>& aDst, Length aLength) {
// Don't allow data larger than the buffer.
MOZ_ASSERT(aLength <= mModuloBuffer->BufferLength().Value());
MOZ_ASSERT(aLength <= aDst.mModuloBuffer->BufferLength().Value());
// Offset inside the buffer (corresponding to our Index).
Offset offset = OffsetInBuffer();
// Compute remaining bytes between this offset and the end of the buffer.
Length remaining = mModuloBuffer->BufferLength().Value() - offset;
if (MOZ_LIKELY(remaining >= aLength)) {
// Can read everything we need before the end of the buffer.
aDst.Write(&mModuloBuffer->mBuffer[offset], aLength);
} else {
// Read as much as possible before the end of the buffer.
aDst.Write(&mModuloBuffer->mBuffer[offset], remaining);
// And then continue from the beginning of the buffer.
aDst.Write(&mModuloBuffer->mBuffer[0], (aLength - remaining));
}
mIndex += aLength;
}
// Read data into an object and move iterator ahead.
// Note that this overwrites `aObject` with bytes from the buffer.
// Restricted to trivially-copyable types, which support this without

View File

@ -486,6 +486,75 @@ void TestModuloBuffer() {
MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
}
// This test function does a `ReadInto` as directed, and checks that the
// result is the same as if the copy had been done manually byte-by-byte.
// `TestReadInto(3, 7, 2)` copies from index 3 to index 7, 2 bytes long.
// Return the output string (from `ReadInto`) for external checks.
auto TestReadInto = [](MB::Index aReadFrom, MB::Index aWriteTo,
MB::Length aBytes) {
constexpr uint32_t TRISize = 16;
// Prepare an input buffer, all different elements.
uint8_t input[TRISize + 1] = "ABCDEFGHIJKLMNOP";
const MB mbInput(input, MakePowerOfTwo32<TRISize>());
// Prepare an output buffer, different from input.
uint8_t output[TRISize + 1] = "abcdefghijklmnop";
MB mbOutput(output, MakePowerOfTwo32<TRISize>());
// Run ReadInto.
auto writer = mbOutput.WriterAt(aWriteTo);
mbInput.ReaderAt(aReadFrom).ReadInto(writer, aBytes);
// Do the same operation manually.
uint8_t outputCheck[TRISize + 1] = "abcdefghijklmnop";
MB mbOutputCheck(outputCheck, MakePowerOfTwo32<TRISize>());
auto readerCheck = mbInput.ReaderAt(aReadFrom);
auto writerCheck = mbOutputCheck.WriterAt(aWriteTo);
for (MB::Length i = 0; i < aBytes; ++i) {
*writerCheck++ = *readerCheck++;
}
// Compare the two outputs.
for (uint32_t i = 0; i < TRISize; ++i) {
# ifdef TEST_MODULOBUFFER_FAILURE_DEBUG
// Only used when debugging failures.
if (output[i] != outputCheck[i]) {
printf(
"*** from=%u to=%u bytes=%u i=%u\ninput: '%s'\noutput: "
"'%s'\ncheck: '%s'\n",
unsigned(aReadFrom), unsigned(aWriteTo), unsigned(aBytes),
unsigned(i), input, output, outputCheck);
}
# endif
MOZ_RELEASE_ASSERT(output[i] == outputCheck[i]);
}
# ifdef TEST_MODULOBUFFER_HELPER
// Only used when adding more tests.
printf("*** from=%u to=%u bytes=%u output: %s\n", unsigned(aReadFrom),
unsigned(aWriteTo), unsigned(aBytes), output);
# endif
return std::string(reinterpret_cast<const char*>(output));
};
// A few manual checks:
constexpr uint32_t TRISize = 16;
MOZ_RELEASE_ASSERT(TestReadInto(0, 0, 0) == "abcdefghijklmnop");
MOZ_RELEASE_ASSERT(TestReadInto(0, 0, TRISize) == "ABCDEFGHIJKLMNOP");
MOZ_RELEASE_ASSERT(TestReadInto(0, 5, TRISize) == "LMNOPABCDEFGHIJK");
MOZ_RELEASE_ASSERT(TestReadInto(5, 0, TRISize) == "FGHIJKLMNOPABCDE");
// Test everything! (16^3 = 4096, not too much.)
for (MB::Index r = 0; r < TRISize; ++r) {
for (MB::Index w = 0; w < TRISize; ++w) {
for (MB::Length len = 0; len < TRISize; ++len) {
TestReadInto(r, w, len);
}
}
}
printf("TestModuloBuffer done\n");
}