/*******************************************************************
  uLan Communication - C interface library

  ul_dbuff.c	- dynamicaly allocated buffer

  (C) Copyright 2001 by Pavel Pisa - Originator
  (C) Copyright 2003 by Frantisek Vacek - Originator

  The uLan driver is distributed under the Gnu General Public License. 
  See file COPYING for details.

  Originator reserve the right to use and publish sources
  under different conditions too. If third party contributors
  do not accept this condition, they can delete this statement
  and only GNU license will apply.
 *******************************************************************/
#include <malloc.h>
#include <string.h>
#include "ul_dbuff.h"

typedef unsigned char byte;

//#define UL_DBUFF_LOGGING

#ifdef UL_DBUFF_LOGGING
    #include "can_vca.h"
    
    #define LOG_FATAL   0
    #define LOG_ERR     1
    #define LOG_MSG     2
    #define LOG_INF     3
    #define LOG_DEB     4
#endif

//#undef DEBUG

//-----------------------------------------------------------------
/**
 * ul_dbuff_init - init memory allocated for dynamic buffer 
 * @buf: buffer structure
 * @flags: flags describing behaviour of the buffer
 *         only UL_DBUFF_IS_STATIC flag is supported.
 *         in this case buffer use unly static array sbuf 
 *
 * Returns capacity of initialised buffer
 */
//-----------------------------------------------------------------
int ul_dbuff_init(ul_dbuff_t *buf, int flags)
{
    buf->capacity = UL_DBUFF_SLEN;
    buf->data = buf->sbuff;
    buf->len = 0;
    buf->flags = flags;
    return buf->capacity;
}                                                                               

//-----------------------------------------------------------------
/**
 * ul_dbuff_destroy - frees all resources allocated by buf 
 * @buf: buffer structure
 */
//-----------------------------------------------------------------
__inline__ void ul_dbuff_destroy(ul_dbuff_t *buf)
{
    ul_dbuff_set_capacity(buf, 0);
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_set_capacity - change capacity of buffer to the new size 
 * @buf: buffer structure
 * @new_capacity: new capacity
 *
 * Returns real capacity of reallocated buffer
 */
//-----------------------------------------------------------------
int ul_dbuff_set_capacity(ul_dbuff_t *buf, int new_capacity)
{
    #ifdef UL_DBUFF_LOGGING
    int old_capacity = buf->capacity;
    #endif
    if(buf->flags & UL_DBUFF_IS_STATIC) {
        buf->capacity = sizeof(buf->sbuff);
        return buf->capacity;
    }
    if(new_capacity <= UL_DBUFF_SLEN) {
        if((buf->data) && (buf->data != &(buf->sbuff[0]))) {
            // dynamic storage -> static
            unsigned long c = buf->capacity;
            if(c > sizeof(buf->sbuff)) c = sizeof(buf->sbuff);
            memcpy(buf->sbuff, buf->data, c);
            free(buf->data);
        }
        buf->capacity = sizeof(buf->sbuff);
        buf->data = buf->sbuff;
    }
    else if(new_capacity != buf->capacity) {
        if(buf->data == buf->sbuff) {
            // static storage -> dynamic
            buf->data = malloc((size_t)new_capacity);
            if(buf->data != NULL) {
                memcpy(buf->data, buf->sbuff, buf->capacity);
                buf->capacity = new_capacity;
            }
            else {
                buf->capacity = 0;
            }
        }
        else {
            // dynamic storage -> dynamic
            buf->data = realloc(buf->data, (size_t)new_capacity);
            if(buf->data != NULL) buf->capacity = new_capacity;
            else buf->capacity = 0;
        }
    }
    if(buf->len > buf->capacity) buf->len = buf->capacity;
    #ifdef UL_DBUFF_LOGGING
    vca_log("dbuff", LOG_ERR, "capacity changed from %d to %ld, required %d\n", old_capacity, buf->capacity, new_capacity);
    #endif
    return buf->capacity;
}                                                                               

//-----------------------------------------------------------------
/**
 * ul_dbuff_set_len - sets a new len of the buffer, change the capacity if neccessary 
 * @buf: buffer structure
 * @new_len: new desired buffer length
 *
 * Returns new buffer length
 */
//-----------------------------------------------------------------
int ul_dbuff_set_len(ul_dbuff_t *buf, int new_len)
{
    if(new_len > buf->capacity) {
        unsigned long new_cap = UL_DBUFF_SLEN;
        for(; new_cap < new_len; new_cap <<= 1);
        ul_dbuff_set_capacity(buf, new_cap);
    }
    if(new_len > buf->capacity) {
        buf->data = buf->sbuff;
        strcpy(buf->data, "set_len ERROR");
        new_len = buf->capacity;
    }
    buf->len = new_len;
    return buf->len;
}
 
//-----------------------------------------------------------------
/**
 * ul_dbuff_cat - appends bytes at dhe end of buffer and change its capacity if neccessary 
 * @buf: buffer structure
 * @b: appended bytes
 * @n: number of apended bytes
 *
 * Returns number length of buffer
 */
//-----------------------------------------------------------------
int ul_dbuff_cat(ul_dbuff_t *buf, const void *b, int n)
{
    unsigned long old_len = buf->len;
    unsigned long new_len = old_len + n;
    if(new_len == ul_dbuff_set_len(buf, new_len)) {
        memcpy(buf->data + old_len, b, n);
    }
    #ifdef UL_DBUFF_LOGGING
    else {
        vca_log("dbuff", LOG_ERR, "ul_dbuff_cat: set_len(%lu) error, old_len == %lu\n, act len == %lu\n", new_len, old_len, buf->len);
    }
    #endif
    return buf->len;
}

//-----------------------------------------------------------------
/**
 * ul_dbuff_strcat - appends str at dhe end of buffer and change its capacity if neccessary 
 * @str: string to append
 *
 * Returns number length of buffer (including terminating '\0')
 */
//-----------------------------------------------------------------
__inline__ int ul_dbuff_strcat(ul_dbuff_t *buf, const char *str)
{
    /*
    #ifdef UL_DBUFF_LOGGING
    if(buf->len > 0) vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: '%s' + '%s'\n", buf->data, str);
    else vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: '' + %s\n", str);
    #endif
    */
    if(buf->len > 0 && buf->data[buf->len-1] == '\0') { 
        /* #ifdef UL_DBUFF_LOGGING
        vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: terminating zero found at %ld, after '%c'\n", buf->len-1, buf->data[buf->len - 2]);
        #endif
        */
        ul_dbuff_set_len(buf, buf->len - 1);
    }
    #ifdef UL_DBUFF_LOGGING
    if(buf->len > 0 && buf->data[buf->len-1] != '\0') {
        vca_log("dbuff", LOG_ERR, "ul_dbuff_strcat: terminating zero not found at %ld, found '%c'\n", buf->len-1, buf->data[buf->len-1]);
    }
    #endif
    /* #ifdef UL_DBUFF_LOGGING
    ul_dbuff_cat(buf, str, strlen(str) + 1);
    vca_log("dbuff", LOG_DEB, "ul_dbuff_strcat: returning '%s'\n", buf->data);
    return buf->len;
    #else
    return ul_dbuff_cat(buf, str, strlen(str) + 1);
    #endif */
    return ul_dbuff_cat(buf, str, strlen(str) + 1);
}
 
//-----------------------------------------------------------------
/**
 * ul_dbuff_strcpy - copy str to the buffer and change its capacity if neccessary 
 * @str: string to copy
 *
 * Returns number length of buffer (including terminating '\0')
 */
//-----------------------------------------------------------------
__inline__ int ul_dbuff_strcpy(ul_dbuff_t *buf, const char *str)
{
    ul_dbuff_set_len(buf, 0);
    return ul_dbuff_strcat(buf, str);
}
 
//-----------------------------------------------------------------
/**
* ul_dbuff_append_byte - appends byte at dhe end of buffer and change its capacity if neccessary 
* @buf: buffer structure
* @b: appended byte
*
 * Returns number length of buffer (including terminating '\0')
*/
//-----------------------------------------------------------------
__inline__ int ul_dbuff_append_byte(ul_dbuff_t *buf, unsigned char b)
{
    return ul_dbuff_cat(buf, &b, 1);
}
 
//-----------------------------------------------------------------
/**
* ul_dbuff_ltrim - remove all white space characters from the left 
* @buf: buffer structure
*
 * Returns number length of buffer (including terminating '\0')
*/
//-----------------------------------------------------------------
int ul_dbuff_ltrim(ul_dbuff_t *buf)
{
	byte *pb = buf->data, *pc;
	for(pc = pb; *pc>'\0' && *pc<=' '; pc++);
	if(pc > pb) {
		while(*pc) *pb++ = *pc++;
		*pb++ = '\0';
	}
    buf->len = pb - buf->data;
	return buf->len;
}

//-----------------------------------------------------------------
/**
* ul_dbuff_rtrim - remove all white space characters from the right 
* @buf: buffer structure
*
 * Returns number length of buffer (including terminating '\0')
*/
//-----------------------------------------------------------------
int ul_dbuff_rtrim(ul_dbuff_t *buf)
{
	byte *pc = buf->data + buf->len;
    if(buf->len <= 1) return buf->len;
    pc--;
	while(pc > buf->data) {
		pc--;
		if(*pc>'\0' && *pc<=' ') {*pc = '\0'; continue;}
		break;
	}
    buf->len = pc - buf->data + 2;
	return buf->len;
}

//-----------------------------------------------------------------
/**
* ul_dbuff_trim - remove all white space characters from the right and from the left 
* @buf: buffer structure
*
 * Returns number length of buffer (including terminating '\0')
*/
//-----------------------------------------------------------------
int ul_dbuff_trim(ul_dbuff_t *buf)
{
    ul_dbuff_rtrim(buf);
    return ul_dbuff_ltrim(buf);
}
//-----------------------------------------------------------------
 
