<commit_before>#![allow(unused_variables)]

use block::Block;
use cocoa::base::{BOOL, id};
use cocoa::foundation::NSUInteger;
use libc::c_void;
use {MTLFeatureSet, MTLPipelineOption, MTLResourceOptions, MTLSize};

/// The `MTLDevice` protocol defines the interface to a single graphics processor (GPU). You use
/// an object that conforms to this protocol to query the capabilities of the processor and to
/// allocate objects used to access those capabilities.
///
/// Your app does not define classes that implement this protocol; it is used by Metal to provide
/// a device object to your app. To obtain a system device, call the `MTLCreateSystemDefaultDevice`
/// function or select a result from the `MTLCopyAllDevices` function.
///
/// Most objects in Metal that perform graphics rendering and computational work are associated
/// directly with a specific device. For example, texture objects are created by a device object
/// and can be used only with that device. Most methods on a `MTLDevice` object create non-transient
/// objects, including command queues, resources (such as buffers and textures), and pipeline
/// states. These objects can be expensive to create and you are encouraged to create them soon
/// after your app launches and reuse them throughout the lifetime of your app. Avoid creating these
/// objects in performance sensitive code.
pub trait MTLDevice {
    /// A Boolean value that indicates whether a device supports a packed depth/stencil buffer.
    /// (read-only)
    ///
    /// # Discussion
    ///
    /// If the value is `YES`, the device supports the `MTLPixelFormatDepth24Unorm_Stencil8` pixel
    /// format.
    unsafe fn depth24Stencil8PixelFormatSupported(self) -> BOOL;

    /// A Boolean value that indicates whether a device is configured as headless. (read-only)
    ///
    /// # Discussion
    ///
    /// If the value is `YES`, the device can not and does not have any displays attached.
    unsafe fn headless(self) -> BOOL;

    /// Boolean value that indicates whether a device is low-power. (read-only)
    ///
    /// # Discussion
    ///
    /// On multi-GPU systems that support automatic graphics switching, this property will return
    /// `YES` for the lower-power GPU.
    unsafe fn lowPower(self) -> BOOL;

    /// The maximum number of threads along each dimension of a threadgroup. (read-only)
    ///
    /// # Discussion
    ///
    /// This is the maximum compute grid size (width, height, depth) supported by the device.
    ///
    /// For more information on the specific capabilities of each feature set, see
    /// [Metal Programming Guide](https://developer.apple.com/library/prerelease/ios/documentation/
    ///     Miscellaneous/Conceptual/MetalProgrammingGuide/Introduction/
    ///     Introduction.html#//apple_ref/doc/uid/TP40014221
    /// ).
    unsafe fn maxThreadsPerGroup(self) -> MTLSize;

    /// A string that identifies the device. (read-only)
    ///
    /// # Discussion
    ///
    /// When this object is viewed in the Xcode debugger, its name is used to label the device.
    /// The name should only be used for informational purposes.
    unsafe fn name(self) -> id;

    /// Determines whether a device implements a particular feature set.
    ///
    /// # Parameters
    ///
    /// `featureSet` - A value that indicates the feature set you are interested in.
    ///
    /// # Return Value
    ///
    /// `YES` if the feature set is supported by the device; otherwise `NO`.
    ///
    /// # Discussion
    ///
    /// For more information on the specific capabilities of each feature set, see
    /// [Metal Programming Guide](https://developer.apple.com/library/prerelease/ios/documentation/
    ///     Miscellaneous/Conceptual/MetalProgrammingGuide/Introduction/
    ///     Introduction.html#//apple_ref/doc/uid/TP40014221
    /// ).
    unsafe fn supportsFeatureSet(self, featureSet: MTLFeatureSet) -> BOOL;

    /// Determines whether a device supports a given texture sample count.
    ///
    /// # Parameters
    ///
    /// `textureSampleCount` - The sample count you are interested in.
    ///
    /// # Return Value
    ///
    /// `YES` if the sample count is supported by the device; otherwise `NO`.
    ///
    /// # Discussion
    ///
    /// For more information on the specific capabilities of each feature set, see
    /// [Metal Programming Guide](https://developer.apple.com/library/prerelease/ios/documentation/
    ///     Miscellaneous/Conceptual/MetalProgrammingGuide/Introduction/
    ///     Introduction.html#//apple_ref/doc/uid/TP40014221
    /// ).
    unsafe fn supportsTextureSampleCount(self, textureSampleCount: NSUInteger) -> BOOL;

    unsafe fn newDefaultLibrary(self) -> id;
    unsafe fn newLibraryWithFile_error(self, filePath: id, error: *mut id) -> id;
    unsafe fn newLibraryWithSource_options_completionHandler(
        self, source: id, options: id, completionHandler: MTLNewLibraryCompletionHandler) -> id;

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newLibraryWithSource_options_error(self, source: id, options: id, error: *mut id)
                                                 -> id;
    unsafe fn newLibraryWithData_error(self, data: id, error: *mut id) -> id;

    unsafe fn newCommandQueue(self) -> id;

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newCommandQueueWithMaxCommandBufferCount(self, maxCommandBufferCount: NSUInteger)
                                                       -> id;

    unsafe fn newBufferWithLength_options(self, length: NSUInteger, options: MTLResourceOptions) -> id;
    unsafe fn newBufferWithBytes_length_options(self, pointer: *mut c_void, length: NSUInteger,
                                                options: MTLResourceOptions)
                                                -> id;
    unsafe fn newBufferWithBytesNoCopy_length_options_deallocator(self, pointer: *mut c_void,
                                                                  length: NSUInteger,
                                                                  options: MTLResourceOptions,
                                                                  deallocator: Block<(*mut c_void,
                                                                                NSUInteger),
                                                                                ()>);

    unsafe fn newTextureWithDescriptor(self, descriptor: id) -> id;

    unsafe fn newSamplerStateWithDescriptor(self, descriptor: id) -> id;

    unsafe fn newStencilDepthStateWithDescriptor(self, descriptor: id) -> id;

    unsafe fn newRenderPipleineStateWithDescriptor_completionHandler(
            self, descriptor: id, completionHandler: MTLNewRenderPipleineStateCompletionHandler);

    unsafe fn newRenderPipelineStateWithDescriptor_options_completionHandler(
            self, descriptor: id, options: MTLPipelineOption,
            completionHandler: MTLNewRenderPipelineStateWithReflectionCompletionHandler);

    unsafe fn newRenderPipelineStateWithDescriptor_error(self, descriptor: id, error: id);

    unsafe fn newRenderPipelineStateWithDescriptor_options_reflection(self, descriptor: id,
                                                                      options: MTLPipelineOption,
                                                                      reflection: id, error: id);

    unsafe fn newComputePipelineStateWithFunction_completionHandler(
            self, function: id, completionHandler: MTLNewComputePipelineStateCompletionHandler);

    unsafe fn newComputePipelineStateWithFunction_options_completionHandler(
            self, function: id, options: MTLPipelineOption,
            completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler);

    unsafe fn newComputePipelineStateWithFunction_error(self, function: id, error: id);

    unsafe fn newComputePipelineStateWithFunction_options_reflection_error(
            self, function: id, options: MTLPipelineOption, reflection: id, error: id);

    unsafe fn newComputePipelineStateWithDescriptor_options_completionHandler(
            self, descriptor: id, options: MTLPipelineOption,
            completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler);

    unsafe fn newComputePipelineStateWithDescriptor_options_reflection_error(
            self, descriptor: id, options: MTLPipelineOption, reflection: id, error: id);
}

impl MTLDevice for id {
    unsafe fn depth24Stencil8PixelFormatSupported(self) -> BOOL {
        msg_send![self, isDepth24Stencil8PixelFormatSupported]
    }

    unsafe fn headless(self) -> BOOL {
        msg_send![self, isHeadless]
    }

    unsafe fn lowPower(self) -> BOOL {
        msg_send![self, isLowPower]
    }

    unsafe fn maxThreadsPerGroup(self) -> MTLSize {
        msg_send![self, maxThreadsPerThreadgroup]
    }

    unsafe fn name(self) -> id {
        msg_send![self, name]
    }

    unsafe fn supportsFeatureSet(self, featureSet: MTLFeatureSet) -> BOOL {
        msg_send![self, supportsFeatureSet:featureSet]
    }

    unsafe fn supportsTextureSampleCount(self, textureSampleCount: NSUInteger) -> BOOL {
        msg_send![self, supportsTextureSampleCount:textureSampleCount]
    }

    unsafe fn newDefaultLibrary(self) -> id {
        msg_send![self, newDefaultLibrary]
    }

    unsafe fn newLibraryWithFile_error(self, filePath: id, error: *mut id) -> id {
        msg_send![self, newLibraryWithFile:filePath error:error]
    }

    unsafe fn newLibraryWithSource_options_completionHandler(
        self, source: id, options: id, completionHandler: MTLNewLibraryCompletionHandler)
        -> id {
        msg_send![self, newLibraryWithSource:source options:options
                           completionHandler:completionHandler]
    }

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newLibraryWithSource_options_error(self, source: id, options: id, error: *mut id)
                                                 -> id {
        msg_send![self, newLibraryWithSource:source options:options error:error]
    }

    unsafe fn newLibraryWithData_error(self, data: id, error: *mut id) -> id {
        msg_send![self, newLibraryWithData:data error:error]
    }

    unsafe fn newCommandQueue(self) -> id {
        msg_send![self, newCommandQueue]
    }

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newCommandQueueWithMaxCommandBufferCount(self, maxCommandBufferCount: NSUInteger)
                                                       -> id {
        msg_send![self, newCommandQueueWithMaxCommandBufferCount:maxCommandBufferCount]
    }

    unsafe fn newBufferWithLength_options(self, length: NSUInteger, options: MTLResourceOptions) -> id {
        msg_send![self, newBufferWithLength:length options:options]
    }

    unsafe fn newBufferWithBytes_length_options(self, pointer: *mut c_void, length: NSUInteger,
                                                options: MTLResourceOptions)
                                                -> id {
        msg_send![self, newBufferWithBytes:pointer length:length options:options]
    }

    unsafe fn newBufferWithBytesNoCopy_length_options_deallocator(self, pointer: *mut c_void,
                                                                  length: NSUInteger,
                                                                  options: MTLResourceOptions,
                                                                  deallocator: Block<(*mut c_void,
                                                                                NSUInteger),
                                                                                ()>) {
        msg_send![self, newBufferWithBytesNoCopy:pointer
                                          length:length
                                         options:options
                                     deallocator:deallocator]
    }

    unsafe fn newTextureWithDescriptor(self, descriptor: id) -> id {
        msg_send![self, newTextureWithDescriptor:descriptor]
    }

    unsafe fn newSamplerStateWithDescriptor(self, descriptor: id) -> id {
        msg_send![self, newSamplerStateWithDescriptor:descriptor]
    }

    unsafe fn newStencilDepthStateWithDescriptor(self, descriptor: id) -> id {
        msg_send![self, newStencilDepthStateWithDescriptor:descriptor]
    }

    unsafe fn newRenderPipleineStateWithDescriptor_completionHandler(
            self, descriptor: id,
            completionHandler: MTLNewRenderPipleineStateCompletionHandler) {
        msg_send![self, newRenderPipleineStateWithDescriptor:descriptor
                                           completionHandler:completionHandler]
    }

    unsafe fn newRenderPipelineStateWithDescriptor_options_completionHandler(
            self, descriptor: id, options: MTLPipelineOption,
            completionHandler: MTLNewRenderPipelineStateWithReflectionCompletionHandler) {
        msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                     options:options
                                           completionHandler:completionHandler]
    }

    unsafe fn newRenderPipelineStateWithDescriptor_error(self, descriptor: id, error: id) {
        msg_send![self, newRenderPipelineStateWithDescriptor:descriptor error:error]
    }

    unsafe fn newRenderPipelineStateWithDescriptor_options_reflection(self, descriptor: id,
                                                                      options: MTLPipelineOption,
                                                                      reflection: id, error: id) {
        msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                     options:options
                                                  reflection:reflection
                                                       error:error]
    }

    unsafe fn newComputePipelineStateWithFunction_completionHandler(
                  self,
                  function: id,
                  completionHandler: MTLNewComputePipelineStateCompletionHandler) {
        msg_send![self, newComputePipelineStateWithFunction:function
                                          completionHandler:completionHandler]
    }

    unsafe fn newComputePipelineStateWithFunction_options_completionHandler(
            self, function: id, options: MTLPipelineOption,
            completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler) {
        msg_send![self, newComputePipelineStateWithFunction:function
                                                    options:options
                                          completionHandler:completionHandler]
    }

    unsafe fn newComputePipelineStateWithFunction_error(self, function: id, error: id) {
        msg_send![self, newComputePipelineStateWithFunction:function error:error]
    }

    unsafe fn newComputePipelineStateWithFunction_options_reflection_error(
            self, function: id, options: MTLPipelineOption, reflection: id, error: id) {
        msg_send![self, newComputePipelineStateWithFunction:function
                                                    options:options
                                                 reflection:reflection
                                                      error:error]
    }

    unsafe fn newComputePipelineStateWithDescriptor_options_completionHandler(
                   self,
                   descriptor: id,
                   options: MTLPipelineOption,
                   completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler) {
        msg_send![self, newComputePipelineStateWithDescriptor:descriptor
                                                      options:options
                                            completionHandler:completionHandler]
    }
    unsafe fn newComputePipelineStateWithDescriptor_options_reflection_error(
            self, descriptor: id, options: MTLPipelineOption, reflection: id, error: id) {
        msg_send![self, newComputePipelineStateWithDescriptor:descriptor
                                                      options:options
                                                   reflection:reflection
                                                        error:error]
    }
}

pub type MTLNewLibraryCompletionHandler = Block<(id, id), ()>;
pub type MTLNewRenderPipleineStateCompletionHandler = Block<(id, id), ()>;
pub type MTLNewRenderPipelineStateWithReflectionCompletionHandler = Block<(id, id, id), ()>;
pub type MTLNewComputePipelineStateCompletionHandler = Block<(id, id), ()>;
pub type MTLNewComputePipelineStateWithReflectionCompletionHandler = Block<(id, id, id), ()>;
<commit_msg>Alter mutability (not sure if correct)<commit_after>#![allow(unused_variables)]

use block::Block;
use cocoa::base::{BOOL, id};
use cocoa::foundation::NSUInteger;
use libc::c_void;
use {MTLFeatureSet, MTLPipelineOption, MTLResourceOptions, MTLSize};

/// The `MTLDevice` protocol defines the interface to a single graphics processor (GPU). You use
/// an object that conforms to this protocol to query the capabilities of the processor and to
/// allocate objects used to access those capabilities.
///
/// Your app does not define classes that implement this protocol; it is used by Metal to provide
/// a device object to your app. To obtain a system device, call the `MTLCreateSystemDefaultDevice`
/// function or select a result from the `MTLCopyAllDevices` function.
///
/// Most objects in Metal that perform graphics rendering and computational work are associated
/// directly with a specific device. For example, texture objects are created by a device object
/// and can be used only with that device. Most methods on a `MTLDevice` object create non-transient
/// objects, including command queues, resources (such as buffers and textures), and pipeline
/// states. These objects can be expensive to create and you are encouraged to create them soon
/// after your app launches and reuse them throughout the lifetime of your app. Avoid creating these
/// objects in performance sensitive code.
pub trait MTLDevice {
    /// A Boolean value that indicates whether a device supports a packed depth/stencil buffer.
    /// (read-only)
    ///
    /// # Discussion
    ///
    /// If the value is `YES`, the device supports the `MTLPixelFormatDepth24Unorm_Stencil8` pixel
    /// format.
    unsafe fn depth24Stencil8PixelFormatSupported(self) -> BOOL;

    /// A Boolean value that indicates whether a device is configured as headless. (read-only)
    ///
    /// # Discussion
    ///
    /// If the value is `YES`, the device can not and does not have any displays attached.
    unsafe fn headless(self) -> BOOL;

    /// Boolean value that indicates whether a device is low-power. (read-only)
    ///
    /// # Discussion
    ///
    /// On multi-GPU systems that support automatic graphics switching, this property will return
    /// `YES` for the lower-power GPU.
    unsafe fn lowPower(self) -> BOOL;

    /// The maximum number of threads along each dimension of a threadgroup. (read-only)
    ///
    /// # Discussion
    ///
    /// This is the maximum compute grid size (width, height, depth) supported by the device.
    ///
    /// For more information on the specific capabilities of each feature set, see
    /// [Metal Programming Guide](https://developer.apple.com/library/prerelease/ios/documentation/
    ///     Miscellaneous/Conceptual/MetalProgrammingGuide/Introduction/
    ///     Introduction.html#//apple_ref/doc/uid/TP40014221
    /// ).
    unsafe fn maxThreadsPerGroup(self) -> MTLSize;

    /// A string that identifies the device. (read-only)
    ///
    /// # Discussion
    ///
    /// When this object is viewed in the Xcode debugger, its name is used to label the device.
    /// The name should only be used for informational purposes.
    unsafe fn name(self) -> id;

    /// Determines whether a device implements a particular feature set.
    ///
    /// # Parameters
    ///
    /// `featureSet` - A value that indicates the feature set you are interested in.
    ///
    /// # Return Value
    ///
    /// `YES` if the feature set is supported by the device; otherwise `NO`.
    ///
    /// # Discussion
    ///
    /// For more information on the specific capabilities of each feature set, see
    /// [Metal Programming Guide](https://developer.apple.com/library/prerelease/ios/documentation/
    ///     Miscellaneous/Conceptual/MetalProgrammingGuide/Introduction/
    ///     Introduction.html#//apple_ref/doc/uid/TP40014221
    /// ).
    unsafe fn supportsFeatureSet(self, featureSet: MTLFeatureSet) -> BOOL;

    /// Determines whether a device supports a given texture sample count.
    ///
    /// # Parameters
    ///
    /// `textureSampleCount` - The sample count you are interested in.
    ///
    /// # Return Value
    ///
    /// `YES` if the sample count is supported by the device; otherwise `NO`.
    ///
    /// # Discussion
    ///
    /// For more information on the specific capabilities of each feature set, see
    /// [Metal Programming Guide](https://developer.apple.com/library/prerelease/ios/documentation/
    ///     Miscellaneous/Conceptual/MetalProgrammingGuide/Introduction/
    ///     Introduction.html#//apple_ref/doc/uid/TP40014221
    /// ).
    unsafe fn supportsTextureSampleCount(self, textureSampleCount: NSUInteger) -> BOOL;

    unsafe fn newDefaultLibrary(self) -> id;
    unsafe fn newLibraryWithFile_error(self, filePath: id, error: *mut id) -> id;
    unsafe fn newLibraryWithSource_options_completionHandler(
        self, source: id, options: id, completionHandler: MTLNewLibraryCompletionHandler) -> id;

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newLibraryWithSource_options_error(self, source: id, options: id, error: *mut id)
                                                 -> id;
    unsafe fn newLibraryWithData_error(self, data: id, error: *mut id) -> id;

    unsafe fn newCommandQueue(self) -> id;

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newCommandQueueWithMaxCommandBufferCount(self, maxCommandBufferCount: NSUInteger)
                                                       -> id;

    unsafe fn newBufferWithLength_options(self, length: NSUInteger, options: MTLResourceOptions) -> id;
    // TODO(burtonage): Check mutability here
    unsafe fn newBufferWithBytes_length_options(self, pointer: *const c_void, length: NSUInteger,
                                                options: MTLResourceOptions)
                                                -> id;
    // TODO(burtonage): Check mutability here
    unsafe fn newBufferWithBytesNoCopy_length_options_deallocator(self, pointer: *mut c_void,
                                                                  length: NSUInteger,
                                                                  options: MTLResourceOptions,
                                                                  deallocator: Block<(*mut c_void,
                                                                                NSUInteger),
                                                                                ()>);

    unsafe fn newTextureWithDescriptor(self, descriptor: id) -> id;

    unsafe fn newSamplerStateWithDescriptor(self, descriptor: id) -> id;

    unsafe fn newStencilDepthStateWithDescriptor(self, descriptor: id) -> id;

    unsafe fn newRenderPipleineStateWithDescriptor_completionHandler(
            self, descriptor: id, completionHandler: MTLNewRenderPipleineStateCompletionHandler);

    unsafe fn newRenderPipelineStateWithDescriptor_options_completionHandler(
            self, descriptor: id, options: MTLPipelineOption,
            completionHandler: MTLNewRenderPipelineStateWithReflectionCompletionHandler);

    unsafe fn newRenderPipelineStateWithDescriptor_error(self, descriptor: id, error: id);

    unsafe fn newRenderPipelineStateWithDescriptor_options_reflection(self, descriptor: id,
                                                                      options: MTLPipelineOption,
                                                                      reflection: id, error: id);

    unsafe fn newComputePipelineStateWithFunction_completionHandler(
            self, function: id, completionHandler: MTLNewComputePipelineStateCompletionHandler);

    unsafe fn newComputePipelineStateWithFunction_options_completionHandler(
            self, function: id, options: MTLPipelineOption,
            completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler);

    unsafe fn newComputePipelineStateWithFunction_error(self, function: id, error: id);

    unsafe fn newComputePipelineStateWithFunction_options_reflection_error(
            self, function: id, options: MTLPipelineOption, reflection: id, error: id);

    unsafe fn newComputePipelineStateWithDescriptor_options_completionHandler(
            self, descriptor: id, options: MTLPipelineOption,
            completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler);

    unsafe fn newComputePipelineStateWithDescriptor_options_reflection_error(
            self, descriptor: id, options: MTLPipelineOption, reflection: id, error: id);
}

impl MTLDevice for id {
    unsafe fn depth24Stencil8PixelFormatSupported(self) -> BOOL {
        msg_send![self, isDepth24Stencil8PixelFormatSupported]
    }

    unsafe fn headless(self) -> BOOL {
        msg_send![self, isHeadless]
    }

    unsafe fn lowPower(self) -> BOOL {
        msg_send![self, isLowPower]
    }

    unsafe fn maxThreadsPerGroup(self) -> MTLSize {
        msg_send![self, maxThreadsPerThreadgroup]
    }

    unsafe fn name(self) -> id {
        msg_send![self, name]
    }

    unsafe fn supportsFeatureSet(self, featureSet: MTLFeatureSet) -> BOOL {
        msg_send![self, supportsFeatureSet:featureSet]
    }

    unsafe fn supportsTextureSampleCount(self, textureSampleCount: NSUInteger) -> BOOL {
        msg_send![self, supportsTextureSampleCount:textureSampleCount]
    }

    unsafe fn newDefaultLibrary(self) -> id {
        msg_send![self, newDefaultLibrary]
    }

    unsafe fn newLibraryWithFile_error(self, filePath: id, error: *mut id) -> id {
        msg_send![self, newLibraryWithFile:filePath error:error]
    }

    unsafe fn newLibraryWithSource_options_completionHandler(
        self, source: id, options: id, completionHandler: MTLNewLibraryCompletionHandler)
        -> id {
        msg_send![self, newLibraryWithSource:source options:options
                           completionHandler:completionHandler]
    }

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newLibraryWithSource_options_error(self, source: id, options: id, error: *mut id)
                                                 -> id {
        msg_send![self, newLibraryWithSource:source options:options error:error]
    }

    unsafe fn newLibraryWithData_error(self, data: id, error: *mut id) -> id {
        msg_send![self, newLibraryWithData:data error:error]
    }

    unsafe fn newCommandQueue(self) -> id {
        msg_send![self, newCommandQueue]
    }

    #[cfg_attr(rustfmt, rustfmt_skip)] // This definition is too long to fit on one line
    unsafe fn newCommandQueueWithMaxCommandBufferCount(self, maxCommandBufferCount: NSUInteger)
                                                       -> id {
        msg_send![self, newCommandQueueWithMaxCommandBufferCount:maxCommandBufferCount]
    }

    unsafe fn newBufferWithLength_options(self, length: NSUInteger, options: MTLResourceOptions) -> id {
        msg_send![self, newBufferWithLength:length options:options]
    }

    unsafe fn newBufferWithBytes_length_options(self, pointer: *const c_void, length: NSUInteger,
                                                options: MTLResourceOptions)
                                                -> id {
        msg_send![self, newBufferWithBytes:pointer length:length options:options]
    }

    unsafe fn newBufferWithBytesNoCopy_length_options_deallocator(self, pointer: *mut c_void,
                                                                  length: NSUInteger,
                                                                  options: MTLResourceOptions,
                                                                  deallocator: Block<(*mut c_void,
                                                                                NSUInteger),
                                                                                ()>) {
        msg_send![self, newBufferWithBytesNoCopy:pointer
                                          length:length
                                         options:options
                                     deallocator:deallocator]
    }

    unsafe fn newTextureWithDescriptor(self, descriptor: id) -> id {
        msg_send![self, newTextureWithDescriptor:descriptor]
    }

    unsafe fn newSamplerStateWithDescriptor(self, descriptor: id) -> id {
        msg_send![self, newSamplerStateWithDescriptor:descriptor]
    }

    unsafe fn newStencilDepthStateWithDescriptor(self, descriptor: id) -> id {
        msg_send![self, newStencilDepthStateWithDescriptor:descriptor]
    }

    unsafe fn newRenderPipleineStateWithDescriptor_completionHandler(
            self, descriptor: id,
            completionHandler: MTLNewRenderPipleineStateCompletionHandler) {
        msg_send![self, newRenderPipleineStateWithDescriptor:descriptor
                                           completionHandler:completionHandler]
    }

    unsafe fn newRenderPipelineStateWithDescriptor_options_completionHandler(
            self, descriptor: id, options: MTLPipelineOption,
            completionHandler: MTLNewRenderPipelineStateWithReflectionCompletionHandler) {
        msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                     options:options
                                           completionHandler:completionHandler]
    }

    unsafe fn newRenderPipelineStateWithDescriptor_error(self, descriptor: id, error: id) {
        msg_send![self, newRenderPipelineStateWithDescriptor:descriptor error:error]
    }

    unsafe fn newRenderPipelineStateWithDescriptor_options_reflection(self, descriptor: id,
                                                                      options: MTLPipelineOption,
                                                                      reflection: id, error: id) {
        msg_send![self, newRenderPipelineStateWithDescriptor:descriptor
                                                     options:options
                                                  reflection:reflection
                                                       error:error]
    }

    unsafe fn newComputePipelineStateWithFunction_completionHandler(
                  self,
                  function: id,
                  completionHandler: MTLNewComputePipelineStateCompletionHandler) {
        msg_send![self, newComputePipelineStateWithFunction:function
                                          completionHandler:completionHandler]
    }

    unsafe fn newComputePipelineStateWithFunction_options_completionHandler(
            self, function: id, options: MTLPipelineOption,
            completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler) {
        msg_send![self, newComputePipelineStateWithFunction:function
                                                    options:options
                                          completionHandler:completionHandler]
    }

    unsafe fn newComputePipelineStateWithFunction_error(self, function: id, error: id) {
        msg_send![self, newComputePipelineStateWithFunction:function error:error]
    }

    unsafe fn newComputePipelineStateWithFunction_options_reflection_error(
            self, function: id, options: MTLPipelineOption, reflection: id, error: id) {
        msg_send![self, newComputePipelineStateWithFunction:function
                                                    options:options
                                                 reflection:reflection
                                                      error:error]
    }

    unsafe fn newComputePipelineStateWithDescriptor_options_completionHandler(
                   self,
                   descriptor: id,
                   options: MTLPipelineOption,
                   completionHandler: MTLNewComputePipelineStateWithReflectionCompletionHandler) {
        msg_send![self, newComputePipelineStateWithDescriptor:descriptor
                                                      options:options
                                            completionHandler:completionHandler]
    }
    unsafe fn newComputePipelineStateWithDescriptor_options_reflection_error(
            self, descriptor: id, options: MTLPipelineOption, reflection: id, error: id) {
        msg_send![self, newComputePipelineStateWithDescriptor:descriptor
                                                      options:options
                                                   reflection:reflection
                                                        error:error]
    }
}

pub type MTLNewLibraryCompletionHandler = Block<(id, id), ()>;
pub type MTLNewRenderPipleineStateCompletionHandler = Block<(id, id), ()>;
pub type MTLNewRenderPipelineStateWithReflectionCompletionHandler = Block<(id, id, id), ()>;
pub type MTLNewComputePipelineStateCompletionHandler = Block<(id, id), ()>;
pub type MTLNewComputePipelineStateWithReflectionCompletionHandler = Block<(id, id, id), ()>;
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::{collections::HashSet, error::Error, path::Path};

use rustc_hash::FxHashMap;

use crossbeam_channel::{unbounded, Receiver};
use ra_db::{CrateGraph, FileId, SourceRootId};
use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags};
use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace};
use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
use ra_vfs_glob::RustPackageFilterBuilder;

type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;

fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
    FileId(f.0)
}
fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
    SourceRootId(r.0)
}

pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
    let root = std::env::current_dir()?.join(root);
    let ws = ProjectWorkspace::discover(root.as_ref())?;
    let project_roots = ws.to_roots();
    let (sender, receiver) = unbounded();
    let sender = Box::new(move |t| sender.send(t).unwrap());
    let (mut vfs, roots) = Vfs::new(
        project_roots
            .iter()
            .map(|pkg_root| {
                RootEntry::new(
                    pkg_root.path().clone(),
                    RustPackageFilterBuilder::default()
                        .set_member(pkg_root.is_member())
                        .into_vfs_filter(),
                )
            })
            .collect(),
        sender,
        Watch(false),
    );

    // FIXME: cfg options?
    let default_cfg_options = {
        let mut opts = get_rustc_cfg_options();
        opts.insert_atom("test".into());
        opts.insert_atom("debug_assertion".into());
        opts
    };

    let (crate_graph, _crate_names) =
        ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| {
            let vfs_file = vfs.load(path);
            log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
            vfs_file.map(vfs_file_to_id)
        });
    log::debug!("crate graph: {:?}", crate_graph);

    let source_roots = roots
        .iter()
        .map(|&vfs_root| {
            let source_root_id = vfs_root_to_id(vfs_root);
            let project_root = project_roots
                .iter()
                .find(|it| it.path() == &vfs.root2path(vfs_root))
                .unwrap()
                .clone();
            (source_root_id, project_root)
        })
        .collect::<FxHashMap<_, _>>();
    let host = load(&source_roots, crate_graph, &mut vfs, receiver);
    Ok((host, source_roots))
}

pub fn load(
    source_roots: &FxHashMap<SourceRootId, PackageRoot>,
    crate_graph: CrateGraph,
    vfs: &mut Vfs,
    receiver: Receiver<VfsTask>,
) -> AnalysisHost {
    let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
    let mut host = AnalysisHost::new(lru_cap, FeatureFlags::default());
    let mut analysis_change = AnalysisChange::new();
    analysis_change.set_crate_graph(crate_graph);

    // wait until Vfs has loaded all roots
    let mut roots_loaded = HashSet::new();
    for task in receiver {
        vfs.handle_task(task);
        let mut done = false;
        for change in vfs.commit_changes() {
            match change {
                VfsChange::AddRoot { root, files } => {
                    let source_root_id = vfs_root_to_id(root);
                    let is_local = source_roots[&source_root_id].is_member();
                    log::debug!(
                        "loaded source root {:?} with path {:?}",
                        source_root_id,
                        vfs.root2path(root)
                    );
                    analysis_change.add_root(source_root_id, is_local);
                    analysis_change.set_debug_root_path(
                        source_root_id,
                        source_roots[&source_root_id].path().display().to_string(),
                    );

                    let mut file_map = FxHashMap::default();
                    for (vfs_file, path, text) in files {
                        let file_id = vfs_file_to_id(vfs_file);
                        analysis_change.add_file(source_root_id, file_id, path.clone(), text);
                        file_map.insert(path, file_id);
                    }
                    roots_loaded.insert(source_root_id);
                    if roots_loaded.len() == vfs.n_roots() {
                        done = true;
                    }
                }
                VfsChange::AddFile { .. }
                | VfsChange::RemoveFile { .. }
                | VfsChange::ChangeFile { .. } => {
                    // We just need the first scan, so just ignore these
                }
            }
        }
        if done {
            break;
        }
    }

    host.apply_change(analysis_change);
    host
}

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

    #[test]
    fn test_loading_rust_analyzer() {
        let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
        let (host, _roots) = load_cargo(path).unwrap();
        let n_crates = Crate::all(host.raw_database()).len();
        // RA has quite a few crates, but the exact count doesn't matter
        assert!(n_crates > 20);
    }
}
<commit_msg>Fix panic in batch analysis<commit_after>//! FIXME: write short doc here

use std::{collections::HashSet, error::Error, path::Path};

use rustc_hash::FxHashMap;

use crossbeam_channel::{unbounded, Receiver};
use ra_db::{CrateGraph, FileId, SourceRootId};
use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags};
use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace};
use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
use ra_vfs_glob::RustPackageFilterBuilder;

type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;

fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
    FileId(f.0)
}
fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
    SourceRootId(r.0)
}

pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
    let root = std::env::current_dir()?.join(root);
    let ws = ProjectWorkspace::discover(root.as_ref())?;
    let project_roots = ws.to_roots();
    let (sender, receiver) = unbounded();
    let sender = Box::new(move |t| sender.send(t).unwrap());
    let (mut vfs, roots) = Vfs::new(
        project_roots
            .iter()
            .map(|pkg_root| {
                RootEntry::new(
                    pkg_root.path().clone(),
                    RustPackageFilterBuilder::default()
                        .set_member(pkg_root.is_member())
                        .into_vfs_filter(),
                )
            })
            .collect(),
        sender,
        Watch(false),
    );

    // FIXME: cfg options?
    let default_cfg_options = {
        let mut opts = get_rustc_cfg_options();
        opts.insert_atom("test".into());
        opts.insert_atom("debug_assertion".into());
        opts
    };

    let (crate_graph, _crate_names) =
        ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| {
            let vfs_file = vfs.load(path);
            log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
            vfs_file.map(vfs_file_to_id)
        });
    log::debug!("crate graph: {:?}", crate_graph);

    let source_roots = roots
        .iter()
        .map(|&vfs_root| {
            let source_root_id = vfs_root_to_id(vfs_root);
            let project_root = project_roots
                .iter()
                .find(|it| it.path() == &vfs.root2path(vfs_root))
                .unwrap()
                .clone();
            (source_root_id, project_root)
        })
        .collect::<FxHashMap<_, _>>();
    let host = load(&source_roots, crate_graph, &mut vfs, receiver);
    Ok((host, source_roots))
}

pub fn load(
    source_roots: &FxHashMap<SourceRootId, PackageRoot>,
    crate_graph: CrateGraph,
    vfs: &mut Vfs,
    receiver: Receiver<VfsTask>,
) -> AnalysisHost {
    let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
    let mut host = AnalysisHost::new(lru_cap, FeatureFlags::default());
    let mut analysis_change = AnalysisChange::new();
    analysis_change.set_crate_graph(crate_graph);

    // wait until Vfs has loaded all roots
    let mut roots_loaded = HashSet::new();
    for task in receiver {
        vfs.handle_task(task);
        let mut done = false;
        for change in vfs.commit_changes() {
            match change {
                VfsChange::AddRoot { root, files } => {
                    let source_root_id = vfs_root_to_id(root);
                    let is_local = source_roots[&source_root_id].is_member();
                    log::debug!(
                        "loaded source root {:?} with path {:?}",
                        source_root_id,
                        vfs.root2path(root)
                    );
                    analysis_change.add_root(source_root_id, is_local);
                    analysis_change.set_debug_root_path(
                        source_root_id,
                        source_roots[&source_root_id].path().display().to_string(),
                    );

                    let mut file_map = FxHashMap::default();
                    for (vfs_file, path, text) in files {
                        let file_id = vfs_file_to_id(vfs_file);
                        analysis_change.add_file(source_root_id, file_id, path.clone(), text);
                        file_map.insert(path, file_id);
                    }
                    roots_loaded.insert(source_root_id);
                    if roots_loaded.len() == vfs.n_roots() {
                        done = true;
                    }
                }
                VfsChange::AddFile { root, file, path, text } => {
                    let source_root_id = vfs_root_to_id(root);
                    let file_id = vfs_file_to_id(file);
                    analysis_change.add_file(source_root_id, file_id, path, text);
                }
                VfsChange::RemoveFile { .. } | VfsChange::ChangeFile { .. } => {
                    // We just need the first scan, so just ignore these
                }
            }
        }
        if done {
            break;
        }
    }

    host.apply_change(analysis_change);
    host
}

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

    #[test]
    fn test_loading_rust_analyzer() {
        let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
        let (host, _roots) = load_cargo(path).unwrap();
        let n_crates = Crate::all(host.raw_database()).len();
        // RA has quite a few crates, but the exact count doesn't matter
        assert!(n_crates > 20);
    }
}
<|endoftext|>


<commit_before>// Support code for rustc's built in test runner generator. Currently,
// none of this is meant for users. It is intended to support the
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.

export test_name;
export test_fn;
export test_desc;
export test_main;
export test_result;
export test_opts;
export tr_ok;
export tr_failed;
export tr_ignored;
export run_test;
export filter_tests;
export parse_opts;

// The name of a test. By convention this follows the rules for rust
// paths, i.e it should be a series of identifiers seperated by double
// colons. This way if some test runner wants to arrange the tests
// heirarchically it may.
type test_name = str;

// A function that runs a test. If the function returns successfully,
// the test succeeds; if the function fails then the test fails. We
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
type test_fn = fn();

// The definition of a single test. A test runner will run a list of
// these.
type test_desc = rec(test_name name,
                     test_fn fn,
                     bool ignore);

// The default console test runner. It accepts the command line
// arguments and a vector of test_descs (generated at compile time).
fn test_main(&vec[str] args, &test_desc[] tests) {
    auto ivec_args = {
        auto iargs = ~[];
        for (str arg in args) {
            iargs += ~[arg]
        }
        iargs
    };
    check ivec::is_not_empty(ivec_args);
    auto opts = alt (parse_opts(ivec_args)) {
        either::left(?o) { o }
        either::right(?m) { fail m }
    };
    if (!run_tests(opts, tests)) {
        fail "Some tests failed";
    }
}

type test_opts = rec(option::t[str] filter,
                     bool run_ignored);

type opt_res = either::t[test_opts, str];

// Parses command line arguments into test options
fn parse_opts(&str[] args) : ivec::is_not_empty(args) -> opt_res {

    // FIXME (#649): Shouldn't have to check here
    check ivec::is_not_empty(args);
    auto args_ = ivec::tail(args);
    auto opts = ~[getopts::optflag("ignored")];
    auto match = alt (getopts::getopts_ivec(args_, opts)) {
        getopts::success(?m) { m }
        getopts::failure(?f) { ret either::right(getopts::fail_str(f)) }
    };

    auto filter = if (vec::len(match.free) > 0u) {
        option::some(match.free.(0))
    } else {
        option::none
    };

    auto run_ignored = getopts::opt_present(match, "ignored");

    auto test_opts = rec(filter = filter,
                         run_ignored = run_ignored);

    ret either::left(test_opts);
}

tag test_result {
    tr_ok;
    tr_failed;
    tr_ignored;
}

// A simple console test runner
fn run_tests(&test_opts opts, &test_desc[] tests) -> bool {

    auto filtered_tests = filter_tests(opts, tests);

    auto out = io::stdout();

    auto total = ivec::len(filtered_tests);
    out.write_line(#fmt("running %u tests", total));

    auto passed = 0u;
    auto failed = 0u;
    auto ignored = 0u;

    for (test_desc test in filtered_tests) {
        out.write_str(#fmt("running %s ... ", test.name));
        alt (run_test(test)) {
            tr_ok {
                passed += 1u;
                write_ok(out);
                out.write_line("");
            }
            tr_failed {
                failed += 1u;
                write_failed(out);
                out.write_line("");
            }
            tr_ignored {
                ignored += 1u;
                write_ignored(out);
                out.write_line("");
            }
        }
    }

    assert passed + failed + ignored == total;

    out.write_str(#fmt("\nresult: "));
    if (failed == 0u) {
        write_ok(out);
    } else {
        write_failed(out);
    }
    out.write_str(#fmt(". %u passed; %u failed; %u ignored\n\n",
                       passed, failed, ignored));

    ret true;

    fn write_ok(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_green);
        }
        out.write_str("ok");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
     }

    fn write_failed(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_red);
        }
        out.write_str("FAILED");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
    }

    fn write_ignored(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_yellow);
        }
        out.write_str("ignored");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
    }
}

fn filter_tests(&test_opts opts, &test_desc[] tests) -> test_desc[] {
    auto filtered = tests;

    filtered = if (option::is_none(opts.filter)) {
        filtered
    } else {
        auto filter_str = alt opts.filter { option::some(?f) { f }
                                            option::none { "" } };

        auto filter = bind fn(&test_desc test,
                              str filter_str) -> option::t[test_desc] {
            if (str::find(test.name, filter_str) >= 0) {
                ret option::some(test);
            } else {
                ret option::none;
            }
        } (_, filter_str);

        ivec::filter_map(filter, filtered)
    };

    filtered = if (!opts.run_ignored) {
        filtered
    } else {
        auto filter = fn(&test_desc test) -> option::t[test_desc] {
            if (test.ignore) {
                ret option::some(rec(name = test.name,
                                     fn = test.fn,
                                     ignore = false));
            } else {
                ret option::none;
            }
        };

        ivec::filter_map(filter, filtered)
    };

    ret filtered;
}

fn run_test(&test_desc test) -> test_result {
    if (!test.ignore) {
        if (run_test_fn_in_task(test.fn)) {
            ret tr_ok;
        } else {
            ret tr_failed;
        }
    } else {
        ret tr_ignored;
    }
}

// We need to run our tests in another task in order to trap test failures.
// But, at least currently, functions can't be used as spawn arguments so
// we've got to treat our test functions as unsafe pointers.
fn run_test_fn_in_task(&fn() f) -> bool {
    fn run_task(*mutable fn() fptr) {
        task::unsupervise();
        (*fptr)()
    }
    auto fptr = ptr::addr_of(f);
    auto test_task = spawn run_task(fptr);
    ret alt (task::join(test_task)) {
        task::tr_success { true }
        task::tr_failure { false }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Refactor std::test<commit_after>// Support code for rustc's built in test runner generator. Currently,
// none of this is meant for users. It is intended to support the
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.

export test_name;
export test_fn;
export test_desc;
export test_main;
export test_result;
export test_opts;
export tr_ok;
export tr_failed;
export tr_ignored;
export run_test;
export filter_tests;
export parse_opts;

// The name of a test. By convention this follows the rules for rust
// paths, i.e it should be a series of identifiers seperated by double
// colons. This way if some test runner wants to arrange the tests
// heirarchically it may.
type test_name = str;

// A function that runs a test. If the function returns successfully,
// the test succeeds; if the function fails then the test fails. We
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
type test_fn = fn();

// The definition of a single test. A test runner will run a list of
// these.
type test_desc = rec(test_name name,
                     test_fn fn,
                     bool ignore);

// The default console test runner. It accepts the command line
// arguments and a vector of test_descs (generated at compile time).
fn test_main(&vec[str] args, &test_desc[] tests) {
    auto ivec_args = {
        auto iargs = ~[];
        for (str arg in args) {
            iargs += ~[arg]
        }
        iargs
    };
    check ivec::is_not_empty(ivec_args);
    auto opts = alt (parse_opts(ivec_args)) {
        either::left(?o) { o }
        either::right(?m) { fail m }
    };
    if (!run_tests(opts, tests)) {
        fail "Some tests failed";
    }
}

type test_opts = rec(option::t[str] filter,
                     bool run_ignored);

type opt_res = either::t[test_opts, str];

// Parses command line arguments into test options
fn parse_opts(&str[] args) : ivec::is_not_empty(args) -> opt_res {

    // FIXME (#649): Shouldn't have to check here
    check ivec::is_not_empty(args);
    auto args_ = ivec::tail(args);
    auto opts = ~[getopts::optflag("ignored")];
    auto match = alt (getopts::getopts_ivec(args_, opts)) {
        getopts::success(?m) { m }
        getopts::failure(?f) { ret either::right(getopts::fail_str(f)) }
    };

    auto filter = if (vec::len(match.free) > 0u) {
        option::some(match.free.(0))
    } else {
        option::none
    };

    auto run_ignored = getopts::opt_present(match, "ignored");

    auto test_opts = rec(filter = filter,
                         run_ignored = run_ignored);

    ret either::left(test_opts);
}

tag test_result {
    tr_ok;
    tr_failed;
    tr_ignored;
}

// A simple console test runner
fn run_tests(&test_opts opts, &test_desc[] tests) -> bool {

    auto filtered_tests = filter_tests(opts, tests);

    auto out = io::stdout();

    auto total = ivec::len(filtered_tests);
    out.write_line(#fmt("running %u tests", total));

    auto passed = 0u;
    auto failed = 0u;
    auto ignored = 0u;

    for (test_desc test in filtered_tests) {
        out.write_str(#fmt("running %s ... ", test.name));
        alt (run_test(test)) {
            tr_ok {
                passed += 1u;
                write_ok(out);
                out.write_line("");
            }
            tr_failed {
                failed += 1u;
                write_failed(out);
                out.write_line("");
            }
            tr_ignored {
                ignored += 1u;
                write_ignored(out);
                out.write_line("");
            }
        }
    }

    assert passed + failed + ignored == total;

    out.write_str(#fmt("\nresult: "));
    if (failed == 0u) {
        write_ok(out);
    } else {
        write_failed(out);
    }
    out.write_str(#fmt(". %u passed; %u failed; %u ignored\n\n",
                       passed, failed, ignored));

    ret true;

    fn write_ok(&io::writer out) {
        write_pretty(out, "ok", term::color_green);
     }

    fn write_failed(&io::writer out) {
        write_pretty(out, "FAILED", term::color_red);
    }

    fn write_ignored(&io::writer out) {
        write_pretty(out, "ignored", term::color_yellow);
    }

    fn write_pretty(&io::writer out, &str word, u8 color) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), color);
        }
        out.write_str(word);
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
    }
}

fn filter_tests(&test_opts opts, &test_desc[] tests) -> test_desc[] {
    auto filtered = tests;

    filtered = if (option::is_none(opts.filter)) {
        filtered
    } else {
        auto filter_str = alt opts.filter { option::some(?f) { f }
                                            option::none { "" } };

        auto filter = bind fn(&test_desc test,
                              str filter_str) -> option::t[test_desc] {
            if (str::find(test.name, filter_str) >= 0) {
                ret option::some(test);
            } else {
                ret option::none;
            }
        } (_, filter_str);

        ivec::filter_map(filter, filtered)
    };

    filtered = if (!opts.run_ignored) {
        filtered
    } else {
        auto filter = fn(&test_desc test) -> option::t[test_desc] {
            if (test.ignore) {
                ret option::some(rec(name = test.name,
                                     fn = test.fn,
                                     ignore = false));
            } else {
                ret option::none;
            }
        };

        ivec::filter_map(filter, filtered)
    };

    ret filtered;
}

fn run_test(&test_desc test) -> test_result {
    if (!test.ignore) {
        if (run_test_fn_in_task(test.fn)) {
            ret tr_ok;
        } else {
            ret tr_failed;
        }
    } else {
        ret tr_ignored;
    }
}

// We need to run our tests in another task in order to trap test failures.
// But, at least currently, functions can't be used as spawn arguments so
// we've got to treat our test functions as unsafe pointers.
fn run_test_fn_in_task(&fn() f) -> bool {
    fn run_task(*mutable fn() fptr) {
        task::unsupervise();
        (*fptr)()
    }
    auto fptr = ptr::addr_of(f);
    auto test_task = spawn run_task(fptr);
    ret alt (task::join(test_task)) {
        task::tr_success { true }
        task::tr_failure { false }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>// Based on threadring.erlang by Jira Isa
use std;

// FIXME: Need a cleaner way to request the runtime to exit
#[nolink]
native mod libc {
    fn exit(status: ctypes::c_int);
}

const n_threads: int = 503;

fn start(+token: int) {
    import iter::*;

    let p = comm::port();
    let ch = iter::foldl(bind int::range(2, n_threads + 1, _),
                         comm::chan(p)) { |ch, i|
        // FIXME: Some twiddling because we don't have a standard
        // reverse range function yet
        let id = n_threads + 2 - i;
        let {to_child, _} = task::spawn_connected::<int, int> {|p, _ch|
            roundtrip(id, p, ch)
        };
        to_child
    };
    comm::send(ch, token);
    roundtrip(1, p, ch);
}

fn roundtrip(id: int, p: comm::port<int>, ch: comm::chan<int>) {
    while (true) {
        alt comm::recv(p) {
          1 {
            std::io::println(#fmt("%d\n", id));
            libc::exit(0i32);
          }
          token {
            #debug("%d %d", id, token);
            comm::send(ch, token - 1);
          }
        }
    }
}

fn main(args: [str]) {
    let token = if vec::len(args) < 2u {
        1000
    } else {
        int::from_str(args[1])
    };

    start(token);
}<commit_msg>Revert "bench: Add threadring shootout benchmark"<commit_after><|endoftext|>


<commit_before>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    let rules = status::get_rules()?;
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/casile.mk"
    )));
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if targets.is_empty() {
        targets.push(String::from("default"));
    }
    if status::is_gha()? {
        targets.push(String::from("_gha"));
    }
    if status::is_glc()? {
        targets.push(String::from("_glc"));
    }
    if (status::is_gha()? || status::is_glc()?) && targets.first().unwrap() != "debug" {
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!("{}", errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<commit_msg>fix(cli): Don't force install-dist target on setup / .gitignore regen<commit_after>use crate::i18n::LocalText;
use crate::*;

use colored::Colorize;
use regex::Regex;
use std::io::prelude::*;
use std::{ffi::OsString, io};
use subprocess::{Exec, ExitStatus, Redirection};

// FTL: help-subcommand-make
/// Build specified target(s)
pub fn run(target: Vec<String>) -> Result<()> {
    setup::is_setup()?;
    show_header("make-header");
    let mut makeflags: Vec<OsString> = Vec::new();
    let cpus = num_cpus::get();
    makeflags.push(OsString::from(format!("--jobs={}", cpus)));
    let mut makefiles: Vec<OsString> = Vec::new();
    let rules = status::get_rules()?;
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/casile.mk"
    )));
    for rule in rules {
        makefiles.push(OsString::from("-f"));
        let p = rule.into_os_string();
        makefiles.push(p);
    }
    makefiles.push(OsString::from("-f"));
    makefiles.push(OsString::from(format!(
        "{}{}",
        CONFIGURE_DATADIR, "/rules/rules.mk"
    )));
    let mut targets: Vec<_> = target.into_iter().collect();
    if targets.is_empty() {
        targets.push(String::from("default"));
    }
    if status::is_gha()? {
        targets.push(String::from("_gha"));
    }
    if status::is_glc()? {
        targets.push(String::from("_glc"));
    }
    if (status::is_gha()? || status::is_glc()?)
        && targets.first().unwrap() != "debug"
        && targets.first().unwrap() != ".gitignore"
    {
        targets.push(String::from("install-dist"));
    }
    let mut process = Exec::cmd("make")
        .args(&makeflags)
        .args(&makefiles)
        .args(&targets);
    let gitname = status::get_gitname()?;
    let git_version = status::get_git_version();
    process = process
        .env("CASILE_CLI", "true")
        .env("CASILEDIR", CONFIGURE_DATADIR)
        .env("CONTAINERIZED", status::is_container().to_string())
        .env("LANGUAGE", lang_to_language(CONF.get_string("language")?))
        .env("PROJECT", &gitname)
        .env("PROJECTDIR", CONF.get_string("path")?)
        .env("PROJECTVERSION", git_version);
    if CONF.get_bool("debug")? {
        process = process.env("DEBUG", "true");
    };
    if CONF.get_bool("quiet")? {
        process = process.env("QUIET", "true");
    };
    if CONF.get_bool("verbose")? || targets.contains(&"debug".into()) {
        process = process.env("VERBOSE", "true");
    };
    let repo = get_repo()?;
    let workdir = repo.workdir().unwrap();
    process = process.cwd(workdir);
    let process = process.stderr(Redirection::Merge).stdout(Redirection::Pipe);
    let mut popen = process.popen()?;
    let buf = io::BufReader::new(popen.stdout.as_mut().unwrap());
    let mut backlog: Vec<String> = Vec::new();
    let seps = Regex::new(r"").unwrap();
    let mut ret: u32 = 0;
    for line in buf.lines() {
        let text: &str = &line.unwrap();
        let fields: Vec<&str> = seps.splitn(text, 4).collect();
        match fields[0] {
            "CASILE" => match fields[1] {
                "PRE" => report_start(fields[2]),
                "STDOUT" => {
                    if status::is_gha()? {
                        println!("{}", fields[3]);
                    } else if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "STDERR" => {
                    if CONF.get_bool("verbose")? {
                        report_line(fields[3]);
                    } else {
                        backlog.push(String::from(fields[3]));
                    }
                }
                "POST" => match fields[2] {
                    "0" => {
                        report_end(fields[3]);
                    }
                    val => {
                        report_fail(fields[3]);
                        ret = val.parse().unwrap_or(1);
                    }
                },
                _ => {
                    let errmsg = LocalText::new("make-error-unknown-code").fmt();
                    panic!("{}", errmsg)
                }
            },
            _ => backlog.push(String::from(fields[0])),
        }
    }
    let status = popen.wait();
    match status {
        Ok(ExitStatus::Exited(int)) => {
            let code = int + ret;
            match code {
                0 => {
                    if CONF.get_bool("debug")?
                        || targets.contains(&"debug".into())
                        || targets.contains(&"-p".into())
                    {
                        dump_backlog(&backlog)
                    };
                    Ok(())
                }
                1 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unfinished").fmt(),
                    )))
                }
                2 => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-build").fmt(),
                    )))
                }
                3 => {
                    if !CONF.get_bool("verbose")? {
                        dump_backlog(&backlog);
                    }
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-target").fmt(),
                    )))
                }
                _ => {
                    dump_backlog(&backlog);
                    Err(Box::new(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        LocalText::new("make-error-unknown").fmt(),
                    )))
                }
            }
        }
        _ => Err(Box::new(io::Error::new(
            io::ErrorKind::InvalidInput,
            LocalText::new("make-error").fmt(),
        ))),
    }
}

fn dump_backlog(backlog: &[String]) {
    let start = LocalText::new("make-backlog-start").fmt();
    eprintln!("{} {}", "┖┄".cyan(), start);
    for line in backlog.iter() {
        eprintln!("{}", line);
    }
    let end = LocalText::new("make-backlog-end").fmt();
    eprintln!("{} {}", "┎┄".cyan(), end);
}

fn report_line(line: &str) {
    eprintln!("{} {}", "┠╎".cyan(), line.dimmed());
}

fn report_start(target: &str) {
    let text = LocalText::new("make-report-start")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.yellow());
}

fn report_end(target: &str) {
    let text = LocalText::new("make-report-end")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.green());
}

fn report_fail(target: &str) {
    let text = LocalText::new("make-report-fail")
        .arg("target", target.white().bold())
        .fmt();
    eprintln!("{} {}", "┠┄".cyan(), text.red());
}
<|endoftext|>


<commit_before>use std::fmt;

use ffi;

/// Network interface index
///
/// Identifies a single interface by index.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InterfaceIndex(u32);

impl InterfaceIndex {
	/// Construct new `InterfaceIndex` from raw index and makes sure
	/// not to use the special reserved values (`0` and `!0`).
	pub fn from_raw(ndx: u32) -> Option<Self> {
		match ndx {
			ffi::INTERFACE_INDEX_ANY => None,
			ffi::INTERFACE_INDEX_LOCAL_ONLY => None,
			ffi::INTERFACE_INDEX_UNICAST => None,
			ffi::INTERFACE_INDEX_P2P => None,
			_ => Some(InterfaceIndex(ndx)),
		}
	}

	/// raw index
	pub fn into_raw(self) -> u32 {
		self.0
	}
}

impl Into<u32> for InterfaceIndex {
	fn into(self) -> u32 {
		self.into_raw()
	}
}

impl fmt::Debug for InterfaceIndex {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		fmt::Debug::fmt(&self.0, f)
	}
}

/// Network interface
///
/// Either identifies a single interface (by index) or the special "Any"
/// or "LocalOnly" interfaces.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Interface {
	/// Any interface; depending on domain name this means either
	/// multicast or unicast
	Any,
	/// Single interface
	Index(InterfaceIndex),
	/// Local machine only
	LocalOnly,
	/// See [`kDNSServiceInterfaceIndexUnicast`](https://developer.apple.com/documentation/dnssd/kdnsserviceinterfaceindexunicast)
	Unicast,
	/// See [`kDNSServiceInterfaceIndexP2P`](https://developer.apple.com/documentation/dnssd/kdnsserviceinterfaceindexp2p)
	PeerToPeer,
}

impl Default for Interface {
	fn default() -> Self {
		Interface::Any
	}
}

impl Interface {
	/// Construct from raw value
	pub fn from_raw(raw: u32) -> Self {
		match raw {
			ffi::INTERFACE_INDEX_ANY => Interface::Any,
			ffi::INTERFACE_INDEX_LOCAL_ONLY => Interface::LocalOnly,
			ffi::INTERFACE_INDEX_UNICAST => Interface::Unicast,
			ffi::INTERFACE_INDEX_P2P => Interface::PeerToPeer,
			_ => Interface::Index(InterfaceIndex(raw)),
		}
	}

	/// Convert to raw value
	pub fn into_raw(self) -> u32 {
		match self {
			Interface::Any => ffi::INTERFACE_INDEX_ANY,
			Interface::Index(InterfaceIndex(raw)) => raw,
			Interface::LocalOnly => ffi::INTERFACE_INDEX_LOCAL_ONLY,
			Interface::Unicast => ffi::INTERFACE_INDEX_UNICAST,
			Interface::PeerToPeer => ffi::INTERFACE_INDEX_P2P,
		}
	}
}

impl Into<u32> for Interface {
	fn into(self) -> u32 {
		self.into_raw()
	}
}
<commit_msg>add Interface::scope_id<commit_after>use std::fmt;

use ffi;

/// Network interface index
///
/// Identifies a single interface by index.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InterfaceIndex(u32);

impl InterfaceIndex {
	/// Construct new `InterfaceIndex` from raw index and makes sure
	/// not to use the special reserved values (`0` and `!0`).
	pub fn from_raw(ndx: u32) -> Option<Self> {
		match ndx {
			ffi::INTERFACE_INDEX_ANY => None,
			ffi::INTERFACE_INDEX_LOCAL_ONLY => None,
			ffi::INTERFACE_INDEX_UNICAST => None,
			ffi::INTERFACE_INDEX_P2P => None,
			_ => Some(InterfaceIndex(ndx)),
		}
	}

	/// raw index
	pub fn into_raw(self) -> u32 {
		self.0
	}
}

impl Into<u32> for InterfaceIndex {
	fn into(self) -> u32 {
		self.into_raw()
	}
}

impl fmt::Debug for InterfaceIndex {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		fmt::Debug::fmt(&self.0, f)
	}
}

/// Network interface
///
/// Either identifies a single interface (by index) or the special "Any"
/// or "LocalOnly" interfaces.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Interface {
	/// Any interface; depending on domain name this means either
	/// multicast or unicast
	Any,
	/// Single interface
	Index(InterfaceIndex),
	/// Local machine only
	LocalOnly,
	/// See [`kDNSServiceInterfaceIndexUnicast`](https://developer.apple.com/documentation/dnssd/kdnsserviceinterfaceindexunicast)
	Unicast,
	/// See [`kDNSServiceInterfaceIndexP2P`](https://developer.apple.com/documentation/dnssd/kdnsserviceinterfaceindexp2p)
	PeerToPeer,
}

impl Default for Interface {
	fn default() -> Self {
		Interface::Any
	}
}

impl Interface {
	/// Construct from raw value
	pub fn from_raw(raw: u32) -> Self {
		match raw {
			ffi::INTERFACE_INDEX_ANY => Interface::Any,
			ffi::INTERFACE_INDEX_LOCAL_ONLY => Interface::LocalOnly,
			ffi::INTERFACE_INDEX_UNICAST => Interface::Unicast,
			ffi::INTERFACE_INDEX_P2P => Interface::PeerToPeer,
			_ => Interface::Index(InterfaceIndex(raw)),
		}
	}

	/// Convert to raw value
	pub fn into_raw(self) -> u32 {
		match self {
			Interface::Any => ffi::INTERFACE_INDEX_ANY,
			Interface::Index(InterfaceIndex(raw)) => raw,
			Interface::LocalOnly => ffi::INTERFACE_INDEX_LOCAL_ONLY,
			Interface::Unicast => ffi::INTERFACE_INDEX_UNICAST,
			Interface::PeerToPeer => ffi::INTERFACE_INDEX_P2P,
		}
	}

	/// Extract scope id / interface index
	///
	/// Returns the interface index (or zero if not a single interface is selected)
	pub fn scope_id(self) -> u32 {
		match self {
			Interface::Index(InterfaceIndex(scope_id)) => scope_id,
			_ => 0,
		}
	}
}

impl Into<u32> for Interface {
	fn into(self) -> u32 {
		self.into_raw()
	}
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Provides a "cursor" over a mutable byte buffer.
//!
//! [`Cursor`] provides a `consume()` function, which can be called repeatedly
//! to take portions of the buffer. An internal cursor will track the location
//! of the buffer. This method is used to implement [`Write`] for [`Cursor`].
//!
//! This type is useful when you want to feed a scratch buffer into a function
//! that performs I/O operations on a buffer, and then extract how much of the
//! buffer was read or written. This is especialy useful when used in
//! conjunction with [`ToWire`]:
//! ```
//! # use manticore::io::*;
//! # use manticore::protocol::wire::*;
//! # struct MyMessage;
//! # impl ToWire for MyMessage {
//! #     fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
//! #         w.write_bytes(&[1, 2, 3, 4]);
//! #         Ok(())
//! #     }
//! # }
//! let msg = MyMessage;
//! let mut buf = [0; 256];
//!
//! let mut cursor = Cursor::new(&mut buf);
//! msg.to_wire(&mut cursor);
//!
//! let msg_bytes = cursor.take_consumed_bytes();
//! assert_ne!(msg_bytes.len(), 0);
//! ```
//!
//! [`Cursor`]: struct.Cursor.html
//! [`Write`]: ../trait.Write.html
//! [`ToWire`]: ../../protocol/wire/trait.ToWire.html

use core::mem;

use crate::io;
use crate::io::Write;

/// A cursor over a buffer of memory.
///
/// See the [module documentation](index.html) for more information.
pub struct Cursor<'a> {
    buf: &'a mut [u8],
    // Invariant: cursor <= buf.len().
    cursor: usize,
}

impl<'a> Cursor<'a> {
    /// Creates a new `Cursor` for the given buffer.
    pub fn new(buf: &'a mut [u8]) -> Self {
        Self { buf, cursor: 0 }
    }

    /// Consumes `n` bytes from the underlying buffer.
    ///
    /// If `n` bytes are unavailable, `BufferExhausted` is returned.
    pub fn consume(&mut self, n: usize) -> Result<&mut [u8], io::Error> {
        let end = self
            .cursor
            .checked_add(n)
            .ok_or(io::Error::BufferExhausted)?;
        if self.buf.len() < end {
            return Err(io::Error::BufferExhausted);
        }
        let output = &mut self.buf[self.cursor..end];
        self.cursor = end;

        Ok(output)
    }

    /// Returns the number of bytes consumed thus far.
    pub fn consumed_len(&self) -> usize {
        self.cursor
    }

    /// Returns the portion of the buffer which has been consumed thus far.
    pub fn consumed_bytes(&self) -> &[u8] {
        &self.buf[..self.cursor]
    }

    /// Takes the portion of the buffer which has been consumed so far,
    /// resetting the cursor value back to zero.
    ///
    /// This function leaves `self` as if it had been newly initialized with
    /// the unconsumed portion of the buffer. Repeatedly calling this function
    /// with no other intervening operations will return `&mut []`.
    ///
    /// Because this function returns a `'a` reference, it is not bound to the
    /// `Cursor` that originally contained it. This function is useful when
    /// a desired reference needs to have the lifetime of the buffer that went
    /// into the `Cursor`, rather than the `Cursor`'s own local lifetime.
    pub fn take_consumed_bytes(&mut self) -> &'a mut [u8] {
        let (output, rest) =
            mem::replace(&mut self.buf, &mut []).split_at_mut(self.cursor);
        self.cursor = 0;
        self.buf = rest;
        output
    }
}

impl Write for Cursor<'_> {
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), io::Error> {
        let dest = self.consume(buf.len())?;
        dest.copy_from_slice(buf);
        Ok(())
    }
}

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

    #[test]
    fn cursor() {
        let mut buf = [0; 8];
        let mut cursor = Cursor::new(&mut buf);

        cursor.write_le::<u32>(0xffaaffaa).unwrap();
        assert_eq!(cursor.consumed_len(), 4);
        assert_eq!(cursor.consumed_bytes(), &[0xaa, 0xff, 0xaa, 0xff]);
        let bytes = cursor.take_consumed_bytes();
        assert_eq!(bytes, &[0xaa, 0xff, 0xaa, 0xff]);
        assert_eq!(cursor.consumed_len(), 0);

        assert!(cursor.write_bytes(&[0x55; 7]).is_err());
    }
}
<commit_msg>[io] Add seek APIs to io::Cursor<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Provides a "cursor" over a mutable byte buffer.
//!
//! [`Cursor`] provides a `consume()` function, which can be called repeatedly
//! to take portions of the buffer. An internal cursor will track the location
//! of the buffer. This method is used to implement [`Write`] for [`Cursor`].
//!
//! This type is useful when you want to feed a scratch buffer into a function
//! that performs I/O operations on a buffer, and then extract how much of the
//! buffer was read or written. This is especialy useful when used in
//! conjunction with [`ToWire`]:
//! ```
//! # use manticore::io::*;
//! # use manticore::protocol::wire::*;
//! # struct MyMessage;
//! # impl ToWire for MyMessage {
//! #     fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
//! #         w.write_bytes(&[1, 2, 3, 4]);
//! #         Ok(())
//! #     }
//! # }
//! let msg = MyMessage;
//! let mut buf = [0; 256];
//!
//! let mut cursor = Cursor::new(&mut buf);
//! msg.to_wire(&mut cursor);
//!
//! let msg_bytes = cursor.take_consumed_bytes();
//! assert_ne!(msg_bytes.len(), 0);
//! ```
//!
//! [`Cursor`]: struct.Cursor.html
//! [`Write`]: ../trait.Write.html
//! [`ToWire`]: ../../protocol/wire/trait.ToWire.html

use core::mem;

use crate::io;
use crate::io::Write;

/// A position for using with [`Cursor::seek()`].
///
/// [`Cursor::seek()`]: struct.Cursor.html#method.seek
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SeekPos {
    /// Seek absolutely, from the start of the buffer.
    Abs(usize),
    /// Seek relatively, from the current cursor value.
    Rel(isize),
}

/// A cursor over a buffer of memory.
///
/// See the [module documentation](index.html) for more information.
pub struct Cursor<'a> {
    buf: &'a mut [u8],
    // Invariant: cursor <= buf.len().
    cursor: usize,
}

impl<'a> Cursor<'a> {
    /// Creates a new `Cursor` for the given buffer.
    pub fn new(buf: &'a mut [u8]) -> Self {
        Self { buf, cursor: 0 }
    }

    /// Consumes `n` bytes from the underlying buffer.
    ///
    /// If `n` bytes are unavailable, `BufferExhausted` is returned.
    pub fn consume(&mut self, n: usize) -> Result<&mut [u8], io::Error> {
        let end = self
            .cursor
            .checked_add(n)
            .ok_or(io::Error::BufferExhausted)?;
        if self.buf.len() < end {
            return Err(io::Error::BufferExhausted);
        }
        let output = &mut self.buf[self.cursor..end];
        self.cursor = end;

        Ok(output)
    }

    /// Seek to an offset within the underlying buffer.
    ///
    /// This function effectively sets the underlying cursor used to partition
    /// the buffer into "consumed" and "unconsumed portions. This function can
    /// be used to temporarially backtrack in combination with
    /// `consumed_len()`.
    ///
    /// Returns [`Error::BufferExhausted`] when attempting to seek beyond the end
    /// of the buffer.
    ///
    /// [`Error::BufferExhausted`]: enum.Error.html#variant.BufferExhausted
    ///
    /// # Example
    /// ```
    /// # use manticore::io::*;
    /// # use manticore::io::cursor::*;
    /// # let mut buf = [0; 16];
    /// let mut cursor = Cursor::new(&mut buf);
    /// cursor.write_bytes(b"barbar")?;
    ///
    /// let mark = cursor.consumed_len();
    /// cursor.seek(SeekPos::Rel(-5))?;
    /// cursor.write_bytes(b"foo")?;
    /// cursor.seek(SeekPos::Abs(mark))?;
    /// assert_eq!(cursor.consumed_bytes(), b"bfooar");
    ///
    /// # Ok::<(), Error>(())
    /// ```
    pub fn seek(&mut self, pos: SeekPos) -> Result<(), io::Error> {
        let offset = match pos {
            SeekPos::Abs(offset) if offset <= self.buf.len() => offset,
            SeekPos::Rel(offset) => {
                let offset = (self.consumed_len() as isize)
                    .checked_add(offset)
                    .ok_or(io::Error::BufferExhausted)?;
                if offset < 0 {
                    return Err(io::Error::BufferExhausted);
                }
                offset as usize
            }
            _ => return Err(io::Error::BufferExhausted),
        };
        self.cursor = offset;
        Ok(())
    }

    /// Returns the number of bytes consumed thus far.
    pub fn consumed_len(&self) -> usize {
        self.cursor
    }

    /// Returns the portion of the buffer which has been consumed thus far.
    pub fn consumed_bytes(&self) -> &[u8] {
        &self.buf[..self.cursor]
    }

    /// Takes the portion of the buffer which has been consumed so far,
    /// resetting the cursor value back to zero.
    ///
    /// This function leaves `self` as if it had been newly initialized with
    /// the unconsumed portion of the buffer. Repeatedly calling this function
    /// with no other intervening operations will return `&mut []`.
    ///
    /// Because this function returns a `'a` reference, it is not bound to the
    /// `Cursor` that originally contained it. This function is useful when
    /// a desired reference needs to have the lifetime of the buffer that went
    /// into the `Cursor`, rather than the `Cursor`'s own local lifetime.
    pub fn take_consumed_bytes(&mut self) -> &'a mut [u8] {
        let (output, rest) =
            mem::replace(&mut self.buf, &mut []).split_at_mut(self.cursor);
        self.cursor = 0;
        self.buf = rest;
        output
    }
}

impl Write for Cursor<'_> {
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), io::Error> {
        let dest = self.consume(buf.len())?;
        dest.copy_from_slice(buf);
        Ok(())
    }
}

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

    #[test]
    fn cursor() {
        let mut buf = [0; 8];
        let mut cursor = Cursor::new(&mut buf);

        cursor.write_le::<u32>(0xffaaffaa).unwrap();
        assert_eq!(cursor.consumed_len(), 4);
        assert_eq!(cursor.consumed_bytes(), &[0xaa, 0xff, 0xaa, 0xff]);
        let bytes = cursor.take_consumed_bytes();
        assert_eq!(bytes, &[0xaa, 0xff, 0xaa, 0xff]);
        assert_eq!(cursor.consumed_len(), 0);

        assert!(cursor.write_bytes(&[0x55; 7]).is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use derive_more::{Display, Error, From};

pub type Result<A> = std::result::Result<A, Error>;

#[derive(Debug, Display, Error, From)]
pub enum Error {
    #[display(fmt = "Agent is busy: {:?}", _0)]
    AgentIsBusy(#[error(not(source))] crate::api::state::Response),

    #[display(fmt = "Abort download was refused: {:?}", _0)]
    AbortDownloadRefused(#[error(not(source))] crate::api::abort_download::Refused),

    #[display(fmt = "Unexpected response: {:?}", _0)]
    UnexpectedResponse(#[error(not(source))] surf::StatusCode),

    Client(#[error(not(source))] surf::Error),

    Io(std::io::Error),

    Env(std::env::VarError),
}
<commit_msg>updatehub-sdk: doc: Add doc for error<commit_after>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use derive_more::{Display, Error, From};

/// Type alias of `Result` where the `Err` is a `updatehub_sdk::Error`.
pub type Result<A> = std::result::Result<A, Error>;

/// UpdateHub SDK error type.
#[derive(Debug, Display, Error, From)]
pub enum Error {
    #[display(fmt = "Agent is busy: {:?}", _0)]
    AgentIsBusy(#[error(not(source))] crate::api::state::Response),

    #[display(fmt = "Abort download was refused: {:?}", _0)]
    AbortDownloadRefused(#[error(not(source))] crate::api::abort_download::Refused),

    #[display(fmt = "Unexpected response: {:?}", _0)]
    UnexpectedResponse(#[error(not(source))] surf::StatusCode),

    Client(#[error(not(source))] surf::Error),

    Io(std::io::Error),

    Env(std::env::VarError),
}
<|endoftext|>