/* Copyright (C) 2019 Momi-g

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/*-*
@_name	stringrow
@auther momi-g
@brief	auto growing str/bin buffer. fast.
@_synopsys
	typedef struct stringrow_tag {
		char* p;
		int ofst;
		int sz;
		int msz;
	} sg_t;
	
	sg_t*	sg_new(void);
	void	sg_free(sg_t* obj)
	int		sg_add(sg_t* obj, const char* bin [,int binsz] )
	int		sg_addf(sg_t* obj, const char* fmt, ...)
	int		sg_ins(sg_t* obj, const char* bin [,int binsz] )
	int		sg_insf(sg_t* obj, const char* fmt, ...)
	
	void	sg_bs(sg_t* obj, int num);
	void	sg_del(sg_t* obj, int num);
	
	char*	sg_ptr(obj [,pos])		// same as (obj->p)+pos
	int		sg_sz(obj)				// same as obj->sz
	void	sg_clear(sg_t* obj);

@_eg
	#include "stringrow.h"
	#include <stdio.h>

	int main() {
		sg_t* obj = sg_new();		// ""
		int rc= sg_add(obj, "foo"); // "foo", rc=3: addsz
		char* p = sg_ptr(obj);		// get headptr
		puts(p);					// >> 'foo'
		p = sg_ptr(obj, 1);			// &(p[1])
		puts(p);					// >> 'oo'
		int sz = sg_sz(obj);		// >> 3

		sg_add(obj, "bar");			// "foobar"
		rc = sg_ins(obj, "baz");	// "bazfoobar", rc=3
		sg_ins(obj, "\0abc");		// "bazfoobar"	,read as str if ag3 isnt
		sg_ins(obj, "\0abc", 2);	// "(\0)abazfoobar"
		sz = sg_sz(obj);			//	11,	sz is holding bin size
		sg_clear(obj);				// ""
		
		sg_addf(obj,"%#x",17);		// "0x11", fmt is same as printf()
		rc = sg_addf(obj,"\0");		// "0x11", rc=0
		rc = sg_addf(obj,"%c", 0);	// "0x11(\0)", rc=1
		sg_bs(obj, 2);				// "0x1"	,del tail 2byte
		sg_del(obj,2);				// "1"		,del head 2byte
		puts(obj->p);				// >> "1"

		sg_add(obj, obj->p);		// >> "11", allow to use inner ptr
		sg_insf(obj,"%s",obj->p);	// >> "1111",
	
		sg_free(obj);
		return 0;
	}
	// ~$ gcc src.c stringrow.c

@param obj	main struct. holds realloc()ed ptr, max memsz, used sz
@param bin	str/binary you want to add
@param binsz addsize >= -2. -1:strlen(bin), -2:strlen(bin)+1. use -1 if noset
@param fmt	add printf() fmt strings instead of normal bindata
@param num	delete byte size
@param pos	optional args. ptr offset byte. use 0 if no set
@return	- sg_add(f)/ins(f) rtns add datasz
@details
 - data is always terminated with '\0'. this last byte is out of sg_sz() cnt.
   so puts(sg_ptr(obj)) never causes SEGV err.
	sg_add(obj, "foo");		// sz=3	puts(sg_ptr(obj) ) >> "foo" 
	sg_add(obj, "\0ab",4);	// sz=7	puts(sg_ptr(obj) ) >> "foo" 
	sg_clear(obj);			// sz=0	puts(sg_ptr(obj) ) >> ""
	sg_ins(obj, "foo");		// sz=3	puts(sg_ptr(obj) ) >> "foo" 

 - str adding funcs rtns added sz
	sg_t* strpool = sg_new();
	char* s = "hw";
	int p1 = sg_add(strpool, s, strlen(s)+1 );	//+1=='\0', p1==3
	s = "gw";
	int p2 = sg_add(strpool, s, -2 );	//-2==slen(s)+1, p2==3, "hw(\0)gw(\0)"
	puts( sg_ptr(strpool)+p1 );	//>>gw
	puts( strpool->p );		//>>hw
 
 - sg_ptr(obj) is almost the same as obj->p +0. sg_ptr(obj, 4) is obj->p +4
 - sg_clear() doesnt shrink heap
 - sloppy benchmark: 	while(i-- >0){ sg_add(obj, "123"); }
	loop(100*1000)
 real	8.652 ms	: msg:add
 real	43.633 ms	: msg:addf
 real	6.422 ms	: msg:ins
 real	32.289 ms	: msg:insf
 real	7.121 ms	: msg:strdup()
 
 	loop( 10*1000), too slow + causes memclash
 real	244.710 ms	: msg:gnu asprintf(), x10 == 2.4s
 real	978.828 ms	: msg:apr_psprintf(), x10 == 9.7s

 FAST: strdup/sg_add(1) >> sg_addf(5) >>>> asprintf(300) >> libapr(1000) :SLOW
@_note -
@conforming posix-2001+
@version 2.1.0, 2021-06-02
-*/
#ifndef stringrow_bf01ef7c
#define stringrow_bf01ef7c

#include <string.h>
#if ( _POSIX_C_SOURCE +0 < 200112L )
 #include "stop cc: needs compiler posix-2001 or upper(c99+)"
#endif

typedef struct stringrow_tag {
	char* p;	//仮想的なtop
	int ofst;	// 中央付近に陣取る
	int sz;
	int msz;
} sg_t;

// init heap sz
#ifndef SG_BUFSZ
 #define SG_BUFSZ	32
#endif

// realloc() if used heap is more than RATIO
#ifndef SG_TRIGRATIO
 #define SG_TRIGRATIO	0.6
#endif

// from license: cc-by-sa 2.5 (code is changed from the orig)
// https://stackoverflow.com/questions/2632300	Q: Ed Marty  ->  A: Matthew Slattery
#define sg_add(...)	sg_addsub(__VA_ARGS__, sg_add3, sg_add2)
#define sg_addsub(a1,a2,a3,mc,...)	mc(a1, a2, a3)
#define sg_add2(a1,a2,...)		sg_add_impl(a1, a2, -1)
#define sg_add3(a1,a2,a3,...)	sg_add_impl(a1, a2, a3)

#define sg_ins(...)	sg_inssub(__VA_ARGS__, sg_ins3, sg_ins2)
#define sg_inssub(a1,a2,a3,mc,...)	mc(a1, a2, a3)
#define sg_ins2(a1,a2,...)		sg_ins_impl(a1, a2, -1)
#define sg_ins3(a1,a2,a3,...)	sg_ins_impl(a1, a2, a3)
// end licence:	cc-by-sa 2.5

sg_t* sg_new(void);
int sg_add_impl(sg_t* str, const char* add, int addsz);
int sg_ins_impl(sg_t* str, const char* add, int addsz);

int sg_addf(sg_t* str, const char* fmt, ...);
int sg_insf(sg_t* str, const char* fmt, ...);
void sg_bs(sg_t* obj, int num);
void sg_del(sg_t* obj, int num);

void sg_clear(sg_t* str);

#define sg_ptr(...)	sg_ptrsub(__VA_ARGS__, 0)
 #define sg_ptrsub(obj,pos,...)	(obj->p+pos)

#define sg_sz(obj)	(obj->sz)
#define sg_free(a)	do{sg_free_alt(a); a=NULL;}while(0)
void sg_free_alt(sg_t* str);

#endif /* stringrow_bf01ef7c */
