diff options
Diffstat (limited to 'mfbt/tests/TestBufferList.cpp')
-rw-r--r-- | mfbt/tests/TestBufferList.cpp | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp new file mode 100644 index 0000000000..9c0d69d7d6 --- /dev/null +++ b/mfbt/tests/TestBufferList.cpp @@ -0,0 +1,372 @@ +/* -*- 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; +} |