diff --git a/src/codegen.c b/src/codegen.c index 4eba2eae..a4e6c0ff 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -2245,6 +2245,10 @@ codegen_coerceviaio_expression(codegen_context *context, {FuncOpCode__devcast_text_to_int2, "text", NULL, "int2", NULL}, {FuncOpCode__devcast_text_to_int4, "text", NULL, "int4", NULL}, {FuncOpCode__devcast_text_to_int8, "text", NULL, "int8", NULL}, + {FuncOpCode__devcast_text_to_float2, "text", NULL, "float2", "pg_strom"}, + {FuncOpCode__devcast_text_to_float4, "text", NULL, "float4", NULL}, + {FuncOpCode__devcast_text_to_float8, "text", NULL, "float8", NULL}, + {FuncOpCode__devcast_text_to_numeric, "text", NULL, "numeric", NULL}, {FuncOpCode__Invalid, NULL, NULL, NULL, NULL}, }; devtype_info *stype; diff --git a/src/xpu_basetype.cu b/src/xpu_basetype.cu index eb14e4eb..a4552c1f 100644 --- a/src/xpu_basetype.cu +++ b/src/xpu_basetype.cu @@ -897,7 +897,7 @@ PG_BITWISE_OPERATOR_TEMPLATE(int8shl,int8,int8,int4,<<) \ if (c < '0' || c > '9') \ { \ - STROM_ELOG(kcxt, "invalid input for int1"); \ + STROM_ELOG(kcxt, "invalid input for " #XTYPE); \ return false; \ } \ if (ival > (__MAX/10) || \ @@ -921,6 +921,231 @@ PG_DEVCAST_TEXT_TO_INT_TEMPLATE(int2,int32_t,SHRT_MIN,SHRT_MAX) PG_DEVCAST_TEXT_TO_INT_TEMPLATE(int4,int32_t,INT_MIN,INT_MAX) PG_DEVCAST_TEXT_TO_INT_TEMPLATE(int8,int64_t,LONG_MIN,LONG_MAX) -//DEVONLY_FUNC_OPCODE(float2, devcast_text_to_float2, text, DEVKIND__ANY, 12) -//DEVONLY_FUNC_OPCODE(float4, devcast_text_to_float4, text, DEVKIND__ANY, 12) -//DEVONLY_FUNC_OPCODE(float8, devcast_text_to_float8, text, DEVKIND__ANY, 12) +STATIC_FUNCTION(bool) +__strtof(const char *str, int len, double *p_fval) +{ + const char *pos; + double fval = 0.0; + double divisor = 1.0; + char sign = '+'; + bool meet_period = false; + + /* skip leading whitespace */ + while (len > 0 && __isspace(*str)) + { + str++; + len--; + } + if (len == 0) + return false; + pos = str; + /* sign (optional) */ + if (*pos == '+' || *pos == '-') + { + sign = *pos; + pos++; + len--; + } + /* Decimal */ + while (len > 0) + { + int c = *pos; + + if (c == '.' && !meet_period) + meet_period = true; + else if (__isdigit(c)) + { + fval = 10.0 * fval + (double)(c - '0'); + if (meet_period) + divisor *= 10.0; + } + else + break; + pos++; + len--; + } + /* Exponential */ + if (len > 0 && (*pos == 'e' || *pos == 'E')) + { + int expo = 0; + char expo_sign = '+'; + + pos++; + len--; + if (len == 0) + goto out; + if (*pos == '+' || *pos == '-') + { + expo_sign = *pos; + pos++; + len--; + } + while (len > 0) + { + int c = *pos; + + if (!__isdigit(c)) + break; + expo = 10 * expo + (c - '0'); + pos++; + len--; + } + if (expo_sign == '+') + divisor *= pow(1.0, expo); + else + fval *= pow(1.0, expo); + } + /* skip tailing whitespace */ + while (len > 0 && __isspace(*pos)) + { + pos++; + len--; + } + /* len == 0 if valid digits */ + if (len == 0) + { + fval /= divisor; + if (sign == '-') + fval = -fval; + *p_fval = fval; + return true; + } +out: + assert(pos >= str); + len += (pos - str); + if (len >= 3 && __strncasecmp(str, "NaN", 3) == 0) + { + fval = DBL_NAN; + str += 3; + len -= 3; + } + else if (len >= 8 && __strncasecmp(str, "Infinity", 8) == 0) + { + fval = DBL_INF; + str += 8; + len -= 8; + } + else if (len >= 9 && __strncasecmp(str, "+Infinity", 9) == 0) + { + fval = DBL_INF; + str += 9; + len -= 9; + } + else if (len >= 9 && __strncasecmp(str, "-Infinity", 9) == 0) + { + fval = -DBL_INF; + str += 9; + len -= 9; + } + else if (len >= 3 && __strncasecmp(str, "inf", 3) == 0) + { + fval = DBL_INF; + str += 3; + len -= 3; + } + else if (len >= 4 && __strncasecmp(str, "+inf", 4) == 0) + { + fval = DBL_INF; + str += 4; + len -= 4; + } + else if (len >= 4 && __strncasecmp(str, "-inf", 4) == 0) + { + fval = -DBL_INF; + str += 4; + len -= 4; + } + /* skip tailing whitespace */ + while (len > 0 && __isspace(*str)) + { + str++; + len--; + } + if (len == 0) + { + *p_fval = fval; + return true; + } + return false; +} + +PUBLIC_FUNCTION(bool) +pgfn_devcast_text_to_float2(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS1(float2,text,arg); + double fval; + + if (XPU_DATUM_ISNULL(&arg)) + result->expr_ops = NULL; + else if (!xpu_text_is_valid(kcxt, &arg)) + return false; + else if (__strtof(arg.value, arg.length, &fval)) + { + if (isfinite(fval) && (fval < -(double)HALF_MAX || + fval > (double)HALF_MAX)) + { + STROM_ELOG(kcxt, "value is out of range for float2"); + return false; + } + result->expr_ops = &xpu_float2_ops; + result->value = fval; + } + else + { + STROM_ELOG(kcxt, "invalid input for float2"); + return false; + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_devcast_text_to_float4(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS1(float4,text,arg); + double fval; + + if (XPU_DATUM_ISNULL(&arg)) + result->expr_ops = NULL; + else if (!xpu_text_is_valid(kcxt, &arg)) + return false; + else if (__strtof(arg.value, arg.length, &fval)) + { + if (isfinite(fval) && (fval < -(double)FLT_MAX || + fval > (double)FLT_MAX)) + { + STROM_ELOG(kcxt, "value is out of range for float4"); + return false; + } + result->expr_ops = &xpu_float4_ops; + result->value = fval; + } + else + { + STROM_ELOG(kcxt, "invalid input for float4"); + return false; + } + return true; +} + +PUBLIC_FUNCTION(bool) +pgfn_devcast_text_to_float8(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS1(float8,text,arg); + double fval; + + if (XPU_DATUM_ISNULL(&arg)) + result->expr_ops = NULL; + else if (!xpu_text_is_valid(kcxt, &arg)) + return false; + else if (__strtof(arg.value, arg.length, &fval)) + { + result->expr_ops = &xpu_float8_ops; + result->value = fval; + } + else + { + STROM_ELOG(kcxt, "invalid input for float8"); + return false; + } + return true; +} diff --git a/src/xpu_common.h b/src/xpu_common.h index 0f905892..199b91b3 100644 --- a/src/xpu_common.h +++ b/src/xpu_common.h @@ -301,39 +301,6 @@ __memcmp(const void *__s1, const void *__s2, size_t n) return 0; } -INLINE_FUNCTION(int) -__strcmp(const char *s1, const char *s2) -{ - unsigned char c1, c2; - - do { - c1 = (unsigned char) *s1++; - c2 = (unsigned char) *s2++; - - if (c1 == '\0') - return c1 - c2; - } while (c1 == c2); - - return c1 - c2; -} - -INLINE_FUNCTION(int) -__strncmp(const char *s1, const char *s2, int n) -{ - unsigned char c1, c2; - - while (n > 0) - { - c1 = (unsigned char) *s1++; - c2 = (unsigned char) *s2++; - - if (c1 == '\0' || c1 != c2) - return c1 - c2; - n--; - } - return 0; -} - /* ---------------------------------------------------------------- * * Fundamental CUDA definitions diff --git a/src/xpu_numeric.cu b/src/xpu_numeric.cu index 18d84ce4..e6a21aad 100644 --- a/src/xpu_numeric.cu +++ b/src/xpu_numeric.cu @@ -1230,3 +1230,174 @@ pg_numeric_to_cstring(kern_context *kcxt, } return (int)(cp - buf); } + +/* + * Devcast function text -> numeric + */ +STATIC_FUNCTION(bool) +__strtonum(const char *str, int len, xpu_numeric_t *result) +{ + const char *pos; + int128_t ival = 0; + int weight = 0; + char sign = '+'; + bool meet_period = false; + + /* zero clear the result */ + memset(result, 0, sizeof(xpu_numeric_t)); + /* skip leading whitespace */ + while (len > 0 && __isspace(*str)) + { + str++; + len--; + } + if (len == 0) + return false; + pos = str; + /* sign (optional) */ + if (*pos == '+' || *pos == '-') + { + sign = *pos; + pos++; + len--; + } + /* decimal */ + while (len > 0) + { + int c = *pos; + + if (c == '.' && !meet_period) + meet_period = true; + else if (__isdigit(c)) + { + ival = 10 * ival + (c - '0'); + if (meet_period) + weight++; + } + else + break; + pos++; + len--; + } + /* exponential */ + if (len > 0 && (*pos == 'e' || *pos == 'E')) + { + int expo = 0; + char expo_sign = '+'; + + pos++; + len--; + if (len == 0) + goto not_finite; + if (*pos == '+' || *pos == '-') + { + expo_sign = *pos; + pos++; + len--; + } + while (len > 0) + { + int c = *pos; + + if (!__isdigit(c)) + break; + expo = 10 * expo + (c - '0'); + pos++; + len--; + } + if (expo_sign == '+') + weight += expo_sign; + else + weight -= expo_sign; + } + /* skip tailing whitespace */ + while (len > 0 && __isspace(*pos)) + { + pos++; + len--; + } + /* len == 0 if finite numeric value */ + if (len == 0) + { + result->expr_ops = &xpu_numeric_ops; + result->kind = XPU_NUMERIC_KIND__VALID; + result->weight = weight; + result->u.value = (sign == '+' ? ival : -ival); + return true; + } +not_finite: + assert(pos >= str); + len += (pos - str); + if (len >= 3 && __strncasecmp(str, "NaN", 3) == 0) + { + result->kind = XPU_NUMERIC_KIND__NAN; + str += 3; + len -= 3; + } + else if (len >= 8 && __strncasecmp(str, "Infinity", 8) == 0) + { + result->kind = XPU_NUMERIC_KIND__POS_INF; + str += 8; + len -= 8; + } + else if (len >= 9 && __strncasecmp(str, "+Infinity", 9) == 0) + { + result->kind = XPU_NUMERIC_KIND__POS_INF; + str += 9; + len -= 9; + } + else if (len >= 9 && __strncasecmp(str, "-Infinity", 9) == 0) + { + result->kind = XPU_NUMERIC_KIND__NEG_INF; + str += 9; + len -= 9; + } + else if (len >= 3 && __strncasecmp(str, "inf", 3) == 0) + { + result->kind = XPU_NUMERIC_KIND__POS_INF; + str += 3; + len -= 3; + } + else if (len >= 4 && __strncasecmp(str, "+inf", 4) == 0) + { + result->kind = XPU_NUMERIC_KIND__NEG_INF; + str += 4; + len -= 4; + } + else if (len >= 4 && __strncasecmp(str, "-inf", 4) == 0) + { + result->kind = XPU_NUMERIC_KIND__NEG_INF; + str += 4; + len -= 4; + } + /* skip tailing whitespace */ + while (len > 0 && __isspace(*str)) + { + str++; + len--; + } + if (len == 0) + { + result->expr_ops = &xpu_numeric_ops; + return true; + } + return false; +} + +PUBLIC_FUNCTION(bool) +devcast_text_to_numeric(XPU_PGFUNCTION_ARGS) +{ + KEXP_PROCESS_ARGS1(numeric,text,arg); + + if (XPU_DATUM_ISNULL(&arg)) + result->expr_ops = NULL; + else if (!xpu_text_is_valid(kcxt, &arg)) + return false; + else if (!__strtonum(arg.value, + arg.length, result)) + { + STROM_ELOG(kcxt, "invalid input for numeric"); + return false; + } + return true; +} diff --git a/src/xpu_opcodes.h b/src/xpu_opcodes.h index 3fbe4169..886dec92 100644 --- a/src/xpu_opcodes.h +++ b/src/xpu_opcodes.h @@ -129,10 +129,10 @@ DEVONLY_FUNC_OPCODE(int1, devcast_text_to_int1, text, DEVKIND__ANY, 10) DEVONLY_FUNC_OPCODE(int2, devcast_text_to_int2, text, DEVKIND__ANY, 10) DEVONLY_FUNC_OPCODE(int4, devcast_text_to_int4, text, DEVKIND__ANY, 10) DEVONLY_FUNC_OPCODE(int8, devcast_text_to_int8, text, DEVKIND__ANY, 10) -//DEVONLY_FUNC_OPCODE(float2, devcast_text_to_float2, text, DEVKIND__ANY, 12) -//DEVONLY_FUNC_OPCODE(float4, devcast_text_to_float4, text, DEVKIND__ANY, 12) -//DEVONLY_FUNC_OPCODE(float8, devcast_text_to_float8, text, DEVKIND__ANY, 12) -//DEVONLY_FUNC_OPCODE(numeric, devcast_text_to_numeric, text, DEVKIND__ANY, 15) +DEVONLY_FUNC_OPCODE(float2, devcast_text_to_float2, text, DEVKIND__ANY, 12) +DEVONLY_FUNC_OPCODE(float4, devcast_text_to_float4, text, DEVKIND__ANY, 12) +DEVONLY_FUNC_OPCODE(float8, devcast_text_to_float8, text, DEVKIND__ANY, 12) +DEVONLY_FUNC_OPCODE(numeric, devcast_text_to_numeric, text, DEVKIND__ANY, 15) /* '+' : add operators */ __FUNC_OPCODE(int1pl, int1/int1, 1, "pg_strom") diff --git a/src/xpu_textlib.h b/src/xpu_textlib.h index f5537dc5..e93eaef7 100644 --- a/src/xpu_textlib.h +++ b/src/xpu_textlib.h @@ -70,6 +70,74 @@ xpu_bytea_is_valid(kern_context *kcxt, const xpu_bytea_t *arg) return true; } +/* + * Functions in string.h + */ +#define __UPPER(x) (((x) >= 'a' && (x) <= 'z') ? 'A' + ((x) - 'a') : (x)) +#define __LOWER(x) (((x) >= 'A' && (x) <= 'Z') ? 'a' + ((X) - 'A') : (x)) + +INLINE_FUNCTION(int) +__strcmp(const char *s1, const char *s2) +{ + for (;;) + { + char c1 = *s1++; + char c2 = *s2++; + int diff = c1 - c2; + + if (c1 == '\0' || diff != 0) + return diff; + } +} + +INLINE_FUNCTION(int) +__strncmp(const char *s1, const char *s2, int n) +{ + while (n > 0) + { + char c1 = (unsigned char) *s1++; + char c2 = (unsigned char) *s2++; + + if (c1 == '\0' || c1 != c2) + return c1 - c2; + n--; + } + return 0; +} + +INLINE_FUNCTION(int) +__strcasecmp(const char *s1, const char *s2) +{ + for (;;) + { + char c1 = *s1++; + char c2 = *s2++; + int diff = __UPPER(c1) - __UPPER(c2); + + if (c1 == '\0' || diff != 0) + return diff; + } +} + +INLINE_FUNCTION(int) +__strncasecmp(const char *s1, const char *s2, int n) +{ + int diff; + + while (n > 0) + { + char c1 = *s1++; + char c2 = *s2++; + + diff = __UPPER(c1) - __UPPER(c2); + if (c1 == '\0' || diff != 0) + return diff; + n--; + } while (diff == 0); + + return diff; +} + /* * functions in ctype.h */ @@ -91,4 +159,16 @@ __islower(int c) return (c >= 'a' && c <= 'z'); } +INLINE_FUNCTION(bool) +__isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +INLINE_FUNCTION(bool) +__isxdigit(int c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + #endif /* XPU_TEXTLIB_H */