/*

  sshadt_std_i.h

  Author: Antti Huima <huima@ssh.fi>

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

  Created Tue Oct 19 16:02:07 1999.

  */

#ifndef SSHADT_STD_I_H_INCLUDED
#define SSHADT_STD_I_H_INCLUDED

#define SSH_ADT_STD_COMPARE(container, h1, h2, result)                        \
do                                                                            \
{                                                                             \
  void *o1, *o2;                                                              \
  SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(container, (SshADTHandle)h1, o1);        \
  SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(container, (SshADTHandle)h2, o2);        \
  result = SSH_ADT_CALL_APP_MANDATORY(container, compare,                     \
                                      (o1, o2, SSH_ADT_APPCTX(container)));   \
}                                                                             \
while(0)

#define SSH_ADT_STD_COMPARE_H_O(container, h1, o2, result)                    \
do                                                                            \
{                                                                             \
  void *o1;                                                                   \
  SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(container, (SshADTHandle)h1, o1);        \
  result = SSH_ADT_CALL_APP_MANDATORY(container, compare,                     \
                                      (o1, o2, SSH_ADT_APPCTX(container)));   \
}                                                                             \
while(0)

#define SSH_ADT_STD_HASH(container, h1, result)                               \
do                                                                            \
{                                                                             \
  void *o1;                                                                   \
  SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(container, (SshADTHandle)h1, o1);        \
  result = SSH_ADT_CALL_APP_MANDATORY(container, hash,                        \
                                      (o1, SSH_ADT_APPCTX(container)));       \
}                                                                             \
while(0)

#define SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(container, handle, result)         \
do                                                                            \
{                                                                             \
  if ((container)->flags & SSH_ADT_FLAG_NEED_EXTRA_NODES)                     \
    {                                                                         \
      unsigned char *__ptr = ((SshADTHandle)(handle));                        \
      __ptr -= sizeof(void *);                                                \
      result = *((void **)__ptr);                                             \
    }                                                                         \
  else                                                                        \
    {                                                                         \
      result = ((unsigned char *)((SshADTHandle)(handle))) -                  \
        (container)->ssh_adt_stdf.header_offset;                              \
    }                                                                         \
}                                                                             \
while(0)

#define SSH_ADT_STD_FREE_I(container, handle)                                 \
do                                                                            \
{                                                                             \
  if ((container)->flags & SSH_ADT_FLAG_CONTAINED_HEADER)                     \
    {                                                                         \
      unsigned char *__ptr;                                                   \
      SSH_ADT_STD_GET_OBJECT_FROM_HANDLE((container), (handle), __ptr);       \
      ssh_xfree(__ptr);                                                       \
    }                                                                         \
  else                                                                        \
    {                                                                         \
      ssh_xfree(handle);                                                      \
    }                                                                         \
}                                                                             \
while(0)

#define SSH_ADT_STD_ALLOC_I(container, size, result)                          \
do                                                                            \
{                                                                             \
  unsigned char *__ptr;                                                       \
  if (!((container)->flags & SSH_ADT_FLAG_CONTAINED_HEADER))                  \
    {                                                                         \
      __ptr = ssh_xmalloc((size) +                                            \
                          (container)->static_data->internal_header_size);    \
      result = __ptr - (container)->ssh_adt_stdf.header_offset;               \
    }                                                                         \
  else                                                                        \
    {                                                                         \
      result = ssh_xmalloc(size);                                             \
    }                                                                         \
}                                                                             \
while(0)

#define SSH_ADT_STD_MAKE_HANDLE(container, object, block, result)             \
do                                                                            \
{                                                                             \
  if ((container)->flags & SSH_ADT_FLAG_NEED_EXTRA_NODES)                     \
    {                                                                         \
      SshADTHandle __handle;                                                  \
      SSH_ADT_PROTECT(block);                                                 \
      *(((void **)__handle)) = object;                                        \
      result = (unsigned char *)__handle + sizeof(void *);                    \
    }                                                                         \
  else                                                                        \
    {                                                                         \
      result = SSH_ADT_HANDLE_FROM_OBJECT((container), (object));             \
    }                                                                         \
}                                                                             \
while(0)

/******************************************************** Function skeletons */

#define SSH_ADT_STD_INIT(name, block)                                         \
static void name(SshADTContainer c, SshADTContainerPars *pars)                \
{                                                                             \
  c->static_data = pars->type;                                                \
  c->flags       = pars->flags;                                               \
  c->hooks       = NULL;                                                      \
                                                                              \
  memcpy(&(c->ssh_adt_stdf), &(pars->f), sizeof(c->ssh_adt_stdf));            \
                                                                              \
  if (!(c->flags & (SSH_ADT_FLAG_ALLOCATE                                     \
                    | SSH_ADT_FLAG_CONTAINED_HEADER)))                        \
    c->flags |= SSH_ADT_FLAG_NEED_EXTRA_NODES;                                \
                                                                              \
  if ((c->flags & SSH_ADT_FLAG_ALLOCATE) &&                                   \
      !(c->flags & SSH_ADT_FLAG_CONTAINED_HEADER))                            \
    {                                                                         \
      c->ssh_adt_stdf.header_offset =                                         \
        -((SshInt32)(c->static_data->internal_header_size));                  \
    }                                                                         \
                                                                              \
  c->ssh_adt_aux.num_objects = 0;                                             \
  SSH_ADT_PROTECT(block);                                                     \
}

#define SSH_ADT_STD_DESTROY(name, block)                                      \
static void name(SshADTContainer c)                                           \
{                                                                             \
  SSH_ADT_CALL_DESTROY_HOOK(c, destr);                                        \
  SSH_ADT_PROTECT(block);                                                     \
  ssh_xfree(c->hooks);                                                        \
}

#define SSH_ADT_STD_INSERT_AT(name, block, make_node_block)                   \
static SshADTHandle name(SshADTContainer c,                                   \
                         SshADTRelativeLocation location,                     \
                         SshADTHandle handle,                                 \
                         void *object)                                        \
{                                                                             \
  SshADTHandle h;                                                             \
  SSH_ADT_STD_MAKE_HANDLE(c, object, make_node_block, h);                     \
  SSH_ADT_PROTECT(block);                                                     \
  c->ssh_adt_aux.num_objects++;                                               \
  SSH_ADT_CALL_HOOK(c, insert, h);                                            \
  return h;                                                                   \
}

#define SSH_ADT_STD_INSERT_TO(name, block, make_node_block)                   \
static SshADTHandle name(SshADTContainer c,                                   \
                         SshADTAbsoluteLocation location,                     \
                         void *object)                                        \
{                                                                             \
  SshADTHandle h;                                                             \
  SSH_ADT_STD_MAKE_HANDLE(c, object, make_node_block, h);                     \
  SSH_ADT_PROTECT(block);                                                     \
  c->ssh_adt_aux.num_objects++;                                               \
  SSH_ADT_CALL_HOOK(c, insert, h);                                            \
  return h;                                                                   \
}

#define SSH_ADT_STD_ALLOC_N_AT(name, block)                                   \
static SshADTHandle name(SshADTContainer c,                                   \
                         SshADTRelativeLocation location,                     \
                         SshADTHandle handle,                                 \
                         size_t size)                                         \
{                                                                             \
  SshADTHandle h;                                                             \
  void *newp;                                                                  \
                                                                              \
  SSH_ADT_STD_ALLOC_I(c, size, newp);                                          \
  SSH_ADT_CALL_APP(c, init, (newp, size, SSH_ADT_APPCTX(c)));                  \
  h = SSH_ADT_HANDLE_FROM_OBJECT(c, newp);                                     \
  SSH_ADT_PROTECT(block);                                                     \
  c->ssh_adt_aux.num_objects++;                                               \
  SSH_ADT_CALL_HOOK(c, insert, h);                                            \
  return h;                                                                   \
}

#define SSH_ADT_STD_ALLOC_N_TO(name, block)                                   \
static SshADTHandle name(SshADTContainer c,                                   \
                  SshADTAbsoluteLocation location,                            \
                  size_t size)                                                \
{                                                                             \
  SshADTHandle h;                                                             \
  void *newp;                                                                  \
                                                                              \
  SSH_ADT_STD_ALLOC_I(c, size, newp);                                          \
  SSH_ADT_CALL_APP(c, init, (newp, size, SSH_ADT_APPCTX(c)));                  \
  h = SSH_ADT_HANDLE_FROM_OBJECT(c, newp);                                     \
  SSH_ADT_PROTECT(block);                                                     \
  c->ssh_adt_aux.num_objects++;                                               \
  SSH_ADT_CALL_HOOK(c, insert, h);                                            \
  return h;                                                                   \
}

#define SSH_ADT_STD_PUT_N_AT(name, block)                                     \
static SshADTHandle name(SshADTContainer c,                                   \
                         SshADTRelativeLocation location,                     \
                         SshADTHandle handle,                                 \
                         size_t size,                                         \
                         void *object)                                        \
{                                                                             \
  SshADTHandle h;                                                             \
  void *newp;                                                                  \
                                                                              \
  SSH_ADT_STD_ALLOC_I(c, size, newp);                                          \
  SSH_ADT_CALL_APP(c, copy, (newp, size, object,                               \
                             SSH_ADT_APPCTX(c)));                             \
  h = SSH_ADT_HANDLE_FROM_OBJECT(c, newp);                                     \
  SSH_ADT_PROTECT(block);                                                     \
  c->ssh_adt_aux.num_objects++;                                               \
  SSH_ADT_CALL_HOOK(c, insert, h);                                            \
  return h;                                                                   \
}

#define SSH_ADT_STD_PUT_N_TO(name, block)                                     \
static SshADTHandle name(SshADTContainer c,                                   \
                         SshADTAbsoluteLocation location,                     \
                         size_t size,                                         \
                         void *object)                                        \
{                                                                             \
  SshADTHandle h;                                                             \
  void *newp;                                                                  \
                                                                              \
  SSH_ADT_STD_ALLOC_I(c, size, newp);                                          \
  SSH_ADT_CALL_APP(c, copy, (newp, size, object,                               \
                                     SSH_ADT_APPCTX(c)));                     \
  h = SSH_ADT_HANDLE_FROM_OBJECT(c, newp);                                     \
  SSH_ADT_PROTECT(block);                                                     \
  c->ssh_adt_aux.num_objects++;                                               \
  SSH_ADT_CALL_HOOK(c, insert, h);                                            \
  return h;                                                                   \
}

#define SSH_ADT_STD_GET(name)                                                 \
static void *name(SshADTContainer c, SshADTHandle handle)                     \
{                                                                             \
  void *ptr;                                                                  \
  SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(c, handle, ptr);                         \
  return ptr;                                                                 \
}

#define SSH_ADT_STD_NUM_OBJECTS(name)                                         \
static size_t name(SshADTContainer c)                                         \
{                                                                             \
  return c->ssh_adt_aux.num_objects;                                          \
}

#define SSH_ADT_STD_GET_HANDLE_TO(name, block)                                \
static SshADTHandle name(SshADTContainer c,                                   \
                         void *object)                                        \
{                                                                             \
  if (c->flags & SSH_ADT_FLAG_NEED_EXTRA_NODES)                               \
    {                                                                         \
      SshADTHandle handle;                                                    \
      SSH_ADT_PROTECT(block);                                                 \
      return handle;                                                          \
    }                                                                         \
  return SSH_ADT_HANDLE_FROM_OBJECT(c, object);                               \
}

#define SSH_ADT_STD_REALLOC(name, block1, block2)                             \
static void *name(SshADTContainer c, void *object, size_t new_size)           \
{                                                                             \
  unsigned char *ptr;                                                         \
  SshADTHandle oldh, newh;                                                    \
                                                                              \
  oldh = SSH_ADT_HANDLE_FROM_OBJECT(c, object);                               \
  SSH_ADT_PROTECT(block1);                                                    \
  if (c->flags & SSH_ADT_FLAG_CONTAINED_HEADER)                               \
    {                                                                         \
      ptr = ssh_xrealloc(object, new_size);                                   \
      newh = SSH_ADT_HANDLE_FROM_OBJECT(c, (void *)ptr);                      \
    }                                                                         \
  else                                                                        \
    {                                                                         \
      ptr = object;                                                           \
      ptr -= c->static_data->internal_header_size;                            \
      ptr = ssh_xrealloc(ptr, new_size +                                      \
                         c->static_data->internal_header_size);               \
      newh = ptr;                                                             \
    }                                                                         \
  SSH_ADT_PROTECT(block2);                                                    \
  SSH_ADT_CALL_REALLOC_HOOK(c, reallocated, oldh, newh);                      \
  return ptr;                                                                 \
}

#define SSH_ADT_STD_DETACH(name, block, destroy_node_block)                   \
static void *name(SshADTContainer c, SshADTHandle handle)                     \
{                                                                             \
  unsigned char *object;                                                      \
                                                                              \
  SSH_ADT_CALL_HOOK(c, detach, handle);                                       \
  SSH_DEBUG(5, ("Detach: handle=%p", handle));                                \
                                                                              \
  SSH_ADT_STD_GET_OBJECT_FROM_HANDLE(c, handle, object);                      \
  SSH_ADT_PROTECT(block);                                                     \
                                                                              \
  if (c->flags & SSH_ADT_FLAG_NEED_EXTRA_NODES)                               \
    {                                                                         \
      void *node = ((unsigned char *)handle) - sizeof(void *);                \
      SSH_ADT_PROTECT(destroy_node_block);                                    \
    }                                                                         \
                                                                              \
  c->ssh_adt_aux.num_objects--;                                               \
  return object;                                                              \
}

#define SSH_ADT_STD_DELETE(name)                                              \
static void name(SshADTContainer container, SshADTHandle handle)              \
{                                                                             \
  void *object;                                                               \
                                                                              \
  object = ssh_adt_detach_i(container, handle);                               \
  SSH_ADT_CALL_APP(container, destr, (object, SSH_ADT_APPCTX(container)));    \
  if (container->flags & SSH_ADT_FLAG_ALLOCATE)                               \
    {                                                                         \
      SSH_ADT_STD_FREE_I(container, handle);                                  \
    }                                                                         \
}

#define SSH_ADT_STD_NOTHING do { } while(0);

#endif /* SSHADT_STD_I_H_INCLUDED */
