/*$
mlk in apdtool
Copyright (c) 2020 Azel

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.
$*/

/*****************************************
 * mBuf
 *****************************************/

#include <string.h>

#include "mlk.h"
#include "mlk_buf.h"
#include "mlk_unicode.h"



/**@ 指定サイズ分を追加してリサイズ */

static mlkbool _resize_add(mBuf *p,int size)
{
	uint8_t *newbuf;
	mlksize addsize,newsize;

	if(size == 0) return TRUE;
	
	if(!p->buf) return FALSE;

	addsize = p->cursize + size;

	if(addsize > p->allocsize)
	{
		//リサイズ
		
		newsize = p->allocsize + p->expand_size;
		if(newsize < addsize) newsize = addsize;

		newbuf = (uint8_t *)mRealloc(p->buf, newsize);
		if(!newbuf) return FALSE;

		p->buf = newbuf;
		p->allocsize = newsize;
	}

	return TRUE;
}


//========================


/**@ 初期化 */

void mBufInit(mBuf *p)
{
	p->buf = NULL;
	p->allocsize = p->cursize = 0;
}

/**@ 解放 */

void mBufFree(mBuf *p)
{
	if(p)
	{
		mFree(p->buf);

		p->buf = NULL;
		p->allocsize = p->cursize = 0;
	}
}

/**@ 初期確保
 *
 * @d:すでに確保されている場合は、先に解放される。
 *
 * @p:allocsize 初期確保サイズ
 * @p:expand_size  自動拡張時に追加する単位 */

mlkbool mBufAlloc(mBuf *p,mlksize allocsize,mlksize expand_size)
{
	mBufFree(p);

	p->buf = (uint8_t *)mMalloc(allocsize);
	if(!p->buf) return FALSE;

	p->allocsize = allocsize;
	p->cursize = 0;
	p->expand_size = expand_size;

	return TRUE;
}

/**@ データ追加の現在位置をリセット */

void mBufReset(mBuf *p)
{
	p->cursize = 0;
}

/**@ データ追加位置を指定サイズ前に戻す */

void mBufBack(mBuf *p,int size)
{
	if(size < p->cursize)
		p->cursize -= size;
	else
		p->cursize = 0;
}

/**@ データ追加の残りサイズ取得 */

mlksize mBufGetRemain(mBuf *p)
{
	return p->allocsize - p->cursize;
}

/**@ 現在のデータ追加位置のポインタ取得 */

uint8_t *mBufGetCurrent(mBuf *p)
{
	return p->buf + p->cursize;
}

/**@ 現在の位置に合わせてバッファをサイズ変更する
 *
 * @d:余ったバッファを切り捨てる。\
 * 現在位置が 0 の場合は何もしない。 */

void mBufCutCurrent(mBuf *p)
{
	uint8_t *newbuf;

	if(p->cursize && p->cursize != p->allocsize)
	{
		newbuf = (uint8_t *)mRealloc(p->buf, p->cursize);
		if(!newbuf) return;

		p->buf = newbuf;
		p->allocsize = p->cursize;
	}
}

/**@ データを追加
 *
 * @d:buf が NULL、または size が 0 以下の場合は何もしない
 * @r:リサイズに失敗した場合 FALSE */

mlkbool mBufAppend(mBuf *p,const void *buf,int size)
{
	if(!buf || size <= 0)
		return TRUE;

	//サイズ拡張

	if(!_resize_add(p, size))
		return FALSE;

	//データ追加

	memcpy(p->buf + p->cursize, buf, size);

	p->cursize += size;

	return TRUE;
}

/**@ 1byte データ追加 */

mlkbool mBufAppendByte(mBuf *p,uint8_t dat)
{
	return mBufAppend(p, &dat, 1);
}

/**@ 指定サイズ分のゼロ値を追加 */

mlkbool mBufAppend0(mBuf *p,int size)
{
	if(!_resize_add(p, size))
		return FALSE;

	memset(p->buf + p->cursize, 0, size);
	p->cursize += size;

	return TRUE;
}

/**@ UTF-8 文字列を追加
 *
 * @d:正規化した UTF-8 文字列を追加。\
 * ヌル文字は常にセットされる。
 * @p:len  ヌル文字を含まないサイズ。負の値で自動。
 * @r:セットされた位置のポインタ。NULL で失敗。 */

char *mBufAppendUTF8(mBuf *p,const char *buf,int len)
{
	char *cur;

	if(len < 0)
		len = mStrlen(buf);

	if(!_resize_add(p, len + 1)) return NULL;

	//セット

	cur = (char *)(p->buf + p->cursize);

	len = mUTF8CopyValidate(cur, buf, len);

	cur[len] = 0;

	p->cursize += len + 1;

	return cur;
}

/**@ 新しく確保したバッファにデータをコピーする
 *
 * @d:mBuf の現在位置までのサイズでバッファを確保し、
 * データをコピーして返す。
 * @r:現在のサイズが 0 なら NULL */

void *mBufCopyToBuf(mBuf *p)
{
	if(p->cursize == 0) return NULL;

	return mMemdup(p->buf, p->cursize);
}
