1 /* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 // This is included first to ensure it doesn't implicitly depend on anything
8 // else.
9 #include "mozilla/BufferList.h"
10 
11 // It would be nice if we could use the InfallibleAllocPolicy from mozalloc,
12 // but MFBT cannot use mozalloc.
13 class InfallibleAllocPolicy
14 {
15 public:
16   template <typename T>
pod_malloc(size_t aNumElems)17   T* pod_malloc(size_t aNumElems)
18   {
19     if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
20       MOZ_CRASH("TestBufferList.cpp: overflow");
21     }
22     T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
23     if (!rv) {
24       MOZ_CRASH("TestBufferList.cpp: out of memory");
25     }
26     return rv;
27   }
28 
free_(void * aPtr)29   void free_(void* aPtr) { free(aPtr); }
30 
reportAllocOverflow() const31   void reportAllocOverflow() const {}
32 
checkSimulatedOOM() const33   bool checkSimulatedOOM() const { return true; }
34 };
35 
36 typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
37 
main(void)38 int main(void)
39 {
40   const size_t kInitialSize = 16;
41   const size_t kInitialCapacity = 24;
42   const size_t kStandardCapacity = 32;
43 
44   BufferList bl(kInitialSize, kInitialCapacity, kStandardCapacity);
45 
46   memset(bl.Start(), 0x0c, kInitialSize);
47   MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize);
48 
49   // Simple iteration and access.
50 
51   BufferList::IterImpl iter(bl.Iter());
52   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize);
53   MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize));
54   MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize + 1));
55   MOZ_RELEASE_ASSERT(!iter.HasRoomFor(size_t(-1)));
56   MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
57   MOZ_RELEASE_ASSERT(!iter.Done());
58 
59   iter.Advance(bl, 4);
60   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4);
61   MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4));
62   MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
63   MOZ_RELEASE_ASSERT(!iter.Done());
64 
65   iter.Advance(bl, 11);
66   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4 - 11);
67   MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4 - 11));
68   MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize - 4 - 11 + 1));
69   MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c);
70   MOZ_RELEASE_ASSERT(!iter.Done());
71 
72   iter.Advance(bl, kInitialSize - 4 - 11);
73   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 0);
74   MOZ_RELEASE_ASSERT(!iter.HasRoomFor(1));
75   MOZ_RELEASE_ASSERT(iter.Done());
76 
77   // Writing to the buffer.
78 
79   const size_t kSmallWrite = 16;
80 
81   char toWrite[kSmallWrite];
82   memset(toWrite, 0x0a, kSmallWrite);
83   bl.WriteBytes(toWrite, kSmallWrite);
84 
85   MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize + kSmallWrite);
86 
87   iter = bl.Iter();
88   iter.Advance(bl, kInitialSize);
89   MOZ_RELEASE_ASSERT(!iter.Done());
90   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
91   MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize));
92   MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
93 
94   // AdvanceAcrossSegments.
95 
96   iter = bl.Iter();
97   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4));
98   MOZ_RELEASE_ASSERT(!iter.Done());
99   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
100   MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
101   MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
102 
103   iter = bl.Iter();
104   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4));
105   MOZ_RELEASE_ASSERT(!iter.Done());
106   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
107   MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
108   MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
109 
110   MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1));
111   MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
112   MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1));
113   MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1)));
114 
115   // Reading non-contiguous bytes.
116 
117   char toRead[kSmallWrite];
118   iter = bl.Iter();
119   iter.Advance(bl, kInitialSize);
120   bl.ReadBytes(iter, toRead, kSmallWrite);
121   MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0);
122   MOZ_RELEASE_ASSERT(iter.Done());
123 
124   // Make sure reading up to the end of a segment advances the iter to the next
125   // segment.
126   iter = bl.Iter();
127   bl.ReadBytes(iter, toRead, kInitialSize);
128   MOZ_RELEASE_ASSERT(!iter.Done());
129   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize);
130 
131   const size_t kBigWrite = 1024;
132 
133   char* toWriteBig = static_cast<char*>(malloc(kBigWrite));
134   for (unsigned i = 0; i < kBigWrite; i++) {
135     toWriteBig[i] = i % 37;
136   }
137   bl.WriteBytes(toWriteBig, kBigWrite);
138 
139   char* toReadBig = static_cast<char*>(malloc(kBigWrite));
140   iter = bl.Iter();
141   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
142   bl.ReadBytes(iter, toReadBig, kBigWrite);
143   MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0);
144   MOZ_RELEASE_ASSERT(iter.Done());
145 
146   free(toReadBig);
147   free(toWriteBig);
148 
149   // Currently bl contains these segments:
150   // #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24
151   // #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32
152   // #2: offset 56, [i%37 for i in 24..56, size 32
153   // ...
154   // #32: offset 1016, [i%37 for i in 984..1016], size 32
155   // #33: offset 1048, [i%37 for i in 1016..1024], size 8
156 
157   static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite;
158 
159   MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize);
160 
161   static size_t kLastSegmentSize = (kTotalSize - kInitialCapacity) % kStandardCapacity;
162 
163   iter = bl.Iter();
164   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kTotalSize - kLastSegmentSize - kStandardCapacity));
165   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity);
166   iter.Advance(bl, kStandardCapacity);
167   MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize);
168   MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37);
169 
170   // Clear.
171 
172   bl.Clear();
173   MOZ_RELEASE_ASSERT(bl.Size() == 0);
174   MOZ_RELEASE_ASSERT(bl.Iter().Done());
175 
176   // Move assignment.
177 
178   const size_t kSmallCapacity = 8;
179 
180   BufferList bl2(0, kSmallCapacity, kSmallCapacity);
181   bl2.WriteBytes(toWrite, kSmallWrite);
182   bl2.WriteBytes(toWrite, kSmallWrite);
183   bl2.WriteBytes(toWrite, kSmallWrite);
184 
185   bl = mozilla::Move(bl2);
186   MOZ_RELEASE_ASSERT(bl2.Size() == 0);
187   MOZ_RELEASE_ASSERT(bl2.Iter().Done());
188 
189   iter = bl.Iter();
190   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
191   MOZ_RELEASE_ASSERT(iter.Done());
192 
193   // MoveFallible
194 
195   bool success;
196   bl2 = bl.MoveFallible<InfallibleAllocPolicy>(&success);
197   MOZ_RELEASE_ASSERT(success);
198   MOZ_RELEASE_ASSERT(bl.Size() == 0);
199   MOZ_RELEASE_ASSERT(bl.Iter().Done());
200   MOZ_RELEASE_ASSERT(bl2.Size() == kSmallWrite * 3);
201 
202   iter = bl2.Iter();
203   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kSmallWrite * 3));
204   MOZ_RELEASE_ASSERT(iter.Done());
205 
206   bl = bl2.MoveFallible<InfallibleAllocPolicy>(&success);
207 
208   // Borrowing.
209 
210   const size_t kBorrowStart = 4;
211   const size_t kBorrowSize = 24;
212 
213   iter = bl.Iter();
214   iter.Advance(bl, kBorrowStart);
215   bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
216   MOZ_RELEASE_ASSERT(success);
217   MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
218 
219   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
220   MOZ_RELEASE_ASSERT(iter.Done());
221 
222   iter = bl2.Iter();
223   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize));
224   MOZ_RELEASE_ASSERT(iter.Done());
225 
226   BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
227   iter1.Advance(bl, kBorrowStart);
228   MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
229   MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
230   MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
231   MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
232 
233   // Extracting.
234 
235   const size_t kExtractStart = 8;
236   const size_t kExtractSize = 24;
237   const size_t kExtractOverSize = 1000;
238 
239   iter = bl.Iter();
240   iter.Advance(bl, kExtractStart);
241   bl2 = bl.Extract(iter, kExtractSize, &success);
242   MOZ_RELEASE_ASSERT(success);
243   MOZ_RELEASE_ASSERT(bl2.Size() == kExtractSize);
244 
245   BufferList bl3 = bl.Extract(iter, kExtractOverSize, &success);
246   MOZ_RELEASE_ASSERT(!success);
247 
248   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kExtractSize - kExtractStart));
249   MOZ_RELEASE_ASSERT(iter.Done());
250 
251   iter = bl2.Iter();
252   MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kExtractSize));
253   MOZ_RELEASE_ASSERT(iter.Done());
254 
255   return 0;
256 }
257