<commit_before>use std::io::{self,ErrorKind,Read,Write};
use std::net::{SocketAddr,SocketAddrV4,SocketAddrV6};
use mio::tcp::{TcpListener,TcpStream};
use rustls::{ServerSession, Session};
use net2::TcpBuilder;
use net2::unix::UnixTcpBuilderExt;
#[cfg(feature = "use_openssl")]
use openssl::ssl::{Error, SslStream};

#[derive(Debug,PartialEq,Copy,Clone)]
pub enum SocketResult {
  Continue,
  WouldBlock,
  Error
}

pub trait SocketHandler {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult);
  fn socket_write(&mut self, buf: &[u8])    -> (usize, SocketResult);
  fn socket_ref(&self) -> &TcpStream;
}

impl SocketHandler for TcpStream {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.read(&mut buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size +=sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => return (size, SocketResult::WouldBlock),
          ErrorKind::BrokenPipe => {
            error!("SOCKET\tbroken pipe reading from the socket");
            return (size, SocketResult::Error)
          },
          _ => {
            error!("SOCKET\tsocket_read error={:?}", e);
            return (size, SocketResult::Error)
          },
        }
      }
    }
  }

  fn socket_write(&mut self,  buf: &[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.write(&buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size += sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => return (size, SocketResult::WouldBlock),
          ErrorKind::BrokenPipe => {
            error!("SOCKET\tbroken pipe writing to the socket");
            return (size, SocketResult::Error)
          },
          _ => {
            //FIXME: timeout and other common errors should be sent up
            error!("SOCKET\tsocket_write error={:?}", e);
            return (size, SocketResult::Error)
          },
        }
      }
    }
  }

  fn socket_ref(&self) -> &TcpStream { self }
}

#[cfg(feature = "use_openssl")]
impl SocketHandler for SslStream<TcpStream> {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.ssl_read(&mut buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size += sz,
        Err(Error::WantRead(_))  => return (size, SocketResult::WouldBlock),
        Err(Error::WantWrite(_)) => return (size, SocketResult::WouldBlock),
        Err(Error::Stream(e))    => {
          error!("SOCKET-TLS\treadable TLS socket err={:?}", e);
          return (size, SocketResult::Error)
        },
        _ => return (size, SocketResult::Error)
      }
    }
  }

  fn socket_write(&mut self,  buf: &[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.ssl_write(&buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size +=sz,
        Err(Error::WantRead(_))  => return (size, SocketResult::WouldBlock),
        Err(Error::WantWrite(_)) => return (size, SocketResult::WouldBlock),
        Err(Error::Stream(e))    => {
          error!("SOCKET-TLS\twritable TLS socket err={:?}", e);
          return (size, SocketResult::Error)
        },
        e => {
          error!("SOCKET-TLS\twritable TLS socket err={:?}", e);
          return (size, SocketResult::Error)
        }
      }
    }
  }

  fn socket_ref(&self) -> &TcpStream { self.get_ref() }
}

pub struct FrontRustls {
  pub stream:  TcpStream,
  pub session: ServerSession,
}

impl SocketHandler for FrontRustls {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    let mut can_read = true;

    while self.session.wants_read() && can_read {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }

      match self.session.read_tls(&mut self.stream) {
        Ok(0)  => can_read = false,
        Ok(sz) => {},
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => can_read = false,
           _ => {
             error!("could not read TLS stream from socket: {:?}", e);
            return (size, SocketResult::Error)
           }
        }
      }

      if let Err(e) = self.session.process_new_packets() {
        error!("could not process read TLS packets: {:?}", e);
        return (size, SocketResult::Error);
      }

      match self.session.read(buf) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size += sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => return (size, SocketResult::WouldBlock),
           _ => {
             error!("could not read data from TLS stream: {:?}", e);
            return (size, SocketResult::Error)
           }
        }

      }
    }

    if can_read {
    (size, SocketResult::Continue)
    } else {
    (size, SocketResult::WouldBlock)
    }
  }

  fn socket_write(&mut self,  buf: &[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    let mut can_write = true;

    while self.session.wants_write() && can_write {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }

      match self.session.write(buf) {
        Ok(0)  => { },
        Ok(sz) => size += sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => {
            // we don't need to do anything, the session will return false in wants_write?
          },
           _ => {
             error!("could not write data to TLS stream: {:?}", e);
            return (size, SocketResult::Error)
           }
        }
      }

      match self.session.write_tls(&mut self.stream) {
        Ok(0)  => can_write = false,
        Ok(sz) => {},
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => can_write = false,
           _ => {
             error!("could not write TLS stream to socket: {:?}", e);
            return (size, SocketResult::Error)
           }
        }
      }

    }

    if can_write {
    (size, SocketResult::Continue)
    } else {
    (size, SocketResult::WouldBlock)
    }
  }

  fn socket_ref(&self) -> &TcpStream { &self.stream }
}

pub fn server_bind(addr: &SocketAddr) -> io::Result<TcpListener> {
  let sock = try!(match *addr {
    SocketAddr::V4(..) => TcpBuilder::new_v4(),
    SocketAddr::V6(..) => TcpBuilder::new_v6(),
  });

  // set so_reuseaddr, but only on unix (mirrors what libstd does)
  if cfg!(unix) {
    try!(sock.reuse_address(true));
  }

  try!(sock.reuse_port(true));

  // bind the socket
  try!(sock.bind(addr));

  // listen
  let listener = try!(sock.listen(1024));
  TcpListener::from_listener(listener, addr)
}

<commit_msg>fix rustls socket implementation<commit_after>use std::io::{self,ErrorKind,Read,Write};
use std::net::{SocketAddr,SocketAddrV4,SocketAddrV6};
use mio::tcp::{TcpListener,TcpStream};
use rustls::{ServerSession, Session};
use net2::TcpBuilder;
use net2::unix::UnixTcpBuilderExt;
#[cfg(feature = "use_openssl")]
use openssl::ssl::{Error, SslStream};

#[derive(Debug,PartialEq,Copy,Clone)]
pub enum SocketResult {
  Continue,
  WouldBlock,
  Error
}

pub trait SocketHandler {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult);
  fn socket_write(&mut self, buf: &[u8])    -> (usize, SocketResult);
  fn socket_ref(&self) -> &TcpStream;
}

impl SocketHandler for TcpStream {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.read(&mut buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size +=sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => return (size, SocketResult::WouldBlock),
          ErrorKind::BrokenPipe => {
            error!("SOCKET\tbroken pipe reading from the socket");
            return (size, SocketResult::Error)
          },
          _ => {
            error!("SOCKET\tsocket_read error={:?}", e);
            return (size, SocketResult::Error)
          },
        }
      }
    }
  }

  fn socket_write(&mut self,  buf: &[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.write(&buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size += sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => return (size, SocketResult::WouldBlock),
          ErrorKind::BrokenPipe => {
            error!("SOCKET\tbroken pipe writing to the socket");
            return (size, SocketResult::Error)
          },
          _ => {
            //FIXME: timeout and other common errors should be sent up
            error!("SOCKET\tsocket_write error={:?}", e);
            return (size, SocketResult::Error)
          },
        }
      }
    }
  }

  fn socket_ref(&self) -> &TcpStream { self }
}

#[cfg(feature = "use_openssl")]
impl SocketHandler for SslStream<TcpStream> {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.ssl_read(&mut buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size += sz,
        Err(Error::WantRead(_))  => return (size, SocketResult::WouldBlock),
        Err(Error::WantWrite(_)) => return (size, SocketResult::WouldBlock),
        Err(Error::Stream(e))    => {
          error!("SOCKET-TLS\treadable TLS socket err={:?}", e);
          return (size, SocketResult::Error)
        },
        _ => return (size, SocketResult::Error)
      }
    }
  }

  fn socket_write(&mut self,  buf: &[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    loop {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }
      match self.ssl_write(&buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size +=sz,
        Err(Error::WantRead(_))  => return (size, SocketResult::WouldBlock),
        Err(Error::WantWrite(_)) => return (size, SocketResult::WouldBlock),
        Err(Error::Stream(e))    => {
          error!("SOCKET-TLS\twritable TLS socket err={:?}", e);
          return (size, SocketResult::Error)
        },
        e => {
          error!("SOCKET-TLS\twritable TLS socket err={:?}", e);
          return (size, SocketResult::Error)
        }
      }
    }
  }

  fn socket_ref(&self) -> &TcpStream { self.get_ref() }
}

pub struct FrontRustls {
  pub stream:  TcpStream,
  pub session: ServerSession,
}

impl SocketHandler for FrontRustls {
  fn socket_read(&mut self,  buf: &mut[u8]) -> (usize, SocketResult) {
    let mut size = 0usize;
    let mut can_read = true;

    while self.session.wants_read() && can_read {
      if size == buf.len() {
        return (size, SocketResult::Continue);
      }

      match self.session.read_tls(&mut self.stream) {
        Ok(0)  => can_read = false,
        Ok(sz) => {},
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => {
            error!("rustls socket_read wouldblock");
            can_read = false;
          },
          _ => {
            error!("could not read TLS stream from socket: {:?}", e);
            return (size, SocketResult::Error)
           }
        }
      }

      if let Err(e) = self.session.process_new_packets() {
        error!("could not process read TLS packets: {:?}", e);
        return (size, SocketResult::Error);
      }

      match self.session.read(&mut buf[size..]) {
        Ok(0)  => return (size, SocketResult::Continue),
        Ok(sz) => size += sz,
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => {
            return (size, SocketResult::WouldBlock);
          },
           _ => {
             error!("could not read data from TLS stream: {:?}", e);
            return (size, SocketResult::Error)
           }
        }

      }
    }

    if can_read {
    (size, SocketResult::Continue)
    } else {
    (size, SocketResult::WouldBlock)
    }
  }

  fn socket_write(&mut self,  buf: &[u8]) -> (usize, SocketResult) {
    let mut sent_size = 0usize;
    let mut buffered_size = 0usize;
    let mut can_write = true;

    match self.session.write(buf) {
      Ok(0)  => {
      },
      Ok(sz) => {
        buffered_size += sz;
      },
      Err(e) => match e.kind() {
        ErrorKind::WouldBlock => {
          // we don't need to do anything, the session will return false in wants_write?
          error!("rustls socket_write wouldblock");
        },
        _ => {
          error!("could not write data to TLS stream: {:?}", e);
          return (buffered_size, SocketResult::Error)
        }
      }
    }

    while self.session.wants_write() && can_write {
      if sent_size == buf.len() {
        return (buffered_size, SocketResult::Continue);
      }

      if sent_size == buffered_size && buffered_size < buf.len() {
        match self.session.write(&buf[buffered_size..]) {
          Ok(0)  => {
          },
          Ok(sz) => {
            buffered_size += sz;
          },
          Err(e) => match e.kind() {
            ErrorKind::WouldBlock => {
              // we don't need to do anything, the session will return false in wants_write?
              error!("rustls socket_write wouldblock");
            },
             _ => {
               error!("could not write data to TLS stream: {:?}", e);
              return (buffered_size, SocketResult::Error)
             }
          }
        }

      }

      match self.session.write_tls(&mut self.stream) {
        Ok(0)  => {
          can_write = false;
        },
        Ok(sz) => {
          sent_size += sz;
        },
        Err(e) => match e.kind() {
          ErrorKind::WouldBlock => can_write = false,
          _ => {
            error!("could not write TLS stream to socket: {:?}", e);
            return (buffered_size, SocketResult::Error)
          }
        }
      }

    }

    if can_write {
      (buffered_size, SocketResult::Continue)
    } else {
      (buffered_size, SocketResult::WouldBlock)
    }
  }

  fn socket_ref(&self) -> &TcpStream { &self.stream }
}

pub fn server_bind(addr: &SocketAddr) -> io::Result<TcpListener> {
  let sock = try!(match *addr {
    SocketAddr::V4(..) => TcpBuilder::new_v4(),
    SocketAddr::V6(..) => TcpBuilder::new_v6(),
  });

  // set so_reuseaddr, but only on unix (mirrors what libstd does)
  if cfg!(unix) {
    try!(sock.reuse_address(true));
  }

  try!(sock.reuse_port(true));

  // bind the socket
  try!(sock.bind(addr));

  // listen
  let listener = try!(sock.listen(1024));
  TcpListener::from_listener(listener, addr)
}

<|endoftext|>


<commit_before>use std::sync::atomic::{self, AtomicPtr};

static GARBAGE: Stack<Box<Drop>> = Stack::new();
static READERS: Stack<RawReader> = Stack::new();
static STATE: State = State::new();

fn gc() {
    // Start the garbage collection.
    if !STATE.start_gc() {
        // Another thread is garbage collecting, so we short-circuit.
        return;
    }

    // Initially, every garbage is marked unused.
    let mut unused = GARBAGE.collect();

    // Traverse the readers and update the reference counts.
    READERS.take_each(|reader| {
        if reader.active.load() {
            // The reader is not released yet, and is thus considered active.

            // Remove the reader from the unused set and insert it back into the log (if it
            // exists in the unused set), as the garbage is active.
            unused.remove(reader.ptr).map(|x| GARBAGE.insert(x));
            // Put the reader back in the structure.
            READERS.insert(reader);
        } else {
            // The reader was released. Destroy it.
            reader.destroy();
        }
    });

    // End the garbage collection cycle.
    STATE.end_gc();
}

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    const fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::new(0 as *const T),
        }
    }

    fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(atomic::Ordering::Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, atomic::Ordering::Release) == head {
                break
            }
        }
    }

    fn take_each(&self, f: F)
    where F: Fn(T) {
        // Replace the old head with a null pointer.
        let mut node = self.head.swap(AtomicPtr::default(), atomic::Ordering::Acquire);

        // We traverse every node until the pointer is null.
        while !node.is_null() {
            // Read the node into an owned box.
            let bx = unsafe { Box::from_raw(head) };
            // Apply the provided closure.
            f(bx.data);
            // Go to the next link.
            node = bx.next;
        }
    }

    fn collect(&self) -> HashSet<T> {
        let mut hs = HashSet::new();
        self.take_each(|x| hs.insert(x));

        hs
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

#[derive(Default)]
struct State {
    flags: AtomicUsize,
}

impl State {
    fn start_gc(&self) -> bool {
        // Mark that a garbage collection is pending.
        if self.flags.fetch_or(1, atomic::Ordering::Relaxed) & 1 != 0 {
            // Another thread is pending to or currently garbage collecting, so we won't do the
            // same.
            return false;
        }

        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(1, 0b11, atomic::Ordering::Relaxed);
            if flags == 1 {
                // Currently, no one accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                return true;
            }
        }
    }

    fn end_gc(&self) {
        self.flags.fetch_sub(1, atomic::Ordering::Relaxed);
    }

    fn start_read(&self) {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);
    }

    fn end_read(&self) {
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);
    }
}

pub struct Reading;

impl Reading {
    pub fn obtain() -> Reading {
        STATE.start_read();

        Reading
    }

    pub fn load<T>(&self, a: &Atomic<T>) -> Reader<T> {
        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::into_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        READERS.push(reader);

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }
}

impl Drop for Reading {
    fn drop(&mut self) {
        STATE.end_read();
    }
}

pub struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    state: State,
}

impl<T> Atomic<T> {
    pub fn new(inner: T) -> Atomic<T> {
        Atomic {
            inner: AtomicPtr::new(Box::into_raw(Box::new(inner))),
            snapshots: Stack::new(),
            readers: Stack::new(),
            flags: State::default(),
        }
    }

    pub fn load(&self) -> Reader<T> {
        let guard = Reading::obtain();
        guard.load(self)
    }

    pub fn store(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the garbage stack.
        GARBAGE.push(Box::from_raw(old));
    }
}
<commit_msg>Enable garbage collection every 2000 ticks (may be adjusted in the future).<commit_after>use std::sync::atomic::{self, AtomicPtr};
use std::cell::Cell;

static GARBAGE: Stack<Box<Drop>> = Stack::new();
static READERS: Stack<RawReader> = Stack::new();
static STATE: State = State::new();
static TICKS_BEFORE_GC: u16 = 2000;

thread_local!(static CLOCK: Cell<u16> = 0);

pub fn gc() {
    // Start the garbage collection.
    if !STATE.start_gc() {
        // Another thread is garbage collecting, so we short-circuit.
        return;
    }

    // Initially, every garbage is marked unused.
    let mut unused = GARBAGE.collect();

    // Traverse the readers and update the reference counts.
    READERS.take_each(|reader| {
        if reader.active.load() {
            // The reader is not released yet, and is thus considered active.

            // Remove the reader from the unused set and insert it back into the log (if it
            // exists in the unused set), as the garbage is active.
            unused.remove(reader.ptr).map(|x| GARBAGE.insert(x));
            // Put the reader back in the structure.
            READERS.insert(reader);
        } else {
            // The reader was released. Destroy it.
            reader.destroy();
        }
    });

    // End the garbage collection cycle.
    STATE.end_gc();
}

pub fn tick() {
    let clock = CLOCK.get();
    if clock == TICKS_BEFORE_GC {
        CLOCK.set(0);
        gc();
    } else {
        CLOCK.set(clock + 1);
    }
}

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    const fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::new(0 as *const T),
        }
    }

    fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(atomic::Ordering::Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, atomic::Ordering::Release) == head {
                break
            }
        }
    }

    fn take_each(&self, f: F)
    where F: Fn(T) {
        // Replace the old head with a null pointer.
        let mut node = self.head.swap(AtomicPtr::default(), atomic::Ordering::Acquire);

        // We traverse every node until the pointer is null.
        while !node.is_null() {
            // Read the node into an owned box.
            let bx = unsafe { Box::from_raw(head) };
            // Apply the provided closure.
            f(bx.data);
            // Go to the next link.
            node = bx.next;
        }
    }

    fn collect(&self) -> HashSet<T> {
        let mut hs = HashSet::new();
        self.take_each(|x| hs.insert(x));

        hs
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

#[derive(Default)]
struct State {
    flags: AtomicUsize,
}

impl State {
    fn start_gc(&self) -> bool {
        // Mark that a garbage collection is pending.
        if self.flags.fetch_or(1, atomic::Ordering::Relaxed) & 1 != 0 {
            // Another thread is pending to or currently garbage collecting, so we won't do the
            // same.
            return false;
        }

        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(1, 0b11, atomic::Ordering::Relaxed);
            if flags == 1 {
                // Currently, no one accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                return true;
            }
        }
    }

    fn end_gc(&self) {
        self.flags.fetch_sub(1, atomic::Ordering::Relaxed);
    }

    fn start_read(&self) {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);
    }

    fn end_read(&self) {
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);
    }
}

pub struct Reading;

impl Reading {
    pub fn obtain() -> Reading {
        STATE.start_read();

        Reading
    }

    pub fn load<T>(&self, a: &Atomic<T>) -> Reader<T> {
        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::into_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        READERS.push(reader);

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }
}

impl Drop for Reading {
    fn drop(&mut self) {
        STATE.end_read();
        tick();
    }
}

pub struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    state: State,
}

impl<T> Atomic<T> {
    pub fn new(inner: T) -> Atomic<T> {
        Atomic {
            inner: AtomicPtr::new(Box::into_raw(Box::new(inner))),
            snapshots: Stack::new(),
            readers: Stack::new(),
            flags: State::default(),
        }
    }

    pub fn load(&self) -> Reader<T> {
        let guard = Reading::obtain();
        guard.load(self)
    }

    pub fn store(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the garbage stack.
        GARBAGE.push(Box::from_raw(old));

        tick();
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom::Start;
use std::io;

pub use super::super::elf::header::*;

#[repr(C)]
#[derive(Clone, Default)]
pub struct Header {
    pub e_ident: [u8; SIZEOF_IDENT],
    pub e_type: u16,
    pub e_machine: u16,
    pub e_version: u32,
    pub e_entry: u64,
    pub e_phoff: u64,
    pub e_shoff: u64,
    pub e_flags: u32,
    pub e_ehsize: u16,
    pub e_phentsize: u16,
    pub e_phnum: u16,
    pub e_shentsize: u16,
    pub e_shnum: u16,
    pub e_shstrndx: u16,
}

pub const SIZEOF_EHDR: usize = 64;

impl fmt::Debug for Header {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "e_ident: {:?} e_type: {} e_machine: 0x{:x} e_version: 0x{:x} e_entry: 0x{:x} \
                e_phoff: 0x{:x} e_shoff: 0x{:x} e_flags: {:x} e_ehsize: {} e_phentsize: {} \
                e_phnum: {} e_shentsize: {} e_shnum: {} e_shstrndx: {}",
               self.e_ident,
               et_to_str(self.e_type),
               self.e_machine,
               self.e_version,
               self.e_entry,
               self.e_phoff,
               self.e_shoff,
               self.e_flags,
               self.e_ehsize,
               self.e_phentsize,
               self.e_phnum,
               self.e_shentsize,
               self.e_shnum,
               self.e_shstrndx)
    }
}

impl Header {
    /// Returns the corresponding ELF header from the given byte array
    pub fn from_bytes(bytes: &[u8; SIZEOF_EHDR]) -> Header {
        // this is not unsafe because the header's size is encoded in the function, although the header can be semantically invalid
        let header: &Header = unsafe { mem::transmute(bytes) };
        header.clone()
    }

    #[cfg(not(feature = "no_endian_fd"))]
    pub fn from_fd(fd: &mut File) -> io::Result<Header> {
        use byteorder::{LittleEndian,BigEndian,ReadBytesExt};
        let mut elf_header = Header::default();

        elf_header.e_ident = [0; SIZEOF_IDENT];
        try!(fd.seek(Start(0)));
        try!(fd.read(&mut elf_header.e_ident));

        match elf_header.e_ident[EI_DATA] {
            ELFDATA2LSB => {
                elf_header.e_type = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_machine = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_version = try!(fd.read_u32::<LittleEndian>());
                elf_header.e_entry = try!(fd.read_u64::<LittleEndian>());
                elf_header.e_phoff = try!(fd.read_u64::<LittleEndian>());
                elf_header.e_shoff = try!(fd.read_u64::<LittleEndian>());
                elf_header.e_flags = try!(fd.read_u32::<LittleEndian>());
                elf_header.e_ehsize = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_phentsize = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_phnum = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_shentsize = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_shnum = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_shstrndx = try!(fd.read_u16::<LittleEndian>());
                Ok(elf_header)
            },
            ELFDATA2MSB => {
                elf_header.e_type = try!(fd.read_u16::<BigEndian>());
                elf_header.e_machine = try!(fd.read_u16::<BigEndian>());
                elf_header.e_version = try!(fd.read_u32::<BigEndian>());
                elf_header.e_entry = try!(fd.read_u64::<BigEndian>());
                elf_header.e_phoff = try!(fd.read_u64::<BigEndian>());
                elf_header.e_shoff = try!(fd.read_u64::<BigEndian>());
                elf_header.e_flags = try!(fd.read_u32::<BigEndian>());
                elf_header.e_ehsize = try!(fd.read_u16::<BigEndian>());
                elf_header.e_phentsize = try!(fd.read_u16::<BigEndian>());
                elf_header.e_phnum = try!(fd.read_u16::<BigEndian>());
                elf_header.e_shentsize = try!(fd.read_u16::<BigEndian>());
                elf_header.e_shnum = try!(fd.read_u16::<BigEndian>());
                elf_header.e_shstrndx = try!(fd.read_u16::<BigEndian>());
                Ok(elf_header)
            },
            d => Err(io::Error::new(io::ErrorKind::Other, format!("Invalid ELF DATA type {:x}",d))),
        }
    }

    #[cfg(feature = "no_endian_fd")]
    pub fn from_fd(fd: &mut File) -> io::Result<Header> {
        let mut elf_header = [0; SIZEOF_EHDR];
        try!(fd.read(&mut elf_header));
        Ok(Header::from_bytes(&elf_header))
    }

}
<commit_msg>elf: move seek into no_endian_fd for now<commit_after>use std::mem;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::io;

pub use super::super::elf::header::*;

#[repr(C)]
#[derive(Clone, Default)]
pub struct Header {
    pub e_ident: [u8; SIZEOF_IDENT],
    pub e_type: u16,
    pub e_machine: u16,
    pub e_version: u32,
    pub e_entry: u64,
    pub e_phoff: u64,
    pub e_shoff: u64,
    pub e_flags: u32,
    pub e_ehsize: u16,
    pub e_phentsize: u16,
    pub e_phnum: u16,
    pub e_shentsize: u16,
    pub e_shnum: u16,
    pub e_shstrndx: u16,
}

pub const SIZEOF_EHDR: usize = 64;

impl fmt::Debug for Header {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "e_ident: {:?} e_type: {} e_machine: 0x{:x} e_version: 0x{:x} e_entry: 0x{:x} \
                e_phoff: 0x{:x} e_shoff: 0x{:x} e_flags: {:x} e_ehsize: {} e_phentsize: {} \
                e_phnum: {} e_shentsize: {} e_shnum: {} e_shstrndx: {}",
               self.e_ident,
               et_to_str(self.e_type),
               self.e_machine,
               self.e_version,
               self.e_entry,
               self.e_phoff,
               self.e_shoff,
               self.e_flags,
               self.e_ehsize,
               self.e_phentsize,
               self.e_phnum,
               self.e_shentsize,
               self.e_shnum,
               self.e_shstrndx)
    }
}

impl Header {
    /// Returns the corresponding ELF header from the given byte array
    pub fn from_bytes(bytes: &[u8; SIZEOF_EHDR]) -> Header {
        // this is not unsafe because the header's size is encoded in the function, although the header can be semantically invalid
        let header: &Header = unsafe { mem::transmute(bytes) };
        header.clone()
    }

    #[cfg(not(feature = "no_endian_fd"))]
    pub fn from_fd(fd: &mut File) -> io::Result<Header> {
        use byteorder::{LittleEndian,BigEndian,ReadBytesExt};
        use std::io::Seek;
        use std::io::SeekFrom::Start;
        let mut elf_header = Header::default();

        elf_header.e_ident = [0; SIZEOF_IDENT];
        try!(fd.seek(Start(0)));
        try!(fd.read(&mut elf_header.e_ident));

        match elf_header.e_ident[EI_DATA] {
            ELFDATA2LSB => {
                elf_header.e_type = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_machine = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_version = try!(fd.read_u32::<LittleEndian>());
                elf_header.e_entry = try!(fd.read_u64::<LittleEndian>());
                elf_header.e_phoff = try!(fd.read_u64::<LittleEndian>());
                elf_header.e_shoff = try!(fd.read_u64::<LittleEndian>());
                elf_header.e_flags = try!(fd.read_u32::<LittleEndian>());
                elf_header.e_ehsize = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_phentsize = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_phnum = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_shentsize = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_shnum = try!(fd.read_u16::<LittleEndian>());
                elf_header.e_shstrndx = try!(fd.read_u16::<LittleEndian>());
                Ok(elf_header)
            },
            ELFDATA2MSB => {
                elf_header.e_type = try!(fd.read_u16::<BigEndian>());
                elf_header.e_machine = try!(fd.read_u16::<BigEndian>());
                elf_header.e_version = try!(fd.read_u32::<BigEndian>());
                elf_header.e_entry = try!(fd.read_u64::<BigEndian>());
                elf_header.e_phoff = try!(fd.read_u64::<BigEndian>());
                elf_header.e_shoff = try!(fd.read_u64::<BigEndian>());
                elf_header.e_flags = try!(fd.read_u32::<BigEndian>());
                elf_header.e_ehsize = try!(fd.read_u16::<BigEndian>());
                elf_header.e_phentsize = try!(fd.read_u16::<BigEndian>());
                elf_header.e_phnum = try!(fd.read_u16::<BigEndian>());
                elf_header.e_shentsize = try!(fd.read_u16::<BigEndian>());
                elf_header.e_shnum = try!(fd.read_u16::<BigEndian>());
                elf_header.e_shstrndx = try!(fd.read_u16::<BigEndian>());
                Ok(elf_header)
            },
            d => Err(io::Error::new(io::ErrorKind::Other, format!("Invalid ELF DATA type {:x}",d))),
        }
    }

    #[cfg(feature = "no_endian_fd")]
    pub fn from_fd(fd: &mut File) -> io::Result<Header> {
        let mut elf_header = [0; SIZEOF_EHDR];
        try!(fd.read(&mut elf_header));
        Ok(Header::from_bytes(&elf_header))
    }

}
<|endoftext|>


<commit_before>//! OpenType font variations common tables.

include!("../generated/generated_variation.rs");

/// Outer and inner indices for reading from an [ItemVariationStore].
#[derive(Copy, Clone, Debug)]
pub struct DeltaSetIndex {
    /// Outer delta set index.
    pub outer: u16,
    /// Inner delta set index.
    pub inner: u16,
}

impl<'a> DeltaSetIndexMap<'a> {
    /// Returns the delta set index for the specified value.
    pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
        let (entry_format, data) = match self {
            Self::Format0(fmt) => (fmt.entry_format() as u32, fmt.map_data()),
            Self::Format1(fmt) => (fmt.entry_format() as u32, fmt.map_data()),
        };
        let entry_size = ((entry_format & EntryFormat::MAP_ENTRY_SIZE_MASK.bits() as u32) >> 4) + 1;
        let data = FontData::new(data);
        let offset = index as usize * entry_size as usize;
        let entry = match entry_size {
            1 => data.read_at::<u8>(offset)? as u32,
            2 => data.read_at::<u16>(offset)? as u32,
            3 => data.read_at::<Uint24>(offset)?.into(),
            4 => data.read_at::<u32>(offset)?,
            _ => {
                return Err(ReadError::MalformedData(
                    "invalid entry size in DeltaSetIndexMap",
                ))
            }
        };
        let bit_count = (entry_format & 0xF) + 1;
        Ok(DeltaSetIndex {
            outer: (entry >> bit_count) as u16,
            inner: (entry & ((1 << bit_count) - 1)) as u16,
        })
    }
}

impl<'a> ItemVariationStore<'a> {
    fn delta(&self, index: DeltaSetIndex, coords: &[F2Dot14]) -> Result<Fixed, ReadError> {
        let data = match self
            .item_variation_datas()
            .nth(index.outer as usize)
            .flatten()
        {
            Some(data) => data?,
            None => return Ok(Fixed::default()),
        };
        let word_delta_count = data.word_delta_count();
        let long_words = word_delta_count & 0x8000 != 0;
        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
        let word_delta_count = word_delta_count & 0x7FFF;

        Ok(Fixed::default())
    }
}

impl<'a> VariationRegion<'a> {
    /// Computes a scalar value for this region and the specified
    /// normalized variation coordinates.
    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed {
        fn f2dot14_to_fixed(x: F2Dot14) -> Fixed {
            Fixed::from_raw(
                (i16::from_be_bytes(x.to_raw()) as i32 * 4).to_be_bytes()
            )
        }
        let mut scalar = Fixed::from_f64(1.0);
        let zero = Fixed::from_f64(0.0);
        for (i, axis_coords) in self.region_axes().iter().enumerate() {
            let coord = coords.get(i).copied().map(|coord| f2dot14_to_fixed(coord)).unwrap_or(zero);
            let start = f2dot14_to_fixed(axis_coords.start_coord.get());
            let end = f2dot14_to_fixed(axis_coords.end_coord.get());
            let peak = f2dot14_to_fixed(axis_coords.peak_coord.get());
            if start > peak || peak > end || peak == zero || start < zero && end > zero {
                continue;
            } else if coord < start || coord > end {
                return zero;
            } else if coord == peak {
                continue;
            } else {
                if coord < peak {
                    scalar = scalar * (coord - start) / (peak - start)
                } else {
                    scalar = scalar * (end - coord) / (end - peak)
                }
            };
        }
        scalar
    }
}

impl<'a> ItemVariationData<'a> {
    /// Returns an iterator over the per-region delta values for the specified
    /// inner index.
    pub fn deltas(&self, inner_index: u16) -> impl Iterator<Item = Fixed> + 'a + Clone {
        let word_delta_count = self.word_delta_count();
        let long_words = word_delta_count & 0x8000 != 0;
        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
        let word_delta_count = word_delta_count & 0x7FFF;
        let region_count = self.region_index_count() as usize;
        let row_size = word_delta_count as usize * word_size
            + region_count.saturating_sub(word_delta_count as usize) * small_size;
        let offset = row_size * inner_index as usize;
        ItemDeltas {
            cursor: FontData::new(self.delta_sets())
                .slice(offset..)
                .unwrap_or_default()
                .cursor(),
            word_delta_count,
            long_words,
            len: region_count as u16,
            pos: 0,
        }
    }
}

#[derive(Clone)]
struct ItemDeltas<'a> {
    cursor: Cursor<'a>,
    word_delta_count: u16,
    long_words: bool,
    len: u16,
    pos: u16,
}

impl<'a> Iterator for ItemDeltas<'a> {
    type Item = Fixed;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.len {
            return None;
        }
        let pos = self.pos;
        self.pos += 1;
        let value = match (pos >= self.word_delta_count, self.long_words) {
            (true, true) | (false, false) => self.cursor.read::<i16>().ok()? as i32,
            (true, false) => self.cursor.read::<i8>().ok()? as i32,
            (false, true) => self.cursor.read::<i32>().ok()?,
        };
        Some(Fixed::from_raw(value.to_be_bytes()))
    }
}
<commit_msg>compute region scalars and combined deltas<commit_after>//! OpenType font variations common tables.

include!("../generated/generated_variation.rs");

/// Outer and inner indices for reading from an [ItemVariationStore].
#[derive(Copy, Clone, Debug)]
pub struct DeltaSetIndex {
    /// Outer delta set index.
    pub outer: u16,
    /// Inner delta set index.
    pub inner: u16,
}

impl<'a> DeltaSetIndexMap<'a> {
    /// Returns the delta set index for the specified value.
    pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
        let (entry_format, data) = match self {
            Self::Format0(fmt) => (fmt.entry_format() as u32, fmt.map_data()),
            Self::Format1(fmt) => (fmt.entry_format() as u32, fmt.map_data()),
        };
        let entry_size = ((entry_format & EntryFormat::MAP_ENTRY_SIZE_MASK.bits() as u32) >> 4) + 1;
        let data = FontData::new(data);
        let offset = index as usize * entry_size as usize;
        let entry = match entry_size {
            1 => data.read_at::<u8>(offset)? as u32,
            2 => data.read_at::<u16>(offset)? as u32,
            3 => data.read_at::<Uint24>(offset)?.into(),
            4 => data.read_at::<u32>(offset)?,
            _ => {
                return Err(ReadError::MalformedData(
                    "invalid entry size in DeltaSetIndexMap",
                ))
            }
        };
        let bit_count = (entry_format & 0xF) + 1;
        Ok(DeltaSetIndex {
            outer: (entry >> bit_count) as u16,
            inner: (entry & ((1 << bit_count) - 1)) as u16,
        })
    }
}

impl<'a> ItemVariationStore<'a> {
    /// Returns the delta value for the specified index and set of normalized
    /// variation coordinates.
    pub fn delta(&self, index: DeltaSetIndex, coords: &[F2Dot14]) -> Result<Fixed, ReadError> {
        let data = match self
            .item_variation_datas()
            .nth(index.outer as usize)
            .flatten()
        {
            Some(data) => data?,
            None => return Ok(Fixed::default()),
        };
        let regions = self.variation_region_list()?.variation_regions();
        let region_indices = data.region_indexes();
        let mut delta = Fixed::ZERO;
        for (i, region_delta) in data.deltas(index.inner).enumerate() {
            let region_index = region_indices
                .get(i)
                .ok_or(ReadError::MalformedData(
                    "invalid delta sets in ItemVariationStore",
                ))?
                .get() as usize;
            let region = regions.get(region_index)?;
            let scalar = region.compute_scalar(coords);
            delta += region_delta * scalar;
        }
        Ok(delta)
    }
}

impl<'a> VariationRegion<'a> {
    /// Computes a scalar value for this region and the specified
    /// normalized variation coordinates.
    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed {
        const ZERO: Fixed = Fixed::ZERO;
        let mut scalar = Fixed::from_f64(1.0);
        for (i, axis_coords) in self.region_axes().iter().enumerate() {
            let coord = coords
                .get(i)
                .copied()
                .map(|coord| coord.to_fixed())
                .unwrap_or(ZERO);
            let start = axis_coords.start_coord.get().to_fixed();
            let end = axis_coords.end_coord.get().to_fixed();
            let peak = axis_coords.peak_coord.get().to_fixed();
            if start > peak || peak > end || peak == ZERO || start < ZERO && end > ZERO {
                continue;
            } else if coord < start || coord > end {
                return ZERO;
            } else if coord == peak {
                continue;
            } else if coord < peak {
                scalar = scalar * (coord - start) / (peak - start)
            } else {
                scalar = scalar * (end - coord) / (end - peak)
            };
        }
        scalar
    }
}

impl<'a> ItemVariationData<'a> {
    /// Returns an iterator over the per-region delta values for the specified
    /// inner index.
    pub fn deltas(&self, inner_index: u16) -> impl Iterator<Item = Fixed> + 'a + Clone {
        let word_delta_count = self.word_delta_count();
        let long_words = word_delta_count & 0x8000 != 0;
        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
        let word_delta_count = word_delta_count & 0x7FFF;
        let region_count = self.region_index_count() as usize;
        let row_size = word_delta_count as usize * word_size
            + region_count.saturating_sub(word_delta_count as usize) * small_size;
        let offset = row_size * inner_index as usize;
        ItemDeltas {
            cursor: FontData::new(self.delta_sets())
                .slice(offset..)
                .unwrap_or_default()
                .cursor(),
            word_delta_count,
            long_words,
            len: region_count as u16,
            pos: 0,
        }
    }
}

#[derive(Clone)]
struct ItemDeltas<'a> {
    cursor: Cursor<'a>,
    word_delta_count: u16,
    long_words: bool,
    len: u16,
    pos: u16,
}

impl<'a> Iterator for ItemDeltas<'a> {
    type Item = Fixed;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.len {
            return None;
        }
        let pos = self.pos;
        self.pos += 1;
        let value = match (pos >= self.word_delta_count, self.long_words) {
            (true, true) | (false, false) => self.cursor.read::<i16>().ok()? as i32,
            (true, false) => self.cursor.read::<i8>().ok()? as i32,
            (false, true) => self.cursor.read::<i32>().ok()?,
        };
        Some(Fixed::from_raw((value << 16).to_be_bytes()))
    }
}
<|endoftext|>


<commit_before>use crate::utils::{span_lint, span_lint_and_then};
use rustc::impl_lint_pass;
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc_session::declare_tool_lint;
use std::cmp::Ordering;
use syntax::ast::*;
use syntax::attr;
use syntax::source_map::Span;
use syntax::symbol::SymbolStr;
use syntax::visit::{walk_block, walk_expr, walk_pat, Visitor};

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: SymbolStr,
    span: Span,
    len: usize,
    whitelist: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 >= threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const WHITELIST: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => self.check_ident(ident),
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

#[must_use]
fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
    for &allow in WHITELIST {
        if whitelisted(interned_name, allow) {
            return Some(allow);
        }
    }
    None
}

#[must_use]
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if whitelisted(&interned_name, existing_name.whitelist) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let mut existing_chars = existing_name.interned.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
            interned: interned_name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Fn(ref sig, _, ref blk) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &ImplItem) {
        if let ImplItemKind::Method(ref sig, ref blk) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, sym!(test)) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<commit_msg>Auto merge of #4926 - matthiaskrgr:rurstup_24, r=matthiaskrgr<commit_after>use crate::utils::{span_lint, span_lint_and_then};
use rustc::impl_lint_pass;
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc_session::declare_tool_lint;
use std::cmp::Ordering;
use syntax::ast::*;
use syntax::attr;
use syntax::source_map::Span;
use syntax::symbol::SymbolStr;
use syntax::visit::{walk_block, walk_expr, walk_pat, Visitor};

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: SymbolStr,
    span: Span,
    len: usize,
    whitelist: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 >= threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const WHITELIST: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => self.check_ident(ident),
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

#[must_use]
fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
    for &allow in WHITELIST {
        if whitelisted(interned_name, allow) {
            return Some(allow);
        }
    }
    None
}

#[must_use]
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if whitelisted(&interned_name, existing_name.whitelist) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let mut existing_chars = existing_name.interned.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
            interned: interned_name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Fn(ref sig, _, ref blk) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if let AssocItemKind::Fn(ref sig, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, sym!(test)) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<|endoftext|>


<commit_before>extern crate stratis_shared as shared;

use std::mem::transmute;
use std::io::Write;
use std::net::Shutdown;

use std::os::raw::c_char;
use std::ffi::CStr;
use std::str;
use std::str::Utf8Error;

use shared::client::Client;
use shared::Uuid;

fn str_from_ptr<'a> (s: *const c_char) -> Result<&'a str,Utf8Error> {
    let cstr = unsafe { CStr::from_ptr(s) };
    str::from_utf8(cstr.to_bytes())
}

#[no_mangle]
pub extern fn new_client() -> *mut Client {
    unsafe { transmute(Box::new(Client::new())) }
}

#[no_mangle]
pub extern fn default_client(key: [u8;20], uuid: [u8;16]) -> *mut Client {
    if let Ok(id) = Uuid::from_bytes(&uuid) {
        unsafe { transmute(Box::new(Client::default(key, id))) }
    }
    else { new_client() }
}

#[no_mangle]
pub extern fn drop_client(cptr: *mut Client) {
    let bc: Box<Client> = unsafe { transmute(cptr) };
    let c: Client = *bc;
    if let Some(ms) = c.stream {
        if let Ok(mut s) = ms.lock() {
            let _ = s.flush();
            let _ = s.shutdown(Shutdown::Both);
        }
    }
}

//TODO: figure out refs, or just use a cstruct perhaps?
#[no_mangle]
pub extern fn get_client_id(cptr: *mut Client) -> [u8;16] {
    let client = unsafe { & *cptr };
    let mut id = [0u8;16];
    for (i,n) in client.base.id.as_bytes().iter().enumerate() {
        id[i] = *n;
    }

    id
}

#[no_mangle]
pub extern fn get_client_key(cptr: *mut Client) -> [u8;20] {
    let client = unsafe { & *cptr };
    let mut key = [0u8;20];
    for (i,n) in client.base.key.iter().enumerate() {
        key[i] = *n;
    }

    key
}

#[no_mangle]
pub extern fn client_connect(cptr: *mut Client, s: *const c_char) -> bool {
    let mut client = unsafe { &mut *cptr };
    if let Ok(s) = str_from_ptr(s) {
        client.connect(s);
    }
    client.stream.is_some()
}
#[no_mangle]
pub extern fn client_login(cptr: *mut Client) -> bool {
    let mut client = unsafe { &mut *cptr };
    client.login()
}
#[no_mangle]
pub extern fn client_register(cptr: *mut Client) {
    let mut client = unsafe { &mut *cptr };
    client.register();
}

/// these are highly OS dependent
#[no_mangle]
pub extern fn client_save(cptr: *mut Client) -> bool {
    let client = unsafe { & *cptr };
    Client::save(&client, "client.key")
}
#[no_mangle]
pub extern fn client_load(cptr: *mut Client) -> bool {
    let mut client = unsafe { &mut *cptr };
    if let Some(c) = Client::load_file("client.key") {
        client.base = c.base;
        true
    }
    else { false }
}

#[no_mangle]
pub extern fn client_chat(cptr: *mut Client, s: *const c_char) {
    let mut client = unsafe { &mut *cptr };
    if let Ok(s) = str_from_ptr(s) {
        client.chat(s);
    }
}
#[no_mangle]
pub extern fn client_nick(cptr: *mut Client, s: *const c_char) {
    let mut client = unsafe { &mut *cptr };
    if let Ok(s) = str_from_ptr(s) {
        client.nick(s);
    }
}
<commit_msg>wip on structs<commit_after>extern crate stratis_shared as shared;

use std::mem::transmute;
use std::io::Write;
use std::net::Shutdown;

use std::os::raw::c_char;
use std::ffi::{CStr,CString};
use std::str;
use std::str::Utf8Error;

use shared::client::Client;
use shared::Uuid;
use shared::chat::MAX_TEXT_LEN;

fn str_from_ptr<'a> (s: *const c_char) -> Result<&'a str,Utf8Error> {
    let cstr = unsafe { CStr::from_ptr(s) };
    str::from_utf8(cstr.to_bytes())
}

#[no_mangle]
pub extern fn new_client() -> *mut Client {
    unsafe { transmute(Box::new(Client::new())) }
}

#[no_mangle]
pub extern fn default_client(key: [u8;20], uuid: [u8;16]) -> *mut Client {
    if let Ok(id) = Uuid::from_bytes(&uuid) {
        unsafe { transmute(Box::new(Client::default(key, id))) }
    }
    else { new_client() }
}

#[no_mangle]
pub extern fn drop_client(cptr: *mut Client) {
    let mut bc = unsafe { Box::from_raw(cptr) };
   /* if let Some(ref mut ms) = bc.stream {
        if let Ok(mut s) = ms.lock() {
            let _ = s.flush();
            let _ = s.shutdown(Shutdown::Both);
        }
    }*/
    drop(bc);
}

//TODO: figure out refs, or just use a cstruct perhaps?
#[no_mangle]
pub extern fn get_client_id(cptr: *mut Client) -> [u8;16] {
    let client = unsafe { & *cptr };
    let mut id = [0u8;16];
    for (i,n) in client.base.id.as_bytes().iter().enumerate() {
        id[i] = *n;
    }

    id
}

#[no_mangle]
pub extern fn get_client_key(cptr: *mut Client) -> [u8;20] {
    let client = unsafe { & *cptr };
    let mut key = [0u8;20];
    for (i,n) in client.base.key.iter().enumerate() {
        key[i] = *n;
    }

    key
}

#[no_mangle]
pub extern fn client_connect(cptr: *mut Client, s: *const c_char) -> bool {
    let mut client = unsafe { &mut *cptr };
    if let Ok(s) = str_from_ptr(s) {
        client.connect(s);
    }
    client.stream.is_some()
}
#[no_mangle]
pub extern fn client_login(cptr: *mut Client) -> bool {
    let mut client = unsafe { &mut *cptr };
    client.login()
}
#[no_mangle]
pub extern fn client_register(cptr: *mut Client) {
    let mut client = unsafe { &mut *cptr };
    client.register();
}

/// these are highly OS dependent
#[no_mangle]
pub extern fn client_save(cptr: *mut Client) -> bool {
    let client = unsafe { & *cptr };
    Client::save(&client, "client.key")
}
#[no_mangle]
pub extern fn client_load(cptr: *mut Client) -> bool {
    let mut client = unsafe { &mut *cptr };
    if let Some(c) = Client::load_file("client.key") {
        client.base = c.base;
        true
    }
    else { false }
}

#[no_mangle]
pub extern fn client_chat(cptr: *mut Client, s: *const c_char) {
    let mut client = unsafe { &mut *cptr };
    if let Ok(s) = str_from_ptr(s) {
        client.chat(s);
    }
}
#[no_mangle]
pub extern fn client_nick(cptr: *mut Client, s: *const c_char) {
    let mut client = unsafe { &mut *cptr };
    if let Ok(s) = str_from_ptr(s) {
        client.nick(s);
    }
}

#[repr(C)]
pub struct ChatFrame {
    //id: [u8;16],
    msg: [u8;MAX_TEXT_LEN],
}

//NOTE: this is meant to be polled on frame-tick
#[no_mangle]
pub extern fn get_client_chat(cptr: *mut Client, chat: &mut ChatFrame) -> bool {
    let client = unsafe { & *cptr };

    if let Ok(mut v) = client.msg.lock() {
        if v.len() > 0 {
            let (uuid, msg) = v.remove(0);

            //chat.id = uuid.as_bytes().clone();

            let bytes = msg.as_bytes();
            for (i,b) in bytes.iter().enumerate() {
                chat.msg[i] = *b;
            }

            //chat.len = bytes.len() as u16;

            return true
        }
    }

    false
}
<|endoftext|>


<commit_before>#![cfg(target_os="linux")]
//! Bindings to kernel/sys.c

//First, let's use linux-api stuff
extern crate linux_api;
use linux_api::{pid_t, uid_t};

//Okay, let's do this!
extern "C" {

    ///Returns the thread group ID of the current process
    pub fn getpid() -> pid_t;

    ///Returns the parent process's thread group ID
    pub fn getppid() -> pid_t;

    ///Returns the current user's ID
    pub fn getuid() -> uid_t;
}

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn ensure_not_on_process_zero() {
        unsafe {
            assert!(getpid() >= 0);
            assert!(getppid() >= 0);
        }
    }

}<commit_msg>linux-api-sys: Add geteuid(), getgid(), getegid()<commit_after>#![cfg(target_os="linux")]
//! Bindings to kernel/sys.c

//First, let's use linux-api stuff
extern crate linux_api;
use linux_api::{pid_t, uid_t, gid_t};

//Okay, let's do this!
extern "C" {

    ///Returns the thread group ID of the current process
    pub fn getpid() -> pid_t;

    ///Returns the parent process's thread group ID
    pub fn getppid() -> pid_t;

    ///Returns the current user's ID
    pub fn getuid() -> uid_t;

    ///Returns the current effective user's ID
    pub fn geteuid() -> uid_t;

    ///Returns the current user's (primary?) group ID
    pub fn getgid() -> gid_t;

    ///Returns the current effective user's (primary?) group ID
    pub fn getegid() -> gid_t;
}

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn ensure_not_on_process_zero() {
        unsafe {
            assert!(getpid() >= 0);
            assert!(getppid() >= 0);
        }
    }

    #[test]
    fn ensure_group_same() {
        unsafe {
            assert_eq!(getgid(), getegid());
        }
    }

    #[test]
    fn ensure_user_same() {
        unsafe {
            assert_eq!(getuid(), geteuid());
        }
    }

}<|endoftext|>