|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
310c69 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
310c69 |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
310c69 |
* of the License, or (at your option) any later version.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
310c69 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
310c69 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
310c69 |
* GNU General Public License for more details.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
310c69 |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
310c69 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
310c69 |
* 02110-1301, USA.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* $Id: //eng/uds-releases/jasper/src/uds/openChapterZone.c#2 $
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "openChapterZone.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "compiler.h"
|
|
Packit Service |
310c69 |
#include "hashUtils.h"
|
|
Packit Service |
310c69 |
#include "logger.h"
|
|
Packit Service |
310c69 |
#include "memoryAlloc.h"
|
|
Packit Service |
310c69 |
#include "permassert.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static INLINE size_t recordsSize(const OpenChapterZone *openChapter)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return (sizeof(UdsChunkRecord) * (1 + openChapter->capacity));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static INLINE size_t slotsSize(size_t slotCount)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return (sizeof(Slot) * slotCount);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Round up to the first power of two greater than or equal
|
|
Packit Service |
310c69 |
* to the supplied number.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param val the number to round up
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return the first power of two not smaller than val for any
|
|
Packit Service |
310c69 |
* val <= 2^63
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static INLINE size_t nextPowerOfTwo(size_t val)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (val == 0) {
|
|
Packit Service |
310c69 |
return 1;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
return (1 << computeBits(val - 1));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int makeOpenChapter(const Geometry *geometry,
|
|
Packit Service |
310c69 |
unsigned int zoneCount,
|
|
Packit Service |
310c69 |
OpenChapterZone **openChapterPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = ASSERT(zoneCount > 0, "zone count must be > 0");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
result = ASSERT_WITH_ERROR_CODE(geometry->openChapterLoadRatio > 1,
|
|
Packit Service |
310c69 |
UDS_BAD_STATE,
|
|
Packit Service |
310c69 |
"Open chapter hash table is too small");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
result = ASSERT_WITH_ERROR_CODE((geometry->recordsPerChapter
|
|
Packit Service |
310c69 |
<= OPEN_CHAPTER_MAX_RECORD_NUMBER),
|
|
Packit Service |
310c69 |
UDS_BAD_STATE,
|
|
Packit Service |
310c69 |
"Too many records (%u) for a single chapter",
|
|
Packit Service |
310c69 |
geometry->recordsPerChapter);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (geometry->recordsPerChapter < zoneCount) {
|
|
Packit Service |
310c69 |
return logUnrecoverable(
|
|
Packit Service |
310c69 |
UDS_INVALID_ARGUMENT,
|
|
Packit Service |
310c69 |
"zone count: %u is larger than the records per chapter %u",
|
|
Packit Service |
310c69 |
zoneCount, geometry->recordsPerChapter);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
size_t capacity = geometry->recordsPerChapter / zoneCount;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// The slot count must be at least one greater than the capacity.
|
|
Packit Service |
310c69 |
// Using a power of two slot count guarantees that hash insertion
|
|
Packit Service |
310c69 |
// will never fail if the hash table is not full.
|
|
Packit Service |
310c69 |
size_t slotCount = nextPowerOfTwo(capacity * geometry->openChapterLoadRatio);
|
|
Packit Service |
310c69 |
OpenChapterZone *openChapter;
|
|
Packit Service |
310c69 |
result = ALLOCATE_EXTENDED(OpenChapterZone, slotCount, Slot,
|
|
Packit Service |
310c69 |
"open chapter", &openChapter);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
openChapter->slotCount = slotCount;
|
|
Packit Service |
310c69 |
openChapter->capacity = capacity;
|
|
Packit Service |
310c69 |
result = allocateCacheAligned(recordsSize(openChapter), "record pages",
|
|
Packit Service |
310c69 |
&openChapter->records);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
freeOpenChapter(openChapter);
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
*openChapterPtr = openChapter;
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
size_t openChapterSize(const OpenChapterZone *openChapter)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return openChapter->size - openChapter->deleted;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void resetOpenChapter(OpenChapterZone *openChapter)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
openChapter->size = 0;
|
|
Packit Service |
310c69 |
openChapter->deleted = 0;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
memset(openChapter->records, 0, recordsSize(openChapter));
|
|
Packit Service |
310c69 |
memset(openChapter->slots, 0, slotsSize(openChapter->slotCount));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static UdsChunkRecord *probeChapterSlots(OpenChapterZone *openChapter,
|
|
Packit Service |
310c69 |
const UdsChunkName *name,
|
|
Packit Service |
310c69 |
unsigned int *slotPtr,
|
|
Packit Service |
310c69 |
unsigned int *recordNumberPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
unsigned int slots = openChapter->slotCount;
|
|
Packit Service |
310c69 |
unsigned int probe = nameToHashSlot(name, slots);
|
|
Packit Service |
310c69 |
unsigned int firstSlot = 0;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
UdsChunkRecord *record;
|
|
Packit Service |
310c69 |
unsigned int probeSlot;
|
|
Packit Service |
310c69 |
unsigned int recordNumber;
|
|
Packit Service |
310c69 |
unsigned int probeAttempts;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
for (probeAttempts = 1; ; ++probeAttempts) {
|
|
Packit Service |
310c69 |
probeSlot = firstSlot + probe;
|
|
Packit Service |
310c69 |
recordNumber = openChapter->slots[probeSlot].recordNumber;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// If the hash slot is empty, we've reached the end of a chain without
|
|
Packit Service |
310c69 |
// finding the record and should terminate the search.
|
|
Packit Service |
310c69 |
if (recordNumber == 0) {
|
|
Packit Service |
310c69 |
record = NULL;
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// If the name of the record referenced by the slot matches and has not
|
|
Packit Service |
310c69 |
// been deleted, then we've found the requested name.
|
|
Packit Service |
310c69 |
record = &openChapter->records[recordNumber];
|
|
Packit Service |
310c69 |
if ((memcmp(&record->name, name, UDS_CHUNK_NAME_SIZE) == 0)
|
|
Packit Service |
310c69 |
&& !openChapter->slots[recordNumber].recordDeleted) {
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Quadratic probing: advance the probe by 1, 2, 3, etc. and try again.
|
|
Packit Service |
310c69 |
// This performs better than linear probing and works best for 2^N slots.
|
|
Packit Service |
310c69 |
probe += probeAttempts;
|
|
Packit Service |
310c69 |
if (probe >= slots) {
|
|
Packit Service |
310c69 |
probe = probe % slots;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// These NULL checks will be optimized away in callers who don't care about
|
|
Packit Service |
310c69 |
// the values when this function is inlined.
|
|
Packit Service |
310c69 |
if (slotPtr != NULL) {
|
|
Packit Service |
310c69 |
*slotPtr = probeSlot;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (recordNumberPtr != NULL) {
|
|
Packit Service |
310c69 |
*recordNumberPtr = recordNumber;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return record;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void searchOpenChapter(OpenChapterZone *openChapter,
|
|
Packit Service |
310c69 |
const UdsChunkName *name,
|
|
Packit Service |
310c69 |
UdsChunkData *metadata,
|
|
Packit Service |
310c69 |
bool *found)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
UdsChunkRecord *record = probeChapterSlots(openChapter, name, NULL, NULL);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (record == NULL) {
|
|
Packit Service |
310c69 |
*found = false;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
*found = true;
|
|
Packit Service |
310c69 |
if (metadata != NULL) {
|
|
Packit Service |
310c69 |
*metadata = record->data;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int putOpenChapter(OpenChapterZone *openChapter,
|
|
Packit Service |
310c69 |
const UdsChunkName *name,
|
|
Packit Service |
310c69 |
const UdsChunkData *metadata,
|
|
Packit Service |
310c69 |
unsigned int *remaining)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
unsigned int slot;
|
|
Packit Service |
310c69 |
UdsChunkRecord *record = probeChapterSlots(openChapter, name, &slot, NULL);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (record != NULL) {
|
|
Packit Service |
310c69 |
record->data = *metadata;
|
|
Packit Service |
310c69 |
*remaining = openChapter->capacity - openChapter->size;
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (openChapter->size >= openChapter->capacity) {
|
|
Packit Service |
310c69 |
return makeUnrecoverable(UDS_VOLUME_OVERFLOW);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
unsigned int recordNumber = ++openChapter->size;
|
|
Packit Service |
310c69 |
openChapter->slots[slot].recordNumber = recordNumber;
|
|
Packit Service |
310c69 |
record = &openChapter->records[recordNumber];
|
|
Packit Service |
310c69 |
record->name = *name;
|
|
Packit Service |
310c69 |
record->data = *metadata;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
*remaining = openChapter->capacity - openChapter->size;
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void removeFromOpenChapter(OpenChapterZone *openChapter,
|
|
Packit Service |
310c69 |
const UdsChunkName *name,
|
|
Packit Service |
310c69 |
bool *removed)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
unsigned int recordNumber;
|
|
Packit Service |
310c69 |
UdsChunkRecord *record
|
|
Packit Service |
310c69 |
= probeChapterSlots(openChapter, name, NULL, &recordNumber);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (record == NULL) {
|
|
Packit Service |
310c69 |
*removed = false;
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Set the deleted flag on the recordNumber in the slot array so search
|
|
Packit Service |
310c69 |
// won't find it and close won't index it.
|
|
Packit Service |
310c69 |
openChapter->slots[recordNumber].recordDeleted = true;
|
|
Packit Service |
310c69 |
openChapter->deleted += 1;
|
|
Packit Service |
310c69 |
*removed = true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void freeOpenChapter(OpenChapterZone *openChapter)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (openChapter != NULL) {
|
|
Packit Service |
310c69 |
FREE(openChapter->records);
|
|
Packit Service |
310c69 |
FREE(openChapter);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|