/**********************************************************************
 * Copyright (c) 2005, 2007 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: ossramboinl.h,v 1.4 2007/03/19 03:14:37 rsmith Exp $
 * 
 * Contributors: 
 * IBM - Initial API and implementation
 **********************************************************************/

/*******************************************************************************

   Source File Name = ossrambo.h 

   Descriptive Name = OSSe RApid Memory Buffer Out (RAMBO)

   Function:
      Defines:

   Dependencies:
      None

   Restrictions:
      None

   Change Activity:
   Defect Date        Who Description
   ====== =========== === ==============================================
   164204 Jan/15/2001 jpk Initial drop
   170752 Apr/25/2001 dns Porting changes for MVS
   172892 May/01/2001 dns added ossRamboMarkSlotsEmpty
   186134 Jan/25/2002 dns integrated ossatomic types and operations
   216026 Jul/23/2002 dns Added ossRamboIsAutoFlusherStopping

   Last Changed =    02/08/27  12:31:38

*******************************************************************************/

#ifndef OSSRAMBOINL_HEADER_INCLUDED
#define OSSRAMBOINL_HEADER_INCLUDED

/*
#include "oss.h"
#include "osserror.h"
#include "ossdebug.h"
#include "osstime.h"
#include "ossipcmemory.h"
*/

/* 186134 DNS - all atomic operations are defined in ossatomic.h now so the
                implementations below are not required.
*/
#include "ossatomic.h"      /* 186134 */
#ifdef SQLLinux
#include <stdint.h>         /* 186135 - for uintptr_t definition */
#endif

/******************************************************************************
   BEGIN TEMPORARY CODE
******************************************************************************/

/* Atomic instructions */
#ifdef ENW
/*
#define FAST_ATOMIC_FUNCTIONS 1
#include "sqloAtomic.h"
#define ossAtomicIncAndRet32(a)         sqloAtomicIncAndRet32(a)
#define ossAtomicIncByValAndRet32(a, b) sqloAtomicIncByValAndRet32(a,b)
*/
// #define ossAtomicCompareAndSwap(a,b,c)

/* 186134 DNS - don't include the following implementations because they are replaced
                by functions in ossatomic.h
*/
#if 0
   #if defined SQLWINT || defined _WIN32
   inline bool ossAtomicCompareAndSwap(
      OSSAtomic * addr,
      OSSRegister oldValue,
      OSSRegister newValue )      /* 186134 */
   {
   	_asm
	   {
	   	mov	ecx,	addr		// load address,
		   mov	eax,	oldValue	// load the old value
		   mov	edx,	newValue	// load the old value

	      lock	cmpxchg	[ecx],	edx	// after exchange eax(return
	      lahf
	      shr eax, 14
	  	   and eax, 1
		   xor eax, 1
   	}
   }
   /* 170752 begin */
   #elif defined MVS
   #define ossAtomicCompareAndSwap(a,b,c)  cs(&b,a,c)
   #elif defined _AIX
   #include <sys/atomic_op.h>
   inline bool ossAtomicCompareAndSwap(
      OSSAtomic * addr,
      OSSRegister oldValue,
      OSSRegister newValue )      /* 186134 */
   {
      return ( _check_lock( (atomic_p)addr, oldValue, newValue ) ) ;
   }
   #else
   /* #elif defined SQLUNIX */
   /* #elif defined SQLLinux */
   /* To Do - proper implementation of the compare and swap
           for other platforms
   */
   /*===============  Start Temporary Code  ===============================*/
   inline bool ossAtomicCompareAndSwap(
      OSSAtomic * addr,
      OSSRegister oldValue,
      OSSRegister newValue )      /* 186134 */
   {
      if ( *addr == oldValue ) {
         *addr = newValue;
         return false;
      }

      return true;
   }
   /*===============  End Temporary Code  =================================*/
   #endif
   /* 170752 end */
#endif  /* 0 */
#else  /* ENW */
#ifdef _AIX
#include <sys/atomic_op.h>
#endif

typedef Uint32 sqlo_register ;
typedef sqlo_register sqlo_atomic ;

inline sqlo_register ossAtomicIncAndRet32( sqlo_atomic * addr )
{
   sqlo_register retVal = 0 ;
   do
   {
      retVal = *addr ;
   } while( _check_lock( (atomic_p)addr, retVal, retVal+1 ) ) ;
   return retVal;
}

inline sqlo_register ossAtomicIncByValAndRet32(
      sqlo_atomic * addr,
      sqlo_register val )
{
   sqlo_register retVal = 0 ;
   do
   {
      retVal = *addr ;
   } while( _check_lock( (atomic_p)addr, retVal, retVal+val ) ) ;
   return retVal;
}

inline bool ossAtomicCompareAndSwap(
      sqlo_atomic * addr,
      sqlo_register oldValue,
      sqlo_register newValue )
{
   return ( _check_lock( (atomic_p)addr, oldValue, newValue ) ) ;
}
#endif
/******************************************************************************
   END TEMPORARY CODE
******************************************************************************/

/* Alignment macro */
#define ossAlignP(x) ossAlign4(x)
#ifdef MVS
#define ossAlign4( i ) ( ( i + 3 ) & ~(uintptr_t)3 )
#elif __OS400__ //230912: use __OS400__ instead since we didn't include oss.h
inline Uint32 ossAlign4( Uint32 i )
{
   return ( ( i + 3 ) & ~(Uint32)3 ) ;
}
#else
inline uintptr_t ossAlign4( uintptr_t i )
{
   return ( ( i + 3 ) & ~(uintptr_t)3 ) ;
}
#endif

/*******************************************************************************

   Function Name
      ossRamboIsInitialized

   Function
      Returns true if the specified RAMBO buffer has been initialized.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsInitialized( OSSRamboCB * pRamboCB )
#else
#pragma inline(ossRamboIsInitialized)
int ossRamboIsInitialized( OSSRamboCB * pRamboCB )
#endif
{
   OSS_ASSERT( NULL != pRamboCB ) ;
   return ( pRamboCB->l2.h2.status & OSS_RAMBO_CREATED ) ;
}


/*******************************************************************************

   Function Name
      ossRamboIsValidOnDiskCB

   Function
      Returns true if the specified RAMBO on-disk control block is valid.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsValidOnDiskCB( OSSRamboOnDiskCB * pOnDiskCB )
#else
#pragma inline(ossRamboIsValidOnDiskCB)
int ossRamboIsValidOnDiskCB( OSSRamboOnDiskCB * pOnDiskCB )
#endif
{
   bool valid = true ;

   OSS_ASSERT( NULL != pOnDiskCB ) ;

   /* Ensure the eye catcher is valid */
   if ( 0 != memcmp( pOnDiskCB,
                     OSS_RAMBO_ONDISKCB_EYE,
                     OSS_RAMBO_EYE_CATCHER_SIZE ) )
   {
      valid = false ;
   }
   return valid ;
}


/*******************************************************************************

   Function Name
      ossRamboIsValidChunkCB

   Function
      Returns true if the specified RAMBO chunk control block is valid.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsValidChunkCB(
      OSSRamboChunkCB * pChunkCB,
      OSSRamboCB * pRamboCB )
#else
#pragma inline(ossRamboIsValidChunkCB)
int ossRamboIsValidChunkCB(
      OSSRamboChunkCB * pChunkCB,
      OSSRamboCB * pRamboCB )
#endif
{
   bool valid = true ;
   uintptr_t min ;
   uintptr_t max ;

   OSS_ASSERT( NULL != pChunkCB ) ;
   OSS_ASSERT( NULL != pRamboCB ) ;

   /* Valid chunk control block address ranges */
/* DNS
   min = (uintptr_t)pRamboCB->l2.h2.pFirstChunkCB ;
   max = (uintptr_t)pRamboCB->l2.h2.pFirstChunkCB
       + pRamboCB->l2.h2.chunkCBSize ;
*/
   min = (uintptr_t)pFirstChunkCB(pRamboCB) ;
   max = (uintptr_t)pFirstChunkCB(pRamboCB)
       + pRamboCB->l2.h2.chunkCBSize ;

   /* Ensure the chunk control block's address and eye catcher are valid */
   if ( (uintptr_t)pChunkCB < min )
   {
      valid = false ;
   }
   else if ( (uintptr_t)pChunkCB > max )
   {
      valid = false ;
   }
   else if ( 0 != memcmp( pChunkCB,
                          OSS_RAMBO_CHUNKCB_EYE,
                          OSS_RAMBO_EYE_CATCHER_SIZE ) )
   {
      valid = false ;
   }

   return valid ;
}


/*******************************************************************************

   Function Name
      ossRamboIsValidSlot

   Function
      Returns true if the specified RAMBO buffer slot is valid.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsValidSlot(
      void * slotAddress,
      OSSRamboCB * pRamboCB )
#else
#pragma inline(ossRamboIsValidSlot)
int ossRamboIsValidSlot(
      void * slotAddress,
      OSSRamboCB * pRamboCB )
#endif
{
   bool valid = true ;
   uintptr_t min ;
   uintptr_t max ;

   OSS_ASSERT( NULL != slotAddress ) ;
   OSS_ASSERT( NULL != pRamboCB ) ;

   /* Valid range of slot addresses */
/* DNS
   min = (uintptr_t)pRamboCB->l2.h2.pMemBuffer ;
   max = (uintptr_t)pRamboCB->l2.h2.pMemBuffer
       + pRamboCB->l2.h2.pOnDiskCB->bufferSize ;
*/
   min = (uintptr_t)pMemBuffer(pRamboCB) ;
   max = (uintptr_t)pMemBuffer(pRamboCB)
       + pOnDiskCB(pRamboCB)->bufferSize ;

   if ( (uintptr_t)slotAddress < min )
   {
      valid = false ;
   }
   else if ( (uintptr_t)slotAddress > max )
   {
      valid = false ;
   }

   return valid ;
}


/*******************************************************************************

   Function Name
      ossRamboMarkSlotsFilled

   Function
      Marks the specified number of slots (numSlotsFilled) starting at
      slotIndex as filled in the chunk control block (pChunkCB).

 ******************************************************************************/
#ifdef __cplusplus
inline void ossRamboMarkSlotsFilled(
      OSSRamboChunkCB * pChunkCB,
      Nuint slotIndex,
      Nuint numSlotsFilled )
#else
#pragma inline(ossRamboMarkSlotsFilled)
void ossRamboMarkSlotsFilled(
      OSSRamboChunkCB * pChunkCB,
      Nuint slotIndex,
      Nuint numSlotsFilled )
#endif
{
   Nuint arrayIndex ;
   Nuint bitShift ;
   Nuint slotsToMark ;
   OSSRegister oldValue ;      /* 186134 */
   OSSRegister newValue ;      /* 186134 */

   OSS_ASSERT( NULL != pChunkCB ) ;
   OSS_ASSERT( slotIndex < OSS_RAMBO_SLOTS_PER_CHUNK ) ;
   OSS_ASSERT( numSlotsFilled > 0 ) ;
   OSS_ASSERT( numSlotsFilled <= OSS_RAMBO_SLOTS_PER_CHUNK ) ;

   /* Flip the bit for each slot that was filled */
   slotsToMark = numSlotsFilled ;
   do
   {
      arrayIndex = slotIndex / OSS_RAMBO_SLOT_BITMAP_SIZE ;
      bitShift   = slotIndex % OSS_RAMBO_SLOT_BITMAP_SIZE ;

      do
      {
         oldValue = ossAtomicPeek(&(pChunkCB->slotMap[arrayIndex])) ;
         newValue = oldValue | ( 1 << bitShift ) ;
      } while ( !ossAtomicCompareAndPoke( &(pChunkCB->slotMap[arrayIndex]),
                                         oldValue, newValue ) ) ;

      slotsToMark-- ;
      slotIndex++ ;
   } while ( slotsToMark > 0 ) ;

   /* Increase the fill count */

#ifdef ENW
   ossAtomicIncByValAndRet( &pChunkCB->fillCount, numSlotsFilled ) ;
#else
   pChunkCB->fillCount += numSlotsFilled ;
#endif
}

/* 172892 begin */
/*******************************************************************************

   Function Name
      ossRamboMarkSlotsEmpty

   Function
      Marks the specified number of slots (numSlotsEmpty) starting at
      slotIndex as empty in the chunk control block (pChunkCB).

 ******************************************************************************/
#ifdef __cplusplus
inline void ossRamboMarkSlotsEmpty(
      OSSRamboChunkCB * pChunkCB,
      Nuint slotIndex,
      Nuint numSlotsEmpty )
#else
#pragma inline(ossRamboMarkSlotsEmpty)
void ossRamboMarkSlotsEmpty(
      OSSRamboChunkCB * pChunkCB,
      Nuint slotIndex,
      Nuint numSlotsEmpty )
#endif
{
   Nuint arrayIndex ;
   Nuint endArrayIndex ;
   Nuint bitShift ;
   Nuint numArrayElements ;
   Nuint endSlotIndex ;
   OSSRegister oldValue ;      /* 186134 */
   OSSRegister newValue ;      /* 186134 */

   OSS_ASSERT( NULL != pChunkCB ) ;
   OSS_ASSERT( slotIndex < OSS_RAMBO_SLOTS_PER_CHUNK ) ;
   OSS_ASSERT( numSlotsEmpty > 0 ) ;
   OSS_ASSERT( numSlotsEmpty <= OSS_RAMBO_SLOTS_PER_CHUNK ) ;

   /* Flip the bit for each slot that was flushed */
   arrayIndex = slotIndex / OSS_RAMBO_SLOT_BITMAP_SIZE ;
   endSlotIndex = slotIndex + numSlotsEmpty - 1;
   endArrayIndex = endSlotIndex / OSS_RAMBO_SLOT_BITMAP_SIZE;
   bitShift = endSlotIndex % OSS_RAMBO_SLOT_BITMAP_SIZE;

   numArrayElements = endArrayIndex - arrayIndex;

   if (bitShift == OSS_RAMBO_SLOT_BITMAP_SIZE - 1)
      numArrayElements++;

   /* zero the whole first bitmap and subseqent whole bitmaps in the range */
   if ( numArrayElements > 0 ) {
/* 186134 begin
      memset( (void *)&pChunkCB->slotMap[arrayIndex], 0x00, numArrayElements * OSS_RAMBO_SLOT_BITMAP_SIZE / 8 ) ;
*/
      unsigned int i;
      for(i=arrayIndex; i < endArrayIndex; i++) {
         ossAtomicPoke(&(pChunkCB->slotMap[i]), 0);
      }
   }
/* 186134 end */

   if (bitShift < OSS_RAMBO_SLOT_BITMAP_SIZE - 1) {
   /* zero the part of the last bitmap in the range that is empty */
      unsigned int i;
      OSSRegister bitmap = 1;      /* 186134 */

      for (i=1; i <= bitShift; i++)
         bitmap = bitmap | ( 1 << i ) ;

      do
      {
         oldValue = ossAtomicPeek(&(pChunkCB->slotMap[endArrayIndex])) ;
         newValue = oldValue & ~bitmap;
      } while ( !ossAtomicCompareAndPoke( &(pChunkCB->slotMap[endArrayIndex]),
                                         oldValue, newValue ) ) ;
   }

   /* Increase the flushed count */

   ossAtomicIncByValAndRet( &pChunkCB->flushedCount, numSlotsEmpty ) ;
/*   pChunkCB->flushedCount += numSlotsEmpty ;   186134 */
}
/* 172892 end */

#ifdef __cplusplus
inline bool ossRamboIsSlotFull(
      OSSRamboChunkCB * pChunkCB,
      Nuint slotIndex )
#else
#pragma inline(ossRamboIsSlotFull)
int ossRamboIsSlotFull(
      OSSRamboChunkCB * pChunkCB,
      Nuint slotIndex )
#endif
{
   Nuint arrayIndex ;
   Nuint bitShift ;

   OSS_ASSERT( NULL != pChunkCB ) ;
   OSS_ASSERT( slotIndex < OSS_RAMBO_SLOTS_PER_CHUNK ) ;

   arrayIndex = slotIndex / OSS_RAMBO_SLOT_BITMAP_SIZE ;
   bitShift   = slotIndex % OSS_RAMBO_SLOT_BITMAP_SIZE ;

   return ( ossAtomicPeek(&pChunkCB->slotMap[arrayIndex]) & (1 << bitShift) ) ;
}


/*******************************************************************************

   Function Name
      ossRamboIsAutoFlusherStarted

   Function
      Returns true if the RAMBO auto-flusher process has been started.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsAutoFlusherStarted( OSSRamboCB * pRamboCB )
#else
#pragma inline(ossRamboIsAutoFlusherStarted)
int ossRamboIsAutoFlusherStarted( OSSRamboCB * pRamboCB )
#endif
{
   OSS_ASSERT( NULL != pRamboCB ) ;
   return ( pRamboCB->l2.h2.status & OSS_RAMBO_AUTOF_STARTED ) ;
}

/* 216026 begin */
/*******************************************************************************

   Function Name
      ossRamboIsAutoFlusherStopping

   Function
      Returns true if the RAMBO auto-flusher process is about to be stopped.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsAutoFlusherStopping( OSSRamboCB * pRamboCB )
#else
#pragma inline(ossRamboIsAutoFlusherStopping)
int ossRamboIsAutoFlusherStopping( OSSRamboCB * pRamboCB )
#endif
{
   OSS_ASSERT( NULL != pRamboCB ) ;
   return ( pRamboCB->l2.h2.status & OSS_RAMBO_AUTOF_STOP_NOW ) ;
}
/* 216026 end */


/*******************************************************************************

   Function Name
      ossRamboIsFlushing

   Function
      Returns true if the RAMBO is currently being flushed.
      Otherwise returns false.

 ******************************************************************************/
#ifdef __cplusplus
inline bool ossRamboIsFlushing( OSSRamboCB * pRamboCB )
#else
#pragma inline(ossRamboIsFlushing)
int ossRamboIsFlushing( OSSRamboCB * pRamboCB )
#endif
{
   OSS_ASSERT( NULL != pRamboCB ) ;
   return ( pRamboCB->l2.h2.status & OSS_RAMBO_FLUSHING ) ;
}


#endif
