Source

blake / blake_fastqueue.c

Full commit
/*
Copyright (C) 2001-2006 Daybo Logic.
All rights reserved.

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. Neither the name of the project nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR 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.
*/
/*
  Blake Fast Queue service.
*/

#define BLAKE_SOURCE

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /*HAVE_CONFIG_H*/

#include <assert.h>
#include <time.h>
#include <stdio.h>

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifdef HAVE_STRING_H
# include <string.h>
#endif

#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif

#ifdef BLAKE_HDRSTOP
# pragma hdrstop
#endif /*BLAKE_HDRSTOP*/

#ifdef HAVE_DLSTDINC_H
# include "dlstdinc.h"
#endif

#ifdef HAVE_DPCRTLMM_H
# include "dpcrtlmm.h"
#endif

#include "blake_threads.h"
#include "blake.h" /* Public header */
#include "blake_fastqueue.h"
#include "blake_main.h"
/*-------------------------------------------------------------------------*/
static const char *rcsid =
  "$Id: blake_fastqueue.c,v 1.1 2006/03/19 16:42:00 ddrp Exp $";

#define QUEUE_NOT_INITSIG (0U)
#define QUEUE_INITSIG (0x1234)

static bool InitQueue(struct blakeFastQueue *PFastQueue);
static void DestroyQueue(struct blakeFastQueue *PFastQueue);
#ifdef BLAKE_THREADS
  static void LockQueue(struct blakeFastQueue *PQueue);
  static void UnlockQueue(struct blakeFastQueue *PQueue);
#else
  /* Fake locking, does nothing useful */
# define LockQueue(pq)   blake_Unused(rcsid)
# define UnlockQueue(pq) blake_Unused(rcsid)
#endif /*BLAKE_THREADS*/
/*-------------------------------------------------------------------------*/
void blake_int_fastqueue_NCI()
{
  blake_Unused(rcsid);
}
/*-------------------------------------------------------------------------*/
BLAKEAPI(enum blakeError) blake_InitFastQueue(
  struct blakeFastQueue *PQueue,
  const unsigned int UBound,
  const size_t ElemSize
)
{
  /* Do standard error tests */
  if ( !blake_int_IsInited() )
    return blakeNotStarted;

  if ( !PQueue || !ElemSize )
    return blakeInvalidParam;

  /*
    NOTE: It's OK if there is only one member of the queue
    (though somewhat stupid)
  */

  memset(PQueue, 0, sizeof(struct blakeFastQueue));
  PQueue->ElemSize = ElemSize;
  PQueue->UBound = UBound;
  if ( !InitQueue(PQueue) ) {
    DestroyQueue(PQueue);
    return blakeNotEnoughMem;
  }

  return blakeSuccess;
}
/*-------------------------------------------------------------------------*/
BLAKEAPI(enum blakeError) blake_PushFastQueue(
  struct blakeFastQueue *PQueue,
  const void *NewItem
)
{
  unsigned int endOfQueue;
  enum blakeError ret = blakeSuccess;
  struct blakeFastQueueInternal* itemPtr;

  /* Do standard error tests */
  if ( !blake_int_IsInited() )
    return blakeNotStarted;

  if ( !PQueue || !NewItem )
    return blakeNotStarted;

  LockQueue(PQueue);
  if ( PQueue->InitSig != QUEUE_INITSIG ) {
    ret = blakeBadObject;
    goto releaseExit;
  }

  endOfQueue = PQueue->Count;
  if ( endOfQueue == PQueue->UBound + 1 ) {
    ret = blakeQueueFull;
    goto releaseExit;
  }

  /* I need to know the space was allocated to copy from,
  if there is no pointer I cannot die with an assertion because
  it's he user's fault not mine.  I'll exit with a catch all error. */

  itemPtr = PQueue->Base + endOfQueue;
  if ( !itemPtr->Dataptr || !PQueue->ElemSize ) {
    ret = blakeUnknownFailure;
    goto releaseExit;
  }

  memcpy(itemPtr->Dataptr, NewItem, PQueue->ElemSize);
  itemPtr->Used = true;
  PQueue->Count++;
  /* That's it */

releaseExit:
  UnlockQueue(PQueue);
  return ret;
}
/*-------------------------------------------------------------------------*/
BLAKEAPI(enum blakeError) blake_TopFastQueue(
  struct blakeFastQueue *PQueue,
  void *PReceiveItem,
  const size_t SizeItem
)
{
  enum blakeError err = blakeSuccess;

  if ( !blake_int_IsInited() ) return blakeNotStarted;
  if ( !PQueue || !PReceiveItem || !SizeItem )
    return blakeInvalidParam;

  LockQueue(PQueue);
  if ( PQueue->InitSig != QUEUE_INITSIG ) {
    err = blakeBadObject;
    goto releaseExit;
  }

  if ( !PQueue->Count ) {
    err = blakeQueueEmpty;
    goto releaseExit;
  }

  /* Right, we're all safe, let's go */
  memset(PReceiveItem, 0, SizeItem);
  if ( !PQueue->Base->Used || !PQueue->Base->Dataptr ) {
    err = blakeUnknownFailure;
    goto releaseExit;
  }
  memcpy(
    PReceiveItem,
    PQueue->Base->Dataptr,
    DPCRTLMM_MIN(PQueue->ElemSize, SizeItem)
  );

releaseExit:
  UnlockQueue(PQueue);
  return err;
}
/*-------------------------------------------------------------------------*/
BLAKEAPI(enum blakeError) blake_PopFastQueue(
  struct blakeFastQueue* PQueue
)
{
  enum blakeError ret = blakeSuccess;
  void* poptmp; /* Holds the raw data pointer for an element as it is moved */
  unsigned int i;

  if ( !blake_int_IsInited() ) return blakeNotStarted;
  if ( !PQueue ) return blakeInvalidParam;

  LockQueue(PQueue);
  if ( PQueue->InitSig != QUEUE_INITSIG ) {
    ret = blakeBadObject;
    goto releaseExit;
  }

  if ( !PQueue->Count ) {
    ret = blakeQueueEmpty;
    goto releaseExit;
  }

  /* Bound check */
  if ( PQueue->Count - 1 > PQueue->UBound ) {
    ret = blakeUnknownFailure;
    goto releaseExit;
  }

  for ( i = 0U; i <= PQueue->UBound; i++ ) {
    struct blakeFastQueueInternal* itemPtr = PQueue->Base + i;
    if ( i == 0 ) /* Item to pop */
      poptmp = itemPtr->Dataptr; /* Save this until we reach the top */

    if ( i < PQueue->UBound ) {
      /* Shift down */
      memcpy(itemPtr, itemPtr + 1, sizeof(struct blakeFastQueueInternal));
    }
    else { /* Very last item */
      itemPtr->Used = false;
      /* Restore data pointer from the popped element */
      itemPtr->Dataptr = poptmp;
    }
  }
  PQueue->Count--;

releaseExit:
  UnlockQueue(PQueue);
  return ret;
}
/*-------------------------------------------------------------------------*/
BLAKEAPI(unsigned int) blake_CountFastQueue(
  struct blakeFastQueue* PQueue
)
{
  unsigned int count = 0;

  if ( !blake_int_IsInited() || !PQueue ) return count;

  LockQueue(PQueue);
  if ( PQueue->InitSig == QUEUE_INITSIG )
    count = PQueue->Count;

  UnlockQueue(PQueue);
  return count;
}
/*-------------------------------------------------------------------------*/
BLAKEAPI(enum blakeError) blake_DestroyFastQueue(
  struct blakeFastQueue* PQueue
)
{
  enum blakeError ret = blakeSuccess;

  if ( !blake_int_IsInited() ) return blakeNotStarted;
  if ( !PQueue ) return blakeInvalidParam;

  if ( PQueue->InitSig == QUEUE_INITSIG )
    DestroyQueue(PQueue);
  else
    ret = blakeBadObject;

  return ret;
}
/*-------------------------------------------------------------------------*/
static bool InitQueue(struct blakeFastQueue* PFastQueue)
{
  assert(PFastQueue);
  if ( PFastQueue ) {
    unsigned int i;
    size_t baseSize =
      sizeof(struct blakeFastQueueInternal) * (PFastQueue->UBound+1);

    PFastQueue->MemoryDebugger = dpcrtlmm_CreateBlockArray();
    if ( !PFastQueue->MemoryDebugger ) return false;

    #ifdef BLAKE_THREADS
      daybothreads_CreateLock(&PFastQueue->Lock);
      PFastQueue->LockCreated = true;
    #endif /*BLAKE_THREADS*/

    PFastQueue->Base =
      (struct blakeFastQueueInternal*)dpcrtlmm_Alloc(
        PFastQueue->MemoryDebugger,
        baseSize
      );

    if ( !PFastQueue->Base ) return false;

    /* Need this so that the destroyer works safely */
    memset(PFastQueue->Base, 0, baseSize);
    for ( i = 0U; i <= PFastQueue->UBound; i++ ) {
      struct blakeFastQueueInternal *PIndex;

      PIndex = PFastQueue->Base + i;
      assert(PFastQueue->ElemSize);

      PIndex->Dataptr =
        dpcrtlmm_Alloc(
          PFastQueue->MemoryDebugger,
          PFastQueue->ElemSize
        );

      if ( !PIndex->Dataptr ) return false;
    }
    PFastQueue->InitSig = QUEUE_INITSIG; /* Mark as Initialised */
    return true; /* Success */
  }
  return false; /* No pointer sent */
}
/*-------------------------------------------------------------------------*/
static void DestroyQueue(
  struct blakeFastQueue *PFastQueue
)
{
  assert(PFastQueue);
  if ( PFastQueue ) {
    unsigned int i;

    if ( PFastQueue->MemoryDebugger && PFastQueue->Base ) {
      for ( i = 0U; i <= PFastQueue->UBound; i++ ) {
        struct blakeFastQueueInternal* PIndex;

        PIndex = PFastQueue->Base + i;
        if ( PIndex->Dataptr )
          dpcrtlmm_Free(PFastQueue->MemoryDebugger, PIndex->Dataptr);
      }
    }

    if ( PFastQueue->MemoryDebugger && PFastQueue->Base ) {
      dpcrtlmm_Free(PFastQueue->MemoryDebugger, PFastQueue->Base);
      PFastQueue->Base = NULL;
    }

    PFastQueue->Count = 0;
    PFastQueue->ElemSize = 0;
    if ( PFastQueue->MemoryDebugger ) {
      dpcrtlmm_DestroyBlockArray(PFastQueue->MemoryDebugger);
      PFastQueue->MemoryDebugger = NULL;
    }

    #if defined(BLAKE_THREADS)
    if ( PFastQueue->LockCreated ) {
      daybothreads_DestroyLock(&PFastQueue->Lock);
      PFastQueue->LockCreated = (blake_bool_t)!PFastQueue->LockCreated;
    }
    #endif /*BLAKE_THREADS*/

    PFastQueue->UBound = 0U;
    PFastQueue->InitSig = QUEUE_NOT_INITSIG;
  }
}
/*-------------------------------------------------------------------------*/
#ifdef BLAKE_THREADS
static void LockQueue(struct blakeFastQueue *PQueue)
{
  assert(PQueue);
  assert(PQueue->LockCreated);
  daybothreads_EnterLock(&PQueue->Lock);
}
#endif /*BLAKE_THREADS*/
/*-------------------------------------------------------------------------*/
#ifdef BLAKE_THREADS
static void UnlockQueue(struct blakeFastQueue *PQueue)
{
  assert(PQueue);
  assert(PQueue->LockCreated);
  daybothreads_LeaveLock(&PQueue->Lock);
}
#endif /*BLAKE_THREADS*/
/*-------------------------------------------------------------------------*/