sudachi/util/
cow_array.rs

1/*
2 *  Copyright (c) 2021-2024 Works Applications Co., Ltd.
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *   Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16
17use std::array::TryFromSliceError;
18use std::convert::TryInto;
19use std::ops::Deref;
20
21pub trait ReadLE {
22    fn from_le_bytes(bytes: &[u8]) -> Result<Self, TryFromSliceError>
23    where
24        Self: Sized;
25}
26
27impl ReadLE for i16 {
28    fn from_le_bytes(bytes: &[u8]) -> Result<Self, TryFromSliceError> {
29        bytes.try_into().map(Self::from_le_bytes)
30    }
31}
32
33impl ReadLE for u32 {
34    fn from_le_bytes(bytes: &[u8]) -> Result<Self, TryFromSliceError>
35    where
36        Self: Sized,
37    {
38        bytes.try_into().map(Self::from_le_bytes)
39    }
40}
41
42/// Copy-on-write array.
43///
44/// Is used for storing performance critical dictionary parts.
45/// `slice` is always valid, `storage` is used in owned mode.
46/// Unfortunately, `Cow<&[T]>` does not equal to `&[T]` in assembly:
47/// See: https://rust.godbolt.org/z/r4a9efjqh
48///
49/// It implements Deref for `&[T]`, so it can be used as slice.
50pub struct CowArray<'a, T> {
51    slice: &'a [T],
52    storage: Option<Vec<T>>,
53}
54
55impl<T: ReadLE + Clone> CowArray<'static, T> {
56    /// Creates from the owned data
57    pub fn from_owned<D: Into<Vec<T>>>(data: D) -> Self {
58        let data = data.into();
59        let slice1: &[T] = &data;
60        let slice: &'static [T] = unsafe { std::mem::transmute(slice1) };
61        Self {
62            storage: Some(data),
63            slice,
64        }
65    }
66}
67
68impl<'a, T: ReadLE + Clone> CowArray<'a, T> {
69    /// Create the CowArray from bytes, reinterpreting bytes as T.
70    ///
71    /// Original data may or not be aligned.
72    /// In non-aligned case, it makes a copy of the original data.
73    pub fn from_bytes(data: &'a [u8], offset: usize, size: usize) -> Self {
74        let align = std::mem::align_of::<T>();
75
76        let real_size = size * std::mem::size_of::<T>();
77        let real_slice = &data[offset..offset + real_size];
78        let ptr = real_slice.as_ptr() as *const T;
79        if is_aligned(ptr as usize, align) {
80            // SAFETY: ptr is aligned and trait bounds are ensuring so the type is sane
81            let reslice = unsafe { std::slice::from_raw_parts(ptr, size) };
82            Self {
83                slice: reslice,
84                storage: None,
85            }
86        } else {
87            let data = copy_of_bytes::<T>(real_slice);
88            let slice_1: &[T] = data.as_slice();
89            // we need transmute to make correct lifetime
90            // slice will always point to vector contents and it is impossible to have
91            // self-referential types in Rust yet
92            let slice: &'a [T] = unsafe { std::mem::transmute(slice_1) };
93            Self {
94                storage: Some(data),
95                slice,
96            }
97        }
98    }
99
100    /// Updates the value of the array
101    ///
102    /// Copies the data array if needed and updates it in place
103    /// Current implementation is not super for Rust because it
104    /// violates borrowing rules, but
105    /// 1. this object does not expose any references outside
106    /// 2. usage of data still follows the pattern 1-mut xor many-read
107    pub fn set(&mut self, offset: usize, value: T) {
108        if self.storage.is_none() {
109            self.storage = Some(self.slice.to_vec());
110            //refresh slice
111            let slice: &[T] = self.storage.as_ref().unwrap().as_slice();
112            self.slice = unsafe { std::mem::transmute(slice) };
113        }
114        if let Some(s) = self.storage.as_mut() {
115            s[offset] = value;
116        }
117    }
118}
119
120impl<'a, T> Deref for CowArray<'a, T> {
121    type Target = [T];
122
123    fn deref(&self) -> &Self::Target {
124        self.slice
125    }
126}
127
128fn is_aligned(offset: usize, alignment: usize) -> bool {
129    debug_assert!(alignment.is_power_of_two());
130    offset % alignment == 0
131}
132
133fn copy_of_bytes<T: ReadLE>(data: &[u8]) -> Vec<T> {
134    let size_t = std::mem::size_of::<T>();
135    assert_eq!(data.len() % size_t, 0);
136    let nelems = data.len() / size_t;
137    let mut result = Vec::with_capacity(nelems);
138    for i in (0..data.len()).step_by(size_t) {
139        let sl = &data[i..i + size_t];
140        result.push(T::from_le_bytes(sl).unwrap());
141    }
142    result
143}
144
145#[cfg(test)]
146mod test {
147    use super::*;
148
149    #[test]
150    fn aligned_1() {
151        assert!(is_aligned(0, 1));
152        assert!(is_aligned(1, 1));
153        assert!(is_aligned(2, 1));
154        assert!(is_aligned(3, 1));
155        assert!(is_aligned(4, 1));
156        assert!(is_aligned(5, 1));
157        assert!(is_aligned(6, 1));
158        assert!(is_aligned(7, 1));
159        assert!(is_aligned(8, 1));
160    }
161
162    #[test]
163    fn aligned_2() {
164        assert!(is_aligned(0, 2));
165        assert!(!is_aligned(1, 2));
166        assert!(is_aligned(2, 2));
167        assert!(!is_aligned(3, 2));
168        assert!(is_aligned(4, 2));
169        assert!(!is_aligned(5, 2));
170        assert!(is_aligned(6, 2));
171        assert!(!is_aligned(7, 2));
172        assert!(is_aligned(8, 2));
173    }
174
175    #[test]
176    fn aligned_4() {
177        assert!(is_aligned(0, 4));
178        assert!(!is_aligned(1, 4));
179        assert!(!is_aligned(2, 4));
180        assert!(!is_aligned(3, 4));
181        assert!(is_aligned(4, 4));
182        assert!(!is_aligned(5, 4));
183        assert!(!is_aligned(6, 4));
184        assert!(!is_aligned(7, 4));
185        assert!(is_aligned(8, 4));
186    }
187}