2020-05-04 02:52:56 -04:00
|
|
|
#ifndef RBIMPL_ARITHMETIC_LONG_H /*-*-C++-*-vi:se ft=cpp:*/
|
|
|
|
#define RBIMPL_ARITHMETIC_LONG_H
|
2020-04-10 01:11:40 -04:00
|
|
|
/**
|
2020-04-08 00:28:13 -04:00
|
|
|
* @file
|
|
|
|
* @author Ruby developers <ruby-core@ruby-lang.org>
|
|
|
|
* @copyright This file is a part of the programming language Ruby.
|
|
|
|
* Permission is hereby granted, to either redistribute and/or
|
|
|
|
* modify this file, provided that the conditions mentioned in the
|
|
|
|
* file COPYING are met. Consult the file for details.
|
2020-05-04 02:52:56 -04:00
|
|
|
* @warning Symbols prefixed with either `RBIMPL` or `ruby3` are
|
2020-04-08 00:28:13 -04:00
|
|
|
* implementation details. Don't take them as canon. They could
|
|
|
|
* rapidly appear then vanish. The name (path) of this header file
|
|
|
|
* is also an implementation detail. Do not expect it to persist
|
|
|
|
* at the place it is now. Developers are free to move it anywhere
|
|
|
|
* anytime at will.
|
|
|
|
* @note To ruby-core: remember that this header can be possibly
|
|
|
|
* recursively included from extension libraries written in C++.
|
|
|
|
* Do not expect for instance `__VA_ARGS__` is always available.
|
|
|
|
* We assume C99 for ruby itself but we don't assume languages of
|
|
|
|
* extension libraries. They could be written in C++98.
|
|
|
|
* @brief Arithmetic conversion between C's `long` and Ruby's.
|
|
|
|
*
|
|
|
|
* ### Q&A ###
|
|
|
|
*
|
|
|
|
* - Q: Why are INT2FIX etc. here, not in `int.h`?
|
|
|
|
*
|
|
|
|
* - A: Because they are in fact handling `long`. It seems someone did not
|
|
|
|
* understand the difference of `int` and `long` when they designed those
|
|
|
|
* macros.
|
|
|
|
*/
|
2020-05-04 02:35:26 -04:00
|
|
|
#include "ruby/impl/config.h"
|
|
|
|
#include "ruby/impl/arithmetic/fixnum.h" /* FIXABLE */
|
|
|
|
#include "ruby/impl/arithmetic/intptr_t.h" /* rb_int2big etc.*/
|
|
|
|
#include "ruby/impl/assume.h"
|
|
|
|
#include "ruby/impl/attr/artificial.h"
|
|
|
|
#include "ruby/impl/attr/cold.h"
|
|
|
|
#include "ruby/impl/attr/const.h"
|
|
|
|
#include "ruby/impl/attr/constexpr.h"
|
|
|
|
#include "ruby/impl/attr/noreturn.h"
|
|
|
|
#include "ruby/impl/cast.h"
|
|
|
|
#include "ruby/impl/dllexport.h"
|
|
|
|
#include "ruby/impl/special_consts.h" /* FIXNUM_FLAG */
|
|
|
|
#include "ruby/impl/value.h"
|
2020-04-08 00:28:13 -04:00
|
|
|
#include "ruby/assert.h"
|
|
|
|
|
|
|
|
#define FIX2LONG RB_FIX2LONG
|
|
|
|
#define FIX2ULONG RB_FIX2ULONG
|
|
|
|
#define INT2FIX RB_INT2FIX
|
|
|
|
#define LONG2FIX RB_INT2FIX
|
|
|
|
#define LONG2NUM RB_LONG2NUM
|
|
|
|
#define NUM2LONG RB_NUM2LONG
|
|
|
|
#define NUM2ULONG RB_NUM2ULONG
|
|
|
|
#define RB_FIX2LONG rb_fix2long
|
|
|
|
#define RB_FIX2ULONG rb_fix2ulong
|
|
|
|
#define RB_LONG2FIX RB_INT2FIX
|
|
|
|
#define RB_LONG2NUM rb_long2num_inline
|
|
|
|
#define RB_NUM2LONG rb_num2long_inline
|
|
|
|
#define RB_NUM2ULONG rb_num2ulong_inline
|
|
|
|
#define RB_ULONG2NUM rb_ulong2num_inline
|
|
|
|
#define ULONG2NUM RB_ULONG2NUM
|
|
|
|
#define rb_fix_new RB_INT2FIX
|
|
|
|
#define rb_long2int rb_long2int_inline
|
|
|
|
|
|
|
|
/** @cond INTERNAL_MACRO */
|
|
|
|
#define RB_INT2FIX RB_INT2FIX
|
|
|
|
/** @endcond */
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_SYMBOL_EXPORT_BEGIN()
|
2020-04-08 00:28:13 -04:00
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_NORETURN()
|
|
|
|
RBIMPL_ATTR_COLD()
|
2020-04-08 00:28:13 -04:00
|
|
|
void rb_out_of_int(SIGNED_VALUE num);
|
|
|
|
|
|
|
|
long rb_num2long(VALUE num);
|
|
|
|
unsigned long rb_num2ulong(VALUE num);
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_SYMBOL_EXPORT_END()
|
2020-04-08 00:28:13 -04:00
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_CONST_ON_NDEBUG()
|
|
|
|
RBIMPL_ATTR_CONSTEXPR_ON_NDEBUG(CXX14)
|
|
|
|
RBIMPL_ATTR_ARTIFICIAL()
|
2020-04-08 00:28:13 -04:00
|
|
|
static inline VALUE
|
|
|
|
RB_INT2FIX(long i)
|
|
|
|
{
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(i));
|
2020-04-08 00:28:13 -04:00
|
|
|
|
|
|
|
/* :NOTE: VALUE can be wider than long. As j being unsigned, 2j+1 is fully
|
|
|
|
* defined. Also it can be compiled into a single LEA instruction. */
|
|
|
|
const unsigned long j = i;
|
|
|
|
const unsigned long k = 2 * j + RUBY_FIXNUM_FLAG;
|
|
|
|
const long l = k;
|
|
|
|
const SIGNED_VALUE m = l; /* Sign extend */
|
|
|
|
const VALUE n = m;
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(n));
|
2020-04-08 00:28:13 -04:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
rb_long2int_inline(long n)
|
|
|
|
{
|
2020-05-04 02:52:56 -04:00
|
|
|
int i = RBIMPL_CAST((int)n);
|
2020-04-08 00:28:13 -04:00
|
|
|
|
|
|
|
if /* constexpr */ (sizeof(long) <= sizeof(int)) {
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSUME(i == n);
|
2020-04-08 00:28:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i != n)
|
|
|
|
rb_out_of_int(n);
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_CONST_ON_NDEBUG()
|
|
|
|
RBIMPL_ATTR_CONSTEXPR_ON_NDEBUG(CXX14)
|
2020-04-08 00:28:13 -04:00
|
|
|
static inline long
|
|
|
|
ruby3_fix2long_by_idiv(VALUE x)
|
|
|
|
{
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
|
2020-04-08 00:28:13 -04:00
|
|
|
|
|
|
|
/* :NOTE: VALUE can be wider than long. (x-1)/2 never overflows because
|
|
|
|
* RB_FIXNUM_P(x) holds. Also it has no portability issue like y>>1
|
|
|
|
* below. */
|
|
|
|
const SIGNED_VALUE y = x - RUBY_FIXNUM_FLAG;
|
|
|
|
const SIGNED_VALUE z = y / 2;
|
2020-05-04 02:52:56 -04:00
|
|
|
const long w = RBIMPL_CAST((long)z);
|
2020-04-08 00:28:13 -04:00
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(w));
|
2020-04-08 00:28:13 -04:00
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_CONST_ON_NDEBUG()
|
|
|
|
RBIMPL_ATTR_CONSTEXPR_ON_NDEBUG(CXX14)
|
2020-04-08 00:28:13 -04:00
|
|
|
static inline long
|
|
|
|
ruby3_fix2long_by_shift(VALUE x)
|
|
|
|
{
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
|
2020-04-08 00:28:13 -04:00
|
|
|
|
|
|
|
/* :NOTE: VALUE can be wider than long. If right shift is arithmetic, this
|
2020-04-20 23:38:36 -04:00
|
|
|
* is noticeably faster than above. */
|
2020-04-08 00:28:13 -04:00
|
|
|
const SIGNED_VALUE y = x;
|
|
|
|
const SIGNED_VALUE z = y >> 1;
|
2020-05-04 02:52:56 -04:00
|
|
|
const long w = RBIMPL_CAST((long)z);
|
2020-04-08 00:28:13 -04:00
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(w));
|
2020-04-08 00:28:13 -04:00
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_CONST()
|
|
|
|
RBIMPL_ATTR_CONSTEXPR(CXX11)
|
2020-04-08 00:28:13 -04:00
|
|
|
static inline bool
|
|
|
|
ruby3_right_shift_is_arithmetic_p(void)
|
|
|
|
{
|
|
|
|
return (-1 >> 1) == -1;
|
|
|
|
}
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_CONST_ON_NDEBUG()
|
|
|
|
RBIMPL_ATTR_CONSTEXPR_ON_NDEBUG(CXX14)
|
2020-04-08 00:28:13 -04:00
|
|
|
static inline long
|
|
|
|
rb_fix2long(VALUE x)
|
|
|
|
{
|
|
|
|
if /* constexpr */ (ruby3_right_shift_is_arithmetic_p()) {
|
|
|
|
return ruby3_fix2long_by_shift(x);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return ruby3_fix2long_by_idiv(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ATTR_CONST_ON_NDEBUG()
|
|
|
|
RBIMPL_ATTR_CONSTEXPR_ON_NDEBUG(CXX14)
|
2020-04-08 00:28:13 -04:00
|
|
|
static inline unsigned long
|
|
|
|
rb_fix2ulong(VALUE x)
|
|
|
|
{
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
|
2020-04-08 00:28:13 -04:00
|
|
|
return rb_fix2long(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline long
|
|
|
|
rb_num2long_inline(VALUE x)
|
|
|
|
{
|
|
|
|
if (RB_FIXNUM_P(x))
|
|
|
|
return RB_FIX2LONG(x);
|
|
|
|
else
|
|
|
|
return rb_num2long(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned long
|
|
|
|
rb_num2ulong_inline(VALUE x)
|
|
|
|
{
|
|
|
|
/* This (negative fixnum would become a large unsigned long while negative
|
|
|
|
* bignum is an exception) has been THE behaviour of NUM2ULONG since the
|
|
|
|
* beginning. It is strange, but we can no longer change how it works at
|
|
|
|
* this moment. We have to get by with it. See also:
|
|
|
|
* https://bugs.ruby-lang.org/issues/9089 */
|
|
|
|
if (RB_FIXNUM_P(x))
|
|
|
|
return RB_FIX2ULONG(x);
|
|
|
|
else
|
|
|
|
return rb_num2ulong(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline VALUE
|
|
|
|
rb_long2num_inline(long v)
|
|
|
|
{
|
|
|
|
if (RB_FIXABLE(v))
|
|
|
|
return RB_LONG2FIX(v);
|
|
|
|
else
|
|
|
|
return rb_int2big(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline VALUE
|
|
|
|
rb_ulong2num_inline(unsigned long v)
|
|
|
|
{
|
|
|
|
if (RB_POSFIXABLE(v))
|
|
|
|
return RB_LONG2FIX(v);
|
|
|
|
else
|
|
|
|
return rb_uint2big(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @cond INTERNAL_MACRO
|
|
|
|
*
|
|
|
|
* Following overload is necessary because sometimes INT2FIX is used as a enum
|
|
|
|
* value (e.g. `enum { FOO = INT2FIX(0) };`). THIS IS NG in theory because a
|
|
|
|
* VALUE does not fit into an enum (which must be a signed int). But we cannot
|
|
|
|
* break existing codes.
|
|
|
|
*/
|
2020-05-04 02:52:56 -04:00
|
|
|
#if RBIMPL_HAS_ATTR_CONSTEXPR_CXX14
|
2020-04-08 00:28:13 -04:00
|
|
|
# /* C++ can write constexpr as enum values. */
|
|
|
|
|
|
|
|
#elif ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
|
|
|
|
# undef INT2FIX
|
2020-05-04 02:52:56 -04:00
|
|
|
# define INT2FIX(i) (RBIMPL_CAST((VALUE)(i)) << 1 | RUBY_FIXNUM_FLAG)
|
2020-04-08 00:28:13 -04:00
|
|
|
|
|
|
|
#else
|
|
|
|
# undef INT2FIX
|
|
|
|
# define INT2FIX(i) \
|
|
|
|
__builtin_choose_expr( \
|
|
|
|
__builtin_constant_p(i), \
|
2020-05-04 02:52:56 -04:00
|
|
|
RBIMPL_CAST((VALUE)(i)) << 1 | RUBY_FIXNUM_FLAG, \
|
2020-04-08 00:28:13 -04:00
|
|
|
RB_INT2FIX(i))
|
|
|
|
#endif
|
|
|
|
/** @endcond */
|
|
|
|
|
2020-05-04 02:52:56 -04:00
|
|
|
#endif /* RBIMPL_ARITHMETIC_LONG_H */
|