예제 #1
0
        public static void AssignPositions(IList <IResourceBlock> blocks, uint basePosition, out ResourceChunkFlags flags, uint usedPages)
        {
            flags = new ResourceChunkFlags();

            if (blocks.Count <= 0)
            {
                return;
            }

            long largestBlockSize = 0;         // find largest structure
            long startPageSize    = BASE_SIZE; // 0x2000; // find starting page size
            long totalBlockSize   = 0;

            foreach (var block in blocks)
            {
                // Get size of all blocks padded
                var blockLength = block.BlockLength;
                totalBlockSize += blockLength;
                totalBlockSize += Pad(totalBlockSize);

                // Get biggest block
                if (largestBlockSize < blockLength)
                {
                    largestBlockSize = blockLength;
                }
            }

            // Get minimum page size to contain biggest block
            while (startPageSize < largestBlockSize)
            {
                startPageSize *= 2;
            }

            var  pageSizeMult = 1;
            long currentPosition;

            var  sys = (basePosition == 0x50000000);
            bool invalidLayout;

            do
            {
                invalidLayout = false;
                var blockset  = new ResourceBuilderBlockSet(blocks, sys);
                var rootblock = blockset.RootBlock;
                currentPosition = 0L;
                var  currentPageSize  = startPageSize;
                var  currentPageStart = 0L;
                var  currentPageSpace = startPageSize;
                long currentRemainder = totalBlockSize;
                var  bucketIndex      = 0;
                var  targetPageSize   = Math.Max(65536 * pageSizeMult, startPageSize >> (sys ? 5 : 2));
                var  minPageSize      = Math.Max(512 * pageSizeMult, Math.Min(targetPageSize, startPageSize) >> 4);
                var  baseShift        = 0u;
                var  baseSize         = 512;

                while (baseSize < minPageSize)
                {
                    baseShift++;
                    baseSize *= 2;
                    if (baseShift >= 0xF)
                    {
                        break;
                    }
                }

                flags = new ResourceChunkFlags(new uint[9], baseShift);

                var baseSizeMax     = baseSize << 8;
                var baseSizeMaxTest = startPageSize;

                while (baseSizeMaxTest < baseSizeMax)
                {
                    bucketIndex++;
                    baseSizeMaxTest *= 2;
                }

                if (!flags.TryAddChunk(bucketIndex))
                {
                    break;
                }

                while (blockset.Count > 0)
                {
                    var isroot = sys && (currentPosition == 0);
                    var block  = isroot ? rootblock : blockset.GetBestBlock(currentPageSpace);

                    // If there is no block to fit in space left
                    if (block == null)
                    {
                        //allocate a new page
                        currentPageStart += currentPageSize;
                        currentPosition   = currentPageStart;

                        // Get the biggest block
                        block = blockset.GetBestBlock(long.MaxValue);
                        var blockLength = block?.BlockLength ?? 0;

                        // Get the smallest page which can contain the block
                        while (blockLength <= (currentPageSize >> 1))
                        {
                            if (currentPageSize <= minPageSize)
                            {
                                break;
                            }
                            if (bucketIndex >= 8)
                            {
                                break;
                            }
                            if ((currentPageSize <= targetPageSize) && (currentRemainder >= (currentPageSize - minPageSize)))
                            {
                                break;
                            }

                            currentPageSize = currentPageSize >> 1;
                            bucketIndex++;
                        }

                        currentPageSpace = currentPageSize;

                        // Try adding another chunk to this bucket
                        if (!flags.TryAddChunk(bucketIndex))
                        {
                            invalidLayout = true;
                            break;
                        }
                    }

                    //add this block to the current page.
                    block.BlockPosition = basePosition + currentPosition;
                    var opos = currentPosition;
                    currentPosition += block.BlockLength;
                    currentPosition += Pad(currentPosition);
                    var usedspace = currentPosition - opos;
                    currentPageSpace -= usedspace;
                    currentRemainder -= usedspace;
                }

                startPageSize *= 2;
                pageSizeMult  *= 2;
            }while ((invalidLayout) || (flags.Size < totalBlockSize) || (flags.Count + usedPages > 128));
        }