<commit_before>use parking_lot::MutexGuard;

use ctxt::get_vm;
use gc::root::Slot;
use gc::space::Space;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{CrossingEntry, CrossingMap};
use gc::swiper::large::LargeSpace;
use gc::swiper::old::{OldGen, OldGenProtected};
use gc::swiper::on_different_cards;
use gc::swiper::young::YoungGen;
use gc::swiper::{CARD_REFS, CARD_SIZE};
use gc::{Address, Region};

use mem;
use object::{offset_of_array_data, Obj};

#[derive(Copy, Clone)]
pub enum VerifierPhase {
    PreMinor,
    PostMinor,
    PreFull,
    PostFull,
}

impl VerifierPhase {
    fn is_pre(self) -> bool {
        match self {
            VerifierPhase::PreMinor => true,
            VerifierPhase::PostMinor => false,
            VerifierPhase::PreFull => true,
            VerifierPhase::PostFull => false,
        }
    }

    fn is_post_minor(self) -> bool {
        match self {
            VerifierPhase::PostMinor => true,
            _ => false,
        }
    }

    fn is_pre_full(self) -> bool {
        match self {
            VerifierPhase::PreFull => true,
            _ => false,
        }
    }
}

pub struct Verifier<'a> {
    young: &'a YoungGen,
    old: &'a OldGen,
    old_protected: MutexGuard<'a, OldGenProtected>,
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    rootset: &'a [Slot],
    large: &'a LargeSpace,
    perm_space: &'a Space,

    refs_to_young_gen: usize,
    in_old: bool,
    in_large: bool,

    young_total: Region,
    eden_active: Region,
    from_active: Region,
    to_active: Region,
    reserved_area: Region,

    phase: VerifierPhase,
    promotion_failed: bool,
}

impl<'a> Verifier<'a> {
    pub fn new(
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [Slot],
        large: &'a LargeSpace,
        perm_space: &'a Space,
        reserved_area: Region,
        phase: VerifierPhase,
        promotion_failed: bool,
    ) -> Verifier<'a> {
        let old_protected = old.protected();

        Verifier {
            young: young,
            old: old,
            old_protected: old_protected,
            card_table: card_table,
            crossing_map: crossing_map,
            rootset: rootset,
            perm_space: perm_space,
            large: large,

            refs_to_young_gen: 0,
            in_old: false,
            in_large: false,

            eden_active: young.eden_active(),
            from_active: young.from_active(),
            to_active: young.to_active(),
            young_total: young.total(),
            reserved_area: reserved_area,

            phase: phase,
            promotion_failed: promotion_failed,
        }
    }

    pub fn verify(&mut self) {
        self.verify_roots();
        self.verify_young();
        self.verify_old();
        self.verify_large();
    }

    fn verify_young(&mut self) {
        let region = self.young.eden_active();
        self.verify_objects(region, "young gen (eden)");

        let region = self.from_active.clone();
        if !self.promotion_failed {
            assert!(region.empty(), "from-space should be empty.");
        }
        self.verify_objects(region, "young gen (from)");

        let region = self.to_active.clone();
        self.verify_objects(region, "young gen (to)");
    }

    fn verify_old(&mut self) {
        self.in_old = true;
        let old_regions = self.old_protected.regions.clone();
        let mut last = self.old.total().start;
        for old_region in old_regions {
            let region_total = old_region.total_region();
            assert_eq!(region_total.start, last);
            self.verify_objects(old_region.active_region(), "old gen");
            last = region_total.end;
        }
        assert_eq!(self.old.total().end, last);
        self.in_old = false;
    }

    fn verify_large(&mut self) {
        self.in_large = true;
        self.large.visit_objects(|addr| {
            let object = addr.to_obj();
            let region = Region::new(addr, addr.offset(object.size()));
            self.verify_objects(region, "large space");
        });
        self.in_large = false;
    }

    fn verify_roots(&mut self) {
        for root in self.rootset {
            self.verify_reference(*root, Address::null(), "root set");
        }
    }

    fn verify_objects(&mut self, region: Region, name: &str) {
        let mut curr = region.start;
        self.refs_to_young_gen = 0;

        while curr < region.end {
            let object = curr.to_mut_obj();

            if object.header().vtblptr().is_null() {
                let next = curr.add_ptr(1);

                if self.in_old && on_different_cards(curr, next) {
                    self.verify_card(curr, region.start);
                    self.verify_crossing(curr, next, false);
                }

                curr = next;
                continue;
            }

            let next = if object.is_array_ref() {
                self.verify_array_ref(object, curr, region.start, name)
            } else {
                self.verify_object(object, curr, region.start, name)
            };

            curr = next;
        }

        assert!(curr == region.end, "object doesn't end at region end");

        if (self.in_old || self.in_large) && !self.card_table.is_aligned(curr) {
            self.verify_card(curr, region.start);
        }
    }

    fn verify_array_ref(
        &mut self,
        object: &mut Obj,
        mut curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        let object_address = curr;

        object.visit_reference_fields(|element| {
            if (self.in_old || self.in_large) && on_different_cards(curr, element.address()) {
                self.verify_card(curr, start);
                curr = element.address();
            }

            self.verify_reference(element, object_address, name);
        });

        let next = object_address.offset(object.size());

        if self.in_old && on_different_cards(object_address, next) {
            self.verify_crossing(object_address, next, true);
        }

        next
    }

    fn verify_object(
        &mut self,
        object: &mut Obj,
        curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        object.visit_reference_fields(|child| {
            self.verify_reference(child, curr, name);
        });

        let next = curr.offset(object.size());

        if (self.in_old || self.in_large) && on_different_cards(curr, next) {
            self.verify_card(curr, start);
            if self.in_old {
                self.verify_crossing(curr, next, false);
            }
        }

        next
    }

    fn verify_card(&mut self, curr: Address, start: Address) {
        let curr_card = self.card_table.card_idx(curr);

        let expected_card_entry = if self.refs_to_young_gen > 0 {
            CardEntry::Dirty
        } else {
            CardEntry::Clean
        };

        let actual_card_entry = self.card_table.get(curr_card);

        // In the verify-phase before the collection the card's dirty-entry isn't
        // guaranteed to be exact. It could be `dirty` although this card doesn't
        // actually contain any references into the young generation. But it should never
        // be clean when there are actual references into the young generation.
        if self.phase.is_pre() && expected_card_entry == CardEntry::Clean {
            self.refs_to_young_gen = 0;
            return;
        }

        // The first card in an old region can't be cleaned if it is not card aligned.
        // Therefore this card could be dirty when it should actually be clean, but this is
        // allowed. Nevertheless it shouldn't be clean when it actually contains references
        // into the young generation.
        if curr_card == self.card_table.card_idx(start)
            && !start.is_card_aligned()
            && expected_card_entry == CardEntry::Clean
        {
            self.refs_to_young_gen = 0;
            return;
        }

        if actual_card_entry != expected_card_entry {
            let card_text = match actual_card_entry {
                CardEntry::Dirty => "dirty",
                CardEntry::Clean => "clean",
            };

            let card_start = self.card_table.to_address(curr_card);
            let card_end = card_start.offset(CARD_SIZE);

            println!(
                "CARD: {} ({}-{}) is marked {} but has {} reference(s).",
                curr_card.to_usize(),
                card_start,
                card_end,
                card_text,
                self.refs_to_young_gen
            );

            panic!("card table entry wrong.");
        }

        assert!(actual_card_entry == expected_card_entry);

        self.refs_to_young_gen = 0;
    }

    fn verify_crossing(&mut self, old: Address, new: Address, array_ref: bool) {
        let new_card_idx = self.card_table.card_idx(new);
        let old_card_idx = self.card_table.card_idx(old);

        if new_card_idx == old_card_idx {
            return;
        }

        let new_card_start = self.card_table.to_address(new_card_idx);
        let old_card_end = self.card_table.to_address(old_card_idx).offset(CARD_SIZE);

        let offset = new.offset_from(new_card_start);
        let offset_words = (offset / mem::ptr_width_usize()) as u8;

        let crossing_middle;
        let loop_start;

        if array_ref {
            crossing_middle = CrossingEntry::LeadingRefs(CARD_REFS as u8);

            if old.offset(offset_of_array_data() as usize) > old_card_end {
                let old_next = old_card_idx.to_usize() + 1;
                let crossing = self.crossing_map.get(old_next.into());
                let diff_words = old_card_end.offset_from(old) / mem::ptr_width_usize();
                assert!(crossing == CrossingEntry::ArrayStart(diff_words as u8));

                loop_start = old_card_idx.to_usize() + 2;
            } else {
                loop_start = old_card_idx.to_usize() + 1;
            }

            if new_card_idx.to_usize() >= loop_start {
                let crossing = self.crossing_map.get(new_card_idx);
                let expected = if offset_words > 0 {
                    CrossingEntry::LeadingRefs(offset_words)
                } else {
                    CrossingEntry::FirstObject(0)
                };
                assert!(crossing == expected, "array crossing at end not correct.");
            }
        } else {
            crossing_middle = CrossingEntry::NoRefs;
            loop_start = old_card_idx.to_usize() + 1;

            let crossing = self.crossing_map.get(new_card_idx);
            let expected = CrossingEntry::FirstObject(offset_words);
            assert!(crossing == expected, "crossing at end not correct.");
        }

        for c in loop_start..new_card_idx.to_usize() {
            assert!(
                self.crossing_map.get(c.into()) == crossing_middle,
                "middle crossing not correct."
            );
        }
    }

    fn verify_reference(&mut self, slot: Slot, container_obj: Address, name: &str) {
        let reference = slot.get();

        if reference.is_null() {
            return;
        }

        if self.old_protected.contains_slow(reference)
            || self.eden_active.contains(reference)
            || self.to_active.contains(reference)
            || self.perm_space.contains(reference)
            || self.large.contains(reference)
            || (self.from_active.contains(reference) && self.promotion_failed)
        {
            let object = reference.to_obj();

            // Verify that the address is the start of an object,
            // for this access its size.
            // To make sure this isn't optimized out by the compiler,
            // make sure that the size doesn't equal 1.
            assert!(object.size() != 1, "object size shouldn't be 1");

            if self.young_total.contains(reference) {
                self.refs_to_young_gen += 1;
            }

            return;
        }

        let perm_region = self.perm_space.used_region();

        println!(
            "PRM: {}; active: {} (size 0x{:x})",
            self.perm_space.total(),
            perm_region,
            perm_region.size(),
        );
        println!(
            "EDN: {}; active: {} (size 0x{:x})",
            self.young.eden_total(),
            self.eden_active,
            self.eden_active.size(),
        );
        println!(
            "FRM: {}; active: {} (size 0x{:x})",
            self.young.from_total(),
            self.from_active,
            self.from_active.size(),
        );
        println!(
            " TO: {}; active: {} (size 0x{:x})",
            self.young.to_total(),
            self.to_active,
            self.to_active.size(),
        );
        {
            println!(
                "OLD total: {} (size 0x{:x})",
                self.old.total(),
                self.old.total().size()
            );
            for old_region in &self.old_protected.regions {
                println!(
                    "OLD region: {}; active: {} (size 0x{:x})",
                    old_region.committed_region(),
                    old_region.active_region(),
                    old_region.active_size(),
                );
            }
        }

        println!(
            "LRG: {}-{}",
            self.large.total().start,
            self.large.total().end
        );
        println!(
            "TTL: {}-{}",
            self.reserved_area.start, self.reserved_area.end
        );
        println!(
            "found invalid reference to {} in {} (at {}, in object {}).",
            reference,
            name,
            slot.address(),
            container_obj
        );

        if self.young.contains(reference)
            && !self.from_active.contains(reference)
            && !self.eden_active.contains(reference)
        {
            println!("reference points into young generation but not into the active semi-space.");
        }

        println!("try print object size and class:");

        let object = reference.to_obj();
        println!("\tsize {}", object.size());
        let cls = object.header().vtbl().class();
        println!("\tclass {}", cls.name(get_vm()));

        panic!("reference neither pointing into young nor old generation.");
    }
}
<commit_msg>check nulls directly after each other<commit_after>use parking_lot::MutexGuard;

use ctxt::get_vm;
use gc::root::Slot;
use gc::space::Space;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{CrossingEntry, CrossingMap};
use gc::swiper::large::LargeSpace;
use gc::swiper::old::{OldGen, OldGenProtected};
use gc::swiper::on_different_cards;
use gc::swiper::young::YoungGen;
use gc::swiper::{CARD_REFS, CARD_SIZE};
use gc::{Address, Region};

use mem;
use object::{offset_of_array_data, Obj};

#[derive(Copy, Clone)]
pub enum VerifierPhase {
    PreMinor,
    PostMinor,
    PreFull,
    PostFull,
}

impl VerifierPhase {
    fn is_pre(self) -> bool {
        match self {
            VerifierPhase::PreMinor => true,
            VerifierPhase::PostMinor => false,
            VerifierPhase::PreFull => true,
            VerifierPhase::PostFull => false,
        }
    }

    fn is_post_minor(self) -> bool {
        match self {
            VerifierPhase::PostMinor => true,
            _ => false,
        }
    }

    fn is_pre_full(self) -> bool {
        match self {
            VerifierPhase::PreFull => true,
            _ => false,
        }
    }
}

pub struct Verifier<'a> {
    young: &'a YoungGen,
    old: &'a OldGen,
    old_protected: MutexGuard<'a, OldGenProtected>,
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,
    rootset: &'a [Slot],
    large: &'a LargeSpace,
    perm_space: &'a Space,

    refs_to_young_gen: usize,
    in_old: bool,
    in_large: bool,

    young_total: Region,
    eden_active: Region,
    from_active: Region,
    to_active: Region,
    reserved_area: Region,

    phase: VerifierPhase,
    promotion_failed: bool,
}

impl<'a> Verifier<'a> {
    pub fn new(
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [Slot],
        large: &'a LargeSpace,
        perm_space: &'a Space,
        reserved_area: Region,
        phase: VerifierPhase,
        promotion_failed: bool,
    ) -> Verifier<'a> {
        let old_protected = old.protected();

        Verifier {
            young: young,
            old: old,
            old_protected: old_protected,
            card_table: card_table,
            crossing_map: crossing_map,
            rootset: rootset,
            perm_space: perm_space,
            large: large,

            refs_to_young_gen: 0,
            in_old: false,
            in_large: false,

            eden_active: young.eden_active(),
            from_active: young.from_active(),
            to_active: young.to_active(),
            young_total: young.total(),
            reserved_area: reserved_area,

            phase: phase,
            promotion_failed: promotion_failed,
        }
    }

    pub fn verify(&mut self) {
        self.verify_roots();
        self.verify_young();
        self.verify_old();
        self.verify_large();
    }

    fn verify_young(&mut self) {
        let region = self.young.eden_active();
        self.verify_objects(region, "young gen (eden)");

        let region = self.from_active.clone();
        if !self.promotion_failed {
            assert!(region.empty(), "from-space should be empty.");
        }
        self.verify_objects(region, "young gen (from)");

        let region = self.to_active.clone();
        self.verify_objects(region, "young gen (to)");
    }

    fn verify_old(&mut self) {
        self.in_old = true;
        let old_regions = self.old_protected.regions.clone();
        let mut last = self.old.total().start;
        for old_region in old_regions {
            let region_total = old_region.total_region();
            assert_eq!(region_total.start, last);
            self.verify_objects(old_region.active_region(), "old gen");
            last = region_total.end;
        }
        assert_eq!(self.old.total().end, last);
        self.in_old = false;
    }

    fn verify_large(&mut self) {
        self.in_large = true;
        self.large.visit_objects(|addr| {
            let object = addr.to_obj();
            let region = Region::new(addr, addr.offset(object.size()));
            self.verify_objects(region, "large space");
        });
        self.in_large = false;
    }

    fn verify_roots(&mut self) {
        for root in self.rootset {
            self.verify_reference(*root, Address::null(), "root set");
        }
    }

    fn verify_objects(&mut self, region: Region, name: &str) {
        let mut curr = region.start;
        let mut last_null = false;
        self.refs_to_young_gen = 0;

        while curr < region.end {
            let object = curr.to_mut_obj();

            if object.header().vtblptr().is_null() {
                let next = curr.add_ptr(1);

                if self.in_old && on_different_cards(curr, next) {
                    self.verify_card(curr, region.start);
                    self.verify_crossing(curr, next, false);
                }

                assert!(!last_null, "there should not be nulls directly after each other");

                curr = next;
                last_null = true;
                continue;
            } else {
                last_null = false;
            }

            let next = if object.is_array_ref() {
                self.verify_array_ref(object, curr, region.start, name)
            } else {
                self.verify_object(object, curr, region.start, name)
            };

            curr = next;
        }

        assert!(curr == region.end, "object doesn't end at region end");

        if (self.in_old || self.in_large) && !self.card_table.is_aligned(curr) {
            self.verify_card(curr, region.start);
        }
    }

    fn verify_array_ref(
        &mut self,
        object: &mut Obj,
        mut curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        let object_address = curr;

        object.visit_reference_fields(|element| {
            if (self.in_old || self.in_large) && on_different_cards(curr, element.address()) {
                self.verify_card(curr, start);
                curr = element.address();
            }

            self.verify_reference(element, object_address, name);
        });

        let next = object_address.offset(object.size());

        if self.in_old && on_different_cards(object_address, next) {
            self.verify_crossing(object_address, next, true);
        }

        next
    }

    fn verify_object(
        &mut self,
        object: &mut Obj,
        curr: Address,
        start: Address,
        name: &str,
    ) -> Address {
        object.visit_reference_fields(|child| {
            self.verify_reference(child, curr, name);
        });

        let next = curr.offset(object.size());

        if (self.in_old || self.in_large) && on_different_cards(curr, next) {
            self.verify_card(curr, start);
            if self.in_old {
                self.verify_crossing(curr, next, false);
            }
        }

        next
    }

    fn verify_card(&mut self, curr: Address, start: Address) {
        let curr_card = self.card_table.card_idx(curr);

        let expected_card_entry = if self.refs_to_young_gen > 0 {
            CardEntry::Dirty
        } else {
            CardEntry::Clean
        };

        let actual_card_entry = self.card_table.get(curr_card);

        // In the verify-phase before the collection the card's dirty-entry isn't
        // guaranteed to be exact. It could be `dirty` although this card doesn't
        // actually contain any references into the young generation. But it should never
        // be clean when there are actual references into the young generation.
        if self.phase.is_pre() && expected_card_entry == CardEntry::Clean {
            self.refs_to_young_gen = 0;
            return;
        }

        // The first card in an old region can't be cleaned if it is not card aligned.
        // Therefore this card could be dirty when it should actually be clean, but this is
        // allowed. Nevertheless it shouldn't be clean when it actually contains references
        // into the young generation.
        if curr_card == self.card_table.card_idx(start)
            && !start.is_card_aligned()
            && expected_card_entry == CardEntry::Clean
        {
            self.refs_to_young_gen = 0;
            return;
        }

        if actual_card_entry != expected_card_entry {
            let card_text = match actual_card_entry {
                CardEntry::Dirty => "dirty",
                CardEntry::Clean => "clean",
            };

            let card_start = self.card_table.to_address(curr_card);
            let card_end = card_start.offset(CARD_SIZE);

            println!(
                "CARD: {} ({}-{}) is marked {} but has {} reference(s).",
                curr_card.to_usize(),
                card_start,
                card_end,
                card_text,
                self.refs_to_young_gen
            );

            panic!("card table entry wrong.");
        }

        assert!(actual_card_entry == expected_card_entry);

        self.refs_to_young_gen = 0;
    }

    fn verify_crossing(&mut self, old: Address, new: Address, array_ref: bool) {
        let new_card_idx = self.card_table.card_idx(new);
        let old_card_idx = self.card_table.card_idx(old);

        if new_card_idx == old_card_idx {
            return;
        }

        let new_card_start = self.card_table.to_address(new_card_idx);
        let old_card_end = self.card_table.to_address(old_card_idx).offset(CARD_SIZE);

        let offset = new.offset_from(new_card_start);
        let offset_words = (offset / mem::ptr_width_usize()) as u8;

        let crossing_middle;
        let loop_start;

        if array_ref {
            crossing_middle = CrossingEntry::LeadingRefs(CARD_REFS as u8);

            if old.offset(offset_of_array_data() as usize) > old_card_end {
                let old_next = old_card_idx.to_usize() + 1;
                let crossing = self.crossing_map.get(old_next.into());
                let diff_words = old_card_end.offset_from(old) / mem::ptr_width_usize();
                assert!(crossing == CrossingEntry::ArrayStart(diff_words as u8));

                loop_start = old_card_idx.to_usize() + 2;
            } else {
                loop_start = old_card_idx.to_usize() + 1;
            }

            if new_card_idx.to_usize() >= loop_start {
                let crossing = self.crossing_map.get(new_card_idx);
                let expected = if offset_words > 0 {
                    CrossingEntry::LeadingRefs(offset_words)
                } else {
                    CrossingEntry::FirstObject(0)
                };
                assert!(crossing == expected, "array crossing at end not correct.");
            }
        } else {
            crossing_middle = CrossingEntry::NoRefs;
            loop_start = old_card_idx.to_usize() + 1;

            let crossing = self.crossing_map.get(new_card_idx);
            let expected = CrossingEntry::FirstObject(offset_words);
            assert!(crossing == expected, "crossing at end not correct.");
        }

        for c in loop_start..new_card_idx.to_usize() {
            assert!(
                self.crossing_map.get(c.into()) == crossing_middle,
                "middle crossing not correct."
            );
        }
    }

    fn verify_reference(&mut self, slot: Slot, container_obj: Address, name: &str) {
        let reference = slot.get();

        if reference.is_null() {
            return;
        }

        if self.old_protected.contains_slow(reference)
            || self.eden_active.contains(reference)
            || self.to_active.contains(reference)
            || self.perm_space.contains(reference)
            || self.large.contains(reference)
            || (self.from_active.contains(reference) && self.promotion_failed)
        {
            let object = reference.to_obj();

            // Verify that the address is the start of an object,
            // for this access its size.
            // To make sure this isn't optimized out by the compiler,
            // make sure that the size doesn't equal 1.
            assert!(object.size() != 1, "object size shouldn't be 1");

            if self.young_total.contains(reference) {
                self.refs_to_young_gen += 1;
            }

            return;
        }

        let perm_region = self.perm_space.used_region();

        println!(
            "PRM: {}; active: {} (size 0x{:x})",
            self.perm_space.total(),
            perm_region,
            perm_region.size(),
        );
        println!(
            "EDN: {}; active: {} (size 0x{:x})",
            self.young.eden_total(),
            self.eden_active,
            self.eden_active.size(),
        );
        println!(
            "FRM: {}; active: {} (size 0x{:x})",
            self.young.from_total(),
            self.from_active,
            self.from_active.size(),
        );
        println!(
            " TO: {}; active: {} (size 0x{:x})",
            self.young.to_total(),
            self.to_active,
            self.to_active.size(),
        );
        {
            println!(
                "OLD total: {} (size 0x{:x})",
                self.old.total(),
                self.old.total().size()
            );
            for old_region in &self.old_protected.regions {
                println!(
                    "OLD region: {}; active: {} (size 0x{:x})",
                    old_region.committed_region(),
                    old_region.active_region(),
                    old_region.active_size(),
                );
            }
        }

        println!(
            "LRG: {}-{}",
            self.large.total().start,
            self.large.total().end
        );
        println!(
            "TTL: {}-{}",
            self.reserved_area.start, self.reserved_area.end
        );
        println!(
            "found invalid reference to {} in {} (at {}, in object {}).",
            reference,
            name,
            slot.address(),
            container_obj
        );

        if self.young.contains(reference)
            && !self.from_active.contains(reference)
            && !self.eden_active.contains(reference)
        {
            println!("reference points into young generation but not into the active semi-space.");
        }

        println!("try print object size and class:");

        let object = reference.to_obj();
        println!("\tsize {}", object.size());
        let cls = object.header().vtbl().class();
        println!("\tclass {}", cls.name(get_vm()));

        panic!("reference neither pointing into young nor old generation.");
    }
}
<|endoftext|>


<commit_before>//! External function calls.
//!
//! To a Cretonne function, all functions are "external". Directly called functions must be
//! declared in the preamble, and all function calls must have a signature.
//!
//! This module declares the data types used to represent external functions and call signatures.

use ir::{Type, FunctionName, SigRef, ArgumentLoc};
use isa::{RegInfo, RegUnit};
use std::cmp;
use std::fmt;
use std::str::FromStr;

/// Function signature.
///
/// The function signature describes the types of arguments and return values along with other
/// details that are needed to call a function correctly.
///
/// A signature can optionally include ISA-specific ABI information which specifies exactly how
/// arguments and return values are passed.
#[derive(Clone, Debug)]
pub struct Signature {
    /// Types of the arguments passed to the function.
    pub argument_types: Vec<ArgumentType>,
    /// Types returned from the function.
    pub return_types: Vec<ArgumentType>,

    /// Calling convention.
    pub call_conv: CallConv,

    /// When the signature has been legalized to a specific ISA, this holds the size of the
    /// argument array on the stack. Before legalization, this is `None`.
    ///
    /// This can be computed from the legalized `argument_types` array as the maximum (offset plus
    /// byte size) of the `ArgumentLoc::Stack(offset)` argument.
    pub argument_bytes: Option<u32>,
}

impl Signature {
    /// Create a new blank signature.
    pub fn new(call_conv: CallConv) -> Signature {
        Signature {
            argument_types: Vec::new(),
            return_types: Vec::new(),
            call_conv,
            argument_bytes: None,
        }
    }

    /// Clear the signature so it is identical to a fresh one returned by `new()`.
    pub fn clear(&mut self, call_conv: CallConv) {
        self.argument_types.clear();
        self.return_types.clear();
        self.call_conv = call_conv;
        self.argument_bytes = None;
    }

    /// Compute the size of the stack arguments and mark signature as legalized.
    ///
    /// Even if there are no stack arguments, this will set `argument_types` to `Some(0)` instead
    /// of `None`. This indicates that the signature has been legalized.
    pub fn compute_argument_bytes(&mut self) {
        let bytes = self.argument_types
            .iter()
            .filter_map(|arg| match arg.location {
                ArgumentLoc::Stack(offset) if offset >= 0 => {
                    Some(offset as u32 + arg.value_type.bytes())
                }
                _ => None,
            })
            .fold(0, cmp::max);
        self.argument_bytes = Some(bytes);
    }

    /// Return an object that can display `self` with correct register names.
    pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplaySignature<'a> {
        DisplaySignature(self, regs.into())
    }

    /// Find the index of a presumed unique special-purpose argument.
    pub fn special_arg_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
        self.argument_types.iter().rposition(
            |arg| arg.purpose == purpose,
        )
    }
}

/// Wrapper type capable of displaying a `Signature` with correct register names.
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);

fn write_list(
    f: &mut fmt::Formatter,
    args: &[ArgumentType],
    regs: Option<&RegInfo>,
) -> fmt::Result {
    match args.split_first() {
        None => {}
        Some((first, rest)) => {
            write!(f, "{}", first.display(regs))?;
            for arg in rest {
                write!(f, ", {}", arg.display(regs))?;
            }
        }
    }
    Ok(())
}

impl<'a> fmt::Display for DisplaySignature<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        write_list(f, &self.0.argument_types, self.1)?;
        write!(f, ")")?;
        if !self.0.return_types.is_empty() {
            write!(f, " -> ")?;
            write_list(f, &self.0.return_types, self.1)?;
        }
        write!(f, " {}", self.0.call_conv)
    }
}

impl fmt::Display for Signature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.display(None).fmt(f)
    }
}

/// Function argument or return value type.
///
/// This describes the value type being passed to or from a function along with flags that affect
/// how the argument is passed.
#[derive(Copy, Clone, Debug)]
pub struct ArgumentType {
    /// Type of the argument value.
    pub value_type: Type,
    /// Special purpose of argument, or `Normal`.
    pub purpose: ArgumentPurpose,
    /// Method for extending argument to a full register.
    pub extension: ArgumentExtension,

    /// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
    /// been legalized.
    pub location: ArgumentLoc,
}

impl ArgumentType {
    /// Create an argument type with default flags.
    pub fn new(vt: Type) -> ArgumentType {
        ArgumentType {
            value_type: vt,
            extension: ArgumentExtension::None,
            purpose: ArgumentPurpose::Normal,
            location: Default::default(),
        }
    }

    /// Create a special-purpose argument type that is not (yet) bound to a specific register.
    pub fn special(vt: Type, purpose: ArgumentPurpose) -> ArgumentType {
        ArgumentType {
            value_type: vt,
            extension: ArgumentExtension::None,
            purpose,
            location: Default::default(),
        }
    }

    /// Create an argument type for a special-purpose register.
    pub fn special_reg(vt: Type, purpose: ArgumentPurpose, regunit: RegUnit) -> ArgumentType {
        ArgumentType {
            value_type: vt,
            extension: ArgumentExtension::None,
            purpose,
            location: ArgumentLoc::Reg(regunit),
        }
    }

    /// Return an object that can display `self` with correct register names.
    pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplayArgumentType<'a> {
        DisplayArgumentType(self, regs.into())
    }
}

/// Wrapper type capable of displaying an `ArgumentType` with correct register names.
pub struct DisplayArgumentType<'a>(&'a ArgumentType, Option<&'a RegInfo>);

impl<'a> fmt::Display for DisplayArgumentType<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.value_type)?;
        match self.0.extension {
            ArgumentExtension::None => {}
            ArgumentExtension::Uext => write!(f, " uext")?,
            ArgumentExtension::Sext => write!(f, " sext")?,
        }
        if self.0.purpose != ArgumentPurpose::Normal {
            write!(f, " {}", self.0.purpose)?;
        }

        if self.0.location.is_assigned() {
            write!(f, " [{}]", self.0.location.display(self.1))?;
        }

        Ok(())
    }
}

impl fmt::Display for ArgumentType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.display(None).fmt(f)
    }
}

/// Function argument extension options.
///
/// On some architectures, small integer function arguments are extended to the width of a
/// general-purpose register.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArgumentExtension {
    /// No extension, high bits are indeterminate.
    None,
    /// Unsigned extension: high bits in register are 0.
    Uext,
    /// Signed extension: high bits in register replicate sign bit.
    Sext,
}

/// The special purpose of a function argument.
///
/// Function arguments and return values are used to pass user program values between functions,
/// but they are also used to represent special registers with significance to the ABI such as
/// frame pointers and callee-saved registers.
///
/// The argument purpose is used to indicate any special meaning of an argument or return value.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArgumentPurpose {
    /// A normal user program value passed to or from a function.
    Normal,

    /// Struct return pointer.
    ///
    /// When a function needs to return more data than will fit in registers, the caller passes a
    /// pointer to a memory location where the return value can be written. In some ABIs, this
    /// struct return pointer is passed in a specific register.
    ///
    /// This argument kind can also appear as a return value for ABIs that require a function with
    /// a `StructReturn` pointer argument to also return that pointer in a register.
    StructReturn,

    /// The link register.
    ///
    /// Most RISC architectures implement calls by saving the return address in a designated
    /// register rather than pushing it on the stack. This is represented with a `Link` argument.
    ///
    /// Similarly, some return instructions expect the return address in a register represented as
    /// a `Link` return value.
    Link,

    /// The frame pointer.
    ///
    /// This indicates the frame pointer register which has a special meaning in some ABIs.
    ///
    /// The frame pointer appears as an argument and as a return value since it is a callee-saved
    /// register.
    FramePointer,

    /// A callee-saved register.
    ///
    /// Some calling conventions have registers that must be saved by the callee. These registers
    /// are represented as `CalleeSaved` arguments and return values.
    CalleeSaved,

    /// A VM context pointer.
    ///
    /// This is a pointer to a context struct containing details about the current sandbox. It is
    /// used as a base pointer for `vmctx` global variables.
    VMContext,

    /// A signature identifier.
    ///
    /// This is a special-purpose argument used to identify the calling convention expected by the
    /// caller in an indirect call. The callee can verify that the expected signature ID matches.
    SignatureId,
}

/// Text format names of the `ArgumentPurpose` variants.
static PURPOSE_NAMES: [&str; 7] = ["normal", "sret", "link", "fp", "csr", "vmctx", "sigid"];

impl fmt::Display for ArgumentPurpose {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(PURPOSE_NAMES[*self as usize])
    }
}

impl FromStr for ArgumentPurpose {
    type Err = ();
    fn from_str(s: &str) -> Result<ArgumentPurpose, ()> {
        match s {
            "normal" => Ok(ArgumentPurpose::Normal),
            "sret" => Ok(ArgumentPurpose::StructReturn),
            "link" => Ok(ArgumentPurpose::Link),
            "fp" => Ok(ArgumentPurpose::FramePointer),
            "csr" => Ok(ArgumentPurpose::CalleeSaved),
            "vmctx" => Ok(ArgumentPurpose::VMContext),
            "sigid" => Ok(ArgumentPurpose::SignatureId),
            _ => Err(()),
        }
    }
}

/// An external function.
///
/// Information about a function that can be called directly with a direct `call` instruction.
#[derive(Clone, Debug)]
pub struct ExtFuncData {
    /// Name of the external function.
    pub name: FunctionName,
    /// Call signature of function.
    pub signature: SigRef,
}

impl fmt::Display for ExtFuncData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.signature, self.name)
    }
}

/// A Calling convention.
///
/// A function's calling convention determines exactly how arguments and return values are passed,
/// and how stack frames are managed. Since all of these details depend on both the instruction set
/// architecture and possibly the operating system, a function's calling convention is only fully
/// determined by a `(TargetIsa, CallConv)` tuple.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallConv {
    /// The C calling convention.
    ///
    /// This is the native calling convention that a C compiler would use on the platform.
    Native,

    /// A JIT-compiled WebAssembly function in the SpiderMonkey VM.
    SpiderWASM,
}

impl fmt::Display for CallConv {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::CallConv::*;
        f.write_str(match *self {
            Native => "native",
            SpiderWASM => "spiderwasm",
        })
    }
}

impl FromStr for CallConv {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::CallConv::*;
        match s {
            "native" => Ok(Native),
            "spiderwasm" => Ok(SpiderWASM),
            _ => Err(()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ir::types::{I32, F32, B8};

    #[test]
    fn argument_type() {
        let mut t = ArgumentType::new(I32);
        assert_eq!(t.to_string(), "i32");
        t.extension = ArgumentExtension::Uext;
        assert_eq!(t.to_string(), "i32 uext");
        t.purpose = ArgumentPurpose::StructReturn;
        assert_eq!(t.to_string(), "i32 uext sret");
    }

    #[test]
    fn argument_purpose() {
        let all_purpose = [
            ArgumentPurpose::Normal,
            ArgumentPurpose::StructReturn,
            ArgumentPurpose::Link,
            ArgumentPurpose::FramePointer,
            ArgumentPurpose::CalleeSaved,
            ArgumentPurpose::VMContext,
        ];
        for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) {
            assert_eq!(e.to_string(), n);
            assert_eq!(Ok(e), n.parse());
        }
    }

    #[test]
    fn call_conv() {
        for &cc in &[CallConv::Native, CallConv::SpiderWASM] {
            assert_eq!(Ok(cc), cc.to_string().parse())
        }
    }

    #[test]
    fn signatures() {
        let mut sig = Signature::new(CallConv::SpiderWASM);
        assert_eq!(sig.to_string(), "() spiderwasm");
        sig.argument_types.push(ArgumentType::new(I32));
        assert_eq!(sig.to_string(), "(i32) spiderwasm");
        sig.return_types.push(ArgumentType::new(F32));
        assert_eq!(sig.to_string(), "(i32) -> f32 spiderwasm");
        sig.argument_types.push(
            ArgumentType::new(I32.by(4).unwrap()),
        );
        assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 spiderwasm");
        sig.return_types.push(ArgumentType::new(B8));
        assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 spiderwasm");

        // Test the offset computation algorithm.
        assert_eq!(sig.argument_bytes, None);
        sig.argument_types[1].location = ArgumentLoc::Stack(8);
        sig.compute_argument_bytes();
        // An `i32x4` at offset 8 requires a 24-byte argument array.
        assert_eq!(sig.argument_bytes, Some(24));
        // Order does not matter.
        sig.argument_types[0].location = ArgumentLoc::Stack(24);
        sig.compute_argument_bytes();
        assert_eq!(sig.argument_bytes, Some(28));

        // Writing ABI-annotated signatures.
        assert_eq!(
            sig.to_string(),
            "(i32 [24], i32x4 [8]) -> f32, b8 spiderwasm"
        );
    }
}
<commit_msg>Add uext() and sext() builder methods to ArgumentType.<commit_after>//! External function calls.
//!
//! To a Cretonne function, all functions are "external". Directly called functions must be
//! declared in the preamble, and all function calls must have a signature.
//!
//! This module declares the data types used to represent external functions and call signatures.

use ir::{Type, FunctionName, SigRef, ArgumentLoc};
use isa::{RegInfo, RegUnit};
use std::cmp;
use std::fmt;
use std::str::FromStr;

/// Function signature.
///
/// The function signature describes the types of arguments and return values along with other
/// details that are needed to call a function correctly.
///
/// A signature can optionally include ISA-specific ABI information which specifies exactly how
/// arguments and return values are passed.
#[derive(Clone, Debug)]
pub struct Signature {
    /// Types of the arguments passed to the function.
    pub argument_types: Vec<ArgumentType>,
    /// Types returned from the function.
    pub return_types: Vec<ArgumentType>,

    /// Calling convention.
    pub call_conv: CallConv,

    /// When the signature has been legalized to a specific ISA, this holds the size of the
    /// argument array on the stack. Before legalization, this is `None`.
    ///
    /// This can be computed from the legalized `argument_types` array as the maximum (offset plus
    /// byte size) of the `ArgumentLoc::Stack(offset)` argument.
    pub argument_bytes: Option<u32>,
}

impl Signature {
    /// Create a new blank signature.
    pub fn new(call_conv: CallConv) -> Signature {
        Signature {
            argument_types: Vec::new(),
            return_types: Vec::new(),
            call_conv,
            argument_bytes: None,
        }
    }

    /// Clear the signature so it is identical to a fresh one returned by `new()`.
    pub fn clear(&mut self, call_conv: CallConv) {
        self.argument_types.clear();
        self.return_types.clear();
        self.call_conv = call_conv;
        self.argument_bytes = None;
    }

    /// Compute the size of the stack arguments and mark signature as legalized.
    ///
    /// Even if there are no stack arguments, this will set `argument_types` to `Some(0)` instead
    /// of `None`. This indicates that the signature has been legalized.
    pub fn compute_argument_bytes(&mut self) {
        let bytes = self.argument_types
            .iter()
            .filter_map(|arg| match arg.location {
                ArgumentLoc::Stack(offset) if offset >= 0 => {
                    Some(offset as u32 + arg.value_type.bytes())
                }
                _ => None,
            })
            .fold(0, cmp::max);
        self.argument_bytes = Some(bytes);
    }

    /// Return an object that can display `self` with correct register names.
    pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplaySignature<'a> {
        DisplaySignature(self, regs.into())
    }

    /// Find the index of a presumed unique special-purpose argument.
    pub fn special_arg_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
        self.argument_types.iter().rposition(
            |arg| arg.purpose == purpose,
        )
    }
}

/// Wrapper type capable of displaying a `Signature` with correct register names.
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);

fn write_list(
    f: &mut fmt::Formatter,
    args: &[ArgumentType],
    regs: Option<&RegInfo>,
) -> fmt::Result {
    match args.split_first() {
        None => {}
        Some((first, rest)) => {
            write!(f, "{}", first.display(regs))?;
            for arg in rest {
                write!(f, ", {}", arg.display(regs))?;
            }
        }
    }
    Ok(())
}

impl<'a> fmt::Display for DisplaySignature<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(")?;
        write_list(f, &self.0.argument_types, self.1)?;
        write!(f, ")")?;
        if !self.0.return_types.is_empty() {
            write!(f, " -> ")?;
            write_list(f, &self.0.return_types, self.1)?;
        }
        write!(f, " {}", self.0.call_conv)
    }
}

impl fmt::Display for Signature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.display(None).fmt(f)
    }
}

/// Function argument or return value type.
///
/// This describes the value type being passed to or from a function along with flags that affect
/// how the argument is passed.
#[derive(Copy, Clone, Debug)]
pub struct ArgumentType {
    /// Type of the argument value.
    pub value_type: Type,
    /// Special purpose of argument, or `Normal`.
    pub purpose: ArgumentPurpose,
    /// Method for extending argument to a full register.
    pub extension: ArgumentExtension,

    /// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
    /// been legalized.
    pub location: ArgumentLoc,
}

impl ArgumentType {
    /// Create an argument type with default flags.
    pub fn new(vt: Type) -> ArgumentType {
        ArgumentType {
            value_type: vt,
            extension: ArgumentExtension::None,
            purpose: ArgumentPurpose::Normal,
            location: Default::default(),
        }
    }

    /// Create a special-purpose argument type that is not (yet) bound to a specific register.
    pub fn special(vt: Type, purpose: ArgumentPurpose) -> ArgumentType {
        ArgumentType {
            value_type: vt,
            extension: ArgumentExtension::None,
            purpose,
            location: Default::default(),
        }
    }

    /// Create an argument type for a special-purpose register.
    pub fn special_reg(vt: Type, purpose: ArgumentPurpose, regunit: RegUnit) -> ArgumentType {
        ArgumentType {
            value_type: vt,
            extension: ArgumentExtension::None,
            purpose,
            location: ArgumentLoc::Reg(regunit),
        }
    }

    /// Convert `self` to an argument type with the `uext` flag set.
    pub fn uext(self) -> ArgumentType {
        debug_assert!(self.value_type.is_int(), "uext on {} arg", self.value_type);
        ArgumentType {
            extension: ArgumentExtension::Uext,
            ..self
        }
    }

    /// Convert `self` to an argument type with the `sext` flag set.
    pub fn sext(self) -> ArgumentType {
        debug_assert!(self.value_type.is_int(), "sext on {} arg", self.value_type);
        ArgumentType {
            extension: ArgumentExtension::Sext,
            ..self
        }
    }

    /// Return an object that can display `self` with correct register names.
    pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplayArgumentType<'a> {
        DisplayArgumentType(self, regs.into())
    }
}

/// Wrapper type capable of displaying an `ArgumentType` with correct register names.
pub struct DisplayArgumentType<'a>(&'a ArgumentType, Option<&'a RegInfo>);

impl<'a> fmt::Display for DisplayArgumentType<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.value_type)?;
        match self.0.extension {
            ArgumentExtension::None => {}
            ArgumentExtension::Uext => write!(f, " uext")?,
            ArgumentExtension::Sext => write!(f, " sext")?,
        }
        if self.0.purpose != ArgumentPurpose::Normal {
            write!(f, " {}", self.0.purpose)?;
        }

        if self.0.location.is_assigned() {
            write!(f, " [{}]", self.0.location.display(self.1))?;
        }

        Ok(())
    }
}

impl fmt::Display for ArgumentType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.display(None).fmt(f)
    }
}

/// Function argument extension options.
///
/// On some architectures, small integer function arguments are extended to the width of a
/// general-purpose register.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArgumentExtension {
    /// No extension, high bits are indeterminate.
    None,
    /// Unsigned extension: high bits in register are 0.
    Uext,
    /// Signed extension: high bits in register replicate sign bit.
    Sext,
}

/// The special purpose of a function argument.
///
/// Function arguments and return values are used to pass user program values between functions,
/// but they are also used to represent special registers with significance to the ABI such as
/// frame pointers and callee-saved registers.
///
/// The argument purpose is used to indicate any special meaning of an argument or return value.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArgumentPurpose {
    /// A normal user program value passed to or from a function.
    Normal,

    /// Struct return pointer.
    ///
    /// When a function needs to return more data than will fit in registers, the caller passes a
    /// pointer to a memory location where the return value can be written. In some ABIs, this
    /// struct return pointer is passed in a specific register.
    ///
    /// This argument kind can also appear as a return value for ABIs that require a function with
    /// a `StructReturn` pointer argument to also return that pointer in a register.
    StructReturn,

    /// The link register.
    ///
    /// Most RISC architectures implement calls by saving the return address in a designated
    /// register rather than pushing it on the stack. This is represented with a `Link` argument.
    ///
    /// Similarly, some return instructions expect the return address in a register represented as
    /// a `Link` return value.
    Link,

    /// The frame pointer.
    ///
    /// This indicates the frame pointer register which has a special meaning in some ABIs.
    ///
    /// The frame pointer appears as an argument and as a return value since it is a callee-saved
    /// register.
    FramePointer,

    /// A callee-saved register.
    ///
    /// Some calling conventions have registers that must be saved by the callee. These registers
    /// are represented as `CalleeSaved` arguments and return values.
    CalleeSaved,

    /// A VM context pointer.
    ///
    /// This is a pointer to a context struct containing details about the current sandbox. It is
    /// used as a base pointer for `vmctx` global variables.
    VMContext,

    /// A signature identifier.
    ///
    /// This is a special-purpose argument used to identify the calling convention expected by the
    /// caller in an indirect call. The callee can verify that the expected signature ID matches.
    SignatureId,
}

/// Text format names of the `ArgumentPurpose` variants.
static PURPOSE_NAMES: [&str; 7] = ["normal", "sret", "link", "fp", "csr", "vmctx", "sigid"];

impl fmt::Display for ArgumentPurpose {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(PURPOSE_NAMES[*self as usize])
    }
}

impl FromStr for ArgumentPurpose {
    type Err = ();
    fn from_str(s: &str) -> Result<ArgumentPurpose, ()> {
        match s {
            "normal" => Ok(ArgumentPurpose::Normal),
            "sret" => Ok(ArgumentPurpose::StructReturn),
            "link" => Ok(ArgumentPurpose::Link),
            "fp" => Ok(ArgumentPurpose::FramePointer),
            "csr" => Ok(ArgumentPurpose::CalleeSaved),
            "vmctx" => Ok(ArgumentPurpose::VMContext),
            "sigid" => Ok(ArgumentPurpose::SignatureId),
            _ => Err(()),
        }
    }
}

/// An external function.
///
/// Information about a function that can be called directly with a direct `call` instruction.
#[derive(Clone, Debug)]
pub struct ExtFuncData {
    /// Name of the external function.
    pub name: FunctionName,
    /// Call signature of function.
    pub signature: SigRef,
}

impl fmt::Display for ExtFuncData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} {}", self.signature, self.name)
    }
}

/// A Calling convention.
///
/// A function's calling convention determines exactly how arguments and return values are passed,
/// and how stack frames are managed. Since all of these details depend on both the instruction set
/// architecture and possibly the operating system, a function's calling convention is only fully
/// determined by a `(TargetIsa, CallConv)` tuple.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallConv {
    /// The C calling convention.
    ///
    /// This is the native calling convention that a C compiler would use on the platform.
    Native,

    /// A JIT-compiled WebAssembly function in the SpiderMonkey VM.
    SpiderWASM,
}

impl fmt::Display for CallConv {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::CallConv::*;
        f.write_str(match *self {
            Native => "native",
            SpiderWASM => "spiderwasm",
        })
    }
}

impl FromStr for CallConv {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::CallConv::*;
        match s {
            "native" => Ok(Native),
            "spiderwasm" => Ok(SpiderWASM),
            _ => Err(()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ir::types::{I32, F32, B8};

    #[test]
    fn argument_type() {
        let t = ArgumentType::new(I32);
        assert_eq!(t.to_string(), "i32");
        let mut t = t.uext();
        assert_eq!(t.to_string(), "i32 uext");
        assert_eq!(t.sext().to_string(), "i32 sext");
        t.purpose = ArgumentPurpose::StructReturn;
        assert_eq!(t.to_string(), "i32 uext sret");
    }

    #[test]
    fn argument_purpose() {
        let all_purpose = [
            ArgumentPurpose::Normal,
            ArgumentPurpose::StructReturn,
            ArgumentPurpose::Link,
            ArgumentPurpose::FramePointer,
            ArgumentPurpose::CalleeSaved,
            ArgumentPurpose::VMContext,
        ];
        for (&e, &n) in all_purpose.iter().zip(PURPOSE_NAMES.iter()) {
            assert_eq!(e.to_string(), n);
            assert_eq!(Ok(e), n.parse());
        }
    }

    #[test]
    fn call_conv() {
        for &cc in &[CallConv::Native, CallConv::SpiderWASM] {
            assert_eq!(Ok(cc), cc.to_string().parse())
        }
    }

    #[test]
    fn signatures() {
        let mut sig = Signature::new(CallConv::SpiderWASM);
        assert_eq!(sig.to_string(), "() spiderwasm");
        sig.argument_types.push(ArgumentType::new(I32));
        assert_eq!(sig.to_string(), "(i32) spiderwasm");
        sig.return_types.push(ArgumentType::new(F32));
        assert_eq!(sig.to_string(), "(i32) -> f32 spiderwasm");
        sig.argument_types.push(
            ArgumentType::new(I32.by(4).unwrap()),
        );
        assert_eq!(sig.to_string(), "(i32, i32x4) -> f32 spiderwasm");
        sig.return_types.push(ArgumentType::new(B8));
        assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8 spiderwasm");

        // Test the offset computation algorithm.
        assert_eq!(sig.argument_bytes, None);
        sig.argument_types[1].location = ArgumentLoc::Stack(8);
        sig.compute_argument_bytes();
        // An `i32x4` at offset 8 requires a 24-byte argument array.
        assert_eq!(sig.argument_bytes, Some(24));
        // Order does not matter.
        sig.argument_types[0].location = ArgumentLoc::Stack(24);
        sig.compute_argument_bytes();
        assert_eq!(sig.argument_bytes, Some(28));

        // Writing ABI-annotated signatures.
        assert_eq!(
            sig.to_string(),
            "(i32 [24], i32x4 [8]) -> f32, b8 spiderwasm"
        );
    }
}
<|endoftext|>


<commit_before>//! Contains the `HandshakePacket` structure, and a `HandshakePacketBuilder` to
//! construct it field by field.
//!
//! # Example
//!
//! ```
//! use fcp_cryptoauth::handshake_packet::{HandshakePacketBuilder, HandshakePacket, HandshakePacketType};
//! let packet = HandshakePacketBuilder::new()
//!         .packet_type(&HandshakePacketType::Key)
//!         .auth_challenge(&[1u8; 12])
//!         .random_nonce(&[2u8; 24])
//!         .sender_perm_pub_key(&[3u8; 32])
//!         .msg_auth_code(&[4u8; 16])
//!         .sender_encrypted_temp_pub_key(&[5u8; 32])
//!         .encrypted_data(&vec![6u8, 7u8, 8u8])
//!         .finalize()
//!         .unwrap();
//! assert_eq!(packet.packet_type(), Ok(HandshakePacketType::Key));
//! assert_eq!(packet.auth_challenge(), [1u8; 12]);
//! assert_eq!(packet.random_nonce(), [2u8; 24]);
//! assert_eq!(packet.sender_perm_pub_key(), [3u8; 32]);
//! assert_eq!(packet.msg_auth_code(), [4u8; 16]);
//! assert_eq!(packet.sender_encrypted_temp_pub_key(), [5u8; 32]);
//! assert_eq!(packet.encrypted_data(), [6u8, 7u8, 8u8]);
//! assert_eq!(packet.raw, vec![
//!         // session state
//!         0, 0, 0, 2,
//!
//!         // auth challenge
//!                      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
//!
//!         // random nonce
//!         2, 2, 2, 2,  2, 2, 2, 2,  2, 2, 2, 2,  2, 2, 2, 2,
//!         2, 2, 2, 2,  2, 2, 2, 2,
//!
//!         // sender's permanent public key
//!                                   3, 3, 3, 3,  3, 3, 3, 3,
//!         3, 3, 3, 3,  3, 3, 3, 3,  3, 3, 3, 3,  3, 3, 3, 3,
//!         3, 3, 3, 3,  3, 3, 3, 3,
//!
//!         // message authentication code
//!                                   4, 4, 4, 4,  4, 4, 4, 4,
//!         4, 4, 4, 4,  4, 4, 4, 4,
//!
//!         // sender's encrypted temporary public key
//!                                   5, 5, 5, 5,  5, 5, 5, 5,
//!         5, 5, 5, 5,  5, 5, 5, 5,  5, 5, 5, 5,  5, 5, 5, 5,
//!         5, 5, 5, 5,  5, 5, 5, 5,
//!
//!         // encrypted data
//!                                   6, 7, 8,
//!         ])
//! ```

extern crate hex;
extern crate byteorder;
use std::fmt;

use hex::ToHex;
use handshake_packet::byteorder::ByteOrder;
use keys::ToBase32;
use keys::crypto_box::PublicKey;

/// Represents the CryptoAuth session state, as defined by
/// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#protocol
#[derive(Eq)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub enum HandshakePacketType {
    Hello,       // 0u32
    RepeatHello, // 1u32
    Key,         // 2u32
    RepeatKey,   // 3u32
}

/// Represents a raw CryptoAuth packet, as defined by
/// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#packet-layout
pub struct HandshakePacket {
    pub raw: Vec<u8>,
}

impl fmt::Debug for HandshakePacket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HandshakePacket {{
        raw:                     0x{},
        state:                   {:?},
        auth_challenge:          0x{},
        nonce:                   0x{},
        sender_perm_pub_key:     0x{} ({}),
        msg_auth_code:           0x{},
        sender_enc_temp_pub_key: 0x{},
        encrypted_data:          0x{}
        }}",
            self.raw.to_vec().to_hex(),
            self.packet_type(),
            self.auth_challenge().to_vec().to_hex(),
            self.random_nonce().to_vec().to_hex(),
            self.sender_perm_pub_key().to_vec().to_hex(), PublicKey(self.sender_perm_pub_key()).to_base32(),
            self.msg_auth_code().to_vec().to_hex(),
            self.sender_encrypted_temp_pub_key().to_vec().to_hex(),
            self.encrypted_data().to_vec().to_hex()
            )
    }
}

impl HandshakePacket {
    pub fn new_from_raw(raw: Vec<u8>) -> Result<HandshakePacket, Vec<u8>> {
        if raw.len() < 120 {
            Err(raw)
        }
        else {
            Ok(HandshakePacket { raw })
        }
    }
    /// Returns the session state of the packet if it is a known session
    /// state, as defined by
    /// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#protocol
    ///
    /// Returns Err(value of field) if the session state field is set
    /// to an unknown value.
    pub fn packet_type(&self) -> Result<HandshakePacketType, u32> {
        match byteorder::BigEndian::read_u32(&self.raw[0..4]) {
            0u32 => Ok(HandshakePacketType::Hello),
            1u32 => Ok(HandshakePacketType::RepeatHello),
            2u32 => Ok(HandshakePacketType::Key),
            3u32 => Ok(HandshakePacketType::RepeatKey),
            n => Err(n),
        }
    }

    /// Returns a copy of the packet's Authorization Challenge.
    pub fn auth_challenge(&self) -> [u8; 12] {
        let mut auth_challenge = [0u8; 12];
        auth_challenge.copy_from_slice(&self.raw[4..16]);
        auth_challenge
    }

    /// Returns a copy of the packet's Nonce.
    pub fn random_nonce(&self) -> [u8; 24] {
        let mut random_nonce = [0u8; 24];
        random_nonce.copy_from_slice(&self.raw[16..40]);
        random_nonce
    }

    /// Returns a copy of the packet's sender permanent public key,
    /// as a byte array.
    pub fn sender_perm_pub_key(&self) -> [u8; 32] {
        let mut sender_perm_pub_key = [0u8; 32];
        sender_perm_pub_key.copy_from_slice(&self.raw[40..72]);
        sender_perm_pub_key
    }

    /// Returns a copy of the packet's Message Authentication Code.
    pub fn msg_auth_code(&self) -> [u8; 16] {
        let mut msg_auth_code = [0u8; 16];
        msg_auth_code.copy_from_slice(&self.raw[72..88]);
        msg_auth_code
    }

    /// Returns a copy of the packet's sender temporary public key,
    /// encrypted and as a byte array.
    pub fn sender_encrypted_temp_pub_key(&self) -> [u8; 32] {
        let mut sender_encrypted_temp_pub_key = [0u8; 32];
        sender_encrypted_temp_pub_key.copy_from_slice(&self.raw[88..120]);
        sender_encrypted_temp_pub_key
    }

    /// Returns a reference to the packet's encrypted “piggy-backed” data.
    pub fn encrypted_data(&self) -> &[u8] {
        // Returning a reference to a slice here because:
        // * the data can potentially be large
        // * this is likely to be the last field read, ie. the last use of
        //   the Package object so a copy would be useless.
        &self.raw[120..]
    }

    /// Returns msg_auth_code, sender_encrypted_temp_pub_key, and encrypted_data
    /// concatenated in that order.
    /// The purpose is to use it as input to the 'open' cryptographic primitive.
    pub fn sealed_data(&self) -> &[u8] {
        &self.raw[72..]
    }
}

/// Used to construct a `HandshakePacket` object field-by-field.
#[derive(Debug)]
pub struct HandshakePacketBuilder {
    pub raw: Vec<u8>,

    // Store whether these fields have been set.
    // Checked by the finalization method.
    packet_type: bool,
    auth_challenge: bool,
    random_nonce: bool,
    sender_perm_pub_key: bool,
    msg_auth_code: bool,
    sender_encrypted_temp_pub_key: bool,
    //encrypted_data: bool,
}

impl HandshakePacketBuilder {
    pub fn new() -> HandshakePacketBuilder {
        HandshakePacketBuilder {
            raw: vec![0; 120],

            packet_type: false,
            auth_challenge: false,
            random_nonce: false,
            sender_perm_pub_key: false,
            msg_auth_code: false,
            sender_encrypted_temp_pub_key: false,
            //encrypted_data: false, // Not mandatory
        }
    }

    /// Sets the session state of the packet.
    pub fn packet_type(mut self, packet_type: &HandshakePacketType) -> HandshakePacketBuilder {
        self.packet_type = true;
        let value = match *packet_type {
            HandshakePacketType::Hello => 0u32,
            HandshakePacketType::RepeatHello => 1u32,
            HandshakePacketType::Key => 2u32,
            HandshakePacketType::RepeatKey => 3u32,
        };
        byteorder::BigEndian::write_u32(&mut self.raw[0..4], value);
        self
    }

    /// Sets the packet's Authorization Challenge.
    pub fn auth_challenge(mut self, auth_challenge: &[u8; 12]) -> HandshakePacketBuilder {
        self.auth_challenge = true;
        self.raw[4..16].clone_from_slice(auth_challenge);
        self
    }

    /// Sets the packet's Nonce.
    pub fn random_nonce(mut self, random_nonce: &[u8; 24]) -> HandshakePacketBuilder {
        self.random_nonce = true;
        self.raw[16..40].clone_from_slice(random_nonce);
        self
    }

    /// Sets the packet's sender permanent public key, provided as a byte array.
    pub fn sender_perm_pub_key(mut self, sender_perm_pub_key: &[u8; 32]) -> HandshakePacketBuilder {
        self.sender_perm_pub_key = true;
        self.raw[40..72].clone_from_slice(sender_perm_pub_key);
        self
    }

    /// Sets the packet's Message Authentication Code.
    pub fn msg_auth_code(mut self, msg_auth_code: &[u8; 16]) -> HandshakePacketBuilder {
        self.msg_auth_code = true;
        self.raw[72..88].clone_from_slice(msg_auth_code);
        self
    }

    /// Sets the packet's sender temporary public key, provided encrypted and
    /// as a byte array.
    pub fn sender_encrypted_temp_pub_key(mut self, sender_encrypted_temp_pub_key: &[u8; 32]) -> HandshakePacketBuilder {
        self.sender_encrypted_temp_pub_key = true;
        self.raw[88..120].clone_from_slice(sender_encrypted_temp_pub_key);
        self
    }

    /// Sets the packet's encrypted “piggy-backed” data.
    pub fn encrypted_data(mut self, encrypted_data: &[u8]) -> HandshakePacketBuilder {
        self.raw.truncate(120);
        self.raw.extend_from_slice(encrypted_data);
        self
    }

    /// Sets the packet's msg_auth_code, sender_encrypted_temp_pub_key,
    /// and encrypted_data concatenated in that order.
    /// The purpose is to use it as output of the 'seal' cryptographic primitive.
    pub fn sealed_data(mut self, sealed_data: &[u8]) -> HandshakePacketBuilder {
        self.raw.truncate(72);
        self.raw.extend_from_slice(sealed_data);
        self
    }

    /// If all mandatory fields have been provided, returns the constructed packet.
    /// Otherwise, returns `Err([packet_type, auth_challenge, random_nonce,
    /// sender_perm_pub_key, msg_auth_code, sender_encrypted_temp_pub_key])`,
    /// Each of the items of the array being a boolean set to true if and only
    /// if the corresponding mandatory field was provided.
    pub fn finalize(self) -> Result<HandshakePacket, [bool; 6]> {
        let booleans = [self.packet_type, self.auth_challenge, self.random_nonce, self.sender_perm_pub_key, self.msg_auth_code, self.sender_encrypted_temp_pub_key];
        if booleans.contains(&false) {
            Err(booleans)
        }
        else {
            Ok(HandshakePacket { raw: self.raw, })
        }
    }
}
<commit_msg>Better Debug for handshake packets.<commit_after>//! Contains the `HandshakePacket` structure, and a `HandshakePacketBuilder` to
//! construct it field by field.
//!
//! # Example
//!
//! ```
//! use fcp_cryptoauth::handshake_packet::{HandshakePacketBuilder, HandshakePacket, HandshakePacketType};
//! let packet = HandshakePacketBuilder::new()
//!         .packet_type(&HandshakePacketType::Key)
//!         .auth_challenge(&[1u8; 12])
//!         .random_nonce(&[2u8; 24])
//!         .sender_perm_pub_key(&[3u8; 32])
//!         .msg_auth_code(&[4u8; 16])
//!         .sender_encrypted_temp_pub_key(&[5u8; 32])
//!         .encrypted_data(&vec![6u8, 7u8, 8u8])
//!         .finalize()
//!         .unwrap();
//! assert_eq!(packet.packet_type(), Ok(HandshakePacketType::Key));
//! assert_eq!(packet.auth_challenge(), [1u8; 12]);
//! assert_eq!(packet.random_nonce(), [2u8; 24]);
//! assert_eq!(packet.sender_perm_pub_key(), [3u8; 32]);
//! assert_eq!(packet.msg_auth_code(), [4u8; 16]);
//! assert_eq!(packet.sender_encrypted_temp_pub_key(), [5u8; 32]);
//! assert_eq!(packet.encrypted_data(), [6u8, 7u8, 8u8]);
//! assert_eq!(packet.raw, vec![
//!         // session state
//!         0, 0, 0, 2,
//!
//!         // auth challenge
//!                      1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
//!
//!         // random nonce
//!         2, 2, 2, 2,  2, 2, 2, 2,  2, 2, 2, 2,  2, 2, 2, 2,
//!         2, 2, 2, 2,  2, 2, 2, 2,
//!
//!         // sender's permanent public key
//!                                   3, 3, 3, 3,  3, 3, 3, 3,
//!         3, 3, 3, 3,  3, 3, 3, 3,  3, 3, 3, 3,  3, 3, 3, 3,
//!         3, 3, 3, 3,  3, 3, 3, 3,
//!
//!         // message authentication code
//!                                   4, 4, 4, 4,  4, 4, 4, 4,
//!         4, 4, 4, 4,  4, 4, 4, 4,
//!
//!         // sender's encrypted temporary public key
//!                                   5, 5, 5, 5,  5, 5, 5, 5,
//!         5, 5, 5, 5,  5, 5, 5, 5,  5, 5, 5, 5,  5, 5, 5, 5,
//!         5, 5, 5, 5,  5, 5, 5, 5,
//!
//!         // encrypted data
//!                                   6, 7, 8,
//!         ])
//! ```

extern crate hex;
extern crate byteorder;
use std::fmt;

use hex::ToHex;
use handshake_packet::byteorder::ByteOrder;
use keys::ToBase32;
use keys::crypto_box::PublicKey;

/// Represents the CryptoAuth session state, as defined by
/// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#protocol
#[derive(Eq)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Clone)]
pub enum HandshakePacketType {
    Hello,       // 0u32
    RepeatHello, // 1u32
    Key,         // 2u32
    RepeatKey,   // 3u32
}

/// Represents a raw CryptoAuth packet, as defined by
/// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#packet-layout
pub struct HandshakePacket {
    pub raw: Vec<u8>,
}

impl fmt::Debug for HandshakePacket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HandshakePacket {{
        raw:                     0x{},
        packet_type:             {:?},
        auth_challenge:          0x{} ({:?}),
        nonce:                   0x{},
        sender_perm_pub_key:     0x{} ({}),
        msg_auth_code:           0x{},
        sender_enc_temp_pub_key: 0x{},
        encrypted_data:          0x{}
        }}",
            self.raw.to_vec().to_hex(),
            self.packet_type(),
            self.auth_challenge().to_vec().to_hex(),
            self.auth_challenge(),
            self.random_nonce().to_vec().to_hex(),
            self.sender_perm_pub_key().to_vec().to_hex(), PublicKey(self.sender_perm_pub_key()).to_base32(),
            self.msg_auth_code().to_vec().to_hex(),
            self.sender_encrypted_temp_pub_key().to_vec().to_hex(),
            self.encrypted_data().to_vec().to_hex()
            )
    }
}

impl HandshakePacket {
    pub fn new_from_raw(raw: Vec<u8>) -> Result<HandshakePacket, Vec<u8>> {
        if raw.len() < 120 {
            Err(raw)
        }
        else {
            Ok(HandshakePacket { raw })
        }
    }
    /// Returns the session state of the packet if it is a known session
    /// state, as defined by
    /// https://github.com/fc00/spec/blob/10b349ab11/cryptoauth.md#protocol
    ///
    /// Returns Err(value of field) if the session state field is set
    /// to an unknown value.
    pub fn packet_type(&self) -> Result<HandshakePacketType, u32> {
        match byteorder::BigEndian::read_u32(&self.raw[0..4]) {
            0u32 => Ok(HandshakePacketType::Hello),
            1u32 => Ok(HandshakePacketType::RepeatHello),
            2u32 => Ok(HandshakePacketType::Key),
            3u32 => Ok(HandshakePacketType::RepeatKey),
            n => Err(n),
        }
    }

    /// Returns a copy of the packet's Authorization Challenge.
    pub fn auth_challenge(&self) -> [u8; 12] {
        let mut auth_challenge = [0u8; 12];
        auth_challenge.copy_from_slice(&self.raw[4..16]);
        auth_challenge
    }

    /// Returns a copy of the packet's Nonce.
    pub fn random_nonce(&self) -> [u8; 24] {
        let mut random_nonce = [0u8; 24];
        random_nonce.copy_from_slice(&self.raw[16..40]);
        random_nonce
    }

    /// Returns a copy of the packet's sender permanent public key,
    /// as a byte array.
    pub fn sender_perm_pub_key(&self) -> [u8; 32] {
        let mut sender_perm_pub_key = [0u8; 32];
        sender_perm_pub_key.copy_from_slice(&self.raw[40..72]);
        sender_perm_pub_key
    }

    /// Returns a copy of the packet's Message Authentication Code.
    pub fn msg_auth_code(&self) -> [u8; 16] {
        let mut msg_auth_code = [0u8; 16];
        msg_auth_code.copy_from_slice(&self.raw[72..88]);
        msg_auth_code
    }

    /// Returns a copy of the packet's sender temporary public key,
    /// encrypted and as a byte array.
    pub fn sender_encrypted_temp_pub_key(&self) -> [u8; 32] {
        let mut sender_encrypted_temp_pub_key = [0u8; 32];
        sender_encrypted_temp_pub_key.copy_from_slice(&self.raw[88..120]);
        sender_encrypted_temp_pub_key
    }

    /// Returns a reference to the packet's encrypted “piggy-backed” data.
    pub fn encrypted_data(&self) -> &[u8] {
        // Returning a reference to a slice here because:
        // * the data can potentially be large
        // * this is likely to be the last field read, ie. the last use of
        //   the Package object so a copy would be useless.
        &self.raw[120..]
    }

    /// Returns msg_auth_code, sender_encrypted_temp_pub_key, and encrypted_data
    /// concatenated in that order.
    /// The purpose is to use it as input to the 'open' cryptographic primitive.
    pub fn sealed_data(&self) -> &[u8] {
        &self.raw[72..]
    }
}

/// Used to construct a `HandshakePacket` object field-by-field.
#[derive(Debug)]
pub struct HandshakePacketBuilder {
    pub raw: Vec<u8>,

    // Store whether these fields have been set.
    // Checked by the finalization method.
    packet_type: bool,
    auth_challenge: bool,
    random_nonce: bool,
    sender_perm_pub_key: bool,
    msg_auth_code: bool,
    sender_encrypted_temp_pub_key: bool,
    //encrypted_data: bool,
}

impl HandshakePacketBuilder {
    pub fn new() -> HandshakePacketBuilder {
        HandshakePacketBuilder {
            raw: vec![0; 120],

            packet_type: false,
            auth_challenge: false,
            random_nonce: false,
            sender_perm_pub_key: false,
            msg_auth_code: false,
            sender_encrypted_temp_pub_key: false,
            //encrypted_data: false, // Not mandatory
        }
    }

    /// Sets the session state of the packet.
    pub fn packet_type(mut self, packet_type: &HandshakePacketType) -> HandshakePacketBuilder {
        self.packet_type = true;
        let value = match *packet_type {
            HandshakePacketType::Hello => 0u32,
            HandshakePacketType::RepeatHello => 1u32,
            HandshakePacketType::Key => 2u32,
            HandshakePacketType::RepeatKey => 3u32,
        };
        byteorder::BigEndian::write_u32(&mut self.raw[0..4], value);
        self
    }

    /// Sets the packet's Authorization Challenge.
    pub fn auth_challenge(mut self, auth_challenge: &[u8; 12]) -> HandshakePacketBuilder {
        self.auth_challenge = true;
        self.raw[4..16].clone_from_slice(auth_challenge);
        self
    }

    /// Sets the packet's Nonce.
    pub fn random_nonce(mut self, random_nonce: &[u8; 24]) -> HandshakePacketBuilder {
        self.random_nonce = true;
        self.raw[16..40].clone_from_slice(random_nonce);
        self
    }

    /// Sets the packet's sender permanent public key, provided as a byte array.
    pub fn sender_perm_pub_key(mut self, sender_perm_pub_key: &[u8; 32]) -> HandshakePacketBuilder {
        self.sender_perm_pub_key = true;
        self.raw[40..72].clone_from_slice(sender_perm_pub_key);
        self
    }

    /// Sets the packet's Message Authentication Code.
    pub fn msg_auth_code(mut self, msg_auth_code: &[u8; 16]) -> HandshakePacketBuilder {
        self.msg_auth_code = true;
        self.raw[72..88].clone_from_slice(msg_auth_code);
        self
    }

    /// Sets the packet's sender temporary public key, provided encrypted and
    /// as a byte array.
    pub fn sender_encrypted_temp_pub_key(mut self, sender_encrypted_temp_pub_key: &[u8; 32]) -> HandshakePacketBuilder {
        self.sender_encrypted_temp_pub_key = true;
        self.raw[88..120].clone_from_slice(sender_encrypted_temp_pub_key);
        self
    }

    /// Sets the packet's encrypted “piggy-backed” data.
    pub fn encrypted_data(mut self, encrypted_data: &[u8]) -> HandshakePacketBuilder {
        self.raw.truncate(120);
        self.raw.extend_from_slice(encrypted_data);
        self
    }

    /// Sets the packet's msg_auth_code, sender_encrypted_temp_pub_key,
    /// and encrypted_data concatenated in that order.
    /// The purpose is to use it as output of the 'seal' cryptographic primitive.
    pub fn sealed_data(mut self, sealed_data: &[u8]) -> HandshakePacketBuilder {
        self.raw.truncate(72);
        self.raw.extend_from_slice(sealed_data);
        self
    }

    /// If all mandatory fields have been provided, returns the constructed packet.
    /// Otherwise, returns `Err([packet_type, auth_challenge, random_nonce,
    /// sender_perm_pub_key, msg_auth_code, sender_encrypted_temp_pub_key])`,
    /// Each of the items of the array being a boolean set to true if and only
    /// if the corresponding mandatory field was provided.
    pub fn finalize(self) -> Result<HandshakePacket, [bool; 6]> {
        let booleans = [self.packet_type, self.auth_challenge, self.random_nonce, self.sender_perm_pub_key, self.msg_auth_code, self.sender_encrypted_temp_pub_key];
        if booleans.contains(&false) {
            Err(booleans)
        }
        else {
            Ok(HandshakePacket { raw: self.raw, })
        }
    }
}
<|endoftext|>


<commit_before>use crossbeam_deque::{Deque, Stealer, Steal};

use std::thread::yield_now;
use std::sync::{Mutex, TryLockError};
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};

use iter::ParallelIterator;
use iter::plumbing::{UnindexedConsumer, UnindexedProducer, bridge_unindexed, Folder};
use current_num_threads;

/// Conversion trait to convert an `Iterator` to a `ParallelIterator`.
///
/// This needs to be distinct from `IntoParallelIterator` because that trait is already implemented
/// on a few `Iterator`s, like `std::ops::Range`.
pub trait AsParallel {
    /// What is the type of the output `ParallelIterator`?
    type Iter: ParallelIterator<Item = Self::Item>;

    /// What is the `Item` of the output `ParallelIterator`?
    type Item: Send;

    /// Convert this type to a `ParallelIterator`.
    fn as_parallel(self) -> Self::Iter;
}

impl<T: Iterator + Send> AsParallel for T
    where T::Item: Send
{
    type Iter = IterParallel<T>;
    type Item = T::Item;

    fn as_parallel(self) -> Self::Iter {
        IterParallel {
            iter: self,
        }
    }
}

/// `IterParallel` is a parallel iterator that wraps a sequential iterator.
#[derive(Debug)]
pub struct IterParallel<Iter> {
    iter: Iter,
}

impl<Iter: Iterator + Send> ParallelIterator for IterParallel<Iter>
    where Iter::Item: Send
{
    type Item = Iter::Item;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: UnindexedConsumer<Self::Item>
    {
        let split_count = AtomicUsize::new(current_num_threads());
        let deque = Deque::new();
        let stealer = deque.stealer();
        let done = AtomicBool::new(false);
        let iter = Mutex::new((self.iter, deque));

        bridge_unindexed(IterParallelProducer {
            split_count: &split_count,
            done: &done,
            iter: &iter,
            items: stealer,
        }, consumer)
    }
}

struct IterParallelProducer<'a, Iter: Iterator + 'a> {
    split_count: &'a AtomicUsize,
    done: &'a AtomicBool,
    iter: &'a Mutex<(Iter, Deque<Iter::Item>)>,
    items: Stealer<Iter::Item>,
}

// manual clone because T doesn't need to be Clone, but the derive assumes it should be
impl<'a, Iter: Iterator + 'a> Clone for IterParallelProducer<'a, Iter> {
    fn clone(&self) -> Self {
        IterParallelProducer {
            split_count: self.split_count,
            done: self.done,
            iter: self.iter,
            items: self.items.clone(),
        }
    }
}

impl<'a, Iter: Iterator + Send + 'a> UnindexedProducer for IterParallelProducer<'a, Iter>
    where Iter::Item: Send
{
    type Item = Iter::Item;

    fn split(self) -> (Self, Option<Self>) {
        let mut count = self.split_count.load(Ordering::SeqCst);

        loop {
            let done = self.done.load(Ordering::SeqCst);
            match count.checked_sub(1) {
                Some(new_count) if !done => {
                    let last_count = self.split_count.compare_and_swap(count, new_count, Ordering::SeqCst);
                    if last_count == count {
                        return (self.clone(), Some(self));
                    } else {
                        count = last_count;
                    }
                }
                _ => {
                    return (self, None);
                }
            }
        }
    }

    fn fold_with<F>(self, mut folder: F) -> F
        where F: Folder<Self::Item>
    {
        loop {
            match self.items.steal() {
                Steal::Data(it) => {
                    folder = folder.consume(it);
                    if folder.full() {
                        return folder;
                    }
                }
                Steal::Empty => {
                    if self.done.load(Ordering::SeqCst) {
                        // the iterator is out of items, no use in continuing
                        return folder;
                    } else {
                        // our cache is out of items, time to load more from the iterator
                        match self.iter.try_lock() {
                            Ok(mut guard) => {
                                let count = current_num_threads();
                                let count = (count * count) * 2;

                                let (ref mut iter, ref deque) = *guard;

                                for _ in 0..count {
                                    if let Some(it) = iter.next() {
                                        deque.push(it);
                                    } else {
                                        self.done.store(true, Ordering::SeqCst);
                                        break;
                                    }
                                }
                            }
                            Err(TryLockError::WouldBlock) => {
                                // someone else has the mutex, just sit tight until it's ready
                                yield_now(); //TODO: use a thread=pool-aware yield? (#548)
                            }
                            Err(TryLockError::Poisoned(_)) => {
                                // TODO: how to handle poison?
                                return folder;
                            }
                        }
                    }
                }
                Steal::Retry => (),
            }
        }
    }
}
<commit_msg>change note around poisoned mutex<commit_after>use crossbeam_deque::{Deque, Stealer, Steal};

use std::thread::yield_now;
use std::sync::{Mutex, TryLockError};
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};

use iter::ParallelIterator;
use iter::plumbing::{UnindexedConsumer, UnindexedProducer, bridge_unindexed, Folder};
use current_num_threads;

/// Conversion trait to convert an `Iterator` to a `ParallelIterator`.
///
/// This needs to be distinct from `IntoParallelIterator` because that trait is already implemented
/// on a few `Iterator`s, like `std::ops::Range`.
pub trait AsParallel {
    /// What is the type of the output `ParallelIterator`?
    type Iter: ParallelIterator<Item = Self::Item>;

    /// What is the `Item` of the output `ParallelIterator`?
    type Item: Send;

    /// Convert this type to a `ParallelIterator`.
    fn as_parallel(self) -> Self::Iter;
}

impl<T: Iterator + Send> AsParallel for T
    where T::Item: Send
{
    type Iter = IterParallel<T>;
    type Item = T::Item;

    fn as_parallel(self) -> Self::Iter {
        IterParallel {
            iter: self,
        }
    }
}

/// `IterParallel` is a parallel iterator that wraps a sequential iterator.
#[derive(Debug)]
pub struct IterParallel<Iter> {
    iter: Iter,
}

impl<Iter: Iterator + Send> ParallelIterator for IterParallel<Iter>
    where Iter::Item: Send
{
    type Item = Iter::Item;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: UnindexedConsumer<Self::Item>
    {
        let split_count = AtomicUsize::new(current_num_threads());
        let deque = Deque::new();
        let stealer = deque.stealer();
        let done = AtomicBool::new(false);
        let iter = Mutex::new((self.iter, deque));

        bridge_unindexed(IterParallelProducer {
            split_count: &split_count,
            done: &done,
            iter: &iter,
            items: stealer,
        }, consumer)
    }
}

struct IterParallelProducer<'a, Iter: Iterator + 'a> {
    split_count: &'a AtomicUsize,
    done: &'a AtomicBool,
    iter: &'a Mutex<(Iter, Deque<Iter::Item>)>,
    items: Stealer<Iter::Item>,
}

// manual clone because T doesn't need to be Clone, but the derive assumes it should be
impl<'a, Iter: Iterator + 'a> Clone for IterParallelProducer<'a, Iter> {
    fn clone(&self) -> Self {
        IterParallelProducer {
            split_count: self.split_count,
            done: self.done,
            iter: self.iter,
            items: self.items.clone(),
        }
    }
}

impl<'a, Iter: Iterator + Send + 'a> UnindexedProducer for IterParallelProducer<'a, Iter>
    where Iter::Item: Send
{
    type Item = Iter::Item;

    fn split(self) -> (Self, Option<Self>) {
        let mut count = self.split_count.load(Ordering::SeqCst);

        loop {
            let done = self.done.load(Ordering::SeqCst);
            match count.checked_sub(1) {
                Some(new_count) if !done => {
                    let last_count = self.split_count.compare_and_swap(count, new_count, Ordering::SeqCst);
                    if last_count == count {
                        return (self.clone(), Some(self));
                    } else {
                        count = last_count;
                    }
                }
                _ => {
                    return (self, None);
                }
            }
        }
    }

    fn fold_with<F>(self, mut folder: F) -> F
        where F: Folder<Self::Item>
    {
        loop {
            match self.items.steal() {
                Steal::Data(it) => {
                    folder = folder.consume(it);
                    if folder.full() {
                        return folder;
                    }
                }
                Steal::Empty => {
                    if self.done.load(Ordering::SeqCst) {
                        // the iterator is out of items, no use in continuing
                        return folder;
                    } else {
                        // our cache is out of items, time to load more from the iterator
                        match self.iter.try_lock() {
                            Ok(mut guard) => {
                                let count = current_num_threads();
                                let count = (count * count) * 2;

                                let (ref mut iter, ref deque) = *guard;

                                for _ in 0..count {
                                    if let Some(it) = iter.next() {
                                        deque.push(it);
                                    } else {
                                        self.done.store(true, Ordering::SeqCst);
                                        break;
                                    }
                                }
                            }
                            Err(TryLockError::WouldBlock) => {
                                // someone else has the mutex, just sit tight until it's ready
                                yield_now(); //TODO: use a thread=pool-aware yield? (#548)
                            }
                            Err(TryLockError::Poisoned(_)) => {
                                // any panics from other threads will have been caught by the pool,
                                // and will be re-thrown when joined - just exit
                                return folder;
                            }
                        }
                    }
                }
                Steal::Retry => (),
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Error as FmtError;
use std::clone::Clone;
use std::fmt::{Display, Formatter};
use std::convert::Into;

/**
 * Kind of error
 */
#[derive(Clone, Copy, Debug)]
pub enum HookErrorKind {
    HookExecutionError,
    AccessTypeViolation,
    Pre(PreHookErrorKind),
    Post(PostHookErrorKind)
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PreHookErrorKind {
    // ...
}

impl Into<HookErrorKind> for PreHookErrorKind {
    fn into(self) -> HookErrorKind {
        HookErrorKind::Pre(self)
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PostHookErrorKind {
    // ...
}

impl Into<HookErrorKind> for PostHookErrorKind {
    fn into(self) -> HookErrorKind {
        HookErrorKind::Post(self)
    }
}

pub trait IntoHookError {
    fn into_hookerror(self) -> HookError;
    fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
}

impl Into<HookError> for HookErrorKind {

    fn into(self) -> HookError {
        HookError::new(self, None)
    }

}

impl Into<HookError> for (HookErrorKind, Box<Error>) {

    fn into(self) -> HookError {
        HookError::new(self.0, Some(self.1))
    }

}

impl Into<HookError> for PreHookErrorKind {

    fn into(self) -> HookError {
        HookError::new(HookErrorKind::Pre(self), None)
    }

}

impl Into<HookError> for (PreHookErrorKind, Box<Error>) {

    fn into(self) -> HookError {
        HookError::new(HookErrorKind::Pre(self.0), Some(self.1))
    }

}

impl Into<HookError> for PostHookErrorKind {

    fn into(self) -> HookError {
        HookError::new(HookErrorKind::Post(self), None)
    }

}

impl Into<HookError> for (PostHookErrorKind, Box<Error>) {

    fn into(self) -> HookError {
        HookError::new(HookErrorKind::Post(self.0), Some(self.1))
    }

}

fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
    match e {
        _ => "",
    }
}

impl Display for HookErrorKind {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "{}", hook_error_type_as_str(self)));
        Ok(())
    }

}

/**
 * Error type
 */
#[derive(Debug)]
pub struct HookError {
    err_type: HookErrorKind,
    cause: Option<Box<Error>>,
}

impl HookError {

    /**
     * Build a new HookError from an HookErrorKind, optionally with cause
     */
    pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
        -> HookError
        {
            HookError {
                err_type: errtype,
                cause: cause,
            }
        }

    /**
     * Get the error type of this HookError
     */
    pub fn err_type(&self) -> HookErrorKind {
        self.err_type.clone()
    }

}

impl Display for HookError {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
        Ok(())
    }

}

impl Error for HookError {

    fn description(&self) -> &str {
        hook_error_type_as_str(&self.err_type.clone())
    }

    fn cause(&self) -> Option<&Error> {
        self.cause.as_ref().map(|e| &**e)
    }

}

<commit_msg>Remove {Pre, Post}HookErrorKind types as they are not needed anymore<commit_after>use std::error::Error;
use std::fmt::Error as FmtError;
use std::clone::Clone;
use std::fmt::{Display, Formatter};
use std::convert::Into;

/**
 * Kind of error
 */
#[derive(Clone, Copy, Debug)]
pub enum HookErrorKind {
    HookExecutionError,
    AccessTypeViolation,
}

pub trait IntoHookError {
    fn into_hookerror(self) -> HookError;
    fn into_hookerror_with_cause(self, cause: Box<Error>) -> HookError;
}

impl Into<HookError> for HookErrorKind {

    fn into(self) -> HookError {
        HookError::new(self, None)
    }

}

impl Into<HookError> for (HookErrorKind, Box<Error>) {

    fn into(self) -> HookError {
        HookError::new(self.0, Some(self.1))
    }

}

fn hook_error_type_as_str(e: &HookErrorKind) -> &'static str {
    match e {
        _ => "",
    }
}

impl Display for HookErrorKind {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "{}", hook_error_type_as_str(self)));
        Ok(())
    }

}

/**
 * Error type
 */
#[derive(Debug)]
pub struct HookError {
    err_type: HookErrorKind,
    cause: Option<Box<Error>>,
}

impl HookError {

    /**
     * Build a new HookError from an HookErrorKind, optionally with cause
     */
    pub fn new(errtype: HookErrorKind, cause: Option<Box<Error>>)
        -> HookError
        {
            HookError {
                err_type: errtype,
                cause: cause,
            }
        }

    /**
     * Get the error type of this HookError
     */
    pub fn err_type(&self) -> HookErrorKind {
        self.err_type.clone()
    }

}

impl Display for HookError {

    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
        try!(write!(fmt, "[{}]", hook_error_type_as_str(&self.err_type.clone())));
        Ok(())
    }

}

impl Error for HookError {

    fn description(&self) -> &str {
        hook_error_type_as_str(&self.err_type.clone())
    }

    fn cause(&self) -> Option<&Error> {
        self.cause.as_ref().map(|e| &**e)
    }

}

<|endoftext|>