Skip to content

Commit 644859c

Browse files
committed
adding typesafe tricks and some data structures.
1 parent 2caa7b6 commit 644859c

File tree

6 files changed

+374
-0
lines changed

6 files changed

+374
-0
lines changed

datastructures/hashtable.c

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#include <stdlib.h>
2+
#include "datastructures.h"
3+
4+
#define _XOPEN_SOURCE 500 /* Enable certain library functions (strdup) on linux. See feature_test_macros(7) */
5+
6+
#include <stdlib.h>
7+
#include <stdio.h>
8+
#include <limits.h>
9+
#include <string.h>
10+
11+
struct entry_s {
12+
void *key;
13+
size_t key_len;
14+
void *value;
15+
size_t value_len;
16+
struct entry_s *next;
17+
};
18+
19+
typedef struct entry_s entry_t;
20+
21+
struct hashtable_s {
22+
int size;
23+
struct entry_s **table;
24+
};
25+
26+
typedef struct hashtable_s hashtable_t;
27+
28+
29+
/* Create a new hashtable. */
30+
hashtable_t *ht_create( int size )
31+
{
32+
hashtable_t *hashtable = NULL;
33+
int i;
34+
size_t table_size = sizeof(hashtable_t) + (size * sizeof(entry_t *));
35+
36+
if( size < 1 ) return NULL;
37+
38+
/* Allocate the table and the entry pointers. */
39+
if((hashtable = calloc(1, table_size)) == NULL ) {
40+
return NULL;
41+
}
42+
43+
/* point to the entry table which is within the block of memory we just allocated. */
44+
hashtable->table = (void *)(hashtable + 1);
45+
46+
hashtable->size = size;
47+
48+
return hashtable;
49+
}
50+
51+
52+
void ht_destroy(hastable_t *hashtable)
53+
{
54+
if(!hashtable) {
55+
return;
56+
}
57+
58+
/* walk down each bin and free everything we find */
59+
for(size_t bin=0; bin<hashtable->size; bin++) {
60+
entry_t *curr = hashtable->table[bin];
61+
62+
while(curr) {
63+
entry_t *next = curr->next;
64+
free(curr);
65+
curr = next;
66+
}
67+
}
68+
69+
free(hashtable);
70+
}
71+
72+
73+
74+
/* Hash a string for a particular hash table. */
75+
static int ht_hash(hashtable_t *hashtable, void *key_arg, size_t key_len)
76+
{
77+
size_t hash, i;
78+
int8_t *key = key_arg;
79+
80+
// http://en.wikipedia.org/wiki/Jenkins_hash_function
81+
for ( hash = i = 0; i < key_len; ++i ) {
82+
hash += key[i], hash += ( hash << 10 ), hash ^= ( hash >> 6 );
83+
}
84+
hash += ( hash << 3 ), hash ^= ( hash >> 11 ), hash += ( hash << 15 );
85+
86+
return hash % hashtable->size;
87+
}
88+
89+
/* Create a key-value pair. */
90+
static entry_t *ht_newpair( void *key, size_t key_len, void *value, size_t value_len)
91+
{
92+
entry_t *newpair;
93+
size_t data_len = sizeof(entry_t) + key_len + value_len;
94+
95+
if( ( newpair = malloc( data_len ) ) == NULL ) {
96+
return NULL;
97+
}
98+
99+
/* stitch up pointers */
100+
newpair->key = (int8_t *)(newpair + 1);
101+
newpair->value = newpair->key + key_len;
102+
newpair->next = NULL;
103+
104+
newpair->key_len = key_len;
105+
newpair->value_len = value_len;
106+
107+
memcpy(newpair->key, key, key_len);
108+
memcpy(newpair->value, value, value_len);
109+
110+
return newpair;
111+
}
112+
113+
114+
/* Retrieve a value from a hash table. */
115+
void *ht_get(hashtable_t *hashtable, void *key, size_t key_len)
116+
{
117+
int bin = 0;
118+
entry_t *pair;
119+
120+
bin = ht_hash(hashtable, key, key_len);
121+
122+
/* Step through the bin, looking for our value. */
123+
pair = hashtable->table[bin];
124+
125+
while(pair != NULL && memcmp(key, key_len, pair->key, pair->key_len) > 0 ) {
126+
pair = pair->next;
127+
}
128+
129+
/* Did we actually find anything? */
130+
if( pair != NULL || memcmp(key, key_len, pair->key, pair->key_len) == 0 ) {
131+
return pair->value;
132+
} else {
133+
return NULL;
134+
}
135+
}
136+
137+
138+
void ht_remove(hashtable_t *hashtable, void *key, size_t key_len)
139+
{
140+
int bin = 0;
141+
entry_t *curr;
142+
entry_t *last = NULL;
143+
144+
bin = ht_hash(hashtable, key, key_len);
145+
146+
/* Step through the bin, looking for our value. */
147+
curr = hashtable->table[bin];
148+
149+
while(curr != NULL && memcmp(key, key_len, curr->key, curr->key_len) > 0 ) {
150+
last = curr;
151+
curr = curr->next;
152+
}
153+
154+
/* Did we actually find anything? */
155+
if(curr != NULL || memcmp(key, key_len, curr->key, curr->key_len) == 0 ) {
156+
if(!last) {
157+
/* head of the list */
158+
hashtable->table[bin] = curr->next;
159+
} else {
160+
last->next = curr->next;
161+
}
162+
163+
free(curr);
164+
}
165+
}
166+
167+
168+
/* Insert a key-value pair into a hash table. */
169+
int ht_set( hashtable_t *hashtable, void *key, size_t key_len, void *value, size_t value_len)
170+
{
171+
int bin = 0;
172+
entry_t *newpair = NULL;
173+
174+
newpair = ht_newpair(key, key_len, value, value_len);
175+
176+
if(!newpair) {
177+
return 0;
178+
}
179+
180+
/* make sure that there is no existing entry with the key */
181+
ht_remove(hashtable, key, key_len);
182+
183+
/* what bin is the entry going to be in? */
184+
bin = ht_hash(hashtable, key, key_len);
185+
186+
next = hashtable->table[bin];
187+
188+
/* find where the new entry should go */
189+
while(next != NULL && memcmp(newpair->key, newpair->key_len, next->key, next->key_len) > 0 ) {
190+
last = next;
191+
next = next->next;
192+
}
193+
194+
/* three cases, we are at the start of the list, at the end of the list, or somewhere in the middle */
195+
if(next == hashtable->table[bin]) {
196+
/* we're at the start of the linked list in this bin. */
197+
newpair->next = next;
198+
hashtable->table[bin] = newpair;
199+
} else if(next == NULL) {
200+
/* we're at the end of the linked list. */
201+
last->next = newpair;
202+
} else {
203+
/* we're in the middle of the list. */
204+
newpair->next = next;
205+
last->next = newpair;
206+
}
207+
208+
return 1;
209+
}
210+
211+
212+
213+
int main( int argc, char **argv ) {
214+
215+
hashtable_t *hashtable = ht_create( 65536 );
216+
217+
ht_set( hashtable, "key1", "inky" );
218+
ht_set( hashtable, "key2", "pinky" );
219+
ht_set( hashtable, "key3", "blinky" );
220+
ht_set( hashtable, "key4", "floyd" );
221+
222+
printf( "%s\n", ht_get( hashtable, "key1" ) );
223+
printf( "%s\n", ht_get( hashtable, "key2" ) );
224+
printf( "%s\n", ht_get( hashtable, "key3" ) );
225+
printf( "%s\n", ht_get( hashtable, "key4" ) );
226+
227+
return 0;
228+
}

datastructures/hashtable.h

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Generic datastructures
3+
*
4+
* hash_table
5+
* linked_list
6+
* vector
7+
*/
8+
9+
#ifndef __DATASTRUCTURES_H__
10+
#define __DATASTRUCTURES_H__
11+
12+
13+
14+
15+
#endif

datastructures/vector.h

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* generic vector type
3+
*
4+
* This implements a very simple vector type.
5+
*/
6+
7+
8+
#ifndef __VECTOR_H__
9+
#define __VECTOR_H__ 1
10+
11+
#ifndef CONCAT
12+
#define CONCAT(A,B) CONCAT_1(A,B)
13+
#define CONCAT_1(A,B) A##B
14+
#endif
15+
16+
#ifdef VECTOR
17+
#warning Vector is defined!
18+
#endif
19+
20+
#undef VECTOR_DECL
21+
#define VECTOR_DECL(T,ELEM_TYPE) typedef struct { \
22+
size_t capacity; \
23+
size_t inc; \
24+
size_t size; \
25+
ELEM_TYPE *elements; \
26+
} T; \
27+
T *CONCAT(T,_create)(size_t capacity, size_t inc); \
28+
void CONCAT(T,_destroy)(T *vec); \
29+
size_t CONCAT(T,_size)(T *vec); \
30+
size_t CONCAT(T,_capacity)(T *vec); \
31+
size_t CONCAT(T,_splice)(T *vec, size_t loc, ELEM_TYPE *data, size_t data_len); \
32+
size_t CONCAT(T,_cut)(T *vec, size_t loc, size_t len);
33+
34+
35+
36+
#define VECTOR_IMPL(T,ELEM_TYPE) \
37+
T *CONCAT(T,_create)(size_t capacity, size_t inc) \
38+
{ \
39+
if(capacity <= 0) { \
40+
return NULL; \
41+
} \
42+
T *new_vec = calloc(1,sizeof(T)); \
43+
if(!new_vec) { \
44+
return NULL; \
45+
} \
46+
new_vec->elements = calloc(1,sizeof(ELEM_TYPE)); \
47+
if(!new_vec->elements) { \
48+
free(new_vec); \
49+
return NULL; \
50+
} \
51+
new_vec->capacity = capacity; \
52+
new_vec->inc = inc; \
53+
new_vec->size = 0; \
54+
return new_vec; \
55+
}\
56+
size_t CONCAT(T,_size)(T *vec) \
57+
{ \
58+
if(vec) return vec->size; \
59+
else return 0; \
60+
} \
61+
size_t CONCAT(T,_capacity)(T *vec) \
62+
{ \
63+
if(vec) return vec->capacity; \
64+
else return 0; \
65+
} \
66+
size_t CONCAT(T,_splice)(T *vec, size_t loc, ELEM_TYPE *data, size_t data_len) \
67+
{ \
68+
size_t new_size = vec->size + data_len; \
69+
if(!vec) return 0; \
70+
if(new_size > capacity) { \
71+
size_t new_cap = ((new_size + vec->inc - 1) / vec->inc) * vec->inc; \
72+
ELEM_TYPE *new_elements = calloc(new_cap, sizeof(ELEM_TYPE)); \
73+
if(!new_elements) return 0; \
74+
vec->capacity = new_cap; \
75+
} \
76+
for(size_t elem = 0; elem < new_size; elem++) { \
77+
if(elem < loc) { new_elements[elem] = vec->elements[elem]; } \
78+
else if(elem < (loc+data_len) { new_elements[elem] = data[elem - loc]; } \
79+
else { new_elements[elem] = vec->elements[elem - data_len]; } \
80+
} \
81+
free(vec->elements); \
82+
vec->elements = new_elements; \
83+
return loc + data_len; \
84+
}
85+
86+
87+
88+
#endif
89+

datastructures/vector_test.c

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "vector.h"
2+
3+
VECTOR_DECL(int_vec, int);
4+
5+
CONCAT(foo,_bar)
6+
7+
VECTOR_IMPL(int_vec, int)
8+

typesafe/test_typesafe.c

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <stdio.h>
2+
#include "typesafe.h"
3+
4+
5+
6+
NEWTYPE(cent_t, unsigned);
7+
NEWTYPE(dollar_t, unsigned);
8+
9+
#define DOLLAR_2_CENT(dollar) (TO_NT(cent_t, 100*FROM_NT(dollar)))
10+
11+
cent_t calc(cent_t amount) {
12+
// expecting 'amount' to semantically represents cents...
13+
return TO_NT(cent_t, FROM_NT(amount)*2);
14+
}
15+
16+
int main(int argc, char* argv[]) {
17+
dollar_t amount = TO_NT(dollar_t, 50); // or alternatively {50};
18+
calc(DOLLAR_2_CENT(amount)); // ok
19+
calc(amount); // raise warning
20+
return 0;
21+
}

typesafe/typesafe.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* taken from Stack Overflow here:
2+
http://stackoverflow.com/questions/36351496/type-safety-in-c
3+
*/
4+
5+
#ifndef __TYPESAFE_H__
6+
#define __TYPESAFE_H__ 1
7+
8+
#define NEWTYPE(nty,oty) typedef struct { oty v; } nty
9+
#define FROM_NT(ntv) ((ntv).v)
10+
#define TO_NT(nty,val) ((nty){(val)}) /* or better ((nty){ .v=(val)}) if C99 */
11+
12+
#endif
13+

0 commit comments

Comments
 (0)