magnus 0.4.4

High level Ruby bindings for Rust.
Documentation
//! Types for working with Ruby's VALUE type, representing all objects, and 'immediate' values such as Fixnum.

#[cfg(ruby_use_flonum)]
mod flonum;

use std::{
    borrow::Cow,
    convert::TryFrom,
    ffi::CStr,
    fmt,
    mem::transmute,
    num::NonZeroUsize,
    ops::{Deref, DerefMut},
    os::raw::{c_char, c_int, c_long, c_ulong},
    ptr,
};

#[cfg(ruby_use_flonum)]
pub use flonum::Flonum;
use rb_sys::{
    rb_any_to_s, rb_block_call, rb_check_funcall, rb_check_id, rb_check_id_cstr,
    rb_check_symbol_cstr, rb_enumeratorize_with_size, rb_eql, rb_equal, rb_funcall_with_block,
    rb_funcallv, rb_gc_register_address, rb_gc_unregister_address, rb_id2name, rb_id2sym,
    rb_inspect, rb_intern3, rb_ll2inum, rb_obj_as_string, rb_obj_classname, rb_obj_freeze,
    rb_obj_is_kind_of, rb_obj_respond_to, rb_sym2id, rb_ull2inum, ruby_fl_type,
    ruby_special_consts, ruby_value_type, RBasic, ID, VALUE,
};

// These don't seem to appear consistently in bindgen output, not sure if they
// aren't consistently defined in the headers or what. Lets just do it
// ourselves.
const RUBY_FIXNUM_MAX: c_ulong = (c_long::MAX / 2) as c_ulong;
const RUBY_FIXNUM_MIN: c_long = c_long::MIN / 2;

use crate::{
    block::Proc,
    class::{self, RClass},
    encoding::{EncodingCapable, RbEncoding},
    enumerator::Enumerator,
    error::{protect, Error},
    exception,
    float::Float,
    integer::{Integer, IntegerType},
    method::{Block, BlockReturn},
    module::Module,
    r_bignum::RBignum,
    r_string::RString,
    symbol::Symbol,
    try_convert::{ArgList, TryConvert, TryConvertOwned},
};

/// Debug assertation that the Value hasn't been garbage collected.
///
// This isn't infallible, if the original object was gc'd and that slot
// reused already this won't panic like it should, but we're trying our
// best here.
#[doc(hidden)]
#[macro_export]
macro_rules! debug_assert_value {
    ($value:expr) => {
        // The memory this points to is managed by Ruby's GC and we can't
        // really know if it's safe to access as with GC compaction this may
        // point to memory now outside that owned by the process. We will likly
        // segfault in that case, which is kind of OK, as we're trying to panic
        // anyway.
        #[allow(unused_unsafe)]
        #[cfg(debug_assertions)]
        match unsafe { $value.rb_type() } {
            ::rb_sys::ruby_value_type::RUBY_T_NONE | ::rb_sys::ruby_value_type::RUBY_T_ZOMBIE => {
                panic!("Attempting to access garbage collected Object")
            }
            #[cfg(ruby_gte_2_7)]
            ::rb_sys::ruby_value_type::RUBY_T_MOVED => {
                panic!("Attempting to access garbage collected Object")
            }
            _ => (),
        };
    };
}

/// Ruby's `VALUE` type, which can represent any Ruby object.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Value(VALUE);

impl Value {
    #[inline]
    pub(crate) const fn new(val: VALUE) -> Self {
        Self(val)
    }

    #[inline]
    pub(crate) unsafe fn r_basic_unchecked(self) -> ptr::NonNull<RBasic> {
        #[cfg(debug_assertions)]
        if self.is_immediate() {
            panic!("attempting to access immediate value as pointer");
        }
        ptr::NonNull::new_unchecked(self.0 as *mut RBasic)
    }

    /// Returns whether `self` is an 'immediate' value.
    ///
    /// 'immediate' values are encoded directly into the `Value` and require
    /// no additional lookup. They will never be garbage collected.
    ///
    /// non-immediate values are pointers to other memory holding the data for
    /// the object.
    #[inline]
    pub(crate) fn is_immediate(self) -> bool {
        let value_p = self.as_rb_value();
        let immediate_p = value_p & ruby_special_consts::RUBY_IMMEDIATE_MASK as VALUE != 0;
        let test = value_p & !(ruby_special_consts::RUBY_Qnil as VALUE) != 0;
        immediate_p || !test // special_const_p
    }

    #[inline]
    pub(crate) fn r_basic(self) -> Option<ptr::NonNull<RBasic>> {
        unsafe { (!self.is_immediate()).then(|| self.r_basic_unchecked()) }
    }

    #[inline]
    fn is_false(self) -> bool {
        self.as_rb_value() == ruby_special_consts::RUBY_Qfalse as VALUE
    }

    /// Returns whether `self` is Ruby's `nil` value.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(eval::<Value>("nil").unwrap().is_nil());
    /// assert!(!eval::<Value>("Object.new").unwrap().is_nil());
    /// assert!(!eval::<Value>("0").unwrap().is_nil());
    /// assert!(!eval::<Value>("[]").unwrap().is_nil());
    /// ```
    #[inline]
    pub fn is_nil(self) -> bool {
        self.as_rb_value() == ruby_special_consts::RUBY_Qnil as VALUE
    }

    #[inline]
    fn is_true(self) -> bool {
        self.as_rb_value() == ruby_special_consts::RUBY_Qtrue as VALUE
    }

    #[inline]
    pub(crate) fn is_undef(self) -> bool {
        self.as_rb_value() == ruby_special_consts::RUBY_Qundef as VALUE
    }

    #[inline]
    fn is_fixnum(self) -> bool {
        self.as_rb_value() & ruby_special_consts::RUBY_FIXNUM_FLAG as VALUE != 0
    }

    #[inline]
    pub(crate) fn is_static_symbol(self) -> bool {
        const MASK: usize = !(usize::MAX << ruby_special_consts::RUBY_SPECIAL_SHIFT as usize);
        self.as_rb_value() as usize & MASK == ruby_special_consts::RUBY_SYMBOL_FLAG as usize
    }

    #[inline]
    pub(crate) fn is_flonum(self) -> bool {
        self.as_rb_value() & ruby_special_consts::RUBY_FLONUM_MASK as VALUE
            == ruby_special_consts::RUBY_FLONUM_FLAG as VALUE
    }

    // derefs a raw pointer that under GC compaction may be outside the
    // process's memory space if the Value has been allowed to get GC'd
    pub(crate) fn rb_type(self) -> ruby_value_type {
        match self.r_basic() {
            Some(r_basic) => {
                unsafe {
                    let ret = r_basic.as_ref().flags & (ruby_value_type::RUBY_T_MASK as VALUE);
                    // this bit is safe, ruby_value_type is #[repr(u32)], the flags
                    // value set by Ruby, and Ruby promises that flags masked like
                    // this will always be a valid entry in this enum
                    std::mem::transmute(ret as u32)
                }
            }
            None => {
                if self.is_false() {
                    ruby_value_type::RUBY_T_FALSE
                } else if self.is_nil() {
                    ruby_value_type::RUBY_T_NIL
                } else if self.is_true() {
                    ruby_value_type::RUBY_T_TRUE
                } else if self.is_undef() {
                    ruby_value_type::RUBY_T_UNDEF
                } else if self.is_fixnum() {
                    ruby_value_type::RUBY_T_FIXNUM
                } else if self.is_static_symbol() {
                    ruby_value_type::RUBY_T_SYMBOL
                } else if self.is_flonum() {
                    ruby_value_type::RUBY_T_FLOAT
                } else {
                    unreachable!()
                }
            }
        }
    }

    /// Checks for equality, delegating to the Ruby method `#==`.
    ///
    /// Ruby optimises this check if `self` and `other` are the same object
    /// or some built-in types, then calling the `#==` method will be skipped.
    ///
    /// Returns `Err` if `#==` raises.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{Integer, RArray};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let a = RArray::from_vec(vec![1, 2, 3]);
    /// let b = RArray::from_vec(vec![1, 2, 3]);
    /// let c = RArray::from_vec(vec![4, 5, 6]);
    /// let d = Integer::from_i64(1);
    /// assert!(a.equal(a).unwrap());
    /// assert!(a.equal(b).unwrap());
    /// assert!(!a.equal(c).unwrap());
    /// assert!(!a.equal(d).unwrap());
    /// ```
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let (a, b): (Value, Value) = eval!("
    ///     class Example
    ///       def ==(other)
    ///         raise
    ///       end
    ///     end
    ///     [Example.new, Example.new]
    /// ").unwrap();
    ///
    /// assert!(a.equal(&b).is_err());
    /// ```
    pub fn equal<T>(self, other: T) -> Result<bool, Error>
    where
        T: Deref<Target = Value>,
    {
        unsafe {
            protect(|| Value::new(rb_equal(self.as_rb_value(), other.as_rb_value())))
                .map(Value::to_bool)
        }
    }

    /// Checks for equality, delegating to the Ruby method `#eql?`.
    ///
    /// See [`Value::equal`] for the equivalent of the `#==` method.
    ///
    /// Ruby optimises this check if `self` and `other` are the same object
    /// or some built-in types, then calling the `#==` method will be skipped.
    ///
    /// Returns `Err` if `#eql?` raises.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{Integer, RArray};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let a = RArray::from_vec(vec![1, 2, 3]);
    /// let b = RArray::from_vec(vec![1, 2, 3]);
    /// let c = RArray::from_vec(vec![4, 5, 6]);
    /// let d = Integer::from_i64(1);
    /// assert!(a.eql(a).unwrap());
    /// assert!(a.eql(b).unwrap());
    /// assert!(!a.eql(c).unwrap());
    /// assert!(!a.eql(d).unwrap());
    /// ```
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let (a, b): (Value, Value) = eval!("
    ///     class Example
    ///       def eql?(other)
    ///         raise
    ///       end
    ///     end
    ///     [Example.new, Example.new]
    /// ").unwrap();
    ///
    /// assert!(a.eql(&b).is_err());
    /// ```
    pub fn eql<T>(self, other: T) -> Result<bool, Error>
    where
        T: Deref<Target = Value>,
    {
        unsafe {
            protect(|| Value::new(rb_eql(self.as_rb_value(), other.as_rb_value()) as VALUE))
                .map(Value::to_bool)
        }
    }

    /// Returns the class that `self` is an instance of.
    ///
    /// # Panics
    ///
    /// panics if self is `Qundef`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Value>("true").unwrap().class().inspect(), "TrueClass");
    /// assert_eq!(eval::<Value>("[1,2,3]").unwrap().class().inspect(), "Array");
    /// ```
    pub fn class(self) -> RClass {
        unsafe {
            match self.r_basic() {
                Some(r_basic) => RClass::from_rb_value_unchecked(r_basic.as_ref().klass),
                None => {
                    if self.is_false() {
                        class::false_class()
                    } else if self.is_nil() {
                        class::nil_class()
                    } else if self.is_true() {
                        class::true_class()
                    } else if self.is_undef() {
                        panic!("undef does not have a class")
                    } else if self.is_fixnum() {
                        class::integer()
                    } else if self.is_static_symbol() {
                        class::symbol()
                    } else if self.is_flonum() {
                        class::float()
                    } else {
                        unreachable!()
                    }
                }
            }
        }
    }

    #[inline]
    pub(crate) const fn as_rb_value(self) -> VALUE {
        self.0
    }

    /// Returns whether `self` is 'frozen'.
    ///
    /// Ruby prevents modifying frozen objects.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(eval::<Value>(":foo").unwrap().is_frozen());
    /// assert!(eval::<Value>("42").unwrap().is_frozen());
    /// assert!(!eval::<Value>("[]").unwrap().is_frozen());
    /// ```
    pub fn is_frozen(self) -> bool {
        match self.r_basic() {
            None => true,
            Some(r_basic) => unsafe {
                r_basic.as_ref().flags & ruby_fl_type::RUBY_FL_FREEZE as VALUE != 0
            },
        }
    }

    /// Returns an error if `self` is 'frozen'.
    ///
    /// Useful for checking if an object is frozen in a function that would
    /// modify it.
    ///
    /// # Examples
    /// ```
    /// use magnus::{eval, Error, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// fn mutate(val: Value) -> Result<(), Error> {
    ///     val.check_frozen()?;
    ///
    ///     /// ...
    ///
    ///     Ok(())
    /// }
    ///
    /// assert!(mutate(eval("Object.new").unwrap()).is_ok());
    /// assert!(mutate(eval(":foo").unwrap()).is_err());
    /// ```
    pub fn check_frozen(self) -> Result<(), Error> {
        if self.is_frozen() {
            Err(Error::new(
                exception::frozen_error(),
                format!("can't modify frozen {}", unsafe { self.classname() }),
            ))
        } else {
            Ok(())
        }
    }

    /// Mark `self` as frozen.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, RArray};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let ary = RArray::new();
    /// assert!(!ary.is_frozen());
    /// ary.freeze();
    /// assert!(ary.is_frozen());
    /// ```
    pub fn freeze(self) {
        unsafe { rb_obj_freeze(self.as_rb_value()) };
    }

    /// Convert `self` to a `bool`, following Ruby's rules of `false` and `nil`
    /// as boolean `false` and everything else boolean `true`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(!eval::<Value>("false").unwrap().to_bool());
    /// assert!(!eval::<Value>("nil").unwrap().to_bool());
    ///
    /// assert!(eval::<Value>("true").unwrap().to_bool());
    /// assert!(eval::<Value>("0").unwrap().to_bool());
    /// assert!(eval::<Value>("[]").unwrap().to_bool());
    /// assert!(eval::<Value>(":foo").unwrap().to_bool());
    /// assert!(eval::<Value>("Object.new").unwrap().to_bool());
    /// ```
    #[inline]
    pub fn to_bool(self) -> bool {
        self.as_rb_value() & !(ruby_special_consts::RUBY_Qnil as VALUE) != 0
    }

    /// Call the method named `method` on `self` with `args`.
    ///
    /// Returns `Ok(T)` if the method returns without error and the return
    /// value converts to a `T`, or returns `Err` if the method raises or the
    /// conversion fails.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, RArray};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let values = eval::<RArray>(r#"["foo", 1, :bar]"#).unwrap();
    /// let result: String = values.funcall("join", (" & ",)).unwrap();
    /// assert_eq!(result, "foo & 1 & bar");
    /// ```
    pub fn funcall<M, A, T>(self, method: M, args: A) -> Result<T, Error>
    where
        M: Into<Id>,
        A: ArgList,
        T: TryConvert,
    {
        unsafe {
            let id = method.into();
            let args = args.into_arg_list();
            let slice = args.as_ref();
            protect(|| {
                Value::new(rb_funcallv(
                    self.as_rb_value(),
                    id.as_rb_id(),
                    slice.len() as c_int,
                    slice.as_ptr() as *const VALUE,
                ))
            })
            .and_then(|v| v.try_convert())
        }
    }

    /// If `self` responds to the method named `method`, call it with `args`.
    ///
    /// Returns `Some(Ok(T))` if the method exists and returns without error,
    /// `None` if it does not exist, or `Some(Err)` if an exception was raised.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{Float, Integer, RString};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let val = Float::from_f64(1.23);
    /// let res: Integer = val.check_funcall("to_int", ()).unwrap().unwrap();
    /// assert_eq!(res.to_i64().unwrap(), 1);
    ///
    /// let val = RString::new("1.23");
    /// let res: Option<Result<Integer, _>> = val.check_funcall("to_int", ());
    /// assert!(res.is_none());
    /// ```
    pub fn check_funcall<M, A, T>(self, method: M, args: A) -> Option<Result<T, Error>>
    where
        M: Into<Id>,
        A: ArgList,
        T: TryConvert,
    {
        let id = method.into();
        let args = args.into_arg_list();
        let slice = args.as_ref();
        unsafe {
            let result = protect(|| {
                Value::new(rb_check_funcall(
                    self.as_rb_value(),
                    id.as_rb_id(),
                    slice.len() as c_int,
                    slice.as_ptr() as *const VALUE,
                ))
            });
            match result {
                Ok(v) if v.is_undef() => None,
                Ok(v) => Some(v.try_convert()),
                Err(e) => Some(Err(e)),
            }
        }
    }

    /// Call the method named `method` on `self` with `args` and `block`.
    ///
    /// Similar to [`funcall`](Value::funcall), but passes `block` as a Ruby
    /// block to the method.
    ///
    /// See also [`block_call`](Value::block_call).
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, block::Proc, RArray, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let values = eval::<RArray>(r#"["foo", 1, :bar]"#).unwrap();
    /// let block = Proc::new(|args, _block| args.first().unwrap().to_r_string());
    /// let _: Value = values.funcall_with_block("map!", (), block).unwrap();
    /// assert_eq!(values.to_vec::<String>().unwrap(), vec!["foo", "1", "bar"]);
    /// ```
    pub fn funcall_with_block<M, A, T>(self, method: M, args: A, block: Proc) -> Result<T, Error>
    where
        M: Into<Id>,
        A: ArgList,
        T: TryConvert,
    {
        unsafe {
            let id = method.into();
            let args = args.into_arg_list();
            let slice = args.as_ref();
            protect(|| {
                Value::new(rb_funcall_with_block(
                    self.as_rb_value(),
                    id.as_rb_id(),
                    slice.len() as c_int,
                    slice.as_ptr() as *const VALUE,
                    block.as_rb_value(),
                ))
            })
            .and_then(|v| v.try_convert())
        }
    }

    /// Call the method named `method` on `self` with `args` and `block`.
    ///
    /// Similar to [`funcall`](Value::funcall), but passes `block` as a Ruby
    /// block to the method.
    ///
    /// As `block` is a function pointer, only functions and closures that do
    /// not capture any variables are permitted. For more flexibility (at the
    /// cost of allocating) see [`Proc::from_fn`] and
    /// [`funcall_with_block`](Value::funcall_with_block).
    ///
    /// The function passed as `block` will receive values yielded to the block
    /// as a slice of [`Value`]s, plus `Some(Proc)` if the block itself was
    /// called with a block, or `None` otherwise.
    ///
    /// The `block` function may return any `R` or `Result<R, Error>` where `R`
    /// implements `Into<Value>`. Returning `Err(Error)` will raise the error
    /// as a Ruby exception.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, RArray, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let values = eval::<RArray>(r#"["foo", 1, :bar]"#).unwrap();
    /// let _: Value = values.block_call("map!", (), |args, _block| args.first().unwrap().to_r_string()).unwrap();
    /// assert_eq!(values.to_vec::<String>().unwrap(), vec!["foo", "1", "bar"]);
    /// ```
    pub fn block_call<M, A, R, T>(
        self,
        method: M,
        args: A,
        block: fn(&[Value], Option<Proc>) -> R,
    ) -> Result<T, Error>
    where
        M: Into<Id>,
        A: ArgList,
        R: BlockReturn,
        T: TryConvert,
    {
        unsafe extern "C" fn call<R>(
            _yielded_arg: VALUE,
            callback_arg: VALUE,
            argc: c_int,
            argv: *const VALUE,
            blockarg: VALUE,
        ) -> VALUE
        where
            R: BlockReturn,
        {
            let func = std::mem::transmute::<VALUE, fn(&[Value], Option<Proc>) -> R>(callback_arg);
            Block::new(func)
                .call_handle_error(argc, argv as *const Value, Value::new(blockarg))
                .as_rb_value()
        }

        let id = method.into();
        let args = args.into_arg_list();
        let slice = args.as_ref();
        let call_func =
            call::<R> as unsafe extern "C" fn(VALUE, VALUE, c_int, *const VALUE, VALUE) -> VALUE;
        #[cfg(ruby_lt_2_7)]
        let call_func: unsafe extern "C" fn() -> VALUE = unsafe { std::mem::transmute(call_func) };

        protect(|| unsafe {
            #[allow(clippy::fn_to_numeric_cast)]
            Value::new(rb_block_call(
                self.as_rb_value(),
                id.as_rb_id(),
                slice.len() as c_int,
                slice.as_ptr() as *const VALUE,
                Some(call_func),
                block as VALUE,
            ))
        })
        .and_then(|v| v.try_convert())
    }

    /// Check if `self` responds to the given Ruby method.
    ///
    /// The `include_private` agument controls whether `self`'s private methods
    /// are checked. If `false` they are not, if `true` they are.
    ///
    /// See also [`Value::check_funcall`].
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::RString;
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let s = RString::new("example");
    /// assert!(s.respond_to("to_str", false).unwrap());
    /// assert!(!s.respond_to("puts", false).unwrap());
    /// assert!(s.respond_to("puts", true).unwrap());
    /// assert!(!s.respond_to("non_existant", false).unwrap());
    /// assert!(!s.respond_to("non_existant", true).unwrap());
    /// ```
    pub fn respond_to<M>(self, method: M, include_private: bool) -> Result<bool, Error>
    where
        M: Into<Id>,
    {
        let id = method.into();
        let mut res = false;
        protect(|| {
            unsafe {
                res =
                    rb_obj_respond_to(self.as_rb_value(), id.as_rb_id(), include_private as c_int)
                        != 0;
            }
            QNIL
        })?;
        Ok(res)
    }

    /// Convert `self` to a Ruby `String`.
    ///
    /// If `self` is already a `String` is it wrapped as a `RString`, otherwise
    /// the Ruby `to_s` method is called.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, class, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let value = eval::<Value>("[]").unwrap();
    /// assert!(value.to_r_string().unwrap().is_kind_of(class::string()));
    /// ```
    pub fn to_r_string(self) -> Result<RString, Error> {
        match RString::from_value(self) {
            Some(v) => Ok(v),
            None => protect(|| unsafe {
                RString::from_rb_value_unchecked(rb_obj_as_string(self.as_rb_value()))
            }),
        }
    }

    /// Convert `self` to a Rust string.
    ///
    /// # Safety
    ///
    /// This may return a direct view of memory owned and managed by Ruby. Ruby
    /// may modify or free the memory backing the returned str, the caller must
    /// ensure this does not happen.
    ///
    /// This can be used safely by immediately calling
    /// [`into_owned`](Cow::into_owned) on the return value.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, QTRUE};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let value = QTRUE;
    /// // safe as we neve give Ruby a chance to free the string.
    /// let s = unsafe { value.to_s() }.unwrap().into_owned();
    /// assert_eq!(s, "true");
    /// ```
    #[allow(clippy::wrong_self_convention)]
    pub unsafe fn to_s(&self) -> Result<Cow<str>, Error> {
        if let Some(s) = RString::ref_from_value(self) {
            if s.is_utf8_compatible_encoding() {
                return s.as_str().map(Cow::Borrowed);
            } else {
                return (*s).to_string().map(Cow::Owned);
            }
        }
        self.to_r_string()
            .and_then(|s| s.to_string().map(Cow::Owned))
    }

    /// Convert `self` to a string. If an error is encountered returns a
    /// generic string (usually the object's class name).
    ///
    /// # Safety
    ///
    /// This may return a direct view of memory owned and managed by Ruby. Ruby
    /// may modify or free the memory backing the returned str, the caller must
    /// ensure this does not happen.
    #[allow(clippy::wrong_self_convention)]
    pub(crate) unsafe fn to_s_infallible(&self) -> Cow<str> {
        match self.to_s() {
            Ok(v) => v,
            Err(_) => Cow::Owned(
                RString::from_rb_value_unchecked(rb_any_to_s(self.as_rb_value()))
                    .to_string_lossy()
                    .into_owned(),
            ),
        }
    }

    /// Convert `self` to its Ruby debug representation.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Symbol, QNIL};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(QNIL.inspect(), "nil");
    /// assert_eq!(Symbol::new("foo").inspect(), ":foo");
    /// ```
    pub fn inspect(self) -> String {
        unsafe {
            let s = protect(|| RString::from_rb_value_unchecked(rb_inspect(self.as_rb_value())))
                .unwrap_or_else(|_| {
                    RString::from_rb_value_unchecked(rb_any_to_s(self.as_rb_value()))
                });
            s.conv_enc(RbEncoding::utf8())
                .unwrap_or(s)
                .to_string_lossy()
                .into_owned()
        }
    }

    /// Return the name of `self`'s class.
    ///
    /// # Safety
    ///
    /// Ruby may modify or free the memory backing the returned str, the caller
    /// must ensure this does not happen.
    ///
    /// This can be used safely by immediately calling
    /// [`into_owned`](Cow::into_owned) on the return value.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, RHash};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let value = RHash::new();
    /// // safe as we never give Ruby a chance to free the string.
    /// let s = unsafe { value.classname() }.into_owned();
    /// assert_eq!(s, "Hash");
    /// ```
    pub unsafe fn classname(&self) -> Cow<str> {
        let ptr = rb_obj_classname(self.as_rb_value());
        let cstr = CStr::from_ptr(ptr);
        cstr.to_string_lossy()
    }

    /// Returns whether or not `self` is an instance of `class`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{class, eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let value = eval::<Value>("[]").unwrap();
    /// assert!(value.is_kind_of(class::array()));
    /// ```
    pub fn is_kind_of<T>(self, class: T) -> bool
    where
        T: Deref<Target = Value> + Module,
    {
        unsafe { Value::new(rb_obj_is_kind_of(self.as_rb_value(), class.as_rb_value())).to_bool() }
    }

    /// Generate an [`Enumerator`] from `method` on `self`, passing `args` to
    /// `method`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{class, eval, r_string};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let s = r_string!("foo\\bar\\baz");
    /// let mut i = 0;
    /// for line in s.enumeratorize("each_line", ("\\",)) {
    ///     assert!(line.unwrap().is_kind_of(class::string()));
    ///     i += 1;
    /// }
    /// assert_eq!(i, 3);
    /// ```
    pub fn enumeratorize<M, A>(self, method: M, args: A) -> Enumerator
    where
        M: Into<Symbol>,
        A: ArgList,
    {
        let args = args.into_arg_list();
        let slice = args.as_ref();
        unsafe {
            Enumerator::from_rb_value_unchecked(rb_enumeratorize_with_size(
                self.as_rb_value(),
                method.into().as_rb_value(),
                slice.len() as c_int,
                slice.as_ptr() as *const VALUE,
                None,
            ))
        }
    }

    /// Convert `self` to the Rust type `T`.
    ///
    /// See the types that [`TryConvert`] is implemented on for what this
    /// method can convert to.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Value>("42").unwrap().try_convert::<i64>().unwrap(), 42);
    /// assert_eq!(eval::<Value>("1.23").unwrap().try_convert::<i64>().unwrap(), 1);
    /// assert_eq!(eval::<Value>("1").unwrap().try_convert::<f64>().unwrap(), 1.0);
    /// assert_eq!(eval::<Value>("nil").unwrap().try_convert::<Option<i64>>().unwrap(), None);
    /// assert_eq!(eval::<Value>("42").unwrap().try_convert::<Option<i64>>().unwrap(), Some(42));
    /// ```
    #[inline]
    pub fn try_convert<T>(self) -> Result<T, Error>
    where
        T: TryConvert,
    {
        T::try_convert(self)
    }
}

impl Default for Value {
    fn default() -> Self {
        Value::new(ruby_special_consts::RUBY_Qnil as VALUE)
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl From<i8> for Value {
    fn from(value: i8) -> Self {
        Integer::from_i64(value as i64).into()
    }
}

impl From<i16> for Value {
    fn from(value: i16) -> Self {
        Integer::from_i64(value as i64).into()
    }
}

impl From<i32> for Value {
    fn from(value: i32) -> Self {
        Integer::from_i64(value as i64).into()
    }
}

impl From<i64> for Value {
    fn from(value: i64) -> Self {
        Integer::from_i64(value).into()
    }
}

impl From<isize> for Value {
    fn from(value: isize) -> Self {
        Integer::from_i64(value as i64).into()
    }
}

impl From<u8> for Value {
    fn from(value: u8) -> Self {
        Integer::from_u64(value as u64).into()
    }
}

impl From<u16> for Value {
    fn from(value: u16) -> Self {
        Integer::from_u64(value as u64).into()
    }
}

impl From<u32> for Value {
    fn from(value: u32) -> Self {
        Integer::from_u64(value as u64).into()
    }
}

impl From<u64> for Value {
    fn from(value: u64) -> Self {
        Integer::from_u64(value).into()
    }
}

impl From<usize> for Value {
    fn from(value: usize) -> Self {
        Integer::from_u64(value as u64).into()
    }
}

impl From<f32> for Value {
    fn from(value: f32) -> Self {
        Float::from_f64(value as f64).into()
    }
}

impl From<f64> for Value {
    fn from(value: f64) -> Self {
        Float::from_f64(value).into()
    }
}

impl TryConvert for Value {
    fn try_convert(val: Value) -> Result<Self, Error> {
        Ok(val)
    }
}

pub(crate) mod private {
    use super::*;

    /// Marker trait for types that have the same representation as [`Value`].
    ///
    /// Types that are `ReprValue` can be safely transmuted to Value.
    ///
    /// # Safety
    ///
    /// This trait should only be implemented for types that a guaranteed to
    /// have the same layout as [`Value`] and have come from the Ruby VM.
    pub unsafe trait ReprValue: Copy {
        /// Convert `self` to a [`Value`].
        ///
        /// Usually types that implement this trait will also implement
        /// `Deref<Target = Value>`. You should prefer `*data` over
        /// `data.to_value()`.
        ///
        /// This method is for use cases where we effectively want
        /// `transmute::<_, Value>(data)`.
        fn to_value(self) -> Value;

        /// Convert `val` to a `Self`.
        ///
        /// # Safety
        ///
        /// This should only be used when `val` is known to uphold all the
        // invariants of `Self`. It is recommended not to use this method.
        unsafe fn from_value_unchecked(val: Value) -> Self;
    }
}

/// Marker trait for types that have the same representation as [`Value`].
///
/// Types that are `ReprValue` can be safely transmuted to Value.
pub trait ReprValue: private::ReprValue {}

unsafe impl private::ReprValue for Value {
    fn to_value(self) -> Value {
        self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        val
    }
}

impl ReprValue for Value {}

#[derive(Clone, Copy)]
#[repr(transparent)]
pub(crate) struct NonZeroValue(NonZeroUsize);

impl NonZeroValue {
    #[inline]
    pub(crate) const unsafe fn new_unchecked(val: Value) -> Self {
        Self(NonZeroUsize::new_unchecked(val.as_rb_value() as usize))
    }

    pub(crate) const fn get(self) -> Value {
        Value::new(self.0.get() as VALUE)
    }

    pub(crate) fn get_ref(&self) -> &Value {
        let self_ptr = self as *const Self;
        let value_ptr = self_ptr as *const Value;
        // we just got this pointer from &self, so we know it's valid to deref
        unsafe { &*value_ptr }
    }
}

/// Protects a Ruby Value from the garbage collector.
///
/// See also [`Value::leak`] for a value that should be permanently excluded
/// from garbage collection.
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
pub struct BoxValue<T>(Box<T>);

impl<T> BoxValue<T>
where
    T: ReprValue,
{
    /// Create a new `BoxValue`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, gc, value::BoxValue, RString, Value};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[inline(never)]
    /// fn box_value() -> BoxValue<RString> {
    ///     BoxValue::new(RString::new("foo"))
    /// }
    ///
    /// # // get the Value in a different stack frame and copy it to a BoxValue
    /// # // test is invalid if this is done in this function.
    /// let boxed = box_value();
    ///
    /// # // make some garbage
    /// # eval::<Value>(r#"1024.times.map {|i| "test#{i}"}"#).unwrap();
    /// // run garbage collector
    /// gc::start();
    ///
    /// # // try and use value
    /// // boxed is still useable
    /// let result: String = eval!(r#"foo + "bar""#, foo = boxed).unwrap();
    ///
    /// assert_eq!(result, "foobar");
    ///
    /// # // didn't segfault? we passed!
    /// ```
    pub fn new(val: T) -> Self {
        let mut boxed = Box::new(val);
        unsafe { rb_gc_register_address(boxed.as_mut() as *mut _ as *mut VALUE) };
        Self(boxed)
    }
}

impl<T> Drop for BoxValue<T> {
    fn drop(&mut self) {
        unsafe {
            rb_gc_unregister_address(self.0.as_mut() as *mut _ as *mut VALUE);
        }
    }
}

impl<T> AsRef<T> for BoxValue<T> {
    fn as_ref(&self) -> &T {
        &self.0
    }
}

impl<T> AsMut<T> for BoxValue<T> {
    fn as_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<T> Deref for BoxValue<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for BoxValue<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<T> fmt::Display for BoxValue<T>
where
    T: ReprValue,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_value().to_s_infallible() })
    }
}

impl<T> fmt::Debug for BoxValue<T>
where
    T: ReprValue,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.to_value().inspect())
    }
}

impl<T> From<BoxValue<T>> for Value
where
    T: ReprValue,
{
    fn from(val: BoxValue<T>) -> Self {
        val.to_value()
    }
}

/// Ruby's `false` value.
///
/// See [`QFALSE`] to obtain a value of this type.
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qfalse(Value);

/// Ruby's `false` value.
pub const QFALSE: Qfalse = Qfalse::new();

impl Qfalse {
    /// Create a new `Qfalse`.
    #[inline]
    const fn new() -> Self {
        Qfalse(Value::new(ruby_special_consts::RUBY_Qfalse as VALUE))
    }

    /// Return `Some(Qfalse)` if `val` is a `Qfalse`, `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, value::Qfalse};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Qfalse::from_value(eval("false").unwrap()).is_some());
    /// assert!(Qfalse::from_value(eval("0").unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        val.is_false().then(Self::new)
    }
}

impl Deref for Qfalse {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl fmt::Display for Qfalse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for Qfalse {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl From<Qfalse> for Value {
    fn from(val: Qfalse) -> Self {
        *val
    }
}

unsafe impl private::ReprValue for Qfalse {
    fn to_value(self) -> Value {
        *self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        Self(val)
    }
}

impl ReprValue for Qfalse {}

impl TryConvert for Qfalse {
    fn try_convert(val: Value) -> Result<Self, Error> {
        Self::from_value(val).ok_or_else(|| {
            Error::new(
                exception::type_error(),
                format!("no implicit conversion of {} into FalseClass", unsafe {
                    val.classname()
                },),
            )
        })
    }
}
impl TryConvertOwned for Qfalse {}

/// Ruby's `nil` value.
///
/// See [`QNIL`] to obtain a value of this type.
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qnil(NonZeroValue);

/// Ruby's `nil` value.
pub const QNIL: Qnil = Qnil::new();

impl Qnil {
    /// Create a new `Qnil`.
    #[inline]
    const fn new() -> Self {
        unsafe {
            Self(NonZeroValue::new_unchecked(Value::new(
                ruby_special_consts::RUBY_Qnil as VALUE,
            )))
        }
    }

    /// Return `Some(Qnil)` if `val` is a `Qnil`, `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, value::Qnil};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Qnil::from_value(eval("nil").unwrap()).is_some());
    /// assert!(Qnil::from_value(eval("0").unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        val.is_nil().then(Self::new)
    }
}

impl Deref for Qnil {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        self.0.get_ref()
    }
}

impl fmt::Display for Qnil {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for Qnil {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl From<Qnil> for Value {
    fn from(val: Qnil) -> Self {
        *val
    }
}

impl From<()> for Value {
    fn from(_: ()) -> Self {
        QNIL.into()
    }
}

impl<T> From<Option<T>> for Value
where
    T: Into<Value>,
{
    fn from(val: Option<T>) -> Self {
        match val {
            Some(t) => t.into(),
            None => QNIL.into(),
        }
    }
}

unsafe impl private::ReprValue for Qnil {
    fn to_value(self) -> Value {
        *self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        Self(NonZeroValue::new_unchecked(val))
    }
}

impl ReprValue for Qnil {}

impl TryConvert for Qnil {
    fn try_convert(val: Value) -> Result<Self, Error> {
        Self::from_value(val).ok_or_else(|| {
            Error::new(
                exception::type_error(),
                format!("no implicit conversion of {} into NilClass", unsafe {
                    val.classname()
                },),
            )
        })
    }
}
impl TryConvertOwned for Qnil {}

/// Ruby's `true` value.
///
/// See [`QTRUE`] to obtain a value of this type.
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qtrue(NonZeroValue);

/// Ruby's `true` value.
pub const QTRUE: Qtrue = Qtrue::new();

impl Qtrue {
    /// Create a new `Qtrue`.
    #[inline]
    const fn new() -> Self {
        unsafe {
            Self(NonZeroValue::new_unchecked(Value::new(
                ruby_special_consts::RUBY_Qtrue as VALUE,
            )))
        }
    }

    /// Return `Some(Qtrue)` if `val` is a `Qtrue`, `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, value::Qtrue};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Qtrue::from_value(eval("true").unwrap()).is_some());
    /// assert!(Qtrue::from_value(eval("1").unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        val.is_true().then(Self::new)
    }
}

impl Deref for Qtrue {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        self.0.get_ref()
    }
}

impl fmt::Display for Qtrue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for Qtrue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl From<Qtrue> for Value {
    fn from(val: Qtrue) -> Self {
        *val
    }
}

impl From<bool> for Value {
    fn from(val: bool) -> Self {
        if val {
            QTRUE.into()
        } else {
            QFALSE.into()
        }
    }
}

unsafe impl private::ReprValue for Qtrue {
    fn to_value(self) -> Value {
        *self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        Self(NonZeroValue::new_unchecked(val))
    }
}

impl ReprValue for Qtrue {}

impl TryConvert for Qtrue {
    fn try_convert(val: Value) -> Result<Self, Error> {
        Self::from_value(val).ok_or_else(|| {
            Error::new(
                exception::type_error(),
                format!("no implicit conversion of {} into TrueClass", unsafe {
                    val.classname()
                },),
            )
        })
    }
}
impl TryConvertOwned for Qtrue {}

/// A placeholder value that represents an undefined value. Not exposed to
/// Ruby level code.
///
/// See [`QUNDEF`] to obtain a value of this type.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Qundef(NonZeroValue);

/// A placeholder value that represents an undefined value. Not exposed to
/// Ruby level code.
pub const QUNDEF: Qundef = Qundef::new();

impl Qundef {
    /// Create a new `Qundef`.
    #[inline]
    const fn new() -> Self {
        unsafe {
            Self(NonZeroValue::new_unchecked(Value::new(
                ruby_special_consts::RUBY_Qundef as VALUE,
            )))
        }
    }

    /// Return `Some(Qundef)` if `val` is a `Qundef`, `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, value::Qundef};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// // nil is not undef
    /// assert!(Qundef::from_value(eval("nil").unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        val.is_undef().then(Self::new)
    }

    /// Return `self` as a [`Value`].
    ///
    /// # Safety
    ///
    /// It is not a good idea to return this to Ruby code, bad things will
    /// happen. There are only a handful of places in Ruby's API where it is
    /// appropriate to pass a [`Value`] created from `Qundef` (hence this
    /// method, rather than implimenting [`Into<Value>`]).
    #[inline]
    pub unsafe fn to_value(self) -> Value {
        self.0.get()
    }
}

/// A Value known to be a fixnum, Ruby's internal representation of small
/// integers.
///
/// See also [`Integer`].
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Fixnum(NonZeroValue);

impl Fixnum {
    /// Return `Some(Fixnum)` if `val` is a `Fixnum`, `None` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Fixnum::from_value(eval("0").unwrap()).is_some());
    /// // too big
    /// assert!(Fixnum::from_value(eval("9223372036854775807").unwrap()).is_none());
    /// // not an int
    /// assert!(Fixnum::from_value(eval("1.23").unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        unsafe {
            val.is_fixnum()
                .then(|| Self(NonZeroValue::new_unchecked(val)))
        }
    }

    #[inline]
    pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
        Self(NonZeroValue::new_unchecked(Value::new(val)))
    }

    #[inline]
    pub(crate) fn from_i64_impl(n: i64) -> Option<Self> {
        #[allow(clippy::useless_conversion)] // not useless when c_long != i64
        (c_ulong::try_from(n)
            .map(|n| n < RUBY_FIXNUM_MAX + 1)
            .unwrap_or(false)
            && c_long::try_from(n)
                .map(|n| n >= RUBY_FIXNUM_MIN)
                .unwrap_or(false))
        .then(|| unsafe {
            let x = transmute::<_, usize>(n as isize);
            Self::from_rb_value_unchecked(x.wrapping_add(x.wrapping_add(1)) as VALUE)
        })
    }

    /// Create a new `Fixnum` from an `i64.`
    ///
    /// Returns `Ok(Fixnum)` if `n` is in range for `Fixnum`, otherwise returns
    /// `Err(RBignum)`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Fixnum::from_i64(0).is_ok());
    /// // too big
    /// assert!(Fixnum::from_i64(4611686018427387904).is_err());
    /// assert!(Fixnum::from_i64(-4611686018427387905).is_err());
    /// ```
    #[inline]
    pub fn from_i64(n: i64) -> Result<Self, RBignum> {
        Self::from_i64_impl(n)
            .ok_or_else(|| unsafe { RBignum::from_rb_value_unchecked(rb_ll2inum(n)) })
    }

    /// Create a new `Fixnum` from a `u64.`
    ///
    /// Returns `Ok(Fixnum)` if `n` is in range for `Fixnum`, otherwise returns
    /// `Err(RBignum)`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Fixnum::from_u64(0).is_ok());
    /// // too big
    /// assert!(Fixnum::from_u64(4611686018427387904).is_err());
    /// ```
    #[inline]
    pub fn from_u64(n: u64) -> Result<Self, RBignum> {
        Self::from_i64_impl(i64::try_from(n).unwrap_or(i64::MAX))
            .ok_or_else(|| unsafe { RBignum::from_rb_value_unchecked(rb_ull2inum(n)) })
    }

    fn is_negative(self) -> bool {
        unsafe { transmute::<_, isize>(self.0) < 0 }
    }

    /// Convert `self` to an `i8`. Returns `Err` if `self` is out of range for
    /// `i8`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Fixnum>("127").unwrap().to_i8().unwrap(), 127);
    /// assert!(eval::<Fixnum>("128").unwrap().to_i8().is_err());
    /// assert_eq!(eval::<Fixnum>("-128").unwrap().to_i8().unwrap(), -128);
    /// assert!(eval::<Fixnum>("-129").unwrap().to_i8().is_err());
    /// ```
    #[inline]
    pub fn to_i8(self) -> Result<i8, Error> {
        let res = self.to_isize();
        if res > i8::MAX as isize || res < i8::MIN as isize {
            return Err(Error::new(
                exception::range_error(),
                "fixnum too big to convert into `i8`",
            ));
        }
        Ok(res as i8)
    }

    /// Convert `self` to an `i16`. Returns `Err` if `self` is out of range for
    /// `i16`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Fixnum>("32767").unwrap().to_i16().unwrap(), 32767);
    /// assert!(eval::<Fixnum>("32768").unwrap().to_i16().is_err());
    /// assert_eq!(eval::<Fixnum>("-32768").unwrap().to_i16().unwrap(), -32768);
    /// assert!(eval::<Fixnum>("-32769").unwrap().to_i16().is_err());
    /// ```
    #[inline]
    pub fn to_i16(self) -> Result<i16, Error> {
        let res = self.to_isize();
        if res > i16::MAX as isize || res < i16::MIN as isize {
            return Err(Error::new(
                exception::range_error(),
                "fixnum too big to convert into `i16`",
            ));
        }
        Ok(res as i16)
    }

    /// Convert `self` to an `i32`. Returns `Err` if `self` is out of range for
    /// `i32`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[cfg(not(windows))]
    /// # {
    /// assert_eq!(eval::<Fixnum>("2147483647").unwrap().to_i32().unwrap(), 2147483647);
    /// assert!(eval::<Fixnum>("2147483648").unwrap().to_i32().is_err());
    /// assert_eq!(eval::<Fixnum>("-2147483648").unwrap().to_i32().unwrap(), -2147483648);
    /// assert!(eval::<Fixnum>("-2147483649").unwrap().to_i32().is_err());
    /// # }
    /// ```
    #[inline]
    pub fn to_i32(self) -> Result<i32, Error> {
        let res = self.to_isize();
        if res > i32::MAX as isize || res < i32::MIN as isize {
            return Err(Error::new(
                exception::range_error(),
                "fixnum too big to convert into `i32`",
            ));
        }
        Ok(res as i32)
    }

    /// Convert `self` to an `i64`. This is infallible as `i64` can represent a
    /// larger range than `Fixnum`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[cfg(not(windows))]
    /// assert_eq!(eval::<Fixnum>("4611686018427387903").unwrap().to_i64(), 4611686018427387903);
    /// # #[cfg(not(windows))]
    /// assert_eq!(eval::<Fixnum>("-4611686018427387904").unwrap().to_i64(), -4611686018427387904);
    /// ```
    #[inline]
    pub fn to_i64(self) -> i64 {
        self.to_isize() as i64
    }

    /// Convert `self` to an `isize`. Returns `Err` if `self` is out of range
    /// for `isize`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[cfg(not(windows))]
    /// assert_eq!(eval::<Fixnum>("4611686018427387903").unwrap().to_isize(), 4611686018427387903);
    /// # #[cfg(not(windows))]
    /// assert_eq!(eval::<Fixnum>("-4611686018427387904").unwrap().to_isize(), -4611686018427387904);
    /// ```
    #[inline]
    pub fn to_isize(self) -> isize {
        unsafe { transmute::<_, isize>(self) >> 1 }
    }

    /// Convert `self` to a `u8`. Returns `Err` if `self` is negative or out of
    /// range for `u8`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Fixnum>("255").unwrap().to_u8().unwrap(), 255);
    /// assert!(eval::<Fixnum>("256").unwrap().to_u8().is_err());
    /// assert!(eval::<Fixnum>("-1").unwrap().to_u8().is_err());
    /// ```
    #[inline]
    pub fn to_u8(self) -> Result<u8, Error> {
        if self.is_negative() {
            return Err(Error::new(
                exception::range_error(),
                "can't convert negative integer to unsigned",
            ));
        }
        let res = self.to_isize();
        if res > u8::MAX as isize {
            return Err(Error::new(
                exception::range_error(),
                "fixnum too big to convert into `u8`",
            ));
        }
        Ok(res as u8)
    }

    /// Convert `self` to a `u16`. Returns `Err` if `self` is negative or out
    /// of range for `u16`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert_eq!(eval::<Fixnum>("65535").unwrap().to_u16().unwrap(), 65535);
    /// assert!(eval::<Fixnum>("65536").unwrap().to_u16().is_err());
    /// assert!(eval::<Fixnum>("-1").unwrap().to_u16().is_err());
    /// ```
    #[inline]
    pub fn to_u16(self) -> Result<u16, Error> {
        if self.is_negative() {
            return Err(Error::new(
                exception::range_error(),
                "can't convert negative integer to unsigned",
            ));
        }
        let res = self.to_isize();
        if res > u16::MAX as isize {
            return Err(Error::new(
                exception::range_error(),
                "fixnum too big to convert into `u16`",
            ));
        }
        Ok(res as u16)
    }

    /// Convert `self` to a `u32`. Returns `Err` if `self` is negative or out
    /// of range for `u32`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[cfg(not(windows))]
    /// # {
    /// assert_eq!(eval::<Fixnum>("4294967295").unwrap().to_u32().unwrap(), 4294967295);
    /// assert!(eval::<Fixnum>("4294967296").unwrap().to_u32().is_err());
    /// # }
    /// assert!(eval::<Fixnum>("-1").unwrap().to_u32().is_err());
    /// ```
    #[inline]
    pub fn to_u32(self) -> Result<u32, Error> {
        if self.is_negative() {
            return Err(Error::new(
                exception::range_error(),
                "can't convert negative integer to unsigned",
            ));
        }
        let res = self.to_isize();
        if res > u32::MAX as isize {
            return Err(Error::new(
                exception::range_error(),
                "fixnum too big to convert into `u32`",
            ));
        }
        Ok(res as u32)
    }

    /// Convert `self` to a `u64`. Returns `Err` if `self` is negative.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[cfg(not(windows))]
    /// assert_eq!(eval::<Fixnum>("4611686018427387903").unwrap().to_u64().unwrap(), 4611686018427387903);
    /// assert!(eval::<Fixnum>("-1").unwrap().to_u64().is_err());
    /// ```
    #[inline]
    pub fn to_u64(self) -> Result<u64, Error> {
        if self.is_negative() {
            return Err(Error::new(
                exception::range_error(),
                "can't convert negative integer to unsigned",
            ));
        }
        Ok(self.to_isize() as u64)
    }

    /// Convert `self` to a `usize`. Returns `Err` if `self` is negative or out
    /// of range for `usize`.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, Fixnum};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// # #[cfg(not(windows))]
    /// assert_eq!(eval::<Fixnum>("4611686018427387903").unwrap().to_usize().unwrap(), 4611686018427387903);
    /// assert!(eval::<Fixnum>("-1").unwrap().to_usize().is_err());
    /// ```
    #[inline]
    pub fn to_usize(self) -> Result<usize, Error> {
        if self.is_negative() {
            return Err(Error::new(
                exception::range_error(),
                "can't convert negative integer to unsigned",
            ));
        }
        Ok(self.to_isize() as usize)
    }
}

impl Deref for Fixnum {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        self.0.get_ref()
    }
}

impl fmt::Display for Fixnum {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for Fixnum {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl From<Fixnum> for Value {
    fn from(val: Fixnum) -> Self {
        *val
    }
}

unsafe impl private::ReprValue for Fixnum {
    fn to_value(self) -> Value {
        *self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        Self(NonZeroValue::new_unchecked(val))
    }
}

impl ReprValue for Fixnum {}

impl TryConvert for Fixnum {
    fn try_convert(val: Value) -> Result<Self, Error> {
        match val.try_convert::<Integer>()?.integer_type() {
            IntegerType::Fixnum(fix) => Ok(fix),
            IntegerType::Bignum(_) => Err(Error::new(
                exception::range_error(),
                "integer too big for fixnum",
            )),
        }
    }
}
impl TryConvertOwned for Fixnum {}

/// A static Ruby symbol that will live for the life of the program and never
/// be garbage collected.
///
/// See also [`Symbol`].
///
/// All [`Value`] methods should be available on this type through [`Deref`],
/// but some may be missed by this documentation.
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct StaticSymbol(NonZeroValue);

impl StaticSymbol {
    /// Return `Some(StaticSymbol)` if `val` is a `StaticSymbol`, `None`
    /// otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, StaticSymbol};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(StaticSymbol::from_value(eval(":foo").unwrap()).is_some());
    /// assert!(StaticSymbol::from_value(eval(r#""bar""#).unwrap()).is_none());
    /// assert!(StaticSymbol::from_value(eval(r#""baz".to_sym"#).unwrap()).is_none());
    /// ```
    #[inline]
    pub fn from_value(val: Value) -> Option<Self> {
        fn is_static_or_permanent_symbol(val: Value) -> bool {
            if val.is_static_symbol() {
                return true;
            }
            debug_assert_value!(val);
            if val.rb_type() != ruby_value_type::RUBY_T_SYMBOL {
                return false;
            }
            let mut p = val.as_rb_value();
            unsafe { rb_check_id(&mut p as *mut _) != 0 }
        }
        unsafe {
            is_static_or_permanent_symbol(val).then(|| Self(NonZeroValue::new_unchecked(val)))
        }
    }

    #[inline]
    pub(crate) unsafe fn from_rb_value_unchecked(val: VALUE) -> Self {
        Self(NonZeroValue::new_unchecked(Value::new(val)))
    }

    /// Create a new StaticSymbol.
    ///
    /// # Examples
    /// ```
    /// use magnus::{eval, StaticSymbol};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let sym = StaticSymbol::new("example");
    /// let result: bool = eval!(":example == sym", sym).unwrap();
    /// assert!(result);
    /// ```
    #[inline]
    pub fn new<T: Into<Id>>(name: T) -> Self {
        name.into().into()
    }

    /// Return the `Id` for `name`, if one exists.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{eval, StaticSymbol};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(StaticSymbol::check("example").is_none());
    /// eval::<StaticSymbol>(":example").unwrap();
    /// assert!(StaticSymbol::check("example").is_some());
    /// ```
    pub fn check(name: &str) -> Option<Self> {
        unsafe {
            let res = Value::new(rb_check_symbol_cstr(
                name.as_ptr() as *mut c_char,
                name.len() as c_long,
                RbEncoding::utf8().as_ptr(),
            ));
            (!res.is_nil()).then(|| Self::from_rb_value_unchecked(res.as_rb_value()))
        }
    }

    /// Return the symbol as a static string reference.
    ///
    /// May error if the name is not valid utf-8.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::StaticSymbol;
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let sym = StaticSymbol::new("example");
    /// assert_eq!(sym.name().unwrap(), "example");
    /// ```
    pub fn name(self) -> Result<&'static str, Error> {
        Id::from(self).name()
    }
}

impl Deref for StaticSymbol {
    type Target = Value;

    fn deref(&self) -> &Self::Target {
        self.0.get_ref()
    }
}

impl fmt::Display for StaticSymbol {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", unsafe { self.to_s_infallible() })
    }
}

impl fmt::Debug for StaticSymbol {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.inspect())
    }
}

impl EncodingCapable for StaticSymbol {}

impl From<Id> for StaticSymbol {
    fn from(id: Id) -> Self {
        unsafe { Self::from_rb_value_unchecked(rb_id2sym(id.as_rb_id())) }
    }
}

impl From<&str> for StaticSymbol {
    fn from(s: &str) -> Self {
        Id::from(s).into()
    }
}

impl From<String> for StaticSymbol {
    fn from(s: String) -> Self {
        Id::from(s).into()
    }
}

impl From<StaticSymbol> for Value {
    fn from(val: StaticSymbol) -> Self {
        *val
    }
}

unsafe impl private::ReprValue for StaticSymbol {
    fn to_value(self) -> Value {
        *self
    }

    unsafe fn from_value_unchecked(val: Value) -> Self {
        Self(NonZeroValue::new_unchecked(val))
    }
}

impl ReprValue for StaticSymbol {}

impl TryConvert for StaticSymbol {
    fn try_convert(val: Value) -> Result<Self, Error> {
        val.try_convert::<Symbol>().map(|s| s.to_static())
    }
}
impl TryConvertOwned for StaticSymbol {}

/// The internal value of a Ruby symbol.
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(transparent)]
pub struct Id(ID);

impl Id {
    pub(crate) fn new(id: ID) -> Self {
        Self(id)
    }

    pub(crate) fn as_rb_id(self) -> ID {
        self.0
    }

    /// Return the `Id` for `name`, if one exists.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::{value::{Id}, StaticSymbol};
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// assert!(Id::check("example").is_none());
    /// StaticSymbol::new("example");
    /// assert!(Id::check("example").is_some());
    /// ```
    pub fn check(name: &str) -> Option<Self> {
        let res = unsafe {
            rb_check_id_cstr(
                name.as_ptr() as *mut c_char,
                name.len() as c_long,
                RbEncoding::utf8().as_ptr(),
            )
        };
        (res != 0).then(|| Self::new(res))
    }

    /// Return the symbol name associated with this Id as a static string
    /// reference.
    ///
    /// May error if the name is not valid utf-8.
    ///
    /// # Examples
    ///
    /// ```
    /// use magnus::value::Id;
    /// # let _cleanup = unsafe { magnus::embed::init() };
    ///
    /// let id = Id::from("example");
    /// assert_eq!(id.name().unwrap(), "example");
    /// ```
    pub fn name(self) -> Result<&'static str, Error> {
        unsafe {
            let ptr = rb_id2name(self.as_rb_id());
            let cstr = CStr::from_ptr(ptr);
            cstr.to_str()
                .map_err(|e| Error::new(exception::encoding_error(), e.to_string()))
        }
    }
}

impl From<&str> for Id {
    fn from(s: &str) -> Self {
        Self::new(unsafe {
            rb_intern3(
                s.as_ptr() as *const c_char,
                s.len() as c_long,
                RbEncoding::utf8().as_ptr(),
            )
        })
    }
}

impl From<String> for Id {
    fn from(s: String) -> Self {
        s.as_str().into()
    }
}

impl From<StaticSymbol> for Id {
    fn from(sym: StaticSymbol) -> Self {
        Self::new(unsafe { rb_sym2id(sym.as_rb_value()) })
    }
}

impl From<Symbol> for Id {
    fn from(sym: Symbol) -> Self {
        Self::new(unsafe { rb_sym2id(sym.as_rb_value()) })
    }
}