1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![feature(raw_ref_op)]
#![no_std]
#![doc = include_str!("../README.md")]

use core::{
    mem::{forget, zeroed},
    ptr::{copy_nonoverlapping, drop_in_place},
};

/// Holds the methods for manipulating arrays in a Vec-like fashion.
/// Will (probably) get into core when
/// [generic-const-exprs](https://doc.rust-lang.org/beta/unstable-book/language-features/generic-const-exprs.html)
/// becomes complete.
pub trait ArrayManipulation<T, const N: usize>: Sized {
    /// Takes an array of L elements and appends it at the end of Self.
    /// Performs 2 calls to `memcpy()`, so if your code heavily uses it
    /// maybe a linked list is a better fit for your use case.
    /// # Examples
    /// ```
    /// use array_manipulation::ArrayManipulation;
    ///
    /// let array: [u8; 4] = [1, 2, 3, 4];
    /// let expected = [1, 2, 3, 4, 5, 6, 7];
    /// let result = array.push([5, 6, 7]);
    /// assert_eq!(expected, result);
    /// ```
    fn push<const L: usize>(self, array: [T; L]) -> [T; N + L];

    /// Takes an array of L elements and appends it at the start of Self.
    /// Performs 2 calls to `memcpy()`, so if your code heavily uses it
    /// maybe a linked list is a better fit for your use case.
    /// # Examples
    /// ```
    /// use array_manipulation::ArrayManipulation;
    ///
    /// let array: [u8; 4] = [1, 2, 3, 4];
    /// let expected = [0, 1, 2, 3, 4];
    /// let result = array.push_back([0]);
    /// assert_eq!(expected, result);
    /// ```
    fn push_back<const L: usize>(self, array: [T; L]) -> [T; N + L];

    /// `memcpy()`s all the elements on an array except the last one.
    /// Basically it creates a new fixed-size array with all the
    /// elements except the last one. Won't compile if N == 0.
    /// # Examples
    /// ```
    /// use array_manipulation::ArrayManipulation;
    ///
    /// let array: [u8; 4] = [1, 2, 3, 4];
    /// let expected = [1, 2, 3];
    /// let result = array.pop();
    /// assert_eq!(expected, result);
    /// ```
    fn pop(self) -> [T; N - 1];

    /// `memcpy()`s all the elements on an array except the first one.
    /// Basically it creates a new fixed-size array with all the
    /// elements except the first one. Won't compile if N == 0.
    /// # Examples
    /// ```
    /// use array_manipulation::ArrayManipulation;
    ///
    /// let array: [u8; 4] = [1, 2, 3, 4];
    /// let expected = [2, 3, 4];
    /// let result = array.pop_back();
    /// assert_eq!(expected, result);
    /// ```
    fn pop_back(self) -> [T; N - 1];
}

impl<T, const N: usize> ArrayManipulation<T, N> for [T; N] {
    fn push<const L: usize>(self, array: [T; L]) -> [T; N + L] {
        unsafe {
            let mut result: [T; N + L] = zeroed(); // no real need to use MaybeUninit

            let dst = &raw mut result; // get ptr
            copy_nonoverlapping(&raw const self, dst.cast(), 1); // copy elements

            let dst = &raw mut result[N..]; // offset ptr by N
            copy_nonoverlapping(&raw const array, dst.cast(), 1); // copy elements

            // avoid drop & deallocation of the copied elements
            forget(self);
            forget(array);

            result
        }
    }

    fn push_back<const L: usize>(self, array: [T; L]) -> [T; N + L] {
        unsafe {
            let mut result: [T; N + L] = zeroed(); // no real need to use MaybeUninit

            let dst = &raw mut result; // get ptr
            copy_nonoverlapping(&raw const array, dst.cast(), 1); // copy elements

            let dst = &raw mut result[L..]; // offset ptr by L
            copy_nonoverlapping(&raw const self, dst.cast(), 1); // copy elements

            // avoid drop & deallocation of the copied elements
            forget(self);
            forget(array);

            result
        }
    }

    fn pop(mut self) -> [T; N - 1] {
        unsafe {
            let mut result: [T; N - 1] = zeroed(); // no real need to use MaybeUninit

            let src = &raw const self; // get ptr
            copy_nonoverlapping(src.cast(), &raw mut result, 1); // copy elements

            drop_in_place(&raw mut self[N - 1]); // drop popped element
            forget(self); // avoid drop & deallocation of the copied elements

            result
        }
    }

    fn pop_back(mut self) -> [T; N - 1] {
        unsafe {
            let mut result: [T; N - 1] = zeroed(); // no real need to use MaybeUninit

            let src = &raw const self[1..]; // offset ptr by size_of::<T>()
            copy_nonoverlapping(src.cast(), &raw mut result, 1); // copy elements

            drop_in_place(&raw mut self[0]); // drop popped element
            forget(self); // avoid drop & deallocation of the copied elements

            result
        }
    }
}