diff --git a/source/libs/tdb/src/db/tdbPage.c b/source/libs/tdb/src/db/tdbPage.c index 9072b2d146..0f8eced76a 100644 --- a/source/libs/tdb/src/db/tdbPage.c +++ b/source/libs/tdb/src/db/tdbPage.c @@ -15,7 +15,13 @@ #include "tdbInt.h" +typedef struct __attribute__((__packed__)) { + u16 size; + u16 nOffset; +} SFreeCell; + static int tdbPageAllocate(SPage *pPage, int size, SCell **ppCell); +static int tdbPageDefragment(SPage *pPage); int tdbPageCreate(int pageSize, SPage **ppPage, void *(*xMalloc)(void *, size_t), void *arg) { SPage *pPage; @@ -66,7 +72,7 @@ int tdbPageInsertCell(SPage *pPage, int idx, SCell *pCell, int szCell) { SCell *pTarget; if (pPage->nOverflow || szCell + pPage->szOffset > pPage->nFree) { - // TODO + // TODO: Page is full } else { ret = tdbPageAllocate(pPage, szCell, &pTarget); if (ret < 0) { @@ -87,18 +93,63 @@ int tdbPageDropCell(SPage *pPage, int idx) { } static int tdbPageAllocate(SPage *pPage, int size, SCell **ppCell) { - SCell *pCell; + SCell *pCell; + SFreeCell *pFreeCell; + int ret; ASSERT(pPage->nFree > size + pPage->szOffset); + pCell = NULL; + *ppCell = NULL; + + // 1. Try to allocate from the free space area if (pPage->pFreeEnd - pPage->pFreeStart > size + pPage->szOffset) { pPage->pFreeEnd -= size; pPage->pFreeStart += pPage->szOffset; pCell = pPage->pFreeEnd; - } else { } + // 2. Try to allocate from the page free list + if (pCell == NULL && TDB_PAGE_FCELL(pPage)) { + pCell = pPage->pData + TDB_PAGE_FCELL(pPage); + for (;;) { + pFreeCell = (SFreeCell *)pCell; + + if (pFreeCell->size >= size) { + break; + } + + if (pFreeCell->nOffset) { + pCell = pPage->pData + pFreeCell->nOffset; + } else { + pCell = NULL; + break; + } + + continue; + } + } + + // 3. Try to dfragment + if (pCell == NULL) { + ret = tdbPageDefragment(pPage); + if (ret < 0) { + return -1; + } + + ASSERT(pPage->pFreeEnd - pPage->pFreeStart > size + pPage->szOffset); + ASSERT(pPage->nFree == pPage->pFreeEnd - pPage->pFreeStart); + + // Allocate from the free space area again + pPage->pFreeEnd -= size; + pPage->pFreeStart += pPage->szOffset; + pCell = pPage->pFreeEnd; + } + + ASSERT(pCell != NULL); + + pPage->nFree = pPage->nFree - size - pPage->szOffset; *ppCell = pCell; return 0; } diff --git a/source/libs/tdb/src/inc/tdbPage.h b/source/libs/tdb/src/inc/tdbPage.h index 70d70dbff5..6c85ebd643 100644 --- a/source/libs/tdb/src/inc/tdbPage.h +++ b/source/libs/tdb/src/inc/tdbPage.h @@ -25,10 +25,10 @@ typedef u8 SCell; // Page header (pageSize < 65536 (64K)) typedef struct __attribute__((__packed__)) { u16 flags; - u16 nCells; - u16 cCells; - u16 fCell; - u16 nFree; + u16 nCells; // number of cells + u16 cCells; // cell content offset + u16 fCell; // first free cell offset + u16 nFree; // total fragment bytes in this page } SPageHdr; // Large page header (pageSize >= 65536 (64K))