gecko-dev/mfbt/tests/TestBufferList.cpp
Nika Layzell 7210a4bca7 Bug 1783242 - Part 3: Remove BufferList::Extract, r=glandium,ipc-reviewers,mccr8
The only uses of this method were removed in Part 1, meaning that it can
now be removed. Support for this method adds a significant amount of
complexity to `BufferList` and IPC serialization.

Differential Revision: https://phabricator.services.mozilla.com/D154439
2022-09-28 19:25:14 +00:00

373 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// This is included first to ensure it doesn't implicitly depend on anything
// else.
#include "mozilla/BufferList.h"
// It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
// but MFBT cannot use mozalloc.
class InfallibleAllocPolicy {
public:
template <typename T>
T* pod_malloc(size_t aNumElems) {
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
MOZ_CRASH("TestBufferList.cpp: overflow");
}
T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
if (!rv) {
MOZ_CRASH("TestBufferList.cpp: out of memory");
}
return rv;
}
template <typename T>
void free_(T* aPtr, size_t aNumElems = 0) {
free(aPtr);
}
void reportAllocOverflow() const {}
bool checkSimulatedOOM() const { return true; }
};
typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
int main(void) {
const size_t kInitialSize = 16;
const size_t kInitialCapacity = 24;
const size_t kStandardCapacity = 32;
BufferList bl(kInitialSize, kInitialCapacity, kStandardCapacity);
memset(bl.Start(), 0x0c, kInitialSize);
MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize);
// Simple iteration and access.
BufferList::IterImpl iter(bl.Iter());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize));
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize + 1));
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(size_t(-1)));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
MOZ_RELEASE_ASSERT(!iter.Done());
iter.Advance(bl, 4);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
MOZ_RELEASE_ASSERT(!iter.Done());
iter.Advance(bl, 11);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4 - 11);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4 - 11));
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize - 4 - 11 + 1));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
MOZ_RELEASE_ASSERT(!iter.Done());
iter.Advance(bl, kInitialSize - 4 - 11);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 0);
MOZ_RELEASE_ASSERT(!iter.HasRoomFor(1));
MOZ_RELEASE_ASSERT(iter.Done());
// Writing to the buffer.
const size_t kSmallWrite = 16;
char toWrite[kSmallWrite];
memset(toWrite, 0x0a, kSmallWrite);
MOZ_ALWAYS_TRUE(bl.WriteBytes(toWrite, kSmallWrite));
MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize + kSmallWrite);
iter = bl.Iter();
iter.Advance(bl, kInitialSize);
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() ==
kInitialCapacity - kInitialSize);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
// AdvanceAcrossSegments.
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4));
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
iter = bl.Iter();
MOZ_RELEASE_ASSERT(
iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4));
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
MOZ_RELEASE_ASSERT(
bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1));
MOZ_RELEASE_ASSERT(
bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
MOZ_RELEASE_ASSERT(
!bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1));
MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1)));
// Reading non-contiguous bytes.
char toRead[kSmallWrite];
iter = bl.Iter();
iter.Advance(bl, kInitialSize);
bl.ReadBytes(iter, toRead, kSmallWrite);
MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
// Make sure reading up to the end of a segment advances the iter to the next
// segment.
iter = bl.Iter();
bl.ReadBytes(iter, toRead, kInitialSize);
MOZ_RELEASE_ASSERT(!iter.Done());
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() ==
kInitialCapacity - kInitialSize);
const size_t kBigWrite = 1024;
char* toWriteBig = static_cast<char*>(malloc(kBigWrite));
for (unsigned i = 0; i < kBigWrite; i++) {
toWriteBig[i] = i % 37;
}
MOZ_ALWAYS_TRUE(bl.WriteBytes(toWriteBig, kBigWrite));
char* toReadBig = static_cast<char*>(malloc(kBigWrite));
iter = bl.Iter();
MOZ_RELEASE_ASSERT(
iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
bl.ReadBytes(iter, toReadBig, kBigWrite);
MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
free(toReadBig);
free(toWriteBig);
// Currently bl contains these segments:
// #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24
// #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32
// #2: offset 56, [i%37 for i in 24..56, size 32
// ...
// #32: offset 1016, [i%37 for i in 984..1016], size 32
// #33: offset 1048, [i%37 for i in 1016..1024], size 8
static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite;
MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize);
static size_t kLastSegmentSize =
(kTotalSize - kInitialCapacity) % kStandardCapacity;
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(
bl, kTotalSize - kLastSegmentSize - kStandardCapacity));
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity);
iter.Advance(bl, kStandardCapacity);
MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize);
MOZ_RELEASE_ASSERT(
unsigned(*iter.Data()) ==
(kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37);
// Clear.
bl.Clear();
MOZ_RELEASE_ASSERT(bl.Size() == 0);
MOZ_RELEASE_ASSERT(bl.Iter().Done());
// Move assignment.
const size_t kSmallCapacity = 8;
BufferList bl2(0, kSmallCapacity, kSmallCapacity);
MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
bl = std::move(bl2);
MOZ_RELEASE_ASSERT(bl2.Size() == 0);
MOZ_RELEASE_ASSERT(bl2.Iter().Done());
iter = bl.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
MOZ_RELEASE_ASSERT(iter.Done());
// MoveFallible
bool success;
bl2 = bl.MoveFallible<InfallibleAllocPolicy>(&success);
MOZ_RELEASE_ASSERT(success);
MOZ_RELEASE_ASSERT(bl.Size() == 0);
MOZ_RELEASE_ASSERT(bl.Iter().Done());
MOZ_RELEASE_ASSERT(bl2.Size() == kSmallWrite * 3);
iter = bl2.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kSmallWrite * 3));
MOZ_RELEASE_ASSERT(iter.Done());
bl = bl2.MoveFallible<InfallibleAllocPolicy>(&success);
// Borrowing.
const size_t kBorrowStart = 4;
const size_t kBorrowSize = 24;
iter = bl.Iter();
iter.Advance(bl, kBorrowStart);
bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
MOZ_RELEASE_ASSERT(success);
MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(
bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
MOZ_RELEASE_ASSERT(iter.Done());
iter = bl2.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize));
MOZ_RELEASE_ASSERT(iter.Done());
BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
iter1.Advance(bl, kBorrowStart);
MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
// RangeLength.
BufferList bl12(0, 0, 8);
MOZ_ALWAYS_TRUE(bl12.WriteBytes("abcdefgh", 8));
MOZ_ALWAYS_TRUE(bl12.WriteBytes("12345678", 8));
// |iter| is at position 0 (1st segment).
iter = bl12.Iter();
iter1 = bl12.Iter();
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 8);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 12);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 3));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 15);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
MOZ_RELEASE_ASSERT(iter1.Done());
// |iter| is at position 1 (1st segment).
iter = bl12.Iter();
iter1 = bl12.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 1));
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 8);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 12);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 2));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 14);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
MOZ_RELEASE_ASSERT(iter1.Done());
// |iter| is at position 8 (2nd segment).
iter = bl12.Iter();
iter1 = bl12.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 8));
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 8));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 3));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 7);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
MOZ_RELEASE_ASSERT(iter1.Done());
// |iter| is at position 9 (2nd segment).
iter = bl12.Iter();
iter1 = bl12.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 9));
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 9));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 2));
MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 6);
MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
MOZ_RELEASE_ASSERT(iter1.Done());
BufferList bl13(0, 0, 8);
MOZ_ALWAYS_TRUE(bl13.WriteBytes("abcdefgh", 8));
MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
MOZ_RELEASE_ASSERT(bl13.Size() == 24);
// At segment border
iter = bl13.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 8));
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 16);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 8);
// Restore state
MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
MOZ_RELEASE_ASSERT(bl13.Size() == 24);
// Before segment border
iter = bl13.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 7));
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 17);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 7);
// Restore state
MOZ_ALWAYS_TRUE(bl13.WriteBytes("h", 1));
MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
MOZ_RELEASE_ASSERT(bl13.Size() == 24);
// In last segment
iter = bl13.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 20));
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 4);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 20);
// No-op truncate
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 20);
// No-op truncate with fresh iterator
iter = bl13.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 20));
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 20);
// Truncate at start of buffer
iter = bl13.Iter();
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 20);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 0);
// No-op truncate at start of buffer
iter = bl13.Iter();
MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
MOZ_RELEASE_ASSERT(iter.Done());
MOZ_RELEASE_ASSERT(bl13.Size() == 0);
return 0;
}