<commit_before>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// 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.

//! Miscellaneous utilities for low level networking

extern crate libc;

use packet::PrimitiveValues;
use packet::ip::IpNextHeaderProtocol;
use datalink::NetworkInterface;
use pnet_macros_support::types::u16be;

use std::fmt;
use std::str::FromStr;
use std::u8;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::mem;
use std::slice;

use internal;
use sockets;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl PrimitiveValues for MacAddr {
    type T = (u8, u8, u8, u8, u8, u8);
    fn to_primitive_values(&self) -> (u8, u8, u8, u8, u8, u8) {
        (self.0, self.1, self.2, self.3, self.4, self.5)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0,
               self.1,
               self.2,
               self.3,
               self.4,
               self.5)
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, fmt)
    }
}

// FIXME Is this the right way to do this? Which occurs is an implementation
//       issue rather than actually defined - is it useful to provide these
//       errors, or would it be better to just give ()?
/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

#[test]
fn mac_addr_from_str() {
    assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
    assert_eq!("ff:ff:ff:ff:ff:ff".parse(),
               Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
    assert_eq!("12:34:56:78:90:ab".parse(),
               Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
    assert_eq!("::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("0::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("::::0::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:90:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooManyComponents));
    assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
}

#[test]
fn str_from_mac_addr() {
    assert_eq!(
        format!("{}", MacAddr(0, 0, 0, 0, 0, 0)),
        "00:00:00:00:00:00"
        );
    assert_eq!(
        format!("{}",MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)),
        "ff:ff:ff:ff:ff:ff"
        );
    assert_eq!(
        format!("{}",MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
        "12:34:56:78:09:ab"
        );
}

#[cfg(target_os = "linux")]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET {
            let sll: *const libc::sockaddr_ll = mem::transmute(sa);
            let mac = MacAddr((*sll).sll_addr[0],
                              (*sll).sll_addr[1],
                              (*sll).sll_addr[2],
                              (*sll).sll_addr[3],
                              (*sll).sll_addr[4],
                              (*sll).sll_addr[5]);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use bindings::bpf;
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK {
            let sdl: *const bpf::sockaddr_dl = mem::transmute(sa);
            let nlen = (*sdl).sdl_nlen as usize;
            let mac = MacAddr((*sdl).sdl_data[nlen] as u8,
                              (*sdl).sdl_data[nlen + 1] as u8,
                              (*sdl).sdl_data[nlen + 2] as u8,
                              (*sdl).sdl_data[nlen + 3] as u8,
                              (*sdl).sdl_data[nlen + 4] as u8,
                              (*sdl).sdl_data[nlen + 5] as u8);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

/// Get a list of available network interfaces for the current machine.
/// Deprecated. Instead use the implementation available for your backend.
/// The default one is at `pnet::datalink::interfaces`.
#[deprecated(note="Moved to datalink::interfaces()")]
#[inline]
pub fn get_network_interfaces() -> Vec<NetworkInterface> {
    ::datalink::interfaces()
}

/// Convert value to byte array
pub trait Octets {
    /// Output type - bytes array
    type Output;

    /// Return value as bytes (big-endian order)
    fn octets(&self) -> Self::Output;
}

impl Octets for u64 {
    type Output = [u8; 8];

    fn octets(&self) -> Self::Output {
        [(*self >> 56) as u8, (*self >> 48) as u8, (*self >> 40) as u8, (*self >> 32) as u8,
         (*self >> 24) as u8, (*self >> 16) as u8, (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u32 {
    type Output = [u8; 4];

    fn octets(&self) -> Self::Output {
        [(*self >> 24) as u8, (*self >> 16) as u8 , (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u16 {
    type Output = [u8; 2];

    fn octets(&self) -> Self::Output {
        [(*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u8 {
    type Output = [u8; 1];

    fn octets(&self) -> Self::Output {
        [*self]
    }
}

/// Calculates a checksum. Used by ipv4 and icmp. The two bytes starting at `skipword * 2` will be
/// ignored. Supposed to be the checksum field, which is regarded as zero during calculation.
pub fn checksum(data: &[u8], skipword: usize) -> u16be {
    let mut sum = sum_be_words(data, skipword);
    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }
    !sum as u16
}

/// Calculate the checksum for a packet built on IPv4. Used by udp and tcp.
pub fn ipv4_checksum(data: &[u8],
                     skipword: usize,
                     extra_data: &[u8],
                     source: Ipv4Addr,
                     destination: Ipv4Addr,
                     next_level_protocol: IpNextHeaderProtocol)
    -> u16be {
    let mut sum = 0u32;

    // Checksum pseudo-header
    sum += ipv4_word_sum(source);
    sum += ipv4_word_sum(destination);

    let IpNextHeaderProtocol(next_level_protocol) = next_level_protocol;
    sum += next_level_protocol as u32;

    let len = data.len() + extra_data.len();
    sum += len as u32;

    // Checksum packet header and data
    sum += sum_be_words(data, skipword);
    sum += sum_be_words(extra_data, extra_data.len()/2);

    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }

    !sum as u16
}

fn ipv4_word_sum(ip: Ipv4Addr) -> u32 {
    let octets = ip.octets();
    ((octets[0] as u32) << 8 | octets[1] as u32) + ((octets[2] as u32) << 8 | octets[3] as u32)
}

/// Calculate the checksum for a packet built on IPv6
pub fn ipv6_checksum(data: &[u8],
                     skipword: usize,
                     extra_data: &[u8],
                     source: Ipv6Addr,
                     destination: Ipv6Addr,
                     next_level_protocol: IpNextHeaderProtocol)
    -> u16be {
    let mut sum = 0u32;

    // Checksum pseudo-header
    sum += ipv6_word_sum(source);
    sum += ipv6_word_sum(destination);

    let IpNextHeaderProtocol(next_level_protocol) = next_level_protocol;
    sum += next_level_protocol as u32;

    let len = data.len() + extra_data.len();
    sum += len as u32;

    // Checksum packet header and data
    sum += sum_be_words(data, skipword);
    sum += sum_be_words(extra_data, extra_data.len()/2);

    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }

    !sum as u16
}

fn ipv6_word_sum(ip: Ipv6Addr) -> u32 {
    ip.segments().iter().map(|x| *x as u32).sum()
}

/// Sum all words (16 bit chunks) in the given data. The word at word offset
/// `skipword` will be skipped. Each word is treated as big endian.
fn sum_be_words(data: &[u8], skipword: usize) -> u32 {
    let len = data.len();
    let wdata: &[u16] = unsafe { slice::from_raw_parts(data.as_ptr() as *const u16, len / 2) };
    assert!(skipword <= wdata.len());

    let mut sum = 0u32;
    let mut i = 0;
    while i < skipword {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    i += 1;
    while i < wdata.len() {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    // If the length is odd, make sure to checksum the final byte
    if len & 1 != 0 {
        sum += (unsafe { *data.get_unchecked(len - 1) } as u32) << 8;
    }

    sum
}

#[cfg(all(test, feature = "benchmark"))]
mod checksum_benchmarks {
    use super::checksum;
    use test::{Bencher, black_box};

    #[bench]
    fn bench_checksum_small(b: &mut Bencher) {
        let data = vec![99u8; 20];
        b.iter(|| checksum(black_box(&data), 5));
    }

    #[bench]
    fn bench_checksum_large(b: &mut Bencher) {
        let data = vec![123u8; 1024];
        b.iter(|| checksum(black_box(&data), 5));
    }
}
<commit_msg>Extract checksum bitshifting to fn<commit_after>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// 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.

//! Miscellaneous utilities for low level networking

extern crate libc;

use packet::PrimitiveValues;
use packet::ip::IpNextHeaderProtocol;
use datalink::NetworkInterface;
use pnet_macros_support::types::u16be;

use std::fmt;
use std::str::FromStr;
use std::u8;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::mem;
use std::slice;

use internal;
use sockets;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl PrimitiveValues for MacAddr {
    type T = (u8, u8, u8, u8, u8, u8);
    fn to_primitive_values(&self) -> (u8, u8, u8, u8, u8, u8) {
        (self.0, self.1, self.2, self.3, self.4, self.5)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0,
               self.1,
               self.2,
               self.3,
               self.4,
               self.5)
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, fmt)
    }
}

// FIXME Is this the right way to do this? Which occurs is an implementation
//       issue rather than actually defined - is it useful to provide these
//       errors, or would it be better to just give ()?
/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

#[test]
fn mac_addr_from_str() {
    assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
    assert_eq!("ff:ff:ff:ff:ff:ff".parse(),
               Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
    assert_eq!("12:34:56:78:90:ab".parse(),
               Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
    assert_eq!("::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("0::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("::::0::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:90:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooManyComponents));
    assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
}

#[test]
fn str_from_mac_addr() {
    assert_eq!(
        format!("{}", MacAddr(0, 0, 0, 0, 0, 0)),
        "00:00:00:00:00:00"
        );
    assert_eq!(
        format!("{}",MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)),
        "ff:ff:ff:ff:ff:ff"
        );
    assert_eq!(
        format!("{}",MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
        "12:34:56:78:09:ab"
        );
}

#[cfg(target_os = "linux")]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET {
            let sll: *const libc::sockaddr_ll = mem::transmute(sa);
            let mac = MacAddr((*sll).sll_addr[0],
                              (*sll).sll_addr[1],
                              (*sll).sll_addr[2],
                              (*sll).sll_addr[3],
                              (*sll).sll_addr[4],
                              (*sll).sll_addr[5]);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use bindings::bpf;
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK {
            let sdl: *const bpf::sockaddr_dl = mem::transmute(sa);
            let nlen = (*sdl).sdl_nlen as usize;
            let mac = MacAddr((*sdl).sdl_data[nlen] as u8,
                              (*sdl).sdl_data[nlen + 1] as u8,
                              (*sdl).sdl_data[nlen + 2] as u8,
                              (*sdl).sdl_data[nlen + 3] as u8,
                              (*sdl).sdl_data[nlen + 4] as u8,
                              (*sdl).sdl_data[nlen + 5] as u8);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

/// Get a list of available network interfaces for the current machine.
/// Deprecated. Instead use the implementation available for your backend.
/// The default one is at `pnet::datalink::interfaces`.
#[deprecated(note="Moved to datalink::interfaces()")]
#[inline]
pub fn get_network_interfaces() -> Vec<NetworkInterface> {
    ::datalink::interfaces()
}

/// Convert value to byte array
pub trait Octets {
    /// Output type - bytes array
    type Output;

    /// Return value as bytes (big-endian order)
    fn octets(&self) -> Self::Output;
}

impl Octets for u64 {
    type Output = [u8; 8];

    fn octets(&self) -> Self::Output {
        [(*self >> 56) as u8, (*self >> 48) as u8, (*self >> 40) as u8, (*self >> 32) as u8,
         (*self >> 24) as u8, (*self >> 16) as u8, (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u32 {
    type Output = [u8; 4];

    fn octets(&self) -> Self::Output {
        [(*self >> 24) as u8, (*self >> 16) as u8 , (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u16 {
    type Output = [u8; 2];

    fn octets(&self) -> Self::Output {
        [(*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u8 {
    type Output = [u8; 1];

    fn octets(&self) -> Self::Output {
        [*self]
    }
}

/// Calculates a checksum. Used by ipv4 and icmp. The two bytes starting at `skipword * 2` will be
/// ignored. Supposed to be the checksum field, which is regarded as zero during calculation.
pub fn checksum(data: &[u8], skipword: usize) -> u16be {
    let sum = sum_be_words(data, skipword);
    finalize_checksum(sum)
}

fn finalize_checksum(mut sum: u32) -> u16be {
    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }
    !sum as u16
}

/// Calculate the checksum for a packet built on IPv4. Used by udp and tcp.
pub fn ipv4_checksum(data: &[u8],
                     skipword: usize,
                     extra_data: &[u8],
                     source: Ipv4Addr,
                     destination: Ipv4Addr,
                     next_level_protocol: IpNextHeaderProtocol)
    -> u16be {
    let mut sum = 0u32;

    // Checksum pseudo-header
    sum += ipv4_word_sum(source);
    sum += ipv4_word_sum(destination);

    let IpNextHeaderProtocol(next_level_protocol) = next_level_protocol;
    sum += next_level_protocol as u32;

    let len = data.len() + extra_data.len();
    sum += len as u32;

    // Checksum packet header and data
    sum += sum_be_words(data, skipword);
    sum += sum_be_words(extra_data, extra_data.len()/2);

    finalize_checksum(sum)
}

fn ipv4_word_sum(ip: Ipv4Addr) -> u32 {
    let octets = ip.octets();
    ((octets[0] as u32) << 8 | octets[1] as u32) + ((octets[2] as u32) << 8 | octets[3] as u32)
}

/// Calculate the checksum for a packet built on IPv6
pub fn ipv6_checksum(data: &[u8],
                     skipword: usize,
                     extra_data: &[u8],
                     source: Ipv6Addr,
                     destination: Ipv6Addr,
                     next_level_protocol: IpNextHeaderProtocol)
    -> u16be {
    let mut sum = 0u32;

    // Checksum pseudo-header
    sum += ipv6_word_sum(source);
    sum += ipv6_word_sum(destination);

    let IpNextHeaderProtocol(next_level_protocol) = next_level_protocol;
    sum += next_level_protocol as u32;

    let len = data.len() + extra_data.len();
    sum += len as u32;

    // Checksum packet header and data
    sum += sum_be_words(data, skipword);
    sum += sum_be_words(extra_data, extra_data.len()/2);

    finalize_checksum(sum)
}

fn ipv6_word_sum(ip: Ipv6Addr) -> u32 {
    ip.segments().iter().map(|x| *x as u32).sum()
}

/// Sum all words (16 bit chunks) in the given data. The word at word offset
/// `skipword` will be skipped. Each word is treated as big endian.
fn sum_be_words(data: &[u8], skipword: usize) -> u32 {
    let len = data.len();
    let wdata: &[u16] = unsafe { slice::from_raw_parts(data.as_ptr() as *const u16, len / 2) };
    assert!(skipword <= wdata.len());

    let mut sum = 0u32;
    let mut i = 0;
    while i < skipword {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    i += 1;
    while i < wdata.len() {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    // If the length is odd, make sure to checksum the final byte
    if len & 1 != 0 {
        sum += (unsafe { *data.get_unchecked(len - 1) } as u32) << 8;
    }

    sum
}

#[cfg(all(test, feature = "benchmark"))]
mod checksum_benchmarks {
    use super::checksum;
    use test::{Bencher, black_box};

    #[bench]
    fn bench_checksum_small(b: &mut Bencher) {
        let data = vec![99u8; 20];
        b.iter(|| checksum(black_box(&data), 5));
    }

    #[bench]
    fn bench_checksum_large(b: &mut Bencher) {
        let data = vec![123u8; 1024];
        b.iter(|| checksum(black_box(&data), 5));
    }
}
<|endoftext|>


<commit_before>use std::process::Command;
use XML;

pub fn panel_xslt() -> String {
    let scss = Command::new("/usr/local/bin/sassc").arg("/Users/jadencarver/dev/superconductor/assets/__pm.scss").output().unwrap();
    let css = String::from_utf8(scss.stdout).unwrap();

    let markup = html! {
        xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" {
            xsl:output method="html" indent="yes" {}

            // BEGINS FORM  ------------------
            xsl:template match="/" {
                div#__pm__panel {
                    form#__pm__commit method="post" name="commit" {
                        style type="text/css" (css)
                        style type="text/css" "@import url('//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/styles/agate.min.css');"
                        div#__pm__task {
                            input type="checkbox" id="__pm__commit__dragged" name="dragged" value="true" {}
                            input type="hidden" id="__pm__commit__task" name="task" value="{/state/task/name}" {}
                            xsl:if test="/state/log/commit" {
                                ul#__pm__commits {
                                    xsl:apply-templates select="/state/log/commit" {}
                                }
                                hr {}
                            }
                            dl.properties {
                                xsl:apply-templates select="/state/properties/property" {}
                            }
                            textarea id="__pm__commit__message" tabindex="1" name="message" placeholder="Add a Comment" {
                                xsl:value-of select="/state/message" {}
                            }
                            div#__pm__new_commit {
                                ul#__pm__new_commit__actions {
                                    li input type="submit" tabindex="5" name="new_task" value="New Task" {}
                                    li input type="submit" tabindex="4" name="save_update" value="Save Update" {}
                                }
                                xsl:if test="/state/changes/change" {
                                    fieldset#__pm__commit__changes.details {
                                        legend#__pm__commit__changes_legend tabindex="2" role="button" {
                                            xsl:if test="not(/state/changes/statistics)" {
                                                "Include Changes"
                                            }
                                            span#__pm__commit__changes__statistics.token {
                                                xsl:if test="/state/changes/statistics/files != 0" {
                                                    span {
                                                        xsl:value-of select="format-number(/state/changes/statistics/files, '#,###.##')" {}
                                                        " file"
                                                        xsl:if test="/state/changes/statistics/files != 1" {
                                                            "s"
                                                        }
                                                    }
                                                }
                                                xsl:if test="/state/changes/statistics/insertions != 0" {
                                                    span.token--positive {
                                                        "+"
                                                        xsl:value-of select="format-number(/state/changes/statistics/insertions, '#,###.##')" {}
                                                    }
                                                }
                                                xsl:if test="/state/changes/statistics/deletions != 0" {
                                                    span.token--negative {
                                                        "-"
                                                        xsl:value-of select="format-number(/state/changes/statistics/deletions, '#,###.##')" {}
                                                    }
                                                }
                                            }
                                        }
                                        ul {
                                            xsl:apply-templates select="/state/changes/change" {}
                                        }
                                    }
                                }
                            }
                        }
                        xsl:choose {
                            xsl:when test="/state/setup" {
                                div.setup {
                                    h1 "Setup Instructions"
                                    p "Begin by entering your project name, and selecting the appropriate properties."
                                }
                            }
                            xsl:when test="/state/diffs/*" {
                                xsl:apply-templates select="/state/diffs" {}
                            }
                            xsl:otherwise {
                                xsl:apply-templates select="/state/tasks" {}
                            }
                        }
                    }
                }
            }
            xsl:template match="/state/properties/property" {
                div.property {
                    xsl:choose {
                        xsl:when test="name = 'Project'" {
                            dt { label for="__pm__commit__properties--project" "Project" }
                            dd.input {
                                input type="text" id="__pm__commit__properties--project" name="property" data-name="Project" value="{/state/task/property[name[text()='Project']]/value}" {}
                            }
                        }
                        xsl:when test="name = 'Status'" {
                            dt { label for="__pm__commit__properties--status" "Status" }
                            dd.select tabindex="1" {
                                xsl:value-of select="/state/task/property[name[text()='Status']]/value" {}
                                select id="__pm__commit__properties--status" name="property" data-name="Status" {
                                    option value="" {}
                                    xsl:apply-templates "options/option" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Developer'" {
                            dt { label for="__pm__commit__properties--developer" "Developer" }
                            dd.select {
                                input type="text" id="__pm__commit__properties--developer" value="{/state/task/property[name[text()='Developer']]/value}" {}
                                select name="property" data-name="Developer" {
                                    xsl:apply-templates "options/option" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Manager'" {
                            dt { label for="__pm__commit__properties--owner" "Manager" }
                            dd.select {
                                input type="text" id="__pm__commit__properties--manager" value="{/state/task/property[name[text()='Manager']]/value}" {}
                                select name="property" data-name="Manager" {
                                    xsl:apply-templates "options/option" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Description'" {
                            dt { label for="__pm__commit__properties--description" "Description" }
                            dd {
                                textarea id="__pm__commit__properties--description" name="property" data-name="Description" {
                                    xsl:value-of select="/state/task/property[name[text()='Description']]/value" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Estimate'" {
                            dt { label for="__pm__commit__properties--estimate" "Estimate" }
                            dd.input {
                                input type="range" id="__pm__commit__properties--estimate" name="property" data-name="Estimate" value="{/state/task/property[name[text()='Estimate']]/value}" {}
                            }
                        }
                        xsl:otherwise {
                            xsl:variable name="name" select="name/text()" {}
                            xsl:variable name="value" select="/state/task/property[name[text() = $name]]/value" {}
                            input type="hidden" name="property" data-name="{name}" value="{$value}" {}
                        }
                    }
                }
            }
            xsl:template match="/state/properties/property/options/option" {
                xsl:variable name="name" select="./parent::options/parent::property/name" {}
                xsl:element name="option" {
                    xsl:attribute name="value" {
                        xsl:value-of select="." {}
                    }
                    xsl:if test="/state/task/property[name[text()=$name]]/value = ." {
                        xsl:attribute name="selected" "selected"
                    }
                    xsl:value-of select="." {}
                }
            }
            xsl:template match="/state/diffs" {
                div.diff {
                    pre {
                        code {
                            xsl:copy-of select="*" {}
                        }
                    }
                }
            }

            // BEGINS TASKS  ------------------
            xsl:key name="task-status" match="/state/task|/state/tasks/task" use="property[name='Status']/value" {}

            xsl:template match="/state/tasks" {
                xsl:choose {
                    xsl:when test="filter" {
                        input type="hidden" name="filter" data-name="{filter/name}" data-value="{filter/value}" {}
                        ul.list {
                            header {
                                button type="submit" name="filter" {
                                    xsl:value-of select="filter/value" {}
                                }
                            }
                            xsl:apply-templates select="(/state/task|./task)[property[name='Status'] and not(property[name='Status']/value='')]" {
                                xsl:sort select="property[name='Ordinal']/value" {}
                            }
                        }
                        ul.list.list--backlog {
                            header {
                                button type="submit" name="filter" {
                                    "Backlog"
                                }
                            }
                            xsl:apply-templates select="(/state/task|./task)[not(property[name='Status']) or property[name='Status']/value='']" {
                                xsl:sort select="property[name='Ordinal']/value" {}
                            }
                        }
                    }
                    xsl:otherwise {
                        xsl:for-each select="/state/properties/property[name='Status']/options/option" {
                            xsl:variable name="status" select="./text()" {}
                            xsl:variable name="max" {
                                xsl:for-each select="key('task-status', $status)/property[name='Ordinal']/value" {
                                    xsl:sort select="." data-type="number" order="descending" {}
                                        xsl:if test="position() = 1" {
                                            xsl:value-of select="." {}
                                        }
                                }
                            }
                            xsl:variable name="next" {
                                xsl:choose {
                                    xsl:when test="not($max) or string(number($max)) = 'NaN'" { "1" }
                                    xsl:otherwise {
                                        xsl:value-of select="$max + 1" {}
                                    }
                                }
                            }
                            ul.tiles data-property-name="Status" data-property-value="{.}" {
                                div.column data-property-name="Ordinal" data-property-value="{$next}" {
                                    header {
                                        button type="submit" name="filter" data-name="Status" data-value="{.}" {
                                            xsl:value-of select="." {}
                                        }
                                    }
                                    xsl:for-each select="key('task-status', $status)" {
                                        xsl:sort select="property[name='Ordinal']/value" data-type="number" {}
                                        xsl:call-template name="task" {}
                                    }
                                }
                            }
                        }
                    }
                }
            }
            xsl:template match="task" name="task" {
                xsl:variable name="ordinal" {
                    xsl:choose {
                        xsl:when test="property[name='Ordinal']/value != ''" {
                            xsl:value-of select="property[name='Ordinal']/value" {}
                        }
                        xsl:otherwise {
                            "1"
                        }
                    }
                }
                xsl:variable name="previous" {
                    xsl:for-each select="key('task-status', property[name='Status']/value)/property[name='Ordinal'][number(value) < $ordinal]/value" {
                        xsl:sort select="." data-type="number" order="descending" {}
                        xsl:if test="position() = 1" {
                            xsl:value-of select="." {}
                        }
                    }
                }
                xsl:variable name="next" {
                    xsl:choose {
                        xsl:when test="not($previous) or $previous = ''" {
                            xsl:value-of select="$ordinal div 2" {}
                        }
                        xsl:otherwise {
                            xsl:value-of select="$previous+(number(format-number($ordinal - $previous, '###0.0###;#')) div 2)" {}
                        }
                    }
                }
                xsl:variable name="class" {
                    "tile "
                    xsl:value-of select="concat('tile--status-', translate(property[name='Status']/value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ', 'abcdefghijklmnopqrstuvwxyz-'))" {}
                }
                li class="{$class}" data-property-name="Ordinal" data-property-value="{$next}" {
                    xsl:element name="div" {
                        xsl:attribute name="draggable" "true"
                        xsl:attribute name="tabindex" "99"
                        xsl:attribute name="id" {
                            xsl:value-of select="concat('__pm__task_', name)" {}
                        }
                        xsl:attribute name="class" {
                            " task "
                            xsl:if test="/state/task/name = name" { " selected " }
                        }
                        xsl:attribute name="data-name" {
                            xsl:value-of select="name" {}
                        }
                        div class="task__name" {
                            xsl:value-of select="name" {}
                        }
                        div.task__property--description {
                            xsl:copy-of select="property[name='Description']/value" {}
                        }
                        xsl:if test="property[name='Estimate']/value != ''" {
                            div.task__property--estimate {
                                xsl:copy-of select="property[name='Estimate']/value" {}
                            }
                        }
                    }
                }
            }

            // BEGINS LOGS  ------------------
            xsl:template match="/state/log/commit" {
                li {
                    xsl:if test="./preceding-sibling::commit[1]/user/email=user/email and ./preceding-sibling::commit[1]/timestamp - timestamp > -7200" {
                        xsl:attribute name="class" "continuous"
                    }
                    img src="{user/image}" alt="{user/name} <{user/email}>" {}
                    xsl:if test="changes" {
                        button.button--medium.attachments {
                            svg id="i-paperclip" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="20" height="20" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" {
                                path d="M10 9 L10 24 C10 28 13 30 16 30 19 30 22 28 22 24 L22 6 C22 3 20 2 18 2 16 2 14 3 14 6 L14 23 C14 24 15 25 16 25 17 25 18 24 18 23 L18 9" {}
                            }
                        }
                    }
                    blockquote {
                        a.user__name href="#" {
                            xsl:value-of select="user/name" {}
                        }
                        xsl:value-of select="message" {}
                    }
                    xsl:if test="task" {
                        dl.tasks {
                            xsl:apply-templates select="task[property]" {}
                        }
                    }
                    div {
                        time datetime="{localtime}" {
                            xsl:value-of select="localtime" {}
                        }
                    }
                }
            }
            xsl:template match="/state/log/commit/task" {
                dt { xsl:value-of select="name" {} }
                dd {
                    ul.properties {
                        xsl:apply-templates select="property" {}
                    }
                }
            }
            xsl:template match="/state/log/commit/task/property" {
                li.token {
                    span.name { xsl:value-of select="name" {} }
                    xsl:if test="before" {
                        span.before.token--neutral { xsl:value-of select="before" {} }
                    }
                    span.after.token--positive { xsl:value-of select="value" {} }
                }
            }
            xsl:template match="/state/changes/change" {
                li tabindex="3" id="{concat('__pm__changes__checkbox--', @id)}" {
                    xsl:element name="input" {
                        xsl:attribute name="name" { "include" }
                        xsl:attribute name="id" {
                            xsl:value-of select="concat('__pm__changes--', @id)" {}
                        }
                        xsl:attribute name="value" {
                            xsl:value-of select="path" {}
                        }
                        xsl:attribute name="tabindex" "-1"
                        xsl:attribute name="type" "checkbox"
                        xsl:if test="included='true'" {
                            xsl:attribute name="checked" {}
                        }
                        xsl:if test="removal='true'" {
                            xsl:attribute name="class" { "delete" }
                        }
                    }
                    label for="__pm__changes--{@id}" {
                        xsl:value-of select="path" {}
                    }
                    button.button--tiny name="diff" value="{path}" { " +10 -10" }
                }
            }
        }
    };
    format!("{}{}", XML, markup.into_string())
}

<commit_msg>Sorting by name fixes backlog jumping around<commit_after>use std::process::Command;
use XML;

pub fn panel_xslt() -> String {
    let scss = Command::new("/usr/local/bin/sassc").arg("/Users/jadencarver/dev/superconductor/assets/__pm.scss").output().unwrap();
    let css = String::from_utf8(scss.stdout).unwrap();

    let markup = html! {
        xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" {
            xsl:output method="html" indent="yes" {}

            // BEGINS FORM  ------------------
            xsl:template match="/" {
                div#__pm__panel {
                    form#__pm__commit method="post" name="commit" {
                        style type="text/css" (css)
                        style type="text/css" "@import url('//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/styles/agate.min.css');"
                        div#__pm__task {
                            input type="checkbox" id="__pm__commit__dragged" name="dragged" value="true" {}
                            input type="hidden" id="__pm__commit__task" name="task" value="{/state/task/name}" {}
                            xsl:if test="/state/log/commit" {
                                ul#__pm__commits {
                                    xsl:apply-templates select="/state/log/commit" {}
                                }
                                hr {}
                            }
                            dl.properties {
                                xsl:apply-templates select="/state/properties/property" {}
                            }
                            textarea id="__pm__commit__message" tabindex="1" name="message" placeholder="Add a Comment" {
                                xsl:value-of select="/state/message" {}
                            }
                            div#__pm__new_commit {
                                ul#__pm__new_commit__actions {
                                    li input type="submit" tabindex="5" name="new_task" value="New Task" {}
                                    li input type="submit" tabindex="4" name="save_update" value="Save Update" {}
                                }
                                xsl:if test="/state/changes/change" {
                                    fieldset#__pm__commit__changes.details {
                                        legend#__pm__commit__changes_legend tabindex="2" role="button" {
                                            xsl:if test="not(/state/changes/statistics)" {
                                                "Include Changes"
                                            }
                                            span#__pm__commit__changes__statistics.token {
                                                xsl:if test="/state/changes/statistics/files != 0" {
                                                    span {
                                                        xsl:value-of select="format-number(/state/changes/statistics/files, '#,###.##')" {}
                                                        " file"
                                                        xsl:if test="/state/changes/statistics/files != 1" {
                                                            "s"
                                                        }
                                                    }
                                                }
                                                xsl:if test="/state/changes/statistics/insertions != 0" {
                                                    span.token--positive {
                                                        "+"
                                                        xsl:value-of select="format-number(/state/changes/statistics/insertions, '#,###.##')" {}
                                                    }
                                                }
                                                xsl:if test="/state/changes/statistics/deletions != 0" {
                                                    span.token--negative {
                                                        "-"
                                                        xsl:value-of select="format-number(/state/changes/statistics/deletions, '#,###.##')" {}
                                                    }
                                                }
                                            }
                                        }
                                        ul {
                                            xsl:apply-templates select="/state/changes/change" {}
                                        }
                                    }
                                }
                            }
                        }
                        xsl:choose {
                            xsl:when test="/state/setup" {
                                div.setup {
                                    h1 "Setup Instructions"
                                    p "Begin by entering your project name, and selecting the appropriate properties."
                                }
                            }
                            xsl:when test="/state/diffs/*" {
                                xsl:apply-templates select="/state/diffs" {}
                            }
                            xsl:otherwise {
                                xsl:apply-templates select="/state/tasks" {}
                            }
                        }
                    }
                }
            }
            xsl:template match="/state/properties/property" {
                div.property {
                    xsl:choose {
                        xsl:when test="name = 'Project'" {
                            dt { label for="__pm__commit__properties--project" "Project" }
                            dd.input {
                                input type="text" id="__pm__commit__properties--project" name="property" data-name="Project" value="{/state/task/property[name[text()='Project']]/value}" {}
                            }
                        }
                        xsl:when test="name = 'Status'" {
                            dt { label for="__pm__commit__properties--status" "Status" }
                            dd.select tabindex="1" {
                                xsl:value-of select="/state/task/property[name[text()='Status']]/value" {}
                                select id="__pm__commit__properties--status" name="property" data-name="Status" {
                                    option value="" {}
                                    xsl:apply-templates "options/option" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Developer'" {
                            dt { label for="__pm__commit__properties--developer" "Developer" }
                            dd.select {
                                input type="text" id="__pm__commit__properties--developer" value="{/state/task/property[name[text()='Developer']]/value}" {}
                                select name="property" data-name="Developer" {
                                    xsl:apply-templates "options/option" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Manager'" {
                            dt { label for="__pm__commit__properties--owner" "Manager" }
                            dd.select {
                                input type="text" id="__pm__commit__properties--manager" value="{/state/task/property[name[text()='Manager']]/value}" {}
                                select name="property" data-name="Manager" {
                                    xsl:apply-templates "options/option" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Description'" {
                            dt { label for="__pm__commit__properties--description" "Description" }
                            dd {
                                textarea id="__pm__commit__properties--description" name="property" data-name="Description" {
                                    xsl:value-of select="/state/task/property[name[text()='Description']]/value" {}
                                }
                            }
                        }
                        xsl:when test="name = 'Estimate'" {
                            dt { label for="__pm__commit__properties--estimate" "Estimate" }
                            dd.input {
                                input type="range" id="__pm__commit__properties--estimate" name="property" data-name="Estimate" value="{/state/task/property[name[text()='Estimate']]/value}" {}
                            }
                        }
                        xsl:otherwise {
                            xsl:variable name="name" select="name/text()" {}
                            xsl:variable name="value" select="/state/task/property[name[text() = $name]]/value" {}
                            input type="hidden" name="property" data-name="{name}" value="{$value}" {}
                        }
                    }
                }
            }
            xsl:template match="/state/properties/property/options/option" {
                xsl:variable name="name" select="./parent::options/parent::property/name" {}
                xsl:element name="option" {
                    xsl:attribute name="value" {
                        xsl:value-of select="." {}
                    }
                    xsl:if test="/state/task/property[name[text()=$name]]/value = ." {
                        xsl:attribute name="selected" "selected"
                    }
                    xsl:value-of select="." {}
                }
            }
            xsl:template match="/state/diffs" {
                div.diff {
                    pre {
                        code {
                            xsl:copy-of select="*" {}
                        }
                    }
                }
            }

            // BEGINS TASKS  ------------------
            xsl:key name="task-status" match="/state/task|/state/tasks/task" use="property[name='Status']/value" {}

            xsl:template match="/state/tasks" {
                xsl:choose {
                    xsl:when test="filter" {
                        input type="hidden" name="filter" data-name="{filter/name}" data-value="{filter/value}" {}
                        ul.list {
                            header {
                                button type="submit" name="filter" {
                                    xsl:value-of select="filter/value" {}
                                }
                            }
                            xsl:apply-templates select="(/state/task|./task)[property[name='Status'] and not(property[name='Status']/value='')]" {
                                xsl:sort select="property[name='Ordinal']/value" {}
                            }
                        }
                        ul.list.list--backlog {
                            header {
                                button type="submit" name="filter" {
                                    "Backlog"
                                }
                            }
                            xsl:apply-templates select="(/state/task|./task)[not(property[name='Status']) or property[name='Status']/value='']" {
                                xsl:sort select="name" {}
                            }
                        }
                    }
                    xsl:otherwise {
                        xsl:for-each select="/state/properties/property[name='Status']/options/option" {
                            xsl:variable name="status" select="./text()" {}
                            xsl:variable name="max" {
                                xsl:for-each select="key('task-status', $status)/property[name='Ordinal']/value" {
                                    xsl:sort select="." data-type="number" order="descending" {}
                                        xsl:if test="position() = 1" {
                                            xsl:value-of select="." {}
                                        }
                                }
                            }
                            xsl:variable name="next" {
                                xsl:choose {
                                    xsl:when test="not($max) or string(number($max)) = 'NaN'" { "1" }
                                    xsl:otherwise {
                                        xsl:value-of select="$max + 1" {}
                                    }
                                }
                            }
                            ul.tiles data-property-name="Status" data-property-value="{.}" {
                                div.column data-property-name="Ordinal" data-property-value="{$next}" {
                                    header {
                                        button type="submit" name="filter" data-name="Status" data-value="{.}" {
                                            xsl:value-of select="." {}
                                        }
                                    }
                                    xsl:for-each select="key('task-status', $status)" {
                                        xsl:sort select="property[name='Ordinal']/value" data-type="number" {}
                                        xsl:call-template name="task" {}
                                    }
                                }
                            }
                        }
                    }
                }
            }
            xsl:template match="task" name="task" {
                xsl:variable name="ordinal" {
                    xsl:choose {
                        xsl:when test="property[name='Ordinal']/value != ''" {
                            xsl:value-of select="property[name='Ordinal']/value" {}
                        }
                        xsl:otherwise {
                            "1"
                        }
                    }
                }
                xsl:variable name="previous" {
                    xsl:for-each select="key('task-status', property[name='Status']/value)/property[name='Ordinal'][number(value) < $ordinal]/value" {
                        xsl:sort select="." data-type="number" order="descending" {}
                        xsl:if test="position() = 1" {
                            xsl:value-of select="." {}
                        }
                    }
                }
                xsl:variable name="next" {
                    xsl:choose {
                        xsl:when test="not($previous) or $previous = ''" {
                            xsl:value-of select="$ordinal div 2" {}
                        }
                        xsl:otherwise {
                            xsl:value-of select="$previous+(number(format-number($ordinal - $previous, '###0.0###;#')) div 2)" {}
                        }
                    }
                }
                xsl:variable name="class" {
                    "tile "
                    xsl:value-of select="concat('tile--status-', translate(property[name='Status']/value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ', 'abcdefghijklmnopqrstuvwxyz-'))" {}
                }
                li class="{$class}" data-property-name="Ordinal" data-property-value="{$next}" {
                    xsl:element name="div" {
                        xsl:attribute name="draggable" "true"
                        xsl:attribute name="tabindex" "99"
                        xsl:attribute name="id" {
                            xsl:value-of select="concat('__pm__task_', name)" {}
                        }
                        xsl:attribute name="class" {
                            " task "
                            xsl:if test="/state/task/name = name" { " selected " }
                        }
                        xsl:attribute name="data-name" {
                            xsl:value-of select="name" {}
                        }
                        div class="task__name" {
                            xsl:value-of select="name" {}
                        }
                        div.task__property--description {
                            xsl:copy-of select="property[name='Description']/value" {}
                        }
                        xsl:if test="property[name='Estimate']/value != ''" {
                            div.task__property--estimate {
                                xsl:copy-of select="property[name='Estimate']/value" {}
                            }
                        }
                    }
                }
            }

            // BEGINS LOGS  ------------------
            xsl:template match="/state/log/commit" {
                li {
                    xsl:if test="./preceding-sibling::commit[1]/user/email=user/email and ./preceding-sibling::commit[1]/timestamp - timestamp > -7200" {
                        xsl:attribute name="class" "continuous"
                    }
                    img src="{user/image}" alt="{user/name} <{user/email}>" {}
                    xsl:if test="changes" {
                        button.button--medium.attachments {
                            svg id="i-paperclip" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="20" height="20" fill="none" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" {
                                path d="M10 9 L10 24 C10 28 13 30 16 30 19 30 22 28 22 24 L22 6 C22 3 20 2 18 2 16 2 14 3 14 6 L14 23 C14 24 15 25 16 25 17 25 18 24 18 23 L18 9" {}
                            }
                        }
                    }
                    blockquote {
                        a.user__name href="#" {
                            xsl:value-of select="user/name" {}
                        }
                        xsl:value-of select="message" {}
                    }
                    xsl:if test="task" {
                        dl.tasks {
                            xsl:apply-templates select="task[property]" {}
                        }
                    }
                    div {
                        time datetime="{localtime}" {
                            xsl:value-of select="localtime" {}
                        }
                    }
                }
            }
            xsl:template match="/state/log/commit/task" {
                dt { xsl:value-of select="name" {} }
                dd {
                    ul.properties {
                        xsl:apply-templates select="property" {}
                    }
                }
            }
            xsl:template match="/state/log/commit/task/property" {
                li.token {
                    span.name { xsl:value-of select="name" {} }
                    xsl:if test="before" {
                        span.before.token--neutral { xsl:value-of select="before" {} }
                    }
                    span.after.token--positive { xsl:value-of select="value" {} }
                }
            }
            xsl:template match="/state/changes/change" {
                li tabindex="3" id="{concat('__pm__changes__checkbox--', @id)}" {
                    xsl:element name="input" {
                        xsl:attribute name="name" { "include" }
                        xsl:attribute name="id" {
                            xsl:value-of select="concat('__pm__changes--', @id)" {}
                        }
                        xsl:attribute name="value" {
                            xsl:value-of select="path" {}
                        }
                        xsl:attribute name="tabindex" "-1"
                        xsl:attribute name="type" "checkbox"
                        xsl:if test="included='true'" {
                            xsl:attribute name="checked" {}
                        }
                        xsl:if test="removal='true'" {
                            xsl:attribute name="class" { "delete" }
                        }
                    }
                    label for="__pm__changes--{@id}" {
                        xsl:value-of select="path" {}
                    }
                    button.button--tiny name="diff" value="{path}" { " +10 -10" }
                }
            }
        }
    };
    format!("{}{}", XML, markup.into_string())
}

<|endoftext|>


<commit_before>extern crate rand;

use unicode_segmentation::UnicodeSegmentation;
use config::*;
use rand::*;
use rand::distributions::{Weighted, WeightedChoice, IndependentSample};
use regex::Regex;

pub struct Word {
    pub syllables : String,
    pub graphemes : String,
    pub syllable_rewrite_history : Vec<(Rewrite, String)>,
    pub grapheme_rewrite_history : Vec<(Rewrite, String)>,
    pub syllable_rejects : Vec<String>,
    pub grapheme_rejects : Vec<String>,
    pub syllable_count : usize,
}

pub struct WordFactory {
    pub first_syllable_list : Vec<Weighted<String>>,
    pub last_syllable_list : Vec<Weighted<String>>,
    pub normal_syllable_list : Vec<Weighted<String>>,

    pub graphemes : Vec<(String, Vec<Weighted<String>>)>,

    pub rewrites : RewriteGroup,

    pub rejects : RejectGroup,

    pub generate_settings : GenerateSettings,
}

pub trait WordGenerator {
//    fn new(
//        &mut self,
//        first_syllables : &Vec<Weighted<String>>,
//        last_syllables : &Vec<Weighted<String>>,
//        normal_syllables : &Vec<Weighted<String>>,
//        graphemes : &Vec<(String, Vec<Weighted<String>>)>,
//        settings: &GenerateSettings);
    fn generate_syllables(&self, word: &mut Word);
    fn generate_graphemes(&self, word: &mut Word);
    fn rewrite_syllables(&self, word: &mut Word);
    fn rewrite_graphemes(&self, word: &mut Word);
    fn mark_syllable_rejects(&self, word: &mut Word);
    fn mark_grapheme_rejects(&self, word: &mut Word);
}

impl WordGenerator for WordFactory {
    fn generate_syllables(&self, word: &mut Word) {
        let mut rng = rand::thread_rng();

        let mut current_chance_for_syllable : f32 = 1.0;
        let mut syllable_count : usize = 0;
        let mut syllable_loop_iter = rng.gen_iter::<f32>();

        loop {
            let result = syllable_loop_iter.next();
            match result {
                Some(x) => {
                    if x < current_chance_for_syllable && syllable_count < self.generate_settings.max_syllables {
                        syllable_count = syllable_count + 1;
                        current_chance_for_syllable *= 1.0-self.generate_settings.syllable_decay_rate;
                    } else {
                        break
                    }
                }
                None => { break }
            }
        }
        word.syllable_count = syllable_count;
        word.syllables = match syllable_count {
            1 => {
                get_random_from_weighted(&self.first_syllable_list)
            },
            2 => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
            _ => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                for _ in 1..(syllable_count - 1) {
                    temp_word.push_str(&get_random_from_weighted(&self.normal_syllable_list));
                }
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
        };
    }

    fn generate_graphemes(&self, word: &mut Word) {
        assert!(!word.syllables.is_empty(), "Cannot call generate_graphemes on a word without a syllable string");

        let mut grapheme_vector = Vec::new();

        //explicitly a separate variable because putting the call to get_word_syllables in the graphemes constructor fails the type coercion to &str
        let word_syllables_str : &str = &get_word_syllables(&word);

        for grapheme in UnicodeSegmentation::graphemes(word_syllables_str, false) {
            grapheme_vector.push(syllable_element_to_random_grapheme(&self.graphemes, &grapheme.to_string()));
        }

        word.graphemes = grapheme_vector.iter().fold(String::new(), |accumulator : String, character| { let mut new_str = accumulator.to_string(); new_str.push_str(&character); new_str});
    }
    fn rewrite_syllables(&self, word: &mut Word) {
        for rewrite in &self.rewrites.syllable_rewrites {
            let rewritten_string = apply_single_rewrite(&rewrite.pattern, &rewrite.replace, &get_word_syllables(&word));
            match rewritten_string {
                Some(result) => word.syllable_rewrite_history.push((rewrite.clone(), result)),
                _ => ()
            }
        }
    }
    fn rewrite_graphemes(&self, word: &mut Word) {
        for rewrite in &self.rewrites.grapheme_rewrites {
            let rewritten_string = apply_single_rewrite(&rewrite.pattern, &rewrite.replace, &get_word_graphemes(&word));
            match rewritten_string {
                Some(result) => word.grapheme_rewrite_history.push((rewrite.clone(), result)),
                _ => ()
            }
        }
    }
    fn mark_syllable_rejects(&self, word: &mut Word) {
        for reject in &self.rejects.syllable_rejects {
            let reject_regex : Regex = match Regex::new(&reject) {
                Ok(res) => res,
                Err(err) => panic!("Error '{}' with regex '{}' in a reject, please verify that it is valid", err, &reject)
            };
            if reject_regex.is_match(&get_word_syllables(&word)) {
                word.syllable_rejects.push(reject.clone());
            }
        }
    }
    fn mark_grapheme_rejects(&self, word: &mut Word) {
        for reject in &self.rejects.grapheme_rejects {
            let reject_regex : Regex = match Regex::new(&reject) {
                Ok(res) => res,
                Err(err) => panic!("Error '{}' with regex '{}' in a reject, please verify that it is valid", err, &reject)
            };
            if reject_regex.is_match(&get_word_graphemes(&word)) {
                word.grapheme_rejects.push(reject.clone());
            }
        }
    }
}

fn apply_single_rewrite(rewrite : &String, replace : &String, source : &String) -> Option<String> {
    let rewrite_regex : Regex = match Regex::new(&rewrite) {
        Ok(res) => res,
        Err(err) => panic!("Error '{}' with regex '{}' in a rewrite, please verify that it is valid", err, &rewrite)
    };
    let replace_str :&str = &replace;
    match rewrite_regex.is_match(&source) {
        true => Some(rewrite_regex.replace_all(&source, replace_str)),
        false => None,
    }
}

fn syllable_element_to_random_grapheme(grapheme_groups : &Vec<(String, Vec<Weighted<String>>)>, syllable_element : &String) -> String {
    let matching_grapheme_group : Vec<Weighted<String>> = match grapheme_groups.iter().filter(|ref i| i.0 == *syllable_element).map(|ref i| i.1.clone()).last() {
        Some(res) => res,

        None => panic!("No grapheme group found for syllable element {}, check your syllable sets and rewrites", &syllable_element)
    };
    get_random_from_weighted(&matching_grapheme_group)
}

fn get_random_from_weighted(values : &Vec<Weighted<String>>) -> String {

    let mut local_values : Vec<Weighted<String>> = values.clone();

    let selector : WeightedChoice<String> = WeightedChoice::new(&mut local_values);
    let mut rng = rand::thread_rng();

    selector.ind_sample(&mut rng)
}

//temporary function to properly obtain rewritten syllables and graphemes for a word, needs to be refactored into a word trait
pub fn get_word_syllables(word : &Word) -> String {
    match word.syllable_rewrite_history.last() {
        Some(result) => result.1.clone(),
        None => word.syllables.clone()
    }
}
pub fn get_word_graphemes(word : &Word) -> String {
    match word.grapheme_rewrite_history.last() {
        Some(result) => result.1.clone(),
        None => word.graphemes.clone()
    }
}
<commit_msg>added comment regarding a string cowercion workaround for clarity<commit_after>extern crate rand;

use unicode_segmentation::UnicodeSegmentation;
use config::*;
use rand::*;
use rand::distributions::{Weighted, WeightedChoice, IndependentSample};
use regex::Regex;

pub struct Word {
    pub syllables : String,
    pub graphemes : String,
    pub syllable_rewrite_history : Vec<(Rewrite, String)>,
    pub grapheme_rewrite_history : Vec<(Rewrite, String)>,
    pub syllable_rejects : Vec<String>,
    pub grapheme_rejects : Vec<String>,
    pub syllable_count : usize,
}

pub struct WordFactory {
    pub first_syllable_list : Vec<Weighted<String>>,
    pub last_syllable_list : Vec<Weighted<String>>,
    pub normal_syllable_list : Vec<Weighted<String>>,

    pub graphemes : Vec<(String, Vec<Weighted<String>>)>,

    pub rewrites : RewriteGroup,

    pub rejects : RejectGroup,

    pub generate_settings : GenerateSettings,
}

pub trait WordGenerator {
//    fn new(
//        &mut self,
//        first_syllables : &Vec<Weighted<String>>,
//        last_syllables : &Vec<Weighted<String>>,
//        normal_syllables : &Vec<Weighted<String>>,
//        graphemes : &Vec<(String, Vec<Weighted<String>>)>,
//        settings: &GenerateSettings);
    fn generate_syllables(&self, word: &mut Word);
    fn generate_graphemes(&self, word: &mut Word);
    fn rewrite_syllables(&self, word: &mut Word);
    fn rewrite_graphemes(&self, word: &mut Word);
    fn mark_syllable_rejects(&self, word: &mut Word);
    fn mark_grapheme_rejects(&self, word: &mut Word);
}

impl WordGenerator for WordFactory {
    fn generate_syllables(&self, word: &mut Word) {
        let mut rng = rand::thread_rng();

        let mut current_chance_for_syllable : f32 = 1.0;
        let mut syllable_count : usize = 0;
        let mut syllable_loop_iter = rng.gen_iter::<f32>();

        loop {
            let result = syllable_loop_iter.next();
            match result {
                Some(x) => {
                    if x < current_chance_for_syllable && syllable_count < self.generate_settings.max_syllables {
                        syllable_count = syllable_count + 1;
                        current_chance_for_syllable *= 1.0-self.generate_settings.syllable_decay_rate;
                    } else {
                        break
                    }
                }
                None => { break }
            }
        }
        word.syllable_count = syllable_count;
        word.syllables = match syllable_count {
            1 => {
                get_random_from_weighted(&self.first_syllable_list)
            },
            2 => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
            _ => {
                let mut temp_word = get_random_from_weighted(&self.first_syllable_list);
                for _ in 1..(syllable_count - 1) {
                    temp_word.push_str(&get_random_from_weighted(&self.normal_syllable_list));
                }
                temp_word.push_str(&get_random_from_weighted(&self.last_syllable_list));
                temp_word
            },
        };
    }

    fn generate_graphemes(&self, word: &mut Word) {
        assert!(!word.syllables.is_empty(), "Cannot call generate_graphemes on a word without a syllable string");

        let mut grapheme_vector = Vec::new();

        //explicitly a separate variable because putting the call to get_word_syllables in the graphemes constructor fails the type coercion to &str
        let word_syllables_str : &str = &get_word_syllables(&word);

        for grapheme in UnicodeSegmentation::graphemes(word_syllables_str, false) {
            grapheme_vector.push(syllable_element_to_random_grapheme(&self.graphemes, &grapheme.to_string()));
        }

        word.graphemes = grapheme_vector.iter().fold(String::new(), |accumulator : String, character| { let mut new_str = accumulator.to_string(); new_str.push_str(&character); new_str});
    }
    fn rewrite_syllables(&self, word: &mut Word) {
        for rewrite in &self.rewrites.syllable_rewrites {
            let rewritten_string = apply_single_rewrite(&rewrite.pattern, &rewrite.replace, &get_word_syllables(&word));
            match rewritten_string {
                Some(result) => word.syllable_rewrite_history.push((rewrite.clone(), result)),
                _ => ()
            }
        }
    }
    fn rewrite_graphemes(&self, word: &mut Word) {
        for rewrite in &self.rewrites.grapheme_rewrites {
            let rewritten_string = apply_single_rewrite(&rewrite.pattern, &rewrite.replace, &get_word_graphemes(&word));
            match rewritten_string {
                Some(result) => word.grapheme_rewrite_history.push((rewrite.clone(), result)),
                _ => ()
            }
        }
    }
    fn mark_syllable_rejects(&self, word: &mut Word) {
        for reject in &self.rejects.syllable_rejects {
            let reject_regex : Regex = match Regex::new(&reject) {
                Ok(res) => res,
                Err(err) => panic!("Error '{}' with regex '{}' in a reject, please verify that it is valid", err, &reject)
            };
            if reject_regex.is_match(&get_word_syllables(&word)) {
                word.syllable_rejects.push(reject.clone());
            }
        }
    }
    fn mark_grapheme_rejects(&self, word: &mut Word) {
        for reject in &self.rejects.grapheme_rejects {
            let reject_regex : Regex = match Regex::new(&reject) {
                Ok(res) => res,
                Err(err) => panic!("Error '{}' with regex '{}' in a reject, please verify that it is valid", err, &reject)
            };
            if reject_regex.is_match(&get_word_graphemes(&word)) {
                word.grapheme_rejects.push(reject.clone());
            }
        }
    }
}

fn apply_single_rewrite(rewrite : &String, replace : &String, source : &String) -> Option<String> {
    let rewrite_regex : Regex = match Regex::new(&rewrite) {
        Ok(res) => res,
        Err(err) => panic!("Error '{}' with regex '{}' in a rewrite, please verify that it is valid", err, &rewrite)
    };

    //explicitly separating out a coercion from String to &str because rust does not perform the coercian in the below replace_all call
    let replace_str :&str = &replace;

    match rewrite_regex.is_match(&source) {
        true => Some(rewrite_regex.replace_all(&source, replace_str)),
        false => None,

    }
}
fn syllable_element_to_random_grapheme(grapheme_groups : &Vec<(String, Vec<Weighted<String>>)>, syllable_element : &String) -> String {
    let matching_grapheme_group : Vec<Weighted<String>> = match grapheme_groups.iter().filter(|ref i| i.0 == *syllable_element).map(|ref i| i.1.clone()).last() {
        Some(res) => res,

        None => panic!("No grapheme group found for syllable element {}, check your syllable sets and rewrites", &syllable_element)
    };
    get_random_from_weighted(&matching_grapheme_group)
}

fn get_random_from_weighted(values : &Vec<Weighted<String>>) -> String {

    let mut local_values : Vec<Weighted<String>> = values.clone();

    let selector : WeightedChoice<String> = WeightedChoice::new(&mut local_values);
    let mut rng = rand::thread_rng();

    selector.ind_sample(&mut rng)
}

//temporary function to properly obtain rewritten syllables and graphemes for a word, needs to be refactored into a word trait
pub fn get_word_syllables(word : &Word) -> String {
    match word.syllable_rewrite_history.last() {
        Some(result) => result.1.clone(),
        None => word.syllables.clone()
    }
}
pub fn get_word_graphemes(word : &Word) -> String {
    match word.grapheme_rewrite_history.last() {
        Some(result) => result.1.clone(),
        None => word.graphemes.clone()
    }
}
<|endoftext|>