#line 1 "parser-ragel.rl"
/*
* Copyright (c) 2019 Miroslav Foltýn.  All Rights Reserved.
* 
* 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 2 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.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

#include "parsers.h"
#include "parser-ragel.h"
#include "parsers-utils.h"
#include "utils.h"


#line 27 "parser-ragel.c"
static const signed char _statsd_actions[] = {
	0, 1, 0, 1, 2, 1, 3, 1,
	4, 1, 6, 1, 7, 1, 8, 2,
	0, 1, 2, 9, 5, 0
};

static const short _statsd_key_offsets[] = {
	0, 0, 2, 12, 22, 24, 30, 32,
	37, 41, 43, 46, 49, 53, 62, 70,
	81, 82, 83, 84, 85, 94, 102, 112,
	124, 133, 145, 156, 166, 0
};

static const char _statsd_trans_keys[] = {
	97, 122, 44, 46, 58, 95, 48, 57,
	65, 90, 97, 122, 43, 45, 46, 95,
	48, 57, 65, 90, 97, 122, 48, 57,
	46, 69, 101, 124, 48, 57, 48, 57,
	69, 101, 124, 48, 57, 43, 45, 48,
	57, 48, 57, 124, 48, 57, 99, 103,
	109, 0, 10, 64, 124, 46, 58, 95,
	48, 57, 65, 90, 97, 122, 46, 95,
	48, 57, 65, 90, 97, 122, 0, 10,
	44, 46, 95, 48, 57, 65, 90, 97,
	122, 0, 0, 35, 115, 46, 61, 95,
	48, 57, 65, 90, 97, 122, 46, 95,
	48, 57, 65, 90, 97, 122, 44, 46,
	58, 95, 48, 57, 65, 90, 97, 122,
	46, 61, 69, 95, 101, 124, 48, 57,
	65, 90, 97, 122, 46, 61, 95, 48,
	57, 65, 90, 97, 122, 46, 61, 69,
	95, 101, 124, 48, 57, 65, 90, 97,
	122, 43, 45, 46, 61, 95, 48, 57,
	65, 90, 97, 122, 46, 61, 95, 124,
	48, 57, 65, 90, 97, 122, 46, 95,
	48, 57, 65, 90, 97, 122, 0
};

static const signed char _statsd_single_lengths[] = {
	0, 0, 4, 4, 0, 4, 0, 3,
	2, 0, 1, 3, 4, 3, 2, 5,
	1, 1, 1, 1, 3, 2, 4, 6,
	3, 6, 5, 4, 2, 0
};

static const signed char _statsd_range_lengths[] = {
	0, 1, 3, 3, 1, 1, 1, 1,
	1, 1, 1, 0, 0, 3, 3, 3,
	0, 0, 0, 0, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 0
};

static const short _statsd_index_offsets[] = {
	0, 0, 2, 10, 18, 20, 26, 28,
	33, 37, 39, 42, 46, 51, 58, 64,
	73, 75, 77, 79, 81, 88, 94, 102,
	112, 119, 129, 138, 146, 0
};

static const signed char _statsd_cond_targs[] = {
	2, 0, 3, 2, 3, 2, 2, 2,
	2, 0, 4, 4, 20, 20, 23, 20,
	20, 0, 5, 0, 6, 8, 8, 11,
	5, 0, 7, 0, 8, 8, 11, 7,
	0, 9, 9, 10, 0, 10, 0, 11,
	10, 0, 12, 12, 19, 0, 28, 17,
	28, 18, 0, 13, 14, 13, 13, 13,
	13, 0, 15, 15, 15, 15, 15, 0,
	28, 16, 28, 15, 15, 15, 15, 15,
	0, 28, 0, 28, 0, 28, 0, 12,
	0, 20, 21, 20, 20, 20, 20, 0,
	22, 22, 22, 22, 22, 0, 3, 22,
	3, 22, 22, 22, 22, 0, 24, 21,
	26, 20, 26, 11, 23, 20, 20, 0,
	20, 21, 20, 25, 20, 20, 0, 20,
	21, 26, 20, 26, 11, 25, 20, 20,
	0, 9, 9, 20, 21, 20, 27, 20,
	20, 0, 20, 21, 20, 11, 27, 20,
	20, 0, 13, 13, 13, 13, 13, 0,
	0, 1, 2, 3, 4, 5, 6, 7,
	8, 9, 10, 11, 12, 13, 14, 15,
	16, 17, 18, 19, 20, 21, 22, 23,
	24, 25, 26, 27, 28, 0
};

static const signed char _statsd_cond_actions[] = {
	5, 1, 7, 0, 7, 0, 0, 0,
	0, 1, 0, 0, 0, 0, 0, 0,
	0, 1, 0, 1, 0, 0, 0, 9,
	0, 1, 0, 1, 0, 0, 9, 0,
	1, 0, 0, 0, 1, 0, 1, 9,
	0, 1, 0, 0, 0, 1, 11, 0,
	11, 0, 1, 0, 13, 0, 0, 0,
	0, 1, 0, 0, 0, 0, 0, 1,
	18, 0, 18, 0, 0, 0, 0, 0,
	1, 18, 1, 11, 1, 11, 1, 0,
	1, 0, 13, 0, 0, 0, 0, 1,
	0, 0, 0, 0, 0, 1, 18, 0,
	18, 0, 0, 0, 0, 1, 0, 13,
	0, 0, 0, 9, 0, 0, 0, 1,
	0, 13, 0, 0, 0, 0, 1, 0,
	13, 0, 0, 0, 9, 0, 0, 0,
	1, 0, 0, 0, 13, 0, 0, 0,
	0, 1, 0, 13, 0, 9, 0, 0,
	0, 1, 0, 0, 0, 0, 0, 0,
	0, 15, 15, 15, 15, 15, 15, 15,
	15, 15, 15, 15, 15, 15, 15, 15,
	15, 15, 15, 15, 15, 15, 15, 15,
	15, 15, 15, 15, 0, 0
};

static const signed char _statsd_to_state_actions[] = {
	0, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 0
};

static const short _statsd_eof_trans[] = {
	153, 154, 155, 156, 157, 158, 159, 160,
	161, 162, 163, 164, 165, 166, 167, 168,
	169, 170, 171, 172, 173, 174, 175, 176,
	177, 178, 179, 180, 181, 0
};

static const int statsd_start = 1;
static const int statsd_first_final = 28;
static const int statsd_error = 0;

static const int statsd_en_main = 1;


#line 27 "parser-ragel.rl"



/**
* Ragel parser entry point
* Parsers given buffer and populates datagram with parsed data if they are valid
* @arg str - Buffer to be parsed
* @arg datagram - Placeholder for parsed data
* @return 1 on success, 0 on fail 
*/
int
ragel_parser_parse(char* str, struct statsd_datagram** datagram) {
	*datagram = (struct statsd_datagram*) malloc(sizeof(struct statsd_datagram));
	*(*datagram) = (struct statsd_datagram) {0};
	ALLOC_CHECK("Not enough memory to save datagram");
	size_t length = strlen(str);
	char *p = str, *pe = (str + length + 1);
	char *eof = pe;
	int cs;
	size_t current_index = 0;
	size_t current_segment_start_index = 0;
	struct tag_collection* tags = NULL;
	char* tag_key = NULL;
	char* tag_value = NULL;
	int tag_key_allocated = 0;
	int tag_value_allocated = 0;
	int any_tags = 0;
	
	
#line 190 "parser-ragel.c"
	{
		cs = (int)statsd_start;
	}
	
#line 195 "parser-ragel.c"
	{
		int _klen;
		unsigned int _trans = 0;
		const char * _keys;
		const signed char * _acts;
		unsigned int _nacts;
		_resume: {}
		if ( p == pe && p != eof )
			goto _out;
		if ( p == eof ) {
			if ( _statsd_eof_trans[cs] > 0 ) {
				_trans = (unsigned int)_statsd_eof_trans[cs] - 1;
			}
		}
		else {
			_keys = ( _statsd_trans_keys + (_statsd_key_offsets[cs]));
			_trans = (unsigned int)_statsd_index_offsets[cs];
			
			_klen = (int)_statsd_single_lengths[cs];
			if ( _klen > 0 ) {
				const char *_lower = _keys;
				const char *_upper = _keys + _klen - 1;
				const char *_mid;
				while ( 1 ) {
					if ( _upper < _lower ) {
						_keys += _klen;
						_trans += (unsigned int)_klen;
						break;
					}
					
					_mid = _lower + ((_upper-_lower) >> 1);
					if ( ( (*( p))) < (*( _mid)) )
						_upper = _mid - 1;
					else if ( ( (*( p))) > (*( _mid)) )
						_lower = _mid + 1;
					else {
						_trans += (unsigned int)(_mid - _keys);
						goto _match;
					}
				}
			}
			
			_klen = (int)_statsd_range_lengths[cs];
			if ( _klen > 0 ) {
				const char *_lower = _keys;
				const char *_upper = _keys + (_klen<<1) - 2;
				const char *_mid;
				while ( 1 ) {
					if ( _upper < _lower ) {
						_trans += (unsigned int)_klen;
						break;
					}
					
					_mid = _lower + (((_upper-_lower) >> 1) & ~1);
					if ( ( (*( p))) < (*( _mid)) )
						_upper = _mid - 2;
					else if ( ( (*( p))) > (*( _mid + 1)) )
						_lower = _mid + 2;
					else {
						_trans += (unsigned int)((_mid - _keys)>>1);
						break;
					}
				}
			}
			
			_match: {}
		}
		cs = (int)_statsd_cond_targs[_trans];
		
		if ( _statsd_cond_actions[_trans] != 0 ) {
			
			_acts = ( _statsd_actions + (_statsd_cond_actions[_trans]));
			_nacts = (unsigned int)(*( _acts));
			_acts += 1;
			while ( _nacts > 0 ) {
				switch ( (*( _acts)) )
				{
					case 0:  {
						{
#line 57 "parser-ragel.rl"
							
							goto error_clean_up;
						}
						
#line 280 "parser-ragel.c"
						
						break; 
					}
					case 1:  {
						{
#line 61 "parser-ragel.rl"
							
							goto error_clean_up;
						}
						
#line 291 "parser-ragel.c"
						
						break; 
					}
					case 3:  {
						{
#line 69 "parser-ragel.rl"
							
							current_segment_start_index = current_index;
						}
						
#line 302 "parser-ragel.c"
						
						break; 
					}
					case 4:  {
						{
#line 73 "parser-ragel.rl"
							
							size_t current_segment_length = current_index - current_segment_start_index;
							(*datagram)->name = (char*) malloc(current_segment_length + 1);
							ALLOC_CHECK("Not enough memory to save metric name");
							memcpy(
							(*datagram)->name,
							&str[current_segment_start_index],
							current_segment_length
							);
							(*datagram)->name[current_segment_length] = '\0';
							current_segment_start_index = current_index + 1; 
						}
						
#line 322 "parser-ragel.c"
						
						break; 
					}
					case 5:  {
						{
#line 86 "parser-ragel.rl"
							
							size_t key_len = strlen(tag_key) + 1;
							size_t value_len = strlen(tag_value) + 1;
							struct tag* t = (struct tag*) malloc(sizeof(struct tag));
							ALLOC_CHECK("Unable to allocate memory for tag.");
							t->key = (char*) malloc(key_len);
							ALLOC_CHECK("Unable to allocate memory for tag key.");
							memcpy(t->key, tag_key, key_len);
							t->value = (char*) malloc(value_len);
							ALLOC_CHECK("Unable to allocate memory for tag value.");
							memcpy(t->value, tag_value, value_len);
							if (!any_tags) {
								tags = (struct tag_collection*) malloc(sizeof(struct tag_collection));
								ALLOC_CHECK("Unable to allocate memory for tag collection.");
								tags->values = (struct tag**) malloc(sizeof(struct tag*));
								tags->values[0] = t;
								tags->length = 1;
								any_tags = 1;
							} else {
								struct tag** new_tags =
								(struct tag**) realloc(tags->values, sizeof(struct tag*) * (tags->length + 1));
								ALLOC_CHECK("Unable to allocate memory for tags");
								if (tags != NULL) {
									tags->values = new_tags;
									tags->values[tags->length] = t;
									tags->length++;
								}
							}
							free(tag_key);
							free(tag_value);
							tag_key = NULL;
							tag_value = NULL;
							tag_key_allocated = 0;
							tag_value_allocated = 0;
						}
						
#line 365 "parser-ragel.c"
						
						break; 
					}
					case 6:  {
						{
#line 122 "parser-ragel.rl"
							
							char* startptr;
							char* endptr;
							if (str[current_segment_start_index] == '+') {
								(*datagram)->explicit_sign = SIGN_PLUS;
								startptr = &str[current_segment_start_index + 1];
							} else if (str[current_segment_start_index] == '-') {
								(*datagram)->explicit_sign = SIGN_MINUS;
								startptr = &str[current_segment_start_index + 1];
							} else {
								(*datagram)->explicit_sign = SIGN_NONE;
								startptr = &str[current_segment_start_index];
							}
							double value = strtod(startptr, &endptr);
							if (startptr == endptr || errno == ERANGE) {
								goto error_clean_up;
							}
							(*datagram)->value = value;
							current_segment_start_index = current_index + 1;
						}
						
#line 393 "parser-ragel.c"
						
						break; 
					}
					case 7:  {
						{
#line 143 "parser-ragel.rl"
							
							if (str[current_segment_start_index] == 'c') {
								(*datagram)->type = METRIC_TYPE_COUNTER;
							} else if (str[current_segment_start_index] == 'g') {
								(*datagram)->type = METRIC_TYPE_GAUGE;
							} else {
								(*datagram)->type = METRIC_TYPE_DURATION;
							}
							current_segment_start_index = current_index + 1;
						}
						
#line 411 "parser-ragel.c"
						
						break; 
					}
					case 8:  {
						{
#line 154 "parser-ragel.rl"
							
							size_t current_segment_length = current_index - current_segment_start_index;
							tag_key = (char *) realloc(tag_key, current_segment_length + 1);
							ALLOC_CHECK("Not enough memory for tag key buffer.");
							tag_key_allocated = 1;
							memcpy(
							tag_key,
							&str[current_segment_start_index], 
							current_segment_length
							);
							tag_key[current_segment_length] = '\0';
							current_segment_start_index = current_index + 1;
						}
						
#line 432 "parser-ragel.c"
						
						break; 
					}
					case 9:  {
						{
#line 168 "parser-ragel.rl"
							
							size_t current_segment_length = current_index - current_segment_start_index;
							tag_value = (char *) realloc(tag_value, current_segment_length + 1);
							ALLOC_CHECK("Not enough memory for tag key buffer.");
							tag_value_allocated = 1;
							memcpy(
							tag_value,
							&str[current_segment_start_index], 
							current_segment_length
							);
							tag_value[current_segment_length] = '\0';
							current_segment_start_index = current_index + 1;
						}
						
#line 453 "parser-ragel.c"
						
						break; 
					}
				}
				_nacts -= 1;
				_acts += 1;
			}
			
		}
		
		if ( p == eof ) {
			if ( cs >= 28 )
				goto _out;
		}
		else {
			_acts = ( _statsd_actions + (_statsd_to_state_actions[cs]));
			_nacts = (unsigned int)(*( _acts));
			_acts += 1;
			while ( _nacts > 0 ) {
				switch ( (*( _acts)) ) {
					case 2:  {
						{
#line 65 "parser-ragel.rl"
							
							current_index++;
						}
						
#line 481 "parser-ragel.c"
						
						break; 
					}
				}
				_nacts -= 1;
				_acts += 1;
			}
			
			if ( cs != 0 ) {
				p += 1;
				goto _resume;
			}
		}
		_out: {}
	}
	
#line 196 "parser-ragel.rl"
	
	(void)statsd_en_main;
	(void)statsd_error;
	(void)statsd_first_final;
	
	if (any_tags) {
		char* json = tag_collection_to_json(tags);
		if (json != NULL) {
			(*datagram)->tags = json;
			(*datagram)->tags_pair_count = tags->length;
		}
		free_tag_collection(tags);
		if (tag_key_allocated) free(tag_key);
			if (tag_value_allocated) free(tag_value);
		}
	if (str[length - 1] == '\n')
		str[length - 1] = 0;
	VERBOSE_LOG(2, "Parsed: %s", str);
	return 1;
	
	error_clean_up:
	if (any_tags) {
		free_tag_collection(tags);
	}
	if (tag_key_allocated) free(tag_key);
		if (tag_value_allocated) free(tag_value);
		if (str[length - 1] == '\n')
		str[length - 1] = 0;
	free_datagram(*datagram);
	METRIC_PROCESSING_ERR_LOG("Throwing away datagram. REASON: unable to parse: %s", str);
	return 0;
};


/**
* --------------------------------------
* |                                    |
* |     UNIT TEST FOR THIS FILE        |
* |                                    |
* --------------------------------------
*/
#if _TEST_TARGET == 2

int
main() {
	INIT_TEST("Running tests for ragel parser:", ragel_parser_parse);
	SUITE_HEADER("Unparsable values")
	CHECK_ERROR("", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("wow", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("wow:2", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("wow|g", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("2|g", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("1:1|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("e x-2 ple:20|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	CHECK_ERROR("example,tags=dwq=qwddqwd=qwd:10|c", NULL, NULL, 0, METRIC_TYPE_NONE, SIGN_NONE);
	SUITE_HEADER("Basic values");
	CHECK_ERROR("example:-1|c", "example", NULL, 1, METRIC_TYPE_COUNTER, SIGN_MINUS);
	CHECK_ERROR("example:+1|g", "example", NULL, 1, METRIC_TYPE_GAUGE, SIGN_PLUS);
	CHECK_ERROR("example:1|ms", "example", NULL, 1, METRIC_TYPE_DURATION, SIGN_NONE);
	SUITE_HEADER("Sanitizable metric name");
	SUITE_HEADER("Non integer values")
	CHECK_ERROR("example:1.2|c", "example", NULL, 1.2, METRIC_TYPE_COUNTER, SIGN_NONE);
	CHECK_ERROR("example:1.000000004|c", "example", NULL, 1.000000004, METRIC_TYPE_COUNTER, SIGN_NONE);
	CHECK_ERROR("example:1.00000000000000000000000000000004|c", "example", NULL, 1.00000000000000000000000000000004, METRIC_TYPE_COUNTER, SIGN_NONE);
	SUITE_HEADER("Instance descriptors")
	CHECK_ERROR("example,instance=20:20|c", "example", "{\"instance\":\"20\"}", 20, METRIC_TYPE_COUNTER, SIGN_NONE);
	SUITE_HEADER("Tag descriptors")
	CHECK_ERROR("example,tagY=20,tagX=10:10|c", "example", "{\"tagX\":\"10\",\"tagY\":\"20\"}", 10, METRIC_TYPE_COUNTER, SIGN_NONE);
	CHECK_ERROR("example,tagY=20:10|c|#tagY:20,tagW:W", "example", "{\"tagW\":\"W\",\"tagY\":\"20\"}", 10, METRIC_TYPE_COUNTER, SIGN_NONE);
	SUITE_HEADER("Malformed tags")
	SUITE_HEADER("Too long tag descriptor (gets thrown away)")
	CHECK_ERROR("example,[encoded data, 4101 characters]:10|c", "example", NULL, 10, METRIC_TYPE_COUNTER, SIGN_NONE);
	END_TEST();
}

#endif
