Blame source/uds/openChapterZone.c

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
}