<commit_before>//! Sparkle is a JIT-based renderer that uses llvm to compile effects as they
//! are loaded.
//! Other than the JIT aspect, it is mostly a literal reimplementation of
//! the reference renderer.

use std::collections::HashMap;
use std::ffi::CString;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;

use jagged_array::Jagged2;
use llvm;
use llvm::{Builder, Context, ContextType, ExecutionEngine, Function, Module};
use llvm_sys;
use llvm_sys::core::{
    LLVMContextCreate,
    LLVMContextDispose,
    LLVMDisposeModule,
    LLVMModuleCreateWithNameInContext,
    LLVMStructCreateNamed,
    LLVMStructSetBody,
};
use llvm_sys::prelude::*;
use llvm_sys::target::{LLVM_InitializeNativeTarget, LLVM_InitializeNativeAsmPrinter,
                   LLVM_InitializeNativeAsmParser};
use ndarray::Array2;
use streaming_iterator::StreamingIterator;

use render::Renderer;
use resman::AudioBuffer;
use routing::{Edge, Effect, GraphWatcher, NodeData, NodeHandle, RouteGraph};
use routing::effect::{PrimitiveEffect, EffectData};

#[derive(Debug)]
pub struct SparkleRenderer {
    // Top-level node map
    nodes: NodeMap,
    /// inputs[slot][sample] represents the value of the external input at
    /// the given slot and time. On OOB, it is assumed to be 0.
    inputs: Vec<Vec<f32>>,
    /// Next expected sample to be queried.
    /// This is tracked because if we do a seek, the inputs need to be zero'd.
    head: u64,
    // LLVM data below
    /// Llvm execution engines. Zipped against the modules.
    llvm_engines: Vec<ExecutionEngine>,
    /// Finalized llvm modules.
    llvm_modules: Vec<Module>,
    /// Module that has yet to be compiled.
    open_module: Option<Module>,
    /// LLVM struct { fn(time, slot, callback_type*)->f32, callback_type* }
    /// Used to pass callbak functions into get_output() to allow effects to access their inputs.
    callback_type: LLVMTypeRef,
    /// LLVM type for fn(time, slot, input_getter: callback_type*) -> f32
    sample_getter_type: LLVMTypeRef,
    /// Object that lets us build IR into basic blocks.
    llvm_builder: Builder,
    // NOTE: LLVM Context must be last member, otherwise jemalloc will try dropping
    // llvm-owned data
    /// Object that provides a context for LLVM calls.
    llvm_ctx: Context,
}

#[derive(Debug, Default)]
struct NodeMap {
    nodes: HashMap<NodeHandle, Node>,
    output_edges: Vec<Option<Edge>>,
}

#[derive(Debug)]
struct Node {
    data: MyNodeData,
    /// Inbound edges, indexed by slot idx.
    inbound: Vec<Option<Edge>>,
}

#[derive(Debug)]
enum MyNodeData {
    /// The node corresponds to a LLVM function with the provided name.
    LlvmFunc(String),
    /// External audio.
    Buffer(AudioBuffer),
}

struct CallbackType {
    input_getter: *const fn(u64, u32, *const CallbackType) -> f32,
    userdata: *const CallbackType,
}

impl Renderer for SparkleRenderer {
    fn fill_buffer(&mut self, buff: &mut Array2<f32>, idx: u64, inputs: Jagged2<f32>) {
        let (n_slots, n_times) = buff.dim().into();
        // Store inputs for future use
        {
            // If this is a seek operation, forget historical input values.
            if idx != self.head {
                for slot in &mut self.inputs {
                    *slot = Vec::new();
                    // NB: Improper indexing on 32-bit OS, but will OOM first.
                    slot.resize(idx as usize, 0f32);
                }
            }
            // Make sure we have storage space for all inputs.
            while self.inputs.len() < buff.len() {
                // NB: Improper indexing on 32-bit OS, but will OOM first.
                let mut v = Vec::with_capacity(idx as usize);
                v.resize(idx as usize, 0f32);
                self.inputs.push(v);
            }
            let mut stream = inputs.stream();
            let mut self_it = self.inputs.iter_mut();
            while let (Some(row), Some(vec_dest)) = (stream.next(), self_it.next()) {
                assert_eq!(vec_dest.len(), idx as usize);
                vec_dest.extend(row);
                assert!(vec_dest.len() <= idx as usize + n_times); // cannot send inputs ahead of outputs.
                let pad_val = vec_dest.last().cloned().unwrap_or(0f32);
                vec_dest.resize(idx as usize +n_times, pad_val);
            }
        }

        // Calculate outputs
        self.prep_execution();
        for slot in 0..n_slots as u32 {
            for time in idx..idx+n_times as u64 {
                buff[[slot as usize, (time - idx) as usize]] = self.get_sample(time, slot);
            }
        }
        // Keep track of the playhead
        self.head = idx + n_times as u64;
    }
}

impl GraphWatcher for SparkleRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(*handle, Node::new(my_node_data));
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.add_edge(edge);
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.nodes.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).expect("Attempt to delete edge, but it was never created!").inbound
        };
        if let Some(stored_edge) = inbound.get_mut(edge.to_slot() as usize) {
            *stored_edge = None
        }
    }
}

impl SparkleRenderer {
    /// Creates a LLVM function with signature:
    /// fn get_sample(time: u64, slot: u32, input_getter: &callback_type) -> f32) -> f32
    /// callback_type should be { input_getter, userdata },
    /// Returns the name of the function that can be used to get the effect's output.
    fn jit_effect(&mut self, effect: &Effect) -> String {
        let fname = format!("{}_get_output", effect.id().name());
        println!("jit: {}", fname);
        if self.get_func(&fname).is_none() {
            // Effect hasn't been compiled yet; do so.
            // TODO: this only checks the execution engines; not uncompiled modules!
            let mut func = self.open_get_sample_fn(&fname);
            // Open a basic block to begin appending instructions.
            let bb = self.llvm_ctx.append_basic_block(&mut func, &fname);
            self.llvm_builder.position_at_end(bb);
            // TODO: fill in function body.
            let time = func.get_param(0).unwrap();
            let slot = func.get_param(1).unwrap();
            let in_getter = func.get_param(2).unwrap();
            let ret = self.llvm_ctx.cons(20f32); // Dummy return value
            self.llvm_builder.build_ret(ret);
        }

        fname
    }
    fn open_get_sample_fn(&mut self, fname: &str) -> Function {
        let ty = self.sample_getter_type;
        self.get_open_module().add_function(ty, fname)
    }
    /// Get or create an editable module
    fn get_open_module(&mut self) -> &mut Module {
        // TODO: use entry API
        if let Some(ref mut module) = self.open_module {
            module
        } else {
            self.open_module = Some(
                self.llvm_ctx.module_create_with_name(
                    &format!("mod{}", self.llvm_modules.len()))
            );
            self.open_module.as_mut().unwrap()
        }
    }
    /// Return a pointer to the compiled function with the provided name.
    /// Search across all execution engines.
    fn get_func(&self, name: &str) -> Option<extern "C" fn()> {
        self.llvm_engines.iter().flat_map(|ee| {
            ee.get_function_address(name).into_iter()
        }).next()
    }
    /// Compile all outstanding functions.
    fn prep_execution(&mut self) {
        // IF there's an open module, compile it.
        if let Some(module) = self.open_module.take() {
            let ee = {
                module.dump();
                llvm::ExecutionEngine::create_for_module(&module).unwrap()
            };
            self.llvm_engines.push(ee);
        }
    }
    /// Allocate renderer data based on data from a RouteGraph node.
    fn make_node(&mut self, effect: &NodeData) -> MyNodeData {
        match *effect.data() {
            EffectData::Buffer(ref buff) => MyNodeData::Buffer(buff.clone()),
            EffectData::Primitive(_e) =>
                MyNodeData::LlvmFunc(self.jit_effect(effect)),
            EffectData::RouteGraph(ref _graph) =>
                MyNodeData::LlvmFunc(self.jit_effect(effect)),
        }
    }
    /// Get the output at a particular time and to a particular output slot.
    fn get_sample(&mut self, time: u64, slot: u32) -> f32 {
        //20f32
        let out_edge = self.nodes.output_edges.get(slot as usize);
        self.get_maybe_edge_value(time, out_edge, &|time2, slot2| {
            *self.inputs.get(slot2 as usize)
                .and_then(|v| v.get(time2 as usize))
                .unwrap_or(&0f32)
        })
    }
    /// Wrapper around `get_edge_value` that will return 0f32 if maybe_edge is not
    /// `Some(&Some(edge))`.
    fn get_maybe_edge_value(&self, time: u64,
        maybe_edge: Option<&Option<Edge>>, get_input: &Fn(u64, u32) -> f32) -> f32
    {
        if let Some(&Some(ref edge)) = maybe_edge {
            self.get_edge_value(time, &edge, get_input)
        } else {
            // Edge doesn't exist; value is zero.
            0f32
        }
    }
    /// Get the value on an edge at a specific time.
    /// This will recurse down, all the way to the input to this node itself.
    /// `get_input(time, slot)` will be called (multiple times, with different args)
    /// in order to query whatever is input to this node.
    fn get_edge_value(&self, time: u64, edge: &Edge, get_input: &Fn(u64, u32) -> f32) -> f32 {
        let from = edge.from_full();
        let from_slot = edge.from_slot();
        if *from.node_handle() == None {
            // reading from an input
            get_input(time, from_slot)
        } else {
            // Reading from another node within the DAG
            let node = &self.nodes[&from];
            match node.data {
                MyNodeData::LlvmFunc(ref fname) => {
                    let out_getter = self.get_func(fname);
                    out_getter.map(|getter| unsafe {
                        let f: extern "C" fn(u64, u32, *const CallbackType) -> f32 = mem::transmute(getter);
                        println!("calling {:?}", f);
                        f(time, from_slot, &mem::transmute(get_input))
                    }).unwrap_or(0f32)
                }
                MyNodeData::Buffer(ref buf) => buf.get(time, from_slot),
            }
        }
    }
}

impl Default for SparkleRenderer {
    fn default() -> SparkleRenderer {
        // Initialize core LLVM features
        llvm::link_in_mcjit();
        llvm::initialize_native_target();
        llvm::initialize_native_asm_printer();

        let llvm_ctx = Context::new();
        let llvm_builder = llvm_ctx.create_builder();
        let llvm_modules = Default::default();
        let llvm_engines = Default::default();
        let open_module = None;

        // Create the callback_type struct.
        // It is recursive, and has a dependency on some fn-ptrs,
        // so its creation is staggered.
        let callback_type = {
            let c_name = CString::new("SampleGetter").unwrap();
            unsafe {
                LLVMStructCreateNamed(llvm_ctx.ptr, c_name.as_ptr())
            }
        };

        let sample_getter_type = llvm::function_type(
            f32::get_type_in_context(&llvm_ctx),
            vec![
                u64::get_type_in_context(&llvm_ctx),
                u32::get_type_in_context(&llvm_ctx),
                llvm::pointer_type(callback_type, 0)
            ],
        /* is_var_arg */false);

        unsafe {
            let mut element_types = vec![sample_getter_type, callback_type];
            let is_packed = false;
            LLVMStructSetBody(callback_type, element_types.as_mut_ptr(),
                element_types.len() as u32, is_packed as i32)
        }

        let (head, inputs, nodes) = Default::default();

        SparkleRenderer {
            head, inputs, nodes,
            llvm_ctx, llvm_builder, llvm_modules, llvm_engines, open_module, callback_type, sample_getter_type
        }
    }
}

impl NodeMap {
    /// Add an edge that connects two nodes within this graph.
    fn add_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).unwrap().inbound
        };
        let slot: u32 = edge.to_slot().into();
        let slot = slot as usize;
        // allocate space to store the edge.
        if inbound.len() <= slot { inbound.resize(slot+1, None) }

        inbound[slot] = Some(edge.clone());
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: Vec::new(),
        }
    }
}

impl Deref for NodeMap {
    type Target = HashMap<NodeHandle, Node>;
    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

impl DerefMut for NodeMap {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.nodes
    }
}
<commit_msg>Implement JIT F32Constant effect.<commit_after>//! Sparkle is a JIT-based renderer that uses llvm to compile effects as they
//! are loaded.
//! Other than the JIT aspect, it is mostly a literal reimplementation of
//! the reference renderer.

use std::collections::HashMap;
use std::ffi::CString;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;

use jagged_array::Jagged2;
use llvm;
use llvm::{Builder, Context, ContextType, ExecutionEngine, Function, Module};
use llvm_sys;
use llvm_sys::core::{
    LLVMContextCreate,
    LLVMContextDispose,
    LLVMDisposeModule,
    LLVMModuleCreateWithNameInContext,
    LLVMStructCreateNamed,
    LLVMStructSetBody,
};
use llvm_sys::prelude::*;
use llvm_sys::target::{LLVM_InitializeNativeTarget, LLVM_InitializeNativeAsmPrinter,
                   LLVM_InitializeNativeAsmParser};
use ndarray::Array2;
use streaming_iterator::StreamingIterator;

use render::Renderer;
use resman::AudioBuffer;
use routing::{Edge, Effect, GraphWatcher, NodeData, NodeHandle, RouteGraph};
use routing::effect::{PrimitiveEffect, EffectData};

#[derive(Debug)]
pub struct SparkleRenderer {
    // Top-level node map
    nodes: NodeMap,
    /// inputs[slot][sample] represents the value of the external input at
    /// the given slot and time. On OOB, it is assumed to be 0.
    inputs: Vec<Vec<f32>>,
    /// Next expected sample to be queried.
    /// This is tracked because if we do a seek, the inputs need to be zero'd.
    head: u64,
    // LLVM data below
    /// Llvm execution engines. Zipped against the modules.
    llvm_engines: Vec<ExecutionEngine>,
    /// Finalized llvm modules.
    llvm_modules: Vec<Module>,
    /// Module that has yet to be compiled.
    open_module: Option<Module>,
    /// LLVM struct { fn(time, slot, callback_type*)->f32, callback_type* }
    /// Used to pass callbak functions into get_output() to allow effects to access their inputs.
    callback_type: LLVMTypeRef,
    /// LLVM type for fn(time, slot, input_getter: callback_type*) -> f32
    sample_getter_type: LLVMTypeRef,
    /// Object that lets us build IR into basic blocks.
    llvm_builder: Builder,
    // NOTE: LLVM Context must be last member, otherwise jemalloc will try dropping
    // llvm-owned data
    /// Object that provides a context for LLVM calls.
    llvm_ctx: Context,
}

#[derive(Debug, Default)]
struct NodeMap {
    nodes: HashMap<NodeHandle, Node>,
    output_edges: Vec<Option<Edge>>,
}

#[derive(Debug)]
struct Node {
    data: MyNodeData,
    /// Inbound edges, indexed by slot idx.
    inbound: Vec<Option<Edge>>,
}

#[derive(Debug)]
enum MyNodeData {
    /// The node corresponds to a LLVM function with the provided name.
    LlvmFunc(String),
    /// External audio.
    Buffer(AudioBuffer),
}

struct CallbackType {
    input_getter: *const fn(u64, u32, *const CallbackType) -> f32,
    userdata: *const CallbackType,
}

impl Renderer for SparkleRenderer {
    fn fill_buffer(&mut self, buff: &mut Array2<f32>, idx: u64, inputs: Jagged2<f32>) {
        let (n_slots, n_times) = buff.dim().into();
        // Store inputs for future use
        {
            // If this is a seek operation, forget historical input values.
            if idx != self.head {
                for slot in &mut self.inputs {
                    *slot = Vec::new();
                    // NB: Improper indexing on 32-bit OS, but will OOM first.
                    slot.resize(idx as usize, 0f32);
                }
            }
            // Make sure we have storage space for all inputs.
            while self.inputs.len() < buff.len() {
                // NB: Improper indexing on 32-bit OS, but will OOM first.
                let mut v = Vec::with_capacity(idx as usize);
                v.resize(idx as usize, 0f32);
                self.inputs.push(v);
            }
            let mut stream = inputs.stream();
            let mut self_it = self.inputs.iter_mut();
            while let (Some(row), Some(vec_dest)) = (stream.next(), self_it.next()) {
                assert_eq!(vec_dest.len(), idx as usize);
                vec_dest.extend(row);
                assert!(vec_dest.len() <= idx as usize + n_times); // cannot send inputs ahead of outputs.
                let pad_val = vec_dest.last().cloned().unwrap_or(0f32);
                vec_dest.resize(idx as usize +n_times, pad_val);
            }
        }

        // Calculate outputs
        self.prep_execution();
        for slot in 0..n_slots as u32 {
            for time in idx..idx+n_times as u64 {
                buff[[slot as usize, (time - idx) as usize]] = self.get_sample(time, slot);
            }
        }
        // Keep track of the playhead
        self.head = idx + n_times as u64;
    }
}

impl GraphWatcher for SparkleRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(*handle, Node::new(my_node_data));
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.add_edge(edge);
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.nodes.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).expect("Attempt to delete edge, but it was never created!").inbound
        };
        if let Some(stored_edge) = inbound.get_mut(edge.to_slot() as usize) {
            *stored_edge = None
        }
    }
}

impl SparkleRenderer {
    /// Creates a LLVM function with signature:
    /// fn get_sample(time: u64, slot: u32, input_getter: &callback_type) -> f32) -> f32
    /// callback_type should be { input_getter, userdata },
    /// Returns the name of the function that can be used to get the effect's output.
    fn jit_effect(&mut self, effect: &Effect) -> String {
        let fname = format!("{}_get_output", effect.id().name());
        println!("jit: {}", fname);
        if self.get_func(&fname).is_none() {
            // Effect hasn't been compiled yet; do so.
            // TODO: this only checks the execution engines; not uncompiled modules!
            let mut func = self.open_get_sample_fn(&fname);
            // Open a basic block to begin appending instructions.
            let bb = self.llvm_ctx.append_basic_block(&mut func, &fname);
            self.llvm_builder.position_at_end(bb);
            let time = func.get_param(0).unwrap();
            let slot = func.get_param(1).unwrap();
            let in_getter = func.get_param(2).unwrap();
            match *effect.data() {
                EffectData::Primitive(prim) => match prim {
                    PrimitiveEffect::F32Constant => {
                        let f32_type = f32::get_type_in_context(&self.llvm_ctx);
                        let slot_as_f32 = self.llvm_builder.build_bit_cast(slot, f32_type, "slot_as_f32");
                        self.llvm_builder.build_ret(slot_as_f32);
                    },
                    _ => {
                        let ret = self.llvm_ctx.cons(20f32);
                        self.llvm_builder.build_ret(ret);
                    },
                },
                EffectData::RouteGraph(ref graph) => {
                    // TODO: for now, just use a dummy return value
                    let ret = self.llvm_ctx.cons(20f32);
                    self.llvm_builder.build_ret(ret);
                },
                _ => panic!("Cannot JIT effect: {:?}", effect)
            }
        }

        fname
    }
    fn open_get_sample_fn(&mut self, fname: &str) -> Function {
        let ty = self.sample_getter_type;
        self.get_open_module().add_function(ty, fname)
    }
    /// Get or create an editable module
    fn get_open_module(&mut self) -> &mut Module {
        // TODO: use entry API
        if let Some(ref mut module) = self.open_module {
            module
        } else {
            self.open_module = Some(
                self.llvm_ctx.module_create_with_name(
                    &format!("mod{}", self.llvm_modules.len()))
            );
            self.open_module.as_mut().unwrap()
        }
    }
    /// Return a pointer to the compiled function with the provided name.
    /// Search across all execution engines.
    fn get_func(&self, name: &str) -> Option<extern "C" fn()> {
        self.llvm_engines.iter().flat_map(|ee| {
            ee.get_function_address(name).into_iter()
        }).next()
    }
    /// Compile all outstanding functions.
    fn prep_execution(&mut self) {
        // IF there's an open module, compile it.
        if let Some(module) = self.open_module.take() {
            let ee = {
                module.dump();
                llvm::ExecutionEngine::create_for_module(&module).unwrap()
            };
            self.llvm_engines.push(ee);
        }
    }
    /// Allocate renderer data based on data from a RouteGraph node.
    fn make_node(&mut self, effect: &NodeData) -> MyNodeData {
        match *effect.data() {
            EffectData::Buffer(ref buff) => MyNodeData::Buffer(buff.clone()),
            EffectData::Primitive(_e) =>
                MyNodeData::LlvmFunc(self.jit_effect(effect)),
            EffectData::RouteGraph(ref _graph) =>
                MyNodeData::LlvmFunc(self.jit_effect(effect)),
        }
    }
    /// Get the output at a particular time and to a particular output slot.
    fn get_sample(&mut self, time: u64, slot: u32) -> f32 {
        //20f32
        let out_edge = self.nodes.output_edges.get(slot as usize);
        self.get_maybe_edge_value(time, out_edge, &|time2, slot2| {
            *self.inputs.get(slot2 as usize)
                .and_then(|v| v.get(time2 as usize))
                .unwrap_or(&0f32)
        })
    }
    /// Wrapper around `get_edge_value` that will return 0f32 if maybe_edge is not
    /// `Some(&Some(edge))`.
    fn get_maybe_edge_value(&self, time: u64,
        maybe_edge: Option<&Option<Edge>>, get_input: &Fn(u64, u32) -> f32) -> f32
    {
        if let Some(&Some(ref edge)) = maybe_edge {
            self.get_edge_value(time, &edge, get_input)
        } else {
            // Edge doesn't exist; value is zero.
            0f32
        }
    }
    /// Get the value on an edge at a specific time.
    /// This will recurse down, all the way to the input to this node itself.
    /// `get_input(time, slot)` will be called (multiple times, with different args)
    /// in order to query whatever is input to this node.
    fn get_edge_value(&self, time: u64, edge: &Edge, get_input: &Fn(u64, u32) -> f32) -> f32 {
        let from = edge.from_full();
        let from_slot = edge.from_slot();
        if *from.node_handle() == None {
            // reading from an input
            get_input(time, from_slot)
        } else {
            // Reading from another node within the DAG
            let node = &self.nodes[&from];
            match node.data {
                MyNodeData::LlvmFunc(ref fname) => {
                    let out_getter = self.get_func(fname);
                    out_getter.map(|getter| unsafe {
                        let f: extern "C" fn(u64, u32, *const CallbackType) -> f32 = mem::transmute(getter);
                        f(time, from_slot, &mem::transmute(get_input))
                    }).unwrap_or(0f32)
                }
                MyNodeData::Buffer(ref buf) => buf.get(time, from_slot),
            }
        }
    }
}

impl Default for SparkleRenderer {
    fn default() -> SparkleRenderer {
        // Initialize core LLVM features
        llvm::link_in_mcjit();
        llvm::initialize_native_target();
        llvm::initialize_native_asm_printer();

        let llvm_ctx = Context::new();
        let llvm_builder = llvm_ctx.create_builder();
        let llvm_modules = Default::default();
        let llvm_engines = Default::default();
        let open_module = None;

        // Create the callback_type struct.
        // It is recursive, and has a dependency on some fn-ptrs,
        // so its creation is staggered.
        let callback_type = {
            let c_name = CString::new("SampleGetter").unwrap();
            unsafe {
                LLVMStructCreateNamed(llvm_ctx.ptr, c_name.as_ptr())
            }
        };

        let sample_getter_type = llvm::function_type(
            f32::get_type_in_context(&llvm_ctx),
            vec![
                u64::get_type_in_context(&llvm_ctx),
                u32::get_type_in_context(&llvm_ctx),
                llvm::pointer_type(callback_type, 0)
            ],
        /* is_var_arg */false);

        unsafe {
            let mut element_types = vec![sample_getter_type, callback_type];
            let is_packed = false;
            LLVMStructSetBody(callback_type, element_types.as_mut_ptr(),
                element_types.len() as u32, is_packed as i32)
        }

        let (head, inputs, nodes) = Default::default();

        SparkleRenderer {
            head, inputs, nodes,
            llvm_ctx, llvm_builder, llvm_modules, llvm_engines, open_module, callback_type, sample_getter_type
        }
    }
}

impl NodeMap {
    /// Add an edge that connects two nodes within this graph.
    fn add_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).unwrap().inbound
        };
        let slot: u32 = edge.to_slot().into();
        let slot = slot as usize;
        // allocate space to store the edge.
        if inbound.len() <= slot { inbound.resize(slot+1, None) }

        inbound[slot] = Some(edge.clone());
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: Vec::new(),
        }
    }
}

impl Deref for NodeMap {
    type Target = HashMap<NodeHandle, Node>;
    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

impl DerefMut for NodeMap {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.nodes
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CString, CStr};
use SdlResult;
use get_error;

use sys::clipboard as ll;

pub fn set_clipboard_text(text: &String) -> SdlResult<()> {
    unsafe {
        let text = CString::new(text.clone()).unwrap();
        let result = ll::SDL_SetClipboardText(text.as_ptr());

        if result == 0 {
            Err(get_error())
        } else {
            Ok(())
        }
    }
}

pub fn get_clipboard_text() -> SdlResult<String> {
    let result = unsafe {
        let buf = ll::SDL_GetClipboardText();
        String::from_utf8_lossy(CStr::from_ptr(buf).to_bytes()).into_owned()
    };

    if result.len() == 0 {
        Err(get_error())
    } else {
        Ok(result)
    }
}

pub fn has_clipboard_text() -> bool {
    unsafe { ll::SDL_HasClipboardText() == 1 }
}
<commit_msg>Add ClipboardUtil<commit_after>use std::ffi::{CString, CStr};
use SdlResult;
use get_error;

use sys::clipboard as ll;

/// Clipboard utility functions. Access with `VideoSubsystem::clipboard()`.
///
/// These functions require the video subsystem to be initialized.
///
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let video_subsystem = sdl_context.video().unwrap();
///
/// video_subsystem.clipboard().set_clipboard_text("Hello World!").unwrap();
/// ```
pub struct ClipboardUtil {
    _subsystem: ::VideoSubsystem
}

impl ::VideoSubsystem {
    #[inline]
    pub fn clipboard(&self) -> ClipboardUtil {
        ClipboardUtil {
            _subsystem: self.clone()
        }
    }
}

impl ClipboardUtil {
    pub fn set_clipboard_text(&self, text: &str) -> SdlResult<()> {
        unsafe {
            let text = CString::new(text).unwrap();
            let result = ll::SDL_SetClipboardText(text.as_ptr());

            if result == 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    pub fn get_clipboard_text(&self) -> SdlResult<String> {
        unsafe {
            let buf = ll::SDL_GetClipboardText();

            if buf.is_null() {
                Err(get_error())
            } else {
                Ok(String::from_utf8_lossy(CStr::from_ptr(buf).to_bytes()).into_owned())
            }
        }
    }

    pub fn has_clipboard_text(&self) -> bool {
        unsafe { ll::SDL_HasClipboardText() == 1 }
    }
}
<|endoftext|>


<commit_before>#![crate_name="sdl2_image"]
#![crate_type = "lib"]

extern crate sdl2;
extern crate sdl2_sys as sys;

#[macro_use]
extern crate bitflags;

use std::os::raw::{c_int, c_char};
use std::ffi::CString;
use std::path::Path;
use sdl2::surface::Surface;
use sdl2::render::Texture;
use sdl2::render::Renderer;
use sdl2::rwops::RWops;
use sdl2::version::Version;
use sdl2::get_error;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(kind="framework", name="SDL2_image")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL2_image")]
    extern {}
}

#[cfg(any(target_os="windows", target_os="linux", target_os="freebsd"))]
mod others {
    #[link(name="SDL2_image")]
    extern {}
}

#[allow(non_camel_case_types, dead_code)]
mod ffi;

/// InitFlags are passed to init() to control which subsystem
/// functionality to load.
bitflags! {
    pub flags InitFlag : u32 {
        const INIT_JPG  = ::ffi::IMG_INIT_JPG as u32,
        const INIT_PNG  = ::ffi::IMG_INIT_PNG as u32,
        const INIT_TIF  = ::ffi::IMG_INIT_TIF as u32,
        const INIT_WEBP = ::ffi::IMG_INIT_WEBP as u32
    }
}

// This is used for error message for init
impl ToString for InitFlag {
    fn to_string(&self) -> String {
        let mut string = "".to_string();
        if self.contains(INIT_JPG) {
            string = string + &"INIT_JPG ".to_string();
        }
        if self.contains(INIT_PNG) {
            string = string + &"INIT_PNG ".to_string();
        }
        if self.contains(INIT_TIF) {
            string = string + &"INIT_TIF ".to_string();
        }
        if self.contains(INIT_WEBP) {
            string = string + &"INIT_WEBP ".to_string();
        }
        string
    }
}

/// Static method extensions for creating Surfaces
pub trait LoadSurface: Sized {
    // Self is only returned here to type hint to the compiler.
    // The syntax for type hinting in this case is not yet defined.
    // The intended return value is Result<~Surface, String>.
    fn from_file(filename: &Path) -> Result<Self, String>;
    fn from_xpm_array(xpm: *const *const i8) -> Result<Self, String>;
}

/// Method extensions to Surface for saving to disk
pub trait SaveSurface {
    fn save(&self, filename: &Path) -> Result<(), String>;
    fn save_rw(&self, dst: &mut RWops) -> Result<(), String>;
}

impl<'a> LoadSurface for Surface<'a> {
    fn from_file(filename: &Path) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from a file
        unsafe {
            let raw = ffi::IMG_Load(CString::new(filename.to_str().unwrap()).unwrap().as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }

    fn from_xpm_array(xpm: *const *const i8) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from XPM data
        unsafe {
            let raw = ffi::IMG_ReadXPMFromArray(xpm as *const *const c_char);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }
}

impl<'a> SaveSurface for Surface<'a> {
    fn save(&self, filename: &Path) -> Result<(), String> {
        //! Saves an SDL Surface to a file
        unsafe {
            let status = ffi::IMG_SavePNG(self.raw(), CString::new(filename.to_str().unwrap()).unwrap().as_ptr() as *const _);
            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    fn save_rw(&self, dst: &mut RWops) -> Result<(), String> {
        //! Saves an SDL Surface to an RWops
        unsafe {
            let status = ffi::IMG_SavePNG_RW(self.raw(), dst.raw(), 0);

            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Method extensions for creating Textures from a Renderer
pub trait LoadTexture {
    fn load_texture(&self, filename: &Path) -> Result<Texture, String>;
}

impl<'a> LoadTexture for Renderer<'a> {
    fn load_texture(&self, filename: &Path) -> Result<Texture, String> {
        //! Loads an SDL Texture from a file
        unsafe {
            let raw = ffi::IMG_LoadTexture(self.raw(), CString::new(filename.to_str().unwrap()).unwrap().as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Texture::from_ll(self, raw))
            }
        }
    }
}

/// Context manager for sdl2_image to manage quiting. Can't do much with it but
/// keep it alive while you are using it.
pub struct Sdl2ImageContext;

impl Drop for Sdl2ImageContext {
    fn drop(&mut self) {
        unsafe { ffi::IMG_Quit(); }
    }
}

pub fn init(flags: InitFlag) -> Result<Sdl2ImageContext, String> {
    //! Initializes SDL2_image with InitFlags.
    //! If not every flag is set it returns an error
    let return_flags = unsafe {
        let used = ffi::IMG_Init(flags.bits() as c_int);
        InitFlag::from_bits_truncate(used as u32)
    };
    if !flags.intersects(return_flags) {
        // According to docs, error message text is not always set
        if get_error() == "" {
            let un_init_flags = return_flags ^ flags;
            let error_str = &("Could not init: ".to_string() + &un_init_flags.to_string());
            let _ = sdl2::set_error(error_str);
        }
        Err(get_error())
    } else {
        Ok(Sdl2ImageContext)
    }
}

pub fn get_linked_version() -> Version {
    //! Returns the version of the dynamically linked SDL_image library
    unsafe {
        Version::from_ll(*ffi::IMG_Linked_Version())
    }
}

#[inline]
fn to_surface_result<'a>(raw: *mut sys::surface::SDL_Surface) -> Result<Surface<'a>, String> {
    if (raw as *mut ()).is_null() {
        Err(get_error())
    } else {
        unsafe { Ok(Surface::from_ll(raw)) }
    }
}

pub trait ImageRWops {
    /// load as a surface. except TGA
    fn load(&self) -> Result<Surface, String>;
    /// load as a surface. This can load all supported image formats.
    fn load_typed(&self, _type: &str) -> Result<Surface, String>;

    fn load_cur(&self) -> Result<Surface, String>;
    fn load_ico(&self) -> Result<Surface, String>;
    fn load_bmp(&self) -> Result<Surface, String>;
    fn load_pnm(&self) -> Result<Surface, String>;
    fn load_xpm(&self) -> Result<Surface, String>;
    fn load_xcf(&self) -> Result<Surface, String>;
    fn load_pcx(&self) -> Result<Surface, String>;
    fn load_gif(&self) -> Result<Surface, String>;
    fn load_jpg(&self) -> Result<Surface, String>;
    fn load_tif(&self) -> Result<Surface, String>;
    fn load_png(&self) -> Result<Surface, String>;
    fn load_tga(&self) -> Result<Surface, String>;
    fn load_lbm(&self) -> Result<Surface, String>;
    fn load_xv(&self)  -> Result<Surface, String>;
    fn load_webp(&self) -> Result<Surface, String>;

    fn is_cur(&self) -> bool;
    fn is_ico(&self) -> bool;
    fn is_bmp(&self) -> bool;
    fn is_pnm(&self) -> bool;
    fn is_xpm(&self) -> bool;
    fn is_xcf(&self) -> bool;
    fn is_pcx(&self) -> bool;
    fn is_gif(&self) -> bool;
    fn is_jpg(&self) -> bool;
    fn is_tif(&self) -> bool;
    fn is_png(&self) -> bool;
    fn is_lbm(&self) -> bool;
    fn is_xv(&self)  -> bool;
    fn is_webp(&self) -> bool;
}

impl<'a> ImageRWops for RWops<'a> {
    fn load(&self) -> Result<Surface, String> {
        let raw = unsafe {
            ffi::IMG_Load_RW(self.raw(), 0)
        };
        to_surface_result(raw)
    }
    fn load_typed(&self, _type: &str) -> Result<Surface, String> {
        let raw = unsafe {
            ffi::IMG_LoadTyped_RW(self.raw(), 0, CString::new(_type.as_bytes()).unwrap().as_ptr() as *const _)
        };
        to_surface_result(raw)
    }

    fn load_cur(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadCUR_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_ico(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadICO_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_bmp(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadBMP_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pnm(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadPNM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xpm(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadXPM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xcf(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadXCF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pcx(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadPCX_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_gif(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadGIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_jpg(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadJPG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tif(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadTIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_png(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadPNG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tga(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadTGA_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_lbm(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadLBM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xv(&self)  -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadXV_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_webp(&self)  -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadWEBP_RW(self.raw()) };
        to_surface_result(raw)
    }

    fn is_cur(&self) -> bool {
        unsafe { ffi::IMG_isCUR(self.raw()) == 1 }
    }
    fn is_ico(&self) -> bool {
        unsafe { ffi::IMG_isICO(self.raw()) == 1 }
    }
    fn is_bmp(&self) -> bool {
        unsafe { ffi::IMG_isBMP(self.raw()) == 1 }
    }
    fn is_pnm(&self) -> bool {
        unsafe { ffi::IMG_isPNM(self.raw()) == 1 }
    }
    fn is_xpm(&self) -> bool {
        unsafe { ffi::IMG_isXPM(self.raw()) == 1 }
    }
    fn is_xcf(&self) -> bool {
        unsafe { ffi::IMG_isXCF(self.raw()) == 1 }
    }
    fn is_pcx(&self) -> bool {
        unsafe { ffi::IMG_isPCX(self.raw()) == 1 }
    }
    fn is_gif(&self) -> bool {
        unsafe { ffi::IMG_isGIF(self.raw()) == 1 }
    }
    fn is_jpg(&self) -> bool {
        unsafe { ffi::IMG_isJPG(self.raw()) == 1 }
    }
    fn is_tif(&self) -> bool {
        unsafe { ffi::IMG_isTIF(self.raw()) == 1 }
    }
    fn is_png(&self) -> bool {
        unsafe { ffi::IMG_isPNG(self.raw()) == 1 }
    }
    fn is_lbm(&self) -> bool {
        unsafe { ffi::IMG_isLBM(self.raw()) == 1 }
    }
    fn is_xv(&self)  -> bool {
        unsafe { ffi::IMG_isXV(self.raw())  == 1 }
    }
    fn is_webp(&self) -> bool {
        unsafe { ffi::IMG_isWEBP(self.raw())  == 1 }
    }
}
<commit_msg>fix clippy warnings<commit_after>#![crate_name="sdl2_image"]
#![crate_type = "lib"]

extern crate sdl2;
extern crate sdl2_sys as sys;

#[macro_use]
extern crate bitflags;

use std::os::raw::{c_int, c_char};
use std::ffi::CString;
use std::path::Path;
use sdl2::surface::Surface;
use sdl2::render::Texture;
use sdl2::render::Renderer;
use sdl2::rwops::RWops;
use sdl2::version::Version;
use sdl2::get_error;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(kind="framework", name="SDL2_image")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL2_image")]
    extern {}
}

#[cfg(any(target_os="windows", target_os="linux", target_os="freebsd"))]
mod others {
    #[link(name="SDL2_image")]
    extern {}
}

#[allow(non_camel_case_types, dead_code)]
mod ffi;

/// InitFlags are passed to init() to control which subsystem
/// functionality to load.
bitflags! {
    pub flags InitFlag : u32 {
        const INIT_JPG  = ::ffi::IMG_INIT_JPG as u32,
        const INIT_PNG  = ::ffi::IMG_INIT_PNG as u32,
        const INIT_TIF  = ::ffi::IMG_INIT_TIF as u32,
        const INIT_WEBP = ::ffi::IMG_INIT_WEBP as u32
    }
}

// This is used for error message for init
impl ToString for InitFlag {
    fn to_string(&self) -> String {
        let mut string = "".to_string();
        if self.contains(INIT_JPG) {
            string = string + &"INIT_JPG ".to_string();
        }
        if self.contains(INIT_PNG) {
            string = string + &"INIT_PNG ".to_string();
        }
        if self.contains(INIT_TIF) {
            string = string + &"INIT_TIF ".to_string();
        }
        if self.contains(INIT_WEBP) {
            string = string + &"INIT_WEBP ".to_string();
        }
        string
    }
}

/// Static method extensions for creating Surfaces
pub trait LoadSurface: Sized {
    // Self is only returned here to type hint to the compiler.
    // The syntax for type hinting in this case is not yet defined.
    // The intended return value is Result<~Surface, String>.
    fn from_file(filename: &Path) -> Result<Self, String>;
    fn from_xpm_array(xpm: *const *const i8) -> Result<Self, String>;
}

/// Method extensions to Surface for saving to disk
pub trait SaveSurface {
    fn save(&self, filename: &Path) -> Result<(), String>;
    fn save_rw(&self, dst: &mut RWops) -> Result<(), String>;
}

impl<'a> LoadSurface for Surface<'a> {
    fn from_file(filename: &Path) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from a file
        unsafe {
            let c_filename = CString::new(filename.to_str().unwrap()).unwrap();
            let raw = ffi::IMG_Load(c_filename.as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }

    fn from_xpm_array(xpm: *const *const i8) -> Result<Surface<'a>, String> {
        //! Loads an SDL Surface from XPM data
        unsafe {
            let raw = ffi::IMG_ReadXPMFromArray(xpm as *const *const c_char);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Surface::from_ll(raw))
            }
        }
    }
}

impl<'a> SaveSurface for Surface<'a> {
    fn save(&self, filename: &Path) -> Result<(), String> {
        //! Saves an SDL Surface to a file
        unsafe {
            let c_filename = CString::new(filename.to_str().unwrap()).unwrap();
            let status = ffi::IMG_SavePNG(self.raw(), c_filename.as_ptr() as *const _);
            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    fn save_rw(&self, dst: &mut RWops) -> Result<(), String> {
        //! Saves an SDL Surface to an RWops
        unsafe {
            let status = ffi::IMG_SavePNG_RW(self.raw(), dst.raw(), 0);

            if status != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }
}

/// Method extensions for creating Textures from a Renderer
pub trait LoadTexture {
    fn load_texture(&self, filename: &Path) -> Result<Texture, String>;
}

impl<'a> LoadTexture for Renderer<'a> {
    fn load_texture(&self, filename: &Path) -> Result<Texture, String> {
        //! Loads an SDL Texture from a file
        unsafe {
            let c_filename = CString::new(filename.to_str().unwrap()).unwrap();
            let raw = ffi::IMG_LoadTexture(self.raw(), c_filename.as_ptr() as *const _);
            if (raw as *mut ()).is_null() {
                Err(get_error())
            } else {
                Ok(Texture::from_ll(self, raw))
            }
        }
    }
}

/// Context manager for `sdl2_image` to manage quiting. Can't do much with it but
/// keep it alive while you are using it.
pub struct Sdl2ImageContext;

impl Drop for Sdl2ImageContext {
    fn drop(&mut self) {
        unsafe { ffi::IMG_Quit(); }
    }
}

/// Initializes `SDL2_image` with `InitFlags`.
/// If not every flag is set it returns an error
pub fn init(flags: InitFlag) -> Result<Sdl2ImageContext, String> {
    let return_flags = unsafe {
        let used = ffi::IMG_Init(flags.bits() as c_int);
        InitFlag::from_bits_truncate(used as u32)
    };
    if !flags.intersects(return_flags) {
        // According to docs, error message text is not always set
        if get_error() == "" {
            let un_init_flags = return_flags ^ flags;
            let error_str = &("Could not init: ".to_string() + &un_init_flags.to_string());
            let _ = sdl2::set_error(error_str);
        }
        Err(get_error())
    } else {
        Ok(Sdl2ImageContext)
    }
}

/// Returns the version of the dynamically linked `SDL_image` library
pub fn get_linked_version() -> Version {
    unsafe {
        Version::from_ll(*ffi::IMG_Linked_Version())
    }
}

#[inline]
fn to_surface_result<'a>(raw: *mut sys::surface::SDL_Surface) -> Result<Surface<'a>, String> {
    if (raw as *mut ()).is_null() {
        Err(get_error())
    } else {
        unsafe { Ok(Surface::from_ll(raw)) }
    }
}

pub trait ImageRWops {
    /// load as a surface. except TGA
    fn load(&self) -> Result<Surface, String>;
    /// load as a surface. This can load all supported image formats.
    fn load_typed(&self, _type: &str) -> Result<Surface, String>;

    fn load_cur(&self) -> Result<Surface, String>;
    fn load_ico(&self) -> Result<Surface, String>;
    fn load_bmp(&self) -> Result<Surface, String>;
    fn load_pnm(&self) -> Result<Surface, String>;
    fn load_xpm(&self) -> Result<Surface, String>;
    fn load_xcf(&self) -> Result<Surface, String>;
    fn load_pcx(&self) -> Result<Surface, String>;
    fn load_gif(&self) -> Result<Surface, String>;
    fn load_jpg(&self) -> Result<Surface, String>;
    fn load_tif(&self) -> Result<Surface, String>;
    fn load_png(&self) -> Result<Surface, String>;
    fn load_tga(&self) -> Result<Surface, String>;
    fn load_lbm(&self) -> Result<Surface, String>;
    fn load_xv(&self)  -> Result<Surface, String>;
    fn load_webp(&self) -> Result<Surface, String>;

    fn is_cur(&self) -> bool;
    fn is_ico(&self) -> bool;
    fn is_bmp(&self) -> bool;
    fn is_pnm(&self) -> bool;
    fn is_xpm(&self) -> bool;
    fn is_xcf(&self) -> bool;
    fn is_pcx(&self) -> bool;
    fn is_gif(&self) -> bool;
    fn is_jpg(&self) -> bool;
    fn is_tif(&self) -> bool;
    fn is_png(&self) -> bool;
    fn is_lbm(&self) -> bool;
    fn is_xv(&self)  -> bool;
    fn is_webp(&self) -> bool;
}

impl<'a> ImageRWops for RWops<'a> {
    fn load(&self) -> Result<Surface, String> {
        let raw = unsafe {
            ffi::IMG_Load_RW(self.raw(), 0)
        };
        to_surface_result(raw)
    }
    fn load_typed(&self, _type: &str) -> Result<Surface, String> {
        let raw = unsafe {
            let c_type = CString::new(_type.as_bytes()).unwrap();
            ffi::IMG_LoadTyped_RW(self.raw(), 0, c_type.as_ptr() as *const _)
        };
        to_surface_result(raw)
    }

    fn load_cur(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadCUR_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_ico(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadICO_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_bmp(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadBMP_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pnm(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadPNM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xpm(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadXPM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xcf(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadXCF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_pcx(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadPCX_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_gif(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadGIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_jpg(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadJPG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tif(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadTIF_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_png(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadPNG_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_tga(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadTGA_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_lbm(&self) -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadLBM_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_xv(&self)  -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadXV_RW(self.raw()) };
        to_surface_result(raw)
    }
    fn load_webp(&self)  -> Result<Surface, String> {
        let raw = unsafe { ffi::IMG_LoadWEBP_RW(self.raw()) };
        to_surface_result(raw)
    }

    fn is_cur(&self) -> bool {
        unsafe { ffi::IMG_isCUR(self.raw()) == 1 }
    }
    fn is_ico(&self) -> bool {
        unsafe { ffi::IMG_isICO(self.raw()) == 1 }
    }
    fn is_bmp(&self) -> bool {
        unsafe { ffi::IMG_isBMP(self.raw()) == 1 }
    }
    fn is_pnm(&self) -> bool {
        unsafe { ffi::IMG_isPNM(self.raw()) == 1 }
    }
    fn is_xpm(&self) -> bool {
        unsafe { ffi::IMG_isXPM(self.raw()) == 1 }
    }
    fn is_xcf(&self) -> bool {
        unsafe { ffi::IMG_isXCF(self.raw()) == 1 }
    }
    fn is_pcx(&self) -> bool {
        unsafe { ffi::IMG_isPCX(self.raw()) == 1 }
    }
    fn is_gif(&self) -> bool {
        unsafe { ffi::IMG_isGIF(self.raw()) == 1 }
    }
    fn is_jpg(&self) -> bool {
        unsafe { ffi::IMG_isJPG(self.raw()) == 1 }
    }
    fn is_tif(&self) -> bool {
        unsafe { ffi::IMG_isTIF(self.raw()) == 1 }
    }
    fn is_png(&self) -> bool {
        unsafe { ffi::IMG_isPNG(self.raw()) == 1 }
    }
    fn is_lbm(&self) -> bool {
        unsafe { ffi::IMG_isLBM(self.raw()) == 1 }
    }
    fn is_xv(&self)  -> bool {
        unsafe { ffi::IMG_isXV(self.raw())  == 1 }
    }
    fn is_webp(&self) -> bool {
        unsafe { ffi::IMG_isWEBP(self.raw())  == 1 }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Joe Neeman.
//
// 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 std::fmt::{Debug, Formatter, Error as FmtError};
use std::u32;

pub type TableStateIdx = u32;

/// A DFA program implemented as a lookup table.
#[derive(Clone)]
pub struct TableInsts<Ret> {
    /// A `256 x num_instructions`-long table.
    pub table: Vec<TableStateIdx>,
    /// If `accept[st]` is not `None` then `st` is accepting, and `accept[st]` is the data
    /// to return.
    pub accept: Vec<Option<Ret>>,
    /// Same as `accept`, but applies only at the end of the input.
    pub accept_at_eoi: Vec<Option<Ret>>,
    /// If true, this program should only be used at the start of the input.
    pub is_anchored: bool,
}

impl<Ret: Debug> Debug for TableInsts<Ret> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        try!(f.write_fmt(format_args!("TableInsts ({} instructions):\n",
                                      self.accept.len())));

        for idx in 0..self.accept.len() {
            try!(f.write_fmt(format_args!("State {}:\n", idx)));
            try!(f.debug_map()
                .entries((0usize..256)
                    .map(|c| (c, self.table[idx * 256 + c]))
                    .filter(|x| x.1 != u32::MAX))
                .finish());
            try!(f.write_str("\n"));
        }

        try!(f.write_str("Accept: "));
        for idx in 0..self.accept.len() {
            if let Some(ref ret) = self.accept[idx] {
                try!(f.write_fmt(format_args!("{} -> {:?}, ", idx, ret)));
            }
        }

        try!(f.write_str("Accept_at_eoi: "));
        for idx in 0..self.accept_at_eoi.len() {
            if let Some(ref ret) = self.accept_at_eoi[idx] {
                try!(f.write_fmt(format_args!("{} -> {:?}, ", idx, ret)));
            }
        }
        Ok(())
    }
}

impl<Ret: Copy + Debug> TableInsts<Ret> {
    pub fn is_anchored(&self) -> bool {
        self.is_anchored
    }

    fn next_state(&self, state: usize, input: u8) -> Option<usize> {
        let next_state = self.table[state * 256 + input as usize];
        if next_state != u32::MAX {
            Some(next_state as usize)
        } else {
            None
        }
    }

    pub fn num_states(&self) -> usize {
        self.accept.len()
    }

    pub fn shortest_match_from(&self, input: &[u8], pos: usize, mut state: usize)
    -> Result<(usize, Ret), usize> {
        for pos in pos..input.len() {
            if let Some(ret) = self.accept[state] {
                return Ok((pos, ret));
            }

            // We've manually inlined next_state here, for better performance (measurably better
            // than using #[inline(always)]).
            let next_state = self.table[state * 256 + input[pos] as usize];
            if next_state != u32::MAX {
                state = next_state as usize;
            } else {
                return Err(pos);
            }
        }

        if let Some(ret) = self.accept_at_eoi[state] {
            Ok((input.len(), ret))
        } else {
            Err(input.len())
        }
    }

    pub fn longest_backward_match_from(&self, input: &[u8], pos: usize, mut state: usize)
    -> Option<(usize, Ret)> {
        let mut ret = None;
        for pos in (0..pos).rev() {
            if let Some(next_ret) = self.accept[state] {
                ret = Some((pos + 1, next_ret));
            }
            if let Some(next_state) = self.next_state(state, input[pos]) {
                state = next_state;
            } else {
                return ret;
            }
        }

        if let Some(end_ret) = self.accept_at_eoi[state] {
            Some((0, end_ret))
        } else {
            ret
        }
    }

    pub fn is_empty(&self) -> bool {
        self.num_states() == 0
    }
}

<commit_msg>Optimize the inner loop.<commit_after>// Copyright 2015-2016 Joe Neeman.
//
// 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 std::fmt::{Debug, Formatter, Error as FmtError};
use std::u32;

pub type TableStateIdx = u32;

/// A DFA program implemented as a lookup table.
#[derive(Clone)]
pub struct TableInsts<Ret> {
    /// A `256 x num_instructions`-long table.
    pub table: Vec<TableStateIdx>,
    /// If `accept[st]` is not `None` then `st` is accepting, and `accept[st]` is the data
    /// to return.
    pub accept: Vec<Option<Ret>>,
    /// Same as `accept`, but applies only at the end of the input.
    pub accept_at_eoi: Vec<Option<Ret>>,
    /// If true, this program should only be used at the start of the input.
    pub is_anchored: bool,
}

impl<Ret: Debug> Debug for TableInsts<Ret> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        try!(f.write_fmt(format_args!("TableInsts ({} instructions):\n",
                                      self.accept.len())));

        for idx in 0..self.accept.len() {
            try!(f.write_fmt(format_args!("State {}:\n", idx)));
            try!(f.debug_map()
                .entries((0usize..256)
                    .map(|c| (c, self.table[idx * 256 + c]))
                    .filter(|x| x.1 != u32::MAX))
                .finish());
            try!(f.write_str("\n"));
        }

        try!(f.write_str("Accept: "));
        for idx in 0..self.accept.len() {
            if let Some(ref ret) = self.accept[idx] {
                try!(f.write_fmt(format_args!("{} -> {:?}, ", idx, ret)));
            }
        }

        try!(f.write_str("Accept_at_eoi: "));
        for idx in 0..self.accept_at_eoi.len() {
            if let Some(ref ret) = self.accept_at_eoi[idx] {
                try!(f.write_fmt(format_args!("{} -> {:?}, ", idx, ret)));
            }
        }
        Ok(())
    }
}

impl<Ret: Copy + Debug> TableInsts<Ret> {
    pub fn is_anchored(&self) -> bool {
        self.is_anchored
    }

    fn next_state(&self, state: usize, input: u8) -> Option<usize> {
        let next_state = self.table[state * 256 + input as usize];
        if next_state != u32::MAX {
            Some(next_state as usize)
        } else {
            None
        }
    }

    pub fn num_states(&self) -> usize {
        self.accept.len()
    }

    pub fn shortest_match_from(&self, input: &[u8], pos: usize, state: usize)
    -> Result<(usize, Ret), usize> {
        let mut state = state as u32;

        for pos in pos..input.len() {
            if let Some(ret) = self.accept[state as usize] {
                return Ok((pos, ret));
            }

            // We've manually inlined next_state here, for better performance (measurably better
            // than using #[inline(always)]).
            state = self.table[(state * 256) as usize + input[pos] as usize];
            if state == u32::MAX {
                return Err(pos);
            }
        }

        if let Some(ret) = self.accept_at_eoi[state as usize] {
            Ok((input.len(), ret))
        } else {
            Err(input.len())
        }
    }

    pub fn longest_backward_match_from(&self, input: &[u8], pos: usize, mut state: usize)
    -> Option<(usize, Ret)> {
        let mut ret = None;
        for pos in (0..pos).rev() {
            if let Some(next_ret) = self.accept[state] {
                ret = Some((pos + 1, next_ret));
            }
            if let Some(next_state) = self.next_state(state, input[pos]) {
                state = next_state;
            } else {
                return ret;
            }
        }

        if let Some(end_ret) = self.accept_at_eoi[state] {
            Some((0, end_ret))
        } else {
            ret
        }
    }

    pub fn is_empty(&self) -> bool {
        self.num_states() == 0
    }
}

<|endoftext|>


<commit_before>use url::{Url};

use std::io::{Reader, IoResult};
use std::fmt::{Show, Formatter, FormatError};
use std::io::net::ip::SocketAddr;
use anymap::AnyMap;

use server_backend::method::Method;
use server_backend::header;
use server_backend::header::Headers;
use server_backend::server::Request as RawRequest;
use server_backend::mime::{Mime, Application, Json};
use server_backend::uri;

pub trait Request: Reader + Show + Send {
    fn remote_addr(&self) -> &SocketAddr;
    fn headers(&self) -> &Headers;
    fn method(&self) -> &Method;
    fn ext(&self) -> &AnyMap;
    fn ext_mut(&mut self) -> &mut AnyMap;
    fn url(&self) -> &Url;

    fn is_json_body(&self) -> bool {
        let content_type = self.headers().get::<header::common::ContentType>();
        if content_type.is_some() {
            match content_type.unwrap().0 {
                Mime(Application, Json, _) => true,
                _ => false
            }
        } else {
            false
        }
    }
}

#[deriving(Send)]
pub struct ServerRequest {
    pub url: Url,
    pub ext: AnyMap,
    raw: RawRequest
}

impl Request for ServerRequest {

    fn url(&self) -> &Url {
        return &self.url;
    }

    fn remote_addr(&self) -> &SocketAddr {
        return &self.raw.remote_addr;
    }

    fn headers(&self) -> &Headers {
        return &self.raw.headers;
    }

    fn method(&self) -> &Method {
        return &self.raw.method;
    }

    fn ext(&self) -> &AnyMap {
        &self.ext
    }

    fn ext_mut(&mut self) -> &mut AnyMap {
        &mut self.ext
    }
}

impl ServerRequest {
    pub fn new(url: Url, req: RawRequest) -> ServerRequest {
        ServerRequest {
            url: url,
            raw: req,
            ext: AnyMap::new(),
        }
    }

    pub fn wrap(req: RawRequest) -> Result<ServerRequest, String> {

        let url = match req.uri {
            uri::AbsolutePath(ref path) => {
                match req.headers.get::<header::common::Host>() {
                    Some(host) => format!("http://{}{}", host.0, path),
                    None => return Err("No HOST header specified in request".to_string())
                }
            },
            uri::AbsoluteUri(ref uri) => format!("{}", uri),
            _ => return Err("Unsupported request URI".to_string())
        };

        let parsed_url = match Url::parse(url.as_slice()) {
            Ok(url) => url,
            Err(parse_error) => return Err(format!("{}", parse_error))
        };

        Ok(ServerRequest::new(parsed_url, req))

    }
}

impl Reader for ServerRequest {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        self.raw.read(buf)
    }
}

impl Show for ServerRequest {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
        try!(writeln!(f, "ServerRequest ->"));
        try!(writeln!(f, "  url: {}", self.url));
        try!(writeln!(f, "  method: {}", self.method()));
        try!(writeln!(f, "  path: {}", self.url.path()));
        try!(writeln!(f, "  query: {}", self.url.query));
        try!(writeln!(f, "  remote_addr: {}", self.remote_addr()));
        Ok(())
    }
}<commit_msg>refactor: update to the new Hyper's `Host` format<commit_after>use url::{Url};

use std::io::{Reader, IoResult};
use std::fmt::{Show, Formatter, FormatError};
use std::io::net::ip::SocketAddr;
use anymap::AnyMap;

use server_backend::method::Method;
use server_backend::header;
use server_backend::header::Headers;
use server_backend::server::Request as RawRequest;
use server_backend::mime::{Mime, Application, Json};
use server_backend::uri;

pub trait Request: Reader + Show + Send {
    fn remote_addr(&self) -> &SocketAddr;
    fn headers(&self) -> &Headers;
    fn method(&self) -> &Method;
    fn ext(&self) -> &AnyMap;
    fn ext_mut(&mut self) -> &mut AnyMap;
    fn url(&self) -> &Url;

    fn is_json_body(&self) -> bool {
        let content_type = self.headers().get::<header::common::ContentType>();
        if content_type.is_some() {
            match content_type.unwrap().0 {
                Mime(Application, Json, _) => true,
                _ => false
            }
        } else {
            false
        }
    }
}

#[deriving(Send)]
pub struct ServerRequest {
    pub url: Url,
    pub ext: AnyMap,
    raw: RawRequest
}

impl Request for ServerRequest {

    fn url(&self) -> &Url {
        return &self.url;
    }

    fn remote_addr(&self) -> &SocketAddr {
        return &self.raw.remote_addr;
    }

    fn headers(&self) -> &Headers {
        return &self.raw.headers;
    }

    fn method(&self) -> &Method {
        return &self.raw.method;
    }

    fn ext(&self) -> &AnyMap {
        &self.ext
    }

    fn ext_mut(&mut self) -> &mut AnyMap {
        &mut self.ext
    }
}

impl ServerRequest {
    pub fn new(url: Url, req: RawRequest) -> ServerRequest {
        ServerRequest {
            url: url,
            raw: req,
            ext: AnyMap::new(),
        }
    }

    pub fn wrap(req: RawRequest) -> Result<ServerRequest, String> {

        let url = match req.uri {
            uri::AbsolutePath(ref path) => {
                match req.headers.get::<header::common::Host>() {
                    Some(host) => format!("http://{}{}", host.hostname, path),
                    None => return Err("No HOST header specified in request".to_string())
                }
            },
            uri::AbsoluteUri(ref uri) => format!("{}", uri),
            _ => return Err("Unsupported request URI".to_string())
        };

        let parsed_url = match Url::parse(url.as_slice()) {
            Ok(url) => url,
            Err(parse_error) => return Err(format!("{}", parse_error))
        };

        Ok(ServerRequest::new(parsed_url, req))

    }
}

impl Reader for ServerRequest {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        self.raw.read(buf)
    }
}

impl Show for ServerRequest {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
        try!(writeln!(f, "ServerRequest ->"));
        try!(writeln!(f, "  url: {}", self.url));
        try!(writeln!(f, "  method: {}", self.method()));
        try!(writeln!(f, "  path: {}", self.url.path()));
        try!(writeln!(f, "  query: {}", self.url.query));
        try!(writeln!(f, "  remote_addr: {}", self.remote_addr()));
        Ok(())
    }
}<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Virgile Andreani <virgile.andreani@anbuco.fr>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ updated to work with both UTF-8 and non-UTF-8 encodings
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype

#[macro_use]
extern crate uucore;
use clap::{crate_version, Arg, Command};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write};
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::{format_usage, InvalidEncodingHandling};

static NAME: &str = "unexpand";
static USAGE: &str = "{} [OPTION]... [FILE]...";
static SUMMARY: &str = "Convert blanks in each FILE to tabs, writing to standard output.\n\
                        With no FILE, or when FILE is -, read standard input.";

const DEFAULT_TABSTOP: usize = 8;

fn tabstops_parse(s: &str) -> Vec<usize> {
    let words = s.split(',');

    let nums = words
        .map(|sn| {
            sn.parse()
                .unwrap_or_else(|_| crash!(1, "{}\n", "tab size contains invalid character(s)"))
        })
        .collect::<Vec<usize>>();

    if nums.iter().any(|&n| n == 0) {
        crash!(1, "{}\n", "tab size cannot be 0");
    }

    if let (false, _) = nums
        .iter()
        .fold((true, 0), |(acc, last), &n| (acc && last <= n, n))
    {
        crash!(1, "{}\n", "tab sizes must be ascending");
    }

    nums
}

mod options {
    pub const FILE: &str = "file";
    pub const ALL: &str = "all";
    pub const FIRST_ONLY: &str = "first-only";
    pub const TABS: &str = "tabs";
    pub const NO_UTF8: &str = "no-utf8";
}

struct Options {
    files: Vec<String>,
    tabstops: Vec<usize>,
    aflag: bool,
    uflag: bool,
}

impl Options {
    fn new(matches: &clap::ArgMatches) -> Self {
        let tabstops = match matches.value_of(options::TABS) {
            None => vec![DEFAULT_TABSTOP],
            Some(s) => tabstops_parse(s),
        };

        let aflag = (matches.is_present(options::ALL) || matches.is_present(options::TABS))
            && !matches.is_present(options::FIRST_ONLY);
        let uflag = !matches.is_present(options::NO_UTF8);

        let files = match matches.value_of(options::FILE) {
            Some(v) => vec![v.to_string()],
            None => vec!["-".to_owned()],
        };

        Self {
            files,
            tabstops,
            aflag,
            uflag,
        }
    }
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    unexpand(&Options::new(&matches)).map_err_context(String::new)
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .name(NAME)
        .version(crate_version!())
        .override_usage(format_usage(USAGE))
        .about(SUMMARY)
        .infer_long_args(true)
        .arg(
            Arg::new(options::FILE)
                .hide(true)
                .multiple_occurrences(true)
                .value_hint(clap::ValueHint::FilePath)
        )
        .arg(
            Arg::new(options::ALL)
                .short('a')
                .long(options::ALL)
                .help("convert all blanks, instead of just initial blanks")
                .takes_value(false),
        )
        .arg(
            Arg::new(options::FIRST_ONLY)
                .long(options::FIRST_ONLY)
                .help("convert only leading sequences of blanks (overrides -a)")
                .takes_value(false),
        )
        .arg(
            Arg::new(options::TABS)
                .short('t')
                .long(options::TABS)
                .help("use comma separated LIST of tab positions or have tabs N characters apart instead of 8 (enables -a)")
                .takes_value(true)
        )
        .arg(
            Arg::new(options::NO_UTF8)
                .short('U')
                .long(options::NO_UTF8)
                .takes_value(false)
                .help("interpret input file as 8-bit ASCII rather than UTF-8"))
}

fn open(path: &str) -> BufReader<Box<dyn Read + 'static>> {
    let file_buf;
    if path == "-" {
        BufReader::new(Box::new(stdin()) as Box<dyn Read>)
    } else {
        file_buf = match File::open(&path) {
            Ok(a) => a,
            Err(e) => crash!(1, "{}: {}", path.maybe_quote(), e),
        };
        BufReader::new(Box::new(file_buf) as Box<dyn Read>)
    }
}

fn next_tabstop(tabstops: &[usize], col: usize) -> Option<usize> {
    if tabstops.len() == 1 {
        Some(tabstops[0] - col % tabstops[0])
    } else {
        // find next larger tab
        // if there isn't one in the list, tab becomes a single space
        tabstops.iter().find(|&&t| t > col).map(|t| t - col)
    }
}

fn write_tabs(
    output: &mut BufWriter<Stdout>,
    tabstops: &[usize],
    mut scol: usize,
    col: usize,
    prevtab: bool,
    init: bool,
    amode: bool,
) {
    // This conditional establishes the following:
    // We never turn a single space before a non-blank into
    // a tab, unless it's at the start of the line.
    let ai = init || amode;
    if (ai && !prevtab && col > scol + 1) || (col > scol && (init || ai && prevtab)) {
        while let Some(nts) = next_tabstop(tabstops, scol) {
            if col < scol + nts {
                break;
            }

            crash_if_err!(1, output.write_all(b"\t"));
            scol += nts;
        }
    }

    while col > scol {
        crash_if_err!(1, output.write_all(b" "));
        scol += 1;
    }
}

#[derive(PartialEq, Eq, Debug)]
enum CharType {
    Backspace,
    Space,
    Tab,
    Other,
}

fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usize) {
    let (ctype, cwidth, nbytes) = if uflag {
        let nbytes = char::from(buf[byte]).len_utf8();

        if byte + nbytes > buf.len() {
            // make sure we don't overrun the buffer because of invalid UTF-8
            (CharType::Other, 1, 1)
        } else if let Ok(t) = from_utf8(&buf[byte..byte + nbytes]) {
            // Now that we think it's UTF-8, figure out what kind of char it is
            match t.chars().next() {
                Some(' ') => (CharType::Space, 0, 1),
                Some('\t') => (CharType::Tab, 0, 1),
                Some('\x08') => (CharType::Backspace, 0, 1),
                Some(c) => (
                    CharType::Other,
                    UnicodeWidthChar::width(c).unwrap_or(0),
                    nbytes,
                ),
                None => {
                    // invalid char snuck past the utf8_validation_iterator somehow???
                    (CharType::Other, 1, 1)
                }
            }
        } else {
            // otherwise, it's not valid
            (CharType::Other, 1, 1) // implicit assumption: non-UTF8 char has display width 1
        }
    } else {
        (
            match buf[byte] {
                // always take exactly 1 byte in strict ASCII mode
                0x20 => CharType::Space,
                0x09 => CharType::Tab,
                0x08 => CharType::Backspace,
                _ => CharType::Other,
            },
            1,
            1,
        )
    };

    (ctype, cwidth, nbytes)
}

fn unexpand(options: &Options) -> std::io::Result<()> {
    let mut output = BufWriter::new(stdout());
    let ts = &options.tabstops[..];
    let mut buf = Vec::new();
    let lastcol = if ts.len() > 1 { *ts.last().unwrap() } else { 0 };

    for file in &options.files {
        let mut fh = open(file);

        while match fh.read_until(b'\n', &mut buf) {
            Ok(s) => s > 0,
            Err(_) => !buf.is_empty(),
        } {
            let mut byte = 0; // offset into the buffer
            let mut col = 0; // the current column
            let mut scol = 0; // the start col for the current span, i.e., the already-printed width
            let mut init = true; // are we at the start of the line?
            let mut pctype = CharType::Other;

            while byte < buf.len() {
                // when we have a finite number of columns, never convert past the last column
                if lastcol > 0 && col >= lastcol {
                    write_tabs(
                        &mut output,
                        ts,
                        scol,
                        col,
                        pctype == CharType::Tab,
                        init,
                        true,
                    );
                    output.write_all(&buf[byte..])?;
                    scol = col;
                    break;
                }

                // figure out how big the next char is, if it's UTF-8
                let (ctype, cwidth, nbytes) = next_char_info(options.uflag, &buf, byte);

                // now figure out how many columns this char takes up, and maybe print it
                let tabs_buffered = init || options.aflag;
                match ctype {
                    CharType::Space | CharType::Tab => {
                        // compute next col, but only write space or tab chars if not buffering
                        col += if ctype == CharType::Space {
                            1
                        } else {
                            next_tabstop(ts, col).unwrap_or(1)
                        };

                        if !tabs_buffered {
                            output.write_all(&buf[byte..byte + nbytes])?;
                            scol = col; // now printed up to this column
                        }
                    }
                    CharType::Other | CharType::Backspace => {
                        // always
                        write_tabs(
                            &mut output,
                            ts,
                            scol,
                            col,
                            pctype == CharType::Tab,
                            init,
                            options.aflag,
                        );
                        init = false; // no longer at the start of a line
                        col = if ctype == CharType::Other {
                            // use computed width
                            col + cwidth
                        } else if col > 0 {
                            // Backspace case, but only if col > 0
                            col - 1
                        } else {
                            0
                        };
                        output.write_all(&buf[byte..byte + nbytes])?;
                        scol = col; // we've now printed up to this column
                    }
                }

                byte += nbytes; // move on to next char
                pctype = ctype; // save the previous type
            }

            // write out anything remaining
            write_tabs(
                &mut output,
                ts,
                scol,
                col,
                pctype == CharType::Tab,
                init,
                true,
            );
            output.flush()?;
            buf.truncate(0); // clear out the buffer
        }
    }
    output.flush()
}
<commit_msg>unexpand: set value name of arg<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Virgile Andreani <virgile.andreani@anbuco.fr>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ updated to work with both UTF-8 and non-UTF-8 encodings
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) nums aflag uflag scol prevtab amode ctype cwidth nbytes lastcol pctype

#[macro_use]
extern crate uucore;
use clap::{crate_version, Arg, Command};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write};
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::{format_usage, InvalidEncodingHandling};

static NAME: &str = "unexpand";
static USAGE: &str = "{} [OPTION]... [FILE]...";
static SUMMARY: &str = "Convert blanks in each FILE to tabs, writing to standard output.\n\
                        With no FILE, or when FILE is -, read standard input.";

const DEFAULT_TABSTOP: usize = 8;

fn tabstops_parse(s: &str) -> Vec<usize> {
    let words = s.split(',');

    let nums = words
        .map(|sn| {
            sn.parse()
                .unwrap_or_else(|_| crash!(1, "{}\n", "tab size contains invalid character(s)"))
        })
        .collect::<Vec<usize>>();

    if nums.iter().any(|&n| n == 0) {
        crash!(1, "{}\n", "tab size cannot be 0");
    }

    if let (false, _) = nums
        .iter()
        .fold((true, 0), |(acc, last), &n| (acc && last <= n, n))
    {
        crash!(1, "{}\n", "tab sizes must be ascending");
    }

    nums
}

mod options {
    pub const FILE: &str = "file";
    pub const ALL: &str = "all";
    pub const FIRST_ONLY: &str = "first-only";
    pub const TABS: &str = "tabs";
    pub const NO_UTF8: &str = "no-utf8";
}

struct Options {
    files: Vec<String>,
    tabstops: Vec<usize>,
    aflag: bool,
    uflag: bool,
}

impl Options {
    fn new(matches: &clap::ArgMatches) -> Self {
        let tabstops = match matches.value_of(options::TABS) {
            None => vec![DEFAULT_TABSTOP],
            Some(s) => tabstops_parse(s),
        };

        let aflag = (matches.is_present(options::ALL) || matches.is_present(options::TABS))
            && !matches.is_present(options::FIRST_ONLY);
        let uflag = !matches.is_present(options::NO_UTF8);

        let files = match matches.value_of(options::FILE) {
            Some(v) => vec![v.to_string()],
            None => vec!["-".to_owned()],
        };

        Self {
            files,
            tabstops,
            aflag,
            uflag,
        }
    }
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::Ignore)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    unexpand(&Options::new(&matches)).map_err_context(String::new)
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .name(NAME)
        .version(crate_version!())
        .override_usage(format_usage(USAGE))
        .about(SUMMARY)
        .infer_long_args(true)
        .arg(
            Arg::new(options::FILE)
                .hide(true)
                .multiple_occurrences(true)
                .value_hint(clap::ValueHint::FilePath)
        )
        .arg(
            Arg::new(options::ALL)
                .short('a')
                .long(options::ALL)
                .help("convert all blanks, instead of just initial blanks")
                .takes_value(false),
        )
        .arg(
            Arg::new(options::FIRST_ONLY)
                .long(options::FIRST_ONLY)
                .help("convert only leading sequences of blanks (overrides -a)")
                .takes_value(false),
        )
        .arg(
            Arg::new(options::TABS)
                .short('t')
                .long(options::TABS)
                .help("use comma separated LIST of tab positions or have tabs N characters apart instead of 8 (enables -a)")
                .takes_value(true)
                .value_name("N, LIST")
        )
        .arg(
            Arg::new(options::NO_UTF8)
                .short('U')
                .long(options::NO_UTF8)
                .takes_value(false)
                .help("interpret input file as 8-bit ASCII rather than UTF-8"))
}

fn open(path: &str) -> BufReader<Box<dyn Read + 'static>> {
    let file_buf;
    if path == "-" {
        BufReader::new(Box::new(stdin()) as Box<dyn Read>)
    } else {
        file_buf = match File::open(&path) {
            Ok(a) => a,
            Err(e) => crash!(1, "{}: {}", path.maybe_quote(), e),
        };
        BufReader::new(Box::new(file_buf) as Box<dyn Read>)
    }
}

fn next_tabstop(tabstops: &[usize], col: usize) -> Option<usize> {
    if tabstops.len() == 1 {
        Some(tabstops[0] - col % tabstops[0])
    } else {
        // find next larger tab
        // if there isn't one in the list, tab becomes a single space
        tabstops.iter().find(|&&t| t > col).map(|t| t - col)
    }
}

fn write_tabs(
    output: &mut BufWriter<Stdout>,
    tabstops: &[usize],
    mut scol: usize,
    col: usize,
    prevtab: bool,
    init: bool,
    amode: bool,
) {
    // This conditional establishes the following:
    // We never turn a single space before a non-blank into
    // a tab, unless it's at the start of the line.
    let ai = init || amode;
    if (ai && !prevtab && col > scol + 1) || (col > scol && (init || ai && prevtab)) {
        while let Some(nts) = next_tabstop(tabstops, scol) {
            if col < scol + nts {
                break;
            }

            crash_if_err!(1, output.write_all(b"\t"));
            scol += nts;
        }
    }

    while col > scol {
        crash_if_err!(1, output.write_all(b" "));
        scol += 1;
    }
}

#[derive(PartialEq, Eq, Debug)]
enum CharType {
    Backspace,
    Space,
    Tab,
    Other,
}

fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usize) {
    let (ctype, cwidth, nbytes) = if uflag {
        let nbytes = char::from(buf[byte]).len_utf8();

        if byte + nbytes > buf.len() {
            // make sure we don't overrun the buffer because of invalid UTF-8
            (CharType::Other, 1, 1)
        } else if let Ok(t) = from_utf8(&buf[byte..byte + nbytes]) {
            // Now that we think it's UTF-8, figure out what kind of char it is
            match t.chars().next() {
                Some(' ') => (CharType::Space, 0, 1),
                Some('\t') => (CharType::Tab, 0, 1),
                Some('\x08') => (CharType::Backspace, 0, 1),
                Some(c) => (
                    CharType::Other,
                    UnicodeWidthChar::width(c).unwrap_or(0),
                    nbytes,
                ),
                None => {
                    // invalid char snuck past the utf8_validation_iterator somehow???
                    (CharType::Other, 1, 1)
                }
            }
        } else {
            // otherwise, it's not valid
            (CharType::Other, 1, 1) // implicit assumption: non-UTF8 char has display width 1
        }
    } else {
        (
            match buf[byte] {
                // always take exactly 1 byte in strict ASCII mode
                0x20 => CharType::Space,
                0x09 => CharType::Tab,
                0x08 => CharType::Backspace,
                _ => CharType::Other,
            },
            1,
            1,
        )
    };

    (ctype, cwidth, nbytes)
}

fn unexpand(options: &Options) -> std::io::Result<()> {
    let mut output = BufWriter::new(stdout());
    let ts = &options.tabstops[..];
    let mut buf = Vec::new();
    let lastcol = if ts.len() > 1 { *ts.last().unwrap() } else { 0 };

    for file in &options.files {
        let mut fh = open(file);

        while match fh.read_until(b'\n', &mut buf) {
            Ok(s) => s > 0,
            Err(_) => !buf.is_empty(),
        } {
            let mut byte = 0; // offset into the buffer
            let mut col = 0; // the current column
            let mut scol = 0; // the start col for the current span, i.e., the already-printed width
            let mut init = true; // are we at the start of the line?
            let mut pctype = CharType::Other;

            while byte < buf.len() {
                // when we have a finite number of columns, never convert past the last column
                if lastcol > 0 && col >= lastcol {
                    write_tabs(
                        &mut output,
                        ts,
                        scol,
                        col,
                        pctype == CharType::Tab,
                        init,
                        true,
                    );
                    output.write_all(&buf[byte..])?;
                    scol = col;
                    break;
                }

                // figure out how big the next char is, if it's UTF-8
                let (ctype, cwidth, nbytes) = next_char_info(options.uflag, &buf, byte);

                // now figure out how many columns this char takes up, and maybe print it
                let tabs_buffered = init || options.aflag;
                match ctype {
                    CharType::Space | CharType::Tab => {
                        // compute next col, but only write space or tab chars if not buffering
                        col += if ctype == CharType::Space {
                            1
                        } else {
                            next_tabstop(ts, col).unwrap_or(1)
                        };

                        if !tabs_buffered {
                            output.write_all(&buf[byte..byte + nbytes])?;
                            scol = col; // now printed up to this column
                        }
                    }
                    CharType::Other | CharType::Backspace => {
                        // always
                        write_tabs(
                            &mut output,
                            ts,
                            scol,
                            col,
                            pctype == CharType::Tab,
                            init,
                            options.aflag,
                        );
                        init = false; // no longer at the start of a line
                        col = if ctype == CharType::Other {
                            // use computed width
                            col + cwidth
                        } else if col > 0 {
                            // Backspace case, but only if col > 0
                            col - 1
                        } else {
                            0
                        };
                        output.write_all(&buf[byte..byte + nbytes])?;
                        scol = col; // we've now printed up to this column
                    }
                }

                byte += nbytes; // move on to next char
                pctype = ctype; // save the previous type
            }

            // write out anything remaining
            write_tabs(
                &mut output,
                ts,
                scol,
                col,
                pctype == CharType::Tab,
                init,
                true,
            );
            output.flush()?;
            buf.truncate(0); // clear out the buffer
        }
    }
    output.flush()
}
<|endoftext|>