/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
** File:        prlayer.c
** Description: Routines for handling pushable protocol modules on sockets.
*/

#include "primpl.h"
#include "prerror.h"
#include "prmem.h"
#include "prlock.h"
#include "prlog.h"
#include "prio.h"

#include <string.h> /* for memset() */
static PRStatus _PR_DestroyIOLayer(PRFileDesc* stack);

void PR_CALLBACK pl_FDDestructor(PRFileDesc* fd) {
  PR_ASSERT(fd != NULL);
  if (NULL != fd->lower) {
    fd->lower->higher = fd->higher;
  }
  if (NULL != fd->higher) {
    fd->higher->lower = fd->lower;
  }
  PR_DELETE(fd);
}

/*
** Default methods that just call down to the next fd.
*/
static PRStatus PR_CALLBACK pl_TopClose(PRFileDesc* fd) {
  PRFileDesc *top, *lower;
  PRStatus rv;

  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);
  PR_ASSERT(fd->secret == NULL);
  PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED);

  if (PR_IO_LAYER_HEAD == fd->identity) {
    /*
     * new style stack; close all the layers, before deleting the
     * stack head
     */
    rv = fd->lower->methods->close(fd->lower);
    _PR_DestroyIOLayer(fd);
    return rv;
  }
  if ((fd->higher) && (PR_IO_LAYER_HEAD == fd->higher->identity)) {
    /*
     * lower layers of new style stack
     */
    lower = fd->lower;
    /*
     * pop and cleanup current layer
     */
    top = PR_PopIOLayer(fd->higher, PR_TOP_IO_LAYER);
    top->dtor(top);
    /*
     * then call lower layer
     */
    return (lower->methods->close(lower));
  } else {
    /* old style stack */
    top = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
    top->dtor(top);
    return (fd->methods->close)(fd);
  }
}

static PRInt32 PR_CALLBACK pl_DefRead(PRFileDesc* fd, void* buf,
                                      PRInt32 amount) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->read)(fd->lower, buf, amount);
}

static PRInt32 PR_CALLBACK pl_DefWrite(PRFileDesc* fd, const void* buf,
                                       PRInt32 amount) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->write)(fd->lower, buf, amount);
}

static PRInt32 PR_CALLBACK pl_DefAvailable(PRFileDesc* fd) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->available)(fd->lower);
}

static PRInt64 PR_CALLBACK pl_DefAvailable64(PRFileDesc* fd) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->available64)(fd->lower);
}

static PRStatus PR_CALLBACK pl_DefFsync(PRFileDesc* fd) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->fsync)(fd->lower);
}

static PRInt32 PR_CALLBACK pl_DefSeek(PRFileDesc* fd, PRInt32 offset,
                                      PRSeekWhence how) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->seek)(fd->lower, offset, how);
}

static PRInt64 PR_CALLBACK pl_DefSeek64(PRFileDesc* fd, PRInt64 offset,
                                        PRSeekWhence how) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->seek64)(fd->lower, offset, how);
}

static PRStatus PR_CALLBACK pl_DefFileInfo(PRFileDesc* fd, PRFileInfo* info) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->fileInfo)(fd->lower, info);
}

static PRStatus PR_CALLBACK pl_DefFileInfo64(PRFileDesc* fd,
                                             PRFileInfo64* info) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->fileInfo64)(fd->lower, info);
}

static PRInt32 PR_CALLBACK pl_DefWritev(PRFileDesc* fd, const PRIOVec* iov,
                                        PRInt32 size, PRIntervalTime timeout) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->writev)(fd->lower, iov, size, timeout);
}

static PRStatus PR_CALLBACK pl_DefConnect(PRFileDesc* fd, const PRNetAddr* addr,
                                          PRIntervalTime timeout) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->connect)(fd->lower, addr, timeout);
}

static PRStatus PR_CALLBACK pl_DefConnectcontinue(PRFileDesc* fd,
                                                  PRInt16 out_flags) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->connectcontinue)(fd->lower, out_flags);
}

static PRFileDesc* PR_CALLBACK pl_TopAccept(PRFileDesc* fd, PRNetAddr* addr,
                                            PRIntervalTime timeout) {
  PRStatus rv;
  PRFileDesc *newfd, *layer = fd;
  PRFileDesc* newstack;
  PRBool newstyle_stack = PR_FALSE;

  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  /* test for new style stack */
  while (NULL != layer->higher) {
    layer = layer->higher;
  }
  newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
  newstack = PR_NEW(PRFileDesc);
  if (NULL == newstack) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return NULL;
  }
  *newstack = *fd; /* make a copy of the accepting layer */

  newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout);
  if (NULL == newfd) {
    PR_DELETE(newstack);
    return NULL;
  }

  if (newstyle_stack) {
    newstack->lower = newfd;
    newfd->higher = newstack;
    return newstack;
  }
  /* this PR_PushIOLayer call cannot fail */
  rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack);
  PR_ASSERT(PR_SUCCESS == rv);
  return newfd; /* that's it */
}

static PRStatus PR_CALLBACK pl_DefBind(PRFileDesc* fd, const PRNetAddr* addr) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->bind)(fd->lower, addr);
}

static PRStatus PR_CALLBACK pl_DefListen(PRFileDesc* fd, PRIntn backlog) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->listen)(fd->lower, backlog);
}

static PRStatus PR_CALLBACK pl_DefShutdown(PRFileDesc* fd, PRIntn how) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->shutdown)(fd->lower, how);
}

static PRInt32 PR_CALLBACK pl_DefRecv(PRFileDesc* fd, void* buf, PRInt32 amount,
                                      PRIntn flags, PRIntervalTime timeout) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->recv)(fd->lower, buf, amount, flags, timeout);
}

static PRInt32 PR_CALLBACK pl_DefSend(PRFileDesc* fd, const void* buf,
                                      PRInt32 amount, PRIntn flags,
                                      PRIntervalTime timeout) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout);
}

static PRInt32 PR_CALLBACK pl_DefRecvfrom(PRFileDesc* fd, void* buf,
                                          PRInt32 amount, PRIntn flags,
                                          PRNetAddr* addr,
                                          PRIntervalTime timeout) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->recvfrom)(fd->lower, buf, amount, flags, addr,
                                        timeout);
}

static PRInt32 PR_CALLBACK pl_DefSendto(PRFileDesc* fd, const void* buf,
                                        PRInt32 amount, PRIntn flags,
                                        const PRNetAddr* addr,
                                        PRIntervalTime timeout) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->sendto)(fd->lower, buf, amount, flags, addr,
                                      timeout);
}

static PRInt16 PR_CALLBACK pl_DefPoll(PRFileDesc* fd, PRInt16 in_flags,
                                      PRInt16* out_flags) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags);
}

static PRInt32 PR_CALLBACK pl_DefAcceptread(PRFileDesc* sd, PRFileDesc** nd,
                                            PRNetAddr** raddr, void* buf,
                                            PRInt32 amount, PRIntervalTime t) {
  PRInt32 nbytes;
  PRStatus rv;
  PRFileDesc* newstack;
  PRFileDesc* layer = sd;
  PRBool newstyle_stack = PR_FALSE;

  PR_ASSERT(sd != NULL);
  PR_ASSERT(sd->lower != NULL);

  /* test for new style stack */
  while (NULL != layer->higher) {
    layer = layer->higher;
  }
  newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE;
  newstack = PR_NEW(PRFileDesc);
  if (NULL == newstack) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return -1;
  }
  *newstack = *sd; /* make a copy of the accepting layer */

  nbytes = sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, t);
  if (-1 == nbytes) {
    PR_DELETE(newstack);
    return nbytes;
  }
  if (newstyle_stack) {
    newstack->lower = *nd;
    (*nd)->higher = newstack;
    *nd = newstack;
    return nbytes;
  }
  /* this PR_PushIOLayer call cannot fail */
  rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack);
  PR_ASSERT(PR_SUCCESS == rv);
  return nbytes;
}

static PRInt32 PR_CALLBACK pl_DefTransmitfile(PRFileDesc* sd, PRFileDesc* fd,
                                              const void* headers, PRInt32 hlen,
                                              PRTransmitFileFlags flags,
                                              PRIntervalTime t) {
  PR_ASSERT(sd != NULL);
  PR_ASSERT(sd->lower != NULL);

  return sd->lower->methods->transmitfile(sd->lower, fd, headers, hlen, flags,
                                          t);
}

static PRStatus PR_CALLBACK pl_DefGetsockname(PRFileDesc* fd, PRNetAddr* addr) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->getsockname)(fd->lower, addr);
}

static PRStatus PR_CALLBACK pl_DefGetpeername(PRFileDesc* fd, PRNetAddr* addr) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->getpeername)(fd->lower, addr);
}

static PRStatus PR_CALLBACK pl_DefGetsocketoption(PRFileDesc* fd,
                                                  PRSocketOptionData* data) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->getsocketoption)(fd->lower, data);
}

static PRStatus PR_CALLBACK
pl_DefSetsocketoption(PRFileDesc* fd, const PRSocketOptionData* data) {
  PR_ASSERT(fd != NULL);
  PR_ASSERT(fd->lower != NULL);

  return (fd->lower->methods->setsocketoption)(fd->lower, data);
}

static PRInt32 PR_CALLBACK pl_DefSendfile(PRFileDesc* sd, PRSendFileData* sfd,
                                          PRTransmitFileFlags flags,
                                          PRIntervalTime timeout) {
  PR_ASSERT(sd != NULL);
  PR_ASSERT(sd->lower != NULL);

  return sd->lower->methods->sendfile(sd->lower, sfd, flags, timeout);
}

/* Methods for the top of the stack.  Just call down to the next fd. */
static PRIOMethods pl_methods = {PR_DESC_LAYERED,
                                 pl_TopClose,
                                 pl_DefRead,
                                 pl_DefWrite,
                                 pl_DefAvailable,
                                 pl_DefAvailable64,
                                 pl_DefFsync,
                                 pl_DefSeek,
                                 pl_DefSeek64,
                                 pl_DefFileInfo,
                                 pl_DefFileInfo64,
                                 pl_DefWritev,
                                 pl_DefConnect,
                                 pl_TopAccept,
                                 pl_DefBind,
                                 pl_DefListen,
                                 pl_DefShutdown,
                                 pl_DefRecv,
                                 pl_DefSend,
                                 pl_DefRecvfrom,
                                 pl_DefSendto,
                                 pl_DefPoll,
                                 pl_DefAcceptread,
                                 pl_DefTransmitfile,
                                 pl_DefGetsockname,
                                 pl_DefGetpeername,
                                 (PRReservedFN)_PR_InvalidInt,
                                 (PRReservedFN)_PR_InvalidInt,
                                 pl_DefGetsocketoption,
                                 pl_DefSetsocketoption,
                                 pl_DefSendfile,
                                 pl_DefConnectcontinue,
                                 (PRReservedFN)_PR_InvalidInt,
                                 (PRReservedFN)_PR_InvalidInt,
                                 (PRReservedFN)_PR_InvalidInt,
                                 (PRReservedFN)_PR_InvalidInt};

PR_IMPLEMENT(const PRIOMethods*) PR_GetDefaultIOMethods(void) {
  return &pl_methods;
} /* PR_GetDefaultIOMethods */

PR_IMPLEMENT(PRFileDesc*)
PR_CreateIOLayerStub(PRDescIdentity ident, const PRIOMethods* methods) {
  PRFileDesc* fd = NULL;
  PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident));
  if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident)) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  } else {
    fd = PR_NEWZAP(PRFileDesc);
    if (NULL == fd) {
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    } else {
      fd->methods = methods;
      fd->dtor = pl_FDDestructor;
      fd->identity = ident;
    }
  }
  return fd;
} /* PR_CreateIOLayerStub */

/*
 * PR_CreateIOLayer
 *      Create a new style stack, where the stack top is a dummy header.
 *      Unlike the old style stacks, the contents of the stack head
 *      are not modified when a layer is pushed onto or popped from a new
 *      style stack.
 */

PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayer(PRFileDesc* top) {
  PRFileDesc* fd = NULL;

  fd = PR_NEWZAP(PRFileDesc);
  if (NULL == fd) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
  } else {
    fd->methods = &pl_methods;
    fd->dtor = pl_FDDestructor;
    fd->identity = PR_IO_LAYER_HEAD;
    fd->higher = NULL;
    fd->lower = top;
    top->higher = fd;
    top->lower = NULL;
  }
  return fd;
} /* PR_CreateIOLayer */

/*
 * _PR_DestroyIOLayer
 *      Delete the stack head of a new style stack.
 */

static PRStatus _PR_DestroyIOLayer(PRFileDesc* stack) {
  if (NULL == stack) {
    return PR_FAILURE;
  }

  PR_DELETE(stack);
  return PR_SUCCESS;
} /* _PR_DestroyIOLayer */

PR_IMPLEMENT(PRStatus)
PR_PushIOLayer(PRFileDesc* stack, PRDescIdentity id, PRFileDesc* fd) {
  PRFileDesc* insert = PR_GetIdentitiesLayer(stack, id);

  PR_ASSERT(fd != NULL);
  PR_ASSERT(stack != NULL);
  PR_ASSERT(insert != NULL);
  PR_ASSERT(PR_IO_LAYER_HEAD != id);
  if ((NULL == stack) || (NULL == fd) || (NULL == insert)) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    return PR_FAILURE;
  }

  if (stack == insert) {
    /* going on top of the stack */
    /* old-style stack */
    PRFileDesc copy = *stack;
    *stack = *fd;
    *fd = copy;
    fd->higher = stack;
    if (fd->lower) {
      PR_ASSERT(fd->lower->higher == stack);
      fd->lower->higher = fd;
    }
    stack->lower = fd;
    stack->higher = NULL;
  } else {
    /*
     * going somewhere in the middle of the stack for both old and new
     * style stacks, or going on top of stack for new style stack
     */
    fd->lower = insert;
    fd->higher = insert->higher;

    insert->higher->lower = fd;
    insert->higher = fd;
  }

  return PR_SUCCESS;
}

PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc* stack, PRDescIdentity id) {
  PRFileDesc* extract = PR_GetIdentitiesLayer(stack, id);

  PR_ASSERT(0 != id);
  PR_ASSERT(NULL != stack);
  PR_ASSERT(NULL != extract);
  if ((NULL == stack) || (0 == id) || (NULL == extract)) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    return NULL;
  }

  if (extract == stack) {
    /* popping top layer of the stack */
    /* old style stack */
    PRFileDesc copy = *stack;
    extract = stack->lower;
    *stack = *extract;
    *extract = copy;
    stack->higher = NULL;
    if (stack->lower) {
      PR_ASSERT(stack->lower->higher == extract);
      stack->lower->higher = stack;
    }
  } else if ((PR_IO_LAYER_HEAD == stack->identity) &&
             (extract == stack->lower) && (extract->lower == NULL)) {
    /*
     * new style stack
     * popping the only layer in the stack; delete the stack too
     */
    stack->lower = NULL;
    _PR_DestroyIOLayer(stack);
  } else {
    /* for both kinds of stacks */
    extract->lower->higher = extract->higher;
    extract->higher->lower = extract->lower;
  }
  extract->higher = extract->lower = NULL;
  return extract;
} /* PR_PopIOLayer */

#define ID_CACHE_INCREMENT 16
typedef struct _PRIdentity_cache {
  PRLock* ml;
  char** name;
  PRIntn length;
  PRDescIdentity ident;
} _PRIdentity_cache;

static _PRIdentity_cache identity_cache;

PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char* layer_name) {
  PRDescIdentity identity, length;
  char **names = NULL, *name = NULL, **old = NULL;

  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  PR_ASSERT((PRDescIdentity)((1UL << ((sizeof(PRDescIdentity) * 8) - 1)) - 1)
            > identity_cache.ident);

  if (NULL != layer_name) {
    name = (char*)PR_Malloc(strlen(layer_name) + 1);
    if (NULL == name) {
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
      return PR_INVALID_IO_LAYER;
    }
    strcpy(name, layer_name);
  }

  /* this initial code runs unsafe */
retry:
  PR_ASSERT(NULL == names);
  /*
   * In the initial round, both identity_cache.ident and
   * identity_cache.length are 0, so (identity_cache.ident + 1) is greater
   * than length.  In later rounds, identity_cache.ident is always less
   * than length, so (identity_cache.ident + 1) can be equal to but cannot
   * be greater than length.
   */
  length = identity_cache.length;
  if ((identity_cache.ident + 1) >= length) {
    length += ID_CACHE_INCREMENT;
    names = (char**)PR_CALLOC(length * sizeof(char*));
    if (NULL == names) {
      if (NULL != name) {
        PR_DELETE(name);
      }
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
      return PR_INVALID_IO_LAYER;
    }
  }

  /* now we get serious about thread safety */
  PR_Lock(identity_cache.ml);
  PR_ASSERT(identity_cache.length == 0 ||
            identity_cache.ident < identity_cache.length);
  identity = identity_cache.ident + 1;
  if (identity >= identity_cache.length) /* there's no room */
  {
    /* we have to do something - hopefully it's already done */
    if ((NULL != names) && (identity < length)) {
      /* what we did is still okay */
      if (identity_cache.length != 0) {
        memcpy(names, identity_cache.name,
               identity_cache.length * sizeof(char*));
      }
      old = identity_cache.name;
      identity_cache.name = names;
      identity_cache.length = length;
      names = NULL;
    } else {
      PR_Unlock(identity_cache.ml);
      if (NULL != names) {
        PR_DELETE(names);
      }
      goto retry;
    }
  }
  if (NULL != name) /* there's a name to be stored */
  {
    identity_cache.name[identity] = name;
  }
  identity_cache.ident = identity;
  PR_ASSERT(identity_cache.ident < identity_cache.length);
  PR_Unlock(identity_cache.ml);

  if (NULL != old) {
    PR_DELETE(old);
  }
  if (NULL != names) {
    PR_DELETE(names);
  }

  return identity;
} /* PR_GetUniqueIdentity */

PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident) {
  const char* rv = NULL;
  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  if ((PR_TOP_IO_LAYER != ident) && (ident >= 0)) {
    PR_Lock(identity_cache.ml);
    PR_ASSERT(ident <= identity_cache.ident);
    rv = (ident > identity_cache.ident) ? NULL : identity_cache.name[ident];
    PR_Unlock(identity_cache.ml);
  }

  return rv;
} /* PR_GetNameForIdentity */

PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd) {
  PR_ASSERT(NULL != fd);
  if (PR_IO_LAYER_HEAD == fd->identity) {
    PR_ASSERT(NULL != fd->lower);
    return fd->lower->identity;
  }
  return fd->identity;
} /* PR_GetLayersIdentity */

PR_IMPLEMENT(PRFileDesc*)
PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id) {
  PRFileDesc* layer = fd;

  if (PR_TOP_IO_LAYER == id) {
    if (PR_IO_LAYER_HEAD == fd->identity) {
      return fd->lower;
    }
    return fd;
  }

  for (layer = fd; layer != NULL; layer = layer->lower) {
    if (id == layer->identity) {
      return layer;
    }
  }
  for (layer = fd; layer != NULL; layer = layer->higher) {
    if (id == layer->identity) {
      return layer;
    }
  }
  return NULL;
} /* PR_GetIdentitiesLayer */

void _PR_InitLayerCache(void) {
  memset(&identity_cache, 0, sizeof(identity_cache));
  identity_cache.ml = PR_NewLock();
  PR_ASSERT(NULL != identity_cache.ml);
} /* _PR_InitLayerCache */

void _PR_CleanupLayerCache(void) {
  if (identity_cache.ml) {
    PR_DestroyLock(identity_cache.ml);
    identity_cache.ml = NULL;
  }

  if (identity_cache.name) {
    PRDescIdentity ident;

    for (ident = 0; ident <= identity_cache.ident; ident++) {
      PR_DELETE(identity_cache.name[ident]);
    }

    PR_DELETE(identity_cache.name);
  }
} /* _PR_CleanupLayerCache */

/* prlayer.c */
