fn test_align_offset() {
    let d = Box::new([0u32; 4]);
    // Get u8 pointer to base
    let raw = d.as_ptr() as *const u8;

    assert_eq!(raw.align_offset(2), 0);
    assert_eq!(raw.align_offset(4), 0);
    assert_eq!(raw.align_offset(8), usize::MAX); // requested alignment higher than allocation alignment

    assert_eq!(raw.wrapping_offset(1).align_offset(2), 1);
    assert_eq!(raw.wrapping_offset(1).align_offset(4), 3);
    assert_eq!(raw.wrapping_offset(1).align_offset(8), usize::MAX); // requested alignment higher than allocation alignment

    assert_eq!(raw.wrapping_offset(2).align_offset(2), 0);
    assert_eq!(raw.wrapping_offset(2).align_offset(4), 2);
    assert_eq!(raw.wrapping_offset(2).align_offset(8), usize::MAX); // requested alignment higher than allocation alignment
}

fn test_align_to() {
    const N: usize = 4;
    let d = Box::new([0u32; N]);
    // Get u8 slice covering the entire thing
    let s = unsafe { std::slice::from_raw_parts(d.as_ptr() as *const u8, 4 * N) };
    let raw = s.as_ptr();

    {
        let (l, m, r) = unsafe { s.align_to::<u32>() };
        assert_eq!(l.len(), 0);
        assert_eq!(r.len(), 0);
        assert_eq!(m.len(), N);
        assert_eq!(raw, m.as_ptr() as *const u8);
    }

    {
        let (l, m, r) = unsafe { s[1..].align_to::<u32>() };
        assert_eq!(l.len(), 3);
        assert_eq!(m.len(), N-1);
        assert_eq!(r.len(), 0);
        assert_eq!(raw.wrapping_offset(4), m.as_ptr() as *const u8);
    }

    {
        let (l, m, r) = unsafe { s[..4*N - 1].align_to::<u32>() };
        assert_eq!(l.len(), 0);
        assert_eq!(m.len(), N-1);
        assert_eq!(r.len(), 3);
        assert_eq!(raw, m.as_ptr() as *const u8);
    }

    {
        let (l, m, r) = unsafe { s[1..4*N - 1].align_to::<u32>() };
        assert_eq!(l.len(), 3);
        assert_eq!(m.len(), N-2);
        assert_eq!(r.len(), 3);
        assert_eq!(raw.wrapping_offset(4), m.as_ptr() as *const u8);
    }

    {
        #[repr(align(8))] struct Align8(u64);
        let (l, m, r) = unsafe { s.align_to::<Align8>() }; // requested alignment higher than allocation alignment
        assert_eq!(l.len(), 4*N);
        assert_eq!(r.len(), 0);
        assert_eq!(m.len(), 0);
    }
}

fn test_from_utf8() {
    const N: usize = 10;
    let vec = vec![0x4141414141414141u64; N];
    let content = unsafe { std::slice::from_raw_parts(vec.as_ptr() as *const u8, 8 * N) };
    println!("{:?}", std::str::from_utf8(content).unwrap());
}

fn test_u64_array() {
    #[derive(Default)]
    #[repr(align(8))]
    struct AlignToU64<T>(T);

    const BYTE_LEN: usize = std::mem::size_of::<[u64; 4]>();
    type Data = AlignToU64<[u8; BYTE_LEN]>;

    fn example(data: &Data) {
        let (head, u64_arrays, tail) = unsafe { data.0.align_to::<[u64; 4]>() };

        assert!(head.is_empty(), "buffer was not aligned for 64-bit numbers");
        assert_eq!(u64_arrays.len(), 1, "buffer was not long enough");
        assert!(tail.is_empty(), "buffer was too long");

        let u64_array = &u64_arrays[0];
        let _val = u64_array[0]; // make sure we can actually load this
    }

    example(&Data::default());
}

fn main() {
    test_align_offset();
    test_align_to();
    test_from_utf8();
    test_u64_array();
}