
#include "RobboLito.h"
#include "RobboTripleBase.h"
#include "RobboTotalBase.h"

static TripleBaseCache* TRIPLE_CACHE[4];
static volatile uint64 *TRIPLE_CACHE_NOTE[4];
static MUTEX_TYPE TRIPLE_BASE_DISK_ACCESS[1];
static uint64 TRIPLE_CACHE_NUM = 0;
static uint64 CURRENT_TRIPLE_CACHE_SIZE = 0;

MUTEX_TYPE TRIPLE_BASE_USAGE[1];

typedef struct { RobboTripleBase* rtb; int e, he; uint64 offset, meld; } typeTNG;
static typeTNG NEXT_GET[1];

int SetTripleBaseCache (int mb)
{
  int i, j;
  mb = 1 << BSR (mb);
  TRIPLE_CACHE_NUM = mb;
  for (i = 0; i < 4; i++)
    if (TRIPLE_CACHE_NOTE[i])
      free (TRIPLE_CACHE_NOTE[i]);
  for (i = 0; i < 4; i++)
    if (TRIPLE_CACHE[i])
      {
        for (j = 0; j < CURRENT_TRIPLE_CACHE_SIZE; j++)
          if (TRIPLE_CACHE[i][j].DATA)
            ALIGNED_FREE (TRIPLE_CACHE[i][j].DATA);
        for (j = 0; j < CURRENT_TRIPLE_CACHE_SIZE; j++)
          if (TRIPLE_CACHE[i][j].DATA)
            ALIGNED_FREE (TRIPLE_CACHE[i][j].INDEX);
        free (TRIPLE_CACHE[i]);
      }
  TRIPLE_CACHE_NOTE[0] = malloc (TRIPLE_CACHE_NUM * sizeof (uint64));
  TRIPLE_CACHE_NOTE[1] = malloc (TRIPLE_CACHE_NUM * sizeof (uint64));
  TRIPLE_CACHE_NOTE[2] = malloc (TRIPLE_CACHE_NUM * sizeof (uint64));
  TRIPLE_CACHE_NOTE[3] = malloc (TRIPLE_CACHE_NUM * sizeof (uint64));
  TRIPLE_CACHE[0] = malloc (TRIPLE_CACHE_NUM * sizeof (TripleBaseCache));
  TRIPLE_CACHE[1] = malloc (TRIPLE_CACHE_NUM * sizeof (TripleBaseCache));
  TRIPLE_CACHE[2] = malloc (TRIPLE_CACHE_NUM * sizeof (TripleBaseCache));
  TRIPLE_CACHE[3] = malloc (TRIPLE_CACHE_NUM * sizeof (TripleBaseCache));
  for (j = 0; j < 4; j++)
    for (i = 0; i < TRIPLE_CACHE_NUM; i++)
      {
	TRIPLE_CACHE[j][i].DATA = NULL;
	TRIPLE_CACHE[j][i].INDEX = NULL;
	TRIPLE_CACHE[j][i].user = 0;
      }
  for (j = 0; j < 4; j++)
    for (i = 0; i < TRIPLE_CACHE_NUM; i++)
      TRIPLE_CACHE_NOTE[j][i] = 0xffffffff;
  CURRENT_TRIPLE_CACHE_SIZE = mb;
  SEND ("TripleCacheSize is %dmb\n", mb);
  return mb;
}

void InitInitTripleBaseCache (uint64 mb)
{
  int i;
  LOCK_INIT (TRIPLE_BASE_USAGE);
  LOCK_INIT (TRIPLE_BASE_DISK_ACCESS);
  for (i = 0; i < 4; i++)
    {
      TRIPLE_CACHE_NOTE[i] = NULL;
      TRIPLE_CACHE[i] = NULL;
    }
  SetTripleBaseCache (mb);
  NEXT_GET->rtb = NULL;
  NEXT_GET->e = 0;
  NEXT_GET->he = 0;
  NEXT_GET->offset = 0;
  NEXT_GET->meld = 0;
}

#define GetCache0(a, b, c) \
  ( (411 * (a) + 517 * (b) + 373 * (c)) & ( TRIPLE_CACHE_NUM - 1) )
#define GetCache1(a, b, c) \
  ( (725 * (a) + 309 * (b) + 611 * (c)) & ( TRIPLE_CACHE_NUM - 1) )
#define GetCache2(a, b, c) \
  ( (887 * (a) + 521 * (b) + 225 * (c)) & ( TRIPLE_CACHE_NUM - 1) )
#define GetCache3(a, b, c) \
  ( (921 * (a) + 1011 * (b) + 703 * (c)) & ( TRIPLE_CACHE_NUM - 1) )
#define GetCache(x ,a, b, c) \
  ((x) >= 2) ? (((x) == 2) ? GetCache2 (a, b, c) : GetCache3 (a, b, c)) \
             : (((x) == 1) ? GetCache1 (a, b, c) : GetCache0 (a, b, c))

#define MAXIMAL_FOPEN 64 /* HACK */ /* only triple */
static uint32 open_file_count = 0;
void OpenTripleFile (RobboTripleBase* rtb)
{
  int i;
  char STR[512];
  if (rtb->disk)
    return;
  open_file_count++;
  if (open_file_count == MAXIMAL_FOPEN)
    {
      for (i = 0; i < TRIPLE_MAX; i++)
	if (rtb->disk)
	  {
	    fclose (rtb->disk);
	    rtb->disk = NULL;
	  }
      open_file_count = 1;
    }
  sprintf (STR, "%s/%s", rtb->DIR_NOME, rtb->path);
  rtb->disk = fopen (STR, "rb");
}

static volatile uint32 TRIPLE_LOADED = 0;
#define CHECK_BYTE(x, y) \
  if (x != y) { SEND ("Bad Triple watermark %s %d %d %d\n", \
		      rtb->string, rtb->num, heap, slice); return FALSE; }
#define CHECK_BYTE0(x, y) \
  if (x != y) { SEND ("Bad Triple watermark (SMP) %s %d " TYPE_64_BIT "\n", \
		      rtb->string, rtb->num, meld); }

void OfftoneTripleSMP () /* HACK */ { NEXT_GET->rtb = NULL; }
boolean SubsumeTripleSMP ()
{
  RobboTripleBase* rtb;
  uint64 off_set, meld;
  int ind, bytes, e, he;
  if (!NEXT_GET->rtb)
    return FALSE;
  LOCK (TRIPLE_BASE_USAGE);
  rtb = NEXT_GET->rtb;
  off_set = NEXT_GET->offset;
  e = NEXT_GET->e;
  he = NEXT_GET->he;
  meld = NEXT_GET->meld;
  NEXT_GET->rtb = NULL; /* very */
  if (TRIPLE_CACHE[e][he].user)
    {
      UNLOCK (TRIPLE_BASE_USAGE);
      return FALSE;
    }
  TRIPLE_CACHE_NOTE[e][he] = 0xffffbad0; /* invalid */
  UNLOCK (TRIPLE_BASE_USAGE);
  LOCK (TRIPLE_BASE_DISK_ACCESS);
  if (!rtb->disk)
    OpenTripleFile (rtb);
  fseek (rtb->disk, off_set, SEEK_SET);
  CHECK_BYTE0 (fgetc (rtb->disk), 0xf2);
  CHECK_BYTE0 (fgetc (rtb->disk), 0x77);
  CHECK_BYTE0 (fgetc (rtb->disk), 0x83);
  CHECK_BYTE0 (fgetc (rtb->disk), 0x83);
  fread (&ind, 4, 1, rtb->disk);
  fread (&bytes, 4, 1, rtb->disk);
  fread (TRIPLE_CACHE[e][he].INDEX, 4, ind, rtb->disk);
  fread (TRIPLE_CACHE[e][he].DATA, 1, bytes, rtb->disk);
  UNLOCK (TRIPLE_BASE_DISK_ACCESS);
  LOCK (TRIPLE_BASE_USAGE);
  TRIPLE_CACHE_NOTE[e][he] = meld;
  UNLOCK (TRIPLE_BASE_USAGE);
  return TRUE;
}

boolean DemandDynamicTripleCache
(RobboTripleBase* rtb, int heap, int slice,
 TripleBaseCache* *CACHE, uint8* *DATA, uint32* *INDEX,
 boolean PROBING_WEAK, boolean PROBING_IMPALE)
{
  int i, h[4], e, bytes, ind;
  uint64 meld = (((uint64) rtb->num) << 48) | (((uint64) heap) << 32) | slice;
  if (rtb->direct == TRUE)
    {
      *CACHE = &rtb->direct_cache[heap]; /* HACK */
      *DATA = ((*CACHE)->DATA) + (0x40000 * slice);
      *INDEX = ((*CACHE)->INDEX) + (0x1220 * slice);
      return TRUE;
    }
 IGOR_REDO:
  for (i = 0; i < 4; i++)
    {
      h[i] = GetCache (i, rtb->num, heap, slice);
      if (TRIPLE_CACHE_NOTE[i][h[i]] == meld)
        {
	  LOCK (TRIPLE_BASE_USAGE);	  
	  if (TRIPLE_CACHE_NOTE[i][h[i]] != meld)
	    {
	      UNLOCK (TRIPLE_BASE_USAGE);
	      if (PROBING_IMPALE)
		goto IGOR_REDO;
	      return FALSE;
	    }
	  TRIPLE_CACHE[i][h[i]].user++;
	  *CACHE = &TRIPLE_CACHE[i][h[i]];	  
	  *DATA = (*CACHE)->DATA;
	  *INDEX = (*CACHE)->INDEX;
	  UNLOCK (TRIPLE_BASE_USAGE);
	  return TRUE;
        }
    }
  TRIPLE_LOADED++;
  e = -1;
  for (i = 0; i < 4; i++)
    if (TRIPLE_CACHE_NOTE[i][h[i]] == 0xffffffff)
      e = i;
  if (e == -1) /* sticky? */
    {
      e = 0;
      for (i = 0; i < 12; i += 3)
        e += (TRIPLE_LOADED >> i);
      e &= 3;
    }
  LOCK (TRIPLE_BASE_USAGE);
#define TRIPLE_CONDITION \
  (!TRIPLE_CACHE[e][h[e]].user && TRIPLE_CACHE_NOTE[e][h[e]] != 0xffffbad0)
  while (!TRIPLE_CONDITION) /* while, for the "break" */
    {
      e ^= 1;
      if (TRIPLE_CONDITION)
	break;
      e ^= 2;
      if (TRIPLE_CONDITION)
	break;
      e ^= 1;
      if (TRIPLE_CONDITION)
	break;
      UNLOCK (TRIPLE_BASE_USAGE);
      if (PROBING_IMPALE)
	goto IGOR_REDO;
      return FALSE;
    }
  if (!PROBING_WEAK)
    {
      TRIPLE_CACHE[e][h[e]].user = 1;
      TRIPLE_CACHE_NOTE[e][h[e]] = 0xffffbad0; /* invalid */
    }
  UNLOCK (TRIPLE_BASE_USAGE);
  if (!TRIPLE_CACHE[e][h[e]].DATA)
    MEMALIGN (TRIPLE_CACHE[e][h[e]].DATA, 0x40, 0x40000);
  if (!TRIPLE_CACHE[e][h[e]].INDEX)
    MEMALIGN (TRIPLE_CACHE[e][h[e]].INDEX, 0x40, 0x1220 * sizeof (uint32));
  /* attend SMP */
  if (PROBING_WEAK)
    {
      LOCK (TRIPLE_BASE_USAGE);
      if (TRIPLE_CACHE_NOTE[e][h[e]] == 0xffffbad0)
	{
	  UNLOCK (TRIPLE_BASE_USAGE);
	  return FALSE;
	}
      rtb->weak++;
      NEXT_GET->rtb = rtb;
      NEXT_GET->e = e;
      NEXT_GET->he = h[e];
      NEXT_GET->offset = rtb->packet_offset[heap][slice];
      NEXT_GET->meld = meld;
      UNLOCK (TRIPLE_BASE_USAGE);
      return FALSE;
    }
  LOCK (TRIPLE_BASE_DISK_ACCESS);
  if (!rtb->disk)
    OpenTripleFile (rtb);
  fseek (rtb->disk, rtb->packet_offset[heap][slice], SEEK_SET);
  CHECK_BYTE (fgetc (rtb->disk), 0xf2);
  CHECK_BYTE (fgetc (rtb->disk), 0x77);
  CHECK_BYTE (fgetc (rtb->disk), 0x83);
  CHECK_BYTE (fgetc (rtb->disk), 0x83);
  fread (&ind, 4, 1, rtb->disk);
  fread (&bytes, 4, 1, rtb->disk);
  fread (TRIPLE_CACHE[e][h[e]].INDEX, 4, ind, rtb->disk);
  fread (TRIPLE_CACHE[e][h[e]].DATA, 1, bytes, rtb->disk);
  UNLOCK (TRIPLE_BASE_DISK_ACCESS);
  *CACHE = &TRIPLE_CACHE[e][h[e]];
  *DATA = (*CACHE)->DATA;
  *INDEX = (*CACHE)->INDEX;
  LOCK (TRIPLE_BASE_USAGE);
  TRIPLE_CACHE_NOTE[e][h[e]] = meld;
  UNLOCK (TRIPLE_BASE_USAGE);
  return TRUE;
}
