element-ptr 0.0.2

A macro to make accessing elements through raw pointers easier.
Documentation
// the old declarative macro, staying around for no real reason

/// Returns the address of an inner element without created unneeded
/// intermediate references.
///
/// The general syntax is
/// ```
/// element_ptr!(base_ptr => /* element accesses */ )
/// ````
/// The possible element accesses are:
/// * `. $field`: Gets a pointer to the field specified by the field name
///     of the struct behind the pointer.
/// * `. $index`: Same as `. $field` but with a tuple index instead of a named struct field.
/// * `[ $index ]`: Gets an element from a pointer to an array or slice at the specified index.
/// * `+ $offset`: Equivalent to [`pointer::add()`]. See its documentation for more info.
/// * `- $offset`: Equivalent to [`pointer::sub()`]. See its documentation for more info.
/// * `u8+ $offset`: Equivalent to [`pointer::byte_add()`]. See its documentation for more info.
/// * `u8- $offset`: Equivalent to [`pointer::byte_sub()`]. See its documentation for more info.
/// * `as $type =>`: Casts the pointer to a pointer with a pointee type of `$type`.
///     If this is the last access within a group, the `=>` may be omitted.
/// * `( $accesses )`: Groups accesses. Has no effect on the order in which accesses are applied,
///     it just exists to allow for syntactic clarity.
/// * `.*`: [Reads] the value behind the pointer. This should generally only be used
///     for moving into a child pointer.
///
/// If some access returns a value that is not a pointer (meaning `.*` or a group containing it
/// as the last access), it will be a compiler error to have any accesses afterwards.
///
/// # Safety
/// * Every intermediate pointer and the final pointer must remain within the bounds of the same
///     allocated object. See [`pointer::offset()`] for more information.
/// * The `.*` element access unconditionally reads the value from memory.
///     See [`read()`] for more information.
/// * Aside from `.*`, all other element accesses do not read from the memory they are pointing to.
///     They also do not create intermediate references.
///
/// [Reads]: core::ptr::read
/// [`read()`]: core::ptr::read
/// [`pointer::add()`]: https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.add
/// [`pointer::sub()`]: https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.sub
/// [`pointer::byte_add()`]: https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.byte_add
/// [`pointer::byte_sub()`]: https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.byte_sub
/// [`pointer::offset()`]: https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.offset
#[macro_export]
#[cfg(not(doctest))] // just don't doctest any of these. Macros are way too hard to do.
macro_rules! element_ptr {
    (
        $ptr:expr => $($t:tt)*
    ) => {
        {
            $crate::helper::element_ptr_unsafe();
            #[allow(unused_unsafe)]
            unsafe { 'out: {
                let ptr = $crate::helper::new_pointer($ptr);
                $crate::__element_ptr_inner!('out ptr => $($t)*);
            } }
        }
    };
}

// $prev should always be a local variable with the type helper::Pointer<_, _>.
#[doc(hidden)]
#[macro_export]
macro_rules! __element_ptr_inner {
    ($out:lifetime $prev:ident => ) => {
        break $out $prev.into_inner();
    };

    ($out:lifetime $prev:ident => ( $($i:tt)* ) $($t:tt)* ) => {
        let val = 'out: {
            $crate::__element_ptr_inner!('out $prev => $($i)*);
        };
        $crate::__element_ptr_inner_dirty!($out val => $($t)*);
    };

    ($out:lifetime $prev:ident => + $add:tt $($t:tt)*) => {
        let ptr = $prev.add($add);
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };
    ($out:lifetime $prev:ident => - $add:tt $($t:tt)*) => {
        let ptr = $prev.sub($add);
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };

    ($out:lifetime $prev:ident => u8+ $add:tt $($t:tt)*) => {
        let ptr = $prev.byte_add($add);
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };
    ($out:lifetime $prev:ident => u8- $add:tt $($t:tt)*) => {
        let ptr = $prev.byte_sub($add);
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };

    // TODO: decide if casting should be included, and which one is better?.
    // ($out:lifetime $prev:ident => as $cast:ty) => {
    //     break $out $prev.cast::<$cast>().into_inner();
    // };

    ($out:lifetime $prev:ident => as $cast:ty => $($t:tt)*) => {
        let ptr = $prev.cast::<$cast>();
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };
    ($out:lifetime $prev:ident => as $cast:ty) => {
        let ptr = $prev.cast::<$cast>();
        $crate::__element_ptr_inner!($out ptr => );
    };

    // // a derefence of any value is fine, but if it continues
    // // then the derefence target needs to be a pointer.
    // ($out:lifetime $prev:ident => .* $($t:tt)*) => {
    //     break $out $prev.read();
    // };
    ($out:lifetime $prev:ident => .* $($t:tt)*) => {
        let ptr = $prev.read();
        $crate::__element_ptr_inner_dirty!($out ptr => $($t)*);
    };

    ($out:lifetime $prev:ident => . $id:ident $($t:tt)*) => {
        let ptr = $prev.copy_addr(
            ::core::ptr::addr_of!((* $prev.into_const() ) . $id)
        );
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };
    // this will place consecutive tuple accesses as
    // a floating point expression, but it still ends up being valid.
    // it is really sketchy however.
    ($out:lifetime $prev:ident => . $id:tt $($t:tt)*) => {
        let ptr = $prev.copy_addr(
            ::core::ptr::addr_of!((* $prev.into_const() ) . $id)
        );
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };

    ($out:lifetime $prev:ident => [ $idx:expr ] $($t:tt)*) => {
        let ptr = $crate::helper::index($prev, $idx);
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };
}

// this is called when $prev may or may not be a pointer.
#[macro_export]
macro_rules! __element_ptr_inner_dirty {
    // otherwise, return from the body with the value.
    ($out:lifetime $prev:ident => ) => {
        break $out $prev;
    };
    // if there are still tokens in the access,
    // verify that the returned value is a pointer, and call
    // the next access.
    ($out:lifetime $prev:ident => $($t:tt)+) => {
        let ptr = $crate::helper::new_pointer($prev);
        $crate::__element_ptr_inner!($out ptr => $($t)*);
    };
}