320 lines
8.3 KiB
C++
320 lines
8.3 KiB
C++
|
|
#pragma once
|
|
|
|
#include "HomeSpan.h"
|
|
|
|
template <class tagType, int maxTags>
|
|
class TLV {
|
|
|
|
int cLen; // total number of bytes in all defined TLV records, including TAG andf LEN (suitable for use as Content-Length in HTTP Body)
|
|
int numTags; // actual number of tags defined
|
|
|
|
struct tlv_t {
|
|
tagType tag; // TAG
|
|
int len; // LENGTH
|
|
uint8_t *val; // VALUE buffer
|
|
int maxLen; // maximum length of VALUE buffer
|
|
char *name; // abbreviated name of this TAG
|
|
};
|
|
|
|
tlv_t tlv[maxTags]; // pointer to array of TLV record structures
|
|
tlv_t *find(tagType tag); // returns pointer to TLV record with matching TAG (or NULL if no match)
|
|
|
|
public:
|
|
|
|
TLV();
|
|
|
|
int create(tagType tag, int maxLen, char *name); // creates a new TLV record of type 'tag' with 'maxLen' bytes and display 'name'
|
|
|
|
void clear(); // clear all TLV structures
|
|
int val(tagType tag); // returns VAL for TLV with matching TAG (or -1 if no match)
|
|
int val(tagType tag, uint8_t val); // sets and returns VAL for TLV with matching TAG (or -1 if no match)
|
|
uint8_t *buf(tagType tag); // returns VAL Buffer for TLV with matching TAG (or NULL if no match)
|
|
uint8_t *buf(tagType tag, int len); // set length and returns VAL Buffer for TLV with matching TAG (or NULL if no match or if LEN>MAX)
|
|
int len(tagType tag); // returns LEN for TLV matching TAG (or 0 if TAG is found but LEN not yet set; -1 if no match at all)
|
|
void print(); // prints all defined TLVs (those with length>0). For diagnostics/debugging only
|
|
int unpack(uint8_t *tlvBuf, int nBytes); // unpacks nBytes of TLV content from single byte buffer into individual TLV records (return 1 on success, 0 if fail)
|
|
int pack(uint8_t *tlvBuf); // if tlvBuf!=NULL, packs all defined TLV records (LEN>0) into a single byte buffer, spitting large TLVs into separate 255-byte chunks. Returns number of bytes (that would be) stored in buffer
|
|
int pack_old(uint8_t *buf); // packs all defined TLV records (LEN>0) into a single byte buffer, spitting large TLVs into separate 255-byte records. Returns number of bytes stored in buffer
|
|
|
|
}; // TLV
|
|
|
|
//////////////////////////////////////
|
|
// TLV contructor()
|
|
|
|
template<class tagType, int maxTags>
|
|
TLV<tagType, maxTags>::TLV(){
|
|
numTags=0;
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV create(tag, maxLen, name)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::create(tagType tag, int maxLen, char *name){
|
|
|
|
if(numTags==maxTags){
|
|
Serial.print("\n*** ERROR: Can't create new TLC tag type with name='");
|
|
Serial.print(name);
|
|
Serial.print("' - exceeded number of records reserved\n\n");
|
|
return(0);
|
|
}
|
|
|
|
tlv[numTags].tag=tag;
|
|
tlv[numTags].maxLen=maxLen;
|
|
tlv[numTags].name=name;
|
|
tlv[numTags].len=-1;
|
|
tlv[numTags].val=(uint8_t *)malloc(maxLen);
|
|
numTags++;
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV find(tag)
|
|
|
|
template<class tagType, int maxTags>
|
|
typename TLV<tagType, maxTags>::tlv_t *TLV<tagType, maxTags>::find(tagType tag){
|
|
|
|
for(int i=0;i<numTags;i++){
|
|
if(tlv[i].tag==tag)
|
|
return(tlv+i);
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV clear()
|
|
|
|
template<class tagType, int maxTags>
|
|
void TLV<tagType, maxTags>::clear(){
|
|
|
|
cLen=0;
|
|
|
|
for(int i=0;i<numTags;i++)
|
|
tlv[i].len=-1;
|
|
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV val(tag)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::val(tagType tag){
|
|
|
|
tlv_t *tlv=find(tag);
|
|
|
|
if(tlv && tlv->len>0)
|
|
return(tlv->val[0]);
|
|
|
|
return(-1);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV val(tag, val)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::val(tagType tag, uint8_t val){
|
|
|
|
tlv_t *tlv=find(tag);
|
|
|
|
if(tlv){
|
|
tlv->val[0]=val;
|
|
tlv->len=1;
|
|
cLen+=tlv->len+2;
|
|
return(val);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV buf(tag)
|
|
|
|
template<class tagType, int maxTags>
|
|
uint8_t *TLV<tagType, maxTags>::buf(tagType tag){
|
|
|
|
tlv_t *tlv=find(tag);
|
|
|
|
if(tlv)
|
|
return(tlv->val);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV buf(tag, len)
|
|
|
|
template<class tagType, int maxTags>
|
|
uint8_t *TLV<tagType, maxTags>::buf(tagType tag, int len){
|
|
|
|
tlv_t *tlv=find(tag);
|
|
|
|
if(tlv && len<=tlv->maxLen){
|
|
tlv->len=len;
|
|
cLen+=tlv->len;
|
|
|
|
for(int i=0;i<tlv->len;i+=255)
|
|
cLen+=2;
|
|
|
|
return(tlv->val);
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV print()
|
|
|
|
template<class tagType, int maxTags>
|
|
void TLV<tagType, maxTags>::print(){
|
|
|
|
if(homeSpan.logLevel<2)
|
|
return;
|
|
|
|
char buf[3];
|
|
|
|
for(int i=0;i<numTags;i++){
|
|
|
|
if(tlv[i].len>0){
|
|
Serial.print(tlv[i].name);
|
|
Serial.print("(");
|
|
Serial.print(tlv[i].len);
|
|
Serial.print(") ");
|
|
|
|
for(int j=0;j<tlv[i].len;j++){
|
|
sprintf(buf,"%02X",tlv[i].val[j]);
|
|
Serial.print(buf);
|
|
}
|
|
|
|
Serial.print("\n");
|
|
|
|
} // len>0
|
|
} // loop over all TLVs
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV pack_old(buf)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::pack_old(uint8_t *buf){
|
|
|
|
int n=0;
|
|
|
|
for(int i=0;i<numTags;i++){
|
|
|
|
if(tlv[i].len>0){
|
|
*buf++=tlv[i].tag;
|
|
*buf++=tlv[i].len;
|
|
memcpy(buf,tlv[i].val,tlv[i].len);
|
|
buf+=tlv[i].len;
|
|
n+=tlv[i].len+2;
|
|
} // len>0
|
|
|
|
} // loop over all TLVs
|
|
|
|
return(n);
|
|
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV pack(tlvBuf)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::pack(uint8_t *tlvBuf){
|
|
|
|
int n=0;
|
|
int nBytes;
|
|
|
|
for(int i=0;i<numTags;i++){
|
|
|
|
if((nBytes=tlv[i].len)>0){
|
|
for(int j=0;j<tlv[i].len;j+=255,nBytes-=255){
|
|
if(tlvBuf!=NULL){
|
|
*tlvBuf++=tlv[i].tag;
|
|
*tlvBuf++=nBytes>255?255:nBytes;
|
|
memcpy(tlvBuf,tlv[i].val+j,nBytes>255?255:nBytes);
|
|
tlvBuf+=nBytes>255?255:nBytes;
|
|
}
|
|
n+=(nBytes>255?255:nBytes)+2;
|
|
} // j-loop
|
|
} // len>0
|
|
|
|
} // loop over all TLVs
|
|
|
|
return(n);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV len(tag)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::len(tagType tag){
|
|
|
|
tlv_t *tlv=find(tag);
|
|
|
|
if(tlv)
|
|
return(tlv->len>0?tlv->len:0);
|
|
|
|
return(-1);
|
|
}
|
|
|
|
//////////////////////////////////////
|
|
// TLV unpack(tlvBuf, nBytes)
|
|
|
|
template<class tagType, int maxTags>
|
|
int TLV<tagType, maxTags>::unpack(uint8_t *tlvBuf, int nBytes){
|
|
|
|
clear();
|
|
|
|
tagType tag;
|
|
int tagLen;
|
|
uint8_t *val;
|
|
int currentLen;
|
|
int state=0;
|
|
|
|
for(int i=0;i<nBytes;i++){
|
|
|
|
switch(state){
|
|
|
|
case 0: // ready to read next tag
|
|
if((tag=(tagType)tlvBuf[i])==-1){ // read TAG; return with error if not found
|
|
clear();
|
|
return(0);
|
|
}
|
|
state=1;
|
|
break;
|
|
|
|
case 1: // ready to read tag length
|
|
tagLen=tlvBuf[i]; // read LEN
|
|
currentLen=len(tag); // get current length of existing tag
|
|
if(!(val=buf(tag,tagLen+currentLen))){ // get VAL Buffer for TAG and set LEN (returns NULL if LEN > maxLen)
|
|
clear();
|
|
return(0);
|
|
}
|
|
|
|
val+=currentLen; // move val to end of current length (tag repeats to load more than 255 bytes)
|
|
|
|
if(tagLen==0) // no bytes to read
|
|
state=0;
|
|
else // move to next state
|
|
state=2;
|
|
break;
|
|
|
|
case 2: // ready to read another byte into VAL
|
|
*val=tlvBuf[i]; // copy byte into VAL buffer
|
|
val++; // increment VAL buffer (already checked for sufficient length above)
|
|
tagLen--; // decrement number of bytes to continue copying
|
|
if(tagLen==0) // no more bytes to copy
|
|
state=0;
|
|
break;
|
|
|
|
} // switch
|
|
} // for-loop
|
|
|
|
if(state==0) // should always end back in state=0
|
|
return(1); // return success
|
|
|
|
clear();
|
|
return(0); // return fail
|
|
}
|