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 public:
15 template <typename T>
pod_malloc(size_t aNumElems)16 T* pod_malloc(size_t aNumElems) {
17 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
18 MOZ_CRASH("TestBufferList.cpp: overflow");
19 }
20 T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T)));
21 if (!rv) {
22 MOZ_CRASH("TestBufferList.cpp: out of memory");
23 }
24 return rv;
25 }
26
27 template <typename T>
free_(T * aPtr,size_t aNumElems=0)28 void free_(T* aPtr, size_t aNumElems = 0) {
29 free(aPtr);
30 }
31
reportAllocOverflow() const32 void reportAllocOverflow() const {}
33
checkSimulatedOOM() const34 bool checkSimulatedOOM() const { return true; }
35 };
36
37 typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
38
main(void)39 int main(void) {
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 MOZ_ALWAYS_TRUE(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() ==
91 kInitialCapacity - kInitialSize);
92 MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize));
93 MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
94
95 // AdvanceAcrossSegments.
96
97 iter = bl.Iter();
98 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4));
99 MOZ_RELEASE_ASSERT(!iter.Done());
100 MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
101 MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
102 MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
103
104 iter = bl.Iter();
105 MOZ_RELEASE_ASSERT(
106 iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4));
107 MOZ_RELEASE_ASSERT(!iter.Done());
108 MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4);
109 MOZ_RELEASE_ASSERT(iter.HasRoomFor(4));
110 MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a);
111
112 MOZ_RELEASE_ASSERT(
113 bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1));
114 MOZ_RELEASE_ASSERT(
115 bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
116 MOZ_RELEASE_ASSERT(
117 !bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1));
118 MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1)));
119
120 // Reading non-contiguous bytes.
121
122 char toRead[kSmallWrite];
123 iter = bl.Iter();
124 iter.Advance(bl, kInitialSize);
125 bl.ReadBytes(iter, toRead, kSmallWrite);
126 MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0);
127 MOZ_RELEASE_ASSERT(iter.Done());
128
129 // Make sure reading up to the end of a segment advances the iter to the next
130 // segment.
131 iter = bl.Iter();
132 bl.ReadBytes(iter, toRead, kInitialSize);
133 MOZ_RELEASE_ASSERT(!iter.Done());
134 MOZ_RELEASE_ASSERT(iter.RemainingInSegment() ==
135 kInitialCapacity - kInitialSize);
136
137 const size_t kBigWrite = 1024;
138
139 char* toWriteBig = static_cast<char*>(malloc(kBigWrite));
140 for (unsigned i = 0; i < kBigWrite; i++) {
141 toWriteBig[i] = i % 37;
142 }
143 MOZ_ALWAYS_TRUE(bl.WriteBytes(toWriteBig, kBigWrite));
144
145 char* toReadBig = static_cast<char*>(malloc(kBigWrite));
146 iter = bl.Iter();
147 MOZ_RELEASE_ASSERT(
148 iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite));
149 bl.ReadBytes(iter, toReadBig, kBigWrite);
150 MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0);
151 MOZ_RELEASE_ASSERT(iter.Done());
152
153 free(toReadBig);
154 free(toWriteBig);
155
156 // Currently bl contains these segments:
157 // #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24
158 // #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32
159 // #2: offset 56, [i%37 for i in 24..56, size 32
160 // ...
161 // #32: offset 1016, [i%37 for i in 984..1016], size 32
162 // #33: offset 1048, [i%37 for i in 1016..1024], size 8
163
164 static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite;
165
166 MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize);
167
168 static size_t kLastSegmentSize =
169 (kTotalSize - kInitialCapacity) % kStandardCapacity;
170
171 iter = bl.Iter();
172 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(
173 bl, kTotalSize - kLastSegmentSize - kStandardCapacity));
174 MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity);
175 iter.Advance(bl, kStandardCapacity);
176 MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize);
177 MOZ_RELEASE_ASSERT(
178 unsigned(*iter.Data()) ==
179 (kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37);
180
181 // Clear.
182
183 bl.Clear();
184 MOZ_RELEASE_ASSERT(bl.Size() == 0);
185 MOZ_RELEASE_ASSERT(bl.Iter().Done());
186
187 // Move assignment.
188
189 const size_t kSmallCapacity = 8;
190
191 BufferList bl2(0, kSmallCapacity, kSmallCapacity);
192 MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
193 MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
194 MOZ_ALWAYS_TRUE(bl2.WriteBytes(toWrite, kSmallWrite));
195
196 bl = std::move(bl2);
197 MOZ_RELEASE_ASSERT(bl2.Size() == 0);
198 MOZ_RELEASE_ASSERT(bl2.Iter().Done());
199
200 iter = bl.Iter();
201 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3));
202 MOZ_RELEASE_ASSERT(iter.Done());
203
204 // MoveFallible
205
206 bool success;
207 bl2 = bl.MoveFallible<InfallibleAllocPolicy>(&success);
208 MOZ_RELEASE_ASSERT(success);
209 MOZ_RELEASE_ASSERT(bl.Size() == 0);
210 MOZ_RELEASE_ASSERT(bl.Iter().Done());
211 MOZ_RELEASE_ASSERT(bl2.Size() == kSmallWrite * 3);
212
213 iter = bl2.Iter();
214 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kSmallWrite * 3));
215 MOZ_RELEASE_ASSERT(iter.Done());
216
217 bl = bl2.MoveFallible<InfallibleAllocPolicy>(&success);
218
219 // Borrowing.
220
221 const size_t kBorrowStart = 4;
222 const size_t kBorrowSize = 24;
223
224 iter = bl.Iter();
225 iter.Advance(bl, kBorrowStart);
226 bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success);
227 MOZ_RELEASE_ASSERT(success);
228 MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize);
229
230 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(
231 bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart));
232 MOZ_RELEASE_ASSERT(iter.Done());
233
234 iter = bl2.Iter();
235 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize));
236 MOZ_RELEASE_ASSERT(iter.Done());
237
238 BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter());
239 iter1.Advance(bl, kBorrowStart);
240 MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
241 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5));
242 MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5));
243 MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data());
244
245 // Extracting.
246
247 const size_t kExtractStart = 8;
248 const size_t kExtractSize = 24;
249 const size_t kExtractOverSize = 1000;
250
251 iter = bl.Iter();
252 iter.Advance(bl, kExtractStart);
253 bl2 = bl.Extract(iter, kExtractSize, &success);
254 MOZ_RELEASE_ASSERT(success);
255 MOZ_RELEASE_ASSERT(bl2.Size() == kExtractSize);
256
257 BufferList bl3 = bl.Extract(iter, kExtractOverSize, &success);
258 MOZ_RELEASE_ASSERT(!success);
259
260 iter = bl2.Iter();
261 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kExtractSize));
262 MOZ_RELEASE_ASSERT(iter.Done());
263
264 BufferList bl4(8, 8, 8);
265 MOZ_ALWAYS_TRUE(bl4.WriteBytes("abcd1234", 8));
266 iter = bl4.Iter();
267 iter.Advance(bl4, 8);
268
269 BufferList bl5 = bl4.Extract(iter, kExtractSize, &success);
270 MOZ_RELEASE_ASSERT(!success);
271
272 BufferList bl6(0, 0, 16);
273 MOZ_ALWAYS_TRUE(bl6.WriteBytes("abcdefgh12345678", 16));
274 MOZ_ALWAYS_TRUE(bl6.WriteBytes("ijklmnop87654321", 16));
275 iter = bl6.Iter();
276 iter.Advance(bl6, 8);
277 BufferList bl7 = bl6.Extract(iter, 16, &success);
278 MOZ_RELEASE_ASSERT(success);
279 char data[16];
280 MOZ_RELEASE_ASSERT(bl6.ReadBytes(iter, data, 8));
281 MOZ_RELEASE_ASSERT(memcmp(data, "87654321", 8) == 0);
282 iter = bl7.Iter();
283 MOZ_RELEASE_ASSERT(bl7.ReadBytes(iter, data, 16));
284 MOZ_RELEASE_ASSERT(memcmp(data, "12345678ijklmnop", 16) == 0);
285
286 BufferList bl8(0, 0, 16);
287 MOZ_ALWAYS_TRUE(bl8.WriteBytes("abcdefgh12345678", 16));
288 iter = bl8.Iter();
289 BufferList bl9 = bl8.Extract(iter, 8, &success);
290 MOZ_RELEASE_ASSERT(success);
291 MOZ_RELEASE_ASSERT(bl9.Size() == 8);
292 MOZ_RELEASE_ASSERT(!iter.Done());
293
294 BufferList bl10(0, 0, 8);
295 MOZ_ALWAYS_TRUE(bl10.WriteBytes("abcdefgh", 8));
296 MOZ_ALWAYS_TRUE(bl10.WriteBytes("12345678", 8));
297 iter = bl10.Iter();
298 BufferList bl11 = bl10.Extract(iter, 16, &success);
299 MOZ_RELEASE_ASSERT(success);
300 MOZ_RELEASE_ASSERT(bl11.Size() == 16);
301 MOZ_RELEASE_ASSERT(iter.Done());
302 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl10, 0));
303 MOZ_RELEASE_ASSERT(iter.Done());
304 iter = bl11.Iter();
305 MOZ_RELEASE_ASSERT(bl11.ReadBytes(iter, data, 16));
306 MOZ_RELEASE_ASSERT(memcmp(data, "abcdefgh12345678", 16) == 0);
307
308 // RangeLength.
309
310 BufferList bl12(0, 0, 8);
311 MOZ_ALWAYS_TRUE(bl12.WriteBytes("abcdefgh", 8));
312 MOZ_ALWAYS_TRUE(bl12.WriteBytes("12345678", 8));
313
314 // |iter| is at position 0 (1st segment).
315 iter = bl12.Iter();
316 iter1 = bl12.Iter();
317 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
318 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
319 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
320 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
321 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 8);
322 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
323 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 12);
324 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 3));
325 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 15);
326 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
327 MOZ_RELEASE_ASSERT(iter1.Done());
328
329 // |iter| is at position 1 (1st segment).
330 iter = bl12.Iter();
331 iter1 = bl12.Iter();
332 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 1));
333 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
334 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
335 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
336 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
337 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
338 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 8);
339 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
340 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 12);
341 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 2));
342 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 14);
343 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
344 MOZ_RELEASE_ASSERT(iter1.Done());
345
346 // |iter| is at position 8 (2nd segment).
347 iter = bl12.Iter();
348 iter1 = bl12.Iter();
349 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 8));
350 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 8));
351 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
352 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
353 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
354 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 3));
355 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 7);
356 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
357 MOZ_RELEASE_ASSERT(iter1.Done());
358
359 // |iter| is at position 9 (2nd segment).
360 iter = bl12.Iter();
361 iter1 = bl12.Iter();
362 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl12, 9));
363 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 9));
364 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 0);
365 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 4));
366 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 4);
367 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 2));
368 MOZ_RELEASE_ASSERT(bl12.RangeLength(iter, iter1) == 6);
369 MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl12, 1));
370 MOZ_RELEASE_ASSERT(iter1.Done());
371
372 BufferList bl13(0, 0, 8);
373 MOZ_ALWAYS_TRUE(bl13.WriteBytes("abcdefgh", 8));
374 MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
375 MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
376 MOZ_RELEASE_ASSERT(bl13.Size() == 24);
377
378 // At segment border
379 iter = bl13.Iter();
380 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 8));
381 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 16);
382 MOZ_RELEASE_ASSERT(iter.Done());
383 MOZ_RELEASE_ASSERT(bl13.Size() == 8);
384
385 // Restore state
386 MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
387 MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
388 MOZ_RELEASE_ASSERT(bl13.Size() == 24);
389
390 // Before segment border
391 iter = bl13.Iter();
392 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 7));
393 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 17);
394 MOZ_RELEASE_ASSERT(iter.Done());
395 MOZ_RELEASE_ASSERT(bl13.Size() == 7);
396
397 // Restore state
398 MOZ_ALWAYS_TRUE(bl13.WriteBytes("h", 1));
399 MOZ_ALWAYS_TRUE(bl13.WriteBytes("12345678", 8));
400 MOZ_ALWAYS_TRUE(bl13.WriteBytes("ABCDEFGH", 8));
401 MOZ_RELEASE_ASSERT(bl13.Size() == 24);
402
403 // In last segment
404 iter = bl13.Iter();
405 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 20));
406 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 4);
407 MOZ_RELEASE_ASSERT(iter.Done());
408 MOZ_RELEASE_ASSERT(bl13.Size() == 20);
409
410 // No-op truncate
411 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
412 MOZ_RELEASE_ASSERT(iter.Done());
413 MOZ_RELEASE_ASSERT(bl13.Size() == 20);
414
415 // No-op truncate with fresh iterator
416 iter = bl13.Iter();
417 MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl13, 20));
418 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
419 MOZ_RELEASE_ASSERT(iter.Done());
420 MOZ_RELEASE_ASSERT(bl13.Size() == 20);
421
422 // Truncate at start of buffer
423 iter = bl13.Iter();
424 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 20);
425 MOZ_RELEASE_ASSERT(iter.Done());
426 MOZ_RELEASE_ASSERT(bl13.Size() == 0);
427
428 // No-op truncate at start of buffer
429 iter = bl13.Iter();
430 MOZ_RELEASE_ASSERT(bl13.Truncate(iter) == 0);
431 MOZ_RELEASE_ASSERT(iter.Done());
432 MOZ_RELEASE_ASSERT(bl13.Size() == 0);
433
434 return 0;
435 }
436