/*
 * $Id: dataset.h,v 1.28 2003/10/30 09:08:42 jasta Exp $
 *
 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
 *
 * 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, 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.
 */

#ifndef __DATASET_H
#define __DATASET_H

/*****************************************************************************/

/**
 * @file dataset.h
 *
 * @brief Arbitrary key/data association data structure.
 *
 * Flexible data structure used for management of arbitrary sets of data.
 * Currently supports a hash table and a linked list backend, btree should
 * come soon.
 */

/*****************************************************************************/

#include "list.h"
#include "array.h"

/*****************************************************************************/

/* ds_data_t flags */
#define DS_NOCOPY   0x01

/**
 * Data type used to describe a single data entry in the dataset.  Two data
 * entries are used per 'row', one for key and one for value.
 */
typedef struct
{
	void   *data;
	size_t  len;
	uint8_t flags;
} ds_data_t;

/*****************************************************************************/

/**
 *  Describes a specific key/value set.
 */
struct dataset_node
{
	ds_data_t *key;
	ds_data_t *value;

	union
	{
		struct dataset_node *hash_next;
		size_t array_offs;
		List *list_link;
	} tdata;
} /* DatasetNode */;

/*****************************************************************************/

/**
 * Selects the backend data structure implementation for the dataset to use.
 */
typedef enum
{
	DATASET_LIST,                      /**< Linked list */
	DATASET_ARRAY,                     /**< Dynamically allocated array */
	DATASET_HASH                       /**< Hash table */
} DatasetType;

#define DATASET_DEFAULT DATASET_HASH

struct dataset;
typedef struct dataset Dataset;

struct dataset_node;
typedef struct dataset_node DatasetNode;

/*****************************************************************************/

#define DS_NOTFOUND 0x00               /**< DS_FIND: Continue iteration */
#define DS_FOUND    0x02               /**< DS_FIND: Found, halt iteration */

#define DS_CONTINUE 0x01               /**< DS_FOREACH: Continue iteration */
#define DS_BREAK    0x02               /**< DS_FOREACH: Halt iteration */
#define DS_REMOVE   0x04               /**< DS_FOREACH: Remove the node */

/**
 * Main dataset iterator callback.  See ::DatasetForeachExFn for the extended
 * type.
 */
typedef void (*DatasetForeachFn) (ds_data_t *key, ds_data_t *value,
                                  void *udata);

/**
 * Extended dataset iterator capable of basic flow control and removal of
 * entries as you pass by them.
 *
 * @return One of either DS_CONTINUE or DS_BREAK, optionally OR'd against
 *         DS_REMOVE.
 */
typedef int (*DatasetForeachExFn) (ds_data_t *key, ds_data_t *value,
                                   void *udata);

/**
 * Helper macro for properly typecasting iterator functions.
 */
#define DS_FOREACH(func) ((DatasetForeachFn)func)
#define DS_FOREACH_EX(func) ((DatasetForeachExFn)func)

typedef DatasetForeachExFn DatasetFindFn;
#define DS_FIND(func) ((DatasetFindFn)func)

/*****************************************************************************/

EXTERN_C_BEGIN

/*****************************************************************************/

/**
 * Allocate a new dataset.  Using this function is optional, see
 * ::dataset_insert for more information.
 *
 * @param type  Backend implementation to use.
 */
LIBGIFT_EXPORT
  Dataset *dataset_new (DatasetType type);

/**
 * Unallocate a dataset and all internally allocated variables.
 */
LIBGIFT_EXPORT
  void dataset_clear (Dataset *d);

/*****************************************************************************/

/**
 * Initialize a ds_data_t element for use with the extended dataset
 * interface.  Example usage:
 *
 * \code
 * ds_data_t key;
 * ds_data_t data;
 *
 * ds_data_init (&key, "foo", 4, DS_NOCOPY);
 * ds_data_init (&data, "bar", 4, DS_NOCOPY);
 *
 * dataset_insert_ex (&d, &key, &data);
 * \endcode
 *
 * @param dsdata  Storage location to initialize.
 * @param data    Data to be attached with this element.
 * @param len     Length of the data segment, or 0 if you wish to leave
 *                undefined (must set DS_NOCOPY).
 * @param flags   Action-specific flags to use for the data element.
 */
LIBGIFT_EXPORT
  void ds_data_init (ds_data_t *dsdata, const void *data, size_t len,
                     int flags);

/*****************************************************************************/

/**
 * Base implementation for ::dataset_insert and ::dataset_insertstr.
 */
LIBGIFT_EXPORT
  DatasetNode *dataset_insert_ex (Dataset **d, ds_data_t *key,
                                  ds_data_t *value);

/**
 * Insert a new key/value set.  If the value at d is NULL, a new dataset will
 * be allocated using DATASET_DEFAULT.
 *
 * @param d
 * @param key        Storage location of the lookup key.
 * @param key_len    Length of \em key for copying.
 * @param value      Storage location of the value.
 * @param value_len
 *
 *   Length of \em value if you wish for the dataset to internally manage the
 *   values memory, otherwise 0 in which case dataset_clear will not free
 *   this address.
 *
 * @return Node handle that has just been inserted, or NULL on error.
 */
LIBGIFT_EXPORT
  DatasetNode *dataset_insert (Dataset **d,
                               const void *key, size_t key_len,
                               const void *value, size_t value_len);

/**
 * Wrapper around ::dataset_insert for string constants.  Copies both key and
 * value.
 */
LIBGIFT_EXPORT
  DatasetNode *dataset_insertstr (Dataset **d, const char *key,
                                  const char *value);

/*****************************************************************************/

/**
 * Base implementation for ::dataset_remove and ::dataset_removestr.
 */
LIBGIFT_EXPORT
  void dataset_remove_ex (Dataset *d, ds_data_t *key);

/**
 * Remove a key/value set by the key.
 */
LIBGIFT_EXPORT
  void dataset_remove (Dataset *d, const void *key, size_t key_len);

/**
 * Wrapped around ::dataset_remove for string constants.
 */
LIBGIFT_EXPORT
  void dataset_removestr (Dataset *d, const char *key);

/**
 * Perform a remove operation from the original lookup node.  No extra lookup
 * will be performed.
 */
LIBGIFT_EXPORT
  void dataset_remove_node (Dataset *d, DatasetNode *node);

/*****************************************************************************/

/**
 * Base implementation for ::dataset_lookup and ::dataset_lookupstr.
 */
LIBGIFT_EXPORT
  ds_data_t *dataset_lookup_ex (Dataset *d, ds_data_t *key);

/**
 * Lookup a value by the set's key.  If you need to know the value_len,
 * consider using ::dataset_lookup_node.
 */
LIBGIFT_EXPORT
  void *dataset_lookup (Dataset *d, const void *key, size_t key_len);

/**
 * Wrapper around ::dataset_lookup for aggregate string constants.
 */
LIBGIFT_EXPORT
  void *dataset_lookupstr (Dataset *d, const char *key);

/**
 * Base implementation for ::dataset_lookup_node.
 */
LIBGIFT_EXPORT
  DatasetNode *dataset_lookup_node_ex (Dataset *d, ds_data_t *key);

/**
 * Lookup a complete set by the set's key.  Similar to ::dataset_lookup
 * except that the full set is returned.
 */
LIBGIFT_EXPORT
  DatasetNode *dataset_lookup_node (Dataset *d, const void *key,
                                    size_t key_len);

/*****************************************************************************/

/**
 * Iterate an entire dataset.  This is the simplest form and does not have
 * any kind of flow control of extended features.
 *
 * @note This iterator is optimized under the assumption that the dataset
 *       integrity will not be affected during iteration.  If you can not
 *       guarantee this, you must use ::dataset_foreach_ex.
 */
LIBGIFT_EXPORT
  void dataset_foreach (Dataset *d, DatasetForeachFn func, void *udata);

/**
 * Extended iterator for the dataset capable of simple flow control and
 * removal of entries as you pass over them.  See ::dataset_foreach for more
 * details.
 */
LIBGIFT_EXPORT
  void dataset_foreach_ex (Dataset *d, DatasetForeachExFn func, void *udata);

/*****************************************************************************/

/**
 * Locate a set value through iteration.  See ::dataset_find_node.
 *
 * @return Pointer to the value entry found.
 */
LIBGIFT_EXPORT
  void *dataset_find (Dataset *d, DatasetFindFn func, void *udata);

/**
 * Locate a dataset node through iteration.  DatasetForeach returns TRUE when
 * the set has been found.
 */
LIBGIFT_EXPORT
  DatasetNode *dataset_find_node (Dataset *d, DatasetFindFn func, void *udata);

/*****************************************************************************/

/**
 * Calculate the dataset length (total number of sets currently inserted).
 * Please note that the DATASET_LIST backend requires iteration to perform
 * this task and as such is considered expensive.
 *
 * @retval Number of data entries in the set.
 */
LIBGIFT_EXPORT
  unsigned int dataset_length (Dataset *d);

/*****************************************************************************/

/**
 * Flatten a dataset to a reallocated list.  The values are not copied.
 */
LIBGIFT_EXPORT
  List *dataset_flatten (Dataset *d);

/*****************************************************************************/

/**
 * Find a unique, non-zero 32-bit value that is not contained in the
 * dataset.  Assumes that the dataset contains integers.
 *
 * @param d
 * @param counter  Hint variable for fast lookup.
 *
 * @retval A unique, non-zero 32-bit integer that is not a key of the dataset.
 */
LIBGIFT_EXPORT
  uint32_t dataset_uniq32 (Dataset *d, uint32_t *counter);

/*****************************************************************************/

EXTERN_C_END

/*****************************************************************************/

#endif /* __DATASET_H */
