public-Homekit_Split/001-homekit_hub/lib/HomeSpan-release-1.8.1-dev/src/TLV.h
Zygfryd Homonto 5f1b6cd8f0 public
2024-07-20 21:58:04 +01:00

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
}