Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-libxively-6eca970 by
Revision 0:82702e998d3f, committed 2013-06-26
- Comitter:
- xively
- Date:
- Wed Jun 26 10:40:43 2013 +0000
- Child:
- 1:0556e1894817
- Commit message:
- libxively v0.1.1-rc0 (34c8b32)
Changed in this revision
--- /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__
--- /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;
+}
+
+}
--- /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__
--- /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;
+}
--- /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__
--- /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__
--- /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;
+}
--- /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__
--- /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;
+}
--- /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__
--- /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__
--- /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__
--- /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;
+}
--- /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
--- /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;
+}
--- /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__
--- /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;
+}
--- /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__
--- /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;
+}
--- /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__
--- /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__
--- /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 );
+}
--- /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
--- /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"
--- /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
--- /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 ) ];
+}
--- /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__
--- /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 };
--- /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
--- /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;
+}
--- /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__
--- /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__
--- /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
--- /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__
--- /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
--- /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__
