NORM-mirror/common/normSegment.cpp

552 lines
15 KiB
C++

#include "normSegment.h"
#include <errno.h> // for strerror()
NormSegmentPool::NormSegmentPool()
: seg_size(0), seg_count(0), seg_total(0), seg_list(NULL),
peak_usage(0), overruns(0), overrun_flag(false)
{
}
NormSegmentPool::~NormSegmentPool()
{
Destroy();
}
bool NormSegmentPool::Init(unsigned int count, unsigned int size)
{
if (seg_list) Destroy();
peak_usage = 0;
overruns = 0;
// This makes sure we get appropriate alignment
unsigned int alloc_size = size / sizeof(char*);
if ((alloc_size*sizeof(char*)) < size) alloc_size++;
seg_size = alloc_size * sizeof(char*);
for (unsigned int i = 0; i < count; i++)
{
char** ptr = new char*[alloc_size];
if (ptr)
{
*ptr = seg_list;
seg_list = (char*)ptr;
seg_count++;
}
else
{
DMSG(0, "NormSegmentPool::Init() memory allocation error: %s\n",
strerror(errno));
seg_total = seg_count;
Destroy();
return false;
}
}
seg_total = seg_count;
return true;
} // end NormSegmentPool::Init()
void NormSegmentPool::Destroy()
{
ASSERT(seg_count == seg_total);
char** ptr = (char**)seg_list;
while (ptr)
{
char* next = *ptr;
delete ptr;
ptr = (char**)next;
}
seg_list = NULL;
seg_count = 0;
seg_total = 0;
seg_size = 0;
} // end NormSegmentPool::Destroy()
char* NormSegmentPool::Get()
{
char** ptr = (char**)seg_list;
if (ptr)
{
seg_list = *ptr;
seg_count--;
#ifdef NORM_DEBUG
overrun_flag = false;
unsigned int usage = seg_total - seg_count;
if (usage > peak_usage) peak_usage = usage;
}
else
{
if (!overrun_flag)
{
overruns++;
overrun_flag = true;
}
#endif // NORM_DEBUG
}
return ((char*)ptr);
} // end NormSegmentPool::GetSegment()
////////////////////////////////////////////////////////////
// NormBlock Implementation
NormBlock::NormBlock()
: size(0), segment_table(NULL), erasure_count(0), parity_count(0)
{
}
NormBlock::~NormBlock()
{
Destroy();
}
bool NormBlock::Init(UINT16 blockSize)
{
if (segment_table) Destroy();
if (!(segment_table = new char*[blockSize]))
{
DMSG(0, "NormBlock::Init() segment_table allocation error: %s\n", strerror(errno));
return false;
}
memset(segment_table, 0, blockSize*sizeof(char*));
if (!pending_mask.Init(blockSize))
{
DMSG(0, "NormBlock::Init() pending_mask allocation error: %s\n", strerror(errno));
Destroy();
return false;
}
if (!repair_mask.Init(blockSize))
{
DMSG(0, "NormBlock::Init() repair_mask allocation error: %s\n", strerror(errno));
Destroy();
return false;
}
size = blockSize;
erasure_count = 0;
parity_count = 0;
return true;
} // end NormBlock::Init()
void NormBlock::Destroy()
{
repair_mask.Destroy();
pending_mask.Destroy();
// (TBD) Option to return segments to pool from which they came
if (segment_table)
{
for (unsigned int i = 0; i < size; i++)
if (segment_table[i]) delete []segment_table[i];
delete []segment_table;
segment_table = (char**)NULL;
}
erasure_count = parity_count = size = 0;
} // end NormBlock::Destroy()
void NormBlock::EmptyToPool(NormSegmentPool& segmentPool)
{
ASSERT(segment_table);
for (unsigned int i = 0; i < size; i++)
{
if (segment_table[i])
{
segmentPool.Put(segment_table[i]);
segment_table[i] = (char*)NULL;
}
}
} // end NormBlock::EmptyToPool()
bool NormBlock::IsEmpty()
{
ASSERT(segment_table);
for (unsigned int i = 0; i < size; i++)
if (segment_table[i]) return false;
return true;
} // end NormBlock::EmptyToPool()
bool NormBlock::IsRepairPending(NormSegmentId end)
{
ASSERT(end <= repair_mask.Size());
repair_mask.SetBits(end, repair_mask.Size() - end);
repair_mask.XCopy(pending_mask);
return (repair_mask.IsSet() ? (repair_mask.FirstSet() < end) : false);
} // end NormBlock::IsRepairPending()
NormBlockPool::NormBlockPool()
: head((NormBlock*)NULL)
{
}
NormBlockPool::~NormBlockPool()
{
Destroy();
}
bool NormBlockPool::Init(UINT32 numBlocks, UINT16 blockSize)
{
if (head) Destroy();
for (UINT32 i = 0; i < numBlocks; i++)
{
NormBlock* b = new NormBlock();
if (b)
{
if (!b->Init(blockSize))
{
DMSG(0, "NormBlockPool::Init() block init error\n");
delete b;
Destroy();
return false;
}
b->next = head;
head = b;
}
else
{
DMSG(0, "NormBlockPool::Init() new block error\n");
Destroy();
return false;
}
}
return true;
} // end NormBlockPool::Init()
void NormBlockPool::Destroy()
{
NormBlock* next;
while ((next = head))
{
head = next->next;
delete next;
}
} // end NormBlockPool::Destroy()
NormBlockBuffer::NormBlockBuffer()
: table((NormBlock**)NULL), range_max(0), range(0)
{
}
NormBlockBuffer::~NormBlockBuffer()
{
Destroy();
}
bool NormBlockBuffer::Init(unsigned long rangeMax, unsigned long tableSize)
{
if (table) Destroy();
// Make sure tableSize is greater than 0 and 2^n
if (!rangeMax || !tableSize)
{
DMSG(0, "NormBlockBuffer::Init() bad range(%lu) or tableSize(%lu)\n",
rangeMax, tableSize);
return false;
}
if (0 != (tableSize & 0x07)) tableSize = (tableSize >> 3) + 1;
if (!(table = new NormBlock*[tableSize]))
{
DMSG(0, "NormBlockBuffer::Init() buffer allocation error: %s\n", strerror(errno));
return false;
}
memset(table, 0, tableSize*sizeof(char*));
hash_mask = tableSize - 1;
range_max = rangeMax;
range = 0;
return true;
} // end NormBlockBuffer::Init()
void NormBlockBuffer::Destroy()
{
range_max = range = 0;
if (table)
{
NormBlock* block;
while((block = Find(range_lo)))
{
DMSG(0, "NormBlockBuffer::Destroy() buffer not empty!?\n");
Remove(block);
delete block;
}
delete []table;
table = (NormBlock**)NULL;
range_max = 0;
}
} // end NormBlockBuffer::Destroy()
NormBlock* NormBlockBuffer::Find(const NormBlockId& blockId) const
{
if (range)
{
if ((blockId < range_lo) || (blockId > range_hi)) return (NormBlock*)NULL;
NormBlock* theBlock = table[((UINT32)blockId) & hash_mask];
//printf("NormBlockBuffer::Find() table[%lu] = %p\n", ((UINT32)blockId) & hash_mask, theBlock);
while (theBlock && (blockId != theBlock->Id())) theBlock = theBlock->next;
return theBlock;
}
else
{
return (NormBlock*)NULL;
}
} // end NormBlockBuffer::Find()
bool NormBlockBuffer::CanInsert(NormBlockId blockId) const
{
if (0 != range)
{
if (blockId < range_lo)
{
if ((range_lo - blockId + range) > range_max)
return false;
else
return true;
}
else if (blockId > range_hi)
{
if ((blockId - range_hi + range) > range_max)
return false;
else
return true;
}
else
{
return true;
}
}
else
{
return true;
}
} // end NormBlockBuffer::CanInsert()
bool NormBlockBuffer::Insert(NormBlock* theBlock)
{
if (range < range_max)
{
const NormBlockId& blockId = theBlock->Id();
if (!range)
{
range_lo = range_hi = blockId;
range = 1;
}
if (blockId < range_lo)
{
UINT32 newRange = range_lo - blockId + range;
if (newRange > range_max) return false;
range_lo = blockId;
range = newRange;
}
else if (blockId > range_hi)
{
UINT32 newRange = blockId - range_hi + range;
if (newRange > range_max) return false;
range_hi = blockId;
range = newRange;
}
UINT32 index = ((UINT32)blockId) & hash_mask;
NormBlock* prev = NULL;
NormBlock* entry = table[index];
while (entry && (entry->Id() < blockId))
{
prev = entry;
entry = entry->next;
}
if (prev)
prev->next = theBlock;
else
table[index] = theBlock;
ASSERT((entry ? (blockId != entry->Id()) : true));
theBlock->next = entry;
return true;
}
else
{
return false;
}
} // end NormBlockBuffer::Insert()
bool NormBlockBuffer::Remove(const NormBlock* theBlock)
{
ASSERT(theBlock);
const NormBlockId& blockId = theBlock->Id();
if (range)
{
if ((blockId < range_lo) || (blockId > range_hi)) return false;
UINT32 index = ((UINT32)blockId) & hash_mask;
NormBlock* prev = NULL;
NormBlock* entry = table[index];
while (entry && (entry->Id() != blockId))
{
prev = entry;
entry = entry->next;
}
if (entry != theBlock) return false;
if (prev)
prev->next = entry->next;
else
table[index] = (NormBlock*)NULL;
if (range > 1)
{
if (blockId == range_lo)
{
// Find next entry for range_lo
UINT32 i = index;
UINT32 endex;
if (range <= hash_mask)
endex = (index + range) & hash_mask;
else
endex = index;
entry = NULL;
UINT32 offset = 0;
NormBlockId nextId = range_hi;
do
{
++i &= hash_mask;
offset++;
if ((entry = table[i]))
{
NormBlockId id = (UINT32)index + offset;
while(entry && (entry->Id() != id))
{
if ((entry->Id() > blockId) && (entry->Id() < nextId))
nextId = entry->Id();
entry = entry->next;
}
if (entry) break;
}
} while (i != endex);
if (entry)
{
range_lo = entry->Id();
range = range_hi - range_lo + 1;
}
else if (nextId != range_hi)
{
range_lo = nextId;
range = range_hi - range_lo + 1;
}
else
{
range = 0;
}
}
else if (blockId == range_hi)
{
// Find prev entry for range_hi
UINT32 i = index;
UINT32 endex;
if (range <= hash_mask)
endex = (index - range) & hash_mask;
else
endex = index;
entry = NULL;
UINT32 offset = 0;
//printf("preving i:%lu endex:%lu lo:%lu hi:%lu\n", i, endex, (UINT32)range_lo, (UINT32) range_hi);
NormBlockId prevId = range_lo;
do
{
--i &= hash_mask;
offset++;
if ((entry = table[i]))
{
NormBlockId id = (UINT32)index - offset;
//printf("Looking for id:%lu at index:%lu\n", (UINT32)id, i);
while(entry && (entry->Id() != id))
{
if ((entry->Id() < blockId) && (entry->Id() > prevId))
prevId = entry->Id();
entry = entry->next;
}
if (entry) break;
}
} while (i != endex);
if (entry)
{
range_hi = entry->Id();
}
else if (prevId != range_lo)
{
range_hi = prevId;
range = range_hi - range_lo + 1;
}
else
{
range = 0;
}
}
}
else
{
range = 0;
}
return true;
}
else
{
return false;
}
} // end NormBlockBuffer::Remove()
NormBlockBuffer::Iterator::Iterator(const NormBlockBuffer& blockBuffer)
: buffer(blockBuffer), reset(true)
{
}
NormBlock* NormBlockBuffer::Iterator::GetNextBlock()
{
if (reset)
{
if (buffer.range)
{
reset = false;
index = buffer.range_lo;
return buffer.Find(index);
}
else
{
return (NormBlock*)NULL;
}
}
else
{
if (buffer.range &&
(index < buffer.range_hi) &&
!(index < buffer.range_lo))
{
// Find next entry _after_ current "index"
UINT32 i = index;
UINT32 endex = buffer.range_hi - index;
if (endex <= buffer.hash_mask)
endex = (index + endex) & buffer.hash_mask;
else
endex = index;
UINT32 offset = 0;
NormBlockId nextId = buffer.range_hi;
do
{
++i &= buffer.hash_mask;
offset++;
NormBlockId id = (UINT32)index + offset;
NormBlock* entry = buffer.table[i];
while ((NULL != entry )& (entry->Id() != id))
{
if ((entry->Id() > index) && (entry->Id() < nextId))
nextId = entry->Id();
entry = NormBlockBuffer::Next(entry);
}
if (entry)
{
index = entry->Id();
return entry;
}
} while (i != endex);
// If we get here, use nextId value
index = nextId;
return buffer.Find(nextId);
}
else
{
return (NormBlock*)NULL;
}
}
} // end NormBlockBuffer::Iterator::GetNextBlock()