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

//! Rust stack-limit management
//!
//! Currently Rust uses a segmented-stack-like scheme in order to detect stack
//! overflow for rust threads. In this scheme, the prologue of all functions are
//! preceded with a check to see whether the current stack limits are being
//! exceeded.
//!
//! This module provides the functionality necessary in order to manage these
//! stack limits (which are stored in platform-specific locations). The
//! functions here are used at the borders of the thread lifetime in order to
//! manage these limits.
//!
//! This function is an unstable module because this scheme for stack overflow
//! detection is not guaranteed to continue in the future. Usage of this module
//! is discouraged unless absolutely necessary.

// iOS related notes
//
// It is possible to implement it using idea from
// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h
//
// In short: _pthread_{get,set}_specific_direct allows extremely fast
// access, exactly what is required for segmented stack
// There is a pool of reserved slots for Apple internal use (0..119)
// First dynamic allocated pthread key starts with 257 (on iOS7)
// So using slot 149 should be pretty safe ASSUMING space is reserved
// for every key < first dynamic key
//
// There is also an opportunity to steal keys reserved for Garbage Collection
// ranges 80..89 and 110..119, especially considering the fact Garbage Collection
// never supposed to work on iOS. But as everybody knows it - there is a chance
// that those slots will be re-used, like it happened with key 95 (moved from
// JavaScriptCore to CoreText)
//
// Unfortunately Apple rejected patch to LLVM which generated
// corresponding prolog, decision was taken to disable segmented
// stack support on iOS.

pub const RED_ZONE: usize = 20 * 1024;

/// This function is invoked from rust's current __morestack function. Segmented
/// stacks are currently not enabled as segmented stacks, but rather one giant
/// stack segment. This means that whenever we run out of stack, we want to
/// truly consider it to be stack overflow rather than allocating a new stack.
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
    use intrinsics;

    unsafe {
        // We're calling this function because the stack just ran out. We need
        // to call some other rust functions, but if we invoke the functions
        // right now it'll just trigger this handler being called again. In
        // order to alleviate this, we move the stack limit to be inside of the
        // red zone that was allocated for exactly this reason.
        let limit = get_sp_limit();
        record_sp_limit(limit - RED_ZONE / 2);

        // This probably isn't the best course of action. Ideally one would want
        // to unwind the stack here instead of just aborting the entire process.
        // This is a tricky problem, however. There's a few things which need to
        // be considered:
        //
        //  1. We're here because of a stack overflow, yet unwinding will run
        //     destructors and hence arbitrary code. What if that code overflows
        //     the stack? One possibility is to use the above allocation of an
        //     extra 10k to hope that we don't hit the limit, and if we do then
        //     abort the whole program. Not the best, but kind of hard to deal
        //     with unless we want to switch stacks.
        //
        //  2. LLVM will optimize functions based on whether they can unwind or
        //     not. It will flag functions with 'nounwind' if it believes that
        //     the function cannot trigger unwinding, but if we do unwind on
        //     stack overflow then it means that we could unwind in any function
        //     anywhere. We would have to make sure that LLVM only places the
        //     nounwind flag on functions which don't call any other functions.
        //
        //  3. The function that overflowed may have owned arguments. These
        //     arguments need to have their destructors run, but we haven't even
        //     begun executing the function yet, so unwinding will not run the
        //     any landing pads for these functions. If this is ignored, then
        //     the arguments will just be leaked.
        //
        // Exactly what to do here is a very delicate topic, and is possibly
        // still up in the air for what exactly to do. Some relevant issues:
        //
        //  #3555 - out-of-stack failure leaks arguments
        //  #3695 - should there be a stack limit?
        //  #9855 - possible strategies which could be taken
        //  #9854 - unwinding on windows through __morestack has never worked
        //  #2361 - possible implementation of not using landing pads

        ::rt::util::report_overflow();

        intrinsics::abort();
    }
}

// Windows maintains a record of upper and lower stack bounds in the Thread Information
// Block (TIB), and some syscalls do check that addresses which are supposed to be in
// the stack, indeed lie between these two values.
// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839)
//
// When using Rust-managed stacks (libgreen), we must maintain these values accordingly.
// For OS-managed stacks (libnative), we let the OS manage them for us.
//
// On all other platforms both variants behave identically.

#[inline(always)]
pub unsafe fn record_os_managed_stack_bounds(stack_lo: usize, _stack_hi: usize) {
    record_sp_limit(stack_lo + RED_ZONE);
}

/// Records the current limit of the stack as specified by `end`.
///
/// This is stored in an OS-dependent location, likely inside of the thread
/// local storage. The location that the limit is stored is a pre-ordained
/// location because it's where LLVM has emitted code to check.
///
/// Note that this cannot be called under normal circumstances. This function is
/// changing the stack limit, so upon returning any further function calls will
/// possibly be triggering the morestack logic if you're not careful.
///
/// Also note that this and all of the inside functions are all flagged as
/// "inline(always)" because they're messing around with the stack limits.  This
/// would be unfortunate for the functions themselves to trigger a morestack
/// invocation (if they were an actual function call).
#[inline(always)]
pub unsafe fn record_sp_limit(limit: usize) {
    return target_record_sp_limit(limit);

    // x86-64
    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $$0x60+90*8, %rsi
              movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
    }

    // x86
    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $$0x48+90*4, %eax
              movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }

    // mips, arm - Some brave soul can port these to inline asm, but it's over
    //             my head personally
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        use libc::c_void;
        return record_sp_limit(limit as *const c_void);
        extern {
            fn record_sp_limit(limit: *const c_void);
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS segmented stack is disabled for now, see related notes
    // openbsd - segmented stack is disabled
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    unsafe fn target_record_sp_limit(_: usize) {
    }
}

/// The counterpart of the function above, this function will fetch the current
/// stack limit stored in TLS.
///
/// Note that all of these functions are meant to be exact counterparts of their
/// brethren above, except that the operands are reversed.
///
/// As with the setter, this function does not have a __morestack header and can
/// therefore be called in a "we're out of stack" situation.
#[inline(always)]
pub unsafe fn get_sp_limit() -> usize {
    return target_get_sp_limit();

    // x86-64
    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq $$0x60+90*8, %rsi
              movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }

    // x86
    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl $$0x48+90*4, %eax
              movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }

    // mips, arm - Some brave soul can port these to inline asm, but it's over
    //             my head personally
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        use libc::c_void;
        return get_sp_limit() as usize;
        extern {
            fn get_sp_limit() -> *const c_void;
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS doesn't support segmented stacks yet.
    // openbsd - OpenBSD doesn't support segmented stacks.
    //
    // This function might be called by runtime though
    // so it is unsafe to unreachable, let's return a fixed constant.
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        1024
    }
}
<commit_msg>std: clarify comments about sp* implementations<commit_after>// Copyright 2013-2015 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.

//! Rust stack-limit management
//!
//! Currently Rust uses a segmented-stack-like scheme in order to detect stack
//! overflow for rust threads. In this scheme, the prologue of all functions are
//! preceded with a check to see whether the current stack limits are being
//! exceeded.
//!
//! This module provides the functionality necessary in order to manage these
//! stack limits (which are stored in platform-specific locations). The
//! functions here are used at the borders of the thread lifetime in order to
//! manage these limits.
//!
//! This function is an unstable module because this scheme for stack overflow
//! detection is not guaranteed to continue in the future. Usage of this module
//! is discouraged unless absolutely necessary.

// iOS related notes
//
// It is possible to implement it using idea from
// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h
//
// In short: _pthread_{get,set}_specific_direct allows extremely fast
// access, exactly what is required for segmented stack
// There is a pool of reserved slots for Apple internal use (0..119)
// First dynamic allocated pthread key starts with 257 (on iOS7)
// So using slot 149 should be pretty safe ASSUMING space is reserved
// for every key < first dynamic key
//
// There is also an opportunity to steal keys reserved for Garbage Collection
// ranges 80..89 and 110..119, especially considering the fact Garbage Collection
// never supposed to work on iOS. But as everybody knows it - there is a chance
// that those slots will be re-used, like it happened with key 95 (moved from
// JavaScriptCore to CoreText)
//
// Unfortunately Apple rejected patch to LLVM which generated
// corresponding prolog, decision was taken to disable segmented
// stack support on iOS.

pub const RED_ZONE: usize = 20 * 1024;

/// This function is invoked from rust's current __morestack function. Segmented
/// stacks are currently not enabled as segmented stacks, but rather one giant
/// stack segment. This means that whenever we run out of stack, we want to
/// truly consider it to be stack overflow rather than allocating a new stack.
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
    use intrinsics;

    unsafe {
        // We're calling this function because the stack just ran out. We need
        // to call some other rust functions, but if we invoke the functions
        // right now it'll just trigger this handler being called again. In
        // order to alleviate this, we move the stack limit to be inside of the
        // red zone that was allocated for exactly this reason.
        let limit = get_sp_limit();
        record_sp_limit(limit - RED_ZONE / 2);

        // This probably isn't the best course of action. Ideally one would want
        // to unwind the stack here instead of just aborting the entire process.
        // This is a tricky problem, however. There's a few things which need to
        // be considered:
        //
        //  1. We're here because of a stack overflow, yet unwinding will run
        //     destructors and hence arbitrary code. What if that code overflows
        //     the stack? One possibility is to use the above allocation of an
        //     extra 10k to hope that we don't hit the limit, and if we do then
        //     abort the whole program. Not the best, but kind of hard to deal
        //     with unless we want to switch stacks.
        //
        //  2. LLVM will optimize functions based on whether they can unwind or
        //     not. It will flag functions with 'nounwind' if it believes that
        //     the function cannot trigger unwinding, but if we do unwind on
        //     stack overflow then it means that we could unwind in any function
        //     anywhere. We would have to make sure that LLVM only places the
        //     nounwind flag on functions which don't call any other functions.
        //
        //  3. The function that overflowed may have owned arguments. These
        //     arguments need to have their destructors run, but we haven't even
        //     begun executing the function yet, so unwinding will not run the
        //     any landing pads for these functions. If this is ignored, then
        //     the arguments will just be leaked.
        //
        // Exactly what to do here is a very delicate topic, and is possibly
        // still up in the air for what exactly to do. Some relevant issues:
        //
        //  #3555 - out-of-stack failure leaks arguments
        //  #3695 - should there be a stack limit?
        //  #9855 - possible strategies which could be taken
        //  #9854 - unwinding on windows through __morestack has never worked
        //  #2361 - possible implementation of not using landing pads

        ::rt::util::report_overflow();

        intrinsics::abort();
    }
}

// Windows maintains a record of upper and lower stack bounds in the Thread Information
// Block (TIB), and some syscalls do check that addresses which are supposed to be in
// the stack, indeed lie between these two values.
// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839)
//
// When using Rust-managed stacks (libgreen), we must maintain these values accordingly.
// For OS-managed stacks (libnative), we let the OS manage them for us.
//
// On all other platforms both variants behave identically.

#[inline(always)]
pub unsafe fn record_os_managed_stack_bounds(stack_lo: usize, _stack_hi: usize) {
    record_sp_limit(stack_lo + RED_ZONE);
}

/// Records the current limit of the stack as specified by `end`.
///
/// This is stored in an OS-dependent location, likely inside of the thread
/// local storage. The location that the limit is stored is a pre-ordained
/// location because it's where LLVM has emitted code to check.
///
/// Note that this cannot be called under normal circumstances. This function is
/// changing the stack limit, so upon returning any further function calls will
/// possibly be triggering the morestack logic if you're not careful.
///
/// Also note that this and all of the inside functions are all flagged as
/// "inline(always)" because they're messing around with the stack limits.  This
/// would be unfortunate for the functions themselves to trigger a morestack
/// invocation (if they were an actual function call).
#[inline(always)]
pub unsafe fn record_sp_limit(limit: usize) {
    return target_record_sp_limit(limit);

    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $$0x60+90*8, %rsi
              movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
    }

    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $$0x48+90*4, %eax
              movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_record_sp_limit(_: usize) {
    }

    // mips, arm - The implementations are a bit big for inline asm!
    //             They can be found in src/rt/arch/$target_arch/record_sp.S
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_record_sp_limit(limit: usize) {
        use libc::c_void;
        return record_sp_limit(limit as *const c_void);
        extern {
            fn record_sp_limit(limit: *const c_void);
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS segmented stack is disabled for now, see related notes
    // openbsd - segmented stack is disabled
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    unsafe fn target_record_sp_limit(_: usize) {
    }
}

/// The counterpart of the function above, this function will fetch the current
/// stack limit stored in TLS.
///
/// Note that all of these functions are meant to be exact counterparts of their
/// brethren above, except that the operands are reversed.
///
/// As with the setter, this function does not have a __morestack header and can
/// therefore be called in a "we're out of stack" situation.
#[inline(always)]
pub unsafe fn get_sp_limit() -> usize {
    return target_get_sp_limit();

    #[cfg(all(target_arch = "x86_64",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq $$0x60+90*8, %rsi
              movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }

    #[cfg(all(target_arch = "x86",
              any(target_os = "macos", target_os = "ios")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl $$0x48+90*4, %eax
              movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86",
              any(target_os = "linux", target_os = "freebsd")))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        let limit;
        asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
        return limit;
    }
    #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        return 1024;
    }

    // mips, arm - The implementations are a bit big for inline asm!
    //             They can be found in src/rt/arch/$target_arch/record_sp.S
    #[cfg(any(target_arch = "mips",
              target_arch = "mipsel",
              all(target_arch = "arm", not(target_os = "ios"))))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        use libc::c_void;
        return get_sp_limit() as usize;
        extern {
            fn get_sp_limit() -> *const c_void;
        }
    }

    // aarch64 - FIXME(AARCH64): missing...
    // powerpc - FIXME(POWERPC): missing...
    // arm-ios - iOS doesn't support segmented stacks yet.
    // openbsd - OpenBSD doesn't support segmented stacks.
    //
    // This function might be called by runtime though
    // so it is unsafe to unreachable, let's return a fixed constant.
    #[cfg(any(target_arch = "aarch64",
              target_arch = "powerpc",
              all(target_arch = "arm", target_os = "ios"),
              target_os = "bitrig",
              target_os = "openbsd"))]
    #[inline(always)]
    unsafe fn target_get_sp_limit() -> usize {
        1024
    }
}
<|endoftext|>


<commit_before>use crate::convert::TryFrom;
use crate::fmt;
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::str;
use crate::sync::Arc;
use crate::sys::hermit::abi;
use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
use crate::sys::unsupported;
use crate::sys_common::AsInner;
use crate::time::Duration;

/// Checks whether the HermitCore's socket interface has been started already, and
/// if not, starts it.
pub fn init() -> io::Result<()> {
    if abi::network_init() < 0 {
        return Err(io::Error::new_const(
            ErrorKind::Unknown,
            &"Unable to initialize network interface",
        ));
    }

    Ok(())
}

#[derive(Debug, Clone)]
pub struct Socket(abi::Handle);

impl AsInner<abi::Handle> for Socket {
    fn as_inner(&self) -> &abi::Handle {
        &self.0
    }
}

impl Drop for Socket {
    fn drop(&mut self) {
        let _ = abi::tcpstream::close(self.0);
    }
}

// Arc is used to count the number of used sockets.
// Only if all sockets are released, the drop
// method will close the socket.
#[derive(Clone)]
pub struct TcpStream(Arc<Socket>);

impl TcpStream {
    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
        let addr = addr?;

        match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
            Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
            _ => Err(io::Error::new_const(
                ErrorKind::Unknown,
                &"Unable to initiate a connection on a socket",
            )),
        }
    }

    pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
        match abi::tcpstream::connect(
            saddr.ip().to_string().as_bytes(),
            saddr.port(),
            Some(duration.as_millis() as u64),
        ) {
            Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
            _ => Err(io::Error::new_const(
                ErrorKind::Unknown,
                &"Unable to initiate a connection on a socket",
            )),
        }
    }

    pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
        abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"Unable to set timeout value"))
    }

    pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
        abi::tcpstream::set_write_timeout(
            *self.0.as_inner(),
            duration.map(|d| d.as_millis() as u64),
        )
        .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"Unable to set timeout value"))
    }

    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
        let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
            io::Error::new_const(ErrorKind::Unknown, &"Unable to determine timeout value")
        })?;

        Ok(duration.map(|d| Duration::from_millis(d)))
    }

    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
        let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
            io::Error::new_const(ErrorKind::Unknown, &"Unable to determine timeout value")
        })?;

        Ok(duration.map(|d| Duration::from_millis(d)))
    }

    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
        abi::tcpstream::peek(*self.0.as_inner(), buf)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"set_nodelay failed"))
    }

    pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
        self.read_vectored(&mut [IoSliceMut::new(buffer)])
    }

    pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        let mut size: usize = 0;

        for i in ioslice.iter_mut() {
            let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
                io::Error::new_const(ErrorKind::Unknown, &"Unable to read on socket")
            })?;

            if ret != 0 {
                size += ret;
            }
        }

        Ok(size)
    }

    #[inline]
    pub fn is_read_vectored(&self) -> bool {
        true
    }

    pub fn write(&self, buffer: &[u8]) -> io::Result<usize> {
        self.write_vectored(&[IoSlice::new(buffer)])
    }

    pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result<usize> {
        let mut size: usize = 0;

        for i in ioslice.iter() {
            size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
                io::Error::new_const(ErrorKind::Unknown, &"Unable to write on socket")
            })?;
        }

        Ok(size)
    }

    #[inline]
    pub fn is_write_vectored(&self) -> bool {
        true
    }

    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
        let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"peer_addr failed"))?;

        let saddr = match ipaddr {
            Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
            Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
            _ => {
                return Err(io::Error::new_const(ErrorKind::Unknown, &"peer_addr failed"));
            }
        };

        Ok(saddr)
    }

    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
        unsupported()
    }

    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
        abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to shutdown socket"))
    }

    pub fn duplicate(&self) -> io::Result<TcpStream> {
        Ok(self.clone())
    }

    pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
        abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"set_nodelay failed"))
    }

    pub fn nodelay(&self) -> io::Result<bool> {
        abi::tcpstream::nodelay(*self.0.as_inner())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"nodelay failed"))
    }

    pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
        abi::tcpstream::set_tll(*self.0.as_inner(), tll)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to set TTL"))
    }

    pub fn ttl(&self) -> io::Result<u32> {
        abi::tcpstream::get_tll(*self.0.as_inner())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to get TTL"))
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        unsupported()
    }

    pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
        abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to set blocking mode"))
    }
}

impl fmt::Debug for TcpStream {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

#[derive(Clone)]
pub struct TcpListener(SocketAddr);

impl TcpListener {
    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
        let addr = addr?;

        Ok(TcpListener(*addr))
    }

    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
        Ok(self.0)
    }

    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
        let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"accept failed"))?;
        let saddr = match ipaddr {
            Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
            Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
            _ => {
                return Err(io::Error::new_const(ErrorKind::Unknown, &"accept failed"));
            }
        };

        Ok((TcpStream(Arc::new(Socket(handle))), saddr))
    }

    pub fn duplicate(&self) -> io::Result<TcpListener> {
        Ok(self.clone())
    }

    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn ttl(&self) -> io::Result<u32> {
        unsupported()
    }

    pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn only_v6(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        unsupported()
    }

    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
        unsupported()
    }
}

impl fmt::Debug for TcpListener {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

pub struct UdpSocket(abi::Handle);

impl UdpSocket {
    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
        unsupported()
    }

    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
        unsupported()
    }

    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
        unsupported()
    }

    pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        unsupported()
    }

    pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        unsupported()
    }

    pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
        unsupported()
    }

    pub fn duplicate(&self) -> io::Result<UdpSocket> {
        unsupported()
    }

    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        unsupported()
    }

    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        unsupported()
    }

    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
        unsupported()
    }

    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
        unsupported()
    }

    pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn broadcast(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn multicast_loop_v4(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
        unsupported()
    }

    pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn multicast_loop_v6(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
        unsupported()
    }

    pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
        unsupported()
    }

    pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn ttl(&self) -> io::Result<u32> {
        unsupported()
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        unsupported()
    }

    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
        unsupported()
    }

    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
        unsupported()
    }

    pub fn send(&self, _: &[u8]) -> io::Result<usize> {
        unsupported()
    }

    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
        unsupported()
    }
}

impl fmt::Debug for UdpSocket {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

pub struct LookupHost(!);

impl LookupHost {
    pub fn port(&self) -> u16 {
        self.0
    }
}

impl Iterator for LookupHost {
    type Item = SocketAddr;
    fn next(&mut self) -> Option<SocketAddr> {
        self.0
    }
}

impl TryFrom<&str> for LookupHost {
    type Error = io::Error;

    fn try_from(_v: &str) -> io::Result<LookupHost> {
        unsupported()
    }
}

impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
    type Error = io::Error;

    fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
        unsupported()
    }
}

#[allow(nonstandard_style)]
pub mod netc {
    pub const AF_INET: u8 = 0;
    pub const AF_INET6: u8 = 1;
    pub type sa_family_t = u8;

    #[derive(Copy, Clone)]
    pub struct in_addr {
        pub s_addr: u32,
    }

    #[derive(Copy, Clone)]
    pub struct sockaddr_in {
        pub sin_family: sa_family_t,
        pub sin_port: u16,
        pub sin_addr: in_addr,
    }

    #[derive(Copy, Clone)]
    pub struct in6_addr {
        pub s6_addr: [u8; 16],
    }

    #[derive(Copy, Clone)]
    pub struct sockaddr_in6 {
        pub sin6_family: sa_family_t,
        pub sin6_port: u16,
        pub sin6_addr: in6_addr,
        pub sin6_flowinfo: u32,
        pub sin6_scope_id: u32,
    }

    #[derive(Copy, Clone)]
    pub struct sockaddr {}

    pub type socklen_t = usize;
}
<commit_msg>Fix copy-paste error in sys/hermit error message.<commit_after>use crate::convert::TryFrom;
use crate::fmt;
use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::str;
use crate::sync::Arc;
use crate::sys::hermit::abi;
use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
use crate::sys::unsupported;
use crate::sys_common::AsInner;
use crate::time::Duration;

/// Checks whether the HermitCore's socket interface has been started already, and
/// if not, starts it.
pub fn init() -> io::Result<()> {
    if abi::network_init() < 0 {
        return Err(io::Error::new_const(
            ErrorKind::Unknown,
            &"Unable to initialize network interface",
        ));
    }

    Ok(())
}

#[derive(Debug, Clone)]
pub struct Socket(abi::Handle);

impl AsInner<abi::Handle> for Socket {
    fn as_inner(&self) -> &abi::Handle {
        &self.0
    }
}

impl Drop for Socket {
    fn drop(&mut self) {
        let _ = abi::tcpstream::close(self.0);
    }
}

// Arc is used to count the number of used sockets.
// Only if all sockets are released, the drop
// method will close the socket.
#[derive(Clone)]
pub struct TcpStream(Arc<Socket>);

impl TcpStream {
    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
        let addr = addr?;

        match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
            Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
            _ => Err(io::Error::new_const(
                ErrorKind::Unknown,
                &"Unable to initiate a connection on a socket",
            )),
        }
    }

    pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
        match abi::tcpstream::connect(
            saddr.ip().to_string().as_bytes(),
            saddr.port(),
            Some(duration.as_millis() as u64),
        ) {
            Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
            _ => Err(io::Error::new_const(
                ErrorKind::Unknown,
                &"Unable to initiate a connection on a socket",
            )),
        }
    }

    pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
        abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"Unable to set timeout value"))
    }

    pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
        abi::tcpstream::set_write_timeout(
            *self.0.as_inner(),
            duration.map(|d| d.as_millis() as u64),
        )
        .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"Unable to set timeout value"))
    }

    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
        let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
            io::Error::new_const(ErrorKind::Unknown, &"Unable to determine timeout value")
        })?;

        Ok(duration.map(|d| Duration::from_millis(d)))
    }

    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
        let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
            io::Error::new_const(ErrorKind::Unknown, &"Unable to determine timeout value")
        })?;

        Ok(duration.map(|d| Duration::from_millis(d)))
    }

    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
        abi::tcpstream::peek(*self.0.as_inner(), buf)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"peek failed"))
    }

    pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
        self.read_vectored(&mut [IoSliceMut::new(buffer)])
    }

    pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        let mut size: usize = 0;

        for i in ioslice.iter_mut() {
            let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
                io::Error::new_const(ErrorKind::Unknown, &"Unable to read on socket")
            })?;

            if ret != 0 {
                size += ret;
            }
        }

        Ok(size)
    }

    #[inline]
    pub fn is_read_vectored(&self) -> bool {
        true
    }

    pub fn write(&self, buffer: &[u8]) -> io::Result<usize> {
        self.write_vectored(&[IoSlice::new(buffer)])
    }

    pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result<usize> {
        let mut size: usize = 0;

        for i in ioslice.iter() {
            size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
                io::Error::new_const(ErrorKind::Unknown, &"Unable to write on socket")
            })?;
        }

        Ok(size)
    }

    #[inline]
    pub fn is_write_vectored(&self) -> bool {
        true
    }

    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
        let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"peer_addr failed"))?;

        let saddr = match ipaddr {
            Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
            Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
            _ => {
                return Err(io::Error::new_const(ErrorKind::Unknown, &"peer_addr failed"));
            }
        };

        Ok(saddr)
    }

    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
        unsupported()
    }

    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
        abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to shutdown socket"))
    }

    pub fn duplicate(&self) -> io::Result<TcpStream> {
        Ok(self.clone())
    }

    pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
        abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"set_nodelay failed"))
    }

    pub fn nodelay(&self) -> io::Result<bool> {
        abi::tcpstream::nodelay(*self.0.as_inner())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"nodelay failed"))
    }

    pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
        abi::tcpstream::set_tll(*self.0.as_inner(), tll)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to set TTL"))
    }

    pub fn ttl(&self) -> io::Result<u32> {
        abi::tcpstream::get_tll(*self.0.as_inner())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to get TTL"))
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        unsupported()
    }

    pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
        abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode)
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"unable to set blocking mode"))
    }
}

impl fmt::Debug for TcpStream {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

#[derive(Clone)]
pub struct TcpListener(SocketAddr);

impl TcpListener {
    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
        let addr = addr?;

        Ok(TcpListener(*addr))
    }

    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
        Ok(self.0)
    }

    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
        let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
            .map_err(|_| io::Error::new_const(ErrorKind::Unknown, &"accept failed"))?;
        let saddr = match ipaddr {
            Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
            Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
            _ => {
                return Err(io::Error::new_const(ErrorKind::Unknown, &"accept failed"));
            }
        };

        Ok((TcpStream(Arc::new(Socket(handle))), saddr))
    }

    pub fn duplicate(&self) -> io::Result<TcpListener> {
        Ok(self.clone())
    }

    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn ttl(&self) -> io::Result<u32> {
        unsupported()
    }

    pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn only_v6(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        unsupported()
    }

    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
        unsupported()
    }
}

impl fmt::Debug for TcpListener {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

pub struct UdpSocket(abi::Handle);

impl UdpSocket {
    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
        unsupported()
    }

    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
        unsupported()
    }

    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
        unsupported()
    }

    pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        unsupported()
    }

    pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
        unsupported()
    }

    pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
        unsupported()
    }

    pub fn duplicate(&self) -> io::Result<UdpSocket> {
        unsupported()
    }

    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        unsupported()
    }

    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
        unsupported()
    }

    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
        unsupported()
    }

    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
        unsupported()
    }

    pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn broadcast(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn multicast_loop_v4(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
        unsupported()
    }

    pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn multicast_loop_v6(&self) -> io::Result<bool> {
        unsupported()
    }

    pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
        unsupported()
    }

    pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
        unsupported()
    }

    pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
        unsupported()
    }

    pub fn ttl(&self) -> io::Result<u32> {
        unsupported()
    }

    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
        unsupported()
    }

    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
        unsupported()
    }

    pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
        unsupported()
    }

    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
        unsupported()
    }

    pub fn send(&self, _: &[u8]) -> io::Result<usize> {
        unsupported()
    }

    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
        unsupported()
    }
}

impl fmt::Debug for UdpSocket {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

pub struct LookupHost(!);

impl LookupHost {
    pub fn port(&self) -> u16 {
        self.0
    }
}

impl Iterator for LookupHost {
    type Item = SocketAddr;
    fn next(&mut self) -> Option<SocketAddr> {
        self.0
    }
}

impl TryFrom<&str> for LookupHost {
    type Error = io::Error;

    fn try_from(_v: &str) -> io::Result<LookupHost> {
        unsupported()
    }
}

impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
    type Error = io::Error;

    fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
        unsupported()
    }
}

#[allow(nonstandard_style)]
pub mod netc {
    pub const AF_INET: u8 = 0;
    pub const AF_INET6: u8 = 1;
    pub type sa_family_t = u8;

    #[derive(Copy, Clone)]
    pub struct in_addr {
        pub s_addr: u32,
    }

    #[derive(Copy, Clone)]
    pub struct sockaddr_in {
        pub sin_family: sa_family_t,
        pub sin_port: u16,
        pub sin_addr: in_addr,
    }

    #[derive(Copy, Clone)]
    pub struct in6_addr {
        pub s6_addr: [u8; 16],
    }

    #[derive(Copy, Clone)]
    pub struct sockaddr_in6 {
        pub sin6_family: sa_family_t,
        pub sin6_port: u16,
        pub sin6_addr: in6_addr,
        pub sin6_flowinfo: u32,
        pub sin6_scope_id: u32,
    }

    #[derive(Copy, Clone)]
    pub struct sockaddr {}

    pub type socklen_t = usize;
}
<|endoftext|>


<commit_before>use cairo;

use crate::float_eq_cairo::ApproxEqCairo;

mod rect {
    use core::ops::{Add, Range, Sub};
    use num_traits::Zero;

    // Use our own min() and max() that are acceptable for floating point

    fn min<T: PartialOrd>(x: T, y: T) -> T {
        if x <= y {
            x
        } else {
            y
        }
    }

    fn max<T: PartialOrd>(x: T, y: T) -> T {
        if x >= y {
            x
        } else {
            y
        }
    }

    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
    pub struct Rect<T> {
        pub x0: T,
        pub y0: T,
        pub x1: T,
        pub y1: T,
    }

    impl<T> Rect<T> {
        #[inline]
        pub fn new(x0: T, y0: T, x1: T, y1: T) -> Self {
            Self { x0, y0, x1, y1 }
        }
    }

    impl<T> Rect<T>
    where
        T: Copy + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T> + Zero,
    {
        #[inline]
        pub fn from_size(w: T, h: T) -> Self {
            Self {
                x0: Zero::zero(),
                y0: Zero::zero(),
                x1: w,
                y1: h,
            }
        }

        #[inline]
        pub fn width(&self) -> T {
            self.x1 - self.x0
        }

        #[inline]
        pub fn height(&self) -> T {
            self.y1 - self.y0
        }

        #[inline]
        pub fn size(&self) -> (T, T) {
            (self.width(), self.height())
        }

        #[inline]
        pub fn x_range(&self) -> Range<T> {
            self.x0..self.x1
        }

        #[inline]
        pub fn y_range(&self) -> Range<T> {
            self.y0..self.y1
        }

        #[inline]
        pub fn contains(self, x: T, y: T) -> bool {
            x >= self.x0 && x < self.x1 && y >= self.y0 && y < self.y1
        }

        #[inline]
        pub fn translate(&self, by: (T, T)) -> Self {
            Self {
                x0: self.x0 + by.0,
                y0: self.y0 + by.1,
                x1: self.x1 + by.0,
                y1: self.y1 + by.1,
            }
        }

        #[inline]
        pub fn intersection(&self, rect: &Self) -> Option<Self> {
            let (x0, y0, x1, y1) = (
                max(self.x0, rect.x0),
                max(self.y0, rect.y0),
                min(self.x1, rect.x1),
                min(self.y1, rect.y1),
            );

            if x1 > x0 && y1 > y0 {
                Some(Self { x0, y0, x1, y1 })
            } else {
                None
            }
        }

        #[inline]
        pub fn union(&self, rect: &Self) -> Self {
            Self {
                x0: min(self.x0, rect.x0),
                y0: min(self.y0, rect.y0),
                x1: max(self.x1, rect.x1),
                y1: max(self.y1, rect.y1),
            }
        }
    }

    impl Rect<i32> {
        #[inline]
        pub fn is_empty(&self) -> bool {
            self.width() == Zero::zero() || self.height() == Zero::zero()
        }

        #[inline]
        pub fn scale(self, x: f64, y: f64) -> Self {
            Self {
                x0: (f64::from(self.x0) * x).floor() as i32,
                y0: (f64::from(self.y0) * y).floor() as i32,
                x1: (f64::from(self.x1) * x).ceil() as i32,
                y1: (f64::from(self.y1) * y).ceil() as i32,
            }
        }
    }
}

pub type IRect = rect::Rect<i32>;

impl From<cairo::Rectangle> for IRect {
    #[inline]
    fn from(r: cairo::Rectangle) -> Self {
        Self {
            x0: r.x.floor() as i32,
            y0: r.y.floor() as i32,
            x1: (r.x + r.width).ceil() as i32,
            y1: (r.y + r.height).ceil() as i32,
        }
    }
}

impl From<IRect> for cairo::Rectangle {
    #[inline]
    fn from(r: IRect) -> Self {
        Self {
            x: f64::from(r.x0),
            y: f64::from(r.y0),
            width: f64::from(r.x1 - r.x0),
            height: f64::from(r.y1 - r.y0),
        }
    }
}

pub trait RectangleExt {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle;
    fn from_size(width: f64, height: f64) -> cairo::Rectangle;
    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle;
    fn is_empty(&self) -> bool;
    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle>;
    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle;
}

impl RectangleExt for cairo::Rectangle {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x,
            y,
            width,
            height,
        }
    }

    fn from_size(width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width,
            height,
        }
    }

    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: x0,
            y: y0,
            width: x1 - x0,
            height: y1 - y0,
        }
    }

    fn is_empty(&self) -> bool {
        self.width.approx_eq_cairo(0.0) || self.height.approx_eq_cairo(0.0)
    }

    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle> {
        let (x1, y1, x2, y2) = (
            self.x.max(rect.x),
            self.y.max(rect.y),
            (self.x + self.width).min(rect.x + rect.width),
            (self.y + self.height).min(rect.y + rect.height),
        );

        if x2 > x1 && y2 > y1 {
            Some(cairo::Rectangle {
                x: x1,
                y: y1,
                width: x2 - x1,
                height: y2 - y1,
            })
        } else {
            None
        }
    }

    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let (x1, y1, x2, y2) = (
            self.x.min(rect.x),
            self.y.min(rect.y),
            (self.x + self.width).max(rect.x + rect.width),
            (self.y + self.height).max(rect.y + rect.height),
        );

        cairo::Rectangle {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle {
        cairo::Rectangle {
            x: self.x + by.0,
            y: self.y + by.1,
            width: self.width,
            height: self.height,
        }
    }
}

pub trait TransformRect {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
}

impl TransformRect for cairo::Matrix {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let points = vec![
            self.transform_point(rect.x, rect.y),
            self.transform_point(rect.x + rect.width, rect.y),
            self.transform_point(rect.x, rect.y + rect.height),
            self.transform_point(rect.x + rect.width, rect.y + rect.height),
        ];

        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
            let (x, y) = points[0];

            (x, y, x, y)
        };

        for &(x, y) in points.iter().take(4).skip(1) {
            if x < xmin {
                xmin = x;
            }

            if x > xmax {
                xmax = x;
            }

            if y < ymin {
                ymin = y;
            }

            if y > ymax {
                ymax = y;
            }
        }

        cairo::Rectangle {
            x: xmin,
            y: ymin,
            width: xmax - xmin,
            height: ymax - ymin,
        }
    }
}

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

    #[test]
    fn empty_rect() {
        let empty = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 0.0,
            height: 0.0,
        };
        let not_empty = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        assert!(empty.is_empty());
        assert!(!not_empty.is_empty());
    }

    #[test]
    fn intersect_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };
        let r3 = cairo::Rectangle {
            x: 10.0,
            y: 10.0,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.intersection(&r2).unwrap();
        assert_approx_eq_cairo!(0.42_f64, r.x);
        assert_approx_eq_cairo!(0.42_f64, r.y);
        assert_approx_eq_cairo!(2.94_f64, r.width);
        assert_approx_eq_cairo!(2.94_f64, r.height);

        let r = r1.intersection(&r3);
        assert!(r.is_none());
    }

    #[test]
    fn union_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.union(&r2);
        assert_approx_eq_cairo!(0.22_f64, r.x);
        assert_approx_eq_cairo!(0.22_f64, r.y);
        assert_approx_eq_cairo!(4.34_f64, r.width);
        assert_approx_eq_cairo!(4.34_f64, r.height);
    }

    #[test]
    fn transform_rect() {
        let r = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 3.14,
            height: 3.14,
        };

        let m = cairo::Matrix::identity();
        let tr = m.transform_rect(&r);
        assert_eq!(tr, r);

        let m = cairo::Matrix::new(2.0, 0.0, 0.0, 2.0, 1.5, 1.5);
        let tr = m.transform_rect(&r);
        assert_approx_eq_cairo!(2.34_f64, tr.x);
        assert_approx_eq_cairo!(2.34_f64, tr.y);
        assert_approx_eq_cairo!(6.28_f64, tr.width);
        assert_approx_eq_cairo!(6.28_f64, tr.height);
    }
}
<commit_msg>Add a Rect type alias for rect::Rect<f64><commit_after>use cairo;

use crate::float_eq_cairo::ApproxEqCairo;

mod rect {
    use crate::float_eq_cairo::ApproxEqCairo;
    use core::ops::{Add, Range, Sub};
    use num_traits::Zero;

    // Use our own min() and max() that are acceptable for floating point

    fn min<T: PartialOrd>(x: T, y: T) -> T {
        if x <= y {
            x
        } else {
            y
        }
    }

    fn max<T: PartialOrd>(x: T, y: T) -> T {
        if x >= y {
            x
        } else {
            y
        }
    }

    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
    pub struct Rect<T> {
        pub x0: T,
        pub y0: T,
        pub x1: T,
        pub y1: T,
    }

    impl<T> Rect<T> {
        #[inline]
        pub fn new(x0: T, y0: T, x1: T, y1: T) -> Self {
            Self { x0, y0, x1, y1 }
        }
    }

    impl<T> Rect<T>
    where
        T: Copy + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T> + Zero,
    {
        #[inline]
        pub fn from_size(w: T, h: T) -> Self {
            Self {
                x0: Zero::zero(),
                y0: Zero::zero(),
                x1: w,
                y1: h,
            }
        }

        #[inline]
        pub fn width(&self) -> T {
            self.x1 - self.x0
        }

        #[inline]
        pub fn height(&self) -> T {
            self.y1 - self.y0
        }

        #[inline]
        pub fn size(&self) -> (T, T) {
            (self.width(), self.height())
        }

        #[inline]
        pub fn x_range(&self) -> Range<T> {
            self.x0..self.x1
        }

        #[inline]
        pub fn y_range(&self) -> Range<T> {
            self.y0..self.y1
        }

        #[inline]
        pub fn contains(self, x: T, y: T) -> bool {
            x >= self.x0 && x < self.x1 && y >= self.y0 && y < self.y1
        }

        #[inline]
        pub fn translate(&self, by: (T, T)) -> Self {
            Self {
                x0: self.x0 + by.0,
                y0: self.y0 + by.1,
                x1: self.x1 + by.0,
                y1: self.y1 + by.1,
            }
        }

        #[inline]
        pub fn intersection(&self, rect: &Self) -> Option<Self> {
            let (x0, y0, x1, y1) = (
                max(self.x0, rect.x0),
                max(self.y0, rect.y0),
                min(self.x1, rect.x1),
                min(self.y1, rect.y1),
            );

            if x1 > x0 && y1 > y0 {
                Some(Self { x0, y0, x1, y1 })
            } else {
                None
            }
        }

        #[inline]
        pub fn union(&self, rect: &Self) -> Self {
            Self {
                x0: min(self.x0, rect.x0),
                y0: min(self.y0, rect.y0),
                x1: max(self.x1, rect.x1),
                y1: max(self.y1, rect.y1),
            }
        }
    }

    impl Rect<i32> {
        #[inline]
        pub fn is_empty(&self) -> bool {
            self.width() == Zero::zero() || self.height() == Zero::zero()
        }

        #[inline]
        pub fn scale(self, x: f64, y: f64) -> Self {
            Self {
                x0: (f64::from(self.x0) * x).floor() as i32,
                y0: (f64::from(self.y0) * y).floor() as i32,
                x1: (f64::from(self.x1) * x).ceil() as i32,
                y1: (f64::from(self.y1) * y).ceil() as i32,
            }
        }
    }

    impl Rect<f64> {
        #[inline]
        pub fn is_empty(&self) -> bool {
            self.width().approx_eq_cairo(0.0) || self.height().approx_eq_cairo(0.0)
        }

        #[inline]
        pub fn scale(self, x: f64, y: f64) -> Self {
            Self {
                x0: self.x0 * x,
                y0: self.y0 * y,
                x1: self.x1 * x,
                y1: self.y1 * y,
            }
        }
    }
}

pub type Rect = rect::Rect<f64>;

impl From<cairo::Rectangle> for Rect {
    #[inline]
    fn from(r: cairo::Rectangle) -> Self {
        Self {
            x0: r.x,
            y0: r.y,
            x1: r.x + r.width,
            y1: r.y + r.height,
        }
    }
}

impl From<Rect> for cairo::Rectangle {
    #[inline]
    fn from(r: Rect) -> Self {
        Self {
            x: r.x0,
            y: r.y0,
            width: r.x1 - r.x0,
            height: r.y1 - r.y0,
        }
    }
}

pub type IRect = rect::Rect<i32>;

impl From<cairo::Rectangle> for IRect {
    #[inline]
    fn from(r: cairo::Rectangle) -> Self {
        Self {
            x0: r.x.floor() as i32,
            y0: r.y.floor() as i32,
            x1: (r.x + r.width).ceil() as i32,
            y1: (r.y + r.height).ceil() as i32,
        }
    }
}

impl From<IRect> for cairo::Rectangle {
    #[inline]
    fn from(r: IRect) -> Self {
        Self {
            x: f64::from(r.x0),
            y: f64::from(r.y0),
            width: f64::from(r.x1 - r.x0),
            height: f64::from(r.y1 - r.y0),
        }
    }
}

pub trait RectangleExt {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle;
    fn from_size(width: f64, height: f64) -> cairo::Rectangle;
    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle;
    fn is_empty(&self) -> bool;
    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle>;
    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle;
}

impl RectangleExt for cairo::Rectangle {
    fn new(x: f64, y: f64, width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x,
            y,
            width,
            height,
        }
    }

    fn from_size(width: f64, height: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width,
            height,
        }
    }

    fn from_extents(x0: f64, y0: f64, x1: f64, y1: f64) -> cairo::Rectangle {
        cairo::Rectangle {
            x: x0,
            y: y0,
            width: x1 - x0,
            height: y1 - y0,
        }
    }

    fn is_empty(&self) -> bool {
        self.width.approx_eq_cairo(0.0) || self.height.approx_eq_cairo(0.0)
    }

    fn intersection(&self, rect: &cairo::Rectangle) -> Option<cairo::Rectangle> {
        let (x1, y1, x2, y2) = (
            self.x.max(rect.x),
            self.y.max(rect.y),
            (self.x + self.width).min(rect.x + rect.width),
            (self.y + self.height).min(rect.y + rect.height),
        );

        if x2 > x1 && y2 > y1 {
            Some(cairo::Rectangle {
                x: x1,
                y: y1,
                width: x2 - x1,
                height: y2 - y1,
            })
        } else {
            None
        }
    }

    fn union(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let (x1, y1, x2, y2) = (
            self.x.min(rect.x),
            self.y.min(rect.y),
            (self.x + self.width).max(rect.x + rect.width),
            (self.y + self.height).max(rect.y + rect.height),
        );

        cairo::Rectangle {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }

    fn translate(&self, by: (f64, f64)) -> cairo::Rectangle {
        cairo::Rectangle {
            x: self.x + by.0,
            y: self.y + by.1,
            width: self.width,
            height: self.height,
        }
    }
}

pub trait TransformRect {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle;
}

impl TransformRect for cairo::Matrix {
    fn transform_rect(&self, rect: &cairo::Rectangle) -> cairo::Rectangle {
        let points = vec![
            self.transform_point(rect.x, rect.y),
            self.transform_point(rect.x + rect.width, rect.y),
            self.transform_point(rect.x, rect.y + rect.height),
            self.transform_point(rect.x + rect.width, rect.y + rect.height),
        ];

        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
            let (x, y) = points[0];

            (x, y, x, y)
        };

        for &(x, y) in points.iter().take(4).skip(1) {
            if x < xmin {
                xmin = x;
            }

            if x > xmax {
                xmax = x;
            }

            if y < ymin {
                ymin = y;
            }

            if y > ymax {
                ymax = y;
            }
        }

        cairo::Rectangle {
            x: xmin,
            y: ymin,
            width: xmax - xmin,
            height: ymax - ymin,
        }
    }
}

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

    #[test]
    fn empty_rect() {
        let empty = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 0.0,
            height: 0.0,
        };
        let not_empty = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        assert!(empty.is_empty());
        assert!(!not_empty.is_empty());
    }

    #[test]
    fn intersect_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };
        let r3 = cairo::Rectangle {
            x: 10.0,
            y: 10.0,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.intersection(&r2).unwrap();
        assert_approx_eq_cairo!(0.42_f64, r.x);
        assert_approx_eq_cairo!(0.42_f64, r.y);
        assert_approx_eq_cairo!(2.94_f64, r.width);
        assert_approx_eq_cairo!(2.94_f64, r.height);

        let r = r1.intersection(&r3);
        assert!(r.is_none());
    }

    #[test]
    fn union_rects() {
        let r1 = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 4.14,
            height: 4.14,
        };
        let r2 = cairo::Rectangle {
            x: 0.22,
            y: 0.22,
            width: 3.14,
            height: 3.14,
        };

        let r = r1.union(&r2);
        assert_approx_eq_cairo!(0.22_f64, r.x);
        assert_approx_eq_cairo!(0.22_f64, r.y);
        assert_approx_eq_cairo!(4.34_f64, r.width);
        assert_approx_eq_cairo!(4.34_f64, r.height);
    }

    #[test]
    fn transform_rect() {
        let r = cairo::Rectangle {
            x: 0.42,
            y: 0.42,
            width: 3.14,
            height: 3.14,
        };

        let m = cairo::Matrix::identity();
        let tr = m.transform_rect(&r);
        assert_eq!(tr, r);

        let m = cairo::Matrix::new(2.0, 0.0, 0.0, 2.0, 1.5, 1.5);
        let tr = m.transform_rect(&r);
        assert_approx_eq_cairo!(2.34_f64, tr.x);
        assert_approx_eq_cairo!(2.34_f64, tr.y);
        assert_approx_eq_cairo!(6.28_f64, tr.width);
        assert_approx_eq_cairo!(6.28_f64, tr.height);
    }
}
<|endoftext|>


<commit_before>//! Utility functions for dealing with sRGB colors.
//!
//! The constant values in this module are taken from http://www.color.org/chardata/rgb/srgb.xalter
use std::slice;

use cairo;
use cairo::prelude::SurfaceExt;
use cairo_sys;

use filters::context::IRect;
use util::clamp;

/// Converts an sRGB color value to a linear sRGB color value (undoes the gamma correction).
///
/// The input and the output are supposed to be in the [0, 1] range.
#[inline]
pub fn linearize(c: f64) -> f64 {
    if c <= (12.92 * 0.0031308) {
        c / 12.92
    } else {
        ((c + 0.055) / 1.055).powf(2.4)
    }
}

/// Converts a linear sRGB color value to a normal sRGB color value (applies the gamma correction).
///
/// The input and the output are supposed to be in the [0, 1] range.
#[inline]
pub fn unlinearize(c: f64) -> f64 {
    if c <= 0.0031308 {
        12.92 * c
    } else {
        1.055 * c.powf(1f64 / 2.4) - 0.055
    }
}

/// Converts an sRGB surface to a linear sRGB surface (undoes the gamma correction).
///
/// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
pub fn linearize_surface(
    surface: &cairo::ImageSurface,
    bounds: IRect,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.get_width();
    let height = surface.get_height();
    let input_stride = surface.get_stride();

    assert!(bounds.x0 >= 0);
    assert!(bounds.y0 >= 0);
    assert!(bounds.x1 < width);
    assert!(bounds.y1 < height);

    // TODO: this currently gives "non-exclusive access" (can we make read-only borrows?)
    // let input_data = surface.get_data().unwrap();
    surface.flush();
    if surface.status() != cairo::Status::Success {
        return Err(surface.status());
    }
    let input_data_ptr = unsafe { cairo_sys::cairo_image_surface_get_data(surface.to_raw_none()) };
    if input_data_ptr.is_null() {
        return Err(cairo::Status::SurfaceFinished);
    }
    let input_data_len = input_stride as usize * height as usize;
    let input_data = unsafe { slice::from_raw_parts(input_data_ptr, input_data_len) };

    let mut output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output_surface.get_stride();

    {
        let mut output_data = output_surface.get_data().unwrap();

        for y in bounds.y0..bounds.y1 {
            for x in bounds.x0..bounds.x1 {
                let input_index = (y * input_stride + x * 4) as usize;
                let output_index = (y * output_stride + x * 4) as usize;

                for c in 0..3 {
                    let input_value = f64::from(input_data[input_index + c]) / 255f64;
                    let output_value = linearize(input_value);
                    output_data[output_index + c] = (output_value * 255f64).round() as u8;
                }
                output_data[output_index + 3] = input_data[input_index + 3];
            }
        }
    }

    Ok(output_surface)
}

/// Converts a linear sRGB surface to a normal sRGB surface (applies the gamma correction).
///
/// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
pub fn unlinearize_surface(
    surface: &cairo::ImageSurface,
    bounds: IRect,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.get_width();
    let height = surface.get_height();
    let input_stride = surface.get_stride();

    assert!(bounds.x0 >= 0);
    assert!(bounds.y0 >= 0);
    assert!(bounds.x1 < width);
    assert!(bounds.y1 < height);

    // TODO: this currently gives "non-exclusive access" (can we make read-only borrows?)
    // let input_data = surface.get_data().unwrap();
    surface.flush();
    if surface.status() != cairo::Status::Success {
        return Err(surface.status());
    }
    let input_data_ptr = unsafe { cairo_sys::cairo_image_surface_get_data(surface.to_raw_none()) };
    if input_data_ptr.is_null() {
        return Err(cairo::Status::SurfaceFinished);
    }
    let input_data_len = input_stride as usize * height as usize;
    let input_data = unsafe { slice::from_raw_parts(input_data_ptr, input_data_len) };

    let mut output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output_surface.get_stride();

    {
        let mut output_data = output_surface.get_data().unwrap();

        for y in bounds.y0..bounds.y1 {
            for x in bounds.x0..bounds.x1 {
                let input_index = (y * input_stride + x * 4) as usize;
                let output_index = (y * output_stride + x * 4) as usize;

                let alpha = input_data[input_index + 3];

                for c in 0..3 {
                    let input_value = f64::from(input_data[input_index + c]) / 255f64;
                    let output_value = unlinearize(input_value);
                    let output_value = (output_value * 255f64).round() as u8;

                    // Remember to clamp to alpha (since we're operating premultiplied alpha).
                    // I think that technically, if this operation produces a value above alpha, it
                    // means that the input surface contained invalid linear sRGB colors?
                    output_data[output_index + c] = clamp(output_value, 0u8, alpha);
                }
                output_data[output_index + 3] = input_data[input_index + 3];
            }
        }
    }

    Ok(output_surface)
}
<commit_msg>Fix srgb::{linearize,unlinearize}_surface()<commit_after>//! Utility functions for dealing with sRGB colors.
//!
//! The constant values in this module are taken from http://www.color.org/chardata/rgb/srgb.xalter
use std::slice;

use cairo;
use cairo::prelude::SurfaceExt;
use cairo_sys;

use filters::context::IRect;

/// Converts an sRGB color value to a linear sRGB color value (undoes the gamma correction).
///
/// The input and the output are supposed to be in the [0, 1] range.
#[inline]
pub fn linearize(c: f64) -> f64 {
    if c <= (12.92 * 0.0031308) {
        c / 12.92
    } else {
        ((c + 0.055) / 1.055).powf(2.4)
    }
}

/// Converts a linear sRGB color value to a normal sRGB color value (applies the gamma correction).
///
/// The input and the output are supposed to be in the [0, 1] range.
#[inline]
pub fn unlinearize(c: f64) -> f64 {
    if c <= 0.0031308 {
        12.92 * c
    } else {
        1.055 * c.powf(1f64 / 2.4) - 0.055
    }
}

/// Converts an sRGB surface to a linear sRGB surface (undoes the gamma correction).
///
/// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
pub fn linearize_surface(
    surface: &cairo::ImageSurface,
    bounds: IRect,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.get_width();
    let height = surface.get_height();
    let input_stride = surface.get_stride();

    assert!(bounds.x0 >= 0);
    assert!(bounds.y0 >= 0);
    assert!(bounds.x1 < width);
    assert!(bounds.y1 < height);

    // TODO: this currently gives "non-exclusive access" (can we make read-only borrows?)
    // let input_data = surface.get_data().unwrap();
    surface.flush();
    if surface.status() != cairo::Status::Success {
        return Err(surface.status());
    }
    let input_data_ptr = unsafe { cairo_sys::cairo_image_surface_get_data(surface.to_raw_none()) };
    if input_data_ptr.is_null() {
        return Err(cairo::Status::SurfaceFinished);
    }
    let input_data_len = input_stride as usize * height as usize;
    let input_data = unsafe { slice::from_raw_parts(input_data_ptr, input_data_len) };

    let mut output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output_surface.get_stride();

    {
        let mut output_data = output_surface.get_data().unwrap();

        for y in bounds.y0..bounds.y1 {
            for x in bounds.x0..bounds.x1 {
                let input_index = (y * input_stride + x * 4) as usize;
                let output_index = (y * output_stride + x * 4) as usize;

                let alpha = input_data[input_index + 3];

                if alpha > 0 {
                    let alpha = f64::from(alpha) / 255f64;

                    for c in 0..3 {
                        let input_value = f64::from(input_data[input_index + c]) / 255f64;
                        let input_value = input_value / alpha; // Unpremultiply alpha.

                        let output_value = linearize(input_value);
                        let output_value = output_value * alpha; // Premultiply alpha again.

                        output_data[output_index + c] = (output_value * 255f64).round() as u8;
                    }
                }

                output_data[output_index + 3] = alpha;
            }
        }
    }

    Ok(output_surface)
}

/// Converts a linear sRGB surface to a normal sRGB surface (applies the gamma correction).
///
/// The returned surface is transparent everywhere except the rectangle defined by `bounds`.
pub fn unlinearize_surface(
    surface: &cairo::ImageSurface,
    bounds: IRect,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.get_width();
    let height = surface.get_height();
    let input_stride = surface.get_stride();

    assert!(bounds.x0 >= 0);
    assert!(bounds.y0 >= 0);
    assert!(bounds.x1 < width);
    assert!(bounds.y1 < height);

    // TODO: this currently gives "non-exclusive access" (can we make read-only borrows?)
    // let input_data = surface.get_data().unwrap();
    surface.flush();
    if surface.status() != cairo::Status::Success {
        return Err(surface.status());
    }
    let input_data_ptr = unsafe { cairo_sys::cairo_image_surface_get_data(surface.to_raw_none()) };
    if input_data_ptr.is_null() {
        return Err(cairo::Status::SurfaceFinished);
    }
    let input_data_len = input_stride as usize * height as usize;
    let input_data = unsafe { slice::from_raw_parts(input_data_ptr, input_data_len) };

    let mut output_surface = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output_surface.get_stride();

    {
        let mut output_data = output_surface.get_data().unwrap();

        for y in bounds.y0..bounds.y1 {
            for x in bounds.x0..bounds.x1 {
                let input_index = (y * input_stride + x * 4) as usize;
                let output_index = (y * output_stride + x * 4) as usize;

                let alpha = input_data[input_index + 3];

                if alpha > 0 {
                    let alpha = f64::from(alpha) / 255f64;

                    for c in 0..3 {
                        let input_value = f64::from(input_data[input_index + c]) / 255f64;
                        let input_value = input_value / alpha; // Unpremultiply alpha.

                        let output_value = unlinearize(input_value);
                        let output_value = output_value * alpha; // Premultiply alpha again.

                        output_data[output_index + c] = (output_value * 255f64).round() as u8;
                    }
                }

                output_data[output_index + 3] = alpha;
            }
        }
    }

    Ok(output_surface)
}
<|endoftext|>


<commit_before>//! # storage
//!
//! Manages internal cargo-make persistant storage.
//!

#[cfg(test)]
#[path = "./storage_test.rs"]
mod storage_test;

use std::fs::File;
use std::io::Read;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use toml;
use environment;
use types::Storage;

fn load_from_path(directory: PathBuf) -> Storage {
    let file_path = Path::new(&directory).join("storage.toml");

    if file_path.exists() {
        match File::open(&file_path) {
            Ok(mut file) => {
                let mut storage_str = String::new();
                file.read_to_string(&mut storage_str).unwrap();

                let mut storage_data: Storage = match toml::from_str(&storage_str) {
                    Ok(mut value) => value,
                    _ => Storage::new(),
                };

                match file_path.to_str() {
                    Some(value) => storage_data.file_name = Some(value.to_string()),
                    None => storage_data.file_name = None,
                };

                storage_data
            }
            _ => Storage::new(),
        }
    } else {
        Storage::new()
    }
}

/// Loads the persisted data
pub(crate) fn load() -> Storage {
    match environment::get_cargo_make_home() {
        Some(directory) => load_from_path(directory),
        None => Storage::new(),
    }
}

/// Stores the data
pub(crate) fn store(storage_data: &Storage) {
    match environment::get_cargo_make_home() {
        Some(directory) => {
            let file_name = directory.join("storage.toml");

            match File::open(&file_name) {
                Ok(mut file) => match toml::to_string_pretty(storage_data) {
                    Ok(toml_str) => {
                        let data = toml_str.as_bytes();

                        match file.write_all(data) {
                            Err(error) => error!(
                                "Error while writing to storage file: {:#?}, error: {:#?}",
                                &file_name, error
                            ),
                            _ => (),
                        }
                    }
                    Err(error) => error!(
                        "Error during serialization of storage, file: {:#?}, error: {:#?}",
                        &file_name, error
                    ),
                },
                Err(error) => error!(
                    "Error while creating/appending to storage file: {:#?}, error: {:#?}",
                    &file_name, error
                ),
            };
        }
        None => (),
    }
}
<commit_msg>initial work on #68<commit_after>//! # storage
//!
//! Manages internal cargo-make persistant storage.
//!

#[cfg(test)]
#[path = "./storage_test.rs"]
mod storage_test;

use std::fs::File;
use std::io::Read;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use toml;
use environment;
use types::Storage;

fn load_from_path(directory: PathBuf) -> Storage {
    let file_path = Path::new(&directory).join("storage.toml");

    if file_path.exists() {
        match File::open(&file_path) {
            Ok(mut file) => {
                let mut storage_str = String::new();
                file.read_to_string(&mut storage_str).unwrap();

                let mut storage_data: Storage = match toml::from_str(&storage_str) {
                    Ok(value) => value,
                    _ => Storage::new(),
                };

                match file_path.to_str() {
                    Some(value) => storage_data.file_name = Some(value.to_string()),
                    None => storage_data.file_name = None,
                };

                storage_data
            }
            _ => Storage::new(),
        }
    } else {
        Storage::new()
    }
}

/// Loads the persisted data
pub(crate) fn load() -> Storage {
    match environment::get_cargo_make_home() {
        Some(directory) => load_from_path(directory),
        None => Storage::new(),
    }
}

/// Stores the data
pub(crate) fn store(storage_data: &Storage) {
    match environment::get_cargo_make_home() {
        Some(directory) => {
            let file_name = directory.join("storage.toml");

            match File::open(&file_name) {
                Ok(mut file) => match toml::to_string_pretty(storage_data) {
                    Ok(toml_str) => {
                        let data = toml_str.as_bytes();

                        match file.write_all(data) {
                            Err(error) => error!(
                                "Error while writing to storage file: {:#?}, error: {:#?}",
                                &file_name, error
                            ),
                            _ => (),
                        }
                    }
                    Err(error) => error!(
                        "Error during serialization of storage, file: {:#?}, error: {:#?}",
                        &file_name, error
                    ),
                },
                Err(error) => error!(
                    "Error while creating/appending to storage file: {:#?}, error: {:#?}",
                    &file_name, error
                ),
            };
        }
        None => (),
    }
}
<|endoftext|>