// // PD_Tuple: C // // Implements PATH and SET_PATH for tuple. // Sets DS_TOP if found. Always returns 0. // REBINT PD_Tuple(REBPVS *pvs) { const REBVAL *setval; REBINT n; REBINT i; REBYTE *dat; REBINT len; dat = VAL_TUPLE(pvs->value); len = VAL_TUPLE_LEN(pvs->value); if (len < 3) { len = 3; } n = Get_Num_From_Arg(pvs->selector); if ((setval = pvs->opt_setval)) { if (n <= 0 || n > cast(REBINT, MAX_TUPLE)) fail (Error_Bad_Path_Select(pvs)); if (IS_INTEGER(setval) || IS_DECIMAL(setval)) i = Int32(setval); else if (IS_BLANK(setval)) { n--; CLEAR(dat + n, MAX_TUPLE - n); VAL_TUPLE_LEN(pvs->value) = n; return PE_OK; } else fail (Error_Bad_Path_Set(pvs)); if (i < 0) i = 0; else if (i > 255) i = 255; dat[n - 1] = i; if (n > len) VAL_TUPLE_LEN(pvs->value) = n; return PE_OK; } else { if (n > 0 && n <= len) { SET_INTEGER(pvs->store, dat[n - 1]); return PE_USE_STORE; } else return PE_NONE; } }
// // Poke_Tuple_Immediate: C // // !!! Note: In the current implementation, tuples are immediate values. // So a POKE only changes the `value` in your hand. // void Poke_Tuple_Immediate( REBVAL *value, const REBVAL *picker, const REBVAL *poke ) { REBYTE *dat = VAL_TUPLE(value); REBINT len = VAL_TUPLE_LEN(value); if (len < 3) len = 3; REBINT n = Get_Num_From_Arg(picker); if (n <= 0 || n > cast(REBINT, MAX_TUPLE)) fail (Error_Out_Of_Range(picker)); REBINT i; if (IS_INTEGER(poke) || IS_DECIMAL(poke)) i = Int32(poke); else if (IS_BLANK(poke)) { n--; CLEAR(dat + n, MAX_TUPLE - n); VAL_TUPLE_LEN(value) = n; return; } else fail (poke); if (i < 0) i = 0; else if (i > 255) i = 255; dat[n - 1] = i; if (n > len) VAL_TUPLE_LEN(value) = n; }
// // Pick_Tuple: C // void Pick_Tuple(REBVAL *out, const REBVAL *value, const REBVAL *picker) { const REBYTE *dat = VAL_TUPLE(value); REBINT len = VAL_TUPLE_LEN(value); if (len < 3) len = 3; REBINT n = Get_Num_From_Arg(picker); // This uses modulus to avoid having a conditional access into the array, // which would trigger Spectre mitigation: // // https://stackoverflow.com/questions/50399940/ // // By always accessing the array and always being in bounds, there's no // speculative execution accessing unbound locations. // REBYTE byte = dat[(n - 1) % len]; if (n > 0 and n <= len) Init_Integer(out, byte); else Init_Nulled(out); }
// // Series_Common_Action_Returns: C // // This routine is called to handle actions on ANY-SERIES! that can be taken // care of without knowing what specific kind of series it is. So generally // index manipulation, and things like LENGTH/etc. // // The strange name is to convey the result in an if statement, in the same // spirit as the `if (XXX_Throws(...)) { /* handle throw */ }` pattern. // REBOOL Series_Common_Action_Returns( REB_R *r, // `r_out` would be slightly confusing, considering R_OUT REBFRM *frame_, REBSYM action ) { REBVAL *value = D_ARG(1); REBVAL *arg = D_ARGC > 1 ? D_ARG(2) : NULL; REBINT index = cast(REBINT, VAL_INDEX(value)); REBINT tail = cast(REBINT, VAL_LEN_HEAD(value)); REBINT len = 0; switch (action) { //-- Navigation: case SYM_HEAD: VAL_INDEX(value) = 0; break; case SYM_TAIL: VAL_INDEX(value) = (REBCNT)tail; break; case SYM_HEAD_Q: *r = (index == 0) ? R_TRUE : R_FALSE; return TRUE; // handled case SYM_TAIL_Q: *r = (index >= tail) ? R_TRUE : R_FALSE; return TRUE; // handled case SYM_PAST_Q: *r = (index > tail) ? R_TRUE : R_FALSE; return TRUE; // handled case SYM_NEXT: if (index < tail) VAL_INDEX(value)++; break; case SYM_BACK: if (index > 0) VAL_INDEX(value)--; break; case SYM_SKIP: case SYM_AT: len = Get_Num_From_Arg(arg); { REBI64 i = (REBI64)index + (REBI64)len; if (action == SYM_SKIP) { if (IS_LOGIC(arg)) i--; } else { // A_AT if (len > 0) i--; } if (i > (REBI64)tail) i = (REBI64)tail; else if (i < 0) i = 0; VAL_INDEX(value) = (REBCNT)i; } break; case SYM_INDEX_OF: SET_INTEGER(D_OUT, cast(REBI64, index) + 1); *r = R_OUT; return TRUE; // handled case SYM_LENGTH: SET_INTEGER(D_OUT, tail > index ? tail - index : 0); *r = R_OUT; return TRUE; // handled case SYM_REMOVE: // /PART length FAIL_IF_LOCKED_SERIES(VAL_SERIES(value)); len = D_REF(2) ? Partial(value, 0, D_ARG(3)) : 1; index = cast(REBINT, VAL_INDEX(value)); if (index < tail && len != 0) Remove_Series(VAL_SERIES(value), VAL_INDEX(value), len); break; case SYM_ADD: // Join_Strings(value, arg); case SYM_SUBTRACT: // "test this" - 10 case SYM_MULTIPLY: // "t" * 4 = "tttt" case SYM_DIVIDE: case SYM_REMAINDER: case SYM_POWER: case SYM_ODD_Q: case SYM_EVEN_Q: case SYM_ABSOLUTE: fail (Error_Illegal_Action(VAL_TYPE(value), action)); default: return FALSE; // not a common operation, not handled } *D_OUT = *value; *r = R_OUT; return TRUE; // handled }