├── BER-TLV specification key information.docx ├── LICENSE ├── README.md └── TLV-decoder-encoder.c /BER-TLV specification key information.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliceOh/Constructed-BER-TLV-Encoder-and-Decoder/0d63a3a9cf3ab02621b8c28a5d844437713b4e4b/BER-TLV specification key information.docx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2024 Alice.Oh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Constructed-BER-TLV-Encoder-and-Decoder 2 | 3 | This project is a Constructed BER-TLV Encoder and Decoder implemented in C. 4 | 5 | ## Data Structure 6 | 7 | The TLV is designed as a linked data structure. The `Tlv_t` structure is defined as follows: 8 | 9 | ``` 10 | typedef struct { 11 | uint16_t nTag; // Tag field with length up to 2 bytes 12 | uint32_t nLength; // Length field with length up to 3 bytes, indicating up to 65535 bytes in following Value field 13 | void* pValue; // Value field pointer 14 | void* pChild; // Pointer pointing to the child TLV of next level 15 | void* pNext; // Pointer pointing to next TLV on the same level 16 | } Tlv_t; 17 | ``` 18 | 19 | This structure represents a TLV object as a node pointing to a TLV at the child level and also pointing to the next TLV on the same level. This allows the code to traverse all the TLV nodes when performing "search TLV" and "free TLV" operations. 20 | 21 | # Additional Information 22 | For more information on the Constructed BER-TLV specification, please refer to the file "BER-TLV specification key information.docx". 23 | -------------------------------------------------------------------------------- /TLV-decoder-encoder.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define TAG_PC_MASK_FIRST_BYTE 0X20 //bit 6 indicate whether the type is primitive or constructed 8 | #define TAG_NUMBER_MASK_FIRST_BYTE 0X1F //bits b5 - b1 of the first byte 9 | #define LENGTH_MASK_SECOND_BYTE 0X7F //bits b7 - b1 of the SECOND byte 10 | 11 | #define SPARE_BUFFER_SIZE 6 12 | #define MAX_VALUE_BUFFER_SIZE_IN_BYTE 1000 13 | #define MAX_TXN_REF_LEN 125 //case 1:250; case 2:125 14 | //************************************************************ 15 | //type define 16 | //************************************************************ 17 | typedef unsigned short uint16_t ; 18 | typedef unsigned int uint32_t; 19 | typedef unsigned char uint8_t; 20 | typedef int BOOL; 21 | #define true 1 22 | #define false 0 23 | 24 | typedef struct 25 | { 26 | uint16_t nTag; //Tag field with length up to 2 bytes 27 | uint32_t nLength; //Length field with length up to 3 bytes, indicating up to 65535 bytes in following Value field 28 | void* pValue; //Value field pointer 29 | void* pChild; //pointer pointing to the child TLV of next level 30 | void* pNext; //pointer pointing to next TLV on the same level 31 | } Tlv_t; 32 | 33 | BOOL DEBUG_FLAG=false;//debug message switch 34 | 35 | //local function 36 | static BOOL IsTagConstructed(uint16_t tag); 37 | 38 | //some helper function 39 | 40 | //return true--- tag indicates constructed TLV 41 | // false --- tag indicates primitive TLV 42 | static BOOL IsTagConstructed(uint16_t tag) 43 | { 44 | BOOL returnValue = false; 45 | 46 | if(((tag&0xFF) & TAG_PC_MASK_FIRST_BYTE) != 0) 47 | { 48 | returnValue = true; 49 | } 50 | return returnValue; 51 | } 52 | 53 | 54 | uint32_t swapEndian32(uint32_t num) 55 | { 56 | // Swap endian (big to little) or (little to big) 57 | uint32_t b0,b1,b2,b3; 58 | uint32_t res; 59 | 60 | b0 = (num & 0x000000ff) << 24u; 61 | b1 = (num & 0x0000ff00) << 8u; 62 | b2 = (num & 0x00ff0000) >> 8u; 63 | b3 = (num & 0xff000000) >> 24u; 64 | 65 | res = b0 | b1 | b2 | b3; 66 | printf("\n Input is 0x%X, output is 0x%X", num, res); 67 | 68 | return res; 69 | } 70 | 71 | uint16_t swapEndian16(uint16_t num) 72 | { 73 | // Swap endian (big to little) or (little to big) 74 | uint16_t b0,b1; 75 | uint16_t res; 76 | 77 | b0 = (num & 0x00ff) << 8u; 78 | b1 = (num & 0xff00) >> 8u; 79 | 80 | res = b0 | b1; 81 | printf("\n Input is 0x%X, output is 0x%X", num, res); 82 | 83 | return res; 84 | } 85 | 86 | uint16_t getRealLength(uint32_t nLength, size_t* copyLengthP) 87 | { 88 | uint16_t realValLength = 0; 89 | uint8_t tempByte = (nLength)&0xFF; 90 | if(tempByte<=0x7F)//1 byte Length field, no header 91 | { 92 | realValLength = tempByte; 93 | *copyLengthP = 1; 94 | } 95 | else 96 | { 97 | switch(tempByte) 98 | { 99 | case 0x81 : 100 | realValLength = nLength>>8; 101 | *copyLengthP = 2; 102 | break; 103 | case 0x82 : 104 | realValLength = (uint16_t)(nLength>>8); 105 | *copyLengthP = 3; 106 | break; 107 | 108 | default : 109 | printf("Unsupported format in Length field"); 110 | break; 111 | } 112 | } 113 | return realValLength; 114 | } 115 | 116 | uint32_t encode_Length(size_t valueLen) 117 | { 118 | uint32_t returnLen = 0; 119 | 120 | if(valueLen<=0x7F)//1 byte Len field 121 | { 122 | returnLen = valueLen; 123 | } 124 | else if(valueLen<=0xFF)//2 byte Len field 125 | { 126 | returnLen = (valueLen<<8)+0x81; 127 | } 128 | else if(valueLen<=0xFFFF)//3 byte Len field 129 | { 130 | returnLen = (valueLen<<8)+0x82; 131 | } 132 | 133 | return returnLen; 134 | } 135 | 136 | 137 | void printTLV(Tlv_t * tlv) 138 | { 139 | uint8_t * valueData; 140 | uint8_t tempByte=0; 141 | size_t realValLength = 0, i=0, copyLength=0; 142 | 143 | printf("\n\n tlv->nTag = 0x%X", tlv->nTag); 144 | printf("\n tlv->nLength = 0x%X", tlv->nLength); 145 | 146 | if(tlv->nLength !=0) 147 | { 148 | realValLength = getRealLength(tlv->nLength, ©Length); 149 | valueData = (uint8_t *)(tlv->pValue); 150 | for(i=0; inTag)&TAG_NUMBER_MASK_FIRST_BYTE)==TAG_NUMBER_MASK_FIRST_BYTE )//bits b5 - b1 of the first byte equal '11111' 167 | { 168 | copyLength = 2;//Tag field with 2 bytes 169 | } 170 | else 171 | { 172 | copyLength = 1;//Tag field with 1 bytes 173 | } 174 | 175 | memcpy(tempTLBuffer, &(tlv->nTag), copyLength);//put in tag into the buffer 176 | addLengthCounter += copyLength; 177 | 178 | //childTlv Length field 179 | realValLength = getRealLength(tlv->nLength, ©Length); 180 | memcpy(tempTLBuffer+addLengthCounter, &(tlv->nLength), copyLength);//put in Length field 181 | 182 | addLengthCounter += copyLength; 183 | 184 | //copy tlv TAG and Length field into transmitterBuffer 185 | memcpy(transmitterBuffer+SPARE_BUFFER_SIZE-addLengthCounter, tempTLBuffer, addLengthCounter); 186 | 187 | //childTlv Value field 188 | copyLength = realValLength; 189 | //memcpy(transmitterBuffer+addLengthCounter, tlv->pValue, copyLength); 190 | addLengthCounter += copyLength; 191 | 192 | for(i=0; i<(addLengthCounter+SPARE_BUFFER_SIZE); i++) 193 | { 194 | printf("\n\t transmitterBuffer Value[%d] = 0x%02X", i, transmitterBuffer[i]); 195 | } 196 | } 197 | 198 | 199 | //************************************************************ 200 | //test function implementation 201 | //************************************************************ 202 | 203 | // Parse the data in buffer to a TLV object. If the buffer contains more than one 204 | // TLV objects at the top level (naming root TLV) in the buffer, only the first root TLV object is parsed. 205 | // If the first root TLV object contains more than one TLV objects at the second or even further down level (naming children TLV) in the buffer, 206 | // all the children TLV of the first root TLV will parsed. 207 | // return: how many bytes have been decoded 208 | size_t TlvParseAllChildrenTLV(const uint8_t* buffer, size_t length, Tlv_t* tlv) 209 | { 210 | size_t counter = 0; 211 | static size_t childCounter = 0; 212 | size_t fistChildIndexInBuffer = 0; 213 | uint8_t tempByte = 0; 214 | size_t copyLength = 0; 215 | BOOL primitiveFlag = true; 216 | size_t i=0;//loop index 217 | uint8_t * valueData = NULL; 218 | uint32_t realValueLength = 0; 219 | Tlv_t* tlv_temp_pointer; 220 | 221 | if(tlv != NULL && buffer != NULL) 222 | { 223 | //init tlv first 224 | tlv->nTag = 0; 225 | tlv->nLength = 0; 226 | tlv->pValue = NULL; 227 | tlv->pChild = NULL; 228 | tlv->pNext = NULL; 229 | 230 | if(DEBUG_FLAG)printf("\n TlvParseAllChildrenTLV(), counter = %d, length = %d", counter, length); 231 | //skip 00 byte before the TLV data 232 | while(buffer[counter]==0) 233 | { 234 | counter++; 235 | } 236 | // parse Tag field 237 | if(buffer[counter]&TAG_PC_MASK_FIRST_BYTE) 238 | { 239 | primitiveFlag = false;//this is a contructed TLV object 240 | printf("\n\n This is a contructed TLV object."); 241 | } 242 | else 243 | { 244 | printf("\n\n This is a primitive TLV object."); 245 | } 246 | if((buffer[counter]&TAG_NUMBER_MASK_FIRST_BYTE)==TAG_NUMBER_MASK_FIRST_BYTE )//bits b5 - b1 of the first byte equal '11111' 247 | { 248 | copyLength = 2;//Tag field with 2 bytes 249 | } 250 | else 251 | { 252 | copyLength = 1;//Tag field with 1 bytes 253 | } 254 | memcpy(&tlv->nTag, &buffer[counter], copyLength);//Tag field with 2 bytes 255 | counter += copyLength; 256 | printf("\n\t tlv->nTag = 0x%X", tlv->nTag); 257 | 258 | //parse Length field 259 | tempByte = buffer[counter]; 260 | if(tempByte<=0x7F)//1 byte Length field, no header 261 | { 262 | memcpy(&tlv->nLength, &buffer[counter], 1); 263 | realValueLength = tlv->nLength; 264 | counter++; 265 | } 266 | else 267 | { 268 | switch(tempByte) 269 | { 270 | case 0x81 : 271 | case 0x82 : 272 | copyLength = tempByte&LENGTH_MASK_SECOND_BYTE; 273 | memcpy(&realValueLength, &buffer[counter+1], copyLength);//skip header to copy 274 | memcpy(&tlv->nLength, &buffer[counter], copyLength+1);//copy with header 275 | counter += (copyLength+1);//don't forget header take 1 byte 276 | break; 277 | 278 | default : 279 | printf("Unsupported format in Length field"); 280 | } 281 | } 282 | printf("\n\t tlv->nLength = 0x%X", tlv->nLength); 283 | if(DEBUG_FLAG)printf("\n\t realValueLength = 0x%X", realValueLength); 284 | //parse Value field 285 | if(realValueLength>0)//only if data is not NULL 286 | { 287 | copyLength = realValueLength; 288 | if(primitiveFlag == false)//constructed TLV 289 | { 290 | tlv->pChild = malloc( sizeof(Tlv_t) ); 291 | fistChildIndexInBuffer = counter; 292 | 293 | childCounter = TlvParseAllChildrenTLV(&buffer[fistChildIndexInBuffer], copyLength, (Tlv_t*)tlv->pChild);//recursive calling 294 | if(DEBUG_FLAG)printf("\n fistChildIndexInBuffer=%d, childCounter=%d", fistChildIndexInBuffer, childCounter); 295 | } 296 | printf("\n"); 297 | tlv->pValue = malloc(realValueLength); 298 | memcpy(tlv->pValue, &buffer[counter], copyLength);//Value field with tlv->nLength bytes 299 | counter += copyLength; 300 | valueData = (uint8_t *)(tlv->pValue); 301 | for(i=0; ipChild); 310 | //more child TLV data? 311 | while(realValueLength>childCounter) 312 | { 313 | tlv_temp_pointer->pNext = malloc( sizeof(Tlv_t) ); 314 | childCounter += TlvParseAllChildrenTLV(&buffer[fistChildIndexInBuffer+childCounter], (length-fistChildIndexInBuffer-childCounter), (Tlv_t*)tlv_temp_pointer->pNext);//recursive calling 315 | tlv_temp_pointer = (Tlv_t*)(tlv_temp_pointer->pNext); 316 | } 317 | } 318 | } 319 | else 320 | { 321 | tlv->pValue = NULL; 322 | } 323 | 324 | } 325 | return counter; 326 | } 327 | // Parse the data in buffer to a TLV object. If the buffer contains more than one 328 | // TLV objects at the same level in the buffer, only the first TLV object is parsed. 329 | // This function should be called before any other TLV function calls. 330 | // return: true-succeed; false-fail 331 | BOOL TlvParse(const uint8_t* buffer, size_t length, Tlv_t* tlv) 332 | { 333 | BOOL returnValue = false; 334 | size_t counter = 0; 335 | uint8_t tempByte = 0; 336 | size_t copyLength = 0; 337 | BOOL primitiveFlag = true; 338 | size_t i=0;//loop index 339 | uint8_t * valueData = NULL; 340 | uint32_t realValueLength = 0; 341 | 342 | if(tlv != NULL && buffer != NULL) 343 | { 344 | //init tlv first 345 | tlv->nTag = 0; 346 | tlv->nLength = 0; 347 | tlv->pValue = NULL; 348 | 349 | //skip 00 byte before the TLV data 350 | while(buffer[counter]==0) 351 | { 352 | counter++; 353 | } 354 | // parse Tag field 355 | if(buffer[counter]&TAG_PC_MASK_FIRST_BYTE) 356 | { 357 | primitiveFlag = false;//this is a contructed TLV object 358 | printf("\n\n This is a contructed TLV object."); 359 | } 360 | else 361 | { 362 | printf("\n\n This is a primitive TLV object."); 363 | } 364 | if((buffer[counter]&TAG_NUMBER_MASK_FIRST_BYTE)==TAG_NUMBER_MASK_FIRST_BYTE )//bits b5 - b1 of the first byte equal '11111' 365 | { 366 | copyLength = 2;//Tag field with 2 bytes 367 | } 368 | else 369 | { 370 | copyLength = 1;//Tag field with 1 bytes 371 | } 372 | memcpy(&tlv->nTag, &buffer[counter], copyLength);//Tag field with 2 bytes 373 | counter += copyLength; 374 | printf("\n\t tlv->nTag = 0x%X", tlv->nTag); 375 | 376 | //parse Length field 377 | tempByte = buffer[counter]; 378 | if(tempByte<=0x7F)//1 byte Length field, no header 379 | { 380 | memcpy(&tlv->nLength, &buffer[counter], 1); 381 | realValueLength = tlv->nLength; 382 | counter++; 383 | } 384 | else 385 | { 386 | switch(tempByte) 387 | { 388 | case 0x81 : 389 | case 0x82 : 390 | copyLength = tempByte&LENGTH_MASK_SECOND_BYTE; 391 | memcpy(&realValueLength, &buffer[counter+1], copyLength);//skip header to copy 392 | memcpy(&tlv->nLength, &buffer[counter], copyLength+1);//copy with header 393 | counter += (copyLength+1);//don't forget header take 1 byte 394 | break; 395 | 396 | default : 397 | printf("Unsupported format in Length field"); 398 | } 399 | } 400 | printf("\n\t tlv->nLength = 0x%X", tlv->nLength); 401 | if(DEBUG_FLAG)printf("\n\t realValueLength = 0x%X", realValueLength); 402 | //parse Value field 403 | if(realValueLength>0)//only if data is not NULL 404 | { 405 | copyLength = realValueLength; 406 | if(primitiveFlag == false)//constructed TLV 407 | { 408 | tlv->pValue = malloc( sizeof(Tlv_t) ); 409 | TlvParse(&buffer[counter], copyLength, (Tlv_t*)tlv->pValue);//recursive calling 410 | } 411 | else 412 | {//primitive TLV 413 | tlv->pValue = malloc(tlv->nLength); 414 | memcpy(tlv->pValue, &buffer[counter], copyLength);//Value field with tlv->nLength bytes 415 | counter += copyLength; 416 | valueData = (uint8_t *)(tlv->pValue); 417 | for(i=0; ipValue = NULL; 426 | } 427 | 428 | returnValue = true; 429 | } 430 | return returnValue; 431 | } 432 | 433 | 434 | //Search the TLV tag. 435 | Tlv_t* TlvSearchTagInTree(uint16_t tag, BOOL recursive,Tlv_t* tlv) 436 | { 437 | Tlv_t* tlv_p=tlv; 438 | Tlv_t* tlv_p_c=tlv; 439 | Tlv_t* returnTLV=NULL; 440 | 441 | printf("\n Come to search Tag 0x%X", tlv->nTag ); 442 | if(tlv->nTag == tag) 443 | { 444 | returnTLV = tlv; 445 | printf("\n Found Tag 0x%x "); 446 | } 447 | else if(recursive == true) 448 | { 449 | if(IsTagConstructed(tlv_p->nTag))//constructive TLV? 450 | { 451 | if(tlv_p->pChild!=NULL)//depth-first 452 | { 453 | tlv_p_c = (Tlv_t* )tlv_p->pChild;//point to child 454 | returnTLV = TlvSearchTagInTree(tag, recursive,tlv_p_c);//search child first 455 | } 456 | if((tlv_p->pNext!=NULL)&&(returnTLV==NULL)) 457 | { 458 | tlv_p_c = (Tlv_t* )tlv_p->pNext; 459 | returnTLV = TlvSearchTagInTree(tag, recursive,tlv_p_c); 460 | } 461 | } 462 | else 463 | { 464 | //free next first 465 | if(tlv_p->pNext!=NULL) 466 | { 467 | tlv_p_c = (Tlv_t* )tlv_p->pNext;//point to next 468 | returnTLV = TlvSearchTagInTree(tag, recursive,tlv_p_c); 469 | } 470 | } 471 | } 472 | return returnTLV; 473 | } 474 | 475 | 476 | // Locate a TLV encoded data object in buffer of given length (if recursively in depth-first order) 477 | // 478 | // Parameters: 479 | // buffer [IN]: The input buffer. 480 | // length [IN]: The length of input buffer. 481 | // tag [IN]: tag ID (up to 2 bytes) to find. 482 | // recursive [IN]: search sub-nodes or not 483 | // tlv [OUT]: A pointer to the found TLV object. 484 | // 485 | // Return value: 486 | // TRUE: if the tag is found. 487 | // FALSE: otherwise 488 | // 489 | // This function only supports Tag ID up to 2 bytes. 490 | BOOL TlvSearchTag(const uint8_t* buffer, size_t length, uint16_t tag, BOOL recursive, Tlv_t* tlv) 491 | { 492 | BOOL returnValue = false; 493 | Tlv_t tlv_obj; 494 | Tlv_t* tlv_p=NULL; 495 | size_t decodedCounter = 0; 496 | 497 | if(DEBUG_FLAG)printf("\n length = %d", length); 498 | while((decodedCounternTag = tag; 527 | tlv->nLength = 0;//no Value yet. 528 | tlv->pValue = buffer; 529 | tlv->pChild = NULL; 530 | tlv->pNext = NULL; 531 | return true; 532 | } 533 | 534 | // Add a TLV object to the TLV container 535 | BOOL TlvAdd(Tlv_t* tlv, const Tlv_t* childTlv) 536 | { 537 | uint32_t addLengthCounter = 0; 538 | BOOL primitiveFlag = true; 539 | uint8_t tempByte=0; 540 | size_t copyLength=0; 541 | size_t realValLength = getRealLength(tlv->nLength, ©Length); 542 | 543 | uint8_t *bufferPointer = ((uint8_t *)(tlv->pValue)) + realValLength;//offset tlv->nLength bytes to put new child TLV 544 | 545 | // childTlv Tag field 546 | if((childTlv->nTag)&TAG_PC_MASK_FIRST_BYTE) 547 | { 548 | primitiveFlag = false;//this is a contructed TLV object 549 | printf("\n This is a contructed child TLV object."); 550 | } 551 | if(((childTlv->nTag)&TAG_NUMBER_MASK_FIRST_BYTE)==TAG_NUMBER_MASK_FIRST_BYTE )//bits b5 - b1 of the first byte equal '11111' 552 | { 553 | copyLength = 2;//Tag field with 2 bytes 554 | } 555 | else 556 | { 557 | copyLength = 1;//Tag field with 1 bytes 558 | } 559 | 560 | memcpy(bufferPointer, &(childTlv->nTag), copyLength);//put in child tag 561 | addLengthCounter = copyLength; 562 | 563 | //childTlv Length field 564 | realValLength = getRealLength(childTlv->nLength, ©Length); 565 | memcpy(bufferPointer+addLengthCounter, &(childTlv->nLength), copyLength);//put in child Length field 566 | addLengthCounter += copyLength; 567 | 568 | //childTlv Value field 569 | copyLength = realValLength; 570 | memcpy(bufferPointer+addLengthCounter, childTlv->pValue, copyLength); 571 | addLengthCounter += copyLength; 572 | 573 | //update parent TLV TAG 574 | tlv->nTag |= TAG_PC_MASK_FIRST_BYTE; //make parent TLV constructed TLV 575 | 576 | //update parent TLV Length 577 | realValLength = getRealLength(tlv->nLength, ©Length); 578 | if(DEBUG_FLAG)printf("\n parent realValLength=0x%x, %d", realValLength, realValLength); 579 | realValLength += addLengthCounter; 580 | if(DEBUG_FLAG)printf("\n parent realValLength=0x%x, %d after addLengthCounter", realValLength, realValLength); 581 | tlv->nLength = encode_Length(realValLength); 582 | if(DEBUG_FLAG)printf("\n After encode length, tlv->nLength = 0x%x", tlv->nLength); 583 | return true; 584 | } 585 | 586 | // Add TLV data to the TLV container 587 | BOOL TlvAddData(Tlv_t* tlv, uint16_t tag, const uint8_t* value, size_t valueLen) 588 | { 589 | BOOL returnValue = false; 590 | uint8_t * valueData = NULL; 591 | size_t i=0; 592 | if((valueLen!=0)&&(value!=NULL)) 593 | { 594 | tlv->pChild = NULL; 595 | tlv->pNext = NULL; 596 | tlv->nTag = tag; 597 | tlv->nLength = encode_Length(valueLen); 598 | tlv->pValue = malloc(valueLen); 599 | memcpy(tlv->pValue, value, valueLen);//Value field with tlv->nLength bytes 600 | returnValue = true; 601 | } 602 | return returnValue; 603 | } 604 | 605 | //Free the TLV object. If it is a constructed TLV object, the contained child object is freed first. 606 | void TlvFree(Tlv_t* tlv) 607 | { 608 | Tlv_t* tlv_p=tlv; 609 | Tlv_t* tlv_p_c=tlv; 610 | 611 | printf("\nCome to free Tag 0x%X", tlv->nTag); 612 | if(IsTagConstructed(tlv_p->nTag))//constructive TLV? 613 | { 614 | if(tlv_p->pChild!=NULL) 615 | { 616 | tlv_p_c = (Tlv_t* )tlv_p->pChild;//point to child 617 | TlvFree(tlv_p_c);//free child first 618 | } 619 | if(tlv_p->pNext!=NULL) 620 | { 621 | tlv_p_c = (Tlv_t* )tlv_p->pNext; 622 | TlvFree(tlv_p_c); 623 | } 624 | } 625 | else 626 | { 627 | //free next first 628 | if(tlv_p->pNext!=NULL) 629 | { 630 | tlv_p_c = (Tlv_t* )tlv_p->pNext;//point to next 631 | TlvFree(tlv_p_c);//free next first 632 | } 633 | } 634 | if(tlv->pValue!=NULL){free(tlv->pValue);}//free primitive TLV 635 | printf("\nFreed Tag 0x%X", tlv->nTag); 636 | return; 637 | } 638 | 639 | 640 | 641 | //****************************************************** 642 | //DEMO function 643 | //****************************************************** 644 | uint8_t tlv1Data[] = 645 | { 646 | 0x70,0x43,0x5F,0x20,0x1A,0x56,0x49,0x53, 647 | 0x41,0x20,0x41,0x43,0x51,0x55,0x49,0x52, 648 | 0x45,0x52,0x20,0x54,0x45,0x53,0x54,0x20, 649 | 0x43,0x41,0x52,0x44,0x20,0x32,0x39,0x57, 650 | 0x11,0x47,0x61,0x73,0x90,0x01,0x01,0x00, 651 | 0x10,0xD1,0x01,0x22,0x01,0x11,0x43,0x87, 652 | 0x80,0x89,0x9F,0x1F,0x10,0x31,0x31,0x34, 653 | 0x33,0x38,0x30,0x30,0x37,0x38,0x30,0x30, 654 | 0x30,0x30,0x30,0x30,0x30,0x90,0x00 655 | }; 656 | 657 | uint8_t tlv2Data[] = 658 | { 659 | 0x00, 0x00, 660 | 0x70,0x81,0x83,0x90,0x81,0x80,0x6F,0xC4, 661 | 0x63,0xDD,0xD0,0x2A,0x73,0xB3,0x5C,0x84, 662 | 0xDA,0xA7,0x26,0xEE,0x4D,0x3F,0x25,0x32, 663 | 0x66,0x22,0xF1,0xD8,0x2A,0x07,0x48,0x11, 664 | 0xAE,0x2B,0x1B,0x9A,0x67,0xCB,0x58,0xD9, 665 | 0x55,0x73,0x5E,0xE6,0x35,0xD5,0x71,0xF3, 666 | 0x9B,0x5C,0xE0,0xF6,0x4D,0x71,0xAF,0x73, 667 | 0x2D,0x83,0xF3,0x7E,0x2B,0xD5,0x6D,0x67, 668 | 0x22,0x13,0x76,0xC9,0x9B,0x14,0x3B,0x05, 669 | 0x30,0xF2,0xFC,0xEA,0xB2,0xFE,0x63,0x50, 670 | 0xC6,0x2F,0xCE,0xA0,0xC1,0x63,0xE4,0xBD, 671 | 0x84,0xEC,0xB8,0x43,0x42,0xD0,0x5E,0xBF, 672 | 0xB6,0x8F,0x6A,0x9E,0x49,0x96,0xD2,0xCA, 673 | 0xB9,0x63,0x96,0x2E,0x54,0x8A,0x5B,0xEE, 674 | 0xF5,0xEF,0xFF,0xD0,0x19,0x55,0xB9,0x2A, 675 | 0xB5,0x06,0x4B,0xAC,0xB0,0xC8,0xBC,0x3E, 676 | 0x1C,0x40,0x28,0x6D,0xFE,0xFC 677 | }; 678 | 679 | /*//ohy self define test data 680 | uint8_t tlv2Data[] = 681 | { 682 | 0x00, 0x00, 683 | 0x70,0x82,0x83,0x01,0x90,0x82,0x80,0x01, 0x6F,0xC4, 684 | 0x63,0xDD,0xD0,0x2A,0x73,0xB3,0x5C,0x84, 685 | 0xDA,0xA7,0x26,0xEE,0x4D,0x3F,0x25,0x32, 686 | 0x66,0x22,0xF1,0xD8,0x2A,0x07,0x48,0x11, 687 | 0xAE,0x2B,0x1B,0x9A,0x67,0xCB,0x58,0xD9, 688 | 0x55,0x73,0x5E,0xE6,0x35,0xD5,0x71,0xF3, 689 | 0x9B,0x5C,0xE0,0xF6,0x4D,0x71,0xAF,0x73, 690 | 0x2D,0x83,0xF3,0x7E,0x2B,0xD5,0x6D,0x67, 691 | 0x22,0x13,0x76,0xC9,0x9B,0x14,0x3B,0x05, 692 | 0x30,0xF2,0xFC,0xEA,0xB2,0xFE,0x63,0x50, 693 | 0xC6,0x2F,0xCE,0xA0,0xC1,0x63,0xE4,0xBD, 694 | 0x84,0xEC,0xB8,0x43,0x42,0xD0,0x5E,0xBF, 695 | 0xB6,0x8F,0x6A,0x9E,0x49,0x96,0xD2,0xCA, 696 | 0xB9,0x63,0x96,0x2E,0x54,0x8A,0x5B,0xEE, 697 | 0xF5,0xEF,0xFF,0xD0,0x19,0x55,0xB9,0x2A, 698 | 0xB5,0x06,0x4B,0xAC,0xB0,0xC8,0xBC,0x3E, 699 | 0x1C,0x40,0x28,0x6D,0xFE,0xFC 700 | }; 701 | */ 702 | uint8_t tlv3Data[] = 703 | { 704 | 0x00,0x00, 705 | 0x70,0x0E,0x5A,0x08,0x47,0x61,0x73,0x90, 706 | 0x01,0x01,0x00,0x10,0x5F,0x34,0x01,0x01, 707 | 0x90,0x00 708 | }; 709 | 710 | uint8_t tlv4Data[] = 711 | { 712 | 0x70,0x59,0x60,0x14,0x57,0x12,0x47,0x61, 713 | 0x73,0x90,0x01,0x01,0x00,0x10,0xD1,0x01, 714 | 0x22,0x01,0x11,0x43,0x87,0x80,0x89,0x90, 715 | 0x5F,0x20,0x1A,0x56,0x49,0x53,0x41,0x20, 716 | 0x41,0x43,0x51,0x55,0x49,0x52,0x45,0x52, 717 | 0x20,0x54,0x45,0x53,0x54,0x20,0x43,0x41, 718 | 0x52,0x44,0x20,0x32,0x39,0x57,0x11,0x47, 719 | 0x61,0x73,0x90,0x01,0x01,0x00,0x10,0xD1, 720 | 0x01,0x22,0x01,0x11,0x43,0x87,0x80,0x89, 721 | 0x9F,0x1F,0x10,0x31,0x31,0x34,0x33,0x38, 722 | 0x30,0x30,0x37,0x38,0x30,0x30,0x30,0x30, 723 | 0x30,0x30,0x30,0x90,0x00 724 | }; 725 | 726 | void put_int32_to_char_array(int32_t value, uint8_t * valueArray) 727 | { 728 | int n=0, i=0; 729 | uint8_t byteCounter=0, returnValue=0; 730 | int32_t tempValue=value; 731 | memset(valueArray, '\0', sizeof(valueArray)); 732 | for(n=0; tempValue!=0; byteCounter++) 733 | { 734 | tempValue = tempValue >> 8; 735 | } 736 | tempValue=value; 737 | while (byteCounter-- > 0) 738 | { 739 | valueArray[i++]=tempValue&0xFF; 740 | tempValue = tempValue >> 8; 741 | } 742 | 743 | return; 744 | } 745 | 746 | 747 | void put_int16_to_char_array(uint16_t value, uint8_t * valueArray) 748 | { 749 | int n=0, i=0; 750 | uint8_t byteCounter=0, returnValue=0; 751 | int32_t tempValue=value; 752 | memset(valueArray, '\0', sizeof(valueArray)); 753 | for(n=0; tempValue!=0; byteCounter++) 754 | { 755 | tempValue = tempValue >> 8; 756 | } 757 | tempValue=value; 758 | while (byteCounter-- > 0) 759 | { 760 | valueArray[i++]=tempValue&0xFF; 761 | tempValue = tempValue >> 8; 762 | } 763 | 764 | return; 765 | } 766 | 767 | 768 | //DEMO function 769 | 770 | 771 | int main(int argc, const char * argv[]) 772 | { 773 | uint16_t tagSearch = 0x90;//tag to search 774 | Tlv_t tlv_decoder_obj[10], tlv_decoder_obj_single; 775 | BOOL recursiveFlag=true; 776 | size_t index = 0, i=0, decodedCounter=0; 777 | int tlv_decoder_obj_counter=0; 778 | 779 | uint8_t transmitterBuffer[MAX_VALUE_BUFFER_SIZE_IN_BYTE+SPARE_BUFFER_SIZE]; 780 | uint8_t * tlv_encoder_buffer = transmitterBuffer+SPARE_BUFFER_SIZE; 781 | Tlv_t tlv_encoder_parent; 782 | Tlv_t tlv_encoder_obj1, tlv_encoder_obj2, tlv_encoder_obj3, tlv_encoder_obj4; 783 | uint16_t tag_encoder=0; 784 | uint8_t TxnRef[MAX_TXN_REF_LEN+1]; 785 | int32_t amount; 786 | uint8_t txnType; 787 | uint16_t currencyCode; 788 | uint8_t valueArray[4]; 789 | uint8_t currencyCodeArray[2]; 790 | 791 | if(argc>1) 792 | { 793 | if((memcmp("debug", argv[1], 5)==0)||(memcmp("DEBUG", argv[1], 5)==0) ) 794 | { 795 | DEBUG_FLAG = true; 796 | printf("\n DEBUG turn on"); 797 | } 798 | } 799 | //DEBUG_FLAG = true;//ohy debug temp 800 | memset(transmitterBuffer, '\0', sizeof(transmitterBuffer)); 801 | memset(TxnRef, '\0', sizeof(TxnRef)); 802 | 803 | /*********************************************************/ 804 | //decoder DEMO with the given example code 805 | /*********************************************************/ 806 | /* 807 | tlv_decoder_obj_counter=0; 808 | decodedCounter = 0; 809 | 810 | while(decodedCounter=0) 817 | { 818 | TlvFree(&tlv_decoder_obj[tlv_decoder_obj_counter]);//free up the tlv space taken by malloc 819 | } 820 | */ 821 | /*********************************************************/ 822 | //decoder and search DEMO with the given example code 823 | /*********************************************************/ 824 | /* 825 | for(index=0; index<2; index++)//two case: recursive and non-recursive search 826 | { 827 | tagSearch = 0x57; 828 | printf("\n\n Search Tag 0x%X in %s mode.", tagSearch, (recursiveFlag?"recursive": "non-recursive")); 829 | if(TlvSearchTag(&tlv4Data[0], sizeof(tlv4Data), tagSearch, recursiveFlag, &tlv_decoder_obj_single)==true) 830 | { 831 | printf("\nFound the search tag 0x%X in %s mode.", tagSearch, (recursiveFlag?"recursive": "non-recursive")); 832 | } 833 | else 834 | { 835 | printf("\nCan not find the search tag 0x%X in %s mode.", tagSearch, (recursiveFlag?"recursive": "non-recursive")); 836 | } 837 | recursiveFlag=false; 838 | } 839 | */ 840 | /*********************************************************/ 841 | //Encoder and Decoder for the test structure DEMO 842 | /*********************************************************/ 843 | /* 844 | Create tags to encode the following data structure into TLV structure: 845 | typedef struct 846 | { 847 | char TxnRef[MAX_TXN_REF_LEN+1]; // Zero terminated string 848 | int32_t amount; 849 | uint8_t txnType; 850 | uint16_t currencyCode; 851 | } TxnInfo_t; 852 | */ 853 | 854 | //mimic transmitter, the transmitted data is in transmitterBuffer 855 | //create parent TLV 856 | tag_encoder = 0x70; 857 | TlvCreate(&tlv_encoder_parent, tag_encoder, tlv_encoder_buffer, MAX_VALUE_BUFFER_SIZE_IN_BYTE); 858 | if(DEBUG_FLAG){printTLV(&tlv_encoder_parent);} 859 | 860 | //create child TLV 861 | //TLV 1 862 | strncpy((char *)TxnRef, "demo string", 11); 863 | tag_encoder = 0xC1;//TAG NUMBER 1 864 | TlvAddData(&tlv_encoder_obj1, tag_encoder, TxnRef, sizeof(TxnRef)); 865 | if(DEBUG_FLAG){printTLV(&tlv_encoder_obj1);} 866 | //TLV 2 867 | amount = 0x12345678; 868 | put_int32_to_char_array(amount, valueArray); 869 | tag_encoder = 0xC2;//TAG NUMBER 2 870 | TlvAddData(&tlv_encoder_obj2, tag_encoder, valueArray, sizeof(valueArray)); 871 | if(DEBUG_FLAG){printTLV(&tlv_encoder_obj2);} 872 | //TLV 3 873 | txnType = 0xAB; 874 | tag_encoder = 0xC3;//TAG NUMBER 3 875 | TlvAddData(&tlv_encoder_obj3, tag_encoder, &txnType, sizeof(txnType)); 876 | if(DEBUG_FLAG){printTLV(&tlv_encoder_obj3);} 877 | //TLV 4 878 | currencyCode = 0xCDEF; 879 | put_int16_to_char_array(currencyCode, currencyCodeArray); 880 | tag_encoder = 0xC4;//TAG NUMBER 4 881 | TlvAddData(&tlv_encoder_obj4, tag_encoder, currencyCodeArray, sizeof(currencyCodeArray)); 882 | if(DEBUG_FLAG){printTLV(&tlv_encoder_obj4);} 883 | 884 | //add child TLV into parent TLV 885 | TlvAdd(&tlv_encoder_parent, &tlv_encoder_obj1); 886 | TlvAdd(&tlv_encoder_parent, &tlv_encoder_obj2); 887 | TlvAdd(&tlv_encoder_parent, &tlv_encoder_obj3); 888 | TlvAdd(&tlv_encoder_parent, &tlv_encoder_obj4); 889 | 890 | if(DEBUG_FLAG){printTLV(&tlv_encoder_parent);} 891 | 892 | printf("\n\n Transmitter/Encoder"); 893 | transmitterTLV(&tlv_encoder_parent, transmitterBuffer); 894 | 895 | //mimic receiver, decode data structure 896 | printf("\n\n Receiver/Decoder"); 897 | TlvParseAllChildrenTLV(transmitterBuffer, sizeof(transmitterBuffer), &tlv_decoder_obj_single); 898 | 899 | TlvFree(&tlv_decoder_obj_single); 900 | return 0; 901 | } 902 | --------------------------------------------------------------------------------