Blame src/pdag.c

Packit 1422b7
/**
Packit 1422b7
 * @file pdag.c
Packit 1422b7
 * @brief Implementation of the parse dag object.
Packit 1422b7
 * @class ln_pdag pdag.h
Packit 1422b7
 *//*
Packit 1422b7
 * Copyright 2015 by Rainer Gerhards and Adiscon GmbH.
Packit 1422b7
 *
Packit 1422b7
 * Released under ASL 2.0.
Packit 1422b7
 */
Packit 1422b7
#include "config.h"
Packit 1422b7
#include <stdlib.h>
Packit 1422b7
#include <stdio.h>
Packit 1422b7
#include <stdarg.h>
Packit 1422b7
#include <string.h>
Packit 1422b7
#include <assert.h>
Packit 1422b7
#include <ctype.h>
Packit 1422b7
#include <libestr.h>
Packit 1422b7
Packit 1422b7
#include "liblognorm.h"
Packit 1422b7
#include "v1_liblognorm.h"
Packit 1422b7
#include "v1_ptree.h"
Packit 1422b7
#include "lognorm.h"
Packit 1422b7
#include "samp.h"
Packit 1422b7
#include "pdag.h"
Packit 1422b7
#include "annot.h"
Packit 1422b7
#include "internal.h"
Packit 1422b7
#include "parser.h"
Packit 1422b7
#include "helpers.h"
Packit 1422b7
Packit 1422b7
void ln_displayPDAGComponentAlternative(struct ln_pdag *dag, int level);
Packit 1422b7
void ln_displayPDAGComponent(struct ln_pdag *dag, int level);
Packit 1422b7
Packit 1422b7
#ifdef	ADVANCED_STATS
Packit 1422b7
uint64_t advstats_parsers_called = 0;
Packit 1422b7
uint64_t advstats_parsers_success = 0;
Packit 1422b7
int advstats_max_pathlen = 0;
Packit 1422b7
int advstats_pathlens[ADVSTATS_MAX_ENTITIES];
Packit 1422b7
int advstats_max_backtracked = 0;
Packit 1422b7
int advstats_backtracks[ADVSTATS_MAX_ENTITIES];
Packit 1422b7
int advstats_max_parser_calls = 0;
Packit 1422b7
int advstats_parser_calls[ADVSTATS_MAX_ENTITIES];
Packit 1422b7
int advstats_max_lit_parser_calls = 0;
Packit 1422b7
int advstats_lit_parser_calls[ADVSTATS_MAX_ENTITIES];
Packit 1422b7
#endif
Packit 1422b7
Packit 1422b7
/* parser lookup table
Packit 1422b7
 * This is a memory- and cache-optimized way of calling parsers.
Packit 1422b7
 * VERY IMPORTANT: the initialization must be done EXACTLY in the
Packit 1422b7
 * order of parser IDs (also see comment in pdag.h).
Packit 1422b7
 *
Packit 1422b7
 * Rough guideline for assigning priorites:
Packit 1422b7
 * 0 is highest, 255 lowest. 255 should be reserved for things that
Packit 1422b7
 * *really* should only be run as last resort --> rest. Also keep in
Packit 1422b7
 * mind that the user-assigned priority is put in the upper 24 bits, so
Packit 1422b7
 * parser-specific priorities only count when the user has assigned
Packit 1422b7
 * no priorities (which is expected to be common) or user-assigned
Packit 1422b7
 * priorities are equal for some parsers.
Packit 1422b7
 */
Packit 1422b7
#ifdef ADVANCED_STATS
Packit 1422b7
#define PARSER_ENTRY_NO_DATA(identifier, parser, prio) \
Packit 1422b7
{ identifier, prio, NULL, ln_v2_parse##parser, NULL, 0, 0 }
Packit 1422b7
#define PARSER_ENTRY(identifier, parser, prio) \
Packit 1422b7
{ identifier, prio, ln_construct##parser, ln_v2_parse##parser, ln_destruct##parser, 0, 0 }
Packit 1422b7
#else
Packit 1422b7
#define PARSER_ENTRY_NO_DATA(identifier, parser, prio) \
Packit 1422b7
{ identifier, prio, NULL, ln_v2_parse##parser, NULL }
Packit 1422b7
#define PARSER_ENTRY(identifier, parser, prio) \
Packit 1422b7
{ identifier, prio, ln_construct##parser, ln_v2_parse##parser, ln_destruct##parser }
Packit 1422b7
#endif
Packit 1422b7
static struct ln_parser_info parser_lookup_table[] = {
Packit 1422b7
	PARSER_ENTRY("literal", Literal, 4),
Packit 1422b7
	PARSER_ENTRY("repeat", Repeat, 4),
Packit 1422b7
	PARSER_ENTRY("date-rfc3164", RFC3164Date, 8),
Packit 1422b7
	PARSER_ENTRY("date-rfc5424", RFC5424Date, 8),
Packit 1422b7
	PARSER_ENTRY("number", Number, 16),
Packit 1422b7
	PARSER_ENTRY("float", Float, 16),
Packit 1422b7
	PARSER_ENTRY("hexnumber", HexNumber, 16),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("kernel-timestamp", KernelTimestamp, 16),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("whitespace", Whitespace, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("ipv4", IPv4, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("ipv6", IPv6, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("word", Word, 32),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("alpha", Alpha, 32),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("rest", Rest, 255),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("op-quoted-string", OpQuotedString, 64),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("quoted-string", QuotedString, 64),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("date-iso", ISODate, 8),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("time-24hr", Time24hr, 8),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("time-12hr", Time12hr, 8),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("duration", Duration, 16),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("cisco-interface-spec", CiscoInterfaceSpec, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("name-value-list", NameValue, 8),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("json", JSON, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("cee-syslog", CEESyslog, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("mac48", MAC48, 16),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("cef", CEF, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("checkpoint-lea", CheckpointLEA, 4),
Packit 1422b7
	PARSER_ENTRY_NO_DATA("v2-iptables", v2IPTables, 4),
Packit 1422b7
	PARSER_ENTRY("string-to", StringTo, 32),
Packit 1422b7
	PARSER_ENTRY("char-to", CharTo, 32),
Packit 1422b7
	PARSER_ENTRY("char-sep", CharSeparated, 32),
Packit 1422b7
	PARSER_ENTRY("string", String, 32)
Packit 1422b7
};
Packit 1422b7
#define NPARSERS (sizeof(parser_lookup_table)/sizeof(struct ln_parser_info))
Packit 1422b7
#define DFLT_USR_PARSER_PRIO 30000 /**< default priority if user has not specified it */
Packit 1422b7
static inline const char *
Packit 1422b7
parserName(const prsid_t id)
Packit 1422b7
{
Packit 1422b7
	const char *name;
Packit 1422b7
	if(id == PRS_CUSTOM_TYPE)
Packit 1422b7
		name = "USER-DEFINED";
Packit 1422b7
	else
Packit 1422b7
		name = parser_lookup_table[id].name;
Packit 1422b7
	return name;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
prsid_t
Packit 1422b7
ln_parserName2ID(const char *const __restrict__ name)
Packit 1422b7
{
Packit 1422b7
	unsigned i;
Packit 1422b7
Packit 1422b7
	for(  i = 0
Packit 1422b7
	    ; i < sizeof(parser_lookup_table) / sizeof(struct ln_parser_info)
Packit 1422b7
	    ; ++i) {
Packit 1422b7
	    	if(!strcmp(parser_lookup_table[i].name, name)) {
Packit 1422b7
			return i;
Packit 1422b7
		}
Packit 1422b7
	    }
Packit 1422b7
	return PRS_INVALID;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* find type pdag in table. If "bAdd" is set, add it if not
Packit 1422b7
 * already present, a new entry will be added.
Packit 1422b7
 * Returns NULL on error, ptr to type pdag entry otherwise
Packit 1422b7
 */
Packit 1422b7
struct ln_type_pdag *
Packit 1422b7
ln_pdagFindType(ln_ctx ctx, const char *const __restrict__ name, const int bAdd)
Packit 1422b7
{
Packit 1422b7
	struct ln_type_pdag *td = NULL;
Packit 1422b7
	int i;
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(ctx, "ln_pdagFindType, name '%s', bAdd: %d, nTypes %d",
Packit 1422b7
		name, bAdd, ctx->nTypes);
Packit 1422b7
	for(i = 0 ; i < ctx->nTypes ; ++i) {
Packit 1422b7
		if(!strcmp(ctx->type_pdags[i].name, name)) {
Packit 1422b7
			td = ctx->type_pdags + i;
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(!bAdd) {
Packit 1422b7
		LN_DBGPRINTF(ctx, "custom type '%s' not found", name);
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* type does not yet exist -- create entry */
Packit 1422b7
	LN_DBGPRINTF(ctx, "custom type '%s' does not yet exist, adding...", name);
Packit 1422b7
	struct ln_type_pdag *newarr;
Packit 1422b7
	newarr = realloc(ctx->type_pdags, sizeof(struct ln_type_pdag) * (ctx->nTypes+1));
Packit 1422b7
	if(newarr == NULL) {
Packit 1422b7
		LN_DBGPRINTF(ctx, "ln_pdagFindTypeAG: alloc newarr failed");
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	ctx->type_pdags = newarr;
Packit 1422b7
	td = ctx->type_pdags + ctx->nTypes;
Packit 1422b7
	++ctx->nTypes;
Packit 1422b7
	td->name = strdup(name);
Packit 1422b7
	td->pdag = ln_newPDAG(ctx);
Packit 1422b7
done:
Packit 1422b7
	return td;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* we clear some multiple times, but as long as we have no loops
Packit 1422b7
 * (dag!) we have no real issue.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
ln_pdagComponentClearVisited(struct ln_pdag *const dag)
Packit 1422b7
{
Packit 1422b7
	dag->flags.visited = 0;
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
		ln_pdagComponentClearVisited(prs->node);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
static void
Packit 1422b7
ln_pdagClearVisited(ln_ctx ctx)
Packit 1422b7
{
Packit 1422b7
	for(int i = 0 ; i < ctx->nTypes ; ++i)
Packit 1422b7
		ln_pdagComponentClearVisited(ctx->type_pdags[i].pdag);
Packit 1422b7
	ln_pdagComponentClearVisited(ctx->pdag);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Process a parser defintion. Note that a single defintion can potentially
Packit 1422b7
 * contain many parser instances.
Packit 1422b7
 * @return parser node ptr or NULL (on error)
Packit 1422b7
 */
Packit 1422b7
ln_parser_t*
Packit 1422b7
ln_newParser(ln_ctx ctx,
Packit 1422b7
	json_object *prscnf)
Packit 1422b7
{
Packit 1422b7
	ln_parser_t *node = NULL;
Packit 1422b7
	json_object *json;
Packit 1422b7
	const char *val;
Packit 1422b7
	prsid_t prsid;
Packit 1422b7
	struct ln_type_pdag *custType = NULL;
Packit 1422b7
	const char *name = NULL;
Packit 1422b7
	const char *textconf = json_object_to_json_string(prscnf);
Packit 1422b7
	int assignedPrio = DFLT_USR_PARSER_PRIO;
Packit 1422b7
	int parserPrio;
Packit 1422b7
Packit 1422b7
	json_object_object_get_ex(prscnf, "type", &json);
Packit 1422b7
	if(json == NULL) {
Packit 1422b7
		ln_errprintf(ctx, 0, "parser type missing in config: %s",
Packit 1422b7
			json_object_to_json_string(prscnf));
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	val = json_object_get_string(json);
Packit 1422b7
	if(*val == '@') {
Packit 1422b7
		prsid = PRS_CUSTOM_TYPE;
Packit 1422b7
		custType = ln_pdagFindType(ctx, val, 0);
Packit 1422b7
		parserPrio = 16; /* hopefully relatively specific... */
Packit 1422b7
		if(custType == NULL) {
Packit 1422b7
			ln_errprintf(ctx, 0, "unknown user-defined type '%s'", val);
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
	} else {
Packit 1422b7
		prsid = ln_parserName2ID(val);
Packit 1422b7
		if(prsid == PRS_INVALID) {
Packit 1422b7
			ln_errprintf(ctx, 0, "invalid field type '%s'", val);
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
		parserPrio = parser_lookup_table[prsid].prio;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	json_object_object_get_ex(prscnf, "name", &json);
Packit 1422b7
	if(json == NULL || !strcmp(json_object_get_string(json), "-")) {
Packit 1422b7
		name = NULL;
Packit 1422b7
	} else {
Packit 1422b7
		name = strdup(json_object_get_string(json));
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	json_object_object_get_ex(prscnf, "priority", &json);
Packit 1422b7
	if(json != NULL) {
Packit 1422b7
		assignedPrio = json_object_get_int(json);
Packit 1422b7
	}
Packit 1422b7
	LN_DBGPRINTF(ctx, "assigned priority is %d", assignedPrio);
Packit 1422b7
Packit 1422b7
	/* we need to remove already processed items from the config, so
Packit 1422b7
	 * that we can pass the remaining parameters to the parser.
Packit 1422b7
	 */
Packit 1422b7
	json_object_object_del(prscnf, "type");
Packit 1422b7
	json_object_object_del(prscnf, "priority");
Packit 1422b7
	if(name != NULL)
Packit 1422b7
		json_object_object_del(prscnf, "name");
Packit 1422b7
Packit 1422b7
	/* got all data items */
Packit 1422b7
	if((node = calloc(1, sizeof(ln_parser_t))) == NULL) {
Packit 1422b7
		LN_DBGPRINTF(ctx, "lnNewParser: alloc node failed");
Packit 1422b7
		free((void*)name);
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	node->node = NULL;
Packit 1422b7
	node->prio = ((assignedPrio << 8) & 0xffffff00) | (parserPrio & 0xff);
Packit 1422b7
	node->name = name;
Packit 1422b7
	node->prsid = prsid;
Packit 1422b7
	node->conf = strdup(textconf);
Packit 1422b7
	if(prsid == PRS_CUSTOM_TYPE) {
Packit 1422b7
		node->custType = custType;
Packit 1422b7
	} else {
Packit 1422b7
		if(parser_lookup_table[prsid].construct != NULL) {
Packit 1422b7
			parser_lookup_table[prsid].construct(ctx, prscnf, &node->parser_data);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	return node;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
struct ln_pdag*
Packit 1422b7
ln_newPDAG(ln_ctx ctx)
Packit 1422b7
{
Packit 1422b7
	struct ln_pdag *dag;
Packit 1422b7
Packit 1422b7
	if((dag = calloc(1, sizeof(struct ln_pdag))) == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
	
Packit 1422b7
	dag->refcnt = 1;
Packit 1422b7
	dag->ctx = ctx;
Packit 1422b7
	ctx->nNodes++;
Packit 1422b7
done:	return dag;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* note: we must NOT free the parser itself, because
Packit 1422b7
 * it is stored inside a parser table (so no single
Packit 1422b7
 * alloc for the parser!).
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
pdagDeletePrs(ln_ctx ctx, ln_parser_t *const __restrict__ prs)
Packit 1422b7
{
Packit 1422b7
	// TODO: be careful here: once we move to real DAG from tree, we
Packit 1422b7
	// cannot simply delete the next node! (refcount? something else?)
Packit 1422b7
	if(prs->node != NULL)
Packit 1422b7
		ln_pdagDelete(prs->node);
Packit 1422b7
	free((void*)prs->name);
Packit 1422b7
	free((void*)prs->conf);
Packit 1422b7
	if(prs->parser_data != NULL)
Packit 1422b7
		parser_lookup_table[prs->prsid].destruct(ctx, prs->parser_data);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_pdagDelete(struct ln_pdag *const __restrict__ pdag)
Packit 1422b7
{
Packit 1422b7
	if(pdag == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(pdag->ctx, "delete %p[%d]: %s", pdag, pdag->refcnt, pdag->rb_id);
Packit 1422b7
	--pdag->refcnt;
Packit 1422b7
	if(pdag->refcnt > 0)
Packit 1422b7
		goto done;
Packit 1422b7
Packit 1422b7
	if(pdag->tags != NULL)
Packit 1422b7
		json_object_put(pdag->tags);
Packit 1422b7
Packit 1422b7
	for(int i = 0 ; i < pdag->nparsers ; ++i) {
Packit 1422b7
		pdagDeletePrs(pdag->ctx, pdag->parsers+i);
Packit 1422b7
	}
Packit 1422b7
	free(pdag->parsers);
Packit 1422b7
	free((void*)pdag->rb_id);
Packit 1422b7
	free((void*)pdag->rb_file);
Packit 1422b7
	free(pdag);
Packit 1422b7
done:	return;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * pdag optimizer step: literal path compaction
Packit 1422b7
 *
Packit 1422b7
 * We compress as much as possible and evalute the path down to
Packit 1422b7
 * the first non-compressable element. Note that we must NOT
Packit 1422b7
 * compact those literals that are either terminal nodes OR
Packit 1422b7
 * contain names so that the literal is to be parsed out.
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
optLitPathCompact(ln_ctx ctx, ln_parser_t *prs)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
Packit 1422b7
	while(prs != NULL) {
Packit 1422b7
		/* note the NOT prefix in the condition below! */
Packit 1422b7
		if(!(   prs->prsid == PRS_LITERAL
Packit 1422b7
		     && prs->name == NULL
Packit 1422b7
		     && prs->node->flags.isTerminal == 0
Packit 1422b7
		     && prs->node->refcnt == 1
Packit 1422b7
		     && prs->node->nparsers == 1
Packit 1422b7
		     /* we need to do some checks on the child as well */
Packit 1422b7
		     && prs->node->parsers[0].prsid == PRS_LITERAL
Packit 1422b7
		     && prs->node->parsers[0].name == NULL
Packit 1422b7
		     && prs->node->parsers[0].node->refcnt == 1)
Packit 1422b7
		  )
Packit 1422b7
			goto done;
Packit 1422b7
Packit 1422b7
		/* ok, we have two compactable literals in a row, let's compact the nodes */
Packit 1422b7
		ln_parser_t *child_prs = prs->node->parsers;
Packit 1422b7
		LN_DBGPRINTF(ctx, "opt path compact: add %p to %p", child_prs, prs);
Packit 1422b7
		CHKR(ln_combineData_Literal(prs->parser_data, child_prs->parser_data));
Packit 1422b7
		ln_pdag *const node_del = prs->node;
Packit 1422b7
		prs->node = child_prs->node;
Packit 1422b7
		child_prs->node = NULL; /* remove, else this would be destructed! */
Packit 1422b7
		ln_pdagDelete(node_del);
Packit 1422b7
	}
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
static int
Packit 1422b7
qsort_parserCmp(const void *v1, const void *v2)
Packit 1422b7
{
Packit 1422b7
	const ln_parser_t *const p1 = (const ln_parser_t *const) v1;
Packit 1422b7
	const ln_parser_t *const p2 = (const ln_parser_t *const) v2;
Packit 1422b7
	return p1->prio - p2->prio;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static int
Packit 1422b7
ln_pdagComponentOptimize(ln_ctx ctx, struct ln_pdag *const dag)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
Packit 1422b7
for(int i = 0 ; i < dag->nparsers ; ++i) { /* TODO: remove when confident enough */
Packit 1422b7
	ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
	LN_DBGPRINTF(ctx, "pre sort, parser %d:%s[%d]", i, prs->name, prs->prio);
Packit 1422b7
}
Packit 1422b7
	/* first sort parsers in priority order */
Packit 1422b7
	if(dag->nparsers > 1) {
Packit 1422b7
		qsort(dag->parsers, dag->nparsers, sizeof(ln_parser_t), qsort_parserCmp);
Packit 1422b7
	}
Packit 1422b7
for(int i = 0 ; i < dag->nparsers ; ++i) { /* TODO: remove when confident enough */
Packit 1422b7
	ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
	LN_DBGPRINTF(ctx, "post sort, parser %d:%s[%d]", i, prs->name, prs->prio);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
	/* now on to rest of processing */
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
		LN_DBGPRINTF(dag->ctx, "optimizing %p: field %d type '%s', name '%s': '%s':",
Packit 1422b7
			prs->node, i, parserName(prs->prsid), prs->name,
Packit 1422b7
			(prs->prsid == PRS_LITERAL) ?  ln_DataForDisplayLiteral(dag->ctx, prs->parser_data)
Packit 1422b7
				: "UNKNOWN");
Packit 1422b7
Packit 1422b7
		optLitPathCompact(ctx, prs);
Packit 1422b7
Packit 1422b7
		ln_pdagComponentOptimize(ctx, prs->node);
Packit 1422b7
	}
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
static void
Packit 1422b7
deleteComponentID(struct ln_pdag *const __restrict__ dag)
Packit 1422b7
{
Packit 1422b7
	free((void*)dag->rb_id);
Packit 1422b7
	dag->rb_id = NULL;
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
		deleteComponentID(prs->node);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
/* fixes rb_ids for this node as well as it predecessors.
Packit 1422b7
 * This is required if the ALTERNATIVE parser type is used,
Packit 1422b7
 * which will create component IDs for each of it's invocations.
Packit 1422b7
 * As such, we do not only fix the string, but know that all
Packit 1422b7
 * children also need fixning. We do this be simply deleting
Packit 1422b7
 * all of their rb_ids, as we know they will be visited again.
Packit 1422b7
 * Note: if we introduce the same situation by new functionality,
Packit 1422b7
 * we may need to review this code here as well. Also note
Packit 1422b7
 * that the component ID will not be 100% correct after our fix,
Packit 1422b7
 * because that ID could acutally be created by two sets of rules.
Packit 1422b7
 * But this is the best we can do.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
fixComponentID(struct ln_pdag *const __restrict__ dag, const char *const new)
Packit 1422b7
{
Packit 1422b7
	char *updated;
Packit 1422b7
	const char *const curr = dag->rb_id;
Packit 1422b7
	int i;
Packit 1422b7
	int len = (int) strlen(curr);
Packit 1422b7
	for(i = 0 ; i < len ; ++i){
Packit 1422b7
		if(curr[i] != new [i])
Packit 1422b7
			break;
Packit 1422b7
	}
Packit 1422b7
	if(i >= 1 && curr[i-1] == '%')
Packit 1422b7
		--i;
Packit 1422b7
	if(asprintf(&updated, "%.*s[%s|%s]", i, curr, curr+i, new+i) == -1)
Packit 1422b7
		goto done;
Packit 1422b7
	deleteComponentID(dag);
Packit 1422b7
	dag->rb_id = updated;
Packit 1422b7
done:	return;
Packit 1422b7
}
Packit 1422b7
/**
Packit 1422b7
 * Assign human-readable identifiers (names) to each node. These are
Packit 1422b7
 * later used in stats, debug output and whereever else this may make
Packit 1422b7
 * sense.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
ln_pdagComponentSetIDs(ln_ctx ctx, struct ln_pdag *const dag, const char *prefix)
Packit 1422b7
{
Packit 1422b7
	char *id = NULL;
Packit 1422b7
Packit 1422b7
	if(prefix == NULL)
Packit 1422b7
		goto done;
Packit 1422b7
	if(dag->rb_id == NULL) {
Packit 1422b7
		dag->rb_id = strdup(prefix);
Packit 1422b7
	} else {
Packit 1422b7
		LN_DBGPRINTF(ctx, "rb_id already exists - fixing as good as "
Packit 1422b7
			"possible. This happens with ALTERNATIVE parser. "
Packit 1422b7
			"old: '%s', new: '%s'",
Packit 1422b7
			dag->rb_id, prefix);
Packit 1422b7
		fixComponentID(dag, prefix);
Packit 1422b7
		LN_DBGPRINTF(ctx, "\"fixed\" rb_id: %s", dag->rb_id);
Packit 1422b7
		prefix = dag->rb_id;
Packit 1422b7
	}
Packit 1422b7
	/* now on to rest of processing */
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
		if(prs->prsid == PRS_LITERAL) {
Packit 1422b7
			if(prs->name == NULL) {
Packit 1422b7
				if(asprintf(&id, "%s%s", prefix,
Packit 1422b7
					ln_DataForDisplayLiteral(dag->ctx, prs->parser_data)) == -1)
Packit 1422b7
					goto done;
Packit 1422b7
			} else {
Packit 1422b7
				if(asprintf(&id, "%s%%%s:%s:%s%%", prefix,
Packit 1422b7
					prs->name,
Packit 1422b7
					parserName(prs->prsid),
Packit 1422b7
					ln_DataForDisplayLiteral(dag->ctx, prs->parser_data)) == -1)
Packit 1422b7
					goto done;
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			if(asprintf(&id, "%s%%%s:%s%%", prefix,
Packit 1422b7
				prs->name ? prs->name : "-",
Packit 1422b7
				parserName(prs->prsid)) == -1)
Packit 1422b7
					goto done;
Packit 1422b7
		}
Packit 1422b7
		ln_pdagComponentSetIDs(ctx, prs->node, id);
Packit 1422b7
		free(id);
Packit 1422b7
	}
Packit 1422b7
done:	return;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Optimize the pdag.
Packit 1422b7
 * This includes all components.
Packit 1422b7
 */
Packit 1422b7
int
Packit 1422b7
ln_pdagOptimize(ln_ctx ctx)
Packit 1422b7
{
Packit 1422b7
	int r = 0;
Packit 1422b7
Packit 1422b7
	for(int i = 0 ; i < ctx->nTypes ; ++i) {
Packit 1422b7
		LN_DBGPRINTF(ctx, "optimizing component %s\n", ctx->type_pdags[i].name);
Packit 1422b7
		ln_pdagComponentOptimize(ctx, ctx->type_pdags[i].pdag);
Packit 1422b7
		ln_pdagComponentSetIDs(ctx, ctx->type_pdags[i].pdag, "");
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(ctx, "optimizing main pdag component");
Packit 1422b7
	ln_pdagComponentOptimize(ctx, ctx->pdag);
Packit 1422b7
	LN_DBGPRINTF(ctx, "finished optimizing main pdag component");
Packit 1422b7
	ln_pdagComponentSetIDs(ctx, ctx->pdag, "");
Packit 1422b7
LN_DBGPRINTF(ctx, "---AFTER OPTIMIZATION------------------");
Packit 1422b7
ln_displayPDAG(ctx);
Packit 1422b7
LN_DBGPRINTF(ctx, "=======================================");
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
#define LN_INTERN_PDAG_STATS_NPARSERS 100
Packit 1422b7
/* data structure for pdag statistics */
Packit 1422b7
struct pdag_stats {
Packit 1422b7
	int nodes;
Packit 1422b7
	int term_nodes;
Packit 1422b7
	int parsers;
Packit 1422b7
	int max_nparsers;
Packit 1422b7
	int nparsers_cnt[LN_INTERN_PDAG_STATS_NPARSERS];
Packit 1422b7
	int nparsers_100plus;
Packit 1422b7
	int *prs_cnt;
Packit 1422b7
};
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Recursive step of statistics gatherer.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
ln_pdagStatsRec(ln_ctx ctx, struct ln_pdag *const dag, struct pdag_stats *const stats)
Packit 1422b7
{
Packit 1422b7
	if(dag->flags.visited)
Packit 1422b7
		return 0;
Packit 1422b7
	dag->flags.visited = 1;
Packit 1422b7
	stats->nodes++;
Packit 1422b7
	if(dag->flags.isTerminal)
Packit 1422b7
		stats->term_nodes++;
Packit 1422b7
	if(dag->nparsers > stats->max_nparsers)
Packit 1422b7
		stats->max_nparsers = dag->nparsers;
Packit 1422b7
	if(dag->nparsers >= LN_INTERN_PDAG_STATS_NPARSERS)
Packit 1422b7
		stats->nparsers_100plus++;
Packit 1422b7
	else
Packit 1422b7
		stats->nparsers_cnt[dag->nparsers]++;
Packit 1422b7
	stats->parsers += dag->nparsers;
Packit 1422b7
	int max_path = 0;
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *prs = dag->parsers+i;
Packit 1422b7
		if(prs->prsid != PRS_CUSTOM_TYPE)
Packit 1422b7
			stats->prs_cnt[prs->prsid]++;
Packit 1422b7
		const int path_len = ln_pdagStatsRec(ctx, prs->node, stats);
Packit 1422b7
		if(path_len > max_path)
Packit 1422b7
			max_path = path_len;
Packit 1422b7
	}
Packit 1422b7
	return max_path + 1;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
static void
Packit 1422b7
ln_pdagStatsExtended(ln_ctx ctx, struct ln_pdag *const dag, FILE *const fp, int level)
Packit 1422b7
{
Packit 1422b7
	char indent[2048];
Packit 1422b7
Packit 1422b7
	if(level > 1023)
Packit 1422b7
		level = 1023;
Packit 1422b7
	memset(indent, ' ', level * 2);
Packit 1422b7
	indent[level * 2] = '\0';
Packit 1422b7
Packit 1422b7
	if(dag->stats.called > 0) {
Packit 1422b7
		fprintf(fp, "%u, %u, %s\n",
Packit 1422b7
			dag->stats.called,
Packit 1422b7
			dag->stats.backtracked,
Packit 1422b7
			dag->rb_id);
Packit 1422b7
	}
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *const prs = dag->parsers+i;
Packit 1422b7
		if(prs->node->stats.called > 0) {
Packit 1422b7
			ln_pdagStatsExtended(ctx, prs->node, fp, level+1);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Gather pdag statistics for a *specific* pdag.
Packit 1422b7
 *
Packit 1422b7
 * Data is sent to given file ptr.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
ln_pdagStats(ln_ctx ctx, struct ln_pdag *const dag, FILE *const fp, const int extendedStats)
Packit 1422b7
{
Packit 1422b7
	struct pdag_stats *const stats = calloc(1, sizeof(struct pdag_stats));
Packit 1422b7
	stats->prs_cnt = calloc(NPARSERS, sizeof(int));
Packit 1422b7
	//ln_pdagClearVisited(ctx);
Packit 1422b7
	const int longest_path = ln_pdagStatsRec(ctx, dag, stats);
Packit 1422b7
Packit 1422b7
	fprintf(fp, "nodes.............: %4d\n", stats->nodes);
Packit 1422b7
	fprintf(fp, "terminal nodes....: %4d\n", stats->term_nodes);
Packit 1422b7
	fprintf(fp, "parsers entries...: %4d\n", stats->parsers);
Packit 1422b7
	fprintf(fp, "longest path......: %4d\n", longest_path);
Packit 1422b7
Packit 1422b7
	fprintf(fp, "Parser Type Counts:\n");
Packit 1422b7
	for(prsid_t i = 0 ; i < NPARSERS ; ++i) {
Packit 1422b7
		if(stats->prs_cnt[i] != 0)
Packit 1422b7
			fprintf(fp, "\t%20s: %d\n", parserName(i), stats->prs_cnt[i]);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	int pp = 0;
Packit 1422b7
	fprintf(fp, "Parsers per Node:\n");
Packit 1422b7
	fprintf(fp, "\tmax:\t%4d\n", stats->max_nparsers);
Packit 1422b7
	for(int i = 0 ; i < 100 ; ++i) {
Packit 1422b7
		pp += stats->nparsers_cnt[i];
Packit 1422b7
		if(stats->nparsers_cnt[i] != 0)
Packit 1422b7
			fprintf(fp, "\t%d:\t%4d\n", i, stats->nparsers_cnt[i]);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	free(stats->prs_cnt);
Packit 1422b7
	free(stats);
Packit 1422b7
	
Packit 1422b7
	if(extendedStats) {
Packit 1422b7
		fprintf(fp, "Usage Statistics:\n"
Packit 1422b7
			    "-----------------\n");
Packit 1422b7
		fprintf(fp, "called, backtracked, rule\n");
Packit 1422b7
		ln_pdagComponentClearVisited(dag);
Packit 1422b7
		ln_pdagStatsExtended(ctx, dag, fp, 0);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Gather and output pdag statistics for the full pdag (ctx)
Packit 1422b7
 * including all disconnected components (type defs).
Packit 1422b7
 *
Packit 1422b7
 * Data is sent to given file ptr.
Packit 1422b7
 */
Packit 1422b7
void
Packit 1422b7
ln_fullPdagStats(ln_ctx ctx, FILE *const fp, const int extendedStats)
Packit 1422b7
{
Packit 1422b7
	if(ctx->ptree != NULL) {
Packit 1422b7
		/* we need to handle the old cruft */
Packit 1422b7
		ln_fullPTreeStats(ctx, fp, extendedStats);
Packit 1422b7
		return;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	fprintf(fp, "User-Defined Types\n"
Packit 1422b7
	            "==================\n");
Packit 1422b7
	fprintf(fp, "number types: %d\n", ctx->nTypes);
Packit 1422b7
	for(int i = 0 ; i < ctx->nTypes ; ++i)
Packit 1422b7
		fprintf(fp, "type: %s\n", ctx->type_pdags[i].name);
Packit 1422b7
Packit 1422b7
	for(int i = 0 ; i < ctx->nTypes ; ++i) {
Packit 1422b7
		fprintf(fp, "\n"
Packit 1422b7
			    "type PDAG: %s\n"
Packit 1422b7
		            "----------\n", ctx->type_pdags[i].name);
Packit 1422b7
		ln_pdagStats(ctx, ctx->type_pdags[i].pdag, fp, extendedStats);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	fprintf(fp, "\n"
Packit 1422b7
		    "Main PDAG\n"
Packit 1422b7
	            "=========\n");
Packit 1422b7
	ln_pdagStats(ctx, ctx->pdag, fp, extendedStats);
Packit 1422b7
Packit 1422b7
#ifdef	ADVANCED_STATS
Packit 1422b7
	const uint64_t parsers_failed = advstats_parsers_called - advstats_parsers_success;
Packit 1422b7
	fprintf(fp, "\n"
Packit 1422b7
		    "Advanced Runtime Stats\n"
Packit 1422b7
	            "======================\n");
Packit 1422b7
	fprintf(fp, "These are actual number from analyzing the control flow "
Packit 1422b7
		    "at runtime.\n");
Packit 1422b7
	fprintf(fp, "Note that literal matching is also done via parsers. As such, \n"
Packit 1422b7
		    "it is expected that fail rates increase with the size of the \n"
Packit 1422b7
		    "rule base.\n");
Packit 1422b7
	fprintf(fp, "\n");
Packit 1422b7
	fprintf(fp, "Parser Calls:\n");
Packit 1422b7
	fprintf(fp, "total....: %10" PRIu64 "\n", advstats_parsers_called);
Packit 1422b7
	fprintf(fp, "succesful: %10" PRIu64 "\n", advstats_parsers_success);
Packit 1422b7
	fprintf(fp, "failed...: %10" PRIu64 " [%d%%]\n",
Packit 1422b7
		parsers_failed,
Packit 1422b7
		(int) ((parsers_failed * 100) / advstats_parsers_called) );
Packit 1422b7
	fprintf(fp, "\nIndividual Parser Calls "
Packit 1422b7
		    "(never called parsers are not shown):\n");
Packit 1422b7
	for(  size_t i = 0
Packit 1422b7
	    ; i < sizeof(parser_lookup_table) / sizeof(struct ln_parser_info)
Packit 1422b7
	    ; ++i) {
Packit 1422b7
		if(parser_lookup_table[i].called > 0) {
Packit 1422b7
			const uint64_t failed = parser_lookup_table[i].called
Packit 1422b7
				- parser_lookup_table[i].success;
Packit 1422b7
			fprintf(fp, "%20s: %10" PRIu64 " [%5.2f%%] "
Packit 1422b7
				    "success: %10" PRIu64 " [%5.1f%%] "
Packit 1422b7
				    "fail: %10" PRIu64 " [%5.1f%%]"
Packit 1422b7
			            "\n",
Packit 1422b7
				parser_lookup_table[i].name,
Packit 1422b7
				parser_lookup_table[i].called,
Packit 1422b7
				(float)(parser_lookup_table[i].called * 100)
Packit 1422b7
				        / advstats_parsers_called,
Packit 1422b7
				parser_lookup_table[i].success,
Packit 1422b7
				(float)(parser_lookup_table[i].success * 100)
Packit 1422b7
				        / parser_lookup_table[i].called,
Packit 1422b7
				failed,
Packit 1422b7
				(float)(failed * 100)
Packit 1422b7
				        / parser_lookup_table[i].called
Packit 1422b7
			       );
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	uint64_t total_len;
Packit 1422b7
	uint64_t total_cnt;
Packit 1422b7
	fprintf(fp, "\n");
Packit 1422b7
	fprintf(fp, "\n"
Packit 1422b7
	            "Path Length Statistics\n"
Packit 1422b7
	            "----------------------\n"
Packit 1422b7
	            "The regular path length is the number of nodes being visited,\n"
Packit 1422b7
		    "where each node potentially evaluates several parsers. The\n"
Packit 1422b7
		    "parser call statistic is the number of parsers called along\n"
Packit 1422b7
		    "the path. That number is higher, as multiple parsers may be\n"
Packit 1422b7
		    "called at each node. The number of literal parser calls is\n"
Packit 1422b7
		    "given explicitely, as they use almost no time to process.\n"
Packit 1422b7
		    "\n"
Packit 1422b7
		);
Packit 1422b7
	total_len = 0;
Packit 1422b7
	total_cnt = 0;
Packit 1422b7
	fprintf(fp, "Path Length\n");
Packit 1422b7
	for(int i = 0 ; i < ADVSTATS_MAX_ENTITIES ; ++i) {
Packit 1422b7
		if(advstats_pathlens[i] > 0 ) {
Packit 1422b7
			fprintf(fp, "%3d: %d\n", i, advstats_pathlens[i]);
Packit 1422b7
			total_len += i * advstats_pathlens[i];
Packit 1422b7
			total_cnt += advstats_pathlens[i];
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	fprintf(fp, "avg: %f\n", (double) total_len / (double) total_cnt);
Packit 1422b7
	fprintf(fp, "max: %d\n", advstats_max_pathlen);
Packit 1422b7
	fprintf(fp, "\n");
Packit 1422b7
Packit 1422b7
	total_len = 0;
Packit 1422b7
	total_cnt = 0;
Packit 1422b7
	fprintf(fp, "Nbr Backtracked\n");
Packit 1422b7
	for(int i = 0 ; i < ADVSTATS_MAX_ENTITIES ; ++i) {
Packit 1422b7
		if(advstats_backtracks[i] > 0 ) {
Packit 1422b7
			fprintf(fp, "%3d: %d\n", i, advstats_backtracks[i]);
Packit 1422b7
			total_len += i * advstats_backtracks[i];
Packit 1422b7
			total_cnt += advstats_backtracks[i];
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	fprintf(fp, "avg: %f\n", (double) total_len / (double) total_cnt);
Packit 1422b7
	fprintf(fp, "max: %d\n", advstats_max_backtracked);
Packit 1422b7
	fprintf(fp, "\n");
Packit 1422b7
Packit 1422b7
	/* we calc some stats while we output */
Packit 1422b7
	total_len = 0;
Packit 1422b7
	total_cnt = 0;
Packit 1422b7
	fprintf(fp, "Parser Calls\n");
Packit 1422b7
	for(int i = 0 ; i < ADVSTATS_MAX_ENTITIES ; ++i) {
Packit 1422b7
		if(advstats_parser_calls[i] > 0 ) {
Packit 1422b7
			fprintf(fp, "%3d: %d\n", i, advstats_parser_calls[i]);
Packit 1422b7
			total_len += i * advstats_parser_calls[i];
Packit 1422b7
			total_cnt += advstats_parser_calls[i];
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	fprintf(fp, "avg: %f\n", (double) total_len / (double) total_cnt);
Packit 1422b7
	fprintf(fp, "max: %d\n", advstats_max_parser_calls);
Packit 1422b7
	fprintf(fp, "\n");
Packit 1422b7
Packit 1422b7
	total_len = 0;
Packit 1422b7
	total_cnt = 0;
Packit 1422b7
	fprintf(fp, "LITERAL Parser Calls\n");
Packit 1422b7
	for(int i = 0 ; i < ADVSTATS_MAX_ENTITIES ; ++i) {
Packit 1422b7
		if(advstats_lit_parser_calls[i] > 0 ) {
Packit 1422b7
			fprintf(fp, "%3d: %d\n", i, advstats_lit_parser_calls[i]);
Packit 1422b7
			total_len += i * advstats_lit_parser_calls[i];
Packit 1422b7
			total_cnt += advstats_lit_parser_calls[i];
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	fprintf(fp, "avg: %f\n", (double) total_len / (double) total_cnt);
Packit 1422b7
	fprintf(fp, "max: %d\n", advstats_max_lit_parser_calls);
Packit 1422b7
	fprintf(fp, "\n");
Packit 1422b7
#endif
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Check if the provided dag is a leaf. This means that it
Packit 1422b7
 * does not contain any subdags.
Packit 1422b7
 * @return 1 if it is a leaf, 0 otherwise
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
isLeaf(struct ln_pdag *dag)
Packit 1422b7
{
Packit 1422b7
	return dag->nparsers == 0 ? 1 : 0;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Add a parser instance to the pdag at the current position.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] ctx
Packit 1422b7
 * @param[in] prscnf json parser config *object* (no array!)
Packit 1422b7
 * @param[in] pdag current pdag position (to which parser is to be added)
Packit 1422b7
 * @param[in/out] nextnode contains point to the next node, either
Packit 1422b7
 *            an existing one or one newly created.
Packit 1422b7
 *
Packit 1422b7
 * The nextnode parameter permits to use this function to create
Packit 1422b7
 * multiple parsers alternative parsers with a single run. To do so,
Packit 1422b7
 * set nextnode=NULL on first call. On successive calls, keep the
Packit 1422b7
 * value. If a value is present, we will not accept non-identical
Packit 1422b7
 * parsers which point to different nodes - this will result in an
Packit 1422b7
 * error.
Packit 1422b7
 *
Packit 1422b7
 * IMPORTANT: the caller is responsible to update its pdag pointer
Packit 1422b7
 *            to the nextnode value when he is done adding parsers.
Packit 1422b7
 *
Packit 1422b7
 * If a parser of the same type with identical data already exists,
Packit 1422b7
 * it is "resued", which means the function is effectively used to
Packit 1422b7
 * walk the path. This is used during parser construction to
Packit 1422b7
 * navigate to new parts of the pdag.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
ln_pdagAddParserInstance(ln_ctx ctx,
Packit 1422b7
	json_object *const __restrict__ prscnf,
Packit 1422b7
	struct ln_pdag *const __restrict__ pdag,
Packit 1422b7
	struct ln_pdag **nextnode)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	ln_parser_t *newtab;
Packit 1422b7
	LN_DBGPRINTF(ctx, "ln_pdagAddParserInstance: %s, nextnode %p",
Packit 1422b7
		json_object_to_json_string(prscnf), *nextnode);
Packit 1422b7
	ln_parser_t *const parser = ln_newParser(ctx, prscnf);
Packit 1422b7
	CHKN(parser);
Packit 1422b7
	LN_DBGPRINTF(ctx, "pdag: %p, parser %p", pdag, parser);
Packit 1422b7
	/* check if we already have this parser, if so, merge
Packit 1422b7
	 */
Packit 1422b7
	int i;
Packit 1422b7
	for(i = 0 ; i < pdag->nparsers ; ++i) {
Packit 1422b7
		LN_DBGPRINTF(ctx, "parser  comparison:\n%s\n%s",  pdag->parsers[i].conf, parser->conf);
Packit 1422b7
		if(   pdag->parsers[i].prsid == parser->prsid
Packit 1422b7
		   && !strcmp(pdag->parsers[i].conf, parser->conf)) {
Packit 1422b7
		   	// FIXME: the current ->conf object is depending on
Packit 1422b7
			//        the order of json elements. We should do a JSON
Packit 1422b7
			//        comparison (a bit more complex). For now, it
Packit 1422b7
			//        works like we do it now.
Packit 1422b7
			// FIXME: if nextnode is set, check we can actually combine,
Packit 1422b7
			//        else err out
Packit 1422b7
			*nextnode = pdag->parsers[i].node;
Packit 1422b7
			r = 0;
Packit 1422b7
			LN_DBGPRINTF(ctx, "merging with pdag %p", pdag);
Packit 1422b7
			pdagDeletePrs(ctx, parser); /* no need for data items */
Packit 1422b7
			goto done;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	/* if we reach this point, we have a new parser type */
Packit 1422b7
	if(*nextnode == NULL) {
Packit 1422b7
		CHKN(*nextnode = ln_newPDAG(ctx)); /* we need a new node */
Packit 1422b7
	} else {
Packit 1422b7
		(*nextnode)->refcnt++;
Packit 1422b7
	}
Packit 1422b7
	parser->node = *nextnode;
Packit 1422b7
	newtab = realloc(pdag->parsers, (pdag->nparsers+1) * sizeof(ln_parser_t));
Packit 1422b7
	CHKN(newtab);
Packit 1422b7
	pdag->parsers = newtab;
Packit 1422b7
	memcpy(pdag->parsers+pdag->nparsers, parser, sizeof(ln_parser_t));
Packit 1422b7
	pdag->nparsers++;
Packit 1422b7
Packit 1422b7
	r = 0;
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	free(parser);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static int ln_pdagAddParserInternal(ln_ctx ctx, struct ln_pdag **pdag, const int mode, json_object *const prscnf,
Packit 1422b7
struct ln_pdag **nextnode);
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * add parsers to current pdag. This is used
Packit 1422b7
 * to add parsers stored in an array. The mode specifies
Packit 1422b7
 * how parsers shall be added.
Packit 1422b7
 */
Packit 1422b7
#define PRS_ADD_MODE_SEQ 0
Packit 1422b7
#define PRS_ADD_MODE_ALTERNATIVE 1
Packit 1422b7
static int
Packit 1422b7
ln_pdagAddParsers(ln_ctx ctx,
Packit 1422b7
	json_object *const prscnf,
Packit 1422b7
	const int mode,
Packit 1422b7
	struct ln_pdag **pdag,
Packit 1422b7
	struct ln_pdag **p_nextnode)
Packit 1422b7
{
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	struct ln_pdag *dag = *pdag;
Packit 1422b7
	struct ln_pdag *nextnode = *p_nextnode;
Packit 1422b7
	
Packit 1422b7
	const int lenarr = json_object_array_length(prscnf);
Packit 1422b7
	for(int i = 0 ; i < lenarr ; ++i) {
Packit 1422b7
		struct json_object *const curr_prscnf =
Packit 1422b7
			json_object_array_get_idx(prscnf, i);
Packit 1422b7
		LN_DBGPRINTF(ctx, "parser %d: %s", i, json_object_to_json_string(curr_prscnf));
Packit 1422b7
		if(json_object_get_type(curr_prscnf) == json_type_array) {
Packit 1422b7
			struct ln_pdag *local_dag = dag;
Packit 1422b7
			CHKR(ln_pdagAddParserInternal(ctx, &local_dag, mode,
Packit 1422b7
						      curr_prscnf, &nextnode));
Packit 1422b7
			if(mode == PRS_ADD_MODE_SEQ) {
Packit 1422b7
				dag = local_dag;
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			CHKR(ln_pdagAddParserInstance(ctx, curr_prscnf, dag, &nextnode));
Packit 1422b7
		}
Packit 1422b7
		if(mode == PRS_ADD_MODE_SEQ) {
Packit 1422b7
			dag = nextnode;
Packit 1422b7
			*p_nextnode = nextnode;
Packit 1422b7
			nextnode = NULL;
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(mode != PRS_ADD_MODE_SEQ)
Packit 1422b7
		dag = nextnode;
Packit 1422b7
	*pdag = dag;
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* add a json parser config object. Note that this object may contain
Packit 1422b7
 * multiple parser instances. Additionally, moves the pdag object to
Packit 1422b7
 * the next node, which is either newly created or previously existed.
Packit 1422b7
 */
Packit 1422b7
static int
Packit 1422b7
ln_pdagAddParserInternal(ln_ctx ctx, struct ln_pdag **pdag,
Packit 1422b7
	const int mode, json_object *const prscnf, struct ln_pdag **nextnode)
Packit 1422b7
{
Packit 1422b7
	int r = LN_BADCONFIG;
Packit 1422b7
	struct ln_pdag *dag = *pdag;
Packit 1422b7
	
Packit 1422b7
	LN_DBGPRINTF(ctx, "ln_pdagAddParserInternal: %s", json_object_to_json_string(prscnf));
Packit 1422b7
	if(json_object_get_type(prscnf) == json_type_object) {
Packit 1422b7
		/* check for special types we need to handle here */
Packit 1422b7
		struct json_object *json;
Packit 1422b7
		json_object_object_get_ex(prscnf, "type", &json);
Packit 1422b7
		const char *const ftype = json_object_get_string(json);
Packit 1422b7
		if(!strcmp(ftype, "alternative")) {
Packit 1422b7
			json_object_object_get_ex(prscnf, "parser", &json);
Packit 1422b7
			if(json_object_get_type(json) != json_type_array) {
Packit 1422b7
				ln_errprintf(ctx, 0, "alternative type needs array of parsers. "
Packit 1422b7
					"Object: '%s', type is %s",
Packit 1422b7
					json_object_to_json_string(prscnf),
Packit 1422b7
					json_type_to_name(json_object_get_type(json)));
Packit 1422b7
				goto done;
Packit 1422b7
			}
Packit 1422b7
			CHKR(ln_pdagAddParsers(ctx, json, PRS_ADD_MODE_ALTERNATIVE, &dag, nextnode));
Packit 1422b7
		} else {
Packit 1422b7
			CHKR(ln_pdagAddParserInstance(ctx, prscnf, dag, nextnode));
Packit 1422b7
			if(mode == PRS_ADD_MODE_SEQ)
Packit 1422b7
				dag = *nextnode;
Packit 1422b7
		}
Packit 1422b7
	} else if(json_object_get_type(prscnf) == json_type_array) {
Packit 1422b7
		CHKR(ln_pdagAddParsers(ctx, prscnf, PRS_ADD_MODE_SEQ, &dag, nextnode));
Packit 1422b7
	} else {
Packit 1422b7
		ln_errprintf(ctx, 0, "bug: prscnf object of wrong type. Object: '%s'",
Packit 1422b7
			json_object_to_json_string(prscnf));
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	*pdag = dag;
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* add a json parser config object. Note that this object may contain
Packit 1422b7
 * multiple parser instances. Additionally, moves the pdag object to
Packit 1422b7
 * the next node, which is either newly created or previously existed.
Packit 1422b7
 */
Packit 1422b7
int
Packit 1422b7
ln_pdagAddParser(ln_ctx ctx, struct ln_pdag **pdag, json_object *const prscnf)
Packit 1422b7
{
Packit 1422b7
	struct ln_pdag *nextnode = NULL;
Packit 1422b7
	int r = ln_pdagAddParserInternal(ctx, pdag, PRS_ADD_MODE_SEQ, prscnf, &nextnode);
Packit 1422b7
	json_object_put(prscnf);
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_displayPDAGComponent(struct ln_pdag *dag, int level)
Packit 1422b7
{
Packit 1422b7
	char indent[2048];
Packit 1422b7
Packit 1422b7
	if(level > 1023)
Packit 1422b7
		level = 1023;
Packit 1422b7
	memset(indent, ' ', level * 2);
Packit 1422b7
	indent[level * 2] = '\0';
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(dag->ctx, "%ssubDAG%s %p (children: %d parsers, ref %d) [called %u, backtracked %u]",
Packit 1422b7
		     indent, dag->flags.isTerminal ? " [TERM]" : "", dag, dag->nparsers, dag->refcnt,
Packit 1422b7
		     dag->stats.called, dag->stats.backtracked);
Packit 1422b7
Packit 1422b7
for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
	ln_parser_t *const prs = dag->parsers+i;
Packit 1422b7
	LN_DBGPRINTF(dag->ctx, "%sfield type '%s', name '%s': '%s': called %u", indent,
Packit 1422b7
		parserName(prs->prsid),
Packit 1422b7
		dag->parsers[i].name,
Packit 1422b7
		(prs->prsid == PRS_LITERAL) ?  ln_DataForDisplayLiteral(dag->ctx, prs->parser_data) : "UNKNOWN",
Packit 1422b7
	dag->parsers[i].node->stats.called);
Packit 1422b7
}
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *const prs = dag->parsers+i;
Packit 1422b7
		LN_DBGPRINTF(dag->ctx, "%sfield type '%s', name '%s': '%s':", indent,
Packit 1422b7
			parserName(prs->prsid),
Packit 1422b7
			dag->parsers[i].name,
Packit 1422b7
			(prs->prsid == PRS_LITERAL) ?  ln_DataForDisplayLiteral(dag->ctx, prs->parser_data) :
Packit 1422b7
				"UNKNOWN");
Packit 1422b7
		if(prs->prsid == PRS_REPEAT) {
Packit 1422b7
			struct data_Repeat *const data = (struct data_Repeat*) prs->parser_data;
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "%sparser:", indent);
Packit 1422b7
			ln_displayPDAGComponent(data->parser, level + 1);
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "%swhile:", indent);
Packit 1422b7
			ln_displayPDAGComponent(data->while_cond, level + 1);
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "%send repeat def", indent);
Packit 1422b7
		}
Packit 1422b7
		ln_displayPDAGComponent(dag->parsers[i].node, level + 1);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
void ln_displayPDAGComponentAlternative(struct ln_pdag *dag, int level)
Packit 1422b7
{
Packit 1422b7
	char indent[2048];
Packit 1422b7
Packit 1422b7
	if(level > 1023)
Packit 1422b7
		level = 1023;
Packit 1422b7
	memset(indent, ' ', level * 2);
Packit 1422b7
	indent[level * 2] = '\0';
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(dag->ctx, "%s%p[ref %d]: %s", indent, dag, dag->refcnt, dag->rb_id);
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_displayPDAGComponentAlternative(dag->parsers[i].node, level + 1);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* developer debug aid, to be used for example as follows:
Packit 1422b7
 * LN_DBGPRINTF(dag->ctx, "---------------------------------------");
Packit 1422b7
 * ln_displayPDAG(dag);
Packit 1422b7
 * LN_DBGPRINTF(dag->ctx, "=======================================");
Packit 1422b7
 */
Packit 1422b7
void
Packit 1422b7
ln_displayPDAG(ln_ctx ctx)
Packit 1422b7
{
Packit 1422b7
	ln_pdagClearVisited(ctx);
Packit 1422b7
	for(int i = 0 ; i < ctx->nTypes ; ++i) {
Packit 1422b7
		LN_DBGPRINTF(ctx, "COMPONENT: %s", ctx->type_pdags[i].name);
Packit 1422b7
		ln_displayPDAGComponent(ctx->type_pdags[i].pdag, 0);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(ctx, "MAIN COMPONENT:");
Packit 1422b7
	ln_displayPDAGComponent(ctx->pdag, 0);
Packit 1422b7
Packit 1422b7
	LN_DBGPRINTF(ctx, "MAIN COMPONENT (alternative):");
Packit 1422b7
	ln_displayPDAGComponentAlternative(ctx->pdag, 0);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* the following is a quick hack, which should be moved to the
Packit 1422b7
 * string class.
Packit 1422b7
 */
Packit 1422b7
static inline void dotAddPtr(es_str_t **str, void *p)
Packit 1422b7
{
Packit 1422b7
	char buf[64];
Packit 1422b7
	int i;
Packit 1422b7
	i = snprintf(buf, sizeof(buf), "l%p", p);
Packit 1422b7
	es_addBuf(str, buf, i);
Packit 1422b7
}
Packit 1422b7
struct data_Literal { const char *lit; }; // TODO remove when this hack is no longe needed
Packit 1422b7
/**
Packit 1422b7
 * recursive handler for DOT graph generator.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
ln_genDotPDAGGraphRec(struct ln_pdag *dag, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	char s_refcnt[16];
Packit 1422b7
	LN_DBGPRINTF(dag->ctx, "in dot: %p, visited %d", dag, (int) dag->flags.visited);
Packit 1422b7
	if(dag->flags.visited)
Packit 1422b7
		return; /* already processed this subpart */
Packit 1422b7
	dag->flags.visited = 1;
Packit 1422b7
	dotAddPtr(str, dag);
Packit 1422b7
	snprintf(s_refcnt, sizeof(s_refcnt), "%d", dag->refcnt);
Packit 1422b7
	s_refcnt[sizeof(s_refcnt)-1] = '\0';
Packit 1422b7
	es_addBufConstcstr(str, " [ label=\"");
Packit 1422b7
	es_addBuf(str, s_refcnt, strlen(s_refcnt));
Packit 1422b7
	es_addBufConstcstr(str, "\"");
Packit 1422b7
Packit 1422b7
	if(isLeaf(dag)) {
Packit 1422b7
		es_addBufConstcstr(str, " style=\"bold\"");
Packit 1422b7
	}
Packit 1422b7
	es_addBufConstcstr(str, "]\n");
Packit 1422b7
Packit 1422b7
	/* display field subdags */
Packit 1422b7
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *const prs = dag->parsers+i;
Packit 1422b7
		dotAddPtr(str, dag);
Packit 1422b7
		es_addBufConstcstr(str, " -> ");
Packit 1422b7
		dotAddPtr(str, prs->node);
Packit 1422b7
		es_addBufConstcstr(str, " [label=\"");
Packit 1422b7
		es_addBuf(str, parserName(prs->prsid), strlen(parserName(prs->prsid)));
Packit 1422b7
		es_addBufConstcstr(str, ":");
Packit 1422b7
		//es_addStr(str, node->name);
Packit 1422b7
		if(prs->prsid == PRS_LITERAL) {
Packit 1422b7
			for(const char *p = ((struct data_Literal*)prs->parser_data)->lit ; *p ; ++p) {
Packit 1422b7
				// TODO: handle! if(*p == '\\')
Packit 1422b7
					//es_addChar(str, '\\');
Packit 1422b7
				if(*p != '\\' && *p != '"')
Packit 1422b7
					es_addChar(str, *p);
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		es_addBufConstcstr(str, "\"");
Packit 1422b7
		es_addBufConstcstr(str, " style=\"dotted\"]\n");
Packit 1422b7
		ln_genDotPDAGGraphRec(prs->node, str);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_genDotPDAGGraph(struct ln_pdag *dag, es_str_t **str)
Packit 1422b7
{
Packit 1422b7
	ln_pdagClearVisited(dag->ctx);
Packit 1422b7
	es_addBufConstcstr(str, "digraph pdag {\n");
Packit 1422b7
	ln_genDotPDAGGraphRec(dag, str);
Packit 1422b7
	es_addBufConstcstr(str, "}\n");
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * recursive handler for statistics DOT graph generator.
Packit 1422b7
 */
Packit 1422b7
static void
Packit 1422b7
ln_genStatsDotPDAGGraphRec(struct ln_pdag *dag, FILE *const __restrict__ fp)
Packit 1422b7
{
Packit 1422b7
	if(dag->flags.visited)
Packit 1422b7
		return; /* already processed this subpart */
Packit 1422b7
	dag->flags.visited = 1;
Packit 1422b7
	fprintf(fp, "l%p [ label=\"%u:%u\"", dag,
Packit 1422b7
		dag->stats.called, dag->stats.backtracked);
Packit 1422b7
Packit 1422b7
	if(isLeaf(dag)) {
Packit 1422b7
		fprintf(fp, " style=\"bold\"");
Packit 1422b7
	}
Packit 1422b7
	fprintf(fp, "]\n");
Packit 1422b7
Packit 1422b7
	/* display field subdags */
Packit 1422b7
Packit 1422b7
	for(int i = 0 ; i < dag->nparsers ; ++i) {
Packit 1422b7
		ln_parser_t *const prs = dag->parsers+i;
Packit 1422b7
		if(prs->node->stats.called == 0)
Packit 1422b7
			continue;
Packit 1422b7
		fprintf(fp, "l%p -> l%p [label=\"", dag, prs->node);
Packit 1422b7
		if(prs->prsid == PRS_LITERAL) {
Packit 1422b7
			for(const char *p = ((struct data_Literal*)prs->parser_data)->lit ; *p ; ++p) {
Packit 1422b7
				if(*p != '\\' && *p != '"')
Packit 1422b7
					fputc(*p, fp);
Packit 1422b7
			}
Packit 1422b7
		} else {
Packit 1422b7
			fprintf(fp, "%s", parserName(prs->prsid));
Packit 1422b7
		}
Packit 1422b7
		fprintf(fp, "\" style=\"dotted\"]\n");
Packit 1422b7
		ln_genStatsDotPDAGGraphRec(prs->node, fp);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
static void
Packit 1422b7
ln_genStatsDotPDAGGraph(struct ln_pdag *dag, FILE *const fp)
Packit 1422b7
{
Packit 1422b7
	ln_pdagClearVisited(dag->ctx);
Packit 1422b7
	fprintf(fp, "digraph pdag {\n");
Packit 1422b7
	ln_genStatsDotPDAGGraphRec(dag, fp);
Packit 1422b7
	fprintf(fp, "}\n");
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
void
Packit 1422b7
ln_fullPDagStatsDOT(ln_ctx ctx, FILE *const fp)
Packit 1422b7
{
Packit 1422b7
	ln_genStatsDotPDAGGraph(ctx->pdag, fp);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
static inline int
Packit 1422b7
addOriginalMsg(const char *str, const size_t strLen, struct json_object *const json)
Packit 1422b7
{
Packit 1422b7
	int r = 1;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
Packit 1422b7
	value = json_object_new_string_len(str, strLen);
Packit 1422b7
	if (value == NULL) {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	json_object_object_add(json, ORIGINAL_MSG_KEY, value);
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
static char *
Packit 1422b7
strrev(char *const __restrict__ str)
Packit 1422b7
{
Packit 1422b7
	char ch;
Packit 1422b7
	size_t i = strlen(str)-1,j=0;
Packit 1422b7
	while(i>j)
Packit 1422b7
	{
Packit 1422b7
		ch = str[i];
Packit 1422b7
		str[i]= str[j];
Packit 1422b7
		str[j] = ch;
Packit 1422b7
		i--;
Packit 1422b7
		j++;
Packit 1422b7
	}
Packit 1422b7
	return str;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/* note: "originalmsg" is NOT added as metadata in order to keep
Packit 1422b7
 * backwards compatible.
Packit 1422b7
 */
Packit 1422b7
static inline void
Packit 1422b7
addRuleMetadata(npb_t *const __restrict__ npb,
Packit 1422b7
	struct json_object *const json,
Packit 1422b7
	struct ln_pdag *const __restrict__ endNode)
Packit 1422b7
{
Packit 1422b7
	ln_ctx ctx = npb->ctx;
Packit 1422b7
	struct json_object *meta = NULL;
Packit 1422b7
	struct json_object *meta_rule = NULL;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
Packit 1422b7
	if(ctx->opts & LN_CTXOPT_ADD_RULE) { /* matching rule mockup */
Packit 1422b7
		if(meta_rule == NULL)
Packit 1422b7
			meta_rule = json_object_new_object();
Packit 1422b7
		char *cstr = strrev(es_str2cstr(npb->rule, NULL));
Packit 1422b7
		json_object_object_add(meta_rule, RULE_MOCKUP_KEY,
Packit 1422b7
			json_object_new_string(cstr));
Packit 1422b7
		free(cstr);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(ctx->opts & LN_CTXOPT_ADD_RULE_LOCATION) {
Packit 1422b7
		if(meta_rule == NULL)
Packit 1422b7
			meta_rule = json_object_new_object();
Packit 1422b7
		struct json_object *const location = json_object_new_object();
Packit 1422b7
		value = json_object_new_string(endNode->rb_file);
Packit 1422b7
		json_object_object_add(location, "file", value);
Packit 1422b7
		value = json_object_new_int((int)endNode->rb_lineno);
Packit 1422b7
		json_object_object_add(location, "line", value);
Packit 1422b7
		json_object_object_add(meta_rule, RULE_LOCATION_KEY, location);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(meta_rule != NULL) {
Packit 1422b7
		if(meta == NULL)
Packit 1422b7
			meta = json_object_new_object();
Packit 1422b7
		json_object_object_add(meta, META_RULE_KEY, meta_rule);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
#ifdef	ADVANCED_STATS
Packit 1422b7
	/* complete execution path */
Packit 1422b7
	if(ctx->opts & LN_CTXOPT_ADD_EXEC_PATH) {
Packit 1422b7
		if(meta == NULL)
Packit 1422b7
			meta = json_object_new_object();
Packit 1422b7
		char hdr[128];
Packit 1422b7
		const size_t lenhdr
Packit 1422b7
		  = snprintf(hdr, sizeof(hdr), "[PATHLEN:%d, PARSER CALLS gen:%d, literal:%d]",
Packit 1422b7
			     npb->astats.pathlen, npb->astats.parser_calls,
Packit 1422b7
			     npb->astats.lit_parser_calls);
Packit 1422b7
		es_addBuf(&npb->astats.exec_path, hdr, lenhdr);
Packit 1422b7
		char * cstr = es_str2cstr(npb->astats.exec_path, NULL);
Packit 1422b7
		value = json_object_new_string(cstr);
Packit 1422b7
		if (value != NULL) {
Packit 1422b7
			json_object_object_add(meta, EXEC_PATH_KEY, value);
Packit 1422b7
		}
Packit 1422b7
		free(cstr);
Packit 1422b7
	}
Packit 1422b7
#endif
Packit 1422b7
Packit 1422b7
	if(meta != NULL)
Packit 1422b7
		json_object_object_add(json, META_KEY, meta);
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * add unparsed string to event.
Packit 1422b7
 */
Packit 1422b7
static inline int
Packit 1422b7
addUnparsedField(const char *str, const size_t strLen, const size_t offs, struct json_object *json)
Packit 1422b7
{
Packit 1422b7
	int r = 1;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
Packit 1422b7
	CHKR(addOriginalMsg(str, strLen, json));
Packit 1422b7
	
Packit 1422b7
	value = json_object_new_string(str + offs);
Packit 1422b7
	if (value == NULL) {
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	json_object_object_add(json, UNPARSED_DATA_KEY, value);
Packit 1422b7
Packit 1422b7
	r = 0;
Packit 1422b7
done:
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* Do some fixup to the json that we cannot do on a lower layer */
Packit 1422b7
static int
Packit 1422b7
fixJSON(struct ln_pdag *dag,
Packit 1422b7
	struct json_object **value,
Packit 1422b7
	struct json_object *json,
Packit 1422b7
	const ln_parser_t *const prs)
Packit 1422b7
Packit 1422b7
{
Packit 1422b7
	int r = LN_WRONGPARSER;
Packit 1422b7
Packit 1422b7
	if(prs->name ==  NULL) {
Packit 1422b7
		if (*value != NULL) {
Packit 1422b7
			/* Free the unneeded value */
Packit 1422b7
			json_object_put(*value);
Packit 1422b7
		}
Packit 1422b7
	} else if(prs->name[0] == '.' && prs->name[1] == '\0') {
Packit 1422b7
		if(json_object_get_type(*value) == json_type_object) {
Packit 1422b7
			struct json_object_iterator it = json_object_iter_begin(*value);
Packit 1422b7
			struct json_object_iterator itEnd = json_object_iter_end(*value);
Packit 1422b7
			while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
				struct json_object *const val = json_object_iter_peek_value(&it);
Packit 1422b7
				json_object_get(val);
Packit 1422b7
				json_object_object_add(json, json_object_iter_peek_name(&it), val);
Packit 1422b7
				json_object_iter_next(&it);
Packit 1422b7
			}
Packit 1422b7
			json_object_put(*value);
Packit 1422b7
		} else {
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "field name is '.', but json type is %s",
Packit 1422b7
				json_type_to_name(json_object_get_type(*value)));
Packit 1422b7
			json_object_object_add_ex(json, prs->name, *value,
Packit 1422b7
				JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
		}
Packit 1422b7
	} else {
Packit 1422b7
		int isDotDot = 0;
Packit 1422b7
		struct json_object *valDotDot = NULL;
Packit 1422b7
		if(json_object_get_type(*value) == json_type_object) {
Packit 1422b7
			/* TODO: this needs to be speeded up by just checking the first
Packit 1422b7
			 * member and ensuring there is only one member. This requires
Packit 1422b7
			 * extensions to libfastjson.
Packit 1422b7
			 */
Packit 1422b7
			int nSubobj = 0;
Packit 1422b7
			struct json_object_iterator it = json_object_iter_begin(*value);
Packit 1422b7
			struct json_object_iterator itEnd = json_object_iter_end(*value);
Packit 1422b7
			while (!json_object_iter_equal(&it, &itEnd)) {
Packit 1422b7
				++nSubobj;
Packit 1422b7
				const char *key = json_object_iter_peek_name(&it);
Packit 1422b7
				if(key[0] == '.' && key[1] == '.' && key[2] == '\0') {
Packit 1422b7
					isDotDot = 1;
Packit 1422b7
					valDotDot = json_object_iter_peek_value(&it);
Packit 1422b7
				} else {
Packit 1422b7
					isDotDot = 0;
Packit 1422b7
				}
Packit 1422b7
				json_object_iter_next(&it);
Packit 1422b7
			}
Packit 1422b7
			if(nSubobj != 1)
Packit 1422b7
				isDotDot = 0;
Packit 1422b7
		}
Packit 1422b7
		if(isDotDot) {
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "subordinate field name is '..', combining");
Packit 1422b7
			json_object_get(valDotDot);
Packit 1422b7
			json_object_put(*value);
Packit 1422b7
			json_object_object_add_ex(json, prs->name, valDotDot,
Packit 1422b7
				JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
		} else {
Packit 1422b7
			json_object_object_add_ex(json, prs->name, *value,
Packit 1422b7
				JSON_C_OBJECT_ADD_KEY_IS_NEW|JSON_C_OBJECT_KEY_IS_CONSTANT);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	r = 0;
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
// TODO: streamline prototype when done with changes
Packit 1422b7
Packit 1422b7
static int
Packit 1422b7
tryParser(npb_t *const __restrict__ npb,
Packit 1422b7
	struct ln_pdag *dag,
Packit 1422b7
	size_t *offs,
Packit 1422b7
	size_t *const __restrict__ pParsed,
Packit 1422b7
	struct json_object **value,
Packit 1422b7
	const ln_parser_t *const prs
Packit 1422b7
	)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	struct ln_pdag *endNode = NULL;
Packit 1422b7
	size_t parsedTo = npb->parsedTo;
Packit 1422b7
#	ifdef	ADVANCED_STATS
Packit 1422b7
	char hdr[16];
Packit 1422b7
	const size_t lenhdr
Packit 1422b7
	  = snprintf(hdr, sizeof(hdr), "%d:", npb->astats.recursion_level);
Packit 1422b7
	es_addBuf(&npb->astats.exec_path, hdr, lenhdr);
Packit 1422b7
	if(prs->prsid == PRS_LITERAL) {
Packit 1422b7
		es_addChar(&npb->astats.exec_path, '\'');
Packit 1422b7
		es_addBuf(&npb->astats.exec_path,
Packit 1422b7
			  ln_DataForDisplayLiteral(dag->ctx,
Packit 1422b7
				prs->parser_data),
Packit 1422b7
			  strlen(ln_DataForDisplayLiteral(dag->ctx,
Packit 1422b7
				prs->parser_data))
Packit 1422b7
			 );
Packit 1422b7
		es_addChar(&npb->astats.exec_path, '\'');
Packit 1422b7
	} else if(parser_lookup_table[prs->prsid].parser
Packit 1422b7
			== ln_v2_parseCharTo) {
Packit 1422b7
		es_addBuf(&npb->astats.exec_path,
Packit 1422b7
			  ln_DataForDisplayCharTo(dag->ctx,
Packit 1422b7
				prs->parser_data),
Packit 1422b7
			  strlen(ln_DataForDisplayCharTo(dag->ctx,
Packit 1422b7
				prs->parser_data))
Packit 1422b7
			 );
Packit 1422b7
	} else {
Packit 1422b7
		es_addBuf(&npb->astats.exec_path,
Packit 1422b7
			parserName(prs->prsid),
Packit 1422b7
			strlen(parserName(prs->prsid)) );
Packit 1422b7
	}
Packit 1422b7
	es_addChar(&npb->astats.exec_path, ',');
Packit 1422b7
#	endif
Packit 1422b7
Packit 1422b7
	if(prs->prsid == PRS_CUSTOM_TYPE) {
Packit 1422b7
		if(*value == NULL)
Packit 1422b7
			*value = json_object_new_object();
Packit 1422b7
		LN_DBGPRINTF(dag->ctx, "calling custom parser '%s'", prs->custType->name);
Packit 1422b7
		r = ln_normalizeRec(npb, prs->custType->pdag, *offs, 1, *value, &endNode);
Packit 1422b7
		LN_DBGPRINTF(dag->ctx, "called CUSTOM PARSER '%s', result %d, "
Packit 1422b7
			"offs %zd, *pParsed %zd", prs->custType->name, r, *offs, *pParsed);
Packit 1422b7
		*pParsed = npb->parsedTo - *offs;
Packit 1422b7
		#ifdef	ADVANCED_STATS
Packit 1422b7
		es_addBuf(&npb->astats.exec_path, hdr, lenhdr);
Packit 1422b7
		es_addBuf(&npb->astats.exec_path, "[R:USR],", 8);
Packit 1422b7
		#endif
Packit 1422b7
	} else {
Packit 1422b7
		r = parser_lookup_table[prs->prsid].parser(npb,
Packit 1422b7
			offs, prs->parser_data, pParsed, (prs->name == NULL) ? NULL : value);
Packit 1422b7
	}
Packit 1422b7
	LN_DBGPRINTF(npb->ctx, "parser lookup returns %d, pParsed %zu", r, *pParsed);
Packit 1422b7
	npb->parsedTo = parsedTo;
Packit 1422b7
Packit 1422b7
#ifdef	ADVANCED_STATS
Packit 1422b7
	++advstats_parsers_called;
Packit 1422b7
	++npb->astats.parser_calls;
Packit 1422b7
	if(prs->prsid == PRS_LITERAL)
Packit 1422b7
		++npb->astats.lit_parser_calls;
Packit 1422b7
	if(r == 0)
Packit 1422b7
		++advstats_parsers_success;
Packit 1422b7
	if(prs->prsid != PRS_CUSTOM_TYPE) {
Packit 1422b7
		++parser_lookup_table[prs->prsid].called;
Packit 1422b7
		if(r == 0)
Packit 1422b7
			++parser_lookup_table[prs->prsid].success;
Packit 1422b7
	}
Packit 1422b7
#endif
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
static void
Packit 1422b7
add_str_reversed(npb_t *const __restrict__ npb,
Packit 1422b7
	const char *const __restrict__ str,
Packit 1422b7
	const size_t len)
Packit 1422b7
{
Packit 1422b7
	ssize_t i;
Packit 1422b7
	for(i = len - 1 ; i >= 0 ; --i) {
Packit 1422b7
		es_addChar(&npb->rule, str[i]);
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
Packit 1422b7
/* Add the current parser to the mockup rule.
Packit 1422b7
 * Note: we add reversed strings, because we can call this
Packit 1422b7
 * function effectively only when walking upwards the tree.
Packit 1422b7
 * This means deepest entries come first. We solve this somewhat
Packit 1422b7
 * elegantly by reversion strings, and then reversion the string
Packit 1422b7
 * once more when we emit it, so that we get the right order.
Packit 1422b7
 */
Packit 1422b7
static inline void
Packit 1422b7
add_rule_to_mockup(npb_t *const __restrict__ npb,
Packit 1422b7
	const ln_parser_t *const __restrict__ prs)
Packit 1422b7
{
Packit 1422b7
	if(prs->prsid == PRS_LITERAL) {
Packit 1422b7
		const char *const val =
Packit 1422b7
			  ln_DataForDisplayLiteral(npb->ctx,
Packit 1422b7
				prs->parser_data);
Packit 1422b7
		add_str_reversed(npb, val, strlen(val));
Packit 1422b7
	} else {
Packit 1422b7
		/* note: name/value order must also be reversed! */
Packit 1422b7
		es_addChar(&npb->rule, '%');
Packit 1422b7
		add_str_reversed(npb,
Packit 1422b7
			parserName(prs->prsid),
Packit 1422b7
			strlen(parserName(prs->prsid)) );
Packit 1422b7
		es_addChar(&npb->rule, ':');
Packit 1422b7
		if(prs->name == NULL) {
Packit 1422b7
			es_addChar(&npb->rule, '-');
Packit 1422b7
		} else {
Packit 1422b7
			add_str_reversed(npb, prs->name, strlen(prs->name));
Packit 1422b7
		}
Packit 1422b7
		es_addChar(&npb->rule, '%');
Packit 1422b7
	}
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
/**
Packit 1422b7
 * Recursive step of the normalizer. It walks the parse dag and calls itself
Packit 1422b7
 * recursively when this is appropriate. It also implements backtracking in
Packit 1422b7
 * those (hopefully rare) cases where it is required.
Packit 1422b7
 *
Packit 1422b7
 * @param[in] dag current tree to process
Packit 1422b7
 * @param[in] string string to be matched against (the to-be-normalized data)
Packit 1422b7
 * @param[in] strLen length of the to-be-matched string
Packit 1422b7
 * @param[in] offs start position in input data
Packit 1422b7
 * @param[out] pPrasedTo ptr to position up to which the the parsing succed in max
Packit 1422b7
 * @param[in/out] json ... that is being created during normalization
Packit 1422b7
 * @param[out] endNode if a match was found, this is the matching node (undefined otherwise)
Packit 1422b7
 *
Packit 1422b7
 * @return regular liblognorm error code (0->OK, something else->error)
Packit 1422b7
 * TODO: can we use parameter block to prevent pushing params to the stack?
Packit 1422b7
 */
Packit 1422b7
int
Packit 1422b7
ln_normalizeRec(npb_t *const __restrict__ npb,
Packit 1422b7
	struct ln_pdag *dag,
Packit 1422b7
	const size_t offs,
Packit 1422b7
	const int bPartialMatch,
Packit 1422b7
	struct json_object *json,
Packit 1422b7
	struct ln_pdag **endNode
Packit 1422b7
	)
Packit 1422b7
{
Packit 1422b7
	int r = LN_WRONGPARSER;
Packit 1422b7
	int localR;
Packit 1422b7
	size_t i;
Packit 1422b7
	size_t iprs;
Packit 1422b7
	size_t parsedTo = npb->parsedTo;
Packit 1422b7
	size_t parsed = 0;
Packit 1422b7
	struct json_object *value;
Packit 1422b7
	
Packit 1422b7
LN_DBGPRINTF(dag->ctx, "%zu: enter parser, dag node %p, json %p", offs, dag, json);
Packit 1422b7
Packit 1422b7
	++dag->stats.called;
Packit 1422b7
#ifdef	ADVANCED_STATS
Packit 1422b7
	++npb->astats.pathlen;
Packit 1422b7
	++npb->astats.recursion_level;
Packit 1422b7
#endif
Packit 1422b7
Packit 1422b7
	/* now try the parsers */
Packit 1422b7
	for(iprs = 0 ; iprs < dag->nparsers && r != 0 ; ++iprs) {
Packit 1422b7
		const ln_parser_t *const prs = dag->parsers + iprs;
Packit 1422b7
		if(dag->ctx->debug) {
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "%zu/%d:trying '%s' parser for field '%s', "
Packit 1422b7
				     "data '%s'",
Packit 1422b7
					offs, bPartialMatch, parserName(prs->prsid), prs->name,
Packit 1422b7
					(prs->prsid == PRS_LITERAL)
Packit 1422b7
					 ? ln_DataForDisplayLiteral(dag->ctx, prs->parser_data)
Packit 1422b7
				 	 : "UNKNOWN");
Packit 1422b7
		}
Packit 1422b7
		i = offs;
Packit 1422b7
		value = NULL;
Packit 1422b7
		localR = tryParser(npb, dag, &i, &parsed, &value, prs);
Packit 1422b7
		if(localR == 0) {
Packit 1422b7
			parsedTo = i + parsed;
Packit 1422b7
			/* potential hit, need to verify */
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "%zu: potential hit, trying subtree %p",
Packit 1422b7
				offs, prs->node);
Packit 1422b7
			r = ln_normalizeRec(npb, prs->node, parsedTo,
Packit 1422b7
					    bPartialMatch, json, endNode);
Packit 1422b7
			LN_DBGPRINTF(dag->ctx, "%zu: subtree returns %d, parsedTo %zu", offs, r, parsedTo);
Packit 1422b7
			if(r == 0) {
Packit 1422b7
				LN_DBGPRINTF(dag->ctx, "%zu: parser matches at %zu", offs, i);
Packit 1422b7
				CHKR(fixJSON(dag, &value, json, prs));
Packit 1422b7
				if(npb->ctx->opts & LN_CTXOPT_ADD_RULE) {
Packit 1422b7
					add_rule_to_mockup(npb, prs);
Packit 1422b7
				}
Packit 1422b7
			} else {
Packit 1422b7
				++dag->stats.backtracked;
Packit 1422b7
				#ifdef	ADVANCED_STATS
Packit 1422b7
					++npb->astats.backtracked;
Packit 1422b7
					es_addBuf(&npb->astats.exec_path, "[B]", 3);
Packit 1422b7
				#endif
Packit 1422b7
				LN_DBGPRINTF(dag->ctx, "%zu nonmatch, backtracking required, parsed to=%zu",
Packit 1422b7
						offs, parsedTo);
Packit 1422b7
				if (value != NULL) { /* Free the value if it was created */
Packit 1422b7
					json_object_put(value);
Packit 1422b7
				}
Packit 1422b7
			}
Packit 1422b7
		}
Packit 1422b7
		/* did we have a longer parser --> then update */
Packit 1422b7
		if(parsedTo > npb->parsedTo)
Packit 1422b7
			npb->parsedTo = parsedTo;
Packit 1422b7
		LN_DBGPRINTF(dag->ctx, "parsedTo %zu, *pParsedTo %zu", parsedTo, npb->parsedTo);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
LN_DBGPRINTF(dag->ctx, "offs %zu, strLen %zu, isTerm %d", offs, npb->strLen, dag->flags.isTerminal);
Packit 1422b7
	if(dag->flags.isTerminal && (offs == npb->strLen || bPartialMatch)) {
Packit 1422b7
		*endNode = dag;
Packit 1422b7
		r = 0;
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
done:
Packit 1422b7
	LN_DBGPRINTF(dag->ctx, "%zu returns %d, pParsedTo %zu, parsedTo %zu",
Packit 1422b7
		offs, r, npb->parsedTo, parsedTo);
Packit 1422b7
#	ifdef	ADVANCED_STATS
Packit 1422b7
	--npb->astats.recursion_level;
Packit 1422b7
#	endif
Packit 1422b7
	return r;
Packit 1422b7
}
Packit 1422b7
Packit 1422b7
int
Packit 1422b7
ln_normalize(ln_ctx ctx, const char *str, const size_t strLen, struct json_object **json_p)
Packit 1422b7
{
Packit 1422b7
	int r;
Packit 1422b7
	struct ln_pdag *endNode = NULL;
Packit 1422b7
	/* old cruft */
Packit 1422b7
	if(ctx->version == 1) {
Packit 1422b7
		r = ln_v1_normalize(ctx, str, strLen, json_p);
Packit 1422b7
		goto done;
Packit 1422b7
	}
Packit 1422b7
	/* end old cruft */
Packit 1422b7
Packit 1422b7
	npb_t npb;
Packit 1422b7
	memset(&npb, 0, sizeof(npb));
Packit 1422b7
	npb.ctx = ctx;
Packit 1422b7
	npb.str = str;
Packit 1422b7
	npb.strLen = strLen;
Packit 1422b7
	if(ctx->opts & LN_CTXOPT_ADD_RULE) {
Packit 1422b7
		npb.rule = es_newStr(1024);
Packit 1422b7
	}
Packit 1422b7
#	ifdef ADVANCED_STATS
Packit 1422b7
	npb.astats.exec_path = es_newStr(1024);
Packit 1422b7
#	endif
Packit 1422b7
Packit 1422b7
	if(*json_p == NULL) {
Packit 1422b7
		CHKN(*json_p = json_object_new_object());
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	r = ln_normalizeRec(&npb, ctx->pdag, 0, 0, *json_p, &endNode);
Packit 1422b7
Packit 1422b7
	if(ctx->debug) {
Packit 1422b7
		if(r == 0) {
Packit 1422b7
			LN_DBGPRINTF(ctx, "final result for normalizer: parsedTo %zu, endNode %p, "
Packit 1422b7
				     "isTerminal %d, tagbucket %p",
Packit 1422b7
				     npb.parsedTo, endNode, endNode->flags.isTerminal, endNode->tags);
Packit 1422b7
		} else {
Packit 1422b7
			LN_DBGPRINTF(ctx, "final result for normalizer: parsedTo %zu, endNode %p",
Packit 1422b7
				     npb.parsedTo, endNode);
Packit 1422b7
		}
Packit 1422b7
	}
Packit 1422b7
	LN_DBGPRINTF(ctx, "DONE, final return is %d", r);
Packit 1422b7
	if(r == 0 && endNode->flags.isTerminal) {
Packit 1422b7
		/* success, finalize event */
Packit 1422b7
		if(endNode->tags != NULL) {
Packit 1422b7
			/* add tags to an event */
Packit 1422b7
			json_object_get(endNode->tags);
Packit 1422b7
			json_object_object_add(*json_p, "event.tags", endNode->tags);
Packit 1422b7
			CHKR(ln_annotate(ctx, *json_p, endNode->tags));
Packit 1422b7
		}
Packit 1422b7
		if(ctx->opts & LN_CTXOPT_ADD_ORIGINALMSG) {
Packit 1422b7
			/* originalmsg must be kept outside of metadata for
Packit 1422b7
			 * backward compatibility reasons.
Packit 1422b7
			 */
Packit 1422b7
			json_object_object_add(*json_p, ORIGINAL_MSG_KEY,
Packit 1422b7
				json_object_new_string_len(str, strLen));
Packit 1422b7
		}
Packit 1422b7
		addRuleMetadata(&npb, *json_p, endNode);
Packit 1422b7
		r = 0;
Packit 1422b7
	} else {
Packit 1422b7
		addUnparsedField(str, strLen, npb.parsedTo, *json_p);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	if(ctx->opts & LN_CTXOPT_ADD_RULE) {
Packit 1422b7
		es_deleteStr(npb.rule);
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
#ifdef	ADVANCED_STATS
Packit 1422b7
	if(r != 0)
Packit 1422b7
		es_addBuf(&npb.astats.exec_path, "[FAILED]", 8);
Packit 1422b7
	else if(!endNode->flags.isTerminal)
Packit 1422b7
		es_addBuf(&npb.astats.exec_path, "[FAILED:NON-TERMINAL]", 21);
Packit 1422b7
	if(npb.astats.pathlen < ADVSTATS_MAX_ENTITIES)
Packit 1422b7
		advstats_pathlens[npb.astats.pathlen]++;
Packit 1422b7
	if(npb.astats.pathlen > advstats_max_pathlen) {
Packit 1422b7
		advstats_max_pathlen = npb.astats.pathlen;
Packit 1422b7
	}
Packit 1422b7
	if(npb.astats.backtracked < ADVSTATS_MAX_ENTITIES)
Packit 1422b7
		advstats_backtracks[npb.astats.backtracked]++;
Packit 1422b7
	if(npb.astats.backtracked > advstats_max_backtracked) {
Packit 1422b7
		advstats_max_backtracked = npb.astats.backtracked;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	/* parser calls */
Packit 1422b7
	if(npb.astats.parser_calls < ADVSTATS_MAX_ENTITIES)
Packit 1422b7
		advstats_parser_calls[npb.astats.parser_calls]++;
Packit 1422b7
	if(npb.astats.parser_calls > advstats_max_parser_calls) {
Packit 1422b7
		advstats_max_parser_calls = npb.astats.parser_calls;
Packit 1422b7
	}
Packit 1422b7
	if(npb.astats.lit_parser_calls < ADVSTATS_MAX_ENTITIES)
Packit 1422b7
		advstats_lit_parser_calls[npb.astats.lit_parser_calls]++;
Packit 1422b7
	if(npb.astats.lit_parser_calls > advstats_max_lit_parser_calls) {
Packit 1422b7
		advstats_max_lit_parser_calls = npb.astats.lit_parser_calls;
Packit 1422b7
	}
Packit 1422b7
Packit 1422b7
	es_deleteStr(npb.astats.exec_path);
Packit 1422b7
#endif
Packit 1422b7
done:	return r;
Packit 1422b7
}