Initial commit

This commit is contained in:
Andrea Mazzoleni 2011-03-01 22:21:30 +01:00
parent db94ceaf5b
commit f4f308e32f
24 changed files with 4514 additions and 0 deletions

83
Makefile.am Normal file
View file

@ -0,0 +1,83 @@
bin_PROGRAMS = snapraid
check_PROGRAMS = testgen
snapraid_SOURCES = \
snapraid.c \
getopt.c \
md5.c \
tommy.c
noinst_HEADERS = \
portable.h \
md5.h \
tommyarray.h \
tommychain.h \
tommyhash.h \
tommyhashdyn.h \
tommylist.h \
tommytypes.h \
util.h \
tommyarray.c \
tommyhash.c \
tommyhashdyn.c \
tommylist.c
testgen_SOURCES = \
testgen.c
EXTRA_DIST = \
autogen.sh \
noautomake.sh \
README AUTHORS HISTORY INSTALL COPYING \
test.conf \
acinclude.m4
clean-local:
rm -f valgrind.log callgrind.log
rm -rf test
CHECK = ./snapraid -v -c test.conf sync
check-local:
rm -rf test
mkdir test
mkdir test/disk1
mkdir test/disk2
mkdir test/disk3
./testgen 1 3 2000
./snapraid -v -c $(srcdir)/test.conf sync
./snapraid -v -c $(srcdir)/test.conf check
./testgen 2 3 2000
./snapraid -v -c $(srcdir)/test.conf sync
./snapraid -v -c $(srcdir)/test.conf check
rm test/disk1/a*
./snapraid -v -c $(srcdir)/test.conf check
echo Success!
valgrind:
valgrind \
--tool=memcheck \
--track-origins=yes \
--read-var-info=yes \
--leak-check=full \
-v $(CHECK) \
2> valgrind.log
tail valgrind.log
callgrind:
valgrind \
--tool=callgrind \
--dump-instr=yes \
--trace-jump=yes \
-v $(CHECK) \
2> callgrind.log
tail callgrind.log
cachegrind:
valgrind \
--tool=cachegrind \
-v $(CHECK) \
2> cachegrind.log
tail cachegrind.log

5
README Normal file
View file

@ -0,0 +1,5 @@
SNAPRAID
========

20
acinclude.m4 Normal file
View file

@ -0,0 +1,20 @@
dnl @synopsis AC_CHECK_CC_OPT(flag, ifyes, ifno)
dnl
dnl Shows a message as like "checking wether gcc accepts flag ... no"
dnl and executess ifyes or ifno.
AC_DEFUN([AC_CHECK_CC_OPT],
[
AC_MSG_CHECKING([whether ${CC-cc} accepts $1])
echo 'void f(){}' > conftest.c
if test -z "`${CC-cc} -c $1 conftest.c 2>&1`"; then
AC_MSG_RESULT([yes])
$2
else
AC_MSG_RESULT([no])
$3
fi
rm -f conftest*
])

17
autogen.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/sh
#
echo "Generating build information using aclocal, automake and autoconf"
echo "This may take a while ..."
# Touch the timestamps on all the files since CVS messes them up
touch configure.ac
# Regenerate configuration files
aclocal
automake --add-missing --force-missing
autoconf
autoheader && touch config.h
# Run configure for this platform
echo "Now you are ready to run ./configure"

47
configure.ac Normal file
View file

@ -0,0 +1,47 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT([snapraid], [0.1])
AM_INIT_AUTOMAKE([foreign no-dependencies])
AC_CONFIG_SRCDIR([snapraid.c])
AC_CONFIG_HEADER([config.h])
AC_CANONICAL_HOST
AC_COPYRIGHT([Copyright 2011 Andrea Mazzoleni])
dnl Checks for programs.
AC_PROG_CC
dnl Checks for libraries.
AC_SYS_LARGEFILE
dnl Checks for header files.
AC_HEADER_STDC
AC_HEADER_DIRENT
AC_HEADER_TIME
AC_FUNC_UTIME_NULL
AC_CHECK_HEADERS([unistd.h getopt.h stdint.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_C_RESTRICT
AC_C_VOLATILE
AC_TYPE_SIZE_T
dnl Checks for library functions.
AC_CHECK_FUNCS([getopt getopt_long snprintf vsnprintf])
AC_CHECK_FUNCS([fsync fallocate])
dnl Configure for 64 bit files
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64"
dnl Enable GLIBC symbols
AC_DEFINE(_GNU_SOURCE,1,[Enable GLIBC symbols])
dnl Checks for architecture
AC_C_BIGENDIAN
dnl Checks for compiler
AC_CHECK_CC_OPT([-Wall], CFLAGS="$CFLAGS -Wall", [])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

113
getopt.c Normal file
View file

@ -0,0 +1,113 @@
/*
* This file is part of the Advance project.
*
* Copyright (C) 2001, 2002 Andrea Mazzoleni
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#if !HAVE_GETOPT
/* This source is extracted from the DJGPP LIBC library */
#define unconst(var, type) ((type)(var))
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <string.h>
#include <stdio.h>
int opterr = 1, optind = 1, optopt = 0;
char *optarg = 0;
#define BADCH (int)'?'
#define EMSG ""
int getopt(int nargc, char *const nargv[], const char *ostr)
{
static const char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
char *p;
if (!*place)
{
if (optind >= nargc || *(place = nargv[optind]) != '-')
{
place = EMSG;
return(EOF);
}
if (place[1] && *++place == '-')
{
++optind;
place = EMSG;
return(EOF);
}
}
if ((optopt = (int)*place++) == (int)':'
|| !(oli = strchr(ostr, optopt)))
{
/*
* if the user didn't specify '-' as an option,
* assume it means EOF.
*/
if (optopt == (int)'-')
return EOF;
if (!*place)
++optind;
if (opterr)
{
if (!(p = strrchr(*nargv, '/')))
p = *nargv;
else
++p;
fprintf(stderr, "%s: illegal option -- %c\n", p, optopt);
}
return BADCH;
}
if (*++oli != ':')
{ /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else
{ /* need an argument */
if (*place) /* no white space */
optarg = unconst(place, char *);
else if (nargc <= ++optind)
{ /* no arg */
place = EMSG;
if (!(p = strrchr(*nargv, '/')))
p = *nargv;
else
++p;
if (opterr)
fprintf(stderr, "%s: option requires an argument -- %c\n", p, optopt);
return BADCH;
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return optopt; /* dump back option letter */
}
#endif

246
md5.c Normal file
View file

@ -0,0 +1,246 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*
* Changed so as no longer to depend on Colin Plumb's `usual.h' header
* definitions; now uses stuff from dpkg's config.h.
* - Ian Jackson <ijackson@nyx.cs.du.edu>.
* Still in the public domain.
*
* Added convenience MD5(buffer,length,digest) function.
* - Gordon Mohr <gojomo@bitzi.com>
* Still in the public domain.
*/
#include "portable.h"
#include "md5.h"
#if WORDS_BIGENDIAN
void byteSwap(uint32_t *buf, unsigned words)
{
md5byte *p = (md5byte *)buf;
do {
*buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 |
((unsigned)p[1] << 8 | p[0]);
p += 4;
} while (--words);
}
#else
#define byteSwap(buf,words)
#endif
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f,w,x,y,z,in,s) \
(w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. md5_update blocks
* the data and converts bytes into longwords for this routine.
*/
static void
md5_transform(uint32_t buf[4], uint32_t const in[16])
{
uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void md5_init(struct md5_t *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bytes[0] = 0;
ctx->bytes[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void md5_update(struct md5_t* ctx, const unsigned char* buf, unsigned len)
{
uint32_t t;
/* Update byte count */
t = ctx->bytes[0];
if ((ctx->bytes[0] = t + len) < t)
ctx->bytes[1]++; /* Carry from low to high */
t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */
if (t > len) {
memcpy((uint8_t *)ctx->in + 64 - t, buf, len);
return;
}
/* First chunk is an odd size */
memcpy((uint8_t *)ctx->in + 64 - t, buf, t);
byteSwap(ctx->in, 16);
md5_transform(ctx->buf, ctx->in);
buf += t;
len -= t;
/* Process data in 64-byte chunks */
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteSwap(ctx->in, 16);
md5_transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void md5_final(struct md5_t* ctx, unsigned char* digest)
{
int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */
uint8_t *p = (uint8_t *)ctx->in + count;
/* Set the first char of padding to 0x80. There is always room. */
*p++ = 0x80;
/* Bytes of padding needed to make 56 bytes (-8..55) */
count = 56 - 1 - count;
if (count < 0) { /* Padding forces an extra block */
memset(p, 0, count + 8);
byteSwap(ctx->in, 16);
md5_transform(ctx->buf, ctx->in);
p = (uint8_t *)ctx->in;
count = 56;
}
memset(p, 0, count);
byteSwap(ctx->in, 14);
/* Append length in bits and transform */
ctx->in[14] = ctx->bytes[0] << 3;
ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
md5_transform(ctx->buf, ctx->in);
byteSwap(ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
}
/*
* Convenience function for when what you need to MD5 is
* in a single contiguous buffer.
*/
void MD5(uint8_t *buffer, unsigned int length, uint8_t *digest)
{
struct md5_t md5ctx;
md5_init(&md5ctx);
md5_update(&md5ctx,buffer,length);
md5_final(&md5ctx,digest);
}

46
md5.h Normal file
View file

@ -0,0 +1,46 @@
/*
* This is the header file for the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*
* Changed so as no longer to depend on Colin Plumb's `usual.h'
* header definitions; now uses stuff from dpkg's config.h
* - Ian Jackson <ijackson@nyx.cs.du.edu>.
* Still in the public domain.
*/
#ifndef MD5_H
#define MD5_H
#ifdef __cplusplus
extern "C" {
#endif
#define MD5_SIZE 16
struct md5_t {
uint32_t buf[4];
uint32_t bytes[2];
uint32_t in[16];
};
void md5_init(struct md5_t* context);
void md5_update(struct md5_t* context, const unsigned char* buf, unsigned len);
void md5_final(struct md5_t* context, unsigned char* digest);
#ifdef __cplusplus
}
#endif
#endif /* !MD5_H */

114
portable.h Normal file
View file

@ -0,0 +1,114 @@
/*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __PORTABLE_H
#define __PORTABLE_H
#if HAVE_CONFIG_H
#include "config.h" /* Use " to include first in the same directory of this file */
#endif
/***************************************************************************/
/* Config */
#ifdef __WIN32__
/* Customize for Windows Mingw/Cygwin */
#define HAVE_SYS_TYPES_H 1
#define HAVE_SYS_STAT_H 1
#define HAVE_DIRENT_H 1
#define TIME_WITH_SYS_TIME 1
#define HAVE_SYS_TIME_H 1
#define HAVE_UNISTD_H 1
#endif
/* Include some standard headers */
#include <stdio.h>
#include <stdlib.h> /* On many systems (e.g., Darwin), `stdio.h' is a prerequisite. */
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#if HAVE_STDINT_H
#include <stdint.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#if HAVE_DIRENT_H
#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#else
#define dirent direct
#define NAMLEN(dirent) (dirent)->d_namlen
#if HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#if HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#if HAVE_NDIR_H
#include <ndir.h>
#endif
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_GETOPT_H
#include <getopt.h>
#endif
#if !HAVE_GETOPT
int getopt(int argc, char * const *argv, const char *options);
extern char *optarg;
extern int optind, opterr, optopt;
#endif
#if HAVE_GETOPT_LONG
#define SWITCH_GETOPT_LONG(a,b) a
#else
#define SWITCH_GETOPT_LONG(a,b) b
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
#endif

1681
snapraid.c Normal file

File diff suppressed because it is too large Load diff

7
test.conf Normal file
View file

@ -0,0 +1,7 @@
block_size 1
parity test/parity
content test/content
disk d1 test/disk1/
disk d2 test/disk2/
disk d3 test/disk3/

76
testgen.c Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "portable.h"
void gen(int disk, int size)
{
char path[PATH_MAX];
char name[PATH_MAX];
FILE* f;
int i;
int l;
l = 1 + rand() % 32;
for(i=0;i<l;++i) {
name[i] = 'a' + (rand() % 22);
}
name[i] = 0;
snprintf(path, sizeof(path), "test/disk%d/%s", disk, name);
f = fopen(path, "w");
if (!f) {
fprintf(stderr, "Error writing %s\n", path);
exit(EXIT_FAILURE);
}
while (size) {
fputc(size, f);
--size;
}
fclose(f);
}
int main(int argc, char* argv[])
{
int i, j;
int s = 1;
int d = 3;
int n = 100;
if (argc != 4) {
fprintf(stderr, "Syntax: testgen SEED DISK FILE\n");
exit(EXIT_FAILURE);
}
s = atoi(argv[1]);
d = atoi(argv[2]);
n = atoi(argv[3]);
srand(s);
for(i=0;i<d;++i) {
for(j=0;j<n;++j) {
gen(i+1, j);
}
}
return 0;
}

31
tommy.c Normal file
View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "portable.h"
/* redefine a safe malloc for tommy use */
#define tommy_malloc malloc_nofail
#define tommy_free free
void* malloc_nofail(size_t size);
/* direct include tommy source */
#include "tommylist.c"
#include "tommyhash.c"
#include "tommyhashdyn.c"
#include "tommyarray.c"

72
tommyarray.c Normal file
View file

@ -0,0 +1,72 @@
/*
* Copyright 2011 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
#include "tommyarray.h"
#include <string.h> /* for memset */
/******************************************************************************/
/* array */
void tommy_array_init(tommy_array* array)
{
/* fixed initial size */
array->bucket_bit = TOMMY_ARRAY_BIT;
array->bucket_max = 1 << array->bucket_bit;
array->bucket[0] = tommy_cast(void**, tommy_malloc(array->bucket_max * sizeof(void*)));
memset(array->bucket[0], 0, array->bucket_max * sizeof(void*));
array->bucket_mac = 1;
array->size = 0;
}
void tommy_array_done(tommy_array* array)
{
unsigned i;
for(i=0;i<array->bucket_mac;++i)
tommy_free(array->bucket[i]);
}
void tommy_array_grow(tommy_array* array, unsigned size)
{
while (size > array->bucket_max) {
/* grow the hash size and allocate */
array->bucket[array->bucket_mac] = tommy_cast(void**, tommy_malloc(array->bucket_max * sizeof(void*)));
memset(array->bucket[array->bucket_mac], 0, array->bucket_max * sizeof(void*));
++array->bucket_mac;
++array->bucket_bit;
array->bucket_max = 1 << array->bucket_bit;
}
if (array->size < size)
array->size = size;
}
tommy_size_t tommy_array_memory_usage(tommy_array* array)
{
return array->bucket_max * (tommy_size_t)sizeof(array->bucket[0][0]);
}

158
tommyarray.h Normal file
View file

@ -0,0 +1,158 @@
/*
* Copyright 2011 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
/** \file
* Dynamic array.
*
* This array is able to grow dynamically upon request.
*
* The resize involve an allocation of a new array segment, without reallocating
* the already allocated memory, and then not increasing the heap fragmentation.
*
* This means that the address of the allocated segments never change.
*/
#ifndef __TOMMYARRAY_H
#define __TOMMYARRAY_H
#include "tommytypes.h"
#include <assert.h> /* for assert */
/******************************************************************************/
/* array */
/**
* Initial and minimal size of the array expressed as a power of 2.
* The initial size is 2^TOMMY_ARRAY_BIT.
*/
#define TOMMY_ARRAY_BIT 6
/** \internal
* Max number of elements as a power of 2.
*/
#define TOMMY_ARRAY_BIT_MAX 32
/**
* Array.
*/
typedef struct tommy_array_struct {
void** bucket[TOMMY_ARRAY_BIT_MAX]; /**< Dynamic array of buckets. */
unsigned bucket_bit; /**< Bits used in the bit mask. */
unsigned bucket_max; /**< Number of buckets. */
unsigned bucket_mac; /**< Number of vectors allocated. */
unsigned size; /**< Currently allocated and initialized size. */
} tommy_array;
/**
* Initializes the array.
*/
void tommy_array_init(tommy_array* array);
/**
* Deinitializes the array.
*/
void tommy_array_done(tommy_array* array);
/**
* Grow the size up to the specified value.
* All the new elements in the array are initialized with the 0 value.
*/
void tommy_array_grow(tommy_array* array, unsigned size);
/**
* Gets a reference of the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_array_grow().
*/
tommy_inline void** tommy_array_ref(tommy_array* array, unsigned pos)
{
unsigned bsr;
assert(pos < array->size);
/* special case for the first bucket */
if (pos < (1 << TOMMY_ARRAY_BIT)) {
return &array->bucket[0][pos];
}
/* get the highest bit set */
bsr = tommy_ilog2_u32(pos);
/* clear the highest bit */
pos -= 1 << bsr;
return &array->bucket[bsr - TOMMY_ARRAY_BIT + 1][pos];
}
/**
* Sets the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_array_grow().
*/
tommy_inline void tommy_array_set(tommy_array* array, unsigned pos, void* node)
{
*tommy_array_ref(array, pos) = node;
}
/**
* Gets the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_array_grow().
*/
tommy_inline void* tommy_array_get(tommy_array* array, unsigned pos)
{
return *tommy_array_ref(array, pos);
}
/**
* Grows and inserts a new element at the end of the array.
*/
tommy_inline void tommy_array_insert(tommy_array* array, void* node)
{
unsigned pos = array->size;
tommy_array_grow(array, pos + 1);
tommy_array_set(array, pos, node);
}
/**
* Gets the initialized size of the array.
*/
tommy_inline unsigned tommy_array_size(tommy_array* array)
{
return array->size;
}
/**
* Gets the size of allocated memory.
*/
tommy_size_t tommy_array_memory_usage(tommy_array* array);
#endif

224
tommychain.h Normal file
View file

@ -0,0 +1,224 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
/** \file
* Chain of nodes.
* A chain of nodes is an abstraction used to implements complex list operations
* like sorting.
*
* Do not use this directly. Use lists instead.
*/
#ifndef __TOMMYCHAIN_H
#define __TOMMYCHAIN_H
#include "tommytypes.h"
/******************************************************************************/
/* chain */
/**
* Chain of nodes.
* A chain of nodes is a sequence of nodes with the following properties:
* - It contains at least one node. A chains of zero nodes cannot exist.
* - The next field of the tail is of *undefined* value.
* - The prev field of the head is of *undefined* value.
* - All the other inner prev and next fields are correctly set.
*/
typedef struct tommy_chain_struct {
tommy_node* head; /**< Pointer at the head of the chain. */
tommy_node* tail; /**< Pointer at the tail of the chain. */
} tommy_chain;
/**
* Splices a chain in the middle of another chain.
*/
tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail)
{
/* set the prev list */
first_after->prev = second_tail;
second_head->prev = first_before;
/* set the next list */
first_before->next = second_head;
second_tail->next = first_after;
}
/**
* Concats two chains.
*/
tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head)
{
/* set the prev list */
second_head->prev = first_tail;
/* set the next list */
first_tail->next = second_head;
}
/**
* Merges two chains.
*/
tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
{
tommy_node* first_i = first->head;
tommy_node* second_i = second->head;
/* merge */
while (1) {
if (cmp(first_i->data, second_i->data) > 0) {
tommy_node* next = second_i->next;
if (first_i == first->head) {
tommy_chain_concat(second_i, first_i);
first->head = second_i;
} else {
tommy_chain_splice(first_i->prev, first_i, second_i, second_i);
}
if (second_i == second->tail)
break;
second_i = next;
} else {
if (first_i == first->tail) {
tommy_chain_concat(first_i, second_i);
first->tail = second->tail;
break;
}
first_i = first_i->next;
}
}
}
/**
* Merges two chains managing special degenerated cases.
* It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains.
*/
tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
{
/* identify the condition first <= second */
if (cmp(first->tail->data, second->head->data) <= 0) {
tommy_chain_concat(first->tail, second->head);
first->tail = second->tail;
return;
}
/* identify the condition second < first */
/* here we must be strict on comparison to keep the sort stable */
if (cmp(second->tail->data, first->head->data) < 0) {
tommy_chain_concat(second->tail, first->head);
first->head = second->head;
return;
}
tommy_chain_merge(first, second, cmp);
}
/**
* Max number of elements as a power of 2.
*/
#define TOMMY_CHAIN_BIT_MAX 32
/**
* Sorts a chain.
* It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity,
* similar at the one used in the SGI STL libraries and in the Linux Kernel,
* but faster on degenerated cases like already ordered lists.
*
* SGI STL stl_list.h
* http://www.sgi.com/tech/stl/stl_list.h
*
* Linux Kernel lib/list_sort.c
* http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c
*/
tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp)
{
/*
* Bit buckets of chains.
* Each bucket contains 2^i nodes or it's empty.
*/
tommy_chain bit[TOMMY_CHAIN_BIT_MAX];
/**
* Value stored inside the bit bucket.
* It's used to know which bucket is empty of full.
*/
unsigned counter;
tommy_node* node = chain->head;
tommy_node* tail = chain->tail;
unsigned mask;
unsigned i;
counter = 0;
while (1) {
tommy_node* next;
tommy_chain carry;
tommy_chain* last;
/* carry bit to add */
last = &carry;
carry.head = node;
carry.tail = node;
next = node->next;
/* add the bit, propagating the carry */
i = 0;
mask = counter;
while ((mask & 1) != 0) {
tommy_chain_merge_degenerated(&bit[i], last, cmp);
mask >>= 1;
last = &bit[i];
++i;
}
/* copy the carry in the first empty bit */
bit[i] = *last;
/* add the carry in the counter */
++counter;
if (node == tail)
break;
node = next;
}
/* merge the buckets */
i = tommy_ctz_u32(counter);
mask = counter >> i;
while (mask != 1) {
mask >>= 1;
if (mask & 1) {
tommy_chain_merge_degenerated(&bit[i+1], &bit[i], cmp);
} else {
bit[i+1] = bit[i];
}
++i;
}
*chain = bit[i];
}
#endif

167
tommyhash.c Normal file
View file

@ -0,0 +1,167 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
#include "tommyhash.h"
/******************************************************************************/
/* hash */
tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr)
{
/* allow unaligned read on Intel x86 and x86_64 platforms */
#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64)
/* defines from http://predef.sourceforge.net/ */
return *(tommy_uint32_t*)ptr;
#else
const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr);
return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24);
#endif
}
#define tommy_rot(x,k) \
(((x)<<(k)) | ((x)>>(32-(k))))
#define tommy_mix(a,b,c) \
do { \
a -= c; a ^= tommy_rot(c, 4); c += b; \
b -= a; b ^= tommy_rot(a, 6); a += c; \
c -= b; c ^= tommy_rot(b, 8); b += a; \
a -= c; a ^= tommy_rot(c,16); c += b; \
b -= a; b ^= tommy_rot(a,19); a += c; \
c -= b; c ^= tommy_rot(b, 4); b += a; \
} while (0)
#define tommy_final(a,b,c) \
do { \
c ^= b; c -= tommy_rot(b,14); \
a ^= c; a -= tommy_rot(c,11); \
b ^= a; b -= tommy_rot(a,25); \
c ^= b; c -= tommy_rot(b,16); \
a ^= c; a -= tommy_rot(c,4); \
b ^= a; b -= tommy_rot(a,14); \
c ^= b; c -= tommy_rot(b,24); \
} while (0)
tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len)
{
const unsigned char* key = tommy_cast(const unsigned char*,void_key);
tommy_uint32_t a, b, c;
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val;
while (key_len > 12) {
a += tommy_le_uint32_read(key + 0);
b += tommy_le_uint32_read(key + 4);
c += tommy_le_uint32_read(key + 8);
tommy_mix(a,b,c);
key_len -= 12;
key += 12;
}
switch (key_len) {
case 0 :
return c; /* used only when called with a zero length */
case 12 :
c += tommy_le_uint32_read(key + 8);
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 11 : c += ((tommy_uint32_t)key[10]) << 16;
case 10 : c += ((tommy_uint32_t)key[9]) << 8;
case 9 : c += key[8];
case 8 :
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 7 : b += ((tommy_uint32_t)key[6]) << 16;
case 6 : b += ((tommy_uint32_t)key[5]) << 8;
case 5 : b += key[4];
case 4 :
a += tommy_le_uint32_read(key + 0);
break;
case 3 : a += ((tommy_uint32_t)key[2]) << 16;
case 2 : a += ((tommy_uint32_t)key[1]) << 8;
case 1 : a += key[0];
}
tommy_final(a,b,c);
return c;
}
tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len)
{
const unsigned char* key = tommy_cast(const unsigned char*,void_key);
tommy_uint32_t a, b, c;
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff);
c += init_val >> 32;
while (key_len > 12) {
a += tommy_le_uint32_read(key + 0);
b += tommy_le_uint32_read(key + 4);
c += tommy_le_uint32_read(key + 8);
tommy_mix(a,b,c);
key_len -= 12;
key += 12;
}
switch (key_len) {
case 0 :
return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */
case 12 :
c += tommy_le_uint32_read(key + 8);
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 11 : c += ((tommy_uint32_t)key[10]) << 16;
case 10 : c += ((tommy_uint32_t)key[9]) << 8;
case 9 : c += key[8];
case 8 :
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 7 : b += ((tommy_uint32_t)key[6]) << 16;
case 6 : b += ((tommy_uint32_t)key[5]) << 8;
case 5 : b += key[4];
case 4 :
a += tommy_le_uint32_read(key + 0);
break;
case 3 : a += ((tommy_uint32_t)key[2]) << 16;
case 2 : a += ((tommy_uint32_t)key[1]) << 8;
case 1 : a += key[0];
}
tommy_final(a,b,c);
return c + ((tommy_uint64_t)b << 32);
}

104
tommyhash.h Normal file
View file

@ -0,0 +1,104 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
/** \file
* Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin.
*/
#ifndef __TOMMYHASH_H
#define __TOMMYHASH_H
#include "tommytypes.h"
/******************************************************************************/
/* hash */
/**
* Hash type used in hashtables.
*/
typedef tommy_key_t tommy_hash_t;
/**
* Hash function.
* Robert Jenkins "lookup3" hash,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle().
* \note
* This function is platform independent as it read always in little endian format.
* \param init_val Initial value. You can use this to concatenate hash. Otherwise set it at 0.
* \param void_key Key to hash.
* \param key_len Length of the key.
* \return The 32 bit hash value.
*/
tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len);
/**
* Hash function for 64 bits.
* Robert Jenkins "lookup3" hash,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2().
* \note
* This function is platform independent as it read always in little endian format.
*/
tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len);
/**
* Integer hash of 32 bits.
* Robert Jenkins "4-byte Integer Hashing",
* from http://burtleburtle.net/bob/hash/integer.html
*/
tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key)
{
key -= key << 6;
key ^= key >> 17;
key -= key << 9;
key ^= key << 4;
key -= key << 3;
key ^= key << 10;
key ^= key >> 15;
return key;
}
/**
* Integer hash of 64 bits.
* Thomas Wang "Integer Hash Function",
* from http://www.cris.com/~Ttwang/tech/inthash.htm
*/
tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key)
{
key = ~key + (key << 21);
key = key ^ (key >> 24);
key = key + (key << 3) + (key << 8);
key = key ^ (key >> 14);
key = key + (key << 2) + (key << 4);
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
#endif

174
tommyhashdyn.c Normal file
View file

@ -0,0 +1,174 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
#include "tommyhashdyn.h"
#include "tommylist.h"
#include <string.h> /* for memset */
/******************************************************************************/
/* hashdyn */
void tommy_hashdyn_init(tommy_hashdyn* hashdyn)
{
/* fixed initial size */
hashdyn->bucket_bit = TOMMY_HASHDYN_BIT;
hashdyn->bucket_max = 1 << hashdyn->bucket_bit;
hashdyn->bucket_mask = hashdyn->bucket_max - 1;
hashdyn->bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(hashdyn->bucket_max * sizeof(tommy_hashdyn_node*)));
memset(hashdyn->bucket, 0, hashdyn->bucket_max * sizeof(tommy_hashdyn_node*));
hashdyn->count = 0;
}
void tommy_hashdyn_done(tommy_hashdyn* hashdyn)
{
tommy_free(hashdyn->bucket);
}
/**
* Resize the bucket vector.
*/
static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, unsigned new_bucket_bit)
{
unsigned bucket_bit;
unsigned bucket_max;
unsigned new_bucket_max;
unsigned new_bucket_mask;
tommy_hashdyn_node** new_bucket;
bucket_bit = hashdyn->bucket_bit;
bucket_max = hashdyn->bucket_max;
new_bucket_max = 1 << new_bucket_bit;
new_bucket_mask = new_bucket_max - 1;
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
/* reinsert all the elements */
if (new_bucket_bit > hashdyn->bucket_bit) {
unsigned i;
/* grow */
for(i=0;i<bucket_max;++i) {
tommy_hashdyn_node* j;
/* setup the new two buckets */
new_bucket[i] = 0;
new_bucket[i + bucket_max] = 0;
/* reinsert the bucket */
j = hashdyn->bucket[i];
while (j) {
tommy_hashdyn_node* j_next = j->next;
unsigned index = j->key & new_bucket_mask;
if (new_bucket[index])
tommy_list_insert_tail_not_empty(new_bucket[index], j);
else
tommy_list_insert_first(&new_bucket[index], j);
j = j_next;
}
}
} else {
unsigned i;
/* shrink */
for(i=0;i<new_bucket_max;++i) {
/* setup the new bucket with the lower bucket*/
new_bucket[i] = hashdyn->bucket[i];
/* concat the upper bucket */
tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]);
}
}
tommy_free(hashdyn->bucket);
/* setup */
hashdyn->bucket_bit = new_bucket_bit;
hashdyn->bucket_max = new_bucket_max;
hashdyn->bucket_mask = new_bucket_mask;
hashdyn->bucket = new_bucket;
}
void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash)
{
unsigned pos = hash & hashdyn->bucket_mask;
tommy_list_insert_tail(&hashdyn->bucket[pos], node, data);
node->key = hash;
++hashdyn->count;
/* grow if more than 50% full */
if (hashdyn->count >= hashdyn->bucket_max / 2) {
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1);
}
}
void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node)
{
unsigned pos = node->key & hashdyn->bucket_mask;
tommy_list_remove_existing(&hashdyn->bucket[pos], node);
--hashdyn->count;
/* shrink if less than 12.5% full */
if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT) {
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1);
}
return node->data;
}
void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_compare_func* cmp, const void* cmp_arg, tommy_hash_t hash)
{
unsigned pos = hash % hashdyn->bucket_max;
tommy_hashdyn_node* i = hashdyn->bucket[pos];
while (i) {
/* we first check if the hash matches, as in the same bucket we may have multiples hash values */
if (i->key == hash && cmp(cmp_arg, i->data) == 0) {
tommy_list_remove_existing(&hashdyn->bucket[pos], i);
--hashdyn->count;
return i->data;
}
i = i->next;
}
return 0;
}
tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn)
{
return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0])
+ tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node);
}

250
tommyhashdyn.h Normal file
View file

@ -0,0 +1,250 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
/** \file
* Dynamic chained hashtable.
*
* This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles
* the size then it reaches a load factor greater than 0.5 and it halves the size with a load
* factor lower than 0.125.
*
* All the elements are reallocated in a single resize operation done inside
* tommy_hashdyn_insert() or tommy_hashdyn_remove().
*
* Note that the resize operation takes approximatively 100 [ms] with 1 million of elements,
* and 1 [second] with 10 millions. This could be a problem in real-time applications.
*
* The resize also fragment the heap, as it involves allocating a double-sized table, copy elements,
* and deallocating the older table. Leaving a big hole in the heap.
*
* The ::tommy_hashlin hashtable fixes both problems.
*
* To initialize the hashtable you have to call tommy_hashdyn_init().
*
* \code
* tommy_hashslin hashdyn;
*
* tommy_hashdyn_init(&hashdyn);
* \endcode
*
* To insert elements in the hashtable you have to call tommy_hashdyn_insert() for
* each element.
* In the insertion call you have to specify the address of the node, the
* address of the object, and the hash value of the key to use.
* The address of the object is used to initialize the tommy_node::data field
* of the node, and the hash to initialize the tommy_node::key field.
*
* \code
* struct object {
* tommy_node node;
* // other fields
* int value;
* };
*
* struct object* obj = malloc(sizeof(struct object)); // creates the object
*
* obj->value = ...; // initializes the object
*
* tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object
* \endcode
*
* To find and element in the hashtable you have to call tommy_hashtable_search()
* providing a comparison function, its argument, and the hash of the key to search.
*
* \code
* int compare(const void* arg, const void* obj)
* {
* return (*(const unsigned*)arg != ((const struct object*)obj)->value;
* }
*
* struct object* obj = tommy_hashdyn_search(&hashdyn, compare, &value_to_find, tommy_inthash_u32(value_to_find));
* if (!obj) {
* // not found
* } else {
* // found
* }
* \endcode
*
* To iterate over all the elements in the hashtable with the same key, you have to
* use tommy_hashdyn_bucket() and follow the tommy_node::next pointer until NULL.
* You have also to check explicitely for the key, as the bucket may contains
* different keys.
*
* \code
* tommy_node* i = tommy_hashdyn_bucket(&hashdyn, tommy_inthash_u32(value_to_find));
* while (i) {
* struct object* obj = i->data; // gets the object pointer
*
* if (obj->value == value_to_find) {
* printf("%d\n", obj->value); // process the object
* }
*
* i = i->next; // goes to the next element
* }
* \endcode
*
* To remove an element from the hashtable you have to call tommy_hashdyn_remove()
* providing a comparison function, its argument, and the hash of the key to search
* and remove.
*
* \code
* struct object* obj = tommy_trie_remove(&hashtable, compare, &value_to_remove, tommy_inthash_u32(value_to_remove));
* if (obj) {
* free(obj); // frees the object allocated memory
* }
* \endcode
*
* To destroy the hashtable you have to remove all the elements, and deinitialize
* the hashtable calling tommy_hashdyn_done().
*
* \code
* tommy_hashdyn_done(&hashdyn);
* \endcode
*
* Note that you cannot iterates over all the elements in the hashtable using the
* hashtable itself. You have to insert all the elements also in a ::tommy_list,
* and use the list to iterate. See the \ref multiindex example for more detail.
*/
#ifndef __TOMMYHASHDYN_H
#define __TOMMYHASHDYN_H
#include "tommyhash.h"
/******************************************************************************/
/* hashdyn */
/** \internal
* Initial and minimal size of the hashtable expressed as a power of 2.
* The initial size is 2^TOMMY_HASHDYN_BIT.
*/
#define TOMMY_HASHDYN_BIT 4
/**
* Dynamic hashtable node.
* This is the node that you have to include inside your objects.
*/
typedef tommy_node tommy_hashdyn_node;
/**
* Dynamic chained hashtable.
*/
typedef struct tommy_hashdyn_struct {
tommy_hashdyn_node** bucket; /**< Hash buckets. One list for each hash modulus. */
unsigned bucket_bit; /**< Bits used in the bit mask. */
unsigned bucket_max; /**< Number of buckets. */
unsigned bucket_mask; /**< Bit mask to access the buckets. */
unsigned count; /**< Number of elements. */
} tommy_hashdyn;
/**
* Initializes the hashtable.
*/
void tommy_hashdyn_init(tommy_hashdyn* hashdyn);
/**
* Deinitializes the hashtable.
*/
void tommy_hashdyn_done(tommy_hashdyn* hashdyn);
/**
* Inserts an element in the hashtable.
*/
void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash);
/**
* Searches and removes an element from the hashtable.
* You have to provide a compare function and the hash of the element you want to remove.
* If the element is not found, 0 is returned.
* If more equal elements are present, the first one is removed.
* This operation is faster than calling tommy_hashdyn_bucket() and tommy_hashdyn_remove_existing() separately.
* \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one.
* The function should return 0 for equal elements, anything other for different elements.
* \param cmp_arg Compare argument passed as first argument of the compare function.
* \param hash Hash of the element to find and remove.
* \return The removed element, or 0 if not found.
*/
void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_compare_func* cmp, const void* cmp_arg, tommy_hash_t hash);
/**
* Gets the bucket of the specified hash.
* The bucket is guaranteed to contain ALL the elements with the specified hash,
* but it can contain also others.
* You can access elements in the bucket following the ::next pointer until 0.
* \param hash Hash of the element to find.
* \return The head of the bucket, or 0 if empty.
*/
tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, tommy_hash_t hash)
{
return hashdyn->bucket[hash & hashdyn->bucket_mask];
}
/**
* Searches an element in the hashtable.
* You have to provide a compare function and the hash of the element you want to find.
* If more equal elements are present, the first one is returned.
* \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one.
* The function should return 0 for equal elements, anything other for different elements.
* \param cmp_arg Compare argument passed as first argument of the compare function.
* \param hash Hash of the element to find.
* \return The first element found, or 0 if none.
*/
tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_compare_func* cmp, const void* cmp_arg, tommy_hash_t hash)
{
tommy_hashdyn_node* i = tommy_hashdyn_bucket(hashdyn, hash);
while (i) {
/* we first check if the hash matches, as in the same bucket we may have multiples hash values */
if (i->key == hash && cmp(cmp_arg, i->data) == 0)
return i->data;
i = i->next;
}
return 0;
}
/**
* Removes an element from the hashtable.
* You must already have the address of the element to remove.
* \return The tommy_node::data field of the node removed.
*/
void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node);
/**
* Gets the number of elements.
*/
tommy_inline unsigned tommy_hashdyn_count(tommy_hashdyn* hashdyn)
{
return hashdyn->count;
}
/**
* Gets the size of allocated memory.
* It includes the size of the ::tommy_trie_node of the stored elements.
*/
tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn);
#endif

87
tommylist.c Normal file
View file

@ -0,0 +1,87 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
#include "tommylist.h"
#include "tommychain.h"
void tommy_list_concat(tommy_list* first, tommy_list* second)
{
tommy_node* first_head;
tommy_node* first_tail;
tommy_node* second_head;
if (tommy_list_empty(second)) {
return;
}
if (tommy_list_empty(first)) {
*first = *second;
return;
}
first_head = tommy_list_head(first);
second_head = tommy_list_head(second);
first_tail = tommy_list_tail(first);
/* set the "circular" prev list */
first_head->prev = second_head->prev;
second_head->prev = first_tail;
/* set the "0 terminated" next list */
first_tail->next = second_head;
}
/** \internal
* Setup a list.
*/
tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail)
{
head->prev = tail;
tail->next = 0;
*list = head;
}
void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp)
{
tommy_chain chain;
tommy_node* head;
if (tommy_list_empty(list))
return;
head = tommy_list_head(list);
/* create a chain from the list */
chain.head = head;
chain.tail = head->prev;
tommy_chain_mergesort(&chain, cmp);
/* restore the list */
tommy_list_set(list, chain.head, chain.tail);
}

309
tommylist.h Normal file
View file

@ -0,0 +1,309 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
/** \file
* Double linked list for collisions into hashtables.
*
* This list is a double linked list mainly targetted for collisions into an hashtables,
* but useable also as a generic list.
*
* The main feature of this list is to require only one pointer to represent the
* list, compared to a classic implementation requiring an head an a tail pointers.
* This reduces the memory usage in hashtables.
*
* Another feature is to support the insertion at the end of the list. This allow to store
* collisions in a stable order. Where for stable order we mean that equal elements keep
* their insertion order.
*
* To initialize the list, you have to call tommy_list_init(), or simply assign
* to it NULL, as an empty list is represented by the NULL value.
*
* \code
* tommy_list list;
*
* tommy_list_init(&list); // initializes the list
* \endcode
*
* To insert elements in the list you have to call tommy_list_insert_tail()
* or tommy_list_insert_head() for each element.
* In the insertion call you have to specify the address of the node and the
* address of the object.
* The address of the object is used to initialize the tommy_node::data field
* of the node.
*
* \code
* struct object {
* tommy_node node;
* // other fields
* int value;
* };
*
* struct object* obj = malloc(sizeof(struct object)); // creates the object
*
* obj->value = ...; // initializes the object
*
* tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object
* \endcode
*
* To iterates over all the elements in the list you have to call
* tommy_list_head() to get the head of the list and follow the
* tommy_node::next pointer until NULL.
*
* \code
* tommy_node* i = tommy_list_head(&list);
* while (i) {
* struct object* obj = i->data; // gets the object pointer
*
* printf("%d\n", obj->value); // process the object
*
* i = i->next; // go to the next element
* }
* \endcode
*
* To destroy the list you have only to remove all the elements, as the list is
* completely inplace and it doesn't allocate memory.
*
* \code
* tommy_node* i = tommy_list_head(&list);
* while (i) {
* tommy_node* i_next = i->next; // saves the next element before freeing
*
* free(i->data); // frees the object allocated memory
*
* i = i_next; // goes to the next element
* }
* \endcode
*/
#ifndef __TOMMYLIST_H
#define __TOMMYLIST_H
#include "tommytypes.h"
/******************************************************************************/
/* list */
/**
* Double linked list for collisions into hashtables.
*/
typedef tommy_node* tommy_list;
/**
* Initializes the list.
* The list is completely inplace, so it doesn't need to be deinitialized.
*/
tommy_inline void tommy_list_init(tommy_list* list)
{
*list = 0;
}
/**
* Gets the head of the list.
* \return The head node. For empty lists 0 is returned.
*/
tommy_inline tommy_node* tommy_list_head(tommy_list* list)
{
return *list;
}
/**
* Gets the tail of the list.
* \return The tail node. For empty lists 0 is returned.
*/
tommy_inline tommy_node* tommy_list_tail(tommy_list* list)
{
tommy_node* head = tommy_list_head(list);
if (!head)
return 0;
return head->prev;
}
/** \internal
* Creates a new list with a single element.
* \param list The list to initialize.
* \param node The node to insert.
*/
tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node)
{
/* one element "circular" prev list */
node->prev = node;
/* one element "0 terminated" next list */
node->next = 0;
*list = node;
}
/** \internal
* Inserts an element at the head of a not empty list.
* The element is inserted at the head of the list. The list cannot be empty.
* \param list The list. The list cannot be empty.
* \param node The node to insert.
*/
tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node)
{
tommy_node* head = tommy_list_head(list);
/* insert in the "circular" prev list */
node->prev = head->prev;
head->prev = node;
/* insert in the "0 terminated" next list */
node->next = head;
*list = node;
}
/** \internal
* Inserts an element at the tail of a not empty list.
* The element is inserted at the tail of the list. The list cannot be empty.
* \param head The node at the list head. It cannot be 0.
* \param node The node to insert.
*/
tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node)
{
/* insert in the "circular" prev list */
node->prev = head->prev;
head->prev = node;
/* insert in the "0 terminated" next list */
node->next = 0;
node->prev->next = node;
}
/**
* Inserts an element at the head of a list.
* \param node The node to insert.
* \param data The object containing the node. It's used to set the tommy_node::data field of the node.
*/
tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data)
{
tommy_node* head = tommy_list_head(list);
if (head) {
tommy_list_insert_head_not_empty(list, node);
} else {
tommy_list_insert_first(list, node);
}
node->data = data;
}
/**
* Inserts an element at the tail of a list.
* \param node The node to insert.
* \param data The object containing the node. It's used to set the tommy_node::data field of the node.
*/
tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data)
{
tommy_node* head = tommy_list_head(list);
if (head) {
tommy_list_insert_tail_not_empty(head, node);
} else {
tommy_list_insert_first(list, node);
}
node->data = data;
}
/** \internal
* Removes an element from the head of a not empty list.
* \param list The list. The list cannot be empty.
* \return The node removed.
*/
tommy_inline tommy_node* tommy_list_remove_head_not_empty(tommy_list* list)
{
tommy_node* head = tommy_list_head(list);
/* remove from the "circular" prev list */
head->next->prev = head->prev;
/* remove from the "0 terminated" next list */
*list = head->next; /* the new head, in case 0 */
return head;
}
/**
* Removes an element from the list.
* You must already have the address of the element to remove.
* \note The node content is left unchanged, including the tommy_node::next
* and tommy_node::prev fields that still contain pointers at the list.
* \param node The node to remove. The node must be in the list.
* \return The tommy_node::data field of the node removed.
*/
tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node)
{
tommy_node* head = tommy_list_head(list);
/* remove from the "circular" prev list */
if (node->next) {
node->next->prev = node->prev;
} else {
head->prev = node->prev; /* the last */
}
/* remove from the "0 terminated" next list */
if (head == node) {
*list = node->next; /* the new head, in case 0 */
} else {
node->prev->next = node->next;
}
return node->data;
}
/**
* Concats two lists.
* The second list is concatenated at the first list.
* \param first The first list.
* \param second The second list. After this call the list content is undefined,
* and you should not use it anymore.
*/
void tommy_list_concat(tommy_list* first, tommy_list* second);
/**
* Sorts a list.
* It's a stable merge sort with O(N*log(N)) worst complexity.
* It's faster on degenerated cases like partially ordered lists.
*/
void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp);
/**
* Checks if empty.
* \return If the list is empty.
*/
tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list)
{
return tommy_list_head(list) == 0;
}
#endif

313
tommytypes.h Normal file
View file

@ -0,0 +1,313 @@
/*
* Copyright 2010 Andrea Mazzoleni. 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 ANDREA MAZZOLENI 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 ANDREA MAZZOLENI 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.
*/
/** \file
* Generic types.
*/
#ifndef __TOMMYTYPES_H
#define __TOMMYTYPES_H
/******************************************************************************/
/* types */
#if defined(_MSC_VER)
#include <stddef.h>
typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */
typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */
typedef size_t tommy_size_t; /**< Generic size_t type. */
typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */
#else
#include <stdint.h>
typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */
typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */
typedef uintptr_t tommy_size_t; /**< Generic size_t type. */
typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */
#endif
typedef int tommy_bool_t; /**< Generic boolean type. */
/** \internal
* Type cast required for the C++ compilation.
* When compiling in C++ we cannot convert a void* pointer to another pointer.
* In such case we need an explicit cast.
*/
#ifdef __cplusplus
#define tommy_cast(type, value) static_cast<type>(value)
#else
#define tommy_cast(type, value) (value)
#endif
/******************************************************************************/
/* heap */
/* by default uses malloc/realloc/free */
/**
* Generic malloc(), realloc() and free() functions.
* Redefine them to what you need. By default they map to the C malloc(), realloc() and free().
*/
#if !defined(tommy_malloc) && !defined(tommy_realloc) && !defined(tommy_free)
#include <stdlib.h>
#define tommy_malloc malloc
#define tommy_realloc realloc
#define tommy_free free
#endif
/******************************************************************************/
/* modificators */
/** \internal
* Definition of the inline keyword if available.
*/
#if !defined(tommy_inline)
#if __STDC_VERSION__ >= 199901L || defined(__cplusplus)
#define tommy_inline inline
#elif defined(_MSC_VER) || defined(__GNUC__)
#define tommy_inline static __inline
#else
#define tommy_inline static
#endif
#endif
/** \internal
* Definition of the always_inline keyword if available.
*/
#if !defined(tommy_always_inline)
#if defined(__GNUC__)
#define tommy_always_inline static __attribute__((always_inline)) __inline
#else
#define tommy_always_inline tommy_inline
#endif
#endif
/** \internal
* Definition of the restrict keyword if available.
*/
#if !defined(tommy_restrict)
#if __STDC_VERSION__ >= 199901L
#define tommy_restrict restrict
#elif defined(_MSC_VER) || defined(__GNUC__)
#define tommy_restrict __restrict
#else
#define tommy_restrict
#endif
#endif
/** \internal
* Hints the compiler that a condition is likely true.
*/
#if !defined(tommy_likely)
#if defined(__GNUC__)
#define tommy_likely(x) __builtin_expect(!!(x),1)
#else
#define tommy_likely(x) (x)
#endif
#endif
/** \internal
* Hints the compiler that a condition is likely false.
*/
#if !defined(tommy_unlikely)
#if defined(__GNUC__)
#define tommy_unlikely(x) __builtin_expect(!!(x),0)
#else
#define tommy_unlikely(x) (x)
#endif
#endif
/******************************************************************************/
/* key */
/**
* Key type used in indexed data structures to store the key or the hash value.
*/
typedef tommy_uint32_t tommy_key_t;
/**
* Bits into the ::tommy_key_t type.
*/
#define TOMMY_KEY_BIT (sizeof(tommy_key_t) * 8)
/******************************************************************************/
/* node */
/**
* Data structure node.
* This node type is shared between all the data structures and used to store some
* info directly into the objects you want to store.
*
* A typical declaration is:
* \code
* struct object {
* tommy_node node;
* // other fields
* };
* \endcode
*/
typedef struct tommy_node_struct {
/**
* Next node.
* The tail node has it at 0, like a 0 terminated list.
*/
struct tommy_node_struct* next;
/**
* Previous node.
* The head node points to the tail node, like a circular list.
*/
struct tommy_node_struct* prev;
/**
* Pointer at the object containing the node.
* This field is initialized when inserting nodes into a data structure.
*/
void* data;
/**
* Key used to store the node.
* With hashtables this field is used to store the hash value.
* With lists this field is not used.
*/
tommy_key_t key;
} tommy_node;
/******************************************************************************/
/* compare */
/**
* Compare function for elements.
*/
typedef int tommy_compare_func(const void* void_a, const void* void_b);
/******************************************************************************/
/* bit hacks */
#if defined(_MSC_VER) && !defined(__cplusplus)
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#endif
/** \internal
* Integer log2 for constants.
* You can use it only for exact power of 2 up to 256.
*/
#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 :(value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0)
/**
* Bit scan reverse or integer log2.
* Return the bit index of the most significant 1 bit.
*
* If no bit is set, the result is undefined.
* To force a return 0 in this case, you can use tommy_ilog2(value | 1).
*
* Other interesting ways for bitscan can be found at:
*
* Bit Twiddling Hacks
* http://graphics.stanford.edu/~seander/bithacks.html
*
* Chess Programming BitScan
* http://chessprogramming.wikispaces.com/BitScan
*
* \param value Value to scan. 0 is not allowed.
* \return The index of the most significan bit set.
*/
tommy_inline unsigned tommy_ilog2_u32(tommy_uint32_t value)
{
#if defined(_MSC_VER)
unsigned long count;
_BitScanReverse(&count, value);
return count;
#elif defined(__GNUC__)
return 31 - __builtin_clz(value);
#else
/* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */
/* from http://graphics.stanford.edu/~seander/bithacks.html */
static const int TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = {
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27];
#endif
}
/**
* Bit scan forward or trailing zero count.
* Return the bit index of the least significant 1 bit.
*
* If no bit is set, the result is undefined.
* \param value Value to scan. 0 is not allowed.
* \return The index of the least significan bit set.
*/
tommy_inline unsigned tommy_ctz_u32(tommy_uint32_t value)
{
#if defined(_MSC_VER)
unsigned long count;
_BitScanForward(&count, value);
return count;
#elif defined(__GNUC__)
return __builtin_ctz(value);
#else
/* Count the consecutive zero bits (trailing) on the right with multiply and lookup */
/* from http://graphics.stanford.edu/~seander/bithacks.html */
static const tommy_uint32_t TOMMY_DE_BRUIJN_INDEX_CTZ[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & -value) * 0x077CB531U)) >> 27];
#endif
}
/**
* Rounds up to the next power of 2.
* For the value 0, the result is undefined.
* \return The smallest power of 2 not less than the specified value.
*/
tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
{
/* Round up to the next highest power of 2 */
/* from http://www-graphics.stanford.edu/~seander/bithacks.html */
--value;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
++value;
return value;
}
#endif

170
util.h Normal file
View file

@ -0,0 +1,170 @@
/*
* Copyright (C) 2011 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/****************************************************************************/
/* string */
static inline char* strtoken(char* s)
{
while (*s && *s != ' ')
++s;
if (*s)
*s++ = 0;
while (*s == ' ')
++s;
return s;
}
static inline char* strskip(char* s)
{
while (*s == ' ')
++s;
return s;
}
static inline int strgets(char* s, unsigned size, FILE* f)
{
char* i = s;
char* send = s + size;
int c;
while (1) {
c = fgetc_unlocked(f);
if (c == EOF || c == '\n')
break;
*i++ = c;
if (i == send) {
return -1;
}
}
if (c == EOF) {
if (ferror_unlocked(f)) {
return -1;
}
if (i == s)
return 0;
}
/* remove ending spaces */
while (i != s && isspace(i[-1]))
--i;
*i = 0;
return 1;
}
static inline int stru(const char* s, unsigned* value)
{
unsigned v;
if (!*s)
return -1;
v = 0;
while (*s>='0' && *s<='9') {
v *= 10;
v += *s - '0';
++s;
}
if (*s)
return -1;
*value = v;
return 0;
}
static inline int stru64(const char* s, uint64_t* value)
{
uint64_t v;
if (!*s)
return -1;
v = 0;
while (*s>='0' && *s<='9') {
v *= 10;
v += *s - '0';
++s;
}
if (*s)
return -1;
*value = v;
return 0;
}
static char strhexset[16] = "0123456789abcdef";
static inline void strenchex(char* str, const void* void_data, unsigned data_len)
{
const unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
unsigned char b = data[i];
*str++ = strhexset[b >> 4];
*str++ = strhexset[b & 0xF];
}
}
static inline char* strdechex(void* void_data, unsigned data_len, char* str)
{
unsigned char* data = void_data;
unsigned i;
for(i=0;i<data_len;++i) {
char c0;
char c1;
unsigned char b0;
unsigned char b1;
c0 = *str;
if (c0 >= 'A' && c0 <= 'F')
b0 = c0 - 'A' + 10;
else if (c0 >= 'a' && c0 <= 'f')
b0 = c0 - 'a' + 10;
else if (c0 >= '0' && c0 <= '9')
b0 = c0 - '0';
else
return str;
++str;
c1 = *str;
if (c1 >= 'A' && c1 <= 'F')
b1 = c1 - 'A' + 10;
else if (c1 >= 'a' && c1 <= 'f')
b1 = c1 - 'a' + 10;
else if (c1 >= '0' && c1 <= '9')
b1 = c1 - '0';
else
return str;
++str;
data[i] = (b0 << 4) | b1;
}
return 0;
}