flacenc/
source.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Module for input source handling.
16
17use std::fmt;
18
19use md5::Digest;
20
21use super::arrayutils::deinterleave;
22use super::arrayutils::find_min_and_max;
23use super::arrayutils::le_bytes_to_i32s;
24use super::constant::MAX_BLOCK_SIZE;
25use super::constant::MAX_CHANNELS;
26use super::constant::MIN_BLOCK_SIZE;
27use super::error::verify_range;
28use super::error::verify_true;
29use super::error::SourceError;
30use super::error::VerifyError;
31
32/// Traits for buffer-like objects that can be filled by [`Source`].
33///
34/// An implementation of [`Source::read_samples`] is expected to call one
35/// of the `fill_*` method declared in this trait.
36///
37/// Note that arguments of `Fill` can be shorter than the actual block size
38/// (or `FrameBuf` size). An impl of `Fill` must accept the samples that is
39/// shorter than the pre-defined length. On the other hand, `Fill` is expected
40/// to return an error if the number of samples is larger than the block size.
41pub trait Fill {
42    /// Fills the target variable with the given interleaved samples.
43    ///
44    /// # Errors
45    ///
46    /// This may fail when configuration of `Fill` is not consistent withto the
47    /// input `interleaved` values.
48    ///
49    /// # Examples
50    ///
51    /// [`FrameBuf`] implements `Fill`.
52    ///
53    /// ```
54    /// # use flacenc::source::{Fill, FrameBuf};
55    /// let mut fb = FrameBuf::with_size(8, 1024).unwrap();
56    /// fb.fill_interleaved(&[0i32; 8 * 1024]);
57    /// ```
58    fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError>;
59
60    /// Fills target with the little-endian bytes that represent samples.
61    ///
62    /// # Errors
63    ///
64    /// This may fail when configuration of `Fill` is not consistent with the
65    /// input `bytes` or `bytes_per_sample` values.
66    ///
67    /// # Examples
68    ///
69    /// [`FrameBuf`] implements `Fill`.
70    ///
71    /// ```
72    /// # use flacenc::source::{Fill, FrameBuf};
73    /// let mut fb = FrameBuf::with_size(2, 64).unwrap();
74    /// // Note that `FrameBuf` (or `Fill` in general) accepts shorter inputs.
75    /// fb.fill_le_bytes(&[0x12, 0x34, 0x54, 0x76, 0x56, 0x78, 0x10, 0x32], 2);
76    /// // this FrameBuf now has 2 channels with elements:
77    /// //   - channel-1 (left) : [0x3412, 0x7856]
78    /// //   - channel-2 (right): [0x7654, 0x3210]
79    /// ```
80    fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError>;
81}
82
83impl<T: Fill, U: Fill> Fill for (T, U) {
84    fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
85        self.0.fill_interleaved(interleaved)?;
86        self.1.fill_interleaved(interleaved)
87    }
88
89    fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
90        self.0.fill_le_bytes(bytes, bytes_per_sample)?;
91        self.1.fill_le_bytes(bytes, bytes_per_sample)
92    }
93}
94
95impl<'a, T> Fill for &'a mut T
96where
97    T: Fill,
98{
99    fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
100        T::fill_interleaved(self, interleaved)
101    }
102
103    fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
104        T::fill_le_bytes(self, bytes, bytes_per_sample)
105    }
106}
107
108/// Reusable buffer for multi-channel framed signals.
109#[derive(Clone, Debug)]
110pub struct FrameBuf {
111    samples: Vec<i32>,
112    channels: usize,
113    size: usize,
114    readbuf: Vec<i32>, // only used when `read_le_bytes` is called
115}
116
117impl FrameBuf {
118    /// Constructs new 2-channel `FrameBuf` that will be later resized.
119    ///
120    /// This is a safe constructor that never fails, and always produce a valid
121    /// `FrameBuf`. This constructor is intended to be used for preparing
122    /// reusable buffer for stereo coding.
123    pub(crate) fn new_stereo_buffer() -> Self {
124        Self {
125            samples: vec![0i32; 256 * 2],
126            channels: 2,
127            size: 256,
128            readbuf: vec![],
129        }
130    }
131
132    /// Constructs `FrameBuf` of the specified size.
133    ///
134    /// # Errors
135    ///
136    /// Returns `VerifyError` if arguments are out of the ranges of FLAC
137    /// specifications.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// # use flacenc::source::FrameBuf;
143    /// let fb = FrameBuf::with_size(2, 1024).unwrap();
144    /// assert_eq!(fb.size(), 1024);
145    /// ```
146    pub fn with_size(channels: usize, size: usize) -> Result<Self, VerifyError> {
147        verify_range!("FrameBuf::with_size (channels)", channels, 1..=MAX_CHANNELS)?;
148        verify_range!(
149            "FrameBuf::with_size (block size)",
150            size,
151            MIN_BLOCK_SIZE..=MAX_BLOCK_SIZE
152        )?;
153        Ok(Self {
154            samples: vec![0i32; size * channels],
155            channels,
156            size,
157            readbuf: vec![],
158        })
159    }
160
161    /// Returns the size in the number of per-channel samples.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// # use flacenc::source::FrameBuf;
167    /// let fb = FrameBuf::with_size(2, 1024).unwrap();
168    /// assert_eq!(fb.size(), 1024);
169    /// ```
170    pub const fn size(&self) -> usize {
171        self.size
172    }
173
174    /// Resizes `FrameBuf`.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// # use flacenc::source::FrameBuf;
180    /// let mut fb = FrameBuf::with_size(2, 1024).unwrap();
181    /// assert_eq!(fb.size(), 1024);
182    /// fb.resize(2048);
183    /// assert_eq!(fb.size(), 2048);
184    /// ```
185    pub fn resize(&mut self, new_size: usize) {
186        self.size = new_size;
187        self.samples.resize(self.size * self.channels, 0i32);
188    }
189
190    /// Returns the number of channels
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// # use flacenc::source::FrameBuf;
196    /// let fb = FrameBuf::with_size(8, 1024).unwrap();
197    /// assert_eq!(fb.channels(), 8);
198    /// ```
199    pub const fn channels(&self) -> usize {
200        self.channels
201    }
202
203    /// Returns samples from the given channel.
204    pub(crate) fn channel_slice(&self, ch: usize) -> &[i32] {
205        &self.samples[ch * self.size..(ch + 1) * self.size]
206    }
207
208    /// Returns mutable samples from the given channel.
209    pub(crate) fn channel_slice_mut(&mut self, ch: usize) -> &mut [i32] {
210        &mut self.samples[ch * self.size..(ch + 1) * self.size]
211    }
212
213    /// Returns the internal representation of multichannel signals.
214    #[cfg(test)]
215    pub(crate) fn raw_slice(&self) -> &[i32] {
216        &self.samples
217    }
218
219    /// Verifies data consistency with the given stream info.
220    pub(crate) fn verify_samples(&self, bits_per_sample: usize) -> Result<(), VerifyError> {
221        let max_allowed = (1i32 << (bits_per_sample - 1)) - 1;
222        let min_allowed = -(1i32 << (bits_per_sample - 1));
223        for ch in 0..self.channels() {
224            let (min, max) = find_min_and_max::<64>(self.channel_slice(ch), 0i32);
225            if min < min_allowed || max > max_allowed {
226                return Err(VerifyError::new(
227                    "input.framebuf",
228                    &format!("input sample must be in the range of bits={bits_per_sample}"),
229                ));
230            }
231        }
232        Ok(())
233    }
234}
235
236impl Fill for FrameBuf {
237    fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
238        let stride = self.size();
239        deinterleave(interleaved, self.channels, stride, &mut self.samples);
240        Ok(())
241    }
242
243    fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
244        self.readbuf.resize(bytes.len() / bytes_per_sample, 0);
245        le_bytes_to_i32s(bytes, &mut self.readbuf, bytes_per_sample);
246
247        let stride = self.size();
248        deinterleave(&self.readbuf, self.channels, stride, &mut self.samples);
249        Ok(())
250    }
251}
252
253/// Context information being updated while reading from [`Source`].
254///
255/// Some information such as MD5 of the input waveform is better handled in
256/// [`Source`]-side rather than via frame buffers. `Context` is for handling
257/// such context variables.
258#[derive(Clone)]
259pub struct Context {
260    md5: md5::Md5,
261    bytes_per_sample: usize,
262    channels: usize,
263    sample_count: usize,
264    frame_count: usize,
265    current_block_size: usize,
266}
267
268impl Context {
269    /// Creates new context.
270    ///
271    /// # Panics
272    ///
273    /// Panics if `bits_per_sample > 32`.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// # use flacenc::source::Context;
279    /// let ctx = Context::new(16, 2, 4);
280    /// assert!(ctx.current_frame_number().is_none());;
281    /// assert_eq!(ctx.total_samples(), 0);
282    /// ```
283    pub fn new(bits_per_sample: usize, channels: usize, block_size: usize) -> Self {
284        let bytes_per_sample = (bits_per_sample + 7) / 8;
285        assert!(
286            bytes_per_sample <= 4,
287            "bits_per_sample={bits_per_sample} cannot be larger than 32."
288        );
289        Self {
290            md5: md5::Md5::new(),
291            bytes_per_sample,
292            channels,
293            sample_count: 0,
294            frame_count: 0,
295            current_block_size: block_size,
296        }
297    }
298
299    /// Returns bytes-per-sample configuration of this `Context`.
300    #[inline]
301    pub fn bytes_per_sample(&self) -> usize {
302        self.bytes_per_sample
303    }
304
305    /// Sets the block size for this `Context`.
306    ///
307    /// Only for variable-block-size encoding and therefore it is not used
308    /// currently.
309    #[allow(dead_code)]
310    pub(crate) fn set_current_block_size(&mut self, size: usize) {
311        self.current_block_size = size;
312    }
313
314    /// Returns the count of the last frame loaded.
315    ///
316    /// # Examples
317    ///
318    /// ```
319    /// # use flacenc::source::{Context, Fill};
320    /// let mut ctx = Context::new(16, 2, 16);
321    /// assert!(ctx.current_frame_number().is_none());
322    ///
323    /// ctx.fill_interleaved(&[0, -1, -2, 3]);
324    /// assert_eq!(ctx.current_frame_number(), Some(0usize));
325    /// ```
326    #[inline]
327    #[allow(clippy::unnecessary_lazy_evaluations)] // false-alarm
328    pub fn current_frame_number(&self) -> Option<usize> {
329        (self.frame_count > 0).then(|| self.frame_count - 1)
330    }
331
332    /// Returns MD5 digest of the consumed samples.
333    ///
334    /// # Examples
335    ///
336    /// ```
337    /// # use flacenc::source::Context;
338    /// let ctx = Context::new(16, 2, 128);
339    /// let zero_md5 = [
340    ///     0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,
341    ///     0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E,
342    /// ];
343    /// assert_eq!(ctx.md5_digest(), zero_md5);
344    /// // it doesn't change if you don't call "update" functions.
345    /// assert_eq!(ctx.md5_digest(), zero_md5);
346    /// ```
347    #[inline]
348    pub fn md5_digest(&self) -> [u8; 16] {
349        self.md5.clone().finalize().into()
350    }
351
352    /// Returns the number of samples loaded.
353    ///
354    /// # Examples
355    ///
356    /// ```
357    /// # use flacenc::source::{Context, Fill};
358    /// let mut ctx = Context::new(16, 2, 30);
359    ///
360    /// ctx.fill_interleaved(&[0, -1, -2, 3]);
361    /// assert_eq!(ctx.total_samples(), 2);
362    /// ```
363    #[inline]
364    pub fn total_samples(&self) -> usize {
365        self.sample_count
366    }
367}
368
369const ZEROS: [u8; 2048] = [0u8; 2048];
370
371impl Fill for Context {
372    fn fill_interleaved(&mut self, interleaved: &[i32]) -> Result<(), SourceError> {
373        if interleaved.is_empty() {
374            return Ok(());
375        }
376        for v in interleaved {
377            self.md5.update(&v.to_le_bytes()[0..self.bytes_per_sample]);
378        }
379        let remain_samples = self.current_block_size * self.channels - interleaved.len();
380        let mut remain = remain_samples * self.bytes_per_sample;
381        while remain > 0 {
382            let bytes = std::cmp::min(ZEROS.len(), remain);
383            let slice = &ZEROS[0..bytes];
384            self.md5.update(slice);
385            remain -= bytes;
386        }
387        self.sample_count += interleaved.len() / self.channels;
388        self.frame_count += 1;
389        Ok(())
390    }
391
392    fn fill_le_bytes(&mut self, bytes: &[u8], bytes_per_sample: usize) -> Result<(), SourceError> {
393        if bytes.is_empty() {
394            return Ok(());
395        }
396        self.md5.update(bytes);
397        let block_byte_count = self.current_block_size * self.channels * bytes_per_sample;
398        let mut remain = block_byte_count - bytes.len();
399        while remain > 0 {
400            let bytes = std::cmp::min(ZEROS.len(), remain);
401            let slice = &ZEROS[0..bytes];
402            self.md5.update(slice);
403            remain -= bytes;
404        }
405        self.sample_count += bytes.len() / self.channels / bytes_per_sample;
406        self.frame_count += 1;
407        Ok(())
408    }
409}
410
411impl fmt::Debug for Context {
412    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        let digest = format!("{:?}", self.md5.clone().finalize());
414        f.debug_struct("Context")
415            .field("bytes_per_sample", &self.bytes_per_sample)
416            .field("channels", &self.channels)
417            .field("sample_count", &self.sample_count)
418            .field("frame_count", &self.frame_count)
419            .field("md5", &digest)
420            .field("current_block_size", &self.current_block_size)
421            .finish()
422    }
423}
424
425/// Trait representing the input source for the encoder.
426pub trait Source {
427    /// Returns the number of channels.
428    fn channels(&self) -> usize;
429    /// Returns the number of bits per sample;
430    fn bits_per_sample(&self) -> usize;
431    /// Returns sampling rate in Hz.
432    fn sample_rate(&self) -> usize;
433    /// Reads samples to [`T: Fill`](Fill).
434    ///
435    /// Implementation of this function must call either
436    /// [`dest.fill_interleaved`](Fill::fill_interleaved) or
437    /// [`dest.fill_le_bytes`](Fill::fill_le_bytes),
438    /// and returns the number of per-channel samples read.
439    ///
440    /// # Errors
441    ///
442    /// This function can return [`SourceError`] when read is failed.
443    fn read_samples<F: Fill>(
444        &mut self,
445        block_size: usize,
446        dest: &mut F,
447    ) -> Result<usize, SourceError>;
448    /// Returns length of source if it's finite and defined.
449    fn len_hint(&self) -> Option<usize> {
450        None
451    }
452}
453
454impl<'a, T: Source> Source for &'a mut T {
455    fn channels(&self) -> usize {
456        T::channels(self)
457    }
458    fn bits_per_sample(&self) -> usize {
459        T::bits_per_sample(self)
460    }
461    fn sample_rate(&self) -> usize {
462        T::sample_rate(self)
463    }
464    fn read_samples<F: Fill>(
465        &mut self,
466        block_size: usize,
467        dest: &mut F,
468    ) -> Result<usize, SourceError> {
469        T::read_samples(self, block_size, dest)
470    }
471    fn len_hint(&self) -> Option<usize> {
472        T::len_hint(self)
473    }
474}
475
476/// Trait representing seekable variant of [`Source`].
477///
478/// This trait is not currently used in the encoder, but some encoding algorithm
479/// in future may require that a source is seekable.
480pub trait Seekable: Source {
481    /// Returns `true` if the source contains no samples.
482    fn is_empty(&self) -> bool {
483        self.len() == 0
484    }
485
486    /// Returns the length in per-channel samples
487    fn len(&self) -> usize;
488
489    /// Seeks to the specified offset from the beginning.
490    ///
491    /// # Errors
492    ///
493    /// This function can return [`SourceError`] when read is failed.
494    fn read_samples_from<F: Fill>(
495        &mut self,
496        offset: usize,
497        block_size: usize,
498        context: &mut F,
499    ) -> Result<usize, SourceError>;
500}
501
502impl<'a, T: Seekable> Seekable for &'a mut T {
503    fn is_empty(&self) -> bool {
504        T::is_empty(self)
505    }
506
507    fn len(&self) -> usize {
508        T::len(self)
509    }
510
511    fn read_samples_from<F: Fill>(
512        &mut self,
513        offset: usize,
514        block_size: usize,
515        context: &mut F,
516    ) -> Result<usize, SourceError> {
517        T::read_samples_from(self, offset, block_size, context)
518    }
519}
520
521/// Source with preloaded samples.
522#[derive(Clone, Debug)]
523#[allow(clippy::module_name_repetitions)]
524pub struct MemSource {
525    channels: usize,
526    bits_per_sample: usize,
527    sample_rate: usize,
528    samples: Vec<i32>,
529    read_head: usize,
530}
531
532impl MemSource {
533    /// Constructs `MemSource` from samples.
534    ///
535    /// # Examples
536    ///
537    /// ```
538    /// use flacenc::source::MemSource;
539    ///
540    /// let src = MemSource::from_samples(&[0, 0, 1, -1, 2, -2, 3, -3], 2, 16, 8000);
541    /// assert_eq!(src.as_slice(), &[0, 0, 1, -1, 2, -2, 3, -3]);
542    /// ```
543    pub fn from_samples(
544        samples: &[i32],
545        channels: usize,
546        bits_per_sample: usize,
547        sample_rate: usize,
548    ) -> Self {
549        Self {
550            channels,
551            bits_per_sample,
552            sample_rate,
553            samples: samples.to_owned(),
554            read_head: 0,
555        }
556    }
557
558    /// Returns sample buffer as a raw slice.
559    ///
560    /// # Examples
561    ///
562    /// ```
563    /// # use flacenc::source::MemSource;
564    /// let src = MemSource::from_samples(&[0, 0, 1, -1, 2, -2, 3, -3], 2, 16, 8000);
565    /// assert_eq!(src.as_slice(), &[0, 0, 1, -1, 2, -2, 3, -3]);
566    /// ```
567    pub fn as_slice(&self) -> &[i32] {
568        &self.samples
569    }
570}
571
572impl Source for MemSource {
573    fn channels(&self) -> usize {
574        self.channels
575    }
576
577    fn bits_per_sample(&self) -> usize {
578        self.bits_per_sample
579    }
580
581    fn sample_rate(&self) -> usize {
582        self.sample_rate
583    }
584
585    fn read_samples<F: Fill>(
586        &mut self,
587        block_size: usize,
588        dest: &mut F,
589    ) -> Result<usize, SourceError> {
590        self.read_samples_from(self.read_head, block_size, dest)
591    }
592
593    fn len_hint(&self) -> Option<usize> {
594        Some(self.len())
595    }
596}
597
598impl Seekable for MemSource {
599    fn len(&self) -> usize {
600        self.samples.len() / self.channels()
601    }
602
603    fn read_samples_from<F: Fill>(
604        &mut self,
605        offset: usize,
606        block_size: usize,
607        dest: &mut F,
608    ) -> Result<usize, SourceError> {
609        let to_read = block_size * self.channels;
610        let begin = std::cmp::min(offset * self.channels, self.samples.len());
611        let end = std::cmp::min(offset * self.channels + to_read, self.samples.len());
612        let src = &self.samples[begin..end];
613
614        dest.fill_interleaved(src)?;
615
616        let read_samples = (end - begin) / self.channels;
617        self.read_head += read_samples;
618        Ok(read_samples)
619    }
620}
621
622#[cfg(test)]
623#[allow(clippy::pedantic, clippy::nursery, clippy::needless_range_loop)]
624mod tests {
625    use super::*;
626
627    #[test]
628    fn reading_and_deinterleaving() {
629        let mut signal = vec![];
630        let block_size = 512;
631        let channels = 4;
632        for t in 0..block_size {
633            for _ch in 0..channels {
634                signal.push(t as i32);
635            }
636        }
637
638        let mut src = MemSource::from_samples(&signal, channels, 16, 16000);
639        let mut framebuf_and_ctx = (
640            FrameBuf::with_size(channels, block_size).unwrap(),
641            Context::new(16, channels, block_size),
642        );
643        let read = src
644            .read_samples_from(0, block_size, &mut framebuf_and_ctx)
645            .expect("Read error");
646        assert_eq!(read, block_size);
647        let (framebuf, _ctx) = framebuf_and_ctx;
648        let mut head = 0;
649        for _ch in 0..channels {
650            for t in 0..block_size {
651                assert_eq!(framebuf.raw_slice()[head], t as i32);
652                head += 1;
653            }
654        }
655    }
656
657    #[test]
658    fn sequential_read() {
659        let mut signal = vec![];
660        let total_size = 1100;
661        let channels = 3;
662        for t in 0..total_size {
663            for ch in 0..channels {
664                let sign: i32 = if ch == 0 { 1 } else { -1 };
665                signal.push(sign * t);
666            }
667        }
668
669        let block_size = 128;
670        let mut src = MemSource::from_samples(&signal, channels, 16, 16000);
671        let ctx = Context::new(16, channels, block_size);
672        let framebuf = FrameBuf::with_size(channels, block_size).unwrap();
673        let mut framebuf_and_ctx = (framebuf, ctx);
674
675        for step in 0..8 {
676            let read = src
677                .read_samples(block_size, &mut framebuf_and_ctx)
678                .expect("Read error");
679            assert_eq!(read, 128);
680            assert_eq!(src.read_head, 128 * (step + 1));
681            for t in 0..block_size {
682                assert_eq!(
683                    framebuf_and_ctx.0.channel_slice(0)[t],
684                    (block_size * step + t) as i32
685                );
686                assert_eq!(
687                    framebuf_and_ctx.0.channel_slice(1)[t],
688                    -((block_size * step + t) as i32)
689                );
690            }
691        }
692        let read = src
693            .read_samples(block_size, &mut framebuf_and_ctx)
694            .expect("Read error");
695        assert_eq!(read, 76);
696        for t in 0..76 {
697            assert_eq!(framebuf_and_ctx.0.channel_slice(0)[t], (1024 + t) as i32);
698            assert_eq!(framebuf_and_ctx.0.channel_slice(1)[t], -((1024 + t) as i32));
699            assert_eq!(framebuf_and_ctx.0.channel_slice(2)[t], -((1024 + t) as i32));
700        }
701    }
702
703    #[test]
704    fn md5_computation() {
705        let mut ctx = Context::new(16, 2, 32);
706        ctx.fill_interleaved(&[0i32; 32 * 2])
707            .expect("update failed");
708
709        // Reference computed with Python's hashlib.
710        assert_eq!(
711            ctx.md5_digest(),
712            [
713                0xF0, 0x9F, 0x35, 0xA5, 0x63, 0x78, 0x39, 0x45, 0x8E, 0x46, 0x2E, 0x63, 0x50, 0xEC,
714                0xBC, 0xE4
715            ]
716        );
717
718        let mut ctx = Context::new(16, 2, 32);
719        ctx.fill_interleaved(&[0xABCDi32; 32 * 2])
720            .expect("update failed");
721        // Reference computed by a reliable version of this library.
722        assert_eq!(
723            ctx.md5_digest(),
724            [
725                0x02, 0x3D, 0x3A, 0xE9, 0x26, 0x0B, 0xB0, 0xC9, 0x51, 0xF6, 0x5B, 0x25, 0x24, 0x62,
726                0xB1, 0xFA
727            ]
728        );
729    }
730}
731
732#[cfg(all(test, feature = "simd-nightly"))]
733mod bench {
734    use super::*;
735
736    extern crate test;
737
738    use test::bench::Bencher;
739    use test::black_box;
740
741    #[bench]
742    fn feeding_bytes_to_context(b: &mut Bencher) {
743        let (bytes_per_sample, channels, block_size) = (2, 2, 4096);
744        let mut ctx = Context::new(bytes_per_sample, channels, block_size);
745        let signal_bytes = vec![0u8; bytes_per_sample * channels * block_size];
746        b.iter(|| ctx.fill_le_bytes(black_box(&signal_bytes), bytes_per_sample));
747    }
748}