/* traceanc.c: ANCILLARY SUPPORT FOR TRACER
 *
 * $Id: //info.ravenbrook.com/project/mps/version/1.113/code/traceanc.c#1 $
 * Copyright (c) 2001-2013 Ravenbrook Limited.
 * See end of file for license.
 * Portions copyright (C) 2002 Global Graphics Software.
 *
 * All this ancillary stuff was making trace.c very cluttered.  
 * Put it here instead.  RHSK 2008-12-09.
 *
 * CONTENTS
 *
 *   - TraceStartMessage.  Posted when a trace starts.
 *
 *   - TraceMessage.  Posted when a trace ends.
 *
 *   - TraceIdMessages.  Pre-allocated messages for traceid.
 *
 *   - ArenaRelease, ArenaClamp, ArenaPark.
 *
 *   - ArenaExposeRemember and ArenaRestoreProtection.
 */

#include "mpm.h"



/* --------  TraceStartMessage  -------- */


/* TraceStartMessage -- posted when a trace starts
 *
 * Internal names:
 *   trace start
 *   TraceStartMessage, tsMessage (struct *)
 *   MessageTypeGCSTART (enum)
 *
 * External names:
 *   mps_message_type_gc_start (enum macro)
 *   MPS_MESSAGE_TYPE_GC_START (enum)
 *
 * (Note: this should properly be called "trace begin", but it's much 
 * too late to change it now!)
 *
 * See <design/message-gc/>.
 */

#define TraceStartMessageSig ((Sig)0x51926535) /* SIG TRaceStartMeSsage */

/* .whybuf:
 * .whybuf.len: Length (in chars) of a char buffer used to store the 
 * reason why a collection started in the TraceStartMessageStruct 
 * (used by mps_message_type_gc_start).  If the reason is too long to 
 * fit, it must be truncated.
 * .whybuf.nul: Check insists that the last char in the array is NUL 
 * (even if there is another NUL earlier in the buffer); this makes 
 * the NUL-termination check fast.
 */
#define TRACE_START_MESSAGE_WHYBUF_LEN 128

typedef struct TraceStartMessageStruct {
  Sig sig;
  char why[TRACE_START_MESSAGE_WHYBUF_LEN];  /* .whybuf */
  MessageStruct messageStruct;
} TraceStartMessageStruct;

#define TraceStartMessageMessage(traceStartMessage) \
  (&((traceStartMessage)->messageStruct))
#define MessageTraceStartMessage(message) \
  (PARENT(TraceStartMessageStruct, messageStruct, message))

Bool TraceStartMessageCheck(TraceStartMessage tsMessage)
{
  CHECKS(TraceStartMessage, tsMessage);
  CHECKD(Message, TraceStartMessageMessage(tsMessage));
  CHECKL(MessageGetType(TraceStartMessageMessage(tsMessage)) ==
         MessageTypeGCSTART);

  /* Check that why is NUL terminated.  See .whybuf.nul */
  CHECKL(tsMessage->why[NELEMS(tsMessage->why)-1] == '\0');

  return TRUE;
}

static void TraceStartMessageDelete(Message message)
{
  TraceStartMessage tsMessage;
  Arena arena;

  AVERT(Message, message);
  tsMessage = MessageTraceStartMessage(message);
  AVERT(TraceStartMessage, tsMessage);

  arena = MessageArena(message);
  tsMessage->sig = SigInvalid;
  MessageFinish(message);

  ControlFree(arena, (void *)tsMessage, sizeof(TraceStartMessageStruct));
}

static const char *TraceStartMessageWhy(Message message)
{
  TraceStartMessage tsMessage;

  AVERT(Message, message);
  tsMessage = MessageTraceStartMessage(message);
  AVERT(TraceStartMessage, tsMessage);

  return tsMessage->why;
}

static MessageClassStruct TraceStartMessageClassStruct = {
  MessageClassSig,               /* sig */
  "TraceGCStart",                /* name */
  MessageTypeGCSTART,            /* Message Type */
  TraceStartMessageDelete,       /* Delete */
  MessageNoFinalizationRef,      /* FinalizationRef */
  MessageNoGCLiveSize,           /* GCLiveSize */
  MessageNoGCCondemnedSize,      /* GCCondemnedSize */
  MessageNoGCNotCondemnedSize,   /* GCNotCondemnedSize */
  TraceStartMessageWhy,          /* GCStartWhy */
  MessageClassSig                /* <design/message/#class.sig.double> */
};

static void traceStartMessageInit(Arena arena, TraceStartMessage tsMessage)
{
  AVERT(Arena, arena);

  MessageInit(arena, TraceStartMessageMessage(tsMessage),
              &TraceStartMessageClassStruct, MessageTypeGCSTART);
  tsMessage->why[0] = '\0';
  tsMessage->why[NELEMS(tsMessage->why)-1] = '\0';  /* .whybuf.nul */

  tsMessage->sig = TraceStartMessageSig;
  AVERT(TraceStartMessage, tsMessage);
}

/* TraceStartWhyToString -- why-code to text
 *
 * Converts a TraceStartWhy* code into a constant string describing 
 * why a trace started.
 */
 
const char *TraceStartWhyToString(int why)
{
  const char *r;

  switch(why) {
  case TraceStartWhyCHAIN_GEN0CAP:
    r = "Generation 0 of a chain has reached capacity:"
        " start a minor collection.";
    break;
  case TraceStartWhyDYNAMICCRITERION:
    r = "Need to start full collection now, or there won't be enough"
        " memory (ArenaAvail) to complete it.";
    break;
  case TraceStartWhyOPPORTUNISM:
    r = "Opportunism: client predicts plenty of idle time,"
        " so start full collection.";
    break;
  case TraceStartWhyCLIENTFULL_INCREMENTAL:
    r = "Client requests: start incremental full collection now.";
    break;
  case TraceStartWhyCLIENTFULL_BLOCK:
    r = "Client requests: immediate full collection.";
    break;
  case TraceStartWhyWALK:
    r = "Walking all live objects.";
    break;
  case TraceStartWhyEXTENSION:
    r = "Extension: an MPS extension started the trace.";
    break;
  default:
    NOTREACHED;
    r = "Unknown reason (internal error).";
    break;
  }

  /* Should fit in buffer without truncation; see .whybuf.len */
  AVER(StringLength(r) < TRACE_START_MESSAGE_WHYBUF_LEN);

  return r;
}


/* traceStartWhyToTextBuffer
 *
 * Converts a TraceStartWhy* code into a string describing why a trace
 * started, and copies that into the text buffer the caller provides.
 * s specifies the beginning of the buffer to write the string
 * into, len specifies the length of the buffer.
 * The string written will be NUL terminated, and truncated if
 * necessary.
 */

static void traceStartWhyToTextBuffer(char *s, size_t len, int why)
{
  const char *r;
  size_t i;

  AVER(s);
  /* len can be anything, including 0. */
  AVER(TraceStartWhyBASE <= why);
  AVER(why < TraceStartWhyLIMIT);

  r = TraceStartWhyToString(why);

  for(i=0; i<len; ++i) {
    s[i] = r[i];
    if(r[i] == '\0')
      break;
  }
  s[len-1] = '\0';  /* .whybuf.nul */
}

/* TracePostStartMessage -- complete and post trace start message
 *
 */

void TracePostStartMessage(Trace trace)
{
  Arena arena;
  TraceId ti;
  TraceStartMessage tsMessage;

  AVERT(Trace, trace);
  AVER(trace->state == TraceUNFLIPPED);

  arena = trace->arena;
  AVERT(Arena, arena);

  ti = trace->ti;
  AVERT(TraceId, ti);

  tsMessage = arena->tsMessage[ti];
  if(tsMessage) {
    AVERT(TraceStartMessage, tsMessage);

    traceStartWhyToTextBuffer(tsMessage->why,
                              sizeof tsMessage->why, trace->why);

    MessagePost(arena, TraceStartMessageMessage(tsMessage));
    arena->tsMessage[ti] = NULL;
  } else {
    arena->droppedMessages += 1;
  }

  /* We have consumed the pre-allocated message */
  AVER(!arena->tsMessage[ti]);
}



/* --------  TraceMessage (trace end)  -------- */


/* TraceMessage -- posted when a trace ends
 *
 * Internal names:
 *   trace end
 *   TraceMessage, tMessage (struct *)
 *   MessageTypeGC (enum)
 *
 * External names:
 *   mps_message_type_gc (enum macro)
 *   MPS_MESSAGE_TYPE_GC (enum)
 *
 * (Note: this should properly be called "trace end", but it's much 
 * too late to change it now!)
 *
 * See <design/message-gc/>.
 */


/* TraceMessage -- type of trace end messages */

#define TraceMessageSig ((Sig)0x51926359)

typedef struct TraceMessageStruct  {
  Sig sig;
  Size liveSize;
  Size condemnedSize;
  Size notCondemnedSize;
  MessageStruct messageStruct;
} TraceMessageStruct;

#define TraceMessageMessage(traceMessage) (&((traceMessage)->messageStruct))
#define MessageTraceMessage(message) \
  (PARENT(TraceMessageStruct, messageStruct, message))

Bool TraceMessageCheck(TraceMessage tMessage)
{
  CHECKS(TraceMessage, tMessage);
  CHECKD(Message, TraceMessageMessage(tMessage));
  CHECKL(MessageGetType(TraceMessageMessage(tMessage)) ==
         MessageTypeGC);
  /* We can't check anything about the statistics.  In particular, */
  /* liveSize may exceed condemnedSize because they are only estimates. */

  return TRUE;
}

static void TraceMessageDelete(Message message)
{
  TraceMessage tMessage;
  Arena arena;

  AVERT(Message, message);
  tMessage = MessageTraceMessage(message);
  AVERT(TraceMessage, tMessage);

  arena = MessageArena(message);
  tMessage->sig = SigInvalid;
  MessageFinish(message);

  ControlFree(arena, (void *)tMessage, sizeof(TraceMessageStruct));
}

static Size TraceMessageLiveSize(Message message)
{
  TraceMessage tMessage;

  AVERT(Message, message);
  tMessage = MessageTraceMessage(message);
  AVERT(TraceMessage, tMessage);

  return tMessage->liveSize;
}

static Size TraceMessageCondemnedSize(Message message)
{
  TraceMessage tMessage;

  AVERT(Message, message);
  tMessage = MessageTraceMessage(message);
  AVERT(TraceMessage, tMessage);

  return tMessage->condemnedSize;
}

static Size TraceMessageNotCondemnedSize(Message message)
{
  TraceMessage tMessage;

  AVERT(Message, message);
  tMessage = MessageTraceMessage(message);
  AVERT(TraceMessage, tMessage);

  return tMessage->notCondemnedSize;
}

static MessageClassStruct TraceMessageClassStruct = {
  MessageClassSig,               /* sig */
  "TraceGC",                     /* name */
  MessageTypeGC,                 /* Message Type */
  TraceMessageDelete,            /* Delete */
  MessageNoFinalizationRef,      /* FinalizationRef */
  TraceMessageLiveSize,          /* GCLiveSize */
  TraceMessageCondemnedSize,     /* GCCondemnedSize */
  TraceMessageNotCondemnedSize,  /* GCNotCondemnedSize */
  MessageNoGCStartWhy,           /* GCStartWhy */
  MessageClassSig                /* <design/message/#class.sig.double> */
};

static void traceMessageInit(Arena arena, TraceMessage tMessage)
{
  AVERT(Arena, arena);

  MessageInit(arena, TraceMessageMessage(tMessage),
              &TraceMessageClassStruct, MessageTypeGC);
  tMessage->liveSize = (Size)0;
  tMessage->condemnedSize = (Size)0;
  tMessage->notCondemnedSize = (Size)0;

  tMessage->sig = TraceMessageSig;
  AVERT(TraceMessage, tMessage);
}

/* TracePostMessage -- complete and post trace end message
 *
 * .message.data: The trace end message contains the live size
 * (forwardedSize + preservedInPlaceSize), the condemned size
 * (condemned), and the not-condemned size (notCondemned).
 */

void TracePostMessage(Trace trace)
{
  Arena arena;
  TraceId ti;
  TraceMessage tMessage;

  AVERT(Trace, trace);
  AVER(trace->state == TraceFINISHED);
  
  arena = trace->arena;
  AVERT(Arena, arena);

  ti = trace->ti;
  AVERT(TraceId, ti);

  tMessage = arena->tMessage[ti];
  if(tMessage) {
    AVERT(TraceMessage, tMessage);

    tMessage->liveSize = trace->forwardedSize + trace->preservedInPlaceSize;
    tMessage->condemnedSize = trace->condemned;
    tMessage->notCondemnedSize = trace->notCondemned;

    MessagePost(arena, TraceMessageMessage(tMessage));
    arena->tMessage[ti] = NULL;
  } else {
    arena->droppedMessages += 1;
  }
  
  /* We have consumed the pre-allocated message */
  AVER(!arena->tMessage[ti]);
}



/* --------  TraceIdMessages  -------- */


/* TraceIdMessagesCheck - pre-allocated messages for this traceid.
 *
 * Messages are absent when already sent, or when (exceptionally) 
 * ControlAlloc failed at the end of the previous trace.  If present, 
 * they must be valid.
 *
 * Messages are pre-allocated all-or-nothing.  So if we've got a 
 * start but no end, that's wrong.
 *
 * Note: this function does not take a pointer to a struct, so it is 
 * not a 'proper' _Check function.  It can be used in CHECKL, but 
 * not CHECKD etc.
 */

Bool TraceIdMessagesCheck(Arena arena, TraceId ti)
{
  CHECKL(!arena->tsMessage[ti]
         || TraceStartMessageCheck(arena->tsMessage[ti]));
  CHECKL(!arena->tMessage[ti]
         || TraceMessageCheck(arena->tMessage[ti]));
  CHECKL(! (arena->tsMessage[ti] && !arena->tMessage[ti]) );

  return TRUE;
}

/* TraceIdMessagesCreate -- pre-allocate all messages for this traceid
 *
 * See <design/message-gc/#lifecycle>.
 *
 * For remote control of ControlAlloc, to simulate low memory:
 *  #define ControlAlloc !TIMCA_remote() ? ResFAIL : ControlAlloc
 *  extern Bool TIMCA_remote(void);
 * See TIMCA_remote() in zmess.c
 */

Res TraceIdMessagesCreate(Arena arena, TraceId ti)
{
  void *p;
  TraceStartMessage tsMessage;
  TraceMessage tMessage;
  Res res;
  
  /* Ensure we don't leak memory */
  AVER(!arena->tsMessage[ti]);
  AVER(!arena->tMessage[ti]);
  
  res = ControlAlloc(&p, arena, sizeof(TraceStartMessageStruct), FALSE);
  if(res != ResOK)
    goto failTraceStartMessage;
  tsMessage = p;

  res = ControlAlloc(&p, arena, sizeof(TraceMessageStruct), FALSE);
  if(res != ResOK)
    goto failTraceMessage;
  tMessage = p;

  traceStartMessageInit(arena, tsMessage);
  AVERT(TraceStartMessage, tsMessage);
  arena->tsMessage[ti] = tsMessage;

  traceMessageInit(arena, tMessage);
  AVERT(TraceMessage, tMessage);
  arena->tMessage[ti] = tMessage;
  
  AVER(TraceIdMessagesCheck(arena, ti));
  
  return ResOK;

failTraceMessage:
  ControlFree(arena, tsMessage, sizeof(TraceStartMessageStruct));
failTraceStartMessage:
  AVER(TraceIdMessagesCheck(arena, ti));
  return res;
}

/* TraceIdMessagesDestroy -- destroy any pre-allocated messages
 *
 * Only used during ArenaDestroy.
 *
 * See <design/message-gc/#lifecycle>.
 */

void TraceIdMessagesDestroy(Arena arena, TraceId ti)
{
  TraceStartMessage tsMessage;
  TraceMessage tMessage;
  
  AVER(TraceIdMessagesCheck(arena, ti));

  tsMessage = arena->tsMessage[ti];
  if(tsMessage) {
    arena->tsMessage[ti] = NULL;
    TraceStartMessageDelete(TraceStartMessageMessage(tsMessage));
  }

  tMessage = arena->tMessage[ti];
  if(tMessage) {
    arena->tMessage[ti] = NULL;
    TraceMessageDelete(TraceMessageMessage(tMessage));
  }

  AVER(!arena->tsMessage[ti]);
  AVER(!arena->tMessage[ti]);
  AVER(TraceIdMessagesCheck(arena, ti));
}



/* --------  ArenaRelease, ArenaClamp, ArenaPark  -------- */


/* ArenaRelease, ArenaClamp, ArenaPark -- allow/prevent collection work.
 *
 * These functions allow or prevent collection work.
 */


/* Forward Declarations */
static void arenaForgetProtection(Globals globals);


/* ArenaClamp -- clamp the arena (no optional collection increments) */

void ArenaClamp(Globals globals)
{
  AVERT(Globals, globals);
  globals->clamped = TRUE;
}


/* ArenaRelease -- release the arena (allow optional collection
 * increments) */

void ArenaRelease(Globals globals)
{
  AVERT(Globals, globals);
  arenaForgetProtection(globals);
  globals->clamped = FALSE;
  (void)TracePoll(globals);
}


/* ArenaPark -- finish all current collections and clamp the arena */

void ArenaPark(Globals globals)
{
  TraceId ti;
  Trace trace;
  Arena arena;

  AVERT(Globals, globals);
  arena = GlobalsArena(globals);

  globals->clamped = TRUE;

  while(arena->busyTraces != TraceSetEMPTY) {
    /* Poll active traces to make progress. */
    TRACE_SET_ITER(ti, trace, arena->busyTraces, arena)
      TraceQuantum(trace);
      if(trace->state == TraceFINISHED) {
        TraceDestroy(trace);
      }
    TRACE_SET_ITER_END(ti, trace, arena->busyTraces, arena);
  }
  
  /* Clear any emergency flag so that the next collection starts normally.
     Any traces that have been finished may have reclaimed memory. */
  ArenaSetEmergency(arena, FALSE);
}

/* ArenaStartCollect -- start a collection of everything in the
 * arena; leave unclamped. */

Res ArenaStartCollect(Globals globals, int why)
{
  Arena arena;
  Res res;
  Trace trace;

  AVERT(Globals, globals);
  arena = GlobalsArena(globals);

  ArenaPark(globals);
  res = TraceStartCollectAll(&trace, arena, why);
  if(res != ResOK)
    goto failStart;
  ArenaRelease(globals);
  return ResOK;

failStart:
  ArenaRelease(globals);
  return res;
}

/* ArenaCollect -- collect everything in arena; leave clamped */

Res ArenaCollect(Globals globals, int why)
{
  Res res;

  AVERT(Globals, globals);
  res = ArenaStartCollect(globals, why);
  if(res != ResOK)
    return res;

  ArenaPark(globals);
  return ResOK;
}



/* --------  ExposeRemember and RestoreProtection  -------- */


/* Low level stuff for Expose / Remember / Restore */

typedef struct RememberedSummaryBlockStruct {
  RingStruct globalRing;        /* link on globals->rememberedSummaryRing */
  struct SummaryPair {
    Addr base;
    RefSet summary;
  } the[RememberedSummaryBLOCK];
} RememberedSummaryBlockStruct;

typedef struct RememberedSummaryBlockStruct *RememberedSummaryBlock;

static void rememberedSummaryBlockInit(struct RememberedSummaryBlockStruct *block)
{
  size_t i;

  RingInit(&block->globalRing);
  for(i = 0; i < RememberedSummaryBLOCK; ++ i) {
    block->the[i].base = (Addr)0;
    block->the[i].summary = RefSetUNIV;
  }
}

static Res arenaRememberSummaryOne(Globals global, Addr base, RefSet summary)
{
  Arena arena;
  RememberedSummaryBlock block;

  AVER(summary != RefSetUNIV);

  arena = GlobalsArena(global);

  if(global->rememberedSummaryIndex == 0) {
    void *p;
    RememberedSummaryBlock newBlock;
    int res;

    res = ControlAlloc(&p, arena, sizeof *newBlock, 0);
    if(res != ResOK) {
      return res;
    }
    newBlock = p;
    rememberedSummaryBlockInit(newBlock);
    RingAppend(GlobalsRememberedSummaryRing(global),
      &newBlock->globalRing);
  }
  block = RING_ELT(RememberedSummaryBlock, globalRing,
    RingPrev(GlobalsRememberedSummaryRing(global)));
  AVER(global->rememberedSummaryIndex < RememberedSummaryBLOCK);
  AVER(block->the[global->rememberedSummaryIndex].base == (Addr)0);
  AVER(block->the[global->rememberedSummaryIndex].summary == RefSetUNIV);
  block->the[global->rememberedSummaryIndex].base = base;
  block->the[global->rememberedSummaryIndex].summary = summary;
  ++ global->rememberedSummaryIndex;
  if(global->rememberedSummaryIndex >= RememberedSummaryBLOCK) {
    AVER(global->rememberedSummaryIndex == RememberedSummaryBLOCK);
    global->rememberedSummaryIndex = 0;
  }

  return ResOK;
}

/* ArenaExposeRemember -- park arena and then lift all protection
   barriers.  Parameter 'remember' specifies whether to remember the
   protection state or not (for later restoration with
   ArenaRestoreProtection).
   */
void ArenaExposeRemember(Globals globals, int remember)
{
  Seg seg;
  Arena arena;

  AVERT(Globals, globals);

  ArenaPark(globals);

  arena = GlobalsArena(globals);
  if(SegFirst(&seg, arena)) {
    Addr base;

    do {
      base = SegBase(seg);
      if(IsSubclassPoly(ClassOfSeg(seg), GCSegClassGet())) {
        if(remember) {
          RefSet summary;

          summary = SegSummary(seg);
          if(summary != RefSetUNIV) {
            Res res = arenaRememberSummaryOne(globals, base, summary);
            if(res != ResOK) {
              /* If we got an error then stop trying to remember any
              protections. */
              remember = 0;
            }
          }
        }
        SegSetSummary(seg, RefSetUNIV);
        AVER(SegSM(seg) == AccessSetEMPTY);
      }
    } while(SegNext(&seg, arena, seg));
  }
}

void ArenaRestoreProtection(Globals globals)
{
  Ring node, next;
  Arena arena;

  arena = GlobalsArena(globals);

  RING_FOR(node, GlobalsRememberedSummaryRing(globals), next) {
    RememberedSummaryBlock block =
      RING_ELT(RememberedSummaryBlock, globalRing, node);
    size_t i;

    for(i = 0; i < RememberedSummaryBLOCK; ++ i) {
      Seg seg;
      Bool b;

      if(block->the[i].base == (Addr)0) {
        AVER(block->the[i].summary == RefSetUNIV);
        continue;
      }
      b = SegOfAddr(&seg, arena, block->the[i].base);
      if(b && SegBase(seg) == block->the[i].base) {
        AVER(IsSubclassPoly(ClassOfSeg(seg), GCSegClassGet()));
        SegSetSummary(seg, block->the[i].summary);
      } else {
        /* Either seg has gone or moved, both of which are */
        /* client errors. */
        NOTREACHED;
      }
    }
  }

  arenaForgetProtection(globals);
}

static void arenaForgetProtection(Globals globals)
{
  Ring node, next;
  Arena arena;

  arena = GlobalsArena(globals);
  /* Setting this early means that we preserve the invariant
     <code/global.c#remembered.summary> */
  globals->rememberedSummaryIndex = 0;
  RING_FOR(node, GlobalsRememberedSummaryRing(globals), next) {
    RememberedSummaryBlock block =
      RING_ELT(RememberedSummaryBlock, globalRing, node);

    RingRemove(node);
    ControlFree(arena, block, sizeof *block);
  }
}

/* C. COPYRIGHT AND LICENSE
 *
 * Copyright (C) 2001-2013 Ravenbrook Limited
 * <http://www.ravenbrook.com/>.
 * All rights reserved.  This is an open source license.  Contact
 * Ravenbrook for commercial licensing options.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * 3. Redistributions in any form must be accompanied by information on how
 * to obtain complete source code for this software and any accompanying
 * software that uses this software.  The source code must either be
 * included in the distribution or be available for no more than the cost
 * of distribution plus a nominal fee, and must be freely redistributable
 * under reasonable conditions.  For an executable file, complete source
 * code means the source code for all modules it contains. It does not
 * include source code for modules or files that typically accompany the
 * major components of the operating system on which the executable file
 * runs.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
