/*
**  Str - String Library
**  Copyright (c) 1999-2001 Ralf S. Engelschall <rse@engelschall.com>
**
**  This file is part of Str, a string handling and manipulation 
**  library which can be found at http://www.engelschall.com/sw/str/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  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.
**
**  str_mem.c: raw memory functions
*/

#include "str_p.h"

#define _str_mem_alloc   malloc
#define _str_mem_realloc realloc
#define _str_mem_free    free

static void *
_str_mem_dup(
    void *mem, 
    size_t bytes)
{
    void *dmem;

    if (mem == NULL)
        return NULL;
    if ((dmem = str_mem_alloc(bytes)) == NULL)
        return NULL;
    str_mem_move(dmem, mem, bytes);
    return dmem;
}

#ifdef HAVE_MEMSET
#define _str_mem_set memset
#else
static void *
_str_mem_set(
    void *dst0, 
    int c0, 
    size_t bytes)
{
    register size_t t;
    register unsigned int c;
    register unsigned char *dst;
    const int word_size = sizeof(unsigned int);
    const int word_mask = (sizeof(unsigned int) - 1);

    dst = dst0;

    /* if not enough words for a reasonable speedup, just fill bytes */
    if (bytes < 3 * word_size) {
        while (bytes != 0) {
            *dst++ = c0;
            --bytes;
        }
        return dst0;
    }

    /* fill the whole stamping word */
    if ((c = (unsigned char)c0) != 0) { 
        c = c | (c << 8);
#if (SIZEOF_INT > 2)
        c = c | (c << 16);
#endif
#if (SIZEOF_INT > 4)
        c = c | (c << 32);
#endif
    }

    /* align destination by filling in bytes */
    if ((t = (long)dst & word_mask) != 0) {
        t = word_size - t;
        bytes -= t;
        do {
            *dst++ = c0;
        } while (--t != 0);
    }

    /* now fill with words. length was >= 2*words so we know t >= 1 here */
    t = bytes / word_size;
    do {
        *(unsigned int *)dst = c;
        dst += word_size;
    } while (--t != 0);

    /* finish with trailing bytes, if there are bytes left */
    t = bytes & word_mask;
    if (t != 0) {
        do {
            *dst++ = c0;
        } while (--t != 0);
    }

    return dst0;
}
#endif

#ifdef HAVE_MEMMOVE
#define _str_mem_move memmove
#else
static void *
_str_mem_move(
    void *dst, 
    const void *src, 
    size_t bytes)
{
    register unsigned char *dst_p;
    register const unsigned char *src_p;

    if (src == NULL || dst == NULL)
        return NULL;
    src_p = src;
    dst_p = dst;
    if (dst > src) { 
        /* must go high to low */
        src_p += bytes;
        dst_p += bytes;
        while (bytes-- > 0)
            *--dst_p = *--src_p;
    }
    else if (dst < src) {
        /* must go low to high */
        while (bytes-- > 0)
            *dst_p++ = *src_p++;
    }
    return dst;
}
#endif

static void *
_str_mem_rev(
    void *src, 
    size_t bytes)
{
    register unsigned char *p1;
    register unsigned char *p2;
    register unsigned char c;

    p1 = (unsigned char *)src;
    p2 = (unsigned char *)src + bytes;
    while (p1 < p2)
        c = *p1, *p1++ = *p2, *p2-- = c;
    return src;
}

#ifdef HAVE_MEMCHR
#define _str_mem_char memchr
#else
static void *
_str_mem_char(
    const void *src, 
    int c, 
    size_t bytes)
{
    register const unsigned char *cp;

    if (bytes != 0) {
        cp = src;
        do {
            if (*cp++ == (unsigned char)c)
                return ((void *)(cp - 1));
        } while (--bytes != 0);
    }
    return NULL;
}
#endif

static void *
_str_mem_mem(
    const void *haystack, size_t haystack_len, 
    const void *needle,   size_t needle_len)
{
    register const char *begin;
    register const char *last_possible;
    
    if (needle_len == 0) 
        /* The first occurrence of the empty string is deemed to occur at
           the end of the string. */
        return (void *)&((const char *)haystack)[haystack_len - 1];
    last_possible = (const char *)haystack + haystack_len - needle_len;
    for (begin = (const char *)haystack; begin <= last_possible; begin++)
        if (*begin == *((const char *)needle) &&
            str_mem_cmp(&begin[1], (const char *)needle + 1, needle_len - 1) == 0)

        return (void *)begin;
    return NULL;
}

#ifdef HAVE_MEMCMP
#define _str_mem_cmp memcmp
#else
static int
_str_mem_cmp(
    const void *src1, 
    const void *src2, 
    size_t bytes)
{
    register const unsigned char *cp1;
    register const unsigned char *cp2;
   
    if (bytes != 0) {
        cp1 = src1;
        cp2 = src2;
        do {
            if (*cp1++ != *cp2++)
                return (*--cp1 - *--cp2);
        } while (--bytes != 0);
    }
    return 0;
}
#endif

/* the API consists only of indirection pointers */
void *(*str_mem_alloc)(size_t)                                   = _str_mem_alloc;
void *(*str_mem_realloc)(void *, size_t)                         = _str_mem_realloc;
void  (*str_mem_free)(void *)                                    = _str_mem_free;
void *(*str_mem_dup)(void *, size_t)                             = _str_mem_dup;
void *(*str_mem_set)(void *, int, size_t)                        = _str_mem_set;
void *(*str_mem_move)(void *, const void *, size_t)              = _str_mem_move;
void *(*str_mem_rev)(void *, size_t)                             = _str_mem_rev;
void *(*str_mem_char)(const void *, int, size_t)                 = _str_mem_char;
void *(*str_mem_mem)(const void *, size_t, const void *, size_t) = _str_mem_mem;
int   (*str_mem_cmp)(const void *, const void *, size_t)         = _str_mem_cmp;

