blob: d138796dab58d5be4356d67ff33becd7d75f9fba [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*-------------------------------------------------------------------------
*
* cdbcellbuf.h
* a container for small items of constant size
*
*-------------------------------------------------------------------------
*/
#ifndef CDBCELLBUF_H
#define CDBCELLBUF_H
#include "cdb/cdbpublic.h" /* CdbCellBuf */
/* Private helper functions needed by inline functions below... */
void *CdbCellBuf_AppendMore(CdbCellBuf *cellbuf);
void *CdbCellBuf_PopMore(CdbCellBuf *cellbuf);
void *CdbCellBuf_LastCellHelper(CdbCellBuf *cellbuf);
/*-------------------------------------------------------------------------
* CdbCellBuf_Iterator
*-------------------------------------------------------------------------
* For iterating over the cells of a CdbCellBuf.
*/
typedef struct CdbCellBuf_Iterator
{
char *cnext; /* next cell */
char *cend; /* end of last cell of current bunch */
CdbCellBuf *cellbuf;
char *cbeg; /* first cell of current bunch */
long begoff; /* total size (bytes) of all cells in
* bunches preceding current bunch */
} CdbCellBuf_Iterator;
/* Private helper functions needed by inline functions below... */
char *CdbCellBuf_Iterator_NextCellHelper(CdbCellBuf_Iterator *it);
char *CdbCellBuf_Iterator_PrevCellHelper(CdbCellBuf_Iterator *it);
char *CdbCellBuf_Iterator_CellAtHelper(CdbCellBuf_Iterator *it, long relCellOff);
void CdbCellBuf_Iterator_SetPosHelper(CdbCellBuf_Iterator *it, long nextCellOff);
/*-------------------------------------------------------------------------
* CdbCellBuf_Bumper
*-------------------------------------------------------------------------
* Each dynamically allocated expansion bunch of cells begins and ends with
* a bumper. At the beginning is a prefix bumper whose 'link' field points
* to the prefix bumper of the previous expansion bunch (or NULL in the first
* expansion bunch). At the end of the bunch is a suffix bumper whose 'link'
* field points to the prefix bumper of the next expansion bunch. The suffix
* bumper of the last expansion bunch is uninitialized. Between the prefix
* and suffix bumper are cells in the quantity 'cellbuf->num_cells_expand'.
*/
typedef struct CdbCellBuf_Bumper
{
CdbCellBuf *cellbuf; /* owner of bunch */
struct CdbCellBuf_Bumper *link; /* prefix: -> prev bunch pfx or NULL.
* suffix: -> prefix of next bunch.
*/
} CdbCellBuf_Bumper;
#define CDBCELLBUF_BUMPER_BYTES (MAXALIGN(sizeof(CdbCellBuf_Bumper)))
/*-------------------------------------------------------------------------
* Initialize/Reset caller's CdbCellBuf
*-------------------------------------------------------------------------
*/
/*
* CdbCellBuf_InitEasy
*
* Initialize caller's CdbCellBuf struct (easy-to-call version).
*
* 'cellbuf' is the area to be initialized as a CdbCellBuf.
* 'cellbytes' is the size of each cell.
*/
void
CdbCellBuf_InitEasy(CdbCellBuf *cellbuf, Size cellbytes);
/*
* CdbCellBuf_Init
*
* Initialize caller's CdbCellBuf struct.
*
* 'cellbuf' is the area to be initialized as a CdbCellBuf.
* 'cellbytes' is the size of each cell.
* 'initial_cell_area' is an optional memory area, properly aligned, where
* the first few cells can be stored. It must be nearby the CdbCellBuf
* itself, typically in the same struct or local variable stack frame.
* This allows a small number of cells to be managed with good locality
* and no palloc/pfree. If NULL, cell memory is allocated from 'context'
* when needed.
* 'num_initial_cells' is the exact number of cells in the initial_cell_area.
* 'num_cells_expand' is the suggested number of cell slots to be allocated
* whenever additional memory is needed. The actual number will be
* rounded to fully utilize a power-of-2 chunk. However, no expansion
* is done if num_cells_expand is zero; instead, after the initial
* cells are exhausted an error is raised.
* 'context' is the memory context from which to allocate space for
* additional cells when there is no more room in initial_cell_area.
* If NULL, the CurrentMemoryContext is used.
*/
void
CdbCellBuf_Init(CdbCellBuf *cellbuf,
size_t cellbytes,
void *initial_cell_area,
int num_initial_cells,
int num_cells_expand,
MemoryContext context);
/*
* CdbCellBuf_Reset
*
* Free memory held by a cellbuf. Does not free the CdbCellBuf struct.
*
* Should be used to clean up cellbufs initialized by CdbCellBuf_Init()
* or CdbCellBuf_InitEasy(). May be used to empty any cellbuf for reuse.
*
* (NB: An acceptable alternative for cleaning up would be to destroy or
* reset the associated MemoryContext.)
*
* Iterators (CdbCellBuf_Iterator) become invalid when their cellbuf is
* reset; afterward the caller should abandon or reinitialize them.
*/
void
CdbCellBuf_Reset(CdbCellBuf *cellbuf);
/*-------------------------------------------------------------------------
* Create/Destroy dynamically allocated CdbCellBuf
*-------------------------------------------------------------------------
*/
/*
* CdbCellBuf_Create
*
* Returns a newly allocated CdbCellBuf.
*
* 'cellbytes' is the size of each cell.
* 'num_initial_cells' is the suggested number of cell slots to be created
* immediately following the CdbCellBuf. This allows a small number
* of cells to be managed with good locality and no palloc/pfree.
* If NULL, all cells are stored in chunks allocated from 'context'.
* The actual number will be rounded up to minimize wasted space.
* 'num_cells_expand' is the suggested number of cell slots to be allocated
* whenever additional memory is needed. The actual number will be
* rounded to fully utilize a power-of-2 chunk. However, no expansion
* is done if num_cells_expand is zero; instead, after the initial
* cells are exhausted an error is raised.
* 'context' is the memory context from which the CdbCellBuf and cells are
* to be allocated. If NULL, the CurrentMemoryContext is used.
*/
CdbCellBuf *
CdbCellBuf_Create(size_t cellbytes,
int num_initial_cells,
int num_cells_expand,
MemoryContext context);
/*
* CdbCellBuf_Destroy
*
* Free a CdbCellBuf struct allocated by CdbCellBuf_Create().
*
* Do not use this to free a cellbuf allocated by CdbCellBuf_CreateWrap()
* unless offsetof_cellbuf_in_struct == 0.
*
* NB: CdbCellBuf_Reset() can be used to free the memory owned by a cellbuf
* without freeing the CdbCellBuf struct itself. That is safe for all
* cellbufs.
*
* NB: An acceptable alternative for cleaning up would be to destroy or
* reset the associated MemoryContext.
*/
void
CdbCellBuf_Destroy(CdbCellBuf *cellbuf);
/*
* CdbCellBuf_CreateWrap
*
* Returns zeroed, newly allocated memory for a structure of given size,
* with a CdbCellBuf initialized at a given offset within the structure,
* and followed by space for initial cells.
*
* 'sizeof_outer_struct' is the size of the struct in which the CdbCellBuf is
* embedded.
* 'offsetof_cellbuf_in_struct' is the offset of the CdbCellBuf within that.
* 'cellbytes' is the size of each cell.
* 'num_initial_cells' is the suggested number of cell slots to be created
* immediately following the struct. This allows a small number
* of cells to be managed with good locality and no palloc/pfree.
* If NULL, all cells are stored in chunks allocated from 'context'.
* The actual number will be rounded up to minimize wasted space.
* 'num_cells_expand' is the suggested number of cell slots to be allocated
* whenever additional memory is needed. The actual number will be
* rounded to fully utilize a power-of-2 chunk. However, no expansion
* is done if num_cells_expand is zero; instead, after the initial
* cells are exhausted an error is raised.
* 'context' is the memory context from which the CdbCellBuf and cells are
* to be allocated. If NULL, the CurrentMemoryContext is used.
*/
void *
CdbCellBuf_CreateWrap(size_t sizeof_outer_struct,
size_t offsetof_cellbuf_in_struct,
size_t cellbytes,
int num_initial_cells,
int num_cells_expand,
MemoryContext context);
/*-------------------------------------------------------------------------
* Examine cellbuf state
*-------------------------------------------------------------------------
*/
/*
* CdbCellBuf_Context
*
* Returns the memory context used for allocating additional bunches of cells.
*/
static inline MemoryContext
CdbCellBuf_Context(const CdbCellBuf *cellbuf)
{
return cellbuf->context;
} /* CdbCellBuf_Context */
/*
* CdbCellBuf_IsEmpty
*
* Returns true if cellbuf is empty.
*/
static inline bool
CdbCellBuf_IsEmpty(const CdbCellBuf *cellbuf)
{
return cellbuf->nfull_total == 0;
} /* CdbCellBuf_IsEmpty */
/*
* CdbCellBuf_IsOk
*
* Returns true if the cellbuf passes a consistency check.
*
* 'cellbytes' must be the cell size which was given at initialization.
*/
bool
CdbCellBuf_IsOk(const CdbCellBuf *cellbuf, Size cellbytes);
/*
* CdbCellBuf_Length
*
* Returns the number of cells in use.
*/
static inline long
CdbCellBuf_Length(const CdbCellBuf *cellbuf)
{
return cellbuf->nfull_total;
} /* CdbCellBuf_Length */
/*
* CdbCellBuf_FirstCell
*
* Returns a pointer to the first cell, or NULL if the cellbuf is empty.
*/
static inline void *
CdbCellBuf_FirstCell(CdbCellBuf *cellbuf)
{
char *firstcell = NULL;
if (cellbuf->nearby_bunch_len > 0)
firstcell = (char *)cellbuf + cellbuf->offset_nearby;
else if (cellbuf->head)
{
Assert(cellbuf->head->cellbuf == cellbuf);
firstcell = (char *)cellbuf->head + CDBCELLBUF_BUMPER_BYTES;
}
if (firstcell == cellbuf->cfree)
firstcell = NULL;
Assert((firstcell != NULL) == (cellbuf->nfull_total > 0));
return firstcell;
} /* CdbCellBuf_FirstCell */
/*
* CdbCellBuf_LastCell
*
* Returns a pointer to the last cell, or NULL if the cellbuf is empty.
*/
static inline void *
CdbCellBuf_LastCell(CdbCellBuf *cellbuf)
{
char *lastcell;
if (cellbuf->cbeg < cellbuf->cfree)
{
Assert(cellbuf->cbeg + cellbuf->cellbytes <= cellbuf->cfree &&
cellbuf->cfree <= cellbuf->cend);
lastcell = cellbuf->cfree - cellbuf->cellbytes;
}
else if (cellbuf->nfull_total > 0)
lastcell = CdbCellBuf_LastCellHelper(cellbuf);
else
lastcell = NULL;
return lastcell;
} /* CdbCellBuf_LastCell */
/*-------------------------------------------------------------------------
* Cell operations
*-------------------------------------------------------------------------
*/
/*
* CdbCellBuf_AppendCell
*
* Allocate a cell at tail of cellbuf.
*
* Returns pointer to the cell. The cell has not been initialized.
*/
static inline void *
CdbCellBuf_AppendCell(CdbCellBuf *cellbuf)
{
void *cell;
Assert(cellbuf &&
cellbuf->cellbytes > 0);
if (cellbuf->cfree < cellbuf->cend)
{
Assert(cellbuf->cbeg &&
cellbuf->cbeg <= cellbuf->cfree &&
cellbuf->cfree + cellbuf->cellbytes <= cellbuf->cend);
cell = cellbuf->cfree;
cellbuf->cfree += cellbuf->cellbytes;
}
else
cell = CdbCellBuf_AppendMore(cellbuf);
cellbuf->nfull_total++;
return cell;
} /* CdbCellBuf_AppendCell */
/*
* CdbCellBuf_PopCell
*
* Deallocate a cell at tail of cellbuf.
*
* Returns pointer to the cell, or NULL if cellbuf is empty. The contents
* of the popped cell remain valid until the next cell operation, or until
* CdbCellBuf_Reset() or CdbCellBuf_Destroy().
*/
static inline void *
CdbCellBuf_PopCell(CdbCellBuf *cellbuf)
{
void *cell;
Assert(cellbuf &&
cellbuf->cellbytes > 0);
if (cellbuf->cbeg < cellbuf->cfree)
{
Assert(cellbuf->cbeg &&
cellbuf->cbeg + cellbuf->cellbytes <= cellbuf->cfree &&
cellbuf->cfree <= cellbuf->cend);
cell = cellbuf->cfree - cellbuf->cellbytes;
cellbuf->cfree = cell;
cellbuf->nfull_total--;
}
else if (cellbuf->nfull_total > 0)
cell = CdbCellBuf_PopMore(cellbuf);
else
cell = NULL;
return cell;
} /* CdbCellBuf_PopCell */
/*-------------------------------------------------------------------------
* CdbCellBuf_Iterator
*-------------------------------------------------------------------------
*/
/*
* CdbCellBuf_Iterator_Init
*
* Initializes caller's CdbCellBuf_Iterator struct, positioning the iterator
* before the first cell.
*
* It is ok to delete items (via CdbCellBuf_PopCell()) that are after the
* iterator position. However, deletion of an item that precedes the iterator
* position can leave the iterator in an inconsistent state; you must take care
* not to use that iterator any more after popping a preceding item.
*
* The following should not be called while iterating: CdbCellBuf_Reset(),
* CdbCellBuf_Destroy().
*/
void
CdbCellBuf_Iterator_Init(CdbCellBuf_Iterator *it, CdbCellBuf *cellbuf);
/*
* CdbCellBuf_Iterator_InitToEnd
*
* Initializes caller's CdbCellBuf_Iterator struct, positioning the iterator
* after the last cell. Equivalent to:
*
* CdbCellBuf_Iterator_Init(it, cellbuf);
* CdbCellBuf_Iterator_SetPos(it, CdbCellBuf_Length(cellbuf));
*/
static inline void
CdbCellBuf_Iterator_InitToEnd(CdbCellBuf_Iterator *it, CdbCellBuf *cellbuf)
{
Assert(it &&
cellbuf &&
cellbuf->cellbytes > 0);
it->cnext = cellbuf->cfree;
it->cend = cellbuf->cend;
it->cellbuf = cellbuf;
it->cbeg = cellbuf->cbeg;
it->begoff = cellbuf->nfull_total * cellbuf->cellbytes -
(cellbuf->cfree - cellbuf->cbeg);
} /* CdbCellBuf_Iterator_InitToEnd */
/*
* CdbCellBuf_Iterator_NextCell
*
* If iterator 'it' is positioned before a cell, returns a pointer to the cell
* and advances the iterator past that cell. Else returns NULL and leaves the
* iterator unchanged.
*
* The iterator will reach new items added by CdbCellBuf_Append() during
* or after the traversal. Even when the end has been reached, the caller
* can add more items and continue calling CdbCellBuf_Iterator_NextCell() to
* retrieve them.
*/
static inline void *
CdbCellBuf_Iterator_NextCell(CdbCellBuf_Iterator *it)
{
char *cell = it->cnext;
if (cell == it->cellbuf->cfree)
cell = NULL;
else if (cell < it->cend)
{
Assert(cell >= it->cbeg);
it->cnext += it->cellbuf->cellbytes;
}
else
{
Assert(cell == it->cend);
cell = CdbCellBuf_Iterator_NextCellHelper(it);
}
return cell;
} /* CdbCellBuf_Iterator_NextCell */
/*
* CdbCellBuf_Iterator_PrevCell
*
* If iterator 'it' is positioned after a cell, returns a pointer to the
* cell and repositions the iterator before that cell. Else returns NULL
* and leaves the iterator unchanged.
*/
static inline void *
CdbCellBuf_Iterator_PrevCell(CdbCellBuf_Iterator *it)
{
char *cell;
if (it->cnext > it->cbeg)
{
Assert(it->cnext <= it->cend);
it->cnext -= it->cellbuf->cellbytes;
cell = it->cnext;
}
else
{
Assert(it->cnext == it->cbeg);
cell = CdbCellBuf_Iterator_PrevCellHelper(it);
}
return cell;
} /* CdbCellBuf_Iterator_PrevCell */
/*
* CdbCellBuf_Iterator_CellAt
*
* Returns a pointer to the cell at a specified position relative to the
* iterator's current position; or NULL if the specified position would
* be out of bounds. Does not affect the position or state of the iterator.
*
* relCellIndex == 0 returns the same value that CdbCellBuf_Iterator_NextCell
* would return, but does not advance the iterator.
*
* relCellIndex == -1 returns the same value that CdbCellBuf_Iterator_PrevCell
* would return, without repositioning the iterator.
*/
static inline void *
CdbCellBuf_Iterator_CellAt(CdbCellBuf_Iterator *it, long relCellIndex)
{
long relCellOff = relCellIndex * it->cellbuf->cellbytes;
char *cell = it->cnext + relCellOff;
if (cell >= it->cbeg &&
cell < it->cend)
{
if (relCellIndex >= 0 &&
it->cend == it->cellbuf->cend &&
cell >= it->cellbuf->cfree)
cell = NULL;
}
else
cell = CdbCellBuf_Iterator_CellAtHelper(it, relCellOff);
return cell;
} /* CdbCellBuf_Iterator_CellAt */
/*
* CdbCellBuf_Iterator_GetPos
*
* Returns the number of cells preceding the current iterator position.
*/
static inline long
CdbCellBuf_Iterator_GetPos(const CdbCellBuf_Iterator *it)
{
CdbCellBuf *cellbuf = it->cellbuf;
long pos;
Assert(cellbuf &&
cellbuf->cellbytes > 0 &&
it->cnext >= it->cbeg &&
it->cnext <= it->cend);
pos = (long)(it->cnext - it->cbeg + it->begoff) / cellbuf->cellbytes;
Assert(pos >= 0 &&
pos <= cellbuf->nfull_total);
return pos;
} /* CdbCellBuf_Iterator_GetPos */
/*
* CdbCellBuf_Iterator_SetPos
*
* Positions the iterator before the cell whose 0-based index is
* 'nextCellIndex'. The first call to CdbCellBuf_Iterator_NextCell()
* will return a pointer to the specified cell (or NULL if the starting
* position equals the number of occupied cells.)
*
* If nextCellIndex == 0, positions the iterator before the first cell.
* If nextCellIndex == CdbCellBuf_Length(), positions the iterator after
* the last cell.
*
* An internal error is raised if nextCellIndex is outside the bounds
* 0 <= nextCellIndex <= CdbCellBuf_Length(it->cellbuf)
*/
static inline void
CdbCellBuf_Iterator_SetPos(CdbCellBuf_Iterator *it, long nextCellIndex)
{
long nextCellOff;
Insist(nextCellIndex <= it->cellbuf->nfull_total);
nextCellOff = nextCellIndex * it->cellbuf->cellbytes;
/* Reposition within current block? */
if (nextCellOff >= it->begoff &&
nextCellOff <= (long)(it->cend - it->cbeg) + it->begoff)
it->cnext = nextCellOff - it->begoff + it->cbeg;
else
CdbCellBuf_Iterator_SetPosHelper(it, nextCellOff);
} /* CdbCellBuf_Iterator_SetPos */
/*
* CdbCellBuf_Iterator_AtBegin
*
* Returns 'true' if iterator is positioned before the first cell or if
* the cellbuf is empty. Returns 'false' if there is a cell preceding
* the iterator position.
*/
static inline bool
CdbCellBuf_Iterator_AtBegin(const CdbCellBuf_Iterator *it)
{
return (it->cbeg == it->cnext &&
it->begoff == 0);
} /* CdbCellBuf_Iterator_AtBegin */
/*
* CdbCellBuf_Iterator_AtEnd
*
* Returns 'true' if iterator is positioned after the last cell or if
* the cellbuf is empty, i.e., if CdbCellBuf_Iterator_NextCell() would
* return NULL. Returns 'false' if iterator is positioned before a cell.
*/
static inline bool
CdbCellBuf_Iterator_AtEnd(const CdbCellBuf_Iterator *it)
{
CdbCellBuf *cellbuf = it->cellbuf;
bool atend;
if (it->cnext == cellbuf->cfree)
atend = true;
/* Maybe tail bunch is empty and iterator is at end of previous bunch */
else if (it->cnext == it->cend &&
it->cnext - it->cbeg + it->begoff == cellbuf->nfull_total * cellbuf->cellbytes)
atend = true;
else
atend = false;
return atend;
} /* CdbCellBuf_Iterator_AtEnd */
/*
* CdbCellBuf_Iterator_Length
*
* Returns the number of cells in the associated cellbuf.
*
* Equivalent to
* CdbCellBuf_Length(it->cellbuf)
*/
static inline long
CdbCellBuf_Iterator_Length(const CdbCellBuf_Iterator *it)
{
return it->cellbuf->nfull_total;
} /* CdbCellBuf_Iterator_Length */
#endif /* CDBCELLBUF_H */