/*
 * i4dstring.cpp --
 *
 *	This file provides an implementation of a dynamic string. The
 *	string grows automatically and efficiently as more characters
 *	are appended.
 *
 *	This facility is modeled after the Tcl_DString feature of Tcl
 *	8.0.4.
 *
 * Copyright (c) 1991-1993 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Copyright (c) 2000-2003, JYL Software Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "e4graphimpl.h"

/*
 * Constructor:
 */

e4_DString::e4_DString()
    : buf(staticSpace)
{
    Reset();
}

/*
 * Destructor:
 */

e4_DString::~e4_DString()
{
    /*
     * If the buffer was relocated to a dynamic buffer, free that buffer.
     */

    if (buf != staticSpace) {
	free(buf);
    }
}

/*
 * Append a string to this DString.
 */

void
e4_DString::Append(const char *str, int slen)
{
    int newlength;
    char *newbuf;

    /*
     * Sanity check -- do nothing if appending NULL.
     */

    if (str == NULL) {
	return;
    }

    /*
     * If the given length is < 0, it means that we have to compute the
     * length here. Otherwise we use the given length as the true length.
     */

    if (slen < 0) {
	slen = strlen(str);
    }

    /*
     * See if there's space in the current buffer. If not, relocate the
     * current content to a new buffer before appending.
     */

    newlength = slen + length;
    if (newlength >= spaceAvailable) {
	spaceAvailable = newlength * 2;
	newbuf = (char *) memcpy((void *) malloc(spaceAvailable),
				 (const void *) buf,
				 length);
	if (buf != staticSpace) {
	    free(buf);
	}
	buf = newbuf;
    }

    /*
     * Now append the new string to the buffer. Note that the string to copy
     * is not NULL terminated so we only copy as much as the caller told us.
     * Also, the string to be appended might contain embedded NULLs and that
     * prevents use of strcpy or strncpy.
     */

    memcpy((void *) ((char *) buf + length), (const void *) str, slen);
    buf[newlength] = '\0';

    /*
     * Update the DString.
     */

    length = newlength;
}

/*
 * Get a copy of this DString. The memory is expected to be freed by the
 * caller when done with using it.
 */

char *
e4_DString::GetCopy() const
{
    char *res;

    if (length <= 0) {
	return NULL;
    }
    res = (char *) memcpy((void *) malloc(length + 1),
			  (const void *) buf,
			  length);
    res[length] = '\0';
    return res;
}

/*
 * Get the string from this DString. The memory is owned by the DString.
 */

char *
e4_DString::Get() const
{
    if (length <= 0) {
	return NULL;
    }
    return buf;
}

/*
 * Get the length of the string in this DString.
 */

int
e4_DString::Length() const
{
    return length;
}

/*
 * Set the length of the string in this DString. If the string is
 * longer, this truncates the string. If the string is shorter, it
 * is a no-op.
 *
 * Either way return the new length of the string.
 */

int
e4_DString::SetLength(int newlength)
{
    if ((newlength <= length) && (newlength >= 0)) {
	length = newlength;
	buf[length] = '\0';
    }

    return length;
}

/*
 * Reset this DString to its original state.
 */

void
e4_DString::Reset(void)
{
    /*
     * Reclaim allocated space, if any.
     */

    if (buf != staticSpace) {
	free(buf);
    }

    /*
     * Reset to original state.
     */

    buf = staticSpace;
    length = 0;
    spaceAvailable = E4_DSTRING_STATIC_SIZE + 1;
    staticSpace[0] = '\0';
}
