/*

  sshadt_i.h

  Author: Antti Huima <huima@ssh.fi>

  Copyright (c) 1999 SSH Communications Security, Finland
  All rights reserved.

  Created Wed Sep  8 17:42:29 1999.

  */

#ifndef SSHADT_I_H_INCLUDED
#define SSHADT_I_H_INCLUDED

/* Object layout.

   1. External object without inlined header.
                
                 +------+
                 |Object| ---------+
                 +------+   |   +--V-----+
   HANDLE ======>|Node  |   |   |Object  | <===== OBJECT
                 +------+   |   +--------+
                            |
                            |----> Visible to application

   Node is allocated by the library, object is given from the outside.
   Handles are pointers to the node data part.

   When this layout is used, the object pointer must be the FIRST
   FIELD in the node.

   2. External object with an inlined header.

   +--------+
   |Object  | <====== OBJECT
   ++-------+
   ||Header | <====== HANDLE
   ++-------+
   |        |
   +--------+

   The whole object is given from the outside. Handles are pointers to
   the internal headers.

   3. Internally allocated object without inlined header.

   +---------+
   |Header   | <====== HANDLE
   +---------+ -----------------------
   |Object   | <====== OBJECT        | Visible to application
   |         |                       |
   +---------+

   The whole object is allocated by the library. Handles are pointers
   to the headers.

   4. Internally allocated object with inlined header.

   +--------+ -----------------------
   |Object  | <====== OBJECT        | Visible to application
   ++-------+                       |
   ||Header | <====== HANDLE
   ++-------+
   |        |
   +--------+

*/
   

#include "sshadt.h"
#include "sshfastalloc.h"

#define SSH_ADT_FLAG_REFCOUNT           0x0001
#define SSH_ADT_FLAG_ALLOCATE           0x0002
#define SSH_ADT_FLAG_CONTAINED_HEADER   0x0004
#define SSH_ADT_FLAG_NEED_EXTRA_NODES   0x0008
#define SSH_ADT_FLAG_PROXY_CONTAINER    0x0010
#define SSH_ADT_FLAG_FASTALLOC          0x0020

/* Hooks. */

typedef void (* SshADTHookFunc)(SshADTHandle handle,
                                void *context);

typedef void (* SshADTReallocHookFunc)(SshADTHandle old_handle,
                                       SshADTHandle new_handle,
                                       void *context);

typedef void (* SshADTDestroyHookFunc)(void *context);

struct ssh_adt_hooks {
  SshADTHookFunc insert, detach, map, unmap;
  void *insert_ctx, *detach_ctx, *map_ctx, *unmap_ctx;

  SshADTReallocHookFunc reallocated;
  void *reallocated_ctx;

  SshADTDestroyHookFunc destroy;
  void *destroy_ctx;
};

typedef struct ssh_adt_hooks SshADTHooks;

#define SSH_ADT_ARGS1(x) , x
#define SSH_ADT_ARGS2(x, y) , x, y
#define SSH_ADT_ARGS0() 

#define SSH_ADT_CALL_HOOK_GENERIC(container, hook_name, args)   \
do                                                              \
{                                                               \
  SshADTHooks *__h = container->hooks;                          \
  if (__h == NULL) break;                                       \
  if (__h->hook_name == NULL) break;                            \
                                                                \
  (*(__h->hook_name)) args;                                     \
}                                                               \
while(0)

#define SSH_ADT_CALL_HOOK_0(container, hook_name) \
SSH_ADT_CALL_HOOK_GENERIC(container, hook_name, (__h->hook_name ## _ctx))

#define SSH_ADT_CALL_HOOK_1(container, hook_name, a) \
SSH_ADT_CALL_HOOK_GENERIC(container, hook_name, (a, __h->hook_name ## _ctx))

#define SSH_ADT_CALL_HOOK_2(container, hook_name, a, b) \
SSH_ADT_CALL_HOOK_GENERIC(container, hook_name, (a, b, __h->hook_name ## _ctx))

#define SSH_ADT_CALL_REALLOC_HOOK(c,h,a,b) SSH_ADT_CALL_HOOK_2(c,h,a,b)
#define SSH_ADT_CALL_HOOK(c,h,a) SSH_ADT_CALL_HOOK_1(c,h,a)
#define SSH_ADT_CALL_DESTROY_HOOK(c,h) SSH_ADT_CALL_HOOK_0(c,h)

void ssh_adt_initialize_hooks(SshADTContainer container);
void ssh_adt_uninitialize_hooks(SshADTContainer container);

/* Convenience macros. */
#define ssh_adt_stdf    u.nonproxy.f
#define ssh_adt_appmeth u.nonproxy.f.app_methods
#define ssh_adt_proxy   u.proxy
#define ssh_adt_aux     u.nonproxy.aux

typedef struct {
  void *object;
} SshADTProtoNode;

typedef struct {
  union {
    SshUInt64 a;                /* To circumvent alignment problems. */
    size_t b;
  } u;
} SshADTSizeField;

typedef struct ssh_adt_container_pars {
  SshADTContainerType type;
  SshUInt32 flags;
  SshADTStandardFields f;
  SshADTContainer ext;
} SshADTContainerPars;

typedef struct {
  SshADTContainerPars top, domain, range;
} SshADTParameters;

/* Type checking and other similar macros. */
#ifdef DEBUG_LIGHT
#define ssh_adt_type_assert(c, t) SSH_ASSERT(c->type == t)
#define ssh_adt_no_special_assert(h) SSH_ASSERT((void *)h > (void *)3)
#else
#define ssh_adt_type_assert(c, t)
#define ssh_adt_no_special_assert(h)
#endif

#define SSH_ADT_IS_INDEX(location) ((location) >= 0)
#define SSH_ADT_GET_INDEX(location) ((unsigned int)(location))

/* Generic single-linked list ops for internal implementation. */
void ssh_adt_genlist_add(void **listptr, void *object, int next_ptr_offset);
void ssh_adt_genlist_remove(void **listptr, void *object, int next_ptr_offset);

#define SSH_ADT_GENLIST_ADD(object_type, next_field, object, list_ptr)  \
do                                                                      \
{                                                                       \
  ssh_adt_genlist_add(list_ptr, object,                                 \
                      SSH_ADT_OFFSET_OF(object_type, next_field));      \
}                                                                       \
while(0)

#define SSH_ADT_GENLIST_REMOVE(object_type, next_field, object, list_ptr)     \
do                                                                            \
{                                                                             \
  ssh_adt_genlist_remove(list_ptr, object,                                    \
                         SSH_ADT_OFFSET_OF(object_type, next_field));         \
}                                                                             \
while(0)

#define SSH_ADT_GENLIST_MOVE(object_type, next_field, object, from, to) \
do                                                                      \
{                                                                       \
  ssh_adt_genlist_add(list_ptr, object,                                 \
                      SSH_ADT_OFFSET_OF(object_type, next_field));      \
  ssh_adt_genlist_remove(list_ptr, object,                              \
                         SSH_ADT_OFFSET_OF(object_type, next_field));   \
}                                                                       \
while(0)

#if 0
#define SSH_ADT_RETURN_IFPROXY(c, mandatory, func, args)        \
do                                                              \
{                                                               \
  if (c->flags & SSH_ADT_FLAG_PROXY_CONTAINER)                  \
    {                                                           \
      SSH_ADT_CALL(c, mandatory, func, args);                   \
      return;                                                   \
    }                                                           \
}                                                               \
while(0)

#define SSH_ADT_RETURN_VALUE_IFPROXY(c, m, func, args, type, d) \
do                                                              \
{                                                               \
  if (c->flags & SSH_ADT_FLAG_PROXY_CONTAINER)                  \
    {                                                           \
      type __temp;                                              \
      SSH_ADT_CALL_VALUE(c, m, func, args, __temp, d);          \
      return __temp;                                            \
    }                                                           \
}                                                               \
while(0)
#endif

#define SSH_ADT_CALL_APP(container, method, args)       \
do                                                      \
{                                                       \
  if (container->ssh_adt_appmeth.method != NULL)        \
    {                                                   \
      (*(container->ssh_adt_appmeth.method))args;       \
    }                                                   \
}                                                       \
while(0)

#define SSH_ADT_CALL_APP_MANDATORY(container, method, args)     \
 (*(container->ssh_adt_appmeth.method))args

#define SSH_ADT_APPCTX(container) \
  (container->ssh_adt_appmeth.context)

#define SSH_ADT_OBJECT_AT_NODE(n) \
  (*((void **)(((unsigned char *)n) - sizeof(void *))))

/* These do not work if external void ptr nodes are used! */
#define SSH_ADT_HANDLE_FROM_OBJECT(c, o)                                      \
((SshADTHandle)(((unsigned char *) o) + ((c)->ssh_adt_stdf.header_offset)))

#define SSH_ADT_OBJECT_FROM_HANDLE(c, h)                                      \
((void *)(((unsigned char *) h) - ((c)->ssh_adt_stdf.header_offset)))

#define SSH_ADT_HASH_OBJECT(c, obj)     \
SSH_ADT_CALL_APP_MANDATORY(c, hash, (obj, SSH_ADT_APPCTX(c)))

/* And then for something different, namely the registration of
   handles can be used internally for implementing e.g. foreach. */

typedef struct ssh_adt_handle_list {
  struct ssh_adt_handle_list *next;
  SshADTHandle *ptr;  
} SshADTHandleList;

#define SSH_ADT_ADD_TO_HANDLE_LIST(listptr, handleptr)          \
do                                                              \
{                                                               \
  SshADTHandleList *__new;                                      \
  __new = ssh_xmalloc(sizeof(*__new));                          \
  new->ptr = handleptr;                                         \
  SSH_ADT_GENLIST_ADD(SshADTHandleList, next, __new, listptr);  \
}                                                               \
while(0)

#define SSH_ADT_ADD_MERGE_HANDLE_LISTS(oldlistptr, newlistptr)  \
do                                                              \
{                                                               \
  XXX...                                                        \
}                                                               \
while(0)

#define SSH_ADT_PROTECT(block) do { block } while(0)

/* Some functions. */

void *ssh_adt_get_object_from_handle(SshADTContainer container,
                                     SshADTHandle handle);

SshADTHandle ssh_adt_get_handle_from_object(SshADTContainer container,
                                            void *object);

size_t ssh_adt_default_size(SshADTContainer container);

SshADTHandle ssh_adt_make_handle(SshADTContainer container,
                                 void *object);

SshADTHandle ssh_adt_make_handle_for_allocated(SshADTContainer container,
                                              void *object);

void ssh_adt_free_i(SshADTContainer container, SshADTHandle handle);

void *ssh_adt_alloc_i(SshADTContainer container, size_t size);

#endif /* SSHADT_I_H_INCLUDED */
