336 lines
9.5 KiB
C
336 lines
9.5 KiB
C
|
/*********************************************************************************
|
||
|
* MIT License
|
||
|
*
|
||
|
* Copyright (c) 2020-2023 Gregg E. Berman
|
||
|
*
|
||
|
* https://github.com/HomeSpan/HomeSpan
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in all
|
||
|
* copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*
|
||
|
********************************************************************************/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
template <class tagType, int maxTags>
|
||
|
class TLV {
|
||
|
|
||
|
int cLen; // total number of bytes in all defined TLV records, including TAG and 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
|
||
|
const 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, const 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)
|
||
|
uint8_t *buf(tagType tag, uint8_t *src, int len); // copies len bytes of src into VAL buffer, and sets length to len, for TLV with matching TAG; returns VAL Buffer on success, 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(int minLogLevel=0); // prints all defined TLVs (those with length>0), subject to specified minimum log level
|
||
|
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
|
||
|
|
||
|
}; // 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, const 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 *)HS_MALLOC(maxLen);
|
||
|
numTags++;
|
||
|
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////
|
||
|
// 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){
|
||
|
if(tlv->maxLen>0)
|
||
|
return(tlv->val[0]);
|
||
|
else
|
||
|
return(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){
|
||
|
if(tlv->maxLen>0)
|
||
|
tlv->val[0]=val;
|
||
|
tlv->len=(tlv->maxLen>0);
|
||
|
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){
|
||
|
|
||
|
return(buf(tag,NULL,len));
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////
|
||
|
// TLV buf(tag, src, len)
|
||
|
|
||
|
template<class tagType, int maxTags>
|
||
|
uint8_t *TLV<tagType, maxTags>::buf(tagType tag, uint8_t *src, 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;
|
||
|
|
||
|
if(src)
|
||
|
memcpy(tlv->val,src,len);
|
||
|
|
||
|
return(tlv->val);
|
||
|
}
|
||
|
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////
|
||
|
// TLV print()
|
||
|
|
||
|
template<class tagType, int maxTags>
|
||
|
void TLV<tagType, maxTags>::print(int minLogLevel){
|
||
|
|
||
|
if(homeSpan.getLogLevel()<minLogLevel)
|
||
|
return;
|
||
|
|
||
|
for(int i=0;i<numTags;i++){
|
||
|
|
||
|
if(tlv[i].len>=0){
|
||
|
Serial.printf("%s(%d) ",tlv[i].name,tlv[i].len);
|
||
|
|
||
|
for(int j=0;j<tlv[i].len;j++)
|
||
|
Serial.printf("%02X",tlv[i].val[j]);
|
||
|
|
||
|
Serial.printf("\n");
|
||
|
|
||
|
} // len>0
|
||
|
} // loop over all TLVs
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////
|
||
|
// 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){
|
||
|
int j=0;
|
||
|
do{
|
||
|
int wBytes=nBytes>255?255:nBytes;
|
||
|
if(tlvBuf!=NULL){
|
||
|
*tlvBuf++=tlv[i].tag;
|
||
|
*tlvBuf++=wBytes;
|
||
|
memcpy(tlvBuf,tlv[i].val+j,wBytes);
|
||
|
tlvBuf+=wBytes;
|
||
|
}
|
||
|
n+=wBytes+2;
|
||
|
j+=wBytes;
|
||
|
nBytes-=wBytes;
|
||
|
} while(nBytes>0);
|
||
|
} // 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
|
||
|
}
|