Commit 2b107568 authored by Per Cederqvist's avatar Per Cederqvist
Browse files

Include assert.h. Only include

stdlib.h if HAVE_STDLIB_H.  Include stdio.h if we didn't get NULL
from stdlib.h.  Inlucd lyskomd.h and server/smalloc.h.
(L2G_BLOCKSIZE): Moved here from local-to-global.h.
(struct l2g_block_info): Likewise.
(is_dense): New static function.
(is_empty): New static function.
(key_value): New static function.
(sparse_skip_deleted): New static function.
(sparse_locate_value): New static function.
(sparse_compact): New static function.
(add_block): Use srealloc instead of realloc.  Don't initialize
	start -- the caller is responsible for doing so.
(delete_block): Use sfree instead of free, and srealloc instead of
	realloc.
(make_sparse): Don't change the value of start, since there is no
	need to, and make_sparse may now be called on an empty block,
	which would cause start to be set to a garbage value.
(find_block): Added a const qualifier to the return value and l2g
	argument.  Use key_value to simplify code.
(find_block_index_key): Added a const qualifier to the l2g
	argument.  Use helper functions to simplify the code.  Use
	sparse_skip_deleted so that the function never returns a deleted
	key.  Never return deleted keys in dense blocks either.
(join_range): New static function.
(join_blocks): New static function.
(l2g_init): Set first_unused to 1.
(l2g_clear): Use sfree instead of free.  Set first_unused to 1.
(l2g_copy): Added const qualifier to the src argument.  Simplified
	code by using helper functions and relying on the fact that
	calling l2g_append with a Text_no=0 is a no-op.  Set first_unused
	of the copy.
(l2g_append): Send a log message and do nothing if an attempt to
	set a local number lower than first_unused is detected.  Set
	first_unused.  Fixed the logic about when to add a new block, when
	to compact the last block, and when to make it sparse.  Simplify
	code by using the helper functions.
(l2g_delete): Don't increase the zeroes counter if a zero is
	deleted.  Delete the entire block if it became empty.  Attempt to
	join this block to its neighbors.  Compact the block if it was
	sparse and contained too many deleted entries.
(l2g_lookup): Added const qualifier to the l2g argument.  Don't
	return uninitialized data.  Use helper functions to simplify code
	and make it slightly faster.
(l2g_next_key): Added const qualifier to the l2g argument.
(l2g_first_appendable_key): New function.
(l2g_delete_global_in_sorted): New function.
(l2g_compact): Removed.
(l2g_dump): Switch placement of l2g and file arguments to match
	other similar functions in the lyskomd code.  Added const
	qualifier.  Dump first_unused.  Use helper functions to simplify
	code.
(l2g_read): Switch placement of l2g and file arguments to match
	other similar functions in the lyskomd code.  Read first_unused.
	Return a success indicator.  Detect unexpected EOF conditions.
(l2g_write): Switch placement of l2g and file arguments to match
	other similar functions in the lyskomd code.  Write first_unused.
	Use helper functions to simplify code.
(l2gi_searchall): Added const qualifier to the l2g argument.
	Simplify code by calling l2gi_searchsome instead of duplicating
	the code.
(l2gi_searchsome): Added const qualifier to the l2g argument.
	Complain if the begin argument is less than 1.  Don't forget to
	initialize search_ended.  Set beginval.  Giving end==0 means that
	the iterator should continue to the end of the set.  Use helper
	functions to simplify the code.
(l2gi_next): Use helper functions to simplify code.  The iterator
	is unlimited if endval==0.
(l2gi_begin): New function.
(l2gi_end): New function.
parent fff3a251
......@@ -5,11 +5,13 @@
*/
/* FIXME: Use smalloc() et al instead of malloc() */
#include <stdlib.h>
#include <assert.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifndef NULL
# include <stdio.h>
#endif
#include <sys/types.h>
#include <time.h>
......@@ -21,6 +23,39 @@
#include "log.h"
#include "ram-parse.h"
#include "ram-output.h"
#include "lyskomd.h"
#include "server/smalloc.h"
/* The test suite requires L2G_BLOCKSIZE to be 10, but that small
blocks will result in far too much malloc-overhead. A value of at
least 100 is probably needed for reasonable performance. */
/* FIXME: make this a global variable that is initialized to, say,
250, and that the test suite program can change to 10 before it
does anything else. */
#define L2G_BLOCKSIZE 10 /* Doesn't seem to matter much. */
struct l2g_block_info {
/* An index into key_block and value_block that indicates the
first free spot. This can also be thought of as the number of
entries in key_block and value_block that are actually in use. */
int first_free;
/* Number of entries in the block that contain the value 0. For
purposes of calculating this value, data past the end of the
block is counted as zeroes. */
int zeroes;
/* First local text no in this block. */
Local_text_no start;
/* NULL if this is a dense block, or a block of L2G_BLOCKSIZE
Local_text_nos if this is a sparse block. */
Local_text_no *key_block;
/* A block of L2G_BLOCKSIZE Text_nos. */
Text_no *value_block;
};
/* ================================================================ */
......@@ -28,6 +63,93 @@
/* ================================================================ */
static inline int
is_dense(const struct l2g_block_info *binfo)
{
return binfo->key_block == NULL;
}
static inline int
is_empty(const struct l2g_block_info *binfo)
{
return binfo->zeroes == L2G_BLOCKSIZE;
}
static inline Local_text_no
key_value(const struct l2g_block_info *binfo,
int index)
{
if (is_dense(binfo))
return binfo->start + index;
else
return binfo->key_block[index];
}
static inline int
sparse_skip_deleted(const struct l2g_block_info *binfo,
int i)
{
while (i < binfo->first_free && binfo->value_block[i] == 0)
++i;
return i;
}
/* Search for LNO in BINFO, and return the index for it. BINFO must
be a non-empty sparse block. If LNO isn't present in the block
this will return the index of next higher entry. If LNO is larger
than the last entry, the index past the largest entry will be
returned. This function never returns the index of a deleted entry. */
static inline int
sparse_locate_value(const struct l2g_block_info *binfo,
Local_text_no lno)
{
int i;
assert(!is_empty(binfo));
assert(!is_dense(binfo));
/* FIXME: implement a binary search instead. */
for (i = 0; i < binfo->first_free && binfo->key_block[i] < lno; ++i)
continue;
/* Skip any lingering deleted entries. */
i = sparse_skip_deleted(binfo, i);
assert(i == binfo->first_free || binfo->key_block[i] >= lno);
assert(i == binfo->first_free || binfo->value_block[i] != 0);
return i;
}
static void
sparse_compact(struct l2g_block_info *binfo)
{
int from, to;
if (binfo->first_free + binfo->zeroes == L2G_BLOCKSIZE)
return;
assert(binfo->first_free + binfo->zeroes > L2G_BLOCKSIZE);
for (from = to = 0; from < binfo->first_free; ++from)
if (binfo->value_block[from] != 0)
{
if (from != to)
{
binfo->value_block[to] = binfo->value_block[from];
binfo->key_block[to] = binfo->key_block[from];
}
++to;
}
binfo->first_free = to;
assert(binfo->first_free + binfo->zeroes == L2G_BLOCKSIZE);
}
/*
* Add a new block to the Local_to_global L2G.
* Since we always add consecutively, we always start out by making
......@@ -36,24 +158,33 @@
* Return a pointer to the newly created block info.
*/
static L2g_block_info *
static struct l2g_block_info *
add_block(Local_to_global *l2g)
{
L2g_block_info * binfo;
struct l2g_block_info * binfo;
#ifndef NDEBUG
int i;
#endif
/* Realloc the block pointer array. */
l2g->num_blocks++;
l2g->blocks = (L2g_block_info *)
realloc(l2g->blocks,
l2g->num_blocks * sizeof(L2g_block_info));
l2g->blocks = srealloc(l2g->blocks,
l2g->num_blocks * sizeof(struct l2g_block_info));
/* Create a new block info and fill it in. */
binfo = &(l2g->blocks[l2g->num_blocks - 1]);
binfo->first_free = 0;
binfo->zeroes = L2G_BLOCKSIZE;
binfo->start = 0;
#ifndef NDEBUG
binfo->start = 0xdeadbeef;
#endif
binfo->key_block = NULL;
binfo->value_block = (Text_no *) malloc(L2G_BLOCKSIZE * sizeof(Text_no));
binfo->value_block = smalloc(L2G_BLOCKSIZE * sizeof(Text_no));
#ifndef NDEBUG
for (i = 0; i < L2G_BLOCKSIZE; ++i)
binfo->value_block[i] = 0xdeadbeef;
#endif
return binfo;
}
......@@ -66,20 +197,21 @@ add_block(Local_to_global *l2g)
static void
delete_block(Local_to_global *l2g,
L2g_block_info *binfo)
struct l2g_block_info *binfo)
{
assert(is_empty(binfo));
/* Remove the blocks from the Block Info */
if (binfo->key_block != NULL)
free(binfo->key_block);
free(binfo->value_block);
sfree(binfo->key_block);
sfree(binfo->value_block);
/* Compact the remaining blocks. */
while (++binfo < l2g->blocks + l2g->num_blocks)
*(binfo - 1) = *binfo;
l2g->num_blocks--;
l2g->blocks = (L2g_block_info *)
realloc(l2g->blocks,
l2g->num_blocks * sizeof(L2g_block_info));
l2g->blocks = srealloc(l2g->blocks,
l2g->num_blocks * sizeof(struct l2g_block_info));
}
......@@ -87,46 +219,51 @@ delete_block(Local_to_global *l2g,
* Make a sparse block from a dense one.
*/
static void
make_sparse(L2g_block_info *binfo)
make_sparse(struct l2g_block_info *binfo)
{
int next;
int i;
assert(is_dense(binfo));
/* Allocate the room for the key block. */
binfo->key_block = (Local_text_no *)
malloc(L2G_BLOCKSIZE * sizeof(Local_text_no));
binfo->key_block = smalloc(L2G_BLOCKSIZE * sizeof(Local_text_no));
#ifndef NDEBUG
for (i = 0; i < L2G_BLOCKSIZE; ++i)
binfo->key_block[i] = 0xdeadbeef;
#endif
/* Compact the block. */
next = 0;
for (i = 0; i < binfo->first_free; ++i) {
if (binfo->value_block[i] != 0) {
for (i = 0; i < binfo->first_free; ++i)
if (binfo->value_block[i] != 0)
{
binfo->key_block[next] = binfo->start + i;
binfo->value_block[next] = binfo->value_block[i];
next++;
}
}
/* Set the rest of the fields. */
binfo->first_free = next;
binfo->zeroes = L2G_BLOCKSIZE - next;
binfo->start = binfo->key_block[0];
/* FIXME: What happens if next == 0? Can this happen? */
}
/*
* Find the block where the local text no LNO is and return a pointer
* to it. Returning a pointer does not mean that the text still exists,
* so a search within the block must also be done later.
* so a search within the block must also be done later. If LNO is
* smaller than the smallest number in the structure a pointer to the
* first block will be returned.
*
* Return NULL if LNO is bigger than the highest number in the structure.
* Return NULL if LNO is bigger than the highest number in the
* structure, or if the structure is empty.
*/
static L2g_block_info *
find_block(Local_to_global *l2g, Local_text_no lno)
static const struct l2g_block_info *
find_block(const Local_to_global *l2g, Local_text_no lno)
{
L2g_block_info * binfo;
const struct l2g_block_info * binfo;
/* If empty, the number can not be in here. */
if (l2g->num_blocks == 0)
......@@ -136,19 +273,14 @@ find_block(Local_to_global *l2g, Local_text_no lno)
binfo = &(l2g->blocks[l2g->num_blocks - 1]);
/* If lno is greater than the biggest local number, return NULL. */
if (binfo->key_block == NULL) {
/* A dense block */
if (lno >= binfo->start + binfo->first_free)
return NULL;
} else {
/* A sparse block */
if (lno > binfo->value_block[binfo->first_free - 1])
return NULL;
}
assert(binfo->first_free > 0);
if (lno > key_value(binfo, binfo->first_free - 1))
return NULL;
/* Find the block where lno *could* be. */
/* FIXME: Binary search? */
while (binfo > l2g->blocks) {
while (binfo > l2g->blocks)
{
if (lno >= binfo->start)
return binfo;
binfo--;
......@@ -158,12 +290,19 @@ find_block(Local_to_global *l2g, Local_text_no lno)
}
/* Find the first local text number higher than LNO and return it.
Return 0 if LNO is higher than any local text number in the
structure. The block where the returned value was found is stored
in BINFO_OUT, and the position within the block is stored in
INDEX_OUT. BINFO_OUT and INDEX_OUT are only filled in if the
return value is non-zero. */
static Local_text_no
find_block_index_key(Local_to_global *l2g, Local_text_no lno,
L2g_block_info **binfo_out,
find_block_index_key(const Local_to_global *l2g, Local_text_no lno,
const struct l2g_block_info **binfo_out,
int *index_out)
{
L2g_block_info * binfo;
const struct l2g_block_info * binfo;
int i;
/* If the number is not to be found, return 0. */
......@@ -171,29 +310,36 @@ find_block_index_key(Local_to_global *l2g, Local_text_no lno,
if (binfo == NULL)
return 0;
/* If lno is lower than the first text, treat this as a special case. */
if (lno >= binfo->start) {
/* Check the found block */
if (binfo->key_block == NULL) {
/* dense block */
for (i = lno - binfo->start + 1; i < binfo->first_free; ++i) {
if (binfo->value_block[i] != 0) {
/* If lno is lower than the first existing entry, return the first
existing entry. The search in the first block is performed the
same way as when no entry was found after LNO in binfo. */
if (lno >= binfo->start)
{
/* Check the found block. See if there is another entry after
the entry for LNO. If so, return it. */
if (is_dense(binfo))
{
for (i = lno - binfo->start + 1; i < binfo->first_free; ++i)
if (binfo->value_block[i] != 0)
{
*binfo_out = binfo;
*index_out = i;
return binfo->start + i;
}
}
} else {
/* sparse block */
/* FIXME: binary search */
for (i = 0; i < binfo->first_free; ++i) {
if (binfo->key_block[i] > lno) {
*binfo_out = binfo;
*index_out = i;
}
else
{
i = sparse_locate_value(binfo, lno);
if (i < binfo->first_free && binfo->key_block[i] == lno)
++i;
i = sparse_skip_deleted(binfo, i);
if (i < binfo->first_free)
{
*binfo_out = binfo;
*index_out = i;
return binfo->key_block[i];
}
return binfo->key_block[i];
}
}
binfo++;
......@@ -204,34 +350,129 @@ find_block_index_key(Local_to_global *l2g, Local_text_no lno,
* until there are no more blocks. Blocks can be completely
* empty due to removals of entries.
*/
for (; binfo < l2g->blocks + l2g->num_blocks; ++binfo) {
for (; binfo < l2g->blocks + l2g->num_blocks; ++binfo)
{
/* If the block contains anything at all, get the item. */
if (binfo->zeroes < L2G_BLOCKSIZE){
if (binfo->key_block == NULL) {
/* dense block */
for (i = 0; i < binfo->first_free; ++i) {
if (binfo->value_block[i] != 0) {
*binfo_out = binfo;
*index_out = i;
return binfo->start + i;
}
if (!is_empty(binfo))
{
for (i = 0; i < binfo->first_free; ++i)
if (binfo->value_block[i] != 0)
{
*binfo_out = binfo;
*index_out = i;
return key_value(binfo, i);
}
} else {
/* sparse block */
*binfo_out = binfo;
*index_out = 0;
return binfo->key_block[0];
}
restart_kom("find_block_index_key found nothing\n");
}
}
return 0;
}
static void
join_range(Local_to_global *l2g,
struct l2g_block_info *first,
struct l2g_block_info *last)
{
int next;
int i;
struct l2g_block_info *binfo;
assert(first < last);
assert(l2g->blocks <= first && first < l2g->blocks + l2g->num_blocks);
assert(l2g->blocks <= last && last < l2g->blocks + l2g->num_blocks);
if (is_dense(first))
make_sparse(first);
else
sparse_compact(first);
next = first->first_free;
for (binfo = first + 1; binfo <= last; ++binfo)
{
for (i = 0; i < binfo->first_free; ++i)
if (binfo->value_block[i] != 0)
{
first->value_block[next] = binfo->value_block[i];
first->key_block[next] = key_value(binfo, i);
next++;
}
if (!is_dense(binfo))
sfree(binfo->key_block);
sfree(binfo->value_block);
}
while (binfo < l2g->blocks + l2g->num_blocks)
{
*(binfo - (last - first)) = *binfo;
++binfo;
}
l2g->num_blocks -= last - first;
l2g->blocks = srealloc(l2g->blocks,
l2g->num_blocks * sizeof(struct l2g_block_info));
assert(next <= L2G_BLOCKSIZE);
first->first_free = next;
first->zeroes = L2G_BLOCKSIZE - next;
}
static int
join_blocks(Local_to_global *l2g,
struct l2g_block_info *binfo)
{
int zeroes;
int gain;
int best;
int i;
if (binfo->zeroes == 0)
/* This block is full. */
return 0;
/* Note: on rare occasions we might be able to create one dense
block from two (dense or sparse) blocks or one sparse block.
We don't bother. The gain would probably be small, and it
would complicate the algorithm significantly. */
zeroes = L2G_BLOCKSIZE;
gain = 0;
best = 0;
for (i = 0; zeroes > 0 && i <= binfo - l2g->blocks; ++i)
{
zeroes -= (L2G_BLOCKSIZE - (binfo-i)->zeroes);
gain += (is_dense(binfo-i) ? 1 : 2);
if (gain > 2 && zeroes >= 0)
best = i;
}
if (best > 0)
{
join_range(l2g, binfo - best, binfo);
return 1;
}
zeroes = L2G_BLOCKSIZE;
gain = 0;
best = 0;
for (i = 0; zeroes > 0 && i < l2g->num_blocks - (binfo - l2g->blocks); ++i)
{
zeroes -= (L2G_BLOCKSIZE - (binfo+i)->zeroes);
gain += (is_dense(binfo+i) ? 1 : 2);
if (gain > 2 && zeroes >= 0)
best = i;
}
if (best > 0)
{
join_range(l2g, binfo, binfo + best);
return 1;
}
return 0;
}
/* ================================================================ */
/* ==== Outside accessible functions ==== */
......@@ -258,6 +499,7 @@ l2g_init(Local_to_global *l2g)
{
/* Initialize the main structure */
l2g->num_blocks = 0;
l2g->first_unused = 1;
l2g->blocks = NULL;
}
......@@ -269,23 +511,26 @@ l2g_init(Local_to_global *l2g)
void
l2g_clear(Local_to_global *l2g)
{
L2g_block_info * binfo;
struct l2g_block_info * binfo;
int i;
/* Free the block info structures. */
binfo = l2g->blocks;
for (i = 0; i < l2g->num_blocks; ++i) {
for (i = 0; i < l2g->num_blocks; ++i)
{
if (binfo->key_block != NULL)
free(binfo->key_block);
free(binfo->value_block);
sfree(binfo->key_block);
sfree(binfo->value_block);
binfo++;
}
/* Free the block pointers. */
l2g->num_blocks = 0;
if (l2g->blocks != NULL) {
free(l2g->blocks);
l2g->first_unused = 1;
if (l2g->blocks != NULL)
{
sfree(l2g->blocks);
l2g->blocks = NULL;
}
}
......@@ -297,38 +542,25 @@ l2g_clear(Local_to_global *l2g)
*/
void
l2g_copy(Local_to_global *from, Local_to_global *to)
l2g_copy(Local_to_global *dest, const Local_to_global *src)
{
L2g_block_info * binfo;
int i, j;
const struct l2g_block_info *binfo;
int i;
/* FIXME: More efficient code */
l2g_clear(to);
binfo = from->blocks;
for (i = 0; i < from->num_blocks; ++i) {
if (binfo->key_block == NULL) {
/* Dense block */
for (j = 0; j < binfo->first_free; ++j) {
if (binfo->value_block[j] != 0)
l2g_append(to, binfo->start + j, binfo->value_block[j]);
}
} else {
/* Sparse block */
for (j = 0; j < binfo->first_free; ++j) {
if (binfo->value_block[j] != 0)
l2g_append(to, binfo->key_block[j], binfo->value_block[j]);
}
}
l2g_clear(dest);
binfo++;
}
for (binfo = src->blocks; binfo < src->blocks + src->num_blocks; ++binfo)
for (i = 0; i < binfo->first_free; ++i)
l2g_append(dest, key_value(binfo, i), binfo->value_block[i]);
assert(src->first_unused >= dest->first_unused);
dest->first_unused = src->first_unused;
}
/*