/* * ----------------------------------------------------- * Convert a string subscript to MUMPS string * Save result in a buffer pointed by targ. * * Entry: * sub - input string in subscript format * targ - output string buffer * xlat_flg- translate flag. * If true convert string to MUMPS format * Return: * (pointer to the last char. * converted in the targ string) + 1. * ----------------------------------------------------- */ unsigned char *gvsub2str(unsigned char *sub, unsigned char *targ, bool xlat_flg) { unsigned char buf1[MAX_KEY_SZ + 1], ch, *ptr, trail_ch; unsigned short *tbl_ptr; int expon, in_length, length, tmp; mstr mstr_ch, mstr_targ; ch = *sub++; if (STR_SUB_PREFIX == ch || (SUBSCRIPT_STDCOL_NULL == ch && KEY_DELIMITER == *sub)) { /* If this is a string */ if (xlat_flg) return gvstrsub(sub, targ); else { in_length = 0; ptr = targ; while ((ch = *sub++)) { /* Copy string to targ, xlating each char */ in_length++; if (STR_SUB_ESCAPE == ch) /* if this is an escape, demote next char */ ch = (*sub++ - 1); *targ++ = ch; } if (transform && gv_target && gv_target->collseq) { mstr_ch.len = in_length; mstr_ch.addr = (char *)ptr; mstr_targ.len = sizeof(buf1); mstr_targ.addr = (char *)buf1; do_xform(gv_target->collseq, XBACK, &mstr_ch, &mstr_targ, &length); memcpy(ptr, mstr_targ.addr, length); /* mstr_targ.addr is used just in case it is reallocated by the XBACK routine */ targ = ptr + length; } } } else { /* Number */ if (SUBSCRIPT_ZERO == ch) *targ++ = '0'; else { tbl_ptr = (unsigned short *)&dpos[0] - 1; trail_ch = KEY_DELIMITER; if ((signed char)ch >= 0) { /* Bit 7 of the exponent is set for positive numbers; must be negative */ trail_ch = NEG_MNTSSA_END; tbl_ptr = (unsigned short *)dneg; ch = ~ch; *targ++ = '-'; } ch -= (SUBSCRIPT_BIAS - 1); /* Unbias the exponent */ expon = ch; if ((signed char)ch <= 0) { /* number is a fraction */ ch = -(signed char)ch; /* Save decimal point */ *targ++ = '.'; /* generate leading 0's */ do *targ++ = '0'; while ((signed char)ch-- > 0) ; /* make expon. really large to avoid * generating extra dots */ expon = LARGE_EXP; } while ((ch = *sub++) && ch != trail_ch) { /* Convert digits loop */ /* adjust dcm. point */ if ((expon -= 2) <= 0) { if (0 != expon) { *targ++ = '.'; expon = LARGE_EXP; PUT_USHORT(targ, tbl_ptr[ch]); targ += sizeof(short); } else { /* Insert dot between digits */ PUT_USHORT(targ, tbl_ptr[ch]); targ += sizeof(short); *targ = *(targ - 1); *(targ - 1) = '.'; targ++; expon = LARGE_EXP; } } else { PUT_USHORT(targ, tbl_ptr[ch]); targ += sizeof(short); } } if (expon > (LARGE_EXP - 100)) { if ('0' == *(targ - 1)) targ--; if ('.' == *(targ - 1)) targ--; } else while (--expon > 0) *targ++ = '0'; } } return (targ); }
void op_fnorder(lv_val *src, mval *key, mval *dst) { mval tmp_sbs; int length; boolean_t is_canonical, is_fnnext, get_first; lvTree *lvt; lvTreeNode *node; uint4 mvt; /* Local copy of mvtype, bit ands use a int4, so do conversion once */ mstr *str; int4 intval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; is_fnnext = TREF(in_op_fnnext); TREF(in_op_fnnext) = FALSE; if (src && (lvt = LV_GET_CHILD(src))) /* caution: assignment */ { MV_FORCE_DEFINED(key); /* If last subscript is null, $ORDER returns the first subscript in that level. * With the obsoleted $NEXT function, a subscript of -1 also triggers the same behavior. */ get_first = FALSE; if (MV_IS_STRING(key) && (0 == key->str.len)) get_first = TRUE; else if (is_fnnext) { mvt = key->mvtype; if (!(mvt & (MV_NM | MV_NUM_APPROX))) { /* Not currently in numeric form. Is it cannonical? */ if (val_iscan(key)) { /* Yes, convert it to numeric */ (void)s2n(key); mvt = key->mvtype; if (!(mvt & MV_NM)) rts_error(VARLSTCNT(1) ERR_NUMOFLOW); } else /* No, not numeric. Note the fact for future reference */ mvt = key->mvtype |= MV_NUM_APPROX; } if (MV_IS_TRUEINT(key, &intval) && (MINUS_ONE == key->m[1])) get_first = TRUE; } if (get_first) node = lvAvlTreeFirst(lvt); else { is_canonical = MV_IS_CANONICAL(key); if (!is_canonical) { assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); if (TREF(local_collseq)) { ALLOC_XFORM_BUFF(key->str.len); tmp_sbs.mvtype = MV_STR; tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); do_xform(TREF(local_collseq), XFORM, &key->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); key = &tmp_sbs; } } else { /* Need to set canonical bit before calling tree search functions. * But input mval could be read-only so cannot modify that even if temporarily. * So take a copy of the mval and modify that instead. */ tmp_sbs = *key; key = &tmp_sbs; MV_FORCE_NUM(key); TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(key); /* used by the lvTreeKeyNext function */ } node = lvAvlTreeKeyNext(lvt, key); } /* If STDNULLCOLL, skip to the next subscript should the current subscript be "" */ if (TREF(local_collseq_stdnull) && (NULL != node) && LV_NODE_KEY_IS_NULL_SUBS(node)) { assert(LVNULLSUBS_OK == TREF(lv_null_subs)); node = lvAvlTreeNext(node); } } else node = NULL; if (NULL == node) { if (!is_fnnext) { dst->mvtype = MV_STR; dst->str.len = 0; } else MV_FORCE_MVAL(dst, -1); } else { LV_NODE_GET_KEY(node, dst); /* Get node key into "dst" depending on the structure type of "node" */ /* Code outside lv_tree.c does not currently know to make use of MV_CANONICAL bit so reset it * until the entire codebase gets fixed to maintain MV_CANONICAL bit accurately at which point, * this RESET can be removed */ TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(dst); if (TREF(local_collseq) && MV_IS_STRING(dst)) { ALLOC_XFORM_BUFF(dst->str.len); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); do_xform(TREF(local_collseq), XBACK, &dst->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); dst->str = tmp_sbs.str; } } }
void op_fnorder(lv_val *src, mval *key, mval *dst) { int cur_subscr; mval tmp_sbs; int length; sbs_blk *num, *str; boolean_t found, is_neg; int4 i; lv_val **lv; lv_sbs_tbl *tbl; sbs_search_status status; boolean_t is_fnnext; is_fnnext = in_op_fnnext; in_op_fnnext = FALSE; found = FALSE; if (src) { if (tbl = src->ptrs.val_ent.children) { MV_FORCE_DEFINED(key); num = tbl->num; str = tbl->str; assert(tbl->ident == MV_SBS); if ((MV_IS_STRING(key) && key->str.len == 0) || (is_fnnext && MV_IS_INT(key) && key->m[1] == MINUS_ONE)) { /* With GT.M collation , if last subscript is null, $o returns the first subscript in that level */ if (tbl->int_flag) { assert(num); for (i = 0, lv = &num->ptr.lv[0]; i < SBS_NUM_INT_ELE; i++, lv++) { if (*lv) { MV_FORCE_MVAL(dst,i); found = TRUE; break; } } } else if (num) { assert(num->cnt); MV_ASGN_FLT2MVAL((*dst),num->ptr.sbs_flt[0].flt); found = TRUE; } } else { if (MV_IS_CANONICAL(key)) { MV_FORCE_NUM(key); if (tbl->int_flag) { assert(num); is_neg = (key->mvtype & MV_INT) ? key->m[1] < 0 : key->sgn; if (is_neg) i = 0; else { if (!is_fnnext && (1 == numcmp(key, (mval *)&SBS_MVAL_INT_ELE))) i = SBS_NUM_INT_ELE; else { i = MV_FORCE_INT(key); i++; } } for (lv = &num->ptr.lv[i]; i < SBS_NUM_INT_ELE; i++, lv++) { if (*lv) { MV_FORCE_MVAL(dst,i); found = TRUE; break; } } } else if (num && lv_nxt_num_inx(num, key, &status)) { MV_ASGN_FLT2MVAL((*dst),((sbs_flt_struct*)status.ptr)->flt); found = TRUE; } } else { if (local_collseq) { ALLOC_XFORM_BUFF(&key->str); tmp_sbs.mvtype = MV_STR; tmp_sbs.str.len = max_lcl_coll_xform_bufsiz; assert(NULL != lcl_coll_xform_buff); tmp_sbs.str.addr = lcl_coll_xform_buff; do_xform(local_collseq, XFORM, &key->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); key = &tmp_sbs; } if (str && lv_nxt_str_inx(str, &key->str, &status)) { dst->mvtype = MV_STR; dst->str = ((sbs_str_struct *)status.ptr)->str; } else { if (!is_fnnext) { dst->mvtype = MV_STR; dst->str.len = 0; } else MV_FORCE_MVAL(dst, -1); } found = TRUE; } } if (!found && str) { /* We are here because * a. key is "" and there is no numeric subscript, OR * b. key is numeric and it is >= the largest numeric subscript at this level implying a switch from * numeric to string subscripts * Either case, return the first string subscript. However, for STDNULLCOLL, skip to the next * subscript should the first subscript be "" */ assert(str->cnt); dst->mvtype = MV_STR; dst->str = str->ptr.sbs_str[0].str; found = TRUE; if (local_collseq_stdnull && 0 == dst->str.len) { assert(lv_null_subs); if (lv_nxt_str_inx(str, &dst->str, &status)) { dst->str = ((sbs_str_struct*)status.ptr)->str; } else found = FALSE; } } } } if (!found) { if (!is_fnnext) { dst->mvtype = MV_STR; dst->str.len = 0; } else MV_FORCE_MVAL(dst, -1); } else if (dst->mvtype == MV_STR && local_collseq) { ALLOC_XFORM_BUFF(&dst->str); assert(NULL != lcl_coll_xform_buff); tmp_sbs.str.addr = lcl_coll_xform_buff; tmp_sbs.str.len = max_lcl_coll_xform_bufsiz; do_xform(local_collseq, XBACK, &dst->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); dst->str = tmp_sbs.str; } }
void op_fnzprevious(lv_val *src, mval *key, mval *dst) { int cur_subscr, length; mval tmp_sbs; lvTreeNode *node; lvTree *lvt; boolean_t is_canonical, get_last; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (src && (lvt = LV_GET_CHILD(src))) /* caution: assignment */ { MV_FORCE_DEFINED(key); /* If last subscript is null, $zprev returns the last subscript in that level. */ get_last = FALSE; if (MV_IS_STRING(key) && (0 == key->str.len)) get_last = TRUE; if (get_last) node = lvAvlTreeLast(lvt); else { is_canonical = MV_IS_CANONICAL(key); if (!is_canonical) { assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); if (TREF(local_collseq)) { ALLOC_XFORM_BUFF(key->str.len); tmp_sbs.mvtype = MV_STR; tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); do_xform(TREF(local_collseq), XFORM, &key->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); key = &tmp_sbs; } } else { /* Need to set canonical bit before calling tree search functions. * But input mval could be read-only so cannot modify that even if temporarily. * So take a copy of the mval and modify that instead. */ tmp_sbs = *key; key = &tmp_sbs; MV_FORCE_NUM(key); TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(key); /* used by the lvAvlTreeKeyPrev function */ } node = lvAvlTreeKeyPrev(lvt, key); } /* If STDNULLCOLL, skip to the previous subscript should the current subscript be "" */ if (TREF(local_collseq_stdnull) && (NULL != node) && LV_NODE_KEY_IS_NULL_SUBS(node)) { assert(LVNULLSUBS_OK == TREF(lv_null_subs)); node = lvAvlTreePrev(node); } } else node = NULL; if (NULL == node) { dst->mvtype = MV_STR; dst->str.len = 0; } else { LV_NODE_GET_KEY(node, dst); /* Get node key into "dst" depending on the structure type of "node" */ /* Code outside lv_tree.c does not currently know to make use of MV_CANONICAL bit so reset it * until the entire codebase gets fixed to maintain MV_CANONICAL bit accurately at which point, * this RESET can be removed */ TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(dst); if (TREF(local_collseq) && MV_IS_STRING(dst)) { ALLOC_XFORM_BUFF(dst->str.len); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); do_xform(TREF(local_collseq), XBACK, &dst->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); dst->str = tmp_sbs.str; } } return; }
unsigned char *mval2subsc(mval *in_val, gv_key *out_key, boolean_t std_null_coll) { boolean_t is_negative; unsigned char buf1[MAX_KEY_SZ + 1], ch, *cvt_table, *in_ptr, *out_ptr; unsigned char *tm, temp_mantissa[NUM_DEC_DG_2L / 2 + 3]; /* Need 1 byte for each two digits. Add 3 bytes slop */ mstr mstr_ch, mstr_buf1; int4 mt, mw, mx; uint4 mvt; /* Local copy of mvtype, bit ands use a int4, so do conversion once */ unsigned int digs, exp_val; int tmp_len, avail_bytes; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* The below assert is an attempt to catch between some and many of the cases where the mvtype is * not an accurate representation of the content. This can happen (and has) when there is a bug in * op_svput() where only the string portion of an mval is set without (re)setting the type leading * to numeric or num-approx values that do not represent reality and causing trouble especially with * subscripts. In that example, "-1" was left as a string with MV_NUM_APPROX on which caused a lot * of trouble with $ORDER in a database when -1 was treated as a string. This assert is not a 100% * catchall of invalid settings but it provides at least some barrier. A full barrier would require * complete conversion which is a bit expensive to always re-do at this point - even in a dbg version. * There is an exception though and that is if the caller is op_fnview. In that case, it could set * MV_NUM_APPROX to indicate a number needs to be treated as a string subscript. Skip that in the assert. */ assert(!(MV_NUM_APPROX & in_val->mvtype) || (NUM_DEC_DG_2L < in_val->str.len) || !val_iscan(in_val) || TREF(skip_mv_num_approx_assert)); out_ptr = out_key->base + out_key->end; if (TREF(transform) && gv_target->nct) { MV_FORCE_STR(in_val); mvt = in_val->mvtype | MV_NUM_APPROX; } else mvt = (uint4)in_val->mvtype; if (!(mvt & (MV_NM | MV_NUM_APPROX))) { /* Not currently in numeric form. Is it cannonical? */ if (val_iscan(in_val)) { /* Yes, convert it to numeric */ (void)s2n(in_val); mvt = in_val->mvtype; assert(mvt & MV_NM); } else { /* No, not numeric. Note the fact for future reference */ mvt = in_val->mvtype |= MV_NUM_APPROX; } } if (mvt & MV_NUM_APPROX) { /* It's a string */ in_ptr = (unsigned char *)in_val->str.addr; tmp_len = in_val->str.len; if (TREF(transform) && gv_target->collseq) { mstr_ch.len = tmp_len; mstr_ch.addr = (char *)in_ptr; mstr_buf1.len = SIZEOF(buf1); mstr_buf1.addr = (char *)buf1; do_xform(gv_target->collseq, XFORM, &mstr_ch, &mstr_buf1, &tmp_len); in_ptr = (unsigned char*)mstr_buf1.addr; /* mstr_buf1.addr is used just in case it is reallocated by the XFORM routine */ } /* Find out how much space is needed at a minimum to store the subscript representation of this string. * That would be STR_SUB_PREFIX + input string + at most TWO KEY_DELIMITER. * Assuming this, compute how much space would still be available in the out_key before reaching the top. * If this is negative, we have to signal a GVSUBOFLOW error. * If this is positive and the input string contains 0x00 or 0x01, we would need additional bytes to * store the STR_SUB_ESCAPE byte. Decrement the available space until it becomes zero * at which point issue a GVSUBOFLOW error as well. */ avail_bytes = out_key->top - (out_key->end + tmp_len + 3); if (0 > avail_bytes) ISSUE_GVSUBOFLOW_ERROR(out_key, KEY_COMPLETE_FALSE); if (0 < tmp_len) { *out_ptr++ = STR_SUB_PREFIX; do { ch = *in_ptr++; if (ch <= 1) { *out_ptr++ = STR_SUB_ESCAPE; if (0 > --avail_bytes) { /* Ensure input key to format_targ_key is double null terminated */ assert(STR_SUB_PREFIX == out_key->base[out_key->end]); out_key->base[out_key->end] = KEY_DELIMITER; ISSUE_GVSUBOFLOW_ERROR(out_key, KEY_COMPLETE_FALSE); } ch++; /* promote character */ } *out_ptr++ = ch; } while (--tmp_len > 0); } else *out_ptr++ = (0 == std_null_coll) ? STR_SUB_PREFIX : SUBSCRIPT_STDCOL_NULL; goto ALLDONE; } /* Its a number, is it an integer? But before this assert that we have enough allocated space in the key * to store the maximum possible numeric subscript and two terminating 0s at the end of the key */ assert((MAX_GVKEY_PADDING_LEN + 1) <= (int)(out_key->top - out_key->end)); if (mvt & MV_INT) { /* Yes, its an integer, convert it */ is_negative = FALSE; cvt_table = pos_code; if (0 > (mt = in_val->m[1])) { is_negative = TRUE; cvt_table = neg_code; mt = -mt; } else if (0 == mt) { *out_ptr++ = SUBSCRIPT_ZERO; goto ALLDONE; } if (10 > mt) { *out_ptr++ = is_negative ? ~(SUBSCRIPT_BIAS - 2) : (SUBSCRIPT_BIAS - 2); *out_ptr++ = cvt_table[mt * 10]; goto FINISH_NUMBER; } if (100 > mt) { *out_ptr++ = is_negative ? ~(SUBSCRIPT_BIAS - 1) : (SUBSCRIPT_BIAS - 1); *out_ptr++ = cvt_table[mt]; goto FINISH_NUMBER; } tm = temp_mantissa; if (1000 > mt) { exp_val = SUBSCRIPT_BIAS; goto ODD_INTEGER; } if (10000 > mt) { exp_val = SUBSCRIPT_BIAS + 1; goto EVEN_INTEGER; } if (100000 > mt) { exp_val = SUBSCRIPT_BIAS + 2; goto ODD_INTEGER; } if (1000000 > mt) { exp_val = SUBSCRIPT_BIAS + 3; goto EVEN_INTEGER; } if (10000000 > mt) { exp_val = SUBSCRIPT_BIAS + 4; goto ODD_INTEGER; } if (100000000 > mt) { exp_val = SUBSCRIPT_BIAS + 5; goto EVEN_INTEGER; } exp_val = SUBSCRIPT_BIAS + 6; ODD_INTEGER: *out_ptr++ = is_negative ? ~(exp_val) : (exp_val); mw = mx = mt / 10; mw *= 10; mw = mt - mw; mt = mx; if (mw) { *tm++ = cvt_table[mw * 10]; goto FINISH_INTEGERS; } goto KEEP_STRIPING; EVEN_INTEGER: *out_ptr++ = is_negative ? ~(exp_val) : (exp_val); KEEP_STRIPING: while (mt) { mw = mx = mt / 100; mw *= 100; mw = mt - mw; mt = mx; if (mw) { *tm++ = cvt_table[mw]; break; } } FINISH_INTEGERS: while (mt) { mw = mx = mt / 100; mw *= 100; mw = mt - mw; *tm++ = cvt_table[mw]; mt = mx; } while (tm > temp_mantissa) *out_ptr++ = *--tm; goto FINISH_NUMBER; } /* Convert 18 digit number */ cvt_table = pos_code; if (0 != (is_negative = in_val->sgn)) cvt_table = neg_code; *out_ptr++ = is_negative ? ~(in_val->e - MV_XBIAS + SUBSCRIPT_BIAS) : (in_val->e - MV_XBIAS + SUBSCRIPT_BIAS); mt = in_val->m[1]; mw = in_val->m[0]; /* Strip top two digits */ mx = mt / (MANT_HI / 100); *out_ptr++ = cvt_table[mx]; mt = (mt - (mx * (MANT_HI / 100))) * 100; /* * The two msd's have now been converted. The maximum number of * data remaining is 7 digits in "mt" and 9 digits in "mw". * If mw is zero, then we should just grind out mt till we are done */ if (0 == mw) goto LAST_LONGWORD; /* there are more than 7 digits left. First, we will put 8 digits in mt, (leaving 8 digits in mw) */ mx = mw / (MANT_HI / 10); mt += mx * 10; mw = (mw - (mx * (MANT_HI / 10))) * 10; if (0 == mw) goto LAST_LONGWORD; for (digs = 0; digs < 4; digs++) { mx = mt / (MANT_HI / 100); *out_ptr++ = cvt_table[mx]; mt = (mt - (mx * (MANT_HI / 100))) * 100; } mt = mw; LAST_LONGWORD: while (mt) { mx = mt / (MANT_HI / 100); *out_ptr++ = cvt_table[mx]; mt = (mt - (mx * (MANT_HI / 100))) * 100; } FINISH_NUMBER: if (is_negative) *out_ptr++ = NEG_MNTSSA_END; ALLDONE: *out_ptr++ = KEY_DELIMITER; *out_ptr = KEY_DELIMITER; out_key->prev = out_key->end; out_key->end = out_ptr - out_key->base; /* Check if after adding the current subscript and the second terminating NULL byte, there is still * MAX_GVKEY_PADDING_LEN bytes (allocated additionally as part of the DBKEYSIZE macro) left at the end. * If not, we have overflown the original max-key-size length. Issue error. */ if ((MAX_GVKEY_PADDING_LEN + 1) > (int)(out_key->top - out_key->end)) ISSUE_GVSUBOFLOW_ERROR(out_key, KEY_COMPLETE_FALSE); return out_ptr; }