656 lines
17 KiB
C++
656 lines
17 KiB
C++
|
|
#include "OVOneToOne.h"
|
|
#include "OVHeapArray.h"
|
|
#include "ov_utility.h"
|
|
|
|
#define HASH(value,mask) (((value^(value>>24))^((value>>8)^(value>>16)))&mask)
|
|
|
|
|
|
/* FYI: "up" stands for UniquePair -- a precursor to OneToOne */
|
|
|
|
namespace {
|
|
typedef struct {
|
|
int active;
|
|
ov_word forward_value, reverse_value;
|
|
ov_size forward_next, reverse_next;
|
|
} up_element;
|
|
}
|
|
|
|
struct _OVOneToOne {
|
|
OVHeap *heap;
|
|
ov_uword mask;
|
|
ov_size size, n_inactive;
|
|
ov_word next_inactive;
|
|
up_element *elem;
|
|
ov_word *forward;
|
|
ov_word *reverse;
|
|
};
|
|
|
|
OVstatus OVOneToOne_Init(OVOneToOne * up, OVHeap * heap)
|
|
{
|
|
ov_utility_zero_range(up, up + 1);
|
|
up->heap = heap;
|
|
return_OVstatus_SUCCESS;
|
|
}
|
|
|
|
OVOneToOne *OVOneToOne_New(OVHeap * heap)
|
|
{
|
|
OVOneToOne *up;
|
|
up = OVHeap_ALLOC(heap, OVOneToOne);
|
|
up->heap = heap;
|
|
return up;
|
|
}
|
|
|
|
void OVOneToOne_Purge(OVOneToOne * up)
|
|
{
|
|
if(up) {
|
|
OVHeapArray_FREE_AUTO_NULL(up->elem);
|
|
OVHeap_FREE_AUTO_NULL(up->heap, up->forward);
|
|
OVHeap_FREE_AUTO_NULL(up->heap, up->reverse);
|
|
}
|
|
}
|
|
|
|
void OVOneToOne_Del(OVOneToOne * up)
|
|
{
|
|
if(up) {
|
|
OVOneToOne_Purge(up);
|
|
OVHeap_FREE_AUTO_NULL(up->heap, up);
|
|
}
|
|
}
|
|
|
|
void OVOneToOne_Reset(OVOneToOne * up)
|
|
{
|
|
OVOneToOne_Purge(up);
|
|
OVOneToOne_Init(up, up->heap);
|
|
}
|
|
|
|
void OVOneToOne_Dump(OVOneToOne * up)
|
|
{
|
|
ov_uword a;
|
|
ov_boolean empty = OV_TRUE;
|
|
if(up && up->mask) {
|
|
for(a = 0; a <= up->mask; a++) {
|
|
if(up->forward[a] || up->reverse[a]) {
|
|
fprintf(stderr,
|
|
" OVOneToOne_Dump: Hashes forward[0x%02x]->%d reverse[0x%02x]->%d\n",
|
|
(unsigned int) a, (int) up->forward[a],
|
|
(unsigned int) a, (int) up->reverse[a]);
|
|
empty = OV_FALSE;
|
|
}
|
|
}
|
|
|
|
for(a = 0; a < up->size; a++)
|
|
if(up->elem[a].active) {
|
|
fprintf(stderr,
|
|
" OVOneToOne_Dump: Elements %d: %d (->%d) %d (->%d)\n",
|
|
(int) a + 1,
|
|
(int) up->elem[a].forward_value,
|
|
(int) up->elem[a].forward_next,
|
|
(int) up->elem[a].reverse_value, (int) up->elem[a].reverse_next);
|
|
empty = OV_FALSE;
|
|
}
|
|
}
|
|
if(empty) {
|
|
fprintf(stderr, " OVOneToOne_Dump: Empty. \n");
|
|
}
|
|
}
|
|
|
|
static void Reload(OVOneToOne * up)
|
|
{ /* assumes hash tables are clean and initialized to zero */
|
|
#ifdef DEBUG_UP
|
|
fprintf(stderr, "Reload-Debug: entered\n");
|
|
#endif
|
|
ov_uword mask = up->mask;
|
|
|
|
if(up->elem && mask) {
|
|
{
|
|
up_element *elem = up->elem;
|
|
ov_uword a;
|
|
for(a = 0; a < up->size; a++) {
|
|
if(elem->active) {
|
|
elem->forward_next = 0; /* 0 is the sentinel for end of list */
|
|
elem->reverse_next = 0;
|
|
}
|
|
elem++;
|
|
}
|
|
}
|
|
|
|
{
|
|
ov_uword a;
|
|
ov_word *forward = up->forward;
|
|
ov_word *reverse = up->reverse;
|
|
up_element *elem = up->elem;
|
|
{
|
|
ov_word fwd, rev;
|
|
ov_word fwd_val;
|
|
ov_word rev_val;
|
|
for(a = 0; a < up->size; a++) {
|
|
if(elem->active) {
|
|
fwd_val = elem->forward_value;
|
|
rev_val = elem->reverse_value;
|
|
fwd = HASH(fwd_val, mask);
|
|
rev = HASH(rev_val, mask);
|
|
elem->forward_next = forward[fwd];
|
|
forward[fwd] = a + 1; /* NOTE: 1 based indices */
|
|
elem->reverse_next = reverse[rev];
|
|
reverse[rev] = a + 1;
|
|
}
|
|
elem++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG_UP
|
|
{
|
|
ov_uword a;
|
|
for(a = 0; a <= up->mask; a++) {
|
|
fprintf(stderr, "Reload-Debug: forward[%d]=%d, reverse[%d]=%d\n",
|
|
a, up->forward[a], a, up->reverse[a]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
OVreturn_word OVOneToOne_GetReverse(OVOneToOne * up, ov_word reverse_value)
|
|
{
|
|
if(!up) {
|
|
OVreturn_word result = { OVstatus_NULL_PTR };
|
|
return result;
|
|
} else {
|
|
if(up->mask) {
|
|
ov_word hash = HASH(reverse_value, up->mask);
|
|
up_element *elem = up->elem;
|
|
ov_word index = up->reverse[hash];
|
|
up_element *cur_elem = elem + (index - 1);
|
|
|
|
while(index) {
|
|
if(cur_elem->reverse_value == reverse_value) {
|
|
OVreturn_word result = { OVstatus_SUCCESS };
|
|
result.word = cur_elem->forward_value;
|
|
return result;
|
|
}
|
|
index = cur_elem->reverse_next;
|
|
cur_elem = elem + (index - 1);
|
|
}
|
|
}
|
|
{
|
|
OVreturn_word result = { OVstatus_NOT_FOUND };
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
OVreturn_word OVOneToOne_IterateForward(OVOneToOne * up, ov_word * hidden)
|
|
{
|
|
if(!up) {
|
|
OVreturn_word result = { OVstatus_NULL_PTR };
|
|
return result;
|
|
} else {
|
|
OVreturn_word result = { OVstatus_YES };
|
|
unsigned int a;
|
|
up_element *cur_elem = up->elem + (*hidden);
|
|
for(a = *hidden; a < up->size; a++) {
|
|
if(cur_elem->active) {
|
|
result.word = cur_elem->forward_value;
|
|
*hidden = a + 1;
|
|
return result;
|
|
}
|
|
cur_elem++;
|
|
}
|
|
*hidden = 0;
|
|
result.status = OVstatus_NO;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
OVreturn_word OVOneToOne_GetForward(OVOneToOne * up, ov_word forward_value)
|
|
{
|
|
if(!up) {
|
|
OVreturn_word result = { OVstatus_NULL_PTR };
|
|
return result;
|
|
} else {
|
|
ov_uword mask = up->mask;
|
|
if(mask) {
|
|
ov_word hash = HASH(forward_value, mask);
|
|
up_element *elem = up->elem;
|
|
ov_word index = up->forward[hash];
|
|
up_element *cur_elem = elem + (index - 1);
|
|
while(index) {
|
|
if(cur_elem->forward_value == forward_value) {
|
|
OVreturn_word result = { OVstatus_SUCCESS };
|
|
result.word = cur_elem->reverse_value;
|
|
return result;
|
|
}
|
|
index = cur_elem->forward_next;
|
|
cur_elem = elem + (index - 1);
|
|
}
|
|
}
|
|
{
|
|
OVreturn_word result = { OVstatus_NOT_FOUND };
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
static OVstatus Recondition(OVOneToOne * up, ov_uword size, int force)
|
|
{
|
|
if(!up) {
|
|
return_OVstatus_NULL_PTR;
|
|
} else {
|
|
ov_uword mask = up->mask;
|
|
#ifdef DEBUG_UP
|
|
fprintf(stderr, "Recondition-Debug: entered for size %d.\n", size);
|
|
#endif
|
|
if((size > mask) || ((size << 2) < mask) || force) {
|
|
|
|
while((size << 2) < mask) {
|
|
mask = mask >> 1;
|
|
if(mask < 2)
|
|
break;
|
|
}
|
|
|
|
while(size > mask) {
|
|
mask = (mask << 1) + 1;
|
|
}
|
|
|
|
#ifdef DEBUG_UP
|
|
fprintf(stderr, "Recondition-Debug: mask %d\n", mask);
|
|
#endif
|
|
{
|
|
if(!up->elem) {
|
|
up->elem = OVHeapArray_CALLOC(up->heap, up_element, size);
|
|
if(!up->elem) {
|
|
return_OVstatus_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
if(mask != up->mask) {
|
|
ov_word *tmp_forward = OVHeap_CALLOC(up->heap, ov_word, mask + 1);
|
|
ov_word *tmp_reverse = OVHeap_CALLOC(up->heap, ov_word, mask + 1);
|
|
if(!(tmp_forward && tmp_reverse)) { /* validate */
|
|
OVHeap_FREE_AUTO_NULL(up->heap, tmp_forward);
|
|
OVHeap_FREE_AUTO_NULL(up->heap, tmp_reverse);
|
|
/* being unable to condition is not an error */
|
|
} else {
|
|
/* impossible to fail after here... */
|
|
OVHeap_FREE_AUTO_NULL(up->heap, up->forward);
|
|
OVHeap_FREE_AUTO_NULL(up->heap, up->reverse);
|
|
up->forward = tmp_forward;
|
|
up->reverse = tmp_reverse;
|
|
up->mask = mask;
|
|
}
|
|
} else {
|
|
ov_utility_zero_range(up->forward, up->forward + (up->mask + 1));
|
|
ov_utility_zero_range(up->reverse, up->reverse + (up->mask + 1));
|
|
}
|
|
Reload(up);
|
|
}
|
|
}
|
|
}
|
|
return_OVstatus_SUCCESS;
|
|
}
|
|
|
|
OVstatus OVOneToOne_Pack(OVOneToOne * up)
|
|
{
|
|
if(!up) {
|
|
return_OVstatus_NULL_PTR;
|
|
} else {
|
|
if(up->n_inactive && up->elem) {
|
|
ov_uword new_size = 0;
|
|
up_element *src = up->elem, *dst = up->elem;
|
|
ov_uword a;
|
|
|
|
for(a = 0; a < up->size; a++) {
|
|
if(src->active) {
|
|
if(src > dst) {
|
|
*dst = *src;
|
|
}
|
|
dst++;
|
|
new_size++;
|
|
}
|
|
src++;
|
|
}
|
|
up->n_inactive = 0;
|
|
up->next_inactive = 0;
|
|
if(new_size > 0 && new_size < up->size) {
|
|
if(!OVHeapArray_SET_SIZE(up->elem, up_element, new_size))
|
|
ov_utility_zero_range(up->elem + new_size, up->elem + up->size);
|
|
}
|
|
up->size = new_size;
|
|
return Recondition(up, new_size, OV_TRUE);
|
|
}
|
|
return_OVstatus_SUCCESS;
|
|
}
|
|
}
|
|
|
|
OVreturn_size OVOneToOne_GetSize(OVOneToOne * up)
|
|
{
|
|
if(!up) {
|
|
OVreturn_size result = { OVstatus_NULL_PTR };
|
|
return result;
|
|
} else {
|
|
OVreturn_size result = { OVstatus_SUCCESS };
|
|
result.size = up->size - up->n_inactive;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
OVstatus OVOneToOne_DelReverse(OVOneToOne * up, ov_word reverse_value)
|
|
{
|
|
if(!up) {
|
|
return_OVstatus_NULL_PTR;
|
|
} else {
|
|
ov_word mask = up->mask;
|
|
if(mask) {
|
|
ov_word rev_hash = HASH(reverse_value, mask);
|
|
ov_word rev = up->reverse[rev_hash];
|
|
|
|
if(!rev) {
|
|
return_OVstatus_NOT_FOUND;
|
|
} else {
|
|
up_element *rev_elem = NULL;
|
|
up_element *elem = up->elem;
|
|
ov_word rev_last = 0;
|
|
|
|
while(rev) {
|
|
rev_elem = elem + (rev - 1);
|
|
if(rev_elem->reverse_value == reverse_value)
|
|
break;
|
|
rev_last = rev;
|
|
rev = rev_elem->reverse_next;
|
|
}
|
|
|
|
if(rev_elem) {
|
|
ov_word rev_fwd_val = rev_elem->forward_value;
|
|
ov_word fwd_hash = HASH(rev_fwd_val, mask);
|
|
ov_word fwd = up->forward[fwd_hash];
|
|
ov_word fwd_last = 0;
|
|
up_element *fwd_elem = NULL;
|
|
|
|
while(fwd) {
|
|
fwd_elem = elem + (fwd - 1);
|
|
if(fwd_elem == rev_elem)
|
|
break;
|
|
fwd_last = fwd;
|
|
fwd = fwd_elem->forward_next;
|
|
}
|
|
|
|
if(rev && (rev == fwd)) {
|
|
|
|
/* excise element */
|
|
|
|
if(rev_last)
|
|
up->elem[rev_last - 1].reverse_next = rev_elem->reverse_next;
|
|
else
|
|
up->reverse[rev_hash] = rev_elem->reverse_next;
|
|
|
|
if(fwd_last)
|
|
up->elem[fwd_last - 1].forward_next = fwd_elem->forward_next;
|
|
else
|
|
up->forward[fwd_hash] = fwd_elem->forward_next;
|
|
|
|
/* store as inactive */
|
|
|
|
rev_elem->active = OV_FALSE;
|
|
rev_elem->forward_next = up->next_inactive;
|
|
up->next_inactive = rev;
|
|
up->n_inactive++;
|
|
if(up->n_inactive > (up->size >> 1)) /* over half of bits are inactive */
|
|
OVOneToOne_Pack(up);
|
|
return_OVstatus_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return_OVstatus_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
OVstatus OVOneToOne_DelForward(OVOneToOne * up, ov_word forward_value)
|
|
{
|
|
if(!up) {
|
|
return_OVstatus_NULL_PTR;
|
|
} else {
|
|
ov_word mask = up->mask;
|
|
if(mask) {
|
|
ov_word fwd_hash = HASH(forward_value, mask);
|
|
ov_word fwd = up->forward[fwd_hash];
|
|
if(!fwd) {
|
|
return_OVstatus_NOT_FOUND;
|
|
} else {
|
|
up_element *fwd_elem = NULL;
|
|
up_element *elem = up->elem;
|
|
ov_word fwd_last = 0;
|
|
|
|
while(fwd) {
|
|
fwd_elem = elem + (fwd - 1);
|
|
if(fwd_elem->forward_value == forward_value)
|
|
break;
|
|
fwd_last = fwd;
|
|
fwd = fwd_elem->forward_next;
|
|
}
|
|
|
|
if(fwd_elem) {
|
|
ov_word fwd_rev_val = fwd_elem->reverse_value;
|
|
ov_word rev_hash = HASH(fwd_rev_val, mask);
|
|
ov_word rev = up->reverse[rev_hash];
|
|
ov_word rev_last = 0;
|
|
up_element *rev_elem = NULL;
|
|
|
|
while(rev) {
|
|
rev_elem = elem + (rev - 1);
|
|
if(rev_elem == fwd_elem)
|
|
break;
|
|
rev_last = rev;
|
|
rev = rev_elem->reverse_next;
|
|
}
|
|
|
|
if(fwd && (fwd == rev)) {
|
|
|
|
/* excise elements */
|
|
|
|
if(fwd_last)
|
|
up->elem[fwd_last - 1].forward_next = fwd_elem->forward_next;
|
|
else
|
|
up->forward[fwd_hash] = fwd_elem->forward_next;
|
|
|
|
if(rev_last)
|
|
up->elem[rev_last - 1].reverse_next = rev_elem->reverse_next;
|
|
else
|
|
up->reverse[rev_hash] = rev_elem->reverse_next;
|
|
|
|
/* store as inactive */
|
|
|
|
fwd_elem->active = OV_FALSE;
|
|
fwd_elem->forward_next = up->next_inactive;
|
|
up->next_inactive = fwd;
|
|
up->n_inactive++;
|
|
if(up->n_inactive > (up->size >> 1)) /* over half of bits are inactive */
|
|
OVOneToOne_Pack(up);
|
|
return_OVstatus_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return_OVstatus_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
void OVOneToOne_Stats(OVOneToOne * up)
|
|
{
|
|
if(up && up->mask) {
|
|
int max_len = 0;
|
|
ov_uword a;
|
|
for(a = 0; a < up->mask; a++) {
|
|
{
|
|
ov_word index = up->forward[a];
|
|
up_element *elem = up->elem;
|
|
int cnt = 0;
|
|
if(index) {
|
|
up_element *cur_elem;
|
|
while(index) {
|
|
cur_elem = elem + (index - 1);
|
|
index = cur_elem->forward_next;
|
|
cnt++;
|
|
}
|
|
if(cnt > max_len)
|
|
max_len = cnt;
|
|
}
|
|
}
|
|
|
|
{
|
|
ov_word index = up->reverse[a];
|
|
up_element *elem = up->elem;
|
|
int cnt = 0;
|
|
if(index) {
|
|
up_element *cur_elem;
|
|
while(index) {
|
|
cur_elem = elem + (index - 1);
|
|
index = cur_elem->reverse_next;
|
|
cnt++;
|
|
}
|
|
if(cnt > max_len)
|
|
max_len = cnt;
|
|
}
|
|
}
|
|
|
|
}
|
|
fprintf(stderr, " OVOneToOne_Stats: MaxLen=%d ", max_len);
|
|
fprintf(stderr, "active=%d n_inactive=%d ", (int)( up->size - up->n_inactive),
|
|
(int) up->n_inactive);
|
|
fprintf(stderr, "mask=0x%x n_alloc=%lu\n", (unsigned int) up->mask,
|
|
(unsigned long) OVHeapArray_GET_SIZE(up->elem));
|
|
}
|
|
}
|
|
|
|
OVstatus OVOneToOne_Set(OVOneToOne * up, ov_word forward_value, ov_word reverse_value)
|
|
{
|
|
if(!up) {
|
|
return_OVstatus_NULL_PTR;
|
|
} else {
|
|
ov_word mask = up->mask;
|
|
ov_word fwd_hash = HASH(forward_value, mask);
|
|
ov_word rev_hash = HASH(reverse_value, mask);
|
|
up_element *fwd_elem = NULL;
|
|
up_element *rev_elem = NULL;
|
|
ov_word fwd;
|
|
ov_word rev;
|
|
if(!mask) {
|
|
fwd = 0;
|
|
rev = 0;
|
|
} else {
|
|
|
|
fwd = up->forward[fwd_hash];
|
|
rev = up->reverse[rev_hash];
|
|
|
|
#ifdef DEBUG_OVOneToOne
|
|
fprintf(stderr, "OVOneToOneSet-Debug: set %d,%d\n", forward_value, reverse_value);
|
|
fprintf(stderr, "OVOneToOneSet-Debug: fwd_hash %d rev_hash %d mask %d size %d\n",
|
|
fwd_hash, rev_hash, up->mask, up->size);
|
|
fprintf(stderr, "OVOneToOneSet-Debug: before search fwd rev %d %d\n", fwd, rev);
|
|
#endif
|
|
|
|
{ /* find elements if they exist, and detect erroneous conditions */
|
|
|
|
up_element *elem = up->elem;
|
|
|
|
#ifdef DEBUG_OVOneToOne
|
|
{
|
|
int a;
|
|
for(a = 0; a < up->size; a++) {
|
|
fprintf(stderr, "OVOneToOneSet-Debug: on entry %d forward_next: %d\n",
|
|
a + 1, elem[a].forward_next);
|
|
}
|
|
|
|
for(a = 0; a <= up->mask; a++) {
|
|
fprintf(stderr,
|
|
"OVOneToOneSet-Debug: on entry %d forward hash %d: reverse_hash: %d\n",
|
|
a, up->forward[a], up->reverse[a]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
while(fwd) {
|
|
fwd_elem = elem + (fwd - 1);
|
|
if(fwd_elem->forward_value == forward_value)
|
|
break;
|
|
fwd = fwd_elem->forward_next;
|
|
}
|
|
while(rev) {
|
|
rev_elem = elem + (rev - 1);
|
|
if(rev_elem->reverse_value == reverse_value)
|
|
break;
|
|
rev = rev_elem->reverse_next;
|
|
}
|
|
}
|
|
|
|
if((fwd && (!rev)) || (rev && (!fwd))) {
|
|
return_OVstatus_DUPLICATE;
|
|
}
|
|
}
|
|
if(!(fwd || rev)) {
|
|
ov_size new_index;
|
|
/* new pair */
|
|
#ifdef DEBUG_OVOneToOne
|
|
fprintf(stderr, "OVOneToOneSet-Debug: New pair.\n");
|
|
#endif
|
|
if(up->n_inactive) {
|
|
new_index = up->next_inactive;
|
|
up->next_inactive = up->elem[new_index - 1].forward_next;
|
|
up->n_inactive--;
|
|
} else {
|
|
if(up->elem && (!OVHeapArray_CHECK(up->elem, up_element, up->size))) {
|
|
return_OVstatus_OUT_OF_MEMORY;
|
|
} else {
|
|
OVstatus result;
|
|
if(OVreturn_IS_ERROR(result = Recondition(up, up->size + 1, OV_FALSE))) {
|
|
return result;
|
|
} else {
|
|
/* guaranteed to succeed past this point, so we can increase size */
|
|
new_index = ++up->size;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
up_element *elem = up->elem + (new_index - 1);
|
|
elem->forward_value = forward_value;
|
|
elem->reverse_value = reverse_value;
|
|
elem->active = OV_TRUE;
|
|
|
|
/* regenerate new hashes */
|
|
mask = up->mask;
|
|
fwd_hash = HASH(forward_value, mask);
|
|
rev_hash = HASH(reverse_value, mask);
|
|
|
|
{
|
|
ov_word *forward_start_index = up->forward + fwd_hash;
|
|
ov_word *reverse_start_index = up->reverse + rev_hash;
|
|
|
|
elem->forward_next = *forward_start_index;
|
|
*forward_start_index = new_index; /* note the +1 offset */
|
|
elem->reverse_next = *reverse_start_index;
|
|
*reverse_start_index = new_index; /* note the +1 offset */
|
|
}
|
|
|
|
#ifdef DEBUG_OVOneToOne
|
|
{
|
|
int a;
|
|
for(a = 0; a <= up->mask; a++) {
|
|
fprintf(stderr, "OVOneToOneSet-Debug: forward[%d]=%d, reverse[%d]=%d\n",
|
|
a, up->forward[a], a, up->reverse[a]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
} else if(fwd_elem != rev_elem) {
|
|
return_OVstatus_MISMATCH;
|
|
} else {
|
|
return_OVstatus_NO_EFFECT;
|
|
/* exists and matched, so do nothing */
|
|
}
|
|
}
|
|
return_OVstatus_SUCCESS;
|
|
}
|