Xively C library
Dependents: Application-xively-jumpstart-demo Application-xively-jumpstart-demo Modified_Xively_Jumpstart HW7-1_Xively_Thermostat
This is Xively C library, the code lives on GitHub.
See our example program and the tutorial, documentation can bee found here.
Revision 0:82702e998d3f, committed 2013-06-26
- Comitter:
- xively
- Date:
- Wed Jun 26 10:40:43 2013 +0000
- Commit message:
- libxively v0.1.1-rc0 (34c8b32)
Changed in this revision
diff -r 000000000000 -r 82702e998d3f src/libxively/comm_layer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/comm_layer.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,85 @@ +// 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 comm_layer.h + * \author Olgierd Humenczuk + * \brief Defines _communication layer_ abstraction interface + * + * The design of _communication layer_ was based on Berkley/POSIX socket API. + */ + +#ifndef __POSIX_COMM_LAYER_H__ +#define __POSIX_COMM_LAYER_H__ + + +#include <stdlib.h> +#include <stdint.h> + +#include "connection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief _The communication layer interface_ - contains function pointers, + * that's what we expose to the layers above and below + * + * It is effectively a class that holds declarations of pure virtual functions. + * + * The interface contain methods related to connecting/disconnecting as well as + * reading/writing data to/from the remote endpoint. + * + * This interface uses abstract `connection_t` class to hold information + * about the connection in a platform-independent fashion. + */ +typedef struct { + /** + * \brief Connect to a given host using it's address and port + * \note It should not reset an existing connection. + * + * \return Pointer to `connection_t` or `0` in case of an error. + */ + connection_t* ( *open_connection )( const char* address, int32_t port ); + + /** + * \brief Send data over the connection + * + * \return Number of bytes sent or `-1` in case of an error. + */ + int ( *send_data )( connection_t* conn, const char* data, size_t size ); + + /** + * \brief Read data from a connection + * + * \return Number of bytes read or `-1` in case of an error. + */ + int ( *read_data )( connection_t* conn, char* buffer, size_t buffer_size ); + + /** + * \brief Close connection and free all allocated memory (if any) + * \note Some implementations may be stack-based as only limited + * number of connections is expected in a typical use-case. + */ + void ( *close_connection )( connection_t* conn ); +} comm_layer_t; + + + /** + * \brief Initialise an implementation of the _communication layer_ + * + * This intialiser assigns function pointers to the actual implementations + * using static function variable trick, hence the intialisation should + * not give any overhead. + * + * \return Structure with function pointers for platform-specific communication + * methods (see `mbed_comm.h` and `posix_comm.h` for how it's done). + */ +const comm_layer_t* get_comm_layer( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __POSIX_COMM_LAYER_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/comm_layers/mbed/mbed_comm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/comm_layers/mbed/mbed_comm.cpp Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,184 @@ +// 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 mbed_comm.cpp + * \author Olgierd Humenczuk + * \brief Implements mbed _communication layer_ abstraction interface using [TCPSocketConnection](http://mbed.org/users/mbed_official/code/Socket/docs/tip/classTCPSocketConnection.html) [see comm_layer.h] + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <assert.h> + +#include "mbed_comm.h" +#include "comm_layer.h" +#include "xi_helpers.h" +#include "xi_allocator.h" +#include "mbed_comm_layer_data_specific.h" +#include "xi_err.h" +#include "xi_macros.h" +#include "xi_printf.h" +#include "xi_globals.h" + +extern "C" { + +connection_t* mbed_open_connection( const char* address, int32_t port ) +{ + // PRECONDITIONS + assert( address != 0 ); + + // variables + mbed_comm_layer_data_specific_t* pos_comm_data = 0; + connection_t* conn = 0; + + // allocation of the socket connector + TCPSocketConnection* socket_ptr = new TCPSocketConnection(); + XI_CHECK_MEMORY( socket_ptr ); + + // set the timeout for blocking operations + socket_ptr->set_blocking( true, xi_globals.network_timeout ); + + // allocate memory for the mbed data specific structure + pos_comm_data = ( mbed_comm_layer_data_specific_t* ) + xi_alloc( sizeof( mbed_comm_layer_data_specific_t ) ); + XI_CHECK_MEMORY( pos_comm_data ); + + // allocate memory for the connection layer + conn = ( connection_t* ) xi_alloc( + sizeof( connection_t ) ); + XI_CHECK_MEMORY( conn ); + + // clean the memory before the usage + memset( conn, 0, sizeof( connection_t ) ); + + // make copy of an address + conn->address = xi_str_dup( address ); + conn->port = port; + + XI_CHECK_MEMORY( conn->address ); + + { // to prevent the skip initializtion warning + pos_comm_data->socket_ptr = socket_ptr; + + // assign the layer specific data + conn->layer_specific = pos_comm_data; + + // try to connect + int s = pos_comm_data->socket_ptr->connect( address, port ); + + // check if not failed + if( s == -1 ) + { + xi_set_err( XI_SOCKET_CONNECTION_ERROR ); + goto err_handling; + } + } + + // POSTCONDITIONS + assert( conn != 0 ); + assert( pos_comm_data->socket_ptr != 0 ); + + return conn; + + // cleanup the memory +err_handling: + // safely destroy the object + if ( pos_comm_data && pos_comm_data->socket_ptr ) + { + delete pos_comm_data->socket_ptr; + pos_comm_data->socket_ptr = 0; + } + if( pos_comm_data ) { XI_SAFE_FREE( pos_comm_data ); } + if( conn ) { XI_SAFE_FREE( conn->address ); } + XI_SAFE_FREE( conn ); + + return 0; +} + +int mbed_send_data( connection_t* conn, const char* data, size_t size ) +{ + // PRECONDITIONS + assert( conn != 0 ); + assert( conn->layer_specific != 0 ); + assert( data != 0 ); + assert( size != 0 ); + + // extract the layer specific data + mbed_comm_layer_data_specific_t* pos_comm_data + = ( mbed_comm_layer_data_specific_t* ) conn->layer_specific; + + // Why not const char* ??? + int bytes_written = pos_comm_data->socket_ptr->send_all( ( char* ) data, size ); + + if( bytes_written == - 1 ) + { + xi_set_err( XI_SOCKET_WRITE_ERROR ); + } + + // store the value + conn->bytes_sent += bytes_written; + + return bytes_written; +} + +int mbed_read_data( connection_t* conn, char* buffer, size_t buffer_size ) +{ + // PRECONDITIONS + assert( conn != 0 ); + assert( conn->layer_specific != 0 ); + assert( buffer != 0 ); + assert( buffer_size != 0 ); + + // extract the layer specific data + mbed_comm_layer_data_specific_t* pos_comm_data + = ( mbed_comm_layer_data_specific_t* ) conn->layer_specific; + + memset( buffer, 0, buffer_size ); + pos_comm_data->socket_ptr->set_blocking(true, 10); + int bytes_read = pos_comm_data->socket_ptr->receive( buffer, buffer_size ); + + if( bytes_read == -1 ) + { + xi_set_err( XI_SOCKET_READ_ERROR ); + } + + // store the value + conn->bytes_received += bytes_read; + + return bytes_read; +} + +void mbed_close_connection( connection_t* conn ) +{ + // PRECONDITIONS + assert( conn != 0 ); + assert( conn->layer_specific != 0 ); + + // extract the layer specific data + mbed_comm_layer_data_specific_t* pos_comm_data + = ( mbed_comm_layer_data_specific_t* ) conn->layer_specific; + + // close the connection & the socket + if( pos_comm_data->socket_ptr->close() == -1 ) + { + xi_set_err( XI_SOCKET_CLOSE_ERROR ); + goto err_handling; + } + + // cleanup the memory +err_handling: + // safely destroy the object + if ( pos_comm_data && pos_comm_data->socket_ptr ) + { + delete pos_comm_data->socket_ptr; + pos_comm_data->socket_ptr = 0; + } + if( conn ) { XI_SAFE_FREE( conn->address ); } + if( conn ) { XI_SAFE_FREE( conn->layer_specific ); } + XI_SAFE_FREE( conn ); + return; +} + +}
diff -r 000000000000 -r 82702e998d3f src/libxively/comm_layers/mbed/mbed_comm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/comm_layers/mbed/mbed_comm.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,31 @@ +// 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 mbed_comm.h + * \author Olgierd Humenczuk + * \brief Implements mbed _communication layer_ functions [see comm_layer.h and mbed_comm.cpp] + */ + +#ifndef __MBED_COMM_H__ +#define __MBED_COMM_H__ + +#include "connection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +connection_t* mbed_open_connection( const char* address, int32_t port ); + +int mbed_send_data( connection_t* conn, const char* data, size_t size ); + +int mbed_read_data( connection_t* conn, char* buffer, size_t buffer_size ); + +void mbed_close_connection( connection_t* conn ); + +#ifdef __cplusplus +} +#endif + +#endif // __MBED_COMM_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/comm_layers/mbed/mbed_comm_layer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/comm_layers/mbed/mbed_comm_layer.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,27 @@ +// 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 mbed_comm_layer.c + * \author Olgierd Humenczuk + * \brief Implements mbed _communication layer_ abstraction interface [see comm_layer.h] + */ + +#include "comm_layer.h" +#include "mbed_comm.h" + + /** + * \brief Initialise POSIX implementation of the _communication layer_ + */ +const comm_layer_t* get_comm_layer() +{ + static comm_layer_t __mbed_comm_layer = + { + &mbed_open_connection + , &mbed_send_data + , &mbed_read_data + , &mbed_close_connection + }; + + return &__mbed_comm_layer; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/comm_layers/mbed/mbed_comm_layer_data_specific.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/comm_layers/mbed/mbed_comm_layer_data_specific.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,19 @@ +// 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 mbed_comm_layer_data_specific.h + * \author Olgierd Humenczuk + * \brief Declares layer-specific data structure + */ + +#ifndef __MBED_COMM_LAYER_DATA_SPECIFIC_H__ +#define __MBED_COMM_LAYER_DATA_SPECIFIC_H__ + +#include "EthernetInterface.h" + +typedef struct { + TCPSocketConnection* socket_ptr; +} mbed_comm_layer_data_specific_t; + +#endif // __MBED_COMM_LAYER_DATA_SPECIFIC_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/connection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/connection.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,49 @@ +// 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 connection.h + * \author Olgierd Humenczuk + * \brief Defines `connection_t`, which is used by the _communication layer_ + * + * It is designed to abstract from implementation- and/or platform- specific + * ways of storing connection info, such as host and port, as well as simple + * statistics counters. It also provides arbitary pointer to implementation's + * own strucutre. + */ + +#ifndef __CONNECTION_H__ +#define __CONNECTION_H__ + +#include "stdlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief _The connection structure_ - holds data needed for further processing + * and error handling + * + * It also contain `layer_specific` field which should point at the platform's + * structure, according to the implementation of that specific _communication_ + * _layer_ and other implementation don't need to care about what that is. + * + * The purpose of that class is to give the abstract interface of a connection + * that can be easly used with the `comm_layer_t` interface, so that it's possible + * to send/receive data to/from the server through different communication layer + * using the same interface. + */ +typedef struct { + void *layer_specific; //!< here the layer can hide some layer specific data + char *address; //!< here we store server's address + int port; //!< here we store server's port + size_t bytes_sent; //!< the data sent counter, just for testing and statistics + size_t bytes_received; //!< the data receive counter, just for tests and statistics +} connection_t; + +#ifdef __cplusplus +} +#endif + +#endif // __CONNECTION_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/csv_data.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/csv_data.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,389 @@ +// 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; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/csv_data.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/csv_data.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,40 @@ +// 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.h + * \author Olgierd Humenczuk + * \brief Implements CSV _data layer_ encoders and decoders specific to Xively CSV data format + */ + +#ifndef __CSV_DATA_H__ +#define __CSV_DATA_H__ + +#include "xively.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char* csv_encode_datapoint( const xi_datapoint_t* dp ); + + +int csv_encode_datapoint_in_place( + char* buffer, size_t buffer_size + , const xi_datapoint_t* datapoint ); + +const char* csv_encode_create_datastream( + const char* buffer + , const xi_datapoint_t* dp ); + +xi_feed_t* csv_decode_feed( + const char* buffer + , xi_feed_t* feed ); + +xi_datapoint_t* csv_decode_datapoint( const char* data, xi_datapoint_t* dp ); + +#ifdef __cplusplus +} +#endif + +#endif // __CSV_DATA_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/csv_data_layer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/csv_data_layer.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,23 @@ +// 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_layer.c + * \author Olgierd Humenczuk + * \brief Implements CSV _data layer_ abstration interface [see csv_data_layer.h and data_layer.h] + */ + +#include "csv_data_layer.h" + +const data_layer_t* get_csv_data_layer() +{ + static const data_layer_t __csv_data_layer = { + csv_encode_datapoint + , csv_encode_datapoint_in_place + , csv_encode_create_datastream + , csv_decode_feed + , csv_decode_datapoint + }; + + return &__csv_data_layer; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/csv_data_layer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/csv_data_layer.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,37 @@ +// 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_layer.h + * \author Olgierd Humenczuk + * \brief Implements CSV _data layer_ abstration interface + */ + +#ifndef __CSV_DATA_LAYER_H__ +#define __CSV_DATA_LAYER_H__ + +#include "xively.h" +#include "data_layer.h" +#include "csv_data.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * \brief Initialise CSV implementation of the _data layer_ + * + * This intialiser assigns function pointers to the actual implementations + * using static function variable trick, hence the intialisation should + * not give any overhead. + * + * \return Structure with function pointers for CSV encoders and decoders + * which had been implemented in `csv_data.c`. + */ +const data_layer_t* get_csv_data_layer( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __CSV_DATA_LAYER_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/data_layer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/data_layer.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,86 @@ +// 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 data_layer.h + * \author Olgierd Humenczuk + * \brief Defines _data layer_ abstraction interface + */ + +#ifndef __DATA_LAYER_H__ +#define __DATA_LAYER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief _The data layer interface_ - contains function pointers, + * that's what we expose to the layers above and below + * + * It is effectively a class that holds declarations of pure virtual functions. + * * All encoders take a given data type (e.g. `xi_feed_t`, `xi_datapoint_t`) and produce + * an encoded string in a buffer, which can be wrapped as payload to _transport layer_ + * or any other layer, such as _gzip_. + * * All decoders take a given data buffer and convert to an appropriate data type. + * + * \note The encoders and decoders do not have to be paired, e.g. `encode_feed` is actually + * implemented using `encode_datapoint_in_place` in a loop (see `http_encode_update_feed`), + * so we only need `decode_feed`. However, this presumption had been made with CSV _data_ + * _layer_ implementation and the interface may need to change if other data formats are + * to be implemented, but it is currently more important to have it the way it is. + * The layering model should allow for simple way of integrating anything, even if there + * might seem to be some inconsistency right now. + */ +typedef struct { + /** + * \brief This function converts `xi_datapoint_t` into an implementation-specific format + * for a datapoint. + * + * \return Pointer to the buffer where encoded string resides. + */ + const char* ( *encode_datapoint )( const xi_datapoint_t* dp ); + + /** + * \brief This function converts `xi_datapoint_t` into an implementation-specific format + * for a datapoint with output buffer given as an argument. + * + * \return Offset or -1 if an error occurred. + */ + int ( *encode_datapoint_in_place )( + char* buffer, size_t buffer_size + , const xi_datapoint_t* dp ); + + /** + * \brief This function converts `xi_datapoint_t` and a given datastream ID string + * into an implementation-specific format for creating datastreams. + + * \return Pointer to the buffer where encoded string resides. + */ + const char* ( *encode_create_datastream )( + const char* data + , const xi_datapoint_t* dp ); + + /** + * \brief This function converts from an implementation-specific format for a feed + * into `xi_feed_t` that is given as an argument. + * + * \return Pointer to feed structure or null if an error occurred. + */ + xi_feed_t* ( *decode_feed )( const char* data, xi_feed_t* feed ); + + /** + * \brief This function converts from an implementation-specific format for a datapoint + * into `xi_datapoint_t` that is given as an argument. + * + * \return Pointer to datastream structure or null if an error occurred. + + */ + xi_datapoint_t* ( *decode_datapoint )( const char* data, xi_datapoint_t* dp ); +} data_layer_t; + +#ifdef __cplusplus +} +#endif + +#endif // __DATA_LAYER_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/http_consts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_consts.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,20 @@ +// 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 http_consts.h + * \author Olgierd Humenczuk + * \brief Constants used by our HTTP parser. + */ + +#ifndef __HTTP_CONSTS_H__ +#define __HTTP_CONSTS_H__ + +static const char XI_HTTP_CRLF[] = "\r\n"; +static const char XI_HTTP_CRLFX2[] = "\r\n\r\n"; +static const char XI_HTTP_QUERY_GET[] = "GET"; +static const char XI_HTTP_QUERY_PUT[] = "PUT"; +static const char XI_HTTP_QUERY_POST[] = "POST"; +static const char XI_HTTP_QUERY_DELETE[] = "DELETE"; + +#endif // __HTTP_CONSTS_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/http_layer_parser.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_layer_parser.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,196 @@ +// 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 http_layer_parser.c + * \author Olgierd Humenczuk + * \brief Our simple HTTP parser [see http_layer_parser.h] + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "xively.h" +#include "xi_macros.h" +#include "http_consts.h" +#include "http_layer_parser.h" +#include "xi_debug.h" +#include "xi_err.h" + +static const char XI_HTTP_STATUS_PATTERN[] = + "HTTP/%d.%d %d %" XI_STR(XI_HTTP_STATUS_STRING_SIZE) "[^\r\n]\r\n"; //!< the match pattern + +#define SET_HTTP_STATUS_PATTERN(a,b,c,d) XI_HTTP_STATUS_PATTERN, &a, &b, &c, d + +static const char XI_HTTP_TOKEN_NAMES[ XI_HTTP_HEADERS_COUNT ][ XI_HTTP_HEADER_NAME_MAX_SIZE ] = + { + "date" // XI_HTTP_HEADER_DATE + , "content-type" // XI_HTTP_HEADER_CONTENT_TYPE + , "content-length" // XI_HTTP_HEADER_CONTENT_LENGTH + , "connection" // XI_HTTP_HEADER_CONNECTION + , "x-request-id" // XI_HTTP_HEADER_X_REQUEST_ID + , "cache-control" // XI_HTTP_HEADER_CACHE_CONTROL + , "age" // XI_HTTP_HEADER_AGE + , "vary" // XI_HTTP_HEADER_VARY + , "unknown" // XI_HTTP_HEADER_UNKNOWN, //!< !!!! this must be always on the last position + }; + +static inline http_header_type_t classify_header( const char* header ) +{ + for( unsigned short i = 0; i < XI_HTTP_HEADER_COUNT - 1; ++i ) + { + if( strcasecmp( header, XI_HTTP_TOKEN_NAMES[ i ] ) == 0 ) + return ( http_header_type_t ) i; + } + + return XI_HTTP_HEADER_UNKNOWN; +} + +const char* parse_http_status( http_response_t* response, const char* content ) +{ + // variables + int c = 0; + + // find the first occurrence of CRLF + const char* header_end_ptr = strstr( content, XI_HTTP_CRLF ); + + // check continuation condition + XI_CHECK_ZERO( header_end_ptr, XI_HTTP_STATUS_PARSE_ERROR ); + + // update the pointer + header_end_ptr += 2; + + // parse + c = sscanf( content + , SET_HTTP_STATUS_PATTERN( + response->http_version1 + , response->http_version2 + , response->http_status + , response->http_status_string ) ); + + // check if all arguments found + XI_CHECK_CND( c != 4, XI_HTTP_STATUS_PARSE_ERROR ); + + // return updated ptr + return header_end_ptr; + +err_handling: + return 0; +} + +const char* parse_http_header( http_response_t* response + , const char* content ) +{ + // find the first occurrence of CRLF + const char* header_end_ptr = strstr( content, XI_HTTP_CRLF ); + const char* header_name_end_ptr = strstr( content, ":" ); + + // check continuation condition + XI_CHECK_ZERO( header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); + XI_CHECK_ZERO( header_name_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); + XI_CHECK_CND( header_name_end_ptr > header_end_ptr, XI_HTTP_HEADER_PARSE_ERROR ); + + { + int size = sizeof( response->http_headers[ response->http_headers_size ].name ); + + XI_CHECK_CND( header_name_end_ptr - content > size - 1, XI_HTTP_HEADER_PARSE_ERROR ); + + memcpy( response->http_headers[ response->http_headers_size ].name + , content, XI_MIN( size - 1, header_name_end_ptr - content ) ); + + // set the guard + XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].name, size ); + } + + // update the pointer + header_name_end_ptr += 2; + + { + int size = sizeof( response->http_headers[ response->http_headers_size ].value ); + + XI_CHECK_CND( header_end_ptr - header_name_end_ptr > size - 1, XI_HTTP_HEADER_PARSE_ERROR ); + + memcpy( response->http_headers[ response->http_headers_size ].value + , header_name_end_ptr, XI_MIN( size - 1, header_end_ptr - header_name_end_ptr ) ); + + // set the guard + XI_GUARD_EOS( response->http_headers[ response->http_headers_size ].value, size ); + } + + // @TODO change the complexity of the classify header + // implementation now it's O( n*m ) it can be done in O( n ) + // using some sort of simple DFA + // I'm working on a very simple Python DFA generator for C + // but still this is a w.i.p. at the moment + + // parse the header name + { + http_header_type_t ht = classify_header( + response->http_headers[ response->http_headers_size ].name ); + + // accept headers that differs from unknown + if( ht != XI_HTTP_HEADER_UNKNOWN ) + { + response->http_headers_checklist[ ht ] = + &response->http_headers[ response->http_headers_size ]; + } + + // set header type + response->http_headers[ response->http_headers_size ].header_type = ht; + + // increment headers size and check if it's okay + response->http_headers_size += 1; + + XI_CHECK_CND( response->http_headers_size >= XI_HTTP_MAX_HEADERS + , XI_HTTP_HEADER_PARSE_ERROR ); + + // update the pointer + header_end_ptr += sizeof( XI_HTTP_CRLF ) - 1; + } + + return header_end_ptr; + +err_handling: + return 0; +} + +http_response_t* parse_http( http_response_t* response, const char* content ) +{ + memset( response, 0, sizeof( http_response_t ) ); + + // remember where is the end of header section + const char* ptr_to_headers_end = strstr( content, XI_HTTP_CRLFX2 ); + + // check the continuation condition + XI_CHECK_ZERO( ptr_to_headers_end, XI_HTTP_PARSE_ERROR ); + + // update the pointer + ptr_to_headers_end += sizeof( XI_HTTP_CRLF ) - 1; + + const char* payload_begin = ptr_to_headers_end + sizeof( XI_HTTP_CRLF ) - 1; + + XI_CHECK_ZERO( payload_begin, XI_HTTP_PARSE_ERROR ); + + // parse status + const char* ptr = parse_http_status( response, content ); + + // check the continuation condition + XI_CHECK_ZERO( ptr, XI_HTTP_PARSE_ERROR ); + + // read the headers + while( ( ptr = parse_http_header( response, ptr ) ) != '\0' + && ptr != ptr_to_headers_end ); + + // if there was an error, forward it + xi_err_t e = xi_get_last_error(); + XI_CHECK_CND( e != XI_NO_ERR, e ); + + // just copy the content + strncpy( response->http_content, payload_begin, sizeof( response->http_content ) ); + + return response; + +err_handling: + return 0; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/http_layer_parser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_layer_parser.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,36 @@ +// 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 http_layer_parser.h + * \author Olgierd Humenczuk + * \brief Our simple HTTP parser + */ + +#ifndef __HTTP_LAYER_PARSER_H__ +#define __HTTP_LAYER_PARSER_H__ + +#include "xi_macros.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief This function takes the pointer to the `http_response_t` structure and + * fills it with parsed data from the give buffer. + * + * While the parser looks at headers, satus line and content, it populates given + * pointer to `http_response_t`. + * + * \return Pointer or null if an error occurred. + * + * \note It currently won't work against partial data. + */ +http_response_t* parse_http( http_response_t* response, const char* data ); + +#ifdef __cplusplus +} +#endif + +#endif
diff -r 000000000000 -r 82702e998d3f src/libxively/http_layer_queries.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_layer_queries.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,276 @@ +// 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 http_layer_queries.h + * \author Olgierd Humenczuk + * \brief Helpers for making HTTP requests (specific to Xively REST/HTTP API) [see http_layer_queries.h] + */ + +#include <stdio.h> +#include <assert.h> + +#include "http_layer_queries.h" +#include "xi_macros.h" +#include "xi_err.h" +#include "xi_consts.h" + + +static const char XI_HTTP_TEMPLATE_FEED[] = "%s /v2/feeds%s.csv%s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n" + "X-ApiKey: %s\r\n"; + +static const char XI_HTTP_ID_TEMPLATE[] = "/%s"; +static const char XI_HTTP_ID_TEMPLATE_D[] = "/%d"; + +static const char XI_HTTP_CONTENT_TEMPLATE[] = "Content-Type: text/plain\r\n" + "Content-Length: %d\r\n"; + +static char XI_QUERY_BUFFER[ XI_QUERY_BUFFER_SIZE ]; +static char XI_CONTENT_BUFFER[ XI_CONTENT_BUFFER_SIZE ]; +static char XI_ID_BUFFER[ XI_ID_BUFFER_SIZE ]; + +inline static int http_construct_string( + char* buffer, size_t buffer_size + , const char* string_id ) +{ + // PRECONDITIONS + assert( buffer != 0 ); + assert( buffer_size != 0 ); + + int s = 0; + int size = buffer_size; + + if( string_id ) + { + s = snprintf( buffer, size + , XI_HTTP_ID_TEMPLATE, string_id ); + + XI_CHECK_SIZE( s, size, XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ); + } + + return s; + +err_handling: + return -1; +} + +inline static int http_construct_int( + char* buffer, size_t buffer_size + , const int32_t* int_id ) +{ + // PRECONDITIONS + assert( buffer != 0 ); + assert( buffer_size != 0 ); + + int s = 0; + int size = buffer_size; + + if( int_id ) + { + s = snprintf( buffer, size + , XI_HTTP_ID_TEMPLATE_D, *int_id ); + + XI_CHECK_SIZE( s, size, XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ) + } + + return s; + +err_handling: + return -1; +} + +inline static int http_construct_feed( + char* buffer, size_t buffer_size + , const int32_t* feed_id ) +{ + // PRECONDITIONS + assert( buffer != 0 ); + assert( buffer_size != 0 ); + + if( feed_id ) + { + return http_construct_int( buffer + , buffer_size, feed_id ); + } + + return 0; +} + +inline static int http_construct_datastream( + char* buffer, size_t buffer_size + , const int32_t* feed_id + , const char* datastream_id ) +{ + // PRECONDITIONS + assert( buffer != 0 ); + assert( buffer_size != 0 ); + + int s = http_construct_feed( buffer, buffer_size, feed_id ); + int offset = 0; + int size = buffer_size; + + XI_CHECK_S( s, size, offset, XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ); + + s = http_construct_string( buffer + offset, buffer_size - offset + , "datastreams" ); + + XI_CHECK_S( s, size, offset, XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ); + + if( datastream_id ) + { + return http_construct_string( buffer + offset, buffer_size - offset + , datastream_id == 0 ? "" : datastream_id ); + } + + return offset; + +err_handling: + return -1; +} + +inline static int http_construct_datapoint( + char* buffer, size_t buffer_size + , const int32_t* feed_id + , const char* datastream_id + , const char* datapoint ) +{ + // PRECONDITIONS + assert( buffer != 0 ); + assert( buffer_size != 0 ); + + int s = http_construct_datastream( buffer, buffer_size + , feed_id, datastream_id ); + int offset = 0; + int size = buffer_size; + + XI_CHECK_S( s, size, offset + , XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ); + + s = http_construct_string( buffer + offset, buffer_size - offset + , "datapoints" ); + + XI_CHECK_S( s, size, offset + , XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ); + + if( datapoint ) + { + return http_construct_string( buffer + offset, buffer_size - offset + , datapoint == 0 ? "" : datapoint ); + } + + return offset; + +err_handling: + return -1; +} + +inline static const char* http_construct_http_query( + const char* http_method + , const char* id + , const char* query_suffix + , const char* x_api_key ) +{ + // PRECONDITIONS + assert( http_method != 0 ); + assert( x_api_key != 0 ); + + int s = snprintf( XI_QUERY_BUFFER, XI_QUERY_BUFFER_SIZE, XI_HTTP_TEMPLATE_FEED + , http_method, id == 0 ? "" : id, query_suffix == 0 ? "" : query_suffix + , XI_HOST, XI_USER_AGENT, x_api_key ); + + XI_CHECK_SIZE( s, XI_QUERY_BUFFER_SIZE + , XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN ); + + return XI_QUERY_BUFFER; + +err_handling: + return 0; +} + +const char* http_construct_request_datapoint( + const char* http_method + , const int32_t* feed_id + , const char* datastream + , const char* datapoint + , const char* x_api_key ) +{ + // PRECONDITIONS + assert( http_method != 0 ); + assert( feed_id != 0 ); + assert( datastream != 0 ); + assert( x_api_key != 0 ); + + int s = http_construct_datapoint( XI_ID_BUFFER, XI_ID_BUFFER_SIZE + , feed_id, datastream, datapoint ); + + XI_CHECK_SIZE( s, XI_ID_BUFFER_SIZE + , XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN ); + + return http_construct_http_query( http_method, XI_ID_BUFFER, 0, x_api_key ); + +err_handling: + return 0; +} + +const char* http_construct_request_datastream( + const char* http_method + , const int32_t* feed_id + , const char* datastream + , const char* x_api_key ) +{ + // PRECONDITIONS + assert( http_method != 0 ); + assert( feed_id != 0 ); + assert( x_api_key != 0 ); + + int s = http_construct_datastream( XI_ID_BUFFER, XI_ID_BUFFER_SIZE + , feed_id, datastream ); + + XI_CHECK_SIZE( s, XI_ID_BUFFER_SIZE + , XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN ) + + return http_construct_http_query( http_method, XI_ID_BUFFER, 0, x_api_key ); + +err_handling: + return 0; +} + +const char* http_construct_request_feed( + const char* http_method + , const int32_t* feed_id + , const char* x_api_key + , const char* query_suffix ) +{ + // PRECONDITIONS + assert( http_method != 0 ); + assert( x_api_key != 0 ); + + int s = http_construct_feed( XI_ID_BUFFER, XI_ID_BUFFER_SIZE, feed_id ); + + XI_CHECK_SIZE( s, XI_ID_BUFFER_SIZE + , XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN ); + + return http_construct_http_query( http_method, XI_ID_BUFFER, query_suffix, x_api_key ); + +err_handling: + return 0; +} + +const char* http_construct_content( + int32_t content_size ) +{ + + int s = snprintf( XI_CONTENT_BUFFER, XI_CONTENT_BUFFER_SIZE + , XI_HTTP_CONTENT_TEMPLATE, content_size ); + + XI_CHECK_SIZE( s, XI_CONTENT_BUFFER_SIZE + , XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN ); + + return XI_CONTENT_BUFFER; + +err_handling: + return 0; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/http_layer_queries.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_layer_queries.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,50 @@ +// 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 http_layer_queries.c + * \author Olgierd Humenczuk + * \brief Helpers for making HTTP requests (specific to Xively REST/HTTP API) + * + * * All functions return pointer to the buffer with request string or null in case of any error. + + * \warning The buffer is managed by the library, so it's forbidden to free the pointer. + */ + +#ifndef __HTTP_LAYERS_QUERIES_H__ +#define __HTTP_LAYERS_QUERIES_H__ + +#include <stdlib.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +const char* http_construct_request_datapoint( + const char* http_method + , const int32_t* feed_id + , const char* datastream_id + , const char* dp_ts_str + , const char* x_api_key ); + +const char* http_construct_request_datastream( + const char* http_method + , const int32_t* feed_id + , const char* datastream_id + , const char* x_api_key ); + +const char* http_construct_request_feed( + const char* http_method + , const int32_t* feed_id + , const char* x_api_key + , const char* query_suffix ); + +const char* http_construct_content( + int32_t content_size ); + +#ifdef __cplusplus +} +#endif + +#endif // __HTTP_LAYERS_QUERIES_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/http_transport.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_transport.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,29 @@ +// 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 http_transport.c + * \author Olgierd Humenczuk + * \brief Implements HTTP _transport layer_ abstraction interface [see http_transport.h and transport_layer.h] + */ + +#include "http_transport_layer.h" +#include "http_transport.h" + +transport_layer_t* get_http_transport_layer( void ) +{ + static transport_layer_t __http_transport_layer = + { + &http_encode_update_feed + , &http_encode_get_feed + , &http_encode_create_datastream + , &http_encode_update_datastream + , &http_encode_get_datastream + , &http_encode_delete_datastream + , &http_encode_delete_datapoint + , &http_encode_datapoint_delete_range + , &http_decode_reply + }; + + return &__http_transport_layer; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/http_transport.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_transport.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,35 @@ +// 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 http_transport.h + * \author Olgierd Humenczuk + * \brief Implements HTTP _transport layer_ abstraction interface + */ + +#ifndef __HTTP_TRANSPORT_H__ +#define __HTTP_TRANSPORT_H__ + +#include "transport_layer.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * \brief Initialise HTTP implementation of the _transport layer_ + * + * This intialiser assigns function pointers to the actual implementations + * using static function variable trick, hence the intialisation should + * not give any overhead. + * + * \return Structure with function pointers for HTTP encoders and decoders + * which had been implemented in `http_transport_layer.c`. + */ +transport_layer_t* get_http_transport_layer( void ); + +#ifdef __cplusplus +} +#endif + +#endif // __HTTP_TRANSPORT_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/http_transport_layer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_transport_layer.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,432 @@ +// 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 http_transport_layer.c + * \author Olgierd Humenczuk + * \brief Implements HTTP _transport layer_ encoders and decoders specific to Xively REST/HTTP API [see http_transport_layer.h] + */ + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "http_transport.h" +#include "http_layer_queries.h" +#include "http_consts.h" +#include "http_layer_parser.h" +#include "xi_macros.h" +#include "xi_helpers.h" +#include "xi_err.h" + +static char XI_HTTP_QUERY_BUFFER[ XI_QUERY_BUFFER_SIZE + XI_CONTENT_BUFFER_SIZE ]; +static char XI_HTTP_QUERY_DATA[ XI_CONTENT_BUFFER_SIZE ]; + + +inline static char* http_encode_concat( char* buffer, size_t buffer_size + , const char* query, const char* content, const char* data ) +{ + int offset = 0; + int size = ( int ) buffer_size; + int s = 0; + + s = snprintf( buffer, size, "%s", query ); + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_CREATE_DATASTREAM ); + + if( content != 0 ) + { + s = snprintf( buffer + offset + , XI_MAX( size - offset, 0 ) + , "%s", content ); + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_CREATE_DATASTREAM ); + } + + s = snprintf( buffer + offset + , XI_MAX( size - offset, 0 ) + , "%s", XI_HTTP_CRLF ); + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_CREATE_DATASTREAM ); + + + if( content != 0 && data != 0 ) + { + s = snprintf( buffer + offset + , XI_MAX( size - offset, 0 ) + , "%s", data ); + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_CREATE_DATASTREAM ); + } + + s = snprintf( buffer + offset + , XI_MAX( size - offset, 0 ) + , "%s", XI_HTTP_CRLF ); + + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_CREATE_DATASTREAM ); + + return buffer; + +err_handling: + return 0; +} + +const char* http_encode_create_datastream( + const data_layer_t* data_transport + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id + , const xi_datapoint_t* datapoint ) +{ + // prepare parts + const char* data = data_transport->encode_create_datastream( + datastream_id, datapoint ); + + if( data == 0 ) { return 0; } + + const char* query = http_construct_request_datastream( + XI_HTTP_QUERY_POST, &feed_id + , 0, x_api_key + ); + + if( query == 0 ) { return 0; } + + const char* content = http_construct_content( strlen( data ) ); + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, content, data ); +} + +const char* http_encode_update_datastream( + const data_layer_t* data_layer + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id + , const xi_datapoint_t* datapoint ) +{ + // prepare parts + const char* data = data_layer->encode_datapoint( datapoint ); + + if( data == 0 ) { return 0; } + + const char* query = http_construct_request_datastream( + XI_HTTP_QUERY_PUT + , &feed_id + , datastream_id + , x_api_key + ); + + if( query == 0 ) { return 0; } + + const char* content = http_construct_content( strlen( data ) ); + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, content, data ); +} + +const char* http_encode_get_datastream( + const data_layer_t* data_layer + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id ) +{ + XI_UNUSED( data_layer ); + + // prepare parts + const char* query = http_construct_request_datastream( + XI_HTTP_QUERY_GET + , &feed_id + , datastream_id + , x_api_key ); + + if( query == 0 ) { return 0; } + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, 0, 0 ); +} + +const char* http_encode_delete_datastream( + const data_layer_t* data_layer + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id ) +{ + XI_UNUSED( data_layer ); + + // prepare parts + const char* query = http_construct_request_datastream( + XI_HTTP_QUERY_DELETE + , &feed_id + , datastream_id + , x_api_key ); + + if( query == 0 ) { return 0; } + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, 0, 0 ); +} + +const char* http_encode_delete_datapoint( + const data_layer_t* data_transport + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id + , const xi_datapoint_t* o ) +{ + XI_UNUSED( data_transport ); + + struct tm* ptm = xi_gmtime( + ( time_t* ) &o->timestamp.timestamp ); + + int s = snprintf( XI_HTTP_QUERY_BUFFER + , sizeof( XI_HTTP_QUERY_BUFFER ) + , "%s/datapoints/%04d-%02d-%02dT%02d:%02d:%02d.%06ldZ" + , datastream_id + , ptm->tm_year + 1900 + , ptm->tm_mon + 1 + , ptm->tm_mday + , ptm->tm_hour + , ptm->tm_min + , ptm->tm_sec + , o->timestamp.micro ); + + XI_CHECK_SIZE( s, ( int ) sizeof( XI_HTTP_QUERY_BUFFER ) + , XI_HTTP_ENCODE_DELETE_DATAPOINT ); + + { + // prepare parts + const char* query = http_construct_request_datastream( + XI_HTTP_QUERY_DELETE + , &feed_id + , XI_HTTP_QUERY_BUFFER + , x_api_key ); + + if( query == 0 ) { return 0; } + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, 0, 0 ); + } + +err_handling: + return 0; +} + +const char* http_encode_update_feed( + const data_layer_t* data_layer + , const char* x_api_key + , const xi_feed_t* feed ) +{ + // prepare buffer + XI_CLEAR_STATIC_BUFFER( XI_HTTP_QUERY_DATA ); + + // PRECONDITIONS + assert( data_layer != 0 ); + assert( x_api_key != 0 ); + assert( feed != 0 ); + + // variables initialization + const char* content = 0; + const char* query = 0; + + { // data part preparation + int offset = 0; + int size = sizeof( XI_HTTP_QUERY_DATA ); + int s = 0; + + // for each datastream + // generate the list of datapoints that you want to update + for( size_t i = 0; i < feed->datastream_count; ++i ) + { + const xi_datastream_t* curr_datastream = &feed->datastreams[ i ]; + + // for each datapoint + for( size_t j = 0; j < curr_datastream->datapoint_count; ++j ) + { + const xi_datapoint_t* curr_datapoint + = &curr_datastream->datapoints[ j ]; + + // add the datastream id to the buffer + s = snprintf( + XI_HTTP_QUERY_DATA + offset, XI_MAX( size - offset, 0 ), "%s," + , curr_datastream->datastream_id ); + + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_UPDATE_FEED ); + + // add the datapoint data to the buffer + s = data_layer->encode_datapoint_in_place( + XI_HTTP_QUERY_DATA + offset, XI_MAX( size - offset, 0 ) + , curr_datapoint ); + + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_UPDATE_FEED ); + } + } + } + + query = http_construct_request_feed( + XI_HTTP_QUERY_PUT + , &feed->feed_id + , x_api_key + , 0 + ); + + if( query == 0 ) { goto err_handling; } + + content = http_construct_content( strlen( XI_HTTP_QUERY_DATA ) ); + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, content, XI_HTTP_QUERY_DATA ); + +err_handling: + return 0; +} + +const char* http_encode_get_feed( + const data_layer_t* data_layer + , const char* x_api_key + , const xi_feed_t* feed ) +{ + // prepare buffer + XI_CLEAR_STATIC_BUFFER( XI_HTTP_QUERY_DATA ); + + // PRECONDITIONS + assert( data_layer != 0 ); + assert( x_api_key != 0 ); + assert( feed != 0 ); + + // variables initialization + const char* query = 0; + + { // data part preparation + int offset = 0; + int size = sizeof( XI_HTTP_QUERY_DATA ); + int s = 0; + + // for each datastream + // generate the list of datastreams that you want to get + for( size_t i = 0; i < feed->datastream_count; ++i ) + { + const xi_datastream_t* curr_datastream = &feed->datastreams[ i ]; + + // add the datastream id to the buffer + s = snprintf( + XI_HTTP_QUERY_DATA + offset, XI_MAX( size - offset, 0 ), i == 0 ? "?datastreams=%s" : ",%s" + , curr_datastream->datastream_id ); + + XI_CHECK_S( s, size, offset, XI_HTTP_ENCODE_UPDATE_FEED ); + } + } + + query = http_construct_request_feed( + XI_HTTP_QUERY_GET + , &feed->feed_id + , x_api_key + , XI_HTTP_QUERY_DATA + ); + + if( query == 0 ) { goto err_handling; } + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, 0, 0 ); + +err_handling: + return 0; +} + +const char* http_encode_datapoint_delete_range( + const data_layer_t* data_layer + , const char* x_api_key + , int32_t feed_id + , const char* datastream_id + , const xi_timestamp_t* start + , const xi_timestamp_t* end ) +{ + XI_UNUSED( data_layer ); + + struct tm stm; + struct tm etm; + + { + struct tm* tmp = start ? xi_gmtime( + ( time_t* ) &start->timestamp ) : 0; + memcpy( &stm, tmp, sizeof( struct tm ) ); + } + + { + struct tm* tmp = end ? xi_gmtime( + ( time_t* ) &end->timestamp ) : 0; + memcpy( &etm, tmp, sizeof( struct tm ) ); + } + + int s = 0; + + if( start && end ) + { + s = snprintf( XI_HTTP_QUERY_BUFFER + , sizeof( XI_HTTP_QUERY_BUFFER ) + , "%s/datapoints?start=%04d-%02d-%02dT%02d:%02d:%02d.%06ldZ&end=%04d-%02d-%02dT%02d:%02d:%02d.%06ldZ" + , datastream_id + , stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday + , stm.tm_hour, stm.tm_min, stm.tm_sec, start->micro + , etm.tm_year + 1900, etm.tm_mon + 1, etm.tm_mday + , etm.tm_hour, etm.tm_min, etm.tm_sec, end->micro ); + } + else if( start ) + { + s = snprintf( XI_HTTP_QUERY_BUFFER + , sizeof( XI_HTTP_QUERY_BUFFER ) + , "%s/datapoints?start=%04d-%02d-%02dT%02d:%02d:%02d.%06ldZ" + , datastream_id + , stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday + , stm.tm_hour, stm.tm_min, stm.tm_sec, start->micro ); + } + else if( end ) + { + s = snprintf( XI_HTTP_QUERY_BUFFER + , sizeof( XI_HTTP_QUERY_BUFFER ) + , "%s/datapoints?end=%04d-%02d-%02dT%02d:%02d:%02d.%06ldZ" + , datastream_id + , etm.tm_year + 1900, etm.tm_mon + 1, etm.tm_mday + , etm.tm_hour, etm.tm_min, etm.tm_sec, end->micro ); + } + else + { + // just set an error + XI_CHECK_CND( 1 == 0, XI_HTTP_ENCODE_DELETE_RANGE_DATAPOINT ); + } + + XI_CHECK_SIZE( s, ( int ) sizeof( XI_HTTP_QUERY_BUFFER ) + , XI_HTTP_ENCODE_DELETE_RANGE_DATAPOINT ); + + { + // prepare parts + const char* query = http_construct_request_datastream( + XI_HTTP_QUERY_DELETE + , &feed_id + , XI_HTTP_QUERY_BUFFER + , x_api_key ); + + if( query == 0 ) { return 0; } + + return http_encode_concat( XI_HTTP_QUERY_BUFFER, sizeof( XI_HTTP_QUERY_BUFFER ) + , query, 0, 0 ); + } + +err_handling: + return 0; +} + + +const xi_response_t* http_decode_reply( + const data_layer_t* data_layer + , const char* response ) +{ + XI_UNUSED( data_layer ); + + static xi_response_t __tmp; + static http_response_t* __response = &__tmp.http; + + // just pass it further + if( parse_http( __response, response ) == 0 ) + { + return 0; + } + + // pass it to the data_layer + return &__tmp; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/http_transport_layer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/http_transport_layer.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,79 @@ +// 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 http_transport_layer.h + * \author Olgierd Humenczuk + * \brief Implements HTTP _transport layer_ encoders and decoders specific to Xively REST/HTTP API + */ + +#ifndef __HTTP_TRANSPORT_LAYER_H__ +#define __HTTP_TRANSPORT_LAYER_H__ + +#include "xively.h" +#include "data_layer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char* http_encode_create_datastream( + const data_layer_t* + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id + , const xi_datapoint_t* value ); + +const char* http_encode_update_datastream( + const data_layer_t* + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id + , const xi_datapoint_t* value ); + +const char* http_encode_get_datastream( + const data_layer_t* + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id ); + +const char* http_encode_delete_datastream( + const data_layer_t* + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id ); + +const char* http_encode_delete_datapoint( + const data_layer_t* + , const char* x_api_key + , int32_t feed_id + , const char *datastream_id + , const xi_datapoint_t* o ); + +const char* http_encode_update_feed( + const data_layer_t* + , const char* x_api_key + , const xi_feed_t* feed ); + +const char* http_encode_get_feed( + const data_layer_t* + , const char* x_api_key + , const xi_feed_t* feed ); + +const char* http_encode_datapoint_delete_range( + const data_layer_t* + , const char* x_api_key + , int feed_id + , const char* datastream_id + , const xi_timestamp_t* start + , const xi_timestamp_t* end ); + +const xi_response_t* http_decode_reply( + const data_layer_t* + , const char* data ); + +#ifdef __cplusplus +} +#endif + +#endif // __HTTP_TRANSPORT_LAYER_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/transport_layer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/transport_layer.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,82 @@ +// 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 transport_layer.h + * \author Olgierd Humenczuk + * \brief Defines _transport layer_ abstraction interface + */ + +#ifndef __TRANSPORT_LAYER_H__ +#define __TRANSPORT_LAYER_H__ + +#include "xively.h" +#include "data_layer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief _The transport layer interface_ - contains function pointers, + * that's what we expose to the layers above and below + * + * It is effectively a class that holds declarations of pure virtual functions. + * * All functions take `data_layer_t` as the first argument. + * * Most encoders convert the result from data layer to an implementation-specific representaion. + * + * \note It depends on the implementation whether any given _transport layer_ method will call upon + * the _data layer_. In `http_transport_layer.c` you can see that `http_encode_datapoint_delete_range()` + * only needs to generate request headers and no body needs to be posted, neither it gets any + * response body as it is doing a `DELETE` request. One should use `XI_UNUSED(data_layer)` at the + * top of the function definition, that is a macro that casts the pointer to unused _data layer_ to void. + * \note Similarly to the _data layer_ (see notes in `data_layer.h`), there no symmetry needed and we only have + * one decoder. + */ +typedef struct { + const char* ( *encode_update_feed )( + const data_layer_t*, const char* api_key + , const xi_feed_t* feed ); + + const char* ( *encode_get_feed )( + const data_layer_t*, const char* api_key + , const xi_feed_t* feed ); + + const char* ( *encode_create_datastream )( + const data_layer_t*, const char* api_key, int32_t feed_id + , const char* datastream_id + , const xi_datapoint_t* dp ); + + const char* ( *encode_update_datastream )( + const data_layer_t*, const char* api_key, int32_t feed_id + , const char* datastream_id + , const xi_datapoint_t* value ); + + const char* ( *encode_get_datastream )( + const data_layer_t*, const char* api_key, int32_t feed_id + , const char* datastream_id ); + + const char* ( *encode_delete_datastream )( + const data_layer_t*, const char* api_key, int32_t feed_id + , const char* datastream_id ); + + const char* ( *encode_delete_datapoint )( + const data_layer_t*, const char* api_key, int32_t feed_id + , const char* datastream_id + , const xi_datapoint_t* datapoint ); + + const char* ( *encode_datapoint_delete_range )( + const data_layer_t*, const char* api_key, int32_t feed_id + , const char* datastream_id + , const xi_timestamp_t* start + , const xi_timestamp_t* end ); + + const xi_response_t* ( *decode_reply )( + const data_layer_t*, const char* data ); +} transport_layer_t; + +#ifdef __cplusplus +} +#endif + +#endif // __TRANSPORT_LAYER_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_allocator.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_allocator.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,21 @@ +// 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 xi_allocator.c + * \author Olgierd Humenczuk + * \brief Our custom `alloc()` and `free()` [see xi_allocator.h] + */ + +#include <stdlib.h> +#include "xi_allocator.h" + +void* xi_alloc( size_t b ) +{ + return ( void* ) malloc( b ); +} + +void xi_free( void* p ) +{ + free( p ); +}
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_allocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_allocator.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,38 @@ +// 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 xi_allocator.h + * \author Olgierd Humenczuk + * \brief Our custom `alloc()` and `free()` + * + * This is a faced built for future use when limitation of certain embedded + * devices require custom memory management, e.g. pooling and leak detection. + */ + +#ifndef __XI_ALLOCATOR_H__ +#define __XI_ALLOCATOR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Behaves like usual `malloc()`, which is supposed to allocate a chunk + * of memory and return the pointer to it. + * + * \return Pointer to allocated memory or null in case of any error. + */ +void* xi_alloc( size_t bytes ); + +/** + * \brief Behaves like usual `free()`, it frees previously allocated chunk of + * of memory. + */ +void xi_free( void* pointer ); + +#ifdef __cplusplus +} +#endif + +#endif
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_consts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_consts.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,82 @@ +// 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 xi_consts.h + * \author Olgierd Humenczuk + * \brief Constants that user may wish to override based on their needs + * + * \warning The meaning of each of these constants below is undocumented, + * so the user actually goes to read the source code and figure + * out what exactly they are doing. + */ + +#ifndef __XI_CONSTST_H__ +#define __XI_CONSTST_H__ + +#ifndef XI_HTTP_MAX_HEADERS +#define XI_HTTP_MAX_HEADERS 16 +#endif + +#ifndef XI_HTTP_STATUS_STRING_SIZE +#define XI_HTTP_STATUS_STRING_SIZE 32 +#endif + +#ifndef XI_HTTP_HEADER_NAME_MAX_SIZE +#define XI_HTTP_HEADER_NAME_MAX_SIZE 64 +#endif + +#ifndef XI_HTTP_HEADER_VALUE_MAX_SIZE +#define XI_HTTP_HEADER_VALUE_MAX_SIZE 64 +#endif + +#ifndef XI_HTTP_MAX_CONTENT_SIZE +#define XI_HTTP_MAX_CONTENT_SIZE 512 +#endif + +#ifndef XI_MAX_DATASTREAMS +#define XI_MAX_DATASTREAMS 16 +#endif + +#ifndef XI_MAX_DATAPOINTS +#define XI_MAX_DATAPOINTS 16 +#endif + +#ifndef XI_MAX_DATASTREAM_NAME +#define XI_MAX_DATASTREAM_NAME 16 +#endif + +#ifndef XI_VALUE_STRING_MAX_SIZE +#define XI_VALUE_STRING_MAX_SIZE 32 +#endif + +#ifndef XI_PRINTF_BUFFER_SIZE +#define XI_PRINTF_BUFFER_SIZE 512 +#endif + +#ifndef XI_QUERY_BUFFER_SIZE +#define XI_QUERY_BUFFER_SIZE 512 +#endif + +#ifndef XI_ID_BUFFER_SIZE +#define XI_ID_BUFFER_SIZE 256 +#endif + +#ifndef XI_CONTENT_BUFFER_SIZE +#define XI_CONTENT_BUFFER_SIZE 256 +#endif + +#ifndef XI_CSV_BUFFER_SIZE +#define XI_CSV_BUFFER_SIZE 128 +#endif + +#ifndef XI_HOST +#define XI_HOST "api.xively.com" +#endif + +#ifndef XI_PORT +#define XI_PORT 80 +#endif + +#endif // __XI_CONSTST_H__ +#define XI_USER_AGENT "libxively-mbed/0.1.x-6eca970"
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_debug.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_debug.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,27 @@ +// 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 xi_debug.h + * \author Olgierd Humenczuk + * \brief Macros to use for debugging (relies on `xi_printf()`) + */ + +#ifndef __XI_DEBUG_H__ +#define __XI_DEBUG_H__ + +#include "xi_printf.h" + +#ifdef XI_DEBUG_OUTPUT + #define xi_debug_log_str(...) xi_printf( "[%d@%s] - %s", __LINE__, __FILE__, __VA_ARGS__ ) + #define xi_debug_log_data(...) xi_printf( "%s", __VA_ARGS__ ) + #define xi_debug_log_int(...) xi_printf( "%d", __VA_ARGS__ ) + #define xi_debug_log_endl(...) xi_printf( "\n" ) +#else + #define xi_debug_log_str(...) + #define xi_debug_log_data(...) + #define xi_debug_log_int(...) + #define xi_debug_log_endl(...) +#endif + +#endif
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_err.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_err.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,61 @@ +// 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 xi_err.c + * \author Olgierd Humenczuk + * \brief Error handling (POSIX-like) [see xi_err.h] + */ + +#include "xi_err.h" +#include "xi_macros.h" + +static xi_err_t xi_err = XI_NO_ERR; + +const char* xi_err_string[ XI_ERR_COUNT ] = +{ + "XI_NO_ERR" + , "XI_OUT_OF_MEMORY" // XI_OUT_OF_MEMORY + , "XI_HTTP_STATUS_PARSE_ERROR" // XI_HTTP_STATUS_PARSE_ERROR + , "XI_HTTP_HEADER_PARSE_ERROR" // XI_HTTP_HEADER_PARSE_ERROR + , "XI_HTTP_PARSE_ERROR" // XI_HTTP_PARSE_ERROR + , "XI_HTTP_ENCODE_CREATE_DATASTREAM" // XI_HTTP_ENCODE_CREATE_DATASTREAM + , "XI_HTTP_ENCODE_UPDATE_DATASTREAM" // XI_HTTP_ENCODE_UPDATE_DATASTREAM + , "XI_HTTP_ENCODE_GET_DATASTREAM" // XI_HTTP_ENCODE_GET_DATASTREAM + , "XI_HTTP_ENCODE_DELETE_DATASTREAM" // XI_HTTP_ENCODE_DELETE_DATASTREAM + , "XI_HTTP_ENCODE_DELETE_DATAPOINT" // XI_HTTP_ENCODE_DELETE_DATAPOINT + , "XI_HTTP_ENCODE_DELETE_RANGE_DATAPOINT" // XI_HTTP_ENCODE_DELETE_RANGE_DATAPOINT + , "XI_HTTP_ENCODE_UPDATE_FEED" // XI_HTTP_ENCODE_UPDATE_FEED + , "XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN" // XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN + , "XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN" // XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN + , "XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN" // XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN + , "XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN" // XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN + , "XI_CSV_DECODE_FEED_PARSER_ERROR" // XI_CSV_DECODE_FEED_PARSER_ERROR + , "XI_CSV_DECODE_DATAPOINT_PARSER_ERROR" // XI_CSV_DECODE_DATAPOINT_PARSER_ERROR + , "XI_CSV_TIME_CONVERTION_ERROR" // XI_CSV_TIME_CONVERTION_ERROR + , "XI_SOCKET_INITIALIZATION_ERROR" // XI_SOCKET_INITIALIZATION_ERROR + , "XI_SOCKET_GETHOSTBYNAME_ERROR" // XI_SOCKET_GETHOSTBYNAME_ERROR + , "XI_SOCKET_CONNECTION_ERROR" // XI_SOCKET_CONNECTION_ERROR + , "XI_SOCKET_SHUTDOWN_ERROR" // XI_SOCKET_SHUTDOWN_ERROR + , "XI_SOCKET_WRITE_ERROR" // XI_SOCKET_WRITE_ERROR + , "XI_SOCKET_READ_ERROR" // XI_SOCKET_READ_ERROR + , "XI_SOCKET_CLOSE_ERROR" // XI_SOCKET_CLOSE_ERROR + , "XI_DATAPOINT_VALUE_BUFFER_OVERFLOW" // XI_DATAPOINT_VALUE_BUFFER_OVERFLOW +}; + +xi_err_t xi_get_last_error() +{ + xi_err_t ret = xi_err; + xi_set_err( XI_NO_ERR ); + return ret; +} + +void xi_set_err( xi_err_t e ) +{ + xi_err = e; +} + +const char* xi_get_error_string( xi_err_t e ) +{ + return xi_err_string[ XI_CLAMP( ( short ) e, 0, XI_ERR_COUNT - 1 ) ]; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_err.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_err.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,84 @@ +// 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 xi_err.h + * \author Olgierd Humenczuk + * \brief Error handling (POSIX-like) + * + * * Every function should return a value + * * There are special values (usually `0` or `-1`) which indicate occurrence of an error + * * User can detect and lookup errors using declarations below + */ + +#ifndef __XI_ERR_H__ +#define __XI_ERR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + XI_NO_ERR = 0 + , XI_OUT_OF_MEMORY + , XI_HTTP_STATUS_PARSE_ERROR + , XI_HTTP_HEADER_PARSE_ERROR + , XI_HTTP_PARSE_ERROR + , XI_HTTP_ENCODE_CREATE_DATASTREAM + , XI_HTTP_ENCODE_UPDATE_DATASTREAM + , XI_HTTP_ENCODE_GET_DATASTREAM + , XI_HTTP_ENCODE_DELETE_DATASTREAM + , XI_HTTP_ENCODE_DELETE_DATAPOINT + , XI_HTTP_ENCODE_DELETE_RANGE_DATAPOINT + , XI_HTTP_ENCODE_UPDATE_FEED + , XI_HTTP_CONSTRUCT_REQUEST_BUFFER_OVERRUN + , XI_HTTP_CONSTRUCT_CONTENT_BUFFER_OVERRUN + , XI_CSV_ENCODE_DATAPOINT_BUFFER_OVERRUN + , XI_CSV_ENCODE_DATASTREAM_BUFFER_OVERRUN + , XI_CSV_DECODE_FEED_PARSER_ERROR + , XI_CSV_DECODE_DATAPOINT_PARSER_ERROR + , XI_CSV_TIME_CONVERTION_ERROR + , XI_SOCKET_INITIALIZATION_ERROR + , XI_SOCKET_GETHOSTBYNAME_ERROR + , XI_SOCKET_CONNECTION_ERROR + , XI_SOCKET_SHUTDOWN_ERROR + , XI_SOCKET_WRITE_ERROR + , XI_SOCKET_READ_ERROR + , XI_SOCKET_CLOSE_ERROR + , XI_DATAPOINT_VALUE_BUFFER_OVERFLOW + , XI_ERR_COUNT +} xi_err_t; + +#define XI_MAX_ERR_STRING 64 + +/** + * \brief Error description lookup table + */ +extern const char* xi_err_string[ XI_ERR_COUNT ]; + +/** + * \brief Error getter for the user + * \return The `xi_err_t` structure which can be converted to a string using `xi_get_error_string()` method. + * + * \warning It resets the last error value, so it's always a good idea to make a copy of it! + */ +extern xi_err_t xi_get_last_error( void ); + +/** + * \brief Error setter for the library itself + * \note Current implementation used a global state variable (_errno_), which is not thread-safe. + * If thread-safety is required, than _errno_ should be made thread-local. + */ +extern void xi_set_err( xi_err_t e ); + +/** + * \brief Error description string getter for a given value of `xi_err_t` + */ +extern const char* xi_get_error_string( xi_err_t e ); + +#ifdef __cplusplus +} +#endif + +#endif // __XI_ERR_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_globals.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_globals.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,12 @@ +// 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 xi_globals.c + * \author Olgierd Humenczuk + * \brief Global run-time settings used by the library [see xi_globals.h] + */ + +#include "xi_globals.h" + +xi_globals_t xi_globals = { 1500 };
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_globals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_globals.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,34 @@ +// 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 xi_globals.h + * \author Olgierd Humenczuk + * \brief Global run-time settings used by the library + */ + +#ifndef __XI_GLOBALS_H__ +#define __XI_GLOBALS_H__ + + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Global run-time settings structure + */ +typedef struct +{ + uint32_t network_timeout; //!< the network timeout (default: 1500 milliseconds) +} xi_globals_t; + +extern xi_globals_t xi_globals; //!< global instance of `xi_globals_t` + +#ifdef __cplusplus +} +#endif + +#endif // __XI_GLOBALS_H__ \ No newline at end of file
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_helpers.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_helpers.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,184 @@ +// Copyright (c) 2003-2013, LogMeIn, Inc. All rights reserved. +// This is part of Xively C library, it is under the BSD 3-Clause license. + +// This file also containes code from MINIX C library, which is under MINIX license +// Copyright (c) 1987, 1997, 2006, Vrije Universiteit, Amsterdam, The Netherlands + +/** + * \file xi_helpers.c + * \author Olgierd Humenczuk + * \brief General helpers used by the library [see xi_helpers.h] + */ + +#include <string.h> +#include <assert.h> + +#include "xi_helpers.h" +#include "xi_allocator.h" + +char* xi_str_dup( const char* s ) +{ + // PRECONDITIONS + assert( s != 0 ); + + size_t len = strlen( s ); + char * ret = xi_alloc( len + 1 ); + if( ret == 0 ) { return 0; } + memcpy( ret, s, len + 1 ); + return ret; +} + +int xi_str_copy_untiln( char* dst, size_t dst_size, const char* src, char delim ) +{ + // PRECONDITIONS + assert( dst != 0 ); + assert( dst_size > 1 ); + assert( src != 0 ); + + size_t counter = 0; + size_t real_size = dst_size - 1; + + while( *src != delim && counter < real_size && *src != '\0' ) + { + *dst++ = *src++; + counter++; + } + + *dst = '\0'; + return counter; +} + +// used by the xi_mktime +#define YEAR0 1900 /* the first year */ +#define EPOCH_YR 1970 /* EPOCH = Jan 1 1970 00:00:00 */ +#define SECS_DAY (24L * 60L * 60L) +#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) +#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365) +#define FIRSTSUNDAY(timp) (((timp)->tm_yday - (timp)->tm_wday + 420) % 7) +#define FIRSTDAYOF(timp) (((timp)->tm_wday - (timp)->tm_yday + 420) % 7) +#define TIME_MAX ULONG_MAX +#define ABB_LEN 3 + +// used by the xi_mktime +static const int _ytab[2][12] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; + +time_t xi_mktime(register struct tm *timep) +{ + register long day, year; + register int tm_year; + int yday, month; + register signed long seconds; + int overflow; + + timep->tm_min += timep->tm_sec / 60; + timep->tm_sec %= 60; + if (timep->tm_sec < 0) { + timep->tm_sec += 60; + timep->tm_min--; + } + timep->tm_hour += timep->tm_min / 60; + timep->tm_min = timep->tm_min % 60; + if (timep->tm_min < 0) { + timep->tm_min += 60; + timep->tm_hour--; + } + day = timep->tm_hour / 24; + timep->tm_hour= timep->tm_hour % 24; + if (timep->tm_hour < 0) { + timep->tm_hour += 24; + day--; + } + timep->tm_year += timep->tm_mon / 12; + timep->tm_mon %= 12; + if (timep->tm_mon < 0) { + timep->tm_mon += 12; + timep->tm_year--; + } + day += (timep->tm_mday - 1); + while (day < 0) { + if(--timep->tm_mon < 0) { + timep->tm_year--; + timep->tm_mon = 11; + } + day += _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]; + } + while (day >= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]) { + day -= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]; + if (++(timep->tm_mon) == 12) { + timep->tm_mon = 0; + timep->tm_year++; + } + } + timep->tm_mday = day + 1; + year = EPOCH_YR; + if (timep->tm_year < year - YEAR0) return (time_t)-1; + seconds = 0; + day = 0; + overflow = 0; + + /* + * Assume that when day becomes negative, there will certainly be overflow on seconds. + * The check for overflow needs not to be done for leapyears divisible by 400. + * The code only works when year (1970) is not a leapyear. + */ + #if EPOCH_YR != 1970 + #error EPOCH_YR != 1970 + #endif + + tm_year = timep->tm_year + YEAR0; + + if (LONG_MAX / 365 < tm_year - year) overflow++; + day = (tm_year - year) * 365; + if (LONG_MAX - day < (tm_year - year) / 4 + 1) overflow++; + day += (tm_year - year) / 4 + + ((tm_year % 4) && tm_year % 4 < year % 4); + day -= (tm_year - year) / 100 + + ((tm_year % 100) && tm_year % 100 < year % 100); + day += (tm_year - year) / 400 + + ((tm_year % 400) && tm_year % 400 < year % 400); + + yday = month = 0; + while (month < timep->tm_mon) { + yday += _ytab[LEAPYEAR(tm_year)][month]; + month++; + } + yday += (timep->tm_mday - 1); + if (day + yday < 0) overflow++; + day += yday; + + timep->tm_yday = yday; + timep->tm_wday = (day + 4) % 7; + + seconds = ( ( timep->tm_hour * 60L ) + timep->tm_min ) * 60L + timep->tm_sec; + + if ( ( TIME_MAX - seconds ) / SECS_DAY < ( unsigned long ) day ) overflow++; + seconds += day * SECS_DAY; + + if ( overflow ) return ( time_t ) - 1; + + if ( ( time_t ) seconds != seconds) return ( time_t ) - 1; + return ( time_t ) seconds; +} + +struct tm* xi_gmtime( time_t* t ) +{ + return gmtime( t ); +} + +char* xi_replace_with( + char p, char r + , char* buffer + , size_t max_chars ) +{ + char* c = buffer; + + while( *c != '\0' && max_chars-- ) + { + if( *c == p ) { *c = r; } + c++; + } + + return c; +}
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_helpers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_helpers.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,71 @@ +// 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 xi_helpers.h + * \author Olgierd Humenczuk + * \brief General helpers used by the library + */ + +#ifndef __XI_HELPERS_H__ +#define __XI_HELPERS_H__ + +#include <stdlib.h> +#include <time.h> +#include <limits.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \note Needed to avoid using `strdup()` which can cause some problems with `free()`, + * because of buggy `realloc()` implementations. + * + * \return Duplicated string or null in case of memory allocation problem. + */ +char* xi_str_dup( const char* s ); + +/** + * \brief Copies `src` string into `dst`, but stops whenever `delim` is reached or the `dst_size` is exceeded + * + * \return Number of copied characters or -1 if an error occurred. + */ +int xi_str_copy_untiln( char* dst, size_t dst_size, const char* src, char delim ); + +/** + * \brief Converts from `tm` to `time_t` + * + * \note This code assumes that unsigned long can be converted to `time_t`. + * A `time_t` should not be wider than `unsigned long`, since this + * would mean that the check for overflow at the end could fail. + * + * \note This implementation had been copied from MINIX C library. + * Which is 100% compatible with our license. + * + * \note This function does not take into account the timezone or the `dst`, + * it just converts `tm` structure using date and time fields (i.e. UTC). + */ +time_t xi_mktime( struct tm* t ); + +/** + * \brief This just wraps system's `gmtime()`, it a facade for future use + */ +struct tm* xi_gmtime( time_t* t ); + +/** + * \brief Replaces `p` with `r` for every `p` in `buffer` + * + * \return Pointer to converted buffer. + * \note This function assumes that the buffer is terminated with `\0`. + */ +char* xi_replace_with( + char p, char r + , char* buffer + , size_t max_chars ); + +#ifdef __cplusplus +} +#endif + +#endif // __XI_HELPERS_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_macros.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_macros.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,40 @@ +// 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 xi_macros.h + * \author Olgierd Humenczuk + * \brief General macros used by the library + */ + +#ifndef __XI_MACROS_H__ +#define __XI_MACROS_H__ + +#include <string.h> + +#define XI_STR_EXPAND(tok) #tok +#define XI_STR(tok) XI_STR_EXPAND(tok) +#define XI_MIN(a,b) (a)<(b)?(a):(b) +#define XI_MAX(a,b) (a)<(b)?(b):(a) +#define XI_UNUSED(x) (void)(x) + +#define XI_GUARD_EOS(s,size) { (s)[ (size) - 1 ] = '\0'; } + +#define XI_CLAMP(a,b,t) XI_MIN( XI_MAX( (a), (b) ), (t) ) + +#define XI_CHECK_CND(cnd,e) if( (cnd) ) { xi_set_err( (e) ); goto err_handling; } +#define XI_CHECK_ZERO(a,e) XI_CHECK_CND((a) == 0,(e)) +#define XI_CHECK_NEG(a) if( (a) < 0 ) ) +#define XI_CHECK_PTR(a,b) if( (a) == (b) ) +#define XI_SAFE_FREE(a) if( (a) ) { xi_free(a); (a) = 0; } +#define XI_CHECK_MEMORY(a) XI_CHECK_CND((a) == 0,XI_OUT_OF_MEMORY) +#define XI_CHECK_SIZE(a,b,e) XI_CHECK_CND(((a) >= (b) || (a) < 0 ),e) +#define XI_CLEAR_STATIC_BUFFER(a) memset( (a), 0, sizeof(a) ) +#define XI_CHECK_S(s,size,o,e) {\ + XI_CHECK_SIZE(s,size-o,e)\ + else\ + {\ + (o) += (s);\ + }} + +#endif //__XI_MACROS_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_printf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_printf.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,47 @@ +// 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 xi_printf.c + * \author Olgierd Humenczuk + * \brief Our custom `printf()` hook [see xi_printf.h] + */ + +#include <stdarg.h> +#include <stdio.h> + +#include "xi_printf.h" +#include "xi_consts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +user_print_t USER_PRINT = 0; + +int xi_printf( const char *fmt, ... ) +{ + char buffer[ XI_PRINTF_BUFFER_SIZE ]; + int n = 0; + + va_list ap; + va_start( ap, fmt ); + vsnprintf( buffer, XI_PRINTF_BUFFER_SIZE, fmt, ap ); + va_end( ap ); + + + if( USER_PRINT ) + { + USER_PRINT( buffer ); + } + else + { + printf( "%s", buffer ); + } + + return n; +} + +#ifdef __cplusplus +} +#endif
diff -r 000000000000 -r 82702e998d3f src/libxively/xi_printf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xi_printf.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,33 @@ +// 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 xi_printf.h + * \author Olgierd Humenczuk + * \brief Our custom `printf()` hook + * + * This is needed on embedded devices, as there is no way to redirect + * the output neither it has any logging facilities out of the box. + * We currently use a function pointer (`user_print_t`) and and the + * user can assign to an external variable (`USER_PRINTF`) to call + * whatever device platform may have available. + */ + +#ifndef __XI_PRINTF_H__ +#define __XI_PRINTF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void ( *user_print_t )( const char* ); + +extern user_print_t USER_PRINT; + +int xi_printf( const char *fmt, ... ); + +#ifdef __cplusplus +} +#endif + +#endif //__XI_PRINTF_H__
diff -r 000000000000 -r 82702e998d3f src/libxively/xively.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xively.c Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,367 @@ +// 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 xively.c + * \brief Xively C library [see xively.h] + */ + +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "xi_allocator.h" +#include "xively.h" +#include "http_transport.h" +#include "csv_data_layer.h" +#include "xi_macros.h" +#include "xi_debug.h" +#include "xi_helpers.h" +#include "xi_err.h" +#include "xi_globals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Get instance of _communication layer_ + * + * \note Although the interface is of the _communication layer_ should + * stay the same, some implemantation may differ. + * + * \return Pointer to the communication layer interface + */ +const comm_layer_t* get_comm_layer( void ); + +#include "comm_layer.h" + +//----------------------------------------------------------------------- +// HELPER MACROS +//----------------------------------------------------------------------- + +#define XI_FUNCTION_VARIABLES connection_t* conn = 0;\ + const comm_layer_t* comm_layer = 0;\ + const transport_layer_t* transport_layer = 0;\ + const data_layer_t* data_layer = 0;\ + char buffer[ XI_HTTP_MAX_CONTENT_SIZE ];\ + const xi_response_t* response = 0;\ + int sent = 0;\ + int recv = 0; + +#define XI_FUNCTION_PROLOGUE XI_FUNCTION_VARIABLES\ + xi_debug_log_str( "Getting the comm layer...\n" );\ + comm_layer = get_comm_layer();\ + xi_debug_log_str( "Getting the transport layer...\n" );\ + transport_layer = get_http_transport_layer();\ + xi_debug_log_str( "Getting the data layer...\n");\ + data_layer = get_csv_data_layer();\ + +#define XI_FUNCTION_GET_RESPONSE if( data == 0 ) { goto err_handling; }\ + xi_debug_log_str( "Connecting to the endpoint...\n" );\ + conn = comm_layer->open_connection( XI_HOST, XI_PORT );\ + if( conn == 0 ) { goto err_handling; }\ + xi_debug_log_str( "Sending data:\n" );\ + xi_debug_log_data( data );\ + sent = comm_layer->send_data( conn, data, strlen( data ) );\ + if( sent == -1 ) { goto err_handling; }\ + xi_debug_log_str( "Sent: " );\ + xi_debug_log_int( ( int ) sent );\ + xi_debug_log_endl();\ + xi_debug_log_str( "Reading data...\n" );\ + recv = comm_layer->read_data( conn, buffer, XI_HTTP_MAX_CONTENT_SIZE );\ + if( recv == -1 ) { goto err_handling; }\ + xi_debug_log_str( "Received: " );\ + xi_debug_log_int( ( int ) recv );\ + xi_debug_log_endl();\ + xi_debug_log_str( "Response:\n" );\ + xi_debug_log_data( buffer );\ + xi_debug_log_endl();\ + response = transport_layer->decode_reply(\ + data_layer, buffer );\ + if( response == 0 ) { goto err_handling; }\ + +#define XI_FUNCTION_EPILOGUE xi_debug_log_str( "Closing connection...\n" );\ +err_handling:\ + if( conn )\ + {\ + comm_layer->close_connection( conn );\ + }\ + return response;\ + +//----------------------------------------------------------------------- +// HELPER FUNCTIONS +//----------------------------------------------------------------------- + +xi_datapoint_t* xi_set_value_i32( xi_datapoint_t* p, int32_t value ) +{ + // PRECONDITION + assert( p != 0 ); + + p->value.i32_value = value; + p->value_type = XI_VALUE_TYPE_I32; + + return p; +} + +xi_datapoint_t* xi_set_value_f32( xi_datapoint_t* p, float value ) +{ + // PRECONDITION + assert( p != 0 ); + + p->value.f32_value = value; + p->value_type = XI_VALUE_TYPE_F32; + + return p; +} + +xi_datapoint_t* xi_set_value_str( xi_datapoint_t* p, const char* value ) +{ + // PRECONDITION + assert( p != 0 ); + + int s = xi_str_copy_untiln( p->value.str_value + , XI_VALUE_STRING_MAX_SIZE, value, '\0' ); + + XI_CHECK_SIZE( s, XI_VALUE_STRING_MAX_SIZE, XI_DATAPOINT_VALUE_BUFFER_OVERFLOW ); + + p->value_type = XI_VALUE_TYPE_STR; + + return p; + +err_handling: + return 0; +} + +void xi_set_network_timeout( uint32_t timeout ) +{ + xi_globals.network_timeout = timeout; +} + +uint32_t xi_get_network_timeout( void ) +{ + return xi_globals.network_timeout; +} + +//----------------------------------------------------------------------- +// MAIN LIBRARY FUNCTIONS +//----------------------------------------------------------------------- + +xi_context_t* xi_create_context( + xi_protocol_t protocol, const char* api_key + , int32_t feed_id ) +{ + // allocate the structure to store new context + xi_context_t* ret = ( xi_context_t* ) xi_alloc( sizeof( xi_context_t ) ); + + XI_CHECK_MEMORY( ret ); + + // copy given numeric parameters as is + ret->protocol = protocol; + ret->feed_id = feed_id; + + // copy string parameters carefully + if( api_key ) + { + // duplicate the string + ret->api_key = xi_str_dup( api_key ); + + XI_CHECK_MEMORY( ret->api_key ); + } + else + { + ret->api_key = 0; + } + + return ret; + +err_handling: + if( ret ) + { + XI_SAFE_FREE( ret ); + } + + return 0; +} + +void xi_delete_context( xi_context_t* context ) +{ + if( context ) + { + XI_SAFE_FREE( context->api_key ); + } + XI_SAFE_FREE( context ); +} + +const xi_response_t* xi_feed_get( + xi_context_t* xi + , xi_feed_t* feed ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_get_feed( + data_layer + , xi->api_key + , feed ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + + feed = data_layer->decode_feed( response->http.http_content, feed ); + if( feed == 0 ) { goto err_handling; } + + XI_FUNCTION_EPILOGUE +} + +const xi_response_t* xi_feed_update( + xi_context_t* xi + , const xi_feed_t* feed ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_update_feed( + data_layer + , xi->api_key + , feed ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + + XI_FUNCTION_EPILOGUE +} + +const xi_response_t* xi_datastream_get( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id, xi_datapoint_t* o ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_get_datastream( + data_layer + , xi->api_key + , feed_id + , datastream_id ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + + o = data_layer->decode_datapoint( + response->http.http_content, o ); + + if( o == 0 ) { goto err_handling; } + + XI_FUNCTION_EPILOGUE +} + + +const xi_response_t* xi_datastream_create( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id + , const xi_datapoint_t* datapoint ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_create_datastream( + data_layer + , xi->api_key + , feed_id + , datastream_id + , datapoint ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + XI_FUNCTION_EPILOGUE +} + +const xi_response_t* xi_datastream_update( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id + , const xi_datapoint_t* datapoint ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_update_datastream( + data_layer + , xi->api_key + , feed_id + , datastream_id + , datapoint ); + + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + + + XI_FUNCTION_EPILOGUE +} +const xi_response_t* xi_datastream_delete( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_delete_datastream( + data_layer + , xi->api_key + , feed_id + , datastream_id ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + + XI_FUNCTION_EPILOGUE +} + +const xi_response_t* xi_datapoint_delete( + const xi_context_t* xi, int feed_id + , const char * datastream_id + , const xi_datapoint_t* o ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_delete_datapoint( + data_layer + , xi->api_key + , feed_id + , datastream_id + , o ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + XI_FUNCTION_EPILOGUE +} + +extern const xi_response_t* xi_datapoint_delete_range( + const xi_context_t* xi, int feed_id + , const char * datastream_id + , const xi_timestamp_t* start + , const xi_timestamp_t* end ) +{ + XI_FUNCTION_PROLOGUE + + const char* data = transport_layer->encode_datapoint_delete_range( + data_layer + , xi->api_key + , feed_id + , datastream_id + , start + , end ); + + if( data == 0 ) { goto err_handling; } + + XI_FUNCTION_GET_RESPONSE + XI_FUNCTION_EPILOGUE +} + + +#ifdef __cplusplus +} +#endif
diff -r 000000000000 -r 82702e998d3f src/libxively/xively.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/libxively/xively.h Wed Jun 26 10:40:43 2013 +0000 @@ -0,0 +1,307 @@ +// 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 xively.h + * \brief Xively C library + */ + +#ifndef __XI_H__ +#define __XI_H__ + +#include <stdlib.h> +#include <stdint.h> + +#include <time.h> + +#include "comm_layer.h" +#include "xi_consts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//----------------------------------------------------------------------- +// TYPES AND STRUCTURES +//----------------------------------------------------------------------- + +/** + * \brief The protocols currently supported by Xively + * \note See source code for details of what's implemented. + */ +typedef enum { + /** `http://api.xively.com` */ + XI_HTTP, + /** `https://api.xively.com` */ + XI_HTTPS, + /** `telnet api.xively.com 8081` */ + XI_TCP, + /** `openssl s_client -host api.xively.com -port 8091 -tls1` */ + XI_TCPS, + /** `ws://api.xively.com:8080` */ + XI_WS, + /** `wss://api.xively.com:8090` */ + XI_WSS, +} xi_protocol_t; + +/** + * \brief _The context structure_ - it's the first agument for all functions + * that communicate with Xively API (_i.e. not helpers or utilities_) + */ +typedef struct { + char *api_key; /** Xively API key */ + xi_protocol_t protocol; /** Xively protocol */ + int32_t feed_id; /** Xively feed ID */ +} xi_context_t; + +/** + * \brief HTTP headers + */ +typedef enum +{ + /** `Date` */ + XI_HTTP_HEADER_DATE = 0, + /** `Content-Type` */ + XI_HTTP_HEADER_CONTENT_TYPE, + /** `Content-Length` */ + XI_HTTP_HEADER_CONTENT_LENGTH, + /** `Connection` */ + XI_HTTP_HEADER_CONNECTION, + /** `X-Request-Id` */ + XI_HTTP_HEADER_X_REQUEST_ID, + /** `Cache-Control` */ + XI_HTTP_HEADER_CACHE_CONTROL, + /** `Vary` */ + XI_HTTP_HEADER_VARY, + /** `Count` */ + XI_HTTP_HEADER_COUNT, + /** `Age` */ + XI_HTTP_HEADER_AGE, + // must go before the last here + XI_HTTP_HEADER_UNKNOWN, + // must be the last here + XI_HTTP_HEADERS_COUNT +} http_header_type_t; + +/** Datapoint value types */ +typedef enum +{ /** 32-bit signed integer */ + XI_VALUE_TYPE_I32 = 0, + /** 32-bit floating point number */ + XI_VALUE_TYPE_F32, + /** any string-econded data */ + XI_VALUE_TYPE_STR, + XI_VALUE_TYPE_COUNT +} xi_value_type_t; + +typedef struct { + http_header_type_t header_type; + char name[ XI_HTTP_HEADER_NAME_MAX_SIZE ]; + char value[ XI_HTTP_HEADER_VALUE_MAX_SIZE ]; +} http_header_t; + +typedef struct { + int http_version1; + int http_version2; + int http_status; + char http_status_string[ XI_HTTP_STATUS_STRING_SIZE ]; + http_header_t* http_headers_checklist[ XI_HTTP_HEADERS_COUNT ]; + http_header_t http_headers[ XI_HTTP_MAX_HEADERS ]; + size_t http_headers_size; + char http_content[ XI_HTTP_MAX_CONTENT_SIZE ]; +} http_response_t; + +/** + * \brief _The response structure_ - it's the return type for all functions + * that communicate with Xively API (_i.e. not helpers or utilities_) + */ +typedef struct { + http_response_t http; +} xi_response_t; + +/** + * \brief The datapoint value union + */ +typedef union { + int32_t i32_value; + float f32_value; + char str_value[ XI_VALUE_STRING_MAX_SIZE ]; +} xi_datapoint_value_t; + +/** + * \brief The datapoint timestamp + */ +typedef struct { + time_t timestamp; + time_t micro; +} xi_timestamp_t; + +/** + * \brief _Xively datapoint structure_ - it contains value and timestamp + * \note A zero-valued timestamp is used by most functions as a convention + * to opt for server-side timestamps. + */ +typedef struct { + xi_datapoint_value_t value; + xi_value_type_t value_type; + xi_timestamp_t timestamp; +} xi_datapoint_t; + +typedef struct { + char datastream_id[ XI_MAX_DATASTREAM_NAME ]; + size_t datapoint_count; + xi_datapoint_t datapoints[ XI_MAX_DATAPOINTS ]; +} xi_datastream_t; + +/** + * \brief _Xively feed structure_ - it contains a fixed array of datastream + * \note The implementation is such that user will need to know in advance + * how many datastreams there can be, which should be sufficent for + * a real-world application. It's also undesired to have some devices + * create dozens of datastreams due to a bug. + */ +typedef struct { + int32_t feed_id; + size_t datastream_count; + xi_datastream_t datastreams[ XI_MAX_DATASTREAMS ]; +} xi_feed_t; + +//----------------------------------------------------------------------- +// HELPER FUNCTIONS +//----------------------------------------------------------------------- + +/** + * \brief Sets the xi_datapoint_t value field to `int32_t` value + * + * \return Pointer or `0` if an error occurred. + */ +extern xi_datapoint_t* xi_set_value_i32( xi_datapoint_t* dp, int32_t v ); + +/** + * \brief Sets the `xi_datapoint_t` value field to `float` value + * \return Pointer or `0` if an error occurred. + */ +extern xi_datapoint_t* xi_set_value_f32( xi_datapoint_t* dp, float v ); + +/** + * \brief Sets the `xi_datapoint_t` value field to zero-terminated string value + * + * \return Pointer or `0` if an error occurred. + */ +extern xi_datapoint_t* xi_set_value_str( xi_datapoint_t* dp, const char* v ); + +/** + * \brief Sets the timeout for network operations + * + * \note The timeout is used by the comunication layer + * to determine whenever it should treat the lag + * in a connection as an error, so if your device + * or your connection is slow, you can try to increase + * the timeout for network operations. It only affects the + * send/recv operations it does not work with connect but that + * behaviour may differ between platforms and communication + * layer imlementations. + */ +extern void xi_set_network_timeout( uint32_t milliseconds ); + +/** + * \brief Gets the current network timeout + */ +extern uint32_t xi_get_network_timeout( void ); + +//----------------------------------------------------------------------- +// MAIN LIBRARY FUNCTIONS +//----------------------------------------------------------------------- + +/** + * \brief Library context constructor + * + * The purpose of this function is to allocate memory and initialise the + * data structures needed in order to use any other library functions. + * + * \return Initialised context structure or `0` if an error occurred + */ +extern xi_context_t* xi_create_context( + xi_protocol_t protocol, const char* api_key + , int32_t feed_id ); + +/** + * \brief Library context destructor + * + * The purpose of this fucntion is to free all allocated resources + * when the application is intending to terminate or stop using the library. + */ +extern void xi_delete_context( xi_context_t* context ); + + +/** + * \brief Update Xively feed + */ +extern const xi_response_t* xi_feed_update( + xi_context_t* xi + , const xi_feed_t* value ); + +/** + * \brief Retrieve Xively feed + */ +extern const xi_response_t* xi_feed_get( + xi_context_t* xi + , xi_feed_t* value ); + +/** + * \brief Create a datastream with given value using server timestamp + */ +extern const xi_response_t* xi_datastream_create( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id + , const xi_datapoint_t* value); + +/** + * \brief Update a datastream with given datapoint using server or local timestamp + */ +extern const xi_response_t* xi_datastream_update( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id + , const xi_datapoint_t* value ); + +/** + * \brief Retrieve latest datapoint from a given datastream + */ +extern const xi_response_t* xi_datastream_get( + xi_context_t* xi, int32_t feed_id + , const char * datastream_id, xi_datapoint_t* dp ); + +/** + * \brief Delete datastream + * \warning This function destroys the data in Xively and there is no way to restore it! + */ +extern const xi_response_t* xi_datastream_delete( + xi_context_t* xi, int feed_id + , const char* datastream_id ); + +/** + * \brief Delete datapoint at a given timestamp + * \warning This function destroys the data in Xively and there is no way to restore it! + * \note You need to provide exact timestamp value to guarantee successful response + * from the API, i.e. it will respond with error 404 if datapoint didn't exist. + * If you need to determine the exact timestamp, it may be easier to call + * `xi_datapoint_delete_range()` with short range instead. + */ +extern const xi_response_t* xi_datapoint_delete( + const xi_context_t* xi, int feed_id + , const char * datastream_id + , const xi_datapoint_t* dp ); + +/** + * \brief Delete all datapoints in given time range + * \warning This function destroys the data in Xively and there is no way to restore it! + */ +extern const xi_response_t* xi_datapoint_delete_range( + const xi_context_t* xi, int feed_id, const char * datastream_id + , const xi_timestamp_t* start, const xi_timestamp_t* end ); + +#ifdef __cplusplus +} +#endif + +#endif // __XI_H__