Skip to content

Conversation

@ianks
Copy link
Collaborator

@ianks ianks commented Dec 14, 2025

Summary

Batch 8 of the stable API implementation plan - Adds complex methods to the Stable API:

  • NUM2DBL / DBL2NUM - Float conversions with Flonum support
  • RHASH_SIZE / RHASH_EMPTY_P - Hash size operations
  • ENCODING_GET - Get encoding index from object

⚠️ Draft Status: Ready for review but marking as draft to coordinate with other batches.

Changes

  • stable_api.rs: Added num2dbl(), dbl2num(), rhash_size(), rhash_empty_p(), and encoding_get() methods
  • Implementations: Version-specific implementations for all 7 Ruby versions (2.7, 3.0-3.4, 4.0)
    • NUM2DBL: Handles Flonum encoding (platform-dependent) with fast paths for Fixnum and Flonum
    • DBL2NUM: May return Flonum or heap Float
    • RHASH_SIZE: Calls rb_hash_size_num()
    • ENCODING_GET: Extracts from flags (bits 16-23)
  • Fallback support: C fallback in compiled.c and Rust wrapper in compiled.rs
  • Public API: Macro wrappers in macros.rs with comprehensive documentation
  • Tests: Parity tests in stable_api_test.rs
  • Performance: Optimized NUM2DBL with Flonum and Fixnum fast paths

Testing

All tests pass successfully ✅

Related Issues

Checklist

  • Code follows project style guide
  • All tests pass
  • Documentation included
  • No breaking changes

Implements #668, #661, #663

- Add num2dbl to StableApiDefinition for NUM2DBL
  Converts Ruby numeric (Float, Fixnum) to C double
  Uses rb_num2dbl for all numeric types

- Add dbl2num to StableApiDefinition for DBL2NUM
  Converts C double to Ruby Float VALUE
  Uses rb_float_new for heap allocation

- Add rhash_size to StableApiDefinition for RHASH_SIZE
  Returns number of entries in a Hash
  Converts fixnum result from rb_hash_size to usize

- Add rhash_empty_p to StableApiDefinition for RHASH_EMPTY_P
  Returns true if hash size is 0
  Implemented as wrapper around rhash_size

- Add encoding_get to StableApiDefinition for ENCODING_GET
  Extracts encoding index from object flags (bits 16-23)
  Provides both macro-based and manual extraction

Implement for all Ruby versions:
- Ruby 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 4.0

Add C fallbacks in compiled.c:
- impl_num2dbl calls NUM2DBL
- impl_dbl2num calls DBL2NUM
- impl_rhash_size calls RHASH_SIZE
- impl_rhash_empty_p calls RHASH_EMPTY_P
- impl_encoding_get with fallback for older Ruby versions

Add public macro wrappers in macros.rs:
- NUM2DBL, DBL2NUM, RHASH_SIZE, RHASH_EMPTY_P, ENCODING_GET

Add comprehensive parity tests:
- test_num2dbl_float: float conversion
- test_num2dbl_fixnum: fixnum conversion
- test_num2dbl_negative_fixnum: negative fixnum
- test_dbl2num_and_num2dbl_roundtrip: roundtrip test
- test_rhash_size_empty: empty hash
- test_rhash_size_with_elements: hash with entries
- test_rhash_empty_p_empty: empty check
- test_rhash_empty_p_with_elements: non-empty check
- test_encoding_get_utf8_string: UTF-8 encoding
- test_encoding_get_ascii_string: ASCII encoding

All 90 stable API tests pass.
Add fast path optimizations to NUM2DBL for all Ruby versions:
- Flonum decoding: Direct bit manipulation when ruby_use_flonum=true
- Fixnum conversion: Fast right-shift conversion to double
- Fallback: Use rb_num2dbl for heap Float and Bignum

This matches the Ruby C implementation in internal/numeric.h and
significantly improves performance for common numeric conversions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants