bfs_num.c

/* [<][>]
[^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. BIGNUM_P
  2. NUMERIC_P
  3. ROBJ_AS_CSTR
  4. my_raise_range_error
  5. my_big2ullong
  6. my_num2ullong
  7. my_big2llong
  8. my_num2llong
  9. my_num2ulong
  10. my_big2long
  11. my_num2long
  12. my_num2ushrt
  13. my_num2shrt
  14. my_num2uchar
  15. my_num2char
  16. my_llong2big
  17. my_llong2num
  18. my_ullong2big
  19. my_ullong2num

/*
 *  bfs_num.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <ruby.h>


#ifndef BIGNUM_P
#  define BIGNUM_P(x) (TYPE(x) == T_BIGNUM)
/* [<][>][^][v][top][bottom][index][help] */
#endif

#ifndef NUMERIC_P
#  define NUMERIC_P(x) rb_obj_is_kind_of((x), rb_cNumeric)
/* [<][>][^][v][top][bottom][index][help] */
#endif

#ifndef ROBJ_AS_CSTR
#  define ROBJ_AS_CSTR(x) (RSTRING(rb_obj_as_string(x))->ptr)
/* [<][>][^][v][top][bottom][index][help] */
#endif


#define Less_Than(x, y)    rb_funcall((x), rb_intern("<"), 1, (y))
#define Greater_Than(x, y) rb_funcall((x), rb_intern(">"), 1, (y))


static void
my_raise_range_error(VALUE x, const char *type)
/* [<][>][^][v][top][bottom][index][help] */
{
  rb_raise(rb_eRangeError, "numeric `%s' out of range of `%s'",
                           ROBJ_AS_CSTR(x), type);
}


#define Check_Negative_Num(x, type) {  \
  if (NUMERIC_P(x) && Less_Than((x), INT2FIX(0))) { \
    my_raise_range_error((x), (type)); \
  }                                    \
}


static unsigned long long
my_big2ullong(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE str;
  int base = 16;
  unsigned long long num;

  str = rb_big2str(x, base);
  errno = 0;
  num = strtoull(RSTRING(str)->ptr, NULL, base);
  if (num == ULONGLONG_MAX && errno) {
    my_raise_range_error(x, "unsigned long long");
  }
  return num;
}


unsigned long long
my_num2ullong(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  Check_Negative_Num(x, "unsigned long long");
  if (BIGNUM_P(x)) {
    return my_big2ullong(x);
  }
  return (unsigned long long)rb_num2long(x);
}


static long long
my_big2llong(VALUE x, const char *type)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE str;
  int base = 16;
  long long num;

  str = rb_big2str(x, base);
  errno = 0;
  num = strtoll(RSTRING(str)->ptr, NULL, base);
  if ((num == LONGLONG_MAX || num == LONGLONG_MIN) && errno) {
    my_raise_range_error(x, type);
  }
  return num;
}


long long
my_num2llong(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (BIGNUM_P(x)) {
    return my_big2llong(x, "long long");
  }
  return (long long)rb_num2long(x);
}


unsigned long
my_num2ulong(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  Check_Negative_Num(x, "unsigned long");
  if (BIGNUM_P(x)) {
    return rb_big2ulong(x);
  }
  return (unsigned long)rb_num2long(x);
}


static long
my_big2long(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  VALUE str;
  int base = 16;
  long num;

  str = rb_big2str(x, base);
  errno = 0;
  num = strtol(RSTRING(str)->ptr, NULL, base);
  if ((num == LONG_MAX || num == LONG_MIN) && errno) {
    my_raise_range_error(x, "long");
  }
  return num;
}


long
my_num2long(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (BIGNUM_P(x)) {
    return my_big2long(x);
  }
  return rb_num2long(x);
}


#define Check_Num_Range(x, max, min, type) {    \
  long long n = (BIGNUM_P(x)                    \
                  ? my_big2llong((x), (type))   \
                  : (long long)rb_num2long(x)); \
  if (n < (min) || n > (max)) {        \
    my_raise_range_error((x), (type)); \
  }                                    \
}


unsigned short
my_num2ushrt(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  Check_Num_Range(x, USHRT_MAX, 0, "unsigned short");
  return (unsigned short)(my_num2ulong(x) & 0xffff);
}


short
my_num2shrt(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  Check_Num_Range(x, SHRT_MAX, SHRT_MIN, "short");
  return (short)(my_num2long(x) & 0xffff);
}


unsigned char
my_num2uchar(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  Check_Num_Range(x, UCHAR_MAX, 0, "unsigned char");
  return (unsigned char)(my_num2ulong(x) & 0xff);
}


char
my_num2char(VALUE x)
/* [<][>][^][v][top][bottom][index][help] */
{
  Check_Num_Range(x, SCHAR_MAX, SCHAR_MIN, "char");
  return (char)(my_num2long(x) & 0xff);
}


static VALUE
my_llong2big(long long n)
/* [<][>][^][v][top][bottom][index][help] */
{
  char str[21];
  sprintf(str, "%+lld", n);
  return rb_cstr2inum(str, 10);
}


VALUE
my_llong2num(long long n)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (FIXABLE(n)) {
    return INT2FIX(n);
  }
  return my_llong2big(n);
}


static VALUE
my_ullong2big(unsigned long long n)
/* [<][>][^][v][top][bottom][index][help] */
{
  char str[21];
  sprintf(str, "%llu", n);
  return rb_cstr2inum(str, 10);
}


VALUE
my_ullong2num(unsigned long long n)
/* [<][>][^][v][top][bottom][index][help] */
{
  if (POSFIXABLE(n)) {
    return INT2FIX(n);
  }
  return my_ullong2big(n);
}

/* [<][>][^][v][top][bottom][index][help] */