Router-R3
view release on metacpan or search on metacpan
--k; \
break; \
} \
++j; \
} \
} \
capture_n[i] = this_capture_n; \
if( i < branch_n - 1 ) \
first_capture_key_head[i+1] = this_capture_key_head_cursor; \
char *errstr; \
if( !r3_tree_insert_pathl_ex(r3, pattern, pattern_len, NULL, &target[i], &errstr) ) { \
r3_tree_free(r3); \
Safefree(pad); \
croak_r3_errstr("insert path"); \
} \
}
#ifdef PERL_R3_DEBUG
#define DUMP_PAD(pad) { \
char *p = (char*)pad; \
printf("DUMP_PAD: (%p)\n", (void*)pad); \
printf(" r3=%p\n", (void*)(*(node**)p)); \
p += sizeof(node*); \
int branch_n = *(int*)p; \
p += sizeof(int); \
printf(" branch_n=%d\n targets:", branch_n); \
for(int i=0; i<branch_n; ++i) { \
printf(" %p", (void*)(*(SV**)p)); \
p += sizeof(SV*); \
} \
printf("\n capture_n:"); \
for(int i=0; i<branch_n; ++i) { \
printf(" %d", *(int*)p); \
p += sizeof(int); \
} \
printf("\n first_capture_key_head:"); \
for(int i=0; i<branch_n; ++i) { \
printf(" %p", (void*)(*(char***)p)); \
p += sizeof(char**); \
} \
printf("\n capture_key:"); \
for(int i=0; i<capture_n_total; ++i) { \
printf(" (%p,%p)", (void*)(*(char**)p), (void*)(*(char**)(p + sizeof(char*)))); \
for(char *pp=*(char**)p; pp!=*(char**)(p + sizeof(char*)); ++pp) \
printf("%c", *pp); \
p += sizeof(char*) * 2; \
} \
printf("\n capture_key_pool: "); \
for(int i=0; i<capture_key_len_total; ++i) { \
printf("%c", *(char*)p); \
++p; \
} \
printf("\n"); \
}
#else
#define DUMP_PAD(pad) ;
#endif
void
new(...)
PPCODE:
{
void *r3_pad;
int branch_n = 0;
int capture_n_total = 0;
int capture_key_len_total = 0;
if( items == 0 )
croak("Router::R3::new without classname?");
if( items == 2 && SvROK(ST(1)) ) {
SV *rv = SvRV(ST(1));
switch( SvTYPE(rv) ) {
case SVt_PVAV: { // [pattern, target, pattern, target, ...]
AV *av = (AV*)rv;
SSize_t len = av_len(av);
if( !(len & 1) )
warn("Router::R3::new with odd length array");
branch_n = len + 1 >> 1;
for(SSize_t i=0; i<=len; i+=2){
SV** key = av_fetch(av, i, 0);
if( !key || !SvPOK(*key) )
warn("The %dth element of the new call argument array should be a string", i);
STRLEN pattern_len;
char * pattern;
if( key )
pattern = SvPVbyte(*key, pattern_len);
else {
pattern = "";
pattern_len = 0;
}
ANALYZE_PATTERN(pattern, pattern_len);
}
break;
}
case SVt_PVHV: { // {pattern => target, pattern => target, ...}
HV *hv = (HV*)rv;
branch_n = hv_iterinit(hv);
char *pattern;
I32 pattern_len;
HE *he;
while( he = hv_iternext(hv) ){
pattern = hv_iterkey(he, &pattern_len);
ANALYZE_PATTERN(pattern, pattern_len);
}
break;
}
default:
warn("Router::R3::new with invalid reference");
}
} else if( items > 2 ) { // pattern, target, pattern, target, ...
branch_n = items >> 1;
if( !(items & 1) )
warn("Router::R3::new with odd arguments");
for(I32 i=1; i<items; i+=2) {
SV * key = ST(i);
if( !SvPOK(key) )
warn("The %dth argument for new call should be a string", i);
STRLEN pattern_len;
char * pattern = SvPVbyte(key, pattern_len);
ANALYZE_PATTERN(pattern, pattern_len);
}
}
else{
pattern = "";
pattern_len = 0;
}
FILL_PATTERN(r3_pad, r3, i2, pattern, pattern_len, (pval ? *pval : NULL));
}
break;
}
case SVt_PVHV: { // {pattern => target, pattern => target, ...}
HV *hv = (HV*)rv;
hv_iterinit(hv);
char *pattern;
I32 pattern_len;
SV *val;
I32 i2 = 0;
while( val = hv_iternextsv(hv, &pattern, &pattern_len) ){
FILL_PATTERN(r3_pad, r3, i2, pattern, pattern_len, val);
++i2;
}
break;
}
default:
warn("Router::R3::new with invalid reference");
}
} else if( items > 2 ) { // pattern, target, pattern, target, ...
I32 i;
for(i=1; i<items; i+=2) {
I32 i2 = i >> 1;
SV *val = i+1 < items ? ST(i+1) : NULL;
STRLEN pattern_len;
char * pattern = SvPVbyte(ST(i), pattern_len);
FILL_PATTERN(r3_pad, r3, i2, pattern, pattern_len, val);
}
}
DUMP_PAD(r3_pad);
int errno;
char *errstr;
if(( errno = r3_tree_compile(r3, &errstr) )) {
r3_tree_free(r3);
Safefree(r3_pad);
croak_r3_errstr("creating R3 routing tree fail");
}
}
SV* ret = newSV(0);
SvUPGRADE(ret, SVt_RV);
SvROK_on(ret);
SvRV(ret) = (SV*)r3_pad;
SV * obj = newRV_noinc(ret);
STRLEN classname_len;
char * classname = SvPVbyte(ST(0), classname_len);
HV * stash = gv_stashpvn(classname, classname_len, 0);
sv_bless(obj, stash);
EXTEND(SP, 1);
PUSHs(sv_2mortal(obj));
}
void
match(SV* r3_sv, SV *str_sv)
PPCODE:
void* r3_pad = SvRV(SvRV(r3_sv));
node* r3 = *(node**)r3_pad;
char *str;
STRLEN str_len;
str = SvPVbyte(str_sv, str_len);
match_entry* entry = match_entry_createl(str, str_len);
node* matched_node = r3_tree_matchl(r3, str, str_len, entry);
if( matched_node ){
SV** target_p = (SV**) matched_node->data;
#ifdef PERL_R3_DEBUG
printf("matched target_p = %p\n", (void*)target_p);
printf("matched target = %p\n", (void*)(*(SV**)target_p));
#endif
EXTEND(SP, 2);
PUSHs(sv_2mortal(newSVsv(*(SV**)target_p)));
HV* captures_hv = newHV();
int capture_n = entry->vars->len;
if( capture_n > 0 ) {
int match_i = target_p - (SV**)( (char*)r3_pad + sizeof(node*) + sizeof(int) );
int branch_n = *(int*)( (char*)r3_pad + sizeof(node*) );
int my_capture_n = *(int*)( (char*)r3_pad + sizeof(node*) + sizeof(int) + sizeof(SV*) * branch_n + sizeof(int) * match_i );
char **capture_key_cursor = *(char***)( (char*)r3_pad + sizeof(node*) + sizeof(int) + sizeof(SV*) * branch_n + sizeof(int) * branch_n + sizeof(char**) * match_i );
char ** captures = entry->vars->tokens;
#ifdef PERL_R3_DEBUG
printf("capture # = %d\n", entry->vars->len);
#endif
for(int i=0; i<capture_n && i<my_capture_n; ++i){
#ifdef PERL_R3_DEBUG
printf("capture_key_cursor = %p -> %p\n", (void*)capture_key_cursor, (void*)*capture_key_cursor);
#endif
hv_store(
captures_hv,
*capture_key_cursor, *(capture_key_cursor+1) - *capture_key_cursor,
newSVpv(captures[i], 0),
0
);
capture_key_cursor += 2;
}
}
PUSHs(sv_2mortal(newRV_noinc((SV*)captures_hv)));
}
match_entry_free(entry);
void DESTROY(SV* r3_sv)
PPCODE:
void* pad = SvRV(SvRV(r3_sv));
int branch_n = *(int*)((char*)pad + sizeof(node*));
SV** target = (SV**)((char*)pad + sizeof(node*) + sizeof(int));
for(int i=0; i<branch_n; ++i)
SvREFCNT_dec(target[i]);
r3_tree_free(*(node**)pad);
Safefree(pad);
SvRV(SvRV(r3_sv)) = 0;
#ifdef PERL_R3_DEBUG
void
test()
CODE:
_test();
#endif
( run in 1.918 second using v1.01-cache-2.11-cpan-5511b514fd6 )