/*--------------------------------------------------------------------*/
/*--- A mapping where the keys exactly cover the address space.    ---*/
/*---                                                 m_rangemap.c ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2014-2014 Mozilla Foundation

   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 of the
   License, 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

/* Contributed by Julian Seward <jseward@acm.org> */

#include "pub_core_basics.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcprint.h"
#include "pub_core_xarray.h"
#include "pub_core_rangemap.h"    /* self */


/* See pub_tool_rangemap.h for details of what this is all about. */

#define UWORD_MIN ((UWord)0)
#define UWORD_MAX (~(UWord)0)

typedef
   struct { UWord key_min; UWord key_max; UWord val; }
   Range;


struct _RangeMap {
   void* (*alloc) ( const HChar*, SizeT ); /* alloc fn (nofail) */
   const HChar* cc;                 /* cost centre for alloc */
   void  (*free) ( void* );         /* free fn */
   XArray* ranges;
};


/* fwds */
static void preen (/*MOD*/RangeMap* rm);
static Word find ( RangeMap* rm, UWord key );
static void split_at ( /*MOD*/RangeMap* rm, UWord key );
static void show ( RangeMap* rm );


RangeMap* VG_(newRangeMap) ( void*(*alloc_fn)(const HChar*,SizeT), 
                             const HChar* cc,
                             void(*free_fn)(void*),
                             UWord initialVal )
{
   /* check user-supplied info .. */
   vg_assert(alloc_fn);
   vg_assert(free_fn);
   RangeMap* rm = alloc_fn(cc, sizeof(RangeMap));
   vg_assert(rm);
   rm->alloc  = alloc_fn;
   rm->cc     = cc;
   rm->free   = free_fn;
   rm->ranges = VG_(newXA)( alloc_fn, cc, free_fn, sizeof(Range) );
   vg_assert(rm->ranges);
   /* Add the initial range */
   Range r;
   r.key_min = UWORD_MIN;
   r.key_max = UWORD_MAX;
   r.val     = initialVal;
   Word i = VG_(addToXA)(rm->ranges, &r);
   vg_assert(i == 0);
   vg_assert(VG_(sizeXA)(rm->ranges) == 1);
   /* */
   return rm;
}

void VG_(deleteRangeMap) ( RangeMap* rm )
{
   vg_assert(rm);
   vg_assert(rm->free);
   vg_assert(rm->ranges);
   VG_(deleteXA)(rm->ranges);
   rm->free(rm);
}

void VG_(bindRangeMap) ( RangeMap* rm,
                         UWord key_min, UWord key_max, UWord val )
{
   vg_assert(key_min <= key_max);
   split_at(rm, key_min);
   if (key_max < UWORD_MAX)
      split_at(rm, key_max + 1);
   Word iMin, iMax, i;
   iMin = find(rm, key_min);
   iMax = find(rm, key_max);
   for (i = iMin; i <= iMax; i++) {
      Range* rng = VG_(indexXA)(rm->ranges, i);
      rng->val = val;
   }
   preen(rm);
}

void VG_(lookupRangeMap) ( /*OUT*/UWord* key_min, /*OUT*/UWord* key_max,
                           /*OUT*/UWord* val, RangeMap* rm, UWord key )
{
   Word   i   = find(rm, key);
   Range* rng = (Range*)VG_(indexXA)(rm->ranges, i);
   *key_min = rng->key_min;
   *key_max = rng->key_max;
   *val     = rng->val;
}

Word VG_(sizeRangeMap) ( RangeMap* rm )
{
   vg_assert(rm && rm->ranges);
   return VG_(sizeXA)(rm->ranges);
}

void VG_(indexRangeMap) ( /*OUT*/UWord* key_min, /*OUT*/UWord* key_max,
                          /*OUT*/UWord* val, RangeMap* rm, Word ix )
{
   vg_assert(rm && rm->ranges);
   Range* rng = (Range*)VG_(indexXA)(rm->ranges, ix);
   *key_min = rng->key_min;
   *key_max = rng->key_max;
   *val     = rng->val;
}

/* Helper functions, not externally visible. */

static void preen (/*MOD*/RangeMap* rm)
{
   Word    i;
   XArray* ranges = rm->ranges;
   for (i = 0; i < VG_(sizeXA)(ranges) - 1; i++) {
      Range* rng0 = VG_(indexXA)(ranges, i+0);
      Range* rng1 = VG_(indexXA)(ranges, i+1);
      if (rng0->val != rng1->val)
         continue;
      rng0->key_max = rng1->key_max;
      VG_(removeIndexXA)(ranges, i+1);
      /* Back up one, so as not to miss an opportunity to merge with
         the entry after this one. */
      i--;
   }
}

static Word find ( RangeMap* rm, UWord key )
{
   XArray* ranges = rm->ranges;
   Word    lo     = 0;
   Word    hi     = VG_(sizeXA)(ranges);
   while (True) {
      /* The unsearched space is lo .. hi inclusive */
      if (lo > hi) {
         /* Not found.  This can't happen. */
         VG_(core_panic)("RangeMap::find: not found");
         /*NOTREACHED*/
         return -1;
      }
      Word   mid         = (lo + hi) / 2;
      Range* mid_rng     = (Range*)VG_(indexXA)(ranges, mid);
      UWord  key_mid_min = mid_rng->key_min;
      UWord  key_mid_max = mid_rng->key_max;
      if (key < key_mid_min) { hi = mid-1; continue; }
      if (key > key_mid_max) { lo = mid+1; continue; }
      return mid;
   }
}

static void split_at ( /*MOD*/RangeMap* rm, UWord key )
{
   XArray* ranges = rm->ranges;
   Word    i      = find(rm, key);
   Range   rng_i0 = *(Range*)VG_(indexXA)( ranges, i+0 );
   if (rng_i0.key_min == key)
      return;
   VG_(insertIndexXA)( ranges, i+1, &rng_i0 );
   /* The insert could have relocated the payload, hence the
      re-indexing of i+0 here. */
   Range* rng_i0p = (Range*)VG_(indexXA)( ranges, i+0 );
   Range* rng_i1p = (Range*)VG_(indexXA)( ranges, i+1 );
   rng_i0p->key_max = key-1;
   rng_i1p->key_min = key;
}

__attribute__((unused))
static void show ( RangeMap* rm )
{
   Word i;
   VG_(printf)("<< %ld entries:\n", VG_(sizeXA)(rm->ranges) );
   for (i = 0; i < VG_(sizeXA)(rm->ranges); i++) {
      Range* rng = (Range*)VG_(indexXA)(rm->ranges, i);
      VG_(printf)("  %016llx  %016llx  --> 0x%llx\n",
                  (ULong)rng->key_min, (ULong)rng->key_max, (ULong)rng->val);
   }
   VG_(printf)(">>\n");
}


/*--------------------------------------------------------------------*/
/*--- end                                             m_rangemap.c ---*/
/*--------------------------------------------------------------------*/