Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 11 Jul 2007 10:09:32 +0000
From:      Pawel Wieczorek <wieczyk@trzask.int.pl>
To:        freebsd-arch@freebsd.org
Subject:   new friend of sys/queue.h and sys/tree.h 
Message-ID:  <4694AC5C.9040107@trzask.int.pl>
In-Reply-To: <20070710.205751.-1962670861.imp@bsdimp.com>
References:  <20070708081511.GX1221@funkthat.com>	<200707101833.l6AIX0xl049962@ambrisko.com> <20070710.205751.-1962670861.imp@bsdimp.com>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Hi,

I did something like sys/queue.h and sys/tree.h: sys/bds.h

bds - Basic Data Structures.

Hash table are basic data structure, it is good to have it in kernel, 
but i did not see some easy to use framework like sys/queue.h in our kernel.

At current time it have only hash table and multi-value hash table,
but i want to add in near future something else.

I did not done full documentation how to use it(yet),
but i did two examples. One is how to use it user-space
and one how to use it in kernel-space.

I tested code on "cc" on OpenSolaris (because cc on sunos is more 
pedantic) and "cc" "c++" "c89" "c99" on FreeBSD 6.2-RELEASE.


I develop sys/bds.h for my private usage, but i think it can be used in 
freebsd kernel programming, because is safe, easy to use and similar to 
sys/queue.h.

I am waiting for comments...


[-- Attachment #2 --]
bdsm000755 001751 001750 00000000000 10645124276 012577 5ustar00wieczykkonta000000 000000 bdsm/Makefile000755 001751 001750 00000000172 10645123743 014317 0ustar00wieczykkonta000000 000000 SUBDIR=example0 example1

load:
	cd example1/dev; make load


copy:
	cp example1/util/bdsctl .



.include<bsd.subdir.mk>
bdsm/man000755 001751 001750 00000000000 10644177742 013357 5ustar00wieczykkonta000000 000000 bdsm/sys000755 001751 001750 00000000000 10645124733 013413 5ustar00wieczykkonta000000 000000 bdsm/example0000755 001751 001750 00000000000 10645124344 014306 5ustar00wieczykkonta000000 000000 bdsm/example1000755 001751 001750 00000000000 10645022507 014305 5ustar00wieczykkonta000000 000000 bdsm/README000644 000000 001750 00000001213 10645124176 013012 0ustar00rootkonta000000 000000 Example usage of sys/bds.h
==========================

To compile type:
# make


example0/		Small programm which allow test
			one structure used by MAP and MMAP.
			After compilation you can execute
			example0/example0 binary.


example1		Small kernel module and utility programm
			which allow test MAP on kernel side.
			After compilation you should type 
			# make load copy
			to load module into kernel and copy bdsctl
			utility to your current direcotry.
			Run utility
			# ./bdsctl
			...and have fun.
			WARNING: Because kernel module is using
				 printf(9) routine, please
				 be on first terminal on console
				 or watch dmesg.

			

bdsm/example1/dev000755 001751 001750 00000000000 10645124371 015065 5ustar00wieczykkonta000000 000000 bdsm/example1/Makefile000755 001751 001750 00000000051 10645030725 016024 0ustar00wieczykkonta000000 000000 SUBDIR=dev util

.include<bsd.subdir.mk>
bdsm/example1/util000755 001751 001750 00000000000 10645124603 015262 5ustar00wieczykkonta000000 000000 bdsm/example1/util/Makefile000644 001751 001750 00000000115 10645037242 017000 0ustar00wieczykkonta000000 000000 PROG=bdsctl
SRCS=main.c
CFLAGS=-I../.. -I../dev
MAN=

.include<bsd.prog.mk>

bdsm/example1/util/main.c000644 001751 001750 00000007000 10645124565 016435 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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 <sys/param.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include "sys/bds.h"
#include "kbds.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <assert.h>

int __fd;

void
opendev()
{
	__fd = open("/dev/bds",O_RDWR);
	assert( __fd != -1 );
}


#define FL_FIRST 0x01
#define FL_LAST 0x02
#define FL_ID 0x04
#define FL_PRINT 0x08
#define FL_REMOVE 0x10
#define FL_RESET 0x20

#define C_INSERT (FL_FIRST|FL_LAST)
#define C_PRINT (FL_PRINT)
#define C_LOOKUP (FL_ID)
#define C_RESET (FL_RESET)
#define C_REMOVE (FL_REMOVE)

void
usage()
{
	printf("bdsctl -f [first] -l [last]i	# add new person into kernel citizen registry\n");
	printf("bsdctl -i [id]			# lookup KCR for ID\n");
	printf("bsdctl -p			# request kernel to print KCR\n");
	printf("bsdctl -r			# reset KCR\n");
	printf("bsdctl -d [id]			# remove from KCR person with id\n");
	printf("WARNING: for 'add' 'reset' commands kernel is printing output by printf(9)\n");
}

int
main(int argc, char **argv)
{
	char ch;
	struct kperson k;
	int id;
	int flag = 0;
	while ( (ch = getopt(argc,argv,"f:l:i:d:rph")) != -1 )
		switch (ch) {
			case 'f':
				flag |= FL_FIRST;
				strncpy(k.first,optarg,FIRST_MAX-1);
				break;

			case 'l':
				flag |= FL_LAST;
				strncpy(k.last,optarg,LAST_MAX-1);
				break;

			case 'i':
				flag |= FL_ID;
				id = strtol(optarg,NULL,10);
				break;

			case 'd':
				flag |= FL_REMOVE;
				id = strtol(optarg,NULL,10);
				break;
			
			case 'r':
				flag |= FL_RESET;
				break;

			case 'p':
				flag |= FL_PRINT;
				break;
			default:
			case 'h':
				usage();
				return(0);
		}
	if (flag == 0) {
		usage();
		return(-1);
	}
	opendev();
	if ( flag == C_INSERT ) {
		ioctl(__fd, BDSIOSPERSON, &k );
		printf("Inserted with id %u\n",k.id);
	} else if ( flag == C_PRINT ) {
		ioctl(__fd, BDSIOCKPRINT );
	} else if ( flag == C_LOOKUP ) {
		k.id = id;
		ioctl(__fd, BDSIOGPERSON,&k );
		if ( k.id == -1 ) {
			printf("Not found\n");
		} else {
			printf("%.4u		%s		%s\n",k.id,k.first,k.last);
		}
	} else if ( flag == C_RESET ) {
		ioctl(__fd, BDSIOCKRESET );
	} else if ( flag == C_REMOVE ) {
		printf("x %lu %u\n",BDSIOCREMOVE,id);
		ioctl(__fd, BDSIOCREMOVE,&id );
	} else
		usage();
	return (0);
}
bdsm/example1/dev/Makefile000755 001751 001750 00000000114 10645036042 016600 0ustar00wieczykkonta000000 000000 KMOD=	bds
SRCS=	bds.c hash_support.c
CFLAGS= -I../..
.include <bsd.kmod.mk>
bdsm/example1/dev/bds.c000755 001751 001750 00000012574 10645124704 016074 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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 <sys/param.h>
#include <sys/uio.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/ioccom.h>
#include <sys/conf.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include "sys/bds.h"

#include "kbds.h"





struct citizen_registry citizen_registry;

/*-------------------------------------------------------------------
 * Data management
 */


MALLOC_DEFINE(M_KCRENTRY, "Kernel Citizen Registry Entry", "");


static void kcr_add( struct kperson *k );
static void kcr_get( struct kperson *k );
static void kcr_print(void);
static void kcr_remove( int id );
static void kcr_reset(void);

void
kcr_add( struct kperson *param )
{
	static int _id = 0x7461;
	struct key_id kid;
	struct kperson *pcopy;
	param->id = kid.id = _id++;
	param->first[FIRST_MAX-1] = 0;
	param->last[LAST_MAX-1] = 0;
	pcopy = (struct kperson *) malloc( sizeof(*pcopy), M_KCRENTRY, M_WAITOK );
	memcpy(pcopy,param,sizeof(*pcopy));
	MAP_INSERT( &citizen_registry, &kid, pcopy, citreg_entry );
}

void
kcr_get( struct kperson *param )
{
	struct key_id kid;
	struct kperson *pcopy;

	kid.id = param->id;
	MAP_LOOKUP( &citizen_registry, &kid, pcopy, citreg_entry );
	if ( pcopy ) {
		memcpy(param,pcopy,sizeof(*pcopy));
	} else {
		param->id = -1;
	}
}

void
kcr_print()
{
	struct kperson *k;
	printf("\n-----Kernel Citizen Registry------------\n");
	MAP_FOREACH( k, &citizen_registry, citreg_entry ) {
		printf("%.4u		%s		%s\n",k->id,k->first,k->last);
	}
	printf("----------------------------------------\n");
}

void
kcr_remove(int id )
{
	struct kperson *person;
	struct key_id kid;
	kid.id = id;
	MAP_LOOKUP( &citizen_registry, &kid, person, citreg_entry );
	if ( person ) {
		MAP_REMOVE( &citizen_registry, person, kperson, citreg_entry );
		free(person,M_KCRENTRY);
	} else {
		printf("No person with id %i\n",id);
	}
}

void
kcr_reset()
{
	struct kperson *person;
	MAP_FOREACH_SAFE( person, &citizen_registry, citreg_entry ) {
		MAP_REMOVE( &citizen_registry, person, kperson, citreg_entry );
		free(person,M_KCRENTRY);
	}
}


/*-------------------------------------------------------------------
 * Device 
 */

static int bdsdev_open(struct cdev *dev, int flag, int otyp, struct thread *td);
static int bdsdev_close(struct cdev *dev, int flag, int otyp, struct thread *td);
static int bdsdev_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td);
static int bdsdev_write(struct cdev *dev, struct uio *uio, int ioflag);
static int bdsdev_read(struct cdev *dev, struct uio *uio, int ioflag);


int
bdsdev_open(struct cdev *dev, int flag, int otyp, struct thread *td)
{
	return (0);
}

int
bdsdev_close(struct cdev *dev, int flag, int otyp, struct thread *td)
{
	return (0);
}

int
bdsdev_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
    struct thread *td)
{
	int error = 0;
	union {
		caddr_t arg;
		struct kperson *person;
		int *id;
	} ARG;

	
	ARG.arg = arg;
	switch(cmd) {
		case BDSIOSPERSON:
			kcr_add(ARG.person);
			break;
		case BDSIOGPERSON:
			kcr_get(ARG.person);
			break;
		case BDSIOCKPRINT:
			kcr_print();
			break;
		case BDSIOCKRESET:
			kcr_reset();
			break;
		case BDSIOCREMOVE:
			kcr_remove(*ARG.id);
			break;
		default:
			error = EINVAL;
			break;
	}
	return (error);
}

int
bdsdev_write(struct cdev *dev, struct uio *uio, int ioflag)
{
    return(ENOTSUP);
}

int
bdsdev_read(struct cdev *dev, struct uio *uio, int ioflag)
{
    return(ENOTSUP);
}

/*-------------------------------------------------------------------
 * Module
 */

static struct cdevsw bds_bdsdevsw = {
	.d_version = D_VERSION,
	.d_open = bdsdev_open,
	.d_close = bdsdev_close,
	.d_read = bdsdev_read,
	.d_write = bdsdev_write,
	.d_ioctl = bdsdev_ioctl,
	.d_name = "bds"
};

static struct cdev *sdev;


static int
bdsdev_load(module_t mod, int cmd, void *arg)
{
    int  err = 0;
    switch (cmd) {
    case MOD_LOAD:
	hash_init(&citizen_registry);
	sdev = make_dev(&bds_bdsdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "bds");
	break;

    case MOD_UNLOAD:
	kcr_reset();
	destroy_dev(sdev);
	break;
    default:
	err = EOPNOTSUPP;
	break;
    }

    return(err);
}

DEV_MODULE(bdsdev, bdsdev_load, NULL);
bdsm/example1/dev/hash_support.c000755 001751 001750 00000003744 10645026050 020035 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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 <sys/param.h>

#include <sys/queue.h>
#include "sys/bds.h"

#include "kbds.h"

static unsigned int key_id_hash( const struct key_id *k );
static int key_id_cmp( const struct key_id *k1, const struct key_id *k2 );
static void key_id_cp( struct key_id *dst, const struct key_id *src );


static unsigned int
key_id_hash( const struct key_id *k )
{
	return(k->id);
}

static int
key_id_cmp( const struct key_id *k1, const struct key_id *k2 )
{
	return( k1->id == k2->id );
}

static void
key_id_cp( struct key_id *dst, const struct key_id *src )
{
	dst->id = src->id;
}


void
hash_init( struct citizen_registry *h )
{
	MAP_INIT(h,key_id_hash,key_id_cmp,key_id_cp);
}
bdsm/example1/dev/kbds.h000755 001751 001750 00000003662 10645124631 016251 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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.
 *
 */

#ifndef K_BDS_H
#define K_BDS_H



struct key_id {
	int id;
};

#define FIRST_MAX 100
#define LAST_MAX 100

struct kperson {
	int id;
	char first[FIRST_MAX];
	char last[FIRST_MAX];
	MAP_ENTRY(kperson,key_id) citreg_entry;
};


#define BDSIOSPERSON	_IOWR('p', 1, struct kperson)
#define BDSIOGPERSON	_IOWR('p', 2, struct kperson)
#define BDSIOCKPRINT	_IO('c',3)
#define BDSIOCKRESET	_IO('c',4)
#define BDSIOCREMOVE	_IOW('p',5,int)

#ifdef _KERNEL

/* Kernel world, kernel citizens ]=;] */

MAP_HEAD( citizen_registry, kperson, 13 );

void hash_init( struct citizen_registry *h );

#endif /* _KERNEL */

#endif /* K_BDS_H */
bdsm/example0/Makefile000755 001751 001750 00000000132 10645124336 016025 0ustar00wieczykkonta000000 000000 CC=cc
PROG=example0
SRCS=main.c hash_support.c
CFLAGS= -I.. 
MAN=


.include<bsd.prog.mk>
bdsm/example0/main.c000755 001751 001750 00000015601 10645122730 015460 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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 <sys/queue.h>
#include "sys/bds.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "example0.h"


struct workers_by_id wrks_by_id;
struct workers_by_last wrks_by_last;


/*===================================================================
 * Data management routines
 */



static void
worker_add( struct worker *worker )
{
	static int _key = 0x1234;
	struct key_id kid;
	struct key_last klast;

	worker->id = _key++;
	kid.id = worker->id;
	MAP_INSERT(&wrks_by_id, &kid, worker, hash_by_id );

	strcpy(klast.last,worker->last);
	MMAP_INSERT(&wrks_by_last,&klast, worker, hash_by_last );
	
}

static struct worker *
worker_lookup( int id )
{
	struct worker *worker;
	struct key_id k;

	k.id = id;
	MAP_LOOKUP(&wrks_by_id, &k, worker, hash_by_id );
	return (worker);
}


static int
worker_remove_by_last( const char * last )
{
	struct worker *worker;
	struct key_last klast;
	strcpy(klast.last,last);
	MMAP_LOOKUP_FOREACH_SAFE( &wrks_by_last, &klast, worker, hash_by_last ) {
		MAP_REMOVE(&wrks_by_id,worker,worker,hash_by_id );
		MMAP_REMOVE(&wrks_by_last,worker,worker,hash_by_last);		
		printf("%.4u %-15s %-15s %.2u %-15s\n",worker->id,worker->first,worker->last,worker->age,worker->city);	
	}
	return(0);
}

static int
worker_remove( int id )
{
	struct worker *worker;
	worker = worker_lookup( id );

	if ( worker == NULL ) return(-1);
	MAP_REMOVE(&wrks_by_id,worker,worker,hash_by_id );
	MMAP_REMOVE(&wrks_by_last,worker,worker,hash_by_last);

	free(worker);
	return(1);
}


static void
worker_list_by_last(const char *last)
{
	struct worker *worker;
	struct key_last klast;
	strcpy(klast.last,last);
	MMAP_LOOKUP_FOREACH( &wrks_by_last, &klast, worker, hash_by_last) {
		printf("%.4u %-15s %-15s %.2u %-15s\n",worker->id,worker->first,worker->last,worker->age,worker->city);	
	}
}

static void
worker_list()
{
	struct worker *worker;
	MAP_FOREACH(  worker, &wrks_by_id, hash_by_id) {
		printf("%.4u %-15s %-15s %.2u %-15s\n",worker->id,worker->first,worker->last,worker->age,worker->city);	
	}
}


/*===================================================================
 * User interface handlers
 */

static int __get(char *k,char *data, int len);
#define __GET( kom, d, l ) if (__get(kom,d,l) ) return;

static void ui_insert();
static void ui_select_id();

static int
__get(char *k,char *data, int len)
{
	int n;
	printf("%s: ",k);
	fflush(stdout);
	data[0] = 0;
	fpurge(stdin);
	if ( fgets(data,len,stdin) == NULL ) return(1);
	n = strlen(data);
	if ( n > 1 )
	data[n-1] = 0;
	return(0);
}

static void
ui_list()
{
	worker_list();
}

static void
ui_select_id()
{
	struct worker *worker;
	char id[10];
	int _id;

	__GET("ID",id,sizeof(id));
	_id = strtol(id,NULL,10);

	worker = worker_lookup(_id);
	if ( worker == NULL ) {
		printf("Error: cannot find worker with id %.4u\n",_id);
		return;
	}
	printf("Found: %.4u %-15s %-15s %.2u %-15s\n",worker->id,worker->first,worker->last,worker->age,worker->city);	
}


static void
ui_select_last()
{
	char last[LAST_MAX];

	__GET("Last",last,sizeof(last));

	worker_list_by_last(last);
}

static void
ui_remove_id()
{
	char id[10];
	int _id;

	__GET("ID",id,sizeof(id));
	_id = strtol(id,NULL,10);

	if ( worker_remove(_id) ) {
		printf("Error: no user with this id\n");
	} else {
		printf("Removed\n");
	}
}

static void
ui_remove_last()
{
	char last[LAST_MAX];
	__GET("Last",last,sizeof(last));

	worker_remove_by_last(last);
}


static void
ui_insert()
{
	struct worker *worker;
	struct worker temp;
	char age[10];

	puts("==> Adding new worker into memory");
	__GET("First name",temp.first,sizeof(temp.first));
	__GET("Last name",temp.last,sizeof(temp.last));
	__GET("Age",age,sizeof(age));
	__GET("City",temp.city,sizeof(temp.city));
	
	temp.age = strtol(age,NULL,10);
	
	worker = (struct worker*) malloc( sizeof(*worker));
	memcpy(worker,&temp,sizeof(*worker));

	worker_add(worker);
	printf("Added with id %.4u\n",worker->id);

}



/*===================================================================
 * Main menu
 */


enum option_v {
	OP_INVALID,
	OP_QUIT,
	OP_INSERT,
	OP_LIST,
	OP_SELECT_ID,
	OP_SELECT_LAST,
	OP_REMOVE_ID,
	OP_REMOVE_LAST
};


struct option {
	const char *text;
	enum option_v value;
};


static enum option_v find_option(char c);
static enum option_v get_option_from_user(void);

struct option options[] = {
	{ "Quit", OP_QUIT },
	{ "Insert new person to memory", OP_INSERT },
	{ "List persons from memort", OP_LIST },
	{ "Check person ID", OP_SELECT_ID },
	{ "Show persons by last name", OP_SELECT_LAST },
	{ "Remove by ID", OP_REMOVE_ID },
	{ "Remove by last", OP_REMOVE_LAST },
	{ NULL, OP_INVALID },
};


static enum option_v
find_option(char c)
{
	struct option *op = options;
	while ( op->text && c-- ) {
		op++;
	}
	
	return (op->value);
}

static enum option_v 
get_option_from_user()
{
	char c;
	fpurge(stdin);
	c = fgetc(stdin) - '0';
	if ( feof(stdin) ) {
		return (OP_QUIT);
	}
	return (find_option(c));
	
}


static int
main_menu()
{
	int i;
	int r = 1;
	struct option *op = options;
	printf("-----\n");
	for (i = 0; op->text; i++,op++ ) {
 		printf(" - %.1u		%s\n",i,op->text);
	}
	printf("\nPress option number key and hit enter: ");
	switch ( get_option_from_user() ) {
		case OP_QUIT:
			r = 0;
			break;

		case OP_INSERT:
			ui_insert();
			break;

		case OP_LIST:
			ui_list();
			break;

		case OP_SELECT_ID:
			ui_select_id();
			break;

		case OP_SELECT_LAST:
			ui_select_last();
			break;

		case OP_INVALID:
			break;

		case OP_REMOVE_ID:
			ui_remove_id();
			break;

		case OP_REMOVE_LAST:
			ui_remove_last();
			break;

	}
	return (r);
}

int
main()
{
	id_hash_init(&wrks_by_id);
	last_hash_init(&wrks_by_last);
	while ( main_menu() );
	return (0);
}
bdsm/example0/hash_support.c000755 001751 001750 00000004473 10645001315 017253 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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 <sys/queue.h>
#include "sys/bds.h"

#include <stdio.h>
#include <string.h>

#include "example0.h"


static unsigned int
key_id_hash( const struct key_id *k )
{
	return(k->id);
}

static int
key_id_cmp( const struct key_id *k1, const struct key_id *k2 )
{
	return( k1->id == k2->id );
}

static void
key_id_cp( struct key_id *dst, const struct key_id *src )
{
	dst->id = src->id;
}


void
id_hash_init( struct workers_by_id *hash )
{
	MAP_INIT(hash,key_id_hash,key_id_cmp,key_id_cp);
}



static unsigned int
key_last_hash( const struct key_last *k )
{
	int h = 29;
	const char *p;
	for ( p = k->last; *p != '\0'; p++ ) {
		h += *p;
	}
	return (h);
}

static int
key_last_cmp( const struct key_last *k1, const struct key_last *k2 )
{
	return (strcasecmp(k1->last,k2->last) == 0);
}


static void
key_last_cp( struct key_last *dst, const struct key_last *src )
{
	strcpy(dst->last,src->last);
}

void
last_hash_init( struct workers_by_last *hash )
{
	MMAP_INIT(hash,key_last_hash,key_last_cmp,key_last_cp);
}
bdsm/example0/example0.h000755 001751 001750 00000003630 10644771317 016265 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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.
 *
 */

#ifndef EXAMPLE_0
#define EXAMPLE_0

#define PRIME_NUMBER 13


#define FIRST_MAX 120
#define LAST_MAX 120
#define CITY_MAX 120

struct key_id {
	int id;
};

struct key_last {
	char last[LAST_MAX];
};

struct worker {
	int id;
	char first[FIRST_MAX];
	char last[LAST_MAX];
	int age;
	char city[CITY_MAX];

	MAP_ENTRY(worker,key_id) hash_by_id;
	MMAP_ENTRY(worker,key_last) hash_by_last;
};

MAP_HEAD(workers_by_id,worker,PRIME_NUMBER);
MMAP_HEAD(workers_by_last,worker,PRIME_NUMBER);

void id_hash_init( struct workers_by_id *table );
void last_hash_init( struct workers_by_last *table );


#endif
bdsm/sys/bds.h000755 001751 001750 00000022703 10645124724 014422 0ustar00wieczykkonta000000 000000 /*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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.
 *
 */

#ifndef SYS_BDS_H
#define SYS_BDS_H


/*
 * This file contain basic data structures. They are similar to structures
 * in sys/queue.h, please read documentation for it first. 
 *
 * !! Include sys/queue.h before this file.
 * 
 * A map ( dictionary, hash-table, associative array ) structure is headed
 * by MAP_HEAD macro. This structure contains table of lists represented
 * by SLIST structure from sys/queue.h. The length of table is typed by user
 * as prime-number-parameter in HEAD macro. Structures are used to
 * decrase time of item access . User had to specify a structure
 * which will be user as key.
 * The initializer (MAP_INIT) need pointers to hash-function, key-copy and
 * key-compare routines. Memory addresses of then them will be stored
 * in data structure for later usage. Please see definition of bds_* types
 * in this file for more information.
 * A multi map structure is headed by MMAP_HEAD macro to reduce lines of code
 *.(less probality to make bug). It is identical to
 * single map with difference that can store many variables for this same key.
 * For this feature it is implementing additional macro: _LOOKUP_FOREACH
 * (and _SAFE version).
 * In implementation the MAP and MMAP are identical, it have different names
 * for logical seperation. It is also using internal template for generate
 * another internal names for MAP and MMAP, in this way compiler can generate
 * error  when programmer will use for example MAP macro on MMAP structure.
 *
 *                          MAP    MMAP
 * _HEAD                    +      +
 * _HEAD_INITIALIZER        -      -
 * _ENTRY                   +      +
 * _INIT                    +      +
 * _EMPTY                   +      +
 * _FIRST                   -      -
 * _NEXT                    -      -
 * _PREV                    -      -
 * _LAST                    -      -
 * _KEY                     +      +
 * _FOREACH                 +      +
 * _FOREACH_SAFE            +      +
 * _FOREACH_REVERSE         -      -
 * _FOREACH_REVERSE_SAFE    -      -
 * _INSERT                  +      +
 * _INSERT_HEAD             -      -
 * _INSERT_BEFORE           -      -
 * _INSERT_AFTER            -      -
 * _INSERT_TAIL             -      -
 * _CONCAT                  -      -
 * _REMOVE_HEAD             -      -
 * _REMOVE                  +      +
 * _LOOKUP                  +      +
 * _LOOKUP_FOREACH          -      +
 * _LOOKUP_FOREACH_SAFE     -      +
 *
 * Implementation notes
 * --------------------
 *
 */


typedef unsigned int bds_hash_func_t( const void *key );
typedef int bds_key_cmp_func_t ( const void *key1, const void *key2 );
typedef void bds_key_cp_func_t( void *keydst, const void *keysrc );

/*
 * Generic map template
 */

#define _tMAP_PRIME_TINY	49
#define _tMAP_PRIME_SMALL	149
#define _tMAP_PRIME_MEDIUM	977
#define _tMAP_PRIME_LARGE	1277
#define _tMAP_PRIME_HUGE	2459


#define _tMAP_LOOKUP_FROM_LIST(head, list,key,var,field,_tfield) do {	\
	int _hifound = 0;						\
	SLIST_FOREACH(var, list, field._tfield ) {			\
		if ( head->cmpfunc(key,&var->field.keycopy) ) {	\
			_hifound = 1;					\
			break;						\
		}							\
	}								\
	if ( !_hifound ) var = NULL;					\
} while(0)

#define _tMAP_INSERT_INTO_LIST(head,list,key,var,field,_tfield) do {	\
	head->cpfunc(&(var->field.keycopy),key);			\
	head->count++;							\
	SLIST_INSERT_HEAD( list, var, field._tfield );			\
} while(0)


#define _tMAP_LIST(head,i) &((head)->list[i])

#define _tMAP_LENGTH(head)  (sizeof((head)->list)/sizeof((head)->list[0]))

#define _tMAP_INDEX(head,key) (((head)->hfunc(key))%_tMAP_LENGTH(head))


#define _tMAP_HEAD(name,type,prime)					\
struct name {								\
	unsigned int tmp_i;						\
	struct type *tmp_v;						\
	int count;							\
	bds_hash_func_t *hfunc;						\
	bds_key_cmp_func_t *cmpfunc;					\
	bds_key_cp_func_t *cpfunc;					\
	SLIST_HEAD(,type) list[prime];					\
}


#define _tMAP_ENTRY(type,keytype,_tfield)				\
struct {								\
	SLIST_ENTRY(type) _tfield;					\
	struct keytype keycopy;						\
}


#define _tMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc) do {			\
	unsigned int _hil;						\
	(head)->hfunc = (bds_hash_func_t*)_hfunc;			\
	(head)->cmpfunc = (bds_key_cmp_func_t*)_cmpfunc;		\
	(head)->cpfunc = (bds_key_cp_func_t*)_cpfunc;			\
	(head)->count = 0;						\
    for ( _hil = 0; _hil < _tMAP_LENGTH(head); _hil++ ) {		\
		SLIST_INIT( _tMAP_LIST(head,_hil) );			\
    }									\
} while(0)

#define _tMAP_EMPTY(head) ((head)->count == 0)

#define _tMAP_KEY(var,field) (&(var)->field.keycopy)


#define _tMAP_INSERT(head, key, var, field,_tfield ) do {		\
	unsigned int _hil = _tMAP_INDEX(head,key);			\
	_tMAP_INSERT_INTO_LIST((head), _tMAP_LIST(head,_hil),		\
			key,var,field,_tfield);				\
} while(0)


#define _tMAP_FOREACH(var, head, field,_tfield)				\
for ( (head)->tmp_i = 0;						\
		(head)->tmp_i < _tMAP_LENGTH(head);			\
		(head)->tmp_i++ )					\
	SLIST_FOREACH(var, _tMAP_LIST(head,(head)->tmp_i), field._tfield)


#define _tMAP_FOREACH_SAFE(var, head, field, _tfield )			\
for ( (head)->tmp_i = 0;						\
		(head)->tmp_i < _tMAP_LENGTH(head);			\
		(head)->tmp_i++ )					\
	SLIST_FOREACH_SAFE(var, _tMAP_LIST(head,(head)->tmp_i), field._tfield,(head)->tmp_v )



#define _tMAP_REMOVE( head, var, type, field, _tfield ) do {		\
	int _hil = _tMAP_INDEX(head, &(var)->field.keycopy);		\
	SLIST_REMOVE( _tMAP_LIST(head,_hil), var, type,field._tfield );\
} while(0)
	


#define _tMAP_LOOKUP( head, key, var, field,_tfield ) do {		\
	unsigned int _hil = _tMAP_INDEX(head,key);			\
	_tMAP_LOOKUP_FROM_LIST( (head), _tMAP_LIST(head,_hil),		\
			key, var, field, _tfield);			\
} while(0)


#define _tMAP_LOOKUP_FOREACH(h, key, var,field,_tfield)			\
	SLIST_FOREACH(var, _tMAP_LIST(h,_tMAP_INDEX(h,key)), field._tfield)\
		if ( (h)->cmpfunc(key,&var->field.keycopy) )
	

#define _tMAP_LOOKUP_FOREACH_SAFE(h, k, d,f, _tfield)			\
	SLIST_FOREACH_SAFE(d, _tMAP_LIST(h,_tMAP_INDEX(h,k)), f._tfield,(h)->tmp_v)\
		if ( (h)->cmpfunc(k,&d->f.keycopy) )


/*
 * Map definitions
 */


#define _MAP_LFIELD	mpfld

#define MAP_HEAD(name,type,prime)					\
	_tMAP_HEAD(name,type,prime)

#define MAP_ENTRY(type,keytype)						\
	_tMAP_ENTRY(type,keytype,_MAP_LFIELD)

#define MAP_KEY(var,field)						\
	_tMAP_KEY(var,field)

#define MAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)				\
	_tMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)

#define MAP_EMPTY(head)							\
	_tMAP_EMPTY(head)

#define MAP_FOREACH( var, head, field)					\
	_tMAP_FOREACH( var, head, field,_MAP_LFIELD)

#define MAP_FOREACH_SAFE( var, head, field )				\
	_tMAP_FOREACH_SAFE( var, head, field, _MAP_LFIELD )

#define MAP_REMOVE( head, var, type, field )				\
	_tMAP_REMOVE( head, var, type, field,_MAP_LFIELD )

#define MAP_REMOVE_BY_KEY( head, key, type, field )			\
	_tMAP_REMOVE_BY_KEY( head, key, type, field,_MAP_LFIELD )

#define MAP_LOOKUP( head, key, var, field )				\
	_tMAP_LOOKUP( head, key, var, field,_MAP_LFIELD )

#define MAP_INSERT(head, key, var, field )				\
	_tMAP_INSERT(head, key, var, field,_MAP_LFIELD )

/*
 * Multi map definitions
 */

#define _MMAP_LFIELD	mmpfld

#define MMAP_HEAD(name,type,prime)					\
	_tMAP_HEAD(name,type,prime)

#define MMAP_ENTRY(type,keytype)					\
	_tMAP_ENTRY(type,keytype,_MMAP_LFIELD)

#define MMAP_KEY(var,field)						\
	_tMAP_KEY(var,field)

#define MMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)				\
	_tMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)

#define MMAP_EMPTY(head)						\
	_tMAP_EMPTY(head)

#define MMAP_FOREACH( var, head, field)					\
	_tMAP_FOREACH( var, head, field,_MMAP_LFIELD)

#define MMAP_FOREACH_SAFE( var, head, field )				\
	_tMAP_FOREACH_SAFE( var, head, field,_MMAP_LFIELD )

#define MMAP_REMOVE( head, var, type, field )				\
	_tMAP_REMOVE( head, var, type, field,_MMAP_LFIELD )

#define MMAP_REMOVE_BY_KEY( head, key, type, field )			\
	_tMAP_REMOVE_BY_KEY( head, key, type, field,_MMAP_LFIELD )

#define MMAP_LOOKUP( head, key, var, field )				\
	_tMAP_LOOKUP( head, key, var, field,_MMAP_LFIELD )

#define MMAP_LOOKUP_FOREACH( head, key, var, field )			\
	_tMAP_LOOKUP_FOREACH( head, key, var, field,_MMAP_LFIELD )

#define MMAP_LOOKUP_FOREACH_SAFE( head, key, var, field )		\
	_tMAP_LOOKUP_FOREACH_SAFE( head, key, var, field,_MMAP_LFIELD )

#define MMAP_INSERT(head, key, var, field )				\
	_tMAP_INSERT(head, key, var, field,_MMAP_LFIELD )



#endif /* SYS_BDS_H */

[-- Attachment #3 --]
/*-
 * Copyright (c) 2007 Pawel Wieczorek <wieczyk@gmail.com>
 * 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 ``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 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.
 *
 */

#ifndef SYS_BDS_H
#define SYS_BDS_H


/*
 * This file contain basic data structures. They are similar to structures
 * in sys/queue.h, please read documentation for it first. 
 *
 * !! Include sys/queue.h before this file.
 * 
 * A map ( dictionary, hash-table, associative array ) structure is headed
 * by MAP_HEAD macro. This structure contains table of lists represented
 * by SLIST structure from sys/queue.h. The length of table is typed by user
 * as prime-number-parameter in HEAD macro. Structures are used to
 * decrase time of item access . User had to specify a structure
 * which will be user as key.
 * The initializer (MAP_INIT) need pointers to hash-function, key-copy and
 * key-compare routines. Memory addresses of then them will be stored
 * in data structure for later usage. Please see definition of bds_* types
 * in this file for more information.
 * A multi map structure is headed by MMAP_HEAD macro to reduce lines of code
 *.(less probality to make bug). It is identical to
 * single map with difference that can store many variables for this same key.
 * For this feature it is implementing additional macro: _LOOKUP_FOREACH
 * (and _SAFE version).
 * In implementation the MAP and MMAP are identical, it have different names
 * for logical seperation. It is also using internal template for generate
 * another internal names for MAP and MMAP, in this way compiler can generate
 * error  when programmer will use for example MAP macro on MMAP structure.
 *
 *                          MAP    MMAP
 * _HEAD                    +      +
 * _HEAD_INITIALIZER        -      -
 * _ENTRY                   +      +
 * _INIT                    +      +
 * _EMPTY                   +      +
 * _FIRST                   -      -
 * _NEXT                    -      -
 * _PREV                    -      -
 * _LAST                    -      -
 * _KEY                     +      +
 * _FOREACH                 +      +
 * _FOREACH_SAFE            +      +
 * _FOREACH_REVERSE         -      -
 * _FOREACH_REVERSE_SAFE    -      -
 * _INSERT                  +      +
 * _INSERT_HEAD             -      -
 * _INSERT_BEFORE           -      -
 * _INSERT_AFTER            -      -
 * _INSERT_TAIL             -      -
 * _CONCAT                  -      -
 * _REMOVE_HEAD             -      -
 * _REMOVE                  +      +
 * _LOOKUP                  +      +
 * _LOOKUP_FOREACH          -      +
 * _LOOKUP_FOREACH_SAFE     -      +
 *
 * Implementation notes
 * --------------------
 *
 */


typedef unsigned int bds_hash_func_t( const void *key );
typedef int bds_key_cmp_func_t ( const void *key1, const void *key2 );
typedef void bds_key_cp_func_t( void *keydst, const void *keysrc );

/*
 * Generic map template
 */

#define _tMAP_PRIME_TINY	49
#define _tMAP_PRIME_SMALL	149
#define _tMAP_PRIME_MEDIUM	977
#define _tMAP_PRIME_LARGE	1277
#define _tMAP_PRIME_HUGE	2459


#define _tMAP_LOOKUP_FROM_LIST(head, list,key,var,field,_tfield) do {	\
	int _hifound = 0;						\
	SLIST_FOREACH(var, list, field._tfield ) {			\
		if ( head->cmpfunc(key,&var->field.keycopy) ) {	\
			_hifound = 1;					\
			break;						\
		}							\
	}								\
	if ( !_hifound ) var = NULL;					\
} while(0)

#define _tMAP_INSERT_INTO_LIST(head,list,key,var,field,_tfield) do {	\
	head->cpfunc(&(var->field.keycopy),key);			\
	head->count++;							\
	SLIST_INSERT_HEAD( list, var, field._tfield );			\
} while(0)


#define _tMAP_LIST(head,i) &((head)->list[i])

#define _tMAP_LENGTH(head)  (sizeof((head)->list)/sizeof((head)->list[0]))

#define _tMAP_INDEX(head,key) (((head)->hfunc(key))%_tMAP_LENGTH(head))


#define _tMAP_HEAD(name,type,prime)					\
struct name {								\
	unsigned int tmp_i;						\
	struct type *tmp_v;						\
	int count;							\
	bds_hash_func_t *hfunc;						\
	bds_key_cmp_func_t *cmpfunc;					\
	bds_key_cp_func_t *cpfunc;					\
	SLIST_HEAD(,type) list[prime];					\
}


#define _tMAP_ENTRY(type,keytype,_tfield)				\
struct {								\
	SLIST_ENTRY(type) _tfield;					\
	struct keytype keycopy;						\
}


#define _tMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc) do {			\
	unsigned int _hil;						\
	(head)->hfunc = (bds_hash_func_t*)_hfunc;			\
	(head)->cmpfunc = (bds_key_cmp_func_t*)_cmpfunc;		\
	(head)->cpfunc = (bds_key_cp_func_t*)_cpfunc;			\
	(head)->count = 0;						\
    for ( _hil = 0; _hil < _tMAP_LENGTH(head); _hil++ ) {		\
		SLIST_INIT( _tMAP_LIST(head,_hil) );			\
    }									\
} while(0)

#define _tMAP_EMPTY(head) ((head)->count == 0)

#define _tMAP_KEY(var,field) (&(var)->field.keycopy)


#define _tMAP_INSERT(head, key, var, field,_tfield ) do {		\
	unsigned int _hil = _tMAP_INDEX(head,key);			\
	_tMAP_INSERT_INTO_LIST((head), _tMAP_LIST(head,_hil),		\
			key,var,field,_tfield);				\
} while(0)


#define _tMAP_FOREACH(var, head, field,_tfield)				\
for ( (head)->tmp_i = 0;						\
		(head)->tmp_i < _tMAP_LENGTH(head);			\
		(head)->tmp_i++ )					\
	SLIST_FOREACH(var, _tMAP_LIST(head,(head)->tmp_i), field._tfield)


#define _tMAP_FOREACH_SAFE(var, head, field, _tfield )			\
for ( (head)->tmp_i = 0;						\
		(head)->tmp_i < _tMAP_LENGTH(head);			\
		(head)->tmp_i++ )					\
	SLIST_FOREACH_SAFE(var, _tMAP_LIST(head,(head)->tmp_i), field._tfield,(head)->tmp_v )



#define _tMAP_REMOVE( head, var, type, field, _tfield ) do {		\
	int _hil = _tMAP_INDEX(head, &(var)->field.keycopy);		\
	SLIST_REMOVE( _tMAP_LIST(head,_hil), var, type,field._tfield );\
} while(0)
	


#define _tMAP_LOOKUP( head, key, var, field,_tfield ) do {		\
	unsigned int _hil = _tMAP_INDEX(head,key);			\
	_tMAP_LOOKUP_FROM_LIST( (head), _tMAP_LIST(head,_hil),		\
			key, var, field, _tfield);			\
} while(0)


#define _tMAP_LOOKUP_FOREACH(h, key, var,field,_tfield)			\
	SLIST_FOREACH(var, _tMAP_LIST(h,_tMAP_INDEX(h,key)), field._tfield)\
		if ( (h)->cmpfunc(key,&var->field.keycopy) )
	

#define _tMAP_LOOKUP_FOREACH_SAFE(h, k, d,f, _tfield)			\
	SLIST_FOREACH_SAFE(d, _tMAP_LIST(h,_tMAP_INDEX(h,k)), f._tfield,(h)->tmp_v)\
		if ( (h)->cmpfunc(k,&d->f.keycopy) )


/*
 * Map definitions
 */


#define _MAP_LFIELD	mpfld

#define MAP_HEAD(name,type,prime)					\
	_tMAP_HEAD(name,type,prime)

#define MAP_ENTRY(type,keytype)						\
	_tMAP_ENTRY(type,keytype,_MAP_LFIELD)

#define MAP_KEY(var,field)						\
	_tMAP_KEY(var,field)

#define MAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)				\
	_tMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)

#define MAP_EMPTY(head)							\
	_tMAP_EMPTY(head)

#define MAP_FOREACH( var, head, field)					\
	_tMAP_FOREACH( var, head, field,_MAP_LFIELD)

#define MAP_FOREACH_SAFE( var, head, field )				\
	_tMAP_FOREACH_SAFE( var, head, field, _MAP_LFIELD )

#define MAP_REMOVE( head, var, type, field )				\
	_tMAP_REMOVE( head, var, type, field,_MAP_LFIELD )

#define MAP_REMOVE_BY_KEY( head, key, type, field )			\
	_tMAP_REMOVE_BY_KEY( head, key, type, field,_MAP_LFIELD )

#define MAP_LOOKUP( head, key, var, field )				\
	_tMAP_LOOKUP( head, key, var, field,_MAP_LFIELD )

#define MAP_INSERT(head, key, var, field )				\
	_tMAP_INSERT(head, key, var, field,_MAP_LFIELD )

/*
 * Multi map definitions
 */

#define _MMAP_LFIELD	mmpfld

#define MMAP_HEAD(name,type,prime)					\
	_tMAP_HEAD(name,type,prime)

#define MMAP_ENTRY(type,keytype)					\
	_tMAP_ENTRY(type,keytype,_MMAP_LFIELD)

#define MMAP_KEY(var,field)						\
	_tMAP_KEY(var,field)

#define MMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)				\
	_tMAP_INIT(head,_hfunc,_cmpfunc,_cpfunc)

#define MMAP_EMPTY(head)						\
	_tMAP_EMPTY(head)

#define MMAP_FOREACH( var, head, field)					\
	_tMAP_FOREACH( var, head, field,_MMAP_LFIELD)

#define MMAP_FOREACH_SAFE( var, head, field )				\
	_tMAP_FOREACH_SAFE( var, head, field,_MMAP_LFIELD )

#define MMAP_REMOVE( head, var, type, field )				\
	_tMAP_REMOVE( head, var, type, field,_MMAP_LFIELD )

#define MMAP_REMOVE_BY_KEY( head, key, type, field )			\
	_tMAP_REMOVE_BY_KEY( head, key, type, field,_MMAP_LFIELD )

#define MMAP_LOOKUP( head, key, var, field )				\
	_tMAP_LOOKUP( head, key, var, field,_MMAP_LFIELD )

#define MMAP_LOOKUP_FOREACH( head, key, var, field )			\
	_tMAP_LOOKUP_FOREACH( head, key, var, field,_MMAP_LFIELD )

#define MMAP_LOOKUP_FOREACH_SAFE( head, key, var, field )		\
	_tMAP_LOOKUP_FOREACH_SAFE( head, key, var, field,_MMAP_LFIELD )

#define MMAP_INSERT(head, key, var, field )				\
	_tMAP_INSERT(head, key, var, field,_MMAP_LFIELD )



#endif /* SYS_BDS_H */

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4694AC5C.9040107>