<commit_before>use std::collections::btree_map::BTreeMap;
use index::EntryKey;
use dovahkiin::types::Id;
use std::collections::HashMap;
use serde::Serialize;
use serde::Deserialize;
use serde::de::DeserializeOwned;
use bifrost::raft::state_machine::StateMachineCtl;
use bifrost::utils::bincode::serialize;
use bincode::deserialize;
use bifrost_plugins::hash_ident;

pub static SM_ID: u64 = hash_ident!(LSM_TREE_PLACEMENT_SM) as u64;

#[derive(Serialize, Deserialize)]
pub enum SplitError {
    AnotherSplitInProgress(Id),
    MidOutOfRange,
    PlacementNotFound
}

pub struct InSplitStatus {
    part_2: Id,
    mid: EntryKey
}

pub struct Placement {
    starts: EntryKey,
    ends: EntryKey,
    in_split: Option<InSplitStatus>,
    id: Id,
}

pub struct PlacementSM {
    placements: HashMap<Id, Placement>,
    starts: BTreeMap<EntryKey, Id>,

}

raft_state_machine! {
    def cmd prepare_split(source: Id)  -> Id | SplitError;
    def cmd start_split(source: Id, mid: Vec<u8>) -> bool | SplitError;
    def cmd complete_split(part_1: Id, part_2: Id) -> bool | SplitError;
    def qry locate(id: Id) -> Id | SplitError;
}

impl StateMachineCmds for PlacementSM {
    fn prepare_split(&mut self, source: Id) -> Result<Id, SplitError> {
        unimplemented!()
    }

    fn start_split(&mut self, source: Id, mid: Vec<u8>) -> Result<bool, SplitError> {
        unimplemented!()
    }

    fn complete_split(&mut self, part_1: Id, part_2: Id) -> Result<bool, SplitError> {
        unimplemented!()
    }

    fn locate(&self, id: Id) -> Result<Id, SplitError> {
        unimplemented!()
    }
}

impl StateMachineCtl for PlacementSM {
    raft_sm_complete!();
    fn id(&self) -> u64 {
        SM_ID
    }
    fn snapshot(&self) -> Option<Vec<u8>> {
        unimplemented!()
    }
    fn recover(&mut self, data: Vec<u8>) {
        unimplemented!()
    }
}<commit_msg>impl lsm-tree placement driver<commit_after>use std::collections::btree_map::BTreeMap;
use index::EntryKey;
use dovahkiin::types::Id;
use std::collections::HashMap;
use serde::Serialize;
use serde::Deserialize;
use serde::de::DeserializeOwned;
use bifrost::raft::state_machine::StateMachineCtl;
use bifrost::utils::bincode::serialize;
use bincode::deserialize;
use bifrost_plugins::hash_ident;
use parking_lot::RwLock;
use std::collections::HashSet;
use std::collections::btree_set::BTreeSet;
use ram::types::RandValue;
use smallvec::SmallVec;
use serde_json::ser::CharEscape::Quote;

pub static SM_ID: u64 = hash_ident!(LSM_TREE_PLACEMENT_SM) as u64;

#[derive(Serialize, Deserialize)]
pub enum SplitError {
    AnotherSplitInProgress(InSplitStatus),
    CannotFindSplitMeta,
    SplitUnmatchSource,
    NoSplitInProgress,
    MidOutOfRange,
    PlacementNotFound
}

#[derive(Serialize, Deserialize)]
pub enum QueryError {
    OutOfRange
}

#[derive(Serialize, Deserialize)]
pub struct QueryResult {
    id: Id,
    split: Option<Id>,
    epoch: u64,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct InSplitStatus {
    dest: Id,
    mid: Vec<u8>
}

pub struct Placement {
    starts: EntryKey,
    ends: EntryKey,
    in_split: Option<InSplitStatus>,
    epoch: u64,
    id: Id,
}

pub struct PlacementSM {
    placements: HashMap<Id, Placement>,
    starts: BTreeMap<EntryKey, Id>,
    pending_new_ids: BTreeMap<Id, Id>,
}

raft_state_machine! {
    def cmd prepare_split(source: Id)  -> Id | SplitError;
    def cmd start_split(source: Id, dest: Id, mid: Vec<u8>) -> u64 | SplitError;
    def cmd complete_split(source: Id, dest: Id) -> u64 | SplitError;
    def qry locate(id: Vec<u8>) -> QueryResult | QueryError;
}

impl StateMachineCmds for PlacementSM {
    fn prepare_split(&mut self, source: Id) -> Result<Id, SplitError> {
        if !self.placements.contains_key(&source) {
            return Err(SplitError::PlacementNotFound);
        }
        let new_id = Id::rand();
        self.pending_new_ids.insert(new_id, source);
        Ok(new_id)
    }

    fn start_split(&mut self, source: Id, dest: Id, mid: Vec<u8>) -> Result<u64, SplitError> {
        if let Some(mut source_placement) = self.placements.get_mut(&source) {
            if let &Some(ref in_progress) = &source_placement.in_split {
                return Err(SplitError::AnotherSplitInProgress(in_progress.clone()));
            }

            let mid_key: EntryKey = SmallVec::from(mid);
            if mid_key < source_placement.starts || mid_key >= source_placement.ends {
                return Err(SplitError::MidOutOfRange);
            }

            source_placement.in_split = Some(InSplitStatus {
                dest,
                mid: mid_key.into_iter().collect()
            });
            source_placement.epoch += 1;
            Ok(source_placement.epoch)
        } else {
            Err(SplitError::PlacementNotFound)
        }
    }

    fn complete_split(&mut self, source: Id, dest: Id) -> Result<u64, SplitError> {
        let (dest_placement, src_epoch) = if let Some(mut source_placement) = self.placements.get_mut(&source) {
            if let Some(pending_src) = self.pending_new_ids.get(&dest) {
                if pending_src != &source {
                    return Err(SplitError::SplitUnmatchSource)
                }
            } else {
                return Err(SplitError::CannotFindSplitMeta)
            }
            let dest_placement = if let &Some(ref in_progress) = &source_placement.in_split {
                if in_progress.dest != dest {
                    return Err(SplitError::AnotherSplitInProgress(in_progress.clone()));
                }
                let dest_ends = source_placement.ends.clone();
                let source_ends = SmallVec::from(in_progress.mid.clone());
                source_placement.ends = source_ends.clone();
                Placement {
                    starts: source_ends,
                    ends: dest_ends,
                    in_split: None,
                    epoch: 0,
                    id: dest
                }
            } else {
                return Err(SplitError::NoSplitInProgress);
            };
            source_placement.in_split = None;
            source_placement.epoch += 1;
            (dest_placement, source_placement.epoch)
        } else {
            return Err(SplitError::PlacementNotFound)
        };
        self.starts.insert(dest_placement.starts.clone(), dest);
        self.placements.insert(dest, dest_placement);
        self.pending_new_ids.remove(&dest);
        Ok(src_epoch)
    }

    fn locate(&self, entry: Vec<u8>) -> Result<QueryResult, QueryError> {
        let search_key = SmallVec::from(entry);
        if let Some((_, placement_id)) = self.starts.range(..=search_key).last() {
            let placement = self.placements.get(placement_id).unwrap();
            let split = placement.in_split.as_ref().map(|s| s.dest);
            return Ok(QueryResult{
                id: placement.id,
                split,
                epoch: placement.epoch
            })
        } else {
            return Err(QueryError::OutOfRange)
        }
    }
}

impl StateMachineCtl for PlacementSM {
    raft_sm_complete!();
    fn id(&self) -> u64 {
        SM_ID
    }
    fn snapshot(&self) -> Option<Vec<u8>> {
        unimplemented!()
    }
    fn recover(&mut self, data: Vec<u8>) {
        unimplemented!()
    }
}<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use session::config::Options;

use std::io::{self, StdoutLock, Write};
use std::time::Instant;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ProfileCategory {
    Parsing,
    Expansion,
    TypeChecking,
    BorrowChecking,
    Codegen,
    Linking,
    Other,
}

struct Categories<T> {
    parsing: T,
    expansion: T,
    type_checking: T,
    borrow_checking: T,
    codegen: T,
    linking: T,
    other: T,
}

impl<T: Default> Categories<T> {
    fn new() -> Categories<T> {
        Categories {
            parsing: T::default(),
            expansion: T::default(),
            type_checking: T::default(),
            borrow_checking: T::default(),
            codegen: T::default(),
            linking: T::default(),
            other: T::default(),
        }
    }
}

impl<T> Categories<T> {
    fn get(&self, category: ProfileCategory) -> &T {
        match category {
            ProfileCategory::Parsing => &self.parsing,
            ProfileCategory::Expansion => &self.expansion,
            ProfileCategory::TypeChecking => &self.type_checking,
            ProfileCategory::BorrowChecking => &self.borrow_checking,
            ProfileCategory::Codegen => &self.codegen,
            ProfileCategory::Linking => &self.linking,
            ProfileCategory::Other => &self.other,
        }
    }

    fn set(&mut self, category: ProfileCategory, value: T) {
        match category {
            ProfileCategory::Parsing => self.parsing = value,
            ProfileCategory::Expansion => self.expansion = value,
            ProfileCategory::TypeChecking => self.type_checking = value,
            ProfileCategory::BorrowChecking => self.borrow_checking = value,
            ProfileCategory::Codegen => self.codegen = value,
            ProfileCategory::Linking => self.linking = value,
            ProfileCategory::Other => self.other = value,
        }
    }
}

struct CategoryData {
    times: Categories<u64>,
    query_counts: Categories<(u64, u64)>,
}

impl CategoryData {
    fn new() -> CategoryData {
        CategoryData {
            times: Categories::new(),
            query_counts: Categories::new(),
        }
    }

    fn print(&self, lock: &mut StdoutLock) {
        macro_rules! p {
            ($name:tt, $rustic_name:ident) => {
                let (hits, total) = self.query_counts.$rustic_name;
                let (hits, total) = if total > 0 {
                    (format!("{:.2}%", (((hits as f32) / (total as f32)) * 100.0)), total.to_string())
                } else {
                    ("".into(), "".into())
                };

                writeln!(
                   lock,
                   "| {0: <16} | {1: <14} | {2: <14} | {3: <8} |",
                   $name,
                   self.times.$rustic_name / 1_000_000,
                   total,
                   hits
                ).unwrap();
            };
        }

        writeln!(lock, "| Phase            | Time (ms)      | Queries        | Hits (%) |").unwrap();
        writeln!(lock, "| ---------------- | -------------- | -------------- | -------- |").unwrap();

        p!("Parsing", parsing);
        p!("Expansion", expansion);
        p!("TypeChecking", type_checking);
        p!("BorrowChecking", borrow_checking);
        p!("Codegen", codegen);
        p!("Linking", linking);
        p!("Other", other);
    }
}

pub struct SelfProfiler {
    timer_stack: Vec<ProfileCategory>,
    data: CategoryData,
    current_timer: Instant,
}

pub struct ProfilerActivity<'a>(ProfileCategory, &'a mut SelfProfiler);

impl<'a> Drop for ProfilerActivity<'a> {
    fn drop(&mut self) {
        let ProfilerActivity (category, profiler) = self;

        profiler.end_activity(*category);
    }
}

impl SelfProfiler {
    pub fn new() -> SelfProfiler {
        let mut profiler = SelfProfiler {
            timer_stack: Vec::new(),
            data: CategoryData::new(),
            current_timer: Instant::now(),
        };

        profiler.start_activity(ProfileCategory::Other);

        profiler
    }

    pub fn start_activity(&mut self, category: ProfileCategory) {
        match self.timer_stack.last().cloned() {
            None => {
                self.current_timer = Instant::now();
            },
            Some(current_category) if current_category == category => {
                //since the current category is the same as the new activity's category,
                //we don't need to do anything with the timer, we just need to push it on the stack
            }
            Some(current_category) => {
                let elapsed = self.stop_timer();

                //record the current category's time
                let new_time = self.data.times.get(current_category) + elapsed;
                self.data.times.set(current_category, new_time);
            }
        }

        //push the new category
        self.timer_stack.push(category);
    }

    pub fn record_query(&mut self, category: ProfileCategory) {
        let (hits, total) = *self.data.query_counts.get(category);
        self.data.query_counts.set(category, (hits, total + 1));
    }

    pub fn record_query_hit(&mut self, category: ProfileCategory) {
        let (hits, total) = *self.data.query_counts.get(category);
        self.data.query_counts.set(category, (hits + 1, total));
    }

    pub fn end_activity(&mut self, category: ProfileCategory) {
        match self.timer_stack.pop() {
            None => bug!("end_activity() was called but there was no running activity"),
            Some(c) =>
                assert!(
                    c == category,
                    "end_activity() was called but a different activity was running"),
        }

        //check if the new running timer is in the same category as this one
        //if it is, we don't need to do anything
        if let Some(c) = self.timer_stack.last() {
            if *c == category {
                return;
            }
        }

        //the new timer is different than the previous, so record the elapsed time and start a new timer
        let elapsed = self.stop_timer();
        let new_time = self.data.times.get(category) + elapsed;
        self.data.times.set(category, new_time);
    }

    fn stop_timer(&mut self) -> u64 {
        let elapsed = self.current_timer.elapsed();

        self.current_timer = Instant::now();

        (elapsed.as_secs() * 1_000_000_000) + (elapsed.subsec_nanos() as u64)
    }

    pub fn print_results(&mut self, opts: &Options) {
        self.end_activity(ProfileCategory::Other);

        assert!(self.timer_stack.is_empty(), "there were timers running when print_results() was called");

        let out = io::stdout();
        let mut lock = out.lock();

        let crate_name = opts.crate_name.as_ref().map(|n| format!(" for {}", n)).unwrap_or_default();

        writeln!(lock, "Self profiling results{}:", crate_name).unwrap();
        writeln!(lock).unwrap();

        self.data.print(&mut lock);

        writeln!(lock).unwrap();
        writeln!(lock, "Optimization level: {:?}", opts.optimize).unwrap();

        let incremental = if opts.incremental.is_some() { "on" } else { "off" };
        writeln!(lock, "Incremental: {}", incremental).unwrap();
    }

    pub fn record_activity<'a>(&'a mut self, category: ProfileCategory) -> ProfilerActivity<'a> {
        self.start_activity(category);

        ProfilerActivity(category, self)
    }
}<commit_msg>Remove redundant s in output<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use session::config::Options;

use std::io::{self, StdoutLock, Write};
use std::time::Instant;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ProfileCategory {
    Parsing,
    Expansion,
    TypeChecking,
    BorrowChecking,
    Codegen,
    Linking,
    Other,
}

struct Categories<T> {
    parsing: T,
    expansion: T,
    type_checking: T,
    borrow_checking: T,
    codegen: T,
    linking: T,
    other: T,
}

impl<T: Default> Categories<T> {
    fn new() -> Categories<T> {
        Categories {
            parsing: T::default(),
            expansion: T::default(),
            type_checking: T::default(),
            borrow_checking: T::default(),
            codegen: T::default(),
            linking: T::default(),
            other: T::default(),
        }
    }
}

impl<T> Categories<T> {
    fn get(&self, category: ProfileCategory) -> &T {
        match category {
            ProfileCategory::Parsing => &self.parsing,
            ProfileCategory::Expansion => &self.expansion,
            ProfileCategory::TypeChecking => &self.type_checking,
            ProfileCategory::BorrowChecking => &self.borrow_checking,
            ProfileCategory::Codegen => &self.codegen,
            ProfileCategory::Linking => &self.linking,
            ProfileCategory::Other => &self.other,
        }
    }

    fn set(&mut self, category: ProfileCategory, value: T) {
        match category {
            ProfileCategory::Parsing => self.parsing = value,
            ProfileCategory::Expansion => self.expansion = value,
            ProfileCategory::TypeChecking => self.type_checking = value,
            ProfileCategory::BorrowChecking => self.borrow_checking = value,
            ProfileCategory::Codegen => self.codegen = value,
            ProfileCategory::Linking => self.linking = value,
            ProfileCategory::Other => self.other = value,
        }
    }
}

struct CategoryData {
    times: Categories<u64>,
    query_counts: Categories<(u64, u64)>,
}

impl CategoryData {
    fn new() -> CategoryData {
        CategoryData {
            times: Categories::new(),
            query_counts: Categories::new(),
        }
    }

    fn print(&self, lock: &mut StdoutLock) {
        macro_rules! p {
            ($name:tt, $rustic_name:ident) => {
                let (hits, total) = self.query_counts.$rustic_name;
                let (hits, total) = if total > 0 {
                    (format!("{:.2}", (((hits as f32) / (total as f32)) * 100.0)), total.to_string())
                } else {
                    ("".into(), "".into())
                };

                writeln!(
                   lock,
                   "| {0: <16} | {1: <14} | {2: <14} | {3: <8} |",
                   $name,
                   self.times.$rustic_name / 1_000_000,
                   total,
                   hits
                ).unwrap();
            };
        }

        writeln!(lock, "| Phase            | Time (ms)      | Queries        | Hits (%) |").unwrap();
        writeln!(lock, "| ---------------- | -------------- | -------------- | -------- |").unwrap();

        p!("Parsing", parsing);
        p!("Expansion", expansion);
        p!("TypeChecking", type_checking);
        p!("BorrowChecking", borrow_checking);
        p!("Codegen", codegen);
        p!("Linking", linking);
        p!("Other", other);
    }
}

pub struct SelfProfiler {
    timer_stack: Vec<ProfileCategory>,
    data: CategoryData,
    current_timer: Instant,
}

pub struct ProfilerActivity<'a>(ProfileCategory, &'a mut SelfProfiler);

impl<'a> Drop for ProfilerActivity<'a> {
    fn drop(&mut self) {
        let ProfilerActivity (category, profiler) = self;

        profiler.end_activity(*category);
    }
}

impl SelfProfiler {
    pub fn new() -> SelfProfiler {
        let mut profiler = SelfProfiler {
            timer_stack: Vec::new(),
            data: CategoryData::new(),
            current_timer: Instant::now(),
        };

        profiler.start_activity(ProfileCategory::Other);

        profiler
    }

    pub fn start_activity(&mut self, category: ProfileCategory) {
        match self.timer_stack.last().cloned() {
            None => {
                self.current_timer = Instant::now();
            },
            Some(current_category) if current_category == category => {
                //since the current category is the same as the new activity's category,
                //we don't need to do anything with the timer, we just need to push it on the stack
            }
            Some(current_category) => {
                let elapsed = self.stop_timer();

                //record the current category's time
                let new_time = self.data.times.get(current_category) + elapsed;
                self.data.times.set(current_category, new_time);
            }
        }

        //push the new category
        self.timer_stack.push(category);
    }

    pub fn record_query(&mut self, category: ProfileCategory) {
        let (hits, total) = *self.data.query_counts.get(category);
        self.data.query_counts.set(category, (hits, total + 1));
    }

    pub fn record_query_hit(&mut self, category: ProfileCategory) {
        let (hits, total) = *self.data.query_counts.get(category);
        self.data.query_counts.set(category, (hits + 1, total));
    }

    pub fn end_activity(&mut self, category: ProfileCategory) {
        match self.timer_stack.pop() {
            None => bug!("end_activity() was called but there was no running activity"),
            Some(c) =>
                assert!(
                    c == category,
                    "end_activity() was called but a different activity was running"),
        }

        //check if the new running timer is in the same category as this one
        //if it is, we don't need to do anything
        if let Some(c) = self.timer_stack.last() {
            if *c == category {
                return;
            }
        }

        //the new timer is different than the previous, so record the elapsed time and start a new timer
        let elapsed = self.stop_timer();
        let new_time = self.data.times.get(category) + elapsed;
        self.data.times.set(category, new_time);
    }

    fn stop_timer(&mut self) -> u64 {
        let elapsed = self.current_timer.elapsed();

        self.current_timer = Instant::now();

        (elapsed.as_secs() * 1_000_000_000) + (elapsed.subsec_nanos() as u64)
    }

    pub fn print_results(&mut self, opts: &Options) {
        self.end_activity(ProfileCategory::Other);

        assert!(self.timer_stack.is_empty(), "there were timers running when print_results() was called");

        let out = io::stdout();
        let mut lock = out.lock();

        let crate_name = opts.crate_name.as_ref().map(|n| format!(" for {}", n)).unwrap_or_default();

        writeln!(lock, "Self profiling results{}:", crate_name).unwrap();
        writeln!(lock).unwrap();

        self.data.print(&mut lock);

        writeln!(lock).unwrap();
        writeln!(lock, "Optimization level: {:?}", opts.optimize).unwrap();

        let incremental = if opts.incremental.is_some() { "on" } else { "off" };
        writeln!(lock, "Incremental: {}", incremental).unwrap();
    }

    pub fn record_activity<'a>(&'a mut self, category: ProfileCategory) -> ProfilerActivity<'a> {
        self.start_activity(category);

        ProfilerActivity(category, self)
    }
}<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Integer and floating-point number formatting

// FIXME: #6220 Implement floating point formatting

#![allow(unsigned_negate)]

use collections::Collection;
use fmt;
use iter::DoubleEndedIterator;
use num::{Int, cast, zero};
use slice::{ImmutableVector, MutableVector};

#[cfg(stage0)]
use iter::Iterator;         // NOTE(stage0): Remove after snapshot.
#[cfg(stage0)]
use option::{Some, None};   // NOTE(stage0): Remove after snapshot.

/// A type that represents a specific radix
trait GenericRadix {
    /// The number of digits.
    fn base(&self) -> u8;

    /// A radix-specific prefix string.
    fn prefix(&self) -> &'static str { "" }

    /// Converts an integer to corresponding radix digit.
    fn digit(&self, x: u8) -> u8;

    /// Format an integer using the radix using a formatter.
    fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result {
        // The radix can be as low as 2, so we need a buffer of at least 64
        // characters for a base 2 number.
        let mut buf = [0u8, ..64];
        let base = cast(self.base()).unwrap();
        let mut curr = buf.len();
        let is_positive = x >= zero();
        if is_positive {
            // Accumulate each digit of the number from the least significant
            // to the most significant figure.
            for byte in buf.mut_iter().rev() {
                let n = x % base;                         // Get the current place value.
                x = x / base;                             // Deaccumulate the number.
                *byte = self.digit(cast(n).unwrap());     // Store the digit in the buffer.
                curr -= 1;
                if x == zero() { break; }                 // No more digits left to accumulate.
            }
        } else {
            // Do the same as above, but accounting for two's complement.
            for byte in buf.mut_iter().rev() {
                let n = -(x % base);                      // Get the current place value.
                x = x / base;                             // Deaccumulate the number.
                *byte = self.digit(cast(n).unwrap());     // Store the digit in the buffer.
                curr -= 1;
                if x == zero() { break; }                 // No more digits left to accumulate.
            }
        }
        f.pad_integral(is_positive, self.prefix(), buf.slice_from(curr))
    }
}

/// A binary (base 2) radix
#[deriving(Clone, PartialEq)]
struct Binary;

/// An octal (base 8) radix
#[deriving(Clone, PartialEq)]
struct Octal;

/// A decimal (base 10) radix
#[deriving(Clone, PartialEq)]
struct Decimal;

/// A hexadecimal (base 16) radix, formatted with lower-case characters
#[deriving(Clone, PartialEq)]
struct LowerHex;

/// A hexadecimal (base 16) radix, formatted with upper-case characters
#[deriving(Clone, PartialEq)]
pub struct UpperHex;

macro_rules! radix {
    ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
        impl GenericRadix for $T {
            fn base(&self) -> u8 { $base }
            fn prefix(&self) -> &'static str { $prefix }
            fn digit(&self, x: u8) -> u8 {
                match x {
                    $($x => $conv,)+
                    x => fail!("number not in the range 0..{}: {}", self.base() - 1, x),
                }
            }
        }
    }
}

radix!(Binary,    2, "0b", x @  0 .. 2 => '0' as u8 + x)
radix!(Octal,     8, "0o", x @  0 .. 7 => '0' as u8 + x)
radix!(Decimal,  10, "",   x @  0 .. 9 => '0' as u8 + x)
radix!(LowerHex, 16, "0x", x @  0 .. 9 => '0' as u8 + x,
                           x @ 10 ..15 => 'a' as u8 + (x - 10))
radix!(UpperHex, 16, "0x", x @  0 .. 9 => '0' as u8 + x,
                           x @ 10 ..15 => 'A' as u8 + (x - 10))

/// A radix with in the range of `2..36`.
#[deriving(Clone, PartialEq)]
pub struct Radix {
    base: u8,
}

impl Radix {
    fn new(base: u8) -> Radix {
        assert!(2 <= base && base <= 36, "the base must be in the range of 0..36: {}", base);
        Radix { base: base }
    }
}

impl GenericRadix for Radix {
    fn base(&self) -> u8 { self.base }
    fn digit(&self, x: u8) -> u8 {
        match x {
            x @  0 ..9 => '0' as u8 + x,
            x if x < self.base() => 'a' as u8 + (x - 10),
            x => fail!("number not in the range 0..{}: {}", self.base() - 1, x),
        }
    }
}

/// A helper type for formatting radixes.
pub struct RadixFmt<T, R>(T, R);

/// Constructs a radix formatter in the range of `2..36`.
///
/// # Example
///
/// ~~~
/// use std::fmt::radix;
/// assert_eq!(format!("{}", radix(55i, 36)), "1j".to_string());
/// ~~~
pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
    RadixFmt(x, Radix::new(base))
}

macro_rules! radix_fmt {
    ($T:ty as $U:ty, $fmt:ident) => {
        impl fmt::Show for RadixFmt<$T, Radix> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match *self { RadixFmt(ref x, radix) => radix.$fmt(*x as $U, f) }
            }
        }
    }
}
macro_rules! int_base {
    ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
        impl fmt::$Trait for $T {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                $Radix.fmt_int(*self as $U, f)
            }
        }
    }
}
macro_rules! integer {
    ($Int:ident, $Uint:ident) => {
        int_base!(Show     for $Int as $Int   -> Decimal)
        int_base!(Signed   for $Int as $Int   -> Decimal)
        int_base!(Binary   for $Int as $Uint  -> Binary)
        int_base!(Octal    for $Int as $Uint  -> Octal)
        int_base!(LowerHex for $Int as $Uint  -> LowerHex)
        int_base!(UpperHex for $Int as $Uint  -> UpperHex)
        radix_fmt!($Int as $Int, fmt_int)

        int_base!(Show     for $Uint as $Uint -> Decimal)
        int_base!(Unsigned for $Uint as $Uint -> Decimal)
        int_base!(Binary   for $Uint as $Uint -> Binary)
        int_base!(Octal    for $Uint as $Uint -> Octal)
        int_base!(LowerHex for $Uint as $Uint -> LowerHex)
        int_base!(UpperHex for $Uint as $Uint -> UpperHex)
        radix_fmt!($Uint as $Uint, fmt_int)
    }
}
integer!(int, uint)
integer!(i8, u8)
integer!(i16, u16)
integer!(i32, u32)
integer!(i64, u64)
<commit_msg>Fix assert message<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Integer and floating-point number formatting

// FIXME: #6220 Implement floating point formatting

#![allow(unsigned_negate)]

use collections::Collection;
use fmt;
use iter::DoubleEndedIterator;
use num::{Int, cast, zero};
use slice::{ImmutableVector, MutableVector};

#[cfg(stage0)]
use iter::Iterator;         // NOTE(stage0): Remove after snapshot.
#[cfg(stage0)]
use option::{Some, None};   // NOTE(stage0): Remove after snapshot.

/// A type that represents a specific radix
trait GenericRadix {
    /// The number of digits.
    fn base(&self) -> u8;

    /// A radix-specific prefix string.
    fn prefix(&self) -> &'static str { "" }

    /// Converts an integer to corresponding radix digit.
    fn digit(&self, x: u8) -> u8;

    /// Format an integer using the radix using a formatter.
    fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result {
        // The radix can be as low as 2, so we need a buffer of at least 64
        // characters for a base 2 number.
        let mut buf = [0u8, ..64];
        let base = cast(self.base()).unwrap();
        let mut curr = buf.len();
        let is_positive = x >= zero();
        if is_positive {
            // Accumulate each digit of the number from the least significant
            // to the most significant figure.
            for byte in buf.mut_iter().rev() {
                let n = x % base;                         // Get the current place value.
                x = x / base;                             // Deaccumulate the number.
                *byte = self.digit(cast(n).unwrap());     // Store the digit in the buffer.
                curr -= 1;
                if x == zero() { break; }                 // No more digits left to accumulate.
            }
        } else {
            // Do the same as above, but accounting for two's complement.
            for byte in buf.mut_iter().rev() {
                let n = -(x % base);                      // Get the current place value.
                x = x / base;                             // Deaccumulate the number.
                *byte = self.digit(cast(n).unwrap());     // Store the digit in the buffer.
                curr -= 1;
                if x == zero() { break; }                 // No more digits left to accumulate.
            }
        }
        f.pad_integral(is_positive, self.prefix(), buf.slice_from(curr))
    }
}

/// A binary (base 2) radix
#[deriving(Clone, PartialEq)]
struct Binary;

/// An octal (base 8) radix
#[deriving(Clone, PartialEq)]
struct Octal;

/// A decimal (base 10) radix
#[deriving(Clone, PartialEq)]
struct Decimal;

/// A hexadecimal (base 16) radix, formatted with lower-case characters
#[deriving(Clone, PartialEq)]
struct LowerHex;

/// A hexadecimal (base 16) radix, formatted with upper-case characters
#[deriving(Clone, PartialEq)]
pub struct UpperHex;

macro_rules! radix {
    ($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
        impl GenericRadix for $T {
            fn base(&self) -> u8 { $base }
            fn prefix(&self) -> &'static str { $prefix }
            fn digit(&self, x: u8) -> u8 {
                match x {
                    $($x => $conv,)+
                    x => fail!("number not in the range 0..{}: {}", self.base() - 1, x),
                }
            }
        }
    }
}

radix!(Binary,    2, "0b", x @  0 .. 2 => '0' as u8 + x)
radix!(Octal,     8, "0o", x @  0 .. 7 => '0' as u8 + x)
radix!(Decimal,  10, "",   x @  0 .. 9 => '0' as u8 + x)
radix!(LowerHex, 16, "0x", x @  0 .. 9 => '0' as u8 + x,
                           x @ 10 ..15 => 'a' as u8 + (x - 10))
radix!(UpperHex, 16, "0x", x @  0 .. 9 => '0' as u8 + x,
                           x @ 10 ..15 => 'A' as u8 + (x - 10))

/// A radix with in the range of `2..36`.
#[deriving(Clone, PartialEq)]
pub struct Radix {
    base: u8,
}

impl Radix {
    fn new(base: u8) -> Radix {
        assert!(2 <= base && base <= 36, "the base must be in the range of 2..36: {}", base);
        Radix { base: base }
    }
}

impl GenericRadix for Radix {
    fn base(&self) -> u8 { self.base }
    fn digit(&self, x: u8) -> u8 {
        match x {
            x @  0 ..9 => '0' as u8 + x,
            x if x < self.base() => 'a' as u8 + (x - 10),
            x => fail!("number not in the range 0..{}: {}", self.base() - 1, x),
        }
    }
}

/// A helper type for formatting radixes.
pub struct RadixFmt<T, R>(T, R);

/// Constructs a radix formatter in the range of `2..36`.
///
/// # Example
///
/// ~~~
/// use std::fmt::radix;
/// assert_eq!(format!("{}", radix(55i, 36)), "1j".to_string());
/// ~~~
pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
    RadixFmt(x, Radix::new(base))
}

macro_rules! radix_fmt {
    ($T:ty as $U:ty, $fmt:ident) => {
        impl fmt::Show for RadixFmt<$T, Radix> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                match *self { RadixFmt(ref x, radix) => radix.$fmt(*x as $U, f) }
            }
        }
    }
}
macro_rules! int_base {
    ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
        impl fmt::$Trait for $T {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                $Radix.fmt_int(*self as $U, f)
            }
        }
    }
}
macro_rules! integer {
    ($Int:ident, $Uint:ident) => {
        int_base!(Show     for $Int as $Int   -> Decimal)
        int_base!(Signed   for $Int as $Int   -> Decimal)
        int_base!(Binary   for $Int as $Uint  -> Binary)
        int_base!(Octal    for $Int as $Uint  -> Octal)
        int_base!(LowerHex for $Int as $Uint  -> LowerHex)
        int_base!(UpperHex for $Int as $Uint  -> UpperHex)
        radix_fmt!($Int as $Int, fmt_int)

        int_base!(Show     for $Uint as $Uint -> Decimal)
        int_base!(Unsigned for $Uint as $Uint -> Decimal)
        int_base!(Binary   for $Uint as $Uint -> Binary)
        int_base!(Octal    for $Uint as $Uint -> Octal)
        int_base!(LowerHex for $Uint as $Uint -> LowerHex)
        int_base!(UpperHex for $Uint as $Uint -> UpperHex)
        radix_fmt!($Uint as $Uint, fmt_int)
    }
}
integer!(int, uint)
integer!(i8, u8)
integer!(i16, u16)
integer!(i32, u32)
integer!(i64, u64)
<|endoftext|>


<commit_before>//!
//! Generic addressing
//!

use std::{fmt, mem};
use addr::{Address, Integer};

/// A trait for anything that has an address bus and can get/set data. The address (any type that
/// implements the `Address` trait) is 16 bit always. The data that can be get/set is 8 bit.
pub trait Addressable {
    /// Memory read: returns the data at the given address
    fn get<A: Address> (&self, addr: A) -> u8;

    /// Get a type from the given address by copying data bits. This gets a number in host platform
    /// byte order format. Since this unsafely transmutes to the target type, this method is
    /// marked unsafe. For integers, better use `get_be` or `get_le` instead.
    unsafe fn get_type<A: Address, T: Copy> (&self, addr: A) -> T {
        let mut val: T = mem::uninitialized();
        let ptr = &mut val as *mut T as *mut u8;
        for (i, addr) in addr.successive().take(mem::size_of::<T>()).enumerate() {
            *ptr.offset(i as isize) = self.get(addr);
        }
        val
    }

    /// Get a number in big endian format from the given address
    fn get_be<A: Address, T: Integer> (&self, addr: A) -> T {
        unsafe { T::from_be(self.get_type(addr)) }
    }

    /// Get a number in little endian format from the given address
    fn get_le<A: Address, T: Integer> (&self, addr: A) -> T {
        unsafe { T::from_le(self.get_type(addr)) }
    }

    /// Memory write: set the data at the given address
    fn set<A: Address> (&mut self, addr: A, data: u8);

    /// Store a type to the given address by copying data bits. This stores a type in host
    /// platform byte order format. Since this unsafely transmutes the stored type, this method
    /// is marked unsafe. For integers, better use `set_be` or `set_le` instead.
    unsafe fn set_type<A: Address, T: Copy> (&mut self, addr: A, val: T) {
        let ptr = &val as *const T as *const u8;
        for (i, addr) in addr.successive().take(mem::size_of::<T>()).enumerate() {
            self.set(addr, *ptr.offset(i as isize));
        }
    }

    /// Store a number in big endian format to the given address
    fn set_be<A: Address, T: Integer> (&mut self, addr: A, val: T) {
        unsafe { self.set_type(addr, T::to_be(val)); }
    }

    /// Store a number in little endian format to the given address
    fn set_le<A: Address, T: Integer> (&mut self, addr: A, val: T) {
        unsafe { self.set_type(addr, T::to_le(val)); }
    }

    /// Copy data from another addressable source
    fn copy<A1: Address, A2: Address, M: Addressable> (&mut self, self_addr: A1, other: &M, other_addr: A2, size: usize) {
        for (dst_addr, src_addr) in self_addr.successive().zip(other_addr.successive()).take(size) {
            self.set(dst_addr, other.get(src_addr));
        }
    }

    /// Return an object for displaying a hexdump of the given address range
    fn hexdump<A: Address> (&self, addr1: A, addr2: A) -> HexDump<A, Self> {
        HexDump { mem: self, addr1: addr1, addr2: addr2 }
    }
}

/// Helper struct for displaying a hexdump of an address range
pub struct HexDump<'a, A, M: 'a + ?Sized> {
    mem: &'a M,
    addr1: A,
    addr2: A,
}

impl<'a, A: Address, M: Addressable> fmt::Display for HexDump<'a, A, M> {
    fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
        for addr in self.addr1.successive().upto(self.addr2) {
            try!(write!(f, "{:02X} ", self.mem.get(addr)));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use addr::Masked;
    use mem::test::TestMemory;
    use super::*;

    #[test]
    fn get_byte () {
        let data = TestMemory;
        assert_eq!(data.get(0x0012), 0x12);
        assert_eq!(data.get(0x1234), 0x46);
    }

    #[test]
    fn get_big_endian_number () {
        let data = TestMemory;
        assert_eq!(      0x02_u8 , data.get_be(0x0002));
        assert_eq!(      0x54_u8 , data.get_be(0x0054));
        assert_eq!(    0x0203_u16, data.get_be(0x0002));
        assert_eq!(    0x5455_u16, data.get_be(0x0054));
        assert_eq!(0x02030405_u32, data.get_be(0x0002));
        assert_eq!(0x54555657_u32, data.get_be(0x0054));
    }

    #[test]
    fn get_signed_big_endian_number () {
        let data = TestMemory;
        assert_eq!(       0x54_i8 , data.get_be(0x0054));
        assert_eq!(      -0x5b_i8 , data.get_be(0x00a5));
        assert_eq!(     0x5455_i16, data.get_be(0x0054));
        assert_eq!(    -0x5a5a_i16, data.get_be(0x00a5));
        assert_eq!( 0x54555657_i32, data.get_be(0x0054));
        assert_eq!(-0x5a595858_i32, data.get_be(0x00a5));
    }

    #[test]
    fn get_masked_big_endian_number () {
        let data = TestMemory;
        assert_eq!(    0x1112_u16, data.get_be(Masked(0x12ff, 0xff00)));
        assert_eq!(0x10111213_u32, data.get_be(Masked(0x12fe, 0xff00)));
    }

    #[test]
    fn get_little_endian_number () {
        let data = TestMemory;
        assert_eq!(      0x02_u8 , data.get_le(0x0002));
        assert_eq!(      0x54_u8 , data.get_le(0x0054));
        assert_eq!(    0x0302_u16, data.get_le(0x0002));
        assert_eq!(    0x5554_u16, data.get_le(0x0054));
        assert_eq!(0x05040302_u32, data.get_le(0x0002));
        assert_eq!(0x57565554_u32, data.get_le(0x0054));
    }

    #[test]
    fn get_signed_little_endian_number () {
        let data = TestMemory;
        assert_eq!(       0x54_i8 , data.get_le(0x0054));
        assert_eq!(      -0x5b_i8 , data.get_le(0x00a5));
        assert_eq!(     0x5554_i16, data.get_le(0x0054));
        assert_eq!(    -0x595b_i16, data.get_le(0x00a5));
        assert_eq!( 0x57565554_i32, data.get_le(0x0054));
        assert_eq!(-0x5758595b_i32, data.get_le(0x00a5));
    }

    #[test]
    fn get_masked_little_endian_number () {
        let data = TestMemory;
        assert_eq!(    0x1211_u16, data.get_le(Masked(0x12ff, 0xff00)));
        assert_eq!(0x13121110_u32, data.get_le(Masked(0x12fe, 0xff00)));
    }

    #[test]
    fn set_byte () {
        let mut data = TestMemory;
        data.set(0x0012, 0x12);
        data.set(0x1234, 0x46);
    }

    #[test]
    fn set_big_endian_number () {
        let mut data = TestMemory;
        data.set_be(0x0002,       0x02_u8 );
        data.set_be(0x0054,       0x54_u8 );
        data.set_be(0x0002,     0x0203_u16);
        data.set_be(0x0054,     0x5455_u16);
        data.set_be(0x0002, 0x02030405_u32);
        data.set_be(0x0054, 0x54555657_u32);
    }

    #[test]
    fn set_signed_big_endian_number () {
        let mut data = TestMemory;
        data.set_be(0x0054,        0x54_i8 );
        data.set_be(0x00a5,       -0x5b_i8 );
        data.set_be(0x0054,      0x5455_i16);
        data.set_be(0x00a5,     -0x5a5a_i16);
        data.set_be(0x0054,  0x54555657_i32);
        data.set_be(0x00a5, -0x5a595858_i32);
    }

    #[test]
    fn set_masked_big_endian_number () {
        let mut data = TestMemory;
        data.set_be(Masked(0x12ff, 0xff00),     0x1112_u16);
        data.set_be(Masked(0x12fe, 0xff00), 0x10111213_u32);
    }

    #[test]
    fn set_little_endian_number () {
        let mut data = TestMemory;
        data.set_le(0x0002,       0x02_u8 );
        data.set_le(0x0054,       0x54_u8 );
        data.set_le(0x0002,     0x0302_u16);
        data.set_le(0x0054,     0x5554_u16);
        data.set_le(0x0002, 0x05040302_u32);
        data.set_le(0x0054, 0x57565554_u32);
    }

    #[test]
    fn set_signed_little_endian_number () {
        let mut data = TestMemory;
        data.set_le(0x0054,        0x54_i8 );
        data.set_le(0x00a5,       -0x5b_i8 );
        data.set_le(0x0054,      0x5554_i16);
        data.set_le(0x00a5,     -0x595b_i16);
        data.set_le(0x0054,  0x57565554_i32);
        data.set_le(0x00a5, -0x5758595b_i32);
    }

    #[test]
    fn set_masked_little_endian_number () {
        let mut data = TestMemory;
        data.set_le(Masked(0x12ff, 0xff00),     0x1211_u16);
        data.set_le(Masked(0x12fe, 0xff00), 0x13121110_u32);
    }

    #[test]
    fn copying_memory () {
        let data1 = TestMemory;
        let mut data2 = TestMemory;
        data2.copy(0x8000, &data1, 0x0080, 0x0080);
    }

    #[test]
    fn dumping_memory () {
        let data = TestMemory;
        assert_eq!(format!("{}", data.hexdump(0x0100, 0x0100)), "01 ");
        assert_eq!(format!("{}", data.hexdump(0x0100, 0x0101)), "01 02 ");
        assert_eq!(format!("{}", data.hexdump(0x0100, 0x0105)), "01 02 03 04 05 06 ");
    }
}
<commit_msg>Fix trailing space and width formatting of hexdumps<commit_after>//!
//! Generic addressing
//!

use std::{fmt, mem};
use std::fmt::Write;
use addr::{Address, Integer};

/// A trait for anything that has an address bus and can get/set data. The address (any type that
/// implements the `Address` trait) is 16 bit always. The data that can be get/set is 8 bit.
pub trait Addressable {
    /// Memory read: returns the data at the given address
    fn get<A: Address> (&self, addr: A) -> u8;

    /// Get a type from the given address by copying data bits. This gets a number in host platform
    /// byte order format. Since this unsafely transmutes to the target type, this method is
    /// marked unsafe. For integers, better use `get_be` or `get_le` instead.
    unsafe fn get_type<A: Address, T: Copy> (&self, addr: A) -> T {
        let mut val: T = mem::uninitialized();
        let ptr = &mut val as *mut T as *mut u8;
        for (i, addr) in addr.successive().take(mem::size_of::<T>()).enumerate() {
            *ptr.offset(i as isize) = self.get(addr);
        }
        val
    }

    /// Get a number in big endian format from the given address
    fn get_be<A: Address, T: Integer> (&self, addr: A) -> T {
        unsafe { T::from_be(self.get_type(addr)) }
    }

    /// Get a number in little endian format from the given address
    fn get_le<A: Address, T: Integer> (&self, addr: A) -> T {
        unsafe { T::from_le(self.get_type(addr)) }
    }

    /// Memory write: set the data at the given address
    fn set<A: Address> (&mut self, addr: A, data: u8);

    /// Store a type to the given address by copying data bits. This stores a type in host
    /// platform byte order format. Since this unsafely transmutes the stored type, this method
    /// is marked unsafe. For integers, better use `set_be` or `set_le` instead.
    unsafe fn set_type<A: Address, T: Copy> (&mut self, addr: A, val: T) {
        let ptr = &val as *const T as *const u8;
        for (i, addr) in addr.successive().take(mem::size_of::<T>()).enumerate() {
            self.set(addr, *ptr.offset(i as isize));
        }
    }

    /// Store a number in big endian format to the given address
    fn set_be<A: Address, T: Integer> (&mut self, addr: A, val: T) {
        unsafe { self.set_type(addr, T::to_be(val)); }
    }

    /// Store a number in little endian format to the given address
    fn set_le<A: Address, T: Integer> (&mut self, addr: A, val: T) {
        unsafe { self.set_type(addr, T::to_le(val)); }
    }

    /// Copy data from another addressable source
    fn copy<A1: Address, A2: Address, M: Addressable> (&mut self, self_addr: A1, other: &M, other_addr: A2, size: usize) {
        for (dst_addr, src_addr) in self_addr.successive().zip(other_addr.successive()).take(size) {
            self.set(dst_addr, other.get(src_addr));
        }
    }

    /// Return an object for displaying a hexdump of the given address range
    fn hexdump<A: Address> (&self, addr1: A, addr2: A) -> HexDump<A, Self> {
        HexDump { mem: self, addr1: addr1, addr2: addr2 }
    }
}

/// Helper struct for displaying a hexdump of an address range
pub struct HexDump<'a, A, M: 'a + ?Sized> {
    mem: &'a M,
    addr1: A,
    addr2: A,
}

impl<'a, A: Address, M: Addressable> fmt::Display for HexDump<'a, A, M> {
    fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str = String::new();
        for addr in self.addr1.successive().upto(self.addr2) {
            try!(write!(str, "{:02X}", self.mem.get(addr)));
            if addr != self.addr2 { try!(write!(str, " ")); }
        }
        str.fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use addr::Masked;
    use mem::test::TestMemory;
    use super::*;

    #[test]
    fn get_byte () {
        let data = TestMemory;
        assert_eq!(data.get(0x0012), 0x12);
        assert_eq!(data.get(0x1234), 0x46);
    }

    #[test]
    fn get_big_endian_number () {
        let data = TestMemory;
        assert_eq!(      0x02_u8 , data.get_be(0x0002));
        assert_eq!(      0x54_u8 , data.get_be(0x0054));
        assert_eq!(    0x0203_u16, data.get_be(0x0002));
        assert_eq!(    0x5455_u16, data.get_be(0x0054));
        assert_eq!(0x02030405_u32, data.get_be(0x0002));
        assert_eq!(0x54555657_u32, data.get_be(0x0054));
    }

    #[test]
    fn get_signed_big_endian_number () {
        let data = TestMemory;
        assert_eq!(       0x54_i8 , data.get_be(0x0054));
        assert_eq!(      -0x5b_i8 , data.get_be(0x00a5));
        assert_eq!(     0x5455_i16, data.get_be(0x0054));
        assert_eq!(    -0x5a5a_i16, data.get_be(0x00a5));
        assert_eq!( 0x54555657_i32, data.get_be(0x0054));
        assert_eq!(-0x5a595858_i32, data.get_be(0x00a5));
    }

    #[test]
    fn get_masked_big_endian_number () {
        let data = TestMemory;
        assert_eq!(    0x1112_u16, data.get_be(Masked(0x12ff, 0xff00)));
        assert_eq!(0x10111213_u32, data.get_be(Masked(0x12fe, 0xff00)));
    }

    #[test]
    fn get_little_endian_number () {
        let data = TestMemory;
        assert_eq!(      0x02_u8 , data.get_le(0x0002));
        assert_eq!(      0x54_u8 , data.get_le(0x0054));
        assert_eq!(    0x0302_u16, data.get_le(0x0002));
        assert_eq!(    0x5554_u16, data.get_le(0x0054));
        assert_eq!(0x05040302_u32, data.get_le(0x0002));
        assert_eq!(0x57565554_u32, data.get_le(0x0054));
    }

    #[test]
    fn get_signed_little_endian_number () {
        let data = TestMemory;
        assert_eq!(       0x54_i8 , data.get_le(0x0054));
        assert_eq!(      -0x5b_i8 , data.get_le(0x00a5));
        assert_eq!(     0x5554_i16, data.get_le(0x0054));
        assert_eq!(    -0x595b_i16, data.get_le(0x00a5));
        assert_eq!( 0x57565554_i32, data.get_le(0x0054));
        assert_eq!(-0x5758595b_i32, data.get_le(0x00a5));
    }

    #[test]
    fn get_masked_little_endian_number () {
        let data = TestMemory;
        assert_eq!(    0x1211_u16, data.get_le(Masked(0x12ff, 0xff00)));
        assert_eq!(0x13121110_u32, data.get_le(Masked(0x12fe, 0xff00)));
    }

    #[test]
    fn set_byte () {
        let mut data = TestMemory;
        data.set(0x0012, 0x12);
        data.set(0x1234, 0x46);
    }

    #[test]
    fn set_big_endian_number () {
        let mut data = TestMemory;
        data.set_be(0x0002,       0x02_u8 );
        data.set_be(0x0054,       0x54_u8 );
        data.set_be(0x0002,     0x0203_u16);
        data.set_be(0x0054,     0x5455_u16);
        data.set_be(0x0002, 0x02030405_u32);
        data.set_be(0x0054, 0x54555657_u32);
    }

    #[test]
    fn set_signed_big_endian_number () {
        let mut data = TestMemory;
        data.set_be(0x0054,        0x54_i8 );
        data.set_be(0x00a5,       -0x5b_i8 );
        data.set_be(0x0054,      0x5455_i16);
        data.set_be(0x00a5,     -0x5a5a_i16);
        data.set_be(0x0054,  0x54555657_i32);
        data.set_be(0x00a5, -0x5a595858_i32);
    }

    #[test]
    fn set_masked_big_endian_number () {
        let mut data = TestMemory;
        data.set_be(Masked(0x12ff, 0xff00),     0x1112_u16);
        data.set_be(Masked(0x12fe, 0xff00), 0x10111213_u32);
    }

    #[test]
    fn set_little_endian_number () {
        let mut data = TestMemory;
        data.set_le(0x0002,       0x02_u8 );
        data.set_le(0x0054,       0x54_u8 );
        data.set_le(0x0002,     0x0302_u16);
        data.set_le(0x0054,     0x5554_u16);
        data.set_le(0x0002, 0x05040302_u32);
        data.set_le(0x0054, 0x57565554_u32);
    }

    #[test]
    fn set_signed_little_endian_number () {
        let mut data = TestMemory;
        data.set_le(0x0054,        0x54_i8 );
        data.set_le(0x00a5,       -0x5b_i8 );
        data.set_le(0x0054,      0x5554_i16);
        data.set_le(0x00a5,     -0x595b_i16);
        data.set_le(0x0054,  0x57565554_i32);
        data.set_le(0x00a5, -0x5758595b_i32);
    }

    #[test]
    fn set_masked_little_endian_number () {
        let mut data = TestMemory;
        data.set_le(Masked(0x12ff, 0xff00),     0x1211_u16);
        data.set_le(Masked(0x12fe, 0xff00), 0x13121110_u32);
    }

    #[test]
    fn copying_memory () {
        let data1 = TestMemory;
        let mut data2 = TestMemory;
        data2.copy(0x8000, &data1, 0x0080, 0x0080);
    }

    #[test]
    fn dumping_memory () {
        let data = TestMemory;
        assert_eq!(format!("{}", data.hexdump(0x0100, 0x0100)), "01");
        assert_eq!(format!("{}", data.hexdump(0x0100, 0x0101)), "01 02");
        assert_eq!(format!("{}", data.hexdump(0x0100, 0x0103)), "01 02 03 04");
        assert_eq!(format!("{:16}", data.hexdump(0x0100, 0x0103)), "01 02 03 04     ");
        assert_eq!(format!("{:>16}", data.hexdump(0x0100, 0x0103)), "     01 02 03 04");
    }
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{
        function_parameters::{async_param_to_remove, CParameter},
        functions::{find_function, find_index_to_ignore, finish_function_name},
        imports::Imports,
        out_parameters::use_function_return_for_result,
        ref_mode::RefMode,
        rust_type::RustType,
    },
    config,
    consts::TYPE_PARAMETERS_START,
    env::Env,
    library::{
        Class, Concurrency, Function, Fundamental, Nullable, ParameterDirection, Type, TypeId,
    },
    traits::IntoString,
};
use std::{collections::vec_deque::VecDeque, slice::Iter};

#[derive(Clone, Eq, Debug, PartialEq)]
pub enum BoundType {
    NoWrapper,
    // lifetime
    IsA(Option<char>),
    // lifetime <- shouldn't be used but just in case...
    AsRef(Option<char>),
}

impl BoundType {
    pub fn need_isa(&self) -> bool {
        matches!(*self, BoundType::IsA(_))
    }
}

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Bound {
    pub bound_type: BoundType,
    pub parameter_name: String,
    pub alias: char,
    pub type_str: String,
    pub callback_modified: bool,
}

#[derive(Clone, Debug)]
pub struct Bounds {
    unused: VecDeque<char>,
    used: Vec<Bound>,
    unused_lifetimes: VecDeque<char>,
    lifetimes: Vec<char>,
}

impl Default for Bounds {
    fn default() -> Bounds {
        Bounds {
            unused: (TYPE_PARAMETERS_START as u8..)
                .take_while(|x| *x <= b'Z')
                .map(|x| x as char)
                .collect(),
            used: Vec::new(),
            unused_lifetimes: "abcdefg".chars().collect(),
            lifetimes: Vec::new(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CallbackInfo {
    pub callback_type: String,
    pub success_parameters: String,
    pub error_parameters: String,
    pub bound_name: char,
}

impl Bounds {
    pub fn add_for_parameter(
        &mut self,
        env: &Env,
        func: &Function,
        par: &CParameter,
        r#async: bool,
        concurrency: Concurrency,
        configured_functions: &[&config::functions::Function],
    ) -> (Option<String>, Option<CallbackInfo>) {
        let type_name = RustType::builder(env, par.typ)
            .ref_mode(RefMode::ByRefFake)
            .try_build();
        if (r#async && async_param_to_remove(&par.name)) || type_name.is_err() {
            return (None, None);
        }
        let mut type_string = type_name.into_string();
        let mut callback_info = None;
        let mut ret = None;
        let mut need_is_into_check = false;

        if !par.instance_parameter && par.direction != ParameterDirection::Out {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
                if r#async && (par.name == "callback" || par.name.ends_with("_callback")) {
                    let func_name = func.c_identifier.as_ref().unwrap();
                    let finish_func_name = finish_function_name(func_name);
                    if let Some(function) = find_function(env, &finish_func_name) {
                        // FIXME: This should work completely based on the analysis of the finish() function
                        // but that a) happens afterwards and b) is not accessible from here either.
                        let mut out_parameters =
                            find_out_parameters(env, function, configured_functions);
                        if use_function_return_for_result(
                            env,
                            function.ret.typ,
                            &func.name,
                            configured_functions,
                        ) {
                            out_parameters.insert(
                                0,
                                RustType::builder(env, function.ret.typ)
                                    .direction(function.ret.direction)
                                    .nullable(function.ret.nullable)
                                    .try_build()
                                    .into_string(),
                            );
                        }
                        let parameters = format_out_parameters(&out_parameters);
                        let error_type = find_error_type(env, function);
                        type_string = format!(
                            "FnOnce(Result<{}, {}>) + Send + 'static",
                            parameters, error_type
                        );
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: parameters,
                            error_parameters: error_type,
                            bound_name,
                        });
                    }
                } else if par.c_type == "GDestroyNotify" || env.library.type_(par.typ).is_function()
                {
                    need_is_into_check = par.c_type != "GDestroyNotify";
                    if let Type::Function(_) = env.library.type_(par.typ) {
                        type_string = RustType::builder(env, par.typ)
                            .direction(par.direction)
                            .scope(par.scope)
                            .concurrency(concurrency)
                            .try_from_glib(&par.try_from_glib)
                            .try_build()
                            .into_string();
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: String::new(),
                            error_parameters: String::new(),
                            bound_name,
                        });
                    }
                }
                if (!need_is_into_check || !*par.nullable)
                    && par.c_type != "GDestroyNotify"
                    && !self.add_parameter(&par.name, &type_string, bound_type, r#async)
                {
                    panic!(
                        "Too many type constraints for {}",
                        func.c_identifier.as_ref().unwrap()
                    )
                }
            }
        } else if par.instance_parameter {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
            }
        }

        (ret, callback_info)
    }

    pub fn type_for(env: &Env, type_id: TypeId, nullable: Nullable) -> Option<BoundType> {
        use self::BoundType::*;
        match env.library.type_(type_id) {
            Type::Fundamental(Fundamental::Filename) => Some(AsRef(None)),
            Type::Fundamental(Fundamental::OsString) => Some(AsRef(None)),
            Type::Fundamental(Fundamental::Utf8) if *nullable => None,
            Type::Class(Class {
                final_type: true, ..
            }) => None,
            Type::Class(Class {
                final_type: false, ..
            }) => Some(IsA(None)),
            Type::Interface(..) => Some(IsA(None)),
            Type::List(_) | Type::SList(_) | Type::CArray(_) => None,
            Type::Fundamental(_) if *nullable => None,
            Type::Function(_) => Some(NoWrapper),
            _ => None,
        }
    }

    fn get_to_glib_extra(bound_type: &BoundType) -> String {
        use self::BoundType::*;
        match bound_type {
            AsRef(_) => ".as_ref()".to_owned(),
            IsA(_) => ".as_ref()".to_owned(),
            _ => String::new(),
        }
    }

    pub fn add_parameter(
        &mut self,
        name: &str,
        type_str: &str,
        bound_type: BoundType,
        r#async: bool,
    ) -> bool {
        if r#async && name == "callback" {
            if let Some(alias) = self.unused.pop_front() {
                self.used.push(Bound {
                    bound_type: BoundType::NoWrapper,
                    parameter_name: name.to_owned(),
                    alias,
                    type_str: type_str.to_string(),
                    callback_modified: false,
                });
                return true;
            }
            return false;
        }
        if self.used.iter().any(|n| n.parameter_name == name) {
            return false;
        }
        if let Some(alias) = self.unused.pop_front() {
            self.used.push(Bound {
                bound_type,
                parameter_name: name.to_owned(),
                alias,
                type_str: type_str.to_owned(),
                callback_modified: false,
            });
            true
        } else {
            false
        }
    }

    pub fn get_parameter_alias_info(&self, name: &str) -> Option<(char, BoundType)> {
        self.used
            .iter()
            .find(|n| n.parameter_name == name)
            .map(|t| (t.alias, t.bound_type.clone()))
    }

    pub fn update_imports(&self, imports: &mut Imports) {
        //TODO: import with versions
        use self::BoundType::*;
        for used in &self.used {
            match used.bound_type {
                NoWrapper => (),
                IsA(_) => imports.add("glib::object::IsA"),
                AsRef(_) => imports.add_used_type(&used.type_str),
            }
        }
    }

    pub fn is_empty(&self) -> bool {
        self.used.is_empty()
    }

    pub fn iter(&self) -> Iter<'_, Bound> {
        self.used.iter()
    }

    pub fn iter_lifetimes(&self) -> Iter<'_, char> {
        self.lifetimes.iter()
    }
}

#[derive(Clone, Debug)]
pub struct PropertyBound {
    pub alias: char,
    pub type_str: String,
}

impl PropertyBound {
    pub fn get(env: &Env, type_id: TypeId) -> Option<PropertyBound> {
        let type_ = env.type_(type_id);
        if type_.is_final_type() {
            return None;
        }
        Some(PropertyBound {
            alias: TYPE_PARAMETERS_START,
            type_str: RustType::builder(env, type_id)
                .ref_mode(RefMode::ByRefFake)
                .try_build()
                .into_string(),
        })
    }
}

fn find_out_parameters(
    env: &Env,
    function: &Function,
    configured_functions: &[&config::functions::Function],
) -> Vec<String> {
    let index_to_ignore = find_index_to_ignore(&function.parameters, Some(&function.ret));
    function
        .parameters
        .iter()
        .enumerate()
        .filter(|&(index, param)| {
            Some(index) != index_to_ignore
                && param.direction == ParameterDirection::Out
                && param.name != "error"
        })
        .map(|(_, param)| {
            // FIXME: This should work completely based on the analysis of the finish() function
            // but that a) happens afterwards and b) is not accessible from here either.
            let nullable = configured_functions
                .iter()
                .find_map(|f| f.parameters.iter().find_map(|p| p.nullable))
                .unwrap_or(param.nullable);

            RustType::builder(env, param.typ)
                .direction(param.direction)
                .nullable(nullable)
                .try_build()
                .into_string()
        })
        .collect()
}

fn format_out_parameters(parameters: &[String]) -> String {
    if parameters.len() == 1 {
        parameters[0].to_string()
    } else {
        format!("({})", parameters.join(", "))
    }
}

fn find_error_type(env: &Env, function: &Function) -> String {
    let error_param = function
        .parameters
        .iter()
        .find(|param| param.direction == ParameterDirection::Out && param.name == "error")
        .expect("error type");
    if let Type::Record(_) = env.type_(error_param.typ) {
        return RustType::builder(env, error_param.typ)
            .direction(error_param.direction)
            .try_build()
            .into_string();
    }
    panic!("cannot find error type")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn get_new_all() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        assert_eq!(bounds.add_parameter("a", "", typ.clone(), false), true);
        // Don't add second time
        assert_eq!(bounds.add_parameter("a", "", typ.clone(), false), false);
        assert_eq!(bounds.add_parameter("b", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("c", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("d", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("e", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("f", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("g", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("h", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("h", "", typ.clone(), false), false);
        assert_eq!(bounds.add_parameter("i", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("j", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("k", "", typ.clone(), false), true);
        assert_eq!(bounds.add_parameter("l", "", typ, false), false);
    }

    #[test]
    fn get_parameter_alias_info() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        bounds.add_parameter("b", "", typ.clone(), false);
        assert_eq!(
            bounds.get_parameter_alias_info("a"),
            Some(('P', typ.clone()))
        );
        assert_eq!(bounds.get_parameter_alias_info("b"), Some(('Q', typ)));
        assert_eq!(bounds.get_parameter_alias_info("c"), None);
    }
}
<commit_msg>analysis/bounds: Panic if all type parameters are exhausted<commit_after>use crate::{
    analysis::{
        function_parameters::{async_param_to_remove, CParameter},
        functions::{find_function, find_index_to_ignore, finish_function_name},
        imports::Imports,
        out_parameters::use_function_return_for_result,
        ref_mode::RefMode,
        rust_type::RustType,
    },
    config,
    consts::TYPE_PARAMETERS_START,
    env::Env,
    library::{
        Class, Concurrency, Function, Fundamental, Nullable, ParameterDirection, Type, TypeId,
    },
    traits::IntoString,
};
use std::{collections::vec_deque::VecDeque, slice::Iter};

#[derive(Clone, Eq, Debug, PartialEq)]
pub enum BoundType {
    NoWrapper,
    // lifetime
    IsA(Option<char>),
    // lifetime <- shouldn't be used but just in case...
    AsRef(Option<char>),
}

impl BoundType {
    pub fn need_isa(&self) -> bool {
        matches!(*self, BoundType::IsA(_))
    }
}

#[derive(Clone, Eq, Debug, PartialEq)]
pub struct Bound {
    pub bound_type: BoundType,
    pub parameter_name: String,
    pub alias: char,
    pub type_str: String,
    pub callback_modified: bool,
}

#[derive(Clone, Debug)]
pub struct Bounds {
    unused: VecDeque<char>,
    used: Vec<Bound>,
    unused_lifetimes: VecDeque<char>,
    lifetimes: Vec<char>,
}

impl Default for Bounds {
    fn default() -> Bounds {
        Bounds {
            unused: (TYPE_PARAMETERS_START..)
                .take_while(|x| *x <= 'Z')
                .collect(),
            used: Vec::new(),
            unused_lifetimes: "abcdefg".chars().collect(),
            lifetimes: Vec::new(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CallbackInfo {
    pub callback_type: String,
    pub success_parameters: String,
    pub error_parameters: String,
    pub bound_name: char,
}

impl Bounds {
    pub fn add_for_parameter(
        &mut self,
        env: &Env,
        func: &Function,
        par: &CParameter,
        r#async: bool,
        concurrency: Concurrency,
        configured_functions: &[&config::functions::Function],
    ) -> (Option<String>, Option<CallbackInfo>) {
        let type_name = RustType::builder(env, par.typ)
            .ref_mode(RefMode::ByRefFake)
            .try_build();
        if (r#async && async_param_to_remove(&par.name)) || type_name.is_err() {
            return (None, None);
        }
        let mut type_string = type_name.into_string();
        let mut callback_info = None;
        let mut ret = None;
        let mut need_is_into_check = false;

        if !par.instance_parameter && par.direction != ParameterDirection::Out {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
                if r#async && (par.name == "callback" || par.name.ends_with("_callback")) {
                    let func_name = func.c_identifier.as_ref().unwrap();
                    let finish_func_name = finish_function_name(func_name);
                    if let Some(function) = find_function(env, &finish_func_name) {
                        // FIXME: This should work completely based on the analysis of the finish() function
                        // but that a) happens afterwards and b) is not accessible from here either.
                        let mut out_parameters =
                            find_out_parameters(env, function, configured_functions);
                        if use_function_return_for_result(
                            env,
                            function.ret.typ,
                            &func.name,
                            configured_functions,
                        ) {
                            out_parameters.insert(
                                0,
                                RustType::builder(env, function.ret.typ)
                                    .direction(function.ret.direction)
                                    .nullable(function.ret.nullable)
                                    .try_build()
                                    .into_string(),
                            );
                        }
                        let parameters = format_out_parameters(&out_parameters);
                        let error_type = find_error_type(env, function);
                        type_string = format!(
                            "FnOnce(Result<{}, {}>) + Send + 'static",
                            parameters, error_type
                        );
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: parameters,
                            error_parameters: error_type,
                            bound_name,
                        });
                    }
                } else if par.c_type == "GDestroyNotify" || env.library.type_(par.typ).is_function()
                {
                    need_is_into_check = par.c_type != "GDestroyNotify";
                    if let Type::Function(_) = env.library.type_(par.typ) {
                        type_string = RustType::builder(env, par.typ)
                            .direction(par.direction)
                            .scope(par.scope)
                            .concurrency(concurrency)
                            .try_from_glib(&par.try_from_glib)
                            .try_build()
                            .into_string();
                        let bound_name = *self.unused.front().unwrap();
                        callback_info = Some(CallbackInfo {
                            callback_type: type_string.clone(),
                            success_parameters: String::new(),
                            error_parameters: String::new(),
                            bound_name,
                        });
                    }
                }
                if (!need_is_into_check || !*par.nullable) && par.c_type != "GDestroyNotify" {
                    self.add_parameter(&par.name, &type_string, bound_type, r#async)
                }
            }
        } else if par.instance_parameter {
            if let Some(bound_type) = Bounds::type_for(env, par.typ, par.nullable) {
                ret = Some(Bounds::get_to_glib_extra(&bound_type));
            }
        }

        (ret, callback_info)
    }

    pub fn type_for(env: &Env, type_id: TypeId, nullable: Nullable) -> Option<BoundType> {
        use self::BoundType::*;
        match env.library.type_(type_id) {
            Type::Fundamental(Fundamental::Filename) => Some(AsRef(None)),
            Type::Fundamental(Fundamental::OsString) => Some(AsRef(None)),
            Type::Fundamental(Fundamental::Utf8) if *nullable => None,
            Type::Class(Class {
                final_type: true, ..
            }) => None,
            Type::Class(Class {
                final_type: false, ..
            }) => Some(IsA(None)),
            Type::Interface(..) => Some(IsA(None)),
            Type::List(_) | Type::SList(_) | Type::CArray(_) => None,
            Type::Fundamental(_) if *nullable => None,
            Type::Function(_) => Some(NoWrapper),
            _ => None,
        }
    }

    fn get_to_glib_extra(bound_type: &BoundType) -> String {
        use self::BoundType::*;
        match bound_type {
            AsRef(_) => ".as_ref()".to_owned(),
            IsA(_) => ".as_ref()".to_owned(),
            _ => String::new(),
        }
    }

    pub fn add_parameter(
        &mut self,
        name: &str,
        type_str: &str,
        mut bound_type: BoundType,
        r#async: bool,
    ) {
        if r#async && name == "callback" {
            bound_type = BoundType::NoWrapper;
        }
        if self.used.iter().any(|n| n.parameter_name == name) {
            return;
        }
        let alias = self.unused.pop_front().expect("No free type aliases!");
        self.used.push(Bound {
            bound_type,
            parameter_name: name.to_owned(),
            alias,
            type_str: type_str.to_owned(),
            callback_modified: false,
        });
    }

    pub fn get_parameter_alias_info(&self, name: &str) -> Option<(char, BoundType)> {
        self.used
            .iter()
            .find(|n| n.parameter_name == name)
            .map(|t| (t.alias, t.bound_type.clone()))
    }

    pub fn update_imports(&self, imports: &mut Imports) {
        //TODO: import with versions
        use self::BoundType::*;
        for used in &self.used {
            match used.bound_type {
                NoWrapper => (),
                IsA(_) => imports.add("glib::object::IsA"),
                AsRef(_) => imports.add_used_type(&used.type_str),
            }
        }
    }

    pub fn is_empty(&self) -> bool {
        self.used.is_empty()
    }

    pub fn iter(&self) -> Iter<'_, Bound> {
        self.used.iter()
    }

    pub fn iter_lifetimes(&self) -> Iter<'_, char> {
        self.lifetimes.iter()
    }
}

#[derive(Clone, Debug)]
pub struct PropertyBound {
    pub alias: char,
    pub type_str: String,
}

impl PropertyBound {
    pub fn get(env: &Env, type_id: TypeId) -> Option<PropertyBound> {
        let type_ = env.type_(type_id);
        if type_.is_final_type() {
            return None;
        }
        Some(PropertyBound {
            alias: TYPE_PARAMETERS_START,
            type_str: RustType::builder(env, type_id)
                .ref_mode(RefMode::ByRefFake)
                .try_build()
                .into_string(),
        })
    }
}

fn find_out_parameters(
    env: &Env,
    function: &Function,
    configured_functions: &[&config::functions::Function],
) -> Vec<String> {
    let index_to_ignore = find_index_to_ignore(&function.parameters, Some(&function.ret));
    function
        .parameters
        .iter()
        .enumerate()
        .filter(|&(index, param)| {
            Some(index) != index_to_ignore
                && param.direction == ParameterDirection::Out
                && param.name != "error"
        })
        .map(|(_, param)| {
            // FIXME: This should work completely based on the analysis of the finish() function
            // but that a) happens afterwards and b) is not accessible from here either.
            let nullable = configured_functions
                .iter()
                .find_map(|f| f.parameters.iter().find_map(|p| p.nullable))
                .unwrap_or(param.nullable);

            RustType::builder(env, param.typ)
                .direction(param.direction)
                .nullable(nullable)
                .try_build()
                .into_string()
        })
        .collect()
}

fn format_out_parameters(parameters: &[String]) -> String {
    if parameters.len() == 1 {
        parameters[0].to_string()
    } else {
        format!("({})", parameters.join(", "))
    }
}

fn find_error_type(env: &Env, function: &Function) -> String {
    let error_param = function
        .parameters
        .iter()
        .find(|param| param.direction == ParameterDirection::Out && param.name == "error")
        .expect("error type");
    if let Type::Record(_) = env.type_(error_param.typ) {
        return RustType::builder(env, error_param.typ)
            .direction(error_param.direction)
            .try_build()
            .into_string();
    }
    panic!("cannot find error type")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn get_new_all() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 1);
        // Don't add second time
        bounds.add_parameter("a", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 1);
        bounds.add_parameter("b", "", typ.clone(), false);
        bounds.add_parameter("c", "", typ.clone(), false);
        bounds.add_parameter("d", "", typ.clone(), false);
        bounds.add_parameter("e", "", typ.clone(), false);
        bounds.add_parameter("f", "", typ.clone(), false);
        bounds.add_parameter("g", "", typ.clone(), false);
        bounds.add_parameter("h", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 8);
        bounds.add_parameter("h", "", typ.clone(), false);
        assert_eq!(bounds.iter().len(), 8);
        bounds.add_parameter("i", "", typ.clone(), false);
        bounds.add_parameter("j", "", typ.clone(), false);
        bounds.add_parameter("k", "", typ, false);
    }

    #[test]
    #[should_panic(expected = "No free type aliases!")]
    fn exhaust_type_parameters() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::NoWrapper;
        for c in 'a'..='l' {
            // Should panic on `l` because all type parameters are exhausted
            bounds.add_parameter(c.to_string().as_str(), "", typ.clone(), false);
        }
    }

    #[test]
    fn get_parameter_alias_info() {
        let mut bounds: Bounds = Default::default();
        let typ = BoundType::IsA(None);
        bounds.add_parameter("a", "", typ.clone(), false);
        bounds.add_parameter("b", "", typ.clone(), false);
        assert_eq!(
            bounds.get_parameter_alias_info("a"),
            Some(('P', typ.clone()))
        );
        assert_eq!(bounds.get_parameter_alias_info("b"), Some(('Q', typ)));
        assert_eq!(bounds.get_parameter_alias_info("c"), None);
    }
}
<|endoftext|>