/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* $Id: //eng/uds-releases/jasper/src/uds/geometry.c#3 $
*/
#include "geometry.h"
#include "deltaIndex.h"
#include "errors.h"
#include "hashUtils.h"
#include "logger.h"
#include "memoryAlloc.h"
#include "permassert.h"
#include "uds.h"
/**********************************************************************/
static int initializeGeometry(Geometry *geometry,
size_t bytesPerPage,
unsigned int recordPagesPerChapter,
unsigned int chaptersPerVolume,
unsigned int sparseChaptersPerVolume)
{
int result = ASSERT_WITH_ERROR_CODE(bytesPerPage >= BYTES_PER_RECORD,
UDS_BAD_STATE,
"page is smaller than a record: %zu",
bytesPerPage);
if (result != UDS_SUCCESS) {
return result;
}
result = ASSERT_WITH_ERROR_CODE(chaptersPerVolume > sparseChaptersPerVolume,
UDS_INVALID_ARGUMENT,
"sparse chapters per volume (%u) must be less"
" than chapters per volume (%u)",
sparseChaptersPerVolume,
chaptersPerVolume);
if (result != UDS_SUCCESS) {
return result;
}
geometry->bytesPerPage = bytesPerPage;
geometry->recordPagesPerChapter = recordPagesPerChapter;
geometry->chaptersPerVolume = chaptersPerVolume;
geometry->sparseChaptersPerVolume = sparseChaptersPerVolume;
geometry->denseChaptersPerVolume =
chaptersPerVolume - sparseChaptersPerVolume;
// Calculate the number of records in a page, chapter, and volume.
geometry->recordsPerPage = bytesPerPage / BYTES_PER_RECORD;
geometry->recordsPerChapter
= geometry->recordsPerPage * recordPagesPerChapter;
geometry->recordsPerVolume
= (unsigned long) geometry->recordsPerChapter * chaptersPerVolume;
geometry->openChapterLoadRatio = DEFAULT_OPEN_CHAPTER_LOAD_RATIO;
// Initialize values for delta chapter indexes.
geometry->chapterMeanDelta = 1 << DEFAULT_CHAPTER_MEAN_DELTA_BITS;
geometry->chapterPayloadBits = computeBits(recordPagesPerChapter - 1);
// We want 1 delta list for every 64 records in the chapter. The "| 077"
// ensures that the chapterDeltaListBits computation does not underflow.
geometry->chapterDeltaListBits
= computeBits((geometry->recordsPerChapter - 1) | 077) - 6;
geometry->deltaListsPerChapter = 1 << geometry->chapterDeltaListBits;
// We need enough address bits to achieve the desired mean delta.
geometry->chapterAddressBits
= (DEFAULT_CHAPTER_MEAN_DELTA_BITS - geometry->chapterDeltaListBits
+ computeBits(geometry->recordsPerChapter - 1));
// Let the delta index code determine how many pages are needed for the index
geometry->indexPagesPerChapter
= getDeltaIndexPageCount(geometry->recordsPerChapter,
geometry->deltaListsPerChapter,
geometry->chapterMeanDelta,
geometry->chapterPayloadBits,
bytesPerPage);
// Now that we have the size of a chapter index, we can calculate the
// space used by chapters and volumes.
geometry->pagesPerChapter
= geometry->indexPagesPerChapter + recordPagesPerChapter;
geometry->pagesPerVolume = geometry->pagesPerChapter * chaptersPerVolume;
geometry->headerPagesPerVolume = 1;
geometry->bytesPerVolume = bytesPerPage *
(geometry->pagesPerVolume + geometry->headerPagesPerVolume);
geometry->bytesPerChapter = bytesPerPage * geometry->pagesPerChapter;
return UDS_SUCCESS;
}
/**********************************************************************/
int makeGeometry(size_t bytesPerPage,
unsigned int recordPagesPerChapter,
unsigned int chaptersPerVolume,
unsigned int sparseChaptersPerVolume,
Geometry **geometryPtr)
{
Geometry *geometry;
int result = ALLOCATE(1, Geometry, "geometry", &geometry);
if (result != UDS_SUCCESS) {
return result;
}
result = initializeGeometry(geometry, bytesPerPage, recordPagesPerChapter,
chaptersPerVolume, sparseChaptersPerVolume);
if (result != UDS_SUCCESS) {
freeGeometry(geometry);
return result;
}
*geometryPtr = geometry;
return UDS_SUCCESS;
}
/**********************************************************************/
int copyGeometry(Geometry *source, Geometry **geometryPtr)
{
return makeGeometry(source->bytesPerPage,
source->recordPagesPerChapter,
source->chaptersPerVolume,
source->sparseChaptersPerVolume,
geometryPtr);
}
/**********************************************************************/
void freeGeometry(Geometry *geometry)
{
FREE(geometry);
}
/**********************************************************************/
uint64_t mapToVirtualChapterNumber(Geometry *geometry,
uint64_t newestVirtualChapter,
unsigned int physicalChapter)
{
unsigned int newestPhysicalChapter
= mapToPhysicalChapter(geometry, newestVirtualChapter);
uint64_t virtualChapter
= newestVirtualChapter - newestPhysicalChapter + physicalChapter;
if (physicalChapter > newestPhysicalChapter) {
virtualChapter -= geometry->chaptersPerVolume;
}
return virtualChapter;
}
/**********************************************************************/
bool hasSparseChapters(const Geometry *geometry,
uint64_t oldestVirtualChapter,
uint64_t newestVirtualChapter)
{
return (isSparse(geometry)
&& ((newestVirtualChapter - oldestVirtualChapter + 1)
> geometry->denseChaptersPerVolume));
}
/**********************************************************************/
bool isChapterSparse(const Geometry *geometry,
uint64_t oldestVirtualChapter,
uint64_t newestVirtualChapter,
uint64_t virtualChapterNumber)
{
return (hasSparseChapters(geometry, oldestVirtualChapter,
newestVirtualChapter)
&& ((virtualChapterNumber + geometry->denseChaptersPerVolume)
<= newestVirtualChapter));
}
/**********************************************************************/
bool areSamePhysicalChapter(const Geometry *geometry,
uint64_t chapter1,
uint64_t chapter2)
{
return ((chapter1 % geometry->chaptersPerVolume)
== (chapter2 % geometry->chaptersPerVolume));
}