Xively Official / mbed-libxively-5d6fdd4

Dependents:   xively-jumpstart-demo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers csv_data.c Source File

csv_data.c

Go to the documentation of this file.
00001 // Copyright (c) 2003-2013, LogMeIn, Inc. All rights reserved.
00002 // This is part of Xively C library, it is under the BSD 3-Clause license.
00003 
00004 /**
00005  * \file    csv_data.c
00006  * \author  Olgierd Humenczuk
00007  * \brief   Implements CSV _data layer_ encoders and decoders specific to Xively CSV data format [see csv_data.h]
00008  */
00009 
00010 #include <assert.h>
00011 #include <stdio.h>
00012 #include <time.h>
00013 
00014 #include "csv_data.h"
00015 #include "xi_macros.h"
00016 #include "xi_helpers.h"
00017 #include "xi_err.h"
00018 #include "xi_consts.h"
00019 
00020 static char XI_CSV_LOCAL_BUFFER[ XI_CSV_BUFFER_SIZE ];
00021 
00022 inline static int csv_encode_value(
00023       char* buffer
00024     , size_t buffer_size
00025     , const xi_datapoint_t* p )
00026 {
00027     // PRECONDITION
00028     assert( buffer != 0 );
00029     assert( buffer_size != 0 );
00030     assert( p != 0 );
00031 
00032     switch( p->value_type )
00033     {
00034         case XI_VALUE_TYPE_I32:
00035             return snprintf( buffer, buffer_size, "%d", p->value.i32_value );
00036         case XI_VALUE_TYPE_F32:
00037             return snprintf( buffer, buffer_size, "%f", p->value.f32_value );
00038         case XI_VALUE_TYPE_STR:
00039             return snprintf( buffer, buffer_size, "%s", p->value.str_value );
00040         default:
00041             return -1;
00042     }
00043 }
00044 
00045 typedef enum
00046 {
00047     XI_CHAR_UNKNOWN = 0,
00048     XI_CHAR_NUMBER,
00049     XI_CHAR_LETTER,
00050     XI_CHAR_DOT,
00051     XI_CHAR_SPACE,
00052     XI_CHAR_NEWLINE,
00053     XI_CHAR_TAB,
00054     XI_CHAR_MINUS
00055 } xi_char_type_t;
00056 
00057 inline static xi_char_type_t csv_classify_char( char c )
00058 {
00059     switch( c )
00060     {
00061         case 13:
00062         case 11:
00063             return XI_CHAR_NEWLINE;
00064         case 9:
00065             return XI_CHAR_TAB;
00066         case 32:
00067             return XI_CHAR_SPACE;
00068         case 33: case 34: case 35: case 36: case 37: case 38: case 39:
00069         case 40: case 41: case 42: case 43: case 44:
00070             return XI_CHAR_UNKNOWN;
00071         case 45:
00072             return XI_CHAR_MINUS;
00073         case 46:
00074             return XI_CHAR_DOT;
00075         case 47:
00076             return XI_CHAR_UNKNOWN;
00077         case 48: case 49: case 50: case 51: case 52: case 53: case 54:
00078         case 55: case 56:
00079         case 57:
00080             return XI_CHAR_NUMBER;
00081         case 58: case 59: case 60: case 61: case 62: case 63:
00082         case 64:
00083             return XI_CHAR_UNKNOWN;
00084         case 65: case 66: case 67: case 68: case 69: case 70: case 71:
00085         case 72: case 73: case 74: case 75: case 76: case 77: case 78:
00086         case 79: case 80: case 81: case 82: case 83: case 84: case 85:
00087         case 86: case 87: case 88: case 89:
00088         case 90:
00089             return XI_CHAR_LETTER;
00090         case 91: case 92: case 93: case 94: case 95:
00091         case 96:
00092             return XI_CHAR_UNKNOWN;
00093         case 97: case 98: case 99: case 100: case 101: case 102: case 103:
00094         case 104: case 105: case 106: case 107: case 108: case 109: case 110:
00095         case 111: case 112: case 113: case 114: case 115: case 116: case 117:
00096         case 118: case 119: case 120: case 121:
00097         case 122:
00098             return XI_CHAR_LETTER;
00099         case 123:
00100         case 124:
00101         case 125:
00102             return XI_CHAR_UNKNOWN;
00103         default:
00104             return XI_CHAR_UNKNOWN;
00105     }
00106 }
00107 
00108 typedef enum
00109 {
00110     XI_STATE_INITIAL = 0,
00111     XI_STATE_MINUS,
00112     XI_STATE_NUMBER,
00113     XI_STATE_FLOAT,
00114     XI_STATE_DOT,
00115     XI_STATE_STRING
00116 } xi_dfa_state_t;
00117 
00118 xi_datapoint_t* csv_decode_value(
00119     const char* buffer, xi_datapoint_t* p )
00120 {
00121     // PRECONDITION
00122     assert( buffer != 0 );
00123     assert( p != 0 );
00124 
00125     // secure the output buffer
00126     XI_GUARD_EOS( p->value.str_value, XI_VALUE_STRING_MAX_SIZE );
00127 
00128     // clean the counter
00129     size_t  counter = 0;
00130 
00131     // the transition function
00132     static const short states[][6][2] =
00133     {
00134           // state initial                          // state minus                            // state number                           // state float                            // state dot                              // string
00135         { { 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  } },
00136         { { 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  } },
00137         { { 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  } },
00138         { { 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  } },
00139         { { 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  } },
00140         { { 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 } },
00141         { { 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  } },
00142         { { 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  } }
00143     };
00144 
00145     char    c = *buffer;
00146     short   s = XI_STATE_INITIAL;
00147 
00148     while( c != '\n' && c !='\0' && c!='\r' )
00149     {
00150         if( counter >= XI_VALUE_STRING_MAX_SIZE - 1 )
00151         {
00152             xi_set_err( XI_DATAPOINT_VALUE_BUFFER_OVERFLOW );
00153             return 0;
00154         }
00155 
00156         xi_char_type_t ct = csv_classify_char( c );
00157         s = states[ ct ][ s ][ 1 ];
00158 
00159         switch( s )
00160         {
00161             case XI_STATE_MINUS:
00162             case XI_STATE_NUMBER:
00163             case XI_STATE_FLOAT:
00164             case XI_STATE_DOT:
00165             case XI_STATE_STRING:
00166                 p->value.str_value[ counter ] = c;
00167                 break;
00168         }
00169 
00170         c = *( ++buffer );
00171         ++counter;
00172     }
00173 
00174     // set the guard
00175     p->value.str_value[ counter ] = '\0';
00176 
00177     // update of the state for loose states...
00178     switch( s )
00179     {
00180         case XI_STATE_MINUS:
00181         case XI_STATE_DOT:
00182         case XI_STATE_INITIAL:
00183             s = XI_STATE_STRING;
00184             break;
00185     }
00186 
00187     switch( s )
00188     {
00189         case XI_STATE_NUMBER:
00190             p->value.i32_value  = atoi( p->value.str_value );
00191             p->value_type       = XI_VALUE_TYPE_I32;
00192             break;
00193         case XI_STATE_FLOAT:
00194             p->value.f32_value  = atof( p->value.str_value );
00195             p->value_type       = XI_VALUE_TYPE_F32;
00196             break;
00197         case XI_STATE_STRING:
00198         default:
00199             p->value_type       = XI_VALUE_TYPE_STR;
00200     }
00201 
00202     return p;
00203 }
00204 
00205 const char* csv_encode_datapoint( const xi_datapoint_t* data )
00206 {
00207 
00208     // PRECONDITIONS
00209     assert( data != 0 );
00210 
00211     return csv_encode_datapoint_in_place( XI_CSV_LOCAL_BUFFER, sizeof( XI_CSV_LOCAL_BUFFER ), data ) == -1 ? 0 : XI_CSV_LOCAL_BUFFER;
00212 }
00213 
00214 int csv_encode_datapoint_in_place(
00215       char* in, size_t in_size
00216     , const xi_datapoint_t* datapoint )
00217 {
00218     // PRECONDITIONS
00219     assert( in != 0 );
00220     assert( datapoint != 0 );
00221 
00222     int s       = 0;
00223     int size    = in_size;
00224     int offset  = 0;
00225 
00226     if( datapoint->timestamp.timestamp != 0 )
00227     {
00228         time_t stamp = datapoint->timestamp.timestamp;
00229         struct tm* gmtinfo = xi_gmtime( &stamp );
00230 
00231         s = snprintf( in, size
00232             , "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ,"
00233             , gmtinfo->tm_year + 1900
00234             , gmtinfo->tm_mon + 1
00235             , gmtinfo->tm_mday
00236             , gmtinfo->tm_hour
00237             , gmtinfo->tm_min
00238             , gmtinfo->tm_sec
00239             , ( int ) datapoint->timestamp.micro );
00240 
00241         XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN );
00242     }
00243 
00244     s = csv_encode_value( in + offset, size - offset, datapoint );
00245     XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN );
00246 
00247     s = snprintf( in + offset, size - offset, "%s", "\n" );
00248     XI_CHECK_S( s, size, offset, XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN );
00249 
00250     return offset;
00251 
00252 err_handling:
00253     return -1;
00254 }
00255 
00256 const char* csv_encode_create_datastream(
00257           const char* datastream_id
00258         , const xi_datapoint_t* data )
00259 {
00260     // PRECONDITIONS
00261     assert( data != 0 );
00262 
00263     int s = snprintf( XI_CSV_LOCAL_BUFFER, sizeof( XI_CSV_LOCAL_BUFFER )
00264             , "%s,%d\n", datastream_id, data->value.i32_value );
00265     int size = XI_CSV_BUFFER_SIZE;
00266 
00267     XI_CHECK_SIZE( s, size, XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN );
00268 
00269     return XI_CSV_LOCAL_BUFFER;
00270 
00271 err_handling:
00272     return 0;
00273 }
00274 
00275 xi_feed_t* csv_decode_feed(
00276       const char* buffer
00277     , xi_feed_t* feed )
00278 {
00279     const char* current     = buffer;
00280     int32_t counter         = 0;
00281 
00282     // find occurence of newline
00283     char* end_of_line = ( char* ) 1;
00284 
00285     // while we didn't jump out of the buffer
00286     while( end_of_line )
00287     {
00288         // get current datapoint
00289         xi_datastream_t* d    = &feed->datastreams[ counter ];
00290         d->datapoint_count    = 0;
00291         xi_datapoint_t* p     = &d->datapoints[ 0 ];
00292         memset( p, 0, sizeof( xi_datapoint_t ) );
00293 
00294         end_of_line = strstr( current, "\n" );
00295 
00296         const char* end_of_datastream_id  = strstr( current, "," );
00297         const char* beg_of_datapoint      = end_of_datastream_id + 1;
00298 
00299         XI_CHECK_ZERO( beg_of_datapoint, XI_CSV_DECODE_FEED_PARSER_ERROR );
00300         XI_CHECK_ZERO( end_of_datastream_id, XI_CSV_DECODE_FEED_PARSER_ERROR );
00301 
00302         int size = sizeof( d->datastream_id );
00303 
00304         int s = xi_str_copy_untiln( d->datastream_id, size
00305             , current, ',' );
00306         XI_CHECK_SIZE( s, size, XI_CSV_DECODE_FEED_PARSER_ERROR );
00307 
00308         xi_datapoint_t* ret = csv_decode_datapoint( beg_of_datapoint, p );
00309         XI_CHECK_ZERO( ret, XI_CSV_DECODE_FEED_PARSER_ERROR )
00310 
00311         d->datapoint_count = 1;
00312         current = end_of_line + 1;
00313 
00314         XI_CHECK_CND( ++counter == XI_MAX_DATASTREAMS
00315             , XI_CSV_DECODE_FEED_PARSER_ERROR );
00316     }
00317 
00318     feed->datastream_count = counter;
00319     return feed;
00320 
00321 err_handling:
00322     return 0;
00323 }
00324 
00325 xi_datapoint_t* csv_decode_datapoint(
00326       const char* buffer
00327     , xi_datapoint_t* datapoint )
00328 {
00329     // PRECONDITIONS
00330     assert( buffer != 0 );
00331     assert( datapoint != 0 );
00332 
00333     int ye, mo, da, h, m, s, ms;
00334 
00335     const char* beg_of_value = strstr( buffer, "," );
00336 
00337     // check continuation condition
00338     XI_CHECK_ZERO( beg_of_value, XI_CSV_DECODE_DATAPOINT_PARSER_ERROR );
00339 
00340     // move to pointer to the proper position
00341     beg_of_value += 1;
00342 
00343     {
00344         int n = sscanf( buffer
00345             , "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ"
00346             , &ye, &mo, &da, &h, &m, &s, &ms );
00347 
00348         // check if the parser worked correctly
00349         XI_CHECK_CND( n != 7, XI_CSV_DECODE_DATAPOINT_PARSER_ERROR );
00350 
00351         // copy parsed data
00352         {
00353             struct tm timeinfo;
00354 
00355             timeinfo.tm_year   = ye - 1900;
00356             timeinfo.tm_mon    = mo - 1;
00357             timeinfo.tm_mday   = da;
00358             timeinfo.tm_hour   = h;
00359             timeinfo.tm_min    = m;
00360             timeinfo.tm_sec    = s;
00361 
00362             time_t timestamp = xi_mktime( &timeinfo );
00363 
00364             XI_CHECK_CND( ( int )timestamp == -1, XI_CSV_TIME_CONVERTION_ERROR );
00365 
00366             datapoint->timestamp.timestamp  = timestamp;
00367             datapoint->timestamp.micro      = ms;
00368         }
00369     }
00370 
00371     xi_datapoint_t* r = csv_decode_value( beg_of_value
00372         , datapoint );
00373 
00374 
00375     XI_CHECK_ZERO( r, XI_CSV_DECODE_DATAPOINT_PARSER_ERROR );
00376 
00377     return datapoint;
00378 
00379 err_handling:
00380     return 0;
00381 }