Dependents: xively-jumpstart-demo
This is Xively C library, the code lives on GitHub.
See our example program and the tutorial, documentation can bee found here.
Diff: src/libxively/csv_data.c
- Revision:
- 0:53753690a8bf
diff -r 000000000000 -r 53753690a8bf src/libxively/csv_data.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/csv_data.c Mon May 13 19:28:22 2013 +0000 @@ -0,0 +1,381 @@ +// Copyright (c) 2003-2013, LogMeIn, Inc. All rights reserved. +// This is part of Xively C library, it is under the BSD 3-Clause license. + +/** + * \file csv_data.c + * \author Olgierd Humenczuk + * \brief Implements CSV _data layer_ encoders and decoders specific to Xively CSV data format [see csv_data.h] + */ + +#include <assert.h> +#include <stdio.h> +#include <time.h> + +#include "csv_data.h" +#include "xi_macros.h" +#include "xi_helpers.h" +#include "xi_err.h" +#include "xi_consts.h" + +static char XI_CSV_LOCAL_BUFFER[ XI_CSV_BUFFER_SIZE ]; + +inline static int csv_encode_value( + char* buffer + , size_t buffer_size + , const xi_datapoint_t* p ) +{ + // PRECONDITION + assert( buffer != 0 ); + assert( buffer_size != 0 ); + assert( p != 0 ); + + switch( p->value_type ) + { + case XI_VALUE_TYPE_I32: + return snprintf( buffer, buffer_size, "%d", p->value.i32_value ); + case XI_VALUE_TYPE_F32: + return snprintf( buffer, buffer_size, "%f", p->value.f32_value ); + case XI_VALUE_TYPE_STR: + return snprintf( buffer, buffer_size, "%s", p->value.str_value ); + default: + return -1; + } +} + +typedef enum +{ + XI_CHAR_UNKNOWN = 0, + XI_CHAR_NUMBER, + XI_CHAR_LETTER, + XI_CHAR_DOT, + XI_CHAR_SPACE, + XI_CHAR_NEWLINE, + XI_CHAR_TAB, + XI_CHAR_MINUS +} xi_char_type_t; + +inline static xi_char_type_t csv_classify_char( char c ) +{ + switch( c ) + { + case 13: + case 11: + return XI_CHAR_NEWLINE; + case 9: + return XI_CHAR_TAB; + case 32: + return XI_CHAR_SPACE; + case 33: case 34: case 35: case 36: case 37: case 38: case 39: + case 40: case 41: case 42: case 43: case 44: + return XI_CHAR_UNKNOWN; + case 45: + return XI_CHAR_MINUS; + case 46: + return XI_CHAR_DOT; + case 47: + return XI_CHAR_UNKNOWN; + case 48: case 49: case 50: case 51: case 52: case 53: case 54: + case 55: case 56: + case 57: + return XI_CHAR_NUMBER; + case 58: case 59: case 60: case 61: case 62: case 63: + case 64: + return XI_CHAR_UNKNOWN; + case 65: case 66: case 67: case 68: case 69: case 70: case 71: + case 72: case 73: case 74: case 75: case 76: case 77: case 78: + case 79: case 80: case 81: case 82: case 83: case 84: case 85: + case 86: case 87: case 88: case 89: + case 90: + return XI_CHAR_LETTER; + case 91: case 92: case 93: case 94: case 95: + case 96: + return XI_CHAR_UNKNOWN; + case 97: case 98: case 99: case 100: case 101: case 102: case 103: + case 104: case 105: case 106: case 107: case 108: case 109: case 110: + case 111: case 112: case 113: case 114: case 115: case 116: case 117: + case 118: case 119: case 120: case 121: + case 122: + return XI_CHAR_LETTER; + case 123: + case 124: + case 125: + return XI_CHAR_UNKNOWN; + default: + return XI_CHAR_UNKNOWN; + } +} + +typedef enum +{ + XI_STATE_INITIAL = 0, + XI_STATE_MINUS, + XI_STATE_NUMBER, + XI_STATE_FLOAT, + XI_STATE_DOT, + XI_STATE_STRING +} xi_dfa_state_t; + +xi_datapoint_t* csv_decode_value( + const char* buffer, xi_datapoint_t* p ) +{ + // PRECONDITION + assert( buffer != 0 ); + assert( p != 0 ); + + // secure the output buffer + XI_GUARD_EOS( p->value.str_value, XI_VALUE_STRING_MAX_SIZE ); + + // clean the counter + size_t counter = 0; + + // the transition function + static const short states[][6][2] = + { + // state initial // state minus // state number // state float // state dot // string + { { XI_CHAR_UNKNOWN , XI_STATE_STRING }, { XI_CHAR_UNKNOWN , XI_STATE_STRING }, { XI_CHAR_UNKNOWN , XI_STATE_STRING }, { XI_CHAR_UNKNOWN , XI_STATE_STRING }, { XI_CHAR_UNKNOWN , XI_STATE_STRING }, { XI_CHAR_UNKNOWN , XI_STATE_STRING } }, + { { XI_CHAR_NUMBER , XI_STATE_NUMBER }, { XI_CHAR_NUMBER , XI_STATE_NUMBER }, { XI_CHAR_NUMBER , XI_STATE_NUMBER }, { XI_CHAR_NUMBER , XI_STATE_FLOAT }, { XI_CHAR_NUMBER , XI_STATE_FLOAT }, { XI_CHAR_NUMBER , XI_STATE_STRING } }, + { { XI_CHAR_LETTER , XI_STATE_STRING }, { XI_CHAR_LETTER , XI_STATE_STRING }, { XI_CHAR_LETTER , XI_STATE_STRING }, { XI_CHAR_LETTER , XI_STATE_STRING }, { XI_CHAR_LETTER , XI_STATE_STRING }, { XI_CHAR_LETTER , XI_STATE_STRING } }, + { { XI_CHAR_DOT , XI_STATE_DOT }, { XI_CHAR_DOT , XI_STATE_DOT }, { XI_CHAR_DOT , XI_STATE_DOT }, { XI_CHAR_DOT , XI_STATE_STRING }, { XI_CHAR_DOT , XI_STATE_STRING }, { XI_CHAR_DOT , XI_STATE_STRING } }, + { { XI_CHAR_SPACE , XI_STATE_STRING }, { XI_CHAR_SPACE , XI_STATE_STRING }, { XI_CHAR_SPACE , XI_STATE_STRING }, { XI_CHAR_SPACE , XI_STATE_STRING }, { XI_CHAR_SPACE , XI_STATE_STRING }, { XI_CHAR_SPACE , XI_STATE_STRING } }, + { { XI_CHAR_NEWLINE , XI_STATE_INITIAL }, { XI_CHAR_NEWLINE , XI_STATE_INITIAL }, { XI_CHAR_NEWLINE , XI_STATE_INITIAL }, { XI_CHAR_NEWLINE , XI_STATE_INITIAL }, { XI_CHAR_NEWLINE , XI_STATE_INITIAL }, { XI_CHAR_NEWLINE , XI_STATE_INITIAL } }, + { { XI_CHAR_TAB , XI_STATE_STRING }, { XI_CHAR_TAB , XI_STATE_STRING }, { XI_CHAR_TAB , XI_STATE_STRING }, { XI_CHAR_TAB , XI_STATE_STRING }, { XI_CHAR_TAB , XI_STATE_STRING }, { XI_CHAR_TAB , XI_STATE_STRING } }, + { { XI_CHAR_MINUS , XI_STATE_MINUS }, { XI_CHAR_MINUS , XI_STATE_STRING }, { XI_CHAR_MINUS , XI_STATE_STRING }, { XI_CHAR_MINUS , XI_STATE_STRING }, { XI_CHAR_MINUS , XI_STATE_STRING }, { XI_CHAR_MINUS , XI_STATE_STRING } } + }; + + char c = *buffer; + short s = XI_STATE_INITIAL; + + while( c != '\n' && c !='\0' && c!='\r' ) + { + if( counter >= XI_VALUE_STRING_MAX_SIZE - 1 ) + { + xi_set_err( XI_DATAPOINT_VALUE_BUFFER_OVERFLOW ); + return 0; + } + + xi_char_type_t ct = csv_classify_char( c ); + s = states[ ct ][ s ][ 1 ]; + + switch( s ) + { + case XI_STATE_MINUS: + case XI_STATE_NUMBER: + case XI_STATE_FLOAT: + case XI_STATE_DOT: + case XI_STATE_STRING: + p->value.str_value[ counter ] = c; + break; + } + + c = *( ++buffer ); + ++counter; + } + + // set the guard + p->value.str_value[ counter ] = '\0'; + + // update of the state for loose states... + switch( s ) + { + case XI_STATE_MINUS: + case XI_STATE_DOT: + case XI_STATE_INITIAL: + s = XI_STATE_STRING; + break; + } + + switch( s ) + { + case XI_STATE_NUMBER: + p->value.i32_value = atoi( p->value.str_value ); + p->value_type = XI_VALUE_TYPE_I32; + break; + case XI_STATE_FLOAT: + p->value.f32_value = atof( p->value.str_value ); + p->value_type = XI_VALUE_TYPE_F32; + break; + case XI_STATE_STRING: + default: + p->value_type = XI_VALUE_TYPE_STR; + } + + return p; +} + +const char* csv_encode_datapoint( const xi_datapoint_t* data ) +{ + + // PRECONDITIONS + assert( data != 0 ); + + return csv_encode_datapoint_in_place( XI_CSV_LOCAL_BUFFER, sizeof( XI_CSV_LOCAL_BUFFER ), data ) == -1 ? 0 : XI_CSV_LOCAL_BUFFER; +} + +int csv_encode_datapoint_in_place( + char* in, size_t in_size + , const xi_datapoint_t* datapoint ) +{ + // PRECONDITIONS + assert( in != 0 ); + assert( datapoint != 0 ); + + int s = 0; + int size = in_size; + int offset = 0; + + if( datapoint->timestamp.timestamp != 0 ) + { + time_t stamp = datapoint->timestamp.timestamp; + struct tm* gmtinfo = xi_gmtime( &stamp ); + + s = snprintf( in, size + , "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ," + , gmtinfo->tm_year + 1900 + , gmtinfo->tm_mon + 1 + , gmtinfo->tm_mday + , gmtinfo->tm_hour + , gmtinfo->tm_min + , gmtinfo->tm_sec + , ( int ) datapoint->timestamp.micro ); + + XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN ); + } + + s = csv_encode_value( in + offset, size - offset, datapoint ); + XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN ); + + s = snprintf( in + offset, size - offset, "%s", "\n" ); + XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN ); + + return offset; + +err_handling: + return -1; +} + +const char* csv_encode_create_datastream( + const char* datastream_id + , const xi_datapoint_t* data ) +{ + // PRECONDITIONS + assert( data != 0 ); + + int s = snprintf( XI_CSV_LOCAL_BUFFER, sizeof( XI_CSV_LOCAL_BUFFER ) + , "%s,%d\n", datastream_id, data->value.i32_value ); + int size = XI_CSV_BUFFER_SIZE; + + XI_CHECK_SIZE( s, size, XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN ); + + return XI_CSV_LOCAL_BUFFER; + +err_handling: + return 0; +} + +xi_feed_t* csv_decode_feed( + const char* buffer + , xi_feed_t* feed ) +{ + const char* current = buffer; + int32_t counter = 0; + + // find occurence of newline + char* end_of_line = ( char* ) 1; + + // while we didn't jump out of the buffer + while( end_of_line ) + { + // get current datapoint + xi_datastream_t* d = &feed->datastreams[ counter ]; + d->datapoint_count = 0; + xi_datapoint_t* p = &d->datapoints[ 0 ]; + memset( p, 0, sizeof( xi_datapoint_t ) ); + + end_of_line = strstr( current, "\n" ); + + const char* end_of_datastream_id = strstr( current, "," ); + const char* beg_of_datapoint = end_of_datastream_id + 1; + + XI_CHECK_ZERO( beg_of_datapoint, XI_CSV_DECODE_FEED_PARSER_ERROR ); + XI_CHECK_ZERO( end_of_datastream_id, XI_CSV_DECODE_FEED_PARSER_ERROR ); + + int size = sizeof( d->datastream_id ); + + int s = xi_str_copy_untiln( d->datastream_id, size + , current, ',' ); + XI_CHECK_SIZE( s, size, XI_CSV_DECODE_FEED_PARSER_ERROR ); + + xi_datapoint_t* ret = csv_decode_datapoint( beg_of_datapoint, p ); + XI_CHECK_ZERO( ret, XI_CSV_DECODE_FEED_PARSER_ERROR ) + + d->datapoint_count = 1; + current = end_of_line + 1; + + XI_CHECK_CND( ++counter == XI_MAX_DATASTREAMS + , XI_CSV_DECODE_FEED_PARSER_ERROR ); + } + + feed->datastream_count = counter; + return feed; + +err_handling: + return 0; +} + +xi_datapoint_t* csv_decode_datapoint( + const char* buffer + , xi_datapoint_t* datapoint ) +{ + // PRECONDITIONS + assert( buffer != 0 ); + assert( datapoint != 0 ); + + int ye, mo, da, h, m, s, ms; + + const char* beg_of_value = strstr( buffer, "," ); + + // check continuation condition + XI_CHECK_ZERO( beg_of_value, XI_CSV_DECODE_DATAPOINT_PARSER_ERROR ); + + // move to pointer to the proper position + beg_of_value += 1; + + { + int n = sscanf( buffer + , "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ" + , &ye, &mo, &da, &h, &m, &s, &ms ); + + // check if the parser worked correctly + XI_CHECK_CND( n != 7, XI_CSV_DECODE_DATAPOINT_PARSER_ERROR ); + + // copy parsed data + { + struct tm timeinfo; + + timeinfo.tm_year = ye - 1900; + timeinfo.tm_mon = mo - 1; + timeinfo.tm_mday = da; + timeinfo.tm_hour = h; + timeinfo.tm_min = m; + timeinfo.tm_sec = s; + + time_t timestamp = xi_mktime( &timeinfo ); + + XI_CHECK_CND( ( int )timestamp == -1, XI_CSV_TIME_CONVERTION_ERROR ); + + datapoint->timestamp.timestamp = timestamp; + datapoint->timestamp.micro = ms; + } + } + + xi_datapoint_t* r = csv_decode_value( beg_of_value + , datapoint ); + + + XI_CHECK_ZERO( r, XI_CSV_DECODE_DATAPOINT_PARSER_ERROR ); + + return datapoint; + +err_handling: + return 0; +}