Skip to content

Commit

Permalink
Merge pull request nats-io#435 from nats-io/hash_remove_single
Browse files Browse the repository at this point in the history
Added nats[Str]Hash_RemoveSingle to remove the sole element of a hash
  • Loading branch information
kozlovic authored Jul 1, 2021
2 parents 0033d1c + d71b261 commit 9096dfd
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 21 deletions.
112 changes: 99 additions & 13 deletions src/hash.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015-2020 The NATS Authors
// Copyright 2015-2021 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down Expand Up @@ -202,6 +202,17 @@ natsHash_Get(natsHash *hash, int64_t key)
return NULL;
}

static void
_maybeShrink(natsHash *hash)
{
if (hash->canResize
&& (hash->numBkts > _BSZ)
&& (hash->used < hash->numBkts / 4))
{
_shrink(hash);
}
}

void*
natsHash_Remove(natsHash *hash, int64_t key)
{
Expand All @@ -224,12 +235,7 @@ natsHash_Remove(natsHash *hash, int64_t key)
hash->used--;

// Check for resizing
if (hash->canResize
&& (hash->numBkts > _BSZ)
&& (hash->used < hash->numBkts / 4))
{
_shrink(hash);
}
_maybeShrink(hash);

break;
}
Expand All @@ -240,6 +246,38 @@ natsHash_Remove(natsHash *hash, int64_t key)
return dataRemoved;
}

natsStatus
natsHash_RemoveSingle(natsHash *hash, int64_t *key, void **data)
{
natsHashEntry *e = NULL;
int i;

if (hash->used != 1)
return nats_setDefaultError(NATS_ERR);

for (i=0; i<hash->numBkts; i++)
{
e = hash->bkts[i];
if (e != NULL)
{
if (key != NULL)
*key = e->key;
if (data != NULL)
*data = e->data;
_freeEntry(e);

hash->used--;
hash->bkts[i] = NULL;

// Check for resizing
_maybeShrink(hash);

break;
}
}
return NATS_OK;
}

void
natsHash_Destroy(natsHash *hash)
{
Expand Down Expand Up @@ -610,6 +648,17 @@ _freeStrEntry(natsStrHashEntry *e)
NATS_FREE(e);
}

static void
_maybeShrinkStr(natsStrHash *hash)
{
if (hash->canResize
&& (hash->numBkts > _BSZ)
&& (hash->used < hash->numBkts / 4))
{
_shrinkStr(hash);
}
}

void*
natsStrHash_Remove(natsStrHash *hash, char *key)
{
Expand All @@ -636,12 +685,7 @@ natsStrHash_Remove(natsStrHash *hash, char *key)
hash->used--;

// Check for resizing
if (hash->canResize
&& (hash->numBkts > _BSZ)
&& (hash->used < hash->numBkts / 4))
{
_shrinkStr(hash);
}
_maybeShrinkStr(hash);

break;
}
Expand All @@ -652,6 +696,48 @@ natsStrHash_Remove(natsStrHash *hash, char *key)
return dataRemoved;
}

natsStatus
natsStrHash_RemoveSingle(natsStrHash *hash, char **key, void **data)
{
natsStrHashEntry *e = NULL;
int i;

if (hash->used != 1)
return nats_setDefaultError(NATS_ERR);

for (i=0; i<hash->numBkts; i++)
{
e = hash->bkts[i];
if (e != NULL)
{
if (key != NULL)
{
char *retKey = e->key;

if (e->freeKey)
{
retKey = NATS_STRDUP(e->key);
if (retKey == NULL)
return nats_setDefaultError(NATS_NO_MEMORY);
}
*key = retKey;
}
if (data != NULL)
*data = e->data;
_freeStrEntry(e);

hash->used--;
hash->bkts[i] = NULL;

// Check for resizing
_maybeShrinkStr(hash);

break;
}
}
return NATS_OK;
}

void
natsStrHash_Destroy(natsStrHash *hash)
{
Expand Down
8 changes: 7 additions & 1 deletion src/hash.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015-2020 The NATS Authors
// Copyright 2015-2021 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand Down Expand Up @@ -93,6 +93,9 @@ natsHash_Get(natsHash *hash, int64_t key);
void*
natsHash_Remove(natsHash *hash, int64_t key);

natsStatus
natsHash_RemoveSingle(natsHash *hash, int64_t *key, void **data);

void
natsHash_Destroy(natsHash *hash);

Expand Down Expand Up @@ -132,6 +135,9 @@ natsStrHash_Get(natsStrHash *hash, char *key);
void*
natsStrHash_Remove(natsStrHash *hash, char *key);

natsStatus
natsStrHash_RemoveSingle(natsStrHash *hash, char **key, void **data);

void
natsStrHash_Destroy(natsStrHash *hash);

Expand Down
10 changes: 3 additions & 7 deletions src/pub.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,17 +376,13 @@ _respHandler(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *clos
rt = (char*) (natsMsg_GetSubject(msg) + NATS_REQ_ID_OFFSET);
resp = (respInfo*) natsStrHash_Remove(nc->respMap, rt);
}
else if ((resp == NULL) && (natsStrHash_Count(nc->respMap) == 1))
else if (natsStrHash_Count(nc->respMap) == 1)
{
// Only if the subject is completely different, we assume that it
// could be the server that has rewritten the subject and so if there
// is a single entry, use that.
natsStrHashIter iter;
void *value = NULL;

natsStrHashIter_Init(&iter, nc->respMap);
natsStrHashIter_Next(&iter, NULL, &value);
natsStrHashIter_RemoveCurrent(&iter);
void *value = NULL;
natsStrHash_RemoveSingle(nc->respMap, NULL, &value);
resp = (respInfo*) value;
}
if (resp != NULL)
Expand Down
84 changes: 84 additions & 0 deletions test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -2108,7 +2108,42 @@ test_natsHash(void)

test("Destroy: ");
natsHash_Destroy(hash);
hash = NULL;
testCond(1);

test("Create new: ");
s = natsHash_Create(&hash, 4);
testCond(s == NATS_OK);

test("Populate: ");
s = natsHash_Set(hash, 1, (void*) 1, NULL);
IFOK(s, natsHash_Set(hash, 2, (void*) 2, NULL));
IFOK(s, natsHash_Set(hash, 3, (void*) 3, NULL));
testCond(s == NATS_OK);

test("Remove one: ");
s = (natsHash_Remove(hash, 2) == (void*) 2) ? NATS_OK : NATS_ERR;
testCond(s == NATS_OK);

test("RemoveSingle fails if more than one: ");
s = natsHash_RemoveSingle(hash, &key, NULL);
testCond(s == NATS_ERR);
nats_clearLastError();

test("Remove second: ");
s = (natsHash_Remove(hash, 1) == (void*) 1) ? NATS_OK : NATS_ERR;
testCond(s == NATS_OK);

test("Remove single: ");
key = 0;
oldval = NULL;
s = natsHash_RemoveSingle(hash, &key, &oldval);
testCond((s == NATS_OK)
&& (hash->used == 0)
&& (key == 3)
&& (oldval == (void*) 3));

natsHash_Destroy(hash);
}

static void
Expand Down Expand Up @@ -2343,7 +2378,56 @@ test_natsStrHash(void)

test("Destroy: ");
natsStrHash_Destroy(hash);
hash = NULL;
testCond(1);

test("Create new: ");
s = natsStrHash_Create(&hash, 4);
testCond(s == NATS_OK);

test("Populate: ");
s = natsStrHash_Set(hash, (char*) "1", true, (void*) 1, NULL);
IFOK(s, natsStrHash_Set(hash, (char*) "2", true, (void*) 2, NULL));
IFOK(s, natsStrHash_Set(hash, (char*) "3", true, (void*) 3, NULL));
testCond(s == NATS_OK);

test("Remove one: ");
s = (natsStrHash_Remove(hash, (char*) "2") == (void*) 2) ? NATS_OK : NATS_ERR;
testCond(s == NATS_OK);

test("RemoveSingle fails if more than one: ");
s = natsStrHash_RemoveSingle(hash, &key, NULL);
testCond(s == NATS_ERR);
nats_clearLastError();

test("Remove second: ");
s = (natsStrHash_Remove(hash, (char*) "1") == (void*) 1) ? NATS_OK : NATS_ERR;
testCond(s == NATS_OK);

test("Remove single (copy of key): ");
key = NULL;
oldval = NULL;
s = natsStrHash_RemoveSingle(hash, &key, &oldval);
testCond((s == NATS_OK)
&& (hash->used == 0)
&& (strcmp(key, "3") == 0)
&& (oldval == (void*) 3));
free(key);
key = NULL;
oldval = NULL;

test("Add key without copy: ");
s = natsStrHash_Set(hash, (char*) "4", false, (void*) 4, NULL);
testCond(s == NATS_OK);

test("Remove single (no copy of key): ");
s = natsStrHash_RemoveSingle(hash, &key, &oldval);
testCond((s == NATS_OK)
&& (hash->used == 0)
&& (strcmp(key, "4") == 0)
&& (oldval == (void*) 4));

natsStrHash_Destroy(hash);
}

static const char*
Expand Down

0 comments on commit 9096dfd

Please sign in to comment.