/* Memory access checker sub functions: use mmap to alloc the bitmaps
   Copyright 1993, 1994, 1995 Tristan Gingold
		  Written September 1993 by Tristan Gingold

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 library 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; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.

The author may be reached by US/French mail:
		Tristan Gingold 
		8 rue Parmentier
		F-91120 PALAISEAU
		FRANCE
*/

/* With Checker, the memory map is like this:
 +----------------------------------------------------------------------------+
 | code | data | bss |  data  | heap | >>>>  |  heap  | stack  | <<<< | stack |
 |      |      |     | bitmap |      | >>>>  | bitmap | bitmap | <<<< |       |
 +----------------------------------------------------------------------------+
      &etext       &end          sbrk(0)   MM_HEAP  MM_STACK        %sp
*/

#include <limits.h>
#include <stddef.h>
#include <unistd.h>
#include "malloc.h"
#include "chkrlib.h"

/* Define the location and sizes of some bitmaps.  */
#ifdef CHKR_DATABITMAP
/* The data bitmap.  */
unsigned char *chkr_data_bitmap;
static int data_bitmap_size;
#endif /* CHKR_DATABITMAP */

#ifdef CHKR_STACKBITMAP
/* The stack bitmap.  */
uchar *chkr_stack_bitmap;
static int stack_bitmap_size;
static uint stack_bitmap_pages;
#endif /* CHKR_STACKBITMAP */

void chkr_init_sbrk (void);

#ifndef MDCHECKER
/* Initialize the bitmaps.  Called by parse-args.c.  */
void
init_morecore (void)
{
  static int initialized = 0;
  PTR result;

  /* Do not over-initialize!  */
  if (initialized)
    return;

#ifndef bytes_per_state
  /* Initialisation.  */
  /* Check for stupid value.  */
  if (bytes_per_state == 0)
    {
      /* Warns and uses a default value instead.  */
      chkr_perror (M_I_BBS_MA_ET);
      bytes_per_state = 1;
    }
  
  /* Compute the log of bytes_per_state.  */
  log_bytes_per_state = 0;
  while (bytes_per_state != 1)
    {
      log_bytes_per_state++;
      bytes_per_state >>= 1;
    }
  
  /* Precompute often used values.  */
  bytes_per_state = 1 << log_bytes_per_state;
  bytes_per_state_round = bytes_per_state - 1;
  log_bytes_per_bbm = log_bytes_per_state + 2;
  bytes_per_bbm = 1 << log_bytes_per_bbm;
  bytes_per_bbm_round = bytes_per_bbm - 1;
#endif /* bytes_per_state */
  
  /* Initialize the map descriptors.  */
  init_mapinfos ();
  
  /* Allocate and initialize the bitmaps.  */
#ifdef CHKR_DATABITMAP
  /* Since the data segment is always RW and initialized, it needn't a bitmap.
     That's the reason why it is SegRW.  However, if you really want a bitmap,
     make it SegNormal (in init_mapinfos) and the bitmap will be create here.
   */
  if (mapinfo[DATABM]->type == SegNormal)
    {
      /* The bitmap is allocated just after the data segment.  */
      result = chkr_sbrk (0);
      data_bitmap_size = (result - (PTR)(objects->end) + bytes_per_bbm_round)
	>> log_bytes_per_bbm;
      /* Alignment.  */
      chkr_data_bitmap = chkr_sbrk ((data_bitmap_size + 0x10) & (~0x0f));
      if (chkr_data_bitmap == (uchar*)-1)
        chkr_data_bitmap = NULL;	/* Won't be checked */
      else
        {
          /* Initialize the bitmap and update the descriptor.  */
          memset (chkr_data_bitmap, 0xff, data_bitmap_size);
          mapinfo[DATABM]->length = data_bitmap_size << log_bytes_per_bbm;
          mapinfo[DATABM]->bmbase = chkr_data_bitmap;
        }
   }
#endif /* CHKR_DATABITMAP */

#ifdef CHKR_STACKBITMAP
  /* The stack bitmap.  */
  if (known_stack_limit)
    stack_bitmapped = known_stack_limit;
  else
    known_stack_limit = stack_bitmapped = (PTR)&result;
#ifdef STACK_GROWS_DOWNWARD
  stack_bitmap_size = (STACK_BASE - (int) (&result) + bytes_per_bbm_round)
    >> log_bytes_per_bbm;
#else /* !STACK_GROWS_DOWNWARD */
  stack_bitmap_size = ((int) (&result) + bytes_per_bbm_round - STACK_BASE)
    >> log_bytes_per_bbm;
#endif /* !STACK_GROWS_DOWNWARD */

  stack_bitmap_pages = (stack_bitmap_size + CHKR_PAGESIZE - 1) / CHKR_PAGESIZE;
  chkr_stack_bitmap =
    (uchar*) mmap ((char *) MM_STACK - stack_bitmap_pages * CHKR_PAGESIZE,
		   stack_bitmap_pages * CHKR_PAGESIZE,
		   MM_PROT,
		   MM_FLAGS,
		   MM_FILE, 0);
  if (chkr_stack_bitmap == (uchar*) - 1)
    chkr_abort ();

  /* Initialize the bitmap: rights are set to read/write.  */
  memset ((char*) MM_STACK - stack_bitmap_size, 0x0, stack_bitmap_size);

  /* Update the descriptor.  */
#ifdef STACK_GROWS_DOWNWARD
  mapinfo[STACKBM]->real_base = stack_bitmapped;
  mapinfo[STACKBM]->base = (PTR) STACK_BASE - ((stack_bitmap_pages * CHKR_PAGESIZE) << log_bytes_per_bbm);
  mapinfo[STACKBM]->length = (stack_bitmap_pages * CHKR_PAGESIZE) << log_bytes_per_bbm;
#else /* !STACK_GROWS_DOWNWARD */
  mapinfo[STACKBM]->real_base = (PTR) STACK_BASE;
  mapinfo[STACKBM]->base = (PTR) STACK_BASE;
  mapinfo[STACKBM]->length = known_stack_limit - (PTR) STACK_BASE;
#endif /* !STACK_GROWS_DOWNWARD */
  mapinfo[STACKBM]->bmbase = chkr_stack_bitmap;
#endif /* CHKR_STACKBITMAP */

  /* Do not forget this !!!  */
  initialized = 1;
  
  /* Initialize the mutex.  */
  mdesc_lock = 0;
}
#endif /* !MDCHECKER */

#ifdef CHKR_STACKBITMAP
/* Called by maccess to update the stack bitmap when the stack pointer has
   changed.  */
void
adjust_stackbitmap (PTR ptr)
{
  /* The new size (in page) of the stack bitmap.  */
  int size;
  
  /* The difference between the old and the new size.  */
  int diff;

  /* Compute the increment.  */
#ifdef STACK_GROWS_DOWNWARD
  size = (((daddr_t) STACK_BASE - (daddr_t) ptr + CHKR_PAGESIZE - 1)
	  / CHKR_PAGESIZE) >> log_bytes_per_state;
#else  /* !STACK_GROWS_DOWNWARD */
  size = (((daddr_t) ptr - (daddr_t) STACK_BASE + CHKR_PAGESIZE - 1)
	  / CHKR_PAGESIZE) >> log_bytes_per_state;
#endif  /* !STACK_GROWS_DOWNWARD */
  diff = size - stack_bitmap_pages;

  /* The stack bitmap is never reduced.  */
  if (diff <= 0)
    return;

  /* Allocate the new pages.  */
#ifdef STACK_GROWS_DOWNWARD
  chkr_stack_bitmap = 
    (uchar*) mmap ((char *) (MM_STACK - size * CHKR_PAGESIZE),
		   diff * CHKR_PAGESIZE, MM_PROT, MM_FLAGS, MM_FILE, 0);
  if (chkr_stack_bitmap == (uchar*) -1)
    chkr_abort ();
#else  /* !STACK_GROWS_DOWNWARD */
  if ((uchar *) mmap ((char *) (MM_STACK + size * CHKR_PAGESIZE),
		      diff * CHKR_PAGESIZE, MM_PROT, MM_FLAGS, MM_FILE, 0)
      == (uchar *) -1)
    chkr_abort ();
#endif  /* !STACK_GROWS_DOWNWARD */

  /* Update the descriptor.  */
  stack_bitmap_pages = size;
  mapinfo[STACKBM]->length = (stack_bitmap_pages * CHKR_PAGESIZE) << log_bytes_per_bbm;
#ifdef STACK_GROWS_DOWNWARD
  mapinfo[STACKBM]->bmbase = chkr_stack_bitmap;
  mapinfo[STACKBM]->base = (char*) STACK_BASE - ((stack_bitmap_pages * CHKR_PAGESIZE) << log_bytes_per_bbm);
#endif  /* !STACK_GROWS_DOWNWARD */

#if 0
  /* no access right */
  memset ((char *) (MM_STACK + stack_bitmap_pages * CHKR_PAGESIZE), 0,
	  diff * CHKR_PAGESIZE);
#endif
}
#endif /* CHKR_STACKBITMAP */

/* NOTA: The following code is an heavily modified piece of code of mmalloc 
 * The copyright is:
 * Support for an sbrk-like function that uses mmap.
   Copyright 1992 Free Software Foundation, Inc.

   Contributed by Fred Fish at Cygnus Support.   fnf@cygnus.com

   This file is part of the GNU C Library.
 */

/* The descriptor which use sbrk_morecore().  */
struct mdesc *__mmalloc_default_mdp;

#ifndef MDCHECKER
/* The maximum number of free pages before releasing it to the system.  */
static int mmap_cache = 4;
#endif

/* An sbrk() version of morecore for mmalloc.  Mutex must be done by the 
   caller */
static PTR
sbrk_morecore (struct mdesc *mdp, int size)
{
  PTR result;
#ifdef CHKR_HEAPBITMAP
  int size1;
  int bm_size;
#endif /* CHKR_HEAPBITMAP */

#ifndef CODE_CONTAINS_NO_BUGS_WELL_IN_FACT_NEVER
  /* Be sure the caller has mutex.  */
  if (!mutex_atomic_swap(&mdp->lock, 1))
    {
      chkr_perror(M_IE_ET);
      chkr_abort();
    }
#endif

#ifndef CHKR_HEAPBITMAP
  /* Really stupid morecore.  */
  result = chkr_sbrk (size);
  if (result == (PTR) -1)
    {
      chkr_perror (M_M_OOM_MC_ET);
      return NULL;
    }
  else
    {
      mdp -> breakval += size;
      mdp -> top += size;
    }
  return result;
#else /* CHKR_HEAPBITMAP */
  /* The real morecore.  */
  /* If the caller wants the current sbrk, it is easy.  */
  if (size == 0)
    {
      result = chkr_sbrk (0);
      return result == (PTR) -1 ? NULL : result;
    }
  
  /* Get more core.  */
  if (size > 0)
    {
      if (mdp->breakval + size > mdp->top)
        {
          /* We really need more core.  */
          result = chkr_sbrk (PAGE_ALIGN (size));
          if (result == (PTR)-1)
	    {
	      /* Out of memory.  Emit a message.  */
    	      chkr_report (M_M_OOM_MC_ET);
	      chkr_printf (M_SIZES_FOR_OOM, get_total_mem() / 1024,
	        get_total_aged_size(mdp) / 1024, size);
	      chkr_disp_call_chain();
	      return NULL;
	    }
	  mdp->top += PAGE_ALIGN(size);
	}
	
      /* The size of bitmap needed by the bytes allocated.  */
      bm_size = (size + bytes_per_bbm_round) >> log_bytes_per_bbm;
      
      /* The new size of the bitmap, in pages.  */
      size1 = (mdp->info.inmem.bitmap->size + bm_size + CHKR_PAGESIZE - 1)
	/ CHKR_PAGESIZE;
      
      /* If new pages are needed, allocate them.  */
      if (size1 > mdp->info.inmem.bitmap->pages)
	{
	  if (mmap ((char *) (mdp->info.inmem.bitmap->base
	                      + mdp->info.inmem.bitmap->pages * CHKR_PAGESIZE),
		    (size1 - mdp->info.inmem.bitmap->pages) * CHKR_PAGESIZE,
		    MM_PROT, MM_FLAGS, MM_FILE, 0) == (char *) -1)
	    chkr_abort ();
	  /* Initialize the new bitmap.  */
	  if (mdp->info.inmem.bitmap->size
	      < mdp->info.inmem.bitmap->pages * CHKR_PAGESIZE)
	    memset ((char *) (mdp->info.inmem.bitmap->base
			      + mdp->info.inmem.bitmap->size),
		    0,
		    bm_size & ~CHKR_PAGESIZE);
	  mdp->info.inmem.bitmap->pages = size1;
	}
      else
	{
	  /* Initialize the new bitmap.  */
	  memset ((char *) (mdp->info.inmem.bitmap->base
			    + mdp->info.inmem.bitmap->size),
		  0,
		  bm_size);
	}
	
      /* Adjust the bitmap descriptor.  */
      mapinfo[mdp->info.inmem.mapinfo]->length += size;
      mdp->info.inmem.bitmap->size =
	(mapinfo[mdp->info.inmem.mapinfo]->length + bytes_per_bbm_round)
	  >> log_bytes_per_bbm;
      result = mdp->breakval;
      mdp->breakval += size;
      return result;
    }
  else
    {
      /* Free core.  */
      if (mdp->top - (mdp->breakval + size) > CHKR_PAGESIZE)
        {
          /* Number of bytes.  */
          int round_size =
	    (mdp->top - (mdp->breakval + size)) & ~CHKR_PAGESIZE;
          result = chkr_sbrk (-round_size);
          if (result == (PTR)-1)
    	    {
    	      /* Should never happen.  */
	      chkr_report (M_M_OOM_MC_ET);
	      chkr_printf (M_SIZES_FOR_OOM, get_total_mem() / 1024,
	        get_total_aged_size(mdp) / 1024, size);
	      chkr_disp_call_chain();
	      return NULL;
	    }
	  mdp->top -= round_size;
	}

      /* Bitmap size freed.  */
      bm_size = (abs (size) + bytes_per_bbm_round) >> log_bytes_per_bbm;
      
      /* The new bitmap size in pages.  */
      size1 = (mdp->info.inmem.bitmap->size - bm_size + CHKR_PAGESIZE - 1)
	/ CHKR_PAGESIZE;
      
      /* If we can free some pages, do it.  */
      if (size1 < mdp->info.inmem.bitmap->pages
         && (mdp->info.inmem.bitmap->pages - size1) > mmap_cache)
	{
	  if (munmap ((char *) (mdp->info.inmem.bitmap->base 
				+ size1 * CHKR_PAGESIZE),
		      (mdp->info.inmem.bitmap->pages - size1) * CHKR_PAGESIZE)
	      != 0)
	    chkr_abort ();
	  /* Adjust the bitmap infos.  */
	  mdp->info.inmem.bitmap->pages = size1;
	}

      /* Update the bitmap descriptor.  */
      mapinfo[mdp->info.inmem.mapinfo]->length += size;
      mdp->info.inmem.bitmap->size =
	(mapinfo[mdp->info.inmem.mapinfo]->length + bytes_per_bbm_round)
	  >> log_bytes_per_bbm;
      result = mdp->breakval;
      mdp->breakval += size;
      return result;
    }
#endif /* !CHKR_HEAPBITMAP */
}

/* Initialize the default malloc descriptor if this is the first time
   a request has been made to use the default sbrk'd region.  */
struct mdesc *
__mmalloc_sbrk_init ()
{
  PTR base;
  unsigned int adj;
  int len = sizeof (struct mdesc);

  if (__malloc_initialized == 0)
    {
      __malloc_initialized = 1;
      chkr_initialize ();	/* Which calls init_morecore.  */
    }
    
  if (mutex_atomic_swap (&mdesc_lock, 1))
    MUTEX_FAIL;

  /* At first, initialize sbrk.  */
  chkr_init_sbrk ();
  
  /* Alignment on LITTLE_SPACE.  */
  base = chkr_sbrk (0);
  adj = (unsigned int)base % LITTLE_SPACE;
  if (adj != 0)
    {
      chkr_sbrk (LITTLE_SPACE - adj);
      base = chkr_sbrk (0);
    }
    
  /* Alloc space for the structure and initialise it.  */
  __mmalloc_default_mdp = (struct mdesc *) chkr_sbrk (len);
  memset ((char *) __mmalloc_default_mdp, 0, len);
  __mmalloc_default_mdp->morecore = sbrk_morecore;
  __mmalloc_default_mdp->base = base;
  __mmalloc_default_mdp->flags |= MMALLOC_SBRK_HEAP;
  __mmalloc_default_mdp->breakval = __mmalloc_default_mdp->top = chkr_sbrk (0);
  __mmalloc_default_mdp->fd = -1;
#ifndef MDCHECKER
  __mmalloc_default_mdp->info.inmem.bitmap =
    (struct bitmapinfo*) sys_malloc(sizeof(struct bitmapinfo));
  __mmalloc_default_mdp->info.inmem.bitmap->base =
    (uchar*) mmap((PTR)MM_HEAP, CHKR_PAGESIZE,
		  MM_PROT, MM_FLAGS, MM_FILE, 0);
  __mmalloc_default_mdp->info.inmem.bitmap->pages = 1;
  __mmalloc_default_mdp->info.inmem.bitmap->size =
    (len + bytes_per_bbm_round) >> log_bytes_per_bbm;
  __mmalloc_default_mdp->info.inmem.mapinfo =
    new_heap (1, __mmalloc_default_mdp);
#endif
  __mmalloc_default_mdp->info.inmem.next_mdesc = _firstmdesc;
  __mmalloc_default_mdp->info.inmem.prev_mdesc = NULL_MDESC;
  
  /* Link this mdesc.  Be sure it is the first.  */
  if (_firstmdesc)
    _firstmdesc->info.inmem.prev_mdesc = __mmalloc_default_mdp;
  if (_lastmdesc == NULL_MDESC)
    _lastmdesc = __mmalloc_default_mdp;
  _firstmdesc = __mmalloc_default_mdp;
  
#ifdef CHKR_HEAPBITMAP
  chkr_set_right ((PTR)__mmalloc_default_mdp, len, CHKR_UN);
#endif
  mutex_atomic_swap (&mdesc_lock, 0);
  return __mmalloc_default_mdp;
}

#undef PAGE_ALIGN
#define PAGE_ALIGN(addr) (caddr_t) (((long)(addr) + CHKR_PAGESIZE - 1) & \
				    ~(CHKR_PAGESIZE - 1))

/* Get core for the memory region specified by MDP, using SIZE as the
   amount to either add to or subtract from the existing region.  Works
   like sbrk(), but using mmap().  */
PTR
__mmalloc_mmap_morecore (struct mdesc *mdp, int size)
{
  PTR result = NULL;
#ifdef CHKR_HEAPBITMAP
  int size1;
  int bm_size;
#endif /* CHKR_HEAPBITMAP */
  off_t foffset;	/* File offset at which new mapping will start */
  size_t mapbytes;	/* Number of bytes to map */
  caddr_t moveto;	/* Address where we wish to move "break value" to */
  caddr_t mapto;	/* Address we actually mapped to */
  char buf = 0;		/* Single byte to write to extend mapped file */

  if (size == 0)
    {
      /* Just return the current "break" value.  */
      result = mdp -> breakval;
    }
  else if (size < 0)
    {
      /* We are deallocating memory.  If the amount requested would cause
	 us to try to deallocate back past the base of the mmap'd region
	 then do nothing, and return NULL.  Otherwise, deallocate the
	 memory and return the old break value.  */
      if (mdp -> breakval + size >= mdp -> base)
	{
	  result = (PTR) mdp -> breakval;
	  mdp -> breakval += size;
	  moveto = PAGE_ALIGN (mdp -> breakval);
	  munmap (moveto, (size_t) (mdp -> top - moveto));
	  mdp -> top = moveto;
#ifdef CHKR_HEAPBITMAP	  
	  bm_size = (abs (size) + bytes_per_bbm_round) >> log_bytes_per_bbm;
          size1 = (mdp->info.inmem.bitmap->size - bm_size + CHKR_PAGESIZE - 1)
	    / CHKR_PAGESIZE;
          if (size1 < mdp->info.inmem.bitmap->pages
	      && (mdp->info.inmem.bitmap->pages - size1) > mmap_cache)
	    {
	      if (munmap ((char *) (mdp->info.inmem.bitmap->base
				    + size1 * CHKR_PAGESIZE),
			  (mdp->info.inmem.bitmap->pages - size1)
			  * CHKR_PAGESIZE) != 0)
	        chkr_abort ();
	      /* Adjust the bitmap info.  */
	      mdp->info.inmem.bitmap->pages = size1;
	    }
          mdp->info.inmem.bitmap->size -= bm_size;
          mapinfo[mdp->info.inmem.mapinfo]->length =
	    mdp->info.inmem.bitmap->size << log_bytes_per_bbm;
#endif /* CHKR_HEAPBITMAP */          
	}
    }
  else
    {
      /* We are allocating memory.  Make sure we have an open file
	 descriptor and then go on to get the memory.  */
      if (mdp -> fd < 0)
	return NULL;
      else if (mdp -> breakval + size > mdp -> top)
	{
	  /* The request would move us past the end of the currently
	     mapped memory, so map in enough more memory to satisfy
	     the request.  This means we also have to grow the mapped-to
	     file by an appropriate amount, since mmap cannot be used
	     to extend a file. */
	  moveto = PAGE_ALIGN (mdp -> breakval + size);
	  mapbytes = moveto - mdp -> top;
	  foffset = mdp -> top - mdp -> base;
#if 1
	  if (lseek (mdp -> fd, foffset + mapbytes - 1, SEEK_SET) == -1)
	    return NULL;
	  if (write (mdp -> fd, &buf, 1) != 1)
	    return NULL;
#else
	  if (ftruncate (mdp -> fd, foffset + mapbytes) == -1)
	    return NULL;
#endif
#if 0 /* def linux */
	  mapto = mmap (mdp -> top, mapbytes, PROT_READ | PROT_WRITE,
	       /* MAP_PRIVATE is used only because MAP_SHARED doesn't work. */
	       /* FIXME. TG 1994-09-02 with Linux 1.1.45 */
			MAP_PRIVATE | MAP_FIXED, mdp -> fd, foffset);
#else
	  mapto = mmap (mdp -> top, mapbytes, PROT_READ | PROT_WRITE,
			MAP_SHARED | MAP_FIXED, mdp -> fd, foffset);
#endif
	  if (mapto == mdp -> top)
	    {
	      mdp -> top = moveto;
	      result = (PTR) mdp -> breakval;
	      mdp -> breakval += size;
	    }
	}
      else
	{
	  result = (PTR) mdp -> breakval;
	  mdp -> breakval += size;
	}
#ifndef MDCHECKER
      bm_size = (size + bytes_per_bbm_round) >> log_bytes_per_bbm;
      size1 = (mdp->info.inmem.bitmap->size + bm_size + CHKR_PAGESIZE - 1)
	/ CHKR_PAGESIZE;
      if (size1 > mdp->info.inmem.bitmap->pages)
	{
	  if (mmap ((char *) (mdp->info.inmem.bitmap->base
	                      + mdp->info.inmem.bitmap->pages * CHKR_PAGESIZE),
		    (size1 - mdp->info.inmem.bitmap->pages) * CHKR_PAGESIZE,
		    MM_PROT, MM_FLAGS, MM_FILE, 0) == (char *) -1)
	    chkr_abort ();
	  /* Initialize the new bitmap.  */
	  if (mdp->info.inmem.bitmap->size
	      < mdp->info.inmem.bitmap->pages * CHKR_PAGESIZE)
	    memset ((char *) (mdp->info.inmem.bitmap->base
			      + mdp->info.inmem.bitmap->size),
		    0,
		    bm_size & ~CHKR_PAGESIZE);
	  mdp->info.inmem.bitmap->pages = size1;
	}
      else
	{
	  /* Initialize the new bitmap.  */
	  memset ((char *) (mdp->info.inmem.bitmap->base
			    + mdp->info.inmem.bitmap->size),
		  0,
		  bm_size);
	}
      /* Adjust the bitmap info.  */
      mdp->info.inmem.bitmap->size += bm_size;
      mapinfo[mdp->info.inmem.mapinfo]->length =
	mdp->info.inmem.bitmap->size << log_bytes_per_bbm;
#endif /* !MDCHECKER */
    }
  return result;
}
