private void AllocateNextSegment(int required, bool allowGrowth) { Debug.Assert(required > 0); // Grow by doubling segment size until we get to 1 MB, then just use 1 MB segments // otherwise a document with 17 MB will waste 15 MB and require very big allocations var powerOfTwoRequestSize = Bits.NextPowerOf2(required); var segmentSize = Math.Max(powerOfTwoRequestSize, _head.Allocation.SizeInBytes * 2); if (powerOfTwoRequestSize < segmentSize || segmentSize > ArenaMemoryAllocator.MaxArenaSize) { // don't grow _too_ much. segmentSize = powerOfTwoRequestSize; } const int oneMb = 1024 * 1024; if (segmentSize > oneMb && required <= oneMb) { segmentSize = oneMb; } // We can sometimes ask the context to grow the allocation size; // it may do so at its own discretion; if this happens, then we // are good to go. if (allowGrowth && _context.GrowAllocation(_head.Allocation, segmentSize)) { return; } // Can't change _head because there may be copies of the current // instance of UnmanagedWriteBuffer going around. Thus, we simply // mutate it to ensure all copies have the same allocations. var allocation = _context.GetMemory(segmentSize); // Copy the head Segment previousHead = _head.ShallowCopy(); // Reset the head (this change happens in all instances at the // same time, albeit not atomically). _head.Previous = previousHead; _head.DeallocationPendingPrevious = previousHead; _head.Allocation = allocation; _head.Address = allocation.Address; _head.Used = 0; _head.AccumulatedSizeInBytes = previousHead.AccumulatedSizeInBytes; }