<commit_before>use crate::core::META_FILEPATH;
use crate::directory::error::{DeleteError, OpenReadError, OpenWriteError};
use crate::directory::AntiCallToken;
use crate::directory::WatchCallbackList;
use crate::directory::{Directory, ReadOnlySource, WatchCallback, WatchHandle};
use crate::directory::{TerminatingWrite, WritePtr};
use fail::fail_point;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, BufWriter, Cursor, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::result;
use std::sync::{Arc, RwLock};

/// Writer associated with the `RAMDirectory`
///
/// The Writer just writes a buffer.
///
/// # Panics
///
/// On drop, if the writer was left in a *dirty* state.
/// That is, if flush was not called after the last call
/// to write.
///
struct VecWriter {
    path: PathBuf,
    shared_directory: RAMDirectory,
    data: Cursor<Vec<u8>>,
    is_flushed: bool,
}

impl VecWriter {
    fn new(path_buf: PathBuf, shared_directory: RAMDirectory) -> VecWriter {
        VecWriter {
            path: path_buf,
            data: Cursor::new(Vec::new()),
            shared_directory,
            is_flushed: true,
        }
    }
}

impl Drop for VecWriter {
    fn drop(&mut self) {
        if !self.is_flushed {
            panic!(
                "You forgot to flush {:?} before its writter got Drop. Do not rely on drop.",
                self.path
            )
        }
    }
}

impl Seek for VecWriter {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        self.data.seek(pos)
    }
}

impl Write for VecWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.is_flushed = false;
        self.data.write_all(buf)?;
        Ok(buf.len())
    }

    fn flush(&mut self) -> io::Result<()> {
        self.is_flushed = true;
        let mut fs = self.shared_directory.fs.write().unwrap();
        fs.write(self.path.clone(), self.data.get_ref());
        Ok(())
    }
}

impl TerminatingWrite for VecWriter {
    fn terminate_ref(&mut self, _: AntiCallToken) -> io::Result<()> {
        self.flush()
    }
}

#[derive(Default)]
struct InnerDirectory {
    fs: HashMap<PathBuf, ReadOnlySource>,
    watch_router: WatchCallbackList,
}

impl InnerDirectory {
    fn write(&mut self, path: PathBuf, data: &[u8]) -> bool {
        let data = ReadOnlySource::new(Vec::from(data));
        self.fs.insert(path, data).is_some()
    }

    fn open_read(&self, path: &Path) -> Result<ReadOnlySource, OpenReadError> {
        self.fs
            .get(path)
            .ok_or_else(|| OpenReadError::FileDoesNotExist(PathBuf::from(path)))
            .map(Clone::clone)
    }

    fn delete(&mut self, path: &Path) -> result::Result<(), DeleteError> {
        match self.fs.remove(path) {
            Some(_) => Ok(()),
            None => Err(DeleteError::FileDoesNotExist(PathBuf::from(path))),
        }
    }

    fn exists(&self, path: &Path) -> bool {
        self.fs.contains_key(path)
    }

    fn watch(&mut self, watch_handle: WatchCallback) -> WatchHandle {
        self.watch_router.subscribe(watch_handle)
    }

    fn total_mem_usage(&self) -> usize {
        self.fs.values().map(|f| f.len()).sum()
    }
}

impl fmt::Debug for RAMDirectory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "RAMDirectory")
    }
}

/// A Directory storing everything in anonymous memory.
///
/// It is mainly meant for unit testing.
/// Writes are only made visible upon flushing.
///
#[derive(Clone, Default)]
pub struct RAMDirectory {
    fs: Arc<RwLock<InnerDirectory>>,
}

impl RAMDirectory {
    /// Constructor
    pub fn create() -> RAMDirectory {
        Self::default()
    }

    /// Returns the sum of the size of the different files
    /// in the RAMDirectory.
    pub fn total_mem_usage(&self) -> usize {
        self.fs.read().unwrap().total_mem_usage()
    }

    /// Write a copy of all of the files saved in the RAMDirectory in the target `Directory`.
    ///
    /// Files are all written using the `Directory::write` meaning, even if they were
    /// written using the `atomic_write` api.
    ///
    /// If an error is encounterred, files may be persisted partially.
    pub fn persist(&self, dest: &mut dyn Directory) -> crate::Result<()> {
        let wlock = self.fs.write().unwrap();
        for (path, source) in wlock.fs.iter() {
            let mut dest_wrt = dest.open_write(path)?;
            dest_wrt.write_all(source.as_slice())?;
            dest_wrt.terminate()?;
        }
        Ok(())
    }
}

impl Directory for RAMDirectory {
    fn open_read(&self, path: &Path) -> result::Result<ReadOnlySource, OpenReadError> {
        self.fs.read().unwrap().open_read(path)
    }

    fn delete(&self, path: &Path) -> result::Result<(), DeleteError> {
        fail_point!("RAMDirectory::delete", |_| {
            use crate::directory::error::IOError;
            let io_error = IOError::from(io::Error::from(io::ErrorKind::Other));
            Err(DeleteError::from(io_error))
        });
        self.fs.write().unwrap().delete(path)
    }

    fn exists(&self, path: &Path) -> bool {
        self.fs.read().unwrap().exists(path)
    }

    fn open_write(&mut self, path: &Path) -> Result<WritePtr, OpenWriteError> {
        let mut fs = self.fs.write().unwrap();
        let path_buf = PathBuf::from(path);
        let vec_writer = VecWriter::new(path_buf.clone(), self.clone());
        let exists = fs.write(path_buf.clone(), &[]);
        // force the creation of the file to mimic the MMap directory.
        if exists {
            Err(OpenWriteError::FileAlreadyExists(path_buf))
        } else {
            Ok(BufWriter::new(Box::new(vec_writer)))
        }
    }

    fn atomic_read(&self, path: &Path) -> Result<Vec<u8>, OpenReadError> {
        Ok(self.open_read(path)?.as_slice().to_owned())
    }

    fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
        fail_point!("RAMDirectory::atomic_write", |msg| Err(io::Error::new(
            io::ErrorKind::Other,
            msg.unwrap_or_else(|| "Undefined".to_string())
        )));
        let path_buf = PathBuf::from(path);

        // Reserve the path to prevent calls to .write() to succeed.
        self.fs.write().unwrap().write(path_buf.clone(), &[]);

        let mut vec_writer = VecWriter::new(path_buf, self.clone());
        vec_writer.write_all(data)?;
        vec_writer.flush()?;
        if path == Path::new(&*META_FILEPATH) {
            let _ = self.fs.write().unwrap().watch_router.broadcast();
        }
        Ok(())
    }

    fn watch(&self, watch_callback: WatchCallback) -> crate::Result<WatchHandle> {
        Ok(self.fs.write().unwrap().watch(watch_callback))
    }
}

#[cfg(test)]
mod tests {
    use super::RAMDirectory;
    use crate::Directory;
    use std::path::Path;
    use std::io::Write;

    #[test]
    fn test_persist() {
        let msg_atomic: &'static [u8] = b"atomic is the way";
        let msg_seq: &'static [u8] = b"sequential is the way";
        let path_atomic: &'static Path = Path::new("atomic");
        let path_seq: &'static Path = Path::new("seq");
        let mut directory = RAMDirectory::create();
        assert!(directory.atomic_write(path_atomic, msg_atomic).is_ok());
        let mut wrt = directory.open_write(path_seq).unwrap();
        assert!(wrt.write_all(msg_seq).is_ok());
        assert!(wrt.flush().is_ok());
        let mut directory_copy = RAMDirectory::create();
        assert!(directory.persist(&mut directory_copy).is_ok());
        assert_eq!(directory_copy.atomic_read(path_atomic).unwrap(), msg_atomic);
        assert_eq!(directory_copy.atomic_read(path_seq).unwrap(), msg_seq);
    }
}
<commit_msg>cargo fmt<commit_after>use crate::core::META_FILEPATH;
use crate::directory::error::{DeleteError, OpenReadError, OpenWriteError};
use crate::directory::AntiCallToken;
use crate::directory::WatchCallbackList;
use crate::directory::{Directory, ReadOnlySource, WatchCallback, WatchHandle};
use crate::directory::{TerminatingWrite, WritePtr};
use fail::fail_point;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, BufWriter, Cursor, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::result;
use std::sync::{Arc, RwLock};

/// Writer associated with the `RAMDirectory`
///
/// The Writer just writes a buffer.
///
/// # Panics
///
/// On drop, if the writer was left in a *dirty* state.
/// That is, if flush was not called after the last call
/// to write.
///
struct VecWriter {
    path: PathBuf,
    shared_directory: RAMDirectory,
    data: Cursor<Vec<u8>>,
    is_flushed: bool,
}

impl VecWriter {
    fn new(path_buf: PathBuf, shared_directory: RAMDirectory) -> VecWriter {
        VecWriter {
            path: path_buf,
            data: Cursor::new(Vec::new()),
            shared_directory,
            is_flushed: true,
        }
    }
}

impl Drop for VecWriter {
    fn drop(&mut self) {
        if !self.is_flushed {
            panic!(
                "You forgot to flush {:?} before its writter got Drop. Do not rely on drop.",
                self.path
            )
        }
    }
}

impl Seek for VecWriter {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        self.data.seek(pos)
    }
}

impl Write for VecWriter {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.is_flushed = false;
        self.data.write_all(buf)?;
        Ok(buf.len())
    }

    fn flush(&mut self) -> io::Result<()> {
        self.is_flushed = true;
        let mut fs = self.shared_directory.fs.write().unwrap();
        fs.write(self.path.clone(), self.data.get_ref());
        Ok(())
    }
}

impl TerminatingWrite for VecWriter {
    fn terminate_ref(&mut self, _: AntiCallToken) -> io::Result<()> {
        self.flush()
    }
}

#[derive(Default)]
struct InnerDirectory {
    fs: HashMap<PathBuf, ReadOnlySource>,
    watch_router: WatchCallbackList,
}

impl InnerDirectory {
    fn write(&mut self, path: PathBuf, data: &[u8]) -> bool {
        let data = ReadOnlySource::new(Vec::from(data));
        self.fs.insert(path, data).is_some()
    }

    fn open_read(&self, path: &Path) -> Result<ReadOnlySource, OpenReadError> {
        self.fs
            .get(path)
            .ok_or_else(|| OpenReadError::FileDoesNotExist(PathBuf::from(path)))
            .map(Clone::clone)
    }

    fn delete(&mut self, path: &Path) -> result::Result<(), DeleteError> {
        match self.fs.remove(path) {
            Some(_) => Ok(()),
            None => Err(DeleteError::FileDoesNotExist(PathBuf::from(path))),
        }
    }

    fn exists(&self, path: &Path) -> bool {
        self.fs.contains_key(path)
    }

    fn watch(&mut self, watch_handle: WatchCallback) -> WatchHandle {
        self.watch_router.subscribe(watch_handle)
    }

    fn total_mem_usage(&self) -> usize {
        self.fs.values().map(|f| f.len()).sum()
    }
}

impl fmt::Debug for RAMDirectory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "RAMDirectory")
    }
}

/// A Directory storing everything in anonymous memory.
///
/// It is mainly meant for unit testing.
/// Writes are only made visible upon flushing.
///
#[derive(Clone, Default)]
pub struct RAMDirectory {
    fs: Arc<RwLock<InnerDirectory>>,
}

impl RAMDirectory {
    /// Constructor
    pub fn create() -> RAMDirectory {
        Self::default()
    }

    /// Returns the sum of the size of the different files
    /// in the RAMDirectory.
    pub fn total_mem_usage(&self) -> usize {
        self.fs.read().unwrap().total_mem_usage()
    }

    /// Write a copy of all of the files saved in the RAMDirectory in the target `Directory`.
    ///
    /// Files are all written using the `Directory::write` meaning, even if they were
    /// written using the `atomic_write` api.
    ///
    /// If an error is encounterred, files may be persisted partially.
    pub fn persist(&self, dest: &mut dyn Directory) -> crate::Result<()> {
        let wlock = self.fs.write().unwrap();
        for (path, source) in wlock.fs.iter() {
            let mut dest_wrt = dest.open_write(path)?;
            dest_wrt.write_all(source.as_slice())?;
            dest_wrt.terminate()?;
        }
        Ok(())
    }
}

impl Directory for RAMDirectory {
    fn open_read(&self, path: &Path) -> result::Result<ReadOnlySource, OpenReadError> {
        self.fs.read().unwrap().open_read(path)
    }

    fn delete(&self, path: &Path) -> result::Result<(), DeleteError> {
        fail_point!("RAMDirectory::delete", |_| {
            use crate::directory::error::IOError;
            let io_error = IOError::from(io::Error::from(io::ErrorKind::Other));
            Err(DeleteError::from(io_error))
        });
        self.fs.write().unwrap().delete(path)
    }

    fn exists(&self, path: &Path) -> bool {
        self.fs.read().unwrap().exists(path)
    }

    fn open_write(&mut self, path: &Path) -> Result<WritePtr, OpenWriteError> {
        let mut fs = self.fs.write().unwrap();
        let path_buf = PathBuf::from(path);
        let vec_writer = VecWriter::new(path_buf.clone(), self.clone());
        let exists = fs.write(path_buf.clone(), &[]);
        // force the creation of the file to mimic the MMap directory.
        if exists {
            Err(OpenWriteError::FileAlreadyExists(path_buf))
        } else {
            Ok(BufWriter::new(Box::new(vec_writer)))
        }
    }

    fn atomic_read(&self, path: &Path) -> Result<Vec<u8>, OpenReadError> {
        Ok(self.open_read(path)?.as_slice().to_owned())
    }

    fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
        fail_point!("RAMDirectory::atomic_write", |msg| Err(io::Error::new(
            io::ErrorKind::Other,
            msg.unwrap_or_else(|| "Undefined".to_string())
        )));
        let path_buf = PathBuf::from(path);

        // Reserve the path to prevent calls to .write() to succeed.
        self.fs.write().unwrap().write(path_buf.clone(), &[]);

        let mut vec_writer = VecWriter::new(path_buf, self.clone());
        vec_writer.write_all(data)?;
        vec_writer.flush()?;
        if path == Path::new(&*META_FILEPATH) {
            let _ = self.fs.write().unwrap().watch_router.broadcast();
        }
        Ok(())
    }

    fn watch(&self, watch_callback: WatchCallback) -> crate::Result<WatchHandle> {
        Ok(self.fs.write().unwrap().watch(watch_callback))
    }
}

#[cfg(test)]
mod tests {
    use super::RAMDirectory;
    use crate::Directory;
    use std::io::Write;
    use std::path::Path;

    #[test]
    fn test_persist() {
        let msg_atomic: &'static [u8] = b"atomic is the way";
        let msg_seq: &'static [u8] = b"sequential is the way";
        let path_atomic: &'static Path = Path::new("atomic");
        let path_seq: &'static Path = Path::new("seq");
        let mut directory = RAMDirectory::create();
        assert!(directory.atomic_write(path_atomic, msg_atomic).is_ok());
        let mut wrt = directory.open_write(path_seq).unwrap();
        assert!(wrt.write_all(msg_seq).is_ok());
        assert!(wrt.flush().is_ok());
        let mut directory_copy = RAMDirectory::create();
        assert!(directory.persist(&mut directory_copy).is_ok());
        assert_eq!(directory_copy.atomic_read(path_atomic).unwrap(), msg_atomic);
        assert_eq!(directory_copy.atomic_read(path_seq).unwrap(), msg_seq);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019-2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;

use crate::context::av1_get_coded_tx_size;
use crate::cpu_features::CpuFeatureLevel;
use crate::quantize::*;
use crate::transform::TxSize;
use crate::util::*;

type DequantizeFn = unsafe fn(
  qindex: u8,
  coeffs_ptr: *const i16,
  _eob: usize,
  rcoeffs_ptr: *mut i16,
  tx_size: TxSize,
  bit_depth: usize,
  dc_delta_q: i8,
  ac_delta_q: i8,
);

cpu_function_lookup_table!(
  DEQUANTIZE_FNS: [Option<DequantizeFn>],
  default: None,
  [(AVX2, Some(dequantize_avx2))]
);

#[inline(always)]
pub fn dequantize<T: Coefficient>(
  qindex: u8, coeffs: &[T], eob: usize, rcoeffs: &mut [T], tx_size: TxSize,
  bit_depth: usize, dc_delta_q: i8, ac_delta_q: i8, cpu: CpuFeatureLevel,
) {
  let call_rust = |rcoeffs: &mut [T]| {
    crate::quantize::rust::dequantize(
      qindex, coeffs, eob, rcoeffs, tx_size, bit_depth, dc_delta_q,
      ac_delta_q, cpu,
    );
  };

  #[cfg(any(feature = "check_asm", test))]
  let ref_rcoeffs = {
    let area = av1_get_coded_tx_size(tx_size).area();
    let mut copy = vec![T::cast_from(0); area];
    copy[..].copy_from_slice(&rcoeffs[..area]);
    call_rust(&mut copy);
    copy
  };

  match T::Pixel::type_enum() {
    PixelType::U8 => {
      if let Some(func) = DEQUANTIZE_FNS[cpu.as_index()] {
        unsafe {
          (func)(
            qindex,
            coeffs.as_ptr() as *const _,
            eob,
            rcoeffs.as_mut_ptr() as *mut _,
            tx_size,
            bit_depth,
            dc_delta_q,
            ac_delta_q,
          )
        }
      } else {
        call_rust(rcoeffs)
      }
    }
    PixelType::U16 => call_rust(rcoeffs),
  }

  #[cfg(any(feature = "check_asm", test))]
  {
    let area = av1_get_coded_tx_size(tx_size).area();
    assert_eq!(&rcoeffs[..area], &ref_rcoeffs[..]);
  }
}

#[target_feature(enable = "avx2")]
unsafe fn dequantize_avx2(
  qindex: u8, coeffs_ptr: *const i16, _eob: usize, rcoeffs_ptr: *mut i16,
  tx_size: TxSize, bit_depth: usize, dc_delta_q: i8, ac_delta_q: i8,
) {
  let log_tx_scale = _mm256_set1_epi32(get_log_tx_scale(tx_size) as i32);

  let quants_ac =
    _mm256_set1_epi32(ac_q(qindex, ac_delta_q, bit_depth) as i32);
  // Use the dc quantize as first vector element for the first iteration
  let mut quants = _mm256_insert_epi32(
    quants_ac,
    dc_q(qindex, dc_delta_q, bit_depth) as i32,
    0,
  );

  let area: usize = av1_get_coded_tx_size(tx_size).area();
  // Step by 16 (256/16) coefficients for each iteration
  let step: usize = 16;
  assert!(area >= step);

  // Increase the pointers as we iterate
  let mut coeffs_ptr: *const i16 = coeffs_ptr;
  let mut rcoeffs_ptr: *mut i16 = rcoeffs_ptr;
  for _i in (0..area).step_by(step) {
    let coeffs = _mm256_load_si256(coeffs_ptr as *const _);
    let coeffs_abs = _mm256_abs_epi16(coeffs);

    // TODO: Since log_tx_scale is at most 2 and we gain an extra bit by taking
    // the abs value (unless the range is [-(2^15), 2^15 + 1]), it might be
    // possible to perform a 16-bit multiply and get the highest bit by
    // comparing coeffs to (1<<16) / quant. The would cost 1 compare, 1 blend,
    // and 1 add, but would get rid of 1 pack, 2 unpacks, 1 shift, and 1
    // multiply.

    let rcoeffs = _mm256_sign_epi16(
      _mm256_packs_epi32(
        // (abs_coeff * quant) >> log_tx_scale
        _mm256_srlv_epi32(
          _mm256_madd_epi16(
            quants,
            // Convert the first half of each lane to 32-bits
            _mm256_unpacklo_epi16(coeffs_abs, _mm256_setzero_si256()),
          ),
          log_tx_scale,
        ),
        // Second half
        _mm256_srlv_epi32(
          _mm256_madd_epi16(
            quants_ac,
            _mm256_unpackhi_epi16(coeffs_abs, _mm256_setzero_si256()),
          ),
          log_tx_scale,
        ),
      ),
      coeffs,
    );
    _mm256_store_si256(rcoeffs_ptr as *mut _, rcoeffs);

    // Only use a dc quantizer for the first iteration
    quants = quants_ac;
    coeffs_ptr = coeffs_ptr.add(step);
    rcoeffs_ptr = rcoeffs_ptr.add(step);
  }
}

#[cfg(test)]
mod test {
  use super::*;
  use rand::{thread_rng, Rng};

  #[test]
  fn dequantize_test() {
    let mut rng = thread_rng();

    use TxSize::*;
    let tx_sizes = [
      TX_4X4, TX_8X8, TX_16X16, TX_32X32, TX_64X64, TX_4X8, TX_8X4, TX_8X16,
      TX_16X8, TX_16X32, TX_32X16, TX_32X64, TX_64X32, TX_4X16, TX_16X4,
      TX_8X32, TX_32X8, TX_16X64, TX_64X16,
    ];

    let bd: usize = 8;

    for &tx_size in &tx_sizes {
      let qindex: u8 = rng.gen_range((MINQ as u8)..(MAXQ as u8));
      let dc_quant: i16 = dc_q(qindex, 0, bd);
      let ac_quant: i16 = ac_q(qindex, 0, bd);

      // Test the min, max, and random eobs
      let eobs = {
        let mut out = [0usize; 16];
        let area: usize = av1_get_coded_tx_size(tx_size).area();
        out[0] = 0;
        out[1] = area;
        for eob in out.iter_mut().skip(2) {
          *eob = rng.gen_range(0..area);
        }
        out
      };

      for &eob in &eobs {
        let mut qcoeffs = Aligned::new([0i16; 32 * 32]);
        let mut rcoeffs = Aligned::new([0i16; 32 * 32]);

        // Generate quantized coefficients upto the eob
        for (i, qcoeff) in qcoeffs.data.iter_mut().enumerate().take(eob) {
          *qcoeff =
            rng.gen::<i16>() / if i == 0 { dc_quant } else { ac_quant };
        }

        // Rely on quantize's internal tests
        dequantize(
          qindex,
          &qcoeffs.data,
          eob,
          &mut rcoeffs.data,
          tx_size,
          bd,
          0,
          0,
          CpuFeatureLevel::default(),
        );
      }
    }
  }
}
<commit_msg>Fix intermittent dequantize test failure<commit_after>// Copyright (c) 2019-2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;

use crate::context::av1_get_coded_tx_size;
use crate::cpu_features::CpuFeatureLevel;
use crate::quantize::*;
use crate::transform::TxSize;
use crate::util::*;

type DequantizeFn = unsafe fn(
  qindex: u8,
  coeffs_ptr: *const i16,
  _eob: usize,
  rcoeffs_ptr: *mut i16,
  tx_size: TxSize,
  bit_depth: usize,
  dc_delta_q: i8,
  ac_delta_q: i8,
);

cpu_function_lookup_table!(
  DEQUANTIZE_FNS: [Option<DequantizeFn>],
  default: None,
  [(AVX2, Some(dequantize_avx2))]
);

#[inline(always)]
pub fn dequantize<T: Coefficient>(
  qindex: u8, coeffs: &[T], eob: usize, rcoeffs: &mut [T], tx_size: TxSize,
  bit_depth: usize, dc_delta_q: i8, ac_delta_q: i8, cpu: CpuFeatureLevel,
) {
  let call_rust = |rcoeffs: &mut [T]| {
    crate::quantize::rust::dequantize(
      qindex, coeffs, eob, rcoeffs, tx_size, bit_depth, dc_delta_q,
      ac_delta_q, cpu,
    );
  };

  #[cfg(any(feature = "check_asm", test))]
  let ref_rcoeffs = {
    let area = av1_get_coded_tx_size(tx_size).area();
    let mut copy = vec![T::cast_from(0); area];
    copy[..].copy_from_slice(&rcoeffs[..area]);
    call_rust(&mut copy);
    copy
  };

  match T::Pixel::type_enum() {
    PixelType::U8 => {
      if let Some(func) = DEQUANTIZE_FNS[cpu.as_index()] {
        unsafe {
          (func)(
            qindex,
            coeffs.as_ptr() as *const _,
            eob,
            rcoeffs.as_mut_ptr() as *mut _,
            tx_size,
            bit_depth,
            dc_delta_q,
            ac_delta_q,
          )
        }
      } else {
        call_rust(rcoeffs)
      }
    }
    PixelType::U16 => call_rust(rcoeffs),
  }

  #[cfg(any(feature = "check_asm", test))]
  {
    let area = av1_get_coded_tx_size(tx_size).area();
    assert_eq!(&rcoeffs[..area], &ref_rcoeffs[..]);
  }
}

#[target_feature(enable = "avx2")]
unsafe fn dequantize_avx2(
  qindex: u8, coeffs_ptr: *const i16, _eob: usize, rcoeffs_ptr: *mut i16,
  tx_size: TxSize, bit_depth: usize, dc_delta_q: i8, ac_delta_q: i8,
) {
  let log_tx_scale = _mm256_set1_epi32(get_log_tx_scale(tx_size) as i32);

  let quants_ac =
    _mm256_set1_epi32(ac_q(qindex, ac_delta_q, bit_depth) as i32);
  // Use the dc quantize as first vector element for the first iteration
  let mut quants = _mm256_insert_epi32(
    quants_ac,
    dc_q(qindex, dc_delta_q, bit_depth) as i32,
    0,
  );

  let area: usize = av1_get_coded_tx_size(tx_size).area();
  // Step by 16 (256/16) coefficients for each iteration
  let step: usize = 16;
  assert!(area >= step);

  // Increase the pointers as we iterate
  let mut coeffs_ptr: *const i16 = coeffs_ptr;
  let mut rcoeffs_ptr: *mut i16 = rcoeffs_ptr;
  for _i in (0..area).step_by(step) {
    let coeffs = _mm256_load_si256(coeffs_ptr as *const _);
    let coeffs_abs = _mm256_abs_epi16(coeffs);

    // TODO: Since log_tx_scale is at most 2 and we gain an extra bit by taking
    // the abs value (unless the range is [-(2^15), 2^15 + 1]), it might be
    // possible to perform a 16-bit multiply and get the highest bit by
    // comparing coeffs to (1<<16) / quant. The would cost 1 compare, 1 blend,
    // and 1 add, but would get rid of 1 pack, 2 unpacks, 1 shift, and 1
    // multiply.

    let rcoeffs = _mm256_sign_epi16(
      _mm256_packs_epi32(
        // (abs_coeff * quant) >> log_tx_scale
        _mm256_srlv_epi32(
          _mm256_madd_epi16(
            quants,
            // Convert the first half of each lane to 32-bits
            _mm256_unpacklo_epi16(coeffs_abs, _mm256_setzero_si256()),
          ),
          log_tx_scale,
        ),
        // Second half
        _mm256_srlv_epi32(
          _mm256_madd_epi16(
            quants_ac,
            _mm256_unpackhi_epi16(coeffs_abs, _mm256_setzero_si256()),
          ),
          log_tx_scale,
        ),
      ),
      coeffs,
    );
    _mm256_store_si256(rcoeffs_ptr as *mut _, rcoeffs);

    // Only use a dc quantizer for the first iteration
    quants = quants_ac;
    coeffs_ptr = coeffs_ptr.add(step);
    rcoeffs_ptr = rcoeffs_ptr.add(step);
  }
}

#[cfg(test)]
mod test {
  use super::*;
  use rand::distributions::{Distribution, Uniform};
  use rand::{thread_rng, Rng};

  #[test]
  fn dequantize_test() {
    let mut rng = thread_rng();

    use TxSize::*;
    let tx_sizes = [
      TX_4X4, TX_8X8, TX_16X16, TX_32X32, TX_64X64, TX_4X8, TX_8X4, TX_8X16,
      TX_16X8, TX_16X32, TX_32X16, TX_32X64, TX_64X32, TX_4X16, TX_16X4,
      TX_8X32, TX_32X8, TX_16X64, TX_64X16,
    ];

    let bd: usize = 8;

    for &tx_size in &tx_sizes {
      let qindex: u8 = rng.gen_range((MINQ as u8)..(MAXQ as u8));
      let dc_quant: i16 = dc_q(qindex, 0, bd);
      let ac_quant: i16 = ac_q(qindex, 0, bd);

      // Test the min, max, and random eobs
      let eobs = {
        let mut out = [0usize; 16];
        let area: usize = av1_get_coded_tx_size(tx_size).area();
        out[0] = 0;
        out[1] = area;
        for eob in out.iter_mut().skip(2) {
          *eob = rng.gen_range(0..area);
        }
        out
      };

      for &eob in &eobs {
        let mut qcoeffs = Aligned::new([0i16; 32 * 32]);
        let mut rcoeffs = Aligned::new([0i16; 32 * 32]);

        // Generate quantized coefficients upto the eob
        let between = Uniform::from(-std::i16::MAX..=std::i16::MAX);
        for (i, qcoeff) in qcoeffs.data.iter_mut().enumerate().take(eob) {
          *qcoeff = between.sample(&mut rng)
            / if i == 0 { dc_quant } else { ac_quant };
        }

        // Rely on quantize's internal tests
        dequantize(
          qindex,
          &qcoeffs.data,
          eob,
          &mut rcoeffs.data,
          tx_size,
          bd,
          0,
          0,
          CpuFeatureLevel::default(),
        );
      }
    }
  }
}
<|endoftext|>


<commit_before>#![cfg(all(unix, feature = "io_uring"))]

use libc::off_t;
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use crate::LogOffset;
use crate::{Error, Result};

use liburing::*;

/// TODO: support for SQPOLL with idle timeout
pub(crate) struct Uring<T> {
    /// Mutable unsafe FFI struct from liburing::
    ring: io_uring,

    /// File
    file: Arc<File>,

    /// IOVec structures that hold information about buffers
    /// used in SQEs.
    iovecs: Vec<libc::iovec>,

    /// iovec buffs. this is specifically needed to preserve buf
    /// ptr lifetime because all write operations are async.
    buf: Vec<Option<Arc<T>>>,

    /// List of free slots: iovecs, user data, etc.
    free_slots: Vec<usize>,
}

/// Pointers can't be passed safely through threads boundaries.
/// `Uring` must enforce it, yet it is not Sync.
#[allow(unsafe_code)]
unsafe impl<T> Send for Uring<T> {}

impl<T> Uring<T> {
    /// Create and initialize new `io_uring` structure with
    /// `size` queue length and `flags` specified properties.
    ///
    /// Available flags from `io_uring.h`:
    /// - `IORING_SETUP_IOPOLL`	(1U << 0)	/* `io_context` is polled */
    /// - `IORING_SETUP_SQPOLL`	(1U << 1)	/* SQ poll thread */
    /// - `IORING_SETUP_SQ_AFF`	(1U << 2)	/* `sq_thread_cpu` is valid */
    /// - `IORING_SETUP_CQSIZE`	(1U << 3)	/* app defines CQ size */
    pub fn new(
        file: Arc<File>,
        size: usize,
        flags: libc::c_uint,
    ) -> Result<Self> {
        if size & 1 != 0 || size < 2 {
            return Err(Error::Unsupported("invalid queue size".into()));
        }

        let ring = unsafe {
            let mut s = mem::MaybeUninit::<io_uring>::uninit();
            let ret = io_uring_queue_init(
                u32::try_from(size).unwrap(),
                s.as_mut_ptr(),
                flags,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }

            s.assume_init()
        };

        let mut uring = Uring::<T> {
            ring,
            file,
            iovecs: Vec::with_capacity(size),
            buf: Vec::with_capacity(size),
            free_slots: Vec::with_capacity(size),
        };

        unsafe {
            let ret = io_uring_register_files(
                &mut uring.ring,
                [uring.file.as_raw_fd()].as_ptr(),
                1,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        uring.iovecs.resize(
            size,
            libc::iovec { iov_base: std::ptr::null_mut(), iov_len: 0 },
        );
        uring.buf.resize(size, None);

        // index free elements
        for i in 0..size {
            uring.free_slots.push(i);
        }

        Ok(uring)
    }

    unsafe fn drain_cqe(&mut self) -> Result<()> {
        loop {
            let mut cqe: *mut io_uring_cqe = std::ptr::null_mut();
            let ret = io_uring_peek_cqe(&mut self.ring, &mut cqe);
            if ret == -libc::EAGAIN {
                // peek found nothing, no completions to drain
                return Ok(());
            }
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
            let i = usize::try_from((*cqe).user_data).unwrap();
            if (*cqe).res < 0 {
                // ignore failed fsyncs with invalid argument due to the
                // overlap with executing ones: 2 drain fsync do not exec twice.
                if (*cqe).res != -libc::EINVAL
                    || i >= self.free_slots.capacity()
                    || self.iovecs[i].iov_len != 0
                {
                    return Err(Error::Io(io::Error::from_raw_os_error(
                        -(*cqe).res,
                    )));
                }
            }
            if i < self.free_slots.capacity() {
                // println!("release i = {}", i);
                self.free_slots.push(i);
                // release
                self.iovecs[i].iov_base = std::ptr::null_mut();
                self.iovecs[i].iov_len = 0;
                // drop arc buf
                self.buf[i] = None;
            }
            io_uring_cqe_seen(&mut self.ring, cqe);
        }
    }

    pub fn pwrite_all(
        &mut self,
        buf: &mut [u8],
        data: Arc<T>,
        offset: LogOffset,
        fsync: bool,
    ) -> Result<()> {
        unsafe {
            // 1. drain completed operations
            self.drain_cqe()?;

            // it is easier to track queue len if num
            // of iovecs will be the same as sqes
            let mut reserve = 1;
            if fsync {
                reserve += 1;
            }

            // 2. reserve SQE slots
            if self.free_slots.len() < reserve {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    libc::c_uint::try_from(reserve).unwrap(),
                );
                if ret < 0 {
                    return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
                self.drain_cqe()?;
                if self.free_slots.len() < reserve {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "wait for free sqes",
                    )));
                }
            }

            // 3. build write() SQE
            {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                self.buf[i] = Some(data);
                self.iovecs[i].iov_base =
                    buf.as_mut_ptr() as *mut std::ffi::c_void;
                self.iovecs[i].iov_len = buf.len();

                io_uring_prep_writev(
                    sqe,
                    // for registered file at index 0
                    // self.file.as_raw_fd(),
                    0,
                    &self.iovecs[i],
                    1,
                    off_t::try_from(offset).unwrap(),
                );
                io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
                // println!("write with i = {}", i);
            }

            // 4. build fsync() SQE
            if fsync {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                // mark explicitly that this is fsync op and it has no data.
                // drain_cqe must ignore fsyncs that got EINVAL due to the
                // overlap with the existing executing fsyncs.
                self.iovecs[i].iov_len = 0;

                io_uring_prep_fsync(
                    sqe,
                    // for registered file at index 0
                    // self.file.as_raw_fd(),
                    0,
                    IORING_FSYNC_DATASYNC,
                );
                // io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN | IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
                // println!("fsync with i = {}", i);
            }

            // 5. submit
            let ret = io_uring_submit(&mut self.ring);
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        Ok(())
    }
}

impl<T> Drop for Uring<T> {
    fn drop(&mut self) {
        unsafe {
            // flush unfinished ops
            if self.free_slots.len() < self.iovecs.len() {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    u32::try_from(self.iovecs.len() - self.free_slots.len())
                        .unwrap(),
                );
                if ret < 0 {
                    panic!(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
            }

            self.drain_cqe().unwrap();

            // auto on exit: io_uring_unregister_files(&mut self.ring);
            io_uring_queue_exit(&mut self.ring);
        };
    }
}
<commit_msg>Remove more tabs from comments<commit_after>#![cfg(all(unix, feature = "io_uring"))]

use libc::off_t;
use std::convert::TryFrom;
use std::fs::File;
use std::io;
use std::mem;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;

use crate::LogOffset;
use crate::{Error, Result};

use liburing::*;

/// TODO: support for SQPOLL with idle timeout
pub(crate) struct Uring<T> {
    /// Mutable unsafe FFI struct from liburing::
    ring: io_uring,

    /// File
    file: Arc<File>,

    /// IOVec structures that hold information about buffers
    /// used in SQEs.
    iovecs: Vec<libc::iovec>,

    /// iovec buffs. this is specifically needed to preserve buf
    /// ptr lifetime because all write operations are async.
    buf: Vec<Option<Arc<T>>>,

    /// List of free slots: iovecs, user data, etc.
    free_slots: Vec<usize>,
}

/// Pointers can't be passed safely through threads boundaries.
/// `Uring` must enforce it, yet it is not Sync.
#[allow(unsafe_code)]
unsafe impl<T> Send for Uring<T> {}

impl<T> Uring<T> {
    /// Create and initialize new `io_uring` structure with
    /// `size` queue length and `flags` specified properties.
    ///
    /// Available flags from `io_uring.h`:
    /// - `IORING_SETUP_IOPOLL`	(1U << 0)  /* `io_context` is polled */
    /// - `IORING_SETUP_SQPOLL`	(1U << 1)  /* SQ poll thread */
    /// - `IORING_SETUP_SQ_AFF`	(1U << 2)  /* `sq_thread_cpu` is valid */
    /// - `IORING_SETUP_CQSIZE`	(1U << 3)  /* app defines CQ size */
    pub fn new(
        file: Arc<File>,
        size: usize,
        flags: libc::c_uint,
    ) -> Result<Self> {
        if size & 1 != 0 || size < 2 {
            return Err(Error::Unsupported("invalid queue size".into()));
        }

        let ring = unsafe {
            let mut s = mem::MaybeUninit::<io_uring>::uninit();
            let ret = io_uring_queue_init(
                u32::try_from(size).unwrap(),
                s.as_mut_ptr(),
                flags,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }

            s.assume_init()
        };

        let mut uring = Uring::<T> {
            ring,
            file,
            iovecs: Vec::with_capacity(size),
            buf: Vec::with_capacity(size),
            free_slots: Vec::with_capacity(size),
        };

        unsafe {
            let ret = io_uring_register_files(
                &mut uring.ring,
                [uring.file.as_raw_fd()].as_ptr(),
                1,
            );
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        uring.iovecs.resize(
            size,
            libc::iovec { iov_base: std::ptr::null_mut(), iov_len: 0 },
        );
        uring.buf.resize(size, None);

        // index free elements
        for i in 0..size {
            uring.free_slots.push(i);
        }

        Ok(uring)
    }

    unsafe fn drain_cqe(&mut self) -> Result<()> {
        loop {
            let mut cqe: *mut io_uring_cqe = std::ptr::null_mut();
            let ret = io_uring_peek_cqe(&mut self.ring, &mut cqe);
            if ret == -libc::EAGAIN {
                // peek found nothing, no completions to drain
                return Ok(());
            }
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
            let i = usize::try_from((*cqe).user_data).unwrap();
            if (*cqe).res < 0 {
                // ignore failed fsyncs with invalid argument due to the
                // overlap with executing ones: 2 drain fsync do not exec twice.
                if (*cqe).res != -libc::EINVAL
                    || i >= self.free_slots.capacity()
                    || self.iovecs[i].iov_len != 0
                {
                    return Err(Error::Io(io::Error::from_raw_os_error(
                        -(*cqe).res,
                    )));
                }
            }
            if i < self.free_slots.capacity() {
                // println!("release i = {}", i);
                self.free_slots.push(i);
                // release
                self.iovecs[i].iov_base = std::ptr::null_mut();
                self.iovecs[i].iov_len = 0;
                // drop arc buf
                self.buf[i] = None;
            }
            io_uring_cqe_seen(&mut self.ring, cqe);
        }
    }

    pub fn pwrite_all(
        &mut self,
        buf: &mut [u8],
        data: Arc<T>,
        offset: LogOffset,
        fsync: bool,
    ) -> Result<()> {
        unsafe {
            // 1. drain completed operations
            self.drain_cqe()?;

            // it is easier to track queue len if num
            // of iovecs will be the same as sqes
            let mut reserve = 1;
            if fsync {
                reserve += 1;
            }

            // 2. reserve SQE slots
            if self.free_slots.len() < reserve {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    libc::c_uint::try_from(reserve).unwrap(),
                );
                if ret < 0 {
                    return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
                self.drain_cqe()?;
                if self.free_slots.len() < reserve {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "wait for free sqes",
                    )));
                }
            }

            // 3. build write() SQE
            {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                self.buf[i] = Some(data);
                self.iovecs[i].iov_base =
                    buf.as_mut_ptr() as *mut std::ffi::c_void;
                self.iovecs[i].iov_len = buf.len();

                io_uring_prep_writev(
                    sqe,
                    // for registered file at index 0
                    // self.file.as_raw_fd(),
                    0,
                    &self.iovecs[i],
                    1,
                    off_t::try_from(offset).unwrap(),
                );
                io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
                // println!("write with i = {}", i);
            }

            // 4. build fsync() SQE
            if fsync {
                let sqe = io_uring_get_sqe(&mut self.ring);
                if sqe.is_null() {
                    return Err(Error::Io(io::Error::new(
                        io::ErrorKind::Other,
                        "unexpected lack of sqes",
                    )));
                }

                let i = self.free_slots.pop().unwrap();
                // mark explicitly that this is fsync op and it has no data.
                // drain_cqe must ignore fsyncs that got EINVAL due to the
                // overlap with the existing executing fsyncs.
                self.iovecs[i].iov_len = 0;

                io_uring_prep_fsync(
                    sqe,
                    // for registered file at index 0
                    // self.file.as_raw_fd(),
                    0,
                    IORING_FSYNC_DATASYNC,
                );
                // io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
                io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN | IOSQE_FIXED_FILE);
                (*sqe).user_data = u64::try_from(i).unwrap();
                // println!("fsync with i = {}", i);
            }

            // 5. submit
            let ret = io_uring_submit(&mut self.ring);
            if ret < 0 {
                return Err(Error::Io(io::Error::from_raw_os_error(-ret)));
            }
        };

        Ok(())
    }
}

impl<T> Drop for Uring<T> {
    fn drop(&mut self) {
        unsafe {
            // flush unfinished ops
            if self.free_slots.len() < self.iovecs.len() {
                let ret = io_uring_submit_and_wait(
                    &mut self.ring,
                    u32::try_from(self.iovecs.len() - self.free_slots.len())
                        .unwrap(),
                );
                if ret < 0 {
                    panic!(Error::Io(io::Error::from_raw_os_error(-ret)));
                }
            }

            self.drain_cqe().unwrap();

            // auto on exit: io_uring_unregister_files(&mut self.ring);
            io_uring_queue_exit(&mut self.ring);
        };
    }
}
<|endoftext|>


<commit_before>use platform::platform::Platform;
use platform::base_platform::BasePlatform;
use platform::bsd;

pub struct Platforms {
    curr: usize,
    base_platforms: Vec<Box<BasePlatform<Item = Box<Platform>>>>,
}

impl Platforms {
    pub fn new() -> Platforms {
        let mut p: Vec<Box<BasePlatform<Item = Box<Platform>>>> = Vec::new();
        p.push(Box::new(bsd::Bsd::new()));
        Platforms {
            curr: 0,
            base_platforms: p,
        }
    }
}

impl Iterator for Platforms {
    type Item = Box<Platform>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.curr < self.base_platforms.len() {
            match self.base_platforms[self.curr].next() {
                Some(x) => Some(x),
                None => {
                    self.curr += 1;
                    self.next()
                }
            }
        } else {
            None
        }
    }
}

impl Platforms {
    fn reset(&mut self) {
        self.curr = 0;
        for p in &mut self.base_platforms {
            p.reset();
        }
    }
}
<commit_msg>Remove unused reset method<commit_after>use platform::platform::Platform;
use platform::base_platform::BasePlatform;
use platform::bsd;

pub struct Platforms {
    curr: usize,
    base_platforms: Vec<Box<BasePlatform<Item = Box<Platform>>>>,
}

impl Platforms {
    pub fn new() -> Platforms {
        let mut p: Vec<Box<BasePlatform<Item = Box<Platform>>>> = Vec::new();
        p.push(Box::new(bsd::Bsd::new()));
        Platforms {
            curr: 0,
            base_platforms: p,
        }
    }
}

impl Iterator for Platforms {
    type Item = Box<Platform>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.curr < self.base_platforms.len() {
            match self.base_platforms[self.curr].next() {
                Some(x) => Some(x),
                None => {
                    self.curr += 1;
                    self.next()
                }
            }
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use any_pointer;
use MessageSize;
use capability::{Params, Promise, Request, RemotePromise, Results};

pub trait ResponseHook {
    fn get<'a>(&'a self) -> ::Result<any_pointer::Reader<'a>>;
}

pub trait RequestHook {
    fn get<'a>(&'a mut self) -> any_pointer::Builder<'a>;
    fn send<'a>(self : Box<Self>) -> RemotePromise<any_pointer::Owned>;
}

pub trait ClientHook {
    fn add_ref(&self) -> Box<ClientHook>;
    fn new_call(&self,
                interface_id: u64,
                method_id: u16,
                size_hint: Option<MessageSize>)
                -> Request<any_pointer::Owned, any_pointer::Owned>;

    fn call(&self, interface_id: u64, method_id: u16,
            params: Box<ParamsHook>, results: Box<ResultsHook>)
            -> (::capability::Promise<Box<ResultsDoneHook>, ::Error>, Box<PipelineHook>);

    fn get_brand(&self) -> usize;
    fn get_ptr(&self) -> usize;

    fn write_target(&self, target: any_pointer::Builder) -> Option<Box<ClientHook>>;

    fn write_descriptor(&self, descriptor: any_pointer::Builder) -> Option<u32>;

    /// If this ClientHook is a promise that has already resolved, returns the inner, resolved version
    /// of the capability.  The caller may permanently replace this client with the resolved one if
    /// desired.  Returns null if the client isn't a promise or hasn't resolved yet -- use
    /// `whenMoreResolved()` to distinguish between them.
    fn get_resolved(&self) -> Option<Box<ClientHook>>;

    /// If this client is a settled reference (not a promise), return nullptr.  Otherwise, return a
    /// promise that eventually resolves to a new client that is closer to being the final, settled
    /// client (i.e. the value eventually returned by `getResolved()`).  Calling this repeatedly
    /// should eventually produce a settled client.
    fn when_more_resolved(&self) -> Option<::capability::Promise<Box<ClientHook>, ::Error>>;
}

impl Clone for Box<ClientHook> {
    fn clone(&self) -> Box<ClientHook> {
        self.add_ref()
    }
}

pub trait ServerHook: 'static {
    fn new_client(server: Box<::capability::Server>) -> ::capability::Client;
}

pub trait ResultsHook {
    fn get<'a>(&'a mut self) -> ::Result<any_pointer::Builder<'a>>;
    fn allow_cancellation(&self);
    fn tail_call(self: Box<Self>, request: Box<RequestHook>) -> Promise<(), ::Error>;

    fn send_return(self: Box<Self>) -> Promise<Box<ResultsDoneHook>, ::Error>;
}

pub trait ResultsDoneHook {
    fn add_ref(&self) -> Box<ResultsDoneHook>;
    fn get<'a>(&'a self) -> ::Result<any_pointer::Reader<'a>>;
}

impl Clone for Box<ResultsDoneHook> {
    fn clone(&self) -> Box<ResultsDoneHook> {
        self.add_ref()
    }
}

pub trait ParamsHook {
    fn get<'a>(&'a self) -> ::Result<any_pointer::Reader<'a>>;
}

// Where should this live?
pub fn internal_get_typed_params<T>(typeless: Params<any_pointer::Owned>) -> Params<T> {
    Params { hook: typeless.hook, marker: ::std::marker::PhantomData }
}

pub fn internal_get_typed_results<T>(typeless: Results<any_pointer::Owned>) -> Results<T> {
    Results { hook: typeless.hook, marker: ::std::marker::PhantomData }
}

pub fn internal_get_untyped_results<T>(typeful: Results<T>) -> Results<any_pointer::Owned> {
    Results { hook: typeful.hook, marker: ::std::marker::PhantomData }
}

pub trait PipelineHook {
    fn add_ref(&self) -> Box<PipelineHook>;
    fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Box<ClientHook>;

    /// Version of get_pipelined_cap() passing the array by move. May avoid a copy in some cases.
    /// Default implementation just calls the other version.
    fn get_pipelined_cap_move(&self, ops: Vec<PipelineOp>) -> Box<ClientHook> {
        self.get_pipelined_cap(&ops)
    }
}

impl Clone for Box<PipelineHook> {
    fn clone(&self) -> Box<PipelineHook> {
        self.add_ref()
    }
}

#[derive(Clone, Copy)]
pub enum PipelineOp {
    Noop,
    GetPointerField(u16),
}

<commit_msg>RequestHook::get_brand() and ResultsHook::direct_tail_call()<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use any_pointer;
use MessageSize;
use capability::{Params, Promise, Request, RemotePromise, Results};

pub trait ResponseHook {
    fn get<'a>(&'a self) -> ::Result<any_pointer::Reader<'a>>;
}

pub trait RequestHook {
    fn get<'a>(&'a mut self) -> any_pointer::Builder<'a>;
    fn get_brand(&self) -> usize;
    fn send<'a>(self : Box<Self>) -> RemotePromise<any_pointer::Owned>;
}

pub trait ClientHook {
    fn add_ref(&self) -> Box<ClientHook>;
    fn new_call(&self,
                interface_id: u64,
                method_id: u16,
                size_hint: Option<MessageSize>)
                -> Request<any_pointer::Owned, any_pointer::Owned>;

    fn call(&self, interface_id: u64, method_id: u16,
            params: Box<ParamsHook>, results: Box<ResultsHook>)
            -> (::capability::Promise<Box<ResultsDoneHook>, ::Error>, Box<PipelineHook>);

    fn get_brand(&self) -> usize;
    fn get_ptr(&self) -> usize;

    fn write_target(&self, target: any_pointer::Builder) -> Option<Box<ClientHook>>;

    fn write_descriptor(&self, descriptor: any_pointer::Builder) -> Option<u32>;

    /// If this ClientHook is a promise that has already resolved, returns the inner, resolved version
    /// of the capability.  The caller may permanently replace this client with the resolved one if
    /// desired.  Returns null if the client isn't a promise or hasn't resolved yet -- use
    /// `whenMoreResolved()` to distinguish between them.
    fn get_resolved(&self) -> Option<Box<ClientHook>>;

    /// If this client is a settled reference (not a promise), return nullptr.  Otherwise, return a
    /// promise that eventually resolves to a new client that is closer to being the final, settled
    /// client (i.e. the value eventually returned by `getResolved()`).  Calling this repeatedly
    /// should eventually produce a settled client.
    fn when_more_resolved(&self) -> Option<::capability::Promise<Box<ClientHook>, ::Error>>;
}

impl Clone for Box<ClientHook> {
    fn clone(&self) -> Box<ClientHook> {
        self.add_ref()
    }
}

pub trait ServerHook: 'static {
    fn new_client(server: Box<::capability::Server>) -> ::capability::Client;
}

pub trait ResultsHook {
    fn get<'a>(&'a mut self) -> ::Result<any_pointer::Builder<'a>>;
    fn allow_cancellation(&self);
    fn tail_call(self: Box<Self>, request: Box<RequestHook>) -> Promise<(), ::Error>;
    fn direct_tail_call(self: Box<Self>, request: Box<RequestHook>) ->
        (::capability::Promise<Box<ResultsDoneHook>, ::Error>, Box<PipelineHook>);

    fn send_return(self: Box<Self>) -> Promise<Box<ResultsDoneHook>, ::Error>;
}

pub trait ResultsDoneHook {
    fn add_ref(&self) -> Box<ResultsDoneHook>;
    fn get<'a>(&'a self) -> ::Result<any_pointer::Reader<'a>>;
}

impl Clone for Box<ResultsDoneHook> {
    fn clone(&self) -> Box<ResultsDoneHook> {
        self.add_ref()
    }
}

pub trait ParamsHook {
    fn get<'a>(&'a self) -> ::Result<any_pointer::Reader<'a>>;
}

// Where should this live?
pub fn internal_get_typed_params<T>(typeless: Params<any_pointer::Owned>) -> Params<T> {
    Params { hook: typeless.hook, marker: ::std::marker::PhantomData }
}

pub fn internal_get_typed_results<T>(typeless: Results<any_pointer::Owned>) -> Results<T> {
    Results { hook: typeless.hook, marker: ::std::marker::PhantomData }
}

pub fn internal_get_untyped_results<T>(typeful: Results<T>) -> Results<any_pointer::Owned> {
    Results { hook: typeful.hook, marker: ::std::marker::PhantomData }
}

pub trait PipelineHook {
    fn add_ref(&self) -> Box<PipelineHook>;
    fn get_pipelined_cap(&self, ops: &[PipelineOp]) -> Box<ClientHook>;

    /// Version of get_pipelined_cap() passing the array by move. May avoid a copy in some cases.
    /// Default implementation just calls the other version.
    fn get_pipelined_cap_move(&self, ops: Vec<PipelineOp>) -> Box<ClientHook> {
        self.get_pipelined_cap(&ops)
    }
}

impl Clone for Box<PipelineHook> {
    fn clone(&self) -> Box<PipelineHook> {
        self.add_ref()
    }
}

#[derive(Clone, Copy)]
pub enum PipelineOp {
    Noop,
    GetPointerField(u16),
}

<|endoftext|>


<commit_before>extern crate gcc;

use std::path::Path;
use std::process::Command;

struct Perl {
    bin: String,
}

impl Perl {
    fn new() -> Perl {
        Perl {
            bin: std::env::var("PERL").unwrap(),
        }
    }

    fn cfg(&self, key: &str) -> String {
        let out = Command::new(&self.bin)
            .arg("-MConfig")
            .arg("-e")
            .arg(format!("print $Config::Config{{{}}}", key))
            .output()
            .unwrap();

        String::from_utf8(out.stdout).unwrap()
    }

    fn run(&self, script: &str) {
        Command::new(&self.bin)
            .arg(script)
            .status()
            .unwrap();
    }
}

fn main() {
    let perl = Perl::new();

    let prefix = perl.cfg("archlibexp");
    let perl_multi = perl.cfg("usemultiplicity");

    let perl_inc = Path::new(&prefix).join("CORE");

    perl.run("build/regen.pl");

    gcc::Config::new()
        .file("ouroboros/libouroboros.c")
        .include(&perl_inc)
        .compile("libouroboros.a");

    if perl_multi == "define" {
        println!("cargo:rustc-cfg=perl_multiplicity");
    }
}
<commit_msg>Pass perl ccflags when building libouroboros.<commit_after>extern crate gcc;

use std::path::Path;
use std::process::Command;

struct Perl {
    bin: String,
}

impl Perl {
    fn new() -> Perl {
        Perl {
            bin: std::env::var("PERL").unwrap(),
        }
    }

    fn cfg(&self, key: &str) -> String {
        let out = Command::new(&self.bin)
            .arg("-MConfig")
            .arg("-e")
            .arg(format!("print $Config::Config{{{}}}", key))
            .output()
            .unwrap();

        String::from_utf8(out.stdout).unwrap()
    }

    fn run(&self, script: &str) {
        Command::new(&self.bin)
            .arg(script)
            .status()
            .unwrap();
    }
}

fn build_ouro(perl: &Perl) {
    let mut gcc = gcc::Config::new();

    let ccflags = perl.cfg("ccflags");
    for flag in ccflags.split_whitespace() {
        gcc.flag(flag);
    }

    let archlib = perl.cfg("archlibexp");
    let coreinc = Path::new(&archlib).join("CORE");
    gcc.include(&coreinc);

    gcc.file("ouroboros/libouroboros.c");
    gcc.compile("libouroboros.a");
}

fn main() {
    let perl = Perl::new();

    build_ouro(&perl);

    perl.run("build/regen.pl");

    if perl.cfg("usemultiplicity") == "define" {
        println!("cargo:rustc-cfg=perl_multiplicity");
    }
}
<|endoftext|>