/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "nslayout.h"
#include "nsDST.h"
#include "nsISupportsUtils.h"
#ifdef NS_DEBUG
#include <string.h>
#endif

// Constructors
inline nsDST::LeafNode::LeafNode(void* aKey, void* aValue)
  : mKey(aKey), mValue(aValue)
{
}
  
inline nsDST::LeafNode::LeafNode(const LeafNode& aNode)
  : mKey(aNode.Key()),  // don't assume it's a leaf node
    mValue(aNode.mValue)
{
}

// Constructor
inline nsDST::TwoNode::TwoNode(const LeafNode& aLeafNode)
  : nsDST::LeafNode((void*)(PtrBits(aLeafNode.mKey) | 0x1), aLeafNode.mValue),
    mLeft(0), mRight(0)
{
}

// If you know that the node is a leaf node, then you can quickly access
// the key without clearing the bit that indicates whether it's a leaf or
// a two node
#define DST_GET_LEAF_KEY(leaf)  ((leaf)->mKey)

// Macros to check whether we branch left or branch right for a given
// key and bit mask
#define DST_BRANCHES_LEFT(key, mask) \
  (0 == (PtrBits(key) & (mask)))
  
#define DST_BRANCHES_RIGHT(key, mask) \
  ((mask) == (PtrBits(key) & (mask)))

/////////////////////////////////////////////////////////////////////////////
// Arena used for fast allocation and deallocation of Node structures.
// Maintains free-list of freed objects

nsDST::NodeArena*
nsDST::NewMemoryArena(PRUint32 aArenaSize)
{
  return new NodeArena(aArenaSize);
}

MOZ_DECL_CTOR_COUNTER(NodeArena);

// Constructor
nsDST::NodeArena::NodeArena(PRUint32 aArenaSize)
  : mLeafNodeFreeList(0), mTwoNodeFreeList(0), mRefCnt(0)
{
  MOZ_COUNT_CTOR(NodeArena);
  PL_INIT_ARENA_POOL(&mPool, "DSTNodeArena", aArenaSize);
}

// Destructor
nsDST::NodeArena::~NodeArena()
{
  MOZ_COUNT_DTOR(NodeArena);

  // Free the arena in the pool and finish using it
  PL_FinishArenaPool(&mPool);
}

void
nsDST::NodeArena::Release()
{
  NS_PRECONDITION(mRefCnt > 0, "unexpected ref count");
  if (--mRefCnt == 0) {
    delete this;
  }
}

// Called by the nsDST::LeafNode's overloaded placement operator when
// allocating a new node. First checks the free list. If the free list is
// empty, then it allocates memory from the arena
void*
nsDST::NodeArena::AllocLeafNode()
{
  void* p;
  
  if (mLeafNodeFreeList) {
    // Remove the node at the head of the free-list
    p = mLeafNodeFreeList;
    mLeafNodeFreeList = (LeafNode*)mLeafNodeFreeList->mKey;

  } else {
    PL_ARENA_ALLOCATE(p, &mPool, sizeof(nsDST::LeafNode));
  }

  return p;
}

// Called by the nsDST::TwoNode's overloaded placement operator when
// allocating a new node. First checks the free list. If the free list is
// empty, then it allocates memory from the arena
void*
nsDST::NodeArena::AllocTwoNode()
{
  void* p;
  
  if (mTwoNodeFreeList) {
    // Remove the node at the head of the free-list
    p = mTwoNodeFreeList;
    mTwoNodeFreeList = (TwoNode*)mTwoNodeFreeList->mKey;

  } else {
    PL_ARENA_ALLOCATE(p, &mPool, sizeof(nsDST::TwoNode));
  }

  return p;
}

// Called by the DST's DestroyNode() function. Adds the node to the head
// of the free list where it can be reused by AllocateNode()
void
nsDST::NodeArena::FreeNode(LeafNode* aLeafNode)
{
#ifdef NS_DEBUG
  memset(aLeafNode, 0xde, sizeof(*aLeafNode));
#endif
  // Add this node to the head of the free-list
  aLeafNode->mKey = mLeafNodeFreeList;
  mLeafNodeFreeList = aLeafNode;
}

// Called by the DST's DestroyNode() function. Adds the node to the head
// of the free list where it can be reused by AllocateNode()
void
nsDST::NodeArena::FreeNode(TwoNode* aTwoNode)
{
#ifdef NS_DEBUG
  memset(aTwoNode, 0xde, sizeof(*aTwoNode));
#endif
  // Add this node to the head of the free-list
  aTwoNode->mKey = mTwoNodeFreeList;
  mTwoNodeFreeList = aTwoNode;
}

// Called by the DST's Clear() function when we want to free the memory
// in the arena pool, but continue using the arena
void
nsDST::NodeArena::FreeArenaPool()
{
  // Free the arena in the pool, but continue using it
  PL_FreeArenaPool(&mPool);

  // Clear the free lists
  mLeafNodeFreeList = 0;
  mTwoNodeFreeList = 0;
}

#ifdef NS_DEBUG
int
nsDST::NodeArena::NumArenas() const
{
  // Calculate the number of arenas in use
  int numArenas = 0;
  for (PLArena* arena = mPool.first.next; arena; arena = arena->next) {
    numArenas++;
  }

  return numArenas;
}
#endif

/////////////////////////////////////////////////////////////////////////////
// Digital search tree for doing a radix-search of pointer-based keys

MOZ_DECL_CTOR_COUNTER(nsDST);

// Constructor
nsDST::nsDST(NodeArena* aArena, PtrBits aLevelZeroBit)
  : mRoot(0), mArena(aArena), mLevelZeroBit(aLevelZeroBit)
{
  NS_PRECONDITION(aArena, "no node arena");
  MOZ_COUNT_CTOR(nsDST);

  // Add a reference to the node arena
  mArena->AddRef();
}

// Destructor
nsDST::~nsDST()
{
  MOZ_COUNT_DTOR(nsDST);

  // If the node arena is shared, then we'll need to explicitly
  // free each node in the tree
  if (mArena->IsShared()) {
    FreeTree(mRoot);
  }

  // Release our reference to the node arena
  mArena->Release();
}

// Called by Remove() to destroy a node. Explicitly calls the destructor
// and then asks the memory arena to free the memory
inline void
nsDST::DestroyNode(LeafNode* aLeafNode)
{
  aLeafNode->~LeafNode();      // call destructor
  mArena->FreeNode(aLeafNode); // free memory
}

// Called by Remove() to destroy a node. Explicitly calls the destructor
// and then asks the memory arena to free the memory
inline void
nsDST::DestroyNode(TwoNode* aTwoNode)
{
  aTwoNode->~TwoNode();       // call destructor
  mArena->FreeNode(aTwoNode); // free memory
}

void
nsDST::FreeTree(LeafNode* aNode)
{
keepLooping:
  if (!aNode) {
    return;
  }

  if (aNode->IsLeaf()) {
    DestroyNode(aNode);

  } else {
    LeafNode* left = ((TwoNode*)aNode)->mLeft;
    LeafNode* right = ((TwoNode*)aNode)->mRight;
  
    DestroyNode((TwoNode*)aNode);
    FreeTree(left);
    aNode = right;
    goto keepLooping;    
  }
}

// Removes all nodes from the tree
nsresult
nsDST::Clear()
{
  if (mArena->IsShared()) {
    FreeTree(mRoot);
  } else {
    mArena->FreeArenaPool();
  }
  mRoot = 0;
  return NS_OK;
}

// Enumerate all the nodes in the tree
nsresult
nsDST::Enumerate(nsDSTNodeFunctor& aFunctor) const
{
  EnumTree(mRoot, aFunctor);
  return NS_OK;
}

void
nsDST::EnumTree(LeafNode* aNode, nsDSTNodeFunctor& aFunctor) const
{
keepLooping:
  if (!aNode) {
    return;
  }

  // Call the function-like object
  aFunctor(aNode->Key(), aNode->mValue);

  if (!aNode->IsLeaf()) {
    EnumTree(((TwoNode*)aNode)->mLeft, aFunctor);
    aNode = ((TwoNode*)aNode)->mRight;
    goto keepLooping;    
  }
}

nsDST::LeafNode*
nsDST::ConvertToLeafNode(TwoNode** aTwoNode)
{
  LeafNode* leaf = new (mArena)LeafNode((*aTwoNode)->Key(), (*aTwoNode)->mValue);

  DestroyNode(*aTwoNode);
  *aTwoNode = (TwoNode*)leaf;
  return leaf;
}

nsDST::TwoNode*
nsDST::ConvertToTwoNode(LeafNode** aLeafNode)
{
  TwoNode* twoNode = new (mArena)TwoNode(**aLeafNode);

  DestroyNode(*aLeafNode);
  *aLeafNode = (LeafNode*)twoNode;
  return twoNode;
}

// Searches the tree for a node with the specified key. Return the value
// or NULL if the key is not in the tree
nsresult
nsDST::Search(void* aKey, unsigned aOptions, void** aValue)
{
  NS_ENSURE_ARG_POINTER(aValue);
  *aValue = 0;  // initialize OUT parameter
  
  nsresult  result = SearchTree(aKey, aOptions, aValue);

#ifdef DEBUG_troy
  if (NS_DST_KEY_NOT_THERE == result) {
    // Use an alternative algorithm to verify that there's really
    // no node with a matching key
    DepthFirstSearch(mRoot, aKey);
  }
#endif
  return result;
}

// Adds a new key to the tree. If the specified key is already in the
// tree, then the existing value is replaced by the new value. Returns
// NS_DST_VALUE_OVERWRITTEN if there is an existing value that is
// overwritten
nsresult
nsDST::Insert(void* aKey, void* aValue, void** aOldValue)
{
  NS_PRECONDITION(0 == (PtrBits(aKey) & (mLevelZeroBit - 1)),
                  "ignored low-order bits are not zero");

  // Initialize OUT parameter
  if (aOldValue) {
    *aOldValue = 0;
  }
  
  // See if there's an existing node with a matching key
  LeafNode**  node = (LeafNode**)&mRoot;
  TwoNode*    branchReduction = 0;
  nsresult    result = NS_OK;

  if (*node) {
    PtrBits   bitMask = mLevelZeroBit;

    while (1) {
      // See if the key matches
      if ((*node)->Key() == aKey) {
        // We found an existing node with a matching key. Replace the value and
        // don't do a branch reduction
        branchReduction = 0;
        break;
      }
      
      // Is this a leaf node?
      if ((*node)->IsLeaf()) {
        if (!branchReduction) {
          // Replace the leaf node with a two node and destroy the
          // leaf node
          TwoNode*  twoNode = ConvertToTwoNode(node);
          
          // Exit the loop and allocate a new leaf node and set its
          // key and value
          node = DST_BRANCHES_LEFT(aKey, bitMask) ? &twoNode->mLeft :
                                                    &twoNode->mRight;
        }
        break;

      } else {
        TwoNode*& twoNode = *(TwoNode**)node;

        // Check whether we search the left branch or the right branch
        if (DST_BRANCHES_LEFT(aKey, bitMask)) {
          // If there's a left node and no right node, then see if we
          // can reduce the one way braching in the tree
          if (twoNode->mLeft && !twoNode->mRight) {
            if (DST_BRANCHES_RIGHT(twoNode->mKey, bitMask)) {
              // Yes, this node can become the right node of the tree. Remember
              // this for later in case we don't find a matching node
              branchReduction = twoNode;
            }
          }
          
          node = &twoNode->mLeft;

        } else {
          // If there's a right node and no left node, then see if we
          // can reduce the one way braching in the tree
          if (twoNode->mRight && !twoNode->mLeft) {
            if (DST_BRANCHES_LEFT(twoNode->mKey, bitMask)) {
              // Yes, this node can become the left node of the tree. Remember
              // this for later in case we don't find a matching node
              branchReduction = twoNode;
            }
          }

          node = &twoNode->mRight;
        }

        // Did we reach a null link?
        if (!*node) {
          break;
        }
      }

      // Move to the next bit in the key
      bitMask <<= 1;
    }
  }

  if (branchReduction) {
    // Reduce the one way branching by moving the existing key and
    // value to either the right or left node
    if (!branchReduction->mLeft) {
      NS_ASSERTION(branchReduction->mRight, "bad state");
      branchReduction->mLeft = new (mArena)LeafNode(*branchReduction);

    } else {
      NS_ASSERTION(!branchReduction->mRight, "bad state");
      branchReduction->mRight = new (mArena)LeafNode(*branchReduction);
    }

    // Replace the existing key and value with the new key and value
    branchReduction->SetKeyAndValue(aKey, aValue);

  } else if (*node) {
    // We found an existing node with a matching key. Replace the current
    // value with the new value
    if (aOldValue) {
      *aOldValue = (*node)->mValue;
    }
    (*node)->mValue = aValue;
    result = NS_DST_VALUE_OVERWRITTEN;

  } else {
    // Allocate a new leaf node and insert it into the tree
    *node = new (mArena)LeafNode(aKey, aValue);
  }

#ifdef DEBUG_troy
  VerifyTree(mRoot);

  // Verify that one and only one node in the tree have the specified key
  DepthFirstSearch(mRoot, aKey);
#endif
  return result;
}

// Helper function that removes and returns the left most leaf node
// of the specified subtree.
// Note: if the parent of the node that is removed is now a leaf,
// it will be converted to a leaf node...
nsDST::LeafNode*
nsDST::RemoveLeftMostLeafNode(TwoNode** aTwoNode)
{
  NS_PRECONDITION(!(*aTwoNode)->IsLeaf(), "bad parameter");

keepLooking:
  LeafNode**  child;

  if ((*aTwoNode)->mLeft) {
    // Walk down the left branch
    child = &(*aTwoNode)->mLeft;
    
    // See if it's a leaf
    if ((*child)->IsLeaf()) {
      // Remove the child from its parent
      LeafNode* result = *child;
      *child = 0;

      // If there's no right node then the parent is now a leaf so
      // convert it to a leaf node
      if (!(*aTwoNode)->mRight) {
        ConvertToLeafNode(aTwoNode);
      }

      // Return the leaf node
      return result;
    }


  } else if ((*aTwoNode)->mRight) {
    // No left branch, so walk down the right branch
    child = &(*aTwoNode)->mRight;

    if ((*child)->IsLeaf()) {
      // Remove the child from its parent
      LeafNode* result = *child;
      *child = 0;

      // That was the parent's only child node so convert the parent
      // node to a leaf node
      ConvertToLeafNode(aTwoNode);

      // Return the leaf node
      return result;
    }

  } else {
    // We should never encounter a two node with both links NULL. It should
    // have been coverted to a leaf instead...
    NS_ASSERTION(0, "bad node type");
    return 0;
  }

  aTwoNode = (TwoNode**)child;
  goto keepLooking;
}

// Returns NS_OK if there is a matching key and NS_DST_KEY_NOT_THERE
// otherwise
nsresult
nsDST::SearchTree(void* aKey, unsigned aOptions, void** aValue)
{
  NS_PRECONDITION(0 == (PtrBits(aKey) & (mLevelZeroBit - 1)),
                  "ignored low-order bits are not zero");

  if (mRoot) {
    LeafNode**  node;
    TwoNode**   parentNode = 0;
    PtrBits     bitMask = mLevelZeroBit;
    
    if (mRoot->Key() == aKey) {
      node = (LeafNode**)&mRoot;

    } else if (mRoot->IsLeaf()) {
      return NS_DST_KEY_NOT_THERE;  // no node with a matching key

    } else {
      // Look for a node with a matching key
      node = (LeafNode**)&mRoot;
      while (1) {
        NS_ASSERTION(!(*node)->IsLeaf(), "unexpected leaf mode");
        parentNode = (TwoNode**)node;
        
        // Check whether we search the left branch or the right branch
        if (DST_BRANCHES_LEFT(aKey, bitMask)) {
          node = &(*(TwoNode**)node)->mLeft;
        } else {
          node = &(*(TwoNode**)node)->mRight;
        }
  
        if (!*node) {
          // We found a NULL link which means no node with a matching key
          return NS_DST_KEY_NOT_THERE;
        }
        
        // Check if the key matches
        if ((*node)->Key() == aKey) {
          break;
        }

        // The key doesn't match. If this is a leaf node that means no
        // node with a matching key
        if ((*node)->IsLeaf()) {
          return NS_DST_KEY_NOT_THERE;
        }

        // Move to the next bit in the key
        bitMask <<= 1;
      }
    }

    // We found a matching node
    *aValue = (*node)->mValue;

    // Should we remove the key/value pair?
    if (aOptions & NS_DST_REMOVE_KEY_VALUE) {
      if ((*node)->IsLeaf()) {
        // Delete the leaf node
        DestroyNode(*node);
  
        // Disconnect the node from its parent node
        *node = 0;
  
        // If the parent now has no child nodes, then convert it to a
        // leaf frame
        if (parentNode && !(*parentNode)->mLeft && !(*parentNode)->mRight) {
          ConvertToLeafNode(parentNode);
        }
  
      } else {
        // We can't just move the left or right subtree up one level, because
        // then we would have to re-sort the tree. Instead replace the node's
        // key and value with that of its left most leaf node (any leaf frame
        // would do)
        LeafNode* leaf = RemoveLeftMostLeafNode((TwoNode**)node);
  
        // Copy over the leaf's key and value
        // Note: RemoveLeftMostLeafNode() may have converted "node" to a 
        // leaf node so don't make any assumptions here
        if ((*node)->IsLeaf()) {
          (*node)->mKey = DST_GET_LEAF_KEY(leaf);
        } else {
          (*node)->mKey = (void*)(PtrBits(DST_GET_LEAF_KEY(leaf)) | 0x01);
        }
        (*node)->mValue = leaf->mValue;
  
        // Delete the leaf node
        DestroyNode(leaf);
      }
#ifdef DEBUG_troy
      VerifyTree(mRoot);
#endif
    }
  
    return NS_OK;
  }

  return NS_DST_KEY_NOT_THERE;
}

// Removes a key from the tree. Returns NS_OK if successful and
// NS_DST_KEY_NOT_THERE if there is no node with a matching key
nsresult
nsDST::Remove(void* aKey)
{
  void*     value;
  nsresult  result = SearchTree(aKey, NS_DST_REMOVE_KEY_VALUE, &value);

#ifdef DEBUG_troy
  if (NS_OK == result) {
    // We found a node with a matching key and we removed it. Verify that
    // we successfully removed the node
    void* ignoreValue;

    NS_POSTCONDITION(Search(aKey, 0, &ignoreValue) == NS_DST_KEY_NOT_THERE,
                     "remove operation failed");
  }
#endif

  return result;
}

#ifdef NS_DEBUG
// Helper function used to verify the integrity of the tree. Does a
// depth-first search of the tree looking for a node with the specified
// key. Also verifies that the key is not in the tree more than once
nsDST::LeafNode*
nsDST::DepthFirstSearch(LeafNode* aNode, void* aKey) const
{
  if (!aNode) {
    return 0;
  } else if (aNode->IsLeaf()) {
    return (aNode->Key() == aKey) ? aNode : 0;
  } else {
    // Search the left branch of the tree
    LeafNode* result = DepthFirstSearch(((TwoNode*)aNode)->mLeft, aKey);

    if (result) {
      // Verify there's no matching node in the right branch
      NS_ASSERTION(!DepthFirstSearch(((TwoNode*)aNode)->mRight, aKey),
                   "key in tree more than once");

    } else {
      // Search the right branch of the tree
      result = DepthFirstSearch(((TwoNode*)aNode)->mRight, aKey);
    }

    // See if the node's key matches
    if (aNode->Key() == aKey) {
      NS_ASSERTION(!result, "key in tree more than once");
      result = aNode;
    }

    return result;
  }
}

// Helper function that verifies the integrity of the tree. Called
// by Insert() and Remove()
void
nsDST::VerifyTree(LeafNode* aNode, int aLevel, PtrBits aLevelKeyBits) const
{
  if (aNode) {
    // Verify that the first "aLevel" bits of this node's key agree with the
    // accumulated key bits that apply to this branch of the tree, i.e., the
    // path to this node as specified by the bits of the key
    if (aLevel > 0) {
      // When calculating the bit mask, take into consideration the low-order
      // bits we ignore
      PtrBits bitMask = (mLevelZeroBit << aLevel) - 1;
      NS_ASSERTION(aLevelKeyBits == (PtrBits(aNode->Key()) & bitMask),
                   "key's bits don't match");
    }

    if (!aNode->IsLeaf()) {
      const TwoNode*  twoNode = (TwoNode*)aNode;
      NS_ASSERTION(twoNode->mLeft || twoNode->mRight,
                   "two node with no child nodes");

      // All node keys in the left subtree should have the next bit set to 0
      VerifyTree(twoNode->mLeft, aLevel + 1, aLevelKeyBits);
  
      // All node keys in the left subtree should have the next bit set to 1
      VerifyTree(twoNode->mRight, aLevel + 1, aLevelKeyBits | (mLevelZeroBit << aLevel));
    }
  }
}

void
nsDST::GatherStatistics(LeafNode* aNode,
                        int       aLevel,
                        int&      aNumLeafNodes,
                        int&      aNumTwoNodes,
                        int       aNodesPerLevel[]) const
{
  if (aNode) {
    aNodesPerLevel[aLevel]++;
    if (aNode->IsLeaf()) {
      aNumLeafNodes++;
    } else {
      aNumTwoNodes++;
      GatherStatistics(((TwoNode*)aNode)->mLeft, aLevel + 1, aNumLeafNodes,
                       aNumTwoNodes, aNodesPerLevel);
      GatherStatistics(((TwoNode*)aNode)->mRight, aLevel + 1, aNumLeafNodes,
                       aNumTwoNodes, aNodesPerLevel);
    }
  }
}

void
nsDST::Dump(FILE* out) const
{
  // Walk the tree gathering statistics about the number of nodes, the height
  // of the tree (maximum node level), the average node level, and the median
  // node level
  static const int maxLevels = sizeof(void*) * 8;

  int numLeafNodes = 0;
  int numTwoNodes = 0;
  int nodesPerLevel[maxLevels];  // count of the number of nodes at a given level
  memset(&nodesPerLevel, 0, sizeof(int) * maxLevels);

  // Walk each node in the tree recording its node level
  GatherStatistics(mRoot, 0, numLeafNodes, numTwoNodes, nodesPerLevel);

  // Calculate the height, average node level, and median node level
  int height, medianLevel = 0, pathLength = 0;
  for (height = 0; height < maxLevels; height++) {
    int count = nodesPerLevel[height];
    if (0 == count) {
      break;
    }

    // Update the median node level
    if (count > nodesPerLevel[medianLevel]) {
      medianLevel = height;
    }

    // Update the path length
    pathLength += height * count;
  }

  // Output the statistics
  int numTotalNodes = numLeafNodes + numTwoNodes;

  fputs("DST statistics\n", out);
  fprintf(out, "  Number of leaf nodes: %d\n", numLeafNodes);
  fprintf(out, "  Number of tree nodes: %d\n", numTwoNodes);
  if (numTotalNodes > 0) {
    fprintf(out, "  Average node size: %.1f\n",
            float(numLeafNodes * sizeof(LeafNode) + numTwoNodes * sizeof(TwoNode)) /
            float(numTotalNodes));
  }
  fprintf(out, "  Number of arenas: %d(%d)\n", mArena->NumArenas(), mArena->ArenaSize());
  fprintf(out, "  Height (maximum node level) of the tree: %d\n", height - 1);
  if (numTotalNodes > 0) {
    fprintf(out, "  Average node level: %.1f\n", float(pathLength) / float(numTotalNodes));
  }
  fprintf(out, "  Median node level: %d\n", medianLevel);
  fprintf(out, "  Path length: %d\n", pathLength);

  // Output the number of nodes at each level of the tree
  fputs("  Nodes per level: ", out);
  fprintf(out, "%d", nodesPerLevel[0]);
  for (int i = 1; i < height; i++) {
    fprintf(out, ", %d", nodesPerLevel[i]);
  }
  fputs("\n", out);
}
#endif

