/* 
 Copyright (C) 2020 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/>.
*/

/*SH_doc
@NAME ped - string edit api using peg. allows binary input.
@SYNOPSIS
<pre>
typedef struct pedstate_tag{
	const char* ruleinfo;
	const char* emsg;
	// other opaque members
	// ...
} ped_t;

typedef struct ped_rtntag{
	int rc;
	const char* emsg;
	const char* bin;
	int binsz;
} ped_rt;

ped_t* ped_new(const char* mode, const char* rstr [, int rstrsz, const char* nlstr, int nlstrsz]);
void ped_free(ped_t* obj);
ped_rt ped_parse(ped_t* obj, const char* instr [, int instrsz]);
void ped_reset(ped_t* obj);
char* ped_fileread(const char* name [,int* sz]);	//fread() wapper
</pre>

@DESCRIPTION
see example or ~$ ped -H for detail usage.	

<pre>
- ped_t* ped_new(mode, rstr [, rstrsz, nlstr, nlstrsz] ): make parser

  mode: option setting string. mode="dr" works as ~$ ped -d -r
    r: use expand mode
    R: use expand mode with locale 'C' regex
    n: noout if hit norule. same as ~$ sed -ne '/xyz/'
    N: stop if hit norule.
    t: output CST string instead of normal result.
    T: output full CST string. slow.
    d: set pegrule infostr to rtn->ruleinfo.
    
    -- below 2 opts doesnt make parser. you cant use ped_parse().
    h: set usage str to rtn->ruleinfo.
    H: set detail usage str to rtn->ruleinfo.
    V: set ped application version msg to rtn->ruleinfo.
  
  rstr  : pegrule string ptr. eg) "rule1 <- 'abc'? [0-9]"
  rstrsz: optional. use strlen(rstr) if noset.
  nlstr : optional. just only use for emsg info. dfl is "\n". string is
         convert with c99 charesc parser. "\n"="\\n"="\134n"="\\u000a".
         this arg never affects parsing result. allow any str. eg) "x\0z"
  nlstrsz: optional. use strlen(nlstr) if noset.

  return: ped_t* rtn, malloc()ed ptr. you always have to ped_free().
    rtn->ruleinfo: set infostr if you use 'd,h,H,V' opt.
    rtn->emsg    : set emsgstr and errno if pegrule is invalid.
  
- ped_free(obj): destroy obj

- ped_parse(obj, instr [, instrsz]): i/o api. substitute stdin/stdout
  obj    : ped_new() return ptr.
  instr  : input strptr. send EOF if set NULL. accept len 0 str "". 
  instrsz: optional. use strlen(instr) if noset. send EOF if set -1.

  return: ped_rt rtn, rtn.rc holds rtndata type.
   rtn.rc>0: request str. rc=8 means 'send 8 or more byte if you can'
     0 : parsed all input. completely suceed.
    -1 : request all string until EOF.
    -2 : rtn parsed str. see rtn.bin. set ped_parse(obj,"") to continue
    -10: catch err. see rtn.emsg. state was reset/initilized.
    ...if rc==7 but leftstr/buffsz is only 3byte, send 3byte plz.
  
  rtn.bin  : set result str if rtn.rc= -2. ptr exists in inner buff.
  rtn.binsz: str size.
  rtn.emsg : set emsg str if rtn.rc= -10. ptr exists in inner buff.
  ...inner buffer is short-lived, so save it if necessary.

- ped_reset(obj): reset state. prepare for parse other file/input etc
 
- ped_fileread(name [,int* sz]): file reader. fread() wrapper
  name: filename. eg) ped_fileread("myrule.peg")
  sz: optional. set bytesize if exists. eg) ped_fileread("a.txt", &buf)

  return:char*, malloc()ed bin/emsg(errno!=0). you always have to free()
</pre>

@EXSAMPLE
```
#include <stdio.h>
#include "ped.h"
int main(int argc, char** argv) {
	// see detail for pegrule: ~$ ped -H
	const char* rstr =
		" RULE1 <- 'abc'	{_1 = 'ABC'} "
		" RULE2 <- [0-9]	{_1 = '@' _1 '@ ' } "
		" RULE3 <- .		{_1 = '*'} "
	;
	ped_t* obj = ped_new("dr", rstr);
	puts(obj->ruleinfo);	//debug etc

	const char* s = "abcxyz123\377";
	ped_rt res = ped_parse(obj, s);
	while(1){
		if(res.rc == 0){ break; }
		else if(res.rc == -1 || res.rc>0){ res = ped_parse(obj, NULL); }
		else if(res.rc == -2){
			printf("%.*s", res.binsz, res.bin );
			res = ped_parse(obj, NULL);
		}
		else if(res.rc== -10){ puts(res.emsg); return 1;}
	}
	ped_free(obj);
	return 0;
}
// ~$ gcc src.c libped.a -lm -ldl
// ~$ ./a.out	#>> ruleinfo + ABC***@1@ @2@ @3@ *

--- 
#include <stdio.h>
#include <stdlib.h>
#include "ped.h"
int main(int argc, char** argv) {
	int rc=0, errno=0;		//luka: self-hosting pedrule(C >> luajit)
	char* p = ped_fileread("luka.ped", &rc);
	if(errno){ puts(p); free(p); exit(1); }
	ped_t* obj = ped_new("r", p);
	if(errno){ puts(obj->emsg); ped_free(obj); exit(1); }
	free(p);

	while(1){
		char arr[1] = {0};
		char* buf = arr;
		rc = fread(buf, 1, 1, stdin);
		if(rc==0){ buf=NULL;}
		ped_rt res = ped_parse(obj, buf, 1);
		if(res.rc == 0){ break; }
		else if(res.rc == -1 || res.rc>0){;;}
		else if(res.rc == -2){ printf("%.*s", res.binsz, res.bin ); }
		else if(res.rc== -10){ puts(res.emsg); break; }
	}
	ped_free(obj);
	return 0;
}
// ~$ gcc -static -Wall -pedantic src.c libped.a -lm -ldl
// ~$ echo "if(a==0){a=1}" | ./a.out	#>> if a==0 then a=1 end
```

@OPTIONS	-
@EXIT_STATUS ped_new(), ped_fileread() set errno if err/failed.
@RETURN_VALUE
<pre>
ped_t* ped_new()  :	suc/fail == ptr/ptr(errno!=0, ptr->emsg)
void ped_free()   : -
ped_rt ped_parse(): check rtn.rc. set rtn.emsg if rtn.rc= -10(err)
void ped_reset()  : -
char* ped_fileread(): suc/fail == ptr/ptr(errno!=0)
</pre>
@ERRORS output emsg and exit(1) if fatal err.
@NOTES
- sloppy benchmark:

<pre>
-- ped vs sed	(1cpu 2.8GHz)
sed  : sed -e 's@[a-zA-Z_][a-zA-Z0-9_]*@-@g'
ped  : ped -re 'rule1 <- ![0-9] [a-zA-Z0-9_]+	{_0 = "-"}'
>>>
	sed: real 0m0.517s
	ped: real 0m0.618s
	...130-150ms to convert 1000 lines (in ped self-hosting) 
</pre>

@CONFORMING_TO POSIX.1-2001+
@BUGS \-
@COPYRIGHT Copyright 2020 momi-g, GPLv3+
@VERSION 2021-07-10 v2.0.1
@SEE_ALSO
<pre>
https://pdos.csail.mit.edu/papers/parsing:popl04.pdf
https://en.wikipedia.org/wiki/Parsing_expression_grammar
</pre>
//SH_docE*/
//set_clists()でgnuregのfastmapが必要
#ifndef _GNU_SOURCE
	#define _GNU_SOURCE
#endif
#include <stdio.h>
#ifndef _DEFAULT_SOURCE
	# inclide "this src needs -D_GNU_SOURCE / glibc"
#endif

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

#include <unistd.h>	//sleep(), fork()
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>	// off64_t, socketpair() compatible
#include <sys/socket.h>

#include <sys/select.h>	//select
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <signal.h>
#include <langinfo.h>
#include <locale.h>
#include <iconv.h>
#include <regex.h>
#include <assert.h>

// below:--- ped_t define
//#define ped_5906e77bc764_SRC
//#include "ped.h"
#include "stringrow.h"
#include "dq.h"
#include "ureg.h"	//*SH_co*	libureg.a	*
#include "charbits.h"
#include "lbc.h"	//*SH_co*	liblbc.a	*

#include "msgp.h"
#include "laptime.h"

typedef struct msgserver_tag {
	sg_t* instr;	//inbuff
	int instr_cur;	//inbuff
	sg_t* outstr;	//outbuff
	int bufsz_min;	// badic bufsz: 128
	
	dq_t* jidx;		// order lists
	dq_t* strq;
	dq_t* strqsz;	
	int flg_reqstr;
	int jidx_reqstr;
	int sz_reqstr;
	int wait_jidxinfo;	//order_block
	
	int* pidarr;
	int* sfdarr;
	int ppid;
	int jobs;
	
	int vflg;
	char* emsg;	//jstとややかぶるけど元締めとして保持する必要がある
	int seof;
	int bcnt;
	int nlcnt;
	int ccnt;
	char* nlstr;
	int nlstrsz;
} msv_t;

// jbm report: socket() data cant treat negative number data
enum {
	J_IOLOST = 9
	, J_SENDSTR_E = 5
	, J_CLOSE = 4
	, J_RESET = 3
	, J_VICINFO = 2
	, J_REQSZ = 1
	, J_SENDSTR = 10	//>=10, holds strdata
	, J_EMSG = 20
	, J_SUM
};
typedef struct jobmsg_tag{
	int flg;
	int bcnt; int nlcnt; int ccnt;
	int reqsz;
	char* bin; int binsz;
	char* emsg;
} jbm_t;

// rules const data
typedef struct rtoken_tag {
	const char* ctg;
	const char* data;
	int num;
	const char* pre;
	const char* suf;
} rtoken_t;
typedef struct atoken_tag {
	const char* ctg;
	const char* data;
	int num;
	int rnum;
	const char* rname;
} atoken_t;
typedef char* clist_t;

typedef struct peg_rulesdata_tag{
	rtoken_t* rtokens;	//-1 -9, -9-9のセンチネル付きだったはず
	int* rhead;		// idx = rhead[rulenum] >>  tk = rtokens[idx] >> rname=tk.data
	atoken_t* atokens;
	int* ahead;
	ureg_t** rglists;	// regex = rglists[tk.num] 
	clist_t* clists;	// *arr[255] = clists[rpos], rpos0 == rule1
//補助データ
	lbc_t* lvm;		//src
	const char* emsg;
	char* svlocale;
	int last_rpos;		// rpos==last_rpos >> %RULEのidx
	int norule_rpos;	// rpos==5 >> norule
	int tmode;
	int regmode;
	const char* nlstr;
	int nlstrsz;
} rdata_t;

enum {
	// resarr mode
	TK_LIT=0
	,TK_LOOP
	,TK_LOOPEND
	,TK_IDENT
	,TK_RTN
	,TK_MARK

	,TK_SUM
};
typedef struct resarr_tag {
	int mode;	// K_LIT etc
	int num;	//litsz, jmpdst
	int rpos;
	int tpos;
	int curpos;	//litdata == lit[curpos] - lit[curpos+num-1]
} resarr_t;

//jmp
typedef struct peg_jumpstate_tag {
	char* emsg;
	int bcnt;
	int nlcnt;
	int ccnt;
	char* nlstr;
	int nlstrsz;
//
	sg_t* instr;
	sg_t* outstr;
	int curpos;	//now charpos
//
	int rpos;
	int tpos;
	int seof;
	resarr_t* resarr;
	int resepos;
	int resmaxsz;
//
	int* ipos_mem;	//improve backtrack marker
	int ipos_c;
	int ipos_e;
//
	jbm_t rtn;
} jst_t;

//edbuf
typedef struct peg_editor_buff_tag {
	dq_t* edq; 
	sg_t* sg0;
	sg_t* sg1;
	sg_t* sg2;
	sg_t* sgbuf;
} edbuf_t;

// fulldata
typedef struct pedstate_tag {
	const char* ruleinfo;
	const char* emsg;
//
	msv_t* msv;		//msg_server
	rdata_t* rdata;	//ruledata
	jst_t* jst;		//jump_state
	edbuf_t* edbuf;	//edit_strbuff
} ped_t;

// editor funcs
// edit mode 全てマイナス flgが>0はlitカウントに使う
enum {
	INFO_FO = -1
	,INFO_FE = -2
	,INFO_JO = -3
	,INFO_JE = -4
	,INFO_NL = -5
	,INFO_SEP= -9
	,INFO_IG = -99
};
enum {
	SRC_PTR=1
	,SRC_1
	,SRC_2
};
typedef struct edit_tag{
	int flg;	// INFO_XXX igとか
	int rnum;	// 基本はcurrent位置。jmpとbackはlaで取得する
	int tnum;
	int src;	// SRC_0 SRC_1 SRC_2
	int pos;	// ptr == ptr(src) + pos
} ed_t;
DQ_IMPL(static, ed_t, e)

//edit時にfldを切り出すがarrなので単純には取れない gfldで範囲指定して結合
typedef struct i2_tag{
	int i1;
	int i2;
} i2_t;

#define ped_5906e77bc764_SRC
#include "ped.h"
static char* ped_info(const char* mode, const char* rstr, int rstrsz);

//state
static void ped_fork(ped_t* ped, int sfd);
static void reset_jobs(ped_t* ped, int flg);
static msv_t* msv_new(const char* mode, const char* nlstr, int nlstrsz);
#define msv_free(msv)	(msv_free_impl(msv), msv=NULL)
static void msv_free_impl(msv_t* msv);
static rdata_t* rdata_new(const char* mode, const char* rstr, int rstrsz, const char* nlstr, int nlstrsz);
#define rdata_free(obj) (rdata_free_impl(obj), obj=NULL)
static void rdata_free_impl(rdata_t* obj);
static char* set_rglists(rdata_t* obj) ;
static void reset_rglists(rdata_t* rdata);
static const char* set_clists(rdata_t* rdata);
static void reset_clists(rdata_t* rdata);
static jst_t* jst_new(const char* nlstr, int nlstrsz);
#define jst_free(obj)	(jst_free_impl(obj), obj=NULL)
static void jst_free_impl(jst_t* obj);
static void jst_reset(jst_t* jst);
static edbuf_t* edbuf_new(void);
static void edbuf_free(edbuf_t* obj);

//io
static int jidx_scwritable(ped_t* ped);
static int jidx_screadable(ped_t* ped, int flg) ;
static int jbm_scwrite(ped_t* ped, int fd, jbm_t* data) ;
static jbm_t jbm_scread(ped_t* ped, int fd, sg_t* sgobj) ;
static int calc_sendsz(ped_t* ped, int reqsz);

//jmp
static jbm_t ped_jmp(ped_t* ped);
static rtoken_t* gettoken(ped_t* ped, int rpos, int tpos);
static void res_push(jst_t* jst, int mode, int n);
static void pfix_restore(ped_t* ped);
static int fix_result(ped_t* ped, int rc);
static void nlpos_update(ped_t* ped);
static int jmp_suc(ped_t* ped);
static int jmp_fail(ped_t* ped);
static ureg_t* getrg(ped_t* ped, int rpos, int tpos);
static const char* jmp_seterr(ped_t* ped, const char* msg);
static void ipos_push(jst_t* jst, int idx);
static int ipos_pop(jst_t* jst);

//edit
static jbm_t ped_edit(ped_t* ped);
static char* edit_rstop(ped_t* ped, int rnum, char* bin, int binsz, int cur);
static atoken_t* edit_getatk(ped_t* ped, int rnum);
static int edit_act(ped_t* ped, ed_t* earr, int sz);
static int edit_doedit(ped_t* ped, ed_t* earr, int ejmp);
static i2_t edit_gfldidx(ed_t* earr, int sdx, int fld);
static int edit_gazlit(ped_t* ped, ed_t* earr, int s, int e, sg_t* sg);
static int edit_displit(ped_t* ped, ed_t* earr, int idx);
static char* edit_write_str(ped_t* ped, ed_t* earr, int earrsz);
static char* edit_srcptr(ped_t* ped, ed_t ed);
static char* edit_write_tree(ped_t* ped, ed_t* earr, int earrsz);
static char* my_itoa8(int num);


#define RESARR_INITSZ	128		//自動伸長タイプ



int fkcnt_[10]={0};
int* clcnt = fkcnt_;
// errはerrno!=0で戻る
// mode="rRtTnNv3hHd"など 数値はjob数
ped_t* ped_new_impl(const char* mode, const char* rstr, int rstrsz, const char* nlstr, int nlstrsz, ...) {
	clcnt[0]=0;	clcnt[1]=0;	clcnt[2]=0;	clcnt[3]=0;
	char* emsg = NULL;
	if(rstrsz<=0){rstrsz=strlen(rstr); }
	if(nlstr==NULL){ nlstr = "\\n"; nlstrsz=2;}
	if(nlstrsz<=0){ nlstrsz=strlen(nlstr); }
	//info系は先行して始末
	;ERRact( strspn(mode, "rRtTnNdhHV0123456789")!=strlen(mode) && (errno=EINVAL)
		, "optstr allows only 'rRtTnNdhHV0-9'"
		, fprintf(stderr, "bad ag1: %s\n",mode); exit(1) );
//
	ped_t* ped = (ped_t*)calloc(1, sizeof(ped_t) );
	;STOP(ped==NULL, "calloc() failed");
//info
	if( strchr(mode,'h') ){ ped->ruleinfo = ped_info("h", rstr, rstrsz); }
	else if( strchr(mode,'H') ){ ped->ruleinfo = ped_info("H", rstr, rstrsz); }
	else if( strchr(mode,'V') ){ ped->ruleinfo = ped_info("V", rstr, rstrsz); }
	else if( strchr(mode,'d') ){ ped->ruleinfo = ped_info(mode, rstr, rstrsz); }
	if( strspn(mode, "hHV") ){ return ped; }

//maindata
	ped->msv = msv_new(mode, nlstr, nlstrsz);
//	
	errno=0;
	ped->rdata = rdata_new(mode, rstr, rstrsz, nlstr, nlstrsz);
	if(errno){
		//emsgはmemされてる
		ped->rdata=NULL;
		emsg=(char*)ped->rdata;
		goto lb_ERR;
	}
//
	ped->jst = jst_new(ped->rdata->nlstr, ped->rdata->nlstrsz);
	if(errno){ emsg=(char*)ped->jst; goto lb_ERR; }
	ped->edbuf = edbuf_new();
//
//launch subproc: fknum, ppid, fork()
	int fknum = ped->msv->jobs -1;
	for(int i=0; i<fknum; i++) {
		int fd[2];
		int pid;
		int rc = socketpair(AF_UNIX,SOCK_STREAM, 0, fd);
		; if(rc){ emsg=strdup(strerror(errno)); goto lb_ERR; }
		pid = fork();
		if(pid==0) {
			//cpid
			close(fd[1]);	//不要な端点は閉じないとeofが飛ばなくなる
			ped_fork(ped, fd[0]);	//大抵子供がstdinメイなので0がよさそう
			exit(0);
		}
		//ppid
		close(fd[0]);
		; if(pid<0){ emsg=strdup(strerror(errno)); goto lb_ERR; }
		//混線の可能性はあるけどjobsで管理する。pipeを二つ作って分離もアリ
		//[0]はselfの情報にしとく pidはflgで使うので常に>0?
		ped->msv->pidarr[i+1] = pid;
		ped->msv->sfdarr[i+1] = fd[1];
	}
//laptime("open_ped");exit(1);
	return ped;
lb_ERR:;
	ped->emsg = emsg;
	return ped;
	DQ_VOIDCALL();
}

void ped_free_impl(ped_t* ped){
	if(ped==NULL){return;}
//	reset_jobs(ped, -1);	//	kill fork
// forkの前の可能性もあるのでreset系は使わずshutdown 運用でreadになってるはず
	free((void*)ped->ruleinfo);		//const外し
	free((void*)ped->emsg);
//
	if(ped->msv && ped->msv->jobs>1){
		int* pidarr = ped->msv->pidarr;
		int* sfdarr = ped->msv->sfdarr;
		for(int i=1; pidarr[i]; i++) { shutdown(sfdarr[i], SHUT_RDWR); }
	}
	//forkの手前だとpidadd[n]は全部0なので自動的にskip
//
	edbuf_free(ped->edbuf);
	jst_free(ped->jst);
	rdata_free(ped->rdata);
	msv_free(ped->msv);
	*ped = (ped_t){0};
	free(ped);
}

// err,別ファイルを読みたいとき。rules系とedbuf系はノータッチ
void ped_reset(ped_t* ped) {
	if(ped==NULL){return;}
	reset_jobs(ped, 0);
	msv_t* msv = ped->msv;
//	s_emsg[0]='\0';
	sg_clear(msv->instr);
	sg_clear(msv->outstr);
	msv->instr_cur=0;
// result mem
	msv->bcnt=0;
	msv->nlcnt=0;
	msv->ccnt=0;
	msv->seof=0;
	msv->wait_jidxinfo=0;
//	
	idq_reset(msv->strq);
	idq_reset(msv->strqsz);
	idq_reset(msv->jidx);
	for(int i=0;i<msv->jobs;i++){
		if(msv->pidarr[i]<0){ msv->pidarr[i] *= -1; }
	}
}

char* ped_fileread_impl(const char* fname, int* sz, ...){
	char* p = NULL;
	sg_t* sg = sg_new();
	FILE* fp = fopen(fname, "r");
	if(fp==NULL){
		sg_addf(sg, "%s: %s", strerror(errno), fname );
		p = strdup(sg->p);
		if(sz){ *sz = -1;}
		goto lb_RTN;
	}
//
	char buf[128]={0};
	int cnt=0;
	while(1){
		int rc = fread(buf, 1, 128, fp);	//1x128 = 128byte  
		if (rc <=0) { break;}
		sg_add(sg, buf, rc);
		cnt+=rc;
	}
	fclose(fp);
	p = strndup(sg_ptr(sg), sg_sz(sg) );
	if(sz){ *sz = sg_sz(sg);}
lb_RTN:;
	sg_free(sg);
	return p;
}



#include <inttypes.h>
//rdataでも再利用するので内部ではなく外部で宣言しとく
#include "util.h"
#include "ped_ljmod.h"
// singlefunc
static char* ped_info(const char* mode, const char* rstr, int rstrsz) {
	const char* fcstr = NULL;
	char* p=strpbrk(mode, "HhdV");
	while(p){
		if(*p=='h'){ fcstr = "ped_help()"; }
		else if(*p=='H'){ fcstr = "ped_Help()"; }
		else if(*p=='d'){ fcstr = "ped_makeinfo(mode, rstr)"; }
		else if(*p=='V'){ fcstr = "ped_version()"; }
		break;
	}
	; STOP(!fcstr, "info opt H/h/d/V not found");
	lbc_t* lvm = lbc_new();
	lbc_reqbcM(lvm, util, ped_ljmod);
	lbc_runstr(lvm, 0, NULL, "return");
	int rc = lbc_pdirectf(lvm, "local ffi = require('ffi')"
		" local rstr = " LBC_P2S
		" local mode = " LBC_P2S
		" local m = require('ped_ljmod')"
		" local gsave, emsg = m.%s"
		" return gsave, emsg"
	, rstr, rstrsz, mode, strlen(mode), fcstr);
	//本来ならif(rc<0)だけど出力がobj[0][1]で同一なので共通化できる
	if(rc<0){ errno=EINVAL; }
	lbc_rt res = lbc_tbval(lvm, 0, 1);
	if(res.tp=='N'){
		res = lbc_tbval(lvm, 0, 2);
		errno=EINVAL;
	}
//	; STOP(res.tp=='N', "fatal, failed to get peginfo");
	p = strndup(res.s, res.sz);
	lbc_free(lvm);
	return p;
}

static void reset_jobs(ped_t* ped, int flg) {
	int* sfdarr = ped->msv->sfdarr;
	sg_t* sgbuf = ped->edbuf->sgbuf;
	//cpid
	while(1) {
		int jidx = jidx_screadable(ped, 0);
		if(jidx<0){ break; }
		int sfd = sfdarr[jidx];
		sg_clear(sgbuf);
		jbm_scread(ped, sfd, sgbuf);	//読み捨て	reqも読み捨ててjb_scwrite()でRESET送信
	}
	//send reset_sig
	jbm_t jbm = {0};
	jbm.flg = J_RESET;
	if(flg<0){ jbm.flg = J_CLOSE;}
	for(int i=1; i < ped->msv->jobs;i++){
		int rc = jbm_scwrite(ped, sfdarr[i], &jbm);
		STOP(rc, "fatal err");
	}
	//self
	jst_reset(ped->jst);
}

static void ped_fork(ped_t* ped, int sfd){
	sg_t* sg = ped->edbuf->sgbuf;
	jst_t* jst = ped->jst;
	jbm_t jbm={0};
	int rc=0;
lb_TOP:;
	while(1){
		jbm = jbm_scread(ped, sfd, sg);	//blocking
//dbg("cpid order", sfd, jbm.flg, jbm.binsz);
		if(jbm.flg==J_VICINFO){
			jst_reset(jst);
			ped->msv->bcnt=jbm.bcnt;
			ped->msv->nlcnt=jbm.nlcnt;
			ped->msv->ccnt=jbm.ccnt;
			continue;
		}
		if(jbm.flg==J_CLOSE){ goto lb_EXIT; }
		if(jbm.flg==J_RESET){
			jst_reset(jst);
			continue;
		}
		if(jbm.flg==J_SENDSTR){
			char* bin = jbm.bin;
			int binsz = jbm.binsz;
//dbg(bin, binsz, jst->seof);
			if(binsz == -1 && jst->seof==0) { jst->seof = 1; }	//-1 打ち止め
			else { sg_add(jst->instr, bin, binsz); }	//単純追加
			break;
		}
		if(jbm.flg==J_SENDSTR_E){
			if(jst->seof==0) { jst->seof = 1; }	//-1 打ち止め
			break;
		}

		if(jbm.flg==J_IOLOST){	goto lb_EXIT; }
		; STOP(1, "fatal err");
	}
	//binとかcurとか準備してメイン始動
	jbm = ped_jmp(ped);	//vicはped_jmp()内部で最低サイズを保証してもらう
	rc = jbm_scwrite(ped, sfd, &jbm);
//dbg(rc, jbm.bin, jbm.flg, jbm.reqsz, J_VICINFO, J_REQSZ);
	if(rc<0){ goto lb_EXIT; }
	if(jbm.flg!=J_VICINFO){ goto lb_TOP;}
	//J_EMSG / J_REQSZ / J_VICINFO の三つだけ SENDはeditが終わった後のみ
	jbm = ped_edit(ped);
	rc = jbm_scwrite(ped, sfd, &jbm);
	if(rc<0){ goto lb_EXIT; }	//sendかemsgのどっちか
	goto lb_TOP;
lb_EXIT:;
	exit(0);
}

static msv_t* msv_new(const char* mode, const char* nlstr, int nlstrsz){
	msv_t* msv = (msv_t*)calloc(1, sizeof(msv_t) );
	msv->instr = sg_new();
	msv->outstr = sg_new();
	msv->bufsz_min = 128;
	//一回でやりとりする最小サイズ 大きすぎても無駄が生じる
	//jmpの完了でも少なすぎるとioに時間を食われるので調整が難しい128-1024あたり
	msv->flg_reqstr = 0;	//dfl
	msv->jidx_reqstr= 0;
	
	msv->jidx = idq_new();
	msv->strq = idq_new();
	msv->strqsz = idq_new();

	int jobs=0;
	char* p = strpbrk(mode, "0123456789");
	if(p){ jobs = (int)strtol(p, NULL, 10); }
	if(jobs<=0){jobs=1;}
	msv->jobs = jobs;
	msv->sfdarr = (int*)calloc(jobs, sizeof(int));	//selfの分 sentinelは止めた
	; if(errno){ return NULL; }
	msv->pidarr = (int*)calloc(jobs, sizeof(int));
	; if(errno){ return NULL; }
	msv->pidarr[0]=getpid();
	msv->sfdarr[0]= -1;	//selfは-1固定

	msv->nlstr = (char*)calloc(nlstrsz+1, sizeof(char) );
	memcpy(msv->nlstr, nlstr, nlstrsz);
	msv->nlstrsz = nlstrsz;
	return msv;
}
static void msv_free_impl(msv_t* msv){
	if(msv==NULL){return;}
	sg_free(msv->instr);
	sg_free(msv->outstr);
//	
	idq_free(msv->jidx);
	idq_free(msv->strq);
	idq_free(msv->strqsz);
//
	free(msv->sfdarr);
	free(msv->pidarr);
	free(msv->nlstr);
//
	*msv=(msv_t){0};	//fill 0
	free(msv);
}

static rdata_t* rdata_new(const char* mode, const char* rstr, int rstrsz, const char* nlstr, int nlstrsz) {
	char* emsg = NULL;
	rdata_t* obj = (rdata_t*)calloc(1, sizeof(rdata_t) );
	lbc_t* lvm = lbc_new();
	obj->lvm=lvm;
// opt setting
// obj->vflg = (strchr(mode, 'v')&&1);
	obj->tmode = 0;
	if(strchr(mode, 't') ){obj->tmode=1;}
	if(strchr(mode, 'T') ){obj->tmode=2;}	//actskipモード。生モード
	obj->regmode = 1;
	if(strchr(mode, 'r') ){obj->regmode=1;}
	if(strchr(mode, 'R') ){obj->regmode=2;}

// mode = "nNrRtT"		//rulesはnNrRの組み合わせのみ必要
// 追加で必要になる
	lbc_reqbcM(lvm, util, ped_ljmod);
	lbc_runstr(lvm, 0, NULL, "return");

//add nlstr conv
	int rc = lbc_pdirectf(lvm,
		" local u = require('util')"
		" local nlstr = " LBC_P2S
		" local emsg = nil"
//" print(nlstr, 123)"
		" GL_NLSTR, emsg = u.clit(nlstr)"	//global, avoid gc
		" return GL_NLSTR, emsg"
	, nlstr, nlstrsz );
	; STOP(rc<0, "fatal err");
	lbc_rt res = lbc_tbval(lvm, 0, 1);
	if(res.tp=='N'){
		//nil...err pegerr nstr is '\n'. parseerr nstr os selectable
		res = lbc_tbval(lvm, 0, 2);
		emsg = strndup(res.s, res.sz+1);
		goto lb_ERR;
	}
	obj->nlstr = res.s;
	obj->nlstrsz = res.sz;
//dbg(obj->nlstr, obj->nlstrsz);
	lbc_directf(lvm,
		" local rstr = " LBC_P2S
		" local mode = " LBC_P2S
		" local m = require('ped_ljmod')"
		" gsave = m.ped_makebase(mode, rstr)"	//avoid gc
		" return gsave"
	, rstr, rstrsz, mode, strlen(mode) );
	res = lbc_tbval(lvm, 0, 1);
	if(res.tp=='N'){
		//nil...err pegerr nstr is '\n'. parseerr nstr os selectable
		res = lbc_tbval(lvm, 0, 2);
		emsg = strndup(res.s, res.sz+1);
		goto lb_ERR;
	}
//dbg(obj->nlstr, obj->nlstrsz);
	obj->rtokens = (void*)(uintptr_t)lbc_tbval(lvm, 0, 1, "rtokens").d;
	obj->rhead = (void*)(uintptr_t)lbc_tbval(lvm, 0, 1, "rhead").d;
	obj->atokens = (void*)(uintptr_t)lbc_tbval(lvm, 0, 1, "atokens").d;
	obj->ahead = (void*)(uintptr_t)lbc_tbval(lvm, 0, 1, "ahead").d;

	//rules data (after opt)
	//set_cmntokens(obj, rtokens, atokens, rhead, ahead); // setたちに先立って生成
//dbg(obj);
	emsg = set_rglists(obj);
	if(emsg){ goto lb_ERR; }	//emsgはmemされてる
	set_clists(obj);
//
	int rpos=0;
	for(;;rpos++){
		int idx = obj->rhead[rpos]-1;	//-1はlua分
		if(obj->rtokens[idx].data[0]=='%'){break;}
	}
	obj->last_rpos = rpos;
	rpos=0;
	for(; obj->rhead[rpos]>=0;rpos++);;
	obj->norule_rpos = rpos;
//	
	return obj;
lb_ERR:;
	rdata_free(obj);
	errno=EINVAL;
	return (rdata_t*)(emsg);	//malloc()ed
}

static void rdata_free_impl(rdata_t* rdata) {
	if(rdata==NULL){ return; }
	//rules data (after opt)
//	reset_cmntokens(obj);
	reset_rglists(rdata);
	reset_clists(rdata);
	lbc_free(rdata->lvm);
	*rdata = (rdata_t){0};
	free(rdata);
}

static char* set_rglists(rdata_t* rdata) {
//idxはluaに合わせる
	char* emsg=NULL;
	rdata->svlocale = strdup( setlocale(LC_CTYPE, NULL) ); //localeはcloseまで永続
	; if(errno){return strdup( strerror(errno));}
//0は無いはず 2は"C" 固定でいく
	if(rdata->regmode==1){
		char* p = setlocale(LC_CTYPE, "");
		if(p==NULL){ emsg="err: setlocale(sysdfl) failed"; goto lb_ERR; }
	}
	else if(rdata->regmode==2){
		char* p = setlocale(LC_CTYPE, "C");
		if(p==NULL){ emsg = "err: setlocale(C) failed"; goto lb_ERR; }
	}
	else{ emsg = "err: reg() mode allows only 1/2 (sys_env/C)"; goto lb_ERR;}
// conv UTF-8 to syscharset
	char ebuf[128]={0};
	char* sysp = nl_langinfo(CODESET);
	iconv_t fd_cv = iconv_open(sysp, "UTF-8");
	if(fd_cv==(iconv_t)-1){
		snprintf(ebuf, 127, "err: conv UTF-8 to %s, system unsupported", sysp);
		emsg = ebuf;
		goto lb_ERR;
	}
//make regex
	int mmsz = 128;
	char* mm = (char*)malloc(mmsz);		//tmpbuff. realloc() placeholder
	dq_t* dq = pdq_new();
	for(int i=0; rdata->rtokens[i].num!= -9; i++) {
		const char* ctg = rdata->rtokens[i].ctg;
		if(strcmp(ctg, "reg")!=0) { pdq_push(dq, NULL); continue;}
		ureg_t* robj=NULL;
		size_t rc= (size_t)-1;
		char* sp = (char*)rdata->rtokens[i].data;
		char* dp = mm;
		size_t inleft = rdata->rtokens[i].num;
		size_t outleft = mmsz-1;	//1byteでerrで対処可能にしとく
		while(rc==(size_t)-1) {
			errno=0;
			iconv(fd_cv, NULL,NULL,NULL,NULL);	//reset?
			rc = iconv(fd_cv, &sp, &inleft, &dp, &outleft);
			if(errno==E2BIG){
				//req mem 状態は変わってるので初期化再挑戦
				mmsz*=2;
				mm = (char*)realloc(mm,mmsz);
				sp = (char*)rdata->rtokens[i].data;
				dp = mm;
				inleft = rdata->rtokens[i].num;
				outleft = mmsz-1;	//1byteでerrで対処可能にしとく
			}
			else if(errno!=0){
				//バイナリ混在なのでそのままコピー 最低1byteは空きがある
				*dp= *sp;
				inleft--;
				outleft--;
				sp++;
				dp++;
			}
		}
		robj = ureg_new_raw(mm, dp-mm);	//m ,sz
		if(robj==NULL){
			int rpos=0;
			for(;;rpos++){if(i< rdata->rhead[rpos]-1){break;} }
			rpos--;
			int ridx = rdata->rhead[rpos]-1;	//-1 lua分
			int tpos = i - ridx;	//一個前 tposは1以上なので+1は不要
			snprintf(ebuf, 127, "regex() failed: rule:%d fld:%d info:%s\n"
				, rpos+1, tpos, rdata->rtokens[ridx+tpos].data);
			emsg=ebuf;	
			break;
		}
		pdq_push(dq, robj);
	}
	pdq_push(dq, NULL);	//-9のはずだが一応ぬるぽを突っこんどく
	rdata->rglists = (ureg_t**)pdq_2arr(dq);	//ポインタ番号の羅列
	iconv_close(fd_cv);
	free(mm);
	pdq_free(dq);
	if(!emsg){ errno=0; return NULL;}
//err	
	free(rdata->rglists);
	rdata->rglists=NULL;
lb_ERR:;
	setlocale(LC_CTYPE, rdata->svlocale);
	free(rdata->svlocale);
	return strdup(emsg);
}

static void reset_rglists(rdata_t* rdata) {
	ureg_t** rglists = rdata->rglists;
	if(rglists==NULL){ return; }
	//rglistはtokensの数に合わせてある。rg以外はNULLが入ってる
	setlocale(LC_CTYPE, rdata->svlocale);
	free(rdata->svlocale);
	; if(!rdata->rglists){return;}
	; if(!rdata->rtokens){return;}
	for(int i=0; rdata->rtokens[i].num!= -9; i++) {
		if(rdata->rglists[i]){	ureg_free(rdata->rglists[i]);}
	}
	free(rdata->rglists);
}

static const char* set_clists(rdata_t* rdata) {
	/*
	ID <- "123"	...1
	ID <- "123"* "xyz"	...1,x
	ID <- "123"+ "xyz"	...1
	ID <- "123"? "xyz"	...1,x
	ID <- !"123" "xyz"	...x xは必須条件 !は全て無視
	ID <- jmp	"xyz"	...jmpのリストをコピー
	ID <- jmp*	"xyz"	...jmp, x
	ID <- ""			...all
	ID <- jmp*	""		...all
		//eofは成功者なしということになる。left>0で絞ることにする
		//逆に文字があったら自動失敗だ。
		//未消費可能なruleは全許可 無限ループはsuc判定で弾く

		//listの戦略は99%のruleは失敗で1%は成功だから成功の頭一文字で
		//フィルターする。完全で無いけど80%ぐらいは削減というもの。
		//hit先頭文字を集める.
	*/
	//idxはrheadでluaになってるので-1でcに合わせる
	//まずはlit,reg系だけ集める >> any eof classが追加されたident以外を集める
	//関門文字を決定したあとでskip系を追加する

	//未消費系のマーキング 最後で使う
	int erpos = 0;	//内部ruleも含めた全ルールのsentinel
	for(; rdata->rhead[erpos]!= -9; erpos++);;
	int* allmk = calloc(sizeof(int), erpos);	//esepはclist無し

	//pass1 下調べ
	dq_t* obj = pdq_new();
	for(int rpos=0; rpos<erpos; rpos++) {
		//bit演算を使えばflg系が少し楽
		char* arr = (char*)calloc(256, sizeof(char));
		int idx = rdata->rhead[rpos] -1;	//lua分 
		int tpos = 1; 	//1スタート 0はheadが入る
		for(;;tpos++){
			rtoken_t tk = rdata->rtokens[idx+tpos];
//dbg(tk.ctg, tk.data, (unsigned char)tk.data[0], tk.num);
//sleep(3);
			// 先頭になれる文字たちを集める
			if(tk.pre[0]=='!') { continue;}
			if(tk.ctg[0]=='l'&&tk.num>0) { arr[(unsigned char)tk.data[0]]=1; }
			//tk.dataはchar*でマイナスになるのでcastが必要 ハマった
			//ureg_t* 
			//rglistはrtokensの羅列idxをNULL付きで一列にrgdataを入れてる
			if(tk.ctg[0]=='r') {
				char* fmap = rdata->rglists[idx+tpos]->fastmap;
				 //gnu-regex fastmap casts char -> unsigned int in innercode
				CBIT_SET(arr, fmap, 256);
			}
			// classとanyはtkdataに0-255までのbit羅列を事前に用意してる
			// litと違って順序情報がいらずdataスペースが空いてるため
			if(tk.ctg[0]=='c'||tk.ctg[0]=='a') { CBIT_SET(arr, tk.data, 256); }
			if(tk.suf[0]=='*'||tk.suf[0]=='?'||(tk.ctg[0]=='l'&&tk.num==0)) {continue;}
			//NEXT可能なのは*?復活系と""!だけ。&はそこで終了
			
			//全部*?系で未消費でオワタ >>sepに突入するので自動break
			if(tk.ctg[0]=='s') {allmk[rpos]=1;}	//未消費可能性を立てておく
			//残骸はsepとかesep,eofもあるけどcharカウントには無関係
			break;
		}
		pdq_push(obj, arr);
	}
	char** clists = (char**)pdq_2arr(obj);
	pdq_free(obj);
	//clistはidentの部分が入ってないので合わせて合成する
	int cnt=0;
	while(1) {
		int lpflg=0;
		for(int rpos=0; rdata->rhead[rpos]!= -9; rpos++) {
			int idx = rdata->rhead[rpos] -1;	//lua分
			int tpos=1;	//0はheadが入ってるのでスキップ
			for(;;tpos++) {
				rtoken_t tk = rdata->rtokens[idx+tpos];
				if(tk.pre[0]=='!') { continue;}
				//追加対象はidentのみ
				if(tk.ctg[0] == 'i') {
					int dst = tk.num -1;	//lua基準, 名前が同じなら拾っていく
					while(1){
						for(int ii=0; ii<256; ii++) {
							//追加変更有り
							if( clists[rpos][ii]==0 && clists[dst][ii]) {
								clists[rpos][ii]=1; lpflg=1;
							}
						}
						dst++;
						//同名からも回収
						int nidx = rdata->rhead[dst]-1;	//lua分
						if(nidx<0){break;} //ruleからidxを取るけどendなら終
						// fix idx>>nidx, typo
						const char* s = rdata->rtokens[nidx].data;
						if( strcmp(tk.data, s)!=0){break;}
						//同名でないidentなら終わり
					}
				}
				//未消費系は追加で拾う			
				if(tk.suf[0]=='*'||tk.suf[0]=='?'||(tk.ctg[0]=='l'&&tk.data[0]=='0')) {continue;}
				break;	//消費系かsepとか
			}
		}
		if(lpflg==0) {break;}
		cnt++;	//dbg用
	}

	// 最後に未消費マークがあれば自動成功 できるだけ弾きたいけど別ロジックが欲しい
	for(int i=0;i<erpos;i++){ if(allmk[i]){ memset(clists[i], 1, 256); } }
	free(allmk);
	rdata->clists=clists;
	return NULL;	//noerr
}
static void reset_clists(rdata_t* rdata) {
	if(rdata==NULL){return;}
	; if(!rdata->clists){return;}
	; if(!rdata->rhead){return;}
	for(int i=0; rdata->rhead[i]!= -9; i++) { free(rdata->clists[i]); }
	free(rdata->clists);
	rdata->clists=NULL;
}

#define RESARR_INITSZ	128		//自動伸長タイプ
static jst_t* jst_new(const char* nlstr, int nlstrsz){
	char* emsg=NULL;
//
	jst_t* res = (jst_t*)calloc(1, sizeof(jst_t) );		//ここでmemerrが起きる
	; if(errno){ emsg=strerror(errno); goto lb_ERR; }
	res->resarr = (resarr_t*)calloc( RESARR_INITSZ, sizeof(resarr_t) );
	; if(errno){ emsg=strerror(errno); goto lb_ERR; }
	res->ipos_mem = (int*)calloc( RESARR_INITSZ, sizeof(int) );
	; if(errno){ emsg=strerror(errno); goto lb_ERR; }

	res->instr = sg_new();
	res->outstr = sg_new();
//
	res->resmaxsz = RESARR_INITSZ;
	res->ipos_e = RESARR_INITSZ;
	res->nlstr = strndup(nlstr, nlstrsz);
	res->nlstrsz = nlstrsz;
	return res;
lb_ERR:;
	return (jst_t*)strdup(emsg);
}

static void jst_free_impl(jst_t* jst) {
	if(jst==NULL){return;}
	free(jst->resarr);
	free(jst->ipos_mem);
	free(jst->nlstr);
	sg_free(jst->instr);
	sg_free(jst->outstr);
	*jst=(jst_t){0};
	free(jst);
}
static void jst_reset(jst_t* jst){
	sg_clear(jst->instr);
	sg_clear(jst->outstr);
	jst->curpos=0;
	// result mem
	jst->bcnt=0;
	jst->nlcnt=0;
	jst->ccnt=0;
	jst->rpos=0;
	jst->tpos=1;
	jst->seof=0;
	jst->resepos = -1;
	jst->ipos_c = -1;
	jst->ipos_mem[0]= 0;

	//ここでres_push()したいけどpedがないからうんこちゃん
	//初回。情報追加しておく。curposとかidentとか重要マーカー情報があちこちで
	//必要なのでvecセット
	res_push(jst, TK_MARK, -9);
	ipos_push(jst, jst->resepos);	//start位置	//eposもiposも初期-1なのでsetで0initになる
}

static edbuf_t* edbuf_new(void){
	edbuf_t* obj = (edbuf_t*)calloc(1, sizeof(edbuf_t) );
	obj->edq = edq_new();
	obj->sg0 = sg_new();
	obj->sg1 = sg_new();
	obj->sg2 = sg_new();
	obj->sgbuf = sg_new();
	return obj;
}
static void edbuf_free(edbuf_t* obj){
	if(obj==NULL){return;}
	edq_free(obj->edq);
	sg_free(obj->sg0);
	sg_free(obj->sg1);
	sg_free(obj->sg2);
	sg_free(obj->sgbuf);
	free(obj);
}



// 先頭ruleは0で入ってくる。 tposも頭は0でくるけど1以上で設定されることがメイン
// rhead, aheadはluaのindexなのでここで調整する
static rtoken_t etoken={ "","",-9,"","" };
static rtoken_t* gettoken(ped_t* ped, int rpos, int tpos) {
	if(rpos<0) {
		jst_t* jst = ped->jst;
		rpos=jst->rpos;
		tpos=jst->tpos;
	}	//初期位置
	int ind = ped->rdata->rhead[rpos] -1;	//cの0とluaのindズレがかみ合ってる。データは-1
	if(ind<0){return &etoken;}	//endは-9でsepは-1になってる -10になる
	return &(ped->rdata->rtokens[ind]) + tpos;
}

//高速化のためdqより直接利用の方が効果的
//呼び出しが1000万回クラスで10倍圧倒的topなのでクリティカル扱い
static void res_push(jst_t* jst, int mode, int n) {
	jst->resepos++;
	if(jst->resmaxsz <= jst->resepos) {
		jst->resmaxsz=jst->resmaxsz*2;
		jst->resarr=(resarr_t*)realloc(jst->resarr, jst->resmaxsz * sizeof(resarr_t));
		; STOP(jst->resarr==NULL, "fatalerr, init realloc() failed. stop");
	}
	//posはjmp/rtn 前の現在所属位置
//	jst->resarr[jst->resepos]= (resarr_t){mode, n, jst->rpos, jst->tpos, jst->curpos};
	resarr_t buf = {mode, n, jst->rpos, jst->tpos, jst->curpos};
	jst->resarr[jst->resepos]= buf;
	//構造体初期化子で作成しないと代入できない。さらに構造体の演算子は=コピーだけ
}

// !&巻き戻し
//resarrの区切りはLIT/RTN/LOOPENDの三つanyもeofもregもLIT. RTNとLOOPENDは遠くまで戻る
//LITとLOOPENDがダイレクトで、suc,failの途中割り込みでRTNもくる
static void pfix_restore(ped_t* ped) {
	//restoreにくるのはsucのみ
	jst_t* jst = ped->jst;
	resarr_t tk = jst->resarr[jst->resepos];
	if(tk.mode == TK_LIT) {
		//lit系。tokenは一つだけの単純戻し。
		jst->curpos = tk.curpos;
		jst->resepos--;
		return;
	}
	if(tk.mode == TK_RTN) {
		jst->resepos = jst->ipos_mem[jst->ipos_c+1];
		//成功rtn直後で+1のmemにはjmp直前の状態が入ってるので利用する
		//スタック的には保証不可だけど触らないようにしてるのでいけるだろう
		//pushする前だから残ってるしそのためのメモだし
		jst->curpos = jst->resarr[jst->resepos].curpos;
		jst->resepos--;
		return;
	}
	//loop系の締切りは面倒. loopとrtnの混合になる
	//ipos_memが使用不能 前のreseposが後続に踏み潰されてるので地道に辿る
	//重いがsuc自体が少なくloop系かつ!&なのでそれほど負担にはならないはず
	if(tk.mode == TK_LOOPEND) {
		//a,b,lp,x,lp,x,lp,x,endで切れるまで辿る. aの完了まで
		jst->resepos--;	//LOOP
		while(1) {
			jst->resepos--;	//LIT or RTN
			int ck = jst->resarr[jst->resepos].mode;
			if(ck==TK_RTN) {
				int cnt=1;
				while(cnt) {
					jst->resepos--;
					ERRact(jst->resepos<0, "fatal err",exit(1));
					int cck = jst->resarr[jst->resepos].mode;
					if(cck==TK_RTN) {cnt++;}
					if(cck==TK_IDENT) {cnt--;}
					//IDENTは残る
				}
			}
			//LIT+共通
			jst->resepos--;	//判定用に一回下げる
			if(jst->resepos==0) {break;}	//topならおしまい
			if(jst->resarr[jst->resepos].mode == TK_LOOP) {continue;}
			break;
		}
		jst->resepos++;		//curを取得して巻き戻し
		jst->curpos = jst->resarr[jst->resepos].curpos;
		jst->resepos--;
		return;
	}
}

static int fix_result(ped_t* ped, int rc) {
	//大変。suf/preの分類.tokenの体裁も整える rtn 0/1/2 loop, suc,fail,eof 色々
	rtoken_t* tk = gettoken(ped, -1, -1);
	jst_t* jst = ped->jst;
	if(rc== -1 && (tk->suf[0]=='*'||tk->suf[0]=='+') ) {
		// 成功でも未消費がありえる 未消費loopは散らばってて
		// 厄介。accとここ。
		// ここではident以外の単体系のみkickする
		//単体系で成功ってことは文字が入ってないといけない
		//loop系で eof*もありえるけど明らかに不正なのでkickでいい
		//単体系はreseposに直前が露出してるので直接観測できる
		if(tk->ctg[0]!='i' && jst->resarr[jst->resepos].curpos == jst->curpos){
			jmp_seterr(ped, "detect 0 byte consume term");
			return -20;
		}
		res_push(ped->jst, TK_LOOP, 0);
		return 0;
	}
//救済。loop系はval/lp,val,lp,val/ 系で?も失敗はval""が欲しいので""追加
	if(rc== -2 && tk->suf[0]=='?') {
		res_push(ped->jst, TK_LIT, 0);
		rc= -1;
	}
	else if (rc== -2 && tk->suf[0]=='*') {
		//resepos== -1もある 初期値で存在しない場合。完全勝利のとき
		//>>初期resepos==0をMARKで追加して不要になった
	//	int modeck = !TK_LOOP;
	//	if(jst->resepos>=0){ modeck =jst->resarr[jst->resepos].mode; }
		int modeck =jst->resarr[jst->resepos].mode;
		if(modeck!=TK_LOOP) {
			//*だが失敗即終了...lit/loop/loope下駄 loopなら二つ前がloopのはず
			res_push(ped->jst, TK_LIT, 0);
			res_push(ped->jst, TK_LOOP, 0);
		}
		res_push(ped->jst, TK_LOOPEND, 0);	//LITでもいいけど!&の巻き戻しで分かりやすくなる
		rc = -1;
	}
	else if (rc== -2 && tk->suf[0]=='+') {
// '+' だけ失敗がある
//		int modeck = !TK_LOOP;	//reseposは-1でidxエラーがある
//		if(jst->resepos>=0){ modeck =jst->resarr[jst->resepos].mode; }
		int modeck = jst->resarr[jst->resepos].mode;
		if(modeck!=TK_LOOP) { rc= -2;}
		else { res_push(ped->jst, TK_LOOPEND, 0); rc= -1; }
	}

// !&ならば共通巻き戻し
	if(strlen(tk->pre)!=0) {
		//res cur修正 &!共通	()系は%でRTNでrposが適切な位置になってるはず
		if(rc==-1) { pfix_restore(ped); }	//失敗は元からres無し。loopはsucしてる
		//一旦全削除
		//反転なら操作
		if(tk->pre[0]=='!' && rc==-1) { rc= -2; }	//成功を踏み潰す 
		else if(tk->pre[0]=='!' && rc== -2) { rc= -1;  }
		//ダミーが必要なら突っこむ restoreで全削除してるため
		if(rc== -1){ res_push(ped->jst, TK_LIT, 0); }
	}
	return rc;
}

// vicの度に飛んでくる ここで消費文字数を更新する
// curposはvicならorderflush後にdelで0になるから単純加算でいい
// 少し無駄だけど毎回全探索をかける。改良の余地有り
static void nlpos_update(ped_t* ped) {
	jst_t* jst = ped->jst;
	int cpos = jst->curpos;
	if(jst->curpos<0){ cpos = -jst->curpos -1; }
//
	int nlcnt=0;
	char* np = sg_ptr(jst->instr) + cpos;
	char* sp = sg_ptr(jst->instr);
	char* spp = sp;
	char nlc = jst->nlstr[0];
	int nlsz = jst->nlstrsz;
	while(sp<np) {
		// nlカウント \0もあるのでstrstrが使えない
		if(*sp==nlc && strncmp(sp, jst->nlstr, nlsz)==0) {
			nlcnt++; sp=sp+nlsz; spp=sp; continue;
		}
		sp++;
	}
	jst->bcnt = cpos;
	jst->nlcnt= nlcnt;
	jst->ccnt = (int)(np - spp);	//累積しない
}

//idx==0はinitかMARKで初回位置を保持する 巻き戻しで必要
//jmpしていればipos_cが1以上になる 0ならjmp無しでvicとかrootとか
//初期代入は
static void ipos_push(jst_t* jst, int idx) {
	jst->ipos_c++;
	jst->ipos_mem[jst->ipos_c] = idx;
	if(jst->ipos_e <= jst->ipos_c) {
		jst->ipos_e = jst->ipos_e*2;
		jst->ipos_mem=(int*)realloc(jst->ipos_mem, jst->ipos_e * sizeof(int));
		; STOP(jst->ipos_mem, "fatalerr, init realloc() failed. stop");
	}
	//構造体初期化子で作成しないと代入できない。さらに構造体の演算子は=コピーだけ
}
static int ipos_pop(jst_t* jst) {
	jst->ipos_c--;
	; STOP(jst->ipos_c<-1, "fatal err");
	return jst->ipos_mem[jst->ipos_c+1];
}

static const char* jmp_seterr(ped_t* ped, const char* msg){
	int svrpos = ped->jst->rpos;
	jst_t* jst = ped->jst;
	const char* bin = sg_ptr(jst->instr)+jst->curpos;
	
	int presz = jst->curpos;
	int pstsz = sg_sz(jst->instr)-jst->curpos;
	if(presz>20){ presz=20; }
	if(pstsz>20){ pstsz=20; }
	
	nlpos_update(ped);
	sg_clear(ped->edbuf->sgbuf);
	sg_addf(ped->edbuf->sgbuf
	, "jmpERR: rule %d %s: l %d,%d(%d byte): %s: %.*s@%.*s"
	, svrpos+1, gettoken(ped, svrpos, 0)->data, ped->msv->nlcnt + jst->nlcnt
	, jst->ccnt, ped->msv->bcnt + jst->bcnt, msg
	, presz, bin-presz, pstsz, bin
	);
	ped->jst->emsg = sg_ptr(ped->edbuf->sgbuf);
	return ped->jst->emsg;
}

static int jmp_suc(ped_t* ped) {
//pfset(NULL,NULL,NULL,NULL);
	jst_t* jst = ped->jst;
	int rc=0;	//基本は進むだけ rcは0,1,10, 11		0はfall-through
	jst->tpos++;	//endはact位置に相当させるのが順当

	//acc / vic判定	drule作成時にendに-1のtokenを付加してる
	if( gettoken(ped, -1, -1)->num<0 ) {
// 戻り先を覗き見. curが進んでないのに+*系だったら永久になるので弾く
// 2団jmpでも最終的に*+系にたどり着くのでそこでkickできる
// jmp系永久はここで弾ける reseposをpresetならpre_curposも始末出来るか
		//jmp直後にipos_push(,...resepos)してるので.curposが取り出せる
		//epos情報はaccもvicも共通で使うので先取り出来る？
		int pos = ipos_pop(ped->jst);
		resarr_t res = jst->resarr[pos];
		if(res.curpos == jst->curpos){
			rtoken_t* rtk = gettoken(ped, res.rpos, res.tpos);
			if(rtk->suf[0]=='*'||rtk->suf[0]=='+'){
				rc= -20;
				jmp_seterr(ped, "detect 0 byte consume");
				goto lb_RTN;
			}
		}
		if(jst->ipos_c>=0) {
			// acc マーカー追加と rpos/tpos戻り
			res_push(ped->jst, TK_RTN, 0);
			//rpos巻き戻し accは成功して戻りで再処理が必要なのでrc=0ではなく-1
			jst->rpos = res.rpos;
			jst->tpos = res.tpos;
			rc= -1;
		} else {
			//vic
			res_push(ped->jst, TK_MARK, -9);
			ipos_push(ped->jst, jst->resepos);
			//iposが-1になるのでcurpos目当てにstart位置入れ直し
			//rposはeditでcurrentのruleを探すのに使うのでrpos=0前に保存
			//iposはcurのみチェックするのでrposがズレてても問題ない
			//一致させておきたいところだけど
			rc=0;
			jst->rpos=0;
			jst->tpos=1;
			//量によって継続かeditか異なる
			if(jst->curpos > ped->msv->bufsz_min ){ rc= -11; }	//報告なら-11, 継続は0
			else if(jst->curpos<0) { rc=-10; jst->seof=2; }	//EOFヒット !. {_0 = "gw"} とか
		}
		goto lb_RTN;
	}
lb_RTN:;
	return rc;
}

static int jmp_fail(ped_t* ped) {
	jst_t* jst = ped->jst;
//resarr_t はmode,num(charcnt), rpos,tpos, curpos(hit先頭位置)の5つを保持してる
	// rc=0,2,20
	//rule頭まで巻き戻し。nextテストでダメならさらにfail
	int rc= 1;
	jst->tpos=0;
	int grp = gettoken(ped, -1, -1)->num;
	jst->resepos = jst->ipos_mem[jst->ipos_c];
	//とりあえず共通巻き戻し popは失敗確定してから
	//jmp, next or fail
	if(jst->ipos_c>0){
		//jmp先で失敗した
		jst->curpos = jst->resarr[jst->resepos].curpos;
		jst->rpos++;
		// next有 小細工してjmp成功を演じる
		if(grp==gettoken(ped, -1, -1)->num ) {
			jst->tpos=1;
			rc=0;
			//jmp先をnextに更新	{mode,n,jst->rpos,jst->tpos,jst->curpos};
			jst->resarr[jst->resepos].num++;	//直接関係ないがluaidx
			goto lb_RTN;
		}
		// next無し失敗 ... ident失敗として差し戻しのfail再判定
		ipos_pop(ped->jst);	//失敗確定
		jst->rpos = jst->resarr[jst->resepos].rpos;
		jst->tpos = jst->resarr[jst->resepos].tpos;
		jst->resepos--;	//rposがidentなのでresultから削除
		rc= -2;
		goto lb_RTN;
	}

	// root nextrule
	jst->rpos++;
	jst->tpos=0;
	// %Rを越えたかruleを全部使い果たした
	// 事前に%RULEでlastpos設定するけど、pegとしては全部のend判定が必要
	// dtokenで渡すときにendが->numはidentだけど<0になるようにしてる
	//通常identはjmp先のrule番号@luaだけど
	if( ped->rdata->last_rpos < jst->rpos || gettoken(ped, -1,-1)->num<0 ) {
		// EOF消費済みの正当勝利。failに入る前に切りたいけどEOF+があるので判別不能
		if(jst->curpos<0) {jst->seof=2; rc= -10; goto lb_RTN;}
		//EOF以外。指示がない場合で全文字消費なら完全成功
		//ぴったりでeofが立ってない場合は確認。endruleなので一歩手前に戻す
		//大抵はlitがいるのでここに落ちる前にseofが立ちそうだけど
		int left = sg_sz(jst->instr) - jst->curpos;
		if(jst->seof==0 && left==0) {
			jst->rpos--;
			jst->tpos=1;
			rc=ped->msv->bufsz_min;
			goto lb_RTN;
		}
		//消費済み勝利 req -1と地道収集でcurpos<0にならない場合が有る 初回とか
		if(jst->seof>=1 && left==0) {jst->seof=2; rc= -10; goto lb_RTN;}
		//それ以外は完全失敗。残りがいるのにruleを使い切った
		rc= -20;
		jmp_seterr(ped, "detect no pegrule byte input");
		goto lb_RTN;
	}
	//endじゃないなら次に挑戦
	jst->tpos=1;
	rc=0;
	goto lb_RTN;
lb_RTN:;
	ERRact(rc== 1, "fatal", exit(1));
	return rc;
}

#define BLKsz	128
static jbm_t ped_jmp(ped_t* ped) {
	// cur初期化とかはworkerの依頼時にこのfuncの外部で処理する
	jbm_t res={0};
	jst_t* jst = ped->jst;
	
	
	//mainloop
	int rc=0;
	while(1) {
//fprintf(stderr, "%d %d %d\n", jst->rpos, jst->tpos, jst->curpos);
		// suc/fail -10/-20, req -1:all >0 sz 
		if(rc>0||rc== -30) {break;}	//文字要求系
		// fix処理
		if(rc!=0) {
			int rrc = fix_result(ped, rc);	// real_rc loopとか生き残り有り
			if(rrc== 0) { rc=0; }	//loopだけかな
			if(rrc== -1) { rc=jmp_suc(ped); }	//s.seof ==2 の判定でvic10あり
			if(rrc== -2) { rc=jmp_fail(ped); }
			if(rc==0||rc== -1||rc== -2) {continue;}	//0(suc),-1(rtn),-2(fail)	途中処理
			//外部系 文字要求>0 全要求-30, -11報告 -10 完全成功 -20 err
			// suc内で0あ-10か-11か判別してる
			break;
		}
		//通常jmp開始
		if(jst->curpos < 0) { goto lb_FAIL; }	//EOF consumed. auto fail all
		int leftsz = sg_sz(jst->instr) - jst->curpos;
		if(jst->seof==0 && leftsz==0) { rc= BLKsz; continue; } //適当に追加
		rtoken_t* tk = gettoken(ped, -1,-1);
		//purne
		if(jst->tpos== 1 && leftsz>0 ) {
			int c = (uint8_t)sg_ptr(jst->instr)[jst->curpos];
			int rc = ped->rdata->clists[jst->rpos][c];	// cfilter
// for(int i=0; i<256;i++){
// 	printf("%d %d\n", i, ped->rdata->clists[jst->rpos][i]);
// }
// sleep(1);
			if(rc==0) { goto lb_FAIL;}
		}
		// core start
		if(tk->ctg[0]=='i') {
			//ident
			int rpos_dst = tk->num -1;	//luaは1startなのでずれる
			res_push(ped->jst, TK_IDENT, rpos_dst);	//jmp先numを突っ込むが実際は使わない
			jst->rpos=rpos_dst;
			jst->tpos=1;
			ipos_push(ped->jst, jst->resepos);	//jmpメモ
			continue; //pre系はaccで戻ってきてからs.resarrで処理する rc=0でいい
		}
		if(tk->ctg[0]=='l') {
			//lit
			int litsz = tk->num;
			if(leftsz<litsz) {
				if(jst->seof!=0) { goto lb_FAIL;}
				rc = litsz-leftsz;	//102とか。>0戻りは追加要求sz
				continue;
			}
//dbg( sg_ptr(jst->instr)+jst->curpos, sg_ptr(jst->instr), tk->data, litsz);
			rc = strncmp( sg_ptr(jst->instr)+jst->curpos, tk->data, litsz);
			if(rc!=0) { goto lb_FAIL; }
			res_push(ped->jst, TK_LIT, litsz);
			jst->curpos += litsz;
			goto lb_SUC;
		}
		if(tk->ctg[0]=='a') {
			//any
			int litsz = 1;
			if(leftsz<1) {
				if(jst->seof!=0) { goto lb_FAIL;}
				rc= 1;	//一文字追加要求
				continue;
			}
			res_push(ped->jst, TK_LIT, litsz);	//anyは1文字のlitとして扱う
			jst->curpos+=litsz;
			goto lb_SUC;
		}
		if(tk->ctg[0]=='c') {
			//class == 1byte, 255リストを準備してる
			int litsz = 1;
			if(leftsz<1) {
				if(jst->seof!=0) { goto lb_FAIL;}
				rc= 1;	//一文字追加要求
				continue;
			}
			char* p = sg_ptr(jst->instr)+jst->curpos;
			int cnum = (uint8_t)p[0];
			rc = tk->data[cnum];
			if(rc==0) {goto lb_FAIL;}
			res_push(ped->jst, TK_LIT, litsz);
			jst->curpos += litsz;
			goto lb_SUC;
		}
		if(tk->ctg[0]=='r') {
			//reg
			if(jst->seof==0) { rc = -30; continue; }		//full要求 regはlongest
			ureg_t* rg = getrg(ped, -1, -1);
			rc = ureg_search_head(rg, sg_ptr(jst->instr)+jst->curpos, leftsz);
			if(rc<0){ goto lb_FAIL; }
//dbg(rg->p, rg->sz);sleep(1);
			int sz = rg->sz;
			res_push(ped->jst, TK_LIT, sz);
			jst->curpos += sz;
			goto lb_SUC;
		}
		if(tk->ctg[0]=='e') {
			; STOP( tk->pre[0]!=0, "fatal err");
			//eofは!を削除してるはず
			// eofは一度ヒットしたら二度と消費しない仕組みが欲しいので-cur
			// paperにも消費とかかれてるからそっちを採用する
			if(leftsz==0&&jst->seof==0) { rc = BLKsz; continue; }
			if(leftsz!=0) { goto lb_FAIL; }
			//eofヒット
			res_push(ped->jst, TK_LIT, 0);	//actもあるのでダミーを突っ込む
			jst->curpos = -jst->curpos -1;
			//EOF curはresに保存済でrollbackで自動的復帰
			//バッファとして使うけど0が-+0なので-1で一つずらす
			goto lb_SUC;
		}
		ERRact(1, "unreachable code", exit(1) );
lb_FAIL:;
		rc= -2;
		continue;
lb_SUC:;
		rc= -1;
		continue;
	}
//外部系	文字要求>0 全要求-30, -11報告 -10 完全成功 -20 err
//	jmb_t res
	if(rc>0){ res.flg=J_REQSZ; res.reqsz = rc;}
	else if(rc== -30){ res.flg=J_REQSZ; res.reqsz = -1; }
	else if(rc== -20){ res.flg=J_EMSG; res.emsg = jst->emsg; }
	else if(rc== -10||rc== -11){
		//基本的に差分のみ msv->bcntがsumでjjst->bcntはadd。足して正確な位置
		//BLKsz以上にcurが動くまで返却しない
		nlpos_update(ped);
		res.flg = J_VICINFO;
		res.bcnt = jst->bcnt;
		res.nlcnt = jst->nlcnt;
		res.ccnt = jst->ccnt;
		// -11,-10 両方とも成功。報告には変わりない vicでnlupdate()してるはず
	}
	else{  STOP(1, "fatal err"); }
	return res;
}

static ureg_t* getrg(ped_t* ped, int rpos, int tpos){
	jst_t* jst = ped->jst;
	if(rpos== -1){ rpos=jst->rpos; }
	if(tpos== -1){ tpos=jst->tpos; }
	int idx = ped->rdata->rhead[rpos]-1+tpos;
	return ped->rdata->rglists[idx];
}

//basestrはjst->instrをそのままつかう
//sg0に移動してもいいけど速度的にそのままの方が有利
static jbm_t ped_edit(ped_t* ped) {
	jbm_t rtn={0};
//bench_direct_rtn	どうせ-O2コード最適化で消える
if(0){
rtn.flg = J_SENDSTR;
rtn.bin = "";
rtn.binsz = 0;
return rtn;
}
	
	jst_t* jst = ped->jst;
	dq_t* edq = ped->edbuf->edq;
	edq_reset(edq);
	edq_push(edq, (ed_t){INFO_SEP,0,0,SRC_PTR,0} );
//pass1
	// lit系loopは結合。ident系loopは情報を残す.
	// numがflgを兼ねる。 <0 lit, -1/2 fld -3/4 jprtn -9 end
	// fld rule jmp などの構造情報を矛盾無く詰め込む.fldはident, litを挟む
	// reseposはデータ最後尾で有効なデータ。next位置ではない。
//	int tflg=1;	//topflg. 先頭とMARKの直後はJOが入ってないので補完する
	int lpflg=0;	//litloop // 1:lit 2:jmp -1:litloop -2:jmploop
	int cur=0;
	int lnum=0;
	int topflg=1;
	//resarrから編集型に整理する 速く分散したいのでjmpでは最低限の操作のみ
	//先頭にはMARKが入ってるのでスキップ
	for(int i=1;i<=jst->resepos;i++) {
		resarr_t res = jst->resarr[i];
		if(topflg){
			edq_push(edq,(ed_t){ INFO_JO,res.rpos+1,res.tpos,SRC_PTR,0} );
			//topにはidentのaE判定が効かないので分散して嫌だけど追加
			//editはTreeでhitしないことがあるのでここでやる
			//jmpは高速化したいから向こうでは判定を減らしたい sucだからkickも
			//ありだけどactはこっちでまとめて受け持ちたい
			jst->emsg = edit_rstop( ped, res.rpos+1, sg_ptr(jst->instr), sg_sz(jst->instr), cur);
			if(jst->emsg){ break; }
			topflg=0;
		}
//ed_t{flg, rnum, tnum, srcptr_base, cur, sz}
		//始末で困るのはloop処理 litとidentの二つで分かれる
		//lit系loop. num=0が入ってる
		if(lpflg==1&&res.mode!=TK_LOOPEND) { lnum+=res.num; continue; }
		if(res.mode == TK_LOOPEND){
			// looptokenはlitかidentのrtnの直後に配置. fldEを付加する
			if(lpflg==1) {
				//lit系終了結合処理. lpendのrposは対象と同じなのでそのまま使える
				edq_push(edq, (ed_t){lnum, res.rpos+1,res.tpos,SRC_PTR,cur} );
//printf("%d %.*s\n", lnum, lnum, jst->instr->p+cur);
//printf("lp@%d %.*s\n", lnum, lnum, jst->instr->p+cur);sleep(1);
				cur+=lnum;
				lnum=0;
			}
			lpflg=0;
			// jmp系はlp==2だけどFEのみなので共通化
			edq_push(edq, (ed_t){INFO_FE, res.rpos+1,res.tpos,SRC_PTR,0} );
			continue;
		}
		if(res.mode==TK_RTN) {
			//rule_end .. 一個前のデータでfeマークを追加。
			edq_push(edq, (ed_t){INFO_JE, res.rpos+1,res.tpos-1, SRC_PTR,0} );
			// 覗き見 loop以外なら閉める tpos-1はnextに進んでるのを戻してる
			res = jst->resarr[i+1];
			if(res.mode==TK_LOOP) {lpflg=2;}
			else{ edq_push(edq,(ed_t){INFO_FE,res.rpos+1,res.tpos-1,SRC_PTR,0} ); }
			continue;
		}
		// 終了マークだけど最初でJOをつけてるので尻尾にもJEをつけて閉じる
		if(res.mode == TK_MARK) {
			edq_push(edq, (ed_t){INFO_JE, res.rpos+1,res.tpos,SRC_PTR,0} );
			edq_push(edq, (ed_t){INFO_SEP, res.rpos+1,res.tpos,SRC_PTR,0} );
			topflg=1;
			continue;
		}
		if(res.mode == TK_LIT) {
			edq_push(edq, (ed_t){INFO_FO, res.rpos+1,res.tpos,SRC_PTR,0} );
			//単体litは閉じて次
			if(jst->resarr[i+1].mode != TK_LOOP) {
//if(res.num)printf("cur:%d %d %.*s\n", cur, res.num, res.num, jst->instr->p+cur);
				edq_push(edq, (ed_t){res.num, res.rpos+1,res.tpos,SRC_PTR,cur} );
				edq_push(edq, (ed_t){INFO_FE, res.rpos+1,res.tpos,SRC_PTR,0} );
//printf("@%d %.*s\n", res.num, res.num, jst->instr->p+cur);sleep(1);
				cur += res.num;
				continue;
			}
			//loop系は結合するのでflg準備. fld綴じはloopendでやる
			lnum = res.num;	//初期化
			lpflg=1;
			continue;
		}
		if(res.mode==TK_IDENT) {
			//loopは一塊にしたいのでFOは単体のみ loopはlpflg=2でjmpするようにしてる
			if(lpflg==0) {edq_push(edq,(ed_t){INFO_FO,res.rpos+1,res.tpos,SRC_PTR,0} );}
			lpflg=0;
			res = jst->resarr[i+1];	//resarr情報から所属位置をチラ見
			edq_push(edq,(ed_t){ INFO_JO,res.rpos+1,res.tpos,SRC_PTR,0});
			//aEを先行あぶり出しで高速化したい errでemsgが入ってる
			jst->emsg = edit_rstop( ped, res.rpos+1, sg_ptr(jst->instr), sg_sz(jst->instr), cur);
			if(jst->emsg){ break; }
			continue;
		}
	}

	// 強制stop err exitはせず指令を待つ
	if(jst->emsg){
		rtn.flg = J_EMSG;
		rtn.emsg = jst->emsg;
		return rtn;
	}
	ed_t* earr = edq_2arr(edq);	//高速化 固定arrに変換
	int earrsz = edq_len(edq);
	// pass1終了 CSTは全てearrが持っている

	// pass2 CSTを元にした加工系
	edbuf_t* edbuf = ped->edbuf;
	sg_clear(edbuf->sg1);
	if(ped->rdata->tmode!=2){ edit_act(ped, earr, earrsz); }
	// result sg1が結果バッファ固定
	if(ped->rdata->tmode==0){ edit_write_str(ped, earr, earrsz);}
	else{ edit_write_tree(ped, earr, earrsz); }
//
	rtn.flg = J_SENDSTR;
	rtn.bin = sg_ptr(edbuf->sg1);
	rtn.binsz = sg_sz(edbuf->sg1);
	free(earr);
	return rtn;
	//noreach: warning cut
	edit_displit(ped, earr, 0);
}

static char* edit_rstop(ped_t* ped, int rnum, char* bin, int binsz, int cur){
	atoken_t* atk = edit_getatk(ped, rnum);
	if( atk==NULL || atk[0].ctg[1] != 'E' ){ return NULL; }
	//err処理。全部組み立ててemsgだけ返却
	//jmp直前のcurからbinを取得する
	//msv+jstのbcntでfullになる jst->bcntが差分に等しい
	int bcnt = ped->msv->bcnt +cur;
	int nlcnt = ped->msv->nlcnt;
	int ccnt = ped->msv->ccnt;
	{
		jst_t* jst = ped->jst;
		char* sp = bin;
		char nlc = jst->nlstr[0];	//高速化のため1byteで先行テスト
		int nlsz = jst->nlstrsz;
		while(sp<bin+cur) {
			// nlカウント \0もあるのでstrstrが使えない
			if(*sp==nlc && strncmp(sp, jst->nlstr, nlsz)==0) {
				nlcnt++;
				sp=sp+nlsz;
				ccnt=1;
				continue;
			}
			ccnt++;
			sp++;
		}
	}
	// bcnt,nlcnt,ccnt 完了 termの直前までのbyteやnl数になる
	// 表示用に+20程度を切り出す
	int presz = cur;
	int pstsz = binsz-cur;
	if(presz>20){ presz=20; }
	if(pstsz>20){ pstsz=20; }
	sg_t* sg = ped->edbuf->sgbuf;
	sg_clear(sg);
	sg_addf(sg, "ERR: hit '_E': %s: l %d,%d (%d byte)\ninput: %.*s@%.*s"
		, atk->rname,nlcnt+1, ccnt+1, bcnt+1, presz, bin+cur-presz, pstsz, bin+cur );
	return sg_ptr(sg);
}

static atoken_t* edit_getatk(ped_t* ped, int rnum){
	rdata_t* rdata = ped->rdata;
	int aidx = rdata->ahead[rnum-1]-1;	//luaで1startなので下駄
	if(aidx<0){return NULL;}
	return &(rdata->atokens[aidx]);
}

// act系はこれを通す 先頭から順次JEを探してactがあれば都度書き換える
// 手前から変換されるので後半のJEactに矛盾がなくなる
static int edit_act(ped_t* ped, ed_t* earr, int sz){
	for(int i=0; i<sz; i++){
		if(earr[i].flg!=INFO_JE){continue;}
		edit_doedit(ped, earr, i);	//editがなければ自動で無視
	}
	return 0;
}

// edit本体: actがあったら入替 空きはIGで埋める sdx/edxはJO JEを除いたfldたち
// litnumが加工後はsg0からsg1に鞍替えさせるので<0で判別する
// %系消さない 必要ならユーザー側で処理
static int edit_doedit(ped_t* ped, ed_t* earr, int ejmp){
	edbuf_t* edbuf = ped->edbuf;
	int edx = ejmp-1;	// JEで飛んでくるのでFEに修正
	int rnum = earr[edx].rnum;
	atoken_t* atk = edit_getatk(ped, rnum);
//dbg(atk, rnum);
	if(atk==NULL){return 0;}		//actがなければskip
//dbg(atk->ctg, atk->data, atk->num, atk->rnum, atk->rname);sleep(1);

	//尻尾からsfld==sdxを探す 内部jmp/fldは残す editで変更時に破壊 それ以外は生き残る
	int sdx=edx;
	int nest=1;
	for(;;sdx--){
		ed_t obj = earr[sdx];
		if(obj.flg==INFO_JE){nest++;}
		if(obj.flg==INFO_JO){nest--;}
		if(nest==0){break; }
	}
	sdx++;	//JOなのでFOに修正 sdxからedxまでが編集対象範囲で基礎値になる

	//sdx - edx がJO-JEの-部分のfld範囲になる
	int ftgt= -1;
	ed_t ig = {INFO_IG, 0, 0, SRC_PTR, (intptr_t)""};	//無視系マーカー
	for(;;atk++) {
	//ctg,data,rname(str) num,rpos(num)	 lit aC aE sep esep
		if(atk->ctg[1]=='s') { break; }	//esep
		; STOP(ftgt== -1&&atk->ctg[1]!='C', "bad act seq, 1st must be aC");
		//aEは前にkickされてるはず
		if(atk->ctg[1]=='i') { sg_add(edbuf->sgbuf, atk->data, atk->num); }
		if(atk->ctg[1]=='C') {
			//aC, init = -1
//dbg("cc", rnum, sdx, edx, earr[edx].flg, rro->sgbuf->p);
			if(ftgt<0) { ftgt=atk->num; sg_clear(edbuf->sgbuf); continue; }
			//_0は全合成
			if(atk->num==0) { edit_gazlit(ped, earr, sdx, edx, edbuf->sgbuf); }
			else {
				i2_t iobj = edit_gfldidx(earr, sdx, atk->num);
				edit_gazlit(ped, earr, iobj.i1, iobj.i2, edbuf->sgbuf);
			}
//dbg("ccc", sdx, edx, earr[edx].flg, rro->sgbuf->p);
		}
		if(atk->ctg[1]=='e') {
//dbg("e", rro->sgbuf->p);
			//sep	書き換え dataはsg1に直接セット 書き換えが多いとsg1は肥大化する
//for(int i=sdx;i<=edx;i++){edit_displit(earr, i); }
//dbg(sdx, rnum);
			if(ftgt==0) {
				//_0ならlitを一旦完全無効化
//edit_displit(earr, idx);
				ftgt=1;
				for(int i=sdx;i<=edx;i++) {
					ed_t obj = earr[i];
					if(obj.flg<0){continue;}
					obj.flg=INFO_IG;	//lit系は一旦IGして1だけ変える
					earr[i]=obj;	//lit系はszを0で悪影響なし
				}
			}
			ed_t ed= earr[sdx];	//rnum, tnumを残してるFOを拝借
			ed.src = SRC_1;
			ed.pos = sg_sz(edbuf->sg1);
			ed.flg = sg_sz(edbuf->sgbuf);
			ed.tnum = ftgt;
			sg_add(edbuf->sg1, sg_ptr(edbuf->sgbuf), ed.flg);
			//arrをダミーで埋めたり調整する
			i2_t iobj = edit_gfldidx(earr, sdx, ftgt);
			int sx = iobj.i1;
			int ex = iobj.i2;
			//sdx - edxがFO-FEの範囲になる IGで全埋め
			for(int i=sx+1;i<=ex-1;i++) { earr[i]=ig; }
			earr[sx+1] = ed;	//結果は先頭へ突っこむ sdxはFO位置
//for(int i=sdx;i<=edx;i++){edit_displit(earr, i); }
			ftgt = -1;
		}
		// lit aC aE next end
		if(atk->ctg[1]=='E') { STOP(1, "unreachable blk"); }
	}
	sg_clear(edbuf->sgbuf);
// nest1以外の内部fld/jmp系をポアする nest1は注目情報があるので残す
	for(int i=1;; i++) {
		i2_t iobj = edit_gfldidx(earr, sdx, i);
		int sx=iobj.i1+1;
		int ex=iobj.i2-1;
		for(int k=sx;k<=ex;k++){ if(earr[k].flg<0){earr[k]=ig;} }
		if(iobj.i2==edx){break;}
	}
	return 0;
}

//fldのOPからCLまでのdqのidxを取得 evenカウント
static i2_t edit_gfldidx(ed_t* earr, int sdx, int fld){
	int cnt=0;
	int nest=0;
	int edx= -1;
	for(int i=sdx;;i++){
		ed_t obj = earr[i];
		if(obj.flg==INFO_FO ){
			nest++;
			if(nest==1){
				cnt++;
				if(cnt==fld){sdx=i;edx=i; }
			}
		}
		else if(obj.flg==INFO_FE ){
			nest--;
			if(nest==0&&edx>=0){edx=i;break;}
		}
	}
//dbg(sdx,edx, earr[edx].flg);
//for(int i=sdx;i<=edx;i++){edit_displit(earr,i);}
	return (i2_t){sdx, edx};
}

//sdx,edxはFO/FE位置
static int edit_gazlit(ped_t* ped, ed_t* earr, int s, int e, sg_t* sg){
	for(int i=s; i<=e; i++) {
		ed_t obj = earr[i];	//直接取得 IGは<0でskip出来る
		if(obj.flg<0){continue;}
//litsrcがinstrだったりsg0だったりsg1だったりする ptrが動くので検査
		char* p = edit_srcptr(ped, obj);
		sg_add(sg, p, obj.flg);	//大抵sgbufに突っこまれるだろう
	}
//dbg(sg->p, obj.sz);
	return 0;
}

//* for dbg
static int edit_displit(ped_t* ped, ed_t* earr, int idx){
	edbuf_t* rro = ped->edbuf;
	ed_t obj = earr[idx];
	if(obj.flg<0 ){ return 0; };
	char* p = (char*)obj.src;
	int sz = obj.flg;
	if(obj.src==SRC_PTR){ p = (char*)obj.pos; }
	else if(obj.src==SRC_1){ p = sg_ptr(rro->sg1)+obj.pos; }
	else if(obj.src==SRC_2){ p = sg_ptr(rro->sg2)+obj.pos; }
printf("%.*s@\n", sz, p);
	return 0;
}

static char* edit_write_str(ped_t* ped, ed_t* earr, int earrsz) {
	edbuf_t* edbuf = ped->edbuf;
	sg_clear(edbuf->sgbuf);
	for(int i=0; i<earrsz; i++){
		ed_t ed = earr[i];
		if(ed.flg<=0){continue;}
		; STOP(ed.src==SRC_2, "fatal err, SRC_2, sg2 must be killed");
		char* p = edit_srcptr(ped, ed);
		sg_add(edbuf->sgbuf, p, ed.flg);
	}
	sg_clear(edbuf->sg1);
	sg_add(edbuf->sg1, sg_ptr(edbuf->sgbuf), sg_sz(edbuf->sgbuf) );
	return 0;
}

static char* edit_srcptr(ped_t* ped, ed_t ed){
	char* p = sg_ptr(ped->jst->instr) + ed.pos;
	if(ed.src==SRC_1){ p = sg_ptr(ped->edbuf->sg1)+ed.pos; }
	else if(ed.src==SRC_2){ p = sg_ptr(ped->edbuf->sg2)+ed.pos; }
	return p;
}


static char* my_itoa8(int num);
static char* edit_write_tree(ped_t* ped, ed_t* earr, int earrsz) {
// こんなの
// # 1 RULE 1 R1
// # 1 FIELD 1
// \142
// # 1 E_FIELD 1
// # 1 E_RULE 1 R1

//全treeは前段階actをスキップで対処する
	edbuf_t* edbuf = ped->edbuf;
	int litcnt= 0;
	int nest=0;
	sg_clear(edbuf->sgbuf);
	for(int i=0; i<earrsz; i++){
		ed_t ed = earr[i];
		if(ed.flg== INFO_SEP&&i){ sg_add(edbuf->sgbuf, "\n"); continue;}
		else if(ed.flg== INFO_FO){
			sg_addf(edbuf->sgbuf, "# %d OP FIELD %d %d\n", nest, ed.rnum, ed.tnum);
		}
		else if(ed.flg>=0){
//			if(litcnt==0){litcnt=1;}
			; STOP(ed.src==SRC_2, "fatal err, detect SRC_2 set");
			char* p = edit_srcptr(ped, ed);
			int sz = ed.flg;
//*
			for(int i=0; i<sz; i++){
				if(litcnt>=19){ litcnt=0; sg_add(edbuf->sgbuf, "\n"); }	//76文字で改行
				char* istr = my_itoa8((uint8_t)p[i] );
				if(strlen(istr)==4){istr++;}
				istr--;
				istr[0]='\\';
				sg_add(edbuf->sgbuf, istr);
				litcnt++;
			}
//*/
		}
		else if(ed.flg== INFO_FE){
			if(litcnt){sg_add(edbuf->sgbuf, "\n");}
			sg_addf(edbuf->sgbuf,"# %d C FIELD %d %d\n",nest,ed.rnum,ed.tnum);
			litcnt= 0;
		}
		else if(ed.flg== INFO_JO){
			nest++;
			int rnum = ed.rnum;
			const char* rname = gettoken(ped, rnum-1,0)->data;
			sg_addf(edbuf->sgbuf, "# %d OP RULE %d 0 %s\n", nest, rnum, rname);
		}
		else if(ed.flg== INFO_JE){
			int rnum = ed.rnum;
			const char* rname = gettoken(ped, rnum-1,0)->data;
			sg_addf(edbuf->sgbuf, "# %d C RULE %d 0 %s\n", nest, rnum, rname);
			nest--;
		}
	}
	sg_clear(edbuf->sg1);
	sg_add(edbuf->sg1, sg_ptr(edbuf->sgbuf), sg_sz(edbuf->sgbuf) );
	return 0;
}

// real	57.661 ms	: ./a.c 63: main_sub(): msg:my_atoi8
// real	158.942 ms	: ./a.c 39: main_sub(): msg:printf("%o")
// >>> ifから'0'+num で高速化
//real	89.577 ms	: ./a.ts.c 12: t_main_sub(): msg:-
//real	131.133 ms	: ./a.ts.c 16: t_main_sub(): msg:-
//real	186.860 ms	: ./a.ts.c 20: t_main_sub(): msg:-

static char my_itoasbuf[128]={0};
static char* my_itoa8(int num){
	//尻尾から詰めていく
	int pos=128-1;
	if(num>0) {
		while(num){
			pos--;
			int v = num%010;
			my_itoasbuf[pos]='0'+v;
			num /=010;
		}
		pos--;
		my_itoasbuf[pos]='0';
	}
	else if(num<0){
		num *= -1;
		while(num){
			pos--;
			int v = num%010;
			my_itoasbuf[pos]='0'+v;
			num /=010;
		}
		pos--;
		my_itoasbuf[pos]='0';
		pos--;
		my_itoasbuf[pos]='-';
	}
	else { pos--; my_itoasbuf[pos]='0'; }
	return my_itoasbuf+pos;
}


/*
//         sv					    sv	                    sv					 
// input-- +-+                in -- +-+               in -- +-+              
//         |=| -> cli1(jmp)         |=| <- cli1(vic)        | | -- cli1(edit) 
//         | |                      | |                     | |              
//         | | -- cli2(-)           | | -- cli2(-)          |=| -> cli2(jmp)   
//         | |                      | |                     | |              
//         | | -- cli3(-)           | | -- cli3(-)          | | -- cli3(-)   
//         +-+                      +-+                     +-+              
// job1 ならioが邪魔なので特別フラグで全て自前処理する
// job2以降は振り分ける 全員手一杯ならsvが自ら処理する
// 専業の方が分かりやすいけどリニア性能なら現場に出てほしい
// job1の振り分けも不要になる
// svの自前処理が済んだら先行一つをbuffして先にinstrを空いたworkerに
// 渡す その後残りのworkerデータを集めて合体。送れるなら送信
// 客は受けたら無条件で再実行してもらう
// その後待機モードにはいって初期状態になる。socket待ち
// - 中間報告 bcnt,nlcnt,ccnt
// - 文字列追加要求 100byteよこせ
// - 成功報告 20byte ...
// - 失敗報告 emsg
// をsocketで高速にやりとりできる形式. 

先頭4intは固定で1stがフラグ残り3つがペイロード. サイズは32固定
1 reqstr, -2,0,0,sz(,bin)	もっと文字をよこせ。cpidからの要求
2.vicrep -1, 12,2,3(,nobin)	
10.normal str, 1, 0,0,sz,bin	1系は単純なbin送受信
20. emsg , 1,0,0,sz,bin	2系は同じだけどemsg
ほか1-9, 10,20 がフラグ分類 J_IOLOST とかCLOSE指令とか
*/

//order可能な奴 socketはバッファ持ちで常にwrite可能なのでpidarrで判別する
//先にresultを回収しないと成果物が破壊される>>pidarr<0でskip
static int jidx_scwritable(ped_t* ped){
	msv_t* msv = ped->msv;
	int rc=0;
	int fdmax= -1;
	// writable
	fd_set fds;
	FD_ZERO(&fds);
	for(int i=1;i<msv->jobs;i++) {
		int sfd = msv->sfdarr[i];
		int pid = msv->pidarr[i];
		if(pid<0){ continue; }	// selfとwork中はskip
		if(sfd > fdmax) { fdmax = sfd; }
		if(sfd>=0){ FD_SET(sfd, &fds);}
	}
	//scはない selfはpidarrが>0だけ許される
	if(fdmax== -1){ return -1;}

	struct timeval tv = {0,0};	//non timewait. 1,  1s + 1us
	struct timeval* tvp = &tv;
	rc = select(fdmax+1, NULL, &fds, NULL, tvp);	// r/w/sigだった気がする
	; STOP(rc == -1, "select err" );
	//writeはreadと違ってsigの受信の心配がないのでEINTR判定は不要か
	
	if(rc==0) {return -1;}
// detect rdy. fdではなくrro->sfdarrのidxを返す。生よりarrとか都合がいい

	int jidx=1;
	for(; !(FD_ISSET(msv->sfdarr[jidx], &fds)); jidx++);;
	return jidx;
}

// jidxを返す
// readableがENTRで失敗でもここに戻るロジックになってる gazかblockの判定だけで使われてる
static int jidx_screadable(ped_t* ped, int flg) {
	msv_t* msv = ped->msv;
	int rc=0;
	int fdmax= -1;
	fd_set fds;
// research
	FD_ZERO(&fds);
	for(int i=1;i<msv->jobs;i++) {
		int pid = msv->pidarr[i];
		int sfd = msv->sfdarr[i];
		if(pid>0){ continue; }	//infoからgazまでpidはマイナス
		if(sfd > fdmax) { fdmax = sfd; }
		if(sfd>=0){ FD_SET(sfd, &fds);}
	}
	if(fdmax== -1){ return -1;}	//job1のみ
	struct timeval tv = {0,0};	//non timewait. 1,  1s + 1us 0でnonblock, NULLでblock
	struct timeval *tvp = &tv;
	if(flg<0){tvp=NULL;}	//NULLでblock待ち
	while(1){
		errno=0;
		rc = select(fdmax+1, &fds, NULL, NULL, tvp);	// r/w/sigだった気がする
		if(rc<0&&errno==EINTR){continue;}	//sleep(10); dbg();}
		//原因不明だがたまにosからシグナルが飛んできてこわされる リトライ
		//errの場合はfdsは変更されないことがposixで保証されている
		break;
	}
	if(rc<=0) {return -1;}	//rdyなし
	int jidx=1;
	for(; !(FD_ISSET(ped->msv->sfdarr[jidx], &fds)); jidx++);;
	return jidx;	//rdyあり
}

//selfはped_jmpとeditが個別に必要だろうか
#include <arpa/inet.h>
static int jbm_scwrite(ped_t* ped, int fd, jbm_t* data) {
	int rc=0;
	if(fd<0){
		//self送信は3つだけ 初期化とresetとstr
		jst_t* jst = ped->jst;
		if(data->flg==J_VICINFO){
			jst_reset(jst);
			jst->bcnt = data->bcnt;
			jst->nlcnt= data->nlcnt;
			jst->ccnt = data->ccnt;
		}
		else if(data->flg==J_SENDSTR){
			char* bin = data->bin;
			int binsz = data->binsz;
			sg_add(jst->instr, bin, binsz);
			jst->rtn = ped_jmp(ped);	//selfのstr_writeはjmpスタートフラグ
			//editはreadで適切な情報がきたら動かせば良い
			//lockはped_parseの上関数で追加削除してる
		}
		else if(data->flg==J_SENDSTR_E){
			if(jst->seof==0) { jst->seof = 1; }	//-1 打ち止め
			jst->rtn = ped_jmp(ped);	//selfのstr_writeはjmpスタートフラグ
		}
		else if(data->flg==J_RESET){ jst_reset(jst); }
		else if(data->flg==J_CLOSE){ ;}	//ig
		else {
			dbg(data->flg);
			;STOP(1, "fatal"); }	//J_CLOSE, J_IOLOST はselfには送ることがないはず
		goto lb_RTN;
	}

//通常送信
	char* bin=NULL;
	int binsz=0;
	int hcnt=4;		// 先頭4intは固定
	uint32_t harr[4]={0};
	harr[0]=(uint32_t)data->flg;	//flgは共通でセット可能
	//ネットワーク越しに-1の値を直接送受信は不可能 8bitのバイトシーケンスのみ
	//例外的にhtonl()系を使ってuint32_tを送れる
	//負数のビット表現は環境依存なので-1はb1001かもしれないしb0001かもしれない
	//aが0x0a 0x01を送って bが0x0a 0x01を受信して1001をb環境でintに直すと
	// -200になってるかもしれない forkで同一環境なのでたまたま一致してるだけ

	//追加分があるならセット IOとかCLOSEはflgだけ
	if(data->flg==J_REQSZ){ harr[3] = data->reqsz; }
	else if(data->flg==J_VICINFO){
		harr[1]= data->bcnt;
		harr[2]= data->nlcnt;
		harr[3]= data->ccnt;
	}
	else if(data->flg==J_SENDSTR){ harr[3]= data->binsz;}
	else if(data->flg==J_EMSG){ harr[3]= strlen(data->bin)+1; }
	//SENDとEMSGはbin持ち
	if(data->flg>=10){
		bin = data->bin;
		binsz = harr[3];
	}
	for(int i=0;i<hcnt;i++){ harr[i]=htonl(harr[i]); }
	
	//header_send
	int cnt=sizeof(harr);
	char* p = (char*)harr;
	while(cnt){
		rc = send(fd, p, cnt, 0);	//ラストは帯域外データ(sig系)とかのオプション
		; STOP(rc<0, "fatal: send header failed" );
		cnt -= rc;
		p += rc;
	}
	//バイナリが有れば送るしなければスキップ
	while(binsz>0){
		rc = send(fd, bin, binsz, 0);
		; STOP(rc<0, "fatal: send sz failed" );
		binsz -= rc;
		bin += rc;
	}
lb_RTN:;
	return 0;
}

static jbm_t jbm_scread(ped_t* ped, int fd, sg_t* sgobj) {
	//事前に検査されて結果が詰まってるものだけくるはず
	jbm_t res={0};
	//self
	//selfでreadするのは回収時のみ その時にeditかreqか判別する
	//info中かinfo後かでjmpかeditか
	if(fd<0){ res = ped->jst->rtn;}
	else{
		//worker
		// 先頭32bitx4は固定
		int hcnt = 4;
		uint32_t harr[4]={0};
		char* p= (char*)harr;
		for(int cnt = sizeof(harr);cnt;) {
			errno=0;
			int rc=recv(fd, p, cnt, 0);
			if(rc==0){exit(0);}	//eof
			else if(rc<0 && errno==EINTR){ continue;}	//sigは無視
			else if(rc<0){
				ERRact(1, "detect i/o err. send free/EOF plz", dbg(fd,p,cnt); exit(1) );
			}
			cnt -= rc;
			p += rc;
		}
		for(int i=0;i<hcnt;i++){ harr[i]=ntohl(harr[i]); }
		res.flg = (int32_t)harr[0];
		// header1byteで残り3byteの意味が異なる
		if(res.flg==J_VICINFO){
			res.bcnt=(int32_t)harr[1];
			res.nlcnt=(int32_t)harr[2];
			res.ccnt=(int32_t)harr[3];
		}
		else if(res.flg==J_REQSZ){ res.reqsz=(int32_t)harr[3]; }
		//bin系のデータを持つかも知れない
		else if(res.flg==J_SENDSTR || res.flg==J_EMSG){
			int binsz = (int32_t)harr[3];	//header情報は[4]でbinszは[3]に入ってる
			sg_clear(sgobj);
			int BUFsz = 128;
			char sbuf[BUFsz+1];
			for(int left=binsz;left>0;){
				int sz = BUFsz;
				if(left<BUFsz){ sz=left; }
				int rc = recv(fd, sbuf, sz, 0);
				; ERRact(rc<=0, "fatal: read failed", dbg(fd, sz); exit(1) );
				sg_add(sgobj, sbuf, rc);
				left -= rc;
			}
			//ptrのセットはsgが動くので追加後に登録
			if(res.flg==J_SENDSTR){ res.bin=sg_ptr(sgobj); res.binsz=binsz; }
			else{ res.emsg = sg_ptr(sgobj); }
		}
		// IOとかCLOSEとかSEND_Eはflgのみで判別可能
	}
	return res;
}

// 送信前調整 在庫を適切なbinszにいじる 1は128とか 残りが少なければ53とか eofなら-1
static int calc_sendsz(ped_t* ped, int reqsz){
	int binsz = reqsz;
	msv_t* msv = ped->msv;
	int leftsz = sg_sz(msv->instr) - msv->instr_cur;
//	
	if(msv->seof==1 && leftsz==0){binsz = -1;}	//在庫無しなら-1でeof
	else if(reqsz<0){binsz = leftsz;}	//在庫ありなら全送信
	else if(reqsz > msv->bufsz_min) { binsz = reqsz; }	//>minは確定
	else if(msv->bufsz_min > leftsz) { binsz = leftsz; }	//足りてるけどminほどじゃない
	else if( msv->bufsz_min > reqsz){ binsz = msv->bufsz_min; } //minいける
	return binsz;
}

//svが統率受け取り gazのみでつかわれる
static ped_rt ped_rtnjbm(ped_t* ped, jbm_t* jbm, int jidx) {
	ped_rt res = {0};
	msv_t* msv = ped->msv;
	jst_t* jst = ped->jst;
//
	if(jbm->flg==J_REQSZ){
		int sz=jbm->reqsz;
		int leftsz = sg_sz(msv->instr) - msv->instr_cur;
		//要求系: 全要求
		if(sz<0 && msv->seof == 0){
			res.rc = -1;
			msv->seof = -1;		//seof == -1はeof要求まで
			msv->flg_reqstr = 1;
			msv->jidx_reqstr = jidx;
			msv->sz_reqstr = sz;
			goto lb_RTN;
		}
		//指定要求
		if(sz>leftsz && msv->seof==0) {
			//少なすぎる要求は水増し
			if(sz < msv->bufsz_min){ sz = msv->bufsz_min; }
			msv->flg_reqstr = 1;
			msv->jidx_reqstr = jidx;
			msv->sz_reqstr = sz;
			res.rc = sz;	//rc>0で文字列要求
			goto lb_RTN;
		}
		//要求不可/不要 あるだけ送る即配
		res.rc = -100;	//-100はスルー要求 単独で始末可能
		sz = calc_sendsz(ped, sz);
		char* p = sg_ptr(msv->instr) + msv->instr_cur;
		if(jidx==0){
			if(sz>=0){ sg_add(jst->instr, p, sz); }
			else if(jst->seof==0){ jst->seof=1; }
			jst->rtn = ped_jmp(ped);
			//通常はscwrite()で自動runだからselfを手動で走らせる
			//戻って直後にscread()で更新jst->rtnが再検査される
		}else{
			jbm_t tmp ={0};
			if(sz<0){ tmp.flg = J_SENDSTR_E; }
			else{
				tmp.flg = J_SENDSTR;
				tmp.bin=p;
				tmp.binsz=sz;
			}
			int sfd = ped->msv->sfdarr[jidx];
			; STOP( jbm_scwrite(ped, sfd, &tmp), "fatal err");
		}
		msv->instr_cur +=  sz<0 ? 0 : sz;
		goto lb_RTN;
	}
//中間報告
	jbm_t jbmbuf={0};
	if(jbm->flg==J_VICINFO){
		//lock== -2ってことはもうINFOを待つ必要が無い ここにくることはない
		//lockされてないのもありえない
		;STOP(msv->wait_jidxinfo==0, "fatal err");
		msv->wait_jidxinfo = 0;
		//selfはmsv->bcntが変更される前に編集する んですり替える
		if(jidx==0){ jbmbuf = ped_edit(ped); }
		//svのデータをupdate
		int bcnt = jbm->bcnt;
		int nlcnt = jbm->nlcnt;
		int ccnt = jbm->ccnt;
		sg_del(msv->instr, bcnt);
		msv->instr_cur=0;
		msv->bcnt += bcnt;
		msv->nlcnt += nlcnt;
		msv->ccnt += ccnt;
		res.rc= -100;
		if(jidx!=0){ goto lb_RTN; }
		//fall_throught, jidx==0 >> self
		jbm = &jbmbuf;	//selfは結果出力にすり替え 
		//成功したらそのまま直接編集に入る
		//editにinfoとreqのrtnはありえないのでそのまま下の分類を拝借する
	}
//完了報告		
	if(jbm->flg==J_SENDSTR){
		//文字列を送ってくるのはEMSGと結果のみ
		//スタック位置検査
		int i=0;
		for(;;i++) {
			; STOP(i>=idq_len(msv->jidx), "fatal err");
			int idx = idq_see(msv->jidx, i);
			int rc = idq_see(msv->strq, i);
			if(rc<0 && jidx == idx){ break; }
		}
		int sqidx=i;
		char* bin = jbm->bin;
		int binsz = jbm->binsz;
		int pos = sg_sz(msv->outstr);
		sg_add(msv->outstr, bin, binsz);	//save
//
		idq_rep(msv->strq, sqidx, pos);	//orderで-1になってcompで>0になる
		idq_rep(msv->strqsz, sqidx, binsz);
		msv->pidarr[jidx] *= -1;	//回収完了でworkerを開ける
		res.rc = -100;
		goto lb_RTN;
	}
//ERR報告		
	if(jbm->flg==J_EMSG) {
		ped_reset(ped);
		res.rc = -10;	//err報告
		res.emsg = jbm->emsg;
		goto lb_RTN;
	}
	; STOP(1, "unreachable code");
lb_RTN:;
	return res;
}

static void ans_reqstr(ped_t* ped){
	msv_t* msv = ped->msv;
	char* bin = sg_ptr(msv->instr) + msv->instr_cur;
	int binsz = calc_sendsz(ped, msv->sz_reqstr);
	jbm_t jbm={0};
	int fd = ped->msv->sfdarr[msv->jidx_reqstr];
	if(binsz>=0){
		jbm.flg = J_SENDSTR;
		jbm.bin = bin;
		jbm.binsz = binsz;
		; STOP( jbm_scwrite(ped, fd, &jbm), "fatal err");
	}
	//全要求ならeofも送信する
	if(msv->sz_reqstr<0){
		jbm.flg = J_SENDSTR_E;
		jbm.bin = NULL;
		jbm.binsz = 0;
		; STOP( jbm_scwrite(ped, fd, &jbm), "fatal err");
	}
	msv->flg_reqstr = 0;
	msv->sz_reqstr = 0;
	msv->jidx_reqstr = 0;
	msv->instr_cur +=  binsz<0 ? 0 : binsz;
}

// selfの処理は常に最優先で行われる
ped_rt gaz_result(ped_t* ped){
	msv_t* msv = ped->msv;
	ped_rt res={0};
	sg_t* sgbuf = ped->edbuf->sgbuf;
	res.rc= -100;	//スルー用
	while(1) {
		if(msv->pidarr[0]<0){
			//self
			int sfd= -1;
			int jidx=0;
			jbm_t jbm = jbm_scread(ped, sfd, sgbuf);
			res = ped_rtnjbm(ped, &jbm, jidx);	//ped_jmp/editが入ってる
			if(res.rc == -100){ continue; }	//rtnjbmがself jmpもeditも strqも始末する
			//selfはここで完了までloop処理される 脱出はpid>0かreqかerrのみ
			//pid<0>0も rtnjbmが操作してくれる
			break;	//reqかerrのみ 完了strも-100で自動スルー>pid+ > 0が外れる
		}
		int jidx = jidx_screadable(ped, 0);
		if(jidx<0){ break; }	//read要求なし
		sg_clear(sgbuf);
		int sfd = ped->msv->sfdarr[jidx];
		jbm_t jbm = jbm_scread(ped, sfd, sgbuf);
		res = ped_rtnjbm(ped, &jbm, jidx);	//jmpとかeditとか入ってる lockも始末される
		if(res.rc== -100){ continue;}	//errかreqstrの二つ
		break;
	}
	return res;
}

//--mainapi

//selfとworkerが入り乱れて分かりにくい フラグはmsv->reqstr_jidxとmsv->wait_jidxinfoの二つ
//reqは注文文字列の送信先、lockはinfoが来るまで待つ用
ped_rt ped_parse_impl(ped_t* ped, const char* s, int ssz, const char* dmy) {
//init
	int fd=0;
	int rc=0;
	ped_rt res={0};
	msv_t* msv = ped->msv;
	if(s==NULL){ssz = -1;}
	else if(dmy[0]==0){ ssz=strlen(s); }	//即値指定でいけるから#系の組み合わせは悪くない

	if(ssz<0){ msv->seof= 1;}	//msvのseofは援軍がこないことを示す
	else{ sg_add(msv->instr, s, ssz); }
	if(msv->seof<0){ res.rc = -1; return res;}
	//complete待ち
	if(sg_sz(msv->instr)+idq_len(msv->jidx)==0 && msv->seof) {goto lb_MERGE;}
	//中間補充 内部で自動リセットされる
	if(msv->flg_reqstr) { ans_reqstr(ped);}	//writeでselfもjmpが発動してる

lb_TOP:;
	res = gaz_result(ped);	//とりあえず成果物回収 先にprocを開けたい
	//selfのlockはgazが最優先で始末する
	if(res.rc != -100){return res;}	//reqかerr
	//-100でくるのは待ちがない状態だけ
	
	if(msv->wait_jidxinfo>0){
		jidx_screadable(ped, -1);
		goto lb_TOP;
	}
//結果待ちのみ blockしてloopを防ぐ selfはgazで始末されてる
	if(msv->wait_jidxinfo== -2){
		for(int i=1;i<msv->jobs;i++) {
			int pid = msv->pidarr[i];
			if(pid<0){ jidx_screadable(ped, -1); }
		}
		goto lb_MERGE;	//全回収
	}
	if(msv->seof && sg_sz(msv->instr)==0){ goto lb_MERGE; } //在庫0ならスキップ

	
//--order
;STOP(msv->wait_jidxinfo!=0, "lock break");
	//送信準備 データだったりfdだったり初回
	const char* bin = sg_ptr(msv->instr)+msv->instr_cur;
	int binsz = sg_sz(msv->instr) - msv->instr_cur;
	binsz = calc_sendsz(ped, binsz);
	int jidx = jidx_scwritable(ped);	//research
	if(jidx<0){
		jidx=0;
		assert(msv->wait_jidxinfo==0);
	}	//空きがなければ自力
	fd = msv->sfdarr[jidx];
clcnt[jidx]++;

	//初回 jstはINFOで自動リセットがかかるはず
	jbm_t jbm={0};
	jbm.flg=J_VICINFO;
	jbm.bcnt = msv->bcnt;
	jbm.nlcnt = msv->nlcnt;
	jbm.ccnt = msv->ccnt;
	jbm_scwrite(ped, fd, &jbm);	//errinfo用に先行して送る
	msv->wait_jidxinfo = jidx;
	if(jidx==0){ msv->wait_jidxinfo = -1; }	//結局 -1は使わない あってもいいけど
	idq_push(msv->jidx, jidx);	// sfdarr[]系idx	selfなら-1が入る
	idq_push(msv->strq, -1);	// -は仕事中マーク かぶるけどmergeで簡単になる
	idq_push(msv->strqsz, -1);
	msv->pidarr[jidx] *= -1;	//pidマイナスで仕事中マーク

	jbm.flg=J_SENDSTR;
	jbm.bin = (char*)bin;
	jbm.binsz = binsz;
	jbm_scwrite(ped, fd, &jbm);
	msv->instr_cur += binsz;	//仮pos. infoがきたら削除してinstrもdelする
	
//後処理
lb_MERGE:;
	sg_t* sg = ped->edbuf->sgbuf;
	sg_clear(sg);
	rc=0;
	while(1) {
		if(idq_len(msv->jidx)==0) {break;}
		int flg = idq_see(msv->strq, 0);
		if(flg<0) {break;}	// -1で仕事中以外の先頭を集める 出力はoutstrの位置
		//成果アリ. 三位一体で切り落としていく
		rc=1;
		idq_drop(msv->jidx);	//不要だけど足並みを揃えるため必要
		int cur = idq_drop(msv->strq);
		int sz = idq_drop(msv->strqsz);
		sg_add(sg, sg_ptr(msv->outstr)+cur, sz);
	}
	//返却有り
	if(rc){
		//gcタイミングは全部捌けた直後だけ
		if(idq_len(msv->jidx)==0) { sg_clear(msv->outstr); }
		res.rc= -2;
		res.bin = sg_ptr(sg);
		res.binsz = sg_sz(sg);
		return res;
	}
	//なければ文字準備
	if( msv->seof==0 && sg_sz(msv->instr) < msv->bufsz_min ){
		res.rc = msv->bufsz_min;
		return res;
	}
	//文字準備もないなら 最終完了チェック
	while(msv->seof){
		if(sg_sz(msv->instr) ) { break; }	//在庫があるなら流す
		if(idq_len(msv->jidx) ) { msv->wait_jidxinfo = -2; break;}	//在庫無しwork中なら回収モード
//seofが立ってて在庫が無く、order済みもない==完了
		ped_reset(ped);
		res.rc=0;
		return res;
	}
	//waitするのはVICINFOの場合のみ。それ以外はselfがあいてるから再挑戦
	goto lb_TOP;
}













/*SH_SMP
#include "ped.h"
int main(){
	return 0;
}
//	~$ gcc smpl.c ped.c

//SH_SMPE*/


/*
 change log
 --
2021-07-10  Momi-g	<dmy@dmy.dmy>

	* ped.c (set_clists): fix grep ident fastmap, limit var idx >> nidx.
	* (jst_jmp): fix 0 consume check logic.

2021-07-05  Momi-g	<dmy@dmy.dmy>

	* ped.c (ped_new): arg:nlstr is converted with c99 charesc parser 
	* (rdata_new): add nlstr convert with luajit 

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

	* ped.c (set_clists): fix char >> unsigned char, doc

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

	* ped.c (ped_init): add h,H opt. fix doc etc.

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

	* ped.c (all): rewrite, support libso, fork(), lbc etc, ver 3

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

	* ped.c (all): gnureg, test OK, octfunc isnt implemented

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

	* ped.c (all): add gnureg, clists generator.

2021-01-15  Momi-g	<dmy@dmy.dmy>

	* ped.c (all): v2 debug, nofork run test, no clist test

2021-01-05  Momi-g	<dmy@dmy.dmy>

	* ped.c (all): rewrite. ver2. add fork(), devide jmp/edit

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

	* ped.c (all): rewrite. ver1.1. add outbuff/rulesave etc

2020-11-05  Momi-g	<dmy@dmy.dmy>

	* ped.c (all): init

*/
