/* -*- 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 T* pod_malloc(size_t aNumElems) { if (aNumElems & mozilla::tl::MulOverflowMask::value) { MOZ_CRASH("TestBufferList.cpp: overflow"); } T* rv = static_cast(malloc(aNumElems * sizeof(T))); if (!rv) { MOZ_CRASH("TestBufferList.cpp: out of memory"); } return rv; } template void free_(T* aPtr, size_t aNumElems = 0) { free(aPtr); } void reportAllocOverflow() const {} bool checkSimulatedOOM() const { return true; } }; typedef mozilla::BufferList 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(malloc(kBigWrite)); for (unsigned i = 0; i < kBigWrite; i++) { toWriteBig[i] = i % 37; } MOZ_ALWAYS_TRUE(bl.WriteBytes(toWriteBig, kBigWrite)); char* toReadBig = static_cast(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(&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(&success); // Borrowing. const size_t kBorrowStart = 4; const size_t kBorrowSize = 24; iter = bl.Iter(); iter.Advance(bl, kBorrowStart); bl2 = bl.Borrow(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; }