<commit_before>use std::fmt::Write;

use crate::token::Token;
use crate::token::Token::*;
use crate::IsbfcIR;

#[cfg(target_os = "redox")]
extern crate syscall;

#[derive(Default)]
struct CompileState {
    output: String,
    loopnum: i32,
    ifnum: i32,
    outbuffsize: i32,
}

/// Takes an offset from the current cell, and returns a string in assembly code
/// representing the register or memory region it is stored in
fn offset_to_operand(offset: i32) -> String {
    if offset == 0 {
        "%r12".to_string()
    } else {
        format!("{}(%rbx)", (offset * 8))
    }
}

fn compile_iter(state: &mut CompileState, tokens: &Vec<Token>, level: usize) {
    let indent = String::from_utf8(vec![b' '; level * 4]).unwrap();
    /// Add line of assembly to output, with indentation and newline, using
    /// format! syntax.
    macro_rules! push_asm {
        ($fmt:expr) => {
            (writeln!(&mut state.output, concat!("{}", $fmt),
                   &indent)).unwrap()
        };
        ($fmt:expr, $($arg:tt)*) => {
            (writeln!(&mut state.output, concat!("{}", $fmt),
                   &indent,
                   $($arg)*)).unwrap()
        };
    }

    let mut outbuffpos = 0;
    for token in tokens {
        match *token {
            Add(offset, value) => {
                let dest = offset_to_operand(offset);
                if value == 1 && dest == "%r12" {
                    push_asm!("inc %r12");
                } else if value >= 1 {
                    push_asm!("addq ${}, {}", value, dest);
                } else if value == -1 && dest == "%r12" {
                    push_asm!("dec %r12");
                } else if value <= -1 {
                    push_asm!("subq ${}, {}", -value, dest);
                }
            }
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = offset_to_operand(src_idx);
                let dest = offset_to_operand(dest_idx);

                if mul != -1 && mul != 1 {
                    push_asm!("movq {}, %rax", src);
                    push_asm!("movq ${}, %rdx", mul.abs());
                    push_asm!("mulq %rdx");
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    push_asm!("movq {}, %rax", src);
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    push_asm!("addq {}, {}", src, dest);
                } else {
                    push_asm!("subq {}, {}", src, dest);
                }
            }
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    push_asm!("xor %r12, %r12");
                } else {
                    push_asm!("movq ${}, {}", value, offset_to_operand(offset));
                }
            }
            Move(offset) => {
                if offset != 0 {
                    push_asm!("movq %r12, (%rbx)");
                    push_asm!("{add_sub} ${shift}, %rbx",
                              add_sub = if offset > 0 { "addq" } else { "subq" },
                              shift = offset.abs() * 8);
                    push_asm!("movq (%rbx), %r12");
                }
            }
            Loop(ref content) => {
                state.loopnum += 1;
                let curloop = state.loopnum;
                push_asm!("jmp endloop{}", curloop);
                push_asm!("loop{}:", curloop);

                compile_iter(state, &content, level + 1);

                push_asm!("endloop{}:", curloop);
                push_asm!("test %r12, %r12");
                push_asm!("jnz loop{}", curloop);
            }
            If(offset, ref content) => {
                state.ifnum += 1;
                let curif = state.ifnum;
                if offset == 0 {
                    push_asm!("test %r12, %r12");
                } else {
                    push_asm!("cmpq $0, {}(%rbx)", offset * 8);
                }
                push_asm!("jz endif{}", curif);

                compile_iter(state, &content, level + 1);

                push_asm!("endif{}:\n", curif);
            }
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                state.loopnum += 1;
                push_asm!("movq %r12, (%rbx)");
                push_asm!("jmp endloop{}", state.loopnum);
                push_asm!("loop{}:", state.loopnum);
                push_asm!("{add_sub} ${shift}, %rbx",
                          add_sub = if offset > 0 { "addq" } else { "subq" },
                          shift = offset.abs() * 8);
                push_asm!("endloop{}:", state.loopnum);
                push_asm!("cmp $0, (%rbx)");
                push_asm!("jnz loop{}", state.loopnum);
                push_asm!("movq (%rbx), %r12");
            }
            Input => {
                push_asm!("");

				#[cfg(target_os = "redox")]
				{
					push_asm!("movq ${}, %rax", syscall::SYS_READ);
					push_asm!("movq %rbx, %rcx");
					push_asm!("xor %rbx, %rbx");
					push_asm!("movq $1, %rdx");
					push_asm!("int $0x80");
					push_asm!("movq %rcx, %rbx");
				}

				#[cfg(not(target_os = "redox"))]
				{
					push_asm!("xor %rax, %rax");
					push_asm!("xor %rdi, %rdi");
					push_asm!("movq %rbx, %rsi");
					push_asm!("movq $1, %rdx");
					push_asm!("syscall");
				}

                push_asm!("movq (%rbx), %r12\n");
            }
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    push_asm!("movq %r12, {}", outaddr);
                } else {
                    push_asm!("movq {}(%rbx), %rax", offset * 8);
                    push_asm!("movq %rax, {}", outaddr);
                }
                if add > 0 {
                    push_asm!("addb ${}, {}", add, outaddr);
                } else if add < 0 {
                    push_asm!("subb ${}, {}", -add, outaddr);
                }
                outbuffpos += 1;
            }
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                push_asm!("movq ${}, {}", value, outaddr);
                outbuffpos += 1;
            }
            Output => {
				#[cfg(target_os = "redox")]
				{
					push_asm!("movq ${}, %rax", syscall::SYS_WRITE);
					push_asm!("movq %rbx, %r11");
					push_asm!("movq $1, %rbx");
					push_asm!("movq $strbuff, %rcx");
					push_asm!("movq ${}, %rdx", outbuffpos);
					push_asm!("int $0x80");
					push_asm!("movq %r11, %rbx\n");
				}

				#[cfg(not(target_os = "redox"))]
				{
					push_asm!("movq $1, %rax");
					push_asm!("movq $1, %rdi");
					push_asm!("movq $strbuff, %rsi");
					push_asm!("movq ${}, %rdx", outbuffpos);
					push_asm!("syscall\n");
				}

                if state.outbuffsize < outbuffpos + 8 {
                    state.outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }
}

impl IsbfcIR {
    /// Compiles the intermediate representation to x86_64 Linux assembly
    /// returning a String
    pub fn compile(&self, tape_size: i32) -> String {
        let mut state = CompileState::default();

        compile_iter(&mut state, &self.tokens, 1);

        // Exit syscall
		#[cfg(not(target_os = "redox"))]
        let exit = concat!("    movq $60, %rax\n",
                           "    movq $0, %rdi\n",
                           "    syscall\n");
		#[cfg(target_os = "redox")]
        let exit = format!(concat!("    movq ${}, %rax\n",
                                   "    movq $0, %rdi\n",
                                   "    int $0x80\n"),
                           syscall::SYS_EXIT);

        format!(concat!(".section .bss\n",
                        "    .lcomm strbuff, {outbuffsize}\n",
                        "    .lcomm mem, {}\n",
                        "    .set startidx, mem + {}\n",
                        ".section .text\n",
                        ".global _start\n",
                        "_start:\n",
                        "    xor %r12, %r12\n",
                        "    movq $startidx, %rbx\n\n",
                        "{}\n",
                        "{exit_syscall}\n",
),
                tape_size,
                tape_size / 2,
                state.output,
                outbuffsize = state.outbuffsize,
                exit_syscall = exit)
    }
}
<commit_msg>Store 'level' in CompileState<commit_after>use std::fmt::Write;

use crate::token::Token;
use crate::token::Token::*;
use crate::IsbfcIR;

#[cfg(target_os = "redox")]
extern crate syscall;

#[derive(Default)]
struct CompileState {
    output: String,
    loopnum: i32,
    ifnum: i32,
    outbuffsize: i32,
    level: usize,
}

/// Takes an offset from the current cell, and returns a string in assembly code
/// representing the register or memory region it is stored in
fn offset_to_operand(offset: i32) -> String {
    if offset == 0 {
        "%r12".to_string()
    } else {
        format!("{}(%rbx)", (offset * 8))
    }
}

fn compile_iter(state: &mut CompileState, tokens: &Vec<Token>) {
    state.level += 1;

    let indent = String::from_utf8(vec![b' '; state.level * 4]).unwrap();
    /// Add line of assembly to output, with indentation and newline, using
    /// format! syntax.
    macro_rules! push_asm {
        ($fmt:expr) => {
            (writeln!(&mut state.output, concat!("{}", $fmt),
                   &indent)).unwrap()
        };
        ($fmt:expr, $($arg:tt)*) => {
            (writeln!(&mut state.output, concat!("{}", $fmt),
                   &indent,
                   $($arg)*)).unwrap()
        };
    }

    let mut outbuffpos = 0;
    for token in tokens {
        match *token {
            Add(offset, value) => {
                let dest = offset_to_operand(offset);
                if value == 1 && dest == "%r12" {
                    push_asm!("inc %r12");
                } else if value >= 1 {
                    push_asm!("addq ${}, {}", value, dest);
                } else if value == -1 && dest == "%r12" {
                    push_asm!("dec %r12");
                } else if value <= -1 {
                    push_asm!("subq ${}, {}", -value, dest);
                }
            }
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = offset_to_operand(src_idx);
                let dest = offset_to_operand(dest_idx);

                if mul != -1 && mul != 1 {
                    push_asm!("movq {}, %rax", src);
                    push_asm!("movq ${}, %rdx", mul.abs());
                    push_asm!("mulq %rdx");
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    push_asm!("movq {}, %rax", src);
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    push_asm!("addq {}, {}", src, dest);
                } else {
                    push_asm!("subq {}, {}", src, dest);
                }
            }
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    push_asm!("xor %r12, %r12");
                } else {
                    push_asm!("movq ${}, {}", value, offset_to_operand(offset));
                }
            }
            Move(offset) => {
                if offset != 0 {
                    push_asm!("movq %r12, (%rbx)");
                    push_asm!("{add_sub} ${shift}, %rbx",
                              add_sub = if offset > 0 { "addq" } else { "subq" },
                              shift = offset.abs() * 8);
                    push_asm!("movq (%rbx), %r12");
                }
            }
            Loop(ref content) => {
                state.loopnum += 1;
                let curloop = state.loopnum;
                push_asm!("jmp endloop{}", curloop);
                push_asm!("loop{}:", curloop);

                compile_iter(state, &content);

                push_asm!("endloop{}:", curloop);
                push_asm!("test %r12, %r12");
                push_asm!("jnz loop{}", curloop);
            }
            If(offset, ref content) => {
                state.ifnum += 1;
                let curif = state.ifnum;
                if offset == 0 {
                    push_asm!("test %r12, %r12");
                } else {
                    push_asm!("cmpq $0, {}(%rbx)", offset * 8);
                }
                push_asm!("jz endif{}", curif);

                compile_iter(state, &content);

                push_asm!("endif{}:\n", curif);
            }
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                state.loopnum += 1;
                push_asm!("movq %r12, (%rbx)");
                push_asm!("jmp endloop{}", state.loopnum);
                push_asm!("loop{}:", state.loopnum);
                push_asm!("{add_sub} ${shift}, %rbx",
                          add_sub = if offset > 0 { "addq" } else { "subq" },
                          shift = offset.abs() * 8);
                push_asm!("endloop{}:", state.loopnum);
                push_asm!("cmp $0, (%rbx)");
                push_asm!("jnz loop{}", state.loopnum);
                push_asm!("movq (%rbx), %r12");
            }
            Input => {
                push_asm!("");

				#[cfg(target_os = "redox")]
				{
					push_asm!("movq ${}, %rax", syscall::SYS_READ);
					push_asm!("movq %rbx, %rcx");
					push_asm!("xor %rbx, %rbx");
					push_asm!("movq $1, %rdx");
					push_asm!("int $0x80");
					push_asm!("movq %rcx, %rbx");
				}

				#[cfg(not(target_os = "redox"))]
				{
					push_asm!("xor %rax, %rax");
					push_asm!("xor %rdi, %rdi");
					push_asm!("movq %rbx, %rsi");
					push_asm!("movq $1, %rdx");
					push_asm!("syscall");
				}

                push_asm!("movq (%rbx), %r12\n");
            }
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    push_asm!("movq %r12, {}", outaddr);
                } else {
                    push_asm!("movq {}(%rbx), %rax", offset * 8);
                    push_asm!("movq %rax, {}", outaddr);
                }
                if add > 0 {
                    push_asm!("addb ${}, {}", add, outaddr);
                } else if add < 0 {
                    push_asm!("subb ${}, {}", -add, outaddr);
                }
                outbuffpos += 1;
            }
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                push_asm!("movq ${}, {}", value, outaddr);
                outbuffpos += 1;
            }
            Output => {
				#[cfg(target_os = "redox")]
				{
					push_asm!("movq ${}, %rax", syscall::SYS_WRITE);
					push_asm!("movq %rbx, %r11");
					push_asm!("movq $1, %rbx");
					push_asm!("movq $strbuff, %rcx");
					push_asm!("movq ${}, %rdx", outbuffpos);
					push_asm!("int $0x80");
					push_asm!("movq %r11, %rbx\n");
				}

				#[cfg(not(target_os = "redox"))]
				{
					push_asm!("movq $1, %rax");
					push_asm!("movq $1, %rdi");
					push_asm!("movq $strbuff, %rsi");
					push_asm!("movq ${}, %rdx", outbuffpos);
					push_asm!("syscall\n");
				}

                if state.outbuffsize < outbuffpos + 8 {
                    state.outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    state.level -= 1;
}

impl IsbfcIR {
    /// Compiles the intermediate representation to x86_64 Linux assembly
    /// returning a String
    pub fn compile(&self, tape_size: i32) -> String {
        let mut state = CompileState::default();

        compile_iter(&mut state, &self.tokens);

        // Exit syscall
		#[cfg(not(target_os = "redox"))]
        let exit = concat!("    movq $60, %rax\n",
                           "    movq $0, %rdi\n",
                           "    syscall\n");
		#[cfg(target_os = "redox")]
        let exit = format!(concat!("    movq ${}, %rax\n",
                                   "    movq $0, %rdi\n",
                                   "    int $0x80\n"),
                           syscall::SYS_EXIT);

        format!(concat!(".section .bss\n",
                        "    .lcomm strbuff, {outbuffsize}\n",
                        "    .lcomm mem, {}\n",
                        "    .set startidx, mem + {}\n",
                        ".section .text\n",
                        ".global _start\n",
                        "_start:\n",
                        "    xor %r12, %r12\n",
                        "    movq $startidx, %rbx\n\n",
                        "{}\n",
                        "{exit_syscall}\n",
),
                tape_size,
                tape_size / 2,
                state.output,
                outbuffsize = state.outbuffsize,
                exit_syscall = exit)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::marker;

/// Part of a multi-segment transfer.
///
/// `Segment`s are transferred using the [`transfer_segments`] method.
///
/// Construct a new `Segment` for a simultaneous (full-duplex) read/write
/// transfer using [`new`]. For read operations without any outgoing data,
/// use [`with_read`]. For write operations where any incoming data
/// should be discarded, use [`with_write`].
///
/// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
/// [`with_read`]: #method.with_read
/// [`with_write`]: #method.with_write
/// [`new`]: #method.new
#[derive(PartialEq, Copy, Clone)]
#[repr(C)]
pub struct Segment<'a, 'b> {
    // Pointer to write buffer, or 0.
    tx_buf: u64,
    // Pointer to read buffer, or 0.
    rx_buf: u64,
    // Number of bytes to transfer in this segment.
    len: u32,
    // Set a different clock speed for this segment. Default = 0.
    speed_hz: u32,
    // Add a delay before the (optional) SS change and the next segment.
    delay_usecs: u16,
    // Bits per word for this segment. The Pi only supports 8 bits (or 9 bits in LoSSI mode). Default = 0.
    bits_per_word: u8,
    // Set to 1 to briefly set SS inactive between this segment and the next. If this is the last segment, keep SS active.
    cs_change: u8,
    // Number of outgoing lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    tx_nbits: u8,
    // Number of incoming lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    rx_nbits: u8,
    // Padding. Set to 0 for forward compatibility.
    pad: u16,
    // Zero-sized variable used to link this struct to the read buffer lifetime.
    read_buffer_lifetime: marker::PhantomData<&'a mut [u8]>,
    // Zero-sized variable used to link this struct to the write buffer lifetime.
    write_buffer_lifetime: marker::PhantomData<&'b [u8]>,
}

impl<'a, 'b> Segment<'a, 'b> {
    /// Constructs a new `Segment` with the default settings, and configures it
    /// for a simultaneous (full-duplex) read/write transfer.
    ///
    /// For `Segment`s that only require either a read or write operation, call
    /// [`with_read`] or [`with_write`] instead of `new`.
    ///
    /// [`transfer_segments`] will only transfer as many bytes as the shortest of
    /// the two buffers contains.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    /// [`Spi`]: struct.Spi.html
    /// [`with_read`]: #method.with_read
    /// [`with_write`]: #method.with_write
    pub fn new(read_buffer: &'a mut [u8], write_buffer: &'b [u8]) -> Segment<'a, 'b> {
        Segment::with_settings(Some(read_buffer), Some(write_buffer), 0, 0, 0, false)
    }

    /// Constructs a new `Segment` with the default settings, and configures it
    /// for a read operation.
    ///
    /// Incoming data from the slave device is written to `read_buffer`. The total
    /// number of bytes read depends on the length of `read_buffer`.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn with_read(read_buffer: &mut [u8]) -> Segment<'_, '_> {
        Segment::with_settings(Some(read_buffer), None, 0, 0, 0, false)
    }

    /// Constructs a new `Segment` with the default settings, and configures it
    /// for a write operation.
    ///
    /// Outgoing data from `write_buffer` is sent to the slave device. Any
    /// incoming data is discarded.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn with_write(write_buffer: &[u8]) -> Segment<'_, '_> {
        Segment::with_settings(None, Some(write_buffer), 0, 0, 0, false)
    }

    /// Constructs a new `Segment` with the specified settings.
    ///
    /// These settings override the values set for [`Spi`], and are only used
    /// for this specific segment.
    ///
    /// If `read_buffer` is set to `None`, any incoming data is discarded.
    ///
    /// If `write_buffer` is set to `None`, a zero-value byte will be sent for every
    /// byte read.
    ///
    /// If both `read_buffer` and `write_buffer` are specified, [`transfer_segments`]
    /// will only transfer as many bytes as the shortest of the two buffers contains.
    ///
    /// `clock_speed` sets a custom clock speed in hertz (Hz).
    ///
    /// `delay` sets a delay in microseconds (µs).
    ///
    /// `bits_per_word` sets the number of bits per word. The Raspberry Pi currently only supports 8 bits per word.
    ///
    /// `ss_change` changes how Slave Select behaves in between two segments (toggle SS), or after the final segment (keep SS active).
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    /// [`Spi`]: struct.Spi.html
    pub fn with_settings(
        read_buffer: Option<&'a mut [u8]>,
        write_buffer: Option<&'b [u8]>,
        clock_speed: u32,
        delay: u16,
        bits_per_word: u8,
        ss_change: bool,
    ) -> Segment<'a, 'b> {
        // Len will contain the length of the shortest of the supplied buffers
        let mut len: u32 = 0;

        let tx_buf = if let Some(buffer) = write_buffer {
            len = buffer.len() as u32;
            buffer.as_ptr() as u64
        } else {
            0
        };

        let rx_buf = if let Some(buffer) = read_buffer {
            if (len > buffer.len() as u32) || tx_buf == 0 {
                len = buffer.len() as u32;
            }
            buffer.as_ptr() as u64
        } else {
            0
        };

        Segment {
            tx_buf,
            rx_buf,
            len,
            speed_hz: clock_speed,
            delay_usecs: delay,
            bits_per_word,
            cs_change: ss_change as u8,
            tx_nbits: 0,
            rx_nbits: 0,
            pad: 0,
            read_buffer_lifetime: marker::PhantomData,
            write_buffer_lifetime: marker::PhantomData,
        }
    }

    /// Returns the number of bytes that will be transferred.
    ///
    /// If both a read buffer and write buffer are supplied, [`transfer_segments`] only
    /// transfers as many bytes as the shortest of the two buffers contains.
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    pub fn len(&self) -> usize {
        self.len as usize
    }

    /// Returns `true` if this segment won't transfer any bytes.
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Gets the custom clock speed in hertz (Hz) for this segment.
    pub fn clock_speed(&self) -> u32 {
        self.speed_hz
    }

    /// Sets a custom clock speed in hertz (Hz) for this segment.
    ///
    /// The SPI driver will automatically select the closest valid frequency.
    ///
    /// By default, `clock_speed` is set to `0`, which means
    /// it will use the same value as configured for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn set_clock_speed(&mut self, clock_speed: u32) {
        self.speed_hz = clock_speed;
    }

    /// Gets the delay in microseconds (µs) for this segment.
    pub fn delay(&self) -> u16 {
        self.delay_usecs
    }

    /// Sets a delay in microseconds (µs) for this segment.
    ///
    /// `set_delay` adds a delay at the end of this segment,
    /// before the (optional) Slave Select change.
    ///
    /// By default, `delay` is set to `0`.
    pub fn set_delay(&mut self, delay: u16) {
        self.delay_usecs = delay;
    }

    /// Gets the number of bits per word for this segment.
    pub fn bits_per_word(&self) -> u8 {
        self.bits_per_word
    }

    /// Sets the number of bits per word for this segment.
    ///
    /// The Raspberry Pi currently only supports 8 bit words.
    ///
    /// By default, `bits_per_word` is set to `0`, which means
    /// it will use the same value as configured for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn set_bits_per_word(&mut self, bits_per_word: u8) {
        self.bits_per_word = bits_per_word;
    }

    /// Gets the state of Slave Select change for this segment.
    pub fn ss_change(&self) -> bool {
        self.cs_change == 1
    }

    /// Changes Slave Select's behavior for this segment.
    ///
    /// If `ss_change` is set to `true`, and this is not the last
    /// segment of the transfer, the Slave Select line will briefly
    /// change to inactive between this segment and the next.
    /// If this is the last segment, setting `ss_change` to true will
    /// keep Slave Select active after the transfer ends.
    ///
    /// By default, `ss_change` is set to `false`.
    pub fn set_ss_change(&mut self, ss_change: bool) {
        self.cs_change = ss_change as u8;
    }
}

impl<'a, 'b> fmt::Debug for Segment<'a, 'b> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Segment")
            .field("tx_buf", &self.tx_buf)
            .field("rx_buf", &self.rx_buf)
            .field("len", &self.len)
            .field("speed_hz", &self.speed_hz)
            .field("delay_usecs", &self.delay_usecs)
            .field("bits_per_word", &self.bits_per_word)
            .field("cs_change", &self.cs_change)
            .field("tx_nbits", &self.tx_nbits)
            .field("rx_nbits", &self.rx_nbits)
            .field("pad", &self.pad)
            .finish()
    }
}
<commit_msg>Update documentation<commit_after>// Copyright (c) 2017-2019 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::marker;

/// Part of a multi-segment transfer.
///
/// `Segment`s are transferred using the [`transfer_segments`] method.
///
/// Construct a new `Segment` for a simultaneous (full-duplex) read/write
/// transfer using [`new`]. For read operations without any outgoing data,
/// use [`with_read`]. For write operations where any incoming data
/// should be discarded, use [`with_write`].
///
/// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
/// [`with_read`]: #method.with_read
/// [`with_write`]: #method.with_write
/// [`new`]: #method.new
#[derive(PartialEq, Copy, Clone)]
#[repr(C)]
pub struct Segment<'a, 'b> {
    // Pointer to write buffer, or 0.
    tx_buf: u64,
    // Pointer to read buffer, or 0.
    rx_buf: u64,
    // Number of bytes to transfer in this segment.
    len: u32,
    // Set a different clock speed for this segment. Default = 0.
    speed_hz: u32,
    // Add a delay before the (optional) SS change and the next segment.
    delay_usecs: u16,
    // Bits per word for this segment. The Pi only supports 8 bits (or 9 bits in LoSSI mode). Default = 0.
    bits_per_word: u8,
    // Set to 1 to briefly set SS inactive between this segment and the next. If this is the last segment, keep SS active.
    cs_change: u8,
    // Number of outgoing lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    tx_nbits: u8,
    // Number of incoming lines used for dual/quad SPI. Not supported on the Raspberry Pi. Default = 0.
    rx_nbits: u8,
    // Padding. Set to 0 for forward compatibility.
    pad: u16,
    // Zero-sized variable used to link this struct to the read buffer lifetime.
    read_buffer_lifetime: marker::PhantomData<&'a mut [u8]>,
    // Zero-sized variable used to link this struct to the write buffer lifetime.
    write_buffer_lifetime: marker::PhantomData<&'b [u8]>,
}

impl<'a, 'b> Segment<'a, 'b> {
    /// Constructs a new `Segment` with the default settings, and configures it
    /// for a simultaneous (full-duplex) read/write transfer.
    ///
    /// For `Segment`s that only require either a read or write operation, call
    /// [`with_read`] or [`with_write`] instead of `new`.
    ///
    /// [`transfer_segments`] will only transfer as many bytes as the shortest of
    /// the two buffers contains.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    /// [`Spi`]: struct.Spi.html
    /// [`with_read`]: #method.with_read
    /// [`with_write`]: #method.with_write
    pub fn new(read_buffer: &'a mut [u8], write_buffer: &'b [u8]) -> Segment<'a, 'b> {
        Segment::with_settings(Some(read_buffer), Some(write_buffer), 0, 0, 0, false)
    }

    /// Constructs a new `Segment` with the default settings, and configures it
    /// for a read operation.
    ///
    /// Incoming data from the slave device is written to `read_buffer`. The total
    /// number of bytes read depends on the length of `read_buffer`. A zero-value
    /// byte is sent for every byte read.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn with_read(read_buffer: &mut [u8]) -> Segment<'_, '_> {
        Segment::with_settings(Some(read_buffer), None, 0, 0, 0, false)
    }

    /// Constructs a new `Segment` with the default settings, and configures it
    /// for a write operation.
    ///
    /// Outgoing data from `write_buffer` is sent to the slave device. Any
    /// incoming data is discarded.
    ///
    /// By default, all customizable settings are set to 0, which means it uses
    /// the same values as set for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn with_write(write_buffer: &[u8]) -> Segment<'_, '_> {
        Segment::with_settings(None, Some(write_buffer), 0, 0, 0, false)
    }

    /// Constructs a new `Segment` with the specified settings.
    ///
    /// These settings override the values set for [`Spi`], and are only used
    /// for this specific segment.
    ///
    /// If `read_buffer` is set to `None`, any incoming data is discarded.
    ///
    /// If `write_buffer` is set to `None`, a zero-value byte is sent for every
    /// byte read.
    ///
    /// If both `read_buffer` and `write_buffer` are specified, [`transfer_segments`]
    /// will only transfer as many bytes as the shortest of the two buffers contains.
    ///
    /// `clock_speed` sets a custom clock speed in hertz (Hz).
    ///
    /// `delay` sets a delay in microseconds (µs).
    ///
    /// `bits_per_word` sets the number of bits per word. The Raspberry Pi currently only supports 8 bits per word.
    ///
    /// `ss_change` changes how Slave Select behaves in between two segments (toggle SS), or after the final segment (keep SS active).
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    /// [`Spi`]: struct.Spi.html
    pub fn with_settings(
        read_buffer: Option<&'a mut [u8]>,
        write_buffer: Option<&'b [u8]>,
        clock_speed: u32,
        delay: u16,
        bits_per_word: u8,
        ss_change: bool,
    ) -> Segment<'a, 'b> {
        // Len will contain the length of the shortest of the supplied buffers
        let mut len: u32 = 0;

        let tx_buf = if let Some(buffer) = write_buffer {
            len = buffer.len() as u32;
            buffer.as_ptr() as u64
        } else {
            0
        };

        let rx_buf = if let Some(buffer) = read_buffer {
            if (len > buffer.len() as u32) || tx_buf == 0 {
                len = buffer.len() as u32;
            }
            buffer.as_ptr() as u64
        } else {
            0
        };

        Segment {
            tx_buf,
            rx_buf,
            len,
            speed_hz: clock_speed,
            delay_usecs: delay,
            bits_per_word,
            cs_change: ss_change as u8,
            tx_nbits: 0,
            rx_nbits: 0,
            pad: 0,
            read_buffer_lifetime: marker::PhantomData,
            write_buffer_lifetime: marker::PhantomData,
        }
    }

    /// Returns the number of bytes that will be transferred.
    ///
    /// If both a read buffer and write buffer are supplied, [`transfer_segments`] only
    /// transfers as many bytes as the shortest of the two buffers contains.
    ///
    /// [`transfer_segments`]: struct.Spi.html#method.transfer_segments
    pub fn len(&self) -> usize {
        self.len as usize
    }

    /// Returns `true` if this segment won't transfer any bytes.
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Gets the custom clock speed in hertz (Hz) for this segment.
    pub fn clock_speed(&self) -> u32 {
        self.speed_hz
    }

    /// Sets a custom clock speed in hertz (Hz) for this segment.
    ///
    /// The SPI driver will automatically select the closest valid frequency.
    ///
    /// By default, `clock_speed` is set to `0`, which means
    /// it will use the same value as configured for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn set_clock_speed(&mut self, clock_speed: u32) {
        self.speed_hz = clock_speed;
    }

    /// Gets the delay in microseconds (µs) for this segment.
    pub fn delay(&self) -> u16 {
        self.delay_usecs
    }

    /// Sets a delay in microseconds (µs) for this segment.
    ///
    /// `set_delay` adds a delay at the end of this segment,
    /// before the (optional) Slave Select change.
    ///
    /// By default, `delay` is set to `0`.
    pub fn set_delay(&mut self, delay: u16) {
        self.delay_usecs = delay;
    }

    /// Gets the number of bits per word for this segment.
    pub fn bits_per_word(&self) -> u8 {
        self.bits_per_word
    }

    /// Sets the number of bits per word for this segment.
    ///
    /// The Raspberry Pi currently only supports 8 bit words.
    ///
    /// By default, `bits_per_word` is set to `0`, which means
    /// it will use the same value as configured for [`Spi`].
    ///
    /// [`Spi`]: struct.Spi.html
    pub fn set_bits_per_word(&mut self, bits_per_word: u8) {
        self.bits_per_word = bits_per_word;
    }

    /// Gets the state of Slave Select change for this segment.
    pub fn ss_change(&self) -> bool {
        self.cs_change == 1
    }

    /// Changes Slave Select's behavior for this segment.
    ///
    /// If `ss_change` is set to `true`, and this is not the last
    /// segment of the transfer, the Slave Select line will briefly
    /// change to inactive between this segment and the next.
    /// If this is the last segment, setting `ss_change` to true will
    /// keep Slave Select active after the transfer ends.
    ///
    /// By default, `ss_change` is set to `false`.
    pub fn set_ss_change(&mut self, ss_change: bool) {
        self.cs_change = ss_change as u8;
    }
}

impl<'a, 'b> fmt::Debug for Segment<'a, 'b> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Segment")
            .field("tx_buf", &self.tx_buf)
            .field("rx_buf", &self.rx_buf)
            .field("len", &self.len)
            .field("speed_hz", &self.speed_hz)
            .field("delay_usecs", &self.delay_usecs)
            .field("bits_per_word", &self.bits_per_word)
            .field("cs_change", &self.cs_change)
            .field("tx_nbits", &self.tx_nbits)
            .field("rx_nbits", &self.rx_nbits)
            .field("pad", &self.pad)
            .finish()
    }
}
<|endoftext|>


<commit_before>use bindings;

use std;
use std::ptr;
use std::ffi::{CStr, CString, OsStr};

use std::os::unix::ffi::OsStrExt;

pub mod internal {
    use std;
    use bindings;
    use libc::c_char;
    use std::ffi::CStr;

    #[derive(Clone)]
    pub struct Tags<'a> {
        node: bindings::glist_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Tags<'a> {
        pub fn new(node: bindings::glist_t) -> Self {
            Tags { node: node, _marker: std::marker::PhantomData }
        }
    }

    impl<'a> Iterator for Tags<'a> {
        type Item = &'a str;

        fn next(&mut self) -> Option<Self::Item> {
            if self.node.is_null() {
                return None
            }

            let tag = unsafe { CStr::from_ptr((*self.node).data.as_ptr() as *const c_char) };
            self.node = unsafe { (*self.node).next };
            Some(tag.to_str().unwrap())
        }
    }

    #[derive(Clone)]
    #[allow(raw_pointer_derive)]
    pub struct Atom<'a> {
        raw: *const bindings::internal::jsgf_atom_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Atom<'a> {
        pub fn new(raw: *const bindings::internal::jsgf_atom_t) -> Self {
            Atom { raw: raw, _marker: std::marker::PhantomData }
        }

        pub fn name(&self) -> &'a str {
            unsafe { CStr::from_ptr((*self.raw).name).to_str().unwrap() }
        }

        pub fn tags(&self) -> Tags<'a>  {
            Tags::new(unsafe { (*self.raw).tags })
        }
    }

    #[derive(Clone)]
    #[allow(raw_pointer_derive)]
    pub struct Atoms<'a> {
        node: bindings::glist_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Atoms<'a> {
        fn new(node: bindings::glist_t) -> Self {
            Atoms { node: node, _marker: std::marker::PhantomData }
        }
    }

    impl<'a> Iterator for Atoms<'a> {
        type Item = Atom<'a>;

        fn next(&mut self) -> Option<Self::Item> {
            if self.node.is_null() {
                return None
            }

            let atom = Atom::new(unsafe {
                (*self.node).data.as_ptr() as *const bindings::internal::jsgf_atom_t
            });
            self.node = unsafe { (*self.node).next };
            Some(atom)
        }
    }

    #[derive(Clone)]
    #[allow(raw_pointer_derive)]
    pub struct Alternatives<'a> {
        node: *const bindings::internal::jsgf_rhs_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Alternatives<'a> {
        fn new(node: *const bindings::internal::jsgf_rhs_t) -> Self {
            Alternatives { node: node, _marker: std::marker::PhantomData }
        }
    }

    impl<'a> Iterator for Alternatives<'a> {
        type Item = Atoms<'a>;

        fn next(&mut self) -> Option<Self::Item> {
            if self.node.is_null() {
                return None;
            }

            let atoms = Atoms::new(unsafe { (*self.node).atoms });
            self.node = unsafe { (*self.node).alt };
            Some(atoms)
        }
    }

    #[derive(Clone)]
    #[allow(raw_pointer_derive)]
    pub struct RuleData<'a> {
        raw: *const bindings::internal::jsgf_rule_s,
        _marker: std::marker::PhantomData<&'a super::Jsgf>,
    }

    impl<'a> RuleData<'a> {
        pub fn new(raw: *const bindings::internal::jsgf_rule_s) -> Self {
            RuleData { raw: raw, _marker: std::marker::PhantomData }
        }

        pub fn alternatives(&self) -> Alternatives<'a> {
            Alternatives::new(unsafe { (*self.raw).rhs })
        }
    }
}

#[derive(Clone)]
#[allow(raw_pointer_derive)]
pub struct Rule<'a> {
    raw: *const bindings::jsgf_rule_t,
    _marker: std::marker::PhantomData<&'a str>,
}

impl<'a> Rule<'a> {
    fn new(raw: *const bindings::jsgf_rule_t) -> Self {
        Rule { raw: raw, _marker: std::marker::PhantomData }
    }

    pub fn name(&self) -> &'a str {
        let name_c = unsafe { bindings::jsgf_rule_name(self.raw) };
        unsafe { CStr::from_ptr(name_c) }.to_str().unwrap()
    }

    pub fn is_public(&self) -> bool {
        let res = unsafe { bindings::jsgf_rule_public(self.raw) };
        res != 0
    }

    pub unsafe fn internal(&self) -> internal::RuleData<'a> {
        internal::RuleData::new(self.raw as *const bindings::internal::jsgf_rule_s)
    }
}

pub struct Rules<'a> {
    raw: *const bindings::jsgf_rule_iter_t,
    _marker: std::marker::PhantomData<&'a str>,
}

impl<'a> Rules<'a> {
    pub fn new(raw: *const bindings::jsgf_rule_iter_t) -> Self {
        Rules { raw: raw, _marker: std::marker::PhantomData }
    }
}

impl<'a> Drop for Rules<'a> {
    fn drop(&mut self) {
        if !self.raw.is_null() {
            unsafe { bindings::jsgf_rule_iter_free(self.raw) };
        }
    }
}

impl<'a> Iterator for Rules<'a> {
    type Item = Rule<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.raw.is_null() {
            None
        } else {
            let item = Rule::new(unsafe { bindings::jsgf_rule_iter_rule(self.raw) });
            self.raw = unsafe { bindings::jsgf_rule_iter_next(self.raw) };
            Some(item)
        }
    }
}

pub struct Jsgf {
    raw: *mut bindings::jsgf_t,
}

impl Jsgf {
    pub fn parse_file(filename: &OsStr) -> Result<Self, ()> {
        let filename_c = CString::new(filename.as_bytes()).unwrap();
        let raw = unsafe { bindings::jsgf_parse_file(filename_c.as_ptr(), ptr::null()) };
        if raw.is_null() {
            Err(())
        } else {
            Ok(Jsgf { raw: raw })
        }
    }

    pub fn parse_string(s: &str) -> Result<Self, ()> {
        let s_c = CString::new(s.as_bytes()).unwrap();
        let raw = unsafe { bindings::jsgf_parse_string(s_c.as_ptr(), ptr::null()) };
        if raw.is_null() {
            Err(())
        } else {
            Ok(Jsgf { raw: raw })
        }
    }

    pub fn name(&self) -> &str {
        let name_c = unsafe { bindings::jsgf_grammar_name(self.raw) };
        assert!(!name_c.is_null());
        unsafe { CStr::from_ptr(name_c) }.to_str().unwrap()
    }

    pub fn rules(&self) -> Rules {
        let raw_rules_iter = unsafe { bindings::jsgf_rule_iter(self.raw) };
        assert!(!raw_rules_iter.is_null());
        Rules::new(raw_rules_iter)
    }

    pub fn public_rule(&self) -> Rule {
        Rule::new(unsafe { bindings::jsgf_get_public_rule(self.raw) })
    }

    pub fn rule<'a>(&'a self, name: &str) -> Option<Rule<'a>> {
        let name_c = CString::new(name).unwrap();
        let raw_rule = unsafe { bindings::jsgf_get_rule(self.raw, name_c.as_ptr()) };
        if raw_rule.is_null() {
            None
        } else {
            Some(Rule::new(raw_rule))
        }
    }
}

impl Drop for Jsgf {
    fn drop(&mut self) {
        unsafe { bindings::jsgf_grammar_free(self.raw) }
    }
}
<commit_msg>Remove `raw_pointer_derive` lint suppressions<commit_after>use bindings;

use std;
use std::ptr;
use std::ffi::{CStr, CString, OsStr};

use std::os::unix::ffi::OsStrExt;

pub mod internal {
    use std;
    use bindings;
    use libc::c_char;
    use std::ffi::CStr;

    #[derive(Clone)]
    pub struct Tags<'a> {
        node: bindings::glist_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Tags<'a> {
        pub fn new(node: bindings::glist_t) -> Self {
            Tags { node: node, _marker: std::marker::PhantomData }
        }
    }

    impl<'a> Iterator for Tags<'a> {
        type Item = &'a str;

        fn next(&mut self) -> Option<Self::Item> {
            if self.node.is_null() {
                return None
            }

            let tag = unsafe { CStr::from_ptr((*self.node).data.as_ptr() as *const c_char) };
            self.node = unsafe { (*self.node).next };
            Some(tag.to_str().unwrap())
        }
    }

    #[derive(Clone)]
    pub struct Atom<'a> {
        raw: *const bindings::internal::jsgf_atom_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Atom<'a> {
        pub fn new(raw: *const bindings::internal::jsgf_atom_t) -> Self {
            Atom { raw: raw, _marker: std::marker::PhantomData }
        }

        pub fn name(&self) -> &'a str {
            unsafe { CStr::from_ptr((*self.raw).name).to_str().unwrap() }
        }

        pub fn tags(&self) -> Tags<'a>  {
            Tags::new(unsafe { (*self.raw).tags })
        }
    }

    #[derive(Clone)]
    pub struct Atoms<'a> {
        node: bindings::glist_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Atoms<'a> {
        fn new(node: bindings::glist_t) -> Self {
            Atoms { node: node, _marker: std::marker::PhantomData }
        }
    }

    impl<'a> Iterator for Atoms<'a> {
        type Item = Atom<'a>;

        fn next(&mut self) -> Option<Self::Item> {
            if self.node.is_null() {
                return None
            }

            let atom = Atom::new(unsafe {
                (*self.node).data.as_ptr() as *const bindings::internal::jsgf_atom_t
            });
            self.node = unsafe { (*self.node).next };
            Some(atom)
        }
    }

    #[derive(Clone)]
    pub struct Alternatives<'a> {
        node: *const bindings::internal::jsgf_rhs_t,
        _marker: std::marker::PhantomData<&'a str>,
    }

    impl<'a> Alternatives<'a> {
        fn new(node: *const bindings::internal::jsgf_rhs_t) -> Self {
            Alternatives { node: node, _marker: std::marker::PhantomData }
        }
    }

    impl<'a> Iterator for Alternatives<'a> {
        type Item = Atoms<'a>;

        fn next(&mut self) -> Option<Self::Item> {
            if self.node.is_null() {
                return None;
            }

            let atoms = Atoms::new(unsafe { (*self.node).atoms });
            self.node = unsafe { (*self.node).alt };
            Some(atoms)
        }
    }

    #[derive(Clone)]
    pub struct RuleData<'a> {
        raw: *const bindings::internal::jsgf_rule_s,
        _marker: std::marker::PhantomData<&'a super::Jsgf>,
    }

    impl<'a> RuleData<'a> {
        pub fn new(raw: *const bindings::internal::jsgf_rule_s) -> Self {
            RuleData { raw: raw, _marker: std::marker::PhantomData }
        }

        pub fn alternatives(&self) -> Alternatives<'a> {
            Alternatives::new(unsafe { (*self.raw).rhs })
        }
    }
}

#[derive(Clone)]
pub struct Rule<'a> {
    raw: *const bindings::jsgf_rule_t,
    _marker: std::marker::PhantomData<&'a str>,
}

impl<'a> Rule<'a> {
    fn new(raw: *const bindings::jsgf_rule_t) -> Self {
        Rule { raw: raw, _marker: std::marker::PhantomData }
    }

    pub fn name(&self) -> &'a str {
        let name_c = unsafe { bindings::jsgf_rule_name(self.raw) };
        unsafe { CStr::from_ptr(name_c) }.to_str().unwrap()
    }

    pub fn is_public(&self) -> bool {
        let res = unsafe { bindings::jsgf_rule_public(self.raw) };
        res != 0
    }

    pub unsafe fn internal(&self) -> internal::RuleData<'a> {
        internal::RuleData::new(self.raw as *const bindings::internal::jsgf_rule_s)
    }
}

pub struct Rules<'a> {
    raw: *const bindings::jsgf_rule_iter_t,
    _marker: std::marker::PhantomData<&'a str>,
}

impl<'a> Rules<'a> {
    pub fn new(raw: *const bindings::jsgf_rule_iter_t) -> Self {
        Rules { raw: raw, _marker: std::marker::PhantomData }
    }
}

impl<'a> Drop for Rules<'a> {
    fn drop(&mut self) {
        if !self.raw.is_null() {
            unsafe { bindings::jsgf_rule_iter_free(self.raw) };
        }
    }
}

impl<'a> Iterator for Rules<'a> {
    type Item = Rule<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.raw.is_null() {
            None
        } else {
            let item = Rule::new(unsafe { bindings::jsgf_rule_iter_rule(self.raw) });
            self.raw = unsafe { bindings::jsgf_rule_iter_next(self.raw) };
            Some(item)
        }
    }
}

pub struct Jsgf {
    raw: *mut bindings::jsgf_t,
}

impl Jsgf {
    pub fn parse_file(filename: &OsStr) -> Result<Self, ()> {
        let filename_c = CString::new(filename.as_bytes()).unwrap();
        let raw = unsafe { bindings::jsgf_parse_file(filename_c.as_ptr(), ptr::null()) };
        if raw.is_null() {
            Err(())
        } else {
            Ok(Jsgf { raw: raw })
        }
    }

    pub fn parse_string(s: &str) -> Result<Self, ()> {
        let s_c = CString::new(s.as_bytes()).unwrap();
        let raw = unsafe { bindings::jsgf_parse_string(s_c.as_ptr(), ptr::null()) };
        if raw.is_null() {
            Err(())
        } else {
            Ok(Jsgf { raw: raw })
        }
    }

    pub fn name(&self) -> &str {
        let name_c = unsafe { bindings::jsgf_grammar_name(self.raw) };
        assert!(!name_c.is_null());
        unsafe { CStr::from_ptr(name_c) }.to_str().unwrap()
    }

    pub fn rules(&self) -> Rules {
        let raw_rules_iter = unsafe { bindings::jsgf_rule_iter(self.raw) };
        assert!(!raw_rules_iter.is_null());
        Rules::new(raw_rules_iter)
    }

    pub fn public_rule(&self) -> Rule {
        Rule::new(unsafe { bindings::jsgf_get_public_rule(self.raw) })
    }

    pub fn rule<'a>(&'a self, name: &str) -> Option<Rule<'a>> {
        let name_c = CString::new(name).unwrap();
        let raw_rule = unsafe { bindings::jsgf_get_rule(self.raw, name_c.as_ptr()) };
        if raw_rule.is_null() {
            None
        } else {
            Some(Rule::new(raw_rule))
        }
    }
}

impl Drop for Jsgf {
    fn drop(&mut self) {
        unsafe { bindings::jsgf_grammar_free(self.raw) }
    }
}
<|endoftext|>


<commit_before>#[link(name="du", vers="1.0.0", author="Derek Chiang")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::stderr;
use std::io::fs;
use std::io::FileStat;
use std::option::Option;
use std::path::Path;
use extra::arc::Arc;
use extra::future::Future;
use extra::getopts::groups;

static VERSION: &'static str = "1.0.0";

struct Options {
    all: bool,
    max_depth: Option<uint>,
    total: bool,
    separate_dirs: bool,
}

fn du(path: &Path, options_arc: Arc<Options>, depth: uint) -> ~[Arc<FileStat>] {
    let mut stats = ~[];
    let mut futures = ~[];
    let options = options_arc.get();
    let mut my_stat = path.stat();

    for f in fs::readdir(path).move_iter() {
        match f.is_file() {
            true => {
                let stat = f.stat();
                my_stat.size += stat.size;
                my_stat.unstable.blocks += stat.unstable.blocks;
                if options.all {
                    stats.push(Arc::new(stat))
                }
            }
            false => {
                let oa_clone = options_arc.clone();
                futures.push(do Future::spawn { du(&f, oa_clone, depth + 1) })
            }
        }
    }

    for future in futures.mut_iter() {
        for stat_arc in future.get().move_rev_iter() {
            let stat = stat_arc.get();
            if !options.separate_dirs && stat.path.dir_path() == my_stat.path {
                my_stat.size += stat.size;
                my_stat.unstable.blocks += stat.unstable.blocks;
            }
            if options.max_depth == None || depth < options.max_depth.unwrap() {
                stats.push(stat_arc.clone());
            }
        }
    }

    stats.push(Arc::new(my_stat));

    return stats;
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        // In task
        groups::optflag("a", "all", " write counts for all files, not just directories"),
        // In main
        groups::optflag("", "apparent-size", "print apparent sizes,  rather  than  disk  usage;
            although  the apparent  size is usually smaller, it may be larger due to holes
            in ('sparse') files, internal  fragmentation,  indirect  blocks, and the like"),
        // // In main
        // groups::optopt("B", "block-size", "scale sizes  by  SIZE before printing them.
        //     E.g., '-BM' prints sizes in units of 1,048,576 bytes.  See SIZE format below.",
        //     "SIZE"),
        // // In main
        // groups::optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'"),
        // In main
        groups::optflag("c", "total", "produce a grand total"),
        // In task
        // groups::optflag("D", "dereference-args", "dereference only symlinks that are listed
        //     on the command line"),
        // In main
        // groups::optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
        //                   names specified in file F;
        //                   If F is - then read names from standard input", "F"),
        // // In task
        // groups::optflag("H", "", "equivalent to --dereference-args (-D)"),
        // In main
        groups::optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)"),
        // In main
        groups::optflag("", "si", "like -h, but use powers of 1000 not 1024"),
        // In main
        groups::optflag("k", "", "like --block-size=1K"),
        // // In task
        // groups::optflag("l", "count-links", "count sizes many times if hard linked"),
        // // In main
        groups::optflag("m", "", "like --block-size=1M"),
        // // In task
        // groups::optflag("L", "dereference", "dereference all symbolic links"),
        // // In task
        // groups::optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
        // // In main
        groups::optflag("0", "null", "end each output line with 0 byte rather than newline"),
        // In main?
        groups::optflag("S", "separate-dirs", "do not include size of subdirectories"),
        // In main
        groups::optflag("s", "summarize", "display only a total for each argument"),
        // // In task
        // groups::optflag("x", "one-file-system", "skip directories on different file systems"),
        // // In task
        // groups::optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
        // // In task
        // groups::optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
        // // In main
        groups::optopt("d", "max-depth", "print the total for a directory (or file, with --all)
            only if it is N or fewer levels below the command
            line argument;  --max-depth=0 is the same as --summarize", "N"),
        // // In main
        // groups::optflag("", "time", "show time of the last modification of any file in the
        //     directory, or any of its subdirectories"),
        // // In main
        // groups::optopt("", "time", "show time as WORD instead of modification time:
        //     atime, access, use, ctime or status", "WORD"),
        // // In main
        // groups::optopt("", "time-style", "show times using style STYLE:
        //     full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("du " + VERSION + " - estimate file space usage");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("  {0:s} [OPTION]... --files0-from=F", program);
        println("");
        println(groups::usage("Summarize disk usage of each FILE, recursively for directories.", opts));
        println("Display  values  are  in  units  of  the  first  available  SIZE from
--block-size,  and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environ‐
ment variables.  Otherwise, units default to  1024  bytes  (or  512  if
POSIXLY_CORRECT is set).

SIZE  is  an  integer and optional unit (example: 10M is 10*1024*1024).
Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB, ...  (pow‐
ers of 1000).");
        return
    }

    let options = Options{
        all: matches.opt_present("all"),
        max_depth: match (matches.opt_present("summarize"), matches.opt_str("max-depth")) {
            (true, Some(s)) => match from_str::<uint>(s) {
                Some(_) => {
                    println!("du: warning: summarizing conflicts with --max-depth={:s}", s);
                    return
                },
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (true, None) => Some(0),
            (false, Some(s)) => match from_str::<uint>(s) {
                Some(u) => Some(u),
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (false, None) => None
        },
        total: matches.opt_present("total"),
        separate_dirs: matches.opt_present("S"),
    };

    let strs = matches.free.clone();
    let strs = match strs.is_empty() {
        true => ~[~"./"],
        false => strs
    };

    let options_arc = Arc::new(options);

    let MB = match matches.opt_present("si") {
        true => 1000 * 1000,
        false => 1024 * 1024,
    };
    let KB = match matches.opt_present("si") {
        true => 1000,
        false => 1024,
    };

    let convert_size = |size: u64| -> ~str {
        if matches.opt_present("human-readable") || matches.opt_present("si") {
            if size > MB {
                format!("{:.1f}MB", (size as f64) / (MB as f64))
            } else if size > KB {
                format!("{:.1f}KB", (size as f64) / (KB as f64))
            } else {
                format!("{}B", size)
            }
        } else if matches.opt_present("k") {
            format!("{}", ((size as f64) / (KB as f64)).ceil())
        } else if matches.opt_present("m") {
            format!("{}", ((size as f64) / (MB as f64)).ceil())
        } else {
            format!("{}", ((size as f64) / (KB as f64)).ceil())
        }
    };

    let line_separator = match matches.opt_present("0") {
        true => "\0",
        false => "\n",
    };

    let mut grand_total = 0;
    for path_str in strs.iter() {
        let path = Path::init(path_str.clone());
        let iter = du(&path, options_arc.clone(), 0).move_iter();
        let (_, len) = iter.size_hint();
        let len = len.unwrap();
        for (index, stat_arc) in iter.enumerate() {
            let stat = stat_arc.get();
            let size = match matches.opt_present("apparent-size") {
                true => stat.size,
                // C's stat is such that each block is assume to be 512 bytes
                // See: http://linux.die.net/man/2/stat
                false => stat.unstable.blocks * 512,
            };
            print!("{:<10} {}", convert_size(size), stat.path.display());
            print(line_separator);
            if options.total && index == (len - 1) {
                // The last element will be the total size of the the path under
                // path_str.  We add it to the grand total.
                grand_total += size;
            }
        }
    }

    if options.total {
        print!("{:<10} total", convert_size(grand_total));
        print(line_separator);
    }
}
<commit_msg>Adding partial support for formatting time<commit_after>#[link(name="du", vers="1.0.0", author="Derek Chiang")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::stderr;
use std::io::fs;
use std::io::FileStat;
use std::option::Option;
use std::path::Path;
use extra::arc::Arc;
use extra::future::Future;
use extra::getopts::groups;
use extra::time::Timespec;

static VERSION: &'static str = "1.0.0";

struct Options {
    all: bool,
    max_depth: Option<uint>,
    total: bool,
    separate_dirs: bool,
}

fn du(path: &Path, options_arc: Arc<Options>, depth: uint) -> ~[Arc<FileStat>] {
    let mut stats = ~[];
    let mut futures = ~[];
    let options = options_arc.get();
    let mut my_stat = path.stat();

    for f in fs::readdir(path).move_iter() {
        match f.is_file() {
            true => {
                let stat = f.stat();
                my_stat.size += stat.size;
                my_stat.unstable.blocks += stat.unstable.blocks;
                if options.all {
                    stats.push(Arc::new(stat))
                }
            }
            false => {
                let oa_clone = options_arc.clone();
                futures.push(do Future::spawn { du(&f, oa_clone, depth + 1) })
            }
        }
    }

    for future in futures.mut_iter() {
        for stat_arc in future.get().move_rev_iter() {
            let stat = stat_arc.get();
            if !options.separate_dirs && stat.path.dir_path() == my_stat.path {
                my_stat.size += stat.size;
                my_stat.unstable.blocks += stat.unstable.blocks;
            }
            if options.max_depth == None || depth < options.max_depth.unwrap() {
                stats.push(stat_arc.clone());
            }
        }
    }

    stats.push(Arc::new(my_stat));

    return stats;
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        // In task
        groups::optflag("a", "all", " write counts for all files, not just directories"),
        // In main
        groups::optflag("", "apparent-size", "print apparent sizes,  rather  than  disk  usage;
            although  the apparent  size is usually smaller, it may be larger due to holes
            in ('sparse') files, internal  fragmentation,  indirect  blocks, and the like"),
        // In main
        groups::optopt("B", "block-size", "scale sizes  by  SIZE before printing them.
            E.g., '-BM' prints sizes in units of 1,048,576 bytes.  See SIZE format below.",
            "SIZE"),
        // In main
        groups::optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'"),
        // In main
        groups::optflag("c", "total", "produce a grand total"),
        // In task
        // groups::optflag("D", "dereference-args", "dereference only symlinks that are listed
        //     on the command line"),
        // In main
        // groups::optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
        //                   names specified in file F;
        //                   If F is - then read names from standard input", "F"),
        // // In task
        // groups::optflag("H", "", "equivalent to --dereference-args (-D)"),
        // In main
        groups::optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)"),
        // In main
        groups::optflag("", "si", "like -h, but use powers of 1000 not 1024"),
        // In main
        groups::optflag("k", "", "like --block-size=1K"),
        // In task
        groups::optflag("l", "count-links", "count sizes many times if hard linked"),
        // // In main
        groups::optflag("m", "", "like --block-size=1M"),
        // // In task
        // groups::optflag("L", "dereference", "dereference all symbolic links"),
        // // In task
        // groups::optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
        // // In main
        groups::optflag("0", "null", "end each output line with 0 byte rather than newline"),
        // In main
        groups::optflag("S", "separate-dirs", "do not include size of subdirectories"),
        // In main
        groups::optflag("s", "summarize", "display only a total for each argument"),
        // // In task
        // groups::optflag("x", "one-file-system", "skip directories on different file systems"),
        // // In task
        // groups::optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
        // // In task
        // groups::optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
        // In main
        groups::optopt("d", "max-depth", "print the total for a directory (or file, with --all)
            only if it is N or fewer levels below the command
            line argument;  --max-depth=0 is the same as --summarize", "N"),
        // In main
        groups::optflag("", "time", "show time of the last modification of any file in the
            directory, or any of its subdirectories"),
        // In main
        groups::optopt("", "time", "show time as WORD instead of modification time:
            atime, access, use, ctime or status", "WORD"),
        // In main
        groups::optopt("", "time-style", "show times using style STYLE:
            full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("du " + VERSION + " - estimate file space usage");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION]... [FILE]...", program);
        println!("  {0:s} [OPTION]... --files0-from=F", program);
        println("");
        println(groups::usage("Summarize disk usage of each FILE, recursively for directories.", opts));
        println("Display  values  are  in  units  of  the  first  available  SIZE from
--block-size,  and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environ‐
ment variables.  Otherwise, units default to  1024  bytes  (or  512  if
POSIXLY_CORRECT is set).

SIZE  is  an  integer and optional unit (example: 10M is 10*1024*1024).
Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB, ...  (pow‐
ers of 1000).");
        return
    }

    let options = Options{
        all: matches.opt_present("all"),
        max_depth: match (matches.opt_present("summarize"), matches.opt_str("max-depth")) {
            (true, Some(s)) => match from_str::<uint>(s) {
                Some(_) => {
                    println!("du: warning: summarizing conflicts with --max-depth={:s}", s);
                    return
                },
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (true, None) => Some(0),
            (false, Some(s)) => match from_str::<uint>(s) {
                Some(u) => Some(u),
                None => {
                    println!("du: invalid maximum depth '{:s}'", s);
                    return
                }
            },
            (false, None) => None
        },
        total: matches.opt_present("total"),
        separate_dirs: matches.opt_present("S"),
    };

    let strs = matches.free.clone();
    let strs = match strs.is_empty() {
        true => ~[~"./"],
        false => strs
    };

    let options_arc = Arc::new(options);

    let MB = match matches.opt_present("si") {
        true => 1000 * 1000,
        false => 1024 * 1024,
    };
    let KB = match matches.opt_present("si") {
        true => 1000,
        false => 1024,
    };

    let block_size = match matches.opt_str("block-size") {
        Some(s) => {
            let mut found_number = false;
            let mut found_letter = false;
            let mut numbers = ~[];
            let mut letters = ~[];
            for c in s.chars() {
                if found_letter && c.is_digit() || !found_number && !c.is_digit() {
                    println!("du: invalid --block-size argument '{}'", s);
                    return
                } else if c.is_digit() {
                    found_number = true;
                    numbers.push(c as u8);
                } else if c.is_alphabetic() {
                    found_letter = true;
                    letters.push(c);
                }
            }
            let number = std::uint::parse_bytes(numbers, 10).unwrap();
            let multiple = match std::str::from_chars(letters).as_slice() {
                "K" => 1024, "M" => 1024 * 1024, "G" => 1024 * 1024 * 1024,
                "T" => 1024 * 1024 * 1024 * 1024, "P" => 1024 * 1024 * 1024 * 1024 * 1024,
                "E" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
                "Z" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
                "Y" => 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
                "KB" => 1000, "MB" => 1000 * 1000, "GB" => 1000 * 1000 * 1000,
                "TB" => 1000 * 1000 * 1000 * 1000, "PB" => 1000 * 1000 * 1000 * 1000 * 1000,
                "EB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                "ZB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                "YB" => 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
                _ => {
                    println!("du: invalid --block-size argument '{}'", s); return
                }
            };
            number * multiple
        },
        None => 1024
    };

    let convert_size = |size: u64| -> ~str {
        if matches.opt_present("human-readable") || matches.opt_present("si") {
            if size > MB {
                format!("{:.1f}M", (size as f64) / (MB as f64))
            } else if size > KB {
                format!("{:.1f}K", (size as f64) / (KB as f64))
            } else {
                format!("{}B", size)
            }
        } else if matches.opt_present("k") {
            format!("{}", ((size as f64) / (KB as f64)).ceil())
        } else if matches.opt_present("m") {
            format!("{}", ((size as f64) / (MB as f64)).ceil())
        } else {
            format!("{}", ((size as f64) / (block_size as f64)).ceil())
        }
    };

    let time_format_str = match matches.opt_str("time-style") {
        Some(s) => {
            match s.as_slice() {
                "full-iso" => "%Y-%m-%d %H:%M:%S.%N %z",
                "long-iso" => "%Y-%m-%d %H:%M",
                "iso" => "%Y-%m-%d",
                _ => {
                    println("
du: invalid argument 'awdwa' for 'time style'
Valid arguments are:
- 'full-iso'
- 'long-iso'
- 'iso'
Try 'du --help' for more information.");
                    return
                }
            }
        },
        None => "%Y-%m-%d %H:%M"
    };

    let line_separator = match matches.opt_present("0") {
        true => "\0",
        false => "\n",
    };

    let mut grand_total = 0;
    for path_str in strs.iter() {
        let path = Path::new(path_str.clone());
        let iter = du(&path, options_arc.clone(), 0).move_iter();
        let (_, len) = iter.size_hint();
        let len = len.unwrap();
        for (index, stat_arc) in iter.enumerate() {
            let stat = stat_arc.get();
            let size = match matches.opt_present("apparent-size") {
                true => stat.unstable.nlink * stat.size,
                // C's stat is such that each block is assume to be 512 bytes
                // See: http://linux.die.net/man/2/stat
                false => stat.unstable.blocks * 512,
            };
            if matches.opt_present("time") {
                let time_str = {
                    let (secs, nsecs) = {
                        let time = match matches.opt_str("time") {
                            Some(s) => match s.as_slice() {
                                "accessed" => stat.accessed,
                                "created" => stat.created,
                                "modified" => stat.modified,
                                _ => {
                                    println("du: invalid argument 'modified' for '--time'
    Valid arguments are:
      - 'accessed', 'created', 'modified'
    Try 'du --help' for more information.");
                                    return
                                }
                            },
                            None => stat.modified
                        };
                        ((time / 1000) as i64, (time % 1000 * 1000) as i32)
                    };
                    let time_spec = Timespec::new(secs, nsecs);
                    extra::time::at(time_spec).strftime(time_format_str)
                };
                print!("{:<10} {:<30} {}", convert_size(size), time_str, stat.path.display());
            } else {
                print!("{:<10} {}", convert_size(size), stat.path.display());
            }
            print(line_separator);
            if options.total && index == (len - 1) {
                // The last element will be the total size of the the path under
                // path_str.  We add it to the grand total.
                grand_total += size;
            }
        }
    }

    if options.total {
        print!("{:<10} total", convert_size(grand_total));
        print(line_separator);
    }
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};
use std::{
    borrow::Cow,
    collections::HashMap,
    iter::{FromIterator, Iterator},
    slice::Iter,
};

/// Influxdb value, Please look at [this address](https://docs.influxdata.com/influxdb/v1.3/write_protocols/line_protocol_reference/)
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Value<'a> {
    /// string
    String(Cow<'a, str>),
    /// Integer
    Integer(i64),
    /// float
    Float(f64),
    /// Bool
    Boolean(bool),
}

/// influxdb point
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Point<'a> {
    /// measurement
    pub measurement: String,
    /// tags
    pub tags: HashMap<String, Value<'a>>,
    /// fields
    pub fields: HashMap<String, Value<'a>>,
    /// timestamp
    pub timestamp: Option<i64>,
}

impl<'a> Point<'a> {
    /// Create a new point
    pub fn new(measurement: &str) -> Point {
        Point {
            measurement: String::from(measurement),
            tags: HashMap::new(),
            fields: HashMap::new(),
            timestamp: None,
        }
    }

    /// Add a tag and its value
    pub fn add_tag<T: Into<String>, F: Into<Value<'a>>>(mut self, tag: T, value: F) -> Self {
        self.tags.insert(tag.into(), value.into());
        self
    }

    /// Add a field and its value
    pub fn add_field<T: Into<String>, F: Into<Value<'a>>>(mut self, field: T, value: F) -> Self {
        self.fields.insert(field.into(), value.into());
        self
    }

    /// Set the specified timestamp
    pub fn add_timestamp(mut self, timestamp: i64) -> Self {
        self.timestamp = Some(timestamp);
        self
    }
}

/// Points
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Points<'a> {
    /// points
    pub point: Vec<Point<'a>>,
}

impl<'a> Points<'a> {
    /// Create a new points
    pub fn new(point: Point) -> Points {
        Points { point: vec![point] }
    }

    /// Insert point into already existing points
    pub fn push(mut self, point: Point<'a>) -> Self {
        self.point.push(point);
        self
    }

    /// Create a multi Points more directly
    pub fn create_new(points: Vec<Point>) -> Points {
        Points { point: points }
    }
}

impl<'a, 'b> IntoIterator for &'a Points<'b> {
    type Item = &'a Point<'b>;
    type IntoIter = Iter<'a, Point<'b>>;

    fn into_iter(self) -> Iter<'a, Point<'b>> {
        self.point.iter()
    }
}

impl<'a> FromIterator<Point<'a>> for Points<'a> {
    fn from_iter<T: IntoIterator<Item = Point<'a>>>(iter: T) -> Self {
        let mut points = Vec::new();

        for point in iter {
            points.push(point);
        }

        Points { point: points }
    }
}

impl<'a> Iterator for Points<'a> {
    type Item = Point<'a>;

    fn next(&mut self) -> Option<Point<'a>> {
        self.point.pop()
    }
}

/// Query data
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Query {
    /// query results
    pub results: Option<Vec<Node>>,
    /// fail message
    pub error: Option<String>,
}

/// Chunked Query data
pub type ChunkedQuery<'de, T> = serde_json::StreamDeserializer<'de, T, Query>;

/// Query data node
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Node {
    /// id
    pub statement_id: Option<u64>,
    /// series
    pub series: Option<Vec<Series>>,
}

/// Query data series
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Series {
    /// measurement
    pub name: Option<String>,
    /// tag
    pub tags: Option<serde_json::Map<String, serde_json::Value>>,
    /// field names and time
    pub columns: Vec<String>,
    /// values
    pub values: Option<Vec<Vec<serde_json::Value>>>,
}

/// Time accuracy
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Precision {
    /// n
    Nanoseconds,
    /// u
    Microseconds,
    /// ms
    Milliseconds,
    /// s
    Seconds,
    /// m
    Minutes,
    /// h
    Hours,
}

impl Precision {
    /// Convert Precision to &str
    pub fn to_str(&self) -> &str {
        match *self {
            Precision::Nanoseconds => "n",
            Precision::Microseconds => "u",
            Precision::Milliseconds => "ms",
            Precision::Seconds => "s",
            Precision::Minutes => "m",
            Precision::Hours => "h",
        }
    }
}

/// Create Points by macro
#[macro_export]
macro_rules! points {
    ($($x:expr),+) => {
        {
            let mut temp_vec = Vec::new();
            $(temp_vec.push($x);)*
            Points { point: temp_vec }
        }
    };
}

/// Create Point by macro
#[macro_export]
macro_rules! point {
    ($x:expr) => {{
        Point::new($x)
    }};
    ($x:expr, $y:expr, $z:expr) => {{
        Point {
            measurement: String::from($x),
            tags: $y,
            fields: $z,
            timestamp: None,
        }
    }};
    ($x:expr, $y:expr, $z:expr, $a:expr) => {{
        Point {
            measurement: String::from($x),
            tags: $y,
            fields: $z,
            timestamp: Some($a),
        }
    }};
}

impl<'a> From<String> for Value<'a> {
    fn from(v: String) -> Self {
        Self::String(Cow::Owned(v))
    }
}

impl<'a> From<&'a str> for Value<'a> {
    fn from(v: &'a str) -> Self {
        Self::String(Cow::Borrowed(v))
    }
}

impl<'a> From<i64> for Value<'a> {
    fn from(v: i64) -> Self {
        Self::Integer(v)
    }
}

impl<'a> From<f64> for Value<'a> {
    fn from(v: f64) -> Self {
        Self::Float(v)
    }
}

impl<'a> From<bool> for Value<'a> {
    fn from(v: bool) -> Self {
        Self::Boolean(v)
    }
}
<commit_msg>added more From implementations for "Value" enum<commit_after>use serde::{Deserialize, Serialize};
use std::{
    borrow::Cow,
    collections::HashMap,
    iter::{FromIterator, Iterator},
    slice::Iter,
};

/// Influxdb value, Please look at [this address](https://docs.influxdata.com/influxdb/v1.3/write_protocols/line_protocol_reference/)
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Value<'a> {
    /// string
    String(Cow<'a, str>),
    /// Integer
    Integer(i64),
    /// float
    Float(f64),
    /// Bool
    Boolean(bool),
}

/// influxdb point
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Point<'a> {
    /// measurement
    pub measurement: String,
    /// tags
    pub tags: HashMap<String, Value<'a>>,
    /// fields
    pub fields: HashMap<String, Value<'a>>,
    /// timestamp
    pub timestamp: Option<i64>,
}

impl<'a> Point<'a> {
    /// Create a new point
    pub fn new(measurement: &str) -> Point {
        Point {
            measurement: String::from(measurement),
            tags: HashMap::new(),
            fields: HashMap::new(),
            timestamp: None,
        }
    }

    /// Add a tag and its value
    pub fn add_tag<T: Into<String>, F: Into<Value<'a>>>(mut self, tag: T, value: F) -> Self {
        self.tags.insert(tag.into(), value.into());
        self
    }

    /// Add a field and its value
    pub fn add_field<T: Into<String>, F: Into<Value<'a>>>(mut self, field: T, value: F) -> Self {
        self.fields.insert(field.into(), value.into());
        self
    }

    /// Set the specified timestamp
    pub fn add_timestamp(mut self, timestamp: i64) -> Self {
        self.timestamp = Some(timestamp);
        self
    }
}

/// Points
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Points<'a> {
    /// points
    pub point: Vec<Point<'a>>,
}

impl<'a> Points<'a> {
    /// Create a new points
    pub fn new(point: Point) -> Points {
        Points { point: vec![point] }
    }

    /// Insert point into already existing points
    pub fn push(mut self, point: Point<'a>) -> Self {
        self.point.push(point);
        self
    }

    /// Create a multi Points more directly
    pub fn create_new(points: Vec<Point>) -> Points {
        Points { point: points }
    }
}

impl<'a, 'b> IntoIterator for &'a Points<'b> {
    type Item = &'a Point<'b>;
    type IntoIter = Iter<'a, Point<'b>>;

    fn into_iter(self) -> Iter<'a, Point<'b>> {
        self.point.iter()
    }
}

impl<'a> FromIterator<Point<'a>> for Points<'a> {
    fn from_iter<T: IntoIterator<Item = Point<'a>>>(iter: T) -> Self {
        let mut points = Vec::new();

        for point in iter {
            points.push(point);
        }

        Points { point: points }
    }
}

impl<'a> Iterator for Points<'a> {
    type Item = Point<'a>;

    fn next(&mut self) -> Option<Point<'a>> {
        self.point.pop()
    }
}

/// Query data
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Query {
    /// query results
    pub results: Option<Vec<Node>>,
    /// fail message
    pub error: Option<String>,
}

/// Chunked Query data
pub type ChunkedQuery<'de, T> = serde_json::StreamDeserializer<'de, T, Query>;

/// Query data node
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Node {
    /// id
    pub statement_id: Option<u64>,
    /// series
    pub series: Option<Vec<Series>>,
}

/// Query data series
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
pub struct Series {
    /// measurement
    pub name: Option<String>,
    /// tag
    pub tags: Option<serde_json::Map<String, serde_json::Value>>,
    /// field names and time
    pub columns: Vec<String>,
    /// values
    pub values: Option<Vec<Vec<serde_json::Value>>>,
}

/// Time accuracy
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Precision {
    /// n
    Nanoseconds,
    /// u
    Microseconds,
    /// ms
    Milliseconds,
    /// s
    Seconds,
    /// m
    Minutes,
    /// h
    Hours,
}

impl Precision {
    /// Convert Precision to &str
    pub fn to_str(&self) -> &str {
        match *self {
            Precision::Nanoseconds => "n",
            Precision::Microseconds => "u",
            Precision::Milliseconds => "ms",
            Precision::Seconds => "s",
            Precision::Minutes => "m",
            Precision::Hours => "h",
        }
    }
}

/// Create Points by macro
#[macro_export]
macro_rules! points {
    ($($x:expr),+) => {
        {
            let mut temp_vec = Vec::new();
            $(temp_vec.push($x);)*
            Points { point: temp_vec }
        }
    };
}

/// Create Point by macro
#[macro_export]
macro_rules! point {
    ($x:expr) => {{
        Point::new($x)
    }};
    ($x:expr, $y:expr, $z:expr) => {{
        Point {
            measurement: String::from($x),
            tags: $y,
            fields: $z,
            timestamp: None,
        }
    }};
    ($x:expr, $y:expr, $z:expr, $a:expr) => {{
        Point {
            measurement: String::from($x),
            tags: $y,
            fields: $z,
            timestamp: Some($a),
        }
    }};
}

impl<'a> From<String> for Value<'a> {
    fn from(v: String) -> Self {
        Self::String(Cow::Owned(v))
    }
}

impl<'a> From<&'a str> for Value<'a> {
    fn from(v: &'a str) -> Self {
        Self::String(Cow::Borrowed(v))
    }
}

impl<'a> From<i64> for Value<'a> {
    fn from(v: i64) -> Self {
        Self::Integer(v)
    }
}

impl<'a> From<i32> for Value<'a> {
    fn from(v: i32) -> Self {
        Self::Integer(v.into())
    }
}

impl<'a> From<i16> for Value<'a> {
    fn from(v: i16) -> Self {
        Self::Integer(v.into())
    }
}

impl<'a> From<i8> for Value<'a> {
    fn from(v: i8) -> Self {
        Self::Integer(v.into())
    }
}

impl<'a> From<f64> for Value<'a> {
    fn from(v: f64) -> Self {
        Self::Float(v)
    }
}

impl<'a> From<f32> for Value<'a> {
    fn from(v: f32) -> Self {
        Self::Float(v.into())
    }
}

impl<'a> From<bool> for Value<'a> {
    fn from(v: bool) -> Self {
        Self::Boolean(v)
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

/*!
# Translation of inline assembly.
*/

use core::prelude::*;

use lib;
use middle::trans::build::*;
use middle::trans::callee;
use middle::trans::common::*;
use middle::ty;

use syntax::ast;

// Take an inline assembly expression and splat it out via LLVM
pub fn trans_inline_asm(bcx: block, ia: &ast::inline_asm) -> block {

    let mut bcx = bcx;
    let mut constraints = ~[];
    let mut cleanups = ~[];
    let mut aoutputs = ~[];

    // Prepare the output operands
    let outputs = do ia.outputs.map |&(c, out)| {
        constraints.push(copy *c);

        let aoutty = ty::arg {
            mode: ast::expl(ast::by_copy),
            ty: expr_ty(bcx, out)
        };
        aoutputs.push(unpack_result!(bcx, {
            callee::trans_arg_expr(bcx, aoutty, out, &mut cleanups, None, callee::DontAutorefArg)
        }));

        let e = match out.node {
            ast::expr_addr_of(_, e) => e,
            _ => fail!(~"Expression must be addr of")
        };

        let outty = ty::arg {
            mode: ast::expl(ast::by_copy),
            ty: expr_ty(bcx, e)
        };

        unpack_result!(bcx, {
            callee::trans_arg_expr(bcx, outty, e, &mut cleanups, None, callee::DontAutorefArg)
        })

    };

    for cleanups.each |c| {
        revoke_clean(bcx, *c);
    }
    cleanups.clear();

    // Now the input operands
    let inputs = do ia.inputs.map |&(c, in)| {
        constraints.push(copy *c);

        let inty = ty::arg {
            mode: ast::expl(ast::by_copy),
            ty: expr_ty(bcx, in)
        };

        unpack_result!(bcx, {
            callee::trans_arg_expr(bcx, inty, in, &mut cleanups, None, callee::DontAutorefArg)
        })

    };

    for cleanups.each |c| {
        revoke_clean(bcx, *c);
    }

    let mut constraints = str::connect(constraints, ",");

    // Add the clobbers to our constraints list
    if *ia.clobbers != ~"" && constraints != ~"" {
        constraints += ~"," + *ia.clobbers;
    } else {
        constraints += *ia.clobbers;
    }

    debug!("Asm Constraints: %?", constraints);

    let numOutputs = outputs.len();

    // Depending on how many outputs we have, the return type is different
    let output = if numOutputs == 0 {
        T_void()
    } else if numOutputs == 1 {
        val_ty(outputs[0])
    } else {
        T_struct(outputs.map(|o| val_ty(*o)))
    };

    let dialect = match ia.dialect {
        ast::asm_att   => lib::llvm::AD_ATT,
        ast::asm_intel => lib::llvm::AD_Intel
    };

    let r = do str::as_c_str(*ia.asm) |a| {
        do str::as_c_str(constraints) |c| {
            // XXX: Allow selection of at&t or intel
            InlineAsmCall(bcx, a, c, inputs, output, ia.volatile, ia.alignstack, dialect)
        }
    };

    // Again, based on how many outputs we have
    if numOutputs == 1 {
        let op = PointerCast(bcx, aoutputs[0], T_ptr(val_ty(outputs[0])));
        Store(bcx, r, op);
    } else {
        for aoutputs.eachi |i, o| {
            let v = ExtractValue(bcx, r, i);
            let op = PointerCast(bcx, *o, T_ptr(val_ty(outputs[i])));
            Store(bcx, v, op);
        }
    }

    return bcx;

}
<commit_msg>librustc: add default per arch clobbers for asm.<commit_after>// Copyright 2012 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.

/*!
# Translation of inline assembly.
*/

use core::prelude::*;

use lib;
use middle::trans::build::*;
use middle::trans::callee;
use middle::trans::common::*;
use middle::ty;

use syntax::ast;

// Take an inline assembly expression and splat it out via LLVM
pub fn trans_inline_asm(bcx: block, ia: &ast::inline_asm) -> block {

    let mut bcx = bcx;
    let mut constraints = ~[];
    let mut cleanups = ~[];
    let mut aoutputs = ~[];

    // Prepare the output operands
    let outputs = do ia.outputs.map |&(c, out)| {
        constraints.push(copy *c);

        let aoutty = ty::arg {
            mode: ast::expl(ast::by_copy),
            ty: expr_ty(bcx, out)
        };
        aoutputs.push(unpack_result!(bcx, {
            callee::trans_arg_expr(bcx, aoutty, out, &mut cleanups, None, callee::DontAutorefArg)
        }));

        let e = match out.node {
            ast::expr_addr_of(_, e) => e,
            _ => fail!(~"Expression must be addr of")
        };

        let outty = ty::arg {
            mode: ast::expl(ast::by_copy),
            ty: expr_ty(bcx, e)
        };

        unpack_result!(bcx, {
            callee::trans_arg_expr(bcx, outty, e, &mut cleanups, None, callee::DontAutorefArg)
        })

    };

    for cleanups.each |c| {
        revoke_clean(bcx, *c);
    }
    cleanups.clear();

    // Now the input operands
    let inputs = do ia.inputs.map |&(c, in)| {
        constraints.push(copy *c);

        let inty = ty::arg {
            mode: ast::expl(ast::by_copy),
            ty: expr_ty(bcx, in)
        };

        unpack_result!(bcx, {
            callee::trans_arg_expr(bcx, inty, in, &mut cleanups, None, callee::DontAutorefArg)
        })

    };

    for cleanups.each |c| {
        revoke_clean(bcx, *c);
    }

    let mut constraints = str::connect(constraints, ",");

    let mut clobbers = getClobbers();
    if *ia.clobbers != ~"" && clobbers != ~"" {
        clobbers = *ia.clobbers + ~"," + clobbers;
    } else {
        clobbers += *ia.clobbers;
    };

    // Add the clobbers to our constraints list
    if clobbers != ~"" && constraints != ~"" {
        constraints += ~"," + clobbers;
    } else {
        constraints += clobbers;
    }

    debug!("Asm Constraints: %?", constraints);

    let numOutputs = outputs.len();

    // Depending on how many outputs we have, the return type is different
    let output = if numOutputs == 0 {
        T_void()
    } else if numOutputs == 1 {
        val_ty(outputs[0])
    } else {
        T_struct(outputs.map(|o| val_ty(*o)))
    };

    let dialect = match ia.dialect {
        ast::asm_att   => lib::llvm::AD_ATT,
        ast::asm_intel => lib::llvm::AD_Intel
    };

    let r = do str::as_c_str(*ia.asm) |a| {
        do str::as_c_str(constraints) |c| {
            // XXX: Allow selection of at&t or intel
            InlineAsmCall(bcx, a, c, inputs, output, ia.volatile, ia.alignstack, dialect)
        }
    };

    // Again, based on how many outputs we have
    if numOutputs == 1 {
        let op = PointerCast(bcx, aoutputs[0], T_ptr(val_ty(outputs[0])));
        Store(bcx, r, op);
    } else {
        for aoutputs.eachi |i, o| {
            let v = ExtractValue(bcx, r, i);
            let op = PointerCast(bcx, *o, T_ptr(val_ty(outputs[i])));
            Store(bcx, v, op);
        }
    }

    return bcx;

}

// Default per-arch clobbers
// Basically what clang does

#[cfg(target_arch = "arm")]
#[cfg(target_arch = "mips")]
fn getClobbers() -> ~str {
    ~""
}

#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
fn getClobbers() -> ~str {
    ~"~{dirflag},~{fpsr},~{flags}"
}
<|endoftext|>