<commit_before>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

const UNCLOSING_QUOTE: &'static str = "quoted arg does not close";

#[derive(Debug)]
enum ParseState {
    ScanningForArguments,
    ScanningArgument,
    ScanningQuotedArgument,
}

/// Parses raw command-line input into a list of separate arguments. Arguments
/// are separated by whitespace, can be quoted, and can have escaped characters
/// inside of them.
///
/// The vector of strings returned can be parsed by a library such as getopts.
pub fn input_to_arguments(input: String) -> Result<Vec<String>, &'static str> {
    let mut args: Vec<String> = Vec::new(); // Output genned from input.

    // This parser uses a basic state machine that can be in a few different
    // states. The parser can be searching for new arguments, stepping through
    // an argument, or stepping through a quoted argument.
    let mut state = ParseState::ScanningForArguments;

    // Argument parsing offsets used when extracting arguments out of the input
    // string. These values represent binary offsets rather than character
    // indices as unicode values can vary in length.
    let mut arg_start: usize = 0;
    let mut offset: usize = 0;

    // Iterate over each character in the input and separate different arguments
    // and put the in a list of arguments. The index from the iterator is not
    // used since it's not a binary offset of the character currently being
    // parsed.
    for c in input.chars() {
        match state {
            // Determine a scanning state depending on the first non-whitespace
            // character encountered and set the index of the first character in
            // the argument.
            ParseState::ScanningForArguments => {
                if !is_whitespace(c) {
                    if is_quote(c) {
                        state = ParseState::ScanningQuotedArgument;
                        if offset < input.len() - 1 {
                            arg_start = offset + 1; // The start of the argument begins here.
                        } else {
                            return Err(UNCLOSING_QUOTE);
                        }
                    } else {
                        state = ParseState::ScanningArgument;
                        arg_start = offset; // The start of the argument begins here.

                        // Push the argument if we're at the end on the input.
                        if offset == input.len() - 1 {
                            let arg = String::from(&input[arg_start..input.len()]);
                            args.push(arg);
                            arg_start = 0;
                            state = ParseState::ScanningForArguments;
                        }
                    }
                }
            },

            // Scans until a whitespace character or the end of the input is
            // reached. Once whitespace is encountered, the argument currently
            // being parsed is pushed to the argument list.
            ParseState::ScanningArgument => {
                if is_whitespace(c) {
                    let arg = String::from(&input[arg_start..offset]);
                    args.push(arg);
                    arg_start = 0;
                    state = ParseState::ScanningForArguments;
                } else if is_quote(c) {
                    return Err(UNCLOSING_QUOTE);
                } else if offset == input.len() - 1 {
                    let arg = String::from(&input[arg_start..input.len()]);
                    args.push(arg);
                    arg_start = 0;
                    state = ParseState::ScanningForArguments;
                }
            },

            // Scan until a non escaped quote is reached. Once encountered, the
            // argument currently being parsed is pushed to the argument list.
            ParseState::ScanningQuotedArgument => {
                if is_quote(c) {
                    let arg = String::from(&input[arg_start..offset]);
                    args.push(arg);
                    arg_start = 0;
                    state = ParseState::ScanningForArguments;
                } else if offset == input.len() - 1 {
                    return Err(UNCLOSING_QUOTE);
                }
            },
        }
        offset += c.to_string().len();
    }

    Ok(args)
}

/// Returns true if the character passed is a whitespace character. Both spaces
/// and tabs are considered whitespace characters.
fn is_whitespace(c: char) -> bool {
    c == ' ' || c == '\t'
}

/// Returns true if the character passed is a quote.
fn is_quote(c: char) -> bool {
    c == '"'
}
<commit_msg>Fixed minor unicode compatibility problem<commit_after>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

const UNCLOSING_QUOTE: &'static str = "quoted arg does not close";

#[derive(Debug)]
enum ParseState {
    ScanningForArguments,
    ScanningArgument,
    ScanningQuotedArgument,
}

/// Parses raw command-line input into a list of separate arguments. Arguments
/// are separated by whitespace, can be quoted, and can have escaped characters
/// inside of them.
///
/// The vector of strings returned can be parsed by a library such as getopts.
pub fn input_to_arguments(input: String) -> Result<Vec<String>, &'static str> {
    let mut args: Vec<String> = Vec::new(); // Output genned from input.

    // This parser uses a basic state machine that can be in a few different
    // states. The parser can be searching for new arguments, stepping through
    // an argument, or stepping through a quoted argument.
    let mut state = ParseState::ScanningForArguments;

    // Argument parsing offsets used when extracting arguments out of the input
    // string. These values represent binary offsets rather than character
    // indices as unicode values can vary in length.
    let mut arg_start: usize = 0;
    let mut offset: usize = 0;

    // Iterate over each character in the input and separate different arguments
    // and put the in a list of arguments. The index from the iterator is not
    // used since it's not a binary offset of the character currently being
    // parsed.
    for c in input.chars() {
        let charlen = c.to_string().len();

        match state {
            // Determine a scanning state depending on the first non-whitespace
            // character encountered and set the index of the first character in
            // the argument.
            ParseState::ScanningForArguments => {
                if !is_whitespace(c) {
                    if is_quote(c) {
                        state = ParseState::ScanningQuotedArgument;
                        if offset < input.len() - charlen {
                            arg_start = offset + charlen; // The start of the argument begins here.
                        } else {
                            return Err(UNCLOSING_QUOTE);
                        }
                    } else {
                        state = ParseState::ScanningArgument;
                        arg_start = offset; // The start of the argument begins here.

                        // Push the argument if we're at the end on the input.
                        if offset == input.len() - charlen {
                            let arg = String::from(&input[arg_start..input.len()]);
                            args.push(arg);
                            arg_start = 0;
                            state = ParseState::ScanningForArguments;
                        }
                    }
                }
            },

            // Scans until a whitespace character or the end of the input is
            // reached. Once whitespace is encountered, the argument currently
            // being parsed is pushed to the argument list.
            ParseState::ScanningArgument => {
                if is_whitespace(c) {
                    let arg = String::from(&input[arg_start..offset]);
                    args.push(arg);
                    arg_start = 0;
                    state = ParseState::ScanningForArguments;
                } else if is_quote(c) {
                    return Err(UNCLOSING_QUOTE);
                } else if offset == input.len() - charlen {
                    let arg = String::from(&input[arg_start..input.len()]);
                    args.push(arg);
                    arg_start = 0;
                    state = ParseState::ScanningForArguments;
                }
            },

            // Scan until a non escaped quote is reached. Once encountered, the
            // argument currently being parsed is pushed to the argument list.
            ParseState::ScanningQuotedArgument => {
                if is_quote(c) {
                    let arg = String::from(&input[arg_start..offset]);
                    args.push(arg);
                    arg_start = 0;
                    state = ParseState::ScanningForArguments;
                } else if offset == input.len() - charlen {
                    return Err(UNCLOSING_QUOTE);
                }
            },
        }
        offset += charlen;
    }

    Ok(args)
}

/// Returns true if the character passed is a whitespace character. Both spaces
/// and tabs are considered whitespace characters.
fn is_whitespace(c: char) -> bool {
    c == ' ' || c == '\t'
}

/// Returns true if the character passed is a quote.
fn is_quote(c: char) -> bool {
    c == '"'
}
<|endoftext|>


<commit_before>extern crate crossbeam;
use std;
use decoders::NUM_CORES;
use std::mem;

extern crate byteorder;
use self::byteorder::{BigEndian, LittleEndian, ByteOrder};

#[derive(Debug, Copy, Clone)]
pub struct Endian {
  big: bool,
}

impl Endian {
  pub fn ru32(&self, buf: &[u8], pos: usize) -> u32 {
    if self.big {
      BEu32(buf,pos)
    } else {
      LEu32(buf,pos)
    }
  }

  pub fn ru16(&self, buf: &[u8], pos: usize) -> u16 {
    if self.big {
      BEu16(buf,pos)
    } else {
      LEu16(buf,pos)
    }
  }
}

pub static BIG_ENDIAN: Endian = Endian{big: true};
pub static LITTLE_ENDIAN: Endian = Endian{big: false};

#[allow(non_snake_case)] #[inline] pub fn BEu32(buf: &[u8], pos: usize) -> u32 {
  BigEndian::read_u32(&buf[pos..pos+4])
}

#[allow(non_snake_case)] #[inline] pub fn LEu32(buf: &[u8], pos: usize) -> u32 {
  LittleEndian::read_u32(&buf[pos..pos+4])
}

#[allow(non_snake_case)] #[inline] pub fn BEu16(buf: &[u8], pos: usize) -> u16 {
  BigEndian::read_u16(&buf[pos..pos+2])
}

#[allow(non_snake_case)] #[inline] pub fn LEu16(buf: &[u8], pos: usize) -> u16 {
  LittleEndian::read_u16(&buf[pos..pos+2])
}

pub fn decode_12be(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*12/8) as usize)..];

    for (o, i) in out.chunks_mut(2).zip(inb.chunks(3)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;
      let g3: u16 = i[2] as u16;

      o[0] = (g1 << 4) | (g2 >> 4);
      o[1] = ((g2 & 0x0f) << 8) | g3;
    }
  }))
}

pub fn decode_12le(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*12/8) as usize)..];

    for (o, i) in out.chunks_mut(2).zip(inb.chunks(3)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;
      let g3: u16 = i[2] as u16;

      o[0] = ((g2 & 0x0f) << 8) | g1;
      o[1] = (g3 << 4) | (g2 >> 4);
    }
  }))
}

pub fn decode_12be_unpacked(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*2) as usize)..];

    for (o, i) in out.chunks_mut(1).zip(inb.chunks(2)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;

      o[0] = ((g1 & 0x0f) << 8) | g2;
    }
  }))
}

pub fn decode_16le(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*2) as usize)..];

    for (o, i) in out.chunks_mut(1).zip(inb.chunks(2)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;

      o[0] = (g2 << 8) | g1;
    }
  }))
}

pub fn decode_threaded<F>(width: usize, height: usize, closure: &F) -> Vec<u16>
  where F : Fn(&mut [u16], usize, usize, usize)+std::marker::Sync {
  let mut out: Vec<u16> = vec![0; (width*height) as usize];

  // Default to the number of available physical cores (no hyperthreading)
  let threads = *NUM_CORES;

  // If we're only using one thread do it all sequentially and be done with it
  if threads < 2 || height < threads {
    closure(&mut out, 0, width, height);
    return out
  }

  let mut split = height/threads;
  if split*threads < height { // Make sure the last split is always the smallest
    split += 1;
  }

  crossbeam::scope(|scope| {
    let mut handles = Vec::new();
    for (i,out_part) in (&mut out[..]).chunks_mut(split*width).enumerate() {
      let start = split*(i);
      let tall = out_part.len()/width;
      let handle = scope.spawn(move || {
        closure(out_part, start, width, tall);
      });
      handles.push(handle);
    }

    for h in handles { h.join() };
  });

  out
}

#[derive(Debug, Clone)]
pub struct LookupTable {
  table: Vec<(u16, u16, u16)>,
}

impl LookupTable {
  pub fn new(table: &[u16]) -> LookupTable {
    let mut tbl = vec![(0,0,0); table.len()];
    for i in 0..table.len() {
      let center = table[i];
      let lower = if i > 0 {table[i-1]} else {center};
      let upper = if i < (table.len()-1) {table[i+1]} else {center};
      let base = center - ((upper - lower + 2) / 4);
      let delta = upper - lower;
      tbl[i] = (center, base, delta);
    }
    LookupTable {
      table: tbl,
    }
  }

//  pub fn lookup(&self, value: u16) -> u16 {
//    let (val, _, _) = self.table[value as usize];
//    val
//  }

  pub fn dither(&self, value: u16, rand: &mut u32) -> u16 {
    let (_, sbase, sdelta) = self.table[value as usize];
    let base = sbase as u32;
    let delta = sdelta as u32;
    let pixel = base + ((delta * (*rand & 2047) + 1024) >> 12);
    *rand = 15700 * (*rand & 65535) + (*rand >> 16);
    pixel as u16
  }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PumpOrder {
  LSB,
  MSB,
}

pub struct BitPump<'a> {
  buffer: &'a [u8],
  pos: usize,
  bits: u64,
  nbits: u32,
  order: PumpOrder,
}

impl<'a> BitPump<'a> {
  pub fn new(src: &'a [u8], order: PumpOrder) -> BitPump {
    BitPump {
      buffer: src,
      pos: 0,
      bits: 0,
      nbits: 0,
      order: order,
    }
  }

  fn fill_bits(&mut self) {
    if self.order == PumpOrder::MSB {
      let mut bytes: [u8;8] = unsafe{mem::transmute(self.bits)};
      let fillbytes = (64-self.nbits) / 8;
      for i in 0..(8-fillbytes) {
        bytes[(7-i) as usize] = bytes[(7-i-fillbytes) as usize];
      }
      for i in 0..fillbytes {
        bytes[(fillbytes-i-1) as usize] = self.buffer[self.pos];
        self.pos += 1;
      }
      self.bits = unsafe{mem::transmute(bytes)};
      self.nbits += fillbytes*8;
    } else {
      let inbits: u64 = LEu32(self.buffer, self.pos) as u64;
      self.pos += 4;
      self.bits = ((inbits << 32) | (self.bits << (32-self.nbits))) >> (32-self.nbits);
      self.nbits += 32;
    }
  }

  pub fn peek_bits(&mut self, num: u32) -> u32 {
    if num > self.nbits {
      self.fill_bits();
    }
    if self.order == PumpOrder::MSB {
      (self.bits >> (self.nbits-num)) as u32
    } else {
      (self.bits & (0x0ffffffffu64 >> (32-num))) as u32
    }
  }

  pub fn get_bits(&mut self, num: u32) -> u32 {
    if num == 0 {
      return 0
    }

    let val = self.peek_bits(num);
    self.nbits -= num;
    if self.order == PumpOrder::MSB {
      let erasebits = 64-self.nbits;
      self.bits = (self.bits << erasebits) >> erasebits;
    } else {
      self.bits = self.bits >> num;
    }
    val
  }

  pub fn get_ibits(&mut self, num: u32) -> i32 {
    let val = self.get_bits(num);
    unsafe{mem::transmute(val)}
  }
}
<commit_msg>BitPump: don't try to erase bits on empty buffers<commit_after>extern crate crossbeam;
use std;
use decoders::NUM_CORES;
use std::mem;

extern crate byteorder;
use self::byteorder::{BigEndian, LittleEndian, ByteOrder};

#[derive(Debug, Copy, Clone)]
pub struct Endian {
  big: bool,
}

impl Endian {
  pub fn ru32(&self, buf: &[u8], pos: usize) -> u32 {
    if self.big {
      BEu32(buf,pos)
    } else {
      LEu32(buf,pos)
    }
  }

  pub fn ru16(&self, buf: &[u8], pos: usize) -> u16 {
    if self.big {
      BEu16(buf,pos)
    } else {
      LEu16(buf,pos)
    }
  }
}

pub static BIG_ENDIAN: Endian = Endian{big: true};
pub static LITTLE_ENDIAN: Endian = Endian{big: false};

#[allow(non_snake_case)] #[inline] pub fn BEu32(buf: &[u8], pos: usize) -> u32 {
  BigEndian::read_u32(&buf[pos..pos+4])
}

#[allow(non_snake_case)] #[inline] pub fn LEu32(buf: &[u8], pos: usize) -> u32 {
  LittleEndian::read_u32(&buf[pos..pos+4])
}

#[allow(non_snake_case)] #[inline] pub fn BEu16(buf: &[u8], pos: usize) -> u16 {
  BigEndian::read_u16(&buf[pos..pos+2])
}

#[allow(non_snake_case)] #[inline] pub fn LEu16(buf: &[u8], pos: usize) -> u16 {
  LittleEndian::read_u16(&buf[pos..pos+2])
}

pub fn decode_12be(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*12/8) as usize)..];

    for (o, i) in out.chunks_mut(2).zip(inb.chunks(3)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;
      let g3: u16 = i[2] as u16;

      o[0] = (g1 << 4) | (g2 >> 4);
      o[1] = ((g2 & 0x0f) << 8) | g3;
    }
  }))
}

pub fn decode_12le(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*12/8) as usize)..];

    for (o, i) in out.chunks_mut(2).zip(inb.chunks(3)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;
      let g3: u16 = i[2] as u16;

      o[0] = ((g2 & 0x0f) << 8) | g1;
      o[1] = (g3 << 4) | (g2 >> 4);
    }
  }))
}

pub fn decode_12be_unpacked(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*2) as usize)..];

    for (o, i) in out.chunks_mut(1).zip(inb.chunks(2)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;

      o[0] = ((g1 & 0x0f) << 8) | g2;
    }
  }))
}

pub fn decode_16le(buf: &[u8], width: usize, height: usize) -> Vec<u16> {
  decode_threaded(width, height, &(|out: &mut [u16], start, width, _| {
    let inb = &buf[((start*width*2) as usize)..];

    for (o, i) in out.chunks_mut(1).zip(inb.chunks(2)) {
      let g1: u16 = i[0] as u16;
      let g2: u16 = i[1] as u16;

      o[0] = (g2 << 8) | g1;
    }
  }))
}

pub fn decode_threaded<F>(width: usize, height: usize, closure: &F) -> Vec<u16>
  where F : Fn(&mut [u16], usize, usize, usize)+std::marker::Sync {
  let mut out: Vec<u16> = vec![0; (width*height) as usize];

  // Default to the number of available physical cores (no hyperthreading)
  let threads = *NUM_CORES;

  // If we're only using one thread do it all sequentially and be done with it
  if threads < 2 || height < threads {
    closure(&mut out, 0, width, height);
    return out
  }

  let mut split = height/threads;
  if split*threads < height { // Make sure the last split is always the smallest
    split += 1;
  }

  crossbeam::scope(|scope| {
    let mut handles = Vec::new();
    for (i,out_part) in (&mut out[..]).chunks_mut(split*width).enumerate() {
      let start = split*(i);
      let tall = out_part.len()/width;
      let handle = scope.spawn(move || {
        closure(out_part, start, width, tall);
      });
      handles.push(handle);
    }

    for h in handles { h.join() };
  });

  out
}

#[derive(Debug, Clone)]
pub struct LookupTable {
  table: Vec<(u16, u16, u16)>,
}

impl LookupTable {
  pub fn new(table: &[u16]) -> LookupTable {
    let mut tbl = vec![(0,0,0); table.len()];
    for i in 0..table.len() {
      let center = table[i];
      let lower = if i > 0 {table[i-1]} else {center};
      let upper = if i < (table.len()-1) {table[i+1]} else {center};
      let base = center - ((upper - lower + 2) / 4);
      let delta = upper - lower;
      tbl[i] = (center, base, delta);
    }
    LookupTable {
      table: tbl,
    }
  }

//  pub fn lookup(&self, value: u16) -> u16 {
//    let (val, _, _) = self.table[value as usize];
//    val
//  }

  pub fn dither(&self, value: u16, rand: &mut u32) -> u16 {
    let (_, sbase, sdelta) = self.table[value as usize];
    let base = sbase as u32;
    let delta = sdelta as u32;
    let pixel = base + ((delta * (*rand & 2047) + 1024) >> 12);
    *rand = 15700 * (*rand & 65535) + (*rand >> 16);
    pixel as u16
  }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PumpOrder {
  LSB,
  MSB,
}

pub struct BitPump<'a> {
  buffer: &'a [u8],
  pos: usize,
  bits: u64,
  nbits: u32,
  order: PumpOrder,
}

impl<'a> BitPump<'a> {
  pub fn new(src: &'a [u8], order: PumpOrder) -> BitPump {
    BitPump {
      buffer: src,
      pos: 0,
      bits: 0,
      nbits: 0,
      order: order,
    }
  }

  fn fill_bits(&mut self) {
    if self.order == PumpOrder::MSB {
      let mut bytes: [u8;8] = unsafe{mem::transmute(self.bits)};
      let fillbytes = (64-self.nbits) / 8;
      for i in 0..(8-fillbytes) {
        bytes[(7-i) as usize] = bytes[(7-i-fillbytes) as usize];
      }
      for i in 0..fillbytes {
        bytes[(fillbytes-i-1) as usize] = self.buffer[self.pos];
        self.pos += 1;
      }
      self.bits = unsafe{mem::transmute(bytes)};
      self.nbits += fillbytes*8;
    } else {
      let inbits: u64 = LEu32(self.buffer, self.pos) as u64;
      self.pos += 4;
      self.bits = ((inbits << 32) | (self.bits << (32-self.nbits))) >> (32-self.nbits);
      self.nbits += 32;
    }
  }

  pub fn peek_bits(&mut self, num: u32) -> u32 {
    if num > self.nbits {
      self.fill_bits();
    }
    if self.order == PumpOrder::MSB {
      (self.bits >> (self.nbits-num)) as u32
    } else {
      (self.bits & (0x0ffffffffu64 >> (32-num))) as u32
    }
  }

  pub fn get_bits(&mut self, num: u32) -> u32 {
    if num == 0 {
      return 0
    }

    let val = self.peek_bits(num);
    self.nbits -= num;
    if self.order == PumpOrder::MSB {
      if self.nbits > 0 {
        let erasebits = 64-self.nbits;
        self.bits = (self.bits << erasebits) >> erasebits;
      }
    } else {
      self.bits = self.bits >> num;
    }
    val
  }

  pub fn get_ibits(&mut self, num: u32) -> i32 {
    let val = self.get_bits(num);
    unsafe{mem::transmute(val)}
  }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, Spanned};
use syntax::visit::FnKind;

use utils::{span_note_and_lint, span_lint_and_then, snippet_opt, match_path_ast, in_external_macro};

/// **What it does:** Checks for return statements at the end of a block.
///
/// **Why is this bad?** Removing the `return` and semicolon will make the code
/// more rusty.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(x: usize) { return x; }
/// ```
declare_lint! {
    pub NEEDLESS_RETURN,
    Warn,
    "using a return statement like `return expr;` where an expression would suffice"
}

/// **What it does:** Checks for `let`-bindings, which are subsequently returned.
///
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
/// more rusty.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// { let x = ..; x }
/// ```
declare_lint! {
    pub LET_AND_RETURN,
    Warn,
    "creating a let-binding and then immediately returning it like `let x = expr; x` at \
     the end of a block"
}

#[derive(Copy, Clone)]
pub struct ReturnPass;

impl ReturnPass {
    // Check the final stmt or expr in a block for unnecessary return.
    fn check_block_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
        if let Some(stmt) = block.stmts.last() {
            match stmt.node {
                ast::StmtKind::Expr(ref expr) |
                ast::StmtKind::Semi(ref expr) => {
                    self.check_final_expr(cx, expr, Some(stmt.span));
                },
                _ => (),
            }
        }
    }

    // Check a the final expression in a block if it's a return.
    fn check_final_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr, span: Option<Span>) {
        match expr.node {
            // simple return is always "bad"
            ast::ExprKind::Ret(Some(ref inner)) => {
                // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
                if !expr.attrs.iter().any(attr_is_cfg) {
                    self.emit_return_lint(cx, span.expect("`else return` is not possible"), inner.span);
                }
            },
            // a whole block? check it!
            ast::ExprKind::Block(ref block) => {
                self.check_block_return(cx, block);
            },
            // an if/if let expr, check both exprs
            // note, if without else is going to be a type checking error anyways
            // (except for unit type functions) so we don't match it
            ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
                self.check_block_return(cx, ifblock);
                self.check_final_expr(cx, elsexpr, None);
            },
            // a match expr, check all arms
            ast::ExprKind::Match(_, ref arms) => {
                for arm in arms {
                    self.check_final_expr(cx, &arm.body, Some(arm.body.span));
                }
            },
            _ => (),
        }
    }

    fn emit_return_lint(&mut self, cx: &EarlyContext, ret_span: Span, inner_span: Span) {
        if in_external_macro(cx, inner_span) {
            return;
        }
        span_lint_and_then(cx,
                           NEEDLESS_RETURN,
                           ret_span,
                           "unneeded return statement",
                           |db| if let Some(snippet) = snippet_opt(cx, inner_span) {
                               db.span_suggestion(ret_span, "remove `return` as shown:", snippet);
                           });
    }

    // Check for "let x = EXPR; x"
    fn check_let_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
        let mut it = block.stmts.iter();

        // we need both a let-binding stmt and an expr
        if_let_chain! {[
            let Some(ref retexpr) = it.next_back(),
            let ast::StmtKind::Expr(ref retexpr) = retexpr.node,
            let Some(stmt) = it.next_back(),
            let ast::StmtKind::Local(ref local) = stmt.node,
            !local.attrs.iter().any(attr_is_cfg),
            let Some(ref initexpr) = local.init,
            let ast::PatKind::Ident(_, Spanned { node: id, .. }, _) = local.pat.node,
            let ast::ExprKind::Path(_, ref path) = retexpr.node,
            match_path_ast(path, &[&id.name.as_str()]),
            !in_external_macro(cx, initexpr.span),
        ], {
                span_note_and_lint(cx,
                                   LET_AND_RETURN,
                                   retexpr.span,
                                   "returning the result of a let binding from a block. \
                                   Consider returning the expression directly.",
                                   initexpr.span,
                                   "this expression can be directly returned");
        }}
    }
}

impl LintPass for ReturnPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NEEDLESS_RETURN, LET_AND_RETURN)
    }
}

impl EarlyLintPass for ReturnPass {
    fn check_fn(&mut self, cx: &EarlyContext, kind: FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) {
        match kind {
            FnKind::ItemFn(.., block) |
            FnKind::Method(.., block) => self.check_block_return(cx, block),
            FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span)),
        }
    }

    fn check_block(&mut self, cx: &EarlyContext, block: &ast::Block) {
        self.check_let_return(cx, block);
    }
}

fn attr_is_cfg(attr: &ast::Attribute) -> bool {
    if attr.meta_item_list().is_some() {
       attr.name().map_or(false, |n| n == "cfg")
    } else {
        false
    }
}
<commit_msg>Simplify attr_is_cfg in returns.rs<commit_after>use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, Spanned};
use syntax::visit::FnKind;

use utils::{span_note_and_lint, span_lint_and_then, snippet_opt, match_path_ast, in_external_macro};

/// **What it does:** Checks for return statements at the end of a block.
///
/// **Why is this bad?** Removing the `return` and semicolon will make the code
/// more rusty.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(x: usize) { return x; }
/// ```
declare_lint! {
    pub NEEDLESS_RETURN,
    Warn,
    "using a return statement like `return expr;` where an expression would suffice"
}

/// **What it does:** Checks for `let`-bindings, which are subsequently returned.
///
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
/// more rusty.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// { let x = ..; x }
/// ```
declare_lint! {
    pub LET_AND_RETURN,
    Warn,
    "creating a let-binding and then immediately returning it like `let x = expr; x` at \
     the end of a block"
}

#[derive(Copy, Clone)]
pub struct ReturnPass;

impl ReturnPass {
    // Check the final stmt or expr in a block for unnecessary return.
    fn check_block_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
        if let Some(stmt) = block.stmts.last() {
            match stmt.node {
                ast::StmtKind::Expr(ref expr) |
                ast::StmtKind::Semi(ref expr) => {
                    self.check_final_expr(cx, expr, Some(stmt.span));
                },
                _ => (),
            }
        }
    }

    // Check a the final expression in a block if it's a return.
    fn check_final_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr, span: Option<Span>) {
        match expr.node {
            // simple return is always "bad"
            ast::ExprKind::Ret(Some(ref inner)) => {
                // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
                if !expr.attrs.iter().any(attr_is_cfg) {
                    self.emit_return_lint(cx, span.expect("`else return` is not possible"), inner.span);
                }
            },
            // a whole block? check it!
            ast::ExprKind::Block(ref block) => {
                self.check_block_return(cx, block);
            },
            // an if/if let expr, check both exprs
            // note, if without else is going to be a type checking error anyways
            // (except for unit type functions) so we don't match it
            ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
                self.check_block_return(cx, ifblock);
                self.check_final_expr(cx, elsexpr, None);
            },
            // a match expr, check all arms
            ast::ExprKind::Match(_, ref arms) => {
                for arm in arms {
                    self.check_final_expr(cx, &arm.body, Some(arm.body.span));
                }
            },
            _ => (),
        }
    }

    fn emit_return_lint(&mut self, cx: &EarlyContext, ret_span: Span, inner_span: Span) {
        if in_external_macro(cx, inner_span) {
            return;
        }
        span_lint_and_then(cx,
                           NEEDLESS_RETURN,
                           ret_span,
                           "unneeded return statement",
                           |db| if let Some(snippet) = snippet_opt(cx, inner_span) {
                               db.span_suggestion(ret_span, "remove `return` as shown:", snippet);
                           });
    }

    // Check for "let x = EXPR; x"
    fn check_let_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
        let mut it = block.stmts.iter();

        // we need both a let-binding stmt and an expr
        if_let_chain! {[
            let Some(ref retexpr) = it.next_back(),
            let ast::StmtKind::Expr(ref retexpr) = retexpr.node,
            let Some(stmt) = it.next_back(),
            let ast::StmtKind::Local(ref local) = stmt.node,
            !local.attrs.iter().any(attr_is_cfg),
            let Some(ref initexpr) = local.init,
            let ast::PatKind::Ident(_, Spanned { node: id, .. }, _) = local.pat.node,
            let ast::ExprKind::Path(_, ref path) = retexpr.node,
            match_path_ast(path, &[&id.name.as_str()]),
            !in_external_macro(cx, initexpr.span),
        ], {
                span_note_and_lint(cx,
                                   LET_AND_RETURN,
                                   retexpr.span,
                                   "returning the result of a let binding from a block. \
                                   Consider returning the expression directly.",
                                   initexpr.span,
                                   "this expression can be directly returned");
        }}
    }
}

impl LintPass for ReturnPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NEEDLESS_RETURN, LET_AND_RETURN)
    }
}

impl EarlyLintPass for ReturnPass {
    fn check_fn(&mut self, cx: &EarlyContext, kind: FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) {
        match kind {
            FnKind::ItemFn(.., block) |
            FnKind::Method(.., block) => self.check_block_return(cx, block),
            FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span)),
        }
    }

    fn check_block(&mut self, cx: &EarlyContext, block: &ast::Block) {
        self.check_let_return(cx, block);
    }
}

fn attr_is_cfg(attr: &ast::Attribute) -> bool {
    attr.meta_item_list().is_some() && attr.name().map_or(false, |n| n == "cfg")
}
<|endoftext|>


<commit_before>//
// Copyright 2019 The Project Oak Authors
//
// 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.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::{debug, info};
use oak_abi::{
    label::Label,
    proto::oak::application::{
        node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
        WebAssemblyConfiguration,
    },
};
use prost::Message;
use std::{collections::HashMap, path::PathBuf, process::Command};
use tonic::{
    metadata::MetadataValue,
    transport::{Certificate, Channel, ClientTlsConfig, Identity},
    Request,
};

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    // Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
    // should end with `target` so that it gets automatically ignored by our `.gitignore`.
    let target_dir = PathBuf::from("oak_tests/target");

    Command::new("cargo")
        .args(&[
            "build",
            &format!(
                "--target-dir={}",
                target_dir.to_str().expect("invalid target dir")
            ),
            "--target=wasm32-unknown-unknown",
            &format!("--manifest-path={}", cargo_path),
        ])
        .env_remove("RUSTFLAGS")
        .spawn()?
        .wait()?;

    let mut module_path = target_dir;
    module_path.push("wasm32-unknown-unknown/debug");
    module_path.push(module_name);

    info!("compiled Wasm module path: {:?}", module_path);

    std::fs::read(module_path)
}

/// Default entrypoint name for the module under test.
const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main";
/// Default URI that the tests expect to find a live Runtime at.
const RUNTIME_URI: &str = "https://localhost:8080";

const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml";
const MODULE_WASM_SUFFIX: &str = ".wasm";

// Retry parameters when connecting to a gRPC server.
const RETRY_COUNT: u32 = 360;
const RETRY_INTERVAL: std::time::Duration = std::time::Duration::from_millis(500);

/// Convenience helper to build and run a single-Node Application with the given module name, using
/// the default name "oak_main" for its entrypoint.
pub fn run_single_module_default(
    module_config_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME)
}

/// Convenience helper to build and run a single-Node application with the given module name, using
/// the provided entrypoint name.
pub fn run_single_module(
    module_config_name: &str,
    entrypoint_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    let combined_config = runtime_config(module_config_name, entrypoint_name);
    oak_runtime::configure_and_run(combined_config)
}

/// Build the configuration needed to launch a test Runtime instance that
/// runs a single-Node application with the given module name and entrypoint.
pub fn runtime_config(
    module_config_name: &str,
    entrypoint_name: &str,
) -> oak_runtime::RuntimeConfiguration {
    let wasm: HashMap<String, Vec<u8>> = [(
        module_config_name.to_owned(),
        compile_rust_wasm(
            DEFAULT_MODULE_MANIFEST,
            &(module_config_name.to_owned() + MODULE_WASM_SUFFIX),
        )
        .expect("failed to build wasm module"),
    )]
    .iter()
    .cloned()
    .collect();

    runtime_config_wasm(wasm, module_config_name, entrypoint_name)
}

/// Build the configuration needed to launch a test Runtime instance that runs the given
/// collection of Wasm modules, starting with the given module name and entrypoint.
pub fn runtime_config_wasm(
    wasm_modules: HashMap<String, Vec<u8>>,
    module_config_name: &str,
    entrypoint_name: &str,
) -> oak_runtime::RuntimeConfiguration {
    oak_runtime::RuntimeConfiguration {
        metrics_port: Some(9090),
        introspect_port: Some(1909),
        grpc_config: oak_runtime::GrpcConfiguration {
            grpc_server_tls_identity: Some(Identity::from_pem(
                include_str!("../certs/local.pem"),
                include_str!("../certs/local.key"),
            )),
            grpc_client_root_tls_certificate: Some(
                oak_runtime::config::load_certificate(&include_str!("../certs/ca.pem")).unwrap(),
            ),
            oidc_client_info: None,
        },
        app_config: ApplicationConfiguration {
            wasm_modules,
            initial_node_configuration: Some(NodeConfiguration {
                name: "test".to_string(),
                config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                    wasm_module_name: module_config_name.to_string(),
                    wasm_entrypoint_name: entrypoint_name.to_string(),
                })),
            }),
        },
    }
}

/// Inject a gRPC request into an Oak Application under test and return the corresponding
/// response (or error).
pub fn grpc_request<R, Q>(
    proxy: &oak_runtime::RuntimeProxy,
    handle: oak_abi::Handle,
    method_name: &str,
    req: &R,
) -> oak::grpc::Result<Q>
where
    R: prost::Message,
    Q: prost::Message + Default,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req =
        oak_abi::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![],
    };

    grpc_req
        .encode(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (req_write_handle, req_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");
    proxy
        .channel_write(req_write_handle, req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (rsp_write_handle, rsp_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![req_read_handle, rsp_write_handle],
    };

    // Send the notification message (with attached handles) into the Node under test.
    proxy
        .channel_write(handle, notify_msg)
        .expect("could not write message");
    proxy
        .channel_close(req_write_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(req_read_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(rsp_write_handle)
        .expect("failed to close channel");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match proxy.channel_read(rsp_read_handle) {
            Ok(Some(r)) => r,
            Ok(None) => {
                debug!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        proxy
            .channel_close(rsp_read_handle)
            .expect("failed to close channel");
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let rsp = oak_abi::proto::oak::encap::GrpcResponse::decode(rsp.data.as_slice())
            .expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        let status = rsp.status.unwrap_or_default();
        if status.code != oak::grpc::Code::Ok as i32 {
            return Err(status);
        }
        let rsp =
            Q::decode(rsp.rsp_msg.as_slice()).expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}

/// Build a channel and interceptor suitable for building a client that connects
/// to a Runtime under test.
pub async fn channel_and_interceptor() -> (Channel, impl Into<tonic::Interceptor>) {
    // Build a channel that connects to the Runtime under test.
    let uri = RUNTIME_URI.parse().expect("Error parsing URI");
    let tls_config = ClientTlsConfig::new()
        .ca_certificate(Certificate::from_pem(include_str!("../certs/ca.pem")));
    let builder = Channel::builder(uri).tls_config(tls_config);

    // The Runtime may have just been started for a test, and make take some time
    // to come fully up, start a gRPC server and accept connections. Allow for
    // this by retrying at intervals until the server responds or we hit a retry
    // limit.
    let mut retries = 0;
    let channel;
    loop {
        match builder.connect().await {
            Ok(c) => {
                debug!("Connected to gRPC server");
                channel = c;
                break;
            }
            Err(err) => {
                if retries < RETRY_COUNT {
                    debug!("Failed to connect, try again momentarily: {:?}", err);
                    retries += 1;
                    std::thread::sleep(RETRY_INTERVAL);
                } else {
                    panic!("Failed to connect, last err: {:?}", err);
                }
            }
        }
    }

    // Build an interceptor that will attach a public-untrusted Oak label to
    // every gRPC request.
    let mut label = Vec::new();
    Label::public_untrusted()
        .encode(&mut label)
        .expect("Error encoding label");
    let interceptor = move |mut request: Request<()>| {
        request.metadata_mut().insert_bin(
            oak_abi::OAK_LABEL_GRPC_METADATA_KEY,
            MetadataValue::from_bytes(label.as_ref()),
        );
        Ok(request)
    };

    (channel, interceptor)
}
<commit_msg>sdk: drop oak_tests::grpc_request now tests over gRPC<commit_after>//
// Copyright 2019 The Project Oak Authors
//
// 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.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::{debug, info};
use oak_abi::{
    label::Label,
    proto::oak::application::{
        node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
        WebAssemblyConfiguration,
    },
};
use prost::Message;
use std::{collections::HashMap, path::PathBuf, process::Command};
use tonic::{
    metadata::MetadataValue,
    transport::{Certificate, Channel, ClientTlsConfig, Identity},
    Request,
};

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    // Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
    // should end with `target` so that it gets automatically ignored by our `.gitignore`.
    let target_dir = PathBuf::from("oak_tests/target");

    Command::new("cargo")
        .args(&[
            "build",
            &format!(
                "--target-dir={}",
                target_dir.to_str().expect("invalid target dir")
            ),
            "--target=wasm32-unknown-unknown",
            &format!("--manifest-path={}", cargo_path),
        ])
        .env_remove("RUSTFLAGS")
        .spawn()?
        .wait()?;

    let mut module_path = target_dir;
    module_path.push("wasm32-unknown-unknown/debug");
    module_path.push(module_name);

    info!("compiled Wasm module path: {:?}", module_path);

    std::fs::read(module_path)
}

/// Default entrypoint name for the module under test.
const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main";
/// Default URI that the tests expect to find a live Runtime at.
const RUNTIME_URI: &str = "https://localhost:8080";

const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml";
const MODULE_WASM_SUFFIX: &str = ".wasm";

// Retry parameters when connecting to a gRPC server.
const RETRY_COUNT: u32 = 360;
const RETRY_INTERVAL: std::time::Duration = std::time::Duration::from_millis(500);

/// Convenience helper to build and run a single-Node Application with the given module name, using
/// the default name "oak_main" for its entrypoint.
pub fn run_single_module_default(
    module_config_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME)
}

/// Convenience helper to build and run a single-Node application with the given module name, using
/// the provided entrypoint name.
pub fn run_single_module(
    module_config_name: &str,
    entrypoint_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    let combined_config = runtime_config(module_config_name, entrypoint_name);
    oak_runtime::configure_and_run(combined_config)
}

/// Build the configuration needed to launch a test Runtime instance that
/// runs a single-Node application with the given module name and entrypoint.
pub fn runtime_config(
    module_config_name: &str,
    entrypoint_name: &str,
) -> oak_runtime::RuntimeConfiguration {
    let wasm: HashMap<String, Vec<u8>> = [(
        module_config_name.to_owned(),
        compile_rust_wasm(
            DEFAULT_MODULE_MANIFEST,
            &(module_config_name.to_owned() + MODULE_WASM_SUFFIX),
        )
        .expect("failed to build wasm module"),
    )]
    .iter()
    .cloned()
    .collect();

    runtime_config_wasm(wasm, module_config_name, entrypoint_name)
}

/// Build the configuration needed to launch a test Runtime instance that runs the given
/// collection of Wasm modules, starting with the given module name and entrypoint.
pub fn runtime_config_wasm(
    wasm_modules: HashMap<String, Vec<u8>>,
    module_config_name: &str,
    entrypoint_name: &str,
) -> oak_runtime::RuntimeConfiguration {
    oak_runtime::RuntimeConfiguration {
        metrics_port: Some(9090),
        introspect_port: Some(1909),
        grpc_config: oak_runtime::GrpcConfiguration {
            grpc_server_tls_identity: Some(Identity::from_pem(
                include_str!("../certs/local.pem"),
                include_str!("../certs/local.key"),
            )),
            grpc_client_root_tls_certificate: Some(
                oak_runtime::config::load_certificate(&include_str!("../certs/ca.pem")).unwrap(),
            ),
            oidc_client_info: None,
        },
        app_config: ApplicationConfiguration {
            wasm_modules,
            initial_node_configuration: Some(NodeConfiguration {
                name: "test".to_string(),
                config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                    wasm_module_name: module_config_name.to_string(),
                    wasm_entrypoint_name: entrypoint_name.to_string(),
                })),
            }),
        },
    }
}

/// Build a channel and interceptor suitable for building a client that connects
/// to a Runtime under test.
pub async fn channel_and_interceptor() -> (Channel, impl Into<tonic::Interceptor>) {
    // Build a channel that connects to the Runtime under test.
    let uri = RUNTIME_URI.parse().expect("Error parsing URI");
    let tls_config = ClientTlsConfig::new()
        .ca_certificate(Certificate::from_pem(include_str!("../certs/ca.pem")));
    let builder = Channel::builder(uri).tls_config(tls_config);

    // The Runtime may have just been started for a test, and make take some time
    // to come fully up, start a gRPC server and accept connections. Allow for
    // this by retrying at intervals until the server responds or we hit a retry
    // limit.
    let mut retries = 0;
    let channel;
    loop {
        match builder.connect().await {
            Ok(c) => {
                debug!("Connected to gRPC server");
                channel = c;
                break;
            }
            Err(err) => {
                if retries < RETRY_COUNT {
                    debug!("Failed to connect, try again momentarily: {:?}", err);
                    retries += 1;
                    std::thread::sleep(RETRY_INTERVAL);
                } else {
                    panic!("Failed to connect, last err: {:?}", err);
                }
            }
        }
    }

    // Build an interceptor that will attach a public-untrusted Oak label to
    // every gRPC request.
    let mut label = Vec::new();
    Label::public_untrusted()
        .encode(&mut label)
        .expect("Error encoding label");
    let interceptor = move |mut request: Request<()>| {
        request.metadata_mut().insert_bin(
            oak_abi::OAK_LABEL_GRPC_METADATA_KEY,
            MetadataValue::from_bytes(label.as_ref()),
        );
        Ok(request)
    };

    (channel, interceptor)
}
<|endoftext|>


<commit_before>use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg};
use rustc_hir::{GenericArg, Local, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `&Option<&T>`.
    ///
    /// **Why is this bad?** Since `&` is Copy, it's useless to have a
    /// reference on `Option<&T>`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// // example code where clippy issues a warning
    /// let x: &Option<&u32> = &Some(&0u32);
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // example code which does not raise clippy warning
    /// let x: Option<&u32> = Some(&0u32);
    /// ```
    pub REF_OPTION_REF,
    style,
    "use `Option<&T>` instead of `&Option<&T>`"
}

declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);

impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        self.check_ref_option_ref(cx, ty);
    }
}

impl RefOptionRef {
    fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if_chain! {
            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
            if mut_ty.mutbl == Mutability::Not;
            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
            let last = last_path_segment(qpath);
            if let Some(res) = last.res;
            if let Some(def_id) = res.opt_def_id();

            if match_def_path(cx, def_id, &paths::OPTION);
            if let Some(ref params) = last_path_segment(qpath).args ;
            if !params.parenthesized;
            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
                GenericArg::Type(inner_ty) => Some(inner_ty),
                _ => None,
            });
            if let TyKind::Rptr(_, _) = inner_ty.kind;

            then {
                span_lint_and_sugg(
                    cx,
                    REF_OPTION_REF,
                    ty.span,
                    "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>",
                    "try",
                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
                    Applicability::Unspecified,
                );
            }
        }
    }
}
<commit_msg>clippy_lint: Add 'ref_option_ref' remove unused import<commit_after>use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg};
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

use if_chain::if_chain;
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for usage of `&Option<&T>`.
    ///
    /// **Why is this bad?** Since `&` is Copy, it's useless to have a
    /// reference on `Option<&T>`.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust,ignore
    /// // example code where clippy issues a warning
    /// let x: &Option<&u32> = &Some(&0u32);
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // example code which does not raise clippy warning
    /// let x: Option<&u32> = Some(&0u32);
    /// ```
    pub REF_OPTION_REF,
    style,
    "use `Option<&T>` instead of `&Option<&T>`"
}

declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);

impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        self.check_ref_option_ref(cx, ty);
    }
}

impl RefOptionRef {
    fn check_ref_option_ref(&self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if_chain! {
            if let TyKind::Rptr(_, ref mut_ty) = ty.kind;
            if mut_ty.mutbl == Mutability::Not;
            if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
            let last = last_path_segment(qpath);
            if let Some(res) = last.res;
            if let Some(def_id) = res.opt_def_id();

            if match_def_path(cx, def_id, &paths::OPTION);
            if let Some(ref params) = last_path_segment(qpath).args ;
            if !params.parenthesized;
            if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
                GenericArg::Type(inner_ty) => Some(inner_ty),
                _ => None,
            });
            if let TyKind::Rptr(_, _) = inner_ty.kind;

            then {
                span_lint_and_sugg(
                    cx,
                    REF_OPTION_REF,
                    ty.span,
                    "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>",
                    "try",
                    format!("Option<{}>", &snippet(cx, inner_ty.span, "..")),
                    Applicability::Unspecified,
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>use system::*;
use gpu::gpu_registers::{GPU_Registers, ShadeProfile};
use image::ImageBuffer;
use image::{RgbaImage, Rgba};

pub struct TileMap
{
    pub tiles: Vec<TileData>,
    pub map: Vec<u8>,
}

impl TileMap {
     pub fn new() -> TileMap
     {
         return TileMap
         {
             tiles: vec![TileData::new();256],
             map: vec![0, 1024],
         }
     }

     pub fn populate_tile_map(&mut self, system_data_original: &mut SystemData, lcdc_data_select: bool, lcdc_display_select: bool)
     {
        let mut system_data = system_data_original;
        for tile_index in 0..self.tiles.len()
        {
            self.tiles[tile_index] = get_tile_data(tile_index as u8, &mut system_data, lcdc_data_select);
        }

        self.map = self.vectorize_map(&mut system_data, lcdc_display_select);
     }

     fn vectorize_map(&mut self, system_data: &mut SystemData, lcdc_display_select: bool) -> Vec<u8>
     {
        let mut map: Vec<u8> = vec![0;1024];
        let mut map_offset: u16 = 0;
        if lcdc_display_select
        {
            map_offset = 0x9C00;
        }
        else {
            map_offset = 0x9800;
        }

        for i in 0..map.len()
        {
            map[i] = system_data.mmu.mem_map[map_offset as usize + i];
        }

        return map
     }
}

#[derive(Clone)]
pub struct SpriteAttribute
{
    pub y_position: u8,
    pub x_position: u8,
    pub tile_number: u8,
    pub flags: u8,
}

impl SpriteAttribute {
    pub fn new() -> SpriteAttribute
    {
        return SpriteAttribute
        {
            y_position: 0,
            x_position: 0,
            tile_number: 0,
            flags: 0,
        }
    }

    pub fn update_sprite(&mut self, y_position: u8, x_position: u8, tile_number: u8, flags: u8)
    {
        self.y_position = y_position;
        self.x_position = x_position;
        self.tile_number = tile_number;
        self.flags = flags;
    }
}

pub struct OAM_Table
{
    pub table: Vec<SpriteAttribute>,
}

impl OAM_Table {
    pub fn new() -> OAM_Table
    {
        return OAM_Table
        {
            table: vec![SpriteAttribute::new();40],
        }
    }

    pub fn populate_oam_table(&mut self, system_data: &SystemData)
    {
        for i in 0..40
        {
            let mut sprite_attribute = SpriteAttribute::new();
            let y_position = system_data.mmu.mem_map[0xFE00 + (i * 4)];
            let x_position = system_data.mmu.mem_map[0xFE01 + (i * 4)];
            let tile_number = system_data.mmu.mem_map[0xFE02 + (i * 4)];
            let flags = system_data.mmu.mem_map[0xFE03 + (i * 4)];
            sprite_attribute.update_sprite(y_position, x_position, tile_number, flags);
            self.table[i] = sprite_attribute;
        }
    }
}

#[derive(Clone)]
pub struct TileData
{
    pub data: Vec<u8>,
}

impl TileData
{
    pub fn new() -> TileData
    {
        return TileData
        {
            data: vec![0; 64],
        }
    }
}

pub fn get_tile_data(tile_index: u8, system_data: &mut SystemData, lcdc_data_select: bool) -> TileData
{
    let mut tile_data = TileData::new();
    let mut vram_offset: u16 = 0;

    if lcdc_data_select
    {
        vram_offset = 0x8000;
    }
    else {
        vram_offset = 0x8800
    }

    let mem_loc: u16 = vram_offset + (tile_index as u16 * 16);
    for byte_pair in 0..8
    {
        for bit in 0..8
        {
            let upper_bit: u8 = (system_data.mmu.mem_map[(mem_loc + (2 * byte_pair as u16)) as usize + 1] >> (7 - bit)) & 0x01;
            let lower_bit: u8 = (system_data.mmu.mem_map[(mem_loc + (2 * byte_pair as u16)) as usize] >> (7 - bit)) & 0x01;
            tile_data.data[(byte_pair as usize * 8) + bit as usize] = (upper_bit << 1) | lower_bit;
        }
    }
    return tile_data;
}

pub fn update_gpu(system_data_original: &mut SystemData, registers_original: &mut Registers, gpu_registers_original: &mut GPU_Registers)
{
    let mut system_data = system_data_original;
    let mut registers = registers_original;
    let mut gpu_registers = gpu_registers_original;
    LCD_Y_Coordinate_Update(&mut system_data, &mut gpu_registers);
    gpu_registers.lcdc_register.update_lcdc_register(&system_data);
    gpu_registers.lcdc_status.update_lcdc_status(&system_data);
    gpu_registers.lcd_position.update(&mut system_data, gpu_registers.lcdc_status.lyc_ly_coincidence_interrupt);
}

pub fn LCD_Y_Coordinate_Update(system_data_original: &mut SystemData, gpu_registers: &mut GPU_Registers)
{
    let mut system_data = system_data_original;
    gpu_registers.lcd_position.ly_register.add_cycles(&system_data);
    let tick_flag = gpu_registers.lcd_position.ly_register.add_sub_cycles(&system_data);
    if tick_flag
    {
        let reset_flag = gpu_registers.lcd_position.ly_register.tick(&mut system_data);
        let ly_value = gpu_registers.lcd_position.ly_register.value;
        if ly_value == 144
        {
            gpu_registers.v_blank = true;
            system_data.mmu.mem_map[0xFF0F] |= 0x01;
            gpu_registers.v_blank_draw_flag = true;
        }
        else if ly_value < 144
        {
            gpu_registers.lcd_position.scroll_x_buffer[ly_value as usize] = gpu_registers.lcd_position.scroll_x;
            gpu_registers.lcd_position.scroll_y_buffer[ly_value as usize] = gpu_registers.lcd_position.scroll_y;
        }
        if reset_flag
        {
            gpu_registers.v_blank = false;
            system_data.mmu.mem_map[0xFF0F] &= 0xFE;
        }
    }
}

pub fn create_background_img(background_tile_map: &TileMap, gpu_registers: &GPU_Registers, system_data: &SystemData, oam_table: &OAM_Table, oam_tile_map: &TileMap) -> RgbaImage
{
    //Technically switchable by the row, will implement later
    let palette_data = system_data.mmu.mem_map[0xFF47];
    let object_palette_0 = system_data.mmu.mem_map[0xFF48];
    let object_palette_1 = system_data.mmu.mem_map[0xFF49];
    let mut image_buffer = ImageBuffer::new(160, 144);
    let mut background_buffer = build_background_bitmap(background_tile_map, gpu_registers.lcdc_register.tile_data);
    if gpu_registers.lcdc_register.sprite_enable
    {
        background_buffer = apply_oam_table_to_bitmap(&oam_table, background_buffer, object_palette_0, object_palette_1, gpu_registers.lcdc_register.sprite_size, &oam_tile_map);
    }
    for row_y in 0..144
    {
        for row_x in 0..160
        {
           let mut row_x_scrolled = (row_x + gpu_registers.lcd_position.scroll_x_buffer[row_y] as usize) % 256;
           let mut row_y_scrolled = (row_y + gpu_registers.lcd_position.scroll_y_buffer[row_y] as usize) % 256;
           let pixel_data = background_buffer[(row_y_scrolled * 256) + row_x_scrolled];
           let pixel = pixel_color_map(pixel_data, palette_data, &gpu_registers.shade_profile);
           image_buffer.put_pixel(row_x as u32, row_y as u32, pixel);
        }
    }
   return image_buffer;
}

fn build_background_bitmap(background_tile_map: &TileMap, tile_data_select: bool) -> Vec<u8>
{

    let mut buffer = vec![0; 0x10000];
    for tile_y in 0..32
    {
        for tile_x in 0..32
        {
            for pixel_y in 0..8
            {
                for pixel_x in 0..8
                {
                    let mut tile = background_tile_map.map[(tile_y * 32) + tile_x];
                    if !tile_data_select
                    {
                        let tile_temp = tile as i8 as i16;
                        tile = (tile_temp + 0x80) as u8;
                    }
                    let pixel_data = background_tile_map.tiles[tile as usize].data[(pixel_y * 8) + pixel_x];
                    buffer[(256 * ((tile_y * 8) + pixel_y)) + ((tile_x * 8) + pixel_x)] = pixel_data;
                }
            }
        }
    }
    return buffer;
}

fn apply_oam_table_to_bitmap(oam_table: &OAM_Table, bitmap: Vec<u8>, palette_0: u8, palette_1: u8, large_sprites: bool, tile_map: &TileMap) -> Vec<u8>
{
    let mut tile = TileData::new();
    let mut tile_big = TileData::new();
    for i in 0..oam_table.table.len()
    {
        let start_y_bit = oam_table.table[i].y_position as i16 - 16;
        let start_x_bit = oam_table.table[i].x_position as i16 - 8;
        if !large_sprites
        {
            // tile = tile_map.tiles[oam_table.table[i].tile_number as usize];
        }
        else
        {
            // tile = tile_map.tiles[oam_table.table[i].tile_number as usize & 0xFE];
            // tile_big = tile_map.tiles[oam_table.table[i].tile_number as usize | 0x01];
        }
    }
    return bitmap;
}

fn pixel_color_map(pixel_data: u8, palette_data: u8, shade_profile: &ShadeProfile) -> Rgba<u8>
{
    let mut pixel_shade = 0;
    match pixel_data
    {
        0 => pixel_shade = palette_data & 0x03,
        1 => pixel_shade = (palette_data & 0x0C) >> 2,
        2 => pixel_shade = (palette_data & 0x30) >> 4,
        3 => pixel_shade = (palette_data & 0xC0) >> 6,
        _ => (),
    }

    match pixel_shade
    {
        0 => return shade_profile.shade_0,
        1 => return shade_profile.shade_1,
        2 => return shade_profile.shade_2,
        3 => return shade_profile.shade_3,
        _ => return shade_profile.default,
    }
}

#[cfg(test)]
mod gpu_tests
{
    use system::get_system_data;
    use gpu::gpu::*;

    #[test]
    fn get_tile_data_test() {
        let mut system_data : SystemData = get_system_data(&String::from("CLASSIC"));

        let temp_memory_values : Vec<u8> = vec![0b00110011, 0b00001111, 0b11001100, 0b11110000];

        for vram_offset in vec![0x8000, 0x8800].iter()
        {
            for i in 0..temp_memory_values.len()
            {
                system_data.mmu.mem_map[*vram_offset + i] = temp_memory_values[i];
            }
        }
        let tiles = vec![get_tile_data(0, &mut system_data, false), get_tile_data(0, &mut system_data, true)];
        for tile in tiles.iter()
        {
            for i in 0..4
            {
                assert_eq!(tile.data[i as usize * 2], i);
                assert_eq!(tile.data[(i as usize * 2) + 1 ], i);
                assert_eq!(tile.data[8 + (i as usize * 2)], 3 - i);
                assert_eq!(tile.data[(8 + (i as usize * 2)) + 1], 3 - i);
            }
        }
    }

    #[test]
    fn get_tile_map_test() {
        let mut system_data : SystemData = get_system_data(&String::from("CLASSIC"));
        let mut TileMap = TileMap::new();

        let memory_values: Vec<u8> = vec![0x00, 0xFF];
        let test_values: Vec<u8> = vec![0, 3];
        let tiles_offset: Vec<usize> = vec![0x8800, 0x8000];
        let bools: Vec<bool> = vec![false, true];

        for offset_index in 0..tiles_offset.len()
        {
            for index in 0..test_values.len()
            {
                for i in 0..0x1000
                {
                    system_data.mmu.mem_map[tiles_offset[offset_index] + i] = memory_values[index];
                }

                TileMap.populate_tile_map(&mut system_data, bools[offset_index], true);

                for i in 0..TileMap.tiles.len()
                {
                    for j in 0..64
                    {
                        assert_eq!(TileMap.tiles[i].data[j], test_values[index]);
                    }
                }
            }
        }

        let display_offset: Vec<usize> = vec![0x9800, 0x9C00];

        for offset_index in 0..display_offset.len()
        {
            for value_index in 0..memory_values.len()
            {
                for i in 0..TileMap.map.len()
                {
                    system_data.mmu.mem_map[display_offset[offset_index] + i] = memory_values[value_index];
                }

                TileMap.populate_tile_map(&mut system_data, false, bools[offset_index]);

                for i in 0..TileMap.map.len()
                {
                    assert_eq!(TileMap.map[i], memory_values[value_index]);
                }
            }
        }

    }

}
<commit_msg>More OAM building and modularizing pixel color code<commit_after>use system::*;
use gpu::gpu_registers::{GPU_Registers, ShadeProfile};
use image::ImageBuffer;
use image::{RgbaImage, Rgba};

pub struct TileMap
{
    pub tiles: Vec<TileData>,
    pub map: Vec<u8>,
}

impl TileMap {
     pub fn new() -> TileMap
     {
         return TileMap
         {
             tiles: vec![TileData::new();256],
             map: vec![0, 1024],
         }
     }

     pub fn populate_tile_map(&mut self, system_data_original: &mut SystemData, lcdc_data_select: bool, lcdc_display_select: bool)
     {
        let mut system_data = system_data_original;
        for tile_index in 0..self.tiles.len()
        {
            self.tiles[tile_index] = get_tile_data(tile_index as u8, &mut system_data, lcdc_data_select);
        }

        self.map = self.vectorize_map(&mut system_data, lcdc_display_select);
     }

     fn vectorize_map(&mut self, system_data: &mut SystemData, lcdc_display_select: bool) -> Vec<u8>
     {
        let mut map: Vec<u8> = vec![0;1024];
        let mut map_offset: u16 = 0;
        if lcdc_display_select
        {
            map_offset = 0x9C00;
        }
        else {
            map_offset = 0x9800;
        }

        for i in 0..map.len()
        {
            map[i] = system_data.mmu.mem_map[map_offset as usize + i];
        }

        return map
     }
}

#[derive(Clone)]
pub struct SpriteAttribute
{
    pub y_position: u8,
    pub x_position: u8,
    pub tile_number: u8,
    pub flags: u8,
}

impl SpriteAttribute {
    pub fn new() -> SpriteAttribute
    {
        return SpriteAttribute
        {
            y_position: 0,
            x_position: 0,
            tile_number: 0,
            flags: 0,
        }
    }

    pub fn update_sprite(&mut self, y_position: u8, x_position: u8, tile_number: u8, flags: u8)
    {
        self.y_position = y_position;
        self.x_position = x_position;
        self.tile_number = tile_number;
        self.flags = flags;
    }
}

pub struct OAM_Table
{
    pub table: Vec<SpriteAttribute>,
}

impl OAM_Table {
    pub fn new() -> OAM_Table
    {
        return OAM_Table
        {
            table: vec![SpriteAttribute::new();40],
        }
    }

    pub fn populate_oam_table(&mut self, system_data: &SystemData)
    {
        for i in 0..40
        {
            let mut sprite_attribute = SpriteAttribute::new();
            let y_position = system_data.mmu.mem_map[0xFE00 + (i * 4)];
            let x_position = system_data.mmu.mem_map[0xFE01 + (i * 4)];
            let tile_number = system_data.mmu.mem_map[0xFE02 + (i * 4)];
            let flags = system_data.mmu.mem_map[0xFE03 + (i * 4)];
            sprite_attribute.update_sprite(y_position, x_position, tile_number, flags);
            self.table[i] = sprite_attribute;
        }
    }
}

#[derive(Clone)]
pub struct TileData
{
    pub data: Vec<u8>,
}

impl TileData
{
    pub fn new() -> TileData
    {
        return TileData
        {
            data: vec![0; 64],
        }
    }

}

pub fn get_tile_data(tile_index: u8, system_data: &mut SystemData, lcdc_data_select: bool) -> TileData
{
    let mut tile_data = TileData::new();
    let mut vram_offset: u16 = 0;

    if lcdc_data_select
    {
        vram_offset = 0x8000;
    }
    else {
        vram_offset = 0x8800
    }

    let mem_loc: u16 = vram_offset + (tile_index as u16 * 16);
    for byte_pair in 0..8
    {
        for bit in 0..8
        {
            let upper_bit: u8 = (system_data.mmu.mem_map[(mem_loc + (2 * byte_pair as u16)) as usize + 1] >> (7 - bit)) & 0x01;
            let lower_bit: u8 = (system_data.mmu.mem_map[(mem_loc + (2 * byte_pair as u16)) as usize] >> (7 - bit)) & 0x01;
            tile_data.data[(byte_pair as usize * 8) + bit as usize] = (upper_bit << 1) | lower_bit;
        }
    }
    return tile_data;
}

pub fn update_gpu(system_data_original: &mut SystemData, registers_original: &mut Registers, gpu_registers_original: &mut GPU_Registers)
{
    let mut system_data = system_data_original;
    let mut registers = registers_original;
    let mut gpu_registers = gpu_registers_original;
    LCD_Y_Coordinate_Update(&mut system_data, &mut gpu_registers);
    gpu_registers.lcdc_register.update_lcdc_register(&system_data);
    gpu_registers.lcdc_status.update_lcdc_status(&system_data);
    gpu_registers.lcd_position.update(&mut system_data, gpu_registers.lcdc_status.lyc_ly_coincidence_interrupt);
}

pub fn LCD_Y_Coordinate_Update(system_data_original: &mut SystemData, gpu_registers: &mut GPU_Registers)
{
    let mut system_data = system_data_original;
    gpu_registers.lcd_position.ly_register.add_cycles(&system_data);
    let tick_flag = gpu_registers.lcd_position.ly_register.add_sub_cycles(&system_data);
    if tick_flag
    {
        let reset_flag = gpu_registers.lcd_position.ly_register.tick(&mut system_data);
        let ly_value = gpu_registers.lcd_position.ly_register.value;
        if ly_value == 144
        {
            gpu_registers.v_blank = true;
            system_data.mmu.mem_map[0xFF0F] |= 0x01;
            gpu_registers.v_blank_draw_flag = true;
        }
        else if ly_value < 144
        {
            gpu_registers.lcd_position.scroll_x_buffer[ly_value as usize] = gpu_registers.lcd_position.scroll_x;
            gpu_registers.lcd_position.scroll_y_buffer[ly_value as usize] = gpu_registers.lcd_position.scroll_y;
        }
        if reset_flag
        {
            gpu_registers.v_blank = false;
            system_data.mmu.mem_map[0xFF0F] &= 0xFE;
        }
    }
}

pub fn create_background_img(background_tile_map: &TileMap, gpu_registers: &GPU_Registers, system_data: &SystemData, oam_table: &OAM_Table, oam_tile_map: &TileMap) -> RgbaImage
{
    //Technically switchable by the row, will implement later
    let palette_data = system_data.mmu.mem_map[0xFF47];
    let object_palette_0 = system_data.mmu.mem_map[0xFF48];
    let object_palette_1 = system_data.mmu.mem_map[0xFF49];
    let mut image_buffer = ImageBuffer::new(160, 144);
    let mut background_buffer = build_background_bitmap(background_tile_map, gpu_registers.lcdc_register.tile_data, palette_data);
    //Convert scroll
    if gpu_registers.lcdc_register.sprite_enable
    {
        background_buffer = apply_oam_table_to_bitmap(&oam_table, background_buffer, object_palette_0, object_palette_1, gpu_registers.lcdc_register.sprite_size, &oam_tile_map);
    }
    for row_y in 0..144
    {
        for row_x in 0..160
        {
           let mut row_x_scrolled = (row_x + gpu_registers.lcd_position.scroll_x_buffer[row_y] as usize) % 256;
           let mut row_y_scrolled = (row_y + gpu_registers.lcd_position.scroll_y_buffer[row_y] as usize) % 256;
           let pixel_data = background_buffer[(row_y_scrolled * 256) + row_x_scrolled];
           //let pixel_shade = pixel_shade_map(pixel_data, palette_data);
           let pixel = pixel_color_map(pixel_data, &gpu_registers.shade_profile);
           image_buffer.put_pixel(row_x as u32, row_y as u32, pixel);
        }
    }
   return image_buffer;
}

fn build_background_bitmap(background_tile_map: &TileMap, tile_data_select: bool, palette_data:u8) -> Vec<u8>
{

    let mut buffer = vec![0; 0x10000];
    for tile_y in 0..32
    {
        for tile_x in 0..32
        {
            for pixel_y in 0..8
            {
                for pixel_x in 0..8
                {
                    let mut tile = background_tile_map.map[(tile_y * 32) + tile_x];
                    if !tile_data_select
                    {
                        let tile_temp = tile as i8 as i16;
                        tile = (tile_temp + 0x80) as u8;
                    }
                    let mut pixel_data = background_tile_map.tiles[tile as usize].data[(pixel_y * 8) + pixel_x];
                    pixel_data = pixel_shade_map(pixel_data, palette_data);
                    buffer[(256 * ((tile_y * 8) + pixel_y)) + ((tile_x * 8) + pixel_x)] = pixel_data;
                }
            }
        }
    }
    return buffer;
}

fn apply_oam_table_to_bitmap(oam_table: &OAM_Table, bitmap: Vec<u8>, palette_0: u8, palette_1: u8, large_sprites: bool, tile_map: &TileMap) -> Vec<u8>
{
    let mut tile = TileData::new();
    let mut tile_big = TileData::new();
    for i in 0..oam_table.table.len()
    {
        let y_position = oam_table.table[i].y_position as i16 - 16;
        let x_position = oam_table.table[i].x_position as i16 - 8;
        let tile_num = oam_table.table[i].tile_number as usize;
        if !large_sprites
        {
            tile = tile_map.tiles[tile_num].clone();
        }
        else
        {
            tile = tile_map.tiles[tile_num & 0xFE].clone();
            tile_big = tile_map.tiles[tile_num | 0x01].clone();
        }

        let flags = oam_table.table[i].flags;
        let y_flip = (flags) & 0x40 >> 6;
        let x_flip = (flags) & 0x20 >> 5;
        let palette = (flags) & 0x10 >> 4;

        // if !large_sprites
        // {
        //     if y_flip == 1
        //     {
        //         for row in 0..4
        //         {
        //             for index in 0..8
        //             {
        //                 let temp = tile.data[(row * 8) + index];
        //                 tile.data[(row * 8) + index] = tile.data[]
        //             }
        //         }
        //     }
        // }

        //Add to bitmap
        for row in 0..8
        {
            for index in 0..8
            {
                if (y_position + row) >= 0
                {
                    if (x_position + index) >= 0
                    {
                        let tile_position = ((y_position + row) * 256) + (x_position + index);
                        //Translate pallete to bitmap
                    }
                }
            }
        }
    }
    return bitmap;
}

//Split into two
fn pixel_shade_map(pixel_data: u8, palette_data: u8,) -> u8
{
    let mut pixel_shade = 0;
    match pixel_data
    {
        0 => return palette_data & 0x03,
        1 => return (palette_data & 0x0C) >> 2,
        2 => return (palette_data & 0x30) >> 4,
        3 => return (palette_data & 0xC0) >> 6,
        _ => return 4,
    }
}

fn pixel_color_map(pixel_shade: u8, shade_profile: &ShadeProfile) -> Rgba<u8>
{
    match pixel_shade
    {
        0 => return shade_profile.shade_0,
        1 => return shade_profile.shade_1,
        2 => return shade_profile.shade_2,
        3 => return shade_profile.shade_3,
        _ => return shade_profile.default,
    }
}

#[cfg(test)]
mod gpu_tests
{
    use system::get_system_data;
    use gpu::gpu::*;

    #[test]
    fn get_tile_data_test() {
        let mut system_data : SystemData = get_system_data(&String::from("CLASSIC"));

        let temp_memory_values : Vec<u8> = vec![0b00110011, 0b00001111, 0b11001100, 0b11110000];

        for vram_offset in vec![0x8000, 0x8800].iter()
        {
            for i in 0..temp_memory_values.len()
            {
                system_data.mmu.mem_map[*vram_offset + i] = temp_memory_values[i];
            }
        }
        let tiles = vec![get_tile_data(0, &mut system_data, false), get_tile_data(0, &mut system_data, true)];
        for tile in tiles.iter()
        {
            for i in 0..4
            {
                assert_eq!(tile.data[i as usize * 2], i);
                assert_eq!(tile.data[(i as usize * 2) + 1 ], i);
                assert_eq!(tile.data[8 + (i as usize * 2)], 3 - i);
                assert_eq!(tile.data[(8 + (i as usize * 2)) + 1], 3 - i);
            }
        }
    }

    #[test]
    fn get_tile_map_test() {
        let mut system_data : SystemData = get_system_data(&String::from("CLASSIC"));
        let mut TileMap = TileMap::new();

        let memory_values: Vec<u8> = vec![0x00, 0xFF];
        let test_values: Vec<u8> = vec![0, 3];
        let tiles_offset: Vec<usize> = vec![0x8800, 0x8000];
        let bools: Vec<bool> = vec![false, true];

        for offset_index in 0..tiles_offset.len()
        {
            for index in 0..test_values.len()
            {
                for i in 0..0x1000
                {
                    system_data.mmu.mem_map[tiles_offset[offset_index] + i] = memory_values[index];
                }

                TileMap.populate_tile_map(&mut system_data, bools[offset_index], true);

                for i in 0..TileMap.tiles.len()
                {
                    for j in 0..64
                    {
                        assert_eq!(TileMap.tiles[i].data[j], test_values[index]);
                    }
                }
            }
        }

        let display_offset: Vec<usize> = vec![0x9800, 0x9C00];

        for offset_index in 0..display_offset.len()
        {
            for value_index in 0..memory_values.len()
            {
                for i in 0..TileMap.map.len()
                {
                    system_data.mmu.mem_map[display_offset[offset_index] + i] = memory_values[value_index];
                }

                TileMap.populate_tile_map(&mut system_data, false, bools[offset_index]);

                for i in 0..TileMap.map.len()
                {
                    assert_eq!(TileMap.map[i], memory_values[value_index]);
                }
            }
        }

    }

}
<|endoftext|>