enh: unit test and optimization for base58

This commit is contained in:
kailixu 2024-02-22 17:46:36 +08:00
parent a34402a11f
commit 6ac6e8f2bd
4 changed files with 117 additions and 8 deletions

View File

@ -22,6 +22,9 @@
extern "C" { extern "C" {
#endif #endif
#define TBASE_MAX_ILEN 4096
#define TBASE_MAX_OLEN 5653
uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen); uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen);
char *base58_encode(const uint8_t *value, int32_t vlen); char *base58_encode(const uint8_t *value, int32_t vlen);

View File

@ -18,24 +18,29 @@
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
#define BASE_BUF_SIZE 256 #define TBASE_BUF_SIZE 256
static const char *basis_58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static const char *basis_58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
char *base58_encode(const uint8_t *value, int32_t vlen) { char *base58_encode(const uint8_t *value, int32_t vlen) {
const uint8_t *pb = value; const uint8_t *pb = value;
const uint8_t *pe = pb + vlen; const uint8_t *pe = pb + vlen;
uint8_t buf[BASE_BUF_SIZE] = {0}; uint8_t buf[TBASE_BUF_SIZE] = {0};
uint8_t *pbuf = &buf[0]; uint8_t *pbuf = &buf[0];
bool bfree = false; bool bfree = false;
int32_t nz = 0, size = 0, len = 0; int32_t nz = 0, size = 0, len = 0;
if (vlen > TBASE_MAX_ILEN) {
terrno = TSDB_CODE_INVALID_PARA;
return NULL;
}
while (pb != pe && *pb == 0) { while (pb != pe && *pb == 0) {
++pb; ++pb;
++nz; ++nz;
} }
size = (pe - pb) * 69 / 50 + 1; size = (pe - pb) * 69 / 50 + 1;
if (size > BASE_BUF_SIZE) { if (size > TBASE_BUF_SIZE) {
if (!(pbuf = taosMemoryCalloc(1, size))) { if (!(pbuf = taosMemoryCalloc(1, size))) {
terrno = TSDB_CODE_OUT_OF_MEMORY; terrno = TSDB_CODE_OUT_OF_MEMORY;
return NULL; return NULL;
@ -47,7 +52,7 @@ char *base58_encode(const uint8_t *value, int32_t vlen) {
int32_t num = *pb; int32_t num = *pb;
int32_t i = 0; int32_t i = 0;
for (int32_t j = (int32_t)size - 1; (num != 0 || i < len) && j >= 0; --j, ++i) { for (int32_t j = (int32_t)size - 1; (num != 0 || i < len) && j >= 0; --j, ++i) {
num += ((int32_t)buf[j]) << 8; num += ((int32_t)pbuf[j]) << 8;
pbuf[j] = num % 58; pbuf[j] = num % 58;
num /= 58; num /= 58;
} }
@ -57,7 +62,7 @@ char *base58_encode(const uint8_t *value, int32_t vlen) {
const uint8_t *pi = pbuf + (size - len); const uint8_t *pi = pbuf + (size - len);
while (pi != pbuf + size && *pi == 0) ++pi; while (pi != pbuf + size && *pi == 0) ++pi;
uint8_t *result = taosMemoryCalloc(1, size + 1); uint8_t *result = taosMemoryCalloc(1, nz + (pbuf + size - pi) + 1);
if (!result) { if (!result) {
terrno = TSDB_CODE_OUT_OF_MEMORY; terrno = TSDB_CODE_OUT_OF_MEMORY;
if (bfree) taosMemoryFree(pbuf); if (bfree) taosMemoryFree(pbuf);
@ -83,11 +88,23 @@ static const signed char index_58[256] = {
uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen) { uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen) {
const char *pe = value + inlen; const char *pe = value + inlen;
uint8_t buf[BASE_BUF_SIZE] = {0}; uint8_t buf[TBASE_BUF_SIZE] = {0};
uint8_t *pbuf = &buf[0]; uint8_t *pbuf = &buf[0];
bool bfree = false; bool bfree = false;
int32_t nz = 0, size = 0, len = 0; int32_t nz = 0, size = 0, len = 0;
if (inlen > TBASE_MAX_OLEN) {
terrno = TSDB_CODE_INVALID_PARA;
return NULL;
}
for (int32_t i = 0; i < inlen; ++i) {
if (value[i] == 0) {
terrno = TSDB_CODE_INVALID_PARA;
return NULL;
}
}
while (*value && isspace(*value)) ++value; while (*value && isspace(*value)) ++value;
while (*value == '1') { while (*value == '1') {
++nz; ++nz;
@ -95,7 +112,7 @@ uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen) {
} }
size = (int32_t)(pe - value) * 733 / 1000 + 1; size = (int32_t)(pe - value) * 733 / 1000 + 1;
if (size > BASE_BUF_SIZE) { if (size > TBASE_BUF_SIZE) {
if (!(pbuf = taosMemoryCalloc(1, size))) { if (!(pbuf = taosMemoryCalloc(1, size))) {
terrno = TSDB_CODE_OUT_OF_MEMORY; terrno = TSDB_CODE_OUT_OF_MEMORY;
return NULL; return NULL;
@ -106,6 +123,7 @@ uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen) {
while (*value && !isspace(*value)) { while (*value && !isspace(*value)) {
int32_t num = index_58[(uint8_t)*value]; int32_t num = index_58[(uint8_t)*value];
if (num == -1) { if (num == -1) {
terrno = TSDB_CODE_INVALID_PARA;
if (bfree) taosMemoryFree(pbuf); if (bfree) taosMemoryFree(pbuf);
return NULL; return NULL;
} }
@ -127,7 +145,7 @@ uint8_t *base58_decode(const char *value, size_t inlen, int32_t *outlen) {
const uint8_t *it = pbuf + (size - len); const uint8_t *it = pbuf + (size - len);
while (it != pbuf + size && *it == 0) ++it; while (it != pbuf + size && *it == 0) ++it;
uint8_t *result = taosMemoryCalloc(1, size + 1); uint8_t *result = taosMemoryCalloc(1, inlen + 1);
if (!result) { if (!result) {
if (bfree) taosMemoryFree(pbuf); if (bfree) taosMemoryFree(pbuf);
terrno = TSDB_CODE_OUT_OF_MEMORY; terrno = TSDB_CODE_OUT_OF_MEMORY;

View File

@ -100,3 +100,11 @@ add_test(
NAME talgoTest NAME talgoTest
COMMAND talgoTest COMMAND talgoTest
) )
# tbaseCodecTest
add_executable(tbaseCodecTest "tbaseCodecTest.cpp")
target_link_libraries(tbaseCodecTest os util common gtest_main)
add_test(
NAME tbaseCodecTest
COMMAND tbaseCodecTest

View File

@ -0,0 +1,80 @@
#include <gtest/gtest.h>
#include <cassert>
#include <iostream>
#include "os.h"
#include "taos.h"
#include "taoserror.h"
#include "tbase58.h"
#include "tbase64.h"
#include "tglobal.h"
using namespace std;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wsign-compare"
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
static void checkBase58Codec(uint8_t *pRaw, int32_t rawLen, int32_t index) {
char *pEnc = base58_encode((const uint8_t *)pRaw, rawLen);
ASSERT_NE(nullptr, pEnc);
int32_t encLen = strlen(pEnc);
std::cout << "index:" << index << ", encLen is " << encLen << std::endl;
int32_t decLen = 0;
char *pDec = (char *)base58_decode((const char *)pEnc, encLen, &decLen);
std::cout << "index:" << index << ", decLen is " << decLen << std::endl;
ASSERT_NE(nullptr, pDec);
ASSERT_EQ(rawLen, decLen);
ASSERT_LE(rawLen, encLen);
ASSERT_EQ(0, strncmp((char *)pRaw, pDec, rawLen));
taosMemoryFreeClear(pDec);
taosMemoryFreeClear(pEnc);
}
TEST(TD_BASE_CODEC_TEST, tbase58_test) {
const int32_t TEST_LEN_MAX = TBASE_MAX_ILEN;
const int32_t TEST_LEN_STEP = 10;
int32_t rawLen = 0;
uint8_t *pRaw = NULL;
pRaw = (uint8_t *)taosMemoryCalloc(1, TEST_LEN_MAX);
ASSERT_NE(nullptr, pRaw);
// 1. normal case
// string blend with char and '\0'
rawLen = TEST_LEN_MAX;
for (int32_t i = 0; i < TEST_LEN_MAX; i += 1000) {
checkBase58Codec(pRaw, rawLen, i);
pRaw[i] = i & 127;
}
// string without '\0'
for (int32_t i = 0; i < TEST_LEN_MAX; ++i) {
pRaw[i] = i & 127;
}
checkBase58Codec(pRaw, TEST_LEN_MAX, 0);
for (int32_t i = 0; i < TEST_LEN_MAX; i += 1000) {
rawLen = i;
checkBase58Codec(pRaw, rawLen, i);
}
taosMemoryFreeClear(pRaw);
ASSERT_EQ(nullptr, pRaw);
// 2. overflow case
char tmp[1];
char *pEnc = base58_encode((const uint8_t *)tmp, TBASE_MAX_ILEN + 1);
ASSERT_EQ(nullptr, pEnc);
char *pDec = (char *)base58_decode((const char *)tmp, TBASE_MAX_OLEN + 1, NULL);
ASSERT_EQ(nullptr, pDec);
taosMemoryFreeClear(pRaw);
ASSERT_EQ(nullptr, pRaw);
}