flac_codec/
byteorder.rs

1// Copyright 2025 Brian Langenberger
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Byte order for PCM samples
10
11/// Sample byte order
12pub trait Endianness: Clone {
13    /// Converts 8-bit sample to bytes in this byte order
14    fn i8_to_bytes(sample: i8) -> [u8; 1];
15
16    /// Converts 16-bit sample to bytes in this byte order
17    fn i16_to_bytes(sample: i16) -> [u8; 2];
18
19    /// Converts 24-bit sample to bytes in this byte order
20    fn i24_to_bytes(sample: i32) -> [u8; 3];
21
22    /// Converts 32-bit sample to bytes in this byte order
23    fn i32_to_bytes(sample: i32) -> [u8; 4];
24
25    /// Converts bytes to 8-bit samples in this byte order
26    fn bytes_to_i8(bytes: [u8; 1]) -> i8;
27
28    /// Converts bytes to 16-bit samples in this byte order
29    fn bytes_to_i16(bytes: [u8; 2]) -> i16;
30
31    /// Converts bytes to 24-bit samples in this byte order
32    fn bytes_to_i24(bytes: [u8; 3]) -> i32;
33
34    /// Converts bytes to 32-bit samples in this byte order
35    fn bytes_to_i32(bytes: [u8; 4]) -> i32;
36
37    /// Converts bytes in this byte order to big-endian
38    fn bytes_to_be(buf: &mut [u8], bytes_per_sample: usize);
39
40    /// Converts bytes in this byte order to little-endian
41    fn bytes_to_le(buf: &mut [u8], bytes_per_sample: usize);
42}
43
44/// Little-endian byte order
45#[derive(Copy, Clone)]
46pub struct LittleEndian;
47
48impl Endianness for LittleEndian {
49    #[inline]
50    fn i8_to_bytes(sample: i8) -> [u8; 1] {
51        sample.to_le_bytes()
52    }
53
54    #[inline]
55    fn i16_to_bytes(sample: i16) -> [u8; 2] {
56        sample.to_le_bytes()
57    }
58
59    #[inline]
60    fn i24_to_bytes(sample: i32) -> [u8; 3] {
61        let unsigned: u32 = if sample >= 0 {
62            sample as u32
63        } else {
64            0x800000 | ((sample - (-1 << 23)) as u32)
65        };
66
67        [
68            (unsigned & 0xFF) as u8,
69            ((unsigned & 0xFF00) >> 8) as u8,
70            (unsigned >> 16) as u8,
71        ]
72    }
73
74    #[inline]
75    fn i32_to_bytes(sample: i32) -> [u8; 4] {
76        sample.to_le_bytes()
77    }
78
79    #[inline]
80    fn bytes_to_i8(bytes: [u8; 1]) -> i8 {
81        i8::from_le_bytes(bytes)
82    }
83
84    #[inline]
85    fn bytes_to_i16(bytes: [u8; 2]) -> i16 {
86        i16::from_le_bytes(bytes)
87    }
88
89    #[inline]
90    fn bytes_to_i24(bytes: [u8; 3]) -> i32 {
91        let unsigned = ((bytes[2] as u32) << 16) | ((bytes[1] as u32) << 8) | bytes[0] as u32;
92
93        if unsigned & 0x800000 == 0 {
94            unsigned as i32
95        } else {
96            (unsigned & 0x7FFFFF) as i32 + (-1 << 23)
97        }
98    }
99
100    #[inline]
101    fn bytes_to_i32(bytes: [u8; 4]) -> i32 {
102        i32::from_le_bytes(bytes)
103    }
104
105    fn bytes_to_be(buf: &mut [u8], bytes_per_sample: usize) {
106        for chunk in buf.chunks_exact_mut(bytes_per_sample) {
107            chunk.reverse();
108        }
109    }
110
111    fn bytes_to_le(_buf: &mut [u8], _bytes_per_sample: usize) {
112        // already little-endian, so nothing to do
113    }
114}
115
116/// Big-endian byte order
117#[derive(Copy, Clone)]
118pub struct BigEndian;
119
120impl Endianness for BigEndian {
121    #[inline]
122    fn i8_to_bytes(sample: i8) -> [u8; 1] {
123        sample.to_be_bytes()
124    }
125
126    #[inline]
127    fn i16_to_bytes(sample: i16) -> [u8; 2] {
128        sample.to_be_bytes()
129    }
130
131    #[inline]
132    fn i24_to_bytes(sample: i32) -> [u8; 3] {
133        let unsigned: u32 = if sample >= 0 {
134            sample as u32
135        } else {
136            0x800000 | ((sample - (-1 << 23)) as u32)
137        };
138
139        [
140            (unsigned >> 16) as u8,
141            ((unsigned & 0xFF00) >> 8) as u8,
142            (unsigned & 0xFF) as u8,
143        ]
144    }
145
146    #[inline]
147    fn i32_to_bytes(sample: i32) -> [u8; 4] {
148        sample.to_be_bytes()
149    }
150
151    #[inline]
152    fn bytes_to_i8(bytes: [u8; 1]) -> i8 {
153        i8::from_be_bytes(bytes)
154    }
155
156    #[inline]
157    fn bytes_to_i16(bytes: [u8; 2]) -> i16 {
158        i16::from_be_bytes(bytes)
159    }
160
161    #[inline]
162    fn bytes_to_i24(bytes: [u8; 3]) -> i32 {
163        let unsigned = ((bytes[0] as u32) << 16) | ((bytes[1] as u32) << 8) | bytes[2] as u32;
164
165        if unsigned & 0x800000 == 0 {
166            unsigned as i32
167        } else {
168            (unsigned & 0x7FFFFF) as i32 + (-1 << 23)
169        }
170    }
171
172    #[inline]
173    fn bytes_to_i32(bytes: [u8; 4]) -> i32 {
174        i32::from_be_bytes(bytes)
175    }
176
177    fn bytes_to_be(_buf: &mut [u8], _bytes_per_sample: usize) {
178        // already big-endian, so nothing to do
179    }
180
181    fn bytes_to_le(buf: &mut [u8], bytes_per_sample: usize) {
182        for chunk in buf.chunks_exact_mut(bytes_per_sample) {
183            chunk.reverse();
184        }
185    }
186}
187
188#[allow(unused)]
189fn test_endianness<F: bitstream_io::Endianness, E: Endianness>() {
190    use bitstream_io::{BitWrite, BitWriter};
191
192    // 8 bits-per-sample to bytes
193    for i in i8::MIN..=i8::MAX {
194        let mut buf1 = [0; 1];
195        let mut w: BitWriter<_, F> = BitWriter::new(buf1.as_mut_slice());
196        w.write::<8, i8>(i).unwrap();
197
198        let buf2 = E::i8_to_bytes(i);
199
200        assert_eq!(buf1, buf2);
201
202        let j = E::bytes_to_i8(buf2);
203        assert_eq!(i, j);
204    }
205
206    // 16 bits-per-sample to bytes
207    for i in i16::MIN..=i16::MAX {
208        let mut buf1 = [0; 2];
209        let mut w: BitWriter<_, F> = BitWriter::new(buf1.as_mut_slice());
210        w.write::<16, i16>(i).unwrap();
211
212        let buf2 = E::i16_to_bytes(i);
213
214        assert_eq!(buf1, buf2);
215
216        let j = E::bytes_to_i16(buf2);
217        assert_eq!(i, j);
218    }
219
220    // 24 bits-per-sample to bytes
221    for i in (-1 << 23)..=((1 << 23) - 1) {
222        let mut buf1 = [0; 3];
223        let mut w: BitWriter<_, F> = BitWriter::new(buf1.as_mut_slice());
224        w.write::<24, i32>(i).unwrap();
225
226        let buf2 = E::i24_to_bytes(i);
227
228        assert_eq!(buf1, buf2);
229
230        let j = E::bytes_to_i24(buf2);
231        assert_eq!(i, j);
232    }
233}
234
235#[test]
236fn test_samples_le() {
237    test_endianness::<bitstream_io::LittleEndian, LittleEndian>()
238}
239
240#[test]
241fn test_samples_be() {
242    test_endianness::<bitstream_io::BigEndian, BigEndian>()
243}