/********************************************************************************* * 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 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 TLV::TLV(){ numTags=0; } ////////////////////////////////////// // TLV create(tag, maxLen, name) template int TLV::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 typename TLV::tlv_t *TLV::find(tagType tag){ for(int i=0;i void TLV::clear(){ cLen=0; for(int i=0;i int TLV::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 int TLV::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 uint8_t *TLV::buf(tagType tag){ tlv_t *tlv=find(tag); if(tlv) return(tlv->val); return(NULL); } ////////////////////////////////////// // TLV buf(tag, len) template uint8_t *TLV::buf(tagType tag, int len){ return(buf(tag,NULL,len)); } ////////////////////////////////////// // TLV buf(tag, src, len) template uint8_t *TLV::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;ilen;i+=255) cLen+=2; if(src) memcpy(tlv->val,src,len); return(tlv->val); } return(NULL); } ////////////////////////////////////// // TLV print() template void TLV::print(int minLogLevel){ if(homeSpan.getLogLevel()=0){ Serial.printf("%s(%d) ",tlv[i].name,tlv[i].len); for(int j=0;j0 } // loop over all TLVs } ////////////////////////////////////// // TLV pack(tlvBuf) template int TLV::pack(uint8_t *tlvBuf){ int n=0; int nBytes; for(int i=0;i=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 int TLV::len(tagType tag){ tlv_t *tlv=find(tag); if(tlv) return(tlv->len>0?tlv->len:0); return(-1); } ////////////////////////////////////// // TLV unpack(tlvBuf, nBytes) template int TLV::unpack(uint8_t *tlvBuf, int nBytes){ clear(); tagType tag; int tagLen; uint8_t *val; int currentLen; int state=0; for(int i=0;i 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 }