Skip to content

feat(stable-api): Add NUM2LONG and LONG2NUM for integer conversion #675

@ianks

Description

@ianks

Summary

Add stable API methods for converting between Ruby numeric values and C's long, equivalent to NUM2LONG, RB_NUM2LONG, LONG2NUM, and RB_LONG2NUM.

Motivation

Integer conversion is one of the most common operations in Ruby C extensions:

  • Extracting array indices
  • Loop counters
  • Size/length values
  • Any numeric parameter handling

The inline versions handle Fixnum fast-path without function call overhead.

Ruby Source Reference

// include/ruby/internal/arithmetic/long.h

static inline long
rb_num2long_inline(VALUE x)
{
    if (RB_FIXNUM_P(x))
        return RB_FIX2LONG(x);
    else
        return rb_num2long(x);  // handles Bignum, Float, etc.
}

static inline VALUE
rb_long2num_inline(long v)
{
    if (RB_FIXABLE(v))
        return RB_LONG2FIX(v);
    else
        return rb_int2big(v);
}

Proposed API

/// Convert a Ruby numeric to C long (akin to `NUM2LONG`).
///
/// Handles Fixnum (fast path), Bignum, Float, and any object responding to to_int.
///
/// # Safety
/// This function is unsafe because it may call Ruby methods (to_int)
/// which could raise exceptions or trigger GC.
unsafe fn num2long(&self, obj: VALUE) -> c_long;

/// Convert C long to Ruby Integer (akin to `LONG2NUM`).
///
/// Returns a Fixnum if the value fits, otherwise creates a Bignum.
fn long2num(&self, val: c_long) -> VALUE;

/// Convert a Ruby Fixnum to C long (akin to `FIX2LONG`).
///
/// # Safety
/// Caller must ensure the VALUE is actually a Fixnum.
fn fix2long(&self, obj: VALUE) -> c_long;

/// Convert C long to Ruby Fixnum (akin to `LONG2FIX`).
///
/// # Panics
/// Debug builds will panic if the value doesn't fit in a Fixnum.
fn long2fix(&self, val: c_long) -> VALUE;

/// Check if a long value fits in a Fixnum (akin to `FIXABLE`).
fn fixable(&self, val: c_long) -> bool;

Implementation Notes

  • FIX2LONG: Right-shift by 1 (arithmetic shift)
  • LONG2FIX: Left-shift by 1, OR with RUBY_FIXNUM_FLAG
  • FIXABLE: Check against RUBY_FIXNUM_MIN and RUBY_FIXNUM_MAX
  • These are simple bit operations, should be very fast
  • Reference: include/ruby/internal/arithmetic/long.h

Checklist

  • Add methods to StableApiDefinition trait
  • Implement for each Ruby version
  • Add C fallback in compiled.c
  • Add public macro wrappers in macros.rs
  • Add comprehensive tests including edge cases
  • Add to show-asm script for performance verification

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions