Пример #1
0
        void SetRange(int sectionIndex, int offset, int length, bool desiredFree)
        {
            if (sectionIndex < 0)
            {
                throw new ArgumentException($"{nameof(sectionIndex)} cannot be negative", nameof(sectionIndex));
            }
            if (offset < 0)
            {
                throw new ArgumentException($"{nameof(offset)} cannot be negative", nameof(offset));
            }
            if (length < 0)
            {
                throw new ArgumentException($"{nameof(length)} cannot be negative", nameof(length));
            }
            if (length == 0)
            {
                return;
            }
            if (sectionIndex > sections.Length)
            {
                throw new ArgumentException($"{nameof(sectionIndex)} cannot be higher than number of sections", nameof(sectionIndex));
            }

            // Check if we need to add a new one instead
            if (sectionIndex == sections.Length)
            {
                if (sectionIndex == 0 ||                                // If sections is empty, just add a new section
                    IsSectionFree(sectionIndex - 1) != desiredFree      // If the previous section is different than desiredFree, we need to add a new section too
                    )
                {
                    Debug.Assert(// either this is the first section and offset needs to be 0
                        (sectionIndex == 0 && offset == 0) ||
                        // or it connects to the previous section
                        (sectionIndex > 0 && offset == sections[sectionIndex - 1].start + sections[sectionIndex - 1].length));
                    sections.Add(new Section
                    {
                        start  = offset,
                        length = length
                    });
                    if (sectionIndex == 0)
                    {
                        firstElementFree = desiredFree;
                    }
                    Debug.Assert(IsSectionFree(sectionIndex) == desiredFree);
                }
                else
                {
                    // Otherwise, we can merge our allocation with the previous allocated section ...
                    Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                    var previousSection = sections[sectionIndex - 1];
                    Debug.Assert(previousSection.start + previousSection.length == offset);
                    previousSection.length    += length;
                    sections[sectionIndex - 1] = previousSection;
                }
                return;
            }

            if (IsSectionFree(sectionIndex) == desiredFree)
            {
                if (desiredFree)
                {
                    throw new ArgumentException("Cannot free section because it's already free");
                }
                else
                {
                    throw new ArgumentException("Cannot allocate section because it's already allocated");
                }
            }

            var section = sections[sectionIndex];

            if (offset + length > section.start + section.length)
            {
                throw new IndexOutOfRangeException("Length of requested section to free is larger than found section");
            }

            // Note: Free and allocated sections follow each other since they always get merged
            // with allocated or free sections next to them

            // Check if our section is exactly the free range we need
            if (section.start == offset && section.length == length)
            {
                // Check if our section is the last section in the list, which means there's no next section
                if (sectionIndex + 1 == sections.Length)
                {
                    // If we're the first section, then there's no previous section
                    if (sectionIndex == 0)
                    {
                        // This means this is the only section in the list and we can just modify it
                        sections[sectionIndex] = section;
                        if (sectionIndex == 0)
                        {
                            firstElementFree = desiredFree;
                        }
                    }
                    else
                    {
                        // Otherwise, we can merge our allocation with the previous allocated section ...
                        Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                        var previousSection = sections[sectionIndex - 1];
                        Debug.Assert(previousSection.start + previousSection.length == section.start);
                        previousSection.length    += length;
                        sections[sectionIndex - 1] = previousSection;

                        // ... and remove the last item in the list (the found section)
                        sections.RemoveAt(sectionIndex);
                    }
                }
                else
                // We know that there's a next section.
                {
                    // If we're the first section, then there's no previous section
                    if (sectionIndex == 0)
                    {
                        // Merge our allocation with the next section ...
                        Debug.Assert(IsSectionFree(sectionIndex + 1) == desiredFree);
                        var nextSection = sections[sectionIndex + 1];
                        Debug.Assert(nextSection.start == section.start + section.length);
                        nextSection.start         -= length;
                        nextSection.length        += length;
                        sections[sectionIndex + 1] = nextSection;

                        // ... and remove the first item in the list (the found section)
                        sections.RemoveAt(sectionIndex);

                        firstElementFree = desiredFree;
                    }
                    else
                    {
                        // We have both a previous and a next section, and we can merge all
                        // three sections together into one section
                        Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                        var previousSection = sections[sectionIndex - 1];
                        Debug.Assert(previousSection.start + previousSection.length == section.start);
                        Debug.Assert(IsSectionFree(sectionIndex + 1) == desiredFree);
                        var nextSection = sections[sectionIndex + 1];
                        Debug.Assert(nextSection.start == section.start + section.length);
                        previousSection.length     = previousSection.length + length + nextSection.length;
                        sections[sectionIndex - 1] = previousSection;

                        // ... and we remove the two entries we don't need anymore
                        sections.RemoveRangeWithBeginEnd(sectionIndex, sectionIndex + 2);
                    }
                }
            }
            else
            // If our allocation doesn't match the section exactly, we need to keep a leftover
            {
                var firstSectionLength = offset - section.start;
                Debug.Assert(firstSectionLength >= 0);
                var middleSectionLength = length;
                var lastSectionLength   = (section.start + section.length) - (offset + length);
                Debug.Assert(lastSectionLength >= 0);

                if (firstSectionLength == 0)
                {
                    Debug.Assert(lastSectionLength > 0);

                    if (sectionIndex == 0)
                    {
                        // Modify the existing section to hold the middle section
                        sections[sectionIndex] = new Section
                        {
                            start  = offset,
                            length = middleSectionLength
                        };

                        // Insert a new section behind it to hold the leftover
                        sections.InsertAt(sectionIndex + 1, new Section
                        {
                            start  = offset + middleSectionLength,
                            length = lastSectionLength
                        });

                        firstElementFree = desiredFree;
                    }
                    else
                    {
                        // Modify the existing section to hold the left-over
                        sections[sectionIndex] = new Section
                        {
                            start  = offset + middleSectionLength,
                            length = lastSectionLength
                        };

                        // Merge middle section with the previous section
                        Debug.Assert(IsSectionFree(sectionIndex - 1) == desiredFree);
                        var previousSection = sections[sectionIndex - 1];
                        Debug.Assert(previousSection.start + previousSection.length == section.start);
                        previousSection.length    += middleSectionLength;
                        sections[sectionIndex - 1] = previousSection;
                    }
                }
                else
                if (lastSectionLength == 0)
                {
                    Debug.Assert(firstSectionLength > 0);
                    // Modify the existing section to hold the left-over
                    sections[sectionIndex] = new Section
                    {
                        start  = section.start,
                        length = firstSectionLength
                    };
                    if (sectionIndex == 0)
                    {
                        firstElementFree = !desiredFree;
                    }

                    if (sectionIndex + 1 == sections.Length)
                    {
                        // Insert a new section to hold the middle section
                        sections.InsertAt(sectionIndex + 1, new Section
                        {
                            start  = offset,
                            length = middleSectionLength
                        });
                    }
                    else
                    {
                        // Merge middle section with the next section
                        Debug.Assert(IsSectionFree(sectionIndex + 1) == desiredFree);
                        var nextSection = sections[sectionIndex + 1];
                        Debug.Assert(nextSection.start == section.start + section.length);
                        nextSection.start         -= middleSectionLength;
                        nextSection.length        += middleSectionLength;
                        sections[sectionIndex + 1] = nextSection;
                    }
                }
                else
                {
                    // Modify the existing section to hold the first left-over
                    sections[sectionIndex] = new Section
                    {
                        start  = section.start,
                        length = firstSectionLength
                    };
                    if (sectionIndex == 0)
                    {
                        firstElementFree = !desiredFree;
                    }

                    // Add the middle section
                    sections.InsertAt(sectionIndex + 1, new Section
                    {
                        start  = offset,
                        length = middleSectionLength
                    });

                    var lastSection = new Section
                    {
                        start  = offset + middleSectionLength,
                        length = lastSectionLength
                    };

                    // Add the last left-over
                    sections.InsertAt(sectionIndex + 2, lastSection);
                }
            }
        }