feat: 移植腾讯云物联网开发平台 C SDK

This commit is contained in:
fancyxu
2022-07-01 11:06:09 +08:00
parent 2be1169b0b
commit 0acc079ed6
195 changed files with 36646 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_common ${src_common} ${src} PARENT_SCOPE)
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,82 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_base64.h
* @brief utils base64 header file
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/**
* @brief Encode a buffer into base64 format.
*
* @param[out] dst destination buffer
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be encoded
* @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
* If that length cannot be represented, then no data is
* written to the buffer and *olen is set to the maximum
* length representable as a size_t.
*
* @note Call this function with dlen = 0 to obtain the
* required buffer size in *olen
*/
int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
/**
* @brief Decode a base64-formatted buffer.
*
* @param[out] dst destination buffer (can be NULL for checking size)
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be decoded
* @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
* not correct. *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* @note Call this function with *dst = NULL or dlen = 0 to obtain
* the required buffer size in *olen
*/
int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_

View File

@@ -0,0 +1,74 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_hmac.h
* @brief header file for utils_hmac
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "utils_sha1.h"
/**
* @brief Sha1 digest size.
*
*/
#define SHA1_DIGEST_SIZE 20
/**
* @brief Get digest of hmac-sha1.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
/**
* @brief Get digest hex of hmac-sha1. Output hexstr format.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_

View File

@@ -0,0 +1,94 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_md5.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-20
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_config.h"
#ifdef AUTH_WITH_NO_TLS
typedef struct {
uint32_t total[2]; /**< number of bytes processed */
uint32_t state[4]; /**< intermediate digest state */
uint8_t buffer[64]; /**< data block being processed */
char md5sum[33]; /**< md5sum result in hex string */
} IotMd5Context;
#else
#include "mbedtls/md5.h"
typedef struct {
mbedtls_md5_context ctx;
char md5sum[33]; /**< md5sum result in hex string */
} IotMd5Context;
#endif
/**
* @brief Reset MD5 context.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_reset(IotMd5Context *ctx);
/**
* @brief MD5 update.
*
* @param[in,out] ctx MD5 context
* @param[in] input input data
* @param[in] ilen data length
*/
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen);
/**
* @brief Finish MD5 calculation, result will store in md5sum.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_finish(IotMd5Context *ctx);
/**
* @brief Compare md5sum with context.
*
* @param[in,out] ctx MD5 context
* @param[in] md5sum md5sum to compare
* @return 0 for the same
*/
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33]);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_

View File

@@ -0,0 +1,119 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_sha1.h
* @brief header file for utils-sha1
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief SHA-1 context structure.
*
*/
typedef struct {
uint32_t total[2]; /**< number of bytes processed */
uint32_t state[5]; /**< intermediate digest state */
unsigned char buffer[64]; /**< data block being processed */
} IotSha1Context;
/**
* @brief Initialize SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be initialized
*/
void utils_sha1_init(IotSha1Context *ctx);
/**
* @brief Clear SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be cleared
*/
void utils_sha1_free(IotSha1Context *ctx);
/**
* @brief Clone (the state of) a SHA-1 context.
*
* @param[out] dst The destination context
* @param[in] src The context to be cloned
*/
void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src);
/**
* @brief SHA-1 context setup
*
* @param[in,out] ctx context to be initialized
*/
void utils_sha1_starts(IotSha1Context *ctx);
/**
* @brief SHA-1 process buffer.
*
* @param[in,out] ctx SHA-1 context
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
*/
void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen);
/**
* @brief SHA-1 final digest
*
* @param[in,out] ctx SHA-1 context
* @param[out] output SHA-1 checksum result
*/
void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20]);
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result
*/
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]);
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result hex
*/
void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40]);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_

View File

@@ -0,0 +1,240 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_base64.c
* @brief base64 encdoe & decode. Reference mbedtls/base64.c.
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-27
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-27 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "utils_base64.h"
static const unsigned char base64_enc_map[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
static const unsigned char base64_dec_map[128] = {
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62,
127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 127, 127, 127, 127, 127};
#define BASE64_SIZE_T_MAX ((size_t)-1) /* SIZE_T_MAX is not standard */
/**
* @brief Check if src buf and dst buf.
*
* @param[in] dst destination buffer
* @param[in] dlen size of the destination buffer
* @param[in] src source buffer
* @param[in] slen amount of data to be encoded
* @return 0 for valid, -1 for invalid
*/
static int _utils_base64_check_valid(unsigned char *dst, size_t dlen, const unsigned char *src, size_t slen)
{
size_t i, n;
uint32_t j, x;
/* First pass: check for validity and get output length */
for (i = n = j = 0; i < slen; i++) {
/* Skip spaces before checking for EOL */
x = 0;
while (i < slen && src[i] == ' ') {
++i;
++x;
}
/* Spaces at end of buffer are OK */
if (i == slen)
break;
if ((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n')
continue;
if (src[i] == '\n')
continue;
/* Space inside a line is an error */
if (x != 0)
return -1;
if (src[i] == '=' && ++j > 2)
return -1;
if (src[i] > 127 || base64_dec_map[src[i]] == 127)
return -1;
if (base64_dec_map[src[i]] < 64 && j != 0)
return -1;
n++;
}
// if no useful data, means error
if (n == 0) {
return -1;
}
/* The following expression is to calculate the following formula without
* risk of integer overflow in n:
* n = ( ( n * 6 ) + 7 ) >> 3;
*/
n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
n -= j;
if (!dst || dlen < n) {
return -1;
}
return 0;
}
/**
* @brief Encode a buffer into base64 format.
*
* @param[out] dst destination buffer
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be encoded
* @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
* If that length cannot be represented, then no data is
* written to the buffer and *olen is set to the maximum
* length representable as a size_t.
*
* @note Call this function with dlen = 0 to obtain the
* required buffer size in *olen
*/
int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen)
{
size_t i, n;
unsigned char *p;
if (slen == 0) {
*olen = 0;
return 0;
}
n = slen / 3 + (slen % 3 != 0);
if (n > (BASE64_SIZE_T_MAX - 1) / 4) {
*olen = BASE64_SIZE_T_MAX;
return -1;
}
n *= 4;
if ((dlen < n + 1) || (NULL == dst)) {
*olen = n + 1;
return -1;
}
n = (slen / 3) * 3;
int C1, C2, C3;
for (i = 0, p = dst; i < n; i += 3) {
C1 = *src++;
C2 = *src++;
C3 = *src++;
*p++ = base64_enc_map[(C1 >> 2) & 0x3F];
*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
*p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
*p++ = base64_enc_map[C3 & 0x3F];
}
if (i < slen) {
C1 = *src++;
C2 = ((i + 1) < slen) ? *src++ : 0;
*p++ = base64_enc_map[(C1 >> 2) & 0x3F];
*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
if ((i + 1) < slen)
*p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
else
*p++ = '=';
*p++ = '=';
}
*olen = p - dst;
*p = 0;
return 0;
}
/**
* @brief Decode a base64-formatted buffer.
*
* @param[out] dst destination buffer (can be NULL for checking size)
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be decoded
* @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
* not correct. *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* @note Call this function with *dst = NULL or dlen = 0 to obtain
* the required buffer size in *olen
*/
int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen)
{
size_t i, n;
uint32_t j, x;
unsigned char *p;
/* First pass: check for validity and get output length */
if (_utils_base64_check_valid(dst, dlen, src, slen)) {
*olen = 0;
return -1;
}
for (i = slen, j = 3, n = x = 0, p = dst; i > 0; i--, src++) {
if (*src == '\r' || *src == '\n' || *src == ' ')
continue;
j -= (base64_dec_map[*src] == 64);
x = (x << 6) | (base64_dec_map[*src] & 0x3F);
if (++n == 4) {
n = 0;
if (j > 0)
*p++ = (unsigned char)(x >> 16);
if (j > 1)
*p++ = (unsigned char)(x >> 8);
if (j > 2)
*p++ = (unsigned char)(x);
}
}
*olen = p - dst;
return 0;
}

View File

@@ -0,0 +1,144 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_hmac.c
* @brief Support hmac
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
* </table>
*/
#include "utils_hmac.h"
/**
* @brief inner/outer padding size
*
*/
#define KEY_IO_PAD_SIZE 64
/**
* @brief sha1 digest size
*
*/
#define SHA1_DIGEST_SIZE 20
/**
* @brief Binary half byte to hex char
*
* @param[in] hb half byte
* @return hex char(0~f)
*/
static char _hb2hex(uint8_t hb)
{
hb = hb & 0xF;
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
}
static int _utils_hmac_sha1_process(const char *msg, int msg_len, const uint8_t *key, int key_len, unsigned char *out)
{
IotSha1Context context;
unsigned char k_ipad[KEY_IO_PAD_SIZE]; /* inner padding - key XORd with ipad */
unsigned char k_opad[KEY_IO_PAD_SIZE]; /* outer padding - key XORd with opad */
int i;
/* start out by storing key in pads */
memset(k_ipad, 0, sizeof(k_ipad));
memset(k_opad, 0, sizeof(k_opad));
memcpy(k_ipad, key, key_len);
memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < KEY_IO_PAD_SIZE; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/* perform inner SHA */
utils_sha1_init(&context); /* init context for 1st pass */
utils_sha1_starts(&context); /* setup context for 1st pass */
utils_sha1_update(&context, k_ipad, KEY_IO_PAD_SIZE); /* start with inner pad */
utils_sha1_update(&context, (unsigned char *)msg, msg_len); /* then text of datagram */
utils_sha1_finish(&context, out); /* finish up 1st pass */
/* perform outer SHA */
utils_sha1_init(&context); /* init context for 2nd pass */
utils_sha1_starts(&context); /* setup context for 2nd pass */
utils_sha1_update(&context, k_opad, KEY_IO_PAD_SIZE); /* start with outer pad */
utils_sha1_update(&context, out, SHA1_DIGEST_SIZE); /* then results of 1st hash */
utils_sha1_finish(&context, out); /* finish up 2nd pass */
return 0;
}
/**
* @brief Get digest of hmac-sha1.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
{
unsigned char out[SHA1_DIGEST_SIZE] = {0};
if (!msg || !digest || !key) {
return -1;
}
if (key_len > KEY_IO_PAD_SIZE) {
return -1;
}
_utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
memcpy(digest, out, SHA1_DIGEST_SIZE);
return 0;
}
/**
* @brief Get digest hex of hmac-sha1.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
{
unsigned char out[SHA1_DIGEST_SIZE] = {0};
if (!msg || !digest || !key) {
return -1;
}
if (key_len > KEY_IO_PAD_SIZE) {
return -1;
}
_utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
for (int i = 0; i < SHA1_DIGEST_SIZE; ++i) {
digest[i * 2] = _hb2hex(out[i] >> 4);
digest[i * 2 + 1] = _hb2hex(out[i]);
}
return 0;
}

View File

@@ -0,0 +1,409 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_md5.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-27
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-27 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "utils_md5.h"
/**
* @brief Binary half byte to hex char
*
* @param[in] hb half byte
* @return hex char(0~f)
*/
static char _hb2hex(uint8_t hb)
{
hb = hb & 0xF;
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
}
/**
* @brief Lower md5sum.
*
* @param[out] md5_lower md5 sum lower
* @param[in] md5 md5 sum
*/
static void _lower(char md5_lower[33], const char md5[33])
{
int i = 0;
for (i = 0; i < 32; i++) {
md5_lower[i] = md5[i];
if (md5[i] >= 'A' && md5[i] <= 'F') {
md5_lower[i] += ('a' - 'A');
}
}
md5_lower[32] = '\0';
}
#ifdef AUTH_WITH_NO_TLS
/**
* @brief 32-bit integer manipulation macros (little endian)
*
*/
#define IOT_MD5_GET_UINT32_LE(n, b, i) \
{ \
(n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | ((uint32_t)(b)[(i) + 2] << 16) | \
((uint32_t)(b)[(i) + 3] << 24); \
}
#define IOT_MD5_PUT_UINT32_LE(n, b, i) \
{ \
(b)[(i)] = (uint8_t)(((n)) & 0xFF); \
(b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \
(b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \
(b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \
}
/**
* @brief Padding for md5.
*
*/
static const uint8_t sg_iot_md5_padding[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/**
* @brief Initialize md5 context.
*
* @param[in,out] ctx MD5 context
*/
static void _utils_md5_init(IotMd5Context *ctx)
{
memset(ctx, 0, sizeof(IotMd5Context));
}
/**
* @brief Start md5 calculate.
*
* @param[in,out] ctx MD5 context
*/
static void _utils_md5_start(IotMd5Context *ctx)
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
}
/**
* @brief Calculate md5.
*
* @param[in,out] ctx MD5 context
* @param[in] data data to calculate
*/
static void _utils_md5_process(IotMd5Context *ctx, const uint8_t data[64])
{
uint32_t X[16], A, B, C, D;
IOT_MD5_GET_UINT32_LE(X[0], data, 0);
IOT_MD5_GET_UINT32_LE(X[1], data, 4);
IOT_MD5_GET_UINT32_LE(X[2], data, 8);
IOT_MD5_GET_UINT32_LE(X[3], data, 12);
IOT_MD5_GET_UINT32_LE(X[4], data, 16);
IOT_MD5_GET_UINT32_LE(X[5], data, 20);
IOT_MD5_GET_UINT32_LE(X[6], data, 24);
IOT_MD5_GET_UINT32_LE(X[7], data, 28);
IOT_MD5_GET_UINT32_LE(X[8], data, 32);
IOT_MD5_GET_UINT32_LE(X[9], data, 36);
IOT_MD5_GET_UINT32_LE(X[10], data, 40);
IOT_MD5_GET_UINT32_LE(X[11], data, 44);
IOT_MD5_GET_UINT32_LE(X[12], data, 48);
IOT_MD5_GET_UINT32_LE(X[13], data, 52);
IOT_MD5_GET_UINT32_LE(X[14], data, 56);
IOT_MD5_GET_UINT32_LE(X[15], data, 60);
#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define P(a, b, c, d, k, s, t) \
{ \
a += F(b, c, d) + X[k] + t; \
a = S(a, s) + b; \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
#define F(x, y, z) (z ^ (x & (y ^ z)))
P(A, B, C, D, 0, 7, 0xD76AA478);
P(D, A, B, C, 1, 12, 0xE8C7B756);
P(C, D, A, B, 2, 17, 0x242070DB);
P(B, C, D, A, 3, 22, 0xC1BDCEEE);
P(A, B, C, D, 4, 7, 0xF57C0FAF);
P(D, A, B, C, 5, 12, 0x4787C62A);
P(C, D, A, B, 6, 17, 0xA8304613);
P(B, C, D, A, 7, 22, 0xFD469501);
P(A, B, C, D, 8, 7, 0x698098D8);
P(D, A, B, C, 9, 12, 0x8B44F7AF);
P(C, D, A, B, 10, 17, 0xFFFF5BB1);
P(B, C, D, A, 11, 22, 0x895CD7BE);
P(A, B, C, D, 12, 7, 0x6B901122);
P(D, A, B, C, 13, 12, 0xFD987193);
P(C, D, A, B, 14, 17, 0xA679438E);
P(B, C, D, A, 15, 22, 0x49B40821);
#undef F
#define F(x, y, z) (y ^ (z & (x ^ y)))
P(A, B, C, D, 1, 5, 0xF61E2562);
P(D, A, B, C, 6, 9, 0xC040B340);
P(C, D, A, B, 11, 14, 0x265E5A51);
P(B, C, D, A, 0, 20, 0xE9B6C7AA);
P(A, B, C, D, 5, 5, 0xD62F105D);
P(D, A, B, C, 10, 9, 0x02441453);
P(C, D, A, B, 15, 14, 0xD8A1E681);
P(B, C, D, A, 4, 20, 0xE7D3FBC8);
P(A, B, C, D, 9, 5, 0x21E1CDE6);
P(D, A, B, C, 14, 9, 0xC33707D6);
P(C, D, A, B, 3, 14, 0xF4D50D87);
P(B, C, D, A, 8, 20, 0x455A14ED);
P(A, B, C, D, 13, 5, 0xA9E3E905);
P(D, A, B, C, 2, 9, 0xFCEFA3F8);
P(C, D, A, B, 7, 14, 0x676F02D9);
P(B, C, D, A, 12, 20, 0x8D2A4C8A);
#undef F
#define F(x, y, z) (x ^ y ^ z)
P(A, B, C, D, 5, 4, 0xFFFA3942);
P(D, A, B, C, 8, 11, 0x8771F681);
P(C, D, A, B, 11, 16, 0x6D9D6122);
P(B, C, D, A, 14, 23, 0xFDE5380C);
P(A, B, C, D, 1, 4, 0xA4BEEA44);
P(D, A, B, C, 4, 11, 0x4BDECFA9);
P(C, D, A, B, 7, 16, 0xF6BB4B60);
P(B, C, D, A, 10, 23, 0xBEBFBC70);
P(A, B, C, D, 13, 4, 0x289B7EC6);
P(D, A, B, C, 0, 11, 0xEAA127FA);
P(C, D, A, B, 3, 16, 0xD4EF3085);
P(B, C, D, A, 6, 23, 0x04881D05);
P(A, B, C, D, 9, 4, 0xD9D4D039);
P(D, A, B, C, 12, 11, 0xE6DB99E5);
P(C, D, A, B, 15, 16, 0x1FA27CF8);
P(B, C, D, A, 2, 23, 0xC4AC5665);
#undef F
#define F(x, y, z) (y ^ (x | ~z))
P(A, B, C, D, 0, 6, 0xF4292244);
P(D, A, B, C, 7, 10, 0x432AFF97);
P(C, D, A, B, 14, 15, 0xAB9423A7);
P(B, C, D, A, 5, 21, 0xFC93A039);
P(A, B, C, D, 12, 6, 0x655B59C3);
P(D, A, B, C, 3, 10, 0x8F0CCC92);
P(C, D, A, B, 10, 15, 0xFFEFF47D);
P(B, C, D, A, 1, 21, 0x85845DD1);
P(A, B, C, D, 8, 6, 0x6FA87E4F);
P(D, A, B, C, 15, 10, 0xFE2CE6E0);
P(C, D, A, B, 6, 15, 0xA3014314);
P(B, C, D, A, 13, 21, 0x4E0811A1);
P(A, B, C, D, 4, 6, 0xF7537E82);
P(D, A, B, C, 11, 10, 0xBD3AF235);
P(C, D, A, B, 2, 15, 0x2AD7D2BB);
P(B, C, D, A, 9, 21, 0xEB86D391);
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
}
/**
* @brief Reset MD5 context.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_reset(IotMd5Context *ctx)
{
memset(ctx, 0, sizeof(IotMd5Context));
_utils_md5_init(ctx);
_utils_md5_start(ctx);
}
/**
* @brief MD5 update.
*
* @param[in,out] ctx MD5 context
* @param[in] input input data
* @param[in] ilen data length
*/
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
{
size_t fill;
uint32_t left;
if (ilen == 0) {
return;
}
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < (uint32_t)ilen) {
ctx->total[1]++;
}
if (left && ilen >= fill) {
memcpy((void *)(ctx->buffer + left), input, fill);
_utils_md5_process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
_utils_md5_process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
}
/**
* @brief Finish MD5 calculation, result will store in md5sum.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_finish(IotMd5Context *ctx)
{
int i;
uint32_t last, padn;
uint32_t high, low;
uint8_t msglen[8];
uint8_t output_tmp[16];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
IOT_MD5_PUT_UINT32_LE(low, msglen, 0);
IOT_MD5_PUT_UINT32_LE(high, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
utils_md5_update(ctx, sg_iot_md5_padding, padn);
utils_md5_update(ctx, msglen, 8);
IOT_MD5_PUT_UINT32_LE(ctx->state[0], output_tmp, 0);
IOT_MD5_PUT_UINT32_LE(ctx->state[1], output_tmp, 4);
IOT_MD5_PUT_UINT32_LE(ctx->state[2], output_tmp, 8);
IOT_MD5_PUT_UINT32_LE(ctx->state[3], output_tmp, 12);
for (i = 0; i < 16; ++i) {
ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
}
ctx->md5sum[32] = '\0';
}
/**
* @brief Compare md5sum with context.
*
* @param[in,out] ctx MD5 context
* @param[in] md5sum md5sum to compare
* @return 0 for the same
*/
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
{
char md5sum_lower[33];
_lower(md5sum_lower, md5sum);
return strncmp(ctx->md5sum, md5sum_lower, 32);
}
#else
/**
* @brief Reset MD5 context.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_reset(IotMd5Context *ctx)
{
memset(ctx, 0, sizeof(IotMd5Context));
mbedtls_md5_init(&ctx->ctx);
mbedtls_md5_starts(&ctx->ctx);
}
/**
* @brief MD5 update.
*
* @param[in,out] ctx MD5 context
* @param[in] input input data
* @param[in] ilen data length
*/
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
{
mbedtls_md5_update(&ctx->ctx, input, ilen);
}
/**
* @brief Finish MD5 calculation, result will store in md5sum.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_finish(IotMd5Context *ctx)
{
int i;
uint8_t output_tmp[16];
mbedtls_md5_finish(&ctx->ctx, output_tmp);
for (i = 0; i < 16; ++i) {
ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
}
ctx->md5sum[32] = '\0';
}
/**
* @brief Compare md5sum with context.
*
* @param[in,out] ctx MD5 context
* @param[in] md5sum md5sum to compare
* @return 0 for the same
*/
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
{
char md5sum_lower[33];
_lower(md5sum_lower, md5sum);
return strncmp(ctx->md5sum, md5sum_lower, 32);
}
#endif

View File

@@ -0,0 +1,408 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_sha1.c
* @brief SHA-1 operation, reference mbedtls
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
* </table>
*/
#include "utils_sha1.h"
#ifndef IOT_SHA1_GET_UINT32_BE
/**
* @brief Get 32-bit integer manipulation macros (big endian)
*
*/
#define IOT_SHA1_GET_UINT32_BE(n, b, i) \
{ \
(n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | \
((uint32_t)(b)[(i) + 3]); \
}
#endif
#ifndef IOT_SHA1_PUT_UINT32_BE
/**
* @brief Put 32-bit integer manipulation macros (big endian)
*
*/
#define IOT_SHA1_PUT_UINT32_BE(n, b, i) \
{ \
(b)[(i)] = (unsigned char)((n) >> 24); \
(b)[(i) + 1] = (unsigned char)((n) >> 16); \
(b)[(i) + 2] = (unsigned char)((n) >> 8); \
(b)[(i) + 3] = (unsigned char)((n)); \
}
#endif
/**
* @brief Binary half byte to hex char
*
* @param[in] hb half byte
* @return hex char(0~f)
*/
static char _hb2hex(uint8_t hb)
{
hb = hb & 0xF;
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
}
/**
* @brief Implementation that should never be optimized out by the compiler.
*
* @param[in] v pointer to ctx
* @param[in] n sizeof ctx
*/
static void utils_sha1_zeroize(void *v, size_t n)
{
volatile unsigned char *p = v;
while (n--) {
*p++ = 0;
}
}
/**
* @brief Initialize SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be initialized
*/
void utils_sha1_init(IotSha1Context *ctx)
{
memset(ctx, 0, sizeof(IotSha1Context));
}
/**
* @brief Clear SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be cleared
*/
void utils_sha1_free(IotSha1Context *ctx)
{
if (!ctx) {
return;
}
utils_sha1_zeroize(ctx, sizeof(IotSha1Context));
}
/**
* @brief Clone (the state of) a SHA-1 context.
*
* @param[out] dst The destination context
* @param[in] src The context to be cloned
*/
void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src)
{
*dst = *src;
}
/**
* @brief SHA-1 context setup
*
* @param[in,out] ctx context to be initialized
*/
void utils_sha1_starts(IotSha1Context *ctx)
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
ctx->state[4] = 0xC3D2E1F0;
}
/**
* @brief SHA-1 process
*
* @param[in,out] ctx pointer to ctx
* @param[in] data data to be calculated
*/
void utils_sha1_process(IotSha1Context *ctx, const unsigned char data[64])
{
uint32_t temp, W[16], A, B, C, D, E;
IOT_SHA1_GET_UINT32_BE(W[0], data, 0);
IOT_SHA1_GET_UINT32_BE(W[1], data, 4);
IOT_SHA1_GET_UINT32_BE(W[2], data, 8);
IOT_SHA1_GET_UINT32_BE(W[3], data, 12);
IOT_SHA1_GET_UINT32_BE(W[4], data, 16);
IOT_SHA1_GET_UINT32_BE(W[5], data, 20);
IOT_SHA1_GET_UINT32_BE(W[6], data, 24);
IOT_SHA1_GET_UINT32_BE(W[7], data, 28);
IOT_SHA1_GET_UINT32_BE(W[8], data, 32);
IOT_SHA1_GET_UINT32_BE(W[9], data, 36);
IOT_SHA1_GET_UINT32_BE(W[10], data, 40);
IOT_SHA1_GET_UINT32_BE(W[11], data, 44);
IOT_SHA1_GET_UINT32_BE(W[12], data, 48);
IOT_SHA1_GET_UINT32_BE(W[13], data, 52);
IOT_SHA1_GET_UINT32_BE(W[14], data, 56);
IOT_SHA1_GET_UINT32_BE(W[15], data, 60);
#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define R(t) \
(temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = S(temp, 1)))
#define P(a, b, c, d, e, x) \
{ \
e += S(a, 5) + F(b, c, d) + K + x; \
b = S(b, 30); \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
#define F(x, y, z) (z ^ (x & (y ^ z)))
#define K 0x5A827999
P(A, B, C, D, E, W[0]);
P(E, A, B, C, D, W[1]);
P(D, E, A, B, C, W[2]);
P(C, D, E, A, B, W[3]);
P(B, C, D, E, A, W[4]);
P(A, B, C, D, E, W[5]);
P(E, A, B, C, D, W[6]);
P(D, E, A, B, C, W[7]);
P(C, D, E, A, B, W[8]);
P(B, C, D, E, A, W[9]);
P(A, B, C, D, E, W[10]);
P(E, A, B, C, D, W[11]);
P(D, E, A, B, C, W[12]);
P(C, D, E, A, B, W[13]);
P(B, C, D, E, A, W[14]);
P(A, B, C, D, E, W[15]);
P(E, A, B, C, D, R(16));
P(D, E, A, B, C, R(17));
P(C, D, E, A, B, R(18));
P(B, C, D, E, A, R(19));
#undef K
#undef F
#define F(x, y, z) (x ^ y ^ z)
#define K 0x6ED9EBA1
P(A, B, C, D, E, R(20));
P(E, A, B, C, D, R(21));
P(D, E, A, B, C, R(22));
P(C, D, E, A, B, R(23));
P(B, C, D, E, A, R(24));
P(A, B, C, D, E, R(25));
P(E, A, B, C, D, R(26));
P(D, E, A, B, C, R(27));
P(C, D, E, A, B, R(28));
P(B, C, D, E, A, R(29));
P(A, B, C, D, E, R(30));
P(E, A, B, C, D, R(31));
P(D, E, A, B, C, R(32));
P(C, D, E, A, B, R(33));
P(B, C, D, E, A, R(34));
P(A, B, C, D, E, R(35));
P(E, A, B, C, D, R(36));
P(D, E, A, B, C, R(37));
P(C, D, E, A, B, R(38));
P(B, C, D, E, A, R(39));
#undef K
#undef F
#define F(x, y, z) ((x & y) | (z & (x | y)))
#define K 0x8F1BBCDC
P(A, B, C, D, E, R(40));
P(E, A, B, C, D, R(41));
P(D, E, A, B, C, R(42));
P(C, D, E, A, B, R(43));
P(B, C, D, E, A, R(44));
P(A, B, C, D, E, R(45));
P(E, A, B, C, D, R(46));
P(D, E, A, B, C, R(47));
P(C, D, E, A, B, R(48));
P(B, C, D, E, A, R(49));
P(A, B, C, D, E, R(50));
P(E, A, B, C, D, R(51));
P(D, E, A, B, C, R(52));
P(C, D, E, A, B, R(53));
P(B, C, D, E, A, R(54));
P(A, B, C, D, E, R(55));
P(E, A, B, C, D, R(56));
P(D, E, A, B, C, R(57));
P(C, D, E, A, B, R(58));
P(B, C, D, E, A, R(59));
#undef K
#undef F
#define F(x, y, z) (x ^ y ^ z)
#define K 0xCA62C1D6
P(A, B, C, D, E, R(60));
P(E, A, B, C, D, R(61));
P(D, E, A, B, C, R(62));
P(C, D, E, A, B, R(63));
P(B, C, D, E, A, R(64));
P(A, B, C, D, E, R(65));
P(E, A, B, C, D, R(66));
P(D, E, A, B, C, R(67));
P(C, D, E, A, B, R(68));
P(B, C, D, E, A, R(69));
P(A, B, C, D, E, R(70));
P(E, A, B, C, D, R(71));
P(D, E, A, B, C, R(72));
P(C, D, E, A, B, R(73));
P(B, C, D, E, A, R(74));
P(A, B, C, D, E, R(75));
P(E, A, B, C, D, R(76));
P(D, E, A, B, C, R(77));
P(C, D, E, A, B, R(78));
P(B, C, D, E, A, R(79));
#undef K
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
ctx->state[4] += E;
}
/**
* @brief SHA-1 process buffer.
*
* @param[in,out] ctx SHA-1 context
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
*/
void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen)
{
size_t fill;
uint32_t left;
if (ilen == 0) {
return;
}
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < (uint32_t)ilen) {
ctx->total[1]++;
}
if (left && ilen >= fill) {
memcpy((void *)(ctx->buffer + left), input, fill);
utils_sha1_process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
utils_sha1_process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
}
static const unsigned char iot_sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
* @brief SHA-1 final digest
*
* @param[in,out] ctx SHA-1 context
* @param[out] output SHA-1 checksum result
*/
void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20])
{
uint32_t last, padn;
uint32_t high, low;
unsigned char msglen[8];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
IOT_SHA1_PUT_UINT32_BE(high, msglen, 0);
IOT_SHA1_PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
utils_sha1_update(ctx, iot_sha1_padding, padn);
utils_sha1_update(ctx, msglen, 8);
IOT_SHA1_PUT_UINT32_BE(ctx->state[0], output, 0);
IOT_SHA1_PUT_UINT32_BE(ctx->state[1], output, 4);
IOT_SHA1_PUT_UINT32_BE(ctx->state[2], output, 8);
IOT_SHA1_PUT_UINT32_BE(ctx->state[3], output, 12);
IOT_SHA1_PUT_UINT32_BE(ctx->state[4], output, 16);
}
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result
*/
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20])
{
IotSha1Context ctx;
utils_sha1_init(&ctx);
utils_sha1_starts(&ctx);
utils_sha1_update(&ctx, input, ilen);
utils_sha1_finish(&ctx, output);
utils_sha1_free(&ctx);
}
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result hex
*/
void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40])
{
unsigned char out[20 + 1] = {0};
utils_sha1(input, ilen, out);
for (int i = 0; i < 20; ++i) {
output_hex[i * 2] = _hb2hex(out[i] >> 4);
output_hex[i * 2 + 1] = _hb2hex(out[i]);
}
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -0,0 +1,179 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_cryptology.cc
* @brief test cryptology
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-07
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotSha1Context
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "utils_base64.h"
#include "utils_hmac.h"
#include "utils_md5.h"
#include "utils_sha1.h"
namespace cryptology_unittest {
/**
* @brief Test base64.
*
*/
TEST(CryptologyTest, base64) {
const uint8_t base64_test_dec[64] = {0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 0xBF, 0x17, 0xD9, 0xA2, 0xC4,
0x17, 0x1A, 0x01, 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 0x0C, 0xB6,
0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA,
0x31, 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 0x00, 0x43, 0xE9, 0x54,
0x97, 0xAF, 0x50, 0x4B, 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97};
const uint8_t base64_test_enc[] =
"JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
"swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
size_t len;
uint8_t buffer[128];
ASSERT_EQ(utils_base64encode(buffer, sizeof(buffer), &len, base64_test_dec, sizeof(base64_test_dec)), 0);
ASSERT_EQ(memcmp(base64_test_enc, buffer, len), 0);
ASSERT_EQ(utils_base64decode(buffer, sizeof(buffer), &len, base64_test_enc, sizeof(base64_test_enc) - 1), 0);
ASSERT_EQ(memcmp(base64_test_dec, buffer, len), 0);
}
/**
* @brief Test hmac.
*
*/
TEST(CryptologyTest, hmac) {
/**
* @brief HMAC-SHA1
*
*/
const char test_buf[] = "Here is a test for hmac-sha1!";
const uint8_t key[] = "0123456789123456";
const char result[] = "614b650ffefff7862c1bc7fdd9de4e472a8184c4";
char buf[128] = {0};
ASSERT_EQ(utils_hmac_sha1_hex(test_buf, strlen(test_buf), key, sizeof(key), buf), 0);
ASSERT_EQ(memcmp(buf, result, sizeof(result)), 0);
}
/**
* @brief Test sha1.
*
*/
TEST(CryptologyTest, sha1) {
/*
* FIPS-180-1 test vectors
*/
const uint8_t sha1_test_buf[3][57] = {{"abc"}, {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, {""}};
const int sha1_test_buflen[3] = {3, 56, 1000};
const uint8_t sha1_test_sum[3][20] = {
{
0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D,
},
{
0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1,
},
{
0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E,
0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F,
},
};
int i, j, buflen, rc = 0;
uint8_t buf[1024];
uint8_t sha1sum[20];
IotSha1Context ctx;
utils_sha1_init(&ctx);
for (i = 0; i < 3; i++) {
utils_sha1_starts(&ctx);
if (i == 2) {
memset(buf, 'a', buflen = 1000);
for (j = 0; j < 1000; j++) {
utils_sha1_update(&ctx, buf, buflen);
}
} else {
utils_sha1_update(&ctx, sha1_test_buf[i], sha1_test_buflen[i]);
}
utils_sha1_finish(&ctx, sha1sum);
ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[i], 20), 0);
}
utils_sha1_free(&ctx);
utils_sha1(sha1_test_buf[0], 3, sha1sum);
ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[0], 20), 0);
}
/**
* @brief Test md5.
*
*/
TEST(CryptologyTest, md5) {
/*
* RFC 1321 test vectors
*/
const uint8_t md5_test_buf[7][81] = {
{""},
{"a"},
{"abc"},
{"message digest"},
{"abcdefghijklmnopqrstuvwxyz"},
{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
{"12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
};
const size_t md5_test_buflen[7] = {0, 1, 3, 14, 26, 62, 80};
const char md5_test_sum[7][33] = {
{"D41D8CD98F00B204E9800998ECF8427E"}, {"0CC175B9C0F1B6A831C399E269772661"}, {"900150983CD24FB0D6963F7D28E17F72"},
{"F96B697D7CB7938D525A2F31AAF161D0"}, {"C3FCD3D76192E4007DFB496CCA67E13B"}, {"D174AB98D277D9F5A5611C2C9F419D9F"},
{"57EDF4A22BE3C955AC49DA2E2107B67A"},
};
/*
* Checkup routine
*/
IotMd5Context md5_ctx;
for (int i = 0; i < 7; i++) {
utils_md5_reset(&md5_ctx);
utils_md5_update(&md5_ctx, md5_test_buf[i], md5_test_buflen[i]);
utils_md5_finish(&md5_ctx);
ASSERT_EQ(utils_md5_compare(&md5_ctx, md5_test_sum[i]), 0);
}
}
} // namespace cryptology_unittest

View File

@@ -0,0 +1,10 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_common ${src_common} ${src} PARENT_SCOPE)
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,335 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_packet.h
* @brief header file for mqtt packet
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-24
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-24 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <string.h>
/**
* @brief header 1 byte + remaining length 1~4 byte(s).
*
*/
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
#define MAX_MQTT_FIXED_HEADER_LEN (1 + MAX_NO_OF_REMAINING_LENGTH_BYTES)
#define MIN_MQTT_FIXED_HEADER_LEN (1 + 1)
/**
* @brief Check if short buffer.
*
*/
#define SHORT_BUFFER_CHECK(buf_len, len) \
if (buf_len < len) { \
return MQTT_ERR_SHORT_BUFFER; \
}
/**
* @brief Error code for mqtt packet。
*
*/
typedef enum {
MQTT_RET_PACKET_OK = 0,
MQTT_ERR_INVALID_PACKET_TYPE = -1,
MQTT_ERR_SHORT_BUFFER = -2,
MQTT_ERR_VERSION_UNSUPPORTED = -3,
MQTT_ERR_SUB_COUNT_EXCEED = -4,
} MQTTPacketErrCode;
/**
* @brief MQTT packet type.
*
*/
typedef enum {
CONNECT = 1,
CONNACK,
PUBLISH,
PUBACK,
PUBREC,
PUBREL,
PUBCOMP,
SUBSCRIBE,
SUBACK,
UNSUBSCRIBE,
UNSUBACK,
PINGREQ,
PINGRESP,
DISCONNECT
} MQTTPacketType;
/**
* @brief Defines the MQTT "Last Will and Testament" (LWT) settings for the connect packet.
*
*/
typedef struct {
const char* topic_name; /**< The LWT topic to which the LWT message will be published */
const char* message; /**< The LWT payload */
uint8_t retained; /**< The retained flag for the LWT message */
uint8_t qos; /**< The quality of service setting for the LWT message */
} MQTTPacketWillOptions;
/**
* @brief MQTT packet connect option.
*
*/
typedef struct {
uint8_t mqtt_version; /**< Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 */
const char* client_id;
uint16_t keep_alive_interval;
uint8_t clean_session;
uint8_t will_flag;
MQTTPacketWillOptions will;
char* username;
char* password;
} MQTTPacketConnectOption;
/**
* @brief Bitfields for the MQTT header byte.
*
*/
typedef union {
uint8_t byte; /**< the whole byte */
struct {
uint8_t retain : 1; /**< retained flag bit */
uint8_t qos : 2; /**< QoS value, 0, 1 or 2 */
uint8_t dup : 1; /**< DUP flag bit */
uint8_t type : 4; /**< message type nibble */
} bits;
} MQTTHeader;
/**
* @brief Connect flags byte.
*
*/
typedef union {
uint8_t all; /**< all connect flags */
struct {
uint8_t : 1; /**< unused */
uint8_t clean_session : 1; /**< cleansession flag */
uint8_t will : 1; /**< will flag */
uint8_t will_qos : 2; /**< will QoS value */
uint8_t will_retain : 1; /**< will retain setting */
uint8_t password : 1; /**< 3.1 password */
uint8_t username : 1; /**< 3.1 user name */
} bits;
} MQTTConnectFlags;
/**
* @brief connack flags byte.
*
*/
typedef union {
unsigned char all; /**< all connack flags */
struct {
unsigned int sessionpresent : 1; /**< session present flag */
unsigned int reserved : 7; /**< unused */
} bits;
} MQTTConnackFlags;
/**
* @brief Flags for publish.
*
*/
typedef struct {
uint8_t dup;
uint8_t qos;
uint8_t retain;
} MQTTPublishFlags;
/**
* @brief Connect return code.
*
*/
typedef enum {
CONNACK_CONNECTION_ACCEPTED = 0, /**< connection accepted */
CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, /**< connection refused: unaccepted protocol version */
CONNACK_IDENTIFIER_REJECTED_ERROR = 2, /**< connection refused: identifier rejected */
CONNACK_SERVER_UNAVAILABLE_ERROR = 3, /**< connection refused: server unavailable */
CONNACK_BAD_USERDATA_ERROR = 4, /**< connection refused: bad user name or password */
CONNACK_NOT_AUTHORIZED_ERROR = 5 /**< connection refused: not authorized */
} MQTTConnackReturnCodes;
/**
* @brief Serialize the connect options into the buffer. See 3.1.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] options the options to be used to build the connect packet
* @return serialized length, or error if <= 0
*/
int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options);
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len);
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len);
/**
* @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] flags the MQTT dup, qos, retained flag
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] topic_name char * - the MQTT topic in the publish
* @param[in] payload byte buffer - the MQTT publish payload
* @param[in] payload_len integer - the length of the MQTT payload
* @return serialized length, or error if <= 0
*/
int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
const char* topic_name, const uint8_t* payload, int payload_len);
/**
* @brief Serialize a puback packet into the supplied buffer. See 3.4.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @return serialized length, or error if <= 0
*/
int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id);
/**
* @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied bufferr
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters and reqQos arrays
* @param[in] topic_filters - array of topic filter names
* @param[in] requested_qos - array of requested QoS
* @return serialized length, or error if <= 0
*/
int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
char* topic_filters[], const int requested_qos[]);
/**
* @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
*
* @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters array
* @param[in] topic_filters - array of topic filter names
* @return serialized length, or error if <= 0
*/
int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[]);
/**
* @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
* @param[out] connack_rc returned integer value of the connack return code
* @return @see MQTTPacketErrCode
*/
int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc);
/**
* @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @return @see MQTTPacketErrCode
*/
int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len);
/**
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] flags the MQTT dup, qos, retained flag
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] topic_name returned string - the MQTT topic in the publish
* @param[out] topic_len returned integer - the length of the MQTT topic
* @param[out] payload returned byte buffer - the MQTT publish payload
* @param[out] payload_len returned integer - the length of the MQTT payload
* @return @see MQTTPacketErrCode
*/
int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
char** topic_name, int* topic_len, uint8_t** payload, int* payload_len);
/**
* @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
/**
* @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
* @param[out] count returned integer - number of members in the grantedQoSs array
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] granted_qos returned array of integers - the granted qualities of service
* @return @see MQTTPacketErrCode
*/
int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
int granted_qos[]);
/**
* @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_

View File

@@ -0,0 +1,364 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_packet_deserialize.c
* @brief implements mqtt packet deserialize. Reference paho.mqtt.embedded-c &
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-21
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "mqtt_packet.h"
/**
* @brief Reads one character from the input buffer. See 1.5.1.
*
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return the character read
*/
static inline char _read_char(uint8_t** pptr)
{
char c = **pptr;
(*pptr)++;
return c;
}
/**
* @brief Calculates an integer from two bytes read from the input buffer. See 1.5.2.
*
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return the integer value calculated
*/
static inline uint16_t _read_int(uint8_t** pptr)
{
uint8_t* ptr = *pptr;
int len = 256 * (*ptr) + (*(ptr + 1));
*pptr += 2;
return (uint16_t)len;
}
/**
* @brief Read a UTF-8 encoded string pointer from the input buffer. See 1.5.3.
*
* @param[out] pstring the pointer of the C string to read.
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return the length of string
*/
static inline int _read_string(char** pstring, uint8_t** pptr)
{
int str_len = _read_int(pptr);
if (str_len > 0) {
*pstring = (char*)*pptr;
*pptr += str_len;
}
return str_len;
}
/**
* @brief Check the mqtt type and get publish flags. See 2.2.
*
* @param[in] packet_type type of mqtt control packet, See 2.2.1
* @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return @see MQTTPacketErrCode
*/
static int _mqtt_packet_type_check(MQTTPacketType packet_type, MQTTPublishFlags* pflags, uint8_t** pptr)
{
MQTTHeader header = {0};
header.byte = _read_char(pptr);
switch (header.bits.type) {
case PUBLISH:
pflags->dup = header.bits.dup;
pflags->qos = header.bits.qos;
pflags->retain = header.bits.retain;
break;
case SUBSCRIBE:
case UNSUBSCRIBE:
if ((header.byte & 0xf0) != 0x10) {
return MQTT_ERR_INVALID_PACKET_TYPE;
}
break;
default:
break;
}
return packet_type != header.bits.type ? MQTT_ERR_INVALID_PACKET_TYPE : MQTT_RET_PACKET_OK;
}
/**
* @brief Deserialize remaining length. See 2.2.3.
*
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] plen the pointer to the remaining length
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return @see MQTTPacketErrCode
*/
static int _mqtt_remaining_length_deserialize(int buf_len, int* plen, uint8_t** pptr)
{
uint8_t* buf = *pptr;
uint8_t c = 0;
uint32_t multiplier = 1;
uint32_t count = 0;
uint32_t len = 0;
do {
if (++count > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
return MQTT_ERR_INVALID_PACKET_TYPE;
}
c = *buf++;
len += (c & 127) * multiplier;
multiplier *= 128;
SHORT_BUFFER_CHECK(buf_len, count + 1 + len);
} while (c & 128);
if (plen) {
*plen = len;
}
*pptr += count;
return MQTT_RET_PACKET_OK;
}
/**
* @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
* @param[out] connack_rc returned integer value of the connack return code
* @return @see MQTTPacketErrCode
*/
int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
MQTTConnackFlags flags = {0};
rc = _mqtt_packet_type_check(CONNACK, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc < 0) {
goto exit;
}
flags.all = _read_char(&ptr);
*session_present = flags.bits.sessionpresent;
*connack_rc = _read_char(&ptr);
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @return @see MQTTPacketErrCode
*/
int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
rc = _mqtt_packet_type_check(PINGRESP, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc < 0) {
goto exit;
}
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] flags the MQTT dup, qos, retained flag
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] topic_name returned string - the MQTT topic in the publish
* @param[out] topic_len returned integer - the length of the MQTT topic
* @param[out] payload returned byte buffer - the MQTT publish payload
* @param[out] payload_len returned integer - the length of the MQTT payload
* @return @see MQTTPacketErrCode
*/
int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
char** topic_name, int* topic_len, uint8_t** payload, int* payload_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
uint8_t* ptr_remain = NULL;
int rc = 0;
int len = 0;
rc = _mqtt_packet_type_check(PUBLISH, flags, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
if (rc) {
goto exit;
}
ptr_remain = ptr;
rc = _read_string(topic_name, &ptr);
if (rc <= 0) {
rc = MQTT_ERR_INVALID_PACKET_TYPE;
goto exit;
}
*topic_len = rc;
if (flags->qos > 0) {
*packet_id = _read_int(&ptr);
}
*payload_len = len - (ptr - ptr_remain);
*payload = ptr;
rc = MQTT_RET_PACKET_OK;
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
rc = _mqtt_packet_type_check(PUBACK, 0, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc) {
goto exit;
}
*packet_id = _read_int(&ptr);
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
* @param[out] count returned integer - number of members in the grantedQoSs array
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] granted_qos returned array of integers - the granted qualities of service
* @return @see MQTTPacketErrCode
*/
int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
int granted_qos[])
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
int len = 0;
rc = _mqtt_packet_type_check(SUBACK, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
if (rc) {
goto exit;
}
*packet_id = _read_int(&ptr);
len -= 2;
*count = 0;
while (len--) {
if (*count > maxcount) {
rc = MQTT_ERR_SUB_COUNT_EXCEED;
goto exit;
}
granted_qos[(*count)++] = _read_char(&ptr);
}
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
rc = _mqtt_packet_type_check(UNSUBACK, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc) {
goto exit;
}
*packet_id = _read_int(&ptr);
exit:
return rc;
}

View File

@@ -0,0 +1,458 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_packet_serialize.c
* @brief implements mqtt packet serialize. Reference paho.mqtt.embedded-c &
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-21
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "mqtt_packet.h"
/**
* @brief Writes one character to an output buffer. See 1.5.1.
*
* @param[in] c the character to write
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static inline void _write_char(char c, uint8_t** pptr)
{
**pptr = c;
(*pptr)++;
}
/**
* @brief Writes an integer as 2 bytes to an output buffer. See 1.5.2.
*
* @param[in] value the integer to write.
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned.
*/
static inline void _write_int(int value, uint8_t** pptr)
{
**pptr = (uint8_t)(value / 256);
(*pptr)++;
**pptr = (uint8_t)(value % 256);
(*pptr)++;
}
/**
* @brief Writes a UTF-8 encoded string to an output buffer. Converts C string to
* length-delimited. See 1.5.3.
*
* @param[in] string the C string to write
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static inline void _write_string(const char* string, uint8_t** pptr)
{
int len = strlen(string);
_write_int(len, pptr);
memcpy(*pptr, string, len);
*pptr += len;
}
/**
* @brief Serialize mqtt fixed header except remaining length. See 2.2.
*
* @param[in] packet_type type of mqtt control packet, See 2.2.1 MQTT Control Packet type
* @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2 Flags
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static void _mqtt_header_serialize(MQTTPacketType packet_type, const MQTTPublishFlags* pflags, uint8_t** pptr)
{
MQTTHeader header = {0};
switch (packet_type) {
case PUBLISH:
header.bits.dup = pflags->dup;
header.bits.qos = pflags->qos;
header.bits.retain = pflags->retain;
break;
case SUBSCRIBE:
case UNSUBSCRIBE:
header.byte = 0x02;
break;
default:
break;
}
header.bits.type |= packet_type;
_write_char(header.byte, pptr); /* write header */
}
/**
* @brief Serialize remaining length. See 2.2.3.
*
* @param[in] length the length to be encoded to remaining length
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static void _mqtt_remaining_length_serialize(int length, uint8_t** pptr)
{
char d;
do {
d = length % 128;
length /= 128;
// if there are more digits to encode, set the top bit of this digit
if (length > 0) {
d |= 0x80;
}
_write_char(d, pptr);
} while (length > 0);
}
/**
* @brief Serialize the remaining length of the MQTT connect packet that would be produced using the
* supplied connect options. See 3.1.1.
*
* @param[in] options the options to be used to build the connect packet
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_connect_remaining_length_serialize(const MQTTPacketConnectOption* options, int buf_len, uint8_t** pptr)
{
if (options->mqtt_version != 3 && options->mqtt_version != 4) {
return MQTT_ERR_VERSION_UNSUPPORTED;
}
int len = 0;
len = options->mqtt_version == 3 ? 12 : 10;
len += strlen(options->client_id) + 2;
if (options->will_flag) {
len += strlen(options->will.topic_name) + 2 + strlen(options->will.message) + 2;
}
if (options->username) {
len += strlen(options->username) + 2;
}
if (options->password) {
len += strlen(options->password) + 2;
}
SHORT_BUFFER_CHECK(buf_len, len);
_mqtt_remaining_length_serialize(len, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the mqtt connect packet variable header. See 3.1.2.
*
* @param[in] options the options to be used to build the connect packet
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_connect_variable_header_serialize(const MQTTPacketConnectOption* options, uint8_t** pptr)
{
if (options->password && !options->username) {
return MQTT_ERR_INVALID_PACKET_TYPE;
}
// 3.1.2.1 protocol name & 3.1.2.2 protocol level
switch (options->mqtt_version) {
case 3:
_write_string("MQIsdp", pptr);
_write_char((char)3, pptr);
break;
case 4:
_write_string("MQTT", pptr);
_write_char((char)4, pptr);
break;
}
// 3.1.2.3 Connect Flags
MQTTConnectFlags flags = {
.bits.clean_session = options->clean_session,
.bits.will = options->will_flag,
.bits.will_qos = options->will_flag ? options->will.qos : 0,
.bits.will_retain = options->will_flag ? options->will.retained : 0,
.bits.username = options->username ? 1 : 0,
.bits.password = options->password ? 1 : 0,
};
_write_char(flags.all, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the remaining length of the MQTT publish packet that would be produced using the supplied
* parameters. See 3.3.2 & 3.3.3.
*
* @param[in] qos the MQTT QoS of the publish (packet id is omitted for QoS 0)
* @param[in] topic_name the topic name to be used in the publish
* @param[in] payload_len the length of the payload to be sent
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_publish_remaining_length_serialize(uint8_t qos, const char* topic_name, int payload_len, int buf_len,
uint8_t** pptr)
{
int len = 0;
len += 2 + strlen(topic_name) + payload_len;
if (qos > 0) {
len += 2; /* packet id */
}
SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
_mqtt_remaining_length_serialize(len, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the length of the MQTT subscribe/unsubscribe packet that would be produced using the
* supplied parameters. See 3.8.1 & 3.10.1.
*
* @param[in] count the number of topic filter strings in topicFilters
* @param[in] topic_filters the array of topic filter strings to be used in the publish
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_sub_unsub_remaining_length_serialize(uint32_t count, char* topic_filters[], int buf_len,
uint8_t** pptr, uint8_t is_sub)
{
int i = 0;
int len = 2; /* packet id */
for (i = 0; i < count; ++i) {
len += 2 + strlen(topic_filters[i]) + is_sub; /* length + topic */
}
SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
_mqtt_remaining_length_serialize(len, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the connect options into the buffer. See 3.1.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] options the options to be used to build the connect packet
* @return serialized length, or error if <= 0
*/
int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc;
uint8_t* ptr = buf;
// 3.1.1 Fixed header
_mqtt_header_serialize(CONNECT, NULL, &ptr);
rc = _mqtt_connect_remaining_length_serialize(options, buf_len, &ptr);
if (rc) {
return rc;
}
// 3.1.2 Variable header
rc = _mqtt_connect_variable_header_serialize(options, &ptr);
if (rc) {
return rc;
}
// 3.1.3 Payload
_write_int(options->keep_alive_interval, &ptr);
_write_string(options->client_id, &ptr);
if (options->will_flag) {
_write_string(options->will.topic_name, &ptr);
_write_string(options->will.message, &ptr);
}
if (options->username) {
_write_string(options->username, &ptr);
}
if (options->password) {
_write_string(options->password, &ptr);
}
return ptr - buf;
}
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
_mqtt_header_serialize(DISCONNECT, 0, &ptr);
_mqtt_remaining_length_serialize(0, &ptr);
return ptr - buf;
}
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
_mqtt_header_serialize(PINGREQ, 0, &ptr);
_mqtt_remaining_length_serialize(0, &ptr);
return ptr - buf;
}
/**
* @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] flags the MQTT dup, qos, retained flag
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] topic_name char * - the MQTT topic in the publish
* @param[in] payload byte buffer - the MQTT publish payload
* @param[in] payload_len integer - the length of the MQTT payload
* @return serialized length, or error if <= 0
*/
int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
const char* topic_name, const uint8_t* payload, int payload_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc;
uint8_t* ptr = buf;
_mqtt_header_serialize(PUBLISH, flags, &ptr);
rc = _mqtt_publish_remaining_length_serialize(flags->qos, topic_name, payload_len, buf_len, &ptr);
if (rc) {
return rc;
}
_write_string(topic_name, &ptr);
if (flags->qos > 0) {
_write_int(packet_id, &ptr);
}
memcpy(ptr, payload, payload_len);
ptr += payload_len;
return ptr - buf;
}
/**
* @brief Serialize a puback packet into the supplied buffer. See 3.4.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @return serialized length, or error if <= 0
*/
int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN + 2);
uint8_t* ptr = buf;
_mqtt_header_serialize(PUBACK, 0, &ptr);
_mqtt_remaining_length_serialize(2, &ptr);
_write_int(packet_id, &ptr);
return ptr - buf;
}
/**
* @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied bufferr
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters and reqQos arrays
* @param[in] topic_filters - array of topic filter names
* @param[in] requested_qos - array of requested QoS
* @return serialized length, or error if <= 0
*/
int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
char* topic_filters[], const int requested_qos[])
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc, i = 0;
uint8_t* ptr = buf;
_mqtt_header_serialize(SUBSCRIBE, 0, &ptr);
rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 1);
if (rc) {
return rc;
}
_write_int(packet_id, &ptr);
for (i = 0; i < count; ++i) {
_write_string(topic_filters[i], &ptr);
_write_char(requested_qos[i], &ptr);
}
return ptr - buf;
}
/**
* @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
*
* @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters array
* @param[in] topic_filters - array of topic filter names
* @return serialized length, or error if <= 0
*/
int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[])
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc, i = 0;
uint8_t* ptr = buf;
_mqtt_header_serialize(UNSUBSCRIBE, 0, &ptr);
rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 0);
if (rc) {
return rc;
}
_write_int(packet_id, &ptr);
for (i = 0; i < count; ++i) {
_write_string(topic_filters[i], &ptr);
}
return ptr - buf;
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -0,0 +1,307 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_mqtt_packet.cc
* @brief unit test for mqtt packet
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-06-01
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-06-01 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_packet.h"
namespace mqtt_packet_unittest {
/**
* @brief Test connect packet.
*
*/
TEST(MQTTPacketTest, CONNECT) {
uint8_t test_packet[] = {
0x10, 0x77, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04, 0xc2, 0x00, 0xf0, 0x00, 0x0e, 0x4a, 0x4e, 0x4c, 0x52,
0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x00, 0x28, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f,
0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x3b, 0x32, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x36, 0x3b,
0x49, 0x41, 0x5a, 0x5a, 0x4a, 0x3b, 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x00, 0x31,
0x66, 0x62, 0x63, 0x39, 0x65, 0x34, 0x36, 0x33, 0x61, 0x32, 0x30, 0x64, 0x66, 0x30, 0x34, 0x35, 0x65, 0x62,
0x66, 0x34, 0x38, 0x34, 0x35, 0x61, 0x35, 0x39, 0x62, 0x32, 0x61, 0x38, 0x35, 0x66, 0x65, 0x62, 0x34, 0x31,
0x34, 0x31, 0x30, 0x30, 0x3b, 0x68, 0x6d, 0x61, 0x63, 0x73, 0x68, 0x61, 0x31,
};
uint8_t packet_buf[2048];
std::string user_name = "JNLRWO3T59test;21010406;IAZZJ;2147483647";
std::string password = "fbc9e463a20df045ebf4845a59b2a85feb414100;hmacsha1";
MQTTPacketConnectOption options = {
.mqtt_version = 4,
.client_id = "JNLRWO3T59test",
.keep_alive_interval = 240,
.clean_session = 1,
.will_flag = 0,
.will = {0},
.username = reinterpret_cast<char *>(const_cast<char *>(user_name.c_str())),
.password = reinterpret_cast<char *>(const_cast<char *>(password.c_str())),
};
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, 1, &options), MQTT_ERR_SHORT_BUFFER);
options.username = NULL;
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_INVALID_PACKET_TYPE);
options.password = NULL;
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options),
sizeof(test_packet) - user_name.length() - password.length() - 4);
options.mqtt_version = 5;
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_VERSION_UNSUPPORTED);
}
/**
* @brief Test connack packet.
*
*/
TEST(MQTTPacketTest, CONNACK) {
uint8_t test_packet[] = {0x20, 0x02, 0x00, 0x00};
uint8_t session_present, connack_rc;
for (uint8_t i = CONNACK_CONNECTION_ACCEPTED; i <= CONNACK_NOT_AUTHORIZED_ERROR; i++) {
test_packet[3] = i;
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
MQTT_RET_PACKET_OK);
ASSERT_EQ(connack_rc, i);
}
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, 2, &session_present, &connack_rc), MQTT_ERR_SHORT_BUFFER);
test_packet[0] = SUBACK;
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
MQTT_ERR_INVALID_PACKET_TYPE);
}
/**
* @brief Test publish packet.
*
*/
TEST(MQTTPacketTest, PUBLISH) {
uint8_t test_packet[] = {
0x32, 0x40, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x2f, 0x74, 0x65,
0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x70, 0xaf, 0x7b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x22, 0x3a, 0x20, 0x22, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x22,
0x2c, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x30, 0x22, 0x7d,
};
uint8_t packet_buf[128];
/**
* @brief QoS1
*
*/
char serialize_topic_name[] = "JNLRWO3T59/test/data";
int serialize_topic_len = strlen(serialize_topic_name);
uint16_t serialize_packet_id = 28847;
uint8_t serialize_payload[] = "{\"action\": \"publish_test\", \"count\": \"0\"}";
int serialize_payload_len = strlen((const char *)serialize_payload);
MQTTPublishFlags serialize_flags = {0, 1, 0};
ASSERT_EQ(mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
serialize_topic_name, serialize_payload, serialize_payload_len),
sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
char *deserialize_topic_name;
int deserialize_topic_len;
uint16_t deserialize_packet_id = 0;
uint8_t *deserialize_payload;
int deserialize_payload_len;
MQTTPublishFlags deserialize_flags;
ASSERT_EQ(mqtt_publish_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_flags,
&deserialize_packet_id, &deserialize_topic_name, &deserialize_topic_len,
&deserialize_payload, &deserialize_payload_len),
0);
ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
ASSERT_EQ(deserialize_topic_len, serialize_topic_len);
ASSERT_EQ(deserialize_payload_len, serialize_payload_len);
ASSERT_EQ(memcmp(&deserialize_flags, &serialize_flags, sizeof(deserialize_flags)), 0);
ASSERT_EQ(memcmp(deserialize_topic_name, serialize_topic_name, deserialize_topic_len), 0);
ASSERT_EQ(memcmp(deserialize_payload, serialize_payload, deserialize_payload_len), 0);
/**
* @brief QoS0
*
*/
serialize_flags.qos = 0;
deserialize_packet_id = 0;
ASSERT_EQ(
mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
serialize_topic_name, serialize_payload, strlen((const char *)serialize_payload)),
sizeof(test_packet) - 2);
ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
&deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
&deserialize_payload_len),
MQTT_RET_PACKET_OK);
ASSERT_NE(deserialize_packet_id, serialize_packet_id);
/**
* @brief bad packet
*
*/
packet_buf[0] = PUBACK;
ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
&deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
&deserialize_payload_len),
MQTT_ERR_INVALID_PACKET_TYPE);
}
/**
* @brief Test puback packet.
*
*/
TEST(MQTTPacketTest, PUBACK) {
uint8_t test_packet[] = {0x40, 0x02, 0x70, 0xaf};
uint8_t packet_buf[128];
uint16_t serialize_packet_id = 28847;
ASSERT_EQ(mqtt_puback_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id), 4);
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
uint16_t deserialize_packet_id;
ASSERT_EQ(mqtt_puback_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_packet_id),
MQTT_RET_PACKET_OK);
ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
}
/**
* @brief Test subscribe packet.
*
*/
TEST(MQTTPacketTest, SUBSCRIBE) {
uint8_t test_packet[] = {
0x82, 0x19, 0x70, 0xae, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54,
0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x00,
};
uint8_t packet_buf[128];
std::string topic = "JNLRWO3T59/test/data";
uint16_t serialize_packet_id = 28846;
char *serialize_topic_name = reinterpret_cast<char *>(const_cast<char *>(topic.c_str()));
int serialize_qos = 0;
ASSERT_EQ(mqtt_subscribe_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id, 1,
&serialize_topic_name, &serialize_qos),
sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
/**
* @brief Test suback packet.
*
*/
TEST(MQTTPacketTest, SUBACK) {
uint8_t test_packet[] = {0x90, 0x03, 0x70, 0xae, 0x00};
int sub_count = 0;
uint16_t packet_id;
int granted_qos[1];
ASSERT_EQ(mqtt_suback_packet_deserialize(test_packet, sizeof(test_packet), 1, &sub_count, &packet_id, granted_qos),
MQTT_RET_PACKET_OK);
ASSERT_EQ(sub_count, 1);
ASSERT_EQ(packet_id, 28846);
ASSERT_EQ(granted_qos[0], 0);
}
/**
* @brief Test unsubscribe packet.
*
*/
TEST(MQTTPacketTest, UNSUBSCRIBE) {
uint8_t test_packet[] = {
0xa2, 0x18, 0x35, 0x74, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33,
0x54, 0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61,
};
uint8_t packet_buf[128];
uint16_t packet_id = 13684;
std::string topic = "JNLRWO3T59/test/data";
char *topic_name = reinterpret_cast<char *>(const_cast<char *>(topic.c_str()));
ASSERT_EQ(mqtt_unsubscribe_packet_serialize(packet_buf, sizeof(packet_buf), packet_id, 1, &topic_name),
sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
/**
* @brief Test connack packet.
*
*/
TEST(MQTTPacketTest, UNSUBACK) {
uint8_t test_packet[] = {0xb0, 0x02, 0x92, 0x6b};
uint16_t packet_id;
ASSERT_EQ(mqtt_unsuback_packet_deserialize(test_packet, sizeof(test_packet), &packet_id), MQTT_RET_PACKET_OK);
ASSERT_EQ(packet_id, 37483);
}
/**
* @brief Test pingreq packet.
*
*/
TEST(MQTTPacketTest, PINGREQ) {
uint8_t test_packet[] = {0xc0, 0x00};
uint8_t packet_buf[128];
ASSERT_EQ(mqtt_pingreq_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
/**
* @brief Test pingresp packet.
*
*/
TEST(MQTTPacketTest, PINGRESP) {
uint8_t test_packet[] = {0xd0, 0x00};
ASSERT_EQ(mqtt_pingresp_packet_deserialize(test_packet, sizeof(test_packet)), MQTT_RET_PACKET_OK);
}
/**
* @brief Test disconnect packet.
*
*/
TEST(MQTTPacketTest, DISCONNECT) {
uint8_t test_packet[] = {0xe0, 0x00};
uint8_t packet_buf[128];
ASSERT_EQ(mqtt_disconnect_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
} // namespace mqtt_packet_unittest

View File

@@ -0,0 +1,10 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_common ${src_common} ${src} PARENT_SCOPE)
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,104 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_downloader.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-20
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/**
* @brief Downloader status.
*
*/
typedef enum {
UTILS_DOWNLOADER_STATUS_SUCCESS = 0,
UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED,
UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED,
UTILS_DOWNLOADER_STATUS_NETWORK_FAILED
} UtilsDownloaderStatus;
/**
* @brief Downloader function.
*
*/
typedef struct {
// memory
void* (*downloader_malloc)(size_t len); /**< user malloc */
void (*downloader_free)(void* val); /**< user free */
// break point
int (*break_point_init)(void* usr_data); /**< init break point, read from flash or file */
void (*break_point_deinit)(void* usr_data); /**< deinit break point */
int (*break_point_set)(void* usr_data); /**< set break point structure */
int (*break_point_save)(void* usr_data); /**< save break point in flash or file */
int (*break_point_check)(void* usr_data); /**< check break point valid */
int (*break_point_restore)(void* usr_data); /**< restore break point */
// data download
int (*data_download_init)(void* usr_data); /**< init data download, such as http connect */
void (*data_download_deinit)(void* usr_data); /**< deinit data download, such as http disconnect */
int (*data_download_is_over)(void* usr_data); /**< check if download finish */
int (*data_download_recv)(void* usr_data); /**< recv data, such as http recv */
int (*data_download_save)(void* usr_data); /**< save data, such as write firmware to flash */
int (*data_download_finish)(void* usr_data, UtilsDownloaderStatus status); /**< process result */
} UtilsDownloaderFunction;
/**
* @brief Init downloader.
*
* @param[in] func download function should implement
* @param[in] usr_data user data using in function
* @return pointer to downloader
*/
void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data);
/**
* @brief Process download using function.
*
* @param[in,out] handle pointer to downloader
* @return -1 for fail, others see data_download_finish
*/
int utils_downloader_process(void* handle);
/**
* @brief Deinit downloader.
*
* @param[in,out] handle pointer to downloader
*/
void utils_downloader_deinit(void* handle);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_

View File

@@ -0,0 +1,146 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_json.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-24
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-24 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>fix bug and add utils_json_value_data_get
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
/**
* @brief Json value type
*
*/
typedef enum {
UTILS_JSON_VALUE_TYPE_INT32 = 0,
UTILS_JSON_VALUE_TYPE_INT64,
UTILS_JSON_VALUE_TYPE_UINT32,
UTILS_JSON_VALUE_TYPE_UINT64,
UTILS_JSON_VALUE_TYPE_FLOAT,
UTILS_JSON_VALUE_TYPE_DOUBLE,
UTILS_JSON_VALUE_TYPE_BOOLEAN,
} UtilsJsonValueType;
/**
* @brief Json array result
*
*/
typedef enum {
UTILS_JSON_ARRAY_ITER_CONTINUE,
UTILS_JSON_ARRAY_ITER_STOP,
} UtilsJsonArrayIterResult;
/**
* @brief Json value
*
*/
typedef struct {
const char *value;
int value_len;
} UtilsJsonValue;
/**
* @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] value value
* @return 0 for success
*/
int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value);
/**
* @brief Get data of value with type.
*
* @param[in] value @see UtilsJsonValue
* @param[in] type value type, string can use value directly @see UtilsJsonValueType
* @param[out] data data pointer, user should match the type
* @return 0 for success
*/
int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data);
/**
* @brief Return unsigned int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data);
/**
* @brief Return int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data);
/**
* @brief Remove '\\' in json string.
*
* @param[in,out] src string to transfer
* @param[in] src_len string len
* @return length after transfer
*/
int utils_json_strip_transfer(char *src, int src_len);
/**
* @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
* according to array_elem_type, obj_cb will be called for each elements.
*
* @param[in] src array string
* @param[in] src_len length of src
* @param[in] obj_cb callback to deal with array element
* @param[in] arg argument passed to the obj_cb
*/
void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
void *arg);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_

View File

@@ -0,0 +1,156 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_list.h
* @brief header file for utils list
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-25 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <string.h>
/**
* @brief ListNode iterator direction.
*
*/
typedef enum {
LIST_HEAD,
LIST_TAIL,
} UtilsListDirection;
/**
* @brief ListNode process result of OnNodeProcessHandle.
*
*/
typedef enum {
LIST_TRAVERSE_CONTINUE,
LIST_TRAVERSE_BREAK,
} UtilsListResult;
/**
* @brief Utils list function.
*
*/
typedef struct {
void *(*list_malloc)(size_t len);
void (*list_free)(void *val);
void *(*list_lock_init)(void);
void (*list_lock)(void *lock);
void (*list_unlock)(void *lock);
void (*list_lock_deinit)(void *lock);
} UtilsListFunc;
/**
* @brief Default list func
*
*/
#define DEFAULT_LIST_FUNCS \
{ \
HAL_Malloc, HAL_Free, HAL_MutexCreate, HAL_MutexLock, HAL_MutexUnlock, HAL_MutexDestroy \
}
/**
* @brief Node process handle called by utils_list_process.
*
*/
typedef UtilsListResult (*OnNodeProcessHandle)(void *list, void *node, void *val, void *usr_data);
/**
* @brief Create list with max len, return NULL if fail.
*
* @param[in] func function needed by list
* @param[in] max_len max_len of list
* @return pointer to list, NULL for failed
*/
void *utils_list_create(UtilsListFunc func, int max_len);
/**
* @brief Destroy list.
*
* @param[in] list pointer to list
*/
void utils_list_destroy(void *list);
/**
* @brief Clear the list
*
* @param list
*/
void utils_list_clear(void *list);
/**
* @brief Get list len.
*
* @param[in] list pointer to list
* @return len of list
*/
int utils_list_len_get(void *list);
/**
* @brief Push the node to list tail, return NULL if node invalid.
*
* @param[in] list pointer to list
* @param[in] val value needed to push to list
* @return pointer to node, NULL for failed
*/
void *utils_list_push(void *list, void *val);
/**
* @brief Pop the val from list head, return NULL if list empty.
*
* @param[in] list pointer to list
* @return val in the head node
*/
void *utils_list_pop(void *list);
/**
* @brief Delete the node in list and release the resource.
*
* @param[in] list pointer to list
* @param[in] node pointer to node needed remove
*/
void utils_list_remove(void *list, void *node);
/**
* @brief Process list using handle function.
*
* @param[in] list pointer to list
* @param[in] direction direction to traverse @see UtilsListDirection
* @param[in] handle process function @see OnNodeProcessHandle
* @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
*/
void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_

View File

@@ -0,0 +1,131 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_log.h
* @brief header file for utils log
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
/**
* @brief SDK log print/upload level.
*
*/
typedef enum {
LOG_LEVEL_DISABLE = 0, /**< disable log print/upload */
LOG_LEVEL_ERROR = 1, /**< error log level */
LOG_LEVEL_WARN = 2, /**< warning log level */
LOG_LEVEL_INFO = 3, /**< info log level */
LOG_LEVEL_DEBUG = 4, /**< debug log level */
} LogLevel;
/**
* @brief User's self defined log handler callback.
*
*/
typedef struct {
void *(*log_malloc)(size_t len);
void (*log_free)(void *val);
void (*log_handle)(const char *message);
void (*log_upload)(LogLevel log_level, const char *message);
void (*log_printf)(const char *fmt, ...);
char *(*log_get_current_time_str)(void);
void *(*log_mutex_create)(void);
void (*log_mutex_lock)(void *mutex);
void (*log_mutex_unlock)(void *mutex);
void (*log_mutex_destroy)(void *mutex);
} LogHandleFunc;
/**
* @brief Default log func
*
*/
#define DEFAULT_LOG_HANDLE_FUNCS \
{ \
HAL_Malloc, HAL_Free, NULL, NULL, HAL_Printf, HAL_Timer_Current, HAL_MutexCreate, HAL_MutexLock, \
HAL_MutexUnlock, HAL_MutexDestroy \
}
/**
* @brief Init log with func, log level, max log size.
*
* @param[in] func function should be implement for utils log
* @param[in] log_level @see LogLevel
* @param[in] max_log_size max size of log to print
* @return 0 for success
*/
int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size);
/**
* @brief Deinit log.
*
*/
void utils_log_deinit(void);
/**
* @brief Set log level.
*
* @param log_level @see LogLevel
*/
void utils_log_set_level(LogLevel log_level);
/**
* @brief Get log level.
*
* @return @see LogLevel
*/
LogLevel utils_log_get_level(void);
/**
* @brief Generate log if level higher than set.
*
* @param[in] file file path
* @param[in] func function where generate log
* @param[in] line line of source file where genertate log
* @param[in] level @see LogLevel
* @param[in] fmt format of log content
*/
void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...);
// Simple APIs for log generation in different level
#define Log_d(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define Log_i(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define Log_w(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
#define Log_e(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_

View File

@@ -0,0 +1,142 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_downloader.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "utils_downloader.h"
/**
* @brief Downloader.
*
*/
typedef struct {
UtilsDownloaderFunction func;
void* usr_data;
} UtilsDownloader;
/**
* @brief Init downloader.
*
* @param[in] func download function should implement
* @param[in] usr_data user data using in function
* @return pointer to downloader
*/
void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data)
{
if (!func.downloader_malloc) {
return NULL;
}
UtilsDownloader* handle = func.downloader_malloc(sizeof(UtilsDownloader));
if (!handle) {
return NULL;
}
handle->func = func;
handle->usr_data = usr_data;
return handle;
}
/**
* @brief Process download using function.
*
* @param[in,out] handle pointer to downloader
* @return -1 for fail, others see data_download_finish
*/
int utils_downloader_process(void* handle)
{
int rc = -1;
UtilsDownloader* downloader = handle;
UtilsDownloaderStatus status = UTILS_DOWNLOADER_STATUS_SUCCESS;
if (!handle) {
return -1;
}
rc = downloader->func.break_point_init(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
goto exit;
}
// if check ok, restore from break point, otherwise set break point
rc = downloader->func.break_point_check(downloader->usr_data)
? downloader->func.break_point_set(downloader->usr_data)
: downloader->func.break_point_restore(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
goto exit;
}
rc = downloader->func.data_download_init(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
goto exit;
}
while (!downloader->func.data_download_is_over(downloader->usr_data)) {
rc = downloader->func.data_download_recv(downloader->usr_data);
if (rc < 0) {
status = UTILS_DOWNLOADER_STATUS_NETWORK_FAILED;
goto exit;
}
rc = downloader->func.data_download_save(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
goto exit;
}
rc = downloader->func.break_point_save(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
goto exit;
}
}
exit:
rc = downloader->func.data_download_finish(downloader->usr_data, status);
downloader->func.break_point_deinit(downloader->usr_data);
downloader->func.data_download_deinit(downloader->usr_data);
return rc;
}
/**
* @brief Deinit downloader.
*
* @param[in,out] handle pointer to downloader
*/
void utils_downloader_deinit(void* handle)
{
UtilsDownloader* downloader = handle;
if (!handle) {
return;
}
downloader->func.downloader_free(handle);
}

View File

@@ -0,0 +1,684 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_json.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-25 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>fix bug and add utils_json_value_data_get
* </table>
*/
#include "utils_json.h"
/**
* @brief Delimiter of json
*
*/
typedef enum {
JSON_DELIMITER_ARRAY_BEGIN = '[',
JSON_DELIMITER_ARRAY_END = ']',
JSON_DELIMITER_OBJECT_BEGIN = '{',
JSON_DELIMITER_OBJECT_END = '}',
JSON_DELIMITER_KEY = '"',
JSON_DELIMITER_VALUE = ':',
JSON_DELIMITER_ELEMENT_END = ',',
JSON_DELIMITER_TYPE_STRING = '"',
JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER = 'T',
JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER = 't',
JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER = 'F',
JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER = 'f',
JSON_DELIMITER_TYPE_NULL_UPPER = 'N',
JSON_DELIMITER_TYPE_NULL_LOWER = 'n',
JSON_DELIMITER_SPACE = ' ',
JSON_DELIMITER_NONE = 0,
} JsonDelimiter;
/**
* @brief Element type support
*
*/
typedef enum {
JSON_ELEMENT_ARRAY = 0,
JSON_ELEMENT_OBJECT,
JSON_ELEMENT_VALUE_STRING,
JSON_ELEMENT_VALUE_NUMBER,
JSON_ELEMENT_VALUE_BOOLEAN,
JSON_ELEMENT_VALUE_NULL,
JSON_ELEMENT_KEY,
} JsonElementType;
/**
* @brief Json Element
*
*/
typedef struct {
JsonElementType type;
char *pos_begin;
char *pos_end;
int element_len;
} JsonElement;
#define json_key JsonElement
#define json_value JsonElement
/**
* @brief Is delimiter begin or end matched
*
*/
typedef struct {
int (*is_begin_matched)(char *str);
int (*is_end_matched)(char *str, int *offset);
} IsDelimiterMatched;
/**
* @brief For array type, when meets '[', means depth++
*
* @param[in] str pointer to char
* @return true for matched
*/
static int _is_array_begin_matched(char *str)
{
return *str == JSON_DELIMITER_ARRAY_BEGIN;
}
/**
* @brief For array type, when meets ']', means depth--
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_array_end_matched(char *str, int *offset)
{
return *str == JSON_DELIMITER_ARRAY_END;
}
/**
* @brief For object type, when meets '{', means depth++
*
* @param[in] str pointer to char
* @return true for matched
*/
static int _is_object_begin_matched(char *str)
{
return *str == JSON_DELIMITER_OBJECT_BEGIN;
}
/**
* @brief For object type, when meets '}', means depth--
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_object_end_matched(char *str, int *offset)
{
return *str == JSON_DELIMITER_OBJECT_END;
}
/**
* @brief For string type, when meets '"', but not "\"", means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_string_end_matched(char *str, int *offset)
{
return *str == '"' && *(str - 1) != '\\';
}
/**
* @brief For number type, when meets not '0~9', '.' or 'f' for float, means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_number_end_matched(char *str, int *offset)
{
return (*str < '0' || *str > '9') && *str != '.' && *str != 'f' && *str != '-';
}
/**
* @brief For boolen type, when meets "true", "True", "false" or "False", means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move, -1 means error
* @return true for matched
*/
static int _is_boolean_end_matched(char *str, int *offset)
{
*offset = !strncmp(str, "true", 4) || !strncmp(str, "TRUE", 4) ? 4 : *offset;
*offset = !strncmp(str, "false", 5) || !strncmp(str, "FALSE", 5) ? 5 : *offset;
if (*offset == 0) {
*offset = -1; // -1 means error
}
return 1;
}
/**
* @brief For boolen type, when meets "null", "NULL", means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move, -1 means error
* @return true for matched
*/
static int _is_null_end_matched(char *str, int *offset)
{
*offset = !strncmp(str, "null", 4) || !strncmp(str, "NULL", 4) ? 4 : 0;
if (*offset == 0) {
*offset = -1; // -1 means error
}
return 1;
}
/**
* @brief Table of matched function of elements
*
*/
static IsDelimiterMatched sg_element_matched_func[] = {
{_is_array_begin_matched, _is_array_end_matched}, // JSON_ELEMENT_ARRAY
{_is_object_begin_matched, _is_object_end_matched}, // JSON_ELEMENT_OBJECT
{NULL, _is_string_end_matched}, // JSON_ELEMENT_VALUE_STRING
{NULL, _is_number_end_matched}, // JSON_ELEMENT_VALUE_NUMBER
{NULL, _is_boolean_end_matched}, // JSON_ELEMENT_VALUE_BOOLEAN
{NULL, _is_null_end_matched}, // JSON_ELEMENT_VALUE_NULL
{NULL, NULL}, // JSON_ELEMENT_KEY
};
/**
* @brief Increase pos and decrease remain len
*
* @param[in,out] pos current pos
* @param[in,out] len current len
*/
static void _increase_pos(char **pos, int *len)
{
(*pos)++;
(*len)--;
};
/**
* @brief Find delimiter and return pos
*
* @param[in] ch delimiter character
* @param[in] spilt_char if 0 then spilt nothing but delimiter
* @param[in] str input string
* @param[in,out] remain_len remaining length
* @return NULL for failed
*/
static char *_find_json_delimiter(JsonDelimiter ch, char spilt_char, const char *str, int *remain_len)
{
char *pos = NULL;
char *src = (char *)str;
while ((*remain_len) > 0 && src && *src) {
if ((!spilt_char && *src != ch) || (spilt_char && *src == spilt_char) || (*src == '\n')) {
_increase_pos(&src, remain_len);
continue;
}
pos = (*src == ch) ? src : 0;
break;
}
return pos;
}
/**
* @brief Find element end
*
* @param[in] is_begin_matched using in nesting
* @param[in] is_end_matched match end
* @param[in] str input string
* @param[in,out] remain_len remaining length
* @return NULL for failed
*/
static char *_find_json_value_element_end(int (*is_begin_matched)(char *), int (*is_end_matched)(char *, int *),
char *str, int *remain_len)
{
int offset = 0;
int depth = 0;
char *pos = NULL;
while (str && *str) {
offset = 0;
if (*remain_len <= 0) {
return NULL;
}
if (is_begin_matched) {
if (is_begin_matched(str)) {
depth++;
}
}
if (is_end_matched(str, &offset)) {
if (!depth) {
if (offset < 0) {
break; // failed
}
pos = str + offset;
(*remain_len) -= offset;
break;
}
if (depth > 0) {
depth--;
}
}
str++;
(*remain_len)--;
}
return pos;
}
/**
* @brief Get json key from string
*
* @param[in] node_begin string begin
* @param[in,out] remain_len remaining length
* @param[out] key key node
* @return 0 for success
*/
static int _get_json_key(char *node_begin, int *remain_len, json_key *key)
{
char *key_begin = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_SPACE, node_begin, remain_len);
if (!key_begin) {
return -1;
}
_increase_pos(&key_begin, remain_len);
char *key_end = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_NONE, key_begin, remain_len);
if (!key_end) {
return -1;
}
key->type = JSON_ELEMENT_KEY;
key->pos_begin = key_begin;
key->pos_end = key_end;
key->element_len = key_end - key_begin;
return 0;
}
/**
* @brief Get json value from key end
*
* @param[in] key_end end of key
* @param[in,out] remain_len remaining length
* @param[out] value value node
* @return 0 for success
*/
static int _get_json_value(char *key_end, int *remain_len, json_value *value)
{
char *value_begin = _find_json_delimiter(JSON_DELIMITER_VALUE, JSON_DELIMITER_SPACE, key_end, remain_len);
if (!value_begin) {
return -1;
}
_increase_pos(&value_begin, remain_len);
// filter all the space
while (*value_begin == ' ') {
value_begin++;
(*remain_len)--;
if (*remain_len <= 0) {
return -1;
}
};
JsonElementType type;
char *value_end = NULL;
switch (*value_begin) {
case JSON_DELIMITER_ARRAY_BEGIN:
// should + 1 for escape first [
_increase_pos(&value_begin, remain_len);
type = JSON_ELEMENT_ARRAY;
break;
case JSON_DELIMITER_OBJECT_BEGIN:
// should + 1 for escape first {
_increase_pos(&value_begin, remain_len);
type = JSON_ELEMENT_OBJECT;
break;
case JSON_DELIMITER_TYPE_STRING:
// should + 1 for escape first "
_increase_pos(&value_begin, remain_len);
type = JSON_ELEMENT_VALUE_STRING;
break;
case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER:
case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER:
case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER:
case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER:
type = JSON_ELEMENT_VALUE_BOOLEAN;
break;
case JSON_DELIMITER_TYPE_NULL_UPPER:
case JSON_DELIMITER_TYPE_NULL_LOWER:
type = JSON_ELEMENT_VALUE_NULL;
break;
default:
type = JSON_ELEMENT_VALUE_NUMBER;
break;
}
value_end = _find_json_value_element_end(sg_element_matched_func[type].is_begin_matched,
sg_element_matched_func[type].is_end_matched, value_begin, remain_len);
if (!value_end) {
return -1;
}
int is_object_or_array = (type == JSON_ELEMENT_ARRAY || type == JSON_ELEMENT_OBJECT);
int is_object_or_array_or_string = (is_object_or_array || type == JSON_ELEMENT_VALUE_STRING);
value->type = type;
value->pos_begin = value_begin - is_object_or_array; // include '[' or '{'
value->element_len = value_end - value->pos_begin + is_object_or_array; // include ']' or '}'
value->pos_end = value_end + is_object_or_array_or_string; // filter '"' , ']' or '}'
return 0;
}
/**
* @brief Get value by key from json string
*
* @param[in] str json string
* @param[in] str_len strign length
* @param[in] key key
* @param[in] key_len key length
* @param[out] value value
* @return 0 for success
*/
int json_get_value_by_key(const char *str, int str_len, const char *key, int key_len, json_value *value)
{
int rc = 0;
char *object_begin = NULL;
json_key key_found = {JSON_ELEMENT_KEY, 0, 0, 0};
object_begin = _find_json_delimiter(JSON_DELIMITER_OBJECT_BEGIN, JSON_DELIMITER_SPACE, (char *)str, &str_len);
if (!object_begin) {
return -1;
}
_increase_pos(&object_begin, &str_len);
do {
rc = _get_json_key(object_begin, &str_len, &key_found);
if (rc) {
break;
}
_increase_pos(&key_found.pos_end, &str_len);
rc = _get_json_value(key_found.pos_end, &str_len, value);
if (rc) {
break;
}
// NULL of object begin can be ok for reach the end of str
object_begin = _find_json_delimiter(JSON_DELIMITER_ELEMENT_END, JSON_DELIMITER_SPACE, value->pos_end, &str_len);
if (object_begin) {
_increase_pos(&object_begin, &str_len);
}
} while (key_len != key_found.element_len || strncmp(key, key_found.pos_begin, key_found.element_len));
return rc;
}
/**
* @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] value value
* @return 0 for success
*/
int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value)
{
int rc = 0;
char *delim = NULL;
char *src_iter = (char *)src;
int src_iter_len = src_len;
char *key_iter = (char *)key;
char *key_next = NULL;
int key_next_len = 0;
json_value value_tmp;
// key can be separated by '.', such as: outer_key.(.......).inner_key
while ((delim = strchr(key_iter, '.')) != NULL) {
key_next = key_iter;
key_next_len = delim - key_iter;
rc = json_get_value_by_key(src_iter, src_iter_len, key_next, key_next_len, &value_tmp);
if (rc) {
return rc;
}
src_iter = value_tmp.pos_begin;
src_iter_len = value_tmp.element_len;
key_iter = delim + 1;
}
// found inner key and get value
rc = json_get_value_by_key(src_iter, src_iter_len, key_iter, key_len + key - key_iter, &value_tmp);
if (rc) {
return rc;
}
value->value = value_tmp.pos_begin;
value->value_len = value_tmp.element_len;
return 0;
}
/**
* @brief Get data of value with type.
*
* @param[in] value @see UtilsJsonValue
* @param[in] type value type, string can use value directly. @see UtilsJsonValueType
* @param[out] data data pointer, user should
* @return 0 for success
*/
int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data)
{
char value_tmp[32] = {0};
if (value.value_len > sizeof(value_tmp) || !value.value_len) {
return -1;
}
strncpy(value_tmp, value.value, value.value_len);
switch (type) {
case UTILS_JSON_VALUE_TYPE_INT32:
return !(sscanf(value_tmp, "%" SCNi32, (int32_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_INT64:
return !(sscanf(value_tmp, "%" SCNi64, (int64_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_UINT32:
return !(sscanf(value_tmp, "%" SCNu32, (uint32_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_UINT64:
return !(sscanf(value_tmp, "%" SCNu64, (uint64_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_FLOAT:
return !(sscanf(value_tmp, "%f", (float *)data) == 1);
case UTILS_JSON_VALUE_TYPE_DOUBLE:
return !(sscanf(value_tmp, "%lf", (double *)data) == 1);
case UTILS_JSON_VALUE_TYPE_BOOLEAN:
*(int *)data = strcmp(value_tmp, "false");
return 0;
default:
break;
}
return -1;
}
/**
* @brief Return unsigned int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data)
{
int rc;
UtilsJsonValue tmp;
rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
if (rc) {
return -1;
}
rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_UINT32, data);
if (rc) {
return -1;
}
return 0;
}
/**
* @brief Return int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data)
{
int rc;
UtilsJsonValue tmp;
rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
if (rc) {
return -1;
}
rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_INT32, data);
if (rc) {
return -1;
}
return 0;
}
/**
* @brief Remove '\\' in json string.
*
* @param[in,out] src string to transfer
* @param[in] src_len string len
* @return length after transfer
*/
int utils_json_strip_transfer(char *src, int src_len)
{
char *src_tmp = src;
char *end = src + src_len;
while (src_tmp < end) {
if (*src_tmp == '\\') {
memmove(src_tmp, src_tmp + 1, end - src_tmp);
end--;
}
src_tmp++;
}
return end - src;
}
/**
* @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
* according to array_elem_type, obj_cb will be called for each elements.
*
* @param[in] src array string
* @param[in] src_len length of src
* @param[in] obj_cb callback to deal with array element
* @param[in] arg argument passed to the obj_cb
*/
void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
void *arg)
{
#define SKIP_SPACE(str) while (*str == ' ' && (str = str + 1))
if (!obj_cb) {
return;
}
const char *start = src + 1, *end = src + 1;
char left_delimiter, right_delimiter;
SKIP_SPACE(start);
switch (*start) {
case JSON_DELIMITER_ARRAY_BEGIN:
left_delimiter = JSON_DELIMITER_ARRAY_BEGIN;
right_delimiter = JSON_DELIMITER_ARRAY_END;
break;
case JSON_DELIMITER_OBJECT_BEGIN:
left_delimiter = JSON_DELIMITER_OBJECT_BEGIN;
right_delimiter = JSON_DELIMITER_OBJECT_END;
break;
case JSON_DELIMITER_TYPE_STRING:
left_delimiter = JSON_DELIMITER_TYPE_STRING;
right_delimiter = JSON_DELIMITER_TYPE_STRING;
break;
default:
left_delimiter = 0;
right_delimiter = JSON_DELIMITER_ELEMENT_END;
break;
}
int remain_length = 0;
while (end && end <= (src + src_len)) {
SKIP_SPACE(start);
if (left_delimiter) {
remain_length = src + src_len - start;
start = _find_json_delimiter(left_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
if (!start) {
break;
}
if (JSON_DELIMITER_TYPE_STRING == left_delimiter) {
start++;
}
}
remain_length = src + src_len - start;
end = _find_json_delimiter(right_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
if (!end) {
if (!left_delimiter) {
obj_cb(start, src + src_len - start - 1, arg);
}
break;
}
if (JSON_DELIMITER_ARRAY_END == right_delimiter || JSON_DELIMITER_OBJECT_END == right_delimiter) {
end++;
}
if (UTILS_JSON_ARRAY_ITER_STOP == obj_cb(start, end - start, arg)) {
return;
}
start = end + 1;
}
}

View File

@@ -0,0 +1,338 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_list.c
* @brief utils list operation
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "utils_list.h"
/**
* @brief Define list node.
*
*/
typedef struct ListNode {
struct ListNode *prev;
struct ListNode *next;
void *val;
} ListNode;
/**
* @brief Double Linked List.
*
*/
typedef struct {
UtilsListFunc func;
ListNode *head;
ListNode *tail;
void *lock;
int len;
int max_len;
} List;
/**
* @brief List iterator.
*
*/
typedef struct {
List *list;
ListNode *next;
UtilsListDirection direction;
} ListIterator;
/**
* @brief Lock list.
*
* @param[in] list pointer to list
*/
static inline void _list_lock(List *list)
{
if (list->lock) {
list->func.list_lock(list->lock);
}
}
/**
* @brief Unlock list.
*
* @param[in] list pointer to list
*/
static inline void _list_unlock(List *list)
{
if (list->lock) {
list->func.list_unlock(list->lock);
}
}
/**
* @brief Delete the node in list and release the resource.
*
* @param[in] list pointer to list
* @param[in] node pointer to node needed remove
*/
static void _list_remove(void *list, void *node)
{
List *self = (List *)list;
ListNode *list_node = (ListNode *)node;
list_node->prev ? (list_node->prev->next = list_node->next) : (self->head = list_node->next);
list_node->next ? (list_node->next->prev = list_node->prev) : (self->tail = list_node->prev);
self->func.list_free(list_node->val);
self->func.list_free(list_node);
if (self->len) {
--self->len;
}
}
/**
* @brief Create list with max len, return NULL if fail.
*
* @param[in] func function needed by list
* @param[in] max_len max_len of list
* @return pointer to list, NULL for failed
*/
void *utils_list_create(UtilsListFunc func, int max_len)
{
List *self;
if (max_len <= 0) {
return NULL;
}
self = (List *)func.list_malloc(sizeof(List));
if (!self) {
return NULL;
}
memset(self, 0, sizeof(List));
if (func.list_lock_init) {
self->lock = func.list_lock_init();
if (!self->lock) {
func.list_free(self);
return NULL;
}
}
self->func = func;
self->max_len = max_len;
return self;
}
/**
* @brief Destroy list.
*
* @param[in] list pointer to list
*/
void utils_list_destroy(void *list)
{
utils_list_clear(list);
List *self = (List *)list;
if (self->lock) {
self->func.list_lock_deinit(self->lock);
}
self->func.list_free(self);
}
/**
* @brief Clear the list.
*
* @param[in] list pointer to list
*/
void utils_list_clear(void *list)
{
if (!list) {
return;
}
List *self = (List *)list;
_list_lock(self);
ListNode *next;
ListNode *curr = self->head;
while (self->len--) {
next = curr->next;
self->func.list_free(curr->val);
self->func.list_free(curr);
curr = next;
}
_list_unlock(self);
}
/**
* @brief Get list len.
*
* @param[in] list pointer to list
* @return len of list
*/
int utils_list_len_get(void *list)
{
List *self = (List *)list;
return self->len;
}
/**
* @brief Push the node to list tail, return NULL if node invalid.
*
* @param[in] list pointer to list
* @param[in] val value needed to push to list
* @return pointer to node, NULL for failed
*/
void *utils_list_push(void *list, void *val)
{
List *self = (List *)list;
_list_lock(self);
if (!val || self->len >= self->max_len) {
_list_unlock(self);
return NULL;
}
ListNode *node;
node = self->func.list_malloc(sizeof(ListNode));
if (!node) {
_list_unlock(self);
return NULL;
}
node->prev = NULL;
node->next = NULL;
node->val = val;
if (self->len) {
node->prev = self->tail;
node->next = NULL;
self->tail->next = node;
self->tail = node;
} else {
self->head = self->tail = node;
node->prev = node->next = NULL;
}
++self->len;
_list_unlock(self);
return node;
}
/**
* @brief Pop the value from list head, return NULL if list empty.
*
* @param[in] list pointer to list
* @return value in the head node
*/
void *utils_list_pop(void *list)
{
List *self = (List *)list;
ListNode *node = NULL;
_list_lock(self);
if (!self->len) {
_list_unlock(self);
return NULL;
}
node = self->head;
if (--self->len) {
(self->head = node->next)->prev = NULL;
} else {
self->head = self->tail = NULL;
}
node->next = node->prev = NULL;
_list_unlock(self);
void *val = node->val;
self->func.list_free(node);
return val;
}
/**
* @brief Delete the node in list and release the resource.
*
* @param[in] list pointer to list
* @param[in] node pointer to node needed remove
*/
void utils_list_remove(void *list, void *node)
{
List *self = (List *)list;
_list_lock(self);
_list_remove(self, node);
_list_unlock(self);
}
/**
* @brief Process list using handle function.
*
* @param[in] list pointer to list
* @param[in] direction direction to traverse
* @param[in] handle process function @see OnNodeProcessHandle
* @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
*/
void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data)
{
int rc;
ListNode *node = NULL;
List *self = (List *)list;
_list_lock(self);
if (!utils_list_len_get(list)) {
_list_unlock(self);
return;
}
ListIterator iterator = {
.direction = direction,
.list = self,
.next = direction == LIST_HEAD ? self->head : self->tail,
};
// traverse list to process
while ((node = iterator.next) != NULL) {
iterator.next = iterator.direction == LIST_HEAD ? node->next : node->prev;
if (!node->val) {
_list_remove(list, node);
continue;
}
// process node and val
if (handle) {
rc = handle(list, node, node->val, usr_data);
if (rc) {
break;
}
}
}
_list_unlock(list);
}

View File

@@ -0,0 +1,178 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file utils_log.c
* @brief different level log generator
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "utils_log.h"
static const char *LEVEL_STR[] = {"DIS", "ERR", "WRN", "INF", "DBG"};
static LogHandleFunc sg_log_handle_func;
static char *sg_log_buffer;
static int sg_log_max_size;
static void *sg_log_mutex;
static LogLevel sg_log_print_level = LOG_LEVEL_DEBUG;
extern LogLevel sg_log_upload_level;
/**
* @brief Get file name form path.
*
* @param[in] path file path
* @return file name
*/
static const char *_get_filename(const char *path)
{
#ifdef WIN32
char ch = '\\';
#else
char ch = '/';
#endif
const char *q = strrchr(path, ch);
if (!q) {
q = path;
} else {
q++;
}
return q;
}
/**
* @brief Init log with func, log level, max log size.
*
* @param[in] func function should be implement for utils log
* @param[in] log_level @see LogLevel
* @param[in] max_log_size max size of log to print
* @return 0 for success
*/
int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size)
{
sg_log_handle_func = func;
sg_log_print_level = log_level;
sg_log_max_size = max_log_size;
if (func.log_mutex_create) {
sg_log_mutex = func.log_mutex_create();
if (!sg_log_mutex) {
return -1;
}
}
if (func.log_malloc) {
sg_log_buffer = func.log_malloc(max_log_size);
if (!sg_log_buffer) {
if (sg_log_mutex) {
func.log_mutex_destroy(sg_log_mutex);
}
}
}
return !sg_log_buffer;
}
/**
* @brief Deinit log.
*
*/
void utils_log_deinit(void)
{
sg_log_handle_func.log_free(sg_log_buffer);
if (sg_log_mutex) {
sg_log_handle_func.log_mutex_destroy(sg_log_mutex);
}
sg_log_mutex = NULL;
sg_log_buffer = NULL;
}
/**
* @brief Set log level.
*
* @param log_level @see LogLevel
*/
void utils_log_set_level(LogLevel log_level)
{
sg_log_print_level = log_level;
}
/**
* @brief Get log level.
*
* @return @see LogLevel
*/
LogLevel utils_log_get_level(void)
{
return sg_log_print_level;
}
/**
* @brief Generate log if level higher than set.
*
* @param[in] file file path
* @param[in] func function where generate log
* @param[in] line line of source file where genertate log
* @param[in] level @see LogLevel
* @param[in] fmt format of log content
*/
void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...)
{
if (level > sg_log_print_level) {
return;
}
if (sg_log_mutex) {
sg_log_handle_func.log_mutex_lock(sg_log_mutex);
}
/* format log content */
const char *file_name = _get_filename(file);
char *o = sg_log_buffer;
memset(sg_log_buffer, 0, sg_log_max_size);
o += snprintf(sg_log_buffer, sg_log_max_size, "%s|%s|%s|%s(%d): ", LEVEL_STR[level],
sg_log_handle_func.log_get_current_time_str(), file_name, func, line);
va_list ap;
va_start(ap, fmt);
vsnprintf(o, sg_log_max_size - 2 - strlen(sg_log_buffer), fmt, ap);
va_end(ap);
strncat(sg_log_buffer, "\r\n", sg_log_max_size - strlen(sg_log_buffer) - 1);
if (level <= sg_log_print_level) {
if (sg_log_handle_func.log_handle) {
sg_log_handle_func.log_handle(sg_log_buffer);
}
sg_log_handle_func.log_printf("%s", sg_log_buffer);
}
/* append to upload buffer */
if (sg_log_handle_func.log_upload) {
sg_log_handle_func.log_upload(level, sg_log_buffer);
}
if (sg_log_mutex) {
sg_log_handle_func.log_mutex_unlock(sg_log_mutex);
}
return;
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -0,0 +1,214 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_utils.cc
* @brief unittest for utils
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-07
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-27 <td>1.1 <td>fancyxu <td>support utils json
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>rename HAL_Timer and add utils json test
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "qcloud_iot_platform.h"
#include "utils_json.h"
#include "utils_list.h"
#include "utils_log.h"
namespace utils_unittest {
/**
* @brief test fixture of utils list
*
*/
class UtilsListTest : public testing::Test {
protected:
void SetUp() override {
UtilsListFunc func = DEFAULT_LIST_FUNCS;
self_list = utils_list_create(func, 10);
ASSERT_NE(self_list, nullptr);
for (int i = 0; i < 10;) {
int *val = reinterpret_cast<int *>(HAL_Malloc(sizeof(int)));
*val = i++;
ASSERT_NE(utils_list_push(self_list, reinterpret_cast<void *>(val)), nullptr);
ASSERT_EQ(utils_list_len_get(self_list), i);
}
}
void TearDown() override { utils_list_destroy(self_list); }
void *self_list;
};
/**
* @brief Test list.
*
*/
TEST_F(UtilsListTest, list) {
ASSERT_EQ(utils_list_push(self_list, reinterpret_cast<void *>(1)), nullptr);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(utils_list_len_get(self_list), 10 - i);
int *val = reinterpret_cast<int *>(utils_list_pop(self_list));
ASSERT_EQ(*val, i);
HAL_Free(val);
}
}
/**
* @brief Test remove list node
*
* @param[in,out] list pointer to list
* @param[in,out] node pointer to node
* @param[in,out] val pointer to val
* @param[in,out] usr_data pointer to usr data
* @return @see UtilsListResult
*/
static UtilsListResult list_process_remove(void *list, void *node, void *val, void *usr_data) {
static int i = 0;
if (*reinterpret_cast<int *>(val) != i++) {
return LIST_TRAVERSE_BREAK;
}
utils_list_remove(list, node);
return LIST_TRAVERSE_CONTINUE;
}
/**
* @brief Test list process.
*
*/
TEST_F(UtilsListTest, list_process) {
utils_list_process(self_list, LIST_HEAD, list_process_remove, NULL);
ASSERT_EQ(utils_list_len_get(self_list), 0);
}
/**
* @brief Test log.
*
*/
TEST(UtilsLogTest, log) {
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
ASSERT_EQ(utils_log_init(func, LOG_LEVEL_DEBUG, 2048), 0);
Log_d("Here is a debug level log test!");
Log_i("Here is a info level log test!");
Log_w("Here is a warning level log test!");
Log_e("Here is a error level log test!");
utils_log_deinit();
}
/**
* @brief Test json.
*
*/
TEST(UtilsJsonTest, json) {
char test_json[] =
"{\"str_test\":\"test\",\"int_test\":100,\"float_test\":1.210f,\"bool_test\":true,"
"\"bool_false_test\":false,\"null_test\":null}";
UtilsJsonValue value;
int data_int = 0;
ASSERT_EQ(utils_json_value_get("int_test", strlen("int_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &data_int), 0);
ASSERT_EQ(data_int, 100);
float data_float = 0.0;
ASSERT_EQ(utils_json_value_get("float_test", strlen("float_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &data_float), 0);
ASSERT_EQ(data_float, 1.210f);
ASSERT_EQ(utils_json_value_get("bool_test", strlen("bool_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(strncmp(value.value, "true", value.value_len), 0);
ASSERT_EQ(utils_json_value_get("bool_false_test", strlen("bool_false_test"), test_json, strlen(test_json), &value),
0);
ASSERT_EQ(strncmp(value.value, "false", value.value_len), 0);
ASSERT_EQ(utils_json_value_get("null_test", strlen("null_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(strncmp(value.value, "null", value.value_len), 0);
ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
char test_json_depth[] = "{\"depth_test\": {\"test\":\"test1\"}}";
ASSERT_EQ(utils_json_value_get("depth_test.test", strlen("depth_test.test"), test_json_depth, strlen(test_json_depth),
&value),
0);
ASSERT_EQ(strncmp(value.value, "test1", value.value_len), 0);
char test_json_before_strip[] =
"{\\\"str_test\":\\\"test\\\",\\\"int_test\\\":100,\\\"float_test\\\":1.210f,\\\"bool_test\\\":true,"
"\\\"bool_false_test\\\":false,\\\"null_test\\\":null}";
ASSERT_EQ(utils_json_strip_transfer(test_json_before_strip, strlen(test_json_before_strip)), strlen(test_json));
ASSERT_EQ(strncmp(test_json_before_strip, test_json, strlen(test_json_before_strip)), 0);
char test_json_break[] = "{\n\"str_test\":\"test\"\n}";
ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json_break, strlen(test_json_break), &value), 0);
ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
}
/**
* @brief Test json array.
*
*/
static const char *sg_dst_value[12];
static UtilsJsonArrayIterResult _array_handle(const char *value, int value_len, void *arg) {
static int i = 0;
EXPECT_EQ(0, strncmp(sg_dst_value[i++], value, value_len));
return UTILS_JSON_ARRAY_ITER_CONTINUE;
}
TEST(UtilsJsonTest, json_array) {
int i = 0;
char test_array_string[] = "[\"test0\",\"test1\"]";
sg_dst_value[i++] = "test0";
sg_dst_value[i++] = "test1";
char test_array_int[] = "[1,2]";
sg_dst_value[i++] = "1";
sg_dst_value[i++] = "2";
char test_array_float[] = "[1.20f,1.21f]";
sg_dst_value[i++] = "1.20f";
sg_dst_value[i++] = "1.21f";
char test_array_bool[] = "[false,true]";
sg_dst_value[i++] = "false";
sg_dst_value[i++] = "true";
char test_array_null[] = "[null,null]";
sg_dst_value[i++] = "null";
sg_dst_value[i++] = "null";
char test_array_obj[] = "[{\"test\":0},{\"test\":1}]";
sg_dst_value[i++] = "{\"test\":0}";
sg_dst_value[i++] = "{\"test\":1}";
utils_json_array_parse(test_array_string, sizeof(test_array_string) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_int, sizeof(test_array_int) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_float, sizeof(test_array_float) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_bool, sizeof(test_array_bool) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_null, sizeof(test_array_null) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_obj, sizeof(test_array_obj) - 1, _array_handle, NULL);
}
} // namespace utils_unittest