/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2006-2017. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
/*
* This file implements the former GC BIFs. They used to do a GC when
* they needed heap space. Because of changes to the implementation of
* literals, those BIFs are now allowed to allocate heap fragments
* (using HeapFragOnlyAlloc()). Note that they must NOT call HAlloc(),
* because the caller does not do any SWAPIN / SWAPOUT (that is,
* HEAP_TOP(p) and HEAP_LIMIT(p) contain stale values).
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sys.h"
#include "erl_vm.h"
#include "global.h"
#include "erl_process.h"
#include "error.h"
#include "bif.h"
#include "big.h"
#include "erl_binary.h"
#include "erl_map.h"
static Eterm double_to_integer(Process* p, double x);
static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3);
static Export erlang_length_export;
void erts_init_bif_guard(void)
{
erts_init_trap_export(&erlang_length_export,
am_erlang, am_length, 3,
&erlang_length_trap);
}
BIF_RETTYPE abs_1(BIF_ALIST_1)
{
Eterm res;
Sint i0, i;
Eterm* hp;
/* integer arguments */
if (is_small(BIF_ARG_1)) {
i0 = signed_val(BIF_ARG_1);
i = ERTS_SMALL_ABS(i0);
if (i0 == MIN_SMALL) {
hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE);
BIF_RET(uint_to_big(i, hp));
} else {
BIF_RET(make_small(i));
}
} else if (is_big(BIF_ARG_1)) {
if (!big_sign(BIF_ARG_1)) {
BIF_RET(BIF_ARG_1);
} else {
int sz = big_arity(BIF_ARG_1) + 1;
Uint* x;
hp = HeapFragOnlyAlloc(BIF_P, sz); /* See note at beginning of file */
sz--;
res = make_big(hp);
x = big_val(BIF_ARG_1);
*hp++ = make_pos_bignum_header(sz);
x++; /* skip thing */
while(sz--)
*hp++ = *x++;
BIF_RET(res);
}
} else if (is_float(BIF_ARG_1)) {
FloatDef f;
GET_DOUBLE(BIF_ARG_1, f);
if (f.fd < 0.0) {
hp = HeapFragOnlyAlloc(BIF_P, FLOAT_SIZE_OBJECT);
f.fd = fabs(f.fd);
res = make_float(hp);
PUT_DOUBLE(f, hp);
BIF_RET(res);
}
else
BIF_RET(BIF_ARG_1);
}
BIF_ERROR(BIF_P, BADARG);
}
BIF_RETTYPE float_1(BIF_ALIST_1)
{
Eterm res;
Eterm* hp;
FloatDef f;
/* check args */
if (is_not_integer(BIF_ARG_1)) {
if (is_float(BIF_ARG_1)) {
BIF_RET(BIF_ARG_1);
} else {
badarg:
BIF_ERROR(BIF_P, BADARG);
}
}
if (is_small(BIF_ARG_1)) {
Sint i = signed_val(BIF_ARG_1);
f.fd = i; /* use "C"'s auto casting */
} else if (big_to_double(BIF_ARG_1, &f.fd) < 0) {
goto badarg;
}
hp = HeapFragOnlyAlloc(BIF_P, FLOAT_SIZE_OBJECT);
res = make_float(hp);
PUT_DOUBLE(f, hp);
BIF_RET(res);
}
BIF_RETTYPE trunc_1(BIF_ALIST_1)
{
Eterm res;
FloatDef f;
/* check arg */
if (is_not_float(BIF_ARG_1)) {
if (is_integer(BIF_ARG_1))
BIF_RET(BIF_ARG_1);
BIF_ERROR(BIF_P, BADARG);
}
/* get the float */
GET_DOUBLE(BIF_ARG_1, f);
/* truncate it and return the resultant integer */
res = double_to_integer(BIF_P, (f.fd >= 0.0) ? floor(f.fd) : ceil(f.fd));
BIF_RET(res);
}
BIF_RETTYPE floor_1(BIF_ALIST_1)
{
Eterm res;
FloatDef f;
if (is_not_float(BIF_ARG_1)) {
if (is_integer(BIF_ARG_1))
BIF_RET(BIF_ARG_1);
BIF_ERROR(BIF_P, BADARG);
}
GET_DOUBLE(BIF_ARG_1, f);
res = double_to_integer(BIF_P, floor(f.fd));
BIF_RET(res);
}
BIF_RETTYPE ceil_1(BIF_ALIST_1)
{
Eterm res;
FloatDef f;
/* check arg */
if (is_not_float(BIF_ARG_1)) {
if (is_integer(BIF_ARG_1))
BIF_RET(BIF_ARG_1);
BIF_ERROR(BIF_P, BADARG);
}
/* get the float */
GET_DOUBLE(BIF_ARG_1, f);
res = double_to_integer(BIF_P, ceil(f.fd));
BIF_RET(res);
}
BIF_RETTYPE round_1(BIF_ALIST_1)
{
Eterm res;
FloatDef f;
/* check arg */
if (is_not_float(BIF_ARG_1)) {
if (is_integer(BIF_ARG_1))
BIF_RET(BIF_ARG_1);
BIF_ERROR(BIF_P, BADARG);
}
/* get the float */
GET_DOUBLE(BIF_ARG_1, f);
/* round it and return the resultant integer */
res = double_to_integer(BIF_P, round(f.fd));
BIF_RET(res);
}
/*
* This version of length/1 is called from native code and apply/3.
*/
BIF_RETTYPE length_1(BIF_ALIST_1)
{
Eterm args[3];
/*
* Arrange argument registers the way expected by
* erts_trapping_length_1(). We save the original argument in
* args[2] in case an error should signaled.
*/
args[0] = BIF_ARG_1;
args[1] = make_small(0);
args[2] = BIF_ARG_1;
return erlang_length_trap(BIF_P, args, A__I);
}
static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3)
{
Eterm res;
res = erts_trapping_length_1(BIF_P, BIF__ARGS);
if (is_value(res)) { /* Success. */
BIF_RET(res);
} else { /* Trap or error. */
if (BIF_P->freason == TRAP) {
/*
* The available reductions were exceeded. Trap.
*/
BIF_TRAP3(&erlang_length_export, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
} else {
/*
* Signal an error. The original argument was tucked away in BIF_ARG_3.
*/
ERTS_BIF_ERROR_TRAPPED1(BIF_P, BIF_P->freason,
bif_export[BIF_length_1], BIF_ARG_3);
}
}
}
/*
* Trappable helper function for calculating length/1.
*
* When calling this function, entries in args[] should be set up as
* follows:
*
* args[0] = List to calculate length for.
* args[1] = Length accumulator (tagged integer).
*
* If the return value is a tagged integer, the length was calculated
* successfully.
*
* Otherwise, if return value is THE_NON_VALUE and p->freason is TRAP,
* the available reductions were exceeded and this function must be called
* again after rescheduling. args[0] and args[1] have been updated to
* contain the next part of the list and length so far, respectively.
*
* Otherwise, if return value is THE_NON_VALUE, the list did not end
* in an empty list (and p->freason is BADARG).
*/
Eterm erts_trapping_length_1(Process* p, Eterm* args)
{
Eterm list;
Uint i;
Uint max_iter;
Uint saved_max_iter;
#if defined(DEBUG) || defined(VALGRIND)
max_iter = 50;
#else
max_iter = ERTS_BIF_REDS_LEFT(p) * 16;
#endif
saved_max_iter = max_iter;
ASSERT(max_iter > 0);
list = args[0];
i = unsigned_val(args[1]);
while (is_list(list) && max_iter != 0) {
list = CDR(list_val(list));
i++, max_iter--;
}
if (is_list(list)) {
/*
* We have exceeded the alloted number of iterations.
* Save the result so far and signal a trap.
*/
args[0] = list;
args[1] = make_small(i);
p->freason = TRAP;
BUMP_ALL_REDS(p);
return THE_NON_VALUE;
} else if (is_not_nil(list)) {
/* Error. Should be NIL. */
BIF_ERROR(p, BADARG);
}
/*
* We reached the end of the list successfully. Bump reductions
* and return result.
*/
BUMP_REDS(p, (saved_max_iter - max_iter) / 16);
return make_small(i);
}
/* returns the size of a tuple or a binary */
BIF_RETTYPE size_1(BIF_ALIST_1)
{
if (is_tuple(BIF_ARG_1)) {
Eterm* tupleptr = tuple_val(BIF_ARG_1);
BIF_RET(make_small(arityval(*tupleptr)));
} else if (is_binary(BIF_ARG_1)) {
Uint sz = binary_size(BIF_ARG_1);
if (IS_USMALL(0, sz)) {
return make_small(sz);
} else {
Eterm* hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE);
BIF_RET(uint_to_big(sz, hp));
}
}
BIF_ERROR(BIF_P, BADARG);
}
/**********************************************************************/
/* returns the bitsize of a bitstring */
BIF_RETTYPE bit_size_1(BIF_ALIST_1)
{
Uint low_bits;
Uint bytesize;
Uint high_bits;
if (is_binary(BIF_ARG_1)) {
bytesize = binary_size(BIF_ARG_1);
high_bits = bytesize >> ((sizeof(Uint) * 8)-3);
low_bits = (bytesize << 3) + binary_bitsize(BIF_ARG_1);
if (high_bits == 0) {
if (IS_USMALL(0,low_bits)) {
BIF_RET(make_small(low_bits));
} else {
Eterm* hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE);
BIF_RET(uint_to_big(low_bits, hp));
}
} else {
Uint sz = BIG_UINT_HEAP_SIZE+1;
Eterm* hp = HeapFragOnlyAlloc(BIF_P, sz);
hp[0] = make_pos_bignum_header(sz-1);
BIG_DIGIT(hp,0) = low_bits;
BIG_DIGIT(hp,1) = high_bits;
BIF_RET(make_big(hp));
}
} else {
BIF_ERROR(BIF_P, BADARG);
}
}
/**********************************************************************/
/* returns the number of bytes need to store a bitstring */
BIF_RETTYPE byte_size_1(BIF_ALIST_1)
{
if (is_binary(BIF_ARG_1)) {
Uint bytesize = binary_size(BIF_ARG_1);
if (binary_bitsize(BIF_ARG_1) > 0) {
bytesize++;
}
if (IS_USMALL(0, bytesize)) {
BIF_RET(make_small(bytesize));
} else {
Eterm* hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE);
BIF_RET(uint_to_big(bytesize, hp));
}
} else {
BIF_ERROR(BIF_P, BADARG);
}
}
/*
* Generate the integer part from a double.
*/
static Eterm
double_to_integer(Process* p, double x)
{
int is_negative;
int ds;
ErtsDigit* xp;
int i;
Eterm res;
size_t sz;
Eterm* hp;
double dbase;
if ((x < (double) (MAX_SMALL+1)) && (x > (double) (MIN_SMALL-1))) {
Sint xi = x;
return make_small(xi);
}
if (x >= 0) {
is_negative = 0;
} else {
is_negative = 1;
x = -x;
}
/* Unscale & (calculate exponent) */
ds = 0;
dbase = ((double)(D_MASK)+1);
while(x >= 1.0) {
x /= dbase; /* "shift" right */
ds++;
}
sz = BIG_NEED_SIZE(ds); /* number of words including arity */
hp = HeapFragOnlyAlloc(p, sz);
res = make_big(hp);
xp = (ErtsDigit*) (hp + 1);
for (i = ds-1; i >= 0; i--) {
ErtsDigit d;
x *= dbase; /* "shift" left */
d = x; /* trunc */
xp[i] = d; /* store digit */
x -= d; /* remove integer part */
}
while ((ds & (BIG_DIGITS_PER_WORD-1)) != 0) {
xp[ds++] = 0;
}
if (is_negative) {
*hp = make_neg_bignum_header(sz-1);
} else {
*hp = make_pos_bignum_header(sz-1);
}
return res;
}
/********************************************************************************
* binary_part guards. The actual implementation is in erl_bif_binary.c
********************************************************************************/
BIF_RETTYPE binary_part_3(BIF_ALIST_3)
{
return erts_binary_part(BIF_P,BIF_ARG_1,BIF_ARG_2, BIF_ARG_3);
}
BIF_RETTYPE binary_part_2(BIF_ALIST_2)
{
Eterm *tp;
if (is_not_tuple(BIF_ARG_2)) {
goto badarg;
}
tp = tuple_val(BIF_ARG_2);
if (arityval(*tp) != 2) {
goto badarg;
}
return erts_binary_part(BIF_P,BIF_ARG_1,tp[1], tp[2]);
badarg:
BIF_ERROR(BIF_P,BADARG);
}