homework-jianmu/source/common/src/tvariant.c

567 lines
14 KiB
C

/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _DEFAULT_SOURCE
#include "tvariant.h"
#include "ttime.h"
#include "ttokendef.h"
#include "tvariant.h"
static int32_t parseBinaryUInteger(const char *z, int32_t n, uint64_t *value) {
// skip head 0b
const char *p = z + 2;
int32_t l = n - 2;
while (*p == '0' && l > 0) {
p++;
l--;
}
if (l > 64) { // too big
return TSDB_CODE_FAILED;
}
uint64_t val = 0;
for (int32_t i = 0; i < l; i++) {
val = val << 1;
if (p[i] == '1') {
val |= 1;
} else if (p[i] != '0') { // abnormal char
return TSDB_CODE_FAILED;
}
}
*value = val;
return TSDB_CODE_SUCCESS;
}
static int32_t parseSignAndUInteger(const char *z, int32_t n, bool *is_neg, uint64_t *value, bool parseFloat) {
// parse sign
bool has_sign = false;
if (z[0] == '-') {
*is_neg = true;
has_sign = true;
} else if (z[0] == '+') {
has_sign = true;
} else if (z[0] != '.' && (z[0] < '0' || z[0] > '9')) {
return TSDB_CODE_FAILED;
}
if (has_sign) {
if (n < 2) {
return TSDB_CODE_FAILED;
}
z++;
n--;
}
SET_ERRNO(0);
char *endPtr = NULL;
if (z[0] == '0' && n > 2) {
if (z[1] == 'b' || z[1] == 'B') {
// paring as binary
return parseBinaryUInteger(z, n, value);
}
if (z[1] == 'x' || z[1] == 'X') {
// parsing as hex
*value = taosStr2UInt64(z, &endPtr, 16);
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
}
}
if (parseFloat) {
// parsing as double
double val = taosStr2Double(z, &endPtr);
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
return TSDB_CODE_FAILED;
}
if (val > (double)UINT64_MAX) {
SET_ERRNO(ERANGE);
return TSDB_CODE_FAILED;
}
*value = round(val);
return TSDB_CODE_SUCCESS;
}
return TSDB_CODE_FAILED;
}
int32_t toDoubleEx(const char *z, int32_t n, uint32_t type, double *value) {
if (n == 0) {
SET_ERRNO(EINVAL);
return TSDB_CODE_FAILED;
}
SET_ERRNO(0);
char *endPtr = NULL;
*value = taosStr2Double(z, &endPtr); // 0x already converted here
if (ERRNO == ERANGE || ERRNO == EINVAL) return TSDB_CODE_FAILED;
if (endPtr - z == n) return TSDB_CODE_SUCCESS;
if (type == TK_NK_BIN || type == TK_NK_STRING) {
bool is_neg = false;
uint64_t uv = 0;
if (TSDB_CODE_SUCCESS == parseSignAndUInteger(z, n, &is_neg, &uv, false)) {
*value = is_neg ? -(double)uv : uv;
return TSDB_CODE_SUCCESS;
}
}
return TSDB_CODE_FAILED;
}
int32_t toIntegerPure(const char *z, int32_t n, int32_t base, int64_t *value) {
SET_ERRNO(0);
char *endPtr = NULL;
*value = taosStr2Int64(z, &endPtr, base);
if (endPtr - z == n) {
if (ERRNO == ERANGE || ERRNO == EINVAL) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
}
bool is_neg = false;
uint64_t uv = 0;
int32_t code = parseSignAndUInteger(z, n, &is_neg, &uv, false); // support 0b/0x
if (code == TSDB_CODE_SUCCESS) {
if (is_neg) {
if (uv > 1ull + INT64_MAX) {
*value = INT64_MIN;
return TSDB_CODE_FAILED;
} else {
*value = -(int64_t)uv;
}
} else {
if (uv > INT64_MAX) {
*value = INT64_MAX;
return TSDB_CODE_FAILED;
}
*value = uv;
}
}
return code;
}
int32_t toIntegerEx(const char *z, int32_t n, uint32_t type, int64_t *value) {
SET_ERRNO(0);
char *endPtr = NULL;
switch (type) {
case TK_NK_INTEGER: {
*value = taosStr2Int64(z, &endPtr, 10);
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
} break;
case TK_NK_FLOAT: {
double val = round(taosStr2Double(z, &endPtr));
if (val < (double)INT64_MIN || val > (double)INT64_MAX) {
return TSDB_CODE_FAILED;
}
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
return TSDB_CODE_FAILED;
}
*value = val;
return TSDB_CODE_SUCCESS;
} break;
default:
break;
}
if (n == 0) {
SET_ERRNO(EINVAL);
return TSDB_CODE_FAILED;
}
// 1. try to parse as integer
*value = taosStr2Int64(z, &endPtr, 10);
if (endPtr - z == n) {
if (ERRNO == ERANGE || ERRNO == EINVAL) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
} else if (ERRNO == 0 && *endPtr == '.') {
// pure decimal part
const char *s = endPtr + 1;
const char *end = z + n;
bool pure = true;
while (s < end) {
if (*s < '0' || *s > '9') {
pure = false;
break;
}
s++;
}
if (pure) {
if (endPtr + 1 < end && endPtr[1] > '4') {
const char *p = z;
while (*p == ' ') {
p++;
}
if (*p == '-') {
if (*value > INT64_MIN) {
(*value)--;
}
} else {
if (*value < INT64_MAX) {
(*value)++;
}
}
}
return TSDB_CODE_SUCCESS;
}
}
// 2. parse as other
bool is_neg = false;
uint64_t uv = 0;
int32_t code = parseSignAndUInteger(z, n, &is_neg, &uv, true);
if (code == TSDB_CODE_SUCCESS) {
// truncate into int64
if (is_neg) {
if (uv > 1ull + INT64_MAX) {
*value = INT64_MIN;
return TSDB_CODE_FAILED;
} else {
*value = -(int64_t)uv;
}
} else {
if (uv > INT64_MAX) {
*value = INT64_MAX;
return TSDB_CODE_FAILED;
}
*value = uv;
}
}
return code;
}
int32_t toUIntegerEx(const char *z, int32_t n, uint32_t type, uint64_t *value) {
SET_ERRNO(0);
char *endPtr = NULL;
const char *p = z;
switch (type) {
case TK_NK_INTEGER: {
*value = taosStr2UInt64(p, &endPtr, 10);
if (*p == '-' && *value) {
return TSDB_CODE_FAILED;
}
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
} break;
case TK_NK_FLOAT: {
double val = round(taosStr2Double(p, &endPtr));
if (val < 0 || val > (double)UINT64_MAX) {
return TSDB_CODE_FAILED;
}
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
return TSDB_CODE_FAILED;
}
*value = val;
return TSDB_CODE_SUCCESS;
} break;
default:
break;
}
if (n == 0) {
SET_ERRNO(EINVAL);
return TSDB_CODE_FAILED;
}
// 1. parse as integer
*value = taosStr2UInt64(p, &endPtr, 10);
if (endPtr - z == n) {
if (ERRNO == ERANGE || ERRNO == EINVAL || (*p == '-' && *value)) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
} else if (ERRNO == 0 && *endPtr == '.') {
const char *s = endPtr + 1;
const char *end = z + n;
bool pure = true;
while (s < end) {
if (*s < '0' || *s > '9') {
pure = false;
break;
}
s++;
}
if (pure) {
if (endPtr + 1 < end && endPtr[1] > '4' && *value < UINT64_MAX) {
(*value)++;
}
if (*p == '-' && *value) {
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
}
}
// 2. parse as other
bool is_neg = false;
int32_t code = parseSignAndUInteger(z, n, &is_neg, value, true);
if (is_neg) {
if (TSDB_CODE_SUCCESS == code && 0 == *value) {
return TSDB_CODE_SUCCESS;
}
return TSDB_CODE_FAILED;
}
return code;
}
int32_t toInteger(const char *z, int32_t n, int32_t base, int64_t *value) {
SET_ERRNO(0);
char *endPtr = NULL;
*value = taosStr2Int64(z, &endPtr, base);
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
SET_ERRNO(0);
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
}
int32_t toUInteger(const char *z, int32_t n, int32_t base, uint64_t *value) {
SET_ERRNO(0);
char *endPtr = NULL;
const char *p = z;
while (*p == ' ') p++;
if (*p == '-') {
return TSDB_CODE_FAILED;
}
*value = taosStr2UInt64(z, &endPtr, base);
if (ERRNO == ERANGE || ERRNO == EINVAL || endPtr - z != n) {
SET_ERRNO(0);
return TSDB_CODE_FAILED;
}
return TSDB_CODE_SUCCESS;
}
/**
* create SVariant from binary string, not ascii data
* @param pVar
* @param pz
* @param len
* @param type
*/
int32_t taosVariantCreateFromBinary(SVariant *pVar, const char *pz, size_t len, uint32_t type) {
switch (type) {
case TSDB_DATA_TYPE_BOOL:
case TSDB_DATA_TYPE_TINYINT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->i = GET_INT8_VAL(pz);
break;
}
case TSDB_DATA_TYPE_UTINYINT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->u = GET_UINT8_VAL(pz);
break;
}
case TSDB_DATA_TYPE_SMALLINT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->i = GET_INT16_VAL(pz);
break;
}
case TSDB_DATA_TYPE_USMALLINT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->u = GET_UINT16_VAL(pz);
break;
}
case TSDB_DATA_TYPE_INT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->i = GET_INT32_VAL(pz);
break;
}
case TSDB_DATA_TYPE_UINT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->u = GET_UINT32_VAL(pz);
break;
}
case TSDB_DATA_TYPE_BIGINT:
case TSDB_DATA_TYPE_TIMESTAMP: {
pVar->nLen = tDataTypes[type].bytes;
pVar->i = GET_INT64_VAL(pz);
break;
}
case TSDB_DATA_TYPE_UBIGINT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->u = GET_UINT64_VAL(pz);
break;
}
case TSDB_DATA_TYPE_DOUBLE: {
pVar->nLen = tDataTypes[type].bytes;
pVar->d = GET_DOUBLE_VAL(pz);
break;
}
case TSDB_DATA_TYPE_FLOAT: {
pVar->nLen = tDataTypes[type].bytes;
pVar->f = GET_FLOAT_VAL(pz);
break;
}
case TSDB_DATA_TYPE_NCHAR: { // here we get the nchar length from raw binary bits length
size_t lenInwchar = len / TSDB_NCHAR_SIZE;
pVar->ucs4 = taosMemoryCalloc(1, (lenInwchar + 1) * TSDB_NCHAR_SIZE);
if(!pVar->ucs4) return terrno;
(void)memcpy(pVar->ucs4, pz, lenInwchar * TSDB_NCHAR_SIZE);
pVar->nLen = (int32_t)len;
break;
}
case TSDB_DATA_TYPE_BINARY:
case TSDB_DATA_TYPE_VARBINARY:
case TSDB_DATA_TYPE_GEOMETRY: { // todo refactor, extract a method
pVar->pz = taosMemoryCalloc(len + 1, sizeof(char));
(void)memcpy(pVar->pz, pz, len);
pVar->nLen = (int32_t)len;
break;
}
default:
pVar->i = GET_INT32_VAL(pz);
pVar->nLen = tDataTypes[TSDB_DATA_TYPE_INT].bytes;
}
pVar->nType = type;
return 0;
}
void taosVariantDestroy(SVariant *pVar) {
if (pVar == NULL) return;
if (pVar->nType == TSDB_DATA_TYPE_BINARY || pVar->nType == TSDB_DATA_TYPE_NCHAR ||
pVar->nType == TSDB_DATA_TYPE_JSON || pVar->nType == TSDB_DATA_TYPE_GEOMETRY ||
pVar->nType == TSDB_DATA_TYPE_VARBINARY) {
taosMemoryFreeClear(pVar->pz);
pVar->nLen = 0;
}
}
int32_t taosVariantAssign(SVariant *pDst, const SVariant *pSrc) {
if (pSrc == NULL || pDst == NULL) return 0;
pDst->nType = pSrc->nType;
if (pSrc->nType == TSDB_DATA_TYPE_BINARY || pSrc->nType == TSDB_DATA_TYPE_VARBINARY ||
pSrc->nType == TSDB_DATA_TYPE_NCHAR || pSrc->nType == TSDB_DATA_TYPE_JSON ||
pSrc->nType == TSDB_DATA_TYPE_GEOMETRY) {
int32_t len = pSrc->nLen + TSDB_NCHAR_SIZE;
char *p = taosMemoryRealloc(pDst->pz, len);
if (!p) return terrno;
(void)memset(p, 0, len);
pDst->pz = p;
(void)memcpy(pDst->pz, pSrc->pz, pSrc->nLen);
pDst->nLen = pSrc->nLen;
return 0;
}
if (IS_NUMERIC_TYPE(pSrc->nType) || (pSrc->nType == TSDB_DATA_TYPE_BOOL)) {
pDst->i = pSrc->i;
}
return 0;
}
int32_t taosVariantCompare(const SVariant *p1, const SVariant *p2) {
if (p1->nType == TSDB_DATA_TYPE_NULL && p2->nType == TSDB_DATA_TYPE_NULL) {
return 0;
}
if (p1->nType == TSDB_DATA_TYPE_NULL) {
return -1;
}
if (p2->nType == TSDB_DATA_TYPE_NULL) {
return 1;
}
if (p1->nType == TSDB_DATA_TYPE_BINARY || p1->nType == TSDB_DATA_TYPE_VARBINARY ||
p1->nType == TSDB_DATA_TYPE_NCHAR || p1->nType == TSDB_DATA_TYPE_GEOMETRY) {
if (p1->nLen == p2->nLen) {
return memcmp(p1->pz, p2->pz, p1->nLen);
} else {
return p1->nLen > p2->nLen ? 1 : -1;
}
} else if (p1->nType == TSDB_DATA_TYPE_DOUBLE) {
if (p1->d == p2->d) {
return 0;
} else {
return p1->d > p2->d ? 1 : -1;
}
} else if (p1->nType == TSDB_DATA_TYPE_FLOAT) {
if (p1->f == p2->f) {
return 0;
} else {
return p1->f > p2->f ? 1 : -1;
}
} else if (IS_UNSIGNED_NUMERIC_TYPE(p1->nType)) {
if (p1->u == p2->u) {
return 0;
} else {
return p1->u > p2->u ? 1 : -1;
}
} else {
if (p1->i == p2->i) {
return 0;
} else {
return p1->i > p2->i ? 1 : -1;
}
}
}
char *taosVariantGet(SVariant *pVar, int32_t type) {
switch (type) {
case TSDB_DATA_TYPE_BOOL:
case TSDB_DATA_TYPE_TINYINT:
case TSDB_DATA_TYPE_SMALLINT:
case TSDB_DATA_TYPE_INT:
case TSDB_DATA_TYPE_BIGINT:
case TSDB_DATA_TYPE_TIMESTAMP:
return (char *)&pVar->i;
case TSDB_DATA_TYPE_UTINYINT:
case TSDB_DATA_TYPE_USMALLINT:
case TSDB_DATA_TYPE_UINT:
case TSDB_DATA_TYPE_UBIGINT:
return (char *)&pVar->u;
case TSDB_DATA_TYPE_DOUBLE:
return (char *)&pVar->d;
case TSDB_DATA_TYPE_FLOAT:
return (char *)&pVar->f;
case TSDB_DATA_TYPE_BINARY:
case TSDB_DATA_TYPE_VARBINARY:
case TSDB_DATA_TYPE_JSON:
case TSDB_DATA_TYPE_GEOMETRY:
return (char *)pVar->pz;
case TSDB_DATA_TYPE_NCHAR:
return (char *)pVar->ucs4;
default:
return NULL;
}
return NULL;
}