System programming in D (Was: The God Language)
Walter Bright
newshound2 at digitalmars.com
Thu Dec 29 11:47:39 PST 2011
On 12/29/2011 3:19 AM, Vladimir Panteleev wrote:
> I'd like to invite you to translate Daniel Vik's C memcpy implementation to D:
> http://www.danielvik.com/2010/02/fast-memcpy-in-c.html
Challenge accepted.
------------------------
/********************************************************************
** File: memcpy.c
**
** Copyright (C) 1999-2010 Daniel Vik
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any
** damages arising from the use of this software.
** Permission is granted to anyone to use this software for any
** purpose, including commercial applications, and to alter it and
** redistribute it freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you
** must not claim that you wrote the original software. If you
** use this software in a product, an acknowledgment in the
** use this software in a product, an acknowledgment in the
** product documentation would be appreciated but is not
** required.
**
** 2. Altered source versions must be plainly marked as such, and
** must not be misrepresented as being the original software.
**
** 3. This notice may not be removed or altered from any source
** distribution.
**
**
** Description: Implementation of the standard library function memcpy.
** This implementation of memcpy() is ANSI-C89 compatible.
**
** The following configuration options can be set:
**
** LITTLE_ENDIAN - Uses processor with little endian
** addressing. Default is big endian.
**
** PRE_INC_PTRS - Use pre increment of pointers.
** Default is post increment of
** pointers.
**
** INDEXED_COPY - Copying data using array indexing.
** Using this option, disables the
** PRE_INC_PTRS option.
**
** MEMCPY_64BIT - Compiles memcpy for 64 bit
** architectures
**
**
** Best Settings:
**
** Intel x86: LITTLE_ENDIAN and INDEXED_COPY
**
*******************************************************************/
module memcpy;
/********************************************************************
** Configuration definitions.
*******************************************************************/
version = LITTLE_ENDIAN;
version = INDEXED_COPY;
/********************************************************************
** Includes for size_t definition
*******************************************************************/
/********************************************************************
** Typedefs
*******************************************************************/
alias ubyte UInt8;
alias ushort UInt16;
alias uint UInt32;
alias ulong UInt64;
version (D_LP64)
{
alias UInt64 UIntN;
enum TYPE_WIDTH = 8;
}
else
{
alias UInt32 UIntN;
enum TYPE_WIDTH = 4;
}
/********************************************************************
** Remove definitions when INDEXED_COPY is defined.
*******************************************************************/
//#if defined (INDEXED_COPY)
//#if defined (PRE_INC_PTRS)
//#undef PRE_INC_PTRS
//#endif /*PRE_INC_PTRS*/
//#endif /*INDEXED_COPY*/
/********************************************************************
** Definitions for pre and post increment of pointers.
*******************************************************************/
version (PRE_INC_PTRS)
{
void START_VAL(ref UInt8* x) { x--; }
ref T INC_VAL(T)(ref T* x) { return *++x; }
UInt8* CAST_TO_U8(void* p, int o) { return cast(UInt8*)p + o + TYPE_WIDTH; }
enum WHILE_DEST_BREAK = (TYPE_WIDTH - 1);
enum PRE_LOOP_ADJUST = -(TYPE_WIDTH - 1);
enum PRE_SWITCH_ADJUST = 1;
}
else
{
void START_VAL(UInt8* x) { }
ref T INC_VAL(T)(ref T* x) { return *x++; }
UInt8* CAST_TO_U8(void* p, int o) { return cast(UInt8*)p + o; }
enum WHILE_DEST_BREAK = 0;
enum PRE_LOOP_ADJUST = 0;
enum PRE_SWITCH_ADJUST = 0;
}
/********************************************************************
**
** void *memcpy(void *dest, const void *src, size_t count)
**
** Args: dest - pointer to destination buffer
** src - pointer to source buffer
** count - number of bytes to copy
**
** Return: A pointer to destination buffer
**
** Purpose: Copies count bytes from src to dest.
** No overlap check is performed.
**
*******************************************************************/
void *memcpy(void *dest, const void *src, size_t count)
{
auto dst8 = cast(UInt8*)dest;
auto src8 = cast(UInt8*)src;
UIntN* dstN;
UIntN* srcN;
UIntN dstWord;
UIntN srcWord;
/********************************************************************
** Macros for copying words of different alignment.
** Uses incremening pointers.
*******************************************************************/
void CP_INCR() {
INC_VAL(dstN) = INC_VAL(srcN);
}
void CP_INCR_SH(int shl, int shr) {
version (LITTLE_ENDIAN)
{
dstWord = srcWord >> shl;
srcWord = INC_VAL(srcN);
dstWord |= srcWord << shr;
INC_VAL(dstN) = dstWord;
}
else
{
dstWord = srcWord << shl;
srcWord = INC_VAL(srcN);
dstWord |= srcWord >> shr;
INC_VAL(dstN) = dstWord;
}
}
/********************************************************************
** Macros for copying words of different alignment.
** Uses array indexes.
*******************************************************************/
void CP_INDEX(size_t idx) {
dstN[idx] = srcN[idx];
}
void CP_INDEX_SH(size_t x, int shl, int shr) {
version (LITTLE_ENDIAN)
{
dstWord = srcWord >> shl;
srcWord = srcN[x];
dstWord |= srcWord << shr;
dstN[x] = dstWord;
}
else
{
dstWord = srcWord << shl;
srcWord = srcN[x];
dstWord |= srcWord >> shr;
dstN[x] = dstWord;
}
}
/********************************************************************
** Macros for copying words of different alignment.
** Uses incremening pointers or array indexes depending on
** configuration.
*******************************************************************/
version (INDEXED_COPY)
{
void CP(size_t idx) { CP_INDEX(idx); }
void CP_SH(size_t idx, int shl, int shr) { CP_INDEX_SH(idx, shl, shr); }
void INC_INDEX(T)(ref T* p, size_t o) { p += o; }
}
else
{
void CP(size_t idx) { CP_INCR(); }
void CP_SH(size_t idx, int shl, int shr) { CP_INCR_SH(shl, shr); }
void INC_INDEX(T)(T* p, size_t o) { }
}
void COPY_REMAINING(size_t count) {
START_VAL(dst8);
START_VAL(src8);
switch (count) {
case 7: INC_VAL(dst8) = INC_VAL(src8);
case 6: INC_VAL(dst8) = INC_VAL(src8);
case 5: INC_VAL(dst8) = INC_VAL(src8);
case 4: INC_VAL(dst8) = INC_VAL(src8);
case 3: INC_VAL(dst8) = INC_VAL(src8);
case 2: INC_VAL(dst8) = INC_VAL(src8);
case 1: INC_VAL(dst8) = INC_VAL(src8);
case 0:
default: break;
}
}
void COPY_NO_SHIFT() {
dstN = cast(UIntN*)(dst8 + PRE_LOOP_ADJUST);
srcN = cast(UIntN*)(src8 + PRE_LOOP_ADJUST);
size_t length = count / TYPE_WIDTH;
while (length & 7) {
CP_INCR();
length--;
}
length /= 8;
while (length--) {
CP(0);
CP(1);
CP(2);
CP(3);
CP(4);
CP(5);
CP(6);
CP(7);
INC_INDEX(dstN, 8);
INC_INDEX(srcN, 8);
}
src8 = CAST_TO_U8(srcN, 0);
dst8 = CAST_TO_U8(dstN, 0);
COPY_REMAINING(count & (TYPE_WIDTH - 1));
}
void COPY_SHIFT(int shift) {
dstN = cast(UIntN*)(((cast(UIntN)dst8) + PRE_LOOP_ADJUST) &
~(TYPE_WIDTH - 1));
srcN = cast(UIntN*)(((cast(UIntN)src8) + PRE_LOOP_ADJUST) &
~(TYPE_WIDTH - 1));
size_t length = count / TYPE_WIDTH;
srcWord = INC_VAL(srcN);
while (length & 7) {
CP_INCR_SH(8 * shift, 8 * (TYPE_WIDTH - shift));
length--;
}
length /= 8;
while (length--) {
CP_SH(0, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(1, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(2, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(3, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(4, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(5, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(6, 8 * shift, 8 * (TYPE_WIDTH - shift));
CP_SH(7, 8 * shift, 8 * (TYPE_WIDTH - shift));
INC_INDEX(dstN, 8);
INC_INDEX(srcN, 8);
}
src8 = CAST_TO_U8(srcN, (shift - TYPE_WIDTH));
dst8 = CAST_TO_U8(dstN, 0);
COPY_REMAINING(count & (TYPE_WIDTH - 1));
}
if (count < 8) {
COPY_REMAINING(count);
return dest;
}
START_VAL(dst8);
START_VAL(src8);
while ((cast(UIntN)dst8 & (TYPE_WIDTH - 1)) != WHILE_DEST_BREAK) {
INC_VAL(dst8) = INC_VAL(src8);
count--;
}
final switch (((cast(UIntN)src8) + PRE_SWITCH_ADJUST) & (TYPE_WIDTH - 1)) {
case 0: COPY_NO_SHIFT(); break;
case 1: COPY_SHIFT(1); break;
case 2: COPY_SHIFT(2); break;
case 3: COPY_SHIFT(3); break;
static if (TYPE_WIDTH >= 4)
{
case 4: COPY_SHIFT(4); break;
case 5: COPY_SHIFT(5); break;
case 6: COPY_SHIFT(6); break;
case 7: COPY_SHIFT(7); break;
}
}
return dest;
}
More information about the Digitalmars-d
mailing list