| /* $Id: HGSMIMemAlloc.cpp 92327 2014-02-17 16:02:08Z sunlover $ */ | |
| /** @file | |
| * VBox Host Guest Shared Memory Interface (HGSMI) - Memory allocator. | |
| */ | |
| /* | |
| * Copyright (C) 2014 Oracle Corporation | |
| * | |
| * This file is part of VirtualBox Open Source Edition (OSE), as | |
| * available from http://www.virtualbox.org. This file is free software; | |
| * you can redistribute it and/or modify it under the terms of the GNU | |
| * General Public License (GPL) as published by the Free Software | |
| * Foundation, in version 2 as it comes in the "COPYING" file of the | |
| * VirtualBox OSE distribution. VirtualBox OSE is distributed in the | |
| * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. | |
| */ | |
| /* | |
| * Memory allocator | |
| * ---------------- | |
| * | |
| * Area [0; AreaSize) contains only the data, control structures are separate. | |
| * Block sizes are power of 2: 32B, ..., 1MB | |
| * Area size can be anything and will be divided initially to largest possible free blocks. | |
| * | |
| * The entire area is described by a list of 32 bit block descriptors: | |
| * * bits 0..3 - order, which is log2 size of the block - 5: 2^(0+5) ... 2^(15+5) == 32B .. 1MB | |
| * * bit 4 - 1 for free blocks. | |
| * * bits 5..31 - block offset. | |
| * | |
| * 31 ... 5 | 4 | 3 ... 0 | |
| * offset F order | |
| * | |
| * There is a sorted collection of all block descriptors | |
| * (key is the block offset, bits 0...4 do not interfere with sorting). | |
| * Also there are lists of free blocks for each size for fast allocation. | |
| * | |
| * | |
| * Implementation | |
| * -------------- | |
| * | |
| * The blocks collection is a sorted linear list. | |
| * | |
| * Initially the entire area consists of one or more largest blocks followed by smaller blocks: | |
| * * 100B area - 64B block with descriptor: 0x00000011 | |
| * 32B block with descriptor: 0x00000030 | |
| * 4B unused | |
| * * 64K area - one 64K block with descriptor: 0x0000001C | |
| * * 512K area - one 512K block with descriptor: 0x0000001F | |
| * | |
| * When allocating a new block: | |
| * * larger free blocks are splitted when there are no smaller free blocks; | |
| * * smaller free blocks are merged if they can build a requested larger block. | |
| */ | |
| #include <VBox/HGSMI/HGSMIMemAlloc.h> | |
| #include <VBox/HGSMI/HGSMI.h> | |
| #include <iprt/err.h> | |
| #include <iprt/string.h> | |
| DECLINLINE(HGSMIOFFSET) hgsmiMADescriptor(HGSMIOFFSET off, bool fFree, HGSMIOFFSET order) | |
| { | |
| return (off & HGSMI_MA_DESC_OFFSET_MASK) | | |
| (fFree? HGSMI_MA_DESC_FREE_MASK: 0) | | |
| (order & HGSMI_MA_DESC_ORDER_MASK); | |
| } | |
| static void hgsmiMABlockFree(HGSMIMADATA *pMA, HGSMIMABLOCK *pBlock) | |
| { | |
| pMA->env.pfnFree(pMA->env.pvEnv, pBlock); | |
| } | |
| static int hgsmiMABlockAlloc(HGSMIMADATA *pMA, HGSMIMABLOCK **ppBlock) | |
| { | |
| int rc = VINF_SUCCESS; | |
| HGSMIMABLOCK *pBlock = (HGSMIMABLOCK *)pMA->env.pfnAlloc(pMA->env.pvEnv, sizeof(HGSMIMABLOCK)); | |
| if (pBlock) | |
| { | |
| RT_ZERO(pBlock->nodeBlock); | |
| *ppBlock = pBlock; | |
| } | |
| else | |
| { | |
| rc = VERR_NO_MEMORY; | |
| } | |
| return rc; | |
| } | |
| /* Divide entire area to free blocks. */ | |
| static int hgsmiMAFormat(HGSMIMADATA *pMA) | |
| { | |
| int rc = VINF_SUCCESS; | |
| /* Initial value, it will be updated in the loop below. */ | |
| pMA->cbMaxBlock = HGSMI_MA_BLOCK_SIZE_MIN; | |
| pMA->cBlocks = 0; | |
| HGSMISIZE cbBlock = HGSMI_MA_BLOCK_SIZE_MAX; | |
| HGSMISIZE cbRemaining = pMA->area.cbArea; | |
| HGSMIOFFSET off = 0; | |
| while (cbBlock >= HGSMI_MA_BLOCK_SIZE_MIN) | |
| { | |
| /* Build a list of free memory blocks with u32BlockSize. */ | |
| uint32_t cBlocks = cbRemaining / cbBlock; | |
| if (cBlocks > 0) | |
| { | |
| if (pMA->cbMaxBlock < cbBlock) | |
| { | |
| pMA->cbMaxBlock = cbBlock; | |
| } | |
| HGSMIOFFSET order = HGSMIMASize2Order(cbBlock); | |
| uint32_t i; | |
| for (i = 0; i < cBlocks; ++i) | |
| { | |
| /* A new free block. */ | |
| HGSMIMABLOCK *pBlock; | |
| rc = hgsmiMABlockAlloc(pMA, &pBlock); | |
| if (RT_FAILURE(rc)) | |
| { | |
| break; | |
| } | |
| pBlock->descriptor = hgsmiMADescriptor(off, true, order); | |
| RTListAppend(&pMA->listBlocks, &pBlock->nodeBlock); | |
| ++pMA->cBlocks; | |
| off += cbBlock; | |
| cbRemaining -= cbBlock; | |
| } | |
| } | |
| if (RT_FAILURE(rc)) | |
| { | |
| break; | |
| } | |
| cbBlock /= 2; | |
| } | |
| return rc; | |
| } | |
| static int hgsmiMARebuildFreeLists(HGSMIMADATA *pMA) | |
| { | |
| int rc = VINF_SUCCESS; | |
| HGSMIMABLOCK *pIter; | |
| RTListForEach(&pMA->listBlocks, pIter, HGSMIMABLOCK, nodeBlock) | |
| { | |
| if (HGSMI_MA_DESC_IS_FREE(pIter->descriptor)) | |
| { | |
| HGSMIOFFSET order = HGSMI_MA_DESC_ORDER(pIter->descriptor); | |
| RTListAppend(&pMA->aListFreeBlocks[order], &pIter->nodeFree); | |
| } | |
| } | |
| return rc; | |
| } | |
| static int hgsmiMARestore(HGSMIMADATA *pMA, HGSMIOFFSET *paDescriptors, uint32_t cDescriptors, HGSMISIZE cbMaxBlock) | |
| { | |
| int rc = VINF_SUCCESS; | |
| pMA->cbMaxBlock = cbMaxBlock; | |
| pMA->cBlocks = 0; | |
| HGSMISIZE cbRemaining = pMA->area.cbArea; | |
| HGSMIOFFSET off = 0; | |
| uint32_t i; | |
| for (i = 0; i < cDescriptors; ++i) | |
| { | |
| /* Verify the descriptor. */ | |
| HGSMISIZE cbBlock = HGSMIMAOrder2Size(HGSMI_MA_DESC_ORDER(paDescriptors[i])); | |
| if ( off != HGSMI_MA_DESC_OFFSET(paDescriptors[i]) | |
| || cbBlock > cbRemaining | |
| || cbBlock > pMA->cbMaxBlock) | |
| { | |
| rc = VERR_INVALID_PARAMETER; | |
| break; | |
| } | |
| /* A new free block. */ | |
| HGSMIMABLOCK *pBlock; | |
| rc = hgsmiMABlockAlloc(pMA, &pBlock); | |
| if (RT_FAILURE(rc)) | |
| { | |
| break; | |
| } | |
| pBlock->descriptor = paDescriptors[i]; | |
| RTListAppend(&pMA->listBlocks, &pBlock->nodeBlock); | |
| ++pMA->cBlocks; | |
| off += cbBlock; | |
| cbRemaining -= cbBlock; | |
| } | |
| return rc; | |
| } | |
| static HGSMIMABLOCK *hgsmiMAGetFreeBlock(HGSMIMADATA *pMA, HGSMIOFFSET order) | |
| { | |
| HGSMIMABLOCK *pBlock = NULL; | |
| HGSMIOFFSET i; | |
| for (i = order; i < RT_ELEMENTS(pMA->aListFreeBlocks); ++i) | |
| { | |
| pBlock = RTListGetFirst(&pMA->aListFreeBlocks[i], HGSMIMABLOCK, nodeFree); | |
| if (pBlock) | |
| { | |
| break; | |
| } | |
| } | |
| if (pBlock) | |
| { | |
| AssertReturn(HGSMI_MA_DESC_IS_FREE(pBlock->descriptor), NULL); | |
| /* Where the block starts. */ | |
| HGSMIOFFSET off = HGSMI_MA_DESC_OFFSET(pBlock->descriptor); | |
| /* 'i' is the order of the block. */ | |
| while (i != order) | |
| { | |
| /* A larger block was found and need to be split to 2 smaller blocks. */ | |
| HGSMIMABLOCK *pBlock2; | |
| int rc = hgsmiMABlockAlloc(pMA, &pBlock2); | |
| if (RT_FAILURE(rc)) | |
| { | |
| pBlock = NULL; | |
| break; | |
| } | |
| /* Create 2 blocks with descreased order. */ | |
| --i; | |
| /* Remove from the free list. */ | |
| RTListNodeRemove(&pBlock->nodeFree); | |
| pBlock->descriptor = hgsmiMADescriptor(off, true, i); | |
| pBlock2->descriptor = hgsmiMADescriptor(off + HGSMIMAOrder2Size(i), true, i); | |
| /* Update list of all blocks by inserting pBlock2 after pBlock. */ | |
| RTListNodeInsertAfter(&pBlock->nodeBlock, &pBlock2->nodeBlock); | |
| ++pMA->cBlocks; | |
| /* Update the free list. */ | |
| RTListAppend(&pMA->aListFreeBlocks[i], &pBlock->nodeFree); | |
| RTListAppend(&pMA->aListFreeBlocks[i], &pBlock2->nodeFree); | |
| } | |
| } | |
| return pBlock; | |
| } | |
| static void hgsmiMAReformatFreeBlocks(HGSMIMADATA *pMA, HGSMIOFFSET maxId, | |
| HGSMIMABLOCK *pStart, HGSMIMABLOCK *pEnd, HGSMISIZE cbBlocks) | |
| { | |
| int rc = VINF_SUCCESS; | |
| /* | |
| * Blocks starting from pStart until pEnd will be replaced with | |
| * another set of blocks. | |
| * | |
| * The new set will include the block with the required order. | |
| * Since the required order is larger than any existing block, | |
| * it will replace at least two existing blocks. | |
| * The new set will also have minimal possible number of blocks. | |
| * Therefore the new set will have at least one block less. | |
| * Blocks will be updated in place and remaining blocks will be | |
| * deallocated. | |
| */ | |
| HGSMISIZE u32BlockSize = HGSMIMAOrder2Size(maxId); | |
| HGSMISIZE cbRemaining = cbBlocks; | |
| HGSMIOFFSET off = HGSMI_MA_DESC_OFFSET(pStart->descriptor); | |
| HGSMIMABLOCK *pBlock = pStart; | |
| while (u32BlockSize >= HGSMI_MA_BLOCK_SIZE_MIN && cbRemaining) | |
| { | |
| /* Build a list of free memory blocks with u32BlockSize. */ | |
| uint32_t cBlocks = cbRemaining / u32BlockSize; | |
| if (cBlocks > 0) | |
| { | |
| HGSMIOFFSET order = HGSMIMASize2Order(u32BlockSize); | |
| uint32_t i; | |
| for (i = 0; i < cBlocks; ++i) | |
| { | |
| if (pBlock == pEnd) | |
| { | |
| /* Should never happen because the new set of blocks is supposed to be smaller. */ | |
| AssertFailed(); | |
| rc = VERR_OUT_OF_RESOURCES; | |
| break; | |
| } | |
| /* Remove from the free list. */ | |
| RTListNodeRemove(&pBlock->nodeFree); | |
| pBlock->descriptor = hgsmiMADescriptor(off, true, order); | |
| RTListAppend(&pMA->aListFreeBlocks[order], &pBlock->nodeFree); | |
| off += u32BlockSize; | |
| cbRemaining -= u32BlockSize; | |
| pBlock = RTListGetNext(&pMA->listBlocks, pBlock, HGSMIMABLOCK, nodeBlock); | |
| } | |
| } | |
| if (RT_FAILURE(rc)) | |
| { | |
| break; | |
| } | |
| u32BlockSize /= 2; | |
| } | |
| Assert(cbRemaining == 0); | |
| if (RT_SUCCESS(rc)) | |
| { | |
| /* Remove remaining free blocks from pBlock until pEnd */ | |
| for (;;) | |
| { | |
| bool fEnd = (pBlock == pEnd); | |
| HGSMIMABLOCK *pNext = RTListGetNext(&pMA->listBlocks, pBlock, HGSMIMABLOCK, nodeBlock); | |
| RTListNodeRemove(&pBlock->nodeFree); | |
| RTListNodeRemove(&pBlock->nodeBlock); | |
| --pMA->cBlocks; | |
| hgsmiMABlockFree(pMA, pBlock); | |
| if (fEnd) | |
| { | |
| break; | |
| } | |
| pBlock = pNext; | |
| } | |
| } | |
| } | |
| static void hgsmiMAQueryFreeRange(HGSMIMADATA *pMA, HGSMIMABLOCK *pBlock, HGSMISIZE cbRequired, | |
| HGSMIMABLOCK **ppStart, HGSMIMABLOCK **ppEnd, HGSMISIZE *pcbBlocks) | |
| { | |
| Assert(HGSMI_MA_DESC_IS_FREE(pBlock->descriptor)); | |
| *pcbBlocks = HGSMIMAOrder2Size(HGSMI_MA_DESC_ORDER(pBlock->descriptor)); | |
| *ppStart = pBlock; | |
| *ppEnd = pBlock; | |
| HGSMIMABLOCK *p; | |
| for (;;) | |
| { | |
| p = RTListGetNext(&pMA->listBlocks, *ppEnd, HGSMIMABLOCK, nodeBlock); | |
| if (!p || !HGSMI_MA_DESC_IS_FREE(p->descriptor)) | |
| { | |
| break; | |
| } | |
| *pcbBlocks += HGSMIMAOrder2Size(HGSMI_MA_DESC_ORDER(p->descriptor)); | |
| *ppEnd = p; | |
| if (cbRequired && *pcbBlocks >= cbRequired) | |
| { | |
| return; | |
| } | |
| } | |
| for (;;) | |
| { | |
| p = RTListGetPrev(&pMA->listBlocks, *ppStart, HGSMIMABLOCK, nodeBlock); | |
| if (!p || !HGSMI_MA_DESC_IS_FREE(p->descriptor)) | |
| { | |
| break; | |
| } | |
| *pcbBlocks += HGSMIMAOrder2Size(HGSMI_MA_DESC_ORDER(p->descriptor)); | |
| *ppStart = p; | |
| if (cbRequired && *pcbBlocks >= cbRequired) | |
| { | |
| return; | |
| } | |
| } | |
| } | |
| static void hgsmiMAMergeFreeBlocks(HGSMIMADATA *pMA, HGSMIOFFSET order) | |
| { | |
| /* Try to create a free block with the order from smaller free blocks. */ | |
| if (order == 0) | |
| { | |
| /* No smaller blocks. */ | |
| return; | |
| } | |
| HGSMISIZE cbRequired = HGSMIMAOrder2Size(order); | |
| /* Scan all free lists of smaller blocks. | |
| * | |
| * Get the sequence of free blocks before and after each free block. | |
| * If possible, re-split the sequence to get the required block and other free block(s). | |
| * If not possible, try the next free block. | |
| * | |
| * Free blocks are scanned from i to 0 orders. | |
| */ | |
| HGSMIOFFSET i = order - 1; | |
| for (;;) | |
| { | |
| HGSMIMABLOCK *pIter; | |
| RTListForEach(&pMA->aListFreeBlocks[i], pIter, HGSMIMABLOCK, nodeFree) | |
| { | |
| Assert(HGSMI_MA_DESC_ORDER(pIter->descriptor) == i); | |
| HGSMISIZE cbBlocks; | |
| HGSMIMABLOCK *pFreeStart; | |
| HGSMIMABLOCK *pFreeEnd; | |
| hgsmiMAQueryFreeRange(pMA, pIter, cbRequired, &pFreeStart, &pFreeEnd, &cbBlocks); | |
| Assert((cbBlocks / HGSMI_MA_BLOCK_SIZE_MIN) * HGSMI_MA_BLOCK_SIZE_MIN == cbBlocks); | |
| /* Verify whether cbBlocks is enough for the requested block. */ | |
| if (cbBlocks >= cbRequired) | |
| { | |
| /* Build new free blocks starting from the requested. */ | |
| hgsmiMAReformatFreeBlocks(pMA, order, pFreeStart, pFreeEnd, cbBlocks); | |
| i = 0; /* Leave the loop. */ | |
| break; | |
| } | |
| } | |
| if (i == 0) | |
| { | |
| break; | |
| } | |
| --i; | |
| } | |
| } | |
| static HGSMIOFFSET hgsmiMAAlloc(HGSMIMADATA *pMA, HGSMISIZE cb) | |
| { | |
| if (cb > pMA->cbMaxBlock) | |
| { | |
| return HGSMIOFFSET_VOID; | |
| } | |
| if (cb < HGSMI_MA_BLOCK_SIZE_MIN) | |
| { | |
| cb = HGSMI_MA_BLOCK_SIZE_MIN; | |
| } | |
| HGSMIOFFSET order = HGSMIPopCnt32(cb - 1) - HGSMI_MA_DESC_ORDER_BASE; | |
| AssertReturn(HGSMIMAOrder2Size(order) >= cb, HGSMIOFFSET_VOID); | |
| AssertReturn(order < RT_ELEMENTS(pMA->aListFreeBlocks), HGSMIOFFSET_VOID); | |
| HGSMIMABLOCK *pBlock = hgsmiMAGetFreeBlock(pMA, order); | |
| if (RT_UNLIKELY(pBlock == NULL)) | |
| { | |
| /* No free block with large enough size. Merge smaller free blocks and try again. */ | |
| hgsmiMAMergeFreeBlocks(pMA, order); | |
| pBlock = hgsmiMAGetFreeBlock(pMA, order); | |
| } | |
| if (RT_LIKELY(pBlock != NULL)) | |
| { | |
| RTListNodeRemove(&pBlock->nodeFree); | |
| pBlock->descriptor &= ~HGSMI_MA_DESC_FREE_MASK; | |
| return HGSMI_MA_DESC_OFFSET(pBlock->descriptor); | |
| } | |
| return HGSMIOFFSET_VOID; | |
| } | |
| static void hgsmiMAFree(HGSMIMADATA *pMA, HGSMIOFFSET off) | |
| { | |
| if (off == HGSMIOFFSET_VOID) | |
| { | |
| return; | |
| } | |
| /* Find the block corresponding to the offset. */ | |
| Assert((off / HGSMI_MA_BLOCK_SIZE_MIN) * HGSMI_MA_BLOCK_SIZE_MIN == off); | |
| HGSMIMABLOCK *pBlock = HGSMIMASearchOffset(pMA, off); | |
| if (pBlock) | |
| { | |
| if (HGSMI_MA_DESC_OFFSET(pBlock->descriptor) == off) | |
| { | |
| /* Found the right block, mark it as free. */ | |
| pBlock->descriptor |= HGSMI_MA_DESC_FREE_MASK; | |
| RTListAppend(&pMA->aListFreeBlocks[HGSMI_MA_DESC_ORDER(pBlock->descriptor)], &pBlock->nodeFree); | |
| return; | |
| } | |
| } | |
| AssertFailed(); | |
| } | |
| int HGSMIMAInit(HGSMIMADATA *pMA, const HGSMIAREA *pArea, | |
| HGSMIOFFSET *paDescriptors, uint32_t cDescriptors, HGSMISIZE cbMaxBlock, | |
| const HGSMIENV *pEnv) | |
| { | |
| AssertReturn(pArea->cbArea < UINT32_C(0x80000000), VERR_INVALID_PARAMETER); | |
| AssertReturn(pArea->cbArea >= HGSMI_MA_BLOCK_SIZE_MIN, VERR_INVALID_PARAMETER); | |
| RT_ZERO(*pMA); | |
| HGSMISIZE cb = (pArea->cbArea / HGSMI_MA_BLOCK_SIZE_MIN) * HGSMI_MA_BLOCK_SIZE_MIN; | |
| int rc = HGSMIAreaInitialize(&pMA->area, pArea->pu8Base, cb, 0); | |
| if (RT_SUCCESS(rc)) | |
| { | |
| pMA->env = *pEnv; | |
| uint32_t i; | |
| for (i = 0; i < RT_ELEMENTS(pMA->aListFreeBlocks); ++i) | |
| { | |
| RTListInit(&pMA->aListFreeBlocks[i]); | |
| } | |
| RTListInit(&pMA->listBlocks); | |
| if (cDescriptors) | |
| { | |
| rc = hgsmiMARestore(pMA, paDescriptors, cDescriptors, cbMaxBlock); | |
| } | |
| else | |
| { | |
| rc = hgsmiMAFormat(pMA); | |
| } | |
| if (RT_SUCCESS(rc)) | |
| { | |
| rc = hgsmiMARebuildFreeLists(pMA); | |
| } | |
| } | |
| return rc; | |
| } | |
| void HGSMIMAUninit(HGSMIMADATA *pMA) | |
| { | |
| HGSMIMABLOCK *pIter; | |
| HGSMIMABLOCK *pNext; | |
| RTListForEachSafe(&pMA->listBlocks, pIter, pNext, HGSMIMABLOCK, nodeBlock) | |
| { | |
| RTListNodeRemove(&pIter->nodeBlock); | |
| hgsmiMABlockFree(pMA, pIter); | |
| } | |
| RT_ZERO(*pMA); | |
| } | |
| HGSMIOFFSET HGSMIMAPointerToOffset(const HGSMIMADATA *pMA, const void *pv) | |
| { | |
| if (HGSMIAreaContainsPointer(&pMA->area, pv)) | |
| { | |
| return HGSMIPointerToOffset(&pMA->area, pv); | |
| } | |
| AssertFailed(); | |
| return HGSMIOFFSET_VOID; | |
| } | |
| void *HGSMIMAOffsetToPointer(const HGSMIMADATA *pMA, HGSMIOFFSET off) | |
| { | |
| if (HGSMIAreaContainsOffset(&pMA->area, off)) | |
| { | |
| return HGSMIOffsetToPointer(&pMA->area, off); | |
| } | |
| AssertFailed(); | |
| return NULL; | |
| } | |
| void *HGSMIMAAlloc(HGSMIMADATA *pMA, HGSMISIZE cb) | |
| { | |
| HGSMIOFFSET off = hgsmiMAAlloc(pMA, cb); | |
| return HGSMIMAOffsetToPointer(pMA, off); | |
| } | |
| void HGSMIMAFree(HGSMIMADATA *pMA, void *pv) | |
| { | |
| HGSMIOFFSET off = HGSMIMAPointerToOffset(pMA, pv); | |
| if (off != HGSMIOFFSET_VOID) | |
| { | |
| hgsmiMAFree(pMA, off); | |
| } | |
| else | |
| { | |
| AssertFailed(); | |
| } | |
| } | |
| HGSMIMABLOCK *HGSMIMASearchOffset(HGSMIMADATA *pMA, HGSMIOFFSET off) | |
| { | |
| /* Binary search in the block list for the offset. */ | |
| HGSMIMABLOCK *pStart = RTListGetFirst(&pMA->listBlocks, HGSMIMABLOCK, nodeBlock); | |
| HGSMIMABLOCK *pEnd = RTListGetLast(&pMA->listBlocks, HGSMIMABLOCK, nodeBlock); | |
| HGSMIMABLOCK *pMiddle; | |
| uint32_t iStart = 0; | |
| uint32_t iEnd = pMA->cBlocks; | |
| uint32_t iMiddle; | |
| for (;;) | |
| { | |
| pMiddle = pStart; | |
| iMiddle = iStart + (iEnd - iStart) / 2; | |
| if (iMiddle == iStart) | |
| { | |
| break; | |
| } | |
| /* Find the block with the iMiddle index. Never go further than pEnd. */ | |
| uint32_t i; | |
| for (i = iStart; i < iMiddle && pMiddle != pEnd; ++i) | |
| { | |
| pMiddle = RTListNodeGetNext(&pMiddle->nodeBlock, HGSMIMABLOCK, nodeBlock); | |
| } | |
| HGSMIOFFSET offMiddle = HGSMI_MA_DESC_OFFSET(pMiddle->descriptor); | |
| if (offMiddle > off) | |
| { | |
| pEnd = pMiddle; | |
| iEnd = iMiddle; | |
| } | |
| else | |
| { | |
| pStart = pMiddle; | |
| iStart = iMiddle; | |
| } | |
| } | |
| return pMiddle; | |
| } | |
| /* | |
| * Helper. | |
| */ | |
| uint32_t HGSMIPopCnt32(uint32_t u32) | |
| { | |
| uint32_t c = 0; | |
| if (u32 > 0xFFFF) { c += 16; u32 >>= 16; } | |
| if (u32 > 0xFF) { c += 8; u32 >>= 8; } | |
| if (u32 > 0xF) { c += 4; u32 >>= 4; } | |
| if (u32 > 0x3) { c += 2; u32 >>= 2; } | |
| if (u32 > 0x1) { c += 1; u32 >>= 1; } | |
| return c + u32; | |
| } |