/*
    (C) 1995 AROS - The Amiga Replacement OS
    $Id: copymem.c 1.1 1995/11/14 22:31:07 digulla Exp digulla $
    $Log: copymem.c $
 * Revision 1.1  1995/11/14  22:31:07  digulla
 * Initial revision
 *
    Desc:
    Lang: english
*/
#include "exec_intern.h"
#include <aros/machine.h>

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

    NAME */
	#include <exec/types.h>
	#include <clib/exec_protos.h>

	__AROS_LH3I(void, CopyMem,

/*  SYNOPSIS */
	__AROS_LA(APTR         , source, A0),
	__AROS_LA(APTR         , dest, A1),
	__AROS_LA(unsigned long, size, D0),

/*  LOCATION */
	struct ExecBase *, SysBase, 104, Exec)

/*  FUNCTION
	This is a fast memory to memory copy function. It copies
	arbitrary amounts of memory with arbitrary alignments. The
	routine tries to be smart and uses optimized copies if
	this is possible.

    INPUTS
	source - These bytes are copied to dest
	dest - This is the destination for size bytes starting at source
	size - This many bytes will be copied.

    RESULT
	None.

    NOTES
	The routine can't handle situations where the destination is
	somewhere in source (eg. insert elements in an array).

    EXAMPLE
	char ptr1[] = "Some text.";
	char ptr2[32];

	CopyMem (ptr1, ptr2, strlen (ptr1));

    BUGS

    SEE ALSO
	CopyMemQuick()

    INTERNALS

    HISTORY
	21-10-95    digulla automatically created from
			    include:clib/exec_protos.h
			    Written the code.
	26-10-95    digulla adjusted to new calling scheme
	17-12-95    digulla Incorporated code by Matthias Fleischner

*****************************************************************************/
{
    __AROS_FUNC_INIT
    UBYTE *src=(UBYTE *)source,*dst=(UBYTE *)dest;
    ULONG mis,low,high;

    /*
	I try to fall back to copying LONGs if possible. To do this I copy
	the misaligned leading bytes of the source first. I use sizeof(LONG)
	instead of LONGALIGN because it is sometimes faster.
    */
    mis = (ULONG)src&(sizeof(LONG)-1);
    if(mis>size)
	mis=size;
    size-=mis;

    if(mis)
      do
	*dst++=*src++;
      while(--mis);

    /*
	The source has the right alignment now. All I need to do is to
	check if this is true for the destination, too.
    */
    if(!((ULONG)dst&(LONGALIGN-1)))
    {
	/* Yes. I may copy LONGs. */
	LONG *s=(LONG *)src,*d=(LONG *)dst;
	ULONG longs;

	/* How many of them? */
	longs=size/sizeof(LONG);

	/*
	    To minimize the loop overhead I copy more than one (eight) LONG per
	    iteration. Therefore I need to split size into size/8 and the rest.
	*/
	low =longs&7;
	high=longs/8;

	/* Then copy for both parts */
	if(low)
	    do
		*d++=*s++;
	    while(--low);

	if(high)
	    do
	    {
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
		*d++=*s++;
	    }while(--high);

	/* Get the rest. */
	size&=sizeof(LONG)-1;
	src=(UBYTE *)s;
	dst=(UBYTE *)d;
    }

    /* The remaining job can only be done by copying single bytes. */
    low =size&7;
    high=size/8;

    /* Copy for both parts */
    if(low)
      do
	*dst++=*src++;
      while(--low);

    /*
	Partly unrolled copying loop. The predecrement helps the compiler to
	find the best possible loop. The if is necessary to do this.
    */
    if(high)
	do
	{
	    *dst++=*src++;
	    *dst++=*src++;
	    *dst++=*src++;
	    *dst++=*src++;
	    *dst++=*src++;
	    *dst++=*src++;
	    *dst++=*src++;
	    *dst++=*src++;
	}while(--high);
    __AROS_FUNC_EXIT
} /* CopyMem */
