<commit_before>// Copyright (c) 2017-2019, 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.

#![deny(bare_trait_objects)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::cast_ptr_alignment)]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::verbose_bit_mask)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::many_single_char_names)]

#[macro_use]
extern crate err_derive;
#[macro_use]
extern crate log;

mod common;
mod decoder;
mod error;
mod muxer;
mod stats;

use crate::common::*;
use crate::error::*;
use crate::stats::*;
use rav1e::prelude::*;

use crate::decoder::Decoder;
use crate::decoder::VideoDetails;
use crate::muxer::*;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::sync::Arc;

struct Source<D: Decoder> {
  limit: usize,
  count: usize,
  input: D,
  #[cfg(all(unix, feature = "signal-hook"))]
  exit_requested: Arc<std::sync::atomic::AtomicBool>,
}

impl<D: Decoder> Source<D> {
  fn read_frame<T: Pixel>(
    &mut self, ctx: &mut Context<T>, video_info: VideoDetails,
  ) {
    if self.limit != 0 && self.count == self.limit {
      ctx.flush();
      return;
    }

    #[cfg(all(unix, feature = "signal-hook"))]
    {
      if self.exit_requested.load(std::sync::atomic::Ordering::SeqCst) {
        ctx.flush();
        return;
      }
    }

    match self.input.read_frame(&video_info) {
      Ok(frame) => {
        match video_info.bit_depth {
          8 | 10 | 12 => {}
          _ => panic!("unknown input bit depth!"),
        }
        self.count += 1;
        let _ = ctx.send_frame(Some(Arc::new(frame)));
      }
      _ => {
        ctx.flush();
      }
    };
  }
}

// Encode and write a frame.
// Returns frame information in a `Result`.
fn process_frame<T: Pixel, D: Decoder>(
  ctx: &mut Context<T>, output_file: &mut dyn Muxer, source: &mut Source<D>,
  pass1file: Option<&mut File>, pass2file: Option<&mut File>,
  buffer: &mut [u8], buf_pos: &mut usize,
  mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<Option<Vec<FrameSummary>>, CliError> {
  let y4m_details = source.input.get_video_details();
  let mut frame_summaries = Vec::new();
  let mut pass1file = pass1file;
  let mut pass2file = pass2file;
  // Submit first pass data to pass 2.
  if let Some(passfile) = pass2file.as_mut() {
    loop {
      let mut bytes = ctx.twopass_bytes_needed();
      if bytes == 0 {
        break;
      }
      // Read in some more bytes, if necessary.
      bytes = bytes.min(buffer.len() - *buf_pos);
      if bytes > 0 {
        passfile
          .read_exact(&mut buffer[*buf_pos..*buf_pos + bytes])
          .expect("Could not read frame data from two-pass data file!");
      }
      // And pass them off.
      let consumed = ctx
        .twopass_in(&buffer[*buf_pos..*buf_pos + bytes])
        .expect("Error submitting pass data in second pass.");
      // If the encoder consumed the whole buffer, reset it.
      if consumed >= bytes {
        *buf_pos = 0;
      } else {
        *buf_pos += consumed;
      }
    }
  }
  // Extract first pass data from pass 1.
  // We call this before encoding any frames to ensure we are in 2-pass mode
  //  and to get the placeholder header data.
  if let Some(passfile) = pass1file.as_mut() {
    if let Some(outbuf) = ctx.twopass_out() {
      passfile
        .write_all(outbuf)
        .expect("Unable to write to two-pass data file.");
    }
  }

  let pkt_wrapped = ctx.receive_packet();
  match pkt_wrapped {
    Ok(pkt) => {
      output_file.write_frame(
        pkt.input_frameno as u64,
        pkt.data.as_ref(),
        pkt.frame_type,
      );
      if let (Some(ref mut y4m_enc_uw), Some(ref rec)) =
        (y4m_enc.as_mut(), &pkt.rec)
      {
        write_y4m_frame(y4m_enc_uw, rec, y4m_details);
      }
      frame_summaries.push(pkt.into());
    }
    Err(EncoderStatus::NeedMoreData) => {
      source.read_frame(ctx, y4m_details);
    }
    Err(EncoderStatus::EnoughData) => {
      unreachable!();
    }
    Err(EncoderStatus::LimitReached) => {
      if let Some(passfile) = pass1file.as_mut() {
        if let Some(outbuf) = ctx.twopass_out() {
          // The last block of data we get is the summary data that needs to go
          //  at the start of the pass file.
          // Seek to the start so we can write it there.
          passfile
            .seek(std::io::SeekFrom::Start(0))
            .expect("Unable to seek in two-pass data file.");
          passfile
            .write_all(outbuf)
            .expect("Unable to write to two-pass data file.");
        }
      }
      return Ok(None);
    }
    e @ Err(EncoderStatus::Failure) => {
      let _ = e.map_err(|e| e.context("Failed to encode video"))?;
    }
    e @ Err(EncoderStatus::NotReady) => {
      let _ = e.map_err(|e| {
        e.context("Mismanaged handling of two-pass stats data")
      })?;
    }
    Err(EncoderStatus::Encoded) => {}
  }
  Ok(Some(frame_summaries))
}

fn do_encode<T: Pixel, D: Decoder>(
  cfg: Config, verbose: bool, mut progress: ProgressInfo,
  output: &mut dyn Muxer, source: &mut Source<D>,
  pass1file_name: Option<&String>, pass2file_name: Option<&String>,
  mut y4m_enc: Option<y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<(), CliError> {
  let mut ctx: Context<T> =
    cfg.new_context().map_err(|e| e.context("Invalid encoder settings"))?;

  let mut pass2file = pass2file_name.map(|f| {
    File::open(f).unwrap_or_else(|_| {
      panic!("Unable to open \"{}\" for reading two-pass data.", f)
    })
  });
  let mut pass1file = pass1file_name.map(|f| {
    File::create(f).unwrap_or_else(|_| {
      panic!("Unable to open \"{}\" for writing two-pass data.", f)
    })
  });

  let mut buffer: [u8; 80] = [0; 80];
  let mut buf_pos = 0;

  while let Some(frame_info) = process_frame(
    &mut ctx,
    &mut *output,
    source,
    pass1file.as_mut(),
    pass2file.as_mut(),
    &mut buffer,
    &mut buf_pos,
    y4m_enc.as_mut(),
  )? {
    for frame in frame_info {
      progress.add_frame(frame.clone());
      if verbose {
        info!("{} - {}", frame, progress);
      } else {
        // Print a one-line progress indicator that overrides itself with every update
        eprint!("\r{}                    ", progress);
      };
    }

    output.flush().unwrap();
  }
  if !verbose {
    // Clear out the temporary progress indicator
    eprint!("\r");
  }
  progress.print_summary(verbose);
  Ok(())
}

fn main() {
  #[cfg(feature = "tracing")]
  use rust_hawktracer::*;
  better_panic::install();
  init_logger();

  #[cfg(feature = "tracing")]
  let instance = HawktracerInstance::new();
  #[cfg(feature = "tracing")]
  let _listener = instance.create_listener(HawktracerListenerType::ToFile {
    file_path: "trace.bin".into(),
    buffer_size: 4096,
  });

  match run() {
    Ok(()) => {}
    Err(e) => error::print_error(&e),
  }
}

fn init_logger() {
  if env::var("RUST_LOG").is_err() {
    env::set_var("RUST_LOG", "info");
  }
  pretty_env_logger::init();
}

fn run() -> Result<(), error::CliError> {
  let mut cli = parse_cli()?;
  // Maximum frame size by specification + maximum y4m header
  let limit = y4m::Limits { bytes: 64 * 64 * 4096 * 2304 + 1024 };
  let mut y4m_dec = y4m::Decoder::new_with_limits(&mut cli.io.input, limit)
    .expect("cannot decode the input");
  let video_info = y4m_dec.get_video_details();
  let y4m_enc = match cli.io.rec.as_mut() {
    Some(rec) => Some(
      y4m::encode(
        video_info.width,
        video_info.height,
        y4m::Ratio::new(
          video_info.time_base.den as usize,
          video_info.time_base.num as usize,
        ),
      )
      .with_colorspace(y4m_dec.get_colorspace())
      .write_header(rec)
      .unwrap(),
    ),
    None => None,
  };

  cli.enc.width = video_info.width;
  cli.enc.height = video_info.height;
  cli.enc.bit_depth = video_info.bit_depth;
  cli.enc.chroma_sampling = video_info.chroma_sampling;
  cli.enc.chroma_sample_position = video_info.chroma_sample_position;

  // If no pixel range is specified via CLI, assume limited,
  // as it is the default for the Y4M format.
  if !cli.color_range_specified {
    cli.enc.pixel_range = PixelRange::Limited;
  }

  if !cli.override_time_base {
    cli.enc.time_base = video_info.time_base;
  }

  let cfg = Config { enc: cli.enc, threads: cli.threads };

  cli.io.output.write_header(
    video_info.width,
    video_info.height,
    cli.enc.time_base.den as usize,
    cli.enc.time_base.num as usize,
  );

  info!(
    "Using y4m decoder: {}x{}p @ {}/{} fps, {}, {}-bit",
    video_info.width,
    video_info.height,
    video_info.time_base.den,
    video_info.time_base.num,
    video_info.chroma_sampling,
    video_info.bit_depth
  );
  info!("Encoding settings: {}", cfg.enc);

  let progress = ProgressInfo::new(
    Rational { num: video_info.time_base.den, den: video_info.time_base.num },
    if cli.limit == 0 { None } else { Some(cli.limit) },
    cfg.enc.show_psnr,
  );

  for _ in 0..cli.skip {
    y4m_dec.read_frame().expect("Skipped more frames than in the input");
  }

  #[cfg(all(unix, feature = "signal-hook"))]
  let exit_requested = {
    use std::sync::atomic::*;
    let e = Arc::new(AtomicBool::from(false));

    fn setup_signal(sig: i32, e: Arc<AtomicBool>) {
      unsafe {
        signal_hook::register(sig, move || {
          if e.load(Ordering::SeqCst) {
            std::process::exit(128 + sig);
          }
          e.store(true, Ordering::SeqCst);
          info!("Exit requested, flushing.");
        })
        .expect("Cannot register the signal hooks");
      }
    }

    setup_signal(signal_hook::SIGTERM, e.clone());
    setup_signal(signal_hook::SIGQUIT, e.clone());
    setup_signal(signal_hook::SIGINT, e.clone());

    e
  };

  #[cfg(all(unix, feature = "signal-hook"))]
  let mut source =
    Source { limit: cli.limit, input: y4m_dec, count: 0, exit_requested };
  #[cfg(not(all(unix, feature = "signal-hook")))]
  let mut source = Source { limit: cli.limit, input: y4m_dec, count: 0 };

  if video_info.bit_depth == 8 {
    do_encode::<u8, y4m::Decoder<'_, Box<dyn Read>>>(
      cfg,
      cli.verbose,
      progress,
      &mut *cli.io.output,
      &mut source,
      cli.pass1file_name.as_ref(),
      cli.pass2file_name.as_ref(),
      y4m_enc,
    )?
  } else {
    do_encode::<u16, y4m::Decoder<'_, Box<dyn Read>>>(
      cfg,
      cli.verbose,
      progress,
      &mut *cli.io.output,
      &mut source,
      cli.pass1file_name.as_ref(),
      cli.pass2file_name.as_ref(),
      y4m_enc,
    )?
  }

  Ok(())
}
<commit_msg>Avoid constant arithmetic overflow on 32-bit<commit_after>// Copyright (c) 2017-2019, 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.

#![deny(bare_trait_objects)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::cast_ptr_alignment)]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::verbose_bit_mask)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::many_single_char_names)]

#[macro_use]
extern crate err_derive;
#[macro_use]
extern crate log;

mod common;
mod decoder;
mod error;
mod muxer;
mod stats;

use crate::common::*;
use crate::error::*;
use crate::stats::*;
use rav1e::prelude::*;

use crate::decoder::Decoder;
use crate::decoder::VideoDetails;
use crate::muxer::*;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::sync::Arc;

struct Source<D: Decoder> {
  limit: usize,
  count: usize,
  input: D,
  #[cfg(all(unix, feature = "signal-hook"))]
  exit_requested: Arc<std::sync::atomic::AtomicBool>,
}

impl<D: Decoder> Source<D> {
  fn read_frame<T: Pixel>(
    &mut self, ctx: &mut Context<T>, video_info: VideoDetails,
  ) {
    if self.limit != 0 && self.count == self.limit {
      ctx.flush();
      return;
    }

    #[cfg(all(unix, feature = "signal-hook"))]
    {
      if self.exit_requested.load(std::sync::atomic::Ordering::SeqCst) {
        ctx.flush();
        return;
      }
    }

    match self.input.read_frame(&video_info) {
      Ok(frame) => {
        match video_info.bit_depth {
          8 | 10 | 12 => {}
          _ => panic!("unknown input bit depth!"),
        }
        self.count += 1;
        let _ = ctx.send_frame(Some(Arc::new(frame)));
      }
      _ => {
        ctx.flush();
      }
    };
  }
}

// Encode and write a frame.
// Returns frame information in a `Result`.
fn process_frame<T: Pixel, D: Decoder>(
  ctx: &mut Context<T>, output_file: &mut dyn Muxer, source: &mut Source<D>,
  pass1file: Option<&mut File>, pass2file: Option<&mut File>,
  buffer: &mut [u8], buf_pos: &mut usize,
  mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<Option<Vec<FrameSummary>>, CliError> {
  let y4m_details = source.input.get_video_details();
  let mut frame_summaries = Vec::new();
  let mut pass1file = pass1file;
  let mut pass2file = pass2file;
  // Submit first pass data to pass 2.
  if let Some(passfile) = pass2file.as_mut() {
    loop {
      let mut bytes = ctx.twopass_bytes_needed();
      if bytes == 0 {
        break;
      }
      // Read in some more bytes, if necessary.
      bytes = bytes.min(buffer.len() - *buf_pos);
      if bytes > 0 {
        passfile
          .read_exact(&mut buffer[*buf_pos..*buf_pos + bytes])
          .expect("Could not read frame data from two-pass data file!");
      }
      // And pass them off.
      let consumed = ctx
        .twopass_in(&buffer[*buf_pos..*buf_pos + bytes])
        .expect("Error submitting pass data in second pass.");
      // If the encoder consumed the whole buffer, reset it.
      if consumed >= bytes {
        *buf_pos = 0;
      } else {
        *buf_pos += consumed;
      }
    }
  }
  // Extract first pass data from pass 1.
  // We call this before encoding any frames to ensure we are in 2-pass mode
  //  and to get the placeholder header data.
  if let Some(passfile) = pass1file.as_mut() {
    if let Some(outbuf) = ctx.twopass_out() {
      passfile
        .write_all(outbuf)
        .expect("Unable to write to two-pass data file.");
    }
  }

  let pkt_wrapped = ctx.receive_packet();
  match pkt_wrapped {
    Ok(pkt) => {
      output_file.write_frame(
        pkt.input_frameno as u64,
        pkt.data.as_ref(),
        pkt.frame_type,
      );
      if let (Some(ref mut y4m_enc_uw), Some(ref rec)) =
        (y4m_enc.as_mut(), &pkt.rec)
      {
        write_y4m_frame(y4m_enc_uw, rec, y4m_details);
      }
      frame_summaries.push(pkt.into());
    }
    Err(EncoderStatus::NeedMoreData) => {
      source.read_frame(ctx, y4m_details);
    }
    Err(EncoderStatus::EnoughData) => {
      unreachable!();
    }
    Err(EncoderStatus::LimitReached) => {
      if let Some(passfile) = pass1file.as_mut() {
        if let Some(outbuf) = ctx.twopass_out() {
          // The last block of data we get is the summary data that needs to go
          //  at the start of the pass file.
          // Seek to the start so we can write it there.
          passfile
            .seek(std::io::SeekFrom::Start(0))
            .expect("Unable to seek in two-pass data file.");
          passfile
            .write_all(outbuf)
            .expect("Unable to write to two-pass data file.");
        }
      }
      return Ok(None);
    }
    e @ Err(EncoderStatus::Failure) => {
      let _ = e.map_err(|e| e.context("Failed to encode video"))?;
    }
    e @ Err(EncoderStatus::NotReady) => {
      let _ = e.map_err(|e| {
        e.context("Mismanaged handling of two-pass stats data")
      })?;
    }
    Err(EncoderStatus::Encoded) => {}
  }
  Ok(Some(frame_summaries))
}

fn do_encode<T: Pixel, D: Decoder>(
  cfg: Config, verbose: bool, mut progress: ProgressInfo,
  output: &mut dyn Muxer, source: &mut Source<D>,
  pass1file_name: Option<&String>, pass2file_name: Option<&String>,
  mut y4m_enc: Option<y4m::Encoder<'_, Box<dyn Write>>>,
) -> Result<(), CliError> {
  let mut ctx: Context<T> =
    cfg.new_context().map_err(|e| e.context("Invalid encoder settings"))?;

  let mut pass2file = pass2file_name.map(|f| {
    File::open(f).unwrap_or_else(|_| {
      panic!("Unable to open \"{}\" for reading two-pass data.", f)
    })
  });
  let mut pass1file = pass1file_name.map(|f| {
    File::create(f).unwrap_or_else(|_| {
      panic!("Unable to open \"{}\" for writing two-pass data.", f)
    })
  });

  let mut buffer: [u8; 80] = [0; 80];
  let mut buf_pos = 0;

  while let Some(frame_info) = process_frame(
    &mut ctx,
    &mut *output,
    source,
    pass1file.as_mut(),
    pass2file.as_mut(),
    &mut buffer,
    &mut buf_pos,
    y4m_enc.as_mut(),
  )? {
    for frame in frame_info {
      progress.add_frame(frame.clone());
      if verbose {
        info!("{} - {}", frame, progress);
      } else {
        // Print a one-line progress indicator that overrides itself with every update
        eprint!("\r{}                    ", progress);
      };
    }

    output.flush().unwrap();
  }
  if !verbose {
    // Clear out the temporary progress indicator
    eprint!("\r");
  }
  progress.print_summary(verbose);
  Ok(())
}

fn main() {
  #[cfg(feature = "tracing")]
  use rust_hawktracer::*;
  better_panic::install();
  init_logger();

  #[cfg(feature = "tracing")]
  let instance = HawktracerInstance::new();
  #[cfg(feature = "tracing")]
  let _listener = instance.create_listener(HawktracerListenerType::ToFile {
    file_path: "trace.bin".into(),
    buffer_size: 4096,
  });

  match run() {
    Ok(()) => {}
    Err(e) => error::print_error(&e),
  }
}

fn init_logger() {
  if env::var("RUST_LOG").is_err() {
    env::set_var("RUST_LOG", "info");
  }
  pretty_env_logger::init();
}

fn run() -> Result<(), error::CliError> {
  let mut cli = parse_cli()?;
  // Maximum frame size by specification + maximum y4m header
  let limit = y4m::Limits {
    // Use saturating operations to gracefully handle 32-bit architectures
    bytes: 64usize
      .saturating_mul(64)
      .saturating_mul(4096)
      .saturating_mul(2304)
      .saturating_add(1024),
  };
  let mut y4m_dec = y4m::Decoder::new_with_limits(&mut cli.io.input, limit)
    .expect("cannot decode the input");
  let video_info = y4m_dec.get_video_details();
  let y4m_enc = match cli.io.rec.as_mut() {
    Some(rec) => Some(
      y4m::encode(
        video_info.width,
        video_info.height,
        y4m::Ratio::new(
          video_info.time_base.den as usize,
          video_info.time_base.num as usize,
        ),
      )
      .with_colorspace(y4m_dec.get_colorspace())
      .write_header(rec)
      .unwrap(),
    ),
    None => None,
  };

  cli.enc.width = video_info.width;
  cli.enc.height = video_info.height;
  cli.enc.bit_depth = video_info.bit_depth;
  cli.enc.chroma_sampling = video_info.chroma_sampling;
  cli.enc.chroma_sample_position = video_info.chroma_sample_position;

  // If no pixel range is specified via CLI, assume limited,
  // as it is the default for the Y4M format.
  if !cli.color_range_specified {
    cli.enc.pixel_range = PixelRange::Limited;
  }

  if !cli.override_time_base {
    cli.enc.time_base = video_info.time_base;
  }

  let cfg = Config { enc: cli.enc, threads: cli.threads };

  cli.io.output.write_header(
    video_info.width,
    video_info.height,
    cli.enc.time_base.den as usize,
    cli.enc.time_base.num as usize,
  );

  info!(
    "Using y4m decoder: {}x{}p @ {}/{} fps, {}, {}-bit",
    video_info.width,
    video_info.height,
    video_info.time_base.den,
    video_info.time_base.num,
    video_info.chroma_sampling,
    video_info.bit_depth
  );
  info!("Encoding settings: {}", cfg.enc);

  let progress = ProgressInfo::new(
    Rational { num: video_info.time_base.den, den: video_info.time_base.num },
    if cli.limit == 0 { None } else { Some(cli.limit) },
    cfg.enc.show_psnr,
  );

  for _ in 0..cli.skip {
    y4m_dec.read_frame().expect("Skipped more frames than in the input");
  }

  #[cfg(all(unix, feature = "signal-hook"))]
  let exit_requested = {
    use std::sync::atomic::*;
    let e = Arc::new(AtomicBool::from(false));

    fn setup_signal(sig: i32, e: Arc<AtomicBool>) {
      unsafe {
        signal_hook::register(sig, move || {
          if e.load(Ordering::SeqCst) {
            std::process::exit(128 + sig);
          }
          e.store(true, Ordering::SeqCst);
          info!("Exit requested, flushing.");
        })
        .expect("Cannot register the signal hooks");
      }
    }

    setup_signal(signal_hook::SIGTERM, e.clone());
    setup_signal(signal_hook::SIGQUIT, e.clone());
    setup_signal(signal_hook::SIGINT, e.clone());

    e
  };

  #[cfg(all(unix, feature = "signal-hook"))]
  let mut source =
    Source { limit: cli.limit, input: y4m_dec, count: 0, exit_requested };
  #[cfg(not(all(unix, feature = "signal-hook")))]
  let mut source = Source { limit: cli.limit, input: y4m_dec, count: 0 };

  if video_info.bit_depth == 8 {
    do_encode::<u8, y4m::Decoder<'_, Box<dyn Read>>>(
      cfg,
      cli.verbose,
      progress,
      &mut *cli.io.output,
      &mut source,
      cli.pass1file_name.as_ref(),
      cli.pass2file_name.as_ref(),
      y4m_enc,
    )?
  } else {
    do_encode::<u16, y4m::Decoder<'_, Box<dyn Read>>>(
      cfg,
      cli.verbose,
      progress,
      &mut *cli.io.output,
      &mut source,
      cli.pass1file_name.as_ref(),
      cli.pass2file_name.as_ref(),
      y4m_enc,
    )?
  }

  Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crypto;
use crypto::{CipherText, CipherTextRef, PlainTextRef};
use hash::Hash;
use hash::tree::HashRef;

use std::mem;

use super::BlobError;
use super::ChunkRef;

pub struct Blob {
    master_key: crypto::FixedKey,
    chunks: CipherText,
    footer: Vec<u8>,
    overhead: usize,
    max_len: usize,
}

impl Blob {
    pub fn new(max_len: usize) -> Blob {
        // TODO(jos): Plug an actual crypto key through somehow.
        let pubkey = crypto::sealed::desc::PublicKey::from_slice(&[215, 136, 80, 128, 158, 109,
                                                                   227, 141, 219, 63, 118, 91,
                                                                   123, 97, 1, 97, 65, 237, 62,
                                                                   171, 83, 159, 200, 11, 68,
                                                                   138, 40, 82, 24, 47, 187, 29])
            .unwrap();
        let seckey = crypto::sealed::desc::SecretKey::from_slice(&[94, 13, 181, 81, 97, 87, 76,
                                                                   37, 53, 92, 120, 232, 17, 126,
                                                                   234, 78, 12, 23, 141, 61, 40,
                                                                   10, 136, 127, 103, 192, 255,
                                                                   193, 142, 154, 101, 35])
            .unwrap();

        let master_key = crypto::FixedKey::new(pubkey, Some(seckey));

        Blob {
            master_key: master_key,
            chunks: CipherText::empty(),
            footer: Vec::with_capacity(max_len / 2),
            overhead: crypto::sealed::desc::overhead(),
            max_len: max_len,
        }
    }

    pub fn read_chunk(blob: &[u8], hash: &Hash, cref: &ChunkRef) -> Result<Vec<u8>, BlobError> {
        let ct = crypto::CipherTextRef::new(blob);
        Ok(crypto::RefKey::unseal(hash, cref, ct)?.into_vec())
    }

    pub fn upperbound_len(&self) -> usize {
        if self.chunks.len() == 0 {
            0
        } else {
            self.chunks.len() + self.footer.len() + self.overhead
        }
    }

    pub fn try_append(&mut self, chunk: &[u8], mut href: &mut HashRef) -> Result<(), ()> {
        let ct = crypto::RefKey::seal(&mut href, PlainTextRef::new(chunk));

        href.persistent_ref.offset = self.chunks.len();
        let mut href_bytes = href.as_bytes();
        assert!(href_bytes.len() < 255);

        if self.upperbound_len() + 1 + href_bytes.len() + ct.len() >= self.max_len {
            if self.chunks.len() == 0 {
                panic!("Can never fit chunk of size {} in blob of size {}",
                       chunk.len(),
                       self.max_len);
            }
            return Err(());
        }

        self.chunks.append(ct);

        // Generate footer entry.
        self.footer.push(href_bytes.len() as u8);
        self.footer.append(&mut href_bytes);

        Ok(())
    }

    pub fn to_ciphertext(&mut self) -> Option<CipherText> {
        if self.chunks.len() == 0 {
            return None;
        }

        let footer = self.master_key.seal(PlainTextRef::new(&self.footer[..]));
        self.footer.truncate(0);

        assert!(self.chunks.len() + footer.len() <= self.max_len);
        let mut out = mem::replace(&mut self.chunks, CipherText::empty());
        out.random_pad_upto(self.max_len - footer.len());
        out.append(footer);

        // Everything has been reset. We are ready to go again.
        assert_eq!(0, self.chunks.len());
        assert_eq!(0, self.footer.len());

        Some(out)
    }

    pub fn refs_from_bytes(&self, bytes: &[u8]) -> Result<Vec<HashRef>, BlobError> {
        let (_rest, footer_vec) = self.master_key.unseal(CipherTextRef::new(bytes))?;
        let mut footer_pos = footer_vec.as_bytes();

        let mut hrefs = Vec::new();
        while footer_pos.len() > 0 {
            let len = footer_pos[0] as usize;
            assert!(footer_pos.len() > len);

            hrefs.push(HashRef::from_bytes(&mut &footer_pos[1..1 + len])?);
            footer_pos = &footer_pos[len + 1..];
        }

        Ok(hrefs)
    }
}
<commit_msg>Allow large HashRefs.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crypto;
use crypto::{CipherText, CipherTextRef, PlainTextRef};
use hash::Hash;
use hash::tree::HashRef;

use std::mem;

use super::BlobError;
use super::ChunkRef;

pub struct Blob {
    master_key: crypto::FixedKey,
    chunks: CipherText,
    footer: Vec<u8>,
    overhead: usize,
    max_len: usize,
}

impl Blob {
    pub fn new(max_len: usize) -> Blob {
        // TODO(jos): Plug an actual crypto key through somehow.
        let pubkey = crypto::sealed::desc::PublicKey::from_slice(&[215, 136, 80, 128, 158, 109,
                                                                   227, 141, 219, 63, 118, 91,
                                                                   123, 97, 1, 97, 65, 237, 62,
                                                                   171, 83, 159, 200, 11, 68,
                                                                   138, 40, 82, 24, 47, 187, 29])
            .unwrap();
        let seckey = crypto::sealed::desc::SecretKey::from_slice(&[94, 13, 181, 81, 97, 87, 76,
                                                                   37, 53, 92, 120, 232, 17, 126,
                                                                   234, 78, 12, 23, 141, 61, 40,
                                                                   10, 136, 127, 103, 192, 255,
                                                                   193, 142, 154, 101, 35])
            .unwrap();

        let master_key = crypto::FixedKey::new(pubkey, Some(seckey));

        Blob {
            master_key: master_key,
            chunks: CipherText::empty(),
            footer: Vec::with_capacity(max_len / 2),
            overhead: crypto::sealed::desc::overhead(),
            max_len: max_len,
        }
    }

    pub fn read_chunk(blob: &[u8], hash: &Hash, cref: &ChunkRef) -> Result<Vec<u8>, BlobError> {
        let ct = crypto::CipherTextRef::new(blob);
        Ok(crypto::RefKey::unseal(hash, cref, ct)?.into_vec())
    }

    pub fn upperbound_len(&self) -> usize {
        if self.chunks.len() == 0 {
            0
        } else {
            self.chunks.len() + self.footer.len() + self.overhead
        }
    }

    pub fn try_append(&mut self, chunk: &[u8], mut href: &mut HashRef) -> Result<(), ()> {
        let ct = crypto::RefKey::seal(&mut href, PlainTextRef::new(chunk));

        href.persistent_ref.offset = self.chunks.len();
        let mut href_bytes = href.as_bytes();
        assert!(href_bytes.len() < 65535);

        if self.upperbound_len() + 1 + href_bytes.len() + ct.len() >= self.max_len {
            if self.chunks.len() == 0 {
                panic!("Can never fit chunk of size {} in blob of size {}",
                       chunk.len(),
                       self.max_len);
            }
            return Err(());
        }

        self.chunks.append(ct);

        // Generate footer entry.
        self.footer.push((href_bytes.len() % 256) as u8);
        self.footer.push((href_bytes.len() / 256) as u8);
        self.footer.append(&mut href_bytes);

        Ok(())
    }

    pub fn to_ciphertext(&mut self) -> Option<CipherText> {
        if self.chunks.len() == 0 {
            return None;
        }

        let footer = self.master_key.seal(PlainTextRef::new(&self.footer[..]));
        self.footer.truncate(0);

        assert!(self.chunks.len() + footer.len() <= self.max_len);
        let mut out = mem::replace(&mut self.chunks, CipherText::empty());
        out.random_pad_upto(self.max_len - footer.len());
        out.append(footer);

        // Everything has been reset. We are ready to go again.
        assert_eq!(0, self.chunks.len());
        assert_eq!(0, self.footer.len());

        Some(out)
    }

    pub fn refs_from_bytes(&self, bytes: &[u8]) -> Result<Vec<HashRef>, BlobError> {
        let (_rest, footer_vec) = self.master_key.unseal(CipherTextRef::new(bytes))?;
        let mut footer_pos = footer_vec.as_bytes();

        let mut hrefs = Vec::new();
        while footer_pos.len() > 0 {
            let len = footer_pos[0] as usize + 256 * (footer_pos[1] as usize);
            assert!(footer_pos.len() > len);

            hrefs.push(HashRef::from_bytes(&mut &footer_pos[2..2 + len])?);
            footer_pos = &footer_pos[len + 2..];
        }

        Ok(hrefs)
    }
}
<|endoftext|>


<commit_before>#![feature(core)]

extern crate cairo;
extern crate gtk;

use std::f64::consts::PI_2;

use gtk::Widget;
use gtk::traits::*;
use gtk::signal::Inhibit;
use gtk::DrawingArea;

use cairo::enums::FontSlant::FontSlantNormal;
use cairo::enums::FontWeight::FontWeightNormal;
use cairo::Context;

fn main() {
    gtk::init();

    drawable(500, 500, |_, cr: Context| {
        cr.set_dash(&[3., 2., 1.], 1.);
        assert_eq!(cr.get_dash(), (vec![3., 2., 1.], 1.));

        cr.scale(500f64, 500f64);

        cr.set_source_rgb(250.0/255.0, 224.0/255.0, 55.0/255.0);
        cr.paint();

        cr.set_line_width(0.05);

        // border
        cr.set_source_rgb(0.3, 0.3, 0.3);
        cr.rectangle(0.0, 0.0, 1.0, 1.0);
        cr.stroke();

        cr.set_line_width(0.03);

        // draw circle
        cr.arc(0.5, 0.5, 0.4, 0.0, PI_2);
        cr.stroke();

        // mouth
        let mouth_top = 0.68;
        let mouth_width = 0.38;

        let mouth_dx = 0.10;
        let mouth_dy = 0.10;

        cr.move_to( 0.50 - mouth_width/2.0, mouth_top);
        cr.curve_to(0.50 - mouth_dx,        mouth_top + mouth_dy,
                     0.50 + mouth_dx,        mouth_top + mouth_dy,
                     0.50 + mouth_width/2.0, mouth_top);

        println!("Extents: {:?}", cr.fill_extents());

        cr.stroke();

        let eye_y = 0.38;
        let eye_dx = 0.15;
        cr.arc(0.5 - eye_dx, eye_y, 0.05, 0.0, PI_2);
        cr.fill();

        cr.arc(0.5 + eye_dx, eye_y, 0.05, 0.0, PI_2);
        cr.fill();

        Inhibit(false)
    });

    drawable(500, 500, |_, cr: Context| {
        cr.scale(500f64, 500f64);

        cr.select_font_face("Sans", FontSlantNormal, FontWeightNormal);
        cr.set_font_size(0.35);

        cr.move_to(0.04, 0.53);
        cr.show_text("Hello");

        cr.move_to(0.27, 0.65);
        cr.text_path("void");
        cr.set_source_rgb(0.5, 0.5, 1.0);
        cr.fill_preserve();
        cr.set_source_rgb(0.0, 0.0, 0.0);
        cr.set_line_width(0.01);
        cr.stroke();

        cr.set_source_rgba(1.0, 0.2, 0.2, 0.6);
        cr.arc(0.04, 0.53, 0.02, 0.0, PI_2);
        cr.arc(0.27, 0.65, 0.02, 0.0, PI_2);
        cr.fill();

        Inhibit(false)
    });

    gtk::main();
}

pub fn drawable<F>(width: i32, height: i32, draw_fn: F)
where F: Fn(Widget, Context) -> Inhibit + 'static {
    let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let drawing_area = Box::new(DrawingArea::new)().unwrap();

    drawing_area.connect_draw(draw_fn);

    window.set_default_size(width, height);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });
    window.add(&drawing_area);
    window.show_all();
}
<commit_msg>s/PI_2/PI * 2./<commit_after>extern crate cairo;
extern crate gtk;

use std::f64::consts::PI;

use gtk::Widget;
use gtk::traits::*;
use gtk::signal::Inhibit;
use gtk::DrawingArea;

use cairo::enums::FontSlant::FontSlantNormal;
use cairo::enums::FontWeight::FontWeightNormal;
use cairo::Context;

fn main() {
    gtk::init();

    drawable(500, 500, |_, cr: Context| {
        cr.set_dash(&[3., 2., 1.], 1.);
        assert_eq!(cr.get_dash(), (vec![3., 2., 1.], 1.));

        cr.scale(500f64, 500f64);

        cr.set_source_rgb(250.0/255.0, 224.0/255.0, 55.0/255.0);
        cr.paint();

        cr.set_line_width(0.05);

        // border
        cr.set_source_rgb(0.3, 0.3, 0.3);
        cr.rectangle(0.0, 0.0, 1.0, 1.0);
        cr.stroke();

        cr.set_line_width(0.03);

        // draw circle
        cr.arc(0.5, 0.5, 0.4, 0.0, PI * 2.);
        cr.stroke();

        // mouth
        let mouth_top = 0.68;
        let mouth_width = 0.38;

        let mouth_dx = 0.10;
        let mouth_dy = 0.10;

        cr.move_to( 0.50 - mouth_width/2.0, mouth_top);
        cr.curve_to(0.50 - mouth_dx,        mouth_top + mouth_dy,
                     0.50 + mouth_dx,        mouth_top + mouth_dy,
                     0.50 + mouth_width/2.0, mouth_top);

        println!("Extents: {:?}", cr.fill_extents());

        cr.stroke();

        let eye_y = 0.38;
        let eye_dx = 0.15;
        cr.arc(0.5 - eye_dx, eye_y, 0.05, 0.0, PI * 2.);
        cr.fill();

        cr.arc(0.5 + eye_dx, eye_y, 0.05, 0.0, PI * 2.);
        cr.fill();

        Inhibit(false)
    });

    drawable(500, 500, |_, cr: Context| {
        cr.scale(500f64, 500f64);

        cr.select_font_face("Sans", FontSlantNormal, FontWeightNormal);
        cr.set_font_size(0.35);

        cr.move_to(0.04, 0.53);
        cr.show_text("Hello");

        cr.move_to(0.27, 0.65);
        cr.text_path("void");
        cr.set_source_rgb(0.5, 0.5, 1.0);
        cr.fill_preserve();
        cr.set_source_rgb(0.0, 0.0, 0.0);
        cr.set_line_width(0.01);
        cr.stroke();

        cr.set_source_rgba(1.0, 0.2, 0.2, 0.6);
        cr.arc(0.04, 0.53, 0.02, 0.0, PI * 2.);
        cr.arc(0.27, 0.65, 0.02, 0.0, PI * 2.);
        cr.fill();

        Inhibit(false)
    });

    gtk::main();
}

pub fn drawable<F>(width: i32, height: i32, draw_fn: F)
where F: Fn(Widget, Context) -> Inhibit + 'static {
    let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let drawing_area = Box::new(DrawingArea::new)().unwrap();

    drawing_area.connect_draw(draw_fn);

    window.set_default_size(width, height);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });
    window.add(&drawing_area);
    window.show_all();
}
<|endoftext|>


<commit_before>extern crate gb;

#[macro_use]
extern crate libretro_backend;

use std::ops::{Deref, DerefMut};

use gb::{
    Emulator,
    Interrupt,
    GrayShade,
    Hardware,
    Key,
};

use libretro_backend::{
    GameData,
    LoadGameResult,
    RuntimeHandle,
    AudioVideoInfo,
    PixelFormat,
    Region,
    JoypadButton,
    CoreInfo,
    MemoryType
};

const FREQUENCY: f64 = 44100.0; // Hz

struct EmulatorWrapper {
    emulator: Option<Emulator>,
    game_data: Option<GameData>,
    frame: [u8; gb::SCREEN_X * gb::SCREEN_Y * 4],
}

impl Default for EmulatorWrapper {
    fn default() -> Self {
        EmulatorWrapper::new()
    }
}

impl EmulatorWrapper {
    pub fn new() -> EmulatorWrapper {
        EmulatorWrapper {
            emulator: None,
            game_data: None,
            frame: [0xFF; gb::SCREEN_X * gb::SCREEN_Y * 4],
        }
    }

    pub fn load_rom(&mut self, data: &[u8]) -> Result<(), String> {
        self.emulator = Some(Emulator::from_data(data, FREQUENCY)?);
        Ok(())
    }

    pub fn update_button(&mut self, handle: &mut RuntimeHandle,
                         button: JoypadButton, gb_button: Key) {
        if handle.is_joypad_button_pressed(0, button) {
            self.cpu.key_down(gb_button);
        } else {
            self.cpu.key_up(gb_button);
        }
    }
}

impl Deref for EmulatorWrapper {
    type Target = Emulator;

    fn deref(&self) -> &Emulator {
        self.emulator.as_ref().unwrap()
    }
}

impl DerefMut for EmulatorWrapper {
    fn deref_mut(&mut self) -> &mut Emulator {
        self.emulator.as_mut().unwrap()
    }
}

impl libretro_backend::Core for EmulatorWrapper {
    fn info() -> CoreInfo {
        CoreInfo::new("gb-rust", env!("CARGO_PKG_VERSION"))
            .supports_roms_with_extension("gb")
            .supports_roms_with_extension("gbc")
    }

    fn memory_data(&mut self, memory_type: MemoryType) -> Option<&mut [u8]> {
        match memory_type {
            MemoryType::SaveRam => Some(self.cpu.handler_holder.ram()),
            MemoryType::Rtc => self.cpu.handler_holder.rtc().map(
                |v| unsafe {
                    std::slice::from_raw_parts_mut(
                        v as *mut _ as *mut u8,
                        std::mem::size_of::<i64>())
                }),
            _ => None,
        }
    }

    fn on_reset(&mut self) {
        self.reset();
    }

    fn on_load_game(&mut self, game_data: GameData) -> LoadGameResult {
        if game_data.data().is_none() {
            return LoadGameResult::Failed(game_data);
        }

        if self.load_rom(game_data.data().unwrap()).is_err() {
            return LoadGameResult::Failed(game_data);
        }

        let av = AudioVideoInfo::new()
            .video(gb::SCREEN_X as u32, gb::SCREEN_Y as u32, 60.0, PixelFormat::ARGB8888)
            .audio(FREQUENCY)
            .region(Region::NTSC);

        self.game_data = Some(game_data);
        return LoadGameResult::Success(av);
    }

    fn on_unload_game(&mut self) -> GameData {
        self.emulator = None;
        self.game_data.take().unwrap()
    }

    fn on_run(&mut self, handle: &mut RuntimeHandle) {
        self.update_button(handle, JoypadButton::A, Key::A);
        self.update_button(handle, JoypadButton::B, Key::B);
        self.update_button(handle, JoypadButton::Select, Key::Select);
        self.update_button(handle, JoypadButton::Start, Key::Start);
        self.update_button(handle, JoypadButton::Up, Key::Up);
        self.update_button(handle, JoypadButton::Down, Key::Down);
        self.update_button(handle, JoypadButton::Left, Key::Left);
        self.update_button(handle, JoypadButton::Right, Key::Right);
        self.cpu.interrupt(Interrupt::Joypad);

        loop {
            self.cpu.next_instruction();

            if self.cpu.handler_holder.should_refresh() {
                break;
            }
        }

        let mut screen = [[GrayShade::C00; gb::SCREEN_X]; gb::SCREEN_Y];
        {
            let buffer = self.cpu.handler_holder.get_screen_buffer();
            screen.copy_from_slice(&buffer[..]);
        }

        for i in 0 .. gb::SCREEN_Y {
            for j in 0 .. gb::SCREEN_X {
                self.frame[i * gb::SCREEN_X * 4 + j * 4 + 0] = (3 - screen[i][j] as u8) * 85;
                self.frame[i * gb::SCREEN_X * 4 + j * 4 + 1] = (3 - screen[i][j] as u8) * 85;
                self.frame[i * gb::SCREEN_X * 4 + j * 4 + 2] = (3 - screen[i][j] as u8) * 85;
                self.frame[i * gb::SCREEN_X * 4 + j * 4 + 3] = (3 - screen[i][j] as u8) * 85;
            }
        }

        handle.upload_video_frame(&self.frame);
        handle.upload_audio_frame(&self.generate_sound()[..]);
    }
}

libretro_core!(EmulatorWrapper);
<commit_msg>Use the palette from the DMG.<commit_after>extern crate gb;

#[macro_use]
extern crate libretro_backend;

use std::ops::{Deref, DerefMut};

use gb::{
    Emulator,
    Interrupt,
    GrayShade,
    Hardware,
    Key,
};

use libretro_backend::{
    GameData,
    LoadGameResult,
    RuntimeHandle,
    AudioVideoInfo,
    PixelFormat,
    Region,
    JoypadButton,
    CoreInfo,
    MemoryType
};

const FREQUENCY: f64 = 44100.0; // Hz

struct EmulatorWrapper {
    emulator: Option<Emulator>,
    game_data: Option<GameData>,
    frame: [u8; gb::SCREEN_X * gb::SCREEN_Y * 4],
}

impl Default for EmulatorWrapper {
    fn default() -> Self {
        EmulatorWrapper::new()
    }
}

impl EmulatorWrapper {
    pub fn new() -> EmulatorWrapper {
        EmulatorWrapper {
            emulator: None,
            game_data: None,
            frame: [0xFF; gb::SCREEN_X * gb::SCREEN_Y * 4],
        }
    }

    pub fn load_rom(&mut self, data: &[u8]) -> Result<(), String> {
        self.emulator = Some(Emulator::from_data(data, FREQUENCY)?);
        Ok(())
    }

    pub fn update_button(&mut self, handle: &mut RuntimeHandle,
                         button: JoypadButton, gb_button: Key) {
        if handle.is_joypad_button_pressed(0, button) {
            self.cpu.key_down(gb_button);
        } else {
            self.cpu.key_up(gb_button);
        }
    }
}

impl Deref for EmulatorWrapper {
    type Target = Emulator;

    fn deref(&self) -> &Emulator {
        self.emulator.as_ref().unwrap()
    }
}

impl DerefMut for EmulatorWrapper {
    fn deref_mut(&mut self) -> &mut Emulator {
        self.emulator.as_mut().unwrap()
    }
}

struct Color {
    a: u8,
    r: u8,
    g: u8,
    b: u8,
}

impl Color {
    #[inline]
    pub fn write(&self, out: &mut [u8]) {
        out[0] = self.b;
        out[1] = self.g;
        out[2] = self.r;
        out[3] = self.a;
    }
}

impl From<GrayShade> for Color {
    fn from(c: GrayShade) -> Self {
        match c {
            GrayShade::C00 => PALETTE_00,
            GrayShade::C01 => PALETTE_01,
            GrayShade::C10 => PALETTE_10,
            GrayShade::C11 => PALETTE_11,
            GrayShade::Transparent => unreachable!(),
        }
    }
}

const PALETTE_11: Color = Color { a: 0xFF, r: 0x0F, g: 0x38, b: 0x0F };
const PALETTE_10: Color = Color { a: 0xFF, r: 0x30, g: 0x62, b: 0x30 };
const PALETTE_01: Color = Color { a: 0xFF, r: 0x8C, g: 0xAD, b: 0x0F };
const PALETTE_00: Color = Color { a: 0xFF, r: 0x9C, g: 0xBD, b: 0x0F };

impl libretro_backend::Core for EmulatorWrapper {
    fn info() -> CoreInfo {
        CoreInfo::new("gb-rust", env!("CARGO_PKG_VERSION"))
            .supports_roms_with_extension("gb")
            .supports_roms_with_extension("gbc")
    }

    fn memory_data(&mut self, memory_type: MemoryType) -> Option<&mut [u8]> {
        match memory_type {
            MemoryType::SaveRam => Some(self.cpu.handler_holder.ram()),
            MemoryType::Rtc => self.cpu.handler_holder.rtc().map(
                |v| unsafe {
                    std::slice::from_raw_parts_mut(
                        v as *mut _ as *mut u8,
                        std::mem::size_of::<i64>())
                }),
            _ => None,
        }
    }

    fn on_reset(&mut self) {
        self.reset();
    }

    fn on_load_game(&mut self, game_data: GameData) -> LoadGameResult {
        if game_data.data().is_none() {
            return LoadGameResult::Failed(game_data);
        }

        if self.load_rom(game_data.data().unwrap()).is_err() {
            return LoadGameResult::Failed(game_data);
        }

        let av = AudioVideoInfo::new()
            .video(gb::SCREEN_X as u32, gb::SCREEN_Y as u32, 60.0, PixelFormat::ARGB8888)
            .audio(FREQUENCY)
            .region(Region::NTSC);

        self.game_data = Some(game_data);
        return LoadGameResult::Success(av);
    }

    fn on_unload_game(&mut self) -> GameData {
        self.emulator = None;
        self.game_data.take().unwrap()
    }

    fn on_run(&mut self, handle: &mut RuntimeHandle) {
        self.update_button(handle, JoypadButton::A, Key::A);
        self.update_button(handle, JoypadButton::B, Key::B);
        self.update_button(handle, JoypadButton::Select, Key::Select);
        self.update_button(handle, JoypadButton::Start, Key::Start);
        self.update_button(handle, JoypadButton::Up, Key::Up);
        self.update_button(handle, JoypadButton::Down, Key::Down);
        self.update_button(handle, JoypadButton::Left, Key::Left);
        self.update_button(handle, JoypadButton::Right, Key::Right);
        self.cpu.interrupt(Interrupt::Joypad);

        loop {
            self.cpu.next_instruction();

            if self.cpu.handler_holder.should_refresh() {
                break;
            }
        }

        let mut screen = [[GrayShade::C00; gb::SCREEN_X]; gb::SCREEN_Y];
        {
            let buffer = self.cpu.handler_holder.get_screen_buffer();
            screen.copy_from_slice(&buffer[..]);
        }

        for i in 0 .. gb::SCREEN_Y {
            for j in 0 .. gb::SCREEN_X {
                let color = Color::from(screen[i][j]);

                let index = i * gb::SCREEN_X * 4 + j * 4;
                color.write(&mut self.frame[index .. index + 4]);
            }
        }

        handle.upload_video_frame(&self.frame);
        handle.upload_audio_frame(&self.generate_sound()[..]);
    }
}

libretro_core!(EmulatorWrapper);
<|endoftext|>


<commit_before>use getopts::{Options,Matches};
use std::net::{TcpStream,SocketAddr};
use std::io::{BufStream,Write};
use std::old_io;

pub fn connect(matches: &Matches) {
    let host = matches.free.get(0).unwrap();
    let port = match matches.free.get(1).unwrap().parse::<u16>() {
        Ok(n) => { n }
        Err(_) => {
            panic!("not in possible range".to_string());
        }
    };
    match TcpStream::connect(&(host.as_slice(), port)) {
        Ok(stream) => {
            readwrite(&stream);
        },
        Err(f) => {
            // just exit
        }
    }
}

fn readwrite(stream: &TcpStream) {
    let mut buf_stream = BufStream::new(stream);
    let mut stdin_reader = old_io::stdin();
    let mut read_buf = [0, 4096];
    loop {
        match stream.peer_addr() {
            Ok(addr) => { /* */ },
            Err(f) => {
                // Hack for when other end closes socket, will exit.
                return;
            }
        }
        // Have to block here, so we can't responsively terminate if server closes socket.
        match stdin_reader.read(&mut read_buf) {
            Ok(n) => {
                buf_stream.write_all(&read_buf.slice_to(n)).unwrap();
                buf_stream.flush(); // overkill, probably
            },
            Err(f) => {
                panic!(f.to_string());
            }
        }
    }
}
<commit_msg>Replace peer_addr hack with flush hack. To determine whether other end has closed socket -- still can't figure out a better way to do this, and terminate immediately as `nc(1)` does.<commit_after>use getopts::{Options,Matches};
use std::net::{TcpStream,SocketAddr};
use std::io::{BufStream,Write};
use std::old_io;

pub fn connect(matches: &Matches) {
    let host = matches.free.get(0).unwrap();
    let port = match matches.free.get(1).unwrap().parse::<u16>() {
        Ok(n) => { n }
        Err(_) => {
            panic!("not in possible range".to_string());
        }
    };
    match TcpStream::connect(&(host.as_slice(), port)) {
        Ok(stream) => {
            readwrite(&stream);
        },
        Err(f) => {
            // just exit
        }
    }
}

fn readwrite(stream: &TcpStream) {
    let mut buf_stream = BufStream::new(stream);
    let mut stdin_reader = old_io::stdin();
    let mut read_buf = [0, 4096];
    loop {
        // Have to block here, so we can't immediately terminate if server closes socket.
        match stdin_reader.read(&mut read_buf) {
            Ok(n) => {
                buf_stream.write_all(&read_buf.slice_to(n)).unwrap();
                match buf_stream.flush() {
                    Ok(_) => { /* */ },
                    Err(f) => {
                        // other end closed socket
                        return;
                    }
                }
            },
            Err(f) => {
                panic!(f.to_string());
            }
        }
    }
}
<|endoftext|>


<commit_before>// This file originally from https://github.com/philipc/rust-dwarf/ and
// distributed under either MIT or Apache 2.0 licenses.
//
// Copyright 2016 The rust-dwarf Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Constant definitions.
//!
//! The DWARF spec's `DW_AT_*` type is represented as `struct DwAt(u16)`,
//! `DW_FORM_*` as `DwForm(u16)`, etc.
//!
//! There are also exported const definitions for each constant.

#![allow(non_upper_case_globals)]
#![allow(missing_docs)]

use std::fmt;

// The `dw!` macro turns this:
//
//     dw!(DwFoo(u32) {
//         DW_FOO_bar = 0,
//         DW_FOO_baz = 1,
//         DW_FOO_bang = 2,
//     });
//
// into this:
//
//     #[derive(Debug, Clone, Copy, PartialEq, Eq)]
//     struct DwFoo(pub u32);
//
//     pub const DW_FOO_bar: DwFoo = DwFoo(0);
//     pub const DW_FOO_baz: DwFoo = DwFoo(1);
//     pub const DW_FOO_bang: DwFoo = DwFoo(2);
//
//     impl fmt::Display for DwFoo {
//         fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
//             match *self {
//                 DW_FOO_bar => write!(f, "DW_FOO_bar"),
//                 DW_FOO_baz => write!(f, "DW_FOO_baz"),
//                 DW_FOO_bang => write!(f, "DW_FOO_bang"),
//                 otherwise => write!(f, "Unknown DwFoo: {}", otherwise.0),
//             }
//         }
//     }
macro_rules! dw {
    ($struct_name:ident($struct_type:ty) { $($name:ident = $val:expr),+ }) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub struct $struct_name(pub $struct_type);

        $(
            pub const $name: $struct_name = $struct_name($val);
        )+

        impl fmt::Display for $struct_name {
            fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
                match *self {
                    $(
                        $name => write!(f, stringify!($name)),
                    )+
                    otherwise => write!(f, "Unknown {}: {}",
                                        stringify!($struct_name),
                                        otherwise.0),
                }
            }
        }
    };
    // Handle trailing comma
    ($struct_name:ident($struct_type:ty) { $($name:ident = $val:expr),+, }) => {
        dw!($struct_name($struct_type) { $($name = $val),+ });
    };
}

dw!(DwChildren(u8) {
    DW_CHILDREN_no = 0,
    DW_CHILDREN_yes = 1,
});

dw!(DwTag(u64) {
    DW_TAG_null = 0x00,

    DW_TAG_array_type = 0x01,
    DW_TAG_class_type = 0x02,
    DW_TAG_entry_point = 0x03,
    DW_TAG_enumeration_type = 0x04,
    DW_TAG_formal_parameter = 0x05,
    DW_TAG_imported_declaration = 0x08,
    DW_TAG_label = 0x0a,
    DW_TAG_lexical_block = 0x0b,
    DW_TAG_member = 0x0d,
    DW_TAG_pointer_type = 0x0f,
    DW_TAG_reference_type = 0x10,
    DW_TAG_compile_unit = 0x11,
    DW_TAG_string_type = 0x12,
    DW_TAG_structure_type = 0x13,
    DW_TAG_subroutine_type = 0x15,
    DW_TAG_typedef = 0x16,
    DW_TAG_union_type = 0x17,
    DW_TAG_unspecified_parameters = 0x18,
    DW_TAG_variant = 0x19,
    DW_TAG_common_block = 0x1a,
    DW_TAG_common_inclusion = 0x1b,
    DW_TAG_inheritance = 0x1c,
    DW_TAG_inlined_subroutine = 0x1d,
    DW_TAG_module = 0x1e,
    DW_TAG_ptr_to_member_type = 0x1f,
    DW_TAG_set_type = 0x20,
    DW_TAG_subrange_type = 0x21,
    DW_TAG_with_stmt = 0x22,
    DW_TAG_access_declaration = 0x23,
    DW_TAG_base_type = 0x24,
    DW_TAG_catch_block = 0x25,
    DW_TAG_const_type = 0x26,
    DW_TAG_constant = 0x27,
    DW_TAG_enumerator = 0x28,
    DW_TAG_file_type = 0x29,
    DW_TAG_friend = 0x2a,
    DW_TAG_namelist = 0x2b,
    DW_TAG_namelist_item = 0x2c,
    DW_TAG_packed_type = 0x2d,
    DW_TAG_subprogram = 0x2e,
    DW_TAG_template_type_parameter = 0x2f,
    DW_TAG_template_value_parameter = 0x30,
    DW_TAG_thrown_type = 0x31,
    DW_TAG_try_block = 0x32,
    DW_TAG_variant_part = 0x33,
    DW_TAG_variable = 0x34,
    DW_TAG_volatile_type = 0x35,
    DW_TAG_dwarf_procedure = 0x36,
    DW_TAG_restrict_type = 0x37,
    DW_TAG_interface_type = 0x38,
    DW_TAG_namespace = 0x39,
    DW_TAG_imported_module = 0x3a,
    DW_TAG_unspecified_type = 0x3b,
    DW_TAG_partial_unit = 0x3c,
    DW_TAG_imported_unit = 0x3d,
    DW_TAG_condition = 0x3f,
    DW_TAG_shared_type = 0x40,
    DW_TAG_type_unit = 0x41,
    DW_TAG_rvalue_reference_type = 0x42,
    DW_TAG_template_alias = 0x43,

    DW_TAG_lo_user = 0x4080,
    DW_TAG_hi_user = 0xffff,
});

dw!(DwAt(u64) {
    DW_AT_null = 0x00,

    DW_AT_sibling = 0x01,
    DW_AT_location = 0x02,
    DW_AT_name = 0x03,
    DW_AT_ordering = 0x09,
    DW_AT_byte_size = 0x0b,
    DW_AT_bit_offset = 0x0c,
    DW_AT_bit_size = 0x0d,
    DW_AT_stmt_list = 0x10,
    DW_AT_low_pc = 0x11,
    DW_AT_high_pc = 0x12,
    DW_AT_language = 0x13,
    DW_AT_discr = 0x15,
    DW_AT_discr_value = 0x16,
    DW_AT_visibility = 0x17,
    DW_AT_import = 0x18,
    DW_AT_string_length = 0x19,
    DW_AT_common_reference = 0x1a,
    DW_AT_comp_dir = 0x1b,
    DW_AT_const_value = 0x1c,
    DW_AT_containing_type = 0x1d,
    DW_AT_default_value = 0x1e,
    DW_AT_inline = 0x20,
    DW_AT_is_optional = 0x21,
    DW_AT_lower_bound = 0x22,
    DW_AT_producer = 0x25,
    DW_AT_prototyped = 0x27,
    DW_AT_return_addr = 0x2a,
    DW_AT_start_scope = 0x2c,
    DW_AT_bit_stride = 0x2e,
    DW_AT_upper_bound = 0x2f,
    DW_AT_abstract_origin = 0x31,
    DW_AT_accessibility = 0x32,
    DW_AT_address_class = 0x33,
    DW_AT_artificial = 0x34,
    DW_AT_base_types = 0x35,
    DW_AT_calling_convention = 0x36,
    DW_AT_count = 0x37,
    DW_AT_data_member_location = 0x38,
    DW_AT_decl_column = 0x39,
    DW_AT_decl_file = 0x3a,
    DW_AT_decl_line = 0x3b,
    DW_AT_declaration = 0x3c,
    DW_AT_discr_list = 0x3d,
    DW_AT_encoding = 0x3e,
    DW_AT_external = 0x3f,
    DW_AT_frame_base = 0x40,
    DW_AT_friend = 0x41,
    DW_AT_identifier_case = 0x42,
    DW_AT_macro_info = 0x43,
    DW_AT_namelist_item = 0x44,
    DW_AT_priority = 0x45,
    DW_AT_segment = 0x46,
    DW_AT_specification = 0x47,
    DW_AT_static_link = 0x48,
    DW_AT_type = 0x49,
    DW_AT_use_location = 0x4a,
    DW_AT_variable_parameter = 0x4b,
    DW_AT_virtuality = 0x4c,
    DW_AT_vtable_elem_location = 0x4d,
    DW_AT_allocated = 0x4e,
    DW_AT_associated = 0x4f,
    DW_AT_data_location = 0x50,
    DW_AT_byte_stride = 0x51,
    DW_AT_entry_pc = 0x52,
    DW_AT_use_UTF8 = 0x53,
    DW_AT_extension = 0x54,
    DW_AT_ranges = 0x55,
    DW_AT_trampoline = 0x56,
    DW_AT_call_column = 0x57,
    DW_AT_call_file = 0x58,
    DW_AT_call_line = 0x59,
    DW_AT_description = 0x5a,
    DW_AT_binary_scale = 0x5b,
    DW_AT_decimal_scale = 0x5c,
    DW_AT_small = 0x5d,
    DW_AT_decimal_sign = 0x5e,
    DW_AT_digit_count = 0x5f,
    DW_AT_picture_string = 0x60,
    DW_AT_mutable = 0x61,
    DW_AT_threads_scaled = 0x62,
    DW_AT_explicit = 0x63,
    DW_AT_object_pointer = 0x64,
    DW_AT_endianity = 0x65,
    DW_AT_elemental = 0x66,
    DW_AT_pure = 0x67,
    DW_AT_recursive = 0x68,
    DW_AT_signature = 0x69,
    DW_AT_main_subprogram = 0x6a,
    DW_AT_data_bit_offset = 0x6b,
    DW_AT_const_expr = 0x6c,
    DW_AT_enum_class = 0x6d,
    DW_AT_linkage_name = 0x6e,

    DW_AT_lo_user = 0x2000,
    DW_AT_hi_user = 0x3fff,
});

dw!(DwForm(u64) {
    DW_FORM_null = 0x00,

    DW_FORM_addr = 0x01,
    DW_FORM_block2 = 0x03,
    DW_FORM_block4 = 0x04,
    DW_FORM_data2 = 0x05,
    DW_FORM_data4 = 0x06,
    DW_FORM_data8 = 0x07,
    DW_FORM_string = 0x08,
    DW_FORM_block = 0x09,
    DW_FORM_block1 = 0x0a,
    DW_FORM_data1 = 0x0b,
    DW_FORM_flag = 0x0c,
    DW_FORM_sdata = 0x0d,
    DW_FORM_strp = 0x0e,
    DW_FORM_udata = 0x0f,
    DW_FORM_ref_addr = 0x10,
    DW_FORM_ref1 = 0x11,
    DW_FORM_ref2 = 0x12,
    DW_FORM_ref4 = 0x13,
    DW_FORM_ref8 = 0x14,
    DW_FORM_ref_udata = 0x15,
    DW_FORM_indirect = 0x16,
    DW_FORM_sec_offset = 0x17,
    DW_FORM_exprloc = 0x18,
    DW_FORM_flag_present = 0x19,
    DW_FORM_ref_sig8 = 0x20,
});
<commit_msg>Add the DW_LNS_* and DW_LNE_* constants<commit_after>// This file originally from https://github.com/philipc/rust-dwarf/ and
// distributed under either MIT or Apache 2.0 licenses.
//
// Copyright 2016 The rust-dwarf Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Constant definitions.
//!
//! The DWARF spec's `DW_AT_*` type is represented as `struct DwAt(u16)`,
//! `DW_FORM_*` as `DwForm(u16)`, etc.
//!
//! There are also exported const definitions for each constant.

#![allow(non_upper_case_globals)]
#![allow(missing_docs)]

use std::fmt;

// The `dw!` macro turns this:
//
//     dw!(DwFoo(u32) {
//         DW_FOO_bar = 0,
//         DW_FOO_baz = 1,
//         DW_FOO_bang = 2,
//     });
//
// into this:
//
//     #[derive(Debug, Clone, Copy, PartialEq, Eq)]
//     struct DwFoo(pub u32);
//
//     pub const DW_FOO_bar: DwFoo = DwFoo(0);
//     pub const DW_FOO_baz: DwFoo = DwFoo(1);
//     pub const DW_FOO_bang: DwFoo = DwFoo(2);
//
//     impl fmt::Display for DwFoo {
//         fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
//             match *self {
//                 DW_FOO_bar => write!(f, "DW_FOO_bar"),
//                 DW_FOO_baz => write!(f, "DW_FOO_baz"),
//                 DW_FOO_bang => write!(f, "DW_FOO_bang"),
//                 otherwise => write!(f, "Unknown DwFoo: {}", otherwise.0),
//             }
//         }
//     }
macro_rules! dw {
    ($struct_name:ident($struct_type:ty) { $($name:ident = $val:expr),+ }) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        pub struct $struct_name(pub $struct_type);

        $(
            pub const $name: $struct_name = $struct_name($val);
        )+

        impl fmt::Display for $struct_name {
            fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
                match *self {
                    $(
                        $name => write!(f, stringify!($name)),
                    )+
                    otherwise => write!(f, "Unknown {}: {}",
                                        stringify!($struct_name),
                                        otherwise.0),
                }
            }
        }
    };
    // Handle trailing comma
    ($struct_name:ident($struct_type:ty) { $($name:ident = $val:expr),+, }) => {
        dw!($struct_name($struct_type) { $($name = $val),+ });
    };
}

dw!(DwChildren(u8) {
    DW_CHILDREN_no = 0,
    DW_CHILDREN_yes = 1,
});

dw!(DwTag(u64) {
    DW_TAG_null = 0x00,

    DW_TAG_array_type = 0x01,
    DW_TAG_class_type = 0x02,
    DW_TAG_entry_point = 0x03,
    DW_TAG_enumeration_type = 0x04,
    DW_TAG_formal_parameter = 0x05,
    DW_TAG_imported_declaration = 0x08,
    DW_TAG_label = 0x0a,
    DW_TAG_lexical_block = 0x0b,
    DW_TAG_member = 0x0d,
    DW_TAG_pointer_type = 0x0f,
    DW_TAG_reference_type = 0x10,
    DW_TAG_compile_unit = 0x11,
    DW_TAG_string_type = 0x12,
    DW_TAG_structure_type = 0x13,
    DW_TAG_subroutine_type = 0x15,
    DW_TAG_typedef = 0x16,
    DW_TAG_union_type = 0x17,
    DW_TAG_unspecified_parameters = 0x18,
    DW_TAG_variant = 0x19,
    DW_TAG_common_block = 0x1a,
    DW_TAG_common_inclusion = 0x1b,
    DW_TAG_inheritance = 0x1c,
    DW_TAG_inlined_subroutine = 0x1d,
    DW_TAG_module = 0x1e,
    DW_TAG_ptr_to_member_type = 0x1f,
    DW_TAG_set_type = 0x20,
    DW_TAG_subrange_type = 0x21,
    DW_TAG_with_stmt = 0x22,
    DW_TAG_access_declaration = 0x23,
    DW_TAG_base_type = 0x24,
    DW_TAG_catch_block = 0x25,
    DW_TAG_const_type = 0x26,
    DW_TAG_constant = 0x27,
    DW_TAG_enumerator = 0x28,
    DW_TAG_file_type = 0x29,
    DW_TAG_friend = 0x2a,
    DW_TAG_namelist = 0x2b,
    DW_TAG_namelist_item = 0x2c,
    DW_TAG_packed_type = 0x2d,
    DW_TAG_subprogram = 0x2e,
    DW_TAG_template_type_parameter = 0x2f,
    DW_TAG_template_value_parameter = 0x30,
    DW_TAG_thrown_type = 0x31,
    DW_TAG_try_block = 0x32,
    DW_TAG_variant_part = 0x33,
    DW_TAG_variable = 0x34,
    DW_TAG_volatile_type = 0x35,
    DW_TAG_dwarf_procedure = 0x36,
    DW_TAG_restrict_type = 0x37,
    DW_TAG_interface_type = 0x38,
    DW_TAG_namespace = 0x39,
    DW_TAG_imported_module = 0x3a,
    DW_TAG_unspecified_type = 0x3b,
    DW_TAG_partial_unit = 0x3c,
    DW_TAG_imported_unit = 0x3d,
    DW_TAG_condition = 0x3f,
    DW_TAG_shared_type = 0x40,
    DW_TAG_type_unit = 0x41,
    DW_TAG_rvalue_reference_type = 0x42,
    DW_TAG_template_alias = 0x43,

    DW_TAG_lo_user = 0x4080,
    DW_TAG_hi_user = 0xffff,
});

dw!(DwAt(u64) {
    DW_AT_null = 0x00,

    DW_AT_sibling = 0x01,
    DW_AT_location = 0x02,
    DW_AT_name = 0x03,
    DW_AT_ordering = 0x09,
    DW_AT_byte_size = 0x0b,
    DW_AT_bit_offset = 0x0c,
    DW_AT_bit_size = 0x0d,
    DW_AT_stmt_list = 0x10,
    DW_AT_low_pc = 0x11,
    DW_AT_high_pc = 0x12,
    DW_AT_language = 0x13,
    DW_AT_discr = 0x15,
    DW_AT_discr_value = 0x16,
    DW_AT_visibility = 0x17,
    DW_AT_import = 0x18,
    DW_AT_string_length = 0x19,
    DW_AT_common_reference = 0x1a,
    DW_AT_comp_dir = 0x1b,
    DW_AT_const_value = 0x1c,
    DW_AT_containing_type = 0x1d,
    DW_AT_default_value = 0x1e,
    DW_AT_inline = 0x20,
    DW_AT_is_optional = 0x21,
    DW_AT_lower_bound = 0x22,
    DW_AT_producer = 0x25,
    DW_AT_prototyped = 0x27,
    DW_AT_return_addr = 0x2a,
    DW_AT_start_scope = 0x2c,
    DW_AT_bit_stride = 0x2e,
    DW_AT_upper_bound = 0x2f,
    DW_AT_abstract_origin = 0x31,
    DW_AT_accessibility = 0x32,
    DW_AT_address_class = 0x33,
    DW_AT_artificial = 0x34,
    DW_AT_base_types = 0x35,
    DW_AT_calling_convention = 0x36,
    DW_AT_count = 0x37,
    DW_AT_data_member_location = 0x38,
    DW_AT_decl_column = 0x39,
    DW_AT_decl_file = 0x3a,
    DW_AT_decl_line = 0x3b,
    DW_AT_declaration = 0x3c,
    DW_AT_discr_list = 0x3d,
    DW_AT_encoding = 0x3e,
    DW_AT_external = 0x3f,
    DW_AT_frame_base = 0x40,
    DW_AT_friend = 0x41,
    DW_AT_identifier_case = 0x42,
    DW_AT_macro_info = 0x43,
    DW_AT_namelist_item = 0x44,
    DW_AT_priority = 0x45,
    DW_AT_segment = 0x46,
    DW_AT_specification = 0x47,
    DW_AT_static_link = 0x48,
    DW_AT_type = 0x49,
    DW_AT_use_location = 0x4a,
    DW_AT_variable_parameter = 0x4b,
    DW_AT_virtuality = 0x4c,
    DW_AT_vtable_elem_location = 0x4d,
    DW_AT_allocated = 0x4e,
    DW_AT_associated = 0x4f,
    DW_AT_data_location = 0x50,
    DW_AT_byte_stride = 0x51,
    DW_AT_entry_pc = 0x52,
    DW_AT_use_UTF8 = 0x53,
    DW_AT_extension = 0x54,
    DW_AT_ranges = 0x55,
    DW_AT_trampoline = 0x56,
    DW_AT_call_column = 0x57,
    DW_AT_call_file = 0x58,
    DW_AT_call_line = 0x59,
    DW_AT_description = 0x5a,
    DW_AT_binary_scale = 0x5b,
    DW_AT_decimal_scale = 0x5c,
    DW_AT_small = 0x5d,
    DW_AT_decimal_sign = 0x5e,
    DW_AT_digit_count = 0x5f,
    DW_AT_picture_string = 0x60,
    DW_AT_mutable = 0x61,
    DW_AT_threads_scaled = 0x62,
    DW_AT_explicit = 0x63,
    DW_AT_object_pointer = 0x64,
    DW_AT_endianity = 0x65,
    DW_AT_elemental = 0x66,
    DW_AT_pure = 0x67,
    DW_AT_recursive = 0x68,
    DW_AT_signature = 0x69,
    DW_AT_main_subprogram = 0x6a,
    DW_AT_data_bit_offset = 0x6b,
    DW_AT_const_expr = 0x6c,
    DW_AT_enum_class = 0x6d,
    DW_AT_linkage_name = 0x6e,

    DW_AT_lo_user = 0x2000,
    DW_AT_hi_user = 0x3fff,
});

dw!(DwForm(u64) {
    DW_FORM_null = 0x00,

    DW_FORM_addr = 0x01,
    DW_FORM_block2 = 0x03,
    DW_FORM_block4 = 0x04,
    DW_FORM_data2 = 0x05,
    DW_FORM_data4 = 0x06,
    DW_FORM_data8 = 0x07,
    DW_FORM_string = 0x08,
    DW_FORM_block = 0x09,
    DW_FORM_block1 = 0x0a,
    DW_FORM_data1 = 0x0b,
    DW_FORM_flag = 0x0c,
    DW_FORM_sdata = 0x0d,
    DW_FORM_strp = 0x0e,
    DW_FORM_udata = 0x0f,
    DW_FORM_ref_addr = 0x10,
    DW_FORM_ref1 = 0x11,
    DW_FORM_ref2 = 0x12,
    DW_FORM_ref4 = 0x13,
    DW_FORM_ref8 = 0x14,
    DW_FORM_ref_udata = 0x15,
    DW_FORM_indirect = 0x16,
    DW_FORM_sec_offset = 0x17,
    DW_FORM_exprloc = 0x18,
    DW_FORM_flag_present = 0x19,
    DW_FORM_ref_sig8 = 0x20,
});

dw!(DwLns(u8) {
    DW_LNS_copy = 0x01,
    DW_LNS_advance_pc = 0x02,
    DW_LNS_advance_line = 0x03,
    DW_LNS_set_file = 0x04,
    DW_LNS_set_column = 0x05,
    DW_LNS_negate_stmt = 0x06,
    DW_LNS_set_basic_block = 0x07,
    DW_LNS_const_add_pc = 0x08,
    DW_LNS_fixed_advance_pc = 0x09,
    DW_LNS_set_prologue_end = 0x0a,
    DW_LNS_set_epilogue_begin = 0x0b,
    DW_LNS_set_isa = 0x0c,
});

dw!(DwLne(u8) {
    DW_LNE_end_sequence = 0x01,
    DW_LNE_set_address = 0x02,
    DW_LNE_define_file = 0x03,
    DW_LNE_set_discriminator = 0x04,

    DW_LNE_lo_user = 0x80,
    DW_LNE_hi_user = 0xff,
});
<|endoftext|>


<commit_before>use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
    TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
    let def_id = body.source.def_id();
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate.skip_binders() {
                ty::PredicateAtom::RegionOutlives(_)
                | ty::PredicateAtom::TypeOutlives(_)
                | ty::PredicateAtom::WellFormed(_)
                | ty::PredicateAtom::Projection(_)
                | ty::PredicateAtom::ConstEvaluatable(..)
                | ty::PredicateAtom::ConstEquate(..)
                | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
                ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
                ty::PredicateAtom::Trait(pred, _) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.self_ty().kind() {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                },
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
    for arg in ty.walk() {
        let ty = match arg.unpack() {
            GenericArgKind::Type(ty) => ty,

            // No constraints on lifetimes or constants, except potentially
            // constants' types, but `walk` will get to them as well.
            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
        };

        match ty.kind() {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                return Err((span, "mutable references in const fn are unstable".into()));
            },
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()));
            },
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        },
                    }
                }
            },
            _ => {},
        }
    }
    Ok(())
}

fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
    match rvalue {
        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            check_place(tcx, *place, span, body)
        },
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc_middle::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                },
                _ => check_operand(tcx, operand, span, body),
            }
        },
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, body)
        },
        Rvalue::Cast(
            CastKind::Pointer(
                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
            ),
            _,
            _,
        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
                deref_ty.ty
            } else {
                // We cannot allow this for now.
                return Err((span, "unsizing casts are only allowed for references right now".into()));
            };
            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                check_operand(tcx, op, span, body)?;
                // Casting/coercing things to slices is fine.
                Ok(())
            } else {
                // We just can't allow trait objects until we have figured out trait method calls.
                Err((span, "unsizing casts are not allowed in const fn".into()))
            }
        },
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, lhs, span, body)?;
            check_operand(tcx, rhs, span, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        },
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        },
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, body)?;
            }
            Ok(())
        },
    }
}

fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, *place, span, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        },

        StatementKind::FakeRead(_, place) |
        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),

        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Coverage(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let [ref proj_base @ .., elem] = *cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        return Err((span, "accessing union fields is unstable".into()));
                    }
                }
            },
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Downcast(..)
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {},
        }
    }

    Ok(())
}

fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdge { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume
        | TerminatorKind::Unreachable => Ok(()),

        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
        TerminatorKind::DropAndReplace { place, value, .. } => {
            check_place(tcx, *place, span, body)?;
            check_operand(tcx, value, span, body)
        },

        TerminatorKind::SwitchInt {
            discr,
            switch_ty: _,
            values: _,
            targets: _,
        } => check_operand(tcx, discr, span, body),

        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        },

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
            fn_span: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                // HACK: This is to "unstabilize" the `transmute` intrinsic
                // within const fns. `transmute` is allowed in all other const contexts.
                // This won't really scale to more intrinsics or functions. Let's allow const
                // transmutes in const fn before we add more hacks to this.
                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
                    return Err((
                        span,
                        "can only call `transmute` from const items, not `const fn`".into(),
                    ));
                }

                check_operand(tcx, func, span, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        },

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, body),

        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
    }
}
<commit_msg>Refactor how SwitchInt stores jump targets<commit_after>use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
    TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
    let def_id = body.source.def_id();
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate.skip_binders() {
                ty::PredicateAtom::RegionOutlives(_)
                | ty::PredicateAtom::TypeOutlives(_)
                | ty::PredicateAtom::WellFormed(_)
                | ty::PredicateAtom::Projection(_)
                | ty::PredicateAtom::ConstEvaluatable(..)
                | ty::PredicateAtom::ConstEquate(..)
                | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
                ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
                ty::PredicateAtom::Trait(pred, _) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.self_ty().kind() {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                },
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
    for arg in ty.walk() {
        let ty = match arg.unpack() {
            GenericArgKind::Type(ty) => ty,

            // No constraints on lifetimes or constants, except potentially
            // constants' types, but `walk` will get to them as well.
            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
        };

        match ty.kind() {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                return Err((span, "mutable references in const fn are unstable".into()));
            },
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()));
            },
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        },
                    }
                }
            },
            _ => {},
        }
    }
    Ok(())
}

fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
    match rvalue {
        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            check_place(tcx, *place, span, body)
        },
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc_middle::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                },
                _ => check_operand(tcx, operand, span, body),
            }
        },
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, body)
        },
        Rvalue::Cast(
            CastKind::Pointer(
                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
            ),
            _,
            _,
        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
                deref_ty.ty
            } else {
                // We cannot allow this for now.
                return Err((span, "unsizing casts are only allowed for references right now".into()));
            };
            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                check_operand(tcx, op, span, body)?;
                // Casting/coercing things to slices is fine.
                Ok(())
            } else {
                // We just can't allow trait objects until we have figured out trait method calls.
                Err((span, "unsizing casts are not allowed in const fn".into()))
            }
        },
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, lhs, span, body)?;
            check_operand(tcx, rhs, span, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        },
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        },
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, body)?;
            }
            Ok(())
        },
    }
}

fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, *place, span, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        },

        StatementKind::FakeRead(_, place) |
        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),

        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Coverage(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let [ref proj_base @ .., elem] = *cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        return Err((span, "accessing union fields is unstable".into()));
                    }
                }
            },
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Downcast(..)
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {},
        }
    }

    Ok(())
}

fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdge { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume
        | TerminatorKind::Unreachable => Ok(()),

        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
        TerminatorKind::DropAndReplace { place, value, .. } => {
            check_place(tcx, *place, span, body)?;
            check_operand(tcx, value, span, body)
        },

        TerminatorKind::SwitchInt {
            discr,
            switch_ty: _,
            targets: _,
        } => check_operand(tcx, discr, span, body),

        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        },

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
            fn_span: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                // HACK: This is to "unstabilize" the `transmute` intrinsic
                // within const fns. `transmute` is allowed in all other const contexts.
                // This won't really scale to more intrinsics or functions. Let's allow const
                // transmutes in const fn before we add more hacks to this.
                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
                    return Err((
                        span,
                        "can only call `transmute` from const items, not `const fn`".into(),
                    ));
                }

                check_operand(tcx, func, span, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        },

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, body),

        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
    }
}
<|endoftext|>