Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
asn1c
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
asn1c
Commits
adfcde29
Commit
adfcde29
authored
7 years ago
by
Lev Walkin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
generic hashing mechanism
parent
3bcab4eb
vlm_master
velichkov_s1ap_plus_option_group
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1043 additions
and
1 deletion
+1043
-1
libasn1common/Makefile.am
libasn1common/Makefile.am
+2
-1
libasn1common/genhash.c
libasn1common/genhash.c
+811
-0
libasn1common/genhash.h
libasn1common/genhash.h
+230
-0
No files found.
libasn1common/Makefile.am
View file @
adfcde29
...
@@ -7,5 +7,6 @@ noinst_LTLIBRARIES = libasn1common.la
...
@@ -7,5 +7,6 @@ noinst_LTLIBRARIES = libasn1common.la
libasn1common_la_SOURCES
=
\
libasn1common_la_SOURCES
=
\
asn1_ref.c asn1_ref.h
\
asn1_ref.c asn1_ref.h
\
asn1_buffer.c asn1_buffer.h
\
asn1_buffer.c asn1_buffer.h
\
asn1_namespace.c asn1_namespace.h
asn1_namespace.c asn1_namespace.h
\
genhash.c genhash.h
This diff is collapsed.
Click to expand it.
libasn1common/genhash.c
0 → 100644
View file @
adfcde29
/*
* Copyright (c) 2002-2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
* Copyright (c) 2001-2004 Netli, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: genhash.c 447 2005-06-07 06:51:10Z vlm $
*/
/*
* Implementation of a hash data structure.
* This particular implementation is supposed to be space-efficient
* particularly in the case of tiny number of hash elements.
* It also has an aggressive hash buckets expanding technique, which allows
* to deal with increasing number of elements without a loss of search speed.
*
* Generally, one structure of type genhash_t is allocated per hash set.
* This structure is supposed to hold all information related to the current
* set, and also holds a tiny number of hash elements, when hash hasn't yet
* grown up. When the number of elements reaches some point, part of the
* genhash_t structure is reused to contain the pointers to the actual
* hash buckets and LRU (Least Recently Used) list's head and tail.
* Elements which were held inside genhash_t will be moved to the hash buckets.
*
* Said above effectively means two modes of operation: TINY and NORMAL.
* They can be distinguished by examining the h->numbuckets value, which
* is 0 for TINY and greater for NORMAL mode.
*
* In the TINY mode we use a lower part of the genhash_t structure
* (lower 32 bytes from 64 bytes of genhash_t) to hold up to IH_VALUE (4)
* key/value pairs.
*
* In the NORMAL mode we use the lower part of the genhash_t structure
* to hold a set of pointers, including a pointer to the hash buckets.
* We agressively expand hash buckets size when adding new elements
* to lower the number of key comparisons.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "genhash.h"
/* 1M entries, 4M RAM */
#define DEFAULT_MAXIMUM_HASH_BUCKETS_NUMBER (1024 * 1024)
static
int
maximum_hash_buckets_number
=
DEFAULT_MAXIMUM_HASH_BUCKETS_NUMBER
;
/*
* A single hash element structure which binds a value to its key.
*/
typedef
struct
genhash_el_s
{
unsigned
int
key_hash
;
/* Saved hash of the key */
void
*
key
;
void
*
value
;
struct
genhash_el_s
*
hash_next
;
/* Collision list inside the bucket */
struct
genhash_el_s
*
hash_prev
;
struct
genhash_el_s
*
lru_prev
;
/* Per-hash LRU list */
struct
genhash_el_s
*
lru_next
;
}
genhash_el
;
/*
* A hash structure with buckets etc.
*/
struct
genhash_s
{
int
(
*
keycmpf
)
(
const
void
*
lkey1
,
const
void
*
rkey2
);
unsigned
int
(
*
keyhashf
)
(
const
void
*
key
);
/* hash function */
void
(
*
keydestroyf
)
(
void
*
key
);
/* key destructor */
void
(
*
valuedestroyf
)
(
void
*
value
);
/* value destructor */
int
numelements
;
/* Total number of hash elements */
int
numbuckets
;
/* 0 means "use _TINY" */
int
lru_limit
;
/* Must be initialized explicitly */
genhash_iter_t
*
iters
;
/* Active iterators */
/* 32-byte boundary here */
union
{
#define IH_VALUES 4
/* Internally held key/value pairs for TINY mode */
struct
_internal_tiny_s
{
void
*
keys
[
IH_VALUES
];
void
*
values
[
IH_VALUES
];
}
_TINY
;
/* 32-byte structure */
struct
_internal_normal_s
{
genhash_el
*
lru_head
;
/* LRU list head */
genhash_el
*
lru_tail
;
/* LRU list tail */
genhash_el
**
buckets
;
/* Hash buckets */
/* void *unused; */
}
_NORMAL
;
}
un
;
#define tiny_keys un._TINY.keys
#define tiny_values un._TINY.values
#define lru_head un._NORMAL.lru_head
#define lru_tail un._NORMAL.lru_tail
#define buckets un._NORMAL.buckets
};
static
int
_genhash_normal_add
(
genhash_t
*
h
,
genhash_el
*
el
,
void
*
key
,
void
*
value
);
genhash_t
*
genhash_new
(
int
(
*
keycmpf
)
(
const
void
*
key1
,
const
void
*
key2
),
unsigned
int
(
*
keyhashf
)
(
const
void
*
key
),
void
(
*
keydestroyf
)
(
void
*
key
),
void
(
*
valuedestroyf
)
(
void
*
value
)
)
{
genhash_t
*
h
;
h
=
(
genhash_t
*
)
malloc
(
sizeof
(
genhash_t
));
if
(
!
h
)
return
NULL
;
memset
(
h
,
0
,
sizeof
(
genhash_t
));
genhash_reinit
(
h
,
keycmpf
,
keyhashf
,
keydestroyf
,
valuedestroyf
);
return
h
;
}
int
genhash_reinit
(
genhash_t
*
h
,
int
(
*
keycmpf
)
(
const
void
*
key1
,
const
void
*
key2
),
unsigned
int
(
*
keyhashf
)
(
const
void
*
key
),
void
(
*
keydestroyf
)
(
void
*
key
),
void
(
*
valuedestroyf
)
(
void
*
value
)
)
{
assert
(
keycmpf
&&
keyhashf
);
h
->
keycmpf
=
keycmpf
;
h
->
keyhashf
=
keyhashf
;
h
->
keydestroyf
=
keydestroyf
;
h
->
valuedestroyf
=
valuedestroyf
;
return
0
;
}
int
genhash_count
(
genhash_t
*
h
)
{
if
(
h
)
{
return
h
->
numelements
;
}
else
{
return
0
;
}
}
static
void
_remove_normal_hash_el
(
genhash_t
*
h
,
genhash_el
*
el
)
{
genhash_iter_t
*
iter
;
void
*
kd_arg
;
void
*
vd_arg
;
/* Remove from the collision list */
if
(
el
->
hash_prev
)
{
if
((
el
->
hash_prev
->
hash_next
=
el
->
hash_next
))
el
->
hash_next
->
hash_prev
=
el
->
hash_prev
;
}
else
{
if
((
h
->
buckets
[
el
->
key_hash
%
h
->
numbuckets
]
=
el
->
hash_next
))
el
->
hash_next
->
hash_prev
=
NULL
;
}
/* Remove from LRU list */
if
(
el
->
lru_prev
)
{
if
((
el
->
lru_prev
->
lru_next
=
el
->
lru_next
))
el
->
lru_next
->
lru_prev
=
el
->
lru_prev
;
else
h
->
lru_tail
=
el
->
lru_prev
;
}
else
{
if
(
h
->
lru_head
==
el
)
{
if
((
h
->
lru_head
=
el
->
lru_next
)
==
NULL
)
h
->
lru_tail
=
NULL
;
else
h
->
lru_head
->
lru_prev
=
NULL
;
}
}
/* Remember key and value */
kd_arg
=
el
->
key
;
vd_arg
=
el
->
value
;
/* Move iterators off the element being deleted */
for
(
iter
=
h
->
iters
;
iter
;
iter
=
iter
->
iter_next
)
{
assert
(
iter
->
hash_ptr
==
h
);
if
(
iter
->
un
.
location
==
el
)
{
iter
->
un
.
location
=
iter
->
order_lru_first
?
el
->
lru_prev
:
el
->
lru_next
;
}
}
free
(
el
);
h
->
numelements
--
;
/* Remove key and value */
if
(
h
->
keydestroyf
)
h
->
keydestroyf
(
kd_arg
);
if
(
h
->
valuedestroyf
)
h
->
valuedestroyf
(
vd_arg
);
}
static
inline
void
_genhash_normal_el_move2top
(
genhash_t
*
h
,
genhash_el
*
el
)
{
/* Disable sorting if iterators are running */
if
(
h
->
iters
)
return
;
/* Move to the top of the hash bucket */
if
(
el
->
hash_prev
)
{
int
bucket
=
el
->
key_hash
%
h
->
numbuckets
;
/* Remove from the current location */
if
((
el
->
hash_prev
->
hash_next
=
el
->
hash_next
))
el
->
hash_next
->
hash_prev
=
el
->
hash_prev
;
/* Move to the top of the hash bucket */
if
((
el
->
hash_next
=
h
->
buckets
[
bucket
]))
el
->
hash_next
->
hash_prev
=
el
;
h
->
buckets
[
bucket
]
=
el
;
el
->
hash_prev
=
NULL
;
}
/* Move to the top of LRU list */
if
(
h
->
lru_limit
&&
el
->
lru_prev
)
{
/* Remove from current location */
if
((
el
->
lru_prev
->
lru_next
=
el
->
lru_next
))
el
->
lru_next
->
lru_prev
=
el
->
lru_prev
;
else
h
->
lru_tail
=
el
->
lru_prev
;
/* Append to the head */
el
->
lru_prev
=
NULL
;
h
->
lru_head
->
lru_prev
=
el
;
el
->
lru_next
=
h
->
lru_head
;
h
->
lru_head
=
el
;
}
}
static
int
_expand_hash
(
genhash_t
*
h
)
{
int
newbuckets_count
;
genhash_el
**
newbuckets
;
/*
* Compute a new number of buckets value.
*/
if
(
h
->
numbuckets
)
{
newbuckets_count
=
h
->
numbuckets
<<
2
;
/* Too big hash table */
if
(
newbuckets_count
>
maximum_hash_buckets_number
)
{
if
(
h
->
numbuckets
<
maximum_hash_buckets_number
)
{
newbuckets_count
=
maximum_hash_buckets_number
;
}
else
{
/* No need to set errno here. */
return
-
1
;
}
}
}
else
{
/* 8 buckets -> 32 bytes of memory */
newbuckets_count
=
IH_VALUES
<<
1
;
if
(
newbuckets_count
>
maximum_hash_buckets_number
)
{
if
(
maximum_hash_buckets_number
)
{
newbuckets_count
=
maximum_hash_buckets_number
;
}
else
{
/* Allowed to store only IH_VALUES elements */
errno
=
EPERM
;
return
-
1
;
}
}
}
/*
* Allocate a new storage for buckets.
*/
newbuckets
=
malloc
(
newbuckets_count
*
sizeof
(
*
newbuckets
));
if
(
newbuckets
)
{
memset
(
newbuckets
,
0
,
newbuckets_count
*
sizeof
(
*
newbuckets
));
}
else
{
return
-
1
;
}
if
(
h
->
numbuckets
)
{
genhash_el
*
el
;
int
bucket
;
/*
* Rehash elements from old h->buckets to newbuckets.
* No need to touch LRU pointers and other stuff - it is okay.
*/
for
(
el
=
h
->
lru_tail
;
el
;
el
=
el
->
lru_prev
)
{
bucket
=
el
->
key_hash
%
newbuckets_count
;
el
->
hash_prev
=
NULL
;
if
((
el
->
hash_next
=
newbuckets
[
bucket
]))
el
->
hash_next
->
hash_prev
=
el
;
newbuckets
[
bucket
]
=
el
;
}
free
(
h
->
buckets
);
h
->
buckets
=
newbuckets
;
h
->
numbuckets
=
newbuckets_count
;
}
else
{
/*
* Moving from inline tiny storage into buckets.
*/
genhash_el
*
els
[
IH_VALUES
]
=
{
NULL
};
struct
_internal_tiny_s
tiny_substruct
;
int
i
;
int
saved_numelements
;
int
saved_lru_limit
;
genhash_iter_t
*
iter
;
/* Pre-allocate hash elements (for "undo") */
for
(
i
=
0
;
i
<
h
->
numelements
;
i
++
)
{
els
[
i
]
=
(
genhash_el
*
)
malloc
(
sizeof
(
genhash_el
));
if
(
els
[
i
]
==
NULL
)
{
for
(
i
=
0
;
i
<
h
->
numelements
;
i
++
)
if
(
els
[
i
])
free
(
els
[
i
]);
free
(
newbuckets
);
return
-
1
;
}
}
/* Save part of the union */
tiny_substruct
=
h
->
un
.
_TINY
;
/* Re-initialize this part in NORMAL model */
memset
(
&
h
->
un
.
_NORMAL
,
0
,
sizeof
(
h
->
un
.
_NORMAL
));
/* There was no allocated buckets, when in tiny hash mode. */
h
->
buckets
=
newbuckets
;
h
->
numbuckets
=
newbuckets_count
;
saved_numelements
=
h
->
numelements
;
saved_lru_limit
=
h
->
lru_limit
;
h
->
numelements
=
0
;
h
->
lru_limit
=
0
;
/* Disable LRU expiration for a while */
for
(
i
=
saved_numelements
-
1
;
i
>=
0
;
--
i
)
{
/*
* genhash_normal_add won't fail, if we supply
* an already allocated genhash_el *.
*/
(
void
)
_genhash_normal_add
(
h
,
els
[
i
],
tiny_substruct
.
keys
[
i
],
tiny_substruct
.
values
[
i
]);
}
/* Now, scan through iterators and convert them TINY->NORMAL */
for
(
iter
=
h
->
iters
;
iter
;
iter
=
iter
->
iter_next
)
{
assert
(
iter
->
hash_ptr
==
h
);
if
(
iter
->
un
.
item_number
<
0
||
iter
->
un
.
item_number
>=
saved_numelements
)
{
iter
->
un
.
location
=
0
;
}
else
{
iter
->
un
.
location
=
els
[
iter
->
un
.
item_number
];
}
}
h
->
lru_limit
=
saved_lru_limit
;
}
return
0
;
}
/*
* Won't return with error if el is provided.
*/
static
int
_genhash_normal_add
(
genhash_t
*
h
,
genhash_el
*
el
,
void
*
key
,
void
*
value
)
{
genhash_el
**
bucket
;
if
(
el
==
NULL
)
{
el
=
malloc
(
sizeof
(
*
el
));
if
(
el
==
NULL
)
{
/* Errno will be set by malloc() */
return
-
1
;
}
}
/* Maintain maximum number of entries */
if
(
h
->
lru_limit
)
{
while
(
h
->
numelements
>=
h
->
lru_limit
)
_remove_normal_hash_el
(
h
,
h
->
lru_tail
);
}
memset
(
el
,
0
,
sizeof
(
genhash_el
));
/* Compute the index of the collision list */
el
->
key_hash
=
h
->
keyhashf
(
key
);
bucket
=
&
h
->
buckets
[
el
->
key_hash
%
h
->
numbuckets
];
el
->
key
=
key
;
el
->
value
=
value
;
/*
* Add to the collision list
*/
el
->
hash_prev
=
NULL
;
if
((
el
->
hash_next
=
*
bucket
))
(
*
bucket
)
->
hash_prev
=
el
;
*
bucket
=
el
;
/*
* Add to the LRU list.
*/
if
(
h
->
lru_head
)
{
el
->
lru_next
=
h
->
lru_head
;
el
->
lru_next
->
lru_prev
=
el
;
h
->
lru_head
=
el
;
}
else
{
h
->
lru_head
=
el
;
h
->
lru_tail
=
el
;
}
h
->
numelements
++
;
return
0
;
}
int
genhash_add
(
genhash_t
*
h
,
void
*
key
,
void
*
value
)
{
if
(
key
==
NULL
)
{
errno
=
EINVAL
;
return
-
1
;
}
if
(
h
->
numbuckets
==
0
)
{
/* We have a tiny internally-held set of elements */
if
(
h
->
numelements
<
IH_VALUES
)
{
h
->
tiny_keys
[
h
->
numelements
]
=
key
;
h
->
tiny_values
[
h
->
numelements
]
=
value
;
h
->
numelements
++
;
return
0
;
}
if
(
_expand_hash
(
h
)
==
-
1
)
return
-
1
;
}
else
{
if
((
h
->
numelements
/
h
->
numbuckets
)
>
2
)
(
void
)
_expand_hash
(
h
);
}
return
_genhash_normal_add
(
h
,
NULL
,
key
,
value
);
}
int
genhash_addunique
(
genhash_t
*
h
,
void
*
key
,
void
*
value
)
{
if
(
genhash_get
(
h
,
key
))
{
errno
=
EEXIST
;
return
-
1
;
}
return
genhash_add
(
h
,
key
,
value
);
}
void
*
genhash_get
(
genhash_t
*
h
,
const
void
*
key
)
{
if
(
h
->
numbuckets
)
{
genhash_el
*
walk
;
int
bucket
=
h
->
keyhashf
(
key
)
%
h
->
numbuckets
;
for
(
walk
=
h
->
buckets
[
bucket
];
walk
;
walk
=
walk
->
hash_next
)
{
if
(
h
->
keycmpf
(
walk
->
key
,
key
)
==
0
)
{
_genhash_normal_el_move2top
(
h
,
walk
);
return
walk
->
value
;
}
}
}
else
{
/* TINY mode */
int
i
;
assert
(
h
->
numelements
<=
IH_VALUES
);
for
(
i
=
0
;
i
<
h
->
numelements
;
i
++
)
{
if
(
h
->
keycmpf
(
h
->
tiny_keys
[
i
],
key
)
==
0
)
/* Don't reorder in TINY mode */
return
h
->
tiny_values
[
i
];
}
}
errno
=
ESRCH
;
return
NULL
;
}
int
genhash_del
(
genhash_t
*
h
,
void
*
key
)
{
if
(
h
->
numbuckets
)
{
/* NORMAL mode */
genhash_el
*
walk
;
int
bucket
;
if
(
h
->
numelements
==
0
)
{
errno
=
ESRCH
;
return
-
1
;
/* not found */
}
bucket
=
h
->
keyhashf
(
key
)
%
h
->
numbuckets
;
for
(
walk
=
h
->
buckets
[
bucket
];
walk
;
walk
=
walk
->
hash_next
)
if
(
h
->
keycmpf
(
walk
->
key
,
key
)
==
0
)
break
;
if
(
walk
)
{
_remove_normal_hash_el
(
h
,
walk
);
return
0
;
}
}
else
{
/* TINY mode */
int
i
;
/* Look for matching key */
for
(
i
=
0
;
i
<
h
->
numelements
;
i
++
)
if
(
h
->
keycmpf
(
h
->
tiny_keys
[
i
],
key
)
==
0
)
break
;
if
(
i
<
h
->
numelements
)
{
/* Remember values */
void
*
kd_arg
=
h
->
tiny_keys
[
i
];
void
*
vd_arg
=
h
->
tiny_values
[
i
];
h
->
numelements
--
;
if
(
h
->
iters
)
{
/* If iterators are involved, we have to
* shift elements to maintain iteration order
* and avoid duplications */
genhash_iter_t
*
iter
;
memmove
(
&
h
->
tiny_keys
[
i
],
&
h
->
tiny_keys
[
i
+
1
],
(
h
->
numelements
-
i
)
*
sizeof
(
h
->
tiny_keys
[
0
]));
memmove
(
&
h
->
tiny_values
[
i
],
&
h
->
tiny_values
[
i
+
1
],
(
h
->
numelements
-
i
)
*
sizeof
(
h
->
tiny_values
[
0
]));
/* Shift the iterator's indexes */
for
(
iter
=
h
->
iters
;
iter
;
iter
=
iter
->
iter_next
)
{
int
in
=
iter
->
un
.
item_number
;
if
(
iter
->
order_lru_first
)
{
if
(
in
>
i
)
iter
->
un
.
item_number
--
;
}
else
{
if
(
in
>=
i
)
iter
->
un
.
item_number
--
;
}
}
}
else
{
/* Substitute it with the last one */
/* No harm if overwriting itself */
h
->
tiny_keys
[
i
]
=
h
->
tiny_keys
[
h
->
numelements
];
h
->
tiny_values
[
i
]
=
h
->
tiny_values
[
h
->
numelements
];
}
h
->
tiny_keys
[
h
->
numelements
]
=
0
;
h
->
tiny_values
[
h
->
numelements
]
=
0
;
/* Delete for real */
if
(
h
->
keydestroyf
)
h
->
keydestroyf
(
kd_arg
);
if
(
h
->
valuedestroyf
)
h
->
valuedestroyf
(
vd_arg
);
return
0
;
}
}
errno
=
ESRCH
;
return
-
1
;
}
/*
* Initialize a hash iterator.
*/
int
genhash_iter_init
(
genhash_iter_t
*
iter
,
genhash_t
*
h
,
int
reverse_order
)
{
iter
->
hash_ptr
=
h
;
iter
->
iter_prev
=
0
;
/* Add itself to the iterators list */
iter
->
iter_next
=
h
->
iters
;
h
->
iters
=
iter
;
iter
->
order_lru_first
=
reverse_order
;
if
(
h
->
numbuckets
)
{
/* NORMAL mode */
if
(
reverse_order
)
{
/* Least recent first order */
iter
->
un
.
location
=
h
->
lru_tail
;
}
else
{
/* Most recent first order */
iter
->
un
.
location
=
h
->
lru_head
;
}
}
else
{
/* TINY mode */
if
(
reverse_order
)
{
iter
->
un
.
item_number
=
0
;
}
else
{
iter
->
un
.
item_number
=
h
->
numelements
-
1
;
}
}
return
h
->
numelements
;
}
int
genhash_iter
(
genhash_iter_t
*
iter
,
void
*
key_p
,
void
*
val_p
)
{
void
**
key
=
key_p
;
void
**
val
=
val_p
;
genhash_t
*
h
=
iter
->
hash_ptr
;
if
(
h
->
numbuckets
)
{
/* NORMAL mode */
genhash_el
*
cur_el
=
iter
->
un
.
location
;
if
(
!
cur_el
)
/* Already finished */
return
0
;
if
(
key
)
*
key
=
cur_el
->
key
;
if
(
val
)
*
val
=
cur_el
->
value
;
/* Move pointer to the next hash element */
iter
->
un
.
location
=
iter
->
order_lru_first
?
cur_el
->
lru_prev
:
cur_el
->
lru_next
;
}
else
{
/* TINY mode */
if
(
iter
->
un
.
item_number
<
0
||
iter
->
un
.
item_number
>=
h
->
numelements
||
h
->
tiny_keys
[
iter
->
un
.
item_number
]
==
0
)
return
0
;
if
(
key
)
*
key
=
h
->
tiny_keys
[
iter
->
un
.
item_number
];
if
(
val
)
*
val
=
h
->
tiny_values
[
iter
->
un
.
item_number
];
/* Advance to the next element */
if
(
iter
->
order_lru_first
)
iter
->
un
.
item_number
++
;
else
iter
->
un
.
item_number
--
;
}
return
1
;
}
void
genhash_iter_done
(
genhash_iter_t
*
iter
)
{
assert
(
iter
->
hash_ptr
->
iters
);
/* Remove itself from the iterators list */
if
(
iter
->
iter_next
)
iter
->
iter_next
->
iter_prev
=
iter
->
iter_prev
;
if
(
iter
->
iter_prev
)
iter
->
iter_prev
->
iter_next
=
iter
->
iter_next
;
else
iter
->
hash_ptr
->
iters
=
iter
->
iter_next
;
/* Shift the head */
iter
->
hash_ptr
=
(
void
*
)
0xdeadbeef
;
}
int
genhash_set_lru_limit
(
genhash_t
*
h
,
int
value
)
{
if
(
h
)
{
int
prev_limit
=
h
->
lru_limit
;
if
(
value
>=
0
)
h
->
lru_limit
=
value
;
return
prev_limit
;
}
else
{
errno
=
EINVAL
;
return
-
1
;
}
}
int
genhash_set_buckets_limit
(
int
value
)
{
int
prev_limit
=
maximum_hash_buckets_number
;
if
(
value
>
0
)
{
maximum_hash_buckets_number
=
value
;
}
return
prev_limit
;
}
void
genhash_destroy
(
genhash_t
*
h
)
{
if
(
h
)
{
assert
(
h
->
iters
==
0
);
/* All iterators MUST be _done(). */
genhash_empty
(
h
,
1
,
1
);
free
(
h
);
}
}
void
genhash_empty
(
genhash_t
*
h
,
int
freekeys
,
int
freevalues
)
{
genhash_iter_t
*
iter
;
if
(
h
==
NULL
)
return
;
/*
* Don't free what could not be freed.
*/
if
(
h
->
keydestroyf
==
NULL
)
freekeys
=
0
;
if
(
h
->
valuedestroyf
==
NULL
)
freevalues
=
0
;
if
(
h
->
numbuckets
==
0
)
{
while
(
h
->
numelements
>
0
)
{
int
n
=
--
h
->
numelements
;
void
*
kd_arg
=
h
->
tiny_keys
[
n
];
void
*
vd_arg
=
h
->
tiny_values
[
n
];
if
(
freekeys
)
h
->
keydestroyf
(
kd_arg
);
if
(
freevalues
)
h
->
valuedestroyf
(
vd_arg
);
}
}
else
{
genhash_el
*
el
,
*
el_next
;
for
(
el
=
h
->
lru_head
;
el
;
el
=
el_next
)
{
void
*
kd_arg
=
el
->
key
;
void
*
vd_arg
=
el
->
value
;
el_next
=
el
->
lru_next
;
free
(
el
);
h
->
numelements
--
;
if
(
freekeys
)
h
->
keydestroyf
(
kd_arg
);
if
(
freevalues
)
h
->
valuedestroyf
(
vd_arg
);
}
free
(
h
->
buckets
);
h
->
numbuckets
=
0
;
/* Move back to TINY model */
}
memset
(
&
h
->
un
,
0
,
sizeof
(
h
->
un
));
/* Invalidate iterators in TINY model */
for
(
iter
=
h
->
iters
;
iter
;
iter
=
iter
->
iter_next
)
{
assert
(
iter
->
hash_ptr
==
h
);
iter
->
un
.
item_number
=
-
1
;
}
assert
(
h
->
numelements
==
0
);
}
/*----- Simple hash and compare functions for common data types ------*/
unsigned
int
hashf_int
(
const
void
*
key
)
{
return
(
*
(
const
int
*
)
key
^
(
*
(
const
int
*
)
key
>>
16
));
}
int
cmpf_int
(
const
void
*
key1
,
const
void
*
key2
)
{
return
(
*
(
const
int
*
)
key1
!=
*
(
const
int
*
)
key2
);
}
unsigned
int
hashf_void
(
const
void
*
key
)
{
return
((
int
)
key
^
((
int
)
key
>>
16
));
}
int
cmpf_void
(
const
void
*
key1
,
const
void
*
key2
)
{
return
(
key1
!=
key2
);
}
/*
* Phong's linear congruential hash
*/
#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c))
unsigned
int
hashf_string
(
const
void
*
keyarg
)
{
register
const
unsigned
char
*
key
;
register
unsigned
int
h
;
register
unsigned
char
c
;
key
=
keyarg
;
for
(
h
=
0
;
(
c
=
*
key
++
);)
dcharhash
(
h
,
c
);
return
(
h
);
}
int
cmpf_string
(
const
void
*
key1
,
const
void
*
key2
)
{
return
strcmp
((
const
char
*
)
key1
,
(
const
char
*
)
key2
);
}
This diff is collapsed.
Click to expand it.
libasn1common/genhash.h
0 → 100644
View file @
adfcde29
/*
* Copyright (c) 2002-2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
* Copyright (c) 2001-2004 Netli, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: genhash.h 447 2005-06-07 06:51:10Z vlm $
*/
#ifndef __GENHASH_H__
#define __GENHASH_H__
/*
* General purpose hashing framework.
* Refer to the corresponding .c source file for the detailed description.
*
* WARNING: Generally, functions don't allow NULL's to be passed
* as the genhash_t pointers, if not explicitly stated otherwise.
*/
typedef
struct
genhash_s
genhash_t
;
/*
* Create a new hash table
* keycmpf : function which returns 0 if keys are equal, else !0
* keyhashf : function which computes the hash value of a key
* keydestroyf : function for destroying keys, can be NULL for no destructor
* valuedestroyf: function for destroying values, can be NULL for no destructor
*/
genhash_t
*
genhash_new
(
int
(
*
keycmpf
)
(
const
void
*
key1
,
const
void
*
key2
),
unsigned
int
(
*
keyhashf
)
(
const
void
*
key
),
void
(
*
keydestroyf
)
(
void
*
key
),
void
(
*
valuedestroyf
)
(
void
*
value
));
/*
* Re-initialize genhash structure with new callback functions.
* (Rarely ever used).
*/
int
genhash_reinit
(
genhash_t
*
hash
,
int
(
*
keycmpf
)
(
const
void
*
key1
,
const
void
*
key2
),
unsigned
int
(
*
keyhashf
)
(
const
void
*
key
),
void
(
*
keydestroyf
)
(
void
*
key
),
void
(
*
valuedestroyf
)
(
void
*
value
));
/*
* Initialize the LRU-driven elements count limiting
* and/or set a new Least Recently Used list size limit.
* If a new entry is being added to the hash, the least recently used entry
* (one at the bottom of the LRU list) will be automatically deleted.
* The deletion may be skipped if the hash is very small
* (currently, "small" means no longer than 4 entries).
* This function is immune to NULL argument.
*
* RETURN VALUES:
* The previous LRU limit, or -1/EINVAL when h is NULL.
* EXAMPLE:
* genhash_set_lru_limit(h, 1500); // Maximum 1500 entries in the hash
*/
int
genhash_set_lru_limit
(
genhash_t
*
h
,
int
new_lru_limit
);
/*
* Set the system-wide (!!!) limit on maximum number of buckets.
* If the value is 0, the hash is allowed to store only 4 elements inline
* (buckets allocation is suppressed).
* If the value is 1, the hash turns out into a linked list.
* The default limit is about 1M buckets.
* RETURN VALUES:
* The previous buckets number limit.
*/
int
genhash_set_buckets_limit
(
int
new_max_number_of_buckets
);
/*
* destroys a hash, freeing each key and/or value.
* Keys are always destroyed before values using the destructors
* specified upon hash creation.
* This function is immune to NULL argument.
*/
void
genhash_destroy
(
genhash_t
*
h
);
/*
* Delete all elements from the hash, retaining the hash structure itself.
* Optionally, it may be told to invoke, or not invoke the corresponding
* key/value destructors.
* This function is immune to NULL argument.
*
* EXAMPLE:
* genhash_empty(h, 1, 1); // Remove all entries, invoking destructors
*/
void
genhash_empty
(
genhash_t
*
h
,
int
freekeys
,
int
freevalues
);
/*
* Add, returns 0 on success, -1 on failure (ENOMEM). Note, you CAN add
* records with duplicate keys. No guarantees about order preservations.
*
* EXAMPLE:
* char *key_str = strdup("key");
* char *val_str = strdup("arbitrary value");
* if(genhash_add(h, key_str, val_str) != 0) {
* free(key_str);
* free(val_str);
* perror("genhash_add failed");
* exit(EX_SOFTWARE);
* }
*/
int
genhash_add
(
genhash_t
*
h
,
void
*
key
,
void
*
value
);
/*
* Add, but only if a mapping is not there already.
* RETURN VALUES:
* 0: Element added successfully.
* -1/EINVAL: Invalid arguments (key == NULL).
* -1/EEXIST: Duplicate entry is found.
* -1/ENOMEM: Memory allocation failed
*/
int
genhash_addunique
(
genhash_t
*
h
,
void
*
key
,
void
*
value
);
/*
* Fetch - returns pointer to a value, NULL/ESRCH if not found
*/
void
*
genhash_get
(
genhash_t
*
h
,
const
void
*
key
);
/*
* Delete - returns 0 on success, -1/ESRCH if not found.
* Keys are always destroyed before values using the destructors
* specified upon hash creation.
*/
int
genhash_del
(
genhash_t
*
h
,
void
*
key
);
/*
* Return the number of elements in a hash.
* This function is immune to NULL argument.
*/
int
genhash_count
(
genhash_t
*
h
);
/*
* External iterator structure for using with iterator-based walking functions.
* This declaration is NOT INTENDED TO BE USED BY AN APPLICATION DIRECTLY
* The pointer to the already allocated structure must be passed to
* genhash_iter*() functions.
*/
typedef
struct
genhash_iter_s
{
genhash_t
*
hash_ptr
;
union
{
int
item_number
;
void
*
location
;
}
un
;
int
order_lru_first
;
struct
genhash_iter_s
*
iter_prev
;
struct
genhash_iter_s
*
iter_next
;
}
genhash_iter_t
;
/*
* Initialize the iterator for walking through the hash.
* The memory block to be used as iterator is provided by the (*iter) pointer.
* This memory must be allocated (possibly, on the stack) by the caller.
* OWNERSHIP:
* The initialized iterator must be disposed of by calling
* genhash_iter_done().
* ORDER:
* By default, the elements are iterated in the "most recent first" order,
* use reverse_order to change that. For very small number of entries
* (currently, 4) the order may be IGNORED.
* RETURN VALUES:
* number of entries the hash had at the moment.
*/
int
genhash_iter_init
(
genhash_iter_t
*
iter
,
genhash_t
*
hash_to_use
,
int
reverse_order
);
/*
* Returns the key and value of each element in optional (key) and (value),
* which must be passed as the pointers to pointers (hence these ***'s).
* OWNERSHIP:
* The key and value are pointers to the internally manageed locations.
* RETURN VALUES:
* 0 if no more elements will be returned, otherwise 1.
* EXAMPLE:
* key_type_t *key; // Pointer to key
* value_type_t *val; // Pointer to value
* genhash_iter_t iter; // Iterator structure
* genhash_iter_init(&iter, hash_ptr, 0); // Prepare iterator
* while(genhash_iter(&iter, &key, &val)) // Iterate over hash elements
* print_keyval(key, val); // Use key and value
* genhash_iter_done(&iter); // Done iterations.
*/
int
genhash_iter
(
genhash_iter_t
*
iter
,
void
*/
**
*/
key
,
void
*/
**
*/
val
);
/*
* Dispose of the iterator.
* After this operations, the iterator contents unusable
* and shall not be accesed. (genhash_iter_init() is OK).
*/
void
genhash_iter_done
(
genhash_iter_t
*
iter
);
/****************************************************************************/
/*
* The following hashing and comparison functions are provided for
* you, or you may supply your own.
*/
unsigned
int
hashf_int
(
const
void
*
key
);
/* Key is an int * */
int
cmpf_int
(
const
void
*
key1
,
const
void
*
key2
);
unsigned
int
hashf_void
(
const
void
*
key
);
int
cmpf_void
(
const
void
*
key1
,
const
void
*
key2
);
unsigned
int
hashf_string
(
const
void
*
key
);
int
cmpf_string
(
const
void
*
key1
,
const
void
*
key2
);
#endif
/* __GENHASH_H__ */
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment