<commit_before>#![deny(trivial_numeric_casts, unsafe_code, unstable_features)]
#![warn(
    missing_debug_implementations,
    missing_docs,
    unused_qualifications,
    unused_import_braces
)]
//! This crate contains the DICOM transfer syntax registry.
//! The transfer syntax registry maps a DICOM UID of a transfer syntax into the
//! respective transfer syntax specifier. In the default implementation, the
//! container of transfer syntaxes is populated before-main through the
//! [inventory] pattern, then making all registerd TSes readily available
//! through the [`TransferSyntaxRegistry`] type.
//!
//! The default Cargo feature `inventory-registry` can be deactivated for
//! environments which do not support `inventory`, with the downside of only
//! providing the built-in transfer syntaxes.
//!
//! This registry should not have to be used directly, except when developing
//! higher level APIs, which should learn to negotiate and resolve the expected
//! transfer syntax automatically.
//!
//! ## Transfer Syntax descriptors
//!
//! This crate encompasses the basic DICOM level of conformance:
//! _Implicit VR Little Endian_,
//! _Explicit VR Little Endian_,
//! and _Explicit VR Big Endian_ are built-in.
//! Transfer syntaxes which are not supported,
//! or which rely on encapsulated pixel data,
//! are only listed as _stubs_ to be replaced by separate libraries.
//! The full list is available in the [`entries`](entries) module.
//!
//! [inventory]: https://docs.rs/inventory/0.1.4/inventory

use byteordered::Endianness;
use dicom_encoding::transfer_syntax::{
    AdapterFreeTransferSyntax as Ts, Codec, TransferSyntaxIndex,
};
use lazy_static::lazy_static;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;

pub use dicom_encoding::TransferSyntax;
pub mod entries;

/// Data type for a registry of DICOM.
pub struct TransferSyntaxRegistryImpl {
    m: HashMap<&'static str, &'static TransferSyntax>,
}

impl fmt::Debug for TransferSyntaxRegistryImpl {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let entries: HashMap<&str, &str> =
            self.m.iter().map(|(uid, ts)| (*uid, ts.name())).collect();
        f.debug_struct("TransferSyntaxRegistryImpl")
            .field("m", &entries)
            .finish()
    }
}

impl TransferSyntaxRegistryImpl {
    /// Obtain a DICOM codec by transfer syntax UID.
    fn get<U: AsRef<str>>(&self, uid: U) -> Option<&'static TransferSyntax> {
        let ts_uid = {
            let uid = uid.as_ref();
            if uid.as_bytes().last().cloned() == Some(b'\0') {
                &uid[..uid.len() - 1]
            } else {
                &uid
            }
        };
        self.m.get(ts_uid).copied()
    }

    /// Register the given transfer syntax (TS) to the system. It can override
    /// another TS with the same UID, in the only case that the TS requires
    /// certain codecs which are not supported by the previously registered
    /// TS. If no such requirements are imposed, this function returns `false`
    /// and no changes are made.
    fn register(&mut self, ts: &'static TransferSyntax) -> bool {
        match self.m.entry(&ts.uid()) {
            Entry::Occupied(mut e) => {
                let replace = match (&e.get().codec(), ts.codec()) {
                    (Codec::Unsupported, Codec::Dataset(_))
                    | (Codec::EncapsulatedPixelData, Codec::PixelData(_)) => true,
                    // weird one ahead: the two specifiers do not agree on
                    // requirements, better keep it as a separate match arm for
                    // debugging purposes
                    (Codec::Unsupported, Codec::PixelData(_)) => {
                        eprintln!("Inconsistent requirements for transfer syntax {}: `Unsupported` cannot be replaced with `PixelData`", ts.uid());
                        false
                    }
                    // ignoring TS with less or equal implementation
                    _ => false,
                };

                if replace {
                    e.insert(ts);
                    true
                } else {
                    false
                }
            }
            Entry::Vacant(e) => {
                e.insert(ts);
                true
            }
        }
    }
}

impl TransferSyntaxIndex for TransferSyntaxRegistryImpl {
    #[inline]
    fn get(&self, uid: &str) -> Option<&TransferSyntax> {
        Self::get(self, uid)
    }
}

/// Zero-sized representative of the main transfer syntax registry.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct TransferSyntaxRegistry;

impl TransferSyntaxIndex for TransferSyntaxRegistry {
    #[inline]
    fn get(&self, uid: &str) -> Option<&TransferSyntax> {
        get_registry().get(uid)
    }
}

lazy_static! {
    static ref BUILT_IN_TS: [TransferSyntax; 29] = {
        use self::entries::*;
        [
            IMPLICIT_VR_LITTLE_ENDIAN.erased(),
            EXPLICIT_VR_LITTLE_ENDIAN.erased(),
            EXPLICIT_VR_BIG_ENDIAN.erased(),

            DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN.erased(),
            JPIP_REFERENCED_DEFLATE.erased(),
            JPEG_BASELINE.erased(),
            JPEG_EXTENDED.erased(),
            JPEG_LOSSLESS_NON_HIERARCHICAL.erased(),
            JPEG_LOSSLESS_NON_HIERARCHICAL_FIRST_ORDER_PREDICTION.erased(),
            JPEG_LS_LOSSLESS_IMAGE_COMPRESSION.erased(),
            JPEG_LS_LOSSY_IMAGE_COMPRESSION.erased(),
            JPEG_2000_IMAGE_COMPRESSION_LOSSLESS_ONLY.erased(),
            JPEG_2000_IMAGE_COMPRESSION.erased(),
            JPEG_2000_PART2_MULTI_COMPONENT_IMAGE_COMPRESSION_LOSSLESS_ONLY.erased(),
            JPEG_2000_PART2_MULTI_COMPONENT_IMAGE_COMPRESSION.erased(),
            JPIP_REFERENCED.erased(),
            MPEG2_MAIN_PROFILE_MAIN_LEVEL.erased(),
            MPEG2_MAIN_PROFILE_HIGH_LEVEL.erased(),
            MPEG4_AVC_H264_HIGH_PROFILE.erased(),
            MPEG4_AVC_H264_BD_COMPATIBLE_HIGH_PROFILE.erased(),
            MPEG4_AVC_H264_HIGH_PROFILE_FOR_2D_VIDEO.erased(),
            MPEG4_AVC_H264_HIGH_PROFILE_FOR_3D_VIDEO.erased(),
            MPEG4_AVC_H264_STEREO_HIGH_PROFILE.erased(),
            HEVC_H265_MAIN_PROFILE.erased(),
            HEVC_H265_MAIN_10_PROFILE.erased(),
            RLE_LOSSLESS.erased(),
            SMPTE_ST_2110_20_UNCOMPRESSED_PROGRESSIVE.erased(),
            SMPTE_ST_2110_20_UNCOMPRESSED_INTERLACED.erased(),
            SMPTE_ST_2110_30_PCM.erased(),
        ]
    };

    static ref REGISTRY: TransferSyntaxRegistryImpl = {
        let mut registry = TransferSyntaxRegistryImpl {
            m: HashMap::with_capacity(32),
        };

        // add built-in TSes manually
        for ts in BUILT_IN_TS.iter() {
            registry.register(ts);
        }
        // add TSes from inventory, if available
        inventory_populate(&mut registry);

        registry
    };
}

#[cfg(feature = "inventory-registry")]
#[inline]
fn inventory_populate(registry: &mut TransferSyntaxRegistryImpl) {
    for ts in inventory::iter::<TransferSyntax> {
        registry.register(ts);
    }
}

#[cfg(not(feature = "inventory-registry"))]
#[inline]
fn inventory_populate(_: &mut TransferSyntaxRegistryImpl) {
    // do nothing
}

/// Retrieve a reference to the global codec registry.
#[inline]
pub(crate) fn get_registry() -> &'static TransferSyntaxRegistryImpl {
    &REGISTRY
}

/// create a TS with an unsupported pixel encapsulation
pub(crate) const fn create_ts_stub(uid: &'static str, name: &'static str) -> Ts {
    TransferSyntax::new(
        uid,
        name,
        Endianness::Little,
        true,
        Codec::EncapsulatedPixelData,
    )
}

/// Retrieve the default transfer syntax.
pub fn default() -> Ts {
    entries::IMPLICIT_VR_LITTLE_ENDIAN
}
<commit_msg>Make transfer syntax registry more flexible against UIDs with null characters (#157)<commit_after>#![deny(trivial_numeric_casts, unsafe_code, unstable_features)]
#![warn(
    missing_debug_implementations,
    missing_docs,
    unused_qualifications,
    unused_import_braces
)]
//! This crate contains the DICOM transfer syntax registry.
//! The transfer syntax registry maps a DICOM UID of a transfer syntax into the
//! respective transfer syntax specifier. In the default implementation, the
//! container of transfer syntaxes is populated before-main through the
//! [inventory] pattern, then making all registerd TSes readily available
//! through the [`TransferSyntaxRegistry`] type.
//!
//! The default Cargo feature `inventory-registry` can be deactivated for
//! environments which do not support `inventory`, with the downside of only
//! providing the built-in transfer syntaxes.
//!
//! This registry should not have to be used directly, except when developing
//! higher level APIs, which should learn to negotiate and resolve the expected
//! transfer syntax automatically.
//!
//! ## Transfer Syntax descriptors
//!
//! This crate encompasses the basic DICOM level of conformance:
//! _Implicit VR Little Endian_,
//! _Explicit VR Little Endian_,
//! and _Explicit VR Big Endian_ are built-in.
//! Transfer syntaxes which are not supported,
//! or which rely on encapsulated pixel data,
//! are only listed as _stubs_ to be replaced by separate libraries.
//! The full list is available in the [`entries`](entries) module.
//!
//! [inventory]: https://docs.rs/inventory/0.1.4/inventory

use byteordered::Endianness;
use dicom_encoding::transfer_syntax::{
    AdapterFreeTransferSyntax as Ts, Codec, TransferSyntaxIndex,
};
use lazy_static::lazy_static;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;

pub use dicom_encoding::TransferSyntax;
pub mod entries;

/// Data type for a registry of DICOM.
pub struct TransferSyntaxRegistryImpl {
    m: HashMap<&'static str, &'static TransferSyntax>,
}

impl fmt::Debug for TransferSyntaxRegistryImpl {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let entries: HashMap<&str, &str> =
            self.m.iter().map(|(uid, ts)| (*uid, ts.name())).collect();
        f.debug_struct("TransferSyntaxRegistryImpl")
            .field("m", &entries)
            .finish()
    }
}

impl TransferSyntaxRegistryImpl {
    /// Obtain a DICOM codec by transfer syntax UID.
    fn get<U: AsRef<str>>(&self, uid: U) -> Option<&'static TransferSyntax> {
        let ts_uid = uid
            .as_ref()
            .trim_end_matches(|c: char| c.is_whitespace() || c == '\0');
        self.m.get(ts_uid).copied()
    }

    /// Register the given transfer syntax (TS) to the system. It can override
    /// another TS with the same UID, in the only case that the TS requires
    /// certain codecs which are not supported by the previously registered
    /// TS. If no such requirements are imposed, this function returns `false`
    /// and no changes are made.
    fn register(&mut self, ts: &'static TransferSyntax) -> bool {
        match self.m.entry(&ts.uid()) {
            Entry::Occupied(mut e) => {
                let replace = match (&e.get().codec(), ts.codec()) {
                    (Codec::Unsupported, Codec::Dataset(_))
                    | (Codec::EncapsulatedPixelData, Codec::PixelData(_)) => true,
                    // weird one ahead: the two specifiers do not agree on
                    // requirements, better keep it as a separate match arm for
                    // debugging purposes
                    (Codec::Unsupported, Codec::PixelData(_)) => {
                        eprintln!("Inconsistent requirements for transfer syntax {}: `Unsupported` cannot be replaced with `PixelData`", ts.uid());
                        false
                    }
                    // ignoring TS with less or equal implementation
                    _ => false,
                };

                if replace {
                    e.insert(ts);
                    true
                } else {
                    false
                }
            }
            Entry::Vacant(e) => {
                e.insert(ts);
                true
            }
        }
    }
}

impl TransferSyntaxIndex for TransferSyntaxRegistryImpl {
    #[inline]
    fn get(&self, uid: &str) -> Option<&TransferSyntax> {
        Self::get(self, uid)
    }
}

/// Zero-sized representative of the main transfer syntax registry.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct TransferSyntaxRegistry;

impl TransferSyntaxIndex for TransferSyntaxRegistry {
    #[inline]
    fn get(&self, uid: &str) -> Option<&TransferSyntax> {
        get_registry().get(uid)
    }
}

lazy_static! {
    static ref BUILT_IN_TS: [TransferSyntax; 29] = {
        use self::entries::*;
        [
            IMPLICIT_VR_LITTLE_ENDIAN.erased(),
            EXPLICIT_VR_LITTLE_ENDIAN.erased(),
            EXPLICIT_VR_BIG_ENDIAN.erased(),

            DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN.erased(),
            JPIP_REFERENCED_DEFLATE.erased(),
            JPEG_BASELINE.erased(),
            JPEG_EXTENDED.erased(),
            JPEG_LOSSLESS_NON_HIERARCHICAL.erased(),
            JPEG_LOSSLESS_NON_HIERARCHICAL_FIRST_ORDER_PREDICTION.erased(),
            JPEG_LS_LOSSLESS_IMAGE_COMPRESSION.erased(),
            JPEG_LS_LOSSY_IMAGE_COMPRESSION.erased(),
            JPEG_2000_IMAGE_COMPRESSION_LOSSLESS_ONLY.erased(),
            JPEG_2000_IMAGE_COMPRESSION.erased(),
            JPEG_2000_PART2_MULTI_COMPONENT_IMAGE_COMPRESSION_LOSSLESS_ONLY.erased(),
            JPEG_2000_PART2_MULTI_COMPONENT_IMAGE_COMPRESSION.erased(),
            JPIP_REFERENCED.erased(),
            MPEG2_MAIN_PROFILE_MAIN_LEVEL.erased(),
            MPEG2_MAIN_PROFILE_HIGH_LEVEL.erased(),
            MPEG4_AVC_H264_HIGH_PROFILE.erased(),
            MPEG4_AVC_H264_BD_COMPATIBLE_HIGH_PROFILE.erased(),
            MPEG4_AVC_H264_HIGH_PROFILE_FOR_2D_VIDEO.erased(),
            MPEG4_AVC_H264_HIGH_PROFILE_FOR_3D_VIDEO.erased(),
            MPEG4_AVC_H264_STEREO_HIGH_PROFILE.erased(),
            HEVC_H265_MAIN_PROFILE.erased(),
            HEVC_H265_MAIN_10_PROFILE.erased(),
            RLE_LOSSLESS.erased(),
            SMPTE_ST_2110_20_UNCOMPRESSED_PROGRESSIVE.erased(),
            SMPTE_ST_2110_20_UNCOMPRESSED_INTERLACED.erased(),
            SMPTE_ST_2110_30_PCM.erased(),
        ]
    };

    static ref REGISTRY: TransferSyntaxRegistryImpl = {
        let mut registry = TransferSyntaxRegistryImpl {
            m: HashMap::with_capacity(32),
        };

        // add built-in TSes manually
        for ts in BUILT_IN_TS.iter() {
            registry.register(ts);
        }
        // add TSes from inventory, if available
        inventory_populate(&mut registry);

        registry
    };
}

#[cfg(feature = "inventory-registry")]
#[inline]
fn inventory_populate(registry: &mut TransferSyntaxRegistryImpl) {
    for ts in inventory::iter::<TransferSyntax> {
        registry.register(ts);
    }
}

#[cfg(not(feature = "inventory-registry"))]
#[inline]
fn inventory_populate(_: &mut TransferSyntaxRegistryImpl) {
    // do nothing
}

/// Retrieve a reference to the global codec registry.
#[inline]
pub(crate) fn get_registry() -> &'static TransferSyntaxRegistryImpl {
    &REGISTRY
}

/// create a TS with an unsupported pixel encapsulation
pub(crate) const fn create_ts_stub(uid: &'static str, name: &'static str) -> Ts {
    TransferSyntax::new(
        uid,
        name,
        Endianness::Little,
        true,
        Codec::EncapsulatedPixelData,
    )
}

/// Retrieve the default transfer syntax.
pub fn default() -> Ts {
    entries::IMPLICIT_VR_LITTLE_ENDIAN
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;
extern crate lifeguard;

#[cfg(test)]
mod tests {
  use test::Bencher;
  use test::black_box;
  use lifeguard::{Pool,RcRecycled};

  const ITERATIONS : u32 = 10_000;

  // Calling String::new() is very close to a no-op; no actual allocation
  // is performed until bytes are pushed onto the end of the String. As such,
  // we need to explicitly ask for some space to be available to trigger allocation.
  const EMPTY_STRING_CAPACITY : usize = 4;

  #[bench]
  fn bench01_standard_allocation_speed(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
      }
    });
  }

  #[bench]
  fn bench02_pooled_allocation_speed_rc(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_rc();
        let _string = pool.new_rc();
        let _string = pool.new_rc();
        let _string = pool.new_rc();
        let _string = pool.new_rc();
      }
    });
  }

  #[bench]
  fn bench02_pooled_allocation_speed_scoped(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
      }
    });
  }

  #[bench]
  fn bench03_standard_initialized_allocation_speed(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = "man".to_owned();
        let _string = "dog".to_owned();
        let _string = "cat".to_owned();
        let _string = "mouse".to_owned();
        let _string = "cheese".to_owned();
      }
    });
  }

  #[bench]
  fn bench04_pooled_initialized_allocation_speed(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_rc_from("man");
        let _string = pool.new_rc_from("dog");
        let _string = pool.new_rc_from("cat");
        let _string = pool.new_rc_from("mouse");
        let _string = pool.new_rc_from("cheese");
      }
    });
  }
  #[bench]
  fn bench05_allocate_vec_vec_str(bencher: &mut Bencher) {
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = Vec::new();
              for _ in 0..100 {
                  v2.push(("test!").to_owned());
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[bench]
  fn bench06_pooled_vec_vec_str(bencher: &mut Bencher) {
      let vec_str_pool : Pool<Vec<RcRecycled<String>>> = Pool::with_size(100);
      let str_pool : Pool<String> = Pool::with_size(10000);
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = vec_str_pool.new_rc();
              for _ in 0..100 {
                  v2.push(str_pool.new_rc_from("test!"));
              }
              v1.push(v2);
          }
          v1
      });
  }
}
<commit_msg>Benchmark parity for Recycled and RcRecycled.<commit_after>#![feature(test)]
extern crate test;
extern crate lifeguard;

#[cfg(test)]
mod tests {
  use test::Bencher;
  use test::black_box;
  use lifeguard::{Pool,Recycled,RcRecycled};

  const ITERATIONS : u32 = 10_000;

  // Calling String::new() is very close to a no-op; no actual allocation
  // is performed until bytes are pushed onto the end of the String. As such,
  // we need to explicitly ask for some space to be available to trigger allocation.
  const EMPTY_STRING_CAPACITY : usize = 4;

  #[bench]
  fn allocation_standard(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
        let _string = black_box(String::with_capacity(EMPTY_STRING_CAPACITY));
      }
    });
  }

  #[bench]
  fn allocation_pooled(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
        let _string = pool.new();
      }
    });
  }

  #[bench]
  fn allocation_pooled_rc(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_rc();
        let _string = pool.new_rc();
        let _string = pool.new_rc();
        let _string = pool.new_rc();
        let _string = pool.new_rc();
      }
    });
  }

  #[bench]
  fn initialized_allocation_standard(b: &mut Bencher) {
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = "man".to_owned();
        let _string = "dog".to_owned();
        let _string = "cat".to_owned();
        let _string = "mouse".to_owned();
        let _string = "cheese".to_owned();
      }
    });
  }

  #[bench]
  fn initialized_allocation_pooled(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_from("man");
        let _string = pool.new_from("dog");
        let _string = pool.new_from("cat");
        let _string = pool.new_from("mouse");
        let _string = pool.new_from("cheese");
      }
    });
  }

  #[bench]
  fn initialized_allocation_pooled_rc(b: &mut Bencher) {
    let pool : Pool<String> = Pool::with_size(5);
    b.iter(|| {
      for _ in 0..ITERATIONS {
        let _string = pool.new_rc_from("man");
        let _string = pool.new_rc_from("dog");
        let _string = pool.new_rc_from("cat");
        let _string = pool.new_rc_from("mouse");
        let _string = pool.new_rc_from("cheese");
      }
    });
  }

  #[bench]
  fn vec_vec_str_standard(bencher: &mut Bencher) {
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = Vec::new();
              for _ in 0..100 {
                  v2.push(("test!").to_owned());
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[bench]
  fn vec_vec_str_pooled(bencher: &mut Bencher) {
      // Note that because we're using scoped values (not Rc'ed values)
      // and we're storing items from one pool in the other,
      // the order that our pools are declared matters.
      // Reversing them results in a compile error regarding lifetimes.
      let str_pool : Pool<String> = Pool::with_size(10000);
      let vec_str_pool : Pool<Vec<Recycled<String>>> = Pool::with_size(100);
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = vec_str_pool.new();
              for _ in 0..100 {
                  v2.push(str_pool.new_from("test!"));
              }
              v1.push(v2);
          }
          v1
      });
  }

  #[bench]
  fn vec_vec_str_pooled_rc(bencher: &mut Bencher) {
      let vec_str_pool : Pool<Vec<RcRecycled<String>>> = Pool::with_size(100);
      let str_pool : Pool<String> = Pool::with_size(10000);
      bencher.iter(|| {
          let mut v1 = Vec::new();
          for _ in 0..100 {
              let mut v2 = vec_str_pool.new_rc();
              for _ in 0..100 {
                  v2.push(str_pool.new_rc_from("test!"));
              }
              v1.push(v2);
          }
          v1
      });
  }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::ptr;

use gdk_pixbuf::{Colorspace, Pixbuf};
use gio::prelude::*;
use glib::translate::*;
use url::Url;

use rsvg_internals::{
    Dpi, Handle, IRect, LoadOptions, LoadingError, Pixels, RenderingError, RsvgDimensionData,
    SharedImageSurface, SizeCallback, SurfaceType,
};

use crate::c_api::set_gerror;

fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
    assert!(width > 0 && height > 0);

    Pixbuf::new(Colorspace::Rgb, true, 8, width, height).ok_or(RenderingError::OutOfMemory)
}

pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> {
    // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized
    // surfaces.  In this case, return a 1-pixel transparent pixbuf.

    let pixbuf = pixbuf_new(1, 1)?;
    pixbuf.put_pixel(0, 0, 0, 0, 0, 0);

    Ok(pixbuf)
}

pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> {
    let width = surface.width();
    let height = surface.height();

    let pixbuf = pixbuf_new(width, height)?;
    let bounds = IRect::from_size(width, height);

    for (x, y, pixel) in Pixels::within(&surface, bounds) {
        let (r, g, b, a) = if pixel.a == 0 {
            (0, 0, 0, 0)
        } else {
            let pixel = pixel.unpremultiply();
            (pixel.r, pixel.g, pixel.b, pixel.a)
        };

        // FIXME: Use pixbuf.put_pixel when
        // https://github.com/gtk-rs/gdk-pixbuf/issues/147
        // is integrated
        my_put_pixel(&pixbuf, x as i32, y as i32, r, g, b, a);
    }

    Ok(pixbuf)
}

// Copied from gtk-rs/gdk-pixbuf
//
// See the following:
//   https://gitlab.gnome.org/GNOME/librsvg/-/issues/584
//   https://github.com/gtk-rs/gdk-pixbuf/issues/147
//
// Arithmetic can overflow in the computation of `pos` if it is not done with usize
// values (everything coming out of a Pixbuf is i32).
//
// When this fix appears in a gtk-rs release, we can remove this.
fn my_put_pixel(pixbuf: &Pixbuf, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8) {
    unsafe {
        let x = x as usize;
        let y = y as usize;
        let n_channels = pixbuf.get_n_channels() as usize;
        assert!(n_channels == 3 || n_channels == 4);
        let rowstride = pixbuf.get_rowstride() as usize;
        let pixels = pixbuf.get_pixels();
        let pos = y * rowstride + x * n_channels;

        pixels[pos] = red;
        pixels[pos + 1] = green;
        pixels[pos + 2] = blue;
        if n_channels == 4 {
            pixels[pos + 3] = alpha;
        }
    }
}

enum SizeKind {
    Zoom,
    WidthHeight,
    WidthHeightMax,
    ZoomMax,
}

struct SizeMode {
    kind: SizeKind,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
}

fn get_final_size(dimensions: &RsvgDimensionData, size_mode: &SizeMode) -> (i32, i32) {
    let in_width = dimensions.width;
    let in_height = dimensions.height;

    let mut out_width;
    let mut out_height;

    match size_mode.kind {
        SizeKind::Zoom => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;
        }

        SizeKind::ZoomMax => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;

            if out_width > size_mode.width || out_height > size_mode.height {
                let zoom_x = f64::from(size_mode.width) / f64::from(out_width);
                let zoom_y = f64::from(size_mode.height) / f64::from(out_height);
                let zoom = zoom_x.min(zoom_y);

                out_width = (zoom * f64::from(out_width) + 0.5) as i32;
                out_height = (zoom * f64::from(out_height) + 0.5) as i32;
            }
        }

        SizeKind::WidthHeightMax => {
            let zoom_x = f64::from(size_mode.width) / f64::from(in_width);
            let zoom_y = f64::from(size_mode.height) / f64::from(in_height);

            let zoom = zoom_x.min(zoom_y);

            out_width = (zoom * f64::from(in_width) + 0.5) as i32;
            out_height = (zoom * f64::from(in_height) + 0.5) as i32;
        }

        SizeKind::WidthHeight => {
            if size_mode.width != -1 {
                out_width = size_mode.width;
            } else {
                out_width = in_width;
            }

            if size_mode.height != -1 {
                out_height = size_mode.height;
            } else {
                out_height = in_height;
            }
        }
    }

    (out_width, out_height)
}

fn render_to_pixbuf_at_size(
    handle: &Handle,
    dimensions: &RsvgDimensionData,
    width: i32,
    height: i32,
    dpi: Dpi,
) -> Result<Pixbuf, RenderingError> {
    if width == 0 || height == 0 {
        return empty_pixbuf();
    }

    let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;

    {
        let cr = cairo::Context::new(&surface);
        cr.scale(
            f64::from(width) / f64::from(dimensions.width),
            f64::from(height) / f64::from(dimensions.height),
        );

        let dimensions = handle.get_dimensions_sub(None, dpi, &SizeCallback::default(), false)?;
        if !(dimensions.width == 0 || dimensions.height == 0) {
            let viewport = cairo::Rectangle {
                x: 0.0,
                y: 0.0,
                width: f64::from(dimensions.width),
                height: f64::from(dimensions.height),
            };

            handle.render_document(&cr, &viewport, dpi, false)?;
        }
    }

    let shared_surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;

    pixbuf_from_surface(&shared_surface)
}

fn get_default_dpi() -> Dpi {
    // This is ugly, but it preserves the C API semantics of
    //
    //   rsvg_set_default_dpi(...);
    //   pixbuf = rsvg_pixbuf_from_file(...);
    //
    // Passing negative numbers here means that the global default DPI will be used.
    Dpi::new(-1.0, -1.0)
}

fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
    Ok(Url::parse(&file.get_uri()).map_err(|_| LoadingError::BadUrl)?)
}

fn pixbuf_from_file_with_size_mode(
    filename: *const libc::c_char,
    size_mode: &SizeMode,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    let dpi = get_default_dpi();

    unsafe {
        let path = PathBuf::from_glib_none(filename);
        let file = gio::File::new_for_path(path);

        let base_url = match url_from_file(&file) {
            Ok(url) => url,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        let load_options = LoadOptions::new(Some(base_url));

        let cancellable: Option<&gio::Cancellable> = None;
        let handle = match file
            .read(cancellable)
            .map_err(|e| LoadingError::from(e))
            .and_then(|stream| Handle::from_stream(&load_options, stream.as_ref(), None))
        {
            Ok(handle) => handle,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        handle
            .get_dimensions(dpi, &SizeCallback::default(), false)
            .and_then(|dimensions| {
                let (width, height) = get_final_size(&dimensions, size_mode);

                render_to_pixbuf_at_size(&handle, &dimensions, width, height, dpi)
            })
            .and_then(|pixbuf| Ok(pixbuf.to_glib_full()))
            .unwrap_or_else(|e| {
                set_gerror(error, 0, &format!("{}", e));
                ptr::null_mut()
            })
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_size => ptr::null_mut();

        !filename.is_null(),
        (width >= 1 && height >= 1) || (width == -1 && height == -1),
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeight,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::Zoom,
            x_zoom,
            y_zoom,
            width: 0,
            height: 0,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom_with_max => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::ZoomMax,
            x_zoom,
            y_zoom,
            width: max_width,
            height: max_height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
    filename: *const libc::c_char,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_max_size => ptr::null_mut();

        !filename.is_null(),
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeightMax,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width: max_width,
            height: max_height,
        },
        error,
    )
}
<commit_msg>pixbuf_utils: don't call get_dimensions_sub twice.<commit_after>use std::path::PathBuf;
use std::ptr;

use gdk_pixbuf::{Colorspace, Pixbuf};
use gio::prelude::*;
use glib::translate::*;
use url::Url;

use rsvg_internals::{
    Dpi, Handle, IRect, LoadOptions, LoadingError, Pixels, RenderingError, RsvgDimensionData,
    SharedImageSurface, SizeCallback, SurfaceType,
};

use crate::c_api::set_gerror;

fn pixbuf_new(width: i32, height: i32) -> Result<Pixbuf, RenderingError> {
    assert!(width > 0 && height > 0);

    Pixbuf::new(Colorspace::Rgb, true, 8, width, height).ok_or(RenderingError::OutOfMemory)
}

pub fn empty_pixbuf() -> Result<Pixbuf, RenderingError> {
    // GdkPixbuf does not allow zero-sized pixbufs, but Cairo allows zero-sized
    // surfaces.  In this case, return a 1-pixel transparent pixbuf.

    let pixbuf = pixbuf_new(1, 1)?;
    pixbuf.put_pixel(0, 0, 0, 0, 0, 0);

    Ok(pixbuf)
}

pub fn pixbuf_from_surface(surface: &SharedImageSurface) -> Result<Pixbuf, RenderingError> {
    let width = surface.width();
    let height = surface.height();

    let pixbuf = pixbuf_new(width, height)?;
    let bounds = IRect::from_size(width, height);

    for (x, y, pixel) in Pixels::within(&surface, bounds) {
        let (r, g, b, a) = if pixel.a == 0 {
            (0, 0, 0, 0)
        } else {
            let pixel = pixel.unpremultiply();
            (pixel.r, pixel.g, pixel.b, pixel.a)
        };

        // FIXME: Use pixbuf.put_pixel when
        // https://github.com/gtk-rs/gdk-pixbuf/issues/147
        // is integrated
        my_put_pixel(&pixbuf, x as i32, y as i32, r, g, b, a);
    }

    Ok(pixbuf)
}

// Copied from gtk-rs/gdk-pixbuf
//
// See the following:
//   https://gitlab.gnome.org/GNOME/librsvg/-/issues/584
//   https://github.com/gtk-rs/gdk-pixbuf/issues/147
//
// Arithmetic can overflow in the computation of `pos` if it is not done with usize
// values (everything coming out of a Pixbuf is i32).
//
// When this fix appears in a gtk-rs release, we can remove this.
fn my_put_pixel(pixbuf: &Pixbuf, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8) {
    unsafe {
        let x = x as usize;
        let y = y as usize;
        let n_channels = pixbuf.get_n_channels() as usize;
        assert!(n_channels == 3 || n_channels == 4);
        let rowstride = pixbuf.get_rowstride() as usize;
        let pixels = pixbuf.get_pixels();
        let pos = y * rowstride + x * n_channels;

        pixels[pos] = red;
        pixels[pos + 1] = green;
        pixels[pos + 2] = blue;
        if n_channels == 4 {
            pixels[pos + 3] = alpha;
        }
    }
}

enum SizeKind {
    Zoom,
    WidthHeight,
    WidthHeightMax,
    ZoomMax,
}

struct SizeMode {
    kind: SizeKind,
    x_zoom: f64,
    y_zoom: f64,
    width: i32,
    height: i32,
}

fn get_final_size(dimensions: &RsvgDimensionData, size_mode: &SizeMode) -> (i32, i32) {
    if dimensions.width == 0 || dimensions.height == 0 {
        return (0, 0);
    }

    let in_width = dimensions.width;
    let in_height = dimensions.height;

    let mut out_width;
    let mut out_height;

    match size_mode.kind {
        SizeKind::Zoom => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;
        }

        SizeKind::ZoomMax => {
            out_width = (size_mode.x_zoom * f64::from(in_width) + 0.5).floor() as i32;
            out_height = (size_mode.y_zoom * f64::from(in_height) + 0.5).floor() as i32;

            if out_width > size_mode.width || out_height > size_mode.height {
                let zoom_x = f64::from(size_mode.width) / f64::from(out_width);
                let zoom_y = f64::from(size_mode.height) / f64::from(out_height);
                let zoom = zoom_x.min(zoom_y);

                out_width = (zoom * f64::from(out_width) + 0.5) as i32;
                out_height = (zoom * f64::from(out_height) + 0.5) as i32;
            }
        }

        SizeKind::WidthHeightMax => {
            let zoom_x = f64::from(size_mode.width) / f64::from(in_width);
            let zoom_y = f64::from(size_mode.height) / f64::from(in_height);

            let zoom = zoom_x.min(zoom_y);

            out_width = (zoom * f64::from(in_width) + 0.5) as i32;
            out_height = (zoom * f64::from(in_height) + 0.5) as i32;
        }

        SizeKind::WidthHeight => {
            if size_mode.width != -1 {
                out_width = size_mode.width;
            } else {
                out_width = in_width;
            }

            if size_mode.height != -1 {
                out_height = size_mode.height;
            } else {
                out_height = in_height;
            }
        }
    }

    (out_width, out_height)
}

fn render_to_pixbuf_at_size(
    handle: &Handle,
    dimensions: &RsvgDimensionData,
    width: i32,
    height: i32,
    dpi: Dpi,
) -> Result<Pixbuf, RenderingError> {
    if width == 0 || height == 0 || dimensions.width == 0 || dimensions.height == 0 {
        return empty_pixbuf();
    }

    let surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;

    {
        let cr = cairo::Context::new(&surface);
        cr.scale(
            f64::from(width) / f64::from(dimensions.width),
            f64::from(height) / f64::from(dimensions.height),
        );

        let viewport = cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: f64::from(dimensions.width),
            height: f64::from(dimensions.height),
        };

        handle.render_document(&cr, &viewport, dpi, false)?;
    }

    let shared_surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;

    pixbuf_from_surface(&shared_surface)
}

fn get_default_dpi() -> Dpi {
    // This is ugly, but it preserves the C API semantics of
    //
    //   rsvg_set_default_dpi(...);
    //   pixbuf = rsvg_pixbuf_from_file(...);
    //
    // Passing negative numbers here means that the global default DPI will be used.
    Dpi::new(-1.0, -1.0)
}

fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
    Ok(Url::parse(&file.get_uri()).map_err(|_| LoadingError::BadUrl)?)
}

fn pixbuf_from_file_with_size_mode(
    filename: *const libc::c_char,
    size_mode: &SizeMode,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    let dpi = get_default_dpi();

    unsafe {
        let path = PathBuf::from_glib_none(filename);
        let file = gio::File::new_for_path(path);

        let base_url = match url_from_file(&file) {
            Ok(url) => url,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        let load_options = LoadOptions::new(Some(base_url));

        let cancellable: Option<&gio::Cancellable> = None;
        let handle = match file
            .read(cancellable)
            .map_err(|e| LoadingError::from(e))
            .and_then(|stream| Handle::from_stream(&load_options, stream.as_ref(), None))
        {
            Ok(handle) => handle,
            Err(e) => {
                set_gerror(error, 0, &format!("{}", e));
                return ptr::null_mut();
            }
        };

        handle
            .get_dimensions_sub(None, dpi, &SizeCallback::default(), false)
            .and_then(|dimensions| {
                let (width, height) = get_final_size(&dimensions, size_mode);

                render_to_pixbuf_at_size(&handle, &dimensions, width, height, dpi)
            })
            .and_then(|pixbuf| Ok(pixbuf.to_glib_full()))
            .unwrap_or_else(|e| {
                set_gerror(error, 0, &format!("{}", e));
                ptr::null_mut()
            })
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_size(
    filename: *const libc::c_char,
    width: i32,
    height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_size => ptr::null_mut();

        !filename.is_null(),
        (width >= 1 && height >= 1) || (width == -1 && height == -1),
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeight,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width,
            height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::Zoom,
            x_zoom,
            y_zoom,
            width: 0,
            height: 0,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_zoom_with_max(
    filename: *const libc::c_char,
    x_zoom: f64,
    y_zoom: f64,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_zoom_with_max => ptr::null_mut();

        !filename.is_null(),
        x_zoom > 0.0 && y_zoom > 0.0,
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::ZoomMax,
            x_zoom,
            y_zoom,
            width: max_width,
            height: max_height,
        },
        error,
    )
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_rust_pixbuf_from_file_at_max_size(
    filename: *const libc::c_char,
    max_width: i32,
    max_height: i32,
    error: *mut *mut glib_sys::GError,
) -> *mut gdk_pixbuf_sys::GdkPixbuf {
    rsvg_return_val_if_fail! {
        rsvg_pixbuf_from_file_at_max_size => ptr::null_mut();

        !filename.is_null(),
        max_width >= 1 && max_height >= 1,
        error.is_null() || (*error).is_null(),
    }

    pixbuf_from_file_with_size_mode(
        filename,
        &SizeMode {
            kind: SizeKind::WidthHeightMax,
            x_zoom: 0.0,
            y_zoom: 0.0,
            width: max_width,
            height: max_height,
        },
        error,
    )
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/largest-local-values-in-a-matrix/
pub fn largest_local(grid: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
    todo!()
}

fn main() {
    println!(
        "{:?}",
        largest_local(vec![
            vec![9, 9, 8, 1],
            vec![5, 6, 2, 6],
            vec![8, 2, 6, 4],
            vec![6, 2, 2, 2]
        ])
    ); // [[9,9],[8,6]]
    println!(
        "{:?}",
        largest_local(vec![
            vec![1, 1, 1, 1, 1],
            vec![1, 1, 1, 1, 1],
            vec![1, 1, 2, 1, 1],
            vec![1, 1, 1, 1, 1],
            vec![1, 1, 1, 1, 1]
        ])
    ); // [[2,2,2],[2,2,2],[2,2,2]]
}
<commit_msg>Solve problem 2373<commit_after>// https://leetcode.com/problems/largest-local-values-in-a-matrix/
pub fn largest_local(grid: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
    let n = grid.len();
    let mut answer = vec![vec![0; n - 2]; n - 2];
    for i in 0..(n - 2) {
        for j in 0..(n - 2) {
            let mut max = 0;
            for row in i..(i + 3) {
                for col in j..(j + 3) {
                    max = max.max(grid[row][col]);
                }
            }
            answer[i][j] = max;
        }
    }
    answer
}

fn main() {
    println!(
        "{:?}",
        largest_local(vec![
            vec![9, 9, 8, 1],
            vec![5, 6, 2, 6],
            vec![8, 2, 6, 4],
            vec![6, 2, 2, 2]
        ])
    ); // [[9,9],[8,6]]
    println!(
        "{:?}",
        largest_local(vec![
            vec![1, 1, 1, 1, 1],
            vec![1, 1, 1, 1, 1],
            vec![1, 1, 2, 1, 1],
            vec![1, 1, 1, 1, 1],
            vec![1, 1, 1, 1, 1]
        ])
    ); // [[2,2,2],[2,2,2],[2,2,2]]
}
<|endoftext|>


<commit_before>extern crate glm;

use sdl2::keyboard::Keycode;
use std::collections::HashSet;
use self::glm::*;

pub struct InputManagerImp {
    pressed_keys: HashSet<Keycode>,
    previously_pressed_keys: HashSet<Keycode>,
    mouse_coords: glm::Vector2<f64>,
}

impl InputManagerImp {
    pub fn new() -> Self {
        InputManagerImp {
            pressed_keys: HashSet::new(),
            previously_pressed_keys: HashSet::new(),
            mouse_coords: dvec2(0., 0.),
        }
    }

    pub fn update(&mut self) {
        self.previously_pressed_keys = self.pressed_keys.clone();
    }

    pub fn set_mouse_coords(&mut self, x: f64, y: f64) {
        self.mouse_coords = dvec2(x, y);
    }

    pub fn press_key(&mut self, keycode: Keycode) {
        self.pressed_keys.insert(keycode);
    }

    pub fn release_key(&mut self, keycode: Keycode) {
        self.pressed_keys.remove(&keycode);
    }

    pub fn is_key_down(&self, keycode: Keycode) -> bool {
        self.pressed_keys.contains(&keycode)
    }

    pub fn was_key_pressed(&self, keycode: Keycode) -> bool {
        self.pressed_keys.contains(&keycode) && !self.previously_pressed_keys.contains(&keycode)
    }

    pub fn was_key_released(&self, keycode: Keycode) -> bool {
        !self.pressed_keys.contains(&keycode) && self.previously_pressed_keys.contains(&keycode)
    }

    pub fn get_mouse_coords(&self) -> glm::Vector2<f64> {
        self.mouse_coords
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sdl2::keyboard::Keycode;

    #[test]
    fn it_adds_pressed_keys() {
        let mut subject = InputManagerImp::new();
        subject.press_key(Keycode::Down);
        assert_eq!(subject.is_key_down(Keycode::Down), true);
    }
}
<commit_msg>simplified input manager until it needs to do more; added test for releasing keys<commit_after>extern crate glm;

use sdl2::keyboard::Keycode;
use std::collections::HashSet;

pub struct InputManagerImp {
    pressed_keys: HashSet<Keycode>,
}

impl InputManagerImp {
    pub fn new() -> Self {
        InputManagerImp {
            pressed_keys: HashSet::new(),
        }
    }

    pub fn press_key(&mut self, keycode: Keycode) {
        self.pressed_keys.insert(keycode);
    }

    pub fn release_key(&mut self, keycode: Keycode) {
        self.pressed_keys.remove(&keycode);
    }

    pub fn is_key_down(&self, keycode: Keycode) -> bool {
        self.pressed_keys.contains(&keycode)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sdl2::keyboard::Keycode;

    #[test]
    fn it_adds_pressed_keys() {
        let mut subject = InputManagerImp::new();
        assert_eq!(subject.is_key_down(Keycode::Down), false);
        subject.press_key(Keycode::Down);
        assert_eq!(subject.is_key_down(Keycode::Down), true);
    }

    #[test]
    fn it_releases_keys() {
        let mut subject = InputManagerImp::new();
        subject.press_key(Keycode::Down);
        subject.press_key(Keycode::Up);
        subject.release_key(Keycode::Down);
        assert_eq!(subject.is_key_down(Keycode::Down), false);
        assert_eq!(subject.is_key_down(Keycode::Up), true);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};

use failure::{bail, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use regex::Regex;

use crate::utils::cordova::CordovaConfig;
use crate::utils::vcs;
use crate::utils::xcode::InfoPlist;

pub fn get_cordova_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    let here = path.unwrap_or(env::current_dir()?);
    let platform = match here.file_name().and_then(OsStr::to_str) {
        Some("android") => "android",
        Some("ios") => "ios",
        _ => return Ok(None),
    };
    let base = match here.parent().and_then(Path::parent) {
        Some(path) => path,
        None => return Ok(None),
    };

    let path = base.join("config.xml");
    if_chain! {
        if let Ok(md) = path.metadata();
        if md.is_file();
        if let Ok(Some(config)) = CordovaConfig::load(path);
        then {
            match platform {
                "android" => Ok(Some(config.android_release_name())),
                "ios" => Ok(Some(config.ios_release_name())),
                _ => unreachable!(),
            }
        } else {
            Ok(None)
        }
    }
}

pub fn get_xcode_release_name(plist: Option<InfoPlist>) -> Result<Option<String>, Error> {
    // if we are executed from within xcode, then we can use the environment
    // based discovery to get a release name without any interpolation.
    if let Some(plist) = plist.or(InfoPlist::discover_from_env()?) {
        return Ok(Some(plist.get_release_name()));
    }

    Ok(None)
}

pub fn infer_gradle_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    lazy_static! {
        static ref APP_ID_RE: Regex = Regex::new(r#"applicationId\s+["']([^"']*)["']"#).unwrap();
        static ref VERSION_NAME_RE: Regex =
            Regex::new(r#"versionName\s+["']([^"']*)["']"#).unwrap();
    }

    let mut contents = String::new();
    let mut here = path.unwrap_or(env::current_dir()?);
    loop {
        if_chain! {
            if let Ok(build_md) = here.join("build.gradle").metadata();
            if build_md.is_file();
            if let Ok(app_md) = here.join("app/build.gradle").metadata();
            if app_md.is_file();
            if let Ok(mut f) = fs::File::open(here.join("app/build.gradle"));
            if f.read_to_string(&mut contents).is_ok();
            if let Some(app_id_caps) = APP_ID_RE.captures(&contents);
            if let Some(version_caps) = VERSION_NAME_RE.captures(&contents);
            then {
                return Ok(Some(format!("{}-{}", &app_id_caps[1], &version_caps[1])));
            }
        }
        if !here.pop() {
            break;
        }
    }

    Ok(None)
}

/// Detects the release name for the current working directory.
pub fn detect_release_name() -> Result<String, Error> {
    // cordova release detection first.
    if let Some(release) = get_cordova_release_name(None)? {
        return Ok(release);
    }

    // try heroku: https://docs.sentry.io/workflow/integrations/legacy-integrations/heroku/#configure-releases
    if let Ok(release) = env::var("HEROKU_SLUG_COMMIT") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // for now only execute this on macs.  The reason is that this uses
    // xcodebuild which does not exist anywhere but there.
    if_chain! {
        if cfg!(target_os="macos");
        if let Some(release) = get_xcode_release_name(None)?;
        then {
            return Ok(release)
        }
    }

    // For android we badly parse gradle files.  We do this because most of the
    // time now people set the ids and versions in the gradle files instead of
    // the xml manifests.
    if let Some(release) = infer_gradle_release_name(None)? {
        return Ok(release);
    }

    if let Ok(head) = vcs::find_head() {
        Ok(head)
    } else {
        bail!("Could not automatically determine release name");
    }
}
<commit_msg>feat(releases): Detect Heroku's SOURCE_VERSION (#613)<commit_after>use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};

use failure::{bail, Error};
use if_chain::if_chain;
use lazy_static::lazy_static;
use regex::Regex;

use crate::utils::cordova::CordovaConfig;
use crate::utils::vcs;
use crate::utils::xcode::InfoPlist;

pub fn get_cordova_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    let here = path.unwrap_or(env::current_dir()?);
    let platform = match here.file_name().and_then(OsStr::to_str) {
        Some("android") => "android",
        Some("ios") => "ios",
        _ => return Ok(None),
    };
    let base = match here.parent().and_then(Path::parent) {
        Some(path) => path,
        None => return Ok(None),
    };

    let path = base.join("config.xml");
    if_chain! {
        if let Ok(md) = path.metadata();
        if md.is_file();
        if let Ok(Some(config)) = CordovaConfig::load(path);
        then {
            match platform {
                "android" => Ok(Some(config.android_release_name())),
                "ios" => Ok(Some(config.ios_release_name())),
                _ => unreachable!(),
            }
        } else {
            Ok(None)
        }
    }
}

pub fn get_xcode_release_name(plist: Option<InfoPlist>) -> Result<Option<String>, Error> {
    // if we are executed from within xcode, then we can use the environment
    // based discovery to get a release name without any interpolation.
    if let Some(plist) = plist.or(InfoPlist::discover_from_env()?) {
        return Ok(Some(plist.get_release_name()));
    }

    Ok(None)
}

pub fn infer_gradle_release_name(path: Option<PathBuf>) -> Result<Option<String>, Error> {
    lazy_static! {
        static ref APP_ID_RE: Regex = Regex::new(r#"applicationId\s+["']([^"']*)["']"#).unwrap();
        static ref VERSION_NAME_RE: Regex =
            Regex::new(r#"versionName\s+["']([^"']*)["']"#).unwrap();
    }

    let mut contents = String::new();
    let mut here = path.unwrap_or(env::current_dir()?);
    loop {
        if_chain! {
            if let Ok(build_md) = here.join("build.gradle").metadata();
            if build_md.is_file();
            if let Ok(app_md) = here.join("app/build.gradle").metadata();
            if app_md.is_file();
            if let Ok(mut f) = fs::File::open(here.join("app/build.gradle"));
            if f.read_to_string(&mut contents).is_ok();
            if let Some(app_id_caps) = APP_ID_RE.captures(&contents);
            if let Some(version_caps) = VERSION_NAME_RE.captures(&contents);
            then {
                return Ok(Some(format!("{}-{}", &app_id_caps[1], &version_caps[1])));
            }
        }
        if !here.pop() {
            break;
        }
    }

    Ok(None)
}

/// Detects the release name for the current working directory.
pub fn detect_release_name() -> Result<String, Error> {
    // cordova release detection first.
    if let Some(release) = get_cordova_release_name(None)? {
        return Ok(release);
    }

    // try heroku: https://docs.sentry.io/workflow/integrations/legacy-integrations/heroku/#configure-releases
    if let Ok(release) = env::var("HEROKU_SLUG_COMMIT") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // Heroku #2 https://devcenter.heroku.com/changelog-items/630
    if let Ok(release) = env::var("SOURCE_VERSION") {
        if !release.is_empty() {
            return Ok(release);
        }
    }

    // for now only execute this on macs.  The reason is that this uses
    // xcodebuild which does not exist anywhere but there.
    if_chain! {
        if cfg!(target_os="macos");
        if let Some(release) = get_xcode_release_name(None)?;
        then {
            return Ok(release)
        }
    }

    // For android we badly parse gradle files.  We do this because most of the
    // time now people set the ids and versions in the gradle files instead of
    // the xml manifests.
    if let Some(release) = infer_gradle_release_name(None)? {
        return Ok(release);
    }

    if let Ok(head) = vcs::find_head() {
        Ok(head)
    } else {
        bail!("Could not automatically determine release name");
    }
}
<|endoftext|>


<commit_before>extern crate time;

use std::thread;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};

use game;

const BOARD_AREA: u8 = 64;

const STARTING_DEPTH: u8 = 6;
const ENDING_SPRINT: u8 = 5;
const TIME_LIMIT: f64 = 3.0;

const LIGHT_STARTING_SCORE: i16 = -10_000;
const DARK_STARTING_SCORE:  i16 =  10_000;

const RANDOMNESS: i16 = 1;

const BONUS_TURN: i16 = 3;

const MOBILITY: i16 = 1;

pub struct AIHeavy {
    depth: u8,
    time_limit: f64,
}

impl AIHeavy {

    pub fn new() -> AIHeavy {
        AIHeavy {
            depth: STARTING_DEPTH,
            time_limit: TIME_LIMIT,
        }
    }

    pub fn make_move(&mut self, game: &game::Game) -> (usize, usize) {

        if game.get_turn() + self.depth + ENDING_SPRINT >= BOARD_AREA {
            return AIHeavy::find_best_move(game, BOARD_AREA - game.get_turn());
        } else {

            let mut start_time = time::precise_time_s();
            let mut best_move: (usize, usize)  = AIHeavy::find_best_move(game, self.depth);
            let mut end_time = time::precise_time_s();

            while ( end_time - start_time < ( self.time_limit / 4.0 ) ) && ( self.depth + 1 + game.get_turn() <= BOARD_AREA ) {
                self.depth += 1;
                start_time = time::precise_time_s();
                best_move = AIHeavy::find_best_move(game, self.depth);
                end_time = time::precise_time_s();
            }

            self.depth -= 1;
            return best_move;
        }
    }

    fn find_best_move(game: &game::Game, depth: u8) -> (usize, usize) {

        if let game::Status::Running { next_player } = game.get_status() {

            if depth > 0 {

                let mut best_move: (usize, usize) = (game::BOARD_SIZE, game::BOARD_SIZE);
                let mut best_score: i16;

                let mut best_end_move: (usize, usize) = (game::BOARD_SIZE, game::BOARD_SIZE);
                let mut best_end_score: i16;

                let mut num_moves: u8 = 0;
                let mut end_game: bool = true;

                match next_player {
                    game::Player::Light => {
                        best_score = LIGHT_STARTING_SCORE;
                        best_end_score = LIGHT_STARTING_SCORE;
                    }
                    game::Player::Dark  => {
                        best_score = DARK_STARTING_SCORE;
                        best_end_score = DARK_STARTING_SCORE;
                    }
                }

                let (tx, rx): (Sender<((usize, usize), (i16, bool))>, Receiver<((usize, usize), (i16, bool))>) = mpsc::channel();

                for row in 0..game::BOARD_SIZE {
                    for col in 0..game::BOARD_SIZE {

                        if game.check_move((row, col)) {
                            num_moves +=1;

                            let thread_tx = tx.clone();
                            let mut game = game.clone();

                            thread::spawn(move || {
                                thread_tx.send(( (row, col), AIHeavy::eval(game.make_move((row, col)), depth - 1) )).unwrap();
                            });
                        }
                    }
                }

                for _ in 0..num_moves {
                    let (current_move, (current_score, current_end_game)) = rx.recv().ok().expect("Could not receive answer");

                    match next_player {
                        game::Player::Light => {
                            if current_end_game {
                                if current_score > best_end_score {
                                    best_end_score = current_score;
                                    best_end_move = current_move;
                                }
                            } else {
                                if current_score + RANDOMNESS > best_score {
                                    best_score = current_score;
                                    best_move = current_move;
                                    end_game = false;
                                }
                            }
                        }
                        game::Player::Dark  => {
                            if current_end_game {
                                if current_score < best_end_score {
                                    best_end_score = current_score;
                                    best_end_move = current_move;
                                }
                            } else {
                                if current_score - RANDOMNESS < best_score {
                                    best_score = current_score;
                                    best_move = current_move;
                                    end_game = false;
                                }
                            }
                        }
                    }
                }

                match next_player {
                    game::Player::Light  => {
                        if best_end_score > 0 || (best_end_score == 0 && best_score < 0) || end_game {
                            return best_end_move;
                        } else {
                            return best_move;
                        }
                    }
                    game::Player::Dark  => {
                        if best_end_score < 0 || (best_end_score == 0 && best_score > 0) || end_game {
                            return best_end_move;
                        } else {
                            return best_move;
                        }
                    }
                }

            } else {
                panic!("Depth cannot be zero");
            }

        } else {
            panic!{"Game ended, cannot make a move!"};
        }
    }

    fn eval(game: &game::Game, depth: u8) -> (i16, bool) {

        return match game.get_status() {
            game::Status::Ended => (game.get_score_diff(), true),
            game::Status::Running { next_player } => {
                if depth == 0 {
                    match next_player {
                        game::Player::Light => (AIHeavy::heavy_eval(game) + BONUS_TURN, false),
                        game::Player::Dark  => (AIHeavy::heavy_eval(game) - BONUS_TURN, false),
                    }
                } else {
                    let mut end_game: bool = true;
                    let mut num_moves: i16 = 0;
                    let mut best_end_score: i16;
                    let mut best_score: i16;

                    return match next_player {

                        game::Player::Light => {
                            best_score = LIGHT_STARTING_SCORE;
                            best_end_score = LIGHT_STARTING_SCORE;

                            for row in 0..game::BOARD_SIZE {
                                for col in 0..game::BOARD_SIZE {
                                    if game.check_move((row, col)) {

                                        let (current_score, current_end_game) = AIHeavy::eval(game.clone().make_move((row, col)), depth - 1);

                                        if current_end_game {
                                            if current_score > best_end_score {
                                                best_end_score = current_score;
                                            }
                                        } else {
                                            num_moves += 1;
                                            if current_score > best_score {
                                                best_score = current_score;
                                                end_game = false;
                                            }
                                        }

                                    }
                                }
                            }

                            if best_end_score > 0 || (best_end_score == 0 && best_score < 0) || end_game {
                                (best_end_score, true)
                            } else {
                                (best_score + MOBILITY*num_moves, false)
                            }
                        }

                        game::Player::Dark  => {
                            best_score = DARK_STARTING_SCORE;
                            best_end_score = DARK_STARTING_SCORE;

                            for row in 0..game::BOARD_SIZE {
                                for col in 0..game::BOARD_SIZE {
                                    if game.check_move((row, col)) {

                                        let (current_score, current_end_game) = AIHeavy::eval(game.clone().make_move((row, col)), depth - 1);

                                        if current_end_game {
                                            if current_score < best_end_score {
                                                best_end_score = current_score;
                                            }
                                        } else {
                                            num_moves += 1;
                                            if current_score < best_score {
                                                best_score = current_score;
                                                end_game = false;
                                            }
                                        }

                                    }
                                }
                            }

                            if best_end_score < 0 || (best_end_score == 0 && best_score > 0) || end_game {
                                (best_end_score, true)
                            } else {
                                (best_score - MOBILITY*num_moves, false)
                            }
                        }

                    }

                }
            }
        }
    }

    fn heavy_eval(game: &game::Game) -> i16 {
        const CORNER_BONUS: i16 = 15;
        const ODD_MALUS: i16 = 3;
        const EVEN_BONUS: i16 = 3;
        const ODD_CORNER_MALUS: i16 = 10;
        const EVEN_CORNER_BONUS: i16 = 5;
        const FIXED_BONUS: i16 = 3;

        const SIDES: [( (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize) ); 4] = [
            ( (0,0), (0,1), (1,1), (0,2), (2,2), (1,0), (2,0) ), // NW corner
            ( (0,7), (1,7), (1,6), (2,7), (2,5), (0,6), (0,5) ), // NE corner
            ( (7,0), (6,0), (6,1), (5,0), (5,2), (7,1), (7,2) ), // SW corner
            ( (7,7), (6,7), (6,6), (5,7), (5,5), (7,6), (7,6) ), // SE corner
            ];

        //let (score_light, score_dark) = game.get_score();
        let mut score: i16 = 0; // (score_light as i16) - (score_dark as i16);

        for special_cells in SIDES.iter() {

            let (corner, odd, odd_corner, even, even_corner, counter_odd, counter_even) = *special_cells;

            if let game::Cell::Taken { player } = game.get_cell(corner) {
                match player {
                    game::Player::Light => {
                        score += CORNER_BONUS;
                        if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(odd) {
                            score += FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(even) {
                                score += FIXED_BONUS;
                            }
                        }
                        if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(counter_odd) {
                            score += FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(counter_even) {
                                score += FIXED_BONUS;
                            }
                        }
                    }
                    game::Player::Dark => {
                        score -= CORNER_BONUS;
                        if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(odd) {
                            score -= FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(even) {
                                score -= FIXED_BONUS;
                            }
                        }
                        if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(counter_odd) {
                            score -= FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(counter_even) {
                                score -= FIXED_BONUS;
                            }
                        }
                    }
                }

            } else {

                if let game::Cell::Taken { player } = game.get_cell(odd) {
                    match player {
                        game::Player::Light => score -= ODD_MALUS,
                        game::Player::Dark  => score += ODD_MALUS,
                    }
                } else if let game::Cell::Taken { player } = game.get_cell(even) {
                    match player {
                        game::Player::Light => score += EVEN_BONUS,
                        game::Player::Dark  => score -= EVEN_BONUS,
                    }
                }

                if let game::Cell::Taken { player } = game.get_cell(counter_odd) {
                    match player {
                        game::Player::Light => score -= ODD_MALUS,
                        game::Player::Dark  => score += ODD_MALUS,
                    }
                } else if let game::Cell::Taken { player } = game.get_cell(counter_even) {
                    match player {
                        game::Player::Light => score += EVEN_BONUS,
                        game::Player::Dark  => score -= EVEN_BONUS,
                    }
                }

                if let game::Cell::Taken { player } = game.get_cell(odd_corner) {
                    match player {
                        game::Player::Light => score -= ODD_CORNER_MALUS,
                        game::Player::Dark  => score += ODD_CORNER_MALUS,
                    }

                } else if let game::Cell::Taken { player } = game.get_cell(even_corner) {
                    match player {
                        game::Player::Light => score += EVEN_CORNER_BONUS,
                        game::Player::Dark  => score -= EVEN_CORNER_BONUS,
                    }
                }
            }
        }

        score
    }

}
<commit_msg>Code polish<commit_after>extern crate time;

use std::thread;
use std::sync::mpsc;
use std::sync::mpsc::{Sender, Receiver};

use game;

const BOARD_AREA: u8 = 64;

const STARTING_DEPTH: u8 = 6;
const ENDING_SPRINT: u8 = 6;
const TIME_LIMIT: f64 = 2.0;

const LIGHT_STARTING_SCORE: i16 = -10_000;
const DARK_STARTING_SCORE:  i16 =  10_000;

const RANDOMNESS: i16 = 1;

const BONUS_TURN: i16 = 3;

const MOBILITY: i16 = 1;

pub struct AIHeavy {
    depth: u8,
    time_limit: f64,
}

impl AIHeavy {

    pub fn new() -> AIHeavy {
        AIHeavy {
            depth: STARTING_DEPTH,
            time_limit: TIME_LIMIT,
        }
    }

    pub fn make_move(&mut self, game: &game::Game) -> (usize, usize) {

        if game.get_turn() + self.depth + ENDING_SPRINT >= BOARD_AREA {
            return AIHeavy::find_best_move(game, BOARD_AREA - game.get_turn());
        } else {

            let mut start_time = time::precise_time_s();
            let mut best_move: (usize, usize)  = AIHeavy::find_best_move(game, self.depth);
            let mut end_time = time::precise_time_s();

            while ( end_time - start_time < ( self.time_limit / 4.0 ) ) && ( self.depth + 1 + game.get_turn() <= BOARD_AREA ) {
                self.depth += 1;
                start_time = time::precise_time_s();
                best_move = AIHeavy::find_best_move(game, self.depth);
                end_time = time::precise_time_s();
            }

            self.depth -= 1;
            return best_move;
        }
    }

    fn find_best_move(game: &game::Game, depth: u8) -> (usize, usize) {

        if let game::Status::Running { next_player } = game.get_status() {

            if depth > 0 {

                let mut best_move: (usize, usize) = (game::BOARD_SIZE, game::BOARD_SIZE);
                let mut best_score: i16;

                let mut best_end_move: (usize, usize) = (game::BOARD_SIZE, game::BOARD_SIZE);
                let mut best_end_score: i16;

                let mut num_moves: u8 = 0;
                let mut end_game: bool = true;

                match next_player {
                    game::Player::Light => {
                        best_score = LIGHT_STARTING_SCORE;
                        best_end_score = LIGHT_STARTING_SCORE;
                    }
                    game::Player::Dark  => {
                        best_score = DARK_STARTING_SCORE;
                        best_end_score = DARK_STARTING_SCORE;
                    }
                }

                let (tx, rx): (Sender<((usize, usize), (i16, bool))>, Receiver<((usize, usize), (i16, bool))>) = mpsc::channel();

                for row in 0..game::BOARD_SIZE {
                    for col in 0..game::BOARD_SIZE {

                        if game.check_move((row, col)) {
                            num_moves +=1;

                            let thread_tx = tx.clone();
                            let mut game = game.clone();

                            thread::spawn(move || {
                                thread_tx.send(( (row, col), AIHeavy::eval(game.make_move((row, col)), depth - 1) )).unwrap();
                            });
                        }
                    }
                }

                for _ in 0..num_moves {
                    let (current_move, (current_score, current_end_game)) = rx.recv().ok().expect("Could not receive answer");

                    match next_player {
                        game::Player::Light => {
                            if current_end_game {
                                if current_score > best_end_score {
                                    best_end_score = current_score;
                                    best_end_move = current_move;
                                }
                            } else {
                                if current_score + RANDOMNESS > best_score {
                                    best_score = current_score;
                                    best_move = current_move;
                                    end_game = false;
                                }
                            }
                        }
                        game::Player::Dark  => {
                            if current_end_game {
                                if current_score < best_end_score {
                                    best_end_score = current_score;
                                    best_end_move = current_move;
                                }
                            } else {
                                if current_score - RANDOMNESS < best_score {
                                    best_score = current_score;
                                    best_move = current_move;
                                    end_game = false;
                                }
                            }
                        }
                    }
                }

                match next_player {
                    game::Player::Light  => {
                        if best_end_score > 0 || (best_end_score == 0 && best_score < 0) || end_game {
                            return best_end_move;
                        } else {
                            return best_move;
                        }
                    }
                    game::Player::Dark  => {
                        if best_end_score < 0 || (best_end_score == 0 && best_score > 0) || end_game {
                            return best_end_move;
                        } else {
                            return best_move;
                        }
                    }
                }

            } else {
                panic!("Depth cannot be zero");
            }

        } else {
            panic!{"Game ended, cannot make a move!"};
        }
    }

    fn eval(game: &game::Game, depth: u8) -> (i16, bool) {

        match game.get_status() {
            game::Status::Ended => (game.get_score_diff(), true),
            game::Status::Running { next_player } => {
                if depth == 0 {
                    match next_player {
                        game::Player::Light => (AIHeavy::heavy_eval(game) + BONUS_TURN, false),
                        game::Player::Dark  => (AIHeavy::heavy_eval(game) - BONUS_TURN, false),
                    }
                } else {

                    match next_player {

                        game::Player::Light => {
                            let mut end_game: bool = true;
                            let mut num_moves: i16 = 0;
                            let mut best_score = LIGHT_STARTING_SCORE;
                            let mut best_end_score: i16 = LIGHT_STARTING_SCORE;

                            for row in 0..game::BOARD_SIZE {
                                for col in 0..game::BOARD_SIZE {
                                    if game.check_move((row, col)) {

                                        let (current_score, current_end_game) = AIHeavy::eval(game.clone().make_move((row, col)), depth - 1);

                                        if current_end_game && current_score > best_end_score {
                                            best_end_score = current_score;
                                        } else {
                                            num_moves += 1;
                                            end_game = false;
                                            if current_score > best_score {
                                                best_score = current_score;
                                            }
                                        }

                                    }
                                }
                            }

                            if end_game || best_end_score > 0 || (best_end_score == 0 && best_score < 0) {
                                (best_end_score, true)
                            } else {
                                (best_score + MOBILITY*num_moves, false)
                            }
                        }

                        game::Player::Dark  => {
                            let mut end_game: bool = true;
                            let mut num_moves: i16 = 0;
                            let mut best_score = DARK_STARTING_SCORE;
                            let mut best_end_score: i16 = DARK_STARTING_SCORE;

                            for row in 0..game::BOARD_SIZE {
                                for col in 0..game::BOARD_SIZE {
                                    if game.check_move((row, col)) {

                                        let (current_score, current_end_game) = AIHeavy::eval(game.clone().make_move((row, col)), depth - 1);

                                        if current_end_game && current_score < best_end_score {
                                            best_end_score = current_score;
                                        } else {
                                            end_game = false;
                                            num_moves += 1;
                                            if current_score < best_score {
                                                best_score = current_score;
                                            }
                                        }

                                    }
                                }
                            }

                            if end_game || best_end_score < 0 || (best_end_score == 0 && best_score > 0) {
                                (best_end_score, true)
                            } else {
                                (best_score - MOBILITY*num_moves, false)
                            }
                        }

                    }

                }
            }
        }
    }

    fn heavy_eval(game: &game::Game) -> i16 {
        const CORNER_BONUS: i16 = 15;
        const ODD_MALUS: i16 = 3;
        const EVEN_BONUS: i16 = 3;
        const ODD_CORNER_MALUS: i16 = 10;
        const EVEN_CORNER_BONUS: i16 = 5;
        const FIXED_BONUS: i16 = 3;

        const SIDES: [( (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize), (usize, usize) ); 4] = [
            ( (0,0), (0,1), (1,1), (0,2), (2,2), (1,0), (2,0) ), // NW corner
            ( (0,7), (1,7), (1,6), (2,7), (2,5), (0,6), (0,5) ), // NE corner
            ( (7,0), (6,0), (6,1), (5,0), (5,2), (7,1), (7,2) ), // SW corner
            ( (7,7), (6,7), (6,6), (5,7), (5,5), (7,6), (7,6) ), // SE corner
            ];

        //let (score_light, score_dark) = game.get_score();
        let mut score: i16 = ( game.get_score_diff() * FIXED_BONUS * game.get_turn() as i16 ) / 64; // (score_light as i16) - (score_dark as i16);

        for &(corner, odd, odd_corner, even, even_corner, counter_odd, counter_even) in SIDES.iter() {

            if let game::Cell::Taken { player } = game.get_cell(corner) {
                match player {
                    game::Player::Light => {
                        score += CORNER_BONUS;
                        if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(odd) {
                            score += FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(even) {
                                score += FIXED_BONUS;
                            }
                        }
                        if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(counter_odd) {
                            score += FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Light } = game.get_cell(counter_even) {
                                score += FIXED_BONUS;
                            }
                        }
                    }
                    game::Player::Dark => {
                        score -= CORNER_BONUS;
                        if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(odd) {
                            score -= FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(even) {
                                score -= FIXED_BONUS;
                            }
                        }
                        if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(counter_odd) {
                            score -= FIXED_BONUS;
                            if let game::Cell::Taken { player: game::Player::Dark } = game.get_cell(counter_even) {
                                score -= FIXED_BONUS;
                            }
                        }
                    }
                }

            } else {

                if let game::Cell::Taken { player } = game.get_cell(odd) {
                    match player {
                        game::Player::Light => score -= ODD_MALUS,
                        game::Player::Dark  => score += ODD_MALUS,
                    }
                } else if let game::Cell::Taken { player } = game.get_cell(even) {
                    match player {
                        game::Player::Light => score += EVEN_BONUS,
                        game::Player::Dark  => score -= EVEN_BONUS,
                    }
                }

                if let game::Cell::Taken { player } = game.get_cell(counter_odd) {
                    match player {
                        game::Player::Light => score -= ODD_MALUS,
                        game::Player::Dark  => score += ODD_MALUS,
                    }
                } else if let game::Cell::Taken { player } = game.get_cell(counter_even) {
                    match player {
                        game::Player::Light => score += EVEN_BONUS,
                        game::Player::Dark  => score -= EVEN_BONUS,
                    }
                }

                if let game::Cell::Taken { player } = game.get_cell(odd_corner) {
                    match player {
                        game::Player::Light => score -= ODD_CORNER_MALUS,
                        game::Player::Dark  => score += ODD_CORNER_MALUS,
                    }

                } else if let game::Cell::Taken { player } = game.get_cell(even_corner) {
                    match player {
                        game::Player::Light => score += EVEN_CORNER_BONUS,
                        game::Player::Dark  => score -= EVEN_CORNER_BONUS,
                    }
                }
            }
        }

        score
    }

}
<|endoftext|>


<commit_before>#![feature(core_intrinsics)]

use std::intrinsics;
use std::mem::{size_of, size_of_val};

struct Bomb;

impl Drop for Bomb {
    fn drop(&mut self) {
        eprintln!("BOOM!");
    }
}

fn main() {
    assert_eq!(size_of::<Option<i32>>(), 8);
    assert_eq!(size_of_val(&()), 0);
    assert_eq!(size_of_val(&42), 4);
    assert_eq!(size_of_val(&[] as &[i32]), 0);
    assert_eq!(size_of_val(&[1, 2, 3] as &[i32]), 12);
    assert_eq!(size_of_val("foobar"), 6);

    assert_eq!(intrinsics::type_name::<Option<i32>>(), "core::option::Option<i32>");

    assert_eq!(intrinsics::likely(false), false);
    assert_eq!(intrinsics::unlikely(true), true);

    unsafe { intrinsics::forget(Bomb); }
}
<commit_msg>test discriminant_value intrinsic<commit_after>#![feature(core_intrinsics)]

use std::intrinsics;
use std::mem::{size_of, size_of_val};

struct Bomb;

impl Drop for Bomb {
    fn drop(&mut self) {
        eprintln!("BOOM!");
    }
}

fn main() {
    assert_eq!(size_of::<Option<i32>>(), 8);
    assert_eq!(size_of_val(&()), 0);
    assert_eq!(size_of_val(&42), 4);
    assert_eq!(size_of_val(&[] as &[i32]), 0);
    assert_eq!(size_of_val(&[1, 2, 3] as &[i32]), 12);
    assert_eq!(size_of_val("foobar"), 6);

    assert_eq!(intrinsics::type_name::<Option<i32>>(), "core::option::Option<i32>");

    assert_eq!(intrinsics::likely(false), false);
    assert_eq!(intrinsics::unlikely(true), true);

    unsafe { intrinsics::forget(Bomb); }

    let _v = intrinsics::discriminant_value(&Some(()));
    let _v = intrinsics::discriminant_value(&0);
    let _v = intrinsics::discriminant_value(&true);
    let _v = intrinsics::discriminant_value(&vec![1,2,3]);
}
<|endoftext|>


<commit_before>//! Asynchronous DNS resolver

use std::io;

use futures::{Future, BoxFuture};

use tokio_core::reactor::Handle;

use domain::resolv::Resolver;
use domain::resolv::lookup::lookup_host;
use domain::bits::DNameBuf;

use std::net::IpAddr;

pub fn resolve(addr: &str, handle: &Handle) -> BoxFuture<IpAddr, io::Error> {
    let dname = addr.parse::<DNameBuf>().unwrap();
    let resolv = Resolver::new(handle);

    trace!("Going to resolve \"{}\"", dname);

    lookup_host(resolv, &dname)
        .map_err(From::from)
        .and_then(move |hosts| {
            match hosts.iter().next() {
                Some(addr) => {
                    trace!("Resolved \"{}\" as {}", dname, addr);
                    Ok(addr)
                }
                None => {
                    let err = io::Error::new(io::ErrorKind::Other,
                                             format!("Failed to resolve \"{}\"", dname));
                    Err(err)
                }
            }
        })
        .boxed()
}
<commit_msg>fix parse DNameBuf panic<commit_after>//! Asynchronous DNS resolver

use std::io;

use futures::{Future, BoxFuture};
use futures::future;

use tokio_core::reactor::Handle;

use domain::resolv::Resolver;
use domain::resolv::lookup::lookup_host;
use domain::bits::DNameBuf;

use std::net::IpAddr;

pub fn resolve(addr: &str, handle: &Handle) -> BoxFuture<IpAddr, io::Error> {
    let dname = match addr.parse::<DNameBuf>() {
        Ok(dname) => dname,
        Err(err) => return future::err(io::Error::new(io::ErrorKind::Other, err))
            .boxed()
    };
    let resolv = Resolver::new(handle);

    trace!("Going to resolve \"{}\"", dname);

    lookup_host(resolv, &dname)
        .map_err(From::from)
        .and_then(move |hosts| {
            match hosts.iter().next() {
                Some(addr) => {
                    trace!("Resolved \"{}\" as {}", dname, addr);
                    Ok(addr)
                }
                None => {
                    let err = io::Error::new(io::ErrorKind::Other,
                                             format!("Failed to resolve \"{}\"", dname));
                    Err(err)
                }
            }
        })
        .boxed()
}
<|endoftext|>