test
Fork of mbed-libxively-6eca970 by
src/libxively/csv_data.c
- Committer:
- xively
- Date:
- 2013-06-26
- Revision:
- 0:82702e998d3f
File content as of revision 0:82702e998d3f:
// 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 = 0; int size = sizeof( XI_CSV_LOCAL_BUFFER ); int offset = 0; s = snprintf( XI_CSV_LOCAL_BUFFER + offset, size - offset , "%s,", datastream_id ); XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN ); s = csv_encode_value( XI_CSV_LOCAL_BUFFER + offset, size - offset, data ); XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN ); s = snprintf( XI_CSV_LOCAL_BUFFER + offset, size - offset, "\n" ); XI_CHECK_S( s, size, offset, 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; }