Adapted to LoRa Semtech + Nucleo
Dependents: LoRaWAN-lmic-app LoRaWAN-lmic-app LoRaWAN-test-10secs LoRaPersonalizedDeviceForEverynet ... more
Fork of cantcoap by
Revision 2:d0d57af6c9df, committed 2015-11-20
- Comitter:
- pnysten
- Date:
- Fri Nov 20 12:30:26 2015 +0000
- Parent:
- 1:5eec2844ad47
- Commit message:
- ?
Changed in this revision
diff -r 5eec2844ad47 -r d0d57af6c9df cantcoap.cpp --- a/cantcoap.cpp Thu Jan 30 14:07:56 2014 +0000 +++ b/cantcoap.cpp Fri Nov 20 12:30:26 2015 +0000 @@ -35,29 +35,12 @@ #include <stdio.h> #include <stdlib.h> #include <stdint.h> +//#include <unistd.h> #include <string.h> -#include <inet.h> #include "cantcoap.h" - -// for debugging output you need https://mbed.org/users/donatien/code/DebugLib/ -#define __DEBUG__ 3 // INFO -#ifndef __MODULE__ - #define __MODULE__ "cantcoap.cpp" -#endif -#include "dbg.h" -// some extra debug stuff -#if __DEBUG__ > 0 - #define INFOX(...) do { fprintf(stderr,__VA_ARGS__); } while(0) - #define INFOLX(...) do { fprintf(stderr,"[INFO] %s:%d ",__MODULE__,__LINE__); fprintf(stderr,__VA_ARGS__); } while(0) - #define DBGLX(...) do { fprintf(stderr,"[DBG] %s:%d ",__MODULE__,__LINE__); fprintf(stderr,__VA_ARGS__); } while(0) - #define DBG_PDU() do { printBin(); } while(0) -#else - #define INFOX(...) do{} while(0) - #define INFOLX(...) do{} while(0) - #define DBGLX(...) do{} while(0) - #define DBG_PDU() do{} while(0) -#endif - +#include "lwip/inet.h" +#include "mbed.h" +//#include <iostream> /// Memory-managed constructor. Buffer for PDU is dynamically sized and allocated by the object. /** @@ -122,34 +105,8 @@ * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength) */ CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) { - int bufferLength = pduLength; - // sanity - if(pduLength<4&&pduLength!=0) { - DBG("PDU cannot have a length less than 4"); - } - - // pdu - _pdu = pdu; - _bufferLength = bufferLength; - if(pduLength==0) { - // this is actually a fresh pdu, header always exists - _pduLength = 4; - // make sure header is zeroed - _pdu[0] = 0x00; _pdu[1] = 0x00; _pdu[2] = 0x00; _pdu[3] = 0x00; - setVersion(1); - } else { - _pduLength = pduLength; - } - - _constructedFromBuffer = 1; - - // options - _numOptions = 0; - _maxAddedOptionNumber = 0; - - // payload - _payloadPointer = NULL; - _payloadLength = 0; + CoapPDU(pdu,pduLength,pduLength); + // delegated to CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength) } /// Construct object from external buffer that may be larger than actual PDU. @@ -298,7 +255,7 @@ (code>COAP_UNSUPPORTED_CONTENT_FORMAT&&code<COAP_INTERNAL_SERVER_ERROR) || (code>COAP_PROXYING_NOT_SUPPORTED) ) { DBG("Invalid CoAP code: %d",code); - return 0; + //return 0; } DBG("CoAP code: %d",code); @@ -452,28 +409,35 @@ * Calls CoapPDU::setURI(uri,strlen(uri). */ int CoapPDU::setURI(char *uri) { - return setURI(uri,strlen(uri)); + return setURI(uri,(int)strlen(uri)); } /// Shorthand function for setting a resource URI. /** - * This will parse the supplied \b uri and construct enough URI_PATH CoAP options to encode it. + * This will parse the supplied \b uri and construct enough URI_PATH and URI_QUERY options to encode it. * The options are added to the PDU. * - * At present queries are not handled. TODO Implement queries. + * At present only simple URI formatting is handled, only '/','?', and '&' separators, and no port or protocol specificaiton. + * + * The function will split on '/' and create URI_PATH elements until it either reaches the end of the string + * in which case it will stop or if it reaches '?' it will start splitting on '&' and create URI_QUERY elements + * until it reaches the end of the string. * - * \note This uses an internal buffer of 16 bytes to manipulate strings. The internal buffer will be - * expanded dynamically if necessary (path component longer than 16 bytes). The internal buffer will - * be freed before the function returns. - * + * Here is an example: + * + * /a/b/c/d?x=1&y=2&z=3 + * + * Will be broken into four URI_PATH elements "a", "b", "c", "d", and three URI_QUERY elements "x=1", "y=2", "z=3" + * + * TODO: Add protocol extraction, port extraction, and some malformity checking. + * * \param uri The uri to parse. * \param urilen The length of the uri to parse. * * \return 1 on success, 0 on failure. */ int CoapPDU::setURI(char *uri, int urilen) { - // only '/' and alphabetic chars allowed - // very simple splitting done + // only '/', '?', '&' and ascii chars allowed // sanitation if(urilen<=0||uri==NULL) { @@ -487,67 +451,73 @@ return 0; } + // TODO, queries + // extract ? to mark where to stop processing path components + // and then process the query params + // local vars char *startP=uri,*endP=NULL; - int oLen = 0; - int bufSpace = 16; - char *uriBuf = (char*)malloc(bufSpace*sizeof(char)); - if(uriBuf==NULL) { - DBG("Error allocating temporary memory."); - return 1; - } - + long oLen = 0; + char splitChar = '/'; + int queryStageTriggered = 0; + uint16_t optionType = COAP_OPTION_URI_PATH; while(1) { - // stop at end of string + // stop at end of string or query if(*startP==0x00||*(startP+1)==0x00) { break; } // ignore leading slash - if(*startP=='/') { + if(*startP==splitChar) { DBG("Skipping leading slash"); startP++; } // find next split point - endP = strchr(startP,'/'); + endP = strchr(startP,splitChar); // might not be another slash if(endP==NULL) { DBG("Ending out of slash"); - endP = uri+urilen; + // check if there is a ? + endP = strchr(startP,'?'); + // done if no queries + if(endP==NULL) { + endP = uri+urilen; + } else { + queryStageTriggered = 1; + } } // get length of segment oLen = endP-startP; - // copy sequence, make space if necessary - if((oLen+1)>bufSpace) { - char *newBuf = (char*)realloc(uriBuf,oLen+1); - if(newBuf==NULL) { - DBG("Error making space for temporary buffer"); - free(uriBuf); - return 1; - } - uriBuf = newBuf; - } + #ifdef DEBUG + char *b = (char*)malloc(oLen+1); + memcpy(b,startP,oLen); + b[oLen] = 0x00; + DBG("Adding URI_PATH %s",b); + free(b); + #endif - // copy into temporary buffer - memcpy(uriBuf,startP,oLen); - uriBuf[oLen] = 0x00; - DBG("Adding URI_PATH %s",uriBuf); // add option - if(addOption(COAP_OPTION_URI_PATH,oLen,(uint8_t*)uriBuf)!=0) { + if(addOption(optionType,oLen,(uint8_t*)startP)!=0) { DBG("Error adding option"); return 1; } startP = endP; + + if(queryStageTriggered) { + splitChar = '&'; + optionType = COAP_OPTION_URI_QUERY; + startP++; + queryStageTriggered = false; + } } - // clean up - free(uriBuf); return 0; } + /// Shorthand for adding a URI QUERY to the option list. /** * Adds a new option to the CoAP PDU that encodes a URI_QUERY. @@ -559,10 +529,12 @@ return addOption(COAP_OPTION_URI_QUERY,strlen(query),(uint8_t*)query); } -/// Concatenates any URI_PATH elements into a single string. +/// Concatenates any URI_PATH elements and URI_QUERY elements into a single string. /** - * Parses the PDU options and extracts all URI_PATH elements, concatenating them into a single string with slash separators. - * The produced string will be null terminated. + * Parses the PDU options and extracts all URI_PATH and URI_QUERY elements, + * concatenating them into a single string with slash and amphersand separators accordingly. + * + * The produced string will be NULL terminated. * * \param dst Buffer into which to copy the concatenated path elements. * \param dstlen Length of buffer. @@ -615,10 +587,24 @@ DBG("No space for initial slash needed 1, got %d",bytesLeft); return 1; } + + char separator = '/'; + int firstQuery = 1; + for(int i=0; i<_numOptions; i++) { o = &options[i]; oLen = o->optionValueLength; - if(o->optionNumber==COAP_OPTION_URI_PATH) { + if(o->optionNumber==COAP_OPTION_URI_PATH||o->optionNumber==COAP_OPTION_URI_QUERY) { + // if the option is a query, change the separator to & + if(o->optionNumber==COAP_OPTION_URI_QUERY) { + if(firstQuery) { + // change previous '/' to a '?' + *(dst-1) = '?'; + firstQuery = 0; + } + separator = '&'; + } + // check space if(oLen>bytesLeft) { DBG("Destination buffer too small, needed %d, got %d",oLen,bytesLeft); @@ -632,16 +618,16 @@ return 0; } - // copy URI path component + // copy URI path or query component memcpy(dst,o->optionValuePointer,oLen); // adjust counters dst += oLen; bytesLeft -= oLen; - // add slash following (don't know at this point if another option is coming) + // add separator following (don't know at this point if another option is coming) if(bytesLeft>=1) { - *dst = '/'; + *dst = separator; dst++; bytesLeft--; } else { @@ -651,7 +637,7 @@ } } - // remove terminating slash + // remove terminating separator dst--; bytesLeft++; // add null terminating byte (always space since reserved) @@ -821,6 +807,16 @@ return 0; } +long CoapPDU::getLongToken(){ + + long token=0; + + for (int i=0; i<getTokenLength(); ++i) { + token+= getTokenPointer()[i]<<8*i; + } + return ntohl(token); +} + /// Sets the CoAP response code void CoapPDU::setCode(CoapPDU::Code code) { _pdu[1] = code; @@ -1211,6 +1207,7 @@ * \param len Length of payload byte sequence. * \return 0 on success, 1 on failure. */ + int CoapPDU::setPayload(uint8_t *payload, int len) { if(payload==NULL) { DBG("NULL payload pointer."); @@ -1229,6 +1226,8 @@ return 0; } + + /// Returns a pointer to the payload buffer. uint8_t* CoapPDU::getPayloadPointer() { return _payloadPointer; @@ -1563,205 +1562,203 @@ /// Prints the PDU in human-readable format. void CoapPDU::printHuman() { - INFOX("__________________\r\n"); + INFO("__________________"); if(_constructedFromBuffer) { - INFOX("PDU was constructed from buffer of %d bytes\r\n",_bufferLength); + INFO("PDU was constructed from buffer of %d bytes",_bufferLength); } - INFOX("PDU is %d bytes long\r\n",_pduLength); - INFOX("CoAP Version: %d\r\n",getVersion()); + INFO("PDU is %d bytes long",_pduLength); + INFO("CoAP Version: %d",getVersion()); INFOX("Message Type: "); switch(getType()) { case COAP_CONFIRMABLE: - INFOX("Confirmable\r\n"); + INFO("Confirmable"); break; case COAP_NON_CONFIRMABLE: - INFOX("Non-Confirmable\r\n"); + INFO("Non-Confirmable"); break; case COAP_ACKNOWLEDGEMENT: - INFOX("Acknowledgement\r\n"); + INFO("Acknowledgement"); break; case COAP_RESET: - INFOX("Reset\r\n"); + INFO("Reset"); break; } - INFOX("Token length: %d\r\n",getTokenLength()); + INFO("Token length: %d",getTokenLength()); INFOX("Code: "); switch(getCode()) { case COAP_EMPTY: - INFOX("0.00 Empty"); + INFO("0.00 Empty"); break; case COAP_GET: - INFOX("0.01 GET"); + INFO("0.01 GET"); break; case COAP_POST: - INFOX("0.02 POST"); + INFO("0.02 POST"); break; case COAP_PUT: - INFOX("0.03 PUT"); + INFO("0.03 PUT"); break; case COAP_DELETE: - INFOX("0.04 DELETE"); + INFO("0.04 DELETE"); break; case COAP_CREATED: - INFOX("2.01 Created"); + INFO("2.01 Created"); break; case COAP_DELETED: - INFOX("2.02 Deleted"); + INFO("2.02 Deleted"); break; case COAP_VALID: - INFOX("2.03 Valid"); + INFO("2.03 Valid"); break; case COAP_CHANGED: - INFOX("2.04 Changed"); + INFO("2.04 Changed"); break; case COAP_CONTENT: - INFOX("2.05 Content"); + INFO("2.05 Content"); break; case COAP_BAD_REQUEST: - INFOX("4.00 Bad Request"); + INFO("4.00 Bad Request"); break; case COAP_UNAUTHORIZED: - INFOX("4.01 Unauthorized"); + INFO("4.01 Unauthorized"); break; case COAP_BAD_OPTION: - INFOX("4.02 Bad Option"); + INFO("4.02 Bad Option"); break; case COAP_FORBIDDEN: - INFOX("4.03 Forbidden"); + INFO("4.03 Forbidden"); break; case COAP_NOT_FOUND: - INFOX("4.04 Not Found"); + INFO("4.04 Not Found"); break; case COAP_METHOD_NOT_ALLOWED: - INFOX("4.05 Method Not Allowed"); + INFO("4.05 Method Not Allowed"); break; case COAP_NOT_ACCEPTABLE: - INFOX("4.06 Not Acceptable"); + INFO("4.06 Not Acceptable"); break; case COAP_PRECONDITION_FAILED: - INFOX("4.12 Precondition Failed"); + INFO("4.12 Precondition Failed"); break; case COAP_REQUEST_ENTITY_TOO_LARGE: - INFOX("4.13 Request Entity Too Large"); + INFO("4.13 Request Entity Too Large"); break; case COAP_UNSUPPORTED_CONTENT_FORMAT: - INFOX("4.15 Unsupported Content-Format"); + INFO("4.15 Unsupported Content-Format"); break; case COAP_INTERNAL_SERVER_ERROR: - INFOX("5.00 Internal Server Error"); + INFO("5.00 Internal Server Error"); break; case COAP_NOT_IMPLEMENTED: - INFOX("5.01 Not Implemented"); + INFO("5.01 Not Implemented"); break; case COAP_BAD_GATEWAY: - INFOX("5.02 Bad Gateway"); + INFO("5.02 Bad Gateway"); break; case COAP_SERVICE_UNAVAILABLE: - INFOX("5.03 Service Unavailable"); + INFO("5.03 Service Unavailable"); break; case COAP_GATEWAY_TIMEOUT: - INFOX("5.04 Gateway Timeout"); + INFO("5.04 Gateway Timeout"); break; case COAP_PROXYING_NOT_SUPPORTED: - INFOX("5.05 Proxying Not Supported"); + INFO("5.05 Proxying Not Supported"); break; case COAP_UNDEFINED_CODE: - INFOX("Undefined Code"); + INFO("Undefined Code"); break; } - INFOX("\r\n"); // print message ID - INFOX("Message ID: %u\r\n",getMessageID()); + INFO("Message ID: %u",getMessageID()); // print token value int tokenLength = getTokenLength(); uint8_t *tokenPointer = getPDUPointer()+COAP_HDR_SIZE; if(tokenLength==0) { - INFOX("No token.\r\n"); + INFO("No token."); } else { - INFOX("Token of %d bytes.\r\n",tokenLength); + INFO("Token of %d bytes.",tokenLength); INFOX(" Value: 0x"); for(int j=0; j<tokenLength; j++) { INFOX("%.2x",tokenPointer[j]); } - INFOX("\r\n"); + INFO(" "); } // print options CoapPDU::CoapOption* options = getOptions(); - INFOX("%d options:\r\n",_numOptions); + INFO("%d options:",_numOptions); for(int i=0; i<_numOptions; i++) { - INFOX("OPTION (%d/%d)\r\n",i,_numOptions); - INFOX(" Option number (delta): %hu (%hu)\r\n",options[i].optionNumber,options[i].optionDelta); + INFO("OPTION (%d/%d)",i + 1,_numOptions); + INFO(" Option number (delta): %hu (%hu)",options[i].optionNumber,options[i].optionDelta); INFOX(" Name: "); switch(options[i].optionNumber) { case COAP_OPTION_IF_MATCH: - INFOX("IF_MATCH"); + INFO("IF_MATCH"); break; case COAP_OPTION_URI_HOST: - INFOX("URI_HOST"); + INFO("URI_HOST"); break; case COAP_OPTION_ETAG: - INFOX("ETAG"); + INFO("ETAG"); break; case COAP_OPTION_IF_NONE_MATCH: - INFOX("IF_NONE_MATCH"); + INFO("IF_NONE_MATCH"); break; case COAP_OPTION_OBSERVE: - INFOX("OBSERVE"); + INFO("OBSERVE"); break; case COAP_OPTION_URI_PORT: - INFOX("URI_PORT"); + INFO("URI_PORT"); break; case COAP_OPTION_LOCATION_PATH: - INFOX("LOCATION_PATH"); + INFO("LOCATION_PATH"); break; case COAP_OPTION_URI_PATH: - INFOX("URI_PATH"); + INFO("URI_PATH"); break; case COAP_OPTION_CONTENT_FORMAT: - INFOX("CONTENT_FORMAT"); + INFO("CONTENT_FORMAT"); break; case COAP_OPTION_MAX_AGE: - INFOX("MAX_AGE"); + INFO("MAX_AGE"); break; case COAP_OPTION_URI_QUERY: - INFOX("URI_QUERY"); + INFO("URI_QUERY"); break; case COAP_OPTION_ACCEPT: - INFOX("ACCEPT"); + INFO("ACCEPT"); break; case COAP_OPTION_LOCATION_QUERY: - INFOX("LOCATION_QUERY"); + INFO("LOCATION_QUERY"); break; case COAP_OPTION_PROXY_URI: - INFOX("PROXY_URI"); + INFO("PROXY_URI"); break; case COAP_OPTION_PROXY_SCHEME: - INFOX("PROXY_SCHEME"); + INFO("PROXY_SCHEME"); break; case COAP_OPTION_BLOCK1: - INFOX("BLOCK1"); + INFO("BLOCK1"); break; case COAP_OPTION_BLOCK2: - INFOX("BLOCK2"); + INFO("BLOCK2"); break; case COAP_OPTION_SIZE1: - INFOX("SIZE1"); + INFO("SIZE1"); break; case COAP_OPTION_SIZE2: - INFOX("SIZE2"); + INFO("SIZE2"); break; default: - INFOX("Unknown option"); + INFO("Unknown option"); break; } - INFOX("\r\n"); - INFOX(" Value length: %u\r\n",options[i].optionValueLength); + INFO(" Value length: %u",options[i].optionValueLength); INFOX(" Value: \""); for(int j=0; j<options[i].optionValueLength; j++) { char c = options[i].optionValuePointer[j]; @@ -1771,14 +1768,14 @@ INFOX("\\%.2d",c); } } - INFOX("\"\r\n"); + INFO("\""); } // print payload if(_payloadLength==0) { - INFOX("No payload.\r\n"); + INFO("No payload."); } else { - INFOX("Payload of %d bytes\r\n",_payloadLength); + INFO("Payload of %d bytes",_payloadLength); INFOX(" Value: \""); for(int j=0; j<_payloadLength; j++) { char c = _payloadPointer[j]; @@ -1788,9 +1785,9 @@ INFOX("\\%.2x",c); } } - INFO("\"\r\n"); + INFO("\""); } - INFOX("__________________\r\n"); + INFO("__________________"); } /// Prints the PDU as a c array (useful for debugging or hardcoding PDUs) @@ -1812,7 +1809,7 @@ uint16_t optionValueLength = getOptionValueLength(option); int extraDeltaBytes = computeExtraBytes(optionDelta); int extraValueLengthBytes = computeExtraBytes(optionValueLength); - int totalLength = 1+extraDeltaBytes+extraValueLengthBytes+optionValueLength; + long totalLength = 1+extraDeltaBytes+extraValueLengthBytes+optionValueLength; if(totalLength>_pduLength) { totalLength = &_pdu[_pduLength-1]-option; @@ -1911,4 +1908,4 @@ /// Dumps the PDU as a byte sequence to stdout. void CoapPDU::print() { fwrite(_pdu,1,_pduLength,stdout); -} \ No newline at end of file +}
diff -r 5eec2844ad47 -r d0d57af6c9df cantcoap.h --- a/cantcoap.h Thu Jan 30 14:07:56 2014 +0000 +++ b/cantcoap.h Fri Nov 20 12:30:26 2015 +0000 @@ -1,31 +1,12 @@ -/** -Copyright (c) 2013, Ashley Mills. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +#pragma once +#pragma clang diagnostic ignored "-Wdeprecated-writable-strings" +#pragma clang diagnostic ignored "-Wconstant-logical-operand" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#pragma once - +/// Copyright (c) 2013, Ashley Mills. +//#include <unistd.h> #include <stdint.h> +#include "dbg.h" #define COAP_HDR_SIZE 4 #define COAP_OPTION_HDR_BYTE 1 @@ -150,6 +131,7 @@ int setTokenLength(uint8_t tokenLength); int getTokenLength(); uint8_t* getTokenPointer(); + long getLongToken(); int setToken(uint8_t *token, uint8_t tokenLength); // message code @@ -261,4 +243,4 @@ #define COAP_CODE_SERVICE_UNAVAILABLE 0xA3 #define COAP_CODE_GATEWAY_TIMEOUT 0xA4 #define COAP_CODE_PROXYING_NOT_SUPPORTED 0xA5 -*/ \ No newline at end of file +*/
diff -r 5eec2844ad47 -r d0d57af6c9df dbg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbg.h Fri Nov 20 12:30:26 2015 +0000 @@ -0,0 +1,23 @@ +#pragma once +#include <stdio.h> + +#define DEBUG 1 +#undef DEBUG + +#define DBG_NEWLINE "\n" + +#define INFO(...) printf(__VA_ARGS__); printf(DBG_NEWLINE); +#define INFOX(...); printf(__VA_ARGS__); +#define ERR(...) printf(__VA_ARGS__); printf(DBG_NEWLINE); + +#ifdef DEBUG + #define DBG(...) fprintf(stderr,"%s:%d ",__FILE__,__LINE__); fprintf(stderr,__VA_ARGS__); fprintf(stderr,"\r\n"); + #define DBGX(...) fprintf(stderr,__VA_ARGS__); + #define DBGLX(...) fprintf(stderr,"%s:%d ",__FILE__,__LINE__); fprintf(stderr,__VA_ARGS__); + #define DBG_PDU() printBin(); +#else + #define DBG(...) {}; + #define DBGX(...) {}; + #define DBGLX(...) {}; + #define DBG_PDU() {}; +#endif