<commit_before>use std::cmp;

use ctxt::SemContext;

use gc::root::IndirectObj;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{CrossingEntry, CrossingMap};
use gc::swiper::in_kilo;
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::{CardIdx, Region, CARD_SIZE};
use gc::Address;

use mem;
use object::Obj;
use timer::{in_ms, Timer};

pub struct MinorCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    young: &'a YoungGen,
    old: &'a OldGen,
    large: &'a LargeSpace,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,

    free: Address,
    promotion_failed: bool,
    promoted_size: usize,
    young_from: Region,
}

impl<'a, 'ast> MinorCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        young: &'a YoungGen,
        old: &'a OldGen,
        large: &'a LargeSpace,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [IndirectObj],
    ) -> MinorCollector<'a, 'ast> {
        MinorCollector {
            ctxt: ctxt,
            young: young,
            old: old,
            large: large,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            free: Address::null(),
            promotion_failed: false,
            promoted_size: 0,
            young_from: young.from_space(),
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();
        let young_init_size = self.young.used_region().size();
        self.free = self.young.to_space().start;

        self.young.unprotect_to_space();

        self.visit_roots();
        self.copy_dirty_cards();
        self.visit_large_objects();
        self.visit_copied_objects();
        self.young.swap_spaces(self.free);

        self.young.protect_to_space();

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let young_new_size = self.young.used_region().size();
            let garbage = young_init_size - young_new_size - self.promoted_size;
            let garbage_ratio = (garbage as f64 / young_init_size as f64) * 100f64;

            println!(
                "GC: Minor GC ({:.2} ms, {:.1}K->{:.1}K, young {:.1}K->{:.1}K, \
                 {:.1}K promoted, {:.1}K/{:.0}% garbage)",
                in_ms(dur),
                in_kilo(init_size),
                in_kilo(new_size),
                in_kilo(young_init_size),
                in_kilo(young_new_size),
                in_kilo(self.promoted_size),
                in_kilo(garbage),
                garbage_ratio
            );
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn visit_roots(&mut self) {
        // detect all references from roots into young generation
        for &root in self.rootset {
            let root_ptr = root.get();

            if self.young.contains(Address::from_ptr(root_ptr)) {
                root.set(self.copy(root_ptr));
            }
        }
    }

    fn visit_large_objects(&mut self) {
        self.large.visit_objects(|addr| {
            let object = unsafe { &mut *addr.to_mut_ptr::<Obj>() };

            if object.is_array_ref() {
                self.visit_large_object_array(object, addr);
            } else {
                self.visit_large_object(object, addr);
            }
        })
    }

    fn visit_large_object_array(&mut self, object: &mut Obj, object_start: Address) {
        let object_end = object_start.offset(object.size() as usize);
        let (start_card_idx, end_card_idx) = self.card_table.card_indices(object_start, object_end);

        for card_idx in start_card_idx..end_card_idx {
            let card_idx = card_idx.into();

            if self.card_table.get(card_idx).is_clean() {
                continue;
            }

            let card_start = self.card_table.to_address(card_idx);
            let card_end = card_start.offset(CARD_SIZE);
            let end = cmp::min(card_end, object_end);

            let mut ref_to_young_gen = false;

            if card_idx.to_usize() == start_card_idx {
                self.copy_range(object_start, end, &mut ref_to_young_gen);
            } else {
                // all but the first card are full with references
                let refs = end.offset_from(card_start) / mem::ptr_width_usize();
                self.copy_refs(card_start, refs, &mut ref_to_young_gen);
            }

            self.update_card(card_idx, ref_to_young_gen);
        }
    }

    fn visit_large_object(&mut self, object: &mut Obj, object_start: Address) {
        if !self.card_table.is_dirty(object_start) {
            return;
        }

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                field.set(self.copy(field_ptr));
            }
        });
    }

    fn visit_copied_objects(&mut self) {
        let mut scan = self.young.to_space().start;

        // visit all fields in copied objects
        while scan < self.free {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if self.young.contains(Address::from_ptr(field_ptr)) {
                    field.set(self.copy(field_ptr));
                }
            });

            scan = scan.offset(object.size());
        }
    }

    // copy all references from old- into young-generation.
    fn copy_dirty_cards(&mut self) {
        self.card_table.visit_dirty_in_old(self.old.free(), |card| {
            let crossing_entry = self.crossing_map.get(card);
            let card_start = self.card_table.to_address(card);
            let card_end = card_start.offset(CARD_SIZE);

            match crossing_entry {
                CrossingEntry::NoRefs => panic!("card dirty without any refs"),
                CrossingEntry::LeadingRefs(refs) => {
                    let mut ref_to_young_gen = false;

                    // copy references at start of card
                    let first_object =
                        self.copy_refs(card_start, refs as usize, &mut ref_to_young_gen);

                    // copy all objects from this card
                    self.copy_old_card(card, first_object, card_end, ref_to_young_gen);
                }

                CrossingEntry::FirstObject(offset) => {
                    let ptr = card_start.offset(offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_old_card(card, ptr, card_end, false);
                }

                CrossingEntry::ArrayStart(offset) => {
                    let ptr = card_start.to_usize() - (offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_old_card(card, ptr.into(), card_end, false);
                }
            }
        });
    }

    fn copy_refs(&mut self, mut ptr: Address, refs: usize, ref_to_young_gen: &mut bool) -> Address {
        for _ in 0..refs {
            let ind_ptr = IndirectObj::from_address(ptr);
            let dir_ptr = ind_ptr.get();

            if self.young_from.contains(Address::from_ptr(dir_ptr)) {
                let copied_obj = self.copy(dir_ptr);
                ind_ptr.set(copied_obj);

                if self.young.contains(Address::from_ptr(copied_obj)) {
                    *ref_to_young_gen = true;
                }
            }

            ptr = ptr.offset(mem::ptr_width_usize());
        }

        ptr
    }

    fn copy_old_card(
        &mut self,
        card: CardIdx,
        mut ptr: Address,
        card_end: Address,
        mut ref_to_young_gen: bool,
    ) {
        let old_end: Address = self.old.free();
        let mut end = cmp::min(card_end, old_end);

        loop {
            ptr = self.copy_range(ptr, end, &mut ref_to_young_gen);

            // if we are in the last card of the old generation, promoted objects
            // will increase `end` towards `card_end`. Those newly promoted objects
            // need also be handled.
            if end == card_end {
                break;
            }

            let old_end = self.old.free();
            let next_end = cmp::min(card_end, old_end);

            if end != next_end {
                end = next_end;
            } else {
                break;
            }
        }

        self.update_card(card, ref_to_young_gen);
    }

    fn copy_range(
        &mut self,
        mut ptr: Address,
        end: Address,
        ref_to_young_gen: &mut bool,
    ) -> Address {
        while ptr < end {
            let object = unsafe { &mut *ptr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields_within(end, |field| {
                let field_ptr = field.get();

                if self.young_from.contains(Address::from_ptr(field_ptr)) {
                    let copied_obj = self.copy(field_ptr);
                    field.set(copied_obj);

                    // determine if copied object is still in young generation
                    if self.young.contains(Address::from_ptr(copied_obj)) {
                        *ref_to_young_gen = true;
                    }
                }
            });

            ptr = ptr.offset(object.size());
        }

        end
    }

    fn update_card(&mut self, card_idx: CardIdx, ref_to_young_gen: bool) {
        // if there are no references to the young generation in this card,
        // set the card to clean.
        if !ref_to_young_gen {
            self.card_table.set(card_idx, CardEntry::Clean);
        }
    }

    fn copy(&mut self, obj: *mut Obj) -> *mut Obj {
        let obj_addr = Address::from_ptr(obj);
        let obj = unsafe { &mut *obj };

        if let Some(fwd) = obj.header().forwarded() {
            return fwd.to_mut_ptr();
        }

        let obj_size = obj.size();
        debug_assert!(
            self.young_from.contains(obj_addr),
            "copy objects only from from-space."
        );

        // if object is old enough we copy it into the old generation
        if self.young.should_be_promoted(obj_addr) {
            let copy_addr = self.old.alloc(obj_size, obj.is_array_ref());

            // if there isn't enough space in old gen keep it in the
            // young generation for now
            if copy_addr.is_null() {
                self.promotion_failed = true;
            } else {
                self.promote_object(obj, copy_addr, obj_size);
                return copy_addr.to_mut_ptr();
            }
        }

        let copy_addr = self.free;
        self.free = copy_addr.offset(obj_size);

        obj.copy_to(copy_addr, obj_size);
        obj.header_mut().forward_to(copy_addr);

        copy_addr.to_mut_ptr()
    }

    fn promote_object(&mut self, obj: &mut Obj, copy_addr: Address, obj_size: usize) {
        obj.copy_to(copy_addr, obj_size);
        self.promoted_size += obj_size;

        // Promoted object can have references to the young generation.
        // Set the card table entry to dirty if this is the case.
        self.handle_promoted_object(copy_addr);

        obj.header_mut().forward_to(copy_addr);
    }

    fn handle_promoted_object(&mut self, addr: Address) {
        let card_idx = self.card_table.card_idx(addr);

        // card is already dirty, nothing left to do.
        if self.card_table.get(card_idx).is_dirty() {
            return;
        }

        let object: &mut Obj = unsafe { &mut *addr.to_mut_ptr() };
        let mut old_to_young_ref = false;

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                old_to_young_ref = true;
            }
        });

        if old_to_young_ref {
            self.card_table.set(card_idx, CardEntry::Dirty);
        }
    }

    pub fn promotion_failed(&self) -> bool {
        self.promotion_failed
    }
}
<commit_msg>swiper: remove card_end from copy_old_card<commit_after>use std::cmp;

use ctxt::SemContext;

use gc::root::IndirectObj;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{CrossingEntry, CrossingMap};
use gc::swiper::in_kilo;
use gc::swiper::large::LargeSpace;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;
use gc::swiper::{CardIdx, Region, CARD_SIZE};
use gc::Address;

use mem;
use object::Obj;
use timer::{in_ms, Timer};

pub struct MinorCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    young: &'a YoungGen,
    old: &'a OldGen,
    large: &'a LargeSpace,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,

    free: Address,
    promotion_failed: bool,
    promoted_size: usize,
    young_from: Region,
}

impl<'a, 'ast> MinorCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        young: &'a YoungGen,
        old: &'a OldGen,
        large: &'a LargeSpace,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [IndirectObj],
    ) -> MinorCollector<'a, 'ast> {
        MinorCollector {
            ctxt: ctxt,
            young: young,
            old: old,
            large: large,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            free: Address::null(),
            promotion_failed: false,
            promoted_size: 0,
            young_from: young.from_space(),
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();
        let young_init_size = self.young.used_region().size();
        self.free = self.young.to_space().start;

        self.young.unprotect_to_space();

        self.visit_roots();
        self.copy_dirty_cards();
        self.visit_large_objects();
        self.visit_copied_objects();
        self.young.swap_spaces(self.free);

        self.young.protect_to_space();

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let young_new_size = self.young.used_region().size();
            let garbage = young_init_size - young_new_size - self.promoted_size;
            let garbage_ratio = (garbage as f64 / young_init_size as f64) * 100f64;

            println!(
                "GC: Minor GC ({:.2} ms, {:.1}K->{:.1}K, young {:.1}K->{:.1}K, \
                 {:.1}K promoted, {:.1}K/{:.0}% garbage)",
                in_ms(dur),
                in_kilo(init_size),
                in_kilo(new_size),
                in_kilo(young_init_size),
                in_kilo(young_new_size),
                in_kilo(self.promoted_size),
                in_kilo(garbage),
                garbage_ratio
            );
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn visit_roots(&mut self) {
        // detect all references from roots into young generation
        for &root in self.rootset {
            let root_ptr = root.get();

            if self.young.contains(Address::from_ptr(root_ptr)) {
                root.set(self.copy(root_ptr));
            }
        }
    }

    fn visit_large_objects(&mut self) {
        self.large.visit_objects(|addr| {
            let object = unsafe { &mut *addr.to_mut_ptr::<Obj>() };

            if object.is_array_ref() {
                self.visit_large_object_array(object, addr);
            } else {
                self.visit_large_object(object, addr);
            }
        })
    }

    fn visit_large_object_array(&mut self, object: &mut Obj, object_start: Address) {
        let object_end = object_start.offset(object.size() as usize);
        let (start_card_idx, end_card_idx) = self.card_table.card_indices(object_start, object_end);

        for card_idx in start_card_idx..end_card_idx {
            let card_idx = card_idx.into();

            if self.card_table.get(card_idx).is_clean() {
                continue;
            }

            let card_start = self.card_table.to_address(card_idx);
            let card_end = card_start.offset(CARD_SIZE);
            let end = cmp::min(card_end, object_end);

            let mut ref_to_young_gen = false;

            if card_idx.to_usize() == start_card_idx {
                self.copy_range(object_start, end, &mut ref_to_young_gen);
            } else {
                // all but the first card are full with references
                let refs = end.offset_from(card_start) / mem::ptr_width_usize();
                self.copy_refs(card_start, refs, &mut ref_to_young_gen);
            }

            self.update_card(card_idx, ref_to_young_gen);
        }
    }

    fn visit_large_object(&mut self, object: &mut Obj, object_start: Address) {
        if !self.card_table.is_dirty(object_start) {
            return;
        }

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                field.set(self.copy(field_ptr));
            }
        });
    }

    fn visit_copied_objects(&mut self) {
        let mut scan = self.young.to_space().start;

        // visit all fields in copied objects
        while scan < self.free {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if self.young.contains(Address::from_ptr(field_ptr)) {
                    field.set(self.copy(field_ptr));
                }
            });

            scan = scan.offset(object.size());
        }
    }

    // copy all references from old- into young-generation.
    fn copy_dirty_cards(&mut self) {
        self.card_table.visit_dirty_in_old(self.old.free(), |card_idx| {
            let crossing_entry = self.crossing_map.get(card_idx);
            let card_start = self.card_table.to_address(card_idx);

            match crossing_entry {
                CrossingEntry::NoRefs => panic!("card dirty without any refs"),
                CrossingEntry::LeadingRefs(refs) => {
                    let mut ref_to_young_gen = false;

                    // copy references at start of card
                    let first_object =
                        self.copy_refs(card_start, refs as usize, &mut ref_to_young_gen);

                    // copy all objects from this card
                    self.copy_old_card(card_idx, first_object, ref_to_young_gen);
                }

                CrossingEntry::FirstObject(offset) => {
                    let ptr = card_start.offset(offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_old_card(card_idx, ptr, false);
                }

                CrossingEntry::ArrayStart(offset) => {
                    assert!(offset == 1);
                    let ptr = card_start.to_usize() - (offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_old_card(card_idx, ptr.into(), false);
                }
            }
        });
    }

    fn copy_refs(&mut self, mut ptr: Address, refs: usize, ref_to_young_gen: &mut bool) -> Address {
        for _ in 0..refs {
            let ind_ptr = IndirectObj::from_address(ptr);
            let dir_ptr = ind_ptr.get();

            if self.young_from.contains(Address::from_ptr(dir_ptr)) {
                let copied_obj = self.copy(dir_ptr);
                ind_ptr.set(copied_obj);

                if self.young.contains(Address::from_ptr(copied_obj)) {
                    *ref_to_young_gen = true;
                }
            }

            ptr = ptr.offset(mem::ptr_width_usize());
        }

        ptr
    }

    fn copy_old_card(
        &mut self,
        card: CardIdx,
        mut ptr: Address,
        mut ref_to_young_gen: bool,
    ) {
        let card_start = self.card_table.to_address(card);
        let card_end = card_start.offset(CARD_SIZE);

        let old_end: Address = self.old.free();
        let mut end = cmp::min(card_end, old_end);

        loop {
            ptr = self.copy_range(ptr, end, &mut ref_to_young_gen);

            // if we are in the last card of the old generation, promoted objects
            // will increase `end` towards `card_end`. Those newly promoted objects
            // need also be handled.
            if end == card_end {
                break;
            }

            let old_end = self.old.free();
            let next_end = cmp::min(card_end, old_end);

            if end != next_end {
                end = next_end;
            } else {
                break;
            }
        }

        self.update_card(card, ref_to_young_gen);
    }

    fn copy_range(
        &mut self,
        mut ptr: Address,
        end: Address,
        ref_to_young_gen: &mut bool,
    ) -> Address {
        while ptr < end {
            let object = unsafe { &mut *ptr.to_mut_ptr::<Obj>() };

            object.visit_reference_fields_within(end, |field| {
                let field_ptr = field.get();

                if self.young_from.contains(Address::from_ptr(field_ptr)) {
                    let copied_obj = self.copy(field_ptr);
                    field.set(copied_obj);

                    // determine if copied object is still in young generation
                    if self.young.contains(Address::from_ptr(copied_obj)) {
                        *ref_to_young_gen = true;
                    }
                }
            });

            ptr = ptr.offset(object.size());
        }

        end
    }

    fn update_card(&mut self, card_idx: CardIdx, ref_to_young_gen: bool) {
        // if there are no references to the young generation in this card,
        // set the card to clean.
        if !ref_to_young_gen {
            self.card_table.set(card_idx, CardEntry::Clean);
        }
    }

    fn copy(&mut self, obj: *mut Obj) -> *mut Obj {
        let obj_addr = Address::from_ptr(obj);
        let obj = unsafe { &mut *obj };

        if let Some(fwd) = obj.header().forwarded() {
            return fwd.to_mut_ptr();
        }

        let obj_size = obj.size();
        debug_assert!(
            self.young_from.contains(obj_addr),
            "copy objects only from from-space."
        );

        // if object is old enough we copy it into the old generation
        if self.young.should_be_promoted(obj_addr) {
            let copy_addr = self.old.alloc(obj_size, obj.is_array_ref());

            // if there isn't enough space in old gen keep it in the
            // young generation for now
            if copy_addr.is_null() {
                self.promotion_failed = true;
            } else {
                self.promote_object(obj, copy_addr, obj_size);
                return copy_addr.to_mut_ptr();
            }
        }

        let copy_addr = self.free;
        self.free = copy_addr.offset(obj_size);

        obj.copy_to(copy_addr, obj_size);
        obj.header_mut().forward_to(copy_addr);

        copy_addr.to_mut_ptr()
    }

    fn promote_object(&mut self, obj: &mut Obj, copy_addr: Address, obj_size: usize) {
        obj.copy_to(copy_addr, obj_size);
        self.promoted_size += obj_size;

        // Promoted object can have references to the young generation.
        // Set the card table entry to dirty if this is the case.
        self.handle_promoted_object(copy_addr);

        obj.header_mut().forward_to(copy_addr);
    }

    fn handle_promoted_object(&mut self, addr: Address) {
        let card_idx = self.card_table.card_idx(addr);

        // card is already dirty, nothing left to do.
        if self.card_table.get(card_idx).is_dirty() {
            return;
        }

        let object: &mut Obj = unsafe { &mut *addr.to_mut_ptr() };
        let mut old_to_young_ref = false;

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                old_to_young_ref = true;
            }
        });

        if old_to_young_ref {
            self.card_table.set(card_idx, CardEntry::Dirty);
        }
    }

    pub fn promotion_failed(&self) -> bool {
        self.promotion_failed
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! CSS table formatting contexts.

#![deny(unsafe_blocks)]

use block::BlockFlow;
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{TableCaptionFlowClass, FlowClass, Flow};
use fragment::FragmentBoundsIterator;
use wrapper::ThreadSafeLayoutNode;

use servo_util::geometry::Au;
use std::fmt;
use style::ComputedValues;
use sync::Arc;

/// A table formatting context.
pub struct TableCaptionFlow {
    pub block_flow: BlockFlow,
}

impl TableCaptionFlow {
    pub fn from_node(constructor: &mut FlowConstructor,
                     node: &ThreadSafeLayoutNode)
                     -> TableCaptionFlow {
        TableCaptionFlow {
            block_flow: BlockFlow::from_node(constructor, node)
        }
    }
}

impl Flow for TableCaptionFlow {
    fn class(&self) -> FlowClass {
        TableCaptionFlowClass
    }

    fn as_table_caption<'a>(&'a mut self) -> &'a mut TableCaptionFlow {
        self
    }

    fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
        &mut self.block_flow
    }

    fn bubble_inline_sizes(&mut self) {
        self.block_flow.bubble_inline_sizes();
    }

    fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
        debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_caption");
        self.block_flow.assign_inline_sizes(ctx);
    }

    fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
        debug!("assign_block_size: assigning block_size for table_caption");
        self.block_flow.assign_block_size(ctx);
    }

    fn compute_absolute_position(&mut self) {
        self.block_flow.compute_absolute_position()
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
    }

    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
    }

    fn build_display_list(&mut self, layout_context: &LayoutContext) {
        debug!("build_display_list_table_caption: same process as block flow");
        self.block_flow.build_display_list(layout_context)
    }

    fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
        self.block_flow.repair_style(new_style)
    }

    fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
        self.iterate_through_fragment_bounds(iterator);
    }
}

impl fmt::Show for TableCaptionFlow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TableCaptionFlow: {}", self.block_flow)
    }
}
<commit_msg>Fix Table Caption infinite recursion.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! CSS table formatting contexts.

#![deny(unsafe_blocks)]

use block::BlockFlow;
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{TableCaptionFlowClass, FlowClass, Flow};
use fragment::FragmentBoundsIterator;
use wrapper::ThreadSafeLayoutNode;

use servo_util::geometry::Au;
use std::fmt;
use style::ComputedValues;
use sync::Arc;

/// A table formatting context.
pub struct TableCaptionFlow {
    pub block_flow: BlockFlow,
}

impl TableCaptionFlow {
    pub fn from_node(constructor: &mut FlowConstructor,
                     node: &ThreadSafeLayoutNode)
                     -> TableCaptionFlow {
        TableCaptionFlow {
            block_flow: BlockFlow::from_node(constructor, node)
        }
    }
}

impl Flow for TableCaptionFlow {
    fn class(&self) -> FlowClass {
        TableCaptionFlowClass
    }

    fn as_table_caption<'a>(&'a mut self) -> &'a mut TableCaptionFlow {
        self
    }

    fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
        &mut self.block_flow
    }

    fn bubble_inline_sizes(&mut self) {
        self.block_flow.bubble_inline_sizes();
    }

    fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
        debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_caption");
        self.block_flow.assign_inline_sizes(ctx);
    }

    fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
        debug!("assign_block_size: assigning block_size for table_caption");
        self.block_flow.assign_block_size(ctx);
    }

    fn compute_absolute_position(&mut self) {
        self.block_flow.compute_absolute_position()
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
    }

    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
    }

    fn build_display_list(&mut self, layout_context: &LayoutContext) {
        debug!("build_display_list_table_caption: same process as block flow");
        self.block_flow.build_display_list(layout_context)
    }

    fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
        self.block_flow.repair_style(new_style)
    }

    fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
        self.block_flow.iterate_through_fragment_bounds(iterator);
    }
}

impl fmt::Show for TableCaptionFlow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TableCaptionFlow: {}", self.block_flow)
    }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2015 Paul Osborne <osbpau@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! PWM Support for the NXP LPC17xx MCUs

use hal::lpc17xx::peripheral_clock::PeripheralClock::PWM1Clock;
use core::intrinsics::abort;

use self::PWMChannel::*;

#[path="../../util/ioreg.rs"]
#[macro_use] mod ioreg;

/// "Channels" correspond to MR1..6 (0 used for period)
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum PWMChannel {
    CHANNEL0 = 0,  // reserved for period
    CHANNEL1,
    CHANNEL2,
    CHANNEL3,
    CHANNEL4,
    CHANNEL5,
    CHANNEL6,
}

impl PWMChannel {

    /// Set the match register for this channel to the provided value
    fn set_mr(&self, value: u32) {
        // note that for whatever reason, the match registers are fragmented
        // across two registers.  They aren't even located next to each other
        // in memory
        match *self {
            CHANNEL0 => { reg::PWM1.mr[0].set_value(value); },
            CHANNEL1 => { reg::PWM1.mr[1].set_value(value); },
            CHANNEL2 => { reg::PWM1.mr[2].set_value(value); },
            CHANNEL3 => { reg::PWM1.mr[3].set_value(value); },
            CHANNEL4 => { reg::PWM1.mr2[0].set_value(value); },
            CHANNEL5 => { reg::PWM1.mr2[1].set_value(value); },
            CHANNEL6 => { reg::PWM1.mr2[2].set_value(value); },
        };
    }
}

#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub struct PWM {
    channel: PWMChannel,
    period_us: u32,
    pulsewidth_us: u32,
}

impl PWM {

    /// Create a new PWM Output on the provided channel
    pub fn new(channel: PWMChannel) -> PWM {
        PWM1Clock.enable();
        PWM1Clock.set_divisor(4);

        // reset TC on match 0 for this channel and
        // enable output
        match channel {
            CHANNEL0 => { unsafe { abort() } },  // CHANNEL0 reserved for internal use
            CHANNEL1 => { reg::PWM1.mcr.set_pwmmr1r(true); reg::PWM1.pcr.set_pwmena1(true); },
            CHANNEL2 => { reg::PWM1.mcr.set_pwmmr2r(true); reg::PWM1.pcr.set_pwmena2(true); },
            CHANNEL3 => { reg::PWM1.mcr.set_pwmmr3r(true); reg::PWM1.pcr.set_pwmena3(true); },
            CHANNEL4 => { reg::PWM1.mcr.set_pwmmr4r(true); reg::PWM1.pcr.set_pwmena4(true); },
            CHANNEL5 => { reg::PWM1.mcr.set_pwmmr5r(true); reg::PWM1.pcr.set_pwmena5(true); },
            CHANNEL6 => { reg::PWM1.mcr.set_pwmmr6r(true); reg::PWM1.pcr.set_pwmena6(true); },
        };

        let pwm = PWM {
            channel: channel,
            period_us: 20_000,  // 20ms
            pulsewidth_us: 0,   // off by default
        };
        pwm.update_output();

        // TODO: Pin Muxing?
        //   - On the LPC1768, a few pads could be used for, e.g. PWM1.3
        //   - Can work as-is if function setup correct on the Pin, but this requires
        //     reading the user manual in detail
        //   - Alt for PWM on various pins is not always the same (1 or 2)
        pwm
    }

    /// Update the PWM Signal based on the current state
    fn update_output(&self) {
        // calculate the number of ticks for our period and pulsewidth
        let freq_hz: u32 = PWM1Clock.frequency();
        let freq_mhz: u32 = freq_hz / 1_000_000;
        let period_ticks: u32 = freq_mhz * self.period_us;
        let pulsewidth_ticks: u32 = freq_mhz * self.pulsewidth_us;

        // Set the match registers for period (CH0) and pulsewidth (CHN)
        CHANNEL0.set_mr(period_ticks);
        self.channel.set_mr(pulsewidth_ticks);

        // set the channel latch to update CH0 and CHN
        reg::PWM1.ler.set_value(1 << CHANNEL0 as u32 |
                                1 << self.channel as u32);

        // enable counter and pwm, clear reset
        reg::PWM1.tcr
            .set_ctr_en(reg::PWM1_tcr_ctr_en::ENABLED)
            .set_pwm_enable(reg::PWM1_tcr_pwm_enable::ENABLED)
            .set_ctr_reset(reg::PWM1_tcr_ctr_reset::SYNCHRONOUS_RESET);
    }

}

/// Implementation of Generic PWMOutput trait for LPC17xx
impl ::hal::pwm::PWMOutput for PWM {
    fn set_period_us(&mut self, period_us: u32) {
        self.period_us = period_us;
        self.update_output();
    }

    fn get_period_us(&self) -> u32 {
        self.period_us
    }

    fn set_pulsewidth_us(&mut self, pulsewidth_us: u32) {
        self.pulsewidth_us = pulsewidth_us;
        self.update_output();
    }

    fn get_pulsewidth_us(&self) -> u32 {
        self.pulsewidth_us
    }
}

/// LPC17xx PWM Register Definitions (User Manual: 24.6)
mod reg {
    use volatile_cell::VolatileCell;
    use core::ops::Drop;

    ioregs!(PWM1 = {
        /// Interrupt Register. The IR can be written to clear
        /// interrupts. The IR can be read to identify which of eight
        /// possible interrupt sources are pending.
        0x00 => reg32 ir {
            0  => irq_mr0,  //= Interrupt flag for PWM match channel 0.
            1  => irq_mr1,  //= Interrupt flag for PWM match channel 1.
            2  => irq_mr2,  //= Interrupt flag for PWM match channel 2.
            3  => irq_mr3,  //= Interrupt flag for PWM match channel 3.
            4  => irq_cap0, //= Interrupt flag for capture input 0
            5  => irq_cap1, //= Interrupt flag for capture input 1.
            // 7:6 Reserved
            8  => irq_mr4,  //= Interrupt flag for PWM match channel 4.
            9  => irq_mr5,  //= Interrupt flag for PWM match channel 5.
            10 => irq_mr6,  //= Interrupt flag for PWM match channel 6.
        }
        /// Timer Control Register. The TCR is used to control the
        /// Timer Counter functions. The Timer Counter can be disabled
        /// or reset through the TCR.
        0x04 => reg32 tcr {
            0 => ctr_en {
                0 => DISABLED,
                1 => ENABLED
            },
            1 => ctr_reset {
                0 => CLEAR_RESET,
                1 => SYNCHRONOUS_RESET
            },
            // 2 => Reserved
            3 => pwm_enable {
                0 => DISABLED,
                1 => ENABLED
            }
        }
        /// Timer Counter. The 32-bit TC is incremented every PR+1
        /// cycles of PCLK.  The TC is controlled through the TCR.
        0x08 => reg32 tc { 31..0 => value }
        /// Prescale Register. The TC is incremented every PR+1 cycles
        /// of PCLK.
        0x0C => reg32 pr { 31..0 => value }
        /// Prescale Counter. The 32-bit PC is a counter which is
        /// incremented to the value stored in PR. When the value in
        /// PR is reached, the TC is incremented. The PC is observable
        /// and controllable through the bus interface.
        0x10 => reg32 pc { 31..0 => value }
        /// Match Control Register. The MCR is used to control if an
        /// interrupt is generated and if the TC is reset when a Match
        /// occurs.
        0x14 => reg32 mcr {
            0 => pwmmr0i,  //= if set, interrupt on pwmmr0
            1 => pwmmr0r,  //= if set, reset on pwmmr0
            2 => pwmmr0s,  //= if set, stop on pwmmr0

            3 => pwmmr1i,  //= if set, interrupt on pwmmr1
            4 => pwmmr1r,  //= if set, reset on pwmmr1
            5 => pwmmr1s,  //= if set, stop on pwmmr1

            6 => pwmmr2i,  //= if set, interrupt on pwmmr2
            7 => pwmmr2r,  //= if set, reset on pwmmr2
            8 => pwmmr2s,  //= if set, stop on pwmmr2

            9 => pwmmr3i,  //= if set, interrupt on pwmmr3
            10 => pwmmr3r,  //= if set, reset on pwmmr3
            11 => pwmmr3s,  //= if set, stop on pwmmr3

            12 => pwmmr4i,  //= if set, interrupt on pwmmr4
            13 => pwmmr4r,  //= if set, reset on pwmmr4
            14 => pwmmr4s,  //= if set, stop on pwmmr4

            15=> pwmmr5i,  //= if set, interrupt on pwmmr5
            16 => pwmmr5r,  //= if set, reset on pwmmr5
            17 => pwmmr5s,  //= if set, stop on pwmmr5

            18 => pwmmr6i,  //= if set, interrupt on pwmmr6
            19 => pwmmr6r,  //= if set, reset on pwmmr6
            20 => pwmmr6s,  //= if set, stop on pwmmr6
        }
        /// Match Registers (0-3).  MR<N> can be enabled in the MCR to reset
        /// the TC, stop both the TC and PC, and/or generate an
        /// interrupt when it matches the TC.  In addition, a match
        /// between this value and the TC sets any PWM output that is
        /// in single-edge mode, and sets PWM<N + 1> if it’s in double-edge
        /// mode.
        0x18 => reg32 mr[4] { 31..0 => value }
        /// Capture Control Register. The CCR controls which edges of
        /// the capture inputs are used to load the Capture Registers
        /// and whether or not an interrupt is generated when a
        /// capture takes place.
        0x28 => reg32 ccr { 31..0 => value }
        /// Capture Registers (0-3). CR<N> is loaded with the value of the TC
        /// when there is an event on the CAPn.<N> input.
        0x30 => reg32 cr[4] { 31..0 => value }
        /// Match Registers (4-6).  See `mr` registers.  Not sure why
        /// banks are fragmented.
        0x40 => reg32 mr2[3] { 31..0 => value }
        /// PWM Control Register. Enables PWM outputs and selects PWM
        /// channel types as either single edge or double edge
        /// controlled.
        0x4c => reg32 pcr {
            9  => pwmena1,
            10 => pwmena2,
            11 => pwmena3,
            12 => pwmena4,
            13 => pwmena5,
            14 => pwmena6,
        }
        /// Load Enable Register. Enables use of new PWM match
        /// values.
        0x50 => reg32 ler { 31..0 => value }
        /// Count Control Register. The CTCR selects between Timer and
        /// Counter mode, and in Counter mode selects the signal and
        /// edge(s) for counting.
        0x70 => reg32 ctcr {
            0..2 => mode {
                0 => TIMER_MODE,
                1 => COUNTER_MODE_RISING,
                2 => COUNTER_MODE_FALLING,
                3 => COUNTER_MODE_BOTH
            }
        }
    });

    extern {
        #[link_name="lpc17xx_iomem_PWM1"] pub static PWM1: PWM1;
    }

}
<commit_msg>pwm: do *not* set the synchronous reset bit<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2015 Paul Osborne <osbpau@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! PWM Support for the NXP LPC17xx MCUs

use hal::lpc17xx::peripheral_clock::PeripheralClock::PWM1Clock;
use core::intrinsics::abort;

use self::PWMChannel::*;

#[path="../../util/ioreg.rs"]
#[macro_use] mod ioreg;

/// "Channels" correspond to MR1..6 (0 used for period)
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum PWMChannel {
    CHANNEL0 = 0,  // reserved for period
    CHANNEL1,
    CHANNEL2,
    CHANNEL3,
    CHANNEL4,
    CHANNEL5,
    CHANNEL6,
}

impl PWMChannel {

    /// Set the match register for this channel to the provided value
    fn set_mr(&self, value: u32) {
        // note that for whatever reason, the match registers are fragmented
        // across two registers.  They aren't even located next to each other
        // in memory
        match *self {
            CHANNEL0 => { reg::PWM1.mr[0].set_value(value); },
            CHANNEL1 => { reg::PWM1.mr[1].set_value(value); },
            CHANNEL2 => { reg::PWM1.mr[2].set_value(value); },
            CHANNEL3 => { reg::PWM1.mr[3].set_value(value); },
            CHANNEL4 => { reg::PWM1.mr2[0].set_value(value); },
            CHANNEL5 => { reg::PWM1.mr2[1].set_value(value); },
            CHANNEL6 => { reg::PWM1.mr2[2].set_value(value); },
        };
    }
}

#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub struct PWM {
    channel: PWMChannel,
    period_us: u32,
    pulsewidth_us: u32,
}

impl PWM {

    /// Create a new PWM Output on the provided channel
    pub fn new(channel: PWMChannel) -> PWM {
        PWM1Clock.enable();
        PWM1Clock.set_divisor(4);

        // reset TC on match 0 for this channel and
        // enable output
        match channel {
            CHANNEL0 => { unsafe { abort() } },  // CHANNEL0 reserved for internal use
            CHANNEL1 => { reg::PWM1.mcr.set_pwmmr1r(true); reg::PWM1.pcr.set_pwmena1(true); },
            CHANNEL2 => { reg::PWM1.mcr.set_pwmmr2r(true); reg::PWM1.pcr.set_pwmena2(true); },
            CHANNEL3 => { reg::PWM1.mcr.set_pwmmr3r(true); reg::PWM1.pcr.set_pwmena3(true); },
            CHANNEL4 => { reg::PWM1.mcr.set_pwmmr4r(true); reg::PWM1.pcr.set_pwmena4(true); },
            CHANNEL5 => { reg::PWM1.mcr.set_pwmmr5r(true); reg::PWM1.pcr.set_pwmena5(true); },
            CHANNEL6 => { reg::PWM1.mcr.set_pwmmr6r(true); reg::PWM1.pcr.set_pwmena6(true); },
        };

        let pwm = PWM {
            channel: channel,
            period_us: 20_000,  // 20ms
            pulsewidth_us: 0,   // off by default
        };
        pwm.update_output();

        // TODO: Pin Muxing?
        //   - On the LPC1768, a few pads could be used for, e.g. PWM1.3
        //   - Can work as-is if function setup correct on the Pin, but this requires
        //     reading the user manual in detail
        //   - Alt for PWM on various pins is not always the same (1 or 2)
        pwm
    }

    /// Update the PWM Signal based on the current state
    fn update_output(&self) {
        // calculate the number of ticks for our period and pulsewidth
        let freq_hz: u32 = PWM1Clock.frequency();
        let freq_mhz: u32 = freq_hz / 1_000_000;
        let period_ticks: u32 = freq_mhz * self.period_us;
        let pulsewidth_ticks: u32 = freq_mhz * self.pulsewidth_us;

        // Set the match registers for period (CH0) and pulsewidth (CHN)
        CHANNEL0.set_mr(period_ticks);
        self.channel.set_mr(pulsewidth_ticks);

        // set the channel latch to update CH0 and CHN
        reg::PWM1.ler.set_value(1 << CHANNEL0 as u32 |
                                1 << self.channel as u32);

        // enable counter and pwm, clear reset
        reg::PWM1.tcr
            .set_ctr_en(reg::PWM1_tcr_ctr_en::ENABLED)
            .set_pwm_enable(reg::PWM1_tcr_pwm_enable::ENABLED);
    }

}

/// Implementation of Generic PWMOutput trait for LPC17xx
impl ::hal::pwm::PWMOutput for PWM {
    fn set_period_us(&mut self, period_us: u32) {
        self.period_us = period_us;
        self.update_output();
    }

    fn get_period_us(&self) -> u32 {
        self.period_us
    }

    fn set_pulsewidth_us(&mut self, pulsewidth_us: u32) {
        self.pulsewidth_us = pulsewidth_us;
        self.update_output();
    }

    fn get_pulsewidth_us(&self) -> u32 {
        self.pulsewidth_us
    }
}

/// LPC17xx PWM Register Definitions (User Manual: 24.6)
mod reg {
    use volatile_cell::VolatileCell;
    use core::ops::Drop;

    ioregs!(PWM1 = {
        /// Interrupt Register. The IR can be written to clear
        /// interrupts. The IR can be read to identify which of eight
        /// possible interrupt sources are pending.
        0x00 => reg32 ir {
            0  => irq_mr0,  //= Interrupt flag for PWM match channel 0.
            1  => irq_mr1,  //= Interrupt flag for PWM match channel 1.
            2  => irq_mr2,  //= Interrupt flag for PWM match channel 2.
            3  => irq_mr3,  //= Interrupt flag for PWM match channel 3.
            4  => irq_cap0, //= Interrupt flag for capture input 0
            5  => irq_cap1, //= Interrupt flag for capture input 1.
            // 7:6 Reserved
            8  => irq_mr4,  //= Interrupt flag for PWM match channel 4.
            9  => irq_mr5,  //= Interrupt flag for PWM match channel 5.
            10 => irq_mr6,  //= Interrupt flag for PWM match channel 6.
        }
        /// Timer Control Register. The TCR is used to control the
        /// Timer Counter functions. The Timer Counter can be disabled
        /// or reset through the TCR.
        0x04 => reg32 tcr {
            0 => ctr_en {
                0 => DISABLED,
                1 => ENABLED
            },
            1 => ctr_reset {
                0 => CLEAR_RESET,
                1 => SYNCHRONOUS_RESET
            },
            // 2 => Reserved
            3 => pwm_enable {
                0 => DISABLED,
                1 => ENABLED
            }
        }
        /// Timer Counter. The 32-bit TC is incremented every PR+1
        /// cycles of PCLK.  The TC is controlled through the TCR.
        0x08 => reg32 tc { 31..0 => value }
        /// Prescale Register. The TC is incremented every PR+1 cycles
        /// of PCLK.
        0x0C => reg32 pr { 31..0 => value }
        /// Prescale Counter. The 32-bit PC is a counter which is
        /// incremented to the value stored in PR. When the value in
        /// PR is reached, the TC is incremented. The PC is observable
        /// and controllable through the bus interface.
        0x10 => reg32 pc { 31..0 => value }
        /// Match Control Register. The MCR is used to control if an
        /// interrupt is generated and if the TC is reset when a Match
        /// occurs.
        0x14 => reg32 mcr {
            0 => pwmmr0i,  //= if set, interrupt on pwmmr0
            1 => pwmmr0r,  //= if set, reset on pwmmr0
            2 => pwmmr0s,  //= if set, stop on pwmmr0

            3 => pwmmr1i,  //= if set, interrupt on pwmmr1
            4 => pwmmr1r,  //= if set, reset on pwmmr1
            5 => pwmmr1s,  //= if set, stop on pwmmr1

            6 => pwmmr2i,  //= if set, interrupt on pwmmr2
            7 => pwmmr2r,  //= if set, reset on pwmmr2
            8 => pwmmr2s,  //= if set, stop on pwmmr2

            9 => pwmmr3i,  //= if set, interrupt on pwmmr3
            10 => pwmmr3r,  //= if set, reset on pwmmr3
            11 => pwmmr3s,  //= if set, stop on pwmmr3

            12 => pwmmr4i,  //= if set, interrupt on pwmmr4
            13 => pwmmr4r,  //= if set, reset on pwmmr4
            14 => pwmmr4s,  //= if set, stop on pwmmr4

            15=> pwmmr5i,  //= if set, interrupt on pwmmr5
            16 => pwmmr5r,  //= if set, reset on pwmmr5
            17 => pwmmr5s,  //= if set, stop on pwmmr5

            18 => pwmmr6i,  //= if set, interrupt on pwmmr6
            19 => pwmmr6r,  //= if set, reset on pwmmr6
            20 => pwmmr6s,  //= if set, stop on pwmmr6
        }
        /// Match Registers (0-3).  MR<N> can be enabled in the MCR to reset
        /// the TC, stop both the TC and PC, and/or generate an
        /// interrupt when it matches the TC.  In addition, a match
        /// between this value and the TC sets any PWM output that is
        /// in single-edge mode, and sets PWM<N + 1> if it’s in double-edge
        /// mode.
        0x18 => reg32 mr[4] { 31..0 => value }
        /// Capture Control Register. The CCR controls which edges of
        /// the capture inputs are used to load the Capture Registers
        /// and whether or not an interrupt is generated when a
        /// capture takes place.
        0x28 => reg32 ccr { 31..0 => value }
        /// Capture Registers (0-3). CR<N> is loaded with the value of the TC
        /// when there is an event on the CAPn.<N> input.
        0x30 => reg32 cr[4] { 31..0 => value }
        /// Match Registers (4-6).  See `mr` registers.  Not sure why
        /// banks are fragmented.
        0x40 => reg32 mr2[3] { 31..0 => value }
        /// PWM Control Register. Enables PWM outputs and selects PWM
        /// channel types as either single edge or double edge
        /// controlled.
        0x4c => reg32 pcr {
            9  => pwmena1,
            10 => pwmena2,
            11 => pwmena3,
            12 => pwmena4,
            13 => pwmena5,
            14 => pwmena6,
        }
        /// Load Enable Register. Enables use of new PWM match
        /// values.
        0x50 => reg32 ler { 31..0 => value }
        /// Count Control Register. The CTCR selects between Timer and
        /// Counter mode, and in Counter mode selects the signal and
        /// edge(s) for counting.
        0x70 => reg32 ctcr {
            0..2 => mode {
                0 => TIMER_MODE,
                1 => COUNTER_MODE_RISING,
                2 => COUNTER_MODE_FALLING,
                3 => COUNTER_MODE_BOTH
            }
        }
    });

    extern {
        #[link_name="lpc17xx_iomem_PWM1"] pub static PWM1: PWM1;
    }

}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cookie::Cookie;
use cookie_storage::CookieStorage;
use fetch::methods::should_be_blocked_due_to_bad_port;
use http_loader;
use hyper::header::{Host, SetCookie};
use net_traits::{CookieSource, MessageData, WebSocketCommunicate};
use net_traits::{WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent};
use net_traits::hosts::replace_hosts;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use websocket::{Client, Message};
use websocket::header::{Origin, WebSocketProtocol};
use websocket::message::Type;
use websocket::receiver::Receiver;
use websocket::result::{WebSocketError, WebSocketResult};
use websocket::sender::Sender;
use websocket::stream::WebSocketStream;
use websocket::ws::receiver::Receiver as WSReceiver;
use websocket::ws::sender::Sender as Sender_Object;

// https://fetch.spec.whatwg.org/#concept-websocket-establish
fn establish_a_websocket_connection(resource_url: &ServoUrl,
                                    origin: String,
                                    protocols: Vec<String>,
                                    cookie_jar: Arc<RwLock<CookieStorage>>)
                                    -> WebSocketResult<(Option<String>,
                                                        Sender<WebSocketStream>,
                                                        Receiver<WebSocketStream>)> {
    // Steps 1-2 are not really applicable here, given we don't exactly go
    // through the same infrastructure as the Fetch spec.

    if should_be_blocked_due_to_bad_port(resource_url) {
        // Subset of steps 11-12, we inline the bad port check here from the
        // main fetch algorithm for the same reason steps 1-2 are not
        // applicable.
        return Err(WebSocketError::RequestError("Request should be blocked due to bad port."));
    }

    // Steps 3-7.
    let net_url = replace_hosts(resource_url);
    let mut request = try!(Client::connect(net_url.as_url()));

    // Client::connect sets the Host header to the host of the URL that is
    // passed to it, so we need to reset it afterwards to the correct one.
    request.headers.set(Host {
        hostname: resource_url.host_str().unwrap().to_owned(),
        port: resource_url.port(),
    });

    // Step 8.
    if !protocols.is_empty() {
        request.headers.set(WebSocketProtocol(protocols.clone()));
    }

    // Steps 9-10.
    // TODO: support for permessage-deflate extension.

    // Subset of step 11.
    // See step 2 of https://fetch.spec.whatwg.org/#concept-fetch.
    request.headers.set(Origin(origin));

    // Transitive subset of step 11.
    // See step 17.1 of https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch.
    http_loader::set_request_cookies(&resource_url, &mut request.headers, &cookie_jar);

    // Step 11, somewhat.
    let response = try!(request.send());

    // Step 12, 14.
    try!(response.validate());

    // Step 13 and transitive subset of step 14.
    // See step 6 of http://tools.ietf.org/html/rfc6455#section-4.1.
    let protocol_in_use = response.protocol().and_then(|header| {
        // https://github.com/whatwg/fetch/issues/515
        header.first().cloned()
    });
    if let Some(ref protocol_name) = protocol_in_use {
        if !protocols.is_empty() && !protocols.iter().any(|p| (&**p).eq_ignore_ascii_case(protocol_name)) {
            return Err(WebSocketError::ProtocolError("Protocol in Use not in client-supplied protocol list"));
        };
    };

    // Transitive subset of step 11.
    // See step 15 of https://fetch.spec.whatwg.org/#http-network-fetch.
    if let Some(cookies) = response.headers.get::<SetCookie>() {
        let mut jar = cookie_jar.write().unwrap();
        for cookie in &**cookies {
            if let Some(cookie) = Cookie::new_wrapped(cookie.clone(), resource_url, CookieSource::HTTP) {
                jar.push(cookie, resource_url, CookieSource::HTTP);
            }
        }
    }

    let (sender, receiver) = response.begin().split();
    Ok((protocol_in_use, sender, receiver))
}

pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData, cookie_jar: Arc<RwLock<CookieStorage>>) {
    thread::Builder::new().name(format!("WebSocket connection to {}", connect_data.resource_url)).spawn(move || {
        let channel = establish_a_websocket_connection(&connect_data.resource_url,
                                                       connect_data.origin,
                                                       connect_data.protocols,
                                                       cookie_jar);
        let (ws_sender, mut receiver) = match channel {
            Ok((protocol_in_use, sender, receiver)) => {
                let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use });
                (sender, receiver)
            },
            Err(e) => {
                debug!("Failed to establish a WebSocket connection: {:?}", e);
                let _ = connect.event_sender.send(WebSocketNetworkEvent::Fail);
                return;
            }

        };

        let initiated_close = Arc::new(AtomicBool::new(false));
        let ws_sender = Arc::new(Mutex::new(ws_sender));

        let initiated_close_incoming = initiated_close.clone();
        let ws_sender_incoming = ws_sender.clone();
        let resource_event_sender = connect.event_sender;
        thread::spawn(move || {
            for message in receiver.incoming_messages() {
                let message: Message = match message {
                    Ok(m) => m,
                    Err(e) => {
                        debug!("Error receiving incoming WebSocket message: {:?}", e);
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
                        break;
                    }
                };
                let message = match message.opcode {
                    Type::Text => MessageData::Text(String::from_utf8_lossy(&message.payload).into_owned()),
                    Type::Binary => MessageData::Binary(message.payload.into_owned()),
                    Type::Ping => {
                        let pong = Message::pong(message.payload);
                        ws_sender_incoming.lock().unwrap().send_message(&pong).unwrap();
                        continue;
                    },
                    Type::Pong => continue,
                    Type::Close => {
                        if !initiated_close_incoming.fetch_or(true, Ordering::SeqCst) {
                            ws_sender_incoming.lock().unwrap().send_message(&message).unwrap();
                        }
                        let code = message.cd_status_code;
                        let reason = String::from_utf8_lossy(&message.payload).into_owned();
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Close(code, reason));
                        break;
                    },
                };
                let _ = resource_event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
            }
        });

        let initiated_close_outgoing = initiated_close.clone();
        let ws_sender_outgoing = ws_sender.clone();
        let resource_action_receiver = connect.action_receiver;
        thread::spawn(move || {
            while let Ok(dom_action) = resource_action_receiver.recv() {
                match dom_action {
                    WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
                        ws_sender_outgoing.lock().unwrap().send_message(&Message::text(data)).unwrap();
                    },
                    WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
                        ws_sender_outgoing.lock().unwrap().send_message(&Message::binary(data)).unwrap();
                    },
                    WebSocketDomAction::Close(code, reason) => {
                        if !initiated_close_outgoing.fetch_or(true, Ordering::SeqCst) {
                            let message = match code {
                                Some(code) => Message::close_because(code, reason.unwrap_or("".to_owned())),
                                None => Message::close()
                            };
                            ws_sender_outgoing.lock().unwrap().send_message(&message).unwrap();
                        }
                    },
                }
            }
        });
    }).expect("Thread spawning failed");
}
<commit_msg>Remove some useless thread spawning in websocket_loader<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cookie::Cookie;
use cookie_storage::CookieStorage;
use fetch::methods::should_be_blocked_due_to_bad_port;
use http_loader;
use hyper::header::{Host, SetCookie};
use net_traits::{CookieSource, MessageData, WebSocketCommunicate};
use net_traits::{WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent};
use net_traits::hosts::replace_hosts;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use websocket::{Client, Message};
use websocket::header::{Origin, WebSocketProtocol};
use websocket::message::Type;
use websocket::receiver::Receiver;
use websocket::result::{WebSocketError, WebSocketResult};
use websocket::sender::Sender;
use websocket::stream::WebSocketStream;
use websocket::ws::receiver::Receiver as WSReceiver;
use websocket::ws::sender::Sender as Sender_Object;

// https://fetch.spec.whatwg.org/#concept-websocket-establish
fn establish_a_websocket_connection(resource_url: &ServoUrl,
                                    origin: String,
                                    protocols: Vec<String>,
                                    cookie_jar: Arc<RwLock<CookieStorage>>)
                                    -> WebSocketResult<(Option<String>,
                                                        Sender<WebSocketStream>,
                                                        Receiver<WebSocketStream>)> {
    // Steps 1-2 are not really applicable here, given we don't exactly go
    // through the same infrastructure as the Fetch spec.

    if should_be_blocked_due_to_bad_port(resource_url) {
        // Subset of steps 11-12, we inline the bad port check here from the
        // main fetch algorithm for the same reason steps 1-2 are not
        // applicable.
        return Err(WebSocketError::RequestError("Request should be blocked due to bad port."));
    }

    // Steps 3-7.
    let net_url = replace_hosts(resource_url);
    let mut request = try!(Client::connect(net_url.as_url()));

    // Client::connect sets the Host header to the host of the URL that is
    // passed to it, so we need to reset it afterwards to the correct one.
    request.headers.set(Host {
        hostname: resource_url.host_str().unwrap().to_owned(),
        port: resource_url.port(),
    });

    // Step 8.
    if !protocols.is_empty() {
        request.headers.set(WebSocketProtocol(protocols.clone()));
    }

    // Steps 9-10.
    // TODO: support for permessage-deflate extension.

    // Subset of step 11.
    // See step 2 of https://fetch.spec.whatwg.org/#concept-fetch.
    request.headers.set(Origin(origin));

    // Transitive subset of step 11.
    // See step 17.1 of https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch.
    http_loader::set_request_cookies(&resource_url, &mut request.headers, &cookie_jar);

    // Step 11, somewhat.
    let response = try!(request.send());

    // Step 12, 14.
    try!(response.validate());

    // Step 13 and transitive subset of step 14.
    // See step 6 of http://tools.ietf.org/html/rfc6455#section-4.1.
    let protocol_in_use = response.protocol().and_then(|header| {
        // https://github.com/whatwg/fetch/issues/515
        header.first().cloned()
    });
    if let Some(ref protocol_name) = protocol_in_use {
        if !protocols.is_empty() && !protocols.iter().any(|p| (&**p).eq_ignore_ascii_case(protocol_name)) {
            return Err(WebSocketError::ProtocolError("Protocol in Use not in client-supplied protocol list"));
        };
    };

    // Transitive subset of step 11.
    // See step 15 of https://fetch.spec.whatwg.org/#http-network-fetch.
    if let Some(cookies) = response.headers.get::<SetCookie>() {
        let mut jar = cookie_jar.write().unwrap();
        for cookie in &**cookies {
            if let Some(cookie) = Cookie::new_wrapped(cookie.clone(), resource_url, CookieSource::HTTP) {
                jar.push(cookie, resource_url, CookieSource::HTTP);
            }
        }
    }

    let (sender, receiver) = response.begin().split();
    Ok((protocol_in_use, sender, receiver))
}

pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData, cookie_jar: Arc<RwLock<CookieStorage>>) {
    thread::Builder::new().name(format!("WebSocket connection to {}", connect_data.resource_url)).spawn(move || {
        let channel = establish_a_websocket_connection(&connect_data.resource_url,
                                                       connect_data.origin,
                                                       connect_data.protocols,
                                                       cookie_jar);
        let (ws_sender, mut receiver) = match channel {
            Ok((protocol_in_use, sender, receiver)) => {
                let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use });
                (sender, receiver)
            },
            Err(e) => {
                debug!("Failed to establish a WebSocket connection: {:?}", e);
                let _ = connect.event_sender.send(WebSocketNetworkEvent::Fail);
                return;
            }

        };

        let initiated_close = Arc::new(AtomicBool::new(false));
        let ws_sender = Arc::new(Mutex::new(ws_sender));

        let initiated_close_incoming = initiated_close.clone();
        let ws_sender_incoming = ws_sender.clone();
        let resource_event_sender = connect.event_sender;
        thread::spawn(move || {
            for message in receiver.incoming_messages() {
                let message: Message = match message {
                    Ok(m) => m,
                    Err(e) => {
                        debug!("Error receiving incoming WebSocket message: {:?}", e);
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
                        break;
                    }
                };
                let message = match message.opcode {
                    Type::Text => MessageData::Text(String::from_utf8_lossy(&message.payload).into_owned()),
                    Type::Binary => MessageData::Binary(message.payload.into_owned()),
                    Type::Ping => {
                        let pong = Message::pong(message.payload);
                        ws_sender_incoming.lock().unwrap().send_message(&pong).unwrap();
                        continue;
                    },
                    Type::Pong => continue,
                    Type::Close => {
                        if !initiated_close_incoming.fetch_or(true, Ordering::SeqCst) {
                            ws_sender_incoming.lock().unwrap().send_message(&message).unwrap();
                        }
                        let code = message.cd_status_code;
                        let reason = String::from_utf8_lossy(&message.payload).into_owned();
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Close(code, reason));
                        break;
                    },
                };
                let _ = resource_event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
            }
        });

        while let Ok(dom_action) = connect.action_receiver.recv() {
            match dom_action {
                WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
                    ws_sender.lock().unwrap().send_message(&Message::text(data)).unwrap();
                },
                WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
                    ws_sender.lock().unwrap().send_message(&Message::binary(data)).unwrap();
                },
                WebSocketDomAction::Close(code, reason) => {
                    if !initiated_close.fetch_or(true, Ordering::SeqCst) {
                        let message = match code {
                            Some(code) => Message::close_because(code, reason.unwrap_or("".to_owned())),
                            None => Message::close()
                        };
                        ws_sender.lock().unwrap().send_message(&message).unwrap();
                    }
                },
            }
        }
    }).expect("Thread spawning failed");
}
<|endoftext|>


<commit_before>extern crate reqwest;

#[cfg(feature = "tls")]
#[test]
fn test_badssl_modern() {
    let text = reqwest::get("https://mozilla-modern.badssl.com/").unwrap()
        .text().unwrap();

    assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}

#[cfg(feature = "tls")]
#[test]
fn test_badssl_self_signed() {
    let text = reqwest::Client::builder()
        .danger_accept_invalid_certs(true)
        .build().unwrap()
        .get("https://self-signed.badssl.com/")
        .send().unwrap()
        .text().unwrap();

    assert!(text.contains("<title>self-signed.badssl.com</title>"));
}

#[cfg(feature = "default-tls")]
#[test]
fn test_badssl_wrong_host() {
    let text = reqwest::Client::builder()
        .danger_accept_invalid_hostnames(true)
        .build().unwrap()
        .get("https://wrong.host.badssl.com/")
        .send().unwrap()
        .text().unwrap();

    assert!(text.contains("<title>wrong.host.badssl.com</title>"));

    let result = reqwest::Client::builder()
        .danger_accept_invalid_hostnames(true)
        .build().unwrap()
        .get("https://self-signed.badssl.com/")
        .send();

    assert!(result.is_err());
}
<commit_msg>tests: add more badssl tests for rustls<commit_after>extern crate reqwest;

#[cfg(feature = "tls")]
#[test]
fn test_badssl_modern() {
    let text = reqwest::get("https://mozilla-modern.badssl.com/").unwrap()
        .text().unwrap();

    assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}

#[cfg(feature = "rustls-tls")]
#[test]
fn test_rustls_badssl_modern() {
    let text = reqwest::Client::builder()
        .use_rustls_tls()
        .build().unwrap()
        .get("https://mozilla-modern.badssl.com/")
        .send().unwrap()
        .text().unwrap();

    assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}

#[cfg(feature = "tls")]
#[test]
fn test_badssl_self_signed() {
    let text = reqwest::Client::builder()
        .danger_accept_invalid_certs(true)
        .build().unwrap()
        .get("https://self-signed.badssl.com/")
        .send().unwrap()
        .text().unwrap();

    assert!(text.contains("<title>self-signed.badssl.com</title>"));
}

#[cfg(feature = "default-tls")]
#[test]
fn test_badssl_wrong_host() {
    let text = reqwest::Client::builder()
        .danger_accept_invalid_hostnames(true)
        .build().unwrap()
        .get("https://wrong.host.badssl.com/")
        .send().unwrap()
        .text().unwrap();

    assert!(text.contains("<title>wrong.host.badssl.com</title>"));

    let result = reqwest::Client::builder()
        .danger_accept_invalid_hostnames(true)
        .build().unwrap()
        .get("https://self-signed.badssl.com/")
        .send();

    assert!(result.is_err());
}
<|endoftext|>


<commit_before>extern crate reqwest;

#[macro_use] mod server;

use std::io::Read;

#[test]
fn test_get() {
    let server = server! {
        request: b"\
            GET /1 HTTP/1.1\r\n\
            Host: $HOST\r\n\
            User-Agent: $USERAGENT\r\n\
            Accept: */*\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 200 OK\r\n\
            Server: test\r\n\
            Content-Length: 0\r\n\
            \r\n\
            "
    };

    let mut res = reqwest::get(&format!("http://{}/1", server.addr())).unwrap();
    assert_eq!(res.status(), &reqwest::StatusCode::Ok);
    assert_eq!(res.version(), &reqwest::HttpVersion::Http11);
    assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test".to_string())));
    assert_eq!(res.headers().get(), Some(&reqwest::header::ContentLength(0)));

    let mut buf = [0; 1024];
    let n = res.read(&mut buf).unwrap();
    assert_eq!(n, 0)
}

#[test]
fn test_redirect_301_and_302_and_303_changes_post_to_get() {
    let client = reqwest::Client::new().unwrap();
    let codes = [301, 302, 303];

    for code in codes.iter() {
        let redirect = server! {
            request: format!("\
                POST /{} HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Content-Length: 0\r\n\
                \r\n\
                ", code),
            response: format!("\
                HTTP/1.1 {} reason\r\n\
                Server: test-redirect\r\n\
                Content-Length: 0\r\n\
                Location: /dst\r\n\
                Connection: close\r\n\
                \r\n\
                ", code),

            request: format!("\
                GET /dst HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Referer: http://$HOST/{}\r\n\
                \r\n\
                ", code),
            response: b"\
                HTTP/1.1 200 OK\r\n\
                Server: test-dst\r\n\
                Content-Length: 0\r\n\
                \r\n\
                "
        };

        let res = client.post(&format!("http://{}/{}", redirect.addr(), code))
            .send()
            .unwrap();
        assert_eq!(res.status(), &reqwest::StatusCode::Ok);
        assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string())));
    }
}

#[test]
fn test_redirect_307_and_308_tries_to_post_again() {
    let client = reqwest::Client::new().unwrap();
    let codes = [307, 308];
    for code in codes.iter() {
        let redirect = server! {
            request: format!("\
                POST /{} HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Content-Length: 5\r\n\
                \r\n\
                Hello\
                ", code),
            response: format!("\
                HTTP/1.1 {} reason\r\n\
                Server: test-redirect\r\n\
                Content-Length: 0\r\n\
                Location: /dst\r\n\
                Connection: close\r\n\
                \r\n\
                ", code),

            request: format!("\
                POST /dst HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Referer: http://$HOST/{}\r\n\
                Content-Length: 5\r\n\
                \r\n\
                Hello\
                ", code),
            response: b"\
                HTTP/1.1 200 OK\r\n\
                Server: test-dst\r\n\
                Content-Length: 0\r\n\
                \r\n\
                "
        };

        let res = client.post(&format!("http://{}/{}", redirect.addr(), code))
            .body("Hello")
            .send()
            .unwrap();
        assert_eq!(res.status(), &reqwest::StatusCode::Ok);
        assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string())));
    }
}

#[test]
fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
    let client = reqwest::Client::new().unwrap();
    let codes = [307, 308];
    for &code in codes.iter() {
        let redirect = server! {
            request: format!("\
                POST /{} HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Transfer-Encoding: chunked\r\n\
                \r\n\
                5\r\n\
                Hello\r\n\
                0\r\n\r\n\
                ", code),
            response: format!("\
                HTTP/1.1 {} reason\r\n\
                Server: test-redirect\r\n\
                Content-Length: 0\r\n\
                Location: /dst\r\n\
                Connection: close\r\n\
                \r\n\
                ", code)
        };

        let res = client.post(&format!("http://{}/{}", redirect.addr(), code))
            .body(reqwest::Body::new(&b"Hello"[..]))
            .send()
            .unwrap();
        assert_eq!(res.status(), &reqwest::StatusCode::from_u16(code));
    }
}

#[test]
fn test_redirect_policy_can_return_errors() {
    let server = server! {
        request: b"\
            GET /loop HTTP/1.1\r\n\
            Host: $HOST\r\n\
            User-Agent: $USERAGENT\r\n\
            Accept: */*\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 302 Found\r\n\
            Server: test\r\n\
            Location: /loop
            Content-Length: 0\r\n\
            \r\n\
            "
    };

    let err = reqwest::get(&format!("http://{}/loop", server.addr())).unwrap_err();
    match err {
        reqwest::Error::RedirectLoop => (),
        e => panic!("wrong error received: {:?}", e),
    }
}

#[test]
fn test_redirect_policy_can_stop_redirects_without_an_error() {
    let server = server! {
        request: b"\
            GET /no-redirect HTTP/1.1\r\n\
            Host: $HOST\r\n\
            User-Agent: $USERAGENT\r\n\
            Accept: */*\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 302 Found\r\n\
            Server: test-dont\r\n\
            Location: /dont
            Content-Length: 0\r\n\
            \r\n\
            "
    };
    let mut client = reqwest::Client::new().unwrap();
    client.redirect(reqwest::RedirectPolicy::none());

    let res = client.get(&format!("http://{}/no-redirect", server.addr()))
        .send()
        .unwrap();

    assert_eq!(res.status(), &reqwest::StatusCode::Found);
    assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dont".to_string())));
}

#[test]
fn test_accept_header_is_not_changed_if_set() {
    let server = server! {
        request: b"\
            GET /accept HTTP/1.1\r\n\
            Host: $HOST\r\n\
            Accept: application/json\r\n\
            User-Agent: $USERAGENT\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 200 OK\r\n\
            Server: test-accept\r\n\
            Content-Length: 0\r\n\
            \r\n\
            "
    };
    let mut client = reqwest::Client::new().unwrap();

    let res = client.get(&format!("http://{}/accept", server.addr()))
        .header(reqwest::header::Accept::json())
        .send()
        .unwrap();

    assert_eq!(res.status(), &reqwest::StatusCode::Ok);
}
<commit_msg>fix unused mut warning in tests<commit_after>extern crate reqwest;

#[macro_use] mod server;

use std::io::Read;

#[test]
fn test_get() {
    let server = server! {
        request: b"\
            GET /1 HTTP/1.1\r\n\
            Host: $HOST\r\n\
            User-Agent: $USERAGENT\r\n\
            Accept: */*\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 200 OK\r\n\
            Server: test\r\n\
            Content-Length: 0\r\n\
            \r\n\
            "
    };

    let mut res = reqwest::get(&format!("http://{}/1", server.addr())).unwrap();
    assert_eq!(res.status(), &reqwest::StatusCode::Ok);
    assert_eq!(res.version(), &reqwest::HttpVersion::Http11);
    assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test".to_string())));
    assert_eq!(res.headers().get(), Some(&reqwest::header::ContentLength(0)));

    let mut buf = [0; 1024];
    let n = res.read(&mut buf).unwrap();
    assert_eq!(n, 0)
}

#[test]
fn test_redirect_301_and_302_and_303_changes_post_to_get() {
    let client = reqwest::Client::new().unwrap();
    let codes = [301, 302, 303];

    for code in codes.iter() {
        let redirect = server! {
            request: format!("\
                POST /{} HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Content-Length: 0\r\n\
                \r\n\
                ", code),
            response: format!("\
                HTTP/1.1 {} reason\r\n\
                Server: test-redirect\r\n\
                Content-Length: 0\r\n\
                Location: /dst\r\n\
                Connection: close\r\n\
                \r\n\
                ", code),

            request: format!("\
                GET /dst HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Referer: http://$HOST/{}\r\n\
                \r\n\
                ", code),
            response: b"\
                HTTP/1.1 200 OK\r\n\
                Server: test-dst\r\n\
                Content-Length: 0\r\n\
                \r\n\
                "
        };

        let res = client.post(&format!("http://{}/{}", redirect.addr(), code))
            .send()
            .unwrap();
        assert_eq!(res.status(), &reqwest::StatusCode::Ok);
        assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string())));
    }
}

#[test]
fn test_redirect_307_and_308_tries_to_post_again() {
    let client = reqwest::Client::new().unwrap();
    let codes = [307, 308];
    for code in codes.iter() {
        let redirect = server! {
            request: format!("\
                POST /{} HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Content-Length: 5\r\n\
                \r\n\
                Hello\
                ", code),
            response: format!("\
                HTTP/1.1 {} reason\r\n\
                Server: test-redirect\r\n\
                Content-Length: 0\r\n\
                Location: /dst\r\n\
                Connection: close\r\n\
                \r\n\
                ", code),

            request: format!("\
                POST /dst HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Referer: http://$HOST/{}\r\n\
                Content-Length: 5\r\n\
                \r\n\
                Hello\
                ", code),
            response: b"\
                HTTP/1.1 200 OK\r\n\
                Server: test-dst\r\n\
                Content-Length: 0\r\n\
                \r\n\
                "
        };

        let res = client.post(&format!("http://{}/{}", redirect.addr(), code))
            .body("Hello")
            .send()
            .unwrap();
        assert_eq!(res.status(), &reqwest::StatusCode::Ok);
        assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dst".to_string())));
    }
}

#[test]
fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
    let client = reqwest::Client::new().unwrap();
    let codes = [307, 308];
    for &code in codes.iter() {
        let redirect = server! {
            request: format!("\
                POST /{} HTTP/1.1\r\n\
                Host: $HOST\r\n\
                User-Agent: $USERAGENT\r\n\
                Accept: */*\r\n\
                Transfer-Encoding: chunked\r\n\
                \r\n\
                5\r\n\
                Hello\r\n\
                0\r\n\r\n\
                ", code),
            response: format!("\
                HTTP/1.1 {} reason\r\n\
                Server: test-redirect\r\n\
                Content-Length: 0\r\n\
                Location: /dst\r\n\
                Connection: close\r\n\
                \r\n\
                ", code)
        };

        let res = client.post(&format!("http://{}/{}", redirect.addr(), code))
            .body(reqwest::Body::new(&b"Hello"[..]))
            .send()
            .unwrap();
        assert_eq!(res.status(), &reqwest::StatusCode::from_u16(code));
    }
}

#[test]
fn test_redirect_policy_can_return_errors() {
    let server = server! {
        request: b"\
            GET /loop HTTP/1.1\r\n\
            Host: $HOST\r\n\
            User-Agent: $USERAGENT\r\n\
            Accept: */*\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 302 Found\r\n\
            Server: test\r\n\
            Location: /loop
            Content-Length: 0\r\n\
            \r\n\
            "
    };

    let err = reqwest::get(&format!("http://{}/loop", server.addr())).unwrap_err();
    match err {
        reqwest::Error::RedirectLoop => (),
        e => panic!("wrong error received: {:?}", e),
    }
}

#[test]
fn test_redirect_policy_can_stop_redirects_without_an_error() {
    let server = server! {
        request: b"\
            GET /no-redirect HTTP/1.1\r\n\
            Host: $HOST\r\n\
            User-Agent: $USERAGENT\r\n\
            Accept: */*\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 302 Found\r\n\
            Server: test-dont\r\n\
            Location: /dont
            Content-Length: 0\r\n\
            \r\n\
            "
    };
    let mut client = reqwest::Client::new().unwrap();
    client.redirect(reqwest::RedirectPolicy::none());

    let res = client.get(&format!("http://{}/no-redirect", server.addr()))
        .send()
        .unwrap();

    assert_eq!(res.status(), &reqwest::StatusCode::Found);
    assert_eq!(res.headers().get(), Some(&reqwest::header::Server("test-dont".to_string())));
}

#[test]
fn test_accept_header_is_not_changed_if_set() {
    let server = server! {
        request: b"\
            GET /accept HTTP/1.1\r\n\
            Host: $HOST\r\n\
            Accept: application/json\r\n\
            User-Agent: $USERAGENT\r\n\
            \r\n\
            ",
        response: b"\
            HTTP/1.1 200 OK\r\n\
            Server: test-accept\r\n\
            Content-Length: 0\r\n\
            \r\n\
            "
    };
    let client = reqwest::Client::new().unwrap();

    let res = client.get(&format!("http://{}/accept", server.addr()))
        .header(reqwest::header::Accept::json())
        .send()
        .unwrap();

    assert_eq!(res.status(), &reqwest::StatusCode::Ok);
}
<|endoftext|>