#include <emsg: see ... '~$ sh aaa.sh.c -h'   (other opt:no/-m/-w/)>	/*
C='^[/][/*]SH_'     ;O=${0##*[/]};R=`dirname $0`;R=${R%/}/;R0=$R$O;R=$R${O%%.*}
O=${0##*.};Rs=$R.$O;Rm=$R.tmp.$O;Rh=$R.h;R=$Rs$Rh$Rm;Rp='printf %s\n ';Rc=:;O="
";[ "${R##*$R0*}" = '' ]&&$Rp"$0:NGsuffix"&&exit 1;R='sed -ne ';Cm=$R'"/[E]ND/!d
:l;n;p;bl"<$R0>$Rm;$Rp"$Rm"';RB=$($R"s/${C}OP//p"<$R0|(F=mw;while read -r a b;do
B=${a%:};F=`$Rp"$F"|$R"s#$B:*##1;p"`${a%_};$Rp"C$B=\$(cat<<'E'$O$b${O}E$O)";done
$Rp"R1=$F"));Rw=$R'"/$C$R/!d;:l;n;/${C}ED/q;p;bl"<$R0';Cw="(R=LS;$Rw;$Rw>&3;R=HD
$Rw;R=SC;$Rw>&3)"'>$Rh 3>$Rs;$Rp"$Rh $Rs"';Re=eval\ ;$Re"$RB";while getopts $R1\
 R;do case $R in \?)exit 1;;*)$Re"O$R=\$OPTARG";Rc=$Rc$O`$Re'$Rp"$C'$R\"`;;esac
done;[ "$Rc" = : ]&&Rc=$Cm;shift $((OPTIND-1));$Re"$C_$O$Rc";exit  #END GPLv3+*/

//SH_LS
/* 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
-*/
/*SH_ED*/

/*SH_HD*/
#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 */
/*SH_ED*/

/*SH_SC*/
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "*SH_bn*.h"

/* tool macros */
#ifndef ERRact
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
 #if  (200112L <= _POSIX_C_SOURCE +0)  /* nealy 200112L, _POSIX_C_SOURCE	c99*/
 #define ERRact(xpr, msg, act)	if(xpr){ fprintf(stderr, \
	"ERR: %s %d %s() pid:%d %s msg:%s sys:%s\n",__FILE__,__LINE__, __func__ \
	,getpid(), "hit(" #xpr ")", msg, strerror(errno) );act ;}
 #else
 #define ERRact(xpr, msg, act)	if(xpr){ fprintf(stderr, \
	"ERR: %s %d %s() pid:%d %s msg:%s sys:%s\n",__FILE__,__LINE__, "func:c99+" \
	,0 , "hit(" #xpr ")", msg, strerror(errno) );act ;}
 #endif
 #define STOP(xpr, msg)	ERRact(xpr, msg, fputs("STOP\n",stderr);exit(1))
#endif

#define loop(a)		for(int lpcnt=1;lpcnt<=a;lpcnt++)
/*tool end*/

#include "msgp.h"

/*src*/
static int sg_adjmem(sg_t* str, int addsz, int edge);

sg_t* sg_new(void) {
	; STOP( SG_BUFSZ<1, "init bufsz is negative num");
	sg_t* obj=(sg_t*)calloc(sizeof(sg_t), 1);
	; if(!obj){return NULL;}	//STOP(!obj, "calloc() failed");

	obj->ofst = SG_BUFSZ/2;
	char* p = (char*)calloc(sizeof(char), SG_BUFSZ);
	; if(!p){return NULL;}	//STOP(!obj, "calloc() failed");

	obj->p = p + obj->ofst;
	obj->sz=0;
	obj->msz= SG_BUFSZ;
	return obj;
}

int sg_add_impl(sg_t* obj, const char* add, int addsz) {
	; STOP(!obj, "NULL obj.");	//削れば10%早くなるけどセグフォで見えないのは嫌
	if(addsz== -1){addsz=strlen(add);}
	else if(addsz== -2){addsz=strlen(add)+1;}
	else if(addsz<-2){ STOP(addsz, "binsz>=0 or -1:strlen(), -2:strlen()+1, -3...err");}
	
	int pos=0;	//addがsgの内部だったらextendで狂う 保存修正 mallocより多分早い
	int flg=0;
	char* bp = obj->p-obj->ofst;
	if((bp <=add) && (add< bp+obj->msz)){ pos=add- obj->p;flg=1; }
	// -1:add 0:ins memerrはstop?
	if(obj->ofst +obj->sz +addsz+1 >obj->msz ){
		int rc = sg_adjmem(obj, addsz, -1);
		if(rc<0){return -1;}
	}
	if(flg){add=obj->p+pos;}

	memmove(obj->p+obj->sz, add, addsz);
	obj->sz += addsz;
	obj->p[obj->sz]=0;
	return addsz;
}
int sg_addf(sg_t* obj, const char* fmt, ...) {
	; STOP(!obj, "NULL obj.");
	char sbuf[1]={0};
	va_list vl;
	va_start(vl, fmt);
	size_t addsz = vsnprintf(sbuf, sizeof(sbuf), fmt, vl);	//sn系は\0を除く。クソが。
	va_end(vl);
	
	int sz = addsz*1;
	sz = SG_BUFSZ - (sz % SG_BUFSZ) + sz;	//アライメントを揃える
	char* p = malloc(sz);		//内部に自身がいると壊れるので外だし
	; if(!p){return -1;}	//STOP(!p, "realloc() failed");

	va_start(vl, fmt);
	vsnprintf( p, sz, fmt, vl);	// \0
	va_end(vl);
//インライン	40ms >> 35ms 10%程度改善
//	int rtn = sg_add_impl(obj, p, addsz);
	if(obj->ofst +obj->sz +addsz+1 >obj->msz ){
		int rc = sg_adjmem(obj, addsz, -1);
		if(rc<0){return -1;}
	}
	memmove(obj->p+obj->sz, p, addsz);
//	memcpy(obj->p+obj->sz, p, addsz);	cpyもmoveも変わらない 内部で一緒かもしれない
	
	obj->sz += addsz;
	obj->p[obj->sz]=0;

	free(p);
	return addsz;
}

int sg_ins_impl(sg_t* obj, const char* add, int addsz) {
	; STOP(!obj, "NULL obj.");
	if(addsz== -1){addsz=strlen(add);}
	else if(addsz== -2){addsz=strlen(add)+1;}
	else if(addsz<-2){ STOP(addsz, "binsz>=0 or -1:strlen(), -2:strlen()+1, -3...err");}

	int pos=0;
	int flg=0;
	char* bp = obj->p-obj->ofst;
	if((bp <=add) && (add< bp+obj->msz)){ pos=add- obj->p;flg=1; }
	if(obj->ofst < addsz+1){
		int rc = sg_adjmem(obj, addsz, 0);
		if(rc<0){return -1;}
	}
	if(flg){add=obj->p+pos;}

	obj->p -= addsz;
	obj->ofst -= addsz;
	obj->sz += addsz;
	memmove(obj->p, add, addsz);
	return addsz;
}

int sg_insf(sg_t* obj, const char* fmt, ...) {
	;	STOP(!obj, "NULL obj.");
	char sbuf[1]={0};
	va_list vl;
	va_start(vl, fmt);
	//insは\0を付けない
	size_t addsz = vsnprintf(sbuf, sizeof(sbuf), fmt, vl);
	va_end(vl);

	int sz = addsz*1;
	sz = SG_BUFSZ - (sz % SG_BUFSZ) + sz;	//アライメントを揃える
	char* p = malloc(sz);		//内部に自身がいると壊れるので外だし
	; if(!p){return -1;}	//STOP(!p, "realloc() failed");
	va_start(vl, fmt);
	vsnprintf( p, sz, fmt, vl);	// \0
	va_end(vl);

//インライン
//	int rtn = sg_ins_impl(obj, p, addsz);
	if(obj->ofst < addsz+1){
		int rc = sg_adjmem(obj, addsz, 0);
		if(rc<0){return -1;}
	}
	obj->p -= addsz;
	obj->ofst -= addsz;
	obj->sz += addsz;
	memmove(obj->p, p, addsz);
//	memcpy(obj->p, p, addsz);

	free(p);
	return addsz;

//	if(obj->ofst < addsz+1){sg_adjmem(obj, addsz, 0);}
//	
//	int rtn = obj->sz;
//	obj->p -= addsz;
//	obj->ofst -= addsz;
//	obj->sz += addsz;
//
//	va_start(vl, fmt);
//	vsnprintf( obj->p, addsz+1, fmt, vl);	//'\0'で踏み潰されるので戻す
//	va_end(vl);
//	return rtn;
}

void sg_bs(sg_t* obj, int num) {
	;	STOP(!obj, "NULL obj.");
	obj->sz -= num;
	if(obj->sz<0){obj->sz=0;}
	obj->p[obj->sz]=0;
	//ofst変化なし
}
void sg_del(sg_t* obj, int num) {
	;	STOP(!obj, "NULL obj.");
	if(obj->sz <num){ num=obj->sz;}
	obj->p  += num;
	obj->ofst += num;
	obj->sz -= num;
}

void sg_clear(sg_t* obj) {
	if(!obj){return;}
	;	STOP(!obj, "NULL obj.");
	obj->p = obj->p - obj->ofst + obj->msz/2;
	obj->ofst = obj->msz /2;
	obj->sz=0;
	obj->p[0]=0;
}

// corecode
static int sg_adjmem(sg_t* obj, int addsz, int edge){
	//	init: |....@....|
	//	add : |....@@...|
	//	ins : |.===@@...|			realloc					memmove
	//	ext	: |.===@@@@@|  >>  |.==@@@@@|.......| >> |......==@@@@@.......|
	
	// 戦略: dataはセンター付近に陣取って端点で伸ばす
	// 端点でも空きが多いなら中央へmove。使用領域が60%以上(根拠無)ならrealloc.
	// この間数のメリットは高速性と使いやすさでメモリは最大で40%無駄になる
	addsz++;	// \0
	
	//容量up 重い	60%以上でexpand
	if (addsz+1+obj->sz >= SG_TRIGRATIO * obj->msz){
		obj->msz *= 2;
		int reqsz = obj->sz + addsz +1;
		while(obj->msz<reqsz){obj->msz *= 2;}
		char* p = realloc(obj->p - obj->ofst, obj->msz);
		; if(!p){return -1;}	//STOP(!p, "realloc() failed");
		obj->p = p + obj->ofst;
	}
	//中心へ移動 重い どっちでも追加後中央付近になるように調整
	// edgeは0で先頭、-1で尻尾だから自動的に余裕がある状態に調整される
	int n_ofst = (obj->msz - obj->sz - addsz -1)/2 + (!edge)*addsz;
	char* n_p = (obj->p - obj->ofst) + n_ofst;
	
	memmove(n_p, obj->p, obj->sz);
	obj->p = n_p;
	obj->ofst = n_ofst;
	obj->p[obj->sz]=0;
	return 0;
}

/*  sg_free ... define */ 
void sg_free_alt(sg_t* obj) {
	if(!obj){return;}
	free(obj->p - obj->ofst);	obj->p=NULL;	
	obj->ofst=0;
	obj->sz=0;
	obj->msz=0;
	free(obj);
}

#ifdef TEST
#include "stringrow.h"		//*SH_co*	-D_GNU_SOURCE
#include "laptime.h"
#include "apr_strings.h"	//*SH_co*	libapr-1.a -lpthread -D_LARGEFILE64_SOURCE	*
#include "hcut.h"
#include "msgp.h"

HCUT_ADD(t_min) {
	int rc=0;
	sg_t* obj = sg_new();	// ""
	sg_add(obj, "foo");		// "foo"
	puts(obj->p);
	sg_free(obj);
	eq_p(obj, NULL);
}

HCUT_ADD(t_stringrow) {
	int rc=0;
	sg_t* obj = sg_new();	// ""
	sg_add(obj, "foo");		// "foo"
	eq_s("foo", obj->p);
	eq_i(3, obj->sz);

	sg_add(obj, "bar");		// "foobar"
	eq_s("foobar", obj->p);
	eq_i(6, obj->sz);
	
	sg_ins(obj, "baz");		// "bazfoobar"
	eq_s("bazfoobar", obj->p);
	eq_i(9, obj->sz);

	sg_ins(obj, "\0abc", 2);	// "(\0)zbazfoobar"	,use only 2byte
	eq_s("", obj->p);
	eq_i(11, obj->sz);

	sg_clear(obj);				// ""
	eq_s("", obj->p);
	eq_i(0, obj->sz);

	sg_addf(obj,"%#x",17);		// "0x11"	,fmt same as printf()
	eq_s("0x11", obj->p);
	eq_i(4, obj->sz);

	sg_bs(obj, 1);			// "0x1"	,del tail 1byte
	eq_s("0x1", obj->p);
	eq_i(3, obj->sz);

	sg_del(obj,2);			// "1"		,del head 2byte
	eq_s("1", obj->p);
	eq_i(1, obj->sz);

	sg_clear(obj);			
	rc = sg_add(obj, "0123456789");
	eq_i(rc, 10, "rtnsz ck");
	rc = sg_ins(obj, "0123456789");
	eq_i(rc, 10, "rtnsz ck");
	sg_ins(obj, "0123456789");
	sg_add(obj, "0123456789");
	eq_s("0123456789012345678901234567890123456789", obj->p);
	
	dbg(obj->p, obj->sz, obj->msz, obj->ofst);
	for(int i=0;i<sg_sz(obj);i++){
		printf("%d ", (obj->p - obj->ofst)[i]);
	}
	puts(obj->p);
	puts("");
//	exit(1);

	eq_i(40, obj->sz, "realloc() test");

	sg_clear(obj);			
	sg_add(obj, "01234567890123456789");
	sg_add(obj, sg_ptr(obj) );
	eq_s("0123456789012345678901234567890123456789", sg_ptr(obj), "inner ptr test");

	sg_clear(obj);			
	sg_add(obj, "01234567890123456789");
	sg_addf(obj, "%s", sg_ptr(obj) );
	eq_s("0123456789012345678901234567890123456789", sg_ptr(obj), "inner ptr test@f");
	
	sg_free(obj);
	eq_p(obj, NULL);
}
#endif

#ifdef TEST
HCUT_ADD(t_bm1) {
	sg_t* obj = sg_new();
	int i=100000;
laptime(0);
	while(i-- >0){ sg_add(obj, "abc"); }
laptime("add");
	sg_free(obj);
//real	0m0.009s
//user	0m0.006s
//sys	0m0.004s
}
#endif

#ifdef TEST
HCUT_ADD(t_bm2) {
	sg_t* obj = sg_new();
	int i=100*1000;
laptime(0);
	while(i-- >0){ sg_addf(obj, "%s", "abc"); }
laptime("addf");
	dbg(obj->sz);
	sg_free(obj);
//real	0m0.042s
//user	0m0.036s
//sys	0m0.005s
}
#endif

#ifdef TEST
HCUT_ADD(t_bm3) {
	sg_t* obj = sg_new();
	int i=100000;
laptime(0);
	while(i-- >0){ sg_ins(obj, obj->p, 3);	}
laptime("ins");
	sg_free(obj);
//real	0m3.541s
//user	0m3.539s
//sys	0m0.000s
}
#endif

#ifdef TEST
HCUT_ADD(t_bm4) {
	sg_t* obj = sg_new();
	int i=100000;
laptime(0);
	while(i-- >0){ sg_insf(obj, "%s", "abc"); }
laptime("insf");
	dbg(obj->sz);
	sg_free(obj);
//real	0m3.510s
//user	0m3.501s
//sys	0m0.008s
}
#endif

#ifdef TEST
HCUT_ADD(t_bm5) {
	apr_initialize();
	apr_pool_t* mp;
	apr_pool_create(&mp, NULL);

//	sg_t* obj = sg_new();
	int i=10 * 1000;
	char* p = "";
laptime(0);
	while(i-- >0){ p=apr_psprintf(mp, "%s123",p); }
laptime("apr_psprintf, 1/10");
	dbg(strlen(p) );
	apr_pool_clear(mp);	//再利用する場合
	apr_pool_destroy(mp);	//もういらない場合
	apr_terminate();
//	sg_free(obj);

//real	0m1.078s
//user	0m0.840s
//sys	0m0.237s
}
#endif

#ifdef TEST
HCUT_ADD(t_bm6) {
//	sg_t* obj = sg_new();
	int i=10 * 1000;
	char* p = "";
laptime(0);
	while(i-- >0){ asprintf(&p, "%s123",p); }
laptime("gnu asprintf()");
	dbg(strlen(p) );
//	sg_free(obj);

//real	0m0.373s
//user	0m0.251s
//sys	0m0.122s
}
#endif

#ifdef TEST
HCUT_ADD(t_bm7) {
//	sg_t* obj = sg_new();
	int i=100 * 1000;
	char* p = "abc";
laptime(0);
	while(i-- >0){ p=strdup(p); }
laptime("strdup()");
	dbg("100*1000");
//	sg_free(obj);

//real	0m0.018s
//user	0m0.009s
//sys	0m0.009s
}
#endif

/*SH_SMP
#include "*SH_bn*.h"
#include <stdio.h>

int main() {
	sg_t* obj = sg_new();		// ""
	sg_add(obj, "foo");		// "foo"
	char* p = sg_ptr(obj);		// get headptr
	puts(p);					// >> 'foo'
	int sz = sg_sz(obj);		// >> 3
printf("%d\n", sz);

	sg_add(obj, "bar");		// "foobar"
	sg_ins(obj, "baz");		// "bazfoobar"
	sg_ins(obj, "\0abc", 2);	// "(\0)zbazfoobar"	,use only 2byte
	sz = sg_sz(obj);			//	11,	sz is holding binary size
	sg_clear(obj);				// ""
puts(obj->p);
	sg_addf(obj,"%#x",17);		// "0x11"	,fmt same as printf()
puts(obj->p);
	sg_bs(obj, 1);			// "0x1"	,del tail 1byte
printf("%d\n", obj->sz);	// >> len=3

	sg_del(obj,2);			// "1"		,del head 2byte
	puts(obj->p);			// >> "1"
	
	sg_free(obj);
	return 0;
}

//SH_SMPE*/

#ifdef TEST
HCUT_RUN("stderr", 1,
t_stringrow, t_bm1, t_bm2, t_bm3, t_bm4, t_bm5, t_bm6, t_bm7 );
#endif


/*
 change log
 --
2021-06-02  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(rtnval) : omit 2021-03-25 rtnsz change.
	rtn is addsz. follows to standards, printf(), puts() etc. v2.1.0
	
2021-05-04  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(doc) : fix doc, addf/insf allows inner ptr
	
	* *SH_bn*.sh.c(sh) : update to new brp script

2021-03-25  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(addf/insf) : fix self str adding bug, add malloc()

2021-03-25  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(add/ins) : change rtn to predata sz, add ag3==-1/-2, fix doc

2021-03-06  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(add/ins) : allow add inner ptr, flg/pos calc, v2.0.3

2021-03-06  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(all) : change logic,  ins 3000ms >> 6ms. v2.0.2

2021-02-24  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(add/addf/ins/insf) : change rtn from datasz >> addsz
	
	* *SH_bn*.sh.c(pop/drop >> bs/del): change apiname

	* *SH_bn*.sh.c(sg_ptr): add 2nd args, ptr offset.

2021-02-20  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(all) : rewrite. v2.0.0

2020-06-12  Momi-g	<dmy@dmy.dmy>

	* *SH_bn*.sh.c(ERRact) : add #if switch.

*/

/*SH_ED*/

/*SH_OP _ set -e;a=`sed -ne "/${C}DF/!d;:l;n;/${C}DE/q;p;bl"<$R0`;eval "$a"	#*/
/*SH_OP	h $p"-tsbS:test/eg/.o/.so -LMP:leak,mem,prof -f:funcs -o:bldout		GPLv3+"	 #*/
/*SH_OP	f sed -ne "/${C}DF/q;/;/d;/^[a-z].*)/p"<$R0 #*/
/*SH_OP t $e"$CW";ftt "$@";$p'cc -O0 -Wall -pedantic -g -pg -ggdb3 $tf $Rs `fOI $Rs $tf` `fg $Rs $tf` `fL`'|fv	#*/
/*SH_OP T $e"$CW";ftt "$@";$p'cc -O2 $tf $Rs `fOI $Rs $tf ` `fg $Rs $tf ` `fL`'|fv	#*/
/*SH_OP s $e"$CB";fgr0 "${C}SMP" "${C}SMPE"<$Rs|fbn>eg.c;$p'cc eg.c `fg eg.c`'|fv #*/

/*SH_OP L $p"valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose ./a.out 2>&1|sed -e '/SUMMA/!d;n;n;n;n'"|fv #*/
/*SH_OP M $p"fM ./a.out"|fv	 #*/
/*SH_OP P $p'valgrind --tool=callgrind --callgrind-out-file=log.out ./a.out;kcachegrind log.out'|fv	 #*/

/*SH_OP b $e"$CW";$p'cc -c $Rs -pedantic -O2 -Wall -g `fg $Rs` `fI $Rs`'|fv;$p"$bn.o"	#*/
/*SH_OP B $e"$Cb";$p"ar -r lib$bn.a $bn.o `fO $Rs`"|fv;$p"lib$bn.a"	#*/
/*SH_OP A $e"$CB";$p'fA lib$bn.a `fg $Rh $Rs|fu|grep '[.]a$'|fU`'|fv;$p"lib$bn.a" #*/
/*SH_OP S $e"$Cb";$p"cc -shared -fPIC -o lib$bn.so $bn.o `fOI $Rs` `fg $Rs`"|fv;$p"lib$bn.so" #*/
/*SH_OP W $e"$Cm$O$Cw">/dev/null;$i0;$i1;$p"$Rs $Rh $tf"	#*/
/*SH_OP o $e"$CW";$p"rm $tf"|fv;fman $R0 3	#*/

/*SH_DF
#-- noob
fman()(fgr0 "${C}doc" "${C}docE"<$1>$Rm
cat $Rm|sed -e's/^@\([_a-zA-Z][_[:alnum:]]*\)/\n\n# \1\n\n/g
/^\\/{s/^\\/<br>/g}'>$bn.md
cat $bn.md|pandoc -f markdown -thtml> $Rm
cat $Rm|pandoc -s -f html -t man -M title="$bn" -M section="$2" \
-M date=`date '+%Y-%m-%d'` > $bn.$2
sed -e 's/^.nf/.RE\n.nf/'<$bn.$2>$Rm
mv $Rm $bn.$2
fhtml
)
fhtml()( md2h $bn.md $bn )

#-- local

#-- vars
bn=`basename ${Rs%.*}`; tf=${Rs%/*}/${bn}.ts.${Rs##*.}; e="eval "; p="$Rp"
#-- mod
fv()(while read -r a;do $e"cat<<E$O# $a${O}E"|sed -e 's@-L.*-L[^ ]*@-L(omit)@g'>/dev/stderr;$e"$a";done)

fbn()(sed -e "s@\*${C##*]}bn\*@$bn@g"|frf)
fsn()(tr -s ' \t' '\n')
fsl()(tr -s '\n' ' ')
fu()(fsn|sort -u)
fU()(fu|fsl;$p)

fgr()(sed -e "/$1/!d;:l;/$2/{p;d};n;bl")	#切出
fgr0()(sed -ne "/$1/!d;:l;n;/$2/d;p;bl")	#抜き切出
fgR()(sed -ne "/$1/bl;p;d;:l;n;/$2/d;bl")	#切すて
fg()(sed -ne "s/.*${C##*]}co\*\([^*]*\).*$/\1/p" "$@"|fsn|awk '!a[$0]{a[$0]=1;print}'|fsl)

# fO src.o from inc"src.abc" etc. kick self
fO()(set -- `fdp "$@"|awk '$0~/[.](h|hpp)$/{print}'|sed -e 's/[.][^.]*$/.o/'|fU`
	buf="";for i;do test -f $i&&buf="$buf $i";done;$p"$buf"
)
fI()(fdp "$@"|sed -e 's/[^/]*$//g'|fu|sed -e '/./s/^/ -I/g'|fu|grep -v '^\-I$'|fU)
fL()(find -L `dirname $R0` -type d|sed -e 's/^/-L/g'|fU)
# inc""系.h,hpp,oをパス付きで羅列 OIはfdpが重複するので高速化でまとめる 複数file_ok
fOI()(
set -- `fdp "$@"`
s="-I./ "`$p"$@"|sed -e 's/[^/]*$//g'|fu|sed -e 's/^/ -I/g'|fu|grep -v '^\-I$'|fU`
set -- `$p"$@"|awk '$0~/[.](h|hpp)$/{print}'|sed -e 's/[.][^.]*$/.o/'|fU`
buf="";for i;do test -f $i&&buf="$buf $i";done;
$p"$buf $s"
)

# 依存inc""を再帰的に取得./以下全て self系はkick
fdp()( l="$*"; paths="$@"; all=""; used=""
 while :;do
	all=`$p$all $paths|fU`	#差分を追加 repの始末 差分たちからaaa.hを取得 partial path
	buf=`(cat $paths|sed -ne 's@^[ \t]*#inc[^"]*.\([a-zA-Z0-9._]*\)".*@\1@p')|sort -u`
	ch=`$p$used $buf|tr -s ' ' '\n'|sort|uniq -u`	#使用済は外す
	used="$used $ch"	#リスト更新
	paths=`fsvy $ch|sort -u`	#ls検索 name系のみのはず
	buf=`$p"$all" "$paths"|fU`	#増えたらloop
	[ ${#all} = ${#buf} ]&&break
 done
# initを除く
 set -- $all
 for i;do a=${i##*[/]}; a=${a%%.*};[ "${l##*$a*}" = "$l" ]&&set -- "$@" $i;shift;done
 $p"$@"
)

# corecode:search + depthck + uniq
fsvy()(c="find -L ./ -false"
	for i; do c="$c -o -path '*'$i";done; l=`$e"$c"`
	for i; do $p"$l"|grep -F "$i"|awk '{sv=$0;print gsub("[/]","") " " sv}'|
	sort -k 1.1,1n -k 2.2,2|awk '{print $2;exit}'; done
)

# libをまとめる
fA()(n=0;dir=`dirname $0`/tmpdir;mkdir $dir;cd $dir;
 for i;do
 	n=$((n+1))
 	cp ../$i $i
 	ar -x $i
 	for ii in *.o;do mv "$ii" "p${n}_$ii";done
 	ar -r lib$bn.aa *.o
 	rm *.o
 done
 $p'mv lib$bn.aa ../lib$bn.a'|fv
 cd ..;rm -r $dir
)

#-- yacc
# /*SH_OP y $e"$CW";fy
# /*SH_OP Y $e"$Cy";fU $( ($p"lib$bn.a";fg $Rs $Rh)|$n|grep '[.]a$'|$U)
fy()(
cat<<'EEE'|fv
f0 "${C}YACC" "${C}YACCE"<$Rs>myyacc.y
f0 "${C}LEX" "${C}LEXE"<$Rs>mylex.l
lex mylex.l; yacc -p zz -dv myyacc.y
cat y.tab.c lex.yy.c > $Rs
gcc -c y.tab.c lex.yy.c -lfl `fA $Rs $Rh`
rm mylex.l myyacc.y lib$bn.a
ar r lib$bn.a `fo $Rs` y.tab.o lex.yy.o
$p"lib$bn.a"
EEE
)

#-- longcmd
frf()(
 awk -v r="${C##*]}rf" 'match($0,r){
 s=substr($0, RSTART+RLENGTH+1)
 gsub(/.[^*]*$/, "", s);split(s, a)
 m="[ -f %s ]&&echo \"/*--copyfrom %s*\"/&&cat %s&&echo \"/*--copyend %s*\"/"
 for(i=1;v=a[i];i++){ system( sprintf(m, v,v,v,v))}
 next
 }
 {print}'
)

ftt()(a="`sed -ne 's@^HCUT_ADD(\([^)]*\).*@\1, @p' $tf|tr -d '\n'`NULL"
	if [ $# != 0 ];then	a=""; for i;do a="$a $i,";done; a="$a NULL"; fi
	sed -ne "p;/_RUN/bl;d;:l;/[)]/{c\\$O $a)$O p;d};n;bl"<$tf>$Rm;mv $Rm $tf)
i0=$e'fgr0 "^#ifdef TEST" "^#endif"<$R0|fbn>$tf'
i1=$e'fgR "^#ifdef TEST" "^#endif"<$Rs|fbn>$Rm;mv $Rm $Rs;fbn<$Rh>$Rm;mv $Rm $Rh'
fM()(
 valgrind -q --tool=massif --massif-out-file=./vmem.buf --stacks=yes --trace-children=yes $1>/dev/null
 ms_print ./vmem.buf|sed -ne '/[KMG]B/bl;d;:l;/snap/q;p;n;bl';rm ./vmem.buf)

/*SH_DE*/
