<commit_before>// The MIT License (MIT)

// Copyright (c) 2014 Y. T. CHUNG <zonyitoo@gmail.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use std::sync::{Arc, Mutex, TaskPool};
use std::thread::Thread;
// use std::sync::atomic::{AtomicOption, SeqCst};
use std::io::net::addrinfo::get_host_addresses;
use std::io::net::ip::IpAddr;

use collect::LruCache;

use config::DEFAULT_DNS_CACHE_CAPACITY;

const TASK_POOL_SIZE: usize = 4;

struct DnsLruCache {
    cache: LruCache<String, Vec<IpAddr>>,
    totally_matched: usize,
    totally_missed: usize,
}

pub struct CachedDns {
    lru_cache: Arc<Mutex<DnsLruCache>>,
    pool: TaskPool,
}

impl CachedDns {
    pub fn new() -> CachedDns {
        CachedDns {
            lru_cache: Arc::new(Mutex::new(DnsLruCache {
                cache: LruCache::new(DEFAULT_DNS_CACHE_CAPACITY),
                totally_missed: 0,
                totally_matched: 0,
            })),
            pool: TaskPool::new(TASK_POOL_SIZE),
        }
    }

    pub fn with_capacity(cache_capacity: usize) -> CachedDns {
        CachedDns {
            lru_cache: Arc::new(Mutex::new(DnsLruCache {
                cache: LruCache::new(cache_capacity),
                totally_missed: 0,
                totally_matched: 0,
            })),
            pool: TaskPool::new(TASK_POOL_SIZE),
        }
    }

    pub fn resolve(&self, addr: &str) -> Option<Vec<IpAddr>> {
        let addr_string = addr.to_string();

        {
            let mut cache = self.lru_cache.lock().unwrap();
            match cache.cache.get(&addr_string).map(|x| x.clone()) {
                Some(addrs) => {
                    cache.totally_matched += 1;
                    debug!("DNS cache matched!: {}", addr_string);
                    debug!("DNS cache matched: {}, missed: {}", cache.totally_matched, cache.totally_missed);
                    return Some(addrs)
                },
                None => {
                    cache.totally_missed += 1;
                    debug!("DNS cache missed!: {}", addr_string);
                    debug!("DNS cache matched: {}, missed: {}", cache.totally_matched, cache.totally_missed);
                }
            }
        }

        let addrs = match get_host_addresses(addr) {
            Ok(addrs) => addrs,
            Err(err) => {
                error!("Failed to resolve {}: {}", addr, err);
                return None;
            }
        };

        let cloned_mutex = self.lru_cache.clone();
        let cloned_addr = addrs.clone();
        self.pool.execute(move || {
            let mut cache = cloned_mutex.lock().unwrap();
            cache.cache.insert(addr_string, cloned_addr);
        });
        Some(addrs)
    }
}

unsafe impl Send for CachedDns {}
unsafe impl Sync for CachedDns {}
<commit_msg>removed useless import<commit_after>// The MIT License (MIT)

// Copyright (c) 2014 Y. T. CHUNG <zonyitoo@gmail.com>

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use std::sync::{Arc, Mutex, TaskPool};
// use std::sync::atomic::{AtomicOption, SeqCst};
use std::io::net::addrinfo::get_host_addresses;
use std::io::net::ip::IpAddr;

use collect::LruCache;

use config::DEFAULT_DNS_CACHE_CAPACITY;

const TASK_POOL_SIZE: usize = 4;

struct DnsLruCache {
    cache: LruCache<String, Vec<IpAddr>>,
    totally_matched: usize,
    totally_missed: usize,
}

pub struct CachedDns {
    lru_cache: Arc<Mutex<DnsLruCache>>,
    pool: TaskPool,
}

impl CachedDns {
    pub fn new() -> CachedDns {
        CachedDns {
            lru_cache: Arc::new(Mutex::new(DnsLruCache {
                cache: LruCache::new(DEFAULT_DNS_CACHE_CAPACITY),
                totally_missed: 0,
                totally_matched: 0,
            })),
            pool: TaskPool::new(TASK_POOL_SIZE),
        }
    }

    pub fn with_capacity(cache_capacity: usize) -> CachedDns {
        CachedDns {
            lru_cache: Arc::new(Mutex::new(DnsLruCache {
                cache: LruCache::new(cache_capacity),
                totally_missed: 0,
                totally_matched: 0,
            })),
            pool: TaskPool::new(TASK_POOL_SIZE),
        }
    }

    pub fn resolve(&self, addr: &str) -> Option<Vec<IpAddr>> {
        let addr_string = addr.to_string();

        {
            let mut cache = self.lru_cache.lock().unwrap();
            match cache.cache.get(&addr_string).map(|x| x.clone()) {
                Some(addrs) => {
                    cache.totally_matched += 1;
                    debug!("DNS cache matched!: {}", addr_string);
                    debug!("DNS cache matched: {}, missed: {}", cache.totally_matched, cache.totally_missed);
                    return Some(addrs)
                },
                None => {
                    cache.totally_missed += 1;
                    debug!("DNS cache missed!: {}", addr_string);
                    debug!("DNS cache matched: {}, missed: {}", cache.totally_matched, cache.totally_missed);
                }
            }
        }

        let addrs = match get_host_addresses(addr) {
            Ok(addrs) => addrs,
            Err(err) => {
                error!("Failed to resolve {}: {}", addr, err);
                return None;
            }
        };

        let cloned_mutex = self.lru_cache.clone();
        let cloned_addr = addrs.clone();
        self.pool.execute(move || {
            let mut cache = cloned_mutex.lock().unwrap();
            cache.cache.insert(addr_string, cloned_addr);
        });
        Some(addrs)
    }
}

unsafe impl Send for CachedDns {}
unsafe impl Sync for CachedDns {}
<|endoftext|>


<commit_before>#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
use core::arch::aarch64::*;

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
unsafe fn idct8(data: &mut [int16x8_t; 8]) {
    // The fixed-point constants here are obtained by taking the fractional part of the constants
    // from the non-SIMD implementation and scaling them up by 1<<15. This is because
    // vqrdmulhq_n_s16(a, b) is effectively equivalent to (a*b)>>15 (except for possibly some
    // slight differences in rounding).

    // The code here is effectively equivalent to the calls to "kernel" in idct.rs, except that it
    // doesn't apply any further scaling and fixed point constants have a different precision.

    let p2 = data[2];
    let p3 = data[6];
    let p1 = vqrdmulhq_n_s16(vqaddq_s16(p2, p3), 17734); // 0.5411961
    let t2 = vqsubq_s16(
        vqsubq_s16(p1, p3),
        vqrdmulhq_n_s16(p3, 27779), // 0.847759065
    );
    let t3 = vqaddq_s16(p1, vqrdmulhq_n_s16(p2, 25079)); // 0.765366865

    let p2 = data[0];
    let p3 = data[4];
    let t0 = vqaddq_s16(p2, p3);
    let t1 = vqsubq_s16(p2, p3);

    let x0 = vqaddq_s16(t0, t3);
    let x3 = vqsubq_s16(t0, t3);
    let x1 = vqaddq_s16(t1, t2);
    let x2 = vqsubq_s16(t1, t2);

    let t0 = data[7];
    let t1 = data[5];
    let t2 = data[3];
    let t3 = data[1];

    let p3 = vqaddq_s16(t0, t2);
    let p4 = vqaddq_s16(t1, t3);
    let p1 = vqaddq_s16(t0, t3);
    let p2 = vqaddq_s16(t1, t2);
    let p5 = vqaddq_s16(p3, p4);
    let p5 = vqaddq_s16(p5, vqrdmulhq_n_s16(p5, 5763)); // 0.175875602

    let t0 = vqrdmulhq_n_s16(t0, 9786); // 0.298631336
    let t1 = vqaddq_s16(
        vqaddq_s16(t1, t1),
        vqrdmulhq_n_s16(t1, 1741), // 0.053119869
    );
    let t2 = vqaddq_s16(
        vqaddq_s16(t2, vqaddq_s16(t2, t2)),
        vqrdmulhq_n_s16(t2, 2383), // 0.072711026
    );
    let t3 = vqaddq_s16(t3, vqrdmulhq_n_s16(t3, 16427)); // 0.501321110

    let p1 = vqsubq_s16(p5, vqrdmulhq_n_s16(p1, 29490)); // 0.899976223
    let p2 = vqsubq_s16(
        vqsubq_s16(vqsubq_s16(p5, p2), p2),
        vqrdmulhq_n_s16(p2, 18446), // 0.562915447
    );

    let p3 = vqsubq_s16(
        vqrdmulhq_n_s16(p3, -31509), // -0.961570560
        p3,
    );
    let p4 = vqrdmulhq_n_s16(p4, -12785); // -0.390180644

    let t3 = vqaddq_s16(vqaddq_s16(p1, p4), t3);
    let t2 = vqaddq_s16(vqaddq_s16(p2, p3), t2);
    let t1 = vqaddq_s16(vqaddq_s16(p2, p4), t1);
    let t0 = vqaddq_s16(vqaddq_s16(p1, p3), t0);

    data[0] = vqaddq_s16(x0, t3);
    data[7] = vqsubq_s16(x0, t3);
    data[1] = vqaddq_s16(x1, t2);
    data[6] = vqsubq_s16(x1, t2);
    data[2] = vqaddq_s16(x2, t1);
    data[5] = vqsubq_s16(x2, t1);
    data[3] = vqaddq_s16(x3, t0);
    data[4] = vqsubq_s16(x3, t0);
}

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
unsafe fn transpose8(data: &mut [int16x8_t; 8]) {
    // Use NEON's 2x2 matrix transposes (vtrn) to do the transposition in each 4x4 block, then
    // combine the 4x4 blocks.
    let a01 = vtrnq_s16(data[0], data[1]);
    let a23 = vtrnq_s16(data[2], data[3]);

    let four0 = vtrnq_s32(vreinterpretq_s32_s16(a01.0), vreinterpretq_s32_s16(a23.0));
    let four1 = vtrnq_s32(vreinterpretq_s32_s16(a01.1), vreinterpretq_s32_s16(a23.1));

    let a45 = vtrnq_s16(data[4], data[5]);
    let a67 = vtrnq_s16(data[6], data[7]);

    let four2 = vtrnq_s32(vreinterpretq_s32_s16(a45.0), vreinterpretq_s32_s16(a67.0));
    let four3 = vtrnq_s32(vreinterpretq_s32_s16(a45.1), vreinterpretq_s32_s16(a67.1));

    data[0] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four0.0), vget_low_s32(four2.0)));
    data[1] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four1.0), vget_low_s32(four3.0)));
    data[2] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four0.1), vget_low_s32(four2.1)));
    data[3] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four1.1), vget_low_s32(four3.1)));
    data[4] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four0.0), vget_high_s32(four2.0)));
    data[5] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four1.0), vget_high_s32(four3.0)));
    data[6] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four0.1), vget_high_s32(four2.1)));
    data[7] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four1.1), vget_high_s32(four3.1)));
}

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
pub unsafe fn dequantize_and_idct_block_8x8(
    coefficients: &[i16; 64],
    quantization_table: &[u16; 64],
    output_linestride: usize,
    output: &mut [u8],
) {
    // The loop below will write to positions [output_linestride * i, output_linestride * i + 8)
    // for 0<=i<8. Thus, the last accessed position is at an offset of output_linestrade * 7 + 7,
    // and if that position is in-bounds, so are all other accesses.
    assert!(
        output.len()
            > output_linestride
                .checked_mul(7)
                .unwrap()
                .checked_add(7)
                .unwrap()
    );

    const SHIFT: i32 = 3;

    // Read the DCT coefficients, scale them up and dequantize them.
    let mut data = [vdupq_n_s16(0); 8];
    for i in 0..8 {
        data[i] = vshlq_n_s16(
            vmulq_s16(
                vld1q_s16(coefficients.as_ptr().wrapping_add(i * 8)),
                vreinterpretq_s16_u16(vld1q_u16(quantization_table.as_ptr().wrapping_add(i * 8))),
            ),
            SHIFT,
        );
    }

    // Usual column IDCT - transpose - column IDCT - transpose approach.
    idct8(&mut data);
    transpose8(&mut data);
    idct8(&mut data);
    transpose8(&mut data);

    for i in 0..8 {
        // The two passes of the IDCT algorithm give us a factor of 8, so the shift here is
        // increased by 3.
        // As values will be stored in a u8, they need to be 128-centered and not 0-centered.
        // We add 128 with the appropriate shift for that purpose.
        const OFFSET: i16 = 128 << (SHIFT + 3);
        // We want rounding right shift, so we should add (1/2) << (SHIFT+3) before shifting.
        const ROUNDING_BIAS: i16 = (1 << (SHIFT + 3)) >> 1;

        let data_with_offset = vqaddq_s16(data[i], vdupq_n_s16(OFFSET + ROUNDING_BIAS));

        vst1_u8(
            output.as_mut_ptr().wrapping_add(output_linestride * i),
            vqshrun_n_s16(data_with_offset, SHIFT + 3),
        );
    }
}

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
pub unsafe fn color_convert_line_ycbcr(y: &[u8], cb: &[u8], cr: &[u8], output: &mut [u8]) -> usize {
    assert!(output.len() % 3 == 0);
    let num = output.len() / 3;
    assert!(num <= y.len());
    assert!(num <= cb.len());
    assert!(num <= cr.len());
    let num_vecs = num / 8;

    for i in 0..num_vecs {
        const SHIFT: i32 = 6;
        // Load.
        let y = vld1_u8(y.as_ptr().wrapping_add(i * 8));
        let cb = vld1_u8(cb.as_ptr().wrapping_add(i * 8));
        let cr = vld1_u8(cr.as_ptr().wrapping_add(i * 8));

        // Convert to 16 bit and shift.
        let y = vreinterpretq_s16_u16(vshll_n_u8(y, SHIFT));
        let cb = vreinterpretq_s16_u16(vshll_n_u8(cb, SHIFT));
        let cr = vreinterpretq_s16_u16(vshll_n_u8(cr, SHIFT));

        // Add offsets
        let y = vqaddq_s16(y, vdupq_n_s16((1 << SHIFT) >> 1));
        let c128 = vdupq_n_s16(128 << SHIFT);
        let cb = vqsubq_s16(cb, c128);
        let cr = vqsubq_s16(cr, c128);

        // Compute cr * 1.402, cb * 0.34414, cr * 0.71414, cb * 1.772
        let cr_140200 = vqaddq_s16(vqrdmulhq_n_s16(cr, 13173), cr);
        let cb_034414 = vqrdmulhq_n_s16(cb, 11276);
        let cr_071414 = vqrdmulhq_n_s16(cr, 23401);
        let cb_177200 = vqaddq_s16(vqrdmulhq_n_s16(cb, 25297), cb);

        // Last conversion step.
        let r = vqaddq_s16(y, cr_140200);
        let g = vqsubq_s16(y, vqaddq_s16(cb_034414, cr_071414));
        let b = vqaddq_s16(y, cb_177200);

        // Shift back and convert to u8.
        let r = vqshrun_n_s16(r, SHIFT);
        let g = vqshrun_n_s16(g, SHIFT);
        let b = vqshrun_n_s16(b, SHIFT);

        // Shuffle + store.
        vst3_u8(
            output.as_mut_ptr().wrapping_add(24 * i),
            uint8x8x3_t(r, g, b),
        );
    }

    num_vecs * 8
}
<commit_msg>Update aarch64 feature combination<commit_after>#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
use core::arch::aarch64::*;

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
#[target_feature(enable = "fp")]
unsafe fn idct8(data: &mut [int16x8_t; 8]) {
    // The fixed-point constants here are obtained by taking the fractional part of the constants
    // from the non-SIMD implementation and scaling them up by 1<<15. This is because
    // vqrdmulhq_n_s16(a, b) is effectively equivalent to (a*b)>>15 (except for possibly some
    // slight differences in rounding).

    // The code here is effectively equivalent to the calls to "kernel" in idct.rs, except that it
    // doesn't apply any further scaling and fixed point constants have a different precision.

    let p2 = data[2];
    let p3 = data[6];
    let p1 = vqrdmulhq_n_s16(vqaddq_s16(p2, p3), 17734); // 0.5411961
    let t2 = vqsubq_s16(
        vqsubq_s16(p1, p3),
        vqrdmulhq_n_s16(p3, 27779), // 0.847759065
    );
    let t3 = vqaddq_s16(p1, vqrdmulhq_n_s16(p2, 25079)); // 0.765366865

    let p2 = data[0];
    let p3 = data[4];
    let t0 = vqaddq_s16(p2, p3);
    let t1 = vqsubq_s16(p2, p3);

    let x0 = vqaddq_s16(t0, t3);
    let x3 = vqsubq_s16(t0, t3);
    let x1 = vqaddq_s16(t1, t2);
    let x2 = vqsubq_s16(t1, t2);

    let t0 = data[7];
    let t1 = data[5];
    let t2 = data[3];
    let t3 = data[1];

    let p3 = vqaddq_s16(t0, t2);
    let p4 = vqaddq_s16(t1, t3);
    let p1 = vqaddq_s16(t0, t3);
    let p2 = vqaddq_s16(t1, t2);
    let p5 = vqaddq_s16(p3, p4);
    let p5 = vqaddq_s16(p5, vqrdmulhq_n_s16(p5, 5763)); // 0.175875602

    let t0 = vqrdmulhq_n_s16(t0, 9786); // 0.298631336
    let t1 = vqaddq_s16(
        vqaddq_s16(t1, t1),
        vqrdmulhq_n_s16(t1, 1741), // 0.053119869
    );
    let t2 = vqaddq_s16(
        vqaddq_s16(t2, vqaddq_s16(t2, t2)),
        vqrdmulhq_n_s16(t2, 2383), // 0.072711026
    );
    let t3 = vqaddq_s16(t3, vqrdmulhq_n_s16(t3, 16427)); // 0.501321110

    let p1 = vqsubq_s16(p5, vqrdmulhq_n_s16(p1, 29490)); // 0.899976223
    let p2 = vqsubq_s16(
        vqsubq_s16(vqsubq_s16(p5, p2), p2),
        vqrdmulhq_n_s16(p2, 18446), // 0.562915447
    );

    let p3 = vqsubq_s16(
        vqrdmulhq_n_s16(p3, -31509), // -0.961570560
        p3,
    );
    let p4 = vqrdmulhq_n_s16(p4, -12785); // -0.390180644

    let t3 = vqaddq_s16(vqaddq_s16(p1, p4), t3);
    let t2 = vqaddq_s16(vqaddq_s16(p2, p3), t2);
    let t1 = vqaddq_s16(vqaddq_s16(p2, p4), t1);
    let t0 = vqaddq_s16(vqaddq_s16(p1, p3), t0);

    data[0] = vqaddq_s16(x0, t3);
    data[7] = vqsubq_s16(x0, t3);
    data[1] = vqaddq_s16(x1, t2);
    data[6] = vqsubq_s16(x1, t2);
    data[2] = vqaddq_s16(x2, t1);
    data[5] = vqsubq_s16(x2, t1);
    data[3] = vqaddq_s16(x3, t0);
    data[4] = vqsubq_s16(x3, t0);
}

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
#[target_feature(enable = "fp")]
unsafe fn transpose8(data: &mut [int16x8_t; 8]) {
    // Use NEON's 2x2 matrix transposes (vtrn) to do the transposition in each 4x4 block, then
    // combine the 4x4 blocks.
    let a01 = vtrnq_s16(data[0], data[1]);
    let a23 = vtrnq_s16(data[2], data[3]);

    let four0 = vtrnq_s32(vreinterpretq_s32_s16(a01.0), vreinterpretq_s32_s16(a23.0));
    let four1 = vtrnq_s32(vreinterpretq_s32_s16(a01.1), vreinterpretq_s32_s16(a23.1));

    let a45 = vtrnq_s16(data[4], data[5]);
    let a67 = vtrnq_s16(data[6], data[7]);

    let four2 = vtrnq_s32(vreinterpretq_s32_s16(a45.0), vreinterpretq_s32_s16(a67.0));
    let four3 = vtrnq_s32(vreinterpretq_s32_s16(a45.1), vreinterpretq_s32_s16(a67.1));

    data[0] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four0.0), vget_low_s32(four2.0)));
    data[1] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four1.0), vget_low_s32(four3.0)));
    data[2] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four0.1), vget_low_s32(four2.1)));
    data[3] = vreinterpretq_s16_s32(vcombine_s32(vget_low_s32(four1.1), vget_low_s32(four3.1)));
    data[4] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four0.0), vget_high_s32(four2.0)));
    data[5] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four1.0), vget_high_s32(four3.0)));
    data[6] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four0.1), vget_high_s32(four2.1)));
    data[7] = vreinterpretq_s16_s32(vcombine_s32(vget_high_s32(four1.1), vget_high_s32(four3.1)));
}

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
#[target_feature(enable = "fp")]
pub unsafe fn dequantize_and_idct_block_8x8(
    coefficients: &[i16; 64],
    quantization_table: &[u16; 64],
    output_linestride: usize,
    output: &mut [u8],
) {
    // The loop below will write to positions [output_linestride * i, output_linestride * i + 8)
    // for 0<=i<8. Thus, the last accessed position is at an offset of output_linestrade * 7 + 7,
    // and if that position is in-bounds, so are all other accesses.
    assert!(
        output.len()
            > output_linestride
                .checked_mul(7)
                .unwrap()
                .checked_add(7)
                .unwrap()
    );

    const SHIFT: i32 = 3;

    // Read the DCT coefficients, scale them up and dequantize them.
    let mut data = [vdupq_n_s16(0); 8];
    for i in 0..8 {
        data[i] = vshlq_n_s16(
            vmulq_s16(
                vld1q_s16(coefficients.as_ptr().wrapping_add(i * 8)),
                vreinterpretq_s16_u16(vld1q_u16(quantization_table.as_ptr().wrapping_add(i * 8))),
            ),
            SHIFT,
        );
    }

    // Usual column IDCT - transpose - column IDCT - transpose approach.
    idct8(&mut data);
    transpose8(&mut data);
    idct8(&mut data);
    transpose8(&mut data);

    for i in 0..8 {
        // The two passes of the IDCT algorithm give us a factor of 8, so the shift here is
        // increased by 3.
        // As values will be stored in a u8, they need to be 128-centered and not 0-centered.
        // We add 128 with the appropriate shift for that purpose.
        const OFFSET: i16 = 128 << (SHIFT + 3);
        // We want rounding right shift, so we should add (1/2) << (SHIFT+3) before shifting.
        const ROUNDING_BIAS: i16 = (1 << (SHIFT + 3)) >> 1;

        let data_with_offset = vqaddq_s16(data[i], vdupq_n_s16(OFFSET + ROUNDING_BIAS));

        vst1_u8(
            output.as_mut_ptr().wrapping_add(output_linestride * i),
            vqshrun_n_s16(data_with_offset, SHIFT + 3),
        );
    }
}

#[cfg(all(feature = "nightly_aarch64_neon", target_arch = "aarch64"))]
#[target_feature(enable = "neon")]
#[target_feature(enable = "fp")]
pub unsafe fn color_convert_line_ycbcr(y: &[u8], cb: &[u8], cr: &[u8], output: &mut [u8]) -> usize {
    assert!(output.len() % 3 == 0);
    let num = output.len() / 3;
    assert!(num <= y.len());
    assert!(num <= cb.len());
    assert!(num <= cr.len());
    let num_vecs = num / 8;

    for i in 0..num_vecs {
        const SHIFT: i32 = 6;
        // Load.
        let y = vld1_u8(y.as_ptr().wrapping_add(i * 8));
        let cb = vld1_u8(cb.as_ptr().wrapping_add(i * 8));
        let cr = vld1_u8(cr.as_ptr().wrapping_add(i * 8));

        // Convert to 16 bit and shift.
        let y = vreinterpretq_s16_u16(vshll_n_u8(y, SHIFT));
        let cb = vreinterpretq_s16_u16(vshll_n_u8(cb, SHIFT));
        let cr = vreinterpretq_s16_u16(vshll_n_u8(cr, SHIFT));

        // Add offsets
        let y = vqaddq_s16(y, vdupq_n_s16((1 << SHIFT) >> 1));
        let c128 = vdupq_n_s16(128 << SHIFT);
        let cb = vqsubq_s16(cb, c128);
        let cr = vqsubq_s16(cr, c128);

        // Compute cr * 1.402, cb * 0.34414, cr * 0.71414, cb * 1.772
        let cr_140200 = vqaddq_s16(vqrdmulhq_n_s16(cr, 13173), cr);
        let cb_034414 = vqrdmulhq_n_s16(cb, 11276);
        let cr_071414 = vqrdmulhq_n_s16(cr, 23401);
        let cb_177200 = vqaddq_s16(vqrdmulhq_n_s16(cb, 25297), cb);

        // Last conversion step.
        let r = vqaddq_s16(y, cr_140200);
        let g = vqsubq_s16(y, vqaddq_s16(cb_034414, cr_071414));
        let b = vqaddq_s16(y, cb_177200);

        // Shift back and convert to u8.
        let r = vqshrun_n_s16(r, SHIFT);
        let g = vqshrun_n_s16(g, SHIFT);
        let b = vqshrun_n_s16(b, SHIFT);

        // Shuffle + store.
        vst3_u8(
            output.as_mut_ptr().wrapping_add(24 * i),
            uint8x8x3_t(r, g, b),
        );
    }

    num_vecs * 8
}
<|endoftext|>


<commit_before>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) Nathan Zadoks <nathan@nathan7.eu>,
//               whitequark <whitequark@whitequark.org>
//               Amanieu d'Antras <amanieu@gmail.com>
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

// To understand the machine code in this file, keep in mind these facts:
// * OR1K C ABI has a "red zone": 128 bytes under the top of the stack
//   that is defined to be unmolested by signal handlers, interrupts, etc.
//   Leaf functions can use the red zone without adjusting r1 or r2.
// * OR1K C ABI passes the first argument in r3. We also use r3 to pass a value
//   while swapping context; this is an arbitrary choice
//   (we clobber all registers and could use any of them) but this allows us
//   to reuse the swap function to perform the initial call. We do the same
//   thing with r4 to pass the stack pointer to the new context.
//
// To understand the DWARF CFI code in this file, keep in mind these facts:
// * CFI is "call frame information"; a set of instructions to a debugger or
//   an unwinder that allow it to simulate returning from functions. This implies
//   restoring every register to its pre-call state, as well as the stack pointer.
// * CFA is "call frame address"; the value of stack pointer right before the call
//   instruction in the caller. Everything strictly below CFA (and inclusive until
//   the next CFA) is the call frame of the callee. This implies that the return
//   address is the part of callee's call frame.
// * Logically, DWARF CFI is a table where rows are instruction pointer values and
//   columns describe where registers are spilled (mostly using expressions that
//   compute a memory location as CFA+n). A .cfi_offset pseudoinstruction changes
//   the state of a column for all IP numerically larger than the one it's placed
//   after. A .cfi_def_* pseudoinstruction changes the CFA value similarly.
// * Simulating return is as easy as restoring register values from the CFI table
//   and then setting stack pointer to CFA.
//
// A high-level overview of the function of the trampolines when unwinding is:
// * The 2nd init trampoline puts a controlled value (written in swap to `new_cfa`)
//   into r2. This is then used as the CFA for the 1st trampoline.
// * This controlled value points to the bottom of the stack of the parent context,
//   which holds the saved r2 and r9 from the call to swap().
// * The 1st init trampoline tells the unwinder to restore r2 and r9
//   from the stack frame at r2 (in the parent stack), thus continuing
//   unwinding at the swap call site instead of falling off the end of context stack.
use stack::Stack;

pub const STACK_ALIGNMENT: usize = 4;

#[derive(Debug, Clone, Copy)]
pub struct StackPointer(*mut usize);

pub unsafe fn init(stack: &Stack, f: unsafe extern "C" fn(usize, StackPointer) -> !) -> StackPointer {
  #[naked]
  unsafe extern "C" fn trampoline_1() {
    asm!(
      r#"
        # gdb has a hardcoded check that rejects backtraces where frame addresses
        # do not monotonically decrease. It is turned off if the function is called
        # "__morestack" and that is hardcoded. So, to make gdb backtraces match
        # the actual unwinder behavior, we call ourselves "__morestack" and mark
        # the symbol as local; it shouldn't interfere with anything.
      __morestack:
      .local __morestack

        # Set up the first part of our DWARF CFI linking stacks together. When
        # we reach this function from unwinding, r2 will be pointing at the bottom
        # of the parent linked stack. This link is set each time swap() is called.
        # When unwinding the frame corresponding to this function, a DWARF unwinder
        # will use r2+8 as the next call frame address, restore r2 from CFA-4 and
        # restore return address (r9) from CFA-8. This mirrors what the second half
        # of `swap_trampoline` does.
        .cfi_def_cfa r2, 8
        .cfi_offset r2, -4
        .cfi_offset r9, -8

        # This nop is here so that the initial swap doesn't return to the start
        # of the trampoline, which confuses the unwinder since it will look for
        # frame information in the previous symbol rather than this one. It is
        # never actually executed.
        l.nop

      .Lend:
      .size __morestack, .Lend-__morestack
      "#
      : : : : "volatile")
  }

  #[naked]
  unsafe extern "C" fn trampoline_2() {
    asm!(
      r#"
        # Set up the second part of our DWARF CFI.
        # When unwinding the frame corresponding to this function, a DWARF unwinder
        # will restore r2 (and thus CFA of the first trampoline) from the stack slot.
        # This stack slot is updated every time swap() is called to point to the bottom
        # of the stack of the context switch just switched from.
        .cfi_def_cfa r2, 8
        .cfi_offset r2, -4
        .cfi_offset r9, -8

        # This nop is here so that the return address of the swap trampoline
        # doesn't point to the start of the symbol. This confuses gdb's backtraces,
        # causing them to think the parent function is trampoline_1 instead of
        # trampoline_2.
        nop

        # Call the provided function.
        l.lwz   r4, 8(r1)
        l.jalr  r4
        l.nop
      "#
      : : : : "volatile")
  }

  unsafe fn push(sp: &mut StackPointer, val: usize) {
    sp.0 = sp.0.offset(-1);
    *sp.0 = val
  }

  // We set up the stack in a somewhat special way so that to the unwinder it
  // looks like trampoline_1 has called trampoline_2, which has in turn called
  // swap::trampoline.
  //
  // There are 2 call frames in this setup, each containing the return address
  // followed by the r2 value for that frame. This setup supports unwinding
  // using DWARF CFI as well as the frame pointer-based unwinding used by tools
  // such as perf or dtrace.
  let mut sp = StackPointer(stack.base() as *mut usize);

  push(&mut sp, f as usize); // Function that trampoline_2 should call

  // Call frame for trampoline_2. The CFA slot is updated by swap::trampoline
  // each time a context switch is performed.
  push(&mut sp, 0xdead0cfa);                // CFA slot
  push(&mut sp, trampoline_1 as usize + 4); // Return after the nop

  // Call frame for swap::trampoline. We set up the r2 value to point to the
  // parent call frame.
  let frame = sp;
  push(&mut sp, frame.0 as usize);          // Pointer to parent call frame
  push(&mut sp, trampoline_2 as usize + 4); // Entry point, skip initial nop

  // The last two values are read by the swap trampoline and are actually in the
  // red zone and not below the stack pointer.
  frame
}

#[inline(always)]
pub unsafe fn swap(arg: usize, new_sp: StackPointer,
                   new_stack: Option<&Stack>) -> (usize, StackPointer) {
  // Address of the topmost CFA stack slot.
  let mut dummy: usize = mem::uninitialized();
  let new_cfa = if let Some(new_stack) = new_stack {
    (new_stack.base() as *mut usize).offset(-2)
  } else {
    // Just pass a dummy pointer if we aren't linking the stack
    &mut dummy
  };

  #[naked]
  unsafe extern "C" fn trampoline() {
    asm!(
      r#"
        # Save the frame pointer and link register; the unwinder uses them to find
        # the CFA of the caller, and so they have to have the correct value immediately
        # after the call instruction that invoked the trampoline.
        l.sw    -4(r1), r2
        l.sw    -8(r1), r9
        .cfi_offset r2, -4
        .cfi_offset r9, -8

        # Link the call stacks together by writing the current stack bottom
        # address to the CFA slot in the new stack.
        l.addi  r7, r1, -8
        l.sw    0(r6), r7

        # Pass the stack pointer of the old context to the new one.
        l.or    r4, r0, r1
        # Load stack pointer of the new context.
        l.or    r1, r0, r5

        # Restore frame pointer and link register of the new context.
        # Load frame and instruction pointers of the new context.
        l.lwz   r2, -4(r1)
        l.lwz   r9, -8(r1)

        # Return into the new context.
        l.jr    r9
        l.nop
      "#
      : : : : "volatile")
  }

  let ret: usize;
  let ret_sp: *mut usize;
  asm!(
    r#"
      # Call the trampoline to switch to the new context.
      l.jal   ${2}
      l.nop
    "#
    : "={r3}" (ret)
      "={r4}" (ret_sp)
    : "s" (trampoline as usize)
      "{r3}" (arg)
      "{r5}" (new_sp.0)
      "{r6}" (new_cfa)
    :/*"r0", "r1",  "r2",  "r3",  "r4",*/"r5",  "r6",  "r7",
      "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15",
      "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
      "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
      "cc", "memory"
    : "volatile");
  (ret, StackPointer(ret_sp))
}
<commit_msg>Unbreak or1k.<commit_after>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) Nathan Zadoks <nathan@nathan7.eu>,
//               whitequark <whitequark@whitequark.org>
//               Amanieu d'Antras <amanieu@gmail.com>
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

// To understand the machine code in this file, keep in mind these facts:
// * OR1K C ABI has a "red zone": 128 bytes under the top of the stack
//   that is defined to be unmolested by signal handlers, interrupts, etc.
//   Leaf functions can use the red zone without adjusting r1 or r2.
// * OR1K C ABI passes the first argument in r3. We also use r3 to pass a value
//   while swapping context; this is an arbitrary choice
//   (we clobber all registers and could use any of them) but this allows us
//   to reuse the swap function to perform the initial call. We do the same
//   thing with r4 to pass the stack pointer to the new context.
//
// To understand the DWARF CFI code in this file, keep in mind these facts:
// * CFI is "call frame information"; a set of instructions to a debugger or
//   an unwinder that allow it to simulate returning from functions. This implies
//   restoring every register to its pre-call state, as well as the stack pointer.
// * CFA is "call frame address"; the value of stack pointer right before the call
//   instruction in the caller. Everything strictly below CFA (and inclusive until
//   the next CFA) is the call frame of the callee. This implies that the return
//   address is the part of callee's call frame.
// * Logically, DWARF CFI is a table where rows are instruction pointer values and
//   columns describe where registers are spilled (mostly using expressions that
//   compute a memory location as CFA+n). A .cfi_offset pseudoinstruction changes
//   the state of a column for all IP numerically larger than the one it's placed
//   after. A .cfi_def_* pseudoinstruction changes the CFA value similarly.
// * Simulating return is as easy as restoring register values from the CFI table
//   and then setting stack pointer to CFA.
//
// A high-level overview of the function of the trampolines when unwinding is:
// * The 2nd init trampoline puts a controlled value (written in swap to `new_cfa`)
//   into r2. This is then used as the CFA for the 1st trampoline.
// * This controlled value points to the bottom of the stack of the parent context,
//   which holds the saved r2 and r9 from the call to swap().
// * The 1st init trampoline tells the unwinder to restore r2 and r9
//   from the stack frame at r2 (in the parent stack), thus continuing
//   unwinding at the swap call site instead of falling off the end of context stack.
use core::mem;
use stack::Stack;

pub const STACK_ALIGNMENT: usize = 4;

#[derive(Debug, Clone, Copy)]
pub struct StackPointer(*mut usize);

pub unsafe fn init(stack: &Stack, f: unsafe extern "C" fn(usize, StackPointer) -> !) -> StackPointer {
  #[naked]
  unsafe extern "C" fn trampoline_1() {
    asm!(
      r#"
        # gdb has a hardcoded check that rejects backtraces where frame addresses
        # do not monotonically decrease. It is turned off if the function is called
        # "__morestack" and that is hardcoded. So, to make gdb backtraces match
        # the actual unwinder behavior, we call ourselves "__morestack" and mark
        # the symbol as local; it shouldn't interfere with anything.
      __morestack:
      .local __morestack

        # Set up the first part of our DWARF CFI linking stacks together. When
        # we reach this function from unwinding, r2 will be pointing at the bottom
        # of the parent linked stack. This link is set each time swap() is called.
        # When unwinding the frame corresponding to this function, a DWARF unwinder
        # will use r2+8 as the next call frame address, restore r2 from CFA-4 and
        # restore return address (r9) from CFA-8. This mirrors what the second half
        # of `swap_trampoline` does.
        .cfi_def_cfa r2, 8
        .cfi_offset r2, -4
        .cfi_offset r9, -8

        # This nop is here so that the initial swap doesn't return to the start
        # of the trampoline, which confuses the unwinder since it will look for
        # frame information in the previous symbol rather than this one. It is
        # never actually executed.
        l.nop

      .Lend:
      .size __morestack, .Lend-__morestack
      "#
      : : : : "volatile")
  }

  #[naked]
  unsafe extern "C" fn trampoline_2() {
    asm!(
      r#"
        # Set up the second part of our DWARF CFI.
        # When unwinding the frame corresponding to this function, a DWARF unwinder
        # will restore r2 (and thus CFA of the first trampoline) from the stack slot.
        # This stack slot is updated every time swap() is called to point to the bottom
        # of the stack of the context switch just switched from.
        .cfi_def_cfa r2, 8
        .cfi_offset r2, -4
        .cfi_offset r9, -8

        # This nop is here so that the return address of the swap trampoline
        # doesn't point to the start of the symbol. This confuses gdb's backtraces,
        # causing them to think the parent function is trampoline_1 instead of
        # trampoline_2.
        l.nop

        # Call the provided function.
        l.lwz   r5, 8(r1)
        l.jalr  r5
        l.nop
      "#
      : : : : "volatile")
  }

  unsafe fn push(sp: &mut StackPointer, val: usize) {
    sp.0 = sp.0.offset(-1);
    *sp.0 = val
  }

  // We set up the stack in a somewhat special way so that to the unwinder it
  // looks like trampoline_1 has called trampoline_2, which has in turn called
  // swap::trampoline.
  //
  // There are 2 call frames in this setup, each containing the return address
  // followed by the r2 value for that frame. This setup supports unwinding
  // using DWARF CFI as well as the frame pointer-based unwinding used by tools
  // such as perf or dtrace.
  let mut sp = StackPointer(stack.base() as *mut usize);

  push(&mut sp, f as usize); // Function that trampoline_2 should call

  // Call frame for trampoline_2. The CFA slot is updated by swap::trampoline
  // each time a context switch is performed.
  push(&mut sp, 0xdead0cfa);                // CFA slot
  push(&mut sp, trampoline_1 as usize + 4); // Return after the nop

  // Call frame for swap::trampoline. We set up the r2 value to point to the
  // parent call frame.
  let frame = sp;
  push(&mut sp, frame.0 as usize);          // Pointer to parent call frame
  push(&mut sp, trampoline_2 as usize + 4); // Entry point, skip initial nop

  // The last two values are read by the swap trampoline and are actually in the
  // red zone and not below the stack pointer.
  frame
}

#[inline(always)]
pub unsafe fn swap(arg: usize, new_sp: StackPointer,
                   new_stack: Option<&Stack>) -> (usize, StackPointer) {
  // Address of the topmost CFA stack slot.
  let mut dummy: usize = mem::uninitialized();
  let new_cfa = if let Some(new_stack) = new_stack {
    (new_stack.base() as *mut usize).offset(-2)
  } else {
    // Just pass a dummy pointer if we aren't linking the stack
    &mut dummy
  };

  #[naked]
  unsafe extern "C" fn trampoline() {
    asm!(
      r#"
        # Save the frame pointer and link register; the unwinder uses them to find
        # the CFA of the caller, and so they have to have the correct value immediately
        # after the call instruction that invoked the trampoline.
        l.sw    -4(r1), r2
        l.sw    -8(r1), r9
        .cfi_offset r2, -4
        .cfi_offset r9, -8

        # Link the call stacks together by writing the current stack bottom
        # address to the CFA slot in the new stack.
        l.addi  r7, r1, -8
        l.sw    0(r6), r7

        # Pass the stack pointer of the old context to the new one.
        l.or    r4, r0, r1
        # Load stack pointer of the new context.
        l.or    r1, r0, r5

        # Restore frame pointer and link register of the new context.
        # Load frame and instruction pointers of the new context.
        l.lwz   r2, -4(r1)
        l.lwz   r9, -8(r1)

        # Return into the new context.
        l.jr    r9
        l.nop
      "#
      : : : : "volatile")
  }

  let ret: usize;
  let ret_sp: *mut usize;
  asm!(
    r#"
      # Call the trampoline to switch to the new context.
      l.jal   ${2}
      l.nop
    "#
    : "={r3}" (ret)
      "={r4}" (ret_sp)
    : "s" (trampoline as usize)
      "{r3}" (arg)
      "{r5}" (new_sp.0)
      "{r6}" (new_cfa)
    :/*"r0", "r1",  "r2",  "r3",  "r4",*/"r5",  "r6",  "r7",
      "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15",
      "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
      "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
      "cc", "memory"
    : "volatile");
  (ret, StackPointer(ret_sp))
}
<|endoftext|>


<commit_before>use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::rc::Rc;
use std::cell::RefCell;
use uuid::Uuid;

use js_types::js_type::{JsVar, JsType, JsPtrEnum};

pub mod scope;

pub type Alloc<T> = Rc<RefCell<T>>;

pub struct AllocBox {
    black_set: HashMap<Uuid, Alloc<JsPtrEnum>>,
    grey_set: HashMap<Uuid, Alloc<JsPtrEnum>>,
    white_set: HashMap<Uuid, Alloc<JsPtrEnum>>,
}

impl AllocBox {
    pub fn new() -> AllocBox {
        AllocBox {
            black_set: HashMap::new(),
            grey_set: HashMap::new(),
            white_set: HashMap::new(),
        }
    }

    pub fn alloc(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> Uuid {
        self.white_set.insert(uuid, Rc::new(RefCell::new(ptr)));
        uuid
    }

    pub fn dealloc(&mut self, uuid: &Uuid) -> bool {
        if let Some(_) = self.white_set.remove(uuid) { true } else { false }
    }

    pub fn mark_roots(&mut self, marks: HashSet<Uuid>) {
        for mark in marks {
            // FIXME? Could a root be grey already?
            if let Some(ptr) = self.white_set.remove(&mark) {
                // Get all child references
                let child_ids = AllocBox::get_var_children(&ptr);
                // Mark current ref as black
                self.black_set.insert(mark, ptr);
                // Mark child references as grey
                self.grey_children(child_ids);
            }
        }
    }

    pub fn mark_vars(&mut self) {
        // Mark any grey object as black, and mark all white objs it refs as grey
        let mut new_grey_set = HashMap::new();
        for (uuid, var) in self.grey_set.drain() {
            let child_ids = AllocBox::get_var_children(&var);
            self.black_set.insert(uuid, var);
            for child_id in child_ids {
                if let Some(var) = self.white_set.remove(&child_id) {
                    new_grey_set.insert(child_id, var);
                }
            }
        }
        self.grey_set = new_grey_set;
    }

    pub fn sweep_vars(&mut self) {
        self.white_set = HashMap::new();
    }

    pub fn find_id(&self, uuid: &Uuid) -> Option<&Alloc<JsPtrEnum>> {
        self.white_set.get(uuid).or_else(||
            self.grey_set.get(uuid).or_else(||
                self.black_set.get(uuid)))
    }

    pub fn update_var(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> bool {
        if let Entry::Occupied(mut view) = self.find_id_mut(&uuid) {
            let inner = view.get_mut();
            *inner.borrow_mut() = ptr;
            true
        } else { false }
    }

    fn grey_children(&mut self, child_ids: HashSet<Uuid>) {
        for child_id in child_ids.iter() {
            if let Some(var) = self.white_set.remove(child_id) {
                self.grey_set.insert(*child_id, var);
            }
        }
    }

    fn get_var_children(ptr: &Alloc<JsPtrEnum>) -> HashSet<Uuid> {
        if let JsPtrEnum::JsObj(ref obj) = *ptr.borrow() {
            obj.get_children()
        } else { HashSet::new() }
    }

    fn find_id_mut(&mut self, uuid: &Uuid) -> Entry<Uuid, Alloc<JsPtrEnum>> {
        if let e @ Entry::Occupied(_) = self.white_set.entry(*uuid) {
            e
        } else if let e @ Entry::Occupied(_) = self.grey_set.entry(*uuid) {
            e
        } else { self.black_set.entry(*uuid) }
    }
}
<commit_msg>Make AllocBox::update_var take its uuid by ref<commit_after>use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::rc::Rc;
use std::cell::RefCell;
use uuid::Uuid;

use js_types::js_type::{JsVar, JsType, JsPtrEnum};

pub mod scope;

pub type Alloc<T> = Rc<RefCell<T>>;

pub struct AllocBox {
    black_set: HashMap<Uuid, Alloc<JsPtrEnum>>,
    grey_set: HashMap<Uuid, Alloc<JsPtrEnum>>,
    white_set: HashMap<Uuid, Alloc<JsPtrEnum>>,
}

impl AllocBox {
    pub fn new() -> AllocBox {
        AllocBox {
            black_set: HashMap::new(),
            grey_set: HashMap::new(),
            white_set: HashMap::new(),
        }
    }

    pub fn alloc(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> Uuid {
        self.white_set.insert(uuid, Rc::new(RefCell::new(ptr)));
        uuid
    }

    pub fn dealloc(&mut self, uuid: &Uuid) -> bool {
        if let Some(_) = self.white_set.remove(uuid) { true } else { false }
    }

    pub fn mark_roots(&mut self, marks: HashSet<Uuid>) {
        for mark in marks {
            // FIXME? Could a root be grey already?
            if let Some(ptr) = self.white_set.remove(&mark) {
                // Get all child references
                let child_ids = AllocBox::get_var_children(&ptr);
                // Mark current ref as black
                self.black_set.insert(mark, ptr);
                // Mark child references as grey
                self.grey_children(child_ids);
            }
        }
    }

    pub fn mark_vars(&mut self) {
        // Mark any grey object as black, and mark all white objs it refs as grey
        let mut new_grey_set = HashMap::new();
        for (uuid, var) in self.grey_set.drain() {
            let child_ids = AllocBox::get_var_children(&var);
            self.black_set.insert(uuid, var);
            for child_id in child_ids {
                if let Some(var) = self.white_set.remove(&child_id) {
                    new_grey_set.insert(child_id, var);
                }
            }
        }
        self.grey_set = new_grey_set;
    }

    pub fn sweep_vars(&mut self) {
        self.white_set = HashMap::new();
    }

    pub fn find_id(&self, uuid: &Uuid) -> Option<&Alloc<JsPtrEnum>> {
        self.white_set.get(uuid).or_else(||
            self.grey_set.get(uuid).or_else(||
                self.black_set.get(uuid)))
    }

    pub fn update_var(&mut self, uuid: &Uuid, ptr: JsPtrEnum) -> bool {
        if let Entry::Occupied(mut view) = self.find_id_mut(&uuid) {
            let inner = view.get_mut();
            *inner.borrow_mut() = ptr;
            true
        } else { false }
    }

    fn grey_children(&mut self, child_ids: HashSet<Uuid>) {
        for child_id in child_ids.iter() {
            if let Some(var) = self.white_set.remove(child_id) {
                self.grey_set.insert(*child_id, var);
            }
        }
    }

    fn get_var_children(ptr: &Alloc<JsPtrEnum>) -> HashSet<Uuid> {
        if let JsPtrEnum::JsObj(ref obj) = *ptr.borrow() {
            obj.get_children()
        } else { HashSet::new() }
    }

    fn find_id_mut(&mut self, uuid: &Uuid) -> Entry<Uuid, Alloc<JsPtrEnum>> {
        if let e @ Entry::Occupied(_) = self.white_set.entry(*uuid) {
            e
        } else if let e @ Entry::Occupied(_) = self.grey_set.entry(*uuid) {
            e
        } else { self.black_set.entry(*uuid) }
    }
}
<|endoftext|>


<commit_before>extern crate crossbeam;

use std::collections::VecDeque;
use std::sync::Mutex;
use std::sync::mpsc::channel;
use std::time::Duration;

use crossbeam::scope;
use crossbeam::sync::MsQueue;
use crossbeam::sync::SegQueue;

use extra_impls::mpsc_queue::Queue as MpscQueue;

mod extra_impls;

const COUNT: u64 = 10000000;
const THREADS: u64 = 2;

#[cfg(feature = "nightly")]
fn time<F: FnOnce()>(f: F) -> Duration {
    let start = ::std::time::Instant::now();
    f();
    start.elapsed()
}

#[cfg(not(feature = "nightly"))]
fn time<F: FnOnce()>(_f: F) -> Duration {
    Duration::new(0, 0)
}

fn nanos(d: Duration) -> f64 {
    d.as_secs() as f64 * 1000000000f64 + (d.subsec_nanos() as f64)
}

trait Queue<T> {
    fn push(&self, T);
    fn try_pop(&self) -> Option<T>;
}

impl<T> Queue<T> for MsQueue<T> {
    fn push(&self, t: T) { self.push(t) }
    fn try_pop(&self) -> Option<T> { self.try_pop() }
}

impl<T> Queue<T> for SegQueue<T> {
    fn push(&self, t: T) { self.push(t) }
    fn try_pop(&self) -> Option<T> { self.try_pop() }
}

impl<T> Queue<T> for MpscQueue<T> {
    fn push(&self, t: T) { self.push(t) }
    fn try_pop(&self) -> Option<T> {
        use extra_impls::mpsc_queue::*;

        loop {
            match self.pop() {
                Data(t) => return Some(t),
                Empty => return None,
                Inconsistent => (),
            }
        }
    }
}

impl<T> Queue<T> for Mutex<VecDeque<T>> {
    fn push(&self, t: T) { self.lock().unwrap().push_back(t) }
    fn try_pop(&self) -> Option<T> { self.lock().unwrap().pop_front() }
}

fn bench_queue_mpsc<Q: Queue<u64> + Sync>(q: Q) -> f64 {
    let d = time(|| {
        scope(|scope| {
            for _i in 0..THREADS {
                let qr = &q;
                scope.spawn(move || {
                    for x in 0..COUNT {
                        let _ = qr.push(x);
                    }
                });
            }

            let mut count = 0;
            while count < COUNT*THREADS {
                if q.try_pop().is_some() {
                    count += 1;
                }
            }
        });
    });

    nanos(d) / ((COUNT * THREADS) as f64)
}

fn bench_queue_mpmc<Q: Queue<bool> + Sync>(q: Q) -> f64 {
    use std::sync::atomic::AtomicUsize;
    use std::sync::atomic::Ordering::Relaxed;

    let prod_count = AtomicUsize::new(0);

    let d = time(|| {
        scope(|scope| {
            for _i in 0..THREADS {
                let qr = &q;
                let pcr = &prod_count;
                scope.spawn(move || {
                    for _x in 0..COUNT {
                        qr.push(true);
                    }
                    if pcr.fetch_add(1, Relaxed) == (THREADS as usize) - 1 {
                        for _x in 0..THREADS {
                            qr.push(false)
                        }
                    }
                });
                scope.spawn(move || {
                    loop {
                        if let Some(false) = qr.try_pop() { break }
                    }
                });
            }

        });
    });

    nanos(d) / ((COUNT * THREADS) as f64)
}

fn bench_chan_mpsc() -> f64 {
    let (tx, rx) = channel();

    let d = time(|| {
        scope(|scope| {
            for _i in 0..THREADS {
                let my_tx = tx.clone();

                scope.spawn(move || {
                    for x in 0..COUNT {
                        let _ = my_tx.send(x);
                    }
                });
            }

            for _i in 0..COUNT*THREADS {
                let _ = rx.recv().unwrap();
            }
        });
    });

    nanos(d) / ((COUNT * THREADS) as f64)
}

fn main() {
    println!("MSQ mpsc: {}", bench_queue_mpsc(MsQueue::new()));
    println!("chan mpsc: {}", bench_chan_mpsc());
    println!("mpsc mpsc: {}", bench_queue_mpsc(MpscQueue::new()));
    println!("Seg mpsc: {}", bench_queue_mpsc(SegQueue::new()));

    println!("MSQ mpmc: {}", bench_queue_mpmc(MsQueue::new()));
    println!("Seg mpmc: {}", bench_queue_mpmc(SegQueue::new()));

//    println!("queue_mpsc: {}", bench_queue_mpsc());
//    println!("queue_mpmc: {}", bench_queue_mpmc());
//   println!("mutex_mpmc: {}", bench_mutex_mpmc());
}
<commit_msg>Remove cfg(nightly) for bench.rs (#59) (#131)<commit_after>extern crate crossbeam;

use std::collections::VecDeque;
use std::sync::Mutex;
use std::sync::mpsc::channel;
use std::time::Duration;

use crossbeam::scope;
use crossbeam::sync::MsQueue;
use crossbeam::sync::SegQueue;

use extra_impls::mpsc_queue::Queue as MpscQueue;

mod extra_impls;

const COUNT: u64 = 10000000;
const THREADS: u64 = 2;

fn time<F: FnOnce()>(f: F) -> Duration {
    let start = ::std::time::Instant::now();
    f();
    start.elapsed()
}

fn nanos(d: Duration) -> f64 {
    d.as_secs() as f64 * 1000000000f64 + (d.subsec_nanos() as f64)
}

trait Queue<T> {
    fn push(&self, T);
    fn try_pop(&self) -> Option<T>;
}

impl<T> Queue<T> for MsQueue<T> {
    fn push(&self, t: T) { self.push(t) }
    fn try_pop(&self) -> Option<T> { self.try_pop() }
}

impl<T> Queue<T> for SegQueue<T> {
    fn push(&self, t: T) { self.push(t) }
    fn try_pop(&self) -> Option<T> { self.try_pop() }
}

impl<T> Queue<T> for MpscQueue<T> {
    fn push(&self, t: T) { self.push(t) }
    fn try_pop(&self) -> Option<T> {
        use extra_impls::mpsc_queue::*;

        loop {
            match self.pop() {
                Data(t) => return Some(t),
                Empty => return None,
                Inconsistent => (),
            }
        }
    }
}

impl<T> Queue<T> for Mutex<VecDeque<T>> {
    fn push(&self, t: T) { self.lock().unwrap().push_back(t) }
    fn try_pop(&self) -> Option<T> { self.lock().unwrap().pop_front() }
}

fn bench_queue_mpsc<Q: Queue<u64> + Sync>(q: Q) -> f64 {
    let d = time(|| {
        scope(|scope| {
            for _i in 0..THREADS {
                let qr = &q;
                scope.spawn(move || {
                    for x in 0..COUNT {
                        let _ = qr.push(x);
                    }
                });
            }

            let mut count = 0;
            while count < COUNT*THREADS {
                if q.try_pop().is_some() {
                    count += 1;
                }
            }
        });
    });

    nanos(d) / ((COUNT * THREADS) as f64)
}

fn bench_queue_mpmc<Q: Queue<bool> + Sync>(q: Q) -> f64 {
    use std::sync::atomic::AtomicUsize;
    use std::sync::atomic::Ordering::Relaxed;

    let prod_count = AtomicUsize::new(0);

    let d = time(|| {
        scope(|scope| {
            for _i in 0..THREADS {
                let qr = &q;
                let pcr = &prod_count;
                scope.spawn(move || {
                    for _x in 0..COUNT {
                        qr.push(true);
                    }
                    if pcr.fetch_add(1, Relaxed) == (THREADS as usize) - 1 {
                        for _x in 0..THREADS {
                            qr.push(false)
                        }
                    }
                });
                scope.spawn(move || {
                    loop {
                        if let Some(false) = qr.try_pop() { break }
                    }
                });
            }

        });
    });

    nanos(d) / ((COUNT * THREADS) as f64)
}

fn bench_chan_mpsc() -> f64 {
    let (tx, rx) = channel();

    let d = time(|| {
        scope(|scope| {
            for _i in 0..THREADS {
                let my_tx = tx.clone();

                scope.spawn(move || {
                    for x in 0..COUNT {
                        let _ = my_tx.send(x);
                    }
                });
            }

            for _i in 0..COUNT*THREADS {
                let _ = rx.recv().unwrap();
            }
        });
    });

    nanos(d) / ((COUNT * THREADS) as f64)
}

fn main() {
    println!("MSQ mpsc: {}", bench_queue_mpsc(MsQueue::new()));
    println!("chan mpsc: {}", bench_chan_mpsc());
    println!("mpsc mpsc: {}", bench_queue_mpsc(MpscQueue::new()));
    println!("Seg mpsc: {}", bench_queue_mpsc(SegQueue::new()));

    println!("MSQ mpmc: {}", bench_queue_mpmc(MsQueue::new()));
    println!("Seg mpmc: {}", bench_queue_mpmc(SegQueue::new()));

//    println!("queue_mpsc: {}", bench_queue_mpsc());
//    println!("queue_mpmc: {}", bench_queue_mpmc());
//   println!("mutex_mpmc: {}", bench_mutex_mpmc());
}
<|endoftext|>


<commit_before>use crate::compiler::lir::{Chunk, ChunkIndex, Instruction};
use itertools::Itertools;
use log::debug;
use std::{
    collections::HashMap,
    fmt::{self, Display},
};

use super::value::Value;

/// A VM can execute some byte code.
pub struct Vm {
    pub(super) chunks: Vec<Chunk>,
    pub(super) status: Status,
    next_instruction: ByteCodePointer,
    pub(super) stack: Vec<StackEntry>,
    heap: HashMap<ObjectPointer, Object>,
    next_heap_address: ObjectPointer,
}

#[derive(Clone)]
pub enum Status {
    Running,
    Done(Value),
    Panicked(Value),
}

/// Stack entries point either to instructions or to objects.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StackEntry {
    ByteCode(ByteCodePointer),
    Object(ObjectPointer),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteCodePointer {
    chunk: ChunkIndex,
    instruction: usize,
}
pub type ObjectPointer = usize;

#[derive(Debug, Clone)] // TODO: remove Clone once it's no longer needed
pub struct Object {
    reference_count: usize,
    data: ObjectData,
}
#[derive(Clone, Debug)] // TODO: remove Clone once it's no longer needed
pub enum ObjectData {
    Int(u64),
    Text(String),
    Symbol(String),
    Struct(HashMap<ObjectPointer, ObjectPointer>),
    Closure {
        // TODO: This could later be just a vector of object pointers, but for
        // now we capture the whole stack.
        captured: Vec<StackEntry>,
        body: ChunkIndex,
    },
}

impl Vm {
    pub fn new(chunks: Vec<Chunk>) -> Self {
        Self {
            chunks: chunks.clone(),
            status: Status::Running,
            next_instruction: ByteCodePointer {
                chunk: chunks.len() - 1,
                instruction: 0,
            },
            stack: vec![],
            heap: HashMap::new(),
            next_heap_address: 0,
        }
    }

    pub fn status(&self) -> Status {
        self.status.clone()
    }

    fn get_from_stack(&self, offset: usize) -> StackEntry {
        self.stack[self.stack.len() - 1 - offset as usize].clone()
    }
    fn get_from_heap(&self, address: ObjectPointer) -> &Object {
        self.heap.get(&address).unwrap()
    }
    fn get_mut_from_heap(&mut self, address: ObjectPointer) -> &mut Object {
        self.heap.get_mut(&address).unwrap()
    }

    pub fn create_object(&mut self, object: ObjectData) -> ObjectPointer {
        let address = self.next_heap_address;
        self.heap.insert(
            address,
            Object {
                reference_count: 1,
                data: object,
            },
        );
        self.next_heap_address += 1;
        address
    }
    pub fn free_object(&mut self, address: ObjectPointer) {
        let object = self.heap.remove(&address).unwrap();
        assert_eq!(object.reference_count, 0);
        match object.data {
            ObjectData::Int(_) => {}
            ObjectData::Text(_) => {}
            ObjectData::Symbol(_) => {}
            ObjectData::Struct(entries) => {
                for (key, value) in entries {
                    self.drop(key);
                    self.drop(value);
                }
            }
            ObjectData::Closure { captured, .. } => {
                for entry in captured {
                    if let StackEntry::Object(address) = entry {
                        self.drop(address);
                    }
                }
            }
        }
    }

    fn dup(&mut self, address: ObjectPointer) {
        self.get_mut_from_heap(address).reference_count += 1;
    }
    fn drop(&mut self, address: ObjectPointer) {
        let object = self.get_mut_from_heap(address);
        object.reference_count -= 1;
        if object.reference_count == 0 {
            self.free_object(address);
        }
    }

    pub(super) fn import(&mut self, value: Value) -> ObjectPointer {
        let value = match value {
            Value::Int(int) => ObjectData::Int(int),
            Value::Text(text) => ObjectData::Text(text),
            Value::Symbol(symbol) => ObjectData::Symbol(symbol),
            Value::Struct(struct_) => {
                let mut entries = HashMap::new();
                for (key, value) in struct_ {
                    let key = self.import(key);
                    let value = self.import(value);
                    entries.insert(key, value);
                }
                ObjectData::Struct(entries)
            }
            Value::Closure { captured, body } => ObjectData::Closure { captured, body },
        };
        self.create_object(value)
    }
    pub(super) fn export(&mut self, address: ObjectPointer) -> Value {
        let value = self.export_helper(address);
        self.drop(address);
        value
    }
    fn export_helper(&self, address: ObjectPointer) -> Value {
        match &self.get_from_heap(address).data {
            ObjectData::Int(int) => Value::Int(*int),
            ObjectData::Text(text) => Value::Text(text.clone()),
            ObjectData::Symbol(symbol) => Value::Symbol(symbol.clone()),
            ObjectData::Struct(struct_) => {
                let mut entries = im::HashMap::new();
                for (key, value) in struct_ {
                    let key = self.export_helper(*key);
                    let value = self.export_helper(*value);
                    entries.insert(key, value);
                }
                Value::Struct(entries)
            }
            ObjectData::Closure { captured, body } => Value::Closure {
                captured: captured.clone(),
                body: *body,
            },
        }
    }

    pub fn run(&mut self, mut num_instructions: u16) {
        assert!(
            matches!(self.status, Status::Running),
            "Called Vm::run on a vm that is not ready to run."
        );
        while matches!(self.status, Status::Running) && num_instructions > 0 {
            num_instructions -= 1;
            let instruction = self.chunks[self.next_instruction.chunk].instructions
                [self.next_instruction.instruction]
                .clone();
            debug!("Executing instruction: {:?}", &instruction);
            self.next_instruction.instruction += 1;
            self.run_instruction(instruction);
            debug!(
                "Stack: {}",
                self.stack
                    .iter()
                    .map(|entry| match entry {
                        StackEntry::ByteCode(byte_code) => {
                            format!("->{}:{}", byte_code.chunk, byte_code.instruction)
                        }
                        StackEntry::Object(address) => format!("{}", address),
                    })
                    .join(", ")
            );

            if self.next_instruction.instruction
                >= self.chunks[self.next_instruction.chunk].instructions.len()
            {
                self.status = Status::Done(Value::nothing());
            }
        }
    }
    pub fn run_instruction(&mut self, instruction: Instruction) {
        match instruction {
            Instruction::CreateInt(int) => {
                let address = self.create_object(ObjectData::Int(int));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateText(text) => {
                let address = self.create_object(ObjectData::Text(text));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateSymbol(symbol) => {
                let address = self.create_object(ObjectData::Symbol(symbol));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateStruct { num_entries } => {
                let mut key_value_addresses = vec![];
                for _ in 0..(2 * num_entries) {
                    match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => panic!("Struct can only contain objects."),
                        StackEntry::Object(address) => key_value_addresses.push(address),
                    }
                }
                let mut entries = HashMap::new();
                for mut key_and_value in &key_value_addresses.into_iter().rev().chunks(2) {
                    let key = key_and_value.next().unwrap();
                    let value = key_and_value.next().unwrap();
                    assert_eq!(key_and_value.next(), None);
                    entries.insert(key, value);
                }
                let address = self.create_object(ObjectData::Struct(entries));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateClosure(chunk_index) => {
                let stack = self.stack.clone();
                for entry in &stack {
                    match entry {
                        StackEntry::ByteCode(_) => {}
                        StackEntry::Object(address) => {
                            self.dup(*address);
                        }
                    }
                }
                let address = self.create_object(ObjectData::Closure {
                    captured: stack,
                    body: chunk_index,
                });
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::PopMultipleBelowTop(n) => {
                let top = self.stack.pop().unwrap();
                for _ in 0..n {
                    if let StackEntry::Object(address) = self.stack.pop().unwrap() {
                        self.drop(address);
                    }
                }
                self.stack.push(top);
            }
            Instruction::PushFromStack(offset) => {
                let entry = self.get_from_stack(offset);
                if let StackEntry::Object(address) = &entry {
                    self.dup(*address);
                }
                self.stack.push(entry);
            }
            Instruction::Call => {
                let closure_address = match self.stack.pop().unwrap() {
                    StackEntry::ByteCode(_) => panic!(),
                    StackEntry::Object(address) => address,
                };
                let (captured, body) = match &self.get_from_heap(closure_address).data {
                    ObjectData::Closure { captured, body } => (captured.clone(), *body),
                    _ => panic!("Can't call non-closure."),
                };
                let num_args = self.chunks[body].num_args;
                let mut args = vec![];
                for _ in 0..num_args {
                    let address = match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => {
                            panic!("You can only pass objects as arguments.")
                        }
                        StackEntry::Object(address) => address,
                    };
                    args.push(address);
                }
                self.stack.push(StackEntry::ByteCode(self.next_instruction));
                self.stack.append(&mut captured.clone());
                for arg in args {
                    self.stack.push(StackEntry::Object(arg));
                }
                self.drop(closure_address);
                self.next_instruction = ByteCodePointer {
                    chunk: body,
                    instruction: 0,
                };
            }
            Instruction::Return => {
                let return_value = self.stack.pop().unwrap();
                let caller = match self.stack.pop().unwrap() {
                    StackEntry::ByteCode(address) => address,
                    StackEntry::Object(_) => panic!("Return with no caller on stack"),
                };
                self.stack.push(return_value);
                self.next_instruction = caller;
            }
            Instruction::Builtin(builtin_function) => {
                self.run_builtin_function(builtin_function);
            }
            Instruction::DebugValueEvaluated(_) => {}
            Instruction::Error(_) => {
                self.panic(
                    "The VM crashed because there was an error in previous compilation stages."
                        .to_string(),
                );
            }
        }
    }

    pub fn panic(&mut self, message: String) -> Value {
        self.status = Status::Panicked(Value::Text(message));
        Value::Symbol("Never".to_string())
    }
}
<commit_msg>Maintain stack trace in VM<commit_after>use crate::compiler::{
    hir,
    lir::{Chunk, ChunkIndex, Instruction},
};
use itertools::Itertools;
use log::debug;
use std::collections::HashMap;

use super::value::Value;

/// A VM can execute some byte code.
pub struct Vm {
    pub(super) chunks: Vec<Chunk>,
    pub(super) status: Status,
    next_instruction: ByteCodePointer,
    pub(super) stack: Vec<StackEntry>,
    heap: HashMap<ObjectPointer, Object>,
    next_heap_address: ObjectPointer,
    stack_trace: Vec<hir::Id>,
}

#[derive(Clone)]
pub enum Status {
    Running,
    Done(Value),
    Panicked(Value),
}

/// Stack entries point either to instructions or to objects.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StackEntry {
    ByteCode(ByteCodePointer),
    Object(ObjectPointer),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteCodePointer {
    chunk: ChunkIndex,
    instruction: usize,
}
pub type ObjectPointer = usize;

#[derive(Debug, Clone)] // TODO: remove Clone once it's no longer needed
pub struct Object {
    reference_count: usize,
    data: ObjectData,
}
#[derive(Clone, Debug)] // TODO: remove Clone once it's no longer needed
pub enum ObjectData {
    Int(u64),
    Text(String),
    Symbol(String),
    Struct(HashMap<ObjectPointer, ObjectPointer>),
    Closure {
        // TODO: This could later be just a vector of object pointers, but for
        // now we capture the whole stack.
        captured: Vec<StackEntry>,
        body: ChunkIndex,
    },
}

impl Vm {
    pub fn new(chunks: Vec<Chunk>) -> Self {
        Self {
            chunks: chunks.clone(),
            status: Status::Running,
            next_instruction: ByteCodePointer {
                chunk: chunks.len() - 1,
                instruction: 0,
            },
            stack: vec![],
            heap: HashMap::new(),
            next_heap_address: 0,
            stack_trace: vec![],
        }
    }

    pub fn status(&self) -> Status {
        self.status.clone()
    }

    fn get_from_stack(&self, offset: usize) -> StackEntry {
        self.stack[self.stack.len() - 1 - offset as usize].clone()
    }
    fn get_from_heap(&self, address: ObjectPointer) -> &Object {
        self.heap.get(&address).unwrap()
    }
    fn get_mut_from_heap(&mut self, address: ObjectPointer) -> &mut Object {
        self.heap.get_mut(&address).unwrap()
    }

    pub fn create_object(&mut self, object: ObjectData) -> ObjectPointer {
        let address = self.next_heap_address;
        self.heap.insert(
            address,
            Object {
                reference_count: 1,
                data: object,
            },
        );
        self.next_heap_address += 1;
        address
    }
    pub fn free_object(&mut self, address: ObjectPointer) {
        let object = self.heap.remove(&address).unwrap();
        assert_eq!(object.reference_count, 0);
        match object.data {
            ObjectData::Int(_) => {}
            ObjectData::Text(_) => {}
            ObjectData::Symbol(_) => {}
            ObjectData::Struct(entries) => {
                for (key, value) in entries {
                    self.drop(key);
                    self.drop(value);
                }
            }
            ObjectData::Closure { captured, .. } => {
                for entry in captured {
                    if let StackEntry::Object(address) = entry {
                        self.drop(address);
                    }
                }
            }
        }
    }

    fn dup(&mut self, address: ObjectPointer) {
        self.get_mut_from_heap(address).reference_count += 1;
    }
    fn drop(&mut self, address: ObjectPointer) {
        let object = self.get_mut_from_heap(address);
        object.reference_count -= 1;
        if object.reference_count == 0 {
            self.free_object(address);
        }
    }

    pub(super) fn import(&mut self, value: Value) -> ObjectPointer {
        let value = match value {
            Value::Int(int) => ObjectData::Int(int),
            Value::Text(text) => ObjectData::Text(text),
            Value::Symbol(symbol) => ObjectData::Symbol(symbol),
            Value::Struct(struct_) => {
                let mut entries = HashMap::new();
                for (key, value) in struct_ {
                    let key = self.import(key);
                    let value = self.import(value);
                    entries.insert(key, value);
                }
                ObjectData::Struct(entries)
            }
            Value::Closure { captured, body } => ObjectData::Closure { captured, body },
        };
        self.create_object(value)
    }
    pub(super) fn export(&mut self, address: ObjectPointer) -> Value {
        let value = self.export_helper(address);
        self.drop(address);
        value
    }
    fn export_helper(&self, address: ObjectPointer) -> Value {
        match &self.get_from_heap(address).data {
            ObjectData::Int(int) => Value::Int(*int),
            ObjectData::Text(text) => Value::Text(text.clone()),
            ObjectData::Symbol(symbol) => Value::Symbol(symbol.clone()),
            ObjectData::Struct(struct_) => {
                let mut entries = im::HashMap::new();
                for (key, value) in struct_ {
                    let key = self.export_helper(*key);
                    let value = self.export_helper(*value);
                    entries.insert(key, value);
                }
                Value::Struct(entries)
            }
            ObjectData::Closure { captured, body } => Value::Closure {
                captured: captured.clone(),
                body: *body,
            },
        }
    }

    pub fn run(&mut self, mut num_instructions: u16) {
        assert!(
            matches!(self.status, Status::Running),
            "Called Vm::run on a vm that is not ready to run."
        );
        while matches!(self.status, Status::Running) && num_instructions > 0 {
            num_instructions -= 1;
            let instruction = self.chunks[self.next_instruction.chunk].instructions
                [self.next_instruction.instruction]
                .clone();
            debug!("Executing instruction: {:?}", &instruction);
            self.next_instruction.instruction += 1;
            self.run_instruction(instruction);
            debug!(
                "Stack: {}",
                self.stack
                    .iter()
                    .map(|entry| match entry {
                        StackEntry::ByteCode(byte_code) => {
                            format!("->{}:{}", byte_code.chunk, byte_code.instruction)
                        }
                        StackEntry::Object(address) => format!("{}", address),
                    })
                    .join(", ")
            );

            if self.next_instruction.instruction
                >= self.chunks[self.next_instruction.chunk].instructions.len()
            {
                self.status = Status::Done(Value::nothing());
            }
        }
    }
    pub fn run_instruction(&mut self, instruction: Instruction) {
        match instruction {
            Instruction::CreateInt(int) => {
                let address = self.create_object(ObjectData::Int(int));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateText(text) => {
                let address = self.create_object(ObjectData::Text(text));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateSymbol(symbol) => {
                let address = self.create_object(ObjectData::Symbol(symbol));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateStruct { num_entries } => {
                let mut key_value_addresses = vec![];
                for _ in 0..(2 * num_entries) {
                    match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => panic!("Struct can only contain objects."),
                        StackEntry::Object(address) => key_value_addresses.push(address),
                    }
                }
                let mut entries = HashMap::new();
                for mut key_and_value in &key_value_addresses.into_iter().rev().chunks(2) {
                    let key = key_and_value.next().unwrap();
                    let value = key_and_value.next().unwrap();
                    assert_eq!(key_and_value.next(), None);
                    entries.insert(key, value);
                }
                let address = self.create_object(ObjectData::Struct(entries));
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::CreateClosure(chunk_index) => {
                let stack = self.stack.clone();
                for entry in &stack {
                    match entry {
                        StackEntry::ByteCode(_) => {}
                        StackEntry::Object(address) => {
                            self.dup(*address);
                        }
                    }
                }
                let address = self.create_object(ObjectData::Closure {
                    captured: stack,
                    body: chunk_index,
                });
                self.stack.push(StackEntry::Object(address));
            }
            Instruction::PopMultipleBelowTop(n) => {
                let top = self.stack.pop().unwrap();
                for _ in 0..n {
                    if let StackEntry::Object(address) = self.stack.pop().unwrap() {
                        self.drop(address);
                    }
                }
                self.stack.push(top);
            }
            Instruction::PushFromStack(offset) => {
                let entry = self.get_from_stack(offset);
                if let StackEntry::Object(address) = &entry {
                    self.dup(*address);
                }
                self.stack.push(entry);
            }
            Instruction::Call => {
                let closure_address = match self.stack.pop().unwrap() {
                    StackEntry::ByteCode(_) => panic!(),
                    StackEntry::Object(address) => address,
                };
                let (captured, body) = match &self.get_from_heap(closure_address).data {
                    ObjectData::Closure { captured, body } => (captured.clone(), *body),
                    _ => panic!("Can't call non-closure."),
                };
                let num_args = self.chunks[body].num_args;
                let mut args = vec![];
                for _ in 0..num_args {
                    let address = match self.stack.pop().unwrap() {
                        StackEntry::ByteCode(_) => {
                            panic!("You can only pass objects as arguments.")
                        }
                        StackEntry::Object(address) => address,
                    };
                    args.push(address);
                }
                self.stack.push(StackEntry::ByteCode(self.next_instruction));
                self.stack.append(&mut captured.clone());
                for arg in args {
                    self.stack.push(StackEntry::Object(arg));
                }
                self.drop(closure_address);
                self.next_instruction = ByteCodePointer {
                    chunk: body,
                    instruction: 0,
                };
            }
            Instruction::Return => {
                let return_value = self.stack.pop().unwrap();
                let caller = match self.stack.pop().unwrap() {
                    StackEntry::ByteCode(address) => address,
                    StackEntry::Object(_) => panic!("Return with no caller on stack"),
                };
                self.stack.push(return_value);
                self.next_instruction = caller;
            }
            Instruction::Builtin(builtin_function) => {
                self.run_builtin_function(builtin_function);
            }
            Instruction::DebugValueEvaluated(_) => {}
            Instruction::DebugClosureEntered(hir_id) => self.stack_trace.push(hir_id),
            Instruction::DebugClosureExited => {
                self.stack_trace.pop().unwrap();
            }
            Instruction::Error(_) => {
                self.panic(
                    "The VM crashed because there was an error in previous compilation stages."
                        .to_string(),
                );
            }
        }
    }

    pub fn panic(&mut self, message: String) -> Value {
        self.status = Status::Panicked(Value::Text(message));
        Value::Symbol("Never".to_string())
    }

    pub fn current_stack_trace(&self) -> Vec<hir::Id> {
        self.stack_trace.clone()
    }
}
<|endoftext|>


<commit_before>use time::{now, Tm, strptime, Duration};
use crypto::mac::{Mac, MacResult};
use crypto::hmac::Hmac;
use crypto::sha2::Sha512Trunc224;
use rustc_serialize::base64::{ToBase64, FromBase64, URL_SAFE, FromBase64Error};
use hyper::header::{Headers, Cookie, SetCookie};
use cookie::Cookie as CookiePair;
use std::str::{FromStr, from_utf8, Utf8Error};
use std::string::ToString;
use std::fmt::{self, Debug};

pub struct AuthToken {
    time: Tm,
    username: String,
    hmac: MacResult,
}

#[derive(Debug)]
pub enum TokenError {
    Base64(FromBase64Error),
    FirstDotNotFound,
    Utf8(Utf8Error),
    SecondDotNotFound,
}

impl Debug for AuthToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "AuthToken {{ time: {:?}, username: {}, hmac: {:?} }}",
               self.time, self.username, self.hmac.code().to_base64(URL_SAFE))
    }
}

impl ToString for AuthToken {
    fn to_string(&self) -> String {
        let text = format!("{}.{}", self.username, self.time.to_timespec().sec)
            .as_bytes()
            .to_base64(URL_SAFE);

        format!("{}.{}", text, self.hmac.code().to_base64(URL_SAFE))
    }
}

impl FromStr for AuthToken {
    type Err = TokenError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split('.').collect();
        if parts.len() != 2 {
            return Err(TokenError::FirstDotNotFound);
        }

        let hmacpart = try!(parts.get(1).unwrap().from_base64());
        let hmac = MacResult::new_from_owned(hmacpart);

        let user = try!(parts.get(0).unwrap().from_base64());
        let userparts: Vec<&str> = try!(from_utf8(&user)).split('.').collect();
        if userparts.len() != 2 {
            return Err(TokenError::SecondDotNotFound);
        }

        let username = userparts.get(0).unwrap();
        let t = strptime(userparts.get(1).unwrap(), "%s").unwrap().to_utc();

        Ok(AuthToken {
            time: t,
            username: username.to_string(),
            hmac: hmac,
        })
    }
}

impl AuthToken {
    pub fn new(hmac_secret: &str, username: &str) -> AuthToken {
        AuthToken::new_with_time(hmac_secret, username, now().to_utc())
    }

    pub fn new_with_time(hmac_secret: &str, username: &str, t: Tm) -> AuthToken {
        let mut hmac = Hmac::new(Sha512Trunc224::new(), hmac_secret.as_bytes());
        let text = format!("{}.{}", username, t.rfc3339());
        hmac.input(text.as_bytes());

        AuthToken {
            time: t,
            username: username.to_string(),
            hmac: hmac.result(),
        }

    }

    pub fn verify_token(good: &AuthToken, unknown: &AuthToken) -> bool {
        let mut is_good = true;

        is_good = is_good && (good.hmac == unknown.hmac);

        let timeout = Duration::minutes(5);

        is_good = is_good && (unknown.time >= (now().to_utc() - timeout));

        is_good
    }

}

impl From<FromBase64Error> for TokenError {
    fn from(e: FromBase64Error) -> TokenError {
        TokenError::Base64(e)
    }
}

impl From<Utf8Error> for TokenError {
    fn from(e: Utf8Error) -> TokenError {
        TokenError::Utf8(e)
    }
}

pub fn set_auth_cookie(hmac_secret: &str, username: &str, headers: &mut Headers) {
    let token = AuthToken::new(hmac_secret, username);
    let c_pair = CookiePair::new("auth_token".to_string(), token.to_string());

    match headers.get_mut::<SetCookie>() {
        Some(c) => {
            c.push(c_pair);
            return;
        },
        None => {}
    };
    headers.set::<SetCookie>(SetCookie(vec![c_pair]));
}

pub fn verify_auth_cookie(hmac_secret: &str, headers: &Headers) -> bool {
    let to_verify = match get_auth_token_from_headers(headers) {
        Some(v) => v,
        None => return false
    };
    let good = AuthToken::new_with_time(hmac_secret, &to_verify.username,
                                        to_verify.time);

    AuthToken::verify_token(&good, &to_verify)
}

pub fn get_auth_token_from_headers(headers: &Headers) -> Option<AuthToken> {
    let cookies = match headers.get::<Cookie>() {
        Some(c) => c,
        None => return None
    };

    let mut auth_string = "".to_string();

    for c in cookies.iter() {
        if c.name == "auth_token" {
            auth_string = c.value.clone();
            break;
        }
    }

    match AuthToken::from_str(&auth_string) {
        Ok(v) => Some(v),
        Err(_) => None
    }
}
<commit_msg>"username" -> more generic "data", make data and time public<commit_after>use time::{now, Tm, strptime, Duration};
use crypto::mac::{Mac, MacResult};
use crypto::hmac::Hmac;
use crypto::sha2::Sha512Trunc224;
use rustc_serialize::base64::{ToBase64, FromBase64, URL_SAFE, FromBase64Error};
use hyper::header::{Headers, Cookie, SetCookie};
use cookie::Cookie as CookiePair;
use std::str::{FromStr, from_utf8, Utf8Error};
use std::string::ToString;
use std::fmt::{self, Debug};

pub struct AuthToken {
    pub time: Tm,
    pub data: String,
    hmac: MacResult,
}

#[derive(Debug)]
pub enum TokenError {
    Base64(FromBase64Error),
    FirstDotNotFound,
    Utf8(Utf8Error),
    SecondDotNotFound,
}

impl Debug for AuthToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "AuthToken {{ time: {:?}, data: {}, hmac: {:?} }}",
               self.time, self.data, self.hmac.code().to_base64(URL_SAFE))
    }
}

impl ToString for AuthToken {
    fn to_string(&self) -> String {
        let text = format!("{}.{}", self.data, self.time.to_timespec().sec)
            .as_bytes()
            .to_base64(URL_SAFE);

        format!("{}.{}", text, self.hmac.code().to_base64(URL_SAFE))
    }
}

impl FromStr for AuthToken {
    type Err = TokenError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split('.').collect();
        if parts.len() != 2 {
            return Err(TokenError::FirstDotNotFound);
        }

        let hmacpart = try!(parts.get(1).unwrap().from_base64());
        let hmac = MacResult::new_from_owned(hmacpart);

        let user = try!(parts.get(0).unwrap().from_base64());
        let userparts: Vec<&str> = try!(from_utf8(&user)).split('.').collect();
        if userparts.len() != 2 {
            return Err(TokenError::SecondDotNotFound);
        }

        let data = userparts.get(0).unwrap();
        let t = strptime(userparts.get(1).unwrap(), "%s").unwrap().to_utc();

        Ok(AuthToken {
            time: t,
            data: data.to_string(),
            hmac: hmac,
        })
    }
}

impl AuthToken {
    pub fn new(hmac_secret: &str, data: &str) -> AuthToken {
        AuthToken::new_with_time(hmac_secret, data, now().to_utc())
    }

    pub fn new_with_time(hmac_secret: &str, data: &str, t: Tm) -> AuthToken {
        let mut hmac = Hmac::new(Sha512Trunc224::new(), hmac_secret.as_bytes());
        let text = format!("{}.{}", data, t.rfc3339());
        hmac.input(text.as_bytes());

        AuthToken {
            time: t,
            data: data.to_string(),
            hmac: hmac.result(),
        }

    }

    pub fn verify_token(good: &AuthToken, unknown: &AuthToken) -> bool {
        let mut is_good = true;

        is_good = is_good && (good.hmac == unknown.hmac);

        let timeout = Duration::minutes(5);

        is_good = is_good && (unknown.time >= (now().to_utc() - timeout));

        is_good
    }

}

impl From<FromBase64Error> for TokenError {
    fn from(e: FromBase64Error) -> TokenError {
        TokenError::Base64(e)
    }
}

impl From<Utf8Error> for TokenError {
    fn from(e: Utf8Error) -> TokenError {
        TokenError::Utf8(e)
    }
}

pub fn set_auth_cookie(hmac_secret: &str, data: &str, headers: &mut Headers) {
    let token = AuthToken::new(hmac_secret, data);
    let c_pair = CookiePair::new("auth_token".to_string(), token.to_string());

    match headers.get_mut::<SetCookie>() {
        Some(c) => {
            c.push(c_pair);
            return;
        },
        None => {}
    };
    headers.set::<SetCookie>(SetCookie(vec![c_pair]));
}

pub fn verify_auth_cookie(hmac_secret: &str, headers: &Headers) -> bool {
    let to_verify = match get_auth_token_from_headers(headers) {
        Some(v) => v,
        None => return false
    };
    let good = AuthToken::new_with_time(hmac_secret, &to_verify.data,
                                        to_verify.time);

    AuthToken::verify_token(&good, &to_verify)
}

pub fn get_auth_token_from_headers(headers: &Headers) -> Option<AuthToken> {
    let cookies = match headers.get::<Cookie>() {
        Some(c) => c,
        None => return None
    };

    let mut auth_string = "".to_string();

    for c in cookies.iter() {
        if c.name == "auth_token" {
            auth_string = c.value.clone();
            break;
        }
    }

    match AuthToken::from_str(&auth_string) {
        Ok(v) => Some(v),
        Err(_) => None
    }
}
<|endoftext|>